首先反編譯apk

看看AndroidManifest.xml情況!

重點看包名

package="com.faloo.BookReader4Android"

還有application下的android:name

android:name="com.tencent.StubShell.TxAppEntry"

看看對應的smali!

smali不熟悉的話,可以反編譯dex再轉jar 直接用java gui看java代碼。

  private void a(String paramString)
  {
    // Byte code:
    //   0: aload_0
    //   1: invokevirtual 59 com/tencent/StubShell/TxAppEntry:getApplicationInfo ()Landroid/content/pm/ApplicationInfo;
    //   4: astore_2
    //   5: aload_2
    //   6: getfield 64 android/content/pm/ApplicationInfo:dataDir Ljava/lang/String;
    //   9: astore_3
    //   10: new 66 java/lang/StringBuilder
    //   13: dup
    //   14: invokespecial 67 java/lang/StringBuilder: ()V
//   17: aload_3
.....
}

有很多 // 開頭的,這個是smali 加了 -d 調試選項時 才會出現的。換jeb看看。

查看smali代碼主要是為了找他調用的哪個so來解密dex , 這里代碼有點多,沒必要花時間去細品代碼。一般so文件都是放在 assets 或者 lib目錄下。先看看 assets 目錄。

沒看到有so文件,可以再看看 lib 目錄下。

有很多 so 文件! 不出意外的話解密的 so 應該就在其中。

這么多so文件一個個分析也不現實, 為了省事 可以直接上ida查看他加載了哪個so也是一樣的。

首先啟動ida server 為了防止被檢測到,最好將 android_server 改一個名字。

默認的端口也修改一下,命令如下 (有的情況 adb shell su 連著打不能生效,需要進入 adb shell 再 su提權后再執行):

adb shell su ./data/local/tmp/ida7032 -p5555

執行成功提示:

IDA Android 32-bit remote debug server(ST) v1.22. Hex-Rays (c) 2004-2017

Listening on 0.0.0.0:5555...

手機端的服務運行起來 并開了一個 5555 端口, 接著需要將手機端口映射到PC上來。

adb forward tcp:6666 tcp:5555

其中5555 是手機里 android_server的端口(需和執行命令時的端口一樣) 。

6666是映射到PC上的端口(理論上是可以1-65535任意端口,前提是沒被占用)。

注意: 端口轉發命令只需運行一次即可。即使android_server關閉 重開了,也不需要重復運行。可以打如下命令查看是否映射過。

PS > adb forward --list

84B5T15B03002203 tcp:6666 tcp:5555

如果映射過就無需再重復映射。

接著以調試模式啟動apk 命令格式是 am start -D -n 包名/入口 具體包名可以通過 AndroidManifest.xml 文件查看。或者通過如下命令查看:

PS > adb shell dumpsys activity top
TASK com.faloo.BookReader4Android id=120
  ACTIVITY com.faloo.BookReader4Android/com.faloo.app.activity.LogoPageActivity 8fc46c9 pid=9911
Failure while dumping the activity: java.io.IOException: Timeout

其中 

com.faloo.BookReader4Android/com.faloo.app.activity.LogoPageActivity 是包名+入口。

命令連起來則是如下:

PS E:\Android\虛擬機臨時使用\臨時> adb shell
angler:/ $ su
angler:/ # am start -D -n com.faloo.BookReader4Android/com.faloo.app.activity.BookActivity
Starting: Intent { cmp=com.faloo.BookReader4Android/com.faloo.app.activity.BookActivity }
angler:/ #

執行此命令后,手機上會提示 Walting For Debugger 字樣。因為我們是以調試方式啟動,所以 apk 在運行后 PC會在第一條指令處等待 恢復運行。

上IDA 

端口輸入我們映射后的端口 6666 

因為我們要看他加載哪個so來解密so和dex文件。所以要勾選加載LIB時中斷。如果是多個so解密dex,這樣我們還能觀察幾個解密so的加載順序。

點OK后 選擇相應的包名。

掛載上去后, 不出意外的話,就能看到如下的畫面:

直接F9運行。

運行后 手機里面還是 等待狀態,這時 需要使用 JDB喚醒進程。

JDB命令如下:

jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8888
java.net.ConnectException: Connection refused: connect
        at java.net.DualStackPlainSocketImpl.connect0(Native Method)
        at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79)
        at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
        at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
        at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
        at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
        at java.net.Socket.connect(Socket.java:589)
        at com.sun.tools.jdi.SocketTransportService.attach(SocketTransportService.java:222)
        at com.sun.tools.jdi.GenericAttachingConnector.attach(GenericAttachingConnector.java:116)
        at com.sun.tools.jdi.SocketAttachingConnector.attach(SocketAttachingConnector.java:90)
        at com.sun.tools.example.debug.tty.VMConnection.attachTarget(VMConnection.java:519)
        at com.sun.tools.example.debug.tty.VMConnection.open(VMConnection.java:328)
        at com.sun.tools.example.debug.tty.Env.init(Env.java:63)
        at com.sun.tools.example.debug.tty.TTY.main(TTY.java:1082)

