首先建立一个ELF可执行文件target,使用ndk-build进行编译
需要先在任意地方建立一个jni,然后在jni目录下建立Android.mk,Application.mk,target.c
Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := target
LOCAL_SRC_FILES := target.c
include $(BUILD_EXECUTABLE)
Application.mk(ABI可以添加其他类型如x86)
APP_ABI := armeabi-v7a
target.c:
#include
int count = 0;
void sevenWeapons(int number)
{
char* str = "Hello,lzh!";
printf("%s %d\n",str,number);
}
int main()
{
while(1)
{
sevenWeapons(count);
count++;
sleep(1);
}
return 0;
}
编写完成后,使用命令行进入jni目录,执行ndk-build
然后在jni的上一个目录就能看到一个libs目录,进入找到target,将其push到安卓中,赋予其权限,然后执行,就能看到如下情况
Hello,lzh! 0
Hello,lzh! 1
...
首先还是建立相关文件jni目录
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hook1
LOCAL_SRC_FILES := hook1.c
include $(BUILD_EXECUTABLE)
Application.mk
APP_ABI := armeabi-v7a
hook1.c
long getSysCallNo(int pid, struct pt_regs *regs)
{
long scno = 0;
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;
}
scno &= 0x000fffff;
}
return scno;
}
void hookSysCallBefore(pid_t pid)
{
struct pt_regs regs;
int sysCallNo = 0;
ptrace(PTRACE_GETREGS, pid, NULL, ®s);
sysCallNo = getSysCallNo(pid, ®s);
printf("Before SysCallNo = %d\n",sysCallNo);
if(sysCallNo == __NR_write)
{
printf("__NR_write: %ld %p %ld\n",regs.ARM_r0,(void*)regs.ARM_r1,regs.ARM_r2);
}
}
void hookSysCallAfter(pid_t pid)
{
struct pt_regs regs;
int sysCallNo = 0;
ptrace(PTRACE_GETREGS, pid, NULL, ®s);
sysCallNo = getSysCallNo(pid, ®s);
printf("After SysCallNo = %d\n",sysCallNo);
if(sysCallNo == __NR_write)
{
printf("__NR_write return: %ld\n",regs.ARM_r0);
}
printf("\n");
}
int main(int argc, char *argv[])
{
if(argc != 2) {
printf("Usage: %s \n" , argv[0]);
return 1;
}
pid_t pid;
int status;
pid = atoi(argv[1]);
if(0 != ptrace(PTRACE_ATTACH, pid, NULL, NULL))
{
printf("Trace process failed:%d.\n", errno);
return 1;
}
ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
while(1)
{
wait(&status);
hookSysCallBefore(pid);
ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
wait(&status);
hookSysCallAfter(pid);
ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
}
ptrace(PTRACE_DETACH, pid, NULL, NULL);
return 0;
}
getSysCallNo:
作用:获取system call编号
//获取system call编号
long getSysCallNo(int pid, struct pt_regs *regs)
{
long scno = 0;
//获取系统调用的SWI指令,这里一共有两个指令EABI,OABI,分别对应两个机器码
scno = ptrace(PTRACE_PEEKTEXT, pid, (void *)(regs->ARM_pc - 4), NULL);
if(scno == 0)
return 0;
//为EABI的时候,从r7中直接获取调用号
if (scno == 0xef000000) {
scno = regs->ARM_r7;
} else {
//为OABI的时候通过公式立即数(scno)=调用号 | 0x900000,先获取立即数,在计算出调用号
if ((scno & 0x0ff00000) != 0x0f900000) {
return -1;
}
scno &= 0x000fffff;
}
return scno;
}
获取到system call的编号之后,我们可以进而获取到各个参数的值,我们可以看到第一个SysCallNo是162,也就是sleep函数。第二个SysCallNo是4,也就是write函数,因为printf本质就是调用write这个系统调用来完成的。
//hook system call前
void hookSysCallBefore(pid_t pid)
{
struct pt_regs regs;
int sysCallNo = 0;
ptrace(PTRACE_GETREGS, pid, NULL, ®s);
sysCallNo = getSysCallNo(pid, ®s);
printf("Before SysCallNo = %d\n",sysCallNo);
if(sysCallNo == __NR_write)
{
printf("__NR_write: %ld %p %ld\n",regs.ARM_r0,(void*)regs.ARM_r1,regs.ARM_r2);
}
}
//hook system call后
void hookSysCallAfter(pid_t pid)
{
struct pt_regs regs;
int sysCallNo = 0;
ptrace(PTRACE_GETREGS, pid, NULL, ®s);
sysCallNo = getSysCallNo(pid, ®s);
printf("After SysCallNo = %d\n",sysCallNo);
if(sysCallNo == __NR_write)
{
printf("__NR_write return: %ld\n",regs.ARM_r0);
}
printf("\n");
}
编写完成后,还是执行ndk-build,得到文件push进安卓手机
然后执行重新执行target文件,再使用下面命令获取target的pid
adb shell "ps |grep "target""
./hook1 19140
还是hook之前的代码,代码如下,想了解原理就直接看注释吧
Applicaton.mk
#APP_OPTIM := release
APP_PLATFORM := android-15
APP_ABI := armeabi-v7a
NDK_TOOLCHAIN_VERSION=4.9
APP_PIE := false
Android.mk:
LOCAL_PATH := $(call my-dir)
APP_CFLAGS := -std=c++11
include $(CLEAR_VARS)
LOCAL_MODULE := hook2
LOCAL_SRC_FILES := hook2.c
include $(BUILD_EXECUTABLE)
hook2.c
//获取system call编号
long getSysCallNo(int pid, struct pt_regs *regs)
{
long scno = 0;
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;
}
scno &= 0x000fffff;
}
return scno;
}
void getdata(pid_t child, 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;//在arm32下long类型长度为4
laddr = str;
//先处理能除的部分
while(i < j) {
data.val = ptrace(PTRACE_PEEKDATA,
child, addr + i * 4,
NULL);//注入
memcpy(laddr, data.chars, long_size);//将data.chars复制到laddr处
++i;
laddr += long_size;//增加一个long的长度
}//类似于链表
j = len % long_size;
//在处理剩下的
if(j != 0) {
data.val = ptrace(PTRACE_PEEKDATA,
child, addr + i * 4,
NULL);
memcpy(laddr, data.chars, j);
}
str[len] = '\0';
}
//原理和get差不多,使用ptrace注入的方式打印
void putdata(pid_t child, 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, child,
addr + i * 4, data.val);
++i;
laddr += long_size;
}
j = len % long_size;
if(j != 0) {
memcpy(data.chars, laddr, j);
ptrace(PTRACE_POKEDATA, child,
addr + i * 4, data.val);
}
}
void modifyString(pid_t pid, long addr, long strlen)
{ //注意这里的strlen是地址的长度!
char* str;
str = (char *)calloc((strlen+1) * sizeof(char), 1);
getdata(pid, addr, str, strlen);
//reverse(str);
str[0]='l';//将字符串的第一个字符改成'l'
printf("Hook -------\n");
printf("%s\n",str);
putdata(pid, addr, str, strlen);
}
void hookSysCallBefore(pid_t pid)
{
struct pt_regs regs;
int sysCallNo = 0;
ptrace(PTRACE_GETREGS, pid, NULL, ®s);
sysCallNo = getSysCallNo(pid, ®s);
//printf("Before SysCallNo = %d\n",sysCallNo);
if(sysCallNo == __NR_write)
{
printf("__NR_write: %ld %p %ld\n",regs.ARM_r0,(void*)regs.ARM_r1,regs.ARM_r2);
modifyString(pid, regs.ARM_r1, regs.ARM_r2);
}
}
void hookSysCallAfter(pid_t pid)
{
struct pt_regs regs;
int sysCallNo = 0;
ptrace(PTRACE_GETREGS, pid, NULL, ®s);
sysCallNo = getSysCallNo(pid, ®s);
printf("After SysCallNo = %d\n",sysCallNo);
if(sysCallNo == __NR_write)
{
printf("__NR_write return: %ld\n",regs.ARM_r0);
}
printf("\n");
}
int main(int argc, char *argv[])
{
if(argc != 2) {
printf("Usage: %s \n" , argv[0]);
return 1;
}
pid_t pid;
int status,errno;
pid = atoi(argv[1]);
if(0 != ptrace(PTRACE_ATTACH, pid, NULL, NULL))
{
printf("Trace process failed:%d.\n", errno);
return 1;
}
ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
while(1)
{
wait(&status);
hookSysCallBefore(pid);
ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
//wait(&status);
//hookSysCallAfter(pid);
//ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
}
ptrace(PTRACE_DETACH, pid, NULL, NULL);
return 0;
}
然后运行,效果如下: