Heaven's gate and all the goodies
As a Practical Example for EarlyBird APC-Injection from Malware Evasion Through Injection part2 Episode of REM-Essentials, I found this interesting sample that's not only using EarlyBird APC Injection, but a whole lot of other tricks, since I found a niche in analyzing code, let's dive into this mess.
Sample sha256: 9173b5a1c2ca928dfa821fb1502470c7f13b66ac2a1638361fda141b4547b792
This Sample uses Early-Bird to inject malicious payload into a legitimate windows binary svchost.exe
in this case.
Basic Threat Intel
The sample starts by injecting into explorer.exe
, it drops two binaries, one of the injected payloads in turn injects into svchost.exe
.

1st stage payload unpacking
1→
Firing the Sample in dnSpy
we find it's real name: phpv1ph20_cr.exe
, stepping into the Main > InitializeComponent()
which are just basic Methods for creating Applications in C#

inside InitializeComponent()
we see basic layout manipulation methods, then some objects and functions with encrypted names, which in short does the following:
Gets a
.bitmap
image from the resources that's most likely is an encrypted executable. [1`]Gets another resource of size
19bytes
, which we can guess is a key. [1``]Saving the
.bitmap
image into a byte array, and passing the array along with the key into a function that returns astocuUXlBXJEyFZuoaR
object that loaded as Assembly and invoked[1```]

→ resources to load and decrypt.

2→
Setting breakpoints on each Assembly.Load()
/ .Invoke()
and running the debugger[2]. We hit the breakpoint before Assembly.Load()
and with checking the stocuUXlBXJEyFZuoaR
object we see an executable, that's definitely the .bitmap
image been decrypted[2``].

3→
We will Step Through the Assembly.Load()
and let the program load the assembly, Stepping Into .Invoke()
and it's relevant InvokeMethodFast()
method, we find ourselves executing inside Program()
function in stub.exe
[3`] . Checking this function we see it performs some anti-analysis checks[3``] and in turn loads a resource and by checking the resources we find a MAIN.bin
binary file that's 41kb
[3```], so we dump this out and jump to the next analysis step.

2nd stage analysis MAIN.bin
MAIN.bin
The Threat Intel step gave us the information that the sample injects into explorer.exe
, drops two PEs into it, and the injected payload inside explorer.exe
injects the final payload into svchost.exe
MAIN.bin: 6ad3ca9bb4e9d96c2507934486c013c1f58c0030d503d3b10b58e5d23209429e
-> Basic Static Analysis
MAIN.bin
has sections with self-modifying code[1], two exports RemoteMain()
, TestSvchost()
[1``] and loads of interesting strings[1```].

**// [1```]**
**// Heaven's Gate?**
00000BAC wow32_64
00000C50 IsWow64Process
00000C60 KERNEL32
00000C6C explorer.exe
00000C7C SeDebugPrivilege
00000C90 SimpleMyMain
00000CA0 CreateProcessInternalW
00000CB8 kernel32
00000CC4 Hello from EXPLORER
00000D84 \\.\pipe\mynamedpipe
00000D9C RemoteMain
00000DA8 TestSvchost
00000E10 Global\%s%d
00000E30 NtAllocateVirtualMemory
00000E48 NtWriteVirtualMemory
00000E60 RtlCreateUserThread
**// EARLYBIRD APC Injection**
00003E1A NtQueueApcThread
00003E2E NtResumeThread
**// IoCs**
Unicode Strings:
---------------------------------------------------------------------------
00000A70 lfFilePath
00000A88 Software\Microsoft\Notepad
00000AC0 explorer.scr
00000ADC scr.lnk
00000AEC Explorer
00000B00 Software\Microsoft\Windows\CurrentVersion\Run
00000B5C explorer.exe
00000B78 Windows Explorer Command
00000BB8 Mozilla/5.0 (Windows; U; MSIE 8.0; Windows NT 6.0; ru-RU)
00000C34 POST
00000CD6 RNumber
00000CE8 Software\Microsoft\Modules
00000D20 %s\%d
00000D2C NameHash
00000D40 FileHash
00000D54 Flags
00000D60 Size
00000D6C Data
00000D78 %s\%s
00000DB8 http://46.105.143.221/next/version.php
00000E1A dntdll.dll
2→
-> Debugging MAIN.bin
MAIN.bin
This sample is pretty tricky and with a mixture of injection techniques/ execution and anti-analysis tricks, I will set a simple flow of it's execution and based on it we will debug through the sample with these major steps in mind.
**MAIN.bin:**
**sha256:** 6ad3ca9bb4e9d96c2507934486c013c1f58c0030d503d3b10b58e5d23209429e
**A]** // drops a **64bit** (first) payload in it's memory space through memcpy
// hollows **wow32_64** native image to contain another (second)
// **32bit** payload via memcpy this payload is an a copy of the original
// dropper with a _ possible _ decrypted binary at offset [0x8000]
**** // with ***WriteProcessMemory*** it injects both payloads into explorer.exe
**B]** // execute the injected payload via a thread as an **APC** using heaven's gate
**explorer.exe:**
**56kb-32bit** payload: 055c614c181237a4a64df6346b9631a394f134bf21964f46b5479e927ef31044
**32kb-64bit** payload: 49c25a5b8413d98d77722b6fddbc2d9c15a9c7ca4e5aef6acd5d8fd6df1e81e4
**C]** // malicious thread injects into svchost.exe, executes payload as an APC.
**svchost:**
**56kb-32bit** payload: 9a64c4f2e9c070a7b2593ad276c75aa65a74d4f90c64be60233e0636335c4cec
// connects to the internet, establishes persistence... etc
// drops two files in C:\\Users\xxx\Music\
**explorer.scr:** 6ad3ca9bb4e9d96c2507934486c013c1f58c0030d503d3b10b58e5d23209429e
**scr.lnk:** 20cc017c62b884b7f15ce889f93e24b86d7bd6f2ad03267966fdd373ac81724d
A]
1 →
After querying running processes with CreateToolhelp32Snapshot()
/ Process32First()
/ Process32Next()
and running the resulted process name pe32.szExeFile
over some mangling, comparing it with this string c48947ea
, the sample opens explorer.exe
to obtain a handle to it.

