{
	"id": "ecc6ea40-d22b-4731-8336-dc106f0f7a92",
	"created_at": "2026-04-10T03:19:59.483058Z",
	"updated_at": "2026-04-10T03:22:17.700518Z",
	"deleted_at": null,
	"sha1_hash": "f1062f3df4f65b5d0635d851a35507d77d53a9f2",
	"title": "Predator’s kill switch: undocumented anti-analysis techniques in iOS spyware",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 2641044,
	"plain_text": "Predator’s kill switch: undocumented anti-analysis techniques in\r\niOS spyware\r\nBy Jamf Threat Labs\r\nArchived: 2026-04-10 03:15:51 UTC\r\nA deep dive into the error code taxonomy and detection mechanisms that prior research didn't cover.\r\nJanuary 14 2026 by\r\nJamf Threat Labs\r\nBy: Shen Yuan and Nir Avraham\r\nIntroduction\r\nIn December 2025, Google's Threat Intelligence Group (GTIG) published extensive research on Intellexa's\r\nPredator spyware, documenting its zero-day exploit chains and the PREYHUNTER stager component. Their\r\nresearch identified that the \"watcher\" module detects developer mode, jailbreak tools, security applications and\r\nnetwork interception configurations.\r\nHowever, while conducting independent reverse engineering of a Predator sample, Jamf Threat Labs discovered\r\nseveral undocumented mechanisms that reveal how sophisticated this spyware's anti-analysis capabilities truly are.\r\nThis post presents original findings including:\r\nA complete error code taxonomy (301-311) that enables operators to diagnose exactly why an implant\r\nfailed\r\nImplementation details for each detection method that go beyond high-level descriptions\r\nAn undocumented crash reporter monitoring system for anti-forensics\r\nSpringBoard hooking to hide recording indicators from victims\r\nKernel exploitation class names that reveal the internal architecture\r\nThese findings demonstrate that Predator's operators have granular visibility into failed deployments — a\r\ncapability that has significant implications for researchers attempting to analyze these samples.\r\nThe CSWatcherSpawner architecture\r\nThe implant contains a C++ class named CSWatcherSpawner::CSWatcherSpawner that orchestrates all anti-analysis checks. The class implements a comprehensive set of detection methods with a sophisticated reporting\r\nmechanism.\r\nWhat makes this architecture notable is not just the breadth of checks, but the reporting mechanism that provides\r\noperators with precise diagnostic information when deployment fails.\r\nhttps://www.jamf.com/blog/predator-spyware-anti-analysis-techniques-ios-error-codes-detection/\r\nPage 1 of 15\n\nFigure 1: check_perform() entry point showing getCountNames() check and error code 311 dispatch for multiple\r\ninstance detection\r\nThe error code taxonomy\r\nThe most significant undocumented finding is Predator's error code system. When any anti-analysis check\r\ntriggers, the malware doesn't simply terminate — it reports a specific error code to its command-and-control\r\ninfrastructure before cleaning up and exiting.\r\nThe check_perform() function reveals the complete taxonomy:\r\nTable 1: Error codes and their conditions\r\nThe missing error codes: 302, 303, 305, 306\r\nA careful examination of the binary's string section reveals an interesting gap in the error code taxonomy. The\r\nerror codes stored in the __cstring section appear sequentially, but with notable gaps:\r\nhttps://www.jamf.com/blog/predator-spyware-anti-analysis-techniques-ios-error-codes-detection/\r\nPage 2 of 15\n\nFigure 2: Error code strings in __cstring section — note the sequential addresses but non-sequential codes (301,\r\n304, 307, 308) revealing missing 302, 303, 305, 306\r\nNotably, error codes 302, 303, 305, and 306 are completely absent from this sample. These gaps in the\r\nnumbering scheme suggest several possibilities:\r\nReserved codes: Placeholder codes for future functionality or checks not yet implemented\r\nVariant-specific: These codes may be used in other Predator variants or versions targeting different\r\nplatforms\r\nDeprecated checks: Detection methods that were removed but whose error codes were preserved in the\r\ntaxonomy\r\nShared taxonomy: The error code system shared across multiple Intellexa products, with different\r\nproducts implementing different subsets\r\nThe non-sequential numbering (jumping from 301 to 304, and from 304 to 307) indicates this error taxonomy has\r\nevolved over time or is designed to accommodate checks that may be conditionally compiled based on the target\r\nconfiguration.\r\nWhy this matters\r\nThis error code system transforms failed deployments from black boxes into diagnostic events. When an operator\r\ndeploys Predator against a target and receives error code 304, they know the target is running security tools — not\r\nthat the exploit failed, not that the device is incompatible, but specifically that active analysis is occurring.\r\nThis has direct implications for targeted individuals: if security analysis tools like Frida are running, Predator will\r\nabort deployment and report error code 304 to operators, who can then troubleshoot why their deployment failed.\r\nDetection implementation details\r\nWhile Google's research mentioned that Predator detects \"custom HTTP proxies\" and \"custom root CAs,\" the\r\nactual implementation details were not published. Here's what the binary reveals:\r\nMultiple instance detection (error 311)\r\nThe first check in check_perform() calls getCountNames() to detect if multiple Predator instances are running.\r\nThis prevents researchers from running multiple analysis instances simultaneously:\r\nhttps://www.jamf.com/blog/predator-spyware-anti-analysis-techniques-ios-error-codes-detection/\r\nPage 3 of 15\n\nFigure 3: getCountNames() pseudocode — enumerates all processes via sysctl, uses proc_pidpath to get\r\nexecutable paths, counts processes running from /private/var/tmp/\r\nThe function iterates through all running processes using sysctl , retrieves each process's executable path via\r\nproc_pidpath() , and counts how many are running from /private/var/tmp/ (Predator's staging directory). If\r\nthe count is two or more, error code 311 is reported.\r\nDeveloper mode detection: targeting researchers\r\nBuilding on Google's finding that Predator detects Developer Mode, we reverse engineered how this check works.\r\nThe is_developer() function specifically targets iOS Developer Mode using the sysctlbyname API:\r\nhttps://www.jamf.com/blog/predator-spyware-anti-analysis-techniques-ios-error-codes-detection/\r\nPage 4 of 15\n\nFigure 4: Developer Mode detection implementation showing\r\nsysctlbyname(\"security.mac.amfi.developer_mode_status\") call\r\nThis is significant because Developer Mode was introduced in iOS 16 specifically for security researchers and\r\ndevelopers. By detecting this, Predator effectively says: \"If you've enabled developer features, you're probably not\r\na normal target.\"\r\nJailbreak detection: is_not_phone()\r\nThe is_not_phone() function checks for the presence of jailbreak-related files and directories:\r\nhttps://www.jamf.com/blog/predator-spyware-anti-analysis-techniques-ios-error-codes-detection/\r\nPage 5 of 15\n\nFigure 5: is_not_phone() graph view — cascading file existence checks for jailbreak indicators\r\nThe function checks for the following paths using the access() syscall:\r\n/bin/bash — Shell binary (not present on stock iOS)\r\nhttps://www.jamf.com/blog/predator-spyware-anti-analysis-techniques-ios-error-codes-detection/\r\nPage 6 of 15\n\n/private/var/tmp/cydia.log — Cydia package manager log\r\n/Applications/Cydia.app — Cydia application\r\n/private/var/lib/apt/ — APT package manager directory\r\n/private/var/lib/cydia — Cydia data directory\r\n/etc/apt — APT configuration\r\n/private/var/stash — Jailbreak stash directory\r\nAdditionally, it checks if /bin/ contains more than two entries (stock iOS has minimal binaries in /bin).\r\nGeographic restrictions: avoiding US and Israeli jurisdiction\r\nGoogle's research identified that Predator checks for US and Israeli locales. Our analysis documented the specific\r\nimplementation of the geographic check (error code 309), which deserves further attention. Predator refuses to\r\nexecute on devices with US or Israeli locale settings:\r\nFigure 6: Geographic restriction check using NSLocale — comparing country code against \"US\" and \"IL\",\r\ntriggering error 309\r\nEnlarge image\r\nWhile we cannot confirm, this is likely a deliberate decision to avoid legal exposure in jurisdictions with strong\r\ncybercrime laws and active law enforcement cooperation with Intellexa's European base.\r\nConsole detection: timing-based analysis\r\nThe is_console_attached() function uses a clever timing-based approach:\r\nFigure 7: Debug console detection showing is_console_attached() call and error code 310 dispatch\r\nhttps://www.jamf.com/blog/predator-spyware-anti-analysis-techniques-ios-error-codes-detection/\r\nPage 7 of 15\n\nEnlarge image\r\nThis detection compares the modification time of the diagnosticd filter file against the system boot time. If\r\nsomeone enabled console logging after the device booted (as a researcher would do), the file's mtime will be\r\nnewer than the boot time.\r\nSecurity tool detection: the complete list\r\nGoogle's research mentioned \"Bash, tcpdump, frida, sshd, or checkrain\" but the actual list in the binary is longer.\r\nThe is_unsafe_running() function reveals the complete process detection list:\r\nhttps://www.jamf.com/blog/predator-spyware-anti-analysis-techniques-ios-error-codes-detection/\r\nPage 8 of 15\n\nFigure 8: is_unsafe_running() assembly — complete process detection list including tcpdump, frida-server,\r\nnetstat, sshd, checkra1nd, loader, McAfee, AvastMobileSecurity, NortonMobileSecurity\r\nEnlarge image\r\nThe complete list of detected processes also includes:\r\nhttps://www.jamf.com/blog/predator-spyware-anti-analysis-techniques-ios-error-codes-detection/\r\nPage 9 of 15\n\ntcpdump — network packet capture\r\nfrida-server — dynamic instrumentation framework\r\nnetstat — network statistics\r\nsshd — SSH daemon\r\ncheckra1nd — checkra1n jailbreak\r\nloader — generic loader process\r\nMcAfee — McAfee mobile security\r\nAvastMobileSecurity — Avast mobile security\r\nNortonMobileSecurity — Norton mobile security\r\nThe inclusion of netstat is noteworthy — it suggests Predator is concerned about targets who might be\r\nmonitoring their own network connections, not just researchers with specialized tools. A privacy-conscious user\r\nsimply checking their network connections would trigger this detection.\r\nSelf-cleanup after error reporting and shutdown\r\nAfter reporting an error code to C2, Predator performs self-cleanup by removing its staging directory:\r\nFigure 9: Self-cleanup code calling _remove on /private/var/tmp/ staging directory after error reporting\r\nThis cleanup happens after the C2 callback, ensuring operators receive the diagnostic information even if the\r\nmalware is immediately removed.\r\nAnother cleanup mechanism is tied to the device shutdown lifecycle, the implant registers a Darwin notification\r\nobserver for “com.apple.springboard.deviceWillShutDown”.\r\nFigure 10: Code to register listener for shutdown signal\r\nhttps://www.jamf.com/blog/predator-spyware-anti-analysis-techniques-ios-error-codes-detection/\r\nPage 10 of 15\n\nWhen the shutdown notification is received, it transits to a cleanup routine removes specific on-disk evidences if\r\nthey exist.\r\nFigure 11: Self-cleanup code on shutdown process\r\nUndocumented anti-forensics: crash reporter monitoring\r\nOne capability not mentioned in any public research is the monitoringCrashReporter() function:\r\nhttps://www.jamf.com/blog/predator-spyware-anti-analysis-techniques-ios-error-codes-detection/\r\nPage 11 of 15\n\nFigure 12: monitoringCrashReporter() — kqueue-based monitoring of\r\n/private/var/mobile/Library/Logs/CrashReporter/\r\nThis function uses kqueue to monitor the CrashReporter directory for any new files. When a crash occurs that\r\nmight expose Predator's presence, the handler processes or removes the crash log before it can be synced or\r\nexamined.\r\nMemory forensics suppression: killing mmaintenanced\r\nThe crash handler specifically targets memory forensics evidence:\r\nFigure 13: crash_reporter_block_handler — kills mmaintenanced when SystemMemory crash reports are detected\r\nEnlarge image\r\nWhen a crash report filename contains \"SystemMemory,\" the handler immediately locates and kills the\r\nmmaintenanced process (memory maintenance daemon) using SIGKILL (signal 9). This prevents investigators\r\nfrom capturing memory dumps.\r\nThis is significant for forensics: crash logs are a valuable artifact for detecting exploitation attempts, and\r\nPredator actively suppresses them.\r\nRecording indicator hiding\r\nThe TestHooker() function reveals how Predator hides the iOS recording indicator from victims:\r\nFigure 14: UMHooker using Mach exception handling for cross-process hooking into SpringBoard\r\nhttps://www.jamf.com/blog/predator-spyware-anti-analysis-techniques-ios-error-codes-detection/\r\nPage 12 of 15\n\nEnlarge image\r\nThis code locates the SpringBoard process, uses kernel exploitation primitives to inject into SpringBoard, and\r\nhooks SBRecordingIndicatorManager methods to suppress the recording indicator. When Predator activates the\r\nmicrophone or camera, victims won't see the orange/green indicator dot.\r\nKernel exploitation class names\r\nThe code reveals several internal class names that indicate the exploitation architecture:\r\nTable 2: Class names and their purposes\r\nThe NSTaskROP::WithoutDeveloperMode template is particularly interesting — it suggests Intellexa has\r\ndeveloped ROP techniques that work even when developer mode is disabled, which is the default state for most\r\nusers.\r\nStubbed functionality: is_corellium()\r\nAn interesting artifact is the is_corellium() function at address 0x100005bb8 :\r\nCorellium is a cloud-based iOS device virtualization platform used by security researchers. The presence of this\r\nstubbed function suggests that Intellexa is aware of Corellium as an analysis platform; detection may have been\r\nimplemented and later disabled, or detection is planned for future versions.\r\nThis function is not called from check_perform() in this sample, but its presence indicates awareness of the\r\nresearch community's tools.\r\nIndicators of compromise\r\nFile access patterns\r\n/private/var/preferences/SystemConfiguration/preferences.plist (proxy check)\r\n/private/var/preferences/Logging/com.apple.diagnosticd.filter.plist (console check)\r\n/private/var/protected/trustd/private/TrustStore.sqlite3 (root CA check)\r\n/private/var/mobile/Library/Logs/CrashReporter/ (crash monitoring)\r\nJailbreak detection paths\r\nProcess detection targets\r\nSQL queries (TrustStore)\r\nConclusion\r\nThis analysis reveals that Predator's anti-analysis capabilities are more sophisticated than previously documented.\r\nThe error code taxonomy demonstrates that Intellexa operators have granular visibility into why deployments fail,\r\nenabling them to adapt their approaches for specific targets.\r\nhttps://www.jamf.com/blog/predator-spyware-anti-analysis-techniques-ios-error-codes-detection/\r\nPage 13 of 15\n\nFor researchers, these findings highlight the importance of:\r\nAir-gapped analysis environments: The C2 callback means any network-connected analysis will alert\r\noperators.\r\nCrash log preservation: Enable crash log collection before analysis begins.\r\nProcess name awareness: Even running netstat triggers detection.\r\nBoot-time considerations: Console logging configured before boot may evade timing-based detection.\r\nFor the broader security community, this analysis demonstrates that commercial spyware vendors invest\r\nsignificant engineering effort into detecting researchers — not just evading security products. The presence of the\r\nis_corellium() stub shows they're watching our tools as closely as we're watching theirs.\r\nAppendix: function reference\r\nTable 3: List of functions and associated address and descriptions\r\nDive into more Jamf Threat Labs research on our blog.\r\nhttps://www.jamf.com/blog/predator-spyware-anti-analysis-techniques-ios-error-codes-detection/\r\nPage 14 of 15\n\nTags:\r\nSource: https://www.jamf.com/blog/predator-spyware-anti-analysis-techniques-ios-error-codes-detection/\r\nhttps://www.jamf.com/blog/predator-spyware-anti-analysis-techniques-ios-error-codes-detection/\r\nPage 15 of 15",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://www.jamf.com/blog/predator-spyware-anti-analysis-techniques-ios-error-codes-detection/"
	],
	"report_names": [
		"predator-spyware-anti-analysis-techniques-ios-error-codes-detection"
	],
	"threat_actors": [],
	"ts_created_at": 1775791199,
	"ts_updated_at": 1775791337,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/f1062f3df4f65b5d0635d851a35507d77d53a9f2.pdf",
		"text": "https://archive.orkl.eu/f1062f3df4f65b5d0635d851a35507d77d53a9f2.txt",
		"img": "https://archive.orkl.eu/f1062f3df4f65b5d0635d851a35507d77d53a9f2.jpg"
	}
}