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 “DriverDisk

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 “DriverDisk”

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

By admin