Bugtation No.92
Wednesday, May 13th, 2009“To” debug “is proper to man.”
- Dmitry Vostokov @ DumpAnalysis.org -
“To” debug “is proper to man.”
- Dmitry Vostokov @ DumpAnalysis.org -
Consider this crash point:
0:000> r
eax=02d0f15c ebx=02a62918 ecx=77e41c30 edx=00000000 esi=ffffffff edi=02a8ed28
eip=76154193 esp=02d0f124 ebp=02d0f130 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
Application!GetData+0xb:
76154193 8b9eac000000 mov ebx,dword ptr [esi+0ACh] ds:0023:000000ab=????????
Seeing 000000ab address we can think that ESI was 0 but it was 0xFFFFFFFF. Adding 0xAC to it produced an effective NULL data pointer 0xAB through integer addition overflow if we consider addition as unsigned. It is easy to see the result if we consider 0xFFFFFFFF as signed -1. Looking at stack trace and function disassembly we see that 0xFFFFFFFF was passed as a parameter:
0:000> kv
ChildEBP RetAddr Args to Child
02d0f130 7616328d ffffffff 02d0f15c 02d0f150 Application!GetData+0xb
[…]
02d0ffec 00000000 740420d8 02a74070 00000000 kernel32!BaseThreadStart+0×34
0:000> u Application!GetData
Application!GetData:
76154188 mov edi,edi
7615418a push ebp
7615418b mov ebp,esp
7615418d push ecx
7615418e push ebx
7615418f push esi
76154190 mov esi,dword ptr [ebp+8]
76154193 mov ebx,dword ptr [esi+0ACh]
This is an example of a sentinel pointer marking the end of a linked list, for example, although NULL pointers having 0 value are usually used. Also -1 value can be used to assign an invalid pointer value.
- Dmitry Vostokov @ DumpAnalysis.org -
When looking at a stack trace of one crashed process we noticed an invalid code pointer. It is not a NULL code pointer but has the same stack trace pattern:
0:000> kL
ChildEBP RetAddr
WARNING: Frame IP not in any known module. Following frames may be wrong.
0013dfb4 00401791 0×5e388583
0013fdf4 0040189f Application!RequestData+0×3e1
0013fee4 00401d0a Application!main+0×3f
0013ffc0 77e4f23b Application!mainCRTStartup+0×16c
0013fff0 00000000 kernel32!BaseProcessStart+0×23
When we look at raw stack data and examine the backward disassembly of the return address we see that invalid code was called from RequestData function and WinDbg stack trace reconstruction is not suspicious (it is structurally and semantically correct):
0:000> dds esp l10
0013dfb8 00401791 Application!RequestData+0x3e1
0013dfbc 00000140
0013dfc0 0013ee50
0013dfc4 00000fa4
0013dfc8 00000000
0013dfcc 00000000
0013dfd0 00000ece
0013dfd4 0013ffc0
0013dfd8 7ffdc000
0013dfdc 00000140
0013dfe0 0000054c
0013dfe4 50000002
0013dfe8 4b4919ac
0013dfec 00000000
0013dff0 00000000
0013dff4 003f003c
0:000> .asm no_code_bytes
Assembly options: no_code_bytes
0:000> ub 00401791
Application!RequestData+0x3c8:
00401778 Application!RequestData+0x3d0 (00401780)
0040177a lea ebx,[ebx]
00401780 push 0
00401782 push eax
00401783 lea ecx,[esp+esi+0E30h]
0040178a push ecx
0040178b push edi
0040178c call Application!recv (0040e382)
When seeing recv call we might suspect that the crash happened just inside that function because the raw stack data upwards (lower addresses) doesn’t have any execution residue left from nested function calls:
0:000> dds esp-100 esp
0013deb8 00000000
0013debc 00000000
0013dec0 00000000
0013dec4 00000000
0013dec8 00000000
0013decc 00000000
0013ded0 00000000
0013ded4 00000000
0013ded8 00000000
0013dedc 00000000
0013dee0 00000000
0013dee4 00000000
0013dee8 00000000
0013deec 00000000
0013def0 00000000
0013def4 00000000
0013def8 00000000
0013defc 00000000
0013df00 00000000
0013df04 00000000
0013df08 00000000
0013df0c 00000000
0013df10 00000000
0013df14 00000000
0013df18 00000000
0013df1c 00000000
0013df20 00000000
0013df24 00000000
0013df28 00000000
0013df2c 00000000
0013df30 00000000
0013df34 00000000
0013df38 00000000
0013df3c 00000000
0013df40 00000000
0013df44 00000000
0013df48 00000000
0013df4c 00000000
0013df50 00000000
0013df54 00000000
0013df58 00000000
0013df5c 00000000
0013df60 00000000
0013df64 00000000
0013df68 00000000
0013df6c 00000000
0013df70 00000000
0013df74 00000000
0013df78 00000000
0013df7c 00000000
0013df80 00000000
0013df84 00000000
0013df88 00000000
0013df8c 00000000
0013df90 00000000
0013df94 00000000
0013df98 00000000
0013df9c 00000000
0013dfa0 00000000
0013dfa4 00000000
0013dfa8 00000000
0013dfac 00000000
0013dfb0 00000000
0013dfb4 00000000
0013dfb8 00401791 Application!RequestData+0x3e1
So we follow recv call forward disassembly (notice that the first jump is indirect):
0:000> u 0040e382
Application!recv:
0040e382 jmp dword ptr [Application!_imp__recv (00410180)]
Application!closesocket:
0040e388 jmp dword ptr [Application!_imp__closesocket (00410170)]
Application!WSAGetLastError:
0040e38e jmp dword ptr [Application!_imp__WSAGetLastError (00410174)]
Application!send:
0040e394 jmp dword ptr [Application!_imp__send (00410178)]
Application!connect:
0040e39a jmp dword ptr [Application!_imp__connect (0041017c)]
Application!htons:
0040e3a0 jmp dword ptr [Application!_imp__htons (00410198)]
Application!setsockopt:
0040e3a6 jmp dword ptr [Application!_imp__setsockopt (00410184)]
Application!socket:
0040e3ac jmp dword ptr [Application!_imp__socket (00410188)]
0:000> dps 00410180 l10
00410180 71ad2f7f ws2_32!recv
00410184 71ad2d47 ws2_32!setsockopt
00410188 71ad410c ws2_32!socket
0041018c 71ad7ca1 ws2_32!gethostbyname
00410190 71ad4f3b ws2_32!WSAStartup
00410194 71ad7b5b ws2_32!gethostname
00410198 71ad28bc ws2_32!htons
0041019c 71ad3da8 ws2_32!WSACleanup
004101a0 00000000
004101a4 00000000
004101a8 00000000
004101ac 00000000
004101b0 00000000
004101b4 45cd184e
004101b8 00000000
004101bc 00000002
0:000> u 71ad2f7f
ws2_32!recv:
71ad2f7f jmp 7fd60000
71ad2f84 sub esp,10h
71ad2f87 push ebx
71ad2f88 xor ebx,ebx
71ad2f8a cmp dword ptr [ws2_32!PrologPointer (71ae4044)],offset ws2_32!Prolog_v2 (71ad6067)
71ad2f94 push esi
71ad2f95 je ws2_32!recv+0×18 (71ad6207)
71ad2f9b lea eax,[ebp-8]
0:000> u 7fd60000
7fd60000 jmp DllA!recv_patch (612101b6)
7fd60005 mov edi,edi
7fd60007 push ebp
7fd60008 mov ebp,esp
7fd6000a jmp ws2_32!recv+0×5 (71ad2f84)
7fd6000f add byte ptr [eax],al
7fd60011 add byte ptr [eax],al
7fd60013 add byte ptr [eax],al
Finally we see that sockets library functions were patched by a 3rd-party module DllA and we need to contact its vendor.
- Dmitry Vostokov @ DumpAnalysis.org -