├── .gitignore ├── LICENSE ├── README.md ├── pykd.py └── x64dbgpylib.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.pyc 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2016, x64dbg 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # x64dbgpylib 2 | 3 | Port of [windbglib](https://github.com/corelan/windbglib) to [x64dbgpy](https://github.com/x64dbg/x64dbgpy), in an effort to support [mona.py](https://github.com/corelan/mona) in [x64dbg](http://x64dbg.com). 4 | 5 | **This is a work in progress, see the [issues](https://github.com/x64dbg/x64dbgpylib/issues) or [contact us](http://x64dbg.com/#contact) to find out how you can help**. In particular, [this issue](https://github.com/x64dbg/x64dbgpylib/issues/5) has the current state of support for mona commands; any help on the remaining commands would be appreciated. 6 | -------------------------------------------------------------------------------- /pykd.py: -------------------------------------------------------------------------------- 1 | import platform 2 | import traceback 3 | import array 4 | import fnmatch 5 | import x64dbgpy.pluginsdk.x64dbg as x64dbg 6 | import x64dbgpy.pluginsdk._scriptapi as script 7 | import x64dbgpy.__breakpoints as breakpoints 8 | 9 | from collections import deque 10 | 11 | version = "0.2.0.29" 12 | 13 | def notImplemented(): 14 | try: 15 | raise NotImplementedError() 16 | except: 17 | traceback.print_stack() 18 | raise 19 | 20 | def archValue(x32, x64): 21 | if is64bitSystem(): 22 | return x64 23 | return x32 24 | 25 | def is64bitSystem(): 26 | return platform.architecture()[0] == "64bit" 27 | 28 | def dbgCommand(command, suppressOutput = False): 29 | args = command.split(" ") 30 | if args[0] == "!teb": 31 | return "TEB at %x" % getImplicitThread() 32 | elif args[0] == "ba": 33 | bpaccess = args[1] 34 | addr = int(args[3], 16) 35 | bpsingleton = breakpoints.Breakpoint() 36 | if bpaccess == "e": 37 | hw_type = bpsingleton.HW_EXECUTE 38 | elif bpaccess == "r": 39 | hw_type = bpsingleton.HW_ACCESS 40 | elif bpaccess == "w": 41 | hw_type = bpsingleton.HW_WRITE 42 | else: 43 | return 44 | bpsingleton.add(addr, None, bp_type=bpsingleton.BP_MEMORY, hw_type=hw_type) 45 | return 46 | elif args[0] == "bc": 47 | bpid = int(args[1], 16) 48 | bpsingleton = breakpoints.Breakpoint() 49 | bpsingleton.remove(bpid) 50 | return 51 | elif args[0] == "bl": 52 | output = "" 53 | bpsingleton = breakpoints.Breakpoint() 54 | bpkeys = bpsingleton.list() 55 | for bpkey in bpkeys: 56 | output += "%0.8x X %0.8x \n" % (bpkey, bpkey) # address as key, dummy type 57 | return output 58 | elif args[0] == "bp": 59 | addr = int(args[1], 16) 60 | bpsingleton = breakpoints.Breakpoint() 61 | bpsingleton.add(addr, None) 62 | return 63 | elif args[0] == "eb": 64 | addr = int(args[1], 16) 65 | bytestowrite = [ int(x, 16) for x in args[2:] ] 66 | for b in bytestowrite: 67 | script.memory.WriteByte(addr, b) 68 | addr += 1 69 | return 70 | elif args[0] == "lm": 71 | output = "" 72 | modules = script.module.GetList() 73 | for m in modules: 74 | modname = "_".join(m.name.split(".")[:-1]) 75 | output += "%0.8x %0.8x %s\n" % (m.base, m.base + m.size, modname) 76 | return output 77 | elif args[0] == "ln": 78 | output = "" 79 | addr = None 80 | try: 81 | addr = int(args[1], 16) 82 | info = script.module.InfoFromAddr(addr) 83 | modulename = info.name 84 | modulebaseaddr = info.base 85 | except: 86 | functionparts = args[1].split("!") 87 | modulename = functionparts[0] 88 | functionname = None 89 | if len(functionparts) > 1: 90 | functionname = functionparts[1] 91 | info = script.module.InfoFromName(modulename) 92 | modulename = info.name # get full name 93 | modulebaseaddr = info.base 94 | symbols = list(filter(lambda s: s.mod == modulename, script.symbol.GetList())) 95 | symbols = sorted(symbols, key=lambda x: x.rva) 96 | if addr: 97 | closestsymbol = min(symbols, key=lambda s: abs(modulebaseaddr + s.rva - addr)) 98 | else: 99 | try: 100 | if functionname: 101 | closestsymbol = next(s for s in symbols if s.name == functionname) 102 | else: 103 | closestsymbol = symbols[0] 104 | except: 105 | return output 106 | closestsymbolindex = symbols.index(closestsymbol) 107 | closestsymboladdr = modulebaseaddr + closestsymbol.rva 108 | closestsymbols = [ closestsymbol ] 109 | if addr and closestsymboladdr > addr: 110 | if closestsymbolindex - 1 > 0: 111 | closestsymbols.insert(0, symbols[closestsymbolindex - 1]) 112 | elif closestsymbolindex + 1 < len(symbols): 113 | closestsymbols.append(symbols[closestsymbolindex + 1]) 114 | for i, s in enumerate(closestsymbols): 115 | if i > 0: 116 | output += " | " 117 | output += "(%0.8x) " % (modulebaseaddr + s.rva) 118 | output += "_".join(modulename.split(".")[:-1]) + "!" + s.name 119 | if closestsymboladdr == addr or functionname: 120 | output += "\nExact matches:" 121 | return output 122 | elif args[0] == "u": 123 | output = "" 124 | try: 125 | addr = int(args[1], 16) 126 | except: 127 | lnoutput = dbgCommand("ln " + args[1]) 128 | if "(" in lnoutput.lower(): 129 | lineparts = lnoutput.split(")") 130 | address = lineparts[0].replace("(", "") 131 | addr = int(address, 16) 132 | else: 133 | return output 134 | try: 135 | length = int(args[2][1:], 16) 136 | except: 137 | length = int(args[3]) 138 | disasminstr = x64dbg.DISASM_INSTR() 139 | while length > 0: 140 | x64dbg.DbgDisasmAt(addr, disasminstr) 141 | instrsize = disasminstr.instr_size 142 | instrbytes = ''.join('%02x' % ord(c) for c in loadBytes(addr, instrsize)) 143 | output += "\n%0.8x %-15s %s\n" % (addr, instrbytes, disasminstr.instruction) 144 | addr += instrsize 145 | length -= 1 146 | return output 147 | elif args[0] == "ub": 148 | output = "" 149 | addr = int(args[1], 16) 150 | length = int(args[2][1:], 16) 151 | disasminstr = x64dbg.DISASM_INSTR() 152 | deq = deque() # stores the base addresses of the found instructions 153 | deq.append(addr) 154 | banned = {} 155 | banned[addr] = [] 156 | # loop end is just a guess, observed closer results to windbg 157 | while len(deq) < (2 * length + 2): 158 | x64dbg.DbgDisasmAt(addr, disasminstr) 159 | endaddr = addr + disasminstr.instr_size 160 | # check if instruction expands till another base address 161 | if endaddr in deq and addr not in banned[endaddr]: 162 | # remove base addresses if already covered by this instruction 163 | while deq[-1] != endaddr: 164 | deq.pop() 165 | deq.append(addr) 166 | banned[addr] = [] 167 | elif deq[-1] - addr > 15: 168 | if len(deq) == 1: 169 | return "" 170 | # base address too far away, won't find instruction, so 171 | # add to ban list for this end address 172 | bannedaddr = deq.pop() 173 | banned[deq[-1]].append(bannedaddr) 174 | addr = deq[-1] 175 | addr -= 1 176 | for i in range(length): 177 | output += "\n" + "%0.8x" % deq[length - i] + "\n" 178 | return output 179 | elif args[0] == "x": 180 | output = "" 181 | functionparts = args[1].split("!") 182 | shortname = functionparts[0] 183 | crit = functionparts[1] 184 | info = script.module.InfoFromName(shortname) 185 | modulename = info.name # get full name 186 | symbols = script.symbol.GetList() 187 | symbols = list(filter(lambda s: s.mod == modulename and fnmatch.fnmatch(s.name, crit), symbols)) 188 | symbols = sorted(symbols, key=lambda x: x.rva) 189 | for s in symbols: 190 | output += "%0.8x%s%s!%s\n" % (info.base + s.rva, " " * 10, shortname, s.name) 191 | return output 192 | else: 193 | print "command: %s" % command 194 | notImplemented() 195 | 196 | def dprintln(str, dml = False): 197 | print str 198 | 199 | def findMemoryRegion(va): 200 | base = script.GetBase(va) 201 | size = script.GetSize(va) 202 | if base == 0 or size == 0: 203 | raise MemoryException("No such address 0x%x" % va) 204 | return base, size 205 | 206 | def findSymbol(va): 207 | notImplemented() 208 | 209 | def getCurrentProcess(): 210 | return x64dbg.DbgGetPebAddress(x64dbg.DbgGetProcessId()) 211 | 212 | def getCurrentProcessId(): 213 | return x64dbg.DbgGetProcessId() 214 | 215 | def getImplicitThread(): 216 | return x64dbg.DbgGetTebAddress(x64dbg.DbgGetThreadId()) 217 | 218 | def getProcessThreads(): 219 | l = x64dbg.THREADLIST() 220 | x64dbg.DbgGetThreadList(l) 221 | result = [] 222 | if l.count > 0: 223 | return [t.BasicInfo.ThreadLocalBase for t in x64dbg.GetThreadInfoList(l)] 224 | return result 225 | 226 | def getVaProtect(va): 227 | return memoryProtect(script.GetProtect(va)) 228 | 229 | def isValid(va): 230 | return x64dbg.DbgMemIsValidReadPtr(va) 231 | 232 | def loadBytes(va, count): 233 | return list(script.Read(va, count)) 234 | 235 | def loadChars(va, count): 236 | return script.Read(va, count) 237 | 238 | def loadCStr(va): 239 | chars = loadChars(va, 256) 240 | index = chars.find('\0') 241 | if index != -1: 242 | return chars[:index] 243 | return chars 244 | 245 | def loadDwords(va, count): 246 | A = array.array("I") 247 | A.fromstring(script.Read(va, count * 4)) 248 | return A.tolist() 249 | 250 | def loadUnicodeString(va): 251 | va = int(va) 252 | # https://msdn.microsoft.com/en-us/library/windows/desktop/aa380518(v=vs.85).aspx 253 | Length = script.ReadWord(va) 254 | va += 2 255 | MaximumLength = script.ReadWord(va) 256 | va += 2 257 | if is64bitSystem(): 258 | va += 4 259 | Buffer = script.ReadPtr(va) 260 | if Length > MaximumLength or not script.IsValidPtr(Buffer): 261 | raise DbgException("Corrupted UNICODE_STRING structure") 262 | A = array.array("u") 263 | A.fromstring(script.Read(Buffer, Length)) 264 | return A.tounicode().rstrip(u'\0') 265 | 266 | def loadWChars(va, count): 267 | A = array.array("u") 268 | A.fromstring(script.Read(va, count * 2)) 269 | return A.tounicode() 270 | 271 | def loadWStr(va): 272 | wchars = loadWChars(va, 256) 273 | index = wchars.find(u'\0') 274 | if index != -1: 275 | return wchars[:index] 276 | return wchars 277 | 278 | def ptrDWord(va): 279 | return script.ReadDword(va) 280 | 281 | def writeBytes(va, data): 282 | A = array.array("B") 283 | A.fromlist(data) 284 | script.Write(va, A.tostring()) 285 | 286 | def reg(name): 287 | return x64dbg.DbgValFromString(name) 288 | 289 | def ptrPtr(va): 290 | return script.ReadPtr(va) 291 | 292 | def typedVar(typename, va): 293 | if typename == "ntdll!_PEB": 294 | peb = typeStruct("_PEB", va) 295 | peb.Ldr = typePtr("_PEB_LDR_DATA*", va + archValue(0x000c, 0x0018)) 296 | peb.ProcessParameters = typePtr("_RTL_USER_PROCESS_PARAMETERS*", va + archValue(0x0010, 0x0020)) 297 | peb.NumberOfHeaps = typeInt32(va + archValue(0x0088, 0x00e8)) 298 | peb.ProcessHeaps = typePtr("void**", va + archValue(0x0090, 0x00f0)) 299 | peb.OSMajorVersion = typeInt16(va + archValue(0x00a4, 0x0118)) 300 | peb.OSMinorVersion = typeInt16(va + archValue(0x00a8, 0x011c)) 301 | peb.OSBuildNumber = typeInt8(va + archValue(0x00ac, 0x0120)) 302 | return peb 303 | elif typename == "_PEB_LDR_DATA": 304 | ldr = typeStruct("_PEB_LDR_DATA", va) 305 | ldr.InLoadOrderModuleList = typeStruct("_LDR_DATA_TABLE_ENTRY", va + archValue(0x000c, 0x0010)) 306 | return ldr 307 | elif typename == "_TEB": 308 | teb = typeStruct("_TEB", va) 309 | teb.Self = va 310 | return teb 311 | elif typename in ("_IMAGE_NT_HEADERS", "_IMAGE_NT_HEADERS64"): 312 | ntheaders = typeStruct(typename, va) 313 | ntheaders.FileHeader = typedVar("_IMAGE_FILE_HEADER", va + 0x0004) 314 | ntheaders.OptionalHeader = typedVar(archValue("_IMAGE_OPTIONAL_HEADER", "_IMAGE_OPTIONAL_HEADER64"), va + 0x0018) 315 | return ntheaders 316 | elif typename == "_IMAGE_FILE_HEADER": 317 | fileheader = typeStruct("_IMAGE_FILE_HEADER", va) 318 | fileheader.NumberOfSections = typeInt16(va + 0x0002) 319 | fileheader.SizeOfOptionalHeader = typeInt16(va + 0x0010) 320 | return fileheader 321 | elif typename in ("_IMAGE_OPTIONAL_HEADER", "_IMAGE_OPTIONAL_HEADER64"): 322 | optheader = typeStruct(typename, va) 323 | optheader.SizeOfCode = typeInt32(va + 0x0004) 324 | optheader.AddressOfEntryPoint = typeInt32(va + 0x0010) 325 | optheader.BaseOfCode = typeInt32(va + 0x0014) 326 | if not is64bitSystem(): 327 | optheader.BaseOfData = typeInt32(va + 0x0018) 328 | optheader.ImageBase = typeInt32(va + archValue(0x001c, 0x0018)) 329 | optionalheadersize = int(typeInt16(va - 0x0004)) 330 | optheader.DataDirectory = [typedVar("_IMAGE_DATA_DIRECTORY", i) for i in range(va + archValue(0x0060, 0x0070), va + optionalheadersize, 8)] 331 | return optheader 332 | elif typename == "_IMAGE_DATA_DIRECTORY": 333 | imgdatadir = typeStruct("_IMAGE_DATA_DIRECTORY", va) 334 | imgdatadir.VirtualAddress = typeInt32(va) 335 | imgdatadir.Size = typeInt32(va + 0x0004) 336 | return imgdatadir 337 | else: 338 | print "typename: %s, va: %x" % (typename, va) 339 | notImplemented() 340 | 341 | def typedVarList(va, typename, flink): 342 | if typename == "ntdll!_LDR_DATA_TABLE_ENTRY" and flink == "InMemoryOrderLinks.Flink": 343 | start = va + archValue(0x0008, 0x0010) 344 | va = script.ReadPtr(start) 345 | result = [] 346 | 347 | while va != start and len(result) < 50: 348 | entry = typeStruct("_LDR_DATA_TABLE_ENTRY", va) 349 | # This is actually _LDR_DATA_TABLE_ENTRY.FullDllName 350 | entry.BaseDllName = typeStruct("UNICODE_STRING", va + archValue(0x001c, 0x0038)) 351 | result.append(entry) 352 | va = script.ReadPtr(va) 353 | 354 | return result 355 | else: 356 | notImplemented() 357 | 358 | class typeBase(object): 359 | def __init__(self, name, size, addr = 0): 360 | self.name = name 361 | self.size = size 362 | self.addr = addr 363 | 364 | def getAddress(self): 365 | return self.addr 366 | 367 | def __int__(self): 368 | return self.getAddress() 369 | 370 | def __add__(self, other): 371 | return self.getAddress() + other 372 | 373 | def __radd__(self, other): 374 | return other + self.getAddress() 375 | 376 | class typePrimitive(typeBase): 377 | def __init__(self, name, size, addr = 0): 378 | super(typePrimitive, self).__init__(name, size, addr) 379 | 380 | def __int__(self): 381 | if self.size == 1: 382 | return script.ReadByte(self.addr) 383 | elif self.size == 2: 384 | return script.ReadWord(self.addr) 385 | elif self.size == 4: 386 | return script.ReadDword(self.addr) 387 | elif self.size == 8: 388 | return script.ReadQword(self.addr) 389 | else: 390 | notImplemented() 391 | 392 | class typeInt8(typePrimitive): 393 | def __init__(self, addr = 0): 394 | super(typeInt8, self).__init__("uint8_t", 1, addr) 395 | 396 | class typeInt16(typePrimitive): 397 | def __init__(self, addr = 0): 398 | super(typeInt16, self).__init__("uint16_t", 2, addr) 399 | 400 | class typeInt32(typePrimitive): 401 | def __init__(self, addr = 0): 402 | super(typeInt32, self).__init__("uint32_t", 4, addr) 403 | 404 | class typeInt64(typePrimitive): 405 | def __init__(self, addr = 0): 406 | super(typeInt64, self).__init__("uint64_t", 8, addr) 407 | 408 | class typePtr(typePrimitive): 409 | def __init__(self, typename = "void*", addr = 0): 410 | super(typePtr, self).__init__(typename, archValue(4, 8), addr) 411 | 412 | def deref(self): 413 | return typedVar(self.name[:-1], int(self)) 414 | 415 | class typeStruct(typeBase): 416 | def __init__(self, name, addr): 417 | super(typeStruct, self).__init__(name, 0, addr) 418 | 419 | class memoryProtect(int): 420 | PageExecute = 16 421 | PageExecuteWriteCopy = 128 422 | PageReadOnly = 2 423 | PageReadWrite = 4 424 | PageExecuteRead = 32 425 | PageExecuteReadWrite = 64 426 | PageNoAccess = 1 427 | PageWriteCopy = 2 428 | 429 | class DbgException(Exception): 430 | pass 431 | 432 | class MemoryException(DbgException): 433 | pass 434 | 435 | class module: 436 | def __init__(self, arg): 437 | if isinstance(arg, basestring): 438 | self._base = script.BaseFromName(arg) 439 | else: 440 | self._base = arg 441 | 442 | self._image = script.NameFromAddr(self._base) 443 | self._size = script.SizeFromAddr(self._base) 444 | 445 | index = self._image.rfind(".") 446 | if index != -1: 447 | self._name = self._image[:index] 448 | else: 449 | self._name = self._image 450 | 451 | if self._base == 0 or self._size == 0: 452 | raise DbgException("Failed to get module for %s" % arg) 453 | 454 | def begin(self): 455 | return self._base 456 | 457 | def size(self): 458 | return self._size 459 | 460 | def image(self): 461 | return self._image 462 | 463 | def name(self): 464 | return self._name 465 | 466 | def end(self): 467 | return self._base + self._size 468 | 469 | def typedVar(self, typename, va): 470 | return typedVar(typename, va) -------------------------------------------------------------------------------- /x64dbgpylib.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2011-2017, Peter Van Eeckhoutte - Corelan GCV 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 Corelan nor the 13 | names of its contributors may be used to endorse or promote products 14 | derived from this software 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 PETER VAN EECKHOUTTE OR CORELAN GCV 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. 26 | 27 | $Revision: 143 $ 28 | $Id: windbglib.py 143 2017-04-02 07:14:58Z corelanc0d3r $ 29 | """ 30 | 31 | __VERSION__ = '1.0' 32 | 33 | # 34 | # Wrapper library around pykd 35 | # (partial immlib logic port) 36 | # 37 | # This library allows you to run mona.py 38 | # under WinDBG, using the pykd extension 39 | # 40 | import pykd 41 | import os 42 | import binascii 43 | import struct 44 | import traceback 45 | import pickle 46 | import ctypes 47 | import array 48 | 49 | import x64dbgpy.pluginsdk.x64dbg as x64dbg 50 | import x64dbgpy.pluginsdk._scriptapi as script 51 | 52 | global MemoryPages 53 | global AsmCache 54 | global OpcodeCache 55 | global InstructionCache 56 | global PageSections 57 | global ModuleCache 58 | global cpebaddress 59 | global PEBModList 60 | 61 | arch = 32 62 | cpebaddress = 0 63 | 64 | PageSections = {} 65 | ModuleCache = {} 66 | PEBModList = {} 67 | 68 | Registers32BitsOrder = ["EAX", "ECX", "EDX", "EBX", "ESP", "EBP", "ESI", "EDI"] 69 | Registers64BitsOrder = ["RAX", "RCX", "RDX", "RBX", "RSP", "RBP", "RSI", "RDI", 70 | "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15"] 71 | 72 | if pykd.is64bitSystem(): 73 | arch = 64 74 | 75 | 76 | # Utility functions 77 | 78 | def getOSVersion(): 79 | osversions = {} 80 | osversions["5.0"] = "2000" 81 | osversions["5.1"] = "xp" 82 | osversions["5.2"] = "2003" 83 | osversions["6.0"] = "vista" 84 | osversions["6.1"] = "win7" 85 | osversions["6.2"] = "win8" 86 | osversions["6.3"] = "win8.1" 87 | osversions["10.0"] = "win10" 88 | peb = getPEBInfo() 89 | majorversion = int(peb.OSMajorVersion) 90 | minorversion = int(peb.OSMinorVersion) 91 | thisversion = str(majorversion) + "." + str(minorversion) 92 | if thisversion in osversions: 93 | return osversions[thisversion] 94 | else: 95 | return "unknown" 96 | 97 | 98 | def getArchitecture(): 99 | if not pykd.is64bitSystem(): 100 | return 32 101 | else: 102 | return 64 103 | 104 | 105 | def getNtHeaders(modulebase): 106 | # http://www.nirsoft.net/kernel_struct/vista/IMAGE_DOS_HEADER.html 107 | # http://www.nirsoft.net/kernel_struct/vista/IMAGE_NT_HEADERS.html 108 | if getArchitecture() == 64: 109 | ntheaders = "_IMAGE_NT_HEADERS64" 110 | else: 111 | ntheaders = "_IMAGE_NT_HEADERS" 112 | 113 | # modulebase + 0x3c = IMAGE_DOS_HEADER.e_lfanew 114 | return pykd.module("ntdll").typedVar(ntheaders, modulebase + pykd.ptrDWord(modulebase + 0x3c)) 115 | 116 | 117 | def clearvars(): 118 | global MemoryPages 119 | global AsmCache 120 | global OpcodeCache 121 | global InstructionCache 122 | global PageSections 123 | global ModuleCache 124 | global cpebaddress 125 | MemoryPages = None 126 | AsmCache = None 127 | OpcodeCache = None 128 | InstructionCache = None 129 | InstructionCache = None 130 | PageSections = None 131 | ModuleCache = None 132 | cpebaddress = None 133 | return 134 | 135 | 136 | def getPEBInfo(): 137 | try: 138 | return pykd.typedVar("ntdll!_PEB", pykd.getCurrentProcess()) 139 | except: 140 | currversion = getPyKDVersion() 141 | print "" 142 | print " Oops - It seems that PyKD was unable problem to get the PEB object." 143 | print " This usually means that" 144 | print " 1. msdiaxxx.dll has not been registered correctly and/or" 145 | print " 2. symbols are missing for ntdll.dll" 146 | print "" 147 | print " Possible solutions:" 148 | print " -------------------" 149 | print " 1. Re-register the VC runtime library:" 150 | print " * For PyKd v%s:" % currversion 151 | if currversion.startswith("0.2"): 152 | print " (Re)Install the x86 VC++ Redistributable Package for Visual Studio 2008" 153 | print " (https://www.microsoft.com/en-us/download/details.aspx?id=29)" 154 | print " Next, run the following command from an administrator prompt:" 155 | print " (x86) regsvr32.exe \"%ProgramFiles%\\Common Files\\microsoft shared\\VC\\msdia90.dll\"\n" 156 | print " (x64) regsvr32.exe \"%ProgramFiles(x86)%\\Common Files\\microsoft shared\\VC\\msdia90.dll\"\n" 157 | else: 158 | print " Either install Visual Studio 2013, or get a copy of msdia120.dll and register it manually\n" 159 | print " You can find a copy of msdia120.dll inside the pykd.zip file inside the github repository" 160 | print " (Use at your own risk!). Place the file in the correct 'VC' folder and run regsvr32 from an administrative prompt:" 161 | print " (x86) regsvr32.exe \"%ProgramFiles%\\Common Files\\microsoft shared\\VC\\msdia120.dll\"\n" 162 | print " (x64) regsvr32.exe \"%ProgramFiles(x86)%\\Common Files\\microsoft shared\\VC\\msdia120.dll\"\n" 163 | 164 | print " 2. Force download of the Symbols for ntdll.dll" 165 | print " * Connect to the internet, and verify that the symbol path is configured correctly" 166 | print " Assuming that the local symbol path is set to c:\\symbols," 167 | print " run the following command from within the windbg application folder" 168 | print " symchk /r c:\\windows\\system32\\ntdll.dll /s SRV*c:\\symbols*http://msdl.microsoft.com/download/symbols" 169 | print "" 170 | print " Restart windbg and try again" 171 | exit(1) 172 | 173 | 174 | def getPEBAddress(): 175 | global cpebaddress 176 | if cpebaddress == 0: 177 | peb = getPEBInfo() 178 | cpebaddress = peb.getAddress() 179 | return cpebaddress 180 | 181 | 182 | def getTEBInfo(): 183 | return pykd.typedVar("_TEB", pykd.getImplicitThread()) 184 | 185 | 186 | def getTEBAddress(): 187 | tebinfo = pykd.dbgCommand("!teb") 188 | if len(tebinfo) > 0: 189 | teblines = tebinfo.split("\n") 190 | tebline = teblines[0] 191 | tebparts = tebline.split(" ") 192 | if len(tebparts) > 2: 193 | return hexStrToInt(tebparts[2]) 194 | # slow 195 | teb = getTEBInfo() 196 | return int(teb.Self) 197 | 198 | 199 | def bin2hex(binbytes): 200 | """ 201 | Converts a binary string to a string of space-separated hexadecimal bytes. 202 | """ 203 | return ' '.join('%02x' % ord(c) for c in binbytes) 204 | 205 | 206 | def hexptr2bin(hexptr): 207 | """ 208 | Input must be a int 209 | output : bytes in little endian 210 | """ 211 | return struct.pack(' 16: 258 | return False 259 | 260 | return set(address.upper()) <= set("ABCDEF1234567890") 261 | 262 | 263 | def intToHex(address): 264 | if arch == 32: 265 | return "0x%08x" % address 266 | if arch == 64: 267 | return "0x%016x" % address 268 | 269 | 270 | def toHexByte(n): 271 | """ 272 | Converts a numeric value to a hex byte 273 | 274 | Arguments: 275 | n - the vale to convert (max 255) 276 | 277 | Return: 278 | A string, representing the value in hex (1 byte) 279 | """ 280 | return "%02X" % n 281 | 282 | 283 | def hex2bin(pattern): 284 | """ 285 | Converts a hex string (\\x??\\x??\\x??\\x??) to real hex bytes 286 | 287 | Arguments: 288 | pattern - A string representing the bytes to convert 289 | 290 | Return: 291 | the bytes 292 | """ 293 | pattern = pattern.replace("\\x", "") 294 | pattern = pattern.replace("\"", "") 295 | pattern = pattern.replace("\'", "") 296 | return ''.join([binascii.a2b_hex(i + j) for i, j in zip(pattern[0::2], pattern[1::2])]) 297 | 298 | 299 | def getPyKDVersion(): 300 | currentversion = pykd.version 301 | currversion = "" 302 | for versionpart in currentversion: 303 | if versionpart != " ": 304 | if versionpart == ",": 305 | currversion += "." 306 | else: 307 | currversion += str(versionpart) 308 | currversion = currversion.strip(".") 309 | return currversion 310 | 311 | 312 | def isPyKDVersionCompatible(currentversion, requiredversion): 313 | # current version should be at least requiredversion 314 | if currentversion == requiredversion: 315 | return True 316 | else: 317 | currentparts = currentversion.split(".") 318 | requiredparts = requiredversion.split(".") 319 | if len(requiredparts) > len(currentparts): 320 | delta = len(requiredparts) - len(currentparts) 321 | cnt = 0 322 | while cnt < delta: 323 | currentparts.append("0") 324 | cnt += 1 325 | 326 | cnt = 0 327 | while cnt < len(requiredparts): 328 | if int(currentparts[cnt]) < int(requiredparts[cnt]): 329 | return False 330 | if int(currentparts[cnt]) > int(requiredparts[cnt]): 331 | return True 332 | cnt += 1 333 | return True 334 | 335 | 336 | def checkVersion(): 337 | pykdurl = "https://github.com/corelan/windbglib/raw/master/pykd/pykd.zip" 338 | pykdurl03 = "https://github.com/corelan/windbglib/raw/master/pykd/pykd03.zip" 339 | pykdversion_needed = "0.2.0.29" 340 | if arch == 64: 341 | pykdversion_needed = "0.2.0.29" 342 | currversion = getPyKDVersion() 343 | if not isPyKDVersionCompatible(currversion, pykdversion_needed): 344 | print "*******************************************************************************************" 345 | print " You are running the wrong version of PyKD, please update " 346 | print " Installed version : %s " % currversion 347 | print " Required version : %s" % pykdversion_needed 348 | print " You can get an updated PyKD version from one of the following sources:" 349 | print " - %s (preferred)" % pykdurl 350 | print " (unzip with 7zip)" 351 | print " - http://pykd.codeplex.com (newer versions may not work !)" 352 | print "*******************************************************************************************" 353 | import sys 354 | sys.exit() 355 | return 356 | if pykdversion_needed != currversion: 357 | # version must be higher 358 | print "*******************************************************************************************" 359 | print " You are running a newer version of pykd.pyd" 360 | print " mona.py was tested against v%s" % pykdversion_needed 361 | print " and not against v%s" % currversion 362 | print " This version may not work properly." 363 | print " If you are having issues, I recommend to download the correct version from" 364 | print " %s" % pykdurl 365 | print " (unzip with 7zip)" 366 | if currversion.startswith("0.3"): 367 | print "" 368 | print " NOTE: PyKD v%s requires msdia120.dll, which only gets installed via Visual Studio 2013 (yup, I know)" % currversion 369 | print " Alternatively, you can use the copy of msdia120.dll from the pykd.pyd file" 370 | print " (%s), but use this file at your own risk" % pykdurl03 371 | print "*******************************************************************************************" 372 | return 373 | 374 | 375 | def getModulesFromPEB(): 376 | global PEBModList 377 | PEBModList = {} 378 | peb = getPEBInfo() 379 | imagenames = [] 380 | # http://www.nirsoft.net/kernel_struct/vista/PEB.html 381 | # http://www.nirsoft.net/kernel_struct/vista/PEB_LDR_DATA.html 382 | # http://www.nirsoft.net/kernel_struct/vista/LDR_DATA_TABLE_ENTRY.html 383 | # The usage of _LDR_DATA_TABLE_ENTRY.SizeOfImage is very confusing and appears to actually contain the module base 384 | offset = 0x10 385 | if arch == 64: 386 | offset = 0x30 387 | moduleLst = pykd.typedVarList(peb.Ldr.deref().InLoadOrderModuleList, "ntdll!_LDR_DATA_TABLE_ENTRY", 388 | "InMemoryOrderLinks.Flink") 389 | if len(PEBModList) == 0: 390 | for mod in moduleLst: 391 | thismod = pykd.loadUnicodeString(mod.BaseDllName).encode("utf8") 392 | modparts = thismod.split("\\") 393 | modulename = modparts[len(modparts) - 1] 394 | fullpath = thismod 395 | exename = modulename 396 | 397 | addtolist = True 398 | 399 | moduleparts = modulename.split(".") 400 | imagename = "" 401 | if len(moduleparts) == 1: 402 | imagename = moduleparts[0] 403 | cnt = 0 404 | while cnt < len(moduleparts) - 1: 405 | imagename = imagename + moduleparts[cnt] + "." 406 | cnt += 1 407 | imagename = imagename.strip(".") 408 | 409 | # no windbg love for + - . 410 | imagename = imagename.replace("+", "_") 411 | imagename = imagename.replace("-", "_") 412 | imagename = imagename.replace(".", "_") 413 | 414 | if imagename in imagenames: 415 | # duplicate name ? Append _ 416 | # mod.getAddress() + offset = _LDR_DATA_TABLE_ENTRY.SizeOfImage 417 | baseaddy = int(pykd.ptrPtr(mod.getAddress() + offset)) 418 | imagename = imagename + "_%08x" % baseaddy 419 | 420 | # check if module can be loaded 421 | try: 422 | modcheck = pykd.module(imagename) 423 | except: 424 | # change to image+baseaddress 425 | # mod.getAddress() + offset = _LDR_DATA_TABLE_ENTRY.SizeOfImage 426 | baseaddy = int(pykd.ptrPtr(mod.getAddress() + offset)) 427 | imagename = "image%08x" % baseaddy 428 | try: 429 | modcheck = pykd.module(imagename) 430 | except: 431 | # try with base addy 432 | try: 433 | modcheck = pykd.module(baseaddy) 434 | imagename = modcheck.name() 435 | # print "Name: %s" % modcheck.name() 436 | # print "Imagename: %s" % modcheck.image() 437 | except: 438 | # try finding it with windbg 'ln' 439 | cmd2run = "ln 0x%08x" % baseaddy 440 | output = pykd.dbgCommand(cmd2run) 441 | if "!__ImageBase" in output: 442 | outputlines = output.split("\n") 443 | for l in outputlines: 444 | if "!__ImageBase" in l: 445 | lparts = l.split("!__ImageBase") 446 | leftpart = lparts[0] 447 | leftparts = leftpart.split(" ") 448 | imagename = leftparts[len(leftparts) - 1] 449 | try: 450 | modcheck = pykd.module(imagename) 451 | except: 452 | print "" 453 | print " *** Error parsing module '%s' ('%s') at 0x%08x ***" % ( 454 | imagename, modulename, baseaddy) 455 | print " *** Please open a github issue ticket at https://github.com/corelan/windbglib ***" 456 | print " *** and provide the output of the following 2 windbg commands in the ticket: ***" 457 | print " lm" 458 | print " !peb" 459 | print " *** Thanks" 460 | print "" 461 | addtolist = False 462 | 463 | if addtolist: 464 | imagenames.append(imagename) 465 | PEBModList[imagename] = [exename, fullpath] 466 | 467 | return moduleLst 468 | 469 | 470 | def getModuleFromAddress(address): 471 | offset = 0x20 472 | if arch == 64: 473 | offset = 0x40 474 | 475 | global ModuleCache 476 | # try fastest way first 477 | try: 478 | thismod = pykd.module(address) 479 | # if that worked, we could add it to the cache if needed 480 | modbase = thismod.begin() 481 | modsize = thismod.size() 482 | modend = modbase + modsize 483 | modulename = thismod.image() 484 | ModuleCache[modulename] = [modbase, modsize] 485 | if (address >= modbase) and (address <= modend): 486 | return thismod 487 | except: 488 | pass 489 | 490 | # maybe cached 491 | for modname in ModuleCache: 492 | modparts = ModuleCache[modname] 493 | # 0 : base 494 | # 1 : size 495 | modbase = modparts[0] 496 | modsize = modparts[1] 497 | modend = modbase + modsize 498 | if (address >= modbase) and (address <= modend): 499 | # print "0x%08x belongs to %s" % (address,modname) 500 | return pykd.module(modname) 501 | # not cached, find it 502 | moduleLst = getModulesFromPEB() 503 | for mod in moduleLst: 504 | thismod = pykd.loadUnicodeString(mod.BaseDllName).encode("utf8") 505 | modparts = thismod.split("\\") 506 | modulename = modparts[len(modparts) - 1].lower() 507 | moduleparts = modulename.split(".") 508 | modulename = "" 509 | if len(moduleparts) == 1: 510 | modulename = moduleparts[0] 511 | cnt = 0 512 | while cnt < len(moduleparts) - 1: 513 | modulename = modulename + moduleparts[cnt] + "." 514 | cnt += 1 515 | modulename = modulename.strip(".") 516 | thismod = "" 517 | imagename = "" 518 | 519 | try: 520 | moduleLst = getModulesFromPEB() 521 | for mod in moduleLst: 522 | thismod = pykd.loadUnicodeString(mod.BaseDllName).encode("utf8") 523 | modparts = thismod.split("\\") 524 | thismodname = modparts[len(modparts) - 1] 525 | moduleparts = thismodname.split(".") 526 | if len(moduleparts) > 1: 527 | thismodname = "" 528 | cnt = 0 529 | while cnt < len(moduleparts) - 1: 530 | thismodname = thismodname + moduleparts[cnt] + "." 531 | cnt += 1 532 | thismodname = thismodname.strip(".") 533 | if thismodname.lower() == modulename.lower(): 534 | # mod.getAddress() + offset = _LDR_DATA_TABLE_ENTRY.SizeOfImage 535 | baseaddy = int(pykd.ptrPtr(mod.getAddress() + offset)) 536 | baseaddr = "%08x" % baseaddy 537 | lmcommand = pykd.dbgCommand("lm") 538 | lmlines = lmcommand.split("\n") 539 | foundinlm = False 540 | for lmline in lmlines: 541 | linepieces = lmline.split(" ") 542 | if linepieces[0].upper() == baseaddr.upper(): 543 | cnt = 2 544 | while cnt < len(linepieces) and not foundinlm: 545 | if linepieces[cnt].strip(" ") != "": 546 | imagename = linepieces[cnt] 547 | foundinlm = True 548 | break 549 | cnt += 1 550 | if not foundinlm: 551 | imagename = "image%s" % baseaddr.lower() 552 | break 553 | except: 554 | pykd.dprintln(traceback.format_exc()) 555 | 556 | try: 557 | modulename = imagename 558 | thismod = pykd.module(imagename) 559 | modbase = thismod.begin() 560 | modsize = thismod.size() 561 | modend = modbase + modsize 562 | ModuleCache[modulename] = [modbase, modsize] 563 | if (address >= modbase) and (address <= modend): 564 | return thismod 565 | except: 566 | thismod = pykd.module(address) 567 | 568 | modbase = thismod.begin() 569 | modsize = thismod.size() 570 | modend = modbase + modsize 571 | modulename = thismod.image() 572 | ModuleCache[modulename] = [modbase, modsize] 573 | if (address >= modbase) and (address <= modend): 574 | return thismod 575 | 576 | return None 577 | 578 | def getImageBaseOnDisk(fullpath): 579 | with open(fullpath, "rb") as pe: 580 | data = pe.read() 581 | nt_header_offset = struct.unpack(" 1: 646 | modulename = functionparts[0] 647 | functionname = functionparts[1] 648 | funcref = "%s!%s" % (modulename, functionname) 649 | cmd2run = "ln %s" % funcref 650 | output = self.nativeCommand(cmd2run) 651 | if "Exact matches" in output: 652 | outputlines = output.split("\n") 653 | for outputline in outputlines: 654 | if "(" in outputline.lower(): 655 | lineparts = outputline.split(")") 656 | address = lineparts[0].replace("(", "") 657 | return hexStrToInt(address) 658 | else: 659 | return 0 660 | else: 661 | return 0 662 | 663 | def getCurrentTEBAddress(self): 664 | return getTEBAddress() 665 | 666 | """ 667 | AsmCache 668 | """ 669 | 670 | def fillAsmCache(self): 671 | 672 | self.AsmCache["push eax"] = "\x50" 673 | self.AsmCache["push ecx"] = "\x51" 674 | self.AsmCache["push edx"] = "\x52" 675 | self.AsmCache["push ebx"] = "\x53" 676 | self.AsmCache["push esp"] = "\x54" 677 | self.AsmCache["push ebp"] = "\x55" 678 | self.AsmCache["push esi"] = "\x56" 679 | self.AsmCache["push edi"] = "\x57" 680 | 681 | self.AsmCache["pop eax"] = "\x58" 682 | self.AsmCache["pop ecx"] = "\x59" 683 | self.AsmCache["pop edx"] = "\x5a" 684 | self.AsmCache["pop ebx"] = "\x5b" 685 | self.AsmCache["pop esp"] = "\x5c" 686 | self.AsmCache["pop ebp"] = "\x5d" 687 | self.AsmCache["pop esi"] = "\x5e" 688 | self.AsmCache["pop edi"] = "\x5f" 689 | 690 | self.AsmCache["jmp eax"] = "\xff\xe0" 691 | self.AsmCache["jmp ecx"] = "\xff\xe1" 692 | self.AsmCache["jmp edx"] = "\xff\xe2" 693 | self.AsmCache["jmp ebx"] = "\xff\xe3" 694 | self.AsmCache["jmp esp"] = "\xff\xe4" 695 | self.AsmCache["jmp ebp"] = "\xff\xe5" 696 | self.AsmCache["jmp esi"] = "\xff\xe6" 697 | self.AsmCache["jmp edi"] = "\xff\xe7" 698 | 699 | self.AsmCache["call eax"] = "\xff\xd0" 700 | self.AsmCache["call ecx"] = "\xff\xd1" 701 | self.AsmCache["call edx"] = "\xff\xd2" 702 | self.AsmCache["call ebx"] = "\xff\xd3" 703 | self.AsmCache["call esp"] = "\xff\xd4" 704 | self.AsmCache["call ebp"] = "\xff\xd5" 705 | self.AsmCache["call esi"] = "\xff\xd6" 706 | self.AsmCache["call edi"] = "\xff\xd7" 707 | 708 | self.AsmCache["jmp [eax]"] = "\xff\x20" 709 | self.AsmCache["jmp [ecx]"] = "\xff\x21" 710 | self.AsmCache["jmp [edx]"] = "\xff\x22" 711 | self.AsmCache["jmp [ebx]"] = "\xff\x23" 712 | self.AsmCache["jmp [esp]"] = "\xff\x24" 713 | self.AsmCache["jmp [ebp]"] = "\xff\x25" 714 | self.AsmCache["jmp [esi]"] = "\xff\x26" 715 | self.AsmCache["jmp [edi]"] = "\xff\x27" 716 | 717 | self.AsmCache["call [eax]"] = "\xff\x10" 718 | self.AsmCache["call [ecx]"] = "\xff\x11" 719 | self.AsmCache["call [edx]"] = "\xff\x12" 720 | self.AsmCache["call [ebx]"] = "\xff\x13" 721 | self.AsmCache["call [esp]"] = "\xff\x14" 722 | self.AsmCache["call [ebp]"] = "\xff\x15" 723 | self.AsmCache["call [esi]"] = "\xff\x16" 724 | self.AsmCache["call [edi]"] = "\xff\x17" 725 | 726 | self.AsmCache["xchg eax,esp"] = "\x94" 727 | self.AsmCache["xchg ecx,esp"] = "\x87\xcc" 728 | self.AsmCache["xchg edx,esp"] = "\x87\xd4" 729 | self.AsmCache["xchg ebx,esp"] = "\x87\xdc" 730 | self.AsmCache["xchg ebp,esp"] = "\x87\xec" 731 | self.AsmCache["xchg edi,esp"] = "\x87\xfc" 732 | self.AsmCache["xchg esi,esp"] = "\x87\xf4" 733 | self.AsmCache["xchg esp,eax"] = "\x94" 734 | self.AsmCache["xchg esp,ecx"] = "\x87\xcc" 735 | self.AsmCache["xchg esp,edx"] = "\x87\xd4" 736 | self.AsmCache["xchg esp,ebx"] = "\x87\xdc" 737 | self.AsmCache["xchg esp,ebp"] = "\x87\xec" 738 | self.AsmCache["xchg esp,edi"] = "\x87\xfc" 739 | self.AsmCache["xchg esp,esi"] = "\x87\xf4" 740 | 741 | self.AsmCache["pushad"] = "\x60" 742 | self.AsmCache["popad"] = "\x61" 743 | 744 | for offset in xrange(4, 80, 4): 745 | thisasm = "\x83\xc4" + hex2bin("%02x" % offset) 746 | self.AsmCache["add esp,%02x" % offset] = thisasm 747 | self.AsmCache["add esp,%x" % offset] = thisasm 748 | 749 | self.AsmCache["retn"] = "\xc3" 750 | self.AsmCache["retf"] = "\xdb" 751 | for offset in xrange(0, 80, 2): 752 | thisasm = "\xc2" + hex2bin("%02x" % offset) + "\x00" 753 | self.AsmCache["retn %02x" % offset] = thisasm 754 | self.AsmCache["retn %x" % offset] = thisasm 755 | self.AsmCache["retn 0x%02x" % offset] = thisasm 756 | return 757 | 758 | """ 759 | Knowledge 760 | """ 761 | 762 | def addKnowledge(self, id, object, force_add=0): 763 | allk = self.readKnowledgeDB() 764 | if not id in allk: 765 | allk[id] = object 766 | else: 767 | if object.__class__.__name__ == "dict": 768 | for odictkey in object: 769 | allk[id][odictkey] = object[odictkey] 770 | with open(self.knowledgedb, "wb") as fh: 771 | pickle.dump(allk, fh, -1) 772 | return 773 | 774 | def getKnowledge(self, id): 775 | allk = self.readKnowledgeDB() 776 | if id in allk: 777 | return allk[id] 778 | else: 779 | return None 780 | 781 | def readKnowledgeDB(self): 782 | allk = {} 783 | try: 784 | with open(self.knowledgedb, "rb") as fh: 785 | allk = pickle.load(fh) 786 | except: 787 | pass 788 | return allk 789 | 790 | def listKnowledge(self): 791 | allk = self.readKnowledgeDB() 792 | allid = [] 793 | for thisk in allk: 794 | allid.append(thisk) 795 | return allid 796 | 797 | def cleanKnowledge(self): 798 | try: 799 | os.remove(self.knowledgedb) 800 | except: 801 | try: 802 | with open(self.knowledgedb, "wb") as fh: 803 | pickle.dump({}, fh, -1) 804 | except: 805 | pass 806 | pass 807 | return 808 | 809 | def forgetKnowledge(self, id, entry=""): 810 | allk = self.readKnowledgeDB() 811 | if entry == "": 812 | if id in allk: 813 | del allk[id] 814 | else: 815 | # find the entry 816 | if id in allk: 817 | thisidkb = allk[id] 818 | if entry in thisidkb: 819 | del thisidkb[entry] 820 | allk[id] = thisidkb 821 | with open(self.knowledgedb, "wb") as fh: 822 | pickle.dump(allk, fh, -1) 823 | return 824 | 825 | def cleanUp(self): 826 | self.cleanKnowledge() 827 | return 828 | 829 | """ 830 | Placeholders 831 | """ 832 | 833 | def analysecode(self): 834 | return 835 | 836 | def isAnalysed(self): 837 | return True 838 | 839 | """ 840 | LOGGING 841 | """ 842 | 843 | def toAsciiOnly(self, message): 844 | newchar = [] 845 | for thischar in message: 846 | if ord(thischar) >= 20 and ord(thischar) <= 126: 847 | newchar.append(thischar) 848 | else: 849 | newchar.append(".") 850 | return "".join(newchar) 851 | 852 | def createLogWindow(self): 853 | return 854 | 855 | def log(self, message, highlight=0, address=None, focus=0): 856 | if not address == None: 857 | message = intToHex(address) + " | " + message 858 | showdml = False 859 | if highlight == 1: 860 | showdml = True 861 | message = "" + message + "" 862 | pykd.dprintln(self.toAsciiOnly(message), showdml) 863 | 864 | def logLines(self, message, highlight=0, address=None, focus=0): 865 | allLines = message.split('\n') 866 | linecnt = 0 867 | messageprefix = "" 868 | if not address == None: 869 | messageprefix = " " * 10 870 | messageprefix += " | " 871 | for line in allLines: 872 | if linecnt == 0: 873 | self.log(line, highlight, address) 874 | else: 875 | self.log(messageprefix + line, highlight) 876 | linecnt += 1 877 | 878 | def updateLog(self): 879 | return 880 | 881 | def setStatusBar(self, message): 882 | return 883 | 884 | def error(self, message): 885 | return 886 | 887 | """ 888 | Process stuff 889 | """ 890 | 891 | def getDebuggedName(self): 892 | # http://www.nirsoft.net/kernel_struct/vista/PEB.html 893 | # http://www.nirsoft.net/kernel_struct/vista/RTL_USER_PROCESS_PARAMETERS.html 894 | peb = getPEBInfo() 895 | ProcessParameters = peb.ProcessParameters 896 | offset = 0x38 897 | if arch == 64: 898 | offset = 0x60 899 | # ProcessParameters + offset = _RTL_USER_PROCESS_PARAMETERS.ImagePathName(_UNICODE_STRING) 900 | sImageFile = pykd.loadUnicodeString(int(ProcessParameters) + offset).encode("utf8") 901 | sImageFilepieces = sImageFile.split("\\") 902 | return sImageFilepieces[len(sImageFilepieces) - 1] 903 | 904 | def getDebuggedPid(self): 905 | # http://www.nirsoft.net/kernel_struct/vista/TEB.html 906 | # http://www.nirsoft.net/kernel_struct/vista/CLIENT_ID.html 907 | teb = getTEBAddress() 908 | offset = 0x20 909 | if arch == 64: 910 | offset = 0x40 911 | # _TEB.ClientId(CLIENT_ID).UniqueProcess(PVOID) 912 | pid = pykd.ptrDWord(teb + offset) 913 | return pid 914 | 915 | """ 916 | OS stuff 917 | """ 918 | 919 | def getOsRelease(self): 920 | peb = getPEBInfo() 921 | majorversion = int(peb.OSMajorVersion) 922 | minorversion = int(peb.OSMinorVersion) 923 | buildversion = int(peb.OSBuildNumber) 924 | osversion = str(majorversion) + "." + str(minorversion) + "." + str(buildversion) 925 | return osversion 926 | 927 | def getOsVersion(self): 928 | return getOSVersion() 929 | 930 | def getPyKDVersionNr(self): 931 | return getPyKDVersion() 932 | 933 | """ 934 | Registers 935 | """ 936 | 937 | def getRegs(self): 938 | regs = [] 939 | if arch == 32: 940 | regs = Registers32BitsOrder 941 | regs.append("EIP") 942 | if arch == 64: 943 | regs = Registers64BitsOrder 944 | regs.append("RIP") 945 | reginfo = {} 946 | for thisreg in regs: 947 | reginfo[thisreg.upper()] = int(pykd.reg(thisreg.lower())) 948 | return reginfo 949 | 950 | """ 951 | Commands 952 | """ 953 | 954 | def nativeCommand(self, cmd2run): 955 | try: 956 | output = pykd.dbgCommand(cmd2run) 957 | return output 958 | except: 959 | # dprintln(traceback.format_exc()) 960 | # dprintln(cmd2run) 961 | return "" 962 | 963 | """ 964 | SEH 965 | """ 966 | 967 | def getSehChain(self): 968 | # http://www.nirsoft.net/kernel_struct/vista/TEB.html 969 | # http://www.nirsoft.net/kernel_struct/vista/NT_TIB.html 970 | # http://www.nirsoft.net/kernel_struct/vista/EXCEPTION_REGISTRATION_RECORD.html 971 | 972 | # x64 has no SEH chain 973 | if arch == 64: 974 | return [] 975 | sehchain = [] 976 | # get top of chain 977 | teb = getTEBAddress() 978 | # _TEB.NtTib(NT_TIB).ExceptionList(PEXCEPTION_REGISTRATION_RECORD) 979 | nextrecord = pykd.ptrPtr(teb) 980 | validrecord = True 981 | while nextrecord != 0xffffffff and pykd.isValid(nextrecord): 982 | # _EXCEPTION_REGISTRATION_RECORD.Next(PEXCEPTION_REGISTRATION_RECORD) 983 | nseh = pykd.ptrPtr(nextrecord) 984 | # _EXCEPTION_REGISTRATION_RECORD.Handler(PEXCEPTION_DISPOSITION) 985 | seh = pykd.ptrPtr(nextrecord + 4) 986 | sehrecord = [nextrecord, seh] 987 | sehchain.append(sehrecord) 988 | nextrecord = nseh 989 | return sehchain 990 | 991 | """ 992 | Memory 993 | """ 994 | 995 | def readMemory(self, location, size): 996 | try: 997 | # return hex2bin(''.join(("%02X" % n) for n in loadBytes(location,size))) 998 | return pykd.loadChars(location, size) 999 | except: 1000 | return "" 1001 | 1002 | def readString(self, location): 1003 | if pykd.isValid(location): 1004 | try: 1005 | return pykd.loadCStr(location) 1006 | except pykd.MemoryException: 1007 | return pykd.loadChars(location, 0x100) 1008 | except: 1009 | return "" 1010 | else: 1011 | return "" 1012 | 1013 | def readWString(self, location): 1014 | if pykd.isValid(location): 1015 | try: 1016 | return pykd.loadWStr(location) 1017 | except pykd.MemoryException: 1018 | return pykd.loadWChars(location, 0x100) 1019 | except: 1020 | return "" 1021 | return 1022 | 1023 | def readUntil(self, start, end): 1024 | if start > end: 1025 | tmp = start 1026 | start = end 1027 | end = tmp 1028 | size = end - start 1029 | return self.readMemory(start, size) 1030 | 1031 | def readLong(self, location): 1032 | return pykd.ptrDWord(location) 1033 | 1034 | def writeMemory(self, location, data): 1035 | A = array.array('B') 1036 | A.fromstring(data) 1037 | pykd.writeBytes(location, A.tolist()) 1038 | return 1039 | 1040 | def writeLong(self, location, dword): 1041 | bytesdword = hexptr2bin(dword) 1042 | self.writeMemory(location, bytesdword) 1043 | return 1044 | 1045 | def getMemoryPages(self): 1046 | offset = 0 1047 | endaddress = 0x7fffffff 1048 | # pagesize = pageSize() 1049 | if len(self.MemoryPages) == 0: 1050 | while offset < endaddress: 1051 | try: 1052 | startaddress, pagesize = pykd.findMemoryRegion(offset) 1053 | pageobj = wpage(startaddress, pagesize) 1054 | if not startaddress in self.MemoryPages: 1055 | self.MemoryPages[startaddress] = pageobj 1056 | offset += pagesize 1057 | except: 1058 | offset += 0x1000 1059 | return self.MemoryPages 1060 | 1061 | def getMemoryPageByAddress(self, address): 1062 | if len(self.MemoryPages) == 0: 1063 | # may never get hit 1064 | self.MemoryPages = self.getMemoryPages() 1065 | pagesize = 0 1066 | startaddress = self.getPageContains(address) 1067 | if startaddress in self.MemoryPages: 1068 | return self.MemoryPages[startaddress] 1069 | else: 1070 | page = wpage(startaddress, pagesize) 1071 | return page 1072 | 1073 | def getMemoryPageByOwner(self, ownerobj): 1074 | return [] 1075 | 1076 | def getPageContains(self, address): 1077 | if len(self.MemoryPages) == 0: 1078 | self.MemoryPages = self.getMemoryPages() 1079 | for pagestart in self.MemoryPages: 1080 | thispage = self.MemoryPages[pagestart] 1081 | pageend = pagestart + thispage.getSize() 1082 | if address >= pagestart and address < pageend: 1083 | return pagestart 1084 | return 0 1085 | 1086 | def getHeapsAddress(self): 1087 | # http://www.nirsoft.net/kernel_struct/vista/PEB.html 1088 | allheaps = [] 1089 | peb = getPEBInfo() 1090 | offset = 0x88 1091 | if arch == 64: 1092 | offset = 0xe8 1093 | # _PEB.NumberOfHeaps(ULONG) 1094 | nrofheaps = int(pykd.ptrDWord(peb + offset)) 1095 | # _PEB.ProcessHeaps(VOID**) 1096 | processheaps = int(peb.ProcessHeaps) 1097 | for i in xrange(nrofheaps): 1098 | # _PEB.ProcessHeaps[i](VOID*) 1099 | nextheap = pykd.ptrPtr(processheaps + (i * (arch / 8))) 1100 | if nextheap == 0x00000000: 1101 | break 1102 | if not nextheap in allheaps: 1103 | allheaps.append(nextheap) 1104 | return allheaps 1105 | 1106 | def getHeap(self, address): 1107 | return wheap(address) 1108 | 1109 | def getPEBAddress(self): 1110 | return getPEBAddress() 1111 | 1112 | def getAllThreads(self): 1113 | allthreads = [] 1114 | for thisthread in pykd.getProcessThreads(): 1115 | allthreads.append(wthread(thisthread)) 1116 | return allthreads 1117 | 1118 | """ 1119 | Modules 1120 | """ 1121 | 1122 | def getModule(self, modulename): 1123 | wmod = None 1124 | self.origmodname = modulename 1125 | if len(PEBModList) == 0: 1126 | getModulesFromPEB() 1127 | try: 1128 | thismod = None 1129 | 1130 | if modulename in PEBModList: 1131 | modentry = PEBModList[modulename] 1132 | fullpath = modentry[1] 1133 | thismod = pykd.module(modulename) 1134 | 1135 | else: 1136 | # find a good one 1137 | for modentry in PEBModList: 1138 | modrecord = PEBModList[modentry] 1139 | # 0 : file 1140 | # 1 : path 1141 | if modulename == modrecord[0]: 1142 | thismod = pykd.module(modentry) 1143 | fullpath = modrecord[1] 1144 | break 1145 | 1146 | if thismod == None: 1147 | # should never hit, as we have tested if modules can be loaded already 1148 | imagename = self.getImageNameForModule(self.origmodname) 1149 | thismod = pykd.module(str(imagename)) 1150 | 1151 | thisimagename = thismod.image() 1152 | thismodname = thismod.name() 1153 | thismodbase = thismod.begin() 1154 | thismodsize = thismod.size() 1155 | thismodpath = thismod.image() 1156 | 1157 | try: 1158 | versionstuff = thismod.getVersion() 1159 | thismodversion = "" 1160 | for vstuff in versionstuff: 1161 | thismodversion = thismodversion + str(vstuff) + "." 1162 | thismodversion = thismodversion.strip(".") 1163 | except: 1164 | thismodversion = "" 1165 | ntHeader = getNtHeaders(thismodbase) 1166 | 1167 | # Get preferred ImageBase from file on disk 1168 | preferredbase = getImageBaseOnDisk(fullpath) 1169 | 1170 | entrypoint = ntHeader.OptionalHeader.AddressOfEntryPoint 1171 | codebase = ntHeader.OptionalHeader.BaseOfCode 1172 | if getArchitecture() == 64: 1173 | database = 0 1174 | else: 1175 | database = ntHeader.OptionalHeader.BaseOfData 1176 | sizeofcode = ntHeader.OptionalHeader.SizeOfCode 1177 | 1178 | wmod = wmodule(thismodname) 1179 | 1180 | wmod.setBaseAddress(thismodbase) 1181 | wmod.setFixupBase(preferredbase) 1182 | wmod.setPath(thismodpath) 1183 | wmod.setSize(thismodsize) 1184 | wmod.setEntry(entrypoint) 1185 | wmod.setCodeBase(codebase) 1186 | wmod.setCodeSize(sizeofcode) 1187 | wmod.setDatabase(database) 1188 | wmod.setVersion(thismodversion) 1189 | except: 1190 | pykd.dprintln("** Error trying to process module %s" % modulename) 1191 | # dprintln(traceback.format_exc()) 1192 | wmod = None 1193 | return wmod 1194 | 1195 | def getAllModules(self): 1196 | getModulesFromPEB() 1197 | for imagename in PEBModList: 1198 | thismodname = PEBModList[imagename][0] 1199 | wmodobject = self.getModule(imagename) 1200 | self.allmodules[thismodname] = wmodobject 1201 | return self.allmodules 1202 | 1203 | def getImageNameForModule(self, modulename): 1204 | # http://www.nirsoft.net/kernel_struct/vista/PEB.html 1205 | # http://www.nirsoft.net/kernel_struct/vista/PEB_LDR_DATA.html 1206 | # http://www.nirsoft.net/kernel_struct/vista/LDR_DATA_TABLE_ENTRY.html 1207 | offset = 0x20 1208 | if arch == 64: 1209 | offset = 0x40 1210 | try: 1211 | imagename = "" 1212 | moduleLst = getModulesFromPEB() 1213 | for mod in moduleLst: 1214 | thismod = pykd.loadUnicodeString(mod.BaseDllName).encode("utf8") 1215 | modparts = thismod.split("\\") 1216 | thismodname = modparts[len(modparts) - 1] 1217 | moduleparts = thismodname.split(".") 1218 | if thismodname.lower() == modulename.lower(): 1219 | # mod.getAddress() + offset = _LDR_DATA_TABLE_ENTRY.SizeOfImage 1220 | baseaddy = int(pykd.ptrPtr(mod.getAddress() + offset)) 1221 | baseaddr = "%08x" % baseaddy 1222 | lmcommand = self.nativeCommand("lm") 1223 | lmlines = lmcommand.split("\n") 1224 | foundinlm = False 1225 | for lmline in lmlines: 1226 | linepieces = lmline.split(" ") 1227 | if linepieces[0].upper() == baseaddr.upper(): 1228 | cnt = 2 1229 | while cnt < len(linepieces) and not foundinlm: 1230 | if linepieces[cnt].strip(" ") != "": 1231 | imagename = linepieces[cnt] 1232 | foundinlm = True 1233 | cnt += 1 1234 | if not foundinlm: 1235 | imagename = "image%s" % baseaddr.lower() 1236 | return imagename 1237 | except: 1238 | pykd.dprintln(traceback.format_exc()) 1239 | return None 1240 | 1241 | """ 1242 | Assembly & Disassembly related routes 1243 | """ 1244 | 1245 | def disasm(self, address): 1246 | return self.getOpcode(address) 1247 | 1248 | def disasmSizeOnly(self, address): 1249 | return self.getOpcode(address) 1250 | 1251 | def disasmForward(self, address, nlines=1): 1252 | opcode = self.getOpcode(address) 1253 | for i in xrange(nlines): 1254 | address += opcode.opsize 1255 | opcode = self.getOpcode(address) 1256 | return opcode 1257 | 1258 | def disasmForwardAddressOnly(self, address, depth): 1259 | # go to correct location 1260 | return self.disasmForward(address, depth).getAddress() 1261 | 1262 | def disasmBackward(self, address, depth): 1263 | while True: 1264 | cmd2run = "ub 0x%08x L%d" % (address, depth) 1265 | try: 1266 | disasmlist = pykd.dbgCommand(cmd2run) 1267 | disasmLinesTmp = disasmlist.split("\n") 1268 | disasmLines = [] 1269 | for line in disasmLinesTmp: 1270 | if line.replace(" ", "") != "": 1271 | disasmLines.append(line) 1272 | lineindex = len(disasmLines) - depth 1273 | if lineindex > -1: 1274 | asmline = disasmLines[lineindex] 1275 | pointer = asmline[0:8] 1276 | return self.getOpcode(hexStrToInt(pointer)) 1277 | else: 1278 | return self.getOpcode(address) 1279 | except: 1280 | # probably invalid instruction, so fake by returning itself 1281 | # caller should check if address is different than what was provided 1282 | if depth == 1: 1283 | return self.getOpcode(address) 1284 | depth -= 1 1285 | 1286 | def assemble(self, code, address=0): 1287 | asm = [] 1288 | for line in code.split('\n'): 1289 | line = line.strip().split(';')[0] 1290 | if not line: 1291 | continue 1292 | 1293 | line = line.replace("RETN", "RET") 1294 | res = script.assembler.AssembleEx(address, line) 1295 | asm.append(res) 1296 | address += len(res) 1297 | 1298 | return binascii.unhexlify("".join(asm)) 1299 | 1300 | def getOpcode(self, address): 1301 | if address in self.OpcodeCache: 1302 | return self.OpcodeCache[address] 1303 | else: 1304 | opcodeobj = opcode(address) 1305 | self.OpcodeCache[address] = opcodeobj 1306 | return opcodeobj 1307 | 1308 | """ 1309 | strings 1310 | """ 1311 | 1312 | def readString(self, address): 1313 | return pykd.loadCStr(address) 1314 | 1315 | """ 1316 | Breakpoints 1317 | """ 1318 | 1319 | def setBreakpoint(self, address): 1320 | try: 1321 | cmd2run = "bp 0x%08x" % address 1322 | self.nativeCommand(cmd2run) 1323 | except: 1324 | return False 1325 | return True 1326 | 1327 | def deleteBreakpoint(self, address): 1328 | getallbps = "bl" 1329 | allbps = self.nativeCommand(getallbps) 1330 | bplines = allbps.split("\n") 1331 | for line in bplines: 1332 | fieldcnt = 0 1333 | if line.replace(" ", "") != "": 1334 | lineparts = line.split(" ") 1335 | id = "" 1336 | type = "" 1337 | bpaddress = "" 1338 | for part in lineparts: 1339 | if part != "": 1340 | fieldcnt += 1 1341 | if fieldcnt == 1: 1342 | id = part 1343 | if fieldcnt == 2: 1344 | type = part 1345 | if fieldcnt == 3: 1346 | bpaddress = part 1347 | break 1348 | if hexStrToInt(bpaddress) == address and id != "": 1349 | rmbp = "bc %s" % id 1350 | self.nativeCommand(rmbp) 1351 | 1352 | def setMemBreakpoint(self, address, memType): 1353 | validtype = False 1354 | bpcommand = "" 1355 | if memType.upper() == "S": 1356 | bpcommand = "ba e 1 0x%08x" % address 1357 | validtype = True 1358 | if memType.upper() == "R": 1359 | bpcommand = "ba r 4 0x%08x" % address 1360 | validtype = True 1361 | if memType.upper() == "W": 1362 | bpcommand = "ba w 4 0x%08x" % address 1363 | validtype = True 1364 | if validtype: 1365 | output = "" 1366 | try: 1367 | output = pykd.dbgCommand(bpcommand) 1368 | except: 1369 | if memType.upper() == "S": 1370 | bpcommand = "bp 0x%08x" % address 1371 | output = pykd.dbgCommand(bpcommand) 1372 | else: 1373 | self.log("** Unable to set memory breakpoint. Check alignment,") 1374 | self.log(" and try to run the following command to get more information:") 1375 | self.log(" %s" % bpcommand) 1376 | 1377 | """ 1378 | Table 1379 | """ 1380 | 1381 | def createTable(self, title, columns): 1382 | return wtable(title, columns) 1383 | 1384 | """ 1385 | Symbols 1386 | """ 1387 | 1388 | def resolveSymbol(self, symbolname): 1389 | resolvecmd = "u %s L1" % symbolname 1390 | try: 1391 | output = self.nativeCommand(resolvecmd) 1392 | outputlines = output.split("\n") 1393 | for line in outputlines: 1394 | lineparts = line.split(" ") 1395 | if len(lineparts) > 1: 1396 | symfound = True 1397 | symaddy = lineparts[0] 1398 | break 1399 | if symfound: 1400 | return symaddy 1401 | else: 1402 | return "" 1403 | except: 1404 | return "" 1405 | 1406 | 1407 | # other classes 1408 | 1409 | class wtable: 1410 | def __init__(self, title, columns): 1411 | self.title = title 1412 | self.columns = columns 1413 | self.values = [] 1414 | 1415 | def add(self, tableindex, values): 1416 | self.values.append(values) 1417 | return None 1418 | 1419 | 1420 | class wmodule: 1421 | def __init__(self, modname): 1422 | self.key = modname 1423 | self.modname = modname 1424 | self.modpath = None 1425 | self.modbase = None 1426 | self.modsize = None 1427 | self.modend = None 1428 | self.entrypoint = None 1429 | self.preferredbase = None 1430 | self.codebase = None 1431 | self.sizeofcode = None 1432 | self.database = None 1433 | self.modversion = None 1434 | 1435 | # setters 1436 | def setBaseAddress(self, value): 1437 | self.modbase = value 1438 | 1439 | def setFixupBase(self, value): 1440 | self.preferredbase = value 1441 | 1442 | def setPath(self, value): 1443 | self.modpath = value 1444 | 1445 | def setSize(self, value): 1446 | self.modsize = value 1447 | 1448 | def setVersion(self, value): 1449 | self.modversion = value 1450 | 1451 | def setEntry(self, value): 1452 | self.entrypoint = value 1453 | 1454 | def setCodeBase(self, value): 1455 | self.codebase = value 1456 | 1457 | def setCodeSize(self, value): 1458 | self.sizeofcode = value 1459 | 1460 | def setDatabase(self, value): 1461 | self.database = value 1462 | 1463 | # getters 1464 | def __str__(self): 1465 | return self.modname 1466 | 1467 | def key(self): 1468 | return self.modname 1469 | 1470 | def getName(self): 1471 | return self.modname 1472 | 1473 | def getBaseAddress(self): 1474 | return self.modbase 1475 | 1476 | def getFixupbase(self): 1477 | return self.preferredbase 1478 | 1479 | def getPath(self): 1480 | return self.modpath 1481 | 1482 | def getSize(self): 1483 | return self.modsize 1484 | 1485 | def getIssystemdll(self): 1486 | modisos = False 1487 | if "WINDOWS" in self.modpath.upper(): 1488 | modisos = True 1489 | else: 1490 | modisos = False 1491 | # exceptions 1492 | if self.modname.lower() == "ntdll": 1493 | modisos = True 1494 | self.issystemdll = modisos 1495 | return self.issystemdll 1496 | 1497 | def getVersion(self): 1498 | return self.modversion 1499 | 1500 | def getEntry(self): 1501 | return self.entrypoint 1502 | 1503 | def getCodebase(self): 1504 | return self.codebase 1505 | 1506 | def getCodesize(self): 1507 | return self.sizeofcode 1508 | 1509 | def getDatabase(self): 1510 | return self.database 1511 | 1512 | def getSymbols(self): 1513 | # enumerate IAT and EAT and put into a symbol object 1514 | ntHeader = getNtHeaders(self.modbase) 1515 | pSize = 4 1516 | if arch == 64: 1517 | pSize = 8 1518 | iatlist = self.getIATList(ntHeader, pSize) 1519 | symbollist = {} 1520 | for iatEntry in iatlist: 1521 | iatEntryAddress = iatEntry 1522 | iatEntryName = iatlist[iatEntry] 1523 | sym = wsymbol("Import", iatEntryAddress, iatEntryName) 1524 | symbollist[iatEntryAddress] = sym 1525 | 1526 | eatlist = self.getEATList(ntHeader, pSize) 1527 | for eatEntry in eatlist: 1528 | eatEntryName = eatEntry 1529 | eatEntryAddress = eatlist[eatEntry] 1530 | sym = wsymbol("Export", eatEntryAddress, eatEntryName) 1531 | symbollist[eatEntryAddress] = sym 1532 | return symbollist 1533 | 1534 | def getIATList(self, ntHeader, pSize): 1535 | # If Import Address Table Directory (DataDirectory[12]) is set this will work. 1536 | # The fallback case of Import Directory (DataDirectory[1]) will produce garbage. 1537 | iatlist = {} 1538 | iatdir = ntHeader.OptionalHeader.DataDirectory[12] 1539 | if iatdir.Size == 0: 1540 | iatdir = ntHeader.OptionalHeader.DataDirectory[1] 1541 | if iatdir.Size > 0: 1542 | iatAddr = self.modbase + iatdir.VirtualAddress 1543 | for i in range(0, iatdir.Size / pSize): 1544 | iatEntry = pykd.ptrPtr(iatAddr + i * pSize) 1545 | if iatEntry != None and iatEntry != 0: 1546 | symbolName = pykd.findSymbol(iatEntry) 1547 | if "!" in symbolName: 1548 | iatlist[iatAddr + i * pSize] = symbolName 1549 | return iatlist 1550 | 1551 | def getEATList(self, ntHeader, pSize): 1552 | # http://www.pinvoke.net/default.aspx/Structures.IMAGE_EXPORT_DIRECTORY 1553 | eatlist = {} 1554 | if ntHeader.OptionalHeader.DataDirectory[0].Size > 0: 1555 | eatAddr = self.modbase + ntHeader.OptionalHeader.DataDirectory[0].VirtualAddress 1556 | # eatAddr + 0x18 = IMAGE_EXPORT_DIRECTORY.NumberOfNames(DWORD) 1557 | nr_of_names = pykd.ptrDWord(eatAddr + 0x18) 1558 | # eatAddr + 0x20 = IMAGE_EXPORT_DIRECTORY.AddressOfNames(DWORD) 1559 | rva_of_names = self.modbase + pykd.ptrDWord(eatAddr + 0x20) 1560 | # eatAddr + 0x1c = IMAGE_EXPORT_DIRECTORY.AddressOfFunctions(DWORD) 1561 | address_of_functions = self.modbase + pykd.ptrDWord(eatAddr + 0x1c) 1562 | for i in range(0, nr_of_names): 1563 | # IMAGE_EXPORT_DIRECTORY.AddressOfNames[i](DWORD) 1564 | eatName = pykd.loadCStr(self.modbase + pykd.ptrDWord(rva_of_names + 4 * i)) 1565 | # IMAGE_EXPORT_DIRECTORY.AddressOfFunctions[i](DWORD) 1566 | eatAddress = self.modbase + pykd.ptrDWord(address_of_functions + 4 * i) 1567 | eatlist[eatName] = eatAddress 1568 | return eatlist 1569 | 1570 | def getSectionAddress(self, sectionname): 1571 | ntHeader = getNtHeaders(self.modbase) 1572 | nrsections = int(ntHeader.FileHeader.NumberOfSections) 1573 | sectionsize = 40 1574 | sizeOptionalHeader = int(ntHeader.FileHeader.SizeOfOptionalHeader) 1575 | for sectioncnt in xrange(nrsections): 1576 | # IMAGE_SECTION_HEADER[i] 1577 | sectionstart = (ntHeader.OptionalHeader.getAddress() + sizeOptionalHeader) + (sectioncnt * sectionsize) 1578 | thissection = pykd.loadChars(sectionstart, 8).rstrip('\0') 1579 | if thissection == sectionname: 1580 | # IMAGE_SECTION_HEADER.SizeOfRawData(DWORD) 1581 | thissectionsize = pykd.ptrDWord(sectionstart + 0x8 + 0x8) 1582 | # IMAGE_SECTION_HEADER.VirtualAddress(DWORD) 1583 | thissectionrva = pykd.ptrDWord(sectionstart + 0x4 + 0x8) 1584 | thissectionstart = self.modbase + thissectionrva 1585 | return thissectionstart 1586 | return 0 1587 | 1588 | 1589 | class wsymbol(): 1590 | def __init__(self, type, address, name): 1591 | self.type = type 1592 | self.address = address 1593 | self.name = name 1594 | 1595 | def getType(self): 1596 | return self.type 1597 | 1598 | def getAddress(self): 1599 | return self.address 1600 | 1601 | def getName(self): 1602 | return self.name 1603 | 1604 | 1605 | class wpage(): 1606 | def __init__(self, begin, size): 1607 | self.begin = begin 1608 | self.size = size 1609 | self.end = self.begin + self.size 1610 | self.protect = None 1611 | 1612 | def getSize(self): 1613 | return self.size 1614 | 1615 | def getMemory(self): 1616 | if self.getAccess() > 0x1: 1617 | try: 1618 | data = pykd.loadChars(self.begin, self.size) 1619 | return data 1620 | except: 1621 | return None 1622 | else: 1623 | return None 1624 | 1625 | def getMemoryOld(self): 1626 | if self.getAccess() > 0x1: 1627 | try: 1628 | nrofdwords = self.size / 4 1629 | delta = self.size - (nrofdwords * 4) 1630 | dwords = pykd.loadDWords(self.begin, nrofdwords) 1631 | curpos = self.begin + (nrofdwords * 4) 1632 | remainingbytes = pykd.loadBytes(curpos, delta) 1633 | allbytes = [] 1634 | for dword in dwords: 1635 | dwordhex = "%08x" % dword 1636 | allbytes.append(dwordhex[6:8] + dwordhex[4:6] + dwordhex[2:4] + dwordhex[0:2]) 1637 | dwords = None 1638 | for byte in remainingbytes: 1639 | allbytes.append("%02x" % bytes) 1640 | data = hex2bin(''.join(allbytes)) 1641 | # return hex2bin(''.join(("%02X" % n) for n in loadBytes(self.begin,self.size))) 1642 | return data 1643 | except: 1644 | return None 1645 | else: 1646 | return None 1647 | 1648 | def getAccess(self, human=False): 1649 | humanaccess = { 1650 | 0x01: "PAGE_NOACCESS", 1651 | 0x02: "PAGE_READONLY", 1652 | 0x04: "PAGE_READWRITE", 1653 | 0x08: "PAGE_WRITECOPY", 1654 | 0x10: "PAGE_EXECUTE", 1655 | 0x20: "PAGE_EXECUTE_READ", 1656 | 0x40: "PAGE_EXECUTE_READWRITE", 1657 | 0x80: "PAGE_EXECUTE_WRITECOPY" 1658 | } 1659 | 1660 | modifiers = { 1661 | 0x100: "PAGE_GUARD", 1662 | 0x200: "PAGE_NOCACHE", 1663 | 0x400: "PAGE_WRITECOMBINE" 1664 | } 1665 | 1666 | modifaccess = {} 1667 | for access in humanaccess: 1668 | newaccess = access 1669 | newacl = humanaccess[access] 1670 | for modif in modifiers: 1671 | newaccess += modif 1672 | newacl = newacl + " " + modifiers[modif] 1673 | modifaccess[newaccess] = newacl 1674 | 1675 | for modif in modifaccess: 1676 | humanaccess[modif] = modifaccess[modif] 1677 | 1678 | if self.protect == None: 1679 | try: 1680 | self.protect = pykd.getVaProtect(self.begin) 1681 | except: 1682 | self.protect = 0x1 1683 | if self.protect == 0x0: 1684 | self.protect = 0x1 1685 | if not human: 1686 | return self.protect 1687 | else: 1688 | if self.protect in humanaccess: 1689 | return humanaccess[self.protect] 1690 | else: 1691 | return "" 1692 | 1693 | def getBegin(self): 1694 | return self.begin 1695 | 1696 | def getBaseAddress(self): 1697 | return self.begin 1698 | 1699 | def getSection(self): 1700 | global PageSections 1701 | if self.begin in PageSections: 1702 | return PageSections[self.begin] 1703 | else: 1704 | sectiontoreturn = "" 1705 | imagename = getModuleFromAddress(self.begin) 1706 | if not imagename == None: 1707 | thismod = pykd.module(imagename.name()) 1708 | thismodbase = thismod.begin() 1709 | thismodend = thismod.end() 1710 | if self.begin >= thismodbase and self.begin <= thismodend: 1711 | # find sections and their addresses 1712 | ntHeader = getNtHeaders(thismodbase) 1713 | nrsections = int(ntHeader.FileHeader.NumberOfSections) 1714 | sectionsize = 40 1715 | sizeOptionalHeader = int(ntHeader.FileHeader.SizeOfOptionalHeader) 1716 | for sectioncnt in xrange(nrsections): 1717 | sectionstart = (ntHeader.OptionalHeader.getAddress() + sizeOptionalHeader) + ( 1718 | sectioncnt * sectionsize) 1719 | thissection = pykd.loadChars(sectionstart, 8).rstrip('\0') 1720 | # IMAGE_SECTION_HEADER.SizeOfRawData(DWORD) 1721 | thissectionsize = pykd.ptrDWord(sectionstart + 0x8 + 0x8) 1722 | # IMAGE_SECTION_HEADER.VirtualAddress(DWORD) 1723 | thissectionrva = pykd.ptrDWord(sectionstart + 0x4 + 0x8) 1724 | thissectionstart = thismodbase + thissectionrva 1725 | thissectionend = thissectionstart + thissectionsize 1726 | if (thissectionstart <= self.begin) and (self.begin <= thissectionend): 1727 | sectiontoreturn = thissection 1728 | break 1729 | else: 1730 | PageSections[self.begin] = thissection 1731 | PageSections[self.begin] = sectiontoreturn 1732 | return sectiontoreturn 1733 | PageSections[self.begin] = sectiontoreturn 1734 | return sectiontoreturn 1735 | else: 1736 | return "" 1737 | 1738 | 1739 | class LogBpHook(): 1740 | def __init__(self): 1741 | return 1742 | 1743 | 1744 | class Function: 1745 | def __init__(self, obj, address): 1746 | self.function_allmodules = {} 1747 | self.address = address 1748 | self.obj = obj 1749 | 1750 | def getName(self): 1751 | modname = "unknown" 1752 | funcname = "unknown" 1753 | # get module this address belongs to 1754 | self.function_allmodules = self.obj.getAllModules() 1755 | for objmod in self.function_allmodules: 1756 | thismod = self.function_allmodules[objmod] 1757 | startaddress = thismod.getBaseAddress() 1758 | size = thismod.getSize() 1759 | endaddress = startaddress + size 1760 | if self.address >= startaddress and self.address <= endaddress: 1761 | modname = thismod.getName().lower() 1762 | syms = thismod.getSymbols() 1763 | for sym in syms: 1764 | if syms[sym].getType().startswith("Export"): 1765 | eatsym = syms[sym] 1766 | if eatsym.getAddress() == self.address: 1767 | funcname = eatsym.getName() 1768 | break 1769 | thename = "%s.%s" % (modname, funcname) 1770 | return thename 1771 | 1772 | def hasAddress(self): 1773 | return False 1774 | 1775 | 1776 | class opcode: 1777 | def __init__(self, address): 1778 | self.address = address 1779 | self._basicinfo = x64dbg.BASIC_INSTRUCTION_INFO() 1780 | x64dbg.DbgDisasmFastAt(self.address, self._basicinfo) 1781 | self.instruction = self._basicinfo.instruction.upper() 1782 | self.opsize = self._basicinfo.size 1783 | 1784 | def isJmp(self): 1785 | if self.instruction.startswith("JMP"): 1786 | return True 1787 | return False 1788 | 1789 | def isCall(self): 1790 | if self.instruction.startswith("CALL"): 1791 | return True 1792 | return False 1793 | 1794 | def isPush(self): 1795 | if self.instruction.startswith("PUSH"): 1796 | return True 1797 | return False 1798 | 1799 | def isPop(self): 1800 | if self.instruction.startswith("POP"): 1801 | return True 1802 | return False 1803 | 1804 | def isRet(self): 1805 | if self.instruction.startswith("RET"): 1806 | return True 1807 | return False 1808 | 1809 | def isRep(self): 1810 | if self.instruction.startswith("REP"): 1811 | return True 1812 | return False 1813 | 1814 | def getDisasm(self): 1815 | return self.instruction 1816 | 1817 | def getAddress(self): 1818 | return self.address 1819 | 1820 | def getDump(self): 1821 | # XXX: LoadChars is too slow to be usable right now!! 1822 | # if self.dump: 1823 | # return self.dump 1824 | # else: 1825 | # self.dump = binascii.hexlify(pykd.loadChars(self.address, self.opsize)) 1826 | # return self.dump 1827 | return "" 1828 | 1829 | 1830 | class wthread: 1831 | def __init__(self, address): 1832 | self.address = address 1833 | 1834 | def getTEB(self): 1835 | # return address of the TEB 1836 | return self.address 1837 | 1838 | def getId(self): 1839 | # http://www.nirsoft.net/kernel_struct/vista/TEB.html 1840 | # http://www.nirsoft.net/kernel_struct/vista/CLIENT_ID.html 1841 | teb = self.getTEB() 1842 | offset = 0x24 1843 | if arch == 64: 1844 | offset = 0x48 1845 | # _TEB.ClientId(CLIENT_ID).UniqueThread(PVOID) 1846 | tid = pykd.ptrDWord(teb + offset) 1847 | return tid 1848 | 1849 | 1850 | class wheap: 1851 | def __init__(self, address): 1852 | self.address = address 1853 | 1854 | def getChunks(self, address): 1855 | return {} 1856 | 1857 | 1858 | class LogBpHook: 1859 | def __init__(self): 1860 | return 1861 | --------------------------------------------------------------------------------