Android APK脫殼--騰訊樂固、360加固一鍵脫殼
另外,參考電子書 https://crifan.github.io/android_app_security_crack/website/
編碼前線關注
112018.09.28 17:22:18字數 478閱讀 86,192
概述
現在使用Proguard進行混淆的程式碼,也很容易被破解,所以就出現了加固工具,讓反編譯的難度更大。但是有了加固技術,就會有反加固技術,正所謂道高一尺魔高一丈。
經過加固後的apk,通過dex2jar
反編譯:
騰訊樂固:
legu.png
360加固:
360jiagu.png
從上面可以看出,經過加固後的apk,通過常規方法反編譯無法獲取到原始碼。
下載工具
脫殼工具FDex2
通過Hook ClassLoader的loadClass方法,反射呼叫getDex方法取得Dex(com.android.dex.Dex類物件),在將裡面的dex寫出。
下載地址:
連結:https://pan.baidu.com/s/1smxtinr 密碼:dk4v
VirtualXposed
VirtualXposed:無需root手機即可使用xp框架。
下載地址:
脫殼
Step1:
將VirtualXposed
、FDex2
和需要脫殼的應用都安裝到手機上。
Step2:
啟動VirtualXposed
,並在VirtualXposed
中安裝FDex2
:
vp-install-fdex2.gif
Step3:
在VirtualXposed
中啟用FDex2
:
active-fdex2.gif
Step4:
在VirtualXposed
中安裝要脫殼的應用,方法和Step2一樣。
Step5:
啟動VirtualXposed
FDex2
,並配置要脫殼的應用。fdex2-config.png
Step6:
在VirtualXposed
中執行要脫殼的應用。
Step7:
脫殼後的dex檔案:
shelling-dex.png
匯出脫殼的dex檔案:
root裝置:
adb root
adb pull /data/user/0/iv.va.exposed/virtual/data/user/0/{packageName} {電腦上的目錄}
未root裝置:
在VirtualXposed
中,設定-->高階設定-->檔案管理,安裝檔案管理器,然後通過檔案管理器進入到指定的目錄,通過分享
功能發到電腦上。
Step8:
通過dex2jar
對 脫殼的dex進行反編譯:
shelling-dex2jar.png
從上圖就可以看到脫殼後的dex檔案被成功的反編譯。
FDex2核心程式碼MainHook
package com.ppma.xposed;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XSharedPreferences;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
public class MainHook implements IXposedHookLoadPackage {
XSharedPreferences xsp;
Class Dex;
Method Dex_getBytes;
Method getDex;
String packagename;
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
xsp = new XSharedPreferences("com.ppma.appinfo", "User");
xsp.makeWorldReadable();
xsp.reload();
initRefect();
packagename = xsp.getString("packagename", null);
XposedBridge.log("設定包名:"+packagename);
if ((!lpparam.packageName.equals(packagename))||packagename==null) {
XposedBridge.log("當前程式包名與設定不一致或者包名為空");
return;
}
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 (IOException e) {
e.printStackTrace();
XposedBridge.log("檔案寫出失敗");
}
}
}