WinDbg 디버깅2015. 3. 22. 22:48
반응형

이번 포스팅에서는 지난 번에 다뤘던 수동으로 64비트 콜스택 추적하기의 이론적인 배경을 알아본다.


지난번 포스팅에서 함수 하나가 사용하는 스택의 크기를 계산하기 위한 공식은 다음과 같았다.


stacksize = 리턴주소 push 크기 + 함수 프롤로그에서 사용한 push 크기 + 함수 프롤로그에서 rsp를 빼는 크기


이런 계산이 가능한 이유는 함수에서 사용하는 스택 크기가 항상 정해져 있기 때문이다.


이를 이해하기 위해서는 일단 64비트의 기본적인 스택 구조를 이해해야 한다.

그림으로 표현해 보면 다음과 같다.



 리턴주소

특수목적 임시저장

(레지스터, 보안쿠키 등)

... 

지역변수

지역변수

지역변수

 ...

... 

파라미터6

 파라미터5

 파라미터4

 파라미터3

 파라미터2

 파라미터1

리턴주소


<= 상위 함수의 rsp

상위 함수의 리턴 주소


함수 내부에서 사용하는 스택 영역







함수 내부에서 호출하는 Sub 함수 중 파라미터가 가장 많은 함수의 파라미터만큼 예약한 공간

(최소 4개 예약)




<= 이 함수의 rsp (파라미터 1의 스택 주소)

Sub 함수 호출시 저장되는 이 함수의 리턴 주소



어떤 함수가 실행 중일 때 스택을 사용하는 형태를 보면 그림과 같이 구성된다.

특별한 경우를 제외하고는 대부분 이와 같은 형태를 보인다.

따라서 이렇게 사용되는 원리를 이해하면 수동으로 콜스택 추적이 가능해 진다.


각 영역들에 대해 알아보면 다음과 같다.


1. 상위 함수 리턴 주소

설명이 필요없이 이 함수를 호출할 때 스택에 저장되는 호출자 주소다.



2. 함수 내부에서 사용하는 스택 영역

지역변수, 임시 저장 영역 등으로 컴파일러가 구성한 스택 영역이다.

컴파일 시에 컴파일러가 이 영역의 크기를 계산하기 때문에 컴파일 시에 크기가 정해진다.



3. Sub 함수 파라미터 예약 공간

이 함수 내부에서 여러 개의 Sub 함수를 호출할 수 있을 텐데 그 중에서 가장 많은 파라미터 수 만큼 예약해 놓는다. 컴파일 시에 컴파일러가 계산해 놓을 수 있다.


함수 내부에서 사용하는 스택 크기는 2번하고 3번을 더한 값이다.

컴파일러는 컴파일시 함수 프롤로그에서 push와 sub rsp로 2, 3번을 구성해 준다.


컴파일러가 함수에서 사용하는 스택의 크기를 미리 계산해서 함수 프롤로그에 코드를 만들어 주기 때문에...

거꾸로 함수 프롤로그를 보고 스택을 사용하는 크기를 계산해서 함수가 사용하는 스택 크기를 알 수 있는 것이다. 



4. 최소 4개 예약?

함수 내부에서 호출하는 Sub 함수들이 파라미터를 1개 밖에 받지 않는다고 하더라도 이 파라미터 공간은 최소 4개가 예약된다. 4개로 정해지는 이유는 64비트에서 파라미터 전달이 4개까지는 rcx, rdx, r8, r9 레지스터를 이용하기 때문인 것 같다. Sub 함수의 파라미터가 5개라면 5개의 파라미터를 저장할 공간을 예약해 놓는다.


재미있는 것은 Sub 함수를 호출할 때 현재 함수에서는 파라미터1~파라미터4 예약 공간을 채우지 않는다는 것이다. 파라미터는 이미 rcx, rdx, r8, r9에 넣어 놨으므로 스택에 다시 저장하지 않는다. 다만 파라미터가 5개 이상인 경우는 스택을 통해 파라미터를 전달하기로 되어 있으니 파라미터5 이후는 스택에 저장해 준다.


파라미터가 7개인 PsCreateSystemThread를 호출하는 코드를 보면서 확인해 본다.


fffff800`01795328 488364243000    and     qword ptr [rsp+30h],0

fffff800`0179532e 488d055bfeffff  lea     rax,[nt!InbvRotateGuiBootDisplay]

fffff800`01795335 488d4c2458      lea     rcx,[rsp+58h]

fffff800`0179533a 4889442428      mov     qword ptr [rsp+28h],rax

fffff800`0179533f 488364242000    and     qword ptr [rsp+20h],0

fffff800`01795345 4533c9          xor     r9d,r9d

fffff800`01795348 4533c0          xor     r8d,r8d

fffff800`0179534b 33d2            xor     edx,edx

fffff800`0179534d e802a21900      call    nt!PsCreateSystemThread 


첫번째 라인은 마지막 7번째 파라미터 스택 위치인 [rsp+30h]에 0을 저장하는 것이다. 네번째 라인에서 [rsp+28h]에 6번째 파라미터를 저장하고 다섯번째 라인에서 [rsp+20h]에 5번째 파라미터를 저장한다.

첫번째 파라미터부터 네번째 파라미터까지는 스택에 저장하는 모양은 볼 수 없고 rcx, edx, r8d, r9d에 저장하는 모양이 나타나 있다.


그러면 예약해 놓은 앞 네개의 파라미터 스택 공간은 언제 사용할까?

MSDN 상으로는 호출된 Sub 함수가 레지스터로 전달된 파라미터를 스택에 저장하고 싶으면 사용할 수 있다고 되어 있다. 디버깅을 편하게 하기 위해 Sub 함수 프롤로그에서 레지스터로 전달된 파라미터를 여기에 저장하게 할 수도 있다고 설명은 하고 있지만... 실제로 대부분 이 영역은 사용하지 않아서 쓰레기값만 저장되어 있다. 그래서 콜스택을 디버깅할 때 함수간에 전달된 파라미터를 찾기가 매우 힘든 것이다.



5. 이 함수의 rsp

64비트에서 함수가 실행될 때 사용하는 스택 베이스는 rsp다.

32비트에서는 함수에 진입하면 ebp를 스택 베이스로 잡고 함수 내부에서 사용하는 스택은 [ebp-xx]로 접근한다. 

하지만 64비트에서는 함수에 진입하면 이 함수에서 사용하는 스택 크기를 모두 계산해서 미리 빼놓고 rsp에 저장한 후에 [rsp+xx]로 접근한다.


그림에서 알 수 있듯이 rsp는 호출할 함수에 전달할 파라미터 1의 주소를 가리키게 된다. 

그래서 다음 위치는 항상 다음과 같은 의미를 가진다.


[rsp+00h] : 호출 함수에 전달할 파라미터 1을 저장

[rsp+08h] : 호출 함수에 전달할 파라미터 2를 저장

[rsp+10h] : 호출 함수에 전달할 파라미터 3을 저장

[rsp+18h] : 호출 함수에 전달할 파라미터 4를 저장


하지만 현재 함수에서 이 위치를 참조하는 일은 없다. ㅠㅠ

그냥 의미만 알아 놓자. 현재 함수가 호출하는 sub 함수에 파라미터 5가 있다면 [rsp+20h]에 저장한다는 것은 알았으니... 



이렇게 해서 64비트 콜스택 구성에 대해 모두 알아봤다.

일단 이정도만 알아도 64비트를 탐험하는 작업이 좀 더 편해질 것 같다.


반응형
Posted by GreeMate
WinDbg 디버깅2015. 2. 26. 23:15
반응형

64비트 콜스택을 좀 보다보니 수동으로 따라갈 수 있는 방법을 하나 알게 되었다.

거의 포기하고 있었는데 보다보니 이런 것들이 보이기 시작하는구나... ㅋㅋ


이론을 먼저 설명하면 머리만 복잡해지니 예제부터 보기로 한다.

다음과 같은 콜스택이 있다고 해 보자.


kd> k

Child-SP          RetAddr           Call Site

fffff880`009adeb0 fffff800`019a6135 nt!ExAllocatePoolWithTag+0x326

fffff880`009adfa0 fffff800`019a3e3e nt!RtlpInheritAcl+0xc5

fffff880`009ae070 fffff800`0197f6c2 nt!RtlpNewSecurityObject+0x8ce

fffff880`009ae300 fffff800`0197e582 nt!ObpAssignSecurity+0x82

fffff880`009ae370 fffff800`01980db3 nt!ObInsertObjectEx+0x1e2

fffff880`009ae5c0 fffff800`019801be nt!PspInsertThread+0x2f3

fffff880`009ae740 fffff800`0192f679 nt!PspCreateThread+0x246

fffff880`009ae9c0 fffff800`01795352 nt!PsCreateSystemThread+0x125

fffff880`009aeab0 fffff800`01bc751d nt!DisplayBootBitmap+0x162

fffff880`009aeb00 fffff800`01b18e29 nt!Phase1InitializationDiscard+0x17d

fffff880`009aecd0 fffff800`0192f73a nt!Phase1Initialization+0x9

fffff880`009aed00 fffff800`016848e6 nt!PspSystemThreadStartup+0x5a

fffff880`009aed40 00000000`00000000 nt!KiStartSystemThread+0x16


ExAllocatePoolWithTag 중간쯤에서 멈춰있는 상태다.

ExAllocatePoolWithTag 함수에서 사용중인 스택 베이스 포인터가 Child-SP fffff880`009adeb0로 나타나고 있다.


