DBG+GUI: option to query the working set before attempting to read a memory page
workaround for http://www.triplefault.io/2017/08/detecting-debuggers-by-abusing-bad.html
This commit is contained in:
parent
4104c0a004
commit
037504643b
|
@ -982,6 +982,7 @@ extern "C" DLL_EXPORT duint _dbg_sendmessage(DBGMSG type, void* param1, void* pa
|
|||
bNoForegroundWindow = settingboolget("Gui", "NoForegroundWindow");
|
||||
bVerboseExceptionLogging = settingboolget("Engine", "VerboseExceptionLogging");
|
||||
bNoWow64SingleStepWorkaround = settingboolget("Engine", "NoWow64SingleStepWorkaround");
|
||||
bQueryWorkingSet = settingboolget("Misc", "QueryWorkingSet");
|
||||
stackupdatesettings();
|
||||
|
||||
duint setting;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
static ULONG fallbackCookie = 0;
|
||||
std::map<Range, MEMPAGE, RangeCompare> memoryPages;
|
||||
bool bListAllPages = false;
|
||||
bool bQueryWorkingSet = false;
|
||||
|
||||
void MemUpdateMap()
|
||||
{
|
||||
|
@ -285,12 +286,36 @@ duint MemFindBaseAddr(duint Address, duint* Size, bool Refresh, bool FindReserve
|
|||
return found->first.first;
|
||||
}
|
||||
|
||||
static bool MemoryReadSafePage(HANDLE hProcess, LPVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesRead)
|
||||
//http://www.triplefault.io/2017/08/detecting-debuggers-by-abusing-bad.html
|
||||
//TODO: name this function properly
|
||||
static bool IgnoreThisRead(HANDLE hProcess, LPVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesRead)
|
||||
{
|
||||
if(!bQueryWorkingSet)
|
||||
return false;
|
||||
PSAPI_WORKING_SET_EX_INFORMATION wsi;
|
||||
wsi.VirtualAddress = (PVOID)PAGE_ALIGN(lpBaseAddress);
|
||||
if(QueryWorkingSetEx(hProcess, &wsi, sizeof(wsi)) && !wsi.VirtualAttributes.Valid)
|
||||
{
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
if(VirtualQueryEx(hProcess, wsi.VirtualAddress, &mbi, sizeof(mbi)) && mbi.State == MEM_COMMIT && mbi.Type == MEM_PRIVATE)
|
||||
{
|
||||
memset(lpBuffer, 0, nSize);
|
||||
if(lpNumberOfBytesRead)
|
||||
*lpNumberOfBytesRead = nSize;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MemoryReadSafePage(HANDLE hProcess, LPVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesRead)
|
||||
{
|
||||
//TODO: remove when proven stable, this function checks if reads are always within page boundaries
|
||||
auto base = duint(lpBaseAddress);
|
||||
if(nSize > PAGE_SIZE - (base & (PAGE_SIZE - 1)))
|
||||
__debugbreak();
|
||||
if(IgnoreThisRead(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead))
|
||||
return true;
|
||||
return MemoryReadSafe(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead);
|
||||
}
|
||||
|
||||
|
@ -299,7 +324,7 @@ bool MemRead(duint BaseAddress, void* Buffer, duint Size, duint* NumberOfBytesRe
|
|||
if(!MemIsCanonicalAddress(BaseAddress))
|
||||
return false;
|
||||
|
||||
if(cache && !MemIsValidReadPtr(BaseAddress, cache))
|
||||
if(cache && !MemIsValidReadPtr(BaseAddress, true))
|
||||
return false;
|
||||
|
||||
if(!Buffer || !Size)
|
||||
|
@ -335,13 +360,53 @@ bool MemRead(duint BaseAddress, void* Buffer, duint Size, duint* NumberOfBytesRe
|
|||
return success;
|
||||
}
|
||||
|
||||
bool MemReadUnsafePage(HANDLE hProcess, LPVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesRead)
|
||||
{
|
||||
//TODO: remove when proven stable, this function checks if reads are always within page boundaries
|
||||
auto base = duint(lpBaseAddress);
|
||||
if(nSize > PAGE_SIZE - (base & (PAGE_SIZE - 1)))
|
||||
__debugbreak();
|
||||
if(IgnoreThisRead(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead))
|
||||
return true;
|
||||
return !!ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead);
|
||||
}
|
||||
|
||||
bool MemReadUnsafe(duint BaseAddress, void* Buffer, duint Size, duint* NumberOfBytesRead)
|
||||
{
|
||||
SIZE_T read = 0;
|
||||
auto result = !!ReadProcessMemory(fdProcessInfo->hProcess, LPCVOID(BaseAddress), Buffer, Size, &read);
|
||||
if(NumberOfBytesRead)
|
||||
*NumberOfBytesRead = read;
|
||||
return result;
|
||||
if(!MemIsCanonicalAddress(BaseAddress) || BaseAddress < PAGE_SIZE)
|
||||
return false;
|
||||
|
||||
if(!Buffer || !Size)
|
||||
return false;
|
||||
|
||||
duint bytesReadTemp = 0;
|
||||
if(!NumberOfBytesRead)
|
||||
NumberOfBytesRead = &bytesReadTemp;
|
||||
|
||||
duint offset = 0;
|
||||
duint requestedSize = Size;
|
||||
duint sizeLeftInFirstPage = PAGE_SIZE - (BaseAddress & (PAGE_SIZE - 1));
|
||||
duint readSize = min(sizeLeftInFirstPage, requestedSize);
|
||||
|
||||
while(readSize)
|
||||
{
|
||||
SIZE_T bytesRead = 0;
|
||||
auto readSuccess = MemReadUnsafePage(fdProcessInfo->hProcess, (PVOID)(BaseAddress + offset), (PBYTE)Buffer + offset, readSize, &bytesRead);
|
||||
*NumberOfBytesRead += bytesRead;
|
||||
if(!readSuccess)
|
||||
break;
|
||||
|
||||
offset += readSize;
|
||||
requestedSize -= readSize;
|
||||
readSize = min(PAGE_SIZE, requestedSize);
|
||||
|
||||
if(readSize && (BaseAddress + offset) % PAGE_SIZE)
|
||||
__debugbreak(); //TODO: remove when proven stable, this checks if (BaseAddress + offset) is aligned to PAGE_SIZE after the first call
|
||||
}
|
||||
|
||||
auto success = *NumberOfBytesRead == Size;
|
||||
SetLastError(success ? ERROR_SUCCESS : ERROR_PARTIAL_COPY);
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool MemoryWriteSafePage(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesWritten)
|
||||
|
@ -450,7 +515,7 @@ bool MemIsCanonicalAddress(duint Address)
|
|||
// 0xFFFF800000000000 = Significant 16 bits set
|
||||
// 0x0000800000000000 = 48th bit set
|
||||
return (((Address & 0xFFFF800000000000) + 0x800000000000) & ~0x800000000000) == 0;
|
||||
#endif // ndef _WIN64
|
||||
#endif //_WIN64
|
||||
}
|
||||
|
||||
bool MemIsCodePage(duint Address, bool Refresh)
|
||||
|
@ -766,7 +831,7 @@ void MemReadDumb(duint BaseAddress, void* Buffer, duint Size)
|
|||
while(readSize)
|
||||
{
|
||||
SIZE_T bytesRead = 0;
|
||||
MemoryReadSafe(fdProcessInfo->hProcess, (PVOID)(BaseAddress + offset), (PBYTE)Buffer + offset, readSize, &bytesRead);
|
||||
MemoryReadSafePage(fdProcessInfo->hProcess, (PVOID)(BaseAddress + offset), (PBYTE)Buffer + offset, readSize, &bytesRead);
|
||||
offset += readSize;
|
||||
requestedSize -= readSize;
|
||||
readSize = min(PAGE_SIZE, requestedSize);
|
||||
|
|
|
@ -8,7 +8,9 @@ struct SimplePage;
|
|||
void MemUpdateMap();
|
||||
void MemUpdateMapAsync();
|
||||
duint MemFindBaseAddr(duint Address, duint* Size = nullptr, bool Refresh = false, bool FindReserved = false);
|
||||
bool MemoryReadSafePage(HANDLE hProcess, LPVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesRead);
|
||||
bool MemRead(duint BaseAddress, void* Buffer, duint Size, duint* NumberOfBytesRead = nullptr, bool cache = false);
|
||||
bool MemReadUnsafePage(HANDLE hProcess, LPVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesRead);
|
||||
bool MemReadUnsafe(duint BaseAddress, void* Buffer, duint Size, duint* NumberOfBytesRead = nullptr);
|
||||
bool MemWrite(duint BaseAddress, const void* Buffer, duint Size, duint* NumberOfBytesWritten = nullptr);
|
||||
bool MemPatch(duint BaseAddress, const void* Buffer, duint Size, duint* NumberOfBytesWritten = nullptr);
|
||||
|
@ -33,6 +35,7 @@ void MemReadDumb(duint BaseAddress, void* Buffer, duint Size);
|
|||
|
||||
extern std::map<Range, MEMPAGE, RangeCompare> memoryPages;
|
||||
extern bool bListAllPages;
|
||||
extern bool bQueryWorkingSet;
|
||||
extern DWORD memMapThreadCounter;
|
||||
|
||||
struct SimplePage
|
||||
|
|
|
@ -295,9 +295,11 @@ void SettingsDialog::LoadSettings()
|
|||
GetSettingBool("Misc", "Utf16LogRedirect", &settings.miscUtf16LogRedirect);
|
||||
GetSettingBool("Misc", "UseLocalHelpFile", &settings.miscUseLocalHelpFile);
|
||||
GetSettingBool("Misc", "QueryProcessCookie", &settings.miscQueryProcessCookie);
|
||||
GetSettingBool("Misc", "QueryWorkingSet", &settings.miscQueryWorkingSet);
|
||||
ui->chkUtf16LogRedirect->setChecked(settings.miscUtf16LogRedirect);
|
||||
ui->chkUseLocalHelpFile->setChecked(settings.miscUseLocalHelpFile);
|
||||
ui->chkQueryProcessCookie->setChecked(settings.miscQueryProcessCookie);
|
||||
ui->chkQueryWorkingSet->setChecked(settings.miscQueryWorkingSet);
|
||||
}
|
||||
|
||||
void SettingsDialog::SaveSettings()
|
||||
|
@ -392,6 +394,7 @@ void SettingsDialog::SaveSettings()
|
|||
BridgeSettingSetUint("Misc", "Utf16LogRedirect", settings.miscUtf16LogRedirect);
|
||||
BridgeSettingSetUint("Misc", "UseLocalHelpFile", settings.miscUseLocalHelpFile);
|
||||
BridgeSettingSetUint("Misc", "QueryProcessCookie", settings.miscQueryProcessCookie);
|
||||
BridgeSettingSetUint("Misc", "QueryWorkingSet", settings.miscQueryWorkingSet);
|
||||
|
||||
BridgeSettingFlush();
|
||||
Config()->load();
|
||||
|
@ -828,3 +831,8 @@ void SettingsDialog::on_chkQueryProcessCookie_toggled(bool checked)
|
|||
{
|
||||
settings.miscQueryProcessCookie = checked;
|
||||
}
|
||||
|
||||
void SettingsDialog::on_chkQueryWorkingSet_toggled(bool checked)
|
||||
{
|
||||
settings.miscQueryWorkingSet = checked;
|
||||
}
|
||||
|
|
|
@ -90,6 +90,7 @@ private slots:
|
|||
void on_chkShowGraphRva_toggled(bool checked);
|
||||
void on_chkUseLocalHelpFile_toggled(bool checked);
|
||||
void on_chkQueryProcessCookie_toggled(bool checked);
|
||||
void on_chkQueryWorkingSet_toggled(bool checked);
|
||||
|
||||
private:
|
||||
//enums
|
||||
|
@ -182,6 +183,7 @@ private:
|
|||
bool miscUtf16LogRedirect;
|
||||
bool miscUseLocalHelpFile;
|
||||
bool miscQueryProcessCookie;
|
||||
bool miscQueryWorkingSet;
|
||||
};
|
||||
|
||||
//variables
|
||||
|
|
|
@ -762,6 +762,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="chkQueryWorkingSet">
|
||||
<property name="text">
|
||||
<string>Query working set before reading memory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
|
|
Loading…
Reference in New Issue