首页 > 代码库 > 32位程序下调用64位函数——进程32位模式与64位模式切换

32位程序下调用64位函数——进程32位模式与64位模式切换

之前学习的32位进程中调用64位进程函数的知识整理一下,也就是32位模式与64位模式之间的切换。

相关博客:http://www.cnblogs.com/lanrenxinxin/p/4821152.html

这个博客中提到了github上的开源库,我在另一份开源项目中也看到了个库,可以切换32位至64位。

如果对这个功能具体实现比较感兴趣的朋友可以看看下面的内容。

我阅读了源码并进行了注释,算是对这个具体方法的分析和学习。

 

 

关键:

1.在x64下的进程,不管是32位或者是64位,实际上都映射了两个地址空间,一个是32位,一个是64位。相当于一个进程的两种工作模式,而且这两种工作模式是可以进行切换的,他们之间关键的区别在与cs寄存器  64位: CS = 0x33 ;  32位:CS = 0x23

2.Wow64进程中的r12寄存器指向64位的TEB结构(TEB64);

3.每个32位进程都会加载ntdll32.dll和ntdll.dll模块。其中ntdll.dll是64位模块,我们可以将进程的32位模式改为64位模式,然后再去操作64位进程。

 

注意:

1.只能实现ntdll下函数模式切换

2.只有64位进程内部包含32位,32位不包含64位

 

测试在32模式下获得64位函数的地址:

int main()
{  
    HMODULE NtdllModuleBase32 = NULL;
    
    NtdllModuleBase32 = GetModuleHandle(L"Ntdll.dll");    

    PVOID v1 = GetProcAddress(NtdllModuleBase32, "NtQuerySystemInformation");
    //v1 = 0x77478e60;
    //v1 = 0x00007ffd30223b80
    printf("Ntdll.dll 32bit address:0x%08x\r\n", NtdllModuleBase32);
    printf("NtQuerySystemInformation 32bit address:0x%08x\r\n", v1);


    DWORD64 NtdllModuleBase64 = 0;
    NtdllModuleBase64 = GetModuleHandle64(L"ntdll.dll");
    DWORD64 v2 =  GetProcAddress64(NtdllModuleBase64, "NtQuerySystemInformation");

    printf("Ntdll.dll 32bit address:0x%016llx\r\n", NtdllModuleBase64);
    printf("NtQuerySystemInformation 64bit address:0x%016llx\r\n", v2);
    //64 32 ---64  32
    
    return 0;
}
DWORD64 GetModuleHandle64(wchar_t* ModuleName)
{
    //定义32位和64位Teb结构
    TEB64 Teb;
    //将得到的teb赋值给结构体
    GetMemoy64(&Teb, GetTeb64(), sizeof(TEB64));

    PEB64 Peb;
    GetMemoy64(&Peb, Teb.ProcessEnvironmentBlock, sizeof(PEB64));

    PEB_LDR_DATA64 PebLdrData;
    GetMemoy64(&PebLdrData, Peb.Ldr, sizeof(PEB_LDR_DATA64));

    DWORD64 LastEntry = Peb.Ldr + offsetof(PEB_LDR_DATA64, InLoadOrderModuleList);
    LDR_DATA_TABLE_ENTRY64 LdrDataTableEntry;
    
    LdrDataTableEntry.InLoadOrderLinks.Flink = PebLdrData.InLoadOrderModuleList.Flink;
    do
    {
        //遍历链表
        GetMemoy64(&LdrDataTableEntry, LdrDataTableEntry.InLoadOrderLinks.Flink, sizeof(LDR_DATA_TABLE_ENTRY64));

        wchar_t BaseDllName[MAX_PATH] = { 0 };
        //得到模块名
        GetMemoy64(BaseDllName, LdrDataTableEntry.BaseDllName.Buffer, LdrDataTableEntry.BaseDllName.MaximumLength);

        if (0 == _wcsicmp(ModuleName, BaseDllName))
            return LdrDataTableEntry.DllBase;
    } while (LdrDataTableEntry.InLoadOrderLinks.Flink != LastEntry);

    return 0;
}
//获得Teb
DWORD64 GetTeb64()
{
    //定义一个寄存器 
    Register64 v1;
    v1.dw64 = 0;

#ifdef _M_IX86
    //开始切换
    X64_Start();
    // R12 register should always contain pointer to TEB64 in WoW64 processes
    // R12寄存器指向是64位TEB 将R12值压栈
    X64_Push(_R12);
    // below pop will pop QWORD from stack, as we‘re in x64 mode now
    //将R12pop给v1 TEB在其中
    __asm pop v1.dw[0]  
        X64_End();  //切换回32位
#endif
    //返回TEB
    return v1.dw64;
}

