| AID | 64 |
|---|---|
| MSTG-ID | MSTG-RESILIENCE-2 |
| Audit | The app prevents debugging and/or detects, and responds to, a debugger being attached. All available debugging protocols must be covered. |
| Guidance | Testing Anti-Debugging Detection Overview As mentioned in the -- Reverse Engineering and Tampering -- chapter, we have to deal with two debugging protocols on Android: we can debug on the Java level with JDWP or on the native layer via a ptrace-based debugger. A good anti-debugging scheme should defend against both types of debugging. Anti-debugging features can be preventive or reactive. As the name implies, preventive anti-debugging prevents the debugger from attaching in the first place; reactive anti-debugging involves detecting debuggers and reacting to them in some way (e.g., terminating the app or triggering hidden behavior). The -- more-is-better -- rule applies: to maximize effectiveness, defenders combine multiple methods of prevention and detection that operate on different API layers and are distributed throughout the app. In the chapter -- Reverse Engineering and Tampering -- , we talked about JDWP, the protocol used for communication between the debugger and the Java Virtual Machine. We showed that it is easy to enable debugging for any app by patching its manifest file, and changing the ro.debuggable system property which enables debugging for all apps. Let's look at a few things developers do to detect and disable JDWP debuggers. We have already encountered the android:debuggable attribute. This flag in the Android Manifest determines whether the JDWP thread is started for the app. Its value can be determined programmatically, via the app's ApplicationInfo object. If the flag is set, the manifest has been tampered with and allows debugging. public static boolean isDebuggable(Context context){ return ((context.getApplicationContext().getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0); } isDebuggerConnected The Android Debug system class offers a static method to determine whether a debugger is connected. The method returns a boolean value. public static boolean detectDebugger() { The same API can be called via native code by accessing the DvmGlobals global structure. JNIEXPORT jboolean JNICALL Java_com_test_debugging_DebuggerConnectedJNI(JNIenv * env, jobject obj) { Timer Checks Debug.threadCpuTimeNanos indicates the amount of time that the current thread has been executing code. Because debugging slows down process execution, you can use the difference in execution time to guess whether a debugger is attached. static boolean detect_threadCpuTimeNanos(){ for(int i=0; i<1000000; ++i) long stop = Debug.threadCpuTimeNanos(); if(stop - start < 10000000) { Messing with JDWP-Related Data Structures In Dalvik, the global virtual machine state is accessible via the DvmGlobals structure. The global variable gDvm holds a pointer to this structure. DvmGlobals contains various variables and pointers that are important for JDWP debugging and can be tampered with. struct DvmGlobals { bool jdwpAllowed; // debugging allowed for this process? Thread* threadList; bool nativeDebuggerActive; }; For example, setting the gDvm.methDalvikDdmcServer_dispatch function pointer to NULL crashes the JDWP thread: JNIEXPORT jboolean JNICALL Java_poc_c_crashOnInit ( JNIEnv* env , jobject ) { Bypassing Debugger Detection There's no generic way to bypass anti-debugging: the best method depends on the particular mechanism(s) used to prevent or detect debugging and the other defenses in the overall protection scheme. For example, if there are no integrity checks or you've already deactivated them, patching the app might be the easiest method. In other cases, a hooking framework or kernel modules might be preferable. The following methods describe different approaches to bypass debugger detection: Patching the anti-debugging functionality: Disable the unwanted behavior by simply overwriting it with NOP instructions. Note that more complex patches may be required if the anti-debugging mechanism is well designed. Effectiveness Assessment Check for anti-debugging mechanisms, including the following criteria: Attaching JDB and ptrace-based debuggers fails or causes the app to terminate or malfunction. Work on bypassing the anti-debugging defenses and answer the following questions: Can the mechanisms be bypassed trivially (e.g., by hooking a single API function)? If anti-debugging mechanisms are missing or too easily bypassed, make suggestions in line with the effectiveness criteria above. These suggestions may include adding more detection mechanisms and better integration of existing mechanisms with other defenses. |
| Category | Impede Dynamic Analysis and Tampering |
| Function | Testing Anti-Debugging Detection (MSTG-RESILIENCE-2) |
| Reference | github.com |
Google Android Mobile Applications Audit
Review this entry