태그 : debugging
2008/12/17   Assembly code에 대한 고찰 (jump 명령어 그리고, pointer 연산)
2008/11/26   [Troubleshooting Tool] Gflags.exe
2008/11/25   [Troubleshooting Tool] Windbg
2008/11/21   [Troubleshooting Tool] Debug Diagnostic Tool
2008/11/04   NTDLL!KiUserExceptionDispatcher
Assembly code에 대한 고찰 (jump 명령어 그리고, pointer 연산)

아래는 x module ttt 라는 함수에 대한 disassemble code이다. 아래를 보고 몇 가지 특징을 잡아 보자.

 

x!ttt:

77f82f35 8b4c2408        mov     ecx,dword ptr [esp+8]

77f82f39 8b442404        mov     eax,dword ptr [esp+4]

77f82f3d 56              push    esi

77f82f3e 668b11          mov     dx,word ptr [ecx]

77f82f41 8d7002          lea     esi,[eax+2]

77f82f44 668910          mov     word ptr [eax],dx

77f82f47 eb08            jmp     x!ttt+0x12 (77f82f51)

 

x!ttt+0x19:

77f82f49 668b11          mov     dx,word ptr [ecx]

77f82f4c 668916          mov     word ptr [esi],dx

77f82f4f 46              inc     esi

77f82f50 46              inc     esi

 

x!ttt+0x12:

77f82f51 41              inc     ecx

77f82f52 41              inc     ecx

77f82f53 6685d2          test    dx,dx

77f82f56 75f1            jne     x!ttt+0x19 (77f82f49)

 

x!ttt+0x23:

77f82f58 5e              pop     esi

77f82f59 c3              ret

 

번째는 jmp 의한 loop 보인다. 상위의 노란색 block 이다.

일단, jmp 명령어에 의해서 77f82f51 분기한다. 그리고, jne 명령어에 의해서 77f82f49 분기될 있다. 결국, 아래 그림과 같이 looping 된다.

 

77f82f47 -------> 77f82f51 -------> 77f82f56 ------+

                         ^                         |

                         |                         |

                         +------77f82f49 <---------+

 

Loop 대한 end 조건을 찾아보면, 녹색 Block 이다.

 

77f82f53 6685d2          test    dx,dx

77f82f56 75f1            jne     x!ttt+0x19 (77f82f49)

 

test dx, dx dx null여부를 Check 하는 알려진 명령어이다. test a,b a&b 같이 AND 연산을 의미한다. 그러므로, dx 0x0 아니면 결과가 0x0 예상할 없다. 이후에 jne 연산을 하여 dx값이 0x0이 아닐 경우에 looping 돌고, 0x0이면 해당 loop 빠지는 condition으로 이뤄져 있음을 있다.

 

번째는 Frame 기반의 code 아닌 보인다. 이유는 assembly code 초기에 ebp(frame pointer) 저장 코드가 존재하지 않는 . 또한, 초기 code 보면,

77f82f35 8b4c2408        mov     ecx,dword ptr [esp+8]

77f82f39 8b442404        mov     eax,dword ptr [esp+4]

 

Esp 기반의 data ecx, eax 전달하는 것으로 보이며, 이는 parameter assign 으로 보인다. 이는 kv command로도 확인된다. 이는 FPO 이다.

 

ChildEBP RetAddr  Args to Child              

0006d950 0100e654 00d19fb0 0006e3b0 00c67fe0 x!ttt+0x1c (FPO: [2,0,1])

//

2 parameters

0 local variables

1 cpu register (??) – push esi

//

번째는 pointer 연산이 존재한다.

 

77f82f35 8b4c2408        mov     ecx,dword ptr [esp+8]

77f82f39 8b442404        mov     eax,dword ptr [esp+4]

77f82f3d 56              push    esi

77f82f3e 668b11          mov     dx,word ptr [ecx]

77f82f41 8d7002          lea     esi,[eax+2]

77f82f44 668910          mov     word ptr [eax],dx

 

상위의 노란 색을 보면, ecx parameter 전달된 address pointer 갖고 있으며, 해당 pointer 번째 value dx 저장한다. 그리고, dx 값을 eax address copy한다. Eax 역시 pointer이다.

 

Debugging , 중요한 것은 data 살피는 것인데, 상위의 code 경우는 stack 존재하는 data 유심히 살펴볼 필요가 있다. 만일, 아래의 위치에 break 되어 있다고 가정하면,

 

77f82f4c 668916          mov     word ptr [esi],dx

 

0:000> ddp esp

0006d950  00d19fb0 0042007b  <---- esp

0006d954  0100e654 5914438b  <---- esp+4

0006d958  00d19fb0 0042007b  <---- esp+8

0006d95c  0006e3b0 0042007b  <---- esp+c

 

mov ecx,dword ptr[esp+8] 에서 해당 parameter 처음 위치, parameter address 어떻게 될까? 00d19fb0 아니고, esp+c, 0006e3b0 맞다. 이유는 중간에 push esi 라는 명령어가 존재하기 때문에 esp 1 decrement 되었기 때문이다. 그러므로, esp+8, 00d19fb0 처음 eax mov parameter 처음 address 된다. Push/pop 따라 esp 변경되기 때문에 이를 유의하여 data check해야 한다.

 

Loop block 안의 code 아래와 같다.

 

77f82f49 668b11          mov     dx,word ptr [ecx]

77f82f4c 668916          mov     word ptr [esi],dx

77f82f4f 46              inc     esi

77f82f50 46              inc     esi

 

x!ttt+0x12:

77f82f51 41              inc     ecx

77f82f52 41              inc     ecx

 

보면, ecx에서 esi dx register temp copy 이뤄지고 있음을 있다. 그리고, inc 번씩 발생하는 , 이는 word 연산이기 때문에 그렇다. Word type data copy되고 pointer 증가하기 때문에 pointer++ inc 발생할 밖에 없다. 이는 아래의 명령어에서도 동일하게 추측된다.

 

77f82f3e 668b11          mov     dx,word ptr [ecx]

77f82f41 8d7002          lea     esi,[eax+2]

77f82f44 668910          mov     word ptr [eax],dx

 

lea 명령은 esi=eax+2 같은 pointer연산이라고 보면 되는 , address +2했다는 것은 word type data dx copy 하고, 다음 word 위치시키기 위해서, word 연산이기 때문에 +2 된다는 것을 있다.

 

그러므로, 해당 function 2개의 pointer source destination으로 받아서 copy하는 operation으로 보인다. 그리고, source data 0x0 때까지 copy 이뤄지는 구조로 이해할 있다.

