《游戏安全:手游安全技术入门》理论篇

1-12章

因为我读这本书的初衷是研究native层的注入和hook技术,并不十分关注游戏外挂本身,而且我已经有一定的逆向经验,所以这部分我只是大致看了看外挂的种类和游戏漏洞的风险点。

需要学习的语言

在逆向分析中主要掌握的是ARM、c、c++、oc,但是除此之外,android软件经常使用的java以及smali代码,还有U3D主逻辑使用的c#和IL代码,以及某些游戏会使用lua或py脚本来写主逻辑,遇到这些语言,只需要在遇到他们时能够读懂基本逻辑即可。

外挂技术——静态修改

如果已经了解游戏的关键逻辑代码的位置,就可以直接修改,针对不同的语言有不同的修改方式,如针对c#编写的逻辑,用ildasm反编译后,修改IL指令,然后通过ilasm编译成dll文件,替换原安装包中的即可。

外挂技术——动态修改

若游戏做了比较严格的完整性校验或签名校验,难以静态绕过,则可以考虑动态。

漏洞的实现原理

  • 游戏逻辑漏洞
    通过修改客户端的游戏来实现外挂功能,游戏逻辑漏洞与游戏网络交互关系密切,如果游戏的绝大部分逻辑在服务端实现,客户端只是同步结果,则这类强联网游戏的安全性较好。
  • 游戏协议稳定型漏洞
    协议稳定型漏洞是指构造畸形协议导致协议处理方在处理协议时出现崩溃的漏洞。
    协议稳定型漏洞的挖掘过程实际上就是畸形协议的构造过程,而畸形协议字段的构造可考虑通过Fuzz工具的思想实现,对各类边缘值的测试。
  • 游戏服务端校验疏忽型漏洞
    本质上就是Web漏洞,服务端校验不严格。

手游的分类和风险点

99-104页

13章 注入技术的实现原理

ptrace注入技术的实现

整体流程

代码实例

Android.mk

1
2
3
4
5
6
7
8
9
LOCAL_PATH := $(call my-dir)  

include $(CLEAR_VARS)
LOCAL_MODULE := inject
LOCAL_SRC_FILES := ptraceInject.c InjectModule.c shellcode.s

LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog

include $(BUILD_EXECUTABLE)

InjectModule.c

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
/************************************************************
FileName: InjectModule.c
Description: ptrace注入
***********************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <asm/user.h>
#include <asm/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 <ptraceInject.h>
#include <utils/PrintLog.h>

/*************************************************
Description: 通过进程名称定位到进程的PID
Input: process_name为要定位的进程名称
Output: 无
Return: 返回定位到的进程PID,若为-1,表示定位失败
Others: 无
*************************************************/
pid_t FindPidByProcessName(const char *process_name)
{
int ProcessDirID = 0;
pid_t pid = -1;
FILE *fp = NULL;
char filename[MAX_PATH] = {0};
char cmdline[MAX_PATH] = {0};

struct dirent * entry = NULL;

if ( process_name == NULL )
return -1;

DIR* dir = opendir( "/proc" );
if ( dir == NULL )
return -1;

while( (entry = readdir(dir)) != NULL )
{
ProcessDirID = atoi( entry->d_name );
if ( ProcessDirID != 0 )
{
snprintf(filename, MAX_PATH, "/proc/%d/cmdline", ProcessDirID);
fp = fopen( filename, "r" );
if ( fp )
{
fgets(cmdline, sizeof(cmdline), fp);
fclose(fp);

if (strncmp(process_name, cmdline, strlen(process_name)) == 0)
{
pid = ProcessDirID;
break;
}
}
}
}

closedir(dir);
return pid;
}

int main(int argc, char *argv[]) {
char InjectModuleName[MAX_PATH] = "/data/libInjectModule.so"; // 注入模块全路径
char RemoteCallFunc[MAX_PATH] = "Inject_entry"; // 注入模块后调用模块函数名称
char InjectProcessName[MAX_PATH] = "com.yiyou.org.mario.xyhy"; // 注入进程名称

// 当前设备环境判断
#if defined(__i386__)
LOGD("Current Environment x86");
return -1;
#elif defined(__arm__)
LOGD("Current Environment ARM");
#else
LOGD("other Environment");
return -1;
#endif

pid_t pid = FindPidByProcessName(InjectProcessName);
if (pid == -1)
{
printf("Get Pid Failed");
return;
}

printf("begin inject process, RemoteProcess pid:%d, InjectModuleName:%s, RemoteCallFunc:%s\n", pid, InjectModuleName, RemoteCallFunc);
int iRet = inject_remote_process(pid, InjectModuleName, RemoteCallFunc, NULL, 0);
//int iRet = inject_remote_process_shellcode(pid, InjectModuleName, RemoteCallFunc, NULL, 0);

if (iRet == 0)
{
printf("Inject Success\n");
}
else
{
printf("Inject Failed\n");
}
printf("end inject,%d\n", pid);
return 0;
}

ptraceInject.h

1
2
3
4
5
6
7
8
9
10
11
12
13
/************************************************************
FileName: ptraceInject.h
Description: ptrace注入
***********************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define MAX_PATH 0x100

int inject_remote_process(pid_t pid, char *LibPath, char *FunctionName, long *FuncParameter, long NumParameter); // 通过ptrace远程调用dlopen/dlsym方式注入模块到远程进程
int inject_remote_process_shellcode(pid_t pid, char *LibPath, char *FunctionName, long *FuncParameter, long NumParameter); // 通过shellcode方式注入模块到远程进程

ptraceInject.c

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
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
/************************************************************
FileName: ptraceInject.c
Description: ptrace注入
***********************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <asm/user.h>
#include <asm/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 <utils/PrintLog.h>
#include <ptraceInject.h>

#define CPSR_T_MASK ( 1u << 5 )

#define REMOTE_ADDR( addr, local_base, remote_base ) ( (uint32_t)(addr) + (uint32_t)(remote_base) - (uint32_t)(local_base) )

const char *libc_path = "/system/lib/libc.so";
const char *linker_path = "/system/bin/linker";

/*************************************************
Description: ptrace使远程进程继续运行
Input: pid表示远程进程的ID
Output: 无
Return: 返回0表示continue成功,返回-1表示失败
Others: 无
*************************************************/
int ptrace_continue(pid_t pid)
{
if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0)
{
LOGD("ptrace cont error, pid:%d", pid);
return -1;
}
return 0;
}

/*************************************************
Description: 使用ptrace Attach到指定进程
Input: pid表示远程进程的ID
Output: 无
Return: 返回0表示attach成功,返回-1表示失败
Others: 无
*************************************************/
int ptrace_attach(pid_t pid)
{
int status = 0;

if (ptrace(PTRACE_ATTACH, pid, NULL, 0) < 0) {
LOGD("attach process error, pid:%d", pid);
return -1;
}

LOGD("attach process pid:%d", pid);
waitpid(pid, &status , WUNTRACED);

return 0;
}

/*************************************************
Description: 使用ptrace detach指定进程
Input: pid表示远程进程的ID
Output: 无
Return: 返回0表示detach成功,返回-1表示失败
Others: 无
*************************************************/
int ptrace_detach(pid_t pid)
{
if (ptrace(PTRACE_DETACH, pid, NULL, 0) < 0) {
LOGD("detach process error, pid:%d", pid);
return -1;
}

LOGD("detach process pid:%d", pid);
return 0;
}

/*************************************************
Description: 使用ptrace获取远程进程的寄存器值
Input: pid表示远程进程的ID,regs为pt_regs结构,存储了寄存器值
Output: 无
Return: 返回0表示获取寄存器成功,返回-1表示失败
Others: 无
*************************************************/
int ptrace_getregs(pid_t pid, struct pt_regs *regs)
{
if (ptrace(PTRACE_GETREGS, pid, NULL, regs) < 0)
{
LOGD("Get Regs error, pid:%d", pid);
return -1;
}

return 0;
}

/*************************************************
Description: 使用ptrace设置远程进程的寄存器值
Input: pid表示远程进程的ID,regs为pt_regs结构,存储需要修改的寄存器值
Output: 无
Return: 返回0表示设置寄存器成功,返回-1表示失败
Others: 无
*************************************************/
int ptrace_setregs(pid_t pid, struct pt_regs *regs)
{
if (ptrace(PTRACE_SETREGS, pid, NULL, regs) < 0)
{
LOGD("Set Regs error, pid:%d", pid);
return -1;
}

return 0;
}

