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

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[n].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[n].data.fd);
                if (ret < 1 && errno != 11)
    {
                    epoll_ctl(kdpfd, EPOLL_CTL_DEL, events[n].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;
}

 

posted @ 2015-02-10 12:11  cgj  阅读(11978)  评论(1编辑  收藏  举报