VC++实现全局钩子勾住textout金山快译的原理实现

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               

大家都对金山快译可以讲英文软件翻译成中文软件感到惊奇

其实就是全局钩子勾住textout函数

钩子头文件如下

void __declspec(dllexport) WINAPI InstallHook(BOOL,DWORD); BOOL WINAPI UnInstallHook(); LRESULT CALLBACK GetMsgProC(int code,WPARAM wParam,LPARAM lParam); void WINAPI HookOneAPI(LPCTSTR pszCalleeModuleName,PROC pfnOriginApiAddress,         PROC pfnDummyFuncAddress,HMODULE hModCallerModule); BOOL WINAPI HookAllAPI(LPCTSTR pszCalleeModuleName,PROC pfnOriginApiAddress,         PROC pfnDummyFuncAddress,HMODULE hModCallerModule); BOOL WINAPI UnhookAllAPIHooks(LPCTSTR pszCalleeModuleName,PROC pfnOriginApiAddress,          PROC pfnDummyFuncAddress,HMODULE hModCallerModule); BOOL WINAPI H_TextOutA(HDC, int, int, LPCSTR, int); BOOL WINAPI H_TextOutW(HDC, int, int, LPCWSTR, int); BOOL WINAPI H_ExtTextOutA(HDC, int, int, UINT, CONST RECT *,LPCSTR, UINT, CONST INT *); BOOL WINAPI H_ExtTextOutW(HDC, int, int, UINT, CONST RECT *,LPCWSTR, UINT, CONST INT *); 


钩子源文件如下

