mirror of
https://github.com/donnaskiez/ac.git
synced 2024-11-21 22:24:08 +01:00
refactor nmi, dpc and some other stuf
This commit is contained in:
parent
fc8e1792dd
commit
0a1e01e8e7
20 changed files with 26823 additions and 322 deletions
16
README.md
16
README.md
|
@ -6,8 +6,9 @@ open source anti cheat (lol) which I made for fun.
|
||||||
|
|
||||||
- Attached thread detection
|
- Attached thread detection
|
||||||
- Process module .text section integrity checks
|
- Process module .text section integrity checks
|
||||||
- NMI and APC stackwalking
|
- NMI stackwalking via isr iretq
|
||||||
- IPI stackwalking which is a relatively unknown method compared to NMIs and APCs
|
- APC stackwalking via RtlCaptureStackBackTrace
|
||||||
|
- DPC stackwalking via RtlCaptureStackBackTrace (harder to disable)
|
||||||
- Handle stripping via obj callbacks
|
- Handle stripping via obj callbacks
|
||||||
- Process handle table enumeration
|
- Process handle table enumeration
|
||||||
- System module verification
|
- System module verification
|
||||||
|
@ -68,6 +69,9 @@ Before we continue, ensure you enable test signing mode as this driver is not si
|
||||||
bcdedit -set TESTSIGNING on
|
bcdedit -set TESTSIGNING on
|
||||||
bcdedit /debug on
|
bcdedit /debug on
|
||||||
```
|
```
|
||||||
|
|
||||||
|
3. Restart Windows
|
||||||
|
|
||||||
## building and running the project
|
## building and running the project
|
||||||
|
|
||||||
1. Clone the project i.e `git clone git@github.com:donnaskiez/ac.git`
|
1. Clone the project i.e `git clone git@github.com:donnaskiez/ac.git`
|
||||||
|
@ -77,16 +81,16 @@ bcdedit /debug on
|
||||||
- `Inf2Cat -> General -> Use Local Time` to `Yes`
|
- `Inf2Cat -> General -> Use Local Time` to `Yes`
|
||||||
- `C/C++ -> Treat Warnings As Errors` to `No`
|
- `C/C++ -> Treat Warnings As Errors` to `No`
|
||||||
- `C/C++ -> Spectre Mitigation` to `Disabled`
|
- `C/C++ -> Spectre Mitigation` to `Disabled`
|
||||||
5. Move the `driver.sys` file located in `ac\x64\Release` into the `Windows\System32\Drivers` directory
|
5. Move the `driver.sys` file located in `ac\x64\Release - No Server\` into the `Windows\System32\Drivers` directory
|
||||||
- You can rename the driver if you would like
|
- You can rename the driver if you would like
|
||||||
6. Use the [OSR Loader](https://www.osronline.com/article.cfm%5Earticle=157.htm) and select `driver.sys` (or whatever you named it) that you moved to the Windows drivers folder. DO NOT REGISTER THE SERVICE YET.
|
6. Use the [OSR Loader](https://www.osronline.com/article.cfm%5Earticle=157.htm) and select `driver.sys` (or whatever you named it) that you moved to the Windows drivers folder. *DO NOT REGISTER THE SERVICE YET*.
|
||||||
7. Under `Service Start` select `System`. This is VERY important!
|
7. Under `Service Start` select `System`. This is VERY important!
|
||||||
8. Click `Register Service`. *Do NOT click* `Start Service`!
|
8. Click `Register Service`. *Do NOT click* `Start Service`!
|
||||||
9. Restart Windows.
|
9. Restart Windows.
|
||||||
10. Once restarted, open the program you would like to protect. This could be anything i.e cs2, notepad etc.
|
10. Once restarted, open the program you would like to protect. This could be anything i.e cs2, notepad etc.
|
||||||
- if you do use a game to test, ensure the games anti-cheat is turned off before testing
|
- if you do use a game to test, ensure the games anti-cheat is turned off before testing
|
||||||
11. Open your dll injector program of choice as administrator (I simply use [Process Hacker](https://processhacker.sourceforge.io/))
|
11. Open your dll injector of choice (I simply use [Process Hacker](https://processhacker.sourceforge.io/))
|
||||||
12. Inject the dll found in `ac\x64\Release` named `user.dll` into the target program
|
12. Inject the dll found in `ac\x64\Release - No Server\` named `user.dll` into the target program
|
||||||
|
|
||||||
Logs will be printed to both the terminal output and the kernel debugger. See below for configuring kernel debugger output.
|
Logs will be printed to both the terminal output and the kernel debugger. See below for configuring kernel debugger output.
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
#define INVALID_DRIVER_LIST_HEAD_POOL 'rwar'
|
#define INVALID_DRIVER_LIST_HEAD_POOL 'rwar'
|
||||||
#define INVALID_DRIVER_LIST_ENTRY_POOL 'gaah'
|
#define INVALID_DRIVER_LIST_ENTRY_POOL 'gaah'
|
||||||
#define POOL_TAG_APC 'apcc'
|
#define POOL_TAG_APC 'apcc'
|
||||||
|
#define POOL_TAG_DPC 'apcc'
|
||||||
#define SYSTEM_MODULES_POOL 'halb'
|
#define SYSTEM_MODULES_POOL 'halb'
|
||||||
#define THREAD_DATA_POOL 'doof'
|
#define THREAD_DATA_POOL 'doof'
|
||||||
#define PROC_AFFINITY_POOL 'eeee'
|
#define PROC_AFFINITY_POOL 'eeee'
|
||||||
|
@ -119,6 +120,7 @@
|
||||||
#define REPORT_HIDDEN_SYSTEM_THREAD 90
|
#define REPORT_HIDDEN_SYSTEM_THREAD 90
|
||||||
#define REPORT_ILLEGAL_ATTACH_PROCESS 100
|
#define REPORT_ILLEGAL_ATTACH_PROCESS 100
|
||||||
#define REPORT_APC_STACKWALK 110
|
#define REPORT_APC_STACKWALK 110
|
||||||
|
#define REPORT_DPC_STACKWALK 120
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generic macros that allow you to quickly determine whether
|
* Generic macros that allow you to quickly determine whether
|
||||||
|
@ -898,23 +900,6 @@ typedef struct _DUMP_HEADER
|
||||||
struct _KDDEBUGGER_DATA64* KdDebuggerDataBlock;
|
struct _KDDEBUGGER_DATA64* KdDebuggerDataBlock;
|
||||||
} DUMP_HEADER, *PDUMP_HEADER;
|
} DUMP_HEADER, *PDUMP_HEADER;
|
||||||
|
|
||||||
typedef union _DIRECTORY_TABLE_BASE
|
|
||||||
{
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
UINT64 Ignored0 : 3; /* 2:0 */
|
|
||||||
UINT64 PageWriteThrough : 1; /* 3 */
|
|
||||||
UINT64 PageCacheDisable : 1; /* 4 */
|
|
||||||
UINT64 _Ignored1 : 7; /* 11:5 */
|
|
||||||
UINT64 PhysicalAddress : 36; /* 47:12 */
|
|
||||||
UINT64 _Reserved0 : 16; /* 63:48 */
|
|
||||||
|
|
||||||
} Bits;
|
|
||||||
|
|
||||||
UINT64 BitAddress;
|
|
||||||
|
|
||||||
} CR3, DIR_TABLE_BASE;
|
|
||||||
|
|
||||||
typedef union _VIRTUAL_MEMORY_ADDRESS
|
typedef union _VIRTUAL_MEMORY_ADDRESS
|
||||||
{
|
{
|
||||||
struct
|
struct
|
||||||
|
@ -1411,4 +1396,32 @@ C_ASSERT(FIELD_OFFSET(DUMP_HEADER, KdDebuggerDataBlock) == 0x80);
|
||||||
# define DUMP_BLOCK_SIZE 0x40000
|
# define DUMP_BLOCK_SIZE 0x40000
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define IA32_GS_BASE 0xc0000101
|
||||||
|
#define KPCR_TSS_BASE_OFFSET 0x008
|
||||||
|
#define TSS_IST_OFFSET 0x01c
|
||||||
|
#define WINDOWS_USERMODE_MAX_ADDRESS 0x00007FFFFFFFFFFF
|
||||||
|
|
||||||
|
typedef struct _MACHINE_FRAME
|
||||||
|
{
|
||||||
|
UINT64 rip;
|
||||||
|
UINT64 cs;
|
||||||
|
UINT64 eflags;
|
||||||
|
UINT64 rsp;
|
||||||
|
UINT64 ss;
|
||||||
|
|
||||||
|
} MACHINE_FRAME, *PMACHINE_FRAME;
|
||||||
|
|
||||||
|
NTKERNELAPI
|
||||||
|
_IRQL_requires_max_(APC_LEVEL)
|
||||||
|
_IRQL_requires_min_(PASSIVE_LEVEL)
|
||||||
|
_IRQL_requires_same_
|
||||||
|
VOID
|
||||||
|
KeGenericCallDpc(_In_ PKDEFERRED_ROUTINE Routine, _In_opt_ PVOID Context);
|
||||||
|
|
||||||
|
NTKERNELAPI
|
||||||
|
_IRQL_requires_(DISPATCH_LEVEL)
|
||||||
|
_IRQL_requires_same_
|
||||||
|
VOID
|
||||||
|
KeSignalCallDpcDone(_In_ PVOID SystemArgument1);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1416,15 +1416,6 @@ DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
|
||||||
return STATUS_FAILED_DRIVER_ENTRY;
|
return STATUS_FAILED_DRIVER_ENTRY;
|
||||||
}
|
}
|
||||||
|
|
||||||
LPCSTR driver_name = NULL;
|
|
||||||
GetDriverName(&driver_name);
|
|
||||||
DEBUG_VERBOSE("Driver name: %s", driver_name);
|
|
||||||
|
|
||||||
// ValidateSystemModules();
|
|
||||||
// ValidateNtoskrnl();
|
|
||||||
// LaunchInterProcessInterrupt(NULL);
|
|
||||||
// EnumerateBigPoolAllocations();
|
|
||||||
DEBUG_VERBOSE("Driver Entry Complete.");
|
DEBUG_VERBOSE("Driver Entry Complete.");
|
||||||
|
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -185,7 +185,4 @@ _Releases_lock_(_Lock_kind_mutex_)
|
||||||
VOID
|
VOID
|
||||||
GetDriverSymbolicLink(_Out_ PUNICODE_STRING DeviceSymbolicLink);
|
GetDriverSymbolicLink(_Out_ PUNICODE_STRING DeviceSymbolicLink);
|
||||||
|
|
||||||
NTSTATUS
|
|
||||||
SelfReferenceDriver();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -34,6 +34,7 @@
|
||||||
<Configuration>Debug</Configuration>
|
<Configuration>Debug</Configuration>
|
||||||
<Platform Condition="'$(Platform)' == ''">x64</Platform>
|
<Platform Condition="'$(Platform)' == ''">x64</Platform>
|
||||||
<RootNamespace>driver</RootNamespace>
|
<RootNamespace>driver</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>$(LatestTargetPlatformVersion)</WindowsTargetPlatformVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
@ -113,6 +114,7 @@
|
||||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||||
<EnableClangTidyCodeAnalysis>false</EnableClangTidyCodeAnalysis>
|
<EnableClangTidyCodeAnalysis>false</EnableClangTidyCodeAnalysis>
|
||||||
<EnableMicrosoftCodeAnalysis>false</EnableMicrosoftCodeAnalysis>
|
<EnableMicrosoftCodeAnalysis>false</EnableMicrosoftCodeAnalysis>
|
||||||
|
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||||
|
@ -201,6 +203,7 @@
|
||||||
<ClInclude Include="common.h" />
|
<ClInclude Include="common.h" />
|
||||||
<ClInclude Include="driver.h" />
|
<ClInclude Include="driver.h" />
|
||||||
<ClInclude Include="hv.h" />
|
<ClInclude Include="hv.h" />
|
||||||
|
<ClInclude Include="ia32.h" />
|
||||||
<ClInclude Include="integrity.h" />
|
<ClInclude Include="integrity.h" />
|
||||||
<ClInclude Include="ioctl.h" />
|
<ClInclude Include="ioctl.h" />
|
||||||
<ClInclude Include="modules.h" />
|
<ClInclude Include="modules.h" />
|
||||||
|
|
|
@ -83,6 +83,9 @@
|
||||||
<ClInclude Include="thread.h">
|
<ClInclude Include="thread.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="ia32.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<MASM Include="asm.asm">
|
<MASM Include="asm.asm">
|
||||||
|
|
26454
driver/ia32.h
Normal file
26454
driver/ia32.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1241,7 +1241,7 @@ GetHardDiskDriveSerialNumber(_Inout_ PVOID ConfigDrive0Serial, _In_ SIZE_T Confi
|
||||||
RtlCopyMemory(ConfigDrive0Serial, serial_number, serial_length);
|
RtlCopyMemory(ConfigDrive0Serial, serial_number, serial_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_INFO("Successfully retrieved hard disk serial number.");
|
DEBUG_VERBOSE("Successfully retrieved hard disk serial number.");
|
||||||
end:
|
end:
|
||||||
|
|
||||||
if (handle)
|
if (handle)
|
||||||
|
|
|
@ -54,7 +54,7 @@ DispatchApcOperation(_In_ PAPC_OPERATION_ID Operation);
|
||||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20017, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20017, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
#define IOCTL_CHECK_FOR_EPT_HOOK \
|
#define IOCTL_CHECK_FOR_EPT_HOOK \
|
||||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20018, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20018, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
#define IOCTL_LAUNCH_IPI_INTERRUPT \
|
#define IOCTL_LAUNCH_DPC_STACKWALK \
|
||||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20019, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20019, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
#define IOCTL_VALIDATE_SYSTEM_MODULES \
|
#define IOCTL_VALIDATE_SYSTEM_MODULES \
|
||||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20020, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20020, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
@ -446,17 +446,6 @@ DeviceControl(_In_ PDRIVER_OBJECT DriverObject, _Inout_ PIRP Irp)
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IOCTL_LAUNCH_IPI_INTERRUPT:
|
|
||||||
|
|
||||||
DEBUG_INFO("IOCTL_LAUNCH_IPI_INTERRUPT Received");
|
|
||||||
|
|
||||||
status = LaunchInterProcessInterrupt(Irp);
|
|
||||||
|
|
||||||
if (!NT_SUCCESS(status))
|
|
||||||
DEBUG_ERROR("LaunchInterProcessInterrupt failed with status %x", status);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IOCTL_VALIDATE_SYSTEM_MODULES:
|
case IOCTL_VALIDATE_SYSTEM_MODULES:
|
||||||
|
|
||||||
DEBUG_INFO("IOCTL_VALIDATE_SYSTEM_MODULES Received");
|
DEBUG_INFO("IOCTL_VALIDATE_SYSTEM_MODULES Received");
|
||||||
|
@ -472,6 +461,19 @@ DeviceControl(_In_ PDRIVER_OBJECT DriverObject, _Inout_ PIRP Irp)
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case IOCTL_LAUNCH_DPC_STACKWALK:
|
||||||
|
|
||||||
|
DEBUG_INFO("IOCTL_LAUNCH_DPC_STACKWALK Received");
|
||||||
|
|
||||||
|
status = DispatchStackwalkToEachCpuViaDpc();
|
||||||
|
|
||||||
|
if (!NT_SUCCESS(status))
|
||||||
|
DEBUG_ERROR("DispatchStackwalkToEachCpuViaDpc failed with status %x",
|
||||||
|
status);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
DEBUG_WARNING("Invalid IOCTL passed to driver: %lx",
|
DEBUG_WARNING("Invalid IOCTL passed to driver: %lx",
|
||||||
stack_location->Parameters.DeviceIoControl.IoControlCode);
|
stack_location->Parameters.DeviceIoControl.IoControlCode);
|
||||||
|
|
519
driver/modules.c
519
driver/modules.c
|
@ -3,6 +3,7 @@
|
||||||
#include "callbacks.h"
|
#include "callbacks.h"
|
||||||
#include "driver.h"
|
#include "driver.h"
|
||||||
#include "ioctl.h"
|
#include "ioctl.h"
|
||||||
|
#include "ia32.h"
|
||||||
|
|
||||||
#define WHITELISTED_MODULE_TAG 'whte'
|
#define WHITELISTED_MODULE_TAG 'whte'
|
||||||
|
|
||||||
|
@ -58,12 +59,6 @@ typedef struct _NMI_POOLS
|
||||||
|
|
||||||
} NMI_POOLS, *PNMI_POOLS;
|
} NMI_POOLS, *PNMI_POOLS;
|
||||||
|
|
||||||
typedef struct _NMI_CORE_CONTEXT
|
|
||||||
{
|
|
||||||
INT nmi_callbacks_run;
|
|
||||||
|
|
||||||
} NMI_CORE_CONTEXT, *PNMI_CORE_CONTEXT;
|
|
||||||
|
|
||||||
typedef struct _MODULE_VALIDATION_FAILURE_HEADER
|
typedef struct _MODULE_VALIDATION_FAILURE_HEADER
|
||||||
{
|
{
|
||||||
INT module_count;
|
INT module_count;
|
||||||
|
@ -72,26 +67,14 @@ typedef struct _MODULE_VALIDATION_FAILURE_HEADER
|
||||||
|
|
||||||
typedef struct _NMI_CONTEXT
|
typedef struct _NMI_CONTEXT
|
||||||
{
|
{
|
||||||
PVOID thread_data_pool;
|
UINT64 interrupted_rip;
|
||||||
PVOID stack_frames;
|
UINT64 interrupted_rsp;
|
||||||
PVOID nmi_core_context;
|
UINT64 kthread;
|
||||||
INT core_count;
|
UINT32 callback_count;
|
||||||
|
BOOLEAN user_thread;
|
||||||
|
|
||||||
} NMI_CONTEXT, *PNMI_CONTEXT;
|
} NMI_CONTEXT, *PNMI_CONTEXT;
|
||||||
|
|
||||||
typedef struct _NMI_CALLBACK_DATA
|
|
||||||
{
|
|
||||||
UINT64 kthread_address;
|
|
||||||
UINT64 kprocess_address;
|
|
||||||
UINT64 start_address;
|
|
||||||
UINT64 stack_limit;
|
|
||||||
UINT64 stack_base;
|
|
||||||
uintptr_t stack_frames_offset;
|
|
||||||
INT num_frames_captured;
|
|
||||||
UINT64 cr3;
|
|
||||||
|
|
||||||
} NMI_CALLBACK_DATA, *PNMI_CALLBACK_DATA;
|
|
||||||
|
|
||||||
typedef struct _INVALID_DRIVER
|
typedef struct _INVALID_DRIVER
|
||||||
{
|
{
|
||||||
struct _INVALID_DRIVER* next;
|
struct _INVALID_DRIVER* next;
|
||||||
|
@ -609,8 +592,9 @@ ValidateDriverObjects(_In_ PSYSTEM_MODULES SystemModules,
|
||||||
|
|
||||||
if (!NT_SUCCESS(status))
|
if (!NT_SUCCESS(status))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("ValidateDriverIOCTLDispatchRegion failed with status %x",
|
DEBUG_ERROR(
|
||||||
status);
|
"ValidateDriverIOCTLDispatchRegion failed with status %x",
|
||||||
|
status);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -781,6 +765,10 @@ end:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: this probably doesnt need to return an NTSTATUS, we can just return a boolean and remove
|
||||||
|
* the out variable.
|
||||||
|
*/
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
IsInstructionPointerInInvalidRegion(_In_ UINT64 RIP,
|
IsInstructionPointerInInvalidRegion(_In_ UINT64 RIP,
|
||||||
_In_ PSYSTEM_MODULES SystemModules,
|
_In_ PSYSTEM_MODULES SystemModules,
|
||||||
|
@ -813,27 +801,24 @@ IsInstructionPointerInInvalidRegion(_In_ UINT64 RIP,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* todo: rename this to analyse stackwalk or something
|
* todo: rename this to analyse stackwalk or something
|
||||||
*/
|
*/
|
||||||
STATIC
|
STATIC
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
AnalyseNmiData(_In_ PNMI_CONTEXT NmiContext, _In_ PSYSTEM_MODULES SystemModules, _Inout_ PIRP Irp)
|
AnalyseNmiData(_In_ PNMI_CONTEXT NmiContext, _In_ PSYSTEM_MODULES SystemModules, _Inout_ PIRP Irp)
|
||||||
{
|
{
|
||||||
PAGED_CODE();
|
PAGED_CODE();
|
||||||
|
|
||||||
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
||||||
|
BOOLEAN flag = FALSE;
|
||||||
|
|
||||||
if (!NmiContext || !SystemModules)
|
if (!NmiContext || !SystemModules)
|
||||||
return STATUS_INVALID_PARAMETER;
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
for (INT core = 0; core < KeQueryActiveProcessorCount(0); core++)
|
||||||
|
|
||||||
for (INT core = 0; core < NmiContext->core_count; core++)
|
|
||||||
{
|
{
|
||||||
PNMI_CORE_CONTEXT context =
|
|
||||||
(PNMI_CORE_CONTEXT)((uintptr_t)NmiContext->nmi_core_context +
|
|
||||||
core * sizeof(NMI_CORE_CONTEXT));
|
|
||||||
|
|
||||||
/* Make sure our NMIs were run */
|
/* Make sure our NMIs were run */
|
||||||
if (!context->nmi_callbacks_run)
|
if (!NmiContext[core].callback_count)
|
||||||
{
|
{
|
||||||
NTSTATUS status =
|
NTSTATUS status =
|
||||||
ValidateIrpOutputBuffer(Irp, sizeof(NMI_CALLBACK_FAILURE));
|
ValidateIrpOutputBuffer(Irp, sizeof(NMI_CALLBACK_FAILURE));
|
||||||
|
@ -842,7 +827,7 @@ AnalyseNmiData(_In_ PNMI_CONTEXT NmiContext, _In_ PSYSTEM_MODULES SystemModules,
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("ValidateIrpOutputBuffer failed with status %x",
|
DEBUG_ERROR("ValidateIrpOutputBuffer failed with status %x",
|
||||||
status);
|
status);
|
||||||
return status;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
NMI_CALLBACK_FAILURE report = {0};
|
NMI_CALLBACK_FAILURE report = {0};
|
||||||
|
@ -859,68 +844,56 @@ AnalyseNmiData(_In_ PNMI_CONTEXT NmiContext, _In_ PSYSTEM_MODULES SystemModules,
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
PNMI_CALLBACK_DATA thread_data =
|
DEBUG_VERBOSE("Analysing Nmi Data for: cpu number: %i callback count: %lx",
|
||||||
(PNMI_CALLBACK_DATA)((uintptr_t)NmiContext->thread_data_pool +
|
|
||||||
core * sizeof(NMI_CALLBACK_DATA));
|
|
||||||
|
|
||||||
DEBUG_VERBOSE("Analysing Nmi Data for: cpu number: %i callback count: %i",
|
|
||||||
core,
|
core,
|
||||||
context->nmi_callbacks_run);
|
NmiContext[core].callback_count);
|
||||||
|
|
||||||
/* Walk the stack */
|
if (NmiContext[core].user_thread)
|
||||||
for (INT frame = 0; frame < thread_data->num_frames_captured; frame++)
|
continue;
|
||||||
|
|
||||||
|
status = IsInstructionPointerInInvalidRegion(
|
||||||
|
NmiContext[core].interrupted_rip, SystemModules, &flag);
|
||||||
|
|
||||||
|
if (!NT_SUCCESS(status))
|
||||||
{
|
{
|
||||||
BOOLEAN flag = TRUE;
|
DEBUG_ERROR("IsInstructionPointerInInvalidRegion failed with status %x",
|
||||||
DWORD64 stack_frame =
|
status);
|
||||||
*(DWORD64*)(((uintptr_t)NmiContext->stack_frames +
|
continue;
|
||||||
thread_data->stack_frames_offset + frame * sizeof(PVOID)));
|
}
|
||||||
|
|
||||||
status =
|
if (!flag)
|
||||||
IsInstructionPointerInInvalidRegion(stack_frame, SystemModules, &flag);
|
{
|
||||||
|
status = ValidateIrpOutputBuffer(Irp, sizeof(NMI_CALLBACK_FAILURE));
|
||||||
|
|
||||||
if (!NT_SUCCESS(status))
|
if (!NT_SUCCESS(status))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR(
|
DEBUG_ERROR("ValidateIrpOutputBuffer failed with status %x",
|
||||||
"IsInstructionPointerInInvalidRegion failed with status %x",
|
status);
|
||||||
status);
|
return status;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flag == FALSE)
|
/*
|
||||||
{
|
* Note: for now, we only handle 1 report at a time so we stop the
|
||||||
status = ValidateIrpOutputBuffer(Irp, sizeof(NMI_CALLBACK_FAILURE));
|
* analysis once we receive a report since we only send a buffer
|
||||||
|
* large enough for 1 report. In the future this should be changed
|
||||||
|
* to a buffer that can hold atleast 4 reports (since the chance we
|
||||||
|
* get 4 reports with a single NMI would be impossible) so we can
|
||||||
|
* continue parsing the rest of the stack frames after receiving a
|
||||||
|
* single report.
|
||||||
|
*/
|
||||||
|
|
||||||
if (!NT_SUCCESS(status))
|
NMI_CALLBACK_FAILURE report = {0};
|
||||||
{
|
report.report_code = REPORT_NMI_CALLBACK_FAILURE;
|
||||||
DEBUG_ERROR("ValidateIrpOutputBuffer failed with status %x",
|
report.kthread_address = NmiContext[core].kthread;
|
||||||
status);
|
report.invalid_rip = NmiContext[core].interrupted_rip;
|
||||||
return status;
|
report.were_nmis_disabled = FALSE;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
Irp->IoStatus.Information = sizeof(NMI_CALLBACK_FAILURE);
|
||||||
* Note: for now, we only handle 1 report at a time so we stop the
|
|
||||||
* analysis once we receive a report since we only send a buffer
|
|
||||||
* large enough for 1 report. In the future this should be changed
|
|
||||||
* to a buffer that can hold atleast 4 reports (since the chance we
|
|
||||||
* get 4 reports with a single NMI would be impossible) so we can
|
|
||||||
* continue parsing the rest of the stack frames after receiving a
|
|
||||||
* single report.
|
|
||||||
*/
|
|
||||||
|
|
||||||
NMI_CALLBACK_FAILURE report = {0};
|
RtlCopyMemory(
|
||||||
report.report_code = REPORT_NMI_CALLBACK_FAILURE;
|
Irp->AssociatedIrp.SystemBuffer, &report, sizeof(NMI_CALLBACK_FAILURE));
|
||||||
report.kthread_address = thread_data->kthread_address;
|
|
||||||
report.invalid_rip = stack_frame;
|
|
||||||
report.were_nmis_disabled = FALSE;
|
|
||||||
|
|
||||||
Irp->IoStatus.Information = sizeof(NMI_CALLBACK_FAILURE);
|
return STATUS_SUCCESS;
|
||||||
|
|
||||||
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,
|
|
||||||
&report,
|
|
||||||
sizeof(NMI_CALLBACK_FAILURE));
|
|
||||||
|
|
||||||
return STATUS_SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -934,99 +907,67 @@ NmiCallback(_Inout_opt_ PVOID Context, _In_ BOOLEAN Handled)
|
||||||
{
|
{
|
||||||
UNREFERENCED_PARAMETER(Handled);
|
UNREFERENCED_PARAMETER(Handled);
|
||||||
|
|
||||||
PKTHREAD current_thread = KeGetCurrentThread();
|
PNMI_CONTEXT nmi_context = (PNMI_CONTEXT)Context;
|
||||||
NMI_CALLBACK_DATA thread_data = {0};
|
ULONG proc_num = KeGetCurrentProcessorNumber();
|
||||||
PNMI_CONTEXT nmi_context = (PNMI_CONTEXT)Context;
|
UINT64 kpcr = 0;
|
||||||
ULONG proc_num = KeGetCurrentProcessorNumber();
|
TASK_STATE_SEGMENT_64* tss = NULL;
|
||||||
|
PMACHINE_FRAME machine_frame = NULL;
|
||||||
if (!nmi_context)
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Cannot allocate pool in this function as it runs at IRQL >= dispatch level
|
* To find the IRETQ frame (MACHINE_FRAME) we need to find the top of the NMI ISR stack.
|
||||||
* so ive just allocated a global pool with size equal to 0x200 * num_procs
|
* This is stored at TSS->Ist[3]. To find the TSS, we can read it from KPCR->TSS_BASE. Once
|
||||||
|
* we have our TSS, we can read the value at TSS->Ist[3] which points to the top of the ISR
|
||||||
|
* stack, and subtract the size of the MACHINE_FRAME struct. Allowing us read the
|
||||||
|
* interrupted RIP.
|
||||||
|
*
|
||||||
|
* The reason this is needed is because RtlCaptureStackBackTrace is not safe to run
|
||||||
|
* at IRQL = HIGH_LEVEL, hence we need to manually unwind the ISR stack to find the
|
||||||
|
* interrupted rip.
|
||||||
*/
|
*/
|
||||||
INT num_frames_captured = RtlCaptureStackBackTrace(NULL,
|
kpcr = __readmsr(IA32_GS_BASE);
|
||||||
STACK_FRAME_POOL_SIZE / sizeof(UINT64),
|
tss = *(TASK_STATE_SEGMENT_64**)(kpcr + KPCR_TSS_BASE_OFFSET);
|
||||||
(uintptr_t)nmi_context->stack_frames +
|
machine_frame = tss->Ist3 - sizeof(MACHINE_FRAME);
|
||||||
proc_num * STACK_FRAME_POOL_SIZE,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
/*
|
if (machine_frame->rip <= WINDOWS_USERMODE_MAX_ADDRESS)
|
||||||
* This function is run in the context of the interrupted thread hence we can
|
nmi_context[proc_num].user_thread = TRUE;
|
||||||
* gather any and all information regarding the thread that may be useful for analysis
|
|
||||||
*/
|
|
||||||
thread_data.kthread_address = (UINT64)current_thread;
|
|
||||||
thread_data.kprocess_address = (UINT64)PsGetCurrentProcess();
|
|
||||||
thread_data.stack_base =
|
|
||||||
*((UINT64*)((uintptr_t)current_thread + KTHREAD_STACK_BASE_OFFSET));
|
|
||||||
thread_data.stack_limit =
|
|
||||||
*((UINT64*)((uintptr_t)current_thread + KTHREAD_STACK_LIMIT_OFFSET));
|
|
||||||
thread_data.start_address =
|
|
||||||
*((UINT64*)((uintptr_t)current_thread + KTHREAD_START_ADDRESS_OFFSET));
|
|
||||||
thread_data.cr3 = __readcr3();
|
|
||||||
thread_data.stack_frames_offset = proc_num * STACK_FRAME_POOL_SIZE;
|
|
||||||
thread_data.num_frames_captured = num_frames_captured;
|
|
||||||
|
|
||||||
RtlCopyMemory(((uintptr_t)nmi_context->thread_data_pool) + proc_num * sizeof(thread_data),
|
nmi_context[proc_num].interrupted_rip = machine_frame->rip;
|
||||||
&thread_data,
|
nmi_context[proc_num].interrupted_rsp = machine_frame->rsp;
|
||||||
sizeof(thread_data));
|
nmi_context[proc_num].kthread = PsGetCurrentThread();
|
||||||
|
nmi_context[proc_num].callback_count += 1;
|
||||||
|
|
||||||
PNMI_CORE_CONTEXT core_context =
|
DEBUG_VERBOSE(
|
||||||
(PNMI_CORE_CONTEXT)((uintptr_t)nmi_context->nmi_core_context +
|
"[NMI CALLBACK]: Core Number: %lx, Interrupted RIP: %llx, Interrupted RSP: %llx",
|
||||||
proc_num * sizeof(NMI_CORE_CONTEXT));
|
proc_num,
|
||||||
|
machine_frame->rip,
|
||||||
core_context->nmi_callbacks_run += 1;
|
machine_frame->rsp);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define NMI_DELAY_TIME 100 * 10000
|
#define NMI_DELAY_TIME 200 * 10000
|
||||||
|
|
||||||
STATIC
|
STATIC
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
LaunchNonMaskableInterrupt(_Inout_ PNMI_CONTEXT NmiContext)
|
LaunchNonMaskableInterrupt()
|
||||||
{
|
{
|
||||||
PAGED_CODE();
|
PAGED_CODE();
|
||||||
|
|
||||||
if (!NmiContext)
|
|
||||||
return STATUS_INVALID_PARAMETER;
|
|
||||||
|
|
||||||
PKAFFINITY_EX ProcAffinityPool =
|
PKAFFINITY_EX ProcAffinityPool =
|
||||||
ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(KAFFINITY_EX), PROC_AFFINITY_POOL);
|
ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(KAFFINITY_EX), PROC_AFFINITY_POOL);
|
||||||
|
|
||||||
if (!ProcAffinityPool)
|
if (!ProcAffinityPool)
|
||||||
return STATUS_MEMORY_NOT_ALLOCATED;
|
return STATUS_MEMORY_NOT_ALLOCATED;
|
||||||
|
|
||||||
NmiContext->stack_frames = ExAllocatePool2(
|
|
||||||
POOL_FLAG_NON_PAGED, NmiContext->core_count * STACK_FRAME_POOL_SIZE, STACK_FRAMES_POOL);
|
|
||||||
|
|
||||||
if (!NmiContext->stack_frames)
|
|
||||||
{
|
|
||||||
ExFreePoolWithTag(ProcAffinityPool, PROC_AFFINITY_POOL);
|
|
||||||
return STATUS_MEMORY_NOT_ALLOCATED;
|
|
||||||
}
|
|
||||||
|
|
||||||
NmiContext->thread_data_pool =
|
|
||||||
ExAllocatePool2(POOL_FLAG_NON_PAGED,
|
|
||||||
NmiContext->core_count * sizeof(NMI_CALLBACK_DATA),
|
|
||||||
THREAD_DATA_POOL);
|
|
||||||
|
|
||||||
if (!NmiContext->thread_data_pool)
|
|
||||||
{
|
|
||||||
ExFreePoolWithTag(NmiContext->stack_frames, STACK_FRAMES_POOL);
|
|
||||||
ExFreePoolWithTag(ProcAffinityPool, PROC_AFFINITY_POOL);
|
|
||||||
return STATUS_MEMORY_NOT_ALLOCATED;
|
|
||||||
}
|
|
||||||
|
|
||||||
LARGE_INTEGER delay = {0};
|
LARGE_INTEGER delay = {0};
|
||||||
delay.QuadPart -= NMI_DELAY_TIME;
|
delay.QuadPart -= NMI_DELAY_TIME;
|
||||||
|
|
||||||
for (ULONG core = 0; core < NmiContext->core_count; core++)
|
for (ULONG core = 0; core < KeQueryActiveProcessorCount(0); core++)
|
||||||
{
|
{
|
||||||
KeInitializeAffinityEx(ProcAffinityPool);
|
KeInitializeAffinityEx(ProcAffinityPool);
|
||||||
KeAddProcessorAffinityEx(ProcAffinityPool, core);
|
KeAddProcessorAffinityEx(ProcAffinityPool, core);
|
||||||
|
|
||||||
|
DEBUG_VERBOSE("Sending NMI");
|
||||||
HalSendNMI(ProcAffinityPool);
|
HalSendNMI(ProcAffinityPool);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1047,29 +988,30 @@ HandleNmiIOCTL(_Inout_ PIRP Irp)
|
||||||
PAGED_CODE();
|
PAGED_CODE();
|
||||||
|
|
||||||
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
||||||
SYSTEM_MODULES system_modules = {0};
|
|
||||||
NMI_CONTEXT nmi_context = {0};
|
|
||||||
PVOID callback_handle = NULL;
|
PVOID callback_handle = NULL;
|
||||||
|
SYSTEM_MODULES system_modules = {0};
|
||||||
|
PNMI_CONTEXT nmi_context = NULL;
|
||||||
|
|
||||||
nmi_context.core_count = KeQueryActiveProcessorCountEx(0);
|
|
||||||
nmi_context.nmi_core_context =
|
|
||||||
ExAllocatePool2(POOL_FLAG_NON_PAGED,
|
|
||||||
nmi_context.core_count * sizeof(NMI_CORE_CONTEXT),
|
|
||||||
NMI_CONTEXT_POOL);
|
|
||||||
|
|
||||||
if (!nmi_context.nmi_core_context)
|
|
||||||
|
|
||||||
|
nmi_context = ExAllocatePool2(POOL_FLAG_NON_PAGED,
|
||||||
|
KeQueryActiveProcessorCount(0) * sizeof(NMI_CONTEXT),
|
||||||
|
NMI_CONTEXT_POOL);
|
||||||
|
|
||||||
|
if (!nmi_context)
|
||||||
return STATUS_MEMORY_NOT_ALLOCATED;
|
return STATUS_MEMORY_NOT_ALLOCATED;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We want to register and unregister our callback each time so it becomes harder
|
* We want to register and unregister our callback each time so it becomes harder
|
||||||
* for people to hook our callback and get up to some funny business
|
* for people to hook our callback and get up to some funny business
|
||||||
*/
|
*/
|
||||||
callback_handle = KeRegisterNmiCallback(NmiCallback, &nmi_context);
|
callback_handle = KeRegisterNmiCallback(NmiCallback, nmi_context);
|
||||||
|
|
||||||
if (!callback_handle)
|
if (!callback_handle)
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("KeRegisterNmiCallback failed with no status.");
|
DEBUG_ERROR("KeRegisterNmiCallback failed with no status.");
|
||||||
ExFreePoolWithTag(nmi_context.nmi_core_context, NMI_CONTEXT_POOL);
|
ExFreePoolWithTag(nmi_context, NMI_CONTEXT_POOL);
|
||||||
return STATUS_UNSUCCESSFUL;
|
return STATUS_UNSUCCESSFUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1081,37 +1023,30 @@ HandleNmiIOCTL(_Inout_ PIRP Irp)
|
||||||
|
|
||||||
if (!NT_SUCCESS(status))
|
if (!NT_SUCCESS(status))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("GetSystemModuleInformation failed with status %x", status);
|
KeDeregisterNmiCallback(callback_handle);
|
||||||
|
ExFreePoolWithTag(nmi_context, NMI_CONTEXT_POOL);
|
||||||
|
DEBUG_ERROR("Error retriving system module information");
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
status = LaunchNonMaskableInterrupt(&nmi_context);
|
|
||||||
|
status = LaunchNonMaskableInterrupt();
|
||||||
|
|
||||||
if (!NT_SUCCESS(status))
|
if (!NT_SUCCESS(status))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("LaunchNonMaskableInterrupt failed with status %x", status);
|
DEBUG_ERROR("Error running NMI callbacks");
|
||||||
|
KeDeregisterNmiCallback(callback_handle);
|
||||||
if (system_modules.address)
|
ExFreePoolWithTag(system_modules.address, SYSTEM_MODULES_POOL);
|
||||||
ExFreePoolWithTag(system_modules.address, SYSTEM_MODULES_POOL);
|
ExFreePoolWithTag(nmi_context, NMI_CONTEXT_POOL);
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
status = AnalyseNmiData(&nmi_context, &system_modules, Irp);
|
|
||||||
|
status = AnalyseNmiData(nmi_context, &system_modules, Irp);
|
||||||
|
|
||||||
if (!NT_SUCCESS(status))
|
if (!NT_SUCCESS(status))
|
||||||
DEBUG_ERROR("AnalyseNmiData failed with status %x", status);
|
DEBUG_ERROR("Error analysing nmi data");
|
||||||
|
|
||||||
if (system_modules.address)
|
|
||||||
ExFreePoolWithTag(system_modules.address, SYSTEM_MODULES_POOL);
|
|
||||||
|
|
||||||
if (nmi_context.nmi_core_context)
|
|
||||||
ExFreePoolWithTag(nmi_context.nmi_core_context, NMI_CONTEXT_POOL);
|
|
||||||
|
|
||||||
if (nmi_context.stack_frames)
|
|
||||||
ExFreePoolWithTag(nmi_context.stack_frames, STACK_FRAMES_POOL);
|
|
||||||
|
|
||||||
if (nmi_context.thread_data_pool)
|
|
||||||
ExFreePoolWithTag(nmi_context.thread_data_pool, THREAD_DATA_POOL);
|
|
||||||
|
|
||||||
|
ExFreePoolWithTag(system_modules.address, SYSTEM_MODULES_POOL);
|
||||||
|
ExFreePoolWithTag(nmi_context, NMI_CONTEXT_POOL);
|
||||||
KeDeregisterNmiCallback(callback_handle);
|
KeDeregisterNmiCallback(callback_handle);
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
@ -1132,8 +1067,8 @@ ApcRundownRoutine(_In_ PRKAPC Apc)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The KernelRoutine is executed in kernel mode at APC_LEVEL before the APC is delivered. This
|
* The KernelRoutine is executed in kernel mode at APC_LEVEL before the APC is delivered.
|
||||||
* is also where we want to free our APC object.
|
* This is also where we want to free our APC object.
|
||||||
*/
|
*/
|
||||||
_IRQL_requires_max_(APC_LEVEL)
|
_IRQL_requires_max_(APC_LEVEL)
|
||||||
STATIC
|
STATIC
|
||||||
|
@ -1169,7 +1104,7 @@ ApcKernelRoutine(_In_ PRKAPC Apc,
|
||||||
frames_captured =
|
frames_captured =
|
||||||
RtlCaptureStackBackTrace(NULL, STACK_FRAME_POOL_SIZE / sizeof(UINT64), buffer, NULL);
|
RtlCaptureStackBackTrace(NULL, STACK_FRAME_POOL_SIZE / sizeof(UINT64), buffer, NULL);
|
||||||
|
|
||||||
if (frames_captured == NULL)
|
if (!frames_captured)
|
||||||
goto free;
|
goto free;
|
||||||
|
|
||||||
for (INT index = 0; index < frames_captured; index++)
|
for (INT index = 0; index < frames_captured; index++)
|
||||||
|
@ -1177,8 +1112,8 @@ ApcKernelRoutine(_In_ PRKAPC Apc,
|
||||||
stack_frame = *(UINT64*)((UINT64)buffer + index * sizeof(UINT64));
|
stack_frame = *(UINT64*)((UINT64)buffer + index * sizeof(UINT64));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Apc->NormalContext holds the address of our context data structure that we passed
|
* Apc->NormalContext holds the address of our context data structure that
|
||||||
* into KeInitializeApc as the last argument.
|
* we passed into KeInitializeApc as the last argument.
|
||||||
*/
|
*/
|
||||||
status = IsInstructionPointerInInvalidRegion(stack_frame, context->modules, &flag);
|
status = IsInstructionPointerInInvalidRegion(stack_frame, context->modules, &flag);
|
||||||
|
|
||||||
|
@ -1285,16 +1220,16 @@ ValidateThreadViaKernelApcCallback(_In_ PTHREAD_LIST_ENTRY ThreadListEntry,
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DEBUG_VERBOSE("Validating thread: %llx, process name: %s via kernel APC stackwalk.",
|
DEBUG_VERBOSE("Validating thread: %llx, process name: %s via kernel APC stackwalk.",
|
||||||
ThreadListEntry->thread,
|
ThreadListEntry->thread,
|
||||||
process_name);
|
process_name);
|
||||||
|
|
||||||
if (ThreadListEntry->thread == KeGetCurrentThread() || !ThreadListEntry->thread)
|
if (ThreadListEntry->thread == KeGetCurrentThread() || !ThreadListEntry->thread)
|
||||||
return;
|
return;
|
||||||
/*
|
/*
|
||||||
* Its possible to set the KThread->ApcQueueable flag to false ensuring that no APCs can be
|
* Its possible to set the KThread->ApcQueueable flag to false ensuring that no APCs
|
||||||
* queued to the thread, as KeInsertQueueApc will check this flag before queueing an APC so
|
* can be queued to the thread, as KeInsertQueueApc will check this flag before
|
||||||
* lets make sure we flip this before before queueing ours. Since we filter out any system
|
* queueing an APC so lets make sure we flip this before before queueing ours. Since
|
||||||
* threads this should be fine... c:
|
* we filter out any system threads this should be fine... c:
|
||||||
*/
|
*/
|
||||||
misc_flags = (PLONG)((UINT64)ThreadListEntry->thread + KTHREAD_MISC_FLAGS_OFFSET);
|
misc_flags = (PLONG)((UINT64)ThreadListEntry->thread + KTHREAD_MISC_FLAGS_OFFSET);
|
||||||
previous_mode = (PCHAR)((UINT64)ThreadListEntry->thread + KTHREAD_PREVIOUS_MODE_OFFSET);
|
previous_mode = (PCHAR)((UINT64)ThreadListEntry->thread + KTHREAD_PREVIOUS_MODE_OFFSET);
|
||||||
|
@ -1310,8 +1245,8 @@ ValidateThreadViaKernelApcCallback(_In_ PTHREAD_LIST_ENTRY ThreadListEntry,
|
||||||
ThreadListEntry->thread, KTHREAD_MISC_FLAGS_APC_QUEUEABLE, TRUE);
|
ThreadListEntry->thread, KTHREAD_MISC_FLAGS_APC_QUEUEABLE, TRUE);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* force thread into an alertable state, noting that this does not guarantee that our APC
|
* force thread into an alertable state, noting that this does not guarantee that
|
||||||
* will be run.
|
* our APC will be run.
|
||||||
*/
|
*/
|
||||||
if (*misc_flags >> KTHREAD_MISC_FLAGS_ALERTABLE == FALSE)
|
if (*misc_flags >> KTHREAD_MISC_FLAGS_ALERTABLE == FALSE)
|
||||||
FlipKThreadMiscFlagsFlag(
|
FlipKThreadMiscFlagsFlag(
|
||||||
|
@ -1348,9 +1283,9 @@ ValidateThreadViaKernelApcCallback(_In_ PTHREAD_LIST_ENTRY ThreadListEntry,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since NMIs are only executed on the thread that is running on each logical core, it makes
|
* Since NMIs are only executed on the thread that is running on each logical core, it makes
|
||||||
* sense to make use of APCs that, while can be masked off, provide us to easily issue a callback
|
* sense to make use of APCs that, while can be masked off, provide us to easily issue a
|
||||||
* routine to threads we want a stack trace of. Hence by utilising both APCs and NMIs we get
|
* callback routine to threads we want a stack trace of. Hence by utilising both APCs and
|
||||||
* excellent coverage of the entire system.
|
* NMIs we get excellent coverage of the entire system.
|
||||||
*/
|
*/
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
ValidateThreadsViaKernelApc()
|
ValidateThreadsViaKernelApc()
|
||||||
|
@ -1421,58 +1356,122 @@ FreeApcStackwalkApcContextInformation(_Inout_ PAPC_STACKWALK_CONTEXT Context)
|
||||||
ExFreePoolWithTag(Context->modules, POOL_TAG_APC);
|
ExFreePoolWithTag(Context->modules, POOL_TAG_APC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define DPC_STACKWALK_STACKFRAME_COUNT 10
|
||||||
|
#define DPC_STACKWALK_FRAMES_TO_SKIP 3
|
||||||
|
|
||||||
|
typedef struct _DPC_CONTEXT
|
||||||
|
{
|
||||||
|
UINT64 stack_frame[DPC_STACKWALK_STACKFRAME_COUNT];
|
||||||
|
UINT16 frames_captured;
|
||||||
|
volatile BOOLEAN executed;
|
||||||
|
|
||||||
|
} DPC_CONTEXT, *PDPC_CONTEXT;
|
||||||
|
|
||||||
|
_Function_class_(KDEFERRED_ROUTINE) _IRQL_requires_max_(DISPATCH_LEVEL)
|
||||||
|
_IRQL_requires_min_(DISPATCH_LEVEL)
|
||||||
|
_IRQL_requires_(DISPATCH_LEVEL)
|
||||||
|
_IRQL_requires_same_
|
||||||
|
VOID
|
||||||
|
DpcStackwalkCallbackRoutine(_In_ PKDPC Dpc,
|
||||||
|
_In_opt_ PVOID DeferredContext,
|
||||||
|
_In_opt_ PVOID SystemArgument1,
|
||||||
|
_In_opt_ PVOID SystemArgument2)
|
||||||
|
{
|
||||||
|
PDPC_CONTEXT context = &((PDPC_CONTEXT)DeferredContext)[KeGetCurrentProcessorNumber()];
|
||||||
|
|
||||||
|
context->frames_captured = RtlCaptureStackBackTrace(DPC_STACKWALK_FRAMES_TO_SKIP,
|
||||||
|
DPC_STACKWALK_STACKFRAME_COUNT,
|
||||||
|
&context->stack_frame,
|
||||||
|
NULL);
|
||||||
|
InterlockedExchange(&context->executed, TRUE);
|
||||||
|
KeSignalCallDpcDone(SystemArgument1);
|
||||||
|
|
||||||
|
DEBUG_VERBOSE("Executed DPC on core: %lx, with %lx frames captured.",
|
||||||
|
KeGetCurrentProcessorNumber(),
|
||||||
|
context->frames_captured);
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC
|
||||||
|
BOOLEAN
|
||||||
|
CheckForDpcCompletion(_In_ PDPC_CONTEXT Context)
|
||||||
|
{
|
||||||
|
for (UINT32 index = 0; index < KeQueryActiveProcessorCount(0); index++)
|
||||||
|
{
|
||||||
|
if (!InterlockedExchange(&Context[index].executed, Context[index].executed))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC
|
||||||
|
NTSTATUS
|
||||||
|
ValidateDpcCapturedStack(_In_ PSYSTEM_MODULES Modules, _In_ PDPC_CONTEXT Context)
|
||||||
|
{
|
||||||
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
||||||
|
BOOLEAN flag = FALSE;
|
||||||
|
PDPC_STACKWALK_REPORT report = NULL;
|
||||||
|
|
||||||
|
for (UINT32 core = 0; core < KeQueryActiveProcessorCount(0); core++)
|
||||||
|
{
|
||||||
|
for (UINT32 frame = 0; frame < Context[core].frames_captured; frame++)
|
||||||
|
{
|
||||||
|
status = IsInstructionPointerInInvalidRegion(
|
||||||
|
Context[core].stack_frame[frame], Modules, &flag);
|
||||||
|
|
||||||
|
if (!NT_SUCCESS(status))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR(
|
||||||
|
"IsInstructionPointerInInvalidRegion failed with status %x",
|
||||||
|
status);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!flag)
|
||||||
|
{
|
||||||
|
report = ExAllocatePool2(POOL_FLAG_NON_PAGED,
|
||||||
|
sizeof(DPC_STACKWALK_REPORT),
|
||||||
|
POOL_TAG_DPC);
|
||||||
|
|
||||||
|
if (!report)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
report->report_code = REPORT_DPC_STACKWALK;
|
||||||
|
report->kthread_address = PsGetCurrentThread();
|
||||||
|
report->invalid_rip = Context[core].stack_frame[frame];
|
||||||
|
|
||||||
|
RtlCopyMemory(report->driver,
|
||||||
|
(UINT64)Context[core].stack_frame[frame] - 0x500,
|
||||||
|
APC_STACKWALK_BUFFER_SIZE);
|
||||||
|
|
||||||
|
InsertReportToQueue(report);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since NMI evasion methods are becoming commonplace, we can use interprocess
|
* Lets use DPCs as another form of stackwalking rather then inter-process interrupts
|
||||||
* interrupts. For now i am just using the same nmi methods. To accomplish this
|
* because DPCs run at IRQL = DISPATCH_LEVEL, allowing us to use functions such as
|
||||||
* we can use KeIpiGenericCall, which runs a specified routine on all processors
|
* RtlCaptureStackBackTrace whereas IPIs run at IRQL = IPI_LEVEL. DPCs are also harder
|
||||||
* simultaneously. The callback routine runs at IRQL IPI_LEVEL which is > DIRQL.
|
* to mask compared to APCs which can be masked with the flip of a bit in the KTHREAD
|
||||||
|
* structure.
|
||||||
*/
|
*/
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
LaunchInterProcessInterrupt(_In_ PIRP Irp)
|
DispatchStackwalkToEachCpuViaDpc()
|
||||||
{
|
{
|
||||||
PAGED_CODE();
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
||||||
|
PDPC_CONTEXT context = NULL;
|
||||||
|
SYSTEM_MODULES modules = {0};
|
||||||
|
|
||||||
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
context = ExAllocatePool2(POOL_FLAG_NON_PAGED,
|
||||||
SYSTEM_MODULES system_modules = {0};
|
KeQueryActiveProcessorCount(0) * sizeof(DPC_CONTEXT),
|
||||||
NMI_CONTEXT ipi_context = {0};
|
POOL_TAG_DPC);
|
||||||
PVOID callback_handle = NULL;
|
|
||||||
|
|
||||||
DEBUG_VERBOSE("Launching Inter Process Interrupt");
|
if (!context)
|
||||||
|
|
||||||
ipi_context.core_count = KeQueryActiveProcessorCountEx(0);
|
|
||||||
ipi_context.nmi_core_context =
|
|
||||||
ExAllocatePool2(POOL_FLAG_NON_PAGED,
|
|
||||||
ipi_context.core_count * sizeof(NMI_CORE_CONTEXT),
|
|
||||||
NMI_CONTEXT_POOL);
|
|
||||||
|
|
||||||
if (!ipi_context.nmi_core_context)
|
|
||||||
return STATUS_MEMORY_NOT_ALLOCATED;
|
return STATUS_MEMORY_NOT_ALLOCATED;
|
||||||
|
|
||||||
ipi_context.stack_frames = ExAllocatePool2(
|
status = GetSystemModuleInformation(&modules);
|
||||||
POOL_FLAG_NON_PAGED, ipi_context.core_count * STACK_FRAME_POOL_SIZE, STACK_FRAMES_POOL);
|
|
||||||
|
|
||||||
if (!ipi_context.stack_frames)
|
|
||||||
{
|
|
||||||
status = STATUS_MEMORY_NOT_ALLOCATED;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
ipi_context.thread_data_pool =
|
|
||||||
ExAllocatePool2(POOL_FLAG_NON_PAGED,
|
|
||||||
ipi_context.core_count * sizeof(NMI_CALLBACK_DATA),
|
|
||||||
THREAD_DATA_POOL);
|
|
||||||
|
|
||||||
if (!ipi_context.thread_data_pool)
|
|
||||||
{
|
|
||||||
status = STATUS_MEMORY_NOT_ALLOCATED;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We query the system modules each time since they can potentially
|
|
||||||
* change at any time
|
|
||||||
*/
|
|
||||||
status = GetSystemModuleInformation(&system_modules);
|
|
||||||
|
|
||||||
if (!NT_SUCCESS(status))
|
if (!NT_SUCCESS(status))
|
||||||
{
|
{
|
||||||
|
@ -1480,29 +1479,29 @@ LaunchInterProcessInterrupt(_In_ PIRP Irp)
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
KeIpiGenericCall(NmiCallback, &ipi_context);
|
/* KeGenericCallDpc will queue a DPC to each processor with importance =
|
||||||
|
* HighImportance. This means our DPC will be inserted into the front of the DPC
|
||||||
|
* queue and executed immediately.*/
|
||||||
|
KeGenericCallDpc(DpcStackwalkCallbackRoutine, context);
|
||||||
|
|
||||||
/*
|
while (!CheckForDpcCompletion(context))
|
||||||
* since the routines are run simultaneously, once we've reached here we can be sure
|
YieldProcessor();
|
||||||
* all routines have run.
|
|
||||||
*/
|
status = ValidateDpcCapturedStack(&modules, context);
|
||||||
status = AnalyseNmiData(&ipi_context, &system_modules, Irp);
|
|
||||||
|
|
||||||
if (!NT_SUCCESS(status))
|
if (!NT_SUCCESS(status))
|
||||||
DEBUG_ERROR("AnalyseNmiData failed with status %x", status);
|
{
|
||||||
|
DEBUG_ERROR("ValidateDpcCapturedStack failed with status %x", status);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_VERBOSE("Finished validating cores via dpc");
|
||||||
end:
|
end:
|
||||||
|
|
||||||
if (system_modules.address)
|
if (modules.address)
|
||||||
ExFreePoolWithTag(system_modules.address, SYSTEM_MODULES_POOL);
|
ExFreePoolWithTag(modules.address, SYSTEM_MODULES_POOL);
|
||||||
|
if (context)
|
||||||
if (ipi_context.nmi_core_context)
|
ExFreePoolWithTag(context, POOL_TAG_DPC);
|
||||||
ExFreePoolWithTag(ipi_context.nmi_core_context, NMI_CONTEXT_POOL);
|
|
||||||
|
|
||||||
if (ipi_context.stack_frames)
|
|
||||||
ExFreePoolWithTag(ipi_context.stack_frames, STACK_FRAMES_POOL);
|
|
||||||
|
|
||||||
if (ipi_context.thread_data_pool)
|
|
||||||
ExFreePoolWithTag(ipi_context.thread_data_pool, THREAD_DATA_POOL);
|
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
|
@ -16,6 +16,17 @@ typedef struct NMI_CALLBACK_FAILURE
|
||||||
|
|
||||||
} NMI_CALLBACK_FAILURE, *PNMI_CALLBACK_FAILURE;
|
} NMI_CALLBACK_FAILURE, *PNMI_CALLBACK_FAILURE;
|
||||||
|
|
||||||
|
#define APC_STACKWALK_BUFFER_SIZE 4096
|
||||||
|
|
||||||
|
typedef struct _DPC_STACKWALK_REPORT
|
||||||
|
{
|
||||||
|
UINT32 report_code;
|
||||||
|
UINT64 kthread_address;
|
||||||
|
UINT64 invalid_rip;
|
||||||
|
CHAR driver[APC_STACKWALK_BUFFER_SIZE];
|
||||||
|
|
||||||
|
} DPC_STACKWALK_REPORT, *PDPC_STACKWALK_REPORT;
|
||||||
|
|
||||||
typedef struct _MODULE_VALIDATION_FAILURE
|
typedef struct _MODULE_VALIDATION_FAILURE
|
||||||
{
|
{
|
||||||
INT report_code;
|
INT report_code;
|
||||||
|
@ -26,8 +37,6 @@ typedef struct _MODULE_VALIDATION_FAILURE
|
||||||
|
|
||||||
} MODULE_VALIDATION_FAILURE, *PMODULE_VALIDATION_FAILURE;
|
} MODULE_VALIDATION_FAILURE, *PMODULE_VALIDATION_FAILURE;
|
||||||
|
|
||||||
#define APC_STACKWALK_BUFFER_SIZE 4096
|
|
||||||
|
|
||||||
typedef struct _APC_STACKWALK_REPORT
|
typedef struct _APC_STACKWALK_REPORT
|
||||||
{
|
{
|
||||||
INT report_code;
|
INT report_code;
|
||||||
|
@ -99,6 +108,6 @@ BOOLEAN
|
||||||
FlipKThreadMiscFlagsFlag(_In_ PKTHREAD Thread, _In_ ULONG FlagIndex, _In_ BOOLEAN NewValue);
|
FlipKThreadMiscFlagsFlag(_In_ PKTHREAD Thread, _In_ ULONG FlagIndex, _In_ BOOLEAN NewValue);
|
||||||
|
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
LaunchInterProcessInterrupt(_In_ PIRP Irp);
|
DispatchStackwalkToEachCpuViaDpc();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "callbacks.h"
|
#include "callbacks.h"
|
||||||
#include "queue.h"
|
#include "queue.h"
|
||||||
|
#include "ia32.h"
|
||||||
|
|
||||||
#define PAGE_BASE_SIZE 0x1000
|
#define PAGE_BASE_SIZE 0x1000
|
||||||
#define POOL_TAG_SIZE 0x004
|
#define POOL_TAG_SIZE 0x004
|
||||||
|
@ -444,9 +445,9 @@ WalkKernelPageTables(_In_ PPROCESS_SCAN_CONTEXT Context)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cr3.BitAddress = __readcr3();
|
cr3.AsUInt = __readcr3();
|
||||||
|
|
||||||
physical.QuadPart = cr3.Bits.PhysicalAddress << PAGE_4KB_SHIFT;
|
physical.QuadPart = cr3.AddressOfPageDirectory << PAGE_4KB_SHIFT;
|
||||||
|
|
||||||
pml4_base.BitAddress = MmGetVirtualForPhysical(physical);
|
pml4_base.BitAddress = MmGetVirtualForPhysical(physical);
|
||||||
|
|
||||||
|
|
|
@ -267,6 +267,16 @@ HandlePeriodicGlobalReportQueueQuery(_Inout_ PIRP Irp)
|
||||||
|
|
||||||
total_size += sizeof(HIDDEN_SYSTEM_THREAD_REPORT);
|
total_size += sizeof(HIDDEN_SYSTEM_THREAD_REPORT);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case REPORT_DPC_STACKWALK:
|
||||||
|
|
||||||
|
RtlCopyMemory((UINT64)report_buffer + sizeof(GLOBAL_REPORT_QUEUE_HEADER) +
|
||||||
|
total_size,
|
||||||
|
report,
|
||||||
|
sizeof(DPC_STACKWALK_REPORT));
|
||||||
|
|
||||||
|
total_size += sizeof(DPC_STACKWALK_REPORT);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* QueuePop frees the node, but we still need to free the returned data */
|
/* QueuePop frees the node, but we still need to free the returned data */
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
<Configuration>Debug</Configuration>
|
<Configuration>Debug</Configuration>
|
||||||
<Platform Condition="'$(Platform)' == ''">x64</Platform>
|
<Platform Condition="'$(Platform)' == ''">x64</Platform>
|
||||||
<RootNamespace>testdrv</RootNamespace>
|
<RootNamespace>testdrv</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>$(LatestTargetPlatformVersion)</WindowsTargetPlatformVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
@ -104,6 +105,7 @@
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release - No Server|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release - No Server|x64'">
|
||||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||||
<Inf2CatUseLocalTime>true</Inf2CatUseLocalTime>
|
<Inf2CatUseLocalTime>true</Inf2CatUseLocalTime>
|
||||||
|
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#define REPORT_HIDDEN_SYSTEM_THREAD 90
|
#define REPORT_HIDDEN_SYSTEM_THREAD 90
|
||||||
#define REPORT_ILLEGAL_ATTACH_PROCESS 100
|
#define REPORT_ILLEGAL_ATTACH_PROCESS 100
|
||||||
#define REPORT_APC_STACKWALK 110
|
#define REPORT_APC_STACKWALK 110
|
||||||
|
#define REPORT_DPC_STACKWALK 120
|
||||||
|
|
||||||
#define TEST_STEAM_64_ID 123456789;
|
#define TEST_STEAM_64_ID 123456789;
|
||||||
|
|
||||||
|
@ -205,6 +206,14 @@ struct APC_STACKWALK_REPORT
|
||||||
UINT64 invalid_rip;
|
UINT64 invalid_rip;
|
||||||
CHAR driver[4096];
|
CHAR driver[4096];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DPC_STACKWALK_REPORT
|
||||||
|
{
|
||||||
|
UINT32 report_code;
|
||||||
|
UINT64 kthread_address;
|
||||||
|
UINT64 invalid_rip;
|
||||||
|
CHAR driver[4096];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -240,6 +240,10 @@ kernelmode::Driver::QueryReportQueue()
|
||||||
ReportTypeFromReportQueue<HIDDEN_SYSTEM_THREAD_REPORT>(
|
ReportTypeFromReportQueue<HIDDEN_SYSTEM_THREAD_REPORT>(
|
||||||
buffer, &total_size, &hidden_report);
|
buffer, &total_size, &hidden_report);
|
||||||
break;
|
break;
|
||||||
|
case REPORT_DPC_STACKWALK:
|
||||||
|
ReportTypeFromReportQueue<DPC_STACKWALK_REPORT>(
|
||||||
|
buffer, &total_size, &hidden_report);
|
||||||
|
break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -521,15 +525,15 @@ kernelmode::Driver::CheckForEptHooks()
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
kernelmode::Driver::LaunchIpiInterrupt()
|
kernelmode::Driver::StackwalkThreadsViaDpc()
|
||||||
{
|
{
|
||||||
BOOLEAN status = FALSE;
|
BOOLEAN status = FALSE;
|
||||||
|
|
||||||
status = DeviceIoControl(
|
status = DeviceIoControl(
|
||||||
this->driver_handle, IOCTL_LAUNCH_IPI_INTERRUPT, NULL, NULL, NULL, NULL, NULL, NULL);
|
this->driver_handle, IOCTL_LAUNCH_DPC_STACKWALK, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
|
||||||
if (status == NULL)
|
if (status == NULL)
|
||||||
LOG_ERROR("failed to launch ipi interrupt %x", GetLastError());
|
LOG_ERROR("failed to stackwalk threads via dpc %x", GetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20017, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20017, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
#define IOCTL_CHECK_FOR_EPT_HOOK \
|
#define IOCTL_CHECK_FOR_EPT_HOOK \
|
||||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20018, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20018, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
#define IOCTL_LAUNCH_IPI_INTERRUPT \
|
#define IOCTL_LAUNCH_DPC_STACKWALK \
|
||||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20019, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20019, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
#define IOCTL_VALIDATE_SYSTEM_MODULES \
|
#define IOCTL_VALIDATE_SYSTEM_MODULES \
|
||||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20020, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20020, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
@ -97,7 +97,7 @@ class Driver
|
||||||
VOID SendClientHardwareInformation();
|
VOID SendClientHardwareInformation();
|
||||||
VOID CheckForHiddenThreads();
|
VOID CheckForHiddenThreads();
|
||||||
VOID CheckForEptHooks();
|
VOID CheckForEptHooks();
|
||||||
VOID LaunchIpiInterrupt();
|
VOID StackwalkThreadsViaDpc();
|
||||||
VOID ValidateSystemModules();
|
VOID ValidateSystemModules();
|
||||||
BOOLEAN InitiateApcOperation(INT OperationId);
|
BOOLEAN InitiateApcOperation(INT OperationId);
|
||||||
};
|
};
|
||||||
|
|
|
@ -100,9 +100,9 @@ kernelmode::KManager::CheckForEptHooks()
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
kernelmode::KManager::LaunchIpiInterrupt()
|
kernelmode::KManager::StackwalkThreadsViaDpc()
|
||||||
{
|
{
|
||||||
this->thread_pool->QueueJob([this]() { this->driver_interface->LaunchIpiInterrupt(); });
|
this->thread_pool->QueueJob([this]() { this->driver_interface->StackwalkThreadsViaDpc(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
|
|
|
@ -33,7 +33,7 @@ class KManager
|
||||||
VOID InitiateApcStackwalkOperation();
|
VOID InitiateApcStackwalkOperation();
|
||||||
VOID CheckForHiddenThreads();
|
VOID CheckForHiddenThreads();
|
||||||
VOID CheckForEptHooks();
|
VOID CheckForEptHooks();
|
||||||
VOID LaunchIpiInterrupt();
|
VOID StackwalkThreadsViaDpc();
|
||||||
VOID ValidateSystemModules();
|
VOID ValidateSystemModules();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,7 +94,7 @@ Init(HINSTANCE hinstDLL)
|
||||||
case 7: kmanager.InitiateApcStackwalkOperation(); break;
|
case 7: kmanager.InitiateApcStackwalkOperation(); break;
|
||||||
case 8: kmanager.CheckForHiddenThreads(); break;
|
case 8: kmanager.CheckForHiddenThreads(); break;
|
||||||
case 9: kmanager.CheckForEptHooks(); break;
|
case 9: kmanager.CheckForEptHooks(); break;
|
||||||
case 10: kmanager.LaunchIpiInterrupt(); break;
|
case 10: kmanager.StackwalkThreadsViaDpc(); break;
|
||||||
case 11: kmanager.ValidateSystemModules(); break;
|
case 11: kmanager.ValidateSystemModules(); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue