几个月前我小小的研究了在WOW64下的32位进程中运行native x64代码。

第二个设想是在64位进程下运行x86代码。它们都是可以的,如我google的一样,

已经有人在使用这两种方法了:

当我研究的时候还没有看上面搜索到的结果,所以下面仅仅代表我自己的见解;)

x86 <-> x64 Transition(x86和x64之间的转换)

最早的来检查x86到x64转换的方法是观察windows中32位版本和64位版本的ntdll.dll中的任意syscall:

32-bits ntdll from Win7 x86 32-bits ntdll from Win7 x64
mov     eax, X

mov     edx, 7FFE0300h
call dword ptr [edx]
;ntdll.KiFastSystemCall retn Z
mov     eax, X
mov ecx, Y
lea edx, [esp+4]
call dword ptr fs:[0C0h]
;wow64cpu!X86SwitchTo64BitMode
add esp, 4
ret Z
正如你所见,在64位系统上新的call
fs:[0xC0](wow64cpu!X86SwitchTo64BitMode) 代替了标准call ntdll.KiFastSystemCall. 
             

wow64cpu!X86SwitchTo64BitMode  执行了一个简单的远跳转到64位的段中了:

	wow64cpu!X86SwitchTo64BitMode:
748c2320 jmp 0033:748C271E ;wow64cpu!CpupReturnFromSimulatedCode

这就是64位Windows系统上转换x64和x86后面的魔术。

此外他也能在非WoW64进程中运行(标准的native 64位应用程序),所以32位代码也能运行在64位应用程序中。

总结一下,运行在64位Windows中的每个进程(x86和x64),都分配了两个代码段:

  • cs = 0×23 -> x86 mode
  • cs = 0×33 -> x64 mode

Running x64 code inside 32-bits process(在32位进程中运行x64代码)

首先,我准备了一些宏,将用它来标记64位代码的开始和结尾:

#define EM(a) __asm __emit (a)

#define X64_Start_with_CS(_cs) \
{ \
EM(0x6A) EM(_cs) /* push _cs */ \
EM(0xE8) EM(0) EM(0) EM(0) EM(0) /* call $+5 */ \
EM(0x83) EM(4) EM(0x24) EM(5) /* add dword [esp], 5 */ \
EM(0xCB) /* retf */ \
} #define X64_End_with_CS(_cs) \
{ \
EM(0xE8) EM(0) EM(0) EM(0) EM(0) /* call $+5 */ \
EM(0xC7) EM(0x44) EM(0x24) EM(4) /* */ \
EM(_cs) EM(0) EM(0) EM(0) /* mov dword [rsp + 4], _cs */ \
EM(0x83) EM(4) EM(0x24) EM(0xD) /* add dword [rsp], 0xD */ \
EM(0xCB) /* retf */ \
} #define X64_Start() X64_Start_with_CS(0x33)
#define X64_End() X64_End_with_CS(0x23)

执行完X64_Start()宏后,CPU直接转换到x64模式,执行完X64_End()宏后立即回到x86模式。

由于远返回的opcode,以上宏都是位置独立的。

能够调用x64版本的APIs是非常有用的。我尝试加载过x64版本的kernel32.dll,他不是一个微不足道的任务,

并且我失败了,所以我需要坚持使用Native API。x64版本的kernel32.dll的主要问题是在已经加载x86版本的

kernel32.dll的情况下,x64 kernel32.dll 有一些额外的检查来阻止正常的加载。我相信通过一些猥琐的hook

来拦截kernel32!BaseDllInitialize能达到目的,但是这是非常复杂的任务。当我开始研究的时候,我是在WIndows

Vista上,并且我能加载(用一些hacks)64位版本的kernel32和user32库,但是他们没有完整的功能,同时我又

转换到Windows7,使用在Vista上的方法不能够正常工作了。

让我们回到主题上,为了使用Native APIs,我需要定位内存中你给的x64版本的ntdll.dll。为了完成这个任务,

我需要解析_PEB_LDR_DATA结构中的InLoadOrderModuleList。64位的_PEB被64位的_TEB包含,并且64位_TEB

类似于x86平台的(在x64上我们需要使用gs段代替fs):

mov   eax, gs:[0x30]

他甚至可以更简单,因为 wow64cpu!CpuSimulate(负责转换CPU到x86模式的函数)将gs:[0x30]的值移动到r12寄存器中,

所以我们的getTEB64()版本看起来像这样:

//to fool M$ inline asm compiler I'm using 2 DWORDs instead of DWORD64
//use of DWORD64 will generate wrong 'pop word ptr[]' and it will break stack
union reg64
{
DWORD dw[2];
DWORD64 v;
}; //macro that simplifies pushing x64 registers
#define X64_Push(r) EM(0x48 | ((r) >> 3)) EM(0x50 | ((r) & 7)) WOW64::TEB64* getTEB64()
{
reg64 reg;
reg.v = 0; X64_Start();
//R12 register should always contain pointer to TEB64 in WoW64 processes
X64_Push(_R12);
//below pop will pop QWORD from stack, as we're in x64 mode now
__asm pop reg.dw[0]
X64_End(); //upper 32 bits should be always 0 in WoW64 processes
if (reg.dw[1] != 0)
return 0; return (WOW64::TEB64*)reg.dw[0];
}

WOW64名字空间定义在"os_structs.h"文件中,随后将会和其他示例代码添加到文章尾部。

负责定位64位ntdll.dll函数定义如下:

DWORD getNTDLL64()
{
static DWORD ntdll64 = 0;
if (ntdll64 != 0)
return ntdll64; WOW64::TEB64* teb64 = getTEB64();
WOW64::PEB64* peb64 = teb64->ProcessEnvironmentBlock;
WOW64::PEB_LDR_DATA64* ldr = peb64->Ldr; printf("TEB: %08X\n", (DWORD)teb64);
printf("PEB: %08X\n", (DWORD)peb64);
printf("LDR: %08X\n", (DWORD)ldr); printf("Loaded modules:\n");
WOW64::LDR_DATA_TABLE_ENTRY64* head = \
(WOW64::LDR_DATA_TABLE_ENTRY64*)ldr->InLoadOrderModuleList.Flink;
do
{
printf(" %ws\n", head->BaseDllName.Buffer);
if (memcmp(head->BaseDllName.Buffer, L"ntdll.dll",
head->BaseDllName.Length) == 0)
{
ntdll64 = (DWORD)head->DllBase;
}
head = (WOW64::LDR_DATA_TABLE_ENTRY64*)head->InLoadOrderLinks.Flink;
}
while (head != (WOW64::LDR_DATA_TABLE_ENTRY64*)&ldr->InLoadOrderModuleList);
printf("NTDLL x64: %08X\n", ntdll64);
return ntdll64;
}

为了完整支持x64 Native API调用,我们还需要等价于GetProcAddress的函数,通过ntdll!LdrGetProcedureAddress更容易

的交流。下面代码负责获取LdrGetProcedureAddress的地址:

DWORD getLdrGetProcedureAddress()
{
BYTE* modBase = (BYTE*)getNTDLL64();
IMAGE_NT_HEADERS64* inh = \
(IMAGE_NT_HEADERS64*)(modBase + ((IMAGE_DOS_HEADER*)modBase)->e_lfanew);
IMAGE_DATA_DIRECTORY& idd = \
inh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
if (idd.VirtualAddress == 0)
return 0; IMAGE_EXPORT_DIRECTORY* ied = \
(IMAGE_EXPORT_DIRECTORY*)(modBase + idd.VirtualAddress); DWORD* rvaTable = (DWORD*)(modBase + ied->AddressOfFunctions);
WORD* ordTable = (WORD*)(modBase + ied->AddressOfNameOrdinals);
DWORD* nameTable = (DWORD*)(modBase + ied->AddressOfNames);
//lazy search, there is no need to use binsearch for just one function
for (DWORD i = 0; i < ied->NumberOfFunctions; i++)
{
if (strcmp((char*)modBase + nameTable[i], "LdrGetProcedureAddress"))
continue;
else
return (DWORD)(modBase + rvaTable[ordTable[i]]);
}
return 0;
}

为了锦上添花,我将介绍有用的函数,能让我在x86的C/C++代码中直接的调用x64 Native APIs:

DWORD64 X64Call(DWORD func, int argC, ...)
{
va_list args;
va_start(args, argC);
DWORD64 _rcx = (argC > 0) ? argC--, va_arg(args, DWORD64) : 0;
DWORD64 _rdx = (argC > 0) ? argC--, va_arg(args, DWORD64) : 0;
DWORD64 _r8 = (argC > 0) ? argC--, va_arg(args, DWORD64) : 0;
DWORD64 _r9 = (argC > 0) ? argC--, va_arg(args, DWORD64) : 0;
reg64 _rax;
_rax.v = 0; DWORD64 restArgs = (DWORD64)&va_arg(args, DWORD64); //conversion to QWORD for easier use in inline assembly
DWORD64 _argC = argC;
DWORD64 _func = func; DWORD back_esp = 0; __asm
{
;//keep original esp in back_esp variable
mov back_esp, esp ;//align esp to 8, without aligned stack some syscalls
;//may return errors !
and esp, 0xFFFFFFF8 X64_Start(); ;//fill first four arguments
push _rcx
X64_Pop(_RCX);
push _rdx
X64_Pop(_RDX);
push _r8
X64_Pop(_R8);
push _r9
X64_Pop(_R9); push edi push restArgs
X64_Pop(_RDI); push _argC
X64_Pop(_RAX); ;//put rest of arguments on the stack
test eax, eax
jz _ls_e
lea edi, dword ptr [edi + 8*eax - 8] _ls:
test eax, eax
jz _ls_e
push dword ptr [edi]
sub edi, 8
sub eax, 1
jmp _ls
_ls_e: ;//create stack space for spilling registers
sub esp, 0x20 call _func ;//cleanup stack
push _argC
X64_Pop(_RCX);
lea esp, dword ptr [esp + 8*ecx + 0x20] pop edi ;//set return value
X64_Push(_RAX);
pop _rax.dw[0] X64_End(); mov esp, back_esp
}
return _rax.v;
}

函数有一点长,但是有注释,并且整个想法也是非常简单的。第一个参数是我们想调用的x64函数地址,第二个参数是指定函数

需要的参数个数,其他的参数依赖于被调用的函数,所有的参数都应该转换成DWORD64。调用X64Call的一个小例子:

DWORD64 GetProcAddress64(DWORD module, char* funcName)
{
static DWORD _LdrGetProcedureAddress = 0;
if (_LdrGetProcedureAddress == 0)
{
_LdrGetProcedureAddress = getLdrGetProcedureAddress();
printf("LdrGetProcedureAddress: %08X\n", _LdrGetProcedureAddress);
if (_LdrGetProcedureAddress == 0)
return 0;
} WOW64::ANSI_STRING64 fName = { 0 };
fName.Buffer = funcName;
fName.Length = strlen(funcName);
fName.MaximumLength = fName.Length + 1;
DWORD64 funcRet = 0;
X64Call(_LdrGetProcedureAddress, 4,
(DWORD64)module, (DWORD64)&fName,
(DWORD64)0, (DWORD64)&funcRet); printf("%s: %08X\n", funcName, (DWORD)funcRet);
return funcRet;
}

Running x86 code inside 64-bits process(在64位进程中运行x86代码)

X86_Start MACRO
LOCAL
xx, rt
call $+5
xx equ $
mov dword ptr [rsp + 4], 23h
add dword ptr [rsp], rt - xx
retf
rt:
ENDM X86_End MACRO
db 6Ah, 33h ; push 33h
db 0E8h, 0, 0, 0, 0 ; call $+5
db 83h, 4, 24h, 5 ; add dword ptr [esp], 5
db 0CBh ; retf
ENDM

Ending notes

文章中使用到的源码链接:

http://download.csdn.net/detail/u014249041/7074553

http://download.csdn.net/detail/u014249041/7074555

原文地址:

http://blog.rewolf.pl/blog/?p=102#.UysCxM4pCqS

