1. 程式人生 > >筆記-第一次ZjDroid脫殼實戰

筆記-第一次ZjDroid脫殼實戰

看了那麼多逆向破解的文章,總得來點實戰的了,正所謂實踐出真知嘛。

拜讀了姜維大神的「Android中Xposed框架篇—基於Xposed的一款脫殼神器ZjDroid工具原理解析」,準備來個應用動手試試了,想起我自己之前有個應用恰好是大數字加固的,於是想試試破自己的應用。 
(純粹是個人黑歷史,就不貼應用連結了)。

環境

我手頭的裝置是版本是 Android7.1.2 電腦上也有Android4.4版本的模擬器(Android Emulator,已root和刷入Xposed,不知道怎麼手動給模擬器刷root的同學參考這個https://android.stackexchange.com/questions/171442/root-android-virtual-device-with-android-7-1-1)

準備

首先嘛,當然是要下載大名鼎鼎的ZjDroid模組了,這個脫殼工具是基於Xposed模組的,看了下該專案的github,似乎並沒有給出ZjDroid現成的apk安裝包,網上雖然是有找,但我還是選擇自己手動編譯了一下,

clone 
匯入AS

在解決了~幾個問題~很多個問題以後總算是編譯成功了。

主要是為了解決匯入庫的問題和multidex的問題(為了支援低sdk版本的裝置)

也算是學習了

在脫殼的邊緣試探

把編譯好的apk裝進我的手機(Android 7.1.2),啟用模組重啟,連上電腦,按照教程裡的方法,啟動倆cmd一個用來看logcat輸出,一個用來發送廣播執行指令,

試了一個: 
檢視dex資訊

am broadcast -a com.zjdroid.invoke --ei target 18881 --es cmd '{action:dump_dexinfo}'
1
logcat輸出是

04-27 21:00:19.798 12563 12563 D zjdroid-shell-com.imlk.BlackAndWhite: the cmd = dump_dexinfo
04-27 21:00:19.805 12563 13323 D zjdroid-shell-com.imlk.BlackAndWhite: The DexFile Infomation ->
04-27 21:00:19.805 12563 13323 D zjdroid-shell-com.imlk.BlackAndWhite: filepath:/data/app/com.imlk.BlackAndWhite-1/base.apk mCookie:-1
04-27 21:00:19.807 12563 13323 D zjdroid-shell-com.imlk.BlackAndWhite: End DexFile Infomation
1
2
3
4
似乎有什麼奇奇怪怪的地方。。 
mCookie的值是-1,不過看教程裡面好像是一串沒有規律的數字哇。心想可能是大數字又升級了,這種方案脫殼可能不能奏效。

不管了,繼續試試:

 am broadcast -a com.zjdroid.invoke --ei target 8880 --es cmd '{"action":"backsmali","dexpath":"/data/app/com.imlk.BlackAndWhite-1/base.apk"}'
1
2
WTF! 
程式退出了! 
看下logcat裡的輸出

04-27 22:16:56.475 18342 18342 D zjdroid-shell-com.imlk.BlackAndWhite: the cmd = backsmali
04-27 22:16:56.487 18342 18734 D zjdroid-shell-com.imlk.BlackAndWhite: start disassemble the mCookie -1
1
2
???? 
戛然而止???

第一時間想到肯定和那個-1有關係,反正手頭有ZjDroid的原始碼,看看到底是哪裡崩了。 
字串搜尋,走起!

在修復的邊緣試探

首先想看看執行dump_dexinfo命令的時候,輸出的那個-1究竟是哪裡來的, 
到ZjDroid的原始碼裡搜尋mCookie: 
(這裡有一個小技巧,到github的專案首頁,頂欄可以指定在這個專案裡搜尋),

果然搜到一處com.android.reverse.request.DumpDexInfoCommandHandler中的doAction()方法輸出了這個-1

public class DumpDexInfoCommandHandler implements CommandHandler {

    @Override
    public void doAction() {
        HashMap<String, DexFileInfo> dexfileInfo = DexFileInfoCollecter.getInstance().dumpDexFileInfo();
        Iterator<DexFileInfo> itor = dexfileInfo.values().iterator();
        DexFileInfo info = null;
        Logger.log("The DexFile Infomation ->");
        while (itor.hasNext()) {
            info = itor.next();
            Logger.log("filepath:"+ info.getDexPath()+" mCookie:"+info.getmCookie()); //這裡輸出啦
        }
        Logger.log("End DexFile Infomation");
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
到as裡面定位到這個檔案,溯源到com.android.reverse.collecter.DexFileInfoCollecter的dumpDexFileInfo()方法

    public HashMap<String, DexFileInfo> dumpDexFileInfo() {
        HashMap<String, DexFileInfo> dexs = new HashMap<String, DexFileInfo>(dynLoadedDexInfo);
        Object dexPathList = RefInvoke.getFieldOjbect("dalvik.system.BaseDexClassLoader", pathClassLoader, "pathList");
        Object[] dexElements = (Object[]) RefInvoke.getFieldOjbect("dalvik.system.DexPathList", dexPathList, "dexElements");
        DexFile dexFile = null;
        for (int i = 0; i < dexElements.length; i++) {
            dexFile = (DexFile) RefInvoke.getFieldOjbect("dalvik.system.DexPathList$Element", dexElements[i], "dexFile");
            String mFileName = (String) RefInvoke.getFieldOjbect("dalvik.system.DexFile", dexFile, "mFileName");
            int mCookie = RefInvoke.getFieldInt("dalvik.system.DexFile", dexFile, "mCookie"); //這裡通過反射獲取"mCookie"這個int型別的變數
            DexFileInfo dexinfo = new DexFileInfo(mFileName, mCookie, pathClassLoader);
            dexs.put(mFileName, dexinfo);
        }
        return dexs;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
可以看到: 
原始碼中,通過反射獲取”mCookie”這個int型別的變數,然後結果得到的是-1,看到這裡我也沒多想,就想去驗證這個變數是否真的是-1

想達到這個目的,首先我想到的是用as動態除錯一波走起。

嘗試用動態除錯找出-1

結合上面的程式碼,可以總結出,應該是 
BaseDexClassLoader中的 
dexElements陣列中的元素中的 
dexFile中的 
mCookie

隨便下個斷點,方便起見,我經常下的斷點就是OnClickListener介面中的onClick方法,注意給介面中的方法下斷點的話,所有的實現了這個介面方法類的這個方法都能被debug捕捉到,

這樣不需要修改源程式,就能輕鬆的下斷點, 
我們只要點選任意一個可點選的view觸發了它的onClick方法,偵錯程式就能捕捉到,之後我們就能任意檢視內容了。

偵錯程式中插入程式碼,檢視內容 


WTF!!! 
mCookie居然是個Long[],說好的int呢! 


居然是Object!

回去看ZjDroid原始碼,從getFieldInt方法切入

            int mCookie = RefInvoke.getFieldInt("dalvik.system.DexFile", dexFile, "mCookie");
1
    public static int getFieldInt(String class_name,Object obj, String filedName){
        try {
            Class obj_class = Class.forName(class_name);
            Field field = obj_class.getDeclaredField(filedName);
            field.setAccessible(true);
            return field.getInt(obj);
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return -1; //這裡!!!!!!!!!!

    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
似乎是呼叫Field型別的物件的getInt()方法發生了異常(肯定會發生異常啦,這個變數都是個陣列,不可能得到int型別,異常也是情理之中的), 
發生異常以後,只能輸出 -1 了,

用同樣的動態除錯方法,對Android 4.4的模擬器進行測試: 
 


看來應該是art虛擬機器和dalvik虛擬機器的不同造成的,在Android4.4上面這個變數是int型別的,

不過我倒是來了興趣,想試試能不能修復這個問題,不過這就要對mCookie深入研究研究了。

第二天

起床搜了搜資料,網上也有好多人遇到了這個問題。

不過關於mCookie的詳細敘述倒是沒多少

退回原點,看看崩潰時的異常:

    --------- beginning of crash
04-28 10:31:34.468 17165-20179/com.imlk.BlackAndWhite E/AndroidRuntime: FATAL EXCEPTION: Thread-3
    Process: com.imlk.BlackAndWhite, PID: 17165
    java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.android.reverse-1/base.apk"],nativeLibraryDirectories=[/system/lib, /vendor/lib]]] couldn't find "libdvmnative.so"
        at java.lang.Runtime.loadLibrary0(Runtime.java:984)
        at java.lang.System.loadLibrary(System.java:1562)
        at com.android.reverse.util.NativeFunction.<clinit>(NativeFunction.java:19)
        at com.android.reverse.smali.MemoryBackSmali.disassembleDexFile(MemoryBackSmali.java:76)
        at com.android.reverse.collecter.DexFileInfoCollecter.backsmaliDexFile(DexFileInfoCollecter.java:141)
        at com.android.reverse.request.BackSmaliCommandHandler.doAction(BackSmaliCommandHandler.java:19)
        at com.android.reverse.mod.CommandBroadcastReceiver$1.run(CommandBroadcastReceiver.java:33)
        at java.lang.Thread.run(Thread.java:761)
1
2
3
4
5
6
7
8
9
10
11
12
說找不到這個libdvmnative.so

而這個檔案是在ZjDroid專案裡的,看看編譯出的apk

裡面是有這個東西的,真是奇了怪了,xposed框架竟然沒幫我把so檔案載入進去??????

後面發現是平臺的原因,我的模擬器是x86的,apk裡只有arm的原始碼

暫時失敗告終

哇,看了一下,這art和dalvik的區別還是很大的,僅僅是修復這些Java層的程式碼是行不通的,因為它這裡好幾個地方用到了so中的native方法,這些是不開源的東西,想修改也修改不了。Android7.1.2上的嘗試只好放棄了。

不過這裡還是學到了一些東西,也第一次嘗試了multidex,但是在Android4.4上好像得自己手動載入餘下的dex,還真讓人頭疼啊。

船新版本

github真的萬能啊!!

撈到了native部分的原始碼!

是從一個叫HeyGirl的專案裡撈到的,這個專案貌似是fork的最早版本的ZjDroid,(不知道是什麼原因原專案已不存在),總之從裡面撈到了native的原始碼,可以學習一波啦

又找來了luajava的原始碼,湊在一起基本上就是原版的ZjDroid了。

我為什麼要費工夫找這些原始碼???

因為github上面的ZjDroid中只有arm平臺的so檔案,而我的虛擬機器是x86的,沒法用,提示找不到so檔案。

繼續嘗試脫

輸入:

adb logcat -s zjdroid-shell-com.imlk.BlackAndWhite
1
logcat:

--------- beginning of /dev/log/system
--------- beginning of /dev/log/main
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): the package = com.imlk.BlackAndWhite has hook
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): the app target id = 3717
1
2
3
4
輸入:

am broadcast -a com.zjdroid.invoke --ei target 3717 --es cmd '{action:dump_dexinfo}'
1
logcat:

D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): the cmd = dump_dexinfo
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): The DexFile Infomation ->
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): filepath:/data/app/com.imlk.BlackAndWhite-1.apk mCookie:-1204615840
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): End DexFile Infomation
1
2
3
4
輸入