//32位下执行64位汇编实现字符串copy
void GetMemoy64(void* DestinationMemory, DWORD64 SourceMemory, size_t SourceMemoryLength)
{
    if ((NULL == DestinationMemory) || (0 == SourceMemory) || (0 == SourceMemoryLength))
        return;

    Register64 v1 = { SourceMemory };
#ifdef _M_IX86
    __asm
    {
        X64_Start();

        ;// below code is compiled as x86 inline asm, but it is executed as x64 code
        ;// that‘s why it need sometimes REX_W() macro, right column contains detailed
        ;// transcription how it will be interpreted by CPU

        push   edi;// push     rdi
        push   esi;// push     rsi
        mov    edi, DestinationMemory;        // mov      edi, dword ptr [dstMem]        ; high part of RDI is zeroed
  REX_W mov    esi, v1.dw[0];                 // mov      rsi, qword ptr [_src]    REX_W 自减
        mov    ecx, SourceMemoryLength;       // mov      ecx, dword ptr [sz]            ; high part of RCX is zeroed
        
        mov    eax, ecx;       // mov      eax, ecx
        and    eax, 3;         // and      eax, 3
        shr    ecx, 2;         // shr      ecx, 2

        rep    movsd;          // rep movs dword ptr [rdi], dword ptr [rsi]
    
        test   eax, eax;       // test     eax, eax
        je     _move_0;        // je       _move_0
        cmp    eax,1;          // cmp      eax, 1
        je     _move_1;        // je       _move_1
        movsw                  // movs     word ptr [rdi], word ptr [rsi]
        cmp    eax, 2;         // cmp      eax, 2
        je     _move_0;        // je       _move_0
_move_1:
        movsb                  // movs     byte ptr [rdi], byte ptr [rsi]

_move_0:
        pop    esi;// pop      rsi
        pop    edi;// pop      rdi

        X64_End();
    }
#endif
}

//32位下执行64位汇编实现字符串比较
BOOL CompareMemory64(void* DestinationMemory, DWORD64 SourceMemory, size_t SourceMemoryLength)
{
    if ((NULL == DestinationMemory) || (0 == SourceMemory) || (0 == SourceMemoryLength))
        return FALSE;

    bool IsOk = FALSE;
    Register64 v1 = { SourceMemory };
#ifdef _M_IX86
    __asm
    {
        X64_Start();

        ;// below code is compiled as x86 inline asm, but it is executed as x64 code
        ;// that‘s why it need sometimes REX_W() macro, right column contains detailed
        ;// transcription how it will be interpreted by CPU

        push   edi;// push      rdi
        push   esi;// push      rsi
         
        mov    edi, DestinationMemory;            // mov       edi, dword ptr [dstMem]       ; high part of RDI is zeroed
REX_W   mov    esi, v1.dw[0];                     // mov       rsi, qword ptr [_src]
        mov    ecx, SourceMemoryLength;           // mov       ecx, dword ptr [sz]           ; high part of RCX is zeroed
               
        mov    eax, ecx;                        // mov       eax, ecx
        and    eax, 3;// and       eax, 3
        shr    ecx, 2;// shr       ecx, 2
    
        repe   cmpsd;// repe cmps dword ptr [rsi], dword ptr [rdi]
        jnz    _ret_false;     // jnz       _ret_false

        test   eax, eax;       // test      eax, eax
        je     _move_0;        // je        _move_0
        cmp    eax, 1;         // cmp       eax, 1
        je     _move_1;        // je        _move_1
        cmpsw;                 // cmps      word ptr [rsi], word ptr [rdi]
        jnz     _ret_false;    // jnz       _ret_false
        cmp    eax, 2;         // cmp       eax, 2
        je     _move_0;        // je        _move_0
    _move_1:
        cmpsb;                 // cmps      byte ptr [rsi], byte ptr [rdi]
        jnz     _ret_false;    // jnz       _ret_false
    _move_0: 
        mov    IsOk, 1;        // mov       byte ptr [result], 1
    _ret_false: 
        pop    esi;            // pop      rsi
        pop    edi;            // pop      rdi

        X64_End();
    }
#endif
    return IsOk;
}

