首页
论坛
专栏
课程

[原创]植物大战僵尸全解密---存档篇

2010-4-11 21:44 41232

[原创]植物大战僵尸全解密---存档篇

2010-4-11 21:44
41232
源码已发:
http://bbs.pediy.com/showthread.php?t=115080&highlight=
2010-04-13
更新了一点,尽量把demo做成program
2010-04-12
原来的程序在删除用户时,没有删除对应用户的游戏进度。现在修正
另外告诉大家一个关于游戏的小密秘。在帖子的最后。

植物大战僵尸全解密---存档篇
内存修改器游侠网上已经有了,再做也没意思了,我来做一个存档编辑工具吧
这是个很赞的游戏,最近沉迷上了.
由于本人水平太菜.老是徘徊在第4关.于是便拿出家伙把它修理了一番.
和游戏相关的,都分析出来了,有些不太常用到的,就不分析了。
没想到还挖出了不少好东西,不敢独享.
我分析的是它的存档文件数据,能分析的都分析出来了,整理成了结构体.方便使用.
存档文件对应的结构体内容如下:
users.dat
/************************************************************************/
/* userdata\users.dat 用户信息文件数据结构                              */
/************************************************************************/
//文件头
typedef struct _FILEHEADER
{
        DWORD        dwValidFlag; /*值必须为0xE,文件有效性标识*/
        DWORD        dwNum;                /*保存用户的数量,十六进制值*/
}FILE_HEADER, *PFILE_HEADER;

typedef struct _USERINFO
{
        WORD        wNameLen;        /*用户名长度*/
        LPSTR        pszName;        /*用户名,实际存放在文件中的是用户名的ASCII字符,不包含末尾的NULL字符*/
        DWORD        dwReserveed;        /*保留,经测试,没有发现其它用途*/
        DWORD        dwIndex;        /*用户ID, 对应生成相应的UserN.dat文件,N的值便是dwIndex/
}USER_INFO, *PUSER_INFO;
userN.dat(N代表用户ID)
/************************************************************************
* userdata\userN.dat 用户存档数据结构, N的值和dwIndex相对应,即每个用户自己的存档文件.
************************************************************************/

typedef struct _USERDATA
{
        DWORD dwValidFlag;                 /*offset:0x0 值必须为0xC,文件有效性标识*/
        DWORD dwGuan;                         /*offset:0x4 当前关卡 */
        DWORD dwMoney;                         /*offset:0x8 金钱数*/
        DWORD dwSilverCup;                /*offset:0xc 获取银向日葵奖杯,得到金杯后,该位上的数字表示完成了n次冒险*/
        /*   更多游戏通关 全胜条件:低0~4位和高24~28位都为1 */
        DWORD dwSC_Game[10];        /*offset:0x10~0x30+0x8 生存模式通关*/
        DWORD dwReserved_9[5];
        DWORD dwXYX_Game[20];        /*offset:0x40+0xc~0x90+0x8 小游戏通关*/
        DWORD dwReserved_10[15];
        DWORD dwJM_1_9_Game[9];        /*offset:0xd0+0x8~0xf0+0x8 解迷游戏1-9关*/
        DWORD dwReserved_11;     /*这个是无限任务,无法通关, 记录的是通过该关的次数, 类似的偏移量还有几个,我全部保留了*/
        DWORD dwJM_10_18_Game[9];/*offset:0x100~0x120 解迷游戏10-18关*/
        DWORD dwReserved_1[31];
        /*  游戏道具  */
        DWORD dwBuy_JQSS;        /*offset:0x1a0 购买 机枪射手,BOOL值*/
        DWORD dwBuy_SSXRK;        /*offset:0x1a0+0x4 购买 双生向日葵,BOOL值*/
        DWORD dwBuy_YYG;        /*offset:0x1a0+0x8 购买 忧郁茹,BOOL值*/
        DWORD dwBuy_XP;                /*offset:0x1a0+0xc 购买 香蒲,BOOL值*/
        DWORD dwBuy_BG;                /*offset:0x1b0 购买冰瓜*/
        DWORD dwBuy_XJC;        /*offset:0x1b0+0x4 购买 吸金磁,BOOL值*/
        DWORD dwBuy_DCW;        /*offset:0x1b0+0x8 购买 地刺王,BOOL值*/
        DWORD dwBuy_YMJNP;        /*offset:0x1b0+0xc 购买 玉米加农炮,BOOL值*/
        DWORD dwBuy_MFZ;        /*offset:0x1c0 购买模仿者,BOOL值*/
        DWORD dwReserved_3[1];
        DWORD dwReserved_12[3]; // 金盏花芽种,共三个,每个变量的值为0x0ea6,但是要在文件尾添加花的生长数据,先保留。
        /*  禅境花园道具  */
        DWORD dwBuy_HJSH;        /*offset:0x1d0+0x4 购买 黄金水壶,BOOL值*/
        DWORD dwBuy_FL;                /*offset:0x1d0+0x8 购买 肥料,低8字节为数量ED,低9~16字节为购买标识0x3*/
        DWORD dwBuy_SCJ;        /*offset:0x1d0+0xc 购买 杀虫剂,低8字节为数量ED,低9~16字节为购买标识0x3*/
        DWORD dwBuy_CPJ;        /*offset:0x1e0 购买 唱片机,BOOL值*/
        DWORD dwBuy_YYST;        /*offset:0x1e0+0x4 购买 园艺手套,BOOL值*/
        DWORD dwBuy_MGY;        /*offset:0x1e0+0x8 购买 蘑菇园,BOOL值*/
        DWORD dwBuy_TC;                /*offset:0x1e0+0xc 购买 推车,BOOL值*/
        /*  游戏辅助道具  */
        DWORD dwBuy_CWGN;        /*offset:0x1f0 购买宠物蜗牛值为, 0x04bbda2fe */
        DWORD dwAddCardNum;        /*offset:0x1f0+0x4 增加的道具槽,有效范围:0x1~0x4*/
        DWORD dwBuy_CTQJC;        /*offset:0x1f0+0x8 购买 池塘清洁车,BOOL值*/
        DWORD dwBuy_WDTC;        /*offset:0x1f0+0xc 购买 屋顶推车,BOOL值*/
        DWORD dwBuy_SB;                /*offset:0x200 购买 扫把, 已购买:0x3,未购买:0*/
        DWORD dwBuy_SZG;        /*offset:0x200+0x4 购买 水族馆,BOOL值*/
        DWORD dwReserved_8[1];
        DWORD dwBuy_ZHS;        /*offset:0x200+0xc 购买 智慧树,BOOL值*/
        DWORD dwBuy_ZHS_FL; /*offset:210 购买 智慧树肥料, 值为0x03e8时状态为可买,买一个加一,最大为10个*/
        DWORD dwBuy_JGBZS;        /*offset:0x210+0x4 购买 坚果包扎术,BOOL值*/
        DWORD dwReserved_5[58];
        /*  开启小游戏  */
        DWORD dwUnlock_XYX;        /*offset:0x300 开启 小游戏,BOOL值*/
        DWORD dwUnlock_JM;        /*offset:0x300+0x4 开启 解迷模式,BOOL值*/
        DWORD dwReserved_6[4];
        DWORD dwUnlock_SC;        /*offset:0x310+0x8 开启 生存模式,BOOL值*/
        DWORD dwReserved_7[6];
}USER_DATA, *PUSER_DATA;

在游戏中修改阳光数的代码如下:
DWORD dwMemAddr = 0x002a9f38;        //指针地址offset,通过偏移的方式读取地址比较保险.
        DWORD dwOffset1  = 0x00000768; //这是个二级指针,即指向指针的指针,这是二级指针指向的地址的偏移量
        DWORD dwOffset2        = 0x00005560; //这是二级指针指向的指针的地址,保存的就是冒险模式中的阳光数,正是我们的目标地址
        __try
        {
                hWnd        =        FindWindow(_T("MainWindow"), NULL);//查找游戏的窗口,如果不存在则说明游戏没有运行
                if (INVALID_HANDLE_VALUE == hWnd)
                {
                        __leave;
                }

                dwMemBase        =        GetWindowLongPtr(hWnd, GWL_HINSTANCE); //先获取游戏进程的基址
                dwMemAddr        +=        dwMemBase; //加上偏移,就是我们要读取的地址了.

                DWORD dwPid = GetDlgItemInt(hDlg, IDC_EDT_PID, NULL, FALSE);

                hProc = OpenProcess(PROCESS_VM_READ|PROCESS_VM_WRITE|PROCESS_VM_OPERATION, FALSE, dwPid); //我们仅仅需要VirtualProtectEx,ReadProcessMemory,WriteProcessMemory这三个操作而已,
                                                                                                                                                                                  //只需要对进程有 PROCESS_VM_READ|PROCESS_VM_WRITE|PROCESS_VM_OPERATION 的权限就够了。
                                                                                                                                                                                                  //MSDN上说PROCESS_VM_OPERATION权限就可以进行WriteProcessMemory操作了,实际过程中却是
                                                                                                                                                                                                  //句柄无效,为了保险。我把三个属性都加上~~~
                if (hProc == NULL)
                {
                        g_bLocked = FALSE;
                        ShowErrorInfo("获取进程出错");
                        __leave;
                }
                                               
                if (!VirtualProtectEx(hProc, (LPVOID)dwMemAddr, sizeof(DWORD), PAGE_READWRITE, &dwOldProtect)) //改变内存地址属性为可读写。
                {
                        ShowErrorInfo("修改内存属性出错");
                        __leave;
                }

               
                DWORD dwReaded = 0;

                if (!ReadProcessMemory(hProc, (LPVOID)dwMemAddr, &dwSunny, sizeof(DWORD), &dwReaded))  //先把0x001224d4的地址内容读出来,由于游戏使用的是二级地址即指向指针的指针,
                {                                                                                      //所以现在读出来的内容其实是一个地址,我们还要继续读,直到读取到数据为止。
                        ShowErrorInfo("读取内存数据出错");
                        __leave;
                }
                               
                dwMemAddr = dwSunny + dwOffset1; //the address offset is 0x768 ^_^~~;

                if (!ReadProcessMemory(hProc, (LPVOID)dwMemAddr, &dwSunny, sizeof(DWORD), &dwReaded))  //先把0x001224d4的地址内容读出来,由于游戏使用的是二级地址即指向指针的指针,
                {                                                                                      //所以现在读出来的内容其实是一个地址,我们还要继续读,直到读取到数据为止。
                        ShowErrorInfo("读取内存数据出错");
                        __leave;
                }
               
                dwMemAddr = dwSunny + dwOffset2; //the address offset is 0x5560 ^_^~~;

                if (!ReadProcessMemory(hProc, (LPVOID)dwMemAddr, &dwSunny, sizeof(DWORD), &dwReaded)) //由于游戏使用的是二级地址,所以只要再读一次就可以得到我们需要的数据了。
                {
                        ShowErrorInfo("读取内存数据出错");
                        __leave;
                }

                if (bIsRead)
                {
                        SetDlgItemInt(hDlg, IDC_EDT_SUNNY, dwSunny, FALSE);   //成功读出来以后把数据显示到界面。
                        //如果能成功获取数据,设置控件状态可用,防止误写入错误的内存地址
                        EnableWindow(GetDlgItem(hDlg, IDC_BTN_CHEAT), TRUE);
                        EnableWindow(GetDlgItem(hDlg, IDC_EDT_SUNNY), TRUE);
                        EnableWindow(GetDlgItem(hDlg, IDC_BTN_LOCK), TRUE);
                }
                else
                {
                        DWORD dwWritten = 0;

                        DWORD dwNewSunny = GetDlgItemInt(hDlg, IDC_EDT_SUNNY, NULL, FALSE);

                        if (dwReaded == dwNewSunny)  //如果不等于我们设定的值才进行修改。
                        {
                                __leave;
                        }

                        if (!WriteProcessMemory(hProc, (LPVOID)dwMemAddr, &dwNewSunny, sizeof(DWORD), &dwWritten))  //如果是要改变数据的值,只进行写操作。
                        {
                                ShowErrorInfo("写入内存数据出错");
                                __leave;
                        }
                }
        }
        __finally
        {
               
                if (dwOldProtect != 0)
                {
                        if (!VirtualProtectEx(hProc, (LPVOID)dwMemAddr, sizeof(DWORD), dwOldProtect, &dwOldProtect)) //做完要做的事以后,再把属性设置回去。这一步也可以省略.
                        {
                                ShowErrorInfo("恢复内存属性出错");

                        }
                }

                CloseHandle(hProc);
        }

}

[2010-04-12]
    国内有汉化版,汉化的版本已经是无限制版本了,而且汉化的质量很棒.   
    这个游戏本身就是绿色免费的小游戏,只不过官方自己加上了一个loader来显示试用信息而已.
    下面教大家一个魔术,把正式版从试用版里变出来,不使用任何修改工具.
    从官方网站下载游戏安装程序.原版是英文的,默认安装路径是C:\Program Files\PopCap Games\Plants vs. Zombies 如果你更改了安装路径,那么就到对应的路径下.运行PlantsVsZombies.exe,会弹出来一个对话框,选择 "Play Trial Game" 来启动游戏,在游戏显示loading画面的时候,再到游戏的安装目录看一下,会发现一个新的文件popcapgame1.exe,文件大小是2.86MB,属性为隐藏.这个不是木马,也不是恶意文件,而是正式版的游戏主程序.该文件是用独占方式创建的,不能直接复制.我们使用强大的磁盘管理工具Disk Genius来提取它.我选择的是复制到桌面.OK了,退出游戏.把桌面上的popcapgame1.exe重命名为PlantsVsZombies.exe,然后覆盖游戏安装目录下的同名文件,再次运行游戏.怎么样?是不是什么提示信息也没有了,而且游戏一切正常,没有任何限制.
    为什么一个商业休闲游戏如此脆弱?或者这正是它的卖点所在,想通过让玩家发现所谓的技巧来提升自己的人气么?亦或是游戏发现自己运行在中国境内,知道俺们china ren不好惹,自动就把程序贡献出来了?


    附件中的PlantsVsZombies_en.rar是我提取出来的主程序,解压到游戏安装目录并选择覆盖原来的文件就行了,版本必须是官方的英文原版.
附件中是我写的游戏助手,用来演示修改阳光数,定制用户存档文件.
界面如下


游戏中效果


[公告]安全服务和外包项目请将项目需求发到看雪企服平台:https://qifu.kanxue.com

上传的附件:
最新回复 (61)
SunTB 2010-4-11 21:51
2
0
这才叫人玩游戏,而不是游戏玩人

支持了
雷刃 2010-4-11 21:52
3
0
挖掘的挺全面,呵呵
yijun8354 12 2010-4-11 21:56
4
0
有空也玩玩
排长 2010-4-11 21:58
5
0
经常玩,可以用用
xiaobaozi 1 2010-4-11 22:03
6
0
无比膜拜!!!!零兄强大
luckbug 2010-4-11 22:16
7
0
什么都不说,我顶!
whypro 2010-4-11 22:23
8
0
谢谢楼主分享,自己努力中!
JiYangTX 2010-4-11 22:43
9
0
膜拜,好强大阿~
一九零五 2010-4-11 22:50
10
0
太强悍了
这才是玩游戏
xyh煌 2010-4-11 22:50
11
0
好厉害啊,羡慕中,要努力啊。
Campaign 2010-4-11 22:56
12
0
这游戏很好玩。。。
riusksk 41 2010-4-11 23:38
13
0
见过没玩过,呵呵……
支持cntrump兄弟!
xouou 2010-4-11 23:47
14
0
我看到我的同事玩过 挺经典的游戏 耐玩
Nisy 5 2010-4-12 00:37
15
0
Up ~ 支持下 楼主能共享下idb文件么
newine 2010-4-12 07:08
16
0
赞一个,俺的学习目标啊
flyforver 2010-4-12 08:44
17
0
好东西,过一段时间研究研究;
正好拿它下手,呵呵
icezy 2010-4-12 08:59
18
0
强大啊。支持了
panti 2010-4-12 08:59
19
0
了研究代码,我还得去玩游戏
eosnfi 2010-4-12 21:26
20
0
赞~~,现在能这么去分析的还真不多,支持下
luoyan 2010-4-12 21:43
21
0
郁闷。。我没用任何修改器。就通关了。。
太郁闷了
xlfxlfxlf 2010-4-12 22:39
22
0
LZ代码写得真好看...太有爱了
hmilywen 2010-4-13 10:32
23
0
这个好喜欢
风间仁 19 2010-4-13 14:32
24
0
分析对应的植物BOOL值是个体力活啊。。
shuichon 2010-4-13 22:13
25
0
不知道改说什么了,这才是玩游戏啊。~~~~

