﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-xingkongyun</title><link>http://www.cppblog.com/xingkongyun/</link><description>星空陨的程序小站</description><language>zh-cn</language><lastBuildDate>Tue, 07 Apr 2026 20:37:40 GMT</lastBuildDate><pubDate>Tue, 07 Apr 2026 20:37:40 GMT</pubDate><ttl>60</ttl><item><title>安全密码框的设计</title><link>http://www.cppblog.com/xingkongyun/archive/2009/12/22/103691.html</link><dc:creator>杨彬彬</dc:creator><author>杨彬彬</author><pubDate>Tue, 22 Dec 2009 05:56:00 GMT</pubDate><guid>http://www.cppblog.com/xingkongyun/archive/2009/12/22/103691.html</guid><wfw:comment>http://www.cppblog.com/xingkongyun/comments/103691.html</wfw:comment><comments>http://www.cppblog.com/xingkongyun/archive/2009/12/22/103691.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.cppblog.com/xingkongyun/comments/commentRss/103691.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xingkongyun/services/trackbacks/103691.html</trackback:ping><description><![CDATA[前两天在学校做的一个小设计的论文，随手发上来，小弟不才，错误之处还请各位大牛多多指点<br>如果大家还有什么键盘记录的方法和防御的方法一定一定要告诉小弟我啊。<br><br>&nbsp;/*****************************************************************************************/<br>
<p><span>键盘作为计算机的主要输入设备，是大部分输入信息的主要来源，但是我们每天从键盘输入的信息安全吗？随着互联网的普及，各种网络应用也是层出不穷，电子购物，网上聊天等等等等，每天我们都会在各种程序上输入各式各样得分密码啊用户名啊银行卡号啊，你认为这些秘密安全吗？那位同学要说了：</span><span>&#8221;</span><span>没事啊，不都是有保护措施吗，像什么</span><span>QQ</span><span>的号称无懈可击的</span><span>nprotect</span><span>技术，网银也有各种安全插件，没事的！</span><span>&#8221; &#8220;</span><span>呵呵，真的像他们说的那摩安全吗，那可不一定，没有什么是绝对的</span><span>&#8221;</span><span>，下面我们就来看看键盘的秘密，看看黑客们是如何记录你的键盘操作的，以及我们该如何抵御这些猥琐的攻击方式。</span></p>
<p>&nbsp;</p>
<p><strong><span>一</span></strong><strong><span>.</span></strong><strong><span>键盘的硬件模型</span></strong><strong></strong></p>
<p><span>其实键盘应该算是计算机中最简单的设备了，在我们使用的普通的计算机系统中，与键盘有关的硬件说白了也就是两个芯片，</span><span>i8048</span><span>和</span><span>i8042 </span><span>。</span><span>i8042</span><span>也就是</span><span>intel8042,</span><span>位于主板上</span><span>,CPU</span><span>通过</span><span>IO</span><span>端口与</span><span>i8042</span><span>通信</span><span>,i8042</span><span>负责读取键盘按键的扫描吗或是发送个中键盘命令</span><span>.i8048,</span><span>它是位于键盘中的</span><span>,</span><span>是将键盘上的按键转换成所谓的扫描码的，然后传送给</span><span>i8042</span><span>。呵呵，就是这末简单个东西。上面说的都是比较老的计算机的结构了，现在这些芯片都集成到南桥芯片组里面去了，不过原理还是这样的。</span></p>
<p><span>当我们按下一个键与抬起的时候都会触发键盘的中断，在老早的计算机中都是采用两片</span><span>i8259A</span><span>芯片级联来管理中断的，键盘挂在主片的</span><span>IRQ1</span><span>引脚上，当有按键按下或抬起是会引发硬件中断，然后会调用相应的中断处理程序进行处理</span><span>.</span></p>
<p><span>在实模式与保护模式下对于中断的处理是不同的。在实模式下我就不说了，也记不的了，自己看看微机原理。在保护模式下是采用</span><span>IDT</span><span>对中断进行管理的，在</span><span>IDT</span><span>中是各种各样的门啊，啥中断门，陷阱门，调用门啊等等等等，对于键盘中断在</span><span>XP</span><span>下对应的是</span><span>0X31</span><span>号中断服务，但也有的</span><span>XP</span><span>对应的事</span><span>0x93</span><span>，原因我也不太明白，反正在我的系统上是</span><span>0x31</span><span>号。</span></p>
<p><span>好了既然都已经都调用中断处理程序了，那么在经过一系列复杂的处理最终我们就可以在应用层上舒舒服服的用</span><span>WORD</span><span>打字了。</span></p>
<p><span>好了，硬件这块就说到这，下面用到了相关的我们在说。</span></p>
<p>&nbsp;</p>
<p><strong><span>二</span></strong><strong><span>.</span></strong><strong><span>猥琐的键盘记录器</span></strong><strong></strong></p>
<p><span>对于那些盗号的所谓的黑客我是很不齿的，这里我们要探讨一些窃取键盘信息的方法并不是围了写个键盘记录的盗号程序，我们只是站在攻防对立统一的角度来看各式盗号手段，并给出相关的防御措施</span><span>.</span></p>
<p><span>我知道的一些盗号的手段也不多，而且都是一些比较普遍的手法，有些也都是爷爷辈的技术了，不过现在还是很好用，好多盗号的还是用这些土枪土炮打打打打劫</span><span>&#8230;</span></p>
<p><span>WINDOWS</span><span>系统是分为应用层与内核层的，从</span><span>CPU</span><span>的角度看就是</span><span>RING3</span><span>与</span><span>RING0</span><span>。应用层是受管制的，不可进行端口</span><span>IO</span><span>，不可执行特权指令，限制多多。内核就不说了，想干嘛就干吧。</span></p>
<p>&nbsp;</p>
<p><span>我也是按照应用层与内核两个层面进行讨论的。好了废话太多了，进入正题吧</span><span>.</span></p>
<p>&nbsp;</p>
<p><strong><span>1.</span></strong><strong><span>爷爷辈的</span></strong><strong><span>WM_GETTEXT</span></strong><strong><span>消息获取密码</span></strong><strong></strong></p>
<p><strong>&nbsp;</strong></p>
<p><span>用过</span><span>MFC</span><span>的都知道密码框吧，就那个</span><span>******</span><span>的框子，他其实是个文本框只不过是加了密码属性而已，本质上还是文本框。对于文本框我们就可以通过对其发送ＷＭ＿ＧＥＴＴＥＸＴ消息来获取密码的。不过还有点问题，在</span><span>WIN98</span><span>系列中这样就</span><span>OK</span><span>了，但是</span><span>NT</span><span>后，你要就是对着密码框大喊</span><span>WM_GETTEXT</span><span>是没用的，密码框会说</span><span>:</span><span>&#8220;你又不是我们家里的，我凭啥把密码告你啊&#8220;</span><span>.</span><span>其实这是跟操作系统有关的，在</span><span>WIN98</span><span>下所用的进程是共享一个</span><span>4GB</span><span>的虚拟内存的，那个就没什么你的我的了，所有的都是大家共有的，所以一个进程对另一个进程发送一个</span><span>WM_GETEXT</span><span>消息，应为大家都是自己人所以密码就告你了。但是到了</span><span>NT</span><span>后各个进程就闹分家了，每个进程独享</span><span>4GB</span><span>的虚拟内存，各个进程之间是互相隔离的，所以就没人理你了。</span></p>
<p><span>我们要采用些特殊的手段才能成功。也就是要把你的那段发送</span><span>WM_GETTEXT</span><span>消息的代码移到目标进程中去执行，方法还是有的，我使用的远程线程技术，也就是将一个功能模块诸如到目标进程中然后执行，这样就</span><span>OK</span><span>了。对于如何进行线程注入，方法很多，</span><span>google</span><span>一下就可以了。</span></p>
<p><span>原理就是这样很简单，问题就在怎样在目标进程中去执行代码，这种方法就说到这把。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>2.</span></strong><strong><span>屠夫的钩子</span></strong><strong></strong></p>
<p><strong>&nbsp;</strong></p>
<p><span>呵呵，玩过</span><span>dota</span><span>吗，对就是屠夫用的钩子，在</span><span>windows</span><span>中同样有钩子，而且也是相当的犀利。</span></p>
<p><span>使用钩子相当的简单，就一个</span><span>API</span><span>函数</span>&nbsp;<span>SetWindowsHookEx</span><span>，不过内涵很丰富，在</span><span>windows</span><span>下存在各式各样的钩子，消息钩子，鼠标钩子，键盘钩子，日志钩子，等等，具体的看看</span><span>MSDN</span><span>，这些钩子各有各的用途，对于黑客们来说主要会用到消息钩子，键盘钩子与日志钩子，这些钩子都可以用来监控键盘，下面分别来说</span><span>.</span></p>
<p>&nbsp;</p>
<p><strong><span>(1)</span></strong><strong><span>往日黄花</span></strong><strong><span>—</span></strong><strong><span>键盘钩子</span></strong><strong></strong></p>
<p><span>不要迷恋哥，哥只是个传说。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>------</span><span>一脑残儿童说</span></p>
<p><span>键盘钩子在当年可是相当的辉煌，在那个</span><span>Rootkit</span><span>还不是很盛行的年代，各种盗号软件</span></p>
<p><span>几乎总是和他联系在一起的，只不过这几年由于所谓的主动防御杀软的出现，这种技术才慢慢的消失</span><span>.</span></p>
<p><span>键盘钩子分为全局钩子与局部钩子。键盘钩子安装之后可以截获所进程的键盘信息。局部钩子只可以截获安装线程的键盘信息。既然要盗号吗，当然是</span><span>IC</span><span>卡，</span><span>IQ</span><span>卡统统告诉我密码，要大面积撒网，就要安装全局钩子。</span></p>
<p><span>键盘钩子也分为两种：普通的键盘钩子与低级键盘钩子。对于这两种钩子的区别我自己在编程中总结的是：低级键盘钩子可以截获一些系统按键，比如</span><span>Windows</span><span>健，但是普通的就不行了。我曾经写了个玩</span><span>Dota</span><span>时屏蔽</span><span>Windows</span><span>健的用的是低级钩子，普通的不行</span><span>.</span><span>如果只是拦截个一般的按键两种钩子无所谓了</span><span>.</span></p>
<p><span>具体的编程很简单</span><span>:</span><span>调用</span><span>SetWindwosHookEx</span><span>函数</span><span>,</span><span>参数就填入</span><span> WH_KEYBORAD</span><span>（普通钩子）或者</span><span>WH_KEYBOARD_LL</span><span>（低级钩子），然后写个钩子的回调函数，在回调函数里面就可以获取按键的虚拟键码了，在讲虚拟键码经过处理就得到我们想要的了</span><span>.</span></p>
<p><span>再提一点就是关于</span><span>SHIFT</span><span>键状态与</span><span>Caps</span><span>与</span><span>Num</span><span>状态的检测，只要调用</span><span>GetKeyState</span><span>函数就可以了，具体的不说了，自己看</span><span>MSDN</span><span>吧。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>（</span></strong><strong><span>2</span></strong><strong><span>）竹林蹊径</span></strong><strong><span>—</span></strong><span><strong><span>日志钩子</span></strong></span><span><strong></strong></span></p>
<p><span><strong>&nbsp;</strong></span></p>
<p><span><span>日志钩子是用来拦截输入到系统消息队列中的输入消息的钩子，键盘消息既然属于输入消息，那就勾住吧。</span></span><span></span></p>
<p><span><span>用法也是</span></span><span><span>so easy</span></span><span><span>，调用</span></span><span>SetWindwosHookEx</span><span>函数传递</span><span><span>WH_JOURNALRECOR</span></span><span><span>D</span></span><span><span>参数给他</span></span><span><span>,</span></span><span><span>在他的回调函数里面有个指向</span></span><span>EVENTMSG</span><span>的指针，结构如下：</span></p>
<p><span>typedef struct {</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>UINT message;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>UINT paramL;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>UINT paramH;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>DWORD time;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>HWND hwnd;</span></p>
<p><span>} EVENTMSG, *PEVENTMSG;</span></p>
<p><span>我们只拦截</span><span>message ==WM_KEYDOWN</span><span>的消息，就是按键按下的消息啦，然后</span><span>paramL&amp;0x000000FF</span><span>的值就是虚拟键码，剩下的和键盘钩子就一样了，不说了，下一节吧。</span></p>
<p>&nbsp;</p>
<p><strong><span>（</span></strong><strong><span>3</span></strong><strong><span>）完美的世界</span></strong><strong><span>---</span></strong><strong><span>中英文记录的消息钩子</span></strong><strong></strong></p>
<p><strong>&nbsp;</strong></p>
<p><span>到目前为止我们记录到得都只是些</span><span> abc123</span><span>这些的字母数字，那位小朋友要说了，我要知道他在网上的聊天内容，行吗？消息钩子就站出来了</span><span>,&#8221;no problem,</span><span>记录中文俺拿手啊</span></p>
<p><span>消息钩子，见名知意，肯定是用来过滤消息的。我们先来了解一个概念</span><span>&#8221;IME&#8221;&#8221;.</span></p>
<p><span>　</span><span>IME </span><span>是输入法编辑器</span><span>(Input Method Editor) </span><span>的英文缩写</span><span>(IME)</span><span>，它是一种专门的应用程序，用来输入代表东亚地区书面语言文字的不同字符。</span></p>
<p><span>说白了</span><span>,</span><span>我们平时输入汉字时其实都是跟这个</span><span>IME</span><span>打交道的。</span><span>IME</span><span>也是会发出很多的消息的，如</span></p>
<p><span>WM_IME_CHAR<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>WM_IME_COMPOSITION</span></p>
<p><span>WM_IME_COMPOSITIONFULL<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>WM_IME_CONTROL</span></p>
<p><span>WM_IME_ENDCOMPOSITION<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>WM_IME_KEYDOWN</span></p>
<p><span>WM_IME_KEYUP<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>WM_IME_NOTIFY</span></p>
<p><span>WM_IME_REQUEST<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>WM_IME_SELECT</span></p>
<p><span>WM_IME_SETCONTEXT<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>WM_IME_STARTCOMPOSITION</span></p>
<p>&nbsp;</p>
<p><span>我们现在主要关心一个消息</span><span>WM_IME_COMPOSITION</span><span>，就是当要拼出一个字的时候会发出这个消息</span><span>.</span><span>，并且副参数为</span><span>GCS_RESULTSTR</span><span>的时候，就说明输入完了，可以将拼出的句子读出来了，这就得到了汉字了</span><span>.,</span><span>下面为参考代码</span><span>:</span></p>
<p>&nbsp;</p>
<p><span>/* this code from ZWELL </span><span>获得输入法处理后的字符串</span><span> */</span></p>
<p><span>if(pmsg-&gt;message==WM_IME_COMPOSITION){</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;DWORD dwSize;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;char lpstr[128];</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;if(pmsg-&gt;lParam &amp; GCS_RESULTSTR){</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;//</span><span>先获取当前正在输入的窗口的输入法句柄</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;hIMC = ImmGetContext(hWnd);</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;if(!hIMC)&nbsp;return 0;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;// </span><span>先将</span><span>ImmGetCompositionString</span><span>的获取长度设为</span><span>0</span><span>来获取字符串大小</span><span>.</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;dwSize = ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, 0);</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;// </span><span>缓冲区大小要加上字符串的</span><span>NULL</span><span>结束符大小</span><span>,</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;// </span><span>考虑到</span><span>UNICODE</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;dwSize += sizeof(WCHAR);</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;memset(lpstr, 0, sizeof(lpstr));</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;// </span><span>再调用一次</span><span>.ImmGetCompositionString</span><span>获取字符串</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;ImmGetCompositionString(hIMC, GCS_RESULTSTR, lpstr, dwSize);</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;//</span><span>现在</span><span>lpstr</span><span>里面即是输入的汉字了。你可以处理</span><span>lpstr,</span><span>当然也可以保存为文件</span><span>...</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;//MessageBox(NULL, lpstr, lpstr, MB_OK); </span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>其实在输入汉字的时候也是会发出</span><span>WM_CHAR</span><span>与</span><span>WM_KEYDOWN</span><span>这些消息的，只不过</span><span>WM_CHAR</span><span>的参数与输入英文是是不同的。汉字的输入实际上是两个</span><span>WM_CHAR</span><span>，用内码就可以判断是否输入的是否是汉字字符。如果是，汉字两个字节的最高位都是</span><span>1</span><span>，连续两次判断就可以做到。即每次的</span><span>CHAR</span><span>字符的最高位是否是</span><span>1</span><span>，如果是，记住这个字符，然后当下</span><span>CHAR</span><span>字符来到是，如果最高位还是</span><span>1</span><span>，就可以将这两个字符合成汉字。这样就可以记录一个汉字了。</span></p>
<p><span>至于</span><span>WM_KEYDOWN</span><span>可以用来记录非</span><span>ASCII</span><span>的按键，像</span><span>F1—F12</span><span>，</span><span>TAB</span><span>，</span><span>ENTER</span><span>等等。</span></p>
<p><span>这样就是中英文完美的键盘记录了</span><span>.</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>(4)</span></strong><strong><span>用泥巴胡个墙吧</span></strong><strong><span>.---</span></strong><strong><span>应用层的抗击</span></strong><strong></strong></p>
<p><strong>&nbsp;</strong></p>
<p><span>应用层键盘记录的小伎俩就说这几种吧（呵呵，俺也就会这几种）</span><span>,</span><span>既然要攻防统一，那我们就来谈谈如何来防御吧。在应用层防御个人感觉很是鸡肋的，实现也很是麻烦，效果也不好，不过还是说说吧，</span></p>
<p><span>首先说说全局键盘钩子吧，全局键盘钩子是不能独立存在的，他必须附加一个动态链接库文件，因为全局钩子是要监控所有的进程的，所以这个模块就要注入到其他的进程的地址空间中去，所以要写一个单独的模块。</span></p>
<p><span>我们知道在</span><span>Windows</span><span>下加载一个模块时使用的</span><span>API</span><span>是</span><span>LoadLibrary</span><span>函数，这个函数内部又会调用</span><span>LoadLibraryEx</span><span>函数，</span><span>windows</span><span>底层是</span><span>UINCODE</span><span>的，所以应该调用的是</span><span>LoadLibraryExW</span><span>。如果我们写的正常程序，如果调用了</span><span>LoadLibrary</span><span>那摩</span><span>LoadLibraryExW</span><span>函数的返回地址应该位于</span><span>Kernel32.dll</span><span>中，或者我们就是直接调用了</span><span>LoadLibraryExW</span><span>那摩返回地址应该位于我们的程序中。但是如果是被装了钩子后，当你按下一个健后，系统会下按键焦点程序的地址空间中加载黑客写的键盘记录模块，调用的是</span><span>LoadLibraryExW</span><span>，那摩这个函数的返回地址就不是以上的两种情况了，经我是实验是位于</span><span>user32.dll</span><span>中。哈哈，根据这一点我们就可以判断一个模块是否为非法加载模块了</span><span>.</span></p>
<p><span>原理就是这样啦。具体的实现要用到</span><span>APIHook</span><span>技术了。在《</span><span>Windwos</span><span>核心编程》中有简绍的。可以</span><span>HOOKIAT</span><span>也可以</span><span>InLineHook</span><span>，我用的是</span><span>InLineHokk</span><span>，在自己写的</span><span>HOOk</span><span>函数中首先获取</span><span>[ESP]</span><span>的值，这个就是返回值了，具体为什么应该都明白吧，不明白就找本汇编书好好补补吧。那摩就拿这个返回值去比较就可以了。简单吧。。</span></p>
<p><span>当然对于</span><span>HOOKAPI</span><span>你也可以用微软的那个</span><span>Hook</span><span>库，那就更简单了。</span></p>
<p>&nbsp;</p>
<p><span>只能检测是不够的，太被动了，我们应该主动出击。</span></p>
<p><span>Windows</span><span>下的钩子逻辑上是一个链状的，一个系统中可以安装很多的钩子，这些钩子会形成一个钩子链，先装的钩子在最前头，前面的钩子通过调用</span><span>CallNextHookEx</span><span>函数将信息传给后面的钩子，如不不调用这个函数那摩链子就断了，后面的钩子永远互惠获取信息。</span></p>
<p><span>好了，聪明的你一定想到了。对，我们不往下传递信息，我们自己处理，让下面的钩子瞪着眼着急去吧。</span></p>
<p><span>具体做法为：我们在我们的程序中装上局部钩子，在局部钩子的回调函数中我们截获按键消息，我们自己存起来，然后再给密码框发个假消息，比如按下了</span><span>A</span><span>健，我们用我们的局部钩子截获了</span><span>A</span><span>健消息，我们保存起来，然后我们给密码框发个假消息，，就说我们接受到了个</span><span>B</span><span>健，然后让不调用那个</span><span>CallNextHookEx</span><span>函数，而是直接返回</span><span>1</span><span>，这样下面的钩子就</span><span>game over </span><span>了</span><span>.</span></p>
<p>&nbsp;</p>
<p><span>好了，应用层的键盘监控与反监控就说这些吧，由于杀毒软件的发展，特别是所谓的牛</span><span>X</span><span>主动防御的出现，这些都已进了历史的垃圾堆了，现在是</span><span>RootKit</span><span>的时代，打劫也要讲与时俱进，下面我们就来看看</span><span>ring0</span><span>下的键盘记录的手段吧。</span></p>
<p>&nbsp;</p>
<p><strong><span>3.</span></strong><strong><span>新的战场，新的战斗</span></strong><strong><span>—rootkit</span></strong><strong><span>的疯狂</span></strong><strong></strong></p>
<p>&nbsp;</p>
<p><span>人都是属驴的，不打不逼是不会走的</span><span>.</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>----------</span><span>俺的一位老师说的</span></p>
<p><span>在</span><span>ring3</span><span>下猥琐黑客们的安逸被被杀软打破了，生存还是死亡。当然是要生存下去了，怎没办？</span><span>&#8221;TMD,</span><span>反了，我们要和杀软对着干</span><span>&#8221;</span><span>。游戏规则被改变了。。黑客软件不在是只做老鼠被杀软这只大猫到处撵这跑，老鼠要吃猫啦。</span></p>
<p>&nbsp;</p>
<p><strong><span>4.</span></strong><strong><span>中规中矩</span></strong><strong><span>----</span></strong><strong><span>键盘过滤驱动</span></strong><strong></strong></p>
<p><strong>&nbsp;</strong></p>
<p><span>既然到了内核的领地，那我们就来看看在内核中是如何处理按键消息的，我们从按下一个健到我们在</span><span>WORD</span><span>看到这个字母，究竟发生了什么</span><span>,</span><span>下面是网上说的</span><span>:</span></p>
<p>&nbsp;</p>
<p><span>/*</span><span>引用自</span><span>: http://hi.baidu.com/buzztiger/blog/item/a851712b<st1:chmetcnv w:st="on" UnitName="C" SourceValue="3739" HasSpace="False" Negative="False" NumberType="1" TCSC="0">3739c</st1:chmetcnv>924d52af170.html */</span></p>
<p>&nbsp;</p>
<p><span>写过</span><span>windows</span><span>程序的人都知道，</span><span>win32</span><span>程序是基于消息驱动的，其中就有键盘消息，这个消息其实是</span><span>csrss.exe</span><span>这个进程发送给应用程序的，而在应用程序中我们可以使用</span><span>setWindowsHook</span><span>的方法来获得键盘消息，从而实现改键啊，捕捉用户按键内容。那么</span><span>csrss.exe</span><span>这个进程的键盘消息是怎么来的呢？原来</span><span>csrss.exe</span><span>中有个</span><span>win32!RawInputThread</span><span>这个线程，这个线程通过一个</span><span>GUID</span><span>，即</span><span>GUID_CLASS_KEYBOARD</span><span>（</span><span>DEFINE_GUID(GUID_CLASS_KEYBOARD, 0x884b96c3, 0x56ef, 0x11d1, 0xbc, 0x8c, 0x00, 0xa0, 0xc9, 0x14, 0x05, 0xdd)</span><span>来获得键盘设备栈中</span><span>PDO</span><span>的符号链接名。</span><span>win32!RawInputThread</span><span>执行到</span><span>win32k!openDevice</span><span>，调用</span><span>zwCreateFile</span><span>打开设备，然后调用</span><span>zwReadFile</span><span>与键盘驱动通信了。它会创建一个</span><span>IRP_MJREAD</span><span>的</span><span>IRP</span><span>发送给键盘驱动，而键盘驱动通常使这个</span><span>IRP Pending</span><span>，这样它就会一直被放在那里等待，等来来自键盘的数据，即</span><span>win32!RawInputThread</span><span>这个线程也会一直等待，等待这个读操作的完成。当键盘有键按下时这个</span><span>IRP</span><span>将会完成，</span><span>win32!RawInputThread</span><span>将对得到的数据进行处理，分发给合适的进程（通常是获得焦点的进程）这时</span><span>win32!RawInputThread</span><span>又会立即再调用</span><span>nt!ZwReadFile</span><span>要求读入数据，又开始了下一个等待，周而复始</span></p>
<p>&nbsp;</p>
<p><span>/*</span><span>引用结束</span><span>*/</span></p>
<p>&nbsp;</p>
<p><span>键盘的驱动栈从上到下依次为</span><span>:kbdclass.sys---</span><span><span>&#224;</span></span><span>i8042port.sys---</span><span><span>&#224;</span></span><span>acpi.sys</span></p>
<p><span>其中</span><span>kbdclass.sys</span><span>为键盘的类驱动，不管是</span><span>PS/2</span><span>键盘还是</span><span>USB</span><span>键盘都要通过这一层驱动，所以在这一层进行过滤可以有和好的兼容性。</span></p>
<p><span>I8042port.sys</span><span>为</span><span>PS/2</span><span>键盘的端口驱动，这个只对</span><span>PS/2</span><span>键盘好用，</span><span>USB</span><span>键盘他管不了的。</span></p>
<p><span>对于键盘的过滤驱动，我选择是在</span><span>kbdclass.sys</span><span>进行过滤</span><span>.</span></p>
<p><span>具体做法：</span></p>
<p><span>1.</span><span>使用</span><span>ObReferenceObjectByName</span><span>获取</span><span>&#8221;\\Driver\\Kbdclass&#8221;</span><span>所对应的驱动对象。</span></p>
<p><span>2.</span><span>枚举这个驱动对象下的所有设备，并创建一个过滤设备附加上去</span><span>.</span></p>
<p><span>3.</span><span>主要处理</span><span>IRP_MJ_READ</span><span>这个</span><span>IRP</span><span>。首先设置一个完成函数，然后向下转发此</span><span>IRP</span><span>。</span></p>
<p><span>4.</span><span>在完成函数中就可以获取此次的案件的扫描码了。</span></p>
<p><span>5.</span><span>对于</span><span>IRP_MJ_POWER, IRP_MJ_PNP</span><span>也要进行处理</span><span>.</span></p>
<p>&nbsp;</p>
<p><span>呵呵，很简单吧，驱动入门级的</span><span>Hello Wolrd</span><span>。</span></p>
<p>&nbsp;</p>
<p><strong><span>5.</span></strong><strong><span>乾坤大挪移</span></strong><strong><span>----HOOKIDT</span></strong><strong><span>与操纵</span></strong><strong><span>APIC</span></strong></p>
<p><strong>&nbsp;</strong></p>
<p><span>在前面我们讲到在按下一个键与抬起的时候会触发一个硬件中断，</span><span>XP</span><span>下，操作系统回去调用</span><span> 0x31</span><span>或</span><span>0x93</span><span>中断处理程序区处理。那么我们可不可以自己写一个</span><span>ISR</span><span>去接管键盘中断呢</span><span>,</span><span>？当然，别忘了，我们是在内核中现在，我们无所不能</span> <span>。</span></p>
<p><span>我们知道在保护模式下是采用</span><span>IDT</span><span>进行中断管理的，</span><span>IDT</span><span>是有许多门组成的。每个闷得结构如下</span><span>:</span></p>
<p><span>typedef struct IDTEntry</span></p>
<p><span>{</span></p>
<p><span>HB_U16 LowOffset ;//</span><span>偏移的低</span><span>16</span><span>位</span></p>
<p><span>HB_U16 Selector ;//</span><span>选择子</span></p>
<p><span>HB_U8&nbsp;Count:5; //</span><span>参数的双字计数</span></p>
<p><span>HB_U8&nbsp;Reserve:3 ;//</span><span>保留为</span><span>0</span></p>
<p><span>HB_U8 Type:4 ;//</span><span>类型</span></p>
<p><span>HB_U8 DT0:1; //DT=0,</span><span>系统段描述符</span></p>
<p><span>HB_U8 DPL:2; //DLP</span></p>
<p><span>HB_U8 P:1; //P</span><span>位</span></p>
<p><span>HB_U16 HightOffset;//</span><span>偏移的高</span><span>16</span><span>位</span></p>
<p><span>}IDTEntry,*PIDTEntry;</span></p>
<p><span>其中</span><span>Lowoffset</span><span>与</span><span>HighOffset</span><span>就构成了实际的中断处理程序的地址</span><span>.</span><span>。我们如果自己写一个中断处理程序，然后修改</span><span>Lowoffset</span><span>与</span><span>HighOffset</span><span>，让其指向我们自己的写的那个函数不久可以了吗</span><span>..</span></p>
<p><span>在我们自己的键盘中断处理函数中我们可以直接将数据从</span><span>i8042</span><span>的端口读出，存储起来，然后再调用原先的系统默认的函数，这样就神不知鬼不觉的达到的不可告人的目的</span><span>.</span></p>
<p>&nbsp;</p>
<p><span>还有一种方法使用的手段于此相似，也是替换，不过这次是将键盘的</span><span>IRQ1</span><span>中断的处理函数的中断向量更改，不在是指向</span><span>0x31</span><span>或是</span><span>0x93</span><span>了，而是指向另一个向量</span><span>.</span><span>这个向量包含我们自己的处理程序</span><span>.</span><span>，这就是</span><span>APIC</span><span>机制</span><span>.</span><span>。不过我的这个破笔记本比较老了，没有这个机制，</span></p>
<p><span>所以只能纸上谈兵了</span><span>.,</span><span>还是先简绍一下</span><span>APIC</span><span>吧</span><span>.</span></p>
<p><span>APIC</span><span>是可以用于多个核心的</span><span>CPU</span><span>的新型中断控制器</span><span>,APIC</span><span>的作用相当于当一个</span><span>IRQ</span><span>发生时，这个硬件决定将</span><span>IRQ</span><span>发个呢个</span><span>CPU</span><span>核心</span><span>,</span><span>以及一何种形式发送等。</span><span>APIC</span><span>是可编程的，也可以将</span><span>PS/2</span><span>键盘的硬件中断请求发给某个</span><span>CPU</span><span>核心，让该核心的</span><span>IDT</span><span>中的某个中断号对应的中断服务程序来处理</span><span>.</span></p>
<p><span>Windows</span><span>将</span><span>APIC</span><span>的系列寄存器映射到了地址</span><span>0xFEC00000</span><span>和</span><span>0xFEC00010</span><span>的位置。</span></p>
<p><span>也就是说我们可以通过编程来进行中断的重定位，具体操作看着</span><span>APIC</span><span>的说明添添数据就可以了，其实和</span><span>HOOKIDT</span><span>一样的。就不多说了</span><span>.</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>6.</span></strong><strong><span>返璞归真</span></strong><strong><span>—</span></strong><strong><span>轮询</span></strong><strong><span>i8042</span></strong></p>
<p>&nbsp;</p>
<p><span>有时候其实一个问题的解决方法并不是月复杂就越强悍，有时候简单的土的掉渣的技术反而是最稳固的</span></p>
<p><span>轮询，这种古老的技术，虽然由于效率低下已经早已被淘汰，但是我们需要的正是这个。</span></p>
<p><span>键盘是可以通过编程关闭中断的，但是当我们按下一个健的时候，键盘的输出缓冲区中仍然会有扫描码填充，只是中断关闭了，操作系统并不知道。</span></p>
<p><span>我们的做法是：首先关闭键盘的中断，然后通过轮询的方法读取输出端口的案件扫描码，自己进行一些处理，然后打开键盘中断，再将此按键重放，这样操作系统会获取这个按键，然后在关闭中断，一直这样循环下去。</span></p>
<p>&nbsp;</p>
<p><span>原理就是这样，简单也很可靠，我最终的密码保护就是采用这个方案的。</span></p>
<p>&nbsp;</p>
<p><span>我的程序分了三个层次</span><span>:</span></p>
<p><img height=214 alt="" src="http://www.cppblog.com/images/cppblog_com/xingkongyun/cenci.JPG" width=465 border=0><span></span></p>
<p><span>因为在</span><span>Ring3</span><span>下无法读写端口的，所以自己写了个驱动，负责读写端口，</span><span>RING3</span><span>上载写个</span><span>DLL</span><span>通过</span><span>DeviceIoControl</span><span>与内核进行通信，传递端口地址与设置的值等信息</span><span>,</span><span>并对上面的应用程序提供简单的如</span><span>READ_PORT(ULONG port),</span><span>这样的接口。</span></p>
<p>&nbsp;</p>
<p><span>在应用程序中，在要保护的密码框获取焦点时</span><span>(</span><span>处理</span><span>WM_SETFOCUS</span><span>消息</span><span>),</span><span>则关闭键盘中断进行轮询</span><span>,</span><span>在市区焦点时则打开键盘中断</span><span>.</span><span>在获取按键后你可以在程序中记录下来，再在密码框中填充个假的密码。</span></p>
<p><span>由于是不间断的轮询所以保证在第一时间获取扫描码，在上层的如过滤驱动，</span><span>HOOKIDT</span><span>等</span></p>
<p><span>还有应用层得到钩子啥的，统统失效，实践证明还是很可靠的，只不过还不是很完善，目前只支持</span><span>PS/2</span><span>键盘，</span><span>USB</span><span>的还不行</span><span>.</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>7.</span></strong><strong><span>我们都</span></strong><strong><span>OUT</span></strong><strong><span>了</span></strong><strong><span>----</span></strong><strong><span>硬件键盘记录器</span></strong><strong></strong></p>
<p><strong>&nbsp;</strong></p>
<p><span>日防夜防，家贼难防啊，要是硬件上做了手脚，那就这能</span><span>55555555555&#8230;&#8230;</span></p>
<p><span>看看这个猥琐的家伙吧</span><span>&#8230;</span><span>高科技哦</span></p>
<p><span><br clear=all></span></p>
<p>&nbsp;<img height=175 alt="" src="http://www.cppblog.com/images/cppblog_com/xingkongyun/hardware-keyloger.jpg" width=201 border=0></p>
<p>&nbsp;</p>
<p><span>好了，该结尾了，呵呵，现在你还信你的键盘吗？</span></p>
<img src ="http://www.cppblog.com/xingkongyun/aggbug/103691.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xingkongyun/" target="_blank">杨彬彬</a> 2009-12-22 13:56 <a href="http://www.cppblog.com/xingkongyun/archive/2009/12/22/103691.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SYSENTER指令相关（大段的转载-_-）</title><link>http://www.cppblog.com/xingkongyun/archive/2009/09/04/95329.html</link><dc:creator>杨彬彬</dc:creator><author>杨彬彬</author><pubDate>Fri, 04 Sep 2009 11:40:00 GMT</pubDate><guid>http://www.cppblog.com/xingkongyun/archive/2009/09/04/95329.html</guid><wfw:comment>http://www.cppblog.com/xingkongyun/comments/95329.html</wfw:comment><comments>http://www.cppblog.com/xingkongyun/archive/2009/09/04/95329.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xingkongyun/comments/commentRss/95329.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xingkongyun/services/trackbacks/95329.html</trackback:ping><description><![CDATA[<p>在 Intel 的软件开发者手册第二、三卷（Vol.2B,Vol.3）中，4.8.7 节是关于 sysenter/sysexit 指令的详细描述。手册中说明，sysenter </p>
<p>指令可用于特权级 3 的用户代码调用特权级 0 的系统内核代码，而 SYSEXIT 指令则用于特权级 0 的系统代码返回用户空间中。sysenter 指</p>
<p>令可以在 3，2，1 这三个特权级别调用（Linux 中只用到了特权级 3），而 SYSEXIT 指令只能从特权级 0 调用。</p>
<p>执行 sysenter 指令的系统必须满足两个条件：1.目标 Ring 0 代码段必须是平坦模式（Flat Mode）的 4GB 的可读可执行的非一致代码段。</p>
<p>2.目标 RING0 堆栈段必须是平坦模式（Flat Mode）的 4GB 的可读可写向上扩展的栈段。</p>
<p>在 Intel 的手册中，还提到了 sysenter/sysexit 和 int n/iret 指令的一个区别，那就是 sysenter/sysexit 指令并不成对，sysenter 指</p>
<p>令并不会把 SYSEXIT 所需的返回地址压栈，sysexit 返回的地址并不一定是 sysenter 指令的下一个指令地址。调用 sysenter/sysexit 指令</p>
<p>地址的跳转是通过设置一组特殊寄存器实现的。这些寄存器包括：</p>
<p>SYSENTER_CS_MSR － 用于指定要执行的 Ring 0 代码的代码段选择符，由它还能得出目标 Ring 0 所用堆栈段的段选择符；</p>
<p>SYSENTER_EIP_MSR － 用于指定要执行的 Ring 0 代码的起始地址；</p>
<p>SYSENTER_ESP_MSR－用于指定要执行的Ring 0代码所使用的栈指针</p>
<p>这些寄存器可以通过 wrmsr 指令来设置，执行 wrmsr 指令时，通过寄存器 edx、eax 指定设置的值，edx 指定值的高 32 位，eax 指定值的</p>
<p>低 32 位，在设置上述寄存器时，edx 都是 0，通过寄存器 ecx 指定填充的 MSR 寄存器，sysenter_CS_MSR、sysenter_ESP_MSR、</p>
<p>sysenter_EIP_MSR 寄存器分别对应 0x174、0x175、0x176，需要注意的是，wrmsr 指令只能在 Ring 0 执行。</p>
<p>这里还要介绍一个特性，就是 Ring0、Ring3 的代码段描述符和堆栈段描述符在全局描述符表 GDT 中是顺序排列的，这样只需知道 </p>
<p>SYSENTER_CS_MSR 中指定的 Ring0 的代码段描述符，就可以推算出 Ring0 的堆栈段描述符以及 Ring3 的代码段描述符和堆栈段描述符。</p>
<p>在 Ring3 的代码调用了 sysenter 指令之后，CPU 会做出如下的操作：</p>
<p>1． 将 SYSENTER_CS_MSR 的值装载到 cs 寄存器</p>
<p>2． 将 SYSENTER_EIP_MSR 的值装载到 eip 寄存器</p>
<p>3． 将 SYSENTER_CS_MSR 的值加 8（Ring0 的堆栈段描述符）装载到 ss 寄存器。</p>
<p>4． 将 SYSENTER_ESP_MSR 的值装载到 esp 寄存器</p>
<p>5． 将特权级切换到 Ring0</p>
<p>6． 如果 EFLAGS 寄存器的 VM 标志被置位，则清除该标志</p>
<p>7． 开始执行指定的 Ring0 代码</p>
<p>在 Ring0 代码执行完毕，调用 SYSEXIT 指令退回 Ring3 时，CPU 会做出如下操作：</p>
<p>1． 将 SYSENTER_CS_MSR 的值加 16（Ring3 的代码段描述符）装载到 cs 寄存器</p>
<p>2． 将寄存器 edx 的值装载到 eip 寄存器</p>
<p>3． 将 SYSENTER_CS_MSR 的值加 24（Ring3 的堆栈段描述符）装载到 ss 寄存器</p>
<p>4． 将寄存器 ecx 的值装载到 esp 寄存器</p>
<p>5． 将特权级切换到 Ring3</p>
<p>6． 继续执行 Ring3 的代码</p>
<p>由此可知，在调用 SYSENTER 进入 Ring0 之前，一定需要通过 wrmsr 指令设置好 Ring0 代码的相关信息，在调用 SYSEXIT 之前，还要保证</p>
<p>寄存器edx、ecx 的正确性。</p>
<p>&nbsp;</p>
<p>根据 Intel 的 CPU 手册，我们可以通过 CPUID 指令来查看 CPU 是否支持 sysenter/sysexit 指令，做法是将 EAX 寄存器赋值 1，调用 </p>
<p>CPUID 指令，寄存器 edx 中第 11 位（这一位名称为 SEP）就表示是否支持。在调用 CPUID 指令之后，还需要查看 CPU 的 Family、Model、</p>
<p>Stepping 属性来确认，因为据称 Pentium Pro 处理器会报告 SEP 但是却不支持 sysenter/sysexit 指令。只有 Family 大于等于 6，Model </p>
<p>大于等于 3，Stepping 大于等于 3 的时候，才能确认 CPU 支持 sysenter/sysexit 指令。</p>
<p>&nbsp;</p>
<p>/=============================================================================<br>//在WINDBG中对NTDLL.dll中的NtCreateFile函数的调试信息<br>ntdll!NtCreateFile:<br>7c92d682 b825000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; eax,25h<br>7c92d687 ba0003fe7f&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; edx,offset SharedUserData!SystemCallStub (7ffe0300)<br>7c92d68c ff12&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp; dword ptr [edx]<br>7c92d68e c22c00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret&nbsp;&nbsp;&nbsp;&nbsp; 2Ch</p>
<p><br>lkd&gt; dd 7ffe0300<br>7ffe0300&nbsp; 7c92eb8b 7c92eb94 00000000 00000000</p>
<p><br>lkd&gt; u 7c92eb8b<br>ntdll!KiFastSystemCall:<br>7c92eb8b 8bd4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; edx,esp<br>7c92eb8d 0f34&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sysenter</p>
<p>/**************************************************************/<br>&nbsp; <br>SYSENTER简介及相关例子<br>&nbsp;<br>&nbsp;<br>文章作者：wowocock1/CVC.GB</p>
<p>;众所周知微软自XP后引进了FASTCALL SYSENTER，SYSEXIT来代替WIN2K下INT2E系统服务调用<br>;其优点是快速而且没有保留堆栈的开销，为了便于大家理解我写下面一个在WIN98下的例子<br>;来说明一下这2条指令的用法。ITNEL的手册上关于他们介绍的很详细，我简要说明一下<br>;SYSENTER是INTEL自P2后引进的快速从RING3~RING0的FASTCALL，从FAMILY 6，MODEL 3，<br>;STEP 3也就是从PII300以后引进的，这也是为什么WINXP需要PII300以上的原因。在使用SYSENTER<br>;之前必须定义好RING0 CS EIP ESP，通过设置相应MSR寄存器,由WRMSR指令来设定（必须在RING0层执行）;<br>;通过将相应的寄存器地址号放入ECX中，WRMSR可以设置这些MSR寄存器,对应关系如下<br>;SYSENTER_CS_MSR 174H SYSENTER_ESP_MSR 175H SYSENTER_EIP_MSR 176H<br>;执行SYSENTER指令的系统必须满足 1：转换后的RING0代码段必须是FLAT，4GB的可读可执行<br>;的非一致代码段.2:转换后的RING0堆栈段必须是FLAT，4GB的可读可写向上扩展的数据段<br>;由于FASTCALL不保存任何返回的地址，所以在调用前你必须自己设定好，RING0代码段SELECTOR<br>;RING0堆栈段SELECTOR，RING3代码段SELECTOR，RING3堆栈段SELECTOR，必须在GDT中连续的排列<br>;所以在XP下相应的SELECTOR，必然是8H，10H，1BH，23H，必须将返回至RING3 EIP,ESP通过寄存器<br>;传递进RING0以便SYSEXIT返回使用，在SYSEXIT返回之前，EDX为RING3 EIP，ECX为RING3 ESP<br>;而相应的CS，SS，则由RING0 CS加上10H，18H来返回<br>;RING3~RING0<br>;1. 装载SYSENTER_CS_MSR 到CS 寄存器.<br>;2. 装载SYSENTER_EIP_MSR到 EIP寄存器。 <br>;3. SYSENTER_CS_MSR+8 装载到SS寄存器 <br>;4.装载SYSENTER_ESP_MSR 到ESP寄存器。 <br>;5. 切换RING0.<br>;6. 清除 EFLAGS的 VM标志<br>;7. 执行RING0例程<br>;RING0~RING3<br>;1。SYSENTER_CS_MSR+16装载到 CS寄存器<br>;2. 将EDX的值送入EIP<br>;3. SYSENTER_CS_MSR+24 装载到SS寄存器 <br>;4. 将ECX的值送入ESP <br>;5.切换回RING3<br>;6. 执行EIP处的RING3指令<br>;下面的例子在示范的基础上加了个小TRICK，就是在通过CALLGATE进RING0设置MSR寄存器的同时<br>;关掉了你机器上的缓存，然后你可以看看在没有缓存的情况下你的感觉如何，然后点击一下<br>;对话框，则经由SYSENTER指令进入RING0设定好的地址处恢复你CPU缓存，所以别担心，还有<br>;没有缓存的时候你的动作最好慢一点，不然会让你等的发疯的，呵呵。<br>.686p<br>.model flat,stdcall<br>option casemap:none</p>
<p>include \masm32\include\windows.inc<br>include \masm32\include\kernel32.inc<br>include \masm32\include\user32.inc</p>
<p>includelib \masm32\lib\kernel32.lib<br>includelib \masm32\lib\user32.lib</p>
<p>sysenter macro<br>db 0fh,34h<br>endm</p>
<p>sysexit macro<br>db 0fh,35h<br>endm</p>
<p>CR0_CD EQU 040000000h ; Cache Disable bit of CR0<br>CR0_NW EQU 020000000h ; Not Write-through bit of CR0 <br>.data<br>Ring0Cs dw 0ffffh,0,09b00h,0cfh<br>Ring0Ss dw 0ffffh,0,09300h,0cfh<br>Ring3Cs dw 0ffffh,0,0fb00h,0cfh<br>Ring3Ss dw 0ffffh,0,0f300h,0cfh</p>
<p>trR dw ?<br>tssRing0Esp dd ?<br>GdtLimit dw ?<br>GdtAddr dd ?</p>
<p>Callgt dq 0 ;call gate&#8217;s selff<br>tmpCs dw ?<br>szTitle db "CPU info",0<br>msg db 100 dup (?)<br>Nightmare db "切换到其他窗口，尝尝没CACHE的滋味!",0</p>
<p>.code<br>Start:<br>mov ax,ds<br>test ax,4<br>jz Exit;winnt<br>xor eax,eax<br>cpuid<br>lea edi,msg<br>xchg eax,ebx<br>stosd<br>xchg eax,edx<br>stosd<br>xchg eax,ecx<br>stosd<br>invoke MessageBoxA,0,addr msg,addr szTitle,0<br>mov eax,1<br>cpuid<br>test edx,800h<br>jz Exit<br>mov eax,2<br>cpuid<br>SetSel:<br>sgdt GdtLimit<br>str word ptr trR ;存储任务寄存器<br>;----------------------- <br>; get the tr mes <br>;----------------------- <br>movzx esi,trR<br>add esi,GdtAddr&nbsp;&nbsp; ;ESi指向GDT中TSS描述副<br>mov eax,[esi+2] <br>and eax,0ffffffh <br>mov ebx,[esi+4] <br>and ebx,0ff000000h <br>or eax,ebx&nbsp;&nbsp;&nbsp; ;eax中保存TSS的基地址</p>
<p>push dword ptr[eax+4]<br>pop dword ptr [tssRing0Esp] ;保存RING0使用的堆栈地址</p>
<p><br>movzx eax,GdtLimit ;在GDT的最后选取四个表目将预设的4个描述符存入<br>test al,1<br>jz @f<br>inc eax<br>@@:<br>sub eax,4*8<br>mov tmpCs,ax<br>add eax,GdtAddr<br>lea esi,Ring0Cs<br>mov edi,eax<br>mov ecx,4*8<br>rep movsb</p>
<p>SetMsr:<br>;------------------------------------- <br>; 在GDT中寻找空白表项来制造调用门 <br>;------------------------------------- <br>mov esi,GdtAddr<br>movzx eax,GdtLimit <br>call Search_XDT <br>;esi==gdt Base <br>mov esi,dword ptr GdtAddr <br>push offset Ring0_SetMsr <br>pop word ptr [esi+eax+0] <br>pop word ptr [esi+eax+6] ;Offset </p>
<p>mov word ptr [esi+eax+2],28h <br>mov word ptr [esi+eax+4],0EC00h ;sel=28h and attribute -&gt;386 call gate! </p>
<p>and dword ptr Callgt,0 </p>
<p>mov word ptr [Callgt+4],ax <br>pushad<br>call fword ptr [Callgt] ;Ring0! <br>popad<br>mov dword ptr [esi+eax+0],0<br>mov dword ptr [esi+eax+4],0</p>
<p>invoke MessageBoxA,0,addr Nightmare,addr Nightmare,0 <br>lea edx,Exit<br>mov ecx,esp<br>sysenter</p>
<p>Exit: <br>push 00000000h ; Exit program<br>call ExitProcess</p>
<p>;----------------------------------------------------------------------</p>
<p>Ring0_SetMsr:<br>mov ecx,174h<br>movzx eax,tmpCs<br>wrmsr<br>inc ecx<br>mov eax,tssRing0Esp<br>wrmsr<br>inc ecx<br>lea eax,Ring0Ip<br>wrmsr</p>
<p>mov eax,cr0 ; read CR0<br>or eax,CR0_CD ; set CD but not NW bit of CR0<br>mov cr0,eax ; cache is now disabled<br>wbinvd ; flush and invalidate cache</p>
<p>; the cache is effectively disabled at this point, but memory<br>; consistency will be maintained. To completely disable cache,<br>; the following two lines may used as well:</p>
<p>or eax,CR0_NW ; now set the NW bit<br>mov cr0,eax ; turn off the cache entirely</p>
<p>retf</p>
<p>;----------------------------------------------------------------------<br>Ring0Ip:<br>pushad</p>
<p>pushf ; save the flags<br>cli ; disable interrupts while we do this<br>mov eax,cr0 ; read CR0<br>and eax,0dfffffffh ; now set the NW bit<br>mov cr0,eax ; turn on the cache entirely</p>
<p>and eax,0bfffffffh ; set CD but not NW bit of CR0<br>mov cr0,eax ; cache is now Ensabled </p>
<p>popf ; restore the flags</p>
<p>mov eax,cr0<br>mov [esp+4*7],eax<br>popad<br>sysexit</p>
<p>;----------------------------------------------------------------------</p>
<p>Search_XDT proc near <br>;entry esi==Base of Ldt or GDT ;Eax==Limit </p>
<p>pushad <br>mov ebx,eax <br>mov eax,8 ; skipping null selector <br>@@1: <br>cmp dword ptr [esi+eax+0],0 <br>jnz @@2 <br>cmp dword ptr [esi+eax+4],0 <br>jz @@3 <br>@@2: <br>add eax,8 <br>cmp eax,ebx <br>jb @@1 ;if we haven&#8217;t found any free GDT entry, <br>;lets use the last two entries <br>mov eax,ebx <br>sub eax,7 <br>@@3: <br>mov [esp+4*7],eax ; return off in eax <br>popad <br>ret <br>Search_XDT endp <br>end Start</p>
<p><br>;=======================================================================================================<br>1&nbsp;&nbsp; 【原创】rootkit hook之[六] -- sysenter Hook&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>--------------------------------------------------------------------------------</p>
<p>标 题: 【原创】rootkit hook之[六] -- sysenter Hook<br>作 者: combojiang<br>时 间: 2008-02-26,12:25<br>链 接: <a href="http://bbs.pediy.com/showthread.php?t=60247">http://bbs.pediy.com/showthread.php?t=60247</a></p>
<p>呵呵，今天这篇内容少，比较简单。</p>
<p>SYSENETER是一条汇编指令，它是在Pentium? II 处理器及以上处理器中提供的，是快速系统调用的一部分。SYSENTER/SYSEXIT这对指令专门用于实现快速调用。在这之前是采用INT 0x2E来实现的。INT 0x2E在系统调用的时候，需要进行栈切换的工作。由于Interrupt/Exception Handler的调用都是通过 call/trap/task这一类的gate来实现的，这种方式会进行栈切换，并且系统栈的地址等信息由TSS提供。这种方式可能会引起多次内存访问 （来获取这些切换信息），因此，从PentiumII开始，IA-32引入了新指令：SYSENTER/SYSEXIT。 有了这两条指令，<br>从用户级到特权级的堆栈以及指令指针的转换，可以通过这一条指令来实现，并且，需要切换到的新堆栈的地址，以及相应过程的第一条指令的位置，都有一组特殊寄存器来实现，这类特殊寄存器在IA-32中称为MSR(Model Specific Register)。这里牵涉到3个特殊寄存器： <br>SYSENTER_CS_MSR: New code segment selector&nbsp;&nbsp; 0x174 <br>SYSENTER_ESP_MSR: New Stack Pointer&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x175 <br>SYSENTER_EIP_MSR: New Instruction Pointer&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x176 <br>这里标出的3个16进制数分别对应这3个寄存器的地址，该地址用于Kernel debug时，通过rdmsr/wrmsr指令来读/写这3个寄存器。步骤如下：<br>10.JPG<br>1. 装载SYSENTER_CS_MSR 到CS 寄存器，设置目标代码段<br>2. 装载SYSENTER_EIP_MSR到 EIP寄存器，设置目标指令 <br>3. SYSENTER_CS_MSR+8 装载到SS寄存器 ，设置栈段<br>4. 装载SYSENTER_ESP_MSR 到ESP寄存器，设置栈帧 <br>5. 切换RING0. <br>6. 清除 EFLAGS的 VM标志 <br>7. 执行RING0例程 </p>
<p>11.JPG<br>1. SYSENTER_CS_MSR+16装载到 CS寄存器 <br>2. 将EDX的值送入EIP <br>3. SYSENTER_CS_MSR+24 装载到SS寄存器 <br>4. 将ECX的值送入ESP <br>5. 切换回RING3 <br>6. 执行EIP处的RING3指令 </p>
<p>我们在windbg中可以看看这个三个寄存器的情况，这个是我机器里的情况。<br>lkd&gt; rdmsr 176<br>msr[176] = 00000000`8053dad0<br>lkd&gt; rdmsr 175<br>msr[175] = 00000000`ba4e0000<br>lkd&gt; rdmsr 174<br>msr[174] = 00000000`00000008</p>
<p>可以看到，我的机器里面当前SYSENTER_EIP_MSR，SYSENTER_ESP_MSR，SYSENTER_CS_MSR这三个寄存器的值。</p>
<p>我们在微软公开的内核WRK中发现关于这三个寄存器的设置，其中SYSENTER_EIP_MSR设置的值是KiFastCallEntry。<br>代码如下：<br>VOID<br>KiLoadFastSyscallMachineSpecificRegisters(<br>&nbsp;&nbsp;&nbsp; IN PLONG Context<br>&nbsp;&nbsp;&nbsp; )</p>
<p>/*++</p>
<p>Routine Description:</p>
<p>&nbsp;&nbsp;&nbsp; Load MSRs used to support Fast Syscall/return.&nbsp; This routine is<br>&nbsp;&nbsp;&nbsp; run on all processors.</p>
<p>Arguments:</p>
<p>&nbsp;&nbsp;&nbsp; None.</p>
<p>Return Value:</p>
<p>&nbsp;&nbsp;&nbsp; None.</p>
<p>--*/</p>
<p>{<br>&nbsp;&nbsp;&nbsp; PKPRCB Prcb;</p>
<p>&nbsp;&nbsp;&nbsp; UNREFERENCED_PARAMETER (Context);</p>
<p>&nbsp;&nbsp;&nbsp; if (KiFastSystemCallIsIA32) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Prcb = KeGetCurrentPrcb();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Use Intel defined way of doing this.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WRMSR(MSR_SYSENTER_CS,&nbsp; KGDT_R0_CODE);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WRMSR(MSR_SYSENTER_EIP, (ULONGLONG)(ULONG)KiFastCallEntry);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WRMSR(MSR_SYSENTER_ESP, (ULONGLONG)(ULONG)Prcb-&gt;DpcStack);</p>
<p>&nbsp;&nbsp;&nbsp; }<br>}</p>
<p>看看我电脑的情况如下：<br>lkd&gt; rdmsr 176<br>msr[176] = 00000000`8053dad0<br>lkd&gt; u 8053dad0<br>nt!KiFastCallEntry:<br>8053dad0 b923000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; ecx,23h<br>8053dad5 6a30&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; 30h<br>8053dad7 0fa1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pop&nbsp;&nbsp;&nbsp;&nbsp; fs<br>8053dad9 8ed9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; ds,cx<br>8053dadb 8ec1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; es,cx<br>8053dadd 8b0d40f0dfff&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; ecx,dword ptr ds:[0FFDFF040h]<br>8053dae3 8b6104&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; esp,dword ptr [ecx+4]<br>8053dae6 6a23&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; 23h</p>
<p>下面是rootkit.com上的一个例子，这个例子有点不厚道，在你卸载的时候会bsod.我简单修改了下，贴代码如下：<br>#include "ntddk.h"</p>
<p>ULONG d_origKiFastCallEntry; // Original value of ntoskrnl!KiFastCallEntry</p>
<p>VOID OnUnload( IN PDRIVER_OBJECT DriverObject )<br>{<br>&nbsp;&nbsp;&nbsp; _asm<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov ecx, 0x176<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xor edx,edx<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov eax, d_origKiFastCallEntry&nbsp;&nbsp;&nbsp;&nbsp; // Hook function address<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wrmsr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Write to the IA32_SYSENTER_EIP register<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>}</p>
<p>// Hook function<br>__declspec(naked) MyKiFastCallEntry()<br>{<br>&nbsp; __asm {<br>&nbsp;&nbsp;&nbsp; jmp [d_origKiFastCallEntry]<br>&nbsp; }<br>}</p>
<p>NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath )<br>{<br>&nbsp; theDriverObject-&gt;DriverUnload&nbsp; = OnUnload; </p>
<p>&nbsp; __asm {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov ecx, 0x176<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rdmsr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // read the value of the IA32_SYSENTER_EIP register<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov d_origKiFastCallEntry, eax<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov eax, MyKiFastCallEntry&nbsp;&nbsp;&nbsp;&nbsp; // Hook function address<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wrmsr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Write to the IA32_SYSENTER_EIP register<br>&nbsp; }</p>
<p>&nbsp; return STATUS_SUCCESS;<br>}</p>
<p>注意一点，大家用windbg的时候，配置symbol path,如图：<br>9.JPG</p>
<p>后面贴上一篇堕落天才写的文章链接：<a href="http://bbs.pediy.com/showthread.php?t=42705">http://bbs.pediy.com/showthread.php?t=42705</a>，<br>他inline hook 了KiFastCallEntry，采用detour方式,写得很不错。 </p>
<p>&nbsp;</p>
<p><br>上传的附件 SysEnterHook.rar (973 字节, 743 次下载)&nbsp; <br>&nbsp; <br>;=====================================================================================================================<br>&nbsp;&nbsp; 【原创】另一种sysenter hook方法(绕过绝大多数的rootkit检测工具的检测)&nbsp;&nbsp;&nbsp; </p>
<p>--------------------------------------------------------------------------------</p>
<p>标 题: 【原创】另一种sysenter hook方法(绕过绝大多数的rootkit检测工具的检测)<br>作 者: 堕落天才<br>时 间: 2007-04-14,11:09<br>链 接: <a href="http://bbs.pediy.com/showthread.php?t=42705">http://bbs.pediy.com/showthread.php?t=42705</a></p>
<p>*****************************************************************************<br>*标题:【原创】另一种sysenter hook方法(绕过绝大多数的rootkit检测工具的检测)&nbsp; *<br>*作者:堕落天才&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *<br>*日期:2007年4月14号&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *<br>*****************************************************************************</p>
<p>&nbsp;&nbsp;&nbsp; 先废话,当初是为了绕开NP对sysenter保护而想出来的,后来发现连RootkitUnhooker都绕了.</p>
<p>&nbsp;&nbsp;&nbsp; 什么是sysenter hook我也不罗唆了,一般的拦截方法就是通过rdmsr wrmsr 两个指令把原来的sysenter地址改成自己的sysenter地址来实现的.这种方法使用方便,但检测也很容易.<br>&nbsp;&nbsp;&nbsp; 这里介绍的另外一种方法不改变sysenter地址,而是通过直接在原来sysenter地址里面写跳转代码来实现的,这实际上跟一般的函数头inline hook一样.这样rootkit检测工具就不会认为sysenter已经改变(实际上也是没变).<br>&nbsp;&nbsp;&nbsp; 一般的rootkit检测工具检测函数inline hook是通过检测长跳转指令0xE9的来判断跳转距离是不是超出函数所在的模块范围来确定的.但是实现跳转我们也可以借助寄存器或变量(用变量跳转需要涉及重定位问题,麻烦.所以一般用寄存器),这样跳转指令就不是0xE9了而是0xFF,这个绝大多数rootkit检测工具是检测不到的(包括著名的RootkitUnhooker,VICE).</p>
<p>&nbsp;&nbsp;&nbsp; 由于我们已经改变了KiFastCall函数头,所以我们只能把原来的函数头代码放到另外一个地方执行(动态分配内存,当然如果不考虑兼容性硬编码也没问题),然后再跳转回来.这里使用了"三级跳",大概是这个样子.<br>&nbsp;&nbsp;&nbsp; sysenter-&gt;KiFastCall<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JMP -&gt; MyKiFastCall(这里进行拦截或什么的)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JMP -&gt; KiFastCall head code (这里执行原来KiFastCall函数头代码)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JMP -&gt; KiFastCall + N(已经执行指令长度)<br>///////////////////////////////////////////////////////////////////////////////////////////////////&nbsp; <br>//堕落天才<br>//2007年4月14日<br>#include&lt;ntddk.h&gt;<br>#include "OpCodeSize.h"</p>
<p>ULONG uSysenter;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //sysenter地址<br>UCHAR uOrigSysenterHead[8];//保存原来的八个字节函数头<br>PUCHAR pMovedSysenterCode; //把原来的KiFastCall函数头保存在这里<br>ULONG i;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //记录服务ID<br>__declspec(naked) void MyKiFastCallEntry(void)<br>{<br>&nbsp; __asm{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pop&nbsp; edi&nbsp;&nbsp;&nbsp;&nbsp; //因为用到了edi来跳转 这里恢复<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp; i, eax&nbsp; //得到服务ID<br>&nbsp; }<br>&nbsp; __asm{&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pushad<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push fs<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push 0x30<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pop fs<br>&nbsp; }<br>&nbsp; <br>&nbsp; DbgPrint("sysenter was hooked! Get service ID:%X",i); //证明自己存在</p>
<p>&nbsp; __asm{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pop fs<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; popad&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; jmp pMovedSysenterCode //第二跳,跳转到原来的函数头代码 <br>&nbsp; }<br>&nbsp; <br>}<br>//////////////////////////////////////////////////////<br>VOID OnUnload(IN PDRIVER_OBJECT DriverObject)<br>{&nbsp;&nbsp;&nbsp; <br>&nbsp; __asm{<br>&nbsp;&nbsp;&nbsp; cli<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp; eax,cr0<br>&nbsp;&nbsp;&nbsp; and&nbsp; eax,not 10000h<br>&nbsp;&nbsp;&nbsp; mov&nbsp; cr0,eax<br>&nbsp; }</p>
<p>&nbsp; memcpy((PVOID)uSysenter,uOrigSysenterHead,8);//把原来函数头的八个字节恢复</p>
<p>&nbsp; __asm{<br>&nbsp;&nbsp;&nbsp; mov&nbsp; eax,cr0<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; or&nbsp;&nbsp; eax,10000h<br>&nbsp;&nbsp;&nbsp; mov&nbsp; cr0,eax<br>&nbsp;&nbsp;&nbsp; sti<br>&nbsp; }<br>&nbsp; ExFreePool(pMovedSysenterCode); // 释放分配的内存<br>&nbsp; DbgPrint("Unload sysenterHook");<br>}<br>////////////////////////////////////////////////////////</p>
<p>VOID HookSysenter()<br>{<br>&nbsp; UCHAR&nbsp; cHookCode[8] = { 0x57,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //push edi&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第一跳,从KiFastCall跳到MyKiFastCallEntry.并绕过rootkit检测工具检测<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0xBF,0,0,0,0,&nbsp; //mov&nbsp; edi,0000<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0xFF,0xE7};&nbsp;&nbsp;&nbsp; //jmp&nbsp; edi</p>
<p>&nbsp; UCHAR&nbsp; JmpCode[]={0xE9,0,0,0,0};&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //jmp 0000 第三跳,从KiFastCall函数头代码跳转到原来KiFastCall+N</p>
<p>&nbsp; int&nbsp;&nbsp;&nbsp; nCopyLen = 0;<br>&nbsp; int&nbsp;&nbsp;&nbsp; nPos = 0;</p>
<p>&nbsp; __asm{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov ecx,0x176<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rdmsr<br>&nbsp;&nbsp;&nbsp; mov uSysenter,eax&nbsp; //得到KiFastCallEntry地址<br>&nbsp; }<br>&nbsp; DbgPrint("sysenter:0x%08X",uSysenter);</p>
<p>&nbsp; nPos = uSysenter;<br>&nbsp;&nbsp; while(nCopyLen&lt;8){ //我们要改写的函数头至少需要8字节 这里计算实际需要COPY的代码长度 因为我们不能把一条完整的指令打断<br>&nbsp;&nbsp;&nbsp; nCopyLen += GetOpCodeSize((PVOID)nPos);&nbsp; //参考1<br>&nbsp;&nbsp;&nbsp; nPos = uSysenter + nCopyLen;<br>&nbsp; }<br>&nbsp; <br>&nbsp; DbgPrint("copy code lenght:%d",nCopyLen);</p>
<p>&nbsp; pMovedSysenterCode = ExAllocatePool(NonPagedPool,20);</p>
<p>&nbsp; memcpy(uOrigSysenterHead,(PVOID)uSysenter,8);//备份原来8字节代码</p>
<p>&nbsp; *((ULONG*)(JmpCode+1)) = (uSysenter + nCopyLen) - ((ULONG)pMovedSysenterCode + nCopyLen)- 5;//计算跳转地址</p>
<p>&nbsp; memcpy(pMovedSysenterCode,(PVOID)uSysenter,nCopyLen); //把原来的函数头放到新分配的内存<br>&nbsp; memcpy((PVOID)(pMovedSysenterCode + nCopyLen),JmpCode,5); //把跳转代码COPY上去</p>
<p>&nbsp; *((ULONG*)(cHookCode+2)) = (ULONG)MyKiFastCallEntry; //HOOK地址<br>&nbsp; <br>&nbsp; DbgPrint("Saved sysenter code:0x%08X",pMovedSysenterCode);<br>&nbsp; DbgPrint("MyKiFastCallEntry:0x%08X",MyKiFastCallEntry);</p>
<p>&nbsp; __asm{<br>&nbsp;&nbsp;&nbsp; cli<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp; eax,cr0<br>&nbsp;&nbsp;&nbsp; and&nbsp; eax,not 10000h<br>&nbsp;&nbsp;&nbsp; mov&nbsp; cr0,eax<br>&nbsp; }</p>
<p>&nbsp; memcpy((PVOID)uSysenter,cHookCode,8);//把改写原来函数头</p>
<p>&nbsp; __asm{<br>&nbsp;&nbsp;&nbsp; mov&nbsp; eax,cr0<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; or&nbsp;&nbsp; eax,10000h<br>&nbsp;&nbsp;&nbsp; mov&nbsp; cr0,eax<br>&nbsp;&nbsp;&nbsp; sti<br>&nbsp; }</p>
<p>}</p>
<p>NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)<br>{</p>
<p>&nbsp; DbgPrint("Welcome to sysenterhook.sys");<br>&nbsp; DriverObject-&gt;DriverUnload = OnUnload;<br>&nbsp; HookSysenter();<br>&nbsp; return STATUS_SUCCESS;<br>}&nbsp;&nbsp;&nbsp; <br>/////////////////////////////////////////////////////////////////////////////////////////////////// <br>以上代码在 XP SP2中文 + RootkitUnhooker下测试通过 </p>
<p>同理 IDT hook也可以用这种方法实现,HOOK的实质是改变程序流程,无论在哪里改变<br>*************************************************************************************************<br>参考1, 海风月影,【分享】西裤哥的 Hook Api Lib 0.2 For C&nbsp; </p>
<p>;http://bbs.pediy.com/showthread.php?p=420864<br>&nbsp;<br>&nbsp;<br></p>
<img src ="http://www.cppblog.com/xingkongyun/aggbug/95329.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xingkongyun/" target="_blank">杨彬彬</a> 2009-09-04 19:40 <a href="http://www.cppblog.com/xingkongyun/archive/2009/09/04/95329.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>NASM 与 VC 混合编程的小结</title><link>http://www.cppblog.com/xingkongyun/archive/2009/04/07/79219.html</link><dc:creator>杨彬彬</dc:creator><author>杨彬彬</author><pubDate>Tue, 07 Apr 2009 14:53:00 GMT</pubDate><guid>http://www.cppblog.com/xingkongyun/archive/2009/04/07/79219.html</guid><wfw:comment>http://www.cppblog.com/xingkongyun/comments/79219.html</wfw:comment><comments>http://www.cppblog.com/xingkongyun/archive/2009/04/07/79219.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/xingkongyun/comments/commentRss/79219.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xingkongyun/services/trackbacks/79219.html</trackback:ping><description><![CDATA[<br><br>
<div class=Section0 style="LAYOUT-GRID:  15.6pt none">
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><font face=宋体>总结一下:</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><font face=宋体>对于NASM与VC混合编程</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><font face=宋体>在ASM文件的开头输入</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">[bits&nbsp;32]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;<font face=宋体>使用32位模式的处理器</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">[section&nbsp;.text]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;text<font face=宋体>段,&nbsp;代码段,只读并可执行</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><font face=宋体>声明代码段并且代码段是32位,应为对于VC生成的PE文件，其代码放在.text的代码段内</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><font face=宋体>，数据放在.data的数据段内,所以如果要定义数据的话，要在.data段内</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">{<font face=宋体>一}&nbsp;在C代码中调用汇编程序</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">1.</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">.<font face=宋体>如果遵守C&nbsp;调用约定</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><font face=宋体>打比方我们在ASM中实现两个数相加的函数:</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">_myadd:</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">push&nbsp;ebp</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">mov&nbsp;ebp,esp</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">mov&nbsp;eax,[ebp+8]&nbsp;&nbsp;;a</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">mov&nbsp;ebx,[ebp+12]&nbsp;&nbsp;&nbsp;&nbsp;;b</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">add&nbsp;eax,ebx</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">add&nbsp;esp,4</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">mov&nbsp;esp,ebp</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">pop&nbsp;ebp</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">ret&nbsp;</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><font face=宋体>在ASM的开头写上global&nbsp;_myadd;</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><font face=宋体>在C文件中声明函数&nbsp;extern&nbsp;"C"&nbsp;int&nbsp;_cdecl&nbsp;myadd(int&nbsp;a,int&nbsp;b);&nbsp;</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><font face=宋体>最主要的是要在ASM的函数名称的前面加上一个&nbsp;_(下划线)&nbsp;,但是在C文件中声明的函数不用加下划线,并且一定要加上extern&nbsp;"C",并且用_cdecl&nbsp;声明，这样以后就可以在C中调用ASM中的函数了。</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">2.</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><font face=宋体>如果遵守stdcall&nbsp;调用约定</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><font face=宋体>还用上面的那个两个数相加的例子</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">_myaddstdcall@8</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">:</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">push&nbsp;ebp</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">mov&nbsp;ebp,esp</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">mov&nbsp;eax,[ebp+8]&nbsp;&nbsp;;a</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">mov&nbsp;ebx,[ebp+12]&nbsp;&nbsp;&nbsp;&nbsp;;b</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">add&nbsp;eax,ebx</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">add&nbsp;esp,4</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">mov&nbsp;esp,ebp</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">pop&nbsp;ebp</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">ret&nbsp;8</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><font face=宋体>在ASM文件的开头写上&nbsp;global&nbsp;</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">_myaddstdcall@8</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><font face=宋体>在C文件中声明函数&nbsp;extern&nbsp;"C"&nbsp;int&nbsp;_stdcall&nbsp;myaddstdcall(int&nbsp;a,int&nbsp;b);&nbsp;</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><font face=宋体>这里要注意的是函数的名称问题,一般是_XXX@N&nbsp;,也就是在开头加上一个下划线,@N中的N跟参数的大小有关系，目前我的理解是所有参数在堆栈中占用的总的大小.但是不确定，我只是推测的，如果那位知道具体的规定一定要告我一声啊。</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">{<font face=宋体>二}&nbsp;在汇编中调用C中的函数</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">1.</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><font face=宋体>遵守C调用约定</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><font face=宋体>举个例子:</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">extern&nbsp;"C"&nbsp;void&nbsp;_cdecl&nbsp;myprint(int&nbsp;a)</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">{</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">printf("myprint&nbsp;%d\n",a);</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"> </span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">}</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><font face=宋体>在ASM中声明&nbsp;extern&nbsp;_myprint&nbsp;,然后就可以用&nbsp;push&nbsp;xxx&nbsp;，call&nbsp;myprint&nbsp;&nbsp;&nbsp;,&nbsp;add&nbsp;esp&nbsp;,4&nbsp;调用了</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><font face=宋体>。要注意的是call&nbsp;调用完后，一定要加上add&nbsp;esp&nbsp;,X&nbsp;来平衡堆栈,应为C调用约定规定是调用者平衡堆栈.</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">2.</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><font face=宋体>遵守stdcall&nbsp;调用约定</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">extern&nbsp;"C"&nbsp;void&nbsp;_stdcall&nbsp;&nbsp;myprint</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">stdcall</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">(</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">int&nbsp;a</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">)</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">{</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">printf("myprintstdcall&nbsp;%</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">d</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">\n",a);</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'">}</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><font face=宋体>在ASM中声明extern&nbsp;_myprintstdcall@4&nbsp;,然后用push&nbsp;xxx&nbsp;&nbsp;,call&nbsp;_myprintstdcall@4&nbsp;调动，这里就不用再加add&nbsp;esp，X了，有函数本身平衡堆栈.</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
<p class=p0 style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt; TEXT-ALIGN: justify"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><font face=宋体>就这些了，都是个人总结，难免有不足与错误，还请大家指正.</font></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: '宋体'; mso-spacerun: 'yes'"><o:p></o:p></span></p>
</div>
<!--endfragment-->
<img src ="http://www.cppblog.com/xingkongyun/aggbug/79219.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xingkongyun/" target="_blank">杨彬彬</a> 2009-04-07 22:53 <a href="http://www.cppblog.com/xingkongyun/archive/2009/04/07/79219.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VC++内联汇编(MSDN相关内容完整翻译)</title><link>http://www.cppblog.com/xingkongyun/archive/2008/12/21/70003.html</link><dc:creator>杨彬彬</dc:creator><author>杨彬彬</author><pubDate>Sun, 21 Dec 2008 09:07:00 GMT</pubDate><guid>http://www.cppblog.com/xingkongyun/archive/2008/12/21/70003.html</guid><wfw:comment>http://www.cppblog.com/xingkongyun/comments/70003.html</wfw:comment><comments>http://www.cppblog.com/xingkongyun/archive/2008/12/21/70003.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/xingkongyun/comments/commentRss/70003.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xingkongyun/services/trackbacks/70003.html</trackback:ping><description><![CDATA[<p>VC++内联汇编，将MSDN里面关于内联汇编的几乎全翻译了，一上午的成果啊，哪翻译错了，告我一声啊。。。</p>
<p><br>【一】.在 __asm block中使用汇编语言</p>
<p>1.关键字__asm调用内联汇编语句<br>有三种方式可用<br>（1）__asm block 形式<br>例子：<br>// asm_overview.cpp<br>// processor: x86<br>void __declspec(naked) main()<br>{<br>&nbsp;&nbsp;&nbsp; // Naked functions must provide their own prolog...<br>&nbsp;&nbsp;&nbsp; __asm {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push ebp<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov ebp, esp<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sub esp, __LOCAL_SIZE<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; // ... and epilog<br>&nbsp;&nbsp;&nbsp; __asm {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pop ebp<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret<br>&nbsp;&nbsp;&nbsp; }<br>}</p>
<p>（2）将__asm放在每句汇编指令的开头<br>例子：<br>__asm push ebp<br>__asm mov&nbsp; ebp, esp<br>__asm sub&nbsp; esp, __LOCAL_SIZE</p>
<p>（3）应为__asm 也是一个语句分隔符，所以可以将汇编指令放在同一行上<br>例子：<br>__asm push ebp&nbsp;&nbsp; __asm mov&nbsp; ebp, esp&nbsp;&nbsp; __asm sub&nbsp; esp, __LOCAL_SIZE</p>
<p>&nbsp;</p>
<p>2.内联汇编指令集<br>VC++编译器支持Pentium 4 和 AMD Athlon的所有指令，额外的被其他目标处理器支持的指令<br>能够被创造用_emit 伪指令。<br>附：_emit 伪指令说明<br>_emit伪指令的MASM的DB指令相似，你能够使用_emit在代码段（text segment）的当前位置<br>去定义一个字节的立即数。_emit 一次只能定义一个字节，并且仅仅能够再代码段（text segment）<br>内定义。</p>
<p>例子：<br>#define randasm __asm _emit 0x4A __asm _emit 0x43 __asm _emit 0x4B<br>&nbsp;.<br>&nbsp;.<br>&nbsp;.<br>__asm {<br>&nbsp;&nbsp;&nbsp;&nbsp; randasm<br>&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>3.再内联汇编中的MASM表达式<br>内联汇编能够使用任何的MASM的表达式，能够使任何操作数和操作码的组合。</p>
<p>4.内联汇编中的数据指令和操作<br>尽管__asm block能够引用C/C++的数据类型和对象(object)，但是他不能定义数据对象用MASM的指令和操作,尤其，不能使用</p>
<p>DB,DW,DD,DQ,DT,DF 或者DUP,THIS。MASM中结构体和记录类型也是不可用的，内联汇编不接受STRUC,RECORD,WIDTH,MASK操作.</p>
<p>5.EVEN 和 ALIGN 指令<br>尽管内联汇编不支持大多数MASM的指令，但是支持EVEN 和 ALIGN 指令，这两个指令填充NOP在汇编代码中去对其数据和指定的边界,这样能够</p>
<p>CUP的数据访问更加高效.</p>
<p>6.内联汇编中的MASM宏指令<br>内联汇编不支持MAsm中的宏指令（MACRO, REPT, IRC, IRP, ENDM）或者宏操作符(&lt;&gt;, !, &amp;, %, .TYPE)。</p>
<p>7.内联汇编中的段引用<br>再内联汇编中指定一个段只能通过寄存器，而不能通过名字（例如，段名_TEXT是不可用的），段超越必须显式的使用寄存器，如ES:[BX].</p>
<p>8.内联汇编中的类型和变量尺寸问题<br>LENGTH, SIZE 和 TYPE 操作符有一个限定的意义再内联汇编中，他们不能被使用和DUP一起(因为再内联汇编中不能使用DUP命令),但是能够使</p>
<p>用他们去得到C/C++变量的尺寸和类型.<br>*LENGTH 操作符返回一个数组的元素数目，非数组变量返回1.<br>*Size 操作符返回C/C++变量的尺寸，一个变量的尺寸是LENGTH与TYPE相乘的结果.<br>*TYPE 操作符返回C/C++类型或变量的尺寸，如果是一个数组变量返回数组中单个元素的TYPE.</p>
<p>例子：<br>int arr[8];<br>__asm&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Size&nbsp; <br>LENGTH arr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sizeof(arr)/sizeof(arr[0])&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8 <br>SIZE arr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sizeof(arr)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 32<br>TYPE arr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sizeof(arr[0])&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4</p>
<p>9.内联汇编的注释问题<br>再__asm block 中可以使用汇编语言的注释<br>例子：<br>__asm mov ax, offset buff ; Load address of buff</p>
<p>【二】.再__asm block 中使用C/C++<br>概述：应为内联汇编能够与C/C++语句混合使用，他梦能够使用C/C++的变量通过名字,还有C/C++语言的其他元素.<br>*符号，包括标号，变量，函数名.<br>*常量，包括符号常量和枚举（enum）<br>*宏，预处理命令<br>*注释（包括 /**/和//）<br>*类型名称<br>*typedef名称，一般都和PTR和TYPE一起使用或者去指定结构体或联合成员</p>
<p>1.在 __asm block 中使用操作符<br>再 __asm block 中不能使用 C/C++特有的操作符，例如&lt;&lt;。C/C++与汇编共用的操作符，如*，是被解释为汇编操作符。<br>举个例子来说，[]操作符在C语言里被解释为数组的下标， C能够自动的转换数组元素的尺寸,解释为首地址+单个元素的长度*方括号内的值.<br>但是再__asm block中，他被看做 MASM索引操作符(index operator),解释为首地址+方括号中的值.<br>下面的实例显示了他们的不同。<br>int array[10];</p>
<p>__asm mov array[6], bx ;&nbsp; Store BX at array+6 (not scaled)</p>
<p>array[6] = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Store 0 at array+24 (scaled) */</p>
<p>能够使用TYPE操作符去达到和C同样的效果<br>__asm mov array[6 * TYPE int], 0 ; Store 0 at array + 24</p>
<p>array[6] = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Store 0 at array + 24 */</p>
<p>2.使用C/C++符号在__asm block 中<br>__asm块能够引用 C/C++在作用域中的符号（包括变量名，函数名，标号,不能调用C++的成员函数）</p>
<p>在使用C/C++符号时有一些限制:<br>*每条汇编语句仅仅能够包含一个C/C++的符号。在LENGTH, TYPE, 和 SIZE表达式中则可以使用多个C/C++符号。<br>*在__asm block中函数引用必须先声明。否则编译器不能区别在__asm block 中的标号与函数名.<br>*不能使用与MASM保留字相同的符号名称（无论大小写）。<br>*结构体和联合类型不能别识别在__asm block中.</p>
<p>3.访问C/C++数据在__asm block中<br>在内联汇编中通过名称访问C/C++变量是十分方便的。在__asm block中能访问任何在作用域中符号。<br>例如，在其作用域中有一个C变量 var， __asm MOV EAX,var 存储var的值在EAX中。</p>
<p>如果一个类，结构体或者联合结构的成员是唯一的，在__asm block中引用他仅仅使用成员变量名，<br>而不用使用变量名或者typedef名在.操作符之前。如果成员名不是唯一的，无论如何，必须放置变量名或者typedef名在.操作符之前。<br>例子：<br>// InlineAssembler_Accessing_C_asm_Blocks.cpp<br>// processor: x86<br>#include &lt;stdio.h&gt;<br>struct first_type<br>{<br>&nbsp;&nbsp; char *weasel;<br>&nbsp;&nbsp; int same_name;<br>};<br>struct second_type<br>{<br>&nbsp;&nbsp; int wonton;<br>&nbsp;&nbsp; long same_name;<br>};<br>int main()<br>{<br>&nbsp;&nbsp; struct first_type hal;<br>&nbsp;&nbsp; struct second_type oat;<br>&nbsp;&nbsp; __asm<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lea ebx, hal<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov ecx, [ebx]hal.same_name ; Must use 'hal'<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov esi, [ebx].weasel&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; Can omit 'hal'<br>&nbsp;&nbsp; }<br>&nbsp;&nbsp; return 0;<br>}</p>
<p>在 __asm block中能够访问C++ 的数据成员而不用去遵守访问限制，但是不能调用C++的成员函数.</p>
<p>4.使用内联汇编写函数<br>略。没啥好讲的，直接看例子<br>int power2( int num, int power )<br>{<br>&nbsp;&nbsp; __asm<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov eax, num&nbsp;&nbsp;&nbsp; ; Get first argument<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov ecx, power&nbsp; ; Get second argument<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; shl eax, cl&nbsp;&nbsp;&nbsp;&nbsp; ; EAX = EAX * ( 2 to the power of CL )<br>&nbsp;&nbsp; }<br>&nbsp;&nbsp; // Return with result in EAX<br>}</p>
<p>5.使用和保存寄存器在内联汇编中<br>一般来说，不应该假设寄存器将会有一个指定的值在__asm blok块开始时，寄存器的值不保证在离开了一个__asm block后被保存,如果你离开</p>
<p>了一个asm块并开始了另一个asm块，不应该应用在上一个块中保存寄存器的值。An __asm block inherits whatever register values result </p>
<p>from the normal flow of control.<br>如果使用__fastcall调用约定，编译器传递参数使用寄存器而不是堆栈，这可能产生一个问题在应用了__asm block的函数中，因为函数无法知</p>
<p>道那个参数是在寄存器中。如果一个函数接受参数在EAX中，但是过后别立刻用来存储其他的值，那摩这个原始的参数就丢失了。并且，在</p>
<p>__fastcall约定中，必须保存ECX寄存器的值。<br>去避免如此的寄存器冲突，不要使用__fastcall调用约定为那些包含__asm block的函数,如果使用/Gr编译器选项指定了全局的__fastcall约定</p>
<p>，那摩定义每个包含__asm block的函数用_stdcall或__cdecl。<br>当使用__asm去写汇编语句在C/C++中，不需要去保存EAX,EBX,ECX,EDX,ESI,EDI。在使用EBX,ESI,EDI时，你强迫编译器去保存并回复这些寄存</p>
<p>器的值在函数的序言与结尾处.<br>也应该保存使用的其他寄存器（如DS,SS,SP,BP,EFLAGS）对于这个__asm block的作用域.<br>也应该保存ESP和EBP除非你有其他的改变他们的原因。（例如，堆栈转换）</p>
<p>下面这段不太好翻译，自己看吧：<br>Some SSE types require eight-byte stack alignment, forcing the compiler to emit dynamic stack-alignment code. To be able to </p>
<p>access both the local variables and the function parameters after the alignment, the compiler maintains two frame pointers. </p>
<p>If the compiler performs frame pointer omission (FPO), it will use EBP and ESP. If the compiler does not perform FPO, it will </p>
<p>use EBX and EBP. To ensure code runs correctly, do not modify EBX in asm code if the function requires dynamic stack </p>
<p>alignment as it could modify the frame pointer. Either move the eight-byte aligned types out of the function, or avoid using </p>
<p>EBX.<br>注意:如果在__asm block中改变了方向标志，通过STD,CLD，那摩就要保存这些标志的原始值.</p>
<p>6.在内联汇编中跳转到指定标号<br>像一般的 C/C++标号，在__asm block有函数作用域（在整个函数中可见，而不仅仅是在定义的__asm block中）,汇编指令与goto语句都能跳到</p>
<p>标号处.<br>定义在__asm block中的标号不是大小写敏感的，goto语句与汇编指令能够引用整个标号而不用考虑大小写。但是C/C++代码中的标号是大小写</p>
<p>敏感的当使用goto语句时,使用汇编语句不用考虑大小写问题.<br>例子：<br>void func( void )<br>{<br>&nbsp;&nbsp; goto C_Dest;&nbsp; /* Legal: correct case&nbsp;&nbsp; */<br>&nbsp;&nbsp; goto c_dest;&nbsp; /* Error: incorrect case */</p>
<p>&nbsp;&nbsp; goto A_Dest;&nbsp; /* Legal: correct case&nbsp;&nbsp; */<br>&nbsp;&nbsp; goto a_dest;&nbsp; /* Legal: incorrect case */</p>
<p>&nbsp;&nbsp; __asm<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jmp C_Dest ; Legal: correct case<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jmp c_dest ; Legal: incorrect case</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jmp A_Dest ; Legal: correct case<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jmp a_dest ; Legal: incorrect case</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a_dest:&nbsp;&nbsp;&nbsp; ; __asm label<br>&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp; C_Dest:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* C label */ <br>&nbsp;&nbsp; return;<br>}<br>int main()<br>{<br>}</p>
<p>在__asm block中不要使用C库的函数名作为标号名称。<br>&nbsp;BAD TECHNIQUE: using library function name as label<br>jne exit<br>&nbsp;&nbsp; .<br>&nbsp;&nbsp; .<br>&nbsp;&nbsp; .<br>exit:<br>&nbsp;&nbsp; ; More __asm code follows</p>
<p>在MASM中（$）符号作为当前的地址计数（current location counter）。他是当前正在被编译的指令的标号.在__asm block 中他的主要作用</p>
<p>是去作为一个长的条件跳转.<br>jne $+5 ; next instruction is 5 bytes long<br>jmp farlabel<br>; $+5<br>&nbsp;&nbsp; .<br>&nbsp;&nbsp; .<br>&nbsp;&nbsp; .<br>farlabel:</p>
<p>【三】.在内联汇编中调用C函数<br>一个__asm block能够调用C函数，包括C库函数。下面是调用printf的例子：<br>// InlineAssembler_Calling_C_Functions_in_Inline_Assembly.cpp<br>// processor: x86<br>#include &lt;stdio.h&gt;</p>
<p>char format[] = "%s %s\n";<br>char hello[] = "Hello";<br>char world[] = "world";<br>int main( void )<br>{<br>&nbsp;&nbsp; __asm<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp; eax, offset world<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push eax<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp; eax, offset hello<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push eax<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp; eax, offset format<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push eax<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; call printf<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //clean up the stack so that main can exit cleanly<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //use the unused register ebx to do the cleanup<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pop&nbsp; ebx<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pop&nbsp; ebx<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pop&nbsp; ebx<br>&nbsp;&nbsp; }<br>}</p>
<p>【四】.定义__asm block作为宏<br>C语言的宏提供了一个简便的方式去插汇编代码进入源代码。但是那需要额外的小心因为宏被扩展到一个单独的逻辑行上（a single logical </p>
<p>line），去创建无错误的宏，应遵守下列规则:<br>*用{}包围__asm block<br>*放__asm 关键字在每条汇编指令的开头<br>*使用老式的注释(/**/)代替汇编中的注释(;)和单行注释(//).</p>
<p>例子:<br>#define PORTIO __asm&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \<br>/* Port output */&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \<br>{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \<br>&nbsp;&nbsp; __asm mov al, 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \<br>&nbsp;&nbsp; __asm mov dx, 0xD007&nbsp;&nbsp; \<br>&nbsp;&nbsp; __asm out dx, al&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \<br>}</p>
<p>一个__asm block写的宏能够带参数，但是不能返回值，因此不要使用这样的宏在C/C++表达式中.</p>
<p>【五】.内联汇编的优化问题<br>__asm block的存在会对优化产生一些影响。首先，编译器不会尝试去优化__asm block中的指令。第二，__asm block会对寄存器变量的存储产</p>
<p>生影响，编译器会避免去登记穿越__asm block的那些寄存器会被修改的变量.</p>
<img src ="http://www.cppblog.com/xingkongyun/aggbug/70003.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xingkongyun/" target="_blank">杨彬彬</a> 2008-12-21 17:07 <a href="http://www.cppblog.com/xingkongyun/archive/2008/12/21/70003.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>通过虚函数表访问私有虚函数</title><link>http://www.cppblog.com/xingkongyun/archive/2008/11/03/65881.html</link><dc:creator>杨彬彬</dc:creator><author>杨彬彬</author><pubDate>Mon, 03 Nov 2008 12:43:00 GMT</pubDate><guid>http://www.cppblog.com/xingkongyun/archive/2008/11/03/65881.html</guid><wfw:comment>http://www.cppblog.com/xingkongyun/comments/65881.html</wfw:comment><comments>http://www.cppblog.com/xingkongyun/archive/2008/11/03/65881.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.cppblog.com/xingkongyun/comments/commentRss/65881.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xingkongyun/services/trackbacks/65881.html</trackback:ping><description><![CDATA[<p>&nbsp;</p>
<p>今天在网上看了一篇关于C++虚函数表的文章，让我对C++又有了更深了的理解，文章链接：<a href="http://www.51cto.com/art/200712/62673_2.htm">http://www.51cto.com/art/200712/62673_2.htm</a></p>
<p>在这篇文章中讲到了通过虚函数表访问私有的虚函数的问题，问题就出在C++在虚函数表中保存了虚函数的地址，而这个地址又很方便查找。<br>在每个C++对象实例的开头存储这这个对象的虚函数表的指针，通过这个指针可找到虚函数表，在虚函数表中就存着虚函数指针，这样我们就可以骗过编译器访问私有的虚函数了。<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">&nbsp;1</span><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;2</span><span style="COLOR: #000000"><img id=Codehighlighter1_11_65_Open_Image onclick="this.style.display='none'; Codehighlighter1_11_65_Open_Text.style.display='none'; Codehighlighter1_11_65_Closed_Image.style.display='inline'; Codehighlighter1_11_65_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_11_65_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_11_65_Closed_Text.style.display='none'; Codehighlighter1_11_65_Open_Image.style.display='inline'; Codehighlighter1_11_65_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_11_65_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_11_65_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;3</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:<br></span><span style="COLOR: #008080">&nbsp;4</span><span style="COLOR: #000000"><img id=Codehighlighter1_39_63_Open_Image onclick="this.style.display='none'; Codehighlighter1_39_63_Open_Text.style.display='none'; Codehighlighter1_39_63_Closed_Image.style.display='inline'; Codehighlighter1_39_63_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_39_63_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_39_63_Closed_Text.style.display='none'; Codehighlighter1_39_63_Open_Image.style.display='inline'; Codehighlighter1_39_63_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;f()</span><span id=Codehighlighter1_39_63_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_39_63_Open_Text><span style="COLOR: #000000">{cout</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">base_f()</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">endl;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;5</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">&nbsp;6</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;child:</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;7</span><span style="COLOR: #000000"><img id=Codehighlighter1_92_188_Open_Image onclick="this.style.display='none'; Codehighlighter1_92_188_Open_Text.style.display='none'; Codehighlighter1_92_188_Closed_Image.style.display='inline'; Codehighlighter1_92_188_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_92_188_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_92_188_Closed_Text.style.display='none'; Codehighlighter1_92_188_Open_Image.style.display='inline'; Codehighlighter1_92_188_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_92_188_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_92_188_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;8</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:<br></span><span style="COLOR: #008080">&nbsp;9</span><span style="COLOR: #000000"><img id=Codehighlighter1_120_145_Open_Image onclick="this.style.display='none'; Codehighlighter1_120_145_Open_Text.style.display='none'; Codehighlighter1_120_145_Closed_Image.style.display='inline'; Codehighlighter1_120_145_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_120_145_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_120_145_Closed_Text.style.display='none'; Codehighlighter1_120_145_Open_Image.style.display='inline'; Codehighlighter1_120_145_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;f()</span><span id=Codehighlighter1_120_145_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_120_145_Open_Text><span style="COLOR: #000000">{cout</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">child_f()</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">endl;}</span></span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">10</span><span style="COLOR: #000000"><img id=Codehighlighter1_160_184_Open_Image onclick="this.style.display='none'; Codehighlighter1_160_184_Open_Text.style.display='none'; Codehighlighter1_160_184_Closed_Image.style.display='inline'; Codehighlighter1_160_184_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_160_184_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_160_184_Closed_Text.style.display='none'; Codehighlighter1_160_184_Open_Image.style.display='inline'; Codehighlighter1_160_184_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;g()</span><span id=Codehighlighter1_160_184_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_160_184_Open_Text><span style="COLOR: #000000">{cout</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">base_g()</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">endl;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">11</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;<br></span><span style="COLOR: #008080">12</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">13</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;main()<br></span><span style="COLOR: #008080">14</span><span style="COLOR: #000000"><img id=Codehighlighter1_203_297_Open_Image onclick="this.style.display='none'; Codehighlighter1_203_297_Open_Text.style.display='none'; Codehighlighter1_203_297_Closed_Image.style.display='inline'; Codehighlighter1_203_297_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_203_297_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_203_297_Closed_Text.style.display='none'; Codehighlighter1_203_297_Open_Image.style.display='inline'; Codehighlighter1_203_297_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_203_297_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_203_297_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">15</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">Fun)(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">);<br></span><span style="COLOR: #008080">16</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;child&nbsp;b;<br></span><span style="COLOR: #008080">17</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Fun&nbsp;pFun;<br></span><span style="COLOR: #008080">18</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;pFun&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(Fun)</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">((</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">b));<br></span><span style="COLOR: #008080">19</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;pFun();<br></span><span style="COLOR: #008080">20</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span></div>
<br>输出结果：child_f();<br><br>这样就访问了私有的虚函数了。<br><br>但是这样只能访问私有的虚函数，那怎么才能访问任意的私有函数呢，像child类的g()函数，还请高人指点。
<img src ="http://www.cppblog.com/xingkongyun/aggbug/65881.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xingkongyun/" target="_blank">杨彬彬</a> 2008-11-03 20:43 <a href="http://www.cppblog.com/xingkongyun/archive/2008/11/03/65881.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>心目中的编程高手</title><link>http://www.cppblog.com/xingkongyun/archive/2008/10/10/63710.html</link><dc:creator>杨彬彬</dc:creator><author>杨彬彬</author><pubDate>Fri, 10 Oct 2008 14:45:00 GMT</pubDate><guid>http://www.cppblog.com/xingkongyun/archive/2008/10/10/63710.html</guid><wfw:comment>http://www.cppblog.com/xingkongyun/comments/63710.html</wfw:comment><comments>http://www.cppblog.com/xingkongyun/archive/2008/10/10/63710.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xingkongyun/comments/commentRss/63710.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xingkongyun/services/trackbacks/63710.html</trackback:ping><description><![CDATA[<p>嗨~~~，发点文字，大家看看，就当自勉吧。。。（努力的学习吧，追赶吧，学无止境啊）<br><br>[转载]心目中的编程高手</p>
<p>Bill Joy, 前任Sun的首席科学家，当年在Berkeley时主持开发了最早版本的BSD。他还是vi和csh的作者。当然，Csh Programming Considered Harmful 是另一个话题乐。据说他想看看自己能不能写个操作系统，就在三天里写了个自己的Unix, 也就是BSD的前身。当然是传说了，但足见他的功力。另一个传说是，1980年初的时候，DARPA让BBN在Berkley Unix里加上BBN开发的TCP/IP代码。但当时还是研究生的B伯伯怒了，拒绝把BBNTCP/IP加入BSD，因为他觉得BBN的TCP/IP写得 不好。于是B伯伯出手了，端的是一箭封喉，很快就写出了高性能的伯克利版TCP/IP。当时BBN和DARPA签了巨额合同开发TCP/IPStack， 谁知他们的代码还不如一个研究生的好。于是他们开会。只见当时B伯伯穿个T-shirt出现在会议室(当时穿T-shirt不象现在，还是相当散漫的 哈)。只见BBN问：你怎么写出来的？而B伯伯答：简单，你读协议，然后编程就行了。最令偶晕倒的是，B伯伯硕士毕业 后决定到工业界发展，于是就到了当时只有一间办公室的Sun, 然后他就把Sparc设计出 来乐。。。象这种软硬通吃的牛人，想不佩服都不行的说。据Bill Joy的同事说，一般开会的时候B伯伯总是拿一堆杂志漫不经心地读。但往往在关键之处，B伯伯发言，直切要害，提出 漂亮的构想，让同事们彻底崩溃。对了，他还是Java Spec和JINI的主要作者之一。</p>
<p><br>John Carmack John Carmack，id Software的founder和Lead Programmer。上个月和一个搞图形的师兄聊天，他竟然不知道John Carmack, 也让偶大大地晕了一把。不过也许搞研究的和搞实战的多少有些隔吧。想必喜欢第一人称射击游戏的都知道J哥哥。90年代初只要能在PC上搞个小动画都能让人 惊叹一番的时候，J哥哥就推出了石破天惊的Castle Wolfstein, 然后再接再励，doom, doomII, Quake...每次都把3-D技术推到极致。J哥哥的简历上说自己的专长是"Exhaust 3-D technology"，真是牛人之言不我欺的说。做J哥哥这样的人是很幸福的，因为各大图形卡厂家一有了新产品就要向他&#8220;进贡&#8221; ，不然如果他的游戏不支持哪种卡，哪种卡基本就会夭折乐。当初MS的Direct3D也得听取 他的意见，修改了不少API。当然，J哥哥在结婚前十数年如一日地每天编程14小时以上， 也是偶们凡人望尘莫及的。对了，J哥哥高中肆业(？)，可以说是自学成才。不过呢，谁要用这个例子来为自己学习不好辩护，就大错特错了。那 Leonardo Da Vinci还是自学成才呢(人是私生子，不能上学)。普通人和天才还是有区别的。对了，其实偶们叫&#8220;达分奇&#8221;是相当不对的，因为Vinci是地名，而Da Vinci就是从Vinci来的人的意思。换句话说，Leonardo Da Vinci就是&#8220;从Vinci来的Leonardo&#8221;的意思。叫别人&#8220;Da Vinci&#8221;就不知所谓乐。嗯，扯远了，打住。</p>
<p><br>David Cutler David Cutler，VMS和Windows NT的首席设计师，去微软前号称硅谷最牛的kernel开发员。当初他和他的手下在微软一周内把一个具备基本功能的bootable kernel写出来，然后说：&#8220;who can't write an OS in a week?"，也是牛气冲天的说。顺便说一句，D爷爷到NT3.5时，管理1500名开发员，自己还兼做设计和编程，不改coder本色啊。D爷爷天生脾 气火爆，和人争论时喜欢双手猛击桌子以壮声势。:-) 日常交谈F-word不离口。他面试秘书时必问："what do you think of the word '****'?" ，让无数美女刹羽而归。终于有一天，一个同样火爆的女面对这个问题脱口而出："That's my favorite word"。于是她被录取乐，为D爷爷工作到NT3.5发布。</p>
<p><br>Donald E. Knuth Don Knuth。高爷爷其实用不着偶多说。学编程的不知道他就好像学物理的不知道牛顿，学数学的不知道欧拉，学音乐的不知道莫扎特，学Delphi的不知到 Anders Hejlsberg，或者学Linux不知道Linus Torvalds一样，不可原谅啊。:-)为了让文章完整，就再罗唆几句吧。高爷爷本科时就开始给行行色色的公司写各种稀奇古怪的编译器挣外快了。他卖给 别人时收一两千美元，那些公司拿了code，加工一下卖出去就是上万上十万。不过也没见高爷爷不爽过，学者本色的说。想想那可是60年代初啊， 高爷爷写编译器写多了，顺带就搞出了个Attribute Grammar和LR(k)，大大地造福后人啊。至于高爷爷在CalTech的编程比赛(有Alan Kay得众多高高手参加)总是第一，写的Tex到86年就code freeze，还附带2^n美分奖励等等都是耳熟能详，偶就不饶舌乐。顺便说一下，高老大爷是无可争议的写作高手。他给Concrete Mathematics 写的前言可谓字字铿锵，堪为前言的典范。他的技术文章也是一绝，文风细致，解释精当，而且没有学究气，不失轻快跳脱。记得几年前读Concrete Mathematics，时不时开怀大笑，让老妈极其郁闷，觉得我nerdy到家，不可救药。其实呢，子非鱼，安知鱼之乐，更不知那完全是高爷爷的功劳。 说到写作高手，不能不提Stephen A. Cook。他的文章当年就被我们的写作老师极力推荐，号称典雅文风的样本。库爷爷一头银发，身材颀长，总是面带谦和的微笑，颇有仙风道骨，正好和他的仙文 相配的说。高爷爷其实还是开源运动的先驱。虽然他没有象Richard Stallman那样八方奔走，但他捐献了好多作品，都可以在网上看到，比如著名的Mathematical Writing，MMIXWare，The Tex Book等，更不用说足以让他流芳百世的Tex乐。</p>
<p><br>Ken Thompson Ken Thompson，C语言前身B语言的作者，Unix的发明人之一(另一个是Dennis M. Riche老大，被尊为DMR)，Belle(一个厉害的国际象棋程序)的作者之一, 操作系统Plan 9的主要作者(另一个是大牛人Rob Pike, 前不久被google挖走了)。Ken爷爷也算是计算机历史上开天辟地的人物了。1969年还是计算机史前时代，普通人都认为只有大型机才能运行通用的操 作系统，小型机只有高山仰止的份儿。至于用高级语言来写操作系统，更是笑谈。Ken爷爷自然不是池中物 ，于是他和DMR怒了，在1969年到1970间用汇编在PDP-7上写出了UNIX的第一个版本。他们并不知道，一场轰轰烈烈的UNIX传奇由此拉开了 序幕。Ken爷爷在1971年又把Unix用C重写，于是C在随后20年成就了不知多少豪杰的梦想和光荣。Ken爷爷还有段佳话： 装了UNIX的PDP-11最早被安装在Bell Lab里供大家日常使用。很快大家就发现Ken爷爷总能进入他们的帐户，获得最高权限。Bell Lab里的科学家都心比天高，当然被搞得郁闷无比。于是有高手怒了，跳出来分析了UNIX代码，找到后门，修改代码，然后重新编译了整个UNIX。就在大 家都以为&#8220;这个世界清净了&#8221;的时候，他们发现Ken爷爷还是轻而易举地拿到他们的帐户权限，百思不解后 ，只好继续郁闷。谁知道这一郁闷，就郁闷了14年，直到Ken爷爷道出个中缘由。原来，代码里的确有后门，但后门不在Unix代码里，而在编译Unix代 码的C编译器里。每次C编译器编译UNIX的代码，就自动生成后门代码。而整个Bell Lab的人，都是用Ken爷爷的C编译器。</p>
<p><br>Rob Pike Rob Pike, AT&amp;T Bell Lab前Member of Technical Staff ，现在google研究操作系统。罗伯伯是Unix的先驱，是贝尔实验室最早和Ken Thompson以及Dennis M. Ritche开发Unix的猛人，UTF-8的设计人。他还在美国名嘴David Letterman的晚间节目上露了一小脸，一脸憨厚地帮一胖子吹牛搞怪。让偶佩服不已的是，罗伯伯还是1980年奥运会射箭的银牌得主。他也是个颇为厉 害的业余天文学家，设计的珈玛射线望远镜差点被NASA用在航天飞机上。他还是两本经典，The Unix Programming Environment 和 The Practice of Programming 的作者之一。如果初学者想在编程方面精益求精，实在该好好读读这两本书。它们都有中文版的说。罗伯伯还写出了Unix下第一个基于位图的窗口系统，并且是 著名的blit 终端的作者。当然了，罗伯伯还是号称锐意革新的操作系统，Plan9，的主要作者。可惜的是，Plan9并没有引起多少人的注意。罗伯伯一怒之下，写出了 振聋发聩的雄文 Systems Software Research is Irrelevant，痛斥当下系统开发不思进取，固步自封的弊病。虽然这篇文章是罗伯伯含忿出手，颇有偏激之词，但确实道出了系统开发的无奈：开发周期 越来越长，代价越来越大，用户被统一到少数几个系统上，结果越来越多的活动是测量和修补，而真正的革新 越来越少。就在罗伯伯郁闷之极的时候，google登门求贤来乐。如果说现在还有一家大众公司在不遗余力地把系统开发推向极致的话，也就是google 乐。随便看看google的成果就知道了。具有超强容错和负载平衡能力的分布式文件系统GFS (现在能够用100,000台廉价PC搭起一个巨型分布系统，并且高效便宜地进行管理的系统也不多哈)，大规模机器学习系统(拼写检查，广告匹配，拼音搜 寻。。。哪个都很牛的说)，更不用说处理海量并行计算的各式google服务了。Rob在System Software Research is Irrelevant里萧瑟地说现在没有人再关心系统研究的前沿成果了。想不到他错了，因为google关心。google网络了大批功成名就的牛人，还 有大量初生牛犊般博士做开发，显然不是没事耍酷，而是因为它们的开发总是试图吸取系统研究的最新成果。 想必Rob Pike在google很幸福。愿他做出更棒的系统。</p>
<p>Dennis M. Ritchie 既然Ken Thompson是我的偶像，新闻组上人称DMR的Dennis M. Ritchie自然也是，毕竟两人共同缔造了UNIX，而Dennis几乎独力把C搞大(当然，C的前身是B，而B是Ken Thompson一手做出来的)。J 两人1983年分享图灵奖，是有史以来少数几个因工程项目得奖的工程师(本来是唯一的一对儿，但Alan Kay才因为SmallTalk得奖，所以就成了唯二的乐) 。一个人一生能做出一个卓越的系统已经不易，DMR的C和UNIX长盛不衰近30年，至今生机勃勃，DMR此生可以无憾的说。D爷爷也算有家学渊源：他老 爸在AT&amp;T贝尔实验室工作了一辈子，并在电路设计方面卓有成就，还出了本颇有影响的书The Design of Switching Circuits，据说在交换理论和逻辑设计方面有独到的论述。当然，D爷爷和他老爸是不同时代的人：他老爸的研究成形于晶体管发明之前，而D爷爷的工作 离了晶体管就玩儿不转乐。:-D不要看D爷爷搞出了C，其实他最爱的编程语言是Alef，在Plan9上运行，支持并行编程。Alef的语法和C相似，但 数据类型和执行方式都和C大大不同。说到语言，D爷爷对后来人有非常中肯的建议：抱着学习的目的来开发你自己的语言，不要冀望于它被 众人接受。这个建议不光对语言开发有用，也适用于其它大型系统的开发。别的不说，DMR后来领导自己的团队在1995年和1996分别推出了Plan9和 Inferno操作系统，又用多少人知道呢？其实，D爷爷当初也没想过C会风行世界。他开发C的初衷和 Eric S. Raymond在Cathedral and Bazaar里阐述的一样，就是要消除自己对现有工具的不爽之处。谁知D爷爷无心插柳，C竟然受到众多程序员的狂热拥戴，连D爷爷自己都大惑不解。在一次 采访中D爷爷说大概那是因为C的抽象程度碰巧既满足了程序员的要求, 又容易实现。当然C一度是Unix上的通用语言也是原因。但不管怎么说，D爷爷对编程语言出色的审美意识奠定了C广为流传的基础。最后八卦一下。D爷爷的 业余爱好和NBA大牛Karl Malone一样：开卡车。不过D爷爷更喜欢开NASCAR，而KM独爱巨无霸。J D爷爷自称心中不供偶像，如果一定要说一个，那就是Ken Thompson了。现在Ken爷爷退休当飞机教练去了，而D爷爷当了贝尔实验室系统开发部的头，整日忙于开支票。他俩合作20年，屡屡创造历史。这段令 人神往的佳话，也就长留你我心中乐。P.S., 很多人都以为Brian W. Kernighan是C的作者。其实BWK只是写了那本经典K&amp;R C。据D爷爷说，他，Ken, 和Kernighan三人中，Kernighan最能写文章，他次之，而Ken写得最少；但说到编程，Ken爷爷才是当之无愧的老大。</p>
<p><br>Edsger Wybe Dijkstra 对，就是E.W. Dijkstra. 一提到EWD，很多人就会想起找最短路径的Dijkstra Algorithm，就好像一提到Sir. Tony Hoare，就想起Quick Sort一样。其实这些个算法不过是两个牛人在他们职业生涯中最琐碎的贡献。比如Dijkstra算法，无非是戴爷爷在1956年为了展示新计算机 ARMAC的计算能力，初试身手的成果，属于他的算法处女作。据戴爷爷自述，他搞出最短路径算法的时候连纸笔 都没用。当时他和他老婆在阿姆斯特丹一家咖啡厅的阳台上晒太阳喝咖啡，突然就把这个算法想出来乐。而且当时的算法研究还比较原始，牛人们忙着用计算机搞数 值计算，对离散算法不屑一顾。那时连一个象样的专注于离散算法的专业期刊都没有。戴爷爷于是推迟发表这 个算法。直到1959年，他才把这个算法发表在Numerische Mathematik的创刊号上，权为捧场。:-) EWD在多个领域牛气冲天，端的是理论和编程两手硬的高手。只不过他的很多工作比较深刻，学校的老先生们觉得本科生接受不了，不给本科生讲而已。戴爷爷大 概因为最短路径算法一战成名，于是有人请他参加另一台计算机X1的设计工作，并且把设计实时中断系统的 任务派给了他。现在看来实时中断也许不算什么，但要知到，X1前根本就没有实时中断的概念。实现它简直就是一场豪赌。戴爷爷起初还不情愿，但经不住项目负 责人Bram和Carel的轮番&#8220;吹捧&#8221;：我们知道实时中断让您工作变得非常困难，但象您这样的牛人肯 定能做出来的说。结果戴爷爷被糖衣炮弹彻底击穿，接下了这个烫手山芋。两三年后，他不仅搞出了实时中断，还围绕这个写出了自己的博士论文，顺利戴上博士 帽。让戴爷爷真正成名立万的还是在X1上开发的Algo60，最早的高级语言之一。戴爷爷没日没夜地工作 了8个月，就搞出了Algo60，也因此获得了1972年的图灵奖。因为Algo60，戴爷爷发表了一篇石破天惊的文章：Recursive Programming，于是人们才知道，原来高级语言也可以高效地实现递归，原来从此以后，所有程序员都不可避免地和戴爷爷发明的一个词(应该说是概 念)打交道：堆栈。而且Algo60还让戴爷爷深入地思考多道程序设计的问题，最终发明了每个系统程序员 都绕不开的概念：semaphore。当然，戴爷爷总是把他发明的概念严格形式化，极具科学家本色的说。和这些成就想比，他提出的吃饭的哲学家问题，也就 没什么好说的了。说来好笑，当时的大学(忘了哪所了)还是觉得戴爷爷没有受过正统的数学训练，也不是专 门搞数值分析的，所以最后不太情愿地给了他一个教职。这种小挫折并不能妨碍象戴爷爷这样的牛人创造历史。他一边教数值分析(:-D) ，一边开始开发一个新的操作系统，并培养计算机科学家。几年后，THE Multiprogramming System横空出世。THE是第一个支持松散耦合，显式同步的进程并由此使得严格证明系统没有死锁变得容易的操作系统。可惜戴爷爷任职的系不识货，还强 行解散了他的研究小组(1972年戴爷爷给他的系主任说他得了图灵奖，系主任的第一反应是你们搞计算机 就喜欢乱发奖)。这让戴爷爷相当郁闷，得了抑郁症。在极度郁闷之中，戴爷爷决定用写作来治疗自己的抑郁症。于是经典就诞生乐：Notes on Structured Programming。戴爷爷从此被尊为结构化编程的奠基人，而且他的抑郁症也被治好乐。EWD太牛，结果他的故事也太多。先到这里吧。1973起，他 的故事就在美国发生了。</p>
<p><br>Anders Hejlsberg，微软.NET的首席架构师，编程语言设计和实现的顶尖高手。他一手做出了Turbo Pascal, 也是Delphi,J++(尤其是WFC)，C#,和.NET的主要作者。这些作品的名字足以为他立传。作为一个程序员，我在这样的大师面前实在无语。生 子当如Anders的说。李维的&lt;&lt;Borland传奇&gt;&gt;里已详细讲述了Anders的传奇故事，我就不用费舌了：</p>
<img src ="http://www.cppblog.com/xingkongyun/aggbug/63710.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xingkongyun/" target="_blank">杨彬彬</a> 2008-10-10 22:45 <a href="http://www.cppblog.com/xingkongyun/archive/2008/10/10/63710.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>保护模式与实模式的切换</title><link>http://www.cppblog.com/xingkongyun/archive/2008/10/07/63414.html</link><dc:creator>杨彬彬</dc:creator><author>杨彬彬</author><pubDate>Tue, 07 Oct 2008 09:22:00 GMT</pubDate><guid>http://www.cppblog.com/xingkongyun/archive/2008/10/07/63414.html</guid><wfw:comment>http://www.cppblog.com/xingkongyun/comments/63414.html</wfw:comment><comments>http://www.cppblog.com/xingkongyun/archive/2008/10/07/63414.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/xingkongyun/comments/commentRss/63414.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xingkongyun/services/trackbacks/63414.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;关于从保护模式切换到实模式的相关说明&nbsp;参考于渊的《自己动手写操作系统》第三章中从实模式切换到保护模式，最后有重新切回实模式的代码（代码如下），其中有几点不太明白的，参考其他文章之后在此记录一下。其中还有不太明白的地方，希望大家能在下面&nbsp;留个言帮我讲明白，谢谢。下面代码有些宏定义没贴出来，应该能看明白。&nbsp;&nbsp;1;&nbsp;===...&nbsp;&nbsp;<a href='http://www.cppblog.com/xingkongyun/archive/2008/10/07/63414.html'>阅读全文</a><img src ="http://www.cppblog.com/xingkongyun/aggbug/63414.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xingkongyun/" target="_blank">杨彬彬</a> 2008-10-07 17:22 <a href="http://www.cppblog.com/xingkongyun/archive/2008/10/07/63414.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>高度优先左高树实现</title><link>http://www.cppblog.com/xingkongyun/archive/2008/09/18/62193.html</link><dc:creator>杨彬彬</dc:creator><author>杨彬彬</author><pubDate>Thu, 18 Sep 2008 09:27:00 GMT</pubDate><guid>http://www.cppblog.com/xingkongyun/archive/2008/09/18/62193.html</guid><wfw:comment>http://www.cppblog.com/xingkongyun/comments/62193.html</wfw:comment><comments>http://www.cppblog.com/xingkongyun/archive/2008/09/18/62193.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xingkongyun/comments/commentRss/62193.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xingkongyun/services/trackbacks/62193.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 高度优先左高树与堆的性质类似，都可有作为优先级队列的底层结构。但应用高度优先左高树可以很容易的实现两个优先级队列的合并操作。&nbsp;&nbsp;1//高度优先左高树(HBLT)实现&nbsp;&nbsp;2#ifndef&nbsp;HBLT_H&nbsp;&nbsp;3#define&nbsp;HBLT_H&nbsp;&nbsp;4#include&nbsp;&lt;queue&gt;&n...&nbsp;&nbsp;<a href='http://www.cppblog.com/xingkongyun/archive/2008/09/18/62193.html'>阅读全文</a><img src ="http://www.cppblog.com/xingkongyun/aggbug/62193.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xingkongyun/" target="_blank">杨彬彬</a> 2008-09-18 17:27 <a href="http://www.cppblog.com/xingkongyun/archive/2008/09/18/62193.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>