使用Pin監控,解析connect()系統調用獲取服務器端IP

使用Pin監控,解析connect()系統調用獲取服務器端IP

connect()函數:

#include <sys/types.h>    
#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

connect()函數通常用於客戶端建立tcp連接。
參數列表:
sockfd:指定數據發送的套接字。
serv_addr:指定數據發送的目的地,包括套接字sockfd想要連接的主機地址和端口號。
addrlen:指定serv_addr結構體的長度。
返回值:
成功則返回0,失敗返回-1,錯誤原因存於errno中。

既然serv_addr中存放了服務器端的IP地址,所以我們要從客戶端獲取服務器端的IP地址,只要得到connect()函數的addr參數就可以了。

struct sockaddr *addr:

struct sockaddr {
    unsigned  short  sa_family;     /* address family, AF_xxx */
    char  sa_data[14];              /* 14 bytes of protocol address */
};

sa_family是地址家族,一般都是“AF_xxx”的形式。好像通常大多用的是都是AF_INET。
sa_data是14字節協議地址。
此數據結構用做bind、connect、recvfrom、sendto等函數的參數,指明地址信息。

但一般編程中並不直接針對此數據結構操作,而是使用另一個與sockaddr等價的數據結構

struct  sockaddr_in {
    short  int  sin_family;               /* Address family */
    unsigned  short  int  sin_port;       /* Port number */
    struct  in_addr  sin_addr;            /* Internet address */
    unsigned  char  sin_zero[8];          /* Same size as struct sockaddr */
};

struct  in_addr {
    unsigned  long  s_addr;
};

sockaddr_in和sockaddr是並列的結構,指向sockaddr_in的結構體的指針也可以指向sockadd的結構體,並代替它。也就是說,我們可以使用sockaddr_in建立所需要的信息,在最後用進行類型轉換就可以了。

我們要的IP地址就在s_addr中。


在Pin中獲取IP地址

查看unistd_64.h得到connect()的系統調用號爲42,所以使用Pin監控Client端的運行,並篩選出42號系統調用。結果如下:

0x7ff9a794561e: 42(0x4, 0x7fff1466d780, 0x10, 0x0, 0x0, 0x4)returns: 0x0

這個就是我們要得到的connect()系統調用的相關信息。顯然第二個參數是serv_addr結構體的內存地址,所以我們現在要做的是從這塊內存中提取出IP地址,即serv_addr的第三個成員變量值sin_addr。sin_addr也是一個in_addr類型的結構體,所以最終要拿到的數據還是unsigned long s_addr

獲得s_addr有兩種方法:

  • 直接用內存操作
    這是我最開始想到的方法,主要思路是既然有了內存地址,那麼就可以根據結構體的定義來計算成員變量的偏移量,然後直接將內存數據取出。

    這種方法不但麻煩,而且有一個致命的問題,那就是數據對齊。許多計算機系統對基本數據類型合法地址做出了一些限制,要求某種類型對象的地址必須是某個K(2、4或8)值的倍數。不一樣的操作系統(比如Linux和Windows)的強制對齊規則是不一樣的,對於結構體類型我們直接計算變量的偏移很有可能會得到錯誤的值。關於數據對齊的知識在《深入理解計算機系統》一書中有詳細的介紹。

  • 使用指針訪問
    既然知道了結構體的內存地址,那我可以在程序中聲明一個sockaddr_in結構體變量,然後將內存地址進行強制類型轉換賦給該變量,這樣就可以通過直接訪問該結構體變量來訪問成員變量,而不需要進行直接的內存操作了。
    最後要使用inet_ntoa()函數將in_addr類型的IP地址轉換爲點分十進制,並打印出來。
    代碼如下:

if(num == 42)
    {
        fprintf(trace,"0x%lx: %ld(0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx)",
             (unsigned long)ip,
             (long)num,
             (unsigned long)arg0,
             (unsigned long)arg1,
             (unsigned long)arg2,
             (unsigned long)arg3,
             (unsigned long)arg4,
             (unsigned long)arg5);

        struct sockaddr_in addr;
        addr =  *((sockaddr_in *)arg1);
        fprintf(trace,"\nIP:%s\n",inet_ntoa(addr.sin_addr));
    }
}

結果如下:

0x7fca80ee061e: 42(0x4, 0x7fffeb46b2c0, 0x10, 0x0, 0x0, 0x4)
IP:127.0.0.1
returns: 0x0
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章