移动安全 安全管理 应用案例 网络威胁 系统安全 应用安全数据安全 云安全
当前位置: 主页 > 信息安全 > 数据安全 >

Android经由过程hook手艺实现透明解密保障数据安然

时间:2013-11-18 10:39来源:TuZhiJiaMi企业信息安全专家 点击:
1、媒介 对用户在Android移动设备商保留首要的隐私文件,凡是采取一些加密保留的软件。但在手机上实现隐私空间的软件鳞次栉比,可是标题问题在于打开文件都需要利用该隐私空间,将加密
Tags数据安全(840)技术(9)Android(84)hook(1)  

  1、媒介

  对用户在Android移动设备商保留首要的隐私文件,凡是采取一些加密保留的软件。但在手机上实现隐私空间的软件鳞次栉比,可是标题问题在于打开文件都需要利用该隐私空间,将加密文件解密惠姑且文件,然后再选择利用法度打开文件。这将致利用户首要文件在设备上明文的存在,存在泄漏的风险。

  并且按照笔者的调研,对360隐私空间,利用法度对姑且文件点窜后不克不及再逆向加密回密文,导致加密把持只能一次进行。LBE隐私空间,相对较好,但其姑且文件存在生命周期太长。

  是以笔者经由过程现有常识会商一种采取hook手艺实现的透明加解密编制,不需要在设备上生成姑且文件,从而呵护用户首要隐私。

  2、手艺要点

  因为Android是基于linux内核的开源系统,按照说话环境不合可以分为Java层、Native C层、Linux Kernel层。Java层的安然是利用Java说话开辟,基于SDK,能实现的功能相对简单。Linux Kernel层安然,需要从源码做起,编译本身的系统,通用性不强。是以在Native C层,经由过程JNI开辟,可利用linux供给的函数实现更多的功能。

  在hook API方面与linux的hook近似利用ptrace 函数与plt表实现,还可以采取Inline hook的编制实现,可是不是是很不变,把持难度大年夜。其本质都是劫持函数调用。

  可是因为处于Linux用户态,每个过程都有本身自力的过程空间,所以必需先注进到所要hook的过程空间,点窜其内存中的过程代码,替代此中过程表的符号地址,是以其保存空间是所注进的过程,只能对某一过程进行HOOK。

  Ptrace函数是调试法度所用,功能强大年夜,不但可以附加某一过程(PTRACE_ATTACH),并且可以肆意点窜方针过程的内存空间(PTRACE_PEEKDATA,读内存。PTRACE_POKEDATA,写内存),乃至是存放器(PTRACE_SETREGS,PTRACE_GETREGS)

  根基流程是操纵存放器指令间断:

  ① PTRACE_ATTACH,绑定方针过程。

  ② PTRACE_GETREGS,获得方针过程存放器状况,并保留。

  ③ PTRACE_PEEKDATA与PTRACE_POKEDATA共同,保留原代码,写进要注进的代码到当前运行位置。

  ④ PTRACE_SETREGS,恢复存放器状况,并继续履行,这是注进的代码开端在方针过程内履行,注进代码完成HOOK,过程与Windows下类似。

  ⑤ 在HOOK完成后,注进的代码履行int3被ptrace捕获,方针过程再次暂停履行。

  ⑥ PTRACE_GETREGS,再次保留存放器。

  ⑦ PTRACE_PEEKDATA与PTRACE_POKEDATA共同还原代码。

  ⑧ PTRACE_SETREGS,恢复存放器,方针过程继续履行。

  ⑨ PTRACE_DETACH,撤消绑定方针过程。

  参考LBE实现道理和看雪上关于Hook Ioctl的文章都根基上遵循这类流程实现HOOK。

  在大白hook工作机制后,对实现Android上的透明加解密需要找到open和close函数的符号存在哪个动态链接库中,hook该利用法度的这个动态链接库,在open把持进行的时辰,将密文文件分块解密到内存中,并将该内存中的文件标识符返回。在close把持进行的时辰将内存中的明文加密到本地密文存储。

  3、关头流程

  1、浏览Android代码查找打开文件和封锁文件过程。这是我们实现透明加解密的关头。

  参考http://blog.chinaunix.net/uid-26926660-id-3326678.html的编制

  可以发现读取文件流的函数最终经由过程JNI编制的read函数实现,一样打开文件的把持最终都回结于open函数。

  而实现Java代码的JNI撑持的动态库是nativehelper.so是以我们需要hook的动态库即nativehelper.so。

  注:在Android初期版本即android2.3、Android4.0上open和close符号在nativehelper.so中,该文件有140k大年夜小。而在android4.1版本以上,谷歌重写了android原生库的实现,nativehelper.so被拆分,笔者在4.0平台进行的开辟并没有浏览寻觅4.1版本之上的。

  2、进行过程注进和ELF节替代

  过程注进就是将一段代码拷贝到方针过程,然后让方针过程履行这段代码的手艺。因为如许的代码机关起来比较复杂,所以实际环境下,只将很少的代码注进到方针过程,而将真正干事的代码放到一个共享库中,即.so文件。被注进的那段代码只负责加载这个.so,并履行里面的函数。因为.so中的函数是在方针过程中履行的,所以在.so中的函数可以点窜方针过程空间的任何内存,当然也能够加钩子,从而达到改变方针过程工作机制的目标。

  当然不是任何过程都有权限履行注进把持的。Android平台上的过程注进是基于ptrace()的,要调用ptrace()需要有root权限。今朝市道上的主流安然软件也都是基于过程注进来治理和节制其他利用过程的。这也就是为甚么这些安然软件需要获得root权限的启事。

  关于若何.so注进的实现,可以参考看雪论坛的上的一个注进库LibInject 和洗大年夜师的一个开源项目Android Injector Library。

  笔者对这类两种编制都有尝试,对Libinject,就是向方针过程中注进libhook.so,起首调用ptrace()函数,挂起该过程。然后遍历过程加载的libc.so,经由过程dlopen和dlsym函数点窜arm存放器的值,然后压进参数,so路径,并将之前找到的dlopen地址压进存放器,直接把持blx,便可让方针过程调用dlopen加载我们的so,同理dlsym调用我们的so里的函数。

  注进完成后,会调用libhook.so库中的hook_entry()函数,该函数实现加载hook函数实现的动态库,并对libnativehelper.so的got表和plt表的遍历和点窜。点窜成本身编写的实现open函数和close函数的动态库中的符号地址。是以需要注进两个库,因为libhook.so在履行完后需要detach方针过程,从而释放,而具体把持的动态库需要常驻内存。实现常驻内存需要在hook_entry()函数中显式加载动态库。

  以上注进今后的过程都由本身编程实现,可以或许加深对ELF格局理解。

  而采取Android Injector Library则相对简单,在调用可履行法度的主函数中实现便可:

  这个文件编译生成注进进口和符号表替代逻辑。

  * 1、在该函数中加载libhook.so经由过程此中的do_hook函数返回本来的open和close地址和要替代的新的open和close函数地址

  * 2、然后静态打开libnativehelper动态库,读取其布局遍历节表,找到全局符号表(GOT表),该表存储了外部依托符号的地址

  * 3、遍历GOT表找到本来的open函数和close函数地址,别离替代为新的open函数和新的close函数便可

图片1

  3、在进修这一过程中,需要体味linux的ELF格局,以下是进修ELF的笔记:拜见《法度员的自我涵养》假定熟谙则可跳过。

图片2

图片3

  ehdr->e_shstrndx索引指向shstrtab的节,可以用来索引节头的字符串名称描述。shstrtab表(Section Header String Table)保留段表顶用到的字符串,最多见的就是段名、

  常常利用的段名申明

  .rodata1Read Only Data,这类段里存放的是只读数据,好比字符串常量、全局const变量。跟”.rodata”一样

  .comment存放的是编译器版本信息

  .debug 调试信息

  .dynamic动态链接信息

  .hash 符号哈希表

  .line 调试时的行号表

  .note 额外的编译器信息。好比法度的公司名、发布版本号等

  .strtab String Table.字符串表

  .symtab Symbol Table.符号表

  .shstrtabSection String Table.段名表

  .plt .got动态链接的跳转表和全局进口表

  .init .fini法度初始化与终结代码段

  符号节,遍历节头时辰。鉴定每个节的类型是不是是SHT_SYMTAB或SHT_DYNSYM,那么对应的节就是符号节。符号节存放的是一张符号表,符号表也是一个持续存储的布局数组.

  编程过程顶用到的变量和函数都可以称之为符号,一个ELF文件中其实不只有一个符号节,凡是是两个,一个为”.dynsym”的动态节类型为SHT_DYNSYM,所有引进的外部符号在这里有所表现,另外一个为SHT_SYMTAB,名字为“.symtab”保留了所有有效符号信息。

  Symbol Table 符号表保留了一个法度在定位和重定位时需要的定义和援引的信息。一个符号表索引是响应的下标。符号表的存在乎义是表此刻多个方针文件进行链接的时辰,在链接中,方针文件之间彼此拼合实际上是方针文件之间对地址的援引,即对函数和变量的地址的援引,而函数和变量可以统称为符号(Symbol),函数名或变量名就是符号名(Symbol Name)。我们可以将符号看作是是链接中的粘合剂,全部链接过程就是基于符号才可以或许准确完成。在符号表”.symtab“中,其也是像段表的布局一样,是一个数组,每个数组元素是一个固定的布局来保留符号的相干信息,好比符号名(不是字符串,而是该符号名在字符串表的下标)、符号对应的值(多是段中的偏移,也多是符号的虚拟地址)、符号大年夜小(数据类型的大年夜小)等等。符号表中记实的通常为全局符号,好比全局变量、全局函数等等。

  方针文件的符号表包含定位或重定位法度符号定义和援引时所需要的信息。符号表进口布局定义以下:

  typedef struct{

  Elf32_Word st_name;

  Elf32_Addr st_value;

  Elf32_Word st_size;

  Unsigned char st_info;

  Unsigned char st_other;

  Elf32_Half st_shndx;

  }Elf32_Sym;

  此中st_name包含指向符号表字符串表(strtab)中的索引,从而可以获得符号名。St_value指出符号的值,多是一个尽对值、地址等。St_size指出符号相干的内存大年夜小,好比一个数据布局包含的字节数等。St_info划定了符号的类型和绑定属性,指出这个符号是一个数据名、函数名、section名仍是源文件名;并且指出该符号的绑定属性是local、global仍是weak。

  GOT表和PLT表

  GOT(Global Offset Table)表中每项都是本运行模块要援引的一个全局变量或函数的地址。可以用GOT表来间接援引全局变量、函数,也能够把GOT表的首地址作为一个基 准,用相对该基准的偏移量来援引静态变量、静态函数。因为加载器不会把运行模块加载到固定地址,在不合过程的地址空间中,各运行模块的尽对地址、相对位 置都不合。这类不合反应到GOT表上,就是每个过程的每个运行模块都有自力的GOT表,所以过程间不克不及共享GOT表。

图片4

  动态链接机制

  起首回顾一下Linux平台上,一个模块甲需要调用别的一个模块乙中的函数时的动态链接机制:

  1、模块甲在编译期间,将要援引的模块乙的名字与函数名写进本身的符号表。

  2、运行期模块甲调用时,调用流程是从调用代码到PLT表到GOT表再跳进模块乙。

  而若何包管模块甲的代码能从其PLT/GOT跳到准确的模块乙进口,这就是链接器做的工作。

  尺度Linux链接器是ld.so,撑持懒绑定,也就是说,模块甲在编译期间生成的调用模块乙的原始代码,流程是从调用代码到PLT表到链接器。运行期第一次调模块乙时,起首进进链接器,链接器按照调用信息加载模块乙搜索其符号并将找到的函数地址填进GOT表,以后的后续调用流程就直接走PLT/GOT表了。这类机制能削减加载时的开消,为Linux发行版等采取。

  Android当然内核基于Linux,但其动态链接机制却不是ld.so而是自带的linker,不撑持懒绑定。也就是说,上述模块甲乙假定在Android平台上,则是模块甲加载时,linker就会按照模块甲中的.rel.plt表和字符串表中的内容加载模块乙并搜刮其所需函数地址并预先填进GOT表。以后调用流程每次都直接走PLT/GOT表,不再进linker,PLT表中也省往了跳至linker的代码,这类流程和“勤奋”绑定近似,倒是为反对供给了一点便利。假定反对懒绑定的进口时模块乙还没加载地址也没找到,反对就没法进行了。

  要反对模块甲对乙的调用,一般思路是经由过程ptrace长途注进并加载一新反对模块至模块甲,并搜刮模块甲的GOT表,找到对模块乙的调用地址,改成新模块内的某函数地址,然后新模块内的这个函数在进行了本身的措置后,再跳到模块乙中。

  Android和Linux的链接器不合导致了内存布局的差别,也导致了网优势行的Linux注进与HOOK的编制行不通。网上的编制是经由过程ptrace注进后,搜刮dynamic的section中的PLTGOT区,往里头取link_map以遍历此过程所加载的模块来搜刮需要hook的函数地址。但Android上,dynamic的section的PLTGOT区前几项都是空的,没有link_map这个数据布局,只能经由过程阐发/proc//maps来遍历模块。

  4、浏览代码中的寄望事项

  在Android Injector Library浏览过程中有几个需要寄望的处所。

  1)操纵捕获SIGSEGV的无效内存援引或段弊端的异常旌旗灯号来履行ptrace。

  2)ptrace(PTRACE_PEEKTEXT, pid, addr, data)

  描述:从内存地址中读取一个字节,pid暗示被跟踪的子过程,内存地址由addr给出,data为用户变量地址用于返回读到的数据。

  在Linux(i386)顶用户代码段与用户数据段重合所以读代替码段和数据段数据措置是一样的。

  3)linker 首要用于实现共享库的加载与链接。它撑持利用法度对库函数的隐式和显式调用。查找/system/bin/linker中加载的libdl.so,加载位置固定,定义了dlopen,dlcose,dlsym,dlerror。

  4)有以下代码理解,即dynsym和symtab的关系