//참고로

Jump 명령어는 Condition code flag 연관이 있다.



이어지는 내용
by 강세윤 | 2008/12/17 13:24 | Windows debugging | 트랙백 | 덧글(0)
[Troubleshooting Tool] Gflags.exe

Gflags.exe Windows System Diagnostics 를 위해 debugging tools for windows에서 제공하는 Tool이다.

상위의 그림만 봐서는 이해하기 어려울 수도 있지만, Application에 대한 System registry의 일부 설정을 통하여 메모리누수나 handle 누수를 tracking 하거나 pageheap enable 하여 Heap corruption Troubleshooting 할 수 있다.

 

메모리누수에 대한 Gflags 설정.


메모리누수를 Check하기 위해서는 gflags.exe UMDH(User Mode Dump Heap)이라는 Tool도 필요하다. UMDH 역시 Debugging tools for Windows에 포함되어 있는 Tool이다.

 

1)     Gflags에 보면, image file tab에서 Create user mode stack trace database라는 option enable 한다. (그전에 image text box에 해당 image 이름을 입력해야 한다) Command 창에서는 gflags –i  CrashApp.exe +ust하고 실행하면 된다.

2)     설정이 되었는지는 레지스트리를 통해서 확인할 있는 ,  HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\CrashApp.exe 이동하여, key중에 REG_DWORD GlobalFlag 존재하는 확인하고, 해당 값이 0x1000으로 설정되어 있으면 정상적으로 설정이 것이다.

3)     그리고, UDDH를 위해서 Symbol 설정을 해야 한다. Command창에서 SET _NT_SYMBOL_PATH=C:\LeakAppSymbols;SRV*c:\symbols*http://msdl.microsoft.com/download/symbols” 하여 환경변수로 설정하고,

4)     Application을 수행한 후, Leak 현상이 발생하기 전 해당 Command 창에

“umdh –p:PID > C:\start.txt” 를 수행한다.

5)     충분히 Leak 현상이 발생하고 나면, “umdh –p:PID > end.txt” command창에서 한번 더 수행을 하고,

6)     마지막으로 해당 command 창에서 umdh –d start.txt end.txt > result.txt” 를 수행하여 결과를 얻는 다.

 

결과 report Leak이전에 잡은 Log와 이후에 잡은 Log를 통해서 차이가 발생하는 Allocation에 대한 결과를 다음과 같이 보여준다. Symbol이 맞아야 Callstack에서 Allocation이 발생하는 Code의 위치를 정확히 확인할 수 있다.

///////////////

+   20480 (  20480 -      0)      1 allocs  BackTrace1516DC

+       1 (      1 -      0)         BackTrace1516DC allocations

 

           ntdll!RtlTimeToElapsedTimeFields+00001963

           buggy!memoryLeak+00000036 (d:\myprog\dbgt\exe\buggy\buggy.cpp, 20)

           buggy!wmain+000000C1 (d:\myprog\dbgt\exe\buggy\buggy.cpp, 225)

           buggy!wmainCRTStartup+0000016A

           kernel32!BaseThreadInitThunk+00000012

           ntdll!RtlInitializeExceptionChain+00000063

           ntdll!RtlInitializeExceptionChain+00000036

 

 

Total increase ==  20480 requested +     24 overhead =  20504

/////////////

여기서 increase 20480 bytes 단위이다.

 

             Heap Corruption에 대한 gflags의 설정

Heap Code에서 사용하는 Memory Block으로 Application에서 메모리 할당이나 해제 시 잘못된 사용으로 인하여 Corruption이 발생할 수 있다. 일반적인 Heap corruption이 발생하는 경우는 다음과 같다.

²  Buffer overrun

²  Buffer underrun

²  Double Free

²  Free block에 대한 access

