You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在x64程序中调用已加载的x86 DLL中API失败问题排查

x64进程中调用x86 DLL API触发无效指令错误的排查

实验背景与已实现流程

本次实验目标是利用Windows x64系统GDT中值为0x23的32位兼容模式选择子,在x64宿主程序中加载并调用x86 DLL的函数。目前已完成以下步骤:

  • 导出x86 DLL的所有导入项并保存至XML文件
  • 通过修改后的MemoryModule从内存加载该x86 DLL,并修补其导入地址表(IAT)

模式切换代码实现

  • 从x64跳转到32位兼容模式的代码:
push 0x23    
xor rcx,rcx    
mov ecx,Start32  
push rcx
retf 

其中Start32是32位入口点,用于调用目标函数:

USE32
push eax
push dword [WhatToPass]
mov eax, [WhatToCall]
call eax
pop eax
  • 从32位兼容模式返回x64模式的代码:
USE64
db 0eah
ret_64:
dd 0
dw 0x33
nop

当前运行状态

当调用的是几乎无操作的exp1函数时,整个流程可在WinDbg中正常执行(WinDbg会显示切换至x86运行模式),但无法在Visual Studio中运行。现在尝试调用x86模块中的标准API(如MessageBeep(0);)时,即使确认地址有效,仍会触发无效执行指令错误。

IAT修补代码(已确认逻辑正确)

void PatchIAT(HINSTANCE h,std::vector<FANDP>* CustomLoading = 0)
{
    PCHAR codeBase = (PCHAR)h;
    XML3::XML x(xResult);
    PIMAGE_NT_HEADERS32           ntheaders = (PIMAGE_NT_HEADERS32)(PCHAR(h) + PIMAGE_DOS_HEADER(h)->e_lfanew);
    PIMAGE_SECTION_HEADER       pSech = IMAGE_FIRST_SECTION(ntheaders);//Pointer to first section header
    DWORD ulsize = 0;
    PIMAGE_IMPORT_DESCRIPTOR importDesc = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(h, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulsize);
    if (!importDesc)
        return;

    for (; importDesc && importDesc->Name; importDesc++) {

        PSTR pszModName = (PSTR)((PBYTE)h + importDesc->Name);
        if (!pszModName)
            break;

        XML3::XMLElement* module = ... ; // 查找对应的XML条目
        if (!module)
            continue;

        DWORD* thunkRef;
        DWORD* funcRef = 0;

        if (importDesc->OriginalFirstThunk) {
            thunkRef = (DWORD*)(codeBase + importDesc->OriginalFirstThunk);
            funcRef = (DWORD*)(codeBase + importDesc->FirstThunk);
        }
        else {
            // 无hint表的情况
            thunkRef = (DWORD*)(codeBase + importDesc->FirstThunk);
            funcRef = (DWORD *)(codeBase + importDesc->FirstThunk);
        }

        DWORD V = 0;
        for (; *thunkRef; thunkRef++, funcRef++) {

            DWORD* wr = (DWORD*)funcRef;

            if (IMAGE_SNAP_BY_ORDINAL(*thunkRef)) {
                const char* fe = (LPCSTR)IMAGE_ORDINAL(*thunkRef);
                 // 从XML中查找对应函数地址V
                ...

            }
            else {
                PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME)(codeBase + (*thunkRef));
             ... // 从XML中查找对应函数地址V
            }


            // 执行IAT修补
            DWORD dwOldProtect = 0;
            if (VirtualProtect((LPVOID)wr, 4, PAGE_READWRITE, &dwOldProtect))
            {
                memcpy((void*)wr, &V, 4);
                VirtualProtect((LPVOID)wr, 4, dwOldProtect, &dwOldProtect);
            }
            VirtualProtect((LPVOID)V, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect);
        }

    }
}

可能的错误原因分析

  • API地址位数不匹配:你从XML中获取的V是x64版本的API地址,而非x86版本。x64进程默认加载的系统DLL(如user32.dll)是64位版本,其导出函数是64位指令集,在32位兼容模式下执行会直接触发无效指令错误。必须确保加载x86版本的系统DLL,并使用其x86函数地址填充IAT。
  • 内存页与地址空间限制:32位兼容模式只能访问x64进程的低4GB地址空间,如果x86 DLL或目标API地址处于高4GB区域,会导致访问异常。同时,即使地址在低4GB,若API属于64位模块,其指令集仍不兼容32位模式。
  • DLL初始化不完整:通过MemoryModule加载的x86 DLL可能未在32位模式下正确执行DllMain初始化,导致内部状态异常,调用API时出错。需确保DLL的初始化流程完全在32位兼容模式下完成。
  • 寄存器与栈状态异常:从x64切换到32位模式时,寄存器状态、栈指针未按32位调用约定初始化,导致参数传递或栈结构错误,触发指令执行异常。

内容的提问来源于stack exchange,提问作者Michael Chourdakis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 16:13:36