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