T1149

LC_MAIN Hijacking

Defense Evasion Last updated:

Adversaries may hijack the LC_MAIN Mach-O load command in macOS binaries to redirect initial execution flow to malicious code before returning control to the legitimate entry point. The LC_MAIN header, introduced in OS X 10.8, defines the entry point offset for a Mach-O executable. By patching this offset to point at an injected code section or cave, an attacker can execute arbitrary code under the identity of a trusted binary, bypassing application whitelisting controls that validate only the file path or name. This technique has been deprecated in the MITRE ATT&CK framework but remains relevant for forensic analysis of older macOS malware samples and legacy systems.

What is T1149 LC_MAIN Hijacking?

LC_MAIN Hijacking (T1149) maps to the Defense Evasion tactic — the adversary is trying to avoid being detected in MITRE ATT&CK.

This page provides production-ready detection logic for LC_MAIN Hijacking, covering the data sources and telemetry it touches: Process: Process Creation, File: File Modification, Microsoft Defender for Endpoint (macOS agent). The queries below are rated high severity at low confidence, and ship for 7 SIEM platforms — KQL, SPL, Elastic, QRadar, Sumo, YARA-L, LogScale.

MITRE ATT&CK

Tactic
Defense Evasion
Canonical reference
https://attack.mitre.org/techniques/T1149/
Microsoft Sentinel / Defender
kusto
// Part 1: Detect use of Mach-O binary inspection and manipulation tools with suspicious flags
let MachOInspectionTools = dynamic(["otool", "jtool", "jtool2", "vtool", "MachOView", "install_name_tool", "lipo"]);
let SuspiciousLoadCmdFlags = dynamic(["-l", "--load-commands", "LC_MAIN", "LC_THREAD", "LC_UNIXTHREAD", "entryoff", "stacksize"]);
let SensitivePaths = dynamic(["/Applications/", "/usr/bin/", "/usr/local/bin/", "/usr/sbin/", "/bin/", "/sbin/", "/opt/"]);
DeviceProcessEvents
| where Timestamp > ago(24h)
| where FileName in~ (MachOInspectionTools)
| where ProcessCommandLine has_any (SuspiciousLoadCmdFlags)
| where ProcessCommandLine has_any (SensitivePaths)
| extend TargetBinary = extract(@"(?:/Applications/[^\s]+\.app/Contents/MacOS/[^\s]+|/usr/(?:bin|local/bin|sbin)/[^\s]+|/bin/[^\s]+|/opt/[^\s]+)", 0, ProcessCommandLine)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, TargetBinary,
         InitiatingProcessFileName, InitiatingProcessCommandLine
| sort by Timestamp desc
// Part 2: Detect writes to Mach-O executable locations by non-system processes
// Run separately or union with above
// DeviceFileEvents
// | where Timestamp > ago(24h)
// | where ActionType in ("FileModified", "FileCreated", "FileRenamed")
// | where FolderPath matches regex @"/Applications/[^/]+\.app/Contents/MacOS"
//    or FolderPath startswith "/usr/bin/"
//    or FolderPath startswith "/usr/local/bin/"
//    or FolderPath startswith "/usr/sbin/"
// | where not(InitiatingProcessFileName in~ ("Installer", "softwareupdate", "pkgutil", "MRT", "XProtect", "trustd"))
// | project Timestamp, DeviceName, AccountName, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine

Detects potential LC_MAIN hijacking activity on macOS endpoints enrolled in Microsoft Defender for Endpoint. The primary query identifies use of Mach-O inspection and manipulation tools (otool, jtool, vtool, install_name_tool) targeting sensitive binary paths with load command flags (LC_MAIN, LC_THREAD, entryoff). A secondary commented-out query detects writes to Mach-O executable locations within .app bundles and system binary directories by non-standard initiating processes. Both patterns are consistent with reconnaissance or modification stages of LC_MAIN entry point hijacking. Confidence is low due to the deprecated status of this technique, limited macOS telemetry in many MDE deployments, and high false positive rate from legitimate developer tooling.

high severity low confidence

Data Sources

Process: Process Creation File: File Modification Microsoft Defender for Endpoint (macOS agent)

Required Tables

DeviceProcessEvents DeviceFileEvents

False Positives

  • Security researchers and reverse engineers routinely use otool, jtool, and vtool with -l flags to inspect Mach-O load commands for legitimate analysis
  • Software build pipelines (Xcode, CMake, conan) invoke install_name_tool and lipo against application binaries during compilation and packaging
  • macOS application notarization and code signing workflows use codesign and related tools against .app bundle executables
  • Third-party software managers (Homebrew, MacPorts) legitimately write to /usr/local/bin and /opt/ during package installation and upgrades
  • System software updates via softwareupdate and MRT modify binaries in protected system paths

Sigma rule & cross-platform mapping

The detection logic for LC_MAIN Hijacking (T1149) above is provided in a vendor-neutral form so you can deploy it on any SIEM. The same logic is shipped here as native KQL (Microsoft Sentinel / Defender), SPL (Splunk), Elastic (Elastic Security (EQL)), QRadar (IBM QRadar (AQL)), Sumo (Sumo Logic CSE), YARA-L (Google Chronicle / SecOps), LogScale (CrowdStrike LogScale (CQL)) queries. In Sigma terms, this detection targets the following logsource:

logsource:
  category: process_creation
  product: windows

Browse the community-maintained Sigma rules for this technique:


Testing Methodology

