android 注入初试

参考

https://jaq.alibaba.com/community/art/show?spm=a313e.7916648.0.0.E3QzcI&articleid=373

hooking linux与android
框架非常多,hook native,jni和java层的函数,但是需要研究原理,可以阅读开源框架的代码,比如xpose。

原生层hook

其实无论是hook还是调试都离不开ptrace这个system call

1
2
3
4
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
//其中request参数为一个联合体,该参数决定了ptrace函数的行为,pid参数为远程进程的ID,addr参数与data参数在不同的request参数取值下表示不同的含义。
//因为long随linux系统位数改变,所以返回采用long

自己实现了个交叉编译make脚本,见同期博客

hook1获取目标系统调用

这个是获取目标信息
hook1:

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
#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>
long getSysCallNo(int pid, struct pt_regs *regs)
{
long scno = 0;
scno = ptrace(PTRACE_PEEKTEXT, pid, (void *)(regs->ARM_pc - 4), NULL);//读取一个指令(4字节),从指定内存地址 pc-4所指向的
if(scno == 0)
return 0;
//ARM架构上,所有的系统调用都是通过SWI来实现的。并且在ARM 架构中有两个SWI指令,分别针对EABI和OABI:
if (scno == 0xef000000) {
scno = regs->ARM_r7;
/*
[EABI] 机器码:
1110 1111 0000 0000 2字节 SWI 0
0为立即数 表示系统调用
具体的调用号存放在寄存器r7中.
*/
} else {
// 命令后半 +立即数的半字节 因为立即数=调用号|0x900000所以 查看立即数是否合法
if ((scno & 0x0ff00000) != 0x0f900000) {
return -1;
}
scno &= 0x000fffff; //取调用号
/*
[OABI] 机器码:
1101 1111 vvvv vvvv 2字节 SWI immed_8
immed_8 为立即数
调用号进行转换以后得到指令中的立即数。立即数=调用号 | 0x900000 立即数中含调用号
*/
}
return scno;
}
void hookSysCallBefore(pid_t pid)
{
struct pt_regs regs;
int sysCallNo = 0;
ptrace(PTRACE_GETREGS, pid, NULL, &regs); //读取寄存器
sysCallNo = getSysCallNo(pid, &regs);
printf("Before SysCallNo = %d\n",sysCallNo);
//关于system call number对应的具体system call可以参考
if(sysCallNo == __NR_write)
{
printf("__NR_write: %ld %p %ld\n",regs.ARM_r0,(void*)regs.ARM_r1,regs.ARM_r2);
}
}
void hookSysCallAfter(pid_t pid)
{
struct pt_regs regs;
int sysCallNo = 0;
ptrace(PTRACE_GETREGS, pid, NULL, &regs);
sysCallNo = getSysCallNo(pid, &regs);
printf("After SysCallNo = %d\n",sysCallNo);
if(sysCallNo == __NR_write)
{
printf("__NR_write return: %ld\n",regs.ARM_r0);
}
printf("\n");
}
int main(int argc, char *argv[])
{
if(argc != 2) {
printf("Usage: %s <pid to be traced>\n", argv[0]);//第一个参数为文件名
return 1;
}
pid_t pid; //32位为int
int status;
pid = atoi(argv[1]); //字串化为int
if(0 != ptrace(PTRACE_ATTACH, pid, NULL, NULL)) //附着目标任务
{
printf("Trace process failed:%d.\n", errno);
return 1;
}
ptrace(PTRACE_SYSCALL, pid, NULL, NULL); //在system call前下断点 再次使用使syscall后也停。
while(1)
{
wait(&status);//等待子进程终止(fork,clone出的),任意一个子进程停则返回,返回id,状态存于status中
hookSysCallBefore(pid);
ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
wait(&status);
hookSysCallAfter(pid);
ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
}
ptrace(PTRACE_DETACH, pid, NULL, NULL);//结束附着
return 0;
}

用ps命令找到目标进程的pid。./hook1 xx 即可。原理就是用ptrace附着进程。 具体见代码注释,printf本质就是调用write这个系统调用,参数为流,内容,大小。

hook2更改目标内存

