天天看點

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

一、前言

今天我們繼續來看破解apk的相關知識,在前一篇:Eclipse動态調試smali源碼破解apk 我們今天主要來看如何使用IDA來調試Android中的native源碼,因為現在一些app,為了安全或者效率問題,會把一些重要的功能放到native層,那麼這樣一來,我們前篇說到的Eclipse調試smali源碼就顯得很無力了,因為核心的都在native層,android中一般native層使用的是so庫檔案,是以我們這篇就來介紹如何調試so檔案的内容,進而讓我們破解成功率達到更高的一層。

二、知識準備

我們在介紹如何調試so檔案的時候,先來看一下準備知識:

第一、IDA工具的使用

早在之前的一篇文章:Android中通過靜态分析技術破解apk 中使用IDA工具靜态分析so檔案,通過分析arm指令,來擷取破解資訊,比如列印的log資訊,來破解apk的,在那時候我們就已經介紹了如何使用IDA工具:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這裡有多個視窗,也有多個視圖,用到最多的就是:

1、Function Window對應的so函數區域:這裡我們可以使用ctrl+f進行函數的搜尋

2、IDA View對應的so中代碼指令視圖:這裡我們可以檢視具體函數對應的arm指令代碼

3、Hex View對應的so的十六進制資料視圖:我們可以檢視arm指令對應的資料等

當然在IDA中我們還需要知道一些常用的快捷鍵:

1、強大的F5快捷鍵可以将arm指令轉化成可讀的C語言,幫助分析

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

首先選中需要翻譯成c語言的函數,然後按下F5:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

看到了,立馬感覺清爽多了,這些代碼看起來應該會好點了。

下面我們還需要做一步,就是還原JNI函數方法名

一般JNI函數方法名首先是一個指針加上一個數字,比如v3+676。然後将這個位址作為一個方法指針進行方法調用,并且第一個參數就是指針自己,比如(v3+676)(v3…)。這實際上就是我們在JNI裡經常用到的JNIEnv方法。因為Ida并不會自動的對這些方法進行識别,是以當我們對so檔案進行調試的時候經常會見到卻搞不清楚這個函數究竟在幹什麼,因為這個函數實在是太抽象了。解決方法非常簡單,隻需要對JNIEnv指針做一個類型轉換即可。比如說上面提到a1和v4指針:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

我們可以選中a1變量,然後按一下y鍵:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

然後将類型聲明為:JNIEnv*。

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

确定之後再來看:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

修改之後,是不是瞬間清晰了很多?另外有人( 貌似是看雪論壇上的)還總結了所有JNIEnv方法對應的數字,位址以及方法聲明:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

2、Shirt+F12快捷鍵,速度打開so中所有的字元串内容視窗

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

有時候,字元串是一個非常重要的資訊,特别是對于破解的時候,可能就是密碼,或者是密碼庫資訊。

3、Ctrl+S快捷鍵,有兩個用途,在正常打開so檔案的IDA View視圖的時候,可以檢視so對應的Segement資訊

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

可以快速得到,一個段的開始位置和結束位置,不過這個位置是相對位置,不是so映射到記憶體之後的位置,關于so中的段資訊,不了解的同學可以參看這篇文章:Android中so檔案格式詳解 這篇文章介紹的很很清楚了,這裡就不在作介紹了。

當在調試頁面的時候,ctrl+s可以快速定位到我們想要調試的so檔案映射到記憶體的位址:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

因為一般一個程式,肯定會包含多個so檔案的,比如系統的so就有好多的,一般都是在/system/lib下面,當然也有我們自己的so,這裡我們看到這裡的開始位置和結束位置就是這個so檔案映射到記憶體中:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這裡我們可以使用cat指令檢視一個程序的記憶體映射資訊:cat /proc/[pid]/maps

我們看到映射資訊中有多so檔案,其實這個不是多個so檔案,而是so檔案中對應的不同Segement資訊被映射到記憶體中的,一般是代碼段,資料段等,因為我們需要調試代碼,是以我們隻關心代碼段,代碼段有一個特點就是具有執行權限x,是以我們隻需要找到權限中有x的那段資料即可。

4、G快捷鍵:在IDA調試頁面的時候,我們可以使用S鍵快速跳轉到指定的記憶體位置

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這裡的跳轉位址,是可以算出來的,比如我現在想跳轉到A函數,然後下斷點,那麼我們可以使用上面說到的ctrl+s查找到so檔案的記憶體開始的基位址,然後再用IDA View中檢視A函數對應的相對位址,相加就是絕對位址,然後跳轉到即可,比如這裡的:

Java_cn_wjdiankong_encryptdemo_MainActivity_isEquals 函數的IDA View中的相對位址(也就是so檔案的位址):E9C

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

上面看到so檔案映射到記憶體的基位址:74FE4000

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

那麼跳轉位址就是:74FE4000+E9C=74FE4E9C

注意:

一般這裡的基位址隻要程式沒有退出,在運作中,那麼他的值就不會變,因為程式的資料已經加載到記憶體中了,基位址不會變的,除非程式退出,又重新運作把資料加載記憶體中了,同時相對位址是永遠不會變的,隻有在修改so檔案的時候,檔案的大小改變了,可能相對位址會改變,其他情況下不會改變,相對位址就是資料在整個so檔案中的位置。

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這裡我們可以看到函數映射到記憶體中的絕對位址了。

注意:

有時候我們發現跳轉到指定位置之後,看到的全是DCB資料,這時候我們選擇函數位址,點選P鍵就可以看到arm指令源碼了:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

5、調試快捷鍵:F8單步調試,F7單步進入調試

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

上面找到函數位址之後,我們可以下斷點了,下斷點很簡單,點選簽名的綠色圈點,變成紅色條目即可,然後我們可以點選F9快捷鍵,或者是點選運作按鈕,即可運作程式:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

其中還有暫停和結束按鈕。我們運作之後,然後在點選so的native函數,觸發斷點邏輯:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這時候,我們看到進入調試界面,點選F8可以單步調試,看到有一個PC訓示器,其實在arm中PC是一個特殊的寄存器,用來存儲目前指令的位址,這個下面會介紹到。

好了到這裡,我們就大緻說了一下關于IDA在調試so檔案的時候,需要用到的快捷鍵:

1、Shift+F12快速檢視so檔案中包含的字元串資訊

2、F5快捷鍵可以将arm指令轉化成可讀的C代碼,這裡同時可以使用Y鍵,修改JNIEnv的函數方法名

3、Ctrl+S有兩個用途,在IDA View頁面中可以檢視so檔案的所有段資訊,在調試頁面可以檢視程式所有so檔案映射到記憶體的基位址

4、G鍵可以在調試界面,快速跳轉到指定的絕對位址,進行下斷點調試,這裡如果跳轉到目的位址之後,發現是DCB資料的話,可以在使用P鍵,進行轉化即可,關于DCB資料,下面會介紹的。

5、F7鍵可以單步進入調試,F8鍵可以單步調試

第二、常用的ARM指令集知識

我們在上面看到IDA打開so之後,看到的是純種的彙編指令代碼,是以這就要求我們必須會看懂彙編代碼,就類似于我們在調試Java層代碼的時候一樣,必須會smali文法,慶幸的是,這兩種文法都不是很複雜,是以我們知道一些大體的文法和指令就可以了,下面我們來看看arm指令中的尋址方式,寄存器,常用指令,看完這三個知識點,我們就會對arm指令有一個大體的了解,對于看arm指令代碼也是有一個大體的認知了。

1、arm指令中的尋址方式

1>. 立即數尋址

也叫立即尋址,是一種特殊的尋址方式,操作數本身包含在指令中,隻要取出指令也就取到了操作數。這個操作數叫做立即數,對應的尋址方式叫做立即尋址。例如:

MOV R0,#64   ;R0  ← 64

2>. 寄存器尋址

寄存器尋址就是利用寄存器中的數值作為操作數,也稱為寄存器直接尋址。

例如:ADD R0,R1, R2   ;R0  ← R1 + R2

3>. 寄存器間接尋址

寄存器間接尋址就是把寄存器中的值作為位址,再通過這個位址去取得操作數,操作數本身存放在存儲器中。

例如:

LDR R0,[R1] ;R0 ←[R1]

4>. 寄存器偏移尋址

這是ARM指令集特有的尋址方式,它是在寄存器尋址得到操作數後再進行移位操作,得到最終的操作數。

例如:

MOV R0,R2,LSL  #3   ;R0 ← R2 * 8 ,R2的值左移3位,結果賦給R0。

5>. 寄存器基址變址尋址

寄存器基址變址尋址又稱為基址變址尋址,它是在寄存器間接尋址的基礎上擴充來的。它将寄存器(該寄存器一般稱作基址寄存器)中的值與指令中給出的位址偏移量相加,進而得到一個位址,通過這個位址取得操作數。

例如:

LDR R0,[R1,#4] ;R0 ←[R1 + 4],将R1的内容加上4形成操作數的位址,取得的操作數存入寄存器R0中。

6>. 多寄存器尋址

這種尋址方式可以一次完成多個寄存器值的傳送。例如:

LDMIA  R0,{R1,R2,R3,R4} ;R1←[R0],R2←[R0+4],R3←[R0+8],R4←[R0+12]

7>. 堆棧尋址

堆棧是一種資料結構,按先進後出(First In Last Out,FILO)的方式工作,使用堆棧指針(Stack Pointer, SP)訓示目前的操作位置,堆棧指針總是指向棧頂。

堆棧尋址舉例如下:

STMFD  SP!,{R1-R7, LR} ;将R1-R7, LR壓入堆棧。滿遞減堆棧。

LDMED  SP!,{R1-R7, LR} ;将堆棧中的資料取回到R1-R7, LR寄存器。空遞減堆棧。

2、ARM中的寄存器

R0-R3:用于函數參數及傳回值的傳遞

R4-R6, R8, R10-R11:沒有特殊規定,就是普通的通用寄存器

R7:棧幀指針(Frame Pointer).指向前一個儲存的棧幀(stack frame)和連結寄存器(link register, lr)在棧上的位址。

R9:作業系統保留

R12:又叫IP(intra-procedure scratch )

R13:又叫SP(stack pointer),是棧頂指針

R14:又叫LR(link register),存放函數的傳回位址。

R15:又叫PC(program counter),指向目前指令位址。

3、ARM中的常用指令含義

ADD 加指令

SUB  減指令

STR 把寄存器内容存到棧上去

LDR 把棧上内容載入一寄存器中

.W 是一個可選的指令寬度說明符。它不會影響為此指令的行為,它隻是確定生成 32 位指令。Infocenter.arm.com的詳細資訊

BL 執行函數調用,并把使lr指向調用者(caller)的下一條指令,即函數的傳回位址

BLX 同上,但是在ARM和thumb指令集間切換。

CMP 指令進行比較兩個操作數的大小

4、ARM指令簡單代碼段分析

C代碼:

#include <stdio.h>

int func(int a, int b, int c, int d, int e, int f)

{

    int g = a + b + c + d + e + f;

    return g;

}

對應的ARM指令:

add r0, r1  将參數a和參數b相加再把結果指派給r0

ldr.w r12, [sp]  把最的一個參數f從棧上裝載到r12寄存器

add r0, r2  把參數c累加到r0上

ldr.w r9, [sp, #4]  把參數e從棧上裝載到r9寄存器

add r0, r3  累加d累加到r0

add r0, r12  累加參數f到r0

add r0, r9  累加參數e到r0

三、構造so案例

好了,關于ARM指令的相關知識,就介紹這麼多了,不過我們在調試分析的時候,肯定不能做到全部的了解,因為本身ARM指令文法就比較複雜,不過幸好大學學習了彙編語言,是以稍微能看懂點,如果不懂彙編的同學那就可能需要補習一下了,因為我們在使用IDA分析so檔案的時候,不會彙編的話,那是肯定行不通的,是以我們必須要看懂彙編代碼的,如果遇到特殊指令不了解的同學,可以網上搜一下即可。

上面我們的準備知識做完了,一個是IDA工具的時候,一個是ARM指令的了解,下面我們就來開始操刀了,為了友善開始,我們先自己寫一個簡單的Android native層代碼,然後進行IDA進行分析即可。

這裡可以使用AndroidStudio中進行建立一個簡單工程,然後建立JNI即可:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這裡順便簡單說一下AndroidStudio中如何進行NDK的開發吧:

第一步:在工程中建立jni目錄

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

第二步:使用javah生成native的頭檔案

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

注意:

javah執行的目錄,必須是類包名路徑的最上層,然後執行:

javah 類全名

注意沒有字尾名java哦

第三步:配置項目的NDK目錄

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

選擇子產品的設定選線:Open Module Settings:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

設定NDK目錄即可

第四步:copy頭檔案到jni目錄下,然後配置gradle中的ndk選項

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這裡隻需要設定編譯之後的子產品名,就是so檔案的名稱,需要産生那幾個平台下的so檔案,還有就是需要用到的lib庫,這裡我們看到我們用到了Android中列印log的庫檔案。

第五步:編譯運作,在build目錄下生成指定的so檔案,copy到工程的libs目錄下即可

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

好了,到這裡我們就快速的在AndroidStudio中建立了一個Native項目,這裡關于native項目的代碼不想解釋太多,就是Java層

傳遞了使用者輸入的密碼,然後native做了校驗過程,把校驗結果傳回到Java層即可:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結
Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

具體的校驗過程這裡不再解釋了。我們運作項目之後,得到apk檔案,那麼下面我們就開始我們的破解旅程了

四、開始破解so檔案

開始破解我們編譯之後的apk檔案

第一、首先我們可以使用最簡單的壓縮軟體,打開apk檔案,然後解壓出他的so檔案

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

我們得到libencrypt.so檔案之後,使用IDA打開它:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

我們知道一般so中的函數方法名都是:Java_類名_方法名

那麼這裡我們直接搜:Java關鍵字即可,或者使用jd-gui工具找到指定的native方法

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

輕按兩下,即可在右邊的IDA View頁面中看到Java_cn_wjdiankong_encryptdemo_MainActivity_isEquals 函數的指令代碼:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

我們可以簡單的分析一下這段指令代碼:

1>、PUSH {r3-r7,lr} 是儲存r3,r4,r5,r6,r7,lr 的值到記憶體的棧中,那麼最後當執行完某操作後,你想傳回到lr指向的地方執行,當然要給pc了,因為pc保留下一條CPU即将執行的指令,隻有給了pc,下一條指令才會執行到lr指向的地方

pc:程式寄存器,保留下一條CPU即将執行的指令

lr: 連接配接傳回寄存器,保留函數傳回後,下一條應執行的指令 

這個和函數最後面的POP {r3-r7,pc}是相對應的。

2>、然後是調用了strlen,malloc,strcpy等系統函數,在每次使用BLX和BL指令調用這些函數的時候,我們都發現了一個規律:就是在調用他們之前一般都是由MOV指令,用來傳遞參數值的,比如這裡的R5裡面存儲的就是strlen函數的參數,R0就是is_number函數的參數,是以我們這樣分析之後,在後面的動态調試的過程中可以得到函數的入口參數值,這樣就能得到一些重要資訊

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

3>、在每次調用有傳回值的函數之後的指令,一般都是比較指令,比如CMP,CBZ,或者是strcmp等,這裡是我們破解的突破點,因為一般加密再怎麼牛逼,最後比較的參數肯定是正确的密碼(或者是正确的加密之後的密碼)和我們輸入的密碼(或者是加密之後的輸入密碼),我們在這裡就可以得到正确密碼,或者是加密之後的密碼:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

到這裡,我們就分析完了native層的密碼比較函數:Java_cn_wjdiankong_encryptdemo_MainActivity_isEquals

如果覺得上面的ARM指令看的吃力,可以使用F5鍵,檢視他的C語言代碼:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

我們這裡看到其實有兩個函數是核心點:

1>is_number函數,這個函數我們看名字應該猜到是判斷是不是數字,我們可以使用F5鍵,檢視他對應的C語言代碼:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這裡簡單一看,主要是看return語句和if判斷語句,看到這裡有一個循環,然後擷取_BYTE*這裡位址的值,并且自增加一,然後存到v2中,如果v3為'\0'的話,就結束循環,然後做一次判斷,就是v2-48是否大于9,那麼這裡我們知道48對應的是ASCII中的數字0,是以這裡可以确定的是就是:用一個循環周遊_BYTE*這裡存的字元串是否為數字串。

2>get_encrypt_str函數,這個函數我們看到名字可以猜測,他是擷取我們輸入的密碼加密之後的值,再次使用F5快捷鍵檢視:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這裡我們看到,首先是一個if語句,用來判斷傳遞的參數是否為NULL,如果是的話,直接傳回,不是的話,使用strlen函數擷取字元串的長度儲存到v2中,然後使用malloc申請一塊堆記憶體,首指針儲存到result,大小是v2+1也就是傳遞進來的字元串長度+1,然後就開始進入循環,首指針result,指派給i指針,開始循環,v3是通過v1-1擷取到的,就是函數傳遞進來字元串的位址,那麼v6就是擷取傳遞進來字元串的字元值,然後減去48,指派給v7,這裡我們可以猜到了,這裡想做字元轉化,把char轉化成int類型,繼續往下看,如果v6==48的話,v7=1,也就是說這裡如果遇到字元'0',就指派1,在往下看,看到我們上面得到的v7值,被用來取key_src數組中的值,那麼這裡我們輕按兩下key_src變量,就跳轉到了他的值地方,果不其然,這裡儲存了一個字元數組,看到他的長度正好是18,那麼這裡我們應該明白了,這裡通過傳遞進來的字元串,循環周遊字元串,擷取字元,然後轉化成數字,在倒序擷取key_src中的字元,儲存到result中。然後傳回。

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

好了,到這裡我們就分析完了這兩個重要的函數的功能,一個是判斷輸入的内容是否為數字字元串,一個是通過輸入的内容擷取密碼内容,然後和正确的加密密碼:ssBCqpBssP 作比較。

第二、開始使用IDA進行調試設定

那麼下面我們就用動态調試來跟蹤傳入的字元串值,和加密之後的值,這裡我們看到沒有列印log的函數,是以很難知道具體的參數和寄存器的值,是以這裡需要開始調試,得知每個函數執行之後的寄存器的值,我們在用IDA進行調試so的時候,需要以下準備步驟:

1、在IDA安裝目錄下擷取android_server指令檔案

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

在IDA安裝目錄\dbgsrv\android_server,這個檔案是幹嘛的呢?他怎麼運作呢?下面來介紹一下:

我們是否還記得之前一篇文章:Android中run-as指令帶來的安全問題  這篇文章中我們介紹了Android中的調試原理,其實是使用gdb和gdbserver來做到的,gdb和gdbserver在調試的時候,必須注入到被調試的程式程序中,但是非root裝置的話,注入别的程序中隻能借助于run-as這個指令了,是以我們知道,如果要調試一個應用程序的話,必須要注入他内部,那麼IDA調試so也是這個原理,他需要注入(Attach附加)程序,才能進行調試,但是IDA沒有自己弄了一個類似于gdbserver這樣的工具,那就是android_server了,是以他需要運作在裝置中,保證和PC端的IDA進行通信,比如擷取裝置的程序資訊,具體程序的so記憶體位址,調試資訊等。

是以我們把android_server儲存到裝置的/data目錄下,修改一下他的運作權限,然後必須在root環境下運作,因為他要做注入程序操作,必須要root。

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

注意:

這裡把他放在了/data目錄下,然後./android_server運作,這裡提示了IDA Android 32-bit,是以後面我們在打開IDA的時候一定要是32位的IDA,不是64位的,不然儲存,IDA在安裝之後都是有兩個可執行的程式,一個是32位,一個是64位的,如果沒打開正确會報這樣的錯誤:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

同樣還有一類問題:error: only position independent executables (PIE) are supported

這個主要是Android5.0以上的編譯選項預設開啟了pie,在5.0以下編譯的原生應用不能運作,有兩種解決辦法,一種是用Android5.0以下的手機進行操作,還有一種就是用IDA6.6+版本即可。

然後我們再看,這裡開始監聽了裝置的23946端口,那麼如果要想讓IDA和這個android_server進行通信,那麼必須讓PC端的IDA也連上這個端口,那麼這時候就需要借助于adb的一個指令了:

adb forward tcp:遠端裝置端口号(進行調試程式端) tcp:本地裝置端口(被調試程式端)

那麼這裡,我們就可以把android_server端口轉發出去:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

然後這時候,我們隻要在PC端使用IDA連接配接上23946這個端口就可以了,這裡面有人好奇了,為什麼遠端端的端口号也是23946,因為後面我們在使用IDA進行連接配接的時候,發現IDA他把這個端口設定死了,就是23946,是以我們沒辦法自定義這個端口了。

我們可以使用netstat指令檢視端口23946的使用情況,看到是ida在使用這個端口

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

2、上面就準備好了android_server,運作成功,下面就來用IDA進行嘗試連接配接,擷取資訊,進行程序附加注入

我們這時候需要在打開一個IDA,之前打開一個IDA是用來分析so檔案的,一般用于靜态分析,我們要調試so的話,需要在打開一個IDA來進行,是以這裡一般都是需要打開兩個IDA,也叫作雙開IDA操作。動靜結合政策。

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這裡記得選擇Go這個選項,就是不需要打開so檔案了,進入是一個空白頁:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

我們選擇Debugger選項,選擇Attach,看到有很多debugger,是以說IDA工具真的很強大,做到很多debugger的相容,可以調試很多平台下的程式。這裡我們選擇Android debugger:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這裡看到,端口是寫死的:23946,不能進行修改,是以上面的adb forward進行端口轉發的時候必須是23946。這裡PC本地機就是調試端,是以host就是本機的ip位址:127.0.0.1,點選确定:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這裡可以看到裝置中所有的程序資訊就列舉出來的,其實都是android_server幹的事,擷取裝置程序資訊傳遞給IDA進行展示。

注意:

如果我們當初沒有用root身份去運作android_server:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這裡就會IDA是不會列舉出裝置的程序資訊:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

還有一個注意的地方,就是IDA和android_server一定要保持一緻。

我們這裡可以ctrl+F搜尋我們需要調試的程序,當然這裡我們必須運作起來我們需要調試的程序,不然也是找不到這個程序的

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

輕按兩下程序,即可進入調試頁面:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這裡為什麼會斷在libc.so中呢?

android系統中libc是c層中最基本的函數庫,libc中封裝了io、檔案、socket等基本系統調用。所有上層的調用都需要經過libc封裝層。是以libc.so是最基本的,是以會斷在這裡,而且我們還需要知道一些常用的系統so,比如linker:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

我們知道,這個linker是用于加載so檔案的子產品,是以後面我們在分析如何在.init_array處下斷點

還有一個就是libdvm.so檔案,他包含了DVM中所有的底層加載dex的一些方法:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

我們在後面動态調試需要dump出加密之後的dex檔案,就需要調試這個so檔案了。

3、找到函數位址,下斷點,開始調試

我們使用Ctrl+S找到需要調試so的基位址:74FE4000

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

然後通過另外一個IDA打開so檔案,檢視函數的相對位址:E9C

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

那麼得到了函數的絕對位址就是:74FE4E9C,使用G鍵快速跳轉到這個絕對位址:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

跳轉到指定位址之後,開始下斷點,點選最左邊的綠色圓點即可下斷點:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

然後點選左上角的綠色按鈕,運作,也可以使用F9鍵運作程式:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

我們點選程式中的按鈕:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

觸發native函數的運作:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

看到了,進入調試階段了,這時候,我們可以使用F8進行單步調試,F7進行單步進入調試:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

我們點選F8進行單步調試,達到is_number函數調用出,看到R0是出入的參數值,我們可以檢視R0寄存器的内容,然後看到是123456,這個就是Java層傳入的密碼字元串,接着往下走:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這裡把is_number函數傳回值儲存到R0寄存中,然後調用CBZ指令,判斷是否為0,如果為0就跳轉到locret_74FE4EEC處,檢視R0寄存器的值不是0,繼續往下走:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

看到了get_encrypt_str函數的調用,函數的傳回值儲存在R1寄存器中,檢視内容:zytyrTRA*B了,那麼看到,上層傳遞的:123456=》zytyrTRA*B了,前面我們靜态分析了get_encrypt_str函數的邏輯,繼續往下看:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

看到了,這裡把上面得到的字元串和ssBCqpBssP作比較,那麼這裡ssBCqpBssP就是正确的加密密碼了,那麼我們現在的資源是:

正确的加密密碼:ssBCqpBssP,加密密鑰庫:zytyrTRA*BniqCPpVs,加密邏輯get_encrypt_str

那麼我們可以寫一個逆向的加密方法,去解析正确的加密密碼得到值即可,這裡為了給大家一個破解的機會,這裡就不公布正确答案了,這個apk我随後會上傳,手癢的同學可以嘗試破解一下。

加密apk下載下傳位址:http://download.csdn.net/detail/jiangwei0910410003/9531638

第三、總結IDA調試的流程

到這裡,我們就分析了如何破解apk的流程,下面來總結一下:

1、我們通過解壓apk檔案,得到對應的so檔案,然後使用IDA工具打開so,找到指定的native層函數

2、通過IDA中的一些快捷鍵:F5,Ctrl+S,Y等鍵來靜态分析函數的arm指令,大緻了解函數的執行流程

3、再次打開一個IDA來進行調試so

1>将IDA目錄中的android_server拷貝到裝置的指定目錄下,修改android_server的運作權限,用Root身份運作android_server

2>使用adb forward進行端口轉發,讓遠端調試端IDA可以連接配接到被調試端

3>使用IDA連接配接上轉發的端口,檢視裝置的所有程序,找到我們需要調試的程序。

4>通過打開so檔案,找到需要調試的函數的相對位址,然後在調試頁面使用Ctrl+S找到so檔案的基位址,相加之後得到絕對位址,使用G鍵,跳轉到函數的位址處,下好斷點。點選運作或者F9鍵。

5>觸發native層的函數,使用F8和F7進行單步調試,檢視關鍵的寄存器中的值,比如函數的參數,和函數的傳回值等資訊

總結就是:在調試so的時候,需要雙開IDA,動靜結合分析。

五、使用IDA來解決反調試問題

那麼到這裡我們就結束了我們這期的破解旅程了?答案是否定的,因為我們看到上面的例子其實是我自己先寫了一個apk,目的就是為了給大家示範,如何使用IDA來進行動态調試so,那麼下面我們還有一個操刀動手的案例,就是2014年,阿裡安全挑戰賽的第二題:AliCrackme_2:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

阿裡真會制造氛圍,還記得我們破解的第一題嗎,這次看到了第二題,好吧,下面來看看破解流程吧:

首先使用aapt指令檢視他的AndroidManifest.xml檔案,得到入口的Activity類:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

然後使用dex2jar和jd-gui檢視他的源碼類:com.yaotong.crackme.MainActivity:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結
Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

看到,他的判斷,是securityCheck方法,是一個native層的,是以這時候我們去解壓apk檔案,擷取他的so檔案,使用IDA打開檢視native函數的相對位址:11A8

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這裡的ARM指令代碼不在分析了,大家自行檢視即可,我們直接進入調試即可:

在打開一個IDA進行關聯調試:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

選擇對應的調試程序,然後确定:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

使用Ctrl+S鍵找到對應so檔案的基位址:74EA9000

和上面得到的相對位址相加得到絕對位址:74EA9000+11A8=74EAA1A8  使用G鍵直接跳到這個位址:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

下個斷點,然後點選F9運作程式:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

擦,IDA退出調試頁面了,我們再次進入調試頁面,運作,還是退出調試頁面了,好了,這下蛋疼了,沒法調試了。

這裡其實是阿裡做了反調試偵查,如果發現自己的程式被調試了,就直接退出程式,那麼這裡有問題了,為什麼知道是反調試呢?這個主要還是看後續自己的破解經驗了,沒技術可言,還有一個就是阿裡如何做到的反調試政策的,這裡限于篇幅,隻是簡單介紹一下原理:

前面說到,IDA是使用android_server在root環境下注入到被調試的程序中,那麼這裡用到一個技術就是Linux中的ptrace,關于這個這裡也不解釋了,大家可以自行的去搜一下ptrace的相關知識,那麼Android中如果一個程序被另外一個程序ptrace了之後,在他的status檔案中有一個字段:TracerPid 可以辨別是被哪個程序trace了,我們可以使用指令檢視我們的被調試的進行資訊:

status檔案在:/proc/[pid]/status

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

看到了,這裡的程序被9187程序trace了,我們在用ps指令看看9187是哪個程序:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

果不其然,是我們的android_server程序,好了,我們知道原理了,也大緻猜到了阿裡在底層做了一個循環檢測這個字段如果不為0,那麼代表自己程序在被人trace,那麼就直接停止退出程式,這個反檢測技術用在很多安全防護的地方,也算是一個重要的知識點了。

那麼下面就來看看如何應對這個反調試?

我們剛剛看到,隻要一運作程式,就退出了調試界面,說明,這個循環檢測程式執行的時機非常早,那麼我們現在知道的最早的兩個時機是:一個是.init_array,一個是JNI_OnLoad

.init_array是一個so最先加載的一個段資訊,時機最早,現在一般so解密操作都是在這裡做的

JNI_OnLoad是so被System.loadLibrary調用的時候執行,他的時機要早于哪些native方法執行,但是沒有.init_array時機早

那麼知道了這兩個時機,下面我們先來看看是不是在JNI_OnLoad函數中做的政策,是以我們需要先動态調試JNI_OnLoad函數

我們既然知道了JNI_OnLoad函數的時機,如果阿裡把檢測函數放在這裡的話,我們不能用之前的方式去調試了,因為之前的那種方式時機太晚了,隻要運作就已經執行了JNI_OnLoad函數,是以就會退出調試頁面,幸好這裡IDA提供了在so檔案load的時機,我們隻需要在Debug Option中設定一下就可以了:

在調試頁面的Debugger 選擇 Debugger Option選項:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

然後勾選Suspend on library load/unload即可

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這樣設定之後,還是不行,因為我們程式已經開始運作,就在static代碼塊中加載so檔案了,static的時機非常早,是以這時候,我們需要讓程式停在加載so檔案之前即可。

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

那麼我想到的就是添加代碼waitForDebugger代碼了,這個方法就是等待debug,我們還記得在之前的調試smali代碼的時候,就是用這種方式讓程式停在了啟動出,然後等待我們去用jdb進行attach操作。

那麼這一次我們可以在System.loadLibrary方法之前加入waitForDebugger代碼即可,但是這裡我們不這麼幹了,還有一種更簡單的方式就是用am指令,本身am指令可以啟動一個程式,當然可以用debug方式啟動:

adb shell am start -D -n com.yaotong.crackme/.MainActivity

這裡一個重要參數就是-D,用debug方式啟動

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

運作完之後,裝置是出于一個等待Debugger的狀态:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這時候,我們再次使用IDA進行程序的附加,然後進入調試頁面,同時設定一下Debugger Option選項,然後定位到JNI_OnLoad函數的絕對位址。

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

但是我們發現,這裡沒有RX權限的so檔案,說明so檔案沒有加載到記憶體中,想一想還是對的,以為我們現在的程式是wait Debugger,也就是還沒有走System.loadLibrary方法,so檔案當然沒有加載到記憶體中,是以我們需要讓我們程式跑起來,這時候我們可以使用jdb指令去attach等待的程式,指令如下:

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

其實這條指令的功能類似于,我們前一篇說到用Eclipse調試smali源碼的時候,在Eclipse中設定遠端調試工程一樣,選擇Attach方式,調試機的ip位址和端口,還記得8700端口是預設的端口,但是我們運作這個指令之後,出現了一個錯誤:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

擦,無法連接配接到目标的VM,那麼這種問題大部分都出現在被調試程式不可調試,我們可以檢視apk的android:debuggable屬性:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

果不其然,這裡沒有debug屬性,是以這個apk是不可以調試的,是以我們需要添加這個屬性,然後在回編譯即可:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

回編譯:java -jar apktool.jar b -d out -o debug.apk

簽名apk:java -jar .\sign\signapk.jar .\sign\testkey.x509.pem .\sign\testkey.pk8 debug.apk debug.sig.apk

然後在次安裝,使用am 指令啟動:

第一步:運作:adb shell am start -D -n com.yaotong.crackme/.MainActivity

出現Debugger的等待狀态

第二步:啟動IDA 進行目标程序的Attach操作

第三步:運作:jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

第三步:設定Debugger Option選項

第四步:點選IDA運作按鈕,或者F9快捷鍵,運作

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

看到了,這次jdb成功的attach住了,debug消失,正常運作了,

但是同時彈出了一個選擇提示:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這時候,不用管它,全部選擇取消按鈕,然後就運作到了linker子產品了:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這時候,說明so已經加載進來了,我們再去擷取JNI_OnLoad函數的絕對位址

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

Ctrl+S查找到了基位址:7515A000

用靜态方式IDA打開so檢視相對位址:1B9C

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

相加得到絕對位址:7515A000+1B9C=7515BB9C,然後點選S鍵,跳轉:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

跳轉到指定的函數位置:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這時候再次點選運作,進入了JNI_OnLoad處的斷點:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

下面咋們就開始單步調試了,但是當我們每次到達BLX R7這條指令執行完之後,就JNI_OnLoad就退出了:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

經過好幾次嘗試都是一樣的結果,是以我們發現這個地方有問題,可能就是反調試的地方了

我們再次進入調試,看見BLX跳轉的地方R7寄存器中是pthread_create函數,這個是linux中建立一個線程的方法

是以阿裡的反調試就在這裡開啟一個線程進行輪訓操作,去讀取/proc/[pid]/status檔案中的TrackerPid字段值,如果發現不為0,就表示有人在調試本應用,在JNI_OnLoad中直接退出。其實這裡可以再詳細進入檢視具體代碼實作的,但是這裡限于篇幅問題,不詳細解釋了,後續在寫一篇文章我們自己可以實作這種反調試機制的。本文的重點是能夠動态調試即可。

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

那麼問題找到了,我們現在怎麼操作呢?

其實很簡單,我們隻要把BLX R7這段指令幹掉即可,如果是smali代碼的話,我們可以直接删除這行代碼即可,但是so檔案不一樣,他是彙編指令,如果直接删除這條指令的話,檔案會發生錯亂,因為本身so檔案就有固定的格式,比如很多Segement的内容,每個Segement的偏移值也是有儲存的,如果這樣去删除會影響這些偏移值,會破壞so檔案格式,導緻so加載出錯的,是以這裡我們不能手動的去删除這條指令,我們還有另外一種方法,就是把這條指令變成空指令,在彙編語言中,nop指令就是一個空指令,他什麼都不幹,是以這裡我們直接改一下指令即可,arm中對應的nop指令是:00 00 00 00

那麼我們看到BLX R7對應的指令位置為:1C58

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

檢視他的Hex内容是:37 FF 2F E1

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

我們可以使用一些二進制檔案軟體進行内容的修改,這裡使用010Editor工具進行修改:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這裡直接修改成00 00 00 00:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這時候,儲存修改之後的so檔案,我們再次使用IDA進行打開檢視:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

哈哈,指令被修改成了:ANDEQ R0,R0,R0了

那麼修改了之後,我們在替換原來的so檔案,再次重新回編譯,簽名安裝,再次按照之前的邏輯給主要的加密函數下斷點,這裡不需要在給JNI_OnLoad函數下斷點了,因為我們已經修改了反調試功能了,是以這裡我們隻需要按照這麼簡單幾步即可:

第一步:啟動程式

第二步:使用IDA進行程序的attach

第三步:找到Java_com_yaotong_crackme_MainActivity_securityCheck函數的絕對位址

第四步:打上斷點,點選運作,進行單步調試

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

看到了吧,這裡我們可以單步調試進來了啦啦,說明我們修改反調試指令成功了。

下面就繼續F8單步調試:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

調試到這裡,發現一個問題,就是CMP指令之後,BNE 指令就開始跳轉到loc_74FAF2D0處了,那麼我們就可以猜到了,CMP指令比較的應該就是我們輸入的密碼和正确的密碼,我們再次從新調試,看看R3和R1寄存器的值

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

看到了這裡的R3寄存器的值就是用寄存器尋址方式,指派字元串的,這裡R2寄存器就是存放字元串的位址,我們看到的内容是aiyou...但是這裡肯定不是全部字元串,因為我們沒看到字元串的結束符:'\0',我們點選R2寄存器,進入檢視完整内容:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這裡是全部内容:aiyou,bucuoo

我們繼續檢視R1寄存器的内容:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

這裡也是同樣用寄存器尋址,R0寄存器存儲的是R1中字元串的位址,我們看到這裡的字元串内容是:jiangwei

這個就是我輸入的内容,那麼這裡就可以豁然開朗了,密碼是上面的:aiyou,bucuoo

我們再次輸入這個密碼:

Android逆向之旅---動态方式破解apk進階篇(IDA調試so源碼) 一、前言 二、知識準備 三、構造so案例 四、開始破解so檔案 五、使用IDA來解決反調試問題 六、技術總結

哈哈哈,破解成功啦啦~~

手癢的同學可以下載下傳項目來玩玩~~

項目下載下傳:http://download.csdn.net/detail/jiangwei0910410003/9531957

六、技術總結

到這裡我們算是講解完了如何使用IDA來調試so代碼,進而破解apk的知識了,因為這裡IDA工具比較複雜,是以這篇文章篇幅有點長,是以同學們可以多看幾遍,就差不多了。下面我們來整理一下這篇文章中涉及到的知識點吧:

第一、IDA中的常用快捷鍵使用

1、Shift+F12可以快速檢視so中的常量字元串内容,有時候,字元串内容是一個很大的突破點

2、使用強大的F5鍵,可以檢視arm彙編指令對應的C語言代碼,同時可以使用Y鍵,進行JNIEnv*方法的還原

3、使用Ctrl+S鍵,可以在IDA View頁面中檢視so的所有段資訊,在調試頁面可以查找對應so檔案映射到記憶體的基位址,這裡我們還可以使用G鍵,進行位址的跳轉

4、使用F8進行單步調試,F7進行單步跳入調試,同時可以使用F9運作程式

第二、ARM彙編指令相關知識

1、了解了幾種尋址方式,有利于我們簡單的讀懂arm彙編指令代碼

2、了解了arm中的幾種寄存器的作用,特别是PC寄存器

3、了解了arm中常用的指令,比如:MOV,ADD,SUB,LDR,STR,CMP,CBZ,BL,BLX

第三、使用IDA進行調試so的步驟,這裡分兩種情況

1、IDA調試無反調試的so代碼步驟:

1》把IDA安裝目錄中的android_server拷貝到裝置的指定目錄中,修改android_server的權限,并且用root方式運作起來,監聽23946端口

2》使用adb forward指令進行端口的轉發,将裝置被調試端的端口轉發到遠端調試端中

3》雙開IDA工具,一個是用來打開so檔案,進行檔案分析,比如簡單分析arm指令代碼,知道大體邏輯,還有就是找到具體函數的相對位置等資訊,還有一個IDA是用來調試so檔案的,我們在Debugger選項中設定Debugger Option,然後附加需要調試的程序

4》進入調試頁面之後,通過Ctrl+S和G快捷鍵,定位到需要調試的關鍵函數,進行下斷點

5》點選運作或者快捷鍵F9,觸發程式的關鍵函數,然後進入斷點,使用F8單步調試,F7單步跳入調試,在調試的過程中主要觀察BL,BLX指令,以及CMP和CBZ等比較指令,然後在檢視具體的寄存器的值。

2、IDA調試有反調試的so代碼步驟:

1》檢視apk是否為可調式狀态,可以使用aapt指令檢視他的AndroidManifest.xml檔案中的android:debuggeable屬性是否為true,如果不是debug狀态,那麼就需要手動的添加這個屬性,然後回編譯,在簽名打包從新安裝

2》使用adb shell am start -D -n com.yaotong.crackme/.MainActivity 指令啟動程式,出于wait Debug狀态

3》打開IDA,進行程序附加,進入到調試頁面

4》使用 jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700  指令attach之前的debug狀态,讓程式正常運作

5》設定Debug Option選項,設定Suspend on library start/exit/Suspend on library load/unload/Suspend on process entry point選項

6》點選運作按鈕或者F9鍵,程式運作停止在linker子產品中,這時候表示so檔案加載進來了,我們通過Ctrl+S和G鍵跳轉到JNI_OnLoad函數出,進行下斷點

7》然後繼續運作,進入JNI_OnLoad斷點處,使用F8進行單步調試,F7進行單步跳入調試,找到反調試代碼處

8》然後使用二進制軟體修改反調試代碼為nop指令,即00值

9》修改之後,在替換原來的so檔案,進行回編譯,從新簽名打包安裝即可

10》按照上面的無反調試的so代碼步驟即可

第四、學習了如何做到反調試檢測

現在很多應用防止别的程序調試或者注入,通常會用自我檢測裝置,原理就是循環檢測/proc/[mypid]/status檔案,檢視他的TracerPid字段是否為0,如果不為0,表示被其他程序trace了,那麼這時候就直接退出程式。因為現在的IDA調試時需要程序的注入,程序注入現在都是使用Linux中的ptrace機制,那麼這裡的TracePid就可以記錄trace的pid,我們可以發現我們的程式被那個程序注入了,或者是被他在調試。進而采取一些措施。

第五、IDA調試的整體原理

我們知道了上面的IDA調試步驟,其實我們可以仔細想一想,他的調試原理大緻是這樣的:

首先他得在被調試端安放一個程式,用于IDA端和調試裝置通信,這個程式就是android_server,因為要附加程序,是以這個程式必須要用root身份運作,這個程式起來之後,就會開啟一個端口23946,我們在使用adb forward進行端口轉發到遠端調試端,這時候IDA就可以和調試端的android_server進行通信了。後面擷取裝置的程序清單,附加程序,傳遞調試資訊,都可以使用這個通信機制完成即可。IDA可以擷取被調試的程序的記憶體資料,一般是在 /proc/[pid]maps 檔案中,是以我們在使用Ctrl+S可以檢視所有的so檔案的基位址,可以周遊maps檔案即可做到。

破解法則:時刻需要注意關鍵的BL/BLX等跳轉指令,在他們執行完之後,肯定會有一些CMP/CBZ等比較指令,這時候就可以檢視重要的寄存器内容來擷取重要資訊。

繼續閱讀