Validate this detection against 5 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.

  1. Test 1Inspect LC_MAIN Entry Point of a System Binary

    Expected signal: macOS Unified Log / ESF process event: process_name=otool, cmdline='otool -l /bin/ls', parent=bash/zsh. osquery process_open_files will show /bin/ls opened for reading by otool. No file modification events are generated by this read-only operation.

  2. Test 2Enumerate All Load Commands of a Sensitive Application Binary

    Expected signal: ESF/stream:process event: process_name=otool, cmdline targeting /Applications/Safari.app/Contents/MacOS/Safari with -l flag. macOS FSEvent: Safari binary opened for reading with otool PID. DeviceProcessEvents (MDE): FileName=otool, ProcessCommandLine contains '-l' and '/Applications/Safari.app/Contents/MacOS/Safari'.

  3. Test 3Verify Code Signature Validity of a Modified Binary

    Expected signal: ESF process event: process_name=codesign, cmdline contains '-v --deep --strict /bin/ls'. macOS Unified Log subsystem com.apple.security.codesigning records the verification result with target binary path and signing identity. If a binary were actually modified, this command would produce a 'code object is not signed at all' or 'a sealed resource is missing or invalid' error.

  4. Test 4Simulate Code Cave Discovery Using nm and size

    Expected signal: ESF process events for nm and size with respective command lines targeting /usr/bin/true. Both binaries are in /usr/bin/ (a monitored sensitive path). DeviceProcessEvents: FileName in ('nm', 'size'), ProcessCommandLine contains '/usr/bin/true'. These events fire consecutively and may indicate scripted reconnaissance.

  5. Test 5Write a Test File to an App Bundle MacOS Directory (Simulated Binary Drop)

    Expected signal: ESF/stream:file events: FileCreated for /tmp/TestApp.app/Contents/MacOS/TestApp and /tmp/TestApp.app/Contents/MacOS/TestApp.bak. DeviceFileEvents: ActionType=FileCreated, FolderPath contains '/MacOS/', InitiatingProcessFileName=bash/zsh. The /tmp/ path is not in the monitored sensitive paths by default — adjust the FolderPath filter to include /tmp/*.app/Contents/MacOS/ for this test to trigger the hunting query.


Response Playbook

Triage

  1. Identify the invoking user and process context — was otool/jtool/vtool run interactively from a terminal by a developer account, or from a non-interactive session (launchd, cron, SSH without TTY)? Non-interactive invocations against system binaries are significantly more suspicious.
  2. Examine the specific target binary referenced in the command line — is it a system utility (/bin/, /usr/bin/), a high-value application (/Applications/Safari.app, /Applications/Keychain Access.app), or a third-party tool? System binary modification is critical severity.
  3. Check the parent process of the manipulation tool — was it spawned by Xcode, Terminal, or a known IDE (legitimate developer workflow), or by launchd, a web browser, a mail client, or an Office document handler (potential initial access chain)?
  4. Retrieve the file modification timestamp of the target binary using `stat -f '%m %N' <path>` via EDR live response or osquery — compare against last known-good state from software inventory or HIDS baseline.
  5. Verify code signature integrity of any flagged binary: `codesign -v --deep <path>` — an invalid, missing, or self-signed signature on a previously Apple-signed binary indicates tampering.
  6. Check for the presence of new Mach-O sections in the binary: `otool -l <binary> | grep -A3 LC_SEGMENT` — unexpected segments with execute permissions (VM_PROT_EXECUTE) that are not present in the vendor's original binary are a strong indicator of code cave injection.
  7. Review the `__TEXT` segment entry point offset: `otool -l <binary> | grep -A5 LC_MAIN` — cross-reference the `entryoff` value against the vendor's documented or checksummed version to detect modification.

Containment

  1. If binary tampering is confirmed on a production endpoint: immediately isolate the host from the network using MDM (Jamf Pro: `jamf recon` then MDM lock, or Microsoft Defender isolation for macOS-enrolled devices) to prevent lateral movement or C2 communication via the hijacked process.
  2. Quarantine the modified binary by moving it to a protected analysis directory: `sudo mv /path/to/compromised_binary /private/var/quarantine/` — do not delete until forensic copy is preserved.
  3. Preserve a forensic image of the modified binary before remediation: `sudo cp -p /path/to/compromised_binary /forensics/$(date +%Y%m%d_%H%M%S)_$(basename /path/to/compromised_binary)` — capture SHA-256 hash for chain of custody.
  4. Restore the legitimate binary from a trusted source: re-run the originating package installer, restore from a verified backup, or re-download and verify against Apple's published checksums before replacing.
  5. Revoke and rotate any credentials or tokens that may have been accessed by processes spawned from the compromised binary — review process execution history to determine what the hijacked entry point may have accessed.
  6. If the endpoint is a build server or developer machine: freeze all software releases and deployments from that machine until full forensic investigation is complete, as supply chain contamination is a key risk of binary modification techniques.

Evidence Collection

  1. Full binary copy with preserved timestamps: `sudo cp -p <compromised_binary> /evidence/` — capture SHA-256, SHA-1, and MD5 hashes (`shasum -a 256 <file>`, `md5 <file>`).
  2. Mach-O header dump: `otool -l <binary> > /evidence/macho_headers.txt` — captures all load commands including LC_MAIN entryoff, LC_SEGMENT sections, and any injected segments.
  3. Section content extraction: `otool -s __TEXT __text <binary> > /evidence/text_section.txt` — the injected code will appear as an additional block before or after the legitimate code.
  4. macOS Unified Log entries: `log collect --last 24h --output /evidence/system.logarchive` — contains process execution records, code signing events (com.apple.security.codesigning), and Gatekeeper assessments.
  5. File system metadata: `ls -la@e <binary>` — extended attributes may reveal quarantine status or modification records; `xattr -l <binary>` captures all extended attribute values.
  6. Process execution history from Endpoint Security Framework or Jamf Protect telemetry: query for all processes spawned from the binary path in the 48 hours preceding discovery.
  7. Code signature audit: `codesign -dvvv --deep <binary> 2>&1 > /evidence/codesign_audit.txt` — captures signing identity, entitlements, and any signature validation failures.
  8. fsevents history via `fs_usage` or macOS FSEvent log: `sudo fs_usage -f filesys | grep <binary_name>` — reveals which processes opened or modified the binary.
  9. Memory forensics if process is still running: acquire process memory using `osxpmem` or `sudo gcore -o /evidence/process.core <pid>` — may reveal the injected payload in the code cave.

Escalation Criteria

  • ! Code signature verification fails on a previously Apple-signed or developer-signed binary — confirms unauthorized binary modification and bypasses Gatekeeper.
  • ! The modified binary is found in a system-protected path (/bin/, /usr/bin/, /usr/sbin/) that should be immutable under System Integrity Protection (SIP) — SIP bypass may have been employed, indicating a sophisticated threat actor.
  • ! The hijacked binary has been executed and spawned unexpected child processes (e.g., Safari.app spawning a shell, Finder spawning curl/python/osascript) — indicates the injected payload has already run.
  • ! Multiple endpoints show the same binary modification with matching injected section hashes — indicates automated deployment or supply chain compromise rather than isolated incident.
  • ! The modification timestamp predates the current detection by weeks or months — suggests long-term persistence and potential extensive dwell time requiring full incident response scope expansion.
  • ! The injected code section contains network connection indicators (IP literals, domain strings, socket syscall patterns) visible in `strings` output — indicates C2 or exfiltration capability built into the hijacked binary.

Investigation Guide

Forensic Artifacts

  • > Mach-O binary: LC_MAIN load command `entryoff` field — stores the file offset of the entry point; a value pointing outside the `__TEXT __text` section bounds indicates hijacking.
  • > Mach-O binary: unexpected `__TEXT` segments or sections with unusual names (e.g., `__malcode`, `__inject`, `__pad`) or high execute permissions relative to legitimate application layout.
  • > macOS Unified Log: `com.apple.security.codesigning` subsystem entries in `/var/log/` or via `log show` — records Gatekeeper and codesign assessment results including failures.
  • > File system: `com.apple.quarantine` extended attribute — modified binaries re-introduced from an external source may retain quarantine xattr; legitimately updated binaries via MAS or softwareupdate do not.
  • > File system: inode change time (`ctime`) vs modification time (`mtime`) — a discrepancy where `ctime` is newer than `mtime` may indicate metadata manipulation following binary patching.
  • > macOS FSEvent log: `/private/var/db/fsevents/` — kernel-level file system event journal records all writes to application bundle paths with timestamps and PIDs.
  • > Audit log: `/var/audit/` — BSM audit trails (if auditd is configured) record `open()`, `write()`, and `execve()` syscalls with full path resolution.
  • > Prefetch equivalent — macOS does not use Windows Prefetch, but Spotlight's `mdimport` database and LaunchServices registration in `/private/var/db/lsd/` record application execution history.
  • > dyld shared cache: `/private/var/db/dyld/` — legitimate system binaries are pre-linked into the shared cache; a binary executing outside the shared cache that claims to be a system tool warrants investigation.
  • > Crash reporter logs: `/Library/Logs/DiagnosticReports/` — if the injected entry point code contains bugs, crash reports will reveal the unexpected code path executing before the legitimate entry point.

Tuning Guidance

This is a deprecated MITRE ATT&CK technique with inherently low telemetry fidelity. Begin tuning by deploying in detection-only mode for 30 days to establish a baseline of legitimate Mach-O tool usage in your environment. The largest source of false positives is developer tooling: if your environment includes macOS developer workstations with Xcode, expect high volumes of otool and install_name_tool invocations. Scope the detection to exclude processes spawned by Xcode.app, CLtools, and known build system paths (e.g., /usr/local/bin/brew, DerivedData directories). For production macOS endpoints (non-developer machines), the threshold for investigation should be much lower — any otool invocation against /Applications/ system apps by non-IT user accounts warrants review. Enable macOS System Integrity Protection (SIP) enforcement reporting via MDM (Jamf or Mosyle) to receive alerts when SIP violations occur, as bypassing SIP is a prerequisite for modifying protected system binaries. File integrity monitoring with known-good SHA-256 hashes of critical system binaries (maintained via `shasum -a 256 /usr/bin/*` baseline at deployment) provides the most reliable detection for actual binary modification, superior to behavioral queries. Consider osquery's `hash` table for continuous binary hash monitoring against a known-good database.


Hunting Queries

Hunt for GUI applications spawning shell interpreters or scripting engines as children — a behavioral indicator that the application binary's entry point has been hijacked to execute a payload before returning to normal operation. Low spawn counts (rare occurrences) are prioritized over high counts to surface novel activity. Legitimate electron apps, Xcode, and Terminal are excluded.

Hunting — KQL
kql
// Hunt for processes spawning from known application paths whose children are unexpected shell or scripting interpreters
// This catches post-exploitation behavior from successfully hijacked binaries
let LegitAppPaths = dynamic(["/Applications/", "/System/Applications/"]);
let SuspiciousChildren = dynamic(["bash", "sh", "zsh", "python", "python3", "ruby", "perl", "osascript", "curl", "wget", "nc", "ncat"]);
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFolderPath has_any (LegitAppPaths)
| where FileName in~ (SuspiciousChildren)
| where not(InitiatingProcessFileName in~ ("Terminal", "iTerm2", "Xcode", "VSCode", "code", "electron"))
| summarize Count=count(), Devices=dcount(DeviceName), CommandLines=make_set(ProcessCommandLine, 10)
         by InitiatingProcessFileName, InitiatingProcessFolderPath, FileName
| where Count < 5
| sort by Count asc
Hunting — SPL
spl
index=endpoint (sourcetype="stream:process" OR sourcetype="macos:endpointsecurity")
(parent_process_path="/Applications/*" OR parent_process_path="/System/Applications/*")
(process_name="bash" OR process_name="sh" OR process_name="zsh" OR process_name="python3" OR process_name="python" OR process_name="ruby" OR process_name="perl" OR process_name="osascript" OR process_name="curl" OR process_name="nc")
NOT (parent_process_name="Terminal" OR parent_process_name="iTerm2" OR parent_process_name="Xcode" OR parent_process_name="Code" OR parent_process_name="Electron")
| stats count as SpawnCount, dc(host) as UniqueHosts, values(process_cmdline) as CommandLines by parent_process_name, parent_process_path, process_name
| where SpawnCount < 5
| sort SpawnCount

Hunt for bulk or automated use of Mach-O inspection tools targeting sensitive binary locations with load command flags. Legitimate developer use tends to target project build directories; high counts targeting /bin/, /usr/bin/, or /Applications/ system binaries from non-Xcode parents indicates reconnaissance or scripted modification activity.

Hunting — KQL
kql
// Hunt for otool/jtool/vtool invocations that inspect LC_MAIN or entry point fields
// across all devices in the past 7 days — establish baseline and identify outliers
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("otool", "jtool", "jtool2", "vtool", "nm", "objdump")
| extend InspectsEntryPoint = ProcessCommandLine has_any ("LC_MAIN", "LC_THREAD", "LC_UNIXTHREAD", "entryoff", "-l", "--load-commands")
| extend TargetsSensitiveBinary = ProcessCommandLine has_any ("/Applications/", "/usr/bin/", "/bin/", "/usr/sbin/", "/sbin/")
| where InspectsEntryPoint and TargetsSensitiveBinary
| summarize Count=count(), UniqueBinaries=dcount(ProcessCommandLine),
           FirstSeen=min(Timestamp), LastSeen=max(Timestamp)
         by DeviceName, AccountName, InitiatingProcessFileName
| where Count > 10
| sort by Count desc
Hunting — SPL
spl
index=endpoint (sourcetype="stream:process" OR sourcetype="macos:endpointsecurity")
(process_name="otool" OR process_name="jtool" OR process_name="jtool2" OR process_name="vtool" OR process_name="nm" OR process_name="objdump")
| eval CmdLine=coalesce(process_cmdline, cmd_line)
| eval InspectsEntryPoint=if(match(CmdLine, "(LC_MAIN|LC_THREAD|LC_UNIXTHREAD|entryoff|-l\b|--load-commands)"), 1, 0)
| eval TargetsSensitiveBinary=if(match(CmdLine, "(/Applications/|/usr/bin/|/bin/|/usr/sbin/|/sbin/)"), 1, 0)
| where InspectsEntryPoint=1 AND TargetsSensitiveBinary=1
| stats count as InvocationCount, dc(CmdLine) as UniqueBinaries, earliest(_time) as FirstSeen, latest(_time) as LastSeen by host, user, parent_process_name
| where InvocationCount > 10
| sort - InvocationCount

Hunt for file write operations directly to the MacOS/ directory inside .app bundles — the standard location for the primary Mach-O executable. Writes by non-system updater processes to this path are the direct precursor to an LC_MAIN hijacking deployment. Installer, softwareupdate, and App Store download agents are excluded as known-good writers.

Hunting — KQL
kql
// Hunt for file writes to Mach-O executable locations within .app bundles by non-system processes
// Focuses on the MacOS/ subdirectory where the primary executable lives
DeviceFileEvents
| where Timestamp > ago(7d)
| where ActionType in ("FileModified", "FileCreated", "FileRenamed")
| where FolderPath matches regex @"/Applications/[^/]+\.app/Contents/MacOS"
| where not(InitiatingProcessFileName in~ (
    "Installer", "install", "softwareupdate", "MRT", "XProtect",
    "trustd", "syspolicyd", "pkgutil", "pkgd", "storedownloadd",
    "com.apple.MobileAsset", "nsurlsessiond"))
| project Timestamp, DeviceName, AccountName, FileName, FolderPath,
         InitiatingProcessFileName, InitiatingProcessCommandLine
| sort by Timestamp desc
Hunting — SPL
spl
index=endpoint (sourcetype="stream:file" OR sourcetype="macos:endpointsecurity" OR sourcetype="osquery:results" name="pack_*_file_events")
(action="write" OR action="create" OR action="rename" OR event_type="write" OR event_type="create")
(path="/Applications/*.app/Contents/MacOS/*" OR columns.path="/Applications/*.app/Contents/MacOS/*")
NOT (process_name="Installer" OR process_name="softwareupdate" OR process_name="MRT" OR process_name="pkgd" OR process_name="storedownloadd" OR process_name="nsurlsessiond" OR process_name="trustd")
| eval FilePath=coalesce(path, 'columns.path', file_path)
| eval WritingProcess=coalesce(process_name, 'columns.process_name')
| table _time, host, user, FilePath, WritingProcess, process_cmdline
| sort - _time

Atomic Red Team Tests

Test 1 Inspect LC_MAIN Entry Point of a System Binary
macos

Uses otool to inspect the LC_MAIN load command of a macOS system binary, enumerating the entry point offset. This simulates the reconnaissance phase of LC_MAIN hijacking where an adversary identifies the current entryoff value before patching. The command targets /bin/ls as a representative Mach-O binary available on all macOS systems.

Command

bash
otool -l /bin/ls | grep -A5 LC_MAIN

Expected Telemetry

macOS Unified Log / ESF process event: process_name=otool, cmdline='otool -l /bin/ls', parent=bash/zsh. osquery process_open_files will show /bin/ls opened for reading by otool. No file modification events are generated by this read-only operation.

Expected Detection

KQL query matches: FileName=otool, ProcessCommandLine contains '-l' and '/bin/'. SPL query matches: InspectsEntryPoint=1, TargetsSensitiveBinary=1. Hunting query 2 increments InvocationCount for this user/host pair.

Test 2 Enumerate All Load Commands of a Sensitive Application Binary
macos

Uses otool with the -l flag to dump all Mach-O load commands from Safari's main executable, including LC_MAIN entryoff and all LC_SEGMENT sections. This simulates adversary enumeration of a high-value target binary to identify injection opportunities (unused sections, padding caves) before entry point modification.

Command

bash
otool -l /Applications/Safari.app/Contents/MacOS/Safari 2>/dev/null | grep -E '(LC_MAIN|LC_SEGMENT|cmd |entryoff|stacksize|segname|vmaddr|fileoff)' | head -60

Expected Telemetry

ESF/stream:process event: process_name=otool, cmdline targeting /Applications/Safari.app/Contents/MacOS/Safari with -l flag. macOS FSEvent: Safari binary opened for reading with otool PID. DeviceProcessEvents (MDE): FileName=otool, ProcessCommandLine contains '-l' and '/Applications/Safari.app/Contents/MacOS/Safari'.

Expected Detection

Primary KQL detection fires: MachOInspectionTools match on 'otool', SuspiciousLoadCmdFlags match on '-l', SensitivePaths match on '/Applications/'. SPL branch 2 fires: HasLoadCmdFlag=1, TargetsSensitivePath=1. Hunting query 1 checks for subsequent Safari child processes.

Test 3 Verify Code Signature Validity of a Modified Binary
macos

Uses codesign to verify the signature of a binary after simulated modification. An adversary would run this check to determine whether their patched binary passes Gatekeeper validation. A successful code signature verification means the modification was undetected; a failure reveals that re-signing is required. This command is also used by defenders to detect tampered binaries.

Command

bash
codesign -v --deep --strict /bin/ls 2>&1 && echo 'SIGNATURE VALID' || echo 'SIGNATURE INVALID - BINARY MAY BE TAMPERED'

Expected Telemetry

ESF process event: process_name=codesign, cmdline contains '-v --deep --strict /bin/ls'. macOS Unified Log subsystem com.apple.security.codesigning records the verification result with target binary path and signing identity. If a binary were actually modified, this command would produce a 'code object is not signed at all' or 'a sealed resource is missing or invalid' error.

Expected Detection

codesign is not directly targeted by the primary KQL/SPL queries but appears in hunting query 2 if run repeatedly. More importantly: if a binary is actually modified, `codesign -v` failure generates com.apple.security.codesigning Unified Log entries that MDE and osquery can surface as code signing policy violations.

Test 4 Simulate Code Cave Discovery Using nm and size
macos

Uses the nm utility to list symbols and the size command to display segment sizes for a Mach-O binary, simulating the process an adversary uses to find unused space (code caves) suitable for injecting malicious shellcode before patching LC_MAIN to redirect execution there. The __text section's alignment padding is a common injection target.

Command

bash
nm -arch x86_64 /usr/bin/true 2>/dev/null | head -20 && echo '---SEGMENT SIZES---' && size /usr/bin/true

Expected Telemetry

ESF process events for nm and size with respective command lines targeting /usr/bin/true. Both binaries are in /usr/bin/ (a monitored sensitive path). DeviceProcessEvents: FileName in ('nm', 'size'), ProcessCommandLine contains '/usr/bin/true'. These events fire consecutively and may indicate scripted reconnaissance.

Expected Detection

size is not in the primary MachOInspectionTools list but nm is included. The hunting query 2 SPL branch matches on nm with TargetsSensitiveBinary=1 for the /usr/bin/ path. Defenders can expand the MachOInspectionTools dynamic array to include 'nm', 'size', 'strings', and 'hexdump' for broader coverage.

Test 5 Write a Test File to an App Bundle MacOS Directory (Simulated Binary Drop)
macos

Simulates the file-write phase of an LC_MAIN hijacking attack by writing a test file to the Contents/MacOS/ directory of a user-installed application bundle. In a real attack, this step would replace or patch the legitimate Mach-O executable. This test uses a benign text file and a non-system app path to avoid affecting system integrity. Requires a test app bundle to exist at the target path.

Command

bash
mkdir -p /tmp/TestApp.app/Contents/MacOS && cp /bin/echo /tmp/TestApp.app/Contents/MacOS/TestApp && echo 'INJECTED_MARKER' > /tmp/TestApp.app/Contents/MacOS/TestApp.bak && ls -la /tmp/TestApp.app/Contents/MacOS/

Cleanup

bash
rm -rf /tmp/TestApp.app

Expected Telemetry

ESF/stream:file events: FileCreated for /tmp/TestApp.app/Contents/MacOS/TestApp and /tmp/TestApp.app/Contents/MacOS/TestApp.bak. DeviceFileEvents: ActionType=FileCreated, FolderPath contains '/MacOS/', InitiatingProcessFileName=bash/zsh. The /tmp/ path is not in the monitored sensitive paths by default — adjust the FolderPath filter to include /tmp/*.app/Contents/MacOS/ for this test to trigger the hunting query.

Expected Detection

Hunting query 3 (KQL/SPL file write to MacOS/ directories) would fire if the FolderPath filter included /tmp/. For production deployment, the filter targets /Applications/ — adjust the atomic test to use a non-system app installed under ~/Applications/ or adjust the hunting query path filter during validation.

Related Detections