Tuesday, March 27, 2012

Malware Tutorial Analysis 23: Tracing Kernel Data Using Data Breakpoints

Learning Goals:
  1. Use WinDbg for kernel debugging
  2. Apply the data tracing and hardware data breakpoint points for analyzing data flow
  3. Understand how rootkits set up and hide a driver module
Applicable to:
  1. Operating Systems
  2. Assembly Language
  3. Operating System Security
1. Introduction
This tutorial continues the analysis presented in Tutorial 20. We reveal how Max++ performs another round of driver infection, and how it sets up and hides an infected driver. We will also study how to use hardware data breakpoint to trace the use of data and kernel data structures. Our analysis starts from _+37AF.


2. Lab Configuration
In general 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 0x100037AF 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 _+37af" in WinDbg to intercept the driver entry function.

3. Data Breakpoints and Tracing File Name
We now continue the analysis after Tutorial 21. We begin with _+37AF. Figure 1 shows the first couple of instructions. As shown in Figure 1, the first section of the code is to massage a collection of names.

Figure 1. Copy and Manipulate Strings

At 0x100037BF, it is copying string "\??\C2CAD...\snifer67" to the area pointed by EDI. Doing a data analysis in WinDbg yields the following. Clearly, EDI value (the starting address) of the string is 0xFAFAF9F8 (which is ESP+34 at this moment)

kd> db fafaf9f8
fafaf9f8  5c 00 3f 00 3f 00 5c 00-43 00 32 00 43 00 41 00  \.?.?.\.C.2.C.A.
fafafa08  44 00 39 00 37 00 32 00-23 00 34 00 30 00 37 00  D.9.7.2.#.4.0.7.
fafafa18  39 00 23 00 34 00 66 00-64 00 33 00 23 00 41 00  9.#.4.f.d.3.#.A.
fafafa28  36 00 38 00 44 00 23 00-41 00 44 00 33 00 34 00  6.8.D.#.A.D.3.4.
fafafa38  43 00 43 00 31 00 32 00-31 00 30 00 37 00 34 00  C.C.1.2.1.0.7.4.
fafafa48  5c 00 4c 00 5c 00 53 00-6e 00 69 00 66 00 65 00  \.L.\.S.n.i.f.e.
fafafa58  72 00 36 00 37 00 00 00-14 fb 57 80 00 f3 c4 e1  r.6.7.....W.....
fafafa68  00 52 2e 81 00 20 2f 81-00 10 00 00 d8 fa 57 80  .R... /.......W.


Similarly you can infer the second string generated by the swpringf at 0x100037DB (in Figure 1) is "\systemroot\system32\drivers\rasppoe" (this is the name of the randomly picked driver). The name could change in every run.

The the challenge to us is that if we look in the notes window, we are not able to infer where these two strings are used! We have to use WinDbg data breakpoints to figure out where these file/service names are used.

Let's take the second string as an example. By analyzing the input parameter of swprintf (as shown in Figure 1, 2nd highlighted area), we know that the second string "\systemroot\system32\drivers\rasppoe" is located at  0xFAFB7A78, as shown in following. Then we could set a data read breakpoint on it: ba r4 fafb7a78 (this means to watch for any reading on the 4 bytes starting at fafb7a78).

