加固学习

3种加固方式的学习记录

  1. 在java层为.apk文件进行加固
  2. 在Native层为.dex文件进行加固
  3. 在Native层为.so文件进行加固

1 Java层加固

1.1 Dex文件混淆加密

混淆加 密主要是为了隐藏 dex 文件中关键的代码,力度从轻到重包括:

  • 静态变量的隐藏

  • 函数的重复定义

  • 函数的隐藏

  • 整个类的隐藏。

混淆后的 dex 文件依旧可以通过 dex2jar jade 等工具的反编译成 Java 源码,但是里面关键的代码已经看不到了。

四种混淆加密的实现方式都是通过修改 class_def 结构体中字段实现的。

1
2
3
4
5
6
7
8
9
10
struct DexClassDef {
u4 classIdx; /* index into typeIds for this class */
u4 accessFlags;
u4 superclassIdx; /* index into typeIds for superclass */
u4 interfacesOff; /* file offset to DexTypeList */
u4 sourceFileIdx; /* index into stringIds for source file name */
u4 annotationsOff; /* file offset to annotations_directory_item */
u4 classDataOff; /* file offset to class_data_item */
u4 staticValuesOff; /* file offset to DexEncodedArray */
};

1.1.2 静态变量隐藏

saticValuesoff保存了每个类中静态变量值的偏移,指向data区一个列表,格式为encode_array_item,如果没有此项,该值为0,实现静态变量隐藏只需要将该字段修改为0

1.1.2 函数的重复定义

class_def -> class_data -> virtual_methods -> code_ff表示的是某个类中某个函数的代码偏移地址。这里修改的都是 virtual_methods中 code_off。

实现方式:读取第一个函数的代码偏移地址,将接下来的函数偏移地址都修改为第一的值。

1.1.3 函数隐藏

class_def -> class_data -> virtual_methods_sizeclass_def -> class_data -> direct_methods_size 记录了类定义中函数的个数,如果没有定义函数则该值为0。所以只要将该值改为0,函数定义就会被隐藏。

1.1.4 类定义隐藏

class_def -> class_data_off 保存了具体类定义的偏移地址,也就是 class_def -> class_data 的地址,如果该值为0则所有实现将被隐藏。隐藏后会把类定义的所有东西都隐藏包括成员变量,成员函数,静态变量,静态函数。

1.2 APK 加固

1586332935168

  1. 待加密的apk文件进行加密
  2. 将已加密apk放在提取出来的壳apk的dex之后,
  3. 将新的dex文件重新放回壳apk,生成新的apk
1586333310192

需要修改dex的checksum,signature,filesize字段,最后需要添加源apk大小

1.2.1 实现原理

img

从已加壳apk中加载dex文件,得到源apk文件,解密源apk文件,加载解密之后的apk,找源程序的Application程序,使其运行。

通过反射置换android.app.ActivityThread中的mClassLoader,将其改为源apk的DexClassLoader

该DexClassLoader一方面加载了源程序、另一方面以原mClassLoader为父节点,这就保证了即加载了源程序又没有放弃原先加载的资源与系统代码

随后找到源程序的Application,通过反射建立并运行:找到解密之后的源Apk的Application类,运行的他的onCreate方法,这样源Apk才开始他的运行生命周期。

1.2.2 程序运行过程

  1. 在加密后的apk的application类中,重写attachBaseContest方法:
    1. 解密源apk
    2. 初始化自定义类加载器
    3. 利用反射,生成正确的Application对象
  2. 重写onCreat方法:
    1. 获取源apk的Application名称
    2. 利用反射,生成正确的Application对象
    3. 利用反射,设置ActivityThread中的Application信息
    4. 调用源程序application对象的onCreate()方法

2 native层加固dex文件

2.1 壳原理

将原始apk文件的dex文件加密之后,放在apk的资源路径下,直通通过native代码重新对dex文件进行解密加载,从而执行原始的dex文件

2.2 流程

  1. 提取原始apk的dex文件,加密后命名位demod.jar,将其放置在Assets目录下
  2. 编写libdemeod.so,用于解密demod.jar文件,并对其进行dexCLassLoader操作
  3. 在壳apk中,新建application作为壳的启动器,并重写attachBaseContext方法和onCreate方法。这两个方法将调用libdemeod.so的方法
  4. 修改AndroidMainfest.xml的application类名称位这个启动类名称
  5. 壳的 attachBaseContext方法里调用native代码,实现功能:解密dmeod.jar文件并利用DexClassLoader对其进行加载,并将其设置成已加载apk的类加载器(即修改成为android.app.LoadedApk的mClassLoader),加载原始apk的入口activity类。
  6. 壳的onCreate方法调用native代码,实现功能:调用与那时apk的真正application类里的attachBaseContext方法与DexClassLoader方法。

3 native层加固so文件

3.1 加密so的section

3.1.1 原理

c代码生成so文件时:

声明重要函数showMessage时指定将其存放在自定义的.mytext段(通过__attribute__((section(".mytext")));

编写解密函数并在其声明时指定其在main函数前运行(即通过__attribute(constructor))。从而使得so文件在被加载到内存后可第一时间对showMessage函数进行解密。此时生成初始so文件section_origin.so,对so文件尚未加密。

参考资料

入门级加固–三种加固方式学习记录