Pages

Saturday, February 25, 2012

Malware Analysis Tutorial 18: Infecting Driver Files (Part II: Simple Infection)

Learning Goals:
  1. Understand the frequently used tricks by malware to infect driver files
  2. Understand the role of registry in Windows operating systems
  3. Practice analyzing function calls
  4. Practice reverse engineering sophisticated data structures in memory
Applicable to:
  1. Operating Systems
  2. Assembly Language
  3. Operating System Security
1. Introduction
This tutorial continues the analysis of Tutorial 17. As shown in Tutorial 17, Max++ randomly picks up a system module which satisfies two criteria: (1) has export table and (2) file size > 0x4c10. This tutorial shows how Max++ infects the randomly picked driver and run it.

We will analyze the code from 0x3C1E73  in this tutorial.


2. Lab Configuration
(0) Start WinXP 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). When WinDbg initiates, types "g" (go) twice to let it continue.

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

(2) 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).

(3) 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 1 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 1: code at 0x4012DC
(4) 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 2: code at 0x401407

(6) Now we will set a breakpoint at 0x3C1E73  .  Goto 0x3C1E73  and set a SOFTWARE BREAKPOINT. SHIFT+F9 there. Press SHIFT+F9 to run to 0x3C1E73  . (You may see a warning that this is out range of the code segment, simply ignore the warning).

(Figure 3 shows the code that you should be able to see at 0x3C1E73  . This is right after the loop for randomly picking up a system module to infect.


Figure 3. Code Starts at 0x3C1E73 (Double Check of System Functions)

Section 3. Warm-Up before Infection
As shown in Figure 3, right after the random pick loop (at 0x3C1E73), EDI points to the name of the system module to infect (in our case, it's "mouclass.sys" - the name will be different every time you run Max++). Starting from 0x3C1E73 to 0x3C1F83, Max++ performs some warm-up activities to check of all important system functions are working right. Details are shown in Figure 3. It checks the following functions:

(1) zwCreateSection - this function allows to create a section (a shared memory object between processes), it can also be used to map between a file and a memory region.
(2) zwMapViewOfSection - map a section object into the address space of a process
(3) zwReadFile - read file contents into a memory region.

If all checks are good, it will continue to the infection.

Challenge 1. As shown in Figure 3, analyze the sprintf call at 0x3C1E84. What are its parameters? What's returned?
Challenge 2. As shown in Figure 3, analyze the zwCreateSection call at 0x3C1EEE. What are its parameters? What's returned?
Challenge 3. As shown in Figure 3, analyze the zwMapViewOfSection call at 0x3C1F20. What are its parameters? What's returned?
Challenge 4. As shown in Figure 3, analyze the zwReadFile call at 0x3C1F73. What are its parameters? What's returned?


Section 4. Mods on Registry
At 0x3C1FAD, Max++ calls function 0x3C196A. This is the function that performs the real malicious infection operation.

The first series of actions Max++ performs is to create a registry entry and set its values, as shown in Figure 4. Assume that the file to infect is mouclass.sys, Max++ creates a registry key named "\registry\MACHINE\SYSTEM\CurrentControlSet\Services\.mouclass". Then it creates several value pair, such as the "start" and the "ImagePath". Note that in the "CurrentControlSet\Services" there is originally a registry named "mouclass" (just without the .dot). The original version of the registry and the new version are displayed in Figure 5(a) and Figure 5(b) respectively, using regedit.
Figure 4. Registry Keys


Look at Figure 5 and compare the values for "Start" and "ImagePath". There are some interesting observations: In the old Mouclass entry, the "Start" type is "0" (meaning load at boot time) and the new ".Mouclass" start type is "3" (meaning load on demand). The ImagePath of the old Mouclass entry is the absolute path to the driver, while the ImagePath of the new Mouclass is "\*". The meaning of these parameters can be found in MSDN documentation [1]. As far as we know, the "\*" is not a legal value for ImagePath. There must be some special handling for this once Max++ infects the file/disk driver. The meaning of "\*" is currently an open question to us.
Figure 5. Registry "mouclass" (old) and ".mouclass" (new)

Section 5. Disable File Protection Service (Function 0x003C15AB)
Next, Max++ disables a file protection service called  sfc_os.dll. Here sfc stands for "Microsoft Windows File Protection Service", it monitors the file operations on system files (including drivers). Max++ needs to disable sfc_os.dll to overwrite the system driver file for infection. To do this, Max++ calls function 0x003C15AB at 0x003C1A16 (shown at the bottom of Figure 3). Function 0x003C15AB kills the kernel process/thread related to sfc_os.dll one by one to achieve the goal. We now delve into the details of Function 0x003C15AB.


Figure 6. Search for "Winlogon.exe"
Shown in Figure 6 is our old friend zwQuerySystemInformation. This times, Max++ uses it to retrieve the information of live processes in the system (last time in Tutorial 16 and 17, it retrieves system modules/drivers). It then enumerates the information of each process and compares its name with "Winlogin.exe" until it finds the Winlogon.exe process.

Challenge 1. How do you tell that the function call at 0x3C1612 (RtlEqualUnicodeString) is to compare with Winlogin.exe? Show the parameters of the RtlEqualUnicodeString call. (hint: use WinDbg to display UNICODE_STRING).

Figure 7. Query Debugging Information
Max++ then does a query on the debugging information of Winlogon.exe process (as shown in Figure 7). We leave the analysis details to you and you can refer to [2] for details of RtlqueryProcessDebugInfo. Potentially, Max++ could take advantage of the information to tell if the system is being debugged or not, but it didn't perform any detection/anti-debugging actions here and directly jumps to function 0x003C14D7 (at 0x003c1651, see Figure 7). Function 0x003C14D7 disables the file protection service.For now, we just keep in mind that the _DEBUG_BUFFER is located at 0x00900000, which might be useful later. Note that the _DEBUG_BUFFER at 0x00900000 stores the information of Winlogon.exe!

Challenge 2. Trace to the RtlQueryProcessDebugInfo, and show the contents of the _DEBUG_MODULE_INFO.

5.1 Kill the File Protection Service
We now trace into function 0x003C14D7. Figure 8 shows its function body. As usual, the first thing you should look at is the input parameter of the function. By examining the first copy of instructions you would soon notice that the function is reading from two registers:
(1) EAX - it contains value 0x00900000 (note: this is the _DEBUG_BUFFER for winlogon.exe)
(2) ECX - it contains the _SYSTEM_MODULE_INFORMION of winlogon.exe.

We leave the  details of inferring the semantics of EAX, ECX parameters to you:
Challenge 2. How do you infer the data type of the EAX, ECX (as input parameters) of function 0x003C14D7?

Figure 8. Function body (Part I) of Function 0x3C14D7

The first part of the function is a loop (from 0x3C14EC to 0x3C150E, see Figure 8). Max++ reads the _DEBUG_MODULE_INFORMATION one by one, and compare its name with "sfc_os.dll" (see the function call at 0x3C14FB, and the stack contents in Figure 8). It jumps out of the loop once it finds the _DEBUG_MODULE_INFORMATION for "sfc_os.dll".

The tricky part of the analysis of this part of the code is the lack of documentation for kernel data structures such as _DEBUG_BUFFER and _DEBUG_MODULE_INFORMATION.

Challenge 3. ***** Observe the instruction at 0x003C14E3, how would you infer that EDI now contains the number of modules, assuming that EAX has the PVOID ModuleInformation of _DEBUG_BUFFER? To simply search "typedef _DEBUG_BUFFER" gives you no more information than a PVOID type. You have to read the source code of RtlQueryProcessDebugInformation (at [3]) and LdrQueryProcessModuleInformationEx (at [4]) to figure it out.

Figure 9. Kill Threads Related to sfc_os.dll

Now, since Max++ identifies the DEBUG_MODULE_INFORMATION of sfc_os.dll, it is able to know the ImageBase address of sfc_os.dll. The next immediate action is a loop which searches all threads of winlogon.exe and kill those that are related to sfc_os.dll. The details are shown in Figure 9.

The basic control flow is:
  for each thread in winlogon.exe
        open thread
        get the base address of the thread
        if base address of the thread - imageAddress of sfc_os  <= imageSize of sfc_os
                  suspend the thread

In summary: if the base address of a thread is inside the image range of sfc_os, it is killed.

We now list some challenges to help you understand the program logic in Figure 9.
Challenge 4. Observe the instruction at 0x3C1519 of Figure 9: MOV EAX, [ESI+8]. How would you infer that EAX will then contain the base of sfc_os.dll?

Challenge 5. Observe the instruction at 0x3C1562 of Figure 9: MOV EAX, [EBP-8]. How would you infer that EAX will then contain the base address of the thread?

Up to now, the sfc_os.dll module should be completely disabled and the OS is not performing security check on the driver files located on disk.

Section 6. The Infection
We now discuss the infection. This part of the code starts from 0x3C1A1B (right after it returns from the call of 0x3C15AB - to disable sfc_os.dll).

Figure 10 shows the first action taken by Max++. It makes sure that the randomly picked driver (e.g., mouclass.sys - note the name is changing everytime you run it) is not compressed. This is achieved by the call of zwFsControlFile.

Challenge 6. Observe the instruction at 0x003C1A63 (call of zwFsControlFile). Analyze the semantics of its function parameters.

Figure 10. Change File Property

Figure 11 displays the next actions of Max++: It creates a section object and then a view. Section object is used by Windows to share memory between processes. It can also be used to map a file to memory. Max++ uses the second case: it creates a section object that maps the contents of the driver file to infect (let it be mouclass.sys) to memory and then creates a view of the section object. You need to analyze the highlighted function calls to get more information, e.g., what is the starting address of the memory region of the file mapping and what is the size?
Figure 11. Create Memory Mapping of File
Figure 12. New Section Object


Challenge 7: Analyze the highlighted functions in Figure 11 and answer the following questions: (1) where is the mapped memory region of mouclass.sys (the file to infect), i.e., where it starts and where it ends? (2) how do you tell that the call zwCreateSection is not to create a shared memory region between two processes, but to map a file? (hint: you could click View->View Memory, and you will notice the starting address of the new section object created, see Figure 12)


Figure 13. Decoding and Copy to Mapped File

Next Max++ establishes a decoding table (see Figure 13), and copies some contents from its own stack (0x12XXXX range) to 0x00380000 (the mapped region for file mouclass.sys). Notice that the value 0x00380000 could vary in a different section. After a number of decoding rounds, we finally have the contents starting from 0x00380000 (see Figure 13, the memory dump section). You will notice the 0x4D5A, which is the magic word for "DOS header".

Conclusion: Max++ performs a very simple infection approach. It just brutally overwrites the beginning of the driver file to infect and does not do any "weaving" of the LONG JUMP instructions to maintain the original logic of the infected file. Once the infected file is overwritten, it loses its original functions! 



The final action Max++ takes is to set up the checksum of the PE header and then writes the contents of the memory region to the file (by calling zwFlushVirtualMemory, see Figure 14).

Challenge 8. Analyze the highlighted area 2 of Figure 14, explain why the instruction is setting the PE checksum?

Figure 14. Set up Checksum and Write Contents Back


As shown in Figure 14 (highlighted area 4), Max++ immediately call zwLoadDriver() to load the newly updated file. If we want to delve into the logic of the new driver file, we would not be able to, because the driver will run in privileged mode while IMM is a ring3 debugger. In this case, we need kernel debugger WinDbg. We will move on to the analysis of the infected driver file in the next tutorial.

References
1. Microsoft, "CurrentControlSet\Services subentry keys", Availabe at http://support.microsoft.com/kb/103000
2.Unknown, "RtlQueryProcessDebugInformation as Anti-Dbg Trick", Available at http://evilcodecave.wordpress.com/2009/04/11/rtlqueryprocessdebuginformation-as-anti-dbg-trick/
3. Unofficial source code of RtlQueryProcessDebugInformation (http://doxygen.reactos.org/d8/dd5/ndk_2rtlfuncs_8h_a18ff51253f8e8f51e2018bcf6d1ca533.html)
4. Unofficial source code of LdrQueryProcessModuleInformationEx (http://doxygen.reactos.org/d7/d55/ldrapi_8c_a2925d74ddee77bdef1e1abab8e1ae8d6.html#a2925d74ddee77bdef1e1abab8e1ae8d6)


Sunday, February 19, 2012

Malware Analysis Tutorial 17: Infecting Driver Files (Part I: Randomly Select a System Module)

Learning Goals:
  1. Understand the frequently used tricks by malware to infect driver files
  2. Understand the role of registry in Windows operating systems
  3. Practice analyzing function cals
  4. Practice reverse engineering sophisticated data structures in memory
Applicable to:
  1. Operating Systems
  2. Assembly Language
  3. Operating System Security
1. Introduction
This tutorial shows some frequently used tricks by malware to infect system drivers. We show how Max++ examines the list of active system modules (drivers) in the system and pick up randomly the candidates for infection. In this tutorial, we will practice the use of WinDbg for examining sophisticated system data structures and learn some important system calls such zwQuerySystemInformation.

We will analyze the code from 0x3C2408  in this tutorial. This is to continue the analysis in Tutorial 16, where we showed that Max++ injects a thread into another running process and the thread will remove the binary executable Max++Loader2010.exe from the disk once the Max++ process terminates.


2. Lab Configuration
(0) Start WinXP 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). When WinDbg initiates, types "g" (go) twice to let it continue.

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

(2) 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).

(3) 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 1 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 1: code at 0x4012DC
(4) 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 2: code at 0x401407

(6) Now we will set a breakpoint at 0x3C2408  .  Goto 0x3C2408 and set a SOFTWARE BREAKPOINT. SHIFT+F9 there. Press SHIFT+F9 to run to 0x3C2408 . (You may see a warning that this is out range of the code segment, simply ignore the warning).

(Figure 3 shows the code that you should be able to see at 0x3C2408 . The first instruction should be TEST EAX, EAX.  The value of the EAX register at this moment should be 0x1, and the control flow will continue to 0x3C2410 (PUSH 10000). We will start our analysis from here.
Figure 3: Code Starting at 0x3C2408


Section 3. Check Infection Status
The first action Max++ takes is to examine if the system has already been affected. It checks the existence of a virtual volume named ""\??\C2CAD972#4079#4fd3#A68D#AD34CC121074". Notice that this is a very interesting name, if you look at the Mcirosoft UNC file name specification [1], it's actually not quite a legal name. Usually UNC names with extended length should start with "\\?\" instead of two question marks inside it [as far as I know]. If the system is never infected before, the call zwOpenFile at 0x3C242D should fail and return an error code. Only when Max++ successfully creates a virtual drive and overwrites the disk operation driver (so that the driver can handle \??\ prefix), the call will report success.

It is interesting to delve more into the call zwOpenFile and look at its parameters. In the past, we have used IMM debugger to directly watch the parameters. When the data type of a parameter is complex, it's more convenient to use WinDbg.  We will in the following show a sample use of WinDbg.

The formal declaration of zwOpenFile can be easily found out by Google. The first three parameters of zwOpenFile are interesting to us: (1) OUT PHANDLE FileHandle, (2) ACCESS_MASK DesiredAccess, and (3) POBJECT_ATTRIBUTES ObjectAttributes, according to [2].


Figure 4. Stack Contents of zwOpenFile Call

Now if we look at the contents of the stack when the call is made (as shown in Figure 4), we might notice that the value for the third parameter (ObjectAttributes) is 0x3D3150. Notice that its data type is POBJECT_ATTRIBUTES where "P" stands for the pointer. Clearly, it means that starting at 0x3D3150, there is a data structure named OBJECT_ATTRIBUTES. It is possible to get the formal declaration of OBJECT_ATTRIBUTES structure and interpret the bits and bytes by yourself. But a simpler way is to use WinDbg to help with it.

For this purpose, let's start the WinDbg in the VBox Windows image. File-> Attack Process -> Max++ (note: not the external one in the host) and then click and select to run Noninvasively. (as shown in Figure 6).

Figure 6. Run WinDebug in Noninvasive Mode
Simply type "dt  _OBJECT_ATTRIBUTES 0x3D3150 -r2" in the WinDbg command window, we have the dump of the contents as shown below. Here "-r2" means to display 2 layers of details level (recursively). Shown in Figure 7 is the dump of the OBJECT_ATTRIBUTES. You can see that the file it tries to access is "\??\C2CAD972#4079#4fd3#A68D#AD34CC121074". The call will return an error code in EAX, which drives the execution flow to 0x3C243B (see Figure 3) and then to 0x3C2461.


Section 4. Search for Modules to Infect (Function 0x3C1C2C)
We now examine the logic of Function 0x3C1C2C (as shown in Figure 7). The first part of the function is to call 0x3D0BC0. This is a function frequently called by the Max++ code. We leave the analysis details to you.


Challenge 1. Analyze the functionality of function 0x003D0BC0 (what are its input? and what are its output? Hint: it adjusts the position of ESP register according to some input and then puts the address of the next immediate instruction into EAX).


Figure 7. First part of Function 0x3C1C2C


The major bulk of the function is the a loop from 0x003C1C5B to 0x003C1D04 (shown in Figure 8). During the first iteration, the code calls zwQueryInformation to get the list of system modules (all running driver processes in the system) and find out the system module whose name is "ndis.sys". Note that NDIS stands for Network Driver Interface Specification, the ndis.sys is a driver file that controls network adapters. You can roughly infer what the loop is doing, but it's beneficial to repeat the analysis process on your own so that you can sharpen your reverse engineering skills. We now expose some of the technical details here:


(1) zwQuerySystemInformation. This is a very important system call provided by the OS. The function prototype is shown in the following, from MSDN.
NTSTATUS WINAPI ZwQuerySystemInformation(
  __in       SYSTEM_INFORMATION_CLASS SystemInformationClass,
  __inout    PVOID SystemInformation,
  __in       ULONG SystemInformationLength,
  __out_opt  PULONG ReturnLength
);
The first parameter is an integer that represents the type of information to query. There are many types available, e.g., system performance information, time of the day etc. In our case, the system information class is SystemModuleInformation, which provides the information of running system modules. Notice that the size of the SystemInformation (second parameter) can vary, you have to pass the BUFFER LENGTH of your preallocated buffer to zwQuerySystemInformation. If zwQuerySystemInformation needs more space, it will inform you that the space is not enough (using the ReturnLength, the 4th parameter). In that case, the Max++ code will go back to reallocate space.[see the Call of 0x3D0BC0 at 0x3C1C61, see Challenge 1]

Figure 8. Read System Module Information

(2) System Module Information. Let's now delve into the data returned by zwQueryInformation. Using a simple google search, we can find the definition of _SYSTEM_MODULE_INFORMATION and _SYSTEM_MODULE. As shown below [the code is from http://source.winehq.org/source/include/winternl.h] .

typedef struct _SYSTEM_MODULE_INFORMATION
{
ULONG               ModulesCount;
SYSTEM_MODULE       Modules[0]; /* FIXME: should be Modules[0] */
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;


typedef struct _SYSTEM_MODULE
{
PVOID               Reserved1;                      /* 00/00 */
PVOID               Reserved2;                      /* 04/08 */
PVOID               ImageBaseAddress;               /* 08/10 */
ULONG               ImageSize;                      /* 0c/18 */
ULONG               Flags;                          /* 10/1c */
WORD                Id;                             /* 14/20 */
WORD                Rank;                           /* 16/22 */
WORD                Unknown;                        /* 18/24 */
WORD                NameOffset;                     /* 1a/26 */
BYTE                Name[MAXIMUM_FILENAME_LENGTH];  /* 1c/28 */
} SYSTEM_MODULE, *PSYSTEM_MODULE;




Note that the second parameter  Modules[0] of _SYSTEM_MODULE_INFORMATION is a real array (i.e., not a simple pointer to the entry address of the array). In another word, the size of _SYSTEM_MODULE_INFORMATION can vary, depending on the value of ModulesCount. Similarly, the last attribute NAME[MAXIMUM-FILENAME_LENGTH] of _SYSTEM_MODULE is a real character array (not a pointer)


Figure 9 shows the first 0x90 bytes of the _SYSTEM_MODULE_INFORMATION structure.

Challenge 2. Given Figure 9, can you infer how many modules are located in the _SYSTEM_MODULE_INFORMATION? (i.e., how many modules are loaded in the system right now?)

Challenge 3. Given Figure 9, can you infer the name of the first module? What is the value of NameOffset in the first _SYSTEM_MODULE?

Figure 9. System Module Information

To help you further understand the logic in Figure 8, we list more challenges below:
Challenge 4. Given Figure 8, observe the instructions from 0x3C1C73 and 0x3C1C78, they push the parameters for zwQueryInformation.  which of the registers (EAX, ESI etc.) contains the buffer to hold system information?

Challenge 5. Given Figure 8, observe the instructions at 0x3C1CDF. The instruction is LEA ESI, DS:[EAX+EDI+1C]. Explain the meaning and purpose of this instruction? Specifically, explain what is contained in EAX, EDI and what's the meaning of offset 0x1c?



Challenge 7. Find a way to get out of the loop and what is the ImageBaseAddress of "ndis.nys"? (you have to first properly set up a conditional breakpoint).

Challenge 8. Explain the  logic between 0x3C1CF3 (NEG EAX) to 0x3C1CFE (JE 0x3C1E1E), and explain why would the JE instruction not jump when the module name is "NDIS.sys".

Figure 10. After NDIS.sys is found

Section 5. Test Registry (Function 0x003C18D4)
When the module of NDIS is found, the control flow continues to 0x003C1D04 (shown in Figure 10). The code first did a name comparison of "ndis.sys" and "win32k.sys", and if not equal, it calls function 0x003C18D4 at 0x003C1D27. We'll provide the analysis of function 0x003C18D4 below.


Fogure 11. First Part of Function 0x3C18D4
Figure 11 shows the first part of Function 0x3C18D4. Most of the functions are very easy to analyze. It first calculates the string length of the ndis.sys file and checks if the file name is not too long. Then it checks if the first letter of the file name is ascii code 2E "." (if not, it continues). Then it searches for the file suffix and verifies if it ends with ".sys". These are fairly routine checks to make sure that the file (ndis.sys) is a normal system driver file. Lastly, it calls lz32.003C250C at 0x003c193e. We now try to analyze function 0x003c250c.

5.1 Function 0x003C250C (Building Object_Attributes)

In general, to analyze a function, we need to figure out three things: (1) what are the input parameters? (2) what are the output parameters? (3) what does the function do?

Analysis of malware functions can be a challenging job, as malware authors will not necessarily have to follow the typical C language calling conventions (i.e., pushing parameters to stack and use standard registers for return).It creates trouble for us to understand the functionality of a function.

Figure 12 shows the body of function 0x003C250C.By studying the instructions we know that the input parameter of this function is the ESI register. The function body calls RtlInitUnicodeString and then is saving some values to the RAM (at ESI+4, ESI+8 etc.). It seems that the function is building some data structure however we have no way to know what exactly the type is. In this case, we have to trace how the return data is used (note that the return of this function is saved in EAX, look at the instruction at 0x3C2535).


Figure 12. Function Body 0x3C250C

Figure 13 shows the caller of 0x3C250C. Notice that at 0x003C1943 (right after the call of function 0x3C250C), the code pushes EAX (the return of function 0x3C250C) into stack, and then it pushed another two words into stack and calls zwOpenKey. Clearly, the return value of 0x003C250C is used as the 3rd parameter of ZwOpenKey, which immediately leads us to the conclusion: function 0x3C250C is building an object of _OBJECT_ATTRIBUTES. Figure 14 shows the details of the object constructed. You can see that the main component is the ObjectName, which contains value "\registry\...\NDIS".

Figure 13. How the return data of Function  0x3C250C is Used

Figure 14. Memory Contents of Object Attributes

5.2 Rest of Function 0x003C18D4 (check existence of registry file)

We now continue to analyze the rest of Function 0x003C18D4 (shown in Figure 15). Interestingly, after calling the ZwOpenKey, the code immediately call ZwClose to close the registry handle immediately. What's its purpose then? If you check the control flow, you would notice that depending on the return value of ZwOpenKey, the function returns 1 when the registry key is successfully opened, and returns 0 when the ZwOpenKey fails.
Figure 15. Rest of Function 0x3C18D4



Figure 16. Reads NDIS.sys

Section 6. Scanning Proper Driver Files (Starting from 0x3C12DC).
As shown in Figure 16, at 0x3C1D27 the code returns from 0x3C18D4 (check the existence of registry for NDIS). Then it performs zwReadFile twice, first to read 0x40 bytes and then to read 0xF8 bytes. The second read loads the entire PE header (0xF8 bytes) into 0x12D388.

Figure 17. Check Proper Size


Now comes the interesting part, see Figure 17. At 0x3C1DDF, the code checks the EXPORT TABLE SIZE of the PE header and see if it is NOT zero. If it's zero, it will continue the search and check another system module. NDIS.sys fails the check (its export table size is 0).

Then at 0x003C1DF3, it calls zwQueryInformationFile and reads the FILE_STANDARD_INFORMATION of the module (this time, the mup.sys). It then reads the file size of the module and compare it with value 0x4C10. If the file size is below 0x4C10, it continues the search.

If everything is fine so far, at 0x003C1E0E, it resets the ID of the _SYSTEM_MODULE to 1 and it writes the actual file size of the system module file back into the _SYSTEM_MODULE (i.e., reset the ImageSize attribute).

For each system module found (with EXPORT TABLE SIZE>0 and file size>0x4c10), the code increments counter at 0x12D5D4 by 1.


Challenge 9. Explain the logic of the code of Figure 17 in details. For example, for instruction at 0x3C1DDF, how would you know that it is checking EXPORT TABLE SIZE?

Section 7.  Random Pick of Drivers(Starting from 0x3C1E30).
We now examine the next section of the malicious logic (shown in Figure 18). When the code executes to 0x3C1E30 (out of the big loop), at EBP-30 (0x12D5D4), it stores the number of system modules that satisfy the criteria: (1) size greater than 0x4C10; and (2) EXPORT TABLE SIZE > 0. On our system, there are 0x19 modules that satisfy the condition.

The next section (from 0x003C1E30 to 0x3C1E6D) randomly picks up a module to infect. First, it calls GetTickCount to get the current time, and then it calls RtlRandom using the current time as seed. Then it does a DIV operation (at 0x3C1E51) on 0x19 (the number of modules), after which, the remainder is stored in EDX.


Figure 18. Randomly Pick a Driver File

The loop from 0x3C1E59 to 0x3C1E6D is used to get the _SYSTEM_MODULE. Given that the random ID stored in EDX, without the loss of generality, let's assume that it's 5. It tries to get the 5th satisfactory module by visiting every module in the list of _SYSTEM_MODULES retrieved by earlier call of zwQuerySystemInformation. Notice that at 0x003C1E59, it compares [ESI+14] with BX (value 1). Here ESI points to the _SYSTEM_MODULE, and the offset 0x14 stores ID. Recall that earlier the code marks each module satisfying the criteria with ID 1, this immediately explains the purpose of the comparison code. After the code jumps out of the loop, ESI points to the selected _SYSTEM_MODULE. Up to now, the random selection of a system module is completed. We will explain the infection process in the next tutorial.

Friday, February 10, 2012

Malware Analysis Tutorial 16: Return Oriented Programming (Return to LIBC) Attack

Learning Goals:
  1. Understand Return to LibC attacks
  2. Analyze Return to LibC data payloads
  3. Practice analyzing function call parameters and stack
Applicable to:
  1. Operating Systems
  2. Assembly Language
  3. Operating System Security
1. Introduction
This tutorial shows a very interesting attack called Return to LibC. The general idea is to form a chain of system calls by setting up stack contents properly so that the target process, when returning from one system call, will continue onto the next system call set up by the attacker. A more general form of the Return to LibC attack is the recently very active Return Oriented Programming [1], where any arbitrary program can be implemented by setting up the stack contents.

Usually, Return to LibC attack is applied to buffer overflow attacks so that it can overcome the protections taken by OS to set stack as non-executable (recall that it does not inject a shell code, but a bunch of function parameters and return addresses).

In this tutorial, we show that the Max++ author takes a very clever application of the Return to LibC attack. Instead of setting up the code section of a thread, the author directly implements the desired program logic as the stack contents, which saves a lot of trouble in injecting a new thread into a running process [see background information in Tutorial 14 and 15].

We will analyze the code from 0x3C1390  in this tutorial.


2. Lab Configuration
(0) Start WinXP 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). When WinDbg initiates, types "g" (go) twice to let it continue.

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

(2) 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).

(3) 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 1 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). At this point, the code at 0x3C24FB has not been extracted. If you go to 0x3C24FB at this moment, IMM will complain that this address is not accessible.
Figure 1: code at 0x4012DC
(4) 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 2: code at 0x401407

(6) Now we will set a breakpoint at 0x3C1390.  Goto 0x3C1390set a SOFTWARE BREAKPOINT. SHIFT+F9 there. Press to run to 0x3C1390. (You may see a warning that this is out range of the code segment, simply ignore the warning).

(Figure 3 shows the code that you should be able to see at 0x3C1390. The first instruction should be PUSH SS:[EBP-8], and he next is a function call: CALL DWORD PTR DS: [3D1128] (see Figure 3). This is a function call to RtlCreateUserThread. We will start our analysis here!
Figure 3. Code Starting from 0x003C1390

Section 3. Creating Target Thread
The first function we are analyzing is located at 0x003C1393 (see Figure 3). RtlCreateUserThread is an undocumented function by MS Windows. But a simple google search can turn out its function declaration as shown following (taken from [2]):

RtlCreateUserThread(
     IN HANDLE ProcessHandle,
     IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL,
     IN BOOLEAN CreateSuspended,
     IN ULONG StackZeroBits,
     IN OUT PULONG StackReserved,
     IN OUT PULONG StackCommit,
     IN PVOID StartAddress,
     IN PVOID StartParameter OPTIONAL,
     OUT PHANDLE ThreadHandle,
     OUT PCLIENT_ID ClientID
); 

There are many parameters, and the following are interesting to us:
(1) ProcessHandle - the handle of the process to create a user thread in.
(2) StartAddress - when the thread is created, where to begin its execution
Figure 4 shows the stack contents when we get to the CALL instruction at 0x3C1390.
Figure 4: Stack Contents for Call RtlCreateUserThread


You can immediately infer that the ProcessHandle is 0x44. If you follow Tutorial 15, you will notice that it's the handle of the target  process (e.g., smss.exe in our VM instance). The startAddress is 0x7C96149B (note! IMM has already found it for you! It's the entry address of RtlExitUserThread!!!) The situation has got a little bit funny here because the author tries to inject a thread which terminates itself immediately! But he/she has additional tricks. Now let's proceed to the next function call.

Section 4. Get Context Thread
 The next system call we are analyzing is located at 0x3C13B5.It's a call to zwGetContext (as shown in Figure 5). A simple search of zwGetContext or ntGetContext yields the function prototype of zwGetContext. It takes two parameters (1) HANDLE threadHandle, and (2) PCONTEXT pContext. Clearly, the first is the handle of the thread to be examined, and the second (i.e., pContext) is a pointer which holds the entry address of the CONTEXT structure. If you look at Figure 5, you might notice that the pContext value is 0x0012D288.

Figure 5. call of zwGetContext
Now we are interested in looking at the contents of the CONTEXT structure at 0x0012D288. Of course, you can use IMM to examine it in the memory dump directly. There is a better way. Using WinDbg can show kernel data structures nicely. For this purpose, let's start the WinDbg in the VBox Windows imageFile-> Attack Process -> Max++ (note: not the external one in the host) and then click and select to run Noninvasively. (as shown in Figure 6).

Figure 6. Run WinDebug in Noninvasive Mode


Now type "dt _CONTEXT -r2 0x0012D288" we have the complete dump of the _CONTEXT structure. Note that the _CONTEXT is used by OS to keep track of the running context (mainly the value of the registers) of a thread when doing context switch. As shown in Figure 7, the initial context of the new thread in smss.exe has several interesting registers:
(1) First, we could see all hardware BPs are cleared.
(2) All segment registers GS/FS/ES/DS are not set yet.
(3) EIP is set to 0x7c96149b. (If you find it out in IMM->View->Modules->ntdll->Names. You will find that this is the entry address of RtlExitUserThread, matching the description before).
(4) The ESP is set to 0x3fff8 (note: this is within the address space of smss.exe, not Max++.exe).

Figure 7. Initial Dump of the _CONTEXT at 0x0012D288


Section 5. Set up smssexe Stack
Next, Max++ will set up the stack for the new thread properly. Let's look at Figure 8. From 0x003C13C3 to 0x003C1455, Max++ prepares the contents first in its own stack (you could see here there are lots of operations to the EBP based addresses). Then at 0x3C145F, it calls the most important function zwWriteVirtualMemory!

Figure 8. Set up Target Stack of smss.exe's new Thread

Google the documentation of zwWriteVirtualMemory or ntWriteVirtualMemory, we could find that it takes four parameters: (1) target process handle [we could verify that this is the handle of the smss.exe], (2) target address [in our case, it's 0x3FF000!], (3) source address (in Max++ addr space): 0x0012D580, (4) number of bytes to write  0x4c!

Figure 9. Contents to Inject
Now let's take a look at the 0x4c bytes to write into the target process smss.exe. It's listed in Figure 9. Notice that the data is starting from 0x0012D580 and ends at 0x0012D5CC i.e., the first word is the 0x7C90CFD0 (i.e., the first word in the first row of Hex Dump  in figure 9, note the byte sequence) in the first row and then the last word is the 0xFFFFFFFF (the first word in the last row). Notice that when copied to the target address space of smss.exe, starting from 0x3FF000 we should have 0x7C90CFD0 and at 0x3FF048 we should have FFFFFFFF.

Section 6. Set Context of smss.exe thread
As shown in Figure 8, the last trick to play is to call the zwSetContextThread call. Using the same trick we used in section 4, we can easily infer the meaning of all parameters of zwSetContextThread. It changes the value of the ESP register to 0x3FF000, EBP is set to 0x0, EIP register is reset to 0x7C90DF30 (using IMM->View->Modules->Names) we can find that it's zwWaitForSingleObject!

Section 7. Analysis of Return To LibC Attack
Now we are getting to the interesting point. The first instruction to be executed by the new thread is the entry of zwWaitForSingleObject(handle, alertable, timeout) . But were are the parameters? If you read about the calling conventions of these ntdll functions you will find that when executing to the first (entry) instruction of a NTDLL function, we have
   ESP --> the return address
   ESP+4:  1st parameter
   ESP+8:  2nd parameter
   ESP+c:  3rd paramter

Thus we immediately get: the return address is (see Figure 9) 0x7C90CFDO (zwClose). The parameters for zwWaitForSingleObject is:
  handle: 0x54 (what's the handle for? It's a challenge for you)
  alertable: 0
   timeOut: 0.

 When zwWaitForSingleObject finishes, it will jump to zwClose. The parameter for zwClose is 0x54 (still the same handle). When zwClose returns, the next function entry is 0x0x7C90D1F0 (zwDelayExecution)!

Challenge of the Day: Finish the above analysis of ReturnToLibC attack employed by Max++ and analyze what it is trying to do? HINT: again if you could find documentation for zwXYZ(), you can try searching ntXYZ().



References
1. E. Buchanan, R. Roemer,  and S. Savage, "Return-Oriented Programming: Exploits Without Code Injection", Blackhat USA 2008.
2.  "Undocumented Functions of NTDLL", Available at http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Executable%20Images/RtlCreateUserThread.html.


Friday, February 3, 2012

Malware Analysis Tutorial 15: Injecting Thread into a Running Process

Learning Goals:
  1. Understand process/thread privileges
  2. Understand intercommunication and object duplication among processes
  3. Practice analyzing function call parameters and stack
Applicable to:
  1. Operating Systems
  2. Assembly Language
  3. Operating System Security
1. Introduction
In this tutorial, we show you another intriguing behavior of Max++: it removes the Max++Loader.exe from disk once it finishes its infection operations, such as modification of registry and infection of driver DLL files. The removal operation is accomplished using a series of interesting techniques: changing of process privileges, adding threads to another running process, hooking up timer events, and return oriented programming (more exactly return to libc attacks).

We will analyze the code from 0x3C1193  in this tutorial.

The key to analyzing such malicious activities is to examine the system calls placed by Max++. We have introduced the techniques and skills you need for such analysis in Tutorial 14.

2. Lab Configuration
(0) Start WinXP 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). When WinDbg initiates, types "g" (go) twice to let it continue.

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

(2) 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).

