0x00
linux socket
网络编程大家肯定不会陌生,然而linux
也可以使用socket这套接口来实现进程间通信,大概的步骤和网络通信差不多,也是可以基于C/S的,服务端创建套接字,然后绑定、侦听,客户端创建套接字,链接,然后就可以通信了。这篇文章主要是关于基本使用以及一点应用,仅仅是个人看法。
0x01 基础部分
1.socket创建
首先来看socket的创建,依然是使用socket()
函数,不过第一个参数,即类型变了,具体函数如下
1 2 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.普通命名
1 2 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.抽象命名空间
1 2 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函数
1 2 3 4 5
| .... .... bind(server_sockfd, (struct sockaddr *)&server_addr, server_len); .... ....
|
2.socket侦听
使用listen函数
1 2 3 4 5
| .... .... listen(server_sockfd, 5); .... ....
|
2.socket连接
下面这个部分,作为服务端,要等待服务端的连接,使用accept函数接收连接然后做处理,作为服务端使用connect函数连接,然后通信。
1 2 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); }
|
1 2 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中的代码
1 2 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; }
|
1 2 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);
}
|
效果如下
1 2 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...
|
1 2 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产生子进程
1 2 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();
|
父进程与子进程启动的目标程序交互,可以劫持输入输出。
1 2 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); }
|
子进程里启动目标程序
1 2 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进程间通信