mirror of
https://github.com/synacktiv/windows_kernel_shadow_stack
synced 2026-08-09 13:12:06 +00:00
Initial commit
This commit is contained in:
+106
@@ -0,0 +1,106 @@
|
||||
---
|
||||
AccessModifierOffset: '-4'
|
||||
# AlignAfterOpenBracket: AlwaysBreak
|
||||
AlignConsecutiveMacros: 'true'
|
||||
AlignConsecutiveAssignments: 'true'
|
||||
AlignConsecutiveDeclarations: 'true'
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: 'true'
|
||||
AlignTrailingComments: 'true'
|
||||
AllowAllArgumentsOnNextLine: 'false'
|
||||
AllowAllConstructorInitializersOnNextLine: 'false'
|
||||
AllowAllParametersOfDeclarationOnNextLine: 'false'
|
||||
AllowShortBlocksOnASingleLine: 'true'
|
||||
# AllowShortCaseLabelsOnASingleLine: 'false'
|
||||
AllowShortCaseLabelsOnASingleLine: 'true'
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
AllowShortIfStatementsOnASingleLine: 'false'
|
||||
# AllowShortLambdasOnASingleLine: Inline
|
||||
# AllowShortLoopsOnASingleLine: 'true'
|
||||
AllowShortLoopsOnASingleLine: 'false'
|
||||
AlwaysBreakAfterDefinitionReturnType: 'false'
|
||||
AlwaysBreakAfterReturnType: None
|
||||
# AlwaysBreakBeforeMultilineStrings: 'false'
|
||||
# AlwaysBreakTemplateDeclarations: 'No'
|
||||
BinPackArguments: 'false'
|
||||
BinPackParameters: 'false'
|
||||
BraceWrapping:
|
||||
# AfterCaseLabel: 'true'
|
||||
AfterClass: 'true'
|
||||
AfterControlStatement: 'true'
|
||||
AfterEnum: 'true'
|
||||
AfterFunction: 'true'
|
||||
AfterNamespace: 'true'
|
||||
AfterObjCDeclaration: 'true'
|
||||
AfterStruct: 'true'
|
||||
AfterUnion: 'true'
|
||||
AfterExternBlock: 'false'
|
||||
BeforeCatch: 'true'
|
||||
BeforeElse: 'true'
|
||||
IndentBraces: 'false'
|
||||
SplitEmptyFunction: 'true'
|
||||
SplitEmptyRecord: 'true'
|
||||
SplitEmptyNamespace: 'true'
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Custom
|
||||
BreakBeforeTernaryOperators: 'false'
|
||||
BreakConstructorInitializers: AfterColon
|
||||
# BreakInheritanceList: AfterColon
|
||||
# BreakStringLiterals: 'true'
|
||||
ColumnLimit: '0'
|
||||
CommentPragmas: Suppress
|
||||
CompactNamespaces: 'false'
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: 'true'
|
||||
ConstructorInitializerIndentWidth: '2'
|
||||
ContinuationIndentWidth: '2'
|
||||
Cpp11BracedListStyle: 'false'
|
||||
DerivePointerAlignment: 'false'
|
||||
# DisableFormat: 'false'
|
||||
# ExperimentalAutoDetectBinPacking: 'false'
|
||||
FixNamespaceComments: 'false'
|
||||
IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
- Regex: '^.*(precomp:pch:stdafx)'
|
||||
Priority: -1
|
||||
- Regex: '^".*"'
|
||||
Priority: 1
|
||||
- Regex: '^<.*>'
|
||||
Priority: 2
|
||||
- Regex: '.*'
|
||||
Priority: 3
|
||||
IndentCaseLabels: 'true'
|
||||
# IndentExternBlock: 'false'
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: '2'
|
||||
IndentWrappedFunctionNames: 'false'
|
||||
KeepEmptyLinesAtTheStartOfBlocks: 'false'
|
||||
# Language: Cpp
|
||||
MacroBlockBegin: "BEGIN_TEST_METHOD_PROPERTIES|BEGIN_MODULE|BEGIN_TEST_CLASS|BEGIN_TEST_METHOD"
|
||||
MacroBlockEnd: "END_TEST_METHOD_PROPERTIES|END_MODULE|END_TEST_CLASS|END_TEST_METHOD"
|
||||
MaxEmptyLinesToKeep: '1'
|
||||
NamespaceIndentation: All
|
||||
PointerAlignment: Left
|
||||
ReflowComments: 'false'
|
||||
SortIncludes: 'false'
|
||||
SortUsingDeclarations: 'true'
|
||||
SpaceAfterCStyleCast: 'false'
|
||||
# SpaceAfterLogicalNot: 'false'
|
||||
SpaceAfterTemplateKeyword: 'false'
|
||||
SpaceBeforeAssignmentOperators: 'true'
|
||||
# SpaceBeforeCpp11BracedList: 'true'
|
||||
# SpaceBeforeCtorInitializerColon: 'false'
|
||||
# SpaceBeforeInheritanceColon: 'false'
|
||||
SpaceBeforeParens: ControlStatements
|
||||
# SpaceBeforeRangeBasedForLoopColon: 'false'
|
||||
SpaceInEmptyParentheses: 'false'
|
||||
SpacesBeforeTrailingComments: '1'
|
||||
SpacesInAngles: 'false'
|
||||
# SpacesInContainerLiterals: 'false'
|
||||
SpacesInCStyleCastParentheses: 'false'
|
||||
SpacesInParentheses: 'false'
|
||||
SpacesInSquareBrackets: 'false'
|
||||
Standard: Cpp11
|
||||
TabWidth: '2'
|
||||
UseTab: Never
|
||||
|
||||
...
|
||||
@@ -0,0 +1,5 @@
|
||||
.vscode/
|
||||
bin/
|
||||
build/
|
||||
build32/
|
||||
build64/
|
||||
@@ -0,0 +1,79 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(shadow_stack LANGUAGES C ASM)
|
||||
|
||||
enable_language(ASM_MASM)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
|
||||
find_package(WDK REQUIRED)
|
||||
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin/${ARCH})
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin/${ARCH})
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin/${ARCH})
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /WX")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W4 /WX")
|
||||
|
||||
set(SIGNTOOL_PATH ${WDK_ROOT}/bin/${WDK_VERSION}/x64/signtool.exe)
|
||||
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||
include_directories(src)
|
||||
|
||||
wdk_add_driver(
|
||||
shadow_stack_driver
|
||||
# [WINVER <winver_version>]
|
||||
NTDDI_VERSION 0x0A00000E
|
||||
|
||||
${CMAKE_SOURCE_DIR}/src/driver.c
|
||||
${CMAKE_SOURCE_DIR}/src/driver/ioctl.c
|
||||
${CMAKE_SOURCE_DIR}/src/driver/utils.c
|
||||
)
|
||||
|
||||
target_link_options(
|
||||
shadow_stack_driver
|
||||
PRIVATE
|
||||
|
||||
"/CETCOMPAT"
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
TARGET shadow_stack_driver
|
||||
POST_BUILD
|
||||
|
||||
COMMAND ${SIGNTOOL_PATH} sign /fd SHA256 $<TARGET_FILE:shadow_stack_driver>
|
||||
)
|
||||
|
||||
add_executable(
|
||||
shadow_stack_client
|
||||
|
||||
${CMAKE_SOURCE_DIR}/src/client.c
|
||||
|
||||
${CMAKE_SOURCE_DIR}/src/client/utils.c
|
||||
)
|
||||
|
||||
target_link_options(
|
||||
shadow_stack_client
|
||||
PRIVATE
|
||||
|
||||
"/CETCOMPAT"
|
||||
)
|
||||
|
||||
add_library(
|
||||
asm_target
|
||||
STATIC
|
||||
|
||||
${CMAKE_SOURCE_DIR}/src/driver/utils.asm
|
||||
)
|
||||
|
||||
target_compile_options(
|
||||
shadow_stack_driver
|
||||
PRIVATE
|
||||
|
||||
"/Od"
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
shadow_stack_driver
|
||||
|
||||
asm_target
|
||||
)
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
# Redistribution and use is allowed under the OSI-approved 3-clause BSD license.
|
||||
# Copyright (c) 2018 Sergey Podobry (sergey.podobry at gmail.com). All rights reserved.
|
||||
|
||||
#.rst:
|
||||
# FindWDK
|
||||
# ----------
|
||||
#
|
||||
# This module searches for the installed Windows Development Kit (WDK) and
|
||||
# exposes commands for creating kernel drivers and kernel libraries.
|
||||
#
|
||||
# Output variables:
|
||||
# - `WDK_FOUND` -- if false, do not try to use WDK
|
||||
# - `WDK_ROOT` -- where WDK is installed
|
||||
# - `WDK_VERSION` -- the version of the selected WDK
|
||||
# - `WDK_WINVER` -- the WINVER used for kernel drivers and libraries
|
||||
# (default value is `0x0601` and can be changed per target or globally)
|
||||
# - `WDK_NTDDI_VERSION` -- the NTDDI_VERSION used for kernel drivers and libraries,
|
||||
# if not set, the value will be automatically calculated by WINVER
|
||||
# (default value is left blank and can be changed per target or globally)
|
||||
#
|
||||
# Example usage:
|
||||
#
|
||||
# find_package(WDK REQUIRED)
|
||||
#
|
||||
# wdk_add_library(KmdfCppLib STATIC KMDF 1.15
|
||||
# KmdfCppLib.h
|
||||
# KmdfCppLib.cpp
|
||||
# )
|
||||
# target_include_directories(KmdfCppLib INTERFACE .)
|
||||
#
|
||||
# wdk_add_driver(KmdfCppDriver KMDF 1.15
|
||||
# Main.cpp
|
||||
# )
|
||||
# target_link_libraries(KmdfCppDriver KmdfCppLib)
|
||||
#
|
||||
|
||||
if(DEFINED ENV{WDKContentRoot})
|
||||
file(GLOB WDK_NTDDK_FILES
|
||||
"$ENV{WDKContentRoot}/Include/*/km/ntddk.h" # WDK 10
|
||||
"$ENV{WDKContentRoot}/Include/km/ntddk.h" # WDK 8.0, 8.1
|
||||
)
|
||||
else()
|
||||
file(GLOB WDK_NTDDK_FILES
|
||||
"C:/Program Files*/Windows Kits/*/Include/*/km/ntddk.h" # WDK 10
|
||||
"C:/Program Files*/Windows Kits/*/Include/km/ntddk.h" # WDK 8.0, 8.1
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WDK_NTDDK_FILES)
|
||||
if (NOT CMAKE_VERSION VERSION_LESS 3.18.0)
|
||||
list(SORT WDK_NTDDK_FILES COMPARE NATURAL) # sort to use the latest available WDK
|
||||
endif()
|
||||
list(GET WDK_NTDDK_FILES -1 WDK_LATEST_NTDDK_FILE)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(WDK REQUIRED_VARS WDK_LATEST_NTDDK_FILE)
|
||||
|
||||
if(NOT WDK_LATEST_NTDDK_FILE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
get_filename_component(WDK_ROOT ${WDK_LATEST_NTDDK_FILE} DIRECTORY)
|
||||
get_filename_component(WDK_ROOT ${WDK_ROOT} DIRECTORY)
|
||||
get_filename_component(WDK_VERSION ${WDK_ROOT} NAME)
|
||||
get_filename_component(WDK_ROOT ${WDK_ROOT} DIRECTORY)
|
||||
if (NOT WDK_ROOT MATCHES ".*/[0-9][0-9.]*$") # WDK 10 has a deeper nesting level
|
||||
get_filename_component(WDK_ROOT ${WDK_ROOT} DIRECTORY) # go up once more
|
||||
set(WDK_LIB_VERSION "${WDK_VERSION}")
|
||||
set(WDK_INC_VERSION "${WDK_VERSION}")
|
||||
else() # WDK 8.0, 8.1
|
||||
set(WDK_INC_VERSION "")
|
||||
foreach(VERSION winv6.3 win8 win7)
|
||||
if (EXISTS "${WDK_ROOT}/Lib/${VERSION}/")
|
||||
set(WDK_LIB_VERSION "${VERSION}")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
set(WDK_VERSION "${WDK_LIB_VERSION}")
|
||||
endif()
|
||||
|
||||
message(STATUS "WDK_ROOT: " ${WDK_ROOT})
|
||||
message(STATUS "WDK_VERSION: " ${WDK_VERSION})
|
||||
|
||||
set(WDK_WINVER "0x0601" CACHE STRING "Default WINVER for WDK targets")
|
||||
set(WDK_NTDDI_VERSION "" CACHE STRING "Specified NTDDI_VERSION for WDK targets if needed")
|
||||
|
||||
set(WDK_ADDITIONAL_FLAGS_FILE "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/wdkflags.h")
|
||||
file(WRITE ${WDK_ADDITIONAL_FLAGS_FILE} "#pragma runtime_checks(\"suc\", off)")
|
||||
|
||||
set(WDK_COMPILE_FLAGS
|
||||
"/Zp8" # set struct alignment
|
||||
"/GF" # enable string pooling
|
||||
"/GR-" # disable RTTI
|
||||
"/Gz" # __stdcall by default
|
||||
"/kernel" # create kernel mode binary
|
||||
"/FIwarning.h" # disable warnings in WDK headers
|
||||
"/FI${WDK_ADDITIONAL_FLAGS_FILE}" # include file to disable RTC
|
||||
"/Oi" # enable intrinsic functions so that you can use functions like _disable or _enable
|
||||
)
|
||||
|
||||
set(WDK_COMPILE_DEFINITIONS "WINNT=1")
|
||||
set(WDK_COMPILE_DEFINITIONS_DEBUG "MSC_NOOPT;DEPRECATE_DDK_FUNCTIONS=1;DBG=1")
|
||||
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
list(APPEND WDK_COMPILE_DEFINITIONS "_X86_=1;i386=1;STD_CALL")
|
||||
set(WDK_PLATFORM "x86")
|
||||
elseif(CMAKE_SIZEOF_VOID_P EQUAL 8 AND CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "ARM64")
|
||||
list(APPEND WDK_COMPILE_DEFINITIONS "_ARM64_;ARM64;_USE_DECLSPECS_FOR_SAL=1;STD_CALL")
|
||||
set(WDK_PLATFORM "arm64")
|
||||
elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
list(APPEND WDK_COMPILE_DEFINITIONS "_AMD64_;AMD64")
|
||||
set(WDK_PLATFORM "x64")
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported architecture")
|
||||
endif()
|
||||
|
||||
string(CONCAT WDK_LINK_FLAGS
|
||||
"/MANIFEST:NO " #
|
||||
"/DRIVER " #
|
||||
"/OPT:REF " #
|
||||
"/INCREMENTAL:NO " #
|
||||
"/OPT:ICF " #
|
||||
"/SUBSYSTEM:NATIVE " #
|
||||
"/MERGE:_TEXT=.text;_PAGE=PAGE " #
|
||||
"/NODEFAULTLIB " # do not link default CRT
|
||||
"/SECTION:INIT,d " #
|
||||
"/VERSION:10.0 " #
|
||||
)
|
||||
|
||||
# Generate imported targets for WDK lib files
|
||||
file(GLOB WDK_LIBRARIES "${WDK_ROOT}/Lib/${WDK_LIB_VERSION}/km/${WDK_PLATFORM}/*.lib")
|
||||
foreach(LIBRARY IN LISTS WDK_LIBRARIES)
|
||||
get_filename_component(LIBRARY_NAME ${LIBRARY} NAME_WE)
|
||||
string(TOUPPER ${LIBRARY_NAME} LIBRARY_NAME)
|
||||
add_library(WDK::${LIBRARY_NAME} INTERFACE IMPORTED)
|
||||
set_property(TARGET WDK::${LIBRARY_NAME} PROPERTY INTERFACE_LINK_LIBRARIES ${LIBRARY})
|
||||
endforeach(LIBRARY)
|
||||
unset(WDK_LIBRARIES)
|
||||
|
||||
function(wdk_add_driver _target)
|
||||
cmake_parse_arguments(WDK "" "KMDF;WINVER;NTDDI_VERSION" "" ${ARGN})
|
||||
|
||||
add_executable(${_target} ${WDK_UNPARSED_ARGUMENTS})
|
||||
|
||||
set_target_properties(${_target} PROPERTIES SUFFIX ".sys")
|
||||
set_target_properties(${_target} PROPERTIES COMPILE_OPTIONS "${WDK_COMPILE_FLAGS}")
|
||||
set_target_properties(${_target} PROPERTIES COMPILE_DEFINITIONS
|
||||
"${WDK_COMPILE_DEFINITIONS};$<$<CONFIG:Debug>:${WDK_COMPILE_DEFINITIONS_DEBUG}>;_WIN32_WINNT=${WDK_WINVER}"
|
||||
)
|
||||
set_target_properties(${_target} PROPERTIES LINK_FLAGS "${WDK_LINK_FLAGS}")
|
||||
if(WDK_NTDDI_VERSION)
|
||||
target_compile_definitions(${_target} PRIVATE NTDDI_VERSION=${WDK_NTDDI_VERSION})
|
||||
endif()
|
||||
|
||||
target_include_directories(${_target} SYSTEM PRIVATE
|
||||
"${WDK_ROOT}/Include/${WDK_INC_VERSION}/shared"
|
||||
"${WDK_ROOT}/Include/${WDK_INC_VERSION}/km"
|
||||
"${WDK_ROOT}/Include/${WDK_INC_VERSION}/km/crt"
|
||||
)
|
||||
|
||||
target_link_libraries(${_target} WDK::NTOSKRNL WDK::HAL WDK::WMILIB)
|
||||
|
||||
if(WDK::BUFFEROVERFLOWK)
|
||||
target_link_libraries(${_target} WDK::BUFFEROVERFLOWK) # to support Windows 7 and Vista
|
||||
else()
|
||||
target_link_libraries(${_target} WDK::BUFFEROVERFLOWFASTFAILK)
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "ARM64")
|
||||
target_link_libraries(${_target} "arm64rt.lib")
|
||||
endif()
|
||||
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
target_link_libraries(${_target} WDK::MEMCMP)
|
||||
endif()
|
||||
|
||||
if(DEFINED WDK_KMDF)
|
||||
target_include_directories(${_target} SYSTEM PRIVATE "${WDK_ROOT}/Include/wdf/kmdf/${WDK_KMDF}")
|
||||
target_link_libraries(${_target}
|
||||
"${WDK_ROOT}/Lib/wdf/kmdf/${WDK_PLATFORM}/${WDK_KMDF}/WdfDriverEntry.lib"
|
||||
"${WDK_ROOT}/Lib/wdf/kmdf/${WDK_PLATFORM}/${WDK_KMDF}/WdfLdr.lib"
|
||||
)
|
||||
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS "/ENTRY:FxDriverEntry@8")
|
||||
elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS "/ENTRY:FxDriverEntry")
|
||||
endif()
|
||||
else()
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS "/ENTRY:GsDriverEntry@8")
|
||||
elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS "/ENTRY:GsDriverEntry")
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(wdk_add_library _target)
|
||||
cmake_parse_arguments(WDK "" "KMDF;WINVER;NTDDI_VERSION" "" ${ARGN})
|
||||
|
||||
add_library(${_target} ${WDK_UNPARSED_ARGUMENTS})
|
||||
|
||||
set_target_properties(${_target} PROPERTIES COMPILE_OPTIONS "${WDK_COMPILE_FLAGS}")
|
||||
set_target_properties(${_target} PROPERTIES COMPILE_DEFINITIONS
|
||||
"${WDK_COMPILE_DEFINITIONS};$<$<CONFIG:Debug>:${WDK_COMPILE_DEFINITIONS_DEBUG};>_WIN32_WINNT=${WDK_WINVER}"
|
||||
)
|
||||
if(WDK_NTDDI_VERSION)
|
||||
target_compile_definitions(${_target} PRIVATE NTDDI_VERSION=${WDK_NTDDI_VERSION})
|
||||
endif()
|
||||
|
||||
target_include_directories(${_target} SYSTEM PRIVATE
|
||||
"${WDK_ROOT}/Include/${WDK_INC_VERSION}/shared"
|
||||
"${WDK_ROOT}/Include/${WDK_INC_VERSION}/km"
|
||||
"${WDK_ROOT}/Include/${WDK_INC_VERSION}/km/crt"
|
||||
)
|
||||
|
||||
if(DEFINED WDK_KMDF)
|
||||
target_include_directories(${_target} SYSTEM PRIVATE "${WDK_ROOT}/Include/wdf/kmdf/${WDK_KMDF}")
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -0,0 +1,82 @@
|
||||
# Proof of Concepts
|
||||
|
||||
This project aims to highlight certain aspects of the shadow stack mitigation implemented by Microsoft in Windows 11.
|
||||
The code is specifically focused on Windows 11 24H2.
|
||||
**It may not work on other version of Windows 11, due to the kernel offset management**.
|
||||
|
||||
The project is divided into two parts: a client and a driver.
|
||||
Communication between them is handled through the IOCTL mechanism.
|
||||
The client requests the driver to perform actions at the kernel level.
|
||||
|
||||
## Features
|
||||
|
||||
- Increment one of the kernel return addresses of the current thread to misalign it and trigger a BSOD.
|
||||
- Skip one of the kernel stack frames of the current thread to highlight the resilience of the shadow stack.
|
||||
- Enable/disable CET in the current `CR4` register, leading to a BSOD
|
||||
- Write to MSR registers, which typically leads to a BSOD
|
||||
- Write to the shadow stack, which results in a BSOD
|
||||
- Write the shadow stack PTE, triggering a BSOD
|
||||
- Perform divisions to highlight `try/except` and shadow stack behaviors during a division by zero
|
||||
|
||||
## Build
|
||||
|
||||
Use `build.bat`.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
As a prerequisite, the following kits have to be for the same Windows build:
|
||||
|
||||
- Microsoft Windows SDK (Software Development Kit)
|
||||
- Microsoft WDK (Windows Driver Kit)
|
||||
|
||||
Note:
|
||||
The POC have been tested with the version `10.0.26100.0` of the kits.
|
||||
|
||||
## Target Machine Configuration
|
||||
|
||||
To activate the kernel shadow stack mitigation, the following values have to be added or set in the `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\DeviceGuard\` key:
|
||||
|
||||
| Name | Type | Data
|
||||
| --- | --- | ---
|
||||
| EnableVirtualizationBasedSecurity | REG_DWORD | 1
|
||||
| Scenarios\HypervisorEnforcedCodeIntegrity\Enabled | REG_DWORD | 1
|
||||
| Scenarios\KernelShadowStacks\Enabled | REG_DWORD | 1
|
||||
|
||||
Also, in order to load a test-signed driver, the following command have to be run in an administrator console:
|
||||
|
||||
```console
|
||||
bcdedit /set testsigning on
|
||||
```
|
||||
|
||||
Then, a reboot is required.
|
||||
|
||||
## Usage
|
||||
|
||||
### Driver Loading
|
||||
|
||||
To load the driver, the following commands have to be entered in an administrator console:
|
||||
|
||||
```console
|
||||
sc create shadow type= kernel binPath= ABSOLUTE_PATH_TO_THE_DRIVER
|
||||
sc start shadow
|
||||
```
|
||||
|
||||
### Client
|
||||
|
||||
The client has the following options:
|
||||
|
||||
| Option | Description
|
||||
| --- | ---
|
||||
| inc | Increment the return address to the caller by 1
|
||||
| skip | Patch RSP in order to point on the previous stack frame
|
||||
| cdisable | Disable CET in CR4
|
||||
| cenable | Enable CET in CR4
|
||||
| wrmsr REGISTER VALUE | Write the REGISTER with the given VALUE
|
||||
| wrshsk | Write into the shadow stack
|
||||
| wrshskpte | Rewrite the shadow stack PTE
|
||||
| div DIVIDEND DIVISOR | Perfom integer division in kernel space
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
SergiusTheBest for its [FindWDK](https://github.com/SergiusTheBest/FindWDK).
|
||||
@@ -0,0 +1,4 @@
|
||||
rmdir bin /S /Q
|
||||
rmdir build /S /Q
|
||||
rmdir build32 /S /Q
|
||||
rmdir build64 /S /Q
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "client/utils.h"
|
||||
|
||||
#include "common/utils.h"
|
||||
|
||||
void usage()
|
||||
{
|
||||
printf("Options:\n");
|
||||
printf(" inc Increment the return address to the caller\n");
|
||||
printf(" by 1\n");
|
||||
printf(" skip Patch RSP in order to point on the previous\n");
|
||||
printf(" stack frame\n");
|
||||
printf(" cdisable Disable CET in CR4\n");
|
||||
printf(" cenable Enable CET in CR4\n");
|
||||
printf(" wrmsr REGISTER VALUE Write the REGISTER with the given VALUE\n");
|
||||
printf(" wrshsk Write into the shadow stack\n");
|
||||
printf(" wrshskpte Rewrite the shadow stack PTE\n");
|
||||
printf(" div DIVIDEND DIVISOR Perfom integer division in kernel space\n");
|
||||
}
|
||||
|
||||
int32_t main(int32_t argc, char* argv[])
|
||||
{
|
||||
if (2 > argc)
|
||||
{
|
||||
printf("Missing arguments\n\n");
|
||||
|
||||
usage();
|
||||
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
if (EXIT_SUCCESS != OpenExposedDevice())
|
||||
{
|
||||
printf("[!] " __FUNCTION__ " failed\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
if (!strcmp("inc", argv[1]))
|
||||
{
|
||||
if (IncRetAddr())
|
||||
{
|
||||
printf("[!] " __FUNCTION__ ": IncRetAddr failed\n");
|
||||
goto EXIT;
|
||||
}
|
||||
}
|
||||
else if (!strcmp("skip", argv[1]))
|
||||
{
|
||||
if (SkipNextFrame())
|
||||
{
|
||||
printf("[!] " __FUNCTION__ ": SkipNextFrame failed\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
printf("Stack returned\n");
|
||||
}
|
||||
else if (!strcmp("cdisable", argv[1]))
|
||||
{
|
||||
if (WriteCr4(0))
|
||||
{
|
||||
printf("[!] " __FUNCTION__ ": WriteCr4 failed\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
printf("Kernel shadow stack disabled in CR4\n");
|
||||
}
|
||||
else if (!strcmp("cenable", argv[1]))
|
||||
{
|
||||
if (WriteCr4(1))
|
||||
{
|
||||
printf("[!] " __FUNCTION__ ": WriteCr4 failed\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
printf("Kernel shadow stack enabled in CR4\n");
|
||||
}
|
||||
else if (!strcmp("wrmsr", argv[1]))
|
||||
{
|
||||
if (4 > argc)
|
||||
{
|
||||
printf("[!] wrmsr: Missing arguments");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
if (WriteMsr(strtoul(argv[2], NULL, 0), strtoull(argv[3], NULL, 0)))
|
||||
{
|
||||
printf("[!] " __FUNCTION__ ": WriteMsr failed\n");
|
||||
goto EXIT;
|
||||
}
|
||||
}
|
||||
else if (!strcmp("wrshsk", argv[1]))
|
||||
{
|
||||
if (WriteShadowStack())
|
||||
{
|
||||
printf("[!] " __FUNCTION__ ": WriteShadowStack failed\n");
|
||||
goto EXIT;
|
||||
}
|
||||
}
|
||||
else if (!strcmp("wrshskpte", argv[1]))
|
||||
{
|
||||
if (WriteShadowStackPte())
|
||||
{
|
||||
printf("[!] " __FUNCTION__ ": WriteShadowStackPte failed\n");
|
||||
goto EXIT;
|
||||
}
|
||||
}
|
||||
else if (!strcmp("div", argv[1]))
|
||||
{
|
||||
if (4 > argc)
|
||||
{
|
||||
printf("[!] div: Missing arguments");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
if (DivInteger(strtol(argv[2], NULL, 0), strtol(argv[3], NULL, 0)))
|
||||
{
|
||||
printf("[!] " __FUNCTION__ ": DivInteger failed\n");
|
||||
goto EXIT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
usage();
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
EXIT:
|
||||
CloseExposedDevice();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,412 @@
|
||||
#include "client/utils.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
|
||||
// ===========================================================================
|
||||
// Inner global declarations
|
||||
// ===========================================================================
|
||||
|
||||
static HANDLE gDevice = INVALID_HANDLE_VALUE;
|
||||
|
||||
// ===========================================================================
|
||||
// Function definitions
|
||||
// ===========================================================================
|
||||
|
||||
void CloseExposedDevice()
|
||||
{
|
||||
if (INVALID_HANDLE_VALUE != gDevice)
|
||||
{
|
||||
CloseHandle(gDevice);
|
||||
gDevice = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t DivInteger(int32_t Dividend, int32_t Divisor)
|
||||
{
|
||||
int32_t ExitCode = EXIT_FAILURE;
|
||||
IoctlDivInteger_t di = { 0 };
|
||||
|
||||
if (INVALID_HANDLE_VALUE == gDevice)
|
||||
{
|
||||
printf("[-] " __FUNCTION__ ": Device not opened\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
__try
|
||||
{
|
||||
di.Quotient = Dividend / Divisor;
|
||||
}
|
||||
__except (1)
|
||||
{
|
||||
printf("[-] Divided by zero\n");
|
||||
}
|
||||
|
||||
di.Dividend = Dividend;
|
||||
di.Divisor = Divisor;
|
||||
|
||||
if (!DeviceIoControl(
|
||||
gDevice,
|
||||
IOCTL_DIV_INTEGER,
|
||||
&di,
|
||||
sizeof(IoctlDivInteger_t),
|
||||
&di,
|
||||
sizeof(IoctlDivInteger_t),
|
||||
NULL,
|
||||
NULL))
|
||||
{
|
||||
printf("[-] " __FUNCTION__ ": DeviceIoControl failed (0x%08x)\n",
|
||||
GetLastError());
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
printf("Division: %i / %i => %i\n", di.Dividend, di.Divisor, di.Quotient);
|
||||
|
||||
ExitCode = EXIT_SUCCESS;
|
||||
|
||||
EXIT:
|
||||
return ExitCode;
|
||||
}
|
||||
|
||||
int32_t IncRetAddr()
|
||||
{
|
||||
int32_t ExitCode = EXIT_FAILURE;
|
||||
|
||||
if (INVALID_HANDLE_VALUE == gDevice)
|
||||
{
|
||||
printf("[-] " __FUNCTION__ ": Device not opened\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
if (!DeviceIoControl(
|
||||
gDevice,
|
||||
IOCTL_INC_RET_ADDR,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL))
|
||||
{
|
||||
printf("[-] " __FUNCTION__ ": DeviceIoControl failed (0x%08x)\n",
|
||||
GetLastError());
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
printf("Stack incremented\n");
|
||||
|
||||
ExitCode = EXIT_SUCCESS;
|
||||
|
||||
EXIT:
|
||||
return ExitCode;
|
||||
}
|
||||
|
||||
int32_t OpenExposedDevice()
|
||||
{
|
||||
int32_t ExitCode = EXIT_FAILURE;
|
||||
|
||||
if (EXIT_SUCCESS != OpenExposedDeviceEx(CLIENT_DEVICE_NAME))
|
||||
{
|
||||
printf("[!] " __FUNCTION__ " failed\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
ExitCode = EXIT_SUCCESS;
|
||||
|
||||
EXIT:
|
||||
return ExitCode;
|
||||
}
|
||||
|
||||
int32_t OpenExposedDeviceEx(char* DeviceName)
|
||||
{
|
||||
int32_t ExitCode = EXIT_FAILURE;
|
||||
|
||||
if (!DeviceName)
|
||||
{
|
||||
printf("[-] " __FUNCTION__ ": Invalid device name\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
if (INVALID_HANDLE_VALUE != gDevice)
|
||||
{
|
||||
printf("[!] " __FUNCTION__ ": Device already opened\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
gDevice = CreateFile(
|
||||
DeviceName,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
NULL);
|
||||
if (INVALID_HANDLE_VALUE == gDevice)
|
||||
{
|
||||
printf("[-] " __FUNCTION__ ": CreateFile failed (0x%08x)\n", GetLastError());
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
ExitCode = EXIT_SUCCESS;
|
||||
|
||||
EXIT:
|
||||
return ExitCode;
|
||||
}
|
||||
|
||||
void PrintCr4Flags(Cr4Flags_t Flags)
|
||||
{
|
||||
printf("CR4 (0x%llx):\n", *(uint64_t*)&Flags);
|
||||
printf(" VME: %s\n", Flags.VME ? "TRUE" : "FALSE");
|
||||
printf(" PVI: %s\n", Flags.PVI ? "TRUE" : "FALSE");
|
||||
printf(" TSD: %s\n", Flags.TSD ? "TRUE" : "FALSE");
|
||||
printf(" DE: %s\n", Flags.DE ? "TRUE" : "FALSE");
|
||||
printf(" PSE: %s\n", Flags.PSE ? "TRUE" : "FALSE");
|
||||
printf(" PAE: %s\n", Flags.PAE ? "TRUE" : "FALSE");
|
||||
printf(" MCE: %s\n", Flags.MCE ? "TRUE" : "FALSE");
|
||||
printf(" PGE: %s\n", Flags.PGE ? "TRUE" : "FALSE");
|
||||
printf(" PCE: %s\n", Flags.PCE ? "TRUE" : "FALSE");
|
||||
printf(" OSFXSR: %s\n", Flags.OSFXSR ? "TRUE" : "FALSE");
|
||||
printf(" OSXMMEXCPT: %s\n", Flags.OSXMMEXCPT ? "TRUE" : "FALSE");
|
||||
printf(" UMIP: %s\n", Flags.UMIP ? "TRUE" : "FALSE");
|
||||
printf(" LA57: %s\n", Flags.LA57 ? "TRUE" : "FALSE");
|
||||
printf(" VMXE: %s\n", Flags.VMXE ? "TRUE" : "FALSE");
|
||||
printf(" SMXE: %s\n", Flags.SMXE ? "TRUE" : "FALSE");
|
||||
printf(" Reserved0: %s\n", Flags.Reserved0 ? "TRUE" : "FALSE");
|
||||
printf(" FSGSBASE: %s\n", Flags.FSGSBASE ? "TRUE" : "FALSE");
|
||||
printf(" PCIDE: %s\n", Flags.PCIDE ? "TRUE" : "FALSE");
|
||||
printf(" OSXSAVE: %s\n", Flags.OSXSAVE ? "TRUE" : "FALSE");
|
||||
printf(" KL: %s\n", Flags.KL ? "TRUE" : "FALSE");
|
||||
printf(" SMEP: %s\n", Flags.SMEP ? "TRUE" : "FALSE");
|
||||
printf(" SMAP: %s\n", Flags.SMAP ? "TRUE" : "FALSE");
|
||||
printf(" PKE: %s\n", Flags.PKE ? "TRUE" : "FALSE");
|
||||
printf(" CET: %s\n", Flags.CET ? "TRUE" : "FALSE");
|
||||
printf(" PKS: %s\n", Flags.PKS ? "TRUE" : "FALSE");
|
||||
printf(" UINTR: %s\n", Flags.UINTR ? "TRUE" : "FALSE");
|
||||
printf(" Reserved1: 0x%llx\n", Flags.Reserved1);
|
||||
}
|
||||
|
||||
void PrintMiscFlags(MiscFlags_t Flags)
|
||||
{
|
||||
printf("Misc flags (0x%x):\n", *(uint32_t*)&Flags);
|
||||
printf(" AutoBoostActive: %s\n", Flags.AutoBoostActive ? "TRUE" : "FALSE");
|
||||
printf(" ReadyTransition: %s\n", Flags.ReadyTransition ? "TRUE" : "FALSE");
|
||||
printf(" WaitNext: %s\n", Flags.WaitNext ? "TRUE" : "FALSE");
|
||||
printf(" SystemAffinityActive: %s\n",
|
||||
Flags.SystemAffinityActive ? "TRUE" : "FALSE");
|
||||
printf(" Alertable: %s\n", Flags.Alertable ? "TRUE" : "FALSE");
|
||||
printf(" UserStackWalkActive: %s\n",
|
||||
Flags.UserStackWalkActive ? "TRUE" : "FALSE");
|
||||
printf(" ApcInterruptRequest: %s\n",
|
||||
Flags.ApcInterruptRequest ? "TRUE" : "FALSE");
|
||||
printf(" QuantumEndMigrate: %s\n",
|
||||
Flags.QuantumEndMigrate ? "TRUE" : "FALSE");
|
||||
printf(" SecureThread: %s\n", Flags.SecureThread ? "TRUE" : "FALSE");
|
||||
printf(" TimerActive: %s\n", Flags.TimerActive ? "TRUE" : "FALSE");
|
||||
printf(" SystemThread: %s\n", Flags.SystemThread ? "TRUE" : "FALSE");
|
||||
printf(" ProcessDetachActive: %s\n",
|
||||
Flags.ProcessDetachActive ? "TRUE" : "FALSE");
|
||||
printf(" CalloutActive: %s\n", Flags.CalloutActive ? "TRUE" : "FALSE");
|
||||
printf(" ScbReadyQueue: %s\n", Flags.ScbReadyQueue ? "TRUE" : "FALSE");
|
||||
printf(" ApcQueueable: %s\n", Flags.ApcQueueable ? "TRUE" : "FALSE");
|
||||
printf(" ReservedStackInUse: %s\n",
|
||||
Flags.ReservedStackInUse ? "TRUE" : "FALSE");
|
||||
printf(" Spare: %s\n", Flags.Spare ? "TRUE" : "FALSE");
|
||||
printf(" TimerSuspended: %s\n", Flags.TimerSuspended ? "TRUE" : "FALSE");
|
||||
printf(" SuspendedWaitMode: %s\n",
|
||||
Flags.SuspendedWaitMode ? "TRUE" : "FALSE");
|
||||
printf(" SuspendSchedulerApcWait: %s\n",
|
||||
Flags.SuspendSchedulerApcWait ? "TRUE" : "FALSE");
|
||||
printf(" CetUserShadowStack: %s\n",
|
||||
Flags.CetUserShadowStack ? "TRUE" : "FALSE");
|
||||
printf(" BypassProcessFreeze: %s\n",
|
||||
Flags.BypassProcessFreeze ? "TRUE" : "FALSE");
|
||||
printf(" CetKernelShadowStack: %s\n",
|
||||
Flags.CetKernelShadowStack ? "TRUE" : "FALSE");
|
||||
printf(" StateSaveAreaDecoupled: %s\n",
|
||||
Flags.StateSaveAreaDecoupled ? "TRUE" : "FALSE");
|
||||
printf(" Reserved: 0x%02x\n", Flags.Reserved);
|
||||
}
|
||||
|
||||
void PrintMsrCet(MsrCetFlags_t Flags)
|
||||
{
|
||||
printf("MSR CET flags (0x%llx):\n", *(uint64_t*)&Flags);
|
||||
printf(" SH_STK_EN: %s\n", Flags.SH_STK_EN ? "TRUE" : "FALSE");
|
||||
printf(" WR_SHSTK_EN: %s\n", Flags.WR_SHSTK_EN ? "TRUE" : "FALSE");
|
||||
printf(" ENDBR_EN: %s\n", Flags.ENDBR_EN ? "TRUE" : "FALSE");
|
||||
printf(" LEG_IW_EN: %s\n", Flags.LEG_IW_EN ? "TRUE" : "FALSE");
|
||||
printf(" NO_TRACK_EN: %s\n", Flags.NO_TRACK_EN ? "TRUE" : "FALSE");
|
||||
printf(" SUPPRESS_DIS: %s\n", Flags.SUPPRESS_DIS ? "TRUE" : "FALSE");
|
||||
printf(" Reserved: 0x%llx\n", Flags.Reserved);
|
||||
printf(" SUPPRESS: %s\n", Flags.SUPPRESS ? "TRUE" : "FALSE");
|
||||
printf(" TRACKER: %s\n", Flags.TRACKER ? "TRUE" : "FALSE");
|
||||
printf(" EB_LEG_BITMAP_BASE: 0x%llx\n", Flags.EB_LEG_BITMAP_BASE);
|
||||
}
|
||||
|
||||
int32_t WriteCr4(uint8_t Value)
|
||||
{
|
||||
int32_t ExitCode = EXIT_FAILURE;
|
||||
|
||||
if (INVALID_HANDLE_VALUE == gDevice)
|
||||
{
|
||||
printf("[-] " __FUNCTION__ ": Device not opened\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
if (!DeviceIoControl(
|
||||
gDevice,
|
||||
IOCTL_WRITE_CR4,
|
||||
&Value,
|
||||
sizeof(Value),
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL))
|
||||
{
|
||||
printf("[-] " __FUNCTION__ ": DeviceIoControl failed (0x%08x)\n",
|
||||
GetLastError());
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
ExitCode = EXIT_SUCCESS;
|
||||
|
||||
EXIT:
|
||||
return ExitCode;
|
||||
}
|
||||
|
||||
int32_t SkipNextFrame()
|
||||
{
|
||||
int32_t ExitCode = EXIT_FAILURE;
|
||||
|
||||
if (INVALID_HANDLE_VALUE == gDevice)
|
||||
{
|
||||
printf("[-] " __FUNCTION__ ": Device not opened\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
if (!DeviceIoControl(
|
||||
gDevice,
|
||||
IOCTL_SKIP_NEXT_FRAME,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL))
|
||||
{
|
||||
printf("[-] " __FUNCTION__ ": DeviceIoControl failed (0x%08x)\n",
|
||||
GetLastError());
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
ExitCode = EXIT_SUCCESS;
|
||||
|
||||
EXIT:
|
||||
return ExitCode;
|
||||
}
|
||||
|
||||
int32_t WriteMsr(uint32_t Register, uint64_t Value)
|
||||
{
|
||||
int32_t ExitCode = EXIT_FAILURE;
|
||||
Msr_t Msr = { 0 };
|
||||
|
||||
if (INVALID_HANDLE_VALUE == gDevice)
|
||||
{
|
||||
printf("[-] " __FUNCTION__ ": Device not opened\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
Msr.Register = Register;
|
||||
Msr.Value = Value;
|
||||
|
||||
if (!DeviceIoControl(
|
||||
gDevice,
|
||||
IOCTL_WRITE_MSR,
|
||||
&Msr,
|
||||
sizeof(Msr_t),
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL))
|
||||
{
|
||||
printf("[-] " __FUNCTION__ ": DeviceIoControl failed (0x%08x)\n",
|
||||
GetLastError());
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
printf("Register [0x%04x] <= 0x%016llx", Register, Value);
|
||||
|
||||
ExitCode = EXIT_SUCCESS;
|
||||
|
||||
EXIT:
|
||||
return ExitCode;
|
||||
}
|
||||
|
||||
int32_t WriteShadowStack()
|
||||
{
|
||||
int32_t ExitCode = EXIT_FAILURE;
|
||||
IoctlWriteShadowStack_t wss = { 0 };
|
||||
|
||||
if (INVALID_HANDLE_VALUE == gDevice)
|
||||
{
|
||||
printf("[-] " __FUNCTION__ ": Device not opened\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
wss.Offset = -0x200;
|
||||
wss.Address = 0x4141414141414141;
|
||||
|
||||
if (!DeviceIoControl(
|
||||
gDevice,
|
||||
IOCTL_WRITE_CURRENT_SHADOW_STACK,
|
||||
&wss,
|
||||
sizeof(IoctlWriteShadowStack_t),
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL))
|
||||
{
|
||||
printf("[-] " __FUNCTION__ ": DeviceIoControl failed (0x%08x)\n",
|
||||
GetLastError());
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
ExitCode = EXIT_SUCCESS;
|
||||
|
||||
EXIT:
|
||||
return ExitCode;
|
||||
}
|
||||
|
||||
int32_t WriteShadowStackPte()
|
||||
{
|
||||
int32_t ExitCode = EXIT_FAILURE;
|
||||
PageEntry64_t PageEntry = { 0 };
|
||||
|
||||
if (INVALID_HANDLE_VALUE == gDevice)
|
||||
{
|
||||
printf("[-] " __FUNCTION__ ": Device not opened\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
PageEntry.Write = 1;
|
||||
|
||||
if (!DeviceIoControl(
|
||||
gDevice,
|
||||
IOCTL_WRITE_CURRENT_SHADOW_STACK_PTE,
|
||||
&PageEntry,
|
||||
sizeof(PageEntry64_t),
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL))
|
||||
{
|
||||
printf("[-] " __FUNCTION__ ": DeviceIoControl failed (0x%08x)\n",
|
||||
GetLastError());
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
ExitCode = EXIT_SUCCESS;
|
||||
|
||||
EXIT:
|
||||
return ExitCode;
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
#ifndef __CLIENT_UTILS_H__
|
||||
#define __CLIENT_UTILS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "common/utils.h"
|
||||
|
||||
// ===========================================================================
|
||||
// Function declarations
|
||||
// ===========================================================================
|
||||
|
||||
/**
|
||||
* \brief Closes the current opened device
|
||||
*/
|
||||
void CloseExposedDevice();
|
||||
|
||||
/**
|
||||
* \brief Permforms an integer division in kernel space
|
||||
*
|
||||
* \param Dividend Value to divide
|
||||
* \param Divisor Value used to divide
|
||||
*
|
||||
* \return EXIT_SUCCESS on success, otherwise EXIT_FAILURE
|
||||
*/
|
||||
int32_t DivInteger(
|
||||
int32_t Dividend,
|
||||
int32_t Divisor);
|
||||
|
||||
/**
|
||||
* \brief Increments the kernel return address to the caller in order to
|
||||
* trigger a mismatch with the shadow stack
|
||||
*
|
||||
* \return EXIT_SUCCESS on success, otherwise EXIT_FAILURE
|
||||
*/
|
||||
int32_t IncRetAddr();
|
||||
|
||||
/**
|
||||
* \brief Opens the device related to "\??\ShadowStack"
|
||||
*
|
||||
* \return EXIT_SUCCESS on success, otherwise EXIT_FAILURE
|
||||
*/
|
||||
int32_t OpenExposedDevice();
|
||||
|
||||
/**
|
||||
* \brief Opens the device related to the given name
|
||||
*
|
||||
* \param DeviceName Pointer to the device name of the device to be opened
|
||||
*
|
||||
* \return EXIT_SUCCESS on success, otherwise EXIT_FAILURE
|
||||
*/
|
||||
int32_t OpenExposedDeviceEx(
|
||||
char* DeviceName);
|
||||
|
||||
/**
|
||||
* \brief Prints the fields of the given CR4 flags
|
||||
*
|
||||
* \param Flags CR4 flags to print
|
||||
*/
|
||||
void PrintCr4Flags(
|
||||
Cr4Flags_t Flags);
|
||||
|
||||
/**
|
||||
* \brief Prints the fields of the given misc flags
|
||||
*
|
||||
* \param Flags Misc flags to print
|
||||
*/
|
||||
void PrintMiscFlags(
|
||||
MiscFlags_t Flags);
|
||||
|
||||
/**
|
||||
* \brief Prints the fields of the given MSR CET flags
|
||||
*
|
||||
* \param Flags MSR CET flags to print
|
||||
*/
|
||||
void PrintMsrCet(
|
||||
MsrCetFlags_t Flags);
|
||||
|
||||
/**
|
||||
* \brief Sets the CET flag of the current CR4 register
|
||||
*
|
||||
* \param Value CET value to set
|
||||
*
|
||||
* \return EXIT_SUCCESS on success, otherwise EXIT_FAILURE
|
||||
*/
|
||||
int32_t WriteCr4(
|
||||
uint8_t Value);
|
||||
|
||||
/**
|
||||
* \brief Skips the next stack frames in order to trigger a valid mismatch
|
||||
* between the stack and the shadow stack
|
||||
*
|
||||
* \return EXIT_SUCCESS on success, otherwise EXIT_FAILURE
|
||||
*/
|
||||
int32_t SkipNextFrame();
|
||||
|
||||
/**
|
||||
* \brief Writes the given value to the given register address
|
||||
*
|
||||
* \param Register Register address where to write
|
||||
* \param Value Value to set
|
||||
*
|
||||
* \return EXIT_SUCCESS on success, otherwise EXIT_FAILURE
|
||||
*/
|
||||
int32_t WriteMsr(
|
||||
uint32_t Register,
|
||||
uint64_t Value);
|
||||
|
||||
/**
|
||||
* \brief Writes data into the current shadow stack
|
||||
*
|
||||
* \return EXIT_SUCCESS on success, otherwise EXIT_FAILURE
|
||||
*/
|
||||
int32_t WriteShadowStack();
|
||||
|
||||
/**
|
||||
* \brief Sets the write flag of the shadow stack PTE
|
||||
*
|
||||
* \return EXIT_SUCCESS on success, otherwise EXIT_FAILURE
|
||||
*/
|
||||
int32_t WriteShadowStackPte();
|
||||
|
||||
#endif // __CLIENT_UTILS_H__
|
||||
@@ -0,0 +1,164 @@
|
||||
#ifndef __COMMON_UTILS_H__
|
||||
#define __COMMON_UTILS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef _MAX_PATH_
|
||||
#define _MAX_PATH_ (0x200)
|
||||
#endif
|
||||
|
||||
#define countof(arr) (sizeof(arr) / sizeof(arr[0]))
|
||||
|
||||
#define BASE_NAME "ShadowStack"
|
||||
|
||||
#define CLIENT_DEVICE_NAME "\\??\\" BASE_NAME
|
||||
#define DRIVER_DEVICE_NAME L"\\Device\\" BASE_NAME
|
||||
#define DOS_DEVICE_NAME L"\\DosDevices\\" BASE_NAME
|
||||
|
||||
#define DEVICE_IOCTL_TYPE 4000
|
||||
|
||||
#define IOCTL_INC_RET_ADDR \
|
||||
CTL_CODE(DEVICE_IOCTL_TYPE, 0x900, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_SKIP_NEXT_FRAME \
|
||||
CTL_CODE(DEVICE_IOCTL_TYPE, 0x901, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WRITE_CURRENT_SHADOW_STACK \
|
||||
CTL_CODE(DEVICE_IOCTL_TYPE, 0x902, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WRITE_CR4 \
|
||||
CTL_CODE(DEVICE_IOCTL_TYPE, 0x903, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WRITE_MSR \
|
||||
CTL_CODE(DEVICE_IOCTL_TYPE, 0x904, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WRITE_CURRENT_SHADOW_STACK_PTE \
|
||||
CTL_CODE(DEVICE_IOCTL_TYPE, 0x905, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_DIV_INTEGER \
|
||||
CTL_CODE(DEVICE_IOCTL_TYPE, 0x906, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IA32_U_CET_REGISTER (0x6a0)
|
||||
#define IA32_S_CET_REGISTER (0x6a2)
|
||||
#define IA32_PL0_SSP_REGISTER (0x6a4)
|
||||
#define IA32_PL1_SSP_REGISTER (0x6a5)
|
||||
#define IA32_PL2_SSP_REGISTER (0x6a6)
|
||||
#define IA32_PL3_SSP_REGISTER (0x6a7)
|
||||
|
||||
// ===========================================================================
|
||||
// Structure definitions
|
||||
// ===========================================================================
|
||||
|
||||
typedef struct _Cr4Flags
|
||||
{
|
||||
uint64_t VME : 1;
|
||||
uint64_t PVI : 1;
|
||||
uint64_t TSD : 1;
|
||||
uint64_t DE : 1;
|
||||
uint64_t PSE : 1;
|
||||
uint64_t PAE : 1;
|
||||
uint64_t MCE : 1;
|
||||
uint64_t PGE : 1;
|
||||
uint64_t PCE : 1;
|
||||
uint64_t OSFXSR : 1;
|
||||
uint64_t OSXMMEXCPT : 1;
|
||||
uint64_t UMIP : 1;
|
||||
uint64_t LA57 : 1;
|
||||
uint64_t VMXE : 1;
|
||||
uint64_t SMXE : 1;
|
||||
uint64_t Reserved0 : 1;
|
||||
uint64_t FSGSBASE : 1;
|
||||
uint64_t PCIDE : 1;
|
||||
uint64_t OSXSAVE : 1;
|
||||
uint64_t KL : 1;
|
||||
uint64_t SMEP : 1;
|
||||
uint64_t SMAP : 1;
|
||||
uint64_t PKE : 1;
|
||||
uint64_t CET : 1;
|
||||
uint64_t PKS : 1;
|
||||
uint64_t UINTR : 1;
|
||||
uint64_t Reserved1 : 38;
|
||||
} Cr4Flags_t;
|
||||
|
||||
typedef struct _MiscFlags
|
||||
{
|
||||
uint32_t AutoBoostActive : 1;
|
||||
uint32_t ReadyTransition : 1;
|
||||
uint32_t WaitNext : 1;
|
||||
uint32_t SystemAffinityActive : 1;
|
||||
uint32_t Alertable : 1;
|
||||
uint32_t UserStackWalkActive : 1;
|
||||
uint32_t ApcInterruptRequest : 1;
|
||||
uint32_t QuantumEndMigrate : 1;
|
||||
uint32_t SecureThread : 1;
|
||||
uint32_t TimerActive : 1;
|
||||
uint32_t SystemThread : 1;
|
||||
uint32_t ProcessDetachActive : 1;
|
||||
uint32_t CalloutActive : 1;
|
||||
uint32_t ScbReadyQueue : 1;
|
||||
uint32_t ApcQueueable : 1;
|
||||
uint32_t ReservedStackInUse : 1;
|
||||
uint32_t Spare : 1;
|
||||
uint32_t TimerSuspended : 1;
|
||||
uint32_t SuspendedWaitMode : 1;
|
||||
uint32_t SuspendSchedulerApcWait : 1;
|
||||
uint32_t CetUserShadowStack : 1;
|
||||
uint32_t BypassProcessFreeze : 1;
|
||||
uint32_t CetKernelShadowStack : 1;
|
||||
uint32_t StateSaveAreaDecoupled : 1;
|
||||
uint32_t Reserved : 8;
|
||||
} MiscFlags_t;
|
||||
|
||||
typedef struct _Msr
|
||||
{
|
||||
uint32_t Register;
|
||||
uint64_t Value;
|
||||
} Msr_t;
|
||||
|
||||
typedef struct _MsrCetFlags
|
||||
{
|
||||
uint64_t SH_STK_EN : 1;
|
||||
uint64_t WR_SHSTK_EN : 1;
|
||||
uint64_t ENDBR_EN : 1;
|
||||
uint64_t LEG_IW_EN : 1;
|
||||
uint64_t NO_TRACK_EN : 1;
|
||||
uint64_t SUPPRESS_DIS : 1;
|
||||
uint64_t Reserved : 4;
|
||||
uint64_t SUPPRESS : 1;
|
||||
uint64_t TRACKER : 1;
|
||||
uint64_t EB_LEG_BITMAP_BASE : 52;
|
||||
} MsrCetFlags_t;
|
||||
|
||||
typedef struct _PageEntry64
|
||||
{
|
||||
uint64_t Valid : 1;
|
||||
uint64_t Write : 1;
|
||||
uint64_t Owner : 1;
|
||||
uint64_t WriteThrough : 1;
|
||||
uint64_t CacheDisabled : 1;
|
||||
uint64_t Accessed : 1;
|
||||
uint64_t Dirty : 1;
|
||||
uint64_t LargePage : 1;
|
||||
uint64_t Global : 1;
|
||||
uint64_t CopyOnWrite : 1;
|
||||
uint64_t PrototypeSoft : 1;
|
||||
uint64_t WriteSoft : 1;
|
||||
uint64_t Pfn : 36;
|
||||
uint64_t Reserved : 15;
|
||||
uint64_t NoExecute : 1;
|
||||
} PageEntry64_t;
|
||||
|
||||
typedef struct _IoctlDivInteger
|
||||
{
|
||||
int32_t Dividend;
|
||||
int32_t Divisor;
|
||||
int32_t Quotient;
|
||||
} IoctlDivInteger_t;
|
||||
|
||||
typedef struct _IoctlOpenFileDesc
|
||||
{
|
||||
uint32_t Length;
|
||||
char Name[_MAX_PATH_];
|
||||
} IoctlOpenFileDesc_t;
|
||||
|
||||
typedef struct _IoctlWriteShadowStack
|
||||
{
|
||||
int32_t Offset;
|
||||
uint64_t Address;
|
||||
} IoctlWriteShadowStack_t;
|
||||
|
||||
#endif // __COMMON_UTILS_H__
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
#include <wdm.h>
|
||||
|
||||
#include "common/utils.h"
|
||||
|
||||
#include "common/utils.h"
|
||||
#include "driver/ioctl.h"
|
||||
#include "driver/utils.h"
|
||||
|
||||
void DriverUnload(PDRIVER_OBJECT DriverObject)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(DriverObject);
|
||||
|
||||
DoRun = 0;
|
||||
|
||||
DeleteExposedDevice(DriverObject);
|
||||
|
||||
DbgPrint("Driver unloaded!\n");
|
||||
}
|
||||
|
||||
NTSTATUS DispatchCreateClose(DEVICE_OBJECT* DeviceObject, IRP* Irp)
|
||||
{
|
||||
NTSTATUS Status = STATUS_SUCCESS;
|
||||
|
||||
UNREFERENCED_PARAMETER(DeviceObject);
|
||||
|
||||
switch (Irp->Tail.Overlay.CurrentStackLocation->MajorFunction)
|
||||
{
|
||||
case IRP_MJ_CREATE:
|
||||
DbgPrint("Device opened\n");
|
||||
break;
|
||||
|
||||
case IRP_MJ_CLOSE:
|
||||
DbgPrint("Device closed\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
Status = STATUS_UNSUCCESSFUL;
|
||||
break;
|
||||
}
|
||||
|
||||
Irp->IoStatus.Status = Status;
|
||||
Irp->IoStatus.Information = 0;
|
||||
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
NTSTATUS DispatchDeviceControl(DEVICE_OBJECT* DeviceObject, IRP* Irp)
|
||||
{
|
||||
IO_STACK_LOCATION* csl = NULL;
|
||||
NTSTATUS Status = STATUS_SUCCESS;
|
||||
uint32_t WrittenBytes = 0;
|
||||
void* Buffer = NULL;
|
||||
|
||||
DbgPrint("Entering " __FUNCTION__ "\n");
|
||||
|
||||
UNREFERENCED_PARAMETER(DeviceObject);
|
||||
|
||||
csl = Irp->Tail.Overlay.CurrentStackLocation;
|
||||
Buffer = Irp->AssociatedIrp.SystemBuffer;
|
||||
|
||||
DbgPrint("IOCTL: 0x%08x\n", csl->Parameters.DeviceIoControl.IoControlCode);
|
||||
|
||||
switch (csl->Parameters.DeviceIoControl.IoControlCode)
|
||||
{
|
||||
case IOCTL_INC_RET_ADDR:
|
||||
Status = IoctlIncRetAddr();
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DbgPrint("IoctlIncRetAddr failed\n");
|
||||
goto EXIT;
|
||||
}
|
||||
break;
|
||||
|
||||
case IOCTL_SKIP_NEXT_FRAME:
|
||||
Status = IoctlSkipNextFrame();
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DbgPrint("IoctlSkipNextFrame failed\n");
|
||||
goto EXIT;
|
||||
}
|
||||
break;
|
||||
|
||||
case IOCTL_WRITE_CURRENT_SHADOW_STACK:
|
||||
Status = IoctlWriteShadowStack(
|
||||
Buffer,
|
||||
csl->Parameters.DeviceIoControl.InputBufferLength);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DbgPrint("IoctlWriteShadowStack failed\n");
|
||||
goto EXIT;
|
||||
}
|
||||
break;
|
||||
|
||||
case IOCTL_WRITE_CR4:
|
||||
Status = IoctlWriteCr4(
|
||||
Buffer,
|
||||
csl->Parameters.DeviceIoControl.InputBufferLength);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DbgPrint("IoctlWriteCr4 failed\n");
|
||||
goto EXIT;
|
||||
}
|
||||
break;
|
||||
|
||||
case IOCTL_WRITE_MSR:
|
||||
Status = IoctlWriteMsr(
|
||||
Buffer,
|
||||
csl->Parameters.DeviceIoControl.InputBufferLength);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DbgPrint("IoctlWriteMsr failed\n");
|
||||
goto EXIT;
|
||||
}
|
||||
break;
|
||||
|
||||
case IOCTL_WRITE_CURRENT_SHADOW_STACK_PTE:
|
||||
Status = IoctlWriteShadowStackPte(
|
||||
Buffer,
|
||||
csl->Parameters.DeviceIoControl.InputBufferLength);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DbgPrint("IoctlWriteShadowStackPte failed\n");
|
||||
goto EXIT;
|
||||
}
|
||||
break;
|
||||
|
||||
case IOCTL_DIV_INTEGER:
|
||||
if (sizeof(IoctlDivInteger_t) >
|
||||
csl->Parameters.DeviceIoControl.OutputBufferLength)
|
||||
{
|
||||
DbgPrint("IoctlDivInteger output buffer to small\n");
|
||||
Status = STATUS_BUFFER_TOO_SMALL;
|
||||
goto EXIT;
|
||||
}
|
||||
Status = IoctlDivInteger(
|
||||
Buffer,
|
||||
csl->Parameters.DeviceIoControl.InputBufferLength);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DbgPrint("IoctlDivInteger failed\n");
|
||||
goto EXIT;
|
||||
}
|
||||
WrittenBytes = csl->Parameters.DeviceIoControl.OutputBufferLength;
|
||||
break;
|
||||
|
||||
default:
|
||||
Status = STATUS_UNSUCCESSFUL;
|
||||
break;
|
||||
}
|
||||
|
||||
EXIT:
|
||||
Irp->IoStatus.Information = WrittenBytes;
|
||||
Irp->IoStatus.Status = Status;
|
||||
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
|
||||
DbgPrint("Leaving " __FUNCTION__ "\n");
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
|
||||
{
|
||||
NTSTATUS Status = STATUS_SUCCESS;
|
||||
|
||||
UNREFERENCED_PARAMETER(RegistryPath);
|
||||
|
||||
Status = DbgSetDebugFilterState(0x100, 3, TRUE);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DbgPrint("Failed to set debug filter state\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
DbgPrint("Driver loading...\n");
|
||||
|
||||
DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreateClose;
|
||||
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchCreateClose;
|
||||
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchDeviceControl;
|
||||
DriverObject->DriverUnload = DriverUnload;
|
||||
|
||||
Status = CreateExposedDevice(DriverObject);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DbgPrint("CreateExposedDevice failed\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
EXIT:
|
||||
return Status;
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
#include <ntifs.h>
|
||||
|
||||
#include "driver/ioctl.h"
|
||||
|
||||
#include <intrin.h>
|
||||
|
||||
#include "common/utils.h"
|
||||
#include "driver/utils.h"
|
||||
|
||||
// ===========================================================================
|
||||
// Offsets 24h2
|
||||
// ===========================================================================
|
||||
|
||||
#define KI_KERNEL_CET_AUDIT_MODE_ENABLED_OFFSET_24H2 0x01201843
|
||||
#define KI_KERNEL_CET_ENABLED_OFFSET_24H2 0x01201841
|
||||
#define KI_KERNEL_CET_LOGGING_OFFSET_24H2 0x00fc6050
|
||||
|
||||
// ===========================================================================
|
||||
// Offsets 24h2
|
||||
// ===========================================================================
|
||||
|
||||
#define KI_KERNEL_CET_AUDIT_MODE_ENABLED_OFFSET KI_KERNEL_CET_AUDIT_MODE_ENABLED_OFFSET_24H2
|
||||
#define KI_KERNEL_CET_ENABLED_OFFSET KI_KERNEL_CET_ENABLED_OFFSET_24H2
|
||||
#define KI_KERNEL_CET_LOGGING_OFFSET KI_KERNEL_CET_LOGGING_OFFSET_24H2
|
||||
|
||||
int32_t DoRun = 1;
|
||||
|
||||
// ===========================================================================
|
||||
// Function definitions
|
||||
// ===========================================================================
|
||||
|
||||
int32_t _DivZeroFilter()
|
||||
{
|
||||
DbgPrint(__FUNCTION__ "\n");
|
||||
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
NTSTATUS IoctlDivInteger(void* Buffer, uint32_t Length)
|
||||
{
|
||||
IoctlDivInteger_t* di = NULL;
|
||||
NTSTATUS Status = STATUS_INVALID_PARAMETER;
|
||||
|
||||
DbgPrintEnter();
|
||||
|
||||
if (!Buffer)
|
||||
{
|
||||
DbgPrint("Invalid buffer\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
if (sizeof(IoctlDivInteger_t) > Length)
|
||||
{
|
||||
DbgPrint("Invalid length\n");
|
||||
Status = STATUS_BUFFER_TOO_SMALL;
|
||||
goto EXIT;
|
||||
}
|
||||
di = (IoctlDivInteger_t*)Buffer;
|
||||
|
||||
try
|
||||
{
|
||||
Status = DivInteger(di->Dividend, di->Divisor);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DbgPrint("[-] " __FUNCTION__ ": DivInteger\n");
|
||||
goto EXIT;
|
||||
}
|
||||
}
|
||||
except(_DivZeroFilter())
|
||||
{
|
||||
DbgPrint("A division by zero occurred\n");
|
||||
Status = STATUS_INTEGER_DIVIDE_BY_ZERO;
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
Status = STATUS_SUCCESS;
|
||||
|
||||
EXIT:
|
||||
DbgPrintLeave();
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
NTSTATUS IoctlIncRetAddr()
|
||||
{
|
||||
DbgPrintEnter();
|
||||
|
||||
IncRetAddr();
|
||||
|
||||
DbgPrintLeave();
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS IoctlWriteCr4(void* Buffer, uint32_t Length)
|
||||
{
|
||||
Cr4Flags_t* Flags = NULL;
|
||||
NTSTATUS Status = STATUS_INVALID_PARAMETER;
|
||||
uint8_t* Value = NULL;
|
||||
uint64_t cr4 = 0;
|
||||
|
||||
DbgPrintEnter();
|
||||
|
||||
if (!Buffer)
|
||||
{
|
||||
DbgPrint("Invalid buffer\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
if (sizeof(uint8_t) > Length)
|
||||
{
|
||||
DbgPrint("Invalid length\n");
|
||||
Status = STATUS_BUFFER_TOO_SMALL;
|
||||
goto EXIT;
|
||||
}
|
||||
Value = (uint8_t*)Buffer;
|
||||
|
||||
Flags = (Cr4Flags_t*)&cr4;
|
||||
cr4 = __readcr4();
|
||||
|
||||
DbgPrint("Reading CR4 = 0x%016llx\n", cr4);
|
||||
|
||||
Flags->CET = *Value & 1;
|
||||
|
||||
DbgPrint("Writing CR4 = 0x%016llx\n", cr4);
|
||||
|
||||
__writecr4(cr4);
|
||||
|
||||
Status = STATUS_SUCCESS;
|
||||
|
||||
EXIT:
|
||||
DbgPrintLeave();
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
NTSTATUS IoctlSkipNextFrame()
|
||||
{
|
||||
DbgPrintEnter();
|
||||
|
||||
SkipNextFrame();
|
||||
|
||||
DbgPrintLeave();
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS IoctlWriteMsr(void* Buffer, uint32_t Length)
|
||||
{
|
||||
Msr_t* Msr = NULL;
|
||||
NTSTATUS Status = STATUS_INVALID_PARAMETER;
|
||||
|
||||
DbgPrintEnter();
|
||||
|
||||
if (!Buffer)
|
||||
{
|
||||
DbgPrint("Invalid buffer\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
if (sizeof(Msr_t) > Length)
|
||||
{
|
||||
DbgPrint("Invalid length\n");
|
||||
Status = STATUS_BUFFER_TOO_SMALL;
|
||||
goto EXIT;
|
||||
}
|
||||
Msr = (Msr_t*)Buffer;
|
||||
|
||||
DbgPrint("Reading MSR[0x%04x] = 0x%016llx\n",
|
||||
Msr->Register,
|
||||
__readmsr(Msr->Register));
|
||||
DbgPrint("Writing MSR[0x%04x] = 0x%016llx\n",
|
||||
Msr->Register,
|
||||
Msr->Value);
|
||||
|
||||
__writemsr(Msr->Register, Msr->Value);
|
||||
|
||||
EXIT:
|
||||
DbgPrintLeave();
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
NTSTATUS IoctlWriteShadowStack(void* Buffer, uint32_t Length)
|
||||
{
|
||||
IoctlWriteShadowStack_t* wss = NULL;
|
||||
NTSTATUS Status = STATUS_INVALID_PARAMETER;
|
||||
uint8_t* u8ShadowStack = NULL;
|
||||
uint64_t* u64ShadowStack = NULL;
|
||||
|
||||
DbgPrintEnter();
|
||||
|
||||
if (!Buffer)
|
||||
{
|
||||
DbgPrint("Invalid buffer\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
if (sizeof(IoctlWriteShadowStack_t) > Length)
|
||||
{
|
||||
DbgPrint("Invalid length\n");
|
||||
Status = STATUS_BUFFER_TOO_SMALL;
|
||||
goto EXIT;
|
||||
}
|
||||
wss = (IoctlWriteShadowStack_t*)Buffer;
|
||||
|
||||
u8ShadowStack = GetThreadKernelShadowStack(KeGetCurrentThread());
|
||||
u64ShadowStack = (uint64_t*)(u8ShadowStack + wss->Offset);
|
||||
|
||||
DbgPrint("Reading -> %p = %p\n", u64ShadowStack, *u64ShadowStack);
|
||||
DbgPrint("Writing -> %p = %p\n", u64ShadowStack, wss->Address);
|
||||
|
||||
*u64ShadowStack = wss->Address;
|
||||
|
||||
EXIT:
|
||||
DbgPrintLeave();
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
NTSTATUS IoctlWriteShadowStackPte(void* Buffer, uint32_t Length)
|
||||
{
|
||||
NTSTATUS Status = STATUS_INVALID_PARAMETER;
|
||||
PageEntry64_t* PageEntry = NULL;
|
||||
PageEntry64_t* ShadowPageEntry = NULL;
|
||||
void* ShadowStack = NULL;
|
||||
|
||||
DbgPrintEnter();
|
||||
|
||||
if (!Buffer)
|
||||
{
|
||||
DbgPrint("Invalid buffer\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
if (sizeof(PageEntry64_t) > Length)
|
||||
{
|
||||
DbgPrint("Invalid length\n");
|
||||
Status = STATUS_BUFFER_TOO_SMALL;
|
||||
goto EXIT;
|
||||
}
|
||||
PageEntry = (PageEntry64_t*)Buffer;
|
||||
|
||||
ShadowStack = GetThreadKernelShadowStack(KeGetCurrentThread());
|
||||
ShadowPageEntry = (PageEntry64_t*)GetShadowPteAddress(ShadowStack);
|
||||
|
||||
DbgPrint("Shadow Page Entry: %p -> %p\n", ShadowPageEntry, *ShadowPageEntry);
|
||||
ShadowPageEntry->Write = PageEntry->Write;
|
||||
|
||||
for (uint64_t i = 0; i < 0xfffffffffffffff0; ++i)
|
||||
{
|
||||
}
|
||||
|
||||
DbgPrint("New Shadow Page Entry: %p -> %p\n", ShadowPageEntry, *ShadowPageEntry);
|
||||
|
||||
Status = STATUS_SUCCESS;
|
||||
|
||||
EXIT:
|
||||
DbgPrintLeave();
|
||||
|
||||
return Status;
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
#ifndef __DRIVER_IOCTL_H__
|
||||
#define __DRIVER_IOCTL_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <wdm.h>
|
||||
|
||||
#include "common/utils.h"
|
||||
|
||||
extern int32_t DoRun;
|
||||
|
||||
// ===========================================================================
|
||||
// Function declarations
|
||||
// ===========================================================================
|
||||
|
||||
/**
|
||||
* \brief Permforms an integer division
|
||||
*
|
||||
* \param Buffer Pointer to the buffer which contains the values for the
|
||||
* division the register address and the
|
||||
* \param Length Length of the input buffer
|
||||
*
|
||||
* \return STATUS_SUCCESS on success, otherwise error code
|
||||
*/
|
||||
NTSTATUS IoctlDivInteger(
|
||||
void* Buffer,
|
||||
uint32_t Length);
|
||||
|
||||
/**
|
||||
* \brief Increments the return address to the caller by 1
|
||||
*
|
||||
* \return STATUS_SUCCESS
|
||||
*/
|
||||
NTSTATUS IoctlIncRetAddr();
|
||||
|
||||
/**
|
||||
* \brief Sets the CET field in the current CR4 register
|
||||
* process
|
||||
*
|
||||
* \param Buffer Pointer to the buffer which contains the CET value to set
|
||||
* \param Length Length of the input buffer
|
||||
*
|
||||
* \return STATUS_SUCCESS on success, otherwise error code
|
||||
*/
|
||||
NTSTATUS IoctlWriteCr4(
|
||||
void* Buffer,
|
||||
uint32_t Length);
|
||||
|
||||
/**
|
||||
* \brief Patches its own return address in order to return on the next return
|
||||
* address in the stack
|
||||
*
|
||||
* \return STATUS_SUCCESS always
|
||||
*/
|
||||
NTSTATUS IoctlSkipNextFrame();
|
||||
|
||||
/**
|
||||
* \brief Writes the given MSR register with the given value
|
||||
*
|
||||
* \param Buffer Pointer to the buffer which contains the register address and
|
||||
* the value to be written
|
||||
* \param Length Length of the input buffer
|
||||
*
|
||||
* \return STATUS_SUCCESS on success, otherwise error code
|
||||
*/
|
||||
NTSTATUS IoctlWriteMsr(
|
||||
void* Buffer,
|
||||
uint32_t Length);
|
||||
|
||||
/**
|
||||
* \brief Writes the given value into the shadow stack at the given offset
|
||||
*
|
||||
* \param Buffer Pointer to the buffer which contains the offset and value to
|
||||
* write
|
||||
* \param Length Length of the input buffer
|
||||
*
|
||||
* \return STATUS_SUCCESS on success, otherwise error code
|
||||
*/
|
||||
NTSTATUS IoctlWriteShadowStack(
|
||||
void* Buffer,
|
||||
uint32_t Length);
|
||||
|
||||
/**
|
||||
* \brief Rewrites the shadow stack PTE with the given PTE write flag
|
||||
*
|
||||
* \param Buffer Pointer to the buffer which contains the PTE to use
|
||||
* \param Length Length of the input buffer
|
||||
*
|
||||
* \return STATUS_SUCCESS on success, otherwise error code
|
||||
*/
|
||||
NTSTATUS IoctlWriteShadowStackPte(
|
||||
void* Buffer,
|
||||
uint32_t Length);
|
||||
|
||||
#endif // __DRIVER_IOCTL_H__
|
||||
@@ -0,0 +1,8 @@
|
||||
.code
|
||||
|
||||
setRsp PROC
|
||||
mov rsp, rcx
|
||||
ret
|
||||
setRsp ENDP
|
||||
|
||||
END
|
||||
@@ -0,0 +1,544 @@
|
||||
#include "driver/utils.h"
|
||||
|
||||
#include <ntstrsafe.h>
|
||||
|
||||
#define KERNEL_ADDR_MASK (0xfffffffffff00000)
|
||||
#define MASK_KERNEL_ADDR(addr) ((addr)&KERNEL_ADDR_MASK)
|
||||
|
||||
// ===========================================================================
|
||||
// Offsets 24h2 - KTHREAD
|
||||
// ===========================================================================
|
||||
|
||||
static uint32_t KernelShadowStack_offset = 0x408;
|
||||
static uint32_t KernelShadowStackBase_offset = 0x418;
|
||||
static uint32_t KernelShadowStackInitial_offset = 0x410;
|
||||
static uint32_t KernelShadowStackLimit_offset = 0x420;
|
||||
static uint32_t MiscFlags_offset = 0x074;
|
||||
static uint32_t Process_offset = 0x220;
|
||||
static uint32_t ThreadListEntry_offset = 0x2f8;
|
||||
|
||||
// ===========================================================================
|
||||
// Offsets 24h2 - EPROCESS
|
||||
// ===========================================================================
|
||||
|
||||
static uint32_t ImageFileName_offset = 0x338;
|
||||
static uint32_t ThreadListHead_offset = 0x30;
|
||||
// static uint32_t ThreadListHead_offset = 0x370;
|
||||
|
||||
// ===========================================================================
|
||||
// Extern declarations
|
||||
// ===========================================================================
|
||||
|
||||
extern void setRsp(void*);
|
||||
|
||||
// ===========================================================================
|
||||
// Function definitions
|
||||
// ===========================================================================
|
||||
|
||||
NTSTATUS CreateExposedDevice(DRIVER_OBJECT* DriverObject)
|
||||
{
|
||||
NTSTATUS Status = STATUS_INVALID_PARAMETER;
|
||||
|
||||
DbgPrintEnter();
|
||||
|
||||
if (!DriverObject)
|
||||
{
|
||||
DbgPrint("Invalid drive object\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
Status = CreateExposedDeviceEx(
|
||||
DriverObject,
|
||||
DRIVER_DEVICE_NAME,
|
||||
DOS_DEVICE_NAME);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DbgPrint("CreateExposedDeviceEx failed\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
Status = STATUS_SUCCESS;
|
||||
|
||||
EXIT:
|
||||
DbgPrintLeave();
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
NTSTATUS CreateExposedDeviceEx(
|
||||
DRIVER_OBJECT* DriverObject,
|
||||
wchar_t* DeviceName,
|
||||
wchar_t* SymlinkName)
|
||||
{
|
||||
DEVICE_OBJECT* DeviceObject = NULL;
|
||||
NTSTATUS Status = STATUS_INVALID_PARAMETER;
|
||||
UNICODE_STRING uDeviceName = { 0 };
|
||||
UNICODE_STRING uSymlinkName = { 0 };
|
||||
|
||||
DbgPrintEnter();
|
||||
|
||||
if (!DriverObject)
|
||||
{
|
||||
DbgPrint("Invalid drive object\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
if (!DeviceName)
|
||||
{
|
||||
DbgPrint("Invalid device name\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
if (!SymlinkName)
|
||||
{
|
||||
DbgPrint("Invalid symbolic link name\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
RtlInitUnicodeString(&uDeviceName, DeviceName);
|
||||
RtlInitUnicodeString(&uSymlinkName, SymlinkName);
|
||||
|
||||
Status = IoCreateDevice(
|
||||
DriverObject,
|
||||
0,
|
||||
&uDeviceName,
|
||||
FILE_DEVICE_UNKNOWN,
|
||||
0,
|
||||
FALSE,
|
||||
&DeviceObject);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DbgPrint("Failed to create device object\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
Status = IoCreateSymbolicLink(&uSymlinkName, &uDeviceName);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DbgPrint("Failed to create symbolic link\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
Status = STATUS_SUCCESS;
|
||||
|
||||
EXIT:
|
||||
if (!NT_SUCCESS(Status) && DeviceObject)
|
||||
{
|
||||
IoDeleteDevice(DeviceObject);
|
||||
}
|
||||
|
||||
DbgPrintLeave();
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
void DeleteExposedDevice(DRIVER_OBJECT* DriverObject)
|
||||
{
|
||||
DbgPrintEnter();
|
||||
|
||||
if (!DriverObject)
|
||||
{
|
||||
DbgPrint("Invalid driver object\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
DeleteExposedDeviceEx(DriverObject, DOS_DEVICE_NAME);
|
||||
|
||||
EXIT:
|
||||
DbgPrintLeave();
|
||||
}
|
||||
|
||||
void DeleteExposedDeviceEx(DRIVER_OBJECT* DriverObject, wchar_t* SymlinkName)
|
||||
{
|
||||
UNICODE_STRING uSymlinkName = { 0 };
|
||||
|
||||
DbgPrintEnter();
|
||||
|
||||
if (!DriverObject)
|
||||
{
|
||||
DbgPrint("Invalid driver object\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
if (!SymlinkName)
|
||||
{
|
||||
DbgPrint("Invalid symbolic link name\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
RtlInitUnicodeString(&uSymlinkName, SymlinkName);
|
||||
|
||||
IoDeleteSymbolicLink(&uSymlinkName);
|
||||
|
||||
if (DriverObject->DeviceObject)
|
||||
{
|
||||
IoDeleteDevice(DriverObject->DeviceObject);
|
||||
}
|
||||
|
||||
EXIT:
|
||||
DbgPrintLeave();
|
||||
}
|
||||
|
||||
int32_t DivInteger(int32_t Dividend, int32_t Divisor)
|
||||
{
|
||||
int32_t Quotient = 0;
|
||||
|
||||
DbgPrintEnter();
|
||||
|
||||
Quotient = Dividend / Divisor;
|
||||
|
||||
DbgPrintLeave();
|
||||
|
||||
return Quotient;
|
||||
}
|
||||
|
||||
void IncRetAddr()
|
||||
{
|
||||
uintptr_t* placeholder = NULL;
|
||||
|
||||
DbgPrintEnter();
|
||||
|
||||
placeholder = (uintptr_t*)&placeholder;
|
||||
DbgPrint("Legitimate return address: %p\n", placeholder[3]);
|
||||
placeholder[3]++;
|
||||
DbgPrint("Incremented return address: %p\n", placeholder[3]);
|
||||
|
||||
DbgPrintLeave();
|
||||
}
|
||||
|
||||
void* GetCurrentThreadKernelShadowStack()
|
||||
{
|
||||
return GetThreadKernelShadowStack(KeGetCurrentThread());
|
||||
}
|
||||
|
||||
void* GetCurrentThreadKernelShadowStackBase()
|
||||
{
|
||||
return GetThreadKernelShadowStackBase(KeGetCurrentThread());
|
||||
}
|
||||
|
||||
void* GetCurrentThreadKernelShadowStackInitial()
|
||||
{
|
||||
return GetThreadKernelShadowStackInitial(KeGetCurrentThread());
|
||||
}
|
||||
|
||||
void* GetCurrentThreadKernelShadowStackLimit()
|
||||
{
|
||||
return GetThreadKernelShadowStackLimit(KeGetCurrentThread());
|
||||
}
|
||||
|
||||
MiscFlags_t GetCurrentThreadMiscFlags()
|
||||
{
|
||||
return GetThreadMiscFlags(KeGetCurrentThread());
|
||||
}
|
||||
|
||||
void* GetKernelBase()
|
||||
{
|
||||
return GetModuleBase((void*)ExAllocatePool2);
|
||||
}
|
||||
|
||||
void* GetMmShadowPfnDatabase()
|
||||
{
|
||||
UNICODE_STRING ustr = { 0 };
|
||||
uintptr_t MmGetVirtualForPhysical = 0;
|
||||
uintptr_t MmShadowPfnDatabase = 0;
|
||||
|
||||
RtlInitUnicodeString(&ustr, L"MmGetVirtualForPhysical");
|
||||
|
||||
MmGetVirtualForPhysical = (uintptr_t)MmGetSystemRoutineAddress(&ustr);
|
||||
|
||||
MmShadowPfnDatabase = *(uintptr_t*)(MmGetVirtualForPhysical + 0x22);
|
||||
|
||||
MmShadowPfnDatabase &= ~(uintptr_t)0xff;
|
||||
|
||||
return (void*)MmShadowPfnDatabase;
|
||||
}
|
||||
|
||||
void* GetModuleBase(void* Address)
|
||||
{
|
||||
char* addr = Address;
|
||||
|
||||
for (addr = (char*)((uintptr_t)addr & 0xffffffffffff0000);; addr -= 0x10000)
|
||||
{
|
||||
if (addr[0] == 'M' && addr[1] == 'Z')
|
||||
{
|
||||
DbgPrint("Module found: %p\n", addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
void* GetShadowPteAddress(void* Address)
|
||||
{
|
||||
uintptr_t Address_ = 0;
|
||||
uintptr_t MmShadowPfnDatabase = 0;
|
||||
|
||||
uintptr_t PteAddress = 0;
|
||||
// uintptr_t PdeAddress = 0;
|
||||
// uintptr_t PdpeAddress = 0;
|
||||
// uintptr_t Pml4Address = 0;
|
||||
|
||||
if (Address)
|
||||
{
|
||||
Address_ = (uintptr_t)Address;
|
||||
MmShadowPfnDatabase = (uintptr_t)GetMmShadowPfnDatabase();
|
||||
|
||||
PteAddress = ((Address_ >> 9) & 0x7FFFFFFFF8LL) + MmShadowPfnDatabase;
|
||||
// PdeAddress = ((PteAddress >> 9) & 0x7FFFFFFFF8LL) + MmShadowPfnDatabase;
|
||||
// PdpeAddress = ((PdeAddress >> 9) & 0x7FFFFFFFF8LL) + MmShadowPfnDatabase;
|
||||
// Pml4Address = ((PdpeAddress >> 9) & 0x7FFFFFFFF8LL) + MmShadowPfnDatabase;
|
||||
}
|
||||
|
||||
return (void*)PteAddress;
|
||||
}
|
||||
|
||||
MiscFlags_t GetThreadMiscFlags(PKTHREAD Thread)
|
||||
{
|
||||
MiscFlags_t Flags = { 0 };
|
||||
|
||||
if (!Thread)
|
||||
{
|
||||
DbgPrint(__FUNCTION__ ": Invalid thread\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
Flags = *(MiscFlags_t*)((char*)Thread + MiscFlags_offset);
|
||||
|
||||
EXIT:
|
||||
return Flags;
|
||||
}
|
||||
|
||||
void* GetThreadKernelShadowStack(PKTHREAD Thread)
|
||||
{
|
||||
void* Stack = NULL;
|
||||
|
||||
if (!Thread)
|
||||
{
|
||||
DbgPrint("Invalid thread\n");
|
||||
goto EXIT;
|
||||
}
|
||||
Stack = *(void**)((char*)Thread + KernelShadowStack_offset);
|
||||
|
||||
DbgPrint("Kernel Shadow Stack: %p\n", Stack);
|
||||
|
||||
EXIT:
|
||||
return Stack;
|
||||
}
|
||||
|
||||
void* GetThreadKernelShadowStackBase(PKTHREAD Thread)
|
||||
{
|
||||
void* Stack = NULL;
|
||||
|
||||
if (!Thread)
|
||||
{
|
||||
DbgPrint("Invalid thread\n");
|
||||
goto EXIT;
|
||||
}
|
||||
Stack = *(void**)((char*)Thread + KernelShadowStackBase_offset);
|
||||
|
||||
DbgPrint("Kernel Shadow Stack Base: %p\n", Stack);
|
||||
|
||||
EXIT:
|
||||
return Stack;
|
||||
}
|
||||
|
||||
void* GetThreadKernelShadowStackInitial(PKTHREAD Thread)
|
||||
{
|
||||
void* Stack = NULL;
|
||||
|
||||
if (!Thread)
|
||||
{
|
||||
DbgPrint("Invalid thread\n");
|
||||
goto EXIT;
|
||||
}
|
||||
Stack = *(void**)((char*)Thread + KernelShadowStackInitial_offset);
|
||||
|
||||
DbgPrint("Kernel Shadow Stack Initial: %p\n", Stack);
|
||||
|
||||
EXIT:
|
||||
return Stack;
|
||||
}
|
||||
|
||||
void* GetThreadKernelShadowStackLimit(PKTHREAD Thread)
|
||||
{
|
||||
void* Stack = NULL;
|
||||
|
||||
if (!Thread)
|
||||
{
|
||||
DbgPrint("Invalid thread\n");
|
||||
goto EXIT;
|
||||
}
|
||||
Stack = *(void**)((char*)Thread + KernelShadowStackLimit_offset);
|
||||
|
||||
DbgPrint("Kernel Shadow Stack Limit: %p\n", Stack);
|
||||
|
||||
EXIT:
|
||||
return Stack;
|
||||
}
|
||||
|
||||
void PrintCetKernelShadowStack(PKTHREAD Thread)
|
||||
{
|
||||
MiscFlags_t* Flags = NULL;
|
||||
|
||||
Flags = (MiscFlags_t*)((char*)Thread + MiscFlags_offset);
|
||||
|
||||
DbgPrint("Thread: %p\n", Thread);
|
||||
|
||||
DbgPrint(" CetUserShadowStack: %s\n",
|
||||
Flags->CetUserShadowStack ? "TRUE" : "FALSE");
|
||||
DbgPrint(" CetKernelShadowStack: %s\n",
|
||||
Flags->CetKernelShadowStack ? "TRUE" : "FALSE");
|
||||
}
|
||||
|
||||
NTSTATUS PrintCetKernelShadowStackCallback(PKTHREAD Thread, void* Context)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(Context);
|
||||
|
||||
PrintCetKernelShadowStack(Thread);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void PrintThreadProcessName(PKTHREAD Thread)
|
||||
{
|
||||
PKPROCESS Process = NULL;
|
||||
|
||||
if (Thread)
|
||||
{
|
||||
Process = *(void**)((char*)Thread + Process_offset);
|
||||
|
||||
DbgPrint("Thread: %p >> %s\n",
|
||||
Thread,
|
||||
(char*)Process + ImageFileName_offset);
|
||||
}
|
||||
}
|
||||
|
||||
NTSTATUS PrintThreadProcessNameCallback(PKTHREAD Thread, void* Context)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(Context);
|
||||
|
||||
PrintThreadProcessName(Thread);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void SetThreadCetKernelShadowStack(PKTHREAD Thread, uint8_t Value)
|
||||
{
|
||||
MiscFlags_t* Flags = NULL;
|
||||
|
||||
if (Thread)
|
||||
{
|
||||
DbgPrint("Thread: %p\n", Thread);
|
||||
|
||||
Flags = (MiscFlags_t*)((char*)Thread + MiscFlags_offset);
|
||||
|
||||
Flags->CetKernelShadowStack = Value & 1;
|
||||
}
|
||||
}
|
||||
|
||||
NTSTATUS SetThreadCetKernelShadowStackCallback(PKTHREAD Thread, void* Context)
|
||||
{
|
||||
if (!Thread)
|
||||
{
|
||||
DbgPrint(__FUNCTION__ ": Invalid thread\n");
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
SetThreadCetKernelShadowStack(Thread, (uintptr_t)Context & 0xff);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void SetThreadKernelShadowStack(PKTHREAD Thread, void* ShadowStack)
|
||||
{
|
||||
if (Thread)
|
||||
{
|
||||
*(void**)((char*)Thread + KernelShadowStack_offset) = ShadowStack;
|
||||
}
|
||||
}
|
||||
|
||||
void SetThreadKernelShadowStackBase(PKTHREAD Thread, void* ShadowStackBase)
|
||||
{
|
||||
if (Thread)
|
||||
{
|
||||
*(void**)((char*)Thread + KernelShadowStackBase_offset) = ShadowStackBase;
|
||||
}
|
||||
}
|
||||
|
||||
void SetThreadKernelShadowStackInitial(
|
||||
PKTHREAD Thread,
|
||||
void* ShadowStackInitial)
|
||||
{
|
||||
if (Thread)
|
||||
{
|
||||
*(void**)((char*)Thread + KernelShadowStackInitial_offset) =
|
||||
ShadowStackInitial;
|
||||
}
|
||||
}
|
||||
|
||||
void SetThreadKernelShadowStackLimit(PKTHREAD Thread, void* ShadowStackLimit)
|
||||
{
|
||||
if (Thread)
|
||||
{
|
||||
*(void**)((char*)Thread + KernelShadowStackLimit_offset) = ShadowStackLimit;
|
||||
}
|
||||
}
|
||||
|
||||
void SkipNextFrame()
|
||||
{
|
||||
uintptr_t module = 0;
|
||||
uintptr_t* new_rsp = NULL;
|
||||
|
||||
DbgPrintEnter();
|
||||
|
||||
module = (uintptr_t)GetModuleBase((void*)SkipNextFrame);
|
||||
new_rsp = (uintptr_t*)&new_rsp;
|
||||
|
||||
for (new_rsp += 2;; ++new_rsp)
|
||||
{
|
||||
if (MASK_KERNEL_ADDR(module) == MASK_KERNEL_ADDR(*new_rsp))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setRsp(new_rsp);
|
||||
|
||||
DbgPrintLeave();
|
||||
}
|
||||
|
||||
NTSTATUS ThreadWalker(ThreadCallback_t Callback, void* Context)
|
||||
{
|
||||
LIST_ENTRY* Entry = NULL;
|
||||
LIST_ENTRY* Head = NULL;
|
||||
NTSTATUS Status = STATUS_INVALID_PARAMETER;
|
||||
PEPROCESS Process = NULL;
|
||||
PKTHREAD Thread = NULL;
|
||||
|
||||
if (!Callback)
|
||||
{
|
||||
DbgPrint(__FUNCTION__ ": Invalid parameter\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
Process = PsGetCurrentProcess();
|
||||
Head = (LIST_ENTRY*)((char*)Process + ThreadListHead_offset);
|
||||
|
||||
for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink)
|
||||
{
|
||||
Thread = (PKTHREAD)((uintptr_t)Entry - ThreadListEntry_offset);
|
||||
|
||||
Status = Callback(Thread, Context);
|
||||
if (NT_ERROR(Status))
|
||||
{
|
||||
DbgPrint(__FUNCTION__ ": Callback failed\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EXIT:
|
||||
DbgPrintLeave();
|
||||
|
||||
return Status;
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
#ifndef __DRIVER_UTILS_H__
|
||||
#define __DRIVER_UTILS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <wdm.h>
|
||||
|
||||
#include "common/utils.h"
|
||||
|
||||
#define DbgPrintEnter() DbgPrint("Entering " __FUNCTION__ "\n")
|
||||
#define DbgPrintLeave() DbgPrint("Leaving " __FUNCTION__ "\n")
|
||||
|
||||
// ===========================================================================
|
||||
// Callback definitions
|
||||
// ===========================================================================
|
||||
|
||||
typedef NTSTATUS (*ThreadCallback_t)(PKTHREAD Thread, void* Context);
|
||||
|
||||
// ===========================================================================
|
||||
// Function declarations
|
||||
// ===========================================================================
|
||||
|
||||
/**
|
||||
* \brief Creates a device with "\Device\ShadowStack" name and exposes it
|
||||
* with "\DosDevices\ShadowStack" as symbolic link
|
||||
*
|
||||
* \param DriverObject Pointer to the driver object of the current driver
|
||||
*
|
||||
* \return STATUS_SUCCESS on success, otherwise error code
|
||||
*/
|
||||
NTSTATUS CreateExposedDevice(
|
||||
DRIVER_OBJECT* DriverObject);
|
||||
|
||||
/**
|
||||
* \brief Creates a device and expose it to the userland
|
||||
*
|
||||
* \param DriverObject Pointer to the driver object of the current driver
|
||||
* \param DeviceName Pointer to the device name of the device to be created
|
||||
* \param SymlinkName Pointer to the name that will be exposed to the userland
|
||||
*
|
||||
* \return STATUS_SUCCESS on success, otherwise error code
|
||||
*/
|
||||
NTSTATUS CreateExposedDeviceEx(
|
||||
DRIVER_OBJECT* DriverObject,
|
||||
wchar_t* DeviceName,
|
||||
wchar_t* SymlinkName);
|
||||
|
||||
/**
|
||||
* \brief Deletes the currently created device
|
||||
*
|
||||
* \param DriverObject Pointer to the driver object of the current driver
|
||||
*/
|
||||
void DeleteExposedDevice(
|
||||
DRIVER_OBJECT* DriverObject);
|
||||
|
||||
/**
|
||||
* \brief Deletes the device related to the given symbolic link
|
||||
*
|
||||
* \param DriverObject Pointer to the driver object of the current driver
|
||||
* \param SymlinkName Pointer to the symbolic link name which has to be deleted
|
||||
*/
|
||||
void DeleteExposedDeviceEx(
|
||||
DRIVER_OBJECT* DriverObject,
|
||||
wchar_t* SymlinkName);
|
||||
|
||||
/**
|
||||
* \brief Performs integer division
|
||||
*
|
||||
* \param Dividend Value to divide
|
||||
* \param Divisor Value used to divide
|
||||
*
|
||||
* \return The quotient
|
||||
*/
|
||||
int32_t DivInteger(
|
||||
int32_t Dividend,
|
||||
int32_t Divisor);
|
||||
|
||||
/**
|
||||
* \brief Increments the return address to the caller by 1
|
||||
*/
|
||||
void IncRetAddr();
|
||||
|
||||
/**
|
||||
* \brief Retrieves the kernel shadow stack pointer of the current thread
|
||||
*
|
||||
* \return The kernel shadow stack pointer of the current thread
|
||||
*/
|
||||
void* GetCurrentThreadKernelShadowStack();
|
||||
|
||||
/**
|
||||
* \brief Retrieves the kernel shadow stack base pointer of the current thread
|
||||
*
|
||||
* \return The kernel shadow stack base pointer of the current thread
|
||||
*/
|
||||
void* GetCurrentThreadKernelShadowStackBase();
|
||||
|
||||
/**
|
||||
* \brief Retrieves the kernel shadow stack initial pointer of the current thread
|
||||
*
|
||||
* \return The kernel shadow stack initial pointer of the current thread
|
||||
*/
|
||||
void* GetCurrentThreadKernelShadowStackInitial();
|
||||
|
||||
/**
|
||||
* \brief Retrieves the kernel shadow stack limit of the current thread
|
||||
*
|
||||
* \return The kernel shadow stack limit of the current thread
|
||||
*/
|
||||
void* GetCurrentThreadKernelShadowStackLimit();
|
||||
|
||||
/**
|
||||
* \brief Retrieves the misc flags of the current thread
|
||||
*
|
||||
* \return Misc flags of the current thread
|
||||
*/
|
||||
MiscFlags_t GetCurrentThreadMiscFlags();
|
||||
|
||||
/**
|
||||
* \brief Looks for the kernel base address
|
||||
*
|
||||
* \return The kernel base address
|
||||
*/
|
||||
void* GetKernelBase();
|
||||
|
||||
/**
|
||||
* \brief Looks for the shadow PFN database
|
||||
*
|
||||
* \return The PFN database
|
||||
*/
|
||||
void* GetMmShadowPfnDatabase();
|
||||
|
||||
/**
|
||||
* \brief Looks for the module base address related to the given address
|
||||
*
|
||||
* \param Address Address in the module from which the base address has to be
|
||||
* retrieved
|
||||
*
|
||||
* \return The module base address
|
||||
*/
|
||||
void* GetModuleBase(
|
||||
void* Address);
|
||||
|
||||
/**
|
||||
* \brief Looks for the virtual shadow PTE address of the given address
|
||||
*
|
||||
* \param Address Address of which to get the virtual PTE address
|
||||
*
|
||||
* \return The virtual PTE address
|
||||
*/
|
||||
void* GetShadowPteAddress(
|
||||
void* Address);
|
||||
|
||||
/**
|
||||
* \brief Retrieves the misc flags of the given thread
|
||||
*
|
||||
* \param Thread Pointer to the thread from which the misc flags are retrieved
|
||||
*
|
||||
* \return Misc flags of the given thread
|
||||
*/
|
||||
MiscFlags_t GetThreadMiscFlags(
|
||||
PKTHREAD Thread);
|
||||
|
||||
/**
|
||||
* \brief Retrieves the kernel shadow stack pointer of the given thread
|
||||
*
|
||||
* \param Thread Pointer to the thread from which retrieve the kernel shadow
|
||||
* stack pointer
|
||||
*
|
||||
* \return The kernel shadow stack pointer of the given thread
|
||||
*/
|
||||
void* GetThreadKernelShadowStack(
|
||||
PKTHREAD Thread);
|
||||
|
||||
/**
|
||||
* \brief Retrieves the kernel shadow stack base pointer of the given thread
|
||||
*
|
||||
* \param Thread Pointer to the thread from which retrieve the kernel shadow
|
||||
* stack base pointer
|
||||
*
|
||||
* \return The kernel shadow stack base pointer of the given thread
|
||||
*/
|
||||
void* GetThreadKernelShadowStackBase(
|
||||
PKTHREAD Thread);
|
||||
|
||||
/**
|
||||
* \brief Retrieves the kernel shadow stack initial pointer of the given thread
|
||||
*
|
||||
* \param Thread Pointer to the thread from which retrieve the kernel shadow
|
||||
* stack initial pointer
|
||||
*
|
||||
* \return The kernel shadow stack initial pointer of the given thread
|
||||
*/
|
||||
void* GetThreadKernelShadowStackInitial(
|
||||
PKTHREAD Thread);
|
||||
|
||||
/**
|
||||
* \brief Retrieves the kernel shadow stack limit pointer of the given thread
|
||||
*
|
||||
* \param Thread Pointer to the thread from which retrieve the kernel shadow
|
||||
* stack limit pointer
|
||||
*
|
||||
* \return The kernel shadow stack limit pointer of the given thread
|
||||
*/
|
||||
void* GetThreadKernelShadowStackLimit(
|
||||
PKTHREAD Thread);
|
||||
|
||||
/**
|
||||
* \brief Prints the CetUserShadowStack and the CetKernelShadowStack flags of
|
||||
* the given thread
|
||||
*
|
||||
* \param Thread Pointer to the thread from which the flags have to be read
|
||||
*/
|
||||
void PrintCetKernelShadowStack(
|
||||
PKTHREAD Thread);
|
||||
|
||||
/**
|
||||
* \brief Callback used to call PrintCetKernelShadowStack (act as a wrapper)
|
||||
*
|
||||
* \param Thread Pointer to the thread from which the flags have to be read
|
||||
* \param Context Unused parameter
|
||||
*
|
||||
* \return STATUS_SUCCESS always
|
||||
*/
|
||||
NTSTATUS PrintCetKernelShadowStackCallback(
|
||||
PKTHREAD Thread,
|
||||
void* Context);
|
||||
|
||||
/**
|
||||
* \brief Prints the process name of the given thread
|
||||
*
|
||||
* \param Thread Pointer to the thread from which the process then process name
|
||||
* are retrieved
|
||||
*/
|
||||
void PrintThreadProcessName(
|
||||
PKTHREAD Thread);
|
||||
|
||||
/**
|
||||
* \brief Callback used to call PrintThreadProcessName (act as a wrapper)
|
||||
*
|
||||
* \param Thread Pointer to the thread from which the process then process name
|
||||
* are retrieved
|
||||
* \param Context Unused parameter
|
||||
*
|
||||
* \return STATUS_SUCCESS always
|
||||
*/
|
||||
NTSTATUS PrintThreadProcessNameCallback(
|
||||
PKTHREAD Thread,
|
||||
void* Context);
|
||||
|
||||
/**
|
||||
* \brief Sets the CetKernelShadowStack of the given thread with given value
|
||||
*
|
||||
* \param Thread Pointer to the thread to which the CetKernelShadowStack flag
|
||||
* as to be set
|
||||
* \param Value Value to set
|
||||
*/
|
||||
void SetThreadCetKernelShadowStack(
|
||||
PKTHREAD Thread,
|
||||
uint8_t Value);
|
||||
|
||||
/**
|
||||
* \brief Callback used to call SetThreadCetKernelShadowStack (act as a wrapper)
|
||||
*
|
||||
* \param Thread Pointer to the thread to which the CetKernelShadowStack flag
|
||||
* as to be set
|
||||
* \param Context Represent the value used to set CetKernelShadowStack in
|
||||
* SetThreadCetKernelShadowStack function
|
||||
*/
|
||||
NTSTATUS SetThreadCetKernelShadowStackCallback(
|
||||
PKTHREAD Thread,
|
||||
void* Context);
|
||||
|
||||
/**
|
||||
* \brief Sets the kernel shadow stack pointer of the given thread
|
||||
*
|
||||
* \param Thread Pointer to the thread to which set the kernel shadow stack
|
||||
* pointer
|
||||
* \param ShadowStack Pointer to the shadow stack to set
|
||||
*/
|
||||
void SetThreadKernelShadowStack(
|
||||
PKTHREAD Thread,
|
||||
void* ShadowStack);
|
||||
|
||||
/**
|
||||
* \brief Sets the kernel shadow stack base pointer of the given thread
|
||||
*
|
||||
* \param Thread Pointer to the thread to which set the kernel shadow
|
||||
* stack base pointer
|
||||
* \param ShadowStackBase Pointer to the shadow stack base to set
|
||||
*/
|
||||
void SetThreadKernelShadowStackBase(
|
||||
PKTHREAD Thread,
|
||||
void* ShadowStackBase);
|
||||
|
||||
/**
|
||||
* \brief Sets the kernel shadow stack initial pointer of the given thread
|
||||
*
|
||||
* \param Thread Pointer to the thread to which set the kernel
|
||||
* shadow stack initial pointer
|
||||
* \param ShadowStackInitial Pointer to the shadow stack initial to set
|
||||
*/
|
||||
void SetThreadKernelShadowStackInitial(
|
||||
PKTHREAD Thread,
|
||||
void* ShadowStackInitial);
|
||||
|
||||
/**
|
||||
* \brief Sets the kernel shadow stack limit pointer of the given thread
|
||||
*
|
||||
* \param Thread Pointer to the thread to which set the kernel shadow
|
||||
* stack limit pointer
|
||||
* \param ShadowStackLimit Pointer to the shadow stack limit to set
|
||||
*/
|
||||
void SetThreadKernelShadowStackLimit(
|
||||
PKTHREAD Thread,
|
||||
void* ShadowStackLimit);
|
||||
|
||||
/**
|
||||
* \brief Skips the next stack frame
|
||||
*/
|
||||
void SkipNextFrame();
|
||||
|
||||
/**
|
||||
* \brief Walks through every thread of the current process
|
||||
*
|
||||
* \param Callback Pointer to the callback to call for each thread
|
||||
* \param Context Pointer to the context to pass to each thread
|
||||
*
|
||||
* \return STATUS_SUCCESS on success, otherwise error code
|
||||
*/
|
||||
NTSTATUS ThreadWalker(
|
||||
ThreadCallback_t Callback,
|
||||
void* Context);
|
||||
|
||||
#endif // __DRIVER_UTILS_H__
|
||||
Reference in New Issue
Block a user