"이 문서는 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-