『网安产品开发实践』Linux编程-基于TCP Socket实现远程执行shell命令

433 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

工作原理

  1. 客户端接收用户输入的命令,客户端通过套接字将命令传送给服务器端
  2. 服务器在收到用户的命令,对命令进行解析
  3. 在服务器端调用对应的命令
  4. 将命令执行的结果发送给客户端,从而实现远距离控制的功能

附加要求

  1. 客户端输入“quit”,客户端程序与服务器端程序打印退出信息,终止程序的执行
  2. 客户输入命令,客户端将命令通过流套接字发送给客户端,服务器执行收到的命令,并将结果发送到客户端显示
  3. 如果没有客户输入的命令,服务器发送命令非法信息,并在客户端显示该条信息
  4. 服务器端可以接收客户端多次连接,能够处理客户端非正常退出

源码

gcc编译后使用

gcc -o cmdtcpserver cmdtcpserver.c
gcc -o cmdtcpclient cmdtcpclient.c
./cmdtcpserver
./cmdtcpclient IP

server

// cmdtcpserver.c
#include<stdio.h>
#include<unistd.h>
#include<sys/socket.h>
#include<string.h>
#include<netdb.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>

// 定义端口和缓冲区大小
#define PORT 8900
#define MAXSIZE 4096

// 定义命令执行的函数
int execute(char* cmd,char* buf){
	FILE *fp;
	int count;
	
	count = 0;

	// 管道
	if (NULL==(fp = popen(cmd,"r"))){
		perror("create pipe error\n");
		return -1;
	}

	// 统计长度
	while(((buf[count] = fgetc(fp))!=EOF)&&count<4095)
		count++;
	
	// 添加终止符
	buf[count]='\0';

	pclose(fp);
	return count;
}


int main(){
	int sockfd;
	int fd;
	struct sockaddr_in client;
	struct sockaddr_in server;
	char send_buf[MAXSIZE];
	char recv_buf[MAXSIZE];
	char cmd[MAXSIZE];
	int sendnum;
	int recvnum;
	int len;
	
	if (-1==(sockfd=socket(AF_INET,SOCK_STREAM,0))){
		perror("create socket error");
		return -1;
	}	

	memset(&server,0,sizeof(struct sockaddr_in));
	memset(&client,0,sizeof(struct sockaddr_in));
	
	server.sin_family = AF_INET;
	server.sin_addr.s_addr = htonl(INADDR_ANY);
	server.sin_port = htons(PORT);

	if (-1==bind(sockfd,(struct sockaddr*)&server,sizeof(struct sockaddr))){
		perror("bind error\n");
		close(sockfd);
		return -1;
	}
	
	if (-1==listen(sockfd,5)){
		perror("listen error\n");
		return -1;
    }

	while(1){		
		memset(recv_buf,0,MAXSIZE);
		memset(send_buf,0,MAXSIZE);
		
		// 为了实现client使用ctrl+c退出后server不挂,accept需要移入while
		if(0>(fd=accept(sockfd,(struct sockaddr*)&client,&len))){
			perror("create connect socket error\n");
			continue;
		}

		if(-1==(recvnum = recv(fd,recv_buf,sizeof(recv_buf),0))){
			perror("recv error\n");
			continue;
		}	

		recv_buf[recvnum]='\0';

		if (0==strcmp(recv_buf,"quit")){
			perror("quit\n");
			break;

		}
		printf("receive: %s\n",recv_buf);

		// 指针指向缓冲区终止符
		// 在/bin、/sbin、/usr/bin查找命令
		strcpy(cmd,"/bin/");
		strcat(cmd,recv_buf);
		execute(cmd,send_buf);

		if ('\0'==*send_buf){
			memset(cmd,0,sizeof(cmd));
			strcpy(cmd,"/sbin/");
			strcat(cmd,recv_buf);
			execute(cmd,send_buf);
			
			if ('\0'==*send_buf){	
				memset(cmd,0,sizeof(cmd));
				strcpy(cmd,"/usr/bin/");
				strcat(cmd,recv_buf);
				execute(cmd,send_buf);
			}
			
			if ('\0'==*send_buf){	
				memset(cmd,0,sizeof(cmd));
				strcpy(cmd,"/usr/sbin/");
				strcat(cmd,recv_buf);
				execute(cmd,send_buf);
			}
		}
		
		// 命令不存在
		if ('\0'==*send_buf)
			sprintf(send_buf,"cmd error\n");

		printf("reply:%s\n",send_buf);
		
		if (-1==send(fd,send_buf,sizeof(send_buf),0)){
			perror("send error\n");
			break;
		}
		// 每次都需要关闭fd
		close(fd);		
	}
	close(sockfd);
	return 0; 
}

client

// cmdtcpclient.c
#include<stdio.h>
#include<unistd.h>
#include<sys/socket.h>
#include<string.h>
#include<netdb.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>

#define PORT 8900
#define MAXSIZE 4096

// 输入提示
void print_usage(char* str){
	fprintf(stderr," %s usage:\n",str);
	fprintf(stderr,"%s IP_Addr",str);
}


int main(int argc, char** argv){
	int sockfd;
	struct sockaddr_in client;
	struct sockaddr_in server;
	char send_buf[MAXSIZE];
	char recv_buf[MAXSIZE];
	int sendnum;
	int recvnum;
	int len;
	int input_len;

	if(2!=argc){
		print_usage(argv[0]);
		return -1;	

	}

	memset(&server,0,sizeof(struct sockaddr_in));
	memset(&client,0,sizeof(struct sockaddr_in));
	
	server.sin_family = AF_INET;
	server.sin_addr.s_addr = inet_addr(argv[1]);
	server.sin_port = htons(PORT);	
	
 	len = sizeof(struct sockaddr);	
 	
	while(1){
		memset(send_buf,0,MAXSIZE);
		memset(recv_buf,0,MAXSIZE);
		
		// 每次新建socket,保证server不挂
		if (-1==(sockfd=socket(AF_INET,SOCK_STREAM,0))){
			perror("create socket error\n");
			return -1;
		}	
		
		if (-1==(connect(sockfd,(struct sockaddr*)&server,sizeof(struct sockaddr)))){
			perror("connect error");
			close(sockfd);
			break;
		}
				
		printf("tcp>");
		fgets(send_buf,MAXSIZE,stdin);
		input_len=strlen(send_buf);
		send_buf[input_len-1]='\0';
		
		if (-1==send(sockfd,send_buf,sizeof(send_buf),0)){
			perror("send error!\n");
			continue;
		}
		
		if (0==strcmp(send_buf,"quit")){
			perror("quit\n");
			continue;
		}
		
		if (-1==recv(sockfd,recv_buf,sizeof(recv_buf),0)){
			perror("recv error!");
			continue;
		}

		fprintf(stdout,"%s\n",recv_buf);
		close(sockfd);

	}
	close(sockfd);
	return 0;
}

运行样例

可以执行长段指令、对无效指令报错、ctrl+c断开后server端不挂,以及quit结束两端会话 在这里插入图片描述

欢迎在评论区留言