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


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 a stocuUXlBXJEyFZuoaR object that loaded as Assembly and invoked[1```]

→ resources to load and decrypt.


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``].


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

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
00000E1A  dntdll.dll


-> Debugging 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.

**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
        **56kb-32bit** payload: 055c614c181237a4a64df6346b9631a394f134bf21964f46b5479e927ef31044
        **32kb-64bit** payload: 49c25a5b8413d98d77722b6fddbc2d9c15a9c7ca4e5aef6acd5d8fd6df1e81e4
  **C]**    // malicious thread injects into svchost.exe, executes payload as an APC.
           **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


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");
                 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


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.exe64bit 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.


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 sysWow64C:\\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:


Last updated