一、前言

经过上次写完在ELF文件中根据函数名找函数,就准备开始编写so文件函数加密,这里这是对代码进行加密,还没有对函数名做混淆,会放到下次写。还有本次的测试机是nexus4,操作系统为android 4.4。

二、函数加密

一般在android中,各种核心的东西都会放在so文件中,因为native层的代码分析难度大,执行效率高。本文选择对so文件的核心函数进行加密,用来对抗静态分析。在加密之前,需要知道加密的原理和流程,在我们对so文件的核心函数加密后,我们需要对其解密,如果不解密的话,你的程序肯定会崩溃。解密的时机肯定是在函数运行之前,本文选择了在 JNIOnLoad函数里执行解密操作,当然JNIOnLoad不是最早执行的函数,但是在库加载时就运行了,是满足我们的要求的。

首先介绍一下对so文件里的函数的加密,要对函数加密当然要先找到函数的地址,本人以前写过一个根据函数名找函数地址的文章子,不了解的同学可以先看看这篇文章,文章地址:https://blog.csdn.net/qq_16812035/article/details/87866217。找到函数地址后和函数大小后,事情就变得简单了。本人做的加密就是对函数的每个字节码进行异或操作,然后保存为新的so文件。

void entryCode(size_t code_base, size_t code_size) {for (size_t i = 0; i < code_size; ++i) {((char*)code_base)[i] = ((char*)code_base)[i] ^ 0xA;}
}

生成新的so文件后,需要替换掉原so文件,替换掉后,重新打包,签名,安装到手机上,运行,直接崩溃,是因为程序执行到你的函数时,因为是加密的,执行了乱七八糟的指令,所以崩溃了,所以我们要编写解密代码。

三、解 密 

我们要在so文件里定义一个JNIOnLoad来覆盖默认的JNIOnload函数,然后开始写我们的解密程序,第一步当然是找到我们的so函数的基地址,在linux中,有一句名言,"一切皆文件",进程也是。我们随便进入一个进程的目录,执行cat maps,可以看到liblog.so的地址范围在0x400e6000-0x400e9000之间,所以它的基地址就是0x400e6000。

接下来就是代码实现部分,首先找到本进程的maps文件,打开并且遍历文件的每一行,找到要查找的库,最后提取它的基地址。代码如下。

//获取目标库文件基地址
unsigned long get_lib_addr(char* libname){char buf[4096];char *temp;unsigned long ret;//获取pidint pd = getpid();//生成进程maps路径sprintf(buf,"/proc/%d/maps",pd);//打开maps文件FILE* fp = fopen(buf,"r");if(fp==NULL){puts("open fail");fclose(fp);return -1;}//按行读取while (fgets(buf, sizeof(buf),fp)){//根据目标函数名找到对应库信息if(strstr(buf,libname)){//字符串切割,返回库函数基地址temp = strtok(buf, "-");//将字符串转为无符号整数ret = strtoul(temp, NULL, 16);__android_log_print(ANDROID_LOG_INFO, "JNITag", "base =  0x%x", ret);break;}}fclose(fp);return ret;
}

找到基地址后,我们要对so文件进行解析,这时候so文件已经加载到内存了,我们不能像以前一样查找我们的函数,我们需要了解一下ELF文件格式的其它信息,在ELF文件头中,有两个字段,指向程序头的偏移e_phoff和程序头数组的数量e_phnum。

程序头中包含一个结构数组,用来描述与程序执行直接相关的目标文件结构信息。

我们要找的.hash、symstr、dynsym节在程序执行的时候会合并到一个类型为.dynamic的段中。代码很简单,遍历程序头数组,检查e_type类型,找到对应动态节区数组。

        Elf32_Ehdr* ehdr = (Elf32_Ehdr*)addr;Elf32_Phdr* phdr = (Elf32_Phdr*)(ehdr->e_phoff + addr);Elf32_Dyn* dyn;for(size_t i = 0;i<ehdr->e_phnum;++i){if(PT_DYNAMIC == phdr->p_type){dyn = (Elf32_Dyn*)(phdr->p_vaddr + addr);break;}phdr++;}

下面是动态节区结构体,d_tag对应节区头的e_type,d_ptr为虚拟地址的偏移。

根据动态节区节区结构体就可以找到我们要的.hash,dynstr,dynsym的内容,代码如下。

        size_t dyncount = phdr->p_memsz/ sizeof(Elf32_Dyn);size_t hashoff = 0;size_t symtaboff = 0;size_t strtaboff = 0;for(size_t i = 0;i<dyncount;++i){if(DT_HASH == dyn->d_tag){hashoff = dyn->d_un.d_ptr;}else if(DT_SYMTAB == dyn->d_tag){symtaboff = dyn->d_un.d_ptr;}else if(DT_STRTAB == dyn->d_tag){strtaboff = dyn->d_un.d_ptr;}dyn++;}

找到这三个节后,事情就好办了,根据.hash节遍历找到目标符号表,然后找到函数偏移地址和大小,可以参考前面链接中的文章。

        unsigned long eflag = ELFHash(name);Elf32_Word* bucketchain = (Elf32_Word*)(hashoff + addr);Elf32_Word bucket_count =  bucketchain[0];Elf32_Word chain_count = bucketchain[1];Elf32_Word* bucket = &bucketchain[2];Elf32_Word* chain = &bucketchain[2 + bucket_count];Elf32_Sym* sym = (Elf32_Sym*)(symtaboff+ addr);char* str = (char*)(strtaboff + addr);funInfo fun_info;size_t mod = eflag%bucket_count;for(size_t i = bucket[mod];i!=0;i = chain[i]){char* findstr = (char*)(str + sym[i].st_name);if(!strcmp(findstr,name)){size_t code_revision = sym[i].st_value;if(code_revision&0x00000001){code_revision--;}fun_info.code_offset = code_revision;fun_info.size = sym[i].st_size;break;}}

得到函数地址后,我们可以进行解密了,因为之前加密的方法是异或0xA,所以这时候我们同样多加密的字节码异或上0xA,这叫做异或的自反性,A^B = C,可以得到C^B = A。但是我们不能像加密一样,直接改,因为有内存保护属性,代码段的权限通常只有读和执行,并没有写,强行写的话,程序会崩溃。所以我们要先修改内存保护属性,然后解密,解密完,将内存保护属性改为原来的权限。linux里提供了一个修改内存权限的函数mprotect。

int mprotect(const void *start, size_t len, int prot)

可以看到函数一共有三个参数:

  1. start 起始地址
  2. len 大小, 必须是页大小的倍数
  3. prot 权限

成功时返回0,失败返回-1。代码如下。

 size_t pagesize = (fun_info.code_offset/PAGE_SIZE + 1)*PAGE_SIZE;mprotect(libBase, pagesize, PROT_EXEC|PROT_READ|PROT_WRITE);for(size_t i = 0;i < fun_info.size;++i){((char*)(fun_info.code_offset + libBase))[i]^=0xA;}mprotect(libBase,pagesize,PROT_EXEC|PROT_READ);

下面看下效果,加密前。

加密之后。

四、总结

在nexus4手机 android4.4中正常运行,其它机型和操作系统没试过,可能会有很多问题,以后有时间测试一下,加密后加大了逆向分析的难度,但这种程度的加密,还是很容易破解的,继续学习点猥琐的招数吧。

代码下载:https://github.com/newhasaki/soEncryption

