小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

淺談api hook技術(shù)

 frie 2005-08-09
 

        APIHook一直是使大家感興趣的話題。屏幕取詞,內(nèi)碼轉(zhuǎn)化,屏幕翻譯,中文平臺等等都涉及到了此項技術(shù)。有很多文章涉及到了這項技術(shù),但都閃爍其詞不肯明明白白的公布。我僅在這里公布以下我用Delphi制作APIHook的一些心得。
       通常的APIHOOK有這樣幾種方法:
      1、自己寫一個動態(tài)鏈接庫,里面定義自己寫的想取代系統(tǒng)的API。把這個動態(tài)鏈接庫映射到2G以上的系統(tǒng)動態(tài)鏈接庫所在空間,把系統(tǒng)動態(tài)鏈接庫中的該API的指向修改指向自己的函數(shù)。這種方法的好處就是可以取代系統(tǒng)中運行全部程序的該API。但他有個局限,就是只適用于Win9x。(原因是NT中動態(tài)鏈接庫不是共享的,每個進程都有自己的一份動態(tài)鏈接庫在內(nèi)存中的映射)
      2、自己寫一個動態(tài)鏈接庫,里面定義自己寫得象替代系統(tǒng)的API。把這個動態(tài)鏈接庫映射到進程的空間里。將該進程對API的調(diào)用指向自己寫的動態(tài)鏈接庫。這種方法的好處是可以選擇性的替代哪個進程的API。而且適用于所有的Windows操作系統(tǒng)。
      這里我選用的是第二種方法。
      第二種方法需要先了解一點PE文件格式的知識。
       首先是一個實模式的的DOS文件頭,是為了保持和DOS的兼容。
       接著是一個DOS的代理模塊。你在純DOS先運行Win32的可執(zhí)行文件,看看是不是也執(zhí)行了,只是顯示的的是一行信息大意是說該Windows程序不能在DOS實模式下運行。
       然后才是真正意義上的Windows可執(zhí)行文件的文件頭。它的具體位置不是每次都固定的。是由文件偏移$3C決定的。我們要用到的就是它。
       如果我們在程序中調(diào)用了一個MessageBoxA函數(shù)那么它的實現(xiàn)過程是這樣的。他先調(diào)用在本進程中的MessageBoxA函數(shù)然后才跳到動態(tài)鏈接庫的MessageBoxA的入口點。即:
       call messageBoxA(0040106c)
       jmp dword ptr [_jmp_MessageBoxA@16(00425294)]
其中00425294的內(nèi)容存儲的就是就是MessageBoxA函數(shù)的入口地址。如果我們做一下手腳,那么......
      那就開始吧!
我們需要定義兩個結(jié)構(gòu)
type
   PImage_Import_Entry = ^Image_Import_Entry;
   Image_Import_Entry = record
      Characteristics: DWORD;
      TimeDateStamp: DWORD;
      MajorVersion: Word;
      MinorVersion: Word;
      Name: DWORD;
      LookupTable: DWORD;
   end;
type
   TImportCode = packed record
      JumpInstruction: Word; file: //定義跳轉(zhuǎn)指令jmp
      AddressOfPointerToFunction: ^Pointer; file: //定義要跳轉(zhuǎn)到的函數(shù)
   end;
   PImportCode = ^TImportCode;
然后是確定函數(shù)的地址。
function LocateFunctionAddress(Code: Pointer): Pointer;
var
   func: PImportCode;
begin
   Result := Code;
   if Code = nil then exit;
   try
      func := code;
      if (func.JumpInstruction = $25FF) then
      begin
         Result := func.AddressOfPointerToFunction^;
      end;
   except
      Result := nil;
   end;
end;
參數(shù)Code是函數(shù)在進程中的指針,即那條Jmp XXX的指令。$25FF就是跳轉(zhuǎn)指令的機器碼。
再下一篇我會講如何替換下那個XXX的內(nèi)容,讓他跳到你想去的地方。

在這里我將要實現(xiàn)轉(zhuǎn)跳。有人說修改內(nèi)存內(nèi)容要進入Ring 0 才可以??墒荳indows本身提供了一個寫內(nèi)存的指令WriteProcessMemory。有了這把利器,我們幾乎無所不能。如游戲的修改等在這里我們只談APIHOOK。
function RepointFunction(OldFunc, NewFunc: Pointer): Integer;
var
   IsDone: TList;
   function RepointAddrInModule(hModule: THandle; OldFunc, NewFunc: Pointer): Integer;
   var
      Dos: PImageDosHeader;
      NT: PImageNTHeaders;
      ImportDesc: PImage_Import_Entry;
      RVA: DWORD;
      Func: ^Pointer;
      DLL: string;
      f: Pointer;
      written: DWORD;
   begin
      Result := 0;
      Dos := Pointer(hModule);
      if IsDone.IndexOf(Dos) >= 0 then exit;
      IsDone.Add(Dos);

      OldFunc := LocateFunctionAddress(OldFunc);

      if IsBadReadPtr(Dos, SizeOf(TImageDosHeader)) then exit;
      if Dos.e_magic <> IMAGE_DOS_SIGNATURE then exit;
      NT := Pointer(Integer(Dos) + dos._lfanew);

      RVA := NT^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
         .VirtualAddress;

      if RVA = 0 then exit;
      ImportDesc := pointer(integer(Dos) + RVA);
      while (ImportDesc^.Name <> 0) do
      begin
         DLL := PChar(Integer(Dos) + ImportDesc^.Name);
         RepointAddrInModule(GetModuleHandle(PChar(DLL)), OldFunc, NewFunc);
         Func := Pointer(Integer(DOS) + ImportDesc.LookupTable);
         while Func^ <> nil do
         begin
            f := LocateFunctionAddress(Func^);
            if f = OldFunc then
            begin
               WriteProcessMemory(GetCurrentProcess, Func, @NewFunc, 4, written);
               if Written > 0 then Inc(Result);
            end;
            Inc(Func);
         end;
         Inc(ImportDesc);
      end;
   end;

begin
   IsDone := TList.Create;
   try
      Result := RepointAddrInModule(GetModuleHandle(nil), OldFunc, NewFunc);
   finally
      IsDone.Free;
   end;
end;
有了這兩個函數(shù)我們幾乎可以更改任何API函數(shù)。
我們可以先寫一個DLL文件。我這里以修改Text相關(guān)函數(shù)為例:
先定義幾個函數(shù):
type
   TTextOutA = function(DC: HDC; X, Y: Integer; Str: PAnsiChar; Count: Integer): BOOL; stdcall;
   TTextOutW = function(DC: HDC; X, Y: Integer; Str: PWideChar; Count: Integer): BOOL; stdcall;
   TTextOut = function(DC: HDC; X, Y: Integer; Str: PChar; Count: Integer): BOOL; stdcall;
   TDrawTextA = function(hDC: HDC; lpString: PAnsiChar; nCount: Integer; var lpRect: TRect; uFormat: UINT): Integer; stdcall;
   TDrawTextW = function(hDC: HDC; lpString: PWideChar; nCount: Integer; var lpRect: TRect; uFormat: UINT): Integer; stdcall;
   TDrawText = function(hDC: HDC; lpString: PChar; nCount: Integer; var lpRect: TRect; uFormat: UINT): Integer; stdcall;
var
   OldTextOutA: TTextOutA;
   OldTextOutW: TTextOutW;
   OldTextOut: TTextOut;
   OldDrawTextA: TDrawTextA;
   OldDrawTextW: TDrawTextW;
   OldDrawText: TDrawText;
......
function MyTextOutA(DC: HDC; X, Y: Integer; Str: PAnsiChar; Count: Integer): BOOL; stdcall;
begin
   OldTextOutA(DC, X, Y, ‘ABC‘, length(‘ABC‘));
end;

function MyTextOutW(DC: HDC; X, Y: Integer; Str: PWideChar; Count: Integer): BOOL; stdcall;
begin
   OldTextOutW(DC, X, Y, ‘ABC‘, length(‘ABC‘));
end;

function MyTextOut(DC: HDC; X, Y: Integer; Str: PChar; Count: Integer): BOOL; stdcall;
begin
   OldTextOut(DC, X, Y, ‘ABC‘, length(‘ABC‘));
end;

function MyDrawTextA(hDC: HDC; lpString: PAnsiChar; nCount: Integer; var lpRect: TRect; uFormat: UINT): Integer; stdcall;
begin
   OldDrawTextA(hDC, ‘ABC‘, length(‘ABC‘), lpRect, uFormat);
end;

function MyDrawTextW(hDC: HDC; lpString: PWideChar; nCount: Integer; var lpRect: TRect; uFormat: UINT): Integer; stdcall;
begin
   OldDrawTextW(hDC, ‘ABC‘, length(‘ABC‘), lpRect, uFormat);
end;

function MyDrawText(hDC: HDC; lpString: PChar; nCount: Integer; var lpRect: TRect; uFormat: UINT): Integer; stdcall;
begin
   OldDrawText(hDC, ‘ABC‘, length(‘ABC‘), lpRect, uFormat);
end;

調(diào)用時我們要把原來的函數(shù)地址保存下來:
   if @OldTextOutA = nil then
      @OldTextOutA := LocateFunctionAddress(@TextOutA);
   if @OldTextOutW = nil then
      @OldTextOutW := LocateFunctionAddress(@TextOutW);
   if @OldTextOut = nil then
      @OldTextOut := LocateFunctionAddress(@TextOut);
   if @OldDrawTextA = nil then
      @OldDrawTextA := LocateFunctionAddress(@DrawTextA);
   if @OldDrawTextW = nil then
      @OldDrawTextW := LocateFunctionAddress(@DrawTextW);
   if @OldDrawText = nil then
      @OldDrawText := LocateFunctionAddress(@DrawText);
然后很順其自然的用自己的函數(shù)替換掉原來的函數(shù)
   RepointFunction(@OldTextOutA, @MyTextOutA);
   RepointFunction(@OldTextOutW, @MyTextOutW);
   RepointFunction(@OldTextOut, @MyTextOut);
   RepointFunction(@OldDrawTextA, @MyDrawTextA);
   RepointFunction(@OldDrawTextW, @MyDrawTextW);
   RepointFunction(@OldDrawText, @MyDrawText);
        在結(jié)束時不要忘記恢復原來函數(shù)的入口,要不然你會死得很難看喲!好了我們在寫一個Demo程序。你會說怎么文字沒有變成ABC呀?是呀,你要刷新一下才行。最小化然后在最大化。看看變了沒有。  
        要不然你就寫代碼刷新一下好了。至于去攔截其他進程的API那就用SetWindowsHookEx寫一個其他的鉤子將DLL映射進去就行了,我就不再浪費口水了。
掌握了該方法你幾乎無所不能。你可以修改其它程序。你可以攔截Createwindow等窗口函數(shù)改變其他程序的窗口形狀、你還可以入侵其它的程序,你還可以......嘿嘿。干了壞事別招出我來就行了。
我還寫了個例子,請在CSDN上下載。

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多