1. 程式人生 > 其它 >教我兄弟學Android逆向12 編寫xposed模組

教我兄弟學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並在程式執行的時候打印出來。

[Java]純文字檢視複製程式碼 ?
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程式碼了。

[Java]純文字檢視複製程式碼 ?
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類

[Java]純文字檢視複製程式碼 ?
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類

[Java]純文字檢視複製程式碼 ?
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

學習時的痛苦是暫時的 未學到的痛苦是終生的