Paranoid by Design: Statically Reconstructing a Multi-Stage JavaScript β†’ PowerShell β†’ .NET Malware Loader

Author: Danyal Rana
Analysis Date: July 2026

Original JS Sample Hash:

SHA -256: cc7b6ff483a2c3a0efe7f4d6ecefe00510ebd166723ebf7ad16e665d124245b9

MD5: 93bc0e2a9639d03ef7a5681c0f9e5ebc

Executive Summary

Modern malware increasingly distributes its functionality across multiple execution stages rather than embedding its capabilities within a single executable. Each stage reconstructs only enough information to produce the next, significantly complicating both automated detection and manual reverse engineering.

This report documents the complete static reconstruction of a multi-stage malware loader consisting of:

Β 
Obfuscated JavaScript
        ↓
PowerShell Loader
        ↓
Managed (.NET) Loader
        ↓
Encrypted Embedded Payload
Β 

The original filename supplied with the sample has intentionally been omitted. Throughout this report the malware is referred to only by a locally assigned analysis name.

Unlike conventional malware investigations, no attempt was made to execute the complete infection chain. Every stage was reconstructed statically, allowing the managed payload to be recovered and analyzed without permitting the malware to reach its intended execution path.

Initial Triage

Initial inspection identified the first-stage sample as a heavily obfuscated Windows Script Host JavaScript file.

Rather than containing obvious malicious functionality, the script consisted almost entirely of autogenerated identifiers, arithmetic expressions, lookup tables, and custom decoding routines intended to overwhelm both analysts and automated deobfuscation tools.

At first glance the script appeared to implement a custom virtual machine.

Further analysis demonstrated that this assumption was incorrect.

Instead, the JavaScript dynamically reconstructed a dictionary of environment variables using statically embedded lookup tables.

This realization fundamentally changed the analysis strategy.

JavaScript Reverse Engineering

The majority of the JavaScript stage consisted of helper routines responsible for reconstructing runtime state rather than performing malicious activity directly.

Analysis revealed the core execution pattern:

Β 
lookup[index]
        ↓
[name, value]
        ↓
environment[name] = value
Β 

Rather than emulating execution, the lookup tables themselves were extracted directly.

A custom parser was developed to reconstruct every environment variable assignment performed by the script.

This resulted in the successful recovery of:

  • 697 complete environment variables

The recovered environment was exported into a standalone table suitable for reconstructing subsequent execution stages without running the malware.

Recovering the PowerShell Stage

The reconstructed environment allowed the PowerShell stage to be recovered entirely offline.

Unlike conventional downloaders, the PowerShell script contained almost no readable strings.

Instead, it concatenated hundreds of previously reconstructed environment variables to produce an encoded managed payload.

Analysis showed that the script referenced 698 unique environment variables.

Only one variable was intentionally absent from the recovered dataset.

Further investigation revealed that this missing value did not belong to the encoded payload itself. Instead, it represented a runtime argument later supplied to the managed entry point.

Recognizing this distinction prevented the reconstructed payload from being incorrectly classified as incomplete.

Custom Encoding Analysis

The PowerShell loader did not employ Base64, hexadecimal encoding, or any standard serialization format.

Instead, it implemented a custom sixteen-character encoding alphabet in which every two encoded characters represented one output byte.

A custom decoder was developed to:

  • Parse the PowerShell loader
  • Recover all referenced environment variables
  • Ignore the runtime-only argument
  • Reconstruct the encoded stream
  • Decode the custom alphabet
  • Rebuild the embedded managed executable

At no point was malware execution required.

Recovering the Managed Assembly

The reconstruction process successfully produced a valid managed Windows executable.

Verification confirmed:

  • PE32+
  • x64
  • .NET / CLR assembly
  • Valid CLR metadata
  • Valid metadata tables
  • Correct type definitions
  • Correct method definitions
  • Valid CLR header

The recovered assembly loaded correctly in Mono analysis tools, confirming that reconstruction had produced the original managed payload rather than a partially recovered artifact.

Managed Loader Analysis

Although the recovered assembly contained relatively few user-defined types, it relied heavily on compiler-generated storage.

The <PrivateImplementationDetails> section contained numerous embedded byte arrays, including a protected blob approximately 279 KB in size alongside several smaller encrypted byte arrays used during execution.

The assembly’s static constructor performed only minimal initialization, assigning these embedded arrays into static fields.

No cryptographic operations occurred during initialization.

Instead, the primary execution routine later consumed these embedded buffers through multiple staged decryption routines.

Embedded Payload Decryption

Static reconstruction revealed that the large embedded payload is stored in encrypted form.

Before decryption, the loader performs several preprocessing stages designed to reconstruct both the payload and its associated key material.

The execution flow currently understood is:

Β 
Embedded payload (v8793)
        ↓
Clone
        ↓
Deinterleave / repack byte stream
        ↓
Recover embedded key material (v6236)
        ↓
Key transformation
        ↓
SHA-256
        ↓
RC4 decryption
        ↓
Payload validation
        ↓
Reflection
Β 

