So I’ve been busy for a while and couldn’t write much, many apologies. For this article I’ll be writing about a new injection method known as Process Doppelganging and create an automated unpacker for it.
Process Doppelganging is a newly discovered injection method that bypasses all AVs HIPs engine. As it has only been discovered recently, I have yet to see any public malware samples that utilise it, so all we have to base our research on is the initial paper on the injection method. The original paper can be found here.
In short, Process Doppelganging relies on a feature called NTFS transaction, which allows you to write to a file without the data being actually written and then choosing to either commit or discard it. Process Doppelganging relies on this to write the malicious payload using a NTFS transaction which results in the file only being modified within the transaction’s context. After that, a section object is created using the NtCreateSection API from the modified file and the transaction is undone with the RollbackTransaction API. Once this is complete, NtCreateProcessEx is called with the malicious section passed as a parameter and then execution is resumed in the remote process.
The call chain goes as follow:
CreateTransaction ==> CreateFileTransacted ==> WriteFile ==> CreateSection ==> NtCreateProcessEx ==> RtlCreateProcessParametersEx ==> VirtualAllocEx ==> WriteProcessMemory ==> NtCreateThreadEx
As we can see, the usage of suspicious apis such as ReadProcessMemory/NtReadVirtualMemory, WriteProcessMemory, NtMapViewOfSection and SetThreadContext is minimal, and the image is loaded by the Windows PE loader rather than written into memory using WPM resulting in a much more legitimate looking process. It is very likely that we will see a threat actor abusing this method to execute their payload in the near future, and we need a way to quickly analyse, unpack and detect threats before they can cause damage. As detecting is the job of AVs, I’ll be writing an unpacker for this injection method.
With most automated unpackers that deals with normal injection methods, a DLL is injected into the malicious process prior to its execution (it is started with the CREATE_SUSPENDED flag and resumed after the dll is injected) and hooks are placed in APIs abused for code injection such as NtWriteVirtualMemory or NtMapViewOfSection. Unpacking a payload executed via Process Doppelganging is no different from unpacking one executed by a runpe that relies on the WriteProcessMemory API, we set hooks and catch the attempt to write the payload. However, for the best performance and results, we do not place hooks at WriteFile (which is used to write the malicious payload) but instead ntdll!NtWriteFile as many malware authors decide to call ntdll to bypass any hooks placed in kernel32. Most antivirus HIPs do not hook kernel32 either, they either place inline hook on ntdll or perform other methods of hooking in usermode as kernel32 hooking is effectively useless unless you are dealing with trashy HF-tiered malware.
After the placement of hooks we need to check the data that is written and see if it is the malicious executable that we are looking for or not. A custom function for checking the buffer to see if a PE file is being written (hint: checking for the magic, MZ) is added and if the payload is in fact a PE file, we log this and dump the file. After that, we call the original NtWriteFile and resume execution. For logging purposes, CreateFileTransactedW and NtRollbackTransaction are hooked in order for us to see whether the NtWriteFile call was executed inside a NTFS transaction or not.
I decided to fork the project PackerAttacker and add the hooks to it instead of starting a new project on my own because it already has support for unpacking some other methods used by packer to execute malware. The project can be found on GitHub. The project required some slight modifications for prime performance (as it was compiled with /MD rather than /MT leading to the VC++ runtime being required for the unpacker to work) but other than that it was perfect as a base for the unpacker.
The unpacker can (and will) be improved greatly such as by using better hooking methods (Wow32Reserved hooking to intercept syscalls or hooking x64 ntdll) but that’s for another time, for the purpose of this post this is all that will be demonstrated.