Wednesday, December 26, 2012

Examining Netstat - Windbg Adventures

 

Here is a scenario which helps us understanding how this stuff really works internally. I generally take a longer aproach since the application is unknown or atleast I don't have the source code for this piece of *app. So we start with finding main.


Finding Main

We will use the x (examine command) to enumerate symbols. (assuming you have the Symbol paths set right)
0:000> x netstat!*main*
00000000`ff1f1258 netstat!_imp___getmainargs = <no type information>
00000000`ff1f1634 netstat!main = <no type information>
00000000`ff1f7100 netstat!_native_dllmain_reason = <no type information>
00000000`ff1f5800 netstat!mainCRTStartup = <no type information>
so we have the main netstat!main but notice the other calls which also have main in their name. Well getting back to the old Galvin Schilbert days it says "OS provides the environment for execution". So here it is netstat!mainCRTStartup which initializes the runtime. What do we really meain by intiailzing the runtime ??? Well  you can read more from the MSVCR source code which is generally there with any visual studio installs. The runtime is responsible for initializing the CRT heap (used by malloc to allocate dynamic memory etc.) and other datastructures.
So the mainCRTStartup Calls the application's main function. as can be seen from the following stack (use kvn or kb or k for stack)
0:000> ~*kvn
.  0  Id: 2020.23c0 Suspend: 1 Teb: 000007ff`fffdd000 Unfrozen
 # Child-SP          RetAddr           : Args to Child                                                           : Call Site
00 00000000`0021fec8 00000000`ff5256b1 : 00000000`00000000 00000000`ff525809 01cde338`35926925 00000019`2c6dbbbd : netstat!main
01 00000000`0021fed0 00000000`76eb652d : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : netstat!K32GetModuleBaseNameA+0x19b
02 00000000`0021ff10 00000000`775ac521 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0xd
03 00000000`0021ff40 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x1d
surprised .. !!!! yeah even I was .... WT**** happened here how did we get K32GetModuleBaseNameA calling the main. Where is the damn mainCRTStartup as we see it in the notpad or a simple casts sample I wrote for some experimentations
 # ChildEBP RetAddr  Args to Child             
00 0032f7dc 00b01248 00000001 004e2338 004e2380 casts!main (FPO: [Non-Fpo]) (CONV: cdecl) [d:\addy\projects\learncpp\casts.cpp @ 22]
01 0032f824 759a339a 7efde000 0032f870 77799ef2 casts!__tmainCRTStartup+0x10b (FPO: [Non-Fpo]) (CONV: cdecl) [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0.c @ 278]
02 0032f830 77799ef2 7efde000 75caaefe 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo])
03 0032f870 77799ec5 00b0129e 7efde000 00000000 ntdll!__RtlUserThreadStart+0x70 (FPO: [Non-Fpo])
04 0032f888 00000000 00b0129e 7efde000 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])

so the only difference in this case is I linked the CRT statically with this application which I generally do when I am building standalone applications so that they don't blow up because of side by side installations of the Runtime DLLs.
I guess we are going to explore this fact later on and check out more on or objective of understanding how netstat works.
so we know netstat!main is our guy. How ???? well get some C basics and intuition if you have that question.

Breaking In and follow the flow

Allright !!!  time for some action let's break-in and see how this main works out the connection. Now again we need to rely on the symbolic names of the function and some common sense to  progress through.  To set a break point we need a bp command (check the help file for variants of b* commands).
0:000> bp netsat!main
Hit the g command to continue and hit the break point
0:000> g
ModLoad: 000007fe`ff6b0000 000007fe`ff6de000   C:\Windows\system32\IMM32.DLL
ModLoad: 000007fe`ff780000 000007fe`ff889000   C:\Windows\system32\MSCTF.dll
Breakpoint 1 hit
netstat!main:
now we have two choices
1. Let's see what functions are called by main and pick n choose our next stop (breakpoint)
2.  step through the main (man this is a lengthy one)
Let's try the first one. "uf" is the unassemble function command in windbg. "uf /c" disassembles all the "call" instructions and shows us which functions are called by netstat!main. Following is the stripped output of uf /c which shows the calls which intersted me while debugging
0:000> uf /c netstat!main
 netstat!main+0x52e (00000000`ff881b62):
    call to netstat!DoIP (00000000`ff88221c)
  netstat!main+0x53f (00000000`ff881b73):
    call to netstat!DoIP (00000000`ff88221c)
  netstat!main+0x550 (00000000`ff881b84):
    call to netstat!DoICMP (00000000`ff8823a4)
  netstat!main+0x560 (00000000`ff881b94):
    call to netstat!DoICMP (00000000`ff8823a4)
  netstat!main+0x571 (00000000`ff881ba5):
    call to netstat!DoTCP (00000000`ff882278)
  netstat!main+0x582 (00000000`ff881bb6):
    call to netstat!DoTCP (00000000`ff882278)
  netstat!main+0x593 (00000000`ff881bc7):
    call to netstat!DoUDP (00000000`ff8822d4)
  netstat!main+0x5a4 (00000000`ff881bd8):
    call to netstat!DoUDP (00000000`ff8822d4)
  netstat!main+0x5e9 (00000000`ff881c1d):
    call to netstat!DoConnections (00000000`ff882428)




