﻿<?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++博客-Figo的狗窝-文章分类-汇编</title><link>http://www.cppblog.com/figoyao/category/12682.html</link><description>我也很想让世界变得更好，可是上帝却不开放源代码。</description><language>zh-cn</language><lastBuildDate>Sun, 03 Jan 2010 11:06:12 GMT</lastBuildDate><pubDate>Sun, 03 Jan 2010 11:06:12 GMT</pubDate><ttl>60</ttl><item><title>游戏辅助工具开发教程-从入门到精通之A2_7篇</title><link>http://www.cppblog.com/figoyao/articles/104700.html</link><dc:creator>Figo</dc:creator><author>Figo</author><pubDate>Sun, 03 Jan 2010 07:37:00 GMT</pubDate><guid>http://www.cppblog.com/figoyao/articles/104700.html</guid><wfw:comment>http://www.cppblog.com/figoyao/comments/104700.html</wfw:comment><comments>http://www.cppblog.com/figoyao/articles/104700.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/figoyao/comments/commentRss/104700.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/figoyao/services/trackbacks/104700.html</trackback:ping><description><![CDATA[作者：<a  href="http://www.figoyao.com/" target="_blank">Figo</a>
<p>本节我们以<strong><a  href="http://www.duotegame.com/soft/4862.html" target="_blank">红警2[共和国之辉]</a></strong>为上手的第一个游戏，首先是作为<a  href="http://www.figoyao.com/blog/2009/12/06/135/" target="_blank">复习</a>，其次是引入新内容：马上我将带领大家学习Win32编程。时间过得真快，希望你这段时间也有一定的进步。</p>
<p>首先给出<a  href="http://figoyao.com/blog/file/RACheatGame.rar" target="_blank">源码和Bin文件</a>，这次有<strong>控制台</strong>和<strong>Win32</strong>两个版本，阅读源码时候最好结合<a  href="http://msdn.microsoft.com/" target="_blank">MSDN</a>。<br>
Win32版本的截图：<br>
<a  href="http://www.figoyao.com/blog/2009/12/10/190/null"><img  src="http://farm5.static.flickr.com/4007/4173385261_1cf7e078d0_o.jpg" class="alignnone" title="Windowsra1" alt="" height="160" width="278"></a><br>
<a  href="http://www.figoyao.com/blog/2009/12/10/190/null"><img  src="http://farm3.static.flickr.com/2664/4173385265_0f58aea650_o.jpg" class="alignnone" title="windustry" alt="" height="157" width="458"></a><br>
控制台版本的截图：<br>
<a  href="http://www.figoyao.com/blog/2009/12/10/190/null"><img  src="http://farm3.static.flickr.com/2531/4173385267_4bd637d41b_o.jpg" class="alignnone" title="ConsoleRA" alt="" height="154" width="683"></a><br>
一个小Tip：红警窗口模式，红警安装完后有一个叫做Ra2.exe的文件，把它的快捷键发送到桌面，然后右键-&gt;属性，在路径名最后输入<span style="color: #ff0000;">-win</span>启动参数：<br>
<a  href="http://www.figoyao.com/blog/2009/12/10/190/null"><img  src="http://farm3.static.flickr.com/2680/4173486179_17c063e963_o.jpg" class="alignnone" title="winramodel" alt="" height="209" width="369"></a><br>
所有源码是在VC6上编写并编译的。下载完文件的读者很快会发现控制台的程序比Win32还大，这两个执行文件我没有做任何处理。<a  href="http://www.figoyao.com/blog/2009/12/06/135/" target="_blank">前面的章节</a>我们分析过一个文字性质的游戏Demo，这里涉及的大部分原理性知识是一样的。重复的知识我就不深入讲解了。<br>
在使用CheatEngine定位需要修改的地址后，我们就开始动手了。这次不必像上次那样还得自己输入游戏进程的PID，我们交给程序来做。</p>
<p>源码还是比较简单的，有疑问的话看看前面的章节的那次讲解。如果还不明白，可以留言。下次开始我就要进入Windows应用程序设计的讲解了。</p>
<p>转载请注明出处：<span id="sample-permalink"><a  href="http://www.figoyao.com/blog/2009/12/10/190" target="_blank">http://www.figoyao.com/blog/2009/12/10/190</a></span></p><img src ="http://www.cppblog.com/figoyao/aggbug/104700.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/figoyao/" target="_blank">Figo</a> 2010-01-03 15:37 <a href="http://www.cppblog.com/figoyao/articles/104700.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏辅助工具开发教程-从入门到精通之A2_6篇</title><link>http://www.cppblog.com/figoyao/articles/104699.html</link><dc:creator>Figo</dc:creator><author>Figo</author><pubDate>Sun, 03 Jan 2010 07:36:00 GMT</pubDate><guid>http://www.cppblog.com/figoyao/articles/104699.html</guid><wfw:comment>http://www.cppblog.com/figoyao/comments/104699.html</wfw:comment><comments>http://www.cppblog.com/figoyao/articles/104699.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/figoyao/comments/commentRss/104699.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/figoyao/services/trackbacks/104699.html</trackback:ping><description><![CDATA[作者：<a  href="http://www.figoyao.com/" target="_blank">Figo</a>
<p>本节承接<a  href="http://www.figoyao.com/blog/2009/12/09/178/" target="_blank">上节</a>继续讲汇编中的指令。</p>
<p>一、标志位操作指令<br>
<a  href="http://farm3.static.flickr.com/2670/4170522907_cc5478b718_o.jpg"><img  src="http://farm3.static.flickr.com/2670/4170522907_cc5478b718_o.jpg" class="alignnone" title="flags" alt="" height="706" width="834"></a><br>
1、<strong>进位CF标志操作指令</strong><br>
清零标志位CLC：CF&lt;-0<br>
置位标志位STC：CF&lt;-1<br>
标志位取反CMC：CF&lt;-NOT CF<br>
2、<strong>方向位DF操作指令</strong><br>
清零方向位CLD：DF&lt;-0<br>
置位方向位STD：DF&lt;-1<br>
3、<strong>中断允许位IF操作指令</strong><br>
清零CLI：IF&lt;-0<br>
置位STI：IF&lt;-1<br>
4、<strong>取标志位操作指令</strong><br>
LAHF：AH&lt;-EFlags低8位<br>
SAHF：EFlags低八位&lt;-AH<br>
5、标志位堆栈操作指令<br>
PUSHF/PUSHFD：16bit/32bit标志位进栈<br>
POPF/POPFD：16bit/32bit标志位出栈</p>
<p>二、算术运算指令<br>
1、<strong>加法指令</strong><br>
1）、<strong>普通加法指令ADD</strong><br>
ADD&nbsp; reg/mem,reg/mem/imm<br>
受影响标志位：AF/CF/OF/PF/SF/ZF<br>
2）、<strong>带进位的加法ADC<br>
</strong>ADC&nbsp; reg/mem,reg/mem/imm<br>
受影响标志位：AF/CF/OF/PF/SF/ZF<br>
该指令和1）中唯一不同的是除了执行1）中加法外还要加上CF。<br>
3）、<strong>加1指令INC<br>
</strong>INC&nbsp; reg/mem<br>
受影响标志位：AF/OF/PF/SF/ZF<br>
4）、<strong>交换加法指令XADD</strong><br>
XADD&nbsp; reg/mem,reg<br>
该指令首先交换两个操作数的值，然再做加法，相当于以下两条指令：<br>
XCHG&nbsp; reg/mem,reg<br>
ADD&nbsp; reg.mem,reg<br>
2、<strong>减法指令</strong>[和加法指令相反]<br>
1）、普通减法SUB<br>
2）、带借位的减法SBB<br>
除了1）中减法还有减去CF<br>
3）、减1指令DEC<br>
4）、求补指令NEG<br>
NEG&nbsp; reg/mem<br>
受影响标志位：AF/CF/OF/PF/SF/ZF<br>
执行结果：操作数=0-操作数<br>
3、<strong>乘法指令</strong><br>
分为带符号乘法和无符号乘法，区别在于：数据的最高位是作为<strong>符号</strong>还是<strong>数值</strong>参与运算。<br>
1）、无符号乘法MUL<br>
MUL&nbsp; reg/mem<br>
受影响标志位：CF/OF<br>
我们知道乘法要有乘数和被乘数，指令中只有一个操作数，所以我们必须知道第二个操作数在哪里。Intel处理的方式是，被乘数提前存在EAX中，然后与给出的乘数相乘，结果放在规定的寄存器中：</p>
<table bgcolor="#ffffff" border="1" bordercolor="#000000">
    <tbody>
        <tr>
            <td><strong>乘数位数</strong></td>
            <td><strong>隐含的被乘数</strong></td>
            <td><strong>乘积存放位置[高-低]</strong></td>
            <td><strong>举例</strong></td>
        </tr>
        <tr>
            <td>8</td>
            <td>AL</td>
            <td>AX</td>
            <td>MUL&nbsp; CL</td>
        </tr>
        <tr>
            <td>16</td>
            <td>AX</td>
            <td>DX-AX</td>
            <td>NUL&nbsp; CX</td>
        </tr>
        <tr>
            <td>32</td>
            <td>EAX</td>
            <td>EDX-EAX</td>
            <td>MUL&nbsp; ECX</td>
        </tr>
    </tbody>
