← Reports

Remote Access Trojan · Worm · Dropper

VioletWorm - game.exe

Author
Moise Medici
Updated
07 Sept 2026 · Completed
Difficulty
Easy
Platform
Windows
Capabilities
Dropping Secondary PayloadsPersistence MechanismsCommand and Control C2 CommunicationCommand Execution via Powershell Cmd BashData-ExfiltrationFile EncryptionKeyloggingScreen CaptureWebcam AccessClipboard ManipulationCredential TheftAMSI and ETW BypassSandbox and VM EvasionReflective Code LoadingUSB SpreadingDenial of Service
Tags
pythonC#

Dynamic Analysis of the Original Sample

In this section we execute the compiled Python file, with the intention of answering question 7 and also of confirming that what we have analyzed from the source code is correct. In general, for simple code like the one extracted, I wouldn’t execute it to confirm my understanding, but since some of the code seems not to have been extracted properly it is worth looking at the behaviour during execution.

For this part of the analysis the focus will be:

  • Procmon to see which files are written, and the PID/PPID relationships between the processes created
  • API Monitor 64 bits to track: LoadLibrary, GetProcAddress and VirtualProtect to see what is written to AmsiScanBuffer and EtwEventWrite
  • Regshot for the registry changes we have seen before, though in our environment SmartScreen should already be disabled

I am currently not focusing on anything network related, since any traffic would be generated by files that have not been analyzed yet and would only be a distraction.

The sample is executed via API Monitor, which will be the parent process.

Procmon process tree for the sample and its children.
Fig. 2: Procmon process tree for the sample and its children.

The process tree is as follows:

  • the sample has PIDs 3932 and 3096
  • RuntimeBroker.exe is executed with PIDs 6348 and 6048, which spawn:
    • mshost6x5y.exe is executed with PID 3936, which opens powershell.exe and Conhost.exe
    • game.exe runs with PID 1140 and spawns schtasks.exe and Conhost.exe

Looking at Regshot, all sorts of activity is happening. More than 300k registry keys and values were deleted, and the three registry keys seen in the Python script have indeed been added, along with a few others:

----------------------------------
Files added: 27
----------------------------------
C:\Users\REM\AppData\Local\Microsoft\CLR_v4.0\UsageLogs\game.exe.log
C:\Users\REM\AppData\Local\Microsoft\CLR_v4.0\UsageLogs\mshost6x5y.exe.log
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD52F.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD530.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD531.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD532.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD533.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD534.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD535.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD546.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD547.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD548.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD549.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD54A.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD54B.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD54C.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD54D.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\RuntimeBroker.exe
C:\Users\REM\AppData\Local\MicrosoftPQGHQ\game.exe
C:\Users\REM\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\game.exe
C:\Users\REM\AppData\Roaming\game.exe
C:\Windows\System32\Tasks\game

Two folders have been added:

----------------------------------
Folders added: 2
----------------------------------
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete
C:\Users\REM\AppData\Local\MicrosoftPQGHQ

With the following data:

----------------------------------
Files added: 27
----------------------------------
C:\Users\REM\AppData\Local\Microsoft\CLR_v4.0\UsageLogs\game.exe.log
C:\Users\REM\AppData\Local\Microsoft\CLR_v4.0\UsageLogs\mshost6x5y.exe.log
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD52F.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD530.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD531.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD532.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD533.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD534.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD535.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD546.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD547.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD548.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD549.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD54A.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD54B.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD54C.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete\icnD54D.tmp
C:\Users\REM\AppData\Local\Microsoft\Windows\RuntimeBroker.exe
C:\Users\REM\AppData\Local\MicrosoftPQGHQ\game.exe
C:\Users\REM\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\game.exe
C:\Users\REM\AppData\Roaming\game.exe
C:\Windows\System32\Tasks\game

Before looking at API Monitor, let’s see which files have been written by the sample and collect their hashes. In Procmon, Tools → File Summary → By Extension shows all the files that have been interacted with, grouped by extension. The following are the interesting executables:

  • C:\Users\REM\AppData\Local\MicrosoftPQGHQ\game.exe and C:\Users\REM\AppData\Roaming\game.exe: both have hash 43e91f2ff0f90919f77aaa7d21a77a93b6e413df8a4e8c818e7d215f800e5d13
  • C:\Users\REM\AppData\Local\Microsoft\Windows\RuntimeBroker.exe: e450b7efc8b429b618d2d22a074a3dd55c07b451eef315e0e20be7d9054ef18c
  • C:\Users\REM\AppData\Local\Microsoft\Windows\Caches\mshost6x5y.exe: file not found anymore, which is expected since it was seen the os.remove python code for this file.

So game.exe has a different hash.

The same can be done with the log extension, where two interesting files are present: C:\Users\REM\AppData\Local\Microsoft\CLR_v4.0\UsageLogs\game.exe.log and C:\Users\REM\AppData\Local\Microsoft\CLR_v4.0\UsageLogs\mshost6x5y.exe.log.

Both contain similar data:

1,"fusion","GAC",0
1,"WinRT","NotApp",1
3,"System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089","C:\WINDOWS\assembly\NativeImages_v4.0.30319_64\System\37a1d51f35918dd36a0d4e34cc91732e\System.ni.dll",0
3,"System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089","C:\WINDOWS\assembly\NativeImages_v4.0.30319_64\System.Core\89bc329e8c65a9e13067c9776d925d78\System.Core.ni.dll",0
3,"Microsoft.VisualBasic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a","C:\WINDOWS\assembly\NativeImages_v4.0.30319_64\Microsoft.V9921e851#\26e9c525765169a4bfa07d6eb6f43ff1\Microsoft.VisualBasic.ni.dll",0
3,"System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a","C:\WINDOWS\assembly\NativeImages_v4.0.30319_64\System.Drawing\7e5e0d92b127a5150606d81839f29044\System.Drawing.ni.dll",0
3,"System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089","C:\WINDOWS\assembly\NativeImages_v4.0.30319_64\System.Windows.Forms\028f9e8b0c8b1820df7bec952b01fe12\System.Windows.Forms.ni.dll",0

Lastly, the tmp files. Under the C:\Users\REM\AppData\Local\Microsoft\Windows\Explorer\IconCacheToDelete directory, more and more tmp files keep getting created. All of them contain unintelligible data.

In Procmon, the PowerShell process created with PID 7656 executes:

Terminal window
"powershell.exe" -WindowStyle Hidden -ExecutionPolicy Bypass -Command "Add-MpPreference -ExclusionPath 'C:\Users'"

This runs the Add-MpPreference command, which changes the Windows Defender configuration 17 by setting C:\Users as an excluded directory. The command is executed in a hidden instance of PowerShell.

Meanwhile, game.exe executes the following command:

"C:\Windows\System32\schtasks.exe" /create /f /sc minute /mo 1 /tn "game" /tr "C:\Users\REM\AppData\Roaming\game.exe"

which uses schtasks.exe, the Windows Task Scheduler, to forcefully create a new task named game that runs every minute (/sc minute /mo 1), using the %appdata%\game.exe path as the executable. This gives the sample persistence and restarts it across reboots. This can be seen in the system:

The game task in Task Scheduler, triggered at logon and repeating every minute indefinitely.
Fig. 3: The game task in Task Scheduler, triggered at logon and repeating every minute indefinitely.

Looking at API Monitor, we have to scroll all the way down through the two processes named after the sample to find the AmsiScanBuffer import, which appears almost at the end of the second process.

API Monitor showing GetProcAddress for AmsiScanBuffer and EtwEventWrite both returning NULL with error 126, the specified module could not be found.
Fig. 4: API Monitor showing GetProcAddress for AmsiScanBuffer and EtwEventWrite both returning NULL with error 126, the specified module could not be found.

The error that the modules cannot be found is odd, since a simple C program executed on the same machine can find and load them:

#include <windows.h>
#include <stdio.h>
int main() {
HANDLE hAmsi = LoadLibraryA("amsi.dll");
FARPROC pScanBuffer = GetProcAddress(hAmsi, "AmsiScanBuffer");
printf("AmsiScanBuffer loaded at address %p\n", pScanBuffer);
HANDLE hNtDll = LoadLibraryA("ntdll.dll");
FARPROC pEtw = GetProcAddress(hNtDll, "EtwEventWrite");
printf("EtwEventWrite loaded at address %p\n", pEtw);
return 0;
}

When executed:

> .\check_modules.exe
AmsiScanBuffer loaded at address 00007FFDE7A923E0
EtwEventWrite loaded at address 00007FFDFAC822E0

It is also unusual that the error returned by GetProcAddress is 126, since 126 is ERROR_MOD_NOT_FOUND, which is not what GetProcAddress would raise. In fact, changing the program above to ask GetProcAddress for a random procedure name shows that the error returned is 127, which stands for ERROR_PROC_NOT_FOUND 17:

#include <windows.h>
#include <stdio.h>
int main() {
HANDLE hNtDll = LoadLibraryA("ntdll.dll");
FARPROC pMoise = GetProcAddress(hNtDll, "Moise");
if (pMoise == NULL) {
printf("Error getting moise %lu\n", GetLastError());
}
return 0;
}

When executed, it prints:

> .\check_modules.exe
Error getting moise 127

After some digging into how to call GetProcAddress and LoadLibrary on a 64 bit system, the issue turns out to be that LoadLibrary returns an invalid address (-1227489280), which internally makes Windows set the error code 126 (ERROR_MOD_NOT_FOUND). Since the last error is never cleared, the execution ends up returning that error as if it had been raised by GetProcAddress. What the author should have done is use LoadLibraryW together with GetProcAddress and a bytes like string. A fully functional rewrite would be:

import ctypes
from ctypes import wintypes
def amsi_check():
kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
kernel32.LoadLibraryW.argtypes = [wintypes.LPCWSTR]
kernel32.LoadLibraryW.restype = wintypes.HMODULE
kernel32.GetProcAddress.argtypes = [wintypes.HMODULE, wintypes.LPCSTR]
kernel32.GetProcAddress.restype = ctypes.c_void_p
amsi_dll = kernel32.LoadLibraryW("amsi.dll")
if not amsi_dll:
raise ctypes.WinError(ctypes.get_last_error())
p_amsi_scan_buffer = kernel32.GetProcAddress(
amsi_dll,
b"AmsiScanBuffer"
)
if not p_amsi_scan_buffer:
raise ctypes.WinError(ctypes.get_last_error())
print(f"AmsiScanBuffer: {p_amsi_scan_buffer:#x}")
amsi_check()