::::::::::::::::::::::::::::
:: rasti's knowledge cave ::
::::::::::::::::::::::::::::
nothing fancy yet

A Deep Dive into Donex Ransomware (Part 1)

Created at: 08/04/2025, 11:25:59
Last Updated at: 08/04/2025

.: #malware-analysis, #reverse-engineering, #real-world :.
Table of Contents

Introduction

I stumbled upon this cool talk and learned about the existence of the Donex ransomware. I didn’t want to spoil myself so I decided to do the analysis first and then watch the talk. The only thing I know is that it’s possible to make a decryptor for this ransomware which really hyped me up and thought it’s a good opportunity to sharpen my malware analysis skills and at the same time share my findings with all of you. I am also recording my analysis process and uploading the videos in my YouTube channel. You can find the playlist here.

You can download the malware sample from Malware Bazaar.

WARNING! It's highly recommended to work in an isolated virtual machine without internet connection as the malware is 100% real. Running it on your host will harm your files.

Initial Analysis

  • SHA256 Checksum: 0adde4246aaa9fb3964d1d6cf3c29b1b13074015b250eb8e5591339f92e1e3ca
  • CPU Architecture: 32-bit (x86)

sub_4035C0 (main)

In the decompilation, we see the following two lines.

WindowA = FindWindowA("ConsoleWindowClass", *argv); // *argv = argv[0]
ShowWindow(WindowA, SW_HIDE);

This is a common technique that the malwares use to operate in stealth.

It searches for the console window spawned by the malware executable and hides it. argv[0] corresponds to the first argument of the program which is the executable name.

sub_4030D0 (Ransomware Initialization)

First, it creates a mutex with name “CheckMutex”. If it already exists, it exits.

if ( CreateMutexA(0, 1, "CheckMutex") && GetLastError() == ERROR_ALREADY_EXISTS )
{
    _loaddll(0);
    JUMPOUT(0x403338);
}

Config Extraction

A huge encrypted blob is then decrypted with the key 0xA9. The size of this blob is 0x21e7 bytes and it turns out to be an XML object with the malware configuration (authors being creative for real…).

for ( i = 0; i < 0x21C0; i += 64 )
  {
    *&xml_blob[i] = _mm_xor_si128(XOR_KEY, *&xml_blob[i]);
    *&xml_blob[i + 16] = _mm_xor_si128(XOR_KEY, *&xml_blob[i + 16]);
    *&xml_blob[i + 32] = _mm_xor_si128(*&xml_blob[i + 32], XOR_KEY);
    *&xml_blob[i + 48] = _mm_xor_si128(XOR_KEY, *&xml_blob[i + 48]);
  }
  for ( ; i < 0x21E7; ++i )
    xml_blob[i] ^= 0xA9u;

From this code, we deduce that the decryption algoritmh is simple XOR.

Let’s decrypt the first few bytes to verify our deductions.

>>> from pwn import xor
>>> xor(b'\xa9', bytes.fromhex('9596D1C4C589DFCCDBDAC0C6C7948E98'))
b"<?xml version='1"

Bingo! Let’s write a short python script to decrypt the XML configuration and dump it into a file.

import pefile
from pwn import xor

pef = pefile.PE('0adde4246aaa9fb3964d1d6cf3c29b1b13074015b250eb8e5591339f92e1e3ca')
BLOB_SIZE = 0x21e7
encrypted_xml_blob = pef.sections[2].get_data()[:BLOB_SIZE]
xml_blob = xor(encrypted_xml_blob, b'\xa9')
open('config.xml', 'w').write(xml_blob.decode())

Here is a preview of the XML configuration:

