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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

梦醒暗黑廿年(二)

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

夢醒暗黑廿年

向抗擊疫情的英雄們致敬
宇春秋

第二篇:NPC篇
一、基本猜想
上一篇找玩家遍歷沒找到反而找到地圖元素,這回再仔細看看d2hackmap的代碼。
一般來說游戲里面NPC、玩家的結(jié)構(gòu)或類的對象都是從一個父類中派生出來,存儲和處理代碼都是一致的。很多游戲的NPC和玩家基址都在一起,可能前一個基址是玩家,+0x04之后就存放著玩家的基址。甚至有的游戲NPC和玩家都混在一個表中,處理的時候僅靠一個標(biāo)志位來區(qū)分。所以在d2hackmap中重點看下面的函數(shù):

void __fastcall DrawAutomapBlob(int xpos, int ypos, DWORD dwColor,UnitAny *pUnit)

為什么會以這個函數(shù)為突破口呢?
1、這個函數(shù)和地圖元素的處理函數(shù)一樣,也有xpos和ypos,應(yīng)該是有坐標(biāo)處理的。(雖然這個坐標(biāo)是對應(yīng)的小地圖坐標(biāo))
2、在這個函數(shù)中有這樣的代碼:

celltype = pUnit->dwUnitType; celltype==UNITNO_PLAYER ... celltype==UNITNO_MONSTER ... celltype==UNITNO_ITEM

很明顯這個函數(shù)中區(qū)分了對象是玩家還是怪物,而且印證了開始時的猜想–NPC和玩家是混在一起的,僅靠一個標(biāo)志位來區(qū)分。雖然第三個UNITNO_ITEM不能猜出具體對應(yīng)的是什么,畢竟ITEM的含義比較廣,但當(dāng)最后遍歷看打印出信息了就一目了然了。(其實ITEM對應(yīng)的是地面物品,并不太出乎意料)
3、這個函數(shù)里面雖然沒有太多的特征方便我們迅速定位,但這個函數(shù)是一個__fastcall調(diào)用,而且是一個__declspec(naked)調(diào)用的一部分,而這個naked調(diào)用是很有特征的:

void __declspec(naked) DrawBlobPath_ASM() {__asm {mov edx , eaxpush esipush [esp+8]call DrawAutomapBlobret 4} }

二、OD上手
找個能用的地圖進入游戲,找到hackmap模塊,按CTRL+S進入查找命令序列,將

mov edx , eax push esi push [esp+8]

復(fù)制到查找窗口,很快就找到了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函數(shù)中。簡單看下6FB119E0函數(shù),里面有段比較熟悉的代碼:

6FB11A49 |. 57 push edi 6FB11A4A |. 56 push esi 6FB11A4B |. E8 82A7FAFF call <jmp.&D2Common.#10651> 6FB11A50 |. 56 push esi 6FB11A51 |. 8BF8 mov edi, eax 6FB11A53 |. E8 9EA7FAFF call <jmp.&D2Common.#11142> 6FB11A58 |. 8B1D B016BA6F mov ebx, dword ptr [0x6FBA16B0] 6FB11A5E |. 8BC8 mov ecx, eax 6FB11A60 |. 8BC7 mov eax, edi 6FB11A62 |. 99 cdq 6FB11A63 |. F7FB idiv ebx 6FB11A65 |. 8B15 F8C1BC6F mov edx, dword ptr [0x6FBCC1F8] 6FB11A6B |. 8BF8 mov edi, eax 6FB11A6D |. 2BFA sub edi, edx 6FB11A6F |. 8BC1 mov eax, ecx 6FB11A71 |. 99 cdq 6FB11A72 |. F7FB idiv ebx 6FB11A74 |. 8B15 FCC1BC6F mov edx, dword ptr [0x6FBCC1FC] 6FB11A7A |. 83C7 08 add edi, 0x8 6FB11A7D |. 8BD8 mov ebx, eax 6FB11A7F |. A1 28C2BC6F mov eax, dword ptr [0x6FBCC228] 6FB11A84 |. 2BDA sub ebx, edx 6FB11A86 |. 83EB 08 sub ebx, 0x8 6FB11A89 |. 3BF8 cmp edi, eax