am broadcast -a com.zjdroid.invoke --ei target 3717 --es cmd '{"action":"backsmali","dexpath":"/data/app/com.imlk.BlackAndWhite-1.apk"}'
1
輸出

D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): the cmd = backsmali
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): start disassemble the mCookie -1203426560
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): dvmnative
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): dvmnative
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): the dexfile header item info start-->>>>>>>>>>
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): the stringStartOffset =112
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): the typeStartOffset =2312
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): the protoStartOffset =2800
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): the fieldStartOffset =4636
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): the methodStartOffset =5156
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): the classStartOffset =8028
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): the classCount =21
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): the dexfile header item info end<<<<<<<<<<<--
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): end disassemble the mCookie: cost time = 3s
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): start build the smali files to dex
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): build the dexfile ok
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): end build the smali files to dex: cost time = 0s
D/zjdroid-shell-com.imlk.BlackAndWhite( 3717): the dexfile data save to =/data/data/com.imlk.BlackAndWhite/files/dexfile.dex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
激動!!! 
似乎是成功了,我們去看看在目標路徑下有沒有我們要的檔案: 
哇是真的有!!

[email protected]_x86:/data/data/com.imlk.BlackAndWhite/files # ll
-rw------- u0_a60   u0_a60      12920 2018-04-29 10:44 dexfile.dex
1
2
快快adb pull出來瞧瞧

