Written by Karsten Hahn and John Dador
In this article, we’ll dive into how this pirate gaming platform turns fun and “free” entertainment into an opportunity for cybercriminals and why a single download of a pirated game can cost you more than a few hours of gameplay.
This blog series consists of two parts. This article is part one and discusses the initial infection and HijackLoader in detail. The second part describes the ACRStealer payload.
Initial infection
In November of 2025, we discovered a Reddit post (figure 1) about malicious activity. The redditor claimed to have been hacked by an unknown infostealer after they had downloaded a game. The initially reported file was masquerading as a Python-based setup file but upon investigation, the downloaded file had been changed into a different one. This signaled to us that the URL is still live and actively infecting.
We asked the affected user for the download URL. This led us to PiviGames, a pirate gaming site.
PiviGames redirection as malvertising
Analysis of the website shows that it employs Cloudflare’s challenge and telemetry scripts to appear legitimate. However, upon closer inspection at the start of the code, it loads a JavaScript file named pgedshop.js. This JavaScript file is the one responsible for redirecting users to malicious sites. This script contains two defined URLs and uses browser cookies to control the site's redirection behavior. On the user’s first visit, the script redirects it to hxxps://adbuho[.]shop/HIx0J which appears to be an advertising network. It then marks the cookies used so that the following visits will redirect to hxxps://pulseadnetwork[.]com/jump/next.php?r=2558259, which is also a benign advertising network (not hosting a malicious file).
The URL hxxps://adbuho[.]shop/HIx0J redirects to a domain consisting of randomized characters followed by a top-level domain ".pro/" and an additional randomized path. This redirection chain ultimately leads to a MediaFire download link.
The MediaFire link hosts a ZIP archive file named "Full Version Setup 6419 Open.zip". Notably, the password is directly embedded in its filename (“6419”).
Upon extracting the ZIP file, multiple resource files are present along with a single executable file named Setup.exe[2].
HijackLoader
The Setup.exe[2] is a clean game launcher, but it executes malicious code via DLL sideloading, when the game loads Conduit.Broker.dll[3].
Conduit.Broker.dll[3] belongs to the HijackLoader family. Notable analysis and descriptions of this loader family have been done before by Nikolaos Pantazopoulos in [zscaler23] and [zscaler25] and by Ryan Weil in [trellix25]. Given the malware’s complexity, we believe our analysis adds a few undocumented details. Furthermore, we provide tooling for HijackLoader.
Stage 1: ConduitBroker.dll
The DLL exports 50 functions and mainly consists of clean code. However, the function BrokerManagerClient_AddPathMapping is patched with malicious code and extends over other import functions such as BrokerManagerClient_MapPath. Whoever inserted that patch, did so without consideration whether the size fits into that function. This also causes odd behavior in IDA Pro because some of these functions start in the middle of patched instructions (see figure 4), and IDA insists on keeping function definitions from exports.
Conduit.Broker.dll uses inlined API resolving, which means there is no dedicated function that the malware uses to dynamically resolve APIs. Instead, the full PEB walking code is inlined whenever a function call should be done. We have seen inlined API resolving in various samples in the past two years, for example in RisePro, just with a different hashing algorithm.
The API hashing algorithm is similar to djb2; however, instead of initializing the seed with 5381 and the multiplier with 33, the malware uses 0x8798338 for the seed and 2 for the multiplier. The exact hashing algorithm is as follows (Python 3 implementation):
def calc_hash(apiname):
hash = 0x8798338
for c in apiname:
hash = (ord(c) + 2 * hash) & 0xFFFFFFFF
return hash & 0xFFFFFFFF
The malware decrypts parts of the file Groumcumgag.ic[4], which resides in the same folder as the DLL. The header for the encrypted data blob starts at offset 0x4A19, where the first dword is the size of the encrypted data (0x2080), and the second dword is an XOR key, in this case 0x1CAB3515. Right after that follows the XOR encrypted data iself (see figure 5).
We created a decryption script for Groumcumgag.ic that you can download here. The decrypted blob contains shellcode for the next stage and the name of a DLL, which is in this case “evr.dll”, the Enhanced Video Renderer.
Conduit.Broker.dll then loads the next stage via module stomping: First, it loads the legitimate system32 library “evr.dll” and injects the decrypted shellcode into the BaseOfCode of “evr.dll”. Then, it calls a function of the shellcode with a struct as argument that, among others, contains the filename “Zootkumbak.uhp”. This file is the configuration for the malware.
Stage 2: evr.dll shellcode and module table
The shellcode in evr.dll has the main purpose of concatenating and decrypting the main configuration from the file Zootkumbak.uhp[5].
But first it resolves imports and determines if certain processes are present by calculating hashes for the process names. If it finds any of the targeted processes, it delays execution for five seconds.
The hash calculation function for both imports and process names, relies on a constant multiplier from the previous stage, so we expect this value to change for other variants of HijackLoader.
Next, the second stage shellcode parses the Zootkumbak.uhp[5] file. The encrypted data for the configuration is fragmented into 90 pieces that are scattered across the file.
To figure out where each of the pieces is located, HijackLoader performs a pattern search. It uses the search pattern ‘?????@IDAT’, which HijackLoader interprets as a wildcarded 4-byte value that is directly followed by the string ‘IDAT’. The wildcarded value is the size of the current chunk. Our sample has a total of 90 chunks.
After the first ‘IDAT’ pattern starts the header for our encrypted configuration. The full header including the search pattern looks as follows:
chunk_size | ‘IDAT’ | magic | xor_key | compressed_size | uncompressed_size
Each of these values is four bytes long. The XOR key decrypts all chunks of the configuration. The compressed size is the total size of all chunks before decompression and the uncompressed size equivalently is the total size after decompression. After this header starts the encrypted data of the first chunk.
For each of the remaining ‘IDAT’ chunks the encrypted data starts directly after the ‘IDAT’ pattern.
To deobfuscate the configuration file Zootkumbak.uhp, we decrypt each chunk with the XOR key from the header, concatenate the chunks in the order they were found, and finally decompress them with LZNT1.
Among others, the configuration file contains the encrypted payload, various settings and a module table with names and offsets to even more configuration data.
We created a configuration extractor that deobfuscates Zootkumbak.uhp, extracts settings from it, dumps the payload and all entries of the module table.
We use the term “module table” here to be consistent with the terminology of previous articles [zcaler23, zscaler25, trellix25]. However, the contents are not strictly limited to modules. The table may, in fact, contain any type of data. In some cases, namely the MUTEX, COPYLIST, SM and CUSTOMINJECTPATH, the module table entries are strings-based settings or string lists. In other cases, the entries are configuration settings, shellcode or full PE images.
The present sample has the following module table entries and values for string entries, you will find a description for each of these entries is in [zcaler23, zscaler25]:
| Module | Value |
|---|---|
| AVDATA | custom structured data containing CRC hashes of AV process names and flags, can be decoded with avdata_decoder.py |
| ESAL | shellcode, assists with injection |
| ESAL64 | shellcode, assists with injection |
| ESLDR | shellcode, assists with injection |
| ESLDR64 | shellcode, assists with injection |
| ESWR | shellcode, assists with injection |
| ESWR64 | shellcode, assists with injection |
| FIXED | PE file, here zip.exe signed by “VMWare Inc”, used a host for Process Doppelgänging |
| LauncherLdr64 | PE file for loading the payload |
| modCreateProcess | shellcode, helps with process creation |
| modCreateProcess64 | shellcode, helps with process creation |
| modTask | shellcode, used for persistence |
| modTask64 | shellcode, used for persistence |
| modUAC | shellcode, UAC bypass |
| modUAC64 | shellcode, UAC bypass |
| modWD | shellcode, defender evasion |
| modWD64 | shellcode, defender evasion |
| modWriteFile | shellcode |
| modWriteFile64 | shellcode |
| rshell | shellcode, loads the payload via module stomping |
| rshell64 | shellcode, loads the payload via module stomping |
| ti | shellcode, main module, 32 bit |
| ti64 | shellcode. main module, 64 bit |
| tinycallProxy | shellcode, executes API calls |
| tinycallProxy64 | shellcode, executes API calls |
| tinystub | PE file stub |
| tinystub64 | PE file stub |
| tinyutilitymodule.dll | PE file |
| tinyutilitymodule64.dll | PE file |
| SM | "mpr.dll" string, name of clean target dll for module stomping |
| COPYLIST | string list with the following filenames:
|
| MUTEX | "RXRCJOIAVDWOEK" string, used as mutex name |
| CUSTOMINJECT | PE file, MicrosoftEdgeUpdate, used as injection host |
| CUSTOMINJECTPATH | "%TEMP%\d0eccdb9\MicrosoftEdgeUpdate.exe" string, path to drop CUSTOMINJECT |
| X64L | shellcode |
The second stage shellcode loads the main module from that module table, which is the shellcode ti64. The configuration file also contains the target DLL path for the next stage in its main header (not in the module table): %windir%\SysWOW64\rasapi32.dll.
Just like the stage before, HijackLoader’s second stage stomps the module: It loads the target rasapi32.dll, then injects the next stage’s shellcode into the target’s BaseOfCode and runs it.
The evr.dll shellcode forwards the deobfuscated configuration data and module table to stage three as arguments.
Stage 3: ti64 module in rasapi32.dll
The code of this shellcode is extensive because it handles all the possible settings of the configuration and the module table entries that might be used by HijackLoader. Because it contains most of the interesting code of this loader family, it is commonly referred to as the main module of HijackLoader.
The ti64 shellcode starts by resolving import functions via API hashing. This time it uses CRC32 as hashing algorithm.
Next, it obtains the SM entry from the module table, which is ‘mpr.dll’ in this case, and saves it as target DLL for module stomping.
The sample then checks NTDLL for inline hooks and, if found, removes them.
If the ANTIVM module table entry is present, it performs various anti-VM checks. However, the present sample does not have this entry.
If a specific flag is set in the config, HijackLoader will copy all files, which are listed in the module table entry COPYLIST, to a subdirectory in %ALLUSERSPROFILE% and restart itself there. The configuration defines this subdirectory at offset 0x13 and for our sample this is “d0eccdb9”.
The ti64 shellcode also loads the encrypted payload from the configuration. To do so, it obtains three values: relative data offset, key size, and encrypted data size.
At offset 0xeec starting from the module table is the relative offset to the encrypted data. This offset is relative to the module count field at 0xee4, so the actual offset is: module table offset + 0xee4 + relative offset
At offset module table + 0xca4 is the key size in dwords.
At offset module table + 0xca8 is the size of the encrypted data.
The encrypted data starts with the XOR key, which decrypts the payload in the remaining data. Our tool decrypts and dumps the payload to a file named PAYLOAD into the same folder as the module table entries.
To sum it up, the ti64 shellcode is an orchestrator for most of the modules in the module table and responsible for the following features of HijackLoader:
- AV product detection and adjustable behavior based on AVDATA flags, process CRC hashes and detected products
- Persistence modules
- Anti-VM modules
- UAC bypass modules
- Choosing one of six payload injection or loading techniques
- Adjustable injection host files via FIXED and CUSTOMINJECT
- Interprocess communication
- NTDLL hook detection and removal
Process injection in ti64 and post stage 3
HijackLoader implements several ways to load and inject the payload in ti64. Several injection_flag bitmasks and the presence of a relocation directory determine which injection function it chooses. It is not possible to determine the meaning of all of these flags based on the sample alone because some are merely read from the configuration.
Furthermore, there is a lot of duplicated code and the parts of ti64 which choose the appropriate injection function are scattered between the huge entry point function and various decision trees in subfunctions. That makes it overall challenging to determine the exact conditions when Hijackloader chooses which injection function.
Generally, we found at least six distinct payload loading and injection methods if we do not count miniscule differences of otherwise duplicated functions. These are:
Two variants of Process Hollowing
Two variants of Process Doppelgänging and Mapped Section Injection hybrid
Mapped Section injection
Run payload via ESWR module
Run payload via Process Doppelgänging and ESWR module
Run payload via LauncherLdr/LauncherLdr64 module
We describe two representative techniques, 1 and 2, below.
For the Process Hollowing method, HijackLoader copies the PE image saved in CUSTOMINJECT to the path in CUSTOMINJECTPATH. For this sample the path is %TEMP%\d0eccdb9\MicrosoftEdgeUpdate.exe and the CUSTOMINJECT executable is indeed a legitimate 32-bit MicrosoftEdgeUpdate.exe.
The ti64 module then loads and runs the modCreateProcess64 module to create a suspended MicrosoftEdgeUpdate.exe process, it injects the 32-bit rshell module shellcode into the 32-bit MicrosoftEdgeUpdate.exe and runs it. The injected rshell then uses Heaven’s Gate with a call-add-retf trampoline to transition to 64-bit code and module stomping for loading the payload.
For the Process Doppelgänging hybrid the ti64 shellcode dumps the clean PE from the FIXED module table entry to disk and names it “com_web_filter_v4_0” (this name is part of the configuration outside of the module table).
In our sample the FIXED module is a clean zip.exe signed by “VMWare Inc”. The module ti64 then uses this clean file as host for Process Doppelgänging by adding a new .dat section to the file and writing the rshell module into it via transactional write and rollback operations.
Afterwards it performs Mapped Section injection to load the .dat section into the remote process of MicrosoftEdgeUpdate.exe. That process, again, runs rshell to load the payload via module stomping.
HijackLoader’s interprocess communication
HijackLoader uses two means for interprocess communication.
Firstly, it saves encrypted data, including the module table and injection information, in temporary files.
Secondly, it saves data in environmental variables, including the file names of the aforementioned temporary files. The variable names of these environmental variables are encoded. The generator for the variable names receives a hash which represents what the variable means. At first HijackLoader generates a system specific seed by calculating the CRC32 hash of the result of GetComputerNameW. Next, it xors the seed with the hash value. Then it passes the value into srand(). Subsequent calls to rand() generate an uppercase string of variable length.
The meaning of some of these hashes is listed in the table below and can be used for logging inter process communication.
| Initial hash | Meaning |
|---|---|
| 0xe1abd1c2 | temporary file name, contains injection information |
| 0xf1e5a323 | command line string |
| 0xaabbccdd | injection flags 1 |
| 0xaaeecedb | injection flags 2 |
| 0xccbbccdd | injection size |
| 0xbbaaccdd | injection address |
| 0xaebecede | injection meta data |
| 0xdabecede | image base of inject PE |
| 0xa5b5c41a | "cmd.exe /start" command line string |
Lovecraftian malware: an exercise in patience
This HijackLoader analysis is by no means complete; we focused on the details that matter most for the present sample.
In previous articles about HijackLoader there were several small but notable mentions about the code quality, and after working through this code ourselves we understand why.
The ti64 module is a big piece of spaghetti software in the form of pure shellcode. In the decompiler, it takes several seconds to scroll through local variable declarations in the entry point function although this code is not obfuscated apart from hash resolving of APIs, modules and process names. Renaming one variable in the decompiler freezes the application for three seconds. The multiple structs we created to make the code understandable have sizes of 0x100 to 0x500 bytes. These structs are re-used in other modules of the code.
Compared to other in-depth analysis work that we have done before, this one is not an experience we would like to repeat very soon.
Our next article will describe the payload of this HijackLoader sample in detail, which is ACRStealer.
Sample hashes
[1] FuII Verslon Setup 6419 σρєи Download.zip (sic!), archive with all files
418a1a6b08456c06f2f4cc9ad49ee7c63e642cce1fa7984ad70fc214602b3b1
[2] Setup.exe, loads Conduit.Broker.dll
5d11218f67cfe78347280b0e1a06ade63c890ac78f970f57b0200ff5be8aa77c
[3] Conduit.Broker.dll, sideloaded DLL with malicious patch
772fde719a53147 6740db34df6bdb530f4e96acfd9a92e30ec0476fe65f588f5
[4] Groumcumgag.ic, encrypted stage 2 shellcode
fed719608185e516c70a1e801b5c568406ef6e1c292e381ba32825c6add93995
[5] Zootkumbak.uhp, encrypted configuration
af2ade19542dde58b424618b928e715ebf61dffb6d8ca9d4b299e532dfa3b763
[6] ACRStealer, payload
59202cb766c3034c308728c2e5770a0d074faa110ea981aa88f570eb402540d2
Previous research on HijackLoader
[zcaler23] www.zscaler.com/blogs/security-research/technical-analysis-hijackloader
[zcaler25] www.zscaler.com/blogs/security-research/analyzing-new-hijackloader-evasion-tactics
[trellix25] www.trellix.com/blogs/research/analysis-of-hijackloader-and-its-infection-chain









