#include "Utils.h" #include "Private.h" #include #pragma alloc_text(PAGE, BBSafeAllocateString) #pragma alloc_text(PAGE, BBSafeInitString) #pragma alloc_text(PAGE, BBStripPath) #pragma alloc_text(PAGE, BBStripFilename) #pragma alloc_text(PAGE, BBFileExists) /// /// Allocate new Unicode string from Paged pool /// /// Resulting string /// Buffer size in bytes to alloacate /// Status code NTSTATUS BBSafeAllocateString( OUT PUNICODE_STRING result, IN USHORT size ) { ASSERT( result != NULL ); if (result == NULL || size == 0) return STATUS_INVALID_PARAMETER; result->Buffer = ExAllocatePoolWithTag( PagedPool, size, BB_POOL_TAG ); result->Length = 0; result->MaximumLength = size; if (result->Buffer) RtlZeroMemory( result->Buffer, size ); else return STATUS_NO_MEMORY; return STATUS_SUCCESS; } /// /// Allocate and copy string /// /// Resulting string /// Source string /// Status code NTSTATUS BBSafeInitString( OUT PUNICODE_STRING result, IN PUNICODE_STRING source ) { ASSERT( result != NULL && source != NULL ); if (result == NULL || source == NULL || source->Buffer == NULL) return STATUS_INVALID_PARAMETER; // No data to copy if (source->Length == 0) { result->Length = result->MaximumLength = 0; result->Buffer = NULL; return STATUS_SUCCESS; } result->Buffer = ExAllocatePoolWithTag( PagedPool, source->MaximumLength, BB_POOL_TAG ); result->Length = source->Length; result->MaximumLength = source->MaximumLength; memcpy( result->Buffer, source->Buffer, source->Length ); return STATUS_SUCCESS; } /// /// Search for substring /// /// Source string /// Target string /// Case insensitive search /// Found position or -1 if not found LONG BBSafeSearchString( IN PUNICODE_STRING source, IN PUNICODE_STRING target, IN BOOLEAN CaseInSensitive ) { ASSERT( source != NULL && target != NULL ); if (source == NULL || target == NULL || source->Buffer == NULL || target->Buffer == NULL) return STATUS_INVALID_PARAMETER; // Size mismatch if (source->Length < target->Length) return -1; USHORT diff = source->Length - target->Length; for (USHORT i = 0; i <= (diff / sizeof( WCHAR )); i++) { if (RtlCompareUnicodeStrings( source->Buffer + i, target->Length / sizeof( WCHAR ), target->Buffer, target->Length / sizeof( WCHAR ), CaseInSensitive ) == 0) { return i; } } return -1; } /// /// Get file name from full path /// /// Path. /// Resulting name /// Status code NTSTATUS BBStripPath( IN PUNICODE_STRING path, OUT PUNICODE_STRING name ) { ASSERT( path != NULL && name ); if (path == NULL || name == NULL) return STATUS_INVALID_PARAMETER; // Empty string if (path->Length < 2) { *name = *path; return STATUS_NOT_FOUND; } for (USHORT i = (path->Length / sizeof( WCHAR )) - 1; i != 0; i--) { if (path->Buffer[i] == L'\\' || path->Buffer[i] == L'/') { name->Buffer = &path->Buffer[i + 1]; name->Length = name->MaximumLength = path->Length - (i + 1)*sizeof( WCHAR ); return STATUS_SUCCESS; } } *name = *path; return STATUS_NOT_FOUND; } /// /// Get directory path name from full path /// /// Path /// Resulting directory path /// Status code NTSTATUS BBStripFilename( IN PUNICODE_STRING path, OUT PUNICODE_STRING dir ) { ASSERT( path != NULL && dir ); if (path == NULL || dir == NULL) return STATUS_INVALID_PARAMETER; // Empty string if (path->Length < 2) { *dir = *path; return STATUS_NOT_FOUND; } for (USHORT i = (path->Length / sizeof( WCHAR )) - 1; i != 0; i--) { if (path->Buffer[i] == L'\\' || path->Buffer[i] == L'/') { dir->Buffer = path->Buffer; dir->Length = dir->MaximumLength = i*sizeof( WCHAR ); return STATUS_SUCCESS; } } *dir = *path; return STATUS_NOT_FOUND; } /// /// Check if file exists /// /// Fully qualifid path to a file /// Status code NTSTATUS BBFileExists( IN PUNICODE_STRING path ) { HANDLE hFile = NULL; IO_STATUS_BLOCK statusBlock = { 0 }; OBJECT_ATTRIBUTES obAttr = { 0 }; InitializeObjectAttributes( &obAttr, path, OBJ_KERNEL_HANDLE, NULL, NULL ); NTSTATUS status = ZwCreateFile( &hFile, FILE_READ_DATA | SYNCHRONIZE, &obAttr, &statusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); if (NT_SUCCESS( status )) ZwClose( hFile ); return status; } /// /// Search for pattern /// /// Pattern to search for /// Used wildcard /// Pattern length /// Base address for searching /// Address range to search in /// Found location /// Status code NTSTATUS BBSearchPattern( IN PCUCHAR pattern, IN UCHAR wildcard, IN ULONG_PTR len, IN const VOID* base, IN ULONG_PTR size, OUT PVOID* ppFound ) { ASSERT( ppFound != NULL && pattern != NULL && base != NULL ); if (ppFound == NULL || pattern == NULL || base == NULL) return STATUS_INVALID_PARAMETER; for (ULONG_PTR i = 0; i < size - len; i++) { BOOLEAN found = TRUE; for (ULONG_PTR j = 0; j < len; j++) { if (pattern[j] != wildcard && pattern[j] != ((PCUCHAR)base)[i + j]) { found = FALSE; break; } } if (found != FALSE) { *ppFound = (PUCHAR)base + i; return STATUS_SUCCESS; } } return STATUS_NOT_FOUND; } /// /// Check if process is terminating /// /// Process /// If TRUE - terminating BOOLEAN BBCheckProcessTermination( PEPROCESS pProcess ) { LARGE_INTEGER zeroTime = { 0 }; return KeWaitForSingleObject( pProcess, Executive, KernelMode, FALSE, &zeroTime ) == STATUS_WAIT_0; } ULONG GenPrologue32( IN PUCHAR pBuf ) { *pBuf = 0x55; *(PUSHORT)(pBuf + 1) = 0xE589; return 3; } ULONG GenEpilogue32( IN PUCHAR pBuf, IN INT retSize ) { *(PUSHORT)pBuf = 0xEC89; *(pBuf + 2) = 0x5D; *(pBuf + 3) = 0xC2; *(PUSHORT)(pBuf + 4) = (USHORT)retSize; return 6; } ULONG GenCall32( IN PUCHAR pBuf, IN PVOID pFn, IN INT argc, ... ) { va_list vl; va_start( vl, argc ); ULONG res = GenCall32V( pBuf, pFn, argc, vl ); va_end( vl ); return res; } ULONG GenCall32V( IN PUCHAR pBuf, IN PVOID pFn, IN INT argc, IN va_list vl ) { ULONG ofst = 0; PULONG pArgBuf = ExAllocatePoolWithTag( PagedPool, argc * sizeof( ULONG ), BB_POOL_TAG ); // cast args for (INT i = 0; i < argc; i++) { PVOID arg = va_arg( vl, PVOID ); pArgBuf[i] = (ULONG)(ULONG_PTR)arg; } // push args for (INT i = argc - 1; i >= 0; i--) { *(PUSHORT)(pBuf + ofst) = 0x68; // push arg *(PULONG)(pBuf + ofst + 1) = pArgBuf[i]; // ofst += 5; } *(PUCHAR)(pBuf + ofst) = 0xB8; // mov eax, pFn *(PULONG)(pBuf + ofst + 1) = (ULONG)(ULONG_PTR)pFn; // ofst += 5; *(PUSHORT)(pBuf + ofst) = 0xD0FF; // call eax ofst += 2; ExFreePoolWithTag( pArgBuf, BB_POOL_TAG ); return ofst; } ULONG GenSync32( IN PUCHAR pBuf, IN PNTSTATUS pStatus, IN PVOID pSetEvent, IN HANDLE hEvent ) { ULONG ofst = 0; *(PUCHAR)(pBuf + ofst) = 0xA3; // mov [pStatus], eax *(PVOID*)(pBuf + ofst + 1) = pStatus; // ofst += 5; *(PUSHORT)(pBuf + ofst) = 0x006A; // push FALSE ofst += 2; *(PUCHAR)(pBuf + ofst) = 0x68; // push hEvent *(PULONG)(pBuf + ofst + 1) = (ULONG)(ULONG_PTR)hEvent; // ofst += 5; *(PUCHAR)(pBuf + ofst) = 0xB8; // mov eax, pSetEvent *(PULONG)(pBuf + ofst + 1) = (ULONG)(ULONG_PTR)pSetEvent;// ofst += 5; *(PUSHORT)(pBuf + ofst) = 0xD0FF; // call eax ofst += 2; return ofst; } ULONG GenPrologue64( IN PUCHAR pBuf ) { *(PULONG)(pBuf + 0) = 0x244C8948; // mov [rsp + 0x08], rcx *(PUCHAR)(pBuf + 4) = 0x8; // *(PULONG)(pBuf + 5) = 0x24548948; // mov [rsp + 0x10], rdx *(PUCHAR)(pBuf + 9) = 0x10; // *(PULONG)(pBuf + 10) = 0x2444894C; // mov [rsp + 0x18], r8 *(PUCHAR)(pBuf + 14) = 0x18; // *(PULONG)(pBuf + 15) = 0x244C894C; // mov [rsp + 0x20], r9 *(PUCHAR)(pBuf + 19) = 0x20; // return 20; } ULONG GenEpilogue64( IN PUCHAR pBuf, IN INT retSize ) { UNREFERENCED_PARAMETER( retSize ); *(PULONG)(pBuf + 0) = 0x244C8B48; // mov rcx, [rsp + 0x08] *(PUCHAR)(pBuf + 4) = 0x8; // *(PULONG)(pBuf + 5) = 0x24548B48; // mov rdx, [rsp + 0x10] *(PUCHAR)(pBuf + 9) = 0x10; // *(PULONG)(pBuf + 10) = 0x24448B4C; // mov r8, [rsp + 0x18] *(PUCHAR)(pBuf + 14) = 0x18; // *(PULONG)(pBuf + 15) = 0x244C8B4C; // mov r9, [rsp + 0x20] *(PUCHAR)(pBuf + 19) = 0x20; // *(PUCHAR)(pBuf + 20) = 0xC3; // ret return 21; } ULONG GenCall64( IN PUCHAR pBuf, IN PVOID pFn, INT argc, ... ) { va_list vl; va_start( vl, argc ); ULONG res = GenCall64V( pBuf, pFn, argc, vl ); va_end( vl ); return res; } ULONG GenCall64V( IN PUCHAR pBuf, IN PVOID pFn, IN INT argc, IN va_list vl ) { USHORT rsp_diff = 0x28; ULONG ofst = 0; if (argc > 4) { rsp_diff = (USHORT)(argc * sizeof( ULONG_PTR )); if (rsp_diff % 0x10) rsp_diff = ((rsp_diff / 0x10) + 1) * 0x10; rsp_diff += 8; } // sub rsp, rsp_diff *(PULONG)(pBuf + ofst) = (0x00EC8348 | rsp_diff << 24); ofst += 4; if (argc > 0) { PVOID arg = va_arg( vl, PVOID ); *(PUSHORT)(pBuf + ofst) = 0xB948; // mov rcx, arg *(PVOID*)(pBuf + ofst + 2) = arg; // ofst += 10; } if (argc > 1) { PVOID arg = va_arg( vl, PVOID ); *(PUSHORT)(pBuf + ofst) = 0xBA48; // mov rdx, arg *(PVOID*)(pBuf + ofst + 2) = arg; // ofst += 10; } if (argc > 2) { PVOID arg = va_arg( vl, PVOID ); *(PUSHORT)(pBuf + ofst) = 0xB849; // mov r8, arg *(PVOID*)(pBuf + ofst + 2) = arg; // ofst += 10; } if (argc > 3) { PVOID arg = va_arg( vl, PVOID ); *(PUSHORT)(pBuf + ofst) = 0xB949; // mov r9, arg *(PVOID*)(pBuf + ofst + 2) = arg; // ofst += 10; } for (INT i = 4; i < argc; i++) { PVOID arg = va_arg( vl, PVOID ); *(PUSHORT)(pBuf + ofst) = 0xB848; // mov rcx, arg *(PVOID*)(pBuf + ofst + 2) = arg; // ofst += 10; // mov [rsp + i*8], rax *(PULONG)(pBuf + ofst) = 0x24448948; *(PUCHAR)(pBuf + ofst + 4) = (UCHAR)(0x20 + (i - 4)*sizeof( arg )); ofst += 5; } *(PUSHORT)(pBuf + ofst) = 0xB848; // mov rax, pFn *(PVOID*)(pBuf + ofst + 2) = pFn; // ofst += 10; *(PUSHORT)(pBuf + ofst) = 0xD0FF; // call rax ofst += 2; // add rsp, rsp_diff *(PULONG)(pBuf + ofst) = (0x00C48348 | rsp_diff << 24); ofst += 4; return ofst; } ULONG GenSync64( IN PUCHAR pBuf, IN PNTSTATUS pStatus, IN PVOID pSetEvent, IN HANDLE hEvent ) { ULONG ofst = 0; *(PUSHORT)(pBuf + ofst) = 0xA348; // mov [pStatus], rax *(PVOID*)(pBuf + ofst + 2) = pStatus; // ofst += 10; *(PUSHORT)(pBuf + ofst) = 0xB948; // mov rcx, hEvent *(PHANDLE)(pBuf + ofst + 2) = hEvent; // ofst += 10; *(pBuf + ofst) = 0x48; // xor rdx, rdx *(PUSHORT)(pBuf + ofst + 1) = 0xD231; // ofst += 3; *(PUSHORT)(pBuf + ofst) = 0xB848; // mov rax, pSetEvent *(PVOID*)(pBuf + ofst + 2) = pSetEvent; // ofst += 10; *(PUSHORT)(pBuf + ofst) = 0xD0FF; // call rax ofst += 2; return ofst; } ULONG GenPrologueT( IN BOOLEAN wow64, IN PUCHAR pBuf ) { return wow64 ? GenPrologue32( pBuf ) : GenPrologue64( pBuf ); } ULONG GenEpilogueT( IN BOOLEAN wow64, IN PUCHAR pBuf, IN INT retSize ) { return wow64 ? GenEpilogue32( pBuf, retSize ) : GenEpilogue64( pBuf, retSize ); } ULONG GenCallT( IN BOOLEAN wow64, IN PUCHAR pBuf, IN PVOID pFn, IN INT argc, ... ) { va_list vl; va_start( vl, argc ); ULONG res = wow64 ? GenCall32V( pBuf, pFn, argc, vl ) : GenCall64V( pBuf, pFn, argc, vl ); va_end( vl ); return res; } ULONG GenCallTV( IN BOOLEAN wow64, IN PUCHAR pBuf, IN PVOID pFn, IN INT argc, IN va_list vl ) { return wow64 ? GenCall32V( pBuf, pFn, argc, vl ) : GenCall64V( pBuf, pFn, argc, vl ); } ULONG GenSyncT( IN BOOLEAN wow64, IN PUCHAR pBuf, IN PNTSTATUS pStatus, IN PVOID pSetEvent, IN HANDLE hEvent ) { return wow64 ? GenSync32( pBuf, pStatus, pSetEvent, hEvent ) : GenSync64( pBuf, pStatus, pSetEvent, hEvent ); }