0x00
linux socket网络编程大家肯定不会陌生,然而linux也可以使用socket这套接口来实现进程间通信,大概的步骤和网络通信差不多,也是可以基于C/S的,服务端创建套接字,然后绑定、侦听,客户端创建套接字,链接,然后就可以通信了。这篇文章主要是关于基本使用以及一点应用,仅仅是个人看法。
0x01 基础部分
1.socket创建
首先来看socket的创建,依然是使用socket()函数,不过第一个参数,即类型变了,具体函数如下
| 12
 3
 4
 5
 6
 7
 
 | #include <sys/socket.h>#include <sys/un.h>
 ....
 ....
 unix_socket = socket(AF_UNIX, type, 0);
 ....
 ....
 
 | 
AF_UNIX也可以替换成AF_LOCAL
2.socket命名
关于socket进程通信,命名方式有两种,一是普通命名,二是抽象命名空间。差别在于,普通命名会根据你给的名字产生一个socket文件,然后你的通信过程socket会读取这个文件,这种方式也就决定了你编写的server必须对这个命名文件的路径有读写权限,而且客户端必须知道这个文件的路径;然而抽象命名空间的方式没有这个限制,但是需要一个全局名称,客户端根据这名称来连接。
1.普通命名
| 12
 3
 4
 5
 6
 7
 
 | #define SERVER_NAME "/tmp/server_socket"
 
 server_addr.sun_family = AF_UNIX;
 strcpy(server_addr.sun_path,SERVER_NAME);
 server_len = sizeof(struct sockaddr_un);
 client_len = server_len;
 
 | 
服务端必须对/tmp/server_socket有读写权限,且客户端知道这个路径。
2.抽象命名空间
| 12
 3
 4
 5
 6
 
 | #define SERVER_NAME "@server_socket"
 server_addr.sun_family = AF_UNIX;
 strcpy(server_addr.sun_path, SERVER_NAME);
 server_addr.sun_path[0]=0;
 server_len = strlen(SERVER_NAME)  + offsetof(struct sockaddr_un, sun_path);
 
 | 
这种方式对地址结构成员sun_path数组赋值的时候,必须把第一个字节置0。
2.socket绑定
这个是对于服务端来说的,也是使用bind函数
| 12
 3
 4
 5
 
 |     ........
 bind(server_sockfd, (struct sockaddr *)&server_addr, server_len);
 ....
 ....
 
 | 
2.socket侦听
使用listen函数
| 12
 3
 4
 5
 
 |     ........
 listen(server_sockfd, 5);
 ....
 ....
 
 | 
