Thursday, April 12, 2012

Malware Analysis Tutorial 26: Rootkit Configuration

Learning Goals:
  1. Practice the use of user mode WinDbg for inspecting process in non-invasive mode
  2. Practice the use of IMM and WinDbg for data analysis
  3. Understand registry operations
  4. Understand file operations at multiple levels
  5. Understand the high level network operations
  6. Practice the general unpacking technique and searching for the decoding key
Applicable to:
  1. Operating Systems
  2. Assembly Language
  3. Operating System Security
1. Introduction
In this section, we go back to the analysis of lz32.dll and finish the rest of it. We will see a wide variety of malicious operations performed by the malicious lz32.dll injected by Max++, including disk formatting of the hidden drive, generation of network payload and configuration, unpacking of code segment, and infection of other system library files.

Our analysis will be mostly performed in the address range 0x3Cxxxx, and for some part we will have to dig into the raspppoe.sys again. We start the analysis from the address 0x3C1B3E. Follow the instructions below.

2. Lab Configuration
We could simply reuse the lab configuration of Tutorial 20. In general, you need two windows XP instances, one for taking notes and the other for running the malware. Also you need a WinDbg instance sitting on the host to debug the win_debug instance. Specifically, in the win_debug instance, you need to set a breakpoint at 0x3C1B3E. This is the place right before the call of loadDriver("raspppoe.sys"), as shown in Figure 1 below.

Figure 1. Code of Lz32.dll after zwLoadDriver

3. Clean Up Operations
Recall that at 0x3C1B3F, max++ (at lz32.dll) calls zwLoadDriver to load rasppoe.sys. The behavior of rasppoe.sys is explained in the Tutorials 20 to 25. Now lz32.dll has to perform some clean up operations to further hide the trail of the malware. At 0x003C1B48 (see the first highlighted area of Figure 1), it closes the file handle of raspppoe.sys, and then from 0x3C1B55 to 0x3C1B3B, there is a loop which resumes a collection of threads one by one. This is actually to re-enable the windows file protection service.

Challenge 1. Use data tracing/breakpoint technique to prove that the loop from 0x3C1B55 to 0x3C1B3B is to re-enable the windows file protection service.

Note that major responsibility of the infected rasspppoe.sys is to set up the I/O handler for disk operations and also the infection of TDI (TCP/IP) service stack. Many services in raspppoe.sys are delayed for 15 seconds using deferred procedure call or delayed timer on system thread, because they need the further configuration done in lz32.dll. We now present the details.

4. Creation of Virtual Drive
The first interesting part of the code is the creation of a virtual drive. The code is shown in Figure 2. It consists of four steps: it first loads the "fmifs" library, and then it gets the procedure address of "FormatEx", and then it calls the function to format the drive. If you look at the stack content, you will notice the name of the drive is "C2CAD...". Once everything is done, it unloads the library.
Figure 2. Create Virtual Drive \??\C2CAD...

5. Storing Malicious File in Hidden Drive
The next action performed by Max++ is to save the infected driver file raspppoe.sys into the newly created hidden drive. The details is shown in Figure 3.
Figure 3. Store Malicious raspppoe.sys file into Hidden Drive. file name: snifer67
As shown in Figure 3, the first action at 0x3C2000 is a call of MapViewOfSection. Essentially, it maps a section object to the address space of the current object. The section object, before this call, is set up by Max++ to be the mapping of the raspppoe.sys driver file (which is infected). After the call is completed, the entire section object will be mapped to somewhere in the current process, in our case, the starting address is 0x00380000. If you look at the memory dump in Figure 3, you can notice the "4D 5A", which is the magic code of a PE file. Then Max++ creates a file named "osseqw" (which is the result of encoding "snifer67" using the current time stamp). It then saves the contents of the rassppoe.sys into the newly created file.

Challenge 1. Prove that the section object used in the MapviewOfSection call at 0x3C2000 is the one for raspppoe.sys (infected driver).