2 →
Through an interesting wrapper function, the sample figures out if it's running under Wow64
or not. It first loads the IsWow64Process()
address[2`] then executes it over it's own context[2``].

passing the hProcess
of explorer.exe
, this wrapper function is executed again[2```], with return value of ZERO
meaning the specified process _ target process explorer.exe
_ is in fact a 64bit process. that's a good guess that maybe the sample would execute the payload through Heaven's Gate which we will get to later.

3 →
The sample then allocates memory at [0x000F0000]
in it's own context[3`], then through another wrapper function, an address inside it's memory space is passed, so we might expect a write operation next[3``].

the memory at [0x000F0000]
is populated in chunks through a typical memcpy()
with a binary in the sample's context at [0x013B8000]
, Next it parses the dropped payload's PE header to check if it's 32bit/ 64bit binary.
dump1: 47092f385933db565b2172cf767c13eaae2724e70e7a96f014a42837fb2a2fa6

After populating the allocated memory with the interesting dump the sample will virtually protect it, at this point we would save it and resolve the imports by fixing mapped offsets.
quickly analyzing the dumped binary we see it's 64-bit code with the name [dll.bin]
, with 6 exports and self-modifying sections.

4 →
For the next payload to be dropped, the sample creates a named file mapping object with CreateFileMappingA()
with the name wow32_64
which is a native windows image, yet it passes no hFile
to the file mapping object, but must pass a MaxSizeHigh
which interestingly enough was [
DBDC
``→``
56kb
]
the sample's own image size, the name of the file mapping object underlying section would be the same wow32_64
. and a section is created.
CreateFileMapping( ..., DBDC, &"wow32_64");
CreateFileMappingNumaW();
NtCreateSection( ..., DBDC, ...);

next the file mapping object along with the created section is mapped in the sample's context by a call to MapViewOfFile()
that in turn calls NtMapViewOfSection()
, we end up with a mapped empty 56kb
section in the sample's process memory.

next the mapped memory block [0x200000]
is allocated using memcpy
again, with the sample's binary at [0x13D0000]
size of [
DBDC
``→``
56kb
]
.

The sample then checks the written binary if it's 64bit
or 32bit
by parsing the PE header's IMAGE_FILE_HEADER
Machine value [
14C
``/``
8664
]

The file mapping object is then unmapped along with the underlying section through NtUnmapViewOfFile()
``→``
ZwUnmapViewOfSection()

5 →
Quickly analyzing the mapped memory block we see it's identical to the sample we are running MAIN.bin
yet it actually contains another executable at offset [0x8000]
that is the first dump we dumped at step3, with old school binary diffing, we see that this version of MAIN.bin
has unpacked what seemed to be an encrypted binary from the sample we're running to a copy of itself, maybe explaining the different PE parsing checks over that mapped memory region.
dump2: c6a7567623a6630866337231e6e9601ae6c7560820ba4f97efe8fa9d2b200f9c


B]
6 →
The Injection into explorer.exe
involves Heaven's Gate
which is a technique to inject into 64bit processes from 32bit payloads, this was clearly obvious in this sample as it made couple of IsWow64Process() checks over itself and the opened process explorer.exe
.
Since we didn't go this far to only get this far, let's talk a bit about Heaven's Gate explaining how it's done in this sample.
The Sample starts by figuring out it's environment, if it's running on a 32bit or 64bit system, the loader itself is 32bit running under Wow64, again Wow64 is considered a sandbox/interface to run 32bit code inside 64bit system without modifying the 32bit code. for every process/ application there is a SysWow64 version of it.

