Practical Malware Analysis - Lab 12 write-up
Covert malware launching is the subject of Chapter 12, and discusses some of the many techniques that malware authors have developed to blend their malware into the normal Windows landscape. From this chapter I learned:
- That malware will load into all threads only if it’s a keylogger or the equivalent (when the goal is message interception)
- That loading into all threads can degrade the running system and may trigger an IPS, therefore, if the goal is to load a DLL in a remote process, only a single thread will be injected in order to remain stealthy
- How process injection, process replacement, hook injection, and APC injection works
- What the detours library is and how malware authors use the
.detour
section to add new DLLs to existing binaries on disk - What DLL injection looks like in x86 assembly
Lab 12-1
Analyze the malware found in the file Lab12-01.exe and Lab12-01.dll. Make sure that these files are in the same directory when performing the analysis.
1. What happens when you run the malware executable?
Answer: A messagebox pops up every minute with the text “Press OK to reboot”. The number in the title of the messagebox increases with every new pop-up displayed.
If you press “OK” the system does not actually reboot. It just displays the message again after a minute.
2. What process is being injected?
Answer: explorer.exe
seems to be the target process that is being injected.
3. How can you make the malware stop the pop-ups?
Answer: The malware doesn’t seem to be persistent, so a reboot of the machine will stop the pop-ups.
4. How does this malware operate?
Answer: The malware executable first enumerates all processes using psapi.dll
. It then gets the current directory (directory that the malware executable is launched from) and concatenates this directory with Lab12-01.dll
and then starts enumerating processes again. Later it gets a handle to explorer.exe
process, which is used in a call to VirtualAllocEx
:
It then performs a call to WriteProcessMemory
, with the following parameters:
- lpNumberOfBytesWritten set to NULL, meaning the parameter will be ignored
- nSize of 0x104 (same size that was just allocated in the call to
VirtualAllocEx
. These are the number of bytes to be written to the specified process. - lpBuffer that points to the malicious
Lab12-01.dll
file, this is the data that will be written in the address space of the specified process. - lpBaseAddress that points to the base address in the specified process to which data is written.
- hProcess which is a handle to the
explorer.exe
process to be mofidied.
The malicious Lab12-01.dll
has now been injected into explorer.exe:
Lab12-01.dll
is responsible for the messagebox that pops up every minute.
Comparing my answers to the Lab 12-1 solutions
Conclusions after comparison:
- Instead of rebooting the entire machine, we could’ve just restarted the
explorer.exe
process to stop the pop-ups.
Lab 12-2
Analyze the malware found in the file Lab12-02.exe.
1. What is the purpose of this program?
Answer: The program launches a process named svchost.exe
that, after checking the strings in memory, appears to be a keylogger:
From the directory in which we executed the program we can confirm our suspicions when we open the newly created practicalmalwareanalysis.log
file:
2. How does the launcher program hide execution?
Answer: The launcher program seems to perform process replacement on C:\WINDOWS\system32\svchost.exe
. This is the return value from the sub_40149D function, that concatenates the system directory with \svchost.exe
.
In the next function call, the program uses the resource section of Lab12-02.exe
, which appears to contain encoded data:
In this function, the program calls VirtualAlloc
with a size of 6000 after loading the resource section. Since this is the same size as the resource section (see bottom left corner of the previous screenshot), my guess is that it is allocating space to store the decoded data.
If a function call to VirtualAlloc
succeeds, the return value is the base address of the allocated region of pages (see: VirtualAlloc function (memoryapi.h)). In this case, the base address will be 00350000:
At this point, the address does not contain any data. But since my guess is that the encoded data of the resource section will be placed here, it is a good idea to keep an eye on this address :)
I renamed the “var_8” variable that this address gets placed in to “interestingMemSection” in IDA:
Next, the length of the resource section gets pushed onto the stack:
And the address to the resource section:
And the address that was the return value of the call to VirtualAlloc
:
These are used as parameters in a call to the memcpy
function, which IDA has nicely labeled for us:
Aaaaand, lo and behold, after the call to memcpy
our “memory address of interest” now contains the encoded data:
Then, the program checks for the MZ magic value by first comparing 0C (stored in EDX) to 4D and then comparing 1B (stored in ECX) to 5A.
Since it is looking for the MZ magic value at the beginning of the encoded data, we know that this section actually contains an executable. After this check, the program pushes the following values onto the stack before calling the sub_401000
function:
- 0x41 (A)
- Not sure what this parameter means, yet…
- 6000
- Length of the encoded data in the resource section
- 00350000
- The return value from the call to
VirtualAlloc
earlier, where the encoded data is now also stored. AKA “our memory address of interest” :)
- The return value from the call to
It looks like this is the decoding function! We see some values being copied, a XOR instruction, and a loop:
Lets break down what exactly happens at the first run in this function:
- EXC (00350000) is pushed onto the stack
- var_4 is set to 0
- var_4 (0) is copied into ECX
- arg_4 (6000) gets compared to ECX (0)
- If the result of this comparison is not below 6000, the jump is taken and the function returns
- If the result of this comparison is below 6000, the jump is not taken and we drop to the next instruction at 0040101E (which is obviously the case)
- arg_0 (00350000) is copied into EDX
- var_4 (0) is added to EDX (00350000)
- So EDX remains 00350000 in this run
- [EDX] (first byte at memory address 00350000, aka 0C) gets copied into AL
- AL (0C) is then XORed with arg_8 (0x41 aka ‘A’)
- By the way, I’m stepping through the code in x32dbg to easily get these values, as you can see in the screenshot below:
- By the way, I’m stepping through the code in x32dbg to easily get these values, as you can see in the screenshot below:
- Now we know why 0x41 (A) was pushed onto the stack before calling this function. The encoded data is being XORed with this parameter!
- arg_0 (00350000) is then copied into ECX
- ECX is then increased by var_4 (0)
- AL (‘M’) is then copied into [ECX] (first byte at 00350000)
- Note that AL contains ‘M’ after the XOR instruction in this first run through the loop. If the encoded data is being decoded in this loop, we can already guess that the next value will be ‘Z’
- We then take the jump to 0040100D
- var_4 (0) gets copied into EAX
- EAX gets increased by 1
- You can probably already tell that we’re in the second run of this loop now… Let’s see if the next value after the XOR instruction is indeed ‘Z’…
- Here we can see that the next byte at 00350000 (1B) is XORed with 0x41 (A)
- And there it is, we can see that ‘Z’ is being copied into 00350001:
So, we found the decoding function. I renamed this function todecodeResource
in IDA.
- Here we can see that the next byte at 00350000 (1B) is XORed with 0x41 (A)
We’re kinda dwelling off here, oops! The evidence of process replacement can be found at the call to CreateProcessA
, which is using the CREATE_SUSPENDED
flag for the svchost.exe
instance:
The book describes that creating a process in a suspended state is key to process replacement. This means that the process will be loaded into memory, but the primary thread of the process is suspended. The program will not do anything until an external program resumes the primary thread. Once the process is created in a suspended state, the next step is to replace the victim process’s memory with the malicious executable. Then, the malware restores the process environment so that the malicious code can run by calling SetThreadContext
to set the entry point to point to the malicious code. ResumeThread
is called to initiate the malware. We can see that this happens in the function at 004010EA.
3. Where is the malicious payload stored?
Answer: As we already mentioned in answer 2, the malicious payload is stored in the resource section of Lab12-02.exe
.
4. How is the malicious payload protected?
Answer: As we already mentioned in answer 2, the data is XOR-encoded. We saw that the data is decoded by XORing each byte with 0x41 (A).
5. How are strings protected?
Answer: The entire payload is XOR encoded with 0x41 (A). This also counts for the strings.
Comparing my answers to the Lab 12-2 solutions
Conclusions after comparison:
- No differences!
Lab 12-3
Analyze the malware extracted during the analysis of Lab 12-2, or use the file Lab12-03.exe.
1. What is the purpose of this malicious payload?
Answer: As we already learned from Lab 12-2, the malicious payload is a keylogger that logs keystrokes to the practicalmalwareanalysis.log
file which is created in the directory that the malware is launched from.
2. How does the malicious payload inject itself?
Answer: The malware is using hook injection.
Note that the original value pushed onto the stack for the idHook
parameter was 0xD (13). I changed this to the WH_KEYBOARD_LL
symbolic constant after looking up this value in the Microsoft documentation for the SetWindowsHookExA function.
WH_KEYBOARD_LL
installs a hook procedure that monitors low-level keyboard input events.
3. What filesystem residue does this program create?
Answer: The practicalmalwareanalysis.log
file where the keystrokes are logged to.
Comparing my answers to the Lab 12-3 solutions
Conclusions after comparison:
- No differences!
Lab 12-4
Analyze the malware found in the file Lab12-04.exe.
1. What does the code at 0x401000 accomplish?
Answer: The function checks if a PID belongs to winlogon.exe
. If it does, it returns 1. If it doesn’t, it returns 0.
Here we can see it compare winlogon.exe
to smss.exe
:
This will return 0, and the program keeps looping through the list off processes until it finds winlogon.exe
.
2. Which process has code injected?
Answer: winlogon.exe
appears to be the target for code injection. After the function at 00401000 finds the PID for winlogon.exe
, it passes that PID to the function at 00401174:
We can confirm this in our debugger:
- Here we can see the value
0x2C8
(712 in decimal) being pushed onto the stack before calling the function at 00401000. We already discussed that the function at 00401000 is the function that checks if the PID belongs towinlogon.exe
- In process explorer we can see that
winlogon.exe
has a PID of 712 (0x2C8
). So the function at 00401000 should return 1
- Stepping over the call to 00401000, we see that the function returned 1
- And we confirm that the PID belonging to
winlogon.exe
is pushed onto the stack before calling the function at 00401174
The function at 00401174 performs privilege escalation using SeDebugPrivilege and then loads sfc_os.dll
with an ordinal value of 2. This ordinal value is being used as the lpStartAddress
when creating a remote thread on the winlogon.exe
process:
3. What DLL is loaded using LoadLibraryA?
Answer: As already mentioned in the previous answer, LoadLibraryA is used to load sfc_os.dll
.
4. What is the fourth argument passed to the CreateRemoteThread call?
Answer: lpStartAddress
, which is set to the ordinal value #2 of sfc_os.dll
. After some online research, I learned that this is an unnamed export that is known as SfcTerminateWatcherThread
. This export can be used to disable Windows File Protection (WFP), and must be invoked in the winlogon.exe
process. See this excellent resource for more information: Disable WFP completely until the computer is next rebooted via undocumented SFC API.
5. What malware is dropped by the main executable?
Answer: The function located at 004011FC replaces C:\Windows\System32\wupdmgr.exe
with the executable that is extracted from the resource in Lab12-04.exe
:
So the executable that we find in the resource section of Lab12-04.exe
is the malware that is being dropped. But before it replaces the legitimate C:\Windows\System32\wupdmgr.exe
, the legitimate wupdmgr.exe
is copied into a temp folder as winup.exe
:
The call to MoveFileA
can be found at 00401584.
6. What is the purpose of this and the dropped malware?
Answer: The malware first searches for the PID of winlogon.exe
, then performs privilege escalation using SeDebugPrivilege, and creates a remote thread on the winlogon.exe
process with ordinal value #2 of sfc_os.dll
(AKA SfcTerminateWatcherThread
) as the lpStartAddress. This is done to disable WFP. Once WFP is disabled, the malware copies the legitimate C:\Windows\System32\wupdmgr.exe
file to a temp folder as winup.exe
. Then it replaces C:\Windows\System32\wupdmgr.exe
with the executable that is stored in the resource section of Lab12-04.exe
. This dropped malware executes the original Windows Update executable (now located in the temp folder as winup.exe
) and attempts to download a file from http://www.practicalmalwareanalysis.com/updater.exe
to C:\Windows\System32\wupdmgr.exe
.
Comparing my answers to the Lab 12-4 solutions
Conclusions after comparison:
- No differences again! I’m proud of this one :)