您的位置:首页 > 移动开发 > Android开发

android hook 框架 ADBI 简介、编译、运行

2015-02-10 12:11 218 查看
Android so注入-libinject2 简介、编译、运行

Android so注入-libinject2 如何实现so注入

Android so注入-Libinject 如何实现so注入

Android so注入挂钩-Adbi 框架简介、编译、运行

Android so注入挂钩-Adbi 框架如何实现so注入

Android so注入挂钩-Adbi 框架如何实现so函数挂钩

Android so注入挂钩-Adbi 框架如何实现dalvik函数挂钩

Android dalvik挂钩-Xposed框架如何实现注入

Android dalvik挂钩-Xposed框架如何实现挂钩

简介

adbi 是一个android平台(arm 32 )的so注入+挂钩框架,源码开放在github上 : ADBI 项目

从hook技术的分类来说,其属于应用层so注入+inline 挂钩, 这种方式的套路是:基于linux系统的ptrace机制,attach一个目标进程,注入一个动态链接库进入目标进程的地址空间,然后用so里边的函数地址替换目标进程地址空间里原有的函数地址(老的函数地址一般也需要保存起来)。

源码目录



hijack: 注入功能,用于注入一个so到目标进程并执行初始化函数,编译为一个可执行程序

libbase: 挂钩框架,用于hook指定so内部的指定函数,并提供unhook函数,编译为一个静态库

example: 一个hook例子,使用libbase,将一个自定义的my_epoll_wait函数编译成一个动态库libexample.so,由 hijack 注入目标进程,并用libexample.so里的my_epoll_wait 挂钩目标进程的 libc.so 库的函数 epoll_wait

编译运行

默认从github clone下面的编译文件 Android.mk 里目标体系架构是 arm ,所以要运行测试例子需要准备一个 Arm 模拟器,另外, README.md 给出的编译方式是 linux 系统的shell脚本,在Windows下不能直接使用,需要改成相应的批处理脚本。这里我直接手动进入相关目录执行ndk-build.

1. 下载ndk, 资源站 下载 ndk

2. 解压到某一目录,将ndk的路径设置进 PATH 环境变量

3. git clone adbi 项目

4. 进入hijack/jni, instruments/base/jni, instruments/base/example/jni 分别执行 ndk-build





5. 启动模拟器

我使用android studio avd 管理器创建了一个模拟器,参数如下



使用android studio 创建一个android项目(默认即可),启动模拟器

6. adb 传输文件到模拟器

adb push libexample.so /data/local/tmp/
adb push hijack /data/local/tmp
adb push target /data/local/tmp
adb push testclient /data/local/tmp


其中,target和testclient是从网上拷贝的 epoll 服务端和客户端代码(本文最后会给出代码),执行情况如下:

第一个shell 执行 adb logcat 用于查看日志,在本次测试里,目标进程是1490,从logcat 可以看出执行了3次挂钩函数

第二个shell执行 ./hijack -p 1490 -l /data/local/tmp/libexample.so -d , 用于注入 libexample.so 到进程 1490 并执行初始化函数(执行挂钩),执行完后,查看1490的maps文件,可以看到,libexample.so已经被注入



第三个shell多次执行 ./testclient 127.0.0.1 , 向服务端发送tcp请求,建立连接,触发服务端的epoll_wait返回

第四个shell执行 /data/local/tmp/target, 即启动服务端,它用epoll_wait监听5000端口,如果有一个请求到来,就分配一个socket去服务



target.c 的代码

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <dlfcn.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <signal.h>
#include <sys/ptrace.h>
#define MAXBUF 1024
#define MAXEPOLLSIZE 10000

/*
setnonblocking - 设置句柄为非阻塞方式
*/
int setnonblocking(int sockfd)
{
if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK) == -1)
{
return -1;
}
return 0;
}

/*
handle_message - 处理每个 socket 上的消息收发
*/
int handle_message(int new_fd)
{
char buf[MAXBUF + 1];
int len;

/* 开始处理每个新连接上的数据收发 */
bzero(buf, MAXBUF + 1);

/* 接收客户端的消息 */
len = recv(new_fd, buf, MAXBUF, 0);
if (len > 0)
{
printf("%d :'%s',共%d个字节的数据/n",new_fd, buf, len);
}
else
{
if (len < 0)
printf("消息接收失败!错误代码是%d,错误信息是'%s'/n", errno, strerror(errno));
close(new_fd);
return -1;
}
/* 处理每个新连接上的数据收发结束 */
return len;
}

void  sig_worker()
{
int listener, new_fd, kdpfd, nfds, n, ret, curfds;
socklen_t len;
struct sockaddr_in my_addr, their_addr;
unsigned int myport, lisnum;
struct epoll_event ev;
struct epoll_event events[MAXEPOLLSIZE];
struct rlimit rt;
myport = 5000;
lisnum = 2;

/* 设置每个进程允许打开的最大文件数 */
rt.rlim_max = rt.rlim_cur = MAXEPOLLSIZE;
if (setrlimit(RLIMIT_NOFILE, &rt) == -1)
{
perror("setrlimit");
exit(1);
}
else
{
printf("setrlimit success \n");
}

/* 开启 socket 监听 */
if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket");
exit(1);
}
else
{
printf("socket create success \n");
}

setnonblocking(listener);

bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = PF_INET;
my_addr.sin_port = htons(myport);
my_addr.sin_addr.s_addr = INADDR_ANY;

if (bind(listener, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1)
{
perror("bind");
exit(1);
}
else
{
printf("ip bind succ\n");
}
if (listen(listener, lisnum) == -1)
{
perror("listen");
exit(1);
}
else
{
printf("listen succ\n");
}

/* 创建 epoll 句柄,把监听 socket 加入到 epoll 集合里 */
kdpfd = epoll_create(MAXEPOLLSIZE);
len = sizeof(struct sockaddr_in);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = listener;
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, listener, &ev) < 0)
{
fprintf(stderr, "epoll set insertion error: fd=%d\n", listener);
return -1;
}
else
{
printf("add socket to epoll succ\n");
}
curfds = 1;
while (1)
{
/* 等待有事件发生 */
nfds = epoll_wait(kdpfd, events, curfds, -1);
if (nfds == -1)
{
if(errno==EINTR) continue;
perror("epoll_wait");
break;
}
/* 处理所有事件 */
for (n = 0; n < nfds; ++n)
{
if (events
.data.fd == listener)
{
new_fd = accept(listener, (struct sockaddr *) &their_addr,&len);
if (new_fd < 0)
{
perror("accept");
continue;
}
else
{
printf("request from %d:%d, giving socket:%d\n",
inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), new_fd);
}
setnonblocking(new_fd);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = new_fd;
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, new_fd, &ev) < 0)
{
fprintf(stderr, "giving %d to epoll %s fail \n",
new_fd, strerror(errno));
return;
}
curfds++;
}
else
{
ret = handle_message(events
.data.fd);
if (ret < 1 && errno != 11)
{
epoll_ctl(kdpfd, EPOLL_CTL_DEL, events
.data.fd,&ev);
curfds--;
}
}
}
}
close(listener);
}
int main(int argc, char **argv)
{
printf("my pid is %d\n",getpid());
printf("cmd %s\n",argv[0]);
#if 0
printf("mmap: 0x%x\n", mmap);
printf("mprotect: 0x%x\n", mprotect);
printf("dlopen: 0x%x\n", dlopen);
printf("dlsym: 0x%x\n", dlsym);
printf("dlerror: 0x%x\n", dlerror);
#endif
sig_worker();
return 0;
}


testclient.c 的代码

#include <netinet/in.h>    // for sockaddr_in
#include <sys/types.h>    // for socket
#include <sys/socket.h>    // for socket
#include <stdio.h>        // for printf
#include <stdlib.h>        // for exit
#include <string.h>        // for bzero
/*
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
*/

#define SERVER_PORT 5000
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512

int main(int argc, char **argv)
{
if (argc != 2)
{
printf("Please input the IP address of the server \n", argv[0]);
exit(1);
}

//设置一个socket地址结构client_addr,代表客户机internet地址, 端口
struct sockaddr_in client_addr;
bzero(&client_addr, sizeof(client_addr)); //把一段内存区的内容全部设置为0
client_addr.sin_family = AF_INET; //internet协议族
client_addr.sin_addr.s_addr = htons(INADDR_ANY); //INADDR_ANY表示自动获取本机地址
client_addr.sin_port = htons(0); //0表示让系统自动分配一个空闲端口
//创建用于internet的流协议(TCP)socket,用client_socket代表客户机socket
int client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket < 0)
{
printf("Create Socket Failed!\n");
exit(1);
}
//把客户机的socket和客户机的socket地址结构联系起来
if (bind(client_socket, (struct sockaddr*) &client_addr,
sizeof(client_addr)))
{
printf("Client Bind Port Failed!\n");
exit(1);
}

//设置一个socket地址结构server_addr,代表服务器的internet地址, 端口
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
if (inet_aton(argv[1], &server_addr.sin_addr) == 0) //服务器的IP地址来自程序的参数
{
printf("Server IP Address Error! \n");
exit(1);
}

server_addr.sin_port = htons(SERVER_PORT);
socklen_t server_addr_length = sizeof(server_addr);
// 向服务器发起连接,连接成功后client_socket代表了客户机和服务器的一个socket连接
if (connect(client_socket, (struct sockaddr*) &server_addr,
server_addr_length) < 0)
{
printf("Can Not Connect To %s!\n", argv[1]);
exit(1);
}

sleep(10);
close(client_socket);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: