Introduction
커널 크래쉬가 나면, OS가 멈추기때문에 하나의 로컬에서는 디버깅을 하지 못하고 VM으로 kdp를 사용하여 디버깅을 해야한다.때문에 하나이상의 VM은 무조건 설치해야 한다. OS X에서 ‘Parallels Desktop’을 사용 한다면 복구파티션을 이용해 OSX VM을 설치 할 수 있다. 원하는 OSX 버전이 있다면 이미지파일 혹은 VM을 검색을 통해 설치하는 방식으로 하면 된다.
Host에서 디버깅을 하기위해 여러가지 환경 구성을 해야하는데, 우선 Kernel Debug Kit 을 다운로드 해야한다. Kernel Debug Kit은 LLDB 혹은 GDB로 어떠한 커널을 디버깅할때 기본적으로 없는 심볼을 로드해 디버깅 할 수 있게 해준다. 커널 바이너리를 직접 분석해 1day를 구경하고싶다면, OSX 버전 업데이트없이 Kernel Debug Kit을 이용하여 바이너리를 분석하면 된다.
Host Setting
https://developer.apple.com/download/more/?=Kernel%20Debug%20Kit
각 버전별로 공유되고있어, 분석하고있는 1-day의 버전에 맞춰 다운로드 받으면 된다. 다운로드한 dmg파일을 실행하여 설치하면 /Library/Developer/KDKs/ 경로에 디버그 킷이 존재한다.
OSX에서 실행되는 커널 바이너리는 /System/Library/Kernels/kernel 에 존재한다. 해당 바이너리를 디버거에 붙혀도 되지만 위 처럼 킷을 설치하는 환경구성은 Host와 Guest의 OSX버전이 달라도 커널디버깅을 할 수 있게 한다.
이제 Host에서 LLDB만 조금 손봐주면 Host의 환경구성은 끝난다.
LLDB에 바이너리를 붙혀 로드하게되면 위와 같은 에러메세지를 볼 수 있다. 해당 에러는 아래와 같이 쉽게 해결 가능하다.
(lldb) command script import "/Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Kernels/kernel.dSYM/Contents/Resources/DWARF/../Python/kernel.py"
songsangjun-ui-MacBook-Pro:~ s0ngsari$ echo "settings set target.load-script-from-symbol-file true" > ~/.lldbinit
Host와 Guest 모두 System Integrity Protection(SIP)를 disable 해야한다.
Host에서의 환경 구성은 모두 끝났고, 이제 Guest의 환경만 구성하면 커널을 디버깅 할 수 있게된다.
Guest Setting
OSX는 boot-args라는것을 nvram을 통해 건드릴 수 있다.
$ nvram boot-args
boot-args는 부팅될 때 설정한 값대로 OS에 적용된다. 하지만 우리가 해당 인자를 사용하는이유는 디버깅에 필요한 Interrupt와 Logging을 위한것이다. boot-args에도 인자가 많지만 우리는 debug라는것을 먼저 설정 할 것이다. 아래 차트를 보고 설정해주면 된다.
커널 디버깅을 할때에는 debug옵션의 인자를 0x144를 준다. DB_NMI플래그를 주게되면 NMI를 사용하여 디버거가 디버기에 붙을수 있게끔 해준다. 옵션을 줄때는 아래와 같이 설정해 주면 된다.
$ sudo nvram boot-args="debug=0x144 -v"
DB_LOG_PI_SCRN
DB_ARP
DB_NMI
Boot Verbose mode
$ sudo reboot
이후 boot-args가 설정되고, NMI 인터럽트를 발생시켜 debugger가 debuggee의 커널을 디버깅 할 수 있게 된다.
가끔 debuggee에서 NMI를 줘도 debugger에서 디버깅이 안될때가 있는데, 그때는 debugger에서 arp 테이블을 추가해줘야 한다.
$ arp -s <DebuggeeIP> <DebuggeeMac>
Host와 Guest모두 설정이 끝났으니 디버깅을 하면 된다.
Attach to debugger
해당 버전에 맞는 1day의 PoC를 예제 삼아 디버깅을 할것이다. 글에서 예제로 쓰이는 1day는 CVE-2017-2370 이다.
$ vi test.c
$ clang -o test test.c
Guest에서 PoC를 컴파일 해주고 NMI를 발생시키면 된다 NMI는 Command + Alt + Control + Shift + Esc로 발생 시킬수 있다. NMI를 발생시키면 OS가 멈추는게 정상이니 안심해도 된다.
디버거에서는 kdp-remote라는것을 사용하여 원격디버깅을 하면 되는데, 처음에 다운로드 받은 KDK의 kernel바이너리를 열어 주면된다.
songsangjun-ui-MacBook-Pro:~ s0ngsari$ lldb /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Kernels/kernel
(lldb) target create "/Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Kernels/kernel"
Loading kernel debugging from /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Kernels/kernel.dSYM/Contents/Resources/DWARF/../Python/kernel.py
LLDB version lldb-350.0.21.9
settings set target.process.python-os-plugin-path "/Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Kernels/kernel.dSYM/Contents/Resources/DWARF/../Python/lldbmacros/core/operating_system.py"
settings set target.trap-handler-names hndl_allintrs hndl_alltraps trap_from_kernel hndl_double_fault hndl_machine_check _fleh_prefabt _ExceptionVectorsBase _ExceptionVectorsTable _fleh_undef _fleh_dataabt _fleh_irq _fleh_decirq _fleh_fiq_generic _fleh_dec
command script import "/Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Kernels/kernel.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.12.1_16B2657.kdk/System/Library/Kernels/kernel' (x86_64).
(lldb)
에러 없이 잘 열렸으면 kdp-remote 커맨드를 사용해서 디버기에 연결하면 된다.
(lldb) kdp-remote <debuggeeIP>
Version: Darwin Kernel Version 16.1.0: Wed Oct 19 20:31:56 PDT 2016; root:xnu-3789.21.4~4/RELEASE_X86_64; UUID=75CA1C4D-7BF4-321B-B544-D8F1B6D60EF8; stext=0xffffff8014200000
Kernel UUID: 75CA1C4D-7BF4-321B-B544-D8F1B6D60EF8
Load Address: 0xffffff8014200000
Kernel slid 0x14000000 in memory.
Loaded kernel file /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Kernels/kernel
Loading 94 kext modules warning: Can't find binary/dSYM for com.apple.kec.corecrypto (809FEC94-017C-307A-B099-A01EFF5485FB)
.warning: Can't find binary/dSYM for com.apple.kec.pthread (36567317-B854-3157-ABF3-CEAD0A3770BB)
.warning: Can't find binary/dSYM for com.apple.kec.Libm (51D82C5F-0248-334D-ADC6-5861BBB83C97)
.warning: Can't find binary/dSYM for com.apple.iokit.IOACPIFamily (4F7FB6AD-2498-3F71-827C-ED7AA4BF2511)
.Target arch: x86_64
Instantiating threads completely from saved state in memory.
.warning: Can't find binary/dSYM for com.apple.driver.AppleACPIPlatform (249D7BA8-3FD5-3207-A482-0605CB898037)
.warning: Can't find binary/dSYM for com.apple.driver.AppleFDEKeyStore (EA5D0966-E8EA-337A-98EB-195806E8F723)
.warning: Can't find binary/dSYM for com.apple.iokit.IOReportFamily (B14DC3D3-7250-3DA3-BF50-C666EBEDAF4C)
.Target arch: x86_64
Instantiating threads completely from saved state in memory.
.warning: Can't find binary/dSYM for com.apple.driver.DiskImages (05A729EF-20B8-3254-8F13-42DF42E0544B)
.warning: Can't find binary/dSYM for com.apple.driver.AppleBusPowerController (DB526B45-1A45-3A81-A0C1-57F826CADEDF)
.Target arch: x86_64
Instantiating threads completely from saved state in memory.
.warning: Can't find binary/dSYM for com.apple.driver.KernelRelayHost (3B58E6F0-DE92-3289-9D3B-3BF12208585F)
.warning: Can't find binary/dSYM for com.apple.driver.AppleCredentialManager (54677B39-44B3-3AAA-BBEC-D78D0B5CC1A7)
.warning: Can't find binary/dSYM for com.apple.driver.AppleMobileFileIntegrity (0EFA4D2C-2271-3C43-B777-17D05716144A)
.warning: Can't find binary/dSYM for com.apple.driver.AppleKeyStore (75515493-6D25-39F7-8F0B-B08B505CAB74)
.warning: Can't find binary/dSYM for com.apple.security.TMSafetyNet (1CB512A3-24BD-344A-BFB4-44A61F27AB03)
.warning: Can't find binary/dSYM for com.apple.kext.AppleMatch (3B280DAB-903F-33DC-8110-525A1154B11E)
.warning: Can't find binary/dSYM for com.apple.security.sandbox (32039FC4-CA9B-3B74-B326-A2BF5CFE45E1)
.warning: Can't find binary/dSYM for com.apple.security.quarantine (EC92F0F9-694E-3E22-8B2C-4A071D20C6BA)
.warning: Can't find binary/dSYM for com.apple.nke.applicationfirewall (2A0DC0EF-655C-3D4B-93FD-3AED72BEBBDC)
.warning: Can't find binary/dSYM for com.apple.driver.AppleAPIC (BC2E6D01-BCBB-3525-BF38-BF99C3F1EC46)
.warning: Can't find binary/dSYM for com.apple.driver.AppleSMBIOS (9BB02681-4B47-3592-AD62-71FB0BF56965)
.warning: Can't find binary/dSYM for com.apple.driver.AppleRTC (3FD1BCF4-8AFC-3CE6-A36E-26410544AD14)
.warning: Can't find binary/dSYM for com.apple.iokit.IOSMBusFamily (185F0EBF-0262-3370-BD47-8FE4C8AA726E)
.warning: Can't find binary/dSYM for com.apple.driver.AppleACPIEC (BC227AE1-3CD5-3938-9C8C-009F1A966FBE)
.warning: Can't find binary/dSYM for com.apple.driver.AppleHPET (2CFB49B8-4CC2-320B-9C6E-99646DFD8571)
.Target arch: x86_64
Instantiating threads completely from saved state in memory.
.warning: Can't find binary/dSYM for com.apple.driver.AppleACPIButtons (4D5E51D6-8A6B-3B6A-A8F2-472D56C9D0C3)
.warning: Can't find binary/dSYM for com.apple.driver.AppleSmartBatteryManager (31670664-0EF0-39B5-A13F-15B8F9EC1283)
.warning: Can't find binary/dSYM for com.apple.driver.AppleEFIRuntime (6B7A5B9A-C313-3F7F-B6E2-60EE54593BC8)
.warning: Can't find binary/dSYM for com.apple.driver.AppleEFINVRAM (6F4404D6-8625-35CA-AEB6-6ECD7B64FA52)
.Target arch: x86_64
Instantiating threads completely from saved state in memory.
.warning: Can't find binary/dSYM for com.apple.driver.usb.AppleUSBHostPacketFilter (9888F9CD-B7EE-3A9D-8530-6FA4C167B26C)
.warning: Can't find binary/dSYM for com.apple.driver.usb.AppleUSBEHCI (BF6EF9A2-F090-3094-B3FA-F34351D946CF)
.warning: Can't find binary/dSYM for com.apple.driver.usb.AppleUSBUHCI (4EF43593-FC11-31E6-8B27-E7A6B5703C15)
.warning: Can't find binary/dSYM for com.apple.driver.usb.AppleUSBUHCIPCI (4EC90565-DB48-3190-8608-1F6DA30B8691)
.warning: Can't find binary/dSYM for com.apple.driver.usb.AppleUSBXHCI (E5F9850E-A1A1-305F-854D-48B46C08B2EC)
.warning: Can't find binary/dSYM for com.apple.driver.usb.AppleUSBXHCIPCI (B4287428-23D9-3547-93B5-2FEB73A02EA6)
.warning: Can't find binary/dSYM for com.apple.driver.usb.AppleUSBEHCIPCI (4FCE62CA-0477-34A2-9564-86F807CDBD4D)
.warning: Can't find binary/dSYM for com.apple.iokit.IOATAFamily (BC25A382-3DA0-33C7-93C5-E8A823B50F98)
.warning: Can't find binary/dSYM for com.apple.driver.AppleIntelPIIXATA (BDC5E432-B04E-3ACF-A213-672128140381)
.warning: Can't find binary/dSYM for com.apple.iokit.IOAHCIFamily (5C275B66-A173-3D92-853A-44FC35D45FFC)
.warning: Can't find binary/dSYM for com.apple.driver.AppleAHCIPort (BE72151C-73BE-35B7-8C31-74F49E4C5E98)
.Target arch: x86_64
Instantiating threads completely from saved state in memory.
.warning: Can't find binary/dSYM for com.apple.driver.AppleIntel8254XEthernet (34B30414-098D-3D22-AAB5-1A754D0647C6)
.warning: Can't find binary/dSYM for com.apple.iokit.IOAHCIBlockStorage (C449634B-8121-3BFB-972D-966847C4321F)
.Target arch: x86_64
Instantiating threads completely from saved state in memory.
.warning: Can't find binary/dSYM for com.apple.iokit.IOAHCISerialATAPI (681FA1E2-E3DE-3FEB-ACA7-16FC2B9078A6)
.warning: Can't find binary/dSYM for com.apple.filesystems.hfs.encodings.kext (68A8D6C1-CDCA-371C-970B-325BF2E7ECAB)
.Target arch: x86_64
Instantiating threads completely from saved state in memory.
.Target arch: x86_64
Instantiating threads completely from saved state in memory.
.Target arch: x86_64
Instantiating threads completely from saved state in memory.
.Target arch: x86_64
Instantiating threads completely from saved state in memory.
.Target arch: x86_64
Instantiating threads completely from saved state in memory.
.warning: Can't find binary/dSYM for com.apple.filesystems.hfs.kext (6C6C4A98-1534-3C52-B006-00FBC479233E)
.warning: Can't find binary/dSYM for com.apple.BootCache (C38789F4-9226-303C-99BE-3B8EAF8EC5C2)
.warning: Can't find binary/dSYM for com.apple.AppleFSCompression.AppleFSCompressionTypeZlib (9B32DDE9-151F-31A1-90E9-3CEB2C7BE27C)
.warning: Can't find binary/dSYM for com.apple.AppleFSCompression.AppleFSCompressionTypeDataless (C6F882D7-C35C-3963-A2FA-10033FF40107)
.warning: Can't find binary/dSYM for com.apple.driver.usb.AppleUSBHostCompositeDevice (30502C8D-F4B2-345F-B8F0-F8C54CAD7F46)
.warning: Can't find binary/dSYM for com.apple.driver.usb.networking (74394A72-1E87-363E-8CFD-182BD8C9362E)
.warning: Can't find binary/dSYM for com.apple.driver.usb.AppleUSBHub (F7BC6869-E4BA-3291-B7EA-BF28A0ABEF4A)
.warning: Can't find binary/dSYM for com.apple.driver.usb.IOUSBHostHIDDevice (0548123A-013B-3C74-86A8-33DF73E9CBBB)
.warning: Can't find binary/dSYM for com.apple.driver.AppleHIDKeyboard (664B787F-6DE5-3211-9081-E434055A550B)
.Target arch: x86_64
Instantiating threads completely from saved state in memory.
.warning: Can't find binary/dSYM for com.parallels.kext.video (5520E5F4-AC7C-9446-6088-5D8CAF25478D)
.warning: Can't find binary/dSYM for com.parallels.driver.AppleIntelAC97Controller (705C3A56-06CE-E995-5A75-618C5EF3D45D)
.warning: Can't find binary/dSYM for com.apple.vecLib.kext (C0ABF85C-CA30-3F02-9E1E-06F3BA5047A8)
.Target arch: x86_64
Instantiating threads completely from saved state in memory.
.warning: Can't find binary/dSYM for com.apple.iokit.IOSlowAdaptiveClockingFamily (F026208D-CC0C-3599-B303-9196904A584E)
.warning: Can't find binary/dSYM for com.apple.driver.AppleIntelSlowAdaptiveClocking (6FE984DD-A1FE-309E-83CF-B346989A6F17)
.warning: Can't find binary/dSYM for com.apple.driver.IOPlatformPluginFamily (087648A2-8A44-3095-AEC7-44A872A46205)
.warning: Can't find binary/dSYM for com.apple.driver.IOPlatformPluginLegacy (9156271B-C61E-3B40-B5B6-102369F12A8B)
.warning: Can't find binary/dSYM for com.apple.driver.AppleSMC (969D80B2-E714-3145-95B0-F61627E0EE4D)
.warning: Can't find binary/dSYM for com.apple.driver.ACPI_SMC_PlatformPlugin (7224B682-B40F-3A4A-BCA0-82727D251ECB)
.warning: Can't find binary/dSYM for com.parallels.kext.tg (09C02F97-D104-80F1-2A96-6BEF8A2F6967)
.warning: Can't find binary/dSYM for com.apple.driver.AppleSMBusController (4DAA381E-3690-3E94-8025-DFB34F714094)
.warning: Can't find binary/dSYM for com.apple.driver.AppleMCCSControl (102DD5D9-2DD5-3BCB-B5C0-BE08E1049CD6)
.warning: Can't find binary/dSYM for com.apple.driver.AppleUpstreamUserClient (F39509A4-191C-35DA-B7D9-08F95E5AB8BC)
.warning: Can't find binary/dSYM for com.apple.driver.AppleHV (39AC9B9B-7B20-322F-82F0-044B3CC08D43)
.warning: Can't find binary/dSYM for com.apple.driver.AppleSSE (907BB577-46DF-3C86-9034-758B61AD054D)
.warning: Can't find binary/dSYM for com.apple.Dont_Steal_Mac_OS_X (B97F871A-44FD-3EA4-BC46-8FD682118C79)
.warning: Can't find binary/dSYM for com.apple.iokit.IOBluetoothFamily (794ACDDD-2B46-3BF0-94E9-4FD7C109A427)
.Target arch: x86_64
Instantiating threads completely from saved state in memory.
.warning: Can't find binary/dSYM for com.apple.iokit.IOBluetoothSerialManager (6F68B8CF-6543-328E-AF57-DD250412CF02)
.warning: Can't find binary/dSYM for com.apple.iokit.IOSurface (D3B2D208-487C-3166-9F7D-D6159AABC428)
.warning: Can't find binary/dSYM for com.apple.iokit.IOUserEthernet (5EE448BD-95EC-35AD-B7FC-A1237E4BB346)
.warning: Can't find binary/dSYM for com.apple.driver.pmtelemetry (F46D019B-17FF-3CD5-A093-0894B81C1404)
.warning: Can't find binary/dSYM for com.parallels.driver.AppleIntelAC97Audio (F8F3B21C-958B-BB10-E13C-42CA34BF6815)
.warning: Can't find binary/dSYM for com.apple.driver.AppleOSXWatchdog (757A8B72-2A1A-32BA-99EC-6D802DE6E91F)
.warning: Can't find binary/dSYM for com.apple.kext.triggers (4E564246-8804-3673-B440-606AD360A3BB)
.warning: Can't find binary/dSYM for com.apple.filesystems.autofs (AA36D92F-D92B-3102-BAE3-F86A0A298143)
.warning: Can't find binary/dSYM for com.apple.filesystems.smbfs (42EF3BC8-5041-3E94-BC74-9D5906694E3A)
.warning: Can't find binary/dSYM for com.apple.driver.usb.cdc (6CB80B6B-9071-38ED-9A4B-635ABF20A429)
.Target arch: x86_64
Instantiating threads completely from saved state in memory.
done.
Target arch: x86_64
Instantiating threads completely from saved state in memory.
kernel was compiled with optimization - stepping may behave oddly; variables may not be available.
Process 1 stopped
* thread #2: tid = 0x16cc, 0xffffff801440bb4e kernel`Debugger [inlined] hw_atomic_sub(delt=1) at locks.c:1513, name = '0xffffff801c791028', queue = '0x0', stop reason = signal SIGSTOP
frame #0: 0xffffff801440bb4e kernel`Debugger [inlined] hw_atomic_sub(delt=1) at locks.c:1513 [opt]
(lldb)
제대로 커널이 잡혔다면 디버기의 프로세스를 Resuming해주고 PoC를 실행시켜주면 디버거에서 크래쉬를 확인 할 수 있다.
(lldb) c
Process 1 resuming
(lldb) Unloading 1 kext modules . done.
Process 1 stopped
* thread #4: tid = 0x1af6, 0xffffff8014751a8c kernel`fp_lookup(p=0xffffff801ebec780, fd=1, resultfp=0xffffff806eb6bf20, locked=0) + 92 at kern_descrip.c:3879, name = '0xffffff801c09a9a8', queue = '0x0', stop reason = EXC_BAD_INSTRUCTION (code=13, subcode=0x0)
frame #0: 0xffffff8014751a8c kernel`fp_lookup(p=0xffffff801ebec780, fd=1, resultfp=0xffffff806eb6bf20, locked=0) + 92 at kern_descrip.c:3879 [opt]
(lldb) register read
General Purpose Registers:
rax = 0x4141414141414141
rbx = 0x0000000000000001
rcx = 0x0000000000000001
rdx = 0xffffff8020d0e800
rdi = 0xffffff801ebec848
rsi = 0x0000000000000001
rbp = 0xffffff806eb6bef0
rsp = 0xffffff806eb6bec0
r8 = 0x0000000000000000
r9 = 0x00007fffda6afa50
r10 = 0x000000000000000a
r11 = 0x0000000000000246
r12 = 0xffffff801ebec780
r13 = 0xffffff801ec3e4f8
r14 = 0x0000000000000000
r15 = 0xffffff806eb6bf20
rip = 0xffffff8014751a8c kernel`fp_lookup + 92 at kern_descrip.c:3879
rflags = 0x0000000000010246
cs = 0x0000000000000008
fs = 0x0000000000000000
gs = 0x0000000000000000
(lldb
LLDB는 GDB와 커맨드가 비슷하지도 않아서 따로 배워두는게 좋다. 혹은 kgmacros라는 스크립트를 사용해서 gdb로 디버깅을 하는것도 나쁘지않다.
Logging the Heap
PoC를 실행하고 브레이크포인트를 걸어도 힙을 일일히 트레이싱하는것은 힘들다. 하지만 위에서 다룬 boot-args를 통해 힙을 트레이싱 할 수가 있다. 이는 OSX heap에서 다루는 zone을 트레이싱하는것이다.
$ sudo nvram boot-args="debug=0x144 -v -zc zlog1=kalloc.128 zlog2=kalloc.256"
위 커맨드와같이 -zc zlog1=zone 을 주고 재부팅을하면 끝난다. 중요한것은 해당 PoC가 어떤 zone을 사용하는지를 알아야한다. 모든 kalloc을 트레이싱하는것이 아니라, kalloc에 있는 여러 zone중에 우리가 선택한 zone을 트레이싱해주기때문에 편하게 트레이싱을 하고싶다면 사용하는 zone 하나만 boot-args 인자로 넘겨주면 된다.
zlog 적용한 모습
(lldb) bt
* thread #2: tid = 0x0cb5, 0xffffff801200bb4e kernel`Debugger [inlined] hw_atomic_sub(delt=1) at locks.c:1513, name = '0xffffff8019752980', queue = '0x0', stop reason = signal SIGSTOP
* frame #0: 0xffffff801200bb4e kernel`Debugger [inlined] hw_atomic_sub(delt=1) at locks.c:1513 [opt]
frame #1: 0xffffff801200bb4e kernel`Debugger(message=<unavailable>) + 910 at model_dep.c:1025 [opt]
frame #2: 0xffffff8011ef368c kernel`panic(str="\"a freed zone element has been modified in zone %s: expected %p but found %p, bits changed %p, at offset %d of %d in element %p, cookies %p %p\"@/Library/Caches/com.apple.xbs/Sources/xnu/xnu-3789.21.4/osfmk/kern/zalloc.c:651") + 236 at debug.c:458 [opt]
frame #3: 0xffffff8011f3f5c0 kernel`backup_ptr_mismatch_panic [inlined] zone_element_was_modified_panic(offset=0) + 800 at zalloc.c:642 [opt]
frame #4: 0xffffff8011f3f559 kernel`backup_ptr_mismatch_panic(zone=<unavailable>, element=<unavailable>, primary=4702111234474983745, backup=<unavailable>) + 697 at zalloc.c:710 [opt]
frame #5: 0xffffff8011f3e739 kernel`try_alloc_from_zone(zone=<unavailable>, check_poison=<unavailable>) + 521 at zalloc.c:832 [opt]
frame #6: 0xffffff8011f3d174 kernel`zalloc_internal(zone=<unavailable>, canblock=1, nopagewait=0) + 484 at zalloc.c:2284 [opt]
frame #7: 0xffffff8011f84580 kernel`vm_map_copyin_internal + 51 at vm_map.c:9428 [opt]
frame #8: 0xffffff8011f8454d kernel`vm_map_copyin_internal(src_map=<unavailable>, src_addr=140351705630208, len=3240, flags=<unavailable>, copy_result=<unavailable>) + 253 at vm_map.c:10279 [opt]
frame #9: 0xffffff8011ed7629 kernel`ipc_kmsg_copyin_ool_descriptor [inlined] vm_map_copyin_common(src_map=<unavailable>, src_destroy=<unavailable>, copy_result=0xffffff8071adbe40, use_maxprot=0) + 201 at vm_map.c:10187 [opt]
frame #10: 0xffffff8011ed7616 kernel`ipc_kmsg_copyin_ool_descriptor(dsc=0xffffff8018874c98, user_dsc=<unavailable>, is_64bit=<unavailable>, paddr=<unavailable>, copy=0xffffff8071adbe40, space_needed=<unavailable>, map=<unavailable>, mr=<unavailable>) + 182 at ipc_kmsg.c:2701 [opt]
frame #11: 0xffffff8011ed7c25 kernel`ipc_kmsg_copyin_body(kmsg=0xffffff8018874c00, space=0xffffff8018925b40, map=0xffffff801c0e9e08) + 613 at ipc_kmsg.c:3035 [opt]
frame #12: 0xffffff8011ee992f kernel`mach_msg_overwrite_trap(args=<unavailable>) + 287 at mach_msg.c:548 [opt]
frame #13: 0xffffff8011ff26ae kernel`mach_call_munger64(state=0xffffff8018dd12c0) + 430 at bsd_i386.c:562 [opt]
frame #14: 0xffffff8011ea5f66 kernel`hndl_mach_scall64 + 22
zlog 적용하지 않은 모습
(lldb) bt
* thread #2: tid = 0x13ab, 0xffffff8015c0bb4e kernel`Debugger [inlined] hw_atomic_sub(delt=1) at locks.c:1513, name = '0xffffff80200f4288', queue = '0x0', stop reason = signal SIGSTOP
* frame #0: 0xffffff8015c0bb4e kernel`Debugger [inlined] hw_atomic_sub(delt=1) at locks.c:1513 [opt]
frame #1: 0xffffff8015c0bb4e kernel`Debugger(message=<unavailable>) + 910 at model_dep.c:1025 [opt]
frame #2: 0xffffff8015af368c kernel`panic(str="\"Invalid queue element linkage for %p: next %p next->prev %p prev %p prev->next %p\"@/Library/Caches/com.apple.xbs/Sources/xnu/xnu-3789.21.4/osfmk/kern/queue.h:245") + 236 at debug.c:458 [opt]
frame #3: 0xffffff8015bec040 kernel`pmap_enter_options [inlined] __QUEUE_ELT_VALIDATE + 81 at queue.h:244 [opt]
frame #4: 0xffffff8015bebfef kernel`pmap_enter_options [inlined] insque at queue.h:347 [opt]
frame #5: 0xffffff8015bebfef kernel`pmap_enter_options [inlined] pv_hash_add + 32 at pmap_internal.h:544 [opt]
frame #6: 0xffffff8015bebfcf kernel`pmap_enter_options(pmap=<unavailable>, vaddr=<unavailable>, pn=<unavailable>, prot=<unavailable>, fault_type=<unavailable>, flags=<unavailable>, wired=<unavailable>, options=<unavailable>, arg=<unavailable>) + 5103 at pmap_x86_common.c:926 [opt]
frame #7: 0xffffff8015b6fb41 kernel`vm_fault_enter(m=0xffffff801c1c3c00, pmap=<unavailable>, vaddr=140736734584832, prot=<unavailable>, caller_prot=<unavailable>, wired=0, change_wiring=<unavailable>, no_cache=0, cs_bypass=<unavailable>, user_tag=1962753648, pmap_options=<unavailable>, need_retry=<unavailable>, type_of_fault=<unavailable>) + 4481 at vm_fault.c:3292 [opt]
frame #8: 0xffffff8015b71405 kernel`vm_fault_internal(map=<unavailable>, vaddr=<unavailable>, caller_prot=<unavailable>, change_wiring=0, interruptible=2, caller_pmap=0x0000000000000000, caller_pmap_addr=0, physpage_p=<unavailable>) + 4421 at vm_fault.c:4086 [opt]
frame #9: 0xffffff8015c069fc kernel`user_trap [inlined] vm_fault(map=<unavailable>, vaddr=<unavailable>, fault_type=<unavailable>, change_wiring=0, interruptible=2, caller_pmap=<unavailable>, caller_pmap_addr=0) + 652 at vm_fault.c:3397 [opt]
frame #10: 0xffffff8015c069d8 kernel`user_trap(saved_state=0xffffff801ff11060) + 616 at trap.c:1120 [opt]
frame #11: 0xffffff8015aa5655 kernel`hndl_alltraps + 229
Call stack을 확인했을때 두개의 차이점이 확실하게 보인다. zlog을 적용한 call stack은 zalloc으로 할당되는것을 볼 수 있지만 적용하지 않은것은 할당되는것들이 보이지않는다. 이렇게 Heap Corruption을 다루는 1day가 있을때 zone 개념을 파악하고 zlog을 사용한다면 더 확실하게 디버깅 할 수 있는 장점이 있어 참고해두면 유용하게 사용 할 수 있다.
ETC
버그에 대한 이해도 중요하지만 디버깅 또한 중요하다. 간단하게 디버깅을 하여 여러가지 커맨드를 사용해볼것이다.
(lldb) bt
* thread #2: tid = 0x1650, 0xffffff801200bb4e kernel`Debugger [inlined] hw_atomic_sub(delt=1) at locks.c:1513, name = '0xffffff801a0eee18', queue = '0x0', stop reason = signal SIGSTOP
* frame #0: 0xffffff801200bb4e kernel`Debugger [inlined] hw_atomic_sub(delt=1) at locks.c:1513 [opt]
frame #1: 0xffffff801200bb4e kernel`Debugger(message=<unavailable>) + 910 at model_dep.c:1025 [opt]
frame #2: 0xffffff8011ef368c kernel`panic(str="\"a freed zone element has been modified in zone %s: expected %p but found %p, bits changed %p, at offset %d of %d in element %p, cookies %p %p\"@/Library/Caches/com.apple.xbs/Sources/xnu/xnu-3789.21.4/osfmk/kern/zalloc.c:651") + 236 at debug.c:458 [opt]
frame #3: 0xffffff8011f3f5c0 kernel`backup_ptr_mismatch_panic [inlined] zone_element_was_modified_panic(offset=0) + 800 at zalloc.c:642 [opt]
frame #4: 0xffffff8011f3f559 kernel`backup_ptr_mismatch_panic(zone=<unavailable>, element=<unavailable>, primary=4702111234474983745, backup=<unavailable>) + 697 at zalloc.c:710 [opt]
frame #5: 0xffffff8011f3e739 kernel`try_alloc_from_zone(zone=<unavailable>, check_poison=<unavailable>) + 521 at zalloc.c:832 [opt]
frame #6: 0xffffff8011f3d174 kernel`zalloc_internal(zone=<unavailable>, canblock=1, nopagewait=0) + 484 at zalloc.c:2284 [opt]
frame #7: 0xffffff8011ed5248 kernel`ipc_kmsg_alloc(msg_and_trailer_size=4352) + 248 at ipc_kmsg.c:929 [opt]
frame #8: 0xffffff8011ef832d kernel`ipc_kobject_server(request=<unavailable>, option=<unavailable>) + 141 at ipc_kobject.c:299 [opt]
frame #9: 0xffffff8011ed5f61 kernel`ipc_kmsg_send(kmsg=<unavailable>, option=<unavailable>, send_timeout=<unavailable>) + 225 at ipc_kmsg.c:1826 [opt]
frame #10: 0xffffff8011ee9957 kernel`mach_msg_overwrite_trap(args=<unavailable>) + 327 at mach_msg.c:556 [opt]
frame #11: 0xffffff8011ff26ae kernel`mach_call_munger64(state=0xffffff8019fed920) + 430 at bsd_i386.c:562 [opt]
frame #12: 0xffffff8011ea5f66 kernel`hndl_mach_scall64 + 22
bt 명령어는 BackTrace 약자로, 현재 커널이 어느위치에 있는지 프레임단위로 보여준다. 각 프레임들을 선택 할 수 있고, 우리가 원하는 프레임으로 뛰어 로컬 변수들을 확인 할 수도 있다.
(lldb) frame select 5
frame #5: 0xffffff8011f3e739 kernel`try_alloc_from_zone(zone=<unavailable>, check_poison=<unavailable>) + 521 at zalloc.c:832 [opt]
frame select 라는 명령으로 프레임 넘버를 선택해주면 그 frame으로 현재위치가 잡히고, rip또한 아래와 같이 그 프레임대로 잡히게 된다.
(lldb) register read
General Purpose Registers:
rbx = 0xffffff801f977000
rbp = 0xffffff8872cabc50
rsp = 0xffffff8872cabc10
r12 = 0x7e415085550ee3c7
r13 = 0x4141414141414141
r14 = 0xffffff8017cd70a0
r15 = 0x4141414141414141
rip = 0xffffff8011f3e739 kernel`try_alloc_from_zone + 521 at zalloc.c:832
rip가 우리가 선택한 frame대로 잡혀있는것을 확인 할 수 있고, 해당 함수에서의 로컬변수값을 볼 수 있다.
(lldb) frame var
(zone_t) zone = <variable not available>
(boolean_t *) check_poison = <variable not available>
(zone_page_metadata *) page_meta = 0xffffff8017cd70a0
(vm_offset_t) element = 18446743524483756032
(vm_offset_t *) primary = 0xffffff801f977000
(vm_offset_t) next_element_primary = 4702111234474983745
(vm_offset_t) next_element = 9097641255853024199
(vm_offset_t) next_element_backup = 4702111234474983745
(vm_offset_t *) backup = <no location, value may have been optimized out>
OSX에서는 Page 맨 처음부분에 Meta data라는것이 존재하는데, 힙청크의 메타데이터와 같이 그 zone의 여러가지 정보들이 들어 있다.
iOS 10의 경우 메타데이터는 아래와 같다:
zindex: zone_array안에 있는 존의 index
page_count : 페이지의 할당 사이즈
free_count : 페이지의 free element갯수
freelist_offset : 페이지의 처음 free element의 주소
우리가 여기서 봐야할 것은 Element라는 변수인데, 무엇인지 한번 확인해보겠다.
(lldb) memory read 0xffffff801f977000
0xffffff801f977000: 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0xffffff801f977010: 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
PoC가 제대로 트리거되었다면 Element에는 A가 잔뜩 채워져있는것이 정상이다. zone에 할당된 엘리먼트에 memset으로 A를 채워넣었기때문에 위와같은 결과가 나오게된다.
이런식으로 PoC를 트리거 한 후, 원하는 프레임이 있다면 해당 프레임을 선택하고 로컬변수를 확인하는 식으로 디버깅을 진행하면 된다. 프레임에 관련된 커맨드는 종종 유용하게 쓰이니 알아두면 좋다.
맥에서 돌아가는 task들의 베이스주소를 확인이 가능한데, 우리가 볼 것은 커널이다. 커널의 task이름은 kernel_task인데, 커맨드로 해당 주소를 출력해볼것이다.
(lldb) showalltasks
task vm_map ipc_space #acts flags pid process io_policy wq_state command
0xffffff80185b3aa0 0xffffff8014d1e6e8 0xffffff80180ab800 134 0 0xffffff80126ba360 -1 -1 -1 kernel_task
0xffffff80185b3000 0xffffff8018db0838 0xffffff80180ab840 6 1 0xffffff8018d81128 -1 -1 -1 launchd
0xffffff801961c000 0xffffff8019603268 0xffffff80195c2740 4 D 34 0xffffff8018d80cb0 -1 -1 -1 UserEventAgent
0xffffff801961caa0 0xffffff8019603838 0xffffff80195c2980 2 D 36 0xffffff8018d80838 TQ -1 -1 -1 uninstalld
0xffffff8019634aa0 0xffffff8019603458 0xffffff80195c29c0 2 D 37 0xffffff8018d81e90 -1 -1 -1 kextd
0xffffff8019634550 0xffffff8019602ba0 0xffffff80195c2700 10 D 38 0xffffff8018d82308 -1 -1 -1 fseventsd
0xffffff8018e67550 0xffffff8019602aa8 0xffffff80195c2680 10 D 44 0xffffff8018d7fad0 -1 -1 -1 configd
0xffffff801965baa0 0xffffff80196026c8 0xffffff80195c2a00 3 D 45 0xffffff8018d834e8 -1 -1 -1 powerd
0xffffff8018ee2000 0xffffff80196029b0 0xffffff80195c2a40 4 D 50 0xffffff8018d846c8 -1 -1 -1 logd
...
이렇게 task, vm_map, ipc_space 각각 할당된 주소를 확인할 수 있고, 해당 task의 객체들 또한 확인이 가능하다. 원하는 task의 process주소를 복사해서 아래와 같이 하면된다.
(lldb) showtaskvme 0xffffff80185b3aa0
vm_map entries for task 0xffffff80185b3aa0
task vm_map ipc_space #acts flags
0xffffff80185b3aa0 0xffffff8014d1e6e8 0xffffff80180ab800 134
vm_map pmap size #ents rsize start:end
0xffffff8014d1e6e8 0xffffff8012688f88 0x00000008f838a000 731 70200 0xffffff7f80000000:0xffffffffffffe000
entry start:end #pgs tag.kmod prot&flags object offset
0xffffff8014d28750 0xffffff7f80000000:0xffffff7f92600000 75264 0 00 0x0000000000000000 0x0
0xffffff8014d287a0 0xffffff7f92600000:0xffffff8000000000 449024 6 37s G_KEXT_MAP 0xffffff7f92600000
0xffffff8018d40640 0xffffff8000000000:0xffffff8012751000 75601 0 00 0x0000000000000000 0x80000000
0xffffff8018d40a50 0xffffff8012751000:0xffffff8012879000 296 0 37 0xffffff8018d93d00 0x0
0xffffff8018d40960 0xffffff8012879000:0xffffff8016ac4000 16971 0 00 0x0000000000000000 0x92879000
...
0xffffff8014d28700 0xffffff8016b99000:0xffffff8017c1b000 4226 10 37 KERNEL_OBJECT 0xffffff8016b99000
0xffffff8014d286b0 0xffffff8017c1b000:0xffffff8047c1b000 196608 12 37s ZONE_MAP 0xffffff8017c1b000
0xffffff8014d28570 0xffffff8047c1b000:0xffffff804bc1b000 16384 0 37s KALLOC_MAP 0xffffff8047c1b000
0xffffff8014d287f0 0xffffff804bc1b000:0xffffff804bc42000 39 18 37 0xffffff801809b600 0x0
0xffffff8014d284d0 0xffffff804bc42000:0xffffff804bd5c000 282 18 37 0xffffff801809b700 0x0
0xffffff8014d28840 0xffffff804bd5c000:0xffffff804bd5d000 1 18 37 0xffffff801809b800 0x0
0xffffff8014d28890 0xffffff804bd5d000:0xffffff804bd84000 39 18 37 0xffffff801809b500 0x0
0xffffff8014d28480 0xffffff804bd84000:0xffffff804be9e000 282 18 37 0xffffff801809b400 0x0
0xffffff8014d28430 0xffffff804be9e000:0xffffff804be9f000 1 18 37 0xffffff801809b900 0x0
0xffffff8014d28520 0xffffff804be9f000:0xffffff804bedf000 64 0 00 KERNEL_OBJECT 0xffffff804be9f000
0xffffff8014d288e0 0xffffff804bedf000:0xffffff804bef1000 18 19 37 0xffffff801809ba00 0x0
0xffffff8014d28930 0xffffff804bef1000:0xffffff804bef5000 4 18 37 0xffffff801809bb00 0x0
0xffffff8014d28980 0xffffff804bef5000:0xffffff804befa000 5 1 37 0xffffff801809b300 0x0
0xffffff8014d289d0 0xffffff804befa000:0xffffff804bf00000 6 17 37 KERNEL_OBJECT 0xffffff804befa000
0xffffff8014d28a20 0xffffff804bf00000:0xffffff804bf01000 1 27 37 0xffffff801809bc00 0x0
0xffffff8014d283e0 0xffffff804bf01000:0xffffff804bf02000 1 27 37 0xffffff801809bd00 0x0
0xffffff8014d28a70 0xffffff804bf02000:0xffffff804c002000 256 7 37s IPC_KERNEL_MAP 0xffffff804bf02000
0xffffff8014d28b10 0xffffff804c002000:0xffffff804c802000 2048 7 37s IPC_KERNEL_COPY_MAP 0xffffff804c002000
커널 오브젝트도 존재하고, Zone이 맵핑된 주소등 다양한 정보들을 볼 수 있다. 원하는 개체를 확인하고 싶을때 위 커맨드를 사용해서 확인하면 된다. 그리고 간단하게 짚고 넘어가야하는것이 있는데 OSX를 공부하면 알게되는 zprint 커맨드이다. zprint는 OSX에서 할당되는 모든 Zone을 출력해주는데, 맥 터미널에서는 기본으로 제공해준다. 하지만 맥 커맨드로 zprint를 하면 주소는 나오지 않고 무슨 zone이 존재하는지만 보여준다. 그렇지만 디버거가 커널을 잡았을때는 다르다. LLDB에서도 zprint 커맨드를 제공해주는데 결과는 아래와 같다.
(lldb) zprint
ZONE TOT_SZ PAGE_COUNT ALLOC_ELTS FREE_ELTS FREE_SZ ALL_FREE_PGS ELT_SZ ALLOC( ELTS PGS WASTE) FLAGS NAME
...
0xffffff801266a0e0 393216 96 22614 1962 31392 0 16 4096 256 1 CX kalloc.16
0xffffff801266a200 1134592 277 20951 14505 464160 1 32 4096 128 1 CX kalloc.32
0xffffff801266a320 836400 205 16259 1166 55968 1 48 4096 85 1 CX kalloc.48
0xffffff801266a440 1585152 387 24594 174 11136 0 64 4096 64 1 CX kalloc.64
0xffffff801266a560 477360 117 5320 647 51760 0 80 4096 51 1 CX kalloc.80
0xffffff801266a680 367200 90 3710 115 11040 0 96 8192 85 2 CX kalloc.96
0xffffff801266a7a0 2203648 538 17016 200 25600 1 128 4096 32 1 CX kalloc.128
0xffffff801266a8c0 236640 58 1264 215 34400 0 160 8192 51 2 CX kalloc.160
0xffffff801266a9e0 270336 66 1353 55 10560 0 192 12288 64 3 CX kalloc.192
0xffffff801266ab00 512000 125 1960 40 10240 0 256 4096 16 1 CX kalloc.256
0xffffff801266ac20 265824 65 902 21 6048 0 288 20480 71 5 CX kalloc.288
0xffffff801266ad40 1282048 313 2420 84 43008 1 512 4096 8 1 CX kalloc.512
0xffffff801266ae60 28224 7 20 29 16704 2 576 4096 7 1 CX kalloc.576
0xffffff801266af80 847872 207 780 48 49152 9 1024 4096 4 1 CX kalloc.1024
0xffffff801266b0a0 32256 8 18 10 11520 0 1152 8192 7 2 CX kalloc.1152
0xffffff801266b1c0 40960 10 17 15 19200 0 1280 20480 16 5 CX kalloc.1280
0xffffff801266b2e0 1568768 383 750 16 32768 3 2048 4096 2 1 CX kalloc.2048
0xffffff801266b400 6479872 1582 314 1268 5193728 1268 4096 4096 1 1 CX kalloc.4096
0xffffff801266b520 1949696 476 84 154 1261568 306 8192 8192 1 2 CX kalloc.8192
...
터미널에서 제공해주는 zprint와는 다르게 LLDB에선 페이지의 메타데이터(주소, 엘리먼트 갯수 등등) 들이 전부 출력된다.
LLDB에는 수많은 커맨드들이 있기 때문에, tab기능을 이용하여 확인하면 원하는 커맨드를 사용할 수 있으니 참고하길 바란다.