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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

梦醒暗黑廿年

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

夢醒暗黑廿年

向抗擊疫情的英雄們致敬

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

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

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

#define CELLNO_WAYPOINT 307

找個能用地圖加載,OD上去在d2hackmap模塊中搜索常量0x 133(十進制307)。
在d2hackmap+ 0x15E60(不同的地圖偏移不同,請對應(yīng)自己的地圖分析,以后涉及hackmap模塊的地址不在做說明)這個函數(shù)中有使用到這個常量。
在這個函數(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)鍵點 6FB104FB |. 85C9 |test ecx, ecx 6FB104FD |. 894C24 18 |mov dword ptr [esp+0x18], ecx

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

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

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

看樣子遍歷了三個地址,分別是[[0x6FBCC1C4]+0x08], [[0x6FBCC1C4]+0x0C], [[0x6FBCC1C4]+0x10]。那么基本上可以確定以下三點:
1、遍歷的基址是0x6FBCC1C4,也就是D2Client+0x11C1C4。
2、需要遍歷三個地址里面的數(shù)據(jù),分別是基址的0x08,0x0C,0x10三個偏移里的值。
3、再次回到6FB10250,發(fā)現(xiàn)遍歷時下一數(shù)據(jù)分別儲存在0x0C和0x10地址,那么可以99%確定是數(shù)據(jù)二叉樹儲存。
二、坐標信息
基本數(shù)據(jù)有了,但最關(guān)鍵的坐標呢?
不急,回到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)該是個short型的數(shù)據(jù),對應(yīng)的就是xpos,那么word ptr [ecx+0x8]對應(yīng)的就是ypos了。然后還有一些shl,div計算就不管了,大概應(yīng)該是xpos(ypos)*5<<1/ [0x6FBA16B0],直接套匯編吧。
沒讀過什么書,沒專門學過編程,所以代碼很挫,看個大概意思就好(英語不好,很多單詞都是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的坐標有負數(shù),所以找出最小坐標和最大坐標,方便畫出圖形。

void InitCurMapInfo() {std::vector<ULONG> arrMapCell;ULONG MapBase=*(ULONG *)(uD2ClientAddr+0x11C1C4);if (IsBadReadPtr((LPVOID)MapBase, 0x20) != 0)return;GetMapCell(*(ULONG *)(MapBase+0x0C),arrMapCell);//實際上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)大概能看出地圖的樣子了,但每個點之間的差距有點大,不能成一個封閉的地圖圖形,我的做法是把每個地圖坐標點除以一個值,走廊窄的地圖/2,大地圖/4。然后判斷斜角點是否有數(shù)據(jù),如果有數(shù)據(jù)就把周圍點補齊,地圖就封閉起來了。望高手指點怎么處理更好!結(jié)果如下:

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

總結(jié)

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

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