首页 > 其他 > 详细

使用Pin监控,解析connect()系统调用获取服务器端IP

时间:2015-03-25 19:27:49      阅读:253      评论:0      收藏:0      [点我收藏+]

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

使用Pin监控,解析connect()系统调用获取服务器端IP

原文:http://blog.csdn.net/hanchaoqi/article/details/44619687

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!