使用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