<?xml version='1.0' encoding='UTF-8'?>
<root>
<white_extens>386;adv;ani;bat;bin;cab;cmd;com;...<REDACTED>...;key;hta;msi;pdb;search-ms</white_extens>
<white_files>bootmgr;autorun.inf;boot.ini;...<REDACTED>...;GDIPFONTCACHEV1.DAT;d3d9caps.dat</white_files>
<white_folders>$recycle.bin;config.msi;...<REDACTED>...;microsoft;appdata</white_folders>	
<kill_keep>sql;oracle;mysq;chrome;veeam;firefox;excel;msaccess;onenote;outlook;powerpnt;winword;wuauclt</kill_keep>
<services>vss;sql;svc$;memtas;mepocs;msexchange;sophos;veeam;backup;GxVss;GxBlr;GxFWD;GxCVD;GxCIMgr</services>
<black_db>ldf;mdf</black_db>
<encryption_thread>30</encryption_thread>
<walk_thread>15</walk_thread>
<local_disks>true</local_disks>
<network_shares>true</network_shares>
<kill_processes>true</kill_processes>
<kill_services>true</kill_services>
<shutdown_system>true</shutdown_system>
<delete_eventlogs>true</delete_eventlogs>	
<cmd>wmic shadowcopy delete /nointeractive</cmd>
<cmd>vssadmin Delete Shadows /All /Quiet</cmd>
<content>            !!! DoNex ransomware warning !!!

&gt;&gt;&gt;&gt; Your data are stolen and encrypted

The data will be published on TOR website if you do not pay the ransom 

...<REDACTED>...

&gt;&gt;&gt;&gt; Warning! If you do not pay the ransom we will attack your company repeatedly again!
</content>
<ico>AAABAAEAIEAAAAEAIACoEAAAF...<REDACTED>.../wAAf/+AB///wA////AP///4H/8=</ico>
</root>

You can view the entire file in my GitHub repository.

Optionally, we can also extract the data from the ico XML element and base64-decode them to see the default icon that the ransomware sets for each encrypted file. You can also find this icon in my repository.

Then, the function sub_410750 is called which calls sub_410A10. Searching online some of this function’s strings, such as "Unable to add value node of type %s to parent", we find out that this function is part of an open-source XML parsing library known as mxml. More specifically, this one is mxml_load_data.

Deleting the shadow copies

The function sub_402DD0 gets the token of the current process and checks whether it belongs to the admins group. This is deduced from the subauthorities defined in the AllocateAndInitializeSid call. The subauthorities are the following:

  • 0x20 – Corresponds to the RID: SECURITY_BUILTIN_DOMAIN_RID.
  • 0x220 – Combination of RIDs: 0x200 | 0x20 which correspond to DOMAIN_GROUP_RID_ADMINS | SECURITY_BUILTIN_DOMAIN_RID.

We can find these values from the original documentation.

If the token belongs to the admins group, it executes the following commands:

cmd /c "wmic shadowcopy delete /nointeractive"
cmd /c "vssadmin Delete Shadows /All /Quiet"

These commands are commonly used by ransomware threat actors to delete the shadow copies in windows systems so that victims cannot restore their original files back from the shadow copies.

Disabling and Reverting WoW64 File System Redirection

Let’s analyze the following code snippet:

ModuleHandleA = GetModuleHandleA("kernel32.dll");
IsWow64Process = GetProcAddress(ModuleHandleA, "IsWow64Process");
if ( !IsWow64Process || (CurrentProcess = GetCurrentProcess(), IsWow64Process(CurrentProcess, &j)) )
{
  if ( j )
    Wow64DisableWow64FsRedirection(&OldValue);
}

Definition from Wikipedia:

WoW64 (Windows 32-bit on Windows 64-bit) is a subsystem of the Windows operating system capable of running 32-bit applications on 64-bit Windows.

In other words, WoW64 is the reason why we can run 32-bit applications (such as this ransomware) in 64-bit Windows systems.

But what is file system redirection?

By default, WoW64 transparently redirects all the system calls made by 32-bit applications to the system folder C:\Windows\SysWoW64 which contains 32-bit libraries and executables.

Calling Wow64DisableWow64FsRedirection, disables this file system redirection and as a result, the native System32 folder is used. This is mandatory for executing the shadow copy deletion as the malware needs to call the 64-bit vssadmin and wmic binaries. Once it’s done, the redirection can be re-enabled using Wow64RevertWow64FsRedirection.

v8 = GetModuleHandleA("kernel32.dll");
ProcAddress = GetProcAddress(v8, "IsWow64Process");
if ( !ProcAddress || (v10 = GetCurrentProcess(), ProcAddress(v10, &j)) )
{
  if ( j )
    Wow64RevertWow64FsRedirection(OldValue);
}

Conclusion

That’s all for the first post. In the next post we explore sub_4014D0 as well as the next two initialization functions. Take care :)