'2014/01'에 해당되는 글 3건

  1. 2014.01.12 Mac 커널 디버깅 3
  2. 2014.01.05 [WinDbg 명령] !cs
  3. 2014.01.01 2014년 목표 4
Mac OS X2014. 1. 12. 20:58
반응형

맥 OS X 커널 디버깅 방법을 간략히 정리해 본다.

참고 사이트에 동일한 내용이 나와 있지만 직접 해 본 내용을 적어 놓는 것도 의미가 있으므로... ㅋㅋ



사전 준비 사항은 다음과 같다.


1. 디버거 맥과 디버기 맥은 동일한 OS X 버전이어야 한다.

    나는 두대의 OS X 10.7 (Lion) 맥북으로 테스트했다.


2. 디버거 맥과 디버기 맥은 동일한 네트워크에 있어야 한다. 

    커널 디버깅이 네트워크로 지원되기 때문에 같은 망 안에 있어야 한다.

    네트워크 커널 디버깅은 지금까지 윈도우 커널도 지원하지 않았던 기능이라 맥의 일부 진보함을 엿볼수 있는 부분이다.


3. 디버거 맥에 Kernel Debug Kit을 다운로드 받아 마운트해야 한다.

    디버기 맥 OS X 버전과 일치하는 것을 다운로드해 사용해야 한다.

    OS X 10.7.4(11E53)



다음과 같이 연결을 진행한다.


1. 디버기 부팅 설정


커널 디버깅 옵션을 켜고 부팅되도록 디버기를 설정한다.


$ sudo nvram boot-args="debug=0x144 -v"

Password:


명령후 재부팅한다.


부팅 옵션(0x144) 의 의미는 다음과 같다.


 Symbolic Name

Flag 

 Meaning

 DB_HALT

 0x01

 Halt at boot-time and wait for debugger attach (gdb). 

 (부팅이 중지되며 디버거를 기다린다.)

 DB_PRT

 0x02

 Send kernel debugging printf output to console.

 DB_NMI

 0x04

 Drop into debugger on NMI (Command–Power, Command-Option-Control-Shift-Escape, or interrupt switch). 

 (NMI 발생시 디버거로 진입한다.)

 DB_KPRT

 0x08

 Send kernel debugging kprintf output to serial port.

 DB_KDB

 0x10

 Make ddb (kdb) the default debugger (requires a custom kernel).

 DB_SLOG

 0x20

 Output certain diagnostic info to the system log.

 DB_ARP

 0x40

 Allow debugger to ARP and route (allows debugging across routers and removes the need for a permanent ARP entry, but is a potential security hole)—not available in all kernels.

 (기본적으로는 IP와 MAC 주소를 고정해 놔야 디버거가 디버기를 찾아내는데 이럴 필요를 없애준다.)

 DB_KDP_BP_DIS

 0x80

 Support old versions of gdb on newer systems.

 DB_LOG_PI_SCRN

 0x100

 Disable graphical panic dialog.
 (크래시 발생시 나타나는 기본 메시지 화면이 나타나지 않게 한다.)

 DB_KERN_DUMP_ON_NMI

 0x0800

 Causes the kernel to core dump when the user triggers an NMI.

 DB_DBG_POST_CORE

 0x1000

 Controls the kernel's behavior after dumping core in response to an NMI (DB_KERN_DUMP_ON_NMI). If the user triggers an NMI and this flag is clear, the kernel will dump core and then continue. Conversely, if this flag is set the kernel will dump core and then wait for a debugger connection. 
 DB_PANICLOG_DUMP

 0x2000

 Controls whether the kernel dumps a full core (if the flag is clear) or simply a panic log (if the flag is set).



2. 디버거에서  gdb 실행


다음 명령으로 gdb를 실행하고 커널 디버깅을 초기화 한다.

(최근 맥은 대부분 64비트로 부팅하기 때문에 x86_64로 설정해 준다. 32비트라면 i386으로 설정한다.)


$ gdb -arch x86_64 /Volumes/KernelDebugKit/mach_kernel


GNU gdb 6.3.50-20050815 (Apple version gdb-1752) (Sat Jan 28 03:02:46 UTC 2012)

Copyright 2004 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are

welcome to change it and/or distribute copies of it under certain conditions.

Type "show copying" to see the conditions.

There is absolutely no warranty for GDB.  Type "show warranty" for details.

This GDB was configured as "x86_64-apple-darwin"...



gdb 내부에서는 다음과 같이 커널 디버깅을 초기화 한다.


(gdb) source /Volumes/KernelDebugKit/kgmacros

Loading Kernel GDB Macros package.  Type "help kgm" for more info.


(gdb) target remote-kdp



3. 드라이버 빌드 및 복사, 로드


개발장비(디버거)에서 드라이버를 빌드하고 테스트장비(디버기)로 복사한다.

테스트장비에서 드라이버를 로드한다.


$ sudo kextutil MyDrv.kext


참고 자료에서는 바로 크래시가 발생하는 드라이버를 사용하기 때문에 시스템이 바로 죽고 디버거를 기다린다.

하지만 여기서는 문제없는 정상적인 드라이버인 MyDrv.kext를 사용해 본다.



4. 디버기 대기 상태 진입


시스템이 죽었다면 바로 디버거를 기다리는 상태가 되겠지만 정상 운영중이기 때문에 커널 디버거를 붙이려면 디버거 대기 상태로 진입해야 한다.


디버기(테스트장비)에서 Command + Power(전원버튼)을 눌러 NMI를 일으키면 다음과 같이 대기 상태가 된다.

왼쪽 상단 화면을 무식하게 깨뜨리면서 텍스트가 출력된다. ^^

(마지막 Connected to remote debugger 라인은 5번 과정이 완료되면 출력된다.)




5. 디버거 연결


gdb에서 다음과 같이 연결을 시도한다.


(gdb) kdp-reattach 172.x.x.x

Connected.



연결이 되면 위에서 보인 디버기 화면의 마지막 라인이 출력된다.

Connected to remote debugger.



6. 디버거에서 드라이버 심볼 생성


참고자료에는 드라이버의 심볼 파일을 생성해서 사용하는 것으로 나와 있다.

윈도우처럼 빌드시 심볼을 생성해 놓고 사용하고 싶은데 아직 찾아보지 못한 관계로 자료에서 가이드하는대로 진행한다.


먼저 연결된 gdb에서 다음 명령으로 드라이버의 주소와 크기를 조회한다.


(gdb) showallkmods


kmod_info           address             size                id    refs     version  name

0xffffff7f80798c60  0xffffff7f80791000  0x0000000000008000  129      0       1.4.2  com.apple.filesystems.cd9660

0xffffff7f81834970  0xffffff7f81803000  0x0000000000035000  128      0       1.7.2  com.apple.filesystems.smbfs

0xffffff7f81e74260  0xffffff7f81e6b000  0x000000000000e000  127      0       70.12  com.apple.driver.AppleBluetoothMultitouch

0xffffff7f81a9f120  0xffffff7f81a97000  0x0000000000012000  126      1       231.4  com.apple.driver.AppleMultitouchDriver

0xffffff7f80da0920  0xffffff7f80d93000  0x0000000000012000  125      1    4.0.5f11  com.apple.driver.IOBluetoothHIDDriver

0xffffff7f81c5bcd0  0xffffff7f81c57000  0x0000000000005000  124      0     1.9.5d0  com.apple.driver.AppleHWSensor

0xffffff7f80fccc30  0xffffff7f80fc5000  0x0000000000008000  123      0           1  com.greemate.driver.MyDrv

0xffffff7f81aabc90  0xffffff7f81aa9000  0x0000000000003000  122      0         122  com.apple.driver.AppleMikeyHIDDriver

0xffffff7f811c2550  0xffffff7f811be000  0x0000000000005000  121      0        1.59  com.apple.driver.AudioAUUC

...


출력된 리스트 중에서 MyDrv.kext를 찾아서 주소와 크기를 찾는다.