Android so文件函数加密相关推荐

  1. android 删除文件函数,Android updater

    这是Android系统来运行updater-scripts的Edify语言的基本介绍. 大部分的Edify命名都是函数,当调用这些函数结束的时候,会返回数据给脚本.当然,你也可以使用这些函数的返回值来 ...

  2. android数据库文件是否加密存储,详解Android数据存储之SQLCipher数据库加密

    前言: 最近研究了Android Sqlite数据库以及ContentProvider程序间数据共享,我们清晰的知道Sqlite数据库默认存放位置data/data/pakage/database目录 ...

  3. 【Android 安全】DEX 加密 ( 代理 Application 开发 | 项目中配置 OpenSSL 开源库 | 使用 OpenSSL 开源库解密 dex 文件 )

    文章目录 一.项目中配置 OpenSSL 开源库 二.OpenSSL 开源库解密参考代码 三.解密 dex 文件的 Java 代码 四.解密 dex 文件的 Jni 代码 参考博客 : [Androi ...

  4. 【Android 安全】DEX 加密 ( 代理 Application 开发 | multiple-dex-core 依赖库开发 | 配置元数据 | 获取 apk 文件并准备相关目录 )

    文章目录 一.multiple-dex-core 依赖库作用 二.配置目录元数据 三.multiple-dex-core 代理 Application 四.获取 apk 文件并准备相关目录 五.相关代 ...

  5. 【数据安全】3. Android 文件级加密(File-based Encryption)技术介绍

    1. 概览  1.1 设计概览 采用文件级加密时: 可以使用不同的密钥对不同的文件进行加密,也可以对加密文件单独解密 可以有的放矢,没有安全要求的文件可以不加密 支持多用户,不同用户使用不同的密钥 基 ...

  6. 【Android 安全】DEX 加密 ( Java 工具开发 | apk 文件签名 )

    文章目录 一.生成 jks 文件 二.签名命令 三.执行结果 四.处理 Unsupported major.minor version 52.0 错误 参考博客 : [Android 安全]DEX 加 ...

  7. 【Android 安全】DEX 加密 ( Java 工具开发 | apk 文件对齐 )

    文章目录 一.apk 对齐操作 二.apk 对齐命令 三.apk 对齐操作代码示例 四.apk 对齐执行结果 参考博客 : [Android 安全]DEX 加密 ( 常用 Android 反编译工具 ...

  8. 【Android 安全】DEX 加密 ( Java 工具开发 | 解压 apk 文件 | 加密生成 dex 文件 | 打包未签名 apk 文件 | 文件解压缩相关代码 )

    文章目录 一.解压 apk 文件 二.加密生成 dex 文件 三.打包未签名 apk 文件 四.完整代码示例 五.文件解压缩相关代码 六.执行结果 参考博客 : [Android 安全]DEX 加密 ...

  9. 【Android 安全】DEX 加密 ( Java 工具开发 | 生成 dex 文件 | Java 命令行执行 )

    文章目录 一.生成 dex 文件 二.生成 dex 文件代码示例 三.生成 dex 结果 参考博客 : [Android 安全]DEX 加密 ( 常用 Android 反编译工具 | apktool ...

最新文章

  1. 如何在Win7电脑上增加新磁盘分区?
  2. MySQL新建匿名用户_初始化MySQL用户(删除匿名用户)
  3. “病毒防治”页面中“社区热帖”版块不显示
  4. [Python图像处理] 二十八.OpenCV快速实现人脸检测及视频中的人脸
  5. python字符串包含关系_Python实现判断一个字符串是否包含子串的方法总结
  6. docker 搭建nginx php mysql_Docker搭建PHP/Nginx/MySQL/Composer环境
  7. java支付宝rsa2签名_sign_tools_RSA256_win
  8. 阿里巴巴发布第四财季财报 菜鸟驿站包裹量增长100%
  9. linux怎么看java环境变量_linux下配置jdk环境变量以及查看java版本
  10. 【codeup22562】最长回文子串(dp基础题)
  11. DuiLib教程--认识她
  12. C语言必背18个经典程序(含免费源码大全)
  13. 有一种VR电影比爱情动作片更“爽”
  14. Robotframework基础篇(一):使用ride编辑器
  15. 吃货在东京 -- 记那段吃不饱的日子 之二 丰州的雪花牛肉
  16. 特别篇:公主,快放开那只巨龙
  17. Flutter 2.10 正式发布,包含 Windows 平台正式版,快来看看有什么新内容
  18. Windows Moblie上的网络连接
  19. 【C++】揭开“引用”的庐山真面目
  20. opencv4使用sift以及surf

热门文章

  1. 学成在线 课程 页面
  2. 如何使百度网盘加速下载文件
  3. 每日分享html之3个logo、1个背景、1个button
  4. 文字翻译器有哪些?文字翻译器哪个好?
  5. Foxmail 7.2数据迁移,导入旧数据
  6. 微信小程序 - [完整源码] 全屏左右菜单联动效果,左侧分类与右侧内容联动,类似美团饿了么的点餐页面 “左边菜单,右边内容“ 效果(开箱即用的示例源码,代码干净整洁且注释详细)
  7. 用keras采用DCGAN网络生成三国群英传7的人物头像
  8. 网络安全笔记-TCP/IP
  9. pandas:世界各国GDP数据集数据清洗案例
  10. Oracle`.`聚宝盆