An Exploration of One of the Methods for Bypassing Jailbreak Detection
In the name of Allah, the Most Gracious, the Most Merciful.
Note: This article has also been published on the company blog where I am employed. Please visit the post for more details: https://medium.com/haktrak-cybersecurity-squad/bypassing-ios-jailbreak-detection-by-patching-the-binary-with-ghidra-write-up-of-no-escape-lab-02b417b3a235
This write-up will outline two distinct approaches:
- For readers primarily interested in the main focus of this article (InshaAllah, it can save significant time for readers already familiar with the basics), please proceed directly to Chapter 4.4, Section “Analyzing the Binary in Ghidra”.
- For those seeking to understand foundational concepts — such as types of jailbreaks, some jailbreak detection methods, or a few environment setups to conduct the test. InshaAllah, this will provide some insights and enrich the reader’s understanding.
I. Introduction
In the past decade, technology has become deeply integrated into our daily lives. One of the most notable changes has been the rise of smartphones, which have become essential to nearly every aspect of modern society. Along with the growth of applications in general, this shift towards mobile-first experiences has certainly created new opportunities for businesses, but on the other hand, it has also opened the door to a greater number of cyberattacks. As mobile phones increasingly store sensitive personal data, the risk of security breaches has grown significantly. This is where mobile application security testing becomes important, especially for both iOS and Android applications, the two dominant platforms in the market today.
iOS, as one of the most popular mobile operating systems today, is supported by a strong security framework due to Apple’s closed ecosystem. This tightly controlled system limits access to key components of the device, providing an added layer of protection against potential threats. However, while iOS devices benefit from robust security controls, iOS applications themselves are not immune to vulnerabilities. Applications, especially those handling sensitive user data, remain prime targets, highlighting the need for additional layers of protection. These measures include encrypting sensitive data stored within the app, testing for local authentication, evaluating API vulnerabilities (such as URL schemes, WebViews, etc), and safeguarding against manipulation, one of which is the implementation of jailbreak detection mechanisms.
Regarding jailbreak detection, this protection often becomes a barrier for penetration testers who need to review the app’s flow — such as studying the connections made to the host, methods of data storage, and other critical interactions. In light of this, this blog will explore one method to bypass jailbreak detection by patching the application’s binary using Ghidra, with a case study based on the Lab No-Escape iOS application from MobileHackingLab, one of the more challenging examples available there.
II. A Basic Overview of Jailbreak
2.1. What is Jailbreak and Why is it Needed?
Jailbreaking refers to the process of removing the restrictions imposed by Apple on iOS devices, such as iPhones, iPads, and even iPods (although these devices are no longer being released). As mentioned earlier, by default, iOS is a closed system, where Apple tightly controls what can be installed on the device, how the system functions, and what can be accessed by third-party applications. Jailbreaking essentially overrides these restrictions, providing greater flexibility for any kind of user.
It is important to note that jailbreaking an iOS device does not alter its hardware in any way.
Now, the question might arise: why do we need jailbreaking? There are various perspectives on this. For common users, jailbreaking provides the freedom to install apps and features that are not available on the Apple App Store, such as customization options or system tweaks. And in the context of cybersecurity, jailbreaking is important for activities such as penetration testing and vulnerability research, as it provides access to areas of the system that are typically locked down. It also enables runtime manipulation, which is essential for identifying and exploiting security vulnerabilities.
2.2. Types of Jailbreak
In this section, I won’t delve into the specifics of performing a jailbreak, but for more detailed information, you can refer to the guide available at https://theapplewiki.com/wiki/Jailbreak.
Technically, jailbreaking methods vary depending on how they exploit iOS vulnerabilities and the level of system access they provide. Broadly, jailbreaking can be classified into four categories. Here is a summary based on information from AppleWiki and a few additional resources:
2.2.1. Tethered Jailbreak
A tethered jailbreak requires the device to be connected to a computer every time it is rebooted. Without this connection, the device won’t boot at all.
2.2.2. Untethered Jailbreak
Untethered jailbreaks were once the standard method for jailbreaking iOS devices — after running the jailbreak once, the device remained jailbroken permanently. These could be installed either through Safari using tools like JailbreakMe, or with a computer using programs such as redsn0w, Absinthe, or Pangu. However, this type of jailbreak began to fade away starting with iOS 9.
2.2.3. Semi-Tethered Jailbreak
A semi-tethered jailbreak allows the device to boot without a computer, but after a reboot, it will revert to an unjailbroken state. To restore the jailbreak and its associated tweaks, the device must be connected to a computer and a specific app must be run to re-enable the jailbreak.
2.2.4. Semi-Untethered Jailbreak
A semi-tethered jailbreak allows the device to function normally without a computer, but if the device is rebooted, it will lose the jailbreak state. However, unlike tethered jailbreaking, it does not require a connection to a computer to restore functionality — users can simply re-enable the jailbreak just by launching a jailbreak app that reactivates the jailbreak state.
Well, regardless of the type of jailbreak used, the next essential step before attempting a jailbreak bypass is understanding the common techniques developers use to detect jailbroken devices.
2.3. Types of Jailbreak Detection
Clearly, jailbreaking often allows testers to gain deeper insights into an application’s flow and even manipulate its logic. Considering this — and other important factors — developers frequently implement various methods to detect jailbroken devices. Below are some of the most commonly used detection techniques:
2.3.1 File-based Detection
This is one of the simplest methods of jailbreak detection. File-based detection works by searching for specific files or directories commonly associated with jailbroken devices, such as /Applications/Cydia.app, /bin/bash, /usr/sbin/sshd, or dynamic libraries like Substrate. If these files or libraries are detected, the application assumes the device is jailbroken
2.3.2. Process-based Detection
Another common method of jailbreak detection is process-based detection. This approach aims to identify suspicious activities by monitoring the processes running on the device, such as tools like frida-server, which are commonly used in jailbroken environments. If such processes are found, the application may restrict functionality or deny access entirely.
2.3.3. Directory Permissions Detection
Directory permissions detection involves checking the permissions of system directories. On a jailbroken device, certain directories that are typically read-only may become writable, allowing unauthorized modifications. If such changes in directory permissions are detected, the application may consider the device to be jailbroken
2.3.4. URI Schemes Detection
Another method of jailbreak detection involves checking for custom URI schemes associated with jailbroken devices. For example, Cydia registers the cydia:// URI scheme, allowing links to direct users to apps available via Cydia. Since iOS applications can query registered URI schemes, the presence of cydia:// is often used to detect whether the device is jailbroken.
While these are some of the most common techniques, it’s worth noting that developers may employ a variety of other methods depending on their specific security needs. Despite the type of jailbreak used or the detection methods employed, the next process is to prepare our testing environment.
III. Setting Up the Testing Environment
Assuming our device has already been jailbroken, so the next step is to install and configure several tools that enable interaction with the device. These essential tools include OpenSSH and Zip (on iOS), as well as Ghidra, and Sideloadly on the desktop.
3.1. Setting Up Tools on iOS Device
3.1.1. Installing OpenSSH
The role of OpenSSH in this activity is primarily for file transfer. While other tools can also accomplish this task, OpenSSH provides flexibility in managing the transfer of files between the desktop and the iOS device. While this choice is somewhat subjective, I personally prefer using OpenSSH for file transfers due to its simplicity.
So, how can it be installed? Since I jailbroke my device using Palera1n, OpenSSH can be installed directly from the strap.palera.in repository. To do this, simply search for “OpenSSH” in your package manager and install it directly.
Note: we can use any package manager, such as Cydia or Sileo, to install the tools. Choose the one that best suits your needs.
To verify that OpenSSH is installed correctly, connect to the device via SSH to confirm OpenSSH is working.
3.1.2. Installing Zip
Another essential tool for this process is the zip
utility, which is required for compressing. To install it, simply run apt install zip within the iOS shell once access via SSH has been established.
3.2. Setting Up Tools on the Desktop
Once the necessary tools are installed on the iOS device, the next step is to set up the required tools on your desktop. While the installation process is generally consistent across different operating systems, it’s important to note that tools like Sideloadly are only supported on macOS and Windows at the moment.
3.2.1. Installing Ghidra
Given that we will be attempting to patch the binary, this tool is essential. While there are other tools available that offer similar functionality, in this case, I will be using Ghidra, as it is also the tool used in the MobileHackingLab course when discussing DVIA in section 4.3.
To get started, Ghidra can be downloaded directly from their GitHub page at https://github.com/NationalSecurityAgency/ghidra/releases. Once downloaded, extract the files and launch the application by running ./ghidrarun in an environment where the JDK is already installed.
3.2.2. Installing Sideloadly
The next step is to install Sideloadly, which can be downloaded from their official website at https://sideloadly.io. Why is this necessary?
Basically, to install applications on an iOS device through official means, it’s required to sign the app with a valid Apple ID or developer certificate. Sideloadly simplifies this process by allowing apps to be sideloaded onto an iOS device, bypassing the restrictions of the App Store.
However, it’s important to note that apps installed via Sideloadly using a free Apple ID are only valid for 7 days. After this period, the app will stop working unless it is re-signed and reinstalled. This is a limitation imposed by Apple to encourage developers to use paid developer accounts for long-term distribution. For paid Apple Developer accounts, the validity period can be extended to one year.
Regardless of the situation, this tool is especially useful when testing applications or deploying modified versions for penetration testing. It allows for easy installation of apps without needing to go through the App Store.
That said, in some cases, Sideloadly may not be necessary. Applications can be installed directly via tools like Filza or IPAInstaller if they are already signed or are from trusted sources. However, apps like No-Escape (in this case) must be signed with a valid Apple ID or developer certificate before they can be installed, which is where tools like Sideloadly come in.
As a note, since Sideloadly is only supported on macOS and Windows, ensure the appropriate operating system is used for this step.
With the required environment now set up, we can proceed to the execution process. Please note that the number of tools involved in this session is intentionally kept minimal, as our primary focus is on patching the existing binary.
IV. The Execution
4.1. Copying the App from the Lab Environment
Given that we are aiming to patch the existing binary, we need the binary of the app to test. When I attempted to download the app binary, I encountered an issue where the URL could not be accessed. In this case, I decided to extract the binary directly from the virtual lab provided by MobileHackingLab.
Once connected to the provided virtual device, the first step is to locate the app using the command:
find /var/containers/Bundle/Application/ -name “*.app”
After finding the app directory, we can navigate to it and archive the directory into a folder named “Payload”.
Why “Payload”? The name ‘Payload’ is part of the standard iOS app bundle structure required for the iOS operating system to correctly recognize and install the app. In the .ipa file format, the app bundle must be placed inside a ‘Payload’ folder containing the .app directory. Failure to follow this structure may prevent the app from being recognized or executed.
As a quick note, if you encounter the error “guru meditation b4822c@****:***** can’t listdir a file” during installation through Sideloadly, it is likely due to the directory not being named “Payload” (with a capital P).
Once the process is complete, we can easily transfer the .zip file from the iOS device to our desktop by using the following scp command on our desktop
scp mobile@192.168.100.200:/tmp/NoEscape.zip .
After the file is on our desktop, we simply need to change its extension to .ipa and then open it in Ghidra.
4.2. Opening the App on the Jailbroken Device
After ensuring that the .ipa file is available on our desktop, the next step is to install the app on our device. As mentioned in the “Setting Up the Testing Environment” section, tools like Sideloadly can be used for this process.
In short, after performing the drag-and-drop action, the app will be installed. However, before running it, we need to verify that the app is indeed from a trusted developer. This is a normal step, as we’ve signed the app using a free Apple ID.
So, to verify this, simply navigate to Settings -> General -> (scroll down to) VPN & Device Management, and verify the developer app listed in this menu.
Afterward, the app will be installed, and once run, we will notice that it immediately detects the iPhone as being jailbroken, causing the app to force close.
The next step, after encountering this, is to proceed with analyzing the app further.
4.3. Importing the Binary into Ghidra.
When we want to analyze a binary with Ghidra, the first step is to create a “Project.” This is a standard process, as the goal is to keep our analysis results organized and structured. In short, we just need to create a new project and assign it a name of our choice.
Once the project is created, we simply select it and use the “Import File” feature to import the binary we wish to analyze.
What if we’re uncertain about which binary to import (though this is a rare scenario)? In such cases, we can use the ipsw tool by Blacktop (https://github.com/blacktop/ipsw) to identify the name of the executable binary. For example:
In the image above, we can see that the bundle executable is named “No Escape.” So, we can directly import the binary named “No Escape” within the .app directory of the application.
When importing a binary into Ghidra, you’ll first encounter a prompt notifying you that the binary is in the Mach-O format, which is typical for iOS applications.
All we need to do is confirm this notification. Once we’ve done that, we can proceed by double-clicking the imported binary to begin analyzing it.
After opening the binary, it’s recommended to check the option labeled “Decompiler Parameter ID.”
But why is this step important? Let’s summarize this.
Technically, as we dive into reverse engineering, Ghidra will attempt to decompile machine code into a higher-level language like C, which helps clarify the application’s flow. However, the output can still be hard to interpret, especially with function parameters and return types.
By enabling the “Decompiler Parameter ID” option, valuable metadata is added to the decompiled code, providing more context about function parameters and return types. This makes it easier to understand how the app processes data and interacts with the system, which is essential for identifying vulnerabilities or understanding functionality.
Below is a comparison between enabling the “Decompiler Parameter ID” (left) and not enabling it (right).
Overall, this small adjustment significantly improves analysis by making the binary’s structure easier to navigate.
4.4. Analyzing the Binary in Ghidra.
As we know, one of the advantages of application development is that developers often name their elements (such as classes or variables) based on their function. For instance, when a developer creates something related to a PIN, the name of that element will typically reflect this connection, such as pinEntered, pinSettings, changePin, and so on.
Of course, there is nothing wrong with this approach, as it makes it easier for developers to trace issues when something goes wrong within a function (and, this is maybe one of the reasons why a technique like obfuscation exists, which helps developers automatically change the names of all elements into something more obscure).
Returning to the main topic, in the context of binary analysis, this ‘positive’ aspect is also beneficial for testers. With a few simple keywords, testers can, hopefully, trace the function that has been developed by the developers.
So, given that we are aiming to trace the flow of the jailbreak function within the application, the first step is to identify functions related to jailbreak. To do this, we can simply navigate to the ‘Symbol Tree’ in Ghidra and enter ‘jailbr.’
Since the term ‘jailbreak’ is sometimes written as ‘jailbroken,’ I recommend searching with just ‘jailbr’ to cover both variations.
4.4.1. Understanding the Decompiled Code
From the decompiled results, we will see the code flow as follows:
/* No_Escape.isJailbroken() -> Swift.Bool */
bool No_Escape::isJailbroken(void)
{
bool bVar1;
dword dVar2;
dword local_18;
dword local_14;
dVar2 = $$No_Escape.(checkForJailbreakFiles_in__BCE8F13474E5A52C60853EA803F80A81)()_->_Swift.Bool
();
if ((dVar2 & 1) == 0) {
local_14 = $$No_Escape.(checkForWritableSystemDirectories_in__BCE8F13474E5A52C60853EA803F80A81)( )_->_Swift.Bool
();
}
else {
local_14 = 1;
}
if ((local_14 & 1) == 0) {
local_18 = $$No_Escape.(canOpenCydia_in__BCE8F13474E5A52C60853EA803F80A81)()_->_Swift.Bool();
}
else {
local_18 = 1;
}
if ((local_18 & 1) == 0) {
bVar1 = $$No_Escape.(checkSandboxViolation_in__BCE8F13474E5A52C60853EA803F80A81)()_->_Swift.Bool
();
}
else {
bVar1 = true;
}
return bVar1 != false;
}
As seen in the code above, there is a main function isJailbroken()
that implements the logic to detect whether the device has been jailbroken. Upon further examination, we can see that this function works by calling several subroutines that check the device’s condition for signs of a jailbreak, which include:
4.4.1.1. checkForJailbreakFiles()
As the name suggests, we can deduce that this function is designed to check whether files commonly found on jailbroken devices exist.
If we trace this function (just double-click on these functions in the decompile window in Ghidra), we will find several strings within it, such as those pointing to Cydia, MobileSubstrate, bash, SSH, and APT directories.
4.4.1.2. checkForWritableSystemDirectories()
In the second part, the function appears to check whether the system allows writing to directories that should not be altered by regular applications.
And if we trace the function further, we can see that the application attempts to write a ‘jailbreak_text.txt’ file to the /private directory.
4.4.1.3. canOpenCydia()
Then, in the third function, we can clearly see that the application checks if Cydia can potentially be opened.
This is evident from the string, where it attempts to initialize a URL scheme "cydia://package/com.example.package”
, indicating an attempt to open Cydia, which is commonly associated with jailbroken devices. (refer back to point 2.3.4 regarding one of the jailbreak detection models).
4.4.1.4. checkSandboxViolation()
And the last one, the application will attempt to perform a check for the existence of the /private/var/lib/apt/
directory.
Why do they check this directory? As a note, one of the reasons is because the /private/var/lib/apt/
directory is typically found on devices that have been modified to run apps not authorized by Apple, such as APT. This directory is often associated with jailbroken devices, where APT is used to install and manage unauthorized software packages.
Overall, the application performs a series of checks in sequence, starting by looking for files commonly associated with jailbroken devices. If no jailbreak files are found, it proceeds to check whether specific system directories are writable, and so on.
If any check returns a value of 1, the device is considered jailbroken. If all checks return 0, the device is considered not jailbroken.
4.4.2. Understanding the Disassembled Code
After we understand the decompiled result, InshaAllah it will be easier for us to follow the assembly flow (though this is subjective).
To facilitate the discussion of each function flow, I will use the following code snippet:
10000a074 29 00 00 94 bl $$No_Escape.(checkForJailbreakFiles_in__BCE8F1 undefined4 $$No_Escape.(checkFor
10000a078 a0 00 00 36 tbz w0,#0x0,LAB_10000a08c
10000a07c 01 00 00 14 b LAB_10000a080
LAB_10000a080 XREF[1]: 10000a07c(j)
10000a080 20 00 80 52 mov w0,#0x1
10000a084 a0 c3 1f b8 stur w0,[x29, #local_14]
10000a088 04 00 00 14 b LAB_10000a098
LAB_10000a08c XREF[1]: 10000a078(j)
10000a08c dc 00 00 94 bl $$No_Escape.(checkForWritableSystemDirectories undefined4 $$No_Escape.(checkFor
In the early part of the above code snippet, we focus on the bl
(Branch with Link) instruction. The bl
instruction is used to jump to another code location and store the address of the next instruction (in this case, the tbz
instruction) into the link register (lr
).
To make this concept easier to understand, imagine we’re reading a book and we’re on page 99. We decide to go to a different chapter, so we jump to page 200. However, we also want to mark where we were on page 99, so that after we’re done reading page 200, we can return to page 99.
This is similar to how the
bl
instruction works. It jumps to a new location (in this case, to page 200). The link register (lr
) stores the address of the next instruction (which would be the one followingbl
, where we need to return to), so after finishing the task on page 200, we can “come back” to page 99 by jumping back to the address stored inlr
.
In summary, when the program reaches the bl
instruction, it “jumps” to the file-checking function. As we know, this function checks whether certain files (as described in section 4.4.1.1) exist on the system. Based on the outcome of this check, the execution will return to the instruction following the bl
and continue to the tbz
instruction.
Next, the tbz
instruction stands for “Test and Branch if Zero“, which checks whether a specific bit in the w0
register is 0. If it is (0), the program will jump (branch) to the location specified by the next label, which is LAB_10000a08c
label. If not, the execution will continue to the next instruction, such as mov w0, #0x1
at LAB_10000a080
.
Consider the following two images:
- In the first image below, we see that the arrow on the left will directly lead to the
checkForWritableSystemDirectories
function if the value ofw0
is 0. This means that no jailbreak-related files were found in the previous check.
- And in the second image below, the arrow leads to
LAB_10000a080
with the instructionmov w0, #0x1
. This means that if the value ofw0
is not 0 (i.e., jailbreak-related files were found), the program setsw0
to 1. This is used to proceed with the program’s execution, which, in this case, triggers a pop-up and then closes the application.
Note: to be able to understand about the
tbz
, think of it like a movie with two parallel storylines. We’re watching the main storyline, but there’s a point where the film asks, “Should we switch to the subplot, or keep going with the main story?”Now, if the condition (like a clue in the story) is met (meaning the bit in
w0
is 0), the movie branches off to the subplot. If the condition isn’t met (the bit is not 0), the movie keeps playing the main story without switching.So, the
tbz
instruction checks whether a specific condition is true (the bit being 0). If it is, the program “switches scenes” and jumps to another location. If it’s not, the story continues as normal, executing the next instruction.
4.4.3. The Four “Jumps” Leading to Jailbreak Detection
So, if we follow the execution flow in a straightforward manner (by following the instruction flow), we can see that the application performs four jumps, all of which aim to set certain values to true
. This results in the application detecting the device as jailbroken.
The four instructions in question are as follows:
0x10000a080 mov w0, #0x1
0x10000a0a4 mov w0, #0x1
0x10000a0c8 mov w0, #0x1
0x10000a0ec mov w8, #0x1
After understanding this flow, we will proceed to modify the application’s flow in the next section.
4.4.4. Patching the Binary
In the context of common bypassing jailbreak detection, it’s important to understand how boolean values work. A boolean typically represents a true or false value, often as 1
(true) or 0
(false). For example, when the application finds jailbreak-related files, it assigns the value 1
to indicate the device is jailbroken. If no such files are found, it assigns the value 0
, indicating the device is not jailbroken.
Now, referring back to the four instructions that set the value to 1. To bypass the jailbreak detection, we can modify the values to 0, as shown below:
0x10000a080 mov w0, #0x0
0x10000a0a4 mov w0, #0x0
0x10000a0c8 mov w0, #0x0
0x10000a0ec mov w8, #0x0
To implement this change, we can right-click on the address to be modified and choose “patch instruction.”
To summarize, the final result is as follows:
4.4.5. Exporting the Binary
After confirming that all changes have been applied, the next step is to export the program:
Ensure that “Original File” is selected in the format section:
Once the process is complete, a summary popup will appear.
Upon checking the directory where the output file is saved, you will find the following binary:
The next step is to replace the binary in the .app directory of the original application with this updated binary:
4.4.6. Installing the Patched Binary
As with the previous steps, we will need to zip the Payload directory once again and convert it back into an .ipa file (refer to point 4.1). Afterward, simply reinstall it using Sideloadly.
Once the installation is complete, upon launching the application, you will notice that it no longer detects the device as jailbroken, indicating that we have successfully bypassed the jailbreak detection implemented by the application.
V. Conclusion
And so, we arrive at the final section of this write-up. In summary, our approach ensures that whenever the application attempts to detect signs of a jailbreak, it consistently returns a false result.
It’s also worth noting that the tools and methods discussed here are not the only options available for bypassing jailbreak detection. Alternative approaches include manipulating the application’s logic using tools like Frida.
Some readers might be wondering, should this method be applied every time we encounter an app that detects a jailbreak? The answer is no. From personal experience, I recommend starting with automation tools first. For example, we could try using Shadow (https://github.com/jjolano/shadow) or explore various scripts available at https://codeshare.frida.re/.
VI. References
- Mobile Hacking Lab, “FREE Course — iOS Application Security,” November 2024. [Online]. Available: https://www.mobilehackinglab.com/course/free-ios-application-security-course.
- Mobile Hacking Lab, “Mobile Application Security Labs No Escape” [Online]. Available: https://www.mobilehackinglab.com/course/lab-no-escape.
- The Apple Wiki, “Jailbreak,” [Online]. Available: https://theapplewiki.com/wiki/Jailbreak.
- N. MATHPATI, “iOS Pentesting 101,” 20 August 2021. [Online]. Available: https://www.cobalt.io/blog/ios-pentesting-101.
- Malwarebytes, “IPHONE JAILBREAKING,” [Online]. Available: https://www.malwarebytes.com/iphone-jailbreaking.
- DUO LABS, “Jailbreak Detector Detector: An Analysis of Jailbreak Detection Methods and the Tools Used to Evade Them,” 18 January 2019. [Online]. Available: https://duo.com/blog/jailbreak-detector-detector.
- V. Yegorov, “Jailbreak detection: The modern way,” 20 November 2022. [Online]. Available: https://github.com/vadim-a-yegorov/Jailbreak-detection-The-modern-way.
- Blue Cedar, “Jailbreak Detection,” [Online]. Available: https://www.bluecedar.com/mobile-app-security-technical-glossary/jailbreak-detection.
- D. C. Weber, “LEARNING GHIDRA BASICS ANALYZING FIRMWARE,” [Online]. Available: https://www.cutawaysecurity.com/learning-ghidra-basics-analyzing-firmware/.
- J. Chambers, “earlyremoval, in the Conservatory, with the Wrench: Exploring Ghidra’s decompiler internals to make automatic P-Code analysis scripts,” 20 May 2022. [Online]. Available: https://www.nccgroup.com/us/research-blog/earlyremoval-in-the-conservatory-with-the-wrench-exploring-ghidra-s-decompiler-internals-to-make-automatic-p-code-analysis-scripts/.
- P. Keeble, “Bypassing JailBreak Detection — DVIAv2 Part 2,” July 2020. [Online]. Available: https://philkeeble.com/ios/reverse-engineering/iOS-Bypass-Jailbreak/.
- https://github.com/NationalSecurityAgency/ghidra/releases
- https://sideloadly.io
- https://github.com/blacktop/ipsw
- https://github.com/jjolano/shadow
- https://codeshare.frida.re/