다음엔 심볼파일을 생성하기 위해 /tmp에 MyDrv.kext를 복사해 놓고 명령창에서 다음과 같이 입력한다.


$ sudo kextutil -s /tmp -n -arch x86_64 -k /Volumes/KernelDebugKit/mach_kernel -e -r /Volumes/KernelDebugKit /tmp/MyDrv.kext


Password:

/tmp/MyDrv.kext appears to be loadable (not including linkage for on-disk libraries).


Enter the hexadecimal load addresses for these extensions

(press Return to skip symbol generation for an extension):


com.greemate.driver.MyDrv: 0xffffff7f80fccc30                                                                          


마지막 라인에서 드라이버 주소를 입력하면 /tmp 에 심볼 파일이 생성된다.



7. 디버거에서 드라이버 심볼 로드


다시 gdb에서 심볼 파일 경로를 지정한다.


(gdb) set kext-symbol-file-path /tmp


디버깅할 드라이버를 추가해 준다.


(gdb) add-kext /tmp/MyDrv.kext

Reading symbols from /private/tmp/com.greemate.driver.MyDrv.sym...done.


이 때 심볼 파일이 로드된다.



8. 디버깅


MyDrv.kext 함수중 디버깅하고 싶은 함수에 브레이크 포인트를 건다.


(gdb) break CMyDrv::foo

Breakpoint 1 at 0xffffff7f80fcc810: file MyDrv.h, line 157.


(gdb) info b
Num Type           Disp Enb Address            What
1   breakpoint     keep y   0xffffff7f80fcc810 in CMyDrv::foo() at MyDrv.h:157



멈춰있던 디버기를 진행시킨다.

(gdb) c
Continuing.


디버기에서 함수가 호출될 수 있는 동작을 취한다.

브레이크 포인트가 걸리면 bt 명령으로 콜스택을 확인한다.


(gdb) bt

#0  0xffffff7f80fcc810 in CMyDrv::foo () at MyDrv.h: 157

#1  0xffffff7f809d7b5d in ?? ()

#2  0xffffff7f809d4371 in ?? ()

#3  0xffffff7f809d4418 in ?? ()

#4  0xffffff800063c5b6 in nfs_socket_search_cleanup (nss=0xffffff80008d8be8) at nfs_socket.c:620

#5  0xffffff800063b330 in nfs_connect_upcall (so=0x121, arg=0xffffff80120ebe38, waitflag=-128) at nfs_socket.c:351

#6  0xffffff800063b1d4 in nfs_connect_upcall (so=0xffffff80008bef20, arg=0xffffff8011bba880, waitflag=-128) at nfs_socket.c:322



8. 디버깅 종료

디버거를 종료하고 싶다면 다음과 같이 끝내주면 된다.

(gdb) quit


9. 결론


일단 이정도까지 진행하면 기본적인 디버깅은 진행할 수 있다.

gdb 명령어가 WinDbg와 달라 아직 명령어에 익숙하지 않다. 

좀 더 찾아보고 적응해야 하는데 정리되는 대로 공유할 예정이다. ^^



[참고자료]

1. Debugging a Kernel Extension with GDB

2. Kernel Programming Guide


반응형
Posted by GreeMate
WinDbg 디버깅2014. 1. 5. 14:04
반응형

프로세스 내의 크리티컬 섹션들을 보여주는 명령입니다.

유저모드 덤프, 커널모드 덤프에서 모두 사용할 수 있는데 여기선 커널 덤프에서의 사용예를 보여 드립니다.


기본적인 사용법은 다음과 같습니다.


!cs [-l] [-o]



예제 1) !cs

프로세스 내의 모든 크리티컬 섹션을 보여줍니다.

크리티컬 섹션을 다 볼 필요는 거의 없지만 기본 사용법이므로 적어봅니다.


1: kd> !cs

-----------------------------------------

DebugInfo          = 0x6A261D60

Critical section   = 0x6A262820 (ntdll!RtlCriticalSectionLock+0x0)

LOCKED

LockCount          = 0x0

