前面提到过打算研究一下基于ARM的Ptrace,并在Mobile上实现Hook. 今天程序调通了,记录如下.
平台:Android 2.3.3, 具体Linux Kernel和ARM的版本大家可以自己去查
目标:实现两个程序target和trace. target循环用printf打印语句,trace追踪target的系统调用并替换target的打印语句
在写程序之前查资料的过程中发现一个奇怪的事情,对于ARM ptrace研究实践的文章非常少,仅有的几篇也基本上都是胡言乱语,互相抄袭,根本无法测试通过。所以还是不要希望坐享其成,老老实实根据理论完成实践。
好,先从target开始. target还是相当简单的,写代码,下载ndk,交叉编译,上传到Android,运行,搞定. 以下是target代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #include <stdio.h> int flag = 1; int count = 0; int main() { char * str = "abcdef" ; while (flag) { printf ( "Target is running:%d\n" , count); count++; sleep(3); } return 0; } |
在Android上的运行情况:
rbserver@rbserver:~/ndk_test$ adb shell
# /data/harry/target
Target is running:0
Target is running:1
Target is running:2
Target is running:3
接下来是trace, 这个花了点时间, 主要是网上误导的资料太多, 轻信于人走了不少弯路。
第一步是要能成功attach并能捕获syscall, 代码如下:
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 | 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; } while (1) { wait(&status); if (WIFEXITED(status)) { break ; } tracePro(traced_process); ptrace(PTRACE_SYSCALL, traced_process, NULL, NULL); } ptrace(PTRACE_DETACH, traced_process, NULL, NULL); return 0; } |
这一部分和x86的代码几乎没有任何区别,因为还没有涉及到寄存器,中断这些架构上的概念。
下面要解决的是如何获取syscall的调用号。这一点ARM和x86有很大的不同。
先看x86原先的代码:
1 2 3 4 5 | orig_eax = ptrace(PTRACE_PEEKUSER, pid, 4 * ORIG_EAX, NULL); if (orig_eax == SYS_write) { ... } |
这样做的原因是在x86架构上,Linux所有的系统调用都是通过软中断 int 0x80来实现的,而系统调用号是存在寄存器EAX中的. 所以如果想获取系统调用号,只需要获取ORIG_EAX的值就可以了。
而在ARM架构上呢,所有的系统调用都是通过SWI来实现的. 虽然也是软中断,但方式不同,因为在ARM 架构中有两个SWI指令,分别针对EABI和OABI (关于EABI和OABI 大家可以搜索相关资料,它们是Linux针对ARM架构的两种系统调用指令):
[EABI]
机器码:1110 1111 0000 0000 -- SWI 0
具体的调用号存放在寄存器r7中.
[OABI]
机器码:1101 1111 vvvv vvvv -- SWI immed_8
调用号进行转换以后得到指令中的立即数。立即数=调用号 | 0x900000
既然需要兼容两种方式的调用,我们在代码上就要分开处理。首先要获取SWI指令判断是EABI还是OABI,如果是EABI,可从r7中获取调用号。如果是OABI,则从SWI指令中获取立即数,反向计算出调用号。具体代码如下:
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 | long getSysCallNo( int pid) { long scno = 0; struct pt_regs regs; ptrace(PTRACE_GETREGS, pid, NULL, ®s); scno = ptrace(PTRACE_PEEKTEXT, pid, ( void *)(regs.ARM_pc - 4), NULL); if (scno == 0) return 0; /* Handle the EABI syscall convention. We do not bother converting structures between the two ABIs, but basic functionality should work even if strace and the traced program have different ABIs. */ if (scno == 0xef000000) { scno = regs.ARM_r7; } else { if ((scno & 0x0ff00000) != 0x0f900000) { return -1; } /* * Fixup the syscall number */ scno &= 0x000fffff; } return scno; } |
完成了这一步以后我们就可以利用trace打印出target所有的系统调用了。运行结果如下:
从结果可以看出,target每调用一次printf,会引发两次__NR_write调用(调用号为4)。
接下来我们也照葫芦画瓢,翻转__NR_write的输入字符串,全部代码如下:
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 | #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 reverse( char *str) { int i, j; char temp; for (i = 0, j = strlen (str) - 2; i <= j; ++i, --j) { temp = str[i]; str[i] = str[j]; str[j] = temp; } } 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); } } long getSysCallNo( int pid, struct pt_regs *regs) { long scno = 0; ptrace(PTRACE_GETREGS, pid, NULL, regs); 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; } /* * Fixup the syscall number */ scno &= 0x000fffff; } return scno; } void tracePro( int pid) { long scno=0; long regV=0; struct pt_regs regs; char * str; scno = getSysCallNo(pid, ®s); if (scno == __NR_write) { str = ( char *) calloc (1, (regs.ARM_r2+1) * sizeof ( char )); getdata(pid, regs.ARM_r1, str, regs.ARM_r2); reverse(str); putdata(pid, regs.ARM_r1, str, regs.ARM_r2); printf ( "Reverse str.\n" ); } } 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; } while (1) { wait(&status); if (WIFEXITED(status)) { break ; } tracePro(traced_process); ptrace(PTRACE_SYSCALL, traced_process, NULL, NULL); } ptrace(PTRACE_DETACH, traced_process, NULL, NULL); return 0; } |
测试通过,输出如下:
好了,到目前为止我们在Linux+ARM的架构上实现了一个完整的ptrace hook应用,下一步考虑进行实战,hook系统的常驻进程,达到干预其它程序的效果。
|Archiver|手机版|小黑屋|软路由 ( 渝ICP备15001194号-1|渝公网安备 50011602500124号 )
GMT+8, 2024-5-10 14:38 , Processed in 0.105224 second(s), 5 queries , Gzip On, Redis On.
Powered by Discuz! X3.5 Licensed
© 2001-2023 Discuz! Team.