玩转Hook——Android权限管理功能探讨(一)
随着Android设备上的隐私安全问题越来越被公众重视,恶意软件对用户隐私,尤其是对电话、短信等私密信息的威胁日益突出,各大主流安全软件均推出了自己的隐私行为监控功能,在root情况下能有效防止恶意软件对用户隐私的窃取,那么这背后的技术原理是什么?我带着疑问开始一步步探索,如果要拦截恶意软件对电话、短信等API的调用,在Java或者Dalvik层面是不好进行的,因为这些层面都没有提供Hook的手段,而在Native层面,我认为可行的方案是对电话、短信的运行库so进行Hook(比如系统运行库\system\lib\libreference-ril.so或\system\lib\libril.so),如果注入自己的so到上述进程后,并通过dlopen()和dlsym()获取原有API地址,替换原有API地址为自己so中的API地址就可以达到Hook的目的。
Hook的前提是进程注入,而Linux下最便捷的进程注入手段——ptrace,是大名鼎鼎的调试工具GDB的关键技术点;本文参考自Pradeep Padala于2002年的博文http://www.linuxjournal.com/article/6100(国内很多博客有这篇文章的译文,不过本着获取“一手”知识的想法,还是细读了原版英文,确实发现了一些翻译得不够到位的地方,在此还是推荐各位能读原文就不要读译文),由于02年时还是ia32(32位Intel Architecture)时代,时至今日,在我ia64也就是x64的机器已经无法运行了,所以自己动手实现了x64版本。代码主要功能是注入子进程的地址空间,Hook住子进程执行系统调用时的参数,并反转其参数,从而逆序输出ls命令的结果。
代码如下:
1 /* 2 ptrace3.c 3 author: pengyiming 4 description: 5 1, child process need be traced by father process 6 2, father process reserve the result of "ls" command which executed by child process 7 */ 8 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <sys/ptrace.h> 13 #include <sys/types.h> 14 #include <sys/wait.h> 15 #include <sys/reg.h> 16 #include <sys/user.h> 17 #include <sys/syscall.h> 18 #include <unistd.h> 19 20 #ifdef __x86_64__ 21 22 #define OFFSET_UNIT 8 23 24 #else 25 26 #define OFFSET_UNIT 4 27 28 #endif 29 30 // converter long to char[] 31 union 32 { 33 long rawData; 34 char strData[sizeof(long)]; 35 } converter; 36 37 void getData(pid_t child, unsigned long long dataAddr, unsigned long long dataLen, char * const p_data) 38 { 39 // PEEKDATA counter 40 int counter = 0; 41 // PEEKDATA max count 42 int maxCount = dataLen / sizeof(long); 43 if (dataLen % sizeof(long) != 0) 44 { 45 maxCount++; 46 } 47 // moving pointer 48 void * p_moving = p_data; 49 50 while (counter < maxCount) 51 { 52 memset(&converter, 0, sizeof(long)); 53 converter.rawData = ptrace(PTRACE_PEEKDATA, child, dataAddr + counter * sizeof(long), NULL); 54 if (converter.rawData < 0) 55 { 56 perror("ptrace peek data error : "); 57 } 58 59 memcpy(p_moving, converter.strData, sizeof(long)); 60 p_moving += sizeof(long); 61 counter++; 62 } 63 p_data[dataLen] = '\0'; 64 } 65 66 void setData(pid_t child, unsigned long long dataAddr, unsigned long long dataLen, char * const p_data) 67 { 68 // POKEDATA counter 69 int counter = 0; 70 // POKEDATA max count 71 int maxCount = dataLen / sizeof(long); 72 // data left length (prevent out of range in memory when written) 73 int dataLeftLen = dataLen % sizeof(long); 74 // moving pointer 75 void * p_moving = p_data; 76 77 // write part of data which align to sizeof(long) 78 int ret; 79 while (counter < maxCount) 80 { 81 memset(&converter, 0, sizeof(long)); 82 memcpy(converter.strData, p_moving, sizeof(long)); 83 ret = ptrace(PTRACE_POKEDATA, child, dataAddr + counter * sizeof(long), converter.rawData); 84 if (ret < 0) 85 { 86 perror("ptrace poke data error : "); 87 } 88 89 p_moving += sizeof(long); 90 counter++; 91 } 92 93 // write data left 94 if (dataLeftLen != 0) 95 { 96 memset(&converter, 0, sizeof(long)); 97 memcpy(converter.strData, p_moving, dataLeftLen); 98 ret = ptrace(PTRACE_POKEDATA, child, dataAddr + counter * sizeof(long), converter.rawData); 99 if (ret < 0) 100 { 101 perror("ptrace poke data error : "); 102 } 103 } 104 } 105 106 void reverseStr(char * p_str) 107 { 108 int strLen = strlen(p_str); 109 char * p_head = p_str; 110 char * p_tail = p_str + strLen - 1; 111 char tempCh; 112 113 // skip '\n' 114 if (*p_tail == '\n') 115 { 116 p_tail--; 117 } 118 119 //exchange char 120 while (p_head < p_tail) 121 { 122 tempCh = *p_head; 123 *p_head = *p_tail; 124 *p_tail = tempCh; 125 126 p_head++; 127 p_tail--; 128 } 129 } 130 131 void debugRegs(struct user_regs_struct * p_regs ) 132 { 133 printf("syscall param DS = %llu\n", p_regs->ds); 134 printf("syscall param RSI = %llu\n", p_regs->rsi); 135 printf("syscall param ES = %llu\n", p_regs->es); 136 printf("syscall param RDI = %llu\n", p_regs->rdi); 137 138 printf("syscall return RAX = %llu\n", p_regs->rax); 139 printf("syscall param RBX = %llu\n", p_regs->rbx); 140 printf("syscall param RCX = %llu\n", p_regs->rcx); 141 printf("syscall param RDX = %llu\n", p_regs->rdx); 142 } 143 144 int main() 145 { 146 pid_t child = fork(); 147 if(child == 0) 148 { 149 ptrace(PTRACE_TRACEME, 0, NULL, NULL); 150 151 // make a syscall(SYS_write) 152 execl("/bin/ls", "ls", NULL); 153 } 154 else 155 { 156 int status; 157 // SYS_write will be called twice, one is entry, another is exit, so we mark it 158 unsigned int calledCount = 0; 159 160 while (1) 161 { 162 wait(&status); 163 if (WIFEXITED(status)) 164 { 165 break; 166 } 167 168 // PEEK regs to find the syscall(SYS_execve) 169 struct user_regs_struct regs; 170 ptrace(PTRACE_GETREGS, child, NULL, ®s); 171 172 // catch it! 173 if (regs.orig_rax == SYS_write) 174 { 175 if (calledCount == 0) 176 { 177 calledCount = 1; 178 179 // debugRegs(®s); 180 181 char * p_dataStr = (char *) malloc((regs.rdx + 1) * sizeof(char)); 182 if (p_dataStr == NULL) 183 { 184 return; 185 } 186 187 getData(child, regs.ds * OFFSET_UNIT + regs.rsi, regs.rdx, p_dataStr); 188 reverseStr(p_dataStr); 189 setData(child, regs.ds * OFFSET_UNIT + regs.rsi, regs.rdx, p_dataStr); 190 } 191 else if (calledCount == 1) 192 { 193 // debugRegs(®s); 194 } 195 } 196 197 ptrace(PTRACE_SYSCALL, child, NULL, NULL); 198 } 199 } 200 201 return 0; 202 }
代码执行结果:
可以看到工程目录下的文件名均被反转输出,已达到想要的效果,那么接下来解释代码中的几个关键点:
1,SYSCALL与orig_rax寄存器
不论是ia32还是ia64,orig_rax寄存器都存放着每一次系统调用的ID,为了方便开发和调试,我们可以在/usr/include/x86_64-linux-gnu/sys/syscall.h中找到系统调用的定义,比如#define SYS_write __NR_write,但是我们无法得知__NR_write具体代表的ID,进一步搜索,可以在/usr/include/x86_64-linux-gnu/asm/unistd_64.h中找到ia64下对__NR_write的定义,#define __NR_write 1,这样一来我们打印出orig_rax寄存器中的值就可以判断此时子进程正在进行何种操作了。
2,PTRACE_PEEKDATA与PTRACE_PEEKTEXT参数的选取
Linux进程的地址空间不存在独立的数据段和代码段(或叫正文段),二者位于同一空间,所以上述两个参数并无实际意义上的区别,不过为了标识我们是在读取数据段中的数据,还是使用PTRACE_PEEKDATA比较好,同理对应于PTRACE_POKEDATA和PTRACE_POKETEXT。
3,联合体converter
由于执行PTRACE_PEEKDATA操作时,返回值的二进制代表内存中的实际数据,我们可以利用“联合体中的变量有相同的初始地址”这一特性来帮助我们完成从二进制到字符串的转换。(这是一个做过嵌入式开发的人基本都知道的小技巧,考虑到做Android开发对这段代码可能会有疑惑,容我啰嗦两句)
4,数据段寻址
这是在实现x64版本时遇到的最大的困难,在getData()与setData()函数中,第二个参数表示数据在数据段中的地址,由于和ia32时寻址方式不一致,苦苦搜索几天,发现国内很多博客上的说法并不一致,最终在Intel官网上下载了Intel处理器开发手册《64-ia-32-architectures-software-developer-vol-1-manual.pdf》方才解答我的问题,寻址方式涉及两个寄存器,DS和RSI,参考手册的说法,DS表示数据段的selector,其实这个selector就是index索引的意思,由于x64下字长64bit,即8个字节,索引乘以8即得数据段在内存中的基址,RSI表示数据段内偏移地址,与基址相加即可得出数据的绝对地址,使用此地址直接访问内存就可以取出数据。
转载于:https://www.cnblogs.com/zealotrouge/p/3544147.html
玩转Hook——Android权限管理功能探讨(一)相关推荐
- mySagasoft MIS与WebMIS架构权限管理功能
mySagasoft MIS与WebMIS架构权限管理功能 作者:sagahu@163.com,2013-03-02,太原. 关键字:RBAC, 权限管理, 角色权限, 资源权限 一.引言 前几天把自 ...
- android 一个字符串分两行显示_重新梳理Android权限管理
Android Developer指南中,对Android安全体系结构的核心有这么一个说法:默认情况下,任何应用程序都无权执行任何会对其他应用程序.操作系统或者用户产生负面影响的操作.这句话其实就很好 ...
- android 跳转权限管理的代码,Android权限管理
Android权限管理 说明 在targetSdkVersion的值为23或者更高,就要进行权限管理,否则如果运行在Android6.0或以上的设备会没有相应权限而导致崩溃 请求权限后,在onRequ ...
- Android权限管理
Android权限管理 说明 在targetSdkVersion的值为23或者更高,就要进行权限管理,否则如果运行在Android6.0或以上的设备会没有相应权限而导致崩溃 请求权限后,在onRequ ...
- Android权限管理之Permission权限机制及使用
前言: 最近突然喜欢上一句诗:"宠辱不惊,看庭前花开花落:去留无意,望天空云卷云舒." 哈哈~,这个和今天的主题无关,最近只要不学习总觉得生活中少了点什么,所以想着围绕着最近面试过 ...
- koa mysql 按钮级权限_Vue 指令实现按钮级别权限管理功能
在项目中经常有需求要根据用户的权限对界面上的元素进行控制,这里介绍了一直简单的实现,仅供参考. 当前用户的权限列表储存在 store 里,也可以是其他地方. 指令 // src/directives/ ...
- android 权限管理之判断禁止后不再提示
android 权限管理之判断禁止后不再提示 我看到了许多博客中写到了单独去判断shouldShowRequestPermissionRationale()方法的值去判断为是否为不再提示,结果遇到了小 ...
- 开源算力引擎 BridgX 发布 0.6.0 版本:新增三种权限管理功能
开源算力引擎 BridgX 推出 V0.6.0 版本,新增权限管理功能,分为 root.管理员和普通用户三种角色,可以帮助用户灵活地分配资源权限,保护云上资产.欢迎前往体验. 开源算力引擎 Bridg ...
- Spring Security认证授权练手小项目 腾讯视频VIP权限管理功能
腾讯视频VIP权限管理 1.项目功能视频演示 2.需求与设计 1.需求 2.功能概要 3.接口设计 3.项目源码结构 4.项目源码下载 5.项目部署 1.部署架构 2.数据库环境准备 3.redis环 ...
最新文章
- 1052 Linked List Sorting
- 246.三元图的应用与绘图实战
- 大道至简第一章观后感(伪代码)
- [LeetCode] Rotate List
- mfc中加logo以及背景图
- Python处理千万级数据
- 【原创】“三次握手,四次挥手”你真的懂吗?
- 微信AI从识物到通用图像搜索的探索揭秘
- 简历中该怎么写自己了解html,我用HTML写简历
- Linux系统编程----8(竞态条件,时序竞态,pause函数,如何解决时序竞态)
- Red Hat Linux 253 实验部分
- MySQL+create+base,MySQL中CREATE DATABASE和CREATE SCHEMA的区别
- fullpage常用配置
- 苏州银行对公定存通项目
- windows本地运行sentinel
- 安装红帽linux显示半屏,在linux下模拟win+arrow来左右半屏当前窗口
- 如何成为一名汽车软件工程师?
- Linux——共享文件夹
- 图解机器学习算法(2) | 模型评估方法与准则(机器学习通关指南·完结)
- 39.JavaScript中Promise的基本概念、使用方法,回调地狱规避、链式编程
热门文章
- java 云服务器 linux,云服务器Linux部署JavaWeb项目
- html toggle自动隐藏,Javascript / HTML – 切换可见性(当另一个div元素呈现可见时自动导致一个div元素隐藏)...
- Android6.0动态权限
- caffe︱cifar-10数据集quick模型的官方案例
- centos7-修改主机名
- spring mvc 中自定义404页面在IE中无法显示favicon.ico问题的解决方法。
- OCM_第十一天课程:Section5 —》数据仓库
- sourceTree 的使用
- Linux守护进程的启动方法
- php+mysql将大数据sql文件导入数据库