When an iOS app is installed from the App Store, its Mach-O binary is encrypted with FairPlay DRM, Apple's protection against reverse engineering. During execution, the iOS kernel decrypts this binary directly in memory.
Apps downloaded from app store are encrypted, extract a decrypted IPA from a jailbroken device
https://github.com/AloneMonkey/frida-ios-dump
-> List apps installed on the device
python dump.py -l
-> Before running frida-ios-dump, edit your SSH server information in dump.py
python dump.py "<bundle_identifier>"
When carrying out this process on Windows, the error "PermissionError: [WinError 5] Access is denied" may occur, because there are code snippets in dump.py that use Linux commands, which are not recognized. Therefore, to solve the problem it is necessary to install gow, which is a program that allows the use of Linux commands in a cmd environment. If this failure has already occurred, you will probably have a temporary record in the 'Payload' folder, which must be deleted to make a new attempt after installing gow. Path to remove the payload folder containing the temporary files:
C:\Users{username}\AppData\Local\Temp
-> Install gow
https://github.com/bmatzelle/gow/releases
Static Reverse Engineering iOS consists of analyzing the binary of iOS apps without executing them, aiming to understand their structure, identify protections, logical flows, sensitive calls and other internal behaviors.
On iOS, apps can be developed in a variety of languages, including Objective-C and Swift, and are compiled directly into machine code specific to the ARM64 architecture used in the processors of modern iOS devices. When disassembling an .ipa file, the resulting code is usually displayed in assembly language corresponding to the ARM64 architecture of the iOS processor. This assembly language represents, at a low level, the fundamental instructions executed directly by the processor.
On iOS, the disassembly process is typically performed using tools such as Ghidra, Radare2, Hopper Disassembler, IDA Pro, which convert machine code to assembly language and provide a decompiled view similar to C code for easier analysis.
-> Ghidra
https://github.com/NationalSecurityAgency/ghidra
-> Radare2
https://github.com/radareorg/radare2
-> Hopper Disassembler
https://www.hopperapp.com/
-> IDA Pro
https://hex-rays.com/ida-pro/
-> Understanding the basic concepts of the ARM64 architecture, including its registers and instructions, as well as the assembly language used to program in this architecture, will help you both understand more of the possible client-side protections, as well as bypass them through static reverse engineering with the tools above.
https://developer.arm.com/documentation/dui0802/b/A64-General-Instructions/
https://github.com/nygard/class-dump
-> Class-dump is a command-line tool that parses the Objective-C segment of Mach-O files, generating declarations for classes, categories, and protocols.
Mach-O files are used on Mach kernel-based systems such as macOS and iOS. They contain information needed to run programs, including executable code, data, and linking information. .ipa files, used to distribute iOS applications, are zip files that contain application components, including the executable binary, which is a Mach-O file. The Mach-O binary is the core of the application, responsible for running the software when launched on an iOS device.
-> Rename application .ipa to .zip
-> Extract the .zip
-> Get the binary file inside the folders with the application name.
./class-dump <ipa_binary>
When working with applications written in Swift, the names of symbols (such as function names, variables and types) are "mangled" (or obfuscated) to include information about their types and context. The swift-demangle tool can be used to convert these obfuscated names back into a readable format.
Extract the Swift binary from the .ipa application as described above. Use the swift-demangle tool to convert the symbol names.
Imagine we have a function in Swift with the following nomenclature.
_$s10Foundation10URLRequestV19_bridgeToObjectiveCSo12NSURLRequestCyF
Using the swift-demangle tool, we can obtain the demangled form of this function.
Foundation.URLRequest._bridgeToObjectiveC() -> __C.NSURLRequest
A common example of use would be to combine the nm binary with the swift-demangle, but you can also dump the symbols in another way and pass them to the swift-demangle to get the same result.
nm <binary> | xcrun swift-demangle
- An important point is that when we are dealing with Swift applications, several tools mentioned above support swift-demangle to be able to better analyze symbols, just have Swift installed on your machine and configure them.
Dynamic reverse engineering in iOS consists of analyzing the behavior of an application while it is running, allowing inspection and manipulation of memory, function calls and execution flow in real time. This approach is essential for identifying protection mechanisms, extracting sensitive data, mapping internal logic and evaluating the security of iOS applications.
Main tools used:
-
Frida: Dynamic instrumentation framework that allows you to inject JavaScript code into native processes. It is widely used for function hooking, parsing native API calls (Objective-C, Swift, C/C++), reading/writing memory, and manipulating control flows in real time.
Info+: Hooking with Frida and Objection on iOS -
Objection: Instrumentation tool based on Frida, provides ready-made commands to bypass protections, extract data from Keychain, enumerate classes/methods, inspect internal sandbox files, etc. Info+: Hooking with Frida and Objection on iOS
-
LLDB: Low-level debugger used with Xcode and iOS Debug Servers, ideal for detailed analysis of apps with or without symbols. Allows you to set breakpoints, track method calls, access ARM64 registers, inspect data structures and modify instructions at run time.
-> Capture logs from your app
idevicesyslog -m <app_name>
-> List all exported symbols that contain _$s (Swift mangled names): enum-swift-symbols.js
Process.enumerateModules().forEach(m => {
try {
m.enumerateSymbols().forEach(sym => {
if (sym.name.includes('_$s'))
console.log(m.name, sym.name, sym.address);
});
} catch (e) {
console.warn(`Error enumerating module symbols ${m.name}: ${e.message}`);
}
});
frida -U -f <bundle_identifier> -l enum-swift-symbols.js
-> List all loaded Objective-C classes: enum-objc-classes.js
try {
if (ObjC.available) {
for (var className in ObjC.classes) {
console.log(className);
}
} else {
console.log("Objective-C runtime not available in this process.");
}
} catch (e) {
console.warn(`Error listing Objective-C classes: ${e.message}`);
}
frida -U -f <bundle_identifier> -l enum-objc-classes.js
-> listing classes - objection
objection --gadget <bundle_identifier> explore -s "ios hooking list classes"
-> list methods of a class - objection
objection --gadget <bundle_identifier> explore -s "ios hooking list class_methods <class_name>"
frida-trace is a tool for dynamic analysis, enabling you to hook native functions and high-level method calls at runtime. It’s ideal for tracking control flow, uncovering sensitive operations (like login or encryption), and bypassing app protections. It generates editable JavaScript handler files (__handlers__) for each traced function, allowing you to customize behavior, log arguments, modify returns, or bypass checks.
-> You can track functions by name pattern, for example, search for a term referring to a possible Jailbreak detection:
frida-trace -U -f <bundle_identifier> -i "*Jail*/i"
-> Trace Objective-C methods where the class or selector contains "Jail":
frida-trace -U -f <bundle_identifier> -m "*[Jail* *]"
or target a specific function, for example:
frida-trace -U -f <bundle_identifier> -i '<function>'
https://github.com/interference-security/frida-scripts/tree/master/iOS