0x6FBA16B0這個基址在地圖元素的遍歷中用到過,而且前面的代碼簡單轉(zhuǎn)成C代碼大概是這樣:

Temp1=D2Common.#10651(Addr)/0x6FBA16B0 Temp2=D2Common.# 11142(Addr)/0x6FBA16B0

是不是和地圖元素的坐標(biāo)處理xpos(ypos)*5<<1/ [0x6FBA16B0]有點像?那大膽的猜一下Addr會不會就是NPC結(jié)構(gòu)的地址,而Temp1和Temp2就是x和y?
其實如果要我寫游戲的代碼,我也很大機率會這樣寫:D。
在6FB119E0中沒有循環(huán)或遞歸調(diào)用的跡象,于是我們再返回上一層到了D2Client的6FB12410函數(shù),而且正好落在下面這段代碼中:

6FB12413 |. A1 FCBBBC6F mov eax, dword ptr [0x6FBCBBFC] 6FB12418 |. 53 push ebx 6FB12419 |. 55 push ebp 6FB1241A |. 8B2D 14BCBC6F mov ebp, dword ptr [0x6FBCBC14] 6FB12420 |. 56 push esi 6FB12421 |. 57 push edi 6FB12422 |. 8D4C24 10 lea ecx, dword ptr [esp+0x10] 6FB12426 |. 51 push ecx 6FB12427 |. 8D5424 18 lea edx, dword ptr [esp+0x18] 6FB1242B |. 52 push edx 6FB1242C |. 33FF xor edi, edi 6FB1242E |. 50 push eax 6FB1242F |. 897C24 20 mov dword ptr [esp+0x20], edi 6FB12433 |. 897C24 1C mov dword ptr [esp+0x1C], edi 6FB12437 |. E8 B49DFAFF call <jmp.&D2Common.#10331> 6FB1243C |. 50 push eax 6FB1243D |. E8 9A9FFAFF call <jmp.&D2Common.#10383> 6FB12442 |. 8B4424 10 mov eax, dword ptr [esp+0x10] 6FB12446 |. 3BC7 cmp eax, edi 6FB12448 |. 8BD8 mov ebx, eax 6FB1244A |. 7E 2A jle short 6FB12476 6FB1244C |. 8D6424 00 lea esp, dword ptr [esp] 6FB12450 |> 8B4424 14 /mov eax, dword ptr [esp+0x14] 6FB12454 |. 8B0CB8 |mov ecx, dword ptr [eax+edi*4] 6FB12457 |. 8B71 74 |mov esi, dword ptr [ecx+0x74] 6FB1245A |. 85F6 |test esi, esi 6FB1245C |. 74 13 |je short 6FB12471 6FB1245E |. 8BFF |mov edi, edi 6FB12460 |> 8BC6 |/mov eax, esi 6FB12462 |. E8 79F5FFFF ||call 6FB119E0 6FB12467 |. 8BB6 E8000000 ||mov esi, dword ptr [esi+0xE8] 6FB1246D |. 85F6 ||test esi, esi 6FB1246F |.^ 75 EF |\jnz short 6FB12460

6FB12462的call 6FB119E0正好是我們剛才分析的代碼,而且下一句mov esi, dword ptr [esi+0xE8]對應(yīng)的C代碼是Addr=*(ULONG *)(Addr+0xE8),接下來兩句test esi, esi和jnz short 6FB12460明顯是個循環(huán)判斷,基本可以判定是找到NPC遍歷了。再仔細看下匯編,遍歷的基址正好是在函數(shù)頭中出現(xiàn)的0x6FBCBBFC。
現(xiàn)在可以寫出遍歷代碼:

