【文章标题】: 是男人就上120层 Patch
【文章作者】: Nukou.G
【软件名称】: 是男人就上120层
【软件大小】: 480K
【下载地址】: 黑客X档案4月份附书光盘中
【加壳方式】: 无
【保护方式】: 序列号
【编写语言】: VC4.0
【使用工具】: OllyDBG
【操作平台】: WinXP
【软件介绍】: 这个游戏没玩过也该听过吧
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
每次这个程序启动都会显示一个对话框:“BGM1.MID was not found. Please put it in the same folder as application file.”很烦人,没有这个文件程序一样运行,所以把这个对话框跳过去。顺便再给游戏加一个功能:1-=键分别对应12个弹跳力度,不用按住空格键蓄力了(当然这样难度降低了)。
先是去掉那恼人的对话框:在MessageBoxA函数上下断点,运行会断在
0040495F |. /0F85 4E000000 jnz 004049B3
00404965 |. |68 00010000 push 100 ; /Count = 100 (256.)
0040496A |. |8D85 00FFFFFF lea eax, dword ptr [ebp-100] ; |
00404970 |. |50 push eax ; |Buffer
00404971 |. |6A 05 push 5 ; |RsrcID = STRING "NS-TOWER Error"
00404973 |. |A1 98D24000 mov eax, dword ptr [40D298] ; |
00404978 |. |50 push eax ; |hInst => 00400000
00404979 |. |FF15 BCF34000 call dword ptr [<&USER32.LoadStringA>>; \LoadStringA
0040497F |. |68 00010000 push 100 ; /Count = 100 (256.)
00404984 |. |8D85 00FEFFFF lea eax, dword ptr [ebp-200] ; |
0040498A |. |50 push eax ; |Buffer
0040498B |. |6A 06 push 6 ; |RsrcID = STRING "BGM1.MID was not found. Please put it in the same folder as application file."
0040498D |. |A1 98D24000 mov eax, dword ptr [40D298] ; |
00404992 |. |50 push eax ; |hInst => 00400000
00404993 |. |FF15 BCF34000 call dword ptr [<&USER32.LoadStringA>>; \LoadStringA
00404999 |. |6A 30 push 30 ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
0040499B |. |8D85 00FFFFFF lea eax, dword ptr [ebp-100] ; |
004049A1 |. |50 push eax ; |Title
004049A2 |. |8D85 00FEFFFF lea eax, dword ptr [ebp-200] ; |
004049A8 |. |50 push eax ; |Text
004049A9 |. |8B45 08 mov eax, dword ptr [ebp+8] ; |
004049AC |. |50 push eax ; |hOwner
004049AD |. |FF15 B8F34000 call dword ptr [<&USER32.MessageBoxA>>; \MessageBoxA
把
0040495F |. /0F85 4E000000 jnz 004049B3
处的jnz改成jmp就可以
然后是给程序加上快捷键:先要找到游戏的过程函数,起初我想在NewGame那个按钮上找找,结果什么也找不到=.=。然后我注意到WINMM.sndPlaySoundA这个函数,蓄力时是不是用这个函数发出声音的?在这个函数上下断点后断在:
00406E47 |. FF15 58F44000 call dword ptr [<&WINMM.sndPlaySoundA>; WINMM.sndPlaySoundA
00406E4D |. 817D 0C 9D000>cmp dword ptr [ebp+C], 9D
00406E54 |. 0F87 1B000000 ja 00406E75
哈哈,我们到游戏的过程里了!给这个函数改名为MakeSnd(在这个函数的开始位置00406E3D上右键>编辑标签>输入名称),给这个大函数(004067DA )命名为Fun
跟着这个函数返回到一个开始于004067DA结束于00406DA2的函数。那这个函数做了些什么呢?这个函数调用的函数们又都做了什么呢?你肯定不愿意去读这么长的汇编代码,而且不知道它是不是真的是我们要找的函数,当然我也是:P。
这里我有个小技巧:把OllyDBG的窗口和游戏窗口分开,不要让OllyDBG的窗口盖住游戏窗口,然后在每个CALL之前都下断(MakeSnd函数就算了,我们知道它做什么的了),多按几次F9,会发现游戏总是在下面4个地方被断:
0040681B |. E8 44FCFFFF call 00406464
00406837 |. E8 6FF7FFFF call 00405FAB
00406D24 |. E8 EAE3FFFF call 00405113
00406D30 |. E8 DAEEFFFF call 00405C0F
而且每次执行
00406D24 |. E8 EAE3FFFF call 00405113
这句之后小人就会向前走一步,那我们就给这个函数命名为Walk,然后把所有call Walk处的断点去掉。
但是程序依然一直在剩下的3个位置中断,这是因为我们没有对游戏窗口进行任何输入。而我们要收集更多的信息来了解程序,所以把这3个函数分别命名为Fun1、Fun2、Fun3,然后去掉这些断点。F9,现在游戏没有被中断。
切到游戏窗口,按一下空格,这次程序中断在
004068F1 |. E8 9DF1FFFF call 00405A93
我们F8一下,如果你的游戏窗口位置放的合适的话,你会发现POWER槽涨了一格!把这个函数命名为ChgPow。存放力度的内存地址很有可能在这个函数里找到。先看看函数的参数(我这里是0x144108):00144108 C2 05 01 00不是很象。只有跟进去了,好在这个函数不是很长,然后我第一眼就看到了可疑的BitBlt的可疑的参数:
00405ABA |. 8B45 08 mov eax, dword ptr [ebp+8] ; |
00405ABD |. 8B80 A8110000 mov eax, dword ptr [eax+11A8] ; |<这里的时候看看EAX
00405AC3 |. C1E0 04 shl eax, 4 ; |
00405AC6 |. 05 A0000000 add eax, 0A0 ; |
00405ACB |. 50 push eax ; |YSrc
跟踪的时候注意寄存器,在执行mov eax, dword ptr [eax+11A8]后看看EAX的值,再看看力度槽,我们找到存放力度的内存了!就是距ChgPow函数的唯一的参数指向的位置+11A8的地方,现在我们回到刚才的函数看看给ChgPow传了什么:
004068ED |. 8B45 08 mov eax, dword ptr [ebp+8]
004068F0 |. 50 push eax
004068F1 |. E8 9DF1FFFF call <ChgPow>
原来是这个函数的第一个参数。
现在我们继续我们的分析,把call ChgPow处的断点去掉,回到游戏中按一下空格,程序又中断了:
00406AE7 |. E8 BFF4FFFF call <Fun2>
又见Fun2,不过除了几个寄存器,画面和力度都没有改变,它到底是干什么的呢?刚才我就在想:小人能跑能跳,跑的函数我找到了,跳的函数跑哪里去了啊?其实刚才调试的过程中我们的小人已经跳了好几下了,而程序会断的几个函数我们都识别出来了,这个Fun2除外,不用想了,它就是Jump函数了。如果你需要我证明的话,你进去游戏按住空格蓄力试试:蓄力的过程中程序没有中断,而松开的时候程序中断在Fun2这个位置,而这个时候就是小人起跳的时候。
多玩一会你就会认出
00406A47 |. E8 72F8FFFF call 004062BE
处的是移动屏幕的MovScr函数
00406CB2 |. E8 57DCFFFF call 0040490E
处的是玩家死亡的Die函数
不过由于它们与我们的目标毫无关联,这里就不赘述了,有兴趣的朋友可以自己跟一下。
现在我们要找的是检查玩家按键的地方,到哪里找?还是刚才我们分析的那个函数。仔细想想:肯定是我们按了蓄力键才会调用ChgPow函数,那我们没按键的时候一定有一个跳转跳过ChgPow函数。从004068F1 call <ChgPow>处向上检查各个跳转。我们够幸运,遇到的第一跳转就那么可疑:
004068B3 |> \8B45 08 mov eax, dword ptr [ebp+8]
004068B6 |. 83B8 08130000>cmp dword ptr [eax+1308], 1
004068BD |. 0F85 66000000 jnz 00406929
看来[ebp+8]+1308就是传说中的标记了,找到该内存地址并下内存写断点。F9,游戏正常运行,我们切过去按一下空格,断在
00404352 |. C780 08130000>mov dword ptr [eax+1308], 1
现在我们找到了判断按键的地方了!把这个语句所在的函数命名为CheKey,在这附近很有可能有修改力度的语句,力度放在距ChgPow函数的参数指向的位置+11A8的地方,有意思的是CheKey的第一参数指向的也是那个位置...
因为我们要给CheKey做手术,所以需要弄明白它的流程,这个函数等价于:
void CheKey(PDWORD pPower, DWORD key, DWORD flag, DWORD, DWORD)
{
if (key == ' ')
{
if (flag == 0)
{
KeyIsPressed = 0;
}
else
{
KeyIsPressed = 1;
if (*(pPower+0x1190) == 0x3)
{
Pause(pPower);
}
}
}
else if (key == 0x1B)
{//0x1B是哪个键...
if (flag != 0)
{
if (*(pPower+0x1190) == 0x3)
{
Pause(pPower);
}
}
}
}
出乎意料的是这个函数没有修改力度,我们得到别处去找到底是哪个位置修改了力度(必须知道游戏原来是怎么做的,我们才能在那基础上修改而不造成错误)。在存放力度的内存下内存写断点,F9,进游戏蓄力,断在:
004068B6 |. 83B8 08130000>cmp dword ptr [eax+1308], 1
004068BD |. 0F85 66000000 jnz 00406929
004068C3 |. 8B45 08 mov eax, dword ptr [ebp+8]
004068C6 |. C780 AC110000>mov dword ptr [eax+11AC], 1
004068D0 |. 8B45 08 mov eax, dword ptr [ebp+8]
004068D3 |. B9 0C000000 mov ecx, 0C
004068D8 |. 8B80 A8110000 mov eax, dword ptr [eax+11A8]
004068DE |. 99 cdq
004068DF |. F7F9 idiv ecx
004068E1 |. 8D42 01 lea eax, dword ptr [edx+1]
004068E4 |. 8B4D 08 mov ecx, dword ptr [ebp+8]
004068E7 |. 8981 A8110000 mov dword ptr [ecx+11A8], eax <断在这里
看来他是判断是否KeyIsPressed标志来给力度加一的。
现在需要知道的都知道了,只剩写代码了。我的策略是:在各个新加的功能键的事件里,把力度修改成原计划的力度-1(1对0,2对1...-对10,=对11)并把KeyIsPressed置1,这样004068E7处加以后就达成了原来的目标了(我不想说的这么拗口的=.=!!!)。
把CheKey改成这样:
void CheKey(PDWORD pPower, DWORD key, DWORD flag, DWORD, DWORD)
{
if (key == ' ')
{
if (flag == 0)
{
KeyIsPressed = 0;
}
else
{
KeyIsPressed = 1;
if (*(pPower+0x1190) == 0x3)
{
00407157(pPower);
}
}
}
else if (key == '1')
{
POWER = 1;
if (flag == 0)
{
KeyIsPressed = 0;
}
else
{
KeyIsPressed = 1;
if (*(pPower+0x1190) == 0x3)
{
Pause(pPower);
}
}
}
else if (key == '2')
....
else if (key == 0x1B)
{//0x1B是哪个键...
if (flag != 0)
{
if (*(pPower+0x1190) == 0x3)
{
Pause(pPower);
}
}
}
}
回游戏试一试,令人失望的是没象我们预想的那样按键之后就跳,依然是以前那样的停止蓄力才跳。看来我们还得找一个信息:他什么条件下才跳(我不是说跳转...)。找这个自然要从Jump函数入手,有两处调用Jump:
00406837 |. E8 6FF7FFFF call <Jump>
与
00406AE7 |. E8 BFF4FFFF call <Jump>
其中00406837处始终被断,不用考虑了。现在研究哪个跳转会决定00406AE7处的语句是否会被执行。这次没省力气的方法了(至少我不知道),我只好在函数Fun的入口开始跟踪,然后我就只发现了1处:
00406823 |. 8B45 08 mov eax, dword ptr [ebp+8]
00406826 |. 83B8 B0110000>cmp dword ptr [eax+11B0], 0
0040682D |. 0F85 E4010000 jnz 00406A17
它离Fun入口不远,找起来没想象的困难。
这里又多了个pPower+0x11B0,当他是WanaJump标志了:P
再加上修改WanaJump的语句,运行测试一下功能键。结果依然令人失望:能听见跳的声音,但是小人完全没动作....
我们在看看正常跳的时候程序是什么情况:
00406AC9 |> \33C0 xor eax, eax
00406ACB |. 8B4D 08 mov ecx, dword ptr [ebp+8]
00406ACE |. 8B89 A0110000 mov ecx, dword ptr [ecx+11A0]<看看ECX的值
00406AD4 |. 03C9 add ecx, ecx
00406AD6 |. 2BC1 sub eax, ecx
00406AD8 |. F7D8 neg eax
00406ADA |. 8B4D 08 mov ecx, dword ptr [ebp+8]
00406ADD |. 2981 98110000 sub dword ptr [ecx+1198], eax
00406AE3 |> 8B45 08 mov eax, dword ptr [ebp+8]
00406AE6 |. 50 push eax
00406AE7 |. E8 BFF4FFFF call <Jump>
在00406AC9处下断点(在这里下断点的原因是我怀疑它之后的语句对Jump的参数有影响),回游戏按空格正常蓄力,注意到pPower+11A0处的值与力度相同。然后让游戏运行,按一下我们的功能键:pPower+11A0的值是0?
再加上修改pPower+11A0处数据的语句。回到游戏,测试一下,成了!
这里是各数据的偏移量(相对于pPower也就是[ebp+8]):
11A8 POWER
11B0 WanaJump
1308 IsKerPressed
11A0 NoName
这里是OllyDBG中的代码:
0040433F . /0F85 74690000 jnz 0040ACB9
=======
0040ACB9 > \33C0 xor eax, eax
0040ACBB . 837D 0C 31 cmp dword ptr [ebp+C], 31
0040ACBF . 75 05 jnz short 0040ACC6
0040ACC1 . B8 01000000 mov eax, 1
0040ACC6 > 837D 0C 32 cmp dword ptr [ebp+C], 32
0040ACCA . 75 05 jnz short 0040ACD1
0040ACCC . B8 02000000 mov eax, 2
0040ACD1 > 837D 0C 33 cmp dword ptr [ebp+C], 33
0040ACD5 . 75 05 jnz short 0040ACDC
0040ACD7 . B8 03000000 mov eax, 3
0040ACDC > 837D 0C 34 cmp dword ptr [ebp+C], 34
0040ACE0 . 75 05 jnz short 0040ACE7
0040ACE2 . B8 04000000 mov eax, 4
0040ACE7 > 837D 0C 35 cmp dword ptr [ebp+C], 35
0040ACEB . 75 05 jnz short 0040ACF2
0040ACED . B8 05000000 mov eax, 5
0040ACF2 > 837D 0C 36 cmp dword ptr [ebp+C], 36
0040ACF6 . 75 05 jnz short 0040ACFD
0040ACF8 . B8 06000000 mov eax, 6
0040ACFD > 837D 0C 37 cmp dword ptr [ebp+C], 37
0040AD01 . 75 05 jnz short 0040AD08
0040AD03 . B8 07000000 mov eax, 7
0040AD08 > 837D 0C 38 cmp dword ptr [ebp+C], 38
0040AD0C . 75 05 jnz short 0040AD13
0040AD0E . B8 08000000 mov eax, 8
0040AD13 > 837D 0C 39 cmp dword ptr [ebp+C], 39
0040AD17 . 75 05 jnz short 0040AD1E
0040AD19 . B8 09000000 mov eax, 9
0040AD1E > 837D 0C 30 cmp dword ptr [ebp+C], 30
0040AD22 . 75 05 jnz short 0040AD29
0040AD24 . B8 0A000000 mov eax, 0A
0040AD29 > 817D 0C BD000>cmp dword ptr [ebp+C], 0BD
0040AD30 . 75 05 jnz short 0040AD37
0040AD32 . B8 0B000000 mov eax, 0B
0040AD37 > 817D 0C BB000>cmp dword ptr [ebp+C], 0BB
0040AD3E . 75 05 jnz short 0040AD45
0040AD40 . B8 0C000000 mov eax, 0C
0040AD45 > 85C0 test eax, eax
0040AD47 .^ 0F84 4196FFFF je 0040438E
0040AD4D . 8B5D 08 mov ebx, dword ptr [ebp+8]
0040AD50 . 837D 10 01 cmp dword ptr [ebp+10], 1
0040AD54 . 75 00 jnz short 0040AD56
0040AD56 > 8983 A8110000 mov dword ptr [ebx+11A8], eax
0040AD5C . C783 08130000>mov dword ptr [ebx+1308], 0
0040AD66 . C783 B0110000>mov dword ptr [ebx+11B0], 1
0040AD70 . 8983 A0110000 mov dword ptr [ebx+11A0], eax
0040AD76 .^ E9 4F96FFFF jmp 004043CA
玩了一下发现:用功能键的时候不是跳,是飞!不踩地板都往上升....
大家还是把新加的功能键当作救命用的吧,不然就跟我一样不想玩它了.....
--------------------------------------------------------------------------------
【经验总结】
看来动手之前的观察是很重要的,要通过观察程序的表现推测程序的运行原理,进而判断我们需要收集的信息,这样动手的
时候就有的方向,可以少走不少弯路。而我这次就走了几次弯路。
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2007年04月26日 13:07:33
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课