콜스택을 수동으로 추적하려면 각 함수의 스택 프레임의 기준을 찾는 것이 중요한데 Child-SP가 기준이라고 보면 된다.

Child-SP를 따라갈 수 있으면 되는데... 어떻게 할 수 있는지 확인해 보자.


ExAllocatePoolWithTag 함수에서 멈춰 있는 상태에서 레지스터 상태를 보면 다음과 같다.


kd> r

rax=fffff8a000006000 rbx=fffffa8000c2d280 rcx=0000000000000001

rdx=0000000000000001 rsi=000000000000000e rdi=0000000000000001

rip=fffff800017c7f36 rsp=fffff880009adeb0 rbp=0000000000001000

 r8=0000000000000000  r9=0000000000000000 r10=0000000000000000

r11=fffff880009ade80 r12=fffffa8000c2c140 r13=0000000000000000

r14=0000000000000002 r15=0000000063416553

iopl=0         nv up ei ng nz na po nc

cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00000286

nt!ExAllocatePoolWithTag+0x326:

fffff800`017c7f36 448928          mov     dword ptr [rax],r13d ds:002b:fffff8a0`00006000=fe05a30d


rsp가 Child-SP와 같은 값을 가지고 있다.


일단 이론을 보기 전에 하나는 그냥 외우고 시작하자.

64비트에서는 기본적으로 rsp가 32비트에서의 ebp와 비슷한 역할을 한다.

64비트 코드를 분석해 보면 알겠지만 대부분 rsp를 거의 변경하지 않고 사용한다.

따라서 ExAllocatePoolWithTag는 rsp를 기준으로 자신의 스택을 사용하고 있다.


여기서 상위 함수의 rsp 값을 찾으려면 ExAllocatePoolWithTag의 코드 앞부분(프롤로그)을 보면 된다.


kd> u nt!ExAllocatePoolWithTag

nt!ExAllocatePoolWithTag:

fffff800`017c7c10 fff5            push    rbp

fffff800`017c7c12 57              push    rdi

fffff800`017c7c13 4157            push    r15

fffff800`017c7c15 4881ecd0000000  sub     rsp,0D0h

fffff800`017c7c1c 8bc1            mov     eax,ecx

fffff800`017c7c1e 458bf8          mov     r15d,r8d

fffff800`017c7c21 448944245c      mov     dword ptr [rsp+5Ch],r8d

fffff800`017c7c26 83e044          and     eax,44h


함수에 진입하자마자 push를 3번하면서 스택을 약간 사용하고 이 함수가 사용할 스택 크기를 확보하는 코드가 있다.

여기에 상위함수가 ExAllocatePoolWithTag를 호출하면서 자신의 리턴 주소를 push하는 것까지만 추가로 고려하면 함수 호출시에 사용된 스택 크기를 알 수 있다.

현재 rsp에서 이 크기를 계산해 주면 상위 함수의 rsp가 나온다.


따라서 상위 함수의 기준 rsp를 정확히 구하는 식은 다음과 같다.


stacksize = 8 (리턴주소 push) + 8*3 (push 3번) + 0D0h (rsp에서 빼준 값)

상위 함수 rsp = 현재 함수 rsp + stacksize 


ExAllocatePoolWithTag의 stacksize 계산 결과는 0F0h다.

다음과 같이 계산할 수 있다.


kd> ? fffff880`009adeb0 + F0

Evaluate expression: -8246327058528 = fffff880`009adfa0


계산된 fffff880`009adfa0는 콜스택을 확인해 보면 두번째 함수인 RtlpInheritAcl의 Chile-SP와 일치한다.


계속해서 같은 방법으로 계속 따라갈 수 있다.

기계적으로 한번 진행해 보자.


함수 프롤로그 보고...


kd> u nt!RtlpInheritAcl

nt!RtlpInheritAcl:

fffff800`019a6070 fff3            push    rbx

fffff800`019a6072 55              push    rbp

fffff800`019a6073 56              push    rsi

fffff800`019a6074 57              push    rdi

fffff800`019a6075 4881eca8000000  sub     rsp,0A8h

fffff800`019a607c 410fb6f1        movzx   esi,r9b

fffff800`019a6080 418bd8          mov     ebx,r8d

fffff800`019a6083 488bea          mov     rbp,rdx


stacksize를 계산한다.

stacksize = 8 + 8*4 + 0A8h = D0h


kd> ? fffff880`009adfa0 + D0

Evaluate expression: -8246327058320 = fffff880`009ae070


fffff880`009ae070은 RtlpNewSecurityObject의 Child-SP와 일치한다.


RtlpNewSecurityObject를 따라가 보자.


kd> u nt!RtlpNewSecurityObject

nt!RtlpNewSecurityObject:

fffff800`019a3570 fff3            push    rbx

fffff800`019a3572 55              push    rbp

fffff800`019a3573 57              push    rdi

fffff800`019a3574 4881ec70020000  sub     rsp,270h

fffff800`019a357b 488b055eb4e6ff  mov     rax,qword ptr [nt!_security_cookie (fffff800`0180e9e0)]

fffff800`019a3582 4833c4          xor     rax,rsp

fffff800`019a3585 4889842450020000 mov     qword ptr [rsp+250h],rax

fffff800`019a358d 488b8424d0020000 mov     rax,qword ptr [rsp+2D0h]


stacksize = 8 + 8*3 + 270h = 290h


kd> ? fffff880`009ae070 + 290

Evaluate expression: -8246327057664 = fffff880`009ae300


fffff880`009ae300은 ObpAssignSecurity의 Child-SP와 일치한다.


ObpAssignSecurity를 확인한다.


kd> u nt!ObpAssignSecurity

nt!ObpAssignSecurity:

fffff800`0197f640 48895c2410      mov     qword ptr [rsp+10h],rbx

fffff800`0197f645 48896c2418      mov     qword ptr [rsp+18h],rbp

fffff800`0197f64a 56              push    rsi

fffff800`0197f64b 57              push    rdi

fffff800`0197f64c 4154            push    r12

fffff800`0197f64e 4883ec50        sub     rsp,50h

fffff800`0197f652 488364247000    and     qword ptr [rsp+70h],0

fffff800`0197f658 4d8be0          mov     r12,r8


stacksize = 8 + 8*3 + 50h = 70h


kd> ? fffff880`009ae300 + 70

Evaluate expression: -8246327057552 = fffff880`009ae370


fffff880`009ae370은 ObInsertObjectEx의 Child-SP와 일치한다.


계속해서 ObInsertObjectEx를 따라가 본다.


kd> u nt!ObInsertObjectEx

nt!ObInsertObjectEx:

fffff800`0197e3a0 fff3            push    rbx

fffff800`0197e3a2 55              push    rbp

fffff800`0197e3a3 56              push    rsi

fffff800`0197e3a4 57              push    rdi

fffff800`0197e3a5 4154            push    r12

fffff800`0197e3a7 4155            push    r13

fffff800`0197e3a9 4156            push    r14

fffff800`0197e3ab 4157            push    r15

kd> u

nt!ObInsertObjectEx+0xd:

fffff800`0197e3ad 4881ec08020000  sub     rsp,208h

fffff800`0197e3b4 488b052506e9ff  mov     rax,qword ptr [nt!_security_cookie (fffff800`0180e9e0)]

