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丢失!\n");
    //在调用原函数之前,可以对IN(输入类)参数进行干涉
    lstrcpy(newText, lpText);//为防止原函数提供的缓冲区不够,这里复制到我们自己的一个缓冲区中再进行操作
    lstrcat(newText, "\t 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*********************************\n", 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\n", szFuncName, TargetFunAddr);
    printf("hook模块基质(此exe):0x%p\n", hModToHook);  //需要hook的程序,这个是此程序
    pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hModToHook, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);; //IID指针
    printf("IID数组地址:0x%p\n", pImportDescriptor);
    while (pImportDescriptor->FirstThunk)
    {
        szModName = (char*)((PBYTE)hModToHook + pImportDescriptor->Name);
        printf("当前模块(DLL)名称:%s\n", szModName);
        if (stricmp(szModName, szModuleName) != 0)
        {
            printf("DLL名字不对下一个\n");
            pImportDescriptor++;
            continue;
        }
        //程序的导入表处理完毕后OriginalFirstThunk可能是无效的,不能再根据名称来查找,而是遍历FirstThunk直接根据地址判断
        pThunkData = (PIMAGE_THUNK_DATA)((BYTE*)hModToHook + pImportDescriptor->FirstThunk);
        while (pThunkData->u1.Function)
        {
            lpAddr = (ULONG_PTR*)pThunkData;
            //找到了地址
            if ((*lpAddr) == TargetFunAddr)
            {
                printf("目标函数找到!\n");
                //通常情况下导入表所在内存页都是只读的,因此需要先修改内存页的属性为可写
                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. 运行情况:

 

确定之后返回:

 

区别正常逻辑:

 

 

posted @ 2020-04-08 16:37  jentle  阅读(1016)  评论(0编辑  收藏  举报