/*************************************************
Description: 获取返回值,ARM处理器中返回值存放在ARM_r0寄存器中
Input: regs存储远程进程当前的寄存器值
Output: 无
Return: 在ARM处理器下返回r0寄存器值
Others: 无
*************************************************/
long ptrace_getret(struct pt_regs * regs)
{
return regs->ARM_r0;
}

/*************************************************
Description: 获取当前执行代码的地址,ARM处理器下存放在ARM_pc中
Input: regs存储远程进程当前的寄存器值
Output: 无
Return: 在ARM处理器下返回pc寄存器值
Others: 无
*************************************************/
long ptrace_getpc(struct pt_regs * regs)
{
return regs->ARM_pc;
}

/*************************************************
Description: 使用ptrace从远程进程内存中读取数据
Input: pid表示远程进程的ID,pSrcBuf表示从远程进程读取数据的内存地址
pDestBuf表示用于存储读取出数据的地址,size表示读取数据的大小
Output: 无
Return: 返回0表示读取数据成功
Others: 无
*************************************************/
int ptrace_readdata(pid_t pid, uint8_t *pSrcBuf, uint8_t *pDestBuf, uint32_t size)
{
uint32_t nReadCount = 0;
uint32_t nRemainCount = 0;
uint8_t *pCurSrcBuf = pSrcBuf;
uint8_t *pCurDestBuf = pDestBuf;
long lTmpBuf = 0;
uint32_t i = 0;

nReadCount = size / sizeof(long);
nRemainCount = size % sizeof(long);

for (i = 0; i < nReadCount; i ++ )
{
lTmpBuf = ptrace(PTRACE_PEEKTEXT, pid, pCurSrcBuf, 0);
memcpy(pCurDestBuf, (char *)(&lTmpBuf), sizeof(long));
pCurSrcBuf += sizeof(long);
pCurDestBuf += sizeof(long);
}

if ( nRemainCount > 0 )
{
lTmpBuf = ptrace(PTRACE_PEEKTEXT, pid, pCurSrcBuf, 0);
memcpy(pCurDestBuf, (char *)(&lTmpBuf), nRemainCount);
}

return 0;
}

//
/*************************************************
Description: 使用ptrace将数据写入到远程进程空间中
Input: pid表示远程进程的ID,pWriteAddr表示写入数据到远程进程的内存地址
pWriteData用于存储写入数据的地址,size表示写入数据的大小
Output: 无
Return: 返回0表示写入数据成功,返回-1表示写入数据失败
Others: 无
*************************************************/
int ptrace_writedata(pid_t pid, uint8_t *pWriteAddr, uint8_t *pWriteData, uint32_t size)
{
uint32_t nWriteCount = 0;
uint32_t nRemainCount = 0;
uint8_t *pCurSrcBuf = pWriteData;
uint8_t *pCurDestBuf = pWriteAddr;
long lTmpBuf = 0;
uint32_t i = 0;

nWriteCount = size / sizeof(long);
nRemainCount = size % sizeof(long);

// 先讲数据以sizeof(long)字节大小为单位写入到远程进程内存空间中
for (i = 0; i < nWriteCount; i ++)
{
memcpy((void *)(&lTmpBuf), pCurSrcBuf, sizeof(long));
if (ptrace(PTRACE_POKETEXT, pid, pCurDestBuf, lTmpBuf) < 0) // PTRACE_POKETEXT表示从远程内存空间写入一个sizeof(long)大小的数据
{
LOGD("Write Remote Memory error, MemoryAddr:0x%lx", (long)pCurDestBuf);
return -1;
}
pCurSrcBuf += sizeof(long);
pCurDestBuf += sizeof(long);
}

// 将剩下的数据写入到远程进程内存空间中
if (nRemainCount > 0)
{
lTmpBuf = ptrace(PTRACE_PEEKTEXT, pid, pCurDestBuf, NULL); //先取出原内存中的数据,然后将要写入的数据以单字节形式填充到低字节处
memcpy((void *)(&lTmpBuf), pCurSrcBuf, nRemainCount);
if (ptrace(PTRACE_POKETEXT, pid, pCurDestBuf, lTmpBuf) < 0)
{
LOGD("Write Remote Memory error, MemoryAddr:0x%lx", (long)pCurDestBuf);
return -1;
}
}

return 0;
}

