參考文檔:
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 = ;
int count = ;
int test()
{
printf("Now in %s. Target is running: %d\n", __func__, count);
count++;
return ;
}
int hookfun()
{
printf("Now in %s. Target is running: %d\n", __func__, count);
count++;
return ;
}
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 ;
}
unsigned int build_jmp_code(long jump_from, long jump_to)
{
0 0 0 1 0 1 imm26
unsigned int b_inst_1 = ;
unsigned int b_inst_2 = ;
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 /= ;
// printf("jump_dis = %d\n", jump_dis);
if (jump_dis > ) {
ret = b_inst_1 | jump_dis;
} else {
ret = b_inst_2 | (jump_dis & );
}
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 = ;
int jback_opcount = ;
int hook_opcount = ;
src_fill = build_jmp_code(src_addr, bridge_addr);
back_fill = build_jmp_code(bridge_addr + jback_opcount * , src_addr + );
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 * , src_code);
printf("set *(int *)0x%lx = 0x%x\n", bridge_addr + jback_opcount * , back_fill);
printf("set *(long *)0x%lx = 0x%lx\n", bridge_addr + hook_opcount * , hookfun_addr);
printf("/************************************/\n\n");
return ;
}
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();
}
return ;
}
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 ;
}
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 = ;
int jback_opcount = ;
int hook_opcount = ;
src_fill = build_jmp_code(src_addr, bridge_addr);
back_fill = build_jmp_code(bridge_addr + jback_opcount * , src_addr + );
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 * , src_code);
printf("set *(int *)0x%lx = 0x%x\n", bridge_addr + jback_opcount * , back_fill);
printf("set *(long *)0x%lx = 0x%lx\n", bridge_addr + hook_opcount * , hookfun_addr);
printf("/************************************/\n\n");
return ;
}
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();
}
return ;
}
改個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 = ;
unsigned int b_inst_2 = ;
unsigned int ret;
int jump_dis;
jump_dis = jump_to - jump_from;
printf("jump_dis = %d\n", jump_dis);
jump_dis /= ;
if (jump_dis > ) {
ret = b_inst_1 | jump_dis;
} else {
ret = b_inst_2 | (jump_dis & );
}
return ret;
}
左移右移還是不靠譜啊,還是除法簡單。
注意正負128M的限制,ptrace mmap bridge_code空間的時候可不能随便申請,得靠原函數近點,目标函數blr跳轉,随便整!