Hi Everyone !

In my previous post, you saw how we can bypass a third party(Rootbeer) root detection mechanism. Now, we will analyze a different third party library on a different platform. The library named JailMonkey is a React Native library for detection if a phone has been jail-broken or rooted for iOS/Android. In this article, I will analyze the library prepared for the iOS operating system, not for the Android version.

I will continue through an application available in the App Store. Therefore, I will not share the package name. I’m not sure, but I remember that the same library is available in DVIAv2. You can also test it yourself through this application;

https://github.com/prateek147/DVIA-v2

Damn Vulnerable iOS App (DVIA)

I divided article into two subheadings. In the first one, we will use Frida to detect the target class in the application through keywords. Then we will detect and hook the value returned on the function and jailbroken device. In the second subheading, we will analyze how the JailMonkey library works and how the functions return.

Let’s start !

Mach-O

Figure – 1 : Mach-O File

First of all, it is useful to learn the format of the executable file superficially…

When you analyze any ipa archive or the directory where an application you downloaded from the App Store is installed, you will see the executable file under the AppName.app/ directory. That is a type of file called Mach-O, which can be run on iOS and OS X operating systems. Apple iOS stores multiple executable files in Mach-O format by embedding them in a single file.

Third party libraries, executable files, object code and various file types are compiled in the respective file. Here we will examine the library named JailMonkey among these third libraries.

 

FIND CLASS AND FUNCTION

The reason I traced using Frida is the ObjC.classes method in the JavaScript API.

Figure – 2 : Find Class

In the screenshot above, I would like to list the classes with the ObjC.classes property via hasOwnProperty(). ObjC.classes is a method that mapping classes with the ObjC.Object JavaScript binding on the current application.

I also want to detect classes that contain only the word “jail” using regexp. When you don’t use it, it will list all the classes in Mach-O file, which means too much output.

Two different classes that contain the word jail were identified. The target function can be in either class. Therefore, it is useful to proceed through two different classes. The second task is to detect the function in the classes.

Figure -3 : Find Function

I have listed the functions that contain the word jail in the classes that pass the relevant if condition using regexp again. The reason I defined the funcName variable in the eval method was to verify that the code is a JavaScript method.

A function named “- isJailBroken” was detected in the JailMonkey class from the outputs. The next goal will be to determine the return value of the relevant function.

Figure – 4 : Find Return Value

I got the return value in the isJailBroken function of the JailMonkey class via Interceptor. The value returned in the log output was 0x1, so it was true. What we need to do is replace the retval variable we created as an example and hook it false. So 0x0

console.log("----------------");
console.log("SCRIPT INJECTED!");
console.log("----------------");

for (var className in ObjC.classes)
{
   if (ObjC.classes.hasOwnProperty(className))
        {
           if(/jail/i.test(className))
            {
                console.log("[*]Detected class\n" + className);
                
                var funcName = eval('ObjC.classes.' + className + '.$methods');
                for (var i = 0; i < funcName.length; i++)
                    {
                        if(/jail/i.test(funcName[i]))
                        {
                            console.log("[*]Function Detected In " + className + "\n" + funcName[i]);
                            const classj = eval('ObjC.classes.'+ className);
                            Interceptor.attach(classj[funcName[i]].implementation, {
                            onLeave: function (retval) {
                                console.log("\n[*]Return value\n" + retval);
                                retval.replace(0x0);
                                console.log("\n[*]New value\n" + retval);
                                }});
                                }
                        }
        }
    }
}

I hooked the value 0x1 to 0x0 in the last state of the code and the warning message disappeared when the app is opened. So Jailbreak detection was bypass.

If we summarize what has been done briefly, we have determined the classes and functions in which contains “jail” in the executable file. After that, we hooked the target function to make it look like a non-jailbroken device.

Yes, I hooked a function and made the warning message disappear. But how did this happen? What is checked in the isJailBroken function?

Let’s analyze the functions of the related library for the answers.

 

APPENDIX: ANALYZE VIA disassembler

I will use the tool called Ghidra. After getting the Mach-O executable file to your local with scp protocol, import the relevant file to Ghidra and start the analysis process.

After the analysis process is completed, I always try to trace keywords.s. For example, I would use the word “xbin” for root detection. For JailBreak, I’ll search for the word “Cydia“.

Figure – 6 : Search Result

I will continue with class::function statements instead of Global for find target class and function. Because this Namespace structure shows that “Cydia” means that the target class is included in a function. Also, It may mean that the corresponding function can perform a query with the word Cydia.

In the screenshot on the left, the directories and files that may be on a jailbroken device are defined in pathsToCheck function.

NSArray represents that the related function is an Array function. I might be wrong as I am weak on Objective-C, but as far as I understand msgSend returns Array list-added variables. And I think retainAutoreleasedReturnValue keeps the constant return of this function.

We are sure that this function is used in a different place. What we need to do for this is to search for the pathsToCheck function and determine the class::function.

I’ll explain as much as I understand through keywords. The variables in the pathsToCheck function were listed with NSArray. In the checkPaths function, the local presence of files in the pathsToCheck function is checked via NSFileManager. If a file representing Jailbreak is detected on the device, a value of true(uVar4) will be assigned. Then the variable passes through the AND operator with 1, and since it is not equal to 0, the checkPaths function will return 1, so it is true.

Now, we need to check if this function’s returning true value makes be useful for other functions.

The value returned from three different functions has been put in the if condition. Even if the checkPaths and checkSchemes functions return 0, the response returned by the canViolateSandbox function inside is referenced. This function writes a txt file to the private/ directory to check via NSFileManager if the sandbox mechanism is working.

Boolean values received for Root detection or Jailbreak detection are usually taken under a single function and return the final value. This means that it is enough to hook the isJailMonkey function. Due to this situation, it was enough to hook the isJailBroken function with Frida.

CONCLUSION

I wrote this article as a motivation to develop myself in the field of mobile security and reverse engineering. Third-party libraries that provide Root detection and Jailbreak detection can now be bypassed by third-party applications(Magisk, Liberty Lite etc.). In cases where these applications do not work, we have seen how we can trace them with keyword. I can’t call this post a guide, but at least it can help you because it contains keywords.

You can make the script I prepared in the first subtitle more organized with try/catch. In addition, I will cleap up my script on GitHub whenever I have the opportunity.

I hope it was a useful writing. If the article has missing or wrong informations, please contact me. Remember…

Knowledge increases with sharing… 

References

https://developer.apple.com/documentation/objectivec/

For Objective-C methods

https://frida.re/docs/javascript-api/

For JavaScript API

https://developer.apple.com/library/archive/documentation/Performance/Conceptual/CodeFootprint/Articles/MachOOverview.html

For more detailed information about Mach-O
Share: