original url : http://blog.sina.com.cn/s/blog_9a8bde1401013tfs.html
预备知识:NProtect实际上也是一个单独的程序,由航海自动载入。NP的主程序是航海根目录下的GameGuard.des文件,这个文件实际上是exe程序,不信你可以把扩展名des改成exe试试。因此——
第一步:将根目录下的GameGuard.des文件改名或者删掉。让航海程序无法载入NP。
弄掉根目录下的GameGuard.des文件以后,航海无法载入NP就报错,会弹出一个对话框”GameGuard.des filedoes not exist or is modified. This can be resolved by installingGameGuard setupfile..(00000099)”。处理方法就是修改航海程序流程,让航海即使知道有错误仍然继续运行。
第二步:用OD载入航海,程序停在入口点。下bpMessageBoxA断点。这样当航海弹出所有对话框的时候,都会被中断。下断点以后,按F9继续运行航海。接着一下子就到错误代码处了。
右下角堆栈一目了然。
按F8返回retn,OD停在加灰行(下同)
00ABBB7F |> \FFB5 A4000000 pushdword ptr ss:[ebp+0xA4] ; /Style
00ABBB85 |. 53 push ebx ; |Title
00ABBB86 |. FFB5 A0000000push dword ptr ss:[ebp+0xA0] ; |Text
00ABBB8C |. FF75 80 push[local.32] ; |hOwner
00ABBB8F |. FF15 98C5AF00call dword ptrds:[<&USER32.MessageBoxA>] ; \MessageBoxA
00ABBB95 |. 85FF test edi,edi
这时候,程序已经检测过GameGuard.des文件被弄掉了。所以我们要返回上一层CALL看看是在什么地方检测的。继续F8到retn处,OD停在
00494F34 |> \8D4C24 08 lea ecx,dword ptrss:[esp+0x8]
00494F38 |. 50 pusheax
00494F39 |. E8 423BF9FF call duokai50.00428A80
00494F3E |> 57 push edi
00494F3F |. 56 pushesi
00494F40 |. 8D5424 10 lea edx,dword ptrss:[esp+0x10]
00494F44 |. 68 D825B000 push duokai50.00B025D8 ; ASCII "(x)"
00494F49 |. 52 pushedx
00494F4A |. E8 4142F9FF call duokai50.00429190
00494F4F |. 8B7424 18 mov esi,dword ptrss:[esp+0x18]
00494F53 |. 8B46 F4 moveax,dword ptr ds:[esi-0xC]
00494F56 |. 83C4 0C addesp,0xC
00494F59 |. 50 pusheax
00494F5A |. 56 pushesi
00494F5B |. 8D4C24 10 lea ecx,dword ptrss:[esp+0x10]
00494F5F |. E8 5C3EF9FF call duokai50.00428DC0
00494F64 |. 8B7C24 08 mov edi,dword ptrss:[esp+0x8]
00494F68 |. 6A 00 push 0x0
00494F6A |. 6A 10 push 0x10
00494F6C |. 57 pushedi
00494F6D |. E8 626C6200 call duokai50.00ABBBD4
00494F72 |. 8D46 F0 lea eax,dword ptrds:[esi-0x10]
往上翻翻看。很明显这是一个case选择条件代码段。看起来还挺复杂。别急着下断点试。让我们分析一下程序思想:我们刚返回的地方是显示错误对话框,然后随便点几个上面的跳转,发现最后都会到显示错误这里来。而上面这么多case条件,应该都是错误A、错误B、错误C……。那么程序流程应该是:判断是否有错误,如果有错误,判断是哪个错误,最后显示错误对话框。s所以我们要找的不是某一个case事件,而是找判断是否有错误代码。因此我们继续往上翻,找
00494DDA |. 83FE 7C cmpesi,0x7C ; Switch (cases 6E..17C)
这句。这句是选择条件起始句。找到这句后继续往上看,找找最靠近的跳转语句。不远处就有一个
00494DA6 /0F84 1A020000 jeduokai50.00494FC6
这句跳的还挺远,甚至retn都不一样。有戏!让我们测试一下,直接将je00494FC6改成jmp 00494FC6。然后CTRL+F2重新载入航海,按CTRL+P,在第一行上点右键选择应用补丁,这样我们的修改就生效了。顺便按ALT+B把前几次的断点都删了。
继续按F9运行。航海成功运行!说明我们改对了。让我们把成果保存到文件:在随便一行代码上点右键,选择复制到可执行文件→全部修改,跳出来的对话框选全部复制,在跳出的窗口点右键选择保存文件,然后看你想保存到哪里都行。
不过这样有个小BUG,退出航海的时候会弹出来一个网页。处理方法:
第三步:将航海目录里的GameGuard子目录里的ggerror.des改名或删除,就不弹出网页了。
至此过NP搞定。
多开:
预备知识:台服默认能开3个航海。所以我们先正常打开3个航海窗口,无需登录。再打开第4个窗口,没任何反应。看来是程序检测了已知窗口,如果超过3个就自动退出了。
第一步:还是OD载入航海,停在入口点。下bpExitProcess断点。将会在程序退出时中断。F9运行。
程序中断下来以后,不能再F8单步了。因为没F8几次,程序直接OVER了。我们直接看堆栈。在堆栈第三行右键,选反汇编窗口跟随。
这么做的原因是,程序执行的流程肯定是:判断是否要退出→如果要退出,执行退出代码。所以如果我们回到堆栈第一行0018FE28那个,肯定是在执行退出代码段。我们应该到判断是否退出代码,所以要回到堆栈第三行那。
现在我们到了这里
00A8CEFF |. 8935 34C3D400mov dword ptr ds:[0xD4C334],esi
00A8CF05 |. FF75 08 push[arg.1]
00A8CF08 |. E8 92FEFFFF call duokai50.00A8CD9F
00A8CF0D |. 33FF xor edi,edi
00A8CF08这个CALL就是执行退出代码的。我们已经知道只能开3个窗口,所以我们往上找找有没有个3比较的(汇编代码类似cmp XXX,0x3)。结果很遗憾没找到……不过这段代码也不长,我们干脆从头开始分析,看看两个变量里有没有和3比较的。在
00A8CE63 /$ 6A 08 push 0x8
处下断。CTRL+F2重新载入,F9运行。程序停住了,F8分析,重点看cmp比较语句和回跳循环判断。找了一圈没收获……换个思路:程序是靠什么得知已经开了几个窗口呢?不会是通过进程名(你可以自己实验)。不会是通过窗口标题“大航海时代online"(还是自己实验,用很多软件可以随意更改程序标题)。那只能是通过窗口的类名了(这个类,大概就是大窗口里可以套小窗口的意思。类名是小窗口的标题名字)。所以
第二步:下BP GetClassNameA断点。CTRL+F2重新载入,F9运行。
中断下来了,F8继续走,返回到游戏代码
0049376E |. FF15 DCC6AF00call dword ptrds:[<&USER32.GetClassName>;\GetClassNameA
00493774 |. A1 401DCD00 mov eax,dword ptr ds:[0xCD1D40]
然后F8继续往下走。
0049377E |. FF90 DC010000call dword ptr ds:[eax+0x1DC]
00493784 |. 50 push eax ; /String2 = "Greate Voyages Online GameMainFrame"
00493785 |. 8D4C24 14 lea ecx,dword ptrss:[esp+0x14] ; |
00493789 |. 51 push ecx ; |String1 = "TaskListThumbnailWnd"
0049378A |. FF15 A0C3AF00call dword ptrds:[<&KERNEL32.lstrcmpA>]; \lstrcmpA
00493790 |. 85C0 test eax,eax
00493792 |. 75 03 jnz Xduokai50.00493797
00493794 |. 43 inc ebx
00493795 |. 891E mov dword ptr ds:[esi],ebx
00493797 |> 5F pop edi
这一眼就看出来了,航海的类名是"Greate Voyages Online GameMainFrame",string1是其他窗口的类名,然后用lstrcmpA函数比较,lstrcmpA函数返回的结果保存在EAX,然后testeax,eax(判断EAX值,结果会影响下面的jnz跳转),如果EAX是0,就inc ebx(ebx=ebx+1)。所以ebx保存的就是已经打开的航海窗口数。
所以第一种修改方法就是将jnz 00493797改成jmp 00493797强制跳转。让ebx永远是0。就可以实现无限开。
我们还不满足,想找出来判断3个窗口的地方。所以我们继续F8到retn处
00493794 |. 43 incebx
00493795 |. 891E mov dword ptrds:[esi],ebx
00493797 |> 5F pop edi
00493798 |. 5E popesi
00493799 |. B8 01000000 mov eax,0x1
0049379E |. 5B popebx
0049379F |. 8BE5 mov esp,ebp
004937A1 |. 5D popebp
004937A2 \. C2 0800 retn0x8
这时我们再F8,发现跳回到系统DLL里去了(注意看OD标题),然后又跳回来,再跳回系统DLL……好累,让我们走个捷径,F8走到004937A2 retn这,看右下角堆栈,往下找第一个返回到上一层CALL的地址(duokai50是我自己的航海程序名,相应的你得知道你自己航海程序名是什么)。这句
0018FD54 0049539A 返回到duokai50.0049539A 来自 USER32.EnumWindows
在上面右击,反汇编窗口跟随,来到
00495394 . FF15 C0C6AF00 call dword ptrds:[<&USER32.EnumWindows>>;\EnumWindows
0049539A . 85C0 test eax,eax
我们看到EnumWindows这个函数是遍历所有窗口的。所以我们没来错地方。在0049539A . 85C0 testeax,eax行上下断,F9运行断到这里,F8继续。下一行就是个jnz跳转,跳到了
004953F4 > \837C24 14 03 cmp dword ptr ss:[esp+0x14],0x3
注意看[esp+0x14]是4,这不就是4和3比较吗?这应该就是判断窗口数量的语句了。为了验证我们可以按空格,把03改成05理论上能开5个航海窗口,再按上面应用补丁的方法验证一下。最后发现我们找的没错,这句就是判断窗口数量的地方。
所以我们又一种修改方法就是把cmp dword ptr ss:[esp+0x14],0x3改成cmp dwordptr ss:[esp+0x14],0x64。就成了100开(64是16进制,换成10进制就是100)。
改完以后,还是按照上面应用补丁的方法保存结果就OK了。