组装结束后附有说明
Instructions appended to end of assembly
我正在尝试按照 this 教程创建二进制文件,但链接器似乎在程序集末尾附加了额外的指令。我假设这是 OS 的拆卸过程。
本教程尝试在 Linux:
上编译基本的 32 位 C 程序
int main() {
}
使用这些命令:
gcc -c test.c
ld -o test -Ttext 0x0 -e main test.o
objcopy -R .note -R .comment -S -O binary test test.bin
ndisasm -b 32 test.bin
我是运行64位Linux,因此修改编译步骤如下:
gcc -m32 -c test.c
ld -m elf_i386 -o test -Ttext 0x0 -e main test.o
objcopy -R .note -R .comment -S -O binary test test.bin
ndisasm -b 32 test.bin
预期输出为:
00000000 55 push ebp
00000001 89E5 mov ebp,esp
00000003 C9 leave
00000004 C3 ret
我的输出如下:
;; START expected output
00000000 55 push bp
00000001 89E5 mov bp,sp
00000003 5D pop bp
00000004 C3 ret
;; END expected output
00000005 0000 add [eax],al
00000007 001400 add [eax+eax],dl
0000000A 0000 add [eax],al
0000000C 0000 add [eax],al
0000000E 0000 add [eax],al
00000010 017A52 add [edx+0x52],edi
00000013 0001 add [ecx],al
00000015 7C08 jl 0x1f
00000017 011B add [ebx],ebx
00000019 0C04 or al,0x4
0000001B 0488 add al,0x88
0000001D 0100 add [eax],eax
0000001F 001C00 add [eax+eax],bl
00000022 0000 add [eax],al
00000024 1C00 sbb al,0x0
00000026 0000 add [eax],al
00000028 D8FF fdivr st7
0000002A FF db 0xff
0000002B FF0500000000 inc dword [dword 0x0]
00000031 41 inc ecx
00000032 0E push cs
00000033 088502420D05 or [ebp+0x50d4202],al
00000039 41 inc ecx
0000003A C50C04 lds ecx,[esp+eax]
0000003D 0400 add al,0x0
0000003F 00 db 0x00
附加指令的用途是什么?如何从目标文件和二进制文件中删除它们?
编辑:
objcopy
参数中的拼写错误(commet -> comment)。更新了反汇编输出。
通常,当您在输出文件中看到额外的 data/instructions 时,问题的根源可能是出现在预期代码之后的部分。处理此问题的一种方法是查询 ELF 可执行文件以查看它定义了哪些部分。可以只查询带有 -x
参数到 OBJDUMP 的部分。使用此命令:
objdump -x test
应该在使用默认参数的 GCC 的大多数现代版本中产生与此类似(不完全)的输出:
test: file format elf32-i386
test
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x00000000
Program Header:
LOAD off 0x00001000 vaddr 0x00000000 paddr 0x00000000 align 2**12
filesz 0x00000040 memsz 0x00000040 flags r-x
STACK off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**4
filesz 0x00000000 memsz 0x00000000 flags rw-
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000005 00000000 00000000 00001000 2**0
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .eh_frame 00000038 00000008 00000008 00001008 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .comment 0000001d 00000000 00000000 00001040 2**0
CONTENTS, READONLY
SYMBOL TABLE:
00000000 l d .text 00000000 .text
00000008 l d .eh_frame 00000000 .eh_frame
00000000 l d .comment 00000000 .comment
00000000 l df *ABS* 00000000 test.c
00001040 g .eh_frame 00000000 __bss_start
00000000 g F .text 00000005 main
00001040 g .eh_frame 00000000 _edata
00001040 g .eh_frame 00000000 _end
首先应该寻找意想不到的部分。在输出到文件 test.bin
之前,您的 OBJCOPY 命令使用 -R
从 ELF 对象中删除部分。你做到了:
objcopy -R .note -R .comment -S -O binary test test.bin
如果我们排除 .note
和 .comment
部分,上面 OBJDUMP 输出中明显剩下的部分是 .eh_frame
。 .eh_frame
被放入您的文件 test.bin
中 .text
部分之后。这包含异常展开信息。这不是实际的说明。 NDISASM 将非代码转储为指令,因为二进制文件不区分代码和数据。 NDISASM盲目地将所有数据转换为指令。
有几种解决方法。您可以像排除其他两个一样排除 .eh_frame
部分。您可以使用:
objcopy -R .note -R .comment -R .eh_frame -S -O binary test test.bin
您还可以告诉 GCC 不要在代码中生成异步异常展开表。这可以通过 GCC 选项来完成:
gcc -m32 -c test.c -fno-asynchronous-unwind-tables
这与我的评论有点不同,因为我建议禁用所有例外。您只需要禁用异步展开表以抑制 .eh_frame
部分。 Whosebug answer 讨论了本节的有用性(或无用性)。 GCC 的手册页 (man gcc
) 讨论了选项 -fasynchronous-unwind-tables
-fasynchronous-unwind-tables
Generate unwind table in DWARF 2 format, if supported by target machine. The table is exact at each
instruction boundary, so it can be used for stack unwinding from asynchronous events (such as debugger or
garbage collector).
这是目前大多数 GCC 的默认设置。使用 -fno-asynchronous-unwind-tables
关闭此功能。
您链接到的 tutorial 是 2000 年制作的。GCC 及其选项(以及发行版使用的默认值)这些年来发生了变化。可能在创建该教程时,异步展开表尚不存在。这可以解释为什么您观察到的输出与教程不同。
我正在尝试按照 this 教程创建二进制文件,但链接器似乎在程序集末尾附加了额外的指令。我假设这是 OS 的拆卸过程。
本教程尝试在 Linux:
上编译基本的 32 位 C 程序int main() {
}
使用这些命令:
gcc -c test.c
ld -o test -Ttext 0x0 -e main test.o
objcopy -R .note -R .comment -S -O binary test test.bin
ndisasm -b 32 test.bin
我是运行64位Linux,因此修改编译步骤如下:
gcc -m32 -c test.c
ld -m elf_i386 -o test -Ttext 0x0 -e main test.o
objcopy -R .note -R .comment -S -O binary test test.bin
ndisasm -b 32 test.bin
预期输出为:
00000000 55 push ebp
00000001 89E5 mov ebp,esp
00000003 C9 leave
00000004 C3 ret
我的输出如下:
;; START expected output
00000000 55 push bp
00000001 89E5 mov bp,sp
00000003 5D pop bp
00000004 C3 ret
;; END expected output
00000005 0000 add [eax],al
00000007 001400 add [eax+eax],dl
0000000A 0000 add [eax],al
0000000C 0000 add [eax],al
0000000E 0000 add [eax],al
00000010 017A52 add [edx+0x52],edi
00000013 0001 add [ecx],al
00000015 7C08 jl 0x1f
00000017 011B add [ebx],ebx
00000019 0C04 or al,0x4
0000001B 0488 add al,0x88
0000001D 0100 add [eax],eax
0000001F 001C00 add [eax+eax],bl
00000022 0000 add [eax],al
00000024 1C00 sbb al,0x0
00000026 0000 add [eax],al
00000028 D8FF fdivr st7
0000002A FF db 0xff
0000002B FF0500000000 inc dword [dword 0x0]
00000031 41 inc ecx
00000032 0E push cs
00000033 088502420D05 or [ebp+0x50d4202],al
00000039 41 inc ecx
0000003A C50C04 lds ecx,[esp+eax]
0000003D 0400 add al,0x0
0000003F 00 db 0x00
附加指令的用途是什么?如何从目标文件和二进制文件中删除它们?
编辑:
objcopy
参数中的拼写错误(commet -> comment)。更新了反汇编输出。
通常,当您在输出文件中看到额外的 data/instructions 时,问题的根源可能是出现在预期代码之后的部分。处理此问题的一种方法是查询 ELF 可执行文件以查看它定义了哪些部分。可以只查询带有 -x
参数到 OBJDUMP 的部分。使用此命令:
objdump -x test
应该在使用默认参数的 GCC 的大多数现代版本中产生与此类似(不完全)的输出:
test: file format elf32-i386 test architecture: i386, flags 0x00000112: EXEC_P, HAS_SYMS, D_PAGED start address 0x00000000 Program Header: LOAD off 0x00001000 vaddr 0x00000000 paddr 0x00000000 align 2**12 filesz 0x00000040 memsz 0x00000040 flags r-x STACK off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**4 filesz 0x00000000 memsz 0x00000000 flags rw- Sections: Idx Name Size VMA LMA File off Algn 0 .text 00000005 00000000 00000000 00001000 2**0 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .eh_frame 00000038 00000008 00000008 00001008 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 2 .comment 0000001d 00000000 00000000 00001040 2**0 CONTENTS, READONLY SYMBOL TABLE: 00000000 l d .text 00000000 .text 00000008 l d .eh_frame 00000000 .eh_frame 00000000 l d .comment 00000000 .comment 00000000 l df *ABS* 00000000 test.c 00001040 g .eh_frame 00000000 __bss_start 00000000 g F .text 00000005 main 00001040 g .eh_frame 00000000 _edata 00001040 g .eh_frame 00000000 _end
首先应该寻找意想不到的部分。在输出到文件 test.bin
之前,您的 OBJCOPY 命令使用 -R
从 ELF 对象中删除部分。你做到了:
objcopy -R .note -R .comment -S -O binary test test.bin
如果我们排除 .note
和 .comment
部分,上面 OBJDUMP 输出中明显剩下的部分是 .eh_frame
。 .eh_frame
被放入您的文件 test.bin
中 .text
部分之后。这包含异常展开信息。这不是实际的说明。 NDISASM 将非代码转储为指令,因为二进制文件不区分代码和数据。 NDISASM盲目地将所有数据转换为指令。
有几种解决方法。您可以像排除其他两个一样排除 .eh_frame
部分。您可以使用:
objcopy -R .note -R .comment -R .eh_frame -S -O binary test test.bin
您还可以告诉 GCC 不要在代码中生成异步异常展开表。这可以通过 GCC 选项来完成:
gcc -m32 -c test.c -fno-asynchronous-unwind-tables
这与我的评论有点不同,因为我建议禁用所有例外。您只需要禁用异步展开表以抑制 .eh_frame
部分。 Whosebug answer 讨论了本节的有用性(或无用性)。 GCC 的手册页 (man gcc
) 讨论了选项 -fasynchronous-unwind-tables
-fasynchronous-unwind-tables Generate unwind table in DWARF 2 format, if supported by target machine. The table is exact at each instruction boundary, so it can be used for stack unwinding from asynchronous events (such as debugger or garbage collector).
这是目前大多数 GCC 的默认设置。使用 -fno-asynchronous-unwind-tables
关闭此功能。
您链接到的 tutorial 是 2000 年制作的。GCC 及其选项(以及发行版使用的默认值)这些年来发生了变化。可能在创建该教程时,异步展开表尚不存在。这可以解释为什么您观察到的输出与教程不同。