// GetD2DllBase包含了以后所有的基址和暗黑的導(dǎo)出函數(shù)初始化,以后的代碼將不再出現(xiàn)這個函數(shù) 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");uD2Common_10331=(ULONG)::GetProcAddress((HMODULE)uD2CommonAddr, (char *)10331); uD2Common_10383=(ULONG)::GetProcAddress((HMODULE)uD2CommonAddr, (char *)10383); uD2Common_10651=(ULONG)::GetProcAddress((HMODULE)uD2CommonAddr, (char *)10651); uD2Common_11142=(ULONG)::GetProcAddress((HMODULE)uD2CommonAddr, (char *)11142); uD2Common_10014=(ULONG)::GetProcAddress((HMODULE)uD2CommonAddr, (char *)10014); uD2Common_10801=(ULONG)::GetProcAddress((HMODULE)uD2CommonAddr, (char *)10801); uD2Common_10973=(ULONG)::GetProcAddress((HMODULE)uD2CommonAddr, (char *)10973); uD2Common_11017=(ULONG)::GetProcAddress((HMODULE)uD2CommonAddr, (char *)11017); uD2Common_10107=(ULONG)::GetProcAddress((HMODULE)uD2CommonAddr, (char *)10107); uD2Common_10867=(ULONG)::GetProcAddress((HMODULE)uD2CommonAddr, (char *)10867); uD2Common_10750=(ULONG)::GetProcAddress((HMODULE)uD2CommonAddr, (char *)10750); uD2Common_10455 = (ULONG)::GetProcAddress((HMODULE)uD2CommonAddr, (char *)10455);uD2Win_10042=(ULONG)::GetProcAddress((HMODULE)uD2WinAddr, (char *)10042);uD2Client_GetItemName=uD2ClientAddr+0x914F0;uD2Client_GetActorName=uD2ClientAddr+0x11BC14;uD2Client_SendPacketCall=uD2ClientAddr+0x143E0;uD2Win_House=uD2WinAddr+0x214B7;uD2Multi_CreateRoom=uD2Multi+0x39D0B;uD2Multi_JoinRoom = uD2Multi + 0x39F20;uD2Client_OpenBag=uD2ClientAddr+0xFAD84;uD2Client_Depository=uD2ClientAddr+0xFADE4;uD2Client_MouseMoveAddr=uD2ClientAddr+0x2A7C0;uD2Client_BuyItem1=uD2ClientAddr+0x11973B;uD2Client_BuyItem2=uD2ClientAddr+0x11C390;//難度uD2Win_InputRoomName = uD2WinAddr + 0x214B0;uD2Client_RoomName = uD2ClientAddr + 0x119940;uD2Client_SelectTalk=uD2ClientAddr+0x11BC00;uD2Client_SetCursorPos = uD2ClientAddr + 0x1621D;uD2multi_GetCurActorWork = uD2Multi + 0x39F7C; } void GetNpcArr(std::vector<ULONG> &arrNpc) {ULONG uBase=*(ULONG *)( uD2ClientAddr +0x11BBFC);if (IsBadReadPtr((LPVOID)uBase, 0x20) != 0)return;ULONG uBase1=uD2Common_10331;ULONG uBase2=uD2Common_10383;ULONG uBegin=0,uEnd=0;_asm{pushadlea eax,uBeginlea ebx,uEndpush ebxpush eaxpush uBasecall uBase1push eaxcall uBase2popad}ULONG Count=0;if(uEnd>0){do{for (ULONG i = *(ULONG *)(*(ULONG *)(uBegin + 4 * Count) + 0x74); i; i = *(ULONG *)(i + 0xE8) )arrNpc.push_back(i);++Count;}while ( Count < uEnd );} }

三、查缺補漏
1、現(xiàn)在已經(jīng)有NPC遍歷和NPC坐標(biāo)了,還差一個NPC名字,這一點在目前找到的代碼中是沒有反映的。其實這是個小問題,一般來說NPC名字肯定是在每個NPC結(jié)構(gòu)中的,從NPC結(jié)構(gòu)的首地址起,每個有效的地址進去看一下就好了,開始以為是個體力活,沒想到每種不同類型的結(jié)構(gòu)對應(yīng)的名字偏移竟然不一樣,從一個體力活變成了一個大大的體力活。以下是總結(jié)的四種不同結(jié)構(gòu)獲取名方法:

bool GetNpcName(ULONG NpcAddr,char *pNameBuf) {ULONG Addr=NpcAddr;if (IsBadReadPtr((LPVOID)Addr,0x100) != 0)return false;ULONG NpcType=*(ULONG *)Addr;if(NpcType==0){//type==0,玩家,0x14就是名字ansiULONG uNpcNameAddr=*(ULONG *)(Addr+0x14);if(IsBadReadPtr((LPVOID)uNpcNameAddr,0x50)!=0)return false;strncpy(pNameBuf,(char *)uNpcNameAddr,0x50);return true;}else if(NpcType==1){//type==1,npc,怪0x14]+0x2c就是名字unicodeULONG uNpcInfoAddr=*(ULONG *)(Addr+0x14);if(IsBadReadPtr((LPVOID)uNpcInfoAddr,0x100)!=0)return false;ULONG uNpcNameAddr=*(ULONG *)(uNpcInfoAddr+0x2C);if(IsBadReadPtr((LPVOID)uNpcNameAddr,0x100)!=0)return false;WCHAR temp[128]={0}; wcsncpy(temp,(WCHAR *)uNpcNameAddr,128);Unicode2Ansi(temp,pNameBuf);}else if(NpcType==2){//type==2桶之類的ULONG uNpcInfoAddr=*(ULONG *)(Addr+0x14);if(IsBadReadPtr((LPVOID)uNpcInfoAddr,0x10)!=0)return false;ULONG uNpcNameAddr=*(ULONG *)(uNpcInfoAddr);if(IsBadReadPtr((LPVOID)uNpcNameAddr,0x100)!=0)return false;WCHAR temp[128]={0}; wcsncpy(temp,(WCHAR *)(uNpcNameAddr+0x40),128);Unicode2Ansi(temp,pNameBuf);}else if(NpcType==4){//=4是地面上的物品ULONG ItemAddr=Addr;WCHAR wszNameBuf[0x100]={0};_asm{pushadpush 0x100lea edx,wszNameBuf;push edxmov eax,ItemAddrpush eaxcall uD2Client_GetItemNamepopad}Unicode2Ansi(wszNameBuf,pNameBuf);}return true; }

2、另外在上文中已經(jīng)說過取NPC坐標(biāo)是通過D2Common模塊中的第10651號和第11142號函數(shù)取得的,現(xiàn)在寫出取坐標(biāo)函數(shù):

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//原來的做法是從匯編代碼計算的,但后來發(fā)現(xiàn)只要人物一動,所有的相對坐標(biāo)都要重算//所以改成直接用人物坐標(biāo),不用算小地圖popad}//這個取real坐標(biāo)沒有像取小地圖坐標(biāo)一樣x+8,y-8realx=tempX2;realy=tempY2; }

3、由于游戲還有尸爆和尋找物品技能會對怪物尸體進行二次處理,所以遍歷出來的數(shù)據(jù)除了存在的怪外,還會有死去的怪,通過對比找出了一個不夠完美的標(biāo)志位來判斷是否存活。目前玩家類結(jié)構(gòu)還沒有處理,只處理了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就是沒打開,取反對應(yīng)函數(shù)名}return true; }

測試代碼如下:

void testGetNpcInfo() {std::vector<ULONG> arrNpc;GetNpcArr(arrNpc);//0x00標(biāo)志玩家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);}}

測試結(jié)果如下:

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

宇春秋原創(chuàng),轉(zhuǎn)載請注明。

第二篇NPC篇結(jié)束。(第三篇包裹篇待續(xù))

總結(jié)

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

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