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