Демонстрационный пример хука и подмены API в приложениях

Демонстрационный пример хука и подмены API в приложениях

////////////////////////////////////////////////////////////////////////////////
//
// ****************************************************************************
// * Unit Name : HookDLL
// * Purpose : ...
// * 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 := CallHookEx(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 : ...
// * 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.
Автор: Александр (Rouse_) Багель Взято из http://forum.sources.ru Демонстрационный пример перехвата вызовов API функций, посредством изменения таблицы импорта.
Скачать демонстрационный пример Александр (Rouse_) Багель http://rouse.drkb.ru

Отправить комментарий

Проверка
Антиспам проверка
Image CAPTCHA
...