So we see calls like DoTCP, DoIP etc. I tried using DoTCP as a call but the program exitted. So I decided to step through and see which one do we call and we see while stepping through using "p" command (step over the calls) and the stepping in using "t" trace command.

netstat!main+0x5e0:
00000000`ff881c14 8bd3            mov     edx,ebx
0:000>
netstat!main+0x5e2:
00000000`ff881c16 418bcf          mov     ecx,r15d
0:000>
netstat!main+0x5e5:
00000000`ff881c19 89442420        mov     dword ptr [rsp+20h],eax ss:00000000`000ff5b0=00000000
0:000>
netstat!main+0x5e9:
00000000`ff881c1d e806080000      call    netstat!DoConnections (00000000`ff882428)
0:000> t
netstat!DoConnections:
00000000`ff882428 44894c2420      mov     dword ptr [rsp+20h],r9d ss:00000000`000ff5a8=000001b5
0:000> p
netstat!DoConnections+0x5:
00000000`ff88242d 89542410        mov     dword ptr [rsp+10h],edx ss:00000000`000ff598=00000000
0:000>
netstat!DoConnections+0x9:
00000000`ff882431 894c2408        mov     dword ptr [rsp+8],ecx ss:00000000`000ff590=000000ff
0:000>
netstat!DoConnections+0xd:
00000000`ff882435 53              push    rbx

We see we call the DoConnections Call in this case. The we disassemble the DoConnections call to see what's our next stop.

Breakpoint 1 hit
netstat!DoConnections:
00000000`ff522428 44894c2420      mov     dword ptr [rsp+20h],r9d ss:00000000`0021fb58=000001b5
0:000> kvn
 # Child-SP          RetAddr           : Args to Child                                                           : Call Site
00 00000000`0021fb38 00000000`ff521c22 : 00000000`000000ff 00000000`00000000 00000000`01cc6940 00000000`000001b5 : netstat!DoConnections
01 00000000`0021fb40 00000000`ff5256b1 : 00000000`00000000 00000000`ff525809 00000000`00000000 00000000`ff521340 : netstat!main+0x5ee
02 00000000`0021fed0 00000000`76eb652d : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : netstat!K32GetModuleBaseNameA+0x19b
03 00000000`0021ff10 00000000`775ac521 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0xd
04 00000000`0021ff40 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x1d

now doing that we have a lots of calls which deal with TCP and UDP tables, with or without the owner modules (netstat -b) would be the most probable candidate calling this.
but InternalGetTcpTable2 interests us more.

netstat!DoConnections+0x1ee (00000000`ff522616):
    call to netstat!InternalGetTcpTable2 (00000000`ff525c80)
  netstat!DoConnections+0x23d (00000000`ff522665):
 
so we disassemble this one and check how does it acquire the TCP table
so now we see

0:000> uf /c netstat!InternalGetTCPTable2
netstat!InternalGetTcpTable2 (00000000`ff525c80)
  IPHLPAPI!InternalGetTcpTable2+0x23 (000007fe`faabde8b):
    call to IPHLPAPI!InternalGetTable (000007fe`faabdc80)
there is only one call and this one actually looks like a public call to the IP Helper API documented in MSDN. Fair .. so let's look at the documentation
http://msdn.microsoft.com/en-us/library/windows/desktop/bb408406(v=vs.85).aspx
ULONG WINAPI GetTcpTable2(
  _Out_    PMIB_TCPTABLE2 TcpTable,
  _Inout_  PULONG SizePointer,
  _In_     BOOL Order
);

now  this is pretty much standard code. as documented in MSDN.
So Now WHAT??? we end here ??? nah .. we are suppose to understand how this piece works.
 

THE UNDOCUMENTED 


 Okay !! so we ended up in a call where there is no source or documentation in terms how this is going to flow. Disassembly to the rescue!!!

 0:000> uf /c IPHLPAPI!InternalGetTable
IPHLPAPI!InternalGetTable (000007fe`faabdc80)
  IPHLPAPI!InternalGetTable+0x3f (000007fe`faabdcbf):
    call to ntdll!RtlAllocateHeap (00000000`775d33a0)
  IPHLPAPI!InternalGetTable+0x5e (000007fe`faabdcde):
    unresolvable call: call    qword ptr [rsp+78h]
  IPHLPAPI!InternalGetTable+0x76 (000007fe`faabdcf6):
    call to ntdll!RtlReAllocateHeap (00000000`775b3f20)
  IPHLPAPI!InternalGetTable+0x93 (000007fe`faabdd13):
    call to ntdll!RtlFreeHeap (00000000`775d3200)
 
so we see this function calls a function pointer which cannot be resolved at the moment. Fair Let's break in there and trace through and notice that we are going to call this function pointer from the stack (rsp is the stack pointer)