致命錯誤:

無法附加到目標 VM。

執行后報了一堆錯誤。這是因為目標APK沒有開啟調試選項。APK拆包后,在 AndroidManiFest.xml 文件中的application 標簽里添加一句 android:debuggable="true"。添加后再重打包即可。添加 debuggable 操作是必須的,否則是無法喚醒進程的。

但是因為一些殼會檢測自身是否被修改。重打包的話,可能會崩潰。解決辦法就是直接修改系統配置文件。

這里直接用 mprop 文件修改,修改之后 所有的APK都不需要手動添加 debuggable 選項,默認就是開啟的。

打如下命令查看是否開啟調試:

adb shell getprop ro.debuggable

1

返回1 表示開啟了  返回0 表示未開啟。

開啟后, 再執行 jdb 命令 發現還是同樣的錯誤提示。

這是因為jdb 的 -connect 也是需要端口操作的,所以還是需要映射端口。

Forward 本身是可以通過端口映射端口,也可以通過進程映射端口。

命令:

adb forward tcp:8888 jdwp:10152

將之前的 tcp 換成了 jdwp 表示通過進程映射。所以后面的 10152 是我們前面查看的 pid。但這操作有點麻煩, 每次啟動都需要執行此命令。

另一種方法可以用 Android Studio 自帶的 DDMS 來映射端口。

Android Studio 3.x版本之后 沒有將 DDMS 集成到菜單欄中了,需要用Eventthing 搜索 monitor.bat 來啟動。使用DDMS的好處是每次重啟APK不需要再有其他操作。端口一般默認都是 8700 或者 8600 (貌似不能修改默認端口, 容易被檢測)。

這個時候在執行 jdb 命令就提示成功了。

jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8888

設置未捕獲的java.lang.Throwable

設置延遲的未捕獲的java.lang.Throwable

正在初始化jdb...

看到這個提示表示成功了。

這個時候IDA界面也喚醒了。

當子進程狀態發生改變的時候, IDA會拋一個錯誤碼為 11 的提示。調試期間可能會反復出現類似的提示,可以通過異常選項里面,忽略掉它。

第一次運行。不下斷點。因為前面我們已經勾選 lib 加載中斷了。第一次主要看他加載了哪些so。直接F9 跑起。

模塊窗口中看到有兩個so 再運行程序就飛了。

因為我們要從SO的入口處開始分析,所以下的斷點一定要精準,否則我們中斷后,入口函數可能已經被執行過了。

SO中有一個類似 PE TLS的東西 可以在 main 和構造函數之前執行,就是 init_array 。

還有一個關鍵的函數是 JNI_OnLoad。

但這兩個函數經常被一些殼 給混淆亂序了,靜態是無法分析的。

靜態方法定位init_array 

方法1:

通過 readelf -d 命令 readelf 是 linux下的程序。

libshella-2.9.0.2.so 文件 INIT_ARRAY 偏移是 0x3e84。

libBugly.so 的偏移是 0x176c8。

方法2: 

直接將目標SO拖入IDA 待IDA加載完后,直接按CTRL + S。

libshella-2.9.0.2.so 文件 INIT_ARRAY 看不到。

libBugly.so 的偏移是 0x176c8。

從圖中可以看出 IDA 的識別能力明顯不如 readelf。但readelf 也不是萬能的,很多混淆亂序殼 也是查不出的。有的查出來 可能也是錯誤的。

通過IDA可以看到 libBugly.so 沒有做處理,而 libshella-2.9.0.2.so 故意隱藏了 init_array 有點此地無銀三百兩的感覺。

我們重點分析 libshella-2.9.0.2.so 文件。他的偏移是 0x3e84。

IDA運行,待 libshella-2.9.0.2.so 加載后,我們在 image+0x3e84 處下斷。

EB686000+0x3e84=0xEB689E84

來到此處

第1個調用的是 libBugly.so 執行的函數:

--------------------------------------------

這個是很早的時候幫朋友寫的,整理硬盤的時候發現的,一起發出來。

文章好像沒有寫完,視頻是完整的。

這個是視頻地址: https://www.bilibili.com/video/BV1PL411W7P3

附件請點擊閱讀原文獲取,其中包含6個文件:

樣本APK;

樣本脫殼后APK;

樣本APK解壓后文件;

樣本APK解壓后dex;

本文文章DOC格式;

本文文章PDF格式。