首页
社区
课程
招聘
[旧帖] [分享](自己翻译的)魔兽争霸1.24 Maphack教程 0.00雪花
发表于: 2011-3-2 21:38 3776

[旧帖] [分享](自己翻译的)魔兽争霸1.24 Maphack教程 0.00雪花

2011-3-2 21:38
3776

1.        写一个魔兽争霸3 1.24d的maphack [mAsm]
一场正在进行的暴风雪,使得所有快餐店都关了,看来无法摆脱我的宿舍了,正是如此,我再没有藉口去逃避写这篇教程了,该开始了:
在我谈论之前,我想有必要提出以下要点:这也是我首次hacking魔兽3,同时也是首次写maphack,因此所有出现在这篇文章的要点完全规定-我鼓励你去指出任何我可以制造的错误,因为我有时不像1337那样。带着这些不碍事的,让我们在这里开始。
  有需要提出的是你的地址可能会不同,因为Game.dll是一个可装载的dll,它伸请了6F000000H的空间,然而,它会意想不到地重新分配它自己,所以要记住。如果它真的做了(重新分配它自己),你有两个选择:重新开始游戏,或者简单地离开你的地址,把所有的在这个教程中的地址作为Game.dll+偏移的组合。哈哈,从每个地址中减去6F000000H,并把结果加入你的基址里。
   这篇教程是基于魔兽争霸3 版本1.24.3.6384(即1.24d)。如果你使用一个不同的版本,偏移很可能会改变,但是这个方法仍然可行。
   去尝试阻止人们hacking他们的游戏,暴雪公司放入了一些反调试的代码,主要以调用SetSecurityInfo()形式,这个函数修改Game.dll的DACL(任意访问控制表),并且不允许从外部修改。然而,知道这个我们通过修改SetSecurityInfo的能数可以很容易地修正这个行为,而这个参数不过是一些代表安全描述的位的组合罢了。
   现在我们怎么着手去找这个?好吧,我们知道Game.dll必须导出一个函数到主执行文件,所以最好开始在那个地方。
   最好的方法去着手调试魔兽争霸3是将它运行于窗口模式。你可以通过在它的快捷方式上点击右键,选属性,并在目标这个方框后面加上“ -window”(没有引号,但有个空格),
   打开魔兽争霸3使它在主屏幕里空闲,并用OLLY附加到它上面(文件->附加)。一旦它被附加了,查看Game.dll(右键->查看->Game)并按Ctrl+N弹出一些导入导出函数的列表。马上你会注意到Game.dll仅导出两个函数,只有一个我们感肖趣:
代码:
Names in Game, item 0
Address=6F009870
Section=.text
Type=Export
Name=GameMain
Names in Game, item 1
Address=6F7E1CE8
Section=.text
Type=Export
Name=<ModuleEntryPoint>

在GameMain上面按Enter,你应该到这里:
代码:

6F009870 > 81EC 08010000    SUB ESP,108
6F009876   A1 6041AC6F      MOV EAX,DWORD PTR DS:[6FAC4160]
6F00987B   33C4             XOR EAX,ESP
6F00987D   898424 04010000  MOV DWORD PTR SS:[ESP+104],EAX
6F009884   56               PUSH ESI
6F009885   8BB424 10010000  MOV ESI,DWORD PTR SS:[ESP+110]
6F00988C   E8 1F2F0000      CALL Game.6F00C7B0
6F009891   E8 7ABC6B00      CALL <JMP.&KERNEL32.GetTickCount>

这个函数调用一些函数,在Game.dll及在Kernel.dll里。但它看起来是最好的开始这些函数的地方,因为它有意义在于它们会保护这些模块,并在继续执行前使这些反调试弄到一边去。所以知道这些,进入到6F00C7B0H的调用:
代码:
6F00C7B0   81EC 20020000    SUB ESP,220
6F00C7B6   A1 6041AC6F      MOV EAX,DWORD PTR DS:[6FAC4160]
6F00C7BB   33C4             XOR EAX,ESP
6F00C7BD   898424 1C020000  MOV DWORD PTR SS:[ESP+21C],EAX

没有感兴趣的东西在这里,只是个开始的函数;然而,如果你向下滚动一些,你会这个宝贵的函数调用:
6F00C852   B8 A484886F      MOV EAX,Game.6F8884A4                    ; ASCII "SetSecurityInfo"
6F00C857   8BCE             MOV ECX,ESI
6F00C859   E8 32FFFFFF      CALL Game.6F00C790