In Step2, the Sample figures out the environment it's running on [Wow64 → payload is 32bit] and the target process [explorer.exe
→ 64bit process], so then it would literally tunnel 32bit code to be executed in 64bit process as we will see next to allocate a block of memory, drop a payload inside it and pass execution to the dropped payload inside the injected target 64bit explorer.exe
process.
In order to access the 64bit environment of a process from a 32bit payload, the sample needs to use the 64bit version of DLLs and functions and the appropriate code segment selector, for both 32bit code and 64bit code there are two different segment selectors [0x23
for 32bit code/ 0x33
for 64bit code].
This process happen as follows:
// obtain handle to **64bit** DLLs/Functions
// implement the desired function
// push 0x33 segment selector
// push the address of code to be executed
// **call** RETF: ret far that allows specifying an address to return to
// + the code segment selector
// i.e. [CALL 0x33:payload.xxxxx]
.
.
.
**push** 33
**call** payload.xxxxx
**add** DWORD PTR ss:[esp], 5
**ret far**
To witness this happen, we will debug explorer.exe
in a 64bit debugger, and as a starter we will set a breakpoint on NtTestAlert()
to catch if the injected payload is executed as an APC:
Execution flow for injecting into explorer.exe

6` →
A] Allocate memory inside explorer.exe
B] Write payload inside the allocated memory block at &allocated_mem


C] Pass execution to payload inside explorer.exe
with RtlCreateUserThread()
specifying the StartAddress
of the created thread[C], which if we jumped to inside the injected payload in explorer.exe
we'd find another jump to a function in the payload[C``].


7 →
Now we get to the fun part where the main process _dropper's process_ passes execution to the payload inside explorer.exe
and we're left with a dropped payload inside a crucial process like explorer.exe
, things will get messy because we are debugging a process that's highly expected to have interrupts, so events outside debugging the intended payload will cause us mega headaches.
Once executing the RtlCreateUserThread()
call, the sample's process exits and we hit NtTestAlert()
breakpoint[7] in the debugged explorer.exe
that let's our payload _that is a queued APC_ execute, which is in fact the location specified to jump at for the created thread[7``].

8 →
Now we're executing from the injected payload inside explorer.exe
and since we're debugging the process, the only active thread now is the malicious thread, so right now anything else depends on explorer.exe
freezes since we're debugging it.

Viewing the strings inside the injected payload, we put breakpoints on interesting strings, we also see wow32_64
the same windows native image the original dropper used to write the second payload into.

C]
9 →
Following execution inside the dropped payload. we see it parses the PE header of the injected payload, resolves some APIs to use, next it constructs the path to 32bit version of svchost.exe
inside sysWow64
→ C:\\windows\syswow64\\svchost.exe

Next the payload creates the 32bit svchost.exe
process.

right now, we would set breakpoints on CreateProcessInternalW(), NtResumeThread(), RtlCreateUserThread(), NtQueueApcThread()
.
9 →
On run we hit CreateProcessInternalW()
breakpoint with the path to 32bit svchost.exe
, creating the process in a suspended state.


The payload then tries to allocate memory inside the spawned svchost.exe
with different calls to VirtualAllocEx()/ WriteProcessMemory()
to eventually drop the final payload.
Confusingly enough the sample just stops there, the malicious thread inside explorer.exe
exits leaving a suspended unmodified svchost.exe
, so I guess the injection into svchost.exe
involves some anti-analysis checks beforehand.

We should expect svchost.exe
to be injected with the final payload, with the main thread executed as an APC, dropping two files on disk.



Interestingly enough while debugging I opened Process Hacker under the infected explorer.exe
_ that I am still debugging _ and I suddenly hit NtQueueApcThread()
breakpoint in the payload, and I end up with a trojaned processhacker.exe
🤔🤔🤔
Yet it was injected by the first payload [64bit payload] that's supposedly will do the same thing the dropper did?? I really have no idea what's happening here.

Yet from analyzing the dropped payloads' assembly we find both main exports are called via NtQueueApcThread()
/ NtResumethread()
, though we couldn't catch the APC into svchost.exe
, we can quite understand how it happened.

For a closer look into the EarlyBird-APC Injection and how it's done over the resuming of a newly created/suspended thread, CyberBit has patched the same Sample, to reproduce the EarlyBird-APC Injection in a simple way to catch it on the other end svchost.exe
Here is the full synopsis of the technique:
https://www.cyberbit.com/blog/endpoint-security/new-early-bird-code-injection-technique-discovered/
Last updated