APP进程注入技术是进行功能劫持的基础,要获得其它进程信息必须预先注入进程。流程注入的实质是暂停原始流程,将控制权移交给定制操作,并在自定义操作执行结束后原流程再继续执行。实际上,因为这段代码结构比较复杂,所以只有一小段代码被注入到so文件中,这是真正的定制代码放。
在执行中,注入代码负责装入so文件。对于Android系统,进程注入主要有两种思路:首先,通过修改动态链接库的内容,比如增加动态链接库中的section信息,在APK包中增加一个动态链接库,在执行动态链接库时,就可以实现注入。将新的动态链接库添加到APK包中,通过修改Smali文件,向Smali文件添加代码System.loadLibrary调用动态链接库。另一种想法是结合ptrace函数进行调试时也经常使用,调用dlopen\dlsym等函数来加载动态链接库并执行指定地址的代码。以下是主要程序:
(1)获取待注入进程的进程号 pid,附加(attach)到该目标进程;通过ps 命 令 集 合 找 到 当 前 进 程 名 对 应 的 进 程 号 pid , 接 着 调 用ptrace(PTRACE_ATTACH, pid, NULL, 0)完成对目标进程附加。
(2)保存寄存器环境,直接调用ptrace(PTRACE_GETREGS,pid,NULL)。
(3)获得mmap、dlopen、dlsym函数的绝对地址。dlopen函数以指定的模式打开指定的动态连接库文件;dlsym根据动态链接库的符号和句柄,返回相应的函数或变量地址。mmap函数是在"/system/lib/libc.so"模块中的"/system/bin/linker"模块中的dlopen和dlsym函数。读入"/proc/pid/maps"可以获得目标进程的加载基础地址。为了获得与模块相对应的mmap、dlopen、dlsym函数的实际地址,在远程进程内存空间中获取mmap、dlopen、dlsym函数的实际地址,通过计算mmap、dlopen、dlsym函数相对于模块的地址,然后用此地址偏移加上与目标进程内存空间中对应函数的基址,此地址是目标进程内存空间中对应函数的绝对地址。
(4)调用mmap来分配内存空间,分配一个临时存储器,存储需要注入的动态链接库。构建mmap参数,如果参数小于4个,直接为R0-R3分配参数;如果参数大于4个,则需要将参数分配到栈中,调用ptrace(PTRACE_SETREGS,pid,NULL,regs)设置PC寄存器作为mmap函数的绝对地址,设置LR寄存器为0。将LR寄存器为0设置为目标进程完成dlopen功能之后,目标进程发生异常,使当前进程重新获得控制。最后获得所分配内存空间的初始地址,然后把一个动态链接库写到内存中。
(5)调用dlopen函数,为dlopen函数设置相关的参数,在参数设置好之后,调用ptrace(PTRACE_SETREGS,pid,NULL,regs)将PC寄存器的绝对地址设置为dlopen函数,设置LR寄存器为0。ptrace(PTRACE_CONT,pid,NULL,0)继续运行目标进程。
(6)按与第五步相同的方法调用dlsym函数,获得要在注入模块中执行的函数地址。
(7)根据所获得的函数地址,按第五步相同的方式调用注入的函数。
(8)以ptrace(PTRACE_DETACH,pid,NULL,0)恢复目标进程堆栈内容和寄存器值并分离附加。