这个有趣-想知道为什么它们把字符串传到EAX中,跟随到6F00C790H的调用,马上你可以见到为什么了:
代码:
6F00C790   50               PUSH EAX
6F00C791   51               PUSH ECX
6F00C792   FF15 BCF2876F    CALL DWORD PTR DS:[<&KERNEL32.GetProcAdd>; kernel32.GetProcAddress
6F00C798   85C0             TEST EAX,EAX
6F00C79A   75 03            JNZ SHORT Game.6F00C79F
6F00C79C   C2 0400          RETN 4
6F00C79F   8B5424 04        MOV EDX,DWORD PTR SS:[ESP+4]
6F00C7A3   8902             MOV DWORD PTR DS:[EDX],EAX
6F00C7A5   B8 01000000      MOV EAX,1
6F00C7AA   C2 0400          RETN 4

看来这个函数没有做什么只是取会这个传给EAX的地址。虽然我们知道这个,我们但我们也可以假定SetSecurityInfo很可能会在我们现在的函数中调用,所以回到6F00C7B0H,向下滚动直到你到了这里:

6F00C8AB   53               PUSH EBX
6F00C8AC   8D4424 30        LEA EAX,DWORD PTR SS:[ESP+30]
6F00C8B0   50               PUSH EAX
6F00C8B1   53               PUSH EBX
6F00C8B2   53               PUSH EBX
6F00C8B3   68 04000080      PUSH 80000004
6F00C8B8   6A 06            PUSH 6
6F00C8BA   55               PUSH EBP
6F00C8BB   FF5424 34        CALL DWORD PTR SS:[ESP+34]
6F00C8BF   85C0             TEST EAX,EAX
6F00C8C1   75 03            JNZ SHORT Game.6F00C8C6

好了马上烦挠我的是在6F00C8B3H行的内容-我们在处理WIN32 API时知道大部份位标志通常以一些8XXXXXXX的形式派生。另一样事情是跳过这个调用。因为SetSecurityInfo是现在这个函数最后的调用API,它看起来应该属于栈帧的顶部。
现在我们怎么处理这个?转换到C,这个函数看起来像:
SetSecurityInfo(ebp,6,0x80000004,ebx,ebx,eax,ebx);
在MSDN查这个函数,你可以看到第三个参数代表安全信息,如果我们简单地也一个NULL位标志入栈,函数会什么也不做。所以改变PUSH 80000004H成为PUSH 0,你很轻松到这里:
6F00C8B3   6A 00            PUSH 0
6F00C8B5   90               NOP
6F00C8B6   90               NOP
6F00C8B7   90               NOP

所有都完成的时候,最后我们可以设置断点了,开始hacking这个游戏了。
在我们开始做之前,我们应该给这一个想法,因为写一个maphack不是像搜索一个显示了的数值那么容易。所有我们真正可以继续下去的事实就是作弊码“iseedeadpeople”让我们查看全地图,并且自从我过去研究过星际争霸的引擎,我们可以假定设定地图可见的状态为0,然而搜索0 及未知值将会是一个错误的方向去处理很多地址,所以我们无论如何也要削减它们。
   然而我们也知道另一个事情,地图数组在地图开始时必须被初始化,所以我们可以用Olly找出那些内存段是从那里改变的。
   在魔兽争霸3里,转到单人游戏,开始自定义游戏,从那里,向动到列表尽头并选择Ice Crown作为你的地图(这个会在后面解释),现在,在你开始前,切换到Olly,按Alt+M弹出内存映射,并刷新它(右键->刷新),这所做的是基本上保存一个现时内存状态的参考指针,并且一旦我们再选择刷新,会比较这些内存段,并标出哪些被改变的。
   我们的参考指针设置了,切换到魔兽争霸3,开始你的游戏,并等待它装载。当最后你的单位出现在屏幕前,切换到Olly,并再次刷新内存并放大内存的方框,看看成堆的红和黄的地址。那些红色高亮的是我们感兴趣的。因为它们代表着改变的内存段。然而你会注意到它们混合着,所以现在开始分开它们两部分这个有趣的任务。
   不要在你排序时,在任何环境下给予魔兽争霸3焦点。所有的内存会被重刷新,而你会失去所有。
我建议你有时间才去做这件事,因为这会耗费一些时间并确保你做一些吸引你的事去使你的思想得到休息。例如:我在洗衣服。
我计厌洗衣服。
我建议的最简单的方法去做这件事就是按大小来排序这些地址(右键->排序->按大小),并复制整个表到记事本,并从底部(通常大小是1000H)到大小是9000H的我们可以删除。为什么?记得我是怎样选“Ice Crown”作为我们的地图?好,一个在GOOGLE上快速搜索会告诉你地图的大小是192* 192.假定地图是一系列的一字节元素组合到一个数组(不是一次坏的假定),我们知道魔兽争霸3需要申请最小36864字节内存,在十六进制中就是9000H。
余下黄色的,你不得不手动把那些去除,但超过列表的3/4是在9000H大小以下的,这个不会用太多时间。
一旦你拥有所有已排序的地址,是时候拿出便利的十六进制计算器,ArtMoney(或者任可内存搜索工具),并且有一些空余时间,开始搜索。当在搜索时,确保你的搜索在一个范围内,通过Option->Searching,并选择“Search in address range”方框里。我们也想确保我们减小我们的搜索甚至通过只搜索一字节的值。要做这个,当你弹出搜索框时,确保你选择的椭圆框下一个类型,并选择“Integer 1 byte”。不同于那个,所有我们知道的是当作弊激活,值是0,当作弊取消,它们会是未知的值。
在一些搜索后你会遇到一个范围的值会在作弊激活时变换为0 ,在作弊取消时是1,128.
典型地我这里弹出接近大小9000块内存,所以我会建议从底部开始。
带着你找到的地址,选择一个,然后在Olly转到它那里(Ctrl+G,在框里输入地址),然后设置一个内存断点(右键->断点->内存写入),并返回游戏,它应该立刻弹出,但如果它没有弹出,切换地图作弊码(iseedeadpeople)去强迫它。如果你选择了个一正确的地址,它应该在停在这里。
6F2AC1F7   66:894E 2E       MOV WORD PTR DS:[ESI+2E],CX

这个有趣-如果你向上滚动到函数的顶部并查询它的参考(在0F2AC1D0H上Ctrl+R),你会看到它被几个地方调用。给出这个,它很可能时一个共享的函数负责设定特定的几个值,所以我们需要去到上一个级别的函数(调用这个函数的函数)。按Ctrl+F9执行到返回,并按F7步入下一条指令,Olly 会引你到这里:
代码:
6F39A882  |. 52             PUSH EDX
6F39A883  |. 50             PUSH EAX
6F39A884  |. 8BCE           MOV ECX,ESI
6F39A886  |. E8 4519F1FF    CALL Game.6F2AC1D0   ;function we were just in
6F39A88B  |. 83BF 28030000 >CMP DWORD PTR DS:[EDI+328],0
   
嗯,这次我们离目标更近一些了。我们在一个更大的函数里,但我们没有它的任何线索。是时候拿起棒子反任意抗了,用NOP填充它,并观察。向上滚到顶部(6F39A7E0H)并选择全部代三直至6F39AB64H(留下 retn 4),并用NOP填充它(二制制->用NOP填充),回到你的游戏,你应该注意到没有树。好吧这更有趣了-看来共享函数被绘图函数所参考(即调用),并且我们也希望阴影函数也参考它,因为它们通常被连接到上面提及的函数。
回到6F2AC1D0H,并再次弹出参考的列表,这次全部完成它们。在用NOP操作符填充完这些在参考周围的代码后,你应该注意到两个没有做任何东西,然而有三个我们非常感兴趣:
6F39A475 - function controls drawing of sprites
6f39a886 - function controls drawing of trees
6f39bccd - function controls shade

不打算深入解释它,但是如果你看这个负责绘图的函数你会找到这个宝贵的函数
代码:
6F39A43F  |. 51             PUSH ECX                              ; /Arg4
6F39A440  |. 8D5424 14      LEA EDX,DWORD PTR SS:[ESP+14]            ; |
6F39A444  |. 894424 48      MOV DWORD PTR SS:[ESP+48],EAX            ; |
6F39A448  |. 52             PUSH EDX                               ; |Arg3
6F39A449  |. 8D4424 20      LEA EAX,DWORD PTR SS:[ESP+20]            ; |
6F39A44D  |. 50             PUSH EAX                                ; |Arg2
6F39A44E  |. 8D4C24 38      LEA ECX,DWORD PTR SS:[ESP+38]         ;
|6F39A452  |. 51             PUSH ECX                                ; |Arg1
6F39A453  |. 8B8F 78010000  MOV ECX,DWORD PTR DS:[EDI+178]           ; |
6F39A459  |. 896C24 20      MOV DWORD PTR SS:[ESP+20],EBP            ; |
6F39A45D  |. 896C24 28      MOV DWORD PTR SS:[ESP+28],EBP            ; |
6F39A461  |. E8 9A7B0000    CALL Game.6F3A2000            ; \Game.6F3A2000

当用NOP填充,你会注意到单位有时从不被显示,但有时在迷雾中显示,在它上面按Enter找出
6F3A2043     66:8B0C41      MOV CX,WORD PTR DS:[ECX+EAX*2]

可以注意到这个和Chotic的教程这间的相似点吗?
这个仅为了LOLS
这三个全部都有趣,然而最后的那个看起来最值得怀疑,因为当它用NOP填充时它在我们可见的区域都覆盖了阴影。
是时候看看这个什么时候被调用-向上滚动到6F89BC40H并通过按Ctrl+R弹出一个参考列表:
代码:
References in Game:.text to 6F39BC40
Address    Disassembly                               Comment
6F39BE22  MOV  EDX,Game.6F39BC40          6F39BC40=Game.6F39BC40
6F39C1DF  MOV EDX,Game.6F39BC40           6F39BC40=Game.6F39BC40

两个连续的MOV指令?这个相当奇怪。。。让我们看第一个尝试找出这里发生了什么。
代码:
6F39BE22  |. BA 40BC396F    MOV EDX,Game.6F39BC40
6F39BE27  |. 8BC8           MOV ECX,EAX
6F39BE29     E8 321E0D00    CALL Game.6F46DC60

   好那个比较有趣,从我制造它的地方,魔兽争霸3传送到指向先前的我们统治的函数指针送到寄存器,并且使用另一个普通函数去处理它。但是为什么有两个参考?我首个想法是可能魔兽争霸3用一个来为单位绘制合适的阴影,如果我们向上滚动到现在我们在里面的这个函数的顶部(6F39BDB0H),并用NOP填充这个从6F39D05CH的调用,我们可以看到我们所有的单位毁坏或者消失。如果你花费更多时间在这个函数你会看到它主要负责设定视半径,其它视野函数涉及单位。
所以留下其它参考:转到6F39C1DFH并且你应该到一个小函数里:
代码:
6F39C1C0     837C24 04 00   CMP DWORD PTR SS:[ESP+4],0
6F39C1C5     56             PUSH ESI
6F39C1C6     8BF1           MOV ESI,ECX
6F39C1C8     74 21          JE SHORT Game.6F39C1EB
6F39C1CA     6A 00          PUSH 0                      ; /Arg1 = 00000000
6F39C1CC     8D8E 00060000  LEA ECX,DWORD PTR DS:[ESI+600]           ; |
6F39C1D2     E8 198DF9FF    CALL Game.6F334EF0        ; \Game.6F334EF0
6F39C1D7     6A 00          PUSH 0
6F39C1D9     56             PUSH ESI
6F39C1DA     E8 910AEDFF    CALL Game.6F26CC70
6F39C1DF     BA 40BC396F    MOV EDX,Game.6F39BC40
6F39C1E4     8BC8           MOV ECX,EAX
6F39C1E6     E8 751A0D00    CALL Game.6F46DC60
6F39C1EB     8D86 00060000  LEA EAX,DWORD PTR DS:[ESI+600]
6F39C1F1     5E             POP ESI
6F39C1F2  \. C2 0400        RETN 4

你应该注意到同一个普通函数被调用到寄存器指针,所以领导我相信我们太低了。如果你向上滚动到6F39C1C0H,你会看到这个从四个地方被调用。
代码:
References in Game:.text to 6F39C1C0
Address    Disassembly                               Comment
6F3573A5   CALL Game.6F39C1C0
6F362147   CALL Game.6F39C1C0
6F3A34B2   CALL Game.6F39C1C0
6F40B5AE   CALL Game.6F39C1C0

如果你跟随前两个调用到不同的级别,你会注意到它们会到达同一个地方,并且看起来盘旋在单位阴影函数周围-所以让我们把焦点放在第三个上。跟随代码最终到达:
代码:
6F3A34AF     53             PUSH EBX
6F3A34B0     8BCF           MOV ECX,EDI
6F3A34B2     E8 098DFFFF    CALL Game.6F39C1C0

如果你向上滚动到函数的顶部你可以看到它被看起来涉及地图的函数所参考,自从我们知道直到调用6F39C1C0H地图指针才初始化,我们可以安全地忽略在它之前的任何东西。在6F3A34BDH的调用起来像普通的函数调用,所以可被忽略。但是在向下滚动之前看到一个字符串使我每次见到它时都大笑。
代码:
6F58AE6C  |. B9 446C976F  MOV ECX,Game.6F976C44 ;ASCII "LOADING_LOADING"

Derp derp, LOADING_LOADING! *
任何谁,去到下一个在6F3A34C5H的调用,第一件事突然出现在你前面的是memset:
代码:
6F40B446   . 52             PUSH EDX                                 ; /n
6F40B447     6A 00          PUSH 0
6F40B449   . 50             PUSH EAX                                 ; |s
6F40B44A   . E8 73643D00    CALL <JMP.&MSVCR80.memset>          ; \memset

好,好,设置了一大块内存为0.,退一步说相当可疑。用NOP填充整个调用(包括在6F40B44FH的栈平衡)并回到游戏。什么都没有。。。除非我们进入一个区域并离开,哇!看起来我们拥有负责重设地图数组每帧的这个部份代码!
是的,它很笨拙及浪费,看来地图数组每帧都重新设置,然后适当地反阴影。
但是怎么处理这个?好吧,让我们尝试改变PUSH 0 到一些更加用户友好的-像PUSH FH,在魔兽争霸3里显示每样东西到每个玩家。回到游戏,你应该注意到你现在拥有一个史诗般的maphack,开始编码!
自从有个相同的想法产生,一些基本的东西我逐字地从我先前的教程复制及粘贴过来。
我为我的懒惰道歉。

所以这里我很可能失去你们中的很多人,因为我们打算用汇编写。我会给你一个机会去点击在浏览器上的后退按钮。。。
那些仍在周围的,让我们开始吧。。。
我个人会用RadASM,虽然我强烈地建议,你拥有自由去用任何你想用的去写代码。我在用MASM汇编并连接,所以可能它里面的一些代码不能在其它ASM包中工作,但是它不应该太难编辑。
首先,让我们创建Shell(这是一个DLL不用猜测)
代码:
.386
.model flat,stdcall
option casemap:none

.code
    main:

    end main

现在,在代码段之前,但在选项之下,我们需要写一些包含文件:
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib

在我们继续之前,我们需要一些变量,所以让我们停下来想一想关于我们实际需要的。显然,我们需要声明一个字符串包含“Game.dll”所以我们可以得到我们的地址(因为Game.dll是动态分配,即使它申请6FXXXXXX作为基址),并且我们需要一些东西保留这个地址。所以在代码上,但在包含文件下面:
代码:
.data
    address dd 0
    dllname db "Game.dll"

好了,现在去到实际的DLL,在Main,在@@后面:
代码:
push ebp
        mov ebp,esp
        mov eax,dword ptr ss:[ebp+0ch]
        cmp eax,1
        jnz @returnf
        push eax
  
        pop eax
        @returnf:
        leave
        retn 0ch

我确实给了你离开的机会。如果你打算用汇编写代码,你最好准备用汇编方式写,而不是这些半HLA****,MASM现在支持
好了,基本的代码解释,因为这是一个DLL,一个我们主要关心的是保持所有东西完好及平衡。首先我们基址指针入栈,然后把栈顶指针传送到基址指针,这会基本告诉代码目前的程序的基址就是我们DLL的基址,这也是计算机先生需要知道的(在互联网上没有女孩,因此,电脑一直是伙计们的)以致可以找出返回以上调用的地方,跳转,计算,等等。PUSH保存了基址指针在栈里面,所以当我们离开我们的代码并返回,基址又再设为魔兽3的基址指针了。在那之后我们转送数据到基址指针+CH(12D),这个是持有调用我们的DLL的原因。
ebp = base
ebp + 4 = __stdcall DllMain
ebp + 8 = hModule
ebp + 12 = ul_reason_for_call
ebp + 16 = lpReserved *
  
如果原因是DLL_PROCESS_ATTACH(或者是1H),那么我们想执行我们的代码,否则我们想离开我们的DLL,并且返回执行流向魔兽3(确保栈平衡-3个参数=retn CH)
  EAX在我们的DLL返回时需要保持调用DLL的原因,所以我们在我们的函数调用开始前通过把它入栈来保存它,并在我们返回前把它从栈中弹出。
  马上,我们需要取回我们的地址-在我们的相反的聚会基址总是属于6F000000H,我们的代码在6F40B447H,所以从这里我们可以推断我们的的地址放在基址+40B448H。从那里,所有我们需要做的是取回目前的基址,我们很好完成:
    lea eax,dllname
    push eax
    call GetModuleHandle
    add eax,40b448h
mov address,eax

LEA代表载入有效地址,这是一个汇编的方式去把一个指向一段内存的指针送入寄存器。
现在,在我们可以开始写代码到我们的地址前,我们需要首先去反保护它,并且在我们可以做之前,我们段要释放一些空间去保存旧的保护类型。
代码:
push 40h
  push 1000h
    push 4h
  push 0
  call VirtualAlloc   
  mov ebx,eax
基础地,这在当前我们玩的进程里分配分配4字节的内存空间(MEM_COMMIT,PAGE_EXECUTE_READWRITE)我们把这个空间的地址传送到EBX,因为EAX会很快被下列的代码调用破坏:
接下来,在我们可以写之前,我们需要在我们的地址的代码去掉保护:
push ebx
push 40h
push 1h
push address
call VirtualProtect

  现在我们的数据没有保护,我们可以最终写到它里面:
mov eax,address
mov byte ptr ds:[eax],0fh

两个MOV指令看起来有点多,所以让我自己解释:因为地址保持这个编移,我们不能直接传送一个值给它-相反,我们需要把它传送它指向的值所以要实现这个,我们把它传送EAX,然后当我们的指针使用。

用我们的新地址现在写,是时候整理所有东西了:
push ebx
        push dword ptr ds:[ebx]
        push 1h
        push address
        call VirtualProtect   
        push 4000h
        push 4h
        push ebx
        call VirtualFree

最终代码:
.386                    ;attilathedud
.model flat,stdcall
option casemap:none

include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib

.data
    address dd 0
    dllname db "Game.dll"

.code
    main:
        push ebp
            mov ebp,esp
            mov eax,dword ptr ss:[ebp+0ch]
            cmp eax,1
            jnz @returnf
            push eax
            lea eax,dllname
        push eax
        call GetModuleHandle
        add eax,40b448h
        mov address,eax
        push 40h
            push 1000h
            push 4h
            push 0
            call VirtualAlloc   
        mov ebx,eax
        push ebx
            push 40h
            push 1h
            push address
            call VirtualProtect  
            mov eax,address
        mov byte ptr ds:[eax],0fh  
        push ebx
            push dword ptr ds:[ebx]
            push 1h
            push address
            call VirtualProtect   
            push 4000h
            push 4h
            push ebx
            call VirtualFree      
            pop eax
            @returnf:
            leave
            retn 0ch

end main

这是它所有的东西。现在,如果这个在Battle.net上被检测,还是没有,我不能说,因为我没有一份合法的魔兽3副本,但是一些可以告诉我的事情是:任何人,希望你喜欢这个教程,直到下次。


原文在3楼。


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 7
支持
分享
最新回复 (4)
雪    币: 2362
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
沒意思  重點應該在過VS HF
2011-3-2 21:47
0
雪    币: 73
活跃值: (319)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
原网站是:http://www.gamethreat.net/forums/open-source-tutorials/42277-writing-maphack-warcraft-3-1-24d-masm.html,刚看到今天就把它翻译了,累。本来想翻译来自己用来看的,但是想想分享给大家也是一种好的生活态度。个人英语水平不高,还望大家多多指正。
2011-3-2 21:50
0
雪    币: 73
活跃值: (319)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
4
回2楼:如果我写得出如何过VS,HF,我就不用翻译这篇比较基础的文章了,直接原创了。
2011-3-2 21:57
0
雪    币: 437
活跃值: (110)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
5
留个位子,明日细看
2011-3-2 22:56
0
游客
登录 | 注册 方可回帖
返回
//