Board logo

标题: [系统相关] 分析CMD管道命令 [打印本页]

作者: broly    时间: 2012-9-13 16:30     标题: 分析CMD管道命令

  1. 引言:
  2. 最近在看一本关于计算机操作系统的书,有一章节讲的内容是程序的进程。
  3. 程序的进程之间有三种通信机制:1.共享存储器系统 2.消息传递系统 3.管道通信
  4. 其中消息传递系统是当前应用最为广泛的一种通信机制。熟悉WINDOWS编程的朋友应该不会陌生。
  5. 书中短短一小段话就把管道通信介绍完毕,可见这部分内容并不是非常重要。
  6. 但是我却对管道通信起了兴趣,想起了CMD下的管道命令,我决定深入了解一番。
复制代码
我们先来了解什么是管道,书中是这样定义的:
所谓“管道”,是指用于连接一个读进程和一个写进程以实现他们之间通信的一个共享文件,又名pipe文件。

在这里,许多人会这样想,“一个文件?那不是硬盘上找的到,在什么位置呢?”
我先跟大家提一下,管道“文件”是由API函数CreateFile创建的,
管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,
它不属于某种文件系统(如FAT32,NTFS,ext3),而是自立门户,单独构成一个文件系统,并且只存在于内存中。
这很类似系统的“设备文件”,所以大家把“管道文件”当作一个缓冲区就可以了。

管道的命名必须遵循特定的命名方法,就是 "\\.\pipe\管道名",当作为客户端的进程要使用时,使用"\\计算机名\\pipe\管道名" 来打开使用。
按照管道的类别分有两种管道,匿名的和命名的;按照管道的传输方向分也可以分成两种,单向的双向的。
根据管道的特点,命名管道通常用在网络环境下不同计算机上运行的进程之间的通信(当然也可以用在同一台机的不同进程中)它可以是单向或双向的;
而匿名管道只能用在同一台计算机中,它只能是单向的。匿名管道其实是通过用给了一个指定名字的有名管道来实现的。

在编程方面的知识我就不扯太多了,有兴趣请自己研究,我在文章结尾会附上参考文档。
CMD下的管道命令 "|" 就是匿名管道。

在CMD下运行:
  1. set input=empty
  2. echo test | set /p input=
  3. echo %input%
复制代码
  1. C:\Users\Broly>set input=empty
  2. C:\Users\Broly>echo test | set /p input=
  3. C:\Users\Broly>echo %input%
  4. empty
复制代码
结果显示的是empty,也就是说第二行的语句好像是没有赋值成功的。

再来看看这个代码:
  1. set input=empty
  2. echo test | (set /p input= & set input)
  3. echo %input%
复制代码
  1. C:\Users\Broly>set input=empty
  2. C:\Users\Broly>echo test | (set /p input= & set input)
  3. input=test
  4. C:\Users\Broly>echo %input%
  5. empty
复制代码
结果显示,管道后面的变量input事实上是被赋值成功的。
那为什么第三条语句会显示input变量的值为empty呢?

因为管道命令 "|" 后面的语句,CMD会新创建一个子进程,
第二句的input实际为一个新的CMD进程里面的变量,
而第三句的input为父进程的变量,所以两个变量的值就不同。

值得注意的是,管道命令 "|" 后面的命令不管是内部命令还是外部命令,CMD都会为其创建新的子进程。

conset工具的实现原理应该就是获取另一个进程的结果再赋值给当前进程的变量。

关于重定向命令:
  1. stdin是标准输入,stdout是标准输出,stderr是标准错误输出。
  2. 大多数的命令行程序从stdin输入,输出到stdout或stderr,有时我们需要重定向stdout,stderr,stdin。
  3. 在Windows编程中,重定向需要用到管道(Pipe)的概念。管道是一种用于在进程间共享数据的机制。一个管道类似于一个管子的两端,一端是写入的,一端是读出的。由一个进程从写入端写入、另一个进程从读出端读出,从而实现通信,就向一个“管道”一样。
  4. 重定向的原理是:
  5. 首先声明两个概念:主程序(重定向的操纵者)、子进程(被重定向的子进程)
  6. 如果要重定位stdout的话,先生成一个管道, 管道的写入端交给子进程去写,主程序从管道的读出端读数据,然后可以把数据写成文件、显示等等。重定向stderr和stdout是相同的。
  7. 同理,要重定向stdin的话,生成一个管道, 管道的写入端由主程序写,子进程从管道的读出端读数据。
复制代码
大概重定向命令和管道命令的过程就是这样,不是很难理解。
以上部分文字是我直接网上复制过来的,有错误的地方望给予指出。

参考资料:http://msdn.microsoft.com/en-us/library/windows/desktop/aa365781(v=vs.85).aspx
作者: Bearxy    时间: 2012-9-13 16:54

  1. set input=empty
  2. echo test | set /p input=
  3. echo %input%
