参考文档:
http://blog.chinaunix.net/uid-20361370-id-1962528.html
共享库注入–injectso实例
http://www.aiuxian.com/article/p-1295924.html
Android平台的 Ptrace, 注入, Hook 全攻略
最近学习ptrace,也想尝试写个hook的程序玩玩。最后阶段的bridge_code代码总是出错。ptrace之后则不能通过gdbserver64 调试被控进程(应该可以hook完修改代码之后退出,用gdbserver64 迅速以attach的方式连接调试,当然原程序里面加入sleep延迟方便操作,这是后话,当时没想到 )。
也不能在原程序里面自行调试,因为源程序没有资格修改自己的代码段,后来想到可以用gdb修改完代码,再继续运行,调试程序具体错在哪。
参考:Android平台的 Ptrace, 注入, Hook 全攻略
直接上代码
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <utils/Log.h>
#include <cutils/properties.h>
#include <cutils/sockets.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/statfs.h>
#include <sys/inotify.h>
#include <poll.h>
#include <cutils/log.h>
#include <sys/reg.h>
#include <sys/uio.h>
#include <elf.h>
#include <asm/ptrace.h>
#include <sys/ptrace.h>
#include <linux/ptrace.h>
#include <sys/mman.h>
int flag = 1;
int count = 0;
int test()
{
printf("Now in %s. Target is running: %d\n", __func__, count);
count++;
return 0;
}
int hookfun()
{
printf("Now in %s. Target is running: %d\n", __func__, count);
count++;
return 0;
}
int bridge_code()
{
asm("stp x6, x7, [sp,#-16]! \n\t"
"stp x4, x5, [sp,#-16]! \n\t"
"stp x2, x3, [sp,#-16]! \n\t"
"stp x0, x1, [sp,#-16]! \n\t"
"ldr x6, loc_tar \n\t"
"mov x7, x30 \n\t"
"stp x6, x7, [sp,#-16]! \n\t"
"blr x6 \n\t"
"ldp x6, x7, [sp],#16 \n\t"
"mov x30,x7 \n\t"
"ldp x0, x1, [sp],#16 \n\t"
"ldp x2, x3, [sp],#16 \n\t"
"ldp x4, x5, [sp],#16 \n\t"
"ldp x6, x7, [sp],#16 \n\t"
"mov x0, x0 \n\t" //exec code
"mov x0, x0 \n\t" //b back
"loc_tar: \t\n"
".word 0x0 \t\n" //hook_func addr
".word 0x0 \t\n"
);
return 0;
}
unsigned int build_jmp_code(long jump_from, long jump_to)
{
0 0 0 1 0 1 imm26
unsigned int b_inst_1 = 0x14000000;
unsigned int b_inst_2 = 0x17000000;
unsigned int ret;
int jump_dis;
// printf("jump_from = %lx\n", jump_from);
// printf("jump_to = %lx\n", jump_to);
jump_dis = jump_to - jump_from;
printf("jump_dis = %d\n", jump_dis);
jump_dis /= 4;
// printf("jump_dis = %d\n", jump_dis);
if (jump_dis > 0 ) {
ret = b_inst_1 | jump_dis;
} else {
ret = b_inst_2 | (jump_dis & 0xffffff);
}
return ret;
}
int print_gdb_change_info(long src_addr, long hookfun_addr, long bridge_addr)
{
unsigned int src_code;
unsigned int src_fill;
unsigned int back_fill;
int fill_opcount = 14;
int jback_opcount = 15;
int hook_opcount = 16;
src_fill = build_jmp_code(src_addr, bridge_addr);
back_fill = build_jmp_code(bridge_addr + jback_opcount * 4 , src_addr + 4);
src_code = *(unsigned int *)src_addr;
printf("/************************************/\n");
printf("gdb change code as following:\n");
printf("set *(int *)0x%lx = 0x%x\n", src_addr, src_fill);
printf("set *(int *)0x%lx = 0x%x\n", bridge_addr + fill_opcount * 4, src_code);
printf("set *(int *)0x%lx = 0x%x\n", bridge_addr + jback_opcount * 4, back_fill);
printf("set *(long *)0x%lx = 0x%lx\n", bridge_addr + hook_opcount * 4, hookfun_addr);
printf("/************************************/\n\n");
return 0;
}
int main()
{
pid_t pid;
pid = getpid();
long ret;
printf("Target pid = %d\n", pid);
printf("test = %p\n", &test);
printf("hookfun = %p\n", &hookfun);
printf("bridge_code = %p\n", &bridge_code);
print_gdb_change_info((long)&test, (long)&hookfun, (long)&bridge_code);
while(flag) {
test();
sleep(10);
}
return 0;
}
Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := bridge_code_test
LOCAL_SRC_FILES := bridge_code_test.c
LOCAL_SHARED_LIBRARIES:= libutils libcutils
#LOCAL_STATIC_LIBRARIES += libz libcutils libstdc++ libc
LOCAL_CFLAGS+= -DAARCH64 -O0
include $(BUILD_EXECUTABLE)
bridge_code 不言而喻,参考文档中是直接给出机器指令,我不想这么麻烦,可以直接写在hook_s的源程序里,然后复制到一段malloc的地址中,修改其中需要修改的地方,ptrace传递到目标程序。当然,在这里就是为了调试这段代码成功与否。无需复制,复制也没用,test源程序的四字节指令不靠gdb,咱也改不了。
hook思路很简单,test程序运行,就先执行hookfun,然后再执行test。
编译,不想折腾,编译非PIE的。直接gdb中运行。
gdb中如何修改已经打印出来了,方便自己,也方便需要的兄弟们调试!
如图,一目了然,test第一条指令跳转到正确的地址,bridge_code也修改正确。gdb,还是你最牛!
直接continue继续运行,结果正确。
太假了,一次就成功了?哪有,错误的道路不想走了,懒。直接给出错误:
"stp x6, x7, [sp,#-16]! \n\t"
"blr x6 \n\t"
"ldp x6, x7, [sp],#16 \n\t"
之前的代码“blr x6”前后的指令都没有。没有,为什么错了呢?
v7之前的abi(或是eabi) 只要求r0-r3无需保存,想当然v8也一样,害死人。
如下图,不说了,让我静静地多哭会。
感觉bridge_code怪怪的,再剽窃下编译器反汇编出来的代码,整个像正常函数的:
int bridge_code()
{
asm("stp x29, x30, [sp,#-80]! \n\t"
"mov x29, sp \n\t"
"stp x0, x1, [sp,#16] \n\t"
"stp x2, x3, [sp,#32] \n\t"
"stp x4, x5, [sp,#48] \n\t"
"stp x6, x7, [sp,#64] \n\t"
"ldr x5, loc_tar \n\t"
"blr x5 \n\t"
"ldp x0, x1, [sp,#16] \n\t"
"ldp x2, x3, [sp,#32] \n\t"
"ldp x4, x5, [sp,#48] \n\t"
"ldp x6, x7, [sp,#64] \n\t"
"ldp x29, x30, [sp],#80 \n\t"
"ret \n\t" //exec code
"ret \n\t" //b back
"loc_tar: \t\n"
".xword hookfun \t\n" //hook_func addr
);
return 0;
}
int print_gdb_change_info(long src_addr, long hookfun_addr, long bridge_addr)
{
unsigned int src_code;
unsigned int src_fill;
unsigned int back_fill;
int fill_opcount = 13;
int jback_opcount = 14;
int hook_opcount = 15;
src_fill = build_jmp_code(src_addr, bridge_addr);
back_fill = build_jmp_code(bridge_addr + jback_opcount * 4 , src_addr + 4);
src_code = *(unsigned int *)src_addr;
printf("/************************************/\n");
printf("gdb change code as following:\n");
printf("set *(int *)0x%lx = 0x%x\n", src_addr, src_fill);
printf("set *(int *)0x%lx = 0x%x\n", bridge_addr + fill_opcount * 4, src_code);
printf("set *(int *)0x%lx = 0x%x\n", bridge_addr + jback_opcount * 4, back_fill);
printf("set *(long *)0x%lx = 0x%lx\n", bridge_addr + hook_opcount * 4, hookfun_addr);
printf("/************************************/\n\n");
return 0;
}
int main()
{
pid_t pid;
pid = getpid();
long ret;
printf("Target pid = %d\n", pid);
printf("test = %p\n", &test);
printf("hookfun = %p\n", &hookfun);
printf("bridge_code = %p\n", &bridge_code);
bridge_code();
print_gdb_change_info((long)&test, (long)&hookfun, (long)&bridge_code);
while(flag) {
test();
sleep(10);
}
return 0;
}
改个bridge_code,还贴其他代码做什么。
一来汇编改了,修改偏移的数字13 14 15也要改改,main函数直接调用了bridge_code,没骗大家,不改之前,bridge_code也是一个正常的函数。
由于“.xword hookfun”的存在,只能编译为非PIE的了。
如下图,hookfun执行了一次。
既然需要用到指令计算,顺便看看:
unsigned int build_jmp_code(long jump_from, long jump_to)
{
0 0 0 1 0 1 imm26
unsigned int b_inst_1 = 0x14000000;
unsigned int b_inst_2 = 0x17000000;
unsigned int ret;
int jump_dis;
jump_dis = jump_to - jump_from;
printf("jump_dis = %d\n", jump_dis);
jump_dis /= 4;
if (jump_dis > 0 ) {
ret = b_inst_1 | jump_dis;
} else {
ret = b_inst_2 | (jump_dis & 0xffffff);
}
return ret;
}
左移右移还是不靠谱啊,还是除法简单。
注意正负128M的限制,ptrace mmap bridge_code空间的时候可不能随便申请,得靠原函数近点,目标函数blr跳转,随便整!