2.socket连接
下面这个部分,作为服务端,要等待服务端的连接,使用accept函数接收连接然后做处理,作为服务端使用connect函数连接,然后通信。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | while(1){
 client_len = sizeof(client_addr);
 client_sockfd = accept(server_sockfd,(struct sockaddr*)&client_addr, &client_len);
 
 
 read(client_sockfd, &ch, 1);
 printf("read from client %d: %c",client_sockfd,ch);
 ch ++;
 write(client_sockfd, &ch, 1);
 
 close(client_sockfd);
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | while(1){
 
 printf("set send content:");
 
 scanf("%c",&ch);
 write(sockfd, &ch, 1);
 printf("send to server:%c \n",ch);
 read(sockfd, &ch, 1);
 printf("read from server: %c\n", ch);
 }
 
 | 
0x02 示例代码
这里直接使用了参考blog中的代码
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 
 | #include <sys/types.h>
 #include <sys/socket.h>
 #include <stdio.h>
 #include <sys/un.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <stddef.h>
 
 #define SERVER_NAME "@server_socket"
 
 
 int main(){
 int server_sockfd, client_sockfd;
 socklen_t server_len, client_len;
 struct sockaddr_un server_addr;
 struct sockaddr_un client_addr;
 char ch;
 int nread;
 
 
 unlink("server_socket");
 server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
 server_addr.sun_family = AF_UNIX;
 strcpy(server_addr.sun_path, SERVER_NAME);
 server_addr.sun_path[0]=0;
 server_len = strlen(SERVER_NAME)  + offsetof(struct sockaddr_un, sun_path);
 bind(server_sockfd, (struct sockaddr *)&server_addr, server_len);
 
 
 listen(server_sockfd, 5);
 client_sockfd = -1;
 client_len = sizeof(client_addr);
 while(1){
 printf("server waiting...\n");
 if(client_sockfd == -1){
 client_sockfd = accept(server_sockfd,(struct sockaddr*)&client_addr, &client_len);
 }
 
 nread = read(client_sockfd, &ch, 1);
 if(nread == 0){
 printf("client %d disconnected\n",client_sockfd);
 client_sockfd = -1;
 }
 else{
 printf("read from client %d: %c\n",client_sockfd,ch);
 ch ++;
 write(client_sockfd, &ch, 1);
 }
 usleep(100);
 }
 return 0;
 }
 
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 
 | #include <sys/types.h>
 #include <sys/socket.h>
 #include <stdio.h>
 #include <sys/un.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <stddef.h>
 
 #define SERVER_NAME "@server_socket"
 
 
 
 int main()
 {
 int sockfd;
 socklen_t len;
 struct sockaddr_un address;
 int result;
 char ch = 'A';
 
 
 sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
 
 address.sun_family = AF_UNIX;
 strcpy(address.sun_path, SERVER_NAME);
 address.sun_path[0]=0;
 
 len =  strlen(SERVER_NAME)  + offsetof(struct sockaddr_un, sun_path);
 
 result = connect(sockfd, (struct sockaddr*)&address, len);
 if(result == -1)
 {
 perror("opps:client1");
 exit(1);
 }
 
 while(1)
 {
 printf("set send content:");
 scanf("%c",&ch);
 write(sockfd, &ch, 1);
 printf("send to server:%c \n",ch);
 read(sockfd, &ch, 1);
 printf("read from server: %c\n", ch);
 
 }
 exit(0);
 
 }
 
 | 
效果如下
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | $ ./server server waiting...
 read from client 4: a
 server waiting...
 read from client 4:
 
 server waiting...
 read from client 4: Z
 server waiting...
 read from client 4:
 
 server waiting...
 
 
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | $ ./client set send content:a
 send to server:a
 read from server: b
 set send content:send to server:
 
 read from server:
 
 set send content:Z
 send to server:Z
 read from server: [
 set send content:send to server:
 
 read from server:
 set send content:
 
 
 | 
服务端把客户端发来的字符加一后送回了。
0x03 关于应用
既然是进程间的通信,那么父子进程之间通信也是可以使用这个东西的。
-    那这个东西就可以用来做一个wapper,赛棍们都懂的 2333333
-    或者可以用来做简单的fuzz,子进程里启动目标程序,然后交互数据都是通过socket传递,那么就可以把一些输入变异之后再送过去吧,而且异常捕获也挺好做,waitpid就可以。不过用来做fuzzer的话,效率不知道怎么样…
0x04 代码
首先创建套接字,然后fork产生子进程
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | int pid;unlink(SOCK_NAME);
 int s = socket(AF_UNIX, SOCK_STREAM, 0);
 struct sockaddr_un addr;
 memcpy(addr.sun_path, SOCK_PATH, strlen(SOCK_PATH));
 addr.sun_family = AF_UNIX;
 bind(s, (struct sockaddr *)&addr, strlen(addr.sun_path) + sizeof(addr.sun_family));
 listen(s, 5);
 pid = fork();
 
 | 
父进程与子进程启动的目标程序交互,可以劫持输入输出。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | struct sockaddr_un child;int clen = sizeof(child);
 int cs = accept(s, (struct sockaddr *) &child, &clen);
 
 if (cs == -1) {
 fprintf(stderr, "server socket fail\n");
 exit(0);
 }
 
 n = read(cs, buf, BUFSIZE);
 write(1, buf, n);
 
 while (1) {
 
 n = read(0, buf, BUFSIZE);
 
 write(cs, buf, n);
 bzero(buf, BUFSIZE);
 
 
 n = read(cs, buf, BUFSIZE);
 write(1, buf, n);
 }
 
 | 
子进程里启动目标程序
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | int s = socket(AF_UNIX, SOCK_STREAM, 0);struct sockaddr_un addr;
 bzero(addr.sun_path, sizeof(addr.sun_path));
 memcpy(addr.sun_path, SOCK_PATH, strlen(SOCK_PATH));
 addr.sun_family = AF_UNIX;
 connect(s, (struct sockaddr *)&addr, strlen(addr.sun_path) + sizeof(addr.sun_family));
 if (s == -1) {
 fprintf(stderr, "cli socket fail\n");
 exit(0);
 }
 dup2(s, 0);
 dup2(s, 1);
 execve("./target", NULL, NULL);
 
 | 
0x05 参考
manpage
linux socket进程间通信