Skip to content

Commit

Permalink
Merge unmerged commits from pull request #131 from LordNoteworthy/pag…
Browse files Browse the repository at this point in the history
…e-exception-debug-checks

Added check to catch CE page exception breakpoints
  • Loading branch information
gsuberland authored Jul 23, 2018
2 parents e846bee + 31c127a commit eb59667
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 1 deletion.
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 @@ -74,6 +75,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

6 comments on commit eb59667

@friuns2
Copy link

@friuns2 friuns2 commented on eb59667 Jul 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should it detect cheat engine? didn't got it work yet
i did pause after PageExceptionInitialEnum(); then i attached debugger and it didn't detect it

@gsuberland
Copy link
Collaborator Author

@gsuberland gsuberland commented on eb59667 Jul 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have to set the breakpoint type to page exceptions inside CE and set a breakpoint somewhere in the main module's address space.

@friuns2
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah ok gonna try that

@friuns2
Copy link

@friuns2 friuns2 commented on eb59667 Jul 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, almoust working!

"Find out what accesses this address" it detects
but "Find out what writes this address" it not detecting

is it possible detect also setter breakpoints?

image

image

@gsuberland
Copy link
Collaborator Author

@gsuberland gsuberland commented on eb59667 Jul 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is designed for regular breakpoints in the main application code (hit Memory View, set a BP on code inside the main module), not for data breakpoints on the heap.

The "find out what accesses this" feature is probably being detected because CE inserts transient breakpoints in order to capture the thread context, and the address that reads that memory is within the main module's code. The "find out what writes to this" probably doesn't catch anything because the code that wrote to it was in another library (e.g. the print API) or CE is using an alternative way to catch writes (e.g. the page write watch APIs).

A potential future improvement is to also do the same checks on pages assigned to the core APIs loaded into the process (ntdll, kernel32, user32, psapi, etc.), but that may come at a cost of having false positives.

At the moment we cannot do this check on the dynamic heap pages because the heap may contain guard pages and noaccess pages during regular operation (e.g. the stack has guard pages to trigger stack expansion, stack smashing protection also uses noaccess/guard pages) and there are other hazards such as mandatory CFG which may cause process termination if we touch the wrong heap memory.

@friuns2
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok big thanks for implementing this!

Please sign in to comment.