zoukankan      html  css  js  c++  java
  • 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
    ", NtdllModuleBase32);
        printf("NtQuerySystemInformation 32bit address:0x%08x
    ", v1);
    
    
        DWORD64 NtdllModuleBase64 = 0;
        NtdllModuleBase64 = GetModuleHandle64(L"ntdll.dll");
        DWORD64 v2 =  GetProcAddress64(NtdllModuleBase64, "NtQuerySystemInformation");
    
        printf("Ntdll.dll 32bit address:0x%016llx
    ", NtdllModuleBase64);
        printf("NtQuerySystemInformation 64bit address:0x%016llx
    ", 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;
    }
  • 相关阅读:
    一条语句简单解决“每个Y的最新X”的SQL经典问题
    SqlServer数据库字典--表.视图.函数.存储过程.触发器.主键.外键.约束.规则.sql
    SqlServer数据库字典--索引.sql
    SqlServer2005或2008数据库字典--表结构.sql
    SqlServer2000数据库字典--表结构.sql
    sqlserver附加 mdf、ldf的方法(手记)
    收缩数据库 DBCC SHRINKFILE
    日志、字段备注查询、自增ID联系设置、常用存储过程
    常用sql 全记录(添加中)
    python类的继承和多态,获取对象信息
  • 原文地址:https://www.cnblogs.com/HsinTsao/p/7270732.html
Copyright © 2011-2022 走看看