Challenge 2. Find out when the snifer67 file in the hidden drive is used.

6.Configuration File
The next function to analyze is 0x3C2162. Figure 4 shows its function body. Its main job is to create the configuration file used by the TDI_SEND activities introduced in section 5 of Tutorial 25.

Figure 4. Writing IP/domain names of malicious servers

As shown in Figure 4, Max++ loads and encodes several strings such as<ip></ip> and <host></host>. It's your job to find out all the malicious servers saved to the configuration file.

Challenge 3. Analyze the function at 0x003C20E0. What are its input parameters?

Challenge 4. Identify all the IP addresses and domain names saved into the configuration file in hidden drive.

Challenge 5. Find the name of the configuration file.

7. Saving Decoding Key in Registry
The next action Max++ performs is to save the "decoding key" which is used for self-packing into registry. Figure 5 shows the code.
Figure 5. Saving Decoding Key

As shown in Figure 5, the decoding key is "1100100", which is retrieved from the PE header of Max+++downloader2010.exe and it is used in decoding/unpacking the code. Max++ creates a registry key in IE explorer and saves the value there.

Challenge 6. Prove that the key 11001800 is from the PE header of Max++.

Challenge 7. Find out where the key is used.

8. Creating HTTP Request
Max++ proceeds to function 3C220A, which creates the HTTP request header for downloading more malicious files. Figure 6 shows its contents. The highlighted area in Figure 6 shows the HTTP request header. It is basically to invoke /install/setup.ppc.php on
Figure 6. Creating HTTP Request Header

9.Creation of Malicious File max++.00.x86
Next we analyze the function located at 0x3C16b8. Figure 7 shows its contents. It first opens the ntdll file and reads its basic file information. Then it creates a file called max++.00.x86 in the hidden drive. The file uses the same configuration (basic file information) of ntdll.

Challenge 8. Observe the function call at 0x3C16CD (in Figure 7). It creates a decoding table. Where does the decoding table start and end?

Figure 7. First Part of 0x3c16b8

Figure 8 shows the rest of the function. It establishes the contents of max++.00.x86. The first action is to allocate a virtual memory of 0x8000 bytes (see the highlighted area in Figure 7). Then it first establishes a decoding table in the stack (in range of 0x12D3XX). Then using this decoding table, Max++ first copies the encrypted code from 0x3C2550 to 0x380000 and deciphers the information via multiple passes of decryption. Finally it writes all the contents into max++.00.x86. The file will be later loaded by another system driver.
Figure 8. Establish file max++.00.x86

10. Contact Malicious Server
Next Max++ contacts the malicious server, as shown in Figure 9. It constructs a winsock and connects to and send a HTTP request to it. Notice that the interesting part is the encoding key "11001800" is embedded in the HTTP request (see the memory dump part of Figure 9). The function does not have a receive operation though. It seems to just inform the malicious server that the infection of Max++ is successful.
Figure 9. Send Traffic to intensivedive

Monday, April 9, 2012

Malware Analysis Tutorial 25: Deferred Procedure Call (DPC) and TCP Connection

Learning Goals:
  1. Use WinDbg for kernel debugging
  2. Apply hardware data breakpoint for analyzing data flow
  3. Understand TDI Network Service
  4. Understand key I/O data structures such as _IRP and _IO_STACK_LOCATION 
  5. Understand deferred procedure call
  6. Understand the use of system timer for delayed actions
  7. Understand the use of WorkItem queue and worker routines
Applicable to:
  1. Operating Systems
  2. Assembly Language
  3. Operating System Security
1. Introduction
We now finish the last part of raspppoe.sys and look at its malicious actions regarding the use of deferred procedure call (DPC) and establishment of a TCP connection to remote server. We show how to use WinDbg to find out an encrypted configuration file that contains the list of malicious servers (IP and domain names) that Max++ tries to contact. In this tutorial, combined with Tutorial 24, we also show the multi-thread tricks played by Max++ to make the analysis harder. The network activity (setting up TCP connection and sending and processing data) is accomplished via multiple threads. This creates trouble in debugging.

