<span style="font-size:24px;">这篇是一系列的关于SO文件保护的自我理解,SO文件保护分为加固,混淆以及最近炒的比较火的虚拟机,由于本人菜鸟,无力分析虚拟机,我相信以后会有机会。。。加固就是将真正的so代码保护起来,不让攻击者那么轻易的发现,至于混淆,由于ART机制的介入,使得O-LLVM越来越火,这以后有机会再分析,这次主要是基于有源码的so文件保护,下次介绍无源码的so文件保护,废话不多说,开搞</span>
    在这之前首先对elf文件结构有一定的了解,不一定完全了解,本菜鸟就不是完全懂,在文章开始之前有个知识点必须了解:

这两个节头要有所了解:.init:可执行指令,构成进程的初始化代码,发生在main函数调用之前。.fini:进程终止指令,发生在main函数调用之后。以上这么分析感觉有点像c++的构造函数和析构函数,的确构造和析构是由此实现的。并且结合GGC的可扩展机制:

__attribute__((section(".mytext")));可以把相应的函数和要保护的代码放在自己所定义的节里面。这就引入了我们今天的主题,可以把我们关键的so文件中的核心函数放在自己所定义的节里面,然后进行加密保护,在合适的时机构造解密函数,当然解密函数可以用这个_attribute__((constructor))进行定义;类似于C++构造函数发生在main函数之前。OK这个就是这篇文章的核心思想。流程安排:1.编写一个Native程序,对里面的关键函数放在自己所定义的节中,并且编写解密函数(当然这个是在你已知加密函数的基础上)2.对得到的.so文件进行加密3.加密后的替换验证接下来走流程:1.编写一个简单的计算器,把核心的代码放在.so文件里面如图:
这个比较简单很容易理解:接下来是关键函数的自定义与解密函数:直接看代码:
首先看到的是getLibAddr()这个函数:在介绍这个函数之前首先了解一个内存映射问题:
和Linux一样,Android提供了基于/proc的“伪文件”系统来作为查看用户进程内存映像的接口(cat /proc/pid/maps)。可以说,这是Android系统内核层开放给用户层关于进程内存信息的一扇窗户。通过它,我们可以查看到当前进程空间的内存映射情况,模块加载情况以及虚拟地址和内存读写执行(rwxp)属性等。
接下来包括内存权限的修改以及函数的解密算法,最后包括内存权限的修改回去,应该都比较好理解。ok,以上编写完以后就编译生成.so文件。
2.对得到的.so文件进行加密:这一块也是一个重点,大致上逻辑我们可以这么认为:先找到那个我们自己所定义的节,然后找到对应的offset和size,最后进行加密,加密完以后重新的写到另一个新的.so文件中,这块是需要建立在对ELF了解的基础上这里重点了解一下这个加密函数,在自己写的时候可以在这个基础上进行改进。首先看一下这个核心加密代码:

private static void encodeSection(byte[] fileByteArys){//读取String Section段System.out.println();int string_section_index = Utils.byte2Short(type_32.hdr.e_shstrndx);elf32_shdr shdr = type_32.shdrList.get(string_section_index);int size = Utils.byte2Int(shdr.sh_size);int offset = Utils.byte2Int(shdr.sh_offset);int mySectionOffset=0,mySectionSize=0;for(elf32_shdr temp : type_32.shdrList){int sectionNameOffset = offset+Utils.byte2Int(temp.sh_name);if(Utils.isEqualByteAry(fileByteArys, sectionNameOffset, encodeSectionName)){//这里需要读取section段然后进行数据加密mySectionOffset = Utils.byte2Int(temp.sh_offset);mySectionSize = Utils.byte2Int(temp.sh_size);byte[] sectionAry = Utils.copyBytes(fileByteArys, mySectionOffset, mySectionSize);for(int i=0;i<sectionAry.length;i++){//sectionAry[i] = (byte)(sectionAry[i] ^ 0xFF);sectionAry[i]=(byte) ~sectionAry[i];}Utils.replaceByteAry(fileByteArys, mySectionOffset, sectionAry);}}//修改Elf Header中的entry和offset值int nSize = mySectionSize/4096 + (mySectionSize%4096 == 0 ? 0 : 1);byte[] entry = new byte[4];entry = Utils.int2Byte((mySectionSize<<16) + nSize);Utils.replaceByteAry(fileByteArys, 24, entry);byte[] offsetAry = new byte[4];offsetAry = Utils.int2Byte(mySectionOffset);Utils.replaceByteAry(fileByteArys, 32, offsetAry);}
以上加密是没有问题的,但是对于最后so文件头的修改简单的说明一下:

修改so文件为什么不会报错的原因进行简单的说明:我们在这考虑一个问题就是Section与Segment的区别,由于OS在映射ELF到内存时,每一个段会占用是页的整数倍,这样会产生浪费,在操作系统的层面来讲,可以吧相同权限的section放在一起成为一个Segment再进行映射,这样一来减少浪费,但是在映射的时候会有一部分信息不会映射到内存中,可以看这个图:、因此来说修改这些不会报错。3.对于文件替换后没有什么问题,运行结果为:总结:该篇是在有源码的基础上进行对特定的section进行加密,但是试想一下,有多少情况下才能有源码,因此局限性比较大,下一篇是基于二进制级别的特定函数的加密,链接为:点击打开链接源码是:http://download.csdn.net/detail/feibabeibei_beibei/9532172
#include "com_example_jni02_CallSo.h"
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <elf.h>
#include <sys/mman.h>
#include <Android/log.h>
//这里对 Java_com_example_jni02_CallSo_plus这个方法进行加密保护
jint JNICALL Java_com_example_jni02_CallSo_plus(JNIEnv* env, jobject obj, jint a, jint b)  __attribute__((section (".mytext")));
JNIEXPORT jstring JNICALL Java_com_example_jni02_CallSo_getString(JNIEnv* env, jobject obj){return (*env)->NewStringUTF(env,"Hello");
}
JNIEXPORT jint JNICALL Java_com_example_jni02_CallSo_plus(JNIEnv* env, jobject obj, jint a, jint b){return a+b;
}
//在调用so文件进行解密
void init_Java_com_example_jni02_CallSo_plus() __attribute__((constructor));
unsigned long getLibAddr();void init_Java_com_example_jni02_CallSo_plus(){char name[15];unsigned int nblock;unsigned int nsize;unsigned long base;unsigned long text_addr;unsigned int i;Elf32_Ehdr *ehdr;Elf32_Shdr *shdr;base=getLibAddr();ehdr=(Elf32_Ehdr *)base;text_addr=ehdr->e_shoff+base;nblock=ehdr->e_entry >>16;nsize=ehdr->e_entry&0xffff;__android_log_print(ANDROID_LOG_INFO, "JNITag", "nblock =  0x%d,nsize:%d", nblock,nsize);__android_log_print(ANDROID_LOG_INFO, "JNITag", "base =  0x%x", text_addr);printf("nblock = %d\n", nblock);//修改内存权限if(mprotect((void *) (text_addr / PAGE_SIZE * PAGE_SIZE), 4096 * nsize, PROT_READ | PROT_EXEC | PROT_WRITE) != 0){puts("mem privilege change failed");__android_log_print(ANDROID_LOG_INFO, "JNITag", "mem privilege change failed");}//进行解密,是针对加密算法的for(i=0;i<nblock;i++){char *addr=(char*)(text_addr+i);*addr=~(*addr);}if(mprotect((void *) (text_addr / PAGE_SIZE * PAGE_SIZE), 4096 * nsize, PROT_READ | PROT_EXEC) != 0){puts("mem privilege change failed");}puts("Decrypt success");
}
//获取到SO文件加载到内存中的起始地址,只有找到起始地址才能够进行解密;
unsigned long getLibAddr(){unsigned long ret=0;char name[]="libaddcomputer.so";char buf[4096];char *temp;int pid;FILE *fp;pid=getpid();sprintf(buf,"/proc/%d/maps",pid);fp=fopen(buf,"r");if(fp==NULL){puts("open failed");goto _error;}while (fgets(buf,sizeof(buf),fp)){if(strstr(buf,name)){temp = strtok(buf, "-");ret = strtoul(temp, NULL, 16);break;}}_error:fclose(fp);return ret;
}
在这里重点解释这个解密函数:

Android SO文件保护加固——加密篇(一)相关推荐

  1. Android中apk加固完善篇之内存加载dex方案实现原理(不落地方式加载)

    一.前言 时隔半年,困扰的问题始终是需要解决的,之前也算是没时间弄,今天因为有人在此提起这个问题,那么就不能不解决了,这里写一篇文章记录一下吧.那么是什么问题呢? 就是关于之前的一个话题:Androi ...

  2. android中的so加固,so加固-加密特定section中的内容

    Android逆向之旅-基于对so中的section加密技术实现so加固 这篇文章写得真心好,建议先阅读一下原著,这里只是自己的实践过程(纸上得来终觉浅,绝知此事要躬行),和一些更细节的解释罢了. 一 ...

  3. Android App Bundle混淆加密加壳加固保护的解决方案(过Google App上架审核)

    Android AAB简介和AAB包格式 AAB即Android App Bundle,是Google官方发布的一种新的App包格式,可以有效缩减App大小,提升用户安装和更新App的体验.在Goog ...

  4. 【Android 教程系列第 30 篇】为什么要为 App 应用加固 ?如何为 App 应用加固 ?

    这是[Android 教程系列第 30 篇],如果觉得有用的话,欢迎关注专栏. 当我们开发的 App 准备做上架应用市场时,应用市场会要求上架的 App 做加固处理,这是为什么呢 ? 文章目录 一:为 ...

  5. Android之Apk加固

    上一篇:Android之Apk打包原理之后,今天记录一下多渠道打包. 首先,说明一下Apk加固的意义: 懂点反编译原理的人可以轻松编译出apk的源码资源,并且可以修改资源代码.重新打包编译. apk加 ...

  6. 【Android 安全】DEX 加密 ( Application 替换 | 兼容 ContentProvider 操作 | 源码资源 )

    文章目录 一. 命中 ActivityThread 中 installProvider 方法的分支三 1. 原理分析 2. 代码实现 二. 在 ContextImpl 的 createPackageC ...

  7. 【Android 安全】DEX 加密 ( Application 替换 | 替换 LoadedApk 中的 Application mApplication 成员 )

    文章目录 一. 当前 Application 替换进度 二. 替换 LoadedApk 中的 Application mApplication 成员 一. 当前 Application 替换进度 上一 ...

  8. 【Android 安全】DEX 加密 ( Application 替换 | ActivityThread 中的 mAllApplications 集合添加 Application )

    文章目录 一. 当前 Application 替换进度 二. ActivityThread 中的 mAllApplications 集合添加 Application 一. 当前 Application ...

  9. 【Android 安全】DEX 加密 ( Application 替换 | 创建用户自定义 Application | 替换 ContextImpl 对象的 mOuterContext 成员 )

    文章目录 一.创建用户自定义 Application 二.替换 ContextImpl 对象的 mOuterContext 成员 dex 解密时 , 需要将 代理 Application 替换为 真实 ...

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

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

最新文章

  1. 与用户登录有关的命令w who whoami last lastb lastlog
  2. mysql查询字符串出现次数
  3. 中国AI论文,爆了!
  4. MySql中有哪些存储引擎
  5. Vue入门 ---- vuex
  6. 报错:Unchecked runtime.lastError:Could not establish connection. Receiving end does not exist.
  7. Yii-- DeleteAll连表删除报错问题的解决方法
  8. 不限流量的物联卡是否真存在
  9. intelj maven 指定编译器版本
  10. 如何用EasyRecovery 快速找回误删的截图
  11. JavaWeb请求的重定向与转发:getRequestDispatcher()的forward方法,sendRedirect方法,以及重定向与转发的区别
  12. axure rp10安装教程,axurerp10安装步骤
  13. stm32之I2C编程前期准备
  14. 逸尘杀菌洗地机2 Pro上手体验
  15. 二、对HEVC/H.265视频编解码器进行隐写的基本思路
  16. Array,String 方法
  17. 测试人生 | 从外行到外包,从手工测试到知名互联大厂测开 这个90后是怎么腾飞的?
  18. 【真的】git pull --all 或 git fetch --all取到自己本地所有分支的最新内容
  19. 微软 2018 开源大事记
  20. Android 设置壁纸被拉伸(固定壁纸 )

热门文章

  1. 放弃75W年薪,回老家当公务员,提离职被领导教育,网友:leader嫉妒了
  2. css面试精讲之防止高度坍塌的4种方式
  3. EfficientFormer | 苹果手机实时推理的Transformer模型,登顶轻量化Backbone之巅
  4. 【附案例】UI交互设计不会做?设计大神带你开启动效灵感之路
  5. 深度:企业为什么需要一个平台级的OA产品?
  6. 中小企业OA系统视频教程(更新程度:完毕)送ppt源码
  7. 如何撰写数据中台蓝图方案
  8. 北京胜新疆夺CBA总冠军 苏群:广东依旧实力最强
  9. rust货轮什么时候出现_中国最早的汉字出现于什么时候?
  10. Python PIL库对阻挡文件blk进行解析,生成红绿色位图