Ubuntu 22.04 - 커널 이미지로부터 커널 함수 역어셈블
지난 글에는 WSL 환경에서의 방법을 다뤘는데요,
WSL 2 - 커널 이미지로부터 커널 함수 역어셈블
; https://www.sysnet.pe.kr/2/0/13780
이번에는 (WSL이 아닌) Ubuntu 22.04의 경우를 살펴보겠습니다. 대신 커널 소스코드 빌드는 하지 않고 있는 환경 그대로 구성할 텐데요, 따라서 이번에는 extract-vmlinux 도구로 커널 이미지를 추출하는 것이 필요합니다.
$ wget -O extract-vmlinux https://raw.githubusercontent.com/torvalds/linux/master/scripts/extract-vmlinux
$ chmod +x extract-vmlinux
$ sudo ./extract-vmlinux /boot/vmlinuz > vmlinux
$ uname -r
6.8.0-47-generic
이미지는 준비됐고, 이제 System.map 파일을 찾아야 하는데요 이미 linux-headers 관련 패키지들이 설치돼 있다면,
// System.map: No such file or directory
// https://www.linuxquestions.org/questions/ubuntu-63/system-map-no-such-file-or-directory-840336/
$ sudo apt install linux-headers-$(uname -r)
$ sudo apt list linux-headers-$(uname -r)
Listing... Done
linux-headers-6.8.0-47-generic/jammy-updates,jammy-security,now 6.8.0-47.47~22.04.1 amd64 [installed]
$ ls /lib/modules/$(uname -r)/build
arch certs Documentation fs init ipc Kconfig lib mm net samples security tools usr
block crypto drivers include io_uring Kbuild kernel Makefile Module.symvers rust scripts sound ubuntu virt
커널 빌드 번호와 함께 /boot 경로에 있는 System.map 파일이 있을 것입니다.
$ ls -l /boot/System.map-$(uname -r)
-rw------- 1 root root 8657806 10월 2 23:39 /boot/System.map-6.8.0-47-generic
그럼 게임 끝났군요. ^^
지난 글에 설명한 방법에 따라, System.map 파일에서 커널 함수의 주소를 구하고,
$ sudo cat /boot/System.map-$(uname -r) | grep proc_sys_open
ffffffff815b2200 t __pfx_proc_sys_open
ffffffff815b2210 t proc_sys_open
그 주소로 objdump를 이용해 역어셈블을 수행할 수 있습니다.
$ objdump -S --start-address=0xffffffff815b2210 ./vmlinux | less
./vmlinux: file format elf64-x86-64
Disassembly of section .text:
ffffffff815b2210 <.text+0x5b2210>:
ffffffff815b2210: e8 cb 3a b0 ff call 0xffffffff810b5ce0
ffffffff815b2215: 55 push %rbp
ffffffff815b2216: 48 c7 c0 e8 44 67 83 mov $0xffffffff836744e8,%rax
ffffffff815b221d: 48 89 e5 mov %rsp,%rbp
ffffffff815b2220: 41 55 push %r13
ffffffff815b2222: 49 89 f5 mov %rsi,%r13
ffffffff815b2225: 41 54 push %r12
ffffffff815b2227: 49 89 fc mov %rdi,%r12
ffffffff815b222a: 53 push %rbx
ffffffff815b222b: 48 8b 5f d8 mov -0x28(%rdi),%rbx
ffffffff815b222f: 48 c7 c7 b8 b8 f2 83 mov $0xffffffff83f2b8b8,%rdi
ffffffff815b2236: 48 85 db test %rbx,%rbx
ffffffff815b2239: 48 0f 44 d8 cmove %rax,%rbx
ffffffff815b223d: e8 8e da c6 00 call 0xffffffff8221fcd0
ffffffff815b2242: 48 83 7b 18 00 cmpq $0x0,0x18(%rbx)
ffffffff815b2247: 75 70 jne 0xffffffff815b22b9
ffffffff815b2249: 83 43 0c 01 addl $0x1,0xc(%rbx)
ffffffff815b224d: 48 c7 c7 b8 b8 f2 83 mov $0xffffffff83f2b8b8,%rdi
ffffffff815b2254: e8 b7 db c6 00 call 0xffffffff8221fe10
ffffffff815b2259: 49 8b 44 24 e0 mov -0x20(%r12),%rax
ffffffff815b225e: 48 81 fb 00 f0 ff ff cmp $0xfffffffffffff000,%rbx
ffffffff815b2265: 77 65 ja 0xffffffff815b22cc
ffffffff815b2267: 48 8b 40 28 mov 0x28(%rax),%rax
ffffffff815b226b: 48 85 c0 test %rax,%rax
ffffffff815b226e: 74 0a je 0xffffffff815b227a
ffffffff815b2270: 48 63 00 movslq (%rax),%rax
ffffffff815b2273: 49 89 85 c8 00 00 00 mov %rax,0xc8(%r13)
ffffffff815b227a: 48 c7 c7 b8 b8 f2 83 mov $0xffffffff83f2b8b8,%rdi
ffffffff815b2281: e8 4a da c6 00 call 0xffffffff8221fcd0
ffffffff815b2286: 83 6b 0c 01 subl $0x1,0xc(%rbx)
ffffffff815b228a: 74 1d je 0xffffffff815b22a9
ffffffff815b228c: 48 c7 c7 b8 b8 f2 83 mov $0xffffffff83f2b8b8,%rdi
...[생략]...
하지만, 위의 설정으로는 gdb를 이용한 경우,
$ sudo gdb ./vmlinux
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04.2) 12.1
...[생략]...
Reading symbols from ./vmlinux...
(No debugging symbols found in ./vmlinux)
(gdb) disass do_sys_open
No symbol table is loaded. Use the "file" command.
저렇게 symbol을 찾을 수 없다고 나옵니다. 분명히 현재 CONFIG_DEBUG_INFO가 활성화된 커널인데,
$ cat /boot/config-6.8.0-47-generic | grep CONFIG_DEBUG_INFO
CONFIG_DEBUG_INFO=y
...[생략]...
// 커널 빌드 시, "make menuconfig", "Kernel hacking" -> "Compile-time checks and compiler options" -> "Debug information" -> "Generate DWARF Version 5 debuginfo" (CONFIG_DEBUG_INFO, CONFIG_DEBUG_INFO_DWARF5) 옵션 설정
음... 어쩔 수 없군요, 이에 대해 검색해 보면,
Installing Ubuntu Kernel Debugging Symbols
; https://hadibrais.wordpress.com/2017/03/13/installing-ubuntu-kernel-debugging-symbols/
디버깅 심벌을 설치하라고 나옵니다. 이를 위해 우선 적절한 repo key를 추가하고,
$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C8CAB6595FDFF622
Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8)).
Executing: /tmp/apt-key-gpghome.YNAyLbATlr/gpg.1.sh --keyserver keyserver.ubuntu.com --recv-keys C8CAB6595FDFF622
gpg: key C8CAB6595FDFF622: "Ubuntu Debug Symbol Archive Automatic Signing Key (2016) <ubuntu-archive@lists.ubuntu.com>" not changed
gpg: Total number processed: 1
gpg: unchanged: 1
아래의 내용으로 ddebs.list 파일을 구성한 다음,
$ cat /etc/apt/sources.list.d/ddebs.list
deb http://ddebs.ubuntu.com/ jammy main restricted universe multiverse
deb http://ddebs.ubuntu.com/ jammy-security main restricted universe multiverse
deb http://ddebs.ubuntu.com/ jammy-updates main restricted universe multiverse
deb http://ddebs.ubuntu.com/ jammy-proposed main restricted universe multiverse
디버깅 심벌 패키지를 설치합니다.
$ sudo apt-get update
$ sudo apt-get install linux-image-$(uname -r)-dbgsym
Reading package lists... Done
...[생략]...
Preparing to unpack .../linux-image-unsigned-6.8.0-47-generic-dbgsym_6.8.0-47.47~22.04.1_amd64.ddeb ...
Unpacking linux-image-unsigned-6.8.0-47-generic-dbgsym (6.8.0-47.47~22.04.1) ...
Selecting previously unselected package linux-image-6.8.0-47-generic-dbgsym.
Preparing to unpack .../linux-image-6.8.0-47-generic-dbgsym_6.8.0-47.47~22.04.1_amd64.ddeb ...
Unpacking linux-image-6.8.0-47-generic-dbgsym (6.8.0-47.47~22.04.1) ...
Setting up linux-image-unsigned-6.8.0-47-generic-dbgsym (6.8.0-47.47~22.04.1) ...
Setting up linux-image-6.8.0-47-generic-dbgsym (6.8.0-47.47~22.04.1) ...
$ ll /usr/lib/debug/boot/vmlinux-$(uname -r)
-rw-r--r-- 1 root root 402529448 10월 2 23:39 /usr/lib/debug/boot/vmlinux-6.8.0-47-generic
그럼 저렇게 다운로드한 vmlinux 파일을 gdb로 열어 역어셈블을 하면 됩니다.
$ gdb /usr/lib/debug/boot/vmlinux-$(uname -r)
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04.2) 12.1
...[생략]...
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /usr/lib/debug/boot/vmlinux-6.8.0-47-generic...
(gdb) disass do_sys_open
Dump of assembler code for function do_sys_open:
0xffffffff814dc5e0 <+0>: call 0xffffffff810b5ce0 <__fentry__>
0xffffffff814dc5e5 <+5>: push %rbp
0xffffffff814dc5e6 <+6>: mov %rsp,%rbp
...[생략]...
0xffffffff814dc65b <+123>: jmp 0xffffffff814dc61e <do_sys_open+62>
0xffffffff814dc65d <+125>: call 0xffffffff82206840 <__stack_chk_fail>
End of assembler dump.
(gdb) q
$ objdump -t /usr/lib/debug/boot/vmlinux-$(uname -r) | less
/usr/lib/debug/boot/vmlinux-6.8.0-47-generic: file format elf64-x86-64
SYMBOL TABLE:
ffffffff81000000 l d .text 0000000000000000 .text
ffffffff82600000 l d .rodata 0000000000000000 .rodata
ffffffff82d46570 l d .pci_fixup 0000000000000000 .pci_fixup
ffffffff82d4a170 l d .tracedata 0000000000000000 .tracedata
...[생략]...
참고로, 다른 CentOS 9에서 gdb를 이용해 커널 이미지를 열었더니,
$ sudo ./extract-vmlinux /boot/vmlinuz-5.14.0-472.el9.x86_64 > vmlinux
$ gdb ./vmlinux
GNU gdb (CentOS Stream) 14.2-3.el9
...[생략]...
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./vmlinux...
This GDB supports auto-downloading debuginfo from the following URLs:
<https://debuginfod.centos.org/>
Enable debuginfod for this session? (y or [n]) y
Debuginfod has been enabled.
To make this setting permanent, add 'set debuginfod enabled on' to .gdbinit.
Downloading separate debug info for /home/testusr/temp/ConsoleApp1/vmlinux
Reading symbols from /home/testusr/.cache/debuginfod_client/68eaa56bbb8f83d229a48ad50ee10a95de117103/debuginfo...
저렇게 debuginfod를 활성화할 거냐고 묻고는 자동으로 심벌 정보를 내려받았습니다. 이후, 그냥 아무런 조치 없이 역어셈블을 할 수 있는데요,
(gdb) disass do_sys_open
Dump of assembler code for function do_sys_open:
0xffffffff81441bf0 <+0>: call 0xffffffff810774f0 <__fentry__>
0xffffffff81441bf5 <+5>: sub $0x20,%rsp
0xffffffff81441bf9 <+9>: mov %gs:0x28,%rax
0xffffffff81441c02 <+18>: mov %rax,0x18(%rsp)
0xffffffff81441c07 <+23>: mov %edx,%eax
...[생략]...
가만 보니까, Ubuntu도 22.04 이후로 저렇게 debuginfod 서비스를 제공한다고 합니다.
Debug symbol packages
; https://ubuntu.com/server/docs/debug-symbol-packages
그런데 이상하군요, 제가 위에서 했던 Ubuntu 실습이 22.04였는데, gdb로 ./vmlinux 파일을 열었을 때 저런 식으로 debuginfod가 관여하지 않고 그냥 곧바로 "(No debugging symbols found in ./vmlinux)"라고 나왔을 뿐입니다. 뭔가 설정이 다를 수 있지만, 암튼... 이번엔 여기까지만. ^^
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]