0%

Android APK加壳技术的研究-内存动态加载

本文主要参考博客:

Android4.0内存Dex数据动态加载技术
APK加壳【2】内存加载dex实现详解

CSDN的大牛写的博客太简洁了,内存动态加载主要参考taoyuanxiaoqi的博文,写得比较详细。我的上一篇文章提到使用DexClassLoader函数,把apk文件加载进来,不过这样的做法会导致apk文件躺在文件系统中,这样的做法并不安全,在4.0之后,可以通过封装Dalvik_dalvik_system_DexFile_openDexFile_bytearray函数,可以在解密出dex文件的byteArray数组后,不需要保存到文件系统的路径上,直接通过4.0的函数在内存中读取。
参考博客他们的做法是,通过修改DexShellTool,并不是把payload.apk拼接到unshell.dex的后面,只是把payload.apkclasses.dex拼接到unshell.dex的后面。然后通过读取出classes.dex数组,并不保存到文件系统中,实现在内存中动态加载。
但是我比较懒,不想修改DexShellTool,还是把payload.apk拼接到后面,然后把payload.apk解压放到文件系统中,然后把里面的classes.dex数组读出来,这样进行动态加载,主要是想验证Dalvik_dalvik_system_DexFile_openDexFile_bytearray函数,文件系统还是躺着一个apk。

配置环境

本文的编译环境如下:

  • Android Studio 1.2.1.1
  • JDK 1.7.0_79
  • SDK
  • NDK

跟我的上一篇文章的环境是一样的,由于taoyuanxiaoqi的博文写的比较详细,代码我就不一一copy了,主要说一下编译过程中可能由于编译版本不同而提示的错误。 建立一个com.droider.dexunshellram的project,app里面的build.gradle如下,minSdkVersion为14,安卓4.0。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
apply plugin: 'com.android.application'

android {
compileSdkVersion 22
buildToolsVersion "22.0.1"

defaultConfig {
applicationId "com.droider.dexunshellram"
minSdkVersion 14
targetSdkVersion 22
versionCode 1
versionName "1.0"

ndk {
moduleName "unshellram"
abiFilters "armeabi-v7a"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.1.1'
}

AndroidManifest.xml如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.droider.dexunshellram">
<application
android:debuggable="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:name="com.droider.dexunshellram.ProxyApplication">
<meta-data android:name="APPLICATION_CLASS_NAME" android:value="android.app.Application"/>
<activity
android:name="com.droider.crackme0201.MainActivity"
android:label="@string/title_activity_main" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

java层

在jni文件的编写,基本上是没有提示错误的,完全参考第二篇taoyuanxiaoqi博主那样编写就行了,编写3个文件,分别是com_droider_dexunshellram_unshellram.ccom_droider_dexunshellram_unshellram.hdalvik_common.h。 遇到的问题:

问题1

在java层中,主要是基于我上一篇文章的com.droider.dexunshell应用修改。无意中发现上一篇文章的RefInvoke.java有漏洞,因为taoyuanxiaoqi博主在DynamicDexClassLoader中使用了import com.eebbk.mingming.k7utils.ReflectUtils;,应该是别人写的反射函数,我想用回自己的RefInvoke。但是上一篇文章中CSDN博主的RefInvoke写法有问题。应该在invokeStaticMethod函数中加入method.setAccessible(true);

1
2
3
4
5
6
public static  Object invokeStaticMethod(String class_name, String method_name, Class[] pareTyple, Object[] pareVaules){

try {
Class obj_class = Class.forName(class_name);
Method method = obj_class.getDeclaredMethod(method_name, pareTyple);
method.setAccessible(true);

问题2

DynamicDexClassLoader.java中,需要把defineClassdefineClass改成defineClassNative,要不会提示找不到defineClass的错误。

1
2
3
4
5
private Class defineClass(String name, ClassLoader loader, int cookie) {
return (Class) RefInvoke.invokeStaticMethod(DexFile.class.getCanonicalName(),
"defineClassNative", new Class[]{String.class, ClassLoader.class,
int.class}, new Object[]{name, loader, cookie});
}

问题3

DynamicDexClassLoader.javafindClass函数中,需要加一个break和注释两行函数,要不会一直不断defineClass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    protected Class findClass(String name) throws ClassNotFoundException {
……
for (int z = 0; z < as.length; z++) {
Log.d(TAG, "findClass-as[z]" + as[z]);
if (as[z].equals(name)) {
Log.d(TAG, "findClass-name" + as[z]);
cls = defineClass(as[z].replace('.', '/'),
mContext.getClassLoader(), cookie);
break;
} else {
// defineClass(as[z].replace('.', '/'), mContext.getClassLoader(),
// cookie);
}
}
……

所以在Java层中按taoyuanxiaoqi博主的文章相应修改一下ProxyApplication.javaRefInvoke.java。然后添加DynamicDexClassLoader.javaunshellram.java。就大功告成,其实上面这些错误的解决方法是在taoyuanxiaoqi的博文评论中找到的。
编译通过后,也需要向我上一篇文章一样,运行一下DexShellTool,把apk拼接到后面,再重新签名安装,apk为上一篇博文的crackme0201
dexunshellram程序app文件夹的代码包链接

搞定,收工。