2010-04-13
更新了一點(diǎn),盡量把demo做成program
2010-04-12
原來(lái)的程序在刪除用戶時(shí),沒(méi)有刪除對(duì)應(yīng)用戶的游戲進(jìn)度。現(xiàn)在修正
另外告訴大家一個(gè)關(guān)于游戲的小密秘。在帖子的最后。
植物大戰(zhàn)僵尸全解密---存檔篇
內(nèi)存修改器游俠網(wǎng)上已經(jīng)有了,再做也沒(méi)意思了,我來(lái)做一個(gè)存檔編輯工具吧
這是個(gè)很贊的游戲,最近沉迷上了.
由于本人水平太菜.老是徘徊在第4關(guān).于是便拿出家伙把它修理了一番.
和游戲相關(guān)的,都分析出來(lái)了,有些不太常用到的,就不分析了。
沒(méi)想到還挖出了不少好東西,不敢獨(dú)享.
我分析的是它的存檔文件數(shù)據(jù),能分析的都分析出來(lái)了,整理成了結(jié)構(gòu)體.方便使用.
存檔文件對(duì)應(yīng)的結(jié)構(gòu)體內(nèi)容如下:
users.dat
/************************************************************************/
/* userdata\users.dat 用戶信息文件數(shù)據(jù)結(jié)構(gòu) */
/************************************************************************/
//文件頭
typedef struct _FILEHEADER
{
DWORD dwValidFlag; /*值必須為0xE,文件有效性標(biāo)識(shí)*/
DWORD dwNum; /*保存用戶的數(shù)量,十六進(jìn)制值*/
}FILE_HEADER, *PFILE_HEADER;
typedef struct _USERINFO
{
WORD wNameLen; /*用戶名長(zhǎng)度*/
LPSTR pszName; /*用戶名,實(shí)際存放在文件中的是用戶名的ASCII字符,不包含末尾的NULL字符*/
DWORD dwReserveed; /*保留,經(jīng)測(cè)試,沒(méi)有發(fā)現(xiàn)其它用途*/
DWORD dwIndex; /*用戶ID, 對(duì)應(yīng)生成相應(yīng)的UserN.dat文件,N的值便是dwIndex/
}USER_INFO, *PUSER_INFO;
userN.dat(N代表用戶ID)
/************************************************************************
* userdata\userN.dat 用戶存檔數(shù)據(jù)結(jié)構(gòu), N的值和dwIndex相對(duì)應(yīng),即每個(gè)用戶自己的存檔文件.
************************************************************************/
typedef struct _USERDATA
{
DWORD dwValidFlag; /*offset:0x0 值必須為0xC,文件有效性標(biāo)識(shí)*/
DWORD dwGuan; /*offset:0x4 當(dāng)前關(guān)卡 */
DWORD dwMoney; /*offset:0x8 金錢(qián)數(shù)*/
DWORD dwSilverCup; /*offset:0xc 獲取銀向日葵獎(jiǎng)杯,得到金杯后,該位上的數(shù)字表示完成了n次冒險(xiǎn)*/
/* 更多游戲通關(guān) 全勝條件:低0~4位和高24~28位都為1 */
DWORD dwSC_Game[10]; /*offset:0x10~0x30+0x8 生存模式通關(guān)*/
DWORD dwReserved_9[5];
DWORD dwXYX_Game[20]; /*offset:0x40+0xc~0x90+0x8 小游戲通關(guān)*/
DWORD dwReserved_10[15];
DWORD dwJM_1_9_Game[9]; /*offset:0xd0+0x8~0xf0+0x8 解迷游戲1-9關(guān)*/
DWORD dwReserved_11; /*這個(gè)是無(wú)限任務(wù),無(wú)法通關(guān), 記錄的是通過(guò)該關(guān)的次數(shù), 類(lèi)似的偏移量還有幾個(gè),我全部保留了*/
DWORD dwJM_10_18_Game[9];/*offset:0x100~0x120 解迷游戲10-18關(guān)*/
DWORD dwReserved_1[31];
/* 游戲道具 */
DWORD dwBuy_JQSS; /*offset:0x1a0 購(gòu)買(mǎi) 機(jī)槍射手,BOOL值*/
DWORD dwBuy_SSXRK; /*offset:0x1a0+0x4 購(gòu)買(mǎi) 雙生向日葵,BOOL值*/
DWORD dwBuy_YYG; /*offset:0x1a0+0x8 購(gòu)買(mǎi) 憂郁茹,BOOL值*/
DWORD dwBuy_XP; /*offset:0x1a0+0xc 購(gòu)買(mǎi) 香蒲,BOOL值*/
DWORD dwBuy_BG; /*offset:0x1b0 購(gòu)買(mǎi)冰瓜*/
DWORD dwBuy_XJC; /*offset:0x1b0+0x4 購(gòu)買(mǎi) 吸金磁,BOOL值*/
DWORD dwBuy_DCW; /*offset:0x1b0+0x8 購(gòu)買(mǎi) 地刺王,BOOL值*/
DWORD dwBuy_YMJNP; /*offset:0x1b0+0xc 購(gòu)買(mǎi) 玉米加農(nóng)炮,BOOL值*/
DWORD dwBuy_MFZ; /*offset:0x1c0 購(gòu)買(mǎi)模仿者,BOOL值*/
DWORD dwReserved_3[1];
DWORD dwReserved_12[3]; // 金盞花芽種,共三個(gè),每個(gè)變量的值為0x0ea6,但是要在文件尾添加花的生長(zhǎng)數(shù)據(jù),先保留。
/* 禪境花園道具 */
DWORD dwBuy_HJSH; /*offset:0x1d0+0x4 購(gòu)買(mǎi) 黃金水壺,BOOL值*/
DWORD dwBuy_FL; /*offset:0x1d0+0x8 購(gòu)買(mǎi) 肥料,低8字節(jié)為數(shù)量ED,低9~16字節(jié)為購(gòu)買(mǎi)標(biāo)識(shí)0x3*/
DWORD dwBuy_SCJ; /*offset:0x1d0+0xc 購(gòu)買(mǎi) 殺蟲(chóng)劑,低8字節(jié)為數(shù)量ED,低9~16字節(jié)為購(gòu)買(mǎi)標(biāo)識(shí)0x3*/
DWORD dwBuy_CPJ; /*offset:0x1e0 購(gòu)買(mǎi) 唱片機(jī),BOOL值*/
DWORD dwBuy_YYST; /*offset:0x1e0+0x4 購(gòu)買(mǎi) 園藝手套,BOOL值*/
DWORD dwBuy_MGY; /*offset:0x1e0+0x8 購(gòu)買(mǎi) 蘑菇園,BOOL值*/
DWORD dwBuy_TC; /*offset:0x1e0+0xc 購(gòu)買(mǎi) 推車(chē),BOOL值*/
/* 游戲輔助道具 */
DWORD dwBuy_CWGN; /*offset:0x1f0 購(gòu)買(mǎi)寵物蝸牛值為, 0x04bbda2fe */
DWORD dwAddCardNum; /*offset:0x1f0+0x4 增加的道具槽,有效范圍:0x1~0x4*/
DWORD dwBuy_CTQJC; /*offset:0x1f0+0x8 購(gòu)買(mǎi) 池塘清潔車(chē),BOOL值*/
DWORD dwBuy_WDTC; /*offset:0x1f0+0xc 購(gòu)買(mǎi) 屋頂推車(chē),BOOL值*/
DWORD dwBuy_SB; /*offset:0x200 購(gòu)買(mǎi) 掃把, 已購(gòu)買(mǎi):0x3,未購(gòu)買(mǎi):0*/
DWORD dwBuy_SZG; /*offset:0x200+0x4 購(gòu)買(mǎi) 水族館,BOOL值*/
DWORD dwReserved_8[1];
DWORD dwBuy_ZHS; /*offset:0x200+0xc 購(gòu)買(mǎi) 智慧樹(shù),BOOL值*/
DWORD dwBuy_ZHS_FL; /*offset:210 購(gòu)買(mǎi) 智慧樹(shù)肥料, 值為0x03e8時(shí)狀態(tài)為可買(mǎi),買(mǎi)一個(gè)加一,最大為10個(gè)*/
DWORD dwBuy_JGBZS; /*offset:0x210+0x4 購(gòu)買(mǎi) 堅(jiān)果包扎術(shù),BOOL值*/
DWORD dwReserved_5[58];
/* 開(kāi)啟小游戲 */
DWORD dwUnlock_XYX; /*offset:0x300 開(kāi)啟 小游戲,BOOL值*/
DWORD dwUnlock_JM; /*offset:0x300+0x4 開(kāi)啟 解迷模式,BOOL值*/
DWORD dwReserved_6[4];
DWORD dwUnlock_SC; /*offset:0x310+0x8 開(kāi)啟 生存模式,BOOL值*/
DWORD dwReserved_7[6];
}USER_DATA, *PUSER_DATA;
在游戲中修改陽(yáng)光數(shù)的代碼如下:
DWORD dwMemAddr = 0x002a9f38; //指針地址offset,通過(guò)偏移的方式讀取地址比較保險(xiǎn).
DWORD dwOffset1 = 0x00000768; //這是個(gè)二級(jí)指針,即指向指針的指針,這是二級(jí)指針指向的地址的偏移量
DWORD dwOffset2 = 0x00005560; //這是二級(jí)指針指向的指針的地址,保存的就是冒險(xiǎn)模式中的陽(yáng)光數(shù),正是我們的目標(biāo)地址
__try
{
hWnd = FindWindow(_T("MainWindow"), NULL);//查找游戲的窗口,如果不存在則說(shuō)明游戲沒(méi)有運(yùn)行
if (INVALID_HANDLE_VALUE == hWnd)
{
__leave;
}
dwMemBase = GetWindowLongPtr(hWnd, GWL_HINSTANCE); //先獲取游戲進(jìn)程的基址
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這三個(gè)操作而已,
//只需要對(duì)進(jìn)程有 PROCESS_VM_READ|PROCESS_VM_WRITE|PROCESS_VM_OPERATION 的權(quán)限就夠了。
//MSDN上說(shuō)PROCESS_VM_OPERATION權(quán)限就可以進(jìn)行WriteProcessMemory操作了,實(shí)際過(guò)程中卻是
//句柄無(wú)效,為了保險(xiǎn)。我把三個(gè)屬性都加上~~~
if (hProc == NULL)
{
g_bLocked = FALSE;
ShowErrorInfo("獲取進(jìn)程出錯(cuò)");
__leave;
}
if (!VirtualProtectEx(hProc, (LPVOID)dwMemAddr, sizeof(DWORD), PAGE_READWRITE, &dwOldProtect)) //改變內(nèi)存地址屬性為可讀寫(xiě)。
{
ShowErrorInfo("修改內(nèi)存屬性出錯(cuò)");
__leave;
}
DWORD dwReaded = 0;
if (!ReadProcessMemory(hProc, (LPVOID)dwMemAddr, &dwSunny, sizeof(DWORD), &dwReaded)) //先把0x001224d4的地址內(nèi)容讀出來(lái),由于游戲使用的是二級(jí)地址即指向指針的指針,
{ //所以現(xiàn)在讀出來(lái)的內(nèi)容其實(shí)是一個(gè)地址,我們還要繼續(xù)讀,直到讀取到數(shù)據(jù)為止。
ShowErrorInfo("讀取內(nèi)存數(shù)據(jù)出錯(cuò)");
__leave;
}
dwMemAddr = dwSunny + dwOffset1; //the address offset is 0x768 ^_^~~;
if (!ReadProcessMemory(hProc, (LPVOID)dwMemAddr, &dwSunny, sizeof(DWORD), &dwReaded)) //先把0x001224d4的地址內(nèi)容讀出來(lái),由于游戲使用的是二級(jí)地址即指向指針的指針,
{ //所以現(xiàn)在讀出來(lái)的內(nèi)容其實(shí)是一個(gè)地址,我們還要繼續(xù)讀,直到讀取到數(shù)據(jù)為止。
ShowErrorInfo("讀取內(nèi)存數(shù)據(jù)出錯(cuò)");
__leave;
}
dwMemAddr = dwSunny + dwOffset2; //the address offset is 0x5560 ^_^~~;
if (!ReadProcessMemory(hProc, (LPVOID)dwMemAddr, &dwSunny, sizeof(DWORD), &dwReaded)) //由于游戲使用的是二級(jí)地址,所以只要再讀一次就可以得到我們需要的數(shù)據(jù)了。
{
ShowErrorInfo("讀取內(nèi)存數(shù)據(jù)出錯(cuò)");
__leave;
}
if (bIsRead)
{
SetDlgItemInt(hDlg, IDC_EDT_SUNNY, dwSunny, FALSE); //成功讀出來(lái)以后把數(shù)據(jù)顯示到界面。
//如果能成功獲取數(shù)據(jù),設(shè)置控件狀態(tài)可用,防止誤寫(xiě)入錯(cuò)誤的內(nèi)存地址
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) //如果不等于我們?cè)O(shè)定的值才進(jìn)行修改。
{
__leave;
}
if (!WriteProcessMemory(hProc, (LPVOID)dwMemAddr, &dwNewSunny, sizeof(DWORD), &dwWritten)) //如果是要改變數(shù)據(jù)的值,只進(jìn)行寫(xiě)操作。
{
ShowErrorInfo("寫(xiě)入內(nèi)存數(shù)據(jù)出錯(cuò)");
__leave;
}
}
}
__finally
{
if (dwOldProtect != 0)
{
if (!VirtualProtectEx(hProc, (LPVOID)dwMemAddr, sizeof(DWORD), dwOldProtect, &dwOldProtect)) //做完要做的事以后,再把屬性設(shè)置回去。這一步也可以省略.
{
ShowErrorInfo("恢復(fù)內(nèi)存屬性出錯(cuò)");
}
}
CloseHandle(hProc);
}
}
下載地址: http://bbs.pediy.com/showthread.php?t=115080&highlight=
國(guó)內(nèi)有漢化版,漢化的版本已經(jīng)是無(wú)限制版本了,而且漢化的質(zhì)量很棒.
這個(gè)游戲本身就是綠色免費(fèi)的小游戲,只不過(guò)官方自己加上了一個(gè)loader來(lái)顯示試用信息而已.
下面教大家一個(gè)魔術(shù),把正式版從試用版里變出來(lái),不使用任何修改工具.
從官方網(wǎng)站下載游戲安裝程序.原版是英文的,默認(rèn)安裝路徑是C:\Program Files\PopCap Games\Plants vs. Zombies 如果你更改了安裝路徑,那么就到對(duì)應(yīng)的路徑下.運(yùn)行PlantsVsZombies.exe,會(huì)彈出來(lái)一個(gè)對(duì)話框,選擇 "Play Trial Game" 來(lái)啟動(dòng)游戲,在游戲顯示loading畫(huà)面的時(shí)候,再到游戲的安裝目錄看一下,會(huì)發(fā)現(xiàn)一個(gè)新的文件popcapgame1.exe,文件大小是2.86MB,屬性為隱藏.這個(gè)不是木馬,也不是惡意文件,而是正式版的游戲主程序.該文件是用獨(dú)占方式創(chuàng)建的,不能直接復(fù)制.我們使用強(qiáng)大的磁盤(pán)管理工具Disk Genius來(lái)提取它.我選擇的是復(fù)制到桌面.OK了,退出游戲.把桌面上的popcapgame1.exe重命名為PlantsVsZombies.exe,然后覆蓋游戲安裝目錄下的同名文件,再次運(yùn)行游戲.怎么樣?是不是什么提示信息也沒(méi)有了,而且游戲一切正常,沒(méi)有任何限制.
為什么一個(gè)商業(yè)休閑游戲如此脆弱?或者這正是它的賣(mài)點(diǎn)所在,想通過(guò)讓玩家發(fā)現(xiàn)所謂的技巧來(lái)提升自己的人氣么?亦或是游戲發(fā)現(xiàn)自己運(yùn)行在中國(guó)境內(nèi),知道俺們china ren不好惹,自動(dòng)就把程序貢獻(xiàn)出來(lái)了?
附件中的PlantsVsZombies_en.rar是我提取出來(lái)的主程序,解壓到游戲安裝目錄并選擇覆蓋原來(lái)的文件就行了,版本必須是官方的英文原版.
附件中是我寫(xiě)的游戲助手,用來(lái)演示修改陽(yáng)光數(shù),定制用戶存檔文件.
本文轉(zhuǎn)自 http://bbs.pediy.com/showthread.php?t=110690