- 浏览: 5135 次
- 性别:
- 来自: 沈阳
最新评论
利用Ptrace在Android平台实现应用程序控制
但凡做过安全软件的人都知道,API Hook和App Control是经常要实现的功能。
为了实现这两个功能,最常用的方法就是写driver,在kernel中拦截检查相应的调用。这种做法的好处是大小通吃,不用关心系统里面到底有多少进程,反正你要做的操作最终总要过我这一关。而缺点就是在kernel中拦截往往得不到我想要的一些参数而无法做出正确的判断。举个例子,手机平台中很多应用都会发短信,我想组织某些应用发短信而允许另一些应用发短信。而发短信的操作并不是由每个应用直接调用的,它们把发送请求发给一个叫SmsService的服务进程,由这个服务进程再调用系统API来发短信。当我们在Kernel里面拦截到这个API的时候,发现调用者都是SmsService,无法区分原始请求者,因而无法做到有目的的拦截。
为了解决上面提到的问题,很多厂商开始想办法Hook SmsService,在SmsService里面可以区分请求的应用和请求的内容,进而做到有区分的拦截。那么如何Hook SmsService 呢?在Linux/Unix/Mac/iOS系统里面最简单的办法可能就是Ptrace了。所以说利用Ptrace可以很容易的做到精准打击,可以具体到Hook每个进程里面的每个点,这是它的优点。那么再说一下它的缺点,如果你想Hook多个进程,你就要给每个进程准备不同的代码,除非你可以找到SmsService这样的“关口”,即便找到了关口,还要提放有没有办法绕过它。
综上所述,Ptrace是一个用来实现API Hook和App Control 的好工具。很多情况下它的缺点可以被忽视,尤其在Android这种很容易找到"关口"的平台上。因此很多Android上的安全软件都是利用Ptrace来实现API Hook和App Control的。
前面扯的有点多,先来说一下这次的目标:
平台:Android 2.3
实现:利用Ptrace剥夺某些应用请求service的功能
1. 编写测试程序SmsSender1
SmsSender1非常简单,它有一个Activity,在onCreate的时候发送一条SMS. 我们的目标就是拦截这个应用请求系统Service的能力而不影响其他应用。
2. 研究servicemanager
servicemanager是我们用Ptrace要修改的目标。为什么选servicemanager? 根据Android的架构,系统的各种service (比如phone, sms, camera)是手机核心功能的“关口”,而servicemanager就是这些service的总"关口"。每个service都要到servicemanager注册,而每个app要想获取服务,需要先到servicemanager来查询各个服务的ID,然后才能根据ID和相应的service利用binder进行通信。所以说把住了servicemanager,就把住了所有关键的服务。
那么从何下手呢?
俗话说源码之前,了无秘密。既然有Android源码,就看源码来加速我们的研究。看了源码以后发现servicemanager里面有一个loop,用来处理各种请求,算是个下手的好地方:
Hook这个函数,通过参数txn和msg可以获知是谁在发起请求,请求什么东西。我们的计划是通过txn->sender_euid来判断是哪个app在请求,然后屏蔽前面写的SmsSender1的所有请求,让它什么都干不了。
具体如何实现?我们现在有两个选择:
选择1, 写一个trace程序,利用Ptrace attach到servicemanager进程,在函数svcmgr_handler中添加断点。中断之后trace根据txn->sender_euid的值来修改相应的寄存器或者内存值,达到改变程序流程的目的。这种做法需要trace一直运行,处于调试servicemanager的状态,每次svcmgr_handler都需要从trace过一次。如果trace挂了会导致不可预料的结果。
选项2,写一个trace程序,只运行一次,利用Ptrace attach到servicemanager进程,在text段修改svcmgr_handler的逻辑并插入我们的代码,然后dettach. 这样的话trace不必长期运行,也不用每次调用都过trace,稳定性和效率都大大提高。
就技术而言,选项2需要考虑更多的问题,也更复杂。但我还是喜欢选项2,呵呵,后面就按照选项2来实现。
3. 编写injection的汇编代码
反编译servicemanager,定位到svcmgr_handler函数:
其中0x8950,0x8952,0x8954,0x8956四条指令对应于源码中这两行:
我们就从0x8950开始修改,将LDR R3,[R0]; LDR R2,[R4];替换成我们的跳转指令BL,跳转的目的地是我们即将插入的逻辑代码,就是判断txn->sender_euid并决定是否屏蔽请求的代码。
我们先将SmsSender1安装到Android上,并查看它的uid. 我们知道在Android系统中每个app都被分配了一个用户,利用用户来进行权限管理,而uid就是用户id,用户分配表可以在/data/system/packages.list里面找到:
可见我们要屏蔽的uid是10038,所以我们的判断逻辑代码编写如下:
逻辑很简单,line3是获取txn->sender_euid,并和10013比较,如果相等,则将r2和r3的值分别设为1和0,目的是让它们不等,因为servicemanager后续的逻辑如果r2!=r3就会return -1;
另外别忘了我们覆盖了servicemanager两条指令LDR R3,[R0]; LDR R2,[R4];,如果uid!=10013,我们需要让程序按照原来的逻辑执行下去,所以line8-9是补充执行被我们覆盖的两条指令。
好了,万事俱备只欠东风,接下来我们可以开始编写trace程序了。
3. 编写trace程序
我们最终完成的trace程序如下:
其中replace_addr = 0x8950;是我们修改svcmgr_handler函数进行拦截的地址。freeaddr = 0xA294;是我们找到的可以插入我们的逻辑代码并且不会影响原有程序的地址。
我觉得程序已经写的很明白了,不需要再进一步解释了,后面开始实测。
4. 测试
先运行模拟器,上传trace程序。运行SmsSender1和系统自带的SMS程序,均可正常运行发送短信。
然后ps查看servicemanager的pid,发现是28. 运行trace 28, 修改内存,inject代码,发现我们的代码已成功写入:
然后运行系统自带的SMS,可正常运行发送短信。运行SmsSender1,无法正常运行。
事实证明App Control获得成功,哇咔咔~~
但凡做过安全软件的人都知道,API Hook和App Control是经常要实现的功能。
为了实现这两个功能,最常用的方法就是写driver,在kernel中拦截检查相应的调用。这种做法的好处是大小通吃,不用关心系统里面到底有多少进程,反正你要做的操作最终总要过我这一关。而缺点就是在kernel中拦截往往得不到我想要的一些参数而无法做出正确的判断。举个例子,手机平台中很多应用都会发短信,我想组织某些应用发短信而允许另一些应用发短信。而发短信的操作并不是由每个应用直接调用的,它们把发送请求发给一个叫SmsService的服务进程,由这个服务进程再调用系统API来发短信。当我们在Kernel里面拦截到这个API的时候,发现调用者都是SmsService,无法区分原始请求者,因而无法做到有目的的拦截。
为了解决上面提到的问题,很多厂商开始想办法Hook SmsService,在SmsService里面可以区分请求的应用和请求的内容,进而做到有区分的拦截。那么如何Hook SmsService 呢?在Linux/Unix/Mac/iOS系统里面最简单的办法可能就是Ptrace了。所以说利用Ptrace可以很容易的做到精准打击,可以具体到Hook每个进程里面的每个点,这是它的优点。那么再说一下它的缺点,如果你想Hook多个进程,你就要给每个进程准备不同的代码,除非你可以找到SmsService这样的“关口”,即便找到了关口,还要提放有没有办法绕过它。
综上所述,Ptrace是一个用来实现API Hook和App Control 的好工具。很多情况下它的缺点可以被忽视,尤其在Android这种很容易找到"关口"的平台上。因此很多Android上的安全软件都是利用Ptrace来实现API Hook和App Control的。
前面扯的有点多,先来说一下这次的目标:
平台:Android 2.3
实现:利用Ptrace剥夺某些应用请求service的功能
1. 编写测试程序SmsSender1
SmsSender1非常简单,它有一个Activity,在onCreate的时候发送一条SMS. 我们的目标就是拦截这个应用请求系统Service的能力而不影响其他应用。
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); SmsManager smsM = SmsManager.getDefault(); if(smsM != null) { smsM.sendTextMessage("123456", null, "abc", null, null); Toast.makeText(this, "send sms", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "sm is null", Toast.LENGTH_SHORT).show(); } }
2. 研究servicemanager
servicemanager是我们用Ptrace要修改的目标。为什么选servicemanager? 根据Android的架构,系统的各种service (比如phone, sms, camera)是手机核心功能的“关口”,而servicemanager就是这些service的总"关口"。每个service都要到servicemanager注册,而每个app要想获取服务,需要先到servicemanager来查询各个服务的ID,然后才能根据ID和相应的service利用binder进行通信。所以说把住了servicemanager,就把住了所有关键的服务。
那么从何下手呢?
俗话说源码之前,了无秘密。既然有Android源码,就看源码来加速我们的研究。看了源码以后发现servicemanager里面有一个loop,用来处理各种请求,算是个下手的好地方:
int svcmgr_handler(struct binder_state *bs, struct binder_txn *txn, struct binder_io *msg, struct binder_io *reply) { struct svcinfo *si; uint16_t *s; unsigned len; void *ptr; uint32_t strict_policy; // LOGI("target=%p code=%d pid=%d uid=%d\n", // txn->target, txn->code, txn->sender_pid, txn->sender_euid); if (txn->target != svcmgr_handle) return -1; // Equivalent to Parcel::enforceInterface(), reading the RPC // header with the strict mode policy mask and the interface name. // Note that we ignore the strict_policy and don't propagate it // further (since we do no outbound RPCs anyway). strict_policy = bio_get_uint32(msg); s = bio_get_string16(msg, &len); if ((len != (sizeof(svcmgr_id) / 2)) || memcmp(svcmgr_id, s, sizeof(svcmgr_id))) { fprintf(stderr,"invalid id %s\n", str8(s)); return -1; } switch(txn->code) { case SVC_MGR_GET_SERVICE: case SVC_MGR_CHECK_SERVICE: s = bio_get_string16(msg, &len); ptr = do_find_service(bs, s, len); if (!ptr) break; bio_put_ref(reply, ptr); return 0; case SVC_MGR_ADD_SERVICE: s = bio_get_string16(msg, &len); ptr = bio_get_ref(msg); if (do_add_service(bs, s, len, ptr, txn->sender_euid)) return -1; break; case SVC_MGR_LIST_SERVICES: { unsigned n = bio_get_uint32(msg); si = svclist; while ((n-- > 0) && si) si = si->next; if (si) { bio_put_string16(reply, si->name); return 0; } return -1; } default: LOGE("unknown code %d\n", txn->code); return -1; } bio_put_uint32(reply, 0); return 0; }
Hook这个函数,通过参数txn和msg可以获知是谁在发起请求,请求什么东西。我们的计划是通过txn->sender_euid来判断是哪个app在请求,然后屏蔽前面写的SmsSender1的所有请求,让它什么都干不了。
具体如何实现?我们现在有两个选择:
选择1, 写一个trace程序,利用Ptrace attach到servicemanager进程,在函数svcmgr_handler中添加断点。中断之后trace根据txn->sender_euid的值来修改相应的寄存器或者内存值,达到改变程序流程的目的。这种做法需要trace一直运行,处于调试servicemanager的状态,每次svcmgr_handler都需要从trace过一次。如果trace挂了会导致不可预料的结果。
选项2,写一个trace程序,只运行一次,利用Ptrace attach到servicemanager进程,在text段修改svcmgr_handler的逻辑并插入我们的代码,然后dettach. 这样的话trace不必长期运行,也不用每次调用都过trace,稳定性和效率都大大提高。
就技术而言,选项2需要考虑更多的问题,也更复杂。但我还是喜欢选项2,呵呵,后面就按照选项2来实现。
3. 编写injection的汇编代码
反编译servicemanager,定位到svcmgr_handler函数:
其中0x8950,0x8952,0x8954,0x8956四条指令对应于源码中这两行:
if (txn->target != svcmgr_handle) return -1;
我们就从0x8950开始修改,将LDR R3,[R0]; LDR R2,[R4];替换成我们的跳转指令BL,跳转的目的地是我们即将插入的逻辑代码,就是判断txn->sender_euid并决定是否屏蔽请求的代码。
我们先将SmsSender1安装到Android上,并查看它的uid. 我们知道在Android系统中每个app都被分配了一个用户,利用用户来进行权限管理,而uid就是用户id,用户分配表可以在/data/system/packages.list里面找到:
可见我们要屏蔽的uid是10038,所以我们的判断逻辑代码编写如下:
push {r1,lr} push {r0-r7} LDR R7, [R4,#0x14] ldr r3,=10013 CMP R7, R3 pop {r0-r7} BEQ loc_ret_1 LDR R3, [R0] LDR R2, [R4] pop {r1,pc} loc_ret_1: mov r3, #0 mov r2, #1 pop {r1,pc}
逻辑很简单,line3是获取txn->sender_euid,并和10013比较,如果相等,则将r2和r3的值分别设为1和0,目的是让它们不等,因为servicemanager后续的逻辑如果r2!=r3就会return -1;
另外别忘了我们覆盖了servicemanager两条指令LDR R3,[R0]; LDR R2,[R4];,如果uid!=10013,我们需要让程序按照原来的逻辑执行下去,所以line8-9是补充执行被我们覆盖的两条指令。
好了,万事俱备只欠东风,接下来我们可以开始编写trace程序了。
3. 编写trace程序
我们最终完成的trace程序如下:
#include <sys/ptrace.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <stdlib.h> #include <sys/syscall.h> int long_size = sizeof(long); void append_asm(char* asm_bin, int* len, unsigned long aasm, int alen) { int i; char* aasmp = &aasm; for(i=0; i<alen; i++) { *(asm_bin+(*len)) = *(aasmp+i); (*len)++; } } void getdata(pid_t pid, long addr, char *str, int len) { char *laddr; int i, j; union u { long val; char chars[long_size]; }data; i = 0; j = len / long_size; laddr = str; while(i < j) { data.val = ptrace(PTRACE_PEEKDATA, pid, addr + i * 4, NULL); memcpy(laddr, data.chars, long_size); ++i; laddr += long_size; } j = len % long_size; if(j != 0) { data.val = ptrace(PTRACE_PEEKDATA, pid, addr + i * 4, NULL); memcpy(laddr, data.chars, j); } str[len] = '\0'; } void putdata(pid_t pid, long addr, char *str, int len) { char *laddr; int i, j; union u { long val; char chars[long_size]; }data; i = 0; j = len / long_size; laddr = str; while(i < j) { memcpy(data.chars, laddr, long_size); ptrace(PTRACE_POKEDATA, pid, addr + i * 4, data.val); ++i; laddr += long_size; } j = len % long_size; if(j != 0) { memcpy(data.chars, laddr, j); ptrace(PTRACE_POKEDATA, pid, addr + i * 4, data.val); } } void build_jmp_asm(char* asm_bin, int* len, unsigned long freeaddr) { append_asm(asm_bin, len, 0xFCA0F001, 4); //b from 0x8950 to 0xa294 } void build_fun_asm(char* asm_bin, int* len) { unsigned long block_uid = 10038; append_asm(asm_bin, len, 0xB502, 2); //push {r1,lr} //fun asm append_asm(asm_bin, len, 0xB4FF, 2); // push {r0-r7} append_asm(asm_bin, len, 0x6967, 2); // LDR R7, [R4,#0x14] append_asm(asm_bin, len, 0x4B05, 2); // ldr r3,=block_uid append_asm(asm_bin, len, 0x429F, 2); // CMP R7, R3 append_asm(asm_bin, len, 0xBCFF, 2); // pop {r0-r7} append_asm(asm_bin, len, 0xD002, 2); // BEQ return -1 //return append_asm(asm_bin, len, 0x6803, 2); //LDR R3, [R0] append_asm(asm_bin, len, 0x6822, 2); //LDR R2, [R4] append_asm(asm_bin, len, 0xBD02, 2); //pop {r1,pc} //return -1 append_asm(asm_bin, len, 0x2300, 2); //mov r3, #0 append_asm(asm_bin, len, 0x2201, 2); //mov r2, #1 append_asm(asm_bin, len, 0xBD02, 2); //pop {r1,pc} append_asm(asm_bin, len, 0x1C00, 2); //nop //write return address append_asm(asm_bin, len, block_uid, 4); } void print_asm(char* asm_bin, int len) { int i; for(i=0; i<len; i++) { printf("%x ", *(asm_bin+i)); } printf("\n\n"); } void tracePro(int pid) { unsigned long replace_addr; unsigned long freeaddr; char asm_jump[32]; int asm_jump_len=0; char asm_fun[1024]; int asm_fun_len=0; char temp[1024]; replace_addr = 0x8950; freeaddr = 0xA294; build_jmp_asm(asm_jump, &asm_jump_len, freeaddr); build_fun_asm(asm_fun, &asm_fun_len); putdata(pid, replace_addr, asm_jump, asm_jump_len); putdata(pid, freeaddr, asm_fun, asm_fun_len); getdata(pid, replace_addr, temp, 64); print_asm(temp, 64); getdata(pid, freeaddr, temp, 64); print_asm(temp, 64); } int main(int argc, char *argv[]) { if(argc != 2) { printf("Usage: %s <pid to be traced>\n", argv[0], argv[1]); return 1; } pid_t traced_process; int status; traced_process = atoi(argv[1]); if(0 != ptrace(PTRACE_ATTACH, traced_process, NULL, NULL)) { printf("Trace process failed:%d.\n", errno); return 1; } tracePro(traced_process); ptrace(PTRACE_DETACH, traced_process, NULL, NULL); return 0; }
其中replace_addr = 0x8950;是我们修改svcmgr_handler函数进行拦截的地址。freeaddr = 0xA294;是我们找到的可以插入我们的逻辑代码并且不会影响原有程序的地址。
我觉得程序已经写的很明白了,不需要再进一步解释了,后面开始实测。
4. 测试
先运行模拟器,上传trace程序。运行SmsSender1和系统自带的SMS程序,均可正常运行发送短信。
然后ps查看servicemanager的pid,发现是28. 运行trace 28, 修改内存,inject代码,发现我们的代码已成功写入:
然后运行系统自带的SMS,可正常运行发送短信。运行SmsSender1,无法正常运行。
事实证明App Control获得成功,哇咔咔~~
相关推荐
所以在这个项目中,我们设计了一个应用程序跟踪器,它基本上适用于 android 平台,它具有跟踪应用程序与内核交互的能力。我们的跟踪器列出了所有交互,这些交互基本上是系统调用,并为任何调试器提供了足够的信息。...
安卓程序ptrace注入例子,送给想研究手游注入的同学 使用JNI注入
==================== PTrace for Android介绍Ptrace 系统调用提供了一个框架,进程(跟踪... 该应用程序可以生成任何其他 android 应用程序并附加到它,从而使跟踪器应用程序能够跟踪和逐步调试被跟踪应用程序。特征T
Android应用程序是运行在一个沙箱中。这个沙箱是基于Linux内核提供的用户ID(UID)和用户组ID(GID)来实现的。Android应用程序在安装的过程中,安装服务PackageManagerService会为它们分配一个唯一的UID和GID,以及...
因此,沙盒应用程序在正常模式下无法访问属于同一应用程序的文件。 Android API沙箱,正在调查中。 注意:Appfence在系统调用级别(基于ptrace)上运行。入门Eclipse + CDT + Android NDK + Android SDK 该项目主要...
ptracer -- 一个用于Python程序的基于ptrace跟踪的库
ptrace注入实例代码,
对Linux下的系统调用Ptrace()所拥有的进程跟踪和控制调试功能进行了分析;结合内核漏洞的具体实例研究其对系统可能造成的安全...最后就病毒技术中的一项关键技术——隐藏,讨论了Ptrace()在Linux病毒隐藏技术中的应用。
GDB & ptrace() System Call
Linux下Ptrace()调用的安全分析.pdf
时,一般说明程序内部调用了ptrace 。 2.2、为验证是否调用了ptrace 可以 debugserver -x backboard *:1234 /BinaryPath(这里是完整路径),然后下符号断点 b ptrace,c 之后看ptrace第一行代码的位置,然后 p $lr ...
ptrace 越狱调试防护
拆装机在capstone和ptrace的帮助下进行简单的实验性反汇编
ptrace注入与zygote区别和联系.V2EEptrace注入与zygote区别和联系.V2EE
Ptrace 提供了一种父进程可以控制子进程运行,并可以检查和改变它的核心image。它主要用于实现断点调试。一个被跟踪的进程运行中,直到发生一个信号。则进程被中止,并且通知其父进程。在进程中止的状态下,进程的...
去除Honor9i(LLD-AL20) Android8.0.0 kernel4.4.23+的ptrace限制,可以正常使用Frida,具体制作教程看https://blog.csdn.net/qq_26914291/article/details/116978870?spm=1001.2014.3001.5502
android下使用ptrace实现hook技术的代码,包括shellcode 编译参考Android.mk标准组件格式即可
ptracer-一个用于基于ptrace的Python程序跟踪的库 Ptracer是一个库,可在Python程序中提供按需系统调用跟踪。 基本用法 import traceback import ptracer def callback ( syscall ): print ( '{}({}) -> {}' . ...