这个是更改目标内存

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
179
#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>
const int long_size = sizeof(long);
//随着宏__WORDSIZE值的改变,long int数据类型的大小也会发生改变。如果__WORDSIZE的值为32,则long int和int类型一样,占有32位。
// 字=word=int=long =32位下为4字节
long getSysCallNo(int pid, struct pt_regs *regs)
{
long scno = 0;
scno = ptrace(PTRACE_PEEKTEXT, pid, (void *)(regs->ARM_pc - 4), NULL);
if(scno == 0)
return 0;
if (scno == 0xef000000) {
scno = regs->ARM_r7;
} else {
if ((scno & 0x0ff00000) != 0x0f900000) {
return -1;
}
scno &= 0x000fffff;
}
return scno;
}
void reverse(char *str)
{ int i, j;
char temp;
for(i = 0, j = strlen(str) - 2;//\n不翻所以多-1
i <= j; ++i, --j) {
temp = str[i];
str[i] = str[j];
str[j] = temp;
}
}
void getdata(pid_t child, long addr,
char *str, int len)
{ char *laddr;
int i, j;
union u {
long val;
char chars[long_size];
}data;//date读时作为long 写时作为char数组
i = 0;
j = len / long_size;
laddr = str;
while(i < j) {
data.val = ptrace(PTRACE_PEEKDATA,
child, addr + i * 4,
NULL);
//用PEEKTEXT去取程序指令,用PEEKDATA去取数据,因为linux下程序质量和数据都在一个地址空间,所以没区别
memcpy(laddr, data.chars, long_size);
++i;
laddr += long_size;
}
j = len % long_size;
if(j != 0) {//把余下的读了
data.val = ptrace(PTRACE_PEEKDATA,
child, addr + i * 4,
NULL);
memcpy(laddr, data.chars, j);
}
str[len] = '\0';
}
void putdata(pid_t child, 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, child,
addr + i * 4, data.val);//写时long(4字节)直接用参4传
++i;
laddr += long_size;
}
j = len % long_size;
if(j != 0) {
memcpy(data.chars, laddr, j);
ptrace(PTRACE_POKEDATA, child,
addr + i * 4, data.val);//这里可能会覆盖目标内存中字串后的一部分
//应该首先通过request参数为PTRACE_PEEKTEXT的ptrace函数读取原内存中的一个word大小的数据,然后将要写入的数据复制到读取出的数据的低位,然后调用ptrace函数将修改后的数据写入远程进程的内存地址处。
}
}
//翻转字符串 任务id 字串地址 字串长度
void modifyString(pid_t pid, long addr, long strlen)
{
char* str;
str = (char *)calloc((strlen+1) * sizeof(char), 1);//分配内存在自己这里
getdata(pid, addr, str, strlen);//取
reverse(str);//翻
putdata(pid, addr, str, strlen);//存
}
void hookSysCallBefore(pid_t pid)
{
struct pt_regs regs;
int sysCallNo = 0;
ptrace(PTRACE_GETREGS, pid, NULL, &regs); //取寄存器
sysCallNo = getSysCallNo(pid, &regs);//取系统调用编号
printf("Before SysCallNo = %d\n",sysCallNo);
if(sysCallNo == __NR_write)
{
printf("__NR_write: %ld %p %ld\n",regs.ARM_r0,(void*)regs.ARM_r1,regs.ARM_r2);
modifyString(pid, regs.ARM_r1, regs.ARM_r2);//r1指向字符串,r2存储长度
}
}
void hookSysCallAfter(pid_t pid)
{
struct pt_regs regs;
int sysCallNo = 0;
ptrace(PTRACE_GETREGS, pid, NULL, &regs);
sysCallNo = getSysCallNo(pid, &regs);
printf("After SysCallNo = %d\n",sysCallNo);
if(sysCallNo == __NR_write)
{
printf("__NR_write return: %ld\n",regs.ARM_r0);
}
printf("\n");
}
int main(int argc, char *argv[])
{
if(argc != 2) {
printf("Usage: %s <pid to be traced>\n", argv[0]);
return 1;
}
pid_t pid;
int status;
pid = atoi(argv[1]);
if(0 != ptrace(PTRACE_ATTACH, pid, NULL, NULL))
{
printf("Trace process failed:%d.\n", errno);
return 1;
}
ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
while(1)
{
wait(&status);
hookSysCallBefore(pid);
ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
wait(&status);
hookSysCallAfter(pid);
ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
}
ptrace(PTRACE_DETACH, pid, NULL, NULL);
return 0;
}