DWORD64 GetFunctionAddressFromExportTable64(WCHAR* ModuleName,char* FunctionName)
{
    DWORD* AddressOfFunctions = 0;
    WORD*  AddressOfNameOrdinals = 0;
    DWORD* AddressOfNames = 0;
    DWORD64 ModuleBase = GetModuleHandle64(ModuleName);
    if (0 == ModuleBase)
        return 0;
    __try
    {
        IMAGE_DOS_HEADER ImageDosHeader;
        GetMemoy64(&ImageDosHeader, ModuleBase, sizeof(IMAGE_DOS_HEADER));

        IMAGE_NT_HEADERS64 ImageNtHeaders;
        GetMemoy64(&ImageNtHeaders, ModuleBase + ImageDosHeader.e_lfanew, sizeof(IMAGE_NT_HEADERS64));

        IMAGE_DATA_DIRECTORY& ImageDataDirectory =
            ImageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];

        if (0 == ImageDataDirectory.VirtualAddress)
            return 0;

        IMAGE_EXPORT_DIRECTORY ImageExportDirectory;

        GetMemoy64(&ImageExportDirectory, ModuleBase + ImageDataDirectory.VirtualAddress, sizeof(IMAGE_EXPORT_DIRECTORY));

        AddressOfFunctions = (DWORD*)malloc(sizeof(DWORD)*ImageExportDirectory.NumberOfFunctions);
        if (NULL == AddressOfFunctions)
        {
            return 0;
        }
        //得到函数地址数组
        GetMemoy64(AddressOfFunctions, ModuleBase + ImageExportDirectory.AddressOfFunctions, sizeof(DWORD)*ImageExportDirectory.NumberOfFunctions);

         AddressOfNameOrdinals = (WORD*)malloc(sizeof(WORD)*ImageExportDirectory.NumberOfFunctions);
        if (NULL == AddressOfNameOrdinals)
        {
            return 0;
        }
        //得到索引数组
        GetMemoy64(AddressOfNameOrdinals, ModuleBase + ImageExportDirectory.AddressOfNameOrdinals, sizeof(WORD)*ImageExportDirectory.NumberOfFunctions);

        AddressOfNames = (DWORD*)malloc(sizeof(DWORD)*ImageExportDirectory.NumberOfNames);
        if (nullptr == AddressOfNames)
        {
            return 0;
        }
        //根据函数名得到函数索引
        GetMemoy64(AddressOfNames, ModuleBase + ImageExportDirectory.AddressOfNames, sizeof(DWORD)*ImageExportDirectory.NumberOfNames);
        for (DWORD i = 0; i < ImageExportDirectory.NumberOfFunctions; i++)
        {
            if (!CompareMemory64(FunctionName, ModuleBase + AddressOfNames[i],
                strlen(FunctionName) + 1))
                continue;
            else
                //根据索引得到函数相对地址 基地址+相对地址=函数绝对地址
                return ModuleBase + AddressOfFunctions[AddressOfNameOrdinals[i]];
        }
    }
    __finally
    {
        if (AddressOfFunctions != NULL)
        {
            free(AddressOfFunctions);
            AddressOfFunctions = NULL;

        }
        if (AddressOfNameOrdinals != NULL)
        {
            free(AddressOfNameOrdinals);
            AddressOfNameOrdinals = NULL;
        }
        if (AddressOfNames != NULL)
        {
            free(AddressOfNames);
            AddressOfNames = NULL;
        }
    }

    return 0;
}
DWORD64 GetProcAddress64(DWORD64 ModuleBase, char* FunctionName)
{
    //GetProcAddress()
    //得到函数地址
    if (0 == __LdrGetProcedureAddress)
    {
        __LdrGetProcedureAddress = GetFunctionAddressFromExportTable64(L"Ntdll.dll", "LdrGetProcedureAddress"); //    //v1 = 0x00007ffd30193560
        if (0 == __LdrGetProcedureAddress)
            return 0;
    }

    _UNICODE_STRING_T<DWORD64> v1 = { 0 };
    v1.Buffer = (DWORD64)FunctionName;
    v1.Length = (WORD)strlen(FunctionName);  //
    v1.MaximumLength = v1.Length + 1;

    DWORD64 FunctionAddress = 0;
    //根据函数在导出表中的地址 得到函数呼叫地址
    X64Call(__LdrGetProcedureAddress, 4,
        (DWORD64)ModuleBase, (DWORD64)&v1, (DWORD64)0, (DWORD64)&FunctionAddress);
    return FunctionAddress;
}
DWORD64 X64Call(DWORD64 FunctionAddresss, int ParameterCount, ...)
{
    
    //64    rcx  rdx  r8  r9  [][][][][][]
    //四寄存器赋值
    va_list v1;
    va_start(v1, ParameterCount);
    Register64 _rcx = { (ParameterCount > 0) ? ParameterCount--, va_arg(v1, DWORD64) : 0 };
    Register64 _rdx = { (ParameterCount > 0) ? ParameterCount--, va_arg(v1, DWORD64) : 0 };
    Register64 _r8 = { (ParameterCount > 0) ? ParameterCount--, va_arg(v1, DWORD64) : 0 };
    Register64 _r9 = { (ParameterCount > 0) ? ParameterCount--, va_arg(v1, DWORD64) : 0 };
    Register64 _rax = { 0 };
    //剩余参数
    Register64 restArgs = { (DWORD64)&va_arg(v1, DWORD64) };

    // conversion to QWORD for easier use in inline assembly
#ifdef _M_IX86
    Register64 _argC = { (DWORD64)ParameterCount }; //剩余参数数量
    DWORD back_esp = 0;
    WORD back_fs = 0;
    
    __asm
    {
        // reset FS segment, to properly handle RFG
        //重置fs段寄存器
        mov    back_fs, fs
        mov    eax, 0x2B
        mov    fs, ax

        // keep original esp in back_esp variable
        // 保存esp
        mov    back_esp, esp
        
        // align esp to 0x10, without aligned stack some syscalls may return errors !
        // (actually, for syscalls it is sufficient to align to 8, but SSE opcodes 
        // requires 0x10 alignment), it will be further adjusted according to the
        // number of arguments above 4
        //esp地址对齐
        and    esp, 0xFFFFFFF0
        //准备ok 开始切换
        X64_Start();

        // below code is compiled as x86 inline asm, but it is executed as x64 code
        // that‘s why it need sometimes REX_W() macro, right column contains detailed
        // transcription how it will be interpreted by CPU

        // fill first four arguments
        //压四个寄存器
        REX_W mov    ecx, _rcx.dw[0];// mov     rcx, qword ptr [_rcx]
        REX_W mov    edx, _rdx.dw[0];// mov     rdx, qword ptr [_rdx]
        push   _r8.dw64;// push    qword ptr [_r8]
        X64_Pop(_R8); ;// pop     r8
        push   _r9.dw64;// push    qword ptr [_r9]
        X64_Pop(_R9); ;// pop     r9
        //剩余寄存器数量
        REX_W mov    eax, _argC.dw[0];// mov     rax, qword ptr [_argC]
         
        // final stack adjustment, according to the    
        // number of arguments above 4
        //剩余参数压栈 push
        test   al, 1;         // test    al, 1
        jnz    no_adjust;     // jnz     _no_adjust
        sub    esp, 8;        // sub     rsp, 8
    no_adjust:
        push   edi;           // push    rdi
        REX_W mov    edi, restArgs.dw[0];   // mov     rdi, qword ptr [restArgs]
        // put rest of arguments on the stack         
        REX_W test   eax, eax;// test    rax, rax
        jz     _ls_e;          // je      _ls_e
        REX_W lea    edi, dword ptr[edi + 8 * eax - 8];// lea     rdi, [rdi + rax*8 - 8]
    _ls:
        REX_W test   eax, eax;     // test    rax, rax
        jz     _ls_e;              // je      _ls_e
        push   dword ptr[edi];     // push    qword ptr [rdi]
        REX_W sub    edi, 8;       // sub     rdi, 8
        REX_W sub    eax, 1;       // sub     rax, 1
        jmp    _ls;                // jmp     _ls
    _ls_e: 
        // create stack space for spilling registers   
        REX_W sub    esp, 0x20;     // sub     rsp, 20h

        call   FunctionAddresss;    // call    qword ptr [func]

        // cleanup stack                            
    REX_W mov    ecx, _argC.dw[0];  // mov     rcx, qword ptr [_argC]
    REX_W lea    esp, dword ptr[esp + 8 * ecx + 0x20];  // lea     rsp, [rsp + rcx*8 + 20h]
    
        pop    edi;                 // pop     rdi
    
        // set return value                            
        REX_W mov    _rax.dw[0], eax;// mov     qword ptr [_rax], rax

        X64_End();

        mov    ax, ds
        mov    ss, ax
        mov    esp, back_esp

        // restore FS segment
        mov    ax, back_fs
            mov    fs, ax
    }
#endif // _M_IX86

    return _rax.dw64;
}

 

32位程序下调用64位函数——进程32位模式与64位模式切换