mirror of https://github.com/x64dbg/TitanEngine
464 lines
16 KiB
C++
464 lines
16 KiB
C++
#include "stdafx.h"
|
|
#include "definitions.h"
|
|
#include "Global.Debugger.h"
|
|
#include "Global.Breakpoints.h"
|
|
|
|
__declspec(dllexport) bool TITCALL MatchPatternEx(HANDLE hProcess, void* MemoryToCheck, int SizeOfMemoryToCheck, void* PatternToMatch, int SizeOfPatternToMatch, PBYTE WildCard)
|
|
{
|
|
if(!MemoryToCheck || !PatternToMatch || !SizeOfPatternToMatch || !SizeOfMemoryToCheck)
|
|
return false;
|
|
|
|
BYTE intWildCard = 0;
|
|
LPVOID ueReadBuffer = NULL;
|
|
DynBuf ueReadBuf;
|
|
SIZE_T ueNumberOfBytesRead = 0;
|
|
MEMORY_BASIC_INFORMATION memoryInformation = {};
|
|
PMEMORY_COMPARE_HANDLER memCmp = (PMEMORY_COMPARE_HANDLER)MemoryToCheck;
|
|
PMEMORY_COMPARE_HANDLER memPattern = (PMEMORY_COMPARE_HANDLER)PatternToMatch;
|
|
|
|
if(WildCard == NULL)
|
|
{
|
|
WildCard = &intWildCard;
|
|
}
|
|
|
|
if(SizeOfMemoryToCheck >= SizeOfPatternToMatch)
|
|
{
|
|
if(hProcess != GetCurrentProcess())
|
|
{
|
|
ueReadBuffer = ueReadBuf.Allocate(SizeOfMemoryToCheck);
|
|
if(ueReadBuffer && ReadProcessMemory(hProcess, MemoryToCheck, ueReadBuffer, SizeOfMemoryToCheck, &ueNumberOfBytesRead))
|
|
{
|
|
if(ueNumberOfBytesRead == 0)
|
|
{
|
|
if(VirtualQueryEx(hProcess, MemoryToCheck, &memoryInformation, sizeof(memoryInformation)) != NULL)
|
|
{
|
|
SizeOfMemoryToCheck = (int)((ULONG_PTR)memoryInformation.BaseAddress + memoryInformation.RegionSize - (ULONG_PTR)MemoryToCheck);
|
|
if(!ReadProcessMemory(hProcess, MemoryToCheck, ueReadBuffer, SizeOfMemoryToCheck, &ueNumberOfBytesRead))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
memCmp = (PMEMORY_COMPARE_HANDLER)ueReadBuffer;
|
|
}
|
|
}
|
|
|
|
if(memCmp)
|
|
{
|
|
for(int i = 0; i < SizeOfMemoryToCheck && i < SizeOfPatternToMatch; i++)
|
|
{
|
|
if(memCmp->Array.bArrayEntry[i] != memPattern->Array.bArrayEntry[i] && memPattern->Array.bArrayEntry[i] != *WildCard)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
__declspec(dllexport) bool TITCALL MatchPattern(void* MemoryToCheck, int SizeOfMemoryToCheck, void* PatternToMatch, int SizeOfPatternToMatch, PBYTE WildCard)
|
|
{
|
|
|
|
if(dbgProcessInformation.hProcess != NULL)
|
|
{
|
|
return(MatchPatternEx(dbgProcessInformation.hProcess, MemoryToCheck, SizeOfMemoryToCheck, PatternToMatch, SizeOfPatternToMatch, WildCard));
|
|
}
|
|
else
|
|
{
|
|
return(MatchPatternEx(GetCurrentProcess(), MemoryToCheck, SizeOfMemoryToCheck, PatternToMatch, SizeOfPatternToMatch, WildCard));
|
|
}
|
|
}
|
|
|
|
__declspec(dllexport) ULONG_PTR TITCALL FindEx(HANDLE hProcess, LPVOID MemoryStart, DWORD MemorySize, LPVOID SearchPattern, DWORD PatternSize, LPBYTE WildCard)
|
|
{
|
|
if(!hProcess || !MemoryStart || !MemorySize || !SearchPattern || !PatternSize)
|
|
return 0;
|
|
|
|
ULONG_PTR Return = NULL;
|
|
LPVOID ueReadBuffer = NULL;
|
|
DynBuf ueReadBuf;
|
|
PUCHAR SearchBuffer = NULL;
|
|
PUCHAR CompareBuffer = NULL;
|
|
MEMORY_BASIC_INFORMATION memoryInformation = {};
|
|
ULONG_PTR ueNumberOfBytesRead = NULL;
|
|
LPVOID currentSearchPosition = NULL;
|
|
DWORD currentSizeOfSearch = NULL;
|
|
BYTE nWildCard = NULL;
|
|
|
|
if(WildCard == NULL)
|
|
{
|
|
WildCard = &nWildCard;
|
|
}
|
|
|
|
if(hProcess != GetCurrentProcess())
|
|
{
|
|
ueReadBuffer = ueReadBuf.Allocate(MemorySize);
|
|
if(ueReadBuffer && !MemoryReadSafe(hProcess, MemoryStart, ueReadBuffer, MemorySize, &ueNumberOfBytesRead))
|
|
{
|
|
if(ueNumberOfBytesRead == NULL)
|
|
{
|
|
if(VirtualQueryEx(hProcess, MemoryStart, &memoryInformation, sizeof(memoryInformation)) != NULL)
|
|
{
|
|
MemorySize = (DWORD)((ULONG_PTR)memoryInformation.BaseAddress + memoryInformation.RegionSize - (ULONG_PTR)MemoryStart);
|
|
if(!MemoryReadSafe(hProcess, MemoryStart, ueReadBuffer, MemorySize, &ueNumberOfBytesRead))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
SearchBuffer = (PUCHAR)ueReadBuffer;
|
|
}
|
|
else
|
|
{
|
|
SearchBuffer = (PUCHAR)MemoryStart;
|
|
}
|
|
|
|
CompareBuffer = (PUCHAR)SearchPattern;
|
|
|
|
DWORD i, j;
|
|
for(i = 0; i < MemorySize && Return == NULL; i++)
|
|
{
|
|
for(j = 0; j < PatternSize; j++)
|
|
{
|
|
if(CompareBuffer[j] != *(PUCHAR)WildCard && SearchBuffer[i + j] != CompareBuffer[j])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if(j == PatternSize)
|
|
{
|
|
Return = (ULONG_PTR)MemoryStart + i;
|
|
}
|
|
}
|
|
|
|
return Return;
|
|
}
|
|
|
|
extern "C" __declspec(dllexport) ULONG_PTR TITCALL Find(LPVOID MemoryStart, DWORD MemorySize, LPVOID SearchPattern, DWORD PatternSize, LPBYTE WildCard)
|
|
{
|
|
|
|
if(dbgProcessInformation.hProcess != NULL)
|
|
{
|
|
return(FindEx(dbgProcessInformation.hProcess, MemoryStart, MemorySize, SearchPattern, PatternSize, WildCard));
|
|
}
|
|
else
|
|
{
|
|
return(FindEx(GetCurrentProcess(), MemoryStart, MemorySize, SearchPattern, PatternSize, WildCard));
|
|
}
|
|
}
|
|
|
|
__declspec(dllexport) bool TITCALL FillEx(HANDLE hProcess, LPVOID MemoryStart, DWORD MemorySize, PBYTE FillByte)
|
|
{
|
|
|
|
unsigned int i;
|
|
MEMORY_BASIC_INFORMATION MemInfo;
|
|
ULONG_PTR ueNumberOfBytesRead;
|
|
BYTE defFillByte = 0x90;
|
|
DWORD OldProtect;
|
|
|
|
if(hProcess != NULL)
|
|
{
|
|
if(FillByte == NULL)
|
|
{
|
|
FillByte = &defFillByte;
|
|
}
|
|
VirtualQueryEx(hProcess, MemoryStart, &MemInfo, sizeof(MEMORY_BASIC_INFORMATION));
|
|
OldProtect = MemInfo.Protect;
|
|
VirtualProtectEx(hProcess, MemoryStart, MemorySize, PAGE_EXECUTE_READWRITE, &OldProtect);
|
|
for(i = 0; i < MemorySize; i++)
|
|
{
|
|
WriteProcessMemory(hProcess, MemoryStart, FillByte, 1, &ueNumberOfBytesRead);
|
|
MemoryStart = (LPVOID)((ULONG_PTR)MemoryStart + 1);
|
|
}
|
|
VirtualProtectEx(hProcess, MemoryStart, MemorySize, OldProtect, &OldProtect);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
__declspec(dllexport) bool TITCALL Fill(LPVOID MemoryStart, DWORD MemorySize, PBYTE FillByte)
|
|
{
|
|
|
|
if(dbgProcessInformation.hProcess != NULL)
|
|
{
|
|
return(FillEx(dbgProcessInformation.hProcess, MemoryStart, MemorySize, FillByte));
|
|
}
|
|
else
|
|
{
|
|
return(FillEx(GetCurrentProcess(), MemoryStart, MemorySize, FillByte));
|
|
}
|
|
}
|
|
|
|
__declspec(dllexport) bool TITCALL PatchEx(HANDLE hProcess, LPVOID MemoryStart, DWORD MemorySize, LPVOID ReplacePattern, DWORD ReplaceSize, bool AppendNOP, bool PrependNOP)
|
|
{
|
|
|
|
unsigned int i, recalcSize;
|
|
LPVOID lpMemoryStart = MemoryStart;
|
|
MEMORY_BASIC_INFORMATION MemInfo;
|
|
ULONG_PTR ueNumberOfBytesRead;
|
|
BYTE FillByte = 0x90;
|
|
DWORD OldProtect;
|
|
|
|
if(hProcess != NULL)
|
|
{
|
|
VirtualQueryEx(hProcess, MemoryStart, &MemInfo, sizeof(MEMORY_BASIC_INFORMATION));
|
|
OldProtect = MemInfo.Protect;
|
|
VirtualProtectEx(hProcess, MemoryStart, MemorySize, PAGE_EXECUTE_READWRITE, &OldProtect);
|
|
|
|
if(MemorySize - ReplaceSize != NULL)
|
|
{
|
|
recalcSize = abs((long)(MemorySize - ReplaceSize));
|
|
if(AppendNOP)
|
|
{
|
|
WriteProcessMemory(hProcess, MemoryStart, ReplacePattern, ReplaceSize, &ueNumberOfBytesRead);
|
|
lpMemoryStart = (LPVOID)((ULONG_PTR)MemoryStart + ReplaceSize);
|
|
for(i = 0; i < recalcSize; i++)
|
|
{
|
|
WriteProcessMemory(hProcess, lpMemoryStart, &FillByte, 1, &ueNumberOfBytesRead);
|
|
lpMemoryStart = (LPVOID)((ULONG_PTR)lpMemoryStart + 1);
|
|
}
|
|
}
|
|
else if(PrependNOP)
|
|
{
|
|
lpMemoryStart = MemoryStart;
|
|
for(i = 0; i < recalcSize; i++)
|
|
{
|
|
WriteProcessMemory(hProcess, lpMemoryStart, &FillByte, 1, &ueNumberOfBytesRead);
|
|
lpMemoryStart = (LPVOID)((ULONG_PTR)lpMemoryStart + 1);
|
|
}
|
|
WriteProcessMemory(hProcess, lpMemoryStart, ReplacePattern, ReplaceSize, &ueNumberOfBytesRead);
|
|
}
|
|
else
|
|
{
|
|
WriteProcessMemory(hProcess, MemoryStart, ReplacePattern, ReplaceSize, &ueNumberOfBytesRead);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WriteProcessMemory(hProcess, MemoryStart, ReplacePattern, ReplaceSize, &ueNumberOfBytesRead);
|
|
}
|
|
VirtualProtectEx(hProcess, MemoryStart, MemorySize, OldProtect, &OldProtect);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
__declspec(dllexport) bool TITCALL Patch(LPVOID MemoryStart, DWORD MemorySize, LPVOID ReplacePattern, DWORD ReplaceSize, bool AppendNOP, bool PrependNOP)
|
|
{
|
|
|
|
if(dbgProcessInformation.hProcess != NULL)
|
|
{
|
|
return(PatchEx(dbgProcessInformation.hProcess, MemoryStart, MemorySize, ReplacePattern, ReplaceSize, AppendNOP, PrependNOP));
|
|
}
|
|
else
|
|
{
|
|
return(PatchEx(GetCurrentProcess(), MemoryStart, MemorySize, ReplacePattern, ReplaceSize, AppendNOP, PrependNOP));
|
|
}
|
|
}
|
|
|
|
__declspec(dllexport) bool TITCALL ReplaceEx(HANDLE hProcess, LPVOID MemoryStart, DWORD MemorySize, LPVOID SearchPattern, DWORD PatternSize, DWORD NumberOfRepetitions, LPVOID ReplacePattern, DWORD ReplaceSize, PBYTE WildCard)
|
|
{
|
|
|
|
unsigned int i;
|
|
ULONG_PTR ueNumberOfBytesRead;
|
|
ULONG_PTR CurrentFoundPattern;
|
|
LPVOID cMemoryStart = MemoryStart;
|
|
DWORD cMemorySize = MemorySize;
|
|
DynBuf lpReadMem;
|
|
LPVOID lpReadMemory = lpReadMem.Allocate(PatternSize);
|
|
|
|
CurrentFoundPattern = (ULONG_PTR)FindEx(hProcess, cMemoryStart, cMemorySize, SearchPattern, PatternSize, WildCard);
|
|
NumberOfRepetitions--;
|
|
while(CurrentFoundPattern != NULL && NumberOfRepetitions != NULL)
|
|
{
|
|
if(ReadProcessMemory(hProcess, (LPVOID)CurrentFoundPattern, lpReadMemory, PatternSize, &ueNumberOfBytesRead))
|
|
{
|
|
for(i = 0; i < ReplaceSize; i++)
|
|
{
|
|
if(memcmp((LPVOID)((ULONG_PTR)ReplacePattern + i), WildCard, 1) != NULL)
|
|
{
|
|
RtlMoveMemory((LPVOID)((ULONG_PTR)lpReadMemory + i), (LPVOID)((ULONG_PTR)ReplacePattern + i), 1);
|
|
}
|
|
}
|
|
PatchEx(hProcess, (LPVOID)CurrentFoundPattern, PatternSize, lpReadMemory, ReplaceSize, true, false);
|
|
}
|
|
cMemoryStart = (LPVOID)(CurrentFoundPattern + PatternSize);
|
|
cMemorySize = (DWORD)((ULONG_PTR)MemoryStart + MemorySize - CurrentFoundPattern);
|
|
CurrentFoundPattern = (ULONG_PTR)FindEx(hProcess, cMemoryStart, cMemorySize, SearchPattern, PatternSize, WildCard);
|
|
NumberOfRepetitions--;
|
|
}
|
|
if(NumberOfRepetitions != NULL)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
__declspec(dllexport) bool TITCALL Replace(LPVOID MemoryStart, DWORD MemorySize, LPVOID SearchPattern, DWORD PatternSize, DWORD NumberOfRepetitions, LPVOID ReplacePattern, DWORD ReplaceSize, PBYTE WildCard)
|
|
{
|
|
|
|
if(dbgProcessInformation.hProcess != NULL)
|
|
{
|
|
return(ReplaceEx(dbgProcessInformation.hProcess, MemoryStart, MemorySize, SearchPattern, PatternSize, NumberOfRepetitions, ReplacePattern, ReplaceSize, WildCard));
|
|
}
|
|
else
|
|
{
|
|
return(ReplaceEx(GetCurrentProcess(), MemoryStart, MemorySize, SearchPattern, PatternSize, NumberOfRepetitions, ReplacePattern, ReplaceSize, WildCard));
|
|
}
|
|
}
|
|
|
|
//what should this function do:
|
|
//- do all possible effort to read memory
|
|
//- filter out breakpoints
|
|
__declspec(dllexport) bool TITCALL MemoryReadSafe(HANDLE hProcess, LPVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesRead)
|
|
{
|
|
SIZE_T ueNumberOfBytesRead = 0;
|
|
SIZE_T* pNumBytes = 0;
|
|
DWORD dwProtect = 0;
|
|
bool retValue = false;
|
|
|
|
//read memory
|
|
if((hProcess == 0) || (lpBaseAddress == 0) || (lpBuffer == 0) || (nSize == 0))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(!lpNumberOfBytesRead)
|
|
{
|
|
pNumBytes = &ueNumberOfBytesRead;
|
|
}
|
|
else
|
|
{
|
|
pNumBytes = lpNumberOfBytesRead;
|
|
}
|
|
|
|
if(!ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, pNumBytes))
|
|
{
|
|
CriticalSectionLocker memProtectLock(LockMemoryProtection);
|
|
|
|
// try to temporarily change the page protections to PAGE_EXECUTE_READ
|
|
std::vector<MEMORY_BASIC_INFORMATION> memRegions;
|
|
MEMORY_BASIC_INFORMATION memInfo;
|
|
ULONG_PTR endAddr = (ULONG_PTR)lpBaseAddress + nSize;
|
|
for(ULONG_PTR page = ALIGN_DOWN_BY(lpBaseAddress, TITANENGINE_PAGESIZE); page < endAddr; page += memInfo.RegionSize)
|
|
{
|
|
if(0 == VirtualQueryEx(hProcess, (LPCVOID)page, &memInfo, sizeof(memInfo)))
|
|
break; // failure ('VirtualProtectEx' will fail too)
|
|
memRegions.push_back(memInfo);
|
|
}
|
|
|
|
if(VirtualProtectEx(hProcess, lpBaseAddress, nSize, PAGE_EXECUTE_READ, &dwProtect))
|
|
{
|
|
if(ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, pNumBytes))
|
|
{
|
|
retValue = true;
|
|
}
|
|
|
|
for(const auto & info : memRegions)
|
|
{
|
|
ULONG_PTR size = info.RegionSize;
|
|
if(endAddr < (ULONG_PTR)info.BaseAddress + info.RegionSize)
|
|
size = endAddr - (ULONG_PTR)info.BaseAddress;
|
|
VirtualProtectEx(hProcess, info.BaseAddress, size, info.Protect, &dwProtect);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
retValue = true;
|
|
}
|
|
|
|
//filter breakpoints
|
|
if(retValue)
|
|
BreakPointPostReadFilter((ULONG_PTR)lpBaseAddress, (unsigned char*)lpBuffer, nSize);
|
|
|
|
return retValue;
|
|
}
|
|
|
|
//what should this function do:
|
|
//- do all possible effort to write memory
|
|
//- re-set breakpoints when overwritten
|
|
__declspec(dllexport) bool TITCALL MemoryWriteSafe(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesWritten)
|
|
{
|
|
SIZE_T ueNumberOfBytesWritten = 0;
|
|
SIZE_T* pNumBytes = 0;
|
|
DWORD dwProtect = 0;
|
|
bool retValue = false;
|
|
|
|
//read memory
|
|
if((hProcess == 0) || (lpBaseAddress == 0) || (lpBuffer == 0) || (nSize == 0))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CriticalSectionLocker lock(LockBreakPointBuffer); //thread-safe
|
|
//disable breakpoints that interfere with the memory to write
|
|
BreakPointPreWriteFilter((ULONG_PTR)lpBaseAddress, nSize);
|
|
|
|
if(!lpNumberOfBytesWritten)
|
|
{
|
|
pNumBytes = &ueNumberOfBytesWritten;
|
|
}
|
|
else
|
|
{
|
|
pNumBytes = lpNumberOfBytesWritten;
|
|
}
|
|
|
|
if(!WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, pNumBytes) || *pNumBytes < nSize)
|
|
{
|
|
CriticalSectionLocker memProtectLock(LockMemoryProtection);
|
|
|
|
// try to temporarily change the page protections to PAGE_EXECUTE_READWRITE
|
|
std::vector<MEMORY_BASIC_INFORMATION> memRegions;
|
|
MEMORY_BASIC_INFORMATION memInfo;
|
|
ULONG_PTR endAddr = (ULONG_PTR)lpBaseAddress + nSize;
|
|
for(ULONG_PTR page = ALIGN_DOWN_BY(lpBaseAddress, TITANENGINE_PAGESIZE); page < endAddr; page += memInfo.RegionSize)
|
|
{
|
|
if(0 == VirtualQueryEx(hProcess, (LPCVOID)page, &memInfo, sizeof(memInfo)))
|
|
break; // failure
|
|
memRegions.push_back(memInfo);
|
|
}
|
|
|
|
if(VirtualProtectEx(hProcess, lpBaseAddress, nSize, PAGE_EXECUTE_READWRITE, &dwProtect))
|
|
{
|
|
if(WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, pNumBytes))
|
|
{
|
|
retValue = true;
|
|
}
|
|
|
|
for(const auto & info : memRegions)
|
|
{
|
|
ULONG_PTR size = info.RegionSize;
|
|
if(endAddr < (ULONG_PTR)info.BaseAddress + info.RegionSize)
|
|
size = endAddr - (ULONG_PTR)info.BaseAddress;
|
|
VirtualProtectEx(hProcess, info.BaseAddress, size, info.Protect, &dwProtect);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
retValue = true;
|
|
}
|
|
|
|
//re-enable breakpoints that interfere with the memory to write
|
|
BreakPointPostWriteFilter((ULONG_PTR)lpBaseAddress, nSize);
|
|
|
|
return retValue;
|
|
}
|