02/26/2026

Free Games, Costly Consequences

Free Games, Costly Consequences Malware

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 MUTEXCOPYLISTSM 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]: 

ModuleValue
AVDATAcustom structured data containing CRC hashes of AV process names and flags, can be decoded with avdata_decoder.py
ESALshellcode, assists with injection
ESAL64shellcode, assists with injection
ESLDRshellcode, assists with injection
ESLDR64shellcode, assists with injection
ESWRshellcode, assists with injection
ESWR64shellcode, assists with injection
FIXEDPE file, here zip.exe signed by “VMWare Inc”, used a host for Process Doppelgänging
LauncherLdr64PE file for loading the payload
modCreateProcessshellcode, helps with process creation
modCreateProcess64shellcode, helps with process creation
modTaskshellcode, used for persistence
modTask64shellcode, used for persistence
modUACshellcode, UAC bypass
modUAC64shellcode, UAC bypass
modWDshellcode, defender evasion
modWD64shellcode, defender evasion
modWriteFileshellcode
modWriteFile64shellcode
rshellshellcode, loads the payload via module stomping
rshell64shellcode, loads the payload via module stomping
tishellcode, main module, 32 bit
ti64shellcode. main module, 64 bit
tinycallProxyshellcode, executes API calls
tinycallProxy64shellcode, executes API calls
tinystubPE file stub
tinystub64PE file stub
tinyutilitymodule.dllPE file
tinyutilitymodule64.dllPE file
SM"mpr.dll" string, name of clean target dll for module stomping
COPYLIST

string list with the following filenames:

  • Conduit.Broker.dll 

  • D_C.exe 

  • Groumcumgag.ic 

  • TE.Common.dll 

  • TE.Host.dll 

  • TE.Loaders.dll 

  • TE.WinRT.dll 

  • Wex.Common.dll 

  • Wex.Communication.dll 

  • Wex.Logger.dll 

  • Zootkumbak.uhp 

  • !D_C.exe 

  • ~TE.Loaders.dll 

MUTEX "RXRCJOIAVDWOEK" string, used as mutex name
CUSTOMINJECTPE file, MicrosoftEdgeUpdate, used as injection host
CUSTOMINJECTPATH "%TEMP%\d0eccdb9\MicrosoftEdgeUpdate.exe" string, path to drop CUSTOMINJECT
X64Lshellcode

 

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:  

  1.  Two variants of Process Hollowing 

  2. Two variants of Process Doppelgänging and Mapped Section Injection hybrid 

  3. Mapped Section injection 

  4. Run payload via ESWR module 

  5. Run payload via Process Doppelgänging and ESWR module 

  6. 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 hashMeaning
0xe1abd1c2temporary file name, contains injection information
0xf1e5a323command line string
0xaabbccddinjection flags 1
0xaaeecedbinjection flags 2
0xccbbccddinjection size
0xbbaaccddinjection address
0xaebecedeinjection meta data
0xdabecedeimage 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