基于ARM的Ptrace
来源:互联网 发布:linux 查看阵列卡 编辑:程序博客网 时间:2024/06/05 19:23
转:http://hi.baidu.com/harry_lime/item/cce5161e4af86d4a71d5e8ff
前面提到过打算研究一下基于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代码:
#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, 代码如下:
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原先的代码:
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指令中获取立即数,反向计算出调用号。具体代码如下:
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的输入字符串,全部代码如下:
#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系统的常驻进程,达到干预其它程序的效果。
基于ARM的Ptrace (二)
上次在研究Ptrace for Android的时候漏了一个东西,如何hook并修改除了Syscall 以外的函数,今天顺便实现一下。
平台:Android 2.3.3
目标:利用Ptrace拦截进程的自定义函数并修改逻辑。
先看目标进程,代码相当简单:
#include <stdio.h>
int
flag = 1;
int
count = 0;
int
sub()
{
printf
(
"Sub call.\n"
);
return
1;
}
int
main()
{
while
(flag)
{
printf
(
"Sub return:%d\n"
, sub());
count++;
sleep(3);
}
return
0;
}
我们要做的是拦截自定义函数sub(),修改函数,跳过printf语句并把返回值改成2.
基本思路是利用Ptrace attach 以后找到函数代码段的入口点,修改相应的代码即可。如何找到函数入口点?静态看或者动态调都可以。我们代码简单,静态看就好了。
静态看的过程并不如想象的顺利,原因是IDA这货真心坑爹,解析Thumb和ARM的混合代码竟然会出错:
只好手动修改一下便于查看:
一目了然,0x84D0处开始返回指针压栈,我们只需要从0x84D2开始把代码改成如下就可以了:
MOVS R0, #2
POP {R3, PC}
因而得出trace的代码如下:
#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
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
tracePro(
int
pid)
{
int
len = 4;
char
insertcode[] =
"\x02\x20\x08\xBD"
;
putdata(pid, 0x84d2, insertcode, len);
}
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;
}
上传至模拟器调试,一次成功:
- 基于ARM的Ptrace
- 浅谈ARM上的Ptrace
- 浅谈ARM上的Ptrace
- 浅谈ARM上的PTRACE(2)
- Linux中基于ptrace的外挂程序设计
- ptrace基于地址调试
- ptrace基于行数调试
- 基于ARM的网络收音机
- 基于ARM的模拟器
- 基于ARM的模拟器
- 基于ARM的模拟器
- ptrace的使用
- ptrace的使用
- ptrace的小例子
- ptrace的使用举例
- ptrace的些许总结
- Ptrace
- ptrace
- 推送
- hbase shell 总结 (待续)
- android 退出整个应用
- memcached+Mysql(主从)
- Android---高效的加载大位图
- 基于ARM的Ptrace
- 栈数据结构实现代码
- java.lang.VerifyError:XXX <init> signature: ()V) Constructor must call super() or this()
- Deploying QT and OpenCV application on Mac OS X
- Html学习笔记
- 观察者模式
- JBoss 系列八十: jBPM 6 中使用 jbpm-console 创建执行 BPM 流程 - I
- 学习PLS_INTEGER,BINARY_INTEGER,INTEGER,NUMBER的概念及性能差异
- android在mac环境下的反编译和重新编译