/*************************************************
Description: 使用ptrace远程call函数
Input: pid表示远程进程的ID,ExecuteAddr为远程进程函数的地址
parameters为函数参数的地址,regs为远程进程call函数前的寄存器环境
Output: 无
Return: 返回0表示call函数成功,返回-1表示失败
Others: 无
*************************************************/
int ptrace_call(pid_t pid, uint32_t ExecuteAddr, long *parameters, long num_params, struct pt_regs* regs)
{
int i = 0;
// ARM处理器,函数传递参数,将前四个参数放到r0-r3,剩下的参数压入栈中
for (i = 0; i < num_params && i < 4; i ++) {
regs->uregs[i] = parameters[i];
}

if (i < num_params) {
regs->ARM_sp -= (num_params - i) * sizeof(long) ; // 分配栈空间,栈的方向是从高地址到低地址
if (ptrace_writedata(pid, (void *)regs->ARM_sp, (uint8_t *)&parameters[i], (num_params - i) * sizeof(long)) == -1)
return -1;
}

regs->ARM_pc = ExecuteAddr; //设置ARM_pc寄存器为需要调用的函数地址
// 与BX跳转指令类似,判断跳转的地址位[0]是否为1,如果为1,则将CPST寄存器的标志T置位,解释为Thumb代码
// 若为0,则将CPSR寄存器的标志T复位,解释为ARM代码
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) {
LOGD("ptrace set regs or continue error, pid:%d", pid);
return -1;
}

int stat = 0;
// 对于使用ptrace_cont运行的子进程,它会在3种情况下进入暂停状态:①下一次系统调用;②子进程退出;③子进程的执行发生错误。
// 参数WUNTRACED表示当进程进入暂停状态后,立即返回
// 将ARM_lr(存放返回地址)设置为0,会导致子进程执行发生错误,则子进程进入暂停状态
waitpid(pid, &stat, WUNTRACED);

// 判断是否成功执行函数
LOGD("ptrace call ret status is %d\n", stat);
while (stat != 0xb7f) {
if (ptrace_continue(pid) == -1) {
LOGD("ptrace call error");
return -1;
}
waitpid(pid, &stat, WUNTRACED);
}

// 获取远程进程的寄存器值,方便获取返回值
if (ptrace_getregs(pid, regs) == -1)
{
LOGD("After call getregs error");
return -1;
}

return 0;
}

/*************************************************
Description: 在指定进程中搜索对应模块的基址
Input: pid表示远程进程的ID,若为-1表示自身进程,ModuleName表示要搜索的模块的名称
Output: 无
Return: 返回0表示获取模块基址失败,返回非0为要搜索的模块基址
Others: 无
*************************************************/
void* GetModuleBaseAddr(pid_t pid, const char* ModuleName)
{
FILE *fp = NULL;
long ModuleBaseAddr = 0;
char *ModulePath, *MapFileLineItem;
char szFileName[50] = {0};
char szMapFileLine[1024] = {0};
char szProcessInfo[1024] = {0};

// 读取"/proc/pid/maps"可以获得该进程加载的模块
if (pid < 0) {
// 枚举自身进程模块
snprintf(szFileName, sizeof(szFileName), "/proc/self/maps");
} else {
snprintf(szFileName, sizeof(szFileName), "/proc/%d/maps", pid);
}

fp = fopen(szFileName, "r");

if (fp != NULL)
{
while (fgets(szMapFileLine, sizeof(szMapFileLine), fp)) {
if (strstr(szMapFileLine, ModuleName))
{
MapFileLineItem = strtok(szMapFileLine, " \t"); // 基址信息
char *Addr = strtok(szMapFileLine, "-");
ModuleBaseAddr = strtoul(Addr, NULL, 16 );

if (ModuleBaseAddr == 0x8000)
ModuleBaseAddr = 0;

break;
}
}

fclose(fp) ;
}

return (void *)ModuleBaseAddr;
}