图片5

  5)在代码中有关于dynsym符号读取挨次的弊端。可是不影响利用。利用androidSDK下的东西readelf

图片6

  5、需要编写本身的open函数和close函数实现加解密把持

  该过程利用Android 平台下的openssl EVP编程,该过程的难度不大年夜。

  关头点一是在于利用密钥空间机关。保举密钥空间利用数组。利用char*字符串即便在字符串最后存在’’也会因为内存中的其他内容影响密钥初始化,呈现意想不到的标题问题。

  关头点二在close时,参数只有文件描述符,可以经由过程下述代码获得文件名。

  关头点三在于利用Openssl进行对称加解密时会填充到响应的块大年夜小,需要手动剥离这些填充。可以采取国际通用填充编制机关填充,或自立机关密文文件头记实填充大年夜小。

  http://en.wikipedia.org/wiki/Padding_(cryptography)

  6、记得在Makefile文件中加进

  LOCAL_LDLIBS+=-L$(SYSROOT)/usr/lib -llog

  LOCAL_LDLIBS+=-L$(SYSROOT)/usr/lib -lcrypto

  LOCAL_LDLIBS+=-L$(SYSROOT)/usr/lib -lssl

  7、需要再进行密钥治理模块的开辟,该过程不再描述。

  4、总结

  该种方案可以或许实此刻android平台上的透明加解密。不足的地方在于需要利用root权限,提早捕获用户法度启动,对其进行hook。在移动设备上效力是瓶颈,并且文件不宜过大年夜。对docxlspdfppttxt等文本、jpg等图片撑持较好,其他格局的文件笔者没有进行测试。

------分隔线----------------------------

推荐内容