【问题标题】:Is there a way to compile raw hex code into a binary executable in C?有没有办法将原始十六进制代码编译成 C 中的二进制可执行文件?
【发布时间】:2018-08-25 09:24:19
【问题描述】:

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

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

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

【问题讨论】:

  • ... a file that contains only a raw hex code and then use system() to compile it in a compiler ... 我认为我们不编译原始十六进制,但我们将汇编代码编译成十六进制
  • 不...我认为我们将程序集编译成十六进制,然后编译成机器语言...您的计算机无法读取十六进制...
  • 除非您正在为某些嵌入式系统编程或使用 Z80(或真正的旧系统),否则现代汇编程序通常会获取汇编文件并将其直接汇编成机器代码。或者它们组装成一个链接到最终可执行文件的目标文件。
  • 我不确定有什么工具可以将文本“1234ABFE”转换为二进制 4 字节(但我很确定 GNU 世界有这样的工具可用),但如果你已经在C语言中,并且愿意使用system来执行这样的工具,为什么不自己编写转换,就像4-liner ...并自己编写二进制文件。 (关于“16 位汇编命令” - 没关系,二进制文件包含分组为字节的位,即如果您使用 hexa 查看器检查输出文件,您将看到 35 1D D5 C3 ... 字节(或者如果您将切换显示到 WORD,然后你会看到你的)。
  • strtoul 将转换 ASCII base 16,或者上帝禁止你使用 *scanf()。以 16 为基数,转换起来有些简单,一个简单的查找表将用于每个半字节、移位和或、移位和或。

标签: assembly compilation hex nasm


【解决方案1】:

如果您想使用 NASM 来处理正确的二进制可执行元数据,并格式化杂乱无章的代码,并且您只想生成代码主体,您可以使用一些标头模板将新的“.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() 执行此编译步骤)然后您也可以使用system() 执行生成的temp1234 二进制文件。


如果您希望生成的文件只包含您的数据,那么您可以使用 C size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) 将字节值直接写入打开的文件(但不要忘记使用二进制文件打开它,例如 FILE *f = fopen("name", "wb");) ,只有当您实际上希望汇编器和链接器也生成常见可执行文件的公共元数据(如 ELF64 等)时,上述生成临时 ASM 文件的解决方法才值得努力......

要在 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);
}

(不要运行由此创建的“exe”文件,它不是要执行的有效 EXE 二进制文件,因为它缺少 DOS 或 Windows EXE 变体文件所需的标头/元数据。这只是示例用 C 代码将二进制数据写入文件)。


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

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

【讨论】:

  • 我想我找到了另一个更紧凑的解决方案......告诉我它是否会工作,因为我现在正在做作业......如果打印到一个文本文件中的字母与汇编操作码作为它们的 ascii 值和将 .txt 更改为 .exe
  • @H3X3R 这不是“.exe”文件的工作方式。说真的,退后一步,尝试找到一些关于磁盘上实际计算机文件包含什么的教程/解释,然后使用一些 hexa 查看器查看一些 ascii 文件(如 C/ASM 源)与生成的 EXE 二进制文件,并保留请注意,它们都在磁盘本身上存储为位(以二进制方式),它的查看器将这些数据格式化为十六进制视图或 ASCII 文本视图。然后像 NASM 这样的汇编程序将读取 ASCII 编码的二进制“.asm”文件,将这些 ASCII 字节解析为字符串,解码它们,翻译成机器码 + 写入 .obj
【解决方案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&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 来做。:)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-12
    • 2018-01-19
    • 2015-02-14
    • 1970-01-01
    • 2010-10-22
    • 2022-08-11
    • 2011-09-03
    • 1970-01-01
    相关资源
    最近更新 更多