有没有办法将原始十六进制代码编译成C中的二进制可执行文件?

时间:2018-03-16 10:32:14

标签: assembly compilation hex nasm

我计划构建一个我想要构建的程序,为此我需要一种方法来生成随机汇编代码并对其进行修改。

我知道如何使用system()函数(C语言),我想知道是否有办法创建一个只包含原始十六进制代码的文件,然后使用system()编译它在像NASM这样的编译器中变成了二进制可执行文件。

注意:不要回答,因为我要制作另一个能满足我需求的页面......这对我来说太过笼统。 (抱歉给您带来不便......)

2 个答案:

答案 0 :(得分:4)

如果你想使用NASM处理正确的二进制可执行元数据,并格式化cruft,并且你只想生成代码的主体,你可以写入磁盘new" .asm"带有一些标题模板的文件,如:

           bits    64
global _start
_start:

然后添加新行:

    dw      0x1234
    dw      0xc3d5
    ...

将这样完整的文件存储在某些" temp1234.asm"命名,然后用NASM将其编译成linux ELF 64b二进制文件(你没有在问题中指定你的目标平台和CPU,所以我现在使用熟悉的,最常见的平台+操作系统,例如,对于其他平台,细节可能有所不同):

nasm -f elf64 temp1234.asm; ld -b elf64-x86-64 -o temp1234 temp1234.o

(使用system()执行此编译步骤)然后您也可以使用temp1234执行生成的system()二进制文件。

如果您希望生成的文件只包含您的数据,那么您可以使用C size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)将字节值直接写入打开的文件中(但不要忘记用二进制文件打开它,如FILE *f = fopen("name", "wb");),只有在你真正希望汇编器和链接器生成常见可执行文件的公共元数据(如ELF64等)时,上面生成临时ASM文件的工作才值得付出努力。 / p>

要在C中准备这样的二进制数据,您可以这样做:

#include <cstdio>
typedef unsigned short word;
void foo() {
    word payload[3] = { 0x1D35, 0xC3D5, 0xA29F };
    FILE *f = fopen("temp.exe", "wb");
    fwrite(payload, 1, sizeof(payload), f);
    fclose(f);
}

(不要运行生成的&#34; exe&#34;由此创建的文件,它不是有效的EXE二进制文件,因为它缺少DOS或Windows EXE变体文件所需的标题/元数据。这只是如何使用C代码将二进制数据写入文件的示例。

最后请注意,如果您将纯x86-16机器操作码编写到名为&#34; something.COM&#34;的文件中,它可以直接在DOS下运行,因为&#34; COM&#34;可执行文件格式是&#34;原始机器代码加载到从偏移量0x100和#34;开始的单个64k内存段中,即将单个字节0xC3写入&#34; test.com&#34;将在DOS下正确执行(只返回DOS,因为0xC3ret指令操作码。)

但是对于大多数其他目标平台,您必须在文件的正确结构化标头中生成包含多个元数据的更复杂的可执行文件,以使它们成为有效的可执行文件。这就是为什么在编写汇编代码时使用汇编程序+链接器很方便的另一个原因,不仅是从文本格式转换为机器代码,因为汇编程序+链接程序在定位特定的可执行格式时会自动生成所有这些标题/元数据你。

答案 1 :(得分:2)

_start:
    mov $1, %rax # write
    lea .foo, %rsi # text
    mov $6, %rdx # text size
    mov $1, %rdi # stdout
    syscall

    mov $60, %rax #exit
    syscall

 .foo: .ascii "Hello\n"

这里有一些汇编代码(对不起,AT&amp; T,这就是我使用的东西,无论如何你要求使用machincode。)

/tmp> as x.S -o x.o
/tmp> ld x.o -o x
ld: warning: cannot find entry symbol _start; defaulting to 0000000000400078
/tmp> ./x
Hello

所以我知道它有效......

/tmp> objdump -d x | awk 'BEGIN{ printf " _start: .byte " } /  [0-9a-f]+:/ { i=2; while( $i ~ /^[0-9a-f]{2}$/ ){ printf "0x%s, ", $i; i++ } } END{ print "" }' > y.s
/tmp> cat y.s
 _start: .byte 0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x34, 0x25, 0xa0, 0x00, 0x40, 0x00, 0x48, 0xc7, 0xc2, 0x06, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc7, 0x01, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 

这就是我如何提取机器代码并将其转换为汇编程序可读语法。最后:

/tmp> as y.s -o y.o
y.s: Assembler messages:
y.s:1: Warning: zero assumed for missing expression
/tmp> ld y.o -o y
ld: warning: cannot find entry symbol _start; defaulting to 0000000000400078
/tmp> ./y
Hello

现在用C进行。:)