我试图在Visual Studio 2015中与_asm _emit
一起尝试实现布尔算法,想在x86程序中插入一些x64操作码,我必须对内存做很多mov
命令,所以我尝试制作某种宏,它接收地址并以little-endian发出,所以:
#define EmitDword(x)\
{\
_asm _emit (x & 0x000000FF) \
_asm _emit ((x >> 8) & 0x000000FF) \
_asm _emit ((x >> 16) & 0x000000FF) \
_asm _emit ((x >> 24) & 0x000000FF) \
}
但我收到错误inline assembler syntax error in 'first operand';
我的想法是,将一个变量的地址传递给宏,这样它就会直接发送到机器代码中,然后我可以这样做:
#define EmitDword(x)\
{\
_asm _emit (x & 0x000000FF) \
_asm _emit ((x >> 8) & 0x000000FF) \
_asm _emit ((x >> 16) & 0x000000FF) \
_asm _emit ((x >> 24) & 0x000000FF) \
}
/* mov qword ptr [addr],reg */
#define X64_MovToMem(addr,reg)\
{\
_asm _emit 0x48\
_asm _emit 0x89\
_asm _emit reg\
_asm _emit 0x25\
EmitDword(addr)\
}
#define _rax 4
void test()
{
DWORD64 someData;
X64_MovToMem(&someData,_rax);
}
有没有办法使用预处理器内联汇编来实现非常量值的发射?
答案 0 :(得分:3)
您无需在32位进程中使用64位代码来访问64位进程环境块。您可以使用32位代码获取其地址,它位于32位地址空间内。如果需要访问在32位地址空间之外分配的内存,则只需要使用64位代码,我不认为Windows会在32位进程中执行此操作。
如果你确实需要在32位可执行文件中使用64位函数,那么使用_asm _emit
比使用更好的方法。首先要做的是用普通的汇编程序编写整个64位函数,并用普通的外部汇编程序组装它。例如,这是一个从MASM语法中的64位指针读取的函数:
_TEXT SEGMENT
__read64ptr:
mov rax, [rsp + 8]
mov eax, [rax]
mov edx, [rax + 4]
retf
_TEXT ENDS
END
这个简单的函数将64位指针作为堆栈的参数。位于指向的地址的64位值放入EAX和EDX。此函数用于使用32位远程调用指令调用。
请注意,返回值占用两个32位堆栈槽,一个用于返回地址的32位偏移,另一个用于选择器。尽管RETF指令在64位模式下执行,但默认情况下它使用32位堆栈大小(与近似RET指令的64位不同),并且可以正确使用保存在32位远端返回地址。叠加。
不幸的是,我们不能直接使用Visual Studio提供的工具来使用此程序集文件。 64位版本的MASM仅创建64位目标文件,链接器不允许我们混合使用32位和64位目标文件。应该可以使用NASM将64位代码组装成32位对象并与Microsoft的链接器链接,但是可以仅使用Microsoft的工具间接使用代码。
为此,请汇编文件并手动将机器代码复制到.text
部分中的C数组中:
#pragma code_seg(push, ".text")
#pragma code_seg(pop)
char const __declspec(allocate(".text")) _read64ptr[] = {
0x48, 0x8b, 0x44, 0x24, 0x08, /* mov rax, [rsp + 8] */
0x8b, 0x00, /* mov eax. [rax] */
0x8b, 0x50, 0x04, /* mov edx, [rax + 4] */
0xcb /* retf */
};
要调用它,您只需要使用这样的代码:
struct {
void const *offset;
unsigned short selector;
} const _read64ptr_ind = { _read64ptr, 0x33 };
unsigned long long
read64ptr(unsigned long long address) {
unsigned long long value;
_asm {
push DWORD PTR [address + 4]
push DWORD PTR [address]
call FWORD PTR [_read64ptr_ind]
add esp, 8
mov DWORD PTR [value], eax
mov DWORD PTR [value + 4], edx
}
return value;
}
使用_read64ptr_ind
的间接是必要的,因为无法在Microsoft内联汇编中编写call 33h:_read64ptr
。另请注意,在此示例中,64位代码选择器0x33
是硬编码的,希望它不会更改。
这是一个使用上面的代码从64位TEB读取64位PEB的地址的示例(即使两者都位于32位地址空间中):
unsigned long long
readgsqword(unsigned long off) {
unsigned long long value;
_asm {
mov edx, [off]
mov eax, gs:[edx]
mov edx, gs:[edx + 4]
mov DWORD PTR [value], eax
mov DWORD PTR [value + 4], edx
}
return value;
}
int
main() {
printf("32-bit TEB address %08lx\n",
__readfsdword(offsetof(NT_TIB, Self)));
printf("32-bit PEB address %08lx\n", __readfsdword(0x30));
unsigned long long teb64 = readgsqword(offsetof(NT_TIB64, Self));
printf("64-bit TEB address %016llx\n", teb64);
printf("64-bit PEB address %016llx\n", readgsqword(0x60));
printf("64-bit PEB address %016llx\n", read64ptr(teb64 + 0x60));
}
在我的计算机上运行它会生成以下输出:
32-bit TEB address 7efdd000
32-bit PEB address 7efde000
64-bit TEB address 000000007efdb000
64-bit PEB address 000000007efdf000
64-bit PEB address 000000007efdf000
正如您所看到的,可以使用32位指针访问所有结构,而不使用任何64位代码。特别是,该示例显示了如何仅使用32位代码获取到64位PEB的32位指针。
最后一点,无法保证Windows能够正确处理在32位进程中运行的64位代码。如果在执行64位代码的任何时候发生中断,则该过程可能最终崩溃。
答案 1 :(得分:1)
对不起,但你要做的事情毫无意义。
我试图回顾你是如何写这个来解决你原来的问题。但是:
我意识到有一种倾向认为'asm更快。"但请记住,C代码被转换为汇编程序。编译器已经生成了asm代码,几乎可以肯定它比你通过emit可以拼凑的东西效率更高。
如果使用64位指令将产生更好的代码(这当然是可能的),你应该构建一个64位的可执行文件。
如果您确信可以创建比C编译器更高效的asm代码,请考虑创建一个完整的asm例程,然后从C代码中调用它。请注意,您将无法将x64汇编程序链接到x86程序。