MacOS 10.12.2 (OSX) KERNEL DEBUGGING

by digipine posted Nov 02, 2017
?

Shortcut

PrevPrev Article

NextNext Article

ESCClose

Larger Font Smaller Font Up Down Go comment Print

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.png

 

각 버전별로 공유되고있어, 분석하고있는 1-day의 버전에 맞춰 다운로드 받으면 된다. 다운로드한 dmg파일을 실행하여 설치하면 /Library/Developer/KDKs/ 경로에 디버그 킷이 존재한다.

 

2.png

 

 

OSX에서 실행되는 커널 바이너리는 /System/Library/Kernels/kernel 에 존재한다. 해당 바이너리를 디버거에 붙혀도 되지만 위 처럼 킷을 설치하는 환경구성은 Host와 Guest의 OSX버전이 달라도 커널디버깅을 할 수 있게 한다.

이제 Host에서 LLDB만 조금 손봐주면 Host의 환경구성은 끝난다.

 

3.png

 

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라는것을 먼저 설정 할 것이다. 아래 차트를 보고 설정해주면 된다.

 

4.png

 

 

커널 디버깅을 할때에는 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기능을 이용하여 확인하면 원하는 커맨드를 사용할 수 있으니 참고하길 바란다.

 

TAG •