<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?xml-stylesheet type="text/css" href="css/rss.css"?>
<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>CSK.Blog</title><link>http://www.csksoft.net/blog/</link><description>Heaven的网络家园-ReformCore Managed</description><generator>RainbowSoft Studio Z-Blog 1.7 Laputa Build 70216</generator><language>zh-CN</language><copyright>Copyright CSKSOFT 2000-2008.</copyright><pubDate>Sat, 22 Nov 2008 23:07:20 +0800</pubDate><item><title>eArts'08中作品的介绍</title><author>csk@citiz.net (csk)</author><link>http://www.csksoft.net/blog/post/eArts08_intro.html</link><pubDate>Thu, 20 Nov 2008 00:33:41 +0800</pubDate><guid>http://www.csksoft.net/blog/post/eArts08_intro.html</guid><description><![CDATA[<p>点击文章标题察看全部内容。</p>
<div class="quote">
<p>作品名称：<strong>你，或者你们，或者我们，或者我</strong></p>
<p>创作时间：2008</p>
<p>展览： Shanghai eArts Festival 2008 </p>
<p>展区：蓄与化&mdash;&mdash;青年新媒体艺术作品展</p>
<p>展览时间：2008-10-18 ~ 2008-11-08<br />
官方网站介绍页面：<a href="http://www.shearts.org/index.php/?p=591&amp;lang=zh-cn">http://www.shearts.org/index.php/?p=591&amp;lang=zh-cn</a>&nbsp;(可能会失效)</p>
</div>
<p>Flv视频播放：</p>
<p><script language="javascript">	    		WirteFlashBlock("/flvplayer/main.swf?movie=http://download.csksoft.net/stream/eArts_demoshow.htm",512,300,"#FFFFFF","flv");	        </script></p>
<p align="center"><img alt="" src="http://www.csksoft.net/data/pic/earts_sample.jpg" /></p>
<p align="center">局部图案</p>
<p>&nbsp;</p>
<p align="center"><a target="_blank" href="http://www.csksoft.net/data/pic/earts_real_working2f.png"><img alt="" src="http://www.csksoft.net/data/pic/earts_real_working2.jpg" /></a></p>
<p align="center">全景图1</p>
<p align="center"><a href="http://www.csksoft.net/data/pic/earts_snapshot_01f.png"><img alt="" src="http://www.csksoft.net/data/pic/earts_snapshot_01.jpg" /></a></p>
<p align="center">全景图2</p>]]></description><category>Flash/媒体</category><comments>http://www.csksoft.net/blog/post/eArts08_intro.html#comment</comments><wfw:comment>http://www.csksoft.net/blog/</wfw:comment><wfw:commentRss>http://www.csksoft.net/blog/sydication.asp?cmt=238</wfw:commentRss><trackback:ping>http://www.csksoft.net/blog/cmd.asp?act=tb&amp;id=238&amp;key=408b4cd2</trackback:ping></item><item><title>欢迎来参观本届eArts Shanghai中我的艺术作品</title><author>csk@citiz.net (csk)</author><link>http://www.csksoft.net/blog/post/237.html</link><pubDate>Sat, 18 Oct 2008 01:32:00 +0800</pubDate><guid>http://www.csksoft.net/blog/post/237.html</guid><description><![CDATA[<p align="left">经历了近1个月的神秘倒计时，或许大家已经猜出来了，不过还是要正式申明。</p>
<p align="left">&nbsp;</p>
<p align="left">很荣幸在本届上海电子艺术节( eArts Shanghai Festivial )中有我的作品。 下面是作品名和相关介绍：</p>
<p align="left">&nbsp;</p>
<h2 class="std_h2">《你，或者你们，或者我们，或者我》[参展作品]</h2>
<p align="left"><br />
作品名称：《你，或者你们，或者我们，或者我》<br />
艺术家： 何为&amp;陈士凯<br />
作品类型： 网络信息装置<br />
创作时间：2008年</p>
<p align="left">作品概念：<br />
搜索引擎作为现代网络最普遍的信息工具，起着便捷沟通用户与全网络的作用。如果我们将用户使用搜索引擎的过程定义为一种交流方式。那么可以说，用户由发问到引擎反馈，以机器为中介为反馈完成了整个问答的交流过程。用户在网络中的自行创作被捕捉作为引擎反馈的语素。为达到更优的反馈效果，用户的操作行为在无意间为引擎引用为缔造新反馈语序的依据。对话获取者是用户，而对话的创作革新者恰又是用户本身是如此应用反复类推的结果。看似借助机器和群体智慧的对话在用户操作及机器规则中完成的选择性的对话恰成为一种无选择性的自我对话。<br />
作品以真实接入的社区搜索关键词为根基。试图探讨对蓄智慧一身的网络及搜寻演化可能及关于信息用户自体身份的迷失。跟踪的数据采用每前一天相应时段的信息以保证正常运行。<br />
</p>
<p align="left">eArts将于2008-10-18，对，就是今天开展。下面是蓄与化&mdash;&mdash;青年新媒体艺术作品展区的地址信息：</p>
<div class="quote">
<p align="left">&ldquo;蓄与化&mdash;&mdash;青年新媒体艺术作品展&rdquo;</p>
<p align="left">活动时间：2008年10月18日 ~ 11月8日<br />
活动地点：创智天地-创智天地广场</p>
</div>
<p align="left">&nbsp;</p>
<p align="left">eArts官方网站：<a href="http://www.shearts.org/index.php/?page_id=15">http://www.shearts.org/index.php/?page_id=15</a></p>
<p align="left">&nbsp;</p>
<p align="left">最近为此准备已经好久没正常休息了，今天恐怕也没法好好睡觉。还是不多说了。如果对此感兴趣欢迎前来捧场~ 至于作品，我觉得质量上不会让各位失望的:-)</p>
<p align="left">&nbsp;</p>
<p align="left">好了，以后有时间再好好总结下巴<br />
<br />
</p>]]></description><category>动态</category><comments>http://www.csksoft.net/blog/post/237.html#comment</comments><wfw:comment>http://www.csksoft.net/blog/</wfw:comment><wfw:commentRss>http://www.csksoft.net/blog/sydication.asp?cmt=237</wfw:commentRss><trackback:ping>http://www.csksoft.net/blog/cmd.asp?act=tb&amp;id=237&amp;key=4fbed2b5</trackback:ping></item><item><title>比较&amp;quot;优美&amp;quot;的进行html代码的截断</title><author>csk@citiz.net (csk)</author><link>http://www.csksoft.net/blog/post/wise_htmlcut.html</link><pubDate>Wed, 27 Aug 2008 22:37:52 +0800</pubDate><guid>http://www.csksoft.net/blog/post/wise_htmlcut.html</guid><description><![CDATA[<p>最近想到该更新下自己网站了，好吧，就从那个我已经看不惯很久的<a href="http://www.csksoft.net/blog/post/MSNSpace_How2Update.html">msn space同步转发器</a>开刀...</p>
<p>如果你是先通过space看到这篇文章的话，应该就能看到效果了。撇去显示效果不说，这里提一个有趣的问题：</p>
<div class="quote">如何比较优雅的截取一段html代码？</div>
<p>换句话说，假设有下面这段html代码：</p>
<div class="code-xml">
<p>&lt;p&gt;this is a truncated html source&lt;a href=\&quot;some _fcksavedurl=&quot;\&quot;some&quot; URL\&quot;&gt;some hyperlink&lt;/a&gt;.&lt;BR&gt;</p>
<p>more text&lt;/p&gt;&lt;p&gt;one more&lt;/p&gt;</p>
</div>
<p>出于某种需要，希望只显示（保留）这段代码的前n个字节。这样的需求其实不少见，最明显的就是在msn space文章同步的时候，我只希望对文章的大致内容转发过去，目前的做法就是截取文章的前1000字节。</p>
<p>如果仅仅是对一段普通文本进行这样的处理，那实在简单不过。直接截取即可。但是html却不同。</p>
<p>例如，对于上面的文字，如果截取的部分是：</p>
<div class="code-xml">&lt;p&gt;this is a truncated html source〈a href=\&quot;some </div>
<p>今后将这段代码用于其它页面将出现不可能预见的问题。</p>
<p>考虑下面的代码：</p>
<div class="code-xml">
<p>&lt;p&gt;Some html code&lt;/p&gt;</p>
<p>The following is a truncated article:&lt;div&gt;&lt;%=truncated_text%&gt;&lt;/div&gt;</p>
</div>
<p>将上面被强制截断的代码片断填充到这段代码可能会让好好的html页面面目全非。</p>
<p>因此，我们需要&quot;优雅&quot;的去截断这段html。</p>
<h2 class="std_h2">何谓&quot;优雅&quot;?</h2>
<p>造成上述问题的根本原因在于对html的强制截断忽略了html的结构信息，使得后面浏览器的分析操作出现紊乱。</p>
<p>因此，优雅的去进行html截断必须在截断后仍旧维持当前的html结构信息。</p>
<p>可能你会说，这还不简单，直接实现一个html parser或者用xml dom分析器来完成此事。的确可以，不过这样的效率和代价实在有点可怕了。因此，要完成这个优雅的截断操作，同时需要在执行效率以及资源消耗间做取舍。同时，尽量避免动辄用dom parser的恶俗。</p>
<p>寻找一种刚好能解决此问题，且保证效率较高并尽量避免滥用资源的途径是我们需要的。</p>
<h2 class="std_h2">我的方法</h2>
<p>相信在这个所谓的什么&quot;web2.0&quot; &quot;web3.0?&quot;的扯淡名词时代恐怕已经有人实现过类似的东西，所以这里仅说说我目前的实现方案。如果你知道有更好的办法，希望能和大家分享。</p>
<p>撇开具体实现，这个问题其实很简单，就是一个配对问题。用一个stack来跟踪html标签的使用情况，每个&lt;tag&gt;应该有一个对应的&lt;/tag&gt;配对。因此我们只要扫描一遍html，堆栈上残留的元素就是没有配对的tag。这些tag就是因为我们的暴力截取而成为孤对的。因此我们只要在html代码末尾FILO的补上这些这些tag的对应/tag即可。</p>
<p>但是还有些细节问题。</p>
<ol>
    <li>因为html没有xml规范，所以一些情况下未配对的tag也是允许的，例如&lt;BR&gt;。 </li>
    <li>存在单一自配对的&lt;tag/&gt;例如&lt;img xxx /&gt;。 </li>