kd> db fafb7a78
fafb7a78  5c 00 73 00 79 00 73 00-74 00 65 00 6d 00 72 00  \.s.y.s.t.e.m.r.
fafb7a88  6f 00 6f 00 74 00 5c 00-73 00 79 00 73 00 74 00  o.o.t.\.s.y.s.t.
fafb7a98  65 00 6d 00 33 00 32 00-5c 00 64 00 72 00 69 00  e.m.3.2.\.d.r.i.
fafb7aa8  76 00 65 00 72 00 73 00-5c 00 6b 00 62 00 64 00  v.e.r.s.\.k.b.d.
fafb7ab8  63 00 6c 00 61 00 73 00-73 00 2e 00 73 00 79 00  c.l.a.s.s...s.y.
fafb7ac8  73 00 00 00 77 7a 56 80-10 0d 00 e1 c4 06 00 00  s...wzV.........
fafb7ad8  a8 7b fb fa 10 0d 00 e1-01 00 00 00 c4 06 00 00  .{..............
fafb7ae8  00 00 00 00 20 0d 00 e1-88 2d 00 e1 f9 ba 13 81  .... ....-......
kd> ba r4 fafb7a78

Now run the program we hit _+0x1b in RtlInitUnicodeString, at this time, if you run Kp (to show the stack contents) you might not be able to get the right sequence of frames in the stack (as shown in the following).


kd> g
Sun Mar 25 20:26:39.359 2012 (UTC - 4:00): Breakpoint 1 hit
nt!RtlInitUnicodeString+0x1b:
804d92c2 66f2af          repne scas word ptr es:[edi]

kd> Kp
ChildEBP RetAddr 
fafb7970 faeaefea nt!RtlInitUnicodeString+0x1b
WARNING: Stack unwind information not available. Following frames may be wrong.
fafb79b4 faeaf808 _+0x2fea
fafb7c7c 805a399d _+0x3808
fafb7d4c 805a3c73 nt!IopLoadDriver+0x66d
fafb7d74 804e426b nt!IopLoadUnloadDriver+0x45
fafb7dac 8057aeff nt!ExpWorkerThread+0x100
fafb7ddc 804f88ea nt!PspSystemThreadStartup+0x34
00000000 00000000 nt!KiThreadStartup+0x16


In this case, we want to step out of RtlInitUnicodeString. There is a command Step Out (shift+f11), however, not working here, because Max++ does not follow the conventional C conventions. We have to press F10 very patiently. After around 10 steps over (F10), we reached _+1a32, as shown below!

kd> p
nt!RtlInitUnicodeString+0x38:
804d92df 5f              pop     edi
kd> p
nt!RtlInitUnicodeString+0x39:
804d92e0 c20800          ret     8
kd> p
_+0x1a32:
faeada32 33c0            xor     eax,eax


_+1a32 is a part of a function in Max++, which is responsible for constructing an instance of _OBJECT_ATTRIBUTES (where "\systemroot\system32\drivers\rasppoe" is served as the ObjectName).

Figure 2. The Function Which Calls RtlInitUnicodeString
Tracing again from _+1a32, we can find that the program flow jumps to _+23e9 (which reads the contents of the driver file and put it in a collection of locked virtual pages).

Challenge 1. Finish the above analysis and provide a detailed report on how the "\systemroot\system32\drivers\raspppoe" string is used.



4. Virtual Pages
We continue the analysis. At _+3803, Max++ calls another function located at  _+23C8 (which reads the contents of a file and puts the contents in virtual pages). There are some interesting technical details here. Figure 3 shows its function body. Note the first highlighted area, it constructs an instance of _OBJECT_ATTRIBUTES that entails the file name "\systemroot\system32\drivers\raspppoe", as discussed in Section 3 (how to trace the use of data). Then Max++ opens the file and queries about the standard file information of the file. When all operations succeed, it proceeds to the creation of virtual pages.
Figure 3. First Part of _+23C8

  We continue to the second part of Function _+23C8 (as shown in Figure 4). In driver implementation, in many cases you have to lock the physical pages for your virtual addresses (so that your contents in RAM will not be swapped into disk by OS). The intention of this part of code is pretty clear: it first requests virtual pages (see the first highlighted area), the virtual page descriptor is saved in a data structure named _MDL (stored at 8121c970). Once successful, it will ask the system to allocate the physical pages (see MmMapLockedPageSpecifyCache). Then Max++ reads the infected driver file into these pages (starting at address 0xf7649000). If you dump the data starts at 0xf7649000, you would find it's really a binary executable (i.e., see the magic 4D5A header info. for DOS header).

kd> dd f7649000
f7649000  00905a4d 00000003 00000004 0000ffff
f7649010  000000b8 00000000 00000040 00000000



Figure 4. Second Part of _+23C8
Now comes the interesting part (see the last highlighted area of Figure 4). Once the file contents of the infected driver are read, Max++ immediately released the physical pages (for virtual address 0xf7649000) immediately. This is quite counter-intuitive, wouldn't Max++ want to use these data later? It's your job to figure it out.

Challenge 3. Use the same trick for tracing the data, set two data breakpoints. One for the _MDL (e.g., in our case it's 0x8121c970)  and one for the starting address of the infected driver executable data (e.g., in our case it's 0xf7649000). Try to figure out if these pages of malicious binary executable are really used or not. In summary, you have to answer the question: why does Max++ release the pages in Figure 4?

Challenge 4. Analyze the function of _+22C3.


5. Infection of Driver Again and the Use of Virtual Pages
At _+3889 Max++ calls function 2D9F. We now analyze its function (as shown in Figure 5). It is used to infect a driver file (the file name is given as the first parameter in its stack frame). The function first creates a section object on the file, then it performs a memcopy from a MDL descriptor to the file, and flushes the contents back to the file.

Figure 5. Infect Driver fips.sys
Challenge 5. use data tracing technique to analyze where is the malicious file content from?

6. Final Set Up of Malicious Disk Driver
In Tutorial 22, we showed you how a malicious disk driver is used to simulate the file requests on "\??\C2CAD..." using a file called "12345678.sav". In the following, we show how this driver is configured by copying attributes from the real disk driver.

Figure 6. Wiring and Copying of Driver Object

 The first part (as shown in the first highlighted area in Figure 6), adjusts the DriverSection field of the infected object. It is actually a basic link list operation, which tries to remove the infected driver from the list of modules. Notice that the type of the DriverSection field (offset 0x14) is _LDR_DATA_TABLE_ENTRY. You can use WinDbg to verify.

Next in the second highlighted area of Figure 6, Max++ tries to copy all the attributes from the original \Driver\Disk object to the infected driver (in this case on the comments it's .serial, the name could change during sessions). There is only one attribute of the infected driver remains: the major function _+2bDE! Up to this point, Max++ has successfully set up the infected disk driver and it has hided it from the loaded module list.

Friday, March 23, 2012

Malware Analysis Tutorial 22: IRP Handler and Infected Disk Driver

Learning Goals:
  1. Use WinDbg for kernel debugging
  2. Understand basic inner working of disk driver
  3. Understand virtual hidden drive creation
  4. Reverse engineering Max++ driver infection technique
Applicable to:
  1. Operating Systems
  2. Assembly Language
  3. Operating System Security
1. Introduction
This tutorial continues the analysis presented in Tutorial 20. We reveal how Max++ uses a modified disk driver to handle I/O requests on the disk it created (its name is "\\?\C2CAD..."). Recall that in section 4.2.3 we showed you Max++ creates a new IO device and hooks it to the malicious driver object, so that whenever an IO request is raised on this device the request will be forwarded to driver object 8112d550, as shown below. Pay attention to the value of MajorFunction (0xfae36bde), this is where IO requests are handled. Obtaining the module base address, we can easily calculate its offset: _+2BDE.

kd> dt _DRIVER_OBJECT 8112d550
nt!_DRIVER_OBJECT
   +0x000 Type             : 0n4
  ...
   +0x02c DriverInit       : 0xfae4772b     long  +0
   +0x030 DriverStartIo    : (null)
   +0x034 DriverUnload     : (null)
   +0x038 MajorFunction    : [28] 0xfae56bde     long  +0



To replicate the experiments of this tutorial, you have to follow the instructions in Section 2 of Tutorial 20. In this tutorial, we perform analysis on the code of raspppoe.sys from _+2BDE (0x10002BDE)

2. Lab Configuration
In general 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 0x10002BDE 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 _+2BDE" in WinDbg to intercept the driver entry function.

3. Background: Windows Driver Development
Opferman provides an excellent introduction and sample code in [1]. In the following, we summarize of the major points here.

(1) Each driver has a driver entry function, its prototype is shown below:

NTSTATUS DriverEntry(PDRIVER_OBJECT pDrv, PUNICODE_STRING reg)

Here pDrv is a pointer to _DRIVER_OBJECT, and reg is a string that represents the registry entry where the driver could store information.

As we shown earlier in Tutorial 20, the DriverEntry function is located at _+372b.

(2) Each driver may have a collection of 28 functions to handle different types of I/O requests (such as close handle, read, write etc.) The IRP Function code can be found at [2] (typical ones are IRP_MR_CREATE and IRP_MR_READ).

You might wonder, do we have to set breakpoints on all of the 28 functions? The answer is YES and NO. Look at the following dump (combined with the dump in section 1).

kd> dd 8112d550
8112d550  00a80004 81210030 00000002 fae54000
8112d560  00008000 ffbd7d80 8112d5f8 001a001a
8112d570  e1389208 8068fa90 00000000 fae5772b
8112d580  00000000 00000000 fae56bde fae56bde
8112d590  fae56bde fae56bde fae56bde fae56bde
8112d5a0  fae56bde fae56bde fae56bde fae56bde
8112d5b0  fae56bde fae56bde fae56bde fae56bde
8112d5c0  fae56bde fae56bde fae56bde fae56bde


At offset 0x38 of the driver object  (the starting of the major function array), all IRP handlers are set to one single function _+2BDE! The malware author tries to be lazy here, and it saves us a lot of job too. We can just concentrate on _+2BDE then!

Now before we move on, we should know that each IRP handler function has the following prototype:

NTSTATUS Handler(PDEVICE_OBJECT pDevice, PIRP pIRP)

Here, the first parameter is a device object, and the second parameter represents the IRP request to handle.

When we hit the _+2BDE handler, we could easily find out the contents of the two input parameters (device located at 8112d550 and irp located at 00070000) as below:

kd> dd esp
fafb73fc  81210030 8112d550 00070000 81210030
fafb740c  fafb7460 804e37f7 81210030 ffbbe7e8
fafb741c  00000000 fb07c7a9 81210030 c000014f
fafb742c  00000000 00000000 c3a408e0 00000000
fafb743c  00000001 00000000 804e2490 fa047501
fafb744c  00000000 fafb7450 fafb7450 804fb1a9
fafb745c  00000000 fafb748c fb07ce80 81210030
fafb746c  fafb7484 ffb6fe10 81210030 ffb6fe10
kd> dt _DEVICE_OBJECT 8112d550
nt!_DEVICE_OBJECT
   +0x000 Type             : 0n4
   +0x002 Size             : 0xa8
   +0x004 ReferenceCount   : 0n-2128543696
   +0x008 DriverObject     : 0x00000002 _DRIVER_OBJECT
   +0x00c NextDevice       : 0xfae54000 _DEVICE_OBJECT
   ...
kd> dt _IRP 00070000
nt!_IRP
   +0x000 Type             : 0n193
   +0x002 Size             : 0
   +0x004 MdlAddress       : 0x00000100 _MDL
  ...




4. Anatomy of Infected Disk Driver
Figure 1 shows you the first part of the IRP handler function at _+2BDE.
Figure 1. Infected Disk Driver

As shown in Figure 1, the control flow  is a very simple decision procedure. First it takes out the PDEVICE_OBJECT pointer from EBP+8 (1st parameter) and compare it with a global variable stored at 100061B0 (see highlighted area). Clearly, the global variables stores the newly created infected device (for \??\C2CAD...). If it is not a request to \??\C2CAD, the flow jumps to 10002BFD (second highlighted area), which calls PoCallDriver to relay the request to low level (real) drivers to do the work; otherwise it calls a self-defined function handleIRPForVirtualVolume which performs the real operation to simulate the virtual disk.

Challenge 1. Analyze the logic between 10002BFD and 10002C25 (highlighted area in Figure 1). Especially, explain the instructions at 0x10002C16 and 0x10002C19.

5. Simulating the Virtual Disk Operations
Now we will analyze the function handleIRPForVirtualVolume. It is located at _+292A. In this case, you need to set a breakpoint using "bp _+292A" in WinDbg. Figure 2 shows its major function body. Notice that you can easily infer from the context that EBX is an input parameter of the function, EBX points to the IRP request right now!

Figure 2. Function body of handleIRPForVirtualVolum


Now comes the interesting part. Look at Figure 2, at 0x1000293C EAX now has the "MajorFunction" of _IO_STACK_LOCATION  (the value is one of the IRP_MJ_xxx types). Then there is a big switch case statement (see the highlighted area in Figure 2), which redirects the control flow to handle each of the different IRP requests such as READ, WRITE, etc.

Challenge 2. Argue that the statement about "0x1000293C EAX now has the "MajorFunction" (the value is one of the IRP_MJ_xxx types" is true. You may need to find out the definition of IRP_MJ_xyz values.

As an example of how Max++ simulates the disk volume operation, we show how it handles the IRP_MJ_READ request. Figure 3 shows the handler code.

Figure 3. Simulate the Disk Operation on File
  First, let's look at the definition of _IO_STACK_LOCATION which represents an I/O operation task. Note that at this moment, ESI points to the current _IO_STACK_LOCATION, the following is its contents. You can easily infer that it's current Device Object is \??\C2CAD...

kd> dt _IO_STACK_LOCATION ff9c7fd8
nt!_IO_STACK_LOCATION
   +0x000 MajorFunction    : 0x3 ''
   +0x001 MinorFunction    : 0 ''
   +0x002 Flags            : 0x2 ''
   +0x003 Control          : 0 ''
   +0x004 Parameters       : __unnamed
   +0x014 DeviceObject     : 0xffb746d8 _DEVICE_OBJECT
   +0x018 FileObject       : (null)
   +0x01c CompletionRoutine : (null)
   +0x020 Context          : (null)


Now look at the first instruction LEA EAX, [ESI-24] in Figure 3. The purpose here is to move 0x24 bytes away (note the direction of stack) and the size of _IO_STACK_LOCATION (0x24). So EAX is now pointing to a new _IO_STACK_LOCATION instance. The next couple of instructions copy the first 9 words of the existing _IO_STACK_LOCATION to the new.

Then at 0x10002B10 (look at the highlighted area of Figure 3), it assigns the value of ECX (from global variable at DS:[1000614C]) to offset 0x18 of the new _IO_STACK_LOCATION. Notice that 0x18 is the FileObject attribute (see above dump of _IO_STACK_LOCATION!). The following is the dump of  the File Object pointed by ECX:

kd> dt _FILE_OBJECT 811b25d0
nt!_FILE_OBJECT
   +0x000 Type             : 0n5
   +0x002 Size             : 0n112
   ...
   +0x02c Flags            : 0x40040
   +0x030 FileName         : _UNICODE_STRING "\WINDOWS\system32\config\yknueenf.sav"
   +0x038 CurrentByteOffset : _LARGE_INTEGER 0x0

   ...





Now it's pretty clear that the READ operation on the disk volume is actually achieved by CONSTRUCTING A NEW _IO_STACK_LOCATION task on the "*.sav" file created by Max++ earlier!

The last interesting point is at 0x10002B17: Max++ hooks up a function for the CompleteRoutine (offset 0x1c of _IO_STACK_LOCATION), the intention is pretty clear: the data stored on the *.sav file is encrypted, and Max++ now decodes it when reading it out.

We've finished a very challenging and interesting analysis of a portion of the infected disk driver. Now it's your job to finish the rest:

Challenge 3. What happens when FormatEx operation is performed on the virtual disk volume?

Challenge 4. Analyze all the other IRP_MJ_ operations supported by the infected disk driver (hint: this could take considerable efforts).




References
[1] T. Opferman, "Driver Development Introduction Part I", available at http://codeproject.com
[2] MSDN, "IRP Function Code", available at

Saturday, March 17, 2012

Malware Analysis Tutorial 21: Hijack Disk Driver

Learning Goals:
  1. Use WinDbg for kernel debugging
  2. Understand basic inner working of disk driver
  3. Understand virtual hidden drive creation
  4. Reverse engineering Max++ driver infection technique
Applicable to:
  1. Operating Systems
  2. Assembly Language
  3. Operating System Security
1. Introduction
This tutorial shows you how to use WinDbg to analyze the malicious .sys (driver) file (let it be raspppoe.sys which is randomly infected by Max++). We will learn some basic analysis techniques using WinDbg.

Once the driver file is loaded, Max++ will establish a virtual hidden drive to store the malicious files it unpacked from itself or downloaded from the Internet. To achieve this goal, Max++ has to perform some low level disk operations. First, it will establish a file called "12345678.sav", which actually stores all the information of the hidden disk drive. Then it overwrites the disk/file driver so that all requests (read/write operations) to the hidden disk drive is actually performed on the file "12345678.sav".

To replicate the experiments of this tutorial, you have to follow the instructions in Section 2 of Tutorial 20.

2. Lab Configuration
In general 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.
(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 _+372b" in WinDbg to intercept the driver entry function.

Once your Win_Notes environment is set up you should have the following dump at 0x1000372b.
Figure 1. Win_Notes Dump

  

Figure 2 shows the initial setting of WinDbg.

Figure 2. Win_Debug ScreenShot


In the following we use "_" to represent the base address of the driver file. In the Win_Notes, the base address is 0x10000000 and in the WinDbg, it is based on the run-time value, e.g., 0xfafe6400. Whenver we refer to an address we will use the form of _ + offset, e.g., _+372b (which is the entry address of the driver)


3. Basic WinDbg Skills for Inspecting Function Parameters
This section introduces several useful techniques you can use to analyze function calls when using WinDbg. We will take the function call at 0x10003743 (in Figure 1) as an example. Our objective is to analyze: what is the function and what are its parameters.

Since we might have to step into the function in WinDbg, let's first set a breakpoint at the next immediate instruction after 0x10003743, if you look at Figure 1 you will notice that the next address is _ + 3749.

In WinDbg let's set two breakpoints:
kd> bp _+3743
kd> bp _+3749
kd> g

You might notice that here we are using "bp" instead of "bu", this is because the module name "_" can be resolved by WinDbg now after the module is loaded.

Now let's examine the data around ESP.  Typing "dd esp", we have the following. Clearly, currently ESP value is 0xfafb39c0 and the first parameter would be located at 0x81184008 and the second parameter would be 0x0000005c and so on.
kd> dd esp
fafb39c0  81184008 0000005c 81139f38 e1533360
fafb39d0  00000000 dd841000 fafb3a18 812e6f80
fafb39e0  8054b6b8 81184076 e14fd07e 81184000
fafb39f0  805702de e14fd008 e14fd000 e14fd080
fafb3a00  00000001 00000000 00000080 00000020
fafb3a10  81184076 80557ee0 81184000 80566ca2
fafb3a20  e14fd07e 805a65ee 805a669a 81184000
fafb3a30  e1bc9908 00000000 e14fd008 e14fd010


Now press F11 in WinDbg into the function _+3743 you will have the following:
kd> t
nt!wcsrchr:
80506d93 8bff            mov     edi,edi

Notice that it shows that we are currently located in the function body of nt!wcsrchr. This allows us to identify the function name: wcsrchr. There could be an easier way to identify it using the "ln" command in WinDbg. However, interestingly, it does not work in our session and at this moment we do not know the cause.

Searching the documentation for wcsrcchr(str, c), we soon find that wcsrchr is the wide character version of strrchr which searches the last occurrence of a character "c" in a string "str". Here the wide character means that each character actually takes two bytes (for internationalization and localization purpose).

So far we could infer that 0x81184008 is the starting address of the string, and the character is 0x5c. Doing a data dump of 0x81184008, we have:

kd> db 0x81184008
81184008  5c 00 52 00 45 00 47 00-49 00 53 00 54 00 52 00  \.R.E.G.I.S.T.R.
81184018  59 00 5c 00 4d 00 41 00-43 00 48 00 49 00 4e 00  Y.\.M.A.C.H.I.N.
81184028  45 00 5c 00 53 00 59 00-53 00 54 00 45 00 4d 00  E.\.S.Y.S.T.E.M.
81184038  5c 00 43 00 6f 00 6e 00-74 00 72 00 6f 00 6c 00  \.C.o.n.t.r.o.l.
81184048  53 00 65 00 74 00 30 00-30 00 31 00 5c 00 53 00  S.e.t.0.0.1.\.S.
81184058  65 00 72 00 76 00 69 00-63 00 65 00 73 00 5c 00  e.r.v.i.c.e.s.\.
81184068  2e 00 6d 00 72 00 78 00-73 00 6d 00 62 00 00 00  ..m.r.x.s.m.b...
81184078  da 01 0c 00 00 00 00 00-ff ff ff ff 18 00 00 00  ................


Look at the above data dump you can immediately infer that the string is "\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services\.mrxsmb" and 0x5c is the "\" character. Clearly, Max++ is trying to get the service name associated with the driver. Keep in mind that the string (service name) is from the Driver_Object (the first parameter of the entire driver entry function)!

Now type "g" again in Windbg so that it hits _+3749. Then run the program by F10 (step over) several times, you will soon reach the code at _+375f (the 0x1000375f in Figure 1). You might notice that it is comparing the value located at [ebx] with 0x2E. Look at the dump you might notice that the ASCII value for 0x2E is "."

So what's the purpose of the code?

_+0x375f:
fae6775f 66833b2e        cmp     word ptr [ebx],2Eh

kd> db ebx
81184068  2e 00 6d 00 72 00 78 00-73 00 6d 00 62 00 00 00  ..m.r.x.s.m.b...
81184078  da 01 0c 00 00 00 00 00-ff ff ff ff 18 00 00 00  ................
81184088  00 10 00 00 01 00 00 00-00 00 00 00 d9 01 00 00  ................
81184098  00 00 00 00 58 4d 4c 03-00 00 00 00 d9 01 0c 00  ....XML.........
811840a8  00 00 00 00 ff ff ff ff-18 00 00 00 00 10 00 00  ................
811840b8  01 00 00 00 00 00 00 00-30 00 00 00 00 00 00 00  ........0.......
811840c8  4c 51 4c 03 00 00 00 00-30 00 0c 00 00 00 00 00  LQL.....0.......
811840d8  ff ff ff ff 18 00 00 00-00 10 00 00 01 00 00 00  ................


The purpose is to examine if the service name starts with ".". Recall that in Tutorial 18 (section 4), when Max++ infects the driver, it also modifies its registry entry (duplicates all the registry contents and creates a new entry name "."). This time, the code is verifying that it is indeed the infected driver!

If this is indeed the infected driver and now since it's "correctly" running. Max++ now has to hide its trace and properly set up the registry key. The next action it's going to perform is to remove the "." and "LEGACY_" registry entry. It's now your job to analyze the two function calls at _+3797 and _+379d (0x10003797 and 0x1000379d in Figure 1).

Challenge 1. Analyze the function call at 0x10003797 in Figure 1 (what is it trying to do and what are its function parameters?)
Challenge 2. Analyze the function call at 0x1000379d in Figure 1 (what is it trying to do and what are its function parameters?)

4. Infecting File Driver
We now look at Max++'s behavior regarding disk driver. Right after modifying the registry entries, Max++ calls function _+36CA. [For your lab, simply go to 0x100036CA in your notes environment and "bp _+36CA" in WinDbg]. As shown in Figure 2, the function consists of two important steps: (1) to create a disk device "\??\C2CAD ..." and set up the 12345678.sav file for simulating all file operations on the disk. This is done by the function call at 0x100036DE; and (2) to infect all disk devices by setting up their IRP requests handlers properly; this is done at the JMP of 0x100036E4 (it jumps to 0x10002C95). We now proceed to these two important calls.
Figure 2. Infection of Disk Driver

4.1 Create Device "\??\C2CAD..." (_+3108)
At 0x100036DE, Max++ calls function createDeviceC2CAD (_+3108). Figure 3 displays the first part of function _+3108 (createDeviceC2CAD is the name we assigned to the function). It consists of two function calls: (1) at 0x1000312C, Max++ calls ntoskrnl.IoCreateDevice to create a device calls "\??\C2CAD..."; and then (2) at 0x10003140, Max++ calls ntoskrnl.ObMakeTemporaryObject to make the newly created device partially effective.

4.1.1 Create Disk Volume

Figure 3. Add Disk Device and Update IRP Handler
The following is the official declaration of IoCreateDevice() function from MSDN [1]. To analyze the contents, we could use the same technique introduced earlier and stop at _+312C to observe the stack. We listed the values of each of the attributes in the following as well (in the comments area).

NTSTATUS IoCreateDevice(
  __in      PDRIVER_OBJECT DriverObject, //"\Driver\.mrxsmb"
  __in      ULONG DeviceExtensionSize,
  __in_opt  PUNICODE_STRING DeviceName, //""\??\C2CAD972#4079#4fd3#A68D#AD34CC121074"
"
  __in      DEVICE_TYPE DeviceType,
  __in      ULONG DeviceCharacteristics,
  __in      BOOLEAN Exclusive,
  __out     PDEVICE_OBJECT *DeviceObject
);

In summary: its purpose is to create a new device named "\??\C2CAD972..." whose driver is ".mrxsmb".

Challenge 3: where is the resulting DEVICE_OBJECT located? (hint: look at the last parameter of IoCreateDevice).

The next function is to call ObMakeTemporary on the newly allocated DEVICE_OBJECT and call ObDereferenceObject again. These two function are reserved function of the OS and there is no official documentation. The purpose is to completely destroy the newly created device object! (so that it's not living in kernel any more!) You might wonder why?

This is because once the device object is deleted, you will not be able to find the device "\??\C2CAD..."anymore. But, the driver mechanism for handling request to "\??\C2CAD..." has somehow partly survived the deletion of device object. The next step of Max++ is to complete the construction of IO handling for "\??\C2CAD..." and simulate its actions using a single file called 12345678.sav.

4.1.2 Create Simulation File 12345678.sav

We now look at how the file 12345678.sav is created. As shown in Figure 4, it consists of three steps: (1) At 0x10003176 it encrypts 12345678.sav using the last AGP write time and generates a random file name such as yknueenef.sav; (2) it then at 0x100031B4 calls zwCreateFile to create the file, (3) it calls zwSetFileControlInfo to make sure that the file is not compressed.
Figure 4. Create File 12345678.sav
To see the trick of the encoding file name, look at the following WinDbg dump below (before and after the call at 0x10003177).

kd> dd esp
fafaf8f4  fafaf940 81138bb8 812f2000 812f2068
fafaf904  fafaf927 0073005c 00730079 00650074
fafaf914  0072006d 006f006f 005c0074 00790073
fafaf924  00740073 006d0065 00320033 0063005c
fafaf934  006e006f 00690066 005c0067 00320031
fafaf944  00340033 00360035 00380037 0073002e
fafaf954  00760061 00000000 00000000 00000000
fafaf964  00000000 00000000 00000000 00000000
kd> db fafaf940
fafaf940  31 00 32 00 33 00 34 00-35 00 36 00 37 00 38 00  1.2.3.4.5.6.7.8.
fafaf950  2e 00 73 00 61 00 76 00-00 00 00 00 00 00 00 00  ..s.a.v.........
fafaf960  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fafaf970  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fafaf980  00 00 00 00 00 00 00 00-23 00 00 00 c1 c8 4d 80  ........#.....M.
fafaf990  08 00 00 00 10 8b 13 81-10 8b 13 81 cc fd fa fa  ................
fafaf9a0  30 00 00 00 10 8b 13 81-00 20 2f 81 68 20 2f 81  0........ /.h /.
fafaf9b0  7c fc fa fa 00 00 00 00-7c fc fa fa e3 76 e0 fa  |.......|....v..

kd> p
_+0x317c:
fae0717c 53              push    ebx
kd> db fafaf940
fafaf940  79 00 6b 00 6e 00 75 00-65 00 65 00 6e 00 66 00  y.k.n.u.e.e.n.f.
fafaf950  2e 00 73 00 61 00 76 00-00 00 00 00 00 00 00 00  ..s.a.v.........
fafaf960  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fafaf970  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fafaf980  00 00 00 00 00 00 00 00-23 00 00 00 c1 c8 4d 80  ........#.....M.
fafaf990  08 00 00 00 10 8b 13 81-10 8b 13 81 cc fd fa fa  ................
fafaf9a0  30 00 00 00 10 8b 13 81-00 20 2f 81 68 20 2f 81  0........ /.h /.
fafaf9b0  7c fc fa fa 00 00 00 00-7c fc fa fa e3 76 e0 fa  |.......|....v..


Challenge 4. Analyze the details of the function call at 0x10003177 (encrypt file name).
Challenge 5. Analyze the details of the function call at 0x100031B4 (create file)
Challenge 6.  Analyze the details of the function call at 0x100031E5 (set file control information)
Figure 5. Set up Properties of 12345678.sav and Test Disk Drive

 As shown in Figure 5, the next action Max++ performs is to set the end of file of 12345678.sav to 0x0100 0000 (because this is going to be a big file that holds a lot of stuff). It then gets the device handler of 12345678.sav (note that the current disk driver is "FileSystem\sr"), however, this handler will be never used (by the IoDeleteDevice at the end). Because at 0x1000329E, it directly jumps and skips the call of IoDeleteDevice call.

Challenge 7. Analyze the details of the function call at 0x100031FD (zwSetInformation).
Challenge 8. Analyze the details of the function call at 0x1000322E (getDevice)
Challenge 9. Analyze the details of the function call at 0x10003297 (which calls _+302E)

Note that function _+302E (in challenge 9) just tests the opening of "\??\C2CAD..." and it fails (no wonder, because the IRP handling has not been set up for the new disk driver yet).

4.2 Infect Disk Devices (_+2C95)
We now look at the function at 0x10002C95 (the jump is made from 0x100036E4). Figure 6 shows its function body. This function infects (modifies) all device objects hooked to the Disk driver. We now check the details in depth. As shown in the highlighted areas in Figure 6, the function performs to actions: (1) first it calls IoEnumerateDeviceObjectList to get the list of device objects registered with the "\Driver\Disk" driver - in our system, we get a list of two devices; (2) it then uses a loop to modify the devices related (creating a new device and making some copies as well as modifications). But what's the purpose of all these actions? We need to get more details and trace the execution.

Figure 6. Infect Devices Hooked to Disk Driver

4.2.1 Device Object List
We are interested in figuring out the device list involved. To achieve this goal, we need to first check out the parameters of IoEnumerateDeviceObjectList. According to [2], the function prototype of IoEnumerateDeviceList is listed below:

NTSTATUS IoEnumerateDeviceObjectList(
  __in   PDRIVER_OBJECT DriverObject,
  __out  PDEVICE_OBJECT *DeviceObjectList,
  __in   ULONG DeviceObjectListSize,
  __out  PULONG ActualNumberDeviceObjects
);

Clearly, the first parameter is the INPUT driver object (related to which we want to enumerate all devices), the second parameter is a pointer points to the place holder for a list of _DEVICE_OBJECTs, and the fourth parameter is another integer point which points to the integer variable that holds the number of device objects.

To examine the contents of the stack, we display the first four words starting from the ESP value, as shown below. Here, the first param (DriverObject) is 81299670 and the fourth param (ActualNumberDeviceObjects) is fafab9bc.


kd> bp _+2cd8
kd> g
fae36cd8 ff15d480e3fa    call    dword ptr [_+0x40d4 (fae380d4)]
kd> dd esp
fafab5a0  81299670 fafab5b4 00000400 fafab9bc

kd> dt _DRIVER_OBJECT 81299670
nt!_DRIVER_OBJECT
   ...
   +0x018 DriverExtension  : 0x81299718 _DRIVER_EXTENSION
   +0x01c DriverName       : _UNICODE_STRING "\Driver\Disk"
  

By examining the contents of _DRIVER_OBJECT using the WinDbg dt command, we are able to know that the driver is "\Driver\Disk". After stepping over the function call, we can identify that there are two Device Objects involved, and they are 812fb810 and 812fc030: . Details are shown below.

kd> p
_+0x2cde:
fae36cde 395dfc          cmp     dword ptr [ebp-4],ebx
kd> dd fafab9bc
fafab9bc  00000002 fafabc7c fae377af ffb3a6e8

kd> dd fafab5b4
fafab5b4  812fb810 812fc030 00000000 e1b63f30

We could display the contents of the two device object at as below. First note their difference in Sector size. What is your guess? You could infer that _DEVICE_OBJECT 812fc030 is not a volume! According to "If the device object does not represent a volume, this member is set to zero. If the device object represents a volume, this member specifies the volume's sector size, in bytes." in [3].

kd> dt _DEVICE_OBJECT 812fb810
nt!_DEVICE_OBJECT
   +0x000 Type             : 0n3
   +0x002 Size             : 0x368
   +0x004 ReferenceCount   : 0n0
...
   +0x0ac SectorSize       : 0x200
   +0x0ae Spare1           : 0
   +0x0b0 DeviceObjectExtension : 0x812fbb78 _DEVOBJ_EXTENSION
   +0x0b4 Reserved         : (null)
kd> dt _DEVICE_OBJECT 812fc030
nt!_DEVICE_OBJECT
   +0x000 Type             : 0n3
   +0x002 Size             : 0x518
   +0x004 ReferenceCount   : 0n0
  ...
   +0x0ac SectorSize       : 0
   +0x0ae Spare1           : 1
   +0x0b0 DeviceObjectExtension : 0x812fc548 _DEVOBJ_EXTENSION
   +0x0b4 Reserved         : (null)
 
4.2.2 The Loop That Performs Infection
We now look at the very tricky actions of related to infection. The loop picture is shown below.
Figure 7: Part 1 of the Loop: check If this the module


At  _+2CF6 (or 0x10002CF6), EDI is now pointing to the current _DEVICE_OBJECT being visited. You can verify the information below (it's now pointing to the second device at 812FC030). Note that the loop starts to process the LAST device in the list! Can you figure out why?

kd> bp _+2CF6
kd> g
Sat Mar 17 10:40:52.406 2012 (UTC - 4:00): Breakpoint 1 hit
_+0x2cf6:
fae36cf6 8b4728          mov     eax,dword ptr [edi+28h]
kd> r edi
edi=812fc030


Challenge 10. Prove that the loop as shown in Figure 6 is processing the device objects from the end to the beginning.

Now, as shown in Figure 7, Max++ is performing a lot of checks on the device properties, mostly performed on the DeviceExtension structure. The device extension structure is device specific and we could not get more details here, but the 2nd device module has passed all checks and now Max++ performs the modification of the module.

4.2.3 Infection
The second part of the loop is shown in Figure 8.

Figure 8. Create New Device Object to Wrap Old Device Object


The first interesting action is the call of IoCreateDevice() at _+2D34. Its first parameter is a DRIVER_OBJECT. When we dump its contents we found something interesting:
kd> dt _DRIVER_OBJECT ffb3a6e8
nt!_DRIVER_OBJECT
   +0x000 Type             : 0n4
  ...
   +0x02c DriverInit       : 0xfae3772b     long  +0
   +0x030 DriverStartIo    : (null)
   +0x034 DriverUnload     : (null)
   +0x038 MajorFunction    : [28] 0xfae36bde     long  +0


This is the infected driver (entry address 372b)! So Max++ is creating another IO device which is associated with the infected driver. Note that the DEVICE_OBJECT returned by the function will be stored at [EBP-8]. You can infer this from the IoCreateDevice() specification and the code.

Next, Max++ will perform some copy actions. For example, at _+2D3E, it is copying the StackSize attribute (located at offset 0x30) of _DEVICE_OBJECT from the old device object to the new one. Then, at _+2D4D, it is setting the first word (guess: AttachedDevice) of the DeviceExtension to the old device. If you look into details, it seems to be performing some standard linked list operations which modifies the DeviceExtension of the new device object in the following ways:
At DeviceExtension[0] it stores the OLD device object, at DeviceExtension [4] it stores the real attached low level hardware device (controled by ATAPI)! While the device itself is registered with the INFECTED driver!

Thus, it is very clear what Max++ wants to do: it inserts another layer of device into the IRP request handler chain such that any disk IRP request will go through the INFECTED DRIVER (entry function: 372b)!

At this point, we have a big picture: Max++ has completed the first part of wiring of the malicious driver to disk operations (a new wrapper disk device object is created). All IRP requests related to disk will be wired to the malicious driver first! But notice that the "\??\C2CAD..." device has not been completely set up. We'll continue to the second part in the next tutorial.

References
1. Microsoft, MSDN, "IoCreateDevice", available at http://msdn.microsoft.com/en-us/library/windows/hardware/ff548397(v=vs.85).aspx
2. Mcrosoft MSDN, "IoEnumerateDeviceObjectList", available at http://msdn.microsoft.com/en-us/library/windows/hardware/ff548342(v=vs.85).aspx
3. Microsoft MSDN, "Device Object", available at http://msdn.microsoft.com/en-us/library/windows/hardware/ff543147(v=vs.85).aspx

Saturday, March 10, 2012

Malware Analysis Tutorial 20: Kernel Debugging - Intercepting Driver Loading

Learning Goals:
  1. Use WinDbg for kernel debugging
  2. Patch Debugger to Defend Malicious Actions by Malware
  3. Understand Driver Entry
  4. Interception of Driver Loading
Applicable to:
  1. Operating Systems
  2. Assembly Language
  3. Operating System Security
1. Introduction
We now explore the more challenging part of a reverse engineering process. In the past, all the malicious actions performed by Max++ have been at the application level (ring-3), which can be effectively traced by Immunity Debugger. After Max++ injects code into a randomly selected driver file, and loads it with zwLoadDriver(), the analysis becomes more difficult - Immunity Debugger is not able to analyze ring-0 code and we have to use WinDbg as a kernel debugger. Unfortunately, WinDbg does not have a convenient notes-taking support. In this case, we will duplicate the VBox instance to two. One used for taking notes (where we will have Immunity Debugger to take comments on code), and the other used for kernel debugging (and it will be controlled by a WinDbg from the host via COM port).

In this tutorial, we concentrate on the sophisticated setup of the lab environment and kick start the analysis of the driver entry function. Our job is to intercept the loading of the driver that Max++ infects.

In the following, we call the VBox instance with IMM the Win_Notes instance and the instance to be debugged the Win_Debug instance. We first begin the introduction of how to set up the lab environment, and then we start the kernel debugging process.

Let us assume the name of the infected driver is rasppoe_2.sys.Note that the version of the IMM we used for this tutorial is 1.8.3. The instructions listed in Section 2 is for this version only.

2. Lab Configuration
The lab configuration of this tutorial is sophisticated. It contains the following four steps:
(1) Take the infected driver.
(2) Make the duplicate copy of VBox instance.
(3) Start the Win_Notes environment.
(4) Start the Win_Debug instance.

2.1 Retrieve the Infected Driver
The first step is to retrieve the infected driver. You can simply follow the instructions in Section 2 of Tutorial 19 (Anatomy of Infected Driver) and save the drivers. Once the driver is taken, you have to email these files back to yourself, and restore the snapshot of the clean system (because it has been infected and Max++ has removed itself from disk). Then download these files from your email. From this point, we can duplicate the VBox instance.

2.2 Make the Duplicate Copy of VBox instance
Now in VirtualBox right click on theWinXP instance and select CLONE, and rename the new instance to Win_Notes and select the full clone and all snapshots. Up to now, you should have two VBox instances of the WinXP system. We use Win_Notes for taking comments only.

Hofstra students can pick up both images from my office.

2.3 Start the Win_Notes Environment
The purpose of the Win_Notes environment is to take the notes. As the malware will crash IMM for a certain reason, we have to change the control flow of IMM a bit to successfully take our notes. Follow the steps below to set up the environment:

(1) Start IMM.
(2) In the first instance of IMM, open the IMM exe file from c:\Program Files\Immunity Debugger\IMM.exe
(3) Set a SOFTWARE breakpoint at 0x004E6095 . This is some vulnerable part of the IMM debugger. It occasionally crashes given mal-formatted data. We now provide some brief explanation here. By no means, we are trying to reverse engineer the IMM debugger, but just want to correct some bugs and provide a minor fix on the current version of the debugger.
   At 0x004E6095 (as shown in Figure 1), IMM is trying to put a "\0" at the end of a string. Here ESI points to the beginning of a string and EDI contains its length. Now, if ESI and EDI are both set to NULL, what happens is the instruction will trigger a segmentation fault (it's then trying to access address 0xFFFFFFFF which leads to segmentation fault).
   To solve the problem, we have to skip this instruction when ESI/EDI value is not right.
Figure 1. Reset the EIP to Skip one struction
  Follow the instructions below:
   Now load the rapppoe_2.sys in the second IMM, and let it run. We will hit the breakpoint 0x004E6095 several times in the 1st IMM. Whenever you see the ESI/EDI pair is 0, launch the Python command window (see Figure 1, the 2nd button on the toolbar), and type the following command to readjust EIP.
   imm.setReg("EIP", 0x004E609A)

  But if the ESI/EDI is ok, don't do the above, just hit F9 and let it continue. You will repeat the above for several times until the .sys file is loaded. Note that, during the process, you will get a couple of warnings like some other modules are out of range, just click ok and let it go.

(4) Now in the second IMM, click View -> Executable Modules, and double click on rapppoe_2.sys, you will be able to jump to the starting address of the module. The module should start from 0x10001000 (PUSH ESI).  Figure 2 shows our analysis window, loaded with the comments (Hofstra students can get the .udd file from my office). 

Figure 2. The Driver File Dump
 (5) We will need an additional step to finish: Click View->Modules in the 2nd IMM, and record its entry address. No matter which driver file Max++ picks to infect,  the entry address (offset) is always 0x372B (as shown in Figure 3).
Figure 3. Entry Point of the Module raspppoe_2.ssys
(6) Now right click in the CPU pane -> Go To (0x1000372B), and you should be located at the entry point of the drive module. The Win_Notes environment is prepared and you can use it to take notes (placing comments on the code). Figure 4 shows you the first part of the malicious infected driver.
Figure 4. Entry Part of the Infected Driver

2.4 Trace the Win_Debug Instance
We now describe how to use the WinDbg on the host to perform the tracing of the driver module. Our purpose is to stop the system on the Driver_Entry function of raspppoe_2.sys. Note that the function can only be traced by WinDbg because IMM is a ring-3 debugger. Our purpose is to stop at the driver entry function. This is accomplished by the following steps.

(1) Start WinXP_DEBUG image in DEBUGGED mode. Now in your host system, start a windows command window and CD to "c:\Program Files\Debugging Tools for Windows (x86)" (where WinDBG is installed). Type "windbg -b -k com:pipe,port=\\.\pipe\com_12" (check the com port number in your VBox instance set up).

(2)  Now in your WinDbg window, type "bu _+372b", this is to set a breakpoint at offset 0x372b for module named "_". You might wonder where the "_" is coming from. Later, we will show you how we find out that after the zwLoadDriver() is called by Max++, t a module named "_" is added by Max++.

(3) Now type "g" twice to let the system go. In the following, we will run Max++ in a controlled way, until we load the driver.


(4) Now launch IMM in the WinXP instance, clear all breakpoints and hardware breakpoints in IMM (see View->Breakpoints and View->Hardware Breakpoints).

(5) Go to 0x4012DC and set a hardware breakpoint there. (why not software bp? Because that region will be self-extracted and overwritten and the software BP will be lost). Pay special attention that once you go to 0x4012DC, directly right click on the line to set hardware BP (currently it's gibberish code).

(6) PressF9 several times run to 0x4012DC. You will encounter several breakpoints before 0x4012DC. If you pay attention, they are actually caused by the int 2d tricks (explained in Tutorial 3 and 4, and 5). Simply ignore them and continue (using F9) until you hit 0x4012DC.

Figure 5 shows the code that you should be able to see. As you can see, this is right before the call of RtlAddVectoredException, where hardware BP is set to break the LdrLoadDll call (see Tutorial 11 for details).
Figure 5: code at 0x4012DC
(7) Now scroll down about 2 pages and set a SOFTWARE BREAKPOINT at 0x401417. This is right after the call of LdrLoadDll("lz32.dll"), where Max++ finishes the loading of lz32.dll. Then hit SHIFT+F9 several times until you reach 0x401417 (you will hit 0x7C90D500 twice, this is somwhere inside ntdll.zwMapViewSection which is being called by LdrLoadDll).



Figure 6: code at 0x401407

(8) Now we will set a breakpoint at 0x3C1B3E  .  As shown in Figure 7, Goto 0x3C1B3E  and set a SOFTWARE BREAKPOINT. Press SHIFT+F9 to run to 0x3C1B3E  . (You may see a warning that this is out range of the code segment, simply ignore the warning).
Figure 7: The Code Right Before zwLoadDriver
Figure 7 shows the code that you should be able to see at 0x3C1B3E  . This is right before the call zwLoadDriver.

*** Now we'll capture the driver loading event.  in WinDbg, pres "Ctrl+Break" to stop the program. click Debug-> Event Filters, select the LoadModule event and click "Add". Then "g" to continue ***

Now go back to the WinXP_Debug, press F8 twice and execute the zwLoadDriver function and you might see that your WinXP_Debug instance is frozen, because now the WinDbg hitsmodule load event first. If you do an "lm" command in WinDbg, you will see that the newly loaded module is "_". Type "g" again in WinDbg, you will now hit the breakpoint bu _ + 372b.

As shown by Figure 8, the instruction we are stopped at is located at fae3772b (this is offset 372b relative to the base address of module "_").

Figure 8: WinDbg DUMP
Press Alt+7 (or View->Disassembly), you can watch the current instructions (as shown in Figure 9). If you compare with Figure 1, you can verify that we are indeed in the Driver Entry function of raspppoe_2.sys.
Figure 9: Disassembly Dump



3. Driver Entry Function
We now proceed to the analysis of the Driver Entry function using WinDbg. According to [1], a driver entry function on Windows has the following prototype:

DRIVER_INITIALIZE DriverEntry; NTSTATUS DriverEntry(
  __in  struct _DRIVER_OBJECT *DriverObject,
 __in  PUNICODE_STRING RegistryPath 
)

The first parameter is an object containing the information about the related driver object, and the second parameter is a unicode string containing the path to the corresponding registry key in the registry database. Our first goal of the analysis is to figure out the values of these two input parameters.

If you look at the first couple of instructions of the function (see Figure 9, from fae3772b to fae37738), they are to set up the stack frame (e.g., to preserve the previous EBP value, to readjust ESP value to create the stack frame and to preserve other relevant registers).

In WinDbg, Press F10 (single step) to run to 0xFAE37738 (see Figure 9), according to the C function call parameter passing convention, we have the 1st parameter (_DRIVER_OBJECT) located at EBP+8 and the 2nd parameter (PUNICODE_STRING) at EBP+C. Now that these two values are simply pointers. Let's first get these two pointers by typing "dd EBP", we have the memory dump below: [Note that the first column is the address for each row and each row has 4 computer words (32-bit each),  e.g., fafabc7c (1st word in 1st row) is the address for fafabd4c (first row), and fafabc8c (1st word of second row) is the address for 00000000). Here EBP value is fafabc7c.


kd> dd EBP
fafabc7c  fafabd4c 805a399d 811254d8 81184000
fafabc8c  00000000 f7a1dcf4 00000000 00000018
fafabc9c  00000000 fafabcc0 00000010 00000000
fafabcac  00000000 811254d8 fafabd70 811462f0
fafabcbc  81184000 00200020 81128f48 e101b000
fafabccc  811254d8 81143280 00000020 8000030c
fafabcdc  fae34000 00000308 00120010 e14fe090
fafabcec  0000007a 00060004 e1bcc5f8 00000000


Clearly, the 1st parameter _DRIVER_OBJECT value is 811254d8 (the entry address of the object), and the _UNICODE_STRING (2nd parameter) starts at 81184000.

We now display their values:
kd> dt _DRIVER_OBJECT 811254d8
nt!_DRIVER_OBJECT
   +0x000 Type             : 0n4
   +0x002 Size             : 0n168
   +0x004 DeviceObject     : (null)
   +0x008 Flags            : 2
   +0x00c DriverStart      : 0xfae34000 Void
   +0x010 DriverSize       : 0x8000
   +0x014 DriverSection    : 0x81143280 Void
   +0x018 DriverExtension  : 0x81125580 _DRIVER_EXTENSION
   +0x01c DriverName       : _UNICODE_STRING "\Driver\.NDProxy"
   +0x024 HardwareDatabase : 0x8068fa90 _UNICODE_STRING "\REGISTRY\MACHINE\HARDWARE\DESCRIPTION\SYSTEM"
   +0x028 FastIoDispatch   : (null)
   +0x02c DriverInit       : 0xfae3772b     long  +0
   +0x030 DriverStartIo    : (null)
   +0x034 DriverUnload     : (null)
   +0x038 MajorFunction    : [28] 0x804fa87e     long  nt!IopInvalidDeviceRequest+0
kd> dt _UNICODE_STRING 81184000
nt!_UNICODE_STRING
 "\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services\.NDProxy"
   +0x000 Length           : 0x70
   +0x002 MaximumLength    : 0x70
   +0x004 Buffer           : 0x81184008  "\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services\.NDProxy"



As you note, this matches the role of raspppoe.sys (which is used for remote internet connectoin).

Challenge of the Day: Analyze the call at _+3743, what are its parameters and what is it doing?

References
[1] Microsoft, "DriverEntry Routine", available at http://msdn.microsoft.com/en-us/library/windows/hardware/ff544113(v=vs.85).aspx