Audilitics
Generic filters
Generic filters
Google Android Mobile Applications Audit
AID64
MSTG-IDMSTG-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
Debugging is a highly effective way to analyze run-time app behavior. It allows the reverse engineer to step through the code, stop app execution at arbitrary points, inspect the state of variables, read and modify memory, and a lot more.

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.
Anti-JDWP-Debugging Examples

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.
Checking the Debuggable Flag in ApplicationInfo

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() {
return Debug.isDebuggerConnected();
}

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) {
if (gDvm.debuggerConnected || gDvm.debuggerActive)
return JNI_TRUE;
return JNI_FALSE;
}

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(){
long start = Debug.threadCpuTimeNanos();

for(int i=0; i<1000000; ++i)
continue;

long stop = Debug.threadCpuTimeNanos();

if(stop - start < 10000000) {
return false;
}
else {
return true;
}
}

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 {
/*
* Some options that could be worth tampering with 🙂
*/

bool jdwpAllowed; // debugging allowed for this process?
bool jdwpConfigured; // has debugging info been provided?
JdwpTransportType jdwpTransport;
bool jdwpServer;
char* jdwpHost;
int jdwpPort;
bool jdwpSuspend;

Thread* threadList;

bool nativeDebuggerActive;
bool debuggerConnected; /* debugger or DDMS is connected */
bool debuggerActive; /* debugger is making requests */
JdwpState* jdwpState;

};

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 ) {
gDvm.methDalvikDdmcServer_dispatch = NULL;
}

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.
Using Frida or Xposed to hook APIs on the Java and native layers: manipulate the return values of functions such as isDebuggable and isDebuggerConnected to hide the debugger.
Changing the environment: Android is an open environment. If nothing else works, you can modify the operating system to subvert the assumptions the developers made when designing the anti-debugging tricks.

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.
Multiple detection methods are scattered throughout the app's source code (as opposed to their all being in a single method or function).
The anti-debugging defenses operate on multiple API layers (Java, native library functions, assembler/system calls).
The mechanisms are somehow original (as opposed to being copied and pasted from StackOverflow or other sources).

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)?
How difficult is identifying the anti-debugging code via static and dynamic analysis?
Did you need to write custom code to disable the defenses? How much time did you need?
What is your subjective assessment of the difficulty of bypassing the mechanisms?

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.

CategoryImpede Dynamic Analysis and Tampering
Function

Testing Anti-Debugging Detection (MSTG-RESILIENCE-2)

Referencegithub.com

This entry has no reviews.

Pin It on Pinterest