- Code: Select all
const DWORD ERROR_ALREADY_EXISTS = 0xB7;
MHS_ADDRESS rtlglw32e = 0;
MHS_ADDRESS createmutexa = 0;
MHS_ADDRESS createmutexw = 0;
DWORD dwBPID = 0;
void On_StartUp(HWND mhswin)
{
//create a new thread instead of doing this in startup
//opening a debugfile during startup caused MHS to freeze every time,
//so this is just a hacky way of getting around that, for now
CloseHandle(CreateThread("WaitForWindow", (DWORD)mhswin));
}
void WaitForWindow(DWORD arg)
{
//grab the argument passed to the thread
HWND mhswin = (HWND)arg;
//MHS pops up on top, so wait for it
while (GetForegroundWindow() != mhswin) Sleep(10);
Sleep(1000);
//open up whatever game/app (can be almost anything as I made this relatively unspecific)
DebugFile("C:\\Program Files\\KRU\\Dark Ages\\Darkages.exe", "", true);
//for some reason, rtlglw32e was sometimes equal to zero,
//so take up to a second to make sure it's not
for (int i = 0; i < 100; i++)
{
rtlglw32e = GetRemoteFuncAddress("ntdll.dll", "RtlGetLastWin32Error");
Sleep(10);
if (rtlglw32e > 0) break;
}
//find both 'CreateMutex'es
createmutexa = GetRemoteFuncAddress("kernel32.dll", "CreateMutexA");
createmutexw = GetRemoteFuncAddress("kernel32.dll", "CreateMutexW");
//this seems to have an error in it, somewhere, as it outputs the following:
//>>RtlGetLastWin32Error: 0x7C910331
//>>CreateMutexA: 0x00000000
//>>CreateMutexW: 0x7C80E93F
//and I know this is wrong because I know that CreateMutexA has a value and is not zero
//MessageBox(MBS_OK, "Startup", "RtlGetLastWin32Error: 0x%08X\nCreateMutexA: 0x%08X\nCreateMutexW: 0x%08X", rtlglw32e, createmutexa, createmutexw);
//set up our breakpoint
SCRIPT_ADD_BP bp = {0};
//we set the breakpoint on RtlGetLastWin32Error+9 because the last error has
//already been passed into EAX at this point
bp.aAddress = rtlglw32e+9;
bp.iType = SPBT_EXECUTE;
bp.iCallback = SYS_FUNCS_SCRIPT_FUNC;
bp.dwNewParms[1] = 1;
bp.pcName = "RtlGetLastWin32Error";
bp.bHardware = true;
bp.bSet = true;
//add the breakpoint
AddBreakpoint(&bp, &dwBPID);
//there was a problem with StopStepping() without this sleep here;
//sometimes it'd stopstepping, sometimes the game would remain paused
Sleep(250);
//if I used 'if (IsStepping()) StopStepping();' without a 'Sleep(250);',
//it'd only unpause the game about 1/3rd of the time, and I couldn't
//figure out why or what was affecting the bug
StopStepping();
}
void On_BP_1(LPVOID lpvAddress, LPPROC_INFO_MHS lpProcInfo)
{
//we only want to do stuff if the last error is 0xB7
if (lpProcInfo->pcContext->Eax == ERROR_ALREADY_EXISTS)
{
bool cmFound = false;
DWORD searchAddress = 0;
DWORD cmSearch = 0;
//we're going to try to search for a CreateMutex call, so gather
//the address of where GetLastError was called out of ESP
ReadProcessMemory(lpProcInfo->hProcess, (void *)lpProcInfo->pcContext->Esp, &searchAddress, 4, NULL);
//let's start searching for the CreateMutex call at searchAddress - 50
searchAddress -= 50;
for (int i = 0; i < 50; i++)
{
//read into cmSearch out of searchAddress
ReadProcessMemory(lpProcInfo->hProcess, (void *)(searchAddress + i), &cmSearch, 4, NULL);
//read cmSearch like a pointer, getting the address of CreateMutex out of the IAT
ReadProcessMemory(lpProcInfo->hProcess, (void *)cmSearch, &cmSearch, 4, NULL);
//if cmSearch is equal to either address, we found it
if (cmSearch == createmutexa || cmSearch == createmutexw)
{
cmFound = true;
break;
}
}
//we go through all the above trouble simply because this is is supposed to be
//specific to CreateMutex and we want to make sure there was a CreateMutex call
//before we change the return value of GetLastError
//GetLastError is called many other times and may have a return value of
//ERROR_ALREADY_EXISTS because of something else, which we don't want to screw with
if (cmFound)
{
lpProcInfo->pcContext->Eax = 0;
lpProcInfo->bSetContext = true;
PrintF("CreateMutex detected! GetLastError patched.");
}
}
}
void On_CloseProcess()
{
//remove the breakpoint when the opened process is closed
RemoveBreakpoint(dwBPID);
}
Seems to work, though you may want to remove the breakpoint after CreateMutex has been patched to avoid possible overhead (depending on how often GetLastError is called); can't remove a breakpoint within a breakpoint, so start up another thread to constantly monitor a boolean variable and remove the breakpoint when true.
Another method would be to breakpoint both CreateMutex calls, then breakpoint RtlGetLastWin32Error inside that CreateMutex breakpoint. That's probably a better method and I really wish I had waited until I woke up to try to tackle this problem because I did everything but comment this last night while I was really tired and now it all seems so much easier. Oh well. Either way, you still need to breakpoint RtlGetLastWin32Error+9 and change the value of EAX, so I suppose it wasn't totally in vain.