├── LICENSE ├── README.md ├── bughunt.c ├── bughunt.h ├── bughunt_build_x64_debug.bat ├── bughunt_build_x64_release.bat ├── bughunt_build_x86_release.bat ├── bughunt_loop.py ├── bughunt_syscall.asm ├── bughunt_syscall_x64.asm ├── bughunt_syscalls.h ├── bughunt_thread.h ├── crash_processing ├── couchdb_submit.py └── kd_batch_commands.txt ├── crashes └── README.txt ├── handles_database.h ├── helpers.h ├── hooking.h ├── library_calls.h ├── library_calls └── brush.h ├── logger.h ├── reproducer ├── build_template.bat └── template.c └── worker_setup ├── dbg_amd64_6.12.2.633.msi ├── dbg_x86_6.12.2.633.msi ├── disable_lockscreen.reg ├── disable_uac.reg ├── disable_windows_error_reporting.reg ├── disable_windows_updates.reg └── worker_setup.py /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 MWR InfoSecurity 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of MWR InfoSecurity nor the names of its contributors 13 | may be used to endorse or promote products derived from this software 14 | without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL MWR INFOSECURITY BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KernelFuzzer 2 | 3 | This is the core Kernel Fuzzer, with example library calls and Syscalls to start fuzzing Windows. The fuzzer has been tested on Windows 7 / 10, OS X and QNX. 4 | 5 | #Getting started 6 | 7 | - Download and install Python 3.5 8 | 9 | - Compile binary for your system using the included .bat scripts for the correct architecture (Windows only!). Tested using Visual Studio 2013 - if you use a different version of VS, edit the script to point at your copy of 'vcvarsall.bat'. 10 | 11 | - Run worker_setup/worker_setup.py 12 | 13 | The script should setup the VM as required, reboot and start the fuzzer. 14 | 15 | #Writing modules / syscalls 16 | 17 | See our Def Con 24 slides over at [MWR Labs] (https://labs.mwrinfosecurity.com/publications/platform-agnostic-kernel-fuzzing/) which give an explanation of the fuzzer and examples of writing library calls and syscalls for the fuzzer. One of each is provided as an example and more examples are provided in the slides. 18 | 19 | #Database 20 | If you wish to send your crashes to a CouchDB instance, this needs to be setup seperately, then edit the bughunt_loop.py scipt with the required information. 21 | 22 | #Contact 23 | Feel free to submit issues or ping us on Twitter - [@NerdKernel] (https://twitter.com/NerdKernel) / [@munmap] (https://twitter.com/munmap). -------------------------------------------------------------------------------- /bughunt.c: -------------------------------------------------------------------------------- 1 | #include "logger.h" 2 | #include "bughunt_thread.h" 3 | 4 | #define MAX_THREADS 64 5 | 6 | // Moved this in the global scope. 7 | // Number of system calls to execute as part of each thread. 8 | static unsigned int syscall_count; 9 | 10 | int main (int argc, char* argv[]) 11 | { 12 | // Number of threads to create. 13 | unsigned int subprocess_count = 0; 14 | 15 | // Thread index. 16 | unsigned int subprocess_idx = 0; 17 | 18 | // Thread data structures. 19 | unsigned int dwThreadIdArray[MAX_THREADS]; 20 | HANDLE hThreadArray[MAX_THREADS]; 21 | 22 | // PRNG Seed. 23 | unsigned int seed = NULL; 24 | unsigned int control_seed = NULL; 25 | 26 | if (argc != 4) 27 | { 28 | printf ("USAGE : %s \n", argv[0]); 29 | printf ("WHERE : subprocess_count : is a base 10 DWORD\n"); 30 | printf (" syscall_count : is a base 10 DWORD\n"); 31 | printf (" seed : Seed to use. Use 1 if you want to create a new seed.\n"); 32 | 33 | return (0xDEADBEEF); // Error. 34 | } 35 | 36 | // Populate our object store, i.e. handles database in the Windows case. 37 | make_HANDLES(); 38 | 39 | subprocess_count = strtol (argv[1], NULL, 10); 40 | syscall_count = strtol (argv[2], NULL, 10); 41 | seed = strtol (argv[3], NULL, 10); 42 | 43 | // Also check if syscall_count is less then MAX_THREADS. 44 | for (subprocess_idx = 0; subprocess_idx < subprocess_count; subprocess_idx += 1) 45 | { 46 | 47 | hThreadArray[subprocess_idx] = CreateThread(NULL, // default security attributes 48 | 0, // use default stack size 49 | (LPTHREAD_START_ROUTINE) bughunt_thread, // thread function name 50 | seed, // argument to thread function 51 | 0, // use default creation flags 52 | &dwThreadIdArray[subprocess_idx]); // returns the thread identifier 53 | 54 | // Check if thread was successfully created. Bail out should we fail to create a new thread. 55 | if (hThreadArray[subprocess_idx] == NULL) 56 | { 57 | printf ("Error creating thread, exiting."); 58 | return (0xDEADBEEF); // Error. 59 | } 60 | } // For loop. 61 | 62 | // Comment out the lines above and uncomment the following one for no threads. 63 | //bughunt_thread(syscall_count, seed); 64 | 65 | // Wait until all threads have terminated. 66 | WaitForMultipleObjects(subprocess_count, hThreadArray, TRUE, INFINITE); 67 | 68 | // Close all thread handles and free memory allocations. 69 | for(subprocess_idx = 0; subprocess_idx < subprocess_count; subprocess_idx += 1) 70 | { 71 | CloseHandle(hThreadArray[subprocess_idx]); 72 | } 73 | return (0); 74 | } -------------------------------------------------------------------------------- /bughunt.h: -------------------------------------------------------------------------------- 1 | #ifndef BUGHUNT_H 2 | #define BUGHUNT_H 3 | 4 | /* CONFIGURATION "FLAGS"... */ 5 | 6 | #define ENABLE_POST_MORTEM 7 | /* #undef ENABLE_POST_MORTEM */ 8 | 9 | 10 | /* TYPEDEFS USED BY THE BUGHUNT APPLICATIONS... */ 11 | 12 | #if _M_IX86 13 | typedef unsigned __int32 POINTER; 14 | #elif _M_X64 15 | typedef unsigned __int64 POINTER; 16 | #endif 17 | 18 | typedef enum 19 | { 20 | _BOOL = 1, 21 | _BOOL_PTR = 2, 22 | _CHAR8 = 3, 23 | _CHAR8_PTR = 4, 24 | _CHAR16 = 5, 25 | _CHAR16_PTR = 6, 26 | _INT8 = 7, 27 | _INT8_PTR = 8, 28 | _INT16 = 9, 29 | _INT16_PTR = 10, 30 | _INT32 = 11, 31 | _INT32_PTR = 12, 32 | _INT64 = 13, 33 | _INT64_PTR = 14, 34 | _UINT8 = 15, 35 | _UINT8_PTR = 16, 36 | _UINT16 = 17, 37 | _UINT16_PTR = 18, 38 | _UINT32 = 19, 39 | _UINT32_PTR = 20, 40 | _UINT64 = 21, 41 | _UINT64_PTR = 22, 42 | _REAL32 = 23, 43 | _REAL32_PTR = 24, 44 | _REAL64 = 25, 45 | _REAL64_PTR = 26, 46 | _HANDLE = 28, 47 | 48 | _VOID_PTR = 27, 49 | 50 | NIL = 0 51 | 52 | } DATATYPE; 53 | 54 | /* SYSCALL DEFINITIONS... */ 55 | 56 | #define SYSCALL_ARGUMENT_N ((size_t)33) 57 | 58 | typedef struct 59 | { 60 | DWORD uid; 61 | DATATYPE argument_datatypes[SYSCALL_ARGUMENT_N]; 62 | DATATYPE return_datatype; 63 | } SYSCALL; 64 | 65 | // Struct to hold a handle value and its index in the array of handles. 66 | typedef struct { 67 | HANDLE value; 68 | int index; 69 | } BH_Handle; 70 | 71 | #include "bughunt_syscalls.h" 72 | //#include "library_calls.h" 73 | 74 | /* TYPEDEFS USED BY SYSCALLS... */ 75 | 76 | typedef unsigned __int8 bool_t; 77 | 78 | typedef char char8_t; 79 | typedef wchar_t char16_t; 80 | 81 | typedef __int8 int8_t; 82 | typedef __int16 int16_t; 83 | typedef __int32 int32_t; 84 | typedef __int64 int64_t; 85 | 86 | typedef unsigned __int8 uint8_t; 87 | typedef unsigned __int16 uint16_t; 88 | typedef unsigned __int32 uint32_t; 89 | typedef unsigned __int64 uint64_t; 90 | 91 | typedef float real32_t; 92 | typedef double real64_t; 93 | 94 | /* FUNCTION PROTOTYPES... */ 95 | 96 | bool_t get_fuzzed_bool (void); 97 | bool_t* get_fuzzed_bool_ptr (void); 98 | char8_t get_fuzzed_char8 (void); 99 | char8_t* get_fuzzed_char8_ptr (void); 100 | char16_t get_fuzzed_char16 (void); 101 | char16_t* get_fuzzed_char16_ptr (void); 102 | int8_t get_fuzzed_int8 (void); 103 | int8_t* get_fuzzed_int8_ptr (void); 104 | int16_t get_fuzzed_int16 (void); 105 | int16_t* get_fuzzed_int16_ptr (void); 106 | int32_t get_fuzzed_int32 (void); 107 | int32_t* get_fuzzed_int32_ptr (void); 108 | int64_t get_fuzzed_int64 (void); 109 | int64_t* get_fuzzed_int64_ptr (void); 110 | uint8_t get_fuzzed_uint8 (void); 111 | uint8_t* get_fuzzed_uint8_ptr (void); 112 | uint16_t get_fuzzed_uint16 (void); 113 | uint16_t* get_fuzzed_uint16_ptr (void); 114 | uint32_t get_fuzzed_uint32 (void); 115 | uint32_t* get_fuzzed_uint32_ptr (void); 116 | uint64_t get_fuzzed_uint64 (void); 117 | uint64_t* get_fuzzed_uint64_ptr (void); 118 | real32_t get_fuzzed_real32 (void); 119 | real32_t* get_fuzzed_real32_ptr (void); 120 | real64_t get_fuzzed_real64 (void); 121 | real64_t* get_fuzzed_real64_ptr (void); 122 | 123 | BYTE* get_fuzzed_block_ptr (const DATATYPE* const, DWORD); 124 | 125 | double random_double_0_to_1 (void); 126 | 127 | DWORD random_DWORD_0_to_N (DWORD); 128 | 129 | SYSCALL* random_SYSCALL (void); 130 | 131 | /* FUNCTION IMPLEMENTATIONS... */ 132 | 133 | bool_t get_fuzzed_bool (void) 134 | { 135 | bool_t bool_BH[] = {0, 1}; 136 | bool_t n; 137 | 138 | n = bool_BH[rand() % sizeof(bool_BH) / sizeof(bool_BH[0])]; 139 | logger("//[Get Fuzzed Value] get_fuzzed_bool : n = %hd", n); 140 | return n; 141 | } 142 | 143 | char8_t get_fuzzed_char8 (void) 144 | { 145 | char8_t char8_BH[] = { ' ', '\t', '\n', '\r', '\\', '/', '@', ':', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '_', '+', '=', ':' }; 146 | char8_t n; 147 | 148 | n = char8_BH[rand() % sizeof(char8_BH) / sizeof(char8_BH[0])]; 149 | logger("//[Get Fuzzed Value] get_fuzzed_char8 : n = %c", n); 150 | return n; 151 | } 152 | 153 | 154 | char16_t get_fuzzed_char16 (void) 155 | { 156 | char16_t char16_BH[] = { ' ', '\t', '\n', '\r', '\\', '/', '@', ':', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '_', '+', '=', ':' }; 157 | char16_t n; 158 | 159 | n = char16_BH[rand() % sizeof(char16_BH) / sizeof(char16_BH[0])]; 160 | logger("//[Get Fuzzed Value] get_fuzzed_char8 : n = %c", n); 161 | return n; 162 | } 163 | 164 | int8_t get_fuzzed_int8 (void) 165 | { 166 | 167 | int8_t n; 168 | 169 | switch (rand() % 8) { 170 | case 0: 171 | n = 1; // one 172 | break; 173 | case 1: 174 | n = 0; 175 | break; 176 | case 2: 177 | n = -1; 178 | break; 179 | case 3: 180 | n = 8; 181 | case 4: 182 | n = 16; 183 | case 5: 184 | n = 32; 185 | case 6: 186 | n = 64; 187 | case 7: 188 | switch (rand() % 3) { 189 | case 0: 190 | n = 0xff; // max 191 | break; 192 | case 1: 193 | n = 1UL << (rand() & 7); // 2^n (1 -> 128) 194 | break; 195 | case 2: 196 | n = rand() & 0xff; // 0 -> 0xff 197 | break; 198 | 199 | } 200 | 201 | } 202 | 203 | logger("//[Get Fuzzed Value] get_fuzzed_int8 : n = %hhd", n); 204 | return n; 205 | } 206 | 207 | int16_t get_fuzzed_int16 (void) 208 | { 209 | 210 | int16_t n; 211 | 212 | switch (rand() % 9) { 213 | case 0: 214 | switch (rand() % 4) { 215 | case 0: 216 | n = 0x8000 >> (rand() & 7); // 2^n (0x100 -> 0x8000) 217 | break; 218 | case 1: 219 | n = rand() & 0xffff; // 0 -> 0xffff 220 | break; 221 | case 2: 222 | n = 0xff00 | rand() & 0xff; // 0xff00 -> 0xffff 223 | break; 224 | case 3: 225 | n = 0xffff; // max 226 | break; 227 | } 228 | break; 229 | case 1: 230 | n = 1; 231 | break; 232 | case 2: 233 | n = 0; 234 | break; 235 | case 3: 236 | n = -1; 237 | break; 238 | case 4: 239 | n = 8; 240 | break; 241 | case 5: 242 | n = 16; 243 | break; 244 | case 6: 245 | n = 32; 246 | break; 247 | case 7: 248 | n = 64; 249 | break; 250 | case 8: 251 | n = 128; 252 | break; 253 | } 254 | logger("//[Get Fuzzed Value] get_fuzzed_int16 : n = %hd", n); 255 | return n; 256 | } 257 | 258 | int32_t get_fuzzed_int32 (void) 259 | { 260 | 261 | int32_t n; 262 | 263 | switch (rand() % 10) { 264 | case 0: 265 | switch (rand() % 11) 266 | { 267 | case 0: 268 | n = 0x80000000 >> (rand() & 0x1f); // 2^n (1 -> 0x10000) 269 | break; 270 | case 1: 271 | n = rand(); // 0 -> RAND_MAX (likely 0x7fffffff) 272 | break; 273 | case 2: 274 | n = (unsigned int)0xff << (4 * (rand() % 7)); 275 | break; 276 | case 3: 277 | n = 0xffff0000; 278 | break; 279 | case 4: 280 | n = 0xffffe000; 281 | break; 282 | case 5: 283 | n = 0xffffff00 | rand() & 0xff; 284 | break; 285 | case 6: 286 | n = 0xffffffff - 0x1000; 287 | break; 288 | case 7: 289 | n = 0x1000; 290 | break; 291 | case 8: 292 | n = 0x1000 * ((rand() % (0xffffffff / 0x1000)) + 1); 293 | break; 294 | case 9: 295 | n = 0xffffffff; // max 296 | break; 297 | case 10: 298 | n = 0x7fffffff; 299 | break; 300 | } 301 | break; 302 | case 1: 303 | n = 1; 304 | break; 305 | case 2: 306 | n = 0; 307 | break; 308 | case 3: 309 | n = -1; 310 | break; 311 | case 4: 312 | n = 8; 313 | break; 314 | case 5: 315 | n = 16; 316 | break; 317 | case 6: 318 | n = 32; 319 | break; 320 | case 7: 321 | n = 64; 322 | break; 323 | case 8: 324 | n = 128; 325 | break; 326 | case 9: 327 | n = 256; 328 | } 329 | logger("//[Get Fuzzed Value] get_fuzzed_int32 : n = %ld", n); 330 | return n; 331 | } 332 | 333 | int64_t get_fuzzed_int64 (void) 334 | { 335 | 336 | int64_t n; 337 | 338 | switch(rand() % 10) { 339 | case 0: 340 | n = 0; 341 | break; 342 | case 1: 343 | n = 1; 344 | break; 345 | case 2: 346 | switch (rand() % 1) { 347 | case 0: 348 | n = 0xffffffffffffffff; 349 | break; 350 | case 1: 351 | n = rand() & 0xffffffffffffffff; 352 | break; 353 | } 354 | break; 355 | case 3: 356 | n = -1; 357 | break; 358 | case 4: 359 | n = 8; 360 | break; 361 | case 5: 362 | n = 16; 363 | break; 364 | case 6: 365 | n = 32; 366 | break; 367 | case 7: 368 | n = 64; 369 | break; 370 | case 8: 371 | n = 128; 372 | break; 373 | case 9: 374 | n = 256; 375 | } 376 | logger("//[Get Fuzzed Value] get_fuzzed_int64 : n = %lld", n); 377 | return n; 378 | } 379 | 380 | uint8_t get_fuzzed_uint8 (void) 381 | { 382 | 383 | uint8_t n = (uint8_t)get_fuzzed_int8(); 384 | logger("//[Get Fuzzed Value] get_fuzzed_uint8 : n = %hhu", n); 385 | return n; 386 | } 387 | 388 | uint16_t get_fuzzed_uint16 (void) 389 | { 390 | 391 | uint16_t n = get_fuzzed_int16(); 392 | logger("//[Get Fuzzed Value] get_fuzzed_uint16 : n = %hu", n); 393 | return n; 394 | } 395 | 396 | uint32_t get_fuzzed_uint32 (void) 397 | { 398 | 399 | uint32_t n = get_fuzzed_int32(); 400 | logger("//[Get Fuzzed Value] get_fuzzed_uint32 : n = %lu", n); 401 | return n; 402 | } 403 | 404 | uint64_t get_fuzzed_uint64 (void) 405 | { 406 | 407 | uint64_t n = get_fuzzed_int64(); 408 | logger("//[Get Fuzzed Value] get_fuzzed_uint64 : n = %llu", n); 409 | return n; 410 | } 411 | 412 | real32_t get_fuzzed_real32 (void) 413 | { 414 | 415 | real32_t n = random_double_0_to_1(); 416 | logger("//[Get Fuzzed Value] get_fuzzed_real32 : n = %lf", n); 417 | return n; 418 | } 419 | 420 | real64_t get_fuzzed_real64 (void) 421 | { 422 | 423 | real64_t n = random_double_0_to_1(); 424 | logger("//[Get Fuzzed Value] get_fuzzed_real64 : n = %llf", n); 425 | return n; 426 | } 427 | 428 | double random_double_0_to_1 (void) 429 | { 430 | 431 | // http://stackoverflow.com/questions/1340729/how-do-you-generate-a-random-double-uniformly-distributed-between-0-and-1-from-c 432 | return ((double)rand() / (double)RAND_MAX); 433 | } 434 | 435 | DWORD random_DWORD_0_to_N (DWORD n) 436 | { 437 | /* RETURNS A PSEUDO-RANDOM DWORD IN THE RANGE [0 ... n]... */ 438 | return ((DWORD)(random_double_0_to_1 () * n)); 439 | } 440 | 441 | SYSCALL* random_SYSCALL (void) 442 | { 443 | unsigned int n = sizeof (SYSCALLS) / sizeof (SYSCALLS[0]); 444 | return (&(SYSCALLS[random_DWORD_0_to_N (n)])); 445 | } 446 | 447 | #endif /* BUGHUNT_H... */ -------------------------------------------------------------------------------- /bughunt_build_x64_debug.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | echo: 3 | echo [Build] Setting up environment variables... 4 | echo: 5 | call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" amd64 6 | echo: 7 | echo [Build] Removing old files... 8 | echo: 9 | del *.log 10 | del *.obj 11 | del *.exe 12 | del *.ilk 13 | echo: 14 | echo [Build] Assembling object file... 15 | echo: 16 | ml64.exe /c /Cx bughunt_syscall_x64.asm 17 | echo: 18 | echo [Build] Final compilation and linking... 19 | echo: 20 | cl.exe /Zi /Tc bughunt.c gdi32.lib Kernel32.lib User32.lib Advapi32.lib Shell32.lib Msimg32.lib Dxva2.lib Mscms.lib bughunt_syscall_x64.obj 21 | ::cl.exe /Zi /Tc reproduce/repro.c gdi32.lib kernel32.lib User32.lib Advapi32.lib Shell32.lib Msimg32.lib Dxva2.lib Mscms.lib bughunt_syscall_x64.obj -------------------------------------------------------------------------------- /bughunt_build_x64_release.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | echo: 3 | echo [Build] Setting up environment variables... 4 | echo: 5 | call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" amd64 >nul 2>&1 6 | echo: 7 | echo [Build] Removing old files... 8 | echo: 9 | del *.log >nul 2>&1 10 | del *.obj >nul 2>&1 11 | del *.exe >nul 2>&1 12 | echo: 13 | echo [Build] Assembling object file... 14 | echo: 15 | ml64.exe /c /Cx bughunt_syscall_x64.asm >nul 2>&1 16 | echo: 17 | echo [Build] Final compilation and linking... 18 | echo: 19 | cl.exe /Tc bughunt.c gdi32.lib kernel32.lib User32.lib Advapi32.lib Shell32.lib Msimg32.lib Dxva2.lib Mscms.lib bughunt_syscall_x64.obj >nul 2>&1 -------------------------------------------------------------------------------- /bughunt_build_x86_release.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | echo: 3 | echo [Build] Setting up environment variables... 4 | echo: 5 | call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" >nul 2>&1 6 | echo: 7 | echo [Build] Removing old files... 8 | echo: 9 | del *.log >nul 2>&1 10 | del *.obj >nul 2>&1 11 | del *.exe >nul 2>&1 12 | echo: 13 | echo [Build] Assembling object file... 14 | echo: 15 | ml.exe /c /Cx bughunt_syscall.asm >nul 2>&1 16 | echo: 17 | echo [Build] Final compilation and linking... 18 | echo: 19 | cl.exe /Tc bughunt.c gdi32.lib kernel32.lib User32.lib Advapi32.lib Shell32.lib Msimg32.lib Dxva2.lib Mscms.lib bughunt_syscall.obj >nul 2>&1 -------------------------------------------------------------------------------- /bughunt_loop.py: -------------------------------------------------------------------------------- 1 | from subprocess import call, TimeoutExpired 2 | import fnmatch 3 | import sys 4 | import shutil 5 | import os 6 | import time 7 | 8 | # Current timeout set to 25 minutes, i.e. 25*60 seconds. 9 | #TIMEOUT=1500 10 | TIMEOUT=600 11 | THREADS=1 12 | # Current number of executions is 1M. 13 | #EXECUTIONS=1000000 14 | EXECUTIONS=350000 15 | SEED=1 16 | 17 | COMMAND='bughunt.exe' 18 | 19 | # Change working directory to where this wrapper is located. 20 | wrapper_path = os.path.abspath(__file__) 21 | wrapper_path_parent = os.path.dirname(wrapper_path) 22 | os.chdir(wrapper_path_parent) 23 | 24 | # Folder name where to preserve any logs and dumps. 25 | #folder = "crashes/%s" % str(tstamp) 26 | folder = "to-be-populated" 27 | crash_found = False 28 | 29 | #try: 30 | # os.makedirs(folder, 777) 31 | #except: 32 | # print("Error, cannot create folder structure.") 33 | # pass 34 | 35 | # Look for memory dumps and move them to a new folder 36 | for r, d, filenames in os.walk('C:/Dumps/'): 37 | for filename in fnmatch.filter(filenames, '*.dmp'): 38 | 39 | try: 40 | print("Crash found!") 41 | try: 42 | print("Creating folder...") 43 | tstamp = time.time() 44 | folder = "crashes/%s" % str(tstamp) 45 | os.makedirs(folder, 777) 46 | except: 47 | print("Error, cannot create folder structure.") 48 | pass 49 | 50 | # Currently copying the memory dump as opposed to just moving it. 51 | #shutil.copyfile("C:/Dumps/%s" % filename, "%s/%s" % (folder, filename)) 52 | # Move instead of copying. 53 | print("Moving memory dump to new folder...") 54 | os.rename("C:/Dumps/%s" % filename, "%s/%s" % (folder, filename)) 55 | 56 | # Process the memory dump using kd_batch_commands.txt. 57 | print("Analysing memory dump... %s" % filename) 58 | #kd_log = open("%s/%s.log" % (folder, filename.split('.')[0]),"wb") 59 | kd_log = open("%s/windbg.log" % folder, "wb") 60 | call(["C:\\Program Files\\Debugging Tools for Windows (x64)\\kd.exe", "-z", "%s\%s" % (folder, filename), "-c", "$$ arg1 9 | ; RDX -> arg2 10 | ; R8 -> arg3 11 | ; R9 -> arg4 12 | 13 | push rbp ; prologue 14 | mov rbp, rsp 15 | sub rsp, 118h 16 | 17 | mov rax, rcx ; 18 | mov r10, rdx 19 | mov rdx, r8 20 | mov r8, r9 21 | 22 | ; mov rcx, [rbp + XXh] ; main (argv[X + 4]) 23 | ; push rcx 24 | 25 | mov rcx, [rbp + 110h] ; main (argv[28 + 4]) = dw0x1B 26 | push rcx 27 | 28 | mov rcx, [rbp + 108h] ; main (argv[28 + 4]) = dw0x1B 29 | push rcx 30 | 31 | mov rcx, [rbp + 100h] ; main (argv[27 + 4]) = dw0x1A 32 | push rcx 33 | 34 | mov rcx, [rbp + 0F8h] ; main (argv[26 + 4]) = dw0x19 35 | push rcx 36 | 37 | mov rcx, [rbp + 0F0h] ; main (argv[25 + 4]) = dw0x18 38 | push rcx 39 | mov rcx, [rbp + 0E8h] ; main (argv[24 + 4]) = dw0x17 40 | push rcx 41 | mov rcx, [rbp + 0E0h] ; main (argv[23 + 4]) = dw0x16 42 | push rcx 43 | mov rcx, [rbp + 0D8h] ; main (argv[22 + 4]) = dw0x15 44 | push rcx 45 | mov rcx, [rbp + 0D0h] ; main (argv[21 + 4]) = dw0x14 46 | push rcx 47 | mov rcx, [rbp + 0C8h] ; main (argv[20 + 4]) = dw0x13 48 | push rcx 49 | mov rcx, [rbp + 0C0h] ; main (argv[19 + 4]) = dw0x12 50 | push rcx 51 | mov rcx, [rbp + 0B8h] ; main (argv[18 + 4]) = dw0x11 52 | push rcx 53 | mov rcx, [rbp + 0B0h] ; main (argv[17 + 4]) = dw0x10 54 | push rcx 55 | mov rcx, [rbp + 0A8h] ; main (argv[16 + 4]) 56 | push rcx 57 | mov rcx, [rbp + 0A0h] ; main (argv[15 + 4]) 58 | push rcx 59 | mov rcx, [rbp + 98h] ; main (argv[14 + 4]) 60 | push rcx 61 | mov rcx, [rbp + 90h] ; main (argv[13 + 4]) 62 | push rcx 63 | mov rcx, [rbp + 88h] ; main (argv[12 + 4]) 64 | push rcx 65 | mov rcx, [rbp + 80h] ; main (argv[11 + 4]) 66 | push rcx 67 | mov rcx, [rbp + 78h] ; main (argv[10 + 4]) 68 | push rcx 69 | mov rcx, [rbp + 70h] ; main (argv[9 + 4]) 70 | push rcx 71 | mov rcx, [rbp + 68h] ; main (argv[8 + 4]) 72 | push rcx 73 | mov rcx, [rbp + 60h] ; main (argv[7 + 4]) 74 | push rcx 75 | mov rcx, [rbp + 58h] ; main (argv[6 + 4]) 76 | push rcx 77 | mov rcx, [rbp + 50h] ; main (argv[5 + 4]) 78 | push rcx 79 | mov rcx, [rbp + 48h] ; main (argv[4 + 4]) 80 | push rcx 81 | mov rcx, [rbp + 40h] ; main (argv[3 + 4]) 82 | push rcx 83 | mov rcx, [rbp + 38h] ; main (argv[2 + 4]) 84 | push rcx 85 | 86 | mov r9, [rbp + 30h] 87 | mov rcx, r10 88 | 89 | ; R9 <- main (argv[4]) 90 | ; R8 <- main (argv[3]) 91 | ; RDX <- main (argv[2]) 92 | ; RCX <- main (argv[1]) 93 | 94 | syscall ; invoke syscall 95 | 96 | mov rsp, rbp ; epilogue, either that or `leave' 97 | pop rbp 98 | ret 99 | 100 | bughunt_syscall ENDP 101 | 102 | END 103 | -------------------------------------------------------------------------------- /bughunt_syscalls.h: -------------------------------------------------------------------------------- 1 | #ifndef BUGHUNT_SYSCALLS_H 2 | #define BUGHUNT_SYSCALLS_H 3 | 4 | // Other Resources 5 | // https://src.chromium.org/svn/trunk/src/tools/traceline/traceline/syscall_map.h 6 | // http://j00ru.vexillium.org/ntapi_64/ 7 | // http://j00ru.vexillium.org/ntapi/ 8 | // http://j00ru.vexillium.org/win32k_syscalls/ 9 | // http://www.cc.gatech.edu/~brendan/volatility/dl/ssdt.py 10 | 11 | /* 12 | typedef struct 13 | { 14 | DWORD uid; 15 | DATATYPE argument_datatypes[SYSCALL_ARGUMENT_N]; 16 | DATATYPE return_datatype; 17 | 18 | } SYSCALL; 19 | */ 20 | 21 | /* LAST VALUE IN EACH argument_datatypes[] MEMBER MUST BE NIL... */ 22 | 23 | SYSCALL SYSCALLS[] = 24 | { 25 | //Windows 7 x64 user32 syscalls. 26 | 27 | { ((DWORD)0x12F5), { NIL }, _BOOL }, 28 | { ((DWORD)0x12D4), { _VOID_PTR, _VOID_PTR, _VOID_PTR, _HANDLE, NIL }, _BOOL }, 29 | 30 | // End of Windows 7 x64 gdi32 syscalls. 31 | }; 32 | 33 | 34 | 35 | #endif /* BUGHUNT_SYSCALLS_H... */ 36 | 37 | 38 | -------------------------------------------------------------------------------- /bughunt_thread.h: -------------------------------------------------------------------------------- 1 | #ifndef BUGHUNT_THREAD_H 2 | #define BUGHUNT_THREAD_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "bughunt.h" 11 | #include "handles_database.h" 12 | #include "library_calls.h" 13 | #include "hooking.h" 14 | 15 | #ifdef _M_IX86 16 | 17 | __declspec(noinline) DWORD __stdcall bughunt_syscall ( 18 | 19 | DWORD _syscall_uid, 20 | 21 | DWORD _dw0x01, 22 | DWORD _dw0x02, 23 | DWORD _dw0x03, 24 | DWORD _dw0x04, 25 | DWORD _dw0x05, 26 | DWORD _dw0x06, 27 | DWORD _dw0x07, 28 | DWORD _dw0x08, 29 | DWORD _dw0x09, 30 | DWORD _dw0x0A, 31 | DWORD _dw0x0B, 32 | DWORD _dw0x0C, 33 | DWORD _dw0x0D, 34 | DWORD _dw0x0E, 35 | DWORD _dw0x0F, 36 | DWORD _dw0x10, 37 | DWORD _dw0x11, 38 | DWORD _dw0x12, 39 | DWORD _dw0x13, 40 | DWORD _dw0x14, 41 | DWORD _dw0x15, 42 | DWORD _dw0x16, 43 | DWORD _dw0x17, 44 | DWORD _dw0x18, 45 | DWORD _dw0x19, 46 | DWORD _dw0x1A, 47 | DWORD _dw0x1B, 48 | DWORD _dw0x1C, 49 | DWORD _dw0x1D, 50 | DWORD _dw0x1E, 51 | DWORD _dw0x1F, 52 | DWORD _dw0x20 53 | ) 54 | { 55 | __asm 56 | { 57 | push _dw0x20 58 | push _dw0x1F 59 | push _dw0x1E 60 | push _dw0x1D 61 | push _dw0x1C 62 | push _dw0x1B 63 | push _dw0x1A 64 | push _dw0x19 65 | push _dw0x18 66 | push _dw0x17 67 | push _dw0x16 68 | push _dw0x15 69 | push _dw0x14 70 | push _dw0x13 71 | push _dw0x12 72 | push _dw0x11 73 | push _dw0x10 74 | push _dw0x0F 75 | push _dw0x0E 76 | push _dw0x0D 77 | push _dw0x0C 78 | push _dw0x0B 79 | push _dw0x0A 80 | push _dw0x09 81 | push _dw0x08 82 | push _dw0x07 83 | push _dw0x06 84 | push _dw0x05 85 | push _dw0x04 86 | push _dw0x03 87 | push _dw0x02 88 | push _dw0x01 89 | 90 | mov eax, _syscall_uid 91 | 92 | mov edx, 7FFE0300h 93 | call dword ptr [edx] 94 | 95 | add esp, 0x80 96 | } 97 | } 98 | 99 | #elif _M_IX64 100 | 101 | extern DWORD __stdcall bughunt_syscall( 102 | 103 | DWORD _syscall_uid, 104 | 105 | QWORD _dw0x01, 106 | QWORD _dw0x02, 107 | QWORD _dw0x03, 108 | QWORD _dw0x04, 109 | QWORD _dw0x05, 110 | QWORD _dw0x06, 111 | QWORD _dw0x07, 112 | QWORD _dw0x08, 113 | QWORD _dw0x09, 114 | QWORD _dw0x0A, 115 | QWORD _dw0x0B, 116 | QWORD _dw0x0C, 117 | QWORD _dw0x0D, 118 | QWORD _dw0x0E, 119 | QWORD _dw0x0F, 120 | QWORD _dw0x10, 121 | QWORD _dw0x11, 122 | QWORD _dw0x12, 123 | QWORD _dw0x13, 124 | QWORD _dw0x14, 125 | QWORD _dw0x15, 126 | QWORD _dw0x16, 127 | QWORD _dw0x17, 128 | QWORD _dw0x18, 129 | QWORD _dw0x19, 130 | QWORD _dw0x1A, 131 | QWORD _dw0x1B, 132 | QWORD _dw0x1C, 133 | QWORD _dw0x1D, 134 | QWORD _dw0x1E, 135 | QWORD _dw0x1F, 136 | QWORD _dw0x20 137 | ); 138 | 139 | #endif 140 | 141 | extern DWORD syscall_count; 142 | 143 | DWORD bughunt_thread(unsigned int seed) 144 | { 145 | unsigned int syscall_idx = 0; 146 | SYSCALL* syscall = NULL; 147 | unsigned int syscall_argument_datatype_idx = 0; 148 | unsigned int syscall_arguments[SYSCALL_ARGUMENT_N - 1]; // DWORD syscall_arguments[32]; 149 | FILE* stream; // For logging. 150 | 151 | BH_Handle syscall_handle_argument; 152 | // The syscall_log_string will hold the string to be logged before the syscall invocation. 153 | char syscall_log_string[512]; 154 | memset(syscall_log_string, '\0', 512); 155 | 156 | // It turns out rand() is thread-safe after all as its state is kept in a thread-local storage (TLS). This means we have to seed every single state on its own. In this case we choose to use a comination of time(NULL), current process ID, and current thread ID. 157 | if (seed == 1) 158 | { 159 | seed = time(NULL) + GetCurrentProcessId() + GetCurrentThreadId(); 160 | logger("//[PRNG Seed] (0x%08X, 0x%08X, %u)", GetCurrentProcessId(), GetCurrentThreadId(), seed); 161 | srand(seed); 162 | } 163 | else //we have been given a seed to use, so use that. 164 | { 165 | logger("//[PRNG Seed] (0x%08X, 0x%08X, %u)", GetCurrentProcessId(), GetCurrentThreadId(), seed); 166 | srand(seed); 167 | } 168 | 169 | for (syscall_idx = 0; syscall_idx < syscall_count; syscall_idx += 1) 170 | { 171 | 172 | // Invoke one or more library calls. 173 | while (TRUE) { 174 | //fflush(NULL); 175 | 176 | // To hook or not to hook? Hook functions at random. 177 | if (rand() % 5 == 1) { 178 | // Uncomment below for hooking. 179 | // 1. Okay, we'll hook. Proceed with installing hook. 180 | //BH_SetWindowsHookEx(); 181 | // 2. Make a library call. 182 | (*random_LIBRARY_CALL())(); 183 | // 3. Uninstall the hook. 184 | //BH_UnhookWindowsHookEx(); 185 | } 186 | else { 187 | (*random_LIBRARY_CALL())(); 188 | } 189 | if (rand() % 2) { 190 | break; 191 | } 192 | } 193 | // Start cionstructing the syscall invocation log string little by little, i.e. argument by argument. 194 | 195 | syscall = random_SYSCALL (); 196 | syscall_argument_datatype_idx = 0; 197 | sprintf(syscall_log_string, "bughunt_syscall(0x%08x,", syscall->uid); 198 | 199 | while ((syscall_argument_datatype_idx < (SYSCALL_ARGUMENT_N - 1)) 200 | && (syscall->argument_datatypes[syscall_argument_datatype_idx] != NIL)) 201 | { 202 | //logger("//syscall_argument_datatype_idx = %d\n", syscall_argument_datatype_idx); 203 | switch (syscall->argument_datatypes[syscall_argument_datatype_idx]) 204 | { 205 | // Something to check is whether the 0x%08x format string specifier is okay in all cases, e.g. 64-bit. 206 | case _BOOL: 207 | syscall_arguments[syscall_argument_datatype_idx] = ((DWORD)get_fuzzed_bool()); 208 | sprintf(syscall_log_string + strlen(syscall_log_string), "0x%08x,", syscall_arguments[syscall_argument_datatype_idx]); 209 | break; 210 | 211 | case _CHAR8: 212 | syscall_arguments[syscall_argument_datatype_idx] = ((DWORD)get_fuzzed_char8()); 213 | sprintf(syscall_log_string + strlen(syscall_log_string), "0x%08x,", syscall_arguments[syscall_argument_datatype_idx]); 214 | break; 215 | 216 | case _CHAR16: 217 | syscall_arguments[syscall_argument_datatype_idx] = ((DWORD)get_fuzzed_char16()); 218 | sprintf(syscall_log_string + strlen(syscall_log_string), "0x%08x,", syscall_arguments[syscall_argument_datatype_idx]); 219 | break; 220 | 221 | case _INT8: 222 | syscall_arguments[syscall_argument_datatype_idx] = ((DWORD)get_fuzzed_int8()); 223 | sprintf(syscall_log_string + strlen(syscall_log_string), "0x%08x,", syscall_arguments[syscall_argument_datatype_idx]); 224 | break; 225 | 226 | case _INT16: 227 | syscall_arguments[syscall_argument_datatype_idx] = ((DWORD)get_fuzzed_int16()); 228 | sprintf(syscall_log_string + strlen(syscall_log_string), "0x%08x,", syscall_arguments[syscall_argument_datatype_idx]); 229 | break; 230 | 231 | case _INT32: 232 | syscall_arguments[syscall_argument_datatype_idx] = ((DWORD)get_fuzzed_int32()); 233 | sprintf(syscall_log_string + strlen(syscall_log_string), "0x%08x,", syscall_arguments[syscall_argument_datatype_idx]); 234 | break; 235 | 236 | case _INT64: 237 | syscall_arguments[syscall_argument_datatype_idx] = ((DWORD)get_fuzzed_int64()); 238 | sprintf(syscall_log_string + strlen(syscall_log_string), "0x%08x,", syscall_arguments[syscall_argument_datatype_idx]); 239 | break; 240 | 241 | case _UINT8: 242 | syscall_arguments[syscall_argument_datatype_idx] = ((DWORD)get_fuzzed_uint8()); 243 | sprintf(syscall_log_string + strlen(syscall_log_string), "0x%08x,", syscall_arguments[syscall_argument_datatype_idx]); 244 | break; 245 | 246 | case _UINT16: 247 | syscall_arguments[syscall_argument_datatype_idx] = ((DWORD)get_fuzzed_uint16()); 248 | sprintf(syscall_log_string + strlen(syscall_log_string), "0x%08x,", syscall_arguments[syscall_argument_datatype_idx]); 249 | break; 250 | 251 | case _UINT32: 252 | syscall_arguments[syscall_argument_datatype_idx] = ((DWORD)get_fuzzed_uint32()); 253 | sprintf(syscall_log_string + strlen(syscall_log_string), "0x%08x,", syscall_arguments[syscall_argument_datatype_idx]); 254 | break; 255 | 256 | case _UINT64: 257 | syscall_arguments[syscall_argument_datatype_idx] = ((DWORD)get_fuzzed_uint64()); 258 | sprintf(syscall_log_string + strlen(syscall_log_string), "0x%08x,", syscall_arguments[syscall_argument_datatype_idx]); 259 | break; 260 | 261 | case _REAL32: 262 | syscall_arguments[syscall_argument_datatype_idx] = ((DWORD)get_fuzzed_real32()); 263 | sprintf(syscall_log_string + strlen(syscall_log_string), "0x%08x,", syscall_arguments[syscall_argument_datatype_idx]); 264 | break; 265 | 266 | case _REAL64: 267 | syscall_arguments[syscall_argument_datatype_idx] = ((DWORD)get_fuzzed_real64()); 268 | sprintf(syscall_log_string + strlen(syscall_log_string), "0x%08x,", syscall_arguments[syscall_argument_datatype_idx]); 269 | break; 270 | 271 | case _HANDLE: 272 | syscall_handle_argument = get_random_HANDLE(); 273 | syscall_arguments[syscall_argument_datatype_idx] = ((DWORD)syscall_handle_argument.value); 274 | sprintf(syscall_log_string + strlen(syscall_log_string), "get_specific_HANDLE(%d),", syscall_handle_argument.index); 275 | break; 276 | 277 | } // Switch statement. 278 | syscall_argument_datatype_idx += 1; 279 | } // While loop. 280 | 281 | for (; syscall_argument_datatype_idx < 32; ) { 282 | //logger("//syscall_argument_datatype_idx = %d\n", syscall_argument_datatype_idx); 283 | sprintf(syscall_log_string + strlen(syscall_log_string), "0x%08X,", 0x4142434445464748); 284 | syscall_argument_datatype_idx += 1; 285 | } 286 | 287 | sprintf(syscall_log_string + strlen(syscall_log_string) - 1, ");"); 288 | logger(syscall_log_string); 289 | 290 | /* INVOKE THE SYSCALL... */ 291 | bughunt_syscall ( 292 | syscall->uid, 293 | syscall_arguments[0], 294 | syscall_arguments[1], 295 | syscall_arguments[2], 296 | syscall_arguments[3], 297 | syscall_arguments[4], 298 | syscall_arguments[5], 299 | syscall_arguments[6], 300 | syscall_arguments[7], 301 | syscall_arguments[8], 302 | syscall_arguments[9], 303 | syscall_arguments[10], 304 | syscall_arguments[11], 305 | syscall_arguments[12], 306 | syscall_arguments[13], 307 | syscall_arguments[14], 308 | syscall_arguments[15], 309 | syscall_arguments[16], 310 | syscall_arguments[17], 311 | syscall_arguments[18], 312 | syscall_arguments[19], 313 | syscall_arguments[20], 314 | syscall_arguments[21], 315 | syscall_arguments[22], 316 | syscall_arguments[23], 317 | syscall_arguments[24], 318 | syscall_arguments[25], 319 | syscall_arguments[26], 320 | syscall_arguments[27], 321 | syscall_arguments[28], 322 | syscall_arguments[29], 323 | syscall_arguments[30], 324 | syscall_arguments[31] 325 | ); 326 | } // For loop. 327 | return (0); 328 | } 329 | 330 | #endif /* BUGHUNT_THREAD_H... */ 331 | -------------------------------------------------------------------------------- /crash_processing/couchdb_submit.py: -------------------------------------------------------------------------------- 1 | import couchdb 2 | from couchdb.design import ViewDefinition 3 | import sys 4 | import shutil 5 | import argparse 6 | import os 7 | import random 8 | import hashlib 9 | 10 | # TODO: Tidy this scipt up! 11 | 12 | class CouchDBlib: 13 | 14 | def __init__(self,url,dbname,username,password): 15 | self.couch = couchdb.Server(url) 16 | self.couch.resource.credentials = (username, password) 17 | self.db = self.couch[dbname] 18 | self.create_views() 19 | 20 | def create_views(self): 21 | print("++ Creating views") 22 | view = ViewDefinition('get_crashes', 'all', '''function(doc) { 23 | emit(doc.crashhash,doc.crashcount); 24 | }''') 25 | view.get_doc(self.db) 26 | view.sync(self.db) 27 | 28 | def zip_crashdata(self,crashpath,fn): 29 | print("++ Zipping ", crashpath) 30 | output_filename = crashpath + "/../" + fn 31 | shutil.make_archive(output_filename, 'zip', crashpath) 32 | return output_filename 33 | 34 | def add_crash_to_db_windbg(self,crashhash,path): 35 | 36 | # Generate a FN for the crash 37 | # Change to a timestamp-based ID. 38 | fn = str(random.randint(0, 0xffffffff)) 39 | 40 | # First zip up the crash directory. 41 | self.zip_crashdata(path,fn) 42 | 43 | # Read the testcases for uploading 44 | testcase_zip = path + "\\../" + fn + ".zip" 45 | testcase = open(testcase_zip,"rb").read() 46 | 47 | 48 | # Change this so the log filename doesn't have to be hardcoded. 49 | debug_log_path = os.path.join(path +"\\windbg.log") 50 | # Open the log file and pull useful information. 51 | debug_log = open(debug_log_path).read() 52 | 53 | startstack = debug_log.find("STACK_TEXT:") 54 | endstack = debug_log.find("THREAD_SHA1_HASH_MOD_FUNC:") 55 | parserstack = debug_log[startstack:endstack] 56 | parserstack = parserstack.encode('utf-8') 57 | 58 | crashhash = hashlib.sha224(parserstack).hexdigest() 59 | 60 | startip = debug_log.find("FAULTING_IP: ") 61 | endip = debug_log.find("CONTEXT: ") 62 | parserip = debug_log[startip:endip] 63 | #parserip = unicode(parserip, 'utf-8') 64 | 65 | startreg = debug_log.find("CONTEXT: ") 66 | endreg = debug_log.find("efl=") 67 | parserreg = debug_log[startreg:endreg] 68 | #parserreg = unicode(parserreg, 'utf-8') 69 | 70 | if self.is_crash_in_db(crashhash): 71 | print("++ Crash already in DB skipping!") 72 | else: 73 | print("++ Adding new crash to DB ++") 74 | doc = { 75 | 'crashhash' : str(crashhash), 76 | 'faultingip' : str(parserip), 77 | 'callstack' : str(parserstack), 78 | 'registers' : str(parserreg), 79 | 'crashcount' : 1, 80 | 'analyzed' : False, 81 | 'reduced' : False 82 | } 83 | 84 | self.db.save(doc) 85 | self.db.put_attachment(doc,testcase, filename=fn+".zip") 86 | self.db.put_attachment(doc,debug_log,filename=fn+".log") # crash log 87 | 88 | # Cleanup the zip file data 89 | os.remove(testcase_zip) 90 | 91 | def add_crash_to_db(self,crashhash,faultingip,stackframe,testcase_path,debug_log_path,fn): 92 | self.zip_crashdata(testcase_path) 93 | testcase = open(testcase_path + "\\testcases.zip","rb").read() 94 | debug_log = open(debug_log_path).read() 95 | 96 | # First determine if the crash is already in DB. 97 | if self.is_crash_in_db(crashhash): 98 | print("++ Crash already in DB skipping!") 99 | else: 100 | print("++ Adding new crash to DB ++") 101 | doc = { 102 | 'crashhash' : crashhash, 103 | 'faultingip' : faultingip, 104 | 'callstack' : stackframe, 105 | 'crashcount' : 1, 106 | 'analyzed' : False, 107 | 'reduced' : False 108 | } 109 | self.db.save(doc) 110 | self.db.put_attachment(doc,testcase, filename=fn+".zip") 111 | self.db.put_attachment(doc,debug_log,filename=fn+".log") 112 | 113 | 114 | def is_crash_in_db(self,crashhash): 115 | crashes = self.db.view('get_crashes/all') 116 | 117 | found = False 118 | for crash in crashes: 119 | if crash.key == crashhash: 120 | print("++ Crash hash already in DB") 121 | self.increment_crash_count(crash.id) 122 | found = True 123 | return found 124 | return found 125 | 126 | 127 | def increment_crash_count(self,id): 128 | doc = self.db[id] 129 | doc['crashcount'] = int(doc['crashcount']) + 1 130 | 131 | self.db.save(doc) 132 | 133 | 134 | 135 | if __name__ == "__main__": 136 | parser = argparse.ArgumentParser(description='Handle couchdb arguments') 137 | parser.add_argument("--server",help='Couchdb Server Address') 138 | parser.add_argument("--databasename",help='Couchdb Database Name') 139 | parser.add_argument("--username",help='Couchdb Username') 140 | parser.add_argument("--password",help='Couchdb Password') 141 | 142 | subparsers = parser.add_subparsers(dest='mode') 143 | addcrash_parser = subparsers.add_parser('add-crash',help='Call to add crash to database') 144 | addcrash_parser.add_argument("--crash-path",help='The path to the crash to store') 145 | addcrash_parser.add_argument("--crash-hash",help='The hash of the crash to store') 146 | 147 | idcrash_parser = subparsers.add_parser('id-crash',help="Call to id a crash") 148 | idcrash_parser.add_argument("--crash-hash",help="The hash of the crash to identify") 149 | 150 | args = parser.parse_args() 151 | 152 | cdblib = CouchDBlib(args.server, args.databasename, args.username,args.password) 153 | 154 | if (args.mode == "id-crash"): 155 | print("++ Identifying crash") 156 | else: 157 | print("++ Adding crash") 158 | print(args.crash_hash) 159 | print(args.crash_path) 160 | 161 | # Get the crash path from the logfile path 162 | cdblib.add_crash_to_db_windbg(args.crash_hash, args.crash_path) 163 | -------------------------------------------------------------------------------- /crash_processing/kd_batch_commands.txt: -------------------------------------------------------------------------------- 1 | .sympath srv*C:\Symbols*https://msdl.microsoft.com/download/symbols 2 | .reload /f 3 | !analyze -v -------------------------------------------------------------------------------- /crashes/README.txt: -------------------------------------------------------------------------------- 1 | Crashes will be stored here once you get this running. 2 | -------------------------------------------------------------------------------- /handles_database.h: -------------------------------------------------------------------------------- 1 | #ifndef HANDLES_DATABASE_H 2 | #define HANDLES_DATABASE_H 3 | 4 | /* 5 | * CRUDE HANDLE DATABASE. 6 | */ 7 | 8 | // When updating either sizes, remember to adjust the other one. 9 | #define HANDLES_N 128 // Updated value, old and original value was 32. 10 | #define HANDLE_CREATOR_SIZE 128 11 | 12 | HANDLE HANDLES[HANDLES_N]; 13 | // The handle creator is subject to removal in the future, don't need it anymore. 14 | char* HANDLE_CREATOR[HANDLE_CREATOR_SIZE]; //When debugging, we need to know what created the handle. This will hold the array in the same order of HANDLES of the handle creator. 15 | 16 | // This tells us the first available, i.e. empoty slot in the array of handles. 17 | // This will initially be set to wherever make_HANDLES stops populating. It will increment till the end of the array and then set back to zero to start overwriting from the begging of the array. 18 | unsigned int HANDLES_ARRAY_AVAILABLE_SLOT_INDEX; 19 | // Do we have empy slots in the array of handles. 20 | int HANDLE_ARRAY_FULLY_POPULATED = 0; 21 | 22 | 23 | /* FUNCTION PROTOTYPES... */ 24 | 25 | 26 | BH_Handle get_random_HANDLE (void); 27 | HANDLE get_specific_HANDLE(int n); 28 | HANDLE put_random_HANDLE (HANDLE handle, char* HandleCreator); 29 | void dump_HANDLES (void); 30 | void make_HANDLES (void); 31 | 32 | // Helpers 33 | HWND OpenNotepad(void); 34 | 35 | 36 | BH_Handle get_random_HANDLE (void) 37 | { 38 | 39 | BH_Handle temp_handle; 40 | unsigned int n; 41 | 42 | if (HANDLE_ARRAY_FULLY_POPULATED) { 43 | n = sizeof (HANDLES) / sizeof (HANDLES[0]); 44 | n = rand() % n; 45 | } 46 | else { 47 | n = rand() % HANDLES_ARRAY_AVAILABLE_SLOT_INDEX; 48 | } 49 | 50 | temp_handle.index = n; 51 | temp_handle.value = HANDLES[n]; 52 | 53 | logger("//[Handler_Function]: get_random_HANDLE : n = %u, HANDLES[n] = 0x%08x, HANDLE_CREATOR[n] = %s", n, HANDLES[n], HANDLE_CREATOR[n]); 54 | return (temp_handle); 55 | } 56 | 57 | HANDLE get_specific_HANDLE(int n) 58 | { 59 | // This is for pulling a handle by its index. 60 | // Providing a negative index in the array as a means of returning NULL. 61 | if (n < 0) { 62 | return NULL; 63 | } 64 | else { 65 | return (HANDLES[n]); 66 | } 67 | } 68 | 69 | HANDLE put_random_HANDLE (HANDLE handle, char *HandleCreator) 70 | { 71 | // Check if the handle is valid, discard if it isn't. 72 | if (handle == NULL || handle == -1 || handle == INVALID_HANDLE_VALUE) { 73 | // Simply return. 74 | logger("//[Handler_Function]: put_random_HANDLE : Ignoring invalid handle."); 75 | // Should probably also check against values less than 33. 76 | // https://cxsecurity.com/issue/WLB-2015090152 77 | return 0; 78 | } 79 | else { 80 | // Check if we have reached the end of the array and if so, set the first available slot back to zero. 81 | if (HANDLES_ARRAY_AVAILABLE_SLOT_INDEX + 1 == HANDLES_N) { 82 | // Reset the available slot index to zero, i.e. start overwriting the handles in the array. 83 | HANDLES_ARRAY_AVAILABLE_SLOT_INDEX = 0; 84 | // Make sure we know that we have fully populated the array, this is used by get_random_HANDLE(). 85 | HANDLE_ARRAY_FULLY_POPULATED = 1; 86 | 87 | } 88 | 89 | HANDLES[HANDLES_ARRAY_AVAILABLE_SLOT_INDEX] = handle; 90 | HANDLE_CREATOR[HANDLES_ARRAY_AVAILABLE_SLOT_INDEX] = HandleCreator; 91 | 92 | /* FOR GOOD MEASURE, RETURN THE HANDLE AS WELL - IT DOESN'T HAVE TO BE USED... */ 93 | return (HANDLES[HANDLES_ARRAY_AVAILABLE_SLOT_INDEX++]); 94 | } 95 | } 96 | 97 | void dump_HANDLES (void) { 98 | // This is only for debugging purposes. 99 | int n; 100 | for (n = 0; n < HANDLES_N; n = n + 1) { 101 | printf("//[Handler_Function]: dump_HANDLES: n = %d, HANDLES[n] = 0x%08x\n", n, HANDLES[n]); 102 | } 103 | } 104 | 105 | void make_HANDLES (void) 106 | { 107 | // Improve the code as we see certain functions failing every time, which means we're not calling them the right way. 108 | // Diversify the handles! 109 | 110 | unsigned int handle_idx = 0; 111 | const POINT ptZero = { 0, 0 }; //to get a handle to the primary monitor 112 | 113 | BITMAP bmp = { 0, 8, 8, 2, 1, 1 }; 114 | BYTE bits [8][2] = { 0xFF, 0, 0x0C, 0, 0x0C, 0, 0x0C, 0, 115 | 0xFF, 0, 0xC0, 0, 0xC0, 0, 0xC0, 0 }; 116 | 117 | HKEY keyCurrentUser; 118 | 119 | HANDLE tempHandle; 120 | unsigned int tempUINT1, tempUINT2; 121 | 122 | INT NumberOfNotepadHandles = 0; 123 | 124 | tempUINT1 = 0; 125 | tempUINT2 = 0; 126 | 127 | 128 | // Initialise the array of handles by setting every handle to 0. 129 | for (handle_idx = 0; handle_idx < HANDLES_N; handle_idx += 1) { 130 | HANDLES[handle_idx] = 0x0000000000000000; 131 | } 132 | 133 | // Populate each one of the handle slots sequentially. 134 | //for (handle_idx = 0; handle_idx < HANDLES_N; handle_idx += 1) { 135 | for (handle_idx = 0; handle_idx < 64; handle_idx += 1) { 136 | 137 | while(HANDLES[handle_idx] == 0x0000000000000000) { 138 | 139 | if (!tempUINT1) { 140 | tempHandle = GetDesktopWindow(); 141 | if (tempHandle == NULL || tempHandle == -1 || tempHandle == INVALID_HANDLE_VALUE) { 142 | logger("//[Handler_Function]: make_HANDLES : Ignoring invalid handle."); 143 | } 144 | else { 145 | logger("//[Handler_Function]: make_HANDLES : n = %u, handle = 0x%08X, HANDLE_CREATOR[n] = %s", handle_idx, tempHandle, "GetDesktopWindow"); 146 | HANDLES[handle_idx] = tempHandle; 147 | HANDLE_CREATOR[handle_idx] = "GetDesktopWindow"; 148 | tempHandle = -1; 149 | tempUINT1 = 1; 150 | break; 151 | } 152 | } 153 | if (!tempUINT2) { 154 | tempHandle = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY); 155 | if (tempHandle == NULL || tempHandle == -1 || tempHandle == INVALID_HANDLE_VALUE) { 156 | logger("//[Handler_Function]: make_HANDLES : Ignoring invalid handle."); 157 | } 158 | else { 159 | logger("//[Handler_Function]: make_HANDLES : n = %u, handle = 0x%08X, HANDLE_CREATOR[n] = %s", handle_idx, tempHandle, "MonitorFromPoint"); 160 | HANDLES[handle_idx] = tempHandle; 161 | HANDLE_CREATOR[handle_idx] = "MonitorFromPoint"; 162 | tempHandle = -1; 163 | tempUINT2 = 1; 164 | break; 165 | } 166 | } 167 | 168 | switch(rand() % 8) { 169 | case 0: 170 | tempHandle = CreateFile(TEXT("C:\\boot.ini"), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 171 | if (tempHandle == NULL || tempHandle == -1 || tempHandle == INVALID_HANDLE_VALUE) { 172 | logger("//[Handler_Function]: make_HANDLES : Ignoring invalid handle."); 173 | } 174 | else { 175 | logger("//[Handler_Function]: make_HANDLES : n = %u, handle = 0x%08X, HANDLE_CREATOR[n] = %s", handle_idx, tempHandle, "CreateFile"); 176 | HANDLES[handle_idx] = tempHandle; 177 | HANDLE_CREATOR[handle_idx] = "CreateFile"; 178 | tempHandle = -1; 179 | } 180 | break; 181 | case 1: 182 | tempHandle = CreateSolidBrush(RGB(0, 255, 0)); 183 | if (tempHandle == NULL || tempHandle == -1 || tempHandle == INVALID_HANDLE_VALUE) { 184 | logger("//[Handler_Function]: make_HANDLES : Ignoring invalid handle."); 185 | } 186 | else { 187 | logger("//[Handler_Function]: make_HANDLES : n = %u, handle = 0x%08X, HANDLE_CREATOR[n] = %s", handle_idx, tempHandle, "CreateSolidBrush"); 188 | HANDLES[handle_idx] = tempHandle; 189 | HANDLE_CREATOR[handle_idx] = "CreateSolidBrush"; 190 | tempHandle = -1; 191 | } 192 | break; 193 | case 2: 194 | tempHandle = FindWindow(NULL, TEXT("Explorer")); 195 | if (tempHandle == NULL || tempHandle == -1 || tempHandle == INVALID_HANDLE_VALUE) { 196 | logger("//[Handler_Function]: make_HANDLES : Ignoring invalid handle."); 197 | } 198 | else { 199 | logger("//[Handler_Function]: make_HANDLES : n = %u, handle = 0x%08X, HANDLE_CREATOR[n] = %s", handle_idx, tempHandle, "FindWindow"); 200 | HANDLES[handle_idx] = tempHandle; 201 | HANDLE_CREATOR[handle_idx] = "FindWindow"; 202 | tempHandle = -1; 203 | } 204 | break; 205 | case 3: 206 | tempHandle = CreateFont(46, 28, 215, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN, "Times New Roman"); 207 | if (tempHandle == NULL || tempHandle == -1 || tempHandle == INVALID_HANDLE_VALUE) { 208 | logger("//[Handler_Function]: make_HANDLES : Ignoring invalid handle."); 209 | } 210 | else { 211 | logger("//[Handler_Function]: make_HANDLES : n = %u, handle = 0x%08X, HANDLE_CREATOR[n] = %s", handle_idx, tempHandle, "CreateFont"); 212 | HANDLES[handle_idx] = tempHandle; 213 | HANDLE_CREATOR[handle_idx] = "CreateFont"; 214 | tempHandle = -1; 215 | } 216 | break; 217 | case 4: 218 | tempHandle = CreateBitmapIndirect(&bmp); 219 | if (tempHandle == NULL || tempHandle == -1 || tempHandle == INVALID_HANDLE_VALUE) { 220 | logger("//[Handler_Function]: make_HANDLES : Ignoring invalid handle."); 221 | } 222 | else { 223 | logger("//[Handler_Function]: make_HANDLES : n = %u, handle = 0x%08X, HANDLE_CREATOR[n] = %s", handle_idx, tempHandle, "CreateBitmapIndirect"); 224 | HANDLES[handle_idx] = tempHandle; 225 | HANDLE_CREATOR[handle_idx] = "CreateBitmapIndirect"; 226 | tempHandle = -1; 227 | } 228 | break; 229 | case 5: 230 | tempHandle = GlobalAlloc(GMEM_FIXED, 10); 231 | if (tempHandle == NULL || tempHandle == -1 || tempHandle == INVALID_HANDLE_VALUE) { 232 | logger("//[Handler_Function]: make_HANDLES : Ignoring invalid handle."); 233 | } 234 | else { 235 | logger("//[Handler_Function]: make_HANDLES : n = %u, handle = 0x%08X, HANDLE_CREATOR[n] = %s", handle_idx, tempHandle, "GlobalAlloc"); 236 | HANDLES[handle_idx] = tempHandle; 237 | HANDLE_CREATOR[handle_idx] = "GlobalAlloc"; 238 | tempHandle = -1; 239 | } 240 | break; 241 | case 6: 242 | RegOpenCurrentUser(KEY_READ, &tempHandle); 243 | if (tempHandle == NULL || tempHandle == -1 || tempHandle == INVALID_HANDLE_VALUE) { 244 | logger("//[Handler_Function]: make_HANDLES : Ignoring invalid handle."); 245 | } 246 | else { 247 | logger("//[Handler_Function]: make_HANDLES : n = %u, handle = 0x%08X, HANDLE_CREATOR[n] = %s", handle_idx, tempHandle, "RegOpenCurrentUser"); 248 | HANDLES[handle_idx] = tempHandle; 249 | HANDLE_CREATOR[handle_idx] = "RegOpenCurrentUser"; 250 | tempHandle = -1; 251 | } 252 | break; 253 | case 7: 254 | tempHandle = OpenNotepad(); 255 | if (tempHandle == NULL || tempHandle == -1 || tempHandle == INVALID_HANDLE_VALUE) { 256 | logger("//[Handler_Function]: make_HANDLES : Ignoring invalid handle."); 257 | } 258 | else { 259 | logger("//[Handler_Function]: make_HANDLES : n = %u, handle = 0x%08X, HANDLE_CREATOR[n] = %s", handle_idx, tempHandle, "OpenNotepad"); 260 | HANDLES[handle_idx] = tempHandle; 261 | HANDLE_CREATOR[handle_idx] = "OpenNotepad"; 262 | tempHandle = -1; 263 | } // if 264 | break; 265 | } // switch 266 | } // while 267 | } // for 268 | 269 | // Alternatively, increment every iteration of the for loop. 270 | // Array has been populated from 0 to 64, i.e. first available slot is 64. 271 | HANDLES_ARRAY_AVAILABLE_SLOT_INDEX = 64; 272 | 273 | 274 | } 275 | 276 | /* HELPER FUNCTIONS */ 277 | 278 | HWND OpenNotepad(void) { 279 | 280 | 281 | char filename[1024]; 282 | FILE *f; 283 | char buf[1024]; 284 | HANDLE tempHandle; 285 | int tempINT; 286 | tempINT = rand() % 1000; 287 | 288 | // Generate a random filename. 289 | sprintf_s(filename, 1023, "f%d.txt", tempINT); 290 | 291 | // Create file. 292 | fopen_s(&f, filename, "w"); 293 | fclose(f); 294 | 295 | // Open file in notepad. 296 | tempHandle = ShellExecuteA(0, "open", "notepad.exe", filename, 0, SW_SHOW); 297 | 298 | if (tempHandle < 33) { 299 | logger("//[Handler Function]: OpenNotepad: Failed to start notepad."); 300 | return 0; 301 | } 302 | 303 | sprintf_s(buf, 1024, "f%d.txt - Notepad", tempINT); 304 | 305 | 306 | //Dirty++ 307 | while(1) { 308 | tempHandle = FindWindow(0, buf); 309 | if (tempHandle) { 310 | return tempHandle; 311 | } 312 | 313 | logger("//[Handler Function]: OpenNotepad: Cannot find handle."); 314 | } 315 | return 0; 316 | } 317 | 318 | 319 | #endif /* HANDLES_DATABASE_H */ 320 | -------------------------------------------------------------------------------- /helpers.h: -------------------------------------------------------------------------------- 1 | #ifndef HELPERS_H 2 | #define HELPERS_H 3 | 4 | #include // ShellExecuteA(); 5 | #include 6 | #include 7 | 8 | /* Prototypes */ 9 | // Helpers returning various structs. 10 | MENUITEMINFO get_MENUITEMINFO(); 11 | // This is used for getting current time in milliseconds. The function is used for appending unique identifiers to variable names. 12 | //WORD get_time_in_ms(void); 13 | // Various callbacks. 14 | 15 | 16 | /* Implementations */ 17 | 18 | 19 | POINT get_POINT(char *vid) { 20 | 21 | POINT p; 22 | 23 | logger("POINT p%s;", vid); 24 | 25 | /* 26 | typedef struct tagPOINT { 27 | LONG x; 28 | LONG y; 29 | } POINT, *PPOINT; 30 | */ 31 | 32 | p.x = get_fuzzed_int32(); 33 | p.y = get_fuzzed_int32(); 34 | 35 | logger("p%s.x = %d;", vid, p.x); 36 | logger("p%s.y = %d;", vid, p.y); 37 | 38 | logger("//[Helper Function] get_POINT"); 39 | 40 | return p; 41 | } 42 | 43 | WORD get_time_in_ms(void) { 44 | SYSTEMTIME time; 45 | WORD millis; 46 | GetSystemTime(&time); 47 | 48 | millis = (time.wSecond * 1000) + time.wMilliseconds; 49 | return millis; 50 | } 51 | 52 | #endif /* HELPERS_H */ -------------------------------------------------------------------------------- /hooking.h: -------------------------------------------------------------------------------- 1 | /* This is still highly experimental, not to mention logging isn't implemented. */ 2 | 3 | /* Prototypes */ 4 | 5 | LRESULT CALLBACK BH_HookCallback(int code, WPARAM wParam, LPARAM lParam); 6 | HHOOK WINAPI BH_SetWindowsHookEx(); 7 | BOOL WINAPI BH_UnhookWindowsHookEx(); 8 | 9 | /* VARIABLES */ 10 | 11 | HHOOK hhk; // Variable used for preserving the hook, this is used to unhook after a function call. 12 | // Remember to declare this variable in the reproducer as either global or scoped to main. 13 | 14 | /* Implementations */ 15 | 16 | // This is still under heavy development, expect problems. 17 | 18 | // https://msdn.microsoft.com/en-gb/library/windows/desktop/ms644990%28v=vs.85%29.aspx 19 | // https://github.com/sam-b/CVE-2014-4113/blob/master/Trigger/Trigger/Trigger.cpp 20 | 21 | 22 | BOOL WINAPI BH_UnhookWindowsHookEx() { 23 | // Call with the global variable, current design allows only one hook to exist at a given moment. 24 | //logger("//[Callback] BH_UnhookWindowsHookEx"); 25 | logger("UnhookWindowsHookEx(hhk);"); 26 | UnhookWindowsHookEx(hhk); 27 | logger("hhk = NULL;"); 28 | hhk = NULL; 29 | } -------------------------------------------------------------------------------- /library_calls.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBRARY_CALLS_H 2 | #define LIBRARY_CALLS_H 3 | 4 | #include "helpers.h" 5 | // Include the actual implementations. 6 | #include "library_calls/brush.h" 7 | 8 | 9 | 10 | // Include any Windows header files in the respective file instead of adding it here. 11 | 12 | // Function Prototypes 13 | // grep '^VOID BH_' library_calls.h |grep '{$'|awk '{gsub(" {", ";");print $0}' 14 | 15 | // Blacklisted Functions 16 | // BH_SetMapperFlags, BH_LockWindowUpdate, BH_GetMessage, BH_GetCountColorProfileElements, BH_DeleteColorTransform, BH_GetCMMInfo, BH_CreateWindowStation, BH_CreateBrushIndirect, BH_SetClipboardData, BH_SwitchDesktop, BH_LineDDA, BH_CloseColorProfile, BH_WaitForSingleObject, BH_DrawEdge, BH_GetSystemPaletteEntries, BH_DispatchMessage, BH_WcsCreateIccProfile, BH_DrawText, BH_ExtTextOut 17 | 18 | void(*LIBRARY_CALLS[]) () = { BH_GetSysColorBrush 19 | }; 20 | 21 | /* Moved here from bughunt.h to get around compilation error. */ 22 | void (*random_LIBRARY_CALL()) (void) 23 | { 24 | unsigned int n = sizeof (LIBRARY_CALLS) / sizeof (LIBRARY_CALLS[0]); 25 | return LIBRARY_CALLS[rand() % n]; 26 | } 27 | 28 | // Stuff to Remember and Handy Tips 29 | // When calling a function, pass a handle by its value. When logging, log by its index in the handles database. 30 | // Consider changing the prototypes of the functions so they all take a single VID argument. VID will be the string to append to the variable names. That way, we'll populate VID only once in the code in the bughunt_thread loop, instead of having it initialised within every single function. 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /library_calls/brush.h: -------------------------------------------------------------------------------- 1 | /* Brush Functions START */ 2 | 3 | 4 | VOID BH_GetSysColorBrush() { 5 | 6 | HBRUSH result_BH_GetSysColorBrush; 7 | int tempInt_BH_GetSysColorBrush; 8 | 9 | char vid[16]; 10 | sprintf(vid, "%d%d", get_time_in_ms(), rand() % 1024); 11 | logger("HBRUSH result_BH_GetSysColorBrush%s;", vid); 12 | logger("int tempInt_BH_GetSysColorBrush%s;", vid); 13 | 14 | tempInt_BH_GetSysColorBrush = get_fuzzed_int32(); 15 | logger("tempInt_BH_GetSysColorBrush%s = %d;", vid, tempInt_BH_GetSysColorBrush); 16 | 17 | logger("//[Library_Call]: BH_GetSysColorBrush"); 18 | logger("result_BH_GetSysColorBrush%s = GetSysColorBrush(tempInt_BH_GetSysColorBrush%s);", vid, vid); 19 | result_BH_GetSysColorBrush = GetSysColorBrush(tempInt_BH_GetSysColorBrush); 20 | 21 | logger("put_random_HANDLE(result_BH_GetSysColorBrush%s, TEXT(\"BH_GetSysColorBrush\"));", vid); 22 | put_random_HANDLE(result_BH_GetSysColorBrush, TEXT("BH_GetSysColorBrush")); 23 | } 24 | 25 | 26 | 27 | /* Brush Functions END */ -------------------------------------------------------------------------------- /logger.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // Global variable to store the log filename. 7 | char logfilename[128]; 8 | // Whether this filename has been set. 9 | int flag = 0; 10 | 11 | VOID logger(const char* fmt, ...) 12 | { 13 | 14 | FILE *stream = NULL; 15 | 16 | va_list args; 17 | va_start(args, fmt); 18 | 19 | if (!flag) { 20 | // Set the log file name. 21 | //snprintf(logfilename, 128, "log.%d.txt", time(NULL) + GetCurrentProcessId() + GetCurrentThreadId()); 22 | sprintf(logfilename, "log.%d.c", time(NULL) + GetCurrentProcessId() + GetCurrentThreadId()); 23 | flag = 1; 24 | } 25 | 26 | // Open our log file. 27 | if ((stream = fopen(logfilename, "a+")) == NULL) { 28 | printf("Error! Cannot open log file, exiting..."); 29 | exit(1); 30 | } 31 | 32 | // Write to file. 33 | if (vfprintf(stream, fmt, args) < 0) { 34 | printf("Error! Cannot write to log file, exiting..."); 35 | exit(1); 36 | } 37 | fprintf(stream, "\n"); 38 | 39 | // Flush content to log file and close handle. 40 | fflush(stream); 41 | fclose(stream); 42 | 43 | // Print to standard out as well. 44 | // Check for error conditions. 45 | if (vfprintf(stdout, fmt, args) < 0) { 46 | printf("Error! Cannot write to standard output, exiting..."); 47 | exit(1); 48 | } 49 | fprintf(stdout, "\n"); 50 | 51 | va_end(args); 52 | } -------------------------------------------------------------------------------- /reproducer/build_template.bat: -------------------------------------------------------------------------------- 1 | call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" amd64 2 | cl.exe /w /Zi /Tc template.c gdi32.lib kernel32.lib User32.lib Advapi32.lib Shell32.lib Msimg32.lib Dxva2.lib Mscms.lib ..\bughunt_syscall_x64.obj -------------------------------------------------------------------------------- /reproducer/template.c: -------------------------------------------------------------------------------- 1 | #include "..\logger.h" 2 | #include "..\bughunt_thread.h" //To avoid the pain that is our dependency hell. Fix this! 3 | 4 | /* I know it's filthy but we can currently build this as follows. 5 | 6 | cl.exe /w /Zi /Tc template.c gdi32.lib kernel32.lib User32.lib Advapi32.lib Shell32.lib Msimg32.lib Dxva2.lib Mscms.lib ..\bughunt_syscall_x64.obj 7 | 8 | */ 9 | 10 | //Windows specific files required for this to work. 11 | // These may not be needed. 12 | #include 13 | #include 14 | 15 | static unsigned int syscall_count; 16 | 17 | int main(int argc, char* argv[]) 18 | { 19 | // Change the following and other variables to plain integers, no one really needs this sloppy DWORDs and all the casting. 20 | //DWORD exit_code = ((DWORD)0xDEADBEEF); 21 | 22 | make_HANDLES(); //We pray and hope that this works correctly. If it doesn't... well, tough. 23 | //From here, we paste the log file in. This should be semi valid C... Hopefully! 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | //return (exit_code); 41 | return 0; 42 | } -------------------------------------------------------------------------------- /worker_setup/dbg_amd64_6.12.2.633.msi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FSecureLABS/KernelFuzzer/9e73157bcff78a100f0699eab2b55c576610ac33/worker_setup/dbg_amd64_6.12.2.633.msi -------------------------------------------------------------------------------- /worker_setup/dbg_x86_6.12.2.633.msi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FSecureLABS/KernelFuzzer/9e73157bcff78a100f0699eab2b55c576610ac33/worker_setup/dbg_x86_6.12.2.633.msi -------------------------------------------------------------------------------- /worker_setup/disable_lockscreen.reg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FSecureLABS/KernelFuzzer/9e73157bcff78a100f0699eab2b55c576610ac33/worker_setup/disable_lockscreen.reg -------------------------------------------------------------------------------- /worker_setup/disable_uac.reg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FSecureLABS/KernelFuzzer/9e73157bcff78a100f0699eab2b55c576610ac33/worker_setup/disable_uac.reg -------------------------------------------------------------------------------- /worker_setup/disable_windows_error_reporting.reg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FSecureLABS/KernelFuzzer/9e73157bcff78a100f0699eab2b55c576610ac33/worker_setup/disable_windows_error_reporting.reg -------------------------------------------------------------------------------- /worker_setup/disable_windows_updates.reg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FSecureLABS/KernelFuzzer/9e73157bcff78a100f0699eab2b55c576610ac33/worker_setup/disable_windows_updates.reg -------------------------------------------------------------------------------- /worker_setup/worker_setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import subprocess 3 | import os 4 | 5 | from ctypes import windll 6 | 7 | # This script prepares a worker for fuzzing. 8 | 9 | # Execute as administrator. 10 | 11 | def install_windbg(): 12 | print("[!] Installing WinDbg...") 13 | subprocess.call(["C:\\Windows\\System32\\msiexec.exe", "/i", "dbg_amd64_6.12.2.633.msi", "/qn"]) 14 | 15 | def install_couch_module(): 16 | print("[!] Installing CouchDB Python module...") 17 | try: 18 | # Quick and dirty, one of these two should work. 19 | subprocess.call(["C:\\Python35\\Scripts\\pip.exe", "install", "couchdb"]) 20 | subprocess.call(["C:\\Python35\\python.exe", "-m", "pip", "install", "couchdb"]) 21 | except: 22 | pass 23 | 24 | def change_registry(): 25 | print("[!] Disabling UAC...") 26 | subprocess.call(["C:\\Windows\\System32\\reg.exe", "import", "disable_uac.reg"]) 27 | print("[!] Disabling Lock Screen...") 28 | subprocess.call(["C:\\Windows\\System32\\reg.exe", "import", "disable_lockscreen.reg"]) 29 | print("[!] Disabling Windows Error Reporting...") 30 | subprocess.call(["C:\\Windows\\System32\\reg.exe", "import", "disable_windows_error_reporting.reg"]) 31 | print("[!] Disabling Windows Updates...") 32 | subprocess.call(["C:\\Windows\\System32\\reg.exe", "import", "disable_windows_updates.reg"]) 33 | 34 | def enable_kernel_dumps(): 35 | print("[!] Enabling Small Memory Dumps...") 36 | 37 | dump_dir = "C:\\Dumps" 38 | #dump_path = dump_dir + "\\MEMORY.DMP" 39 | 40 | # Create dump dir. 41 | if not os.path.exists(dump_dir): 42 | os.mkdir(dump_dir) 43 | 44 | # Set DebugInfoType to 3 for small memory dumps. 45 | # https://support.microsoft.com/en-gb/kb/307973 46 | #This does not get what we want, so we set to 2 for kernel dumps. 47 | subprocess.call(["wmic","recoveros","set","DebugInfoType=2"]) 48 | 49 | # Set memory dump path. 50 | #subprocess.call(["wmic","recoveros","set",'DebugFilePath="' + dump_dir + '"']) 51 | # Set minidump path. 52 | subprocess.call(["wmic","recoveros","set","MiniDumpDirectory=%s" % dump_dir]) 53 | 54 | def schedule_task(): 55 | print("[!] Scheduling Task...") 56 | # We replace a shortcut to our wrapper in the Startup folder with a scheduled task. 57 | # C:\Users\munmap>schtasks /create /tn "Bug Hunter" /tr C:\Users\munmap\Desktop\bughunt_subprocess_types\bughunt_loop.py /sc onstart 58 | # SUCCESS: The scheduled task "Bug Hunter" has successfully been created. 59 | # 60 | # C:\Users\munmap> 61 | 62 | #subprocess.call(["C:\\Windows\\System32\\schtasks", "/create", "/tn", "Bug Hunter", "/tr", "%s\\..\\bughunt_loop.py" % os.getcwd(), "/sc", "onstart"]) 63 | subprocess.call(["C:\\Windows\\System32\\schtasks", "/create", "/tn", "Bug Hunter", "/tr", "%s\\..\\bughunt_loop.py" % os.getcwd(), "/sc", "onlogon"]) 64 | 65 | # C:\Users\munmap>schtasks /delete /TN "Bug Hunter" 66 | # WARNING: Are you sure you want to remove the task "Bug Hunter" (Y/N)? Y 67 | # SUCCESS: The scheduled task "Bug Hunter" was successfully deleted. 68 | # 69 | # C:\Users\munmap>schtasks 70 | 71 | def enable_special_pool(): 72 | print("[!] Enabling Special Pool...") 73 | # Enable special pool for win32k.sys. 74 | # https://msdn.microsoft.com/en-gb/library/windows/hardware/ff556083%28v=vs.85%29.aspx 75 | subprocess.call(["C:\\Windows\\System32\\verifier.exe", "/driver", "win32k.sys", "/flags", "0x00000001"]) 76 | 77 | def enable_kernel_dbg(): 78 | # Enable kernel debugging. 79 | print("[!] Enabling Kernel Debugging...") 80 | subprocess.call(["C:\\Windows\\System32\\bcdedit","/debug","on"]) 81 | 82 | def reboot_system(): 83 | print("[!] Rebooting in 5 seconds...") 84 | subprocess.call(['shutdown', '-r', '-t', '5']) 85 | 86 | def main(): 87 | 88 | #kdll = windll.LoadLibrary("kernel32.dll") 89 | 90 | if not windll.shell32.IsUserAnAdmin(): 91 | print("This script must be ran as admin!") 92 | sys.exit(1) 93 | 94 | install_windbg() 95 | install_couch_module() 96 | change_registry() 97 | enable_kernel_dumps() 98 | schedule_task() 99 | enable_special_pool() 100 | #enable_kernel_dbg() # Not needed anymore. 101 | reboot_system() 102 | 103 | # Leaving an artefact to know this has been executed. 104 | # open(".setup",'a').close() 105 | 106 | if __name__ == "__main__": 107 | main() 108 | --------------------------------------------------------------------------------