Code: Select all
int __stdcall DllAttach()
{
D2TEMPLATE_GetDebugPrivilege();
void* hGame = OpenProcess(PROCESS_ALL_ACCESS, 0, GetCurrentProcessId());
if (!hGame) return 0;
if (!D2TEMPLATE_LoadModules())
{
CloseHandle(hGame);
return 0;
}
D2TEMPLATE_ApplyPatch(hGame, gptTemplatePatches); // right here
CloseHandle(hGame);
return 1;
}
Code: Select all
if (!D2TEMPLATE_ApplyPatch(hGame, gptTemplatePatches))
{
CloseHandle(hGame);
return 0;
};
Code: Select all
{D2DLL_D2CLIENT, 0x9A6F0, 0x05, (DWORD)0x8B4424CC, FALSE}
Code: Select all
6FB4A6F0 8B4424 CC MOV EAX,DWORD PTR SS:[ESP-34]
6FB4A6F4 8B4424 CC MOV EAX,DWORD PTR SS:[ESP-34]
6FB4A6F8 8B4424 CC MOV EAX,DWORD PTR SS:[ESP-34]
6FB4A6FC 8B4424 CC MOV EAX,DWORD PTR SS:[ESP-34]
6FB4A700 8B4424 CC MOV EAX,DWORD PTR SS:[ESP-34]
Code: Select all
6FB4A6F0 CC INT3
6FB4A6F1 CC INT3
6FB4A6F2 CC INT3
6FB4A6F3 CC INT3
6FB4A6F4 CC INT3
Code: Select all
{D2DLL_D2CLIENT, 0x9A6F1, 0x34, (BYTE)PATCH_NOPBLOCK, FALSE}
Food wrote:I'm still looking into ASM hoping I'll be able to grasp enough of the thing to be useful here. Anyway I noticed a probable bug in the template.
In DLLmain.cpp D2TEMPLATE_ApplyPatch returns TRUE for success and FALSE for failure, but when the function is called inside D2TEMPLATE_DllAttach the return value is not checked.Code: Select all
int __stdcall DllAttach()
{
D2TEMPLATE_GetDebugPrivilege();
void* hGame = OpenProcess(PROCESS_ALL_ACCESS, 0, GetCurrentProcessId());
if (!hGame) return 0;
if (!D2TEMPLATE_LoadModules())
{
CloseHandle(hGame);
return 0;
}
D2TEMPLATE_ApplyPatch(hGame, gptTemplatePatches); // right here
CloseHandle(hGame);
return 1;
}
I suppose the code should beCode: Select all
if (!D2TEMPLATE_ApplyPatch(hGame, gptTemplatePatches))
{
CloseHandle(hGame);
return 0;
};
Of course an or in the previous if would suffice, but long lines are less readable and split lines aren't as nice to look at.
I also have a question. How do I find stuff in the debugger? I mean, let's say I want to change the number of items generated in the gamble screen (first random thing I thought), how can I find where that code is executed? And more importantly, how would I recognize the relative code once I see it?
I'm not asking for a full course / tutorial, but a few pointers would be nice. And yes, I read another tutorial that explained how to make the game check for every item gamble cost in the .txt (as opposed to only rings and amulets) but it started with "let's go to this address of this specific dll because here is where the code is". That's cool, but supposing I didn't know where the code was, how would I find it?
Cheers.
EDIT: I realized that when a block patch is specified (so when size > 0) only the least significant byte of the DWORD (or whatever the patch size is) will be applied. Thus if I have this patchCode: Select all
{D2DLL_D2CLIENT, 0x9A6F0, 0x05, (DWORD)0x8B4424CC, FALSE}
The resulting patch will NOT beCode: Select all
6FB4A6F0 8B4424 CC MOV EAX,DWORD PTR SS:[ESP-34]
6FB4A6F4 8B4424 CC MOV EAX,DWORD PTR SS:[ESP-34]
6FB4A6F8 8B4424 CC MOV EAX,DWORD PTR SS:[ESP-34]
6FB4A6FC 8B4424 CC MOV EAX,DWORD PTR SS:[ESP-34]
6FB4A700 8B4424 CC MOV EAX,DWORD PTR SS:[ESP-34]
but rather:Code: Select all
6FB4A6F0 CC INT3
6FB4A6F1 CC INT3
6FB4A6F2 CC INT3
6FB4A6F3 CC INT3
6FB4A6F4 CC INT3
I guess this makes sense since I don't foresee the need to replicate the same exact WORD-sized command over and over, so I believe this is by design? Well, anyway it's documented now.
As an aside, this means that block patches would IMHO be more readable if they were written likeCode: Select all
{D2DLL_D2CLIENT, 0x9A6F1, 0x34, (BYTE)PATCH_NOPBLOCK, FALSE}
so the fact they can only be byte-sized would be rather obvious.
Code: Select all
#define name 0xCC24448B
Code: Select all
{D2DLL_D2CLIENT, 0x9A6F0, 0x05, (DWORD)name, FALSE}
Food wrote:I'm still looking into ASM hoping I'll be able to grasp enough of the thing to be useful here. Anyway I noticed a probable bug in the template.
In DLLmain.cpp D2TEMPLATE_ApplyPatch returns TRUE for success and FALSE for failure, but when the function is called inside D2TEMPLATE_DllAttach the return value is not checked.
Code: Select all
int __stdcall DllAttach()
{
D2TEMPLATE_GetDebugPrivilege();
void* hGame = OpenProcess(PROCESS_ALL_ACCESS, 0, GetCurrentProcessId());
if (!hGame) return 0;
if (!D2TEMPLATE_LoadModules())
{
CloseHandle(hGame);
return 0;
}
#define __SERVER_BUILD__
D2TEMPLATE_ApplyPatch(hGame, gptD2ServerPatches);
#endif
D2TEMPLATE_ApplyPatch(hGame, gptD2GamePatches);
D2TEMPLATE_ApplyPatch(hGame, gptD2ClientPatches);
D2TEMPLATE_ApplyPatch(hGame, gptD2CommonPatches);
D2TEMPLATE_ApplyPatch(hGame, gptD2GfxPatches);
D2TEMPLATE_ApplyPatch(hGame, gptD2WinPatches);
D2TEMPLATE_ApplyPatch(hGame, gptD2LangPatches);
D2TEMPLATE_ApplyPatch(hGame, gptD2FogPatches);
D2TEMPLATE_ApplyPatch(hGame, gptD2NetPatches);
#ifdef __CLIENT_BUILD__
if (!CONFIG_AllocConfigData()) return 0;
D2TEMPLATE_ApplyPatch(hGame, gptD2LaunchPatches);
D2TEMPLATE_ApplyPatch(hGame, gptD2GdiPatches);
D2TEMPLATE_ApplyPatch(hGame, gptD2GlidePatches);
D2TEMPLATE_ApplyPatch(hGame, gptD2Direct3DPatches);
D2TEMPLATE_ApplyPatch(hGame, gptD2SmackW32Patches);
D2TEMPLATE_ApplyPatch(hGame, gptD2BnClientPatches);
#endif
THREADS_Create();
CloseHandle(hGame);
return 1;
}
Food wrote:I also have a question. How do I find stuff in the debugger? I mean, let's say I want to change the number of items generated in the gamble screen (first random thing I thought), how can I find where that code is executed? And more importantly, how would I recognize the relative code once I see it?
I'm not asking for a full course / tutorial, but a few pointers would be nice. And yes, I read another tutorial that explained how to make the game check for every item gamble cost in the .txt (as opposed to only rings and amulets) but it started with "let's go to this address of this specific dll because here is where the code is". That's cool, but supposing I didn't know where the code was, how would I find it?
Food wrote:EDIT: I realized that when a block patch is specified (so when size > 0) only the least significant byte of the DWORD (or whatever the patch size is) will be applied. Thus if I have this patch
Code: Select all
{D2DLL_D2COMMON, 0x602B6, 0x26, (DWORD)PATCH_NOPBLOCK, FALSE}, //states records limit
Code: Select all
if (hPatch->nPatchSize > 0)
{
BYTE Buffer[8192];
for (size_t i = 0; i < hPatch->nPatchSize; i++)
{
Buffer[i] = (BYTE)dwData;
}
VirtualProtect(hAddress, hPatch->nPatchSize, PAGE_EXECUTE_READWRITE, &dwOldPage);
nReturn = WriteProcessMemory(hGame, hAddress, &Buffer, hPatch->nPatchSize, 0);
VirtualProtect(hAddress, hPatch->nPatchSize, dwOldPage, 0);
}