Virus Share: Random Sample #1 - Part Three: Injection


Continuation of our analysis of Virus Share: Random Sample #1 - Part Two. We start with inspecting the function placed at the start of ZwWriteVirtualMemory to learn it's objective.

Follow along note: We'll be using the sample dumped after step 8 (part one) for IDA. When running the sample in x64dbg, the dumped sample is taken just prior to step 8.

Exercise caution (i.e. run in an isolated VM) as this is a live malware sample.
I'm not responsible for any damage.

### Recap Here is what we have covered so far:

Part One

  • Extracted executable from web page
  • Using the upx executable, unpacked the first packer
  • Using a debugger, unpacked the sample a second time
  • The final running instance of the dumped executable looked to have been wrapped with another packer that, when removed, caused the unpacked executable to throw an error during execution.
    • Had to leave the final packer in place for the debugger but removed for IDA.

Part Two

  • Inspected API hooking of ZwWriteVirtualMemory
  • Call to CreateProcessA to trigger it
  • Removal of the API hook

## Analysis

The function (below) gets called in place of ZwWriteVirualMemory. Let's walk through each box and determine what the overall goal is.

Screenshot 1 - ZwWriteVirtualMemory replacement function

### Trampoline `402A59` - `402A79`

The observant reader probably noticed that ds:dword_40DFB7 @ 402A6E was already clarified in the prior article in this series. This calls the trampoline code constructed earlier before pushing all the registers onto the stack and checking to see which path to take. The condition check ensures that [ebp+hProcess] is a valid value before moving onto the next session.

### Run Once Check `402A7F` - `402A86`

ds:dword_40DFA3 looks to be a condition flag used to ensure the code between 402AA5 and 402B14 is only executed once.

### Is Null Check `402A8C` - `402A93`

This block checks to see if ds:lpBaseAddress contains a value (aka. not null). The value is set two boxes down, but the value comes from the function call in the next box.

### Get Base Address `402A99` - `402AA3`

The main piece in this box is the function call to sub_402270 @ 402A9C. Opening this function up we see a few PE file related constants.

Screenshot 2 - sub_402270

It appears to be looking for the MZ tag and the PE signature but what base address is it looking at? After looking at it more closely, we discover a neat trick.

Whose PEB Am I?

The magic lies in the function sub_40220E; however, we need to know two more things before starting the analysis.

Screenshot 3 - sub_40220E

First, the highlighted function is simply a wrapper function for calling ZwQueryInformationProcess.

Screenshot 4 - ZwQueryInformationProcess wrapper function

Secondly, we need to know the parameters for ZwQueryInformationProcess.

