Android开发学习之路-加固实践
Posted 东月之神
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android开发学习之路-加固实践相关的知识,希望对你有一定的参考价值。
前言
- 起因也是想要看一些优秀的程序某些内容是怎么实现的,所以需要脱壳,但是对于怎么加固也还是比较感兴趣的,加固涉及到的安全的内容很多很多,这里也只是用个简单的例子来过把瘾,参考了些文章,只是为了理顺思路,也为之后的脱壳做个准备。
- 其实加固可以理解为,一个应用程序,利用了插件化的功能,启动了另一个apk,而这个apk是经过加密的,壳应用会在加载需要启动的apk的时候去解密。这样即使逆向了dex文件,也看不出来原先apk的代码。
- 为此准备了源程序,壳工程,加固工具以及一些脚本来实现。
1.ApkTest
首先我们准备下需要被加固的文件apk,这里我们自己新建工程写一个例子,ApkTestApplication如下:
这里就打印下ApkTestApplication的onCreate
override fun onCreate()
super.onCreate()
Log.i("ApkTestApplication", "Apk Test Application onCreate:$this")
主工程的MainActivity如下,主要就是显示下是ApkTest这个工程,并且可以跳转到第二个页面
class MainActivity : BaseActivity()
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
Log.d("MainActivity", "onCreate")
setContentView(R.layout.activity_test)
tvName.text = showStr()
btNext.setOnClickListener
gotoSecondActivity()
private fun showStr(): String
return "Hello,I am ApkTest!"
private fun gotoSecondActivity()
startActivity(Intent(this, SecondActivity::class.java))
这个工程没有什么好讲的了,很普通的一个android工程。
详细代码可以参考:ApkTest
2.ShellApk
既然是加固,那么就需要有个壳,可以把待加固的应用程序经过加密后放到壳app里面,这样,即便被反编译后还是看不到原应用的dex文件内容。
-
App启动
防止壳程序运行自己的代码,所以需要在attachBaseContext中实现加载源程序。
-
解密源程序
新建payload_odex文件,并在其中创建playload.apk文件用于存放源程序的apk,从壳程序的dex文件中读取源程序的apk文件,解密存放。
-
初始化源程序的ClassLoader,设置壳程序的classLoader为源程序ClassLoader
获取主线程对象,创建源程序的DexClassLoader对象,加载apk内的类和本地代码,然后把当前进程的mClassLoader设置成源程序apk的DexClassLoader。尝试检测下源程序的MainActivity存不存在判断classLoader是否替换完成。
-
反射生成正确的Application对象
读取Manifest文件中定义的源程序的application类,设置源程序的Application,并把当前进程的application设置为null,删除老的application,使用源程序的application。反射设置ActivityThread中的Application信息
-
设置provider相关的配置
-
调用源程序application的onCreate方法
-
源程序正常运行
AndroidManifest.xml代码如下:
<application
android:name="com.jared.shellapk.ShellApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<meta-data android:name="APPLICATION_CLASS_NAME" android:value="com.jared.apktest.ApkTestApplication"/>
<activity android:name="com.jared.apktest.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name="com.jared.apktest.SecondActivity" />
</application>
这里定义了APPLICATION_CLASS_NAME为源程序的application类,埋坑了MainActivty和SecondActivity用于ApkTest程序启动后的调用。
详细代码可以参考: ShellApk
3.DexPackTool
加固工具其实就是在壳程序的dex文件中加入了源程序的apk文件,这里就是在ShellApk的class.dex文件中加入了ApkTest.apk文件。
首先我们了解下Dex文件的结构:
- Dex文件
Dex文件结构 |
---|
Dex文件头部 |
字符串索引区 |
类型索引区 |
方法索引区 |
原型索引区 |
类定义区 |
数据区 |
链接数据区 |
我们如果要修改dex文件,就需要更新Dex文件头部信息,其中头部信息包括了:
Dex文件头部 | |
---|---|
magic[8] | dex的文件标识,一般称为魔术 |
checksum | dex文件的校验和,通过它可以判断dex文件是否被损坏或者被篡改 |
signature[kSHA1DigestLen] | 检验dex文件,其实就是把整个dex文件用SHA-1签名得到的一个值 |
fileSize | 整个文件的大小,占用4个字节 |
headerSize | 头结构的大小,占用4个字节 |
- 加固后的文件结构
加固后Dex文件 | |
---|---|
ShellApk的Dex文件 | 修改了文件头的ShellApk的classes.dex |
加密的ApkTest的Apk | 经过了异或加密后的ApkTest的apk文件 |
加密的ApkTest的大小 | 为了解密读取对应的大小,需要知道加密后的ApkTest的文件大小 |
- 工具代码如下
File payloadSrcFile = new File("files/SourceApk.apk"); // 需要加壳的源程序
System.out.println("apk size:"+payloadSrcFile.length( ));
File packDexFile = new File("files/SourceApk.dex"); // 壳程序dex
byte[] payloadArray = encrpt(readFileBytes(payloadSrcFile)); // 以二进制形式读出源apk,并进行加密处理
byte[] packDexArray = readFileBytes(packDexFile); // 以二进制形式读出dex
int payloadLen = payloadArray.length;
int packDexLen = packDexArray.length;
int totalLen = payloadLen + packDexLen + 4; // 多出4字节是存放长度的
byte[] newdex = new byte[totalLen]; // 申请了新的长度
// 添加解壳代码
System.arraycopy(packDexArray, 0, newdex, 0, packDexLen); // 先拷贝dex内容
// 添加加密后的解壳数据
System.arraycopy(payloadArray, 0, newdex, packDexLen, payloadLen); // 再在dex内容后面拷贝apk的内容
// 添加解壳数据长度
System.arraycopy(intToByte(payloadLen), 0, newdex, totalLen-4, 4); // 最后4字节为长度
// 修改DEX file size文件头
fixFileSizeHeader(newdex);
// 修改DEX SHA1 文件头
fixSHA1Header(newdex);
// 修改DEX CheckSum文件头
fixCheckSumHeader(newdex);
String str = "files/classes.dex"; // 创建一个新文件
File file = new File(str);
if (!file.exists())
file.createNewFile();
FileOutputStream localFileOutputStream = new FileOutputStream(str);
localFileOutputStream.write(newdex); // 将新计算出的二进制dex数据写入文件
localFileOutputStream.flush();
localFileOutputStream.close();
这里,我们运行打包了一个DexPackTool.jar文件,用以后续的加固工作。
详细代码可以参考: DexPackTool
4.脚本执行加固
以上我们可以生成三个文件
- 源程序ApkTest工程的,命名为SourceApk.apk,
- 壳程序ShellApk工程,名为app-debug.apk
- 加固工具, 名为DexPackTool.jar
为了方便我们写一个脚本来实现加固
rm app-test.apk
python getDex.py
java -jar DexPackTool.jar
cp files/classes.dex .
# exchange dex
aapt r app-debug.apk classes.dex
aapt a app-debug.apk classes.dex
cp app-debug.apk app-test.apk
#sign
./resign.sh
adb install app-test.apk
- 拷贝SourceApk.apk到files文件夹下
- 通过getDex.py从app-debug.apk中获取classes.dex文件,拷贝到files文件夹下
- DexPackTool把SourceApk.apk加密后加入到classes.dex文件末尾,重新生成新的classes.dex文件
- 通过aapt命令删除app-debug.apk的classes.dex文件,并加入加固后新生成的classes.dex文件
- 重新签名apk,安装
详细代码可以参考: ShellTools
5.验证
我们运行ShellApk程序可以看下加入的log信息:
12-25 14:30:22.062 19460-19460/? D/ResourcesManager: creating new AssetManager and set to /data/app/com.jared.shellapk-1/base.apk
12-25 14:30:22.082 19460-19460/? I/ShellApplication: apk size:0
12-25 14:30:22.593 19460-19460/? D/ShellApplication: apk size: 6103481
12-25 14:30:22.593 19460-19460/? I/System.out: 22b245
12-25 14:30:26.887 19460-19460/com.jared.shellapk I/ShellApplication: classloader:dalvik.system.DexClassLoader[DexPathList[[zip file "/data/data/com.jared.shellapk/app_payload_odex/payload.apk"],nativeLibraryDirectories=[/data/data/com.jared.shellapk/app_payload_lib, /vendor/lib, /system/lib]]]
12-25 14:30:26.887 19460-19460/com.jared.shellapk I/ShellApplication: actObj:class com.jared.apktest.MainActivity
12-25 14:30:26.887 19460-19460/com.jared.shellapk I/ShellApplication: onCreate
12-25 14:30:26.897 19460-19460/com.jared.shellapk I/ShellApplication: app:com.jared.apktest.ApkTestApplication@3839a4b9
12-25 14:30:26.907 19460-19460/com.jared.shellapk I/ApkTestApplication: Apk Test Application onCreate:com.jared.apktest.ApkTestApplication@3839a4b9
12-25 14:30:26.917 19460-19460/com.jared.shellapk D/MainActivity: onCreate
运行的是ShellApk,但是实际上最后都是运行ApkTest工程的效果。
参考:
以上是关于Android开发学习之路-加固实践的主要内容,如果未能解决你的问题,请参考以下文章