【问题标题】:How to make a C program that can run x86 hex codes如何制作可以运行 x86 十六进制代码的 C 程序
【发布时间】:2012-06-30 10:46:54
【问题描述】:

我有一组可以转换为汇编指令的十六进制代码,我想用 C 语言创建可以执行这些代码的程序。

unsigned char rawData[5356] = {
    0x4C, 0x01, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x0C, 0x00, 0x00,
    0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x2E, 0x74, 0x65, 0x78,
    0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0xB4, 0x05, 0x00, 0x00, 0xA4, 0x01, 0x00, 0x00, 0x68, 0x08, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x20, 0x00, 0x30, 0x60,
    0x2E, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x40, 0x00, 0x30, 0xC0, 0x2E, 0x62, 0x73, 0x73, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x30, 0xC0, 0x2F, 0x34, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x14, 0x00, 0x00, 0x00, 0x58, 0x07, 0x00, 0x00, 0x32, 0x0C, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x10, 0x30, 0x60,
    0x2F, 0x33, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x6C, 0x07, 0x00, 0x00,...and so on

【问题讨论】:

  • 这是作业吗?这不是一个真正的问题,而是一个任务。您应该告诉我们您已经尝试过什么以及您迷路的地方。
  • 您是否尝试过声明一个指向 rawData 的函数指针(请注意调用约定)然后调用该函数???此外,由于 DEP(只需禁用它),Windows(如果您使用的是 Windows)可能会拒绝此操作。
  • 不要循环数组,分配给数组的内存是程序本身(就像一个指向普通函数的指针)。只需调用一次。要禁用 DEP...Google,Windows 版本之间的过程略有不同。
  • 要启用直接执行,您应该分配数组并将页面标记为可执行。使用可行的解决方案在下面查看我的答案

标签: c x86 inline-assembly


【解决方案1】:

使用 x86 是可能的。

这是一个小样本。分配具有写入/执行权限的页面并将您的操作码复制到那里。

#ifdef _WIN32
#include <windows.h>
#else
#include <sys/mman.h>
#include <unistd.h>
#endif


int main(){
    char opcodes[] = { ..... }; 

    #ifdef _WIN32

    HANDLE mem_handle = CreateFileMappingA( INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0,  length, NULL);

    void* mem_map = MapViewOfFile( mem_handle, FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE, 0x0, 0x0, length);

    #else // posix
    void* mem_map = mmap(NULL, sizeof(opcodes), PROT_EXEC|PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
    #endif

    memcpy(mem_map, opcodes, sizeof(opcodes));

    (( void(*)() )mem_map)();

    return 0;
}

对于 POSIX 系统,使用 mmap() 调用。

还可以阅读有关蹦床的信息。见链接:http://pages.cs.wisc.edu/~weinrich/papers/method_dispatch.pdf

你没有说这是一个完整的程序还是一个单一的功能。相对/绝对寻址可能存在问题。

小备注:此代码也适用于启用 MMU 的 PowerPC 和 ARM。

【讨论】:

  • +1 不错的解决方案!我猜它不是一个可执行文件,而只是一个函数,它不以任何已知标记开头。
【解决方案2】:

声明一个函数指针,然后调用该函数。

void (*f)(void) = (void (*)(void)) rawData;
f();

当然,这是未定义的行为,不能保证有效。

【讨论】:

  • 谢谢,所以我只是循环遍历数组
  • 这就是为什么他们称 C 比汇编程序更上一层楼!
  • 是的,这是正确的。然而,也有可能的并发症。 1)程序集是否真的遵循C调用约定(你可能可以调用它,但它可能不会回来)。 2)您可能无法做到这一点,因为现代系统不赞成将表面上“只是数据”的内容转换为可执行代码。数组可能所在的数据段可能被标记为不可执行。这取决于各种因素(平台、操作系统、编译和链接选项等)
  • 我想知道这是否可以用于在 C 中实现粗略反射。您从程序的二进制文件中读取函数的操作码并手动编辑操作码,然后使用该函数执行修改后的函数指针……嗯。
  • @WillHartung:“现代系统不赞成将表面上的“只是数据”转换为可执行代码。”我说如果你准备好处理复杂性,所有数据都应该可以用作代码。再说一次,我真的很喜欢具有高度反身性的语言。
【解决方案3】:

在某些平台上,您不能只声明:

void (*f)(void) = (void (*)(void)) rawData;

试试

f(); 

运行十六进制代码。

因为数据页可能无法执行。在不关心函数内容的情况下定义函数的一种便捷方法是将.s 文件添加到您的项目中。

用 GNU as 编译它,并将它的目标文件链接到您的最终程序。

例如:

ma​​in.c

int main()
{
    helloasm();
    return 0;
}

x.s

代码像 C 语句一样工作:printf("Hello ASM\n"); exit(11);

.global helloasm
helloasm:
.byte 0x48, 0xc7, 0xc7, 0x01, 0x00, 0x00, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c
.byte 0x6f, 0x20, 0x41, 0x53, 0x4d, 0x21, 0x0a, 0x5e, 0x48, 0xc7, 0xc2, 0x0b, 0x00, 0x00, 0x00, 0xb8
.byte 0x01, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xbf, 0x0b, 0x00, 0x00, 0x00, 0xb8, 0x3c, 0x00, 0x00, 0x00
.byte 0x0f, 0x05

编译并运行

as x.s -o x.o
gcc main.c x.o -o main
./main
Hello ASM!

另外,如果您的十六进制代码数组在二进制文件中,例如 a.bin

hexdump -C a.bin
00000000  48 c7 c7 01 00 00 00 e8  0b 00 00 00 48 65 6c 6c  |H...........Hell|
00000010  6f 20 41 53 4d 21 0a 5e  48 c7 c2 0b 00 00 00 b8  |o ASM!.^H.......|
00000020  01 00 00 00 0f 05 bf 0b  00 00 00 b8 3c 00 00 00  |............<...|
00000030  0f 05                                             |..|
00000032

那么你的 x.s 可能是:

.global helloasm
helloasm:
.incbin "a.bin"

【讨论】:

    【解决方案4】:

    查看头文件elf.h。

    您需要使用 OPCodes 完成这些结构中的字段。

    在 x86 中有一个加载可执行文件的协议,否则在链接器将控制权传递给加载的代码后,它会崩溃。

    看看这里如何创建一个有效的可执行文件:

    http://bellard.org/otcc/otccelfn.c

    【讨论】:

      【解决方案5】:

      您不能直接将它们输出到文件中,然后使用system() 调用吗?这样您就不必担心该数组是否遵循 C 调用约定。

      【讨论】:

      • 只有当十六进制代码形成一个可正确执行的程序而不只是一个函数时才有效。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-04-15
      • 1970-01-01
      • 2015-03-21
      • 2014-07-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多