日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

梦醒暗黑廿年

發(fā)布時(shí)間:2024/8/1 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 梦醒暗黑廿年 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

夢(mèng)醒暗黑廿年

向抗擊疫情的英雄們致敬

宇春秋
卌年未有過的閉門養(yǎng)豬生活,無病呻吟回憶年少游戲時(shí)光。
從英雄無敵三到文明四,突然想起廿年前通宵在網(wǎng)吧干暗黑二單機(jī)的日子。那時(shí)候什么都不會(huì),一點(diǎn)支配骷髏都不點(diǎn),一個(gè)人帶著一大群骷髏干普通蟲子,整個(gè)網(wǎng)吧看著我半個(gè)小時(shí)磨死都瑞爾的轟動(dòng),那一瞬間的滿足,到廿年后的今天都?xì)v歷在目。重玩暗黑二的時(shí)候,正好某網(wǎng)開劇毒世界,帶著廿年的回憶,懵懂的沖了進(jìn)去,然后在G戰(zhàn)隊(duì)群里看大佬們普及基本知識(shí),一點(diǎn)一點(diǎn)的從零開始學(xué)符文之語(yǔ),BUG殺,隔河殺…。
肝了一個(gè)月之后感覺心潮澎湃,只是每次蠻子手動(dòng)BO得太麻煩,本想找個(gè)能自動(dòng)BO的地圖,網(wǎng)上找了一圈竟然沒有,于是撿起OD想自己做個(gè)補(bǔ)丁,沒想到一補(bǔ)就補(bǔ)了半個(gè)月,而且補(bǔ)出了這個(gè)系列(暫定叫系列吧,雖然絕大部分?jǐn)?shù)據(jù)都有了,但第一次寫文章,還不知道能有幾篇:D)。
本系列只做學(xué)習(xí)交流,請(qǐng)不要用于其它非法目地。
另感謝sting大神,第一步的基礎(chǔ)研究就是從讀d2hackmap開始的,由于網(wǎng)上找了很久都沒找到免費(fèi)的1.13c版本源代碼,只能隨便找了個(gè)對(duì)應(yīng)1.09版本的代碼研究,后來還找了個(gè)d2hackmap2.24也就是對(duì)應(yīng)1.11b版本的代碼研究,兩個(gè)版本的代碼和文件結(jié)構(gòu)變化很大,分析的時(shí)候兩個(gè)版本可能有點(diǎn)混,但最后出的結(jié)果是好。
第一篇:地圖篇
開始真沒想過從地圖開始研究起,起初只是想BO得找到玩家,也就是遍歷周圍數(shù)據(jù),結(jié)果人物數(shù)據(jù)還沒找到,反而把地圖數(shù)據(jù)找出來了。
一、遍歷數(shù)據(jù)。
首先想到的是不開地圖進(jìn)游戲,小地圖上各個(gè)怪是沒有文字提示的,開地圖就有BOSS、精英怪和玩家的名字提示,所以這事肯定是地圖做的。
翻d2hackmap的代碼,里面正好有DrawAutomapCellPatch這個(gè)函數(shù),看函數(shù)名應(yīng)該是做這事的。仔細(xì)研讀了下,發(fā)現(xiàn)…看不懂,對(duì)這個(gè)游戲的數(shù)據(jù)一點(diǎn)研究沒有,硬來撞代碼,肯定頭疼。但沒辦法,先簡(jiǎn)單的分析一下。
函數(shù)的原型是這樣的:

void __stdcall DrawAutomapCellPatch(CellContext *pCellContext, DWORD xpos, DWORD ypos, RECT *cliprect, DWORD bright)

里面有xpos和ypos,坐標(biāo)嘛,那pCellContext肯定就是地圖元素的結(jié)構(gòu)了。函數(shù)里面有個(gè)常量CELLNO_WAYPOINT,定義的是

#define CELLNO_WAYPOINT 307

找個(gè)能用地圖加載,OD上去在d2hackmap模塊中搜索常量0x 133(十進(jìn)制307)。
在d2hackmap+ 0x15E60(不同的地圖偏移不同,請(qǐng)對(duì)應(yīng)自己的地圖分析,以后涉及hackmap模塊的地址不在做說明)這個(gè)函數(shù)中有使用到這個(gè)常量。
在這個(gè)函數(shù)中所有的數(shù)據(jù)都用到了堆棧,而且用到的數(shù)據(jù)和地址沒有關(guān)系,果斷CTRL+F9返回上一層看有沒有收獲。
返回到的上一層是D2Client.dll,原型如下:

6FB104D9 |. 8B4424 20 |mov eax, dword ptr [esp+0x20] 6FB104DD |. 51 |push ecx 6FB104DE |. 8D5424 2C |lea edx, dword ptr [esp+0x2C] 6FB104E2 |. 52 |push edx 6FB104E3 |. 55 |push ebp 6FB104E4 |. 50 |push eax 6FB104E5 |. 8D4C24 48 |lea ecx, dword ptr [esp+0x48] 6FB104E9 |. 51 |push ecx 6FB104EA |. E8 71594CA0 |call d2hackma.0FFD5E60>>>>>>d2hackmap HOOK 6FB104EF |. 8B4C24 18 |mov ecx, dword ptr [esp+0x18] 6FB104F3 |> 3B6E 10 |cmp ebp, dword ptr [esi+0x10] ; D2Client.6FAF4B50 6FB104F6 |. 7F 0F |jg short 6FB10507 6FB104F8 |. 8B49 10 |mov ecx, dword ptr [ecx+0x10]>>>>關(guān)鍵點(diǎn) 6FB104FB |. 85C9 |test ecx, ecx 6FB104FD |. 894C24 18 |mov dword ptr [esp+0x18], ecx

在6FB104F8有這樣的一行代碼:mov ecx, dword ptr [ecx+0x10],翻譯成C語(yǔ)言就是:

Addr=*(ULONG *)(Addr+0x10)

看樣子不是鏈表就是二叉樹。
那么最初的ecx地址是哪兒來的的呢?回到D2Client的調(diào)用函數(shù)頭6FB10250下斷,結(jié)果很迷茫,怎么返回的還是這個(gè)函數(shù)呢?
這種情況只有一種可能:遞歸調(diào)用。如果真是遞歸調(diào)用那么數(shù)據(jù)結(jié)構(gòu)很大可能就是二叉樹了,畢竟二叉樹的經(jīng)典遍歷就是遞歸嘛!
到D2Client模塊頭,CTRL+F搜索call 6FB10250,看是哪兒最初調(diào)用這個(gè)遞歸的。
第一次搜索到的還是在6FB10250函數(shù)中,CTRL+L繼續(xù)搜索,找到原型如下:

6FB10E05 |> \A1 C4C1BC6F mov eax, dword ptr [0x6FBCC1C4] 6FB10E0A |. 8B48 08 mov ecx, dword ptr [eax+0x8] 6FB10E0D |. 8D5424 10 lea edx, dword ptr [esp+0x10] 6FB10E11 |. E8 3AF4FFFF call 6FB10250>>>>>>>>調(diào)用1 6FB10E16 |. E8 E55D4CA0 call d2hackma.0FFD6C00 6FB10E1B |? 90 nop 6FB10E1C |. 8B49 0C mov ecx, dword ptr [ecx+0xC] 6FB10E1F |. 8D5424 10 lea edx, dword ptr [esp+0x10] 6FB10E23 |. E8 28F4FFFF call 6FB10250>>>>>>>>調(diào)用2 6FB10E28 |. A1 C4C1BC6F mov eax, dword ptr [0x6FBCC1C4] 6FB10E2D |. 8B48 10 mov ecx, dword ptr [eax+0x10] 6FB10E30 |. 8D5424 10 lea edx, dword ptr [esp+0x10] 6FB10E34 |. E8 17F4FFFF call 6FB10250>>>>>>>>調(diào)用3 6FB10E39 |. A1 B0C1BC6F mov eax, dword ptr [0x6FBCC1B0] 6FB10E3E |. 8B1D C4C1BC6F mov ebx, dword ptr [0x6FBCC1C4] 6FB10E44 |. 48 dec eax 6FB10E45 |. 8B43 04 mov eax, dword ptr [ebx+0x4]

看樣子遍歷了三個(gè)地址,分別是[[0x6FBCC1C4]+0x08], [[0x6FBCC1C4]+0x0C], [[0x6FBCC1C4]+0x10]。那么基本上可以確定以下三點(diǎn):
1、遍歷的基址是0x6FBCC1C4,也就是D2Client+0x11C1C4。
2、需要遍歷三個(gè)地址里面的數(shù)據(jù),分別是基址的0x08,0x0C,0x10三個(gè)偏移里的值。
3、再次回到6FB10250,發(fā)現(xiàn)遍歷時(shí)下一數(shù)據(jù)分別儲(chǔ)存在0x0C和0x10地址,那么可以99%確定是數(shù)據(jù)二叉樹儲(chǔ)存。
二、坐標(biāo)信息
基本數(shù)據(jù)有了,但最關(guān)鍵的坐標(biāo)呢?
不急,回到D2Client的6FB10250的函數(shù)看代碼,原型如下:

6FB102A6 |> \0FBF41 06 |movsx eax, word ptr [ecx+0x6] 6FB102AA |. 8B2D B016BA6F |mov ebp, dword ptr [0x6FBA16B0] 6FB102B0 |. 8D0480 |lea eax, dword ptr [eax+eax*4] 6FB102B3 |. D1E0 |shl eax, 1 6FB102B5 |. 99 |cdq 6FB102B6 |. F7FD |idiv ebp 6FB102B8 |. 8BF8 |mov edi, eax 6FB102BA |. 2B3D F8C1BC6F |sub edi, dword ptr [0x6FBCC1F8] 6FB102C0 |. 0FBF41 08 |movsx eax, word ptr [ecx+0x8] 6FB102C4 |. 8D0480 |lea eax, dword ptr [eax+eax*4] 6FB102C7 |. D1E0 |shl eax, 1 6FB102C9 |. 99 |cdq 6FB102CA |. F7FD |idiv ebp

看6FB102A6的movsx eax, word ptr [ecx+0x6] ,還記得嗎?ecx是地圖元素的地址,word ptr [ecx+0x6]應(yīng)該是個(gè)short型的數(shù)據(jù),對(duì)應(yīng)的就是xpos,那么word ptr [ecx+0x8]對(duì)應(yīng)的就是ypos了。然后還有一些shl,div計(jì)算就不管了,大概應(yīng)該是xpos(ypos)*5<<1/ [0x6FBA16B0],直接套匯編吧。
沒讀過什么書,沒專門學(xué)過編程,所以代碼很挫,看個(gè)大概意思就好(英語(yǔ)不好,很多單詞都是BAIDU查的:L)。代碼如下:

void GetMapCell(ULONG Addr,std::vector<ULONG> &arrMapCell) {if (IsBadReadPtr((LPVOID)Addr,0x100) != 0) return ;arrMapCell.push_back(Addr);GetMapCell(*(ULONG *)(Addr+0x0c),arrMapCell);GetMapCell(*(ULONG *)(Addr+0x10),arrMapCell);return; }void GetD2DllBase() {uD2ClientAddr=(ULONG)::GetModuleHandleA("D2Client.dll");uD2CommonAddr=(ULONG)::GetModuleHandleA("D2Common.dll");uD2WinAddr=(ULONG)::GetModuleHandleA("D2Win.dll");uD2Multi=(ULONG)::GetModuleHandleA("D2Multi.dll");uD2Launch = (ULONG)::GetModuleHandleA("d2launch.dll"); ...... }

三、畫出地圖
1、由于暗黑2的坐標(biāo)有負(fù)數(shù),所以找出最小坐標(biāo)和最大坐標(biāo),方便畫出圖形。