hook3动态执行sleep()函数

这个是调用用远程已有模块执行

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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
#include <stdio.h>
#include <stdlib.h>
#include <sys/user.h>
#include <sys/ptrace.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <dirent.h>
#include <unistd.h>
#include <string.h>
#include <elf.h>
#include <android/log.h>
#include <errno.h>
#define CPSR_T_MASK ( 1u << 5 )
const char *libc_path = "/system/lib/libc.so";
const int long_size = sizeof(long);
//设置寄存器
int ptrace_setregs(pid_t pid, struct pt_regs * regs)
{
if (ptrace(PTRACE_SETREGS, pid, NULL, regs) < 0) {
perror("ptrace_setregs: Can not set register values");
return -1;
}
return 0;
}
//使目标继续执行
int ptrace_continue(pid_t pid)
{
if (ptrace(PTRACE_CONT, pid, NULL, 0) < 0) {
perror("ptrace_cont");
return -1;
}
return 0;
}
//往目标地址 id 地址 数据 大小
void putdata(pid_t child, 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, child,
addr + i * 4, data.val);
++i;
laddr += long_size;
}
j = len % long_size;
if(j != 0) {
memcpy(data.chars, laddr, j);
ptrace(PTRACE_POKEDATA, child,
addr + i * 4, data.val);
}
}
//取模块地址
void* get_module_base(pid_t pid, const char* module_name)
{
FILE *fp;
long addr = 0;
char *pch;
char filename[32];
char line[1024];
//读取proc运行时数据/pid/maps
if (pid == 0) {
snprintf(filename, sizeof(filename), "/proc/self/maps");
} else {
snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
}
fp = fopen(filename, "r");
if (fp != NULL) {
while (fgets(line, sizeof(line), fp)) {
if (strstr(line, module_name)) {
pch = strtok( line, "-" ); //这里不清楚,需要linux系统知识
addr = strtoul( pch, NULL, 16 );
if (addr == 0x8000)
addr = 0;
break;
}
}
fclose(fp) ;
}
return (void *)addr;
}
//取sleep地址 任务名 模块名 本地地址
long get_remote_addr(pid_t target_pid, const char* module_name, void* local_addr)
{
void* local_handle, *remote_handle;
local_handle = get_module_base(0, module_name);//取自己的libc.so地址
remote_handle = get_module_base(target_pid, module_name);//取目标的libc.so地址
printf("module_base: local[%p], remote[%p]\n", local_handle, remote_handle);
long ret_addr = (long)((uint32_t)local_addr + (uint32_t)remote_handle - (uint32_t)local_handle);
printf("remote_addr: [%p]\n", (void*) ret_addr);
return ret_addr;
}
//远程调用函数 目标id 函数地址 参数 参数个数 寄存器
int ptrace_call(pid_t pid, long addr, long *params, uint32_t num_params, struct pt_regs* regs)
{
uint32_t i;
for (i = 0; i < num_params && i < 4; i ++) {
regs->uregs[i] = params[i]; //填参数,寄存器只能填4个
}
//
// push remained params onto stack
//
if (i < num_params) {//其它的用栈填
regs->ARM_sp -= (num_params - i) * sizeof(long) ;//栈先减 一个参用4个字节
putdata(pid, (long)regs->ARM_sp, (char*)&params[i], (num_params - i) * sizeof(long));//直接存数据
//参数入栈是从右到左压的,所以右在高左在低,所以填充直接从低往高写即可。(设计就是为了这样便利)
}
regs->ARM_pc = addr;//pc指向函数
//决定以什么模式运行函数 由pc决定
if (regs->ARM_pc & 1) {
/* thumb */
regs->ARM_pc &= (~1u);
regs->ARM_cpsr |= CPSR_T_MASK;
} else {
/* arm */
regs->ARM_cpsr &= ~CPSR_T_MASK;
}
regs->ARM_lr = 0;
//远程进程的函数调用结束后,会跳转到LR寄存器存储的地址处,但由于LR寄存器被设置为0,会导致远程进程执行出错,此时进程会进入暂停状态
if (ptrace_setregs(pid, regs) == -1
|| ptrace_continue(pid) == -1) {//设置且继续执行
printf("error\n");
return -1;
}
int stat = 0;
waitpid(pid, &stat, WUNTRACED);//waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。WUNTRACED若子进程进入暂停状态,则马上返回
while (stat != 0xb7f) {
if (ptrace_continue(pid) == -1) {
printf("error\n");
return -1;
}
waitpid(pid, &stat, WUNTRACED);
}
return 0;
}
void inject(pid_t pid)
{
struct pt_regs old_regs,regs;
long sleep_addr;
//save old regs
ptrace(PTRACE_GETREGS, pid, NULL, &old_regs);
memcpy(&regs, &old_regs, sizeof(regs));//存寄存器
printf("getting remote sleep_addr:\n");
sleep_addr = get_remote_addr(pid, libc_path, (void *)sleep);//取sleep地址 hook3貌似写成能通用的了
long parameters[1];
parameters[0] = 5;//参数,每个参数4字节(32位下)
ptrace_call(pid, sleep_addr, parameters, 1, &regs);//运行sleep
//restore old regs
ptrace(PTRACE_SETREGS, pid, NULL, &old_regs);
}
int main(int argc, char *argv[])
{
if(argc != 2) {
printf("Usage: %s <pid to be traced>\n", argv[0]);
return 1;
}
pid_t pid;
int status;
pid = atoi(argv[1]);
if(0 != ptrace(PTRACE_ATTACH, pid, NULL, NULL))//附加操作会中断目标进程
{
printf("Trace process failed:%d.\n", errno);
return 1;
}
printf("\n\n hook3 \n\n");
inject(pid); //远程执行
ptrace(PTRACE_DETACH, pid, NULL, 0);
//这个是脱离附着 ,在detach后被注入进程将继续运行。但是我却显示Stopped (signal)而退出了,原因未知
return 0;
}

