本文介紹在Fedora上對Linux核心的vmlinuz進行反彙編。如果核心是debug版本,可以用來檢視某個函數的源代碼。
1. 安裝kernel-devel軟體包
dnf -y install kernel-devel
2. 提取vmlinux
- vmlinux是一個包括Linux kernel的靜态連結的可運作檔案。
- vmlinuz是vmlinux經過gzip和objcopy制作出來的壓縮檔案。
/usr/src/kernels/$(uname -r)/scripts/extract-vmlinux /boot/vmlinuz-$(uname -r) > vmlinux
3. 反彙編vmlinux
objdump -D vmlinux > vmlinux.out
4. 檢視
vmlinux裡的函數
這裡以函數tcp4_proc_init為例。/proc/kallsyms存儲了所有的核心符号表,/boot/System.map則存儲了靜态的核心符号表。有關System.map,請閱讀這裡。
e.g.
root# grep tcp4_proc_init /proc/kallsyms
ffffffffa37dd330 t tcp4_proc_init_net
ffffffffa479d258 T tcp4_proc_init
root# grep tcp4_proc_init /boot/System.map-$(uname -r)
ffffffff817dd330 t tcp4_proc_init_net
ffffffff8279d258 T tcp4_proc_init
root# egrep -in ffffffff8279d258 vmlinux.out
8960512:ffffffff8279d258: e8 93 47 26 ff callq 0xffffffff81a019f0
8960912:ffffffff8279d96d: e8 e6 f8 ff ff callq 0xffffffff8279d258
root# N=8960512
root# sed -n "$((N-5)),$((N+5))"p vmlinux.out
ffffffff8279d24e: e8 7d 14 91 fe callq 0xffffffff810ae6d0
ffffffff8279d253: eb bd jmp 0xffffffff8279d212
ffffffff8279d255: 5b pop %rbx
ffffffff8279d256: 5d pop %rbp
ffffffff8279d257: c3 retq
ffffffff8279d258: e8 93 47 26 ff callq 0xffffffff81a019f0
ffffffff8279d25d: 48 c7 c7 40 ca 31 82 mov $0xffffffff8231ca40,%rdi
ffffffff8279d264: e9 27 d3 fb fe jmpq 0xffffffff8175a590
ffffffff8279d269: e8 82 47 26 ff callq 0xffffffff81a019f0
ffffffff8279d26e: 48 c7 c7 60 c8 31 82 mov $0xffffffff8231c860,%rdi
ffffffff8279d275: e8 16 d3 fb fe callq 0xffffffff8175a590
函數tcp4_proc_init()的源代碼如下:
/* https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/net/ipv4/tcp_ipv4.c?h=v4.16.9#n2392 */
2377 static int __net_init tcp4_proc_init_net(struct net *net)
2378 {
2379 return tcp_proc_register(net, &tcp4_seq_afinfo);
2380 }
2381
2382 static void __net_exit tcp4_proc_exit_net(struct net *net)
2383 {
2384 tcp_proc_unregister(net, &tcp4_seq_afinfo);
2385 }
2386
2387 static struct pernet_operations tcp4_net_ops = {
2388 .init = tcp4_proc_init_net,
2389 .exit = tcp4_proc_exit_net,
2390 };
2391
2392 int __init tcp4_proc_init(void)
2393 {
2394 return register_pernet_subsys(&tcp4_net_ops);
2395 }
2396
2397 void tcp4_proc_exit(void)
2398 {
2399 unregister_pernet_subsys(&tcp4_net_ops);
2400 }
從L2394,我們可以看出tcp4_proc_init()調用了函數register_pernet_subsys(), 重新檢視vmlinux.out驗證一下。
root# egrep 'T register_pernet_subsys' /boot/System.map-$(uname -r)
ffffffff8175a590 T register_pernet_subsys
root# sed -n "$N, $((N+20))"p vmlinux.out > /tmp/1
root# egrep -in retq /tmp/1
11:ffffffff8279d28a: c3 retq
root# sed -n '1,11'p /tmp/1 | cat -n
1 ffffffff8279d258: e8 93 47 26 ff callq 0xffffffff81a019f0
2 ffffffff8279d25d: 48 c7 c7 40 ca 31 82 mov $0xffffffff8231ca40,%rdi
3 ffffffff8279d264: e9 27 d3 fb fe jmpq 0xffffffff8175a590
4 ffffffff8279d269: e8 82 47 26 ff callq 0xffffffff81a019f0
5 ffffffff8279d26e: 48 c7 c7 60 c8 31 82 mov $0xffffffff8231c860,%rdi
6 ffffffff8279d275: e8 16 d3 fb fe callq 0xffffffff8175a590
7 ffffffff8279d27a: 85 c0 test %eax,%eax
8 ffffffff8279d27c: 74 0c je 0xffffffff8279d28a
9 ffffffff8279d27e: 48 c7 c7 c0 d0 14 82 mov $0xffffffff8214d0c0,%rdi
10 ffffffff8279d285: e8 b4 c1 90 fe callq 0xffffffff810a943e
11 ffffffff8279d28a: c3 retq
root# sed -n '1,11'p /tmp/1 | cat -n | egrep ffffffff8175a590
3 ffffffff8279d264: e9 27 d3 fb fe jmpq 0xffffffff8175a590
6 ffffffff8279d275: e8 16 d3 fb fe callq 0xffffffff8175a590
參考資料:
- How to extract and disassemble a Linux kernel image (vmlinuz)