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;
}
时间: 2024-08-02 11:04:57

32位程序下调用64位函数——进程32位模式与64位模式切换的相关文章

32位程序在64位电脑下运行,

操作的注册表写入和读取会被定位到HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node下 但部份系统注册信息在HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node下是没有的,所以就会导致32位程序在64位程序下执行出现异常.如HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography下MachineGuid在那个节点下是没有的 为了这个问题让我花了不少冤枉时间.在此记一下 32位程序在64位电脑下运行,

VS2013下的64位与32位程序配置

VS2013下的64位与32位程序配置 在Windows 7 64bit和Visual Studio 2013下生成64位程序. 新建一个Visual Studio Win32 Console项目,命名为WinTestX64. 代码如下 #include "stdafx.h" #include <iostream> using namespace std; #define PRINT(a) cout << #a << ":" &l

32位程序注入64位DLL到64位进程

向其它进程注入DLL通常的做法是通过调用CreateRemoteThread这个API在目标进程内创建一个远程线程.用这个线程来调用LoadLibraryA或LoadLibraryW(下文统称LoadLibrary)以实现让目标进程载入指定的DLL文件. 使用CreateRemoteThread创建一个远程线程须要传入一个线程过程函数的地址,而且这个函数地址是须要在目标进程中有效的. 因为LoadLibrary是kernel32.dll的导出函数.所以对于执行在同一个系统上的同为32位的进程或同

关于如何生成32位/64位程序的问题

生成32位/64位程序不是由所在操作系统的位数决定的(意思是在32位系统下生成的不一定是32位,同理在64位下生成的也不一定是64位),这主要由编译器的位数决定的. 要生成32位/64位的程序需要在编译环境中选择相应的编译器(32位/64位)即可. 这里以vs2008 (生成64位程序)为例: 在解决方案中打开配置管理器,点击选择新建解决方案平台,选择x64平台,编译生成就可以了. 如果没有该选项,则需要安装相应x64编译器

ubuntu64运行32位程序安装过程

Ubuntu运行32位程序可以使用如下方法: 第一步: 确认你有一个64位架构的内核 你可以打开终端然后输入: dpkg --print-architecture 你将会看到像下面这样的内容: amd64 这说明着你已经拥有了64位架构内核. 第二步: 确认你打开了多架构支持功能 (多架构支持可以让你在有64位库的情况下使用32位库.) 输入: dpkg --print-foreign-architectures 输出是: i386 如果你还没有多架构支持你需要打开它. 另一种第二步:打开多架构

64位系统下使用xampp,扩展spidermoney

系统:centos6.5 64位 lampp包:php-5.3.0 默认该版本的lampp集成包不支持64位操作系统,要想安装在64位系统下,得添加32位的lib环境 执行/opt/lampp/bin/php -m (基本上是缺少什么直接yum安装就行) [[email protected] opt]# ./lampp/bin/php -m-bash: ./lampp/bin/php: /lib/ld-linux.so.2: bad ELF interpreter: No such file o

x86 x64下调用约定浅析

x86平台下调用约定 我们都知道x86平台下常用的有三种调用约定,__cdecl.__stdcall.__fastcall.我们分别对这三种调用约定进行分析. __cdecl __cdecl是C/C++的默认调用约定,如果不显示声明调用约定的情况下,就是该调用约定.下面我们来从汇编层次来熟悉这种调用约定. 我写了一个函数,如下: 1 int __cdecl TestCdecl(int a, int b, int c, int d, int e) 2 { 3 return a + b + c +

微信小程序云开发之云函数创建

云函数 云函数是一段运行在云端的代码,无需管理服务器,在开发工具内编写.一键上传部署即可运行后端代码. 小程序内提供了专门用于云函数调用的 API.开发者可以在云函数内使用 wx-server-sdk 提供的 getWXContext 方法获取到每次调用的上下文(appid.openid 等),无需维护复杂的鉴权机制,即可获取天然可信任的用户登录态(openid). 1. 云函数创建 根据官网提示,创建一个云函数,命名为 add, 功能是将 a , b 两数相加,步骤如下: 在文件夹 cloud

关于32位程序在64位系统下运行中需要注意的重定向问题(有图,很清楚)

0x00 前言 最近学习了[email protected]的文章<Persistence Architecture Matters>,恰巧解决了我之前遇到过的一个问题,理清了文件和注册表重定向中需要注意的细节 大家在学习的过程中难免也会碰到,所以在此分享一下. <Persistence Architecture Matters>的链接:https://labs.mwrinfosecurity.com/blog/persistence-architecture-matters/ 0