針對函數的多線程inline API HOOK

源自: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指令指向我們的函數,具體代碼如下,這種方法就不多做贅述了:

  1. #include <windows.h>     
  2. #include <stdio.h>     
  3. #include <iostream.h>     
  4. #include <tchar.h>     
  5.     
  6. //修改API入口爲 mov eax, 00400000;jmp eax是程序能跳轉到自己的函數     
  7. BYTE NewBytes[5] = {0xe9,0x0,0x0,0x0,0x0};    
  8. BYTE OldBytes[5] = {0};  
  9.     
  10. FARPROC CreateProcessInternalW_Addr;    
  11. typedef BOOL (_stdcall *PFNCreateProcessInternalW)    
  12. (    
  13.   HANDLE hToken,    
  14.   LPCTSTR lpApplicationName,     
  15.   LPTSTR lpCommandLine,     
  16.   LPSECURITY_ATTRIBUTES lpProcessAttributes,    
  17.   LPSECURITY_ATTRIBUTES lpThreadAttributes,     
  18.   BOOL bInheritHandles,     
  19.   DWORD dwCreationFlags,    
  20.   LPVOID lpEnvironment,     
  21.   LPCTSTR lpCurrentDirectory,     
  22.   LPSTARTUPINFO lpStartupInfo,     
  23.   LPPROCESS_INFORMATION lpProcessInformation ,    
  24. PHANDLE hNewToken    
  25. );    
  26.   
  27.   
  28. PFNCreateProcessInternalW RealCreateProcessInternalW;  
  29.     
  30. BOOL WINAPI MyCreateProcessInternalW(    
  31.   HANDLE hToken,    
  32.   LPCTSTR lpApplicationName,     
  33.   LPTSTR lpCommandLine,     
  34.   LPSECURITY_ATTRIBUTES lpProcessAttributes,    
  35.   LPSECURITY_ATTRIBUTES lpThreadAttributes,     
  36.   BOOL bInheritHandles,     
  37.   DWORD dwCreationFlags,    
  38.   LPVOID lpEnvironment,     
  39.   LPCTSTR lpCurrentDirectory,     
  40.   LPSTARTUPINFO lpStartupInfo,     
  41.   LPPROCESS_INFORMATION lpProcessInformation ,    
  42. PHANDLE hNewToken    
  43. )  
  44. {    
  45.     MessageBox(NULL,"MyCreateProcessInternalW","",MB_OK);    
  46. //    恢復API頭8個字節     
  47.     if(!WriteProcessMemory( INVALID_HANDLE_VALUE, (void*)RealCreateProcessInternalW,(void*)OldBytes,5, NULL))  
  48.         printf("write error!\n");   
  49.    //調用正確的函數     
  50.     FlushInstructionCache(GetCurrentProcess(),(void*)RealCreateProcessInternalW,5);  
  51.     BOOL Flag=(BOOL)(PFNCreateProcessInternalW)RealCreateProcessInternalW(hToken,lpApplicationName,  
  52.         lpCommandLine,lpProcessAttributes,  
  53.         lpThreadAttributes,bInheritHandles,  
  54.         dwCreationFlags,lpEnvironment,  
  55.         lpCurrentDirectory,lpStartupInfo,  
  56.         lpProcessInformation,hNewToken);  
  57.     printf("OK!\n");  
  58. //    寫入跳轉語句,繼續Hook     
  59.     WriteProcessMemory( INVALID_HANDLE_VALUE, (void *)RealCreateProcessInternalW,(void*)NewBytes,5, NULL);   
  60.     return TRUE;  
  61. }    
  62.    
  63.     
  64. void main()    
  65. {    
  66.     //讀CreateFile函數的前8個字節    
  67.     HMODULE hHand= LoadLibrary("Kernel32.dll");  
  68.     RealCreateProcessInternalW=(PFNCreateProcessInternalW)GetProcAddress(hHand,"CreateProcessInternalW");  
  69.     if(ReadProcessMemory(INVALID_HANDLE_VALUE,(void *)RealCreateProcessInternalW,OldBytes,5,NULL)==0)    
  70.     {    
  71.         printf("ReadProcessMemory error\n");    
  72.         return;    
  73.     }    
  74.         
  75.     printf("OldBytes is %x%x%x%x%x\n",OldBytes[0],OldBytes[1],OldBytes[2],    
  76.         OldBytes[3],OldBytes[4]);    
  77.         
  78.     //將NewBytes改成My函數地址    
  79.     printf("%x\n",MyCreateProcessInternalW);  
  80.     printf("%x\n",RealCreateProcessInternalW);  
  81.     *(DWORD*)(NewBytes + 1) = (DWORD)MyCreateProcessInternalW-(DWORD)RealCreateProcessInternalW-5;    
  82.         
  83.     printf("NewBytes is %x%x%x%x%x\n",NewBytes[0],NewBytes[1],NewBytes[2],NewBytes[3],    
  84.         NewBytes[4]);    
  85.         
  86.     //寫入跳轉,開始Hook     
  87.     WriteProcessMemory(INVALID_HANDLE_VALUE,RealCreateProcessInternalW,NewBytes,5,NULL);    
  88.     ShellExecute(NULL,"open","c:\\Help.txt","","",SW_SHOW);  
  89. }   


 

       上面這種方法有個明顯的缺陷,不知道各位看出來沒有?對,就是當有多個線程執行了我們inline Hook的目標函數的時候,一個線程在使用修改之前的RealCreateProcessInternalW,一個線程在使用修改之後的RealCreateProcessInternalW,就會導致修改之前的RealCreateProcessInternalW函數不能夠實現掛鉤的效果。當然也有解決方法,爲這個函數加鎖,只能夠一個線程使用!這種方法當然也有弊端,當要使用這個函數的線程較多時就會出現較多的等待信號量的情況,程序效率變低。

       下面我們講解另一種inline API Hook的方法,這種方法支持多線程操作。

       我們很自然的想到,修改了RealCreateProcessInternalW的前5個字節爲jmp XXXX到我們的自己的MyCreateProcessInternalW後在執行我們的MyCreateProcessInternalW的時候執行我們的目標指令之後再執行RealCreateProcessInternalW的前5個字節的指令在跳回RealCreateProcessInternalW+5的位置進行執行,不就能夠實現真實的RealCreateProcessInternalW的效果了嗎?

 

        於是我們就這樣寫我們的MyCreateProcessInternalW

  1. BOOL WINAPI MyCreateProcessInternalW(    
  2.   HANDLE hToken,    
  3.   LPCTSTR lpApplicationName,     
  4.   LPTSTR lpCommandLine,     
  5.   LPSECURITY_ATTRIBUTES lpProcessAttributes,    
  6.   LPSECURITY_ATTRIBUTES lpThreadAttributes,     
  7.   BOOL bInheritHandles,     
  8.   DWORD dwCreationFlags,    
  9.   LPVOID lpEnvironment,     
  10.   LPCTSTR lpCurrentDirectory,     
  11.   LPSTARTUPINFO lpStartupInfo,     
  12.   LPPROCESS_INFORMATION lpProcessInformation ,    
  13. PHANDLE hNewToken    
  14. )  
  15. {    
  16.     MessageBox(NULL,"MyCreateProcessInternalW","",MB_OK);  
  17.     _asm  
  18.     {  
  19.         push a  
  20.         mov eax,RealCreateProcessInternalW  
  21.         add eax,5  
  22.         jmp eax  
  23.     }  
  24.     return TRUE;  
  25. }  


 

        運行程序,發現程序崩潰了!爲什麼呢???此處樓主也是辛苦的找了很久,最後“列寧”同志一語點醒了我,棧平衡!!!