NTSTATUS WINAPI ZwQueryInformationProcess(
  _In_      HANDLE           ProcessHandle,
  _In_      PROCESSINFOCLASS ProcessInformationClass,
  _Out_     PVOID            ProcessInformation,
  _In_      ULONG            ProcessInformationLength,
  _Out_opt_ PULONG           ReturnLength

Code Snippet 1 - ZwQueryInformationProcess function parameters defined on MSDN[^n]

With that we are now ready to perform some analysis. First, push 0 @ 40222A is really the constant for ProcessBasicInformation. According to MSDN, when ProcessInformationClass is zero ZwQueryInformationProcess "retrieves a pointer to a PEB structure that can be used to determine whether the specified process is being debugged, and a unique value used by the system to identify the specified process."[^n]

The next parameter of interest is the out parameter that is really just a pointer to the buffer which the data will be written to. The parameter for ProcessInformation is [ebp+var_240]. The effective address of the variable is obtained and pushed onto the stack. When the actual call to ds:dword_40DA5F aka. ZwQueryInformationProcess (defined @ 4019C1 for those interested) is made, the memory location starting at the address of [ebp+var_240] is written to.

...Stick with me now...

The next thing we need to understand is that function variables are stored contiguously on the stack when using stdcall functions on the x86 architecture. So if the data being written is too big for the space allocated; then, the data spills over into adjacent memory space (gotta love lack of bounds checking in C). Looking closely, we notice that the difference between -0x240 and -0x23C is 0x4

40220E var_240= byte ptr -240h
40220E lpBaseAddress= dword pt -23Ch

Code Snippet 2- Variable Positioning

Now look at the struct returned by ZwQueryInformationProcess when ProcessInformationClass is zero.

    PVOID Reserved1;
    PPEB PebBaseAddress;
    PVOID Reserved2[2];
    ULONG_PTR UniqueProcessId;
    PVOID Reserved3;

Code Snippet 3 - PROCESS_BASIC_INFORMATION struct defined on MSDN[^n]

Way larger than 4 bytes! The first 4 bytes (-0x240--0x23D) hold the PVOID Reserved1 bytes. The next four bytes (-0x23C - -0x239) end up holding the PebBaseAddress. This corresponds to lpBaseAddress variable due to its proximity to var_240.

Screenshot 5 - Variable Overflow

Now lpBaseAddress holds the address to the PEB! The address value is dereferenced when pushed onto the stack as a parameter to ReadProcessMemory causing it to read memory from that address and writing it to the location of Buffer. Here we experience the same local variable trick again!

40220E Buffer= byte ptr -254h
40220E var_24C= dword ptr -24Ch

Code Snippet 4 - Variable Positioning

The difference between Buffer and var_24C is 8. Starting at index 8 (zero based index), the dword value of ImageBaseAddress is stored into var_24C (offset 0x8 from the start of the PEB contains the ImageBaseAddress). Thus the base address of a running process is acquired.

Here is the magic!

Due to ZwWriteVirtualMemory being hooked and the call to CreateProcess, the current process that hProcess points to is actually the process created by CreateProcess, not the process which called CreateProcess. We can confirm this by looking at the handle parameter in x64Dbg @ 402A6B when the EIP is @ 402A6E and comparing that to the handles show in Process Hacker for svchost.exe.

Notice the handle value is 0x34 in x64Dbg.

Screenshot 6 - hProcess in x64Dbg

And handle 0x34 corresponds with iexplorer.exe in Process Hacker even though the process being looked at is the malicious svchost.exe.

Screenshot 7 - hProcess in Process Hacker

So the purpose of this box is to retrieved the ImageBaseAddress of iexplorer.exe.

Actual Return Value

Ok, now that we know which PE file is being inspected we can figure out the return value. First, it reads the first 0x40 bytes starting at the ImageBaseAddress. Then, after confirming that the memory segment starts with ZM, the malware reads the next 0xF8 bytes from the start of the PE header. Notice the malware is doing the same trick again to get the pointer to the PE header (0x640x28=0x3C). After confirming it is reading the PE header the malware sets EAX to the value of the AddressOfEntryPoint (0x15C - 0x134 = 0x28) and that's the value returned!

### Writing MZ File `402AA5` - `402AD1` In this box a few things are done, but we are not going to go into fine detail on everything the function call @ `402ABF` does. First, we start with some variables being set.
  • The control flag ds:dword_40DFA3 is set to 1. This will keep execution from going down this path again when ZwWriteVirtualMemory is called.
  • iexplorer.exe entry point value is stored in ds:lpBaseAddress

After that, the call to sub_402002 is made. The parameter byte_404031 appears to be pointing to a MZ file when looking at it in IDA's hex viewer.

Screenshot 8 - MZ File

This MZ file gets written into memory inside the virtual address space for iexplorer.exe. Looking closely, in addition to this MZ file, some other functions from the malicious svchost.exe are written into iexplorer.exe's address space. Let's focus on what these two lines store:

402AC4 mov     ds:dword_40DFA8, eax
402AC9 mov     ds:dword_40DFAD, edx

Code Snippet 5 - Variable Assignment

In the function sub_402002 we see that the values of EAX and EDX are the return values from VirtualAlloc/Ex functions. EAX's RVA content contains the function sub_401F5D. EDX's RVA points to an address space whose contents are constructed dynamically from a list of resolved Windows API functions.

### Hook Entry Point `402AD3` - `402B14`

The interesting part in the next section is the call to WriteProcess.

402AE9 lea     eax, [ebp+NumberOfBytesWritten]
402AEC push    eax             ; lpNumberOfBytesWritten
402AED push    0Ch             ; nSize
402AEF push    offset byte_40DFA7 ; lpBuffer
402AF4 push    ds:lpBaseAddress ; lpBaseAddress
402AFA push    [ebp+hProcess]  ; hProcess
402AFD call    WriteProcess

Code Snippet 6 - Writing iexplorer.exe Hook

Notice the value at byte_40DFA7 is being written to ds:lpBaseAddress (the entry point to iexplorer.exe). Looking at this in IDA's hex editor we see:

Screenshot 9 - Hook Code Prep

The values being written start where the BF is located and end at FF D7 (12 bytes total. nSize = 0xC). Hmm, these are probably assembly instructions. Looking them up we see that BF corresponds to a MOV instruction and 68 is a push instruction.[^n] What about FF D7? If we open a random exe in x64Dbg and allocate some memory and load that memory segment in both the disassembler and dump windows and make the first two bytes FF D7, we see that it's the assembly instructions for call edi.

Screenshot 10 - FF D7

Remember this from the prior box?

402AC4 mov     ds:dword_40DFA8, eax
402AC9 mov     ds:dword_40DFAD, edx

Code Snippet 7 - Variable Assignment

Both of these dwords land inside the address space being copied into the entry point of iexplorer.exe

So code written should look something like this:

mov edi, sub_401F5D
push resolved_addresses
call edi

Code Snippet 7 - Hook Psuedocode

Essentially this hooks the entry point of iexplorer.exe forcing its custom code to be executed first.

### Finishing Up `402B19` - `402B1B` Nothing really spectacular occurs here. This simply returns all variables pushed onto the stack via the `pusha` at the beginning and exits the function.
## Conclusion Here we see another layer of obfuscation as a PE file is written into the address space of `iexplorer.exe` and forcing it to be executed while masking as a legitimate process.

In part four of this series we will cover extrapolating the injected binary from memory for further analysis.

## References
comments powered by Disqus