zoukankan      html  css  js  c++  java
  • 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 
    ");
        }
    
    
    
     /* 开启 socket 监听 */
        if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1)
        {
            perror("socket");
            exit(1);
        }
        else
        {
            printf("socket create success 
    ");
       }
    
        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
    ");
     }
        if (listen(listener, lisnum) == -1)
        {
            perror("listen");
            exit(1);
        }
        else
        {
            printf("listen succ
    ");
     }
    
     /* 创建 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
    ", listener);
            return -1;
        }
     else
        {
      printf("add socket to epoll succ
    ");
     }
        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
    ",
                                 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 
    ",
                                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
    ",getpid());
    printf("cmd %s
    ",argv[0]);
    #if 0
      printf("mmap: 0x%x
    ", mmap);
         printf("mprotect: 0x%x
    ", mprotect);
         printf("dlopen: 0x%x
    ", dlopen);
          printf("dlsym: 0x%x
    ", dlsym);
           printf("dlerror: 0x%x
    ", 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 
    ", 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!
    ");
            exit(1);
        }
        //把客户机的socket和客户机的socket地址结构联系起来
        if (bind(client_socket, (struct sockaddr*) &client_addr,
                sizeof(client_addr)))
        {
            printf("Client Bind Port Failed!
    ");
            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! 
    ");
            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!
    ", argv[1]);
            exit(1);
        }
    
        sleep(10);
        close(client_socket);
        return 0;
    }
  • 相关阅读:
    ios开发之--UITextField光标右移
    ios开发之--复制到剪切板
    Splash scroll_position 属性
    Splash plugins_enabled 属性
    Splash images_enabled 属性
    Splash resource_timeout 属性
    Splash js_enabled 属性
    Splash args 属性
    Splash 对象属性
    Splash Lua 脚本
  • 原文地址:https://www.cnblogs.com/jiayy/p/4282123.html
Copyright © 2011-2022 走看看