先用dex2jar處理,然後用jd-gui檢視。。。

我擦這不就是殼子嗎?逗我吧!!!

繼續研究

研究發現

加固應用在載入以後會動態載入兩個dex,所以加上apk,一共有三個DexFile

可以通過用as除錯看出來:

這裡的dexElements的來源是:

ClassLoader->pathList->dexElements
1
dexElements = {DexPathList$Element[3]@831564034400} 
    0 = {[email protected]} "dex file "[email protected]""
        dexFile = {[email protected]
            guard = {[email protected]
            mFileName = "/data/app/com.imlk.BlackAndWhite-1.apk"
            mCookie = -1192464816
        file = null
        zipFile = null
        zip = null
        isDirectory = false
        initialized = false
    1 = {[email protected]} "dex file "[email protected]""
        dexFile = {[email protected]
            guard = {[email protected]
            mFileName = "/data/app/com.imlk.BlackAndWhite-1.apk"
            mCookie = -1192527312
        file = null
        zipFile = null
        zip = null
        isDirectory = false
        initialized = false
    2 = {[email protected]} "zip file "/data/app/com.imlk.BlackAndWhite-1.apk""
        dexFile = {[email protected]
            guard = {[email protected]
            mFileName = "/data/app/com.imlk.BlackAndWhite-1.apk"
            mCookie = -1193659808
        file = {[email protected]} "/data/app/com.imlk.BlackAndWhite-1.apk"
        zipFile = null
        zip = {[email protected]} "/data/app/com.imlk.BlackAndWhite-1.apk"
        isDirectory = false
        initialized = false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
但是原版的ZjDroid預設是以檔名稱mFileName為鍵key,在一個map中儲存載入了的dex檔案的相關資訊的,這導致三個檔案只被儲存了一個資訊,於是我魔改了一下,讓ZjDroid將mCookie作為鍵,傳送的命令也通過指定mCookie的值來dump我們要的檔案,這樣就解決了衝突問題。

魔改版本 
KB5201314/ZjDroid: 
https://github.com/KB5201314/ZjDroid

脫!

到這一步可以說是非常nice了。

啟動時的輸出:

D/zjdroid-shell-com.imlk.BlackAndWhite( 3525): the package = com.imlk.BlackAndWhite has hook
D/zjdroid-shell-com.imlk.BlackAndWhite( 3525): the app target id = 3525
D/zjdroid-shell-com.imlk.BlackAndWhite( 3525): openDexFileNative() is invoked with filepath:/data/app/com.imlk.BlackAndWhite-1.apk result:-1196414688
D/zjdroid-shell-com.imlk.BlackAndWhite( 3525): openDexFileNative() is invoked with filepath:/data/app/com.imlk.BlackAndWhite-1.apk result:-1196412000
1
2
3
4
那個result就是mCookie

看一下dexinfo

D/zjdroid-shell-com.imlk.BlackAndWhite( 3525): the cmd = dump_dexinfo
D/zjdroid-shell-com.imlk.BlackAndWhite( 3525): The DexFile Infomation ->
D/zjdroid-shell-com.imlk.BlackAndWhite( 3525): filepath:/data/app/com.imlk.BlackAndWhite-1.apk dexElementToString:zip file "/data/app/com.imlk.BlackAndWhite-1.apk" mCookie:-1197778512
D/zjdroid-shell-com.imlk.BlackAndWhite( 3525): filepath:/data/app/com.imlk.BlackAndWhite-1.apk dexElementToString:dex file "[email protected]" mCookie:-1196412000
D/zjdroid-shell-com.imlk.BlackAndWhite( 3525): filepath:/data/app/com.imlk.BlackAndWhite-1.apk dexElementToString:dex file "[email protected]" mCookie:-1196414688
D/zjdroid-shell-com.imlk.BlackAndWhite( 3525): End DexFile Infomation
1
2
3
4
5
6
這就是我魔改的版本啦,可以看到dexinfo裡還顯示了當前檔案是dex檔案還是zip檔案,也給出了mCookie

下面對三個檔案進行dumpdex

am broadcast -a com.zjdroid.invoke --ei target 3525 --es cmd '{"action":"dump_dexfile","mCookie":"-1197778512"}'
am broadcast -a com.zjdroid.invoke --ei target 3525 --es cmd '{"action":"dump_dexfile","mCookie":"-1196412000"}'
am broadcast -a com.zjdroid.invoke --ei target 3525 --es cmd '{"action":"dump_dexfile","mCookie":"-1196414688"}'
1
2
3
4
得到的三個檔案大小為

     Length Name
     ------ ----
     359520 dexdump-1197778512.odex
      39652 dexdump-1196412000.odex
    1051756 dexdump-1196414688.odex
1
2
3
4
5
直覺告訴我最大的那個應該是我們要的了

用backsmali.jar工具:

java -jar E:\toolBox\smali_JesusFreke\baksmali-2.2.2.jar deodex -d .\system\framework\ -o out -a 19 .\dexdump-1196414688.odex
1
命令是deodex
另外,還要指定sdk版本,-a引數後面的就是sdk版本,我的模擬器sdk版本是19。
-o是指定輸出的資料夾
注意這裡需要把系統裡面的\system\framework\資料夾裡面的東西手動取出來,因為backsmali.jar工具要用到這些東西,-d後面的引數就是framework資料夾的路徑 
比如: 
我當前資料夾的檔案樹:
.
├── dexdump-1196412000.odex
├── dexdump-1196414688.odex
├── dexdump-1197778512.odex
└── system
    └── framework
        ├── am.jar
        ├── am.odex
        ...
        ├── wm.jar
        └── wm.odex
1
2
3
4
5
6
7
8
9
10
11
ok,out資料夾下已經是輸出的smali檔案了,

用smali.jar轉成dex檔案

 java -jar E:\toolBox\smali_JesusFreke\smali-2.2.2.jar assemble -a 19 .\out\
1
用jadx開開試試

好激動啊!!!可算是幸苦沒有白費

改Application

把dex填回原來的apk裡面去,再用apktool反編譯,修改AndroidManifest.xml裡面的Application欄位

回編譯時出現error,最後一句是

W: E:\toolBox\resource\dumped\com.imlk.BlackAndWhite\unsigned\res\layout-v26\abc_screen_toolbar.xml:5: error: No resource identifier found for attribute 'keyboardNavigationCluster' in package 'android'
W:
1
2
看看apktool的help

usage: apktool b[uild] [options] <app_path>
 -f,--force-all          Skip changes detection and build all files.
 -o,--output <dir>       The name of apk that gets written. Default is dist/name.apk
 -p,--frame-path <dir>   Uses framework files located in <dir>.
1
2
3
4
嘗試著用-p指定framework資料夾:

java -jar E:\toolBox\apktool\apktool_2.3.1.jar b -p system\framework unsigned
1
居然成功了! 
簽名

去除StubApp

又崩了

04-30 02:30:34.604 3999-3999/com.imlk.BlackAndWhite E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.imlk.BlackAndWhite, PID: 3999
    java.lang.NoClassDefFoundError: com.stub.StubApp
        at com.imlk.BlackAndWhite.MainActivity.<clinit>(Unknown Source)
        at java.lang.Class.newInstanceImpl(Native Method)
        at java.lang.Class.newInstance(Class.java:1208)
...
1
2
3
4
5
6
7
看來有些東西沒清理乾淨

大數字往裡面加了靜態程式碼塊啊

在jadx裡全域性搜尋StubApp,去檔案裡註釋掉

native的onCreate方法

04-30 02:35:41.484 4151-4151/com.imlk.BlackAndWhite E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.imlk.BlackAndWhite, PID: 4151
    java.lang.UnsatisfiedLinkError: Native method not found: com.imlk.BlackAndWhite.MainActivity.onCreate:(Landroid/os/Bundle;)V
        at com.imlk.BlackAndWhite.MainActivity.onCreate(Native Method)
        at android.app.Activity.performCreate(Activity.java:5231)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
1
2
3
4
5
6
7
哇,沒想到這個onCreate方法居然是native的!!! 
皮萬斤!

看到應用裡兩個Activity裡面上一步的靜態程式碼塊有一點區別

    static {
        StubApp.interface11(0);
    }
1
2
3
4

    static {
        StubApp.interface11(1);
    }
1
2
3
4
猜測是根據引數值來識別是哪個Activity的

看看我們dump出來的其它dex

哇,簡直崩潰。。。做不來做不來

告一段落

所以說完全復原我是搞不來了,不過僅僅是看看原始碼還是能的

能力有限啊,搞不了,還是先繼續學習吧。。。。

2018_04_30

小插曲

編譯後安裝在Android4.4上時偶遇這個問題, 
安裝上以後,XposedInstaller直接就崩潰了,看日誌也一頭霧水,上網搜才知道是模組的versionCode和versionName沒有設定,在gradle.build里加上就好了。

04-29 09:29:04.538 5603-5619/de.robv.android.xposed.installer E/AndroidRuntime: FATAL EXCEPTION: RepositoryReload
    Process: de.robv.android.xposed.installer, PID: 5603
    android.database.sqlite.SQLiteConstraintException: installed_modules.version_name may not be NULL (code 19)
        at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
        at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:782)
        at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:788)
        at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:86)
        at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1469)
        at android.database.sqlite.SQLiteDatabase.insertOrThrow(SQLiteDatabase.java:1365)
        at de.robv.android.xposed.installer.repo.RepoDb.insertInstalledModule(RepoDb.java:374)
        at de.robv.android.xposed.installer.util.ModuleUtil.getInstance(ModuleUtil.java:52)
        at de.robv.android.xposed.installer.XposedApp.updateProgressIndicator(XposedApp.java:114)
        at de.robv.android.xposed.installer.util.RepoLoader$2.run(RepoLoader.java:210)
1
2
3
4
5
6
7
8
9
10
11
12
13
參考資料

- Android中Xposed框架篇—基於Xposed的一款脫殼神器ZjDroid工具原理解析: 
https://blog.csdn.net/jiangwei0910410003/article/details/52840602 
- [原創]安卓逆向之基於Xposed-ZjDroid脫殼: 
https://bbs.pediy.com/thread-218798.htm 
- ZjDroid專案原始碼(已經停更4年): 
https://github.com/halfkiss/ZjDroid 
- HeyGirl專案地址 
https://github.com/mikusjelly/HeyGirl 
- android am命令用法: 
https://blog.csdn.net/u010164190/article/details/72875865 
- [原創]360加固逆向脫殼之過反除錯: 
https://bbs.pediy.com/thread-213214.htm 
- [原創]360加固逆向脫殼之過反除錯–後續: 
https://bbs.pediy.com/thread-213377.htm 
- 360加固保動態脫殼: 
https://www.cnblogs.com/2014asm/p/4104456.html 
- JesusFreke smali: 
https://bitbucket.org/JesusFreke/smali/downloads/