Hung Window?, No source?, No problem!! Part 1
"이 문서는 http://blogs.msdn.com/ntdebugging blog 의 번역이며 원래의 자료가 통보 없이 변경될 수 있습니다. 이 자료는 법률적 보증이 없으며 의견을 주시기 위해 원래의 blog 를 방문하실 수 있습니다. (http://blogs.msdn.com/ntdebugging/archive/2007/06/13/hung-window-no-source-no-problem-part-1.aspx)"
Hung Window?, No source?, No problem!! Part 1
안녕하세요. 제 이름은 Jeff 이고 Microsoft CPR Platforms team 의 Escalation Engineer 입니다. 이 블로그는 Hung window 찾기의 후속 편 입니다. 이 Lab 을 하기 위해 dumphungwindow 와 badwindow.exe 를 이전 블로그에서 다운 받아야 하고 Debugging tools for windows 를 down 받아야 합니다.
Debugging tools: http://www.microsoft.com/whdc/devtools/debugging/installx86.mspx
이전 blog http://blogs.msdn.com/ntdebugging/archive/2007/05/29/detecting-and-automatically-dumping-hung-gui-based-windows-applications.aspx
위의 두 개를 설치한 후 이 문서의 내용을 시작할 수 있습니다. 이후 문서에서 왜 Window 가 화면 출력을 멈추고 응답하지 않는 이 Debug 하고 밝힐 것 입니다.
Step 1 badwindow.exe 실행
Step 2 dumphugnwindow.exe 실행
Step 3 badwindow.exe 메뉴에서 Hang – Hang Type 1 선택
Dump hung window 가 응답 없음 메시지를 찾고 badwindow.exe process 의 dump 를 만드는 메시지를 확인한 후 다음 작업을 진행해야 합니다.
************ OUTPUT *************
C:\source\dumphungwindow\release>dumphungwindow.exe
Dumps will be saved in C:\Users\jeffda\AppData\Local\Temp\
scanning for hung windows
**
Hung Window found dumping process (7924) badwindow.exe
Dumping unresponsive process
C:\Users\jeffda\AppData\Local\Temp\HWNDDump_Day6_12_2007_Time9_53_34_Pid7924_badwindow.exe.dmp
Dump complete
Hung Window found dumping process (7924) badwindow.exe
Dumping unresponsive process
C:\Users\jeffda\AppData\Local\Temp\HWNDDump_Day6_12_2007_Time9_53_46_Pid7924_badwindow.exe.dmp
Dump complete
Hung Window found dumping process (7924) badwindow.exe
Dumping unresponsive process
C:\Users\jeffda\AppData\Local\Temp\HWNDDump_Day6_12_2007_Time9_53_56_Pid7924_badwindow.exe.dmp
Dump complete*
************ OUTPUT *************
Step 4 C:\websymbols 라는 로컬 심볼폴더를 만듭니다.
Step 5 Windbg 에서 file - Symbols 메뉴를 선택하여 다음과 같이 설정 합니다.
SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols
보다 자세한 내용은 http://www.microsoft.com/whdc/devtools/debugging/debugstart.mspx 에서 확인할 수 있습니다.
Step 6 Windbg 에서 File – Open – Crash dump 를 선택하여 생성된 첫번째 덤프 파일을 엽니다. 아래와 같은 초기화면이 출력될 것 입니다.
Microsoft (R) Windows Debugger Version 6.7.0001.0
Copyright (c) Microsoft Corporation. All rights reserved.
***** WARNING: Your debugger is probably out-of-date.
***** Check http://dbg for updates.
Loading Dump File [C:\Users\jeffda\AppData\Local\Temp\HWNDDump_Day6_12_2007_Time9_53_34_Pid7924_badwindow.exe.dmp]
User Mini Dump File with Full Memory: Only application data is available
Symbol search path is: Symbol search path is: SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols
Executable search path is:
Windows Vista Version 6000 MP (2 procs) Free x86 compatible
Product: WinNt, suite: SingleUserTS
Debug session time: Tue Jun 12 09:53:35.000 2007 (GMT-4)
System Uptime: 11 days 18:41:43.089
Process Uptime: 0 days 0:00:32.000
....................................
Loading unloaded module list
.
eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=004e3750 edi=004d9c60
eip=777fb2b2 esp=0017ee18 ebp=0017ee44 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
ntdll!NtAlpcSendWaitReceivePort+0x12:
777fb2b2 c22000 ret 20h
Step 7 Debugger 화면에서 (Windbg 화면 가장 아래 쪽에 0:000> 과 같은 화면) 에서 ~*k 를 입력 하면 아래와 같은 화면을 보실 수 있습니다.
. 0 Id: 1ef4.17a8 Suspend: 0 Teb: 7efdd000 Unfrozen
ChildEBP RetAddr
0017ee14 76f584c5 ntdll!NtAlpcSendWaitReceivePort+0x12
0017ee44 76f5899d rpcrt4!LRPC_CASSOCIATION::AlpcSendWaitReceivePort+0x2f
0017ee78 76f58514 rpcrt4!LRPC_BASE_CCALL::DoSendReceive+0x48
0017ee90 76f57785 rpcrt4!LRPC_BASE_CCALL::SendReceive+0x2c
0017eea0 76f5766b rpcrt4!I_RpcSendReceive+0x25
0017eeb4 76f5763a rpcrt4!NdrSendReceive+0x31
0017eec0 76ff0146 rpcrt4!NdrpSendReceive+0x9
0017f2c8 756e5ff3 rpcrt4!NdrClientCall2+0x18f
0017f2e0 756e5ef9 dnsapi!R_ResolverQuery+0x19
0017f34c 756e5d34 dnsapi!Query_PrivateExW+0x257
0017f388 756e6025 dnsapi!Query_Shim+0xbb
0017f3b0 74f461b4 dnsapi!DnsQuery_W+0x20
0017f3e0 74f4611c mswsock!SaBlob_Query+0x2d
0017f424 74f459db mswsock!Rnr_DoDnsLookup+0x129
0017f6c0 76d75a72 mswsock!Dns_NSPLookupServiceNext+0x25c
0017f6d8 76d75a52 ws2_32!NSPROVIDER::NSPLookupServiceNext+0x17
0017f6f4 76d75994 ws2_32!NSPROVIDERSTATE::LookupServiceNext+0x1c
0017f720 76d7590a ws2_32!NSQUERY::LookupServiceNext+0xa4
0017f764 76d7b11f ws2_32!WSALookupServiceNextW+0x74
0017f7b4 76d7ab77 ws2_32!WSALookupServiceNextA+0x52
1 Id: 1ef4.f80 Suspend: 0 Teb: 7efda000 Unfrozen
ChildEBP RetAddr
0275fef0 76e41220 ntdll!ZwWaitForSingleObject+0x15
0275ff60 76e41188 kernel32!WaitForSingleObjectEx+0xbe
*** ERROR: Symbol file could not be found. Defaulted to export symbols for FwcWsp.dll -
0275ff74 55620611 kernel32!WaitForSingleObject+0x12
WARNING: Stack unwind information not available. Following frames may be wrong.
0275ff88 5561f10c FwcWsp!RatDumpStack+0x1bd
0275ffa0 76eb19f1 FwcWsp!RatDbgPrint+0x156
0275ffac 7782d109 kernel32!BaseThreadInitThunk+0xe
0275ffec 00000000 ntdll!_RtlUserThreadStart+0x23
2 Id: 1ef4.1d04 Suspend: 0 Teb: 7efd7000 Unfrozen
ChildEBP RetAddr
028cfe90 76e4edb5 ntdll!ZwWaitForMultipleObjects+0x15
028cff2c 76e430c3 kernel32!WaitForMultipleObjectsEx+0x11d
028cff48 5560c841 kernel32!WaitForMultipleObjects+0x18
WARNING: Stack unwind information not available. Following frames may be wrong.
028cff84 5560c9c0 FwcWsp!WspDisable+0x1b47
028cffa0 76eb19f1 FwcWsp!WspDisable+0x1cc6
028cffac 7782d109 kernel32!BaseThreadInitThunk+0xe
028cffec 00000000 ntdll!_RtlUserThreadStart+0x23
숫자 ID 는 Thread 숫자를 나타냅니다. 보다 정확하게는 Process ID 와 Thread 라고 할 수 있습니다. 1ef4.1d04 는 1ef4 라는 Process ID 와 1d04 라는 Thread ID 를 나타내고 Suppend: 0 teb: 7efd7000 Unfrozen 은 Thread 의 상태를 나타 냅니다.
각각의 Tread 는 call stack 을 가지고 가장 최근 호출된 함수가 stack 의 상단에 위치 합니다. 각각의 호출은 stack 을 키웁니다. Thread 0 번에서는 모든 Stack 을 보여주지 주지 않고 있지만 더 많은 stack 정보를 windbg 에서 확인할 수 있습니다.
이 case 에서는 thread 0 에서 “k 123” 명령을 사용해서 보다 많은 stack 을 확인할 수 있습니다.
0:000> k 123
ChildEBP RetAddr
0017ee14 76f584c5 ntdll!NtAlpcSendWaitReceivePort+0x12
0017ee44 76f5899d rpcrt4!LRPC_CASSOCIATION::AlpcSendWaitReceivePort+0x2f
0017ee78 76f58514 rpcrt4!LRPC_BASE_CCALL::DoSendReceive+0x48
0017ee90 76f57785 rpcrt4!LRPC_BASE_CCALL::SendReceive+0x2c
0017eea0 76f5766b rpcrt4!I_RpcSendReceive+0x25
0017eeb4 76f5763a rpcrt4!NdrSendReceive+0x31
0017eec0 76ff0146 rpcrt4!NdrpSendReceive+0x9
0017f2c8 756e5ff3 rpcrt4!NdrClientCall2+0x18f
0017f2e0 756e5ef9 dnsapi!R_ResolverQuery+0x19
0017f34c 756e5d34 dnsapi!Query_PrivateExW+0x257
0017f388 756e6025 dnsapi!Query_Shim+0xbb
0017f3b0 74f461b4 dnsapi!DnsQuery_W+0x20
0017f3e0 74f4611c mswsock!SaBlob_Query+0x2d
0017f424 74f459db mswsock!Rnr_DoDnsLookup+0x129
0017f6c0 76d75a72 mswsock!Dns_NSPLookupServiceNext+0x25c
0017f6d8 76d75a52 ws2_32!NSPROVIDER::NSPLookupServiceNext+0x17
0017f6f4 76d75994 ws2_32!NSPROVIDERSTATE::LookupServiceNext+0x1c
0017f720 76d7590a ws2_32!NSQUERY::LookupServiceNext+0xa4
0017f764 76d7b11f ws2_32!WSALookupServiceNextW+0x74
0017f7b4 76d7ab77 ws2_32!WSALookupServiceNextA+0x52
0017f7e0 76d7dba3 ws2_32!getxyDataEnt+0xbd
0017fa1c 0040147d ws2_32!gethostbyname+0xb4
0017fbc8 004012fc badwindow!hangtype1+0x3d [c:\source\badwindow\badwindow\badwindow.cpp @ 310]
0017fc24 772a87af badwindow!WndProc+0xdc [c:\source\badwindow\badwindow\badwindow.cpp @ 274]
0017fc50 772a8936 user32!InternalCallWinProc+0x23
0017fcc8 772a8a7d user32!UserCallWinProcCheckWow+0x109
0017fd2c 772a8ad0 user32!DispatchMessageWorker+0x380
0017fd3c 004010fb user32!DispatchMessageW+0xf
0017ff0c 00401817 badwindow!wWinMain+0xfb [c:\source\badwindow\badwindow\badwindow.cpp @ 124]
0017ffa0 76eb19f1 badwindow!__tmainCRTStartup+0x150 [f:\sp\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 589]
0017ffac 7782d109 kernel32!BaseThreadInitThunk+0xe
0017ffec 00000000 ntdll!_RtlUserThreadStart+0x23
여러분이 보시는 것과 같이 현재 Thread 는 WinMain Thread 입니다. 보다 자세히 이야기 하면 badwindow!wWinMain 입니다. Winmain 는 메시지를 처리하는 것으로 DispatchMessageW 를 호출하여 User32 module 안으로 들어가고 User32 에서 메시지를 적합한 Window 의 WinProc 으로 전달해 줍니다.
위의 스택을 확인해 보면 badwindow!WinProc 라는 함수가 있는 것을 볼 수 있습니다. 중요한 것은 WinProc 가 들어오는 메시지를 처리할 수 있는 상태여야 한다는 것 입니다. 만약 이곳에서 루프를 도는 등 메시지를 처리할 수 없는 상태에 빠진다면 프로세스는 들어오는 메시지를 처리할 수 없고 화면을 출력할 수도 없는 등 프로그램이 멈춘 것으로 보일 것 입니다.
만약 WinProc 의 스택을 더 확인 해 본다면 gethostbyname 함수를 호출한 것을 확인할 수 있을 것 입니다. http://msdn2.microsoft.com/en-us/library/ms738524.aspx
문서를 확인해 보면 Gethostbyname 함수는 서버 이름만을 파라미터로 받는 함수고 dump 에서 무엇을 찾고 있는지 확인할 수 있을 것 입니다.
0:000> kv 123
ChildEBP RetAddr Args to Child
0017ee14 76f584c5 00000424 40020000 004d9c60 ntdll!NtAlpcSendWaitReceivePort+0x12 (FPO: [8,0,0])
0017ee44 76f5899d 00020000 004d9c60 004e3638 rpcrt4!LRPC_CASSOCIATION::AlpcSendWaitReceivePort+0x2f (FPO: [Non-Fpo])
0017ee78 76f58514 0017eed8 0017ef04 0017f2e8 rpcrt4!LRPC_BASE_CCALL::DoSendReceive+0x48 (FPO: [Non-Fpo])
0017ee90 76f57785 0017eed8 0017ef04 0017eeb4 rpcrt4!LRPC_BASE_CCALL::SendReceive+0x2c (FPO: [Non-Fpo])
0017eea0 76f5766b 0017eed8 756e54f0 0017ef04 rpcrt4!I_RpcSendReceive+0x25 (FPO: [Non-Fpo])
0017eeb4 76f5763a 0017ef04 004d9d18 76ff0146 rpcrt4!NdrSendReceive+0x31
0017eec0 76ff0146 59a28201 00000000 00000000 rpcrt4!NdrpSendReceive+0x9 (FPO: [0,0,0])
0017f2c8 756e5ff3 756e54f0 756e5aec 0017f2e8 rpcrt4!NdrClientCall2+0x18f
0017f2e0 756e5ef9 00000000 0279091c 00000001 dnsapi!R_ResolverQuery+0x19 (FPO: [Non-Fpo])
0017f34c 756e5d34 0279091c 00000001 14000000 dnsapi!Query_PrivateExW+0x257 (FPO: [Non-Fpo])
0017f388 756e6025 00000001 0279091c 00000001 dnsapi!Query_Shim+0xbb (FPO: [Non-Fpo])
0017f3b0 74f461b4 0279091c 00000001 14000000 dnsapi!DnsQuery_W+0x20 (FPO: [Non-Fpo])
0017f3e0 74f4611c 0279091c 00000001 14000000 mswsock!SaBlob_Query+0x2d (FPO: [Non-Fpo])
0017f424 74f459db 0279091c 0031c2e0 00307118 mswsock!Rnr_DoDnsLookup+0x129 (FPO: [Non-Fpo])
0017f6c0 76d75a72 027908b8 00000000 0017f794 mswsock!Dns_NSPLookupServiceNext+0x25c (FPO: [Non-Fpo])
0017f6d8 76d75a52 00307270 027908b8 00000000 ws2_32!NSPROVIDER::NSPLookupServiceNext+0x17 (FPO: [Non-Fpo])
0017f6f4 76d75994 0031bc18 00000000 0017f794 ws2_32!NSPROVIDERSTATE::LookupServiceNext+0x1c (FPO: [Non-Fpo])
0017f720 76d7590a 00307130 00000000 0017f794 ws2_32!NSQUERY::LookupServiceNext+0xa4 (FPO: [Non-Fpo])
0017f764 76d7b11f 00307118 00000000 0017f794 ws2_32!WSALookupServiceNextW+0x74 (FPO: [Non-Fpo])
0017f7b4 76d7ab77 00307118 00000000 0017f7ec ws2_32!WSALookupServiceNextA+0x52 (FPO: [Non-Fpo])
0017f7e0 76d7dba3 00307118 0000013c 0017fa38 ws2_32!getxyDataEnt+0xbd (FPO: [Non-Fpo])
0017fa1c 0040147d 0017fa38 00000000 00070520 ws2_32!gethostbyname+0xb4 (FPO: [Non-Fpo])
0017fbc8 004012fc 00401220 0017fbfc 00401220 badwindow!hangtype1+0x3d (FPO: [Uses EBP] [0,101,0]) (CONV: cdecl) [c:\source\badwindow\badwindow\badwindow.cpp @ 310]
0017fc24 772a87af 00070520 00000111 00008003 badwindow!WndProc+0xdc (CONV: stdcall) [c:\source\badwindow\badwindow\badwindow.cpp @ 274]
0017fc50 772a8936 00401220 00070520 00000111 user32!InternalCallWinProc+0x23
0017fcc8 772a8a7d 00000000 00401220 00070520 user32!UserCallWinProcCheckWow+0x109 (FPO: [Non-Fpo])
0017fd2c 772a8ad0 00401220 00000000 0017ff0c user32!DispatchMessageWorker+0x380 (FPO: [Non-Fpo])
0017fd3c 004010fb 0017fd54 00403938 00000001 user32!DispatchMessageW+0xf (FPO: [Non-Fpo])
0017ff0c 00401817 00400000 00000000 004a0f84 badwindow!wWinMain+0xfb (CONV: stdcall) [c:\source\badwindow\badwindow\badwindow.cpp @ 124]
0017ffa0 76eb19f1 7efde000 0017ffec 7782d109 badwindow!__tmainCRTStartup+0x150 (FPO: [Non-Fpo]) (CONV: cdecl) [f:\sp\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 589]
0017ffac 7782d109 7efde000 0017206b 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo])
0017ffec 00000000 00401987 7efde000 00000000 ntdll!_RtlUserThreadStart+0x23 (FPO: [Non-Fpo])
0:000> da 0017fa38
0017fa38 "AServerThatDoesNotExist14"
Winproc 에서 이러한 함수를 호출하는 것은 좋은 방법이 아니라고 할 수 있습니다.
Hangtype 1 이 무엇을 하는지 좀더 살펴 보도록 하겠습니다. (이 project 에 포함되어 있는 C code 를 보셔도 됩니다.) 저는 어셈블리 언어를 보는 것을 좋아 하는데 이 방법은 source 가 없는 상태에서 분석을 하는데 좋은 연습이 됩니다. Gethostbyname 함수에서 return 주소로 0040147d 를 확인할 수 있고 이를 통해 어떻게 gethostbyname 이 호출되었는지 확인할 수 있습니다. UF 함수를 사용하여 해당 함수의 전체 code 를 어셈블리로 출력할 수 있습니다. 아래 예제에서는 gethostbyname 함수의 return 주소를 uf 의 인자로 사용하였습니다. 어셈블리 code 에 주석을 달아 놓고 나중에 C 로 보도록 하겠습니다.
추가적으로 intel cpu 에서 프로그래밍 하는 방법은 다음에서 확인할 수 있습니다. http://developer.intel.com/design/pentium/manuals/24319001.pdf
0:000> uf 0040147d
Stack 에 로컬 402 byte 의 변수를 만듭니다. Source 를 확인해 보면 sznamebuff[400], int I, 그리고 host 구조체의 포인터를 발견할 수 있을 것 입니다.
badwindow!hangtype1 [c:\source\badwindow\badwindow\badwindow.cpp @ 298]:
298 00401440 81ec94010000 sub esp,194h
악성코드가 stack 에서 실행할 수 없게 stack 을 확인하는 코드 입니다..
298 00401446 a100304000 mov eax,dword ptr [badwindow!__security_cookie (00403000)]
298 0040144b 33c4 xor eax,esp
298 0040144d 89842490010000 mov dword ptr [esp+190h],eax
Ebx 레지스터를 스택에 저장 합니다.
298 00401454 53 push ebx
Sprintf 함수의 함수 포인터를 import table 에서 가져 옵니다.
299 00401455 8b1d7c204000 mov ebx,dword ptr [badwindow!_imp__sprintf (0040207c)]
base/frame 포인터를 스택에 저장 합니다.
299 0040145b 55 push ebp
Gethostbyname 함수 포인터를 import table 에서 가져와 ebp 에 저장합니다.
299 0040145c 8b2d20214000 mov ebp,dword ptr [badwindow!_imp__gethostbyname (00402120)]
ESI 와 EDI 를 stack 에 저장 합니다.
299 00401462 56 push esi
299 00401463 57 push edi
ESI 를 0으로 초기화 합니다.
299 00401464 33f6 xor esi,esi
badwindow!hangtype1+0x26 [c:\source\badwindow\badwindow\badwindow.cpp @ 306]:
ESI 를 stack 에 넣습니다. 이 것은 우리가 만들고자 하는 함수의 3번째 파라미터 이고 초기 값은 0 입니다.
306 00401466 56 push esi
Stack 에서 유효주소를 로드 하여 eax 에 저장합니다.
306 00401467 8d442414 lea eax,[esp+14h]
이것은 무엇일까요? badwindow!`string' (004021ac) 확인해 보면 다음과 같습니다.
0:000> db 004021ac
004021ac 41 53 65 72 76 65 72 54-68 61 74 44 6f 65 73 4e AServerThatDoesN
004021bc 6f 74 45 78 69 73 74 25-64 00 00 00 57 65 20 61 otExist%d...We a
004021cc 72 65 20 69 6e 20 68 61-6e 67 74 79 70 65 32 74 re in hangtype2t
004021dc 68 72 65 61 64 61 00 00-57 65 20 61 72 65 20 69 hreada..We are i
004021ec 6e 20 68 61 6e 67 74 79-70 65 32 74 68 72 65 61 n hangtype2threa
004021fc 64 62 00 00 48 00 00 00-00 00 00 00 00 00 00 00 db..H...........
0040220c 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0040221c 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
Sprintf 에 print 포맷을 우리가 만들고자 하는 함수의 두번째 파라미터로 넣는 것을 확인할 수 있습니다.
306 0040146b 68ac214000 push offset badwindow!`string' (004021ac)
Eax 를 stack 에 넣습니다. eax 는 stack 에 있던 주소로 sprintf 의 첫 번째 파라미터로 사용됩니다. 이것은 Buffer 로 szNameBuffer 입니다.
306 00401470 50 push eax
Sprintf 함수를 호출 합니다. (ebx 에 함수 포인터를 저장했던 것을 기억 하십시오.)
306 00401471 ffd3 call ebx
Stacks grown down or negative as they get larger, so adding in this case is cleaning up the stack from our sprintf call
306 00401473 83c40c add esp,0Ch
szNameBuffer 의 주소를 얻습니다.
307 00401476 8d4c2410 lea ecx,[esp+10h]
Name buffer 의 주소를 스택에 넣습니다.
307 0040147a 51 push ecx
Gethostbyname 을 호출합니다. (ebp 에 함수포인터를 넣었던 것을 기억 합니다.) return 값이 EAX 에 저장되는 것을 기억 합니다.
307 0040147b ffd5 call ebp
250 을 스택에 넣습니다. (gethostbyname 의 return 주소 입니다.)
310 0040147d 68fa000000 push 0FAh
Eax 값을 edi 에 넣습니다. Eax 값은 sleep 함수가 return 되었을 때 덮어써질 것이고 나중에 이 값을 다시 사용해야 해서 Eax 값을 저장해야 합니다. EAX 는 gethostbyname 의 return 값 입니다.
310 00401482 8bf8 mov edi,eax
Sleep 를 호출하여 250 ms 동안 멈추도록 합니다.
310 00401484 ff150c204000 call dword ptr [badwindow!_imp__Sleep (0040200c)]
Int i 값이 들어 있는 ESI 값을 증가 시킵니다. 처음에는 0 이었으며 지금은 ESI+1 값을 가집니다. Loop 를 실행 하면서 계속 증가될 것 입니다.
311 0040148a 83c601 add esi,1
ESI 과 100 을 비교 합니다.
312 0040148d 83fe64 cmp esi,64h
ESI 값이 100 보다 크다면 00401496 으로 jump 해야 합니다.
312 00401490 7f04 jg badwindow!hangtype1+0x56 (00401496)
Gethostbyname 의 return 값인 EDI 값을 확인 합니다. TEST 는 논리적인 비교 입니다. EDI 와 EDI 를 비교하여 EDI 가 0인지 확인 합니다.
304 00401492 85ff test edi,edi
EDI가 0 이라면 이 주소로 jump 합니다. 이 주소는 loop 의 시작 주소 입니다.
304 00401494 74d0 je badwindow!hangtype1+0x26 (00401466)
Stack 을 정리하고 함수를 빠져 나가기 위한 작업을 합니다.
318 00401496 8b8c24a0010000 mov ecx,dword ptr [esp+1A0h]
318 0040149d 5f pop edi
318 0040149e 5e pop esi
318 0040149f 5d pop ebp
318 004014a0 5b pop ebx
318 004014a1 33cc xor ecx,esp
318 004014a3 e8c5010000 call badwindow!__security_check_cookie (0040166d)
318 004014a8 81c494010000 add esp,194h
318 004014ae c3 ret
아래는 hangtype1 의 C 함수 입니다.
void hangtype1(void)
{
int i =0;
char szNameBuff[400];
struct hostent * myhost=NULL;
// Lets see if any of my favorte machines are on the network.
while (!myhost)
{
sprintf(szNameBuff,"AServerThatDoesNotExist%d",i);
myhost = gethostbyname (szNameBuff);
// We are hitting network resource in a loop.
// Let's not hit the server with to many request at once. We will pause 250ms.
Sleep(250);
i++;
if(i>100)
{
break;
}
}
}
무슨 일이 일어났을 까요. 함수가 실행되면 AserverThatDoesNotExist14 를 stack 에서 확인할 수 있을 것 입니다. 이 loop는 100 번 동안 250ms 동안의 sleep 을 가지며 계속 실행되는데 25초 동안 network 을 통해 잘못된 server 이름을 찾으려 한 것을 알 수 있습니다. 이 코드들은 우리가 일반적으로 분석하는 문제점 입니다.
그럼 어떻게 하면 application 을 멈추지 않고 이 함수를 실행할 수 있을 가요? 대답은 간단 합니다. Multithread 를 사용하는 것 입니다. Winproc 에서 hangtype1 을 직접 호출하는 것이 아니고 Thread 를 만들어서 호출하는 것 입니다.
void __cdecl hangtype1fixthread(void *)
{
// our blocking function.
hangtype1();
}
Then from our winproc instead of calling hangtype1 we call another function called hangtype1fix that creates a new thread that in turn executes out hangtype1 code in another thread context so our winproc can continue to execute and process incoming messages. The being thread call is asynchronously . The thread is create immediately and your thread continues to run.
Winproc 에서 hangtype1 을 호출 하는 대신에 hangtype1fix 를 해로운 Thread 를 생성해서 호출하는 것 입니다. 새로운 Thread hangtype1 은 실행될 것이고 WinProc 는 process 로 들어오는 메시지를 처리할 수 있을 것 입니다. Thread 는 비동기적으로 호출될 것 이고 새로운 Thread 가 즉시 생성되어 처리할 것 입니다.
void hangtype1fix(void)
{
_beginthread(hangtype1fixthread, 12000, NULL);
}
이 블로그에서 정보를 얻으셨기를 바랍니다.
Thanks Jeff-