
Опытный
 
Профиль
Группа: Участник
Сообщений: 469
Регистрация: 23.4.2005
Репутация: 5 Всего: 29
|
А, все понятно. Библиотека это хук? Верно? Тогда смотри что происходит - ты находясь в чужом адресном пространстве выделяешь в нем память ложишь в нее данные потом нотифицируешь свое приложение куда передаешь адрес, а приложение пытается прочитать по этому адресу но уже в своем адресном пространстве, где естественно ничего не лежит. Вот тебе и вылет. тут тебе проще использовать WM_COPYDATA, ну а если хочешь всеже по своему принципу - тогда нужно делать ReadProcessMemory Добавлено @ 11:05 Вот смотри писал примерчик комуто перехвата вызова RECV в удаленном приложении: DLL: Код | //////////////////////////////////////////////////////////////////////////////// // // **************************************************************************** // * Unit Name : HookDLL // * Purpose : Демонстрационный пример хука и подмены API в приложениях... // * Author : Александр (Rouse_) Багель // * Version : 1.00 // **************************************************************************** //
library HookDLL;
uses Windows, Messages, Winsock;
const GlobMapID = 'Global Hook for API Interception {2E662583-74C4-45DB-B6DF-FE318C94258D}';
const // Константы нотификаций NOTIFY_DLL_INJECT = 1; NOTIFY_API_CALL = 2; NOTIFY_API_INTERCEPT_SUCCESS = 3; NOTIFY_API_INTERCEPT_FAILED = 4;
type // Структура для нотификации приложения TLogData = record AppName: ShortString; // Имя приложения FuncName: String[8]; // Имя функции FuncPointer: Integer; // Адрес функции IP: String[15]; // IP адрес Port: Cardinal; // Порт Buff: array [0..$FFFF] of Char; // Содержимое буффера BuffSize: Word; // Размер буфера end;
// Структура с рабочей информацией хука PShareInf = ^TShareInf; TShareInf = record AppWndHandle: HWND; OldHookHandle: HHOOK; hm:THandle; end;
// Структуры для работы с таблицей импорта TIIDUnion = record case Integer of 0: (Characteristics: DWORD); 1: (OriginalFirstThunk: DWORD); end;
PImageImportDescriptor = ^TImageImportDescriptor; TImageImportDescriptor = record Union: TIIDUnion; TimeDateStamp: DWORD; ForwarderChain: DWORD; Name: DWORD; FirstThunk: DWORD; end;
PImageThunkData = ^TImageThunkData32; TImageThunkData32 = packed record _function : PDWORD; end;
function ImageDirectoryEntryToData(Base: Pointer; MappedAsImage: ByteBool; DirectoryEntry: Word; var Size: ULONG): Pointer; stdcall; external 'imagehlp.dll';
var MapHandle: THandle = 0; ShareInf: PShareInf = nil; OldRecv: FARPROC = nil; Replaced: Boolean; AppTitle: ShortString;
// Перехват API посредством подмены в таблице импорта // ============================================================================= function ReplaceIATEntryInOneMod(const OldProc, NewProc: FARPROC): Boolean; var ImportEntry: PImageImportDescriptor; Thunk: PImageThunkData; Protect, newProtect: DWORD; ImageBase: Cardinal; DOSHeader: PImageDosHeader; NTHeader: PImageNtHeaders; begin Result := False; if OldProc = nil then Exit; if NewProc = nil then Exit; ImageBase := GetModuleHandle(nil);
// Зная структуру PE заголовка - находим начало таблицы импорта DOSHeader := PImageDosHeader(ImageBase); if IsBadReadPtr(Pointer(ImageBase), SizeOf(TImageNtHeaders)) then Exit; if (DOSHeader^.e_magic <> IMAGE_DOS_SIGNATURE) then Exit; NTHeader := PImageNtHeaders(DWORD(DOSHeader) + DWORD(DOSHeader^._lfanew)); if NTHeader^.Signature <> IMAGE_NT_SIGNATURE then Exit; ImportEntry := PImageImportDescriptor(DWORD(ImageBase) + DWORD(NTHeader^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)); if DWORD(ImportEntry) = DWORD(NTHeader) then Exit;
if ImportEntry <> nil then begin // Бежим по записям таблицы ... while ImportEntry^.Name <> 0 do begin Thunk := PImageThunkData(DWORD(ImageBase) + DWORD(ImportEntry^.FirstThunk)); // ... пока таблица не кончится ... while Thunk^._function <> nil do begin // ... или не найдем нужную нам запись. if (Thunk^._function = OldProc) then begin // Производим подмену, сначала так... if not IsBadWritePtr(@Thunk^._function, sizeof(DWORD)) then begin Thunk^._function := NewProc; Result := True; end else begin // ... ну а если не получилось - тогда вот так if VirtualProtect(@Thunk^._function, SizeOf(DWORD), PAGE_EXECUTE_READWRITE, Protect) then begin Thunk^._function := NewProc; newProtect := Protect; VirtualProtect(@Thunk^._function, SizeOf(DWORD), newProtect, Protect); Result := True; end; end; end else Inc(PChar(Thunk), SizeOf(TImageThunkData32)); end; ImportEntry := Pointer(Integer(ImportEntry) + SizeOf(TImageImportDescriptor)); end; end; end;
// Наша функция которая будет работать вместо оригинальной ... // ============================================================================= function InterceptedRecv(s: TSocket; var Buf; len, flags: Integer): Integer; stdcall; type TrecvImage = function(s: TSocket; var Buf; len, flags: Integer): Integer; stdcall; var CDS: TCopyDataStruct; SockAddr: TSockAddr; AddrLen: Integer; Data: TLogData; begin // Первоначально вызываем оригинальную функцию, но данные будем писать в свой буффер... Result := TrecvImage(OldRecv)(s, Data.Buff[0], len, flags); // Получаем информацию кто с кем связался if getpeername(s, SockAddr, AddrLen) = SOCKET_ERROR then Exit; Data.IP := inet_ntoa(SockAddr.sin_addr); Data.Port := ntohs(SockAddr.sin_port); Data.BuffSize := Result; Data.AppName := AppTitle;
// Тут можно встроить проверку (к примеру по какому нибудь порту) if True then Move(Data.Buff[0], Buf, Result) // проверка успешна - пишем в буфер полученные данные else Result := SOCKET_ERROR; // в противном случае говорим что вызов неуспешен.
// Отправляем полученные данные нашему приложению... CDS.dwData := NOTIFY_API_CALL; CDS.cbData := SizeOf(TLogData); CDS.lpData := @Data; SendMessage(ShareInf^.AppWndHandle, WM_COPYDATA, 0, Integer(@CDS)); end;
// Начало и завершение работы нашего хука ... // ============================================================================= procedure DLLEntryPoint(dwReason: DWORD); //stdcall; <- вот это как раз не нужно... var CDS: TCopyDataStruct; Data: TLogData; ImageBase: Cardinal; FileName: array [0..MAX_PATH - 1] of Char; begin case dwReason Of DLL_PROCESS_ATTACH: begin // Все данные во избежании разрыва цепочки хуков храним в отображаемом в память процесса файле, // только тогда все экземпляры хука будут владеть достоверной информацией MapHandle := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, SizeOf(TShareInf), GlobMapID); ShareInf := MapViewOfFile(MapHandle, FILE_MAP_ALL_ACCESS, 0, 0, SizeOf(TShareInf));
// Получаем информацию о процессе в который подгружена наша библиотека Replaced := False; OldRecv := GetProcAddress(GetModuleHandle('wsock32.dll'), 'recv'); DisableThreadLibraryCalls(hInstance); ImageBase := GetModuleHandle(nil); ZeroMemory(@FileName, SizeOf(FileName)); GetModuleFileName(ImageBase, @FileName, SizeOf(FileName)); AppTitle := String(FileName);
// Нотифицируем приложение о успешном внедрении библиотеки // И сообщаем информацию о процессе ZeroMemory(@Data, SizeOf(TLogData)); Data.AppName := AppTitle; Data.FuncName := 'recv'; Data.FuncPointer := Integer(OldRecv);
CDS.dwData := NOTIFY_DLL_INJECT; CDS.cbData := SizeOf(TLogData); CDS.lpData := @Data; SendMessage(ShareInf^.AppWndHandle, WM_COPYDATA, 0, Integer(@CDS));
// Подменяем процедуры своими (если это нужное нам приложение) if Pos('NETCHAT.EXE', AnsiUpper(@FileName)) <>0 then begin if OldRecv <> nil then // Смотрим - успешно ли подменилась запись в таблице импорта? if ReplaceIATEntryInOneMod(OldRecv, @InterceptedRecv) then begin CDS.dwData := NOTIFY_API_INTERCEPT_SUCCESS; // Успешно... Replaced := True; // Ставим флаг, что была замена... end else CDS.dwData := NOTIFY_API_INTERCEPT_FAILED; // Не успешно...
// Нотифицируем наше приложение о результате подмены... CDS.cbData := SizeOf(TLogData); CDS.lpData := @Data; SendMessage(ShareInf^.AppWndHandle, WM_COPYDATA, 0, Integer(@CDS)); end; end; DLL_PROCESS_DETACH: begin UnMapViewOfFile(ShareInf); CloseHandle(MapHandle); // Возвращаем изменения как они и были (если замена была удачна) if Replaced then ReplaceIATEntryInOneMod(@InterceptedRecv, OldRecv); end; end; end;
// Это наш хук, он нужен только для внедрения в удаленный процесс ... // ============================================================================= function Hook(Code: Integer; WParam: WPARAM; LParam: LPARAM): LRESULT; stdcall; begin Result := CallNextHookEx(ShareInf^.OldHookHandle, Code, WParam, LParam); // вызываем след. ловушку end;
// Установка хука ... // ============================================================================= function SetHook(Wnd: HWND): BOOL; stdcall; begin if ShareInf <> nil then begin ShareInf^.AppWndHandle := Wnd; ShareInf^.OldHookHandle := SetWindowsHookEx(WH_GETMESSAGE, @Hook, HInstance, 0); // <- Обратите внимание, не допускаем главной ошибки Result := ShareInf^.OldHookHandle <> 0; end else Result:=False; end;
// Снятие хука ... // ============================================================================= function RemoveHook: BOOL; stdcall; begin Result := UnhookWindowsHookEx(ShareInf^.OldHookHandle); CloseHandle(ShareInf^.hm); end;
exports SetHook, RemoveHook;
begin DLLProc := @DLLEntryPoint; DLLEntryPoint(DLL_PROCESS_ATTACH); end.
|
Приложение: Код | //////////////////////////////////////////////////////////////////////////////// // // **************************************************************************** // * Unit Name : uMain // * Purpose : Демонстрационный пример хука и подмены API в приложениях... // * Author : Александр (Rouse_) Багель // * Version : 1.00 // **************************************************************************** //
unit uMain;
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ActiveX;
const // Константы нотификаций NOTIFY_DLL_INJECT = 1; NOTIFY_API_CALL = 2; NOTIFY_API_INTERCEPT_SUCCESS = 3; NOTIFY_API_INTERCEPT_FAILED = 4;
type TLogData = record AppName: ShortString; // Имя приложения FuncName: String[8]; // Имя функции FuncPointer: Integer; // Адрес функции IP: String[15]; // IP адрес Port: Cardinal; // Порт Buff: array [0..$FFFF] of Char; // Содержимое буффера BuffSize: Word; // Размер буфера end; PLogData = ^TLogData;
THADemo = class(TForm) memReport: TMemo; procedure FormCreate(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); private procedure WMCopyData(var Msg: TMessage); message WM_COPYDATA; end;
function SetHook(Wnd: HWND): BOOL; stdcall; external 'HookDLL.dll' name 'SetHook'; function RemoveHook: BOOL; stdcall; external 'HookDLL.dll' name 'RemoveHook';
var HADemo: THADemo;
implementation
{$R *.dfm}
{ TForm1 }
procedure THADemo.FormCreate(Sender: TObject); begin if not SetHook(Handle) Then MessageBox(Handle, 'Невозможно установить хук.', PChar(Application.Title), MB_OK OR MB_ICONHAND); end;
procedure THADemo.FormClose(Sender: TObject; var Action: TCloseAction); begin if not RemoveHook Then MessageBox(Handle, 'Невозможно снять хук.', PChar(Application.Title), MB_OK OR MB_ICONHAND); end;
procedure THADemo.WMCopyData(var Msg: TMessage); const ReportInject = 'Библиотека внедрена в приложение "%s", функция "%s" имеет адрес: $%s'; ReportIntercept = 'Приложение: "%s" IP %s:%d размер данных = %d буфер = "%s"'; ReportSucceeded = 'Перехват функции "%s" в модуле "%s" успешен.'; ReportFailed = 'Перехват функции "%s" в модуле "%s" неуспешен!!!'; var Data: TLogData; Buffer: String; begin Data := PLogData(PCopyDataStruct(Msg.LParam)^.lpData)^; // Типы нотификаций case PCopyDataStruct(Msg.LParam)^.dwData of NOTIFY_DLL_INJECT: // Пришло уведомление о внедрении библиотеки в удаленный процесс with Data do memReport.Lines.Add(Format(ReportInject, [AppName, FuncName, IntToHex(Data.FuncPointer, 8)])); NOTIFY_API_CALL: // Уведомление о вызове функции begin SetLength(Buffer, Data.BuffSize); Move(Data.Buff[0], Buffer[1], Data.BuffSize); with Data do memReport.Lines.Add(Format(ReportIntercept, [AppName, IP, Port, BuffSize, Buffer])); end; NOTIFY_API_INTERCEPT_SUCCESS: // Уведомление о удачной подмене таблицы импорта with Data do memReport.Lines.Add(Format(ReportSucceeded, [FuncName, AppName])); NOTIFY_API_INTERCEPT_FAILED: // Уведомление о неудачной подмене таблицы импорта with Data do memReport.Lines.Add(Format(ReportFailed, [FuncName, AppName])); end; end;
end.
|
Но правильней всего внедрять не хуком а через CreateRemoteThread Примерно вот так: Код | //////////////////////////////////////////////////////////////////////////////// // // **************************************************************************** // * Project : Inject/Eject Library Demo // * Unit Name : main // * Purpose : Демонстрационный пример внедрения библиотеки через CreateRemoteThread // * Author : Александр (Rouse_) Багель // * Version : 1.00 // **************************************************************************** //
unit main;
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
resourcestring BTN_INJECT = 'Inject'; BTN_EJECT = 'Eject'; const DLLName = 'hooklib.dll';
type TfrmMain = class(TForm) btnInjectEject: TButton; GroupBox: TGroupBox; lbStatus: TListBox; procedure btnInjectEjectClick(Sender: TObject); private function InjectLib(const ProcessID: DWORD): Boolean; function EjectLib(const ProcessID: DWORD): Boolean; end;
// Декларация функций при помощи которых будет происходить выгрузка билиотеки TGetModuleHandle = function (lpModuleName: PChar): HMODULE; stdcall; TFreeLibrary = function (hLibModule: HMODULE): BOOL; stdcall;
// Структура передаваемая потоковой функции при выгрузке библиотеки PEjectLibStruct = ^TEjectLibStruct; TEjectLibStruct = record hGetModuleHandle: TGetModuleHandle; hFreeLibrary: TFreeLibrary; lpModuleName: PChar; end;
var frmMain: TfrmMain;
implementation
{$R *.dfm}
{ TfrmMain }
// Обработчик кнопки на внедрение/выгрузку библиотеки // ============================================================================= procedure TfrmMain.btnInjectEjectClick(Sender: TObject); begin TComponent(Sender).Tag := TComponent(Sender).Tag + 1; if (TComponent(Sender).Tag mod 2) = 1 then begin btnInjectEject.Caption := BTN_EJECT; if InjectLib(GetCurrentProcessID) then lbStatus.Items.Add('Library injected succes.') else lbStatus.Items.Add('Library injected fail.'); end else begin btnInjectEject.Caption := BTN_INJECT; if EjectLib(GetCurrentProcessID) then lbStatus.Items.Add('Library ejected succes.') else lbStatus.Items.Add('Library ejected fail.'); end; end;
// Пока наш процесс не получит отлабочные привилегии, // весь этот код работать не будет // ============================================================================= function SetDebugPriv: Boolean; var Token: THandle; tkp: TTokenPrivileges; ReturnLength: DWORD; begin Result := false; // Получаем токен текущего процесса if OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, Token) then begin // Получаем Luid привилегии if LookupPrivilegeValue(nil, PChar('SeDebugPrivilege'), tkp.Privileges[0].Luid) then begin // Заполняем необходимые параметры tkp.PrivilegeCount := 1; tkp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; // Включаем привилегию Result := AdjustTokenPrivileges(Token, false, tkp, 0, nil, ReturnLength); end; end; end;
// Функция внедряет библиотеку в удаленный процесс с PID равным ProcessID // Для успешного внедрения нужно передать адрес функции LoadLibraryA // и путь к загружаемой библиотеке. // Строку с путем необходимо разместить в алресном пространстве удаленного процесса // ============================================================================= function TfrmMain.InjectLib(const ProcessID: DWORD): Boolean; var Process: HWND; ThreadRtn: FARPROC; DllPath: String; RemoteDll: Pointer; BytesWriten: DWORD; Thread: DWORD; ThreadId: DWORD; ExitCode: DWORD; begin // Устанавливаем отладочные привилегии для нашего процесса Result := SetDebugPriv; if not Result then Exit; Process := 0; Thread := 0; try // Открываем процесс Process := OpenProcess(PROCESS_CREATE_THREAD or PROCESS_VM_OPERATION or PROCESS_VM_WRITE, True, ProcessID); if Process = 0 then Exit; // Выделяем в нем память под строку DllPath := ExtractFilePath(ParamStr(0)) + DLLName; RemoteDll := VirtualAllocEx(Process, nil, Length(DllPath), MEM_COMMIT or MEM_TOP_DOWN, PAGE_READWRITE); if RemoteDll = nil then Exit; // Пишем путь к длл в его адресное пространство if not WriteProcessMemory(Process, RemoteDll, PChar(DllPath), Length(DllPath), BytesWriten) then Exit; if BytesWriten <> DWORD(Length(DllPath)) then Exit; // Получаем адрес функции из Kernel32.dll ThreadRtn := GetProcAddress(GetModuleHandle('Kernel32.dll'), 'LoadLibraryA'); if ThreadRtn = nil then Exit; // Запускаем удаленный поток Thread := CreateRemoteThread(Process, nil, 0, ThreadRtn, RemoteDll, 0, ThreadId); if Thread = 0 then Exit; // Ждем пока удаленный поток отработает... if (WaitForSingleObject(Thread, INFINITE) = WAIT_OBJECT_0) then if GetExitCodeThread(Thread, ExitCode) then Result := ExitCode = 0; finally // Удаленный поток свою задачу выполнил и загрузил нашу библиотеку, // можно освобождать занятую память... if RemoteDll <> nil then VirtualFreeEx(Process, @RemoteDll, 0, MEM_RELEASE); if Thread <> 0 then CloseHandle(Thread); if Process <> 0 then CloseHandle(Process); end; end;
// Для того чтобы выгрузить библиотеку, необходимо найти ее адрес в удаленном // процессе и вызвать там же FreeLibrary // Этим у нас будет заниматься вот такая функция // Для успешной ее работы необходимо передать 3 параметра. // 1: Адреса функций GetModuleHandle и FreeLibrary; // 2: Имя модуля, выгрузку которого мы будем производить // ============================================================================= function RemoteFreeLibrary(lpParameter: Pointer): DWORD; stdcall; var hLibModule: HMODULE; begin Result := 0; if lpParameter = nil then Exit; // Получаем описатель нашей библиотеки (используем переданные параметры) hLibModule := TGetModuleHandle(PEjectLibStruct(lpParameter)^.hGetModuleHandle) (PEjectLibStruct(lpParameter)^.lpModuleName); if hLibModule <> 0 then // Выгружаем библиотеку Result := DWORD(TFreeLibrary(PEjectLibStruct(lpParameter)^.hFreeLibrary)(hLibModule)); end;
// Данная функция запускает в удаленном процессе поток // с потоковой функцией RemoteFreeLibrary // и подготавливает для ее работы необходимые данные // ============================================================================= function TfrmMain.EjectLib(const ProcessID: DWORD): Boolean; var Process: HWND; BytesWriten: DWORD; Thread: DWORD; ThreadId: DWORD; ExitCode: DWORD; EjectLibStruct: TEjectLibStruct; EjectLibStructAddr: Pointer; begin Result := False; Process := 0; Thread := 0; try // Открываем процесс Process := OpenProcess(PROCESS_CREATE_THREAD or PROCESS_VM_OPERATION or PROCESS_VM_WRITE, True, ProcessID); if Process = 0 then Exit; // Выделяем в нем память под имя модуля EjectLibStruct.lpModuleName := VirtualAllocEx(Process, nil, Length(DLLName), MEM_COMMIT or MEM_TOP_DOWN, PAGE_READWRITE); if EjectLibStruct.lpModuleName = nil then Exit; // Пишем имя модуля в его адресное пространство if not WriteProcessMemory(Process, EjectLibStruct.lpModuleName, PChar(DLLName), Length(DLLName), BytesWriten) then Exit; if BytesWriten <> DWORD(Length(DLLName)) then Exit; // Получаем адрес функции FreeLibrary из Kernel32.dll EjectLibStruct.hFreeLibrary := GetProcAddress(GetModuleHandle('Kernel32.dll'), 'FreeLibrary'); if not Assigned(EjectLibStruct.hFreeLibrary) then Exit; // Получаем адрес функции GetModuleHandle из Kernel32.dll EjectLibStruct.hGetModuleHandle := GetProcAddress(GetModuleHandle('Kernel32.dll'), 'GetModuleHandleA'); if not Assigned(EjectLibStruct.hGetModuleHandle) then Exit; // Выделяем память под структуру, которая передается нашей функции EjectLibStructAddr := VirtualAllocEx(Process, nil, SizeOf(TEjectLibStruct), MEM_COMMIT or MEM_TOP_DOWN, PAGE_READWRITE); if EjectLibStructAddr = nil then Exit; // Пишем саму структуру if not WriteProcessMemory(Process, EjectLibStructAddr, @EjectLibStruct, SizeOf(TEjectLibStruct), BytesWriten) then Exit; if BytesWriten <> DWORD(SizeOf(TEjectLibStruct)) then Exit;
// Запускаем удаленный поток Thread := CreateRemoteThread(Process, nil, 0, @RemoteFreeLibrary, EjectLibStructAddr, 0, ThreadId); if Thread = 0 then Exit; // Ждем пока удаленный поток отработает... if (WaitForSingleObject(Thread, INFINITE) = WAIT_OBJECT_0) then if GetExitCodeThread(Thread, ExitCode) then Result := BOOL(ExitCode); finally // Удаленный поток свою задачу выполнил и выгрузил нашу библиотеку, // можно освобождать занятую память... VirtualFreeEx(Process, @EjectLibStruct.lpModuleName, 0, MEM_RELEASE); VirtualFreeEx(Process, @EjectLibStructAddr, 0, MEM_RELEASE); if Thread <> 0 then CloseHandle(Thread); if Process <> 0 then CloseHandle(Process); end; end;
end.
|
|