目錄html
Markdown版本筆記 | 個人GitHub首頁 | 個人博客 | 個人微信 | 個人郵箱 |
---|---|---|---|---|
MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
PS:如下全部內容,包括測試用來逆向的APP均已脫敏java
參考個人其餘博客內容:android
Android Killer 是一款能夠對APK進行反編譯的工具,它可以對反編譯後的Smali
文件進行修改,並將修改後的文件進行打包。git
AndroidKiller的基本功能:github
能夠對修改後的工程從新打包
軟件中的Apktool
可能會由於版本過低致使 apk 的反編譯失敗,此時須要到 Apktool 官網去下載最新版本的Apktool。api
下載完成後找到解壓好的 AndroidKiller 目錄下的bin\apktool\apktool
目錄將下載的最新版的 apktool 複製進去。微信
而後修改 AndroidKiller 根目錄下的bin\apktool
下的apktool.bat
和apktool.ini
文件,將裏面對應的文件名改成你下載的最新的 apktool 文件名。app
使用 AndroidKiller 對 Apk 進行反編譯只須要將 Apk 文件拖入軟件便可。框架
反編譯後咱們能夠對資源文件
等進行簡單的修改,修改後後點擊左上角菜單欄中的Android -- 編譯
便可從新編譯成APK。ide
另外咱們也能夠點擊 smali 目錄,查看反編譯的 smali 源碼,並能夠在某一 smali 源碼文件中右鍵 -- 查看 -- 查看源碼
經過 Java Decompiler
查看反編譯的 java 源碼。
常常會修改包名、應用圖標、應用名稱等基本信息,只需在這裏找到相應的res並修改便可。
另外還能夠查看其使用的Application,由於通常一些初始化操做、全局常量的設置等都是在Application裏面。
<manifest package="包名" > <application android:icon="圖標" android:label="名稱" android:name="使用的Application">
或者說是開啓debug模式,通常有以下幾種處理思路
按以下方式即可以 hook 住系統 Log 的方法調用,咱們能夠在 Log.d 等方法被調用 前/後 作本身的邏輯,好比對日誌進行過濾、把日誌保存到文件中。
public class XposedZygoteInit implements IXposedHookZygoteInit { @Override public void initZygote(StartupParam startupParam) throws Throwable { //hook住某個方法。參數:類名,類加載器,方法名,參數列表,Hook成功後的回調 XposedHelpers.findAndHookMethod("android.util.Log", StartupParam.class.getClassLoader(), "d", String.class, String.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { //Log.i("bqt", "【代理系統的Log類,能夠在這裏把日誌保存到文件中】" + param.args[0] + " ," + param.args[1]); } }); } }
好比,反編譯一款 APP 後發現其打印日誌的部分邏輯以下:
public class LogTool{ public static boolean a = ; public static String b = "通用的tag"; public static void a(String paramString) { if (a) { Log.d(b, paramString); } }
在 release 包中,這個值靜態字段 a 確定是 false,咱們只需將其改成 true 便可打印 debug 下才會打印的日誌。
hook 方式以下:
public class XposedInit implements IXposedHookLoadPackage { @Override public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) throws Exception { if (lpparam.packageName.equals(應用的包名,進入if表明此應用啓動了,能夠開始hook了)) { Field field = XposedHelpers.findClass("完整路徑.LogTool", lpparam.classLoader).getField("a"); field.setAccessible(true); field.setBoolean(null, true); //靜態字段的 the object whose field should be modified is null } } }
PS:hook 系統類不能經過 IXposedHookLoadPackage
,而須要經過 IXposedHookZygoteInit
,由於此應用啓動前,系統類已經被加載過了,已經錯過了 hook 的時機了。
好比,反編譯一款 APP 後發現其打印日誌的部分邏輯以下:
public class LogTool { public static void d(String paramString1, String paramString2) { if (isLogAble(LogLevel.DEBUG)) { //... } } private static boolean isLogAble(LogLevel paramLogLevel) { if (logcatLevel.value == LogLevel.OFF.value) { return false; } if (logcatLevel.value == LogLevel.ALL.value) { return true; } return logcatLevel.compare(paramLogLevel) >= 0; } }
相似上述案例,咱們只需hook住isLogAble方法,而且將返回值直接返回true便可跳過日誌級別判斷。
hook 方式以下:
XposedHelpers.findAndHookMethod("類的完整路徑", lpparam.classLoader, "isLogAble", "參數的完整路徑", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { Log.i("bqt", "【" + Arrays.toString(param.args) + "】"); param.setResult(true); } });
這種方式和上面那種方式相似,不過適用場景更普遍。
APP編譯時會自動爲每個module生成一個 BuildConfig
類,其中包含一些常用到的與環境相關的常量,例如:
public final class BuildConfig { public static final boolean DEBUG = Boolean.parseBoolean("true"); //是不是調試模式 public static final String APPLICATION_ID = "com.bqt.test"; //包名 public static final String BUILD_TYPE = "debug"; public static final String FLAVOR = ""; public static final int VERSION_CODE = 1; public static final String VERSION_NAME = "1.0"; }
不少時候,咱們判斷是否應打印日誌就是根據 BuildConfig.DEBUG
來肯定的,因此咱們只須要修改這個值就能夠了。
修改這個值的方式確定不是經過Xposed框架,由於這些常量都是 final 類型的,動態框架確定是沒法修改的,一種可行的方式:
.class
文件,具體詳見:Javassist 字節碼 簡介 案例 MD首先須要明白,混淆後的類名、方法名、成員變量名等都是固定的,反編譯後你看到的名字是什麼,運行時它的名字就是什麼,不會再變的
而後你還須要知道,他的名字雖然是固定的,可是每每你沒辦法直接hook,由於不少混淆後的名字都是非法字符,你即無法輸入(IDE不識別),也無法經過編譯(編譯器不識別),因此須要採用特殊的方式來hook。
如下是一種可供參考的案例:
//從指定類中混淆後的方法名中獲取匹配的方法 String methodName = ""; Method[] methods = XposedHelpers.findClass("o.ayc", lpparam.classLoader).getDeclaredMethods(); for (Method method : methods) { method.setAccessible(true); //匹配返回值,這裏只是簡單字符串匹配,更精確的能夠經過類型匹配 if (method.getReturnType().toString().contains("HttpURLConnection")) { methodName = method.getName(); break; } }
經過上述方法,即可跳過IDE和編譯器
的名稱規範的檢查,在運行時
即可匹配混淆後的類或方法(運行時並不會進行規範性檢查)。
一、安裝 XposedInstaller.apk,安裝後啓動此應用,安裝 framework ,重啓手機
二、AS工程中添加依賴
compileOnly 'de.robv.android.xposed:api:82:sources'//xposed依賴,注意這個版本號和framework版本號並非一致的 implementation files('libs/javassist.jar')//非必須
三、application下添加三個meta-data
<meta-data android:name="xposedmodule" android:value="true"/> <meta-data android:name="xposeddescription" android:value="這是對你使用xposed能完成功能的簡要描述"/> <meta-data android:name="xposedminversion" android:value="89"/>
四、編寫Hook邏輯
五、配置完整的Hook類類名
新建assets文件夾,文件夾下新建xposed_init
文件(文件名固定),在文件中填寫hook邏輯所在類的fully qualified class name:
com.bqt.test.temp.XposedInit com.bqt.test.temp.XposedZygoteInit
六、有任何代碼變更,都需重裝APP後重啓手機才能生效
public class XposedInit implements IXposedHookLoadPackage { @Override public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) throws Exception { if (lpparam.packageName.equals("應用1的包名,進入if表明此應用啓動了,能夠開始hook了")) { //開啓日誌打印 XposedHelpers.findAndHookMethod("類的完整路徑", lpparam.classLoader, "isLogAble", "參數的完整路徑", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { Log.i("bqt", "日誌級別【" + Arrays.toString(param.args) + "】"); param.setResult(true); } }); //從指定類中混淆後的方法名中獲取匹配的方法 String methodName = ""; Method[] methods = XposedHelpers.findClass("o.ayc", lpparam.classLoader).getDeclaredMethods(); for (Method method : methods) { method.setAccessible(true); //匹配返回值,這裏只是簡單字符串匹配,更精確的能夠經過類型匹配 if (method.getReturnType().toString().contains("HttpURLConnection")) { methodName = method.getName(); break; } } //hook住某個方法。參數:類名,類加載器,方法名,參數列表,Hook成功後的回調 XposedHelpers.findAndHookMethod("o.ayc", lpparam.classLoader, methodName, String.class, new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { Log.i("bqt", "1-【" + param.method.getName() + "】" + param.args[0]); } }); //只能hook具體的方法,不能hook接口的方法或抽象的方法。好比只能hook住某個Runnable的實現類的run方法,而不能hook住全部Runnable的run方法 XposedHelpers.findAndHookMethod("o.ayf", lpparam.classLoader, "run", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { Log.i("bqt", "2-【" + param.thisObject.getClass().toString() + "】"); } }); } else if (lpparam.packageName.equals("應用2的包名")) { //開啓日誌打印 Field field = XposedHelpers.findClass("完整路徑.LogTool", lpparam.classLoader).getField("a"); field.setAccessible(true); field.setBoolean(null, true); //靜態字段的 the object whose field should be modified is null //注意,基本類型變量的class文件不能使用其相應包裝類型來標識,例如 boolean.class 不能使用 Boolean.class 來代替 XposedHelpers.findAndHookMethod("包名.a", lpparam.classLoader, "a", long.class, String.class, boolean.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { Log.i("bqt", "3-【" + Arrays.toString(param.args) + "】"); } }); } } }
public class XposedZygoteInit implements IXposedHookZygoteInit { @Override public void initZygote(StartupParam startupParam) throws Throwable { XposedHelpers.findAndHookMethod("android.util.Log", StartupParam.class.getClassLoader(), "d", String.class, String.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { //Log.i("bqt", "【代理系統的Log類,能夠在這裏把日誌保存到文件中】" + param.args[0] + " ," + param.args[1]); } }); } }
2019-10-23