几个月前我小小的研究了在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. python中------decode解码出现的0xca问题解决方法

    一.错误: 解决方法: #源代码 data = sk.recv(1024) print(str(data,'gbk')) #修改代码 data = sk.recv(1024) print(str(da ...

  2. es6的正则扩展笔记之修饰符

    es6对于正则表达式添加了 u 修饰符和 y 修饰符. u 修饰符:含义为“Unicode模式”,用来正确处理大于\uFFFF的Unicode字符.    该修饰符不光会正确处理正则表达式,还会正确处 ...

  3. [WeChall] Training: MySQL I (MySQL, Exploit, Training)

    Training: MySQL I (MySQL, Exploit, Training) MySQL Authentication Bypass - The classic This one is t ...

  4. ZooKeeper Administrator's Guide A Guide to Deployment and Administration(吃别人嚼过的馍没意思,直接看官网资料)

    Deployment System Requirements Supported Platforms Required Software Clustered (Multi-Server) Setup ...

  5. linux命令应用之一

    某个目录下有两个文件a.txt和b.txt.文件格式为(ip username),例如: a.txt 127.0.0.1 zhangsan127.0.0.1 wangxiao127.0.0.2 lis ...

  6. C. Brutality Educational Codeforces Round 59 (Rated for Div. 2) 贪心+思维

    C. Brutality time limit per test 1 second memory limit per test 256 megabytes input standard input o ...

  7. MySQL高级知识(五)——索引分析

    前言:前面已经学习了explain(执行计划)的相关知识,这里利用explain对索引进行优化分析. 0.准备 首先创建三张表:tb_emp(职工表).tb_dept(部门表)和tb_desc(描述表 ...

  8. Django 中的 日志处理

    日志处理: 上线后必须使用 便于以后的 维护 管理 根据日志 处理 BUG 在 项目中 定义一个 存放日志的 文件夹 log 存放所有 等级 的 日志 配置: 将下面的日志的 配置 写入 django ...

  9. 4、原生jdbc链接数据库常用资源名

    原生jdbc链接数据库要素:#MySql:String url="jdbc:mysql://localhost:3306/数据库名";String name="root& ...

  10. 转发 .Net平台下ActiveMQ入门实例 https://www.cnblogs.com/madyina/p/4121458.html

    1.ActiveMQ简介 先分析这么一个场景:当我们在网站上购物时,必须经过,下订单.发票创建.付款处理.订单履行.航运等.但是,当用户下单后,立即跳转到“感谢那您的订单” 页面.不仅如此,若果没有延 ...