²  메모리 재사용에 대한 문제, (예를 들어, 메모리를 free하고 동일한 size를 재사용할 때, 동일한 address가 잡힐 것이라는 기대감에서 발생할 수 있는 문제. 등등

 

Heap Corruption 은 임의의 Heap block이 깨진 이후 다른 시점에 Allocation이나 Memory 사용시 예기치 않게 Access Violation이 발생할 수 있기 때문에 AV 순간 Crash dump를 수집한다고 해도 해당 Callstack이 문제 근원이 되는 Callstack이 아닐 가능성이 매우 크다. 이를 위해서 Gflags allocation Check하도록 설정하여 잘못된 사용시 이를 Debugger를 통해 알려줄 수 있도록 도와준다.

 

-       Normal Pageheap

²  free될 때만 allocation check

²  gflags –p /enable CrashApp.exe

²  High Memory usageprocess를 위해서는 “gflags.exe –p /enable lsass.exe /decommit /random 20” 를 이용할 수 있다. random 2020% pageheapenable 된다는 의미.

 

-       Full Pageheap

²   No access pages를 사용하여 Allocation block padding

²  gflags –p /enable CrashApp.exe /full

²  만일, /decommit option을 이용하면, Memory1/2 로 줄일 수 있다.

n  gflags –p /enable CrashApp.exe /full /decommit

n  decommit No access pages 대신에 reserved virtual memory zone을 이용함.

 

Heap이 깨졌다는 것은 어떻게 알까? 먼저, AV crash dump를 확인하고, Heap corruption 여부를 확인하여 다시 Crash dump를 수집하게 된다. 간혹, 다음과 같은 Function stack에서 AV가 발생하는 것을 볼 수 있다.


ntdll!RtlpCoalesceFreeBlocks+0x10c
ntdll!RtlFreeHeap+0x142
. . .


또는


ntdll!RtlpCoalesceFreeBlocks+0x10c
ntdll!RtlpExtendHeap+0x1c1
ntdll!RtlAllocateHeap+0x3b6
. . .


그리고, 문제가 발생한 AddressheapCheck해봤을 때 (“!heap –x <address>” command를 수행) Heap block size가 이상하거나 bad pointer(garbage allocation data 또는 알 수 없는 영역)로 의심이 간다면, Heap corruption을 조심스레 의심해 볼 수 있다.

 

Pageheap에 따른 Heap block의 구조는 아래의 문서를 참조한다. 중요한 것은 Block headersuffix bytes 인데, 이는 Heap blockAllocated된 것인지 Free된 것인지를 확인할 수 있는 정보가 된다.

//// http://msdn.microsoft.com/en-us/library/cc266933.aspx발췌...

The following diagrams show the arrangement of heap blocks. These are valid in Windows 2000 (Service Pack 1 and later), Windows XP, and later versions of Windows.

Light page heap block — allocated:

 +-----+---------------+---+                                  
 |     |               |   |                                  
 +-----+---------------+---+                                  
    ^         ^          ^                                    
    |         |          8 suffix bytes (filled with 0xA0)    
    |         User allocation (filled with E0 if zeroing not requested) 
    Block header (starts with 0xABCDAAAA and ends with 0xDCBAAAAA) 

Light page heap block freed:

 +-----+---------------+---+                                  
 |     |               |   |                                  
 +-----+---------------+---+                                  
    ^         ^          ^                                    
    |         |          8 suffix bytes (filled with 0xA0)    
    |         User allocation (filled with F0 bytes)          
    Block header (starts with 0xABCDAAA9 and ends with 0xDCBAAA9) 

Full page heap block — allocated:

 +-----+---------+---+-------                                 
 |     |         |   |  ... N/A page                          
 +-----+---------+---+-------                                 
    ^       ^      ^                                          
    |       |      0-7 suffix bytes (filled with 0xD0)        
    |       User allocation (if zeroing not requested, filled   
            with E0 in Windows 2000 and C0 in Windows XP)       
    Block header (starts with 0xABCDBBBB and ends with 0xDCBABBBB) 

Full page heap block — freed:

 +-----+---------+---+-------                                 
 |     |         |   |  ... N/A page                          
 +-----+---------+---+-------                                 
    ^       ^      ^                                          
    |       |      0-7 suffix bytes (filled with 0xD0)        
    |       User allocation (filled with F0 bytes)            
    Block header (starts with 0xABCDBBA and ends with 0xDCBABBBA) 

/////

추가적으로 Handle leak의 경우도 gflags 를 이용하여 Troubleshooting 할 수 있다. 참조:http://byung.egloos.com/4481638

by 강세윤 | 2008/11/26 18:01 | Windows debugging | 트랙백 | 덧글(0)
[Troubleshooting Tool] Windbg

Windbg는 그야 말로 Debugger Tool이다. 메모리 덤프를 수집할 수도 있으며, 수집된 Dump를 분석하는 데 도움이 되는 Tool이다.

 

먼저, 메모리 덤프 수집을 위해서는 다음과 같은 방법이 존재한다.

 

AeDebug Dr. Wastn 대신에 다음과 같이 WinDbg를 설정할 수 있다.

 

windbg -p %ld -e %ld -g

 

그리고, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options Application 이름을 key로 추가하고, 문자열 “debugger” REG_SZ 역시 추가, Windbg.exe path를 입력하여, 아래 그림 참고, Application 수행 시 Windbg attach 할 수 있다. 이 경우는 Service Application의 수행 시 문제가 발생하는 경우에 유용하다.

Windbg의 메뉴에는 Open Executable… 을 제공하는 데, 이는 Windbg를 통해서 Application launch 시킬 수 있는 메뉴이다. 또한 실행 중인 Process Windbg Attach 할 수 있는 Attach to a Process… 메뉴도 존재한다.

일단, Debugger Process Attach가 되면, 아래와 같은 화면을 Open된다. 익숙하지 않을 수 있겠지만, 하단 부분을 보면 Command 창이 존재하는 데, command 창에 명령어를 입력함으로써 Dump의 분석이나 수집이 가능하다.

일반적으로 command에서 ‘g’명령어를 입력하면, 해당 Application 2nd chance exception이 발생할 때, debug break이 되어 windbg에 알려준다. 이 경우에 ‘.dump’ command를 이용하여 Dump를 수집할 수 있다.

 

.dump /ma

 

MiniOption

Effect

a

Creates a minidump with all optional additions. The /ma option is equivalent to /mfFhut — it adds full memory data, handle data, unloaded module information, basic memory information, and thread time information to the minidump.

 

/a option mini dump를 수집할 때의 option인데, 만일 /ma 와 같이 수집한다면, mini dump에 추가적으로 full memory data 가 포함된다. 가장 흔한 option이며, /f option (Full Dump)보다 더 많은 정보를 가지고 있다.

 

AV(Access Violation)와 같은 명백한 2nd chance exception외에 C++ EH(E06D7363) 이나 CLR Exception(E0434F4D) 와 같은 1st Chance Exception에 대한 Dump를 수집하기 위해선 해당 Exception이 발생했을 때, debug break되도록 해야 할 필요가 있다. 이 경우에는 sxe command를 이용한다. 예를 들어,

 

sxe eh

sxe clr

 

또는 exception code를 직접입력 할 수도 있다.

 

sxe E06D7363

sxe E0434F4D

 

그리고, 이러한 설정을 다시 disable 하기 위해서‘sxd’를 이용한다.

 

물론, windbg debugger 이기 때문에 bp(break point)를 설정할 수 있다. 그러므로, 다음과 같이 conditional break point를 걸고, dump를 수집할 수도 있다.

 

bp mscorwks!RaiseTheException "j (poi(poi(poi(poi(esp+4))+8)+48) = 02000092)'.dump /ma /u OOM.dmp;g''gc' "

 

bp 다음의 mscorwks!RaiseTheException  address가 위치할 자리이다. Address 대신에 이렇게 function name을 사용할 수 있는 것은 적절한 symbol link되어 있기 때문인데, 처음 File menu에서 보았지만, symbol path를 설정할 수 있는 menu가 존재한다. Command를 통해서는 다음과 같이 설정할 수 있다.

 

.sympath+ srv*c:\pubsymbols*http://msdl.microsoft.com/download/symbols

.reload

 

적절한 symbolload되어 있는 것은 다음과 같이 확인할 수 있다. 일단, lm command를 확인하면, 아래와 같은 module list를 확인할 수 있다. Deferred 는 아직 symbolload되지 않았다는 의미이다.

 

0:001> lm

start    end        module name

00400000 00414000   CrashApp   (deferred)            

10000000 10018000   ItClient   (deferred)            

10200000 10287000   MSVCR71D   (deferred)            

6eb70000 6ec15000   IMETIP     (deferred)            

6ec50000 6ec72000   IMKRAPI    (deferred)            

6ec80000 6ecac000   IMJKAPI    (deferred)

. . . . . . . . . .

 

.reload /f command를 이용하여 강제로 load 시킨 후에 lm vm command를 이용하여 확인할 수 있다. 만일, 존재하지 않으면, no symbols, 그리고 맞지 않으면 m(ismatch) 이라고 표시되는 것을 알 수 있다. 외에 C (Checksum이 맞지 않는 경우), T(Timestamp 가 맞지 않는 경우) 로 표시되기도 한다. 정상적인 경우는 private 이나 public 이란 표시와 함께 Loaded symbol image file에 정확한 pdb file path를 확인할 수 있다.

 

0:000> .reload /f CrashApp.exe

0:000> lm vmCrashApp  

start    end        module name

00400000 00414000   CrashApp   (no symbols)          

    Loaded symbol image file: C:\APPS\CrashApp\Debug\CrashApp.exe

    Image path: C: \APPS\CrashApp\Debug\CrashApp.exe

    Image name: CrashApp.exe

    Timestamp:        Fri May 20 03:45:17 2005 (428CDEBD)

    CheckSum:         000239A4

    ImageSize:        00014000

    File version:     1.0.0.1

    Product version:  1.0.0.1

    File flags:       1 (Mask 3F) Debug

    File OS:          4 Unknown Win32

    File type:        1.0 App

    File date:        00000000.00000000

    Translations:     0409.04b0

    ProductName:      CrashApp Application

    InternalName:     CrashApp

    OriginalFilename: CrashApp.EXE

    ProductVersion:   1, 0, 0, 1

    FileVersion:      1, 0, 0, 1

    FileDescription:  CrashApp MFC Application

    LegalCopyright:   Copyright (C) 2001

 

Conditional break point 는 주로 (꼭 그런 것은 아니지만) function return value나 전달되는 parameter의 값을 이용하여 설정하는 경우가 많이 있다. 그러므로, 이러한 값들이 WinDbg를 통해서 어떻게 보여지는 지 아는 것이 중요하다. 다음은 이와 관련하여 stack walking을 살펴보고자 한다.

0:000> r

eax=00931b70 ebx=7ffd5000 ecx=00000001 edx=00931c10 esi=00000000 edi=00000000

eip=00401010 esp=0012ff30 ebp=0012ff34 iopl=0         nv up ei pl zr na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

Stackwalker!func1:

00401010 55              push    ebp

 

r register 를 확인하는 command 이다. 보면, eax .. 등등과 eip, esp 그리고, ebp의 정보를 확인할 수 있다. 현재, break이 된 위치는 오른쪽 source code에도 나와있지만, func1 을 호출하는 시점이다. Source code를 보면, func1에서 func2로 호출이 되는 구조인데, func2 2개의 parameter를 전달하고 있다. 이 경우에 사용하는 자료구조가 stack이다. Stack push/pop operation에 의해 function call에 필요한 local variablesparameters를 저장하거나 전달한다. 이러한 구조에서 stack의 모양은 어떻게 변할까?

Raw stack을 확인할 수 있는 명령어, dds 를 이용하여 local variablesparameters에 의해 사용되는 stack의 구조를 살펴보자. 현재, esp0012ff30 이다.

 

0:000> dds esp

0012ff30  00401008 Stackwalker!main+0x8

0012ff34  0012ff80

0012ff38  00401187 Stackwalker!__tmainCRTStartup+0x117

 

여기서 0012ff30 address가 가지고 있는 00401008값은 무엇일까? 일단, uf 명령어를 통해서 func1을 호출하는 main funcation에 대한 assembly를 살펴보자

 

0:000> uf Stackwalker!main

Stackwalker!main

   11 00401000 55              push    ebp

   11 00401001 8bec            mov     ebp,esp

   12 00401003 e808000000      call    Stackwalker!func1 (00401010)

   13 00401008 33c0            xor     eax,eax

   13 0040100a 5d              pop     ebp

   13 0040100b c3              ret

 

그러므로, func1을 호출하고 난 다음의 address , return address stack에 저장한다. 결국, call 명령과 동시에 return address stack에 저장되고, stack pointer는 감소된다. (stack push 동작에 의해 high address에서 low address로 이동한다.)

그리고 나서, ebp stack에 저장한다. 그리고, 현재의 esp ebp에 저장하여 새로운 Stack frame을 생성한다고 보면 된다.

 

0012ff2c  0012ff34   < ---- 현재의 esp:0012ff2c 에는 old ebp address를 저장.

0012ff30  00401008 Stackwalker!main+0x8

0012ff34  0012ff80

0012ff38  00401187 Stackwalker!__tmainCRTStartup+0x117

 

ebp stack chain 형태로 연결이 되어 있고, 이 값들을 통해서 stack walking이 가능한 것이다. 이를 기반으로 callstack을 재 구성할 수 있다. Kb command ebp 기반의 callstack을 보여준다.

 

0:000> kb

ChildEBP RetAddr  Args to Child             

0012ff2c 00401008 0012ff80 00401187 00000001 Stackwalker!func1+0x4

0012ff34 00401187 00000001 00931b70 00931c10 Stackwalker!main+0x8

0012ff80 0040105f 0012ff94 75834911 7ffd5000 Stackwalker!__tmainCRTStartup+0x117

0012ff88 75834911 7ffd5000 0012ffd4 76f2e4b6 Stackwalker!mainCRTStartup+0xf

0012ff94 76f2e4b6 7ffd5000 7692139b 00000000 kernel32!BaseThreadInitThunk+0xe

0012ffd4 76f2e489 00401050 7ffd5000 00000000 ntdll!__RtlUserThreadStart+0x23

0012ffec 00000000 00401050 7ffd5000 00000000 ntdll!_RtlUserThreadStart+0x1b

 

그리고 나서 source code를 보면, local variable int y 0 assign 한다. 이를 assembly code에서 살펴보면,

 

0:000> u Stackwalker!func1 eip+1

Stackwalker!func1

00401010 55              push    ebp

00401011 8bec            mov     ebp,esp

00401013 51              push    ecx

00401014 c745fc00000000  mov     dword ptr [ebp-4],0

 

Int y에 해당하는 address ebp-4 로 표현되고 있다. Local variable ebp-4, -8, -c 형태로 stack이 감소하면서 할당된다.

 

0:000> dds esp

0012ff28  00000000

0012ff2c  0012ff34   < ----- ebp

0012ff30  00401008 Stackwalker!main+0x8

0012ff34  0012ff80

0012ff38  00401187 Stackwalker!__tmainCRTStartup+0x117

 

그 다음은 func2를 호출해야 하는 데, parameters 2개가 필요한 것을 볼 수 있다. 이를 위해서 먼저 stack 2개의 parameters push 하는 동작을 한다. Assembly를 보면, 다음과 같다.

 

18 0040101b 6a0a            push    0Ah  ; 두번째 parameter: 10

   18 0040101d 8d45fc          lea     eax,[ebp-4]

   18 00401020 50              push    eax ; 첫번째 parameter : y ßlocal variable

   18 00401021 e80a000000      call    Stackwalker!func2 (00401030)

 

첫번째 parameter pointer인 것을 알 수 있는 데, address를 넘기기 위해서 ebp-4에 대한 address eax copy하고 eax stack에 저장하는 operation을 취하고 있다. 그리고, stack을 살펴보면, 다음과 같다.

 

0:000> dds esp

0012ff1c  00401026 Stackwalker!func1+0x16 < --- function call에 의한 ret address

0012ff20  0012ff28   < ---- ebp-4였던 local variablepointer, 첫번째 parameter

0012ff24  0000000a  < ---- push 0ah에 해당하는 두번째 parameter

0012ff28  00000000  < ---- ebp-4

0012ff2c  0012ff34    < ---- ebp

0012ff30  00401008 Stackwalker!main+0x8

0012ff34  0012ff80

0012ff38  00401187 Stackwalker!__tmainCRTStartup+0x117

 

Source code를 보면, func2에서는 첫번째 parameter에 두번째 parameter의 연산이 존재하는 데, stack의 구조상 parameter func2 ebp보다 positive 위치에 존재할 수 밖에 없다.

 

0:000> uf Stackwalker!func2

Stackwalker!func2

   21 00401030 55              push    ebp

   21 00401031 8bec            mov     ebp,esp

   22 00401033 8b4508          mov     eax,dword ptr [ebp+8] << 현재, esp의 위치

   22 00401036 8b08            mov     ecx,dword ptr [eax]

   22 00401038 034d0c          add     ecx,dword ptr [ebp+0Ch]

   22 0040103b 8b5508          mov     edx,dword ptr [ebp+8]

   22 0040103e 890a            mov     dword ptr [edx],ecx

   24 00401040 5d              pop     ebp

   24 00401041 c3              ret

 

Assembly code에서 ebp+81st parameter이다. 그리고, ebp+c 2nd parameter임을 알 수 있다. 이것은 아래의 raw stack을 통해서 확인이 가능하다.

 

0:000> dds esp

0012ff18  0012ff2c                        < ---- ebp : old ebp

0012ff1c  00401026 Stackwalker!func1+0x16 < ---- ebp+4 : ret address

0012ff20  0012ff28                        < ---- ebp+8 : 1st parameter

0012ff24  0000000a                       < ---- ebp+c : 2nd parameter

0012ff28  00000000

0012ff2c  0012ff34

0012ff30  00401008 Stackwalker!main+0x8

 

이후, 여기서 ret으로 하고 return address로 돌아갔을 때의 raw stack과 비교하여 살펴보면, Func2에 대한 stackclear된 것을 다음과 같이 확인할 수 있다.

 

0:000> dds esp

0012ff20  0012ff28

0012ff24  0000000a

0012ff28  0000000a

0012ff2c  0012ff34

0012ff30  00401008 Stackwalker!main+0x8

0012ff34  0012ff80

0012ff38  00401187 Stackwalker!__tmainCRTStartup+0x117

이외에도 Windbg를 통해서 Data를 확인하기 위한 몇 가지 유용한 command 로는 다음이 있다.

dv command 는 현 Framelocal variables에 대한 정보를 출력한다.

 

0:000> dv /V

0012ff20 @ebp+0x08               x = 0x0012ff28

0012ff24 @ebp+0x0c             val = 10

 

dc command를 이용하여 해당 address에 존재하는 data를 확인할 수 있다.

 

0:000> dc 0x0012ff28 l1

0012ff28  0000000a                             ....

0:000> ?a

Evaluate expression: 10 = 0000000a

 

knLnframe numberCallstack에 표시하는 데, .frame # command를 이용하여 해당 frame으로 이동 할 수 있으며, 그러므로, 해당 framelocal variables도 쉽게 확인할 수 있다.

 

0:000> knL

 # ChildEBP RetAddr 

00 0012ff18 00401026 Stackwalker!func2+0x10

01 0012ff2c 00401008 Stackwalker!func1+0x16

02 0012ff34 00401187 Stackwalker!main+0x8

03 0012ff80 0040105f Stackwalker!__tmainCRTStartup+0x117

04 0012ff88 75834911 Stackwalker!mainCRTStartup+0xf

05 0012ff94 76f2e4b6 kernel32!BaseThreadInitThunk+0xe

06 0012ffd4 76f2e489 ntdll!__RtlUserThreadStart+0x23

07 0012ffec 00000000 ntdll!_RtlUserThreadStart+0x1b

 

0:000> .frame 1

01 0012ff2c 00401008 Stackwalker!func1+0x16

0:000> dv /i /V

prv local  0012ff28 @ebp-0x04               y = 10

by 강세윤 | 2008/11/25 16:28 | Windows debugging | 트랙백 | 덧글(0)
[Troubleshooting Tool] Debug Diagnostic Tool

Debug Diagnostics Tool AD+ 와 같이 특정한 문제에 대해서 Memory Dump를 수집할 수 있는 기능을 제공한다. 여기서 특정한 문제라는 것은 Hang/Performance 문제/Memory 누수/Virtual Memory 조각화 및 다양한 Crash Issue이다. 뿐만 아니라 Crash Memory 관련 문제에 대해서 분석 report를 제공한다는 점에 있어서 유용하다.

 

먼저, Crash Rule에서 보면, 따로 설정하지 않은 1st Chance Exception에 대한 설정을 할 수 있다. 아래의 그림과 같이 Action Type으로 Log를 남기거나 Mini Dump Full Dump를 수집할 수 있도록 설정가능하며, dump 생성의 수를 제한할 수 도 있다. (만일, 0라고 설정하면 제한하지 않는 다는 의미이다.) Performance를 고려하여 대부분 None이거나 간혹 Log 정도를 남기게 된다.

그리고, 임의의 1st Exception에 대한 Dump 수집을 설정할 수 있다. (이 말은 기본적으로 아무 설정을 하지 않았을 때, 2nd chance exception에 대한 Crash Dump가 기본적으로 수집된다) 이는 상위 그림의 Advances Settings Exceptions 버튼을 Click하여 설정한다.

상위의 그림은 좌측의 Exception Code AV Click 하여 1st Chance AV가 발생했을 때 자료를 어떻게 수집할 지 설정하는 방법이다. 앞서서 설명한 것처럼 Action Type Log Mini Full User Dump를 수집할 수 있도록 설정할 수 있다. 특이한 경우는 CLR Exception의 경우이다.

보면, .NET Exception Type 이라는 부분이 존재한다. 여기에 Exception 정보를 추가하면, 해당하는 Exception 에 대해서 Full User Dump를 수집하도록 설정된다.

 

Advanced Settings에 또 한가지는 Break point 를 설정하여 Dump를 수집할 수 있다는 것이다. 다음의 그림을 보자.

기본적으로 list Kernel32!ExitProcess나 몇 가지가 추가되어 있다. 일반적으로 2nd Chance Exception으로 인하여 Process terminated 되는 경우에 Crash Dump를 수집하므로, Kernel32!ExitProcess list에서 선택하고 Action Type Full user Dump로 설정하여 Crash Rule 을 완성한다. 하지만, 외에 원하는 부분에 BP를 설정하고 Dump를 수집하거나 Log를 남기고 싶을 경우에는 상위의 그림과 같이 Breakpoint Expression 부분에 원하는 address를 설정하여 추가시킬 수도 있다.

 

그리고, Heap Corruption되는 증상에 대해서 일반적으로 Pageheap Enabled 하고 Crash Dump를 수집하게 되는 데, Debug Diagnostic Tool에서는 Advanced Settings PageHeap Flags 라는 부분에서 PageHeap을 설정할 수 있다.

Debug Diagnostic Tool Process Memory Leak 현상을 Detection 하기 위해서 Leaktrack.dll injection 시켜서 Leak tracking 할 수 있는 기능이 존재한다.

Processes Tab을 확인해 보면, Process ID에 따른 Process list 정보를 알 수 있는 데, Leak으로 의심이 되는 Process를 선택하여 오른쪽 마우스를 Click 하면, Monitor For Leaks 라는 Menu를 확인할 수 있다. 이는 앞서서 설명한 Leaktrack.dll을 해당 Process Injection 시키는 역할을 한다. 이후에 Private Bytes의 증가를 유심히 확인하여 충분히 메모리가 지속적으로 증가하는 패턴이 확인되면, Create Full Userdump menu 를 통해 메모리 dump를 수집할 수 있다. 간혹 Memory Leak으로 인하여 Out Of Memory Exception이나 Crash를 동반하는 경우가 있으므로, Crash Rule Kernal32!ExitProcess Full user dump와 함께 leaktrack을 걸어 놓는 것도 좋을 듯 하다.

 

** 메모리 Leak?

Return 되지 않는 resource 로 인하여 process가 점유하고 있는 메모리가 지속적으로 증가하는 현상을 말한다. Logic에 따라서 Application이 메모리를 보유하고 사용할 수도 있으므로, 이와는 구분 되어야 한다. 이 경우, 시스템 resource가 부족하여 시스템의 전반적인 performance에 영향을 줄 수 있으며, 간혹 Hang 증상이 발생할 수 있다. 메모리 Leak을 판단하기 위해서는 메모리의 증가하는 패턴이 꾸준히 발생하기 때문에 성능로그의 Private Bytes Virtual Bytes 그리고, Working Set의 데이터 패턴을 보고 Leak 여부를 판단해야만 한다.

*******

 

Debug Diagnostic Tool은 다음과 같은 메모리 할당에 대한 Tracking이 가능하다.

1)    NT Heap allocator