Mixing x86 with x64 code (混合编写x86和x64代码)的更多相关文章

  1. FreeBSD上编写x86 Shellcode初学者指南

    FreeBSD上编写x86 Shellcode初学者指南 来源 https://www.4hou.com/binary/14375.html 介绍 本教程的目的是帮助你熟悉如何在FreeBSD操作系统 ...

  2. Qt5 Addin 出现问题模块计算机类型“x64”与目标计算机类型“X86”冲突

    Qt5 Addin 出现问题   怎样VS2013下安装Qt5的插件   http://jingyan.baidu.com/article/a948d65159d8890a2dcd2e84.html ...

  3. 模块计算机类型“X64”与目标计算机类型“x86”冲突

    问题描述:在X64 平台上开发dll 文件,在生成dll时Vs 2010 出现如下错误 :"fatal error LNK1112: 模块计算机类型"X64"与目标计算机 ...

  4. 在VS Code中编写IAR项目

    在VS Code中编写IAR项目 首先按照网上的教程,下载C/C++插件,以及IAR Eebedded Workbench插件,安装完成重启VS Code. 项目目录下新建.vscode文件夹,并新建 ...

  5. Visual Studio Code如何编写运行C、C++

    Visual Studio Code如何编写运行C.C++ 作者:知乎用户链接:https://www.zhihu.com/question/30315894/answer/154979413来源:知 ...

  6. VS Code中编写C

    Visual Studio Code如何编写运行C.C++? Visual Studio Code的C/C++扩展功能 vscode配置C/C++的编译调试环境

  7. 02 How to Write Go Code 如何编写go语言代码

    How to Write Go Code   如何编写go语言代码 Introduction   介绍 Code organization  组织代码 Overview  概述 Workspaces  ...

  8. 使用Visual Studio Code编写和激活ABAP代码 (上)

    猪年春节后的第一篇,Jerry祝各位猪年大吉! 2019年的六分之一马上就快过完了,不知道大家在新的一年是否给自己定了新的小目标呢?这里Jerry先预祝大家到2019年年底的时候,在年初制定的小目标都 ...

  9. 使用VS Code快速编写HTML

    VS Code 有自动补全HTML代码方法体的功能 1.打开VS Code并新建文件,点击底部右侧语言模式选项,默认为纯文本(plaintext),将其改为HTML. 2.在空文件第一行输入”!“,光 ...

随机推荐

  1. Linux 小知识翻译 - 「服务器」

    这次聊聊 「服务器」 这个词. 可能会觉得为什么「突然问这个?」.接下来请先考虑一下下面的题目. A) 「Web服务器是指提供网页数据的软件」 B) 「Web服务器是指运行上述软件的硬件」 那么,究竟 ...

  2. Unity基础6 Shadow Map 阴影实现

    这篇实现来的有点墨迹,前前后后折腾零碎的时间折腾了半个月才才实现一个基本的shadow map流程,只能说是对原理理解更深刻一些,但离实际应用估计还需要做很多优化.这篇文章大致分析下shadow ma ...

  3. C++_调用约束

    1.要求 声明定义处调用约定必须相同 int __stdcall add(int a, int b); int __stdcall add(int a, int b) {  return a + b; ...

  4. [福大软工] Z班 团队作业——随堂小测(同学录) 作业成绩

    团队作业--随堂小测(同学录) 作业链接 http://www.cnblogs.com/easteast/p/7763645.html 作业情况 本次作业从原先预计的3小时,有些组打了鸡血连续肝了4. ...

  5. css样式的书写顺序及原理

    刚开始学习前端的时候,每次写css样式都是用到什么就在样式表后添加什么,完全没有考虑到样式属性的书写顺序对网页加载代码的影响.后来逐渐才知道正确的样式顺序不仅易于查看,并且也属于css样式优化的一种方 ...

  6. centos7 mysql5.7安装

    环境:centos7.4 mysql:5.7 安装方式yum安装: wget https://dev.mysql.com/get/mysql57-community-release-el7-9.noa ...

  7. 008_python列表的传值与传址

    一. 今天发现一个奇怪的现象,代码如下: aList = ['xyz', 'zara', 'abc', 'xyz','xysdfji','xywooudd'] for x in aList: if x ...

  8. 获取数值型数组中大于60的元素个数,给数值型数组中不足60分的加20分。(数组,for循环,if条件判断语句)

    package com.Summer_0420.cn; /** * @author Summer * 获取数值型数组中大于60的元素个数 * 给数值型数组中不足60分的加20分 */ public c ...

  9. 根据成绩输出对应的等级(使用if多分支和switch语句分别实现)

    根据成绩输出对应的等级,使用if多分支和switch语句分别实现. a)        A级   [90,100] b)        B级   [80,90) c)        C级   [70, ...

  10. 《Python神经网络编程》中文版PDF+英文版PDF+源代码,业界良心书

    下载:https://pan.baidu.com/s/1hI6wMPq4UFvEmpgF3ZV1jg 关于内容 这本书主要揭示神经网络背后的概念,并介绍如何通过Python实现神经网络.全书主要讲了三 ...