﻿<?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++博客-道。道。道-随笔分类-misc</title><link>http://www.cppblog.com/eday/category/3082.html</link><description /><language>zh-cn</language><lastBuildDate>Wed, 21 May 2008 01:35:44 GMT</lastBuildDate><pubDate>Wed, 21 May 2008 01:35:44 GMT</pubDate><ttl>60</ttl><item><title>Windows系统安全设置</title><link>http://www.cppblog.com/eday/archive/2006/11/18/15387.html</link><dc:creator>松*</dc:creator><author>松*</author><pubDate>Sat, 18 Nov 2006 08:59:00 GMT</pubDate><guid>http://www.cppblog.com/eday/archive/2006/11/18/15387.html</guid><description><![CDATA[  系统的安装<br />1、不要选择从网络上安装<br />虽然微软支持在线安装，但这绝对不安全。在系统未全部安装完之前不要连入网络，特别是Internet！甚至不要把一切硬件都连接好来安装。因为Windows 2000安装时，在输入用户管理员账号“Administrator”的密码后，系统会建立一个“ADMIN”的共享账号，但是并没有用刚输入的密码来保护它，这种情况一直会持续到计算机再次启动。在此期间，任何人都可以通过“ADMIN”进入系统；同时，安装完成，各种服务会马上自动运行，而这时的服务器还到处是漏洞，非常容易从外部侵入。<br />2、要选择NTFS格式来分区<br />　　最好所有的分区都是NTFS格式，因为NTFS格式的分区在安全性方面更加有保障。就算其他分区采用别的格式(如FAT32)，但至少系统所在的分区中应是NTFS格式。<br />另外，应用程序不要和系统放在同一个分区中，以免攻击者利用应用程序的漏洞(如微软的IIS的漏洞)导致系统文件的泄漏，甚至让入侵者远程获取管理员权限。<br />3、系统版本的选择<br />版本的选择：WIN2000有各种语言的版本，对于我们来说，可以选择英文版或简体中文版，我强烈建议：在语言不成为障碍的情况下，请一定使用英文版。要知道，微软的产品是以Bug &amp; <br />Patch而著称的，中文版的Bug远远多于英文版，而补丁一般还会迟至少半个月（也就是说一般微软公布了漏洞后你的机子还会有半个月处于无保护状况） <br />　　4、组件的定制：<br />win2000在默认情况下会安装一些常用的组件，但是正是这个默认安装是很危险的，你应该确切的知道你需要哪些服务，而且仅仅安装你确实需要的服务，根据安全原则，最少的服务+最小的权限=最大的安全。典型的WEB服务器需要的最小组件选择是：只安装IIS的Com Files，IIS Snap-In，WWW Server组件。如果你确实需要安装其他组件，请慎重，特别是：Indexing Service, FrontPage 2000 Server Extensions, Internet Service Manager (HTML)这几个危险服务。<br />5、分区和逻辑盘的分配<br />建议最少建立两个分区，一个系统分区，一个应用程序分区，这是因为，微软的IIS经常会有泄漏源码/溢出的漏洞，如果把系统和IIS放在同一个驱动器会导致系统文件的泄漏甚至入侵者远程获取ADMIN。推荐的安全配置是建立三个逻辑驱动器，第一个大于2G，用来装系统和重要的日志文件，第二个放IIS，第三个放FTP，这样无论IIS或FTP出了安全漏洞都不会直接影响到系统目录和系统文件。要知道，IIS和FTP是对外服务的，比较容易出问题。而把IIS和FTP分开主要是为了防止入侵者上传程序并从IIS中运行。（这个可能会导致程序开发人员和编辑的苦恼，管他呢，反正你是管理员J）<br />6、安装杀毒软件。<br />杀毒软件不仅能杀掉一些著名的病毒，还能查杀大量木马和后门程序，因此要注意经常运行程序并升级病毒库。 7、安装防火墙。<br />8、安装系统补丁。<br />到微软网站下载最新的补丁程序：<br />经常访问微软和一些安全站点，下载最新的Service Pack和漏洞补丁，是保障服务器长久安全的惟一方法。<br />9、安装顺序的选择<br />首先，何时接入网络：Win2000在安装时的ADMIN$的共享的漏洞；同时，只要安装一完成，各种服务就会自动运行，而这时的服务器是满身漏洞，非常容易进入的，因此，在完全安装并配置好win2000 SERVER之前，一定不要把主机接入网络。 <br />其次，补丁的安装：补丁的安装应该在所有应用程序安装完之后，因为补丁程序往往要替换/修改某些系统文件，如果先安装补丁再安装应用程序有可能导致补丁不能起到应有的效果，例如：IIS的HotFix就要求每次更改IIS的配置都需要安装。<br />系统的安全设置<br />1、用户安全设置<br />用户帐号检查，停止不需要的帐号，建议更改默认的帐号名。<br />1)、禁用Guest账号 <br />　　在计算机管理的用户里面把Guest账号禁用。为了保险起见，最好给Guest加一个复杂的密码。 <br />2)、限制不必要的用户 <br />　　去掉所有的Duplicate User用户、测试用户、共享用户等等。用户组策略设置相应权限，并且经常检查系统的用户，删除已经不再使用的用户。<br />3)、创建两个管理员账号 <br />　　创建一个一般权限用户用来收信以及处理一些日常事物，另一个拥有Administrators权限的用户只在需要的时候使用。 <br />4)、把系统Administrator账号改名 <br />　　大家都知道，Windows 2000的Administrator用户是不能被停用的，这意味着别人可以一遍又一遍地尝试这个用户的密码。尽量把它伪装成普通用户，比如改成Guesycludx。 <br />5)、创建一个陷阱用户 <br />　　什么是陷阱用户?即创建一个名为“Administrator”的本地用户，把它的权限设置成最低，什么事也干不了的那种，并且加上一个超过10位的超级复杂密码。这样可以让想入侵的人慢慢忙一段时间。 <br />6)、把共享文件的权限从Everyone组改成授权用户 <br />　　不要把共享文件的用户设置成“Everyone”组，包括打印共享，默认的属性就是“Everyone”组的。 <br />7)、开启用户策略 （不建议）<br />　　使用用户策略，分别设置复位用户锁定计数器时间为20分钟，用户锁定时间为20分钟，用户锁定阈值为3次。 <br />8)、不让系统显示上次登录的用户名 （可选）<br />打开注册表编辑器并找到注册表项HKLMSoftwareMicrosoftWindowsTCurrentVersionWinlogonDont-DisplayLastUserName，把键值改成1<br />9)、系统账号/共享列表<br />Win2000的默认安装允许任何用户通过空用户得到系统所有账号/共享列表，这个本来是为了方便局域网用户共享文件的，但是一个远程用户也可以得到你的用户列表并使用暴力法破解用户密码。可以通过更改注册表Local_Machine\System\CurrentControlSet\Control\LSA-RestrictAnonymous = 1来禁止139空连接，还可以在win2000的本地安全策略（如果是域服务器就是在域服务器安全和域安全策略中）就有这样的选项RestrictAnonymous（匿名连接的额外限制），这个选项有三个值： 0：None. Rely on default permissions（无，取决于默认的权限） 1：Do not allow enumeration of SAM accounts and shares（不允许枚举SAM帐号和共享） 2：No access without explicit anonymous permissions（没有显式匿名权限就不允许访问） 0这个值是系统默认的，什么限制都没有，远程用户可以知道你机器上所有的账号、组信息、共享目录、网络传输列表(NetServerTransportEnum等等，对服务器来说这样的设置非常危险。 1这个值是只允许非NULL用户存取SAM账号信息和共享信息。 2这个值是在win2000中才支持的，如果不想有任何共享，就设为2。一般推荐设为1。<br />2、 口令安全设置<br />1)、使用安全密码 <br />　　要注意密码的复杂性，还要记住经常改密码。 <br />2)、开启密码策略 <br />注意应用密码策略，如启用密码复杂性要求，设置密码长度最小值为8位，设置强制密码历史为5次，时间为42天。 <br />3、 系统安全设置<br />1)、利用Windows 2000的安全配置工具来配置安全策略<br />微软提供了一套基于MMC（管理控制台）安全配置和分析工具，利用它们你可以很方便地配置你的服务器以满足你的要求。具体内容请参考微软主页：http://www.microsoft.com/windows2000/techinfo/<br />howitworks/security/sctoolset.asp<br />2)、安全日志：Win2000的默认安装是不开任何安全审核的！可以到本地安全策略<br />-&gt;审核策略中打开相应的审核，推荐的审核是：<br />账户管理 成功 失败 <br />登录事件 成功 失败 <br />对象访问 失败 <br />策略更改 成功 失败 <br />特权使用 失败 <br />系统事件 成功 失败 <br />目录服务访问 失败 <br />账户登录事件 成功 失败 <br />（审核项目少的缺点是万一你想看发现没有记录那就一点都没辙；审核项目太多不仅会占用系统资源而且会导致你根本没空去看，这样就失去了审核的意义。）<br />与之相关的是： <br />在账户策略-&gt;密码策略中设定： <br />密码复杂性要求 启用 <br />密码长度最小值 8位 <br />强制密码历史 5次 <br />最长存留期 42天 <br />在账户策略-&gt;账户锁定策略中设定： <br />账户锁定 5次错误登录 <br />锁定时间 20分钟 <br />复位锁定计数 20分钟 <br />同样，Terminal Service的安全日志默认也是不开的，我们可以在Terminal Service Configration（远程服务配置）-权限-高级中配置安全审核，一般来说只要记录登录、注销事件就可以了。<br />3)、目录和文件权限： <br />为了控制好服务器上用户的权限，同时也为了预防以后可能的入侵和溢出，我们还必须非常小心地设置目录和文件的访问权限，NT的访问权限分为：读取、写入、读取及执行、修改、列目录、完全控制。在默认的情况下，大多数的文件夹对所有用户（Everyone这个组）是完全敞开的（Full Control），你需要根据应用的需要进行权限重设。 <br />在进行权限控制时，请记住以下几个原则： <br />1&gt;限是累计的：如果一个用户同时属于两个组，那么他就有了这两个组所允许的所有权限； <br />2&gt;拒绝的权限要比允许的权限高（拒绝策略会先执行）如果一个用户属于一个被拒绝访问某个资源的组，那么不管其他的权限设置给他开放了多少权限，他也一定不能访问这个资源。所以请非常小心地使用拒绝，任何一个不当的拒绝都有可能造成系统无法正常运行； <br />3&gt;文件权限比文件夹权限高<br />4&gt;利用用户组来进行权限控制是一个成熟的系统管理员必须具有的优良习惯之一； <br />5&gt;仅给用户真正需要的权限，权限的最小化原则是安全的重要保障；<br />4)、禁止建立空连接（IPC＄）<br />默认情况下，任何用户都可通过空连接连上服务器，进而枚举出账号，猜测密码。我们可以通过修改注册表来禁止建立空连接：即把“ Local_Machine\System\CurrentControlSet\Control\LSA-RestrictAnonymous” 的值改成“1”即可。如果使用“2”可能会造成你的一些服务无法启动，如SQL Server<br />此外，安全和应用在很多时候是矛盾的。因此，你需要在其中找到平衡点，如果安全原则妨碍了系统应用，那么这个安全原则也不是一个好的原则<br />5)、禁止管理共享<br />HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet<br />\Services\LanmanServer\Parameters项<br />对于服务器，添加键值“AutoShareServer”，类型为“REG_DWORD”，值为“0”。<br />对于客户机，添加键值“AutoShareWks”，类型为“REG_DWORD”，值为“0”。<br />(关闭server服务)<br />6)、还有一个就是139端口，139端口是NetBIOS　Session端口，用来文件和打印共享，注意的是运行samba的unix机器也开放了139端口，功能一样。以前流光2000用来判断对方主机类型不太准确，估计就是139端口开放既认为是NT机，现在好了。 <br />关闭139口听方法是在“网络和拨号连接”中“本地连接”中选取“Internet协议(TCP/IP)”属性，进入“高级TCP/IP设置”“WINS设置”里面有一项“禁用TCP/IP的NETBIOS”，打勾就关闭了139端口。 <br />对于个人用户来说，可以在各项服务属性设置中设为“禁用”，以免下次重启服务也重新启动，端口也开放了<br />7)、使用组策略，IP策略<br /><br />------------------------<br />8)、防范SYN攻击<br />使用SYN淹没攻击保护<br />相关的值项在<br />HKLM\SYSTEM\CurrentControlSet\Service\Tcpip\Parameters下。<br />1） DWORD：SynAttackProtect：定义了是否允许SYN淹没攻击保护，值1表示允许起用WIN2000的SYN淹没攻击保护。<br />2） DWORD：TcpMaxConnectResponseRetransmissions：定义了对于连接请求回应包的重发次数。值为1，则SYN淹没攻击不会有效果，但是这样会造成连接请求失败几率的增高。SYN淹没攻击保护只有在该值&gt;=2时才会被启用，默认值为3。<br />（上边两个值定义是否允许SYN淹没攻击保护，下面三个则定义了激活SYN淹没攻击保护的条件，满足其中之一，则系统自动激活SYN淹没攻击保护。）<br />3） DWORD：TcpMaxHalfOpen：定义能够处于SYN_RECEIVED状态的TCP连接的数目。默认值100。<br />4） TcpMaxHalfOpenRetried：定义在重新发送连接请求后，仍然处于SYN_RECEIVED状态的TCP连接的数目。默认值80。<br />5） TcpMaxPortsExhausted：定义系统拒绝连接请求的次数。默认值5。<br />减小syn-ack包的响应时间。<br />HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpMaxConnectResponseRetransmissions定义了重发SYN-ACK包的次数。<br />增大NETBT的连接块增加幅度和最大数器。NETBT使用139端口。<br />HKLM\SYSTEM\CurrentControlSet\Services\NetBt\Parameters\BacklogIncrement默认值为3，最大20，最小1。<br />HKLM\SYSTEM\CurrentControlSet\Services\NetBt\Parameters\MaxConnBackLog默认值为1000，最大可取40000。<br />动态配置Backlog。<br />相关的值项在HKLM\SYSTEM\CurrentControlSet\Service\AFD\Parameters下<br />1) DWORD：EnableDynamicBacklog：定义是否允许动态Backlog，默认为0，1为允许。<br />2) DWORD：MinimumDynamicBacklog：定义动态Backlog分配的未使用的自由连接的最小数目。默认值为0，建议设为20。<br />3) DWORD：MaximumDynamicBacklog：定义最大“准”连接数目。大小取决于内存的大小，一般每32M最大可以增加5000个。<br />4) DWORD：DynamicBacklogGrowthDelta：定义每次增加的自由连接数目，建议设置为10。<br />10)、预防DoS： <br />在注册表HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters中更改以下值可以防御一定强度的DoS攻击 <br />SynAttackProtect REG_DWORD 2 <br />EnablePMTUDiscovery REG_DWORD 0 <br />NoNameReleaseOnDemand REG_DWORD 1 <br />EnableDeadGWDetect REG_DWORD 0 <br />KeepAliveTime REG_DWORD 300,000 <br />PerformRouterDiscovery REG_DWORD 0 <br />EnableICMPRedirects REG_DWORD 0<br />11)、防止ICMP重定向报文的攻击<br />HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\ParametersEnableICMPRedirects REG_DWORD 0x0(默认值为0x1)<br />该参数控制Windows 2000是否会改变其路由表以响应网络设备(如路由器)发送给它的ICMP重定向消息，有时会被利用来干坏事。Win2000中默认值为1，表示响应ICMP重定向报文。<br />12)、禁止响应ICMP路由通告报文<br />HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\interfacePerformRouterDiscovery REG_DWORD 0x0(默认值为0x2)<br />“ICMP路由公告”功能可造成他人计算机的网络连接异常,数据被窃听，计算机被用于流量攻击等严重后果。此问题曾导致校园网某些局域网大面积，长时间的网络异常。建议关闭响应ICMP路由通告报文.Win2000中默认值为2，表示当DHCP发送路由器发现选项时启用。<br />13)、设置生存时间<br />HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\ParametersDefaultTTL REG_DWORD 0-0xff(0-255 十进制,默认值128)<br />指定传出IP数据包中设置的默认生存时间(TTL)值。TTL决定了IP数据包在到达目标前在网络中生存的最大时间。它实际上限定了IP数据包在丢弃前允许通过的路由器数量.有时利用此数值来探测远程主机*作系统。<br />14)、不支持IGMP协议<br />HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\ParametersIGMPLevel REG_DWORD 0x0(默认值为0x2)<br />记得Win9x下有个bug，就是用可以用IGMP使别人蓝屏，修改注册表可以修正这个bug。Win2000虽然没这个bug了，但IGMP并不是必要的，因此照样可以去掉。改成0后用route print将看不到那个讨厌的224.0.0.0项了。<br />15)、设置arp缓存老化时间设置<br />HKLM\SYSTEM\CurrentControlSet\Services:\Tcpip\Parameters<br />ArpCacheLife REG_DWORD 0-0xFFFFFFFF(秒数,默认值为120秒)<br />ArpCacheMinReferencedLife REG_DWORD 0-0xFFFFFFFF(秒数,默认值为600)<br />如果ArpCacheLife大于或等于ArpCacheMinReferencedLife，则引用或未引用的ARP缓存项在ArpCacheLife秒后到期。如果ArpCacheLife小于ArpCacheMinReferencedLife，未引用项在ArpCacheLife秒后到期，而引用项在ArpCacheMinReferencedLife秒后到期。每次将出站数据包发送到项的IP地址时，就会引用ARP缓存中的项。<br /><br />16)、禁止死网关监测技术<br />HKLM\SYSTEM\CurrentControlSet\Services:\Tcpip\Parameters<br />EnableDeadGWDetect REG_DWORD 0x0(默认值为ox1)<br />如果你设置了多个网关，那么你的机器在处理多个连接有困难时，就会自动改用备份网关，有时候这并不是一项好主意，建议禁止死网关监测。<br />17)、不支持路由功能<br />HKLM\SYSTEM\CurrentControlSet\Services:\Tcpip\Parameters<br />IPEnableRouter REG_DWORD 0x0(默认值为0x0)<br />把值设置为0x1可以使Win2000具备路由功能，由此带来不必要的问题。<br />18)、做NAT时放大转换的对外端口最大值<br />HKLM\SYSTEM\CurrentControlSet\Services:\Tcpip\Parameters<br />MaxUserPort REG_DWORD 5000-65534(十进制)(默认值0x1388--十进制为5000)<br />当应用程序从系统请求可用的用户端口数时，该参数控制所使用的最大端口数。正常情况下，短期端口的分配数量为1024-5000。将该参数设置到有效范围以外时，就会使用最接近的有效数值(5000或65534)。使用NAT时建议把值放大点。<br />19)、修改MAC地址<br />HKLM\SYSTEM\CurrentControlSet\Control\Class找到右窗口的说明为"网卡"的目录，比如说是{4D36E972-E325-11CE-BFC1-08002BE10318}展开之，在其下0000,0001,0002...的分支中找到"DriverDesc"的键值为你网卡的说明，比如说"DriverDesc"的值为"Intel&amp;reg; 82559 Fast Ethernet LAN on Motherboard"然后在右窗口新建一字符串值，名字为"Networkaddress"，内容为你想要的MAC值，比如说是"004040404040"然后重起计算机，ipconfig /all看看。<br />20)、禁止光盘的自动运行功能<br />Windows 2000的光盘的自动运行功能也是系统安全的隐患，光盘中只要存在autorun．inf文件，则系统会自动试图执行文件中open字段后的文件路径(市场上已经出现了破解屏保密码的光盘，如果不禁止光盘的自动运行功能，以上所做的设置都将是白费)，步骤为：<br />⑴展开HKEY＿LOCAL＿MACHINE\SO<br />FTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer子键分支；<br />⑵在Explorer主键中新建DWORD值NoDriveTypeAutoRun，改值为1。<br />21)、禁止使用MS－DOS方式<br />采用上述方法隐藏某个磁盘分区的作用仅限于图形界面，但在字符界面如MS－DOS方式无效，因此我们必须采用适当的方法禁止普通用户使用MS－DOS方式。方法为：<br />①展开HKLU\SOFTWARE \Policies\Microsoft\Windows\system分支；<br />②新建一个名为DisableCMD的DWORD值，改值为1(这项用于禁止用户进入Windows 2000的MS－DOS方式)。值为2则批处理文件也不能运行。<br />22)、禁止运行指定的程序<br />23)、只允许运行指定的程序<br />24)、禁止使用注册表编辑器<br />HKCU\Software\Microsoft\Windows\Current Version\policies\System下<br />DWORD：DisableRegistryTools：值为1则禁用注册表编辑器。注意：使用此功能最好作个注册表备份，或者准备一个其他的注册表修改工具，因为禁止了注册表编辑器以后，就不能再用该注册表编辑器将值项改回来了。<br />22)、禁止使用任何程序（对于前边设置的陷阱用户）<br />不允许运行任何程序。方法为：<br />①展开HKLU\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer子键分支；<br />②在Explorer主键下新建RestrctRun的DWORD值，将值改为1。<br />不过你也可以发一下善心，给他几个Windows 2000自带的游戏玩一玩，方法为：在Explorer主键下新建名为RestrctRun的主键，在其下分别新建名为1、2、3、4的字符串值，将值分别改为MSHEARTS．EXE、FREECELL．EXE、WINMINE．EXE、SOL．EXE(只需程序名，无需路径)，则系统只能运行网上红心大战、空当接龙、扫雷、纸牌。无法执行其它任何程序。<br />服务安全设置<br />1)、关闭不必要的端口<br />关闭端口意味着减少功能，在安全和功能上面需要你做一点决策。如果服务器安装在防火墙的后面，冒险就会少些。但是，永远不要认为你可以高枕无忧了。用端口扫描器扫描系统已开放的端口，确定系统开放的哪些服务可能引起黑客入侵。在系统目录中的\system32\drivers\etc\services 文件中有知名端口和服务的对照表可供参考。具体方法为：打开“ 网上邻居/属性/本地连接/属性/internet 协议(TCP/IP)/属性/高级/选项/TCP/IP筛选/属性” 打开“TCP/IP筛选”，添加需要的TCP、UDP协议即可。<br />2)、设置好安全记录的访问权限<br />安全记录在默认情况下是没有保护的，把它设置成只有Administrators和系统账户才有权访问。<br />3)、把敏感文件存放在另外的文件服务器中<br />虽然现在服务器的硬盘容量都很大，但是你还是应该考虑是否有必要把一些重要的用户数据（文件、数据表、项目文件等）存放在另外一个安全的服务器中，并且经常备份它们。<br />4)、停止不必要的服务<br />服务开的太多也不是个好事，将没有必要的服务通通关掉吧！特别是连管理员都不知道是干什么的服务，还开着干什么！关掉！免得给系统带来灾难。<br />另外，管理员如果不外出，不需要远程管理你的计算机的话，最好将一切的远程网络登录功能都关掉。注意，除非特别需要，否则禁用“Task Scheduler”、“RunAs Service”服务！<br />关闭一项服务的方法很简单，运行cmd.exe之后，直接net stop servername即可<br />5)、更改管理服务的端口<br />pcanywhere端口号，终端服务端口号。<br />5)、在Win2000中如何关闭ICMP(Ping)<br />ICMP的全名是Internet Control and Message Protocal即因特网控制消息/错误报文协议，这个协议主要是用来进行错误信息和控制信息的传递，例如著名的Ping和Tracert工具都是利用ICMP协议中的ECHO request报文进行的（请求报文ICMP ECHO类型8代码0，应答报文ICMP ECHOREPLY类型0代码0）。<br />ICMP协议有一个特点---它是无连结的，也就是说只要发送端完成ICMP报文的封装并传递给路由器，这个报文将会象邮包一样自己去寻找目的地址，这个特点使得ICMP协议非常灵活快捷，但是同时也带来一个致命的缺陷---易伪造（邮包上的寄信人地址是可以随便写的），任何人都可以伪造一个ICMP报文并发送出去，伪造者可以利用SOCK_RAW编程直接改写报文的ICMP首部和IP首部，这样的报文携带的源地址是伪造的，在目的端根本无法追查，（攻击者不怕被抓那还不有恃无恐？）根据这个原理，外面出现了不少基于ICMP的攻击软件，有通过网络架构缺陷制造ICMP风暴的，有使用非常大的报文堵塞网络的，有利用ICMP碎片攻击消耗服务器CPU的，甚至如果将ICMP协议用来进行通讯，可以制作出不需要任何TCP/UDP端口的木马（参见相关文章）......既然ICMP协议这么危险，我们为什么不关掉它呢？<br />我们都知道，Win2000在网络属性中自带了一个TCP/IP过滤器，我们来看看能不能通过这里关掉ICMP协议，桌面上右击网上邻居-&gt;属性-&gt;右击你要配置的网卡-&gt;属性-&gt;TCP/IP-&gt;高级-&gt;选项-&gt;TCP/IP过滤，这里有三个过滤器，分别为：TCP端口、UDP端口和IP协议，我们先允许TCP/IP过滤，然后一个一个来配置，先是TCP端口，点击"只允许"，然后在下面加上你需要开的端口，一般来说WEB服务器只需要开80(www)，FTP服务器需要开20(FTP Data)，21(FTP Control)，邮件服务器可能需要打开25(SMTP),110(POP3)，以此类推......接着是UDP，UDP协议和ICMP协议一样是基于无连结的，一样容易伪造，所以如果不是必要（例如要从UDP提供DNS服务之类）应该选择全部不允许，避免受到洪水（Flood）或碎片（Fragment）攻击。最右边的一个编辑框是定义IP协议过滤的，我们选择只允许TCP协议通过，添加一个6（6是TCP在IP协议中的代码，IPPROTO_TCP=6）,从道理上来说，只允许TCP协议通过时无论UDP还是ICMP都不应该能通过，可惜的是这里的IP协议过滤指的是狭义的IP协议，从架构上来说虽然ICMP协议和IGMP协议都是IP协议的附属协议，但是从网络7层结构上ICMP/IGMP协议与IP协议同属一层，所以微软在这里的IP协议过滤是不包括ICMP协议的，也就是说即使你设置了“只允许TCP协议通过”，ICMP报文仍然可以正常通过，所以如果我们要过滤ICMP协议还需要另想办法。<br />刚刚在我们进行TCP/IP过滤时，还有另外一个选项：IP安全机制（IP Security），我们过滤ICMP的想法就要着落在它身上。<br />　　打开本地安全策略，选择IP安全策略，在这里我们可以定义自己的IP安全策略。<br />　　一个IP安全过滤器由两个部分组成：过滤策略和过滤*作，过滤策略决定哪些报文应当引起过滤器的关注，过滤*作决定过滤器是“允许”还是“拒绝”报文的通过。要新建IP安全过滤器，必须新建自己的过滤策略和过滤*作：右击本机的IP安全策略，选择管理IP过滤器，在IP过滤器管理列表中建立一个新的过滤规则：ICMP_ANY_IN，源地址选任意IP，目标地址选本机，协议类型是ICMP，切换到管理过滤器*作，增加一个名为Deny的*作，*作类型为"阻止"（Block）。这样我们就有了一个关注所有进入ICMP报文的过滤策略和丢弃所有报文的过滤*作了。需要注意的是，在地址选项中有一个镜像选择，如果选中镜像，那么将会建立一个对称的过滤策略，也就是说当你关注any IP-&gt;my IP的时候，由于镜像的作用，实际上你也同时关注了my IP-&gt;any IP，你可以根据自己的需要选择或者放弃镜像。<br />再次右击本机的IP安全策略，选择新建IP过滤策略，建立一个名称为ICMP Filter的过滤器，通过增加过滤规则向导，我们把刚刚定义的ICMP_ANY_IN过滤策略指定给ICMP Filter，然后在*作选框中选择我们刚刚定义的Deny*作，退出向导窗口，右击ICMP Filter并启用它，现在任何地址进入的ICMP报文都会被丢弃了。 <br />虽然用IP sec能够对ICMP报文进行过滤，不过*作起来太麻烦，而且如果你只需要过滤特定的ICMP报文，还要保留一些常用报文（如主机不可达、网络不可达等），IP sec策略就力不从心了，我们可以利用Win2000的另一个强大工具路由与远程访问控制（Routing &amp; Remote Access）来完成这些复杂的过滤*作。<br />路由与远程访问控制是Win2000用来管理路由表、配置VPN、控制远程访问、进行IP报文过滤的工具，默认情况下并没有安装，所以首先你需要启用它，打开"管理工具"-&gt;"路由与远程访问"，右击服务器（如果没有则需要添加本机）选择"配置并启用路由及远程访问"，这时配置向导会让你选择是什么样的服务器，一般来说，如果你不需要配置VPN服务器，那么选择"手动配置"就可以了，配置完成后，主机下将出现一个IP路由的选项，在"常规"中选择你想配置的网卡（如果你有多块网卡，你可以选择关闭某一块的ICMP），在网卡属性中点击"输入筛选器"，添加一条过滤策略"from:ANY　to:ANY　协议:ICMP　类型:8 :编码:0　丢弃"就可以了（类型8编码0就是Ping使用的ICMP_ECHO报文，如果要过滤所有的ICMP报文只需要将类型和编码都设置为255）<br />　<br />IIS：IIS是微软的组件中漏洞最多的一个，平均两三个月就要出一个漏洞，而微软的IIS默认安装又实在不敢恭维，所以IIS的配置是我们的重点，现在大家跟着我一起来： <br />首先，把C盘那个什么Inetpub目录彻底删掉，在D盘建一个Inetpub（要是你不放心用默认目录名也可以改一个名字，但是自己要记得）在IIS管理器中将主目录指向D:\Inetpub； <br />其次，那个IIS安装时默认的什么scripts等虚拟目录一概删除（罪恶之源呀，忘了http://www.target.com/scripts/..%c1%1c../winnt/system32/cmd.exe了？我们虽然已经把Inetpub从系统盘挪出来了，但是还是小心为上），如果你需要什么权限的目录可以自己慢慢建，需要什么权限开什么。（特别注意写权限和执行程序的权限，没有绝对的必要千万不要给）<br />第三，应用程序配置：在IIS管理器中删除必须之外的任何无用映射，必须指的是ASP, ASA和其他你确实需要用到的文件类型，例如你用到stml等（使用server side include），实际上90%的主机有了上面两个映射就够了，其余的映射几乎每个都有一个凄惨的故事：htw, htr, idq, ida……想知道这些故事？去查以前的漏洞列表吧。什么？找不到在哪里删？在IIS管理器中右击主机-&gt;属性-&gt;WWW服务 编辑-&gt;主目录 配置-&gt;应用程序映射，然后就开始一个个删吧（里面没有全选的，嘿嘿）。接着在刚刚那个窗口的应用程序调试书签内将脚本错误消息改为发送文本（除非你想ASP出错的时候用户知道你的程序/网络/数据库结构）错误文本写什么？随便你喜欢，自己看着办。点击确定退出时别忘了让虚拟站点继承你设定的属性。 <br />为了对付日益增多的cgi漏洞扫描器，还有一个小技巧可以参考，在IIS中将HTTP404 Object Not Found出错页面通过URL重定向到一个定制HTM文件，可以让目前绝大多数CGI漏洞扫描器失灵。其实原因很简单，大多数CGI扫描器在编写时为了方便，都是通过查看返回页面的HTTP代码来判断漏洞是否存在的，例如，著名的IDQ漏洞一般都是通过取1.idq来检验，如果返回HTTP200，就认为是有这个漏洞，反之如果返回HTTP404就认为没有，如果你通过URL将HTTP404出错信息重定向到HTTP404.htm文件，那么所有的扫描无论存不存在漏洞都会返回HTTP200，90%的CGI扫描器会认为你什么漏洞都有，结果反而掩盖了你真正的漏洞，让入侵者茫然无处下手（武侠小说中常说全身漏洞反而无懈可击，难道说的就是这个境界？）不过从个人角度来说，我还是认为扎扎实实做好安全设置比这样的小技巧重要的多。 <br />最后，为了保险起见，你可以使用IIS的备份功能，将刚刚的设定全部备份下来，这样就可以随时恢复IIS的安全配置。还有，如果你怕IIS负荷过高导致服务器满负荷死机，也可以在性能中打开CPU限制，例如将IIS的最大CPU使用率限制在70%。<br />需要注意事项<br />实际上，安全和应用在很多时候是矛盾的，因此，你需要在其中找到平衡点，毕竟服务器是给用户用而不是做OPEN HACK的，如果安全原则妨碍了系统应用，那么这个安全原则也不是一个好的原则。 <br />网络安全是一项系统工程，它不仅有空间的跨度，还有时间的跨度。很多朋友（包括部分系统管理员）认为进行了安全配置的主机就是安全的，其实这其中有个误区：我们只能说一台主机在一定的情况一定的时间上是安全的，随着网络结构的变化、新的漏洞的发现，管理员/用户的*作，主机的安全状况是随时随地变化着的，只有让安全意识和安全制度贯穿整个过程才能做到真正的安全。 <br />提高IIS 5.0网站伺服器的执行效率的八种方法 <br />　以下是提高IIS 5.0网站伺服器的执行效率的八种方法：<br />　1. 启用HTTP的持续作用可以改善15~20%的执行效率。<br />　2. 不启用记录可以改善5~8%的执行效率。<br />　3. 使用 [独立] 的处理程序会损失20%的执行效率。<br />　4. 增加快取记忆体的保存档案数量，可提高Active Server Pages之效能。<br />　5. 勿使用CGI程式。<br />　6. 增加IIS 5.0电脑CPU数量。<br />　7. 勿启用ASP侦错功能。<br />　8. 静态网页采用HTTP 压缩，大约可以减少20%的传输量。<br />　简单介绍如下。 　1、启用HTTP的持续作用<br />　启用HTTP的持续作用（Keep-Alive）时，IIS与浏览器的连线不会断线，可以改善执行效率，直到浏览器关闭时连线才会断线。因为维持「Keep-Alive」状态时，於每次用户端请求时都不须重新建立一个新的连接，所以将改善伺服器的效率。<br />　此功能为HTTP 1.1预设的功能，HTTP 1.0加上Keep-Alive <br />header也可以提供HTTP的持续作用功能。<br />　2、启用HTTP的持续作用可以改善15~20%的执行效率。<br />　如何启用HTTP的持续作用呢？步骤如下：<br />　在 [Internet服务管理员] 中，选取整个IIS电脑、或Web站台，於 [内容] 之 [主目录] <br />页，勾选 [HTTP的持续作用] 选项。<br />　3、不启用记录<br />　不启用记录可以改善5~8%的执行效率。<br />　如何设定不启用记录呢？步骤如下：<br />　在 [Internet服务管理员] 中，选取整个IIS电脑、或Web站台，於 [内容] 之 [主目录]页，不勾选 [启用记录] 选项。<br />　设定非独立的处理程序<br />　使用 [独立] 的处理程序会损失20%的执行效率，此处所谓「独立」系指将 [主目录]、[虚拟目录] 页之应用程式保护选项设定为 [高（独立的）] 时。因此 [应用程式保护] 设定为 [低 (IIS处理程序)] 时执行效率较高，设定画面如下：<br />　如何设定非「独立」的处理程序呢？步骤如下：<br />　在 [Internet服务管理员] 中，选取整个IIS电脑、Web站台、或应用程式的起始目录。於 [内容] 之 [主目录]、[虚拟目录] 页，设定应用程式保护选项为 [低 (IIS处理程序)] 。<br />　4、调整快取（Cache）记忆体<br />　IIS 5.0将静态的网页资料暂存於快取（Cache）记忆体当中；IIS <br />4.0则将静态的网页资料暂存於档案当中。调整快取（Cache）记忆体的保存档案数量可以改善执行效率。<br />　ASP指令档案执行过後，会在暂存於快取（Cache）记忆体中以提高执行效能。增加快取记忆体的保存档案数量，可提高Active Server Pages之效能。<br />　可以设定所有在整个IIS电脑、「独立」Web站台、或「独立」应用程式上执行之应用程式的快取记忆体档案数量。<br />　如何设定快取（Cache）功能呢？步骤如下：<br />　在 [Internet服务管理员] <br />中，选取整个IIS电脑、「独立」Web站台、或「独立」应用程式的起始目录。於 [内容]之[主目录]、[虚拟目录] 页，按下 [设定] 按钮时，即可由 [处理程序选项] 页设定 [指令档快取记忆体] 。<br />如何设定快取（Cache）记忆体档案数量呢？步骤如下：<br />在 [Internet服务管理员] 中，选取整个IIS电脑、或Web站台的起始目录。於 [内容]之[伺服器扩充程式] 页，按下 [设定] 按钮。<br />　即可设定快取（Cache）记忆体档案数量。<br />　5、勿使用CGI程式<br />　使用CGI程式时，因为处理程序（Process）须不断地产生与摧毁，造成执行效率不佳。<br />　一般而言，执行效率比较如下：<br />　 　 静态网页（Static）：100<br />　 　 ISAPI：50<br />　 　 ASP：10<br />　 　 CGI：1<br />　换句话说，ASP比CGI可能快10倍，因此勿使用CGI程式可以改善IIS的执行效率。<br />　以弹性（Flexibility）而言：ASP &gt; CGI &gt; ISAPI &gt; 静态网页（Static）。<br />　以安全（Security）而言：ASP（独立） = ISAPI（独立）= CGI &gt; ASP（非独立） = <br />ISAPI（非独立）= 静态网页（Static）。<br />　6、增加IIS 5.0电脑CPU数量<br />　根据微软的测试报告，增加IIS 4.0电脑CPU数量，执行效率并不会改善多少；但是增加IIS 5.0电脑CPU数量，执行效率会几乎成正比地提供，换句话说，两颗CPU的IIS 5.0电脑执行效率几乎是一颗CPU电脑的两倍，四颗CPU的IIS 5.0电脑执行效率几乎是一颗CPU电脑的四倍。<br />　IIS 5.0将静态的网页资料暂存於快取（Cache）记忆体当中；IIS <br />4.0则将静态的网页资料暂存於档案当中。调整快取（Cache）记忆体的保存档案数量可以改善执行效率。<br />　7、启用ASP侦错功能<br />　勿启用ASP侦错功能可以改善执行效率。<br />　如何勿启用ASP侦错功能呢？步骤如下：<br />　於 [Internet服务管理员] 中，选取Web站台、或应用程式的起始目录，按右键选择 [内容]，按 [主目录]、[虚拟目录] 或 [目录] 页，按下 [设定] 按钮，选择 [应用程式侦错] 页，不勾选 [启用ASP伺服器端指令侦错]、[启用ASP用户端指令侦错] 选项。<br />　8、静态网页采用HTTP 压缩<br />　静态网页采用HTTP 压缩，大约可以减少20%的传输量。<br />　HTTP压缩功能启用或关闭，系针对整台IIS伺服器来设定。<br />　用户端使用IE 5.0浏览器连线到已经启用HTTP压缩IIS 5.0之Web伺服器，才有HTTP压缩功能。<br />　如何启用HTTP压缩功能呢？步骤如下：<br />　若要启用HTTP压缩功能，方法为在 [Internet服务管理员] 中，选取电脑之 [内容]，於 [主要内容] <br />之下选取 [WWW服务]。然後按一下 [编辑] 按钮，於 [服务] 页上，选取 [压缩静态档案] 可以压缩静态档案，不选取 [压缩应用程式档案] 。<br />　动态产生的内容档案（压缩应用程式档案）也可以压缩，但是须耗费额外CPU处理时间，若% Processor Time已经百分之八十或更多时，建议不要压缩。 <img src ="http://www.cppblog.com/eday/aggbug/15387.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eday/" target="_blank">松*</a> 2006-11-18 16:59 <a href="http://www.cppblog.com/eday/archive/2006/11/18/15387.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>修改远程桌面端口</title><link>http://www.cppblog.com/eday/archive/2006/11/18/15388.html</link><dc:creator>松*</dc:creator><author>松*</author><pubDate>Sat, 18 Nov 2006 08:59:00 GMT</pubDate><guid>http://www.cppblog.com/eday/archive/2006/11/18/15388.html</guid><description><![CDATA[
		<div class="item-content">
				<p>具体操作如下：</p>
				<p>在终端服务器上做如下修改 <br />KEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\ Control\Terminal Server\Wds\rdpwd\Tds\tcp<br />和<br />HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp<br />下都有一个PortNumber值，通常为3389，将其修改为自己的值，如9876(可自己指定端口，但最好不要设为低端端口，以免冲突)； <br /> <br />2.重新启动服务器。  </p>
				<p>在客户端做如下改动  </p>
				<p>1. 打开客户端连接管理器；  <br />2. 输入主机地址，后面跟端口号，例如: 202.100.4.15:9876</p>
				<div class="clear">
				</div>
		</div>
<img src ="http://www.cppblog.com/eday/aggbug/15388.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eday/" target="_blank">松*</a> 2006-11-18 16:59 <a href="http://www.cppblog.com/eday/archive/2006/11/18/15388.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关闭Windows XP相关端口保安全</title><link>http://www.cppblog.com/eday/archive/2006/11/18/15386.html</link><dc:creator>松*</dc:creator><author>松*</author><pubDate>Sat, 18 Nov 2006 08:58:00 GMT</pubDate><guid>http://www.cppblog.com/eday/archive/2006/11/18/15386.html</guid><description><![CDATA[
		<div class="item-content">
				<a name="part">
						<u>　　Windows XP作为一个被广泛使用的系统，现在已经受到了越来越多攻击者的“青睐”。当然最简单的防范方法是装个网络防火墙，不过在没有防火墙时，我们有什么办法呢？关闭Windows XP中的无用端口可以让系统安全很多。 <br /><br />　　<b>一、找出自身开放的端口</b><br /><br />　　扫描端口，然后找漏洞是攻击者入侵的基本思路。可以说，机器上开放的端口越多，攻击者入侵的机会就越大，因此我们可以通过关闭一些我们不用的端口来提高电脑的安全性。 <br /><br />　　那如何知道我们的Windows XP开放了哪些端口呢？我们可以用命令“Netstat”来查看系统中开放的端口。 <br /><br />　　我们需要用到这个命令的两个参数：-a、-n。参数-a显示当前所有连接和侦听端口，而参数-n以数字格式显示地址和端口号（而不是尝试查找名称），两者可以结合起来使用：Netstatan（如图1），就能查看当前端口的开放情况。 <br /><br /></u>
				</a>
				<center>
						<a name="part">
								<u>
										<img style="DISPLAY: block; MARGIN: 0px auto 10px; TEXT-ALIGN: center" alt="10b347ce545.jpg" src="http://img3.pp.sohu.com/images/2006/4/30/14/14/10b347ce545.jpg" border="0" />
								</u>
						</a>
				</center>
				<p>
						<br />
						<br />　　图1 <br /><br />　　通过这个命令，如果我们发现一个异常的端口号在监听，可以先去网上查找常见木马的端口号对照一下，如果发现有木马使用的端口，就应该用杀除木马的软件检查系统了。</p>
				<p>
						<a name="part">
								<u>　　<b>二、关闭无用端口</b><br /><br />　　知道怎么查看机器的端口情况之后，接下来一个问题是，哪些端口是必需的，哪些端口是可以关闭的？这个问题稍微复杂一点，因为除了Windows XP默认开放的135、137、138、139和445，有些跟网络有关的软件需要使用到一些端口，最常用的比如QQ使用4000端口。这里笔者把情况想像成最简单：一台只需要浏览网页的电脑。那么针对这个系统，我们自己来配置一下以提高安全性。 <br /><br />　　1、关闭软件开启的端口。可以打开本地连接的“属性→Internet协议（TCP/IP）→属性→高级→选项→TCP/IP筛选属性”，然后都选上“只允许”（如图2）。请注意，如果发现某个常用的网络工具不能起作用的时候，请搞清它在你主机所开的端口，然后在“TCP/IP筛选”中添加相应的端口。 <br /><br /></u>
						</a>
				</p>
				<center>
						<u>
								<img style="DISPLAY: block; MARGIN: 0px auto 10px; TEXT-ALIGN: center" alt="10b347f19d3.jpg" src="http://img3.pp.sohu.com/images/2006/4/30/14/17/10b347f19d3.jpg" border="0" />
						</u>
				</center>
				<p>
						<br />
						<br />　　图2 <br /><br />　　2、禁用NetBIOS。打开本地连接的“属性→Internet协议（TCP/IP）→属性→高级→WINS→禁用TCP/IP上的NetBIOS”（如图3）。这样一来就关闭了137、138以及139端口，从而预防IPC$入侵。 <br /><br /></p>
				<center>
						<img style="DISPLAY: block; MARGIN: 0px auto 10px; TEXT-ALIGN: center" alt="10b347fab75.jpg" src="http://img3.pp.sohu.com/images/2006/4/30/14/17/10b347fab75.jpg" border="0" />
				</center>
				<p>
						<br />
						<br />　　图3 <br /><br />　　3、开启Windows XP自带的网络防火墙。打开本地连接的“属性→高级”，启用防火墙之后，单击设置可以设置系统开放关闭哪些服务。一般来说，这些服务都可以不要，关闭这些服务后，这些服务涉及的端口就不会被轻易打开了。 <br /><br />　　4、禁用445端口。向注册表“HKEY_LO-CAL_MACHINE\SYSTEM\CurrentControlSet\Servi ces\NetBT\Parameters”中追加名为“SMBDeviceEnabled”的DWORD值，并将其设置为0，就好了。 <br /><br />　　通过以上设置，你的Windows XP系统的安全性将大大提高。要补充的是，文章是针对那些直接拨号上网的机器，而不包括通过网关代理上网的机器。 <br /></p>
				<div class="clear">
				</div>
		</div>
<img src ="http://www.cppblog.com/eday/aggbug/15386.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eday/" target="_blank">松*</a> 2006-11-18 16:58 <a href="http://www.cppblog.com/eday/archive/2006/11/18/15386.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏外挂设计技术探讨-四</title><link>http://www.cppblog.com/eday/archive/2006/11/18/15383.html</link><dc:creator>松*</dc:creator><author>松*</author><pubDate>Sat, 18 Nov 2006 08:55:00 GMT</pubDate><guid>http://www.cppblog.com/eday/archive/2006/11/18/15383.html</guid><description><![CDATA[
		<div class="item-content">1）我们要起动WSA，这时个要用到的WSAStartup函数，用法如下： 
<p><br />　　 2）使用socket函数得到socket句柄，m<a href="http://hackbase.com/game/game/2005022010070_3.html#" target="_blank"><font color="#000000">_</font></a>hSocket:=Socket(AF<a href="http://hackbase.com/game/game/2005022010070_3.html#" target="_blank"><font color="#000000">_</font></a>INET, SOCK<a href="http://hackbase.com/game/game/2005022010070_3.html#" target="_blank"><font color="#000000">_</font></a>RAW, IPPROTO<a href="http://hackbase.com/game/game/2005022010070_3.html#" target="_blank"><font color="#000000">_</font></a>IP); 用法如下：</p><p><br />　　在程序里m<a href="http://hackbase.com/game/game/2005022010070_3.html#" target="_blank"><font color="#000000">_</font></a>hSocket为socket句柄，AF<a href="http://hackbase.com/game/game/2005022010070_3.html#" target="_blank"><font color="#000000">_</font></a>INET，SOCK<a href="http://hackbase.com/game/game/2005022010070_3.html#" target="_blank"><font color="#000000">_</font></a>RAW，IPPROTO<a href="http://hackbase.com/game/game/2005022010070_3.html#" target="_blank"><font color="#000000">_</font></a>IP均为常量。</p><p>　　3)定义SOCK<a href="http://hackbase.com/game/game/2005022010070_3.html#" target="_blank"><font color="#000000">_</font></a>ADDR类型，跟据我们的网卡IP给Sock<a href="http://hackbase.com/game/game/2005022010070_3.html#" target="_blank"><font color="#000000">_</font></a>ADDR类型附值，然后我们使用bind函数来绑定我们的网卡，Bind函</p><p>数用法如下：</p><p>4)用WSAIoctl来注册WSA的输入输出组件，其用法如下：</p><p><br />　　5)下面做死循环，在死循环块里，来实现数据的接收。但是徇环中间要用Sleep()做延时，不然程序会出错。</p><p>　　6)在循环块里，用recv函数来接收数据，recv函数用法如下：</p><p><br />　　7)在buffer里就是我们接收回来的数据了，如果我们想要知道数据是什么地方发来的，那么，我们要定义一定IP包结</p><p>构，用CopyMemory()把IP信息从buffer里面读出来就可以了，不过读出来的是十六进制的数据需要转换一下。</p><p>　　看了封包捕获的全过程序，对你是不是有点起发，然而在这里要告诉大家的是封包的获得是很容易的，但是许多<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a></p><p>的封包都是加密的，如果你想搞清楚所得到的是什么内容还需要自己进行封包解密。 </p><p><br />四种网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂的设计方法</p><p>[文章导读] <br /> <br />在几年前我看到别人玩网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>用上了外挂，做为程序员的我心里实在是不爽 </p><p><br /> </p><p>在几年前我看到别人玩网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>用上了外挂，做为程序员的我心里实在是不爽，想搞清楚这到底是怎么回事。就拿了一些</p><p>来<a href="http://hackbase.com/hacker/leak" target="_blank"><font color="#000000">研究</font></a>，小有心得，拿出来与大家共享，外挂无非就是分几种罢了（依制作难度）：</p><p><br />　1、动作式，所谓动作式，就是指用API发命令给窗口或API控制鼠标、键盘等，使<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>里的<a href="http://hackbase.com/gest/hackbase" target="_blank"><font color="#000000">人物</font></a>进行流动或者<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">攻击</font></a>，最</p><p>早以前的“石器”外挂就是这种方式。（这种外挂完全是垃圾，TMD，只要会一点点API的人都知道该怎么做，不过这种外</p><p>挂也是<a href="http://hackbase.com/hacker/hacker" target="_blank"><font color="#000000">入门</font></a>级的好东东，虽然不能提高你的战斗力，但是可以提高你的士气）</p><p>　　2、本地修改式，这种外挂跟传统上的一些<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>修改器没有两样，做这种外挂在<a href="http://hackbase.com/hacker/program" target="_blank"><font color="#000000">编程</font></a>只需要对内存地址有一点认识并</p><p>且掌握API就可以实现，“精灵”的外挂这是这种方式写成的，它的难点在于找到那些地址码，找地址一般地要借助于别</p><p>人的<a href="http://hackbase.com/hacker/tool" target="_blank"><font color="#000000">工具</font></a>，有的<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>还有双码校验，正正找起来会比较困难。（这种外挂，比上一种有一点点难度，但是这种外挂做起来</p><p>能够用，也是有一定难度的啦~~，这种外挂可以很快提升你对内存地址的理解及应用，是你<a href="http://hackbase.com/hacker/program" target="_blank"><font color="#000000">编程</font></a><a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>提高的好东东）</p><p>　　3、<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">木马</font></a>式，这种外挂的目的是帮外挂制作者偷到用户的<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">密码</font></a>（TMD，“烂”就一个字，不过要知已知彼所以还是要谈</p><p>一下啦~~），做这种外挂有一定的难度，需要HOOK或键盘监视<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>做底子，才可以完成，它的原理是先首截了用户的帐号</p><p>或<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">密码</font></a>，然后发到指定邮箱。（我以前写过这样的东东，但是从来没有用过，我知道这种东东很不道德，所以以后千万别</p><p>用呀！）</p><p>　　4、加速式，这种外挂可以加快<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>的速度……（对不起大家，这种东东我没有实际做过，所以不能妄自评，惭愧）</p><p>　　这几种外挂之中，前三种可以用VB，Delphi等语言比较好实现，后两种则要用VC等底层支持比较好的<a href="http://hackbase.com/hacker/program" target="_blank"><font color="#000000">编程</font></a><a href="http://hackbase.com/hacker/tool" target="_blank"><font color="#000000">工具</font></a>才好实</p><p>现。 </p><p>　　动作式外挂</p><p>　　首先，先来谈一下动作式的外挂，这也是我第一次写外挂时做的最简单的一种。</p><p>　　记得还在“石器”时代的时候，我看到别人挂着一种<a href="http://down.hackbase.com/" target="_blank"><font color="#000000">软件</font></a>（外挂）<a href="http://hackbase.com/gest/hackbase" target="_blank"><font color="#000000">人物</font></a>就可以四外游走（当时我还不知道外挂怎么回</p><p>事），于是找了这种<a href="http://down.hackbase.com/" target="_blank"><font color="#000000">软件</font></a>过来<a href="http://hackbase.com/hacker/leak" target="_blank"><font color="#000000">研究</font></a>（拿来后才听别人说这叫外挂），发现这种东东其实实现起来并不难，仔佃看其实<a href="http://hackbase.com/gest/hackbase" target="_blank"><font color="#000000">人物</font></a></p><p>的行走无非就是鼠标在不同的地方点来点去而已，看后就有实现这功能的冲动，随后跑到MSDN上看了一些资料，发现这种</p><p>实现这几个功能，只需要几个简单的API函数就可以搞定：</p><p>　　1、首先我们要知道现在鼠标的位置（为了好还原现在鼠标的位置）所以我们就要用到API函数GetCursorPos，它的使</p><p>用方法如下：</p><p><br />　　 2、我们把鼠标的位置移到要到<a href="http://hackbase.com/gest/hackbase" target="_blank"><font color="#000000">人物</font></a>走到的地方，我们就要用到SetCursorPos函数来移动鼠标位置，它的使用方法如</p><p>下：</p><p><br />　　3、模拟鼠标发出按下和放开的动作，我们要用到mouse<a href="http://hackbase.com/game/game/2005022010070_3.html#" target="_blank"><font color="#000000">_</font></a>event函数来实现，具休使用方法用下：</p><p><br />　　在它的dwFlags处，可用的事件很多如移动MOUSEEVENTF<a href="http://hackbase.com/game/game/2005022010070_3.html#" target="_blank"><font color="#000000">_</font></a>MOVE，左键按下MOUSEEVENTF<a href="http://hackbase.com/game/game/2005022010070_3.html#" target="_blank"><font color="#000000">_</font></a>LEFTDOWN，左键放开</p><p>MOUSEEVENTF<a href="http://hackbase.com/game/game/2005022010070_3.html#" target="_blank"><font color="#000000">_</font></a>LEFTUP，具体的东东还是查一下MSDN吧~~~~~</p><p>　　 好了，有了前面的<a href="http://hackbase.com/network/zs" target="_blank"><font color="#000000">知识</font></a>，我们就可以来看看<a href="http://hackbase.com/gest/hackbase" target="_blank"><font color="#000000">人物</font></a>移走是怎么实现的了：</p><p><br />　　看了以上的<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>，是不是觉得<a href="http://hackbase.com/gest/hackbase" target="_blank"><font color="#000000">人物</font></a>的游走很简单啦~~，举一仿三，还有好多好东东可以用这个<a href="http://hackbase.com/network/network" target="_blank"><font color="#000000">技巧</font></a>实现（我早就说过</p><p>，TMD，这是垃圾外挂的做法，相信了吧~~~），接下来，再看看<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>里面自动<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">攻击</font></a>的做法吧（必需<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>中<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">攻击</font></a>支持快捷键</p><p>的），道理还是一样的，只是用的API不同罢了~~~，这回我们要用到的是keybd<a href="http://hackbase.com/game/game/2005022010070_3.html#" target="_blank"><font color="#000000">_</font></a>event函数，其用法如下：</p><p> </p><p>　　我们还要知道扫描码不可以直接使用，要用函数MapVirtualKey把键值转成扫描码，MapVirtualKey的具体使用方法如</p><p>下：</p><p><br />　　好了，比说此快接键是CTRL+A，接下来让我们看看实际<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>是怎么写的：</p><p><br />　　首先模拟按下了CTRL键，再模拟按下A键，再模拟放开A键，最后放开CTRL键，这就是一个模拟按快捷键的周期。</p><p>　　（看到这里，差不多对简易外挂有了一定的了解了吧~~~~做一个试试？如果你举一仿三还能有更好的东东出来，这就</p><p>要看你的领悟能力了~~，不过不要高兴太早这只是才开始，以后还有更复杂的东东等着你呢~~）</p><p><br />本地修改式外挂</p><p>　　现在我们来看看，比动作式外挂更进一步的外挂——本地修改式外挂的整个制作过程进行一个详细的分解。</p><p>　　具我所知，本地修改式外挂最典型的应用就是在“精灵”<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>上面，因为我在近一年前（“精灵”还在测试阶段），</p><p>我所在的公司里有很多同事玩“精灵”，于是我看了一下<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>的数据处理方式，发现它所发送到<a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a>器上的信息是存在于</p><p>内存当中（我看后第一个感受是：修改这种<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>和修改单机版的<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>没有多大分别，换句话说就是在他向<a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a>器提交信息</p><p>之前修改了内存地址就可以了），当时我找到了地址于是修改了内存地址，果然，按我的想法修改了地址，让系统自动提</p><p>交后，果然成功了~~~~~，后来“精灵”又改成了双地址校检，内存校检等等，在这里我就不废话了~~~~，OK，我们就来</p><p>看看这类外挂是如何制作的：</p><p>　　在做外挂之前我们要对Windows的内存有个具体的认识，而在这里我们所指的内存是指系统的内存偏移量，也就是相</p><p>对内存，而我们所要对其进行修改，那么我们要对几个Windows API进行了解，OK，跟着例子让我们看清楚这种外挂的制</p><p>作和API的应用（为了保证网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>的正常运行，我就不把找内存地址的方法详细解说了）：</p><p>　　1、首先我们要用FindWindow,知道<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>窗口的句柄，因为我们要通过它来得知<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>的运行后所在进程的ID，下面就是</p><p>FindWindow的用法：</p><p><br />　　2、我们GetWindowThreadProcessId来得到<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>窗口相对应进程的进程ID，函数用法如下：</p><p>DWORD GetWindowThreadProcessId(</p><p>HWND hWnd, // handle of window<br />LPDWORD lpdwProcessId // address of variable for process identifier<br />);</p><p>　　3、得到<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>进程ID后，接下来的事是要以最高权限打开进程，所用到的函数OpenProcess的具体使用方法如下：</p><p><br />　　在dwDesiredAccess之处就是设存取方式的地方，它可设的权限很多，我们在这里使用只要使用PROCESS<a href="http://hackbase.com/game/game/2005022010070_3.html#" target="_blank"><font color="#000000">_</font></a>ALL<a href="http://hackbase.com/game/game/2005022010070_3.html#" target="_blank"><font color="#000000">_</font></a>ACCESS </p><p>来打开进程就可以，其他的方式我们可以查一下MSDN。</p><p>　　4、打开进程后，我们就可以用函数对存内进行操作，在这里我们只要用到WriteProcessMemory来对内存地址写入数</p><p>据即可（其他的操作方式比如说：ReadProcessMemory等，我在这里就不一一介绍了），我们看一下WriteProcessMemory</p><p>的用法：</p><p><br />　　5、下面用CloseHandle关闭进程句柄就完成了。</p><p>　　这就是这类<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂的程序实现部份的方法，好了，有了此方法，我们就有了理性的认识，我们看看实际例子，提升</p><p>一下我们的感性认识吧，下面就是XX<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>的外挂<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>，我们照上面的方法对应去<a href="http://hackbase.com/hacker/leak" target="_blank"><font color="#000000">研究</font></a>一下吧：</p><p><br />　　这个<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>是用了多地址对所要提交的数据进行了校验，所以说这类<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂制作并不是很难，最难的是要找到这些地</p><p>址。</p><p><br /><a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">木马</font></a>式外挂</p><p>　　<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">木马</font></a>式外挂，可能大多像<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">木马</font></a>吧，是帮助做外挂的人偷取别人<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>的帐号及<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">密码</font></a>的东东。因为网络上有此类外挂的存</p><p>在，所以今天不得不说一下（我个人是非常讨厌这类外挂的，请看过本文的朋友不要到处乱用此<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>，谢谢合作）。要做</p><p>此类外挂的程序实现方法很多（比如HOOK，键盘监视等<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>），因为HOOK<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>对程序员的<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>要求比较高并且在实际应用</p><p>上需要多带一个<a href="http://hackbase.com/News/industry" target="_blank"><font color="#000000">动态</font></a>链接库，所以在文中我会以键盘监视<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>来实现此类<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">木马</font></a>的制作。键盘监视<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>只需要一个.exe文件</p><p>就能实现做到后台键盘监视，这个程序用这种<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>来实现比较适合。</p><p>　　在做程序之前我们必需要了解一下程序的思路：</p><p>　　1、我们首先知道你想记录<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>的登录窗口名称。</p><p>　　2、判断登录窗口是否出现。</p><p>　　3、如果登录窗口出现，就记录键盘。</p><p>　　4、当窗口关闭时，把记录信息，通过邮件发送到程序设计者的邮箱。</p><p>　　第一点我就不具体分析了，因为你们比我还要了解你们玩的是什么<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>，登录窗口名称是什么。从第二点开始，我们</p><p>就开始这类外挂的程序实现之旅：</p><p>　　那么我们要怎么样判断登录窗口虽否出现呢？其实这个很简单，我们用FindWindow函数就可以很轻松的实现了：</p><p><br />　　实际程序实现中，我们要找到'xx'窗口，就用FindWindow(nil,'xx')如果当返回值大于0时表示窗口已经出现，那么</p><p>我们就可以对键盘信息进行记录了。</p><p>　　先首我们用SetWindowsHookEx设置监视日志，而该函数的用法如下：</p><p><br />　　在这里要说明的是在我们程序当中我们要对HOOKPROC这里我们要通过写一个函数，来实现而HINSTANCE这里我们直接</p><p>用本程序的HINSTANCE就可以了，具体实现方法为：</p><p><br />　　而HOOKPROC里的函数就要复杂一点点：</p><p><br />　　以上就是记录键盘的整个过程，简单吧，如果记录完可不要忘记释放呀，UnHookWindowsHookEx(hHook)，而hHOOK,就</p><p>是创建setwindowshookex后所返回的句柄。</p><p>　　我们已经得到了键盘的记录，那么现在最后只要把记录的这些信息发送回来，我们就大功造成了。其他发送这块并不</p><p>是很难，只要把记录从文本文件里边读出来，用DELPHI自带的电子邮件组件发一下就万事OK了。<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>如下：</p><p><br />　　这个程序全部功能已经实现，编编试试。</p><p><br />加速型外挂</p><p>　　原本我一直以为加速外挂是针对某个<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>而写的，后来发现我这种概念是不对的，所谓加速外挂其实是修改时钟频率</p><p>达到加速的目的。</p><p>　　以前DOS时代玩过<a href="http://hackbase.com/hacker/program" target="_blank"><font color="#000000">编程</font></a>的人就会马上想到，这很简单嘛不就是直接修改一下8253寄存器嘛，这在以前DOS时代可能可以</p><p>行得通，但是windows则不然。windows是一个32位的操作系统，并不是你想改哪就改哪的（微软的东东就是如此霸气，说</p><p>不给你改就不给你改），但要改也不是不可能，我们可以通过两种方法来实现：第一是写一个硬件驱动来完成，第二是用</p><p>Ring0来实现（这种方法是CIH的作者陈盈豪首用的，它的原理是修改一下IDE表-&gt;创建一个中断门-&gt;进入Ring0-&gt;调用中断</p><p>修改向量，但是没有办法只能用ASM汇编来实现这一切*<a href="http://hackbase.com/game/game/2005022010070_3.html#" target="_blank"><font color="#000000">_</font></a>*，做为高级语言使用者惨啦！），用第一种方法用点麻烦，所以</p><p>我们在这里就用第二种方法实现吧~~~</p><p>　　在实现之前我们来理一下思路吧：</p><p>　　1、我们首先要写一个过程在这个过程里嵌入汇编语言来实现修改IDE表、创建中断门，修改向量等工作</p><p>　　2、调用这个过程来实现加速功能</p><p>　　好了，现在思路有了，我们就边看<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>边讲解吧：</p><p>　　首先我们建立一个过程，这个过程就是本程序的核心部份：</p><p><br />　　最核心的东西已经写完了，大部份读者是知其然不知其所以然吧，呵呵，不过不知其所以然也然。下面我们就试着用</p><p>一下这个过程来做一个类似于“变速齿轮”的一个东东吧！</p><p>　　先加一个窗口，在窗口上放上一个trackbar控件把其Max设为20，Min设为1，把Position设为10，在这个控件的</p><p>Change事件里写上：</p><p><br />　　因为windows默认的值为$1742，所以我们把1742做为基数，又因为值越小越快，反之越慢的原理，所以写了这样一个</p><p>公式，好了，这就是“变速齿轮”的一个Delphi＋ASM版了（只适用于win9X），呵呵，试一下吧，这对你帮助会很大的，</p><p>呵呵。</p><p>　　在win2000里，我们不可能实现在直接对端口进行操作，Ring0也失了效，有的人就会想到，我们可以写驱动程序来完</p><p>成呀，但在这里我告诉你，windows2000的驱动不是一个VxD就能实现的，像我这样的低手是写不出windows所用的驱动WDM</p><p>的，没办法，我只有借助外力实现了，ProtTalk就是一个很好的设备驱动，他很方便的来实现对低层端口的操作，从而实</p><p>现加速外挂。</p><p>　　1、我们首先要下一个PortTalk驱动，他的官方网站是<a href="http://www.beyondlogic.org/"><font color="#000000">http://www.beyondlogic.org/</font></a></p><p>　　2、我们要把里面的prottalk.sys拷贝出来。</p><p>　　3、建立一个Protalk.sys的接口（我想省略了，大家可以上<a href="http://www.freewebs.com/liuyue/porttalk.pas"><font color="#000000">http://www.freewebs.com/liuyue/porttalk.pas</font></a>下个pas </p><p>文件自己看吧）</p><p>　　4、实现加速外挂。</p><p>　　下面就讲一下这程序的实现方法吧，如果说用ProtTalk来操作端口就容易多了，比win98下用ring权限操作方便。</p><p>　　1、新建一个工程，把刚刚下的接口文件和Protalk.sys一起拷到工程文件保存的文件夹下。</p><p>　　2、我们在我们新建的工程加入我们的接口文件</p><p><br />　　3、我们建立一个过程</p><p><br />　　4、先加一个窗口，在窗口上放上一个trackbar控件把其Max设为20，Min设为1，把Position设为10，在这个控件的</p><p>Change事件里写上：</p><p><br />　　就这么容易。</p><p><br />在内存中修改数据的网游外挂</p><p>[文章导读] <br /> <br />现在很多<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>都是把一些信息存入内存单元的，那么我们只需要修改具体内存值就能修改<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>中的属性，很多网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>也</p><p>不外于此 </p><p><br /> </p><p>　　现在很多<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>都是把一些信息存入内存单元的，那么我们只需要修改具体内存值就能修改<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>中的属性，很多网络游</p><p>戏也不外于此。</p><p><br />　　曾几何时，一些网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>也是可以用内存外挂进行修改的，后来被发现后，这些<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>就把单一内存地址改成多内存地</p><p>址校验，加大了修改难度，不过仍然可以通过内存分析器可以破解的。诸如“FPE”这样的<a href="http://down.hackbase.com/" target="_blank"><font color="#000000">软件</font></a>便提供了一定的内存分析</p><p>功能。</p><p>　　“FPE”是基于内存外挂的佼佼者，是家喻户晓的<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>修改<a href="http://down.hackbase.com/" target="_blank"><font color="#000000">软件</font></a>。很多同类的<a href="http://down.hackbase.com/" target="_blank"><font color="#000000">软件</font></a>都是模仿“FPE”而得到玩家的认可</p><p>。而“FPE”实现的<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>到现在都没有公开，很多人只能够通过猜测“FPE”的实现方法，实现同类外挂。笔者也曾经模仿</p><p>过“FPE”实现相应的功能，如“内存修改”、“内存查询”等<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>。稍后会对此<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>进行剖析。</p><p>　　既然要做内存外挂，那么就必须对Windows的内存机制有所了解。<a href="http://hackbase.com/skill" target="_blank"><font color="#000000">计算机</font></a>的内存(RAM)总是不够用的，在操作系统中内</p><p>存就有物理内存和虚拟内存之分，因为程序创建放入物理内存的地址都是在变化的，所以在得到<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>属性时并不能够直接</p><p>访问物理内存地址。在v86模式下，段寄存器使用方法与实模式相同，那么可以通过段寄存器的值左移4位加上地址偏移量</p><p>就可以得到线性地址，而程序创建时在线性地址的中保留4MB-2GB的一段地址，<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>中属性便放于此。在windows中把虚拟</p><p>内存块称之为页，而每页为4KB，在访问内存时读取<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>属性时，为了不破坏数据完整性的快速浏览内存地址值，最好一</p><p>次访问一页。</p><p>　　在操作进程内存时，不需要再使用汇编语言，Windows中提供了一些访问进程内存空间的API，便可以直接对进程内存</p><p>进行操作。但初学者一般掌握不了这一项<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>，为了使初学者也能够对内存进行操作，做出基于内存控制的外挂，笔者把</p><p>一些内存操作及一些内存操作逻辑进行了封装，以控件形式提供给初学者。控件名为：MpMemCtl。</p><p>　　初学者在使用此控件时，要先安装外挂引擎控件包（在此后的每篇文章中外挂引擎控件包仅提供与该文章相应的控制</p><p>控件），具体控件安装方式，请参阅《Delphi指南》，由于篇幅所限，恕不能详细提供。</p><p>　　在引擎安装完成后，便可以在Delphi中的组件栏内，找到[MP GameControls]控件组，其中可以找到[MpMemCtl]控件</p><p>。初学者可以使用此控件可以对内存进行控制。</p><p>　　一、 得到进程句柄</p><p>　　需要操作<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>内存,那么首先必须确认要操作的<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>,而<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>程序在运行时所产生的每一个进程都有一个唯一的句柄。</p><p>　　使用控件得到句柄有三种方法：</p><p>　　1、 通过控件打开程序得到句柄。</p><p>　　在控件中，提供了startProgram方法，通过该方法，可以打开程序得到进程句柄，并且可以返回进程信息。</p><p><br />　　该方法提供了两个参数，第一个参数为要打开的程序路径，第二个参数为打开程序后所创建进程的进程信息。使用这</p><p>个方法在得到进程信息的同时，并给控件的ProcHandle（进程句柄）属性进行了附值，这时可以使用控件直接对内存进程</p><p>读写操作。其应用实例如下：</p><p><br />　　2、通过控件根据程序名称得到句柄。</p><p>　　在控件中，对系统运行进程也有了相应的描述，控件提供了两个方法，用于根据程序名称得到相应的进程句柄。</p><p>getProcIDs()可以得到系统现在所运行的所有程序的名称列表。getProcID()可以通过所运行程序名称，得到相应进程的</p><p>句柄。</p><p><br />　　其应用实例如下：</p><p>　　首先可以通过getProcIDs()并把参数列表返回ComboBox1.Items里：</p><p><br />　　接着可以通过getProcID()得到相应的进程句柄，并给控件的ProcHandle（进程句柄）属性进行了附值，这时可以使</p><p>用控件直接对内存进程读写操作。</p><p><br />　　3、通过控件根据窗口名称得到句柄。</p><p>　　在控件中，控件提供了两个方法，用于根据窗口名称得到相应的进程句柄。可以通过getALLWindow()得到所有在进程</p><p>中运行的窗口。getWinProcHandle()可以通过相应的窗口名称，得到相应的进程的句柄。</p><p><br />　　其应用实例如下：</p><p>　　首先可以通过getALLWindow ()并把参数列表返回ComboBox1.Items里：</p><p><br />　　接着可以通过getWinProcHandle ()得到相应的进程句柄，并给控件的ProcHandle（进程句柄）属性进行了附值，这</p><p>时可以使用控件直接对内存进程读写操作。</p><p><br />　　二、使<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>暂停</p><p>　　在程序中，为了便于更好的得到<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>的当前属性。在控件中提供了<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>暂停方法。只需要调用该方法，<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>便可以自</p><p>由的暂停或启动。该方法为：pauseProc()</p><p><br />　　控制类型只能够传入参数0或1，0代表使<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>暂停，1代表取消暂停。其应用实例如下：</p><p><br />　　三、读写内存值</p><p>　　<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>属性其实寄存在内存地址值里，<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>中要了解或修改<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>属性，可以通过对内存地值的读出或写入完成。</p><p>　　通过控件，要读写内存地址值很容易。可以通过调用控件提供的getAddressValue（）及setAddressValue（）两个方</p><p>法即可，在使用方法之前，要确认的是要给ProcHandle属性进行附值，因为对内存的操作必须基于进程。给ProcHandle属</p><p>性附值的方法，在上文中已经介绍。无论是对内存值进行读还是进行写，都要明确所要操作的内存地址。</p><p><br />　　要注意的是，传入内存地址时，内存地址必须为Pointer型。其应用实例如下：</p><p>　　读取地址值（如果“主角”等级所存放的地址为4549632）：</p><p><br />　　这时aValue变量里的值为内存地址[4549632]的值。</p><p>　　写入地址值：</p><p><br />　　通过该方法可以把要修改的内存地址值改为87，即把“主角”等级改为87。</p><p>　　四、内存地址值分析</p><p>　　在<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>中要想要到<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>属性存放的内存地址，那么就对相应内存地址进行内存分析，经过分析以后才可得到<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>属性</p><p>存放的人存地址。</p><p>　　控件提供两种基于内存地址的分析方法。一种是按精确地址值进行搜索分析，另一种是按内存变化增减量进行搜索分</p><p>析。</p><p>　　1、 如果很明确的知道当前想要修改的地址值，那么就用精确地址值进行搜索分析</p><p>　　在<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>中，需要修改<a href="http://hackbase.com/gest/hackbase" target="_blank"><font color="#000000">人物</font></a>的经验值，那么首先要从<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>画面上获得经验值信息，如<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a><a href="http://hackbase.com/gest/hackbase" target="_blank"><font color="#000000">人物</font></a>当前经验值为9800，需要</p><p>把经验值调高，那么这时候就需要对<a href="http://hackbase.com/gest/hackbase" target="_blank"><font color="#000000">人物</font></a>经验值在内存中搜索得到相应的内存地址，当然很可能在内存中地址值为9800的</p><p>很多，第一次很可能搜索出若干个地址值为9800的地址。等待经验值再有所变化，如从9800变为了20000时，再次进行搜</p><p>索，那么从刚刚所搜索到的地址中，便可以进一步获得范围更少的内存地址，以此类推，那么最后可得到经验值具体存放</p><p>的地址。</p><p>　　如要用控件来实现内存值精确搜索，其实方法很简单，只需要调用该控件的Search（）方法即可。但是在搜索之前要</p><p>确认搜索的范围，正如前文中所说：“而程序创建时在线性地址的中保留4MB-2GB的一段地址”，所以要搜索的地址应该</p><p>是4MB-2GB之间，所以要把控件的MaxAddress属性设为2GB，把控件的MinAddress属性设为4MB。还有一个需要确认的是需</p><p>要搜索的值，那么应该把SearchValue属性设置为当前搜索的值。如果需要显示搜索进度那么可以把ShowGauge属性挂上一</p><p>个相应的TGauge控件（该控件为进度条控件）。</p><p><br />　　在搜索分析时为了提高搜索效率、实现业务逻辑，那么需要传入一个参数，从而确认是否是第一次进行内存。其应用</p><p>实例如下：</p><p><br />　　2、 如果不明确当前想要修改的地址值，只知道想要修改的值变大或变小，那么就按内存变化增减量进行搜索分析。</p><p>　　如有些<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>的<a href="http://hackbase.com/gest/hackbase" target="_blank"><font color="#000000">人物</font></a>血值不显示出来，但要对<a href="http://hackbase.com/gest/hackbase" target="_blank"><font color="#000000">人物</font></a>血值进行修改，那么只有借助于内存量增减变化而进行搜索分析出该</p><p><a href="http://hackbase.com/gest/hackbase" target="_blank"><font color="#000000">人物</font></a>血值存放的地址。如果<a href="http://hackbase.com/gest/hackbase" target="_blank"><font color="#000000">人物</font></a>被怪物打了一下，那么<a href="http://hackbase.com/gest/hackbase" target="_blank"><font color="#000000">人物</font></a>血值就会减少，那么这时候就用减量进行搜索分析，如果<a href="http://hackbase.com/gest/hackbase" target="_blank"><font color="#000000">人物</font></a></p><p>吃了“血”<a href="http://hackbase.com/gest/hackbase" target="_blank"><font color="#000000">人物</font></a>血值就会增加，那么这时候就用增量进行搜索分析。经过不断搜索，最后会把范围放血值的内存地址给搜</p><p>索出来。</p><p>　　如要用控件来实现内存值精确搜索，其实方法很简单，只需要调用该控件的compare()方法即可。MaxAddress、</p><p>MinAddress属性设置上面章节中有详细介绍，在此不再重提。在此分析中不需要再指定SearchValue属性。如果需要显示</p><p>搜索进度那么可以把ShowGauge属性挂上一个相应的TGauge控件。</p><p><br />　　在搜索分析时为了提高搜索效率、实现业务逻辑，那么需要传入一个参数，从而确认是否是第一次进行内存。搜索分</p><p>析类型有两种：如果参数值为0，那么就代表增量搜索。如果参数值为1，那么就代表减量搜索。其应用实例如下：</p><p><br />　　五、得到内存地址值</p><p>　　在控件中，提供获得分析后内存地址列表的方法，只需要调用getAddressList()方法，便可以获得分析过程中或分析</p><p>结果地址列表。但如果使用的是按内存变化增减量进行搜索分析的方法，那么第一次可能会搜索出来很多的地址，致使返</p><p>回速度过长，那么建议使用getAddressCount（）方法确定返回列表为一定长度后才给予返回。</p><p><br />　　其应用实例如下：</p><p><br />　　通过以上五个步骤，便可以整合成一个功能比较完备的，基于内存控制方法的<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂。有了“FPE”的关键部份功</p><p>能。利用此<a href="http://hackbase.com/hacker/tool" target="_blank"><font color="#000000">工具</font></a>，通过一些方法，不仅仅可以分析出来<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>属性单内存地址，而且可以分析出一部份多内存<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>属性存放</p><p>地址。</p><div class="clear"></div></div>
<img src ="http://www.cppblog.com/eday/aggbug/15383.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eday/" target="_blank">松*</a> 2006-11-18 16:55 <a href="http://www.cppblog.com/eday/archive/2006/11/18/15383.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>网络字节顺序和机器顺序什么区别</title><link>http://www.cppblog.com/eday/archive/2006/11/18/15384.html</link><dc:creator>松*</dc:creator><author>松*</author><pubDate>Sat, 18 Nov 2006 08:55:00 GMT</pubDate><guid>http://www.cppblog.com/eday/archive/2006/11/18/15384.html</guid><description><![CDATA[
		<div class="item-content">
				<p>存在两种字节顺序：NBO与HBO</p>
				<p>网络字节顺序NBO（Network Byte Order）：<br />按从高到低的顺序存储，在网络上使用统一的网络字节顺序，可以避免兼容性问题。</p>
				<p>主机字节顺序（HBO，Host Byte Order）：<br />不同的机器HBO不相同，与CPU设计有关</p>
				<p>
						<br />计算机数据存储有两种字节优先顺序：高位字节优先和低位字节优先。Internet上数据以高位字节优先顺序在网络上传输，所以对于在内部是以低位字节优先方式存储数据的机器，在Internet上传输数据时就需要进行转换。 <br />　　我们要讨论的第一个结构类型是：struct sockaddr，该类型是用来保存socket信息的： <br />　　struct sockaddr { <br />　　unsigned short sa_family; /* 地址族， AF_xxx */ <br />　 char sa_data[14]; /* 14 字节的协议地址 */ }; <br />　　sa_family一般为AF_INET；sa_data则包含该socket的IP地址和端口号。 <br />　　另外还有一种结构类型： <br />　　struct sockaddr_in { <br />　　 short int sin_family; /* 地址族 */ <br />　　 unsigned short int sin_port; /* 端口号 */ <br />　　 struct in_addr sin_addr; /* IP地址 */ <br />　　 unsigned char sin_zero[8]; /* 填充0 以保持与struct sockaddr同样大小 */ <br />　　}; <br />　　这个结构使用更为方便。sin_zero(它用来将sockaddr_in结构填充到与struct sockaddr同样的长度)应该用bzero()或memset()函数将其置为零。指向sockaddr_in 的指针和指向sockaddr的指针可以相互转换，这意味着如果一个函数所需参数类型是sockaddr时，你可以在函数调用的时候将一个指向sockaddr_in的指针转换为指向sockaddr的指针；或者相反。sin_family通常被赋AF_INET；sin_port和sin_addr应该转换成为网络字节优先顺序；而sin_addr则不需要转换。 <br />　　我们下面讨论几个字节顺序转换函数： <br />　　htons()--"Host to Network Short" ; htonl()--"Host to Network Long" <br />　　ntohs()--"Network to Host Short" ; ntohl()--"Network to Host Long" <br />　　在这里， h表示"host" ，n表示"network"，s 表示"short"，l表示 "long"。 <br />　　打开socket 描述符、建立绑定并建立连接 <br />　　socket函数原型为： <br />　　int socket(int domain, int type, int protocol); <br />　　domain参数指定socket的类型：SOCK_STREAM 或SOCK_DGRAM；protocol通常赋值“0”。Socket()调用返回一个整型socket描述符，你可以在后面的调用使用它。 <br />　　一旦通过socket调用返回一个socket描述符，你应该将该socket与你本机上的一个端口相关联（往往当你在设计服务器端程序时需要调用该函数。随后你就可以在该端口监听服务请求;而客户端一般无须调用该函数）。 Bind函数原型为： <br />　　int bind(int sockfd,struct sockaddr *my_addr, int addrlen); <br />　　Sockfd是一个socket描述符，my_addr是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针;addrlen常被设置为sizeof(struct sockaddr)。 <br />　　最后，对于bind 函数要说明的一点是，你可以用下面的赋值实现自动获得本机IP地址和随机获取一个没有被占用的端口号： <br />　　my_addr.sin_port = 0; /* 系统随机选择一个未被使用的端口号 */ <br />　　my_addr.sin_addr.s_addr = INADDR_ANY; /* 填入本机IP地址 */ <br />　　通过将my_addr.sin_port置为0，函数会自动为你选择一个未占用的端口来使用。同样，通过将my_addr.sin_addr.s_addr置为INADDR_ANY，系统会自动填入本机IP地址。Bind()函数在成功被调用时返回0；遇到错误时返回“-1”并将errno置为相应的错误号。另外要注意的是，当调用函数时，一般不要将端口号置为小于1024的值，因为1~1024是保留端口号，你可以使用大于1024中任何一个没有被占用的端口号。 <br />　　Connect()函数用来与远端服务器建立一个TCP连接，其函数原型为： <br />　　int connect(int sockfd, struct sockaddr *serv_addr, int addrlen); <br />　　Sockfd是目的服务器的sockt描述符；serv_addr是包含目的机IP地址和端口号的指针。遇到错误时返回-1，并且errno中包含相应的错误码。进行客户端程序设计无须调用bind()，因为这种情况下只需知道目的机器的IP地址，而客户通过哪个端口与服务器建立连接并不需要关心，内核会自动选择一个未被占用的端口供客户端来使用。 <br />　　Listen()——监听是否有服务请求 <br />　　在服务器端程序中，当socket与某一端口捆绑以后，就需要监听该端口，以便对到达的服务请求加以处理。 <br />　　int listen(int sockfd， int backlog); <br />　　Sockfd是Socket系统调用返回的socket 描述符；backlog指定在请求队列中允许的最大请求数，进入的连接请求将在队列中等待accept()它们（参考下文）。Backlog对队列中等待服务的请求的数目进行了限制，大多数系统缺省值为20。当listen遇到错误时返回-1，errno被置为相应的错误码。 <br />　　故服务器端程序通常按下列顺序进行函数调用： <br />　　socket(); bind(); listen(); /* accept() goes here */ <br />　　accept()——连接端口的服务请求。 <br />　　当某个客户端试图与服务器监听的端口连接时，该连接请求将排队等待服务器accept()它。通过调用accept()函数为其建立一个连接，accept()函数将返回一个新的socket描述符，来供这个新连接来使用。而服务器可以继续在以前的那个 socket上监听，同时可以在新的socket描述符上进行数据send()（发送）和recv()（接收）操作： <br />　　int accept(int sockfd, void *addr, int *addrlen); <br />　　sockfd是被监听的socket描述符，addr通常是一个指向sockaddr_in变量的指针，该变量用来存放提出连接请求服务的主机的信息（某台主机从某个端口发出该请求）；addrten通常为一个指向值为sizeof(struct sockaddr_in)的整型指针变量。错误发生时返回一个-1并且设置相应的errno值。 <br />　　Send()和recv()——数据传输 <br />　　这两个函数是用于面向连接的socket上进行数据传输。 <br />　　Send()函数原型为： <br />　　int send(int sockfd, const void *msg, int len, int flags); <br />　　Sockfd是你想用来传输数据的socket描述符，msg是一个指向要发送数据的指针。 <br />　　Len是以字节为单位的数据的长度。flags一般情况下置为0（关于该参数的用法可参照man手册）。 <br />　　char *msg = "Beej was here!"; int len， bytes_sent; ... ... <br />　　len = strlen(msg); bytes_sent = send(sockfd, msg,len,0); ... ... <br />　　Send()函数返回实际上发送出的字节数，可能会少于你希望发送的数据。所以需要对send()的返回值进行测量。当send()返回值与len不匹配时，应该对这种情况进行处理。 <br />　　recv()函数原型为： <br />　　int recv(int sockfd,void *buf,int len,unsigned int flags); <br />　　Sockfd是接受数据的socket描述符；buf 是存放接收数据的缓冲区；len是缓冲的长度。Flags也被置为0。Recv()返回实际上接收的字节数，或当出现错误时，返回-1并置相应的errno值。 <br />　　Sendto()和recvfrom()——利用数据报方式进行数据传输 <br />　　在无连接的数据报socket方式下，由于本地socket并没有与远端机器建立连接，所以在发送数据时应指明目的地址，sendto()函数原型为： <br />　　int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen); <br />　　该函数比send()函数多了两个参数，to表示目地机的IP地址和端口号信息，而tolen常常被赋值为sizeof (struct sockaddr)。Sendto 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。 <br />　　Recvfrom()函数原型为： <br />　　int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen); <br />　　from是一个struct sockaddr类型的变量，该变量保存源机的IP地址及端口号。fromlen常置为sizeof (struct sockaddr)。当recvfrom()返回时，fromlen包含实际存入from中的数据字节数。Recvfrom()函数返回接收到的字节数或当出现错误时返回-1，并置相应的errno。 <br />　　应注意的一点是，当你对于数据报socket调用了connect()函数时，你也可以利用send()和recv()进行数据传输，但该socket仍然是数据报socket，并且利用传输层的UDP服务。但在发送或接收数据报时，内核会自动为之加上目地和源地址信息。 <br />　　Close()和shutdown()——结束数据传输 <br />　　当所有的数据操作结束以后，你可以调用close()函数来释放该socket，从而停止在该socket上的任何数据操作：close(sockfd); <br />　　你也可以调用shutdown()函数来关闭该socket。该函数允许你只停止在某个方向上的数据传输，而一个方向上的数据传输继续进行。如你可以关闭某socket的写操作而允许继续在该socket上接受数据，直至读入所有数据。 <br />　　int shutdown(int sockfd,int how); <br />　　Sockfd的含义是显而易见的，而参数 how可以设为下列值： <br />　　·0-------不允许继续接收数据 <br />　　·1-------不允许继续发送数据 <br />　　·2-------不允许继续发送和接收数据，均为允许则调用close () <br />　　shutdown在操作成功时返回0，在出现错误时返回-1（并置相应errno）。 <br />　　DNS——域名服务相关函数 <br />　　由于IP地址难以记忆和读写，所以为了读写记忆方便，人们常常用域名来表示主机，这就需要进行域名和IP地址的转换。函数gethostbyname()就是完成这种转换的，函数原型为： <br />　　struct hostent *gethostbyname(const char *name); <br />　　函数返回一种名为hosten的结构类型，它的定义如下： <br />　　struct hostent { <br />　　 char *h_name; /* 主机的官方域名 */ <br />　　 char **h_aliases; /* 一个以NULL结尾的主机别名数组 */ <br />　　 int h_addrtype; /* 返回的地址类型，在Internet环境下为AF-INET */ <br />　　 int h_length; /*地址的字节长度 */ <br />　　 char **h_addr_list; /* 一个以0结尾的数组，包含该主机的所有地址*/ <br />　　}; <br />　#define h_addr h_addr_list[0] /*在h-addr-list中的第一个地址*/ </p>
				<p>2、将主机的unsigned long值转换为网络字节顺序(32位)：为什么要这样做呢？因为不同的计算机使用不同的字节顺序存储数据。因此任何从Winsock函数对IP地址和端口号的引用和传给Winsock函数的IP地址和端口号均时按照网络顺序组织的。<br />      u_long  htonl(u_long hostlong);<br />      举例：htonl(0)=0<br />      htonl(80)= 1342177280</p>
				<p>3、将unsigned long数从网络字节顺序转换位主机字节顺序，是上面函数的逆函数。       u_long  ntohl(u_long netlong);<br />      举例：ntohl(0)=0<br />      ntohl(1342177280)= 80<br /></p>
				<p>4、将主机的unsigned short值转换为网络字节顺序(16位)：原因同2：       u_short  htons(u_short hostshort);<br />      举例：htonl(0)=0<br />      htonl(80)= 20480</p>
				<p>5、将unsigned short数从网络字节顺序转换位主机字节顺序，是上面函数的逆函数。       u_short  ntohs(u_short netshort);<br />      举例：ntohs(0)=0<br />      ntohsl(20480)= 80</p>
				<div class="clear">
				</div>
		</div>
