不是我残忍,老是拿这一个游戏开刀,而是稍微大一点的游戏我就啃不动了...
这次用Loader来...
功能:
1、注册
2、阻止错误窗口的显示
3、快捷键(用1-9键分别对应12个弹跳力度,不用按住空格键蓄力了)
4、无敌(在屏幕最底部增加一个浮动的木板,这样玩家永远不会死~)
因为我这是继续以前的工作,中间用到的部分地址来的可能有点莫名其妙,这个时候你需要看我之前的分析:
<是男人就上120层 破解>
<是男人就上120层 Patch>
=====
1、注册
最直接的解决方法莫过于把检查注册码的函数004031AD返回值固定为1。
做法有两种:
1.把004031B6处的命令改为jmp 004032FC
2.把0040310B处的命令改为mov eax, 1
采用方法1,既将004031B6处的8B 45 08 33 C9改为E9 41 01 00 00
==============程序代码割线==============
const LPVOID dwRegPatch = (LPVOID)0x004031B6;
BYTE RegPatcho[] = {0x8B, 0x45, 0x08, 0x33, 0xC9};//注册-004031B6原始数据
BYTE RegPatchp[] = {0xE9, 0x41, 0x01, 0x00, 0x00};//注册-004031B6补丁数据
ReadProcessMemory(pinfo.hProcess, dwRegPatch, Buffer, sizeof (RegPatcho), &bytesRead);
if (memcmp(Buffer, RegPatcho, bytesRead) == false)
WriteProcessMemory(pinfo.hProcess, (LPVOID)dwRegPatch, RegPatchp, sizeof (RegPatchp), &bytesRead);
==============程序代码割线==============
2、阻止错误窗口的显示
把0040495F处的jnz改成jmp就可以,既将0F 85 4E 00 00 00改为EB 52 90 90 90 90
==============程序代码割线==============
const LPVOID dwNagPatch = (LPVOID)0x0040495F;
BYTE NagPatcho[] = {0x0F, 0x85, 0x4E, 0x00, 0x00, 0x00};//错误窗口-0040495F原始数据
BYTE NagPatchp[] = {0xEB, 0x52, 0x90, 0x90, 0x90, 0x90};//错误窗口-0040495F补丁数据
//Nag补丁
ReadProcessMemory(pinfo.hProcess, dwNagPatch, Buffer, sizeof (NagPatcho), &bytesRead);
if (memcmp(Buffer, NagPatcho, bytesRead) == false)
WriteProcessMemory(pinfo.hProcess, dwNagPatch, NagPatchp, sizeof (NagPatchp), &bytesRead);
==============程序代码割线==============
3、快捷键
用<是男人就上120层 Patch>里的结果,我又把代码改进了一下:
==============汇编代码割线==============
0040ACB9 > \837D 0C 31 cmp dword ptr [ebp+C], 31
0040ACBD .^ 0F82 C796FFFF jb 0040438A
0040ACC3 . 837D 0C 39 cmp dword ptr [ebp+C], 39
0040ACC7 .^ 0F87 BD96FFFF ja 0040438A
0040ACCD . 8B45 0C mov eax, dword ptr [ebp+C]
0040ACD0 . 83E8 30 sub eax, 30
0040ACD3 . 83C0 03 add eax, 3
0040ACD6 . 8B5D 08 mov ebx, dword ptr [ebp+8]
0040ACD9 . 837D 10 01 cmp dword ptr [ebp+10], 1
0040ACDD . 75 00 jnz short 0040ACDF
0040ACDF > 8983 A8110000 mov dword ptr [ebx+11A8], eax
0040ACE5 . C783 08130000>mov dword ptr [ebx+1308], 0
0040ACEF . C783 B0110000>mov dword ptr [ebx+11B0], 1
0040ACF9 . 8983 A0110000 mov dword ptr [ebx+11A0], eax
0040ACFF .^ E9 C696FFFF jmp 004043CA
==============汇编代码割线==============
将0040433F处的jnz 0040438A改为jnz 0040ACB9,既将0F 85 45 00 00 00改为0F 85 74 69 00 00
0040ACB9起的代码:
83 7D 0C 31 0F 82 C7 96 FF FF 83 7D 0C 39 0F 87 BD 96 FF FF 8B 45 0C 83 E8 30 83 C0 03 8B 5D 08
83 7D 10 01 75 00 89 83 A8 11 00 00 C7 83 08 13 00 00 00 00 00 00 C7 83 B0 11 00 00 01 00 00 00
89 83 A0 11 00 00 E9 C6 96 FF FF
==============程序代码割线==============
const LPVOID dwCheckKey = (LPVOID)0x0040433F;
BYTE CheckKeyo[] = {0x0F, 0x85, 0x45, 0x00, 0x00, 0x00};//CheckKey-0040433F原始数据
BYTE CheckKeyp[] = {0x0F, 0x85, 0x74, 0x69, 0x00, 0x00};//CheckKey-0040433F补丁数据
const LPVOID dwHotKey = (LPVOID)0x0040ACB9;
BYTE Hotkeys[] = {0x83, 0x7D, 0x0C, 0x31, 0x0F, 0x82, 0xC7, 0x96, 0xFF, 0xFF, 0x83, 0x7D, 0x0C, 0x39, 0x0F,
0x87, 0xBD, 0x96, 0xFF, 0xFF, 0x8B, 0x45, 0x0C, 0x83, 0xE8, 0x30, 0x83, 0xC0, 0x03, 0x8B,
0x5D, 0x08, 0x83, 0x7D, 0x10, 0x01, 0x75, 0x00, 0x89, 0x83, 0xA8, 0x11, 0x00, 0x00, 0xC7,
0x83, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC7, 0x83, 0xB0, 0x11, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x89, 0x83, 0xA0, 0x11, 0x00, 0x00, 0xE9, 0xC6, 0x96, 0xFF, 0xFF};//快捷键功能实现代码-0040ACB9
//Hotkey补丁
WriteProcessMemory(pinfo.hProcess, dwHotKey, Hotkeys, sizeof (Hotkeys), &bytesRead);
SuspendThread(pinfo.hThread);
if (sto == false)
{//打开开关
ReadProcessMemory(pinfo.hProcess, dwCheckKey, Buffer, sizeof (CheckKeyo), &bytesRead);
if (memcmp(Buffer, CheckKeyo, bytesRead) == false)
WriteProcessMemory(pinfo.hProcess, dwCheckKey, CheckKeyp, sizeof (CheckKeyp), &bytesRead);
sto = true;
}
else
{//关闭开关
ReadProcessMemory(pinfo.hProcess, dwCheckKey, Buffer, sizeof (CheckKeyp), &bytesRead);
if (memcmp(Buffer, CheckKeyp, bytesRead) == false)
WriteProcessMemory(pinfo.hProcess, dwCheckKey, CheckKeyo, sizeof (CheckKeyo), &bytesRead);
sto = false;
}
ResumeThread(pinfo.hThread);
==============程序代码割线==============
4、无敌
这个功能就没有现成的东西用了,要自己去找了。我们要找的是生成木板的函数。有了生成木板的函数,我们就能在屏幕底部给加一道保险了。
现在我们有什么线索帮助我们找到这个函数呢?小人跳起来后的落地判断应该会涉及到木板的相关数据,然后我们再根据这个数据顺藤摸瓜找到生成木板的函数。首先想到的是Jump函数。
Jump函数的分析:这个函数里调用了SetRect、IntersectRect两个API,一个是创建矩形一个是求两个矩形的相交部分。猜测程序是用小人的矩形和其他各个木板的矩形求相交的部分,若没有任何一个木板和它相交则说明小人处于悬空状态。经过分析,发现最初创建的那个矩形就是小人。而和它求相交的矩形一共有0xB个,这些矩形是在函数004010F3里创建的。几乎可以肯定这些矩形就是木板,现在要把存放这些木板坐标的内存找出来。
注意到这一段:
00406035 > /6A 01 push 1
00406037 . |6A 60 push 60
00406039 . |8B45 D8 mov eax, dword ptr [ebp-28]
0040603C . |8D0440 lea eax, dword ptr [eax+eax*2]
0040603F . |8B4D 08 mov ecx, dword ptr [ebp+8]
00406042 . |8B84C1 BC1100>mov eax, dword ptr [ecx+eax*8+11BC]
00406049 . |50 push eax
0040604A . |8B45 D8 mov eax, dword ptr [ebp-28]
0040604D . |8D0440 lea eax, dword ptr [eax+eax*2]
00406050 . |8B4D 08 mov ecx, dword ptr [ebp+8]
00406053 . |BB 0A000000 mov ebx, 0A
00406058 . |8B84C1 C01100>mov eax, dword ptr [ecx+eax*8+11C0]
0040605F . |99 cdq
00406060 . |F7FB idiv ebx
00406062 . |8B4D D8 mov ecx, dword ptr [ebp-28]
00406065 . |8D0C49 lea ecx, dword ptr [ecx+ecx*2]
00406068 . |8B55 08 mov edx, dword ptr [ebp+8]
0040606B . |8B8CCA B81100>mov ecx, dword ptr [edx+ecx*8+11B8]
00406072 . |03C8 add ecx, eax
00406074 . |51 push ecx
00406075 . |8D45 E0 lea eax, dword ptr [ebp-20]
00406078 . |50 push eax
00406079 . |E8 75B0FFFF call 004010F3
函数004010F3中调用了SetRect,实际上这个函数是起了个适配器的作用:这个函数的参数是高、宽和左下角点坐标,而SetRect的参数是左下角坐标和右上角坐标。
0040606B . |8B8CCA B81100>mov ecx, dword ptr [edx+ecx*8+11B8]
中的[edx+ecx*8+11B8]应该就是几个木板中的一个的左坐标。在[edx+ecx*8+11B8]所指向的内存下写断点,重新开始游戏会断在00404DBD函数中。中断11次后游戏正常运行,向上跳几层后游戏中断在MoveScreen函数中。现在有两处线索了。分析两处,发现00404DBD这个函数只是初始化,MoveScreen里才是游戏进行时生成木板的地方。但是我在分析00404DBD的时候发现了有趣的东西,木板的信息是
struct Board
{
DWORD type;
POINT pt;
DWORD unkown1;
DWORD unkown2;
DWORD unkown3;
};
结构体成员type是木板的类型,而类型为5的时候,生成的是地板...快要摸到瓜了,现在需要做的工作是分析MovScreen函数,找找下手的地方……
MoveScreen大致流程是这样的:对于所有的木板,加上屏幕要上移的距离,当有哪块木板的y坐标加上要上移的距离后超出0x160,既要移动到屏幕之外的时候,就生成一个新的木板(新木板的数据覆盖掉那块要移动到屏幕之外的木板的数据)。我们想弄一个跟着屏幕上升的地板,在这里做手脚就可以了。下面这段代码是判断一块木板是否会移动到屏幕之外:
00406345 |> \8B45 FC |mov eax, dword ptr [ebp-4]
00406348 |. 8D0440 |lea eax, dword ptr [eax+eax*2]
0040634B |. 8B4D 08 |mov ecx, dword ptr [ebp+8]
0040634E |. 8B84C1 BC1100>|mov eax, dword ptr [ecx+eax*8+11BC]
00406355 |. 0345 0C |add eax, dword ptr [ebp+C]
00406358 |. 3D 60010000 |cmp eax, 160
0040635D |. 0F8C D0000000 |jl 00406433
而00406433处的代码是更新那些不会被移动到屏幕外面的木板的坐标的:
00406433 |> \8B45 0C |mov eax, dword ptr [ebp+C]
00406436 |. 8B4D FC |mov ecx, dword ptr [ebp-4]
00406439 |. 8D0C49 |lea ecx, dword ptr [ecx+ecx*2]
0040643C |. 8B55 08 |mov edx, dword ptr [ebp+8]
0040643F |. 0184CA BC1100>|add dword ptr [edx+ecx*8+11BC], eax
我们想弄一个跟着屏幕上升的地板,只需要在0040635D处跳转之前判断当前处理的是不是地板,如果是地板的话就跳过00406433处代码,阻止程序修改地板的位置,这样地板的位置就会始终保持在屏幕最底部。
先用脚本试试这个方法是否行的通:
==============脚本分割线==============
var bak
var addr
eob break
break:
//获得当前处理的木板的type属性
mov addr, eax
shl addr, 3
add addr, ecx
add addr, 11B4
mov bak, [addr]
//若当前处理的木板是地板,则直接跳到406446处
cmp bak, 5
jne continue
bp634E:
mov eip, 406446
continue:
run
==============脚本分割线==============
但是这里还有一个问题:我要无敌这个功能是可以打开关闭的,而上面这个实现方法只能从游戏一开始就使用才能起作用。因为游戏进行到一半的时候才打开开关的话,很可能就没有地板了,更不要说保持地板的坐标了。
很明显,这个问题的意思是:我的算法是在有地板这个前提之下的,我怎么通过保证这个前提来保证我的算法工作正常呢?
答案就是刚打开开关的时候检查是否有地板,没有的话就找一个要被移出屏幕底端的木板并把它修改成地板(随便找一个的话会产生一个木板凭空消失的现象,显得不自然)。
实现:
==============汇编代码割线==============
0040634E /FF25 F0EF4000 jmp dword ptr [40EFF0] ; 是男人就.0040AD10
//维持地板上升的代码段
0040AD10 . 50 push eax
0040AD11 . 8B84C1 B4110000 mov eax, dword ptr [ecx+eax*8+11B4]
0040AD18 . 83F8 05 cmp eax, 5
0040AD1B . 58 pop eax
0040AD1C .^ 0F84 24B7FFFF je 00406446
0040AD22 . 8B84C1 BC110000 mov eax, dword ptr [ecx+eax*8+11BC]
0040AD29 .^ E9 27B6FFFF jmp 00406355
//刚打开开关的时候的操作:寻找是否有地板,
//有的话则把维持地板上升的代码段补丁到程序中;
//如果没有地板则把改造一个木板的代码段补丁到程序中。
0040AD30 . 53 push ebx
0040AD31 . 52 push edx
0040AD32 . BA 00000000 mov edx, 0
0040AD37 > 8D0452 lea eax, dword ptr [edx+edx*2]
0040AD3A . 8B9CC1 B4110000 mov ebx, dword ptr [ecx+eax*8+11B4]
0040AD41 . 83FB 05 cmp ebx, 5
0040AD44 74 48 je short 0040AD8E
0040AD46 . 42 inc edx
0040AD47 . 83FA 0E cmp edx, 0E
0040AD4A .^ 76 EB jbe short 0040AD37
0040AD4C . C705 F0EF4000 60AD4000 mov dword ptr [40EFF0], 0040AD60
0040AD56 . 5A pop edx
0040AD57 . 5B pop ebx
0040AD58 .^ E9 E9B6FFFF jmp 00406446
//发现一个要移出屏幕底部的木块后把它改造成地板,然后把维持地板上升的代码段补丁到程序中
0040AD60 . 8B45 FC mov eax, dword ptr [ebp-4]
0040AD63 . 8D0440 lea eax, dword ptr [eax+eax*2]
0040AD66 . C784C1 B4110000 05000000 mov dword ptr [ecx+eax*8+11B4], 5
0040AD71 C784C1 B8110000 10000000 mov dword ptr [ecx+eax*8+11B8], 10
0040AD7C . C784C1 BC110000 50010000 mov dword ptr [ecx+eax*8+11BC], 150
0040AD87 . EB 07 jmp short 0040AD90
//把维持地板上升的代码段补丁到程序中
0040AD8E 5A pop edx
0040AD8F 5B pop ebx
0040AD90 > C705 F0EF4000 10AD4000 mov dword ptr [40EFF0], 0040AD10
0040AD9A ^ E9 A7B6FFFF jmp 00406446
==============汇编代码割线==============
程序流程就是先让程序执行0040AD30处代码,寻找是否有地板,然后执行相应操作,这是初始化操作。完成初始化后,就可以把0040AD10处代码“连接”到程序里了。
==============程序代码割线==============
const LPVOID dwMoveFloor = (LPVOID)0x0040634E;//补丁的位置
const LPVOID dwJMPAddr = (LPVOID)0x0040EFF0;//存放跳转地址的指针
const DWORD dwFirstJMP = 0x0040AD30;//初始化跳转地址
BYTE MoveFlooro[] = {0x8B, 0x84, 0xC1, 0xBC, 0x11, 0x00, 0x00};//原始数据
BYTE MoveFloorp[] = {0xFF, 0x25, 0xF0, 0xEF, 0x40, 0x00, 0x90};//补丁数据
const LPVOID dwMoveFloors = (LPVOID)0x0040AD10;
BYTE MoveFloors[] = {0x50, 0x8B, 0x84, 0xC1, 0xB4, 0x11, 0x00, 0x00, 0x83, 0xF8, 0x05, 0x58, 0x0F, 0x84, 0x24, 0xB7, 0xFF,
0xFF, 0x8B, 0x84, 0xC1, 0xBC, 0x11, 0x00, 0x00, 0xE9, 0x27, 0xB6, 0xFF, 0xFF, 0x00, 0x00, 0x53, 0x52,
0xBA, 0x00, 0x00, 0x00, 0x00, 0x8D, 0x04, 0x52, 0x8B, 0x9C, 0xC1, 0xB4, 0x11, 0x00, 0x00, 0x83, 0xFB,
0x05, 0x74, 0x48, 0x42, 0x83, 0xFA, 0x0E, 0x76, 0xEB, 0xC7, 0x05, 0xF0, 0xEF, 0x40, 0x00, 0x60, 0xAD,
0x40, 0x00, 0x5A, 0x5B, 0xE9, 0xE9, 0xB6, 0xFF, 0xFF, 0x90, 0x90, 0x90, 0x8B, 0x45, 0xFC, 0x8D, 0x04,
0x40, 0xC7, 0x84, 0xC1, 0xB4, 0x11, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xC7, 0x84, 0xC1, 0xB8, 0x11,
0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xC7, 0x84, 0xC1, 0xBC, 0x11, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00,
0xEB, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0x5B, 0xC7, 0x05, 0xF0, 0xEF, 0x40, 0x00, 0x10, 0xAD,
0x40, 0x00, 0xE9, 0xA7, 0xB6, 0xFF, 0xFF};//MoveFloor功能代码
//MovingFloor补丁
WriteProcessMemory(pinfo.hProcess, dwMoveFloors, MoveFloors, sizeof (MoveFloors), &bytesRead);
SuspendThread(pinfo.hThread);
if (inv == false)
{//打开开关
ReadProcessMemory(pinfo.hProcess, dwMoveFloor, Buffer, sizeof (MoveFlooro), &bytesRead);
if (memcmp(Buffer, MoveFlooro, bytesRead) == false)
{
WriteProcessMemory(pinfo.hProcess, dwMoveFloor, MoveFloorp, sizeof (MoveFloorp), &bytesRead);
WriteProcessMemory(pinfo.hProcess, dwJMPAddr, &dwFirstJMP, sizeof (dwFirstJMP), &bytesRead);
}
inv = true;
}
else
{//关闭开关
ReadProcessMemory(pinfo.hProcess, dwMoveFloor, Buffer, sizeof (MoveFloorp), &bytesRead);
if (memcmp(Buffer, MoveFloorp, bytesRead) == false)
WriteProcessMemory(pinfo.hProcess, dwMoveFloor, MoveFlooro, sizeof (MoveFlooro), &bytesRead);
inv = false;
}
ResumeThread(pinfo.hThread);
=============程序代码割线==============
- 标 题: 是男人就上120层 Loader
- 作 者:Nukou
- 时 间:2007-08-22 14:47
- 附 件:Loader.rar
- 链 接:http://bbs.pediy.com/showthread.php?t=50152