/*************************************************
Description: 获取远程进程与本进程都加载的模块中函数的地址
Input: pid表示远程进程的ID,ModuleName表示模块名称,LocalFuncAddr表示本地进程中该函数的地址
Output: 无
Return: 返回远程进程中对应函数的地址
Others: 无
*************************************************/
void* GetRemoteFuncAddr(pid_t pid, const char *ModuleName, void *LocalFuncAddr)
{
void *LocalModuleAddr, *RemoteModuleAddr, *RemoteFuncAddr;

LocalModuleAddr = GetModuleBaseAddr(-1, ModuleName);
RemoteModuleAddr = GetModuleBaseAddr(pid, ModuleName);

RemoteFuncAddr = (void *)((long)LocalFuncAddr - (long)LocalModuleAddr + (long)RemoteModuleAddr);

return RemoteFuncAddr;
}

/*************************************************
Description: 通过远程直接调用dlopen\dlsym的方法ptrace注入so模块到远程进程中
Input: pid表示远程进程的ID,LibPath为被远程注入的so模块路径,FunctionName为远程注入的模块后调用的函数
FuncParameter指向被远程调用函数的参数(若传递字符串,需要先将字符串写入到远程进程空间中),NumParameter为参数的个数
Output: 无
Return: 返回0表示注入成功,返回-1表示失败
Others: 无
*************************************************/
int inject_remote_process(pid_t pid, char *LibPath, char *FunctionName, long *FuncParameter, long NumParameter)
{
int iRet = -1;
struct pt_regs CurrentRegs, OriginalRegs; // CurrentRegs表示远程进程中当前的寄存器值,OriginalRegs存储注入前的寄存器值,方便恢复
void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr, *dlerror_addr; // 远程进程中需要调用函数的地址
void *RemoteMapMemoryAddr, *RemoteModuleAddr, *RemoteModuleFuncAddr; // RemoteMapMemoryAddr为远程进程空间中映射的内存基址,RemoteModuleAddr为远程注入的so模块加载基址,RemoteModuleFuncAddr为注入模块中需要调用的函数地址
long parameters[6];

// Attach远程进程
if (ptrace_attach(pid) == -1)
return iRet;

// 获取远程进程的寄存器值
if (ptrace_getregs(pid, &CurrentRegs) == -1)
{
ptrace_detach(pid);
return iRet;
}

LOGD("ARM_r0:0x%lx, ARM_r1:0x%lx, ARM_r2:0x%lx, ARM_r3:0x%lx, ARM_r4:0x%lx, ARM_r5:0x%lx, ARM_r6:0x%lx, ARM_r7:0x%lx, ARM_r8:0x%lx, ARM_r9:0x%lx, ARM_r10:0x%lx, ARM_ip:0x%lx, ARM_sp:0x%lx, ARM_lr:0x%lx, ARM_pc:0x%lx", \
CurrentRegs.ARM_r0, CurrentRegs.ARM_r1, CurrentRegs.ARM_r2, CurrentRegs.ARM_r3, CurrentRegs.ARM_r4, CurrentRegs.ARM_r5, CurrentRegs.ARM_r6, CurrentRegs.ARM_r7, CurrentRegs.ARM_r8, CurrentRegs.ARM_r9, CurrentRegs.ARM_r10, CurrentRegs.ARM_ip, CurrentRegs.ARM_sp, CurrentRegs.ARM_lr, CurrentRegs.ARM_pc);

// 保存远程进程空间中当前的上下文寄存器环境
memcpy(&OriginalRegs, &CurrentRegs, sizeof(CurrentRegs));

// 获取mmap函数在远程进程中的地址
mmap_addr = GetRemoteFuncAddr(pid, libc_path, (void *)mmap);
LOGD("mmap RemoteFuncAddr:0x%lx", (long)mmap_addr);

// 设置mmap的参数
// void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offsize);
parameters[0] = 0; // 设置为NULL表示让系统自动选择分配内存的地址
parameters[1] = 0x1000; // 映射内存的大小
parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC; // 表示映射内存区域可读可写可执行
parameters[3] = MAP_ANONYMOUS | MAP_PRIVATE; // 建立匿名映射
parameters[4] = 0; // 若需要映射文件到内存中,则为文件的fd
parameters[5] = 0; //文件映射偏移量

// 调用远程进程的mmap函数,建立远程进程的内存映射
if (ptrace_call(pid, (long)mmap_addr, parameters, 6, &CurrentRegs) == -1)
{
LOGD("Call Remote mmap Func Failed");
ptrace_detach(pid);
return iRet;
}

// 获取mmap函数执行后的返回值,也就是内存映射的起始地址
RemoteMapMemoryAddr = (void *)ptrace_getret(&CurrentRegs);
LOGD("Remote Process Map Memory Addr:0x%lx", (long)RemoteMapMemoryAddr);

// 分别获取dlopen、dlsym、dlclose等函数的地址
dlopen_addr = GetRemoteFuncAddr(pid, linker_path, (void *)dlopen);
dlsym_addr = GetRemoteFuncAddr(pid, linker_path, (void *)dlsym);
dlclose_addr = GetRemoteFuncAddr(pid, linker_path, (void *)dlclose);
dlerror_addr = GetRemoteFuncAddr(pid, linker_path, (void *)dlerror);

LOGD("dlopen RemoteFuncAddr:0x%lx", (long)dlopen_addr);
LOGD("dlsym RemoteFuncAddr:0x%lx", (long)dlsym_addr);
LOGD("dlclose RemoteFuncAddr:0x%lx", (long)dlclose_addr);
LOGD("dlerror RemoteFuncAddr:0x%lx", (long)dlerror_addr);

// 将要加载的so库路径写入到远程进程内存空间中
if (ptrace_writedata(pid, RemoteMapMemoryAddr, LibPath, strlen(LibPath) + 1) == -1)
{
LOGD("Write LibPath:%s to RemoteProcess error", LibPath);
ptrace_detach(pid);
return iRet;
}

// 设置dlopen的参数,返回值为模块加载的地址
// void *dlopen(const char *filename, int flag);
parameters[0] = (long)RemoteMapMemoryAddr;
parameters[1] = RTLD_NOW| RTLD_GLOBAL;

if (ptrace_call(pid, (long)dlopen_addr, parameters, 2, &CurrentRegs) == -1)
{
LOGD("Call Remote dlopen Func Failed");
ptrace_detach(pid);
return iRet;
}

// RemoteModuleAddr为远程进程加载注入模块的地址
RemoteModuleAddr = (void *)ptrace_getret(&CurrentRegs);
LOGD("Remote Process load module Addr:0x%lx", (long)RemoteModuleAddr);

if ((long)RemoteModuleAddr == 0x0) // dlopen 错误
{
LOGD("dlopen error");
if (ptrace_call(pid, (long)dlerror_addr, parameters, 0, &CurrentRegs) == -1)
{
LOGD("Call Remote dlerror Func Failed");
ptrace_detach(pid);
return iRet;
}
char *Error = (void *)ptrace_getret(&CurrentRegs);
char LocalErrorInfo[1024] = {0};
ptrace_readdata(pid, Error, LocalErrorInfo, 1024);
LOGD("dlopen error:%s", LocalErrorInfo);
ptrace_detach(pid);
return iRet;
}

// 将so库中需要调用的函数名称写入到远程进程内存空间中
if (ptrace_writedata(pid, RemoteMapMemoryAddr + strlen(LibPath) + 2, FunctionName, strlen(FunctionName) + 1) == -1)
{
LOGD("Write FunctionName:%s to RemoteProcess error", FunctionName);
ptrace_detach(pid);
return iRet;
}

// 设置dlsym的参数,返回值为远程进程内函数的地址
// void *dlsym(void *handle, const char *symbol);
parameters[0] = (long)RemoteModuleAddr;
parameters[1] = (long)(RemoteMapMemoryAddr + strlen(LibPath) + 2);

if (ptrace_call(pid, (long)dlsym_addr, parameters, 2, &CurrentRegs) == -1)
{
LOGD("Call Remote dlsym Func Failed");
ptrace_detach(pid);
return iRet;
}

// RemoteModuleFuncAddr为远程进程空间内获取的函数地址
RemoteModuleFuncAddr = (void *)ptrace_getret(&CurrentRegs);
LOGD("Remote Process ModuleFunc Addr:0x%lx", (long)RemoteModuleFuncAddr);

if (ptrace_call(pid, (long)RemoteModuleFuncAddr, FuncParameter, NumParameter, &CurrentRegs) == -1)
{
LOGD("Call Remote injected Func Failed");
ptrace_detach(pid);
return iRet;
}

if (ptrace_setregs(pid, &OriginalRegs) == -1)
{
LOGD("Recover reges failed");
ptrace_detach(pid);
return iRet;
}
LOGD("Recover Regs Success");
ptrace_getregs(pid, &CurrentRegs);
if (memcmp(&OriginalRegs, &CurrentRegs, sizeof(CurrentRegs)) != 0)
{
LOGD("Set Regs Error");
}

//Detach
if (ptrace_detach(pid) == -1)
{
LOGD("ptrace detach failed");
return iRet;
}

return 0;
}

