├── .gitignore ├── LICENSE ├── README.md └── heap tracing ├── blogpost_scripts ├── heap_trace_v1.py ├── pykd_hello_world.py └── soft_hooking.py ├── heap_trace.js ├── heap_trace.py ├── ie_execcommand_uaf.zip ├── overhead testing ├── debugger_runtime.py └── heaptrace_runtime.py ├── sample.html ├── sample.log ├── screenshots ├── pykd_heap_trace_v1.PNG ├── pykd_heap_trace_villoc_example.PNG ├── pykd_hello_world.PNG └── pykd_soft_hooking.PNG └── villoc.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Sphinx documentation 53 | docs/_build/ 54 | 55 | # PyBuilder 56 | target/ 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Sam Brown 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # windbg-plugins 2 | Repository for any useful windbg plugins I've written. 3 | 4 | #heap_trace 5 | Hooks heap operations and tracks their arguments and return values. 6 | Run: 7 |
8 | .load pykd.pyd 9 | !py "PATH_TO_REPO\heap_trace.py" 10 |11 | This will log to your home directory as log.log. You can then create a villoc visualisation of this by running: 12 |
13 | python villoc.py log.log out.html 14 |15 | Example villoc output: 16 |  17 | #Requirements 18 | All plugins use the [pykd](https://pykd.codeplex.com/) python interface for windbg. -------------------------------------------------------------------------------- /heap tracing/blogpost_scripts/heap_trace_v1.py: -------------------------------------------------------------------------------- 1 | import pykd 2 | 3 | return_reg = "rax" 4 | stack_pointer = "rsp" 5 | 6 | def get_address(localAddr): 7 | res = pykd.dbgCommand("x " + localAddr) 8 | result_count = res.count("\n") 9 | if result_count == 0: 10 | print localAddr + " not found." 11 | return None 12 | if result_count > 1: 13 | print "[-] Warning, more than one result for", localAddr 14 | return res.split()[0] 15 | 16 | #RtlAllocateHeap( 17 | # IN PVOID HeapHandle, 18 | # IN ULONG Flags, 19 | # IN ULONG Size ); 20 | class handle_allocate_heap(pykd.eventHandler): 21 | def __init__(self): 22 | addr = get_address("ntdll!RtlAllocateHeap") 23 | if addr == None: 24 | return 25 | self.bp_init = pykd.setBp(int(addr, 16), self.enter_call_back) 26 | self.bp_end = None 27 | 28 | def enter_call_back(self,bp): 29 | self.out = "RtlAllocateHeap(" 30 | esp = pykd.reg("esp") 31 | self.out += hex(pykd.ptrPtr(esp + 4)) + " , " 32 | self.out += hex(pykd.ptrMWord(esp + 0x8)) + " , " 33 | self.out += hex(pykd.ptrMWord(esp + 0xC)) + ") = " 34 | if self.bp_end == None: 35 | disas = pykd.dbgCommand("uf ntdll!RtlAllocateHeap").split('\n') 36 | for i in disas: 37 | if 'ret' in i: 38 | self.ret_addr = i.split()[0] 39 | break 40 | self.bp_end = pykd.setBp(int(self.ret_addr, 16), self.return_call_back) 41 | return False 42 | 43 | def return_call_back(self,bp): 44 | self.out += hex(pykd.reg(return_reg)) 45 | print self.out 46 | return False 47 | 48 | try: 49 | pykd.reg("rax") 50 | except: 51 | return_reg = "eax" 52 | stack_pointer = "esp" 53 | 54 | handle_allocate_heap() 55 | pykd.go() -------------------------------------------------------------------------------- /heap tracing/blogpost_scripts/pykd_hello_world.py: -------------------------------------------------------------------------------- 1 | import pykd 2 | 3 | print pykd.dbgCommand("!teb") -------------------------------------------------------------------------------- /heap tracing/blogpost_scripts/soft_hooking.py: -------------------------------------------------------------------------------- 1 | import pykd 2 | 3 | def get_address(localAddr): 4 | res = pykd.dbgCommand("x " + localAddr) 5 | result_count = res.count("\n") 6 | if result_count == 0: 7 | print localAddr + " not found." 8 | return None 9 | if result_count > 1: 10 | print "[-] Warning, more than one result for", localAddr 11 | return res.split()[0] 12 | 13 | class handle_allocate_heap(pykd.eventHandler): 14 | def __init__(self): 15 | addr = get_address("ntdll!RtlAllocateHeap") 16 | if addr == None: 17 | return 18 | self.bp_init = pykd.setBp(int(addr, 16), self.enter_call_back) 19 | self.bp_end = None 20 | 21 | def enter_call_back(self,bp): 22 | print "RtlAllocateHeap called." 23 | if self.bp_end == None: 24 | disas = pykd.dbgCommand("uf ntdll!RtlAllocateHeap").split('\n') 25 | for i in disas: 26 | if 'ret' in i: 27 | self.ret_addr = i.split()[0] 28 | break 29 | self.bp_end = pykd.setBp(int(self.ret_addr, 16), self.return_call_back) 30 | return False 31 | 32 | def return_call_back(self,bp): 33 | print "RtlAllocateHeap returned." 34 | return False 35 | 36 | handle_allocate_heap() 37 | pykd.go() -------------------------------------------------------------------------------- /heap tracing/heap_trace.js: -------------------------------------------------------------------------------- 1 | /* 2 | Uses windbg's JavaScript scripting support to trace user mode memory allocations in a format viewable with villoc 3 | Heavily based off of https://doar-e.github.io/blog/2017/12/01/debugger-data-model/ 4 | */ 5 | 6 | "use strict"; 7 | 8 | var archBits = 0; 9 | var returnReg = null; 10 | var stackPointer = null; 11 | var allocateHeapOut = null; 12 | var reallocateHeapOut = null; 13 | var freeHeapOut = null; 14 | 15 | function hex(val) { 16 | return val.toString(16); 17 | } 18 | 19 | function print(msg) { 20 | host.diagnostics.debugLog(msg); 21 | } 22 | 23 | function findRetAddrs(mod, name){ 24 | var addrs = []; 25 | for(var line of host.namespace.Debugger.Utility.Control.ExecuteCommand("uf " + mod + ':' + name)){ 26 | if(line.includes('ret')){ 27 | var addr = host.parseInt64(line.split(" ")[0], 16); 28 | addrs.push(addr); 29 | } 30 | } 31 | return addrs; 32 | } 33 | /*#RtlAllocateHeap( 34 | # IN PVOID HeapHandle, 35 | # IN ULONG Flags, 36 | # IN ULONG Size );*/ 37 | function handleAllocateHeap() { 38 | var regs = host.currentThread.Registers.User; 39 | var out = "RtlAllocateHeap("; 40 | var args = null; 41 | if(archBits == 32){ 42 | args = host.memory.readMemoryValues(regs[stackPointer] + 4, 3, 4); 43 | } else { 44 | args = [regs.rcx, regs.rdx, regs.r8]; 45 | } 46 | out += hex(args[0]) + ", "; 47 | out += hex(args[1]) + ", "; 48 | out += hex(args[2]) + ") = "; 49 | allocateHeapOut = out; 50 | return false; 51 | } 52 | 53 | /* #RtlReAllocateHeap( 54 | #IN PVOID HeapHandle, 55 | #IN ULONG Flags, 56 | # IN PVOID MemoryPointer, 57 | # IN ULONG Size ); 58 | */ 59 | function handleReAllocateHeap() { 60 | var regs = host.currentThread.Registers.User; 61 | var out = "RtlReAllocateHeap("; 62 | var args = null; 63 | if(archBits == 32){ 64 | args = host.memory.readMemoryValues(regs[stackPointer] + 4, 4, 4); 65 | } else { 66 | args = [regs.rcx, regs.rdx, regs.r8, regs.r9]; 67 | } 68 | out += hex(args[0]) + ", "; 69 | out += hex(args[1]) + ", "; 70 | out += hex(args[2]) + ", "; 71 | out += hex(args[3]) + ") = "; 72 | reallocateHeapOut = out; 73 | return false; 74 | } 75 | 76 | /*#RtlFreeHeap( 77 | #IN PVOID HeapHandle, 78 | #IN ULONG Flags OPTIONAL, 79 | #IN PVOID MemoryPointer );*/ 80 | function handleFreeHeap() { 81 | var regs = host.currentThread.Registers.User; 82 | var out = "RtlFreeHeap("; 83 | var args = null; 84 | if(archBits == 32){ 85 | args = host.memory.readMemoryValues(regs[stackPointer] + 4, 3, 4); 86 | } else { 87 | args = [regs.rcx, regs.rdx, regs.r8]; 88 | } 89 | out += hex(args[0]) + ", "; 90 | out += hex(args[1]) + ", "; 91 | out += hex(args[2]) + ") = "; 92 | freeHeapOut = out; 93 | return false; 94 | } 95 | 96 | function handleAllocateHeapRet() { 97 | var regs = host.currentThread.Registers.User; 98 | if(allocateHeapOut != null){ 99 | print(allocateHeapOut + hex(regs[returnReg]) + "\r\n"); 100 | allocateHeapOut = null; 101 | } 102 | return false; 103 | } 104 | 105 | function handleReAllocateHeapRet() { 106 | var regs = host.currentThread.Registers.User; 107 | if(reallocateHeapOut != null){ 108 | print(reallocateHeapOut + hex(regs[returnReg]) + "\r\n"); 109 | reallocateHeapOut = null; 110 | } 111 | return false; 112 | } 113 | 114 | function handleFreeHeapRet() { 115 | var regs = host.currentThread.Registers.User; 116 | if(freeHeapOut != null){ 117 | print(freeHeapOut + hex(regs[returnReg]) + "\r\n"); 118 | freeHeapOut = null; 119 | } 120 | return false; 121 | } 122 | 123 | function invokeScript() { 124 | try { 125 | host.currentThread.Registers.User.rax; 126 | archBits = 64; 127 | returnReg = "rax"; 128 | stackPointer = "rsp"; 129 | } catch (e) { 130 | archBits = 32; 131 | returnRed = "eax"; 132 | stackPointer = "esp"; 133 | } 134 | 135 | print("Running on a " + archBits + "-bit process.\r\n"); 136 | 137 | var RtlAllocateHeap = host.getModuleSymbolAddress('ntdll', 'RtlAllocateHeap'); 138 | var RtlFreeHeap = host.getModuleSymbolAddress('ntdll', 'RtlFreeHeap'); 139 | var RtlReAllocateHeap = host.getModuleSymbolAddress('ntdll', 'RtlReAllocateHeap'); 140 | 141 | print('Hooking:\r\n\tRltAllocateHeap: ' + hex(RtlAllocateHeap) + "\r\n\tRtlFreeHeap: " + hex(RtlFreeHeap) + "\r\n\tRtlReAllocateHeap: " + hex(RtlReAllocateHeap) + "\r\n"); 142 | var breakpointsAlreadySet = host.currentProcess.Debug.Breakpoints.Any( 143 | bp => bp.Address == RtlAllocateHeap || bp.Address == RtlFreeHeap || by.Address == RtlReAllocateHeap 144 | ); 145 | if(breakpointsAlreadySet == false) { 146 | host.namespace.Debugger.Utility.Control.ExecuteCommand('bp /w "@$scriptContents.handleAllocateHeap()" ' + RtlAllocateHeap.toString(16)); 147 | host.namespace.Debugger.Utility.Control.ExecuteCommand('bp /w "@$scriptContents.handleFreeHeap()" ' + RtlFreeHeap.toString(16)); 148 | host.namespace.Debugger.Utility.Control.ExecuteCommand('bp /w "@$scriptContents.handleReAllocateHeap()" ' + RtlReAllocateHeap.toString(16)); 149 | } 150 | var RtlAllocateHeapRet = findRetAddrs("ntdll", "RtlAllocateHeap"); 151 | var RtlFreeHeapRet = findRetAddrs('ntdll', 'RtlFreeHeap'); 152 | var RtlReAllocateHeapRet = findRetAddrs('ntdll', 'RtlReAllocateHeap'); 153 | 154 | print('\tRltAllocateHeapRet: ' + hex(RtlAllocateHeapRet) + "\r\n\tRtlFreeHeapRet: " + hex(RtlFreeHeapRet) + "\r\n\tRtlReAllocateHeapRet: " + hex(RtlReAllocateHeapRet) + "\r\n"); 155 | 156 | var retBreakpointsAlreadySet = host.currentProcess.Debug.Breakpoints.Any( 157 | bp => bp.Address in RtlAllocateHeapRet || bp.Address in RtlFreeHeapRet || bp.Address in RtlReAllocateHeapRet 158 | ); 159 | 160 | if(retBreakpointsAlreadySet == false) { 161 | for(var addr in RtlAllocateHeapRet){ 162 | host.namespace.Debugger.Utility.Control.ExecuteCommand('bp /w "@$scriptContents.handleAllocateHeapRet()" ' + RtlAllocateHeapRet[addr].toString(16)); 163 | } 164 | for(var addr in RtlFreeHeapRet){ 165 | host.namespace.Debugger.Utility.Control.ExecuteCommand('bp /w "@$scriptContents.handleFreeHeapRet()" ' + RtlFreeHeapRet[addr].toString(16)); 166 | } 167 | for(var addr in RtlReAllocateHeapRet){ 168 | host.namespace.Debugger.Utility.Control.ExecuteCommand('bp /w "@$scriptContents.handleReAllocateHeapRet()" ' + RtlReAllocateHeapRet[addr].toString(16)); 169 | } 170 | } 171 | } -------------------------------------------------------------------------------- /heap tracing/heap_trace.py: -------------------------------------------------------------------------------- 1 | import pykd 2 | from os.path import expanduser 3 | 4 | home = expanduser("~") 5 | return_reg = "rax" 6 | stack_pointer = "rsp" 7 | arch_bits = 64 8 | log = None 9 | 10 | def get_address(localAddr): 11 | res = pykd.dbgCommand("x " + localAddr) 12 | result_count = res.count("\n") 13 | if result_count == 0: 14 | print localAddr + " not found." 15 | return None 16 | if result_count > 1: 17 | print "[-] Warning, more than one result for", localAddr 18 | return res.split()[0] 19 | 20 | #RtlAllocateHeap( 21 | # IN PVOID HeapHandle, 22 | # IN ULONG Flags, 23 | # IN ULONG Size ); 24 | class handle_allocate_heap(pykd.eventHandler): 25 | def __init__(self): 26 | addr = get_address("ntdll!RtlAllocateHeap") 27 | if addr == None: 28 | return 29 | self.bp_init = pykd.setBp(int(addr, 16), self.enter_call_back) 30 | self.bp_end = None 31 | 32 | def enter_call_back(self,bp): 33 | self.out = "RtlAllocateHeap(" 34 | if arch_bits == 32: 35 | esp = pykd.reg(stack_pointer) 36 | self.out += hex(pykd.ptrPtr(esp + 4)) + " , " 37 | self.out += hex(pykd.ptrMWord(esp + 0x8)) + " , " 38 | self.out += hex(pykd.ptrMWord(esp + 0xC)) + ") = " 39 | else: 40 | self.out += hex(pykd.reg("rcx")) + " , " 41 | self.out += hex(pykd.reg("rdx")) + " , " 42 | self.out += hex(pykd.reg("r8")) + ") = " 43 | if self.bp_end == None: 44 | disas = pykd.dbgCommand("uf ntdll!RtlAllocateHeap").split('\n') 45 | for i in disas: 46 | if 'ret' in i: 47 | self.ret_addr = i.split()[0] 48 | break 49 | self.bp_end = pykd.setBp(int(self.ret_addr, 16), self.return_call_back) 50 | return False 51 | 52 | def return_call_back(self,bp): 53 | log.write(self.out + hex(pykd.reg(return_reg)) + "\n") 54 | return False 55 | 56 | #RtlFreeHeap( 57 | #IN PVOID HeapHandle, 58 | #IN ULONG Flags OPTIONAL, 59 | #IN PVOID MemoryPointer ); 60 | class handle_free_heap(pykd.eventHandler): 61 | def __init__(self): 62 | addr = get_address("ntdll!RtlFreeHeap") 63 | if addr == None: 64 | return 65 | self.bp_init = pykd.setBp(int(addr, 16), self.enter_call_back) 66 | self.bp_end = None 67 | 68 | def enter_call_back(self,bp): 69 | self.out = "RtlFreeHeap(" 70 | if arch_bits == 32: 71 | esp = pykd.reg(stack_pointer) 72 | self.out += hex(pykd.ptrPtr(esp + 4)) + " , " 73 | self.out += hex(pykd.ptrMWord(esp + 0x8)) + " , " 74 | self.out += hex(pykd.ptrPtr(esp + 0xC)) + ") = " 75 | else: 76 | self.out += hex(pykd.reg("rcx")) + " , " 77 | self.out += hex(pykd.reg("rdx")) + " , " 78 | self.out += hex(pykd.reg("r8")) + ") = " 79 | if self.bp_end == None: 80 | disas = pykd.dbgCommand("uf ntdll!RtlFreeHeap").split('\n') 81 | for i in disas: 82 | if 'ret' in i: 83 | self.ret_addr = i.split()[0] 84 | break 85 | self.bp_end = pykd.setBp(int(self.ret_addr, 16), self.return_call_back) 86 | return False 87 | 88 | def return_call_back(self,bp): 89 | #returns a BOOLEAN which is a byte under the hood 90 | ret_val = hex(pykd.reg("al")) 91 | log.write(self.out + ret_val + "\n") 92 | return False 93 | 94 | #RtlReAllocateHeap( 95 | #IN PVOID HeapHandle, 96 | #IN ULONG Flags, 97 | # IN PVOID MemoryPointer, 98 | # IN ULONG Size ); 99 | 100 | class handle_realloc_heap(pykd.eventHandler): 101 | def __init__(self): 102 | addr = get_address("ntdll!RtlReAllocateHeap") 103 | if addr == None: 104 | return 105 | self.bp_init = pykd.setBp(int(addr, 16), self.enter_call_back) 106 | self.bp_end = None 107 | 108 | def enter_call_back(self,bp): 109 | self.out = "RtlReAllocateHeap(" 110 | if arch_bits == 32: 111 | esp = pykd.reg(stack_pointer) 112 | self.out += hex(pykd.ptrPtr(esp + 4)) + " , " 113 | self.out += hex(pykd.ptrMWord(esp + 0x8)) + " , " 114 | self.out += hex(pykd.ptrPtr(esp + 0xC)) + " , " 115 | self.out += hex(pykd.ptrMWord(esp + 0x10)) + ") = " 116 | else: 117 | self.out += hex(pykd.reg("rcx")) + " , " 118 | self.out += hex(pykd.reg("rdx")) + " , " 119 | self.out += hex(pykd.reg("r8")) + " , " 120 | self.out += hex(pykd.reg("r9")) + ") = " 121 | if self.bp_end == None: 122 | disas = pykd.dbgCommand("uf ntdll!RtlReAllocateHeap").split('\n') 123 | for i in disas: 124 | if 'ret' in i: 125 | self.ret_addr = i.split()[0] 126 | break 127 | self.bp_end = pykd.setBp(int(self.ret_addr, 16), self.return_call_back) 128 | return False 129 | 130 | def return_call_back(self,bp): 131 | log.write(self.out + hex(pykd.reg(return_reg)) + "\n") 132 | return False 133 | 134 | log = open(home + "\log.log","w+") 135 | 136 | try: 137 | pykd.reg("rax") 138 | except: 139 | arch_bits = 32 140 | return_reg = "eax" 141 | stack_pointer = "esp" 142 | 143 | handle_allocate_heap() 144 | handle_free_heap() 145 | handle_realloc_heap() 146 | pykd.go() -------------------------------------------------------------------------------- /heap tracing/ie_execcommand_uaf.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sam-b/windbg-plugins/00b6d5a2b7dc4b87d287015a7780d962e40e6a6f/heap tracing/ie_execcommand_uaf.zip -------------------------------------------------------------------------------- /heap tracing/overhead testing/debugger_runtime.py: -------------------------------------------------------------------------------- 1 | import pykd 2 | from os.path import expanduser 3 | import timeit 4 | 5 | start = timeit.default_timer() 6 | 7 | home = expanduser("~") 8 | return_reg = "rax" 9 | stack_pointer = "rsp" 10 | arch_bits = 64 11 | log = None 12 | def get_address(localAddr): 13 | res = pykd.dbgCommand("x " + localAddr) 14 | result_count = res.count("\n") 15 | if result_count == 0: 16 | print localAddr + " not found." 17 | return None 18 | if result_count > 1: 19 | print "[-] Warning, more than one result for", localAddr 20 | return res.split()[0] 21 | 22 | class time(pykd.eventHandler): 23 | def __init__(self): 24 | addr = get_address("jscript9!StrToDbl") 25 | if addr == None: 26 | return 27 | self.start = timeit.default_timer() 28 | self.bp_init = pykd.setBp(int(addr, 16), self.enter_call_back) 29 | def enter_call_back(self,bp): 30 | end = timeit.default_timer() 31 | print "Heap spray took: " + str(end - self.start) 32 | return True 33 | 34 | time() 35 | pykd.go() -------------------------------------------------------------------------------- /heap tracing/overhead testing/heaptrace_runtime.py: -------------------------------------------------------------------------------- 1 | import pykd 2 | from os.path import expanduser 3 | import timeit 4 | 5 | home = expanduser("~") 6 | return_reg = "rax" 7 | stack_pointer = "rsp" 8 | arch_bits = 64 9 | log = None 10 | 11 | def get_address(localAddr): 12 | res = pykd.dbgCommand("x " + localAddr) 13 | result_count = res.count("\n") 14 | if result_count == 0: 15 | print localAddr + " not found." 16 | return None 17 | if result_count > 1: 18 | print "[-] Warning, more than one result for", localAddr 19 | return res.split()[0] 20 | 21 | class start(pykd.eventHandler): 22 | def __init__(self): 23 | addr = get_address("jscript9!StrToDbl") 24 | if addr == None: 25 | return 26 | self.start = timeit.default_timer() 27 | handle_allocate_heap() 28 | handle_free_heap() 29 | handle_realloc_heap() 30 | self.bp_init = pykd.setBp(int(addr, 16), self.enter_call_back) 31 | 32 | def enter_call_back(self,bp): 33 | end = timeit.default_timer() 34 | print "Heap spray took: " + str(end - self.start) 35 | return True 36 | 37 | 38 | #RtlAllocateHeap( 39 | # IN PVOID HeapHandle, 40 | # IN ULONG Flags, 41 | # IN ULONG Size ); 42 | class handle_allocate_heap(pykd.eventHandler): 43 | def __init__(self): 44 | addr = get_address("ntdll!RtlAllocateHeap") 45 | if addr == None: 46 | return 47 | self.bp_init = pykd.setBp(int(addr, 16), self.enter_call_back) 48 | self.bp_end = None 49 | 50 | def enter_call_back(self,bp): 51 | self.out = "RtlAllocateHeap(" 52 | if arch_bits == 32: 53 | esp = pykd.reg(stack_pointer) 54 | self.out += hex(pykd.ptrPtr(esp + 4)) + " , " 55 | self.out += hex(pykd.ptrMWord(esp + 0x8)) + " , " 56 | self.out += hex(pykd.ptrMWord(esp + 0xC)) + ") = " 57 | else: 58 | self.out += hex(pykd.reg("rcx")) + " , " 59 | self.out += hex(pykd.reg("rdx")) + " , " 60 | self.out += hex(pykd.reg("r8")) + ") = " 61 | if self.bp_end == None: 62 | disas = pykd.dbgCommand("uf ntdll!RtlAllocateHeap").split('\n') 63 | for i in disas: 64 | if 'ret' in i: 65 | self.ret_addr = i.split()[0] 66 | break 67 | self.bp_end = pykd.setBp(int(self.ret_addr, 16), self.return_call_back) 68 | return False 69 | 70 | def return_call_back(self,bp): 71 | log.write(self.out + hex(pykd.reg(return_reg)) + "\n") 72 | return False 73 | 74 | #RtlFreeHeap( 75 | #IN PVOID HeapHandle, 76 | #IN ULONG Flags OPTIONAL, 77 | #IN PVOID MemoryPointer ); 78 | class handle_free_heap(pykd.eventHandler): 79 | def __init__(self): 80 | addr = get_address("ntdll!RtlFreeHeap") 81 | if addr == None: 82 | return 83 | self.bp_init = pykd.setBp(int(addr, 16), self.enter_call_back) 84 | self.bp_end = None 85 | 86 | def enter_call_back(self,bp): 87 | self.out = "RtlFreeHeap(" 88 | if arch_bits == 32: 89 | esp = pykd.reg(stack_pointer) 90 | self.out += hex(pykd.ptrPtr(esp + 4)) + " , " 91 | self.out += hex(pykd.ptrMWord(esp + 0x8)) + " , " 92 | self.out += hex(pykd.ptrPtr(esp + 0xC)) + ") = " 93 | else: 94 | self.out += hex(pykd.reg("rcx")) + " , " 95 | self.out += hex(pykd.reg("rdx")) + " , " 96 | self.out += hex(pykd.reg("r8")) + ") = " 97 | if self.bp_end == None: 98 | disas = pykd.dbgCommand("uf ntdll!RtlFreeHeap").split('\n') 99 | for i in disas: 100 | if 'ret' in i: 101 | self.ret_addr = i.split()[0] 102 | break 103 | self.bp_end = pykd.setBp(int(self.ret_addr, 16), self.return_call_back) 104 | return False 105 | 106 | def return_call_back(self,bp): 107 | #returns a BOOLEAN which is a byte under the hood 108 | ret_val = hex(pykd.reg("al")) 109 | log.write(self.out + ret_val + "\n") 110 | return False 111 | 112 | #RtlReAllocateHeap( 113 | #IN PVOID HeapHandle, 114 | #IN ULONG Flags, 115 | # IN PVOID MemoryPointer, 116 | # IN ULONG Size ); 117 | 118 | class handle_realloc_heap(pykd.eventHandler): 119 | def __init__(self): 120 | addr = get_address("ntdll!RtlReAllocateHeap") 121 | if addr == None: 122 | return 123 | self.bp_init = pykd.setBp(int(addr, 16), self.enter_call_back) 124 | self.bp_end = None 125 | 126 | def enter_call_back(self,bp): 127 | self.out = "RtlReAllocateHeap(" 128 | if arch_bits == 32: 129 | esp = pykd.reg(stack_pointer) 130 | self.out += hex(pykd.ptrPtr(esp + 4)) + " , " 131 | self.out += hex(pykd.ptrMWord(esp + 0x8)) + " , " 132 | self.out += hex(pykd.ptrPtr(esp + 0xC)) + " , " 133 | self.out += hex(pykd.ptrMWord(esp + 0x10)) + ") = " 134 | else: 135 | self.out += hex(pykd.reg("rcx")) + " , " 136 | self.out += hex(pykd.reg("rdx")) + " , " 137 | self.out += hex(pykd.reg("r8")) + " , " 138 | self.out += hex(pykd.reg("r9")) + ") = " 139 | if self.bp_end == None: 140 | disas = pykd.dbgCommand("uf ntdll!RtlReAllocateHeap").split('\n') 141 | for i in disas: 142 | if 'ret' in i: 143 | self.ret_addr = i.split()[0] 144 | break 145 | self.bp_end = pykd.setBp(int(self.ret_addr, 16), self.return_call_back) 146 | return False 147 | 148 | def return_call_back(self,bp): 149 | log.write(self.out + hex(pykd.reg(return_reg)) + "\n") 150 | return False 151 | 152 | log = open(home + "\log.log","w+") 153 | 154 | try: 155 | pykd.reg("rax") 156 | except: 157 | arch_bits = 32 158 | return_reg = "eax" 159 | stack_pointer = "esp" 160 | 161 | start() 162 | pykd.go() -------------------------------------------------------------------------------- /heap tracing/sample.html: -------------------------------------------------------------------------------- 1 | 54 | 66 | 67 |
RtlFreeHeap(0xb20000, 0x0, 0xb33eb8) = 0x1
Couldn't find block at 0xb33eb0, added marker.
RtlAllocateHeap(0x230000, 0x0, 0x3c) = 0x24c8570
RtlFreeHeap(0x230000, 0x0, 0x24cc4d8) = 0x1
Couldn't find block at 0x24cc4d0, added marker.
RtlFreeHeap(0x230000, 0x0, 0x24cc4a0) = 0x1
Couldn't find block at 0x24cc498, added marker.
RtlFreeHeap(0x230000, 0x0, 0x24744d0) = 0x1
Couldn't find block at 0x24744c8, added marker.
RtlFreeHeap(0x230000, 0x0, 0xc87d28) = 0x1
Couldn't find block at 0xc87d20, added marker.
RtlAllocateHeap(0x230000, 0x0, 0x54) = 0xc87d28
RtlAllocateHeap(0x230000, 0x0, 0x11) = 0x2468cf8
RtlAllocateHeap(0x230000, 0x0, 0x11) = 0x2468c58
RtlAllocateHeap(0x230000, 0x0, 0x48) = 0x24d94e8
RtlAllocateHeap(0x230000, 0x0, 0x4) = 0x24794c0
RtlFreeHeap(0x230000, 0x0, 0x24794c0) = 0x1
RtlFreeHeap(0x230000, 0x0, 0x24c8570) = 0x1
RtlAllocateHeap(0x230000, 0x0, 0x3c) = 0x24c8570
RtlFreeHeap(0x230000, 0x0, 0x24c8570) = 0x1
RtlAllocateHeap(0x230000, 0x0, 0x3c) = 0x24c8570
RtlFreeHeap(0x230000, 0x0, 0x24c8570) = 0x1
RtlAllocateHeap(0x230000, 0x0, 0x3c) = 0x24c8570
RtlFreeHeap(0x230000, 0x0, 0x24c8570) = 0x1
RtlAllocateHeap(0x230000, 0x0, 0x3c) = 0x24c8570
RtlFreeHeap(0x230000, 0x0, 0x24c8570) = 0x1
RtlAllocateHeap(0x230000, 0x0, 0x3c) = 0x24c8570
RtlFreeHeap(0x230000, 0x0, 0x24c8570) = 0x1
RtlAllocateHeap(0x230000, 0x0, 0x3c) = 0x24c8570
RtlFreeHeap(0x230000, 0x0, 0x24c8570) = 0x1
%s
' % html_escape(str(msg))) 363 | 364 | for msg in state.errors: 365 | out.write('%s
' % html_escape(str(msg))) 366 | 367 | out.write('