zoukankan      html  css  js  c++  java
  • IATHOOK(1): 对当前程序hook出弹窗并修改内容

    IAThook的原理学习帖子有很多,我需要复现一下几个基本概念先:

    1.  PE结构下IID数组相关指针(放假太久都快忘了各个指针位置了)

    这个之前我写过简单的分析工具,参考:https://bbs.pediy.com/thread-255851.htm   结构总览:

    2. 主要思路:

    虽然r3层的钩子是对进程局部hook,但是实现iat hook为什么就一定要外注入DLL呢(手动狗头)

           程序运行有一个小弹窗,然后开始hook,利用GetModuleHandle(NULL)获取当前进程模块句柄,然后确定目标 dll名字和目标函数名称:(char*)"user32.dll", (char*)"MessageBoxA"  ,LoadLibrary动态加载起来目标dll,获得相应的基质和GetProcAddress目标函数指针(这里对PE的结构处理函数可参考我之前的帖子https://bbs.pediy.com/thread-255851.htm  对PE的基本处理函数应有尽有)。找到目标函数之后,需要进行权限修改,因为目标只有读权限,这里借鉴了加密与解密。 找到之后获得目标函数地址后赋给我们之前定义好的Detor,就可以对源messagebox就行修改了。

    3.完整代码如下:

    vs2019:  如果提示const char[]转 char*    就对函数形参加const, 项目属性改成多字符集

    #include <windows.h>
    #include <stdio.h>
    #include <imagehlp.h>
    #pragma comment(lib,"imagehlp.lib")
    
    
    //以MessageBoxA的原型定义一个函数指针类型
    typedef int
    (WINAPI* PMessageBoxA)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);
    
    //以MessageBoxA的原型定义一个函数来替代原始的MessageBoxA Detor函数
    int WINAPI My_MessageBoxA(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption,UINT uType);
    
    //存在以下关系
    //*(*pThunkPointer) == *pOriginalFuncAddr ;
    BOOL InstallHook(
        HMODULE hModToHook,
        char* szModuleName,
        char* szFuncName,
        PVOID ProxyFunc,
        PULONG_PTR* pThunkPointer,
        ULONG_PTR* pOriginalFuncAddr
    );
    
    VOID ShowMsgBox(const char* szMsg);
    BOOL IAT_InstallHook();
    VOID IAT_UnInstallHook();
    //保存原始MessageBoxA的地址
    PMessageBoxA OldMessageBox = NULL;
    //指向IAT中pThunk的地址
    PULONG_PTR g_PointerToIATThunk = NULL;
    
    int main(int argc, char* argv[])
    {
        ShowMsgBox("1+1=2 ? 对吗");
        IAT_InstallHook();
        ShowMsgBox("八对");
        IAT_UnInstallHook();
        ShowMsgBox("hook卸载完成");
        return 0;
    }
    
    //之所以把这个调用单独放在一个函数中,是因为Release模式下对调用进行了优化,第二次调用时直接采用了寄存器寻址而不是导入表
    //因此,单独放在一个函数中可以避免这个情况。
    
    VOID ShowMsgBox(const char* szMsg)
    {
        MessageBoxA(NULL, szMsg, "Test", MB_OK);
    }
    
    
    int WINAPI My_MessageBoxA(
        HWND hWnd,          // handle of owner window
        LPCTSTR lpText,     // address of text in message box
        LPCTSTR lpCaption,  // address of title of message box
        UINT uType          // style of message box
    )
    {
        int ret;
        char newText[1024] = { 0 };
        char newCaption[256] = "jentle";
        printf("MessageBox丢失!
    ");
        //在调用原函数之前,可以对IN(输入类)参数进行干涉
        lstrcpy(newText, lpText);//为防止原函数提供的缓冲区不够,这里复制到我们自己的一个缓冲区中再进行操作
        lstrcat(newText, "	 MessageBox Hacked by jentle");//篡改的消息框内容
        ret = OldMessageBox(hWnd, newText, newCaption, uType);//调用原MessageBox,并保存返回值
        return ret;//这里你还可以干涉原始函数的返回值
    
    }
    
    BOOL IAT_InstallHook()
    {
        BOOL bResult = FALSE;
        HMODULE currectExe = GetModuleHandle(NULL);
        PULONG_PTR pt;
        ULONG_PTR OrginalAddr;
        bResult = InstallHook(currectExe, (char*)"user32.dll", (char*)"MessageBoxA", (PVOID)My_MessageBoxA, &pt, &OrginalAddr);
        if (bResult)
        {
            printf("************************Hook完毕! pThunk=0x%p  OriginalAddr = 0x%p*********************************
    ", pt, OrginalAddr);
            g_PointerToIATThunk = pt;
            OldMessageBox = (PMessageBoxA)OrginalAddr;
        }
        return bResult;
    
    }
    
    VOID IAT_UnInstallHook()
    {
    
        DWORD dwOLD;
        MEMORY_BASIC_INFORMATION  mbi;
        if (g_PointerToIATThunk)
        {
            //查询并修改内存页的属性
            VirtualQuery((LPCVOID)g_PointerToIATThunk, &mbi, sizeof(mbi));
            VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOLD);
            //将原始的MessageBoxA地址填入IAT中
            *g_PointerToIATThunk = (ULONG)OldMessageBox;
            //恢复内存页的属性
            VirtualProtect(mbi.BaseAddress, mbi.RegionSize, dwOLD, 0);
        }
    
    }
    BOOL InstallHook(
        HMODULE hModToHook, //待Hook的模块基址
         char* szModuleName, //目标DLL名字
        char* szFuncName, //目标函数名字
        PVOID DetourFunc, //Detour函数
        PULONG_PTR* pThunkPointer, //
        ULONG_PTR* pOriginalFuncAddr
    )
    {
        PIMAGE_IMPORT_DESCRIPTOR  pImportDescriptor;
        PIMAGE_THUNK_DATA         pThunkData;
        ULONG ulSize;
        HMODULE hModule = 0;
        ULONG_PTR TargetFunAddr;
        PULONG_PTR lpAddr;
        char* szModName;
        BOOL result = FALSE;
        BOOL bRetn = FALSE;
    
        hModule = LoadLibrary(szModuleName);//动态加载目标DLL
        TargetFunAddr = (ULONG_PTR)GetProcAddress(hModule, szFuncName);  //目标函数指针
        printf("目标%s函数地址 :0x%p
    ", szFuncName, TargetFunAddr);
        printf("hook模块基质(此exe):0x%p
    ", hModToHook);  //需要hook的程序,这个是此程序
        pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hModToHook, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);; //IID指针
        printf("IID数组地址:0x%p
    ", pImportDescriptor);
        while (pImportDescriptor->FirstThunk)
        {
            szModName = (char*)((PBYTE)hModToHook + pImportDescriptor->Name);
            printf("当前模块(DLL)名称:%s
    ", szModName);
            if (stricmp(szModName, szModuleName) != 0)
            {
                printf("DLL名字不对下一个
    ");
                pImportDescriptor++;
                continue;
            }
            //程序的导入表处理完毕后OriginalFirstThunk可能是无效的,不能再根据名称来查找,而是遍历FirstThunk直接根据地址判断
            pThunkData = (PIMAGE_THUNK_DATA)((BYTE*)hModToHook + pImportDescriptor->FirstThunk);
            while (pThunkData->u1.Function)
            {
                lpAddr = (ULONG_PTR*)pThunkData;
                //找到了地址
                if ((*lpAddr) == TargetFunAddr)
                {
                    printf("目标函数找到!
    ");
                    //通常情况下导入表所在内存页都是只读的,因此需要先修改内存页的属性为可写
                    DWORD dwOldProtect;
                    MEMORY_BASIC_INFORMATION  mbi;
                    VirtualQuery(lpAddr, &mbi, sizeof(mbi));
                    bRetn = VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtect);
                    if (bRetn)
                    {
                        //内存页属性修改成功,继续下一步操作,先保存原始数据
                        if (pThunkPointer != NULL)
                        {
                            *pThunkPointer = lpAddr;
                        }
                        if (pOriginalFuncAddr != NULL)
                        {
                            *pOriginalFuncAddr = *lpAddr;
                        }
                        //修改地址
                        *lpAddr = (ULONG_PTR)DetourFunc;
                        result = TRUE;
                        //恢复内存页的属性
                        VirtualProtect(mbi.BaseAddress, mbi.RegionSize, dwOldProtect, 0);
                    }
                    break;
                }
                pThunkData++;
            }
            pImportDescriptor++;
        }
    
        FreeLibrary(hModule);
        return result;
    }

    4. 运行情况:

    确定之后返回:

    区别正常逻辑:

  • 相关阅读:
    如何保证最少消费一次redis的list队列数据
    如果设置Redis客户端的超时时长?
    REdis一致性方案探讨
    Linux后台开发工具箱-葵花宝典
    REdis主从复制之repl_backlog
    C++之Lambda研究
    Redis-5.0.5集群配置
    REdis之maxmemory解读
    [转载]centos6.3安装启动使用PostgreSQL 9.2
    [转载]linux环境变量设置方法总结(PATH/LD_LIBRARY_PATH)
  • 原文地址:https://www.cnblogs.com/jentleTao/p/12660851.html
Copyright © 2011-2022 走看看