(3) 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 1 shows the code that 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). At this point, the code at 0x3C24FB has not been extracted. If you go to 0x3C24FB at this moment, IMM will complain that this address is not accessible.
Figure 1: code at 0x4012DC
(4) 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 2: code at 0x401407

(6) Now we will set a breakpoint at 0x3C1193.  Goto 0x3C1193and set a SOFTWARE BREAKPOINT there. Press SHIFT+F9 to run to 0x3C1193. (You may see a warning that this is out range of the code segment, simply ignore the warning).

(Figure 3 shows the code that you should be able to see at 0x3C1193. The first instruction should be PUSH EBP, and then MOV EBP, ESP, and then SUB EBP, 384.).



Figure 3: Function 0x3C1193
3. Brief Analysis of Function 0x3C1193
We now briefly describe the functionality of 0x3C1193 and we leave most analysis details to you in Section 4 (challenges).

The first part of the code is shown in Figure 3. Max++ first calls a function getModuleHandleW of itself, and then it calls LdrFindEntryForAddress (its own module). Then there is a big loop from 0x3C11F1 to 0x3c1224, which follows the module list and examine the information of processes. This is done using zwQueryProcessInformation (there is an internal loop which adjusts the buffer size if the size is not enough). After several iterations, Max++ identifies a process which satisfies some criteria on parent process and children threads. In our case, the process identified is smss.exe. Then it opens a thread (think about which thread it is), and assign a bunch of privileges to the thread (See Figure 4).


