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