/*************************************************
Description: 通过shellcode方式ptrace注入so模块到远程进程中
Input: pid表示远程进程的ID,LibPath为被远程注入的so模块路径,FunctionName为远程注入的模块后调用的函数
FuncParameter指向被远程调用函数的参数(若传递字符串,需要先将字符串写入到远程进程空间中),NumParameter为参数的个数
Output: 无
Return: 返回0表示注入成功,返回-1表示失败
Others: 无
*************************************************/
int inject_remote_process_shellcode(pid_t pid, char *LibPath, char *FunctionName, long *FuncParameter, long NumParameter)
{
int iRet = -1;
struct pt_regs CurrentRegs, OriginalRegs; // CurrentRegs表示远程进程中当前的寄存器值,OriginalRegs存储注入前的寄存器值,方便恢复
void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr, *dlerror_addr; // 远程进程中需要调用函数的地址
void *RemoteMapMemoryAddr, *RemoteModuleAddr, *RemoteModuleFuncAddr; // RemoteMapMemoryAddr为远程进程空间中映射的内存基址,RemoteModuleAddr为远程注入的so模块加载基址,RemoteModuleFuncAddr为注入模块中需要调用的函数地址
long parameters[10];

uint8_t *dlopen_param1_ptr, *dlsym_param2_ptr, *saved_r0_pc_ptr, *inject_param_ptr, *remote_code_start_ptr, *local_code_start_ptr, *local_code_end_ptr;

extern uint32_t _dlopen_addr_s, _dlopen_param1_s, _dlopen_param2_s, _dlsym_addr_s, \
_dlsym_param2_s, _dlclose_addr_s, _inject_start_s, _inject_end_s, _inject_function_param_s, \
_saved_cpsr_s, _saved_r0_pc_s;

uint32_t code_length;

// Attach远程进程
if (ptrace_attach(pid) == -1)
return iRet;

// 获取远程进程的寄存器值
if (ptrace_getregs(pid, &CurrentRegs) == -1)
{
ptrace_detach(pid);
return iRet;
}

LOGD("ARM_r0:0x%lx, ARM_r1:0x%lx, ARM_r2:0x%lx, ARM_r3:0x%lx, ARM_r4:0x%lx, ARM_r5:0x%lx, ARM_r6:0x%lx, ARM_r7:0x%lx, ARM_r8:0x%lx, ARM_r9:0x%lx, ARM_r10:0x%lx, ARM_ip:0x%lx, ARM_sp:0x%lx, ARM_lr:0x%lx, ARM_pc:0x%lx", \
CurrentRegs.ARM_r0, CurrentRegs.ARM_r1, CurrentRegs.ARM_r2, CurrentRegs.ARM_r3, CurrentRegs.ARM_r4, CurrentRegs.ARM_r5, CurrentRegs.ARM_r6, CurrentRegs.ARM_r7, CurrentRegs.ARM_r8, CurrentRegs.ARM_r9, CurrentRegs.ARM_r10, CurrentRegs.ARM_ip, CurrentRegs.ARM_sp, CurrentRegs.ARM_lr, CurrentRegs.ARM_pc);

// 保存远程进程空间中当前的上下文寄存器环境
memcpy(&OriginalRegs, &CurrentRegs, sizeof(CurrentRegs));

// 获取mmap函数在远程进程中的地址
mmap_addr = GetRemoteFuncAddr(pid, libc_path, (void *)mmap);
LOGD("mmap RemoteFuncAddr:0x%lx", (long)mmap_addr);

// 设置mmap的参数
// void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offsize);
parameters[0] = 0; // 设置为NULL表示让系统自动选择分配内存的地址
parameters[1] = 0x4000; // 映射内存的大小
parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC; // 表示映射内存区域可读可写可执行
parameters[3] = MAP_ANONYMOUS | MAP_PRIVATE; // 建立匿名映射
parameters[4] = 0; // 若需要映射文件到内存中,则为文件的fd
parameters[5] = 0; //文件映射偏移量

// 调用远程进程的mmap函数,建立远程进程的内存映射
if (ptrace_call(pid, (long)mmap_addr, parameters, 6, &CurrentRegs) == -1)
{
LOGD("Call Remote mmap Func Failed");
ptrace_detach(pid);
return iRet;
}

// 获取mmap函数执行后的返回值,也就是内存映射的起始地址
RemoteMapMemoryAddr = (void *)ptrace_getret(&CurrentRegs);
LOGD("Remote Process Map Memory Addr:0x%lx", (long)RemoteMapMemoryAddr);

// 分别获取dlopen、dlsym、dlclose等函数的地址
dlopen_addr = GetRemoteFuncAddr(pid, linker_path, (void *)dlopen);
dlsym_addr = GetRemoteFuncAddr(pid, linker_path, (void *)dlsym);
dlclose_addr = GetRemoteFuncAddr(pid, linker_path, (void *)dlclose);
dlerror_addr = GetRemoteFuncAddr(pid, linker_path, (void *)dlerror);

LOGD("dlopen RemoteFuncAddr:0x%lx", (long)dlopen_addr);
LOGD("dlsym RemoteFuncAddr:0x%lx", (long)dlsym_addr);
LOGD("dlclose RemoteFuncAddr:0x%lx", (long)dlclose_addr);
LOGD("dlerror RemoteFuncAddr:0x%lx", (long)dlerror_addr);

remote_code_start_ptr = RemoteMapMemoryAddr + 0x3C00; // 远程进程中存放shellcode代码的起始地址
local_code_start_ptr = (uint8_t *)&_inject_start_s; // 本地进程中shellcode的起始地址
local_code_end_ptr = (uint8_t *)&_inject_end_s; // 本地进程中shellcode的结束地址

_dlopen_addr_s = (uint32_t)dlopen_addr;
_dlsym_addr_s = (uint32_t)dlsym_addr;
_dlclose_addr_s = (uint32_t)dlclose_addr;

LOGD("Inject Code Start:0x%x, end:0x%x", (int)local_code_start_ptr, (int)local_code_end_ptr);

// 计算shellcode中一些变量的存放起始地址
code_length = (uint32_t)&_inject_end_s - (uint32_t)&_inject_start_s;
dlopen_param1_ptr = local_code_start_ptr + code_length + 0x20;
dlsym_param2_ptr = dlopen_param1_ptr + MAX_PATH;
saved_r0_pc_ptr = dlsym_param2_ptr + MAX_PATH;
inject_param_ptr = saved_r0_pc_ptr + MAX_PATH;

// 写入dlopen的参数LibPath
strcpy( dlopen_param1_ptr, LibPath );
_dlopen_param1_s = REMOTE_ADDR( dlopen_param1_ptr, local_code_start_ptr, remote_code_start_ptr );

// 写入dlsym的第二个参数,需要调用的函数名称
strcpy( dlsym_param2_ptr, FunctionName );
_dlsym_param2_s = REMOTE_ADDR( dlsym_param2_ptr, local_code_start_ptr, remote_code_start_ptr );

//保存cpsr寄存器
_saved_cpsr_s = OriginalRegs.ARM_cpsr;

//保存r0-pc寄存器
memcpy( saved_r0_pc_ptr, &(OriginalRegs.ARM_r0), 16 * 4 ); // r0 ~ r15
_saved_r0_pc_s = REMOTE_ADDR( saved_r0_pc_ptr, local_code_start_ptr, remote_code_start_ptr );

memcpy( inject_param_ptr, FuncParameter, NumParameter );
_inject_function_param_s = REMOTE_ADDR( inject_param_ptr, local_code_start_ptr, remote_code_start_ptr );

ptrace_writedata( pid, remote_code_start_ptr, local_code_start_ptr, 0x400 );

memcpy( &CurrentRegs, &OriginalRegs, sizeof(CurrentRegs) );
CurrentRegs.ARM_sp = (long)remote_code_start_ptr;
CurrentRegs.ARM_pc = (long)remote_code_start_ptr;
ptrace_setregs( pid, &CurrentRegs );
ptrace_detach( pid );

return 0;
}

Zygote注入技术的实现