Our analysis starts from _+38A1.

2. Lab Configuration
We will use the instructions of Section 2 of Tutorial 20. In the following we just remind you of several important steps in the configuration:
(1) You need a separate image named "Win_Notes" to record and comment the code. You don't really need to run the malware on this instance, but just to record all your observations using the .udd file. To do this, you have to modify the control flow of IMM so that it does not crash on .sys files. See Section 2 of Tutorial 20 for details. Jump to 0x100038A1 to start the analysis.
(2) The second "Win_DEBUG" image has to be run in the DEBUG mode and there should be a WinDbg hooked from the host system using COM part -- so here, we are doing kernel debugging.
(3) Set a breakpoint "bu _+38A1" in WinDbg to intercept the driver entry function.

3. Code Starting at _+38A1

Figure 1. Last Part of rasppoe.sys

As shown in Figure 1 (first highlighted part), the first call is IoAllocateWorkItem(PDevice_Object). It produces a structure named IO_WorkItem that describes a work item for system threads. Using WinDbg, we can easily identify the device_object passed to the call.

kd> dt _DEVICE_OBJECT ffa50bb8 -r1
      +0x010 DriverSize       : 0x8e00
      +0x014 DriverSection    : 0x8131d310 Void
      +0x018 DriverExtension  : 0x81131888 _DRIVER_EXTENSION
      +0x01c DriverName       : _UNICODE_STRING "\Driver\Disk"

Then the created IoWorkItem is saved to a global variable.
Challenge 1. Find out when the IoWorkItem is used using data breakpoints.

The next step performed is a call to KeInitializeTimer and the timer object is also saved in a global variable.
Challenge 2. Find when and how the timer is used.

