Pin截获socket系统调用初步分析
根据为知笔记中上传的Pin tool for tracing system calls,修改代码过滤出socket相关的系统调用,并进行初步的分析。主要有2点:
- 过滤socket相关的系统调用
- 分析得到的系统调用参数
过滤socket相关的系统调用
socket编程中与访问网络相关的主要关注socket()、accept()。然后在PinTool中过滤出这两个系统调用,对他们的参数进行初步的分析。下面先看一下这两个函数:
int socket(int domain,int type,int protocol);
socket函数说明
socket()用来建立一个新的socket,也就是向系统注册,通知系统建立一通信端口。
参数domain 指定使用何种的地址类型,完整的定义在/usr/include/bits/socket.h 内,底下是常见的协议:
PF_UNIX/PF_LOCAL/AF_UNIX/AF_LOCAL UNIX 进程通信协议
PF_INET/AF_INET Ipv4网络协议
PF_INET6/AF_INET6 Ipv6 网络协议
PF_IPX/AF_IPX IPX-Novell协议
PF_NETLINK/AF_NETLINK 核心用户接口装置
PF_X25/AF_X25 ITU-T X.25/ISO-8208 协议
PF_AX25/AF_AX25 业余无线AX.25协议
PF_ATMPVC/AF_ATMPVC 存取原始ATM PVCs
PF_APPLETALK/AF_APPLETALK appletalk(DDP)协议
PF_PACKET/AF_PACKET 初级封包接口
参数type有下列几种数值:
SOCK_STREAM 提供双向连续且可信赖的数据流,即TCP。支持OOB 机制,在所有数据传送前必须使用connect()来建立连线状态。
SOCK_DGRAM 使用不连续不可信赖的数据包连接
SOCK_SEQPACKET 提供连续可信赖的数据包连接
SOCK_RAW 提供原始网络协议存取
SOCK_RDM 提供可信赖的数据包连接
SOCK_PACKET 提供和网络驱动程序直接通信。
参数protocol用来指定socket所使用的传输协议编号,通常此参考不用管它,设为0即可。
返回值成功则返回socket处理代码,失败返回-1。
int accept(int s,struct sockaddr * addr,int * addrlen);
struct sockaddr
{
unsigned short int sa_family;
char sa_data[14];
};
accept函数说明
accept 系统调用是等待传入连接的阻塞调用。处理连接请求后,accept 将返回新的套接字描述符。将此新的套接字连接到客户端,使另外一个套接字 s 保持 LISTEN 状态,以接受进一步连接。
参数s是套接字描述符。参数addr所指的结构会被系统填入远程主机的地址数据,参数addrlen为scokaddr的结构长度。
此外sockaddr结构会因使用不同的socket domain而有不同结构定义,例如使用AF_INET domain,其socketaddr结构定义便为
struct socketaddr_in
{
unsigned short int sin_family;
uint16_t sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
struct in_addr
{
uint32_t s_addr;
};
sin_family 即为sa_family
sin_port 为使用的port编号
sin_addr.s_addr 为IP 地址
sin_zero 未使用。
也就是说判断程序的网络访问情况我们主要考虑:
socket(int domain,int type,int protocol)函数中的domain参数,是否为PF_INET/AF_INET
accept(int s,struct sockaddr * addr,int * addrlen)函数中addr参数内的IP地址
另一方面SystemCallTrace.cpp中SysBefore()函数用来打印系统调用号和参数。通过查看unistd_64.h中定义的各个系统调用的系统调用号,其中(socket,41),(accept,43),(listen,50),系统调用号41到50之间都是与socket相关的,所以我就在代码中做一个简单的过滤。代码如下:
if((num >= 41) && (num <= 50))
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);
修改makefile.rules,添加SystemCallTrace工具:
TEST_TOOL_ROOTS := MyPinTool SystemCallTrace
生成SystemCallTrace.so
root@kali:~/pin-2.14-71313-gcc.4.4.7-linux/source/tools/MyPinTool# make
从网上下载一个简单的TCP小程序将其放到根目录下,首先生成可执行文件tcp_server和tcp_client,开始做实验,代码见附件。
root@kali:~/pin-2.14-71313-gcc.4.4.7-linux# bash pin.sh -t ~/pin-2.14-71313-gcc.4.4.7-linux/source/tools/MyPinTool/obj-intel64/SystemCallTrace.so -- ~/tcp_server
现在tcp_server已经运行起来了,运行tcp_client向server发送几个字符,结束实验。打开strace.out,部分结果如下:
0x7f7099132a95: 41(0x2, 0x1, 0x0, 0x0, 0x7f70993dc300, 0x7f70ac435310)returns: 0x4
0x7f709913258e: 43(0x4, 0x0, 0x0, 0x0, 0xffffffff, 0x0)returns: 0x5
分析得到的系统调用参数
strace.out中
0x7f7099132a95: 41(0x2, 0x1, 0x0, 0x0, 0x7f70993dc300, 0x7f70ac435310)returns: 0x4
系统调用号: 41,即系统调用socket()
参数1: 0x2,在/usr/include/x86_64-linux-gnu/bits/socket.h中定义了socket使用的协议的值,0x2即PF_INET/AF_INET
参数2: 0x1,在/usr/include/x86_64-linux-gnu/bits/socket.h定义了socket类型,SOCK_STREAM = 1
参数3: 0x0
返回值: 0x4,返回socket句柄为4
下面我们看一下tcp_server.c的源代码:
//初始化Socket
if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
可以看出关于socket()函数的参数,分析结果与源程序是完全一致的。
我们在这一步获得了参数AF_INET
0x7f709913258e: 43(0x4, 0x0, 0x0, 0x0, 0xffffffff, 0x0)returns: 0x5
系统调用号: 43,即系统调用accept()
参数1: 0x0,即socket句柄为4,也就是socket()的返回值。
参数2: 0x0,这是一个(struct sockaddr*)类型的地址
参数3: 0x0,是参数2结构体的长度
返回值: 0x5,返回已连接socket句柄为5
下面我们看一下tcp_server.c的源代码:
//阻塞直到有客户端连接,不然多浪费CPU资源。
if( (connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1){
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
continue;
}
可以看出关于accept()函数的参数,分析结果与源程序是也完全一致的。
但是,客户端的IP地址在哪里,如何获得,这个。。。。。再想想