随着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, &regs);
171
172       // catch it!
173       if (regs.orig_rax == SYS_write)
174       {
175           if (calledCount == 0)
176           {
177             calledCount = 1;
178
179             // debugRegs(&regs);
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(&regs);
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权限管理功能探讨(一)相关推荐

  1. mySagasoft MIS与WebMIS架构权限管理功能

    mySagasoft MIS与WebMIS架构权限管理功能 作者:sagahu@163.com,2013-03-02,太原. 关键字:RBAC, 权限管理, 角色权限, 资源权限 一.引言 前几天把自 ...

  2. android 一个字符串分两行显示_重新梳理Android权限管理

    Android Developer指南中,对Android安全体系结构的核心有这么一个说法:默认情况下,任何应用程序都无权执行任何会对其他应用程序.操作系统或者用户产生负面影响的操作.这句话其实就很好 ...

  3. android 跳转权限管理的代码,Android权限管理

    Android权限管理 说明 在targetSdkVersion的值为23或者更高,就要进行权限管理,否则如果运行在Android6.0或以上的设备会没有相应权限而导致崩溃 请求权限后,在onRequ ...

  4. Android权限管理

    Android权限管理 说明 在targetSdkVersion的值为23或者更高,就要进行权限管理,否则如果运行在Android6.0或以上的设备会没有相应权限而导致崩溃 请求权限后,在onRequ ...

  5. Android权限管理之Permission权限机制及使用

    前言: 最近突然喜欢上一句诗:"宠辱不惊,看庭前花开花落:去留无意,望天空云卷云舒." 哈哈~,这个和今天的主题无关,最近只要不学习总觉得生活中少了点什么,所以想着围绕着最近面试过 ...

  6. koa mysql 按钮级权限_Vue 指令实现按钮级别权限管理功能

    在项目中经常有需求要根据用户的权限对界面上的元素进行控制,这里介绍了一直简单的实现,仅供参考. 当前用户的权限列表储存在 store 里,也可以是其他地方. 指令 // src/directives/ ...

  7. android 权限管理之判断禁止后不再提示

    android 权限管理之判断禁止后不再提示 我看到了许多博客中写到了单独去判断shouldShowRequestPermissionRationale()方法的值去判断为是否为不再提示,结果遇到了小 ...

  8. 开源算力引擎 BridgX 发布 0.6.0 版本:新增三种权限管理功能

    开源算力引擎 BridgX 推出 V0.6.0 版本,新增权限管理功能,分为 root.管理员和普通用户三种角色,可以帮助用户灵活地分配资源权限,保护云上资产.欢迎前往体验. 开源算力引擎 Bridg ...

  9. Spring Security认证授权练手小项目 腾讯视频VIP权限管理功能

    腾讯视频VIP权限管理 1.项目功能视频演示 2.需求与设计 1.需求 2.功能概要 3.接口设计 3.项目源码结构 4.项目源码下载 5.项目部署 1.部署架构 2.数据库环境准备 3.redis环 ...

最新文章

  1. 1052 Linked List Sorting
  2. 246.三元图的应用与绘图实战
  3. 大道至简第一章观后感(伪代码)
  4. [LeetCode] Rotate List
  5. mfc中加logo以及背景图
  6. Python处理千万级数据
  7. 【原创】“三次握手,四次挥手”你真的懂吗?
  8. 微信AI从识物到通用图像搜索的探索揭秘
  9. 简历中该怎么写自己了解html,我用HTML写简历
  10. Linux系统编程----8(竞态条件,时序竞态,pause函数,如何解决时序竞态)
  11. Red Hat Linux 253 实验部分
  12. MySQL+create+base,MySQL中CREATE DATABASE和CREATE SCHEMA的区别
  13. fullpage常用配置
  14. 苏州银行对公定存通项目
  15. windows本地运行sentinel
  16. 安装红帽linux显示半屏,在linux下模拟win+arrow来左右半屏当前窗口
  17. 如何成为一名汽车软件工程师?
  18. Linux——共享文件夹
  19. 图解机器学习算法(2) | 模型评估方法与准则(机器学习通关指南·完结)
  20. 39.JavaScript中Promise的基本概念、使用方法,回调地狱规避、链式编程

热门文章

  1. java 云服务器 linux,云服务器Linux部署JavaWeb项目
  2. html toggle自动隐藏,Javascript / HTML – 切换可见性(当另一个div元素呈现可见时自动导致一个div元素隐藏)...
  3. Android6.0动态权限
  4. caffe︱cifar-10数据集quick模型的官方案例
  5. centos7-修改主机名
  6. spring mvc 中自定义404页面在IE中无法显示favicon.ico问题的解决方法。
  7. OCM_第十一天课程:Section5 —》数据仓库
  8. sourceTree 的使用
  9. Linux守护进程的启动方法
  10. php+mysql将大数据sql文件导入数据库