Figure 4. Adding Privileges to Thread
Figure 5 shows the next section of the code. This part is quite interesting. Now look at the function call at 0x003C12DA. The DosPathNameToNtPathName call converts string "c:\.. ..\Max++downloader.exe" to the format of NtPath (look at the stack top for the string). Then it opens the file at 0x0003C1331, and then duplicates the file object at 0x003C1355. Notice where is the duplicate object to be created! It's to be placed in the "smss.exe" process (which is not the Max++ process)!

Figure 5. Duplicate File Object
The last part of the code is very tricky, and we'll leave the discussion of the return oriented technique to the next tutorial. Here, we'll only give some general idea. Figure 6 shows the last part of the code. At 0x003C1393, Max++ calls RtlCreateUserThread to create a new thread (question: who's the owner? Max++ or smss.exe?). Then it sets up the context of the newly created thread. From 0x003C13CF to 0x3C14FF, Max++ spent quite some efforts in setting up the contents of a piece of memory and then it copies this piece to a special region in the target process (smss.exe). Then it calls zwSetContextThread (at 0x003C1481) to set up the EIP register of the new thread. NOTE that here we have not specified the function body for the new thread! How does the new thread accomplish the job? We'll elaborate a very interesting technique called return oriented programming in the next tutorial.
Figure 6. Create a New Thread in smss.exe Process



4. Challenges
[Hint: for system functions started with "zw", the "nt" series documentation can be often found. For example, "zwDuplicateObject" has a corresponding "ntDuplicateObject"]

Challenge 1. Analyze the semantics of all function parameters and the return of getModuleHandleW call at 0x3C11A6 (see Figure 3)

Challenge 2. Analyze the semantics of all function parameters and the return of LdrFindEntryForAddress call at 0x3C11AD (see Figure 3)

Challenge 3. Analyze the semantics of all function parameters and the return of zwQuerySystemInformation call at 0x3C11F3 (see Figure 3)

Challenge 4 Summarize the function of the loop starting from 0x3C1209 to 0x3C123F which searches for a a process to inject a thread. There are some properties such a host process must satisfy. Read the code and describe these desired properties (on parent process and children threads).

Challenge 5 In Figure 4, Max++ is adding a collection of privileges to a thread? Which thread it is (which process does this thread belong to)? Try to figure it out and prove that your argument is correct.

Challenge 6 In Figure 4, Max++ is adding a collection of privileges to a thread? Analyze RtlAdjustPrivilege calls at 0x003C1297, 0x003C12A3, 0x003C12AF, 0x003C12BB, etc. List the privileges added.

Challenge 7  Analyze the function call DosPathNameToNtPathName at 0x003C12DA (in Figure 5).

Challenge 8  Analyze the function call zwOpenFile at 0x003C1331 (in Figure 5).

Challenge 9  Analyze the function call zwDuplicateObject at 0x003C1355 (in Figure 5). Where is the new object (duplicated object) stored?

Challenge 10  Analyze the function call zwDuplicateObject at 0x003C136e (in Figure 5). Which object is duplicated?

Challenge 11  Analyze the function call RtlCreateUserThread at 0x003C1390 (in Figure 6).

Challenge 12  Analyze the function call zwContextThread at 0x003C13B5 (in Figure 6). 

Challenge 13  Analyze the function call zwWriteVirtualMemory at 0x003C145F (in Figure 6).