手動繞過百度加固Debug.isDebuggerConnected反調試的方法

本文博客地址:http://blog.csdn.net/qq1084283172/article/details/78237571


1.調用Debug.isDebuggerConnected函數這種反調試的Android加固比較少,主要是因爲Debug.isDebuggerConnected這種方法的反調試作用不大,過掉也比較容易。百度的Android應用加固使用了調用Debug.isDebuggerConnected函數檢測程序被調試的反調試方法。



2.繞過基於Debug.isDebuggerConnected函數檢測進行反調試的方法整理:


【1】.對基於Debug.isDebuggerConnected函數檢測進行反調試的百度加固的Android應用使用Apktool工具進行解包處理,在解包後的所有smali文件中全局搜索關鍵字符串“isDebuggerConnected”,查找到Debug.isDebuggerConnected函數檢測反調試的smali彙編代碼的位置,修改smali代碼檢測位置處的判斷條件 繞過Debug.isDebuggerConnected函數檢測的反調試,使用Apktool工具對解包修改後的百度加固的Android應用的smali文件進行重新打包和簽名處理,推薦使用AndroidKiller工具進行這所有的操作。



【2】.在 Dalvik模式下 進行百度加固的Andorid應用的動態so庫文件調試時,使用IDA腳本IDC文件 Hook VMDebug.isDebuggerConnected函數的Native層實現函數dvmDbgIsDebuggerConnected,修改dvmDbgIsDebuggerConnected函數的返回值(基於VMDebug.isDebuggerConnected函數的Nativev層的函數dvmDbgIsDebuggerConnected)並且dvmDbgIsDebuggerConnected函數在libdvm.so庫文件中還是導出函數,具體的原理參考《在百度加固中正確使用ida的姿勢》。



Hook dvmDbgIsDebuggerConnected函數的IDA腳本(至於腳本中,第一次r0寄存器的值爲1的時候爲什麼不改成0 ,需要參考一下Android源碼的實現才能理解)。

from idaapi import *  
from idc import *

debug_addr = LocByName("_Z25dvmDbgIsDebuggerConnectedv")
end = FindFuncEnd(debug_addr) - 0x02
count = 0;

class DumpHook(DBG_Hooks):
    def dbg_bpt(self,tid,ea):
        global count
        r0 = GetRegValue('r0')
        if r0 == 1:
            count = count + 1
            if count == 2:
                SetRegValue(0,"r0")
        ResumeProcess()
        return 0

AddBpt(end)
debug = DumpHook()
debug.hook()

print "hook" 

dalvik虛擬機模式 下,VMDebug.isDebuggerConnected函數最終調用的是Native函數 dvmDbgIsDebuggerConnected



函數dvmDbgIsDebuggerConnectedlibdvm.so庫文件 中的導出函數



art虛擬機模式 下,VMDebug.isDebuggerConnected函數最終調用的是Native函數art::Dbg::IsDebuggerActive



函數art::Dbg::IsDebuggerActivelibart.so庫文件 中的導出函數



【3】.解包百度加固的Android應用,把百度加固的 libbaiduprotect.so 庫文件單獨拿出來,自己編寫一個dalvik虛擬機模式下的 loader程序 調用百度加固的 libbaiduprotect.so 庫文件中的JNI_Onload函數bypass掉殼代碼和反調試,具體的方法可以參考看雪論壇的文章《百度加固逆向分析》。

#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include <jni.h>

