y0u_bat

macOS Kernel Debugging Guide 본문

System/[macOS]

macOS Kernel Debugging Guide

유뱃 2017. 3. 17. 00:12

macOS Kernel Debugging Guide

이문서에서는 10.10.5 OS X에서 lldb를 이용하여 커널 디버깅하는 기초적인 내용에 대해서 다룹니다.

우선 맥 커널 디버깅을 하기 위해서는 Kernel 버전이 동일한 macOS 가상머신이 두개 있어야합니다.

Kernel Debug Kit 설치

커널 버전에 맞는 KDK 를 두 가상머신에 전부다 깔아줍니다.

설치하고 나면, /Library/Developer/KDKs/*.kdk/System/Kernels/kernel.development 가 있을텐데, 이것을 /System/Library/Kernels/ 에 복사해줍니다. 복사해주면 부팅할때 development kernel로 부팅 할 수 있습니다.

sudo cp /Library/Developer/KDKs/KDK_*.kdk/System/Library/Kernels/kernel.development /Systems/Library/Kernels

NVRAM boot-args 업데이트

nvram 은 전원이 꺼져 있을때 정보를 유지하는 메모리입니다. 부팅할때 시스템에서 nvram에 있는 boot-args 을 검사하여 부팅 합니다.

macOS kernel debug 하기 위해서 boot-args를 설정 해야됩니다.

sudo nvram boot-args="debug=0x141 kext-dev-mode=1 kcsuffix=development pmuflags=1 -v"
  • debug=0x141 는 디버그 모드 활성화 시키고 부팅할때 원격 디버거 연결을 기다립니다.

    • (DB_HALT | DB_ARP | DB_LOG_PI_SCRN) 옵션을 사용하고 있습니다.

  • kext-dev-mode=1는 서명되지 않은 kext를 로드 할 수 있습니다.

  • kcsuffix=development는 시스템에 복사한 development kernel로 부팅 할 수 있게 해줍니다.

  • pmuflags=1 는 watchdog timer를 비활성화 해줍니다.

  • -v 는 디버깅할때 유용한 kernel verbose 모드를 활성화 시킵니다.


kext cache 무효화

sudo kextcache -invalidate /

새로운 커널을 사용할려면 kextcache를 무효화 시켜야 됩니다.

Reboot

sudo reboot

변경 사항들을 저장할려면 재부팅을 해야됩니다.

Xcode 설치

디버거를 실행시킬 가상머신에서 다음과 같은 명령어로 Xcode를 설치 할 수 있습니다

xcode-select --install

xcode를 설치하고 나면 lldb가 정상적으로 실행되는것을 볼 수 있습니다.

macOS kernel Debugging

이제 환경 설정은 다 끝나서, 커널 디버깅을 할 수 있습니다.

(lldb) target create /Library/Developer/KDKs/KDK_10.10.5_14F27.kdk/System/Library/Kernels/kernel.development
Loading kernel debugging from /Library/Developer/KDKs/KDK_10.10.5_14F27.kdk/System/Library/Kernels/kernel.development.dSYM/Contents/Resources/DWARF/../Python/kernel.py
LLDB version lldb-340.4.119
settings set target.process.python-os-plugin-path "/Library/Developer/KDKs/KDK_10.10.5_14F27.kdk/System/Library/Kernels/kernel.development.dSYM/Contents/Resources/DWARF/../Python/lldbmacros/core/operating_system.py"
command script import "/Library/Developer/KDKs/KDK_10.10.5_14F27.kdk/System/Library/Kernels/kernel.development.dSYM/Contents/Resources/DWARF/../Python/lldbmacros/xnu.py"
xnu debug macros loaded successfully. Run showlldbtypesummaries to enable type summaries.


Current executable set to '/Library/Developer/KDKs/KDK_10.10.5_14F27.kdk/System/Library/Kernels/kernel.development' (x86_64).
(lldb)

lldb를 실행하고 target을 KDK에 있는 kernel.development로 설정해줍니다.

그리고 디버거 모드를 활성화 시킨 vm을 부팅해줍니다.

Darwin Bootstrapper Version 2.0.2: Sun Jul  5 21:53:13 PDT 2015; root:libxpc_executables-559.40.1~1/launchd/RELEASE_X86_64
boot-args="debug=0x141 kext-dev-mode=1 kcsuffix=development pmuflags=1 -v"
IOKernelDebugger: registering debugger
ethernet MAC address: 00:1c:42:11:1d:6c
ip address: 10.37.129.7

Waiting for remote debugger connection.

디버거 모드를 활성시킨 vm에서 위와 같은 화면이 뜨는것을 볼 수 있습니다.

이제 lldb에서 kdp-remote 10.37.129.7으로 remote debugging를 시작 할 수 있습니다.

(lldb) kdp-remote 10.37.129.7
Version: Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 PDT 2015; root:xnu-2782.40.9~1/DEVELOPMENT_X86_64; UUID=C75BDFDD-9F27-3694-BB80-73CF991C13D8; stext=0xffffff801ee00000
Kernel UUID: C75BDFDD-9F27-3694-BB80-73CF991C13D8
Load Address: 0xffffff801ee00000
Loading kernel debugging from /Library/Developer/KDKs/KDK_10.10.5_14F27.kdk/System/Library/Kernels/kernel.development.dSYM/Contents/Resources/DWARF/../Python/kernel.py
LLDB version lldb-340.4.119
settings set target.process.python-os-plugin-path "/Library/Developer/KDKs/KDK_10.10.5_14F27.kdk/System/Library/Kernels/kernel.development.dSYM/Contents/Resources/DWARF/../Python/lldbmacros/core/operating_system.py"
Target arch: x86_64
Instantiating threads completely from saved state in memory.
command script import "/Library/Developer/KDKs/KDK_10.10.5_14F27.kdk/System/Library/Kernels/kernel.development.dSYM/Contents/Resources/DWARF/../Python/lldbmacros/xnu.py"
xnu debug macros loaded successfully. Run showlldbtypesummaries to enable type summaries.


Kernel slid 0x1ec00000 in memory.
Loaded kernel file /Library/Developer/KDKs/KDK_10.10.5_14F27.kdk/System/Library/Kernels/kernel.development
Loading 57 kext modules ......................................................... done.
Target arch: x86_64
Instantiating threads completely from saved state in memory.
kernel.development was compiled with optimization - stepping may behave oddly; variables may not be available.
Process 1 stopped
* thread #2: tid = 0x0146, 0xffffff801eebb4c8 kernel.development`kdp_register_send_receive(send=<unavailable>, receive=<unavailable>) + 392 at kdp_udp.c:463, name = '0xffffff8025a61b50', queue = '0x0', stop reason = signal SIGSTOP
    frame #0: 0xffffff801eebb4c8 kernel.development`kdp_register_send_receive(send=<unavailable>, receive=<unavailable>) + 392 at kdp_udp.c:463 [opt]
(lldb)  

맥 커널을 실행 시키고 나서부터는 커널을 다시 멈출 수 없습니다. 그래서 커널을 실행하기전에 미리 브레이크포인터와 설정들을 커널을 실행하기전에 미리 해줘야됩니다. 만약에 커널을 정지할려고 하면 오류 메세지가 뜨는것을 볼 수 있습니다.

(lldb) breakpoint set --name OSUnserializeBinary
Breakpoint 2: where = kernel.development`OSUnserializeBinary(char const*, unsigned long, OSString**) + 29 at OSSerializeBinary.cpp:287, address = 0xffffff801f46080d

breakpoint set —name fuction으로 커널의 원하는 함수에 브레이크 포인트를 걸 수 있습니다.

그리고 continue 하면, 커널이 실행되면서 해당 브레이크 포인터에서 브레이크 걸리는것을 볼 수 있습니다.

Comments