【文章标题】: 修改游戏进度的保存路径
【文章作者】: windale
【下载地址】: 游戏运行所需文件过大,不提供下载
【作者声明】: 本人菜鸟一个,此文章没有技术含量,只是叙述了一下思路。希望对其他菜鸟和新手有所帮助!
--------------------------------------------------------------------------------
【详细过程】
World Of Goo是一款独具创意的益智游戏,并且画面和音乐的制作都相当精致。由于我安装了影子系统,电脑重启之后游戏的进度就丢失了。我使用的是单一影子模式,仅对系统盘执行保护,所以断定游戏的进度肯定是存放在系统盘中。按照常规,相关文件应该会保存在C:\Documents and Settings\All Users\Application Data里面。结果不出所料,C:\Documents and Settings\All Users\Application Data\2DBoy\WorldOfGoo里面有个pers2.dat文件,应该就是游戏进度数据了。如果能将文件保存的路径改在非系统盘下,就不会丢失进度了。不知道会不会很难,先试试再说!
于是先用PEiD查壳,结果没有加壳,省去了脱壳的麻烦,编译器是Microsoft Visual Studio .NET 8.0。
然后用OD载入。先把pers2.dat文件给删掉,程序发现文件不存在就会创建一个新的。输入命令bp CreateFileA下断。可是中断之后,程序始终在系统领空,跳不出来。于是考虑别的办法。因为文件名pers2.dat应该是固定的,所以在程序中应该会出现相应的字符串。使用插件Ultra String Reference -> Find ASCII,之后在结果中查找字符串"pers2.dat",果然找到了。而且在其上方依次有字符串"\2dboy"、"\worldofgoo"和"\",看来此处附近就是执行文件操作的代码。双击其中的一个,来到了这个位置:
004B3880 |. 8D5424 18 lea edx, dword ptr [esp+18] 004B3884 |. 52 push edx 004B3885 |. 6A 00 push 0 004B3887 |. 6A 00 push 0 004B3889 |. 6A 23 push 23 004B388B |. 6A 00 push 0 004B388D |. 894F 04 mov dword ptr [edi+4], ecx 004B3890 |. FF15 14D55800 call dword ptr [<&SHELL32.SHGetFolder>; SHELL32.SHGetFolderPathA 004B3896 |. 8D4424 18 lea eax, dword ptr [esp+18] 004B389A |. 50 push eax 004B389B |. 8BCE mov ecx, esi 004B389D |. FF15 5CD25800 call dword ptr [<&MSVCP80.std::basic_>; MSVCP80.std::basic_string<char,std::char_traits<char>,std::allocator<char> >::append 004B38A3 |. 68 F4295E00 push 005E29F4 ; ASCII "\2DBoy" 004B38A8 |. 8BCE mov ecx, esi 004B38AA |. FF15 5CD25800 call dword ptr [<&MSVCP80.std::basic_>; MSVCP80.std::basic_string<char,std::char_traits<char>,std::allocator<char> >::append 004B38B0 |. 837E 18 10 cmp dword ptr [esi+18], 10 004B38B4 |. 72 05 jb short 004B38BB 004B38B6 |. 8B46 04 mov eax, dword ptr [esi+4] 004B38B9 |. EB 03 jmp short 004B38BE 004B38BB |> 8D46 04 lea eax, dword ptr [esi+4] 004B38BE |> 8B1D 24D15800 mov ebx, dword ptr [<&KERNEL32.Creat>; kernel32.CreateDirectoryA 004B38C4 |. 6A 00 push 0 ; /pSecurity = NULL 004B38C6 |. 50 push eax ; |Path 004B38C7 |. FFD3 call ebx ; \CreateDirectoryA 004B38C9 |. 8B2D 14D15800 mov ebp, dword ptr [<&KERNEL32.GetLa>; ntdll.RtlGetLastWin32Error 004B38CF |. FFD5 call ebp ; [GetLastError 004B38D1 |. 68 FC295E00 push 005E29FC ; ASCII "\WorldOfGoo" 004B38D6 |. 8BCE mov ecx, esi 004B38D8 |. FF15 5CD25800 call dword ptr [<&MSVCP80.std::basic_>; MSVCP80.std::basic_string<char,std::char_traits<char>,std::allocator<char> >::append 004B38DE |. 837E 18 10 cmp dword ptr [esi+18], 10 004B38E2 |. 72 05 jb short 004B38E9 004B38E4 |. 8B46 04 mov eax, dword ptr [esi+4] 004B38E7 |. EB 03 jmp short 004B38EC 004B38E9 |> 8D46 04 lea eax, dword ptr [esi+4] 004B38EC |> 6A 00 push 0 004B38EE |. 50 push eax 004B38EF |. FFD3 call ebx 004B38F1 |. FFD5 call ebp 004B38F3 |. 68 082A5E00 push 005E2A08 004B38F8 |. 8BCE mov ecx, esi 004B38FA |. FF15 5CD25800 call dword ptr [<&MSVCP80.std::basic_>; MSVCP80.std::basic_string<char,std::char_traits<char>,std::allocator<char> >::append 004B3900 |. 68 0C2A5E00 push 005E2A0C ; pers2.dat 004B3905 |. 8BCE mov ecx, esi 004B3907 |. FF15 5CD25800 call dword ptr [<&MSVCP80.std::basic_>; MSVCP80.std::basic_string<char,std::char_traits<char>,std::allocator<char> >::append 004B390D |. 57 push edi ; /Arg1 004B390E |. E8 7D020000 call 004B3B90 ; \WorldOfG.004B3B90
这个函数有点陌生,查了一下MSDN,得到其原型如下:
HRESULT SHGetFolderPath( HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath );
然后又在MSDN中找到了对CSIDL的描述,列举了各种文件夹所对应的常量,其中有个CSIDL_PERSONAL,代表“我的文档”文件夹。正好我把“我的文档”移到了D盘,不受影子系统的保护,那么把nFolder参数改成CSIDL_PERSONAL应该就行了。
查看原程序的代码,此参数的值是23,那么应该改成多少呢?正好电脑上安装了VC++ 6.0,在其安装目录搜索了一下,结果在SHLOBJ.H文件中找到了相关定义:
#define CSIDL_PERSONAL 0x0005
重新运行游戏,果然在“我的文档”中创建了进度数据文件。重启电脑,运行游戏,进度没有丢失。大功告成!