Android开发学习之路-加固实践

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6


前言

  • 起因也是想要看一些优秀的程序某些内容是怎么实现的,所以需要脱壳,但是对于怎么加固也还是比较感兴趣的,加固涉及到的安全的内容很多很多,这里也只是用个简单的例子来过把瘾,参考了些文章,只是为了理顺思路,也为之后的脱壳做个准备。
  • 其实加固可以理解为,一个应用程序,利用了插件化的功能,启动了另一个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工程的效果。

参考:


阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: android