int main()
{
    JavaVM* vm;
    JNIEnv* env;
    jint res;
     
    JavaVMInitArgs vm_args;
    JavaVMOption options[1];
    options[0].optionString = "-Djava.class.path=.";
    vm_args.version=0x00010002;
    vm_args.options=options;
    vm_args.nOptions =1;
    vm_args.ignoreUnrecognized=JNI_TRUE;
     
    printf("[+] dlopen libdvm.so\n");
	// RTLD_LAZY RTLD_NOW
    void *handle = dlopen("/system/lib/libdvm.so", RTLD_LAZY);
    if(!handle) {
		
		printf("[-] dlopen libdvm.so failed!!\n");
		return 0;
    }
	
    // 先創建一個java虛擬機。因爲JNI_ONload函數參數第一個參數爲JavaVM。
    typedef int (*JNI_CreateJavaVM_Type)(JavaVM**, JNIEnv**, void*);
    JNI_CreateJavaVM_Type JNI_CreateJavaVM_Func = (JNI_CreateJavaVM_Type)dlsym(handle, "JNI_CreateJavaVM");
    if(!JNI_CreateJavaVM_Func) {
		
		printf("[-] dlsym failed\n");
		return 0;
    }
	
	// 創建java虛擬機
    res = JNI_CreateJavaVM_Func(&vm, &env, &vm_args)
    void* si = dlopen("/data/local/tmp/libbaiduprotect.so", RTLD_LAZY);
    if(si == NULL) {
		
		printf("[-] dlopen err!\n");
		return 0;
    }
	
    typedef jint (*FUN)(JavaVM* vm, void* res);
    FUN func_onload = (FUN)dlsym(si, "JNI_OnLoad");
	// 將斷點下在了這裏可以正好獲取到JNI_Onload的函數地址。
    if(func_οnlοad==NULL)
        return 0;
	
	// 調用JNI_Onload函數
    func_onload(vm,NULL);
    return 0;
}

關於Android系統上dalvik虛擬機模式下java虛擬機的創建方法,可以參考書籍《Android框架揭祕》102頁~110頁中,關於dalvik虛擬機模式下java虛擬機創建的代碼分析。




Dalvik虛擬機模式下,java虛擬機的創建可以參考Andorid 4.4.4 r1的源碼文件 /dalvik/vm/Jni.cpp  中的代碼。

http://androidxref.com/4.4.4_r1/xref/dalvik/vm/Jni.cpp#3424



Art虛擬機模式下,java虛擬機的創建可以參考Android 4.4.4 r1的源碼文件 /art/runtime/jni_internal.cc 中的代碼。

http://androidxref.com/4.4.4_r1/xref/art/runtime/jni_internal.cc#2888



【4】.自己編寫個簡單的Android程序,自定義加載百度加固的動態庫文件libbaiduprotect.so,然後在這個Android應用的基礎上進行百度加固動態庫文件libbaiduprotect.so的動態調試。



3.這裏再介紹一種 手動繞過百度加固Debug.isDebuggerConnected反調試的方法,比較實用也比較簡單不需要太多的操作。在介紹這種手動過掉Debug.isDebuggerConnected函數反調試的方法之前,先了解一下Debug.isDebuggerConnected函數的執行流程,以Android 4.4.4 r1的源碼爲分析基礎。


【1】.Debug.isDebuggerConnected函數是在Android 4.4.4 r1源碼的文件 /frameworks/base/core/java/android/os/Debug.java 中實現的,該函數最終調用的是VMDebug.isDebuggerConnected函數。

http://androidxref.com/4.4.4_r1/xref/frameworks/base/core/java/android/os/Debug.java#isDebuggerConnected



【2】.VMDebug.isDebuggerConnected函數是在Native層實現的,在Android 4.4.4 r1源碼的文件 /libcore/dalvik/src/main/java/dalvik/system/VMDebug.java 中,到這裏Debug.isDebuggerConnected函數的java層實現已經基本完成了,接下來是Debug.isDebuggerConnected函數在Native層的實現,由於Android系統可以運行在Dalvik虛擬機模式下或者Art虛擬機模式下,因此在Dalvik虛擬機模式下和Art虛擬機模式下,Debug.isDebuggerConnected函數底層的具體實現會有有所不同,需要分開來分析和學習。

http://androidxref.com/4.4.4_r1/xref/libcore/dalvik/src/main/java/dalvik/system/VMDebug.java#122



【3】.在dalvik虛擬機模式下,VMDebug.isDebuggerConnected函數是在Android 4.4.4 r1源碼的文件/dalvik/vm/native/dalvik_system_VMDebug.cpp 中實現的,具體就是對應Native層的函數Dalvik_dalvik_system_VMDebug_isDebuggerConnected

http://androidxref.com/4.4.4_r1/xref/dalvik/vm/native/dalvik_system_VMDebug.cpp#Dalvik_dalvik_system_VMDebug_isDebuggerConnected


Dalvik_dalvik_system_VMDebug_isDebuggerConnected函數的實現,Dalvik_dalvik_system_VMDebug_isDebuggerConnected最終調用的是libdvm.so庫文件的導出函數dvmDbgIsDebuggerConnected


【4】.dvmDbgIsDebuggerConnected函數是在Android 4.4.4 r1源碼的文件 /dalvik/vm/Debugger.cpp 中實現的,最終Debug.isDebuggerConnected函數的返回值是由全局對象gDvm的成員變量gDvm.debuggerActive決定的。

http://androidxref.com/4.4.4_r1/xref/dalvik/vm/Debugger.cpp#443



【5】.在art虛擬機模式下,VMDebug.isDebuggerConnected函數是在Android 4.4.4 r1源碼的文件 /art/runtime/native/dalvik_system_VMDebug.cc 中實現的,具體就是實現函數VMDebug_isDebuggerConnected



【6】.VMDebug_isDebuggerConnected函數最終是調用的 Dbg::IsDebuggerActive函數。

http://androidxref.com/4.4.4_r1/xref/art/runtime/native/dalvik_system_VMDebug.cc#117



【7】.Dbg::IsDebuggerActive 函數是在Android 4.4.4 r1源碼文件 /art/runtime/debugger.cc 中實現的,具體就是獲取全局變量gDebuggerActive的狀態值。

http://androidxref.com/4.4.4_r1/xref/art/runtime/debugger.cc#578



4.Debug.isDebuggerConnected函數的底層實現已經分析差不多啦,下面就說下 Dalvik虛擬機模式下 手動Debug.isDebuggerConnected反調試的方法。關於Android應用so庫文件的動態調試就不詳細介紹了,網上的教程很多自己去看。


【A】.由於Dalvik虛擬機模式下,函數dvmDbgIsDebuggerConnectedlibdvm.so庫文件 中的導出函數,因此在進行Dalvik虛擬機模式下的so庫文件的

動態調試時,想要 過掉Debug.isDebuggerConnected函數的反調試 需要在 libdvm.so庫文件dvmDbgIsDebuggerConnected函數開頭和結尾的位置下斷點進行攔截,然後修改dvmDbgIsDebuggerConnected函數的返回值爲0即可。

【B】.由於Art虛擬機模式下,函數art::Dbg::IsDebuggerActivelibart.so庫文件 中的導出函數,因此在進行Art虛擬機模式下的so庫文件的

動態調試時,想要 過掉Debug.isDebuggerConnected函數的反調試 需要在libart.so庫文件art::Dbg::IsDebuggerActive函數開頭和結尾的位置下斷點進行攔截,然後修改art::Dbg::IsDebuggerActive函數的返回值爲0即可。


下面就以dalvik虛擬機模式下的百度加固的 libbaiduprotect.so 庫文件動態調試爲例,進行手動繞過百度加固Debug.isDebuggerConnected反調試的方法步驟說明。


【1】.百度加固的Android應用以調試模式啓動等待調試以後,IDA Pro附加調試該百度加固的Android應用成功以後如下圖設置IDA Pro的調試運行選項,並在libdvm.so庫文件dvmDbgIsDebuggerConnected函數開頭和結尾的位置下斷點進行攔截,然後F9 運行當前被附加的Android應用程序幾次,不過F9運行當前被附加的程序幾次以後,該應用程序會斷在dvmDbgIsDebuggerConnected函數的開頭或者結尾的位置即當前函數斷點被觸發啦,沒事,不用理會,繼續做下面的操作即可。



【2】.另開啓一個命令行終端Terminate,使用 jdb調試器 連接到被調試附加百度加固的Android應用,jdb調試器連接成功以後,dvmDbgIsDebuggerConnected函數開頭的斷點會被觸發,斷在dvmDbgIsDebuggerConnected函數開頭的位置,再F9運行1次 斷在dvmDbgIsDebuggerConnected函數結尾的位置,此時dvmDbgIsDebuggerConnected函數的返回值R0的值爲1(不做任何操作繼續F9運行),將第2次dvmDbgIsDebuggerConnected函數的返回值R0的值1修改爲0 即可手動繞過百度加固Debug.isDebuggerConnected的反調試(千萬記得:jdb附加之後,是修改dvmDbgIsDebuggerConnected函數第2次返回值1爲0)。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章