DelphiHookApi(經典)

論壇裏有關於HOOK API的貼子, 但其實如今方式顯示得麻煩, 其實如今攔截API通常不用那種方式, 大都採用inline Hook API方式。其實也就是直接修改了要攔截的API源碼的頭部,讓它無條件跳轉到咱們本身的處理過程。編程

   很少說別的了,開始咱們本身的Hook API吧。app

   咱們今天要攔截的API以下:編程語言

   MessageBoxA、MessageBoxW、MessageBeep 和 OpenProcess 。ide

   首先,你們都知道要在整個系統範圍中攔截,須要使用Dll來完成。如今咱們打開Delphi 2009,新建一個Dll工程:hookDll。須要說明的是,Delphi是徹底面向對象的編程語言,因此咱們不要浪費,這個Dll打算用類的方式完成。因而,在新建的DLL工程中在添加一個Unit Pas,命名爲unitHook, 用來寫攔截類的處理。unitHook.pas中的代碼以下:測試

unit unitHook;指針

interfaceorm

uses
  Windows, Messages, Classes, SysUtils;對象

type進程

  //NtHook類相關類型
  TNtJmpCode=packed record  //8字節
    MovEax:Byte;
    Addr:DWORD;
    JmpCode:Word;
    dwReserved:Byte;
  end;內存

  TNtHookClass=class(TObject)
  private
    hProcess:THandle;
    NewAddr:TNtJmpCode;
    OldAddr:array[0..7] of Byte;
    ReadOK:Boolean;
  public
    BaseAddr:Pointer;
    constructor Create(DllName,FuncName:string;NewFunc:Pointer);
    destructor Destroy; override;
    procedure Hook;
    procedure UnHook;
  end;

implementation

//==================================================
//NtHOOK 類開始
//==================================================
constructor TNtHookClass.Create(DllName: string; FuncName: string;NewFunc:Pointer);
var
  DllModule:HMODULE;
  dwReserved:DWORD;
begin
  //獲取模塊句柄
  DllModule:=GetModuleHandle(PChar(DllName));
  //若是得不到說明未被加載
  if DllModule=0 then DllModule:=LoadLibrary(PChar(DllName));
  //獲得模塊入口地址(基址)
  BaseAddr:=Pointer(GetProcAddress(DllModule,PChar(FuncName)));
  //獲取當前進程句柄
  hProcess:=GetCurrentProcess;
  //指向新地址的指針
  NewAddr.MovEax:=$B8;
  NewAddr.Addr:=DWORD(NewFunc);
  NewAddr.JmpCode:=$E0FF;
  //保存原始地址
  ReadOK:=ReadProcessMemory(hProcess,BaseAddr,@OldAddr,8,dwReserved);
  //開始攔截
  Hook;
end;

//釋放對象
destructor TNtHookClass.Destroy;
begin
  UnHook;
  CloseHandle(hProcess);

  inherited;
end;

//開始攔截
procedure TNtHookClass.Hook;
var
  dwReserved:DWORD;
begin
  if (ReadOK=False) then Exit;
  //寫入新的地址
  WriteProcessMemory(hProcess,BaseAddr,@NewAddr,8,dwReserved);
end;

//恢復攔截
procedure TNtHookClass.UnHook;
var
  dwReserved:DWORD;
begin
  if (ReadOK=False) then Exit;
  //恢復地址
  WriteProcessMemory(hProcess,BaseAddr,@OldAddr,8,dwReserved);
end;

end.
至此,unitHook.pas的代碼OK了,其中加了詳細的註釋,在此就再也不多作解釋。如今切換到Dll的代碼頁,
寫入如下代碼:
 
library hookdll;

uses
  SysUtils, Windows,
  Classes,
  unitHook in 'unitHook.pas';

{$R *.res}

const
  HOOK_MEM_FILENAME  =  'tmp.hkt';

var
  hhk: HHOOK;
  Hook: array[0..3] of TNtHookClass;

  //內存映射
  MemFile: THandle;
  startPid: PDWORD;   //保存PID

{--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--}

//攔截 MessageBoxA
function NewMessageBoxA(_hWnd: HWND; lpText, lpCaption: PAnsiChar; uType: UINT):
Integer; stdcall;
type
  TNewMessageBoxA = function (_hWnd: HWND; lpText, lpCaption: PAnsiChar; uType:
UINT): Integer; stdcall;
begin
  lpText := PAnsiChar('已經被攔截 MessageBoxA');
  Hook[0].UnHook;
  Result := TNewMessageBoxA(Hook[0].BaseAddr)(_hWnd, lpText, lpCaption, uType);
  Hook[0].Hook;
end;

//攔截 MessageBoxW
function NewMessageBoxW(_hWnd: HWND; lpText, lpCaption: PWideChar; uType: UINT):
Integer; stdcall;
type
  TNewMessageBoxW = function (_hWnd: HWND; lpText, lpCaption: PWideChar; uType:
UINT): Integer; stdcall;
begin
  lpText := '已經被攔截 MessageBoxW';
  Hook[2].UnHook;
  Result := TNewMessageBoxW(Hook[2].BaseAddr)(_hWnd, lpText, lpCaption, uType);
  Hook[2].Hook;
end;

//攔截 MessageBeep
function NewMessageBeep(uType: UINT): BOOL; stdcall;
type
  TNewMessageBeep = function (uType: UINT): BOOL; stdcall;
begin
  Result := True;
end;

