梦醒暗黑廿年
夢醒暗黑廿年
向抗擊疫情的英雄們致敬
宇春秋
 卌年未有過的閉門養豬生活,無病呻吟回憶年少游戲時光。
 從英雄無敵三到文明四,突然想起廿年前通宵在網吧干暗黑二單機的日子。那時候什么都不會,一點支配骷髏都不點,一個人帶著一大群骷髏干普通蟲子,整個網吧看著我半個小時磨死都瑞爾的轟動,那一瞬間的滿足,到廿年后的今天都歷歷在目。重玩暗黑二的時候,正好某網開劇毒世界,帶著廿年的回憶,懵懂的沖了進去,然后在G戰隊群里看大佬們普及基本知識,一點一點的從零開始學符文之語,BUG殺,隔河殺…。
 肝了一個月之后感覺心潮澎湃,只是每次蠻子手動BO得太麻煩,本想找個能自動BO的地圖,網上找了一圈竟然沒有,于是撿起OD想自己做個補丁,沒想到一補就補了半個月,而且補出了這個系列(暫定叫系列吧,雖然絕大部分數據都有了,但第一次寫文章,還不知道能有幾篇:D)。
 本系列只做學習交流,請不要用于其它非法目地。
 另感謝sting大神,第一步的基礎研究就是從讀d2hackmap開始的,由于網上找了很久都沒找到免費的1.13c版本源代碼,只能隨便找了個對應1.09版本的代碼研究,后來還找了個d2hackmap2.24也就是對應1.11b版本的代碼研究,兩個版本的代碼和文件結構變化很大,分析的時候兩個版本可能有點混,但最后出的結果是好。
 第一篇:地圖篇
 開始真沒想過從地圖開始研究起,起初只是想BO得找到玩家,也就是遍歷周圍數據,結果人物數據還沒找到,反而把地圖數據找出來了。
 一、遍歷數據。
 首先想到的是不開地圖進游戲,小地圖上各個怪是沒有文字提示的,開地圖就有BOSS、精英怪和玩家的名字提示,所以這事肯定是地圖做的。
 翻d2hackmap的代碼,里面正好有DrawAutomapCellPatch這個函數,看函數名應該是做這事的。仔細研讀了下,發現…看不懂,對這個游戲的數據一點研究沒有,硬來撞代碼,肯定頭疼。但沒辦法,先簡單的分析一下。
 函數的原型是這樣的:
里面有xpos和ypos,坐標嘛,那pCellContext肯定就是地圖元素的結構了。函數里面有個常量CELLNO_WAYPOINT,定義的是
#define CELLNO_WAYPOINT 307找個能用地圖加載,OD上去在d2hackmap模塊中搜索常量0x 133(十進制307)。
 在d2hackmap+ 0x15E60(不同的地圖偏移不同,請對應自己的地圖分析,以后涉及hackmap模塊的地址不在做說明)這個函數中有使用到這個常量。
 在這個函數中所有的數據都用到了堆棧,而且用到的數據和地址沒有關系,果斷CTRL+F9返回上一層看有沒有收獲。
 返回到的上一層是D2Client.dll,原型如下:
在6FB104F8有這樣的一行代碼:mov ecx, dword ptr [ecx+0x10],翻譯成C語言就是:
Addr=*(ULONG *)(Addr+0x10)看樣子不是鏈表就是二叉樹。
 那么最初的ecx地址是哪兒來的的呢?回到D2Client的調用函數頭6FB10250下斷,結果很迷茫,怎么返回的還是這個函數呢?
 這種情況只有一種可能:遞歸調用。如果真是遞歸調用那么數據結構很大可能就是二叉樹了,畢竟二叉樹的經典遍歷就是遞歸嘛!
 到D2Client模塊頭,CTRL+F搜索call 6FB10250,看是哪兒最初調用這個遞歸的。
 第一次搜索到的還是在6FB10250函數中,CTRL+L繼續搜索,找到原型如下:
看樣子遍歷了三個地址,分別是[[0x6FBCC1C4]+0x08], [[0x6FBCC1C4]+0x0C], [[0x6FBCC1C4]+0x10]。那么基本上可以確定以下三點:
 1、遍歷的基址是0x6FBCC1C4,也就是D2Client+0x11C1C4。
 2、需要遍歷三個地址里面的數據,分別是基址的0x08,0x0C,0x10三個偏移里的值。
 3、再次回到6FB10250,發現遍歷時下一數據分別儲存在0x0C和0x10地址,那么可以99%確定是數據二叉樹儲存。
 二、坐標信息
 基本數據有了,但最關鍵的坐標呢?
 不急,回到D2Client的6FB10250的函數看代碼,原型如下:
看6FB102A6的movsx eax, word ptr [ecx+0x6] ,還記得嗎?ecx是地圖元素的地址,word ptr [ecx+0x6]應該是個short型的數據,對應的就是xpos,那么word ptr [ecx+0x8]對應的就是ypos了。然后還有一些shl,div計算就不管了,大概應該是xpos(ypos)*5<<1/ [0x6FBA16B0],直接套匯編吧。
 沒讀過什么書,沒專門學過編程,所以代碼很挫,看個大概意思就好(英語不好,很多單詞都是BAIDU查的:L)。代碼如下:
三、畫出地圖
 1、由于暗黑2的坐標有負數,所以找出最小坐標和最大坐標,方便畫出圖形。
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是墻的數據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、畫完之后發現大概能看出地圖的樣子了,但每個點之間的差距有點大,不能成一個封閉的地圖圖形,我的做法是把每個地圖坐標點除以一個值,走廊窄的地圖/2,大地圖/4。然后判斷斜角點是否有數據,如果有數據就把周圍點補齊,地圖就封閉起來了。望高手指點怎么處理更好!結果如下:
 
 本人原創,轉載請注明。
 第一篇地圖篇完。(第二篇NPC篇待續)
總結
 
                            
                        - 上一篇: extremeComponents资料
- 下一篇: 提供博客里提到的几个程序的下载地址
