gpt4 book ai didi

C 预处理器和 "_asm _emit"指令

转载 作者:太空宇宙 更新时间:2023-11-04 01:21:59 25 4
gpt4 key购买 nike

我正在尝试在 Visual Studio 2015 中实现 bool 运算以及 _asm _emit,想在 x86 程序中插入一些 x64 操作码,我必须做很多 mov 到内存命令,所以我尝试制作某种宏,它获取地址并以小端方式发出:

#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);
}

有没有什么方法可以使用预处理器内联汇编来实现非常量值的发射?

最佳答案

您无需在 32 位进程中乱用 64 位代码来访问 64 位进程环境 block 。您可以使用 32 位代码获取其地址,并且它位于 32 位地址空间内。如果您需要访问在 32 位地址空间之外分配的内存,您只需要使用 64 位代码,我认为 Windows 永远不会在 32 位进程中这样做。

如果您确实需要在 32 位可执行文件中使用 64 位函数,那么有一种比使用 _asm_emit 更好的方法。要做的第一件事是用普通汇编语言编写整个 64 位函数,然后用普通的外部汇编器对其进行汇编。例如,这是一个从 64 位指针读取 MASM 语法的函数:

_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 位堆栈大小(与 64 位 near RET 指令不同)并且可以正确使用保存在堆栈。

不幸的是,我们不能直接使用 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 位代码时随时发生中断,则进程可能最终崩溃。

关于C 预处理器和 "_asm _emit"指令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40188138/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com