1. Use objdump dump ELF binary
如果想 dump 每個 section 的 binary, 可以用objdump
$ objdump --full-contents /bin/ls Contents of section .interp: 400238 2f6c6962 36342f6c 642d6c69 6e75782d /lib64/ld-linux- 400248 7838362d 36342e73 6f2e3200 x86-64.so.2. Contents of section .note.ABI-tag: 400254 04000000 10000000 01000000 474e5500 ............GNU. 400264 00000000 02000000 06000000 18000000 ................ Contents of section .note.gnu.build-id: 400274 04000000 14000000 03000000 474e5500 ............GNU. 400284 64d095bc 6589dd4b fbf1c6d6 2ae98538 d...e..K....*..8 400294 5965461b YeF. Contents of section .gnu.hash: 400298 03000000 72000000 02000000 07000000 ....r........... 4002a8 a201400c 12010c3f 28440003 a8040000 ..@....?(D...... 4002b8 72000000 75000000 7e000000 281d8c1c r...u...~...(... 4002c8 4245d5ec bbe3927c bc50769e 86f0967c BE.....|.Pv....| 4002d8 96a08997 3cad390d d871581c ce2c6372 ....<.9..qX..,cr 4002e8 e46241f5 b88df10e 39f28b1c 32c4f712 .bA.....9...2... 4002f8 ead3ef0e b3a2f712 ........
但示通常會印出一堆, 所以通常會看特定的section
可以用 objdump 找 section
$ objdump --section-headers /bin/ls /bin/ls: file format elf64-x86-64 Sections: Idx Name Size VMA LMA File off Algn 0 .interp 0000001c 0000000000400238 0000000000400238 00000238 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 1 .note.ABI-tag 00000020 0000000000400254 0000000000400254 00000254 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 2 .note.gnu.build-id 00000024 0000000000400274 0000000000400274 00000274 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 3 .gnu.hash 00000068 0000000000400298 0000000000400298 00000298 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 4 .dynsym 00000c18 0000000000400300 0000000000400300 00000300 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 5 .dynstr 00000593 0000000000400f18 0000000000400f18 00000f18 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 6 .gnu.version 00000102 00000000004014ac 00000000004014ac 000014ac 2**1 CONTENTS, ALLOC, LOAD, READONLY, DATA 7 .gnu.version_r 00000090 00000000004015b0 00000000004015b0 000015b0 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 8 .rela.dyn 000000a8 0000000000401640 0000000000401640 00001640 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 9 .rela.plt 00000a80 00000000004016e8 00000000004016e8 000016e8 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 10 .init 0000001a 0000000000402168 0000000000402168 00002168 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 11 .plt 00000710 0000000000402190 0000000000402190 00002190 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 12 .text 0000f65a 00000000004028a0 00000000004028a0 000028a0 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE ......
找到想要印的section
$ objdump --full-contents --section .interp /bin/ls /bin/ls: file format elf64-x86-64 Contents of section .interp: 400238 2f6c6962 36342f6c 642d6c69 6e75782d /lib64/ld-linux- 400248 7838362d 36342e73 6f2e3200 x86-64.so.2.
也可以指定範圍, 比如說 .interp 從 0x400238 到 0x400253
$ objdump --full-contents --start-address=0x400238 --stop-address=0x400253 /bin/ls /bin/ls: file format elf64-x86-64 Contents of section .interp: 400238 2f6c6962 36342f6c 642d6c69 6e75782d /lib64/ld-linux- 400248 7838362d 36342e73 6f2e32 x86-64.so.2
如果指定的範圍超過 section 邊界的話, 它會將跨過的 section 名字也印出來
$ objdump --full-contents --start-address=0x400238 --stop-address=0x400280 /bin/ls /bin/ls: file format elf64-x86-64 Contents of section .interp: 400238 2f6c6962 36342f6c 642d6c69 6e75782d /lib64/ld-linux- 400248 7838362d 36342e73 6f2e3200 x86-64.so.2. Contents of section .note.ABI-tag: 400254 04000000 10000000 01000000 474e5500 ............GNU. 400264 00000000 02000000 06000000 18000000 ................ Contents of section .note.gnu.build-id: 400274 04000000 14000000 03000000 ............
如果只想把 ELF 檔案當 binary 來印, 或是想印某些純 binary 檔
$ objdump --full-contents -b binary /bin/ls
2. Use objdump to disassemble file
假如有個 "hello.c", 內容如下:
// hello.c #include <stdio.h> int main() { printf("Hello World\n"); return 0; }
然後 compile : "gcc -c -g hello.c", "gcc -o hello hello.o"
可以用 objdump 反組譯
$ objdump -d hello.o hello.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <main>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: bf 00 00 00 00 mov $0x0,%edi 9: e8 00 00 00 00 callq e <main+0xe> e: b8 00 00 00 00 mov $0x0,%eax 13: 5d pop %rbp 14: c3 retq
使用 -d (--disassemble) 反組譯, 通常只會包含執行碼的 section (.text之類的)
如果想反組譯所有 section, 可以使用 -D ( --disassemble-all ), 但是會把不是程式碼的部份也當成程式碼反組譯
反組譯的輸出格式是
address <symbol> address: code byte sequence disassemble result
如果不想看到 code byte sequence
$ objdump -d --no-show-raw-insn hello.o hello.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <main>: 0: push %rbp 1: mov %rsp,%rbp 4: mov $0x0,%edi 9: callq e <main+0xe> e: mov $0x0,%eax 13: pop %rbp 14: retq
如果想看到逐行加上 symbol 的位置
$ objdump -d --prefix-address hello.o hello.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <main> push %rbp 0000000000000001 <main+0x1> mov %rsp,%rbp 0000000000000004 <main+0x4> mov $0x0,%edi 0000000000000009 <main+0x9> callq 000000000000000e <main+0xe> 000000000000000e <main+0xe> mov $0x0,%eax 0000000000000013 <main+0x13> pop %rbp 0000000000000014 <main+0x14> retq此時 code byte sequence 就會被忽略, 如果想加回code byte sequence
$ objdump -d --prefix-address --show-raw-insn hello.o hello.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <main> 55 push %rbp 0000000000000001 <main+0x1> 48 89 e5 mov %rsp,%rbp 0000000000000004 <main+0x4> bf 00 00 00 00 mov $0x0,%edi 0000000000000009 <main+0x9> e8 00 00 00 00 callq 000000000000000e <main+0xe> 000000000000000e <main+0xe> b8 00 00 00 00 mov $0x0,%eax 0000000000000013 <main+0x13> 5d pop %rbp 0000000000000014 <main+0x14> c3 retq
如果只想反組譯特定的 section
$ objdump -d --section .init hello hello: file format elf64-x86-64 Disassembly of section .init: 00000000004003e0 <_init>: 4003e0: 48 83 ec 08 sub $0x8,%rsp 4003e4: 48 8b 05 0d 0c 20 00 mov 0x200c0d(%rip),%rax # 600ff8 <_DYNAMIC+0x1d0> 4003eb: 48 85 c0 test %rax,%rax 4003ee: 74 05 je 4003f5 <_init+0x15> 4003f0: e8 3b 00 00 00 callq 400430 <__gmon_start__@plt> 4003f5: 48 83 c4 08 add $0x8,%rsp 4003f9: c3 retq反組譯的部份跟之前一樣, 也可以設定 --start-address 以及 --stop-address
如果 object file 包含 debug information, 加上 -l (--line-numbers) 可以把指令與原始碼行號對應資訊印出來, 但如果 object file 沒有 debug information 就沒有任何效果
$ objdump -d -l hello.o hello.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <main>: main(): /home/william/temp/hello.c:3 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp /home/william/temp/hello.c:4 4: bf 00 00 00 00 mov $0x0,%edi 9: e8 00 00 00 00 callq e <main+0xe> /home/william/temp/hello.c:5 e: b8 00 00 00 00 mov $0x0,%eax /home/william/temp/hello.c:6 13: 5d pop %rbp 14: c3 retq
如果加上 -S (--source), 如果找的到原始檔就會顯示對應的原始碼, 但如果 object file 沒有 debug information 就沒有作用
$ objdump -d -S hello.o hello.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <main>: #include <stdio.h> int main() { 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp printf("Hello World\n"); 4: bf 00 00 00 00 mov $0x0,%edi 9: e8 00 00 00 00 callq e <main+0xe> return 0; e: b8 00 00 00 00 mov $0x0,%eax } 13: 5d pop %rbp 14: c3 retq-S 與 -l 可以同時使用
其中可以注意的是, 在 printf 下面的 address 4, bf 00 00 00 00, 後面的位置都是00, 是因為檔案還沒 reallocate, 在 linking 之後才會嵌入位置
$ objdump -S hello ...... 000000000040052d <main>: 40052d: 55 push %rbp 40052e: 48 89 e5 mov %rsp,%rbp 400531: bf d4 05 40 00 mov $0x4005d4,%edi 400536: e8 d5 fe ff ff callq 400410 <puts@plt> 40053b: b8 00 00 00 00 mov $0x0,%eax 400540: 5d pop %rbp 400541: c3 retq 400542: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 400549: 00 00 00 40054c: 0f 1f 40 00 nopl 0x0(%rax) ......
可以看到執行檔裡已經嵌入位置 bf d4 05 40 00