一、APP打包流程
- 打包資源文件,生成R.java文件
打包資源的工具是aapt(The Android Asset Packaing Tool)(E:\Documents\Android\sdk\build-tools\25.0.0\aapt.exe)。
在這個過程中,項目中的AndroidManifest.xml文件和佈局文件XML都會編譯,然後生成相應的R.java,另外AndroidManifest.xml會被aapt編譯成二進制。
存放在APP的res目錄下的資源,該類資源在APP打包前大多會被編譯,變成二進制文件,並會爲每個該類文件賦予一個resource id。對於該類資源的訪問,應用層代碼則是通過resource id進行訪問的。Android應用在編譯過程中aapt工具會對資源文件進行編譯,並生成一個resource.arsc文件,resource.arsc文件相當於一個文件索引表,記錄了很多跟資源相關的信息。 - 處理aidl文件,生成相應的Java文件
這一過程中使用到的工具是aidl(Android Interface Definition Language),即Android接口描述語言(E:\Documents\Android\sdk\build-tools\25.0.0\aidl.exe)。
aidl工具解析接口定義文件然後生成相應的Java代碼接口供程序調用。如果在項目沒有使用到aidl文件,則可以跳過這一步。 - 編譯項目源代碼,生成class文件
項目中所有的Java代碼,包括R.java和.aidl文件,都會變Java編譯器(javac)編譯成.class文件,生成的class文件位於工程中的bin/classes目錄下。 - 轉換所有的class文件,生成classes.dex文件
dx工具生成可供Android系統Dalvik虛擬機執行的classes.dex文件,該工具位於(E:\Documents\Android\sdk\build-tools\25.0.0\dx.bat)。
任何第三方的libraries和.class文件都會被轉換成.dex文件。dx工具的主要工作是將Java字節碼轉成成Dalvik字節碼、壓縮常量池、消除冗餘信息等。 - 打包生成APK文件
所有沒有編譯的資源,如images、assets目錄下資源(該類文件是一些原始文件,APP打包時並不會對其進行編譯,而是直接打包到APP中,對於這一類資源文件的訪問,應用層代碼需要通過文件名對其進行訪問);編譯過的資源和.dex文件都會被apkbuilder工具打包到最終的.apk文件中。
打包的工具apkbuilder位於 android-sdk/tools目錄下。apkbuilder爲一個腳本文件,實際調用的是(E:\Documents\Android\sdk\tools\lib)文件中的com.android.sdklib.build.ApkbuilderMain類。 - 對APK文件進行簽名
一旦APK文件生成,它必須被簽名才能被安裝在設備上。
在開發過程中,主要用到的就是兩種簽名的keystore。一種是用於調試的debug.keystore,它主要用於調試,在Eclipse或者Android Studio中直接run以後跑在手機上的就是使用的debug.keystore。
另一種就是用於發佈正式版本的keystore。 - 對簽名後的APK文件進行對齊處理
如果你發佈的apk是正式版的話,就必須對APK進行對齊處理,用到的工具是zipalign(E:\Documents\Android\sdk
build-tools\25.0.0\zipalign.exe)
對齊的主要過程是將APK包中所有的資源文件距離文件起始偏移爲4字節整數倍,這樣通過內存映射訪問apk文件時的速度會更快。對齊的作用就是減少運行時內存的使用。
二、APP常用抓包技術
- 工具
Charles,fiddler,burpsuite,Packet Capture等,具體操作可以查找百度 - 抓包方法
a.常規操作直接抓
b.使用Xposed+JustTrustMe關閉SSL證書驗證抓包
關閉SSL證書校驗之前抓包
關閉SSL證書校驗之後抓包
c.使用Packet Capture抓取TCP數據包
d.通過寫xposed hook插件打印請求url和請求參數(示例不方便透露有疑問可聯繫)
四、APP脫殼
- .加殼的原理
給dex文件加層殼,反編譯後的代碼就是加殼的代碼,看不到原dex代碼,在一定程度上來說,還是可以起到防破解的,也可以防止二次打包 - .常用的APP加固殼
360 騰訊樂固、百度、網易、阿里、愛加密、梆梆、娜迦、頂象等 3手寫脫殼工具 - 手寫脫殼工具:
脫殼方式有好多種,企業殼最難搞,這裏分享一種使用xposed插件完成脫殼,主要的代碼如下:
public class PackageHook {
Class Dex;
Method Dex_getBytes;
Method getDex;
String packagename;
public PackageHook(XC_LoadPackage.LoadPackageParam sharePkgParam) {
packageHook(sharePkgParam);
}
private void packageHook(final XC_LoadPackage.LoadPackageParam lpparam) {
Log.i("jyy", lpparam.packageName);
final String packagename = "你要脫殼的app包名";
//添加程序包名
initRefect();
XposedBridge.log("目標包名:" + lpparam.packageName);
String str = "java.lang.ClassLoader";
String str2 = "loadClass";
XposedHelpers.findAndHookMethod(str, lpparam.classLoader, str2, String.class, Boolean.TYPE, new XC_MethodHook() {
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Class cls = (Class) param.getResult();
if (cls == null) {
//XposedBridge.log("cls == null");
return;
}
String name = cls.getName();
XposedBridge.log("當前類名:" + name);
byte[] bArr = (byte[]) Dex_getBytes.invoke(getDex.invoke(cls, new Object[0]), new Object[0]);
if (bArr == null) {
XposedBridge.log("數據爲空:返回");
return;
}
XposedBridge.log("開始寫數據");
String dex_path = "/data/data/" + packagename + "/" + packagename + "_" + bArr.length + ".dex";
XposedBridge.log(dex_path);
File file = new File(dex_path);
if (file.exists()) return;
writeByte(bArr, file.getAbsolutePath());
}
} );
}
public void initRefect() {
try {
Dex = Class.forName("com.android.dex.Dex");
Dex_getBytes = Dex.getDeclaredMethod("getBytes", new Class[0]);
getDex = Class.forName("java.lang.Class").getDeclaredMethod("getDex", new Class[0]);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
public void writeByte(byte[] bArr, String str) {
try {
OutputStream outputStream = new FileOutputStream(str);
outputStream.write(bArr);
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
XposedBridge.log("文件寫出失敗");
}
}
通過代碼可以發現就是反射拿到Android中com.android.dex.Dex對象然後調用getDex方法然後把字節寫到文件,其實就是脫殼後dex文件。Fdex2脫殼工具的核心原理就是上述代碼。
五、App逆向分析
- 目標:cgv.apk 用戶註冊接口分析(僅僅用於學習,違法使用後果自負)
- 工具:jadx,jeb, Android studio等
- 抓包:註冊包
如果沒有逆向分析APP的看到請求參數是不是有點傻眼了,下面來探探究竟,先脫殼然後使用jadx打開脫殼後的dex,然後搜索關鍵詞
繼續跟蹤代碼,發現CLIENT_KEY是通過一個算法得到這個值
繼續跟進代碼(選中該方法按ctrl+鼠標左鍵)
先看h方法其實就是一個MD5算法
繼續跟進上述a方法中的a方法:
其實f方法就是做了一些字符串拼接操作,g方法就是做了數組排序,可以自行繼續追蹤下去,如果是Java開發則只需要拷貝就行,其他語言改寫就可以。到這裏算法基本已經明確,現在最主要的問題就是a方法的參數:
常規操作是去找這個方法在哪裏調用,這裏先介紹一個技巧可以使用xposed 寫一個hook插件把參數打印出來,如果打出來的參數還有加密,還是得去找它被調用的地方。不熟悉xposed框架可以去百度,一個神器的框架,hook插件代碼:
private void cgvHook(final XC_LoadPackage.LoadPackageParam lpp) {
Log.i("jyy", lpp.packageName);
if (!lpp.packageName.contains("com.cgv.cn.movie")) {
return;
}
XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
ClassLoader cl = ((Context) param.args[0]).getClassLoader();
Class<?> hookclass = null;
try {
hookclass = cl.loadClass("com.cgv.cn.movie.b.ar");
} catch (Exception e) {
Log.e("jyy", "尋找xxx.xxx.xxx報錯", e);
return;
}
//cn.ikicker.moviefans.ui.activity.b
//a(String str, String str2, String str3)
Log.e("jyy", "尋找xxx.xxx.xxx成功");
//com.loopj.android.http
Class hookclasss = cl.loadClass("com.loopj.android.http.RequestParams");
XposedHelpers.findAndHookMethod(hookclass, "a",hookclasss,String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
Object params = param.args[0];
String param1 = (String) param.args[1];
XposedBridge.log("a params:"+params.toString());
XposedBridge.log("a param1:"+param1);
super.beforeHookedMethod(param);
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
String result = (String) param.getResult();
XposedBridge.log("a result:"+result);
super.afterHookedMethod(param);
}
});
});
}
打開ddms就可以查看a params打印出來的就是我們a方法的第一個參數,a param1打印出的就是第二參數,a result打印出的就是a方法的返回值:
圖稍後補上
加密算法和加密參數已經知道了,CLIENT_KEY基本搞定了,其他的參數分析過程類似,這裏就不贅述,這些都是一些初級的操作,還有很多app算法套路更深需要不斷提升自己的實力才能擊破他。其實這種Java基礎加密完全可以寫一套常用算法hook也就是可以直接打印出加密參數和加密結果,本人寫了一個Java基本算法自吐插件,常規算法可以不需要脫殼和逆向app,直接打印加密算法和加密參數,但也有缺陷僅僅是Java層的算法而且是常規算法,如果算法有做修改則還是需要逆向代碼,但是可以給我做一些前期的準備,過濾掉一些參數算法逆向操作。