复制代码
第二句赋值不成功,是因为|开启了子进程,一个是父进程的变量,一个是子进程的,所以不成功。
  1. echo.D|xcopy "test" "test"
复制代码
不是都会开启子进程吗?这样不是互相矛盾了,既然开启子进程,而子进程的变量和父进程的又不一样,那为什么这样又是可以的?
作者: broly    时间: 2012-9-13 17:48

回复 2# Bearxy
    这位朋友,请你确定自己把整篇文章都理解了再提问吧,是细心看完而不是随便一目十行的那种。如果还有疑问我一一解答。
作者: broly    时间: 2012-9-13 18:00

为了让大家更容易理解,我还是详细再说说吧:
当cmd运行一个内部命令时,它是不会创建子进程的,仅是当前cmd进程。
而运行一个外部命令时,cmd会创建一个子进程,也就是外部命令的进程。

而管道命令有一点可以明确的是,它做进程之间的通信用,所以父进程的数据可以通过管道传递给子进程。
如果管道后面的命令是内部命令,内部命令需要的是cmd的支持,所以创建的子进程是cmd.exe。而外部命令则不会再创建一个cmd.exe,依旧是本身进程做子进程。

像一楼的例子中set是一个内部命令,将会产生一个新cmd.exe子进程,而input变量的值就是由管道传过来的值,input变量是存在于子进程cmd.exe中。

这样详细说明,还有谁不能够理解?
作者: Demon    时间: 2012-9-13 18:12

看标题还以为是技术内幕,原来只是理论分析。

CMD的重定向不是用管道实现的,见http://demon.tw/reverse/cmd-internal-redirect.html
作者: broly    时间: 2012-9-13 18:29

回复 5# Demon
    文中只是说重定向要用到管道的概念,用这个概念分析挺合适的啊。

其实我也有用Od分析过|,解析的过程也就是文中说的那样,你也可以去看看。至于说技术内幕这个系列,还是交给你来合适,我也只是像引言提到的,突然来兴趣了才研究下的。
作者: plp626    时间: 2012-9-14 17:18

回复 5# Demon

demon 和楼主 试着分析下 conset 的原理:

子进程 是如何改变父进程的环境变量的?

有什么api直接调用,还是需要一些特殊技巧?
  1. [/size]
  2. ->dir | conset x=
  3. ->set x
  4. x= 驱动器 D 中的卷是 SOFT
  5. 卷的序列号是 B89F-E38E
  6. D:\Documents_and_Settings\plp2\desktop\x 的目录
  7. 2012-08-27  23:51    <DIR>          .
  8. 2012-08-27  23:51    <DIR>          ..
  9. 2012-08-20  01:57    <DIR>          Debug
  10. 2012-08-20  02:00             4,222 x.dsp
  11. 2012-08-20  01:53               508 x.dsw
  12. 2012-08-20  02:00            33,792 x.ncb
  13. 2012-08-20  02:00            48,640 x.opt
  14. 2012-08-20  01:57             1,227 x.plg
  15.                5 个文件         88,389 字节
  16.                3 个目录  5,525,741,568 可用字节
复制代码

作者: broly    时间: 2012-9-15 01:22

本帖最后由 broly 于 2012-9-15 01:23 编辑

回复 7# plp626


    刚才看了下程序,发现主要是用到了两个API函数:GetEnvironmentVariable,SetEnvironmentVariable

  去MSDN找了些有用的资料,可以参考:http://msdn.microsoft.com/zh-cn/ ... loads/ms682009.aspx

今晚跟朋友出去了,回来的好晚,我要睡觉了,明天有空再试试资料中的例子代码。
作者: CrLf    时间: 2012-9-15 01:37

回复 8# broly


    问题是它是怎么影响到父进程环境变量的呢?
作者: plp626    时间: 2012-9-15 13:25

SetEnvironmentVariable 这个是修改进程自身的,每个操作系统都会提供如此功能的接口;
我想修改父进程的api怕是没有; 处于安全考虑,如果有这个api也太危险了;

在谷歌搜索,C and C++ windows Change Environment Variable In Parent Process

具体的 特殊技巧 没找到。。。
作者: plp626    时间: 2012-9-15 13:35

本帖最后由 plp626 于 2012-9-15 13:59 编辑

注入,谁能看懂给大伙把这黑客技术 普及下:
http://www.codeproject.com/Artic ... t-Variables-of-Remo

鉴于这个话题已经远离本贴主题, 另开一贴,感兴趣的会员可来此讨论:

http://www.bathome.net/thread-19036-1-1.html
作者: Demon    时间: 2012-9-16 21:39

回复  Demon
    文中只是说重定向要用到管道的概念,用这个概念分析挺合适的啊。

其实我也有用Od分析 ...
broly 发表于 2012-9-13 18:29


文中分析的太简单,写了篇《批处理技术内幕:重定向与管道》。




欢迎光临 批处理之家 (http://bbs.bathome.net/) Powered by Discuz! 7.2