fffff800`0197e3bb 4833c4          xor     rax,rsp

fffff800`0197e3be 48898424f0010000 mov     qword ptr [rsp+1F0h],rax

fffff800`0197e3c6 488bac2480020000 mov     rbp,qword ptr [rsp+280h]

fffff800`0197e3ce 4c8bf9          mov     r15,rcx

fffff800`0197e3d1 4533d2          xor     r10d,r10d

fffff800`0197e3d4 410fb647e8      movzx   eax,byte ptr [r15-18h]


이번엔 u 명령 한번으로는 sub rsp, 208h까지 보이지 않아서 u 명령을 한번 더 사용했다.

마찬가지로 모든 push와 sub rsp 명령에서 사용한 스택 사용량을 계산하면 된다.


stacksize = 8 + 8*8 + 208h = 250h


kd> ? fffff880`009ae370 + 250

Evaluate expression: -8246327056960 = fffff880`009ae5c0


fffff880`009ae5c0는 PspInsertThread의 Child-SP와 일치한다.



이 정도면 충분히 연습이 됐을 것 같다.

모두 같은 방법으로 따라간 것이고 계속해서 같은 방법으로 진행할 수 있다


너무 길어졌으므로 이에 대한 이론은 다음 포스팅에서 다루도록 하겠다.


반응형
Posted by GreeMate
WinDbg 디버깅2015. 1. 27. 23:05
반응형

얼마 전부터 Visual Studio를 실행할 때 거의 1~2분간 행이 걸려 있는 현상이 발생했었다.


뭐가 문제인지 어떻게 확인할까 하다가...

배운게 도둑질이라고 행이 걸려 있을 때 전체 덤프를 떠 버렸다. ^^


Windbg로 덤프를 열고 먼저 msdev.exe 프로세스를 찾아서 실행중인 스레드를 봤다.


0: kd> !process 0 3f msdev.exe

PROCESS fffffa800fce6b30

    SessionId: 1  Cid: 1a10    Peb: 7efdf000  ParentCid: 1268

    DirBase: 1dfa1c000  ObjectTable: fffff8a00862a110  HandleCount: 314.

    Image: MSDEV.EXE

    VadRoot fffffa80107d3c90 Vads 367 Clone 0 Private 12752. Modified 197443. Locked 0.

    DeviceMap fffff8a007ec5160

    Token                             fffff8a0157176f0

    ElapsedTime                       00:00:45.997

    UserTime                          00:00:00.000

    KernelTime                        00:00:00.062

    ...

        THREAD fffffa80126a0060  Cid 1a10.19d8  Teb: 000000007efdb000 Win32Thread: fffff900c2d53c20 RUNNING on processor 3

        Not impersonating

        DeviceMap                 fffff8a007ec5160

        Owning Process            fffffa800fce6b30       Image:         MSDEV.EXE

        Attached Process          N/A            Image:         N/A

        Wait Start TickCount      131878         Ticks: 0

        Context Switch Count      156311         IdealProcessor: 7                 LargeStack

        UserTime                  00:00:21.762

        KernelTime                00:00:13.041

*** ERROR: Module load completed but symbols could not be loaded for MSDEV.EXE

        Win32 Start Address MSDEV (0x000000000040155c)

        Stack Init fffff8800c441530 Current fffff8800c441210

        Base fffff8800c442000 Limit fffff8800c435000 Call fffff8800c441580

        Priority 9 BasePriority 8 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5


        Child-SP          RetAddr           Call Site

        fffff880`0c440680 fffff880`012aa404 Ntfs!NtfsFindPrefix+0x222

        fffff880`0c440730 fffff880`012a7af3 Ntfs!NtfsFindStartingNode+0x6e4

        fffff880`0c440800 fffff880`0128a149 Ntfs!NtfsCommonCreate+0x3d3

        fffff880`0c4409d0 fffff880`01140367 Ntfs!NtfsNetworkOpenCreate+0x119

        fffff880`0c440cb0 fffff880`011438ba fltmgr!FltpPerformFastIoCall+0x357

        fffff880`0c440d10 fffff880`01161a9f fltmgr!FltpPassThroughFastIo+0xda

        fffff880`0c440d50 fffff800`035d3c72 fltmgr!FltpFastIoQueryOpen+0x10f

        fffff880`0c440df0 fffff800`035cf878 nt!IopParseDevice+0x1249

        fffff880`0c440f50 fffff800`035d0a96 nt!ObpLookupObjectName+0x588

        fffff880`0c441040 fffff800`035aef66 nt!ObOpenObjectByName+0x306

        fffff880`0c441110 fffff800`032d2e53 nt!NtQueryAttributesFile+0x145

        fffff880`0c4413a0 00000000`7754168a nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffff880`0c4413a0)

        00000000`0008c9f8 00000000`73a2ae19 ntdll!NtQueryAttributesFile+0xa

        00000000`0008ca00 00000000`73a1d18f wow64!whNtQueryAttributesFile+0x91

        00000000`0008ca80 00000000`750c2776 wow64!Wow64SystemServiceEx+0xd7

        00000000`0008d340 00000000`73a1d286 wow64cpu!ServiceNoTurbo+0x2d

        00000000`0008d400 00000000`73a18a90 wow64!RunCpuSimulation+0xa

        00000000`0008d450 00000000`739e2c52 wow64!Wow64KiUserCallbackDispatcher+0x204

        00000000`0008d7a0 00000000`775411f5 wow64win!whcbfnDWORD+0xe2

        00000000`0008e190 00000000`739efe4a ntdll!KiUserCallbackDispatcherContinue (TrapFrame @ 00000000`0008e058)

        00000000`0008e218 00000000`739caf02 wow64win!ZwUserMessageCall+0xa

        00000000`0008e220 00000000`739e281f wow64win!whNT32NtUserMessageCallCB+0x32

        00000000`0008e270 00000000`739cb03e wow64win!Wow64DoMessageThunk+0x8b

        00000000`0008e2b0 00000000`73a1d18f wow64win!whNtUserMessageCall+0x12e

        00000000`0008e350 00000000`750c2776 wow64!Wow64SystemServiceEx+0xd7

        00000000`0008ec10 00000000`73a1d286 wow64cpu!ServiceNoTurbo+0x2d

        00000000`0008ecd0 00000000`73a1c69e wow64!RunCpuSimulation+0xa

        00000000`0008ed20 00000000`77534966 wow64!Wow64LdrpInitialize+0x42a

        00000000`0008f270 00000000`77531937 ntdll!LdrpInitializeProcess+0x17e3

        00000000`0008f760 00000000`7751c34e ntdll! ?? ::FNODOBFM::`string'+0x28ff0

        00000000`0008f7d0 00000000`00000000 ntdll!LdrInitializeThunk+0xe



나머지 스레드들은 모두 대기중이라 크게 의미 없었다.

현재 실행중인 스레드를 보니 NTFS에서 어떤 파일을 처리중인데 NtQueryAttributesFile 요청으로부터 진행됐다.
이 파일이 뭔지가 궁금했는데 64비트라 살짝 난감했다. 파라미터를 확인할 수 있을지 없을지 몰라서...ㅋㅋ

그래도 궁금해서 NtQueryAttributesFile의 첫번째 파라미터를 찾아보기로 했다.


1. 파라미터가 저장된 스택 영역 찾기

NtQueryAttributesFile의 함수 원형은 다음과 같고 우리가 관심있는 파라미터는 파일 이름이 들어 있는 첫번째 파라미터 ObjectAttributes다.

NTSTATUS NtQueryAttributesFile(
  _In_   POBJECT_ATTRIBUTES ObjectAttributes,
  _Out_  PFILE_BASIC_INFORMATION FileInformation
);


이제 NtQueryAttributesFile의 코드를 좀 살펴 보자.

0: kd> uf nt!NtQueryAttributesFile

nt!NtQueryAttributesFile:

fffff800`035aee20 48895c2418      mov     qword ptr [rsp+18h],rbx

fffff800`035aee25 56              push    rsi

fffff800`035aee26 57              push    rdi

fffff800`035aee27 4154            push    r12

fffff800`035aee29 4881ec70020000  sub     rsp,270h

fffff800`035aee30 488b05a9fbe9ff  mov     rax,qword ptr [nt!_security_cookie (fffff800`0344e9e0)]

fffff800`035aee37 4833c4          xor     rax,rsp

fffff800`035aee3a 4889842460020000 mov     qword ptr [rsp+260h],rax

fffff800`035aee42 488bda          mov     rbx,rdx

fffff800`035aee45 488bf1          mov     rsi,rcx  ; 첫번째 파라미터 저장 (rsi <= rcx)

fffff800`035aee48 65488b042588010000 mov   rax,qword ptr gs:[188h]

fffff800`035aee51 0fb6b8f6010000  movzx   edi,byte ptr [rax+1F6h]

fffff800`035aee58 4084ff          test    dil,dil

fffff800`035aee5b 7432            je      nt!NtQueryAttributesFile+0x6e (fffff800`035aee8f)


nt!NtQueryAttributesFile+0x3d:

fffff800`035aee5d 488bca          mov     rcx,rdx

fffff800`035aee60 f6c207          test    dl,7

fffff800`035aee63 750e            jne     nt!NtQueryAttributesFile+0x53 (fffff800`035aee73)


nt!NtQueryAttributesFile+0x45:

fffff800`035aee65 488b0594c1f5ff  mov     rax,qword ptr [nt!MmUserProbeAddress (fffff800`0350b000)]