</table>
<p>2)、有符号乘法IMUL<br>
IMUL&nbsp; reg/mem<br>
受影响标志位：CF/OF<br>
其操作数都作为有符号数相乘。<br>
4、<strong>除法指令<br>
</strong>1）无符号除法<br>
DIV&nbsp; reg/mem<br>
不影响标志位<br>
被除数默认存放规则如下：</p>
<table bgcolor="#ffffff" border="1" bordercolor="#000000">
    <tbody>
        <tr>
            <td>除数位数</td>
            <td>隐含的被除数</td>
            <td>商</td>
            <td>余数</td>
        </tr>
        <tr>
            <td>8</td>
            <td>AX</td>
            <td>AL</td>
            <td>AH</td>
        </tr>
        <tr>
            <td>16</td>
            <td>DX-AX</td>
            <td>AX</td>
            <td>DX</td>
        </tr>
        <tr>
            <td>32</td>
            <td>EDX-EAX</td>
            <td>EAX</td>
            <td>EDX</td>
        </tr>
    </tbody>
</table>
<p>2）有符号除法<br>
IDIV&nbsp; reg/mem<br>
受影响标志位：AF/CF/OF/PF/SF/ZF</p>
<p>三、逻辑运算指令<br>
1、<strong>逻辑与AND指令<br>
</strong>AND&nbsp;&nbsp;reg/mem,reg/mem/imm<br>
受影响的标志位：CF(0)/OF(0)/SF/PF/ZF<br>
执行过程：源操作数和目的操作数进行逻辑与运算，结果存放在目的操作数。<br>
<strong>2、逻辑或OR指令</strong><br>
OR&nbsp;&nbsp;reg/mem,reg/mem/imm<br>
受影响的标志位：CF(0)/OF(0)/SF/PF/ZF<br>
执行过程：源操作数和目的操作数进行逻辑或运算，结果存放在目的操作数。<br>
<strong>3、逻辑非NOT指令</strong><br>
NOT&nbsp;&nbsp;reg/mem<br>
受影响的标志位：无<br>
执行过程：把操作数按位取反。<br>
<strong>4、逻辑异与XOR指令</strong><br>
XOR&nbsp;&nbsp;reg/mem,reg/mem/imm<br>
受影响的标志位：CF(0)/OF(0)/SF/PF/ZF<br>
执行过程：源操作数和目的操作数进行逻辑异与运算，结果存放在目的操作数。<br>
异或就是两数相同则结果为0，反之为1。<br>
<strong>5、测试指令TEST</strong><br>
TEST&nbsp;&nbsp;reg/mem,reg/mem/imm<br>
受影响的标志位：SF/PF/ZF<br>
test类似and指令，但只是逻辑与进行测试，根据结果修改标志位，却不改变操作数的值。</p>
<p>四、跳转指令<br>
1、<strong>无条件跳转指令<br>
</strong>JUMP&nbsp; reg/mem/imm<br>
执行结果是跳转到指定地址，然后继续执行代码。按照<a  href="http://www.intel.com/Assets/PDF/manual/253666.pdf" target="_blank">Intel指令手册</a>上的说法，jump一共有4中不同的跳法，这比起跳大神的神棍的跳法可少多了：<br>
1）、Near Jump<br>
这种跳法为啥叫near呢，因为它跳来跳去都是在一个代码段跳，所以又称作段内转移。这就好比你遛狗，总是不出你的县城，这就叫<strong>near遛</strong>：）<br>
2）、Short Jump<br>
Short Jump是Near Jump的一种，但是比Near还有Near，它跳转的范围是[-128,127]。这时候你可能只是在你的村子溜溜狗而已，太Short了。<br>
3）、Far Jump<br>
一看Far就是那么有气势，此时跳转的范围已经扩大到其他段，你连其他段都能跳，本段当然更可以了，但是要注意，此时你可以跳转到的目标代码的特权级别必
须和当前代码相同：如果你是用户权限，你跳转到系统权限的代码就会出错。这时候你已经厌倦在小县城遛狗了，终于，你鼓起勇气，决定去丈母娘的县城遛狗
╮(╯_╰)╭。<br>
4）、Task Switch<br>
这个跳转一下就从你当前任务的代码到别的任务里面执行代码了，可以说是跳的最远的。<br>
2、<strong>条件跳转指令<br>
</strong>顾名思义，得依据条件来跳转，在这里，条件指的是标志寄存器的一个或多个标志位，如果满足条件就跳转否则继续执行跳转指令下面的代码。在介绍指令的时候，首先说下助记符中字母的含义：<br>
<strong><span style="color: #ff0000;">E</span></strong>：Equal[相等]<br>
<span style="color: #ff0000;"><strong>A</strong></span>：Above[大于]<br>
<span style="color: #ff0000;"><strong>B</strong></span>：Below[小于]<br>
<span style="color: #ff0000;"><strong>G</strong></span>：Greater Than[大于]<br>
<span style="color: #ff0000;"><strong>L</strong></span>：Less Than[小于]<br>
<strong><span style="color: #ff0000;">N</span></strong>:Not[不]<br>
条件跳转大体上分为2类：<br>
<strong>直接标志位测试</strong></p>
<table bgcolor="#ffffff" border="1" bordercolor="#000000">
    <tbody>
        <tr>
            <td><strong>助记符</strong></td>
            <td><strong>测试条件</strong></td>
            <td><strong>跳转条件</strong></td>
        </tr>
        <tr>
            <td>JC</td>
            <td>CF=1</td>
            <td>有进位</td>
        </tr>
        <tr>
            <td>JNC</td>
            <td>CF=0</td>
            <td>无进位</td>
        </tr>
        <tr>
            <td>JZ/JE</td>
            <td>ZF=1</td>
            <td>为0或相等</td>
        </tr>
        <tr>
            <td>JNZ/JNE</td>
            <td>ZF=0</td>
            <td>不为0或不等</td>
        </tr>
        <tr>
            <td>JS</td>
            <td>SF=1</td>
            <td>负数</td>
        </tr>
        <tr>
            <td>JNS</td>
            <td>SF=0</td>
            <td>正数</td>
        </tr>
        <tr>
            <td>JO</td>
            <td>OF=1</td>
            <td>溢出</td>
        </tr>
        <tr>
            <td>JNO</td>
            <td>OF=0</td>
            <td>无溢出</td>
        </tr>
        <tr>
            <td>JP/JPE</td>
            <td>PF=1</td>
            <td>偶数</td>
        </tr>
        <tr>
            <td>JNP/JPO</td>
            <td>PF=0</td>
            <td>奇数</td>
        </tr>
    </tbody>