IPHLPAPI!InternalGetTable+0x5e (000007fe`faabdcde):
    unresolvable call: call    qword ptr [rsp+78h]

0:000> bp 000007fe`faabdcde
0:000> g
Breakpoint 2 hit
IPHLPAPI!InternalGetTable+0x5e:
000007fe`faabdcde ff542478        call    qword ptr [rsp+78h] ss:00000000`0021fa78={IPHLPAPI!GetTcpTable2 (000007fe`faac13d4)}
0:000> t
IPHLPAPI!GetTcpTable2:
000007fe`faac13d4 4883ec38        sub     rsp,38h
disassembling the Table2 call
0:000> uf /c IPHLPAPI!GetTcpTable2
IPHLPAPI!GetTcpTable2 (000007fe`faac13d4)
  IPHLPAPI!GetTcpTable2+0x15 (000007fe`faac13e9):
    call to IPHLPAPI!GetTcpTableInternal (000007fe`faac104c)
this call then calls the Nsi which is the new way of storing network info since windows 2008/vista onwards
0:000> uf /c IPHLPAPI!GetTcpTableInternal
IPHLPAPI!GetTcpTableInternal (000007fe`faac104c)
  IPHLPAPI!GetTcpTableInternal+0xa8 (000007fe`faac10f4):
    call to IPHLPAPI!NsiAllocateAndGetTable (000007fe`faab1080)
  <<SNIP>>

 So we figure the whole call flow since it;s going to be too much if we keep documenting the steps

 0:000> uf /c NSI!NsiEnumerateObjectsAllParameters
NSI!NsiEnumerateObjectsAllParameters (000007fe`ff381130)
  NSI!NsiEnumerateObjectsAllParameters+0x30 (000007fe`ff381160):
    call to NSI!memset (000007fe`ff381120)
  NSI!NsiEnumerateObjectsAllParameters+0xc4 (000007fe`ff3811f4):
    call to NSI!NsiEnumerateObjectsAllParametersEx (000007fe`ff381224)

0:000> uf /c NSI!NsiEnumerateObjectsAllParametersEx
NSI!NsiEnumerateObjectsAllParametersEx (000007fe`ff381224)
  NSI!NsiEnumerateObjectsAllParametersEx+0x29 (000007fe`ff38124d):
    call to NSI!NsiIoctl (000007fe`ff381010)

0:000> uf /c NSI!NsiIoctl
NSI!NsiIoctl (000007fe`ff381010)
  NSI!NsiIoctl+0xb8 (000007fe`ff381070):
    call to kernel32!CreateEventAStub (00000000`76eb1120)
  NSI!NsiIoctl+0x103 (000007fe`ff3810b4):
    call to ntdll!ZwDeviceIoControlFile (00000000`775d1380)
  NSI!NsiIoctl+0x12c (000007fe`ff3810ca):
    call to kernel32!CloseHandleImplementation (00000000`76ec2f20)
  NSI!NsiIoctl+0x15d (000007fe`ff38147f):
    call to ntdll!RtlNtStatusToDosError (00000000`775d5110)
  NSI!NsiIoctl+0x56 (000007fe`ff3814c1):
    call to kernel32!CreateFileWImplementation (00000000`76eb1870)
  NSI!NsiIoctl+0x181 (000007fe`ff381b0e):
    call to kernel32!DeviceIoControlImplementation (00000000`76eb67c0)
  NSI!NsiIoctl+0x18b (000007fe`ff381b18):
    call to kernel32!GetLastErrorStub (00000000`76ec2d70)
  NSI!NsiIoctl+0x65 (000007fe`ff381cc0):
    call to kernel32!GetLastErrorStub (00000000`76ec2d70)
  NSI!NsiIoctl+0x7c (000007fe`ff381ccb):
    call to kernel32!CloseHandleImplementation (00000000`76ec2f20)
  NSI!NsiIoctl+0xc6 (000007fe`ff381cd7):
    call to kernel32!GetLastErrorStub (00000000`76ec2d70)
  NSI!NsiIoctl+0x11a (000007fe`ff381cea):
    call to ntdll!ZwWaitForSingleObject (00000000`775d1350)

The IPHLPAPI -- > NSI!NsiEnumerateObjectsAllParameters --> NsiEnumerateObjectsAllParametersEx --> NsiIoctl --> ZwDeviceIoControlFile

so we are going to send an IOCTL to the Kernel to get this information. Most probably to NSI kernel interface which will boil to AFD or TCPIP since these are the two components which implement the networking facility in windows. 

 We now need a VM to live debug to understand the call flow or we can simply use process monitor to track this same calls but again, process monitor can be a tricky bet since we can only view stacks where we have a file or registry I/O and IOCTL processing in kernel may or may not have these operations but what the heck!! let's give it a shot  And As I suspected we don't see this either in system process or in netstat process contexts. We do see some RPC calls for lsass and some winsock getnameinfo calls but not this ioctl.

 But this is where we end today since we got to know a great deal of the call flow.
 
 

No comments:

Post a Comment