The loader first reconstructs embedded key material before applying a simple transformation.

The transformed data is hashed using SHA-256, producing a 256-bit cryptographic key.

This hash is then supplied directly to a standard RC4 implementation to decrypt the primary embedded payload.

Following decryption, the loader validates the reconstructed data by checking the initial decrypted bytes before continuing execution.

At the time of writing, analysis of the final decrypted payload remains ongoing.

Runtime String Decryption

The loader employs a second independent decryption subsystem responsible for protecting runtime metadata.

Rather than embedding reflection strings directly within the assembly, several encrypted byte arrays are cloned from static fields and decrypted during execution.

The reconstructed workflow is:

Β 
Embedded byte arrays
        ↓
Clone
        ↓
RC4 decryption
        ↓
UTF-8 conversion
        ↓
Trim()
        ↓
Reflection metadata
Β 

Static analysis confirmed that these decrypted strings are subsequently supplied to reflection routines including Assembly.GetType() and later method resolution logic.

Separating payload encryption from metadata encryption significantly reduces the amount of useful information available through traditional static string extraction.

Reflection and Runtime Code Generation

The recovered assembly imports remarkably few suspicious Windows APIs directly.

Instead, execution is built dynamically through extensive use of:

  • Reflection
  • DynamicMethod
  • ILGenerator
  • CreateDelegate
  • Marshal.GetDelegateForFunctionPointer()

Rather than relying on static P/Invoke declarations, the loader constructs callable delegates dynamically during execution.

Analysis further demonstrated that reflection metadata is itself decrypted only at runtime.

Type names and method names therefore do not appear in plaintext within the managed assembly, significantly complicating static analysis.

Manual Export Resolution

One of the most notable characteristics of the managed loader is the absence of conventional Windows API resolution routines.

No static imports were observed for functions such as:

  • LoadLibrary
  • GetProcAddress
  • VirtualAlloc
  • VirtualProtect

Instead, the loader performs manual export resolution entirely within managed code.

Observed behaviour includes:

  • Enumerating loaded process modules
  • Reading PE structures using Marshal::ReadInt16() and Marshal::ReadInt32()
  • Parsing export tables manually
  • Resolving exported function names using Marshal::PtrToStringAnsi()
  • Comparing export names against runtime-generated strings
  • Converting resolved addresses into callable delegates using Marshal::GetDelegateForFunctionPointer()

This effectively implements a managed export resolver while avoiding conventional Win32 import tables.

Current Execution Chain

The reconstructed execution flow is currently understood as:

Β 
Obfuscated JavaScript
        ↓
Environment reconstruction
        ↓
PowerShell loader
        ↓
Custom sixteen-character decoder
        ↓
Recovered .NET assembly
        ↓
Managed loader
        β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚              β”‚
        β–Ό              β–Ό
Runtime string RC4   Payload RC4
        β”‚              β”‚
Reflection      Embedded payload
        β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
               β–Ό
Runtime execution
Β 

Every stage described above was reconstructed statically.

No execution of the intended malware chain was required.

Key Findings

  • Successfully reconstructed a multi-stage JavaScript β†’ PowerShell β†’ .NET infection chain.
  • Recovered 697 environment variables through structural analysis.
  • Rebuilt the PowerShell stage without executing malware.
  • Reverse engineered the custom sixteen-character encoding scheme.
  • Successfully reconstructed the managed executable.
  • Verified valid CLR metadata and PE structure.
  • Identified a protected 279 KB embedded payload.
  • Reconstructed the payload key derivation process using SHA-256.
  • Identified a standard RC4 implementation protecting the embedded payload.
  • Identified a second independent RC4 implementation protecting runtime reflection strings.
  • Observed reflection-driven execution using dynamically decrypted metadata.
  • Identified manual PE export parsing implemented entirely in managed code.
  • Observed runtime delegate generation using Reflection.Emit and dynamically resolved native function pointers.

Conclusion

This sample demonstrates a carefully layered architecture designed to frustrate both automated detection and manual reverse engineering.

Rather than relying on a single obfuscation technique, functionality is distributed across JavaScript, PowerShell, and managed code, with each stage reconstructing only enough information to produce the next.

A key outcome of this investigation is that the complete managed loader was successfully recovered without permitting the malware to execute along its intended infection chain. By reconstructing the JavaScript environment, reversing the custom PowerShell encoding scheme, rebuilding the managed assembly offline, and statically reversing its internal cryptographic routines, it was possible to bypass the malware’s intended execution flow entirely.

Analysis of the managed stage revealed two distinct RC4-based cryptographic subsystems. One protects the primary embedded payload through a SHA-256-derived key, while the second encrypts runtime reflection metadata that is only decrypted immediately before use. Combined with dynamic delegate generation and manual export resolution implemented entirely in managed code, these techniques significantly reduce static indicators while increasing the complexity of reverse engineering.

Although the loader’s internal architecture has now been largely reconstructed, analysis of the final decrypted payload remains ongoing. Future revisions of this report will document the terminal execution stage, characterize the decrypted payload, and complete the reconstruction of the malware’s full runtime behaviour.