</ol>
<p>&nbsp;同时，对于暴力截断的html代码，我们不能奢望它们正好截断在一对tag之间，就像上面给出的例子那样，对于末尾是</p>
<div class="code-xml">〈a href=\&quot;some </div>
<p>&nbsp;的情况，我们还首先需要将这些&ldquo;残缺&rdquo;的tag给删去，否则即使配对仍旧存在问题。</p>
<p>大致的思路就是这样，关键就是如何实现的问题了。这里我<strong>仅采用javascript的String.replace方法</strong>进行上述的html扫描。</p>
<h2 class="std_h2">具体实现</h2>
<p>实际上实现的困难在于如何去识别html的tag。这里的底线是不能用现有的dom parser。Javascript的正则式功能在这里就能派上大用处了。根据上面的分析，为了进行tag匹配的分析，我们需要识别出html中的所有tag。</p>
<p>他们存在下面几种形式</p>
<ol>
    <li>&lt;&nbsp;tagname some_attr&gt; </li>
    <li>&lt; / tagname&gt; </li>
    <li>&lt; tagname /&gt;</li>
</ol>
<p>我们的任务是编写正则式去查找这些tag。并且捕获到tagname。这里用到了一个偏方，利用String.replace方法。</p>
<p>一般，很多人都这样用它some_str.replace(/some regexp/, &quot;new text&quot;);</p>
<p>实际上，他有另一个形式：</p>
<div class="code-cstyle">
<p>some.replace(/some regexp/, function( match_text)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return &quot;new text&quot;;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; });</p>
</div>
<p>利用一个匿名函数进行替换... </p>
<p>实际上，这个replace是一个再好不过的基于正则式的查找操作手段。因为相比手写一个查找循环，他的迭代操作是在解析器内部实现的，效率肯定要高很多。同时据我所知，要写个循环依次查找每个匹配正则式的循环很不容易，貌似没法指定搜索的开始字符。</p>
<p>好了，到此关键问题解决了。试验证明用这个办法效率完全可以接受，同时完全没有使用任何第三方的东西。</p>
<p>是具体实现时候要注意考虑前面提到的所有特殊情况。</p>
<p>下面来看下效果，为了展示各种情况，另外给个例子：</p>
<div class="code-xml">&lt;p&gt;this is a truncated html sourcesome hyperlink&lt;/a&gt;.&lt;BR&gt;more text&lt;/p&gt;&lt;div&gt;&lt;dt&gt;some url〈a href=\&quot;some UR<br />
</div>
<p>经过处理后，变成了：</p>
<div class="code-xml">&lt;p&gt;this is a truncated html sourcesome hyperlink&lt;/a&gt;.&lt;BR&gt;more text&lt;/p&gt;&lt;div&gt;&lt;dt&gt;some url&lt;/dt&gt;&lt;/div&gt;</div>
<p>&nbsp;</p>
<p>有了这个功能，相信在作诸如自动缩略文处理时候就会轻松许多。（此刻让我想起了某些blog强制把html转成纯文本作略文的办法...）</p>
<p>&nbsp;</p>
<p>最后，给出一个测试代码。喜欢的可以直接拿去用。当然，留下原始作者信息</p>
<p><a href="ftp://FTP_Visitor:visitor@ftp.csksoft.net/Public/Products/code_and_lib/wise_cut_csk.zip">ftp://FTP_Visitor:visitor@ftp.csksoft.net/Public/Products/code_and_lib/wise_cut_csk.zip</a></p>]]></description><category>系统/平台/设计</category><comments>http://www.csksoft.net/blog/post/wise_htmlcut.html#comment</comments><wfw:comment>http://www.csksoft.net/blog/</wfw:comment><wfw:commentRss>http://www.csksoft.net/blog/sydication.asp?cmt=236</wfw:commentRss><trackback:ping>http://www.csksoft.net/blog/cmd.asp?act=tb&amp;id=236&amp;key=6ca15bb6</trackback:ping></item><item><title>intel x86提供的Branch Trace Store的功能</title><author>csk@citiz.net (csk)</author><link>http://www.csksoft.net/blog/post/bts_setup.html</link><pubDate>Thu, 19 Jun 2008 21:26:44 +0800</pubDate><guid>http://www.csksoft.net/blog/post/bts_setup.html</guid><description><![CDATA[<h1 class="std_h1">写给转载者：</h1>
<p>转载请保留作者信息</p>
<p>来源： <a href="http://www.csksoft.net/blog">http://www.csksoft.net/blog</a></p>
<p>作者：陈士凯</p>
<h1 class="std_h1">介绍</h1>
<p>Branch Trace Store(BTS)是目前广泛被intel CPU所提供的一种硬件辅助调试功能，因为在MSRA项目需要，所以作了一些基于它的应用。虽然Intel的CPU开发手册[1]提供了比较详细的使用方法，但是由于比较笼统，且缺少win32下的相关资料。所以我打算把其中的一些tricky的事情和大家分享下。这些内容当然也是我自己查找公开资料得到的，因此也不算什么秘密吧 :-)</p>
<p>&nbsp;</p>
<p>BTS简单的说就是允许CPU将自己实际执行到的分支指令(jmp/jxx/call/int/etc.)的相关信息保存下来的功能。一般CPU都会保存每个分支指令的源地址和目标地址，该地址在保护模式下是虚拟地址形式表示的。利用这个功能，可以实时地了解当前CPU正在执行代码的实际流程情况，很多分析软件，如Intel的Vtune或者profiling库，如*nix平台下的perfmon[2]都用它来做一些程序性能分析。当然，还可以做很多其他有趣的事，比如逆向工程，具体我就不说了。</p>
<p>&nbsp;</p>
<p>不过目前很少有资料具体介绍如何在win32下开启该功能并实现一个可用的BTS捕捉引擎。当然可以参考perfmon的代码，但BTS在其中只是很小一个部分，同时为了实现跨平台，对于新手来说难度较大。因此我这里重点介绍对于单核心的NetBurst构架的CPU（通俗地说就是P4这类）在win32下的具体实现细节。其他的构架，比如现在的Core Due，大家可以参考intel的开发手册举一反三。</p>
<p>&nbsp;</p>
<h1 class="std_h1">工作框架</h1>
<p>实际上，上面所说的BTS只是intel对于分支指令信息捕获机制的一个分支，就P4而言，大致提供了下列几种分支指令的捕获手段</p>
<h2 class="std_h2">Last Branch Record(LBR)</h2>
<p>故名思意，该方式将记录最后几个分支信息，实际上NetBurst CPU中内建了若干个MSR寄存器用于记录Last Branch Record，称为LBR Stack。在该模式下运行的CPU会采用Round Robin的方式循环填充那几个MSR寄存器。</p>
<p>该方式常用在调试器的Call Stack分析上，我们这里就不涉及了</p>
<p>&nbsp;</p>
<h2 class="std_h2">Branch Trace Messages</h2>
<p>该方式和我们将介绍的Branch Trace Store大致类似，与LBR不同的是，CPU会把分支信息发送到系统总线上供第三方硬件在总线上接收数据。而BTS则直接将数据保存到由程序制定的内存单元中。我们这里也不讨论该方式。</p>
<p>&nbsp;</p>
<h2 class="std_h2">Branch Trace Store</h2>
<p>简单的说，BTS就是将分支信息保存到了有程序（实际就是我们）制定的一块内存空间中。而当这块内存用尽时，CPU可以采用Round Robin的方式循环填充，这就和LBR类似，但可以指定内存块的大小来决定最大捕捉量。另一种处理方式是在内存快用尽的时候，一个事先由我们设定的处理中断将触发来完成对当前内存块中BTS数据的保存工作。这样就可以记录任意多的分支信息了。我们这里主要考虑的就是这种方式的BTS。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>按照intel开发手册的描述，BTS开启后的CPU执行模式如下图所示：</p>
<p align="center"><img alt="" src="http://www.csksoft.net/data/pic/bts_main_framework.gif" /></p>
<p>&nbsp;</p>
<p>每当CPU执行到一个分支指令后，它就会产生一个如上左图的BTS记录项。这里举个例子：</p>
<div class="quote">
<p>0x80001234&nbsp;&nbsp; mov eax,[esp]</p>
<p>0x80001238&nbsp;&nbsp; or eax,eax</p>
<p>0x80001240&nbsp;&nbsp; jnz 0x80002000</p>
<p>0x80001245&nbsp;&nbsp; nop</p>
<p>...</p>
</div>
<p>&nbsp;</p>
<p>当执行上面这段执行，其中遇到了jnz指令，如果他的跳转条件成立，那么将产生一个从0x80001240到0x80002000的跳转，那么产生的BTS记录项就是{From:0x80001240,To:0x80002000,...}</p>
<p>&nbsp;</p>
<p>对于P4的CPU，这个BTS记录项的结构如下：</p>
<div class="code-cstyle">
<p>struct BTS_ITEM_BLOCK_P4{<br />
&nbsp;&nbsp;&nbsp; ULONG&nbsp;&nbsp; dwBranchFrom;<br />
&nbsp;&nbsp;&nbsp; ULONG&nbsp;&nbsp; dwBranchTo;<br />
&nbsp;&nbsp;&nbsp; ULONG&nbsp;&nbsp; dwFlags;<br />
};</p>
</div>
<p>每次CPU执行到分支指令的时候，只要BTS开启，它就会产生一个如上结构得数据块，然后把它存储到事先定义好的一块内存当中去。而这块内存是比较灵活的，如上图所示，CPU需要知道这块内存的起始地址、结束地址、以及当记录到第几个记录时候需要触发一个中断程序来负责将现有的BTS记录保存下来防止内存溢出。当BTS记录存储到图中那个灰底色的&ldquo;BTS记录#m&rdquo;时，就会触发中断，可以发现它往往并不是整块记录内存的末端，具体原因就不解释了。</p>
<p>&nbsp;</p>
<p>为了让CPU知道我们设置的内存空间的信息，需要提供一个叫做DS_BUFFER_MGR_BLOCK的结构来描述这块内存，他包括的内容如图所示，然后我们需要设置一个名为IA32_DS_AREA的MSR寄存器来指向这个DS_BUFFER_MGR_BLOCK数据的地址。</p>
<p>&nbsp;</p>
<p>P4下DS_BUFFER_MGR_BLOCK的完整结构是：</p>
<div class="code-cstyle">struct DS_BUFFER_MGR_BLOCK_P4{<br />
<strong>&nbsp;&nbsp;&nbsp; PVOID&nbsp;&nbsp; pBTSBufferBase;<br />
&nbsp;&nbsp;&nbsp; PVOID&nbsp;&nbsp; pBTSBufferIndex;<br />
&nbsp;&nbsp;&nbsp; PVOID&nbsp;&nbsp; pBTSMaxSize;<br />
&nbsp;&nbsp;&nbsp; PVOID&nbsp;&nbsp; pBTSIntThresold;<br />
</strong>&nbsp;&nbsp;&nbsp; PVOID&nbsp;&nbsp; pPEBSBufferBase;<br />
&nbsp;&nbsp;&nbsp; PVOID&nbsp;&nbsp; pPEBSIndex;<br />
&nbsp;&nbsp;&nbsp; PVOID&nbsp;&nbsp; pPEBSMaxSize;<br />
&nbsp;&nbsp;&nbsp; PVOID&nbsp;&nbsp; pPEBSIntThresold;<br />
&nbsp;&nbsp;&nbsp; ULONG&nbsp;&nbsp; dwPEBSCounterReset;<br />
&nbsp;&nbsp;&nbsp; ULONG&nbsp;&nbsp; Reserved;<br />
};</div>
<p>&nbsp;</p>
<p>注意上面黑体的条目，这些部分是BTS需要设置的，后面的是PEBS采用机制所需要的信息，这里我们忽略他们。这些条目需要设置成保存BTS内存的相关地址（虚拟内存地址）。</p>
<p>&nbsp;</p>
<p>同时，还需要让CPU知道用于处理BTS溢出的中断号。目前的Local APIC控制器上有一个称为Performance Mon. Counter的寄存器项。当BTS记录项接近溢出时，就会通过这个记录项中存储的中断向量值去触发对应的中断程序。这个Performance Mon. Counter的格式如下图所示（图片来自intel手册）：</p>
<p align="center"><img alt="" src="http://www.csksoft.net/data/pic/bts_LVT_PC.gif" /></p>
<p>由于APIC芯片寄存器映射到了物理内存空间0xFEE00000H处，因此，对于其中寄存器的读写操作可以像标准内存操作那样进行。不过，由于采用虚拟内存的操作系统环境下，需要将该空间映射到对应的虚拟内存地址下，同时要避免操作系统的缓存行为。该部分细节我会在后面介绍。对于APIC芯片和LVT的一些具体细节可以参考intel手册[3]。</p>
<p>&nbsp;</p>
<p>以上便是整个BTS工作的框架，如果不考虑具体的操作系统环境，对于他们的设置不存在很大的难度。不过由于OS引入了各类抽象机制或者保护手段，所以有一些tricky的问题需要处理。下面我讲介绍一个具体的实现流程。</p>
<h1 class="std_h1">Win32下BTS机制的开启和相关框架</h1>
<p>经过上面的分析，对于Win32下进行BTS捕捉，需要进行如下的操作</p>
<ol>
    <li>初始化用于存放BTS记录的内存空间 </li>
    <li>设置对于上述内存空间的描述信息，即DS_BUFFER_MGR_BLOCK，并将其地址值写入IA32_DS_AREA MSR寄存器 </li>
    <li>编写处理BTS溢出操作的中断服务程序，并在IDT表中设立表项，创建一个中断服务 </li>
    <li>设置Local APIC的Performance mon. Counter项，指向上述IDT表中我们创建的中断项 </li>
    <li>通过对专门的MSR寄存器读写开启BTS功能 </li>
