mirror of
https://github.com/cheat-engine/cheat-engine
synced 2026-08-15 02:26:08 -04:00
1327 lines
No EOL
31 KiB
C
1327 lines
No EOL
31 KiB
C
#pragma warning( disable: 4100 4103 4146 4213)
|
|
|
|
#include "ntifs.h"
|
|
#include <windef.h>
|
|
#ifdef CETC
|
|
#include "tdiwrapper.h"
|
|
#include "kfiles.h"
|
|
#endif
|
|
#include "memscan.h"
|
|
#include "DBKFunc.h"
|
|
|
|
#include "vmxhelper.h"
|
|
#include "vmxoffload.h" //PTE structs
|
|
#include "noexceptions.h"
|
|
/*#include "deepkernel.h"
|
|
*/
|
|
|
|
UINT_PTR KnownPageTableBase = 0;
|
|
|
|
void VirtualAddressToIndexes(QWORD address, int *pml4index, int *pagedirptrindex, int *pagedirindex, int *pagetableindex)
|
|
/*
|
|
* Returns the indexes for the given address (ia32e)
|
|
*/
|
|
{
|
|
if (PTESize == 8)
|
|
{
|
|
*pml4index = (address >> 39) & 0x1ff;
|
|
*pagedirptrindex = (address >> 30) & 0x1ff;
|
|
*pagedirindex = (address >> 21) & 0x1ff;
|
|
*pagetableindex = (address >> 12) & 0x1ff;
|
|
}
|
|
else
|
|
{
|
|
*pml4index = 0;
|
|
*pagedirptrindex = 0;
|
|
*pagedirindex = (address >> 22) & 0x3ff;
|
|
*pagetableindex = (address >> 12) & 0x3ff;
|
|
}
|
|
}
|
|
|
|
QWORD IndexesToVirtualAddress(int pml4index, int pagedirptrindex, int pagedirindex, int pagetableindex, int offset)
|
|
{
|
|
QWORD r;
|
|
#ifndef AMD64
|
|
if (PTESize == 8)
|
|
{
|
|
#endif
|
|
r = ((QWORD)pml4index & 0x1ff) << 39;
|
|
|
|
if (pml4index >= 256)
|
|
r = r | 0xFFFF000000000000ULL;
|
|
|
|
r |= ((QWORD)pagedirptrindex & 0x1ff) << 30;
|
|
r |= ((QWORD)pagedirindex & 0x1ff) << 21;
|
|
r |= ((QWORD)pagetableindex & 0x1ff) << 12;
|
|
r |= offset & 0xfff;
|
|
|
|
|
|
#ifndef AMD64
|
|
}
|
|
else
|
|
{
|
|
r |= (pagedirindex & 0x3ff) << 22;
|
|
r |= (pagetableindex & 0x3ff) << 12;
|
|
r |= offset & 0xfff;
|
|
}
|
|
#endif
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
#ifndef AMD64
|
|
void VirtualAddressToPageEntries32(DWORD address, PPDE *pagedirentry, PPTE *pagetableentry)
|
|
{
|
|
DWORD PTE = address;
|
|
UINT_PTR PTB = (DWORD)getPageTableBase();
|
|
PTE = PTE >> 12;
|
|
PTE = PTE * 4;
|
|
PTE = PTE + PTB;
|
|
*pagetableentry = (PPTE)PTE;
|
|
|
|
UINT_PTR PDE = PTE;
|
|
PDE = PDE & 0x0000ffffffffffffULL;
|
|
PDE = PDE >> 12;
|
|
PDE = PDE * 4;
|
|
PDE = PDE + PTB;
|
|
*pagedirentry = (PPDE)PDE;
|
|
}
|
|
#endif
|
|
|
|
void VirtualAddressToPageEntries64(QWORD address, PPDPTE_PAE *pml4entry, PPDPTE_PAE *pagedirpointerentry, PPDE_PAE *pagedirentry, PPTE_PAE *pagetableentry)
|
|
{
|
|
QWORD PTE = address;
|
|
|
|
QWORD PTB=getPageTableBase();
|
|
|
|
PTE = PTE & 0x0000ffffffffffffULL;
|
|
PTE = PTE >> 12;
|
|
PTE = PTE * 8;
|
|
PTE = PTE + PTB;
|
|
*pagetableentry = (PPTE_PAE)(UINT_PTR)PTE;
|
|
|
|
//*pagetableentry = (PPTE_PAE)((((QWORD)address & 0x0000ffffffffffffull) >> 12)*8) + 0xfffff80000000000ULL;
|
|
|
|
QWORD PDE = PTE;
|
|
PDE = PDE & 0x0000ffffffffffffULL;
|
|
PDE = PDE >> 12;
|
|
PDE = PDE * 8;
|
|
PDE = PDE + PTB;
|
|
*pagedirentry = (PPDE_PAE)(UINT_PTR)PDE;
|
|
|
|
|
|
//*pagedirentry = (PPDE_PAE)((((QWORD)*pagetableentry & 0x0000ffffffffffffull )>> 12)*8) + 0xfffff80000000000ULL;
|
|
|
|
QWORD PDPTR = PDE;
|
|
PDPTR = PDPTR & 0x0000ffffffffffffULL;
|
|
PDPTR = PDPTR >> 12;
|
|
PDPTR = PDPTR * 8;
|
|
PDPTR = PDPTR + PTB;
|
|
*pagedirpointerentry = (PPDPTE_PAE)(UINT_PTR)PDPTR;
|
|
|
|
//*pagedirpointerentry = (PPDPTE_PAE)((((QWORD)*pagedirentry & 0x0000ffffffffffffull )>> 12)*8) + 0xfffff80000000000ULL;
|
|
#ifdef AMD64
|
|
QWORD PML4 = PDPTR;
|
|
PML4 = PML4 & 0x0000ffffffffffffULL;
|
|
PML4 = PML4 >> 12;
|
|
PML4 = PML4 * 8;
|
|
PML4 = PML4 + PTB;
|
|
*pml4entry = (PPDPTE_PAE)PML4;
|
|
#else
|
|
*pml4entry = NULL;
|
|
#endif
|
|
}
|
|
|
|
BOOLEAN IsAddressSafe(UINT_PTR StartAddress)
|
|
{
|
|
#ifdef AMD64
|
|
//cannonical check. Bits 48 to 63 must match bit 47
|
|
UINT_PTR toppart=(StartAddress >> 47);
|
|
if (toppart & 1)
|
|
{
|
|
//toppart must be 0x1ffff
|
|
if (toppart != 0x1ffff)
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
//toppart must be 0
|
|
if (toppart != 0)
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
//return TRUE;
|
|
if (loadedbydbvm)
|
|
{
|
|
BYTE x=0;
|
|
UINT_PTR lasterror;
|
|
disableInterrupts();
|
|
vmx_disable_dataPageFaults();
|
|
|
|
x=*(volatile BYTE *)StartAddress;
|
|
|
|
vmx_enable_dataPageFaults();
|
|
lasterror=vmx_getLastSkippedPageFault();
|
|
enableInterrupts();
|
|
|
|
DbgPrint("IsAddressSafe dbvm-mode: lastError=%p\n", lasterror);
|
|
|
|
if (lasterror) return FALSE;
|
|
}
|
|
|
|
|
|
{
|
|
#ifdef AMD64
|
|
UINT_PTR kernelbase=0x7fffffffffffffffULL;
|
|
|
|
|
|
if (StartAddress<kernelbase)
|
|
return TRUE;
|
|
else
|
|
{
|
|
PHYSICAL_ADDRESS physical;
|
|
physical.QuadPart=0;
|
|
physical=MmGetPhysicalAddress((PVOID)StartAddress);
|
|
return (physical.QuadPart!=0);
|
|
}
|
|
#else
|
|
/* MDL x;
|
|
|
|
|
|
MmProbeAndLockPages(&x,KernelMode,IoModifyAccess);
|
|
|
|
|
|
MmUnlockPages(&x);
|
|
*/
|
|
ULONG kernelbase=0x7ffe0000;
|
|
|
|
if ((!HiddenDriver) && (StartAddress<kernelbase))
|
|
return TRUE;
|
|
|
|
{
|
|
UINT_PTR PTE,PDE;
|
|
struct PTEStruct *x;
|
|
|
|
/*
|
|
PHYSICAL_ADDRESS physical;
|
|
physical=MmGetPhysicalAddress((PVOID)StartAddress);
|
|
return (physical.QuadPart!=0);*/
|
|
|
|
|
|
PTE=(UINT_PTR)StartAddress;
|
|
PTE=PTE/0x1000*PTESize+0xc0000000;
|
|
|
|
//now check if the address in PTE is valid by checking the page table directory at 0xc0300000 (same location as CR3 btw)
|
|
PDE=PTE/0x1000*PTESize+0xc0000000; //same formula
|
|
|
|
x=(PVOID)PDE;
|
|
if ((x->P==0) && (x->A2==0))
|
|
{
|
|
//Not present or paged, and since paging in this area isn't such a smart thing to do just skip it
|
|
//perhaps this is only for the 4 mb pages, but those should never be paged out, so it should be 1
|
|
//bah, I've got no idea what this is used for
|
|
return FALSE;
|
|
}
|
|
|
|
if (x->PS==1)
|
|
{
|
|
//This is a 4 MB page (no pte list)
|
|
//so, (startaddress/0x400000*0x400000) till ((startaddress/0x400000*0x400000)+(0x400000-1) ) ) is specified by this page
|
|
}
|
|
else //if it's not a 4 MB page then check the PTE
|
|
{
|
|
//still here so the page table directory agreed that it is a usable page table entry
|
|
x=(PVOID)PTE;
|
|
if ((x->P==0) && (x->A2==0))
|
|
return FALSE; //see for explenation the part of the PDE
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
UINT_PTR getPEThread(UINT_PTR threadid)
|
|
{
|
|
//UINT_PTR *threadid;
|
|
PETHREAD selectedthread;
|
|
UINT_PTR result=0;
|
|
|
|
|
|
|
|
if (PsLookupThreadByThreadId((PVOID)(UINT_PTR)threadid,&selectedthread)==STATUS_SUCCESS)
|
|
{
|
|
result=(UINT_PTR)selectedthread;
|
|
ObDereferenceObject(selectedthread);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BOOLEAN WriteProcessMemory(DWORD PID,PEPROCESS PEProcess,PVOID Address,DWORD Size, PVOID Buffer)
|
|
{
|
|
PEPROCESS selectedprocess=PEProcess;
|
|
KAPC_STATE apc_state;
|
|
NTSTATUS ntStatus=STATUS_UNSUCCESSFUL;
|
|
|
|
if (selectedprocess==NULL)
|
|
{
|
|
//DbgPrint("WriteProcessMemory:Getting PEPROCESS\n");
|
|
if (!NT_SUCCESS(PsLookupProcessByProcessId((PVOID)(UINT_PTR)PID,&selectedprocess)))
|
|
return FALSE; //couldn't get the PID
|
|
|
|
//DbgPrint("Retrieved peprocess");
|
|
}
|
|
|
|
//selectedprocess now holds a valid peprocess value
|
|
__try
|
|
{
|
|
RtlZeroMemory(&apc_state,sizeof(apc_state));
|
|
|
|
KeAttachProcess((PEPROCESS)selectedprocess);
|
|
|
|
__try
|
|
{
|
|
char* target;
|
|
char* source;
|
|
unsigned int i;
|
|
|
|
//DbgPrint("Checking safety of memory\n");
|
|
|
|
if ((IsAddressSafe((UINT_PTR)Address)) && (IsAddressSafe((UINT_PTR)Address+Size-1)))
|
|
{
|
|
|
|
//still here, then I gues it's safe to read. (But I can't be 100% sure though, it's still the users problem if he accesses memory that doesn't exist)
|
|
BOOL disabledWP = FALSE;
|
|
|
|
target=Address;
|
|
source=Buffer;
|
|
|
|
if ((loadedbydbvm) || (KernelWritesIgnoreWP)) //add a extra security around it as the PF will not be handled
|
|
{
|
|
disableInterrupts();
|
|
|
|
if (loadedbydbvm)
|
|
vmx_disable_dataPageFaults();
|
|
|
|
if (KernelWritesIgnoreWP)
|
|
{
|
|
DbgPrint("Disabling CR0.WP");
|
|
setCR0(getCR0() & (~(1 << 16))); //disable the WP bit
|
|
disabledWP = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
if ((!loadedbydbvm) && ((KernelWritesIgnoreWP) || ((UINT_PTR)target >= 0x8000000000000000ULL)))
|
|
{
|
|
i = NoExceptions_CopyMemory(target, source, Size);
|
|
if (i != (int)Size)
|
|
ntStatus = STATUS_UNSUCCESSFUL;
|
|
else
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
RtlCopyMemory(target, source, Size);
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
|
|
if ((loadedbydbvm) || (disabledWP))
|
|
{
|
|
UINT_PTR lastError=0;
|
|
|
|
if (disabledWP)
|
|
{
|
|
setCR0(getCR0() | (1 << 16));
|
|
DbgPrint("Enabled CR0.WP");
|
|
}
|
|
|
|
if (loadedbydbvm)
|
|
{
|
|
lastError = vmx_getLastSkippedPageFault();
|
|
vmx_enable_dataPageFaults();
|
|
}
|
|
|
|
enableInterrupts();
|
|
|
|
DbgPrint("lastError=%p\n", lastError);
|
|
if (lastError)
|
|
ntStatus=STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|
|
__finally
|
|
{
|
|
KeDetachProcess();
|
|
}
|
|
}
|
|
__except(1)
|
|
{
|
|
//DbgPrint("Error while writing\n");
|
|
ntStatus = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
if (PEProcess==NULL) //no valid peprocess was given so I made a reference, so lets also dereference
|
|
ObDereferenceObject(selectedprocess);
|
|
|
|
return NT_SUCCESS(ntStatus);
|
|
}
|
|
|
|
|
|
BOOLEAN ReadProcessMemory(DWORD PID,PEPROCESS PEProcess,PVOID Address,DWORD Size, PVOID Buffer)
|
|
{
|
|
PEPROCESS selectedprocess=PEProcess;
|
|
//KAPC_STATE apc_state;
|
|
NTSTATUS ntStatus=STATUS_UNSUCCESSFUL;
|
|
|
|
if (PEProcess==NULL)
|
|
{
|
|
if (!NT_SUCCESS(PsLookupProcessByProcessId((PVOID)(UINT_PTR)PID,&selectedprocess)))
|
|
return FALSE; //couldn't get the PID
|
|
|
|
}
|
|
|
|
//selectedprocess now holds a valid peprocess value
|
|
__try
|
|
{
|
|
KeAttachProcess((PEPROCESS)selectedprocess);
|
|
|
|
|
|
|
|
__try
|
|
{
|
|
char* target;
|
|
char* source;
|
|
int i;
|
|
|
|
|
|
if ((IsAddressSafe((UINT_PTR)Address)) && (IsAddressSafe((UINT_PTR)Address+Size-1)))
|
|
{
|
|
|
|
|
|
|
|
target=Buffer;
|
|
source=Address;
|
|
|
|
if (loadedbydbvm) //add a extra security around it
|
|
{
|
|
disableInterrupts();
|
|
vmx_disable_dataPageFaults();
|
|
}
|
|
|
|
|
|
if ((loadedbydbvm) || ((UINT_PTR)source < 0x8000000000000000ULL))
|
|
{
|
|
RtlCopyMemory(target, source, Size);
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
i=NoExceptions_CopyMemory(target, source, Size);
|
|
if (i != (int)Size)
|
|
ntStatus = STATUS_UNSUCCESSFUL;
|
|
else
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
if (loadedbydbvm)
|
|
{
|
|
UINT_PTR lastError;
|
|
lastError=vmx_getLastSkippedPageFault();
|
|
vmx_enable_dataPageFaults();
|
|
|
|
enableInterrupts();
|
|
|
|
DbgPrint("lastError=%p\n", lastError);
|
|
if (lastError)
|
|
ntStatus=STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
}
|
|
__finally
|
|
{
|
|
|
|
KeDetachProcess();
|
|
}
|
|
}
|
|
__except(1)
|
|
{
|
|
//DbgPrint("Error while reading: ReadProcessMemory(%x,%p, %p, %d, %p\n", PID, PEProcess, Address, Size, Buffer);
|
|
|
|
ntStatus = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
if (PEProcess==NULL) //no valid peprocess was given so I made a reference, so lets also dereference
|
|
ObDereferenceObject(selectedprocess);
|
|
|
|
return NT_SUCCESS(ntStatus);
|
|
}
|
|
|
|
UINT64 maxPhysAddress = 0;
|
|
UINT64 getMaxPhysAddress(void)
|
|
{
|
|
if (maxPhysAddress==0)
|
|
{
|
|
int physicalbits;
|
|
DWORD r[4];
|
|
__cpuid(r, 0x80000008);
|
|
|
|
//get max physical address
|
|
physicalbits = r[0] & 0xff;
|
|
|
|
maxPhysAddress = 0xFFFFFFFFFFFFFFFFULL;
|
|
maxPhysAddress = maxPhysAddress >> physicalbits; //if physicalbits==36 then maxPhysAddress=0x000000000fffffff
|
|
maxPhysAddress = ~(maxPhysAddress << physicalbits); //<< 36 = 0xfffffff000000000 . after inverse : 0x0000000fffffffff
|
|
}
|
|
|
|
return maxPhysAddress;
|
|
}
|
|
|
|
|
|
NTSTATUS ReadPhysicalMemory(char *startaddress, UINT_PTR bytestoread, void *output)
|
|
{
|
|
HANDLE physmem;
|
|
UNICODE_STRING physmemString;
|
|
OBJECT_ATTRIBUTES attributes;
|
|
WCHAR physmemName[] = L"\\device\\physicalmemory";
|
|
UCHAR* memoryview;
|
|
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
|
|
PMDL outputMDL;
|
|
|
|
DbgPrint("ReadPhysicalMemory(%p, %d, %p)", startaddress, bytestoread, output);
|
|
|
|
if (((UINT64)startaddress > getMaxPhysAddress()) || ((UINT64)startaddress + bytestoread > getMaxPhysAddress()))
|
|
{
|
|
DbgPrint("Invalid physical address\n");
|
|
return ntStatus;
|
|
}
|
|
|
|
outputMDL = IoAllocateMdl(output, (ULONG)bytestoread, FALSE, FALSE, NULL);
|
|
__try
|
|
{
|
|
MmProbeAndLockPages(outputMDL, KernelMode, IoWriteAccess);
|
|
}
|
|
__except (1)
|
|
{
|
|
IoFreeMdl(outputMDL);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
__try
|
|
{
|
|
RtlInitUnicodeString( &physmemString, physmemName );
|
|
|
|
InitializeObjectAttributes( &attributes, &physmemString, OBJ_CASE_INSENSITIVE, NULL, NULL );
|
|
ntStatus=ZwOpenSection( &physmem, SECTION_ALL_ACCESS, &attributes );
|
|
if (ntStatus==STATUS_SUCCESS)
|
|
{
|
|
//hey look, it didn't kill it
|
|
SIZE_T length;
|
|
PHYSICAL_ADDRESS viewBase;
|
|
UINT_PTR offset;
|
|
UINT_PTR toread;
|
|
|
|
viewBase.QuadPart = (ULONGLONG)(startaddress);
|
|
|
|
length=0x2000;//pinp->bytestoread; //in case of a overlapping region
|
|
toread=bytestoread;
|
|
|
|
memoryview=NULL;
|
|
|
|
DbgPrint("ReadPhysicalMemory:viewBase.QuadPart=%x", viewBase.QuadPart);
|
|
|
|
|
|
ntStatus=ZwMapViewOfSection(
|
|
physmem, //sectionhandle
|
|
NtCurrentProcess(), //processhandle (should be -1)
|
|
&memoryview, //BaseAddress
|
|
0L, //ZeroBits
|
|
length, //CommitSize
|
|
&viewBase, //SectionOffset
|
|
&length, //ViewSize
|
|
ViewShare,
|
|
0,
|
|
PAGE_READWRITE);
|
|
|
|
if ((ntStatus == STATUS_SUCCESS) && (memoryview!=NULL))
|
|
{
|
|
if (toread > length)
|
|
toread = length;
|
|
|
|
if (toread)
|
|
{
|
|
__try
|
|
{
|
|
|
|
offset = (UINT_PTR)(startaddress)-(UINT_PTR)viewBase.QuadPart;
|
|
|
|
if (offset + toread > length)
|
|
{
|
|
DbgPrint("Too small map");
|
|
}
|
|
else
|
|
{
|
|
RtlCopyMemory(output, &memoryview[offset], toread);
|
|
}
|
|
|
|
ZwUnmapViewOfSection(NtCurrentProcess(), memoryview);
|
|
}
|
|
__except (1)
|
|
{
|
|
DbgPrint("Failure mapping physical memory");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DbgPrint("ReadPhysicalMemory error:ntStatus=%x", ntStatus);
|
|
}
|
|
|
|
ZwClose(physmem);
|
|
};
|
|
}
|
|
__except(1)
|
|
{
|
|
DbgPrint("Error while reading physical memory\n");
|
|
}
|
|
|
|
MmUnlockPages(outputMDL);
|
|
IoFreeMdl(outputMDL);
|
|
return ntStatus;
|
|
}
|
|
|
|
UINT_PTR SignExtend(UINT_PTR a)
|
|
{
|
|
#ifdef AMD64
|
|
if ((a >> 47)==1)
|
|
return a | 0xFFFF000000000000ULL; //add sign extended bits
|
|
else
|
|
return a;
|
|
#else
|
|
return a;
|
|
#endif
|
|
}
|
|
|
|
|
|
UINT_PTR getPageTableBase()
|
|
{
|
|
#if (NTDDI_VERSION >= NTDDI_VISTA)
|
|
if (KnownPageTableBase==0)
|
|
{
|
|
RTL_OSVERSIONINFOW v;
|
|
v.dwOSVersionInfoSize = sizeof(v);
|
|
if (RtlGetVersion(&v))
|
|
{
|
|
DbgPrint("RtlGetVersion failed");
|
|
return 0;
|
|
}
|
|
|
|
if ((v.dwMajorVersion >= 10) && (v.dwBuildNumber >= 14393))
|
|
{
|
|
PHYSICAL_ADDRESS a;
|
|
PVOID r;
|
|
a.QuadPart = getCR3() & 0xFFFFFFFFFFFFF000ULL;
|
|
r = MmGetVirtualForPhysical(a); //if this stops working, look for CR3 in the pml4 table
|
|
|
|
KnownPageTableBase = ((UINT_PTR)r) & 0xFFFFFF8000000000ULL;
|
|
|
|
MAX_PTE_POS = (UINT_PTR)((QWORD)KnownPageTableBase + 0x7FFFFFFFF8ULL);
|
|
MAX_PDE_POS = (UINT_PTR)((QWORD)KnownPageTableBase + 0x7B7FFFFFF8ULL);
|
|
|
|
|
|
}
|
|
else
|
|
KnownPageTableBase=PAGETABLEBASE;
|
|
|
|
DbgPrint("PageTableBase at %p\n", KnownPageTableBase);
|
|
}
|
|
|
|
return KnownPageTableBase;
|
|
#else
|
|
return PAGETABLEBASE;
|
|
#endif
|
|
|
|
}
|
|
|
|
typedef void PRESENTPAGECALLBACK(UINT_PTR StartAddress, UINT_PTR EndAddress, struct PTEStruct *pageEntry);
|
|
BOOL walkPagingLayout(PEPROCESS PEProcess, UINT_PTR MaxAddress, PRESENTPAGECALLBACK OnPresentPage)
|
|
{
|
|
#ifdef AMD64
|
|
UINT_PTR pagebase = getPageTableBase();
|
|
#else
|
|
UINT_PTR pagebase = PAGETABLEBASE;
|
|
#endif
|
|
|
|
if (pagebase == 0)
|
|
return FALSE;
|
|
|
|
if (OnPresentPage == NULL)
|
|
return FALSE;
|
|
|
|
__try
|
|
{
|
|
KeAttachProcess((PEPROCESS)PEProcess);
|
|
__try
|
|
{
|
|
UINT_PTR currentAddress = 0; //start from address 0
|
|
UINT_PTR lastAddress = 0;
|
|
struct PTEStruct *PPTE, *PPDE, *PPDPE, *PPML4E;
|
|
|
|
while ((currentAddress < MaxAddress) && (lastAddress<=currentAddress) )
|
|
{
|
|
//DbgPrint("currentAddress=%p\n", currentAddress);
|
|
lastAddress = currentAddress;
|
|
|
|
|
|
(UINT_PTR)PPTE = (UINT_PTR)(((currentAddress & 0xFFFFFFFFFFFFULL) >> 12) *PTESize + pagebase);
|
|
(UINT_PTR)PPDE = (UINT_PTR)((((UINT_PTR)PPTE) & 0xFFFFFFFFFFFFULL) >> 12) *PTESize + pagebase;
|
|
(UINT_PTR)PPDPE = (UINT_PTR)((((UINT_PTR)PPDE) & 0xFFFFFFFFFFFFULL) >> 12) *PTESize + pagebase;
|
|
(UINT_PTR)PPML4E = (UINT_PTR)((((UINT_PTR)PPDPE) & 0xFFFFFFFFFFFFULL) >> 12) *PTESize + pagebase;
|
|
if (PTESize == 8)
|
|
(UINT_PTR)PPDPE = ((((UINT_PTR)PPDE) & 0xFFFFFFFFFFFFULL) >> 12) *PTESize + pagebase;
|
|
else
|
|
(UINT_PTR)PPDPE = 0;
|
|
|
|
#ifdef AMD64
|
|
(UINT_PTR)PPML4E = ((((UINT_PTR)PPDPE) & 0xFFFFFFFFFFFFULL) >> 12) *PTESize + pagebase;
|
|
#else
|
|
(UINT_PTR)PPML4E = 0;
|
|
#endif
|
|
|
|
|
|
#ifdef AMD64
|
|
if ((PPML4E) && (PPML4E->P == 0))
|
|
{
|
|
currentAddress &= 0xffffff8000000000ULL;
|
|
currentAddress += 0x8000000000ULL;
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
if ((PPDPE) && (PPDPE->P == 0))
|
|
{
|
|
currentAddress &= 0xffffffffc0000000ULL;
|
|
currentAddress += 0x40000000;
|
|
continue;
|
|
}
|
|
|
|
if (PPDPE->PS) //some systems have 1GB page support. But not sure windows uses these
|
|
{
|
|
DbgPrint("----->%llx is a 1GB range", currentAddress);
|
|
OnPresentPage(currentAddress, currentAddress + 0x40000000 - 1, PPDPE);
|
|
currentAddress += 0x40000000;
|
|
continue;
|
|
}
|
|
|
|
if (PPDE->P == 0)
|
|
{
|
|
|
|
if (PAGE_SIZE_LARGE == 0x200000)
|
|
currentAddress &= 0xffffffffffe00000ULL;
|
|
else
|
|
currentAddress &= 0xffffffffffc00000ULL;
|
|
|
|
currentAddress += PAGE_SIZE_LARGE;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (PPDE->PS)
|
|
{
|
|
OnPresentPage(currentAddress, currentAddress + PAGE_SIZE_LARGE-1, PPDE);
|
|
currentAddress += PAGE_SIZE_LARGE;
|
|
continue;
|
|
}
|
|
|
|
if (PPTE->P == 0)
|
|
{
|
|
|
|
currentAddress &= 0xfffffffffffff000ULL;
|
|
currentAddress += 0x1000;
|
|
|
|
continue;
|
|
}
|
|
|
|
OnPresentPage(currentAddress, currentAddress + 0xfff, PPTE);
|
|
currentAddress += 0x1000;
|
|
}
|
|
|
|
}
|
|
__finally
|
|
{
|
|
KeDetachProcess();
|
|
}
|
|
}
|
|
__except (1)
|
|
{
|
|
DbgPrint("Excepion while walking the paging layout\n");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
PPENTRY AccessedList = NULL;
|
|
int AccessedListSize;
|
|
|
|
void CleanAccessedList()
|
|
{
|
|
PPENTRY e = AccessedList;
|
|
PPENTRY previous;
|
|
//DbgPrint("Cleaning list");
|
|
|
|
while (e)
|
|
{
|
|
previous = e;
|
|
e = e->Next;
|
|
ExFreePool(previous);
|
|
}
|
|
|
|
AccessedList = NULL;
|
|
AccessedListSize = 0;
|
|
}
|
|
|
|
|
|
void StoreAccessedRanges(UINT_PTR StartAddress, UINT_PTR EndAddress, struct PTEStruct *pageEntry)
|
|
{
|
|
if (pageEntry->A)
|
|
{
|
|
if ((AccessedList) && (AccessedList->Range.EndAddress == StartAddress - 1)) //group
|
|
AccessedList->Range.EndAddress = EndAddress;
|
|
else
|
|
{
|
|
//insert
|
|
PPENTRY e;
|
|
e = ExAllocatePool(PagedPool, sizeof(PENTRY));
|
|
e->Range.StartAddress = StartAddress;
|
|
e->Range.EndAddress = EndAddress;
|
|
e->Next = AccessedList;
|
|
|
|
AccessedList = e;
|
|
AccessedListSize++;
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
int enumAllAccessedPages(PEPROCESS PEProcess)
|
|
{
|
|
#ifdef AMD64
|
|
UINT_PTR MaxAddress = 0x80000000000ULL;
|
|
#else
|
|
UINT_PTR MaxAddress = 0x80000000;
|
|
#endif
|
|
|
|
CleanAccessedList();
|
|
|
|
if (walkPagingLayout(PEProcess, MaxAddress, StoreAccessedRanges))
|
|
{
|
|
//DbgPrint("AccessedListSize=%d\n", AccessedListSize);
|
|
return AccessedListSize*sizeof(PRANGE);
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int getAccessedPageList(PPRANGE List, int ListSizeInBytes)
|
|
{
|
|
PPENTRY e = AccessedList;
|
|
int maxcount = ListSizeInBytes / sizeof(PRANGE);
|
|
int i = 0;
|
|
|
|
// DbgPrint("getAccessedPageList\n");
|
|
|
|
while (e)
|
|
{
|
|
if (i >= maxcount)
|
|
{
|
|
//DbgPrint("%d>=%d", i, maxcount);
|
|
break;
|
|
}
|
|
|
|
//DbgPrint("i=%d (%p -> %p)\n", i, e->Range.StartAddress, e->Range.EndAddress);
|
|
List[i] = e->Range;
|
|
e = e->Next;
|
|
|
|
i++;
|
|
}
|
|
|
|
CleanAccessedList();
|
|
|
|
return i*sizeof(PRANGE);
|
|
}
|
|
|
|
|
|
void MarkPageAsNotAccessed(UINT_PTR StartAddress, UINT_PTR EndAddress, struct PTEStruct *pageEntry)
|
|
{
|
|
pageEntry->A = 0;
|
|
}
|
|
|
|
NTSTATUS markAllPagesAsNeverAccessed(PEPROCESS PEProcess)
|
|
{
|
|
#ifdef AMD64
|
|
UINT_PTR MaxAddress = 0x80000000000ULL;
|
|
#else
|
|
UINT_PTR MaxAddress = 0x80000000;
|
|
#endif
|
|
|
|
if (walkPagingLayout(PEProcess, MaxAddress, MarkPageAsNotAccessed))
|
|
return STATUS_SUCCESS;
|
|
else
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
}
|
|
|
|
|
|
|
|
UINT_PTR FindFirstDifferentAddress(QWORD address, DWORD *protection)
|
|
//scans the pagetable system for the first fully present entry
|
|
//returns 0 when there is no other present address
|
|
{
|
|
int i,ri;
|
|
int pml4index, pagedirpointerindex, pagedirindex, pagetableindex;
|
|
VirtualAddressToIndexes(address, &pml4index, &pagedirpointerindex, &pagedirindex, &pagetableindex);
|
|
#ifndef AMD64
|
|
if (PTESize == 8)
|
|
#endif
|
|
{
|
|
PPDPTE_PAE pml4entry, pagedirpointerentry;
|
|
PPDE_PAE pagedirentry;
|
|
PPTE_PAE pagetableentry;
|
|
*protection = 0;
|
|
|
|
//scan till present or end of memory
|
|
while (1)
|
|
{
|
|
VirtualAddressToPageEntries64(address, &pml4entry, &pagedirpointerentry, &pagedirentry, &pagetableentry);
|
|
VirtualAddressToIndexes(address, &pml4index, &pagedirpointerindex, &pagedirindex, &pagetableindex);
|
|
|
|
if (*protection == 0)
|
|
{
|
|
//get the initial protection
|
|
if ((((pml4entry) && (pml4entry->P)) || (pml4entry == NULL)) && (pagedirpointerentry->P) && (pagedirentry->P) && ((pagedirentry->PS) || (pagetableentry->P)))
|
|
{
|
|
if (pagedirentry->PS)
|
|
{
|
|
if (pagedirentry->RW)
|
|
*protection = PAGE_EXECUTE_READWRITE;
|
|
else
|
|
*protection = PAGE_EXECUTE_READ;
|
|
}
|
|
else
|
|
{
|
|
if (pagetableentry->RW)
|
|
*protection = PAGE_EXECUTE_READWRITE;
|
|
else
|
|
*protection = PAGE_EXECUTE_READ;
|
|
}
|
|
}
|
|
else
|
|
*protection = PAGE_NOACCESS;
|
|
}
|
|
|
|
#ifdef AMD64
|
|
if (pml4entry->P == 0)
|
|
{
|
|
//unreadable at PML4 level
|
|
|
|
if (*protection != PAGE_NOACCESS)
|
|
return address;
|
|
|
|
pagedirpointerindex = 0;
|
|
pagedirindex = 0;
|
|
pagetableindex = 0;
|
|
|
|
for (ri=1, i = pml4index + 1; i < 512; i++, ri++)
|
|
{
|
|
if ((ri >= 512) || (ri < 0))
|
|
DbgBreakPointWithStatus(ri);
|
|
|
|
if (pml4entry[ri].P)
|
|
{
|
|
//found a valid PML4 entry
|
|
//scan for a valid pagedirpointerentry
|
|
pml4index = i;
|
|
address = IndexesToVirtualAddress(pml4index, pagedirpointerindex, pagedirindex, pagetableindex, 0);
|
|
VirtualAddressToPageEntries64(address, &pml4entry, &pagedirpointerentry, &pagedirentry, &pagetableentry);
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
if (pml4entry->P == 0)
|
|
{
|
|
//nothing readable found
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
if (pagedirpointerentry->P == 0)
|
|
{
|
|
if (*protection != PAGE_NOACCESS)
|
|
return (UINT_PTR)address;
|
|
|
|
pagedirindex = 0;
|
|
pagetableindex = 0;
|
|
|
|
for (ri=1, i = pagedirpointerindex + 1; i < 512; i++, ri++)
|
|
{
|
|
if ((ri >= 512) || (ri < 0))
|
|
DbgBreakPointWithStatus(ri);
|
|
|
|
if (pagedirpointerentry[ri].P)
|
|
{
|
|
//found a valid pagedirpointerentry
|
|
pagedirpointerindex = i;
|
|
address = IndexesToVirtualAddress(pml4index, pagedirpointerindex, pagedirindex, pagetableindex, 0);
|
|
VirtualAddressToPageEntries64(address, &pml4entry, &pagedirpointerentry, &pagedirentry, &pagetableentry);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pagedirpointerentry->P == 0)
|
|
{
|
|
#ifdef AMD64
|
|
pagedirpointerindex = 0;
|
|
pml4index++;
|
|
if (pml4index >= 512) return 0; //end of the list
|
|
|
|
address = IndexesToVirtualAddress(pml4index, pagedirpointerindex, pagedirindex, pagetableindex, 0);
|
|
continue; //try the next PML4 entry
|
|
#else
|
|
//nothing readable found
|
|
return 0;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (pagedirentry->P == 0)
|
|
{
|
|
if (*protection != PAGE_NOACCESS)
|
|
return (UINT_PTR)address;
|
|
|
|
pagetableindex = 0;
|
|
for (ri=1, i = pagedirindex + 1; i < 512; i++, ri++)
|
|
{
|
|
if ((ri >= 512) || (ri < 0))
|
|
DbgBreakPointWithStatus(ri);
|
|
|
|
if (pagedirentry[ri].P)
|
|
{
|
|
//found a valid pagedirentry
|
|
pagedirindex = i;
|
|
address = IndexesToVirtualAddress(pml4index, pagedirpointerindex, pagedirindex, pagetableindex, 0);
|
|
VirtualAddressToPageEntries64(address, &pml4entry, &pagedirpointerentry, &pagedirentry, &pagetableentry);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pagedirentry->P == 0)
|
|
{
|
|
pagedirindex = 0;
|
|
pagedirpointerindex++;
|
|
if (pagedirpointerindex >= 512)
|
|
{
|
|
pagedirpointerindex = 0;
|
|
pml4index++;
|
|
if (pml4index >= 512) return 0; //end of the list
|
|
}
|
|
|
|
address = IndexesToVirtualAddress(pml4index, pagedirpointerindex, pagedirindex, pagetableindex, 0);
|
|
continue; //try the next pagedirpointer entry
|
|
}
|
|
}
|
|
|
|
if (pagedirentry->PS)
|
|
{
|
|
if (*protection == PAGE_NOACCESS)
|
|
return (UINT_PTR)address;
|
|
|
|
if ((pagedirentry->RW) && (*protection != PAGE_EXECUTE_READWRITE))
|
|
return (UINT_PTR)address;
|
|
|
|
//go to the next one
|
|
pagedirindex++;
|
|
if (pagedirindex >= 512)
|
|
{
|
|
pagedirindex = 0;
|
|
pagedirpointerindex++;
|
|
if (pagedirpointerindex >= 512)
|
|
{
|
|
pagedirpointerindex = 0;
|
|
pml4index++;
|
|
if (pml4index >= 512)
|
|
return 0; //end of the list
|
|
}
|
|
}
|
|
address = IndexesToVirtualAddress(pml4index, pagedirpointerindex, pagedirindex, pagetableindex, 0);
|
|
continue;
|
|
}
|
|
|
|
|
|
if (pagetableentry->P == 0)
|
|
{
|
|
if (*protection != PAGE_NOACCESS)
|
|
return (UINT_PTR)address;
|
|
|
|
for (ri=1, i = pagetableindex + 1; i < 512; i++, ri++)
|
|
{
|
|
if ((ri >= 512) || (ri < 0))
|
|
DbgBreakPointWithStatus(ri);
|
|
|
|
if (pagetableentry[ri].P)
|
|
{
|
|
//found a valid pagetable entry
|
|
pagetableindex = i;
|
|
address = IndexesToVirtualAddress(pml4index, pagedirpointerindex, pagedirindex, pagetableindex, 0);
|
|
VirtualAddressToPageEntries64(address, &pml4entry, &pagedirpointerentry, &pagedirentry, &pagetableentry);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pagetableentry->P == 0)
|
|
{
|
|
pagetableindex = 0;
|
|
pagedirindex++;
|
|
if (pagedirindex >= 512)
|
|
{
|
|
pagedirindex = 0;
|
|
pagedirpointerindex++;
|
|
if (pagedirpointerindex >= 512)
|
|
{
|
|
pagedirpointerindex = 0;
|
|
pml4index++;
|
|
if (pml4index >= 512)
|
|
return 0; //end of the list
|
|
}
|
|
}
|
|
address = IndexesToVirtualAddress(pml4index, pagedirpointerindex, pagedirindex, pagetableindex, 0);
|
|
continue; //try the next pagedir entry
|
|
}
|
|
}
|
|
|
|
//still here so a present page
|
|
if (*protection == PAGE_NOACCESS)
|
|
return (UINT_PTR)address;
|
|
|
|
if ((pagetableentry->RW) && (*protection != PAGE_EXECUTE_READWRITE))
|
|
return (UINT_PTR)address;
|
|
|
|
//next entry
|
|
pagetableindex++;
|
|
if (pagetableindex >= 512)
|
|
{
|
|
pagetableindex = 0;
|
|
pagedirindex++;
|
|
if (pagedirindex >= 512)
|
|
{
|
|
pagedirindex = 0;
|
|
pagedirpointerindex++;
|
|
if (pagedirpointerindex >= 512)
|
|
{
|
|
pagedirpointerindex = 0;
|
|
pml4index++;
|
|
if (pml4index >= 512)
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
address = IndexesToVirtualAddress(pml4index, pagedirpointerindex, pagedirindex, pagetableindex, 0);
|
|
}
|
|
}
|
|
#ifndef AMD64
|
|
else
|
|
{
|
|
PPDE pagedirentry;
|
|
PPTE pagetableentry;
|
|
while (1)
|
|
{
|
|
VirtualAddressToPageEntries32((UINT_PTR)address, &pagedirentry, &pagetableentry);
|
|
VirtualAddressToIndexes((UINT_PTR)address, &pml4index, &pagedirpointerindex, &pagedirindex, &pagetableindex);
|
|
|
|
if (*protection == 0)
|
|
{
|
|
//get the initial protection
|
|
if ((pagedirentry->P) && ((pagedirentry->PS) || (pagetableentry->P)))
|
|
{
|
|
if (pagedirentry->PS)
|
|
{
|
|
if (pagedirentry->RW)
|
|
*protection = PAGE_EXECUTE_READWRITE;
|
|
else
|
|
*protection = PAGE_EXECUTE_READ;
|
|
}
|
|
else
|
|
{
|
|
if (pagetableentry->RW)
|
|
*protection = PAGE_EXECUTE_READWRITE;
|
|
else
|
|
*protection = PAGE_EXECUTE_READ;
|
|
}
|
|
}
|
|
else
|
|
*protection = PAGE_NOACCESS;
|
|
}
|
|
|
|
if (pagedirentry->P == 0)
|
|
{
|
|
if (*protection != PAGE_NOACCESS)
|
|
return (UINT_PTR)address;
|
|
|
|
pagetableindex = 0;
|
|
for (ri=1, i = pagedirindex + 1; i < 1024; i++, ri++)
|
|
{
|
|
if (pagedirentry[ri].P)
|
|
{
|
|
//found a valid pagedirentry
|
|
pagedirindex = i;
|
|
address = IndexesToVirtualAddress(0, 0, pagedirindex, pagetableindex, 0);
|
|
VirtualAddressToPageEntries32((UINT_PTR)address, &pagedirentry, &pagetableentry);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pagedirentry->P == 0)
|
|
return 0; //end of the list
|
|
}
|
|
|
|
if (pagedirentry->PS)
|
|
{
|
|
if (*protection == PAGE_NOACCESS)
|
|
return (UINT_PTR)address;
|
|
|
|
if ((pagedirentry->RW) && (*protection != PAGE_EXECUTE_READWRITE))
|
|
return (UINT_PTR)address;
|
|
|
|
//go to the next one
|
|
pagedirindex++;
|
|
if (pagedirindex >= 1024)
|
|
return 0;
|
|
|
|
address = IndexesToVirtualAddress(0, 0, pagedirindex, pagetableindex, 0);
|
|
}
|
|
|
|
|
|
if (pagetableentry->P == 0)
|
|
{
|
|
if (*protection != PAGE_NOACCESS)
|
|
return (UINT_PTR)address;
|
|
|
|
for (ri=1, i = pagetableindex + 1; i < 1024; i++, ri++)
|
|
{
|
|
if (pagetableentry[ri].P)
|
|
{
|
|
//found a valid pagetable entry
|
|
pagetableindex = i;
|
|
address = IndexesToVirtualAddress(0, 0, pagedirindex, pagetableindex, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pagetableentry->P == 0)
|
|
{
|
|
pagetableindex = 0;
|
|
pagedirindex++;
|
|
if (pagedirindex >= 1024)
|
|
return 0;
|
|
|
|
address = IndexesToVirtualAddress(0, 0, pagedirindex, pagetableindex, 0);
|
|
continue; //try the next pagedir entry
|
|
}
|
|
}
|
|
|
|
//still here so a present page
|
|
if (*protection == PAGE_NOACCESS)
|
|
return (UINT_PTR)address;
|
|
|
|
if ((pagetableentry->RW) && (*protection != PAGE_EXECUTE_READWRITE))
|
|
return (UINT_PTR)address;
|
|
|
|
//next entry
|
|
pagetableindex++;
|
|
if (pagetableindex >= 1024)
|
|
{
|
|
pagetableindex = 0;
|
|
pagedirindex++;
|
|
if (pagedirindex >= 1024)
|
|
return 0;
|
|
}
|
|
address = IndexesToVirtualAddress(pml4index, pagedirpointerindex, pagedirindex, pagetableindex, 0);
|
|
|
|
}
|
|
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
BOOLEAN GetMemoryRegionData(DWORD PID,PEPROCESS PEProcess, PVOID mempointer,ULONG *regiontype, UINT_PTR *memorysize,UINT_PTR *baseaddress)
|
|
{
|
|
UINT_PTR CurrentAddress;
|
|
KAPC_STATE apc_state;
|
|
PEPROCESS selectedprocess=PEProcess;
|
|
|
|
if (getPageTableBase() == 0)
|
|
{
|
|
DbgPrint("GetMemoryRegionData failed because pagebase == 0");
|
|
return FALSE;
|
|
}
|
|
|
|
if (PEProcess==NULL)
|
|
{
|
|
if (!NT_SUCCESS(PsLookupProcessByProcessId((PVOID)(UINT_PTR)PID,&selectedprocess)))
|
|
return FALSE; //couldn't get the PID
|
|
}
|
|
|
|
*baseaddress=(UINT_PTR)mempointer & (UINT_PTR)(~0xfff);
|
|
*memorysize=0;
|
|
*regiontype=0;
|
|
//switch context to the target process
|
|
|
|
RtlZeroMemory(&apc_state,sizeof(apc_state));
|
|
|
|
__try
|
|
{
|
|
KeAttachProcess((PEPROCESS)selectedprocess);
|
|
__try
|
|
{
|
|
CurrentAddress=FindFirstDifferentAddress(*baseaddress, regiontype);
|
|
*memorysize = CurrentAddress-*baseaddress;
|
|
}
|
|
__finally
|
|
{
|
|
KeDetachProcess();
|
|
if (PEProcess==NULL) //no valid peprocess was given so I made a reference, so lets also dereference
|
|
ObDereferenceObject(selectedprocess);
|
|
}
|
|
}
|
|
__except(1)
|
|
{
|
|
DbgPrint("Exception in GetMemoryRegionData\n");
|
|
DbgPrint("mempointer=%p",mempointer);
|
|
}
|
|
|
|
return 0;
|
|
} |