#include "stdafx.h"   #include "APIHook_Dll.h"   #include <ImageHlp.h>   #include <tlhelp32.h>   #pragma comment(lib,"ImageHlp") //定义全局共享数据段   #pragma data_seg("Shared")   HMODULE hmodDll=NULL;   HHOOK hHook=NULL;   #pragma data_seg()   #pragma comment(linker,"/Section:Shared,rws") //设置全局共享数据段的属性   / DllMain 函数 /   //dll的入口点   BOOL APIENTRY DllMain( HMODULE hModule,                           DWORD  ul_reason_for_call,                           LPVOID lpReserved         )   {    switch(ul_reason_for_call)    {    case DLL_PROCESS_ATTACH:     //if(sHook)           case DLL_PROCESS_DETACH:     UnInstallHook();     break;    }    hmodDll=hModule;       return TRUE;   }   / HookOneAPI 函数 /   //进行IAT转换的关键函数,其参数含义:   //pszCalleeModuleName:需要hook的模块名   //pfnOriginApiAddress:要替换的自己API函数的地址   //pfnDummyFuncAddress:需要hook的模块名的地址   //hModCallerModule:我们要查找的模块名称,如果没有被赋值,   //     将会被赋值为枚举的程序所有调用的模块   void WINAPI HookOneAPI(LPCTSTR pszCalleeModuleName,PROC pfnOriginApiAddress,            PROC pfnDummyFuncAddress,HMODULE hModCallerModule)   {    ULONG size;    //获取指向PE文件中的Import中IMAGE_DIRECTORY_DESCRIPTOR数组的指针    PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)     ImageDirectoryEntryToData(hModCallerModule,TRUE,IMAGE_DIRECTORY_ENTRY_IMPORT,&size);    if (pImportDesc == NULL)     return;    //查找记录,看看有没有我们想要的DLL    for (;pImportDesc->Name;pImportDesc++)    {     LPSTR pszDllName = (LPSTR)((PBYTE)hModCallerModule+pImportDesc->Name);     if (lstrcmpiA(pszDllName,pszCalleeModuleName) == 0)      break;    }    if (pImportDesc->Name == NULL)    {     return;    }    //寻找我们想要的函数    PIMAGE_THUNK_DATA pThunk =      (PIMAGE_THUNK_DATA)((PBYTE)hModCallerModule+pImportDesc->FirstThunk);//IAT    for (;pThunk->u1.Function;pThunk++)    {     //ppfn记录了与IAT表项相应的函数的地址     PROC * ppfn= (PROC *)&pThunk->u1.Function;       if (*ppfn == pfnOriginApiAddress)      {      //如果地址相同,也就是找到了我们想要的函数,进行改写,将其指向我们所定义的函数      WriteProcessMemory(GetCurrentProcess(),ppfn,&(pfnDummyFuncAddress),       sizeof(pfnDummyFuncAddress),NULL);      return;     }    }   }   //查找所挂钩的进程所应用的dll模块的   BOOL WINAPI HookAllAPI(LPCTSTR pszCalleeModuleName,PROC pfnOriginApiAddress,           PROC pfnDummyFuncAddress,HMODULE hModCallerModule)   {    if (pszCalleeModuleName == NULL)    {     return FALSE;    }    if (pfnOriginApiAddress == NULL)    {     return FALSE;    }    //如果没传进来要挂钩的模块名称,枚举被挂钩进程的所有引用的模块,    //并对这些模块进行传进来的相应函数名称的查找        if (hModCallerModule == NULL)    {     MEMORY_BASIC_INFORMATION mInfo;     HMODULE hModHookDLL;     HANDLE hSnapshot;     MODULEENTRY32 me = {sizeof(MODULEENTRY32)};     //MODULEENTRY32:描述了一个被指定进程所应用的模块的struct     VirtualQuery(HookOneAPI,&mInfo,sizeof(mInfo));     hModHookDLL=(HMODULE)mInfo.AllocationBase;          hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,0);     BOOL bOk = Module32First(hSnapshot,&me);     while (bOk)     {      if (me.hModule != hModHookDLL)      {       hModCallerModule = me.hModule;//赋值       //me.hModule:指向当前被挂钩进程的每一个模块        HookOneAPI(pszCalleeModuleName,pfnOriginApiAddress,        pfnDummyFuncAddress,hModCallerModule);      }      bOk = Module32Next(hSnapshot,&me);     }     return TRUE;      }    //如果传进来了,进行查找    else    {     HookOneAPI(pszCalleeModuleName,pfnOriginApiAddress,       pfnDummyFuncAddress,hModCallerModule);     return TRUE;    }    return FALSE;   }    UnhookAllAPIHooks 函数 /   //通过使pfnDummyFuncAddress与pfnOriginApiAddress相等的方法,取消对IAT的修改   BOOL WINAPI UnhookAllAPIHooks(LPCTSTR pszCalleeModuleName,PROC pfnOriginApiAddress,            PROC pfnDummyFuncAddress,HMODULE hModCallerModule)   {    PROC temp;    temp = pfnOriginApiAddress;    pfnOriginApiAddress = pfnDummyFuncAddress;    pfnDummyFuncAddress = temp;    return HookAllAPI(pszCalleeModuleName,pfnOriginApiAddress,     pfnDummyFuncAddress,hModCallerModule);   }   // GetMsgProc 函数    //钩子子程。与其它钩子子程不大相同,没做什么有意义的事情,继续调用下一个钩子子程,形成循环   LRESULT CALLBACK GetMsgProc(int code,WPARAM wParam,LPARAM lParam)   {    return CallNextHookEx(hHook,code,wParam,lParam);   }    InstallHook 函数 /   //安装或卸载钩子,BOOL IsHook参数是标志位   //对要钩哪个API函数进行初始化   //我们这里装的钩子类型是WH_GETMESSAGE   void __declspec(dllexport) WINAPI InstallHook(BOOL IsHook,DWORD dwThreadId)   {    if(IsHook)    {    hHook=SetWindowsHookEx(WH_GETMESSAGE,(HOOKPROC)GetMsgProc,hmodDll,dwThreadId);        //GetProcAddress(GetModuleHandle("GDI32.dll"),"ExtTextOutA"):取得要钩的函数在所在dll中的地址        HookAllAPI("GDI32.dll",GetProcAddress(GetModuleHandle("GDI32.dll"),     "TextOutW"),(PROC)&H_TextOutW,NULL);    HookAllAPI("GDI32.dll",GetProcAddress(GetModuleHandle("GDI32.dll"),     "TextOutA"),(PROC)&H_TextOutA,NULL);    }    else    {     UnInstallHook();     UnhookAllAPIHooks("GDI32.dll",GetProcAddress(GetModuleHandle("GDI32.dll"),      "TextOutW"),(PROC)&H_TextOutW,NULL);     UnhookAllAPIHooks("GDI32.dll",GetProcAddress(GetModuleHandle("GDI32.dll"),      "TextOutA"),(PROC)&H_TextOutA,NULL);    }   }   / UnInstallHook 函数    //卸载钩子   BOOL WINAPI UnInstallHook()   {    UnhookWindowsHookEx(hHook);    return TRUE;   }   / H_TextOutA 函数 /   //我们的替换函数,可以在里面实现我们所要做的功能   //这里我做的是显示一个对话框,指明是替换了哪个函数   BOOL WINAPI H_TextOutA(HDC hdc,int nXStart,int nYStart,LPCSTR lpString,int cbString)   {   //  FILE *stream=fopen("logfile.txt","a+t");    MessageBox(NULL,"TextOutA","APIHook_Dll ---rivershan",MB_OK);    TextOutA(hdc,nXStart,nYStart,lpString,cbString);//返回原来的函数,以显示字符   // fprintf(stream,lpString);   // fclose(stream);    return TRUE;   }   / H_TextOutW 函数 /   //同上   BOOL WINAPI H_TextOutW(HDC hdc,int nXStart,int nYStart,LPCWSTR lpString,int cbString)   {     MessageBox(NULL,"TextOutW","APIHook_Dll ---rivershan",MB_OK);    TextOutW(hdc,nXStart,nYStart,lpString,cbString);//返回原来的函数,以显示字符    return TRUE;   



