假设我想写一个程序来显示“hello world”,我想用二进制写。我怎么能这样做?
我有一些想法:
- 我需要确定我使用的是什么芯片架构
- 我需要找出它使用哪种二进制文件
- 我需要一些关于这种二进制文件的参考
- 我可能需要更改编辑器中的设置(Vim)
任何人都可以帮我完成这个吗?
假设我想写一个程序来显示“hello world”,我想用二进制写。我怎么能这样做?
我有一些想法:
任何人都可以帮我完成这个吗?
它有点复杂,因为实际上打印“Hello, world!” stdout 是一个系统调用,因此您需要知道正确的内核系统调用号。这当然因操作系统而异。此外,您还需要了解二进制格式,尽管ELF(可执行和可链接格式)在少数 Unix 和 Linux 版本中是通用的,但它也往往会有所不同。
这是 Linux 汇编代码:
section .text
global _start ;must be declared for linker (ld)
_start: ;tell linker entry point
mov edx,len ;message length
mov ecx,msg ;message to write
mov ebx,1 ;file descriptor (stdout)
mov eax,4 ;system call number (sys_write)
int 0x80 ;call kernel
mov eax,1 ;system call number (sys_exit)
int 0x80 ;call kernel
section .data
msg db 'Hello, world!',0xa ;our dear string
len equ $ - msg ;length of our dear string
...在 32 位 Linux 上,编译结果为 360 字节的二进制文件,尽管它大部分是零:
00000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 |.ELF............|
00000010 02 00 03 00 01 00 00 00 80 80 04 08 34 00 00 00 |............4...|
00000020 c8 00 00 00 00 00 00 00 34 00 20 00 02 00 28 00 |........4. ...(.|
00000030 04 00 03 00 01 00 00 00 00 00 00 00 00 80 04 08 |................|
00000040 00 80 04 08 9d 00 00 00 9d 00 00 00 05 00 00 00 |................|
00000050 00 10 00 00 01 00 00 00 a0 00 00 00 a0 90 04 08 |................|
00000060 a0 90 04 08 0e 00 00 00 0e 00 00 00 06 00 00 00 |................|
00000070 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000080 ba 0e 00 00 00 b9 a0 90 04 08 bb 01 00 00 00 b8 |................|
00000090 04 00 00 00 cd 80 b8 01 00 00 00 cd 80 00 00 00 |................|
000000a0 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 21 0a 00 2e |Hello, world!...|
000000b0 73 68 73 74 72 74 61 62 00 2e 74 65 78 74 00 2e |shstrtab..text..|
000000c0 64 61 74 61 00 00 00 00 00 00 00 00 00 00 00 00 |data............|
000000d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
000000f0 0b 00 00 00 01 00 00 00 06 00 00 00 80 80 04 08 |................|
00000100 80 00 00 00 1d 00 00 00 00 00 00 00 00 00 00 00 |................|
00000110 10 00 00 00 00 00 00 00 11 00 00 00 01 00 00 00 |................|
00000120 03 00 00 00 a0 90 04 08 a0 00 00 00 0e 00 00 00 |................|
00000130 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 |................|
00000140 01 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 |................|
00000150 ae 00 00 00 17 00 00 00 00 00 00 00 00 00 00 00 |................|
00000160 01 00 00 00 00 00 00 00 |........|
由于您想“手动编译”,这基本上意味着将上面的汇编程序助记符转换为它们的操作码,然后以正确的二进制格式包装结果(上面示例中的ELF)
更新:正如 @adam-rosenfield 给出的答案所示,“Hello, world!”的 ELF 二进制文件 可以手工制作到 116 字节。原始答案现已删除,但版主仍然可以看到,所以这里有一个副本:
这是一个使用 Linux 系统调用的 32 字节版本:
.globl _start _start: movb $4, %al xor %ebx, %ebx inc %ebx movl $hello, %ecx xor %edx, %edx movb $11, %dl int $0x80 ;;; sys_write(1, $hello, 11) xor %eax, %eax inc %eax int $0x80 ;;; sys_exit(something) hello: .ascii "Hello world"
当编译成最小的 ELF 文件时,完整的可执行文件是 116 字节:
00000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 |.ELF............| 00000010 02 00 03 00 01 00 00 00 54 80 04 08 34 00 00 00 |........T...4...| 00000020 00 00 00 00 00 00 00 00 34 00 20 00 01 00 00 00 |........4. .....| 00000030 00 00 00 00 01 00 00 00 00 00 00 00 00 80 04 08 |......| 00000040 00 80 04 08 74 00 00 00 74 00 00 00 05 00 00 00 |....t.........| 00000050 00 10 00 00 b0 04 31 分贝 43 b9 69 80 04 08 31 d2 |......1.Ci..1.| 00000060 b2 0b cd 80 31 c0 40 cd 80 48 65 6c 6c 6f 20 77 |....1.@..你好 w| 00000070 6f 72 6c 64 |orld| 00000074
通常,您会为此使用十六进制编辑器。找出汇编代码,手动组装,使用十六进制编辑器输入二进制值,然后将它们保存到文件中。获得文件后,放入机器监视器并在可用地址加载文件,然后跳转到第一条指令。这在单板计算机上是相当普遍的做法,今天仍然在微控制器上完成,但这不是你要在现代操作系统上做的事情。如果您真的想这样做,我建议您运行一个低级仿真器(SIMH可以工作)或使用微控制器(您可以花不到 5 美元购买TI MSP430开发套件)。