又一年要过完了,没有什么提高,总结一下学习过程的中的笔记,顺便锻炼一下写文档的能力。
过时的技术,各位大佬请轻喷。
Dex加固
加壳程序工作流程:加密源程序APK文件,把加密后数据写入壳APK的Dex文件尾部并添加数据长度,修改Dex文件头部的checksum,signature,file_size字段,使用源程序AndroidMainfest.xml替换壳程序的AndroidMainfest.xml文件内容。
脱壳程序工作流程:读取Dex文件末尾数据以及长度信息,解密数据保存文件,动态加载源APK。
加壳程序:
DEX文件头部结构如下:
修复CheckSum,CheckSum使用alder32算法校验除去magic,checksum以外的部分。
修复Signature,Signature使用sha1计算除去 magic ,checksum 和 signature以外的部分。
修改添加加密数据后的Dex文件大小
修改Dex头部信息时需要先修改filesize,在计算signature,最后计算checksum的值否则会提示DEX文件无效。
到这里加壳程序就完成了很简单,只是做加密数据,合并内容,修改dex header字段,生成新dex文件。
脱壳程序:
首先需要创建一个MyApplication继承Application,实现attachBaseContext()和onCreate方法,因为程序启动后会先调用Application,之后在调用Activity。
在attachBaseContext()里解密数据释放源APK文件,使用反射加载APK然后替换ClassLoader,代码如下。
到这里为止已经实现了一个简单的壳了,但是一些APP会有Application类,这时还需要替换源程序的Application,代码如下。
至此实现了一个壳的雏形,测试一下效果。
加密源APK生成新dex文件
把新dex文件放入壳Apk中,重新签名,安装程序,启动程序,通过log可以看见执行了源APK中的代码。
再说一下实现过程中发现的一些问题,在java层进行解密及动态加载过程很容易被反编译,反编译后的伪代码与源码几乎没差,可以清楚的看出壳的运行逻辑,同时还会释放出源apk文件存放在本地。这时需要把关键代码移植到Native层,同时在内存中加载文件,这里就不多说了可以看代码,为了偷懒部分代码还是在java实现,完全可以把attachBaseContext和onCreate用native实现。
So加固
一)通过加密节的方式加密
解密程序流程:通过__attribute__((section(".mytext")))属性将要加密的函数定义在.mytext节中,实现解密函数,添加__attribute__((constructor))属性,将代码定义在.init_array段。
解密函数的实现很简单,这里首先在getLibAddr函数中通过/proc/<pid>/maps文件获得加载的so文件路径和基址,通过ehdr->e_entry这个变量获取到被加密节的大小,ehdr->e_shoff获得加密节的地址偏移解密数据。
代码如下:
加密程序流程:解析elf结构,从文件头读取section偏移shoff,shnum和shstrtab,读取shstrtab中的字符串,从shoff读取section header,通过pShdr->sh_name读取节表名比较是否自定义节表名,通过pShdr->sh_offset pShdr->sh_size读取节表大小以及内容进行加密,修改section字段中的e_shoff为pShdr->sh_addr,修改e_entry为pShdr->sh_size,写入文件。
关于修改文件头的说明:作为动态链接库,e_entry入口地址是无意义的,因为程序被加载时,设定的跳转地址是动态连接器的地址,这个字段是可以被作为数据填充的。so装载时,与链接视图没有关系,即e_shoff、e_shentsize、e_shnum和e_shstrndx这些字段是可以任意修改。
代码如下:
加密后Add函数如下
二)加密SO中指定函数
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)