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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM