zoukankan      html  css  js  c++  java
  • 实现拦截API的钩子(Hook)

    道理不多讲,简单说就是将系统API的跳转地址,替换为我们自己写的API的地址,所以要求我们自定义的API函数要和被拦截的API有相同的参数。在用完后,记得恢复。

    因为要挂全局的钩子,所以Hook的部分,做成DLL。   源码下载

    Hook.DLL主工程文件代码

    [delphi] view plaincopy
     
    1. library Hook;  
    2.   
    3. uses  
    4.   SysUtils,  
    5.   Windows,  
    6.   Classes,  
    7.   ApiDefine in 'ApiDefine.pas',  
    8.   APIHook in 'APIHook.pas';  
    9.   
    10. {$R *.res}  
    11. var  
    12.   HookHandle: HHook;  
    13.   
    14. function HookProc(code:Integer;wparam:WPARAM;lparam:LPARAM):LRESULT;stdcall;  
    15. begin  
    16.   Result := CallNextHookEx(HookHandle,code,wparam,lparam);  
    17. end;  
    18.   
    19. procedure SetHook;stdcall;  
    20. begin  
    21.   HookHandle := SetWindowsHookEx(WH_GETMESSAGE,@HookProc,HInstance,0);  
    22. end;  
    23.   
    24. procedure StopHook;stdcall;  
    25. begin  
    26.   UnhookWindowsHookEx(HookHandle);  
    27. end;  
    28.   
    29. exports  
    30.   SetHook name 'SetHook',  
    31.   StopHook name 'StopHook';  
    32.   
    33. {已启动就挂上,修改API函数指向}  
    34. begin  
    35.   API_Hook;  
    36. end.  

    APIHook单元,这个单元实现对API地址的替换

    [c-sharp] view plaincopy
     
    1. unit APIHook;  
    2.   
    3. interface  
    4.   
    5. uses  
    6.   Windows, SysUtils, Classes;  
    7.   
    8. type  
    9.   //引入表入口数据结构  
    10.   Image_Import_Entry = packed record  
    11.     OriginalFirstThunk:DWORD;  
    12.     TimeDateStamp:DWORD;  
    13.     ForwarderChain:DWORD;  
    14.     Name:DWORD;  
    15.     FirstThunk:DWORD;  
    16.   end;  
    17.   PImage_Import_Entry = ^Image_Import_Entry;  
    18.   TImportCode = packed record  
    19.     JmpCode: Word;  
    20.     AddressOfPFun: PPointer;  
    21.   end;  
    22.   PImportCode = ^TImportCode;  
    23.   
    24.   function GetFunTrueAddress(Code:Pointer):Pointer;  
    25.   function ReplaceFunAddress(oldfun:Pointer;newfun:Pointer):Integer;  
    26.   
    27. implementation  
    28.   
    29. //获得实际地址  
    30. function GetFunTrueAddress(Code: Pointer): Pointer;  
    31. var  
    32.    func: PImportCode;  
    33. begin  
    34.    Result := Code;  
    35.    if Code = nil then exit;  
    36.    try  
    37.       func := code;  
    38.       if (func.JmpCode = $25FF) then  
    39.       begin  
    40.          Result := func.AddressOfPFun^;  
    41.       end;  
    42.    except  
    43.       Result := nil;  
    44.    end;  
    45. end;  
    46.   
    47. //替换地址  
    48. function ReplaceFunAddress(oldfun:Pointer;newfun:Pointer): Integer;  
    49. var  
    50.    IsDone: TList;  
    51.    function ReplaceAddressInModule(hModule: THandle; OldFunc, NewFunc: Pointer): Integer;  
    52.    var  
    53.       DosHeader: PImageDosHeader;  
    54.       NTHeader: PImageNTHeaders;  
    55.       ImportDesc: PImage_Import_Entry;  
    56.       RVA: DWORD;  
    57.       Func: ^Pointer;  
    58.       DLL: string;  
    59.       f: Pointer;  
    60.       written: DWORD;  
    61.    begin  
    62.       Result := 0;  
    63.       DosHeader := Pointer(hModule);  
    64.       //已经找过,则退出  
    65.       if IsDone.IndexOf(DosHeader) >= 0 then exit;  
    66.       IsDone.Add(DosHeader);  
    67.   
    68.       oldfun := GetFunTrueAddress(OldFunc);  
    69.   
    70.       if IsBadReadPtr(DosHeader, SizeOf(TImageDosHeader)) then exit;  
    71.       if DosHeader.e_magic <> IMAGE_DOS_SIGNATURE then exit;  
    72.       NTHeader := Pointer(Integer(DosHeader) + DosHeader._lfanew);  
    73.       //引入表的虚拟地址  
    74.       RVA := NTHeader^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;  
    75.   
    76.       if RVA = 0 then exit;  
    77.       ImportDesc := pointer(integer(DosHeader) + RVA);  
    78.       while (ImportDesc^.Name <> 0) do  
    79.       begin  
    80.         //引入文件名  
    81.          DLL := PChar(Integer(DosHeader) + ImportDesc^.Name);  
    82.          //获得该DLL的句柄,然后递归查找  
    83.          ReplaceAddressInModule(GetModuleHandle(PChar(DLL)), oldfun, newfun);  
    84.          //引入函数入口  
    85.          Func := Pointer(Integer(DOSHeader) + ImportDesc.FirstThunk);  
    86.          //如果函数指针不为空  
    87.          while Func^ <> nil do  
    88.          begin  
    89.            //取得真是地址  
    90.             f := GetFunTrueAddress(Func^);  
    91.             //如果和我们要拦截的Api函数地址一样  
    92.             if f = oldfun then  
    93.             begin  
    94.               //替换成我们自己的Api地址  
    95.                WriteProcessMemory(GetCurrentProcess, Func, @NewFunc, 4, written);  
    96.                if Written > 0 then Inc(Result);  
    97.             end;  
    98.             //继续找  
    99.             Inc(Func);  
    100.          end;  
    101.          Inc(ImportDesc);  
    102.       end;  
    103.    end;  
    104.   
    105. begin  
    106.    IsDone := TList.Create;  
    107.    try  
    108.      //GetModuleHandle,参数nil,为获取自身的模块句柄  
    109.       Result := ReplaceAddressInModule(GetModuleHandle(nil), oldfun, newfun);  
    110.    finally  
    111.       IsDone.Free;  
    112.    end;  
    113. end;  
    114. end.  

    ApiDefine单元,这里实现我们自定义的API

    [delphi] view plaincopy
     
    1. unit ApiDefine;  
    2.   
    3. interface  
    4.   
    5. uses  
    6.   Windows, SysUtils, Classes,Messages,APIHook,ShellAPI;  
    7.   
    8.   procedure API_Hook;  
    9.   procedure API_UnHook;  
    10.   
    11. implementation  
    12.   
    13. //自定义Api的类型  
    14. type  
    15.   TMsgA = function(hwn: hwnd; lptext: pchar; lpcapion: pchar; utype: cardinal):integer; stdcall;  
    16.   TShellExc = function(hwn: HWND;lpoperate: PChar;lpfilename: PChar; lpparam: PChar; lpdir:PChar;cmd:Integer):Integer;stdcall;  
    17.   TTextOut = function(DC:HDC;X:Integer;Y:Integer;options:Integer;rect:PRect;str:PAnsiChar;count:Integer;dx:PInteger):Boolean;stdcall;  
    18. var  
    19.   oldMsgA : TMsgA;  
    20.   oldShellExc : TShellExc;  
    21.   oldTextOut : TTextOut;  
    22.   
    23. //自定义Api的实现  
    24. function NewMsgA(hwn: hwnd; lptext: pchar; lpcaption: pchar; utype: cardinal):integer; stdcall;  
    25. begin  
    26.   Result := oldMsgA(hwn,'成功拦截MessageBoxA','哈哈',utype);  
    27. end;    
    28.   
    29. function NewShellExc(hwn: HWND;lpoperate: PChar;lpfilename: PChar; lpparam: PChar; lpdir:PChar;cmd:Integer):Integer;stdcall;  
    30. begin  
    31.   Result := oldShellExc(hwn,lpoperate,'c:/2.txt',lpfilename,lpdir,cmd);  
    32. end;  
    33.   
    34. {TextOut调用的是ExtTextOut}  
    35. function NewTextOut(DC:HDC;X:Integer;Y:Integer;options:Integer;rect:PRect;str:PAnsiChar;count:Integer;dx:PInteger):Boolean;stdcall;  
    36. begin  
    37.   {这个rect也是可以修改的,以便容纳更多的字符显示}  
    38.   Result := oldTextOut(DC,50,50,options,rect,'中国',count,dx);  
    39. end;  
    40.   
    41. procedure API_Hook;  
    42. begin  
    43.   if @oldMsgA = nil then  
    44.     @oldMsgA := GetFunTrueAddress(@MessageBoxA);  
    45.   if @oldShellExc = nil then  
    46.     @oldShellExc := GetFunTrueAddress(@ShellExecute);  
    47.   if @oldTextOut = nil then  
    48.     @oldTextOut := GetFunTrueAddress(@ExtTextOut);  
    49.   //替换    
    50.   ReplaceFunAddress(@oldMsgA,@NewMsgA);  
    51.   ReplaceFunAddress(@oldShellExc,@NewShellExc);  
    52.   ReplaceFunAddress(@oldTextOut,@NewTextOut);  
    53. end;  
    54.   
    55. procedure API_UnHook;  
    56. begin  
    57.   if @oldMsgA <> nil then  
    58.     ReplaceFunAddress(@NewMsgA,@oldMsgA);  
    59.   if @oldShellExc <> nil then  
    60.     ReplaceFunAddress(@NewShellExc,@oldShellExc);  
    61.   if @oldTextOut <> nil then  
    62.     ReplaceFunAddress(@NewTextOut,@oldTextOut);  
    63. end;  
    64.   
    65. initialization  
    66. //结束时恢复原Api地址  
    67. finalization  
    68.   API_UnHook;  
    69.   
    70. end.  

    主程序代码,大家可以把,消息、打开文件、画文字的代码写到另外的程序,本程序只负责挂钩和摘钩,那样可以看到系统钩子的效果。

    [delphi] view plaincopy
     
    1. unit TestMain;  
    2.   
    3. interface  
    4.   
    5. uses  
    6.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,  
    7.   Dialogs, StdCtrls,ShellAPI;  
    8.   
    9. type  
    10.   TForm1 = class(TForm)  
    11.     btn_Hook: TButton;  
    12.     btn_Msg: TButton;  
    13.     btn_UnHook: TButton;  
    14.     btn_OpenFiel: TButton;  
    15.     btn_TextOut: TButton;  
    16.     procedure btn_HookClick(Sender: TObject);  
    17.     procedure btn_MsgClick(Sender: TObject);  
    18.     procedure btn_UnHookClick(Sender: TObject);  
    19.     procedure btn_OpenFielClick(Sender: TObject);  
    20.     procedure btn_TextOutClick(Sender: TObject);  
    21.   private  
    22.     { Private declarations }  
    23.   public  
    24.     { Public declarations }  
    25.   end;  
    26.   
    27. var  
    28.   Form1: TForm1;  
    29.   
    30. implementation  
    31.   
    32. procedure SetHook;stdcall;external 'Hook.dll';  
    33. procedure StopHook;stdcall;external 'Hook.dll';  
    34.   
    35. {$R *.dfm}  
    36.   
    37. procedure TForm1.btn_HookClick(Sender: TObject);  
    38. begin  
    39.   SetHook;  
    40. end;  
    41. procedure TForm1.btn_UnHookClick(Sender: TObject);  
    42. begin  
    43.   StopHook;  
    44. end;  
    45.   
    46. {被拦截后,执行我们自己的NewMsgA方法}  
    47. procedure TForm1.btn_MsgClick(Sender: TObject);  
    48. begin  
    49.   MessageBoxA(Handle,'能拦住我吗','询问',MB_OK);  
    50. end;  
    51.   
    52. {本想打开c:/1.txt,被拦截后,打开c:/2.txt}  
    53. procedure TForm1.btn_OpenFielClick(Sender: TObject);  
    54. begin  
    55.   ShellExecute(Handle,'open','c:/1.txt',nil,nil,SW_NORMAL);  
    56. end;  
    57. {本想在0,0出画出'Hello',被拦截后,在50,50的位置画出'中国'}  
    58. procedure TForm1.btn_TextOutClick(Sender: TObject);  
    59. begin   
    60.   Self.Canvas.TextOut(0,0,'Hello');  
    61. end;  
    62.   
    63. end.  

    下图是执行画文字代码后的效果,本想在[0,0]坐标画出'Hello',被拦截后,在[50,50]的位置画出'中国'

    http://blog.csdn.net/bdmh/article/details/6104475

  • 相关阅读:
    3.7日复习
    作业
    小米官网需要的数据
    EL&JSTL
    变量和运算符
    jsp
    session的应用----验证码
    restframework-总结
    Django-Rest-Framework部分源码流程分析
    Django-Rest-Framework
  • 原文地址:https://www.cnblogs.com/findumars/p/5001977.html
Copyright © 2011-2022 走看看