CMD 控制台 CLS 命令内幕

引入

今天碰到有人在群里问我“C实现清屏是不是只能system(“cls”);或一直换行?”,一时不知道怎么回答,毕竟一般在控制台里清屏,就是用的 system(“cls”)。

system(“cls”) 是调用了 C 标准库函数 system,作用就是将命令传递给控制台去解析执行。那么我们回顾一下命令执行的过程大致如下:会有一个字符串的解析,会尝试搜索外部命令,会与内部命令一一对比,还要看看是否可能加了参数,最后才转入 cls 的执行函数,然后调 API 清屏。

这么一看,它的效率实在不敢恭维。对于一个在速度上要求极高的人,这是不可忍受的。想到 Demon 大神曾对 cmd 进行了一系列分析,写出了个[批处理内幕]系列文章,那么我为什么不分析分析 cls 的背后到底都做了什么呢?

水平有限,如果操作有误还请大家不吝指教。
##准备
调试工具 OllyDbg 吾爱破解专用版,被调试程序 cmd.exe,系统环境:Win7 Ultimate。

步骤

  1. 用 od 打开 cmd.exe,F9 运行,如图
    CMD 控制台 CLS 命令内幕_第1张图片
  2. od 点击暂停,单步步过直到状态变为“运行”,回到 cmd,输入 cls,回车,回到 od,发现状态已变为暂停
    CMD 控制台 CLS 命令内幕_第2张图片
  3. 一直 F8 步过,直到回到 cmd 模块内
    CMD 控制台 CLS 命令内幕_第3张图片
  4. 接下来一直 f8,每次经过一个 call 的时候注意,看步过后 cmd 的窗口内容变化。若窗口内容被清空,那么 cls 代码就在这个 call 中,在 call 上 f4,回到 cmd 窗口里再次输入一个 cls 回车,返回来 f7 步进那个 call,在新的函数段接着 f8
    CMD 控制台 CLS 命令内幕_第4张图片
  5. 总共确定了三次,最后到达一个函数段,大致来看跟屏幕滚动有关,对代码的高度敏感告诉我,这就是要找的地方
    CMD 控制台 CLS 命令内幕_第5张图片
  6. 然后放慢速度,往下接着 f8,发现步过第一个 call 时内容没有变化,那么看来,从这里开始都是 cls 的内容了。接下来要做的就是分析汇编代码,推出 C 代码了。
    由汇编代码可见,他是先获取了控制台窗口缓冲区的信息,然后调用了缓冲区滚动函数,最后设置光标回归左上角。之后又看了看,在清屏之后 retn 后又进行了一系列操作,然后输出提示符。不过那就与本代码无关了。

推源码,尝试过使用 IDA,然而根本找不到那个地址(后来想到交叉引用,不过那时候我已经推出代码了,故而不再考虑),于是就只能手推了。根据 MSDN 手册作参考,推的过程不细讲,最后得到如下的代码
CMD 控制台 CLS 命令内幕_第6张图片

void clearScreen()
{
	HANDLE hStdOut;
	CONSOLE_SCREEN_BUFFER_INFO bufInfo;
	SMALL_RECT scroll;
	COORD newCursorPointer;
	CHAR_INFO ciFill;
	
	hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
	if(!GetConsoleScreenBufferInfo(hStdOut, &bufInfo))
		return;
	
	scroll.Left = 0;
	scroll.Top = 0;
	scroll.Right = bufInfo.dwSize.X;
	scroll.Bottom = bufInfo.dwSize.Y;
	newCursorPointer.X = 0;
	newCursorPointer.Y = -bufInfo.dwSize.Y;
	ciFill.Char.UnicodeChar = L' ';
	ciFill.Attributes = bufInfo.wAttributes;
	ScrollConsoleScreenBufferW(hStdOut, &scroll, NULL, newCursorPointer, &ciFill);
	newCursorPointer.Y = 0;
	SetConsoleCursorPosition(hStdOut, newCursorPointer);
}

好了,大功告成。经测试对 200 行的输出,system(“cls”) 所耗费时间大概在 90ms 到 100ms 之间浮动,而同样条件下 clearScreen() 仅 1ms 到 3ms,高下立判。测试代码就不附了。
##结语
终于可以回答那位朋友了,除了 system(“cls”) 以及他说的那种方法以外,还可以用我的 clearScreen() 哦~

另由此引申下,很多朋友喜欢用在一个程序中调用另一个程序,不过一个小小的 system(“cls”) 都要花费那么多的时间,调用其他进程的损耗不知道多少?因此能在这个程序里实现最好,尽量少调用外部程序。

为了写这篇文章还特地回去翻了翻 Demon 大神的一篇博文,批处理技术内幕:ECHO 命令,里面有说到控制台解析命令时的大致过程。虽然本文中并没有用到多少。曾记得 Demon 大神好像有一篇文章对 cmd 命令搜索过程做了详细的分析,不过这次没找到,以后看看补上吧。

你可能感兴趣的:(C,逆向分析与破解日志)