| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Delphi: Общие вопросы > Перехват вызовов функций |
| Автор: TAPAKAH 30.3.2002, 14:24 |
| Прежде всего .. чего я нашёл, а затем что же мне делать :) У Мэта Питрэка (это который работал (а возможно и работает) в фирме NuMega и участвовал в разработке SoftIce и BoundsChecker) есть такая замечательная книжка Windows 95 System Programming SECRETS Вот лежит она у меня значит (на русском) и нахожу я там такую забавную вещь 1. Как написать ApiSpy 2. Собственно какие механизмы перехвата вызовов существуют. Ну чаще всего используют такую вещь как DEBUG PROCESS (этого в интернете навалом). Там более менее механизм ясен. можно посмотреть вот здесь http://delphi.mastak.ru/articles/Dapi/index.html Это делается для перехвата вызовов в других процессах ... а вот для своего процесса Питрек предложил следующее (это перевод на Дельфи с C++) PIMAGE_IMPORT_BY_NAME = ^TIMAGE_IMPORT_BY_NAME; TIMAGE_IMPORT_BY_NAME = packed record Hint : WORD; Name : BYTE; end; PIMAGE_THUNK_DATA = ^TIMAGE_THUNK_DATA; TU1_IMAGE_THUNK_DATA = packed record case Integer of 0: (ForwarderString : PBYTE); 1: (Func : PDWORD); 2: (Ordinal : DWORD); 3: (AddressOfData : PIMAGE_IMPORT_BY_NAME); end; TIMAGE_THUNK_DATA = packed record u1 : TU1_IMAGE_THUNK_DATA; end; PIMAGE_IMPORT_DESCRIPTOR = ^TIMAGE_IMPORT_DESCRIPTOR; TIMAGE_IMPORT_DESCRIPTOR= packed record OriginalFirstThunk : DWORD; TimeDateStamp : DWORD; // 0 if not bound, // -1 if bound, and real date\time stamp // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) // O.W. date/time stamp of DLL bound to (Old BIND) ForwarderChain : DWORD; // -1 if no forwarders Name : DWORD; FirstThunk : DWORD; // RVA to IAT (if bound this IAT has actual addresses) end; implementation function HookImportedFunction(FromModule : HMODULE; FuncModule : String; FuncName : String; NewProc : PDWORD) : PDWORD; var OriginalProc : PDWORD; pDOS_HEADER : PImageDosHeader; pNT_HEADER : PImageNtHeaders; pImportDesc : PIMAGE_IMPORT_DESCRIPTOR; pThunk : PIMAGE_THUNK_DATA; ModuleName : String; begin Result:=nil; if IsBadCodePtr(NewProc) then Exit; // Получаем оригинальный адрес функции которую хотим перехватить OriginalProc:=GetProcAddress(GetModuleHandle(PChar(FuncModule)),PChar(FuncName)); if not Assigned(OriginalProc) then Exit; // Всевозможные проверки pDOS_HEADER:=PImageDosHeader(FromModule); if IsBadReadPtr(pDOS_HEADER,SizeOf(TImageDosHeader)) then Exit; if pDOS_HEADER^.e_magic<>IMAGE_DOS_SIGNATURE then Exit; pNT_HEADER:=PImageNtHeaders(DWORD(pDOS_HEADER)+DWORD(pDOS_HEADER^._lfanew)); if IsBadReadPtr(pNT_HEADER,SizeOf(TImageNtHeaders)) then Exit; if pNT_HEADER^.Signature<>IMAGE_NT_SIGNATURE then Exit; // Получаем указатель на секцию импорта модуля. pImportDesc:=PIMAGE_IMPORT_DESCRIPTOR(DWORD(pDOS_HEADER)+ DWORD(pNT_HEADER^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddres s)); if pImportdesc=PIMAGE_IMPORT_DESCRIPTOR(pNT_HEADER) then Exit; // Перебираем дескрипторы с целью поиска дескриптера с именем, соответствующим параметру FuncModule while (pImportdesc^.Name<>0) do begin ModuleName:=StrPas(PChar(DWORD(pDOS_HEADER)+DWORD(pImportdesc^.Name))); if UpperCase(ModuleName)=UpperCase(FuncModule) then Break; Inc(pImportdesc,1); end; if pImportdesc^.Name=0 then Exit; // Получить указатель на таблицу адресов импорта в найденном модуле pThunk:=PIMAGE_THUNK_DATA(DWORD(pDOS_HEADER)+DWORD(pImportdesc^.FirstThunk)); // Просмотр таблицы адресов импорта в поисках адреса // соответсвующего полученному выше с помощью GetProcAddress while (pThunk^.u1.Func<>nil) do begin if DWORD(pThunk^.u1.Func)=DWORD(OriginalProc) then begin // Ура нашли :) Заменим адрес функции, адресом перехвата. И вернем настоящий адрес. pThunk^.u1.Func:=NewProc; Result:=OriginalProc; break; end; Inc(pThunk); end; end; Хочу сразу заметить перевод не совсем точен .. так как добивался чтоб заработало :) (там были ещё вставки чтоб работало и под Win32s) Если кому стало интересно .. то исходные коды (и из Windows 95 System Programming SECRETS в том числе) лежат по адресу http://www.wheaty.net/ Это непосредственно функция .. а вот пример вызова : В своём dpr напишите следующее var OriginalShowWindow : function (hWnd: HWND; nCmdShow: Integer): BOOL; stdcall; function MyShowWindow(hWnd: HWND; nCmdShow: Integer): BOOL; stdcall; begin Windows.Beep(1000,1000); Result:=OriginalShowWindow(hWnd,SW_MAXIMIZE); end; begin @OriginalShowWindow:=HookImportedFunction(GetModuleHandle(nil),'USER32.DLL','ShowWindow',@MyShowWindow); if @OriginalShowWindow=nil then Raise Exception.Create('Hook на ShowWindow не был установлен'); // Application... //... end; А потом запихните сюда ещё и шутку :) http://www.forum.vingrad.ru/cgi-bin/newforum/ikonboard.cgi?act=ST;f=2;t=182 Заранее предупреждаю с MessageBox такая штука например не пройдёт .. у меня почему то её истинный указатель хранится дальше в памяти .. бред какой-то (причём только её) Ну да ладно .. это была вводная часть, а теперь собственно проблема : Мне хотелось бы перехватывать все обращения к строковым ресурсам реализуемые с помощью LoadStringA и надстройкой над ней в Дельфи LoadStr Когда я пытаюсь сделать финт ушами и написать var OriginalLoadString : function (hInstance: HINST; uID: UINT; lpBuffer: PChar; nBufferMax: Integer): Integer; stdcall; function MyLoadString(hInstance: HINST; uID: UINT; lpBuffer: PChar; nBufferMax: Integer): Integer; stdcall; begin Result:=OriginalLoadString(hInstance,uID,lpBuffer,nBufferMax); MessageBox(0,lpBuffer,lpBuffer,MB_OK); end; @OriginalLoadString:=HookImportedFunction(GetModuleHandle('VCL50.BPL'),'USER32.DLL','LoadStringA',@MyLoadString); if @OriginalLoadString=nil then Raise Exception.Create('Hook на LoadStringA не был установлен'); ShowMessage(LoadStr(38807)); То отображаеться два раза окно в котором написано OK OK и третий раз собственно выполняется ShowMessage где и выводится нормальная фраза. Совершенно не догоняю где он берёт этот ОК .. потому что строка там Drag Column .... и так далее. Может кто чего посоветует, или может кто нибудь знает ещё какие-нибудь методы перехвата ... В общем жду помощи .. и обсуждение написанного здесь :) |
| Автор: Chingachguk 1.4.2002, 07:58 |
| Может быть, ShowMessage неявно использует эту LoadString, которую ты перехватил ? Попробуй просто вызвать без ShowMessage ее ?... |
| Автор: TAPAKAH 1.4.2002, 08:18 |
| Ну по поводу ОК это я конечно ступил ... Понятно что эта строчка запрашивается функцией ShowMessage :) Вот здесь : function CreateMessageDialog(const Msg: string; DlgType: TMsgDlgType; Buttons: TMsgDlgButtons): TForm; .... PChar(LoadResString(ButtonCaptions[B])), -1, ..... Но всё равно непонятно .. куда девается подлинная строчка... Сегодня я буду проверять другой вариант ... с изменением кода в рантайме (вставляется jmp) посмотрим как он сработает .. Если кому интересно здесь (с исходниками на С++) http://www.uinc.ru/articles/26/index.shtml |
| Автор: Chingachguk 1.4.2002, 14:48 |
| Насчет сообщения - может, вывести значения указателей на то, что ты хочешь вывести и то, что передается в сам MessageBox ? |
| Автор: TAPAKAH 1.4.2002, 19:39 |
| Chingachguk! Спасибо за поддержку :) В общем сегодня я разродился . Итак ... Посмотрев на все исходники на си и на паскале .. появилась вот такая вещь : Допустим мы хотим перехватить вызов какой-нибудь функции ... ну просто очень хотим .. например :) LoadStringA Я попробовал вот такой вариант : 1. Ищем адрес этой функции в нашем адресном пространстве.. (попросту вызываем GetProcAddress) 2. Разрешаем изменение памяти с помощью VirtualProtectEx 3. Выделяем память под новую заглушку OrigFunc (новую оригинальную функцию) - нам надо 10 байт .. позже станет ясно почему. 4. Копируем первые пять байт из нашей (OurFunc) функции в OrigFunc (это тот код который мы будем менять) (речь конечно об адресах) 5. Заменяем первые 5 байт по адресу OurFunc на jmp (адрес функции перехвата) 6. Заменяем оставшиеся 5 байт по адресу OrigFunc на jmp OurFunc+5 Усё! Теперь при вызове OurFunc будет произведен переход на функцию ловушку. А если нам необходимо вызвать оригинальную функцию то мы вызываем OrigFunc А я то потел .. :( здесь всё то же самое что и на http://www.uinc.ru/articles/26/index.shtml за одним исключением .. там используется хитрая (одна для всех функций) функция для перехвата ... + чтобы вызвать оригинальный код нужно востановить эти 5 байт .. Вот она моя фукция: (есть конечно вероятность что я где то в теории ложанулся ... но вроде работает) // Рабочий вариант.. использует jmp, возвращает указатель на оригинальную функцию function HookFunction(FuncModule : String; FuncName : String; NewProc : PDWORD) : PDWORD; function HookFunction(FuncModule : String; FuncName : String; NewProc : PDWORD) : PDWORD; var ptr : pointer; ptr_proc : PDWORD; OldProtectAPI : Cardinal; begin Result:=nil; ptr_proc:=GetProcAddress(GetModuleHandle(PChar(FuncModule)),PChar(FuncName)); if not Assigned(ptr_proc) then Exit; if(not VirtualProtectEx(GetCurrentProcess(),ptr_proc, 6, PAGE_EXECUTE_READWRITE, OldProtectAPI)) then begin RaiseLastWin32Error; end; // Создаём новую функцию в которую запоминаем первые пять байт + jmp на остальной код измененной функции ptr:=AllocMem(10); CopyMemory(ptr,ptr_proc,5); PBYTE(DWORD(ptr)+5)^:=$e9; PDWORD(DWORD(ptr)+6)^:=DWORD(ptr_proc)-DWORD(ptr)-5; // Меняем оригинальную функцию : вставляем jmp на хук PBYTE(ptr_proc)^:=$e9; PDWORD(DWORD(ptr_proc)+1)^:=DWORD(NewProc)-DWORD(ptr_proc)-5; Result:=ptr; end; А вот мой многострадальный пример :) var OriginalLoadString : function (hInstance: HINST; uID: UINT; lpBuffer: PChar; nBufferMax: Integer): Integer; stdcall; function MyLoadString(hInstance: HINST; uID: UINT; lpBuffer: PChar; nBufferMax: Integer): Integer; stdcall; begin Result:=OriginalLoadString(hInstance,uID,lpBuffer,nBufferMax); StrCopy(lpBuffer,'Опа'); Result:=3; end; begin @OriginalLoadString:=HookFunction('user32.dll','LoadStringA',@MyLoadString); end; |
| Автор: Chingachguk 2.4.2002, 01:35 |
| Слушай ! А я правильно понял, что ты врубил этот jmp ПРЯМО В ТЕЛО user32 Оба-на ! А я как-то пытался только прочитать(! jmp @@Go CallCreate: call CreateFile @@Go: ; Получим адрес jmp-а из команды call CreateFile mov eax,offset CallCreate+5 add eax,cs:[eax+1-5] ; Получим реальный адрес входа в CreateFile ; Получим offset адреса CreateFile из команды jmp ds:[CreateFileA] mov eax,cs:[eax+2] ; jmp ds:[addr] => 25FF(2 байта), addr(4 байта) ; Извлечем сам адрес по смещению в jmp-е mov ebx,ds:[eax] mov eax,ebx call PrintEAX ; Вывести реальный оффсет CreateFileA (сегмент наш) ; Перехватим точку входа в CreateFile mov eax,cs:[ebx] call PrintEAX ; Вывести 4 байта начала CreateFileA ... - И тут-то у меня был JPF Или надо было не через CS читать ?! А самое главное - теперь ЛЮБОЕ приложение, вызвавшее эту ф-цию, передасться тебе сперва ?! Али только если из твоего вызов будет, если для каждой задачи винда делает копию этих dll ? |
| Автор: TAPAKAH 2.4.2002, 23:09 |
| Я почти понял что ты делаешь .. но это слишком всё таки сложно для меня ... ассемблер я знаю плохо :) (но всё же вместо обьявления вызова функции и дальнейших преобразований легче получить её адрес по имени ... GetProcAddress ) А вот про схему работы рассказать могу :) То что там меняеться копируеться и так далее .. то как ты правильно заметил .. это только для моего адресного пространства Так как винда действительно при создании процесса подгружает все длл в его адресное пространство (во все его 4 гига или 2 :) не помню). => перехват произойдёт только при вызове этой функции из моего процесса. НО :) Всё просто супер .. так как существует такая вещь как SetWindowsHookEx с помощью которой можно внедриться во все процессы системы .. => можно написать некую длл в которой сделать свою функцию которую ты хотел бы использовать вместо стандартной и написать код внедрения её во все процессы .. и тогда бум :) и например вместо стандартного MessageBox будет показываться какой нить супер навороченный диалог :) MessageBox |
| Автор: TAPAKAH 2.4.2002, 23:33 |
| Кстати .. знаешь я посмотрел .. повнимательнее вроде стало ясно ... не знаю по теории всё верно .. да и по практике тоже. А попробуй разлочить на чтение (навряд ли конечно такой бред но кто знает) перед чтением первых четырёх байт самой функции CreateFile VirtualProtectEx(GetCurrentProcess(),ptr_proc, 4, PAGE_EXECUTE_READWRITE, OldProtectAPI)) Скорее всего из-за этого сыпется .. А читать сто пудов надо через cs .. :) |
| Автор: Fantasist 3.4.2002, 19:41 |
| Cлушай, ТАРАКАН, можешь в двух словах описать идею о внедрении в процессы с помощью SetWindowsHookEx? |
| Автор: TAPAKAH 3.4.2002, 23:05 |
| если тебе нужен рабочий пример на дельфи .. то пиши, солью Ну если не углублятся то вот вырезка из МСДН а The global hooks are a shared resource, and installing one affects all applications in the same desktop as the calling thread. All global hook functions must be in libraries. Global hooks should be restricted to special-purpose applications or to use as a development aid during application debugging. Libraries that no longer need a hook should remove its hook procedure. Ну в общем чего здесь написано .. а написано следующее если вы устанавливаете ловушку для всей системы .. то 1. ваша функция-ловушка должна лежать в длл 2. все процессы подгрузят эту длл в своё адресное пространство. Усё .. а дальше на инициализацию своей длл ки вешаешь перехват вызовов функции и получаешь то что я писал в предыдущем постинге. пример вызова SetWindowsHookEx(WH_GETMESSAGE,@CallBackHook,inst,0); и усё инст - HMODULE длл откуда была загружена CallBackHook Кстати .. см шутку .. это продолжение из MSDN Installing and Releasing Hook Procedures You can install a hook procedure by calling the SetWindowsHookEx function and specifying the type of hook calling the procedure, whether the procedure should be associated with all threads in the same desktop as the calling thread or with a particular thread, and a pointer to the procedure entry point. You must place a global hook procedure in a DLL separate from the application installing the hook procedure. The installing application must have the handle to the DLL module before it can install the hook procedure. To retrieve a handle to the DLL module, call the LoadLibrary function with the name of the DLL. After you have obtained the handle, you can call the GetProcAddress function to retrieve a pointer to the hook procedure. Finally, use SetWindowsHookEx to install the hook procedure address in the appropriate hook chain. SetWindowsHookEx passes the module handle, a pointer to the hook-procedure entry point, and 0 for the thread identifier, indicating that the hook procedure should be associated with all threads in the same desktop as the calling thread. This sequence is shown in the following example. HOOKPROC hkprcSysMsg; static HINSTANCE hinstDLL; static HHOOK hhookSysMsg; hinstDLL = LoadLibrary((LPCTSTR) "c:\\windows\\sysmsg.dll"); hkprcSysMsg = (HOOKPROC)GetProcAddress(hinstDLL, "SysMessageProc"); hhookSysMsg = SetWindowsHookEx(WH_SYSMSGFILTER,hkprcSysMsg,hinstDLL,0); You can release a thread-specific hook procedure (remove its address from the hook chain) by calling the UnhookWindowsHookEx function, specifying the handle to the hook procedure to release. Release a hook procedure as soon as your application no longer needs it. You can release a global hook procedure by using UnhookWindowsHookEx, but this function does not free the DLL containing the hook procedure. This is because global hook procedures are called in the process context of every application in the desktop, causing an implicit call to the LoadLibrary function for all of those processes. Because a call to the FreeLibrary function cannot be made for another process, there is then no way to free the DLL. The system eventually frees the DLL after all processes explicitly linked to the DLL have either terminated or called FreeLibrary and all processes that called the hook procedure have resumed processing outside the DLL. An alternative method for installing a global hook procedure is to provide an installation function in the DLL, along with the hook procedure. With this method, the installing application does not need the handle to the DLL module. By linking with the DLL, the application gains access to the installation function. The installation function can supply the DLL module handle and other details in the call to SetWindowsHookEx. The DLL can also contain a function that releases the global hook procedure; the application can call this hook-releasing function when terminating. А шутка в том что они насильно подгружают длл .. а вот выгружать бояться .. :) и правильно делают : You can release a global hook procedure by using UnhookWindowsHookEx, but this function does not free the DLL containing the hook procedure. This is because global hook procedures are called in the process context of every application in the desktop, causing an implicit call to the LoadLibrary function for all of those processes. Because a call to the FreeLibrary function cannot be made for another process, there is then no way to free the DLL. То бишь пока ты сам себя не выгрузишь .. либо процесс тебя случайно не найдёт и выгрузит .. либо он завершится .. твоя длл будет болтатся в адресном пространстве этого (любого - всех) процесса |
| Автор: Chingachguk 4.4.2002, 02:42 | ||
А, все равно это будет типа твоего варианта, только кривенько Если как ты гришь, ты ставишь бряк ТОЛЬКО в свою dll-ку, то и у меня то же самое ... А насчет cs&VirtualProtectEx я попробую ! |
| Автор: TAPAKAH 4.4.2002, 21:54 |
| Да блин .. всё таки я лажанулся в теории .. хук будет работать прекрассно .. но вот если вызвать оригинальную функцию то (иногда) будет шняга .. Предположим по аддресу в который мы впаяли jmp инструкцию из 5 байт, было 3 инструкции по 2 байта ... то мы получим просто офигительную ошибку потому что скопируем 2 инструкции и ещё половинку :) вот блин ... самое изящное решение которое я вижу .. копировать не 5 байт а то количество которое необходимо чтобы все инструкции остались "законченными" :) Так вот теперь трабл ... как определить длину инструкции в байтах ??? Chingachguk :) Спасай :) |
| Автор: Chingachguk 5.4.2002, 02:06 |
| Ааа ! ж)))) А я-то думаю - что это ты так уверенно заменил пять байтов ... Я винду тока начал рубить, думал - может, так и надо Точно сказать не могу, но вот как это делали вири в досе, когда не хотели менять в таблице прерываний адрес int 21h(ну это чтоб антивири не кричали "int 21 перехвачен !") Называется, кажется, сплайсинг. - Заготавливаем наш обработчик для Trace_Into(режим пошагового выполнения программы - это вроде int 3). Если включен режим трассировки(флаг TF), то после КАЖДОЙ асм-команды к нему переходит управление; - Переводим проц в трайсе-режим и выполняем команду int 21h. Следим за сегментом и смещением команды, с которой нам пришло прерывание - в нашем обработчике; - Как только пошли команды настоящего обработчика, то мониторя их, отмеряем нужное себе количество байт, которые надо поменять. Например, хотим воткнуть команду int 0FAh - переход на вирь. Те нужно 2 байта. Пусть оригинальный обработчик дос-а имеет такой вход: nop ; 1 байт есть, но нам надо два mov ax,bx ; опа, уже 2 байта - нам хватит. mov cx,bx ; - Это следующая команда Запоминаем эти три байта и втыкаем команду int 0EAh, теперь обработчик дос-а имеет вид: int 0FAh мусор в 1 байт, оставщийся от mov ax,bx mov cx,bx ; - Это следующая команда Теперь в обработчике int 0FAh сразу выполняем сохраненные две команды: nop , mov ax,bx И переходим на третью команду - на mov cx,bx. А что, никак в винде нельзя хук поставить на вызов апи-шек ? Стандартно ? Я вот только что прочел, что можно (вызывая стандартный сервис VMM VxD) поставить хуки на другие сервисы VxD |
| Автор: Chingachguk 14.4.2002, 10:37 |
| to ТАРАКАН: если интересно, мне в голову пришла идея, как сделать под вин эту проблему - ну, когда у тебя неизвестное число байт занимает первая команда в dll. Безо всяких трейсеров ... |
| Автор: TAPAKAH 14.4.2002, 22:40 |
| Конечно интересно! Я её уже сделал, но по железному :) надыбал исходник disasm на си сделал длл и подключил к своему проектику а в длл парочка полезных функций .. например прочитать по такому то адресу в памяти .. инструкцию .. функция читает и возвращает длину прочитанной инструкции. если интересно .. то могу выложить переделанный дельфийный код .. ну а длл .. вышлю мылом :) |
| Автор: Chingachguk 15.4.2002, 00:10 |
| Что я вроде придумал по этому поводу ? ж) Пусть есть некотрое начало DLL, например, такое: @@DLL_ENTRY: push ebp mov ebp,esp mov eax,[ebp+4] ; ... и некоторый вызов ее произошел с точки: @@Call_POINT: call @@DLL_ENTRY @@Next_User_Code: ; .. О'кей, мы не знаем ДЛЯ произвольной DLL длину инструкций, если только не дизасмить ее, как ты написал выше. Кстати, вышли мне исходняк на си, а тока чтобы был он не более 50-100 кил, ибо в ящик не влезет он мой Итак, запомним 5 байт от начала (c push ebp) - или столько ровно, чтобы на место этих байт воткнуть команду call OUR_PROC: @@DLL_ENTRY: call OUR_PROC ; Что-то, оставшееся от команды и т.д. Очевидно, при вызове DLL call разом перекинет нас в OUR_PROC. Что же мы будем делать в ней ? Как выполним деформированный код ? : OUR_PROC: push ebp mov ebp,esp ; Получим указатель на стек, там адрес возврата в ; @@Call_POINT ; Теперь мы воccтановим код по адресу @@DLL_ENTRY - ведь он ; сейчас разрушен врезкой команды call OUR_PROC ; Допустим, код мы сохранили в ячейке DLLSaveCode pushad ; Все регистры сейвим pushfd ; Флаги тоже mov esi,offset DLLSaveCode mov edi,offset @@DLL_ENTRY mov ecx,5 rep movsb ; Восстановили код DLL ; Теперь нам надо грамотно вернуться в точку DLL_ENTRY ; В стеке по адресу [ebp] валяется код возврата на 5 байт дальше ; чем нам надо, 5 байт = размер call OUR_PROC sub dword ptr [ebp],5 ; Вернемся назад, в DLL_ENTRY ; Так, а как же мы В СЛЕДУЮЩИЙ РАЗ получим управление - ведь команда ; call OUR_PROC замещена ОРИГИНАЛЬНЫМ кодом ? ; А вот как - засунем вместо АДРЕСА ВОЗВРАТА на точку перовго вызова - ; - @@Next_User_Code адрес нашей RET_PROC: ; Сначала сохраним этот адрес: mov eax,dword ptr [ebp+4] ; Вроде бы он там - надо бы проверить точно mov FinalRETAddr,eax ; А потом модифицируем стек: mov dword ptr [ebp+4],offset RET_PROC ; Все, теперь что-то делаем ... ; И выходим - следующая встреча в RET_PROC popfd popad pop ebp ret ; В эту точку мы попадаем вместо стандартной @@Next_User_Code RET_PROC: push ebp mov ebp,esp pushfd pushad ; Все сохранили. Нам нужно опять вбить код команды call OUR_PROC ; в точку @@DLL_ENTRY - это чтоб опять перехватить вызов ; Т.е. забиваем команду call OUR_PROC в тело DLL mov byte ptr @@DLL_ENTRY,Код_Команды_Call - вроде, 9Ah ? mov dword ptr @@DLL_ENTRY+1,Ну_там_Смещение_OUR_PROC ; Или что-то в этом духе - пишу в общем смысле ; Теперь вернемся на нужную точку @@Next_User_Code ; Ее адрес мы сохранили выше ; Все по местам - и возврат popad popfd pop ebp jmp dword ptr FinalRETAddr FinalRETAddr dd ? Вот вроде бы и все |
| Автор: TAPAKAH 15.4.2002, 19:29 |
| Не! Это мы уже проходили :) Усё верно .., но если у тебя в момент выполнения оригинального кода запустится эта же функция из другой нити .. то она у тебя не перехватится ... так как код будет восстановлен... в том то и ж***. Сишный исходник ща вышлю! |
| Автор: Chingachguk 17.4.2002, 12:07 |
| Да, ты совершенно прав ! Увы ! А я думал - такая красивая идея ... ;((( Кстати, мыл так и не дошел - правда, mail.ru не работал в понедельник >.. |
| Автор: TAPAKAH 18.4.2002, 07:34 |
| Знаешь .. может я и туплю .. но я чегой то не могу найти твоё мыло на форуме .. как отправить письмо с форума нашёл .. а вот где посмотреть куда отправляю неа... Скажи мыло плз! |
| Автор: Chingachguk 18.4.2002, 11:59 |
| vzubko@mail.ru |
| Автор: Mikel 18.4.2002, 19:41 |
| Ну дык вы че-нть решили? А то мне тоже эта тема интересна, а если есть исходники, вышлите плизmikel@smtp.ru |
| Автор: Mikel 20.4.2002, 19:15 |
| Посмотрел я твой исходник, спасибо. Но мне надо изменить для чужого АП. Можете что-нть подсказать? |
| Автор: TAPAKAH 21.4.2002, 00:03 |
| Ну вот! Приехали! Почитай постинги ... я ж уже писал .. что для чужого .. то же самое .. используешь SetWindowsHookEx и при инициализации своей библиотеки .. (в которой функция-хук ) проверяешь что она загружена в нужном процессе и вызываешь HookFunction |
| Автор: Mikel 21.4.2002, 07:42 |
| Спасибо, попробую. |
| Автор: neutrino 21.4.2002, 15:05 |
| А мне тоже эта тема интересна, вышлите пожалуйста: nitrene@hotbox.ru |
| Автор: Mikel 21.4.2002, 16:28 |
| Лучше выложи её здесь целиком, чтобы не отсылать всем. |
| Автор: Chingachguk 22.4.2002, 17:10 |
| Хочу заметить, что попытка использовать указанные выше принципы снятия запрета на модификацию "системного" кода (kernel32/user32...) будут работать не всегда ... Я вот пытался открыть доступ на запись в эти DLL ф-цией VirtualProtectEx - и у меня ничего не получалось(возвращалась ошибка). Но собственное приложение я мог открыть на запись. Думал я над этим ... Пока не прочел сегодня, что копирование при записи в 95-х (у меня именно они) не поддерживается to ТАРАКАН: если не секрет, какая у тебя ось была ? |
| Автор: TAPAKAH 22.4.2002, 20:56 |
| Хе хе ! :) У меня 2000 на работе и дома :) Так что селяви :) Теперь понятно почему у тебя не пахало :) Ну чтож .. будем знать! А ты где это прочёл .. ??? А под 98 пахать будет ??? |
| Автор: Chingachguk 23.4.2002, 02:07 |
| Прочел в "Технике сетевых атак" Криса Касперски ... Купил ... уже вчера Под 98 пахать не будет, ибо у меня как раз они, родимые ... |
| Автор: TAPAKAH 23.4.2002, 06:33 |
| Во блин! :) Ты меня огорчаешь .. А ты случайно не прочёл там как это можно обойти ???? |
| Автор: Chingachguk 23.4.2002, 11:49 |
| Нет, не прочел, и, думаю, не прочту Там написано: DLL - разделяемый ресурс. Если все дружно читают(вызывают) его, то все OK. А если кто-то пишет в него ? Тогда NT(UNIX, etc) создают копию именно для этого приложения. А 95 не создают(там так написано). Так что вот ;( |
| Автор: TAPAKAH 25.4.2002, 22:22 |
| Не знаю ! По поводу того что win95 использует таки уже загруженные библиотеки для разных процессов такую инфу я нашёл, а вот то что нельзя код изменить .. не було такого ... Лады .. остановимся на том что через пару дней я поставлю себе win95 и сам усё опробую. Сомнительно всё это! Возможно то о чём ты написал .. справедливо только для kernel32 ???? А вот по поводу НТ в моём случае 2000 я был абсолютно уверен что он каждый раз подгружает длл в память процесса .. по мере необходимости... (это конечно более ресурсоёмко зато на порядок надёжней..) И уверенность моя была основана .. на просмотре размера программ. В общем .. поставлю 95 и всё станет ясно .. |
| Автор: Chingachguk 26.4.2002, 02:22 |
| Насчет того, что кернел выделен - я проверил. Ты же из user32 функции хватал - и я попробовал ... Не пошло. И еще откуда-то - та же лажа. А копированием занимается исчо и юникс. |
| Автор: TAPAKAH 26.4.2002, 06:49 |
| В общем я просто предположил из за того что в 95 иногда в коде системных длл встречаются например вот такие вещи if module='kernel32' так что я надеюсь это обойти.. Ну через пару дней скажу чего получилось :) |
| Автор: TAPAKAH 10.5.2002, 15:39 |
| Тут Vit Написал про подведение итогов :) Подвожу! В 95/98 функция VirtualProtectEx не заработала! Итак функция перехвата действительно работает только под платформы написанные на базе НТ. WIN2000 Professional,Server- проверено, остальные не проверялись,но большое подозрение что работать будет. Алгоритм работы ещё раз и просто : Есть некий адрес. При выполнении кода по этому адресу .. нам надо выполнить свой код => берём и вставляем вместо того кода jmp а исходный (частично - 5 байт или больше) куда нибудь копируем. В результате что мы получим : Мы получим много если .. попробуем их оценить: 1. Если сумма длин инструкции (нашей функции) меньше 5 байт то есть вероятность что после изменения случится беда :). Но не переживайте .. вероятность того что она будет меньше 5 байт чрезвычайно мала. Подробности лучше узнать у Чингачгука .. как выглядит функция размером меньше 5 байт. (то есть если у вас эта функция была перехвачена 1 раз .. то с большой вероятностью она будет перехвачена снова .. так как размер у неё постоянен, конечно в рамках одной ОС :) ) Если вам очень хочется на крайний случай можно провести анализ кода (этих 5 байт на предмет ret ов) - динамически. Так что эта ошибка вполне может быть отсеяна. 2. Теперь вернёмся к оригинальному коду функции который мы перехватили. Если мы её всё же будем вызывать .. то опять же существует вероятность того что в её первых 5 байтах используются локальные смещения .. которые при переносе несомненно будут сылаться не туда куда надо .. но тут уже проще :) либо упадёт сразу .. либо не упадёт никогда. 3. Во всём остальном вещь работоспособная и что главное применима .. ко всему вашему адресному пространству... то бишь все функции из различных библиотек можно перехватить. 4. К примеру мне очень понравилось перехватывать метод TDataset.Open и выводить в лог запросы (что-то типа мониторинга обращения приложения к базе данных). Ну вот и всё .. ! P.S. Этим пользоваться можно Но только крайне осторожно! :) |
| Автор: TAPAKAH 10.5.2002, 16:12 |
| А вот исходники .. для них необходима правда ещё библиотека disasmf которая и нужна собственно для вычисления длины инструкции .. Если на форуме найдётся место то выложу. 2 Vit! Можно куда-нить скинуть ??? const DisAsm = 'disasmf.dll'; // Рабочий вариант.. использует jmp, возвращает указатель на оригинальную функцию function HookFunction(FuncModule : String; FuncName : String; NewProc : PDWORD) : PDWORD; // function UnAssembleMem(offset: DWORD): DWORD; stdcall; implementation function UnAssembleMem; external DisAsm name '_UnAssembleMem@4'; function HookFunction(FuncModule : String; FuncName : String; NewProc : PDWORD) : PDWORD; const JmpLen =5; var ptr : pointer; ptr_proc : PDWORD; OldProtectAPI : Cardinal; CodeLen : integer; InstructionLen : integer; begin Result:=nil; ptr_proc:=GetProcAddress(GetModuleHandle(PChar(FuncModule)),PChar(FuncName)); if not Assigned(ptr_proc) then Exit; // Определяем минимальный размер кода (больший чем размер инструкции jmp (e9)- 5 байт) CodeLen:=0; while CodeLen<JmpLen do begin InstructionLen:=UnAssembleMem(DWORD(ptr_proc)+CodeLen); if InstructionLen<=0 then raise Exception.Create('Это какая то неправильная инструкция :) по аддресу : '+IntToStr(DWORD(ptr_proc)+CodeLen)); Inc(CodeLen,InstructionLen); end; // Создаём новую функцию в которую запоминаем первые CodeLen байт + jmp на остальной код измененной функции ptr:=AllocMem(CodeLen+JmpLen); CopyMemory(ptr,ptr_proc,CodeLen); PBYTE(DWORD(ptr)+CodeLen)^:=$e9; PDWORD(DWORD(ptr)+CodeLen+1)^:=DWORD(DWORD(ptr_proc)-DWORD(ptr)-JmpLen); // Меняем оригинальную функцию : вставляем jmp на хук if(not VirtualProtectEx(GetCurrentProcess(),ptr_proc, CodeLen, PAGE_EXECUTE_READWRITE, OldProtectAPI)) then begin RaiseLastWin32Error; end; PBYTE(ptr_proc)^:=$e9; PDWORD(DWORD(ptr_proc)+1)^:=DWORD(NewProc)-DWORD(ptr_proc)-JmpLen; // Забиваем оставшуюся неиспользуемую часть кода NOP ами .. для выравнивания кода FillMemory(Pointer(DWORD(ptr_proc)+JmpLen),CodeLen-JmpLen,90); Result:=ptr; end; А вот пример :) var OriginalLoadString : function (hInstance: HINST; uID: UINT; lpBuffer: PChar; nBufferMax: Integer): Integer; stdcall; function MyLoadString(hInstance: HINST; uID: UINT; lpBuffer: PChar; nBufferMax: Integer): Integer; stdcall; begin Result:=OriginalLoadString(hInstance,uID,lpBuffer,nBufferMax); StrCopy(lpBuffer,'Опа'); Result:=3; end; begin @OriginalLoadString:=HookFunction('user32.dll','LoadStringA',@MyLoadString); end; |
| Автор: Vit 10.5.2002, 16:39 |
| Ну если очень большие исходники то можно мне на почту, если на форум влазят - то на форум. Мой ящик: nevzorov@yahoo.com, если письмо больше 20 метров то использовать vnevzorov@trondent.com. В ближайшее время перенесу это в FAQ, спасибо за отклик. |