</ol>
<p>下面我就每个过程的关键问题具体展开介绍，同时给出些示例代码（不保证能运行）。同时，因为涉及到了底层的硬件控制，这里的代码自然就是在Kernel模式下执行的，如果对WinDDK的开发不熟悉，可以先参考相关文献。</p>
<p>&nbsp;</p>
<h2 class="std_h2">初始化用于存放BTS记录的内存空间</h2>
<p>这部分就不具体介绍了，直接可以使用内核的ExAllocatePool来分配内存单元，需要注意的是，对所分配的内存必须位于系统的Nonpage pool中。在这里假设所创建的内存空间用符号pBtsBuffer表示：</p>
<div class="quote">BTS_ITEM_BLOCK_P4 *pBtsBuffer;</div>
<p>&nbsp;</p>
<h2 class="std_h2">设置DS_BUFFER_MGR_BLOCK</h2>
<p>对于DS_BUFFER_MGR_BLOCK的设置只需要关注BTS部分，假设已经分配了2000个BTS记录的内存空间，且希望当记录到第1900项时触发中断，那么初始化代码可以是：</p>
<div class="code-cstyle">
<p>DS_BUFFER_MGR_BLOCK *pDSBlk = &lt;some allocating code&gt;;</p>
<p>pDSBlk-&gt;pBTSBufferBase = pBtsBuffer;<br />
pDSBlk-&gt;pBTSBufferIndex =&nbsp; pDSBlk-&gt;pBTSBufferBase;</p>
<p>pDSBlk-&gt;pBTSMaxSize = &amp;pBtsBuffer[2000];<br />
pDSBlk-&gt;pBTSIntThresold = &amp;pBtsBuffer[1900];</p>
</div>
<p>在设置好了数据后，就要将DS_BUFFER_MGR_BLOCK的地址写入IA32_DS_AREA ：</p>
<div class="code-cstyle">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov ecx,IA32_DS_AREA<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov eax, pDSBlk<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WRMSR</div>
<p>其中，&nbsp;IA32_DS_AREA为值：0x600</p>
<p>&nbsp;</p>
<h2 class="std_h2">编写BTS溢出处理中断服务程序</h2>
<p>在Windows下添加中断处理程序的具体方式这里就不介绍了，可以参照Undocumented Windows NT[4]。这里需要注意几个问题：</p>
<ol>
    <li>在进入中断后以关闭BTS为宜 </li>
    <li>在中断退出前，需要设置pDSBlk-&gt;pBTSBufferIndex到初始状态，即pDSBlk-&gt;pBTSBufferIndex =&nbsp; pDSBlk-&gt;pBTSBufferBase; </li>
    <li>当BTS中断触发后，CPU将在Local APCI中的Performance Mon. Counter寄存器项上打开屏蔽位，从而阻止对中断的二次触发，所以需要在中断结束前关闭该屏蔽位 </li>
    <li>给APIC发送EOI命令，通知该中断结束服务 </li>
</ol>
<p>&nbsp;对于BTS的开关操作，将在后续介绍。对于清除Performance Mon. Counter寄存器的屏蔽位，可以通过简单的对其进行重设达到效果，因此可以参考后面介绍的方法。</p>
<p>下面给出一个简单的中断处理过程代码：</p>
<p>&nbsp;</p>
<div class="code-cstyle">
<p>void __declspec( naked )&nbsp;&nbsp;&nbsp; BTS_handler(void)<br />
{&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; //保存当前线程状态，恢复内核模式相关寄存器<br />
&nbsp;&nbsp;&nbsp;&nbsp; __asm<br />
&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cli<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pushad<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pushfd<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;fs<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;ds<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;es<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;gs</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; ebx,0x30<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; eax,0x23<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; fs,bx<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; ds,ax<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; es,ax<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; <strong>//关闭BTS，具体见后文<br />
&nbsp;&nbsp;&nbsp; __asm{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov ecx,MSR_DEBUGCTL<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RDMSR<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov tmp_old_msr_bts,eax<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xor eax,eax<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WRMSR<br />
&nbsp;&nbsp;&nbsp; }<br />
</strong>&nbsp;&nbsp;&nbsp; //相关操作，如保存BTS到磁盘<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; //结束中断处理--------------------------------------<br />
&nbsp;&nbsp;&nbsp; <strong>pDSBlk-&gt;pBTSBufferIndex =&nbsp; pDSBlk-&gt;pBTSBufferBase; //重设BTS记录指针<br />
</strong>&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; <strong>//开启BTS，具体见后文<br />
&nbsp;&nbsp;&nbsp; __asm<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //restore BTS<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov ecx,MSR_DEBUGCTL<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov eax,tmp_old_msr_bts<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WRMSR<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; //清除local apic中preformance mon. counter的屏蔽位，具体间后文<br />
&nbsp;&nbsp;&nbsp; local_apic-&gt;lvt_pc.mask=0;<br />
&nbsp;&nbsp;&nbsp; //发送eoi指令字<br />
&nbsp;&nbsp;&nbsp; local_apic-&gt;eoi.eoi = 0;<br />
</strong>&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; //恢复当前线程环境<br />
&nbsp;&nbsp;&nbsp; __asm{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sti<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pop&nbsp;gs<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pop es<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pop ds<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pop fs<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; popfd<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; popad<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iretd&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; }<br />
}</p>
</div>
<p>&nbsp;</p>
<p>其中，对于BTS的开启关闭，将在后文介绍。这里主要讨论对于local apic的操作。在前一章中已经知道，pc中将该芯片的寄存器映射到了物理地址的0xFEE00000H处。而按照Microsoft Windows Internals[5]中的描述，该地址在windows中被映射到了虚拟地址：0xFFFFD000H处。</p>
<p>经过我的验证，的确如此，但是估计因为内核的保护机制，对这块映射的内存地址并不能直接操作。首先是对于很多寄存器，比如发送EOI的EOI寄存器，对它的写入操作将导致BSOD。同时，我也发现内核采取了缓存的手段。</p>
<p>这里采用来自Micah Richert[6]的方法。同时感谢他提供的local apic结构声明[7]。不通过windows内核映射的空间，而是直接重新将那块物理地址进行映射：</p>
<div class="code-cstyle">&nbsp;&nbsp;&nbsp; PHYSICAL_ADDRESS&nbsp;&nbsp;&nbsp; pa;<br />
<br />
&nbsp;&nbsp;&nbsp; pa.HighPart = 0; <br />
&nbsp;&nbsp;&nbsp; pa.LowPart = 0xFEE00000; // 原始物理地址<br />
&nbsp;&nbsp;&nbsp; local_apic = (PAPIC)MmMapIoSpace(pa,sizeof(APIC), <strong>MmNonCached</strong>);</div>
<p>注意其中的黑体参数。今后便可以直接操作local_apic 。同时，Micah Richert定义了很完善的local apic寄存器结构，可以直接用结构体定义完成操作。</p>
<p>按照intel手册的描述，在中断完成后需要发送eoi指令，否则该中断将得不到再次触发。具体做法就是给local apic中的eoi寄存器清零。具体代码见上文。</p>
<p>&nbsp;</p>
<h2 class="std_h2">安装中断服务程序并设置Performance Mon. Counter 寄存器</h2>
<p>&nbsp;对于中断服务程序的安装（注册）可以参考很多现有的文章，比如[4]。其中，我选用了ID为0x28的中断，因为win32中该中断项往往是闲置的。具体的手段就是修改IDT表，这部分内容这里不再涉及。</p>
<p>在完成中断的安装后，需要设置Performance Mon. Counter 寄存器。比如我设置int 0x28为BTS溢出中断，那么Performance Mon. Counter需要如下设置：</p>
<div class="code-cstyle">local_apci-&gt;lvt_pc = 0x28;</div>
<p>&nbsp;</p>
<h2 class="std_h2">BTS功能的开关</h2>
<p>对于NetBurst构架下，将通过MSR_DEBUGCTL MSR进行BTS的开关操作，下图为该MSR我们所关心的位：</p>
<p align="center">&nbsp;<img alt="" src="http://www.csksoft.net/data/pic/bts_BTS_MSR.gif" /></p>
<p>换句话说，我们只需要往该MSR中写入0x1c即可开启BTS。而清零则可关闭。</p>
<div class="code-cstyle">
<p>//开启BTS</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov ecx,MSR_DEBUGCTL<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>mov eax,0x1c</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WRMSR</p>
</div>
<p>&nbsp;</p>
<div class="code-cstyle">
<p>//关闭BTS</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov ecx,MSR_DEBUGCTL<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>xor&nbsp; eax,eax</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WRMSR</p>
</div>
<p>&nbsp;</p>
<h2 class="std_h2">其他问题</h2>
<p>上述办法仅针对但核心的P4处理器有效。如果面对多核环境或者多处理器环境，需要分别设置每个逻辑处理单元的local apic。同时可能要考虑潜在的竞争情况。</p>
<p>通过上述办法捕捉到的BTS将包含整个系统的分支信息，其中不但混杂着用户态、内核态（甚至虚拟化技术的Hypervisor模式），同时不同线程也将混合在一起。因此对于实用的分析，需要作额外的处理。比如要知道线程切换的信息。这里可以参考Pjf关于线程切换信息的捕获方式[8]。</p>
<p>另一个需要注意的问题是如果直接在中断处理程序中进行对BTS写入文件将严重影响当前线程的执行。同时如果执行在不同的线程上下文中将使得诸如ZwWriteFile等导致系统锁死。</p>
<p>&nbsp;</p>
<h1 class="std_h1">小结</h1>
<p>经过上述的设置过程，即可实现win32下的BTS功能开启。目前BTS已经广泛的存在intel各系列CPU中。同时也被其他各类CPU所采用。除了目前性能分析软件使用外，BTS对系统分析、逆向工程等均有很大</p>
<h1 class="std_h1">参考文献</h1>
<p>[1] Chapter 18.6, Intel 64 and IA-32 Architectures Software Developer&rsquo;s Manual, Volume 3B: System Programming Guide, Part 2 </p>
<p>[2] perfmon2, the hardware-based performance monitoring interface for Linux, <a href="http://perfmon2.sourceforge.net">http://perfmon2.sourceforge.net</a></p>
<p>[3] Chapter 9.4, Intel 64 and IA-32 Architectures Software Developer&rsquo;s Manual, Volume 3A: System Programming Guide, Part&nbsp;1 <br />
[4] Prasad Dabak, Sandeep Phadke, Milind Borate, Undocumented Windows NT, Chapter 10 </p>
<p>[5] Mark E. Russinovich and David A. Solomon, Microsoft Windows Internals, 4th Edition</p>
<p>[6] Micah Richert, <a href="http://www.snl.salk.edu/~micah/">http://www.snl.salk.edu/~micah/</a></p>
<p>[7] Micah Richert, RTInterrupt, <a href="http://www.snl.salk.edu/~micah/RTInterrupt/">http://www.snl.salk.edu/~micah/RTInterrupt/</a></p>
<p>[8] Pjf, <a href="http://www.blogcn.com/user17/pjf/blog/4331410.html">http://www.blogcn.com/user17/pjf/blog/4331410.html</a></p>]]></description><category>系统/平台/设计</category><comments>http://www.csksoft.net/blog/post/bts_setup.html#comment</comments><wfw:comment>http://www.csksoft.net/blog/</wfw:comment><wfw:commentRss>http://www.csksoft.net/blog/sydication.asp?cmt=234</wfw:commentRss><trackback:ping>http://www.csksoft.net/blog/cmd.asp?act=tb&amp;id=234&amp;key=e9bd68ce</trackback:ping></item><item><title>为系里面设计的系衫</title><author>csk@citiz.net (csk)</author><link>http://www.csksoft.net/blog/post/233.html</link><pubDate>Thu, 19 Jun 2008 20:39:06 +0800</pubDate><guid>http://www.csksoft.net/blog/post/233.html</guid><description><![CDATA[<div class="quote">无关的话：长达近4个月的MSRA实习结束了，这里也该更新了。所以先写一些很早就该说的事</div>
<div class="quote">本文章的逻辑发布时间大致为4.28左右</div>
<p>今年是我所在的计算机系50周年庆，同时作为即将毕业的学生。能为自己系设计纪年衫十分荣幸，而且该系衫是免费向我们发放的:-)</p>
<p>&nbsp;</p>
<p>下面是效果图，衣服我猜最近应该能拿到了，期待一下。不过之前没设计过纪念衫，专业人士就别拍我了... </p>
<p>本次采用2色印刷，所以色彩上还是比较有限制的</p>
<p>&nbsp;</p>
<p align="center"><img alt="" src="http://www.csksoft.net/data/pic/cs-tshirt-front.jpg" /></p>
<p align
<p align="center">&nbsp;</p>
<p align="center"><img alt="" src="http://www.csksoft.net/data/pic/cs-tshirt-back.jpg" /></p>
<p align="center">反面</p>
<p align="center">&nbsp;</p>
<p>什么使用权我就不要了，需要的直接拿源文件去吧</p>
<p><a href="http://www.csksoft.net/data/cs-tshirt-src_v2_csk.rar">http://www.csksoft.net/data/cs-tshirt-src_v2_csk.rar</a></p>
<p>&nbsp;</p>
<p>&nbsp;</p>]]></description><category>动态</category><comments>http://www.csksoft.net/blog/post/233.html#comment</comments><wfw:comment>http://www.csksoft.net/blog/</wfw:comment><wfw:commentRss>http://www.csksoft.net/blog/sydication.asp?cmt=233</wfw:commentRss><trackback:ping>http://www.csksoft.net/blog/cmd.asp?act=tb&amp;id=233&amp;key=120fa54b</trackback:ping></item><item><title>启用新logo,欢迎交换链接</title><author>csk@citiz.net (csk)</author><link>http://www.csksoft.net/blog/post/site_logo.html</link><pubDate>Thu, 27 Mar 2008 01:11:37 +0800</pubDate><guid>http://www.csksoft.net/blog/post/site_logo.html</guid><description><![CDATA[<p>很久就打算重新设计网站的logo以便相互交换链接用。但估计是事情太多，同时也太懒。终于在2年后做出了新logo...</p>
<p>&nbsp;</p>
<h2 class="std_h2">标准尺寸 (94 x 37)</h2>
<p align="center"><img alt="CSKSOFT_STD" src="http://www.csksoft.net/data/logo/std_web_logo.png" /></p>
<p align="left">&nbsp;</p>
<div class="quote">图片地址：<a href="http://www.csksoft.net/data/logo/std_web_logo.png">http://www.csksoft.net/data/logo/std_web_logo.png</a></div>
<h2 class="std_h2">扩展版本 (120 x 40)</h2>
<p align="center"><img alt="CSKSOFT_STD" src="http://www.csksoft.net/data/logo/std_web_logo_120_40.png" /></p>
<p align="center">&nbsp;</p>
<div class="quote">图片地址：<a href="http://www.csksoft.net/data/logo/std_web_logo_120_40.png">http://www.csksoft.net/data/logo/std_web_logo_120_40.png</a></div>
<p align="left">&nbsp;</p>
<p align="left">原始的logo因为本人没有任何经验，尺寸都不是标准的.. 这次应该还看得过去。</p>
<p align="left">&nbsp;</p>
<h2 class="std_h2">希望指向的地址是: </h2>
<div class="quote"><a href="http://www.csksoft.net/">http://www.csksoft.net</a><a href="http://www.csksoft.net"></a></div>
<p align="left">&nbsp;</p>
<h2 class="std_h2">如果希望仅仅指向本人Blog，地址是：</h2>
<div class="quote"><a href="http://www.csksoft.net/blog">http://www.csksoft.net/blog</a><a href="http://www.csksoft.net/blog"></a></div>
<p align="center">或者</p>
<div class="quote"><a href="http://blog.csksoft.net/">http://blog.csksoft.net</a><a href="http://blog.csksoft.net"> (考虑到域名服务不太稳定，故不推荐)</a></div>
<p align="left">&nbsp;</p>
<h2 class="std_h2">如果是文字链接，推荐的文字信息是：</h2>
<div class="quote">CSKSOFT</div>
<p align="center">或者</p>
<div class="quote">CSK.Blog</div>
<p align="left">&nbsp;</p>
<p align="left">下面是一个指向我主网站的logo代码示例：</p>
<div class="code-xml">&lt;a href=&quot;<a href="http://www.csksoft.net/">http://www.csksoft.net</a>&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;<a href="http://www.csksoft.net/data/logo/std_web_logo.png"><u>http://www.csksoft.net/data/logo/std_web_logo.png</u></a>&quot; border=&quot;0&quot; alt=&quot;CSKSOFT&quot; /&gt;&lt;/a&gt;</div>
<p align="left">效果：</p>
<p align="center"><a target="_blank" href="http://www.csksoft.net"><img alt="CSKSOFT" border="0" src="http://www.csksoft.net/data/logo/std_web_logo.png" /></a></p>
<p align="left">&nbsp;</p>
<p align="left">如果你有自己的Blog或者网站等，十分欢迎相互交换链接。具体方式可以联系我（见 <a href="http://www.csksoft.net/blog/">关于</a>）。</p>
<p align="left">我会将你网站的logo和链接置于blog中的友情链接中。</p>
<p align="left">&nbsp;</p>
<p align="left">在此感谢所有关心和鼓励我和本网站的朋友们</p>]]></description><category>动态</category><comments>http://www.csksoft.net/blog/post/site_logo.html#comment</comments><wfw:comment>http://www.csksoft.net/blog/</wfw:comment><wfw:commentRss>http://www.csksoft.net/blog/sydication.asp?cmt=232</wfw:commentRss><trackback:ping>http://www.csksoft.net/blog/cmd.asp?act=tb&amp;id=232&amp;key=e8114d1f</trackback:ping></item><item><title>在CSDN游戏开发专家BLOG上设立镜像的说明</title><author>csk@citiz.net (csk)</author><link>http://www.csksoft.net/blog/post/231.html</link><pubDate>Sat, 15 Mar 2008 02:56:23 +0800</pubDate><guid>http://www.csksoft.net/blog/post/231.html</guid><description><![CDATA[很感谢CSDN编辑的热情邀请，为了让更多人看到有用的消息。我在CSDN 游戏开发专家blog区开设了本blog的镜像。基本上今后技术类的文章将会全文转发过去。但是因为一些样式的问题，可能在csdn的镜像会有些显示差别。但不会影响阅读。]]></description><category>动态</category><comments>http://www.csksoft.net/blog/post/231.html#comment</comments><wfw:comment>http://www.csksoft.net/blog/</wfw:comment><wfw:commentRss>http://www.csksoft.net/blog/sydication.asp?cmt=231</wfw:commentRss><trackback:ping>http://www.csksoft.net/blog/cmd.asp?act=tb&amp;id=231&amp;key=1fad9a3b</trackback:ping></item><item><title>VC编写Demo Scene的一些可能技巧</title><author>csk@citiz.net (csk)</author><link>http://www.csksoft.net/blog/post/demo_scene_tip1.html</link><pubDate>Sat, 15 Mar 2008 01:27:53 +0800</pubDate><guid>http://www.csksoft.net/blog/post/demo_scene_tip1.html</guid><description><![CDATA[本来也不是专门为了写这篇文章，只不过觉得已这样的形式发表比较合适。同时好久都没有写过教程了，以往都以简单的发表作品或者通报一些事情为主。总的来说这篇文章还是有点参考价值的，希望能有所帮助。</p>
<p>&nbsp;</p>
<h2 class="std_h2">转载注意：</h2>
<p>请注明原文出处：&nbsp;<a href="http://www.csksoft.net/blog/post/demo_scene_tip1.html">http://www.csksoft.net/blog/post/<font color="#0000ff">demo_scene_tip1.html</font></a></p>
<p>&nbsp;</p>
<p>Demo Scene我就不想再介绍了，对他还不了解或者听说过但不明白原理和背景的可以参考我以前写的一些文章：</p>
<div class="quote">
<p>Demo Scene:Principles,Techniques,and Tools (<a href="http://www.csksoft.net/blog/post/154.html">http://www.csksoft.net/blog/post/154.html</a>)</p>
<p>模块音乐(Mod)的制作和使用,Demo程序的主体之一 (<a href="http://www.csksoft.net/blog/post/intro2track_music.html">http://www.csksoft.net/blog/post/intro2track_music.html</a>)</p>
</div>
<p>&nbsp;</p>
<p>目前国内Demo Scene基本处在0起步的阶段，已经有了一些小团体打算去参加欧洲的比赛，但是还没有一定的规模。同时，对于制作这类程序网上也没有系统的资料。使得制作Demo Scene被看成一种高深的事情。</p>
<p>下面我就说说目前在Windows平台下，使用最常用的开发工具(Visual C++)如何来制作一个符合64kb demo的程序框架和常用技巧。当然这只是一些次要手法，最核心的还是3d引擎、mod音乐的设计。因为那些资料很好找。所以就不再涉及。</p>
<p>&nbsp;</p>
<p>我将介绍下面几个方面的技术：</p>
<p>&nbsp;</p>
<div class="quote">
<p>1.如何产生体积最小的程序</p>
<p>2.如何不使用C运行库开发程序</p>
<p>3.如何实现高速GDI绘图</p>
<p>4.对于NT5.0提供的LayeredWindow的使用--不规则窗体、窗口的AlphaBlend渲染、鼠标事件穿透</p>
<p>5.如何将所有数据（代码、图片等）整合在一个C文件中</p>
<p>6.其他的一些编译技巧</p>
<p>7.一个完整的示例程序代码</p>
</div>
<h1 class="std_h1">问题和需求</h1>
<p>Scene Demo中有一个项目为4kb-intro 或者 64kb-intro。 他要求Demo的程序体积必须小于或者正好等于4/64kb。而往往正是这类Demo程序在国内流传最广。因为大家都认为那么小的体积能播放长时间的高品质3d动画和音乐是不可思议得。甚至有人将45分钟的demo动画看成是avi视频，45分钟的音乐算作44KHz采样的wav。计算出将他们压缩到64kb完全是不可能的（见farbrausch的作品: the product 中的说明字幕）。当然这只是忽悠外行的吓人话。其实写过游戏引擎的人都知道那只是通过实时渲染的到的，而音乐本身就是体积在12kb左右的mod音乐序列（见我以前写过的文章）。</p>
<p>&nbsp;</p>
<p>目前很多机器都已安装最新版本的DirectX，而OpenGL是windows的默认库之一。这样Demo Scene设计者一般就不需要自己去编写基本的3d引擎。动画部分几个基本特效的代码不会超过30kb（这里假设开发者具有较高的设计素质），而一些复杂网格模型的纹理贴图即时采用bmp保存，也在100kb-300kb左右。加上mod音乐和其播放引擎。一个64kb DemoScence程序的原始体积一般应在600kb。而不是通过用等效为avi文件计算方法算出的几个G的天文数字。</p>
<p>&nbsp;</p>
<p>不过，问题就产生了，实际程序体积只有64kb。在这600kb-&gt;64kb还是有相当距离的。如何尽可能的去减少这部分文件大小，以及其中伴随的一些技巧就是本文所要讨论的。</p>
<p>....</p>
<p align="center"><img alt="" src="http://www.csksoft.net/data/pic/fruit_alert_scr.jpg" /></p>
<p><b>请点击文章查看完整内容</b><p>]]></description><category>系统/平台/设计</category><comments>http://www.csksoft.net/blog/post/demo_scene_tip1.html#comment</comments><wfw:comment>http://www.csksoft.net/blog/</wfw:comment><wfw:commentRss>http://www.csksoft.net/blog/sydication.asp?cmt=230</wfw:commentRss><trackback:ping>http://www.csksoft.net/blog/cmd.asp?act=tb&amp;id=230&amp;key=528983dc</trackback:ping></item><item><title>让一切都过去吧，高手挑战2过关方法</title><author>csk@citiz.net (csk)</author><link>http://www.csksoft.net/blog/post/Netcompet2_solutions.html</link><pubDate>Fri, 22 Feb 2008 23:55:35 +0800</pubDate><guid>http://www.csksoft.net/blog/post/Netcompet2_solutions.html</guid><description><![CDATA[<p>这个游戏已经好久没有维护了，就连我也差点忘记自己还制作过这个游戏。不过没想到的是一直还有玩家在尝试过关，虽然和刚发布时候相比已经冷清很多。考虑到今后自己可能没有时间再出后续版本，同时对很多人来说，这个游戏的第二版或许难度高了些。也有人写信给我怀疑第二版根本无法通过。因此，我花了点时间，自己作为一名闯关者，把这个游戏过了一遍。下面就是我本人的过关方法，这个攻略应该说是&ldquo;官方&rdquo;版本了，也就是我出题时希望挑战者采取的方法。</p>
<p>&nbsp;</p>
<p><strong>虽然是面向初学者的游戏，挑战2还是有一定深度和可玩性的。没尝试过的朋友不妨借助这篇文章自己体验下:-)</strong></p>
<p>&nbsp;</p>
<h2 class="std_h2">转载注意.保留下面信息:</h2>
<div class="quote">来源: <a href="http://www.csksoft.net/blog">http://www.csksoft.net/blog</a></div>
<p>&nbsp;</p>
<h1 class="std_h1">背景</h1>
<p>高手挑战是我在发布自己网站之后不久制作的一款网页闯关游戏，原本只是为了好玩随便制作了下，但出乎我意料的是他的第一版在网络上大受欢迎。</p>
<p>接着我制作了这个游戏的第二个版本，他的难度和前一版相比有很大的提升，不再是简单的设计一些基础的操作能力。而是涉及互联网相关技术(HTTP协议/HTML/CSS/XML/RPC/FLASH/JavaScript等)、图像处理、音频处理、基本的反汇编调试技术、基本的网络安全技术、原始数据恢复等方面。同时游戏有不单单简单的考察这些知识，而是通过巧妙组织具有很强的趣味性。</p>
<p>&nbsp;</p>
<div class="quote">
<p>游戏的URL是：<a href="http://www.csksoft.net/netcompet2/">http://www.csksoft.net/netcompet2/</a></p>
<p>与2005年年初发布，目前累计有50多人通过挑战，参与者9240人，通过率 &lt;= 0.5%</p>
</div>
<p>&nbsp;</p>
<p>通过上面数据，可以看出游戏还是具有一定的难度的。为了防止通过者将答案散步在网上，我对游戏也作了一定的处理，比如最终闯关成功认证时候需要验证码，而验证码是由之前闯关时游戏者收集到的验证码碎片通过特定的计算公式得出的。而验证码碎片以及计算公式对于不同玩家是不相同的。另外，游戏系统会纪录每个游戏者的游戏进度，一方面可以帮助游戏者下次登陆后能自动进入上一次的关卡继续游戏，同时也能在一定程度上防止提前进入没有通过的关卡(防止直接从网上看到答案)。</p>
<p>虽然这些手段不能非常有效地阻止答案泄露，但一定程度上还是有作用的。因此从游戏发布至今将近3年中，通过者寥寥无几，网上也很难找到比较完善的通关策略。</p>
<p>&nbsp;</p>
<div class="quote">
<p>一共14关，每一关就是一个页面，游戏者需要做的就是通过当前关卡提供的信息，找到下一关的页面地址。</p>
<p>在通关过程中需要收集5个验证码片断</p>
<p>最终，通关者需要将收集来的验证码片断通过游戏提供的工具组合成最终的验证码，并通过最终的验证。此时闯关成功。</p>
</div>
<p>&nbsp;</p>
<h1 class="std_h1">通关方法</h1>
<p>下面介绍的方法是我自己亲自操作得来的，因此可以保证100%的正确性。</p>
<p>但是需要注意的是，就像上面提到的，具体的数据和方法对于每个人都存在差异，因此不可能照搬得来。</p>
<p>本游戏在中途关卡也做过调整，这次公布的方法包括了调整前后的差别。</p>
<p>&nbsp;</p>
<h2 class="std_h2">第一关</h2>
<p>地址：<a href="http://www.csksoft.net/netcompet2/gamearea/start2.htm">http://www.csksoft.net/netcompet2/gamearea/start2.htm</a></p>
<p>&nbsp;</p>
<p>这一关的线索在网页源代码中，通过提示察看其中的CSS文件stdStyle.css</p>
<p>会发现如下代码：</p>
<div class="quote">nextstage {<br />
&nbsp;URL:deefr3c.htm;<br />
}</div>
<p>deefr3c.htm就是下一关地址</p>
<p>&nbsp;</p>
<div class="quote">
<p>获得验证码碎片：CT02</p>
</div>
<p>&nbsp;</p>
<h2 class="std_h2">第二关</h2>
<p>地址：<a href="http://www.csksoft.net/netcompet2/gamearea/deefr3c.htm">http://www.csksoft.net/netcompet2/gamearea/deefr3c.htm</a></p>
<p>&nbsp;</p>
<p>这关是一个ASCII变换问题，由提示中给出的关系：@LE -&gt; htm</p>
<p>通过查找ASCII码表可知，@与h，L与t，E与m他们的ASCII值均相差40，因此，可以只需要将本关给出的字符串:+L=J=G?J9E.@LE，一次把每个字符的ASCII值加40，遍可以获得下一关的地址。</p>
<p>上述计算可以手工查表计算，也可以编写程序获得。这里推荐利用我Flash网站的即时代码执行功能编程计算得到。下面是计算本关答案的ReformScript代码：</p>
<div class="code-cstyle">
<p>var src_text;<br />
var dest_text;</p>
<p>src_text&nbsp; = &quot;+L=J=G?J9E.@LE&quot;;<br />
dest_text = &quot;&quot;;</p>
<p>for (i=0;i&lt;src_text.length;i++)<br />
{<br />
&nbsp;&nbsp; cur_ascii = src_text.charCodeAt(i);<br />
&nbsp;&nbsp; if (src_text.substr(i,1) != &quot;.&quot;) {<br />
&nbsp;&nbsp;&nbsp; cur_ascii += 40;<br />
&nbsp;&nbsp; }<br />
&nbsp;&nbsp; dest_text+= String.fromCharCode(cur_ascii);<br />
}<br />
msgout(dest_text);</p>
</div>
<p>&nbsp;将上述代码复制入Flash网站并执行，获得结果：Stereogram.htm</p>
<p>&nbsp;</p>
<h2 class="std_h2">第三关</h2>
<p>地址：<a href="http://www.csksoft.net/netcompet2/gamearea/Stereogram.htm">http://www.csksoft.net/netcompet2/gamearea/Stereogram.htm</a></p>
<p>按照提示的说明，下载页面上的图片文件：stere.jpg</p>
<p>下一关的信息就藏在该文件中，根据jpg的定义，其中允许保留创建程序的字段信息。可以通过16进制察看器观察。这里采用windows/DOS系统自带的debug工具。</p>
<p>在debug中输入如下指令：</p>
<div class="quote">N stere.jpg<br />
L 100<br />
D 100 L100</div>
<p>此时，该文件头部长度为0x100字节的数据显示如下：</p>
<p><font face="Courier New" size="1">-d100 L100<br />
13D4:0100&nbsp; FF D8 FF E0 00 10 4A 46-49 46 00 01 02 01 00 48&nbsp;&nbsp; ......JFIF.....H<br />
13D4:0110&nbsp; 00 48 00 00 FF E1 34 2C-45 78 69 66 00 00 4D 4D&nbsp;&nbsp; .H....4,Exif..MM<br />
13D4:0120&nbsp; 00 2A 00 00 00 08 00 07-01 12 00 03 00 00 00 01&nbsp;&nbsp; .*..............<br />
13D4:0130&nbsp; 00 01 00 00 01 1A 00 05-00 00 00 01 00 00 00 62&nbsp;&nbsp; ...............b<br />
13D4:0140&nbsp; 01 1B 00 05 00 00 00 01-00 00 00 6A 01 28 00 03&nbsp;&nbsp; ...........j.(..<br />
13D4:0150&nbsp; 00 00 00 01 00 02 00 00-01 31 00 02 00 00 00 1B&nbsp;&nbsp; .........1......<br />
13D4:0160&nbsp; 00 00 00 72 01 32 00 02-00 00 00 14 00 00 00 8D&nbsp;&nbsp; ...r.2..........<br />
13D4:0170&nbsp; 87 69 00 04 00 00 00 01-00 00 00 A4 00 00 00 D0&nbsp;&nbsp; .i..............<br />
13D4:0180&nbsp; 00 00 00 48 00 00 00 01-00 00 00 48 00 00 00 01&nbsp;&nbsp; ...H.......H....<br />
13D4:0190&nbsp; 43 53 4B 53 4F 46 54 20-53 74 65 72 65 6F 69 63&nbsp;&nbsp; <font color="#ff0000">CSKSOFT Stereoic</font><br />
13D4:01A0&nbsp; 20 4B 65 79 3A 44 72 65-61 6D 69 6E 67 43 68 69&nbsp;&nbsp;&nbsp;<font color="#ff0000"> Key:DreamingChi</font><br />
13D4:01B0&nbsp; 6C 64 2E 68 74 6D 20 42-65 20 43 61 72 65 00 00&nbsp;&nbsp; <font color="#ff0000">ld.htm Be Care</font>..<br />
13D4:01C0&nbsp; 00 00 00 03 A0 01 00 03-00 00 00 01 00 01 00 00&nbsp;&nbsp; ................<br />
13D4:01D0&nbsp; A0 02 00 04 00 00 00 01-00 00 02 42 A0 03 00 04&nbsp;&nbsp; ...........B....<br />
13D4:01E0&nbsp; 00 00 00 01 00 00 01 AE-00 00 00 00 00 00 00 06&nbsp;&nbsp; ................<br />
13D4:01F0&nbsp; 01 03 00 03 00 00 00 01-00 06 00 00 01 1A 00 05&nbsp;&nbsp; ................</font></p>
<p>&nbsp;</p>
<p>注意上述红字部分，得到字符串DreamingChild.htm</p>
<p>&nbsp;</p>
<p>此时，大部分玩家会以为下一关的地址就是<a href="http://www.csksoft.net/netcompet2/gamearea/DreamingChild.htm">http://www.csksoft.net/netcompet2/gamearea/DreamingChild.htm</a></p>
<p>虽然上述链接的确会进入一个关卡，但显示为3.5关。这是一个陷阱，此时需要回到第三关页面，在html代码中找到注释的代码：</p>
<div class="quote">
<p>&lt;!--<br />
&nbsp;别忘了这里！<br />
&nbsp;&lt;TitleName&gt;.&lt;ExtName&gt;<br />
&nbsp;Asc(&lt;NewName&gt;(n))=Asc(&lt;TitleName&gt;(n))+1 'where n ranges from 1 to Length(&lt;TitleName&gt;)<br />
&nbsp;FinalData:&lt;NewName&gt;.&lt;ExtName&gt;<br />
--&gt;</p>
</div>
<p>说明下一关的正确地址是需要通过对前面得出的字符串作一定变换得来的，阅读伪代码，很容易知道这也是一个ASCII变换问题。可以通过修改之前给出的ReformScript代码计算出本关答案：</p>
<div class="code-cstyle">
<p>var src_text;<br />
var dest_text;</p>
<p>src_text&nbsp; = &quot;DreamingChild&quot;;<br />
dest_text = &quot;&quot;;</p>
<p>for (i=0;i&lt;src_text.length;i++)<br />
{<br />
&nbsp;&nbsp; cur_ascii = src_text.charCodeAt(i);<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp; cur_ascii += 1;<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp; dest_text+= String.fromCharCode(cur_ascii);<br />
}<br />
msgout(dest_text);</p>
</div>
<p>执行上述代码，得到结果：EsfbnjohDijme，因此，下一关地址为EsfbnjohDijme.htm</p>
<p>&nbsp;</p>
<h2 class="std_h2">第四关</h2>
<p>地址：<a href="http://www.csksoft.net/netcompet2/gamearea/EsfbnjohDijme.htm">http://www.csksoft.net/netcompet2/gamearea/EsfbnjohDijme.htm</a></p>
<p>这一关页面中，要求闯关者输入正确的密码以便进入下一关。</p>
<p>当然这不可能要求闯关者去猜测出密码，破解的思路是查看页面代码，会发现如下的JavaScript:</p>
<div class="code-cstyle">function Chk(pass) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (pass ==GetPassWord()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; alert(&quot;密码正确&quot;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; location.href = GetNextStg();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; alert(&quot;密码错误！&quot;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; location.href = &quot;<a href="http://www.csksoft.net/">http://www.csksoft.net</a>&quot;;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;}</div>
<p>很明显，这段代码是用于判断输入的密码是否正确，而程序仅仅是通过将输入的密码pass和由函数GetPassWord()给出的正确密码进行比较。因此，破解的思路就是设法获取GetPassWord()函数的返回值或者查看函数代码。</p>
<p>继续分析页面源代码，会发现GetPassWord函数是由GetPassWord.asp文件给出的：</p>
<div class="code-xml">&lt;script&nbsp; src=&quot;http://www.csksoft.net/NetCompet2/gamearea/GetPassWord.asp&quot;&gt;&lt;/script&gt;</div>
<p>尝试下载该文件，发现其中的内容是代码：</p>
<div class="code-cstyle">function GetPassWord(){return &quot;Stereoic&quot;}</div>
<p>&nbsp;十分令人吃惊的代码，因此，所需要的密码就是Stereoic。在输入密码后，便自动进入了下一关。</p>
<p>&nbsp;</p>
<h2 class="std_h2">第五关</h2>
<p>地址：<a href="http://www.csksoft.net/netcompet2/gamearea/hardDays.htm">http://www.csksoft.net/netcompet2/gamearea/hardDays.htm</a></p>
<p>这关涉及Flash的逆向工程和破解。</p>
<p>页面上Flash逐渐显示着下一关的地址，然而显示到中途却停止了。无法看到下一关地址的后半部分。此时需要通过对swf文件进行逆向工程，分析并提取其中的素材来获得答案。</p>
<p>分析本关页面，当前现实的flash文件是mainStg.swf，将它下载至本地后，使用相关的逆向工程工具分析，这里采用SWFDecompiler。</p>
<p>分析其中第一祯的代码：</p>
<div class="code-cstyle">@Frame:1 : movloder.loadMovie(&quot;nextlevel.swf&quot;);</div>
<p>发现具有下一关地址信息的flash文件并不是mainStg.swf，而是另一个文件：nextlevel.swf。因此将nextlevel.swf下载到本地，利用SWFDecompiler提取其中的图片资源，下一关的地址便出现了：</p>
<p>&nbsp;</p>
<p align="center"><img alt="" src="http://www.csksoft.net/data/netcompetII_solution/stage5.jpg" /></p>
<p align="left">&nbsp;</p>
<div class="quote">这一关的验证码碎片：06842fr</div>
<p>&nbsp;</p>
<h2 class="std_h2">第六关</h2>
<p>地址：<a href="http://www.csksoft.net/netcompet2/gamearea/dejvsdewd.htm">http://www.csksoft.net/netcompet2/gamearea/dejvsdewd.htm</a></p>
<p>这关没有任何技术难度，伪造了IE5.0的404默认显示页面。如果足够细心，就能发现。下一关地址使用白色字体存在于这个&quot;404错误&quot;的页面里，用ctrl+a反色显示就能发现</p>
<p>&nbsp;</p>
<h2 class="std_h2">第七关</h2>
<p>地址：<a href="http://www.csksoft.net/netcompet2/gamearea/eng-sfcd.htm">http://www.csksoft.net/netcompet2/gamearea/eng-sfcd.htm</a></p>
<p>同样也是没有难度的关，页面中需要填完整的句子是</p>
<div class="quote">The quick brown fox jumps over the lazy dog.</div>
<p>因为在这句话中，26个英文字母都出现了，所以常用于测试字体显示效果。在windows中的英文字体浏览就使用了这句话。</p>
<p>这关答案就是dog.htm</p>
<p>&nbsp;</p>
<h2 class="std_h2">第八关</h2>
<p>地址：<a href="http://www.csksoft.net/netcompet2/gamearea/dog.htm">http://www.csksoft.net/netcompet2/gamearea/dog.htm</a></p>
<p>这关只需要下载那段录音，使用相关音频处理工具，比如以前的cooledit，将播放速率降低即可。将听到的单词记下来，就是下一关地址</p>
<p>&nbsp;</p>
<h2 class="std_h2">第九关</h2>
<p>地址：<a href="http://www.csksoft.net/netcompet2/gamearea/goodday.html">http://www.csksoft.net/netcompet2/gamearea/goodday.html</a></p>
<p>这一关首先需要下载我编写的Stereoic程序，同时要借助一些调试工具。</p>
<p>&nbsp;</p>
<p>通过如下命令行来执行StereoPower: </p>
<div class="quote">StereoPower.exe /?</div>
<p>此时会弹出一个消息框，其中有与下一关相关的字符串：bmbouvsjohstbhbz</p>
<p>同时注意到页面中的文字：</p>
<div class="quote">&lt;CAESAR-1&gt;+&quot;.htm&quot;</div>
<p>CAESAR实际上就是上面获得的字符串，它实际也是一个ASCII变换处理，同样，利用上面的ReformScript做修改，就能获得下一关地址：</p>
<div class="code-cstyle">
<p>var src_text;<br />
var dest_text;</p>
<p>src_text&nbsp; = &quot;bmbouvsjohstbhbz&quot;;<br />
dest_text = &quot;&quot;;</p>
<p>for (i=0;i&lt;src_text.length;i++)<br />
{<br />
&nbsp;&nbsp; cur_ascii = src_text.charCodeAt(i);<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp; cur_ascii -= 1;<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp; dest_text+= String.fromCharCode(cur_ascii);<br />
}<br />
msgout(dest_text);</p>
</div>
<p>获得的结果是：alanturingrsagay</p>
<p>所以答案就是alanturingrsagay.htm</p>
<p>&nbsp;</p>
<p>同时，这一关还需要收集验证码碎片，但很多闯关者都会忽略。</p>
<p>获得验证码碎片的方法是需要修改Stereoic.exe的资源文件，可以用EXESCOPE或者Visual C++的资源编辑器修改。找到其中的图案设置对话框，会发现Visible=FALSE属性的按钮，将它设为可见，保存修改</p>
<p>&nbsp;</p>
<p align="center"><img alt="" src="http://www.csksoft.net/data/netcompetII_solution/stage9.jpg" /></p>
<p>&nbsp;</p>
<p>运行程序，点击那个按钮，从弹出的提示中知道StereoPower.exe的一个调用格式：</p>
<div class="quote">
<p>StereoPower.exe -p [fileSrc] [fileDest]</p>
</div>
<p align="center">&nbsp;</p>
<p>不过这次是需要获得程序的进程返回值，可以用Ollydbg或者Visual C++作为调试器调用程序获得，或者自己编写程序。这里给出相关的C代码片断：</p>
<div class="code-cstyle">
<p>STARTUPINFO sinfo;<br />
PROCESS_INFORMATION pinfo;<br />
DWORD Exitcode;</p>
<p>&nbsp;</p>
<p>ZeroMemory(&amp;sinfo,sizeof(STARTUPINFO));<br />
sinfo.cb=sizeof(sinfo);<br />
sinfo.dwFlags=STARTF_USESHOWWINDOW;<br />
sinfo.wShowWindow=SW_SHOW;<br />
&nbsp;&nbsp;<br />
CreateProcess(NULL,&quot;StereoPower.exe /?&quot;,NULL,NULL,FALSE,<br />
&nbsp;&nbsp;&nbsp; NORMAL_PRIORITY_CLASS,NULL,NULL,&amp;sinfo,&amp;pinfo);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
WaitForSingleObject(pinfo.hProcess, INFINITE);</p>
<p>GetExitCodeProcess(pinfo.hProcess,&amp;Exitcode);</p>
</div>
<p>&nbsp;</p>
<p><br />
最终，程序的进程返回值会保存在变量Exitcode中，他就是本关的验证码碎片</p>
<div class="quote">本关验证码碎片：FE</div>
<p>&nbsp;</p>
<h2 class="std_h2">第十关</h2>
<p>新地址：<a href="http://www.csksoft.net/netcompet2/gamearea/alanturingrsagay.htm">http://www.csksoft.net/netcompet2/gamearea/alanturingrsagay.htm</a></p>
<p>废弃地址：<a href="http://nofrost.nease.net/alanturingrsagay.htm">http://nofrost.nease.net/alanturingrsagay.htm</a></p>
<p>&nbsp;</p>
<p>关于这一关地址的变更以及废弃的关卡解法参考：<a href="http://www.csksoft.net/blog/post/48.html">http://www.csksoft.net/blog/post/48.html</a></p>
<p>这一关是常识问题，答案是ajax。</p>
<p>&nbsp;</p>
<h2 class="std_h2">第十一关</h2>
<p>地址：<a href="http://www.csksoft.net/netcompet2/gamearea/ajax.htm">http://www.csksoft.net/netcompet2/gamearea/ajax.htm</a></p>
<p>这关涉及数据分析技巧，下载页面中的那个二进制数据后，首先用16进制编辑器打开，察看数据。这里仍旧采用debug程序</p>
<p><font size="1"><font face="Courier New">-N bin.dat<br />
-L100<br />
-d100 l100<br />
13D4:0100&nbsp; 73 61 6D 70 6C 65 2E 62-6D 70 00 00 00 00 00 00&nbsp;&nbsp; <font color="#ff0000">sample.bmp</font>......<br />
13D4:0110&nbsp; 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00&nbsp;&nbsp; ................<br />
13D4:0120&nbsp; 00 00 30 30 30 30 30 30-31 30 30 30 40 30 30 30&nbsp;&nbsp; </font><font face="Courier New">..<font color="#0000ff">0000001000@000</font></font><br />
</font><font face="Courier New"><font size="1">13D4:0130&nbsp; 31 36 31 31 37 31 30 00-00 00 00 00 00 00 00 00&nbsp;&nbsp; <font color="#0000ff">1611710</font>.........<br />
13D4:0140&nbsp; 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00&nbsp;&nbsp; ................<br />
13D4:0150&nbsp; 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00&nbsp;&nbsp; ................<br />
13D4:0160&nbsp; 00 00 00 00 00 00 6D 61-69 6E 64 61 74 61 2E 6A&nbsp;&nbsp; ......</font><font size="1"><font color="#ff0000">maindata.j<br />
</font>13D4:0170&nbsp; 70 67 00 00 00 00 00 00-00 00 00 00 00 00 00 00&nbsp;&nbsp; <font color="#ff0000">pg</font>..............<br />
13D4:0180&nbsp; 00 00 00 00 00 00 00 00-30 30 30 31 36 31 32 37&nbsp;&nbsp; ........<font color="#0000ff">00016127</font><br />
13D4:0190&nbsp; 34 36 40 30 30 30 30 30-36 32 34 31 31 00 00 00&nbsp;&nbsp; </font></font><font face="Courier New" color="#0000ff" size="1">46@0000062411</font><font face="Courier New" size="1">...<br />
13D4:01A0&nbsp; 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00&nbsp;&nbsp; ................<br />
13D4:01B0&nbsp; 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00&nbsp;&nbsp; ................<br />
13D4:01C0&nbsp; 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00&nbsp;&nbsp; ................<br />
13D4:01D0&nbsp; 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00&nbsp;&nbsp; ................<br />
13D4:01E0&nbsp; 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00&nbsp;&nbsp; ................<br />
13D4:01F0&nbsp; 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00&nbsp;&nbsp; ................</font></p>
<p>&nbsp;</p>
<p>该文件类似于一种打包文件格式。在文件头部给出了该文件中所包含的2个文件，他们的文件名就是上面的红字部分。</p>
<p>接下来的任务就是确定这2个文件在此打包文件中的位置和大小。分析上面的蓝字部分。</p>
<p>可以猜测出，@符号前面的数字为本文件在此打包文件中的起始位置(偏移量)，@后面的数字自然就是文件长度。不过需要注意的是这里的数字都是8进制格式的，这一点比较难发现。</p>
<p>对上述数字作进制转化：</p>
<div class="quote">
<p>0000001000(Oct) -&gt; 512(Dec) -&gt; 0x200<br />
0001611710(Oct) -&gt; 463816(Dec) -&gt; 0x713C8</p>
<p>0001612746(Oct) -&gt; 464358(Dec) -&gt; 0x715E6<br />
0000062411(Oct) -&gt; 25865(Dec) -&gt; 0x6509</p>
</div>
<p>接下来把相关的数据单独提取保存就能得到sample.bmp和maindata.jpg这2个文件。</p>
<p>这里给出已经提取出来的版本：<a href="http://www.csksoft.net/data/netcompetII_solution/bin.rar">http://www.csksoft.net/data/netcompetII_solution/bin.rar</a></p>
<p>&nbsp;</p>
<p>接下来分析sample.bmp。按照图片的提示，同时需要联系第一版本的高手挑战。猜测这个bmp中有隐藏数据。因此需要使用我网站上提供的picmixer工具，将隐写在bmp中的数据提取出来。picmixer的下载地址是：<a href="ftp://FTP_Visitor:visitor@ftp.csksoft.net/Public/Products/APP/picmixer.rar">ftp://FTP_Visitor:visitor@ftp.csksoft.net/Public/Products/APP/picmixer.rar</a></p>
<p>或者在Flash网站中的作品区也能找到下载。</p>
<p>对bmp中隐写数据的提取，能得到文件SerialNum.txt。他的内容为：</p>
<div class="quote">本关验证码碎片:DTG</div>
<p>打开另外一个文件maindata.jpg，里面就是下一关的地址</p>
<p>&nbsp;</p>
<h2 class="std_h2">第十二关</h2>
<p>地址：<a href="http://www.csksoft.net/netcompet2/gamearea/decoder.htm">http://www.csksoft.net/netcompet2/gamearea/decoder.htm</a></p>
<p>这一关与之前要求输入密码类似，但察看源代码发现没有任何线索。</p>
<p>实际上他考察了对SQL注入漏洞的了解，用经典的注入代码：</p>
<div class="quote">' or ''='</div>
<p>作为用户名和密码，就能十分轻松的过关。</p>
<p>&nbsp;</p>
<div class="quote">
<p>本关验证码碎片：jTv</p>
</div>
<p>&nbsp;</p>
<h2 class="std_h2">第十三关</h2>
<p>地址：<a href="http://www.csksoft.net/netcompet2/gamearea/package.htm">http://www.csksoft.net/netcompet2/gamearea/package.htm</a></p>
<p>这一关实际上要求以下面的HTTP header来请求页面：<a href="http://www.csksoft.net/netcompet2/gamearea/package.asp">http://www.csksoft.net/netcompet2/gamearea/package.asp</a></p>
<font color="#0000ff">
<div class="quote"><font color="#0000ff">UserAgent: Mozilla/2.0 (compatible; MSIE 0.03; Linux) <br />
ReferenceURL: http://www.sjtu.edu.cn/index.htm</font></div>
</font>
<p>请求的页面会检查这2个字段，如果不满足一些必要的信息，就会拒绝显示内容。解决办法可以利用诸如FlashGet等下载工具中定义这2个字段的数据来访问网页实现。或者可以自己编写http客户端程序。</p>
<p>如果能成功获取请求页面的数据，下一关地址便可得到。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h2 class="std_h2">第十四关(最终关)</h2>
<p>地址：<a href="http://www.csksoft.net/netcompet2/gamearea/ffeewwdd.htm">http://www.csksoft.net/netcompet2/gamearea/ffeewwdd.htm</a></p>
<p>这一关主要涉及程序的调试和逆向工程技巧，主要任务是破解用于合成最终验证码的程序。</p>
<p>该程序需要输入用户名和序列号后才能使用，但是对于闯关者来说不可能知道自己的序列号是什么，因此需要通过相关的技巧，要么获取序列号，要么强行绕过程序的检验。</p>
<p>这里分别介绍这2种办法。</p>
<p>1.获取正确的序列号</p>
<p>当然有足够精力的话可以逆向工程来分析序列号算法，自己写注册机，但是对于本程序，只需要用最简单的内存搜索即可。</p>
<p>先随便输入用户名和序列号，比如：</p>
<p align="center"><img alt="" src="http://www.csksoft.net/data/netcompetII_solution/final_get_serial_1.1.jpg" /></p>
<p>然后在该程序进程内存中搜索随便输入的序列号，这里是cskcskcsk。采用的工具可以使Ollydbg或者WinHex，下面是WinHex的搜索结果：</p>
<p align="center"><img alt="" src="http://www.csksoft.net/data/netcompetII_solution/final_get_serial_1.jpg" /></p>
<p>会发现在cskcskcsk字符串下方的就是正确的注册码，因为本程序采用的是十分危险的明文校验，所以可以用这样简单的办法获取</p>
<p>此办法操作简单，有效，因此是推荐的做法。不过也可以直接通过修改跳转指令破解注册码验证来实现对程序功能的应用。</p>
<p>这里给出一种破解办法，具体思路省略。</p>
<p>使用Ollydbg分析程序，找到如下部分：</p>
<font face="Courier New"><font size="1"><font color="#ff0000">00402CDC&nbsp;&nbsp; .&nbsp; E8 F3980000&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp; 0040C5D4</font><br />
00402CE1&nbsp;&nbsp; .&nbsp; 83C4 08&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; add&nbsp;&nbsp;&nbsp;&nbsp; esp, 8<br />
00402CE4&nbsp;&nbsp; .&nbsp; 85C0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test&nbsp;&nbsp;&nbsp; eax, eax<br />
00402CE6&nbsp;&nbsp; .&nbsp; 8B4424 08&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; eax, dword ptr [esp+8]<br />
00402CEA&nbsp;&nbsp; .&nbsp; 0F95C3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setne&nbsp;&nbsp; bl<br />
00402CED&nbsp;&nbsp; .&nbsp; 83C0 F0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; add&nbsp;&nbsp;&nbsp;&nbsp; eax, -10<br />
00402CF0&nbsp;&nbsp; .&nbsp; 8D48 0C&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lea&nbsp;&nbsp;&nbsp;&nbsp; ecx, dword ptr [eax+C]<br />
00402CF3&nbsp;&nbsp; .&nbsp; 83CA FF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; or&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; edx, FFFFFFFF<br />
00402CF6&nbsp;&nbsp; .&nbsp; F0:0FC111&nbsp;&nbsp;&nbsp;&nbsp; lock xadd dword ptr [ecx], edx<br />
00402CFA&nbsp;&nbsp; .&nbsp; 4A&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dec&nbsp;&nbsp;&nbsp;&nbsp; edx<br />
00402CFB&nbsp;&nbsp; .&nbsp; 85D2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test&nbsp;&nbsp;&nbsp; edx, edx<br />
00402CFD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7F 08&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jg&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; short 00402D07<br />
00402CFF&nbsp;&nbsp; .&nbsp; 8B08&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; ecx, dword ptr [eax]<br />
00402D01&nbsp;&nbsp; .&nbsp; 8B11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; edx, dword ptr [ecx]<br />
00402D03&nbsp;&nbsp; .&nbsp; 50&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; eax<br />
00402D04&nbsp;&nbsp; .&nbsp; FF52 04&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp; dword ptr [edx+4]<br />
00402D07&nbsp;&nbsp; &gt;&nbsp; 84DB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test&nbsp;&nbsp;&nbsp; bl, bl<br />
00402D09&nbsp;&nbsp; .&nbsp; 5B&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pop&nbsp;&nbsp;&nbsp;&nbsp; ebx<br />
00402D0A&nbsp;&nbsp; .&nbsp; 6A 00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; 0<br />
<font color="#ff0000">00402D0C&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 74 22&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; je&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; short 00402D30</font><br />
00402D0E&nbsp;&nbsp; .&nbsp; 6A 00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; 0<br />
00402D10&nbsp;&nbsp; .&nbsp; 68 547C4200&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; 00427C54</font></font>
<p>&nbsp;</p>
<p>上述部分为验证注册码是否正确的片段，在执行call指令前，就能发现堆栈顶部就是正确的注册码，因此可以通过对此处的拦截实现注册机。也可以修改第二处红色部分，即je跳转，将它修改为强制跳转jmp。就实现了绕过注册码检测的功能。</p>
<p>这里给出采用上述破解办法修改的程序：<a href="http://www.csksoft.net/data/netcompetII_solution/SerialChk_cracker.rar">http://www.csksoft.net/data/netcompetII_solution/SerialChk_cracker.rar</a></p>
<p>&nbsp;</p>
<p>最终，就出现了验证码合成的界面：</p>
<p align="center"><img alt="" src="http://www.csksoft.net/data/netcompetII_solution/final_get_serial_1.2.jpg" /></p>
<p>填入上面收集到的验证码碎片后，就能获取最终验证码。最后只需通过系统验证便宣告过关：</p>
<p align="center"><img alt="" src="http://www.csksoft.net/data/netcompetII_solution/finally.jpg" /></p>
<p>&nbsp;</p>
<h1 class="std_h1">后记</h1>
<p>终于写完了这篇&ldquo;攻略&rdquo;。说实话，在3年后我自己重新玩这个游戏也被困住了。很多东西我自己也生疏了，难道这几年来我在退步？不管如何，写完这篇文章代表这个游戏已经告一段落。看着那么长的攻略，觉得那些通关的朋友真的很不容易。毕竟要揣摩别人的心思比技术本身要难得多。</p>
<p>对于很多人而言，这个游戏还是有难度的。希望通过这篇文章，不但是介绍过关的思路，我更是希望能让更多人了解这些领域的相关知识和趣味。</p>
<p>最后说明的事如果对文章中有不理解的部分，请自行网上查找，本人不负责解答。如果有任何错误，希望能告诉我，我会立刻修正。</p>]]></description><category>funs</category><comments>http://www.csksoft.net/blog/post/Netcompet2_solutions.html#comment</comments><wfw:comment>http://www.csksoft.net/blog/</wfw:comment><wfw:commentRss>http://www.csksoft.net/blog/sydication.asp?cmt=229</wfw:commentRss><trackback:ping>http://www.csksoft.net/blog/cmd.asp?act=tb&amp;id=229&amp;key=35cf2191</trackback:ping></item><item><title>准备上路了</title><author>csk@citiz.net (csk)</author><link>http://www.csksoft.net/blog/post/228.html</link><pubDate>Fri, 15 Feb 2008 00:37:45 +0800</pubDate><guid>http://www.csksoft.net/blog/post/228.html</guid><description><![CDATA[2.17 不但是学校最后一个学期的开学日期，也是我前往北京实习的日期。

说来惭愧，和很多同龄人相比，我到现在还没有一个人独自在异地长久居住过。所以这次MSRA实习的经历也将是我自理能力的考验吧。

之前在网上看了些其他人在msra实习的经历，基本感觉还是蛮理想的。希望接下来的3个月都能顺顺利利的，同时我也要尽力工作，不能让mentor失望。

这段时间一直着手准备实习的物品，所以也无心作其他的事。感觉我已经很久没有过“两点一线”专注的生活了。虽然将来的3个月要和亲人朋友离别一段时间，不过同时应该能有一段“心无杂念”的日子了吧。一直以来觉得自己自从高中以来已经逐渐迷失自我了，最近发生的一些事情甚至慢慢的在摧残着信心。希望3个月的埋头研究工作能让我恢复回来。毕竟懒散的生活不属于我。



今天距离2008年的2.14已经过去了近40分钟，我想那一天我会永远记住，不但有好的回忆，同时也是一位亲人永远离开我的一天。
]]></description><category>随感</category><comments>http://www.csksoft.net/blog/post/228.html#comment</comments><wfw:comment>http://www.csksoft.net/blog/</wfw:comment><wfw:commentRss>http://www.csksoft.net/blog/sydication.asp?cmt=228</wfw:commentRss><trackback:ping>http://www.csksoft.net/blog/cmd.asp?act=tb&amp;id=228&amp;key=fe9b6af3</trackback:ping></item></channel></rss>
