作者:AGanNo2
Email:AGanNo2@163.com
1.VC的IDE在编译链接做了些什么
用惯了VC的IDE下习惯性的点击“compile”和“build”的可能不知道在你按下这些按钮时IDE在背后到底做了写什么,对于熟悉命令行下进行编译链接的人对cl.exe、link.exe、rc.exe这些编译链接的工具不是陌生的,在这里我并不想讨论这些工具怎样进行编译的以及那些名目繁多的选项参数,而是从“亚微观”上揭开在你点击那些按钮之后,IDE在背后干的事情,肯定有初学者像我一样想了解,当然那些unix/linux下的程序员以及会写makefile来组织编译链接进程的Profession的程序员就不用看下去了。
2.vcspawn的选项和参数
vcspawn,突然说到这个可能很多不大熟,其实在你点击那些按钮的时候,并不是由IDE自己直接去调用cl.exe、link.exe、rc.exe这些工具的,而是通过vcspawn.exe这个工具进行中转的,简单说IDE把参数传递给vcspawn,然后由vcspawn来建立进程去执行这些编译链接的命令,然后把编译链接过程中的信息输出到IDE的最下面的输出窗口的,在这里vcspawn的输出被重定向到那个窗口了(一般通过管道),而由vcspawn新建的进程由于继承了父进程的输出,因此也会把重定向到IDE的输出窗口。
下面是个典型的IDE向vcspawn传递的参数:(M$对这些参数好像一点文档都没有,这些是通过调式手段截获的)
VCSPAWN.EXE -e 1380 -m ~vcecho!Compiling resources…\n
rc.exe /l 0×804 “/foRelease/SSDowner.res” /d “NDEBUG” “D:\Microsoft Visual Studio\MyProjects\test\test.rc”\n
~vcecho!Compiling…\n
cl.exe @C:\DOCUME~1\STILLW~1\LOCALS~1\Temp\RSP5.tmp\n
~vcecho!Linking…\n
link.exe @C:\DOCUME~1\STILLW~1\LOCALS~1\Temp\RSP6.tmp\n
可以看到这里只有两个选项-e和-m,当然还会有其他的,后文再说
其中-e 1380中的参数1380是IDE的进程句柄或者管道句柄。-m后面带的参数比较长,也是有规律的,据观察,他们以’\0xa’作为分割符,~vcecho!也是一个关键字,它表示后面的在\n之前的字符串将被输出到IDE的输出窗口里,这样我们在编译的过程中也就看到了Compiling resources…、Compiling…、Linking…这些表示编译进程的信息,类似~vcecho!的词还有~vctime!和~vcsleep!,我想不用多解释就明白它们是什么意思了。
下面看由’\0xa’分割得到的其它三句:
rc.exe /l 0×804 “/foRelease/SSDowner.res” /d “NDEBUG” “D:\Microsoft Visual Studio\MyProjects\test\test.rc”
cl.exe @C:\DOCUME~1\STILLW~1\LOCALS~1\Temp\RSP5.tmp
link.exe @C:\DOCUME~1\STILLW~1\LOCALS~1\Temp\RSP6.tmp
vcspawn将把这些做为命令行分别建立新的进程。在这里,很明显只有rc.exe给出了比较详细的链接参数,也难怪因为一般资源文件只有一个就够了,而源文件和目标文件会有多个,于是cl.exe和link.exe的参数中只有它们的临时文件,这些临时文件RSP5.tmp和RSP6.tmp(名称随机)的内容大致如下:(在IDE清理删除它们之前拷贝过来的):
RSP5.tmp
/nologo /ML /W3 /GX /O2 /D “WIN32″ /D “NDEBUG” /D “_WINDOWS” /D “_MBCS” /Fp”Release/test.pch” /YX /Fo”Release/” /Fd”Release/” /FD /c
“D:\Microsoft Visual Studio\MyProjects\test\main.c”
RSP6.tmp
kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /incremental:no /pdb:”Release/test.pdb” /machine:I386 /out:”Release/test.exe”
“.\Release\main.obj”
“.\Release\test.res”
分别是编译器(cl.exe)和链接器(linke.exe)的参数,熟悉命令行编译链接的对这些应该很熟悉。
vcspawn除了-e和-m这连个参数外,还有-t -p(一般放在一块),甚至在你点击“execute”(执行)的时候,也并不是由IDE去直接执行连接后的可执行文件,而还是由vcspawn作为中转,vcspawn启动可执行文件的参数如下:(这个bill也不告诉俺)
vcspawn.exe -t -p ‘D:\Microsoft Visual Studio\MyProjects\test\Release\test.exe”
这个就可以解释为什么在IDE下点击“execute”去执行编译后的程序后都会有”Press any key to continue”这句,而且在按了一个键后才结束,这些在我们的程序中并没有这些,这些都是位于vcspawn.exe中的,这也是初学者直接点击一个自己写的console程序后看到屏幕一闪而过的原因(当然并非所有的程序都这样)
3.逆向写兼容的vcspawn
在通过阅读vcspawn的反汇编代码了解其大致流程后,就开始了俺的逆向重构的过程,用的是C,由于严格按照vcspawn的反汇编代码的逻辑来写的,所以花了我近一天的时间,力求做到逻辑上的一致(甚至是汇编代码级别上的,吹牛啦),由于VC6.0的vcspawn的版本是6.00的,因此自己写的兼容的vcspawn在vc6.0和evc4.2上工作的很好(放在我机器D:\Microsoft Visual Studio\Common\MSDev98\Bin这个目录里,覆盖原版),不禁窃喜,vc2003的看了一下,也无多大区别(参数有点区别),已无兴趣再做重复的事情。
vcspawn大都调用的都是msvcrt这个库的东西,而且是多线程动态链接的(/MD),编译连接后体积只有7.5k,可谓小巧。
4.这个有什么用?
是啊,有什么用呢,在我自己写的vcspawn中我实现了输出那些编译链接的细节,就差没把临时文件拷过来了(这个不难),其它的可以在其中加入自己要执行的命令或者输出一些更详细的信息。不过说过来好像还是无大用处,不过使我自己独自的弄明白了IDE到底干了些啥,顺便练了下逆向工程
最后抓个图留做纪念
其中红色的部分是兼容性版本的vcspawn输出的编译链接中输出的详细信息。
>> 本文固定链接: http://www.vcgood.com/archives/1395