源自:http://blog.csdn.net/yiyefangzhou24/article/details/7523449
Inline API HOOK方法有很多,常見的一種方法無需內嵌彙編語句,原理簡單易懂,針對64位微軟操作系統不允許內嵌彙編這種無解的事比較給力(列寧大神提供的消息,本人未親自嘗試)。下面簡單說一下這種方法的原理。
首先說一說inline HOOK的原理,具體的資料上說的很多了,我只是想提一下核心的技術實現,主要是修改映射到內存的api函數的執行代碼的第一條指令(一般是第一條指令)成jmp指令,使其跳轉到我們的函數,執行完畢之後跳轉回去繼續執行。
首先我們需要知道一個函數比如MessageBox的首條指令的內容是什麼,佔幾個字節。這我們可以使用ollydbg進行反彙編得到,具體方法參考ollydbg使用手冊。我們這裏作爲例子的是CreateProcessInternalW這個函數,這個函數是由kernel32導出的一個內部函數,我們可以使用ShellExecute來調出這個函數,我們隨便寫一個程序,調用ShellExecute,反彙編跟蹤到CreateProcessInternalW函數(這裏涉及反彙編入門知識,隨便找找資料就有),就不難發現這個函數第一條指令是push指令,如圖:
總共5字節,那我們就可以將他修改成jmp指令指向我們的函數,具體代碼如下,這種方法就不多做贅述了:
- #include <windows.h>
- #include <stdio.h>
- #include <iostream.h>
- #include <tchar.h>
- //修改API入口爲 mov eax, 00400000;jmp eax是程序能跳轉到自己的函數
- BYTE NewBytes[5] = {0xe9,0x0,0x0,0x0,0x0};
- BYTE OldBytes[5] = {0};
- FARPROC CreateProcessInternalW_Addr;
- typedef BOOL (_stdcall *PFNCreateProcessInternalW)
- (
- HANDLE hToken,
- LPCTSTR lpApplicationName,
- LPTSTR lpCommandLine,
- LPSECURITY_ATTRIBUTES lpProcessAttributes,
- LPSECURITY_ATTRIBUTES lpThreadAttributes,
- BOOL bInheritHandles,
- DWORD dwCreationFlags,
- LPVOID lpEnvironment,
- LPCTSTR lpCurrentDirectory,
- LPSTARTUPINFO lpStartupInfo,
- LPPROCESS_INFORMATION lpProcessInformation ,
- PHANDLE hNewToken
- );
- PFNCreateProcessInternalW RealCreateProcessInternalW;
- BOOL WINAPI MyCreateProcessInternalW(
- HANDLE hToken,
- LPCTSTR lpApplicationName,
- LPTSTR lpCommandLine,
- LPSECURITY_ATTRIBUTES lpProcessAttributes,
- LPSECURITY_ATTRIBUTES lpThreadAttributes,
- BOOL bInheritHandles,
- DWORD dwCreationFlags,
- LPVOID lpEnvironment,
- LPCTSTR lpCurrentDirectory,
- LPSTARTUPINFO lpStartupInfo,
- LPPROCESS_INFORMATION lpProcessInformation ,
- PHANDLE hNewToken
- )
- {
- MessageBox(NULL,"MyCreateProcessInternalW","",MB_OK);
- // 恢復API頭8個字節
- if(!WriteProcessMemory( INVALID_HANDLE_VALUE, (void*)RealCreateProcessInternalW,(void*)OldBytes,5, NULL))
- printf("write error!\n");
- //調用正確的函數
- FlushInstructionCache(GetCurrentProcess(),(void*)RealCreateProcessInternalW,5);
- BOOL Flag=(BOOL)(PFNCreateProcessInternalW)RealCreateProcessInternalW(hToken,lpApplicationName,
- lpCommandLine,lpProcessAttributes,
- lpThreadAttributes,bInheritHandles,
- dwCreationFlags,lpEnvironment,
- lpCurrentDirectory,lpStartupInfo,
- lpProcessInformation,hNewToken);
- printf("OK!\n");
- // 寫入跳轉語句,繼續Hook
- WriteProcessMemory( INVALID_HANDLE_VALUE, (void *)RealCreateProcessInternalW,(void*)NewBytes,5, NULL);
- return TRUE;
- }
- void main()
- {
- //讀CreateFile函數的前8個字節
- HMODULE hHand= LoadLibrary("Kernel32.dll");
- RealCreateProcessInternalW=(PFNCreateProcessInternalW)GetProcAddress(hHand,"CreateProcessInternalW");
- if(ReadProcessMemory(INVALID_HANDLE_VALUE,(void *)RealCreateProcessInternalW,OldBytes,5,NULL)==0)
- {
- printf("ReadProcessMemory error\n");
- return;
- }
- printf("OldBytes is %x%x%x%x%x\n",OldBytes[0],OldBytes[1],OldBytes[2],
- OldBytes[3],OldBytes[4]);
- //將NewBytes改成My函數地址
- printf("%x\n",MyCreateProcessInternalW);
- printf("%x\n",RealCreateProcessInternalW);
- *(DWORD*)(NewBytes + 1) = (DWORD)MyCreateProcessInternalW-(DWORD)RealCreateProcessInternalW-5;
- printf("NewBytes is %x%x%x%x%x\n",NewBytes[0],NewBytes[1],NewBytes[2],NewBytes[3],
- NewBytes[4]);
- //寫入跳轉,開始Hook
- WriteProcessMemory(INVALID_HANDLE_VALUE,RealCreateProcessInternalW,NewBytes,5,NULL);
- ShellExecute(NULL,"open","c:\\Help.txt","","",SW_SHOW);
- }
上面這種方法有個明顯的缺陷,不知道各位看出來沒有?對,就是當有多個線程執行了我們inline Hook的目標函數的時候,一個線程在使用修改之前的RealCreateProcessInternalW,一個線程在使用修改之後的RealCreateProcessInternalW,就會導致修改之前的RealCreateProcessInternalW函數不能夠實現掛鉤的效果。當然也有解決方法,爲這個函數加鎖,只能夠一個線程使用!這種方法當然也有弊端,當要使用這個函數的線程較多時就會出現較多的等待信號量的情況,程序效率變低。
下面我們講解另一種inline API Hook的方法,這種方法支持多線程操作。
我們很自然的想到,修改了RealCreateProcessInternalW的前5個字節爲jmp XXXX到我們的自己的MyCreateProcessInternalW後在執行我們的MyCreateProcessInternalW的時候執行我們的目標指令之後再執行RealCreateProcessInternalW的前5個字節的指令在跳回RealCreateProcessInternalW+5的位置進行執行,不就能夠實現真實的RealCreateProcessInternalW的效果了嗎?
於是我們就這樣寫我們的MyCreateProcessInternalW:
- BOOL WINAPI MyCreateProcessInternalW(
- HANDLE hToken,
- LPCTSTR lpApplicationName,
- LPTSTR lpCommandLine,
- LPSECURITY_ATTRIBUTES lpProcessAttributes,
- LPSECURITY_ATTRIBUTES lpThreadAttributes,
- BOOL bInheritHandles,
- DWORD dwCreationFlags,
- LPVOID lpEnvironment,
- LPCTSTR lpCurrentDirectory,
- LPSTARTUPINFO lpStartupInfo,
- LPPROCESS_INFORMATION lpProcessInformation ,
- PHANDLE hNewToken
- )
- {
- MessageBox(NULL,"MyCreateProcessInternalW","",MB_OK);
- _asm
- {
- push a
- mov eax,RealCreateProcessInternalW
- add eax,5
- jmp eax
- }
- return TRUE;
- }
運行程序,發現程序崩潰了!爲什麼呢???此處樓主也是辛苦的找了很久,最後“列寧”同志一語點醒了我,棧平衡!!!
我們先來回顧一下我們的過程:
發現問題了沒有?沒有發現問題沒關係,我們將問題程序進行反彙編,進入MyCreateProcessInternalW,如圖:
我們發現原來RealCreateProcessInternalW的棧信息在jmp到MyCreateProcessInternalW的時候棧數據被破壞了,因爲MyCreateProcessInternalW也push了好多數據進去。這就是我們程序崩潰的原因!怎麼解決?好辦在jmp之前pop所有MyCreateProcessInternalW的push來平衡棧信息。問題來了,RealCreateProcessInternalW的第一條指令如何執行?我們來回顧一下RealCreateProcessInternalW的棧信息是如何初始化的,首先push ebp,再從右至左(_stdcall方式)push所有參數在執行第一條指令push XXXX,push XXX被替換成了jmp指令,所以沒有執行,那好辦了我們在MyCreateProcessInternalW函數中平衡完棧之後直接push XXX就可以了,仔細想想?前提是我們之前要保存push XXXX的XXX值,如上面的程序中的OldByte中。
所以程序就出來了,這裏只貼上我們的MyCreateProcessInternalW函數代碼,其他的同上:
- BOOL WINAPI MyCreateProcessInternalW(
- HANDLE hToken,
- LPCTSTR lpApplicationName,
- LPTSTR lpCommandLine,
- LPSECURITY_ATTRIBUTES lpProcessAttributes,
- LPSECURITY_ATTRIBUTES lpThreadAttributes,
- BOOL bInheritHandles,
- DWORD dwCreationFlags,
- LPVOID lpEnvironment,
- LPCTSTR lpCurrentDirectory,
- LPSTARTUPINFO lpStartupInfo,
- LPPROCESS_INFORMATION lpProcessInformation ,
- PHANDLE hNewToken
- )
- {
- MessageBox(NULL,"MyCreateProcessInternalW","",MB_OK);
- _asm
- {
- mov eax,RealCreateProcessInternalW
- add eax,5
- pop edi
- pop esi
- pop ebx
- pop ebp
- push a
- jmp eax
- }
- return TRUE;
- }
正因爲菜鳥的思維侷限性,侷限性不可避免,換一個函數,換一個編譯器就不行了,但是如果我們要求方法儘量簡單,代碼儘量短小精悍的話這不失爲一種好方法,另外如果有人關注通用的方法,參看“列寧”同志的博客:
最後菜鳥言論,僅供娛樂。