衆所周知,Android應用開發完成後,除了使用Google官方的混淆外,還須要使用一些第三方的安全軟件的加殼處理,比較出名的有騰訊樂固、360加固和愛加密等。我以前所在的公司,就是使用愛加密進行加殼處理的。java
雖然加密後,讓軟件的安全性更高了,但並非無懈可擊,一些反加固技術和脫殼技術應運而生。今天要說的就是騰訊樂固、360加固一鍵脫殼。android
工程,通過加固後的apk,經過dex2jar反編譯效果是下面這樣的:安全
騰訊樂固加固:
360加固
能夠發現,通過加固處理由,反編譯是沒法直接獲取到源碼的,代碼的結構以下圖所示:
app
要對Android的apk文件進行脫殼,須要使用的軟件有:框架
不過,須要說明的是,此技術在Android9.0及以上版本是行不通的,而且VirtualXposed有軟件版本限制。工具
下載地址:
連接: https://pan.baidu.com/s/10ZfD... 提取碼: asu1加密
VirtualXposed:無需root手機便可使用Xposed框架
下載連接:
https://vxposed.com/spa
首先,將VirtualXposed、FDex2和須要脫殼的應用都安裝到手機上。而後,啓動VirtualXposed,並在VirtualXposed中安裝FDex2。
而後,在VirtualXposed中選擇模塊管理激活FDex2。
在VirtualXposed中安裝要脫殼的應用,具體和上面的步驟同樣。而後,啓動VirtualXposed中的FDex2,並配置要脫殼的應用。
在VirtualXposed中運行要脫殼的應用,脫殼後的dex文件以下圖:
而後,使用adb pull命令將脫殼後的dex文件導出到電腦。code
adb pull /data/user/0/iv.va.exposed/virtual/data/user/0/{packageName}
最後,再經過dex2jar對 脫殼的dex進行反編譯。
對象
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("文件寫出失敗"); } } }
經過Hook ClassLoader的loadClass方法,反射調用getDex方法取得Dex(com.android.dex.Dex類對象),再將裏面的dex寫出,這就是Hook的原理。