windows 下實現函數打樁:攔截API方式

來源:互聯網
上載者:User

標籤:c++函數打樁windows   api攔截   dll記憶體注入   

windows 下實現函數打樁:攔截API方式           最近因為工作需要,開始研究函數打樁的方法。由於不想對工程做過多的修改,於是放棄了使用Google gmock的想法。但是也足足困擾另外我一天一宿。經過奮戰,終於有所收穫。閑話少說,開始看看有什麼方法。
一、基礎準備1. 函數調用的原理:通過函數名(函數的入口地址)對函數進行訪問,假設我們能夠改變函數首地址指向的記憶體的話,使其跳轉到另一個函數去執行的話,那麼就可以實現函數打樁了。2. 方法:對函數首地址出寫入一條組合語言 jmp xxx (其中xxx是要跳轉的相對位址)。3. 令原函數為oldFun,新函數為newFun,那麼打樁時函數跳轉的相對位址 offset = newFun - oldFun - (我們制定的這條指令的大小),此處為絕對跳轉指令的長度=5。  jmp xxx一共6位元組。
函數: 1. VirtualQuery
WINBASEAPISIZE_TWINAPIVirtualQuery(    __in_opt LPCVOID lpAddress,   //所查記憶體位址    __out_bcount_part(dwLength, return) PMEMORY_BASIC_INFORMATION lpBuffer,   //儲存記憶體地區的buffer    __in     SIZE_T dwLength                                                  //資訊長度                                                    );
該函數用於查詢某一段記憶體地區的記憶體資訊,事實VirtualQueryEx也可以使用。2. VirtualProtect
WINBASEAPIBOOLWINAPIVirtualProtect(    __in  LPVOID lpAddress,    __in  SIZE_T dwSize,    __in  DWORD flNewProtect,    __out PDWORD lpflOldProtect    );
該函數用於修改指定記憶體區dwSize個位元組的保護模式。
3. VirtualProtectEx
WINBASEAPIBOOLWINAPIVirtualProtectEx(    __in  HANDLE hProcess,   //進程控制代碼    __in  LPVOID lpAddress,  //需要修改的記憶體首地址    __in  SIZE_T dwSize,     //修改的位元組數    __in  DWORD flNewProtect,  //新的保護屬性    __out PDWORD lpflOldProtect  //舊的保護屬性    );
VirtualProtectEx 用於改變指定進程記憶體段的保護模式,預設情況下函數的記憶體空間不可寫,這就是為什麼要用改變保護屬性的函數。
4. ReadProcessMemory
WINBASEAPIBOOLWINAPIReadProcessMemory(    __in      HANDLE hProcess,    __in      LPCVOID lpBaseAddress,    __out_bcount_part(nSize, *lpNumberOfBytesRead) LPVOID lpBuffer,    __in      SIZE_T nSize,    __out_opt SIZE_T * lpNumberOfBytesRead    );
讀取進程記憶體,lpProcess是首地址,而lpBuffer用於儲存讀出的資料,nSize是需要讀出的位元組數。

5. WriteProcessMemory
WINBASEAPIBOOLWINAPIWriteProcessMemory(    __in      HANDLE hProcess,    __in      LPVOID lpBaseAddress,    __in_bcount(nSize) LPCVOID lpBuffer,    __in      SIZE_T nSize,    __out_opt SIZE_T * lpNumberOfBytesWritten    );
該函數用於寫進程的記憶體空間,可以向進程記憶體注入想要注入的資料,例如函數等。
6. GetCurrentProcess
WINBASEAPI__outHANDLEWINAPIGetCurrentProcess(    VOID    );
該函數返回一個偽進程控制代碼0xffffffff,任何需要進程控制代碼的記憶體都可以使用它。

二、對庫中API打樁 方案一: 打樁:
#define FLATJMPCODE_LENGTH 5            //x86 平坦記憶體模式下,絕對跳轉指令長度#define FLATJMPCMD_LENGTH  1            //機械碼0xe9長度#define FLATJMPCMD         0xe9         //對應彙編的jmp指令// 記錄被打樁函數的內容,以便恢複BYTE g_apiBackup[FLATJMPCODE_LENGTH+FLATJMPCMD_LENGTH];BOOL setStub(LPVOID ApiFun,LPVOID HookFun){    BOOL    IsSuccess = FALSE;    DWORD   TempProtectVar;              //臨時保護屬性變數    MEMORY_BASIC_INFORMATION MemInfo;    //記憶體分頁屬性資訊        VirtualQuery(ApiFun,&MemInfo,sizeof(MEMORY_BASIC_INFORMATION));        if(VirtualProtect(MemInfo.BaseAddress,MemInfo.RegionSize,        PAGE_READWRITE,&MemInfo.Protect))                            //修改頁面為可寫    {        memcpy((void*)g_apiBackup,(const void*)ApiFun, sizeof(g_apiBackup));        *(BYTE*)ApiFun = FLATJMPCMD;                                 //攔截API,在函數程式碼片段前面注入jmp xxx        *(DWORD*)((BYTE*)ApiFun + FLATJMPCMD_LENGTH) = (DWORD)HookFun -            (DWORD)ApiFun - FLATJMPCODE_LENGTH;                VirtualProtect(MemInfo.BaseAddress,MemInfo.RegionSize,            MemInfo.Protect,&TempProtectVar);                        //改回原屬性                IsSuccess = TRUE;    }        return IsSuccess;}
清樁:
BOOL clearStub(LPVOID ApiFun){    BOOL    IsSuccess = FALSE;    DWORD   TempProtectVar;              //臨時保護屬性變數    MEMORY_BASIC_INFORMATION MemInfo;    //記憶體分頁屬性資訊        VirtualQuery(ApiFun,&MemInfo,sizeof(MEMORY_BASIC_INFORMATION));        if(VirtualProtect(MemInfo.BaseAddress,MemInfo.RegionSize,        PAGE_READWRITE,&MemInfo.Protect))                            //修改頁面為可寫    {        memcpy((void*)ApiFun, (const void*)g_apiBackup, sizeof(g_apiBackup));  //恢複程式碼片段                VirtualProtect(MemInfo.BaseAddress,MemInfo.RegionSize,            MemInfo.Protect,&TempProtectVar);                        //改回原屬性                IsSuccess = TRUE;    }        return IsSuccess;}

方案二: 打樁:
bool setStub(LPVOID ApiFun,LPVOID HookFun){HANDLE file_handler = GetCurrentProcess();           //擷取進程偽控制代碼DWORD oldProtect,TempProtectVar;char newCode[6];                                     //用於讀取函數原有記憶體資訊int SIZE = FLATJMPCODE_LENGTH+FLATJMPCMD_LENGTH;     //需要修改的記憶體大小if(!VirtualProtectEx(file_handler,ApiFun,SIZE,PAGE_READWRITE,&oldProtect))  //修改記憶體為可讀寫{return false;}if(!ReadProcessMemory(file_handler,ApiFun,newCode,SIZE,NULL))              //讀取記憶體{return false;}memcpy((void*)g_apiBackup,(const void*)newCode, sizeof(g_apiBackup));      //儲存被打樁函數資訊*(BYTE*)ApiFun = FLATJMPCMD;                                            *(DWORD*)((BYTE*)ApiFun + FLATJMPCMD_LENGTH) = (DWORD)HookFun - (DWORD)ApiFun - FLATJMPCODE_LENGTH;   //樁函數注入     VirtualProtectEx(file_handler,ApiFun,SIZE,oldProtect,&TempProtectVar);  //恢複保護屬性}
清樁:
bool clearStub(LPVOID ApiFun){    BOOL    IsSuccess = FALSE;    HANDLE file_handler = GetCurrentProcess();    DWORD oldProtect,TempProtectVar;    int SIZE = FLATJMPCODE_LENGTH+FLATJMPCMD_LENGTH;if(VirtualProtectEx(file_handler,ApiFun,SIZE,PAGE_READWRITE,&oldProtect))    {        memcpy((void*)ApiFun, (const void*)g_apiBackup, sizeof(g_apiBackup));             //恢複被打樁函數記憶體        VirtualProtectEx(file_handler,ApiFun,SIZE,oldProtect,&TempProtectVar);        IsSuccess = TRUE;     }        return IsSuccess;}

方案三: 打樁:
bool setStub(LPVOID ApiFun,LPVOID HookFun){HANDLE file_handler = GetCurrentProcess();DWORD oldProtect,TempProtectVar;char newCode[6];int SIZE = FLATJMPCODE_LENGTH+FLATJMPCMD_LENGTH;if(!ReadProcessMemory(file_handler,ApiFun,newCode,SIZE,NULL)){return false;}memcpy((void*)g_apiBackup,(const void*)newCode, sizeof(g_apiBackup));*(BYTE*)newCode = FLATJMPCMD;                                        *(DWORD*)((BYTE*)newCode + FLATJMPCMD_LENGTH) = (DWORD)HookFun - (DWORD)ApiFun - FLATJMPCODE_LENGTH;  if(!WriteProcessMemory(file_handler,ApiFun,newCode,FLATJMPCODE_LENGTH,NULL)){return false;}}
 說來也怪,這個方案沒有改變讀取許可權,居然也可以,這裡寫入的方式是用WriteProcessMemory來實現,與直接用指標同理。清樁同上。但是如果直接用指標來寫就會出錯,暫時不知道原因。

至此我們實現了函數的打樁,但是有個小小的問題,若僅僅是如此,對類函數中成員函數打樁有點小問題,指標無法轉換,這是因為類成員函數的指標不僅僅是一個普通的指標,他還包括其他資訊。所有這裡需要解決這個問題,網上找到了兩個方法:
1. 類的普通函數成員地址轉換
LPVOID GetClassFnAddress(...){    LPVOID FnAddress;    __asm    {        lea eax,FnAddress        mov edx,[ebp+8]    // ebp+8 為第一個形參的地址,ebp+C 為第二個形參的地址,以此類推        mov [eax],edx    }    return FnAddress;}

2. 類的虛成員函數地址轉換
LPVOID GetClassVirtualFnAddress(LPVOID pthis,int Index) //Add 2010.8.6{    LPVOID FnAddress;    *(int*)&FnAddress = *(int*)pthis;                       //lpvtable    *(int*)&FnAddress = *(int*)((int*)FnAddress + Index);    return FnAddress;}

至此函數打樁的介紹告一段落。












windows 下實現函數打樁:攔截API方式

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.