梦醒暗黑廿年(二)
夢醒暗黑廿年
向抗擊疫情的英雄們致敬
宇春秋
第二篇:NPC篇
一、基本猜想
上一篇找玩家遍歷沒找到反而找到地圖元素,這回再仔細看看d2hackmap的代碼。
一般來說游戲里面NPC、玩家的結構或類的對象都是從一個父類中派生出來,存儲和處理代碼都是一致的。很多游戲的NPC和玩家基址都在一起,可能前一個基址是玩家,+0x04之后就存放著玩家的基址。甚至有的游戲NPC和玩家都混在一個表中,處理的時候僅靠一個標志位來區分。所以在d2hackmap中重點看下面的函數:
為什么會以這個函數為突破口呢?
1、這個函數和地圖元素的處理函數一樣,也有xpos和ypos,應該是有坐標處理的。(雖然這個坐標是對應的小地圖坐標)
2、在這個函數中有這樣的代碼:
很明顯這個函數中區分了對象是玩家還是怪物,而且印證了開始時的猜想–NPC和玩家是混在一起的,僅靠一個標志位來區分。雖然第三個UNITNO_ITEM不能猜出具體對應的是什么,畢竟ITEM的含義比較廣,但當最后遍歷看打印出信息了就一目了然了。(其實ITEM對應的是地面物品,并不太出乎意料)
3、這個函數里面雖然沒有太多的特征方便我們迅速定位,但這個函數是一個__fastcall調用,而且是一個__declspec(naked)調用的一部分,而這個naked調用是很有特征的:
二、OD上手
找個能用的地圖進入游戲,找到hackmap模塊,按CTRL+S進入查找命令序列,將
復制到查找窗口,很快就找到了d2hackmap代碼地址:
0F064C50 8BD0 mov edx, eax 0F064C52 56 push esi 0F064C53 FF7424 08 push dword ptr [esp+0x8] 0F064C57 E8 94FDFFFF call 0F0649F0 0F064C5C C2 0400 retn 0x4 0F064C5F CC int3在0F064C50這兒下斷, call 0F0649F0中的代碼就不用看了,具體的分析過程直接有源代碼可看,我們按F8單步返回到上一層看是怎么處理的。
上一層是在D2Client 的6FB119E0函數中。簡單看下6FB119E0函數,里面有段比較熟悉的代碼:
0x6FBA16B0這個基址在地圖元素的遍歷中用到過,而且前面的代碼簡單轉成C代碼大概是這樣:
Temp1=D2Common.#10651(Addr)/0x6FBA16B0 Temp2=D2Common.# 11142(Addr)/0x6FBA16B0是不是和地圖元素的坐標處理xpos(ypos)*5<<1/ [0x6FBA16B0]有點像?那大膽的猜一下Addr會不會就是NPC結構的地址,而Temp1和Temp2就是x和y?
其實如果要我寫游戲的代碼,我也很大機率會這樣寫:D。
在6FB119E0中沒有循環或遞歸調用的跡象,于是我們再返回上一層到了D2Client的6FB12410函數,而且正好落在下面這段代碼中:
6FB12462的call 6FB119E0正好是我們剛才分析的代碼,而且下一句mov esi, dword ptr [esi+0xE8]對應的C代碼是Addr=*(ULONG *)(Addr+0xE8),接下來兩句test esi, esi和jnz short 6FB12460明顯是個循環判斷,基本可以判定是找到NPC遍歷了。再仔細看下匯編,遍歷的基址正好是在函數頭中出現的0x6FBCBBFC。
現在可以寫出遍歷代碼:
三、查缺補漏
1、現在已經有NPC遍歷和NPC坐標了,還差一個NPC名字,這一點在目前找到的代碼中是沒有反映的。其實這是個小問題,一般來說NPC名字肯定是在每個NPC結構中的,從NPC結構的首地址起,每個有效的地址進去看一下就好了,開始以為是個體力活,沒想到每種不同類型的結構對應的名字偏移竟然不一樣,從一個體力活變成了一個大大的體力活。以下是總結的四種不同結構獲取名方法:
2、另外在上文中已經說過取NPC坐標是通過D2Common模塊中的第10651號和第11142號函數取得的,現在寫出取坐標函數:
void GetNpcPointInMap(ULONG NpcAddr,int &realx,int &realy) {int MiniMapCellLen=*(int *)(uD2ClientAddr+0xF16B0);if (IsBadReadPtr((LPVOID)NpcAddr, 0x200) != 0)return;int tempX=0,tempY=0,tempX2=0,tempY2=0;_asm{pushadmov esi,NpcAddr;push esicall uD2Common_10651push esimov edi, eaxcall uD2Common_11142mov ebx, MiniMapCellLenmov ecx, eaxmov eax, edicdqidiv ebxmov edi, eaxmov tempX2,eaxsub edi, edxmov eax, ecxcdqidiv ebxadd edi, 8mov ebx, eaxmov tempY2,eaxsub ebx, edxsub ebx, 8mov tempX,edimov tempY,ebx//原來的做法是從匯編代碼計算的,但后來發現只要人物一動,所有的相對坐標都要重算//所以改成直接用人物坐標,不用算小地圖popad}//這個取real坐標沒有像取小地圖坐標一樣x+8,y-8realx=tempX2;realy=tempY2; }3、由于游戲還有尸爆和尋找物品技能會對怪物尸體進行二次處理,所以遍歷出來的數據除了存在的怪外,還會有死去的怪,通過對比找出了一個不夠完美的標志位來判斷是否存活。目前玩家類結構還沒有處理,只處理了NPC類和地面附加物(如桶)代碼如下:
ULONG IsNpcLive(ULONG NpcAddr) {ULONG Addr=NpcAddr;if (IsBadReadPtr((LPVOID)Addr,0x100) != 0)return false;ULONG NpcType=*(ULONG *)Addr;if(NpcType==0){return false;}else if(NpcType==1){//type==1,npc,怪return *(ULONG *)(Addr+0x44);//死了就=0,不死就非0}else if(NpcType==2){//type==2桶類 ULONG uNpcInfoAddr=*(ULONG *)(Addr+0x14);if(IsBadReadPtr((LPVOID)uNpcInfoAddr,0x100)!=0)return false;return !*(WORD *)(uNpcInfoAddr+0x42);//如果是1就是桶爆了,0就是沒打開,取反對應函數名}return true; }測試代碼如下:
void testGetNpcInfo() {std::vector<ULONG> arrNpc;GetNpcArr(arrNpc);//0x00標志玩家00,npc01,桶02,地面物品04,for(ULONG i=0;i<arrNpc.size();i++){char szName[256]={0};int realx=0,realy=0;GetNpcPointInMap(arrNpc[i],realx,realy);GetNpcName(arrNpc[i], szName); ULONG Other = *(ULONG *)(arrNpc[i] + 0x14);//地面物品的顏色ULONG Color = 0;if (IsBadReadPtr((LPVOID)Other, 0x100) == 0){Color = *(ULONG *)(Other);}char str[256]={0};sprintf(str,"index=%d,type=%d,Addr=%08X,Name=%s,Face=%d,Point:%d,%d,status=%d,%d,Color=%d",i,*(ULONG *)arrNpc[i],arrNpc[i], szName,*(ULONG *)(arrNpc[i]+0x04),realx,realy,IsNpcLive(arrNpc[i]),*(ULONG *)(arrNpc[i]+0x10),Color);WriteLog(str);}}測試結果如下:
10:21:13 index=2,type=1,Addr=09100D00,Name=恰西,Face=154,Point:855,4360,status=17408,8,Color=157069056 10:21:13 index=3,type=2,Addr=09100700,Name=一股邪惡力量,Face=37,Point:860,4363,status=1,2,Color=159420184 10:21:13 index=4,type=1,Addr=09D2AD00,Name=一股邪惡力量,Face=149,Point:864,4367,status=768,1,Color=157066936 10:21:13 index=5,type=2,Addr=09101300,Name=一股邪惡力量,Face=37,Point:839,4362,status=1,2,Color=159420184 10:21:13 index=6,type=2,Addr=09101100,Name=一股邪惡力量,Face=37,Point:821,4362,status=1,2,Color=159420184 10:21:13 index=7,type=1,Addr=09100900,Name=一股邪惡力量,Face=152,Point:816,4363,status=5308,1,Color=157068208 10:21:13 index=8,type=1,Addr=09D29900,Name=一股邪惡力量,Face=149,Point:814,4364,status=883,1,Color=157066936 10:21:13 index=9,type=1,Addr=09D29A00,Name=一股邪惡力量,Face=149,Point:852,4367,status=1024,1,Color=157066936 10:21:13 index=10,type=2,Addr=09100F00,Name=一股邪惡力量,Face=37,Point:829,4375,status=1,2,Color=159420184 10:21:13 index=11,type=1,Addr=09100B00,Name=一股邪惡力量,Face=152,Point:820,4376,status=490,1,Color=157068208 10:21:13 index=12,type=1,Addr=09D29800,Name=一股邪惡力量,Face=149,Point:840,4376,status=1024,1,Color=157066936 10:21:13 index=13,type=1,Addr=09101500,Name=一股邪惡力量,Face=179,Point:880,4364,status=240,1,Color=157079656 10:21:13 index=14,type=2,Addr=09101700,Name=一股邪惡力量,Face=37,Point:883,4370,status=1,2,Color=159420184 10:21:13 index=15,type=2,Addr=09101B00,Name=小站,Face=119,Point:908,4381,status=1,2,Color=159456920 10:21:13 index=16,type=2,Addr=09101900,Name=一股邪惡力量,Face=37,Point:917,4390,status=1,2,Color=159420184 10:21:13 index=17,type=2,Addr=09101D00,Name=一股邪惡力量,Face=37,Point:928,4396,status=1,2,Color=159420184 10:21:13 index=18,type=1,Addr=09D24600,Name=一股邪惡力量,Face=149,Point:788,4383,status=1152,1,Color=157066936 10:21:13 index=19,type=1,Addr=09D24500,Name=一股邪惡力量,Face=149,Point:802,4390,status=1339,1,Color=157066936 10:21:13 index=20,type=1,Addr=09D24400,Name=一股邪惡力量,Face=149,Point:788,4374,status=305,1,Color=157066936 10:21:13 index=21,type=2,Addr=09D2E400,Name=一股邪惡力量,Face=37,Point:812,4386,status=1,2,Color=159420184 10:21:13 index=22,type=2,Addr=09D2E200,Name=一股邪惡力量,Face=37,Point:800,4374,status=1,2,Color=159420184 10:21:13 index=23,type=2,Addr=09D2E000,Name=一股邪惡力量,Face=37,Point:792,4388,status=1,2,Color=159420184 10:21:13 index=24,type=1,Addr=09101F00,Name=基得,Face=147,Point:807,4388,status=128,2,Color=157066088 10:21:13 index=25,type=1,Addr=09D2E800,Name=一股邪惡力量,Face=152,Point:855,4373,status=4865,1,Color=157068208 10:21:13 index=26,type=2,Addr=09D2EE00,Name=你的私人儲藏箱,Face=267,Point:865,4380,status=1,0,Color=159523224 10:21:13 index=27,type=2,Addr=09D2F000,Name=一股邪惡力量,Face=37,Point:849,4381,status=1,2,Color=159420184 10:21:13 index=28,type=2,Addr=09D2EC00,Name=一股邪惡力量,Face=37,Point:887,4384,status=1,2,Color=159420184 10:21:13 index=29,type=1,Addr=09D2E600,Name=瓦瑞夫,Face=155,Point:872,4386,status=3968,1,Color=157069480 10:21:13 index=30,type=0,Addr=090E8A00,Name=ddd,Face=4,Point:874,4388,status=0,5,Color=6579300 10:21:13 index=31,type=2,Addr=09D2EF00,Name=火焰,Face=39,Point:864,4389,status=1,0,Color=159421080 10:21:13 index=32,type=4,Addr=090E9300,Name=手斧【普通】 (1),Face=0,Point:872,4390,status=1,3,Color=2 10:21:13 index=33,type=4,Addr=090E9400,Name=圓盾【普通】【輕】 (1),Face=328,Point:873,4390,status=1,3,Color=2 10:21:13 index=34,type=2,Addr=09D2F200,Name=一股邪惡力量,Face=385,Point:870,4392,status=1,0,Color=159576088 10:21:13 index=35,type=1,Addr=09D2EA00,Name=卡夏,Face=150,Point:889,4394,status=1280,1,Color=157067360宇春秋原創,轉載請注明。
第二篇NPC篇結束。(第三篇包裹篇待續)
總結
- 上一篇: 从JavaEye社区被迫改名说起(转载他
- 下一篇: ListView的优化