1.     HeapAlloc

2.     LocalAlloc

3.     GlobalAlloc

2)    Virtual Memory allocator

1.     VirtualAlloc

3)    C Run time memory allocator

1.     malloc

2.     calloc

3.     operator new

4)    BSTR allocator

1.     SysAllocString

5)    OLE/COM allocator

1.     CoTaskMemAlloc, IMalloc interface

 

Debug Diagnostic Tool이 제공하는 기능 중에 Crash Dump Memory Dump 분석하여 reporting을 출력해주는 기능을 가지고 있다.

그림에서처럼 Advanced Analysis Tab에는 Crash memory analysis를 선택할 수 있다. 분석에 앞서서 Symbol을 정확하게 맞춰주는 것이 필요한데, tools menuOptions and Settings 를 통해서 설정할 수 있다.

Symbol Search path For analysis 에는 default Microsoft Public Symbol이(http://msdl.microsoft.com/download/symbols, 그리고, C:\symcache directory를 local machine의 symbol cache로 사용하도록 ) 설정되어 있다. 그리고, 필요한 symbol path Symbol Search Path for Debugging에 위치시켜야만 Symbol이 맞지 않아서 잘못된 reporting이 출력되는 것을 방지할 수 있다.

 

Debug Diag는 다음과 같은 Memory leak 관련 report 를 출력한다.

보면, Analysis summary에서 눈에 띄게 Memory를 보유하고 있는 Function Call Check할 수 있다. 상위 report에서는 asp.dll VBScript.dll 과 관련한 Callstack에서 메모리를 보유하고 있음을 보여주고 있는 데, 해당 Function Click 하면, 어떤 Callstack에 메모리 할당이 이뤄졌는지 다음과 같이 reporting 하고 있음을 확인할 수 있다. 그림에서는 안 나타나 있지만, sample callstack 정보와 더불어 단위 Allocation size와 Allocation Count에 따른 unfreed memory에 대한 Top 10 list of allocations 정보를 출력해 줌으로써 Allocation pattern을 알 수 있다.

그리고, 다음은 Application에서 사용하고 있는 Virtual Memory에 대한 정보이다. 아래 그림에서도 알 수 있지만, Application 전반적으로 Reserved/Commited 그리고 Free 영역에 대한 사용정도를 알 수 있다. 그리고, Virtual Memory Details에서는 Application 에서 VirtualAlloc이 얼마나 되는 지, Threads가 사용한 Memory 또는 Native Heap으로 사용된 메모리가 많은 지에 대한 정보를 제공한다. 

Heap Memory의 경우는 다음과 같이 따로 정보를 제공한다. 각 Heap의 종류에 따라(예를 들어, Application에서 필요에 따라 Heap을 따로 관리하는 경우가 많은 데, default Heap 외에 crt heap이나 odbc나 oledb가 사용하는 mpheap 등이 존재할 수 있다.) 어느정도의 memory가 reserved 되어 있는 지, commit 되어 있는 지 보고해주며, 또한 Heap block에 따른 Address 정보나 세부적으로 memory의 할당여부를 확인할 수 있다. 이러한 Heap block의 정보를 가지고 의심이 나는 Heap 영역에 어떤 Data가 존재하는 지를 Memory Dump에서 Windbg를 통해 확인해 볼 수 있기 때문에 유용하다.  

위의 그림에서도 나타나 있지만, Virtual Memory나 Heap Memory에 대한 Allocation 정보에서의 Top 10 List of allocations 보다 직관적으로 unfree된 pattern을 알 수 있는 유용한 정보이다. 그러므로, 이러한 Heap이나 Virtual Memory에 대한 정보는 Application의 메모리 누수인지 아닌지에 대한 판단 및 누수일 때의 원인을 찾는 데 도움을 줄 수 있다.

그리고, 추가적으로 Handle count에 대한 정보를 출력해줌으로써 Handle leaks에 대한 tracking도 가능하고, IIS Hang과 같은 Issue를 위해 HTTP Ping을 주기적으로 설정하는 기능도 존재한다.

by 강세윤 | 2008/11/21 17:57 | Windows debugging | 트랙백 | 덧글(0)
NTDLL!KiUserExceptionDispatcher

Application에 문제가 발생하여 오류나 Exception정보가 Popup되는 경우도 있지만, 그렇지 않고 Process가 죽어버리는 경우도 생각보다 많다. 아래의 Crash dump의 경우는 2nd chance exception 이 발생하지 않고 오류로 인한 Kernel32!ExitProcess가 호출되는 경우인데, 이러한 경우가 대표적으로 Exception 이 중간에 씹힌 경우라고 하겠다.

 

ChildEBP RetAddr  Args to Child             

0f28e810 7928b829 80131506 0f28ec94 0f28eca4 KERNEL32!ExitProcess

0f28e820 7928b8f8 0e1a9420 00000040 0f28e920 mscorsvr!FailFast+0x114

0f28e834 7c006f08 0f28e85c 00000000 0f28e85c mscorsvr!FailFast+0x198

0f28e85c 77fbb272 0f28e920 0f28ec94 0f28e934 msvcr70!_except_handler3+0x4b

0f28e880 77facc28 0f28e920 0f28ec94 0f28e934 NTDLL!ExecuteHandler+0x26

0f28e908 77f91bc6 0f28e920 0f28e934 0f28e920 NTDLL!RtlDispatchException+0x76

0f28e908 77e7bcb1 0f28e920 0f28e934 0f28e920 NTDLL!KiUserExceptionDispatcher+0xe

0f28ec58 7928b8ce e0434f4d 00000001 00000000 KERNEL32!RaiseException+0x56

0f28eca4 7928b969 00000040 003611ec 79230fcd mscorsvr!FailFast+0x16e

0f28ecb0 79230fcd 00000001 0f28edd0 0f28f128 mscorsvr!FatalInternalError+0xd

0f28ecd4 791cf1ad 0f28edd0 0f28f128 0f28edec mscorsvr!GetPrevSEHRecord+0x65d

0f28ecec 7928f651 0f28edd0 0f28f128 0f28edec mscorsvr!COMPlusFrameHandler+0x41

0f28ed0c 77fbb272 0f28edd0 0f28f128 0f28edec mscorsvr!ContextTransitionFrameHandler+0x35

0f28ed30 77facc28 0f28edd0 0f28f128 0f28edec NTDLL!ExecuteHandler+0x26

0f28edb8 77f91bc6 0f28edd0 0f28edec 0f28edd0 NTDLL!RtlDispatchException+0x76

0f28edb8 792d4797 0f28edd0 0f28edec 0f28edd0 NTDLL!KiUserExceptionDispatcher+0xe

0f28f0d4 792d463d 0e1a9420 0f28f1c8 0018c4b8 mscorsvr!ComCallMLStubCache::CompileMLStub+0x5d7

0f28f114 791d6920 0f28f178 791f1c58 0f28f1c8 mscorsvr!ComCallMLStubCache::CompileMLStub+0x1af

0f28f15c 792d4755 0018c4b8 792d4605 0f28f178 mscorsvr!Thread::DoADCallBack+0x5c

0f28f18c 003ba1fd 0e1a9420 0f28f1c8 0f28f1f4 mscorsvr!ComCallMLStubCache::CompileMLStub+0x595

 

경험 많은 사람이라면 해당 Dumpmscorsvr!ComCallMLStubCache::CompileMLStub에서부터 문제가 발생했을 것이라고 예상한다. 그 이후에 Callstack Exceptionhandling되고 씹히고 FailFast 되고 … 하지만, 사실 여기서 주목해야 하는 부분은 NTDLL!KiUserExceptionDispatcher 이다. 이는 Exception Context정보를 가지고 있기 때문인데, 2nd parameter가 바로 그것이다. 상위의 Callstack 2개의 NTDLL!KiUserExceptionDispatcher이 존재한다.

 

0:018> dc 0f28e934 l2

0f28e934  0001003f 00000000                    ?.......

 

1003f Context StructureContextFlags 이므로 Context structure임을 확인할 수 있다. 그리고,

해당 Context로 이동하면 CLR Exception이 발생했음을 알 수 있다.

 

0:018> .cxr 0f28e934

eax=0f28ec08 ebx=0e1a9420 ecx=00000000 edx=003611ec esi=00000000 edi=0f28edec

eip=77e7bcb1 esp=0f28ec00 ebp=0f28ec58 iopl=0         nv up ei pl zr na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

KERNEL32!RaiseException+0x56:

77e7bcb1 5f              pop     edi

0:018> kb

ChildEBP RetAddr  Args to Child             

0f28ec58 7928b8ce e0434f4d 00000001 00000000 KERNEL32!RaiseException+0x56

0f28eca4 7928b969 00000040 003611ec 79230fcd mscorsvr!FailFast+0x16e

0f28ecb0 79230fcd 00000001 0f28edd0 0f28f128 mscorsvr!FatalInternalError+0xd

0f28ecd4 791cf1ad 0f28edd0 0f28f128 0f28edec mscorsvr!GetPrevSEHRecord+0x65d

0f28ecec 7928f651 0f28edd0 0f28f128 0f28edec mscorsvr!COMPlusFrameHandler+0x41

0f28ed0c 77fbb272 0f28edd0 0f28f128 0f28edec mscorsvr!ContextTransitionFrameHandler+0x35

0f28ed30 77facc28 0f28edd0 0f28f128 0f28edec NTDLL!ExecuteHandler+0x26

0f28edb8 77f91bc6 0f28edd0 0f28edec 0f28edd0 NTDLL!RtlDispatchException+0x76

0f28edb8 792d4797 0f28edd0 0f28edec 0f28edd0 NTDLL!KiUserExceptionDispatcher+0xe

0f28f0d4 792d463d 0e1a9420 0f28f1c8 0018c4b8 mscorsvr!ComCallMLStubCache::CompileMLStub+0x5d7

0f28f114 791d6920 0f28f178 791f1c58 0f28f1c8 mscorsvr!ComCallMLStubCache::CompileMLStub+0x1af

0f28f15c 792d4755 0018c4b8 792d4605 0f28f178 mscorsvr!Thread::DoADCallBack+0x5c

0f28f18c 003ba1fd 0e1a9420 0f28f1c8 0f28f1f4 mscorsvr!ComCallMLStubCache::CompileMLStub+0x595

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

0f28f204 6b3ae86a 00000000 11cf8e78 11cf8e7c 0x3ba1fd

0f28f25c 6b3b010e 00000000 00000001 00138750 comsvcs!CJITActivation::DeactivateAndReleaseObject+0x86

0f28f2bc 7cf573f0 11cf8e7c 0e14f3a8 0e14f3a8 comsvcs!CJITActivation::DestroyStub+0x3be

0f28f2d8 7cf5db7e 0e14f3a8 00000000 0e1ffef0 OLE32!CObjectContext::DestroyIdentity+0x47

0f28f2ec 7cf51d0e 00001000 0e1eab48 0f28f388 OLE32!CIDObject::SetZombie+0x78

0f28f308 7cf51c55 00000000 7cf52804 0e1ffef0 OLE32!CStdWrapper::PrepareForDestruction+0xb4

0f28f310 7cf52804 0e1ffef0 7cf037a0 00000000 OLE32!PrepareWrapperForDestruction+0xb

 

그리고, 다시

 

0:018> dc 0f28edec l2

0f28edec  0001003f 00000000                    ?.......

0:018> .cxr 0f28edec

eax=00000000 ebx=00000000 ecx=00000007 edx=0017aadc esi=0dc2ee80 edi=01309780

eip=792d4797 esp=0f28f0b8 ebp=0f28f0d4 iopl=0         nv up ei pl nz na po nc

cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00010202

mscorsvr!ComCallMLStubCache::CompileMLStub+0x5d7:

792d4797 8b00            mov     eax,dword ptr [eax]  ds:0023:00000000=????????

 

보통, .Frame 10 해서 해당 line으로 가서 확인하는 것과 해당 Context로 이동해서 확인하는 것은 차이가 있다. Context로 이동한다는 것은 해당 문제가 발생한 시점의 Register 정보 등을 확인할 수 있지만 line으로 이동하면 local variables나 parameters정보외에는 ... 부족하다. 

 

그리고, kv command를 사용하여 Callstack을 확인하는 것에 익숙해지는 것이 좋다. Kv command Context 정보, Calling Conv FPO 정보 등을 표시해 주기 때문에 유용하다.

 

# ChildEBP RetAddr  Args to Child             

00 0f28e810 7928b829 80131506 0f28ec94 0f28eca4 KERNEL32!ExitProcess (FPO: [Non-Fpo])

01 0f28e820 7928b8f8 0e1a9420 00000040 0f28e920 mscorsvr!FailFast+0x114 (FPO: [Non-Fpo])

02 0f28e834 7c006f08 0f28e85c 00000000 0f28e85c mscorsvr!FailFast+0x198 (FPO: [Non-Fpo])

03 0f28e85c 77fbb272 0f28e920 0f28ec94 0f28e934 msvcr70!_except_handler3+0x4b (FPO: [Uses EBP] [3,0,7]) (CONV: cdecl)

04 0f28e880 77facc28 0f28e920 0f28ec94 0f28e934 NTDLL!ExecuteHandler+0x26

05 0f28e908 77f91bc6 0f28e920 0f28e934 0f28e920 NTDLL!RtlDispatchException+0x76 (FPO: [Non-Fpo])

06 0f28e908 77e7bcb1 0f28e920 0f28e934 0f28e920 NTDLL!KiUserExceptionDispatcher+0xe (FPO: [2,0,0]) (CONTEXT @ 0f28e934)

07 0f28ec58 7928b8ce e0434f4d 00000001 00000000 KERNEL32!RaiseException+0x56 (FPO: [Non-Fpo])

08 0f28eca4 7928b969 00000040 003611ec 79230fcd mscorsvr!FailFast+0x16e (FPO: [Non-Fpo])

09 0f28ecb0 79230fcd 00000001 0f28edd0 0f28f128 mscorsvr!FatalInternalError+0xd (FPO: [0,0,0])

0a 0f28ecd4 791cf1ad 0f28edd0 0f28f128 0f28edec mscorsvr!GetPrevSEHRecord+0x65d (FPO: [Non-Fpo])

0b 0f28ecec 7928f651 0f28edd0 0f28f128 0f28edec mscorsvr!COMPlusFrameHandler+0x41 (FPO: [Non-Fpo])

0c 0f28ed0c 77fbb272 0f28edd0 0f28f128 0f28edec mscorsvr!ContextTransitionFrameHandler+0x35 (FPO: [Non-Fpo])

0d 0f28ed30 77facc28 0f28edd0 0f28f128 0f28edec NTDLL!ExecuteHandler+0x26

0e 0f28edb8 77f91bc6 0f28edd0 0f28edec 0f28edd0 NTDLL!RtlDispatchException+0x76 (FPO: [Non-Fpo])

0f 0f28edb8 792d4797 0f28edd0 0f28edec 0f28edd0 NTDLL!KiUserExceptionDispatcher+0xe (FPO: [2,0,0]) (CONTEXT @ 0f28edec)

10 0f28f0d4 792d463d 0e1a9420 0f28f1c8 0018c4b8 mscorsvr!ComCallMLStubCache::CompileMLStub+0x5d7 (FPO: [Non-Fpo])

11 0f28f114 791d6920 0f28f178 791f1c58 0f28f1c8 mscorsvr!ComCallMLStubCache::CompileMLStub+0x1af (FPO: [Non-Fpo])

12 0f28f15c 792d4755 0018c4b8 792d4605 0f28f178 mscorsvr!Thread::DoADCallBack+0x5c (FPO: [Non-Fpo])

13 0f28f18c 003ba1fd 0e1a9420 0f28f1c8 0f28f1f4 mscorsvr!ComCallMLStubCache::CompileMLStub+0x595 (FPO: [Non-Fpo])

by 강세윤 | 2008/11/04 12:48 | Windows debugging | 트랙백 | 덧글(0)
< 이전페이지 다음페이지 >