mirror of
https://github.com/WithSecureLabs/ModuleStomping
synced 2026-06-06 17:00:48 +00:00
Add materials for final part of series, part 3
This commit is contained in:
@@ -0,0 +1,307 @@
|
||||
#include "pch.h"
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "..\driver\public.h"
|
||||
#include "findPFNDatabase.h"
|
||||
|
||||
class statistics {
|
||||
public:
|
||||
statistics()
|
||||
{
|
||||
scannedPages = ignoredPagesNX = scannedProcesses = modifiedPages = 0;
|
||||
}
|
||||
|
||||
unsigned int scannedPages;
|
||||
unsigned int ignoredPagesNX;
|
||||
unsigned int scannedProcesses;
|
||||
unsigned int modifiedPages;
|
||||
};
|
||||
|
||||
class modifiedPage {
|
||||
public:
|
||||
modifiedPage(DWORD newProcessID, wchar_t* newModuleName, void* newPageBase, BYTE newSectionName[8], unsigned long long newSectionOffset);
|
||||
|
||||
unsigned long processID;
|
||||
std::wstring moduleName;
|
||||
|
||||
unsigned long long pageBase;
|
||||
|
||||
std::wstring sectionName;
|
||||
unsigned long long sectionOffset;
|
||||
};
|
||||
|
||||
modifiedPage::modifiedPage(DWORD newProcessID, wchar_t* newModuleName, void* newPageBase, BYTE newSectionName[8], unsigned long long newSectionOffset)
|
||||
: processID(newProcessID), moduleName(newModuleName), pageBase((unsigned long long)newPageBase), sectionName(L""), sectionOffset(newSectionOffset)
|
||||
{
|
||||
wchar_t sectionNameCleaned[9];
|
||||
memset(sectionNameCleaned, 0, 9 * sizeof(wchar_t));
|
||||
wsprintf(sectionNameCleaned, L"%.8s", newSectionName);
|
||||
sectionName.append(sectionNameCleaned);
|
||||
}
|
||||
|
||||
BOOL EnableDebugPrivilege(BOOL bEnable)
|
||||
{
|
||||
HANDLE hToken = nullptr;
|
||||
LUID luid;
|
||||
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) return FALSE;
|
||||
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) return FALSE;
|
||||
|
||||
TOKEN_PRIVILEGES tokenPriv;
|
||||
tokenPriv.PrivilegeCount = 1;
|
||||
tokenPriv.Privileges[0].Luid = luid;
|
||||
tokenPriv.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;
|
||||
|
||||
if (!AdjustTokenPrivileges(hToken, FALSE, &tokenPriv, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int scanProcess(HANDLE driverHnd, DWORD targetPID, HANDLE toScanHandle, std::vector<modifiedPage> *resultsOut, statistics* stats)
|
||||
{
|
||||
DWORD cbNeeded;
|
||||
int s = EnumProcessModules(toScanHandle, NULL, 0, &cbNeeded);
|
||||
if (s == 0)
|
||||
{
|
||||
printf("Couldn't call EnumProcessModules to get buffer size, gle %d\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
HMODULE* moduleList = (HMODULE*)malloc(cbNeeded);
|
||||
memset(moduleList, 0, cbNeeded);
|
||||
s = EnumProcessModules(toScanHandle, moduleList, cbNeeded, &cbNeeded);
|
||||
if (s == 0)
|
||||
{
|
||||
// This'll happen sometimes if there's a module loaded between our calls.
|
||||
// TODO: we can retry in this case.
|
||||
printf("Couldn't call EnumProcessModules to get modules, gle %d.\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (HMODULE* thisModPtr = &moduleList[0]; thisModPtr < &moduleList[cbNeeded / sizeof(HMODULE)]; thisModPtr++)
|
||||
{
|
||||
HMODULE thisModule = *thisModPtr;
|
||||
TCHAR szModName[MAX_PATH];
|
||||
memset(szModName, 0, MAX_PATH * sizeof(TCHAR));
|
||||
if (GetModuleFileNameEx(toScanHandle, thisModule, szModName, sizeof(szModName) / sizeof(TCHAR)) == 0)
|
||||
{
|
||||
printf("GetModuleFileNameEx failed, GLE %d\n", GetLastError());
|
||||
continue;
|
||||
}
|
||||
IMAGE_DOS_HEADER mz;
|
||||
SIZE_T bytesRead;
|
||||
if (!ReadProcessMemory(toScanHandle, thisModule, &mz, sizeof(IMAGE_DOS_HEADER), &bytesRead))
|
||||
{
|
||||
printf("Can't read module MZ header\n");
|
||||
return -1;
|
||||
}
|
||||
if (mz.e_magic != IMAGE_DOS_SIGNATURE)
|
||||
{
|
||||
printf("MZ header not found\n");
|
||||
continue;
|
||||
}
|
||||
IMAGE_NT_HEADERS pe;
|
||||
unsigned long long peAddress = (((unsigned long long)thisModule) + mz.e_lfanew);
|
||||
if (!ReadProcessMemory(toScanHandle, (void*)peAddress, &pe, sizeof(IMAGE_NT_HEADERS), &bytesRead))
|
||||
{
|
||||
printf("Can't read module PE header\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pe.Signature != IMAGE_NT_SIGNATURE)
|
||||
{
|
||||
printf("PE header not found\n");
|
||||
continue;
|
||||
}
|
||||
IMAGE_SECTION_HEADER* sect;
|
||||
unsigned long long firstSectionAddress = peAddress + FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + sizeof(IMAGE_OPTIONAL_HEADER);
|
||||
sect = (IMAGE_SECTION_HEADER*)malloc(sizeof(IMAGE_SECTION_HEADER) * pe.FileHeader.NumberOfSections);
|
||||
if (!ReadProcessMemory(toScanHandle, (LPCVOID)(firstSectionAddress), sect, sizeof(IMAGE_SECTION_HEADER) * pe.FileHeader.NumberOfSections, &bytesRead))
|
||||
{
|
||||
printf("Can't read first section of module\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (unsigned long sectionIndex = 0; sectionIndex < pe.FileHeader.NumberOfSections; sectionIndex++)
|
||||
{
|
||||
IMAGE_SECTION_HEADER* thisSection = §[sectionIndex];
|
||||
unsigned long long relocatedSectionBase = thisSection->VirtualAddress + (unsigned long long)thisModule;
|
||||
|
||||
// We are interested only in executable sections.
|
||||
// TODO: check that discardable pages are zero'ed out?
|
||||
// TODO: check that non-executable pages haven't been made executable?
|
||||
if ((thisSection->Characteristics & IMAGE_SCN_MEM_EXECUTE) == 0)
|
||||
{
|
||||
stats->ignoredPagesNX += (thisSection->SizeOfRawData / 0x1000);
|
||||
// printf("%ls!%s (at %p) is not executable, skipping\n", szModName, thisSection->Name, relocatedSectionBase);
|
||||
continue;
|
||||
}
|
||||
//printf("scanning %ls!%s (at %p), size 0x%08lx\n", szModName, thisSection->Name, relocatedSectionBase, thisSection->SizeOfRawData);
|
||||
|
||||
int dirtyPages = 0;
|
||||
int errorPages = 0;
|
||||
getPageInfoRequest req;
|
||||
req.pageToCheck = relocatedSectionBase;
|
||||
// Work out how many pages we will check
|
||||
req.numberOfPagesToCheck = thisSection->Misc.VirtualSize / 0x1000;
|
||||
if (thisSection->Misc.VirtualSize % 0x1000 != 0)
|
||||
req.numberOfPagesToCheck++;
|
||||
req.targetPID = targetPID;
|
||||
|
||||
getPageInfoResponse* resp = (getPageInfoResponse*)malloc(sizeof(getPageInfoRequest) * req.numberOfPagesToCheck);
|
||||
memset(resp, 0x00, sizeof(getPageInfoResponse) * req.numberOfPagesToCheck);
|
||||
|
||||
DWORD bytesRet;
|
||||
s = DeviceIoControl(driverHnd, IOCTL_DRIVER_QUERY_VA, &req, sizeof(req), resp, sizeof(getPageInfoResponse) * req.numberOfPagesToCheck, &bytesRet, NULL);
|
||||
if (s == 0)
|
||||
{
|
||||
errorPages++;
|
||||
printf("DeviceIoControl failed, GLE %d\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
stats->scannedPages += req.numberOfPagesToCheck;
|
||||
|
||||
for (unsigned int n = 0; n < req.numberOfPagesToCheck; n++)
|
||||
{
|
||||
unsigned long long pageAddress = relocatedSectionBase + (n * 0x1000);
|
||||
|
||||
if (!resp[n].isValid)
|
||||
{
|
||||
printf("Page at 0x%016llx (%ls!%s) not valid (maybe it's paged out?) 0x%08lx\n", pageAddress, szModName, thisSection->Name, thisSection->Characteristics);
|
||||
errorPages++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (resp[n].isDirty)
|
||||
{
|
||||
dirtyPages++;
|
||||
resultsOut->push_back(modifiedPage(targetPID, (wchar_t*)szModName, (void*)pageAddress, thisSection->Name, (pageAddress - relocatedSectionBase)));
|
||||
stats->modifiedPages++;
|
||||
}
|
||||
}
|
||||
|
||||
// if (dirtyPages == 0)
|
||||
// printf("Module %ls: OK\n", szModName);
|
||||
// else
|
||||
// printf("Module %ls: detected %d dirty pages!\n", szModName, dirtyPages);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int setPFNDatabase(HANDLE driverHnd, unsigned long long PFNDatabaseStart)
|
||||
{
|
||||
setPFNDatabaseRequest req;
|
||||
req.offsetToMmPfnDatabaseInNtDllFromExAllocatePoolWithTag = PFNDatabaseStart;
|
||||
|
||||
DWORD bytesRet;
|
||||
int s = DeviceIoControl(driverHnd, IOCTL_DRIVER_SET_PFN_DATABASE, &req, sizeof(req), NULL, 0, &bytesRet, NULL);
|
||||
if (s == 0)
|
||||
{
|
||||
printf("Failed to set PFN database to 0x%016llx: GLE %d\n", PFNDatabaseStart, GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
EnableDebugPrivilege(TRUE);
|
||||
|
||||
HANDLE driverHnd = CreateFile(L"\\\\.\\cowspot", GENERIC_ALL, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||
if (driverHnd == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
printf("Couldn't open driver device '%ls', gle %d\n", DOS_DEVICE_NAME, GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (setPFNDatabase(driverHnd, findPFNDatabase()) != 0)
|
||||
return -1;
|
||||
|
||||
HANDLE snapshotHnd = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
|
||||
if (snapshotHnd == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
printf("CreateToolhelp32Snapshot failed, GLE %d\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
PROCESSENTRY32 proc;
|
||||
memset(&proc, 0, sizeof(PROCESSENTRY32));
|
||||
proc.dwSize = sizeof(PROCESSENTRY32);
|
||||
|
||||
if (!Process32First(snapshotHnd, &proc))
|
||||
{
|
||||
printf("Process32First failed, GLE %d\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
statistics stat;
|
||||
std::vector<modifiedPage> results;
|
||||
|
||||
unsigned long start = GetTickCount();
|
||||
|
||||
while (Process32Next(snapshotHnd, &proc))
|
||||
{
|
||||
HANDLE toScanHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, proc.th32ProcessID);
|
||||
if (toScanHandle == NULL)
|
||||
{
|
||||
printf("Couldn't open target process with PID %d ('%ls'), gle %d\n", proc.th32ProcessID, proc.szExeFile, GetLastError());
|
||||
continue;
|
||||
}
|
||||
|
||||
stat.scannedProcesses++;
|
||||
if (scanProcess(driverHnd, proc.th32ProcessID, toScanHandle, &results, &stat) != 0)
|
||||
printf("Failed to scan process '%ls'\n", proc.szExeFile);
|
||||
// else
|
||||
// printf("Scanned process '%ls'\n", proc.szExeFile);
|
||||
CloseHandle(toScanHandle);
|
||||
}
|
||||
|
||||
CloseHandle(snapshotHnd);
|
||||
|
||||
unsigned long end = GetTickCount();
|
||||
|
||||
printf("Scan took %dms\n", (end - start));
|
||||
|
||||
// Print some stats and the results.
|
||||
printf("Scanned %d pages, ignored %d NX pages (total %d). Found %d modified pages.\n", stat.scannedPages, stat.ignoredPagesNX, stat.ignoredPagesNX + stat.scannedPages, stat.modifiedPages);
|
||||
for (unsigned int n = 0; n < results.size(); n++)
|
||||
{
|
||||
modifiedPage thisModifiedPage = results[n];
|
||||
printf("PID %04d module '%ls', section %S, offset 0x%08llux\n", thisModifiedPage.processID, thisModifiedPage.moduleName.c_str(), thisModifiedPage.sectionName.c_str(), thisModifiedPage.sectionOffset);
|
||||
/*
|
||||
HANDLE toScanHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, thisModifiedPage.processID);
|
||||
if (toScanHandle == NULL)
|
||||
{
|
||||
printf("Couldn't open target process with PID %d ('%ls'), gle %d\n", proc.th32ProcessID, proc.szExeFile, GetLastError());
|
||||
continue;
|
||||
}
|
||||
SIZE_T bytesRead;
|
||||
unsigned char* pageContents[0x2000];
|
||||
memset(pageContents, 0, 0x2000);
|
||||
if (!ReadProcessMemory(toScanHandle, (LPCVOID)thisModifiedPage.pageBase, pageContents, 0x2000, &bytesRead))
|
||||
{
|
||||
printf("ReadProcessMemory failed\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
for (unsigned int n = 0; n < 0x2001; n++)
|
||||
{
|
||||
printf("0x%02hhx ", (unsigned)pageContents[n]);
|
||||
if (n % 0x10 == 0)
|
||||
printf("\n0x%08lx: ", n);
|
||||
}
|
||||
CloseHandle(toScanHandle);*/
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{BBBF57E1-3EC9-4C5C-8305-AD434ED61842}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>UI</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<SpectreMitigation>false</SpectreMitigation>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<SpectreMitigation>false</SpectreMitigation>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<SpectreMitigation>false</SpectreMitigation>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<SpectreMitigation>false</SpectreMitigation>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IncludePath>$(VSInstallDir)\DIA SDK\Include;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>$(VSInstallDir)\DIA SDK\Include;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>$(VSInstallDir)\DIA SDK\Include;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IncludePath>$(VSInstallDir)\DIA SDK\Include;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="findPFNDatabase.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="findPFNDatabase.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="UI.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,106 @@
|
||||
#include "pch.h"
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
#include <dia2.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
unsigned long findSymbol(IDiaSymbol* g_pGlobalSymbol, const wchar_t* symbolName);
|
||||
|
||||
unsigned long long findPFNDatabase()
|
||||
{
|
||||
IDiaDataSource *g_pDiaDataSource;
|
||||
IDiaSession *g_pDiaSession;
|
||||
IDiaSymbol *g_pGlobalSymbol;
|
||||
|
||||
// Assemble the path to ntoskrnl.exe. It'll be in System32.
|
||||
wchar_t systemDir[MAX_PATH];
|
||||
GetSystemDirectory(systemDir, MAX_PATH);
|
||||
std::wstring exeFilename(L"");
|
||||
exeFilename.append(systemDir);
|
||||
exeFilename.append(L"\\ntoskrnl.exe");
|
||||
|
||||
// Assemble the symbol path. We use the current directory as a cache path.
|
||||
wchar_t curPath[MAX_PATH];
|
||||
GetCurrentDirectory(MAX_PATH, curPath);
|
||||
std::wstring symPath(L"");
|
||||
symPath.append(L"symsrv*symsrv.dll*");
|
||||
symPath.append(curPath);
|
||||
symPath.append(L"*http://msdl.microsoft.com/download/symbols");
|
||||
|
||||
HRESULT hr = CoInitialize(NULL);
|
||||
|
||||
hr = CoCreateInstance(__uuidof(DiaSource), NULL, CLSCTX_INPROC_SERVER, __uuidof(IDiaDataSource), (void **)&g_pDiaDataSource);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
printf("CoCreateInstance failed for UUID of IDiaDataSource - HRESULT is %08X\n", hr);
|
||||
if (hr == REGDB_E_CLASSNOTREG)
|
||||
printf("This means the DIA class is not registered. You may need to register it via regsvr32.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Loading PDBs..\n");
|
||||
hr = g_pDiaDataSource->loadDataForExe(exeFilename.c_str(), symPath.c_str(), NULL);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
printf("loadDataForExe failed for file '%ls' - HRESULT is %08X\n", exeFilename.c_str(), hr);
|
||||
if (hr == E_PDB_NOT_FOUND)
|
||||
printf("This is E_PDB_NOT_FOUND. Check that you have internet connectivity, and the correct symbol server configured.\n");
|
||||
return false;
|
||||
}
|
||||
printf("Loading PDBs complete.\n");
|
||||
|
||||
hr = (g_pDiaDataSource)->openSession(&g_pDiaSession);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
printf("openSession failed - HRESULT is %08X\n", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
g_pDiaSession->put_loadAddress(0x0);
|
||||
|
||||
hr = (g_pDiaSession)->get_globalScope(&g_pGlobalSymbol);
|
||||
|
||||
if (hr != S_OK)
|
||||
{
|
||||
printf("get_globalScope failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now we can resolve the symbols we want.
|
||||
unsigned long long MmPFNDatabase = findSymbol(g_pGlobalSymbol, L"MmPfnDatabase");
|
||||
unsigned long long ExAllocatePoolWithTag = findSymbol(g_pGlobalSymbol, L"ExAllocatePoolWithTag");
|
||||
|
||||
if (MmPFNDatabase == 0)
|
||||
printf("Unable to resolve MmPFNDatabase");
|
||||
if (ExAllocatePoolWithTag == 0)
|
||||
printf("Unable to resolve ExAllocatePoolWithTag");
|
||||
if (MmPFNDatabase == 0 || ExAllocatePoolWithTag == 0)
|
||||
return false;
|
||||
|
||||
return ExAllocatePoolWithTag - MmPFNDatabase;
|
||||
}
|
||||
|
||||
unsigned long findSymbol(IDiaSymbol* g_pGlobalSymbol, const wchar_t* symbolName)
|
||||
{
|
||||
IDiaEnumSymbols *pEnumSymbols;
|
||||
|
||||
if (FAILED(g_pGlobalSymbol->findChildren(SymTagPublicSymbol, symbolName, nsNone, &pEnumSymbols)))
|
||||
return false;
|
||||
|
||||
IDiaSymbol *pCompiland;
|
||||
unsigned long celt;
|
||||
|
||||
if (FAILED(pEnumSymbols->Next(1, &pCompiland, &celt)) || (celt != 1))
|
||||
return false;
|
||||
|
||||
unsigned long symRVA;
|
||||
pCompiland->get_relativeVirtualAddress(&symRVA);
|
||||
|
||||
pCompiland->Release();
|
||||
pEnumSymbols->Release();
|
||||
|
||||
return symRVA;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
unsigned long long findPFNDatabase();
|
||||
@@ -0,0 +1 @@
|
||||
#include "pch.h"
|
||||
+20
@@ -3,6 +3,10 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.28010.2046
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "driver", "driver\driver.vcxproj", "{65A559E4-6946-4252-BFBE-C3B5D1B8108C}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UI", "UI\UI.vcxproj", "{BBBF57E1-3EC9-4C5C-8305-AD434ED61842}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "inject", "inject\inject.vcxproj", "{35928C18-5D5D-4BC6-88CE-E5BBE00C0446}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "inject_simple", "inject_simple\inject_simple.vcxproj", "{5C90611E-0874-4618-9C3D-B1385C83FBDF}"
|
||||
@@ -17,6 +21,22 @@ Global
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{65A559E4-6946-4252-BFBE-C3B5D1B8108C}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{65A559E4-6946-4252-BFBE-C3B5D1B8108C}.Debug|x64.Build.0 = Debug|x64
|
||||
{65A559E4-6946-4252-BFBE-C3B5D1B8108C}.Debug|x64.Deploy.0 = Debug|x64
|
||||
{65A559E4-6946-4252-BFBE-C3B5D1B8108C}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{65A559E4-6946-4252-BFBE-C3B5D1B8108C}.Release|x64.ActiveCfg = Release|x64
|
||||
{65A559E4-6946-4252-BFBE-C3B5D1B8108C}.Release|x64.Build.0 = Release|x64
|
||||
{65A559E4-6946-4252-BFBE-C3B5D1B8108C}.Release|x64.Deploy.0 = Release|x64
|
||||
{65A559E4-6946-4252-BFBE-C3B5D1B8108C}.Release|x86.ActiveCfg = Release|x64
|
||||
{BBBF57E1-3EC9-4C5C-8305-AD434ED61842}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{BBBF57E1-3EC9-4C5C-8305-AD434ED61842}.Debug|x64.Build.0 = Debug|x64
|
||||
{BBBF57E1-3EC9-4C5C-8305-AD434ED61842}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{BBBF57E1-3EC9-4C5C-8305-AD434ED61842}.Debug|x86.Build.0 = Debug|Win32
|
||||
{BBBF57E1-3EC9-4C5C-8305-AD434ED61842}.Release|x64.ActiveCfg = Release|x64
|
||||
{BBBF57E1-3EC9-4C5C-8305-AD434ED61842}.Release|x64.Build.0 = Release|x64
|
||||
{BBBF57E1-3EC9-4C5C-8305-AD434ED61842}.Release|x86.ActiveCfg = Release|Win32
|
||||
{BBBF57E1-3EC9-4C5C-8305-AD434ED61842}.Release|x86.Build.0 = Release|Win32
|
||||
{35928C18-5D5D-4BC6-88CE-E5BBE00C0446}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{35928C18-5D5D-4BC6-88CE-E5BBE00C0446}.Debug|x64.Build.0 = Debug|x64
|
||||
{35928C18-5D5D-4BC6-88CE-E5BBE00C0446}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
|
||||
Binary file not shown.
+395
@@ -0,0 +1,395 @@
|
||||
#include <Ntifs.h>
|
||||
#include <Ntddk.h>
|
||||
//#include <aux_klib.h>
|
||||
#include "driver.h"
|
||||
#include "public.h"
|
||||
|
||||
_Use_decl_annotations_ DRIVER_INITIALIZE DriverEntry;
|
||||
_Use_decl_annotations_ DRIVER_UNLOAD DriverUnload;
|
||||
_Dispatch_type_(IRP_MJ_CREATE) DRIVER_DISPATCH irp_mj_create;
|
||||
_Dispatch_type_(IRP_MJ_CLOSE) DRIVER_DISPATCH irp_mj_close;
|
||||
_Dispatch_type_(IRP_MJ_DEVICE_CONTROL) DRIVER_DISPATCH irp_mj_device_control;
|
||||
|
||||
NTSTATUS queryVA(getPageInfoRequest* params, getPageInfoResponse* response);
|
||||
NTSTATUS queryVAFromIRP(PIRP Irp);
|
||||
NTSTATUS setPFNDatabase(setPFNDatabaseRequest* req);
|
||||
NTSTATUS setPFNDatabaseFromIRP(PIRP Irp);
|
||||
int isTableEntryValid(unsigned long long entry);
|
||||
unsigned long long getChildTableFromTableEntry(unsigned long long entry);
|
||||
__drv_requiresIRQL(APC_LEVEL) NTSTATUS readMemoryFromPhysical(unsigned long long address, char* errMsg, void* tableOut);
|
||||
|
||||
privateInfo prv;
|
||||
|
||||
// TODO: Get PFN structure info via PDBs instead of hardcoding it here.
|
||||
struct PFN
|
||||
{
|
||||
// 0x00
|
||||
unsigned long long padding1;
|
||||
// 0x08
|
||||
unsigned long long PTEAddress;
|
||||
// 0x10
|
||||
unsigned long long OriginalPte;
|
||||
// 0x18
|
||||
unsigned long long u2;
|
||||
// 0x20 - u3
|
||||
unsigned short referenceCount;
|
||||
unsigned char e1;
|
||||
unsigned char e3;
|
||||
unsigned long e4; // or e2
|
||||
// 0x28
|
||||
unsigned long long u4;
|
||||
};
|
||||
|
||||
_Use_decl_annotations_ NTSTATUS DriverEntry(_In_ struct _DRIVER_OBJECT *DriverObject, _In_ PUNICODE_STRING RegistryPath)
|
||||
{
|
||||
NTSTATUS s;
|
||||
UNICODE_STRING deviceName;
|
||||
UNICODE_STRING DOSDeviceName;
|
||||
|
||||
UNREFERENCED_PARAMETER(RegistryPath);
|
||||
|
||||
DriverObject->DriverUnload = DriverUnload;
|
||||
DriverObject->MajorFunction[IRP_MJ_CREATE] = irp_mj_create;
|
||||
DriverObject->MajorFunction[IRP_MJ_CLOSE] = irp_mj_close;
|
||||
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = irp_mj_device_control;
|
||||
|
||||
// Initialise our 'private' data, shared throughout the driver
|
||||
memset(&prv, 0, sizeof(privateInfo));
|
||||
|
||||
// Create our device and the DOS symlink to it, as usual
|
||||
RtlInitUnicodeString(&deviceName, DEVICE_NAME);
|
||||
s = IoCreateDevice(DriverObject, 0, &deviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &prv.deviceObject);
|
||||
if (!NT_SUCCESS(s))
|
||||
{
|
||||
DbgPrint("Failed IoCreateDevice: 0x%08lx\n", s);
|
||||
return s;
|
||||
}
|
||||
RtlInitUnicodeString(&DOSDeviceName, DOS_DEVICE_NAME);
|
||||
|
||||
s = IoCreateSymbolicLink(&DOSDeviceName, &deviceName);
|
||||
if (!NT_SUCCESS(s))
|
||||
{
|
||||
IoDeleteDevice(prv.deviceObject);
|
||||
DbgPrint("Failed IoCreateSymbolicLink: 0x%08lx\n", s);
|
||||
return s;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
VOID DriverUnload(_In_ struct _DRIVER_OBJECT *DriverObject)
|
||||
{
|
||||
UNICODE_STRING DOSDeviceName;
|
||||
|
||||
UNREFERENCED_PARAMETER(DriverObject);
|
||||
|
||||
RtlInitUnicodeString(&DOSDeviceName, DOS_DEVICE_NAME);
|
||||
IoDeleteSymbolicLink(&DOSDeviceName);
|
||||
IoDeleteDevice(prv.deviceObject);
|
||||
}
|
||||
|
||||
_Use_decl_annotations_ NTSTATUS irp_mj_create(PDEVICE_OBJECT DeviceObject, PIRP Irp)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(DeviceObject);
|
||||
|
||||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||||
Irp->IoStatus.Information = 0;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
_Use_decl_annotations_ NTSTATUS irp_mj_close(PDEVICE_OBJECT DeviceObject, PIRP Irp)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(DeviceObject);
|
||||
|
||||
// FIXME: Make sure all pending requests on this handle are complete
|
||||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||||
Irp->IoStatus.Information = 0;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS setPFNDatabaseFromIRP(PIRP Irp)
|
||||
{
|
||||
PIO_STACK_LOCATION irpStack;
|
||||
setPFNDatabaseRequest inputBuffer;
|
||||
int bytesReturned;
|
||||
NTSTATUS s;
|
||||
|
||||
bytesReturned = 0;
|
||||
|
||||
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(setPFNDatabaseRequest))
|
||||
{
|
||||
s = STATUS_BUFFER_TOO_SMALL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(&inputBuffer, Irp->AssociatedIrp.SystemBuffer, sizeof(setPFNDatabaseRequest));
|
||||
|
||||
s = setPFNDatabase(&inputBuffer);
|
||||
|
||||
out:
|
||||
Irp->IoStatus.Status = s;
|
||||
Irp->IoStatus.Information = 0;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
return s;
|
||||
}
|
||||
|
||||
_Use_decl_annotations_ NTSTATUS irp_mj_device_control(PDEVICE_OBJECT DeviceObject, PIRP Irp)
|
||||
{
|
||||
PIO_STACK_LOCATION irpStack;
|
||||
unsigned long functionCode;
|
||||
|
||||
UNREFERENCED_PARAMETER(DeviceObject);
|
||||
|
||||
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
functionCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
|
||||
|
||||
switch (functionCode)
|
||||
{
|
||||
case IOCTL_DRIVER_QUERY_VA:
|
||||
return queryVAFromIRP(Irp);
|
||||
case IOCTL_DRIVER_SET_PFN_DATABASE:
|
||||
return setPFNDatabaseFromIRP(Irp);
|
||||
default:
|
||||
DbgPrint("IRP_MJ_DEVICE_CONTROL: Unrecognised function code 0x%08lx\n", functionCode);
|
||||
}
|
||||
|
||||
Irp->IoStatus.Status = STATUS_ILLEGAL_FUNCTION;
|
||||
Irp->IoStatus.Information = 0;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS setPFNDatabase(setPFNDatabaseRequest* req)
|
||||
{
|
||||
PVOID MmPfnDatabaseUnsafe;
|
||||
unsigned long long MmPfnDatabase;
|
||||
MM_COPY_ADDRESS src;
|
||||
SIZE_T bytesRead;
|
||||
NTSTATUS s;
|
||||
int didExcept;
|
||||
|
||||
unsigned long numberOfPFNs = 0x2000; // FIXME
|
||||
|
||||
// Since we don't know the base address of ntdll (and don't want to call any undocumented stuff to get it), we accept an offset to MmPfnDatabase
|
||||
// from an exported entry (ExAllocatePoolWithTag). Since this comes from userspace, we still need to santise it as best we can. We can't make it
|
||||
// foolproof but we can do some basic checks. Since we only ever read the PFN database via MmCopyMemory, it should be safe for userspace to give
|
||||
// us a bad address, anyway.
|
||||
MmPfnDatabaseUnsafe = (PVOID)( ((unsigned long long)ExAllocatePoolWithTag) - req->offsetToMmPfnDatabaseInNtDllFromExAllocatePoolWithTag );
|
||||
|
||||
// We now have a pointer to MmPfnDatabase, which is itself a pointer to the first PFN. We should try to read it, and find the PFN DB base.
|
||||
src.VirtualAddress = MmPfnDatabaseUnsafe;
|
||||
#pragma warning( push )
|
||||
#pragma warning( disable : 6001 ) // VS things 'MmPfnDatabase' can be uninitialized in this call. It cannot.
|
||||
s = MmCopyMemory(&MmPfnDatabase, src, sizeof(PVOID), MM_COPY_MEMORY_VIRTUAL, &bytesRead);
|
||||
#pragma warning( pop )
|
||||
if (!NT_SUCCESS(s))
|
||||
{
|
||||
DbgPrint("Cannot read MmPfnDatabase pointer %p as provided by userspace\n", MmPfnDatabaseUnsafe);
|
||||
return s;
|
||||
}
|
||||
if (bytesRead != sizeof(PVOID))
|
||||
{
|
||||
DbgPrint("Short read of read MmPfnDatabase pointer %p as provided by userspace (read %llu of %llu bytes)\n", MmPfnDatabaseUnsafe, bytesRead, sizeof(PVOID));
|
||||
return STATUS_ACCESS_VIOLATION;
|
||||
}
|
||||
|
||||
// Now we have the PFN database pointer, and we can do some basic checks on it.
|
||||
// It should be aligned on a 4K boundary (I think?). This is totally from observation
|
||||
// and may be incorrect.
|
||||
if ((MmPfnDatabase & 0x0000000000000fff) != 0)
|
||||
{
|
||||
DbgPrint("Dereferenced MmPfnDatabase pointer is not correctly aligned?\n");
|
||||
return STATUS_BAD_DATA;
|
||||
}
|
||||
|
||||
// This should not be in a user-space buffer
|
||||
__try
|
||||
{
|
||||
ProbeForRead((PVOID)MmPfnDatabase, sizeof(struct PFN) * numberOfPFNs, 1);
|
||||
didExcept = FALSE;
|
||||
}
|
||||
#pragma warning( push )
|
||||
#pragma warning( disable : 6320 ) // "warning C6320: Exception-filter expression is the constant EXCEPTION_EXECUTE_HANDLER. This might mask exceptions that were not intended to be handled."
|
||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||
#pragma warning( pop )
|
||||
{
|
||||
didExcept = TRUE;
|
||||
}
|
||||
if (!didExcept)
|
||||
{
|
||||
DbgPrint("Dereferenced MmPfnDatabase pointer is in userspace\n");
|
||||
return STATUS_BAD_DATA;
|
||||
}
|
||||
|
||||
// TODO: more checks. We're giving userspace the ability to give kernel space a pointer here
|
||||
// so we should be as careful as we possibly can be.
|
||||
|
||||
// OK, all our checks passed!
|
||||
prv.PFNDatabase = MmPfnDatabase;
|
||||
DbgPrint("MmPfnDatabase is 0x%016llx\n", prv.PFNDatabase);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS queryVAFromIRP(PIRP Irp)
|
||||
{
|
||||
PIO_STACK_LOCATION irpStack;
|
||||
getPageInfoRequest inputBuffer;
|
||||
getPageInfoResponse* outputBuffer;
|
||||
int bytesReturned;
|
||||
NTSTATUS s;
|
||||
|
||||
bytesReturned = 0;
|
||||
|
||||
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(getPageInfoRequest))
|
||||
{
|
||||
s = STATUS_BUFFER_TOO_SMALL;
|
||||
goto out;
|
||||
}
|
||||
memcpy(&inputBuffer, Irp->AssociatedIrp.SystemBuffer, sizeof(getPageInfoRequest));
|
||||
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(getPageInfoResponse) * inputBuffer.numberOfPagesToCheck)
|
||||
{
|
||||
s = STATUS_BUFFER_TOO_SMALL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
outputBuffer = (getPageInfoResponse*)Irp->AssociatedIrp.SystemBuffer;
|
||||
memset(outputBuffer, 0, sizeof(getPageInfoResponse) * inputBuffer.numberOfPagesToCheck);
|
||||
|
||||
s = queryVA(&inputBuffer, outputBuffer);
|
||||
|
||||
if (NT_SUCCESS(s))
|
||||
bytesReturned = sizeof(getPageInfoResponse) * inputBuffer.numberOfPagesToCheck;
|
||||
|
||||
out:
|
||||
Irp->IoStatus.Status = s;
|
||||
Irp->IoStatus.Information = bytesReturned;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
return s;
|
||||
}
|
||||
|
||||
NTSTATUS queryVA(getPageInfoRequest* params, getPageInfoResponse* response)
|
||||
{
|
||||
PMDL mdl;
|
||||
PPFN_NUMBER pfnArray;
|
||||
unsigned int pfnArrayCount;
|
||||
unsigned int pfnIdx;
|
||||
struct PFN* MmPfnDatabase = (struct PFN*)prv.PFNDatabase;
|
||||
struct PFN ourPFN;
|
||||
unsigned long long pte;
|
||||
KAPC_STATE state;
|
||||
PEPROCESS eprocess;
|
||||
NTSTATUS s;
|
||||
MM_COPY_ADDRESS srcAddress;
|
||||
PHYSICAL_ADDRESS phys;
|
||||
SIZE_T numRead;
|
||||
|
||||
UNREFERENCED_PARAMETER(response);
|
||||
|
||||
if (params->numberOfPagesToCheck == 0)
|
||||
{
|
||||
DbgPrint("Asked to scan 0 pages\n");
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
mdl = IoAllocateMdl((PVOID)params->pageToCheck, 0x1000 * params->numberOfPagesToCheck, FALSE, FALSE, NULL);
|
||||
if (!mdl)
|
||||
{
|
||||
return STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
s = PsLookupProcessByProcessId((HANDLE)params->targetPID, &eprocess);
|
||||
if (!NT_SUCCESS(s))
|
||||
{
|
||||
IoFreeMdl(mdl);
|
||||
DbgPrint("PsLookupProcesByProcessId failed for PID 0x%04lx: 0x%08lx\n", params->targetPID, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
KeStackAttachProcess(eprocess, &state);
|
||||
|
||||
__try
|
||||
{
|
||||
MmProbeAndLockPages(mdl, UserMode, IoReadAccess);
|
||||
}
|
||||
#pragma warning( push )
|
||||
#pragma warning( disable : 6320 ) // "warning C6320: Exception-filter expression is the constant EXCEPTION_EXECUTE_HANDLER. This might mask exceptions that were not intended to be handled."
|
||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||
#pragma warning( pop )
|
||||
{
|
||||
s = STATUS_BAD_DATA;
|
||||
goto out;
|
||||
}
|
||||
pfnArray = MmGetMdlPfnArray(mdl);
|
||||
pfnArrayCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(mdl), MmGetMdlByteCount(mdl));
|
||||
// Read the PTE from the PFN database using MmCopyMemory, in case we have the pfn database base address wrong.
|
||||
// MmCopyMemory won't let me read us the memory by VA - not 100% sure why but I suspect because it is checked
|
||||
// against the PFN table and no mapping is found (?) - so we just translate to physical address and read that
|
||||
// instead.
|
||||
for (pfnIdx = 0; pfnIdx < pfnArrayCount; pfnIdx++)
|
||||
{
|
||||
phys = MmGetPhysicalAddress(&MmPfnDatabase[pfnArray[pfnIdx]]);
|
||||
srcAddress.PhysicalAddress.QuadPart = phys.QuadPart;
|
||||
#pragma warning( push )
|
||||
#pragma warning( disable : 6001 ) // VS things 'ourPFN' can be uninitialized in this call. It cannot.
|
||||
s = MmCopyMemory(&ourPFN, srcAddress, sizeof(struct PFN), MM_COPY_MEMORY_PHYSICAL, &numRead);
|
||||
#pragma warning( pop )
|
||||
if (!NT_SUCCESS(s) || numRead != sizeof(struct PFN))
|
||||
{
|
||||
DbgPrint("Failed to read PFN from PFN database at %p (%p[0x%16llx]): NTSTATUS 0x%08lx, transferred %llu of %llu bytes\n", srcAddress.VirtualAddress, MmPfnDatabase, pfnArray[pfnIdx], s, numRead, sizeof(struct PFN));
|
||||
if (NT_SUCCESS(s))
|
||||
s = STATUS_PARTIAL_COPY;
|
||||
goto out;
|
||||
}
|
||||
pte = ourPFN.PTEAddress;
|
||||
|
||||
// DbgPrint("VA 0x%016llx PFN %p\n", params->pageToCheck, &MmPfnDatabase[pfn[0]]);
|
||||
|
||||
response[pfnIdx].isValid = TRUE; // TODO
|
||||
response[pfnIdx].isDirty = (ourPFN.e1 >> 4) & 0x01;
|
||||
}
|
||||
s = STATUS_SUCCESS;
|
||||
|
||||
out:
|
||||
MmUnlockPages(mdl);
|
||||
IoFreeMdl(mdl);
|
||||
KeUnstackDetachProcess(&state);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
__drv_requiresIRQL(APC_LEVEL)
|
||||
NTSTATUS readMemoryFromPhysical(unsigned long long address, char* errMsg, void* tableOut)
|
||||
{
|
||||
NTSTATUS s;
|
||||
MM_COPY_ADDRESS srcAddress;
|
||||
SIZE_T numRead;
|
||||
SIZE_T bytesToRead = 0x200 * sizeof(unsigned long long);
|
||||
|
||||
srcAddress.PhysicalAddress.QuadPart = address;
|
||||
s = MmCopyMemory(tableOut, srcAddress, bytesToRead, MM_COPY_MEMORY_PHYSICAL, &numRead);
|
||||
|
||||
if (!NT_SUCCESS(s) || numRead != bytesToRead)
|
||||
{
|
||||
DbgPrint("Failed to MmCopyMemory table '%s' from physical location 0x%08llx: 0x%08lx (read 0x%08llx of 0x%08llx bytes)\n", errMsg, srcAddress.PhysicalAddress.QuadPart, s, numRead, bytesToRead);
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
unsigned long long getChildTableFromTableEntry(unsigned long long entry)
|
||||
{
|
||||
// TODO/FIXME: We should honour the size of the child table pointer here, which is set as
|
||||
// M-12 (M being set in the sillicon I think). Bit 63 is XD, and 62-52 is ignored, but 51
|
||||
// through M is reserved by the sillicon so we should ignore it..
|
||||
return ((unsigned long long)((entry & ~(0xfff0'0000'0000'0FFF)) ));
|
||||
}
|
||||
|
||||
int isTableEntryValid(unsigned long long entry)
|
||||
{
|
||||
return (entry & 0x01) != 0;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
struct privateInfo
|
||||
{
|
||||
PDEVICE_OBJECT deviceObject;
|
||||
unsigned long long PFNDatabase;
|
||||
}; typedef struct privateInfo privateInfo;
|
||||
|
||||
extern privateInfo prv;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
;
|
||||
; driver.inf
|
||||
;
|
||||
|
||||
[Version]
|
||||
Signature="$WINDOWS NT$"
|
||||
Class=System
|
||||
ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318}
|
||||
Provider=%ManufacturerName%
|
||||
DriverVer=1
|
||||
CatalogFile=driver.cat
|
||||
|
||||
[DestinationDirs]
|
||||
DefaultDestDir = 12
|
||||
|
||||
|
||||
[SourceDisksNames]
|
||||
1 = %DiskName%,,,""
|
||||
|
||||
[SourceDisksFiles]
|
||||
|
||||
|
||||
[Manufacturer]
|
||||
%ManufacturerName%=Standard,NT$ARCH$
|
||||
|
||||
[Standard.NT$ARCH$]
|
||||
|
||||
|
||||
[Strings]
|
||||
ManufacturerName="<Your manufacturer name>" ;TODO: Replace with your manufacturer name
|
||||
ClassName=""
|
||||
DiskName="driver Source Disk"
|
||||
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{65A559E4-6946-4252-BFBE-C3B5D1B8108C}</ProjectGuid>
|
||||
<TemplateGuid>{dd38f7fc-d7bd-488b-9242-7d8754cde80d}</TemplateGuid>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform Condition="'$(Platform)' == ''">Win32</Platform>
|
||||
<RootNamespace>driver</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<TargetVersion>Windows10</TargetVersion>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
|
||||
<ConfigurationType>Driver</ConfigurationType>
|
||||
<DriverType>WDM</DriverType>
|
||||
<SpectreMitigation>false</SpectreMitigation>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<TargetVersion>Windows10</TargetVersion>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
|
||||
<ConfigurationType>Driver</ConfigurationType>
|
||||
<DriverType>WDM</DriverType>
|
||||
<SpectreMitigation>false</SpectreMitigation>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||
<Inf2CatUseLocalTime>true</Inf2CatUseLocalTime>
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<IncludePath>$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||
<Inf2CatUseLocalTime>true</Inf2CatUseLocalTime>
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<IncludePath>$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Link>
|
||||
<AdditionalDependencies>Aux_klib.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<ClCompile>
|
||||
<EnablePREfast>true</EnablePREfast>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Link>
|
||||
<AdditionalDependencies>Aux_klib.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<ClCompile>
|
||||
<EnablePREfast>true</EnablePREfast>
|
||||
<PreprocessorDefinitions>_PFT_SHOULD_CHECK_RETURN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<Inf Include="driver.inf" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<FilesToPackage Include="$(TargetPath)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="driver.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="driver.h" />
|
||||
<ClInclude Include="public.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#define DOS_DEVICE_NAME L"\\DosDevices\\cowspot"
|
||||
#define DEVICE_NAME L"\\Device\\cowspot"
|
||||
|
||||
struct getPageInfoResponse
|
||||
{
|
||||
unsigned char isValid;
|
||||
unsigned char isDirty;
|
||||
}; typedef struct getPageInfoResponse getPageInfoResponse;
|
||||
|
||||
struct getPageInfoRequest
|
||||
{
|
||||
unsigned long targetPID;
|
||||
unsigned long numberOfPagesToCheck;
|
||||
unsigned long long pageToCheck;
|
||||
}; typedef struct getPageInfoRequest getPageInfoRequest;
|
||||
|
||||
#define IOCTL_DRIVER_QUERY_VA CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
|
||||
struct setPFNDatabaseRequest
|
||||
{
|
||||
unsigned long long offsetToMmPfnDatabaseInNtDllFromExAllocatePoolWithTag;
|
||||
}; typedef struct setPFNDatabaseRequest setPFNDatabaseRequest;
|
||||
|
||||
#define IOCTL_DRIVER_SET_PFN_DATABASE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
|
||||
Reference in New Issue
Block a user