//攔截 OpenProcess , 防止關閉
function NewOpenProcess(dwDesiredAccess: DWORD; bInheritHandle: BOOL; dwProcessId:
DWORD): THandle; stdcall;
type
  TNewOpenProcess = function (dwDesiredAccess: DWORD; bInheritHandle: BOOL;
dwProcessId: DWORD): THandle; stdcall;
begin
  if startPid^ = dwProcessId then begin
    result := 0;
    Exit;
  end;
  Hook[3].UnHook;
  Result := TNewOpenProcess(Hook[3].BaseAddr)(dwDesiredAccess, bInheritHandle,
dwProcessId);
  Hook[3].Hook;
end;

{--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--}

//安裝API Hook
procedure InitHook;
begin
  Hook[0] := TNtHookClass.Create('user32.dll', 'MessageBoxA', @NewMessageBoxA);
  Hook[1] := TNtHookClass.Create('user32.dll', 'MessageBeep', @NewMessageBeep);
  Hook[2] := TNtHookClass.Create('user32.dll', 'MessageBoxW', @NewMessageBoxW);
  Hook[3] := TNtHookClass.Create('kernel32.dll', 'OpenProcess', @NewOpenProcess);
end;

//刪除API Hook
procedure UninitHook;
var
  I: Integer;
begin
  for I := 0 to High(Hook) do
  begin
    FreeAndNil(Hook[I]);
  end;
end;

{--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--}

//內存映射共想
procedure MemShared();
begin
  MemFile:=OpenFileMapping(FILE_MAP_ALL_ACCESS,False, HOOK_MEM_FILENAME);
if MemFile = 0 then begin  //打開失敗則衉c2建內存映射文件
    MemFile := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0,
                              4, HOOK_MEM_FILENAME);
  end;
  if MemFile <> 0 then
    //映射文件到變量
    startPid := MapViewOfFile(MemFile,FILE_MAP_ALL_ACCESS,0,0,0);
end;

//傳遞消息
function HookProc(nCode, wParam, lParam: Integer): Integer; stdcall;
begin
  Result := CallNextHookEx(hhk, nCode, wParam, lParam);
end;

//開始HOOK
procedure StartHook(pid: DWORD); stdcall;
begin
  startPid^ := pid;
  hhk := SetWindowsHookEx(WH_CALLWNDPROC, HookProc, hInstance, 0);
end;

//結束HOOK
procedure EndHook; stdcall;
begin
  if hhk <> 0 then
    UnhookWindowsHookEx(hhk);
end;

//環境處理
procedure DllEntry(dwResaon: DWORD);
begin
  case dwResaon of
    DLL_PROCESS_ATTACH: InitHook;   //DLL載入
    DLL_PROCESS_DETACH: UninitHook; //DLL刪除
  end;
end;

exports
  StartHook, EndHook;

begin
  MemShared;

  { 分配DLL程序到 DllProc 變量 }
  DllProc := @DllEntry;
  { 調用DLL加載處理 }
  DllEntry(DLL_PROCESS_ATTACH);
end.

     這樣,咱們用來hook API 的 Dll 就完工了。 在Dll中,咱們還使用到了內存映射,用來實如今攔

截全局時的內存共享,如這個例子中須要保存調用此hook的進程句柄,以防止經過任務管理器關閉示例程序。

   編譯生成 hookdll.dll 文件,就可使用了。如今咱們再來創建一個測試用的程序。

   如附圖所示,畫3個按鈕,分別爲"Hook"、"UnHook"、"MessageBox",前兩個用來

安裝和刪除鉤子,第三個用來顯示一個消息框,你將會看到被Hook後的狀況。測試工程的代碼以下:


unit FMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TfrmMain = class(TForm)
    btnHook: TButton;
    btnUnhook: TButton;
    Button1: TButton;
    procedure btnHookClick(Sender: TObject);
    procedure btnUnhookClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  frmMain: TfrmMain;

  procedure StartHook(pid: DWORD); stdcall; external 'hookdll.dll';
  procedure EndHook; stdcall; external 'hookdll.dll';

implementation

{$R *.dfm}

procedure TfrmMain.btnHookClick(Sender: TObject);
begin
  StartHook(GetCurrentProcessId);
end;

procedure TfrmMain.btnUnhookClick(Sender: TObject);
begin
  EndHook;
end;

procedure TfrmMain.Button1Click(Sender: TObject);
begin
  MessageBox(0, 'abdfadfasdf', nil, 0);
end;

procedure TfrmMain.FormCreate(Sender: TObject);
begin

end;

end.
 
      完成後運行,先不點擊"hook"按鈕,直接點擊MessageBox,你會發現如今已經被攔截了
。爲何咱們尚未安裝鉤子就被攔截了呢?程序出錯了嗎?呵呵。固然沒有出錯。反過來看看DLL中
的一處代碼:
.............

//環境處理
procedure DllEntry(dwResaon: DWORD);
begin
  case dwResaon of
    DLL_PROCESS_ATTACH: InitHook;   //DLL載入
    DLL_PROCESS_DETACH: UninitHook; //DLL刪除
  end;
end;

............

begin
  MemShared;

  { 分配DLL程序到 DllProc 變量 }   DllProc := @DllEntry;   { 調用DLL加載處理 }   DllEntry(DLL_PROCESS_ATTACH); end.        能夠看到,在DLL裝入內存的時候其實就已經調用了InitHook,將要攔截的API攔截了 。這時候看看任務管理器能不能關閉咱們的程序,試一下就知道還能夠,由於咱們尚未調用 StartHook來傳入咱們程序的PID,因此還能夠被關閉。       到此這篇文章就結束了, 本人從小語文沒及過格(^_^),文章寫的不太好,不過源代碼都貼上了, 有詳細的註釋,相信你們也能看明白。若是你發現有什麼錯誤的地方,要記得告訴我哦!     最後感謝 cxwr(菜新)大大的支持,能完成這篇文章少不了他的功勞。

相關文章
相關標籤/搜索