<img src ="http://www.cppblog.com/eday/aggbug/15384.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eday/" target="_blank">松*</a> 2006-11-18 16:55 <a href="http://www.cppblog.com/eday/archive/2006/11/18/15384.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏外挂设计技术探讨-二</title><link>http://www.cppblog.com/eday/archive/2006/11/18/15381.html</link><dc:creator>松*</dc:creator><author>松*</author><pubDate>Sat, 18 Nov 2006 08:54:00 GMT</pubDate><guid>http://www.cppblog.com/eday/archive/2006/11/18/15381.html</guid><description><![CDATA[
		<div class="item-content">(8).编译项目ActiveKey，生成ActiveKey.DLL和ActiveKey.lib。 
<p>　　接着，我们还需要创建一个外壳程序将全局钩子安装了Windows系统中，这个外壳程序编写步骤如下：</p><p>　　(1).创建一个对话框模式的应用程序，项目名为Simulate。</p><p>　　(2).在主对话框中加入一个按钮，使用ClassWizard为其创建CLICK事件。</p><p>　　(3).将ActiveKey项目Debug目录下的ActiveKey.DLL和ActiveKey.lib拷贝到Simulate项目目录下。</p><p>　　(4).从“工程”菜单中选择“设置”，弹出Project Setting对话框，选择Link标签，在“对象/库模块”中输入</p><p>ActiveKey.lib。</p><p>　　(5).将ActiveKey项目中的ActiveKey.h头文件加入到Simulate项目中，并在Stdafx.h中加入#include ActiveKey.h。</p><p>　　(6).在按钮单击事件函数输入如下<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>：</p><p>　　　void CSimulateDlg::OnButton1() <br />　　　{<br />// TODO: Add your control notification handler code here<br />if( !bSetup )<br />{<br />m<a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a>hook.Start();//激活全局钩子。<br />}<br />else<br />{<br />m<a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a>hook.Stop();//撤消全局钩子。<br />}<br />bSetup = !bSetup;</p><p>　　　}  </p><p>　　(7).编译项目，并运行程序，单击按钮激活外挂。</p><p>　　(8).启<a href="http://hackbase.com/flash" target="_blank"><font color="#000000">动画</font></a>笔程序，选择文本<a href="http://hackbase.com/hacker/tool" target="_blank"><font color="#000000">工具</font></a>并将笔的颜色设置为红色，将鼠标放在任意位置后，按F10键，画笔程序自动移动</p><p>鼠标并写下一个红色的大写R。图一展示了按F10键前的画笔程序的状态，图二展示了按F10键后的画笔程序的状态。</p><p><br />图一：按F10前状态(001.jpg)</p><p><br />图二：按F10后状态(002.jpg) </p><p><br /> </p><p>　五、封包<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a></p><p>　　通过对动作模拟<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>的介绍，我们对<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂有了一定程度上的认识，也学会了使用动作模拟<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>来实现简单的动作</p><p>模拟型<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂的制作。这种动作模拟型<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂有一定的局限性，它仅仅只能解决使用<a href="http://hackbase.com/skill" target="_blank"><font color="#000000">计算机</font></a>代替人力完成那么有规律</p><p>、繁琐而无聊的<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>动作。但是，随着网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>的盛行和复杂度的增加，很多<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>要求将客户端动作信息及时反馈回<a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a></p><p>器，通过<a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a>器对这些动作信息进行有效认证后，再向客户端发送下一步<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>动作信息，这样动作模拟<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>将失去原有的</p><p>效应。为了更好地“外挂”这些<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>，<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂程序也进行了升级换代，它们将以前针对<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>用户界面层的模拟推进到数</p><p>据通讯层，通过封包<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>在客户端挡截<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a><a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a>器发送来的<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>控制数据包，分析数据包并修改数据包；同时还需按照游</p><p>戏数据包结构创建数据包，再模拟客户端发送给<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a><a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a>器，这个过程其实就是一个封包的过程。</p><p>　　封包的<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>是实现第二类<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂的最核心的<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>。封包<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>涉及的<a href="http://hackbase.com/network/zs" target="_blank"><font color="#000000">知识</font></a>很广泛，实现方法也很多，如挡截WinSock</p><p>、挡截API函数、挡截消息、VxD驱动程序等。在此我们也不可能在此文中将所有的封包<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>都进行详细介绍，故选择两种</p><p>在<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂程序中最常用的两种方法：挡截WinSock和挡截API函数。</p><p>　　1． 挡截WinSock</p><p>　　众所周知，Winsock是Windows网络<a href="http://hackbase.com/hacker/program" target="_blank"><font color="#000000">编程</font></a>接口，它工作于Windows应用层，它提供与底层传输<a href="http://hackbase.com/network/protocol" target="_blank"><font color="#000000">协议</font></a>无关的高层数据传输</p><p><a href="http://hackbase.com/hacker/program" target="_blank"><font color="#000000">编程</font></a>接口。在Windows系统中，使用WinSock接口为应用程序提供基于<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">TCP/IP</font></a><a href="http://hackbase.com/network/protocol" target="_blank"><font color="#000000">协议</font></a>的网络访问<a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a>，这些<a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a>是由</p><p>Wsock32.DLL<a href="http://hackbase.com/News/industry" target="_blank"><font color="#000000">动态</font></a>链接库提供的函数库来完成的。</p><p>　　由上说明可知，任何Windows基于<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">TCP/IP</font></a>的应用程序都必须通过WinSock接口访问网络，当然网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>程序也不例外。</p><p>由此我们可以想象一下，如果我们可以控制WinSock接口的话，那么控制<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>客户端程序与<a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a>器之间的数据包也将易如</p><p>反掌。按着这个思路，下面的工作就是如何完成控制WinSock接口了。由上面的介绍可知，WinSock接口其实是由一个<a href="http://hackbase.com/News/industry" target="_blank"><font color="#000000">动态</font></a></p><p>链接库提供的一系列函数，由这些函数实现对网络的访问。有了这层的认识，问题就好办多了，我们可以制作一个类似的</p><p><a href="http://hackbase.com/News/industry" target="_blank"><font color="#000000">动态</font></a>链接库来代替原WinSock接口库，在其中实现WinSock32.dll中实现的所有函数，并保证所有函数的参数个数和顺序、</p><p>返回值类型都应与原库相同。在这个自制作的<a href="http://hackbase.com/News/industry" target="_blank"><font color="#000000">动态</font></a>库中，可以对我们感兴趣的函数（如发送、接收等函数）进行挡截，放</p><p>入外挂控制<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>，最后还继续调用原WinSock库中提供的相应功能函数，这样就可以实现对网络数据包的挡截、修改和发</p><p>送等封包功能。</p><p>　　下面重点介绍创建挡截WinSock外挂程序的基本步骤：</p><p>　　(1) 创建DLL项目，选择Win32 Dynamic-Link Library，再选择An empty DLL project。</p><p>　　(2) 新建文件wsock32.h，按如下步骤输入<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>：</p><p>　　① 加入相关变量声明：</p><p>　　　HMODULE hModule=NULL; //模块句柄<br />　　　char buffer[1000]; //缓冲区<br />　　　FARPROC proc; //函数入口指针  </p><p>　　② 定义指向原WinSock库中的所有函数地址的指针变量，因WinSock库共提供70多个函数，限于篇幅，在此就只选择</p><p>几个常用的函数列出，有关这些库函数的说明可参考MSDN相关内容。</p><p>　　　//定义指向原WinSock库函数地址的指针变量。<br />　　　SOCKET (<a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a><a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a>stdcall *socket1)(int ,int,int);//创建Sock函数。<br />　　　int　(<a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a><a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a>stdcall *WSAStartup1)(WORD,LPWSADATA);//初始化WinSock库函数。<br />　　　int　(<a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a><a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a>stdcall *WSACleanup1)();//清除WinSock库函数。<br />　　　int (<a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a><a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a>stdcall *recv1)(SOCKET ,char FAR * ,int ,int );//接收数据函数。<br />　　　int (<a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a><a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a>stdcall *send1)(SOCKET ,const char * ,int ,int);//发送数据函数。<br />　　　int (<a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a><a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a>stdcall *connect1)(SOCKET,const struct sockaddr *,int);//创建连接函数。<br />　　　int (<a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a><a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a>stdcall *bind1)(SOCKET ,const struct sockaddr *,int );//绑定函数。<br />　　　......其它函数地址指针的定义略。  </p><p>　　(3) 新建wsock32.cpp文件，按如下步骤输入<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>：</p><p>　　① 加入相关头文件声明：</p><p>　　　#include <br />　　　#include <br />　　　#include "wsock32.h"  </p><p>　　② 添加DllMain函数，在此函数中首先需要加载原WinSock库，并获取此库中所有函数的地址。<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>如下：</p><p>　　　BOOL WINAPI DllMain (HANDLE hInst,ULONG ul<a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a>reason<a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a>for<a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a>call,LPVOID lpReserved)<br />　　　{<br />　　　　if(hModule==NULL){<br />　　　　　//加载原WinSock库，原WinSock库已复制为wsock32.001。<br />　　　hModule=LoadLibrary("wsock32.001"); <br />　　}<br />　　　　else return 1;<br />//获取原WinSock库中的所有函数的地址并保存，下面仅列出部分<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>。<br />if(hModule!=NULL){<br />　　　　　//获取原WinSock库初始化函数的地址，并保存到WSAStartup1中。<br />proc=GetProcAddress(hModule,"WSAStartup");<br />　　　WSAStartup1=(int (<a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a>stdcall *)(WORD,LPWSADATA))proc;<br />　　　　　//获取原WinSock库消除函数的地址，并保存到WSACleanup1中。<br />　　　　proc=GetProcAddress(hModule i,"WSACleanup");<br />　　　　WSACleanup1=(int (<a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a>stdcall *)())proc;<br />　　　　　//获取<a href="http://hackbase.com/News/hk" target="_blank"><font color="#000000">原创</font></a>建Sock函数的地址，并保存到socket1中。<br />　　　　proc=GetProcAddress(hModule,"socket");<br />　　　　　socket1=(SOCKET (<a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a>stdcall *)(int ,int,int))proc;<br />　　　　　//获取<a href="http://hackbase.com/News/hk" target="_blank"><font color="#000000">原创</font></a>建连接函数的地址，并保存到connect1中。<br />　　　　　proc=GetProcAddress(hModule,"connect");<br />　　　　　connect1=(int (<a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a>stdcall *)(SOCKET ,const struct sockaddr *,int ))proc;<br />　　　　　//获取原发送函数的地址，并保存到send1中。<br />　　　　　proc=GetProcAddress(hModule,"send");<br />　　　　　send1=(int (<a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a>stdcall *)(SOCKET ,const char * ,int ,int ))proc;<br />　　　　　//获取原接收函数的地址，并保存到recv1中。<br />　　　　　proc=GetProcAddress(hModule,"recv");<br />　　　　　recv1=(int (<a href="http://hackbase.com/game/game/2005022010070_1.html#" target="_blank"><font color="#000000">_</font></a>stdcall *)(SOCKET ,char FAR * ,int ,int ))proc;<br />　　　　　......其它获取函数地址<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>略。<br />　　　}<br />　　　else return 0;<br />　　　return 1;<br />} </p><p>　　③ 定义库输出函数，在此可以对我们感兴趣的函数中添加外挂控制<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>，在所有的输出函数的最后一步都调用原</p><p>WinSock库的同名函数。部分输出函数定义<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>如下：</p><p>//库输出函数定义。<br />//WinSock初始化函数。<br />　　　　int PASCAL FAR WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData)<br />　　　　{<br />　　　　　//调用原WinSock库初始化函数<br />　　　　　return WSAStartup1(wVersionRequired,lpWSAData);<br />　　　　}<br />　　　　//WinSock结束清除函数。<br />　　　　int PASCAL FAR WSACleanup(void)<br />　　　　{<br />　　　　　return WSACleanup1(); //调用原WinSock库结束清除函数。<br />　　　　}<br />　　　　//创建Socket函数。<br />　　　　SOCKET PASCAL FAR socket (int af, int type, int protocol)<br />　　　　{<br />　　　　　//调用原WinSock库创建Socket函数。<br />　　　　　return socket1(af,type,protocol);<br />　　　　}<br />　　　　//发送数据包函数<br />　　　　int PASCAL FAR send(SOCKET s,const char * buf,int len,int flags)<br />　　　　{<br />　　　//在此可以对发送的缓冲buf的内容进行修改，以实现欺骗<a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a>器。<br />　　　外挂<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>......<br />　　　//调用原WinSock库发送数据包函数。<br />　　　　　return send1(s,buf,len,flags);<br />　　　　}<br />//接收数据包函数。<br />　　　　int PASCAL FAR recv(SOCKET s, char FAR * buf, int len, int flags)<br />　　　　{<br />　　　//在此可以挡截到<a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a>器端发送到客户端的数据包，先将其保存到buffer中。<br />　　　strcpy(buffer,buf);<br />　　　//对buffer数据包数据进行分析后，对其按照玩家的指令进行相关修改。<br />　　　外挂<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>......<br />　　　//最后调用原WinSock中的接收数据包函数。<br />　　　　　return recv1(s, buffer, len, flags);<br />　　　　　}<br />　　　　.......其它函数定义<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>略。</p><div class="clear"></div></div>
<img src ="http://www.cppblog.com/eday/aggbug/15381.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eday/" target="_blank">松*</a> 2006-11-18 16:54 <a href="http://www.cppblog.com/eday/archive/2006/11/18/15381.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏外挂设计技术探讨-三</title><link>http://www.cppblog.com/eday/archive/2006/11/18/15382.html</link><dc:creator>松*</dc:creator><author>松*</author><pubDate>Sat, 18 Nov 2006 08:54:00 GMT</pubDate><guid>http://www.cppblog.com/eday/archive/2006/11/18/15382.html</guid><description><![CDATA[
		<div class="item-content">(4)、新建wsock32.def配置文件，在其中加入所有库输出函数的声明，部分声明<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>如下： 
