Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added check to catch CE page exception breakpoints #131

Merged
merged 1 commit into from
Jul 23, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Added check to catch CE page exception breakpoints
Cheat Engine has a page exception BP mode that avoids detection from other anti-debug tricks. This new check catches them in two ways. The first way is to look for any guard pages or noaccess pages in the main module, although CE bypasses this trick by hooking VirtualQuery. The method has been left in to catch out other incomplete implementations. The second approach is to enumerate all of the main module's page states at startup, then check them again later and look for any executable pages that became non-executable. This resolves #129.
  • Loading branch information
gsuberland committed Jul 23, 2018
commit 31c127a5cc58c0a13817b3559da563e47f07ea18
2 changes: 2 additions & 0 deletions al-khaser/Al-khaser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ int main(void)
print_category(TEXT("Initialisation"));
API::Init();
API::PrintAvailabilityReport();
if (ENABLE_DEBUG_CHECKS) PageExceptionInitialEnum();

/* TLS checks */
if (ENABLE_TLS_CHECKS) {
Expand Down Expand Up @@ -73,6 +74,7 @@ int main(void)
exec_check(&VirtualAlloc_WriteWatch_APICalls, TEXT("Checking VirtualAlloc write watch (API calls) "));
exec_check(&VirtualAlloc_WriteWatch_IsDebuggerPresent, TEXT("Checking VirtualAlloc write watch (IsDebuggerPresent) "));
exec_check(&VirtualAlloc_WriteWatch_CodeWrite, TEXT("Checking VirtualAlloc write watch (code write) "));
exec_check(&PageExceptionBreakpointCheck, TEXT("Checking for page exception breakpoints "));
}

/* Generic sandbox detection */
Expand Down
139 changes: 139 additions & 0 deletions al-khaser/Anti Debug/PageExceptionBreakpointCheck.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#include "stdafx.h"

#ifdef _DEBUG
#define OutputDebugStringDbgOnly(S) OutputDebugString(S)
#else
#define OutputDebugStringDbgOnly(S) do {} while(0);
#endif

std::vector<PVOID> executablePages = {};

void PageExceptionInitialEnum()
{
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
size_t pageSize = sysInfo.dwPageSize;

HMODULE hMainModule;
MODULEINFO moduleInfo;

MEMORY_BASIC_INFORMATION memInfo = { 0 };

// Get the main module handle from an address stored within it (pointer to this method)
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR)PageExceptionBreakpointCheck, &hMainModule) == TRUE)
{
// Get information about the main module (we want to know the size of it)
if (GetModuleInformation(GetCurrentProcess(), hMainModule, &moduleInfo, sizeof(MODULEINFO)) == TRUE)
{
// cast the module to a pointer
unsigned char* module = static_cast<unsigned char*>(moduleInfo.lpBaseOfDll);
for (size_t ofs = 0; ofs < moduleInfo.SizeOfImage; ofs += pageSize)
{
if (VirtualQuery(module + ofs, &memInfo, sizeof(MEMORY_BASIC_INFORMATION)) >= sizeof(MEMORY_BASIC_INFORMATION))
{
if ((memInfo.Protect & PAGE_EXECUTE) == PAGE_EXECUTE ||
(memInfo.Protect & PAGE_EXECUTE_READ) == PAGE_EXECUTE_READ ||
(memInfo.Protect & PAGE_EXECUTE_WRITECOPY) == PAGE_EXECUTE_WRITECOPY ||
(memInfo.Protect & PAGE_EXECUTE_READWRITE) == PAGE_EXECUTE_READWRITE)
{
executablePages.push_back(module + ofs);
}
}
}
}
}
}

