mirror of
https://github.com/eversinc33/Banshee
synced 2026-06-08 14:08:04 +00:00
311 lines
7.3 KiB
C++
311 lines
7.3 KiB
C++
#include "Misc.hpp"
|
|
#include "Debug.hpp"
|
|
#include "Globals.hpp"
|
|
|
|
/*
|
|
* @brief Checks whether a wide string is null or contains only null characters.
|
|
*
|
|
* @param[in] PWchar Pointer to a wide string.
|
|
*
|
|
* @return BOOLEAN True if null or only null characters, otherwise false.
|
|
*/
|
|
BOOLEAN
|
|
BeIsStringNull(
|
|
_In_ PWCHAR pWchar
|
|
)
|
|
{
|
|
return (pWchar == NULL || *pWchar == '\0');
|
|
}
|
|
|
|
/*
|
|
* @brief Checks whether a wide string is null-terminated.
|
|
*
|
|
* @param[in] Array Pointer to a wide string.
|
|
* @param[in] ArrayLength Length of the string.
|
|
*
|
|
* @return BOOLEAN True if null-terminated, otherwise false.
|
|
*/
|
|
BOOLEAN
|
|
BeIsStringTerminated(
|
|
_In_ PWCHAR array,
|
|
_In_ ULONG arrayLength
|
|
)
|
|
{
|
|
BOOLEAN bStringIsTerminated = FALSE;
|
|
USHORT index = 0;
|
|
|
|
while (index < arrayLength && bStringIsTerminated == FALSE)
|
|
{
|
|
if (array[index] == L'\0')
|
|
{
|
|
bStringIsTerminated = TRUE;
|
|
}
|
|
else
|
|
{
|
|
index++;
|
|
}
|
|
}
|
|
return bStringIsTerminated;
|
|
}
|
|
|
|
/*
|
|
* @brief Checks whether a wide string is null-terminated, not empty, and properly aligned.
|
|
*
|
|
* @param[in] TargetString The string to check.
|
|
* @param[in] Size The size of the string buffer.
|
|
*
|
|
* @return NTSTATUS STATUS_SUCCESS if valid, otherwise an error code.
|
|
*/
|
|
NTSTATUS
|
|
BeCheckStringIsAlignedNotEmptyAndTerminated(
|
|
_In_ PWCHAR targetString,
|
|
_In_ ULONG size
|
|
)
|
|
{
|
|
//
|
|
// Check alignment
|
|
//
|
|
if (size % sizeof(WCHAR) != 0)
|
|
{
|
|
LOG_MSG("Invalid alignment \r\n");
|
|
return STATUS_INVALID_BUFFER_SIZE;
|
|
}
|
|
|
|
if (!targetString)
|
|
{
|
|
LOG_MSG("Empty buffer \r\n");
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
LOG_MSG("String received: %ws \r\n", targetString);
|
|
|
|
if (BeIsStringTerminated(targetString, size) == FALSE)
|
|
{
|
|
LOG_MSG("Not null terminated! \r\n");
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @brief Performs a case-insensitive substring search in a wide string.
|
|
*
|
|
* @param[in] String The string to search in.
|
|
* @param[in] Pattern The pattern to search for.
|
|
*
|
|
* @return PWCHAR Pointer to the match if found, otherwise NULL.
|
|
*/
|
|
PWCHAR
|
|
StrStrIW(
|
|
_In_ CONST PWCHAR string,
|
|
_In_ CONST PWCHAR pattern
|
|
) {
|
|
PWCHAR pptr, sptr, start;
|
|
|
|
for (start = (PWCHAR)string; *start != NULL; ++start)
|
|
{
|
|
while (((*start != NULL) && (RtlUpcaseUnicodeChar(*start) != RtlUpcaseUnicodeChar(*pattern))))
|
|
{
|
|
++start;
|
|
}
|
|
|
|
if (NULL == *start)
|
|
return NULL;
|
|
|
|
pptr = (PWCHAR)pattern;
|
|
sptr = (PWCHAR)start;
|
|
|
|
while (RtlUpcaseUnicodeChar(*sptr) == RtlUpcaseUnicodeChar(*sptr))
|
|
{
|
|
sptr++;
|
|
pptr++;
|
|
|
|
if (NULL == *pptr)
|
|
return (start);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* @brief Extracts the base name from a full file path.
|
|
* Taken from: https://github.com/GetRektBoy724/DCMB/blob/main/DCMB/dcmb.c#L3
|
|
*
|
|
* @param[in] FullName Full file path.
|
|
*
|
|
* @return PCHAR Pointer to the base name within the string.
|
|
*/
|
|
PCHAR
|
|
GetBaseNameFromFullPath(
|
|
_In_ PCHAR fullName
|
|
)
|
|
{
|
|
SIZE_T fullNameLength = strlen(fullName);
|
|
|
|
for (SIZE_T I = fullNameLength; I > 0; I--)
|
|
{
|
|
if (*(fullName + I) == '\\')
|
|
{
|
|
return fullName + I + 1;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* @brief Creates a security descriptor with full access to all users.
|
|
*
|
|
* @param[out] Sd Pointer to the created security descriptor.
|
|
*
|
|
* @return NTSTATUS STATUS_SUCCESS if successful, otherwise an error code.
|
|
*/
|
|
NTSTATUS
|
|
BeCreateSecurityDescriptor(
|
|
_Out_ PSECURITY_DESCRIPTOR* sd
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
// Create the DACL
|
|
/*
|
|
* Figured out that since we want full access to anyone, we can also just add a 0 ACL ... :)
|
|
*/
|
|
/*
|
|
ULONG daclSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG) + RtlLengthSid(SeExports->SeAuthenticatedUsersSid);
|
|
PACL Dacl = (PACL)ExAllocatePoolWithTag(NonPagedPool, daclSize, DRIVER_TAG);
|
|
if (!Dacl)
|
|
{
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
|
|
NtStatus = RtlCreateAcl(Dacl, daclSize, ACL_REVISION);
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{
|
|
ExFreePool(Dacl);
|
|
return NtStatus;
|
|
}
|
|
|
|
NtStatus = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, SECTION_ALL_ACCESS, SeExports->SeAuthenticatedUsersSid);
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{
|
|
ExFreePool(Dacl);
|
|
return NtStatus;
|
|
}
|
|
*/
|
|
|
|
// Create the security descriptor
|
|
*sd = (PSECURITY_DESCRIPTOR)ExAllocatePoolWithTag(NonPagedPool, SECURITY_DESCRIPTOR_MIN_LENGTH, DRIVER_TAG); // TODO: FREE
|
|
if (!*sd)
|
|
{
|
|
ExFreePool(*sd);
|
|
// ExFreePool(Dacl);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
status = RtlCreateSecurityDescriptor(*sd, SECURITY_DESCRIPTOR_REVISION);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
ExFreePool(sd);
|
|
// ExFreePool(Dacl);
|
|
return status;
|
|
}
|
|
|
|
status = RtlSetDaclSecurityDescriptor(*sd, TRUE, 0, FALSE); // 0 = Dacl
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
LOG_MSG("Failed to set DACL in security descriptor\n");
|
|
ExFreePool(*sd);
|
|
// ExFreePool(Dacl);
|
|
return status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* @brief Sets a named event to a specified state.
|
|
*
|
|
* @param[in] hEvent Handle to the event.
|
|
* @param[in] Set TRUE to signal the event, FALSE to reset it.
|
|
*
|
|
* @return NTSTATUS STATUS_SUCCESS if successful, otherwise an error code.
|
|
*/
|
|
NTSTATUS
|
|
BeSetNamedEvent(
|
|
_In_ HANDLE hEvent,
|
|
_In_ BOOLEAN set
|
|
) {
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
if (set)
|
|
{
|
|
status = BeGlobals::pZwSetEvent(hEvent, NULL);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
LOG_MSG("Failed to set named event: 0x%X\n", status);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BeGlobals::pZwResetEvent(hEvent, NULL);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
LOG_MSG("Failed to reset named event: 0x%X\n", status);
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* @brief Waits for an event to be signaled.
|
|
*
|
|
* @param[in] hEvent Handle to the event.
|
|
*
|
|
* @return NTSTATUS STATUS_SUCCESS if successful, otherwise an error code.
|
|
*/
|
|
NTSTATUS
|
|
BeWaitForEvent(
|
|
_In_ HANDLE hEvent
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
Status = ZwWaitForSingleObject(hEvent, FALSE, NULL);
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* @brief Creates a named event with a specified initial state.
|
|
*
|
|
* @param[out] PhEvent Pointer to a handle for the created event.
|
|
* @param[in] EventName Name of the event.
|
|
* @param[in] InitialSignaledState Initial state of the event (signaled or non-signaled).
|
|
*
|
|
* @return NTSTATUS STATUS_SUCCESS if successful, otherwise an error code.
|
|
*/
|
|
NTSTATUS
|
|
BeCreateNamedEvent(
|
|
_Out_ PHANDLE phEvent,
|
|
_In_ PUNICODE_STRING eventName,
|
|
_In_ BOOLEAN initialSignaledState
|
|
) {
|
|
//
|
|
// Add permissions to all users to our event, so that a lowpriv agent can still access the rootkit
|
|
//
|
|
PSECURITY_DESCRIPTOR sd = { 0 };
|
|
OBJECT_ATTRIBUTES oa = { 0 };
|
|
|
|
BeCreateSecurityDescriptor(&sd);
|
|
InitializeObjectAttributes(&oa, eventName, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT | OBJ_KERNEL_HANDLE | OBJ_OPENIF, NULL, sd);
|
|
|
|
NTSTATUS Status = BeGlobals::pZwCreateEvent(phEvent, EVENT_ALL_ACCESS, &oa, NotificationEvent, initialSignaledState);
|
|
if (!NT_SUCCESS(Status))
|
|
DbgPrint("Failed to create named event: 0x%X\n", Status);
|
|
|
|
ExFreePool(sd);
|
|
return Status;
|
|
} |