Managed Code에서 Access Violation 발생 시 Debugging (예)

Managed Code역시 Access Violation(c0000005)는 발생한다. Managed 환경에서 Pointer 연산이 존재하지 않기 때문에 발생하지 않지 않나 라고 생각할 수도 있겠지만, Managed Code 역시 Intermediate code가 결국은 machine code로 변환되고 실행되기 때문에 register 에 들어있는 Address invalid 한 경우는 흔하게 발생할 수 있다. 다음은 managed application AV로 인한 Crash가 발생한 대표적인 Callstack 이다.

 

0012ef30 792c3b86 c0000005 00000000 00000002 kernel32!RaiseException+0x53

0012ef70 792c3bce 791b4418 0012f104 0104d2c8 mscorwks!LinkFrameAndThrow+0x49

0012efc4 791dc474 00008004 00000000 011e63b0 mscorwks!CPFH_HandleManagedFault

0012efd8 793439ff 0012f070 00000003 0012f03c mscorwks!CallDescrWorker+0x30

0012f0cc 79343c36 0012eff0 0117bfc4 00ff8a6b mscorwks!CallDescrWithObjectArray+0x3bb

0012f1bc 00fe102c 0012f1c8 011dde14 0012f25c mscorwks!CStackBuilderSink::PrivateProcessMessage+0x16c

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

0012f274 799d16d1 00000000 00000000 799d1694 0xfe102c

0012f280 799d1694 011d4a6c 011a9a94 01196858 mscorlib_79990000+0x416d1

00000000 00000000 00000000 00000000 00000000 mscorlib_79990000+0x41694

 

Kernel32!RaiseException FunctionArgument를 통해서 AV가 발생했음을 여지 없이 보여주고 있다. 그러면, 어디에서 AV가 발생한 것일 까? AV managed exception은 아니기 때문에 Native Code에서 살펴보아야 할 것 같은 데.. 상위의 Callstack은 뭔가 Access Violation CLR에서 handling 된 것은 아닌지 의심하게 해준다. 예를 들어, mscorwks!CPFH_HandleManagedFault 와 같은 함수의 이름이 그렇다. 그러므로, Managed Debugging을 해본 사람이라면 그렇듯이 Managed Callstack을 확인해 볼 필요가 있다.

 

0:000> !clrstack

Thread 0

ESP         EIP      

0x0012ef78  0x7c812aeb [FRAME: FaultingExceptionFrame]

0x0012f104  0x7c812aeb [FRAME: GCFrame]

0x0012f1fc  0x7c812aeb [FRAME: ECallMethodFrame] [DEFAULT] [hasThis] Object System.Runtime.Remoting.Messaging.StackBuilderSink.PrivateProcessMessage(Class System.Reflection.MethodBase,SZArray Object,Object,I4,Boolean,ByRef SZArray Object)

0x0012f220  0x799d19f3 [DEFAULT] [hasThis] Class System.Runtime.Remoting.Messaging.IMessage System.Runtime.Remoting.Messaging.StackBuilderSink.SyncProcessMessage(Class System.Runtime.Remoting.Messaging.IMessage,I4,Boolean)

. . .

Managed CallstackNative callstack과는 다르게 ESP, EIP 정보와 MethodDesc 정보 그리고, Frame 정보가 존재한다. 여기서 Frame 이라는 자료구조가 특이하다. 이는 EE(CLR에서의 Execution Engine) Managed Thread라는 형태로 Stack walking을 할 때, 필요로 한 정보들, 예를 들어, registers, local variables return address 등의 정보(Managed에서는 흔히 Activation Record라는 용어를 사용한다.) keeping 하는 것에 대한 일종의 자료구조 형태로 Frame을 이용하기도 한다. 상위 Managed Callstack을 보면, FaultingExceptionFrame 이라는 것이 존재하는 데, 이것은 http://blogs.msdn.com/joelpob/archive/2004/03/05/84738.aspx 문서에 언급한 것처럼, Hardware exceptions에 대한 Exception Handling정보를 포함하고 있음을 알 수 있는 데, 실제로 FaultingExceptionFrame Memory Dumping에서 살펴보면, Exception이 발생한 시점의 return address에 대한 Jitting MethodDesc 정보를 포함하고 있다.

 

0:000> dd 0x0012ef78 l4

0012ef78  791b4418 0012f104 0104d2c8 041b011b

 

0:000> !ip2md 041b011b

MethodDesc: 0x00ff8a70

Jitted by normal JIT

Method Name : [DEFAULT] [hasThis] I4 IEHost.Execute.IEExecuteRemote.ExecuteAsAssembly(String,Class System.Security.Policy.Evidence,SZArray UI1,ValueClass System.Configuration.Assemblies.AssemblyHashAlgorithm)

MethodTable 0xff8abc

Module: 0x1d7e48

mdToken: 0x06000006 (c:\windows\assembly\gac\ieexecremote\1.0.5000.0__b03f5f7f11d50a3a\ieexecremote.dll)

Flags : 0x20a0

Method VA : 0x041b0098

 

또한, Low callstack을 확인해보면, mscorwks!g_SavedExceptionInfo 라는 전역변수를 확인할 수 있는 데, 해당 전역변수는 Exception Record Exception Context와 같은 정보를 가지고 있으므로 AV가 발생한 시점의 Context로 이동이 가능하다.

 

0:000> dds esp

0012eedc  00000002

0012eee0  c0000005

0012eee4  00000000

0012eee8  00000000

0012eeec  7c812aeb kernel32!RaiseException+0x53

0012eef0  00000002

0012eef4  00000000

0012eef8  00000000

0012eefc  00000080

0012ef00  0226b761

0012ef04  003a0043

0012ef08  0064005c

0012ef0c  0000213c

0012ef10  0006f748

0012ef14  77bf0017 msvcrt!_vsnwprintf+0x30

0012ef18  0006f728

0012ef1c  0200c7b4

0012ef20  0006fc4c

0012ef24  00000001

0012ef28  0230b459

0012ef2c  00007ef6

0012ef30  0012ef70

0012ef34  792c3b86 mscorwks!LinkFrameAndThrow+0x49

0012ef38  c0000005

0012ef3c  00000000

0012ef40  00000002

0012ef44  793ef084 mscorwks!g_SavedExceptionInfo+0x14

0012ef48  00000000

0012ef4c  011dbf88

0012ef50  0014eba8

0012ef54  00000000

0012ef58  0012ef48

 

0:000> x mscorwks!g_SavedExceptionInfo

793ef070 mscorwks!g_SavedExceptionInfo = <no type information>

 

0:000> dd 793ef070

793ef070  c0000005 00000000 00000000 041b011b  <--- c0000005(AV), 041b011b=MethodDesc

793ef080  00000002 00000000 00000000 0001003f

793ef090  00000000 00000000 00000000 00000000

793ef0a0  00000000 00000000 ffff027f ffff4020

793ef0b0  ffffffff 799b14d4 05d8001b 0012f21c

793ef0c0  0001003f 00000000 00000000 00000000  <---- 1003f Context structure 시작위치

793ef0d0  00000000 00000000 00000000 ffff027f

793ef0e0  ffff4020 ffffffff 799b14d4 05d8001b

 

0:000> .cxr 793ef0c0 

eax=011ed86c ebx=0014eba8 ecx=00000000 edx=011ed86c esi=011dbf88 edi=00000000

eip=041b011b esp=0012ef9c ebp=0012efc4 iopl=0         nv up ei pl zr na pe nc

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

041b011b 8b01            mov     eax,dword ptr [ecx]  ds:0023:00000000=????????

0:000> kb

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

ChildEBP RetAddr  Args to Child             

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

0012efc4 791dc474 00008004 00000000 011e63b0 0x41b011b

0012efd8 793439ff 0012f070 00000003 0012f03c mscorwks!CallDescrWorker+0x30

0012f0cc 79343c36 0012eff0 0117bfc4 00ff8a6b mscorwks!CallDescrWithObjectArray+0x3bb

0012f1bc 00fe102c 0012f1c8 011dde14 0012f25c mscorwks!CStackBuilderSink::PrivateProcessMessage+0x16c

0012f274 799d16d1 00000000 00000000 799d1694 0xfe102c

0012f280 799d1694 011d4a6c 011a9a94 01196858 mscorlib_79990000+0x416d1

00000000 00000000 00000000 00000000 00000000 mscorlib_79990000+0x41694

 

그러므로, 0x41b011b AV가 발생한 시점의 return address이며, Native Callstack에서는 상위와 같이 보여진다. Access Violation은 왜 발생했을 까? MethodDesc 에 대한 Assembly code는 아래와 같이 확인한다.

 

0:000> !u 0x41b011b

Will print '>>> ' at address: 0x041b011b

Normal JIT generated code

[DEFAULT] [hasThis] I4 IEHost.Execute.IEExecuteRemote.ExecuteAsAssembly(String,Class System.Security.Policy.Evidence,SZArray UI1,ValueClass System.Configuration.Assemblies.AssemblyHashAlgorithm)