BOOL PageExceptionBreakpointCheck()
{
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
size_t pageSize = sysInfo.dwPageSize;

HMODULE hMainModule;
MODULEINFO moduleInfo;

MEMORY_BASIC_INFORMATION memInfo = { 0 };

wchar_t buffer[512];

// first we check if any of the pages are executable+guard or noaccess

// Get the main module handle from an address stored within it (pointer to this method)
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR)PageExceptionBreakpointCheck, &hMainModule) == TRUE)
{
// Get information about the main module (we want to know the size of it)
if (GetModuleInformation(GetCurrentProcess(), hMainModule, &moduleInfo, sizeof(MODULEINFO)) == TRUE)
{
// cast the module to a pointer
unsigned char* module = static_cast<unsigned char*>(moduleInfo.lpBaseOfDll);
for (size_t ofs = 0; ofs < moduleInfo.SizeOfImage; ofs += pageSize)
{
SecureZeroMemory(buffer, 512);
wsprintf(buffer, L"Scanning %p... ", module + ofs);
OutputDebugStringDbgOnly(buffer);
if (VirtualQuery(module + ofs, &memInfo, sizeof(MEMORY_BASIC_INFORMATION)) >= sizeof(MEMORY_BASIC_INFORMATION))
{
if (memInfo.AllocationProtect == 0)
OutputDebugStringDbgOnly(L"^ AllocationProtect is zero. Potential shenanigans.");
if (memInfo.Protect == 0)
OutputDebugStringDbgOnly(L"^ Protect is zero. Potential shenanigans.");

if ((memInfo.Protect & PAGE_EXECUTE) == PAGE_EXECUTE ||
(memInfo.Protect & PAGE_EXECUTE_READ) == PAGE_EXECUTE_READ ||
(memInfo.Protect & PAGE_EXECUTE_WRITECOPY) == PAGE_EXECUTE_WRITECOPY ||
(memInfo.Protect & PAGE_EXECUTE_READWRITE) == PAGE_EXECUTE_READWRITE)
{
// this is an executable page
OutputDebugStringDbgOnly(L"^ is executable.");

if ((memInfo.Protect & PAGE_GUARD) == PAGE_GUARD ||
(memInfo.AllocationProtect & PAGE_GUARD) == PAGE_GUARD)
{
// this is an executable guard page, page exception debugging detected
OutputDebugStringDbgOnly(L"^ is guard page !!!!!!");
return TRUE;
}
}

if ((memInfo.Protect & PAGE_NOACCESS) == PAGE_NOACCESS)
{
// this is a NOACCESS page, which shouldn't exist here (alternative way to set page exception BPs)
OutputDebugStringDbgOnly(L"^ is NOACCESS !!!!!!!");
return TRUE;
}
}
else OutputDebugStringDbgOnly(L"^ FAILED!");
}
}

OutputDebugStringDbgOnly(L"Moving on to delta check...");

for (PVOID page : executablePages)
{
SecureZeroMemory(buffer, 512);
wsprintf(buffer, L"Scanning delta for %p... ", page);
OutputDebugStringDbgOnly(buffer);

if (VirtualQuery(page, &memInfo, sizeof(MEMORY_BASIC_INFORMATION)) >= sizeof(MEMORY_BASIC_INFORMATION))
{
if (memInfo.AllocationProtect == 0)
OutputDebugStringDbgOnly(L"^ AllocationProtect is zero. Potential shenanigans.");
if (memInfo.Protect == 0)
OutputDebugStringDbgOnly(L"^ Protect is zero. Potential shenanigans.");

if (!((memInfo.Protect & PAGE_EXECUTE) == PAGE_EXECUTE ||
(memInfo.Protect & PAGE_EXECUTE_READ) == PAGE_EXECUTE_READ ||
(memInfo.Protect & PAGE_EXECUTE_WRITECOPY) == PAGE_EXECUTE_WRITECOPY ||
(memInfo.Protect & PAGE_EXECUTE_READWRITE) == PAGE_EXECUTE_READWRITE))
{
// page was executable, now isn't!
OutputDebugStringDbgOnly(L"^ was executable, now isn't !!!!!!");
return TRUE;
}
}
}
}

return FALSE;
}
4 changes: 4 additions & 0 deletions al-khaser/Anti Debug/PageExceptionBreakpointCheck.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#pragma once

void PageExceptionInitialEnum();
BOOL PageExceptionBreakpointCheck();
2 changes: 2 additions & 0 deletions al-khaser/al-khaser.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@
<ClCompile Include="Anti Debug\NtSetInformationThread_ThreadHideFromDebugger.cpp" />
<ClCompile Include="Anti Debug\NtYieldExecution.cpp" />
<ClCompile Include="Anti Debug\OutputDebugStringAPI.cpp" />
<ClCompile Include="Anti Debug\PageExceptionBreakpointCheck.cpp" />
<ClCompile Include="Anti Debug\ParentProcess.cpp" />
<ClCompile Include="Anti Debug\PEB_BeingDebugged.cpp" />
<ClCompile Include="Anti Debug\ProcessHeap_Flags.cpp" />
Expand Down Expand Up @@ -235,6 +236,7 @@
<ClInclude Include="Anti Debug\NtSetInformationThread_ThreadHideFromDebugger.h" />
<ClInclude Include="Anti Debug\NtYieldExecution.h" />
<ClInclude Include="Anti Debug\OutputDebugStringAPI.h" />
<ClInclude Include="Anti Debug\PageExceptionBreakpointCheck.h" />
<ClInclude Include="Anti Debug\ParentProcess.h" />
<ClInclude Include="Anti Debug\PEB_BeingDebugged.h" />
<ClInclude Include="Anti Debug\ProcessHeap_Flags.h" />
Expand Down
6 changes: 6 additions & 0 deletions al-khaser/al-khaser.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,9 @@
<ClCompile Include="Shared\ApiTypeDefs.cpp">
<Filter>Shared\Source</Filter>
</ClCompile>
<ClCompile Include="Anti Debug\PageExceptionBreakpointCheck.cpp">
<Filter>Anti Debug\Source</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Shared\VersionHelpers.h">
Expand Down Expand Up @@ -425,6 +428,9 @@
<ClInclude Include="Shared\ApiTypeDefs.h">
<Filter>Shared\Header</Filter>
</ClInclude>
<ClInclude Include="Anti Debug\PageExceptionBreakpointCheck.h">
<Filter>Anti Debug\Header</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<MASM Include="Anti Debug\int2d_x64.asm">
Expand Down
5 changes: 4 additions & 1 deletion al-khaser/stdafx.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
//#pragma once

#include <string>
#include <vector>

#include <Windows.h>
#include <winternl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <assert.h>
#include <IPTypes.h>
#include <Iphlpapi.h>
Expand Down Expand Up @@ -74,6 +76,7 @@
#include "Anti Debug\SharedUserData_KernelDebugger.h"
#include "Anti Debug\ProcessJob.h"
#include "Anti Debug\WriteWatch.h"
#include "Anti Debug\PageExceptionBreakpointCheck.h"

/* Anti dumping headers */
#include "Anti Dump\ErasePEHeaderFromMemory.h"
Expand Down