加密算法很简单,只是对每个字节进行异或一下 。
这里是为了简单,所以就用了很简单的加密算法,其实为了增加破解难度,我们应该使用更高效的加密算法,同时最好将加密操作放到native层去做 。
这里需要两个输入文件:
- 源程序APK文件:SourceApk.apk
- 壳程序的DEX文件:SourceApk.dex
第一个文件就是源程序项目编译之后的APK文件,第二个文件是下面要讲的第三个项目:壳程序项目中的classes.dex文件,修改名称之后得到 。3.壳程序项目:PackApk

文章插图
先来了解一下壳程序项目的工作:
- 通过反射置换android.app.ActivityThread中的mClassLoader为加载解密出APK的DexClassLoader,该DexClassLoader一方面加载了源程序,另一方面以原mClassLoader为父节点,这就保证即加载了源程序,又没有放弃原先加载的资源与系统代码 。关于这部分内容不了解的可以看一下Android动态加载之免安装运行程序这篇文章 。
- 找到源程序的Application,通过反射建立并运行 。这里需要注意的是,我们现在是加载一个完整的APK,让他运行起来 。一个APK运行的时候都是有一个Application对象的,这个也是一个程序运行之后的全局类,所以我们必须找到解密之后的源程序APK的Application类,运行它的onCreate方法,这样源程序APK才开始它的运行生命周期 。后面会说如何得到源程序APK的Application类:使用meta标签进行设置 。
下面看一下整体流程:

文章插图
下面看一下代码: ProxyApplication.java
- 得到壳程序APK中的DEX文件,然后从这个文件中得到源程序APK进行解密、加载
// 这是context赋值@Overrideprotected void attachBaseContext(Context base) { super.attachBaseContext(base); try { // 创建两个文件夹payload_odex、payload_lib,私有的,可写的文件目录 File odex = this.getDir("payload_odex", MODE_PRIVATE); File libs = this.getDir("payload_lib", MODE_PRIVATE); odexPath = odex.getAbsolutePath(); libPath = libs.getAbsolutePath(); apkFileName = odex.getAbsolutePath() + "/payload.apk"; File dexFile = new File(apkFileName); Log.i("demo", "apk size:"+dexFile.length()); if (!dexFile.exists()) { dexFile.createNewFile(); //在payload_odex文件夹内,创建payload.apk // 读取程序classes.dex文件 byte[] dexdata = https://www.isolves.com/it/cxkf/ydd/Android/2019-09-04/this.readDexFileFromApk();// 分离出解壳后的apk文件已用于动态加载 this.splitPayLoadFromDex(dexdata); } // 配置动态加载环境 Object currentActivityThread = RefInvoke.invokeStaticMethod( "android.app.ActivityThread", "currentActivityThread", new Class[] {}, new Object[] {});//获取主线程对象String packageName = this.getPackageName();//当前apk的包名 ArrayMap mPackages = (ArrayMap) RefInvoke.getFieldOjbect( "android.app.ActivityThread", currentActivityThread, "mPackages"); WeakReference wr = (WeakReference) mPackages.get(packageName); // 创建被加壳apk的DexClassLoader对象 加载apk内的类和本地代码(c/c++代码) DexClassLoader dLoader = new DexClassLoader(apkFileName, odexPath, libPath, (ClassLoader) RefInvoke.getFieldOjbect( "android.app.LoadedApk", wr.get(), "mClassLoader")); //把当前进程的mClassLoader设置成了被加壳apk的DexClassLoader RefInvoke.setFieldOjbect("android.app.LoadedApk", "mClassLoader", wr.get(), dLoader);Log.i("demo","classloader:"+dLoader); try{ Object actObj = dLoader.loadClass("com.example.sourceapk.MainActivity"); Log.i("demo", "actObj:"+actObj); }catch(Exception e){ Log.i("demo", "activity:"+Log.getStackTraceString(e)); } } catch (Exception e) { Log.i("demo", "error:"+Log.getStackTraceString(e)); e.printStackTrace(); }}这里需要注意的一个问题,就是我们需要找到一个时机,就是在壳程序还没有运行起来的时候,来加载源程序的APK,执行它的onCreate方法,那么这个时机不能太晚,不然的话,就是运行壳程序,而不是源程序了 。查看源码我们知道 。Application中有一个方法:attachBaseContext这个方法,它在Application的onCreate方法执行前就会执行了,所以我们的工作就需要在这里进行 。A) 从APK中获取到DEX文件
/** * 从apk包里面获取dex文件内容(byte) * @return * @throws IOException */private byte[] readDexFileFromApk() throws IOException { ByteArrayOutputStream dexByteArrayOutputStream = new ByteArrayOutputStream(); ZipInputStream localZipInputStream = new ZipInputStream( new BufferedInputStream(new FileInputStream( this.getApplicationInfo().sourceDir))); while (true) { ZipEntry localZipEntry = localZipInputStream.getNextEntry(); if (localZipEntry == null) { localZipInputStream.close(); break; } if (localZipEntry.getName().equals("classes.dex")) { byte[] arrayOfByte = new byte[1024]; while (true) { int i = localZipInputStream.read(arrayOfByte); if (i == -1) break; dexByteArrayOutputStream.write(arrayOfByte, 0, i); } } localZipInputStream.closeEntry(); } localZipInputStream.close(); return dexByteArrayOutputStream.toByteArray();}
推荐阅读
-
-
袁师傅|强忍病痛疏散乘客的杭州335路公交司机 被授予“市五一劳动奖章”
-
-
-
魔兽世界|魔兽世界怀旧服:萌新法师搞事情,ROLL走骑士圣契,理由令人无话可说
-
攻略大全▲热血传奇:战士怎么玩?还会坚持玩下去吗?
-
小泡泡星座关键时刻却出其不意,大开眼界,3大星座为人低调不张扬
-
田柳Talk|称中国战机制造技术领先俄罗斯,仅有一块短板,英国智库捧杀中国
-
-
【新西兰】载中国游客大巴新西兰翻车事故最新进展:司机被控鲁莽
-
-
「生肖」最快下个月,缘分桃花重扑心头,余生不背叛的3大生肖!
-
女性梦见蝎子爬身上预示着什么 女性梦见蝎子爬身上预示着什么预兆
-
周扬青|远离渣男小猪之后的周扬青事业做到国外,努力让自己更加优秀
-
-
-
-
-
「车家号」价格相差200万,Q8和Urus到底差在哪?,同平台打造
-
49岁烧香磕头求四胎的钟丽缇幸福吗|49岁烧香磕头求四胎的钟丽缇幸福吗?