</table>
<p><strong>间接标志位测试</strong></p>
<table bgcolor="#ffffff" border="1">
    <tbody>
        <tr>
            <td><strong>类别</strong></td>
            <td><strong>助记符</strong></td>
            <td><strong>测试条件</strong></td>
            <td><strong>跳转条件</strong></td>
        </tr>
        <tr>
            <td>无符号数</td>
            <td>
            <table bgcolor="#ffffff" border="1">
                <tbody>
                    <tr>
                        <td>JA/JNBE</td>
                    </tr>
                    <tr>
                        <td>JAE/JNB</td>
                    </tr>
                    <tr>
                        <td>JB/JNAE</td>
                    </tr>
                    <tr>
                        <td>JBE/JNA</td>
                    </tr>
                </tbody>
            </table>
            </td>
            <td>
            <table bgcolor="#ffffff" border="1">
                <tbody>
                    <tr>
                        <td>CF=0且ZF=0</td>
                    </tr>
                    <tr>
                        <td>CF=0</td>
                    </tr>
                    <tr>
                        <td>CF=1</td>
                    </tr>
                    <tr>
                        <td>CF=1或ZF=1</td>
                    </tr>
                </tbody>
            </table>
            </td>
            <td>
            <table bgcolor="#ffffff" border="1">
                <tbody>
                    <tr>
                        <td>大于/大于等于</td>
                    </tr>
                    <tr>
                        <td>大于等于</td>
                    </tr>
                    <tr>
                        <td>小于/小于等于</td>
                    </tr>
                    <tr>
                        <td>小于等于</td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
        <tr>
            <td>有符号数</td>
            <td>
            <table bgcolor="#ffffff" border="1">
                <tbody>
                    <tr>
                        <td>JG/JNLE</td>
                    </tr>
                    <tr>
                        <td>JGE/JNL</td>
                    </tr>
                    <tr>
                        <td>JL/JNGE</td>
                    </tr>
                    <tr>
                        <td>JLE/JNG</td>
                    </tr>
                </tbody>
            </table>
            </td>
            <td>
            <table bgcolor="#ffffff" border="1">
                <tbody>
                    <tr>
                        <td>ZF=0且SF=OF</td>
                    </tr>
                    <tr>
                        <td>SF=OF</td>
                    </tr>
                    <tr>
                        <td>SF!=OF</td>
                    </tr>
                    <tr>
                        <td>SF!=OF或ZF=1</td>
                    </tr>
                </tbody>
            </table>
            </td>
            <td>
            <table bgcolor="#ffffff" border="1">
                <tbody>
                    <tr>
                        <td>大于/大于等于</td>
                    </tr>
                    <tr>
                        <td>大于等于</td>
                    </tr>
                    <tr>
                        <td>小于/小于等于</td>
                    </tr>
                    <tr>
                        <td>小于等于</td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<p>以上是根据<a  href="http://www.intel.com/Assets/PDF/manual/253666.pdf" target="_blank">Intel指令手册</a>（<em>3-542 vol.2A</em>）整理，有些教材的跳转条件是错误的。</p>
