Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.144.86.138] |
|
Сообщ.
#1
,
|
|
|
Библиотека:
//////////////////////////////////////////////////////////////////////////////// // // **************************************************************************** // * Unit Name : Changer // * Purpose : Библиотека для внедрения в удаленный процесс // : и перехвата функций // * Author : Александр (Rouse_) Багель // * Copyright : © Fangorn Wizards Lab 1998 - 2007 г. // * Version : 1.02 // * Home Page : http://rouse.drkb.ru // **************************************************************************** // library Changer; uses Windows, Messages, SysUtils, Winsock; const GlobMapID = '{84E5CFE5-30F8-425D-8A2C-98CF9530C0A2}'; const // Константы нотификаций NOTIFY_DLL_LOAD = 1; NOTIFY_API_CALL = 2; NOTIFY_API_INTERCEPT_SUCCESS = 3; NOTIFY_API_INTERCEPT_FAILED = 4; NOTIFY_DLL_UNLOAD = 5; type TReplaceStatus = (rsSuccess, rsFailed, rsNotFound); PIMAGE_IMPORT_BY_NAME = ^IMAGE_IMPORT_BY_NAME; {$EXTERNALSYM PIMAGE_IMPORT_BY_NAME} _IMAGE_IMPORT_BY_NAME = record Hint: Word; Name: array [0..0] of Char; end; {$EXTERNALSYM _IMAGE_IMPORT_BY_NAME} IMAGE_IMPORT_BY_NAME = _IMAGE_IMPORT_BY_NAME; {$EXTERNALSYM IMAGE_IMPORT_BY_NAME} TImageImportByName = IMAGE_IMPORT_BY_NAME; PImageImportByName = PIMAGE_IMPORT_BY_NAME; // Структура для нотификации приложения TLogData = record AppName: ShortString; // Имя приложения FuncName: String[30]; // Имя функции 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; // Структура в которой описываются все перехватываемые фукнции TInterceptedAPI = record Lib_Name: String; FuncName: String; FuncAddr: Pointer; OrigAddr: Pointer; Replaced: Boolean; end; TInterceptedAPIs = array of TInterceptedAPI; // Структуры для работы с таблицей импорта 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; PWSAEVENT = ^WSAEVENT; WSAEVENT = THandle; { WinSock 2 extension -- WSABUF and QOS struct, include qos.h } { to pull in FLOWSPEC and related definitions } WSABUF = packed record len: U_LONG; { the length of the buffer } buf: PChar; { the pointer to the buffer } end {WSABUF}; PWSABUF = ^WSABUF; LPWSABUF = PWSABUF; TServiceType = LongInt; TFlowSpec = packed record TokenRate, // In Bytes/sec TokenBucketSize, // In Bytes PeakBandwidth, // In Bytes/sec Latency, // In microseconds DelayVariation : LongInt;// In microseconds ServiceType : TServiceType; MaxSduSize, MinimumPolicedSize : LongInt;// In Bytes end; PFlowSpec = ^TFLOWSPEC; QOS = packed record SendingFlowspec: TFlowSpec; { the flow spec for data sending } ReceivingFlowspec: TFlowSpec; { the flow spec for data receiving } ProviderSpecific: WSABUF; { additional provider specific stuff } end; TQualityOfService = QOS; PQOS = ^QOS; LPQOS = PQOS; _OVERLAPPED = record Internal: DWORD; InternalHigh: DWORD; Offset: DWORD; OffsetHigh: DWORD; hEvent: THandle; end; PWSAOverlapped = ^_OVERLAPPED; LPWSAOVERLAPPED = PWSAOverlapped; LPCONDITIONPROC = function(lpCallerId: LPWSABUF; lpCallerData: LPWSABUF; lpSQOS, lpGQOS: LPQOS; lpCalleeId, lpCalleeData: LPWSABUF; g: DWORD; dwCallbackData: DWORD): Integer; stdcall; LPWSAOVERLAPPED_COMPLETION_ROUTINE = procedure(const dwError, cbTransferred: DWORD; const lpOverlapped: LPWSAOVERLAPPED; const dwFlags: DWORD); stdcall; function ImageDirectoryEntryToData(Base: Pointer; MappedAsImage: ByteBool; DirectoryEntry: Word; var Size: ULONG): Pointer; stdcall; external 'imagehlp.dll'; var MapHandle: THandle = 0; ShareInf: PShareInf = nil; InterceptedAPIs: TInterceptedAPIs; Data: TLogData; AppTitle: ShortString; // Перехват API посредством подмены в таблице импорта // ============================================================================= function ReplaceIATEntry(const OldProc, NewProc: FARPROC): TReplaceStatus; var ImportEntry: PImageImportDescriptor; Thunk: PImageThunkData; Protect, newProtect: DWORD; ImageBase: Cardinal; DOSHeader: PImageDosHeader; NTHeader: PImageNtHeaders; begin Result := rsNotFound; 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 Result := rsFailed; // Производим подмену, сначала так... if not IsBadWritePtr(@Thunk^._function, sizeof(DWORD)) then begin Thunk^._function := NewProc; Result := rsSuccess; 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 := rsSuccess; end; end; end else Inc(PChar(Thunk), SizeOf(TImageThunkData32)); end; ImportEntry := Pointer(Integer(ImportEntry) + SizeOf(TImageImportDescriptor)); end; end; end; // Функция которая будет работать вместо оригинальной Recv ... // ============================================================================= 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(InterceptedAPIs[0].OrigAddr)(s, Data.Buff[0], len, flags); // Получаем информацию кто с кем связался if getpeername(s, SockAddr, AddrLen) <> SOCKET_ERROR then begin Data.IP := inet_ntoa(SockAddr.sin_addr); Data.Port := ntohs(SockAddr.sin_port); end; 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; // Функция которая будет работать вместо оригинальной WSARecv ... // ============================================================================= function InterceptedWSARecv(s: TSocket; lpBuffers: LPWSABUF; dwBufferCount: DWORD; var lpNumberOfBytesRecvd: DWORD; dwFlags: DWORD; lpOverlapped: LPWSAOVERLAPPED; lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE): Integer; stdcall; type TWSARecvImage = function(s: TSocket; lpBuffers: LPWSABUF; dwBufferCount: DWORD; var lpNumberOfBytesRecvd: DWORD; dwFlags: DWORD; lpOverlapped: LPWSAOVERLAPPED; lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE): Integer; stdcall; var CDS: TCopyDataStruct; SockAddr: TSockAddr; AddrLen, I, Offset: Integer; Data: TLogData; TmpBuffers: LPWSABUF; TmpBuffer: PChar; begin // Первоначально вызываем оригинальную функцию, но данные будем писать в свой буффер... Result := TWSARecvImage(InterceptedAPIs[1].OrigAddr)(s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, dwFlags, lpOverlapped, lpCompletionRoutine); // Получаем информацию кто с кем связался if getpeername(s, SockAddr, AddrLen) <> SOCKET_ERROR then begin Data.IP := inet_ntoa(SockAddr.sin_addr); Data.Port := ntohs(SockAddr.sin_port); end; Data.BuffSize := lpNumberOfBytesRecvd; Data.AppName := AppTitle; // Тут можно встроить проверку (к примеру по какому нибудь порту) if True then begin Offset := 0; TmpBuffers := lpBuffers; for I := 0 to dwBufferCount - 1 do begin // проверка успешна - пишем в буфер полученные данные Move(TmpBuffers^.buf^, Data.Buff[Offset], TmpBuffers^.len); Inc(TmpBuffers); Inc(Offset, TmpBuffers^.len); end; end else begin Result := SOCKET_ERROR; // в противном случае говорим что вызов неуспешен. TmpBuffers := lpBuffers; for I := 0 to dwBufferCount - 1 do begin TmpBuffer := TmpBuffers^.buf; for Offset := 0 to TmpBuffers^.len - 1 do begin TmpBuffer^ := #0; Inc(TmpBuffer); end; end; lpNumberOfBytesRecvd := 0; end; // Отправляем полученные данные нашему приложению... CDS.dwData := NOTIFY_API_CALL; CDS.cbData := SizeOf(TLogData); CDS.lpData := @Data; SendMessage(ShareInf^.AppWndHandle, WM_COPYDATA, 0, Integer(@CDS)); end; // Функция которая будет работать вместо оригинальной WSARecvEx ... // ============================================================================= function InterceptedWSARecvEx(s: TSocket; var Buf; len: Integer; var flags: Integer): Integer; stdcall; type TWSARecvExImage = function(s: TSocket; var Buf; len: Integer; var flags: Integer): Integer; stdcall; var CDS: TCopyDataStruct; SockAddr: TSockAddr; AddrLen: Integer; Data: TLogData; begin // Первоначально вызываем оригинальную функцию, но данные будем писать в свой буффер... Result := TWSARecvExImage(InterceptedAPIs[2].OrigAddr)(s, Data.Buff[0], len, flags); // Получаем информацию кто с кем связался if getpeername(s, SockAddr, AddrLen) <> SOCKET_ERROR then begin Data.IP := inet_ntoa(SockAddr.sin_addr); Data.Port := ntohs(SockAddr.sin_port); end; 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 InitData; var FileName: array [0..MAX_PATH - 1] of Char; begin ZeroMemory(@FileName, SizeOf(FileName)); GetModuleFileName(HInstance, @FileName, SizeOf(FileName)); AppTitle := String(FileName); SetLength(InterceptedAPIs, 3); InterceptedAPIs[0].Lib_Name := 'wsock32.dll'; InterceptedAPIs[0].FuncName := 'recv'; InterceptedAPIs[0].FuncAddr := @InterceptedRecv; InterceptedAPIs[1].Lib_Name := 'ws2_32.dll'; InterceptedAPIs[1].FuncName := 'WSARecv'; InterceptedAPIs[1].FuncAddr := @InterceptedWSARecv; InterceptedAPIs[2].Lib_Name := 'ws2_32.dll'; InterceptedAPIs[2].FuncName := 'WSARecvEx'; InterceptedAPIs[2].FuncAddr := @InterceptedWSARecvEx; end; // Начало и завершение работы нашего хука ... // ============================================================================= procedure DLLEntryPoint(dwReason: DWORD); //stdcall; <- вот это как раз не нужно... var CDS: TCopyDataStruct; ImageBase: Cardinal; FileName: array [0..MAX_PATH - 1] of Char; I: Integer; ReplaceStatus: TReplaceStatus; begin case dwReason Of DLL_PROCESS_ATTACH: begin DisableThreadLibraryCalls(hInstance); InitData; // Все данные во избежании разрыва цепочки хуков // храним в отображаемом в память процесса файле, // только тогда все экземпляры хука будут владеть // достоверной информацией MapHandle := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, SizeOf(TShareInf), GlobMapID); ShareInf := MapViewOfFile(MapHandle, FILE_MAP_ALL_ACCESS, 0, 0, SizeOf(TShareInf)); // Получаем информацию о процессе в который подгружена наша библиотека ImageBase := GetModuleHandle(nil); ZeroMemory(@FileName, SizeOf(FileName)); GetModuleFileName(ImageBase, @FileName, SizeOf(FileName)); AppTitle := String(FileName); // Нотифицируем приложение о успешном внедрении библиотеки // И сообщаем информацию о процессе ZeroMemory(@Data, SizeOf(TLogData)); Data.AppName := AppTitle; CDS.dwData := NOTIFY_DLL_LOAD; CDS.cbData := SizeOf(TLogData); CDS.lpData := @Data; SendMessage(ShareInf^.AppWndHandle, WM_COPYDATA, 0, Integer(@CDS)); //if Pos('IEXPLORE.EXE', AnsiUpper(@FileName)) <> 0 then // Подменяем адреса вызовов функций своими обработчиками for I := 0 to Length(InterceptedAPIs) - 1 do begin InterceptedAPIs[I].Replaced := False; InterceptedAPIs[I].OrigAddr := GetProcAddress(GetModuleHandle(PChar(InterceptedAPIs[I].Lib_Name)), PChar(InterceptedAPIs[I].FuncName)); ReplaceStatus := rsNotFound; if InterceptedAPIs[I].OrigAddr <> nil then // Смотрим - успешно ли подменилась запись в таблице импорта? ReplaceStatus := ReplaceIATEntry(InterceptedAPIs[I].OrigAddr, InterceptedAPIs[I].FuncAddr); case ReplaceStatus of rsSuccess: begin CDS.dwData := NOTIFY_API_INTERCEPT_SUCCESS; // Успешно... InterceptedAPIs[I].Replaced := True; // Ставим флаг, что была замена... end; rsFailed: CDS.dwData := NOTIFY_API_INTERCEPT_FAILED; // Не успешно... rsNotFound: Continue; end; // Нотифицируем наше приложение о результате подмены... Data.FuncName := InterceptedAPIs[I].FuncName; Data.FuncPointer := Integer(InterceptedAPIs[I].OrigAddr); CDS.cbData := SizeOf(TLogData); CDS.lpData := @Data; SendMessage(ShareInf^.AppWndHandle, WM_COPYDATA, 0, Integer(@CDS)); end; end; DLL_PROCESS_DETACH: begin // Возвращаем изменения как они и были (если замена была удачна) for I := 0 to Length(InterceptedAPIs) - 1 do if InterceptedAPIs[I].Replaced then ReplaceIATEntry(InterceptedAPIs[I].FuncAddr, InterceptedAPIs[I].OrigAddr); // Нотифицируем приложение о выгрузке библиотеки CDS.dwData := NOTIFY_DLL_UNLOAD; CDS.cbData := SizeOf(TLogData); CDS.lpData := @Data; SendMessage(ShareInf^.AppWndHandle, WM_COPYDATA, 0, Integer(@CDS)); UnMapViewOfFile(ShareInf); CloseHandle(MapHandle); 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 : Changer // * Purpose : Пример внедрения в удаленный процесс // : и перехвата функций // * Author : Александр (Rouse_) Багель // * Copyright : © Fangorn Wizards Lab 1998 - 2007 г. // * Version : 1.02 // * Home Page : http://rouse.drkb.ru // **************************************************************************** // unit uMain; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; const // Константы нотификаций NOTIFY_DLL_LOAD = 1; NOTIFY_API_CALL = 2; NOTIFY_API_INTERCEPT_SUCCESS = 3; NOTIFY_API_INTERCEPT_FAILED = 4; NOTIFY_DLL_UNLOAD = 5; type // Структура для нотификации приложения PLogData = ^TLogData; TLogData = record AppName: ShortString; // Имя приложения FuncName: String[30]; // Имя функции FuncPointer: Integer; // Адрес функции IP: String[15]; // IP адрес Port: Cardinal; // Порт Buff: array [0..$FFFF] of Char; // Содержимое буффера BuffSize: Word; // Размер буфера end; TRecvLoggerMainDialog = class(TForm) memReport: TMemo; procedure FormDestroy(Sender: TObject); procedure FormCreate(Sender: TObject); private procedure WMCopyData(var Msg: TMessage); message WM_COPYDATA; end; function SetHook(Wnd: HWND): BOOL; stdcall; external 'Changer.dll' name 'SetHook'; function RemoveHook: BOOL; stdcall; external 'Changer.dll' name 'RemoveHook'; var RecvLoggerMainDialog: TRecvLoggerMainDialog; implementation {$R *.dfm} procedure TRecvLoggerMainDialog.FormCreate(Sender: TObject); begin if not SetHook(Handle) then MessageBox(Handle, 'Невозможно установить хук.', PChar(Application.Title), MB_OK or MB_ICONHAND); end; procedure TRecvLoggerMainDialog.FormDestroy(Sender: TObject); begin if not RemoveHook then MessageBox(Handle, 'Невозможно снять хук.', PChar(Application.Title), MB_OK or MB_ICONHAND); end; function ByteToHexStr(Data: Pointer; Len: Integer): String; var I, Octets, PartOctets: Integer; DumpData: String; begin if Len = 0 then Exit; I := 0; Octets := 0; PartOctets := 0; Result := ''; while I < Len do begin case PartOctets of 0: Result := Result + Format('%.4d: ', [Octets]); 9: begin Inc(Octets, 10); PartOctets := -1; Result := Result + ' ' + DumpData + sLineBreak; DumpData := ''; end; else begin Result := Result + Format('%s ', [IntToHex(TByteArray(Data^)[I], 2)]); if TByteArray(Data^)[I] in [$19..$FF] then DumpData := DumpData + Chr(TByteArray(Data^)[I]) else DumpData := DumpData + '.'; Inc(I); end; end; Inc(PartOctets); end; if PartOctets <> 0 then begin PartOctets := (8 - Length(DumpData)) * 3; Inc(PartOctets, 4); Result := Result + StringOfChar(' ', PartOctets) + DumpData end; end; procedure TRecvLoggerMainDialog.WMCopyData(var Msg: TMessage); const ReportLoad = 'Библиотека внедрена в приложение "%s"'; ReportIntercept = 'Приложение: "%s" IP %s:%d размер данных = %d '#13#10'%s'; ReportSucceeded = 'Перехват функции "%s" в модуле "%s" успешен.'; ReportFailed = 'Перехват функции "%s" в модуле "%s" неуспешен!!!'; ReportUnload = 'Библиотека выгружена из приложения "%s"'; var Data: TLogData; Buffer: String; begin Data := PLogData(PCopyDataStruct(Msg.LParam)^.lpData)^; // Типы нотификаций case PCopyDataStruct(Msg.LParam)^.dwData of NOTIFY_DLL_LOAD: // Пришло уведомление о внедрении библиотеки в удаленный процесс with Data do memReport.Lines.Add(Format(ReportLoad, [AppName])); NOTIFY_API_CALL: // Уведомление о вызове функции begin SetLength(Buffer, Data.BuffSize); Move(Data.Buff, Buffer[1], Data.BuffSize); with Data do memReport.Lines.Add(Format(ReportIntercept, [AppName, IP, Port, BuffSize, ByteToHexStr(@Buffer[1], BuffSize)])); 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])); NOTIFY_DLL_UNLOAD: // Пришло уведомление о выгрузке библиотеки with Data do memReport.Lines.Add(Format(ReportUnload, [AppName])); end; end; end. Автор: Rouse_ |