我們先來回顧一下我們的過程:

發現問題了沒有?沒有發現問題沒關係,我們將問題程序進行反彙編,進入MyCreateProcessInternalW,如圖:

 

 

       我們發現原來RealCreateProcessInternalW的棧信息在jmpMyCreateProcessInternalW的時候棧數據被破壞了,因爲MyCreateProcessInternalWpush了好多數據進去。這就是我們程序崩潰的原因!怎麼解決?好辦在jmp之前pop所有MyCreateProcessInternalWpush來平衡棧信息。問題來了,RealCreateProcessInternalW的第一條指令如何執行?我們來回顧一下RealCreateProcessInternalW的棧信息是如何初始化的,首先push ebp,再從右至左(_stdcall方式)push所有參數在執行第一條指令push XXXXpush XXX被替換成了jmp指令,所以沒有執行,那好辦了我們在MyCreateProcessInternalW函數中平衡完棧之後直接push XXX就可以了,仔細想想?前提是我們之前要保存push XXXXXXX值,如上面的程序中的OldByte中。

       所以程序就出來了,這裏只貼上我們的MyCreateProcessInternalW函數代碼,其他的同上:

  1. BOOL WINAPI MyCreateProcessInternalW(    
  2.   HANDLE hToken,    
  3.   LPCTSTR lpApplicationName,     
  4.   LPTSTR lpCommandLine,     
  5.   LPSECURITY_ATTRIBUTES lpProcessAttributes,    
  6.   LPSECURITY_ATTRIBUTES lpThreadAttributes,     
  7.   BOOL bInheritHandles,     
  8.   DWORD dwCreationFlags,    
  9.   LPVOID lpEnvironment,     
  10.   LPCTSTR lpCurrentDirectory,     
  11.   LPSTARTUPINFO lpStartupInfo,     
  12.   LPPROCESS_INFORMATION lpProcessInformation ,    
  13. PHANDLE hNewToken    
  14. )  
  15. {    
  16.     MessageBox(NULL,"MyCreateProcessInternalW","",MB_OK);  
  17.     _asm  
  18.     {  
  19.         mov eax,RealCreateProcessInternalW  
  20.         add eax,5  
  21.         pop edi                        
  22.         pop esi  
  23.         pop ebx  
  24.         pop ebp  
  25.         push a  
  26.         jmp eax  
  27.     }  
  28.     return TRUE;  
  29. }    


 

       正因爲菜鳥的思維侷限性,侷限性不可避免,換一個函數,換一個編譯器就不行了,但是如果我們要求方法儘量簡單,代碼儘量短小精悍的話這不失爲一種好方法,另外如果有人關注通用的方法,參看“列寧”同志的博客:

64位 inline Hook

        最後菜鳥言論,僅供娛樂。

發佈了49 篇原創文章 · 獲贊 4 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章