hook4利用Ptrace动态加载so并执行自定义函数

这个失败了。

hook4:

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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
#include <stdio.h>
#include <stdlib.h>
#include <sys/user.h>
#include <sys/ptrace.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <dirent.h>
#include <unistd.h>
#include <string.h>
#include <elf.h>
#include <android/log.h>
#include <errno.h>
#define CPSR_T_MASK ( 1u << 5 )
const char *libc_path = "/system/lib/libc.so";
const int long_size = sizeof(long);
//设置寄存器
int ptrace_setregs(pid_t pid, struct pt_regs * regs)
{
if (ptrace(PTRACE_SETREGS, pid, NULL, regs) < 0) {
perror("ptrace_setregs: Can not set register values");
return -1;
}
return 0;
}
//继续执行
int ptrace_continue(pid_t pid)
{
if (ptrace(PTRACE_CONT, pid, NULL, 0) < 0) {
perror("ptrace_cont");
return -1;
}
return 0;
}
//放数据
void putdata(pid_t child, 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, child,
addr + i * 4, data.val);
++i;
laddr += long_size;
}
j = len % long_size;
if(j != 0) {
memcpy(data.chars, laddr, j);
ptrace(PTRACE_POKEDATA, child,
addr + i * 4, data.val);
}
}
//取模块地址
void* get_module_base(pid_t pid, const char* module_name)
{
FILE *fp;
long addr = 0;
char *pch;
char filename[32];
char line[1024];
if (pid == 0) {
snprintf(filename, sizeof(filename), "/proc/self/maps");
} else {
snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
}
fp = fopen(filename, "r");
if (fp != NULL) {
while (fgets(line, sizeof(line), fp)) {
if (strstr(line, module_name)) {
pch = strtok( line, "-" );
addr = strtoul( pch, NULL, 16 );
if (addr == 0x8000)
addr = 0;
break;
}
}
fclose(fp) ;
}
return (void *)addr;
}
//取目标函数地址
long get_remote_addr(pid_t target_pid, const char* module_name, void* local_addr)
{
void* local_handle, *remote_handle;
local_handle = get_module_base(0, module_name);
remote_handle = get_module_base(target_pid, module_name);
long ret_addr = (long)((uint32_t)local_addr + (uint32_t)remote_handle - (uint32_t)local_handle);
return ret_addr;
}
//调用目标函数
int ptrace_call(pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct pt_regs* regs)
{
uint32_t i;
for (i = 0; i < num_params && i < 4; i ++) {
regs->uregs[i] = params[i];
}
//
// push remained params onto stack
//
if (i < num_params) {
regs->ARM_sp -= (num_params - i) * sizeof(long) ;
putdata(pid, regs->ARM_sp, (char*)&params[i], (num_params - i) * sizeof(long));
}
regs->ARM_pc = addr;
if (regs->ARM_pc & 1) {
/* thumb */
regs->ARM_pc &= (~1u);
regs->ARM_cpsr |= CPSR_T_MASK;
} else {
/* arm */
regs->ARM_cpsr &= ~CPSR_T_MASK;
}
regs->ARM_lr = 0;
if (ptrace_setregs(pid, regs) == -1
|| ptrace_continue(pid) == -1) {
printf("error\n");
return -1;
}
int stat = 0;
waitpid(pid, &stat, WUNTRACED);
while (stat != 0xb7f) {//返回为执行完成发生返回地址错误时 regs->ARM_lr = 0;
if (ptrace_continue(pid) == -1) {
printf("error\n");
return -1;
}
waitpid(pid, &stat, WUNTRACED);
}
return 0;
}
//加载模块并调用执行函数 id so的路径 函数名 参数
void injectSo(pid_t pid,char* so_path, char* function_name,char* parameter)
{
struct pt_regs old_regs,regs;
long mmap_addr, dlopen_addr, dlsym_addr, dlclose_addr;
//保存当前寄存器的状态
ptrace(PTRACE_GETREGS, pid, NULL, &old_regs);
memcpy(&regs, &old_regs, sizeof(regs));
//取目标程序的mmap, dlopen, dlsym, dlclose 地址
printf("getting remote addres:\n");
mmap_addr = get_remote_addr(pid, libc_path, (void *)mmap);
dlopen_addr = get_remote_addr( pid, libc_path, (void *)dlopen );
dlsym_addr = get_remote_addr( pid, libc_path, (void *)dlsym );
dlclose_addr = get_remote_addr( pid, libc_path, (void *)dlclose );
printf("mmap_addr=%p dlopen_addr=%p dlsym_addr=%p dlclose_addr=%p\n",
(void*)mmap_addr,(void*)dlopen_addr,(void*)dlsym_addr,(void*)dlclose_addr);
long parameters[10];
//调用mmap分配一段内存空间用来保存参数信息,因为它的参数都可以只用栈传递,但其他函数有的需要字串不能用栈传,因此要开辟空间
/*
mmap()可以用来将一个文件或者其它对象映射进内存,如果我们把flag设置为MAP_ANONYMOUS并且把参数fd设置为0的话就相当于直接映射一段内容为空的内存
void *mmap(void *start,size_t length,int prot,int fd,off_t offset);
start:映射区的开始地址,设置为0时表示由系统决定映射区的起始地址。
length:映射区的长度。
prot:期望的内存保护标志,不能与文件的打开模式冲突。我们这里设置为RWX。
flags:指定映射对象的类型,映射选项和映射页是否可以共享。我们这里设置为:MAP_ANONYMOUS(匿名映射,映射区不与任何文件关联),MAP_PRIVATE(建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件)。
fd:有效的文件描述词。匿名映射设置为0。
off_toffset:被映射对象内容的起点。设置为0。
*/
parameters[0] = 0; //start
parameters[1] = 0x4000; //length
parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC; //prot =WRX
parameters[3] = MAP_ANONYMOUS | MAP_PRIVATE; //flag
parameters[4] = 0; //fd
parameters[5] = 0; //offset
ptrace_call(pid, mmap_addr, parameters, 6, &regs);
//获取返回结果
ptrace(PTRACE_GETREGS, pid, NULL, &regs);
long map_base = regs.ARM_r0;
printf("map_base = %p\n", (void*)map_base);
//调用dlopen加载so文件
printf("save so_path = %s to map_base = %p\n", so_path, (void*)map_base);
putdata(pid, map_base, so_path, strlen(so_path) + 1);//把so路径放入目标内存中
parameters[0] = map_base;//参1 路径
parameters[1] = RTLD_NOW| RTLD_GLOBAL;
ptrace_call(pid, dlopen_addr, parameters, 2, &regs);
ptrace(PTRACE_GETREGS, pid, NULL, &regs);//调用函数并返回库引用
long handle = regs.ARM_r0;
printf("handle = %p\n",(void*) handle);
//调用dlsym找到目标函数地址
printf("save function_name = %s to map_base = %p\n", function_name, (void*)map_base);
putdata(pid, map_base, function_name, strlen(function_name) + 1);//存函数名
parameters[0] = handle;
parameters[1] = map_base;
ptrace_call(pid, dlsym_addr, parameters, 2, &regs);
ptrace(PTRACE_GETREGS, pid, NULL, &regs);
long function_ptr = regs.ARM_r0;//取函数地址
printf("function_ptr = %p\n", (void*)function_ptr);
//使用ptrace_call执行目标函数
printf("save parameter = %s to map_base = %p\n", parameter, (void*)map_base);
putdata(pid, map_base, parameter, strlen(parameter) + 1);
parameters[0] = map_base;
printf("执行目标函数");
ptrace_call(pid, function_ptr, parameters, 1, &regs);
printf("执行目标函数完了");
//调用 dlclose 卸载so文件
parameters[0] = handle;
ptrace_call(pid, dlclose_addr, parameters, 1, &regs);
//恢复寄存器的状态
ptrace(PTRACE_SETREGS, pid, NULL, &old_regs);
}
int main(int argc, char *argv[])
{
if(argc != 2) {
printf("Usage: %s <pid to be traced>\n", argv[0]);
return 1;
}
pid_t pid;
int status;
pid = atoi(argv[1]);
if(0 != ptrace(PTRACE_ATTACH, pid, NULL, NULL))
{
printf("Trace process failed:%d.\n", errno);
return 1;
}
char* so_path = "/data/local/tmp/libinject.so";//动态加载的文件
char* function_name = "mzhengHook";//调用的函数名
char* parameter = "sevenWeapons";//参数
injectSo(pid, so_path, function_name, parameter);//远程加载调用
ptrace(PTRACE_DETACH, pid, NULL, 0);
return 0;
}

