利用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的能力而不影响其他应用。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

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"nullnull);

                                                     

            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,用来处理各种请求,算是个下手的好地方:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

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四条指令对应于源码中这两行:


1

2

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,所以我们的判断逻辑代码编写如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

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程序如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

#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获得成功,哇咔咔~~

时间: 2024-10-02 05:57:40

利用Ptrace在Android平台实现应用程序控制的相关文章

利用PAWserver搭建Android平台PHP环境

PAWserver可以实现我们在Android上运行PHP的愿望@Appinn 这是一个技术性的文章,有些折腾,不涉及代码(用模拟器的话需要几行),但不难,主要是探讨思路. 没有特别实际的性的用途,非折腾帝可以忽略. 主要软件:PAWserver, 设备要求:安装PHP要求ARM架构. 所以老鼠的中兴U880做不到,使用模拟器操作,ARM架构手机用户可以直接看第二节. 第一节:Android模拟器端口映射 Android系统默认只能通过IP(10.0.2.2)单向访问PC电脑,而PC电脑不能通过

利用Android平台录音,得到的音频严重失真

问题描述 利用Android平台录音,得到的音频严重失真 最近在做Android开发录音方面的开发,利用mediarecord录取声音,得到的音频严重失真,几乎听不出声音的内容,但从声音可以确定的是,这个声音确实是刚才录进去的.开始以为是采样率方面出错,但从打印信息看到,audio系统获取的采样率是8000,通道数是1,调用编码器时识别的采样率也是8000,通道数也是1.现在很迷糊,不知道什么原因导致系统录音失真,请大神帮我定位一下问题. 解决方案 你试试我这段代码看能不能录,public st

Android平台下利用zxing实现二维码开发

现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描库主要有zxing和zbar,zbar在iPos平台上应用比较成熟,而在Android平台上主流还是用zxing库,因此这里主要讲述如何利用zxing进行二维码开发. 1.如何将zxing的Android源码导入工程. 在导入zxing的android源码之前,先去官方下载zxing的源码:http://code.google.com/p/zxing/downloads/list. 我这里下载的是

Android平台利用ZXING生成二维码图片

zxing是google的一个开源二维码项目,目前基本上和二维码打交道的东西,都会用到它. 最近项目中用到了android手机需要根据提供的字符串生成二维码图片,之前用zxing做过二维码解码,编码还没做过,看了一些demo都是用到了zxing的j2se包的内容,这个在android或者其他平台上显然无法实现,所以我们要利用zxing生成二维矩阵,然后根据android平台的提供的api来生成图片. /** * 用字符串生成二维码 * @param str * @author zhouzhe@l

微软证实Office软件将登陆iOS和Android平台

对于既想利用Office帮助Windows Phone"鹤立鸡群",又想确保Office"无处不在"的微软而言,这是一个棘手的平衡问题. 北京时间10月11日消息,据国外媒体报道,尽管部分否认了捷克分部发表的一份声明,但微软证实Office软件将以某种方式登陆iOS和Android平台. 微软捷克分部在声明中称,Office将于明年初登陆iOS和Android平台. 微软的表态则不太明确,"我们之前就曾公布过,Office Mobile将支持Windows

Freeline - Android平台上的秒级编译方案

Freeline 技术揭秘 Freeline是什么? Freeline是蚂蚁金服旗下一站式理财平台蚂蚁聚宝团队15年10月在Android平台上的量身定做的一个基于动态替换的编译方案,5月阿里集团内部开源,稳定性方面:完善的基线对齐,进程级别异常隔离机制.性能方面:内部采用了类似Facebook的开源工具buck的多工程多任务并发思想:端口扫描,代码扫描,并发编译,并发dx,并发merge dex等策略,在多核机器上有明显加速效果,另外在class及dex,resources层面作了相应缓存策略

Android平台快速滋生7种类型恶意软件

短短一个月时间,Android上的恶意软件数量就已经翻了一倍,从一万变到两万.快速增长的Android威胁已经是个值得大家关注的焦点. 官方Android软件商店 – Google Play变成有毒应用程序的温床.假冒的Skype.Imstagram.愤怒小鸟太空版,以及其他假冒知名应用程序的恶意软件可以向增值服务商发送短信,让用户产生额外费用.用户好奇的天性也被间谍程序(例如Spy Tool和Spy Phone Pro+)用来赚钱.设计复杂的BotPanda甚至会隐藏自己的行为,在Root过的

Android平台进入数字家庭领域

8月6日消息, 业界标准处理器架构与内核厂商MIPS科技公司(MIPS Technologies, Inc) 今天宣布,为推动 Android?平台进入手机以外应用领域所做的努力实现了重要里程碑进展.就在宣布将 Android 平台移植到 MIPS? 架构两个月后,MIPS 科技已开始对大众公开源代码,同时也已针对少数重要客户启动了一项"早期使用计划"(Early Access Program),让他们能够在正式公开前,先使用特定硬件和程序代码优化.这些客户将与 MIPS 科技的工程团

android平台-急急急 高悬赏 在Android平台上怎样做一个学生签到的系统

问题描述 急急急 高悬赏 在Android平台上怎样做一个学生签到的系统 急急急!!奉上全部c币~ 求怎样能在Android手机上做个小APP实现: 通过Android平台和GPS定位系统实现学生注册.登录,利用GPS定位发送位置进行签到,并能结合人脸识别验证,最终实现学生签到系统.并要求界面简单,使用Java语言. 解决方案 急急急!房屋销售管理系统的数据库急急 解决方案二: 参考一下吧http://download.csdn.net/detail/fnkfdn/9518416 解决方案三: