//Mousepad's Diablo II Maphack //cut down version for diablo2 v1.08 //freeware, to be redistributed free and unaltered //© 2001 mousepad - mousepad@btinternet.com //NOTE :- "Link incremently" must be disabled #include #include HANDLE GetProcessHandle(char *szClass, char *szTitle) { HWND hwnd = FindWindow(szClass, szTitle); DWORD pid = 0; GetWindowThreadProcessId(hwnd, &pid); return OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); } BOOL VirtualProtectEx(HANDLE hProcess, LPVOID lpAddress, DWORD dwSize, DWORD flNewProtect) { DWORD dummy; return VirtualProtectEx(hProcess, lpAddress, dwSize, flNewProtect, &dummy); } void WriteProcessBYTES(HANDLE hProcess, DWORD lpAddress, void *buf, int len) { DWORD oldprot = 0; VirtualProtectEx(hProcess, (void *)lpAddress, len, PAGE_READWRITE, &oldprot); WriteProcessMemory(hProcess, (void *)lpAddress, buf, len, 0); VirtualProtectEx(hProcess, (void *)lpAddress, len, oldprot); } void FillProcessBYTES(HANDLE hProcess, DWORD lpAddress, BYTE ch, int len) { BYTE *fillbuf = new BYTE[len]; memset(fillbuf, ch, len); WriteProcessBYTES(hProcess, lpAddress, fillbuf, len); delete fillbuf; } void InterceptCode(HANDLE diablo2_ph, int inst, DWORD pOldCode, DWORD pNewCode, int nBytes) { BYTE *buf1 = new BYTE[nBytes]; buf1[0] = inst; *(DWORD *)(buf1+1) = pNewCode-(pOldCode+5); memset(buf1+5, 0x90, nBytes-5); //nops WriteProcessBYTES(diablo2_ph, pOldCode, buf1, nBytes); delete buf1; } #define INST_NOP 0x90 #define INST_CALL 0xe8 #define INST_JMP 0xe9 #define INST_RET 0xc3 #define DB(x) __asm _emit x #define FUNCTLEN(func) ((DWORD)func##END-(DWORD)func) #define ADDR_MAPHACK 0x00400400 void __declspec(naked) AutomapHack_ASM() { __asm { jmp keydown //+0 jmp drawmap2 //+2 DB(0) //+4, draw flag DB(1) DB(40) DB(75) DB(103) DB(109) DB(133) //+5, act levels DB(0) DB(0) DB(0) DB(0) DB(0) DB(0) //+11, fake cmd buffer keydown: //esi = virtual key code cmp esi,'0' jne notkey0 mov eax,ADDR_MAPHACK+4 //draw flag mov byte ptr [eax],1 notkey0: //original code mov ecx,0x6FBEB198 ret drawmap2: mov eax,ADDR_MAPHACK+4 //draw flag cmp byte ptr [eax],0 je nodrawmap mov byte ptr [eax],0 pushad call drawmap popad nodrawmap: //original code mov eax,0x6FB5D5F0 jmp eax drawmap: call getactlevels levelloop: pushad call getptrlevel call setuplevel call setupmaplayer call drawlevel popad inc eax cmp eax,ebx jb levelloop //restore previous automap layer call getptrcurrlevel call setupmaplayer ret getactlevels: //get range of levels to draw //returns eax = first level, ebx = last level+1 call getptrcurrlevel mov eax,[eax+0x04] //ptDrlgLevel->level no mov edi,ADDR_MAPHACK+5+1 //act levels table getactloop: scasb jnb getactloop movzx eax,byte ptr [edi-2] movzx ebx,byte ptr [edi-1] ret setuplevel: //generate level if not ready //eax = level no cmp dword ptr [eax+0x30],0 //ptDrlgLevel->ptRoomFirst (room2) jne levelready pushad push eax mov eax,0x6FD85BE0 //setup level (d2common 2716) call eax popad levelready: ret getptrcurrlevel: //get ptr to level player currently in mov eax,0x6FB5E250 //get room1 of player (d2client) call eax mov eax,[eax+0x38] //room1->room2 mov eax,[eax] //room2->ptDrlglevel ret getptrlevel: //get ptr to level //eax = level no push eax //level no mov eax,0x6FBDEDF4 //ptAct (d2client) mov eax,[eax] mov eax,[eax+0x08] //ptAct->ptDrlg push eax //ptDrlg mov eax,0x6FD85A60 //get ptDrlgLevel (d2common 2715) call eax ret setupmaplayer: //prepare automap layer needed for level //no seperate function avaiable so patchs on demand //eax = ptDrlgLevel //returns edi = automap layer struct push eax mov ecx,[eax+0x04] //ptDrlgLevel->level no mov eax,0x6FAF561F //end of setup automap layer (d2client) mov byte ptr [eax],INST_RET push eax xor ebp,ebp mov eax,0x6FAF5554 //setup automap layer (d2client) call eax pop eax mov byte ptr [eax],0x8b pop eax ret drawlevel: //draw an entire level //eax = ptDrlgLevel //edi = automap layer struct mov esi,[eax+0x30] //ptDrlgLevel->ptRoomFirst (room2) roomloop: test esi,esi je leveldone pushad call drawroom popad mov esi,[esi+0xe8] //room2->next room2 jmp roomloop leveldone: ret drawroom: //draw a room, prepares and discards if not ready //esi = room2 //edi = automap layer struct cmp dword ptr [esi+0xe4],0 //room2->room1 jne drawroom2 mov ecx,ADDR_MAPHACK+11 //fake cmd buffer mov eax,[esi+0x04] //room2->x pos mov [ecx+1],ax mov eax,[esi+0x08] //room2->y pos mov [ecx+3],ax mov eax,[esi] //room2->ptDrlglevel mov eax,[eax+0x04] //ptDrlglevel->level no mov [ecx+5],al pushad //prepare room using cmd 07 mov eax,0x6FAE1B50 //recv cmd 07 (d2client) call eax popad pushad call drawroom2 popad //discard room using cmd 08, works without but memory greedy mov eax,0x6FAE1BC0 //recv cmd 08 (d2client) call eax ret drawroom2: //draw a room, must allready be prepared //esi = room2 //edi = automap layer struct mov ecx,[esi+0xe4] //room2->room1 push edi mov edx,1 //clip off flag mov eax,0x6FAF6B40 //draw 1 room of automap (d2client) call eax ret } } void __declspec(naked) AutomapHack_ASMEND() {} void __cdecl main() { HANDLE diablo2_ph = GetProcessHandle("Diablo II", "Diablo II"); if (diablo2_ph == 0) { printf("cant get d2 process handle\n"); return; } FillProcessBYTES(diablo2_ph, 0x6FAF6B76, INST_NOP, 2); //floors inside FillProcessBYTES(diablo2_ph, 0x6FAF6BCA, INST_NOP, 2); //walls inside FillProcessBYTES(diablo2_ph, 0x6FAF6C30, INST_NOP, 6); //shrine distance VirtualProtectEx(diablo2_ph, (void *)0x00400400, 0xc00, PAGE_EXECUTE_READWRITE); WriteProcessBYTES(diablo2_ph, ADDR_MAPHACK, AutomapHack_ASM, FUNCTLEN(AutomapHack_ASM)); InterceptCode(diablo2_ph, INST_CALL, 0x6FAFEAD9, ADDR_MAPHACK+0, 5); //keydown intercept InterceptCode(diablo2_ph, INST_CALL, 0x6FAF54A0, ADDR_MAPHACK+2, 5); //drawmap intercept VirtualProtectEx(diablo2_ph, (void *)0x6FAF561F, 1, PAGE_EXECUTE_READWRITE); CloseHandle(diablo2_ph); }