我只有两天,一天用来听许巍,一天用来干别的。
今年没去outing,也很少给自己放假,积到年末就剩下了很多。这两天给自己放羊,计划 写点技术文章。起床后打开电脑,顺手放些音乐,是许巍的《每一刻都是崭新的》,不料上了瘾,又把《在别处》、《那一年》以及05年的北京演唱会折腾来,就 这么听了一整天。许巍的音乐,什么歌都一个调调,可它就有一种力量能让人沉迷进去,然后什么也干不了。今天起来学了乖,许巍还是想听的,为了不至于沉迷进 去,我找了另外一首歌换着听,也是我很喜欢的,很绝,特颓废,叫《Suicide is Painless》。
疯狂的玩着Diablo II的时候,我一个人在北京忙着拿学位。本来打算好毕业后就离开北京,离开这个我已经呆了8年的城市,一个偶然的原因让我又留了下来,多待了半年。那半年 陪着我的除了工作加班,就只有《时光.漫步》和逆向工程做Maphack。从小学玩街机开始,我玩过很多游戏,用过的游戏作弊软件数不清,但我从来没想到 有一天我会自己做外挂。Diablo是我非常喜欢的一款游戏,从第一代开始,Diablo、HellFire、Diablo II、 Diablo II LOD(毁灭之王)都玩过,但上Closed server是很后来的事,已是09的后期。经历过09时代的暗黑玩家都知道,那时Maphack是标准配备,差不多人人都用,很多私服甚至把它做为唯一 允许使用的外挂。我自然也用,从最初的单纯使用,后来学会自己改配置文件,再后来开始看一些源代码。
万众期待的1.10补丁发布以后,我和很多玩家一样等着新的Maphack出来。由于在此之前Maphack的作者鼠标垫(MousePad)在1.10 补丁beta 2发布后第一时间就推出了相应的Maphack版本(即Maphack 5.1s),大家都认为针对1.10正式版的Maphack也会很快。不料等了一周没任何动静。这时候有人(jhj)做了个只能开地图的简易版 maphack。又过了一周还是没动静,我有点儿等得不耐烦,在一天上班的路上,我琢磨着这个事,突然有了想法,想自己也做一个。我自然不想做简易版,这 个已经有人做过了,再做没意思。鼠标垫老兄倒是公布过一份maphack的早期版本的源代码(maphack 4.6),不过相对于最新版本来说功能太少,在此基础上再实现新功能工作量还是挺大的,而我只想做个临时版。想来想去,觉得比较简单的做法是在最新的 Maphack程序上改出一个能给1.10用的版本。这么做有几个问题要想清楚,一个主要的困难在于数据结构可能改变。比如,在1.10中小场景的数据表 示如下:
在1.11中变成这样:
如 你所见,数据还是那些数据,但是相对位置变了。在有maphack源代码的情况下,这是一个简单的问题,只要在头文件中重新定义一下DrlgLevel结 构再编译一下就行了。问题是我没有源代码,只能在现有maphack程序上改,只要maphack用到的游戏内部数据结构中的一个改了,这个想法就完蛋。 但是仔细想了一下,我觉得这点不会有问题,因为最新的Maphack是for 1.10 beta 2的,beta 2是正式版发布之前的最后一个beta版,一般来说软件开发在beta阶段主要任务是改bug,程序框架和主要的数据结构不应该有什么变化,最后的 beta版改动就更小了。另外一个问题当然是必须找出maphack所有的patch点和引用的内部函数在1.10中的相应地址,这些共有100多个,数 量看起来是挺吓人,不过不存在技术问题。一个有利的条件是通过研究maphack 4.6的源代码和反汇编maphack 5.1s程序,我知道这些patch点和引用的内部函数地址在内存中的位置是连续的,这大大有利于二进制级别的修正。总之经过一番分析,我觉得在最新程序 的基础上改应该是可行的。事实证明我的想法也没有错,除了有一点不至于导致程序崩溃的数据结构有所改变外,其他的基本没什么问题。大约花了一周时间,我把 100多个地址找齐了,做出第一个版本。又做了一些测试、改bug,把它稳定了下来。
刚开始的时候我的想法很简单,只是想在真正的Maphack 发布之前给大家一个可用的东西,不至于摸黑。我也把它当成一个玩具,顺便玩玩逆向工程,并不打算得到什么回报,因此我只把它放到了我当时所在私服的BBS (ZIXIA)上。但是什么东西一到网络上,事情就不归你控制,于是很快就有人把它放到公众网上,再过不久又被老外发现,又到了国外网站。老外很热心,甚 至给它建了个技术支持网站。老外一知道,不可避免地原作者也会知道,终于在Maphack的支持论坛上引起了轩然大波。很多老鼠垫(MousePad)的 粉丝们破口大骂,说这是ripoff,是trojan,还有的说我没有给鼠标垫credit。鼠标垫本人也很不满,主要理由是这个东西已经被我改过,不能 算他的作品(出什么问题不该归他负责),应该换个名字,至少maphack加载时显示的这句话应该改掉:
不过从事后来看,鼠标垫对此不满还有另外一个原因:他其实早就把新版本升级好了,迟迟不发布是打算拿Maphack卖钱,那当时正忙于做防盗版功 能。我这 么半道一横杠子插进来,正好挡了人家的财道!其实直到现在我对鼠标垫本人还是挺尊重的,这位老兄在D2 hacking上做了很多原创性贡献,逆向工程的本领没的说,C程序也是写的无比精巧,绝对是世界一流的黑客。要是事先知道他打算卖钱,我是肯定不会弄出 这么个东西的。
名字的问题其实我在发布前也有考虑。首先,正因为我尊重鼠标垫老兄,所以从一开始我就打算给他credit,否则早就把名字改了。再者,我做的工作也就是 修正一下内存地址(虽然比较多),绝大部分的代码还是他的,我当然不认为这是我的软件。最后,我只是想做一个临时的替代品,也没想搞出多大动静,因此起什 么名字本来就是无所谓的。不过事后看起来,比较合适的做法还是应该改改那句话,成这样:
不管怎样,这个事的结果是我落了个吃力不讨好。最不爽的是那帮粉丝整天在那儿ripoff ripoff的骂,把我惹得火大,你们不是说我ripoff吗,那我要配得上这个称号,我得给它整个名副其实的ripoff,我要把Maphack 5.1s整个程序reverse过来,反汇编成C,在此基础上再加一些功能,做一个和maphack竞争的软件。
完全反汇编一个程序到C是一件很疯狂的事情,一般情况下很难做到,我以前也从来没做过,不要说做,连想都没想过。不过火大归火大,做事情还是要理性的,这 事儿到底可不可行,能不能干,得好好分析分析。玩逆向工程,我觉得最重要的一点是大局观一定要好,也就是说你有一个想法,到底可不可行,在技术上要能把握 住,事先就得想清楚。因为逆向工 程随便一个程序,面对的汇编代码往往都是上百万数量级的,就像大海捞针,如果没事先想清楚,很容易一个猛子扎进去结果什么都没捞着,打击是很大的。比较而 言正向程序设计上就要容易的多,要实现一个想法,你可以google别人的做法,你有很多基础库可以利用,你技术能力弱,写的代码也就是丑陋点儿,效率低 点儿,不至于做不出来。
在这个问题上,我觉得我有两个有利条件。一是有maphack 4.6的源代码,虽然最新版本在功能上增加了很多,但基本框架是没变的。另一个有利条件是鼠标垫是C语言高手,代码很精练,一句废话都没有,对于性能来说 这虽然很好,但是从逆向工程的角度看反而是缺点。最后,我花了两周时间把它反汇编出来,又发出4个beta版本改bug。
这就是d2hackmap的由来。我之所以把它命名为hackmap,主要有几层含义,一是名字和maphack很像,大家一看就知道是什么东西。事实上 不但名字像,功能上也完全兼容原来的程序。这是我的一大设计目标,用过maphack的人都知道它的配置文件是很复杂的,如果我另外搞一套,肯定会有非常 多的技术支持问题,会把我累死。而maphack的使用大家都早已熟悉,网上有很多论坛在讨论它的使用,兼容maphack无疑能把我的技术支持工作量降 到最小(即使是这样我还是收到了大量的邮件)。第二层含义是hackmap = hack maphack,也就是说hackmap是我hack了maphack得来的。第三层含义是恶搞鼠标垫一把,故意拿他开涮。
我玩D2最起劲的时候还是在09时期,1.10时期虽然也玩,但是已经很少了,1.11基本不玩。本来都没打算做1.11的hackmap,因为那时候我 已经知道warden不好对付,搞不好别人用了被BAN我又是吃力不讨好,所以刚开始只做了一个相对安全点儿的只能开地图的版本,没花多少功夫。后来看到 很多人还是强烈希望想有一个全功能版本,就又做了一个,反正也只是体力劳动的问题。不过全功能版本的anti-warden我从一开始就没打算做, maphack这种软件和游戏本身耦合的太紧密,要把自己隐藏起来工作量很大,即使做出来也是白做(除了挣点儿经验值)。我认为对抗到最后还是 warden会赢,因为maphack(注意我只是说maphack类外挂)没办法对付warden的数据完整性检查。想想maphack最基本的功能是 什么?开地图!如果你进入游戏后5秒钟内就把所有场景地图都打开了,不是作弊就见鬼了。还有,如果你每次进入游戏都直飞目的地,一点弯路都不带绕的(D2 的场景地图是随机的),除了作弊还能是什么。如果warden针对这些数据进行检测,必死无疑。当然warden目前为止还没进化到这一步。
最后,我想说一句,人分三六九等,外挂也分善意的和恶意的。像maphack这种就是善意的,它的目标是帮助玩家更好的进行游戏。甚至它还有一些防恶意外 挂的功能。比如有些恶意外挂利用游戏程序的bug可以让别的玩家掉线,maphack就可以避免这种情况,这相当于给游戏改了bug。用d2loader 的玩家知道d2loader在过关时有bug,会导致游戏退出,hackmap就改了这个bug。还有,迄今为止我给hackmap做的比较费劲的一个功 能就是支持中文聊天(最早不是我做的)。D2的玩家都知道,D2游戏本身不支持中文输入,在游戏中输入的汉字都是乱码,要交流只能用拼音,或者英文。 hackmap在1.15和1.16版本中增加了对中文输入的支持,不过我一直觉得不够满意,没能解决所有问题。其中的原因主要是因为D2用到了三种字符 集:ANSI/MBCS,UNICODE和UTF-8:通常情况下用ANSI/MBCS,和多语言有关的部分用UNICODE,通过网络传输时又转换成 UTF-8,比如聊天信息(大致的情况是这样的,具体的记不确切了)。这几种字符集之间互相转换,D2还有简体中文和繁体中文两种版本,错综复杂,在没有 源代码的情况下很难把所有情况都照顾 到,所以在hackmap 2.0以后,我干脆又把这一功能取消了。要彻底解决这个问题只能重新设计游戏代码。
今年没去outing,也很少给自己放假,积到年末就剩下了很多。这两天给自己放羊,计划 写点技术文章。起床后打开电脑,顺手放些音乐,是许巍的《每一刻都是崭新的》,不料上了瘾,又把《在别处》、《那一年》以及05年的北京演唱会折腾来,就 这么听了一整天。许巍的音乐,什么歌都一个调调,可它就有一种力量能让人沉迷进去,然后什么也干不了。今天起来学了乖,许巍还是想听的,为了不至于沉迷进 去,我找了另外一首歌换着听,也是我很喜欢的,很绝,特颓废,叫《Suicide is Painless》。
疯狂的玩着Diablo II的时候,我一个人在北京忙着拿学位。本来打算好毕业后就离开北京,离开这个我已经呆了8年的城市,一个偶然的原因让我又留了下来,多待了半年。那半年 陪着我的除了工作加班,就只有《时光.漫步》和逆向工程做Maphack。从小学玩街机开始,我玩过很多游戏,用过的游戏作弊软件数不清,但我从来没想到 有一天我会自己做外挂。Diablo是我非常喜欢的一款游戏,从第一代开始,Diablo、HellFire、Diablo II、 Diablo II LOD(毁灭之王)都玩过,但上Closed server是很后来的事,已是09的后期。经历过09时代的暗黑玩家都知道,那时Maphack是标准配备,差不多人人都用,很多私服甚至把它做为唯一 允许使用的外挂。我自然也用,从最初的单纯使用,后来学会自己改配置文件,再后来开始看一些源代码。
万众期待的1.10补丁发布以后,我和很多玩家一样等着新的Maphack出来。由于在此之前Maphack的作者鼠标垫(MousePad)在1.10 补丁beta 2发布后第一时间就推出了相应的Maphack版本(即Maphack 5.1s),大家都认为针对1.10正式版的Maphack也会很快。不料等了一周没任何动静。这时候有人(jhj)做了个只能开地图的简易版 maphack。又过了一周还是没动静,我有点儿等得不耐烦,在一天上班的路上,我琢磨着这个事,突然有了想法,想自己也做一个。我自然不想做简易版,这 个已经有人做过了,再做没意思。鼠标垫老兄倒是公布过一份maphack的早期版本的源代码(maphack 4.6),不过相对于最新版本来说功能太少,在此基础上再实现新功能工作量还是挺大的,而我只想做个临时版。想来想去,觉得比较简单的做法是在最新的 Maphack程序上改出一个能给1.10用的版本。这么做有几个问题要想清楚,一个主要的困难在于数据结构可能改变。比如,在1.10中小场景的数据表 示如下:
struct DrlgLevel {
DrlgMisc *pDrlgMisc; //+00
DWORD nLevelNo; //+04
DWORD _1[3];
D2Seed seed; //+14
DWORD _2[5];
DrlgRoom2 *pRoom2First; //+30
DWORD _3[126];
DrlgLevel *pLevelNext; //+22c
};
DrlgMisc *pDrlgMisc; //+00
DWORD nLevelNo; //+04
DWORD _1[3];
D2Seed seed; //+14
DWORD _2[5];
DrlgRoom2 *pRoom2First; //+30
DWORD _3[126];
DrlgLevel *pLevelNext; //+22c
};
在1.11中变成这样:
struct DrlgLevel { //sizeof(DrlgLevel) = 0x230
DWORD _1[5];
DWORD nLevelNo; //+14
DWORD _1a[120];
D2Seed seed; //+1f8
DWORD _2[1];
DrlgRoom2 *pRoom2First; //+204
DrlgMisc *pDrlgMisc; //+208
DWORD _3[8];
DrlgLevel *pLevelNext; //+22c
};
DWORD _1[5];
DWORD nLevelNo; //+14
DWORD _1a[120];
D2Seed seed; //+1f8
DWORD _2[1];
DrlgRoom2 *pRoom2First; //+204
DrlgMisc *pDrlgMisc; //+208
DWORD _3[8];
DrlgLevel *pLevelNext; //+22c
};
如 你所见,数据还是那些数据,但是相对位置变了。在有maphack源代码的情况下,这是一个简单的问题,只要在头文件中重新定义一下DrlgLevel结 构再编译一下就行了。问题是我没有源代码,只能在现有maphack程序上改,只要maphack用到的游戏内部数据结构中的一个改了,这个想法就完蛋。 但是仔细想了一下,我觉得这点不会有问题,因为最新的Maphack是for 1.10 beta 2的,beta 2是正式版发布之前的最后一个beta版,一般来说软件开发在beta阶段主要任务是改bug,程序框架和主要的数据结构不应该有什么变化,最后的 beta版改动就更小了。另外一个问题当然是必须找出maphack所有的patch点和引用的内部函数在1.10中的相应地址,这些共有100多个,数 量看起来是挺吓人,不过不存在技术问题。一个有利的条件是通过研究maphack 4.6的源代码和反汇编maphack 5.1s程序,我知道这些patch点和引用的内部函数地址在内存中的位置是连续的,这大大有利于二进制级别的修正。总之经过一番分析,我觉得在最新程序 的基础上改应该是可行的。事实证明我的想法也没有错,除了有一点不至于导致程序崩溃的数据结构有所改变外,其他的基本没什么问题。大约花了一周时间,我把 100多个地址找齐了,做出第一个版本。又做了一些测试、改bug,把它稳定了下来。
刚开始的时候我的想法很简单,只是想在真正的Maphack 发布之前给大家一个可用的东西,不至于摸黑。我也把它当成一个玩具,顺便玩玩逆向工程,并不打算得到什么回报,因此我只把它放到了我当时所在私服的BBS (ZIXIA)上。但是什么东西一到网络上,事情就不归你控制,于是很快就有人把它放到公众网上,再过不久又被老外发现,又到了国外网站。老外很热心,甚 至给它建了个技术支持网站。老外一知道,不可避免地原作者也会知道,终于在Maphack的支持论坛上引起了轩然大波。很多老鼠垫(MousePad)的 粉丝们破口大骂,说这是ripoff,是trojan,还有的说我没有给鼠标垫credit。鼠标垫本人也很不满,主要理由是这个东西已经被我改过,不能 算他的作品(出什么问题不该归他负责),应该换个名字,至少maphack加载时显示的这句话应该改掉:
<Maphack>: Mousepad's Diablo II Maphack v1.10 (v5.1s) installed.
不过从事后来看,鼠标垫对此不满还有另外一个原因:他其实早就把新版本升级好了,迟迟不发布是打算拿Maphack卖钱,那当时正忙于做防盗版功 能。我这 么半道一横杠子插进来,正好挡了人家的财道!其实直到现在我对鼠标垫本人还是挺尊重的,这位老兄在D2 hacking上做了很多原创性贡献,逆向工程的本领没的说,C程序也是写的无比精巧,绝对是世界一流的黑客。要是事先知道他打算卖钱,我是肯定不会弄出 这么个东西的。
名字的问题其实我在发布前也有考虑。首先,正因为我尊重鼠标垫老兄,所以从一开始我就打算给他credit,否则早就把名字改了。再者,我做的工作也就是 修正一下内存地址(虽然比较多),绝大部分的代码还是他的,我当然不认为这是我的软件。最后,我只是想做一个临时的替代品,也没想搞出多大动静,因此起什 么名字本来就是无所谓的。不过事后看起来,比较合适的做法还是应该改改那句话,成这样:
<Maphack>: Mousepad's Diablo II Maphack v1.10 (v5.1s) sting edited version installed.
不管怎样,这个事的结果是我落了个吃力不讨好。最不爽的是那帮粉丝整天在那儿ripoff ripoff的骂,把我惹得火大,你们不是说我ripoff吗,那我要配得上这个称号,我得给它整个名副其实的ripoff,我要把Maphack 5.1s整个程序reverse过来,反汇编成C,在此基础上再加一些功能,做一个和maphack竞争的软件。
完全反汇编一个程序到C是一件很疯狂的事情,一般情况下很难做到,我以前也从来没做过,不要说做,连想都没想过。不过火大归火大,做事情还是要理性的,这 事儿到底可不可行,能不能干,得好好分析分析。玩逆向工程,我觉得最重要的一点是大局观一定要好,也就是说你有一个想法,到底可不可行,在技术上要能把握 住,事先就得想清楚。因为逆向工 程随便一个程序,面对的汇编代码往往都是上百万数量级的,就像大海捞针,如果没事先想清楚,很容易一个猛子扎进去结果什么都没捞着,打击是很大的。比较而 言正向程序设计上就要容易的多,要实现一个想法,你可以google别人的做法,你有很多基础库可以利用,你技术能力弱,写的代码也就是丑陋点儿,效率低 点儿,不至于做不出来。
在这个问题上,我觉得我有两个有利条件。一是有maphack 4.6的源代码,虽然最新版本在功能上增加了很多,但基本框架是没变的。另一个有利条件是鼠标垫是C语言高手,代码很精练,一句废话都没有,对于性能来说 这虽然很好,但是从逆向工程的角度看反而是缺点。最后,我花了两周时间把它反汇编出来,又发出4个beta版本改bug。
这就是d2hackmap的由来。我之所以把它命名为hackmap,主要有几层含义,一是名字和maphack很像,大家一看就知道是什么东西。事实上 不但名字像,功能上也完全兼容原来的程序。这是我的一大设计目标,用过maphack的人都知道它的配置文件是很复杂的,如果我另外搞一套,肯定会有非常 多的技术支持问题,会把我累死。而maphack的使用大家都早已熟悉,网上有很多论坛在讨论它的使用,兼容maphack无疑能把我的技术支持工作量降 到最小(即使是这样我还是收到了大量的邮件)。第二层含义是hackmap = hack maphack,也就是说hackmap是我hack了maphack得来的。第三层含义是恶搞鼠标垫一把,故意拿他开涮。
我玩D2最起劲的时候还是在09时期,1.10时期虽然也玩,但是已经很少了,1.11基本不玩。本来都没打算做1.11的hackmap,因为那时候我 已经知道warden不好对付,搞不好别人用了被BAN我又是吃力不讨好,所以刚开始只做了一个相对安全点儿的只能开地图的版本,没花多少功夫。后来看到 很多人还是强烈希望想有一个全功能版本,就又做了一个,反正也只是体力劳动的问题。不过全功能版本的anti-warden我从一开始就没打算做, maphack这种软件和游戏本身耦合的太紧密,要把自己隐藏起来工作量很大,即使做出来也是白做(除了挣点儿经验值)。我认为对抗到最后还是 warden会赢,因为maphack(注意我只是说maphack类外挂)没办法对付warden的数据完整性检查。想想maphack最基本的功能是 什么?开地图!如果你进入游戏后5秒钟内就把所有场景地图都打开了,不是作弊就见鬼了。还有,如果你每次进入游戏都直飞目的地,一点弯路都不带绕的(D2 的场景地图是随机的),除了作弊还能是什么。如果warden针对这些数据进行检测,必死无疑。当然warden目前为止还没进化到这一步。
最后,我想说一句,人分三六九等,外挂也分善意的和恶意的。像maphack这种就是善意的,它的目标是帮助玩家更好的进行游戏。甚至它还有一些防恶意外 挂的功能。比如有些恶意外挂利用游戏程序的bug可以让别的玩家掉线,maphack就可以避免这种情况,这相当于给游戏改了bug。用d2loader 的玩家知道d2loader在过关时有bug,会导致游戏退出,hackmap就改了这个bug。还有,迄今为止我给hackmap做的比较费劲的一个功 能就是支持中文聊天(最早不是我做的)。D2的玩家都知道,D2游戏本身不支持中文输入,在游戏中输入的汉字都是乱码,要交流只能用拼音,或者英文。 hackmap在1.15和1.16版本中增加了对中文输入的支持,不过我一直觉得不够满意,没能解决所有问题。其中的原因主要是因为D2用到了三种字符 集:ANSI/MBCS,UNICODE和UTF-8:通常情况下用ANSI/MBCS,和多语言有关的部分用UNICODE,通过网络传输时又转换成 UTF-8,比如聊天信息(大致的情况是这样的,具体的记不确切了)。这几种字符集之间互相转换,D2还有简体中文和繁体中文两种版本,错综复杂,在没有 源代码的情况下很难把所有情况都照顾 到,所以在hackmap 2.0以后,我干脆又把这一功能取消了。要彻底解决这个问题只能重新设计游戏代码。