设置全局钩子如下


  InstallHook(TRUE,0);

           

给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow
这里写图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
3.1 程序说明 当启动程序Try.EXE调用SetMouseHook()后,Windows系统将MouseHook.DLL映射入每一个有鼠标消息传入的进程地址空间。映射时将用DLL_PROCESS_ATTACH作为参数fdwReason的值调用DllEntryPoint(),DllEntryPoint()调用ModifyCall()搜索该进程对TextOutA()的调用并将其替换为调用MyTextOut()。这样当该进程调用GDI32.DLL的TextOutA()时实际调用的却是MouseHook.DLL的MyTextOut()。 ModifyCall()利用进程的HINSTANCE(也即HMODULE,对于Win32而言它们是一回事,即装载基址)找到DOS文件头结构IMAGE_DOS_HEADER,再利用IMAGE_DOS_HEADER中的e_lfanew成员找到Win32的IMAGE_NT_HEADERS结构,该结构含有动态连接所需的信息。IMAGE_NT_HEADERS中的OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]含有DLL函数引入表的RVA(相对虚拟地址)和大小。搜索该表所指向的DLL引入函数地址,值与GetProcAddress()返回值相同的单元就是对应的DLL函数地址存放单元,将MyTextOut()地址写入即可。详细情形请参阅有关PE文件格式的说明和Winnt.h中的定义。 当启动程序调用UnMouseHook()时,过程与之类似,只是此时是为了卸下WH_MOUSE全局钩子并恢复原来对TextOutA()的调用。 此处设置WH_MOUSE全局钩子的目的只是利用全局钩子的特性将MouseHook.DLL“挤进”其它进程的地址空间,因此钩子过程MouseProc()很简单,只是传递一下消息而已。 两个#pragma data_seg()编译器指令是为了定义一个名为.MouseHook的数据段(更确切地说是数据节),该数据段在MouseHook.DEF中被说明为共享,之所以如此是因为各个进程空间中的MouseProc()需要该钩子的句柄hMouseHook,而hMouseHook只在启动程序Try.EXE调用SetMouseHook()时得到一次,因此只能放在共享内存中,否则编程将变得复杂起来。至于每个进程中被替换下来的TextOutA()地址,是属于单个进程空间的,故放在局部数据中,Windows系统会为每次映射使用不同的内存。实际上,TextOutA()的引入地址在所有的进程中都是相同的,这是因为为了页面管理的简单和进程切换的效率,对每个进程Windows 9x将系统DLL映射在同一地址上,但这不是Windows对外保证的,而只是权宜之计,以后可能改变,且Windows NT的情况也可能不同。 MyTextOut()将截获的TextOutA()的参数lpText(即输出字符串)改变以后才输出,从而可以看到截获是否成功。之所以改变两个字符而不是简单的一个,是因为只改变一个字符将导致汉字输出乱码。 MyTextOut()源码中唯一的一条汇编语句__asm sub esp,14h是所有源码中最难写的语句。如果没有这条指令,MyTextOut()将无法正确返回到进程调用TextOutA()处的下一条指令上,出现的“意外”情况是:进程调用TextOutA()的最后一个参数、即输出字符串长度参数将作为返回地址从堆栈中弹出,从而使EIP为一个很小的值,程序进入Windows系统用作指针检查的低端内存,导致非法内存访问。在调试过程中发现导致这种现象的原因是MyTextOut()在临返回前使用了add esp,14h来清除并不需要清除的堆栈,从而破坏了堆栈。显然,原因在于函数调用说明使编译器产生了“错误”的堆栈管理代码,我不知道如何改正这一点,只好使用__asm sub esp,14h强行使堆栈指针指向“正确”的返回地址。有知晓个中奥妙的同志请与作者联系,多多指教。 以下程序在Windows 98、Microsoft Visual Studio 97中调试通过,由于编程中并未使用Windows 9x的特性,且程序依靠的PE文件格式在Windows 9x和Wiundows NT中是通用的,因此上述方法在Windows NT可能也是可行的,只是我并未验证(我没有装Windows NT的机器)。另外,某些方面的情况由于编译器和操作系统不同可能会有所不同(如编译器生成的指令),我的叙述会因此而偏颇甚至错误,在此先做个提醒,也欢迎来意见改进编程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值