OwningThread       = 0x460

RecursionCount     = 0x1

LockSemaphore      = 0x0

SpinCount          = 0x0

-----------------------------------------

DebugInfo          = 0x6A261D80

Critical section   = 0x6A262580 (ntdll!DeferedCriticalSection+0x0)

NOT LOCKED

LockSemaphore      = 0x7FC

SpinCount          = 0x0

-----------------------------------------

DebugInfo          = 0x6A262600

Critical section   = 0x6A26074C (ntdll!LoaderLock+0x0)

NOT LOCKED

LockSemaphore      = 0x0

SpinCount          = 0x0

-----------------------------------------

...



예제2) !cs -l -o

현재 락이 걸려있는 크리티컬 섹션만 보여줍니다.

크리티컬 섹션에 의한 대드락 분석시 유용합니다.


-l 은 락이 걸린 것만 보여주는 옵션이구요.

-o는 소유자의 스택을 볼 수 있게 해 주는 옵션입니다.


1: kd> !cs -l -o

-----------------------------------------

DebugInfo          = 0x77725760

Critical section   = 0x77725300 (ntdll!LdrpLoaderLock+0x0)

LOCKED

LockCount          = 0x4

WaiterWoken        = No

OwningThread       = 0x00000df4

RecursionCount     = 0x1

LockSemaphore      = 0x148

SpinCount          = 0x00000000

OwningThread       = .thread 86f27030

-----------------------------------------

DebugInfo          = 0x0033dbd8

Critical section   = 0x6f7f80c4 (WSCAPI!g_EventManager+0x2C)

LOCKED

LockCount          = 0x0

WaiterWoken        = No

OwningThread       = 0x00000e90

RecursionCount     = 0x2

LockSemaphore      = 0x18C

SpinCount          = 0x00000000

OwningThread       = .thread 8f693450

-----------------------------------------

DebugInfo          = 0x0033dca0

Critical section   = 0x71af50cc (wscisvif!g_critSecWscCallback+0x0)

LOCKED

LockCount          = 0x1

WaiterWoken        = No

OwningThread       = 0x00000e98

RecursionCount     = 0x1

LockSemaphore      = 0x14C

SpinCount          = 0x00000000

OwningThread       = .thread 86f2dd78



여러 스레드의 콜스택이 크리티컬 섹션에서 멈춰 있는 경우 어떤 스레드가 크리티컬 섹션을 소유하고 놓아주지 않는지를 찾아야 하는데요.

위와 같이 LOCKED 크리티컬 섹션을 찾아서 관심있는 크리티컬 섹션을 보면 OwningThread를 찾을 수 있습니다. 


Critical section 0x71af50cc를 추적한다고 가정하면 .thread 86f2dd78로 해당 스레드 콘텍스트로 변경하고 kv로 콜스택을 볼 수 있습니다.


그냥 다음과 같이 !thread 86f2dd78 명령을 바로 사용해서 볼 수도 있습니다.


1: kd> !thread 86f2dd78

THREAD 86f2dd78  Cid 0df0.0e98  Teb: 7ffd9000 Win32Thread: 00000000 WAIT: (Executive) UserMode Non-Alertable

    86f2df8c  Semaphore Limit 0x1

Not impersonating

DeviceMap                 84a07d60

Owning Process            9c55e7c0       Image:         MyApp.exe

Attached Process          N/A            Image:         N/A

Wait Start TickCount      14578          Ticks: 60781 (0:00:15:48.189)

Context Switch Count      17             IdealProcessor: 1             

UserTime                  00:00:00.000

KernelTime                00:00:00.000

Win32 Start Address ntdll!TppWorkerThread (0x776a2990)

Stack Init 9887b000 Current 9887ac30 Base 9887b000 Limit 98878000 Call 0

Priority 9 BasePriority 8 PriorityDecrement 0 IoPriority 2 PagePriority 5

Kernel stack not resident.

ChildEBP RetAddr  Args to Child              

9887ac48 818e91ba 86f2dd78 86f2de00 805d1120 nt!KiSwapContext+0x26 

9887ac8c 81884ef8 86f2dd78 00000000 86f2dd78 nt!KiSwapThread+0x44f

9887ace4 81a37be1 86f2df8c 00000000 81870c01 nt!KeWaitForSingleObject+0x492

9887ad4c 81887c56 000000f8 025ffa60 00000000 nt!NtWaitForKeyedEvent+0x1c1

9887ad4c 776c5cd4 000000f8 025ffa60 00000000 nt!KiSystemServicePostCall 

025ffa20 776c5540 77669767 000000f8 025ffa60 ntdll!KiFastSystemCallRet (FPO: [0,0,0])

025ffa24 77669767 000000f8 025ffa60 00000000 ntdll!NtWaitForKeyedEvent+0xc (FPO: [4,0,0])

025ffa68 776780b3 00000000 776780b3 00337150 ntdll!TppBarrierAdjust+0x18f (FPO: [Non-Fpo])

025ffa90 77678119 00337130 00000000 77696100 ntdll!TppCleanupGroupMemberWait+0x54 

025ffaa8 776781e5 00337130 00000000 00000000 ntdll!TppWorkWait+0x85 (FPO: [Non-Fpo])

025ffb14 77677e72 00337130 00000000 752ee9ce ntdll!TpWaitForWait+0xea (FPO: [Non-Fpo])

025ffb58 777f9851 0031fca8 ffffffff 025ffbac ntdll!RtlDeregisterWaitEx+0xd0 (FPO: [Non-Fpo])

025ffb68 6f7f533c 0031fca8 ffffffff 4ce62565 kernel32!UnregisterWaitEx+0x17 (FPO: [Non-Fpo])

025ffbac 6f7f5416 00000000 025ffbdc 6f7f3659 WSCAPI!CServiceMonitor::~CServiceMonitor+0x36 

025ffbb8 6f7f3659 00000001 7782b08d 00000000 WSCAPI!CServiceMonitor::`scalar deleting destructor'+0xd 

025ffbdc 6f7f5179 00000170 00000000 009d2498 WSCAPI!CEventManager::UnSubscribe+0xa1 

025ffbfc 71af1c0d 009d2508 71af509c 71af5020 WSCAPI!WscUnRegisterChanges+0xb6 

025ffc24 71af1f76 025ffc50 7767a637 025ffc8c wscisvif!CWscIsv::UnregisterWscServiceNotifications+0x15

025ffc2c 7767a637 025ffc8c 009d2498 00318fb8 wscisvif!WscNotificationThreadProcInternal+0xd 

025ffc50 776a2c1f 025ffc8c 00319018 752eef16 ntdll!TppWorkpExecuteCallback+0xf2 

025ffd80 7782d2e9 00343d20 025ffdcc 776a1603 ntdll!TppWorkerThread+0x545 (FPO: [Non-Fpo])

025ffd8c 776a1603 00343d20 752eef5a 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo])

025ffdcc 776a15d6 776a2990 00343d20 00000000 ntdll!__RtlUserThreadStart+0x23 (FPO: [Non-Fpo])

025ffde4 00000000 776a2990 00343d20 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])




Critical section 0x71af50cc를 소유하고 있는 thread 86f2dd78이 Semaphore 86f2df8c를 무한정 기다리면서...

다른 스레드가 Critical section 0x71af50cc를 얻지 못해 대드락에 빠져 있는 상황입니다.


반응형
Posted by GreeMate
끄적끄적2014. 1. 1. 23:21
반응형

1. 스카이프 전화영어 다시 시작

    2년간 쉬었던 스카이프 전화영어를 다시 해야 겠다.


2. 블로그 영문화

    요즘 방문자가 늘다보니 글로벌화에 대한 관심이 생겼다.

 

3. 2주에 한번은 주말에 가족들과 어디론가 가 보기

    주말을 너무 비 효율적으로 보내는 것 같아 생산적인 활동을 찾아보기로 한다.



아.. 심플하다.

2014년 화이팅!!!

반응형
Posted by GreeMate