教我兄弟學Android逆向12 編寫xposed模組
上一篇 《教我兄弟學Android逆向11 動態除錯init_array》我帶你 用IDA動態除錯了init_array段和JNI_OnLoad裡面的方法,雖然你學的很吃力,但是經過自己不斷上網查閱資料,花了幾天的時間終於把不懂的地方弄清楚了。你從網上面加了好幾個Android逆向學習交流群,看到群裡面有人說xposed相關的內容,你聽的雲裡霧裡的,想到之前面試的時候面試官也問過xpose相關的問題,你對xpose更是產生了濃厚的好奇心。
那麼xpose到底是什麼東西呢?在開始本節課之前我們先了解一下它。
原理:
Xpose是一款特殊的安卓應用,誕生於著名的XDA論壇,它的原理是替換安卓系統/system/bin目錄下的app_process來控制zygote程序,使得app_pross在啟動時會載入XposedBridge.jar,從而實現對zygode程序以及其建立的
優點:
xpose可以在我們不破壞apk自身的情況下實現對函式的hook,修改函式的引數和返回值,改變函式的結構並執行我們自己的程式碼,用好了xposed可以對我們的逆向過程起到事半功倍的作用。
缺點:
本身不能對so中的函式進行修改 需要結合其他框架。
看完上面的介紹我想你應該對xpose有個基本的瞭解了那麼本節課我們一起來揭開xpose的神祕面紗。
要麼學!要麼不學!學和不學之間沒有中間值 不學就放棄,學就要去認真的學! --致選擇
部落格同步更新地址
https://blog.csdn.net/ASSYIRAN/article/details/86139496
編譯環境:
AndroidStudio3.0.1版本
測試手機:
Nexus 4
所需框架和jar包:
1.XposedBridge的jar包
[color=rgba(0, 0, 0, 0.75)]主要功能是提供給Xpose的模組開發者所需的api 我們接下來開發xpose模組需要這個jar包
2.xposedInstaller框架
Xpose安裝到手機的框架,用來載入我們編寫出來的模組
下載連結:
https://pan.baidu.com/s/1fEShR4h1XK7yJIhOKtiYiw
提取碼:vrxg
補充:
上面下載連結所給的xpose框架和模組都是事先編譯好的,我們平常做專案時直接拿來用就可以。
xpose是一個開源專案,開源地址:
https://github.com/rovo89
一 .搭建hook環境用來編譯框架所需要的模組
1.開啟AS在專案app目錄下新建lib目錄並將XposedBridgeApi-54.jar放到lib目錄下 右鍵Add As Library將jar包新增進依賴。
2.開啟專案分支src/main目錄下的AndroidManifest.xml 在application標籤裡面新增內容如下圖所示:
<meta-data
android:name="xposedmodule"
android:value="true" />
<meta-data
android:name="xposeddescription"
android:value="Easy example" />
<meta-data
android:name="xposedminversion"
android:value="54" />
<ignore_js_op>
3.開啟app目錄下的build.gradle將
compile files('lib/XposedBridgeApi-54.jar')
更改為
provided files('lib/XposedBridgeApi-54.jar')
<ignore_js_op>
4.新建Hook入口類HookMain實現xposed的介面IXposedHookLoadPackage並重寫方法handleLoadPackage 如圖所示 這個寫法格式是固定的。 [Java]純文字檢視複製程式碼 ?
1 2 3 4 |
public class HookMain implements IXposedHookLoadPackage {
public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
}
}
|
<ignore_js_op>
5.在src/main/assets下新建檔案xposed_init並將HookMain類並將hook的主入口類以包名+類名的格式寫進去。
com.example.xposed_test.HookMain
<ignore_js_op>
二.編寫框架所需模組 Hook系統的imei檢測hook環境是否配置成功
1.開啟專案的MainActivity類並寫一個getIMEI方法獲取當前系統的imei並在程式執行的時候打印出來。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout.layout);
Log.i( "手機的imei是" ,getIMEI( this ));
}
public static final String getIMEI(Context context) {
try {
//例項化TelephonyManager物件
TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
//獲取IMEI號
String imei = telephonyManager.getDeviceId();
//在次做個驗證,也不是什麼時候都能獲取到的啊
if (imei == null ) {
imei = "" ;
}
return imei;
} catch (Exception e) {
e.printStackTrace();
return "" ;
}
}
|
<ignore_js_op>
2.執行程式得到當前手機的imei 355136055053345 說明hook環境配置成功。
<ignore_js_op>
3.開啟HookMain編寫Hook imei的程式碼
Hook一個函式需要滿足三個條件:
(1)方法的包名+類名
(2)方法名
(3)方法的引數型別
對初學者這裡可能會有個疑問
(1)如何找到方法的包名+類名?
(2)如何找到方法引數型別
針對上面的兩個問題這裡有個小技巧,對於系統函式你要hook哪個方法就先找到這個方法的定義 這裡我們要hook的方法是getDeviceId 所以我們要找到它的定義位置
<ignore_js_op>
<ignore_js_op>
<ignore_js_op>
4.方法的包名+類名和引數型別都知道了 下面就可以按照固定格式編寫hook程式碼了。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class HookMain implements IXposedHookLoadPackage {
public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
//固定格式
findAndHookMethod(
"android.telephony.TelephonyManager" , //要hook的包名+類名
lpparam.classLoader, //classLoader固定
"getDeviceId" , //要hook的方法名
//方法引數 沒有就不填
new XC_MethodHook() {
@Override
//方法執行前執行
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
}
//方法執行後執行,改方法的返回值一定要在方法執行完畢後更改
protected void afterHookedMethod(MethodHookParam param)
throws Throwable {
param.setResult( "355888888888888" );
}
}
);
}
}
|
<ignore_js_op>
5.將AndroidStudio編譯出來的hook程式安裝到手機 並將xposedInstaller框架安裝到手機 開啟xposed應用 在我們編寫的模組後面打鉤後點安裝更新。
<ignore_js_op>
<ignore_js_op>
6.重啟後開啟模組執行檢視log看到imei已經變成355888888888888說明hook成功。如果有沒hook成功的同學可能環境沒搭建好,重新來一次。
<ignore_js_op>
三.實戰
安裝並開啟附件中的解鎖程式apk發現程式需要輸入密碼才能解鎖,接下來我將帶你一起完成以下兩個任務:
任務一
獲得解鎖碼
任務二
解鎖程式開啟寶箱
要求:只能通過hook去開啟寶箱,修改本地檔案等視為無效。
任務一
1.用jeb開啟解鎖程式apk發現程式被簡單混淆,我們找到MainActivity檢視反編譯程式碼
<ignore_js_op>
<ignore_js_op>
<ignore_js_op>
<ignore_js_op>
2.經過大致的分析我們知道解鎖碼為當前手機的androidid經過md5加密後與固定字串hfdcxy1011進行拼接後再進行一次md5加密得到的值擷取前6位。
這裡我們有三種方式可以把這個解鎖碼打印出來
(1)我們知道第一個a方法是最後一層的加密 我們可以hook這個a方法把它的返回值打印出來 然後取其前6位為解鎖碼
(2)因為整個apk只有一處對substring的呼叫 我們可以hook系統函式substring把函式返回值打印出來
(3)通過分析知道第二個a方法為log列印的方法 我們可以Hook這個a方法的引數 把解鎖碼通過log打印出來
這裡我給大家演示第一種hook方法
前面我們hook imei的時候已經說過了如果要hook一個方法要滿足以下三個條件:
(1)方法的包名+類名
我們找打它為com.hfdcxy.android.by.test.a
(2)方法名
這裡不用多提了吧,你要hook哪個方法,方法名就是哪個這裡為a
(3)方法的引數型別
String
<ignore_js_op>
<ignore_js_op>
3.下面開始編寫Hook程式碼
我們把Hook imei的那份程式碼複製粘貼後放到下面,按照格式修改成hook a方法的程式碼。還要過濾下包名防止xposed找不到包名對應的類報錯 這裡的包名是Manifest下的包名。
com.ss.android.ugc.aweme
<ignore_js_op>
<ignore_js_op>
HookMain類
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
public class HookMain implements IXposedHookLoadPackage {
public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
//固定格式 因為是系統函式所以無需過濾包名
findAndHookMethod(
"android.telephony.TelephonyManager" , //要hook的包名+類名
lpparam.classLoader, //classLoader固定
"getDeviceId" , //要hook的方法名
//方法引數 沒有就不填
new XC_MethodHook() {
@Override
//方法執行前執行
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
}
//方法執行後執行,改方法的返回值一定要在方法執行完畢後更改
protected void afterHookedMethod(MethodHookParam param)
throws Throwable {
param.setResult( "355888888888888" );
}
}
);
if (!lpparam.packageName.equals( "com.ss.android.ugc.aweme" )) //這裡過濾一下包名
{
return ;
}
Log.i( "Tiger_test" , "hook進入解鎖程式" );
//Hook a方法
findAndHookMethod(
"com.hfdcxy.android.by.test.a" , //要hook的包名+類名
lpparam.classLoader, //classLoader固定
"a" , //要hook的方法名
String. class , //方法的引數型別 這裡為String類
new XC_MethodHook() {
@Override
//方法執行前執行
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
}
//方法執行後執行,改方法的返回值一定要在方法執行完畢後更改
protected void afterHookedMethod(MethodHookParam param)
throws Throwable {
Log.i( "Tiger_test" , "a方法的第一個引數為:" +param.args[ 0 ].toString()); //param.args[0]為方法的第一個引數,同理param.args[1]為第二個引數
Log.i( "Tiger_test" , "a方法的返回值為:" +param.getResult()); //方法的返回值只能放在afterHookedMethod中獲取
//以下為修改
//param.args[0] = "235wtwerteq" //如果要修改第一個引數可以直接這樣寫
//param.setResult("7f769c0f91efd402a23d63627f48f03e"); //param.setResult修改方法的返回值
}
}
);
}
}
|
4.編譯專案並安裝到手機,xposed框架打鉤模組,安裝並更新重啟後開啟解鎖程式隨便輸入一個解鎖碼點選解鎖,可以看到a方法的引數和返回值已經被我們通過log打印出來了,因為a方法在程式中被呼叫了兩次所以這裡列印了兩次。
我們只需看最後一次的返回值即可,取前6位可以看到我的手機的解鎖碼為4567d2
<ignore_js_op>
5.輸入這個解鎖碼點選解鎖 出現如圖所示的介面說明解鎖碼正確 任務一獲取解鎖碼也就完成了。
<ignore_js_op>
下面我們開看下任務二
1.任務二要求解鎖程式開啟寶箱,我們看到這個介面有兩個按鈕一個是充值1金幣,另一個按鈕是開啟寶箱。測試發現這裡我不斷手點充值1金幣把金幣總量充值到100還是開啟不了寶箱,提示金幣不足,請充值。
<ignore_js_op>
2.我們繼續分析程式碼,發現輸入正確的解鎖碼後程序會跳轉到DrawActivity類,這個類中有兩個onClick 第一個是充值的onClick第二個是開啟寶箱,分析發現只有金幣大於9999時才能開啟寶箱。
這裡我們有兩種方法去開啟寶箱
(1)用手不斷點選充值1金幣,需要點9999次,這無疑是太耗費時間的,所以不可取。
(2)直接Hook第一個onClick裡面的a方法把第三個引數改成10000即可開啟寶箱。
<ignore_js_op>
<ignore_js_op>
<ignore_js_op>
3.編寫hook程式碼
找到hook所需要的三個條件:
(1)方法的包名+類名
我們找打它為com.hfdcxy.android.by.test.b
(2)方法名
這裡不用多提了吧,你要hook哪個方法,方法名就是哪個這裡為a
(3)方法的引數型別 這裡有三個
SharedPreferences
TextView
int
<ignore_js_op>
HookMain類
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
public class HookMain implements IXposedHookLoadPackage {
public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
//固定格式
findAndHookMethod(
"android.telephony.TelephonyManager" , //要hook的包名+類名
lpparam.classLoader, //classLoader固定
"getDeviceId" , //要hook的方法名
//方法引數 沒有就不填
new XC_MethodHook() {
@Override
//方法執行前執行
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
}
//方法執行後執行,改方法的返回值一定要在方法執行完畢後更改
protected void afterHookedMethod(MethodHookParam param)
throws Throwable {
param.setResult( "355888888888888" );
}
}
);
if (!lpparam.packageName.equals( "com.ss.android.ugc.aweme" )) //這裡過濾一下包名
{
return ;
}
Log.i( "Tiger_test" , "hook進入解鎖程式" );
//Hook a方法
findAndHookMethod(
"com.hfdcxy.android.by.test.a" , //要hook的包名+類名
lpparam.classLoader, //classLoader固定
"a" , //要hook的方法名
String. class , //方法的引數型別 這裡為String類
new XC_MethodHook() {
@Override
//方法執行前執行,修改引數的地方
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
}
//方法執行後執行,修改方法的返回值的地方
protected void afterHookedMethod(MethodHookParam param)
throws Throwable {
Log.i( "Tiger_test" , "a方法的第一個引數為:" +param.args[ 0 ].toString()); //param.args[0]為方法的第一個引數,同理param.args[1]為第二個引數
Log.i( "Tiger_test" , "a方法的返回值為:" +param.getResult()); //方法的返回值只能放在afterHookedMethod中獲取
//以下為修改
//param.args[0] = "235wtwerteq" //如果要修改第一個引數可以直接這樣寫
//param.setResult("7f769c0f91efd402a23d63627f48f03e"); //param.setResult修改方法的返回值
}
}
);
findAndHookMethod(
"com.hfdcxy.android.by.test.b" , //要hook的包名+類名
lpparam.classLoader, //classLoader固定
"a" , //要hook的方法名
SharedPreferences. class , //方法的引數型別
TextView. class , //方法的引數型別
int . class , //方法的引數型別
new XC_MethodHook() {
@Override
//方法執行前執行,修改引數的地方
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.args[ 2 ] = 10000 ; //注意修改引數要放在beforeHookedMethod裡面寫
Log.i( "Tiger_test" , "成功充值一萬金幣" );
}
//方法執行後執行,修改方法的返回值的地方
protected void afterHookedMethod(MethodHookParam param)
throws Throwable {
}
}
);
}
}
|
4.編譯xposed框架安裝模組,開啟解鎖程式輸入解鎖碼後點擊充值1金幣,然後點選開啟寶箱。顯示彈框開啟成功,獲得xposed新手稱號。
<ignore_js_op>
<ignore_js_op>
<ignore_js_op>
四.總結
本節課我們首先了解了xpose的基本原理並一起搭建了xpose的hook環境。接下來我們又一起用xpose破解瞭解鎖程式這個apk,通過對本節課的學習我想你對xpose已經有了一個全新的認識。環境搭建有問題的的同學可以直接把下面的課堂Demo匯入到專案中去學習,課堂上面講解的內容一定要多加練習,看不懂的要多百度,百度上面有很多關於xpose的教程,各種教程對比著來學習,總能理解的。
五.課後作業
1.hook本節課例子中的其他函式獲取解鎖碼
2.hook修改極品美女找茬遊戲中的金幣餘額為999
遊戲下載連結
https://blog.csdn.net/ASSYIRAN/article/details/86014551
六.進階
課堂作業完成後請參考以下兩條連結,並自己編寫對應模組練習裡面的例子,完成對xpose的進階。
http://www.cnblogs.com/gordon0918/p/6732100.html
https://bbs.pediy.com/thread-225190-1.htm
課堂Demo
連結:https://pan.baidu.com/s/1uTkofcZIORJeAscXIRaf6g
提取碼:6vi6