【发布时间】:2020-02-13 12:03:59
【问题描述】:
我有一个十六进制值表示汇编指令的数组。
我想将 HelloWorld 数组作为 printf 的参数推送。
(int)&HelloWorld 是获取我的数组地址的正确方法还是有更好的解决方案?
以及如何将 Code[] 作为函数调用?
我尝试使用函数指针,但程序跳转到内存中的其他位置并最终在无效指令处崩溃。
这是我的代码:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <errno.h>
int main()
{
int HelloWorld[15] =
{
// Hello World
0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x00
};
int Code[50] =
{
// PUSH [DWORD PTR] DS:<address of array HelloWorld>
0xFF, 0x35, (int)&HelloWorld,
// CALL <address of function printf>
0xE8, (int)&printf,
// ADD esp, 4
0x83, 0xC4, 0x04,
// RET
0xC3
};
long unsigned int ProtectionCode = 0;
// Change protection to read, write, execute
int ChangeProtect = VirtualProtect(Code, 50, PAGE_EXECUTE_READWRITE,
&ProtectionCode);
if(ChangeProtect == 0)
{
printf("Error: %s\n", strerror(errno));
return 1;
}
void (*CodePtr)() = &Code;
(*CodePtr)();
return 0;
}
感谢您的帮助。
【问题讨论】:
-
“我有一个十六进制值表示汇编指令的数组。” -- 汇编指令不应该是一堆整数。它们应该以包含指令的字符串形式出现,例如
"mov eax, 3"。然后通过使用asm关键字运行。您尝试的任何类似函数指针转换的行为都是未定义的行为,并且只有在您的编译器文档说明您可以这样做时才能可靠地工作。 -
假设您使用 x86_32 或 x86_64:问题是,确实在内存中“0xff,0x35”存储为“ff 00 00 00 35 00 00 00 ...”,而您想要有“ff 35 ...”。当您尝试使用机器代码创建缓冲区时,您必须更加小心。您还可以尝试将缓冲区写入文件并使用 objdump 对其进行反汇编以验证其格式是否正确。否则,函数调用看起来没问题。
-
这个问题重复了 Stack Overflow 上的其他问题;不时询问如何执行包含机器指令的数据。首先,答案取决于平台,因此您必须指定您使用的操作系统。其次,你必须得到正确的机器码。
int的数组,或者最好是unsigned int,可以用于具有固定指令大小的机器,但是对于 Intel,unsigned char的数组是更好的方法。第三,您需要调用操作系统例程来更改页面映射并刷新指令缓存。 -
这个问题确实比那些原版有更多的问题,所以可以重新打开它来解决这些问题。但它们是基本错误,可能应该单独讨论。使用
(uintptr_t)&HelloWorld是获取地址以供动态准备的指令使用的一种方法,但可能还有其他方法。但是,对于 x86 架构,必须将指令流写入为字节序列,而不是int,并且必须将地址(或偏移量或使用的任何内容)作为字节而不是整体复制到该序列中int或uintptr_t.