As shown in Figure 1, the subsequent action is a call to KeInitializeDPC. This is an important call. According to MSDN document [1], the prototype of KeInitializeDPC is shown below. Here DPC means deferred procedure call. It is frequently used to service I/O requests (so that low priority calls will be executed later). The function creates and stores a _DPC structure in its first parameter, and registers a function as a second parameter.
VOID KeInitializeDpc(
  __out     PRKDPC Dpc,
  __in      PKDEFERRED_ROUTINE DeferredRoutine,
  __in_opt  PVOID DeferredContext
Using WinDbg, we can soon infer that the deferred procedure to call later is located at _+3016.
kd> p
faf4f8c2 6a00            push    0
kd> p
faf4f8c4 6816f0f4fa      push    offset _+0x3016 (faf4f016)
kd> p
faf4f8c9 be7821f5fa      mov     esi,offset _+0x6178 (faf52178)
kd> p
faf4f8ce 56              push    esi
kd> p
faf4f8cf ff152001f5fa    call    dword ptr [_+0x4120 (faf50120)]
We can set a breakpoint at _+3016 (bp _+3016) and see what is going on.

Next, Max++ calls another interesting function KeSetTimerEx to link all the previous actions together. According to [2], the prototype of KeSetTimerEx is listed in the following:

  __inout   PKTIMER Timer,
  __in      LARGE_INTEGER DueTime,
  __in      LONG Period,
  __in_opt  PKDPC Dpc
The KeSetTimerEx uses a previously constructed timer to set an "alarm" so that after some
timethe DPC procedure that _+3016 is triggered.

Challenge 3. Prove that the alarm will trigger _+3016 15 seconds later.

4. IO Worker Routine that Connects to Server

Figure 2 shows the function body of _+3016.  It's mainly a call to IoQueueWorkItem.
Figure 2. Function body of _+3016
 The following is IoQueueWorkItem's prototype from [3]. It pushes an work item into the queue and associate a function that processes the work item when it's taken out.
VOID IoQueueWorkItem(
  __in      PIO_WORKITEM IoWorkItem,
  __in      PIO_WORKITEM_ROUTINE WorkerRoutine,
  __in      WORK_QUEUE_TYPE QueueType,
  __in_opt  PVOID Context

 Using WinDBG to dump the stack contents, we can easily infer that the WorkerRoutine is located at _+1a0c (in our fiture: it's named DPCWorkItemFunc_connects_to_server). We now proceed to the analysis of _1a0c. By MSDN [4], WorkerRoutine should have the following prototype:

VOID WorkItem(
  __in      PDEVICE_OBJECT DeviceObject,
  __in_opt  PVOID Context

We now proceed to analyze the worker routine _+1a0c. At the first instruction of _+1a0c, we dump the stack contents as following:

kd> dd esp
fafb3d64  80564610 8114edf0 00000000 805622fc
fafb3d74  fafb3dac 804e426b 81227cb0 00000000
fafb3d84  812e9da8 00000000 00000000 00000000
fafb3d94  00000001 80562334 00000000 812e9da8
fafb3da4  00000000 805645fd fafb3ddc 8057aeff
fafb3db4  81227cb0 00000000 00000000 00000000
fafb3dc4  fafb3db8 00000000 ffffffff 804e2490
fafb3dd4  804f8ab0 00000000 00000000 804f88ea
kd> dt _DEVICE_OBJECT 8114edf0  -r1

       +0x01c DriverName       : _UNICODE_STRING "\Driver\Disk"

Clearly, here 80564610 is the return address, 8114edf0 is a device object (see the prototype of WorkItem routine above), and 0x0000 is the context. You can verify that 8114edf0 is a disk device. Figure 3 shows the simple function body of _+1a0c. It consists of only 4 instructions. At 10001A0E, it pushes the offset of an object attributes into the stack and then it calls a function (readConfig_ConnectToServer) located at _+18d6.

Figure 3. Worker Item Routine
Now let's first dump the contents of OBJ_ATTR_CONFIG_FILE and proceed to _+18d6. The dump is shown below: that's clearly a file name inside the hidden disk drive!

kd> dt _OBJECT_ATTRIBUTES faf4a050
   +0x000 Length           : 0x18
   +0x004 RootDirectory    : (null)
   +0x008 ObjectName       : 0xfaf482e8 _UNICODE_STRING "\??\C2CAD972#4079#4fd3#A68D#AD34CC121074\{49B474EB-92D0-464f-B1DD-1F37AABF9D95}"
   +0x00c Attributes       : 0x40
   +0x010 SecurityDescriptor : (null)
   +0x014 SecurityQualityOfService : (null)

5. Read Configuration File in Hidden Drive and Connect to Remote Server

Figure 5. First Part of _+18d6
Now we proceed to _+18d6. Figure 5 shows its first part of the code. The control flow is very straightforward. Max++ first opens a file (the one in hidden drive, as shown in Section 4). Then it reads about the contents of the file, and then establishes an encoding table, and uses this encoding table to decipher the file contents. Shown in the following is the dump of the file contents, and we can now find out some interesting information: the list of IP addresses and domain names to contact!

kd> db fafb3b3c
fafb3b3c  3c 69 70 3e 38 35 2e 31-37 2e 32 33 39 2e 32 31  <ip>
fafb3b4c  32 3c 2f 69 70 3e 3c 68-6f 73 74 3e 69 6e 74 65  2</ip><host>inte
fafb3b5c  6e 73 65 64 69 76 65 2e-63 6f 6d 3c 2f 68 6f 73</hos
fafb3b6c  74 3e 4e 80 98 3b fb fa-12 cc 51 80 08 00 00 00  t>N..;....Q.....
fafb3b7c  00 00 00 00 98 3b fb fa-2e cc 51 80 01 20 f1 df  .....;....Q.. ..
fafb3b8c  02 02 00 00 5d 34 4e 80-4a 61 00 00 f6 32 4e 80  ....]4N.Ja...2N.
fafb3b9c  fd 32 4e 80 12 c6 05 fb-30 00 00 00 38 3c fb fa  .2N.....0...8<..
fafb3bac  08 56 6f 80 00 0d db ba-08 40 00 00 00 00 00 00  .Vo......@......

Challenge 1: replicate the success to find out the contents of the configuration file of Max++.

Figure 6 shows the rest of function _+18d6. The logic is very clear. At 0x10001983, it calls a self-defined function getTagContent (located at _+1486) to look for a tag <ip> in the configuration file. It soon finds out the ip string Then it calls system function RtlIpv4StringToAddressExA to generate the 32-bit representation of the IP address.

Challenge 2. Analyze function _+1486 (getTagContent). What are its parameters and output?

Figure 6. Rest of Function _+18d6
 Then at 0x100019F8 (see the last highlighted area of Figure 6), it calls a self-defined function openConnectTo85.17.xx (located at _+1832). The following lists the dump of the stack contents before the call. Observe its first parameter: d4ef1155. Can you guess its meaning?

kd> dd esp
fafb3a28  d4ef1155 00005000 00000001 ffb4b850
fafb3a38  812e9da8 652e7ecd a4362152 3c135905
fafb3a48  ee8d12d7 d1b04962 0150df55 0fd8ad5f

It is not hard to see that d4ef1155 is the integer representation of IP address (note the byte sequence and the HEX translation, e.g., 0x55 is decimal number 85).

6. Establish the TCP Connection
Function _+1832 establishes a TCP connection to the, however, it does not send anything. The TDI_SEND operation is actually issued by the system thread discussed in Tutorial 24. Why doesn't Max++  try to accomplish in one thread? It's creating trouble for analysts.

Figure 7 presents the function body of _+1832 here. We omit most of the analysis and leave the details to you.

Figure 7. Establish the TCP Connection

As shown in Figure 7, Max++ follows the general procedure of establishing a TCP connection -- it first binds the address and then requests the connection.

Challenge 4. Completely analyze all the details of function _+1832

1.  MSDN, "KeInitializeDPC routine", available at
2. MSDN, "KeSetTimerEx routine", available at
3. MSDN, "IoQueueWorkItem", available at
4. MSDN, "Work Item Routine", available at

Tuesday, April 3, 2012

Malware Analysis Tutorial 24: Tracing Malicious TDI Network Behaviors of Max++

Learning Goals:
  1. Use WinDbg for kernel debugging
  2. Apply the data tracing and hardware data breakpoint points for analyzing data flow
  3. Understand TDI Network Service
  4. Understand key I/O data structures such as _IRP and _IO_STACK_LOCATION
Applicable to:
  1. Operating Systems
  2. Assembly Language
  3. Operating System Security
1. Introduction
This tutorial continues the analysis of the infected raspppoe driver and presents Max++'s malicious network activities and the low level I/O operations that create a lot of trouble in debugging. We show how Max++ directly constructs TDI low level I/O requests for sending out network data. TDI is the windows low level I/O interface for TCP/IP devices.

Our analysis starts from _+1BF2.

2. Lab Configuration
We will use the instructions of Section 2 of Tutorial 20. In the following we just remind you of several important steps in the configuration:
(1) You need a separate image named "Win_Notes" to record and comment the code. You don't really need to run the malware on this instance, but just to record all your observations using the .udd file. To do this, you have to modify the control flow of IMM so that it does not crash on .sys files. See Section 2 of Tutorial 20 for details. Jump to 0x10001BF2 to start the analysis.
(2) The second "Win_DEBUG" image has to be run in the DEBUG mode and there should be a WinDbg hooked from the host system using COM part -- so here, we are doing kernel debugging.
(3) Set a breakpoint "bu _+1bF2" in WinDbg to intercept the driver entry function.

3. Function _+1BF2 (Network Activities)
Function body of _+1BF2 is shown in Figure 1. This is a relatively simple function. The first action (see highlighted area in Figure 1) is a call to KeInitializeQueue. This is to create a queue for processing I/O request. Interestingly, this queue is NOT used in function _+1BF2. You can follow the same technique as shown in Tutorial 23 to find out how the queue is used.

Challenge 1. Use hardware data breakpoints to find out how the I/O queue created at _+1C07 is used. For example, you have to figure out who is inserting new items into the queue?

Figure 1. Start a System Thread

 Next the function creates a system thread (at _+1C1F in Figure 1). The start routine of the system thread is _+393A and the disk driver object will be passed as the parameter to the start routine. Notice that it is already infected (its Major Function is set to _+2BDE)

Challenge 2. Prove that the INFECTED disk driver object is passed as the parameter to the start routine.

Now if we set a breakpoint "bp _+393A" at the start routine, we get into its function body. The function immediately calls _+1b88. The function body of _+1b88 is shown below:

Figure 2. Process IRP Request WorkItem

 Function _+1b88 deserves some special attention and requires skillful analysis. The first portion of the function (0x10001B95 to 0x10001BB5, in Figure 2) is easy to understand. Based on the error code returned by KeRemoveQueue, the function will take proper processing actions. Then if KeRemoveQueue is returning correctly, which is a _LIST_ENTRY instance, we have to understand the actions performed by Max++ from 0x10001BB7 (see Figure 2).

There are four instructions, from 0x10001BB7, seem to reference some data attributes related to EAX. The question is what is the data type of EAX. If we look at instruction at 0x10001BC3 (see the highlighted box in Figure 2), and then the call of IoFreeIrp at 0x1BC7, we can immediately infer that EAX-58 is the entry address of an _IRP. Then based on this fact, we could easily infer the meaning of the rest of the code.

Finally, at 0x10001BDD (see the last highlighted area in Figure 2), Max++ jumps to _+21A1. This address must be set up by Max++ when it sets up the IRP work item.

We now look at function _+21A1. Like any other driver processing logic, it consists of a lot of switch cases. Tracing through the calls using WinDbg, we soon reach function _+1660 (as shown in the following).

Figure 3. Function Body of _+1660

As shown in Figure 3, function _+1660 first reads the current IO status code passed as the parameter,  stored in [EBP+8]. If everything ok (network device ok), it proceeds to generate a GUID (unique identifier), uses sprintf to generate the HTTP header request "/install/setup.php" and calls a function "sendsHTTPReq_Get_InstallPHP" (label we attached to function _+2090).

([LAB NOTE: Because Max++ tried to access x86.00.dll located at 74.117.xx.xx and it failed. The device status is C00000B5. To present that it is successful,  we can use the "ed 0xaaaabbbb 0" command to reset the error code, where "aaaabbbb" is the EBP+8].).

Challenge 3: Trace who is writing to [EBP+8].

Figure 4. Function Body of _+2090: Send out TCP Packet

[Lab Notice: to cheat the malware again and make it believe that the TDI service is there. Set a BP at _+20D4 and then change the value of eax to 1, e.g., using WinDbg command "r eax=1"]
As shown in Figure 4, the job of function _+2090 is to send out network packets. Its main function is to build up IRP requests and invoke the network driver to send out the request. We now expose some of its major operations here.

At _+2102 (see the highlighted area in Figure 4), we encounter the first interesting call: IoBuildDeviceIoControlRequest. By the MSDN document [1], the prototype of the function is listed below. Clearly it returns an instance of IRP object. The interesting parameters are: IoControlCode, DeviceObject, Input/Output Buffer, PKEvent. We now check the parameters of these parameters one by one.
PIRP IoBuildDeviceIoControlRequest(
  __in       ULONG IoControlCode,
  __in       PDEVICE_OBJECT DeviceObject,
  __in_opt   PVOID InputBuffer,
  __in       ULONG InputBufferLength,
  __out_opt  PVOID OutputBuffer,
  __in       ULONG OutputBufferLength,
  __in       BOOLEAN InternalDeviceIoControl,
  __in_opt   PKEVENT Event,
  __out      PIO_STATUS_BLOCK IoStatusBlock

Doing a dump of the stack using WinDbg right before the call of IoBuildDeviceIoControlRequest, we have the following:

kd> dd esp
f7df4d04  00000002 ffb24530 00000000 00000000
f7df4d14  e11c600c 0000008c 00000001 00000000
f7df4d24  f7df4d34 00000000 e10e26c8 00000000
f7df4d34  faf36091 00000008 ffb24530 f7df4d6c
f7df4d44  faf356e2 00000000 e10e26c8 00000000
f7df4d54  00000000 c66258dd 11e17d8d 00089c9e
f7df4d64  02b75027 00000382 f7df4d84 faf3628c
f7df4d74  00000000 e10e26c8 00000000 c00000b5
Clearly, the IoControlCode is 0x2, the DeviceObject is ffb24530, and the output buffer is 0xe11c600c. If you later dump the contents of DeviceObject using "dt _DEVICE_OBJECT ffb24530 -r2" you will find that its driver object is "Driver/TCP". To find out the meaning of IoControlCode, 0x2, simply search for "#define IRP_MJ_CREATE" on Google, you will soon learn that 0x2 means IRP_MJ_CLOSE. Note that interestingly, the output buffer, which is supposed to be overwritten has the following (which is the HTTP request header for

kd> db 0xe11c600c
e11c600c  47 45 54 20 2f 69 6e 73-74 61 6c 6c 2f 73 65 74  GET /install/set
e11c601c  75 70 2e 70 68 70 3f 6d-3d 30 38 30 30 32 37 35  up.php?m=0800275
e11c602c  30 62 37 30 32 20 48 54-54 50 2f 31 2e 31 0d 0a  0b702 HTTP/1.1..
e11c603c  48 6f 73 74 3a 20 69 6e-74 65 6e 73 65 64 69 76  Host: intensediv
e11c604c  65 2e 63 6f 6d 0d 0a 55-73 65 72 2d 41 67 65 6e
e11c605c  74 3a 20 4f 70 65 72 61-2f 39 2e 32 39 20 28 57  t: Opera/9.29 (W
e11c606c  69 6e 64 6f 77 73 20 4e-54 20 35 2e 31 3b 20 55  indows NT 5.1; U
e11c607c  3b 20 65 6e 29 0d 0a 43-6f 6e 6e 65 63 74 69 6f  ; en)..Connectio

Why use it as output buffer (it should be an input buffer instead!)? Let's first examine the _IRP generated by the call, it is stored in EAX. The following shows the dump. The first interesting data member is the StartVa (virtual address) associated with the _IRP, it is 0xe11c6000 (which is very close to the output buffer above). Then the next interesting part is that: stack count is 1, current location is 2, and currentStackLocation is 0x81158a3c.

kd> dt _IRP 811589a8 -r2
   +0x000 Type             : 0n6
   +0x002 Size             : 0x94
   +0x004 MdlAddress       : 0xffa339e0 _MDL
      +0x000 Next             : (null)
      +0x004 Size             : 0n32
      +0x006 MdlFlags         : 0n138
      +0x008 Process          : (null)
      +0x00c MappedSystemVa   : 0x81257000 Void
      +0x010 StartVa          : 0xe11c6000 Void
      +0x014 ByteCount        : 0x8c
      +0x018 ByteOffset       : 0xc
   +0x008 Flags            : 0
   +0x022 StackCount       : 1 ''
   +0x023 CurrentLocation  : 2 ''
     +0x040 Tail             : __unnamed
      +0x000 Overlay          : __unnamed
         +0x000 DeviceQueueEntry : _KDEVICE_QUEUE_ENTRY
         +0x000 DriverContext    : [4] (null)
         +0x010 Thread           : 0x8112c200 _ETHREAD
         +0x014 AuxiliaryBuffer  : (null)
         +0x018 ListEntry        : _LIST_ENTRY [ 0x0 - 0x0 ]
         +0x020 CurrentStackLocation : 0x81158a3c _IO_STACK_LOCATION
         +0x020 PacketType       : 0x81158a3c
         +0x024 OriginalFileObject : (null)

There is no sufficient information of the entire data structure of _IRP, and we again have to rely on google. Searching _IRP and StackCount and CurrentLocation we found the following code from [3]:

#define IoSetNextIoStackLocation(_IRP){

Note that the type of currentLocation is byte, while the currentStackLocation is _IO_STACK_LOCATION (so the -- operator is actually to reduce by 9x24 bytes, i.e., the size of IO_STACK_LOCATION). You can also notice that the stack growth direction is DOWN! In another word, notice that IO_STACK_LOCATION is a subtask of the entire _IRP, i.e., an _IRP can consists of multiple IO_STACK_LOCATION located on the IO_stack, consecutively. To move to the next _IO_STACK_LOCATION, reduce the current IO_STACK_LOCATION address by 24.

Let's print the contents of the current _IO_STACK_LOCATION and see what it is:
kd> dt _IO_STACK_LOCATION 0x81158a3c
   +0x000 MajorFunction    : 0x9c ''
   +0x001 MinorFunction    : 0x89 ''
   +0x002 Flags            : 0x15 ''
   +0x003 Control          : 0x81 ''
   +0x004 Parameters       : __unnamed
   +0x014 DeviceObject     : (null)
   +0x018 FileObject       : (null)
   +0x01c CompletionRoutine : 0x81158a58     long  +ffffffff81158a58
   +0x020 Context          : 0x81158a58 Void

Notice the major function is 0x9c, minor function is 0x89. If you search winnt_types.h you will find that it is MEANINGLESS! Why? Because the _IO_STACK_LOCATION we are displaying is INVALID! Pay attention to the value of stackCount (it's 1), which means that there is ONLY ONE _IO_STACK_LOCATION (subtask) associated with this IRP, and the currentLocation is 2 (which means that 0x81158a58) is actually 0x24 bytes after the ACTUAL _IO_STACK_LOCATION associated with the _IRP!

Now look at the code located at 0x1000211b and 0x1000211E, its counter part in WinDbg is shown below:
faf3611b 8b4860          mov     ecx,dword ptr [eax+60h]
kd> p
faf3611e 83e924          sub     ecx,24h
kd> kd> r ecx

The motivation is clear, now ECX has the real address of the _IO_STACK_LOCATION. The next couple of instructions set up the contents of the _IO_STACK_LOCATION, and the real contents of the _IO_STACK_LOCATION are:

kd> dt _IO_STACK_LOCATION 81158a18
   +0x000 MajorFunction    : 0xf ''
   +0x001 MinorFunction    : 0x7 ''
   +0x002 Flags            : 0 ''
   +0x003 Control          : 0 ''
   +0x004 Parameters       : __unnamed
   +0x014 DeviceObject     : (null)
   +0x018 FileObject       : 0xffbc7698 _FILE_OBJECT
   +0x01c CompletionRoutine : (null)
   +0x020 Context          : (null)

Now the major function is 0xf (IRP_MJ_INTERNAL_DEVICE_CONTROL) and the MinorFunction code is 0x7 (TDI_SEND). If you read the MSDN documentation about TDI_SEND [4], you might notice that the data to send is stored in _IRP->MDL (which contains the HTTP request to,
and the _IO_STACK_LOCATION->FileObject represents the TCP connection socket end-object.

Up to now, we are clear that function _1660 is trying to send a request "/install/setup.php..." to If you open the WireShark on the Linux Gateway, you will be able to capture this packet.

Challenge of the Day: Can you find out the code which is responsible for getting the data returned by the "/install/setup.php"? [hint: use data breakpoints and think about how to use TDI service to receive].

1. MSDN, "IoBuildDeviceControlRequest Routine", available at
2.  MSDN, "KeInsertQueue", available at
3.  winddk.h, available on
4. MSDN, "TDI_SEND", available at