- Understand process/thread privileges
- Understand intercommunication and object duplication among processes
- Practice analyzing function call parameters and stack
- Operating Systems
- Assembly Language
- Operating System Security
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 FilesDebugging Tools for Windows (x86)” (where WinDBG is installed). Type “windbg -b -k com:pipe,port=\.pipecom_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|
[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).