Linux内核之vmlinuz反汇编
本文介绍在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
参考资料: