Frida is a dynamic instrumentation framework and has remained as the most popular reverse engineering tool among security researchers, pentesters and even the bad actors. Frida is more robust compared to other hooking frameworks ( such as Xposed and its variants ) because it helps to instrument binaries generated from different langugages such as Java, Objective-C, C, C++, C#. Because Frida allows to instrument the app, security sensitive apps such as enterprise, banking and government apps deploy various mechanisms to detect the presence of Frida. Unlike xposed framework, frida specific artifacts appear on the application’s process memory only when an application is hooked. Frida opearates in multiple modes such as Embedded mode and injected mode. In either case, frida specific native library ( frida-agent or frida-gadget ) appears in the process memory of the app.
Some of the popular frida detection mechanisms taken from OWASP MSTG and from my personal experience on pentesting multiple apps are discussed below
a) Detect presence of frida-agent, frida-gadget native library in /proc/<pid>/maps file which can be bypassed by changing the name of native library.
b) Scan the memory of native libraries for presence of certain strings or frida specific symbols as mentioned in this blog, but this can be eventually bypassed by recompiling the frida library after patching the frida specific artifacts.
c) Iterate through all the TCP open ports and pass D-Bus AUTH message to see if frida-server responds. This is effective approach as long as frida-server is used.When application is tampered to use frida-gadget then this mechanism is not useful.
d) Check for the presence of frida specific files in /data/local/tmp, which are quite trivial to bypass by renaming the frida specific files.
e) Check if the executable section of native library is writeable ( rwxp flag for the native library in /proc/<pid>/maps file) which is unlikely to happen on a normal device.
In this blog post, I will discuss few other mechanisms which are not widely used, but still can serve as a good detection mechanism for frida.
Presence of frida specific threads
Frida-gadget and Frida-server creates new threads in the process under instrumentation. Since these threads are named threads such as gmain, gum-js-loop, it is possible to detect the presence of such threads. Since frida can be attached at any time to the process, this iteration has to happen either in a loop or before executing sensitive code. A next step after finding the presence of thread will be to react either by killing the frida specific thread or exit gracefully. This will crash or freezes the app and prevents further instrumentation.
Presence of frida specific named pipes
Especially when frida-server is used, the communication between frida-agent injected in the remote process communicates with frida-server through pipes. Since these pipes have an explicit name, it is possible to iterate through different files under /proc/<pid>/fd to find the named pipes corresponding to frida.
In both detection mechanisms discussed above
a) Hook the libc APIs and point to fake files which does not have any frida specific artifacts
b) Hook the native library functions which does the iteration of folders/files in /proc/<pid> paths
c) Recompile the frida library replacing the strings being checked with arbitrary values.
(a) and (b) can be made more stealthy by using syscalls and using native obfuscators. But (c) cannot be mitigated unless there is a different mechanism to detect presence of frida. This brings us to the next mechanism which detects frida based on its fundamental concept of tampering the executable section.
Disk to Memory checks
When frida-agent or frida-gadget hooks into the targeted native libraries, it dynamically tampers the text section of the native library. The detection mechanism here is to compare the executable section of the native library on disk vs loaded executable section on memory. Compute the checksum on disk and compare with the checksum computed on the memory of executable section helps to detect the tampering of code. Since Frida can attach to the app at any point of time, the comparison has to be done at frequent intervals or before executing sensitive code. Though this method is effective in thwarting any dynamic tampering of the code, the price for that is performance especially when the size of the native libraries are huge.
In have published all the detection mechanisms discussed here in this github project. Especially for the 3rd mechanism, I have included the dynamic tamper check for native library of the app and libc library to detect hooking on libc APIs. Beware that the code provided in github cannot be used as it is, because without using syscalls and sufficient obfuscation of control flow and strings, it maybe trivial to bypass the detection mechanism. However Frida is enhancing its powerful Stalker engine which can patch the instructions of the core logic after the native library is loaded and before its execution. So, all the mechanisms discussed above can eventually be bypassed. It is needless to say determined reverse engineers will have the final laugh.