void InitCurMapInfo() {std::vector<ULONG> arrMapCell;ULONG MapBase=*(ULONG *)(uD2ClientAddr+0x11C1C4);if (IsBadReadPtr((LPVOID)MapBase, 0x20) != 0)return;GetMapCell(*(ULONG *)(MapBase+0x0C),arrMapCell);//實(shí)際上0x0C是墻的數(shù)據(jù)int MiniMapCellLen=*(int *)(uD2ClientAddr+0xF16B0);int MinX=65535,MinY=65535;int MaxX=0,MaxY=0;for(ULONG i=0;i<arrMapCell.size();i++){ULONG Addr=arrMapCell[i];if (IsBadReadPtr((LPVOID)Addr,0x100) == 0){int x=0,y=0;_asm{pushadmov ecx,Addrmovsx eax, word ptr [ecx+6]mov ebx, MiniMapCellLenlea eax, dword ptr [eax+eax*4]shl eax, 1cdqidiv ebxmov edi, eaxmov x,edimov ecx,Addrmovsx eax, word ptr [ecx+8]mov ebx, MiniMapCellLenlea eax, dword ptr [eax+eax*4]shl eax, 1cdqidiv ebxmov edi, eaxmov y,edipopad}if(x<MinX)MinX=x;if(y<MinY)MinY=y;if(x>MaxX)MaxX=x;if(y>MaxY)MaxY=y;}}siCurMapMinX=MinX;siCurMapMinY=MinY;siCurMapMaxX=MaxX;siCurMapMaxY=MaxY; }

2、保存地圖

void GetMapInfo() {std::vector<ULONG> arrMapCell;ULONG MapBase=*(ULONG *)(uD2ClientAddr+0x11C1C4);int MiniMapCellLen=*(int *)(uD2ClientAddr+0xF16B0);InitCurMapInfo();int MinX=siCurMapMinX,MaxX=siCurMapMaxX,MinY=siCurMapMinY,MaxY=siCurMapMaxY;CImage image;image.Create(MaxX-MinX,MaxY-MinY,24);//Create的背景是黑色的for (int y=0; y<MaxY-MinY; y++){BYTE *p=(BYTE *)image.GetPixelAddress(0,y);//黑色背景不好看,改成白色memset(p,255,(MaxX-MinX)*3);}HDC hdc=image.GetDC();SetBkMode(hdc,TRANSPARENT); CFont font;font.CreateFontA(20,0,0,0,200,FALSE,FALSE,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH|FF_SWISS,"Arial");::SelectObject(hdc,font.GetSafeHandle());GetMapCell(*(ULONG *)(MapBase+0x0C),arrMapCell);//0x0C是墻的數(shù)據(jù)for(ULONG i=0;i<arrMapCell.size();i++){ULONG Addr=arrMapCell[i];if (IsBadReadPtr((LPVOID)Addr,0x100) == 0){int x=0,y=0;_asm{pushadmov ecx,Addrmovsx eax, word ptr [ecx+6]mov ebx, MiniMapCellLenlea eax, dword ptr [eax+eax*4]shl eax, 1cdqidiv ebxmov edi, eaxmov x,edimov ecx,Addrmovsx eax, word ptr [ecx+8]mov ebx, MiniMapCellLenlea eax, dword ptr [eax+eax*4]shl eax, 1cdqidiv ebxmov edi, eaxmov y,edipopad}x=x-MinX;y=y-MinY;image.SetPixel(x,y,RGB(255,0,0));}}arrMapCell.clear();GetMapCell(*(ULONG *)(MapBase+0x08),arrMapCell);//是地圖附加元素,如水面for(ULONG i=0;i<arrMapCell.size();i++){ULONG Addr=arrMapCell[i];if (IsBadReadPtr((LPVOID)Addr,0x100) == 0){int x=0,y=0;_asm{pushadmov ecx,Addrmovsx eax, word ptr [ecx+6]mov ebx, MiniMapCellLenlea eax, dword ptr [eax+eax*4]shl eax, 1cdqidiv ebxmov edi, eaxmov x,edimov ecx,Addrmovsx eax, word ptr [ecx+8]mov ebx, MiniMapCellLenlea eax, dword ptr [eax+eax*4]shl eax, 1cdqidiv ebxmov edi, eaxmov y,edipopad}x=x-MinX;y=y-MinY;if(*(WORD *)(Addr+4)>=4&&*(WORD *)(Addr+4)<=8)//0x08是地圖元素,4-8是河水走不過去,需要畫在地圖上,其它的都畫上去太亂不好看{image.SetPixel(x,y,RGB(0,0,255));}}}arrMapCell.clear();GetMapCell(*(ULONG *)(MapBase+0x10),arrMapCell);//不知道是什么,沒注意for(ULONG i=0;i<arrMapCell.size();i++){ULONG Addr=arrMapCell[i];if (IsBadReadPtr((LPVOID)Addr,0x100) == 0){int x=0,y=0;_asm{pushadmov ecx,Addrmovsx eax, word ptr [ecx+6]mov ebx, MiniMapCellLenlea eax, dword ptr [eax+eax*4]shl eax, 1cdqidiv ebxmov edi, eaxmov x,edimov ecx,Addrmovsx eax, word ptr [ecx+8]mov ebx, MiniMapCellLenlea eax, dword ptr [eax+eax*4]shl eax, 1cdqidiv ebxmov edi, eaxmov y,edipopad}x=x-MinX;y=y-MinY;image.SetPixel(x,y,RGB(0,255,0));}}image.ReleaseDC();image.Save("C:\\map.bmp"); }

3、畫完之后發(fā)現(xiàn)大概能看出地圖的樣子了,但每個(gè)點(diǎn)之間的差距有點(diǎn)大,不能成一個(gè)封閉的地圖圖形,我的做法是把每個(gè)地圖坐標(biāo)點(diǎn)除以一個(gè)值,走廊窄的地圖/2,大地圖/4。然后判斷斜角點(diǎn)是否有數(shù)據(jù),如果有數(shù)據(jù)就把周圍點(diǎn)補(bǔ)齊,地圖就封閉起來了。望高手指點(diǎn)怎么處理更好!結(jié)果如下:

本人原創(chuàng),轉(zhuǎn)載請(qǐng)注明。
第一篇地圖篇完。(第二篇NPC篇待續(xù))

總結(jié)

以上是生活随笔為你收集整理的梦醒暗黑廿年的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。