<p>五、<strong>其他指令<br>
</strong>本教程不可能把所有指令都介绍完整，有些指令需要在实践的过程中自己查阅手册，学习一门技能从来都不是一蹴而就的。<br>
1、nop指令<br>
nop是空指令，就是占位，除此没有任何作用。<br>
2、call指令<br>
call&nbsp; reg/mem/imm<br>
call指令执行的过程是，首先把当前EIP或CS加EIP压入堆栈，然后跳转到call后操作数指定地址执行代码。手册里介绍了四种跳转，这里只讲下
Near Call和Far
Call。前者call呼叫的函数在同一代码段，此时只需把EIP压栈即可；而后者call呼叫的是不同段，需要把当前段的选择子和EIP均压入栈。<br>
3、ret/retn/retf<br>
ret&nbsp; reg/mem/imm[可选]<br>
retn是近返回；retf是远返回。</p>
<p>至此汇编语言的基本内容就介绍完了。</p>
<p>转载请注明出处：<span id="sample-permalink"><a  href="http://www.figoyao.com/blog/2009/12/09/182" target="_blank">http://www.figoyao.com/blog/2009/12/09/182</a></span></p><img src ="http://www.cppblog.com/figoyao/aggbug/104699.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/figoyao/" target="_blank">Figo</a> 2010-01-03 15:36 <a href="http://www.cppblog.com/figoyao/articles/104699.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏辅助工具开发教程-从入门到精通之A2_5篇</title><link>http://www.cppblog.com/figoyao/articles/104698.html</link><dc:creator>Figo</dc:creator><author>Figo</author><pubDate>Sun, 03 Jan 2010 07:35:00 GMT</pubDate><guid>http://www.cppblog.com/figoyao/articles/104698.html</guid><wfw:comment>http://www.cppblog.com/figoyao/comments/104698.html</wfw:comment><comments>http://www.cppblog.com/figoyao/articles/104698.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/figoyao/comments/commentRss/104698.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/figoyao/services/trackbacks/104698.html</trackback:ping><description><![CDATA[作者：<a  href="http://www.figoyao.com/" target="_blank">Figo</a>
<p>本节主要讲解汇编中的指令。</p>
<p>下载一个<a  href="http://www.52z.com/soft/19908.html" target="_blank">指令查看器</a>，用于初期查询之用。在讲指令之前，我先简单说下汇编中有关变量定义的相关内容。</p>
<p>一、标识符和表达式<br>
汇编中，标号、内存变量、子程序名和宏定义名等都叫做标识符，最多由31个字母、数字和特殊字符（?、@、_、$）等组成，不能以数字开头。汇编中是不区分大小写的。并且标识符不能和编译器保留关键字重名，如：<br>
<strong>正确的</strong>：msg、_Student、?iRand<br>
<strong>错误的</strong>：2boys、mov、eax<br>
这里需要注意的是，ABCH和0ABCH，前者是标识符，后者是16进制数。在实际编程中一般要求十六进制数以0或0x开头。<br>
1、内存变量定义<br>
一般我们常用的有字节(byte)、字(word)和双字(doubleword)，浮点数在后面会涉及。定义变量的一般形式是：<br>
<strong>变量名</strong> <strong>类型</strong> 表达式1,表达式2,&#8230;&#8230;表达式n<br>
表达式个数取决于实际定义；变量名命名规则上面已经介绍；类型常用的有<strong>db</strong>(byte)、<strong>dw</strong>(word)、<strong>dd</strong>(doubleword)。各表达式之间用逗号隔开，最后一个表达式没有逗号；如果变量没有在定义时初始，表达式用问号（<strong>?</strong>）代替。例子如下：<br>
1）、定义一个名为iPlayerHP的单字变量，初始值为200<br>
iPlayer&nbsp;&nbsp;<strong>dw </strong> 200<br>
2）、定义一个名为msgname的ASCII字符串变量，初始值为&#8217;I am Figo Yao.&#8217;<br>
msgname&nbsp; <strong>db</strong> &#8216;I am Figo Yao.&#8217;<br>
3）、定义一个名为iTime的双字变量，初始值不确定<br>
iTime&nbsp; <strong>dd</strong> ?<br>
4）、定义一个长度为100的，名为qqNUM的双字数组变量，初始值不确定<br>
qqNum&nbsp; <strong>dd</strong> 100&nbsp; <strong>dup</strong>(?)<br>
dup是关键字，用在重复定义的情形。<br>
其一般用法如下：<br>
次数&nbsp; dup&nbsp; (表达式1,表达式2,&#8230;&#8230;表达式n)<br>
括号中是被重复的部分，如<br>
qqNum&nbsp; <strong>dd</strong> 100&nbsp; <strong>dup</strong>(10000,10010)<br>
这个表达式的结果是：qqNum数组长度是200，数值是10000和10010交替重复。即<br>
qqNum[200] = {10000,10010,10000,10010&#8230;10000,10010,10000,10010};<br>
5）、结构类型定义<br>
其语法为：<br>
结构变量名&nbsp; STRUC<br>
数据定义<br>
结构变量名&nbsp; ENDS<br>
比如我们先来定义一个时间结构：<br>
stTime&nbsp; <span style="color: #008000;">struc</span><br>
iHour&nbsp; db&nbsp; 0&nbsp;&nbsp;&nbsp; <span style="color: #008000;">//偏移量是0<br>
</span> iMin&nbsp;&nbsp;&nbsp;&nbsp; db&nbsp; 0&nbsp;&nbsp;&nbsp; <span style="color: #008000;">//偏移量是1<br>
</span> iSec&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; db&nbsp; 0&nbsp;&nbsp;&nbsp; <span style="color: #008000;">//偏移量是2<br>
</span>stTime&nbsp; <span style="color: #008000;">struc<br>
<span style="color: #000000;">对于没有名字的结构体，也可以用偏移量来访问。比如我们初始化一个stTime的结构体(假设eax指向分配的地址)和一个保存秒的变量：<br>
TestTime &nbsp;&nbsp; <strong>stTime</strong> &lt;10,25,0&gt;<br>
</span></span>TestSec&nbsp;&nbsp;&nbsp; <strong>db</strong> ?<br>
TestSec&nbsp;&nbsp;&nbsp; <strong>equ</strong> TestTime.iSec<br>
我们看到取结构体分量使用的是点(.)加变量名。<span style="color: #ff0000;">equ</span>是等于的意思，属于关键字。和上面最后一句等价的语句是：<br>
TestHour&nbsp;&nbsp;&nbsp; <strong>equ</strong> byte <strong>ptr</strong> [eax+2]<br>
ptr是指明数据长度，属于关键字，一般用法为：<br>
数据类型&nbsp;&nbsp;&nbsp; ptr&nbsp;&nbsp;&nbsp; 地址<br>
操作的结果是指明操作数的地址和类型。因为我们第二种获取结构体变量的方法使用了偏移量，得到存放数据的地址后，还需要告诉CPU从这个地址读取什么类型的数据，因为我们的<strong>秒变量</strong>是字节，所以ptr前面的是byte。</p>
<p>二、汇编指令<br>
首先规定一些代号以便于讲解：<br>
reg：寄存器<br>
mem：内存单元<br>
imm：立即数<br>
这些代号后面如果跟数字，则表示是多少bit，如：reg16表示16bit的寄存器；立即数的意思是直接的具体的数值，如0&#215;1234H。<br>
下面的内容大体和教材分类一样：<br>
1、数据传输指令<br>
1）、<strong>mov</strong>指令（move）<br>
mov指令的格式如下：<br>
mov&nbsp; op1,op2<br>
[mov&nbsp;&nbsp; reg/mem,reg/mem/imm]<br>
执行的结果是op2的值被传送（复制）到op1。<br>
如：<br>
mov&nbsp; eax,0&#215;1234h<br>
mov&nbsp; dword ptr [40995b],ecx<br>
mov&nbsp; es,dx<br>
<a  href="http://www.figoyao.com/blog/2009/12/09/178/null"><img  src="http://farm3.static.flickr.com/2593/4170248411_af8a8c5838_o.jpg" class="alignnone" title="direction" alt="" height="244" width="383"></a><br>
但是mov有几条规定需要注意：<br>
<strong>a</strong>、两个操作数类型必须相同，即同为db、dw或dd等类型。<br>
<strong>b</strong>、mov指令不能用于改变cs寄存器，否则会导致程序异常错误。如mov&nbsp; cs,ax 是禁止的;但mov&nbsp; ax,cs 是允许的。<br>
<strong>c</strong>、两个操作数不能同为下列段寄存器的组合：DS、ED、SS、FS、GS，如：mov&nbsp; es,ds 是错误的用法，如果一定要把ds的数据传给es，可以如下操作：<br>
mov&nbsp; ax,ds<br>
mov&nbsp; es,ax<br>
<strong>d</strong>、两个操作数不能同为内存单元，如mov&nbsp; [1234h],[2345h] 是错误的用法。<br>
<strong>e</strong>、指令指针EIP不能作为mov的操作数。<br>
2）、<strong>movsx</strong>/<strong>movzx</strong>（传送填充指令）<br>
2a）、movsx是符号填充指令（move with sign-extend）<br>
movsx&nbsp; op1,op2<br>
一般op2的bit数比op1少，在mov中是禁止这么传送数据的，但是movsx可以，执行结果是op1低位由op2填充，op1剩下的高位由op2的符号位填充。<br>
如：<br>
movsx&nbsp; eax,10H<br>
执行完后，eax的低五位为二进制10000，从第六个bit起的高位均填充1。<br>
2b）、movzx是零填充指令（move with zero-extend）<br>
与符号填充唯一的区别是使用0来填充高位。<br>
3）、交换指令<strong>xchg</strong>（Exchange）<br>
格式如下：<br>
xchg&nbsp; reg/mem,reg/mem<br>
xchg&nbsp; op1,op2<br>
执行结果是op1中存储原来op2的数据，op2中存储原来op1的数据。<br>
4）、取有效地址指令<strong>lea</strong>（load effective address）<br>
格式如下：<br>
lea&nbsp; reg,mem<br>
lea&nbsp; op1,op2<br>
执行结果是内存单元op2的有效地址（偏移地址）赋给op1。<br>
在汇编中还有一种获取有效地址的方法，使用offset<strong><span style="color: #ff0000;">伪指令</span></strong>，如：<br>
lea&nbsp; eax,dword ptr [1234H]和mov&nbsp; eax,offset&nbsp; dword ptr
[1234H]是等价的，但是后者的执行速度更快。不过当去堆栈处地址时候是不可以用offset的，因为offset在编译时实际上编译器会先求出变量
地址，然后用地址作为立即数进而合成机器码；而lea是CPU的指令，是在程序运行时候求的，所以可以处理动态地址，如：lea&nbsp; eax,dword
ptr [ebp-5ch] 的情况就不可以用offset。<br>
5）、堆栈操作指令<br>
进出栈操作<br>
push&nbsp; reg/mem/imm<br>
执行push指令后堆栈指针ESP依据进栈数据类型自动修改，如push&nbsp; eax，因为eax是4个字节，所以push执行完后，esp=esp-4，[esp]&lt;-eax。与之对应的是pop出栈指令。<br>
pusha/pushad<br>
执行该指令会依次把EAX、ECX、EDX、EBX、ESP、EBP、ESI、EDI存入堆栈，这些通用寄存器的值是执行指令前一瞬间的快照值，即要执行
这条指令的一瞬间，我们把此时的通用寄存器的值记录下来，那么这条指令就是把我们记录的值按照上面顺序依次存入堆栈。pusha是16bit的版
本，pushad是32bit的版本。与之对应的是popa和popad，这条指令是把刚才存储的快照依照和上面存储过程的逆序恢复。</p>
<p>这节就暂时讲到这里，下次我们接着讲指令。</p>
<p>转载请注明出处：<span id="sample-permalink"><a  href="http://www.figoyao.com/blog/2009/12/09/178/" target="_blank">http://www.figoyao.com/blog/2009/12/09/178/ </a></span></p><img src ="http://www.cppblog.com/figoyao/aggbug/104698.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/figoyao/" target="_blank">Figo</a> 2010-01-03 15:35 <a href="http://www.cppblog.com/figoyao/articles/104698.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏辅助工具开发教程-从入门到精通之A2_4篇</title><link>http://www.cppblog.com/figoyao/articles/104697.html</link><dc:creator>Figo</dc:creator><author>Figo</author><pubDate>Sun, 03 Jan 2010 07:34:00 GMT</pubDate><guid>http://www.cppblog.com/figoyao/articles/104697.html</guid><wfw:comment>http://www.cppblog.com/figoyao/comments/104697.html</wfw:comment><comments>http://www.cppblog.com/figoyao/articles/104697.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/figoyao/comments/commentRss/104697.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/figoyao/services/trackbacks/104697.html</trackback:ping><description><![CDATA[作者：<a  href="http://www.figoyao.com/" target="_blank">Figo</a>
<p>本节主要讲解汇编中的操作数寻址。</p>
<p>先来一张IDA反汇编的截图：<br>
<a  href="http://www.figoyao.com/blog/2009/12/08/159/null"><img  src="http://farm3.static.flickr.com/2729/4169306976_b50b9cde00_o.jpg" class="alignnone" title="idadecode" alt="" height="479" width="732"></a><br>
我们看到汇编代码的形式为：<br>
<strong>指令</strong> <span style="color: #008000;">操作数1</span>[可选],<span style="color: #008000;">操作数2</span>[可选]<br>
中括号（[]）内的可选，指的是操作数的个数取决于指令的类型：如果是单操作数指令，比如<strong><span style="color: #003366;">push</span></strong>指令，那么<span style="color: #008000;">操作数2（<span style="color: #ff0000;">下简称<strong>op2</strong></span>）</span>是没有的；如果是双操作数指令，比如<span style="color: #003366;"><strong>mov</strong></span>，两个操作数都有；如果是无操作数指令则后面什么也不跟。在进一步讲解之前，我先简单讲下<strong><span style="color: #003366;">mov</span></strong>指令，该指令的作用是把op2的值复制到op1。<strong>从&#8230;到&#8230;</strong>是一个具有方向的句式，我们以后把op1叫做<strong>目的操作数</strong>，op2则叫<strong>源操作数</strong>，因为是<strong>从</strong>op2<strong>到</strong>op1╮(╯_╰)╭。虽然下面是以mov指令作为讲解的对象，以下规则是可以拓展到所有指令的。</p>
<p>毛主席教导我们：有的放矢，抓住老鼠的猫就是好猫！哎，抓老鼠不是邓爷爷教导的吗？汗&#8230;下面我的讲解和大部分教科书的方式有很大不同，但是我可以保证你看完之后获得的信息量<strong>&gt;=</strong>教科书。相信我，没错的：）。</p>
<p>首先思考一下：指令存在的目的是什么？CPU是做什么的？我们一般称CPU为中央处理器，处理什么呢？当然是二进制数据了，我偷偷告诉你，其实CPU只会加减法而已。这样说来，<a  href="http://www.intel.com/" target="_blank">Intel</a>也不是什么高科技公司嘛[<a  href="http://www.loongson.cn/" target="_blank">龙芯</a>大笑]。我们小学就学过加减法，不就是两个数相加减吗？是的，所谓寻址，就是告诉CPU做加减法的数存在哪里，仅此而已。<a  href="http://www.figoyao.com/blog/2009/12/07/147/" target="_blank">前面</a>已经讲过，计算机里面能存储数据的地方有：<strong>硬盘</strong>等外部存储设备、<strong>内存</strong>、<strong>高速缓存</strong>和<strong>寄存器</strong>。我们可以无视硬盘和高速缓存：前者不直接和CPU打交道，后者暂时不直接和我们打交道。这样的话就我们关心的就只有内存和寄存器了，那我们要做的就是搞明白在汇编里面是如何表示内存地址的.为什么不管寄存器寻址呢？因为寄存器属于CPU内部固件，所以CPU可以<strong>直接</strong>访问。就好比，你去别人家拜访的话首先要知道别人的地址；在自己家里的话，想去哪个房间直接去喽，当然不要地址了！那么我们下面主要讲的就是内存地址的表示，上节教程说过，32位CPU表示地址的格式为：<br>
<a  href="http://www.figoyao.com/blog/2009/12/08/159/null"><img  src="http://farm3.static.flickr.com/2664/4169370494_0902358298_o.jpg" class="alignnone" title="addr32" alt="" height="97" width="461"></a><br>
第一个是段选择子，第二个是偏移地址[又叫有效地址]。至于得到这2个参数后如何寻址<a  href="http://www.figoyao.com/blog/2009/12/07/147/" target="_blank">上节已经说过了</a>。对于这2个参数在汇编语言中是如何给出的呢？比如我把op2复制到op1，按道理说，应该这样写：mov eax,ds:[1234H]。这句中的ds是数据段寄存器作为选择子，后面的[1234]表示偏移地址，记住<strong>一定要加中括号</strong>！不过，Intel的游戏规则是这么说的：你这么做，是可以的，但经过我们砖家的研究发现啊，大部分情况使用<strong>默认的段选择子</strong>更省事。说完这些，Inte在它的<a  href="http://www.intel.com/Assets/PDF/manual/253665.pdf" target="_blank">指令手册</a>（参见：Vol1 3-29页）中给出了默认的段选择子的规则（下面的英语比高考阅读简单多了）：<br>
<a  href="http://farm3.static.flickr.com/2667/4169414622_1c0386e72f_o.jpg"><img  src="http://farm3.static.flickr.com/2667/4169414622_1c0386e72f_o.jpg" class="alignnone" title="IntelAddrprefix" alt="" height="386" width="956"></a><br>
由上表我们看到：代码段默认的选择子是CS，堆栈段默认的选择子是SS，数据段默认的选择子是DS，字符串默认的选择子是ES。返回来看最上面那张反汇编图，我们看下地址为00401004处的汇编代码：<strong><span style="color: #003366;">mov&nbsp;&nbsp;</span>&nbsp;&nbsp; esi, [esp+<span style="color: #008000;">48h</span>+<span style="color: #008000;">hInstance</span>]。</strong>中括号里面的是偏移地址（hInstance和48H一样，都是数值，在运行时由Windows系统传递具体数值进行初始化），但是没有指明选择子，由于是要把<strong>堆栈</strong>中的数据复制到ESI中，结合上面的规定，我们知道选择子是SS。所以<strong>还原</strong>一下这个语句应该这么写：<strong><span style="color: #003366;">mov&nbsp;&nbsp;</span>&nbsp;&nbsp; esi, ss:[esp+<span style="color: #008000;">48h</span>+<span style="color: #008000;">hInstance</span>]</strong>。有同学发问了：&#8220;你怎么知道复制的数据在堆栈呢？&#8221;能提出这个说明这位同学是认真思考了，我来回答一下：凡是看到中括号第一个是ESP或EBP，那么选择子就是SS，别忘了ESP可是叫做堆栈指针寄存器，那可不是盖的！再来看下地址为00401036处的汇编代码：<strong><span style="color: #003366;">call ds</span></strong>:<span style="color: #d02ecd;"><strong>LoadIconA</strong></span>。这句是呼叫函数LoadIconA载入图标，函数一般存放在代码段，但是这个函数存放的地址却是数据段，因为Intel规定的代码段默认的选择子是cs，所以此处必须指明为ds，CPU才知道此处以ds作为选择子。其它的情况如果是字符串指令，则是ES，否则都用DS。</p>
<p>呵呵，我认为还算通俗易懂啊。今天先到这里。</p>
<p>转载请注明出处：<span id="sample-permalink"><a  href="http://www.figoyao.com/blog/2009/12/08/159/" target="_blank">http://www.figoyao.com/blog/2009/12/08/159/</a></span></p><img src ="http://www.cppblog.com/figoyao/aggbug/104697.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/figoyao/" target="_blank">Figo</a> 2010-01-03 15:34 <a href="http://www.cppblog.com/figoyao/articles/104697.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏辅助工具开发教程-从入门到精通之A2_3篇</title><link>http://www.cppblog.com/figoyao/articles/104696.html</link><dc:creator>Figo</dc:creator><author>Figo</author><pubDate>Sun, 03 Jan 2010 07:33:00 GMT</pubDate><guid>http://www.cppblog.com/figoyao/articles/104696.html</guid><wfw:comment>http://www.cppblog.com/figoyao/comments/104696.html</wfw:comment><comments>http://www.cppblog.com/figoyao/articles/104696.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/figoyao/comments/commentRss/104696.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/figoyao/services/trackbacks/104696.html</trackback:ping><description><![CDATA[作者：<a  href="http://www.figoyao.com/" target="_blank">Figo</a>
<p>本节主要讲解汇编中的内存管理。</p>
<p>CPU对内存的管理的最小单元是字节（8个bit），为了标识不同单元，就给每个单元一个编号，这个编号就是我们下面说的物理地址：地址是一个无符
号的二进制整数，但为了书写方便一般写成十六进制形式，如0&#215;410234等。下面首先讲解32位CPU的内存管理模式：实模式和保护模式。</p>
<p>一、实模式<br>
物理地址=段地址&#215;16+偏移量<br>
段地址存储在<strong>段寄存器</strong>中，即<strong>CS</strong>（代码段寄存器）、<strong>DS</strong>（数据段寄存器）、<strong>ES</strong>（附加段寄存器）、<strong>SS</strong>（堆栈段寄存器）、<strong>FS</strong>（附加段寄存器）、<strong>GS</strong>（附加段寄存器），其中FS和GS是80386及以后型号的CPU增加的寄存器。在实际编程中，遵循的原则如下表所示：</p>
<table bgcolor="#ffffff" border="1" bordercolor="#000000">
    <tbody>
        <tr>
            <td><strong>指令操作</strong></td>
            <td><strong>缺省的段寄存器</strong></td>
            <td><strong>可选的段寄存器</strong></td>
            <td><strong>偏移量</strong></td>
        </tr>
        <tr>
            <td>取指令</td>
            <td>CS</td>
            <td>&nbsp;</td>
            <td>IP</td>
        </tr>
        <tr>
            <td>堆栈操作</td>
            <td>SS</td>
            <td>&nbsp;</td>
            <td>SP</td>
        </tr>
        <tr>
            <td>取操作数</td>
            <td>DS</td>
            <td>CS、ES、SS</td>
            <td>有效地址</td>
        </tr>
        <tr>
            <td>串操作</td>
            <td>DS/ES</td>
            <td>&nbsp;</td>
            <td>SI/DI</td>
        </tr>
        <tr>
            <td>BP指针寄存器</td>
            <td>SS</td>
            <td>CS、DS、ES</td>
            <td>有效地址</td>
        </tr>
    </tbody>
</table>
<p>简单解释一下上表，比如取操作数，缺省用DS作为段寄存器，偏移量为有效地址，<strong>DS:[100H]</strong>，方括号中的是有效地址，冒号前的DS作为段基地址，假设此时DS的值为0&#215;23E6H，那么内存单元的物理地址为：0&#215;23E6H&#215;16+100H=0&#215;23F60H。所谓缺省的段寄存器，是说如果地址表达式为[<strong>100H</strong>]，就认为DS是段基地址。为了便于理解，你可以这么去想：你要去宿舍楼找小明，首先需要知道楼层（段基地址），到楼层后再通过房间号（偏移地址），这样就找到小明了。<br>
内存最小单元式字节，但是我们经常接触的是字（2个字节）或双字（4个字节）。前面讲过，内存最小单元的编号就是地址，那么寻址字或双字的时候以哪个最小单元的地址为准呢？先来看个示意图：<br>
<a  href="http://www.figoyao.com/blog/2009/12/07/147/null"><img  src="http://farm3.static.flickr.com/2617/4165719637_33e6edddf5_o.jpg" title="addrbyte" alt="" height="263" width="330"></a><br>
地址为23451H的字节的数值为：12H；<br>
地址为23451H的字的数值为：3412H；<br>
地址为23451H的双字的数值为：78563412H。<br>
Intel的CPU规定：高(低)地址存储单元存储高(低)字节。所以地址为23451H的字的数值的低字节为低地址23451H处的12H，高字节为23452H处的34H。</p>
<p>二、保护模式<br>
此时的地址计算与实模式完全不同，段寄存器存储的不再是段基地址。对于32位CPU来讲，可以使用32根地址线访问内存，排列组合一下可以算出总共可以访
问2^32Byte，即4GB。顾名思义，保护模式涉及到地址空间优先级等安全属性的描述，段寄存器就用来保存这些安全描述信息，但由于信息太多，需要
64位长的数据才可存储完整，这个存储安全信息的64位长的数据有个专门的称谓：段描述符（Segment
Descriptor）。16位的段寄存器是不够用的，为了解决这个问题。80X86CPU引入了两个新的寄存器：全局描述符表寄存器<strong>GDTR</strong>（48位）和局部描述符表寄存器<strong>LDTR</strong>（16位）。原理就是充分利用内存空间，实现机制如下：<br>
<a  href="http://farm3.static.flickr.com/2768/4168308535_79ecce6d8a_o.jpg"><img  src="http://farm3.static.flickr.com/2768/4168308535_79ecce6d8a_o.jpg" class="alignnone" title="GDTR" alt="" height="695" width="851"></a><br>
在操作系统启动后，GDTR指向一块内存区域，该区域的起始地址为GDTR的16到47位指示的地址，长度为GDTR的0到15位指示的长度。这块内存被称作<strong>全局描述符表（GDT）</strong>，
因为它存储了操作系统使用的代码段、数据段和堆栈段的全部信息；同时还存储了用户建立的执行任务的信息（局部描述符表LDT索引），该信息记录了各个任务
内部代码段、数据段等的描述表的起始地址，可以理解成各个任务描述表的起始地址都存在这里，通过它可以定位所有任务的内存相关信息。<br>
在保护模式下，地址格式为XXXX:YYYYYYYY，XXXX表示段选择器，为DS、CS、FS等段寄存器；YYYYYYYY为偏移地址。我们要做的是通过XXXX确定描述符，然后找到基地址，加上偏移地址，进而得到有效的线性地址，此时才能定位内存单元。步骤如下：<br>
首先检查XXXX的第2位是否为0：为0表示描述符在GDT中，其位置由XXXX的高13位确定；不为0表示描述符在LDT中，要找LDT首先需要找到它
的基址，而基址存储在GDT中，其位置由LDTR的高13位确定，得到LDT基址后，我们进一步通过XXXX的高13位在LDT中确定目标内存单元的基
址，然后结合偏移地址就得到了线性地址。</p>
<p>今天就讲到这里，下节将开始寻址方式的讲解。</p>
<p>转载请注明出处：<span id="sample-permalink"><a  href="http://www.figoyao.com/blog/2009/12/07/147/" target="_blank">http://www.figoyao.com/blog/2009/12/07/147/</a></span></p><img src ="http://www.cppblog.com/figoyao/aggbug/104696.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/figoyao/" target="_blank">Figo</a> 2010-01-03 15:33 <a href="http://www.cppblog.com/figoyao/articles/104696.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏辅助工具开发教程-从入门到精通之A2_2篇</title><link>http://www.cppblog.com/figoyao/articles/104695.html</link><dc:creator>Figo</dc:creator><author>Figo</author><pubDate>Sun, 03 Jan 2010 07:32:00 GMT</pubDate><guid>http://www.cppblog.com/figoyao/articles/104695.html</guid><wfw:comment>http://www.cppblog.com/figoyao/comments/104695.html</wfw:comment><comments>http://www.cppblog.com/figoyao/articles/104695.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/figoyao/comments/commentRss/104695.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/figoyao/services/trackbacks/104695.html</trackback:ping><description><![CDATA[<p>作者：<a  href="http://www.figoyao.com/" target="_blank">Figo</a></p>
<p>讲了这么多天理论知识有的同学可能有些迷茫和厌倦了，你看，教室后门那个穿夹克的同学都睡着了。所以今天我们来小小的演习一下，囿于目前大家的知识储备，下面的练习只能归为局部演习，等你们羽翼丰满的时候我会领着大家实战。千万记住：欲速则不达。</p>
<p>首先我们需要自己编一个小游戏：玩家和怪物打斗，通过文字输出战斗情况。代码如下：</p>
<div class="wp-caption alignnone" style="width: 674px;"><a  href="http://www.figoyao.com/blog/2009/12/06/135/null"><img  src="http://farm3.static.flickr.com/2693/4162765690_fe723c609b_o.jpg" title="SimpleGame" alt="SimpleGame" height="762" width="664"></a>
<p class="wp-caption-text">SimpleGame</p>
</div>
<p>执行界面如下，怪物和玩家的血量依次递减：<br>
<a  href="http://www.figoyao.com/blog/2009/12/06/135/null"><img  src="http://farm3.static.flickr.com/2594/4162009261_a511b356d6_o.jpg" class="alignnone" title="exegame" alt="" height="303" width="188"></a><br>
包括以后真正的实战，我们要非常明确自己的目标：这里的目标是把玩家血量改为递增，即玩家被怪物攻击一次血量增加一个伤害值大小的数值。在<a  href="http://www.figoyao.com/blog/2009/12/05/124/" target="_blank">上节教程</a>，我们讲过与CPU交互的数据主要存储在<strong>内存</strong>中，那我们要做的就是找到内存中存储玩家血量数据的地址，因为CPU是通过<strong>地址总线</strong>来定位数据，这也就是我们第一步要做的事情：找地址，工具使用大家耳熟能详的<strong><a  href="http://www.cheatengine.org/" target="_blank">CheatEngine</a></strong>。步骤如下：<br>
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" height="363" width="420">
<embed lk_media="yes" lk_mediaid="lk_juiceapp_mediaPopup_1262503883945" type="application/x-shockwave-flash" src="http://www.tudou.com/v/2vPUYWVwp5k" wmode="opaque" allowscriptaccess="always" allowfullscreen="true" height="363" width="420"></object><br>
视频中的相关代码和程序打包下载：<a  href="http://figoyao.com/blog/file/CheatGame.rar" target="_blank">点击下载</a>。<br>
视频的<strong>清晰版</strong>下载：<a  href="http://down.qiannao.com/space/file/figoyao/share/lesson-002d01.avi/.page" target="_blank">点击下载</a>。</p>
<p>下面讲解一下外挂程序的细节：<br>
<a  href="http://www.figoyao.com/blog/2009/12/06/135/null"><img  src="http://farm3.static.flickr.com/2582/4162195159_ed9a407ac3_o.jpg" class="alignnone" title="AimBot" alt="" height="651" width="768"></a><br>
需要讲解的函数有2个：<strong>OpenProcess</strong>和<strong>WriteProcessMemory</strong>。<br>
1、OpenProcess(<em>dwDesiredAccess,<em>bInheritHandle,<em>dwProcessId</em></em></em>)<br>
第一个参数：对被打开进程的访问权，设置为PROCESS_ALL_ACCESS，即所有访问权；<br>
第二个参数：打开进程创建的子进程是否可以继承该访问权限，设置为NULL；<br>
第三个参数：要打开的进程的ID值，即我们在任务管理器里看到的PID（看不到的同学，通过<strong>查看</strong>-&gt;<strong>选择列</strong>-&gt;勾选<strong>PID</strong>）。<br>
此函数执行成功的话我们就得到了目标进程的句柄，你可以暂时把句柄理解系统为进程指定的身份证号。<br>
2、WriteProcessMemory(<em>hProcess,<em>lpBaseAddress,<em>lpBuffer,<em>nSize,<em>lpNumberOfBytesWritten</em></em></em></em></em>)<br>
第一个参数：要写入内存的目标进程句柄，已经通过上面函数得到了；<br>
第二个参数：要写入内存的地址指针，就是我们通过OllyDbg得到的那个地址；<br>
第三个参数：要写入的数据，即我们修改<strong>sub</strong>指令为<strong>add</strong>指令；<br>
第四个参数：要写入数据的大小；<br>
第五个参数：可选参数，与本处无关，设置为NULL。</p>
<p>本节我们给出了一个完整的Demo演示，实战中不会这么顺利很简单。希望这篇教程能够提起大家的兴趣，以迎接真正的挑战：<span style="color: #ff0000;"><strong>当你掌握了足够技术后，如果说还有什么能够阻挡你，只有你的想象力！</strong></span></p>
<p>转载请注明出处：<span id="sample-permalink"><a  href="http://www.figoyao.com/blog/2009/12/06/135/" target="_blank">http://www.figoyao.com/blog/2009/12/06/135/</a></span></p><img src ="http://www.cppblog.com/figoyao/aggbug/104695.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/figoyao/" target="_blank">Figo</a> 2010-01-03 15:32 <a href="http://www.cppblog.com/figoyao/articles/104695.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏辅助工具开发教程-从入门到精通之A2_1篇</title><link>http://www.cppblog.com/figoyao/articles/104694.html</link><dc:creator>Figo</dc:creator><author>Figo</author><pubDate>Sun, 03 Jan 2010 07:31:00 GMT</pubDate><guid>http://www.cppblog.com/figoyao/articles/104694.html</guid><wfw:comment>http://www.cppblog.com/figoyao/comments/104694.html</wfw:comment><comments>http://www.cppblog.com/figoyao/articles/104694.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/figoyao/comments/commentRss/104694.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/figoyao/services/trackbacks/104694.html</trackback:ping><description><![CDATA[<p>作者：<a  href="http://www.figoyao.com/" target="_blank">Figo</a></p>
<p>本篇起我们开始讲解汇编语言，最终目标是能读懂OllyDbg或IDA等反汇编工具得到的汇编代码，并可进行基本的高级语言嵌入汇编程序设计。我假设<a  href="http://www.figoyao.com/blog/2009/11/28/34/" target="_blank">A1系列文章</a>你已经读过，并掌握基本的高级语言程序设计方法。</p>
<p>对任何事物都要从ta产生的来龙去脉去研究，这样才能获得全面深刻的理解。话说上世纪40年代产生了第一代计算机，由于是基于二进制数制逻辑，人们可以和计算机交流的语言限于<a  href="http://zh.wikipedia.org/zh-cn/%E6%9C%BA%E5%99%A8%E8%AF%AD%E8%A8%80" target="_blank">机器语言</a>，你完全可以把它理解为某种外星语言：机器语言完全由0和1组成，当然，这属于人家计算机内部交流语言。我们都知道<a  href="http://zh.wikipedia.org/zh-tw/%E5%8D%A0%E5%A3%AB%E9%82%A6" target="_blank">詹姆士.邦德</a>代号007，知道周星星代号<a  href="http://zh.wikipedia.org/zh-cn/%E5%94%90%E4%BC%AF%E8%99%8E%E9%BB%9E%E7%A7%8B%E9%A6%99" target="_blank">9527</a>，
知道圣诞节1225，情人节214&#8230;说这么多，其实是为了告诉大家：每个人的记忆力都很好的，相信我，汇编里面要你记住的常用代号不会比节日多：）。最
初，为了要计算机按照人们意图行事，不得不通过机器语言与之交流，这就使得当时的程序员和远古时期的巫师一样稀少和神秘，因为机器语言容易出错，比如，你
找出下面两个数字的不同：<br>
1000110100011101001100011100011和1000110100011101001101011100011。<br>
这还属于最简单的，如果是十几张纸都是0和1，让你找错，恐怕效率和正确率都不会太高。当人们意识到这个问题时，开始构造一种机制来简化编程，这样就有了汇编语言，它的思想就是互相替换：在人编写程序时候，用邦德<strong>替换</strong>007，周星星<strong>替换</strong>9527，&#8230;；在计算机执行时候，用007<strong>替换</strong>邦德，9527<strong>替换</strong>周星星，&#8230;。虽然这里说的是替换，但实际属于<a  href="http://zh.wikipedia.org/zh-cn/%E7%BC%96%E8%AF%91" target="_blank">编译原理</a>的东西，操作起来比较复杂，此处就不深入了。</p>
<p>永远记住，汇编语言是处理器相关的，此处主要以<a  href="http://www.intel.com/technology/product/index.htm" target="_blank">X86系列</a>处理器为原型讲解，<a  href="http://www.amd.com/" target="_blank">AMD公司</a>的CPU一般都是兼容IA-32架构的，所以满足X86规则的汇编程序在AMD兼容芯片一样可以运行。</p>
<p>我们知道计算机几个主要配件：CPU、内存和硬盘等，而把这些配件整合到一起的就是主板，上面有各种形式的导线。下面是一张逻辑图：<br>
<a  href="http://www.figoyao.com/blog/2009/12/05/124/null"><img  src="http://farm3.static.flickr.com/2753/4160289334_2606383040_o.jpg" class="alignnone" title="CPUArchitect" alt="" height="247" width="566"></a><br>
上图中的<strong>I/O设备</strong>是
指输入（Input）输出（Output）设备，如鼠标、键盘和摄像头等；I/O接口指的是USB、COM等接口；存储器主要指内存。从上图我们看到由
CPU引出三种总线：控制、地址和数据总线，这是CPU坐阵指挥的途径，也就是说所有CPU指令最终都要表现在总线的操作上，而汇编语言本质上就是学会通
过CPU操作总线的方法。那么，好奇的你就没有什么想问的吗？记住，有时候，好奇是比知识更重要的想象力！虽然好奇<a  href="http://en.wikipedia.org/wiki/Curiosity_killed_the_cat" target="_blank">害死加菲猫</a>。那么，CPU里面是怎么个组成呢？在我们学校有这么一个传说：<a  href="http://baike.baidu.com/view/993031.htm" target="_blank">胡伟武</a>同学本科毕业设计就是用单片机搭出一个类8086芯片系统，这从一个侧面表达出X86的复杂。幸运的是，大家暂时不必了解太多；不幸的是，如果你打算像我的那个去<a  href="http://www.360.cn/" target="_blank">360</a>的师兄一样<strong>坚持走底层技术路线</strong>的话，你还是有必要读读Intel指令手册的。不过，我们暂时只需做个&#8220;幸运儿&#8221;：）。</p>
<p>当我们要求计算机对两个数执行求和运算时，CPU处理的流程是怎样的呢？假设要对a与b求和，a、b均已经存在于内存了。结合上面架构图来看：CPU通过<strong>控制总线</strong>发出读数据的指令，通过<strong>地址总线</strong>指明要读数据在内存中的地址，通过<strong>数据总线</strong>把数据传给CPU，CPU把数据存储在自己的<strong>寄存器</strong>中，然后通过<strong>算术逻辑部件</strong>（ALU）完成运算。这里多了两个名词：寄存器和算术逻辑部件，我将逐一讲解。</p>
<p>任何事物不能凭空存在，包括数据，CPU要交互的数据大部分放在内存和硬盘：硬盘向内存传输数据的速率远低于内存向CPU传输数据的速率；内存向
CPU传输数据的速率远低于CPU的处理速率，现在的CPU为了缓解这个矛盾，一般都使用高速缓存机制，因为高速缓存向CPU传输数据的速率更快。那么，
问题来了：CPU接收数据后把数据存放在哪里呢？答案是寄存器，寄存器的存取速率比内存快很多，主要用来存放运算过程中的数据地址、数据及中间结果等。本
篇教程是以32位处理器为原型讲解，首先要讲的是最常用的<strong>通用寄存器</strong>：<span style="color: #ff0000;">EAX</span>、<span style="color: #ff0000;">EBX</span>、<span style="color: #ff0000;">ECX</span>和<span style="color: #ff0000;">EDX</span>。这就是寄存器代号，类似9527代表周星星：<br>
<a  href="http://www.figoyao.com/blog/2009/12/05/124/null"><img  src="http://farm3.static.flickr.com/2635/4160382064_d98c938b92_o.gif" class="alignnone" title="EAX" alt="" height="117" width="367"></a><br>
有的同学看到上面的图可能像魔兽争霸一样：混乱之至了，怎么又有AH、AL和AX，这是什么啊？我们来论资排辈一下，《天龙八部》里面少林<strong><span style="color: #0000ff;">觉</span>字辈</strong>高于<strong><span style="color: #0000ff;">玄</span>字辈</strong>，因为他们老师的辈分不同所导致；那么把从AL到EAX按照<strong>位数</strong>排
下辈：（AL、AH[8位]）&lt;AX[16位]&lt;EAX[32位]。其实它们指的是同一个东西的不同部分，好比一个手由5个指头组成，一个人
有2个手：但自始自终都是指的一个人，只是范围不同而已。同理EBX、ECX和EDX与EAX结构和命名类似，比如EBX也有BX、BH和BL。这样做的
目的是提供最大的灵活性，比如有时你只需要存一个8位的值，那么就没必要占用全部寄存器，只需要用一部分即可：这与宿舍类似，一个宿舍假设可以容纳4个
人，那么只有一个学生的时候占一张床即可，不必全部占满。<br>
<a  href="http://www.figoyao.com/blog/2009/12/05/124/null"><img  src="http://farm3.static.flickr.com/2794/4168308533_ba350da612_o.jpg" class="alignnone" title="EXX" alt="" height="326" width="564"></a></p>
<p>除了通用寄存器，还有<strong>变址和指针寄存器(EDI/ESI/ESP/EBP)</strong>、<strong>指令指针寄存器(EIP)</strong>、<strong>标志寄存器(EFlags)</strong>：这些都是32位的寄存器。CPU中16位的寄存器是<strong>段寄存器</strong>，也就是说段寄存器这个房子的平米数比前面的寄存器要少，最大可以存16bit。<br>
<a  href="http://www.figoyao.com/blog/2009/12/05/124/null"><img  src="http://farm3.static.flickr.com/2567/4168308537_f948d1984f_o.jpg" class="alignnone" title="EIP" alt="" height="715" width="465"></a><br>
各种寄存器的主要用途如下表：</p>
<table bgcolor="#ffffff" border="1" bordercolor="#000000">
    <tbody>
        <tr>
            <td><strong>寄存器</strong></td>
            <td><strong>代号</strong></td>
            <td><strong>主要用途</strong></td>
        </tr>
        <tr>
            <td>累加器</td>
            <td>EAX</td>
            <td>算术运算、存储中间结果、函数返回值</td>
        </tr>
        <tr>
            <td>基地址寄存器</td>
            <td>EBX</td>
            <td>基地址指针</td>
        </tr>
        <tr>
            <td>计数器</td>
            <td>ECX</td>
            <td>循环计数、移位操作计数、重复操作计数</td>
        </tr>
        <tr>
            <td>数据寄存器</td>
            <td>EDX</td>
            <td>乘除运算、存储中间结果</td>
        </tr>
        <tr>
            <td>源变址寄存器</td>
            <td>ESI</td>
            <td>存储指针、串指令的源操作数指针</td>
        </tr>
        <tr>
            <td>源目标变址寄存器</td>
            <td>EDI</td>
            <td>存储指针、串指令的目的操作数指针</td>
        </tr>
        <tr>
            <td>基地址指针</td>
            <td>EBP</td>
            <td>存储指针、存取堆栈指针</td>
        </tr>
        <tr>
            <td>栈顶指针</td>
            <td>ESP</td>
            <td>堆栈的栈顶指针</td>
        </tr>
    </tbody>
</table>
<p>这节暂时讲到这里。</p>
<p>转载请注明出处：<span id="sample-permalink"><a  href="http://www.figoyao.com/blog/2009/12/05/124/" target="_blank">http://www.figoyao.com/blog/2009/12/05/124/</a></span></p><img src ="http://www.cppblog.com/figoyao/aggbug/104694.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/figoyao/" target="_blank">Figo</a> 2010-01-03 15:31 <a href="http://www.cppblog.com/figoyao/articles/104694.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>