1 /* Capstone Disassembly Engine */ 2 /* By Satoshi Tanda <tanda.sat (at) gmail.com>, 2016 */ 3 4 #include "winkernel_mm.h" 5 #include <ntddk.h> 6 #include <Ntintsafe.h> 7 8 // A pool tag for memory allocation 9 static const ULONG CS_WINKERNEL_POOL_TAG = 'kwsC'; 10 11 12 // A structure to implement realloc() 13 typedef struct _CS_WINKERNEL_MEMBLOCK { 14 size_t size; // A number of bytes allocated 15 char data[1]; // An address returned to a caller 16 } CS_WINKERNEL_MEMBLOCK; 17 C_ASSERT(sizeof(CS_WINKERNEL_MEMBLOCK) == sizeof(void *) * 2); 18 19 20 // free() 21 void CAPSTONE_API cs_winkernel_free(void *ptr) 22 { 23 if (ptr) { 24 ExFreePoolWithTag(CONTAINING_RECORD(ptr, CS_WINKERNEL_MEMBLOCK, data), CS_WINKERNEL_POOL_TAG); 25 } 26 } 27 28 // malloc() 29 void * CAPSTONE_API cs_winkernel_malloc(size_t size) 30 { 31 // Disallow zero length allocation because they waste pool header space and, 32 // in many cases, indicate a potential validation issue in the calling code. 33 NT_ASSERT(size); 34 35 // FP; a use of NonPagedPool is required for Windows 7 support 36 #pragma prefast(suppress : 30030) // Allocating executable POOL_TYPE memory 37 size_t number_of_bytes = 0; 38 CS_WINKERNEL_MEMBLOCK *block = NULL; 39 // A specially crafted size value can trigger the overflow. 40 // If the sum in a value that overflows or underflows the capacity of the type, 41 // the function returns NULL. 42 if (!NT_SUCCESS(RtlSizeTAdd(size, sizeof(CS_WINKERNEL_MEMBLOCK), &number_of_bytes))) { 43 return NULL; 44 } 45 block = (CS_WINKERNEL_MEMBLOCK *)ExAllocatePoolWithTag( 46 NonPagedPool, number_of_bytes, CS_WINKERNEL_POOL_TAG); 47 if (!block) { 48 return NULL; 49 } 50 block->size = size; 51 52 return block->data; 53 } 54 55 // calloc() 56 void * CAPSTONE_API cs_winkernel_calloc(size_t n, size_t size) 57 { 58 size_t total = n * size; 59 60 void *new_ptr = cs_winkernel_malloc(total); 61 if (!new_ptr) { 62 return NULL; 63 } 64 65 return RtlFillMemory(new_ptr, total, 0); 66 } 67 68 // realloc() 69 void * CAPSTONE_API cs_winkernel_realloc(void *ptr, size_t size) 70 { 71 void *new_ptr = NULL; 72 size_t current_size = 0; 73 size_t smaller_size = 0; 74 75 if (!ptr) { 76 return cs_winkernel_malloc(size); 77 } 78 79 new_ptr = cs_winkernel_malloc(size); 80 if (!new_ptr) { 81 return NULL; 82 } 83 84 current_size = CONTAINING_RECORD(ptr, CS_WINKERNEL_MEMBLOCK, data)->size; 85 smaller_size = (current_size < size) ? current_size : size; 86 RtlCopyMemory(new_ptr, ptr, smaller_size); 87 cs_winkernel_free(ptr); 88 89 return new_ptr; 90 } 91 92 // vsnprintf(). _vsnprintf() is available for drivers, but it differs from 93 // vsnprintf() in a return value and when a null-terminator is set. 94 // cs_winkernel_vsnprintf() takes care of those differences. 95 #pragma warning(push) 96 // Banned API Usage : _vsnprintf is a Banned API as listed in dontuse.h for 97 // security purposes. 98 #pragma warning(disable : 28719) 99 int CAPSTONE_API cs_winkernel_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr) 100 { 101 int result = _vsnprintf(buffer, count, format, argptr); 102 103 // _vsnprintf() returns -1 when a string is truncated, and returns "count" 104 // when an entire string is stored but without '\0' at the end of "buffer". 105 // In both cases, null-terminator needs to be added manually. 106 if (result == -1 || (size_t)result == count) { 107 buffer[count - 1] = '\0'; 108 } 109 110 if (result == -1) { 111 // In case when -1 is returned, the function has to get and return a number 112 // of characters that would have been written. This attempts so by retrying 113 // the same conversion with temp buffer that is most likely big enough to 114 // complete formatting and get a number of characters that would have been 115 // written. 116 char* tmp = cs_winkernel_malloc(0x1000); 117 if (!tmp) { 118 return result; 119 } 120 121 result = _vsnprintf(tmp, 0x1000, format, argptr); 122 NT_ASSERT(result != -1); 123 cs_winkernel_free(tmp); 124 } 125 126 return result; 127 } 128 #pragma warning(pop) 129