强!!!
fishinbubb 2010-4-14 12:51
26
0
源代码贴上来啊!哈哈!
comeon 2010-4-16 12:15
27
0
挺 不错的游戏,不过已经通关了。也不再那么想玩它了。呵呵。 Mark.
alwaysrun 2010-4-16 16:59
28
0
真全面啊。。
snn 2010-4-17 08:53
29
0
强悍!!太佩服了!
Crakme 2010-4-18 09:30
30
0
代码风格好帅气哦
支持
yylautumn 2010-4-18 09:47
31
0
挺不错的,我按楼主的介绍做下试试!
青鸟kai 2010-4-23 19:13
32
0
忍不住支持一下,想当初我是死打过去的,看出来了,这是游戏玩我!~
skyHope 2010-4-23 19:41
33
0
很帅气的编码风格!嘿嘿!
有个小小的请求。不知道cntrump你是怎么分析挖掘出来的。帮小菜我扫盲一下说说思路好吗?
rerefrancd 1 2010-4-24 09:19
34
0
数据分析很强大
henaxxz 2010-4-24 09:31
35
0
玩过这个游戏。挺好。楼主很强
kagayaki 2010-4-25 03:05
36
0
支持!!!!!!!!!!
不问年少 15 2010-4-27 09:09
37
0
不错,支持一把,不过早有修改器了
研究的精神值得学习啊!

上传的附件:
grail 2010-4-27 12:16
38
0
没玩过这个游戏,下次玩玩
cntrump 13 2010-5-8 11:35
39
0
[QUOTE=不问年少;797942]不错,支持一把,不过早有修改器了
研究的精神值得学习啊!

[/QUOTE]

存档修改器,我相信我还是首发,他们的是内存修改器。
我做完之后,搜索了一下,也发现有存档修改器。不过没有我做的全。
虽然别人有了,但是自己做出来才是自己的。
shityou 2010-5-12 12:30
40
0
哈哈,支持楼主
ppanger 4 2010-5-12 13:21
41
0
感谢分享 很cool 的游戏 很cool 的分析 :)
einyboy 2010-6-16 08:54
42
0
楼主说下那些游戏基址是怎么找到的啊。
JiLiR 2010-6-16 15:15
43
0
谢谢楼主分享,不过对于这种全屏的游戏该如何调试呢?我以前调试的时候总是卡住,不知道该怎么办
Florence 2010-6-18 13:14
44
0
顶,不过这个游戏挺简单,很容易就玩通关
fanhexin 2010-6-19 10:18
45
0
楼主如何挖掘出存档的数据结构的?
xztwana 2010-6-19 22:31
46
0
学习下哈~~~~~~~~~~~~~~~~~~~
tianci 2010-7-4 13:11
47
0
强人  赞一个!!!
youstar 2 2010-7-5 09:40
48
0
修改一处错误,感觉这里应该是WORD`
typedef struct _FILEHEADER
{
  DWORD  dwValidFlag; /* 值必须为0xE,文件有效性标识*/
  WORD  dwNum;    /* 保存用户的数量,十六进制值*/
}FILE_HEADER, *PFILE_HEADER;
BHack 2010-8-14 19:22
49
0
膜拜楼主,话说我也在分析,不过貌似后面还有储存花园里植物的
HeddaZ 2010-9-15 19:02
50
0
感谢,受益匪浅。无论是技术还是游戏通关上
游客
登录 | 注册 方可回帖
返回