mirror of
https://github.com/donnaskiez/ac.git
synced 2024-11-21 22:24:08 +01:00
sleep
This commit is contained in:
parent
dbf7d786b5
commit
6cac089f5c
15 changed files with 860 additions and 195 deletions
20
ac.sln
20
ac.sln
|
@ -9,7 +9,9 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "driver", "driver\driver.vcx
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "service", "service\service.csproj", "{6228E9DD-E1EA-45D8-8054-A00FC2D63414}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "server", "server\server.csproj", "{4D0777F0-2D3D-4FD7-9C0F-CD4DEC1A99E9}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "server", "server\server.csproj", "{4D0777F0-2D3D-4FD7-9C0F-CD4DEC1A99E9}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "debuglib", "debuglib\debuglib.vcxproj", "{BD01B133-A4BF-4292-A261-6530CBC1A481}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -95,6 +97,22 @@ Global
|
|||
{4D0777F0-2D3D-4FD7-9C0F-CD4DEC1A99E9}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4D0777F0-2D3D-4FD7-9C0F-CD4DEC1A99E9}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4D0777F0-2D3D-4FD7-9C0F-CD4DEC1A99E9}.Release|x86.Build.0 = Release|Any CPU
|
||||
{BD01B133-A4BF-4292-A261-6530CBC1A481}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{BD01B133-A4BF-4292-A261-6530CBC1A481}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{BD01B133-A4BF-4292-A261-6530CBC1A481}.Debug|ARM64.ActiveCfg = Debug|x64
|
||||
{BD01B133-A4BF-4292-A261-6530CBC1A481}.Debug|ARM64.Build.0 = Debug|x64
|
||||
{BD01B133-A4BF-4292-A261-6530CBC1A481}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{BD01B133-A4BF-4292-A261-6530CBC1A481}.Debug|x64.Build.0 = Debug|x64
|
||||
{BD01B133-A4BF-4292-A261-6530CBC1A481}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{BD01B133-A4BF-4292-A261-6530CBC1A481}.Debug|x86.Build.0 = Debug|Win32
|
||||
{BD01B133-A4BF-4292-A261-6530CBC1A481}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{BD01B133-A4BF-4292-A261-6530CBC1A481}.Release|Any CPU.Build.0 = Release|x64
|
||||
{BD01B133-A4BF-4292-A261-6530CBC1A481}.Release|ARM64.ActiveCfg = Release|x64
|
||||
{BD01B133-A4BF-4292-A261-6530CBC1A481}.Release|ARM64.Build.0 = Release|x64
|
||||
{BD01B133-A4BF-4292-A261-6530CBC1A481}.Release|x64.ActiveCfg = Release|x64
|
||||
{BD01B133-A4BF-4292-A261-6530CBC1A481}.Release|x64.Build.0 = Release|x64
|
||||
{BD01B133-A4BF-4292-A261-6530CBC1A481}.Release|x86.ActiveCfg = Release|Win32
|
||||
{BD01B133-A4BF-4292-A261-6530CBC1A481}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
138
debuglib/debuglib.vcxproj
Normal file
138
debuglib/debuglib.vcxproj
Normal file
|
@ -0,0 +1,138 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" 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>17.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{bd01b133-a4bf-4292-a261-6530cbc1a481}</ProjectGuid>
|
||||
<RootNamespace>debuglib</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.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>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</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" />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="symbols.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="symbols.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
27
debuglib/debuglib.vcxproj.filters
Normal file
27
debuglib/debuglib.vcxproj.filters
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="symbols.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="symbols.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
264
debuglib/symbols.cpp
Normal file
264
debuglib/symbols.cpp
Normal file
|
@ -0,0 +1,264 @@
|
|||
#include "symbols.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
static NtSystemDebugControl g_NtSystemDebugControl = NULL;
|
||||
|
||||
BOOL
|
||||
EnablePrivilege(
|
||||
_In_ PCWSTR PrivilegeName,
|
||||
_In_ BOOLEAN Acquire
|
||||
)
|
||||
{
|
||||
HANDLE tokenHandle;
|
||||
BOOL ret;
|
||||
ULONG tokenPrivilegesSize = FIELD_OFFSET( TOKEN_PRIVILEGES, Privileges[ 1 ] );
|
||||
PTOKEN_PRIVILEGES tokenPrivileges = static_cast< PTOKEN_PRIVILEGES >( calloc( 1, tokenPrivilegesSize ) );
|
||||
|
||||
if ( tokenPrivileges == NULL )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
tokenHandle = NULL;
|
||||
tokenPrivileges->PrivilegeCount = 1;
|
||||
ret = LookupPrivilegeValue( NULL,
|
||||
PrivilegeName,
|
||||
&tokenPrivileges->Privileges[ 0 ].Luid );
|
||||
if ( ret == FALSE )
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
tokenPrivileges->Privileges[ 0 ].Attributes = Acquire ? SE_PRIVILEGE_ENABLED
|
||||
: SE_PRIVILEGE_REMOVED;
|
||||
|
||||
ret = OpenProcessToken( GetCurrentProcess(),
|
||||
TOKEN_ADJUST_PRIVILEGES,
|
||||
&tokenHandle );
|
||||
if ( ret == FALSE )
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
ret = AdjustTokenPrivileges( tokenHandle,
|
||||
FALSE,
|
||||
tokenPrivileges,
|
||||
tokenPrivilegesSize,
|
||||
NULL,
|
||||
NULL );
|
||||
if ( ret == FALSE )
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
Exit:
|
||||
if ( tokenHandle != NULL )
|
||||
{
|
||||
CloseHandle( tokenHandle );
|
||||
}
|
||||
free( tokenPrivileges );
|
||||
return ret;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
CreateDump(
|
||||
_In_ PCSTR FilePath
|
||||
)
|
||||
{
|
||||
HRESULT result;
|
||||
HANDLE handle;
|
||||
HMODULE module;
|
||||
SYSDBG_LIVEDUMP_CONTROL_FLAGS flags;
|
||||
SYSDBG_LIVEDUMP_CONTROL_ADDPAGES pages;
|
||||
SYSDBG_LIVEDUMP_CONTROL liveDumpControl;
|
||||
NTSTATUS status;
|
||||
ULONG returnLength;
|
||||
|
||||
handle = INVALID_HANDLE_VALUE;
|
||||
result = S_OK;
|
||||
flags.AsUlong = 0;
|
||||
pages.AsUlong = 0;
|
||||
|
||||
//
|
||||
// Get function addresses
|
||||
//
|
||||
module = LoadLibrary( L"ntdll.dll" );
|
||||
if ( module == NULL )
|
||||
{
|
||||
result = S_FALSE;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
g_NtSystemDebugControl = ( NtSystemDebugControl )
|
||||
GetProcAddress( module, "NtSystemDebugControl" );
|
||||
|
||||
FreeLibrary( module );
|
||||
|
||||
if ( g_NtSystemDebugControl == NULL )
|
||||
{
|
||||
result = S_FALSE;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// Get SeDebugPrivilege
|
||||
//
|
||||
if ( !EnablePrivilege( SE_DEBUG_NAME, TRUE ) )
|
||||
{
|
||||
result = S_FALSE;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// Create the target file (must specify synchronous I/O)
|
||||
//
|
||||
handle = CreateFileA( FilePath,
|
||||
GENERIC_WRITE | GENERIC_READ,
|
||||
0,
|
||||
NULL,
|
||||
CREATE_ALWAYS,
|
||||
FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING,
|
||||
NULL );
|
||||
|
||||
if ( handle == INVALID_HANDLE_VALUE )
|
||||
{
|
||||
result = S_FALSE;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// Try to create the requested dump
|
||||
//
|
||||
memset( &liveDumpControl, 0, sizeof( liveDumpControl ) );
|
||||
|
||||
//
|
||||
// The only thing the kernel looks at in the struct we pass is the handle,
|
||||
// the flags and the pages to dump.
|
||||
//
|
||||
liveDumpControl.DumpFileHandle = ( PVOID )( handle );
|
||||
liveDumpControl.AddPagesControl = pages;
|
||||
liveDumpControl.Flags = flags;
|
||||
|
||||
status = g_NtSystemDebugControl( CONTROL_KERNEL_DUMP,
|
||||
( PVOID )( &liveDumpControl ),
|
||||
sizeof( liveDumpControl ),
|
||||
NULL,
|
||||
0,
|
||||
&returnLength );
|
||||
|
||||
if ( NT_SUCCESS( status ) )
|
||||
{
|
||||
result = S_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = S_FALSE;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
Exit:
|
||||
if ( handle != INVALID_HANDLE_VALUE )
|
||||
{
|
||||
CloseHandle( handle );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
VOID GetKernelStructureOffsets(KERNEL_STRUCTURE_OFFSETS* KernelOffsets)
|
||||
{
|
||||
UINT64 kernel_base = NULL;
|
||||
HMODULE handle;
|
||||
HRESULT result;
|
||||
ULONG type_kprocess;
|
||||
ULONG type_eprocess;
|
||||
ULONG type_kthread;
|
||||
ULONG type_ethread;
|
||||
DebugCreateFunction dbg_create_function;
|
||||
PDEBUG_SYMBOLS symbols = nullptr;
|
||||
PDEBUG_DATA_SPACES4 data_spaces = nullptr;
|
||||
PDEBUG_CLIENT client = nullptr;
|
||||
PDEBUG_CONTROL debug_control = nullptr;
|
||||
PCSTR dump_path = "C:\\temp.dmp";
|
||||
|
||||
result = CreateDump( dump_path );
|
||||
|
||||
if ( result != S_OK )
|
||||
return;
|
||||
|
||||
handle = GetModuleHandle( L"dbgeng.dll" );
|
||||
|
||||
if ( handle == NULL )
|
||||
return;
|
||||
|
||||
dbg_create_function = ( DebugCreateFunction )GetProcAddress( handle, "DebugCreate" );
|
||||
|
||||
if ( dbg_create_function == NULL )
|
||||
return;
|
||||
|
||||
result = dbg_create_function( __uuidof( IDebugClient ), ( PVOID* )&client );
|
||||
|
||||
if ( result != S_OK )
|
||||
goto end;
|
||||
|
||||
result = client->QueryInterface( __uuidof( IDebugSymbols ), ( PVOID* )&symbols );
|
||||
|
||||
if ( result != S_OK )
|
||||
goto end;
|
||||
|
||||
result = client->QueryInterface( __uuidof( IDebugDataSpaces ), ( PVOID* )&data_spaces );
|
||||
|
||||
if ( result != S_OK )
|
||||
goto end;
|
||||
|
||||
result = client->QueryInterface( __uuidof( IDebugControl ), ( PVOID* )&debug_control );
|
||||
|
||||
if ( result != S_OK )
|
||||
goto end;
|
||||
|
||||
result = client->OpenDumpFile( dump_path );
|
||||
|
||||
//result = debug_control->WaitForEvent( DEBUG_WAIT_DEFAULT, INFINITE );
|
||||
|
||||
if ( result != S_OK )
|
||||
goto end;
|
||||
|
||||
data_spaces->ReadDebuggerData( DEBUG_DATA_KernBase, &kernel_base, sizeof( UINT64 ), nullptr );
|
||||
|
||||
symbols->GetTypeId( kernel_base, "_KPROCESS", &type_kprocess );
|
||||
symbols->GetTypeId( kernel_base, "_EPROCESS", &type_eprocess );
|
||||
symbols->GetTypeId( kernel_base, "_KTHREAD", &type_kthread );
|
||||
symbols->GetTypeId( kernel_base, "_ETHREAD", &type_ethread );
|
||||
|
||||
symbols->GetFieldOffset( kernel_base, type_kprocess, "ThreadListHead", &KernelOffsets->KPROCESS.thread_list_head );
|
||||
symbols->GetFieldOffset( kernel_base, type_kprocess, "DirectoryTableBase", &KernelOffsets->KPROCESS.directory_table_base );
|
||||
|
||||
symbols->GetFieldOffset( kernel_base, type_eprocess, "PeakVirtualSize", &KernelOffsets->EPROCESS.peak_virtual_size );
|
||||
symbols->GetFieldOffset( kernel_base, type_eprocess, "VadRoot", &KernelOffsets->EPROCESS.vad_root );
|
||||
symbols->GetFieldOffset( kernel_base, type_eprocess, "ObjectTable", &KernelOffsets->EPROCESS.object_table );
|
||||
symbols->GetFieldOffset( kernel_base, type_eprocess, "ImageFileName", &KernelOffsets->EPROCESS.image_name );
|
||||
symbols->GetFieldOffset( kernel_base, type_eprocess, "Peb", &KernelOffsets->EPROCESS.process_environment_block );
|
||||
|
||||
symbols->GetFieldOffset( kernel_base, type_kthread, "StackBase", &KernelOffsets->KTHREAD.stack_base );
|
||||
symbols->GetFieldOffset( kernel_base, type_kthread, "StackLimit", &KernelOffsets->KTHREAD.stack_limit );
|
||||
symbols->GetFieldOffset( kernel_base, type_kthread, "ThreadListEntry", &KernelOffsets->KTHREAD.threadlist );
|
||||
symbols->GetFieldOffset( kernel_base, type_kthread, "ApcState", &KernelOffsets->KTHREAD.apc_state );
|
||||
symbols->GetFieldOffset( kernel_base, type_kthread, "StartAddress", &KernelOffsets->KTHREAD.start_address );
|
||||
|
||||
end:
|
||||
|
||||
if ( client != nullptr )
|
||||
{
|
||||
client->EndSession( DEBUG_END_ACTIVE_DETACH );
|
||||
client->Release();
|
||||
}
|
||||
|
||||
if ( symbols != nullptr )
|
||||
symbols->Release();
|
||||
|
||||
if ( data_spaces != nullptr )
|
||||
data_spaces->Release();
|
||||
|
||||
if ( debug_control != nullptr )
|
||||
debug_control->Release();
|
||||
}
|
141
debuglib/symbols.h
Normal file
141
debuglib/symbols.h
Normal file
|
@ -0,0 +1,141 @@
|
|||
#ifndef SYMBOLS_H
|
||||
#define SYMBOLS_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <WDBGEXTS.H>
|
||||
#include <DbgEng.h>
|
||||
|
||||
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
|
||||
#define CONTROL_TRIAGE_DUMP 29
|
||||
#define CONTROL_KERNEL_DUMP 37
|
||||
#define TRIAGE_SIZE 0x20000 // must be >132k and <1MB
|
||||
#define MAX_TRIAGE_THREADS 16
|
||||
|
||||
#pragma comment(lib, "ntdll")
|
||||
|
||||
//
|
||||
// From NDK, argument required for parameter 29.
|
||||
//
|
||||
typedef struct _SYSDBG_TRIAGE_DUMP
|
||||
{
|
||||
ULONG Flags;
|
||||
ULONG BugCheckCode;
|
||||
ULONG_PTR BugCheckParam1;
|
||||
ULONG_PTR BugCheckParam2;
|
||||
ULONG_PTR BugCheckParam3;
|
||||
ULONG_PTR BugCheckParam4;
|
||||
ULONG ProcessHandles;
|
||||
ULONG ThreadHandles;
|
||||
PHANDLE Handles;
|
||||
} SYSDBG_TRIAGE_DUMP, * PSYSDBG_TRIAGE_DUMP;
|
||||
|
||||
//
|
||||
// Undocumented. Structures relevant for new parameter 37.
|
||||
// Greetz to Alex I.
|
||||
//
|
||||
typedef union _SYSDBG_LIVEDUMP_CONTROL_FLAGS
|
||||
{
|
||||
struct
|
||||
{
|
||||
ULONG UseDumpStorageStack : 1;
|
||||
ULONG CompressMemoryPagesData : 1;
|
||||
ULONG IncludeUserSpaceMemoryPages : 1;
|
||||
ULONG Reserved : 29;
|
||||
};
|
||||
ULONG AsUlong;
|
||||
} SYSDBG_LIVEDUMP_CONTROL_FLAGS;
|
||||
|
||||
|
||||
typedef union _SYSDBG_LIVEDUMP_CONTROL_ADDPAGES
|
||||
{
|
||||
struct
|
||||
{
|
||||
ULONG HypervisorPages : 1;
|
||||
ULONG Reserved : 31;
|
||||
};
|
||||
ULONG AsUlong;
|
||||
} SYSDBG_LIVEDUMP_CONTROL_ADDPAGES;
|
||||
|
||||
typedef struct _SYSDBG_LIVEDUMP_CONTROL
|
||||
{
|
||||
ULONG Version;
|
||||
ULONG BugCheckCode;
|
||||
ULONG_PTR BugCheckParam1;
|
||||
ULONG_PTR BugCheckParam2;
|
||||
ULONG_PTR BugCheckParam3;
|
||||
ULONG_PTR BugCheckParam4;
|
||||
PVOID DumpFileHandle;
|
||||
PVOID CancelEventHandle;
|
||||
SYSDBG_LIVEDUMP_CONTROL_FLAGS Flags;
|
||||
SYSDBG_LIVEDUMP_CONTROL_ADDPAGES AddPagesControl;
|
||||
} SYSDBG_LIVEDUMP_CONTROL, * PSYSDBG_LIVEDUMP_CONTROL;
|
||||
|
||||
typedef
|
||||
NTSTATUS
|
||||
( __stdcall*
|
||||
NtSystemDebugControl ) (
|
||||
ULONG ControlCode,
|
||||
PVOID InputBuffer,
|
||||
ULONG InputBufferLength,
|
||||
PVOID OutputBuffer,
|
||||
ULONG OutputBufferLength,
|
||||
PULONG ReturnLength
|
||||
);
|
||||
|
||||
BOOL
|
||||
EnablePrivilege(
|
||||
__in PCWSTR PrivilegeName,
|
||||
__in BOOLEAN Acquire
|
||||
);
|
||||
|
||||
NTSTATUS
|
||||
CreateTriageDump(
|
||||
__in HANDLE FileHandle,
|
||||
__in ULONG Pid
|
||||
);
|
||||
|
||||
NTSTATUS
|
||||
CreateKernelDump(
|
||||
__in HANDLE FileHandle,
|
||||
__in SYSDBG_LIVEDUMP_CONTROL_FLAGS Flags,
|
||||
__in SYSDBG_LIVEDUMP_CONTROL_ADDPAGES Pages
|
||||
);
|
||||
|
||||
INT
|
||||
wmain(
|
||||
__in INT Argc,
|
||||
__in PWCHAR Argv[]
|
||||
);
|
||||
|
||||
typedef HRESULT( *DebugCreateFunction )( _In_ REFIID, _Out_ PVOID* );
|
||||
|
||||
struct KERNEL_STRUCTURE_OFFSETS
|
||||
{
|
||||
struct KPROCESS
|
||||
{
|
||||
ULONG thread_list_head;
|
||||
ULONG directory_table_base;
|
||||
}KPROCESS;
|
||||
|
||||
struct EPROCESS
|
||||
{
|
||||
ULONG peak_virtual_size;
|
||||
ULONG vad_root;
|
||||
ULONG object_table;
|
||||
ULONG image_name;
|
||||
ULONG process_environment_block;
|
||||
}EPROCESS;
|
||||
|
||||
struct KTHREAD
|
||||
{
|
||||
ULONG stack_base;
|
||||
ULONG stack_limit;
|
||||
ULONG threadlist;
|
||||
ULONG apc_state;
|
||||
ULONG start_address;
|
||||
}KTHREAD;
|
||||
};
|
||||
|
||||
VOID GetKernelStructureOffsets( KERNEL_STRUCTURE_OFFSETS* KernelOffsets );
|
||||
|
||||
#endif
|
|
@ -46,7 +46,6 @@ typedef struct _CALLBACKS_CONFIGURATION
|
|||
|
||||
static const uintptr_t EPROCESS_IMAGE_FILE_NAME_OFFSET = 0x5a8;
|
||||
static const uintptr_t EPROCESS_HANDLE_TABLE_OFFSET = 0x570;
|
||||
static const uintptr_t OBJECT_HEADER_SIZE = 0x30;
|
||||
static const uintptr_t EPROCESS_PLIST_ENTRY_OFFSET = 0x448;
|
||||
|
||||
static UNICODE_STRING OBJECT_TYPE_PROCESS = RTL_CONSTANT_STRING( L"Process" );
|
||||
|
|
|
@ -41,10 +41,21 @@
|
|||
#define KTHREAD_APC_STATE_OFFSET 0x258
|
||||
#define KTHREAD_START_ADDRESS_OFFSET 0x450
|
||||
|
||||
#define EPROCESS_VIRTUAL_SIZE_OFFSET 0x498
|
||||
#define EPROCESS_PEAK_VIRTUAL_SIZE_OFFSET 0x490
|
||||
#define EPROCESS_VAD_ROOT_OFFSET 0x7d8
|
||||
#define EPROCESS_OBJECT_TABLE_OFFSET 0x570
|
||||
#define EPROCESS_IMAGE_NAME_OFFSET 0x5a8
|
||||
#define EPROCESS_PEB_OFFSET 0x550
|
||||
|
||||
#define KPROCESS_THREADLIST_OFFSET 0x030
|
||||
#define KPROCESS_DIRECTORY_TABLE_BASE_OFFSET 0x028
|
||||
|
||||
#define OBJECT_HEADER_SIZE 0x30
|
||||
#define OBJECT_HEADER_TYPE_INDEX_OFFSET 0x018
|
||||
|
||||
#define KPROCESS_OFFSET_FROM_POOL_HEADER_SIZE_1 0x70
|
||||
#define KPROCESS_OFFSET_FROM_POOL_HEADER_SIZE_2 0x80
|
||||
#define EPROCESS_SIZE 0xa40
|
||||
|
||||
#define KPCRB_CURRENT_THREAD 0x8
|
||||
|
||||
|
|
|
@ -875,12 +875,12 @@ NTSTATUS ParseSMBIOSTable(
|
|||
*
|
||||
* source: https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf line 823
|
||||
*/
|
||||
while ( smbios_table_header->Type != SMBIOS_SYSTEM_INFORMATION_TYPE_2_TABLE )
|
||||
while ( smbios_table_header->Type != VMWARE_SMBIOS_TABLE )
|
||||
GetNextSMBIOSStructureInTable( &smbios_table_header );
|
||||
|
||||
status = GetStringAtIndexFromSMBIOSTable(
|
||||
smbios_table_header,
|
||||
MOTHERBOARD_SERIAL_CODE_TABLE_INDEX,
|
||||
VMWARE_SMBIOS_TABLE_INDEX,
|
||||
ConfigMotherboardSerialNumber,
|
||||
ConfigMotherboardSerialNumberMaxSize
|
||||
);
|
||||
|
|
305
driver/pool.c
305
driver/pool.c
|
@ -30,12 +30,8 @@ CHAR EXECUTIVE_OBJECT_POOL_TAGS[ EXECUTIVE_OBJECT_COUNT ][ POOL_TAG_LENGTH ] =
|
|||
"\x4C\x69\x6E\x6B" /* Symbolic links */
|
||||
};
|
||||
|
||||
typedef struct _PROCESS_SCAN_CONTEXT
|
||||
{
|
||||
PVOID process_buffer;
|
||||
ULONG process_count;
|
||||
|
||||
}PROCESS_SCAN_CONTEXT, *PPROCESS_SCAN_CONTEXT;
|
||||
PVOID process_buffer = NULL;
|
||||
ULONG process_count = NULL;
|
||||
|
||||
PKDDEBUGGER_DATA64 GetGlobalDebuggerData()
|
||||
{
|
||||
|
@ -64,13 +60,12 @@ PKDDEBUGGER_DATA64 GetGlobalDebuggerData()
|
|||
dump_header
|
||||
);
|
||||
|
||||
debugger_data =
|
||||
( PKDDEBUGGER_DATA64 )ExAllocatePool2( POOL_FLAG_NON_PAGED, sizeof( KDDEBUGGER_DATA64 ), POOL_DEBUGGER_DATA_TAG );
|
||||
debugger_data = ( PKDDEBUGGER_DATA64 )ExAllocatePool2( POOL_FLAG_NON_PAGED, sizeof( KDDEBUGGER_DATA64 ), POOL_DEBUGGER_DATA_TAG );
|
||||
|
||||
if ( !debugger_data )
|
||||
goto end;
|
||||
|
||||
RtlCopyMemory( debugger_data, dump_header->KdDebuggerDataBlock, sizeof( KDDEBUGGER_DATA64 ));
|
||||
RtlCopyMemory( debugger_data, dump_header->KdDebuggerDataBlock, sizeof( KDDEBUGGER_DATA64 ) );
|
||||
|
||||
end:
|
||||
|
||||
|
@ -80,25 +75,100 @@ end:
|
|||
return debugger_data;
|
||||
}
|
||||
|
||||
/*
|
||||
* This for some reason does not work on my main pc but works on the vm. The debugger
|
||||
* data it uses to get the list head may be providing different data based on whether
|
||||
* or not the machine is being debugged? Not sure. So need to look into this at somepoint.
|
||||
*/
|
||||
VOID GetPsActiveProcessHead(
|
||||
_In_ PUINT64 Address
|
||||
)
|
||||
{
|
||||
/* TODO: have a global debugger pool here since shit aint really change */
|
||||
PKDDEBUGGER_DATA64 debugger_data = GetGlobalDebuggerData();
|
||||
|
||||
if ( !debugger_data )
|
||||
return;
|
||||
|
||||
*Address = *(UINT64*)( debugger_data->PsActiveProcessHead );
|
||||
*Address = *( UINT64* )( debugger_data->PsActiveProcessHead );
|
||||
|
||||
ExFreePoolWithTag( debugger_data, POOL_DEBUGGER_DATA_TAG );
|
||||
}
|
||||
|
||||
/*
|
||||
* Here we define a signature that can be used to find EPROCESS structures consistently across
|
||||
* major windows versions. The fields we test have proven to be consistent in the following study:
|
||||
*
|
||||
* https://www.cise.ufl.edu/~traynor/papers/ccs09b.pdf
|
||||
*
|
||||
* Aswell as some of my own additional research and testing. The following signature is used:
|
||||
*
|
||||
* PeakVirtualSize must be greater then 0 for any valid process:
|
||||
* -> EPROCESS->PeakVirtualSize > 0
|
||||
*
|
||||
* The DirectoryTableBase must be 0x20 aligned:
|
||||
* -> EPROCESS->DirectoryTableBase % 20 == 0
|
||||
*
|
||||
* The pool allocation size must be greater then the size of an EPROCESS allocation and
|
||||
* less then the size of a page. Allocation size can be found with the following formula:
|
||||
* -> AllocationSize = POOL_HEADER->BlockSize * CHUNK_SIZE - sizeof(POOL_HEADER)
|
||||
* -> AllocationSize > sizeof(EPROCESS)
|
||||
* -> AllocationSize < PAGE_SIZE (4096)
|
||||
*
|
||||
* Pool type must be non-null:
|
||||
* -> POOL_HEADER->PoolType != NULL
|
||||
*
|
||||
* The process PEB must be a usermode address and 0x1000 aligned:
|
||||
* -> EPROCESS->Peb & 0x7ffd0000 == 0x7ffd0000 && EPROCESS->Peb % 0x1000 == 0
|
||||
*
|
||||
* The object table must have the following properties and be 0x8 aligned:
|
||||
* -> EPROCESS->ObjectTable & 0xe0000000 == 0xe0000000 && EPROCESS->ObjectTable % 0x8 == 0
|
||||
*
|
||||
* The allocation size, when AND'd with 0xfff0 must not equal 0xfff0:
|
||||
* -> AllocationSize & 0xfff0 != 0xfff0
|
||||
*
|
||||
* This signature will allow us to consistently and accurately determine if a given pool allocation is
|
||||
* indeed an executive process allocation across major versions of Windows.
|
||||
*/
|
||||
BOOLEAN ValidateIfAddressIsProcessStructure(
|
||||
_In_ PVOID Address,
|
||||
_In_ PPOOL_HEADER PoolHeader
|
||||
)
|
||||
{
|
||||
UINT64 peak_virtual_size = NULL;
|
||||
UINT64 dir_table_base = NULL;
|
||||
UINT64 allocation_size = NULL;
|
||||
UINT64 peb = NULL;
|
||||
UINT64 object_table = NULL;
|
||||
UINT32 pool_type = NULL;
|
||||
BOOLEAN peb_test = FALSE;
|
||||
BOOLEAN object_table_test = FALSE;
|
||||
UINT64 allocation_size_test = NULL;
|
||||
|
||||
if ( MmIsAddressValid( ( UINT64 )Address + EPROCESS_PEAK_VIRTUAL_SIZE_OFFSET ) )
|
||||
peak_virtual_size = *( UINT64* )( ( UINT64 )Address + EPROCESS_PEAK_VIRTUAL_SIZE_OFFSET );
|
||||
|
||||
if ( MmIsAddressValid( ( UINT64 )Address + KPROCESS_DIRECTORY_TABLE_BASE_OFFSET ) )
|
||||
dir_table_base = *( UINT64* )( ( UINT64 )Address + KPROCESS_DIRECTORY_TABLE_BASE_OFFSET );
|
||||
|
||||
if ( MmIsAddressValid( ( UINT64 )PoolHeader + 0x02 ) )
|
||||
{
|
||||
allocation_size = PoolHeader->BlockSize * CHUNK_SIZE - sizeof( POOL_HEADER );
|
||||
pool_type = PoolHeader->PoolType;
|
||||
}
|
||||
|
||||
if ( MmIsAddressValid( ( UINT64 )Address + EPROCESS_PEB_OFFSET ) )
|
||||
peb = *( UINT64* )( ( UINT64 )Address + EPROCESS_PEB_OFFSET );
|
||||
|
||||
if (MmIsAddressValid((UINT64)Address + EPROCESS_OBJECT_TABLE_OFFSET ) )
|
||||
object_table = *( UINT64* )( ( UINT64 )Address + EPROCESS_OBJECT_TABLE_OFFSET );
|
||||
|
||||
peb_test = peb == NULL || ( peb & 0x7ffd0000 == 0x7ffd0000 && peb % 0x1000 == NULL );
|
||||
object_table_test = object_table == NULL || ( object_table & 0xe0000000 == 0xe0000000 && object_table % 0x8 == 0 );
|
||||
allocation_size_test = allocation_size & 0xfff0;
|
||||
|
||||
if ( peak_virtual_size > 0 && ( dir_table_base & 0x20 ) == 0 && allocation_size > EPROCESS_SIZE &&
|
||||
pool_type != NULL && !( allocation_size_test == 0xfff0 ) && !peb_test && !object_table_test )
|
||||
{
|
||||
DEBUG_LOG( "Virtual size: %llx, dir table base: %llx, allocation size: %llx", peak_virtual_size, dir_table_base, allocation_size );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* For ~90% of EPROCESS structures the header layout is as follows:
|
||||
*
|
||||
|
@ -135,7 +205,7 @@ VOID ScanPageForKernelObjectAllocation(
|
|||
_In_ UINT64 PageBase,
|
||||
_In_ ULONG PageSize,
|
||||
_In_ ULONG ObjectIndex,
|
||||
_In_ PPROCESS_SCAN_CONTEXT Context
|
||||
_In_ PVOID AddressBuffer
|
||||
)
|
||||
{
|
||||
INT length = 0;
|
||||
|
@ -143,6 +213,8 @@ VOID ScanPageForKernelObjectAllocation(
|
|||
CHAR current_sig_byte;
|
||||
PPOOL_HEADER pool_header;
|
||||
PEPROCESS process = NULL;
|
||||
PEPROCESS process_size_one = NULL;
|
||||
PEPROCESS process_size_two = NULL;
|
||||
LPCSTR process_name;
|
||||
PUINT64 address_list;
|
||||
ULONG allocation_size;
|
||||
|
@ -164,48 +236,40 @@ VOID ScanPageForKernelObjectAllocation(
|
|||
{
|
||||
pool_header = ( UINT64 )PageBase + offset - 0x04;
|
||||
|
||||
if ( !MmIsAddressValid( (PVOID)pool_header ) )
|
||||
if ( !MmIsAddressValid( ( PVOID )pool_header ) )
|
||||
break;
|
||||
|
||||
/*
|
||||
* This is some hard coded trash, need to figure out how we can differentiate different
|
||||
* types of objects since they would each have a varying number of headers, object sizes etc.
|
||||
* All EPROCESS allocations contain the following header objects:
|
||||
*
|
||||
* For now we check 2 sizes, one of which is 0x10 smaller then the other (the unknown header?)
|
||||
* and make sure the pool is still allocated by checking the PoolType != 0.
|
||||
* -> OBJECT_HEADER with size 0x30
|
||||
* -> OBJECT_HEADER_HANDLE_TABLE with size 0x10
|
||||
* -> OBJECT_HEADER_QUOTA_INFO with size 0x20
|
||||
*
|
||||
* more: https://www.imf-conference.org/imf2006/23_Schuster-PoolAllocations.pdf
|
||||
* And a small number may an unknown additional header with size 0x10
|
||||
*/
|
||||
allocation_size = pool_header->BlockSize * CHUNK_SIZE - sizeof( POOL_HEADER );
|
||||
process_size_one = ( PEPROCESS )( ( UINT64 )pool_header + sizeof( POOL_HEADER ) + KPROCESS_OFFSET_FROM_POOL_HEADER_SIZE_1 );
|
||||
process_size_two = ( PEPROCESS )( ( UINT64 )pool_header + sizeof( POOL_HEADER ) + KPROCESS_OFFSET_FROM_POOL_HEADER_SIZE_2 );
|
||||
|
||||
if ( ( allocation_size == WIN_PROCESS_ALLOCATION_SIZE ||
|
||||
allocation_size == WIN_PROCESS_ALLOCATION_SIZE_2 ) &&
|
||||
pool_header->PoolType != NULL )
|
||||
if ( ValidateIfAddressIsProcessStructure( process_size_one, pool_header ) )
|
||||
process = process_size_one;
|
||||
|
||||
if ( process == NULL )
|
||||
{
|
||||
if ( allocation_size == WIN_PROCESS_ALLOCATION_SIZE )
|
||||
process = process = ( PEPROCESS )( ( UINT64 )pool_header + sizeof( POOL_HEADER ) + 0x70 );
|
||||
|
||||
if ( allocation_size == WIN_PROCESS_ALLOCATION_SIZE_2 )
|
||||
process = process = ( PEPROCESS )( ( UINT64 )pool_header + sizeof( POOL_HEADER ) + 0x80 );
|
||||
|
||||
if ( process == NULL )
|
||||
{
|
||||
DEBUG_LOG( "Process size is different to expected cos we hardcoded dis trash" );
|
||||
if ( ValidateIfAddressIsProcessStructure( process_size_two, pool_header ) )
|
||||
process = process_size_two;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
address_list = ( PUINT64 )Context->process_buffer;
|
||||
address_list = ( PUINT64 )AddressBuffer;
|
||||
|
||||
/*
|
||||
* Find the first free entry in the process address list and store the address.
|
||||
*/
|
||||
for ( INT i = 0; i < Context->process_count; i++ )
|
||||
for ( INT i = 0; i < process_count; i++ )
|
||||
{
|
||||
if ( address_list[ i ] == NULL )
|
||||
{
|
||||
if ( address_list[ i ] == NULL )
|
||||
{
|
||||
address_list[ i ] = ( UINT64 )process;
|
||||
break;
|
||||
}
|
||||
address_list[ i ] = ( UINT64 )process;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,28 +282,10 @@ VOID ScanPageForKernelObjectAllocation(
|
|||
}
|
||||
}
|
||||
|
||||
VOID EnumerateKernelLargePages(
|
||||
_In_ UINT64 PageBase,
|
||||
_In_ ULONG PageSize,
|
||||
_In_ ULONG ObjectIndex,
|
||||
_In_ PPROCESS_SCAN_CONTEXT Context
|
||||
)
|
||||
{
|
||||
for ( INT page_index = 0; page_index < PageSize; page_index++ )
|
||||
{
|
||||
ScanPageForKernelObjectAllocation(
|
||||
PageBase + ( page_index * PageSize ),
|
||||
PAGE_SIZE,
|
||||
ObjectIndex,
|
||||
Context
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Using MmGetPhysicalMemoryRangesEx2(), we can get a block of structures that
|
||||
* describe the physical memory layout. With each physical page base we are going
|
||||
* to enumerate, we want to make sure it lies within an appropriate region of
|
||||
* to enumerate, we want to make sure it lies within an appropriate region of
|
||||
* physical memory, so this function is to check for exactly that.
|
||||
*/
|
||||
BOOLEAN IsPhysicalAddressInPhysicalMemoryRange(
|
||||
|
@ -265,6 +311,24 @@ BOOLEAN IsPhysicalAddressInPhysicalMemoryRange(
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
VOID EnumerateKernelLargePages(
|
||||
_In_ UINT64 PageBase,
|
||||
_In_ ULONG PageSize,
|
||||
_In_ PVOID AddressBuffer,
|
||||
_In_ ULONG ObjectIndex
|
||||
)
|
||||
{
|
||||
for ( INT page_index = 0; page_index < PageSize; page_index++ )
|
||||
{
|
||||
ScanPageForKernelObjectAllocation(
|
||||
PageBase + ( page_index * PageSize ),
|
||||
PAGE_SIZE,
|
||||
ObjectIndex,
|
||||
AddressBuffer
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is your basic page table walk function. On intel systems, paging has 4 levels,
|
||||
* each table holds 512 entries with a total size of 0x1000 (512 * sizeof(QWORD)). Each entry
|
||||
|
@ -272,26 +336,24 @@ BOOLEAN IsPhysicalAddressInPhysicalMemoryRange(
|
|||
* of the base of the next table in the structure. So for example, a PML4 entry contains
|
||||
* a physical address that points to the base of the PDPT table, it is the same for a PDPT
|
||||
* entry -> PD base and so on.
|
||||
*
|
||||
* However, as with all good things Windows has implemented security features meaning
|
||||
* we cannot use functions such as MmCopyMemory or MmMapIoSpace on paging structures,
|
||||
* so we must find another way to walk the pages. Luckily for us, there exists
|
||||
*
|
||||
* However, as with all good things Windows has implemented security features meaning
|
||||
* we cannot use functions such as MmCopyMemory or MmMapIoSpace on paging structures,
|
||||
* so we must find another way to walk the pages. Luckily for us, there exists
|
||||
* MmGetVirtualForPhysical. This function is self explanatory and returns the corresponding
|
||||
* virtual address given a physical address. What this means is that we can extract a page
|
||||
* entry physical address, pass it to MmGetVirtualForPhysical which returns us the virtual
|
||||
* address of the base of the next page structure. This is because page tables are still
|
||||
* address of the base of the next page structure. This is because page tables are still
|
||||
* mapped by the kernel and exist in virtual memory just like everything else and hence
|
||||
* reading the value at all 512 entries from the virtual base will give us the equivalent
|
||||
* reading the value at all 512 entries from the virtual base will give us the equivalent
|
||||
* value as directly reading the physical address.
|
||||
*
|
||||
*
|
||||
* Using this, we essentially walk the page tables as any regular translation would
|
||||
* except instead of simply reading the physical we translate it to a virtual address
|
||||
* and extract the physical address from the value at each virtual address page entry.
|
||||
*/
|
||||
|
||||
VOID WalkKernelPageTables(
|
||||
_In_ PPROCESS_SCAN_CONTEXT Context
|
||||
)
|
||||
VOID WalkKernelPageTables( PVOID AddressBuffer )
|
||||
{
|
||||
CR3 cr3;
|
||||
PML4E pml4_base;
|
||||
|
@ -310,6 +372,7 @@ VOID WalkKernelPageTables(
|
|||
UINT64 base_1gb_virtual_page;
|
||||
PHYSICAL_ADDRESS physical;
|
||||
PPHYSICAL_MEMORY_RANGE physical_memory_ranges;
|
||||
KIRQL irql;
|
||||
|
||||
physical_memory_ranges = MmGetPhysicalMemoryRangesEx2( NULL, NULL );
|
||||
|
||||
|
@ -333,7 +396,7 @@ VOID WalkKernelPageTables(
|
|||
if ( !MmIsAddressValid( pml4_base.BitAddress + pml4_index * sizeof( UINT64 ) ) )
|
||||
continue;
|
||||
|
||||
pml4_entry.BitAddress = *(UINT64*)( pml4_base.BitAddress + pml4_index * sizeof( UINT64 ) );
|
||||
pml4_entry.BitAddress = *( UINT64* )( pml4_base.BitAddress + pml4_index * sizeof( UINT64 ) );
|
||||
|
||||
if ( pml4_entry.Bits.Present == NULL )
|
||||
continue;
|
||||
|
@ -367,14 +430,14 @@ VOID WalkKernelPageTables(
|
|||
|
||||
base_1gb_virtual_page = MmGetVirtualForPhysical( physical );
|
||||
|
||||
if (!base_1gb_virtual_page || !MmIsAddressValid( base_1gb_virtual_page ) )
|
||||
if ( !base_1gb_virtual_page || !MmIsAddressValid( base_1gb_virtual_page ) )
|
||||
continue;
|
||||
|
||||
EnumerateKernelLargePages(
|
||||
base_1gb_virtual_page,
|
||||
LARGE_PAGE_1GB_ENTRIES,
|
||||
INDEX_PROCESS_POOL_TAG,
|
||||
Context
|
||||
AddressBuffer,
|
||||
INDEX_PROCESS_POOL_TAG
|
||||
);
|
||||
|
||||
continue;
|
||||
|
@ -415,8 +478,8 @@ VOID WalkKernelPageTables(
|
|||
EnumerateKernelLargePages(
|
||||
base_2mb_virtual_page,
|
||||
LARGE_PAGE_2MB_ENTRIES,
|
||||
INDEX_PROCESS_POOL_TAG,
|
||||
Context
|
||||
AddressBuffer,
|
||||
INDEX_PROCESS_POOL_TAG
|
||||
);
|
||||
|
||||
continue;
|
||||
|
@ -424,9 +487,6 @@ VOID WalkKernelPageTables(
|
|||
|
||||
physical.QuadPart = pd_entry.Bits.PhysicalAddress << PAGE_4KB_SHIFT;
|
||||
|
||||
if ( !MmIsAddressValid( pd_entry.BitAddress ) )
|
||||
continue;
|
||||
|
||||
pt_base = MmGetVirtualForPhysical( physical );
|
||||
|
||||
if ( !pt_base || !MmIsAddressValid( pt_base ) )
|
||||
|
@ -458,7 +518,7 @@ VOID WalkKernelPageTables(
|
|||
base_virtual_page,
|
||||
PAGE_BASE_SIZE,
|
||||
INDEX_PROCESS_POOL_TAG,
|
||||
Context
|
||||
AddressBuffer
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -468,38 +528,25 @@ VOID WalkKernelPageTables(
|
|||
DEBUG_LOG( "Finished scanning memory" );
|
||||
}
|
||||
|
||||
VOID IncrementProcessCounter(
|
||||
_In_ PEPROCESS Process,
|
||||
_Inout_opt_ PVOID Context
|
||||
)
|
||||
VOID IncrementProcessCounter()
|
||||
{
|
||||
PPROCESS_SCAN_CONTEXT context = ( PPROCESS_SCAN_CONTEXT )Context;
|
||||
|
||||
if ( !context )
|
||||
return;
|
||||
|
||||
context->process_count += 1;
|
||||
process_count++;
|
||||
}
|
||||
|
||||
VOID CheckIfProcessAllocationIsInProcessList(
|
||||
_In_ PEPROCESS Process,
|
||||
_Inout_opt_ PVOID Context
|
||||
_In_ PEPROCESS Process
|
||||
)
|
||||
{
|
||||
PUINT64 allocation_address;
|
||||
PPROCESS_SCAN_CONTEXT context = ( PPROCESS_SCAN_CONTEXT )Context;
|
||||
|
||||
if ( !context )
|
||||
return;
|
||||
|
||||
for ( INT i = 0; i < context->process_count; i++ )
|
||||
for ( INT i = 0; i < process_count; i++ )
|
||||
{
|
||||
allocation_address = ( PUINT64 )context->process_buffer;
|
||||
allocation_address = ( PUINT64 )process_buffer;
|
||||
|
||||
if ( ( UINT64 )Process >= allocation_address[ i ] - PROCESS_OBJECT_ALLOCATION_MARGIN &&
|
||||
( UINT64 )Process <= allocation_address[ i ] + PROCESS_OBJECT_ALLOCATION_MARGIN )
|
||||
{
|
||||
RtlZeroMemory( ( UINT64 )context->process_buffer + i * sizeof( UINT64 ), sizeof( UINT64 ) );
|
||||
RtlZeroMemory( ( UINT64 )process_buffer + i * sizeof( UINT64 ), sizeof( UINT64 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -521,74 +568,74 @@ NTSTATUS FindUnlinkedProcesses(
|
|||
{
|
||||
PUINT64 allocation_address;
|
||||
PINVALID_PROCESS_ALLOCATION_REPORT report_buffer = NULL;
|
||||
PROCESS_SCAN_CONTEXT context = { 0 };
|
||||
|
||||
EnumerateProcessListWithCallbackFunction(
|
||||
IncrementProcessCounter,
|
||||
&context
|
||||
NULL
|
||||
);
|
||||
|
||||
if ( context.process_count == NULL )
|
||||
if ( process_count == NULL )
|
||||
{
|
||||
DEBUG_ERROR( "Faield to get process count " );
|
||||
return STATUS_ABANDONED;
|
||||
}
|
||||
|
||||
context.process_buffer =
|
||||
ExAllocatePool2( POOL_FLAG_NON_PAGED, context.process_count * 2 * sizeof( UINT64 ), PROCESS_ADDRESS_LIST_TAG );
|
||||
process_buffer = ExAllocatePool2( POOL_FLAG_NON_PAGED, process_count * 2 * sizeof( UINT64 ), PROCESS_ADDRESS_LIST_TAG );
|
||||
|
||||
if ( !context.process_buffer )
|
||||
return STATUS_MEMORY_NOT_ALLOCATED;
|
||||
if ( !process_buffer )
|
||||
return STATUS_ABANDONED;
|
||||
|
||||
WalkKernelPageTables( &context );
|
||||
WalkKernelPageTables( process_buffer );
|
||||
|
||||
EnumerateProcessListWithCallbackFunction(
|
||||
CheckIfProcessAllocationIsInProcessList,
|
||||
&context
|
||||
NULL
|
||||
);
|
||||
|
||||
allocation_address = ( PUINT64 )context.process_buffer;
|
||||
allocation_address = ( PUINT64 )process_buffer;
|
||||
|
||||
for ( INT i = 0; i < context.process_count; i++ )
|
||||
for ( INT i = 0; i < process_count; i++ )
|
||||
{
|
||||
if ( allocation_address[ i ] == NULL )
|
||||
continue;
|
||||
|
||||
/* process has been deallocated yet the pool header hasnt been updated? */
|
||||
if ( *( UINT8* )( allocation_address[ i ] + EPROCESS_VIRTUAL_SIZE_OFFSET ) == 0x00 )
|
||||
if ( *( UINT8* )( allocation_address[ i ] + EPROCESS_PEAK_VIRTUAL_SIZE_OFFSET ) == 0x00 )
|
||||
continue;
|
||||
|
||||
/* report / do some further analysis */
|
||||
DEBUG_ERROR( "INVALID POOL proc OMGGG" );
|
||||
|
||||
report_buffer =
|
||||
ExAllocatePool2( POOL_FLAG_NON_PAGED, sizeof( INVALID_PROCESS_ALLOCATION_REPORT ), INVALID_PROCESS_REPORT_TAG );
|
||||
report_buffer = ExAllocatePool2( POOL_FLAG_NON_PAGED, sizeof( INVALID_PROCESS_ALLOCATION_REPORT ), INVALID_PROCESS_REPORT_TAG );
|
||||
|
||||
if ( !report_buffer )
|
||||
goto end;
|
||||
|
||||
report_buffer->report_code = REPORT_INVALID_PROCESS_ALLOCATION;
|
||||
|
||||
RtlCopyMemory(
|
||||
RtlCopyMemory(
|
||||
report_buffer->process,
|
||||
allocation_address[i],
|
||||
allocation_address[ i ],
|
||||
REPORT_INVALID_PROCESS_BUFFER_SIZE );
|
||||
|
||||
Irp->IoStatus.Information = sizeof( INVALID_PROCESS_ALLOCATION_REPORT );
|
||||
|
||||
RtlCopyMemory(
|
||||
Irp->AssociatedIrp.SystemBuffer,
|
||||
report_buffer,
|
||||
RtlCopyMemory(
|
||||
Irp->AssociatedIrp.SystemBuffer,
|
||||
report_buffer,
|
||||
sizeof( INVALID_PROCESS_ALLOCATION_REPORT ) );
|
||||
}
|
||||
|
||||
end:
|
||||
|
||||
if (report_buffer )
|
||||
if ( report_buffer )
|
||||
ExFreePoolWithTag( report_buffer, INVALID_PROCESS_REPORT_TAG );
|
||||
|
||||
if (context.process_buffer )
|
||||
ExFreePoolWithTag( context.process_buffer, PROCESS_ADDRESS_LIST_TAG );
|
||||
if ( process_buffer )
|
||||
ExFreePoolWithTag( process_buffer, PROCESS_ADDRESS_LIST_TAG );
|
||||
|
||||
process_count = NULL;
|
||||
process_buffer = NULL;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
|
@ -92,29 +92,6 @@ namespace server.Message
|
|||
info.MotherboardSerialNumber,
|
||||
info.DeviceDriver0Serial);
|
||||
|
||||
/*
|
||||
* When a client connects to the server, we will perform the following.
|
||||
*
|
||||
* 1. Check if the user exists,
|
||||
* - if the user doesn't exist we instert them into the database.
|
||||
* - if the user does exist, check if they're banned
|
||||
* - if the user is banned, return a response packet stating the
|
||||
* client may not proceed
|
||||
* 2. Next we check if the hardware is banned. We don't care if the hardware doesn't exist
|
||||
* at this point because if it doesn't exist it can't be banned.
|
||||
* - If the hardware is banned, return a cannot proceed packet.
|
||||
* 3. Here, we use the method GetUserBySteamId get either get the existing user from the database
|
||||
* or the newly created user. We then set the User property of the HardwareConfigurationEntity
|
||||
* - This prevents the bug where a new user was always being added with an existing
|
||||
* user existing with the same steam id.
|
||||
* 4. Then we check if the users hardware already exists in the database, and the foreign
|
||||
* UserId references the user by checking if the Steam64Id matches.
|
||||
* - If a hardware configuration already exists, we send a response packet
|
||||
* allowing the client to continue.
|
||||
* 5. If we make it to here, the user is new and the hardware is not banned, so we then create
|
||||
* a new user and a new hardware configuration and insert it into the database and then
|
||||
* return a packet notifying the client can continue.
|
||||
*/
|
||||
using (var context = new ModelContext())
|
||||
{
|
||||
context.Database.EnsureCreated();
|
||||
|
@ -124,25 +101,30 @@ namespace server.Message
|
|||
Steam64Id = this._packetHeader.steam64_id
|
||||
};
|
||||
|
||||
if (!user.CheckIfUserExists())
|
||||
{
|
||||
_logger.Information("User does not exist in database, creating new user.");
|
||||
user.InsertUser();
|
||||
}
|
||||
else if (user.CheckIfUserIsBanned())
|
||||
{
|
||||
_logger.Information("User is banned, updating response packet to halt client.");
|
||||
SetResponsePacketData(0, sendPacketHeader.RequestId, (int)USER_BAN_REASONS.USER_BAN);
|
||||
return;
|
||||
}
|
||||
|
||||
var hardwareConfiguration = new HardwareConfigurationEntity(context)
|
||||
{
|
||||
DeviceDrive0Serial = info.DeviceDriver0Serial,
|
||||
MotherboardSerial = info.MotherboardSerialNumber,
|
||||
User = user.GetUserBySteamId(this._packetHeader.steam64_id)
|
||||
};
|
||||
|
||||
if (user.CheckIfUserExists())
|
||||
{
|
||||
if (user.CheckIfUserIsBanned())
|
||||
{
|
||||
_logger.Information("User is banned, updating response packet to halt client.");
|
||||
SetResponsePacketData(0, sendPacketHeader.RequestId, (int)USER_BAN_REASONS.USER_BAN);
|
||||
return;
|
||||
}
|
||||
|
||||
hardwareConfiguration.User = user.GetUserBySteamId(this._packetHeader.steam64_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Information("User does not exist in database, creating new user.");
|
||||
user.InsertUser();
|
||||
hardwareConfiguration.User = user;
|
||||
}
|
||||
|
||||
if (hardwareConfiguration.CheckIfHardwareIsBanned())
|
||||
{
|
||||
_logger.Information("User is hardware banned, updating response packet to halt client.");
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define COMMON_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "../debuglib/symbols.h"
|
||||
|
||||
#define LOG_INFO(fmt, ...) printf("[+] " fmt "\n", ##__VA_ARGS__)
|
||||
#define LOG_ERROR(fmt, ...) printf("[-] " fmt "\n", ##__VA_ARGS__)
|
||||
|
|
|
@ -626,3 +626,26 @@ VOID kernelmode::Driver::SendClientHardwareInformation()
|
|||
this->report_interface->ServerSend(
|
||||
&system_information, sizeof( global::headers::SYSTEM_INFORMATION ), CLIENT_SEND_SYSTEM_INFORMATION );
|
||||
}
|
||||
|
||||
#pragma comment(lib, "debuglib")
|
||||
|
||||
VOID GetKernelStructureOffsets()
|
||||
{
|
||||
KERNEL_STRUCTURE_OFFSETS offsets = { 0 };
|
||||
GetKernelStructureOffsets( &offsets );
|
||||
|
||||
LOG_INFO( "KPROCESS->ThreadListHead: %lx", offsets.KPROCESS.thread_list_head );
|
||||
LOG_INFO( "KPROCESS->DirectoryTableBase: %lx", offsets.KPROCESS.directory_table_base );
|
||||
|
||||
LOG_INFO( "EPROCESS->PeakVirtualSize: %lx", offsets.EPROCESS.peak_virtual_size );
|
||||
LOG_INFO( "EPROCESS->VadRoot: %lx", offsets.EPROCESS.vad_root );
|
||||
LOG_INFO( "EPROCESS->ObjectTable: %lx", offsets.EPROCESS.object_table );
|
||||
LOG_INFO( "EPROCESS->ImageFileName: %lx", offsets.EPROCESS.image_name );
|
||||
LOG_INFO( "EPROCESS->Peb: %lx", offsets.EPROCESS.process_environment_block );
|
||||
|
||||
LOG_INFO( "KTHREAD->StackBase: %lx", offsets.KTHREAD.stack_base );
|
||||
LOG_INFO( "KTHREAD->StackLimit: %lx", offsets.KTHREAD.stack_limit );
|
||||
LOG_INFO( "KTHREAD->ThreadListEntry: %lx", offsets.KTHREAD.threadlist );
|
||||
LOG_INFO( "KTHREAD->ApcState: %lx", offsets.KTHREAD.apc_state );
|
||||
LOG_INFO( "KTHREAD->StartAddress: %lx", offsets.KTHREAD.start_address );
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
|
||||
#define MAX_MODULE_PATH 256
|
||||
|
||||
void GetKernelStructureOffsets();
|
||||
|
||||
namespace kernelmode
|
||||
{
|
||||
class Driver
|
||||
|
@ -38,6 +40,7 @@ namespace kernelmode
|
|||
VOID NotifyDriverOnProcessLaunch();
|
||||
VOID CheckDriverHeartbeat();
|
||||
VOID NotifyDriverOnProcessTermination();
|
||||
//VOID GetKernelStructureOffsets();
|
||||
|
||||
public:
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include <iostream>
|
||||
#include <Windows.h>
|
||||
#include <string>
|
||||
#include <WDBGEXTS.H>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
|
@ -17,6 +18,13 @@ DWORD WINAPI Init(HINSTANCE hinstDLL)
|
|||
freopen_s( &file, "CONOUT$", "w", stdout );
|
||||
freopen_s( &file, "CONIN$", "r", stdin );
|
||||
|
||||
GetKernelStructureOffsets();
|
||||
|
||||
while ( true )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
|
||||
|
||||
LPTSTR pipe_name = (LPTSTR)L"\\\\.\\pipe\\DonnaACPipe";
|
||||
|
@ -40,34 +48,35 @@ DWORD WINAPI Init(HINSTANCE hinstDLL)
|
|||
|
||||
while ( !GetAsyncKeyState( VK_DELETE ) )
|
||||
{
|
||||
srand( time( NULL ) );
|
||||
int seed = ( rand() % 6 );
|
||||
kmanager.ScanPoolsForUnlinkedProcesses();
|
||||
//srand( time( NULL ) );
|
||||
//int seed = ( rand() % 6 );
|
||||
|
||||
std::cout << "Seed: " << seed << std::endl;
|
||||
//std::cout << "Seed: " << seed << std::endl;
|
||||
|
||||
switch ( seed )
|
||||
{
|
||||
case 0:
|
||||
kmanager.EnumerateHandleTables();
|
||||
break;
|
||||
case 1:
|
||||
kmanager.PerformIntegrityCheck();
|
||||
break;
|
||||
case 2:
|
||||
kmanager.ScanPoolsForUnlinkedProcesses();
|
||||
break;
|
||||
case 3:
|
||||
kmanager.VerifySystemModules();
|
||||
break;
|
||||
case 4:
|
||||
kmanager.ValidateProcessModules();
|
||||
break;
|
||||
case 5:
|
||||
kmanager.RunNmiCallbacks();
|
||||
break;
|
||||
}
|
||||
//switch ( seed )
|
||||
//{
|
||||
//case 0:
|
||||
// kmanager.EnumerateHandleTables();
|
||||
// break;
|
||||
//case 1:
|
||||
// kmanager.PerformIntegrityCheck();
|
||||
// break;
|
||||
//case 2:
|
||||
// kmanager.ScanPoolsForUnlinkedProcesses();
|
||||
// break;
|
||||
//case 3:
|
||||
// kmanager.VerifySystemModules();
|
||||
// break;
|
||||
//case 4:
|
||||
// kmanager.ValidateProcessModules();
|
||||
// break;
|
||||
//case 5:
|
||||
// kmanager.RunNmiCallbacks();
|
||||
// break;
|
||||
//}
|
||||
|
||||
kmanager.MonitorCallbackReports();
|
||||
//kmanager.MonitorCallbackReports();
|
||||
|
||||
std::this_thread::sleep_for( std::chrono::seconds( 10 ) );
|
||||
}
|
||||
|
|
|
@ -109,6 +109,7 @@
|
|||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Imagehlp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>C:\Users\lachuie\source\repos\ac\x64\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
|
@ -126,6 +127,7 @@
|
|||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Imagehlp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>C:\Users\lachuie\source\repos\ac\x64\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
|
|
Loading…
Reference in a new issue