<p>　　　LIBRARY "wsock32"<br />　　　E<a href="http://hackbase.com/skill/XP" target="_blank"><font color="#000000">XP</font></a>ORTS <br />　　　　WSAStartup @1<br />　　　WSACleanup @2<br />　　　　recv @3<br />　　　　send @4<br />　　　　socket @5<br />　　　bind @6<br />　　　closesocket @7<br />　　　connect @8 </p><p>　　　......其它输出函数声明<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>略。</p><p>　　(5)、从“工程”菜单中选择“设置”，弹出Project Setting对话框，选择Link标签，在“对象/库模块”中输入</p><p>Ws2<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>32.lib。</p><p>　　(6)、编译项目，产生wsock32.dll库文件。</p><p>　　(7)、将系统目录下原wsock32.dll库文件拷贝到被外挂程序的目录下，并将其改名为wsock.001；再将上面产生的</p><p>wsock32.dll文件同样拷贝到被外挂程序的目录下。重新启动<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>程序，此时<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>程序将先加载我们自己制作的</p><p>wsock32.dll文件，再通过该库文件间接调用原WinSock接口函数来实现访问网络。上面我们仅仅介绍了挡载WinSock的实</p><p>现过程，至于如何加入外挂控制<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>，还需要外挂开发人员对<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>数据包结构、内容、加密算法等方面的仔细分析（这个</p><p>过程将是一个艰辛的过程），再生成外挂控制<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>。关于数据包分析方法和<a href="http://hackbase.com/network/network" target="_blank"><font color="#000000">技巧</font></a>，不是本文讲解的范围，如您感兴趣可以</p><p>到网上查查相关资料。</p><p><br />(3)、注入外挂<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>进入被挂<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>进程中</p><p>　　完成了定位和修改程序中调用API函数<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>后，我们就可以随意设计自定义的API函数的替代函数了。做完这一切后，</p><p>还需要将这些<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>注入到被外挂<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>程序进程内存空间中，不然<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>进程根本不会访问到替代函数<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>。注入方法有很多</p><p>，如利用全局钩子注入、利用<a href="http://hackbase.com/skill/regedit" target="_blank"><font color="#000000">注册表</font></a>注入挡截User32库中的API函数、利用CreateRemoteThread注入（仅限于NT/2000）、</p><p>利用BHO注入等。因为我们在动作模拟<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>一节已经接触过全局钩子，我相信聪明的读者已经完全掌握了全局钩子的制作</p><p>过程，所以我们在后面的实例中，将继续利用这个全局钩子。至于其它几种注入方法，如果感兴趣可参阅MSDN有关内容。</p><p>　　有了以上理论基础，我们下面就开始制作一个挡截MessageBoxA和recv函数的实例，在开发<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂程序 时，可以此</p><p>实例为框架，加入相应的替代函数和处理<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>即可。此实例的开发过程如下：</p><p>　　(1) 打开前面创建的ActiveKey项目。</p><p>　　(2) 在ActiveKey.h文件中加入HOOKAPI结构，此结构用来存储被挡截API函数名称、原API函数地址和替代函数地址。</p><p>　　　typedef struct tag<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>HOOKAPI <br />　　　{ <br />　　　LPCSTR szFunc;//被HOOK的API函数名称。<br />　　　PROC pNewProc;//替代函数地址。<br />　　　PROC pOldProc;//原API函数地址。<br />　　　}HOOKAPI, *LPHOOKAPI;  </p><p>　　(3) 打开ActiveKey.cpp文件，首先加入一个函数，用于定位输入库在输入数据段中的IAT地址。<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>如下：</p><p>　　　extern "C" <a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a><a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>declspec(dllexport)PIMAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>IMPORT<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>DESCRIPTOR <br />　　　LocationIAT(HMODULE hModule, LPCSTR szImportMod) <br />　　　//其中，hModule为进程模块句柄；szImportMod为输入库名称。<br />　　　{ <br />　　　//检查是否为DOS程序，如是返回NULL，因DOS程序没有IAT。<br />　　　PIMAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>DOS<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>HEADER pDOSHeader = (PIMAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>DOS<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>HEADER) hModule; <br />　　　if(pDOSHeader-&gt;e<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>magic != IMAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>DOS<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>SIGNATURE) return NULL; <br />　　　　//检查是否为NT标志，否则返回NULL。<br />　　　　PIMAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>NT<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>HEADERS pNTHeader = (PIMAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>NT<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>HEADERS)((DWORD)pDOSHeader+ (DWORD)(pDOSHeader-</p><p>&gt;e<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>lfanew)); <br />　　　　if(pNTHeader-&gt;Signature != IMAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>NT<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>SIGNATURE) return NULL; <br />　　　　//没有IAT表则返回NULL。<br />　　　　if(pNTHeader-&gt;OptionalHeader.DataDirectory[IMAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>DIRECTORY<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>ENTRY<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>IMPORT].VirtualAddress == 0) </p><p>return NULL; <br />　　　　//定位第一个IAT位置。 <br />　　　　PIMAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>IMPORT<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>DESCRIPTOR pImportDesc = (PIMAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>IMPORT<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>DESCRIPTOR)((DWORD)pDOSHeader + (DWORD)</p><p>(pNTHeader-&gt;OptionalHeader.DataDirectory[IMAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>DIRECTORY<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>ENTRY<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>IMPORT].VirtualAddress)); <br />　　　　//根据输入库名称循环检查所有的IAT，如匹配则返回该IAT地址，否则检测下一个IAT。<br />　　　　while (pImportDesc-&gt;Name) <br />　　　　{ <br />　　　　　//获取该IAT描述的输入库名称。<br />　　　PSTR szCurrMod = (PSTR)((DWORD)pDOSHeader + (DWORD)(pImportDesc-&gt;Name)); <br />　　　if (stricmp(szCurrMod, szImportMod) == 0) break; <br />　　　pImportDesc++; <br />　　　　} <br />　　　　if(pImportDesc-&gt;Name == NULL) return NULL; <br />　　　return pImportDesc; <br />　　　} </p><p>　　再加入一个函数，用来定位被挡截API函数的IAT项并修改其内容为替代函数地址。<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>如下：</p><p>　　　extern "C" <a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a><a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>declspec(dllexport) <br />　　　HookAPIByName( HMODULE hModule, LPCSTR szImportMod, LPHOOKAPI pHookApi) <br />　　　//其中，hModule为进程模块句柄；szImportMod为输入库名称；pHookAPI为HOOKAPI结构指针。<br />　　　{ <br />　　　　//定位szImportMod输入库在输入数据段中的IAT地址。<br />　　　　PIMAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>IMPORT<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>DESCRIPTOR pImportDesc = LocationIAT(hModule, szImportMod); <br />　　if (pImportDesc == NULL) return FALSE; <br />　　　　//第一个Thunk地址。<br />　　　　PIMAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>THUNK<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>DATA pOrigThunk = (PIMAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>THUNK<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>DATA)((DWORD)hModule + (DWORD)(pImportDesc-</p><p>&gt;OriginalFirstThunk)); <br />　　 //第一个IAT项的Thunk地址。<br />　　　　PIMAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>THUNK<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>DATA pRealThunk = (PIMAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>THUNK<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>DATA)((DWORD)hModule + (DWORD)(pImportDesc-</p><p>&gt;FirstThunk)); <br />　　　　//循环查找被截API函数的IAT项，并使用替代函数地址修改其值。<br />　　　while(pOrigThunk-&gt;u1.Function) <br />{ <br />　//检测此Thunk是否为IAT项。<br />if((pOrigThunk-&gt;u1.Ordinal &amp; IMAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>ORDINAL<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>FLAG) != IMAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>ORDINAL<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>FLAG) <br />{<br />　 //获取此IAT项所描述的函数名称。<br />　PIMAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>IMPORT<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>BY<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>NAME pByName =(PIMAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>IMPORT<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>BY<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>NAME)((DWORD)hModule+(DWORD)(pOrigThunk-</p><p>&gt;u1.AddressOfData)); <br />　if(pByName-&gt;Name[0] == '\0') return FALSE; <br />　　//检测是否为挡截函数。<br />if(strcmpi(pHookApi-&gt;szFunc, (char*)pByName-&gt;Name) == 0) <br />　 { <br />　　　　　　　MEMORY<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>BASIC<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>INFORMATION mbi<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>thunk;<br />　　　　　　　//查询修改页的信息。<br />　　　　　　　VirtualQuery(pRealThunk, &amp;mbi<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>thunk, sizeof(MEMORY<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>BASIC<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>INFORMATION)); <br />//改变修改页保护属性为PAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>READWRITE。<br />　　　　　　　VirtualProtect(mbi<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>thunk.BaseAddress,mbi<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>thunk.RegionSize, PAGE<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>READWRITE, </p><p>&amp;mbi<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>thunk.Protect); <br />//保存原来的API函数地址。<br />　　　 　　if(pHookApi-&gt;pOldProc == NULL) <br />pHookApi-&gt;pOldProc = (PROC)pRealThunk-&gt;u1.Function; <br />　 //修改API函数IAT项内容为替代函数地址。<br />pRealThunk-&gt;u1.Function = (PDWORD)pHookApi-&gt;pNewProc; <br />//恢复修改页保护属性。<br />DWORD dwOldProtect; <br />　　　　　　　VirtualProtect(mbi<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>thunk.BaseAddress, mbi<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>thunk.RegionSize, mbi<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>thunk.Protect, </p><p>&amp;dwOldProtect); <br />　　　　　 } <br />} <br />　 pOrigThunk++; <br />　 pRealThunk++; <br />} <br />　　SetLastError(ERROR<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>SUCCESS); //设置错误为ERROR<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>SUCCESS，表示成功。<br />　　return TRUE; <br />　　　}  </p><p>　　(4) 定义替代函数，此实例中只给MessageBoxA和recv两个API进行挡截。<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>如下：</p><p>　　　static int WINAPI MessageBoxA1 (HWND hWnd , LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)<br />　　　{<br />　　　　//过滤掉原MessageBoxA的正文和标题内容，只显示如下内容, 。<br />return MessageBox(hWnd, "Hook API OK!", "Hook API", uType); <br />　　　} <br />　　　static int WINAPI recv1(SOCKET s, char FAR *buf, int len, int flags )<br />　　　{<br />　　　//此处可以挡截<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a><a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a>器发送来的网络数据包，可以加入分析和处理数据<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>。<br />　　　return recv(s,buf,len,flags);<br />　　　}  </p><p>　　(5) 在KeyboardProc函数中加入激活挡截API<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>，在if( wParam == 0X79 )语句中后面加入如下else if语句：</p><p>　　　......<br />　　　//当激活F11键时，启动挡截API函数功能。<br />　　　else if( wParam == 0x7A )<br />　　　{ <br />　　　　HOOKAPI api[2];<br />api[0].szFunc ="MessageBoxA";//设置被挡截函数的名称。<br />api[0].pNewProc = (PROC)MessageBoxA1;//设置替代函数的地址。<br />api[1].szFunc ="recv";//设置被挡截函数的名称。<br />api[1].pNewProc = (PROC)recv1; //设置替代函数的地址。<br />//设置挡截User32.dll库中的MessageBoxA函数。<br />HookAPIByName(GetModuleHandle(NULL),"User32.dll",&amp;api[0]);<br />//设置挡截Wsock32.dll库中的recv函数。<br />HookAPIByName(GetModuleHandle(NULL),"Wsock32.dll",&amp;api[1]);<br />　　　}<br />　　　......  </p><p>　　(6) 在ActiveKey.cpp中加入头文件声明 "#include "wsock32.h"。 从“工程”菜单中选择“设置”，弹出Project </p><p>Setting对话框，选择Link标签，在“对象/库模块”中输入Ws2<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>32..lib。</p><p>　　(7) 重新编译ActiveKey项目，产生ActiveKey.dll文件，将其拷贝到Simulate.exe目录下。运行Simulate.exe并启动</p><p>全局钩子。激活任意应用程序，按F11键后，运行此程序中可能调用MessageBoxA函数的操作，看看信息框是不是有所变化</p><p>。同样，如此程序正在接收网络数据包，就可以实现封包功能了。</p><p>　　六、结束语</p><p>　　除了以上介绍的几种<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂程序常用的<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>以外，在一些外挂程序中还使用了<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>数据修改<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>、<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>加速<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>等</p><p>。在这篇文章里，就不逐一介绍了。</p><p>网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂核心封包揭密</p><p>[文章导读] <br /> <br />网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>的封包<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>是大多数<a href="http://hackbase.com/hacker/program" target="_blank"><font color="#000000">编程</font></a>爱好者都比较关注的关注的问题之一，在这里就让我们一起<a href="http://hackbase.com/hacker/leak" target="_blank"><font color="#000000">研究</font></a>一下这一个问题吧 <br />　　网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>的封包<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>是大多数<a href="http://hackbase.com/hacker/program" target="_blank"><font color="#000000">编程</font></a>爱好者都比较关注的关注的问题之一，在这里就让我们一起<a href="http://hackbase.com/hacker/leak" target="_blank"><font color="#000000">研究</font></a>一下这一个问题吧</p><p>。</p><p>　　别看这是封包这一问题，但是涉及的<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>范围很广范，实现的方式也很多（比如说APIHOOK,VXD,Winsock2都可以实现</p><p>），在这里我们不可能每种<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>和方法都涉及，所以我在这里以Winsock2<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>作详细讲解，就算作抛砖引玉。</p><p>　　由于大多数读者对封包类<a href="http://hackbase.com/hacker/program" target="_blank"><font color="#000000">编程</font></a>不是很了解，我在这里就简单介绍一下相关<a href="http://hackbase.com/network/zs" target="_blank"><font color="#000000">知识</font></a>：</p><p>　　APIHooK：</p><p>　　由于Windows的把内核提供的功能都封装到API里面，所以大家要实现功能就必须通过API，换句话说就是我们要想捕</p><p>获数据封包，就必须先要得知道并且捕获这个API，从API里面得到封包信息。</p><p>　　VXD：</p><p>　　直接通过控制VXD驱动程序来实现封包信息的捕获，不过VXD只能用于win9X。</p><p>　　winsock2：</p><p>　　winsock是Windows网络<a href="http://hackbase.com/hacker/program" target="_blank"><font color="#000000">编程</font></a>接口，winsock工作在应用层，它提供与底层传输<a href="http://hackbase.com/network/protocol" target="_blank"><font color="#000000">协议</font></a>无关的高层数据传输<a href="http://hackbase.com/hacker/program" target="_blank"><font color="#000000">编程</font></a>接口，</p><p>winsock2是winsock2.0提供的<a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a>提供者接口，但只能在win2000下用。</p><p>　　好了，我们开始进入winsock2封包式<a href="http://hackbase.com/hacker/program" target="_blank"><font color="#000000">编程</font></a>吧。</p><p>　　在封包<a href="http://hackbase.com/hacker/program" target="_blank"><font color="#000000">编程</font></a>里面我准备分两个步骤对大家进行讲解：1、封包的捕获，2、封包的发送。</p><p>　　首先我们要实现的是封包的捕获：</p><p>　　Delphi的封装的winsock是1.0版的，很自然winsock2就用不成。如果要使用winsock2我们要对winsock2在Delphi里面</p><p>做一个接口，才可以使用winsock2。</p><p>　　1、如何做winsock2的接口？</p><p>　　1）我们要先定义winsock2.0所用得到的类型，在这里我们以WSA<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>DATA类型做示范，大家可以举一仿三的来实现</p><p>winsock2其他类型的封装。</p><p>　　我们要知道WSA<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>DATA类型会被用于WSAStartup(wVersionRequired: word; var WSData: TWSAData): Integer;，大家</p><p>会发现WSData是引用参数，在传入参数时传的是变量的地址，所以我们对WSA<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>DATA做以下封装：</p><p><br />　　2）我们要从WS2<a href="http://hackbase.com/game/game/2005022010070_2.html#" target="_blank"><font color="#000000">_</font></a>32.DLL引入winsock2的函数，在此我们也是以WSAStartup为例做函数引入：</p><p><br />　　通过以上方法，我们便可以对winsock2做接口，下面我们就可以用winsock2做封包捕获了，不过首先要有一块网卡。</p><p>因为涉及到正在运作的网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>安全问题，所以我们在这里以IP数据包为例做封包捕获，如果下面的某些数据类型您不是</p><p>很清楚，请您查阅MSDN：</p><div class="clear"></div></div>
<img src ="http://www.cppblog.com/eday/aggbug/15382.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eday/" target="_blank">松*</a> 2006-11-18 16:54 <a href="http://www.cppblog.com/eday/archive/2006/11/18/15382.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏外挂设计技术探讨 一</title><link>http://www.cppblog.com/eday/archive/2006/11/18/15380.html</link><dc:creator>松*</dc:creator><author>松*</author><pubDate>Sat, 18 Nov 2006 08:53:00 GMT</pubDate><guid>http://www.cppblog.com/eday/archive/2006/11/18/15380.html</guid><description><![CDATA[
		<p>网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>盛行催生了各种外挂，让商家头痛不已，那么外挂是怎么设计的呢？看看本文就知道了 </p>
		<p> </p>
		<p>一、 前言 </p>
		<p>　　所谓<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂，其实是一种<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外辅程序，它可以协助玩家自动产生<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>动作、修改<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>网络数据包以及修改<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>内</p>
		<p>存数据等，以实现玩家用最少的时间和金钱去完成功力升级和过关斩将。虽然，现在对<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂程序的“合法”身份众说</p>
		<p>纷纭，在这里我不想对此发表任何个人意见，让时间去说明一切吧。</p>
		<p>
				<br />　　随着网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>的时代的来临，<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂在原有的功能之上进行了新的发展，它变得更加多种多样，功能更加强大，操</p>
		<p>作更加简单，以至有些<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>的外挂已经成为一个体系，比如《石器时代》，外挂品种达到了几十种，自动战斗、自动行走</p>
		<p>、自动练级、自动补血、加速、不遇敌、原地遇敌、快速增加经验值、按键精灵……几乎无所不包。</p>
		<p>　　<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂的设计主要是针对于某个<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>开发的，我们可以根据它针对的<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>的类型可大致可将外挂分为两种大类。</p>
		<p>　　一类是将<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>中大量繁琐和无聊的<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">攻击</font></a>动作使用外挂自动完成，以帮助玩家轻松搞定<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">攻击</font></a>对象并可以快速的增加玩家</p>
		<p>的经验值。比如在《龙族》中有一种工作的设定，玩家的工作等级越高，就可以驾驭越好的装备。但是增加工作等级却不</p>
		<p>是一件有趣的事情，毋宁说是重复枯燥的机械劳动。如果你想做法师用的杖，首先需要做基本工作--?砍树。砍树的方法</p>
		<p>很简单，在一棵大树前不停的点鼠标就可以了，每10000的经验升一级。这就意味着玩家要在大树前不停的点击鼠标，这</p>
		<p>种无聊的事情通过"按键精灵"就可以解决。外挂的"按键精灵"功能可以让玩家摆脱无趣的点击鼠标的工作。</p>
		<p>　　另一类是由外挂程序产生欺骗性的网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>封包，并将这些封包发送到网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a><a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a>器，利用这些虚假信息欺骗<a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a></p>
		<p>器进行<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>数值的修改，达到修改角色能力数值的目的。这类外挂程序针对性很强，一般在设计时都是针对某个<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>某个</p>
		<p>版本来做的，因为每个网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a><a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a>器与客户端交流的数据包各不相同，外挂程序必须要对欺骗的网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a><a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a>器的数据</p>
		<p>包进行分析，才能产生<a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a>器识别的数据包。这类外挂程序也是当前最流利的一类<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂程序。</p>
		<p>　　另外，现在很多外挂程序功能强大，不仅实现了自动动作代理和封包功能，而且还提供了对网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>的客户端程序的</p>
		<p>数据进行修改，以达到欺骗网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a><a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a>器的目的。我相信，随着网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>商家的反外挂<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>的进展，<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂将会产生</p>
		<p>更多更优秀的<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>，让我们期待着看场<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>大战吧......</p>
		<p>　　三、外挂<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>综述</p>
		<p>　　可以将开发<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂程序的过程大体上划分为两个部分：</p>
		<p>　　前期部分工作是对外挂的主体<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>进行分析，不同类型的外挂分析主体<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>的内容也不相同。如外挂为上述谈到的外</p>
		<p>挂类型中的第一类时，其分析过程常是针对<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>的场景中的<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">攻击</font></a>对象的位置和分布情况进行分析，以实现外挂自动进行攻</p>
		<p>击以及位置移动。如外挂为外挂类型中的第二类时，其分析过程常是针对<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a><a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a>器与客户端之间通讯包数据的结构、内</p>
		<p>容以及加密算法的分析。因网络<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>公司一般都不会公布其<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>产品的通讯包数据的结构、内容和加密算法的信息，所以</p>
		<p>对于开发第二类外挂成功的关键在于是否能正确分析<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>包数据的结构、内容以及加密算法，虽然可以使用一些<a href="http://hackbase.com/hacker/tool" target="_blank"><font color="#000000">工具</font></a>辅助</p>
		<p>分析，但是这还是一种坚苦而复杂的工作。</p>
		<p>　　后期部分工作主要是根据前期对<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>的分析结果，使用大量的程序开发<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>编写外挂程序以实现对<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>的控制或修改</p>
		<p>。如外挂程序为第一类外挂时，通常会使用到鼠标模拟<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>来实现<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>角色的自动位置移动，使用键盘模拟<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>来实现游</p>
		<p>戏角色的自动<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">攻击</font></a>。如外挂程序为第二类外挂时，通常会使用到挡截Sock和挡截API函数<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>，以挡截<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a><a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a>器传来的</p>
		<p>网络数据包并将数据包修改后封包后传给<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a><a href="http://vip.hackbase.com/" target="_blank"><font color="#000000">服务</font></a>器。另外，还有许多外挂使用对<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>客户端程序内存数据修改<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>以及</p>
		<p>
				<a href="http://hackbase.com/game" target="_blank">
						<font color="#000000">游戏</font>
				</a>加速<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>。</p>
		<p>　　本文主要是针对开发<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂程序后期使用的程序开发<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>进行探讨，重点介绍的如下几种在<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂中常使用的程</p>
		<p>序开发<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>：</p>
		<p>　　● 动作模拟<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>：主要包括键盘模拟<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>和鼠标模拟<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>。</p>
		<p>　　● 封包<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>：主要包括挡截Sock<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>和挡截API<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>。</p>
		<p>四、动作模拟<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a></p>
		<p>　　我们在前面介绍过，几乎所有的<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>都有大量繁琐和无聊的<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">攻击</font></a>动作以增加玩家的功力，还有那些数不完的迷宫，这</p>
		<p>些好像已经成为了角色<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>的代名词。现在，外挂可以帮助玩家从这些繁琐而无聊的工作中摆脱出来，专注于<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>情节的</p>
		<p>进展。外挂程序为了实现自动角色位置移动和自动<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">攻击</font></a>等功能，需要使用到键盘模拟<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>和鼠标模拟<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>。下面我们将重</p>
		<p>点介绍这些<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>并编写一个简单的实例帮助读者理解动作模拟<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>的实现过程。</p>
		<p>　　１． 鼠标模拟<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a><br />　　<br />　　几乎所有的<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>中都使用了鼠标来改变角色的位置和方向，玩家仅用一个小小的鼠标，就可以使角色畅游天下。那么</p>
		<p>，我们如何实现在没有玩家的参与下角色也可以自动行走呢。其实实现这个并不难，仅仅几个Windows API函数就可以搞</p>
		<p>定，让我们先来认识认识这些API函数。</p>
		<p>　　(1) 模拟鼠标动作API函数mouse<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>event，它可以实现模拟鼠标按下和放开等动作。</p>
		<p>　　　　VOID mouse<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>event(<br />　　　　　　DWORD dwFlags, // 鼠标动作标识。<br />　　　　　　DWORD dx, // 鼠标水平方向位置。<br />　　　　　　DWORD dy, // 鼠标垂直方向位置。<br />　　　　　　DWORD dwData, // 鼠标轮子转动的数量。<br />　　　　　　DWORD dwExtraInfo // 一个关联鼠标动作辅加信息。<br />　　　　); </p>
		<p>　　其中，dwFlags表示了各种各样的鼠标动作和点击活动，它的常用取值如下：</p>
		<p>　　　MOUSEEVENTF<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>MOVE　表示模拟鼠标移动事件。</p>
		<p>　　　MOUSEEVENTF<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>LEFTDOWN 表示模拟按下鼠标左键。</p>
		<p>　　　MOUSEEVENTF<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>LEFTUP 表示模拟放开鼠标左键。</p>
		<p>　　　MOUSEEVENTF<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>RIGHTDOWN 表示模拟按下鼠标右键。</p>
		<p>　　　MOUSEEVENTF<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>RIGHTUP 表示模拟放开鼠标右键。</p>
		<p>　　　MOUSEEVENTF<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>MIDDLEDOWN 表示模拟按下鼠标中键。</p>
		<p>　　　MOUSEEVENTF<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>MIDDLEUP 表示模拟放开鼠标中键。</p>
		<p>　　(2)、设置和获取当前鼠标位置的API函数。获取当前鼠标位置使用GetCursorPos()函数，设置当前鼠标位置使用</p>
		<p>SetCursorPos()函数。</p>
		<p>　　　　BOOL GetCursorPos(<br />　　　　　LPPOINT　lpPoint // 返回鼠标的当前位置。<br />　　　　);<br />　　　　BOOL SetCursorPos(<br />　　　　int X, // 鼠标的水平方向位置。<br />　　　　　　int Y //鼠标的垂直方向位置。<br />　　　　);  </p>
		<p>　　通常<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>角色的行走都是通过鼠标移动至目的地，然后按一下鼠标的按钮就搞定了。下面我们使用上面介绍的API函</p>
		<p>数来模拟角色行走过程。</p>
		<p>　　　CPoint oldPoint,newPoint;<br />　　　GetCursorPos(&amp;oldPoint); //保存当前鼠标位置。<br />　　　newPoint.x = oldPoint.x+40;<br />　　　newPoint.y = oldPoint.y+10;<br />　　　SetCursorPos(newPoint.x,newPoint.y); //设置目的地位置。<br />　　　mouse<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>event(MOUSEEVENTF<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>RIGHTDOWN,0,0,0,0);//模拟按下鼠标右键。<br />　　　mouse<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>event(MOUSEEVENTF<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>RIGHTUP,0,0,0,0);//模拟放开鼠标右键。  </p>
		<p>　　2． 键盘模拟<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a></p>
		<p>　　在很多<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>中，不仅提供了鼠标的操作，而且还提供了键盘的操作，在对<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">攻击</font></a>对象进行<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">攻击</font></a>时还可以使用快捷键。为</p>
		<p>了使这些<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">攻击</font></a>过程能够自动进行，外挂程序需要使用键盘模拟<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>。像鼠标模拟<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>一样，Windows API也提供了一系列</p>
		<p>API函数来完成对键盘动作的模拟。</p>
		<p>　　模拟键盘动作API函数keydb<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>event，它可以模拟对键盘上的某个或某些键进行按下或放开的动作。</p>
		<p>　　　VOID keybd<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>event(<br />　　　　　BYTE bVk, // 虚拟键值。<br />　　　　　BYTE bScan, // 硬件扫描码。<br />　　　　　DWORD dwFlags, // 动作标识。<br />　　　　　DWORD dwExtraInfo // 与键盘动作关联的辅加信息。<br />　　　); </p>
		<p>　　其中，bVk表示虚拟键值，其实它是一个BYTE类型值的宏，其取值范围为1-254。有关虚拟键值表请在MSDN上使用关键</p>
		<p>字“Virtual-Key Codes”查找相关资料。bScan表示当键盘上某键被按下和放开时，键盘系统硬件产生的扫描码，我们可</p>
		<p>以MapVirtualKey()函数在虚拟键值与扫描码之间进行转换。dwFlags表示各种各样的键盘动作，它有两种取值：</p>
		<p>KEYEVENTF<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>EXTENDEDKEY和KEYEVENTF<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>KEYUP。</p>
		<p>　　下面我们使用一段<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>实现在<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>中按下Shift+R快捷键对<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">攻击</font></a>对象进行<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">攻击</font></a>。</p>
		<p>　　　keybd<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>event(VK<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>CONTROL,MapVirtualKey(VK<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>CONTROL,0),0,0); //按下CTRL键。<br />　　　keybd<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>event(0x52,MapVirtualKey(0x52,0),0,0);//键下R键。<br />　　　keybd<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>event(0x52,MapVirtualKey(0x52,0), KEYEVENTF<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>KEYUP,0);//放开R键。<br />　　　keybd<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>event(VK<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>CONTROL,MapVirtualKey(VK<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>CONTROL,0), <br />　　　KEYEVENTF<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>KEYUP,0);//放开CTRL键。 </p>
		<p>　　3． 激活外挂</p>
		<p>　　上面介绍的鼠标和键盘模拟<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>实现了对<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>角色的动作部分的模拟，但要想外挂能工作于<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>之上，还需要将其与</p>
		<p>
				<a href="http://hackbase.com/game" target="_blank">
						<font color="#000000">游戏</font>
				</a>的场景窗口联系起来或者使用一个激活键，就象按键精灵的那个激活键一样。我们可以用GetWindow函数来枚举窗口</p>
		<p>，也可以用Findwindow函数来查找特定的窗口。另外还有一个FindWindowEx函数可以找到窗口的子窗口，当<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>切换场景</p>
		<p>的时候我们可以用FindWindowEx来确定一些当前窗口的特征，从而判断是否还在这个场景，方法很多了，比如可以</p>
		<p>GetWindowInfo来确定一些东西，比如当查找不到某个按钮的时候就说明<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>场景已经切换了等等办法。当使用激活键进</p>
		<p>行关联，需要使用Hook<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>开发一个全局键盘钩子，在这里就不具体介绍全局钩子的开发过程了，在后面的实例中我们将</p>
		<p>会使用到全局钩子，到时将学习到全局钩子的相关<a href="http://hackbase.com/network/zs" target="_blank"><font color="#000000">知识</font></a>。</p>
		<p>
				<br />　　4． 实例实现</p>
		<p>　　通过上面的学习，我们已经基本具备了编写动作式<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂的能力了。下面我们将创建一个画笔程序外挂，它实现自</p>
		<p>动移<a href="http://hackbase.com/flash" target="_blank"><font color="#000000">动画</font></a>笔字光标的位置并写下一个红色的“R”字。以这个实例为基础，加入相应的<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>动作规则，就可以实现一个完</p>
		<p>整的<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>外挂。这里作者不想使用某个<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>作为例子来开发外挂（因没有<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>商家的授权啊！），如读者感兴趣的话可以</p>
		<p>找一个<a href="http://hackbase.com/game" target="_blank"><font color="#000000">游戏</font></a>试试，最好仅做测试<a href="http://hackbase.com/network" target="_blank"><font color="#000000">技术</font></a>用。</p>
		<p>　　首先，我们需要编写一个全局钩子，使用它来激活外挂，激活键为F10。创建全局钩子步骤如下：</p>
		<p>　　(1)．选择MFC AppWizard(DLL)创建项目ActiveKey，并选择MFC Extension DLL（共享MFC拷贝）类型。</p>
		<p>　　(2).插入新文件ActiveKey.h，在其中输入如下<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>：</p>
		<p>　　　#ifndef <a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>KEYDLL<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>H<br />　　　#define <a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>KEYDLL<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>H</p>
		<p>　　　class AFX<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>EXT<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>CLASS CKeyHook:public CObject<br />　　　{<br />　　　　public:<br />　CKeyHook();<br />　~CKeyHook();<br />　HHOOK Start();　//安装钩子<br />　BOOL Stop(); //卸载钩子<br />　　　};<br />　　　#endif  </p>
		<p>　　(3).在ActiveKey.cpp文件中加入声明＂#include ActiveKey.h＂。</p>
		<p>　　(4).在ActiveKey.cpp文件中加入共享数据段，<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>如下：</p>
		<p>　　　//Shared data section<br />　　　#pragma data<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>seg("sharedata")<br />　　　HHOOK glhHook=NULL; //钩子句柄。<br />　　　HINSTANCE glhInstance=NULL; //DLL实例句柄。<br />　　　#pragma data<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>seg()  </p>
		<p>　　(5).在ActiveKey.def文件中设置共享数据段属性，<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>如下：</p>
		<p>　　　SETCTIONS<br />　　　shareddata READ WRITE SHARED </p>
		<p>　　(6).在ActiveKey.cpp文件中加入CkeyHook类的实现<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>和钩子函数<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>：</p>
		<p>　　　//键盘钩子处理函数。<br />　　　extern "C" LRESULT WINAPI KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)<br />　　　{<br />　　　if( nCode &gt;= 0 )<br />　　　{<br />　　　if( wParam == 0X79 )//当按下F10键时，激活外挂。<br />　{<br />　　//外挂实现<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>。<br />CPoint newPoint,oldPoint;<br />　　 GetCursorPos(&amp;oldPoint);<br />　　 newPoint.x = oldPoint.x+40;<br />　　 newPoint.y = oldPoint.y+10;<br />　　 SetCursorPos(newPoint.x,newPoint.y);<br />　　 mouse<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>event(MOUSEEVENTF<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>LEFTDOWN,0,0,0,0);//模拟按下鼠标左键。<br />　　mouse<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>event(MOUSEEVENTF<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>LEFTUP,0,0,0,0);//模拟放开鼠标左键。<br />　　keybd<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>event(VK<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>SHIFT,MapVirtualKey(VK<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>SHIFT,0),0,0); //按下SHIFT键。<br />　　keybd<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>event(0x52,MapVirtualKey(0x52,0),0,0);//按下R键。<br />　　keybd<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>event(0x52,MapVirtualKey(0x52,0),KEYEVENTF<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>KEYUP,0);//放开R键。<br />　　keybd<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>event(VK<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>SHIFT,MapVirtualKey(VK<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>SHIFT,0),KEYEVENTF<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>KEYUP,0);//放开SHIFT键。<br />　　　　　　SetCursorPos(oldPoint.x,oldPoint.y);<br />　}<br />　　　}<br />　　　return CallNextHookEx(glhHook,nCode,wParam,lParam);<br />　　　}</p>
		<p>　　　CKeyHook::CKeyHook(){}<br />　　　CKeyHook::~CKeyHook()<br />　　　{　<br />　　　if( glhHook )<br />Stop();<br />　　　}<br />　　　//安装全局钩子。<br />　　　HHOOK CKeyHook::Start()<br />　　　{ <br />glhHook = SetWindowsHookEx(WH<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>KEYBOARD,KeyboardProc,glhInstance,0);//设置键盘钩子。<br />return glhHook;<br />}<br />　　　//卸载全局钩子。<br />　　　BOOL CKeyHook::Stop()<br />　　　{<br />　　　BOOL bResult = TRUE;<br />　if( glhHook )<br />　　　bResult = UnhookWindowsHookEx(glhHook);//卸载键盘钩子。<br />　　　return bResult;<br />　　　}  </p>
		<p>　　(7).修改DllMain函数，<a href="http://hackbase.com/hacker" target="_blank"><font color="#000000">代码</font></a>如下：</p>
		<p>　　　extern "C" int APIENTRY<br />　　　DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)<br />　　　{<br />//如果使用lpReserved参数则删除下面这行 <br />UNREFERENCED<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>PARAMETER(lpReserved);</p>
		<p>if (dwReason == DLL<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>PROCESS<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>ATTACH)<br />{<br />　　TRACE0("NOtePadHOOK.DLL Initializing!\n");<br />　　 //扩展DLL仅初始化一次 <br />　　if (!AfxInitExtensionModule(ActiveKeyDLL, hInstance))<br />return 0;<br />　　new CDynLinkLibrary(ActiveKeyDLL);<br />　　　　　　//把DLL加入<a href="http://hackbase.com/News/industry" target="_blank"><font color="#000000">动态</font></a>MFC类库中 <br />　　glhInstance = hInstance;<br />　　//插入保存DLL实例句柄 <br />}<br />else if (dwReason == DLL<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>PROCESS<a href="http://hackbase.com/game/game/2005022010070.html#" target="_blank"><font color="#000000">_</font></a>DETACH)<br />{<br />　　TRACE0("NotePadHOOK.DLL Terminating!\n");<br />　　//终止这个链接库前调用它 <br />　　AfxTermExtensionModule(ActiveKeyDLL);<br />}<br />return 1; <br />　　　} </p>
<img src ="http://www.cppblog.com/eday/aggbug/15380.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eday/" target="_blank">松*</a> 2006-11-18 16:53 <a href="http://www.cppblog.com/eday/archive/2006/11/18/15380.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>外挂浅谈</title><link>http://www.cppblog.com/eday/archive/2006/11/18/15374.html</link><dc:creator>松*</dc:creator><author>松*</author><pubDate>Sat, 18 Nov 2006 08:49:00 GMT</pubDate><guid>http://www.cppblog.com/eday/archive/2006/11/18/15374.html</guid><description><![CDATA[
		<div class="item-content">
				<p>　　现在大部分的主流外挂，包括按键精灵，自动做一些动作之类的外挂。 都是通过HOOK进游戏窗口，并且用不同的HOOK类型来完成的，比如对于 网络，通常是通过HOOK消息，把DLL弄到我们的游戏程序中，然后通过 <br />GetProcAddress得到DLL中它们卑劣的函数的地址和真实的函数地址， 然后通过WriteProcessMemory来把我们的函数地址改成他们他们的API 地址。 </p>
				<p>　　那么在我们的游戏执行的时候，收到消息就会先触发他们的钩子，等他们布置好邪恶的陷阱，然后再执行我们的程序。那么，对于这类型的外挂，该怎么防呢？俗话说得好，以彼之道，还制彼身。所谓魔高一尺，道高一丈。所谓邪不胜正。所谓天网恢恢。所谓做贼心虚。 </p>
				<p>　　恩，WINDOWS的钩子有个特点，就是钩子链，因为对于同一个进程，即使是同一类型的钩子，能同时有多个，也就是说，对于同一个游戏，开两个功能相同的外挂也可以。那么，怎么样来决定钩子的顺序呢？所谓 <br />后来者先得，WINDOWS的做法是，最后一个HOOK某个进程的钩子最先执行。并且振奋人心的消息是，在钩子里面可以控制下一个钩子是否执行。 </p>
				<p>　　这个函数是CallNextHookEx，也就是说，如果在某个钩子里面不执行这个函数的话，钩子链就会在这中断，那么我们的思路就很简单了，在游戏运行中，开一个进程，每隔一段时间就hook我们的主程序，然后在钩 <br />子里面，不执行CallNextHookEx，这样就可以避免别人的钩子执行。 </p>
				<p>　　所谓，仅仅发现敌人还不够，还要消灭敌人。不急，我们来看看如何彻底把敌人打成粉碎性骨折。恩，要消灭敌人就要复杂一点了，要针对不同类型的钩子来采取不同类型的方法了，因为敌人的钩子不管怎么做， <br />无非是出于两种目的，一种是修改数据，另外就是过滤数据。其核心思想就是我们自己的游戏注册两个钩子，一个总是在钩子链的最底层，另外一个总是在钩子链的最上层，一比较两个钩子收到的消息，就知道中 <br />间有没有别的钩子了。一但发现有别的钩子，不用想了，肯定是外挂，最少也用了按键精灵，封号，杀档，想怎么干就怎么干。 </p>
				<p>　　当然，敌人也不是这么脆弱的，据说有人用raw socket来截获所有的网络消息，这个跟钩子无关，这个更底层一些。不过不用怕，俗话说得好，以彼之道，还制彼身。所谓魔高一尺，道高一丈。所谓邪不胜正。所谓 <br />天网恢恢。所谓做贼心虚。对付拦截raw socket的外挂，这里首先要把敌人分类，对于水平最次的敌人，方法也相应要简单得多。 最次的敌人一般用的方法是自己写一个wsock32.dll放在和游戏相同的目录下，来替换掉系统的wsock32.dll。对于这种愚蠢的方法，解决的办法有很多，把load time的载入wsock32.dll改成run time的载入，然后指定一下路径，就什么问题都没有了。更简单点，运行游戏的时候检查一下当前目录下有没有wsock32.dll，有的话，那就肯定是外挂钩子了。 </p>
				<p>　　比最等级高一点点的敌人，会喜欢用钩子来直接钩，一般的做法是，先通过GetProcAddress来获取SOCKET API的地址，然后通过WriteProcessMemory的方法来修改入口地址，将其改成jmp 自己的函数地址。这种方法其实很卑劣的，所谓无毒不丈夫，我们可以通过修改通用的GetProcAddress的代码来防止别人拦截SOCKET，这样，直接连防火墙都可以突破了。 </p>
				<p>　　这里的技术难点在于，我们不能用类似WriteProcessMemory的方法来写内存因为我们不知道究竟外挂是哪个进程，所以我们需要修改WINDOWS的代码段，这样讲理论上是不可能的，可惜WINDOWS自己给自己留了个后门，在kernel.dll　里面，UINT AllocCsToDsAlias(UINT)，通过把API的代码段的选择符传给它，可以返回一个可以写的数据段的选择符，然后把新的选择符和API的入口地址加在一起，就可以得到一个可以写的代码段的指针。 </p>
				<p>　　国内好多取词软件和全屏翻译软件都是用的这个原理，具体的例子如下：<br />　　比如GetProcAddress这个API，在kernel.dll里面，我们要做针对它的通用钩子就应该按照以下的步簇： <br />typedef UINT (WINAPI* FOO)(UINT); <br />FOO AllocCsToDsAlias; <br />HMODULE hKernel = GetModuleHandle("kernel"); <br />AllocCsToDsAlias = (FOO)GetProcAddress(hKernel, "AllocCsToDsAlias"); <br />FARPROC entry = GetProcAddress(hKernel, "GetProcAddress"); <br />WORD offset = (WORD)(FP_OFF(entry)); <br />UINT selector = AllocCsToDsAlias(FP_SEG(entry)); <br />BYTE *addr = (BYTE *)MK_FP(selector, offset); </p>
				<p>　　然后就可以往addr这个地址写5个BYTE的东西，第一个BYTE是jmp，不同的CPU　可能会不同，之后的一个DWORD是你的函数的地址。。 </p>
				<p>　　这样，可以通过保存两份addr的数据来做到钩子的开关，当钩子打开的时候，所有的GetProcAddress的调用都会调到我们的函数，这个时候可以通过检查如果有人想GetProcAddress wsock32.dll里的东西，我们就干掉它。。 </p>
				<p>　　同样，如果有人通过开启socket这个API的SOCK_RAW参数来调用RAW SOCKET监视网络，我们也可以通过上面的方法来做到先入为主，看谁在监视我们的网络传输。</p>
				<p>　　其实查外挂的原理和捉病毒的原理一样，只是现在做外挂的技术还不成熟。当做外挂的技术和防外挂的技术在同一条线上的时候，想从技术上防住外挂是不可能的。 </p>
				<p>　　其实是无奈，杀毒软件的做法是出来一种新的病毒，在第一时间内公布其特征码，然后更新所有的客户端，看到这种特征的，就杀。其实查杀外挂也该如此，其实CS的Cheating-Death就是这个原理，不管出了什么新的外挂，CD 总是在第一时间内更新，然后客户端也就傻傻的只要一看到有这个特征的东西就把它干掉。其实仔细想想，这是个很好的主意，从外挂开发商的动机来分析。他们之所以要开发外挂，估计炫耀技术是一方面，更重要的还是想赚钱，或者用来挂机之类的，既然要赚钱，或者挂机，就必然会被其他玩家发现或者举报，这个时候应该做的就是尽快的下载一份外挂，或者根据其行为研究其特征，并在防外挂的特征码上记下一笔，其实不用太复杂，最简单的做法就是记住其外挂窗口的名称，然后只要看到这个外挂窗口，做些处理就好了。 </p>
				<p>　　那么这样一来，关键的技术就落在如何设计查外挂这个结构上了，首先客户端需要的是一个查外挂引擎，和一个外挂特征库，根据引擎和特征库生成一个固定的版本号，然后每次登陆的时候就用这个版本号，跟服务器上的版本号对比，如果不一样的话，则从服务器自动下载最新的查外挂引擎和外挂特征库。 </p>
				<p>　　这样虽然不能将外挂赶尽杀绝，却能比较大的限度上围剿使用外挂的风气，相信使用外挂的玩家也只是想更好的玩游戏，只不过动的念头有些歪了，但其出发点仍是好的，只要在他使用外挂的时候多些阻拦或者诱导他不使用外挂，这样效果就会好很多。设想一下，谁愿意每天等一个外挂的更新，而不去玩他很想玩的游戏呢。这样一来，制作外挂的人也会逐渐减少，从而进入一个良性循环。 </p>
				<p>　　就好象如今写病毒，并不如当年那么流行了。。-。-<br /></p>
				<div class="clear">
				</div>
		</div>
<img src ="http://www.cppblog.com/eday/aggbug/15374.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eday/" target="_blank">松*</a> 2006-11-18 16:49 <a href="http://www.cppblog.com/eday/archive/2006/11/18/15374.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>金山词霸的取词原理</title><link>http://www.cppblog.com/eday/archive/2006/11/18/15371.html</link><dc:creator>松*</dc:creator><author>松*</author><pubDate>Sat, 18 Nov 2006 08:47:00 GMT</pubDate><guid>http://www.cppblog.com/eday/archive/2006/11/18/15371.html</guid><description><![CDATA[取词的过程 <br />0 判断鼠标是否在一个地方停留了一段时间 <br />1 取得鼠标当前位置 <br />2 以鼠标位置为中心生成一个矩形 <br />3 挂上API钩子 <br />4 让这个矩形产生重画消息 <br />5 在钩子里等输出字符 <br />6 计算鼠标在哪个单词上面，把这个单词保存下来 <br />7 如果得到单词则摘掉API钩子，在一段时间后，无论是否得到单词都摘掉API钩子 <br />8 用单词查词库，显示解释框 <br /><br />如何挂钩子： <br />所谓钩子其实就是在WindowsAPI入口写一个JMP XXXX:XXXX语句，跳转到自己的代码里。 <br />步骤如下： <br />1.取得Windows API入口，用GetProcAddress实现 <br />2.保存API入口的前五个字节，因为JMP是0xEA，地址是4个字节 <br />3.写入跳转语句 <br />这步最复杂 <br />Windows的代码段本来是不可以写的，但是Microsoft给自己留了个后门。 <br />有一个未公开函数是AllocCsToDsAlias， <br />UINT WINAPI ALLOCCSTODSALIAS(UINT); <br />你可以取到这个函数的入口，把API的代码段的选择符传给他，他会返回一个可写的数据段选择符。这个选择符用完要释放的。用新选择符和API入口的偏移量合成一个指针就可以写windows的代码段了。 <img src ="http://www.cppblog.com/eday/aggbug/15371.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eday/" target="_blank">松*</a> 2006-11-18 16:47 <a href="http://www.cppblog.com/eday/archive/2006/11/18/15371.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汉化Windows重定位技术</title><link>http://www.cppblog.com/eday/archive/2006/11/18/15372.html</link><dc:creator>松*</dc:creator><author>松*</author><pubDate>Sat, 18 Nov 2006 08:47:00 GMT</pubDate><guid>http://www.cppblog.com/eday/archive/2006/11/18/15372.html</guid><description><![CDATA[摘 要: 四通利方(RichWin)，中文之星(CStar)是大家广为熟知的汉化Windows产品，<br />"陷阱”技术即动态修改Windows代码，一直是其对外宣称的过人技术，它究竟是如<br />何实现的，这自然是核心机密。本文试图解开这个秘密，并同时介绍Windows的模块<br />调用机制与重定位概念，并给出了采用"陷阱"技术动态修改Windows代码的示例源程<br />序。 <br />关键词：汉化Windows重定位技术 <br /><br />一、发现了什么？<br />作者多年来一直从事Windows下的软件开发工作，经历<br />了Windows2.0、3.0、3.1,直至WindowsNT,95的成长过程，也遍历<br />了长青窗口、长城窗口、DBWin、CStar、RichWin等多个Windows汉化<br />产品。从现在看来，影响最大也最为成功的，当推四通利方<br />的RichWin，此外，中文之星CStar与RichWin师出一门，其核心技术<br />自然也差不许多。其对外宣传采用独特的“陷阱”技术动态<br />修改Windows代码，一直是作者感兴趣的地方。<br /><br />EXEHDR是MicrosoftVisualC++开发工具中很有用的一个程<br />序，它可以检查NE（New_Executable）格式文件，用它来分析RichWin<br />的WSENGINE.DLL或CStar的CHINESE.DLL就会发现与众不同的两点：<br />（ 以CStar 1.20 为 例） <br />C:\CSTAR&gt;exehdr chinese.dll /v<br />..................................<br />6 type offset target<br />BASE060aseg 2 offset 0000<br />PTR 047eimp GDI.GETCHARABCWIDTHS<br />PTR 059bimp GDI.ENUMFONTFAMILIES<br />PTR 0451imp DISPLAY.14( EXTTEXTOUT )<br />PTR 0415imp KEYBOARD.4( TOASCII )<br />PTR 04baimp KEYBOARD.5( ANSITOOEM )<br />PTR 04c9imp KEYBOARD.6( OEMTOANSI )<br />PTR 04d8imp KEYBOARD.134( ANSITOOEMBUFF)<br />PTR 05f5imp USER.430( LSTRCMP )<br />PTR 04e7imp KEYBOARD.135( OEMTOANSIBUFF)<br />PTR 0514imp USER.431( ANSIUPPER)<br />PTR 0523imp USER.432( ANSILOWER )<br />PTR 05aaimp GDI.56( CREATEFONT)<br />PTR 056eimp USER.433( ISCHARALPHA )<br />PTR 05b9imp GDI.57( CREATEFONTINDIRECT )<br />PTR 057dimp USER.434( ISCHARALPHANUMERIC )<br />PTR 049cimp USER.179( GETSYSTEMMETRICS )<br />PTR 0550imp USER.435( ISCHARUPPER)<br />PTR 055fimp USER.436( ISCHARLOWER)<br />PTR 0532imp USER.437( ANSIUPPERBUFF)<br />PTR 0541imp USER.438( ANSILOWERBUFF)<br />PTR 05c8imp GDI.69( DELETEOBJECT )<br />PTR 058cimp GDI.70( ENUMFONTS )<br />PTR 04abimp KERNEL.ISDBCSLEADBYTE<br />PTR 05d7imp GDI.82( GETOBJECT)<br />PTR 048dimp KERNEL.74 ( OPENFILE )<br />PTR 0460imp GDI.91( GETTEXTEXTENT)<br />PTR 05e6imp GDI.92( GETTEXTFACE)<br />PTR 046fimp GDI.350 ( GETCHARWIDTH )<br />PTR 0442imp GDI.351 ( EXTTEXTOUT )<br />PTR 0604imp USER.471( LSTRCMPI )<br />PTR 04f6imp USER.472( ANSINEXT )<br />PTR 0505imp USER.473( ANSIPREV )<br />PTR 0424imp USER.108( GETMESSAGE )<br />PTR 0433imp USER.109( PEEKMESSAGE)<br />35 relocations<br /><br />*******扩号内为作者加上的对应WindowsAPI函数<br />第一，在数据段中，发现了重定位信息。<br />第二，这些重定位信息提示的函数，全都与文字显示<br />输出和键盘，字符串有关。也就是说汉化Windows，必须修改这些函数。<br />在这非常特殊的地方，隐藏着什么呢？无庸致疑，这与众不同的两点，对打开“陷阱”技术之门而言，不是金钥匙，也是敲门砖。<br /><br />二、Windows的模块调用机制与重定位概念<br />为了深入探究“陷阱”技术，我们先来介绍Windows的模块调用机制。Windows的运行分实模式（RealMode），标准模式（StandMode）和增强模式（386EnhancedMode）三种，虽然这几种模式各不相同，但其核心模块的调用关系却是完全一致的。<br />主要的三个模块，有如下的关系：<br /><br />KERNEL是Windows系统内核，它不依赖其它模块。<br /><br />GDI是Windows图形设备接口模块，它依赖于KERNEL模块。<br /><br />USER是Windows用户接口服务模块，它依赖于KERNEL，GDI模块及设备驱动程序等所有模块。<br />这三个模块，实际上就是Windows的三个动态连接库，在系统的存在形式如下，KERNEL有三种不同形式，Kernel.exe(实模式),Krnl286.exe(标准模式),Krnl386.<br />exe(386增强模式)；GDI模块是Gdi.exe；USER模块是User.exe，虽然文件名都以EXE为扩展名，但它们实际都是动态连接库。<br /><br />同时，几乎所有的API函数都隐藏在这三个模块中。用EXEHDR对这三个模块分析，就可列出一大堆你所熟悉的WindowsAPI函数。<br /><br />以GDI模块为例，<br /><br />C:\WINDOWS\SYSTEM&gt;exehdr gdi.exe<br />Exports:<br />ord seg offset name<br />............<br />351 1923eEXTTEXTOUT exported, shared data<br />56 319e1CREATEFONT exported, shared data<br />............<br /><br />至此，你已能从Windows纷繁复杂的系统中，理出一些头续来。下面，再引入一个重要概念——重定位。<br />一个Windows执行程序对调用API函数，或对其它动态库的调用，在程序装入内存前，都是一些不能定位的动态连接，当程序调入内存时，这些远调用都需要重新定位，重新定位的依据就是重定位表。在Windows执行程序（包括动态库）的每个段后面，通常都跟有这样一个重定位表。重定位包含调用函数所在模块，函数序列号，以及定位在模块中的位置。<br /><br />例如，用EXEHDR/v分析CHINESE.DLL得到<br /><br />6 type offset target<br />..........<br />PTR 0442imp GDI.351 <br />..........<br /><br />就表明，在本段的0442H偏移处，调用了GDI的第351号函数。如果在0442H处是0000:FFFF，则表示，本段内仅此一处调用了GDI.351函数，否则，表明了本段内还有一处调用此函数，调用的位置就是0442H处所指向的内容，实际上重定位表只含有引用位置的链表的链头。那么，GDI.351是一个什么函数呢？还是用EXEHDR对GDI.EXE作一分析，就可得出，在GDI的出口（Export）函数中，第351号是ExtTextOut。<br />这样，我们在EXEHDR这一简单而非常有用的工具帮助下，已经在Windows的浩瀚海洋中畅游了一会，下面就来掀开“陷阱”技术的神秘面纱。<br /><br />三、动态汉化Windows原理<br />我们知道，传统的汉化Windows的方法，是要直接修改Windows的显示、输入、打印等模块代码，或用DDK直接开发“中文设备”驱动模块，这样不仅工作量浩大，而且，系统的完备性很难保证，性能上也有很多限制（早期的长青窗口就是这样），这样，只有从内核上修改Windows核心代码才是最彻底的办法。<br />从Windows的模块调用机制，我们可以看到，Windows实际上是由包括在KERNEL，GDI，USER等几个模块中的众多函数支撑的。那么，修改其中涉及语言文字处理的函数，使之能适应中文需要，不就能达到汉化目的了吗？因而，我们可以得出这样的结论：在自己的模块中重新编写涉及文字显示，输入的多个函数，然后，将Windows中对这些函数的引用，改向到自己的这些模块中来。<br /><br />修改哪些函数才能完成汉化，这需要深入分析Windows的内部结构，但CHINESE.DLL已明确无误地告诉了我们，在其数据段的重定位表中列出的引用函数，正是CStar修改了的Windows函数！<br /><br />为了验证这一思路，我们利用RichWin作一核实。<br /><br />用EXEHDR分析GDI.EXE，得出ExtTextOut函数在GDI的第一代码段6139H偏移处（不同版本的Windows其所在代码段和偏移可能不一样）。然后，用HelpWalk（也是MicrosoftVisualC++开发工具中的一个）检查GDI的Code1段，6139H处前5个字节是B8FF054555，经过运行RichWin4.3forInternet后，再查看同样的地方，已改为EA08088F3D,其实反汇编就知道，这5个字节就是代表Jmp3D8F:0808，而句柄为0x3D8F的模块，用HelpWalk能观察到正是RichWin的WSENGINE.DLL的第一代码段（模块名为TEXTMAN）。而偏移0808H处B8B73D45558BEC1E，正是一个函数起始的地方，这实际上就是RichWin所重改写的ExtTextOut函数。退出RichWin后，再用HelpWalk观察GDI的Code1代码段，一切又恢复正常！这与前面的分析结论完全吻合！那么，下一个关键点就是如何动态修改Windows的函数代码，也就是汉化Windows的核心——“陷阱”技术。<br /><br />四、“陷阱”技术<br />讨论“陷阱”技术，还要回到前面的两个发现。发现之二，已能解释为修改的Windows函数，而发现之一，却仍是一个迷。<br />数据段存放的是变量及常量等内容，如果这里面包含有重定位信息，那么，必定要在变量说明中将函数指针赋给一个FARPROC类型的变量，于是，在变量说明中写下：<br />FARPROC FarProcFunc=ExtTextOut;<br />果然，我自己程序的数据段中也有了重定位信息。这样，当程序调入内存中时，变量FarProcFunc已是函数ExtTextOut的地址了。<br /><br />要直接修改代码段的内容，还遇到一个难题，就是代码段是不可改写的。这时，需要用到一个未公开的Windows函数AllocCStoDSAlias取得与代码段有相同基址的可写数据段别名，其函数声明为<br />WORD FAR PASCAL AllocCStoDSAlias(WORD code_sel);<br />参数是代码段的句柄，返回值是可写数据段别名句柄。<br /><br />Windows中函数地址是32位，高字是其模块的内存句柄，低字是函数在模块内的偏移。将得到的可写数据段别名句柄锁定，再将函数偏移处的5个字节保留下来，然后将其改为转向替代函数（用EA Jmp）<br /><br />*(lpStr+wOffset)=0xEA;<br />*(lpStr+wOffset+1)=lpFarProcReplace;<br /><br />反汇编即是Jmp lpFarProcReplace，最后，内存解锁。<br />这就是我们为Windows设的“陷阱”，当所有对此函数的调用都无条件地转到我们规定的替代函数处。当程序结束之前，将保留的5字节内容再置回来，否则，系统会崩溃。<br /><br />下面给出作者编写的使Windows的ExtTextOut函数落入自己函数“陷阱”的源程序。<br /><br />//源程序 relocate.c<br />#include <br />#include <br /><br />BOOL WINAPI MyExtTextOut(HDC hDC, int x, int y, UINT nInt1, const RECT FAR* lpRect,LPCSTR lpStr, UINT nInt2, int FAR* lpInt);<br />WORD FAR PASCAL AllocCStoDSAlias(WORD code_sel);<br /><br />typedef struct tagFUNC<br />{ <br />FARPROC lpFarProcReplace;//替代函数地址<br />FARPROC lpFarProcWindows;//Windows函数地址<br />BYTE bOld;//保存原函数第一字节<br />LONG lOld;//保存原函数接后的四字节长值<br />}FUNC;<br /><br />FUNC Func={MyExtTextOut,ExtTextOut};<br /><br />//Windows主函数<br />int PASCAL WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)<br />{<br />HANDLE hMemCode;//代码段句柄<br />WORD hMemData;//相同基址的可写数据段别名 <br />WORD wOffset; //函数偏移<br />LPSTR lpStr;<br />LPLONG lpLong;<br />char lpNotice[96];<br /><br /><br />hMemCode=HIWORD((LONG) Func.lpFarProcWindows );<br />wOffset=LOWORD((LONG) Func.lpFarProcWindows );<br /><br />wsprintf(lpNotice,"函数所在模块句柄 0x%4xH,偏移 0x%4xH",hMemCode,wOffset);<br />MessageBox(NULL,lpNotice,"提示",MB_OK);<br /><br />//取与代码段有相同基址的可写数据段别名 <br />hMemData=AllocCStoDSAlias(hMemCode);<br /><br />lpStr=GlobalLock(hMemData); <br /><br />lpLong=(lpStr+wOffset+1 );<br />//保存原函数要替换的头几个字节<br />Func.bOld=*(lpStr+wOffset);<br />Func.lOld=*lpLong;<br /><br />*(lpStr+wOffset)=0xEA; <br />*lpLong=Func.lpFarProcReplace;<br />GlobalUnlock(hMemData);<br /><br />MessageBox(NULL,"改为自己的函数","提示",MB_OK);<br /><br />//将保留的内容改回来<br />hMemData=AllocCStoDSAlias(hMemCode);<br />lpStr=GlobalLock(hMemData); <br />lpLong=(lpStr+wOffset+1 );<br />*(lpStr+wOffset)=Func.bOld; <br />*lpLong=Func.lOld;<br />GlobalUnlock(hMemData);<br /><br />MessageBox(NULL,"改回原Windows函数","提示",MB_OK);<br />return 1;<br />}<br /><br />//自己的替代函数<br />BOOL WINAPI MyExtTextOut(HDC hDC, int x, int y, UINT nInt1, const RECT FAR* lpRect, LPCSTR lpStr, UINT nInt2, int FAR* lpInt)<br />{<br />BYTE NameDot[96]={<br />0x09, 0x00, 0xfd, 0x08, 0x09, 0x08, 0x09, 0x10, 0x09, 0x20, <br />0x79, 0x40, 0x41, 0x04, 0x47, 0xfe, 0x41, 0x40, 0x79, 0x40, <br />0x09, 0x20, 0x09, 0x20, 0x09, 0x10, 0x09, 0x4e, 0x51, 0x84, <br />0x21, 0x00, 0x02, 0x00, 0x01, 0x04, 0xff, 0xfe, 0x00, 0x00, <br />0x1f, 0xf0, 0x10, 0x10, 0x10, 0x10, 0x1f, 0xf0, 0x00, 0x00, <br />0x7f, 0xfc, 0x40, 0x04, 0x4f, 0xe4, 0x48, 0x24, 0x48, 0x24, <br />0x4f, 0xe4, 0x40, 0x0c, 0x10, 0x80, 0x10, 0xfc, 0x10, 0x88, <br />0x11, 0x50, 0x56, 0x20, 0x54, 0xd8, 0x57, 0x06, 0x54, 0x20, <br />0x55, 0xfc, 0x54, 0x20, 0x55, 0xfc, 0x5c, 0x20, 0x67, 0xfe, <br />0x00, 0x20, 0x00, 0x20, 0x00, 0x20<br />}; <br /><br />HBITMAP hBitmap,hOldBitmap;<br />HDC hMemDC;<br />BYTE far *lpDot;<br />int i;<br /><br />for ( i=0;i&lt;3;i++ )<br />{<br />lpDot=(LPSTR)NameDot+i*32;<br />hMemDC=CreateCompatibleDC(hDC);<br />hBitmap=CreateBitmap(16,16,1,1,lpDot);<br />SetBitmapBits(hBitmap,32L,lpDot);<br />hOldBitmap=SelectObject(hMemDC,hBitmap);<br />BitBlt(hDC,x+i*16,y,16,16,hMemDC,0,0,SRCCOPY);<br />DeleteDC(hMemDC);<br />DeleteObject(hBitmap);<br />}<br /><br />return TRUE;<br />}<br /><br />//模块定义文件relocate.def<br />NAMERELOCATE<br />EXETYPE WINDOWS<br />CODEPRELOAD MOVEABLE DISCARDABLE<br />DATAPRELOAD MOVEABLE MULTIPLE<br />HEAPSIZE1024<br />EXPORTS<br /><br />五、结束语<br />本文从原理上分析了称为“陷阱”技术的汉化Windows方法。要彻底汉化Windows还要涉及显示，键盘输入等诸多内容，决非一日之功。但作为对“陷阱”技术的分析，本文介绍了将任一Windows函数调用改向到自己指定函数处的通用方法，这种方法可以拓展到其它应用中，如多语种显示，不同内码制式的切换显示等。<br />参考文献：<br />AndrewSchulmanDavidMaxeyMattPietrek,《未公开的Windows核心技术》，清华大学出版社，1993年。<br /><br />王志东，“Windows中文环境”，《Windows软件的应用与开发》，1993.5。<br />（作者地址：山东潍坊华光科技股份公司研究开发中心 张高峰 邮编261041 ）<img src ="http://www.cppblog.com/eday/aggbug/15372.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eday/" target="_blank">松*</a> 2006-11-18 16:47 <a href="http://www.cppblog.com/eday/archive/2006/11/18/15372.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏外挂设计技术探讨(下)</title><link>http://www.cppblog.com/eday/archive/2006/11/18/15363.html</link><dc:creator>松*</dc:creator><author>松*</author><pubDate>Sat, 18 Nov 2006 08:36:00 GMT</pubDate><guid>http://www.cppblog.com/eday/archive/2006/11/18/15363.html</guid><description><![CDATA[五、封包<b>技术</b><br /><br />　　通过对动作模拟<b>技术</b>的介绍，我们对<b>游戏</b>外挂有了一定程度上的认识，也学会了使用动作模拟<b>技术</b>来实现简单的动作模拟型<b>游戏</b>外挂的制作。这种动作模拟型<b>游戏</b>外挂有一定的局限性，它仅仅只能解决使用计算机代替人力完成那么有规律、繁琐而无聊的<b>游戏</b>动作。但是，随着网络<b>游戏</b>的盛行和复杂度的增加，很多<b>游戏</b>要求将客户端动作信息及时反馈回服务器，通过服务器对这些动作信息进行有效认证后，再向客户端发送下一步<b>游戏</b>动作信息，这样动作模拟<b>技术</b>将失去原有的效应。为了更好地“外挂”这些<b>游戏</b>，<b>游戏</b>外挂程序也进行了升级换代，它们将以前针对<b>游戏</b>用户界面层的模拟推进到数据通讯层，通过封包<b>技术</b>在客户端挡截<b>游戏</b>服务器发送来的<b>游戏</b>控制数据包，分析数据包并修改数据包；同时还需按照<b>游戏</b>数据包结构创建数据包，再模拟客户端发送给<b>游戏</b>服务器，这个过程其实就是一个封包的过程。<br /><br />　　封包的<b>技术</b>是实现第二类<b>游戏</b>外挂的最核心的<b>技术</b>。封包<b>技术</b>涉及的知识很广泛，实现方法也很多，如挡截WinSock、挡截API函数、挡截消息、VxD驱动程序等。在此我们也不可能在此文中将所有的封包<b>技术</b>都进行详细介绍，故选择两种在<b>游戏</b>外挂程序中最常用的两种方法：挡截WinSock和挡截API函数。<br /><br />　　1． 挡截WinSock<br /><br />　　众所周知，Winsock是<b>Windows</b>网络编程接口，它工作于<b>Windows</b>应用层，它提供与底层传输协议无关的高层数据传输编程接口。在<b>Windows</b>系统中，使用WinSock接口为应用程序提供基于TCP/IP协议的网络访问服务，这些服务是由Wsock32.DLL动态链接库提供的函数库来完成的。<br /><br />　　由上说明可知，任何<b>Windows</b>基于TCP/IP的应用程序都必须通过WinSock接口访问网络，当然网络<b>游戏</b>程序也不例外。由此我们可以想象一下，如果我们可以控制WinSock接口的话，那么控制<b>游戏</b>客户端程序与服务器之间的数据包也将易如反掌。按着这个思路，下面的工作就是如何完成控制WinSock接口了。由上面的介绍可知，WinSock接口其实是由一个动态链接库提供的一系列函数，由这些函数实现对网络的访问。有了这层的认识，问题就好办多了，我们可以制作一个类似的动态链接库来代替原WinSock接口库，在其中实现WinSock32.dll中实现的所有函数，并保证所有函数的参数个数和顺序、返回值类型都应与原库相同。在这个自制作的动态库中，可以对我们感兴趣的函数（如发送、接收等函数）进行挡截，放入外挂控制代码，最后还继续调用原WinSock库中提供的相应功能函数，这样就可以实现对网络数据包的挡截、修改和发送等封包功能。<br /><br />　　下面重点介绍创建挡截WinSock外挂程序的基本步骤：<br /><br />　　(1) 创建DLL项目，选择Win32 Dynamic-Link Library，再选择An empty DLL project。<br /><br />　　(2) 新建文件wsock32.h，按如下步骤输入代码：<br /><br />　　① 加入相关变量声明：<br /><br />　　　HMODULE hModule=NULL; //模块句柄<br />　　　char buffer[1000]; //缓冲区<br />　　　FARPROC proc; //函数入口指针 <br /><br />　　② 定义指向原WinSock库中的所有函数地址的指针变量，因WinSock库共提供70多个函数，限于篇幅，在此就只选择几个常用的函数列出，有关这些库函数的说明可参考MSDN相关内容。<br /><br />　　　//定义指向原WinSock库函数地址的指针变量。<br />　　　SOCKET (__stdcall *socket1)(int ,int,int);//创建Sock函数。<br />　　　int　(__stdcall *WSAStartup1)(WORD,LPWSADATA);//初始化WinSock库函数。<br />　　　int　(__stdcall *WSACleanup1)();//清除WinSock库函数。<br />　　　int (__stdcall *recv1)(SOCKET ,char FAR * ,int ,int );//接收数据函数。<br />　　　int (__stdcall *send1)(SOCKET ,const char * ,int ,int);//发送数据函数。<br />　　　int (__stdcall *connect1)(SOCKET,const struct sockaddr *,int);//创建连接函数。<br />　　　int (__stdcall *bind1)(SOCKET ,const struct sockaddr *,int );//绑定函数。<br />　　　......其它函数地址指针的定义略。 <br /><br />　　(3) 新建wsock32.cpp文件，按如下步骤输入代码：<br /><br />　　① 加入相关头文件声明：<br /><br />　　　#include "windows.h"<br />　　　#include "stdio.h"<br />　　　#include "wsock32.h" <br /><br />　　② 添加DllMain函数，在此函数中首先需要加载原WinSock库，并获取此库中所有函数的地址。代码如下：<br /><br />　　　BOOL WINAPI DllMain (HANDLE hInst,ULONG ul_reason_for_call,LPVOID lpReserved)<br />　　　{<br />　　　　if(hModule==NULL){<br />　　　　　//加载原WinSock库，原WinSock库已复制为wsock32.001。<br />　　　hModule=LoadLibrary("wsock32.001"); <br />　　}<br />　　　　else return 1;<br />//获取原WinSock库中的所有函数的地址并保存，下面仅列出部分代码。<br />if(hModule!=NULL){<br />　　　　　//获取原WinSock库初始化函数的地址，并保存到WSAStartup1中。<br />proc=GetProcAddress(hModule,"WSAStartup");<br />　　　WSAStartup1=(int (_stdcall *)(WORD,LPWSADATA))proc;<br />　　　　　//获取原WinSock库消除函数的地址，并保存到WSACleanup1中。<br />　　　　proc=GetProcAddress(hModule i,"WSACleanup");<br />　　　　WSACleanup1=(int (_stdcall *)())proc;<br />　　　　　//获取原创建Sock函数的地址，并保存到socket1中。<br />　　　　proc=GetProcAddress(hModule,"socket");<br />　　　　　socket1=(SOCKET (_stdcall *)(int ,int,int))proc;<br />　　　　　//获取原创建连接函数的地址，并保存到connect1中。<br />　　　　　proc=GetProcAddress(hModule,"connect");<br />　　　　　connect1=(int (_stdcall *)(SOCKET ,const struct sockaddr *,int ))proc;<br />　　　　　//获取原发送函数的地址，并保存到send1中。<br />　　　　　proc=GetProcAddress(hModule,"send");<br />　　　　　send1=(int (_stdcall *)(SOCKET ,const char * ,int ,int ))proc;<br />　　　　　//获取原接收函数的地址，并保存到recv1中。<br />　　　　　proc=GetProcAddress(hModule,"recv");<br />　　　　　recv1=(int (_stdcall *)(SOCKET ,char FAR * ,int ,int ))proc;<br />　　　　　......其它获取函数地址代码略。<br />　　　}<br />　　　else return 0;<br />　　　return 1;<br />} <br /><br />　　③ 定义库输出函数，在此可以对我们感兴趣的函数中添加外挂控制代码，在所有的输出函数的最后一步都调用原WinSock库的同名函数。部分输出函数定义代码如下：<br /><br />//库输出函数定义。<br />//WinSock初始化函数。<br />　　　　int PASCAL FAR WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData)<br />　　　　{<br />　　　　　//调用原WinSock库初始化函数<br />　　　　　return WSAStartup1(wVersionRequired,lpWSAData);<br />　　　　}<br />　　　　//WinSock结束清除函数。<br />　　　　int PASCAL FAR WSACleanup(void)<br />　　　　{<br />　　　　　return WSACleanup1(); //调用原WinSock库结束清除函数。<br />　　　　}<br />　　　　//创建Socket函数。<br />　　　　SOCKET PASCAL FAR socket (int af, int type, int protocol)<br />　　　　{<br />　　　　　//调用原WinSock库创建Socket函数。<br />　　　　　return socket1(af,type,protocol);<br />　　　　}<br />　　　　//发送数据包函数<br />　　　　int PASCAL FAR send(SOCKET s,const char * buf,int len,int flags)<br />　　　　{<br />　　　//在此可以对发送的缓冲buf的内容进行修改，以实现欺骗服务器。<br />　　　外挂代码......<br />　　　//调用原WinSock库发送数据包函数。<br />　　　　　return send1(s,buf,len,flags);<br />　　　　}<br />//接收数据包函数。<br />　　　　int PASCAL FAR recv(SOCKET s, char FAR * buf, int len, int flags)<br />　　　　{<br />　　　//在此可以挡截到服务器端发送到客户端的数据包，先将其保存到buffer中。<br />　　　strcpy(buffer,buf);<br />　　　//对buffer数据包数据进行分析后，对其按照玩家的指令进行相关修改。<br />　　　外挂代码......<br />　　　//最后调用原WinSock中的接收数据包函数。<br />　　　　　return recv1(s, buffer, len, flags);<br />　　　　　}<br />　　　　.......其它函数定义代码略。 <br /><br />　　(4)、新建wsock32.def配置文件，在其中加入所有库输出函数的声明，部分声明代码如下：<br /><br />　　　LIBRARY "wsock32"<br />　　　EXPORTS <br />　　　　WSAStartup @1<br />　　　WSACleanup @2<br />　　　　recv @3<br />　　　　send @4<br />　　　　socket @5<br />　　　bind @6<br />　　　closesocket @7<br />　　　connect @8 <br /><br />　　　......其它输出函数声明代码略。<br /><br />　　(5)、从“工程”菜单中选择“设置”，弹出Project Setting对话框，选择Link标签，在“对象/库模块”中输入Ws2_32.lib。<br /><br />　　(6)、编译项目，产生wsock32.dll库文件。<br /><br />　　(7)、将系统目录下原wsock32.dll库文件拷贝到被外挂程序的目录下，并将其改名为wsock.001；再将上面产生的wsock32.dll文件同样拷贝到被外挂程序的目录下。重新启动<b>游戏</b>程序，此时<b>游戏</b>程序将先加载我们自己制作的wsock32.dll文件，再通过该库文件间接调用原WinSock接口函数来实现访问网络。上面我们仅仅介绍了挡载WinSock的实现过程，至于如何加入外挂控制代码，还需要外挂开发人员对<b>游戏</b>数据包结构、内容、加密算法等方面的仔细分析（这个过程将是一个艰辛的过程），再生成外挂控制代码。关于数据包分析方法和技巧，不是本文讲解的范围，如您感兴趣可以到网上查查相关资料。<br /><br /><br />　　2.挡截API<br /><br />　　挡截API<b>技术</b>与挡截WinSock<b>技术</b>在原理上很相似，但是前者比后者提供了更强大的功能。挡截WinSock仅只能挡截WinSock接口函数，而挡截API可以实现对应用程序调用的包括WinSock API函数在内的所有API函数的挡截。如果您的外挂程序仅打算对WinSock的函数进行挡截的话，您可以只选择使用上小节介绍的挡截WinSock<b>技术</b>。随着大量外挂程序在功能上的扩展，它们不仅仅只提供对数据包的挡截，而且还对<b>游戏</b>程序中使用的<b>Windows</b> API或其它DLL库函数的挡截，以使外挂的功能更加强大。例如，可以通过挡截相关API函数以实现对非中文<b>游戏</b>的汉化功能，有了这个利器，可以使您的外挂程序无所不能了。<br /><br />　　挡截API<b>技术</b>的原理核心也是使用我们自己的函数来替换掉<b>Windows</b>或其它DLL库提供的函数，有点同挡截WinSock原理相似吧。但是，其实现过程却比挡截WinSock要复杂的多，如像实现挡截Winsock过程一样，将应用程序调用的所有的库文件都写一个模拟库有点不大可能，就只说<b>Windows</b> API就有上<b>千</b>个，还有很多库提供的函数结构并未公开，所以写一个模拟库代替的方式不大现实，故我们必须另谋良方。<br /><br />　　挡截API的最终目标是使用自定义的函数代替原函数。那么，我们首先应该知道应用程序何时、何地、用何种方式调用原函数。接下来，需要将应用程序中调用该原函数的指令代码进行修改，使它将调用函数的指针指向我们自己定义的函数地址。这样，外挂程序才能完全控制应用程序调用的API函数，至于在其中如何加入外挂代码，就应需求而异了。最后还有一个重要的问题要解决，如何将我们自定义的用来代替原API函数的函数代码注入被外挂<b>游戏</b>程序进行地址空间中，因在<b>Windows</b>系统中应用程序仅只能访问到本进程地址空间内的代码和数据。<br /><br />　　综上所述，要实现挡截API函数，至少需要解决如下三个问题：<br /><br />　　● 如何定位<b>游戏</b>程序中调用API函数指令代码？<br /><br />　　● 如何修改<b>游戏</b>程序中调用API函数指令代码？<br /><br />　　● 如何将外挂代码（自定义的替换函数代码）注入到<b>游戏</b>程序进程地址空间？<br /><br />　　下面我们逐一介绍这几个问题的解决方法：<br /><br />　　(1) 、定位调用API函数指令代码<br /><br />　　我们知道，在汇编语言中使用CALL指令来调用函数或过程的，它是通过指令参数中的函数地址而定位到相应的函数代码的。那么，我们如果能寻找到程序代码中所有调用被挡截的API函数的CALL指令的话，就可以将该指令中的函数地址参数修改为替代函数的地址。虽然这是一个可行的方案，但是实现起来会很繁琐，也不稳健。庆幸的是，<b>Windows</b>系统中所使用的可执行文件（PE格式）采用了输入地址表机制，将所有在程序调用的API函数的地址信息存放在输入地址表中，而在程序代码CALL指令中使用的地址不是API函数的地址，而是输入地址表中该API函数的地址项，如想使程序代码中调用的API函数被代替掉，只用将输入地址表中该API函数的地址项内容修改即可。具体理解输入地址表运行机制，还需要了解一下PE格式文件结构，其中图三列出了PE格式文件的大致结构。<br /><br /><img src="http://www.cgfront.com/web/uploads/153.jpg" border="0" /><br /><br /><br /><br />图三：PE格式大致结构图<br /><br /><br />　　PE格式文件一开始是一段DOS程序，当你的程序在不支持<b>Windows</b>的环境中运行时，它就会显示“This Program cannot be run in DOS mode”这样的警告语句，接着这个DOS文件头，就开始真正的PE文件内容了。首先是一段称为“IMAGE_NT_HEADER”的数据，其中是许多关于整个PE文件的消息，在这段数据的尾端是一个称为Data Directory的数据表，通过它能快速定位一些PE文件中段（section）的地址。在这段数据之后，则是一个“IMAGE_SECTION_HEADER”的列表，其中的每一项都详细描述了后面一个段的相关信息。接着它就是PE文件中最主要的段数据了，执行代码、数据和资源等等信息就分别存放在这些段中。<br /><br />　　在所有的这些段里，有一个被称为“.idata”的段（输入数据段）值得我们去注意，该段中包含着一些被称为输入地址表（IAT，Import Address Table）的数据列表。每个用隐式方式加载的API所在的DLL都有一个IAT与之对应，同时一个API的地址也与IAT中一项相对应。当一个应用程序加载到内存中后，针对每一个API函数调用，相应的产生如下的汇编指令： <br /><br />　　JMP DWORD PTR [XXXXXXXX] <br /><br />　　或<br /><br />　　CALL DWORD PTR [XXXXXXXX]<br /><br />　　其中，[XXXXXXXX]表示指向了输入地址表中一个项，其内容是一个DWORD，而正是这个DWORD才是API函数在内存中的真正地址。因此我们要想拦截一个API的调用，只要简单的把那个DWORD改为我们自己的函数的地址。<br /><br />　　(2) 、修改调用API函数代码<br /><br />　　从上面对PE文件格式的分析可知，修改调用API函数代码其实是修改被调用API函数在输入地址表中IAT项内容。由于<b>Windows</b>系统对应用程序指令代码地址空间的严密保护机制，使得修改程序指令代码非常困难，以至于许多高手为之编写VxD进入Ring0。在这里，我为大家介绍一种较为方便的方法修改进程内存，它仅需要调用几个<b>Windows</b>核心API函数，下面我首先来学会一下这几个API函数：<br /><br />　　　DWORD VirtualQuery(<br />　　　LPCVOID lpAddress, // address of region<br />　　　PMEMORY_BASIC_INFORMATION lpBuffer, // information buffer<br />　　　DWORD dwLength // size of buffer<br />　　　); <br /><br />　　该函数用于查询关于本进程内虚拟地址页的信息。其中，lpAddress表示被查询页的区域地址；lpBuffer表示用于保存查询页信息的缓冲；dwLength表示缓冲区大小。返回值为实际缓冲大小。<br /><br />　　　BOOL VirtualProtect(<br />　　　LPVOID lpAddress, // region of committed pages<br />　　　SIZE_T dwSize, // size of the region<br />　　　DWORD flNewProtect, // desired access protection<br />　　　PDWORD lpflOldProtect // old protection<br />　　　); <br /><br />　　该函数用于改变本进程内虚拟地址页的保护属性。其中，lpAddress表示被改变保护属性页区域地址；dwSize表示页区域大小；flNewProtect表示新的保护属性，可取值为PAGE_READONLY、PAGE_READWRITE、PAGE_EXECUTE等；lpflOldProtect表示用于保存改变前的保护属性。如果函数调用成功返回“T”，否则返回“F”。<br /><br />　　有了这两个API函数，我们就可以随心所欲的修改进程内存了。首先，调用VirtualQuery()函数查询被修改内存的页信息，再根据此信息调用VirtualProtect()函数改变这些页的保护属性为PAGE_READWRITE，有了这个权限您就可以任意修改进程内存数据了。下面一段代码演示了如何将进程虚拟地址为0x0040106c处的字节清零。<br /><br />　　　BYTE* pData = 0x0040106c;<br />　　　MEMORY_BASIC_INFORMATION mbi_thunk; <br />　　　//查询页信息。<br />　　　VirtualQuery(pData, &amp;mbi_thunk, sizeof(MEMORY_BASIC_INFORMATION)); <br />　　　//改变页保护属性为读写。<br />　　　VirtualProtect(mbi_thunk.BaseAddress,mbi_thunk.RegionSize, <br />　　　PAGE_READWRITE, &amp;mbi_thunk.Protect); <br />　　　//清零。<br />　　　*pData = 0x00;<br />　　　//恢复页的原保护属性。<br />　　　DWORD dwOldProtect; <br />　　　VirtualProtect(mbi_thunk.BaseAddress,mbi_thunk.RegionSize, <br />　　　mbi_thunk.Protect, &amp;dwOldProtect); <br /><br /><br />　　(3)、注入外挂代码进入被挂<b>游戏</b>进程中<br /><br />　　完成了定位和修改程序中调用API函数代码后，我们就可以随意设计自定义的API函数的替代函数了。做完这一切后，还需要将这些代码注入到被外挂<b>游戏</b>程序进程内存空间中，不然<b>游戏</b>进程根本不会访问到替代函数代码。注入方法有很多，如利用全局钩子注入、利用注册表注入挡截User32库中的API函数、利用CreateRemoteThread注入（仅限于NT/2000）、利用BHO注入等。因为我们在动作模拟<b>技术</b>一节已经接触过全局钩子，我相信聪明的读者已经完全掌握了全局钩子的制作过程，所以我们在后面的实例中，将继续利用这个全局钩子。至于其它几种注入方法，如果感兴趣可参阅MSDN有关内容。<br /><br />　　有了以上理论基础，我们下面就开始制作一个挡截MessageBoxA和recv函数的实例，在开发<b>游戏</b>外挂程序 时，可以此实例为框架，加入相应的替代函数和处理代码即可。此实例的开发过程如下：<br /><br />　　(1) 打开前面创建的ActiveKey项目。<br /><br />　　(2) 在ActiveKey.h文件中加入HOOKAPI结构，此结构用来存储被挡截API函数名称、原API函数地址和替代函数地址。<br /><br />　　　typedef struct tag_HOOKAPI <br />　　　{ <br />　　　LPCSTR szFunc;//被HOOK的API函数名称。<br />　　　PROC pNewProc;//替代函数地址。<br />　　　PROC pOldProc;//原API函数地址。<br />　　　}HOOKAPI, *LPHOOKAPI; <br /><br />　　(3) 打开ActiveKey.cpp文件，首先加入一个函数，用于定位输入库在输入数据段中的IAT地址。代码如下：<br /><br />　　　extern "C" __declspec(dllexport)PIMAGE_IMPORT_DESCRIPTOR <br />　　　LocationIAT(HMODULE hModule, LPCSTR szImportMod) <br />　　　//其中，hModule为进程模块句柄；szImportMod为输入库名称。<br />　　　{ <br />　　　//检查是否为DOS程序，如是返回NULL，因DOS程序没有IAT。<br />　　　PIMAGE_DOS_HEADER pDOSHeader = (PIMAGE_DOS_HEADER) hModule; <br />　　　if(pDOSHeader-&gt;e_magic != IMAGE_DOS_SIGNATURE) return NULL; <br />　　　　//检查是否为NT标志，否则返回NULL。<br />　　　　PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDOSHeader+ (DWORD)(pDOSHeader-&gt;e_lfanew)); <br />　　　　if(pNTHeader-&gt;Signature != IMAGE_NT_SIGNATURE) return NULL; <br />　　　　//没有IAT表则返回NULL。<br />　　　　if(pNTHeader-&gt;OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress == 0) return NULL; <br />　　　　//定位第一个IAT位置。 <br />　　　　PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pDOSHeader + (DWORD)(pNTHeader-&gt;OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)); <br />　　　　//根据输入库名称循环检查所有的IAT，如匹配则返回该IAT地址，否则检测下一个IAT。<br />　　　　while (pImportDesc-&gt;Name) <br />　　　　{ <br />　　　　　//获取该IAT描述的输入库名称。<br />　　　PSTR szCurrMod = (PSTR)((DWORD)pDOSHeader + (DWORD)(pImportDesc-&gt;Name)); <br />　　　if (stricmp(szCurrMod, szImportMod) == 0) break; <br />　　　pImportDesc++; <br />　　　　} <br />　　　　if(pImportDesc-&gt;Name == NULL) return NULL; <br />　　　return pImportDesc; <br />　　　} <br /><br />　　再加入一个函数，用来定位被挡截API函数的IAT项并修改其内容为替代函数地址。代码如下：<br /><br />　　　extern "C" __declspec(dllexport) <br />　　　HookAPIByName( HMODULE hModule, LPCSTR szImportMod, LPHOOKAPI pHookApi) <br />　　　//其中，hModule为进程模块句柄；szImportMod为输入库名称；pHookAPI为HOOKAPI结构指针。<br />　　　{ <br />　　　　//定位szImportMod输入库在输入数据段中的IAT地址。<br />　　　　PIMAGE_IMPORT_DESCRIPTOR pImportDesc = LocationIAT(hModule, szImportMod); <br />　　if (pImportDesc == NULL) return FALSE; <br />　　　　//第一个Thunk地址。<br />　　　　PIMAGE_THUNK_DATA pOrigThunk = (PIMAGE_THUNK_DATA)((DWORD)hModule + (DWORD)(pImportDesc-&gt;OriginalFirstThunk)); <br />　　 //第一个IAT项的Thunk地址。<br />　　　　PIMAGE_THUNK_DATA pRealThunk = (PIMAGE_THUNK_DATA)((DWORD)hModule + (DWORD)(pImportDesc-&gt;FirstThunk)); <br />　　　　//循环查找被截API函数的IAT项，并使用替代函数地址修改其值。<br />　　　while(pOrigThunk-&gt;u1.Function) <br />{ <br />　//检测此Thunk是否为IAT项。<br />if((pOrigThunk-&gt;u1.Ordinal &amp; IMAGE_ORDINAL_FLAG) != IMAGE_ORDINAL_FLAG) <br />{<br />　 //获取此IAT项所描述的函数名称。<br />　PIMAGE_IMPORT_BY_NAME pByName =(PIMAGE_IMPORT_BY_NAME)((DWORD)hModule+(DWORD)(pOrigThunk-&gt;u1.AddressOfData)); <br />　if(pByName-&gt;Name[0] == '\0') return FALSE; <br />　　//检测是否为挡截函数。<br />if(strcmpi(pHookApi-&gt;szFunc, (char*)pByName-&gt;Name) == 0) <br />　 { <br />　　　　　　　MEMORY_BASIC_INFORMATION mbi_thunk;<br />　　　　　　　//查询修改页的信息。<br />　　　　　　　VirtualQuery(pRealThunk, &amp;mbi_thunk, sizeof(MEMORY_BASIC_INFORMATION)); <br />//改变修改页保护属性为PAGE_READWRITE。<br />　　　　　　　VirtualProtect(mbi_thunk.BaseAddress,mbi_thunk.RegionSize, PAGE_READWRITE, &amp;mbi_thunk.Protect); <br />//保存原来的API函数地址。<br />　　　 　　if(pHookApi-&gt;pOldProc == NULL) <br />pHookApi-&gt;pOldProc = (PROC)pRealThunk-&gt;u1.Function; <br />　 //修改API函数IAT项内容为替代函数地址。<br />pRealThunk-&gt;u1.Function = (PDWORD)pHookApi-&gt;pNewProc; <br />//恢复修改页保护属性。<br />DWORD dwOldProtect; <br />　　　　　　　VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, mbi_thunk.Protect, &amp;dwOldProtect); <br />　　　　　 } <br />} <br />　 pOrigThunk++; <br />　 pRealThunk++; <br />} <br />　　SetLastError(ERROR_SUCCESS); //设置错误为ERROR_SUCCESS，表示成功。<br />　　return TRUE; <br />　　　} <br /><br />　　(4) 定义替代函数，此实例中只给MessageBoxA和recv两个API进行挡截。代码如下：<br /><br />　　　static int WINAPI MessageBoxA1 (HWND hWnd , LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)<br />　　　{<br />　　　　//过滤掉原MessageBoxA的正文和标题内容，只显示如下内容。<br />return MessageBox(hWnd, "Hook API OK!", "Hook API", uType); <br />　　　} <br />　　　static int WINAPI recv1(SOCKET s, char FAR *buf, int len, int flags )<br />　　　{<br />　　　//此处可以挡截<b>游戏</b>服务器发送来的网络数据包，可以加入分析和处理数据代码。<br />　　　return recv(s,buf,len,flags);<br />　　　} <br /><br />　　(5) 在KeyboardProc函数中加入激活挡截API代码，在if( wParam == 0X79 )语句中后面加入如下else if语句：<br /><br />　　　......<br />　　　//当激活F11键时，启动挡截API函数功能。<br />　　　else if( wParam == 0x7A )<br />　　　{ <br />　　　　HOOKAPI api[2];<br />api[0].szFunc ="MessageBoxA";//设置被挡截函数的名称。<br />api[0].pNewProc = (PROC)MessageBoxA1;//设置替代函数的地址。<br />api[1].szFunc ="recv";//设置被挡截函数的名称。<br />api[1].pNewProc = (PROC)recv1; //设置替代函数的地址。<br />//设置挡截User32.dll库中的MessageBoxA函数。<br />HookAPIByName(GetModuleHandle(NULL),"User32.dll",&amp;api[0]);<br />//设置挡截Wsock32.dll库中的recv函数。<br />HookAPIByName(GetModuleHandle(NULL),"Wsock32.dll",&amp;api[1]);<br />　　　}<br />　　　...... <br /><br />　　(6) 在ActiveKey.cpp中加入头文件声明 "#include "wsock32.h"。 从“工程”菜单中选择“设置”，弹出Project Setting对话框，选择Link标签，在“对象/库模块”中输入Ws2_32..lib。<br /><br />　　(7) 重新编译ActiveKey项目，产生ActiveKey.dll文件，将其拷贝到Simulate.exe目录下。运行Simulate.exe并启动全局钩子。激活任意应用程序，按F11键后，运行此程序中可能调用MessageBoxA函数的操作，看看信息框是不是有所变化。同样，如此程序正在接收网络数据包，就可以实现封包功能了。<br /><br />　　六、结束语<br /><br />　　除了以上介绍的几种<b>游戏</b>外挂程序常用的<b>技术</b>以外，在一些外挂程序中还使用了<b>游戏</b>数据修改<b>技术</b>、<b>游戏</b>加速<b>技术</b>等。在这篇文章里，就不逐一介绍了。<img src ="http://www.cppblog.com/eday/aggbug/15363.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eday/" target="_blank">松*</a> 2006-11-18 16:36 <a href="http://www.cppblog.com/eday/archive/2006/11/18/15363.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏外挂设计技术探讨(上)</title><link>http://www.cppblog.com/eday/archive/2006/11/18/15362.html</link><dc:creator>松*</dc:creator><author>松*</author><pubDate>Sat, 18 Nov 2006 08:35:00 GMT</pubDate><guid>http://www.cppblog.com/eday/archive/2006/11/18/15362.html</guid><description><![CDATA[一、 前言 <br /><br />　　所谓<b>游戏</b>外挂，其实是一种<b>游戏</b>外辅程序，它可以协助玩家自动产生<b>游戏</b>动作、修改<b>游戏</b>网络数据包以及修改<b>游戏</b>内存数据等，以实现玩家用最少的时间和金钱去完成功力升级和过关斩将。虽然，现在对<b>游戏</b>外挂程序的“合法”身份众说纷纭，在这里我不想对此发表任何个人意见，让时间去说明一切吧。<br /><br />　　不管<b>游戏</b>外挂程序是不是“合法”身份，但是它却是具有一定的<b>技术</b>含量的，在这些小小程序中使用了许多高端<b>技术</b>，如拦截Sock<b>技术</b>、拦截API<b>技术</b>、模拟键盘与鼠标<b>技术</b>、直接修改程序内存<b>技术</b>等等。本文将对常见的<b>游戏</b>外挂中使用的<b>技术</b>进行全面剖析。<br /><br />　　二、认识外挂<br /><br />　　<b>游戏</b>外挂的历史可以追溯到单机版<b>游戏</b>时代，只不过当时它使用了另一个更通俗易懂的名字??<b>游戏</b>修改器。它可以在<b>游戏</b>中追踪锁定<b>游戏</b>主人公的各项能力数值。这样玩家在<b>游戏</b>中可以达到主角不掉血、不耗费魔法、不消耗金钱等目的。这样降低了<b>游戏</b>的难度，使得玩家更容易通关。<br /><br />　　随着网络<b>游戏</b>的时代的来临，<b>游戏</b>外挂在原有的功能之上进行了新的发展，它变得更加多种多样，功能更加强大，操作更加简单，以至有些<b>游戏</b>的外挂已经成为一个体系，比如《石器时代》，外挂品种达到了几十种，自动战斗、自动行走、自动练级、自动补血、加速、不遇敌、原地遇敌、快速增加经验值、按键精灵……几乎无所不包。<br /><br />　　<b>游戏</b>外挂的设计主要是针对于某个<b>游戏</b>开发的，我们可以根据它针对的<b>游戏</b>的类型可大致可将外挂分为两种大类。<br /><br />　　一类是将<b>游戏</b>中大量繁琐和无聊的攻击动作使用外挂自动完成，以帮助玩家轻松搞定攻击对象并可以快速的增加玩家的经验值。比如在《龙族》中有一种工作的设定，玩家的工作等级越高，就可以驾驭越好的装备。但是增加工作等级却不是一件有趣的事情，毋宁说是重复枯燥的机械劳动。如果你想做法师用的杖，首先需要做基本工作--?砍树。砍树的方法很简单，在一棵大树前不停的点鼠标就可以了，每10000的经验升一级。这就意味着玩家要在大树前不停的点击鼠标，这种无聊的事情通过"按键精灵"就可以解决。外挂的"按键精灵"功能可以让玩家摆脱无趣的点击鼠标的工作。<br /><br />　　另一类是由外挂程序产生欺骗性的网络<b>游戏</b>封包，并将这些封包发送到网络<b>游戏</b>服务器，利用这些虚假信息欺骗服务器进行<b>游戏</b>数值的修改，达到修改角色能力数值的目的。这类外挂程序针对性很强，一般在设计时都是针对某个<b>游戏</b>某个版本来做的，因为每个网络<b>游戏</b>服务器与客户端交流的数据包各不相同，外挂程序必须要对欺骗的网络<b>游戏</b>服务器的数据包进行分析，才能产生服务器识别的数据包。这类外挂程序也是当前最流利的一类<b>游戏</b>外挂程序。<br /><br />　　另外，现在很多外挂程序功能强大，不仅实现了自动动作代理和封包功能，而且还提供了对网络<b>游戏</b>的客户端程序的数据进行修改，以达到欺骗网络<b>游戏</b>服务器的目的。我相信，随着网络<b>游戏</b>商家的反外挂<b>技术</b>的进展，<b>游戏</b>外挂将会产生更多更优秀的<b>技术</b>，让我们期待着看场<b>技术</b>大战吧......<br /><br />　　三、外挂<b>技术</b>综述<br /><br />　　可以将开发<b>游戏</b>外挂程序的过程大体上划分为两个部分：<br /><br />　　前期部分工作是对外挂的主体<b>游戏</b>进行分析，不同类型的外挂分析主体<b>游戏</b>的内容也不相同。如外挂为上述谈到的外挂类型中的第一类时，其分析过程常是针对<b>游戏</b>的场景中的攻击对象的位置和分布情况进行分析，以实现外挂自动进行攻击以及位置移动。如外挂为外挂类型中的第二类时，其分析过程常是针对<b>游戏</b>服务器与客户端之间通讯包数据的结构、内容以及加密算法的分析。因网络<b>游戏</b>公司一般都不会公布其<b>游戏</b>产品的通讯包数据的结构、内容和加密算法的信息，所以对于开发第二类外挂成功的关键在于是否能正确分析<b>游戏</b>包数据的结构、内容以及加密算法，虽然可以使用一些工具辅助分析，但是这还是一种坚苦而复杂的工作。<br /><br />　　后期部分工作主要是根据前期对<b>游戏</b>的分析结果，使用大量的程序开发<b>技术</b>编写外挂程序以实现对<b>游戏</b>的控制或修改。如外挂程序为第一类外挂时，通常会使用到鼠标模拟<b>技术</b>来实现<b>游戏</b>角色的自动位置移动，使用键盘模拟<b>技术</b>来实现<b>游戏</b>角色的自动攻击。如外挂程序为第二类外挂时，通常会使用到挡截Sock和挡截API函数<b>技术</b>，以挡截<b>游戏</b>服务器传来的网络数据包并将数据包修改后封包后传给<b>游戏</b>服务器。另外，还有许多外挂使用对<b>游戏</b>客户端程序内存数据修改<b>技术</b>以及<b>游戏</b>加速<b>技术</b>。<br /><br />　　本文主要是针对开发<b>游戏</b>外挂程序后期使用的程序开发<b>技术</b>进行探讨，重点介绍的如下几种在<b>游戏</b>外挂中常使用的程序开发<b>技术</b>：<br /><br />　　● 动作模拟<b>技术</b>：主要包括键盘模拟<b>技术</b>和鼠标模拟<b>技术</b>。<br /><br />　　● 封包<b>技术</b>：主要包括挡截Sock<b>技术</b>和挡截API<b>技术</b>。<br /><br /><br />四、动作模拟<b>技术</b><br /><br />　　我们在前面介绍过，几乎所有的<b>游戏</b>都有大量繁琐和无聊的攻击动作以增加玩家的功力，还有那些数不完的迷宫，这些好像已经成为了角色<b>游戏</b>的代名词。现在，外挂可以帮助玩家从这些繁琐而无聊的工作中摆脱出来，专注于<b>游戏</b>情节的进展。外挂程序为了实现自动角色位置移动和自动攻击等功能，需要使用到键盘模拟<b>技术</b>和鼠标模拟<b>技术</b>。下面我们将重点介绍这些<b>技术</b>并编写一个简单的实例帮助读者理解动作模拟<b>技术</b>的实现过程。<br /><br />　　１． 鼠标模拟<b>技术</b><br />　　<br />　　几乎所有的<b>游戏</b>中都使用了鼠标来改变角色的位置和方向，玩家仅用一个小小的鼠标，就可以使角色畅游天下。那么，我们如何实现在没有玩家的参与下角色也可以自动行走呢。其实实现这个并不难，仅仅几个<b>Windows</b> API函数就可以搞定，让我们先来认识认识这些API函数。<br /><br />　　(1) 模拟鼠标动作API函数mouse_event，它可以实现模拟鼠标按下和放开等动作。<br /><br />　　　　VOID mouse_event(<br />　　　　　　DWORD dwFlags, // 鼠标动作标识。<br />　　　　　　DWORD dx, // 鼠标水平方向位置。<br />　　　　　　DWORD dy, // 鼠标垂直方向位置。<br />　　　　　　DWORD dwData, // 鼠标轮子转动的数量。<br />　　　　　　DWORD dwExtraInfo // 一个关联鼠标动作辅加信息。<br />　　　　); <br /><br />　　其中，dwFlags表示了各种各样的鼠标动作和点击活动，它的常用取值如下：<br /><br />　　　MOUSEEVENTF_MOVE　表示模拟鼠标移动事件。<br /><br />　　　MOUSEEVENTF_LEFTDOWN 表示模拟按下鼠标左键。<br /><br />　　　MOUSEEVENTF_LEFTUP 表示模拟放开鼠标左键。<br /><br />　　　MOUSEEVENTF_RIGHTDOWN 表示模拟按下鼠标右键。<br /><br />　　　MOUSEEVENTF_RIGHTUP 表示模拟放开鼠标右键。<br /><br />　　　MOUSEEVENTF_MIDDLEDOWN 表示模拟按下鼠标中键。<br /><br />　　　MOUSEEVENTF_MIDDLEUP 表示模拟放开鼠标中键。<br /><br />　　(2)、设置和获取当前鼠标位置的API函数。获取当前鼠标位置使用GetCursorPos()函数，设置当前鼠标位置使用SetCursorPos()函数。<br /><br />　　　　BOOL GetCursorPos(<br />　　　　　LPPOINT　lpPoint // 返回鼠标的当前位置。<br />　　　　);<br />　　　　BOOL SetCursorPos(<br />　　　　int X, // 鼠标的水平方向位置。<br />　　　　　　int Y //鼠标的垂直方向位置。<br />　　　　); <br /><br />　　通常<b>游戏</b>角色的行走都是通过鼠标移动至目的地，然后按一下鼠标的按钮就搞定了。下面我们使用上面介绍的API函数来模拟角色行走过程。<br /><br />　　　CPoint oldPoint,newPoint;<br />　　　GetCursorPos(&amp;oldPoint); //保存当前鼠标位置。<br />　　　newPoint.x = oldPoint.x+40;<br />　　　newPoint.y = oldPoint.y+10;<br />　　　SetCursorPos(newPoint.x,newPoint.y); //设置目的地位置。<br />　　　mouse_event(MOUSEEVENTF_RIGHTDOWN,0,0,0,0);//模拟按下鼠标右键。<br />　　　mouse_event(MOUSEEVENTF_RIGHTUP,0,0,0,0);//模拟放开鼠标右键。 <br /><br />　　2． 键盘模拟<b>技术</b><br /><br />　　在很多<b>游戏</b>中，不仅提供了鼠标的操作，而且还提供了键盘的操作，在对攻击对象进行攻击时还可以使用快捷键。为了使这些攻击过程能够自动进行，外挂程序需要使用键盘模拟<b>技术</b>。像鼠标模拟<b>技术</b>一样，<b>Windows</b> API也提供了一系列API函数来完成对键盘动作的模拟。<br /><br />　　模拟键盘动作API函数keydb_event，它可以模拟对键盘上的某个或某些键进行按下或放开的动作。<br /><br />　　　VOID keybd_event(<br />　　　　　BYTE bVk, // 虚拟键值。<br />　　　　　BYTE bScan, // 硬件扫描码。<br />　　　　　DWORD dwFlags, // 动作标识。<br />　　　　　DWORD dwExtraInfo // 与键盘动作关联的辅加信息。<br />　　　); <br /><br />　　其中，bVk表示虚拟键值，其实它是一个BYTE类型值的宏，其取值范围为1-254。有关虚拟键值表请在MSDN上使用关键字“Virtual-Key Codes”查找相关资料。bScan表示当键盘上某键被按下和放开时，键盘系统硬件产生的扫描码，我们可以MapVirtualKey()函数在虚拟键值与扫描码之间进行转换。dwFlags表示各种各样的键盘动作，它有两种取值：KEYEVENTF_EXTENDEDKEY和KEYEVENTF_KEYUP。<br /><br />　　下面我们使用一段代码实现在<b>游戏</b>中按下Shift+R快捷键对攻击对象进行攻击。<br /><br />　　　keybd_event(VK_CONTROL,MapVirtualKey(VK_CONTROL,0),0,0); //按下CTRL键。<br />　　　keybd_event(0x52,MapVirtualKey(0x52,0),0,0);//键下R键。<br />　　　keybd_event(0x52,MapVirtualKey(0x52,0), KEYEVENTF_KEYUP,0);//放开R键。<br />　　　keybd_event(VK_CONTROL,MapVirtualKey(VK_CONTROL,0), <br />　　　KEYEVENTF_KEYUP,0);//放开CTRL键。 <br /><br />　　3． 激活外挂<br /><br />　　上面介绍的鼠标和键盘模拟<b>技术</b>实现了对<b>游戏</b>角色的动作部分的模拟，但要想外挂能工作于<b>游戏</b>之上，还需要将其与<b>游戏</b>的场景窗口联系起来或者使用一个激活键，就象按键精灵的那个激活键一样。我们可以用GetWindow函数来枚举窗口，也可以用Findwindow函数来查找特定的窗口。另外还有一个FindWindowEx函数可以找到窗口的子窗口，当<b>游戏</b>切换场景的时候我们可以用FindWindowEx来确定一些当前窗口的特征，从而判断是否还在这个场景，方法很多了，比如可以GetWindowInfo来确定一些东西，比如当查找不到某个按钮的时候就说明<b>游戏</b>场景已经切换了等等办法。当使用激活键进行关联，需要使用Hook<b>技术</b>开发一个全局键盘钩子，在这里就不具体介绍全局钩子的开发过程了，在后面的实例中我们将会使用到全局钩子，到时将学习到全局钩子的相关知识。<br /><br /><br />　　4． 实例实现<br /><br />　　通过上面的学习，我们已经基本具备了编写动作式<b>游戏</b>外挂的能力了。下面我们将创建一个画笔程序外挂，它实现自动移动画笔字光标的位置并写下一个红色的“R”字。以这个实例为基础，加入相应的<b>游戏</b>动作规则，就可以实现一个完整的<b>游戏</b>外挂。这里作者不想使用某个<b>游戏</b>作为例子来开发外挂（因没有<b>游戏</b>商家的授权啊！），如读者感兴趣的话可以找一个<b>游戏</b>试试，最好仅做测试<b>技术</b>用。<br /><br />　　首先，我们需要编写一个全局钩子，使用它来激活外挂，激活键为F10。创建全局钩子步骤如下：<br /><br />　　(1)．选择MFC AppWizard(DLL)创建项目ActiveKey，并选择MFC Extension DLL（共享MFC拷贝）类型。<br /><br />　　(2).插入新文件ActiveKey.h，在其中输入如下代码：<br /><br />　　　#ifndef _KEYDLL_H<br />　　　#define _KEYDLL_H<br /><br />　　　class AFX_EXT_CLASS CKeyHook:public CObject<br />　　　{<br />　　　　public:<br />　CKeyHook();<br />　~CKeyHook();<br />　HHOOK Start();　//安装钩子<br />　BOOL Stop(); //卸载钩子<br />　　　};<br />　　　#endif <br /><br />　　(3).在ActiveKey.cpp文件中加入声明＂#include ActiveKey.h＂。<br /><br />　　(4).在ActiveKey.cpp文件中加入共享数据段，代码如下：<br /><br />　　　//Shared data section<br />　　　#pragma data_seg("sharedata")<br />　　　HHOOK glhHook=NULL; //钩子句柄。<br />　　　HINSTANCE glhInstance=NULL; //DLL实例句柄。<br />　　　#pragma data_seg() <br /><br />　　(5).在ActiveKey.def文件中设置共享数据段属性，代码如下：<br /><br />　　　SETCTIONS<br />　　　shareddata READ WRITE SHARED <br /><br />　　(6).在ActiveKey.cpp文件中加入CkeyHook类的实现代码和钩子函数代码：<br /><br />　　　//键盘钩子处理函数。<br />　　　extern "C" LRESULT WINAPI KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)<br />　　　{<br />　　　if( nCode &gt;= 0 )<br />　　　{<br />　　　if( wParam == 0X79 )//当按下F10键时，激活外挂。<br />　{<br />　　//外挂实现代码。<br />CPoint newPoint,oldPoint;<br />　　 GetCursorPos(&amp;oldPoint);<br />　　 newPoint.x = oldPoint.x+40;<br />　　 newPoint.y = oldPoint.y+10;<br />　　 SetCursorPos(newPoint.x,newPoint.y);<br />　　 mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);//模拟按下鼠标左键。<br />　　mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);//模拟放开鼠标左键。<br />　　keybd_event(VK_SHIFT,MapVirtualKey(VK_SHIFT,0),0,0); //按下SHIFT键。<br />　　keybd_event(0x52,MapVirtualKey(0x52,0),0,0);//按下R键。<br />　　keybd_event(0x52,MapVirtualKey(0x52,0),KEYEVENTF_KEYUP,0);//放开R键。<br />　　keybd_event(VK_SHIFT,MapVirtualKey(VK_SHIFT,0),KEYEVENTF_KEYUP,0);//放开SHIFT键。<br />　　　　　　SetCursorPos(oldPoint.x,oldPoint.y);<br />　}<br />　　　}<br />　　　return CallNextHookEx(glhHook,nCode,wParam,lParam);<br />　　　}<br /><br />　　　CKeyHook::CKeyHook(){}<br />　　　CKeyHook::~CKeyHook()<br />　　　{　<br />　　　if( glhHook )<br />Stop();<br />　　　}<br />　　　//安装全局钩子。<br />　　　HHOOK CKeyHook::Start()<br />　　　{ <br />glhHook = Set<b>Windows</b>HookEx(WH_KEYBOARD,KeyboardProc,glhInstance,0);//设置键盘钩子。<br />return glhHook;<br />}<br />　　　//卸载全局钩子。<br />　　　BOOL CKeyHook::Stop()<br />　　　{<br />　　　BOOL bResult = TRUE;<br />　if( glhHook )<br />　　　bResult = Unhook<b>Windows</b>HookEx(glhHook);//卸载键盘钩子。<br />　　　return bResult;<br />　　　} <br /><br />　　(7).修改DllMain函数，代码如下：<br /><br />　　　extern "C" int APIENTRY<br />　　　DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)<br />　　　{<br />//如果使用lpReserved参数则删除下面这行 <br />UNREFERENCED_PARAMETER(lpReserved);<br /><br />if (dwReason == DLL_PROCESS_ATTACH)<br />{<br />　　TRACE0("NOtePadHOOK.DLL Initializing!\n");<br />　　 //扩展DLL仅初始化一次 <br />　　if (!AfxInitExtensionModule(ActiveKeyDLL, hInstance))<br />return 0;<br />　　new CDynLinkLibrary(ActiveKeyDLL);<br />　　　　　　//把DLL加入动态MFC类库中 <br />　　glhInstance = hInstance;<br />　　//插入保存DLL实例句柄 <br />}<br />else if (dwReason == DLL_PROCESS_DETACH)<br />{<br />　　TRACE0("NotePadHOOK.DLL Terminating!\n");<br />　　//终止这个链接库前调用它 <br />　　AfxTermExtensionModule(ActiveKeyDLL);<br />}<br />return 1; <br />　　　} <br /><br />　　(8).编译项目ActiveKey，生成ActiveKey.DLL和ActiveKey.lib。<br /><br />　　接着，我们还需要创建一个外壳程序将全局钩子安装了<b>Windows</b>系统中，这个外壳程序编写步骤如下：<br /><br />　　(1).创建一个对话框模式的应用程序，项目名为Simulate。<br /><br />　　(2).在主对话框中加入一个按钮，使用ClassWizard为其创建CLICK事件。<br /><br />　　(3).将ActiveKey项目Debug目录下的ActiveKey.DLL和ActiveKey.lib拷贝到Simulate项目目录下。<br /><br />　　(4).从“工程”菜单中选择“设置”，弹出Project Setting对话框，选择Link标签，在“对象/库模块”中输入ActiveKey.lib。<br /><br />　　(5).将ActiveKey项目中的ActiveKey.h头文件加入到Simulate项目中，并在Stdafx.h中加入#include ActiveKey.h。<br /><br />　　(6).在按钮单击事件函数输入如下代码：<br /><br />　　　void CSimulateDlg::OnButton1() <br />　　　{<br />// TODO: Add your control notification handler code here<br />if( !bSetup )<br />{<br />m_hook.Start();//激活全局钩子。<br />}<br />else<br />{<br />m_hook.Stop();//撤消全局钩子。<br />}<br />bSetup = !bSetup;<br /><br />　　　} <br /><br />　　(7).编译项目，并运行程序，单击按钮激活外挂。<br /><br />　　(8).启动画笔程序，选择文本工具并将笔的颜色设置为红色，将鼠标放在任意位置后，按F10键，画笔程序自动移动鼠标并写下一个红色的大写R。图一展示了按F10键前的画笔程序的状态，图二展示了按F10键后的画笔程序的状态。<br /><br /><img src="http://www.cgfront.com/web/uploads/151.jpg" border="0" /><br />图一：按F10前状态(001.jpg)<br /><br /><br /><img src="http://www.cgfront.com/web/uploads/152.jpg" border="0" /><br />图二：按F10后状态(002.jpg)<br /><img src ="http://www.cppblog.com/eday/aggbug/15362.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eday/" target="_blank">松*</a> 2006-11-18 16:35 <a href="http://www.cppblog.com/eday/archive/2006/11/18/15362.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>