Begin 0x041b0098, size 0x159

041b0098 55              push    ebp

041b0099 8bec            mov     ebp,esp

041b009b 83ec18          sub     esp,18h

041b009e 57              push    edi

041b009f 56              push    esi

041b00a0 53              push    ebx

041b00a1 c745f800000000  mov     dword ptr [ebp-8],0

041b00a8 894dec          mov     dword ptr [ebp-14h],ecx

041b00ab 8bfa            mov     edi,edx

041b00ad 8b75ec          mov     esi,dword ptr [ebp-14h]

041b00b0 ff750c          push    dword ptr [ebp+0Ch]

041b00b3 ff7508          push    dword ptr [ebp+8]

041b00b6 8bcf            mov     ecx,edi

041b00b8 33d2            xor     edx,edx

041b00ba ff15144eba79    call    dword ptr [mscorlib_79990000+0x214e14 (79ba4e14)] (System.Reflection.Assembly.LoadFrom)

041b00c0 8945e8          mov     dword ptr [ebp-18h],eax

041b00c3 b93c8cff00      mov     ecx,0FF8C3Ch

041b00c8 e84b1f87fc      call    00a22018

041b00cd 8d5608          lea     edx,[esi+8]

041b00d0 e8e32f82fc      call    009d30b8

041b00d5 8b4608          mov     eax,dword ptr [esi+8]

041b00d8 8d5004          lea     edx,[eax+4]

041b00db e8f73082fc      call    009d31d7

041b00e0 8b5608          mov     edx,dword ptr [esi+8]

041b00e3 8b450c          mov     eax,dword ptr [ebp+0Ch]

041b00e6 8d5208          lea     edx,[edx+8]

041b00e9 e8ca2f82fc      call    009d30b8

041b00ee 8b4608          mov     eax,dword ptr [esi+8]

041b00f1 8b5508          mov     edx,dword ptr [ebp+8]

041b00f4 895010          mov     dword ptr [eax+10h],edx

041b00f7 8b4608          mov     eax,dword ptr [esi+8]

041b00fa c7400c02000000  mov     dword ptr [eax+0Ch],2

041b0101 8b4de8          mov     ecx,dword ptr [ebp-18h] ; ebp-18h는 LoadFrom의 ret object, Assembly class

041b0104 8b01            mov     eax,dword ptr [ecx]

041b0106 ff5068          call    dword ptr [eax+68h] ; Assembly class가 제공하는 Method

041b0109 8bf8            mov     edi,eax ; AV 유발하는 edi eax+68h Method call ret value

041b010b b9e8dcc079      mov     ecx,offset mscorlib_79990000+0x27dce8 (79c0dce8)

041b0110 e8c2300375      call    mscorwks!COMClass::GetClassFromHandle (791e31d7)

041b0115 6a00            push    0

041b0117 8bcf            mov     ecx,edi   ;<--- AV 유발하는 ecx edi 부터..

041b0119 8bd0            mov     edx,eax

>>> 041b011b 8b01            mov     eax,dword ptr [ecx]  ;<-- AV, ecx Null

 

0:000> r

Last set context:

eax=011ed86c ebx=0014eba8 ecx=00000000 edx=011ed86c esi=011dbf88 edi=00000000

eip=041b011b esp=0012ef9c ebp=0012efc4 iopl=0         nv up ei pl zr na pe nc

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

041b011b 8b01            mov     eax,dword ptr [ecx]  ds:0023:00000000=????????

 

ecx Null 이다. 이는 edi 로 부터 얻어진다. edi Method Call return value 인데, 이는 결국, System.Reflection.Assembly.LoadFrom return value Assembly class에서 제공하는 특정 method return value 추측할 있다. 문제는 Assembly class 확인하기 위해서 @ebp-18 정보를 확인해 보면, 정상적으로 Assembly load되지 않았음을 있다.

 

0:000> !dumpassembly @ebp-18

Parent Domain: 0x00008004  <--- ?????

Name: X/¹y    

ClassLoader: 0x00000000  <--- ?????

  Module Name

0x66893209

 

결국, System.Reflection.Assembly.LoadFrom   부터 얻어진 Assembly class 문제되어 AV 발생한 것으로 확인된다. 이는 추후에 NullReferenceException으로 다시 돌아온다. Native Code 거나 Managed Code거나 Object Null Check 필수!!.

by 강세윤 | 2009/01/15 14:49 | Windows debugging | 트랙백 | 덧글(0)
트랙백 주소 : http://byung.egloos.com/tb/4787690
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]

:         :

:

비공개 덧글

< 이전페이지 다음페이지 >