inject.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <android/log.h>
#include <elf.h>
#include <fcntl.h>
#define LOG_TAG "DEBUG"
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)
int mzhengHook(char * str){
printf("mzheng Hook pid = %d\n", getpid());
printf("Hello %s\n", str);
LOGD("mzheng Hook pid = %d\n", getpid());
LOGD("Hello %s\n", str);
return 0;
}

关于arm的regs
pt_regs结构的定义:

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
struct pt_regs {
long uregs[18];32位
};
#define ARM_cpsr uregs[16]
#define ARM_pc uregs[15]
#define ARM_lr uregs[14]
#define ARM_sp uregs[13]
#define ARM_ip uregs[12]
#define ARM_fp uregs[11]
#define ARM_r10 uregs[10]
#define ARM_r9 uregs[9]
#define ARM_r8 uregs[8]
#define ARM_r7 uregs[7]
#define ARM_r6 uregs[6]
#define ARM_r5 uregs[5]
#define ARM_r4 uregs[4]
#define ARM_r3 uregs[3]
#define ARM_r2 uregs[2]
#define ARM_r1 uregs[1]
#define ARM_r0 uregs[0]
#define ARM_ORIG_r0 uregs[17]

还有一篇下暂时暂停,因为hook4不成功,且需要其成功
注入4不成功的原因是我的测试系统android>7.0其添加了加载so白名单,只有应用包内的so与系统白名单的so可以加载。因此加载失败
解决方案:

  1. 自己实现so加载,稳定但工作量太大
  2. 先注入修改linker,再加载-可行

后续可以继续参考蒸米的文章实现