fffff800`035aee6c 483bd0          cmp     rdx,rax

fffff800`035aee6f 7308            jae     nt!NtQueryAttributesFile+0x58 (fffff800`035aee79)


nt!NtQueryAttributesFile+0x51:

fffff800`035aee71 eb09            jmp     nt!NtQueryAttributesFile+0x5b (fffff800`035aee7c)


nt!NtQueryAttributesFile+0x53:

fffff800`035aee73 e818070b00      call    nt!ExRaiseDatatypeMisalignment (fffff800`0365f590)

fffff800`035aee78 cc              int     3


nt!NtQueryAttributesFile+0x58:

fffff800`035aee79 488bc8          mov     rcx,rax


nt!NtQueryAttributesFile+0x5b:

fffff800`035aee7c 0fb601          movzx   eax,byte ptr [rcx]

fffff800`035aee7f 8801            mov     byte ptr [rcx],al

fffff800`035aee81 0fb64127        movzx   eax,byte ptr [rcx+27h]

fffff800`035aee85 884127          mov     byte ptr [rcx+27h],al

fffff800`035aee88 eb05            jmp     nt!NtQueryAttributesFile+0x6e (fffff800`035aee8f)


nt!NtQueryAttributesFile+0x6e:

fffff800`035aee8f 41bcb8000000    mov     r12d,0B8h

fffff800`035aee95 4d8bc4          mov     r8,r12

fffff800`035aee98 33d2            xor     edx,edx

fffff800`035aee9a 488d4c2450      lea     rcx,[rsp+50h]

fffff800`035aee9f e8ec6ed2ff      call    nt!memset (fffff800`032d5d90)

fffff800`035aeea4 41bb08000000    mov     r11d,8

fffff800`035aeeaa 6644895c2450    mov     word ptr [rsp+50h],r11w

fffff800`035aeeb0 664489642452    mov     word ptr [rsp+52h],r12w

fffff800`035aeeb6 b807000000      mov     eax,7

fffff800`035aeebb 6689842496000000 mov     word ptr [rsp+96h],ax

fffff800`035aeec3 c78424a800000001000000 mov dword ptr [rsp+0A8h],1

fffff800`035aeece c784249000000000402000 mov dword ptr [rsp+90h],204000h

fffff800`035aeed9 48899c24b0000000 mov     qword ptr [rsp+0B0h],rbx

fffff800`035aeee1 488d842410010000 lea     rax,[rsp+110h]

fffff800`035aeee9 48898424b8000000 mov     qword ptr [rsp+0B8h],rax

fffff800`035aeef1 c68424d100000001 mov     byte ptr [rsp+0D1h],1

fffff800`035aeef9 488d842450010000 lea     rax,[rsp+150h]

fffff800`035aef01 48898424d8000000 mov     qword ptr [rsp+0D8h],rax

fffff800`035aef09 4889b42480000000 mov     qword ptr [rsp+80h],rsi     <= rsi 값을 스택 영역에 저장

fffff800`035aef11 c78424e000000020000000 mov dword ptr [rsp+0E0h],20h

fffff800`035aef1c 65488b042588010000 mov   rax,qword ptr gs:[188h]

fffff800`035aef25 48ff8030030000  inc     qword ptr [rax+330h]

fffff800`035aef2c 65ff042564220000 inc     dword ptr gs:[2264h]

fffff800`035aef34 488d442440      lea     rax,[rsp+40h]

fffff800`035aef39 4889442430      mov     qword ptr [rsp+30h],rax

fffff800`035aef3e 488d442450      lea     rax,[rsp+50h]

fffff800`035aef43 4889442428      mov     qword ptr [rsp+28h],rax

fffff800`035aef48 c744242080000000 mov     dword ptr [rsp+20h],80h

fffff800`035aef50 4533c9          xor     r9d,r9d

fffff800`035aef53 440fb6c7        movzx   r8d,dil

fffff800`035aef57 488b15eac0f5ff  mov     rdx,qword ptr [nt!IoFileObjectType (fffff800`0350b048)]

fffff800`035aef5e 488bce          mov     rcx,rsi    <= rsi 값을 첫번째 파라미터로 전달

fffff800`035aef61 e82a180200      call    nt!ObOpenObjectByName (fffff800`035d0790)



일단 64비트에서는 첫번째 파라미터를 rcx로 전달한다는 것을 알고 있으므로 첫번째 파라미터를 추적해 볼 수 있다. 
함수 진입부에서부터 ObOpenObjectByName을 호출할 때까지의 rcx 값의 이동을 추적하면 된다.

fffff800`035aee45에서 rcx값을 rsi에 저장한다.
fffff800`035aef09에서 rsi값을 [rsp+80h]에 저장한다.
fffff800`035aef5e에서 ObOpenObjectByName을 호출하기 위해 rsi를 첫번째 파라미터 rcx로 전달한다.

ObOpenObjectByName의 첫번째 파라미터도 POBJECT_ATTRIBUTES 이기 때문이다.

NTSTATUS STDCALL
ObOpenObjectByName(
  POBJECT_ATTRIBUTES ObjectAttributes,
  POBJECT_TYPE ObjectType,
  PVOID ParseContext,
  KPROCESSOR_MODE AccessMode,
  ACCESS_MASK DesiredAccess,
  PACCESS_STATE PassedAccessState,
  PHANDLE Handle);


fffff800`035aee45와 fffff800`035aef61 사이에서는 rsi값이 변하지 않기 때문에 이 구간에서 rsi는 NtQueryAttributesFile의 첫번째 파라미터인 POBJECT_ATTRIBUTES다.

fffff800`035aef09에서 rsi값을 [rsp+80h]에 저장해 놓았으므로 이 값을 찾으면 된다. 


2. 스택 기준 주소 찾기

NtQueryAttributesFile 내부 코드가 실행될 때 사용하던 rsp를 찾아야 하는데 이것은 콜스택에서 찾을 수 있다.

        Child-SP          RetAddr           Call Site

        ...

        fffff880`0c441040 fffff800`035aef66 nt!ObOpenObjectByName+0x306

        fffff880`0c441110 fffff800`032d2e53 nt!NtQueryAttributesFile+0x145



WinDbg 콜스택을 보면 친절하게 Child-SP라는 것을 보여주는데 이것이 함수에서 사용하는 rsp다.
따라서 NtQueryAttributesFile의 rsp는  fffff880`0c441110이다.

사실 rsp가 스택 포인터라 함수 내부에서 계속 변할 것이라고 생각했는데 NtQueryAttributesFile 함수 코드를 들여다보면 알 수 있듯이 함수 prolog에서 rsp를 설정하고 나면 함수 내부에서 rsp 값은 변경되지 않는다. 그래서 rsp는 마치 32비트의 ebp처럼 사용된다. 64비트 콜스택에서 왜 Child-SP라는 것을 보여주는지 잘 몰랐었는데 이제야 쓸모가 있는 정보였다는 것을 알게 되었다.


3. 파라미터 내용 확인

본론으로 돌아와서 [rsp+80]을 확인해 보자.

0: kd> dq fffff880`0c441110+80

fffff880`0c441190  00000000`0008d2e0 00000000`00000000

fffff880`0c4411a0  00070000`00200000 00000000`00000000

...


[rsp+80]에 저장된 00000000`0008d2e0이 POBJECT_ATTRIBUTES 다.



0: kd> dt _OBJECT_ATTRIBUTES

ntdll!_OBJECT_ATTRIBUTES

   +0x000 Length           : Uint4B

   +0x008 RootDirectory    : Ptr64 Void

   +0x010 ObjectName       : Ptr64 _UNICODE_STRING

   +0x018 Attributes       : Uint4B

   +0x020 SecurityDescriptor : Ptr64 Void

   +0x028 SecurityQualityOfService : Ptr64 Void



내용을 보면 각 필드의 값을 알 수 있다.


0: kd> dq 00000000`0008d2e0

00000000`0008d2e0  00000032`00000030 00000000`00000000

00000000`0008d2f0  00000000`0008d2d0 00000000`00000040

00000000`0008d300  00000000`00000000 00000000`00000000

...



필드를 대입해 보면 다음과 같다.


Length: 30

RootDirectory: 0

ObjectName: 00000000`0008d2d0

Attributes: 40

SecurityDescriptor: 0

SecurityQualityOfService: 0



_UNICODE_STRING 타입인 ObjectName 00000000`0008d2d0를 확인해 보면 다음과 같다.


0: kd> dt _UNICODE_STRING 00000000`0008d2d0

ntdll!_UNICODE_STRING

 "\??\D:\TEMP\Project\TestTool\dummy\dummy.cpp"

   +0x000 Length           : 0x5A

   +0x002 MaximumLength    : 0x21a

   +0x008 Buffer           : 0x00000000`04ffd1b8  "\??\D:\TEMP\Project\TestTool\dummy\dummy.cpp"




4. 문제의 원인


파일 경로를 보니 번뜩 머리를 스치는 것이 있다.


'아뿔싸... 저 경로는 현재 존재하지 않는 경로다.'


얼마전에 개발 장비를 변경하면서 개발 환경을 그대로 복사한게 문제였다.

Visual Studio의 임시 파일들을 그대로 복사해 왔는데 데이터 디스크(D:)는 새로 구성하는 바람에 D:\TEMP는 존재하지 않는다. 

존재하지 않는 경로의 파일을 읽어대려고 Visual Studio는 그렇게 버벅대고 있었나 부다.


Visual Studio의 임시 파일들을 모두 삭제하고 나니 행이 없어졌다.

만세!!!





반응형
Posted by GreeMate
WinDbg 디버깅2014. 12. 16. 09:24
반응형

나의 맥북 에어에서 자유롭게 Windows 커널 디버깅을 하고 싶었는데...

Parallels가 있으니 Windows 가상 머신 두개 띄우고 어찌 어찌 연결할 수 있지 않을까 해서 검색을 시작했는데...


지금 내게 딱 필요한 링크를 발견했다. ㅋㅋ

사뿐히 링크!


http://s3ize.blogspot.kr/2012/09/tip-os-x-windbg-windows-kernel-debugging_21.html


반응형
Posted by GreeMate
WinDbg 디버깅2014. 9. 2. 00:29
반응형

프로세스가 크래시될 때 유저모드 전체 덤프가 생성되도록 설정하는 방법이다.


1. Vista 이상


Vista부터 Windows Error Reporting이 생기면서 자동으로 MS에 덤프를 전송하게 됐는데...

정작 로컬 시스템에는 덤프를 남기지 않는다.


이런 경우 다음 레지스트리 설정을 통해 덤프를 남기도록 설정할 수 있다.


HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps

DumpFolder = %LOCALAPPDATA%\CrashDumps (원하는 경로로 변경해도 됨)

DumpType = 2     (2는 전체덤프)



자세한 설명은 다음 링크를 참고한다.

http://msdn.microsoft.com/ko-kr/library/windows/desktop/bb787181(v=vs.85).aspx



2. XP 이하


닥터 왓슨이 덤프를 남기지 못하는 경우가 있기 때문에 ntsd를 이용해 수동으로 설정해서 유저덤프를 얻는 방법도 있다.

크래시 발생시 실행 디버거를 다음과 같이 ntsd로 설정한다.


HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug

Debugger = ntsd -p %ld -e %ld -c ".dump /ma /u C:\Dumps\UserDump.dmp; q"


64비트 OS라면 32비트 프로세스를 위해 다음도 추가해 준다.


HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug

Debugger = ntsd -p %ld -e %ld -c ".dump /ma /u C:\Dumps\UserDump.dmp; q"


참고할 만한 링크는 다음과 같다.

http://support.citrix.com/article/ctx105888


반응형
Posted by GreeMate
WinDbg 디버깅2014. 8. 5. 23:40
반응형

WDK와 WinDbg를 이렇게 깔끔하게 제공하는 페이지가 있었다니...ㅋㅋ

http://msdn.microsoft.com/en-us/windows/hardware/hh852365

반응형
Posted by GreeMate
WinDbg 디버깅2014. 5. 14. 23:35
반응형

사용중이던 모듈이 갑자기 언로드되어 버린 후 해당 모듈의 함수가 호출되어 크래시가 발생하는 경우가 있다.


이런 경우 WinDbg는 콜스택을 다음과 같이 보여준다.


   1 Id: 15f0.45c Suspend: -1 Teb: 00007ff7`e7d6e000 Unfrozen

Child-SP RetAddr : Args to Child : Call Site

00000000`042cf890 00000000`00000000 : 00007ffb`083243a3 00000000`00000000 00000000`5c7bcabc 00000000`03949738 : <Unloaded_MyTest.dll>+0x117ae3


언로드된 이후의 상태라 어떤 함수인지 확인이 불가능하다.


이런 경우 해당 바이너리 파일과 심볼 파일을 가지고 있다면 WinDbg에 다시 로드해 분석을 진행할 수 있다.

먼저 C:\MyImage에 MyTest.dll, MyTest.pdb를 넣어 놓고 다음과 같이 심볼 경로, 이미지 경로를 설정해 준다.


0:000> .sympath+ C:\MyImage

0:000> .exepath+ C:\MyImage

 


다음으로 lm 명령으로 확인하면 언로드된 모듈임을 알 수 있다.

이 정보에서 로드 당시의 로드 주소를 찾을 수 있다.


0:000> lm

start             end                 module name

...


Unloaded modules:

...

00007ffb`011a0000 00007ffb`0133d000  MyTest.dll


.reload 명령을 다음과 같이 사용하면 WinDbg가 모듈을 로드해서 보여준다.


.reload 모듈이름=로드주소,모듈크기


lm 결과에서 start 주소와 end 주소가 나왔기 때문에 end - start로 모듈크기를 구할 수 있다.

다음과 같이 실행한다.


0:000> .reload MyTest.dll=00007ffb`011a0000,19D000


그리고 나서 콜스택을 다시 보면 다음과 같이 나온다.


   1 Id: 15f0.45c Suspend: -1 Teb: 00007ff7`e7d6e000 Unfrozen

Child-SP RetAddr : Args to Child : Call Site

00000000`042cf890 00000000`00000000 : 00007ffb`083243a3 00000000`00000000 00000000`5c7bcabc 00000000`03949738 : MyTest!CMyClass::~CMyClass+0x33 


이렇게 되면 소스에서 어떤 부분이 문제인지 바로 찾아 볼 수 있다.

커널 덤프의 드라이버에 대해서도 동일하게 적용된다.


반응형
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
WinDbg 디버깅2013. 11. 22. 21:46
반응형

커널 메모리가 손상되면 분석하기 매우 곤란한 경우가 많은데요.

그중에서 분석이 가능한 한 가지 상황에 대해 분석을 진행해 보겠습니다.


이번 분석의 초식은 "깨진 메모리 앞쪽 메모리를 의심하라" 입니다.


MyDrv.sys가 사용중인 메모리가 손상돼 발생한 덤프입니다.


0: kd> !analyze -v

*******************************************************************************

*                                                                             *

*                        Bugcheck Analysis                                    *

*                                                                             *

*******************************************************************************


DRIVER_IRQL_NOT_LESS_OR_EQUAL (d1)

An attempt was made to access a pageable (or completely invalid) address at an

interrupt request level (IRQL) that is too high.  This is usually

caused by drivers using improper addresses.

If kernel debugger is available get stack backtrace.

Arguments:

Arg1: 00000008, memory referenced

Arg2: 00000002, IRQL

Arg3: 00000000, value 0 = read operation, 1 = write operation

Arg4: 8c032fa5, address which referenced memory


Debugging Details:

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

READ_ADDRESS:  00000008 

CURRENT_IRQL:  2


FAULTING_IP: 

MyDrv!GetListEntry+85 

8c032fa5 3b5108          cmp     edx,dword ptr [ecx+8]


DEFAULT_BUCKET_ID:  WIN7_DRIVER_FAULT

BUGCHECK_STR:  0xD1

PROCESS_NAME:  SomeProcess.exe


TRAP_FRAME:  bdf939b4 -- (.trap 0xffffffffbdf939b4)

ErrCode = 00000000

eax=00000000 ebx=8c0344b0 ecx=00000000 edx=c339fdc0 esi=0101ed94 edi=bdf93b60

eip=8c032fa5 esp=bdf93a28 ebp=bdf93a4c iopl=0         nv up ei pl nz na pe cy

cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010207

MyDrv!GetListEntry+0x85:

8c032fa5 3b5108          cmp     edx,dword ptr [ecx+8] ds:0023:00000008=????????

Resetting default scope


LAST_CONTROL_TRANSFER:  from 8c032fa5 to 83248b7f


STACK_TEXT:  

bdf939b4 8c032fa5 badb0d00 c339fdc0 00000000 nt!KiTrap0E+0x1b3

bdf93a4c 8c03331d c339fdc0 a0fa8b08 bdf93a68 MyDrv!GetListEntry+0x85 

bdf93a5c 8c0325fc c339fdc0 bdf93a7c 8c0364e5 MyDrv!GetEntryTable+0xd 

...

bdf93c10 76e270f4 0101f08c 00020019 0101eda0 nt!KiSystemServicePostCall

WARNING: Frame IP not in any known module. Following frames may be wrong.

0101ef94 00000000 00000000 00000000 00000000 0x76e270f4


STACK_COMMAND:  kb


FOLLOWUP_IP: 

MyDrv!GetListEntry+85 

8c032fa5 3b5108          cmp     edx,dword ptr [ecx+8]


SYMBOL_STACK_INDEX:  1

SYMBOL_NAME:  MyDrv!GetListEntry+85

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: MyDrv

IMAGE_NAME:  MyDrv.sys


DEBUG_FLR_IMAGE_TIMESTAMP:  5099ff75

FAILURE_BUCKET_ID:  0xD1_MyDrv!GetListEntry+85

BUCKET_ID:  0xD1_MyDrv!GetListEntry+85

Followup: MachineOwner

---------




.trap 명령으로 죽은 시점을 복원합니다.


0: kd> .trap 0xffffffffbdf939b4

ErrCode = 00000000

eax=00000000 ebx=8c0344b0 ecx=00000000 edx=c339fdc0 esi=0101ed94 edi=bdf93b60

eip=8c032fa5 esp=bdf93a28 ebp=bdf93a4c iopl=0         nv up ei pl nz na pe cy

cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010207

MyDrv!GetListEntry+0x85:

8c032fa5 3b5108          cmp     edx,dword ptr [ecx+8] ds:0023:00000008=????????



GetListEntry 함수에서 죽었는데 변수를 살펴보면 pListEntry 가 NULL이어서 문제가 발생했습니다.


0: kd> dv

     pListHead = 0x8c038d00 [ 0x89e95988 - 0x864f3ea8 ]

     pListEntry = 0x00000000



pListEntry는 pListHead에서 연결되는 리스트이므로 pListHead를 살펴봅니다.


0: kd> dl 0x8c038d00 

8c038d00  89e95988 864f3ea8 8c038d08 8c038d08

89e95988  00000000 00000000 00000000 00000000



pListHead가 가리키는 89e95988의 내용은 더블 링크드 리스트라서 00000000이면 안되고 어떤 포인터가 들어 있어야 하는데 00000000 이 들어 있습니다.


기존에 있던 포인터 값이 0으로 덮어써진 것으로 추정됩니다.


0: kd> db 89e95988

89e95988  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

89e95998  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

89e959a8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

89e959b8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

89e959c8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

89e959d8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

89e959e8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

89e959f8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................



어떤 모듈이 이 영역을 덮어쓰는지 추적합니다.

일단 메모리 정보를 확인합니다.


0: kd> !pool 89e95988  

Pool page 89e95988 region is Nonpaged pool


89e95808 doesn't look like a valid small pool allocation, checking to see

if the entire page is actually part of a large page allocation...


89e95808 is freed (or corrupt) pool

Bad previous allocation size @89e95808, last size was 0


...


Pool page [ 89e95000 ] is __inVALID.


Analyzing linked list...

[ 89e95000 ]: invalid previous size [ 0x43 ] should be [ 0x0 ]

[ 89e95000 --> 89e95c10 (size = 0xc10 bytes)]: Corrupt region


Scanning for single bit errors...

None found



메모리 헤더까지 손상되어 정보가 나오지 않습니다.


바로 앞 페이지의 정보를 확인합니다. 

보통은 앞의 메모리를 사용하는 모듈이 뒤 부분의 메모리를 손상시키기 때문입니다.


0: kd> !pool 89e94378 

Pool page 89e94378 region is Nonpaged pool

*89e89000 : large page allocation, Tag is Ddk , size is 0xc808 bytes

Pooltag Ddk  : Default for driver allocated memory (user's of ntddk.h)



Ddk 태그를 가진 메모리가 확인됩니다. 이 부분은 손상되지 않았습니다.

이 메모리를 사용하는 모듈이 메모리를 손상했을 가능성이 높습니다.


89e89000 메모리를 들여다보면 특정한 패턴을 가지고 있고 이 패턴이 0xc808 길이를 넘어서 이어집니다.

따라서 이 메모리를 사용하던 모듈이 메모리 경계를 넘어서 메모리를 손상시켰다고 가정할 수 있습니다.


Ddk는 디폴트 태그라서 태그로는 어떤 모듈이 사용중인지 알 수 없으니...

시스템에 로드된 모듈중에서 89e89000 라는 메모리 포인터를 가지고 있는 모듈을 검색합니다.


0: kd> !for_each_module s @#Base @#End 00 90 e8 89

Page 69a9 not present in the dump file. Type ".hh dbgerr004" for details

Page 6414d not present in the dump file. Type ".hh dbgerr004" for details

Page 63b60 not present in the dump file. Type ".hh dbgerr004" for details

bebb5424  00 90 e8 89 21 00 00 00-00 00 00 00 01 00 00 00  ....!...........



다행히 bebb5424 하나만 찾아지네요.

bebb5424 위치에서 89e89000 포인터를 보관하고 있습니다.


bebb5424 은 어떤 모듈인지 확인합니다.


0: kd> u bebb5424

NotMyDrv+0x6424:

bebb5424 0090e8892100    add     byte ptr [eax+2189E8h],dl

bebb542a 0000            add     byte ptr [eax],al

bebb542c 0000            add     byte ptr [eax],al

bebb542e 0000            add     byte ptr [eax],al

bebb5430 0100            add     dword ptr [eax],eax

bebb5432 0000            add     byte ptr [eax],al

bebb5434 0800            or      byte ptr [eax],al

bebb5436 0000            add     byte ptr [eax],al



NotMyDrv.sys 모듈의 내부로 나옵니다.

NotMyDrv.sys 내부의 어떤 위치인지 확인합니다.


0: kd> lmvm NotMyDrv

start    end        module name

bebaf000 bebbe780   NotMyDrv     (no symbols)           

    Loaded symbol image file: NotMyDrv.sys

    Image path: \??\C:\Windows\system32\Drivers\NotMyDrv.sys

    Image name: NotMyDrv.sys


0: kd> !dh bebaf000


File Type: EXECUTABLE IMAGE

FILE HEADER VALUES

     14C machine (i386)

       5 number of sections

4D880A01 time date stamp Tue Mar 22 11:31:29 2011


       0 file pointer to symbol table

       0 number of symbols

      E0 size of optional header

     10E characteristics

            Executable

            Line numbers stripped

            Symbols stripped

            32 bit word machine


OPTIONAL HEADER VALUES

     10B magic #

    6.00 linker version

    6300 size of code

    9200 size of initialized data

       0 size of uninitialized data

    50D3 address of entry point

     280 base of code

         ----- new -----

00010000 image base

      80 section alignment

      80 file alignment

       1 subsystem (Native)

    5.00 operating system version

    5.00 image version

    1.10 subsystem version

    F780 size of image

     280 size of headers

   16350 checksum

00040000 size of stack reserve

00001000 size of stack commit

00100000 size of heap reserve

00001000 size of heap commit

       0  DLL characteristics

       0 [       0] address [size] of Export Directory

    E680 [      3C] address [size] of Import Directory

    EC00 [     4A8] address [size] of Resource Directory

       0 [       0] address [size] of Exception Directory

       0 [       0] address [size] of Security Directory

    F100 [     4F8] address [size] of Base Relocation Directory

     370 [      1C] address [size] of Debug Directory

       0 [       0] address [size] of Description Directory

       0 [       0] address [size] of Special Directory

       0 [       0] address [size] of Thread Storage Directory

       0 [       0] address [size] of Load Configuration Directory

       0 [       0] address [size] of Bound Import Directory

     280 [      F0] address [size] of Import Address Table Directory

       0 [       0] address [size] of Delay Import Directory

       0 [       0] address [size] of COR20 Header Directory

       0 [       0] address [size] of Reserved Directory



SECTION HEADER #1

   .text name

    5D18 virtual size

     280 virtual address

    5D80 size of raw data

     280 file pointer to raw data

       0 file pointer to relocation table

       0 file pointer to line numbers

       0 number of relocations

       0 number of line numbers

68000020 flags

         Code

         Not Paged

         (no align specified)

         Execute Read



Debug Directories(1)

     Type       Size     Address  Pointer

     cv           8d           0     f780 [Debug data not mapped]



SECTION HEADER #2

   .data name

    8680 virtual size

    6000 virtual address

    8680 size of raw data

    6000 file pointer to raw data

       0 file pointer to relocation table

       0 file pointer to line numbers

       0 number of relocations

       0 number of line numbers

C8000040 flags

         Initialized Data

         Not Paged

         (no align specified)

         Read Write


SECTION HEADER #3

    INIT name

     556 virtual size

    E680 virtual address

     580 size of raw data

    E680 file pointer to raw data

       0 file pointer to relocation table

       0 file pointer to line numbers

       0 number of relocations

       0 number of line numbers

E2000020 flags

         Code

         Discardable

         (no align specified)

         Execute Read Write


SECTION HEADER #4

   .rsrc name

     4A8 virtual size

    EC00 virtual address

     500 size of raw data

    EC00 file pointer to raw data

       0 file pointer to relocation table

       0 file pointer to line numbers

       0 number of relocations

       0 number of line numbers

42000040 flags

         Initialized Data

         Discardable

         (no align specified)

         Read Only


SECTION HEADER #5

  .reloc name

     61E virtual size

    F100 virtual address

     680 size of raw data

    F100 file pointer to raw data

       0 file pointer to relocation table

       0 file pointer to line numbers

       0 number of relocations

       0 number of line numbers

42000040 flags

         Initialized Data

         Discardable

         (no align specified)

         Read Only




bebb5424은 bebaf000 + 6000 에서 시작하는 data SECTION에 위치하는 메모리입니다.

NotMyDrv.sys의 데이터 영역에 있으므로 이 드라이버에서 사용하는 전역변수로 추정됩니다.


결국 NotMyDrv.sys라는 드라이버가 MyDrv.sys가 사용하는 메모리를 overwrite해서 발생하는 문제로 볼 수 있습니다.



반응형
Posted by GreeMate
WinDbg 디버깅2013. 7. 9. 11:01
반응형

1. 문제 현상


프로세스 오브젝트가 손상되어 Bugcheck 0x3B가 발생하는 경우를 맞이하게 되어 정리해 본다.

Bugcheck의 내용은 다음과 같다.


0: kd> !analyze -v

*******************************************************************************

*                                                                             *

*                        Bugcheck Analysis                                    *

*                                                                             *

*******************************************************************************


SYSTEM_SERVICE_EXCEPTION (3b)

An exception happened while executing a system service routine.

Arguments:

Arg1: 00000000c0000005, Exception code that caused the bugcheck

Arg2: fffff80002f6df33, Address of the instruction which caused the bugcheck

Arg3: fffff88003ae63c0, Address of the context record for the exception that caused the bugcheck

Arg4: 0000000000000000, zero.


Debugging Details:

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


EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - 0x%08lx


FAULTING_IP: 

nt!ObInsertObjectEx+193

fffff800`02f6df33 4c8b6320        mov     r12,qword ptr [rbx+20h]


CONTEXT:  fffff88003ae63c0 -- (.cxr 0xfffff88003ae63c0)

rax=fffffa8001b2c8e0 rbx=0000000000000001 rcx=0000000000000000

rdx=0000000000000000 rsi=fffff980000b6f30 rdi=fffff88003ae7840

rip=fffff80002f6df33 rsp=fffff88003ae6da0 rbp=0000000000000000

 r8=0000000002000000  r9=0000000000000000 r10=0000000000000000

r11=fffff80002c09000 r12=0000000000000000 r13=0000000000000001

r14=0000000000000000 r15=fffff98028094b30

iopl=0         nv up ei ng nz na pe nc

cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00010282

nt!ObInsertObjectEx+0x193:

fffff800`02f6df33 4c8b6320        mov     r12,qword ptr [rbx+20h] ds:002b:00000000`00000021=????????????????

Resetting default scope


DEFAULT_BUCKET_ID:  VISTA_DRIVER_FAULT


BUGCHECK_STR:  0x3B


PROCESS_NAME:  MyApp.exe


CURRENT_IRQL:  2


LAST_CONTROL_TRANSFER:  from fffff80002f2ee8f to fffff80002f6df33


STACK_TEXT:  

fffff880`03ae6da0 fffff800`02f2ee8f : ffffffff`ffffffff 00000000`00000000 00000000`00000000 fffff980`28094b30 : nt!ObInsertObjectEx+0x193

fffff880`03ae6ff0 fffff800`02f2d5be : fffff980`28094b00 fffff980`18986e30 fffff880`02000000 fffff880`03ae76f0 : nt!PspInsertProcess+0x23f

fffff880`03ae7080 fffff800`02c87453 : 00000000`00000000 0000007f`ffffffff 00000000`00000000 00000980`00000000 : nt!NtCreateUserProcess+0x6c9

fffff880`03ae7bb0 00000000`77011dea : 00000000`7471b9eb 00000000`001740c0 00000000`009ad368 00000000`0008eab0 : nt!KiSystemServiceCopyEnd+0x13

00000000`0008dd38 00000000`7471b9eb : 00000000`001740c0 00000000`009ad368 00000000`0008eab0 00000000`0008eab0 : ntdll!NtCreateUserProcess+0xa

00000000`0008dd40 00000000`747313e7 : 00000000`0008e268 00000000`009ad368 00000000`009ad9d8 00000000`0008eab0 : wow64!Wow64NtCreateUserProcess+0x15f

00000000`0008e130 00000000`7471cf87 : 00000000`00000000 00000000`7efdb000 00000000`7efdd000 00000000`74730bf8 : wow64!whNtCreateUserProcess+0x7ef

00000000`0008e350 00000000`74852776 : 00000000`7547977a 00000000`74710023 00000000`00000246 00000000`009af01c : wow64!Wow64SystemServiceEx+0xd7

00000000`0008ec10 00000000`7471d07e : 00000000`00000000 00000000`74851920 00000000`0008eea0 00000000`76feecd1 : wow64cpu!TurboDispatchJumpAddressEnd+0x2d

00000000`0008ecd0 00000000`7471c549 : 00000000`00000000 00000000`00000000 00000000`74714ac8 00000000`7ffe0030 : wow64!RunCpuSimulation+0xa

00000000`0008ed20 00000000`77004956 : 00000000`00173c20 00000000`00000000 00000000`770f2670 00000000`770c5978 : wow64!Wow64LdrpInitialize+0x429

00000000`0008f270 00000000`77001a17 : 00000000`00000000 00000000`77004061 00000000`0008f820 00000000`00000000 : ntdll!LdrpInitializeProcess+0x17e4

00000000`0008f760 00000000`76fec32e : 00000000`0008f820 00000000`00000000 00000000`7efdf000 00000000`00000000 : ntdll! ?? ::FNODOBFM::`string'+0x29220

00000000`0008f7d0 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!LdrInitializeThunk+0xe


FOLLOWUP_IP: 

nt!ObInsertObjectEx+193

fffff800`02f6df33 4c8b6320        mov     r12,qword ptr [rbx+20h]


SYMBOL_STACK_INDEX:  0

SYMBOL_NAME:  nt!ObInsertObjectEx+193

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: nt

IMAGE_NAME:  ntkrnlmp.exe

DEBUG_FLR_IMAGE_TIMESTAMP:  4fa390f3

STACK_COMMAND:  .cxr 0xfffff88003ae63c0 ; kb

FAILURE_BUCKET_ID:  X64_0x3B_VRFK_nt!ObInsertObjectEx+193

BUCKET_ID:  X64_0x3B_VRFK_nt!ObInsertObjectEx+193


Followup: MachineOwner

---------



0: kd> .cxr 0xfffff88003ae63c0

rax=fffffa8001b2c8e0 rbx=0000000000000001 rcx=0000000000000000

rdx=0000000000000000 rsi=fffff980000b6f30 rdi=fffff88003ae7840

rip=fffff80002f6df33 rsp=fffff88003ae6da0 rbp=0000000000000000

 r8=0000000002000000  r9=0000000000000000 r10=0000000000000000

r11=fffff80002c09000 r12=0000000000000000 r13=0000000000000001

r14=0000000000000000 r15=fffff98028094b30

iopl=0         nv up ei ng nz na pe nc

cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00010282


nt!ObInsertObjectEx+0x193:

fffff800`02f6df33 4c8b6320        mov     r12,qword ptr [rbx+20h] ds:002b:00000000`00000021=????????????????



nt!ObInsertObjectEx에서 죽어버리는 문제다.


rbx=1 이었기 때문에 죽은 것인데 이 rbx 값은 프로세스 오브젝트의 헤더 + 20 위치에 있던 값이다.

프로세스 오브젝트의 포인터가  r15에 들어 있으므로 다음과 같이 확인할 수 있다.


0: kd> dt _OBJECT_HEADER fffff98028094b30-30

nt!_OBJECT_HEADER

   +0x000 PointerCount     : 0n4

   +0x008 HandleCount      : 0n0

   +0x008 NextToFree       : (null) 

   +0x010 Lock             : _EX_PUSH_LOCK

   +0x018 TypeIndex        : 0x7 ''

   +0x019 TraceFlags       : 0 ''

   +0x01a InfoMask         : 0x8 ''

   +0x01b Flags            : 0 ''

   +0x020 ObjectCreateInfo : 0x00000000`00000001 _OBJECT_CREATE_INFORMATION

   +0x020 QuotaBlockCharged : 0x00000000`00000001 Void

   +0x028 SecurityDescriptor : (null) 

   +0x030 Body             : _QUAD

   

ObjectCreateInfo의 1이 rbx로 전달되어 죽은 것이다.

ObjectCreateInfo는 1이 아니라 어떤 포인터가 들어 있어야 했다.

어째서 이런 값이 들어 갔는지는 덤프 분석을 통해서는 알 길이 없었다.


콜스택을 보고 프로세스가 생성되는 시점이라는 정도의 정보만 가늠할 수 있었다.


0: kd> k

  *** Stack trace for last set context - .thread/.cxr resets it

Child-SP          RetAddr           Call Site

fffff880`03ae6da0 fffff800`02f2ee8f nt!ObInsertObjectEx+0x193

fffff880`03ae6ff0 fffff800`02f2d5be nt!PspInsertProcess+0x23f

fffff880`03ae7080 fffff800`02c87453 nt!NtCreateUserProcess+0x6c9

fffff880`03ae7bb0 00000000`77011dea nt!KiSystemServiceCopyEnd+0x13

00000000`0008dd38 00000000`7471b9eb ntdll!NtCreateUserProcess+0xa

00000000`0008dd40 00000000`747313e7 wow64!Wow64NtCreateUserProcess+0x15f

00000000`0008e130 00000000`7471cf87 wow64!whNtCreateUserProcess+0x7ef

00000000`0008e350 00000000`74852776 wow64!Wow64SystemServiceEx+0xd7

00000000`0008ec10 00000000`7471d07e wow64cpu!TurboDispatchJumpAddressEnd+0x2d

00000000`0008ecd0 00000000`7471c549 wow64!RunCpuSimulation+0xa

00000000`0008ed20 00000000`77004956 wow64!Wow64LdrpInitialize+0x429

00000000`0008f270 00000000`77001a17 ntdll!LdrpInitializeProcess+0x17e4

00000000`0008f760 00000000`76fec32e ntdll! ?? ::FNODOBFM::`string'+0x29220

00000000`0008f7d0 00000000`00000000 ntdll!LdrInitializeThunk+0xe




2. 문제 원인


결국에는 100% 문제가 재현되는 시스템을 섭외해서 원인을 확인하고야 말았다.


ObjectCreateInfo가 1로 변하는 순간을 잡아 보니 MyDrv.sys가 ObOpenObjectByPointer()를 호출한 순간이었다.


0: kd> kL

Child-SP          RetAddr           Call Site

fffff880`03ae62f0 fffff800`02f7c5ec nt!ObpIncrementHandleCountEx+0x205

fffff880`03ae63b0 fffff800`02f6f4f1 nt!ObpCreateHandle+0x19c

fffff880`03ae64c0 fffff800`02f612cc nt!ObOpenObjectByPointerWithTag+0x109

fffff880`03ae66e0 fffff880`02f6c0e5 nt!ObOpenObjectByPointer+0x30

fffff880`03ae6730 fffff880`02f6c04d MyDrv!DoSomething+0x75

...

fffff880`03ae6cc0 fffff800`02f2c7da nt!PspSetupUserProcessAddressSpace+0x13b

fffff880`03ae6da0 fffff800`02f2d398 nt!PspAllocateProcess+0x11fa

fffff880`03ae7080 fffff800`02c87453 nt!NtCreateUserProcess+0x4a3

fffff880`03ae7bb0 00000000`77011dea nt!KiSystemServiceCopyEnd+0x13

   

커널 내부 함수인 ObpIncrementHandleCountEx() 내부에서 ObjectCreateInfo를 1로 만드는 것이었다!!!

OS 버그란 말인가?


ObOpenObjectByPointer()를 호출하는 것이 특별할 것도 없는데 왜 이런 일이 생기는지 조사해야 했다.

이 함수에 전달한 프로세스 오브젝트의 헤더를 살펴봤다.


0: kd> dt _OBJECT_HEADER fffff98028094b30-30

nt!_OBJECT_HEADER

   +0x000 PointerCount     : 0n2

   +0x008 HandleCount      : 0n0

   +0x008 NextToFree       : (null) 

   +0x010 Lock             : _EX_PUSH_LOCK

   +0x018 TypeIndex        : 0x7 ''

   +0x019 TraceFlags       : 0 ''

   +0x01a InfoMask         : 0x8 ''

   +0x01b Flags            : 0x1 ''  ; OB_FLAG_CREATE_INFO

   +0x020 ObjectCreateInfo : 0xfffffa80`02b26220 _OBJECT_CREATE_INFORMATION

   +0x020 QuotaBlockCharged : 0xfffffa80`02b26220 Void

   +0x028 SecurityDescriptor : (null) 

   +0x030 Body             : _QUAD


ObjectCreateInfo에는 1로 변경되기 전까지는 어떤 포인터가 존재하고 있었다.


Flags는 1인데 OB_FLAG_CREATE_INFO를 의미한다.

좀 찾아보니 오브젝트를 생성하고 있는 중이라는 뜻으로 보였다.

OB_FLAG_CREATE_INFO는 오브젝트 생성이 완료되고 나면 없어진다.


실제로 문제를 일으키는 ObpIncrementHandleCountEx() 함수를 따라 들어가다 보면 Flags가 1인 것을 확인하고 ObjectCreateInfo를 망가뜨리는 코드로 점프하는 것을 볼 수 있다.


즉, OB_FLAG_CREATE_INFO가 아직 존재할 때 ObOpenObjectByPointer()를 호출하면 OS에서 이를 제대로 처리 못하는 버그가 있는 것이다.



3. 부록


이 내용을 보더니 우리팀 배주임님이 검색해서 찾아준 링크

http://uninformed.org/index.cgi?v=1&a=5&p=5


내가 얼추 추정하고 지나갔던 것들을 상세하게 분석해서 명쾌하게 설명하고 있다.

아... 부(끄)러워라... *^^*


반응형
Posted by GreeMate