【问题标题】:Hooking IAT address is incorrect挂钩 IAT 地址不正确
【发布时间】:2012-03-14 20:28:13
【问题描述】:

我正在尝试查找在测试应用程序中导入的 wglGetProcAddress (OpenGl32.dll) 条目。出于某种原因,名称为“wglGetProcAddress”的导入确实指向通过在 Opengl32.dll 上调用 GetModuleHandle 和 GetProcAddress 返回的相同函数。

可执行文件已加载到内存中,并且是一个进程,其线程当前处于挂起状态。以下代码正确读取了该可执行文件导入的名称模块及其函数。因此,IAT 不应包含 RVA,因为它已被加载。

HMODULE h = GetModuleHandle("OPENGL32.dll");
DWORD expect_addr = (DWORD)GetProcAddress(h, "wglGetProcAddress");

PIMAGE_IMPORT_DESCRIPTOR import_desc = (PIMAGE_IMPORT_DESCRIPTOR)(pmem + import_dir);
while (import_desc->Name)
{
    PIMAGE_THUNK_DATA thunk = (PIMAGE_THUNK_DATA)(pmem + import_desc->OriginalFirstThunk);
    while (thunk->u1.Function)
    {
        PIMAGE_IMPORT_BY_NAME import = (PIMAGE_IMPORT_BY_NAME)(pmem + thunk->u1.AddressOfData);

        printf("%s 0x%X\n", import->Name, thunk->u1.Function);
        if ((DWORD)expect_addr == (DWORD)thunk->u1.Function)
        {
            printf("Found wglGetProcAddress\n");
        }
        else if (!strcmp((const char*)import->Name, "wglGetProcAddress"))
        {
            printf("Found wglGetProcAddress's import, but the function has a different value.\n");
        }
        ++thunk;
    }

    ++import_desc;
}

GetProcAddress 从该原始值返回地址 60XXC245,其中 XX 变化,但 thunk->u1.Function 始终返回 0xA46D8。 thunk->u1 中的所有内容(Function、AddressOfData、Ordinal 和 ForwarderString)都有 相同的值。导入描述符和导入的名称正确。有人看到我错过了什么吗?

编辑: 我正在尝试其他方法:我正在扫描 pmem(内存中可执行文件的图像)以查找我期望的 IAT 条目,但它也没有找到:

HMODULE h = GetModuleHandle("OPENGL32.dll");
DWORD expect_addr = (DWORD)GetProcAddress(h, "wglGetProcAddress");

printf("Looking for 0x%X\n", expect_addr);
for (int i = 0; i < pmem_size - sizeof(DWORD); i++)
{
    if (*(DWORD*)(pmem + i) == expect_addr)
    {
        printf("0x%X at 0x%X\n", *(DWORD*)(pmem + i), i);
    }
}

已解决:我没有意识到,但是使用 CREATE_SUSPENDED 调用 CreateProcess 会阻止 Windows 加载器使用实际地址填充 FirstThunk。如果我让进程运行一秒钟然后挂起线程,它会很好地挂钩 IAT 地址。现在我得去寻找一种方法来解决这个问题。

【问题讨论】:

    标签: c hook portable-executable


    【解决方案1】:

    u1.Function 表示相对于IMAGE_IMPORT_BY_NAME 条目的偏移量(如果设置了IMAGE_ORDINAL_FLAG 位,则为序号条目)。这就是为什么它与来自GetProcAddress 的值不匹配的原因,因为它不是函数的地址,而是函数的地址import entry

    当您找到与您的函数匹配的 thunk 时,您也需要使用它来查找链接器更改的虚拟化地址,形成 other thunk 列表。改变你的代码,让它这样做:

    while (import_desc->Name)
    {
        PIMAGE_THUNK_DATA thunk = (PIMAGE_THUNK_DATA)((DWORD)GetModuleHandle(NULL) + import_desc->OriginalFirstThunk);
        int i = 0;
        while (thunk->u1.Function)
        {
            PIMAGE_IMPORT_BY_NAME import = (PIMAGE_IMPORT_BY_NAME)((DWORD)GetModuleHandle(NULL) + thunk->u1.AddressOfData);
    
            void** p = (void**)((DWORD)GetModuleHandle(NULL) + import_desc->FirstThunk);
            printf("%s 0x%X\n", import->Name, p[i]);//thunk->u1.Function);
            if ((DWORD)expect_addr == (DWORD)p[i])
            {
                printf("Found wglGetProcAddress\n");
            }
            else if (!strcmp((const char*)import->Name, "wglGetProcAddress"))
            {
                printf("Found wglGetProcAddress's import, but the function has a different value.\n");
            }
            ++thunk;
        }
    
        ++import_desc;
    }
    

    为了获得 IAT 条目,我做的事情有点不同:

    inline const void** GetImportAddress(HMODULE hModule, IMAGE_IMPORT_DESCRIPTOR* pTable, size_t nThunk)
    {
        const void** pAddressBlock = (const void**)((DWORD)hModule + pTable->FirstThunk);
        return &pAddressBlock[nThunk];      
    }
    
    const void** GetImport(HMODULE hModule, const char* szDll, const char* szFunction)
    {
        const char* szDllName = NULL;
        IMAGE_IMPORT_DESCRIPTOR* pTable = GetImportDescriptor(hModule);
        while(pTable->Characteristics != 0 && (szDllName = GetImportTableName(hModule,pTable)) != NULL)
        {
            if(!lstrcmpiA(szDll,szDllName))
            {
                IMAGE_THUNK_DATA* pThunkData = GetThunk(hModule,pTable);
                if(pThunkData != NULL)
                {
                    size_t nThunk = 0;
                    while(pThunkData->u1.AddressOfData != 0)
                    {
                        if(pThunkData->u1.Ordinal & IMAGE_ORDINAL_FLAG)
                        {
                            if(IMAGE_ORDINAL32(pThunkData->u1.Ordinal) == (DWORD)szFunction)
                                return GetImportAddress(hModule,pTable,nThunk);
                        }
                        else
                        {
                            IMAGE_IMPORT_BY_NAME* pImport = GetImport(hModule,pThunkData);
                            if(!lstrcmpA(szFunction,(const char*)pImport->Name))
                                return GetImportAddress(hModule,pTable,nThunk);
                        }
    
                        nThunk++;
                        pThunkData++;
                    }
                }
            }
    
            pTable++;
        }
    
        return NULL;
    }
    

    【讨论】:

    • 它与 GetProcAddress 中的值不匹配。如何找到地址块?
    • @Dmi:我添加了我在许多项目中使用的代码来执行 IAT 挂钩和查找,应该会给你你想要的东西
    • 谢谢,但如果我使用 const void** addr = GetImportAddress(pmem, n) 其中 n 是块索引,*addr 再次给我 0xA46D8。
    • @Dmi:用一个可以工作的固定版本的代码重写了一点(它开始时遇到了同样的问题)。您基本上从 FirstOriginalThunk 打印出 IAT 偏移量,而不是从 FirstThunk 打印出虚拟化地址。
    • 哦,我没有意识到,但是使用 CREATE_SUSPENDED 调用 CreateProcess 会阻止 Windows 加载器使用实际地址填充 FirstThunk。 ://
    猜你喜欢
    • 1970-01-01
    • 2015-12-20
    • 2012-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-09
    相关资源
    最近更新 更多