├── .gitattributes ├── .gitignore ├── LICENSE.md ├── README.md ├── main.py └── pdbparse ├── __init__.py ├── construct ├── __init__.py ├── adapters.py ├── core.py ├── debug.py ├── formats │ ├── __init__.py │ ├── archive │ │ ├── __init__.py │ │ ├── pkzip.py │ │ └── rar.py │ ├── audio │ │ ├── __init__.py │ │ ├── mp3.py │ │ ├── ogg.py │ │ └── wav.py │ ├── data │ │ ├── __init__.py │ │ └── cap.py │ ├── document │ │ ├── __init__.py │ │ ├── doc.py │ │ ├── pdf.py │ │ └── postscript.py │ ├── executable │ │ ├── __init__.py │ │ ├── elf32.py │ │ └── pe32.py │ ├── filesystem │ │ ├── __init__.py │ │ ├── cdfs.py │ │ ├── ext2.py │ │ ├── ext3.py │ │ ├── fat12.py │ │ ├── fat16.py │ │ ├── fat32.py │ │ ├── mbr.py │ │ └── ntfs5.py │ ├── graphics │ │ ├── __init__.py │ │ ├── bmp.py │ │ ├── emf.py │ │ ├── gif.py │ │ ├── jpeg.py │ │ ├── png.py │ │ └── wmf.py │ └── video │ │ ├── __init__.py │ │ ├── avi.py │ │ ├── divx.py │ │ ├── mpeg2.py │ │ └── mpeg3.py ├── lib │ ├── __init__.py │ ├── binary.py │ ├── bitstream.py │ ├── container.py │ ├── hex.py │ └── utils.py ├── macros.py ├── protocols │ ├── __init__.py │ ├── application │ │ ├── __init__.py │ │ ├── dns.py │ │ ├── ftp.py │ │ ├── http.py │ │ ├── irc.py │ │ ├── netbios.py │ │ ├── pop3.py │ │ ├── smtp.py │ │ ├── snmp.py │ │ ├── telnet.py │ │ ├── tftp.py │ │ ├── xdr.py │ │ └── xwindows.py │ ├── ipstack.py │ ├── layer2 │ │ ├── __init__.py │ │ ├── arp.py │ │ ├── ethernet.py │ │ └── mtp2.py │ ├── layer3 │ │ ├── __init__.py │ │ ├── dhcpv4.py │ │ ├── dhcpv6.py │ │ ├── icmpv4.py │ │ ├── icmpv6.py │ │ ├── ipv4.py │ │ ├── ipv6.py │ │ ├── ipx.py │ │ └── mtp3.py │ ├── layer4 │ │ ├── __init__.py │ │ ├── isup.py │ │ ├── sctp.py │ │ ├── tcp.py │ │ └── udp.py │ └── ss7stack.py └── text.py ├── dbgold.py ├── dbi.py ├── fpo.py ├── gdata.py ├── info.py ├── omap.py ├── pe.py ├── peinfo.py ├── postfix_eval.py ├── symlookup.py ├── tpi.py ├── undecorate.py └── undname.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Arthur Gerkis 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 | 2 | # IDA PDB Loader (IPL) 3 | 4 | ## What is this 5 | 6 | This is a simple IDA plugin to load PDB symbols. The problem is that 7 | sometimes IDA crashes for me when trying to load symbols, so I came up 8 | with this quick and dirty alternative. 9 | 10 | 11 | ## Requirements & installation 12 | 13 | This plugin relies on Python pdbparse module 14 | (https://github.com/moyix/pdbparse), and I have it included in plugin, 15 | because I had to do minor modifications to code. 16 | 17 | pdbparse also relies on other Python module named construct 18 | (https://pypi.python.org/pypi/construct). I have included construct in 19 | plugin. pdbparse is using old API (version 2.0.0 is known to support 20 | it). 21 | 22 | 23 | ## How it works 24 | 25 | Load plugin (Alt+F7), pick PDB file and wait. It will take a while if PDB 26 | file is big. 27 | 28 | 29 | ## Other things to know 30 | 31 | Tested only on IDA 6.9 x32. Sometimes fails to find symbols for certain 32 | functions. Currently only renames functions. 33 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!c:\\python27\python.exe 2 | # -*- coding: utf-8 -*- 3 | 4 | '''IDA PDB Loader.''' 5 | 6 | # FIXME: fails to find non-mangled names, pdbparse bug? 7 | 8 | 9 | import traceback 10 | 11 | import idautils 12 | import idaapi 13 | import idc 14 | 15 | import pdbparse.symlookup 16 | idaapi.require('pdbparse.symlookup') 17 | 18 | __author__ = 'Arthur Gerkis' 19 | __version__ = '0.0.2' 20 | 21 | 22 | class Plugin(object): 23 | '''IDA Pro Plugin''' 24 | def __init__(self): 25 | super(Plugin, self).__init__() 26 | self.symbol_path = '' 27 | self.image_base = 0 28 | self.PDBLookup = None 29 | 30 | def run(self): 31 | '''Public function.''' 32 | 33 | self.symbol_path = idc.AskFile(0, '*.pdb', 'Choose PDB file...') 34 | self.image_base = idaapi.get_imagebase() 35 | 36 | print "IPL: Loading PDB data, might take a while..." 37 | self.PDBLookup = pdbparse.symlookup.Lookup([(self.symbol_path, self.image_base)]) 38 | 39 | if not self.PDBLookup: 40 | print "IPL: PDBLookup failed to initialize, exiting." 41 | return 42 | 43 | self._rename_functions() 44 | return 45 | 46 | def _rename_functions(self): 47 | '''Rename functions.''' 48 | 49 | print "IPL: Started to rename functions..." 50 | 51 | failed = 0 52 | total = 0 53 | for function in idautils.Functions(): 54 | total += 1 55 | pdb_mangled_name = self.PDBLookup.lookup(function, True) 56 | if not pdb_mangled_name: 57 | failed += 1 58 | print "IPL: Failed to find symbol for function: 0x{:08x}".format(function) 59 | continue 60 | _, mangled_function_name = pdb_mangled_name.split('!') 61 | # https://www.hex-rays.com/products/ida/support/idadoc/203.shtml 62 | idc.MakeNameEx(function, mangled_function_name, 63 | idc.SN_AUTO | idc.SN_NOCHECK) 64 | print "IPL: Total {} functions, {} failed to rename.".format(total, failed) 65 | 66 | 67 | def main(): 68 | '''Main.''' 69 | try: 70 | Plugin().run() 71 | except Exception: 72 | print traceback.format_exc() 73 | 74 | 75 | if __name__ == '__main__': 76 | main() 77 | -------------------------------------------------------------------------------- /pdbparse/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import absolute_import 3 | 4 | from struct import unpack,calcsize 5 | 6 | PDB_STREAM_ROOT = 0 # PDB root directory 7 | PDB_STREAM_PDB = 1 # PDB stream info 8 | PDB_STREAM_TPI = 2 # type info 9 | PDB_STREAM_DBI = 3 # debug info 10 | 11 | _PDB2_SIGNATURE = b"Microsoft C/C++ program database 2.00\r\n\032JG\0\0" 12 | _PDB2_SIGNATURE_LEN = len(_PDB2_SIGNATURE) 13 | _PDB2_FMT = "<%dsIHHII" % _PDB2_SIGNATURE_LEN 14 | _PDB2_FMT_SIZE = calcsize(_PDB2_FMT) 15 | 16 | _PDB7_SIGNATURE = b"Microsoft C/C++ MSF 7.00\r\n\x1ADS\0\0\0" 17 | _PDB7_SIGNATURE_LEN = len(_PDB7_SIGNATURE) 18 | _PDB7_FMT = "<%dsIIIII" % _PDB7_SIGNATURE_LEN 19 | _PDB7_FMT_SIZE = calcsize(_PDB7_FMT) 20 | 21 | # Internal method to calculate the number of pages required 22 | # to store a stream of size "length", given a page size of 23 | # "pagesize" 24 | def _pages(length, pagesize): 25 | num_pages = length // pagesize 26 | if (length % pagesize): num_pages += 1 27 | return num_pages 28 | 29 | class StreamFile: 30 | def __init__(self, fp, pages, size=-1, page_size=0x1000): 31 | self.fp = fp 32 | self.pages = pages 33 | self.page_size = page_size 34 | if size == -1: self.end = len(pages)*page_size 35 | else: self.end = size 36 | self.pos = 0 37 | def read(self, size=-1): 38 | if size == -1: 39 | pn_start, off_start = self._get_page(self.pos) 40 | pdata = self._read_pages(self.pages[pn_start:]) 41 | self.pos = self.end 42 | return pdata[off_start:self.end-off_start] 43 | else: 44 | pn_start, off_start = self._get_page(self.pos) 45 | pn_end, off_end = self._get_page(self.pos + size) 46 | pdata = self._read_pages(self.pages[pn_start:pn_end+1]) 47 | self.pos += size 48 | 49 | return pdata[off_start:-(self.page_size - off_end)] 50 | def seek(self, offset, whence=0): 51 | if whence == 0: 52 | self.pos = offset 53 | elif whence == 1: 54 | self.pos += offset 55 | elif whence == 2: 56 | self.pos = self.end + offset 57 | 58 | if self.pos < 0: self.pos = 0 59 | if self.pos > self.end: self.pos = self.end 60 | def tell(self): 61 | return self.pos 62 | def close(self): 63 | self.fp.close() 64 | 65 | # Private helper methods 66 | def _get_page(self, offset): 67 | return (offset // self.page_size, offset % self.page_size) 68 | def _read_pages(self, pages): 69 | s = b"" 70 | for pn in pages: 71 | self.fp.seek(pn*self.page_size) 72 | s += self.fp.read(self.page_size) 73 | return s 74 | 75 | class PDBStream: 76 | """Base class for PDB stream types. 77 | 78 | data: the data that makes up this stream 79 | index: the index of this stream in the file 80 | page_size: the size of a page, in bytes, of the PDB file 81 | containing this stream 82 | 83 | The constructor signature here is valid for all subclasses. 84 | 85 | """ 86 | def _get_data(self): 87 | pos = self.stream_file.tell() 88 | self.stream_file.seek(0) 89 | data = self.stream_file.read() 90 | self.stream_file.seek(pos) 91 | return data 92 | data = property(fget=_get_data) 93 | 94 | def __init__(self, fp, pages, index, size=-1, page_size=0x1000, fast_load=False, parent=None): 95 | self.fp = fp 96 | self.fast_load = fast_load 97 | self.parent = parent 98 | self.pages = pages 99 | self.index = index 100 | self.page_size = page_size 101 | if size == -1: self.size = len(pages)*page_size 102 | else: self.size = size 103 | self.stream_file = StreamFile(self.fp, pages, size=size, page_size=page_size) 104 | 105 | def reload(self): 106 | """Convenience method. Reloads a PDBStream. May return a more specialized type.""" 107 | try: 108 | pdb_cls = self.parent._stream_map[self.index] 109 | except (KeyError,AttributeError): 110 | pdb_cls = PDBStream 111 | return pdb_cls(self.fp, self.pages, self.index, size=self.size, 112 | page_size=self.page_size, fast_load=self.fast_load, 113 | parent=self.parent) 114 | 115 | class ParsedPDBStream(PDBStream): 116 | def __init__(self, fp, pages, index=PDB_STREAM_PDB, size=-1, 117 | page_size=0x1000, fast_load=False, parent=None): 118 | PDBStream.__init__(self, fp, pages, index, size=size, page_size=page_size, fast_load=fast_load, parent=parent) 119 | if fast_load: return 120 | else: self.load() 121 | 122 | def load(self): 123 | pass 124 | 125 | class PDB7RootStream(PDBStream): 126 | """Class representing the root stream of a PDB file. 127 | 128 | Parsed streams are available as a tuple of (size, [list of pages]) 129 | describing each stream in the "streams" member of this class. 130 | 131 | """ 132 | def __init__(self, fp, pages, index=PDB_STREAM_ROOT, size=-1, 133 | page_size=0x1000, fast_load=False): 134 | PDBStream.__init__(self, fp, pages, index, size=size, page_size=page_size) 135 | 136 | data = self.data 137 | 138 | (self.num_streams,) = unpack(">> from construct import * 9 | 10 | Example: 11 | >>> from construct import * 12 | >>> 13 | >>> s = Struct("foo", 14 | ... UBInt8("a"), 15 | ... UBInt16("b"), 16 | ... ) 17 | >>> 18 | >>> s.parse("\x01\x02\x03") 19 | Container(a = 1, b = 515) 20 | >>> print s.parse("\x01\x02\x03") 21 | Container: 22 | a = 1 23 | b = 515 24 | >>> s.build(Container(a = 1, b = 0x0203)) 25 | "\x01\x02\x03" 26 | """ 27 | from core import * 28 | from adapters import * 29 | from macros import * 30 | from debug import Probe, Debugger 31 | 32 | 33 | #=============================================================================== 34 | # meta data 35 | #=============================================================================== 36 | __author__ = "tomer filiba (tomerfiliba [at] gmail.com)" 37 | __version__ = "2.00" 38 | 39 | #=============================================================================== 40 | # shorthands 41 | #=============================================================================== 42 | Bits = BitField 43 | Byte = UBInt8 44 | Bytes = Field 45 | Const = ConstAdapter 46 | Tunnel = TunnelAdapter 47 | Embed = Embedded 48 | 49 | #=============================================================================== 50 | # backward compatibility with RC1 51 | #=============================================================================== 52 | MetaField = Field 53 | MetaBytes = Field 54 | GreedyRepeater = GreedyRange 55 | OptionalGreedyRepeater = OptionalGreedyRange 56 | Repeater = Array 57 | StrictRepeater = Array 58 | MetaRepeater = Array 59 | OneOfValidator = OneOf 60 | NoneOfValidator = NoneOf 61 | 62 | #=============================================================================== 63 | # don't want to leek these out... 64 | #=============================================================================== 65 | del encode_bin, decode_bin, int_to_bin, bin_to_int, swap_bytes 66 | del Packer, StringIO 67 | del HexString, LazyContainer, AttrDict 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /pdbparse/construct/adapters.py: -------------------------------------------------------------------------------- 1 | from core import Adapter, AdaptationError, Pass 2 | from lib import int_to_bin, bin_to_int, swap_bytes, StringIO 3 | from lib import FlagsContainer, HexString 4 | 5 | 6 | #=============================================================================== 7 | # exceptions 8 | #=============================================================================== 9 | class BitIntegerError(AdaptationError): 10 | __slots__ = [] 11 | class MappingError(AdaptationError): 12 | __slots__ = [] 13 | class ConstError(AdaptationError): 14 | __slots__ = [] 15 | class ValidationError(AdaptationError): 16 | __slots__ = [] 17 | class PaddingError(AdaptationError): 18 | __slots__ = [] 19 | 20 | #=============================================================================== 21 | # adapters 22 | #=============================================================================== 23 | class BitIntegerAdapter(Adapter): 24 | """ 25 | Adapter for bit-integers (converts bitstrings to integers, and vice versa). 26 | See BitField. 27 | 28 | Parameters: 29 | * subcon - the subcon to adapt 30 | * width - the size of the subcon, in bits 31 | * swapped - whether to swap byte order (little endian/big endian). 32 | default is False (big endian) 33 | * signed - whether the value is signed (two's complement). the default 34 | is False (unsigned) 35 | * bytesize - number of bits per byte, used for byte-swapping (if swapped). 36 | default is 8. 37 | """ 38 | __slots__ = ["width", "swapped", "signed", "bytesize"] 39 | def __init__(self, subcon, width, swapped = False, signed = False, 40 | bytesize = 8): 41 | Adapter.__init__(self, subcon) 42 | self.width = width 43 | self.swapped = swapped 44 | self.signed = signed 45 | self.bytesize = bytesize 46 | def _encode(self, obj, context): 47 | if obj < 0 and not self.signed: 48 | raise BitIntegerError("object is negative, but field is not signed", 49 | obj) 50 | obj2 = int_to_bin(obj, width = self.width) 51 | if self.swapped: 52 | obj2 = swap_bytes(obj2, bytesize = self.bytesize) 53 | return obj2 54 | def _decode(self, obj, context): 55 | if self.swapped: 56 | obj = swap_bytes(obj, bytesize = self.bytesize) 57 | return bin_to_int(obj, signed = self.signed) 58 | 59 | class MappingAdapter(Adapter): 60 | """ 61 | Adapter that maps objects to other objects. 62 | See SymmetricMapping and Enum. 63 | 64 | Parameters: 65 | * subcon - the subcon to map 66 | * decoding - the decoding (parsing) mapping (a dict) 67 | * encoding - the encoding (building) mapping (a dict) 68 | * decdefault - the default return value when the object is not found 69 | in the decoding mapping. if no object is given, an exception is raised. 70 | if `Pass` is used, the unmapped object will be passed as-is 71 | * encdefault - the default return value when the object is not found 72 | in the encoding mapping. if no object is given, an exception is raised. 73 | if `Pass` is used, the unmapped object will be passed as-is 74 | """ 75 | __slots__ = ["encoding", "decoding", "encdefault", "decdefault"] 76 | def __init__(self, subcon, decoding, encoding, 77 | decdefault = NotImplemented, encdefault = NotImplemented): 78 | Adapter.__init__(self, subcon) 79 | self.decoding = decoding 80 | self.encoding = encoding 81 | self.decdefault = decdefault 82 | self.encdefault = encdefault 83 | def _encode(self, obj, context): 84 | try: 85 | return self.encoding[obj] 86 | except (KeyError, TypeError): 87 | if self.encdefault is NotImplemented: 88 | raise MappingError("no encoding mapping for %r" % (obj,)) 89 | if self.encdefault is Pass: 90 | return obj 91 | return self.encdefault 92 | def _decode(self, obj, context): 93 | try: 94 | return self.decoding[obj] 95 | except (KeyError, TypeError): 96 | if self.decdefault is NotImplemented: 97 | raise MappingError("no decoding mapping for %r" % (obj,)) 98 | if self.decdefault is Pass: 99 | return obj 100 | return self.decdefault 101 | 102 | class FlagsAdapter(Adapter): 103 | """ 104 | Adapter for flag fields. Each flag is extracted from the number, resulting 105 | in a FlagsContainer object. Not intended for direct usage. 106 | See FlagsEnum. 107 | 108 | Parameters 109 | * subcon - the subcon to extract 110 | * flags - a dictionary mapping flag-names to their value 111 | """ 112 | __slots__ = ["flags"] 113 | def __init__(self, subcon, flags): 114 | Adapter.__init__(self, subcon) 115 | self.flags = flags 116 | def _encode(self, obj, context): 117 | flags = 0 118 | for name, value in self.flags.iteritems(): 119 | if getattr(obj, name, False): 120 | flags |= value 121 | return flags 122 | def _decode(self, obj, context): 123 | obj2 = FlagsContainer() 124 | for name, value in self.flags.iteritems(): 125 | setattr(obj2, name, bool(obj & value)) 126 | return obj2 127 | 128 | class StringAdapter(Adapter): 129 | """ 130 | Adapter for strings. Converts a sequence of characters into a python 131 | string, and optionally handles character encoding. 132 | See String. 133 | 134 | Parameters: 135 | * subcon - the subcon to convert 136 | * encoding - the character encoding name (e.g., "utf8"), or None to 137 | return raw bytes (usually 8-bit ASCII). 138 | """ 139 | __slots__ = ["encoding"] 140 | def __init__(self, subcon, encoding = None): 141 | Adapter.__init__(self, subcon) 142 | self.encoding = encoding 143 | def _encode(self, obj, context): 144 | if self.encoding: 145 | obj = obj.encode(self.encoding) 146 | return obj 147 | def _decode(self, obj, context): 148 | obj = "".join(obj) 149 | if self.encoding: 150 | obj = obj.decode(self.encoding) 151 | return obj 152 | 153 | class PaddedStringAdapter(Adapter): 154 | r""" 155 | Adapter for padded strings. 156 | See String. 157 | 158 | Parameters: 159 | * subcon - the subcon to adapt 160 | * padchar - the padding character. default is "\x00". 161 | * paddir - the direction where padding is placed ("right", "left", or 162 | "center"). the default is "right". 163 | * trimdir - the direction where trimming will take place ("right" or 164 | "left"). the default is "right". trimming is only meaningful for 165 | building, when the given string is too long. 166 | """ 167 | __slots__ = ["padchar", "paddir", "trimdir"] 168 | def __init__(self, subcon, padchar = "\x00", paddir = "right", 169 | trimdir = "right"): 170 | if paddir not in ("right", "left", "center"): 171 | raise ValueError("paddir must be 'right', 'left' or 'center'", 172 | paddir) 173 | if trimdir not in ("right", "left"): 174 | raise ValueError("trimdir must be 'right' or 'left'", trimdir) 175 | Adapter.__init__(self, subcon) 176 | self.padchar = padchar 177 | self.paddir = paddir 178 | self.trimdir = trimdir 179 | def _decode(self, obj, context): 180 | if self.paddir == "right": 181 | obj = obj.rstrip(self.padchar) 182 | elif self.paddir == "left": 183 | obj = obj.lstrip(self.padchar) 184 | else: 185 | obj = obj.strip(self.padchar) 186 | return obj 187 | def _encode(self, obj, context): 188 | size = self._sizeof(context) 189 | if self.paddir == "right": 190 | obj = obj.ljust(size, self.padchar) 191 | elif self.paddir == "left": 192 | obj = obj.rjust(size, self.padchar) 193 | else: 194 | obj = obj.center(size, self.padchar) 195 | if len(obj) > size: 196 | if self.trimdir == "right": 197 | obj = obj[:size] 198 | else: 199 | obj = obj[-size:] 200 | return obj 201 | 202 | class LengthValueAdapter(Adapter): 203 | """ 204 | Adapter for length-value pairs. It extracts only the value from the 205 | pair, and calculates the length based on the value. 206 | See PrefixedArray and PascalString. 207 | 208 | Parameters: 209 | * subcon - the subcon returning a length-value pair 210 | """ 211 | __slots__ = [] 212 | def _encode(self, obj, context): 213 | return (len(obj), obj) 214 | def _decode(self, obj, context): 215 | return obj[1] 216 | 217 | class CStringAdapter(StringAdapter): 218 | r""" 219 | Adapter for C-style strings (strings terminated by a terminator char). 220 | 221 | Parameters: 222 | * subcon - the subcon to convert 223 | * terminators - a sequence of terminator chars. default is "\x00". 224 | * encoding - the character encoding to use (e.g., "utf8"), or None to 225 | return raw-bytes. the terminator characters are not affected by the 226 | encoding. 227 | """ 228 | __slots__ = ["terminators"] 229 | def __init__(self, subcon, terminators = "\x00", encoding = None): 230 | StringAdapter.__init__(self, subcon, encoding = encoding) 231 | self.terminators = terminators 232 | def _encode(self, obj, context): 233 | return StringAdapter._encode(self, obj, context) + self.terminators[0] 234 | def _decode(self, obj, context): 235 | return StringAdapter._decode(self, obj[:-1], context) 236 | 237 | class TunnelAdapter(Adapter): 238 | """ 239 | Adapter for tunneling (as in protocol tunneling). A tunnel is construct 240 | nested upon another (layering). For parsing, the lower layer first parses 241 | the data (note: it must return a string!), then the upper layer is called 242 | to parse that data (bottom-up). For building it works in a top-down manner; 243 | first the upper layer builds the data, then the lower layer takes it and 244 | writes it to the stream. 245 | 246 | Parameters: 247 | * subcon - the lower layer subcon 248 | * inner_subcon - the upper layer (tunneled/nested) subcon 249 | 250 | Example: 251 | # a pascal string containing compressed data (zlib encoding), so first 252 | # the string is read, decompressed, and finally re-parsed as an array 253 | # of UBInt16 254 | TunnelAdapter( 255 | PascalString("data", encoding = "zlib"), 256 | GreedyRange(UBInt16("elements")) 257 | ) 258 | """ 259 | __slots__ = ["inner_subcon"] 260 | def __init__(self, subcon, inner_subcon): 261 | Adapter.__init__(self, subcon) 262 | self.inner_subcon = inner_subcon 263 | def _decode(self, obj, context): 264 | return self.inner_subcon._parse(StringIO(obj), context) 265 | def _encode(self, obj, context): 266 | stream = StringIO() 267 | self.inner_subcon._build(obj, stream, context) 268 | return stream.getvalue() 269 | 270 | class ExprAdapter(Adapter): 271 | """ 272 | A generic adapter that accepts 'encoder' and 'decoder' as parameters. You 273 | can use ExprAdapter instead of writing a full-blown class when only a 274 | simple expression is needed. 275 | 276 | Parameters: 277 | * subcon - the subcon to adapt 278 | * encoder - a function that takes (obj, context) and returns an encoded 279 | version of obj 280 | * decoder - a function that takes (obj, context) and returns an decoded 281 | version of obj 282 | 283 | Example: 284 | ExprAdapter(UBInt8("foo"), 285 | encoder = lambda obj, ctx: obj / 4, 286 | decoder = lambda obj, ctx: obj * 4, 287 | ) 288 | """ 289 | __slots__ = ["_encode", "_decode"] 290 | def __init__(self, subcon, encoder, decoder): 291 | Adapter.__init__(self, subcon) 292 | self._encode = encoder 293 | self._decode = decoder 294 | 295 | class HexDumpAdapter(Adapter): 296 | """ 297 | Adapter for hex-dumping strings. It returns a HexString, which is a string 298 | """ 299 | __slots__ = ["linesize"] 300 | def __init__(self, subcon, linesize = 16): 301 | Adapter.__init__(self, subcon) 302 | self.linesize = linesize 303 | def _encode(self, obj, context): 304 | return obj 305 | def _decode(self, obj, context): 306 | return HexString(obj, linesize = self.linesize) 307 | 308 | class ConstAdapter(Adapter): 309 | """ 310 | Adapter for enforcing a constant value ("magic numbers"). When decoding, 311 | the return value is checked; when building, the value is substituted in. 312 | 313 | Parameters: 314 | * subcon - the subcon to validate 315 | * value - the expected value 316 | 317 | Example: 318 | Const(Field("signature", 2), "MZ") 319 | """ 320 | __slots__ = ["value"] 321 | def __init__(self, subcon, value): 322 | Adapter.__init__(self, subcon) 323 | self.value = value 324 | def _encode(self, obj, context): 325 | if obj is None or obj == self.value: 326 | return self.value 327 | else: 328 | raise ConstError("expected %r, found %r" % (self.value, obj)) 329 | def _decode(self, obj, context): 330 | if obj != self.value: 331 | raise ConstError("expected %r, found %r" % (self.value, obj)) 332 | return obj 333 | 334 | class SlicingAdapter(Adapter): 335 | """ 336 | Adapter for slicing a list (getting a slice from that list) 337 | 338 | Parameters: 339 | * subcon - the subcon to slice 340 | * start - start index 341 | * stop - stop index (or None for up-to-end) 342 | * step - step (or None for every element) 343 | """ 344 | __slots__ = ["start", "stop", "step"] 345 | def __init__(self, subcon, start, stop = None): 346 | Adapter.__init__(self, subcon) 347 | self.start = start 348 | self.stop = stop 349 | def _encode(self, obj, context): 350 | if self.start is None: 351 | return obj 352 | return [None] * self.start + obj 353 | def _decode(self, obj, context): 354 | return obj[self.start:self.stop] 355 | 356 | class IndexingAdapter(Adapter): 357 | """ 358 | Adapter for indexing a list (getting a single item from that list) 359 | 360 | Parameters: 361 | * subcon - the subcon to index 362 | * index - the index of the list to get 363 | """ 364 | __slots__ = ["index"] 365 | def __init__(self, subcon, index): 366 | Adapter.__init__(self, subcon) 367 | if type(index) is not int: 368 | raise TypeError("index must be an integer", type(index)) 369 | self.index = index 370 | def _encode(self, obj, context): 371 | return [None] * self.index + [obj] 372 | def _decode(self, obj, context): 373 | return obj[self.index] 374 | 375 | class PaddingAdapter(Adapter): 376 | r""" 377 | Adapter for padding. 378 | 379 | Parameters: 380 | * subcon - the subcon to pad 381 | * pattern - the padding pattern (character). default is "\x00") 382 | * strict - whether or not to verify, during parsing, that the given 383 | padding matches the padding pattern. default is False (unstrict) 384 | """ 385 | __slots__ = ["pattern", "strict"] 386 | def __init__(self, subcon, pattern = "\x00", strict = False): 387 | Adapter.__init__(self, subcon) 388 | self.pattern = pattern 389 | self.strict = strict 390 | def _encode(self, obj, context): 391 | return self._sizeof(context) * self.pattern 392 | def _decode(self, obj, context): 393 | if self.strict: 394 | expected = self._sizeof(context) * self.pattern 395 | if obj != expected: 396 | raise PaddingError("expected %r, found %r" % (expected, obj)) 397 | return obj 398 | 399 | 400 | #=============================================================================== 401 | # validators 402 | #=============================================================================== 403 | class Validator(Adapter): 404 | """ 405 | Abstract class: validates a condition on the encoded/decoded object. 406 | Override _validate(obj, context) in deriving classes. 407 | 408 | Parameters: 409 | * subcon - the subcon to validate 410 | """ 411 | __slots__ = [] 412 | def _decode(self, obj, context): 413 | if not self._validate(obj, context): 414 | raise ValidationError("invalid object", obj) 415 | return obj 416 | def _encode(self, obj, context): 417 | return self._decode(obj, context) 418 | def _validate(self, obj, context): 419 | raise NotImplementedError() 420 | 421 | class OneOf(Validator): 422 | """ 423 | Validates that the value is one of the listed values 424 | 425 | Parameters: 426 | * subcon - the subcon to validate 427 | * valids - a set of valid values 428 | """ 429 | __slots__ = ["valids"] 430 | def __init__(self, subcon, valids): 431 | Validator.__init__(self, subcon) 432 | self.valids = valids 433 | def _validate(self, obj, context): 434 | return obj in self.valids 435 | 436 | class NoneOf(Validator): 437 | """ 438 | Validates that the value is none of the listed values 439 | 440 | Parameters: 441 | * subcon - the subcon to validate 442 | * invalids - a set of invalid values 443 | """ 444 | __slots__ = ["invalids"] 445 | def __init__(self, subcon, invalids): 446 | Validator.__init__(self, subcon) 447 | self.invalids = invalids 448 | def _validate(self, obj, context): 449 | return obj not in self.invalids 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | -------------------------------------------------------------------------------- /pdbparse/construct/debug.py: -------------------------------------------------------------------------------- 1 | """ 2 | Debugging utilities for constructs 3 | """ 4 | import sys 5 | import traceback 6 | import pdb 7 | import inspect 8 | from core import Construct, Subconstruct 9 | from lib import HexString, Container, ListContainer, AttrDict 10 | 11 | 12 | class Probe(Construct): 13 | """ 14 | A probe: dumps the context, stack frames, and stream content to the screen 15 | to aid the debugging process. 16 | See also Debugger. 17 | 18 | Parameters: 19 | * name - the display name 20 | * show_stream - whether or not to show stream contents. default is True. 21 | the stream must be seekable. 22 | * show_context - whether or not to show the context. default is True. 23 | * show_stack - whether or not to show the upper stack frames. default 24 | is True. 25 | * stream_lookahead - the number of bytes to dump when show_stack is set. 26 | default is 100. 27 | 28 | Example: 29 | Struct("foo", 30 | UBInt8("a"), 31 | Probe("between a and b"), 32 | UBInt8("b"), 33 | ) 34 | """ 35 | __slots__ = [ 36 | "printname", "show_stream", "show_context", "show_stack", 37 | "stream_lookahead" 38 | ] 39 | counter = 0 40 | 41 | def __init__(self, name = None, show_stream = True, 42 | show_context = True, show_stack = True, 43 | stream_lookahead = 100): 44 | Construct.__init__(self, None) 45 | if name is None: 46 | Probe.counter += 1 47 | name = "" % (Probe.counter,) 48 | self.printname = name 49 | self.show_stream = show_stream 50 | self.show_context = show_context 51 | self.show_stack = show_stack 52 | self.stream_lookahead = stream_lookahead 53 | def __repr__(self): 54 | return "%s(%r)" % (self.__class__.__name__, self.printname) 55 | def _parse(self, stream, context): 56 | self.printout(stream, context) 57 | def _build(self, obj, stream, context): 58 | self.printout(stream, context) 59 | def _sizeof(self, context): 60 | return 0 61 | 62 | def printout(self, stream, context): 63 | obj = Container() 64 | if self.show_stream: 65 | obj.stream_position = stream.tell() 66 | follows = stream.read(self.stream_lookahead) 67 | if not follows: 68 | obj.following_stream_data = "EOF reached" 69 | else: 70 | stream.seek(-len(follows), 1) 71 | obj.following_stream_data = HexString(follows) 72 | print 73 | 74 | if self.show_context: 75 | obj.context = context 76 | 77 | if self.show_stack: 78 | obj.stack = ListContainer() 79 | frames = [s[0] for s in inspect.stack()][1:-1] 80 | frames.reverse() 81 | for f in frames: 82 | a = AttrDict() 83 | a.__update__(f.f_locals) 84 | obj.stack.append(a) 85 | 86 | print "=" * 80 87 | print "Probe", self.printname 88 | print obj 89 | print "=" * 80 90 | 91 | class Debugger(Subconstruct): 92 | """ 93 | A pdb-based debugger. When an exception occurs in the subcon, a debugger 94 | will appear and allow you to debug the error (and even fix on-the-fly). 95 | 96 | Parameters: 97 | * subcon - the subcon to debug 98 | 99 | Example: 100 | Debugger( 101 | Enum(UBInt8("foo"), 102 | a = 1, 103 | b = 2, 104 | c = 3 105 | ) 106 | ) 107 | """ 108 | __slots__ = ["retval"] 109 | def _parse(self, stream, context): 110 | try: 111 | return self.subcon._parse(stream, context) 112 | except Exception: 113 | self.retval = NotImplemented 114 | self.handle_exc("(you can set the value of 'self.retval', " 115 | "which will be returned)") 116 | if self.retval is NotImplemented: 117 | raise 118 | else: 119 | return self.retval 120 | def _build(self, obj, stream, context): 121 | try: 122 | self.subcon._build(obj, stream, context) 123 | except Exception: 124 | self.handle_exc() 125 | def handle_exc(self, msg = None): 126 | print "=" * 80 127 | print "Debugging exception of %s:" % (self.subcon,) 128 | print "".join(traceback.format_exception(*sys.exc_info())[1:]) 129 | if msg: 130 | print msg 131 | pdb.post_mortem(sys.exc_info()[2]) 132 | print "=" * 80 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ax330d/ida_pdb_loader/051b6806810d8aaa40f973442b06c3c0e4c24131/pdbparse/construct/formats/__init__.py -------------------------------------------------------------------------------- /pdbparse/construct/formats/archive/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | archive file formats (tar, zip, 7zip, rar, ace, ...) 3 | """ -------------------------------------------------------------------------------- /pdbparse/construct/formats/archive/pkzip.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ax330d/ida_pdb_loader/051b6806810d8aaa40f973442b06c3c0e4c24131/pdbparse/construct/formats/archive/pkzip.py -------------------------------------------------------------------------------- /pdbparse/construct/formats/archive/rar.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ax330d/ida_pdb_loader/051b6806810d8aaa40f973442b06c3c0e4c24131/pdbparse/construct/formats/archive/rar.py -------------------------------------------------------------------------------- /pdbparse/construct/formats/audio/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | audio file formats (wav, mp3, ogg, midi, ...) 3 | """ 4 | 5 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/audio/mp3.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ax330d/ida_pdb_loader/051b6806810d8aaa40f973442b06c3c0e4c24131/pdbparse/construct/formats/audio/mp3.py -------------------------------------------------------------------------------- /pdbparse/construct/formats/audio/ogg.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ax330d/ida_pdb_loader/051b6806810d8aaa40f973442b06c3c0e4c24131/pdbparse/construct/formats/audio/ogg.py -------------------------------------------------------------------------------- /pdbparse/construct/formats/audio/wav.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ax330d/ida_pdb_loader/051b6806810d8aaa40f973442b06c3c0e4c24131/pdbparse/construct/formats/audio/wav.py -------------------------------------------------------------------------------- /pdbparse/construct/formats/data/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | all sorts of raw data serialization (tcpdump capture files, etc.) 3 | """ 4 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/data/cap.py: -------------------------------------------------------------------------------- 1 | """ 2 | tcpdump capture file 3 | """ 4 | from construct import * 5 | import time 6 | from datetime import datetime 7 | 8 | 9 | class MicrosecAdapter(Adapter): 10 | def _decode(self, obj, context): 11 | return datetime.fromtimestamp(obj[0] + (obj[1] / 1000000.0)) 12 | def _encode(self, obj, context): 13 | offset = time.mktime(*obj.timetuple()) 14 | sec = int(offset) 15 | usec = (offset - sec) * 1000000 16 | return (sec, usec) 17 | 18 | packet = Struct("packet", 19 | MicrosecAdapter( 20 | Sequence("time", 21 | ULInt32("time"), 22 | ULInt32("usec"), 23 | ) 24 | ), 25 | ULInt32("length"), 26 | Padding(4), 27 | HexDumpAdapter(Field("data", lambda ctx: ctx.length)), 28 | ) 29 | 30 | cap_file = Struct("cap_file", 31 | Padding(24), 32 | Rename("packets", OptionalGreedyRange(packet)), 33 | ) 34 | 35 | 36 | if __name__ == "__main__": 37 | obj = cap_file.parse_stream(open("../../test/cap2.cap", "rb")) 38 | print len(obj.packets) 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/document/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | document file formats (doc, wri, odf, xls, ...) 3 | """ -------------------------------------------------------------------------------- /pdbparse/construct/formats/document/doc.py: -------------------------------------------------------------------------------- 1 | """ 2 | Microsoft Word Document 3 | """ 4 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/document/pdf.py: -------------------------------------------------------------------------------- 1 | """ 2 | Abode's Portable Document Format (pdf) 3 | """ 4 | 5 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/document/postscript.py: -------------------------------------------------------------------------------- 1 | """ 2 | Postscript document 3 | """ 4 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/executable/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ax330d/ida_pdb_loader/051b6806810d8aaa40f973442b06c3c0e4c24131/pdbparse/construct/formats/executable/__init__.py -------------------------------------------------------------------------------- /pdbparse/construct/formats/executable/elf32.py: -------------------------------------------------------------------------------- 1 | """ 2 | Executable and Linkable Format (ELF), 32 bit, little endian 3 | Used on *nix systems as a replacement of the older a.out format 4 | """ 5 | from construct import * 6 | 7 | 8 | elf32_program_header = Struct("program_header", 9 | Enum(ULInt32("type"), 10 | NULL = 0, 11 | LOAD = 1, 12 | DYNAMIC = 2, 13 | INTERP = 3, 14 | NOTE = 4, 15 | SHLIB = 5, 16 | PHDR = 6, 17 | _default_ = Pass, 18 | ), 19 | ULInt32("offset"), 20 | ULInt32("vaddr"), 21 | ULInt32("paddr"), 22 | ULInt32("file_size"), 23 | ULInt32("mem_size"), 24 | ULInt32("flags"), 25 | ULInt32("align"), 26 | ) 27 | 28 | elf32_section_header = Struct("section_header", 29 | ULInt32("name_offset"), 30 | Pointer(lambda ctx: ctx._.strtab_data_offset + ctx.name_offset, 31 | CString("name") 32 | ), 33 | Enum(ULInt32("type"), 34 | NULL = 0, 35 | PROGBITS = 1, 36 | SYMTAB = 2, 37 | STRTAB = 3, 38 | RELA = 4, 39 | HASH = 5, 40 | DYNAMIC = 6, 41 | NOTE = 7, 42 | NOBITS = 8, 43 | REL = 9, 44 | SHLIB = 10, 45 | DYNSYM = 11, 46 | _default_ = Pass, 47 | ), 48 | ULInt32("flags"), 49 | ULInt32("addr"), 50 | ULInt32("offset"), 51 | ULInt32("size"), 52 | ULInt32("link"), 53 | ULInt32("info"), 54 | ULInt32("align"), 55 | ULInt32("entry_size"), 56 | OnDemandPointer(lambda ctx: ctx.offset, 57 | HexDumpAdapter(Field("data", lambda ctx: ctx.size)) 58 | ), 59 | ) 60 | 61 | elf32_file = Struct("elf32_file", 62 | Struct("identifier", 63 | Const(Bytes("magic", 4), "\x7fELF"), 64 | Enum(Byte("file_class"), 65 | NONE = 0, 66 | CLASS32 = 1, 67 | CLASS64 = 2, 68 | ), 69 | Enum(Byte("encoding"), 70 | NONE = 0, 71 | LSB = 1, 72 | MSB = 2, 73 | ), 74 | Byte("version"), 75 | Padding(9), 76 | ), 77 | Enum(ULInt16("type"), 78 | NONE = 0, 79 | RELOCATABLE = 1, 80 | EXECUTABLE = 2, 81 | SHARED = 3, 82 | CORE = 4, 83 | ), 84 | Enum(ULInt16("machine"), 85 | NONE = 0, 86 | M32 = 1, 87 | SPARC = 2, 88 | I386 = 3, 89 | Motorolla68K = 4, 90 | Motorolla88K = 5, 91 | Intel860 = 7, 92 | MIPS = 8, 93 | ), 94 | ULInt32("version"), 95 | ULInt32("entry"), 96 | ULInt32("ph_offset"), 97 | ULInt32("sh_offset"), 98 | ULInt32("flags"), 99 | ULInt16("header_size"), 100 | ULInt16("ph_entry_size"), 101 | ULInt16("ph_count"), 102 | ULInt16("sh_entry_size"), 103 | ULInt16("sh_count"), 104 | ULInt16("strtab_section_index"), 105 | 106 | # calculate the string table data offset (pointer arithmetics) 107 | # ugh... anyway, we need it in order to read the section names, later on 108 | Pointer(lambda ctx: 109 | ctx.sh_offset + ctx.strtab_section_index * ctx.sh_entry_size + 16, 110 | ULInt32("strtab_data_offset"), 111 | ), 112 | 113 | # program header table 114 | Rename("program_table", 115 | Pointer(lambda ctx: ctx.ph_offset, 116 | Array(lambda ctx: ctx.ph_count, 117 | elf32_program_header 118 | ) 119 | ) 120 | ), 121 | 122 | # section table 123 | Rename("sections", 124 | Pointer(lambda ctx: ctx.sh_offset, 125 | Array(lambda ctx: ctx.sh_count, 126 | elf32_section_header 127 | ) 128 | ) 129 | ), 130 | ) 131 | 132 | 133 | if __name__ == "__main__": 134 | obj = elf32_file.parse_stream(open("../../test/_ctypes_test.so", "rb")) 135 | [s.data.value for s in obj.sections] 136 | print obj 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/executable/pe32.py: -------------------------------------------------------------------------------- 1 | """ 2 | Portable Executable (PE) 32 bit, little endian 3 | Used on MSWindows systems (including DOS) for EXEs and DLLs 4 | 5 | 1999 paper: 6 | http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/pecoff.doc 7 | 8 | 2006 with updates relevant for .NET: 9 | http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/pecoff_v8.doc 10 | """ 11 | from construct import * 12 | import time 13 | 14 | 15 | class UTCTimeStampAdapter(Adapter): 16 | def _decode(self, obj, context): 17 | return time.ctime(obj) 18 | def _encode(self, obj, context): 19 | return int(time.mktime(time.strptime(obj))) 20 | 21 | def UTCTimeStamp(name): 22 | return UTCTimeStampAdapter(ULInt32(name)) 23 | 24 | class NamedSequence(Adapter): 25 | __slots__ = ["mapping", "rev_mapping"] 26 | prefix = "unnamed_" 27 | def __init__(self, subcon, mapping): 28 | Adapter.__init__(self, subcon) 29 | self.mapping = mapping 30 | self.rev_mapping = dict((v, k) for k, v in mapping.iteritems()) 31 | def _encode(self, obj, context): 32 | d = obj.__dict__ 33 | obj2 = [None] * len(d) 34 | for name, value in d.iteritems(): 35 | if name in self.rev_mapping: 36 | index = self.rev_mapping[name] 37 | elif name.startswith("__"): 38 | obj2.pop(-1) 39 | continue 40 | elif name.startswith(self.prefix): 41 | index = int(name.split(self.prefix)[1]) 42 | else: 43 | raise ValueError("no mapping defined for %r" % (name,)) 44 | obj2[index] = value 45 | return obj2 46 | def _decode(self, obj, context): 47 | obj2 = Container() 48 | for i, item in enumerate(obj): 49 | if i in self.mapping: 50 | name = self.mapping[i] 51 | else: 52 | name = "%s%d" % (self.prefix, i) 53 | setattr(obj2, name, item) 54 | return obj2 55 | 56 | msdos_header = Struct("msdos_header", 57 | Const(Bytes("magic", 2), "MZ"), 58 | ULInt16("partPag"), 59 | ULInt16("page_count"), 60 | ULInt16("relocation_count"), 61 | ULInt16("header_size"), 62 | ULInt16("minmem"), 63 | ULInt16("maxmem"), 64 | ULInt16("relocation_stackseg"), 65 | ULInt16("exe_stackptr"), 66 | ULInt16("checksum"), 67 | ULInt16("exe_ip"), 68 | ULInt16("relocation_codeseg"), 69 | ULInt16("table_offset"), 70 | ULInt16("overlay"), 71 | Padding(8), 72 | ULInt16("oem_id"), 73 | ULInt16("oem_info"), 74 | Padding(20), 75 | ULInt32("coff_header_pointer"), 76 | Anchor("_assembly_start"), 77 | OnDemand( 78 | HexDumpAdapter( 79 | Field("code", 80 | lambda ctx: ctx.coff_header_pointer - ctx._assembly_start 81 | ) 82 | ) 83 | ), 84 | ) 85 | 86 | symbol_table = Struct("symbol_table", 87 | String("name", 8, padchar = "\x00"), 88 | ULInt32("value"), 89 | Enum(ExprAdapter(SLInt16("section_number"), 90 | encoder = lambda obj, ctx: obj + 1, 91 | decoder = lambda obj, ctx: obj - 1, 92 | ), 93 | UNDEFINED = -1, 94 | ABSOLUTE = -2, 95 | DEBUG = -3, 96 | _default_ = Pass, 97 | ), 98 | Enum(ULInt8("complex_type"), 99 | NULL = 0, 100 | POINTER = 1, 101 | FUNCTION = 2, 102 | ARRAY = 3, 103 | ), 104 | Enum(ULInt8("base_type"), 105 | NULL = 0, 106 | VOID = 1, 107 | CHAR = 2, 108 | SHORT = 3, 109 | INT = 4, 110 | LONG = 5, 111 | FLOAT = 6, 112 | DOUBLE = 7, 113 | STRUCT = 8, 114 | UNION = 9, 115 | ENUM = 10, 116 | MOE = 11, 117 | BYTE = 12, 118 | WORD = 13, 119 | UINT = 14, 120 | DWORD = 15, 121 | ), 122 | Enum(ULInt8("storage_class"), 123 | END_OF_FUNCTION = 255, 124 | NULL = 0, 125 | AUTOMATIC = 1, 126 | EXTERNAL = 2, 127 | STATIC = 3, 128 | REGISTER = 4, 129 | EXTERNAL_DEF = 5, 130 | LABEL = 6, 131 | UNDEFINED_LABEL = 7, 132 | MEMBER_OF_STRUCT = 8, 133 | ARGUMENT = 9, 134 | STRUCT_TAG = 10, 135 | MEMBER_OF_UNION = 11, 136 | UNION_TAG = 12, 137 | TYPE_DEFINITION = 13, 138 | UNDEFINED_STATIC = 14, 139 | ENUM_TAG = 15, 140 | MEMBER_OF_ENUM = 16, 141 | REGISTER_PARAM = 17, 142 | BIT_FIELD = 18, 143 | BLOCK = 100, 144 | FUNCTION = 101, 145 | END_OF_STRUCT = 102, 146 | FILE = 103, 147 | SECTION = 104, 148 | WEAK_EXTERNAL = 105, 149 | ), 150 | ULInt8("number_of_aux_symbols"), 151 | Array(lambda ctx: ctx.number_of_aux_symbols, 152 | Bytes("aux_symbols", 18) 153 | ) 154 | ) 155 | 156 | coff_header = Struct("coff_header", 157 | Const(Bytes("magic", 4), "PE\x00\x00"), 158 | Enum(ULInt16("machine_type"), 159 | UNKNOWN = 0x0, 160 | AM33 = 0x1d3, 161 | AMD64 = 0x8664, 162 | ARM = 0x1c0, 163 | EBC = 0xebc, 164 | I386 = 0x14c, 165 | IA64 = 0x200, 166 | M32R = 0x9041, 167 | MIPS16 = 0x266, 168 | MIPSFPU = 0x366, 169 | MIPSFPU16 = 0x466, 170 | POWERPC = 0x1f0, 171 | POWERPCFP = 0x1f1, 172 | R4000 = 0x166, 173 | SH3 = 0x1a2, 174 | SH3DSP = 0x1a3, 175 | SH4 = 0x1a6, 176 | SH5= 0x1a8, 177 | THUMB = 0x1c2, 178 | WCEMIPSV2 = 0x169, 179 | _default_ = Pass 180 | ), 181 | ULInt16("number_of_sections"), 182 | UTCTimeStamp("time_stamp"), 183 | ULInt32("symbol_table_pointer"), 184 | ULInt32("number_of_symbols"), 185 | ULInt16("optional_header_size"), 186 | FlagsEnum(ULInt16("characteristics"), 187 | RELOCS_STRIPPED = 0x0001, 188 | EXECUTABLE_IMAGE = 0x0002, 189 | LINE_NUMS_STRIPPED = 0x0004, 190 | LOCAL_SYMS_STRIPPED = 0x0008, 191 | AGGRESSIVE_WS_TRIM = 0x0010, 192 | LARGE_ADDRESS_AWARE = 0x0020, 193 | MACHINE_16BIT = 0x0040, 194 | BYTES_REVERSED_LO = 0x0080, 195 | MACHINE_32BIT = 0x0100, 196 | DEBUG_STRIPPED = 0x0200, 197 | REMOVABLE_RUN_FROM_SWAP = 0x0400, 198 | SYSTEM = 0x1000, 199 | DLL = 0x2000, 200 | UNIPROCESSOR_ONLY = 0x4000, 201 | BIG_ENDIAN_MACHINE = 0x8000, 202 | ), 203 | 204 | # symbol table 205 | Pointer(lambda ctx: ctx.symbol_table_pointer, 206 | Array(lambda ctx: ctx.number_of_symbols, symbol_table) 207 | ) 208 | ) 209 | 210 | def PEPlusField(name): 211 | return IfThenElse(name, lambda ctx: ctx.pe_type == "PE32Plus", 212 | ULInt64(None), 213 | ULInt32(None), 214 | ) 215 | 216 | optional_header = Struct("optional_header", 217 | # standard fields 218 | Enum(ULInt16("pe_type"), 219 | PE32 = 0x10b, 220 | PE32plus = 0x20b, 221 | ), 222 | ULInt8("major_linker_version"), 223 | ULInt8("minor_linker_version"), 224 | ULInt32("code_size"), 225 | ULInt32("initialized_data_size"), 226 | ULInt32("uninitialized_data_size"), 227 | ULInt32("entry_point_pointer"), 228 | ULInt32("base_of_code"), 229 | If(lambda ctx: ctx.pe_type == "PE32", 230 | # only in PE32 files 231 | ULInt32("base_of_data") 232 | ), 233 | 234 | # WinNT-specific fields 235 | PEPlusField("image_base"), 236 | ULInt32("section_aligment"), 237 | ULInt32("file_alignment"), 238 | ULInt16("major_os_version"), 239 | ULInt16("minor_os_version"), 240 | ULInt16("major_image_version"), 241 | ULInt16("minor_image_version"), 242 | ULInt16("major_subsystem_version"), 243 | ULInt16("minor_subsystem_version"), 244 | Padding(4), 245 | ULInt32("image_size"), 246 | ULInt32("headers_size"), 247 | ULInt32("checksum"), 248 | Enum(ULInt16("subsystem"), 249 | UNKNOWN = 0, 250 | NATIVE = 1, 251 | WINDOWS_GUI = 2, 252 | WINDOWS_CUI = 3, 253 | POSIX_CIU = 7, 254 | WINDOWS_CE_GUI = 9, 255 | EFI_APPLICATION = 10, 256 | EFI_BOOT_SERVICE_DRIVER = 11, 257 | EFI_RUNTIME_DRIVER = 12, 258 | EFI_ROM = 13, 259 | XBOX = 14, 260 | _defualt_ = Pass 261 | ), 262 | FlagsEnum(ULInt16("dll_characteristics"), 263 | NO_BIND = 0x0800, 264 | WDM_DRIVER = 0x2000, 265 | TERMINAL_SERVER_AWARE = 0x8000, 266 | ), 267 | PEPlusField("reserved_stack_size"), 268 | PEPlusField("stack_commit_size"), 269 | PEPlusField("reserved_heap_size"), 270 | PEPlusField("heap_commit_size"), 271 | ULInt32("loader_flags"), 272 | ULInt32("number_of_data_directories"), 273 | 274 | NamedSequence( 275 | Array(lambda ctx: ctx.number_of_data_directories, 276 | Struct("data_directories", 277 | ULInt32("address"), 278 | ULInt32("size"), 279 | ) 280 | ), 281 | mapping = { 282 | 0 : 'export_table', 283 | 1 : 'import_table', 284 | 2 : 'resource_table', 285 | 3 : 'exception_table', 286 | 4 : 'certificate_table', 287 | 5 : 'base_relocation_table', 288 | 6 : 'debug', 289 | 7 : 'architecture', 290 | 8 : 'global_ptr', 291 | 9 : 'tls_table', 292 | 10 : 'load_config_table', 293 | 11 : 'bound_import', 294 | 12 : 'import_address_table', 295 | 13 : 'delay_import_descriptor', 296 | 14 : 'complus_runtime_header', 297 | } 298 | ), 299 | ) 300 | 301 | section = Struct("section", 302 | String("name", 8, padchar = "\x00"), 303 | ULInt32("virtual_size"), 304 | ULInt32("virtual_address"), 305 | ULInt32("raw_data_size"), 306 | ULInt32("raw_data_pointer"), 307 | ULInt32("relocations_pointer"), 308 | ULInt32("line_numbers_pointer"), 309 | ULInt16("number_of_relocations"), 310 | ULInt16("number_of_line_numbers"), 311 | FlagsEnum(ULInt32("characteristics"), 312 | TYPE_REG = 0x00000000, 313 | TYPE_DSECT = 0x00000001, 314 | TYPE_NOLOAD = 0x00000002, 315 | TYPE_GROUP = 0x00000004, 316 | TYPE_NO_PAD = 0x00000008, 317 | TYPE_COPY = 0x00000010, 318 | CNT_CODE = 0x00000020, 319 | CNT_INITIALIZED_DATA = 0x00000040, 320 | CNT_UNINITIALIZED_DATA = 0x00000080, 321 | LNK_OTHER = 0x00000100, 322 | LNK_INFO = 0x00000200, 323 | TYPE_OVER = 0x00000400, 324 | LNK_REMOVE = 0x00000800, 325 | LNK_COMDAT = 0x00001000, 326 | MEM_FARDATA = 0x00008000, 327 | MEM_PURGEABLE = 0x00020000, 328 | MEM_16BIT = 0x00020000, 329 | MEM_LOCKED = 0x00040000, 330 | MEM_PRELOAD = 0x00080000, 331 | ALIGN_1BYTES = 0x00100000, 332 | ALIGN_2BYTES = 0x00200000, 333 | ALIGN_4BYTES = 0x00300000, 334 | ALIGN_8BYTES = 0x00400000, 335 | ALIGN_16BYTES = 0x00500000, 336 | ALIGN_32BYTES = 0x00600000, 337 | ALIGN_64BYTES = 0x00700000, 338 | ALIGN_128BYTES = 0x00800000, 339 | ALIGN_256BYTES = 0x00900000, 340 | ALIGN_512BYTES = 0x00A00000, 341 | ALIGN_1024BYTES = 0x00B00000, 342 | ALIGN_2048BYTES = 0x00C00000, 343 | ALIGN_4096BYTES = 0x00D00000, 344 | ALIGN_8192BYTES = 0x00E00000, 345 | LNK_NRELOC_OVFL = 0x01000000, 346 | MEM_DISCARDABLE = 0x02000000, 347 | MEM_NOT_CACHED = 0x04000000, 348 | MEM_NOT_PAGED = 0x08000000, 349 | MEM_SHARED = 0x10000000, 350 | MEM_EXECUTE = 0x20000000, 351 | MEM_READ = 0x40000000, 352 | MEM_WRITE = 0x80000000, 353 | ), 354 | 355 | OnDemandPointer(lambda ctx: ctx.raw_data_pointer, 356 | HexDumpAdapter(Field("raw_data", lambda ctx: ctx.raw_data_size)) 357 | ), 358 | 359 | OnDemandPointer(lambda ctx: ctx.line_numbers_pointer, 360 | Array(lambda ctx: ctx.number_of_line_numbers, 361 | Struct("line_numbers", 362 | ULInt32("type"), 363 | ULInt16("line_number"), 364 | ) 365 | ) 366 | ), 367 | 368 | OnDemandPointer(lambda ctx: ctx.relocations_pointer, 369 | Array(lambda ctx: ctx.number_of_relocations, 370 | Struct("relocations", 371 | ULInt32("virtual_address"), 372 | ULInt32("symbol_table_index"), 373 | ULInt16("type"), 374 | ) 375 | ) 376 | ), 377 | ) 378 | 379 | pe32_file = Struct("pe32_file", 380 | # headers 381 | msdos_header, 382 | coff_header, 383 | Anchor("_start_of_optional_header"), 384 | optional_header, 385 | Anchor("_end_of_optional_header"), 386 | Padding(lambda ctx: min(0, 387 | ctx.coff_header.optional_header_size - 388 | ctx._end_of_optional_header + 389 | ctx._start_of_optional_header 390 | ) 391 | ), 392 | 393 | # sections 394 | Array(lambda ctx: ctx.coff_header.number_of_sections, section) 395 | ) 396 | 397 | 398 | if __name__ == "__main__": 399 | print pe32_file.parse_stream(open("../../test/notepad.exe", "rb")) 400 | print pe32_file.parse_stream(open("../../test/sqlite3.dll", "rb")) 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/filesystem/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | file systems on-disk formats (ext2, fat32, ntfs, ...) 3 | and related disk formats (mbr, ...) 4 | """ 5 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/filesystem/cdfs.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISO-9660 Compact Disk file system format 3 | """ 4 | 5 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/filesystem/ext2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Extension 2 (ext2) 3 | Used in Linux systems 4 | """ 5 | from construct import * 6 | 7 | 8 | Char = SLInt8 9 | UChar = ULInt8 10 | Short = SLInt16 11 | UShort = ULInt16 12 | Long = SLInt32 13 | ULong = ULInt32 14 | 15 | def BlockPointer(name): 16 | return Struct(name, 17 | ULong("block_number"), 18 | OnDemandPointer(lambda ctx: ctx["block_number"]), 19 | ) 20 | 21 | superblock = Struct("superblock", 22 | ULong('inodes_count'), 23 | ULong('blocks_count'), 24 | ULong('reserved_blocks_count'), 25 | ULong('free_blocks_count'), 26 | ULong('free_inodes_count'), 27 | ULong('first_data_block'), 28 | Enum(ULong('log_block_size'), 29 | OneKB = 0, 30 | TwoKB = 1, 31 | FourKB = 2, 32 | ), 33 | Long('log_frag_size'), 34 | ULong('blocks_per_group'), 35 | ULong('frags_per_group'), 36 | ULong('inodes_per_group'), 37 | ULong('mtime'), 38 | ULong('wtime'), 39 | UShort('mnt_count'), 40 | Short('max_mnt_count'), 41 | Const(UShort('magic'), 0xEF53), 42 | UShort('state'), 43 | UShort('errors'), 44 | Padding(2), 45 | ULong('lastcheck'), 46 | ULong('checkinterval'), 47 | ULong('creator_os'), 48 | ULong('rev_level'), 49 | Padding(235 * 4), 50 | ) 51 | 52 | group_descriptor = Struct("group_descriptor", 53 | ULong('block_bitmap'), 54 | ULong('inode_bitmap'), 55 | ULong('inode_table'), 56 | UShort('free_blocks_count'), 57 | UShort('free_inodes_count'), 58 | UShort('used_dirs_count'), 59 | Padding(14), 60 | ) 61 | 62 | inode = Struct("inode", 63 | FlagsEnum(UShort('mode'), 64 | IXOTH = 0x0001, 65 | IWOTH = 0x0002, 66 | IROTH = 0x0004, 67 | IRWXO = 0x0007, 68 | IXGRP = 0x0008, 69 | IWGRP = 0x0010, 70 | IRGRP = 0x0020, 71 | IRWXG = 0x0038, 72 | IXUSR = 0x0040, 73 | IWUSR = 0x0080, 74 | IRUSR = 0x0100, 75 | IRWXU = 0x01C0, 76 | ISVTX = 0x0200, 77 | ISGID = 0x0400, 78 | ISUID = 0x0800, 79 | IFIFO = 0x1000, 80 | IFCHR = 0x2000, 81 | IFDIR = 0x4000, 82 | IFBLK = 0x6000, 83 | IFREG = 0x8000, 84 | IFLNK = 0xC000, 85 | IFSOCK = 0xA000, 86 | IFMT = 0xF000, 87 | ), 88 | UShort('uid'), 89 | ULong('size'), 90 | ULong('atime'), 91 | ULong('ctime'), 92 | ULong('mtime'), 93 | ULong('dtime'), 94 | UShort('gid'), 95 | UShort('links_count'), 96 | ULong('blocks'), 97 | FlagsEnum(ULong('flags'), 98 | SecureDelete = 0x0001, 99 | AllowUndelete = 0x0002, 100 | Compressed = 0x0004, 101 | Synchronous = 0x0008, 102 | ), 103 | Padding(4), 104 | StrictRepeater(12, ULong('blocks')), 105 | ULong("indirect1_block"), 106 | ULong("indirect2_block"), 107 | ULong("indirect3_block"), 108 | ULong('version'), 109 | ULong('file_acl'), 110 | ULong('dir_acl'), 111 | ULong('faddr'), 112 | UChar('frag'), 113 | Byte('fsize'), 114 | Padding(10) , 115 | ) 116 | 117 | # special inodes 118 | EXT2_BAD_INO = 1 119 | EXT2_ROOT_INO = 2 120 | EXT2_ACL_IDX_INO = 3 121 | EXT2_ACL_DATA_INO = 4 122 | EXT2_BOOT_LOADER_INO = 5 123 | EXT2_UNDEL_DIR_INO = 6 124 | EXT2_FIRST_INO = 11 125 | 126 | directory_record = Struct("directory_entry", 127 | ULong("inode"), 128 | UShort("rec_length"), 129 | UShort("name_length"), 130 | Field("name", lambda ctx: ctx["name_length"]), 131 | Padding(lambda ctx: ctx["rec_length"] - ctx["name_length"]) 132 | ) 133 | 134 | 135 | print superblock.sizeof() 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/filesystem/ext3.py: -------------------------------------------------------------------------------- 1 | """ 2 | Extension 3 (ext3) 3 | Used primarily for concurrent Linux systems (ext2 + journalling) 4 | """ 5 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/filesystem/fat12.py: -------------------------------------------------------------------------------- 1 | """ 2 | File Allocation Table (FAT) / 12 bit version 3 | Used primarily for diskettes 4 | """ 5 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/filesystem/fat16.py: -------------------------------------------------------------------------------- 1 | """ 2 | MS-DOS FAT 16 3 | """ 4 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/filesystem/fat32.py: -------------------------------------------------------------------------------- 1 | """ 2 | File Allocation Table (FAT) / 32 bit version 3 | Used for USB flash disks, old MSWindows systems, etc. 4 | """ 5 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/filesystem/mbr.py: -------------------------------------------------------------------------------- 1 | """ 2 | Master Boot Record 3 | The first sector on disk, contains the partition table, bootloader, et al. 4 | 5 | http://www.win.tue.nl/~aeb/partitions/partition_types-1.html 6 | """ 7 | from construct import * 8 | 9 | 10 | mbr = Struct("mbr", 11 | HexDumpAdapter(Bytes("bootloader_code", 446)), 12 | Array(4, 13 | Struct("partitions", 14 | Enum(Byte("state"), 15 | INACTIVE = 0x00, 16 | ACTIVE = 0x80, 17 | ), 18 | BitStruct("beginning", 19 | Octet("head"), 20 | Bits("sect", 6), 21 | Bits("cyl", 10), 22 | ), 23 | Enum(UBInt8("type"), 24 | Nothing = 0x00, 25 | FAT12 = 0x01, 26 | XENIX_ROOT = 0x02, 27 | XENIX_USR = 0x03, 28 | FAT16_old = 0x04, 29 | Extended_DOS = 0x05, 30 | FAT16 = 0x06, 31 | FAT32 = 0x0b, 32 | FAT32_LBA = 0x0c, 33 | NTFS = 0x07, 34 | LINUX_SWAP = 0x82, 35 | LINUX_NATIVE = 0x83, 36 | _default_ = Pass, 37 | ), 38 | BitStruct("ending", 39 | Octet("head"), 40 | Bits("sect", 6), 41 | Bits("cyl", 10), 42 | ), 43 | UBInt32("sector_offset"), # offset from MBR in sectors 44 | UBInt32("size"), # in sectors 45 | ) 46 | ), 47 | Const(Bytes("signature", 2), "\x55\xAA"), 48 | ) 49 | 50 | 51 | 52 | if __name__ == "__main__": 53 | cap1 = ( 54 | "33C08ED0BC007CFB5007501FFCBE1B7CBF1B065057B9E501F3A4CBBDBE07B104386E00" 55 | "7C09751383C510E2F4CD188BF583C610497419382C74F6A0B507B4078BF0AC3C0074FC" 56 | "BB0700B40ECD10EBF2884E10E84600732AFE4610807E040B740B807E040C7405A0B607" 57 | "75D2804602068346080683560A00E821007305A0B607EBBC813EFE7D55AA740B807E10" 58 | "0074C8A0B707EBA98BFC1E578BF5CBBF05008A5600B408CD1372238AC1243F988ADE8A" 59 | "FC43F7E38BD186D6B106D2EE42F7E239560A77237205394608731CB80102BB007C8B4E" 60 | "028B5600CD1373514F744E32E48A5600CD13EBE48A560060BBAA55B441CD13723681FB" 61 | "55AA7530F6C101742B61606A006A00FF760AFF76086A0068007C6A016A10B4428BF4CD" 62 | "136161730E4F740B32E48A5600CD13EBD661F9C3496E76616C69642070617274697469" 63 | "6F6E207461626C65004572726F72206C6F6164696E67206F7065726174696E67207379" 64 | "7374656D004D697373696E67206F7065726174696E672073797374656D000000000000" 65 | "0000000000000000000000000000000000000000000000000000000000000000000000" 66 | "00000000000000000000000000000000002C4463B7BDB7BD00008001010007FEFFFF3F" 67 | "000000371671020000C1FF0FFEFFFF761671028A8FDF06000000000000000000000000" 68 | "000000000000000000000000000000000000000055AA" 69 | ).decode("hex") 70 | 71 | print mbr.parse(cap1) 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/filesystem/ntfs5.py: -------------------------------------------------------------------------------- 1 | """ 2 | NT File System (NTFS) version 5 3 | Used for MSWindows systems since Windows 2000 4 | """ 5 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/graphics/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | graphic file formats, including imagery (bmp, jpg, gif, png, ...), 3 | models (3ds, ...), etc. 4 | """ 5 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/graphics/bmp.py: -------------------------------------------------------------------------------- 1 | """ 2 | Windows/OS2 Bitmap (BMP) 3 | this could have been a perfect show-case file format, but they had to make 4 | it ugly (all sorts of alignment or 5 | """ 6 | from construct import * 7 | 8 | 9 | #=============================================================================== 10 | # pixels: uncompressed 11 | #=============================================================================== 12 | def UncompressedRows(subcon, align_to_byte = False): 13 | """argh! lines must be aligned to a 4-byte boundary, and bit-pixel 14 | lines must be aligned to full bytes...""" 15 | if align_to_byte: 16 | line_pixels = Bitwise( 17 | Aligned(Array(lambda ctx: ctx.width, subcon), modulus = 8) 18 | ) 19 | else: 20 | line_pixels = Array(lambda ctx: ctx.width, subcon) 21 | return Array(lambda ctx: ctx.height, 22 | Aligned(line_pixels, modulus = 4) 23 | ) 24 | 25 | uncompressed_pixels = Switch("uncompressed", lambda ctx: ctx.bpp, 26 | { 27 | 1 : UncompressedRows(Bit("index"), align_to_byte = True), 28 | 4 : UncompressedRows(Nibble("index"), align_to_byte = True), 29 | 8 : UncompressedRows(Byte("index")), 30 | 24 : UncompressedRows( 31 | Sequence("rgb", Byte("red"), Byte("green"), Byte("blue")) 32 | ), 33 | } 34 | ) 35 | 36 | #=============================================================================== 37 | # pixels: Run Length Encoding (RLE) 8 bit 38 | #=============================================================================== 39 | class RunLengthAdapter(Adapter): 40 | def _encode(self, obj): 41 | return len(obj), obj[0] 42 | def _decode(self, (length, value)): 43 | return [value] * length 44 | 45 | rle8pixel = RunLengthAdapter( 46 | Sequence("rle8pixel", 47 | Byte("length"), 48 | Byte("value") 49 | ) 50 | ) 51 | 52 | #=============================================================================== 53 | # file structure 54 | #=============================================================================== 55 | def iff(cond, thenval, elseval): 56 | if cond: 57 | return thenval 58 | else: 59 | return elseval 60 | 61 | bitmap_file = Struct("bitmap_file", 62 | # header 63 | Const(String("signature", 2), "BM"), 64 | ULInt32("file_size"), 65 | Padding(4), 66 | ULInt32("data_offset"), 67 | ULInt32("header_size"), 68 | Enum(Alias("version", "header_size"), 69 | v2 = 12, 70 | v3 = 40, 71 | v4 = 108, 72 | ), 73 | ULInt32("width"), 74 | ULInt32("height"), 75 | Value("number_of_pixels", lambda ctx: ctx.width * ctx.height), 76 | ULInt16("planes"), 77 | ULInt16("bpp"), # bits per pixel 78 | Enum(ULInt32("compression"), 79 | Uncompressed = 0, 80 | RLE8 = 1, 81 | RLE4 = 2, 82 | Bitfields = 3, 83 | JPEG = 4, 84 | PNG = 5, 85 | ), 86 | ULInt32("image_data_size"), # in bytes 87 | ULInt32("horizontal_dpi"), 88 | ULInt32("vertical_dpi"), 89 | ULInt32("colors_used"), 90 | ULInt32("important_colors"), 91 | 92 | # palette (24 bit has no palette) 93 | OnDemand( 94 | Array(lambda ctx: iff(ctx.bpp <= 8, 2 ** ctx.bpp, 0), 95 | Struct("palette", 96 | Byte("blue"), 97 | Byte("green"), 98 | Byte("red"), 99 | Padding(1), 100 | ) 101 | ) 102 | ), 103 | 104 | # pixels 105 | OnDemandPointer(lambda ctx: ctx.data_offset, 106 | Switch("pixels", lambda ctx: ctx.compression, 107 | { 108 | "Uncompressed" : uncompressed_pixels, 109 | } 110 | ), 111 | ), 112 | ) 113 | 114 | 115 | if __name__ == "__main__": 116 | obj = bitmap_file.parse_stream(open("../../test/bitmap8.bmp", "rb")) 117 | print obj 118 | print repr(obj.pixels.value) 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/graphics/emf.py: -------------------------------------------------------------------------------- 1 | """ 2 | Enhanced Meta File 3 | """ 4 | from construct import * 5 | 6 | 7 | record_type = Enum(ULInt32("record_type"), 8 | ABORTPATH = 68, 9 | ANGLEARC = 41, 10 | ARC = 45, 11 | ARCTO = 55, 12 | BEGINPATH = 59, 13 | BITBLT = 76, 14 | CHORD = 46, 15 | CLOSEFIGURE = 61, 16 | CREATEBRUSHINDIRECT = 39, 17 | CREATEDIBPATTERNBRUSHPT = 94, 18 | CREATEMONOBRUSH = 93, 19 | CREATEPALETTE = 49, 20 | CREATEPEN = 38, 21 | DELETEOBJECT = 40, 22 | ELLIPSE = 42, 23 | ENDPATH = 60, 24 | EOF = 14, 25 | EXCLUDECLIPRECT = 29, 26 | EXTCREATEFONTINDIRECTW = 82, 27 | EXTCREATEPEN = 95, 28 | EXTFLOODFILL = 53, 29 | EXTSELECTCLIPRGN = 75, 30 | EXTTEXTOUTA = 83, 31 | EXTTEXTOUTW = 84, 32 | FILLPATH = 62, 33 | FILLRGN = 71, 34 | FLATTENPATH = 65, 35 | FRAMERGN = 72, 36 | GDICOMMENT = 70, 37 | HEADER = 1, 38 | INTERSECTCLIPRECT = 30, 39 | INVERTRGN = 73, 40 | LINETO = 54, 41 | MASKBLT = 78, 42 | MODIFYWORLDTRANSFORM = 36, 43 | MOVETOEX = 27, 44 | OFFSETCLIPRGN = 26, 45 | PAINTRGN = 74, 46 | PIE = 47, 47 | PLGBLT = 79, 48 | POLYBEZIER = 2, 49 | POLYBEZIER16 = 85, 50 | POLYBEZIERTO = 5, 51 | POLYBEZIERTO16 = 88, 52 | POLYDRAW = 56, 53 | POLYDRAW16 = 92, 54 | POLYGON = 3, 55 | POLYGON16 = 86, 56 | POLYLINE = 4, 57 | POLYLINE16 = 87, 58 | POLYLINETO = 6, 59 | POLYLINETO16 = 89, 60 | POLYPOLYGON = 8, 61 | POLYPOLYGON16 = 91, 62 | POLYPOLYLINE = 7, 63 | POLYPOLYLINE16 = 90, 64 | POLYTEXTOUTA = 96, 65 | POLYTEXTOUTW = 97, 66 | REALIZEPALETTE = 52, 67 | RECTANGLE = 43, 68 | RESIZEPALETTE = 51, 69 | RESTOREDC = 34, 70 | ROUNDRECT = 44, 71 | SAVEDC = 33, 72 | SCALEVIEWPORTEXTEX = 31, 73 | SCALEWINDOWEXTEX = 32, 74 | SELECTCLIPPATH = 67, 75 | SELECTOBJECT = 37, 76 | SELECTPALETTE = 48, 77 | SETARCDIRECTION = 57, 78 | SETBKCOLOR = 25, 79 | SETBKMODE = 18, 80 | SETBRUSHORGEX = 13, 81 | SETCOLORADJUSTMENT = 23, 82 | SETDIBITSTODEVICE = 80, 83 | SETMAPMODE = 17, 84 | SETMAPPERFLAGS = 16, 85 | SETMETARGN = 28, 86 | SETMITERLIMIT = 58, 87 | SETPALETTEENTRIES = 50, 88 | SETPIXELV = 15, 89 | SETPOLYFILLMODE = 19, 90 | SETROP2 = 20, 91 | SETSTRETCHBLTMODE = 21, 92 | SETTEXTALIGN = 22, 93 | SETTEXTCOLOR = 24, 94 | SETVIEWPORTEXTEX = 11, 95 | SETVIEWPORTORGEX = 12, 96 | SETWINDOWEXTEX = 9, 97 | SETWINDOWORGEX = 10, 98 | SETWORLDTRANSFORM = 35, 99 | STRETCHBLT = 77, 100 | STRETCHDIBITS = 81, 101 | STROKEANDFILLPATH = 63, 102 | STROKEPATH = 64, 103 | WIDENPATH = 66, 104 | _default_ = Pass, 105 | ) 106 | 107 | generic_record = Struct("records", 108 | record_type, 109 | ULInt32("record_size"), # Size of the record in bytes 110 | Union("params", # Parameters 111 | Field("raw", lambda ctx: ctx._.record_size - 8), 112 | Array(lambda ctx: (ctx._.record_size - 8) // 4, ULInt32("params")) 113 | ), 114 | ) 115 | 116 | header_record = Struct("header_record", 117 | Const(record_type, "HEADER"), 118 | ULInt32("record_size"), # Size of the record in bytes 119 | SLInt32("bounds_left"), # Left inclusive bounds 120 | SLInt32("bounds_right"), # Right inclusive bounds 121 | SLInt32("bounds_top"), # Top inclusive bounds 122 | SLInt32("bounds_bottom"), # Bottom inclusive bounds 123 | SLInt32("frame_left"), # Left side of inclusive picture frame 124 | SLInt32("frame_right"), # Right side of inclusive picture frame 125 | SLInt32("frame_top"), # Top side of inclusive picture frame 126 | SLInt32("frame_bottom"), # Bottom side of inclusive picture frame 127 | Const(ULInt32("signature"), 0x464D4520), 128 | ULInt32("version"), # Version of the metafile 129 | ULInt32("size"), # Size of the metafile in bytes 130 | ULInt32("num_of_records"), # Number of records in the metafile 131 | ULInt16("num_of_handles"), # Number of handles in the handle table 132 | Padding(2), 133 | ULInt32("description_size"), # Size of description string in WORDs 134 | ULInt32("description_offset"), # Offset of description string in metafile 135 | ULInt32("num_of_palette_entries"), # Number of color palette entries 136 | SLInt32("device_width_pixels"), # Width of reference device in pixels 137 | SLInt32("device_height_pixels"), # Height of reference device in pixels 138 | SLInt32("device_width_mm"), # Width of reference device in millimeters 139 | SLInt32("device_height_mm"), # Height of reference device in millimeters 140 | 141 | # description string 142 | Pointer(lambda ctx: ctx.description_offset, 143 | StringAdapter( 144 | Array(lambda ctx: ctx.description_size, 145 | Field("description", 2) 146 | ) 147 | ) 148 | ), 149 | 150 | # padding up to end of record 151 | Padding(lambda ctx: ctx.record_size - 88), 152 | ) 153 | 154 | emf_file = Struct("emf_file", 155 | header_record, 156 | Array(lambda ctx: ctx.header_record.num_of_records - 1, 157 | generic_record 158 | ), 159 | ) 160 | 161 | 162 | if __name__ == "__main__": 163 | obj = emf_file.parse_stream(open("../../test/emf1.emf", "rb")) 164 | print obj 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/graphics/gif.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ax330d/ida_pdb_loader/051b6806810d8aaa40f973442b06c3c0e4c24131/pdbparse/construct/formats/graphics/gif.py -------------------------------------------------------------------------------- /pdbparse/construct/formats/graphics/jpeg.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ax330d/ida_pdb_loader/051b6806810d8aaa40f973442b06c3c0e4c24131/pdbparse/construct/formats/graphics/jpeg.py -------------------------------------------------------------------------------- /pdbparse/construct/formats/graphics/png.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ax330d/ida_pdb_loader/051b6806810d8aaa40f973442b06c3c0e4c24131/pdbparse/construct/formats/graphics/png.py -------------------------------------------------------------------------------- /pdbparse/construct/formats/graphics/wmf.py: -------------------------------------------------------------------------------- 1 | """ 2 | Windows Meta File 3 | """ 4 | from construct import * 5 | 6 | 7 | wmf_record = Struct("records", 8 | ULInt32("size"), # size in words, including the size, function and params 9 | Enum(ULInt16("function"), 10 | Arc = 0x0817, 11 | Chord = 0x0830, 12 | Ellipse = 0x0418, 13 | ExcludeClipRect = 0x0415, 14 | FloodFill = 0x0419, 15 | IntersectClipRect = 0x0416, 16 | LineTo = 0x0213, 17 | MoveTo = 0x0214, 18 | OffsetClipRgn = 0x0220, 19 | OffsetViewportOrg = 0x0211, 20 | OffsetWindowOrg = 0x020F, 21 | PatBlt = 0x061D, 22 | Pie = 0x081A, 23 | RealizePalette = 0x0035, 24 | Rectangle = 0x041B, 25 | ResizePalette = 0x0139, 26 | RestoreDC = 0x0127, 27 | RoundRect = 0x061C, 28 | SaveDC = 0x001E, 29 | ScaleViewportExt = 0x0412, 30 | ScaleWindowExt = 0x0400, 31 | SetBkColor = 0x0201, 32 | SetBkMode = 0x0102, 33 | SetMapMode = 0x0103, 34 | SetMapperFlags = 0x0231, 35 | SetPixel = 0x041F, 36 | SetPolyFillMode = 0x0106, 37 | SetROP2 = 0x0104, 38 | SetStretchBltMode = 0x0107, 39 | SetTextAlign = 0x012E, 40 | SetTextCharacterExtra = 0x0108, 41 | SetTextColor = 0x0209, 42 | SetTextJustification = 0x020A, 43 | SetViewportExt = 0x020E, 44 | SetViewportOrg = 0x020D, 45 | SetWindowExt = 0x020C, 46 | SetWindowOrg = 0x020B, 47 | _default_ = Pass, 48 | ), 49 | Array(lambda ctx: ctx.size - 3, ULInt16("params")), 50 | ) 51 | 52 | wmf_placeable_header = Struct("placeable_header", 53 | Const(ULInt32("key"), 0x9AC6CDD7), 54 | ULInt16("handle"), 55 | SLInt16("left"), 56 | SLInt16("top"), 57 | SLInt16("right"), 58 | SLInt16("bottom"), 59 | ULInt16("units_per_inch"), 60 | Padding(4), 61 | ULInt16("checksum") 62 | ) 63 | 64 | wmf_file = Struct("wmf_file", 65 | # --- optional placeable header --- 66 | Optional(wmf_placeable_header), 67 | 68 | # --- header --- 69 | Enum(ULInt16("type"), 70 | InMemory = 0, 71 | File = 1, 72 | ), 73 | Const(ULInt16("header_size"), 9), 74 | ULInt16("version"), 75 | ULInt32("size"), # file size is in words 76 | ULInt16("number_of_objects"), 77 | ULInt32("size_of_largest_record"), 78 | ULInt16("number_of_params"), 79 | 80 | # --- records --- 81 | GreedyRange(wmf_record) 82 | ) 83 | 84 | if __name__ == "__main__": 85 | obj = wmf_file.parse_stream(open("../../test/wmf1.wmf", "rb")) 86 | print obj 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/video/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | video file formats (avi, mpg, divx, ...) 3 | """ 4 | -------------------------------------------------------------------------------- /pdbparse/construct/formats/video/avi.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ax330d/ida_pdb_loader/051b6806810d8aaa40f973442b06c3c0e4c24131/pdbparse/construct/formats/video/avi.py -------------------------------------------------------------------------------- /pdbparse/construct/formats/video/divx.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ax330d/ida_pdb_loader/051b6806810d8aaa40f973442b06c3c0e4c24131/pdbparse/construct/formats/video/divx.py -------------------------------------------------------------------------------- /pdbparse/construct/formats/video/mpeg2.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ax330d/ida_pdb_loader/051b6806810d8aaa40f973442b06c3c0e4c24131/pdbparse/construct/formats/video/mpeg2.py -------------------------------------------------------------------------------- /pdbparse/construct/formats/video/mpeg3.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ax330d/ida_pdb_loader/051b6806810d8aaa40f973442b06c3c0e4c24131/pdbparse/construct/formats/video/mpeg3.py -------------------------------------------------------------------------------- /pdbparse/construct/lib/__init__.py: -------------------------------------------------------------------------------- 1 | from binary import int_to_bin, bin_to_int, swap_bytes, encode_bin, decode_bin 2 | from bitstream import BitStreamReader, BitStreamWriter 3 | from container import (Container, AttrDict, FlagsContainer, 4 | ListContainer, LazyContainer) 5 | from hex import HexString, hexdump 6 | from utils import Packer, StringIO 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /pdbparse/construct/lib/binary.py: -------------------------------------------------------------------------------- 1 | def int_to_bin(number, width = 32): 2 | if number < 0: 3 | number += 1 << width 4 | i = width - 1 5 | bits = ["\x00"] * width 6 | while number and i >= 0: 7 | bits[i] = "\x00\x01"[number & 1] 8 | number >>= 1 9 | i -= 1 10 | return "".join(bits) 11 | 12 | _bit_values = {"\x00" : 0, "\x01" : 1, "0" : 0, "1" : 1} 13 | def bin_to_int(bits, signed = False): 14 | number = 0 15 | bias = 0 16 | if signed and _bit_values[bits[0]] == 1: 17 | bits = bits[1:] 18 | bias = 1 << len(bits) 19 | for b in bits: 20 | number <<= 1 21 | number |= _bit_values[b] 22 | return number - bias 23 | 24 | def swap_bytes(bits, bytesize = 8): 25 | i = 0 26 | l = len(bits) 27 | output = [""] * ((l // bytesize) + 1) 28 | j = len(output) - 1 29 | while i < l: 30 | output[j] = bits[i : i + bytesize] 31 | i += bytesize 32 | j -= 1 33 | return "".join(output) 34 | 35 | _char_to_bin = {} 36 | _bin_to_char = {} 37 | for i in range(256): 38 | ch = chr(i) 39 | bin = int_to_bin(i, 8) 40 | _char_to_bin[ch] = bin 41 | _bin_to_char[bin] = ch 42 | _bin_to_char[bin] = ch 43 | 44 | def encode_bin(data): 45 | return "".join(_char_to_bin[ch] for ch in data) 46 | 47 | def decode_bin(data): 48 | assert len(data) & 7 == 0, "data length must be a multiple of 8" 49 | i = 0 50 | j = 0 51 | l = len(data) // 8 52 | chars = [""] * l 53 | while j < l: 54 | chars[j] = _bin_to_char[data[i:i+8]] 55 | i += 8 56 | j += 1 57 | return "".join(chars) 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /pdbparse/construct/lib/bitstream.py: -------------------------------------------------------------------------------- 1 | from binary import encode_bin, decode_bin 2 | 3 | 4 | class BitStreamReader(object): 5 | __slots__ = ["substream", "buffer", "total_size"] 6 | def __init__(self, substream): 7 | self.substream = substream 8 | self.total_size = 0 9 | self.buffer = "" 10 | def close(self): 11 | if self.total_size % 8 != 0: 12 | raise ValueError("total size of read data must be a multiple of 8", 13 | self.total_size) 14 | def tell(self): 15 | return self.substream.tell() 16 | def seek(self, pos, whence = 0): 17 | self.buffer = "" 18 | self.total_size = 0 19 | self.substream.seek(pos, whence) 20 | def read(self, count): 21 | assert count >= 0 22 | l = len(self.buffer) 23 | if count == 0: 24 | data = "" 25 | elif count <= l: 26 | data = self.buffer[:count] 27 | self.buffer = self.buffer[count:] 28 | else: 29 | data = self.buffer 30 | count -= l 31 | bytes = count // 8 32 | if count & 7: 33 | bytes += 1 34 | buf = encode_bin(self.substream.read(bytes)) 35 | data += buf[:count] 36 | self.buffer = buf[count:] 37 | self.total_size += len(data) 38 | return data 39 | 40 | 41 | class BitStreamWriter(object): 42 | __slots__ = ["substream", "buffer", "pos"] 43 | def __init__(self, substream): 44 | self.substream = substream 45 | self.buffer = [] 46 | self.pos = 0 47 | def close(self): 48 | self.flush() 49 | def flush(self): 50 | bytes = decode_bin("".join(self.buffer)) 51 | self.substream.write(bytes) 52 | self.buffer = [] 53 | self.pos = 0 54 | def tell(self): 55 | return self.substream.tell() + self.pos // 8 56 | def seek(self, pos, whence = 0): 57 | self.flush() 58 | self.substream.seek(pos, whence) 59 | def write(self, data): 60 | if not data: 61 | return 62 | if type(data) is not str: 63 | raise TypeError("data must be a string, not %r" % (type(data),)) 64 | self.buffer.append(data) 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /pdbparse/construct/lib/container.py: -------------------------------------------------------------------------------- 1 | def recursion_lock(retval, lock_name = "__recursion_lock__"): 2 | def decorator(func): 3 | def wrapper(self, *args, **kw): 4 | if getattr(self, lock_name, False): 5 | return retval 6 | setattr(self, lock_name, True) 7 | try: 8 | return func(self, *args, **kw) 9 | finally: 10 | setattr(self, lock_name, False) 11 | wrapper.__name__ = func.__name__ 12 | return wrapper 13 | return decorator 14 | 15 | class Container(object): 16 | """ 17 | A generic container of attributes 18 | """ 19 | __slots__ = ["__dict__", "__attrs__"] 20 | def __init__(self, **kw): 21 | self.__dict__.update(kw) 22 | object.__setattr__(self, "__attrs__", kw.keys()) 23 | 24 | def __eq__(self, other): 25 | try: 26 | return self.__dict__ == other.__dict__ 27 | except AttributeError: 28 | return False 29 | def __ne__(self, other): 30 | return not (self == other) 31 | 32 | def __delattr__(self, name): 33 | object.__delattr__(self, name) 34 | self.__attrs__.remove(name) 35 | def __setattr__(self, name, value): 36 | d = self.__dict__ 37 | if name not in d: 38 | self.__attrs__.append(name) 39 | d[name] = value 40 | def __getitem__(self, name): 41 | return self.__dict__[name] 42 | def __delitem__(self, name): 43 | self.__delattr__(name) 44 | def __setitem__(self, name, value): 45 | self.__setattr__(name, value) 46 | def __update__(self, obj): 47 | for name in obj.__attrs__: 48 | self[name] = obj[name] 49 | def __copy__(self): 50 | new = self.__class__() 51 | new.__attrs__ = self.__attrs__[:] 52 | new.__dict__ = self.__dict__.copy() 53 | return new 54 | 55 | @recursion_lock("<...>") 56 | def __repr__(self): 57 | attrs = sorted("%s = %r" % (k, v) 58 | for k, v in self.__dict__.iteritems() 59 | if not k.startswith("_")) 60 | return "%s(%s)" % (self.__class__.__name__, ", ".join(attrs)) 61 | def __str__(self): 62 | return self.__pretty_str__() 63 | @recursion_lock("<...>") 64 | def __pretty_str__(self, nesting = 1, indentation = " "): 65 | attrs = [] 66 | ind = indentation * nesting 67 | for k in self.__attrs__: 68 | v = self.__dict__[k] 69 | if not k.startswith("_"): 70 | text = [ind, k, " = "] 71 | if hasattr(v, "__pretty_str__"): 72 | text.append(v.__pretty_str__(nesting + 1, indentation)) 73 | else: 74 | text.append(repr(v)) 75 | attrs.append("".join(text)) 76 | if not attrs: 77 | return "%s()" % (self.__class__.__name__,) 78 | attrs.insert(0, self.__class__.__name__ + ":") 79 | return "\n".join(attrs) 80 | 81 | class FlagsContainer(Container): 82 | """ 83 | A container providing pretty-printing for flags. Only set flags are 84 | displayed. 85 | """ 86 | def __pretty_str__(self, nesting = 1, indentation = " "): 87 | attrs = [] 88 | ind = indentation * nesting 89 | for k in self.__attrs__: 90 | v = self.__dict__[k] 91 | if not k.startswith("_") and v: 92 | attrs.append(ind + k) 93 | if not attrs: 94 | return "%s()" % (self.__class__.__name__,) 95 | attrs.insert(0, self.__class__.__name__+ ":") 96 | return "\n".join(attrs) 97 | 98 | class ListContainer(list): 99 | """ 100 | A container for lists 101 | """ 102 | __slots__ = ["__recursion_lock__"] 103 | def __str__(self): 104 | return self.__pretty_str__() 105 | @recursion_lock("[...]") 106 | def __pretty_str__(self, nesting = 1, indentation = " "): 107 | if not self: 108 | return "[]" 109 | ind = indentation * nesting 110 | lines = ["["] 111 | for elem in self: 112 | lines.append("\n") 113 | lines.append(ind) 114 | if hasattr(elem, "__pretty_str__"): 115 | lines.append(elem.__pretty_str__(nesting + 1, indentation)) 116 | else: 117 | lines.append(repr(elem)) 118 | lines.append("\n") 119 | lines.append(indentation * (nesting - 1)) 120 | lines.append("]") 121 | return "".join(lines) 122 | 123 | class AttrDict(object): 124 | """ 125 | A dictionary that can be accessed both using indexing and attributes, 126 | i.e., 127 | x = AttrDict() 128 | x.foo = 5 129 | print x["foo"] 130 | """ 131 | __slots__ = ["__dict__"] 132 | def __init__(self, **kw): 133 | self.__dict__ = kw 134 | def __contains__(self, key): 135 | return key in self.__dict__ 136 | def __nonzero__(self): 137 | return bool(self.__dict__) 138 | def __repr__(self): 139 | return repr(self.__dict__) 140 | def __str__(self): 141 | return self.__pretty_str__() 142 | def __pretty_str__(self, nesting = 1, indentation = " "): 143 | if not self: 144 | return "{}" 145 | text = ["{\n"] 146 | ind = nesting * indentation 147 | for k in sorted(self.__dict__.keys()): 148 | v = self.__dict__[k] 149 | text.append(ind) 150 | text.append(repr(k)) 151 | text.append(" : ") 152 | if hasattr(v, "__pretty_str__"): 153 | try: 154 | text.append(v.__pretty_str__(nesting+1, indentation)) 155 | except Exception: 156 | text.append(repr(v)) 157 | else: 158 | text.append(repr(v)) 159 | text.append("\n") 160 | text.append((nesting-1) * indentation) 161 | text.append("}") 162 | return "".join(text) 163 | def __delitem__(self, key): 164 | del self.__dict__[key] 165 | def __getitem__(self, key): 166 | return self.__dict__[key] 167 | def __setitem__(self, key, value): 168 | self.__dict__[key] = value 169 | def __copy__(self): 170 | new = self.__class__() 171 | new.__dict__ = self.__dict__.copy() 172 | return new 173 | def __update__(self, other): 174 | if isinstance(other, dict): 175 | self.__dict__.update(other) 176 | else: 177 | self.__dict__.update(other.__dict__) 178 | 179 | class LazyContainer(object): 180 | __slots__ = ["subcon", "stream", "pos", "context", "_value"] 181 | def __init__(self, subcon, stream, pos, context): 182 | self.subcon = subcon 183 | self.stream = stream 184 | self.pos = pos 185 | self.context = context 186 | self._value = NotImplemented 187 | def __eq__(self, other): 188 | try: 189 | return self._value == other._value 190 | except AttributeError: 191 | return False 192 | def __ne__(self, other): 193 | return not (self == other) 194 | def __str__(self): 195 | return self.__pretty_str__() 196 | def __pretty_str__(self, nesting = 1, indentation = " "): 197 | if self._value is NotImplemented: 198 | text = "" 199 | elif hasattr(self._value, "__pretty_str__"): 200 | text = self._value.__pretty_str__(nesting, indentation) 201 | else: 202 | text = repr(self._value) 203 | return "%s: %s" % (self.__class__.__name__, text) 204 | def read(self): 205 | self.stream.seek(self.pos) 206 | return self.subcon._parse(self.stream, self.context) 207 | def dispose(self): 208 | self.subcon = None 209 | self.stream = None 210 | self.context = None 211 | self.pos = None 212 | def _get_value(self): 213 | if self._value is NotImplemented: 214 | self._value = self.read() 215 | return self._value 216 | value = property(_get_value) 217 | has_value = property(lambda self: self._value is not NotImplemented) 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | -------------------------------------------------------------------------------- /pdbparse/construct/lib/hex.py: -------------------------------------------------------------------------------- 1 | _printable = dict((chr(i), ".") for i in range(256)) 2 | _printable.update((chr(i), chr(i)) for i in range(32, 128)) 3 | 4 | def hexdump(data, linesize = 16): 5 | prettylines = [] 6 | if len(data) < 65536: 7 | fmt = "%%04X %%-%ds %%s" 8 | else: 9 | fmt = "%%08X %%-%ds %%s" 10 | fmt = fmt % (3 * linesize - 1,) 11 | for i in xrange(0, len(data), linesize): 12 | line = data[i : i + linesize] 13 | hextext = " ".join(b.encode("hex") for b in line) 14 | rawtext = "".join(_printable[b] for b in line) 15 | prettylines.append(fmt % (i, hextext, rawtext)) 16 | return prettylines 17 | 18 | class HexString(str): 19 | """ 20 | represents a string that will be hex-dumped (only via __pretty_str__). 21 | this class derives of str, and behaves just like a normal string in all 22 | other contexts. 23 | """ 24 | def __init__(self, data, linesize = 16): 25 | str.__init__(self, data) 26 | self.linesize = linesize 27 | def __new__(cls, data, *args, **kwargs): 28 | return str.__new__(cls, data) 29 | def __pretty_str__(self, nesting = 1, indentation = " "): 30 | sep = "\n" + indentation * nesting 31 | return sep + sep.join(hexdump(self)) 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /pdbparse/construct/lib/utils.py: -------------------------------------------------------------------------------- 1 | try: 2 | from cStringIO import StringIO 3 | except ImportError: 4 | from StringIO import StringIO 5 | 6 | 7 | try: 8 | from struct import Struct as Packer 9 | except ImportError: 10 | from struct import pack, unpack, calcsize 11 | class Packer(object): 12 | __slots__ = ["format", "size"] 13 | def __init__(self, format): 14 | self.format = format 15 | self.size = calcsize(format) 16 | def pack(self, *args): 17 | return pack(self.format, *args) 18 | def unpack(self, data): 19 | return unpack(self.format, data) 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | protocols - a collection of network protocols 3 | unlike the formats package, protocols convey information between two sides 4 | """ 5 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/application/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | application layer (various) protocols 3 | """ 4 | 5 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/application/dns.py: -------------------------------------------------------------------------------- 1 | """ 2 | Domain Name System (TCP/IP protocol stack) 3 | """ 4 | from construct import * 5 | from construct.protocols.layer3.ipv4 import IpAddressAdapter 6 | 7 | 8 | class DnsStringAdapter(Adapter): 9 | def _encode(self, obj, context): 10 | parts = obj.split(".") 11 | parts.append("") 12 | return parts 13 | def _decode(self, obj, context): 14 | return ".".join(obj[:-1]) 15 | 16 | dns_record_class = Enum(UBInt16("class"), 17 | RESERVED = 0, 18 | INTERNET = 1, 19 | CHAOS = 3, 20 | HESIOD = 4, 21 | NONE = 254, 22 | ANY = 255, 23 | ) 24 | 25 | dns_record_type = Enum(UBInt16("type"), 26 | IPv4 = 1, 27 | AUTHORITIVE_NAME_SERVER = 2, 28 | CANONICAL_NAME = 5, 29 | NULL = 10, 30 | MAIL_EXCHANGE = 15, 31 | TEXT = 16, 32 | X25 = 19, 33 | ISDN = 20, 34 | IPv6 = 28, 35 | UNSPECIFIED = 103, 36 | ALL = 255, 37 | ) 38 | 39 | query_record = Struct("query_record", 40 | DnsStringAdapter( 41 | RepeatUntil(lambda obj, ctx: obj == "", 42 | PascalString("name") 43 | ) 44 | ), 45 | dns_record_type, 46 | dns_record_class, 47 | ) 48 | 49 | rdata = Field("rdata", lambda ctx: ctx.rdata_length) 50 | 51 | resource_record = Struct("resource_record", 52 | CString("name", terminators = "\xc0\x00"), 53 | Padding(1), 54 | dns_record_type, 55 | dns_record_class, 56 | UBInt32("ttl"), 57 | UBInt16("rdata_length"), 58 | IfThenElse("data", lambda ctx: ctx.type == "IPv4", 59 | IpAddressAdapter(rdata), 60 | rdata 61 | ) 62 | ) 63 | 64 | dns = Struct("dns", 65 | UBInt16("id"), 66 | BitStruct("flags", 67 | Enum(Bit("type"), 68 | QUERY = 0, 69 | RESPONSE = 1, 70 | ), 71 | Enum(Nibble("opcode"), 72 | STANDARD_QUERY = 0, 73 | INVERSE_QUERY = 1, 74 | SERVER_STATUS_REQUEST = 2, 75 | NOTIFY = 4, 76 | UPDATE = 5, 77 | ), 78 | Flag("authoritive_answer"), 79 | Flag("truncation"), 80 | Flag("recurssion_desired"), 81 | Flag("recursion_available"), 82 | Padding(1), 83 | Flag("authenticated_data"), 84 | Flag("checking_disabled"), 85 | Enum(Nibble("response_code"), 86 | SUCCESS = 0, 87 | FORMAT_ERROR = 1, 88 | SERVER_FAILURE = 2, 89 | NAME_DOES_NOT_EXIST = 3, 90 | NOT_IMPLEMENTED = 4, 91 | REFUSED = 5, 92 | NAME_SHOULD_NOT_EXIST = 6, 93 | RR_SHOULD_NOT_EXIST = 7, 94 | RR_SHOULD_EXIST = 8, 95 | NOT_AUTHORITIVE = 9, 96 | NOT_ZONE = 10, 97 | ), 98 | ), 99 | UBInt16("question_count"), 100 | UBInt16("answer_count"), 101 | UBInt16("authority_count"), 102 | UBInt16("additional_count"), 103 | Array(lambda ctx: ctx.question_count, 104 | Rename("questions", query_record), 105 | ), 106 | Rename("answers", 107 | Array(lambda ctx: ctx.answer_count, resource_record) 108 | ), 109 | Rename("authorities", 110 | Array(lambda ctx: ctx.authority_count, resource_record) 111 | ), 112 | Array(lambda ctx: ctx.additional_count, 113 | Rename("additionals", resource_record), 114 | ), 115 | ) 116 | 117 | 118 | if __name__ == "__main__": 119 | cap1 = ( 120 | "2624010000010000000000000377777706676f6f676c6503636f6d0000010001" 121 | ).decode("hex") 122 | 123 | cap2 = ( 124 | "2624818000010005000600060377777706676f6f676c6503636f6d0000010001c00c00" 125 | "05000100089065000803777777016cc010c02c0001000100000004000440e9b768c02c" 126 | "0001000100000004000440e9b793c02c0001000100000004000440e9b763c02c000100" 127 | "0100000004000440e9b767c030000200010000a88600040163c030c030000200010000" 128 | "a88600040164c030c030000200010000a88600040165c030c030000200010000a88600" 129 | "040167c030c030000200010000a88600040161c030c030000200010000a88600040162" 130 | "c030c0c00001000100011d0c0004d8ef3509c0d0000100010000ca7c000440e9b309c0" 131 | "80000100010000c4c5000440e9a109c0900001000100004391000440e9b709c0a00001" 132 | "00010000ca7c000442660b09c0b00001000100000266000440e9a709" 133 | ).decode("hex") 134 | 135 | obj = dns.parse(cap1) 136 | print obj 137 | print repr(dns.build(obj)) 138 | 139 | print "-" * 80 140 | 141 | obj = dns.parse(cap2) 142 | print obj 143 | print repr(dns.build(obj)) 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/application/ftp.py: -------------------------------------------------------------------------------- 1 | """ 2 | File Transfer Protocol (TCP/IP protocol stack) 3 | """ 4 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/application/http.py: -------------------------------------------------------------------------------- 1 | """ 2 | Hyper Text Transfer Protocol (TCP/IP protocol stack) 3 | 4 | Construct is not meant for text manipulation, and is probably not the right 5 | tool for the job, but I wanted to demonstrate how this could be done using 6 | the provided `text` module. 7 | """ 8 | from construct import * 9 | from construct.text import * 10 | 11 | 12 | class HttpParamDictAdapter(Adapter): 13 | """turns the sequence of params into a dict""" 14 | def _encode(self, obj, context): 15 | return [Container(name = k, value = v) for k, v in obj.iteritems()] 16 | def _decode(self, obj, context): 17 | return dict((o.name, o.value) for o in obj) 18 | 19 | 20 | lineterm = Literal("\r\n") 21 | space = Whitespace() 22 | 23 | # http parameter: 'name: value\r\n' 24 | http_param = Struct("params", 25 | StringUpto("name", ":\r\n"), 26 | Literal(":"), 27 | space, 28 | StringUpto("value", "\r"), 29 | lineterm, 30 | ) 31 | 32 | http_params = HttpParamDictAdapter( 33 | OptionalGreedyRange(http_param) 34 | ) 35 | 36 | # request: command and params 37 | http_request = Struct("request", 38 | StringUpto("command", " "), 39 | space, 40 | StringUpto("url", " "), 41 | space, 42 | Literal("HTTP/"), 43 | StringUpto("version", "\r"), 44 | lineterm, 45 | http_params, 46 | lineterm, 47 | ) 48 | 49 | # reply: header (answer and params) and data 50 | http_reply = Struct("reply", 51 | Literal("HTTP/"), 52 | StringUpto("version", " "), 53 | space, 54 | DecNumber("code"), 55 | space, 56 | StringUpto("text", "\r"), 57 | lineterm, 58 | http_params, 59 | lineterm, 60 | HexDumpAdapter( 61 | Field("data", lambda ctx: int(ctx["params"]["Content-length"])) 62 | ), 63 | ) 64 | 65 | # session: request followed reply 66 | http_session = Struct("session", 67 | http_request, 68 | http_reply, 69 | ) 70 | 71 | 72 | if __name__ == "__main__": 73 | cap1 = ( 74 | "474554202f636e6e2f2e656c656d656e742f696d672f312e352f6365696c696e672f6e" 75 | "61765f706970656c696e655f646b626c75652e67696620485454502f312e310d0a486f" 76 | "73743a20692e636e6e2e6e65740d0a557365722d4167656e743a204d6f7a696c6c612f" 77 | "352e30202857696e646f77733b20553b2057696e646f7773204e5420352e313b20656e" 78 | "2d55533b2072763a312e382e3129204765636b6f2f3230303631303130204669726566" 79 | "6f782f322e300d0a4163636570743a20696d6167652f706e672c2a2f2a3b713d302e35" 80 | "0d0a4163636570742d4c616e67756167653a20656e2d75732c656e3b713d302e350d0a" 81 | "4163636570742d456e636f64696e673a20677a69702c6465666c6174650d0a41636365" 82 | "70742d436861727365743a2049534f2d383835392d312c7574662d383b713d302e372c" 83 | "2a3b713d302e370d0a4b6565702d416c6976653a203330300d0a436f6e6e656374696f" 84 | "6e3a206b6565702d616c6976650d0a526566657265723a20687474703a2f2f7777772e" 85 | "636e6e2e636f6d2f0d0a0d0a485454502f312e3120323030204f4b0d0a446174653a20" 86 | "53756e2c2031302044656320323030362031373a34383a303120474d540d0a53657276" 87 | "65723a204170616368650d0a436f6e74656e742d747970653a20696d6167652f676966" 88 | "0d0a457461673a202266313232383761352d63642d3562312d30220d0a4c6173742d6d" 89 | "6f6469666965643a204d6f6e2c2032372046656220323030362032323a33393a303920" 90 | "474d540d0a436f6e74656e742d6c656e6774683a20313435370d0a4163636570742d72" 91 | "616e6765733a2062797465730d0a4b6565702d416c6976653a2074696d656f75743d35" 92 | "2c206d61783d313032340d0a436f6e6e656374696f6e3a204b6565702d416c6976650d" 93 | "0a0d0a47494638396148001600f7000037618d436a94ebf0f4cad5e1bccad93a638fd2" 94 | "dce639628e52769c97adc44c7299426a93dce3eb6182a5dee5ec5d7fa338628d466d95" 95 | "88a1bb3c65907b97b4d43f3ba7bacdd9e1eaa6b8cce6ebf1dc5a59cc1313718faed8e0" 96 | "e99fb3c8ced9e350759b6989aa6787a85e80a391a8c0ffffffbbc9d8b1c2d3e0e7eed1" 97 | "dae5c2cfdcd2dbe57c98b4e7ecf23b648f587ba098aec4859eb9e4e9ef3e67918aa3bc" 98 | "aebfd17793b1cfd9e4abbdcfbfcddbb3c3d44b71995a7da13f6791a5b8cccbd6e17491" 99 | "b051759cd535327390afc7d2dfb8c7d7b0c0d24e739a7693b19bb0c64f749ac3cfdd49" 100 | "6f97afc0d14f749b3d66916e8cacb167758ba3bdd84b4c476e96c8d4e0d84340406892" 101 | "597ca0d53331adbed0a3b7cb52779d6f8ead9eb2c87a96b3a6b9cc567a9f94aac294ab" 102 | "c24b70985a7ca1b5c5d5b9c8d7aabccfd94849819bb7acbdd0c5d1dedb5253486f9744" 103 | "6c95da4943ae3832b7464fc40e0e3d659096acc3546d93c63c42796b88dce4eb815b74" 104 | "d02d1e9db2c7dc4a4a89a1bbc2393cd8413e9aafc5d01d1eb7c6d6da4142d43837c542" 105 | "48d3dce6687897d3322a829cb8d93438b2c2d3cd2120c4d1dd95abc3d6dfe8ca0e0cd8" 106 | "4c45e1e7eeb6c5d5cdd7e2d93c3c6c8bab5f5a73b14c56c6282b5b6386cd2826cf2829" 107 | "d5dee73e638c9f788acf3626686683436790d02724d32f2f7f728cde6261dd6864df6d" 108 | "6bc0353ecc3537dd545499617387637a864a5e8e697fd437388ca5be90a7c085687e8f" 109 | "a6bfd31d1e48648ce26665476d96d93137cd100fcb4944587195c02e34cd1619d94342" 110 | "7d7a95da4141da4343d63930d73c3399677bc3d0ddd22a2ad01f22d42f2d6d7d9dd124" 111 | "1de14b516384a6c64c52a64b58ab49514969915b7ea2c3636a734a5daa5255d9454468" 112 | "87a9bb3439be3b39dc353ecf26245e7396bc444c585d806081a46283a6dd615dd74a46" 113 | "dd675dd74138c90909dbe2ea6d8cac834d6489a2bcb15a65c34851b8636d54789e5679" 114 | "9ec26e78ae5762c20000d0dae4955c68dde4ecc0676fe0e6ed87a0bb4a7098446b948c" 115 | "a4bd8f6980aa39317d98b5c50b0d21f90400000000002c00000000480016000008ff00" 116 | "01081c48b0a0c18308132a5c583000c38710234a04e070a2c58b122b62dcc8d1a0c68e" 117 | "20377ec4c802038290080f24b08070e4453627d0b8406950828f160f0eba9c38228311" 118 | "09340df2f0704f8c4e83b4b2d98a82e79fb703b77c455a06204e33816226e1100140e5" 119 | "191f024d267c43a18005270a17241830e8e8c051fcb88d2b044f8e3860b0be914aa5ea" 120 | "53bf6d02cd40da5206800d01fe189d2b500744c217022204729c10028d220edc0a74b5" 121 | "2a0dbb6a98a8c1d160281d2f0dd7e8595b24f086010c5c007c3921d0c11726002e0df8" 122 | "9153c18f79057a5ce8d10000901a066c00b8b2a40365292704680610cd8a103b02ed15" 123 | "db706a8ea45d539471ff222450460a3e0a00207104a08100272978e4d9c7020500062c" 124 | "b0a5d84124170a2e9e9c018e00fa7c90c4112d3c01803a5a48e71141d058b78940ed94" 125 | "f30b20b1109385206d6c204c792b78915e17678cd14208000c80c0000a3651830561c4" 126 | "20401766bcb1441004a447003e044c13c28c00f8b186830d1164ca1d6968f28a1e7f54" 127 | "10c53a1590f38c31c8e062496b068011847a2a0ce442154a54e20e0060e8e001191444" 128 | "e0c6070ba8a0440e5c994001013b70501c00d01149d047740493cc14c3e8c24a16adf4" 129 | "d2082a9d4893491b7d08a4c3058401a00803035de14018393803050a4c5ca0861bf920" 130 | "20c01b176061c01000d4034415304c100e0010c88e5204a50f16248a368984b2073388" 131 | "00008a3cf100d08d39a5084442065bb597c4401390108109631c820e0058acc0001a33" 132 | "c0b0c02364ccf20e005e1c01c10a17b001c00c6b5132dd450f64d0040d0909000e470f" 133 | "78e0402deb5ef4c1315a1470d0016a2cc09104438e70101520bd00c4044119844d0c08" 134 | "71d0f0c40c7549f1c506895102c61c53d1051125941010003b").decode("hex") 135 | x = http_session.parse(cap1) 136 | print x 137 | #print x.request.url 138 | #print x.request.params["Referer"] 139 | #print x.reply.params["Server"] 140 | #print "-" * 80 141 | #print x 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/application/irc.py: -------------------------------------------------------------------------------- 1 | """ 2 | the Internet Relay Chat (IRC) protocol 3 | """ 4 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/application/netbios.py: -------------------------------------------------------------------------------- 1 | """ 2 | Microsoft Windows NetBIOS (TCP/IP protocol stack) 3 | """ 4 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/application/pop3.py: -------------------------------------------------------------------------------- 1 | """ 2 | the Post Office Protocol v3 (POP3) protocol 3 | """ 4 | 5 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/application/smtp.py: -------------------------------------------------------------------------------- 1 | """ 2 | the Simple Network Management Protocol (SNMP) 3 | """ -------------------------------------------------------------------------------- /pdbparse/construct/protocols/application/snmp.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple Network Management Protocol (TCP/IP protocol stack) 3 | """ 4 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/application/telnet.py: -------------------------------------------------------------------------------- 1 | """ 2 | Telnet (TCP/IP protocol stack) 3 | 4 | http://support.microsoft.com/kb/231866 5 | """ 6 | from construct import * 7 | from construct.text import * 8 | 9 | 10 | command_code = Enum(Byte("code"), 11 | SE = 240, # suboption end 12 | NOP = 241, # no-op 13 | Data_Mark = 242, # 14 | Break = 243, # 15 | Suspend = 244, # 16 | Abort_output = 245, # 17 | Are_You_There = 246, # 18 | Erase_Char = 247, # 19 | Erase_Line = 248, # 20 | Go_Ahead = 249, # other side can transmit now 21 | SB = 250, # suboption begin 22 | WILL = 251, # send says it will do option 23 | WONT = 252, # send says it will NOT do option 24 | DO = 253, # sender asks other side to do option 25 | DONT = 254, # sender asks other side NOT to do option 26 | IAC = 255, # interpretr as command (escape char) 27 | ) 28 | 29 | option_code = Enum(Byte("option"), 30 | TRANSMIT_BINARY = 0, 31 | ECHO = 1, 32 | RECONNECTION = 2, 33 | SUPPRESS_GO_AHEAD = 3, 34 | APPROX_MESSAGE_SIZE_NEGOTIATION = 4, 35 | STATUS = 5, 36 | TIMING_MARK = 6, 37 | RCTE = 7, 38 | OUTPUT_LINE_WIDTH = 8, 39 | OUTPUT_PAGE_SIZE = 9, 40 | NAOCRD = 10, 41 | NAOHTS = 11, 42 | NAOHTD = 12, 43 | NAOFFD = 13, 44 | NAOVTS = 14, 45 | NAOVTD = 15, 46 | NAOLFD = 16, 47 | EXTENDED_ASCII = 17, 48 | LOGOUT = 18, 49 | BM = 19, 50 | DATA_ENTRY_TERMINAL = 20, 51 | SUPDUP = 21, 52 | SUPDUP_OUTPUT = 22, 53 | SEND_LOCATION = 23, 54 | TERMINAL_TYPE = 24, 55 | END_OF_RECORD = 25, 56 | TUID = 26, 57 | OUTMRK = 27, 58 | TTYLOC = 28, 59 | TELNET_3270_REGIME = 29, 60 | X3_PAD = 30, 61 | NAWS = 31, 62 | TERMINAL_SPEED = 32, 63 | REMOTE_FLOW_CONTROL = 33, 64 | LINEMODE = 34, 65 | X_DISPLAY_LOCATION = 35, 66 | ENVIRONMENT_OPTION = 36, 67 | AUTHENTICATION = 37, 68 | ENCRYPTION_OPTION = 38, 69 | NEW_ENVIRONMENT_OPTION = 39, 70 | TN3270E = 40, 71 | XAUTH = 41, 72 | CHARSET = 42, 73 | RSP = 43, 74 | COM_PORT_CONTROL_OPTION = 44, 75 | TELNET_SUPPRESS_LOCAL_ECHO = 45, 76 | TELNET_START_TLS = 46, 77 | _default_ = Pass, 78 | ) 79 | 80 | class LookaheadAdapter(Adapter): 81 | def _encode(self, obj, context): 82 | if obj == "\xff": 83 | obj = "\xff\xff" 84 | return obj 85 | def _decode(self, (this, next), context): 86 | if this == "\xff": 87 | if next == "\xff": 88 | return "\xff" 89 | else: 90 | raise ValidationError("IAC") 91 | else: 92 | return this 93 | 94 | def TelnetData(name): 95 | return StringAdapter( 96 | GreedyRange( 97 | LookaheadAdapter( 98 | Sequence(name, 99 | Char("data"), 100 | Peek(Char("next")), 101 | ) 102 | ) 103 | ) 104 | ) 105 | 106 | telnet_suboption = Struct("suboption", 107 | option_code, 108 | TelnetData("parameters"), 109 | ) 110 | 111 | telnet_command = Struct("command", 112 | Literal("\xff"), 113 | command_code, 114 | Switch("option", lambda ctx: ctx.code, 115 | { 116 | "WILL" : option_code, 117 | "WONT" : option_code, 118 | "DO" : option_code, 119 | "DONT" : option_code, 120 | "SB" : telnet_suboption, 121 | }, 122 | default = Pass, 123 | ), 124 | ) 125 | 126 | telnet_unit = Select("telnet_unit", 127 | HexDumpAdapter(TelnetData("data")), 128 | telnet_command, 129 | ) 130 | 131 | telnet_session = Rename("telnet_session", GreedyRange(telnet_unit)) 132 | 133 | 134 | if __name__ == "__main__": 135 | # note: this capture contains both the client and server sides 136 | # so you'll see echos and stuff all mingled. it's not Construct's 137 | # fault, i was just too lazy to separate the two. 138 | cap1 = ( 139 | "fffd25fffb25fffa2501fff0fffa25000000fff0fffb26fffd18fffd20fffd23fffd27" 140 | "fffd24fffe26fffb18fffb1ffffc20fffc23fffb27fffc24fffd1ffffa2701fff0fffa" 141 | "1801fff0fffa1f009d0042fff0fffa2700fff0fffa1800414e5349fff0fffb03fffd01" 142 | "fffd22fffb05fffd21fffd03fffb01fffc22fffe05fffc21fffe01fffb01fffd06fffd" 143 | "00fffc010d0a7364662e6c6f6e65737461722e6f726720287474797239290d0a696620" 144 | "6e65772c206c6f67696e20276e657727202e2e0d0a0d0a6c6f67696e3a20fffd01fffc" 145 | "06fffb006e6e657765770d0a0d0a0d0a4c617374206c6f67696e3a2054687520446563" 146 | "2032312032303a31333a353320323030362066726f6d2038372e36392e34312e323034" 147 | "2e6361626c652e3031322e6e65742e696c206f6e2074747972760d0a0d0a596f752077" 148 | "696c6c206e6f7720626520636f6e6e656374656420746f204e455755534552206d6b61" 149 | "636374207365727665722e0d0a506c65617365206c6f67696e20617320276e65772720" 150 | "7768656e2070726f6d707465642e0d0a0d0a5b52455455524e5d202d2054484953204d" 151 | "41592054414b452041204d4f4d454e54202e2e201b5b481b5b4a547279696e67203139" 152 | "322e39342e37332e32302e2e2e0d0a436f6e6e656374656420746f206f6c2e66726565" 153 | "7368656c6c2e6f72672e0d0a4573636170652063686172616374657220697320276f66" 154 | "66272e0d0a0d0a7364662e6c6f6e65737461722e6f726720287474797033290d0a6966" 155 | "206e65772c206c6f67696e20276e657727202e2e0d0a0d0a6c6f67696e3a206e6e6577" 156 | "65770d0a0d0a0d0a4c617374206c6f67696e3a20546875204465632032312032303a30" 157 | "343a303120323030362066726f6d207364662e6c6f6e65737461722e6f7267206f6e20" 158 | "74747970390d0a1b5b481b5b4a57656c636f6d6520746f207468652053444620507562" 159 | "6c69632041636365737320554e49582053797374656d202d204573742e20313938370d" 160 | "0a596f75206172652074686520333735746820677565737420746f6461792c206c6f67" 161 | "67656420696e206f6e2032312d4465632d30362032303a31353a32332e0d0a0d0a4172" 162 | "6520796f75207573696e672057696e646f777320324b206f722058503f2028592f4e29" 163 | "200d0a202020202020202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d" 164 | "2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a2020202020207c20494d504f52" 165 | "54414e54212020504c4541534520524541442054484953205645525920434152454655" 166 | "4c4c59207c0d0a202020202020202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d" 167 | "2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a0d0a54686572652069" 168 | "7320612062756720696e207468652057696e646f777328746d292032303030202f2058" 169 | "502054454c4e455420636c69656e742077686963680d0a63617573657320697420746f" 170 | "2073656e642061203c43523e3c4c463e2028646f75626c652072657475726e29206279" 171 | "2064656661756c742e202049660d0a796f7520617265207573696e672057696e646f77" 172 | "7328746d292054454c4e455420796f75204d55535420636f7272656374207468697320" 173 | "5249474854204e4f570d0a696e206f7264657220746f20434f4e54494e55452e202050" 174 | "6c656173652074616b652074686520666f6c6c6f77696e6720342073746570733a0d0a" 175 | "0d0a2020312e202045534341504520746f207468652054454c4e45543e2070726f6d70" 176 | "74206279207072657373696e67205b4354524c5d207769746820796f7572205d206b65" 177 | "790d0a2020322e2020417420796f75722054454c4e45543e2070726f6d707420747970" 178 | "653a202027756e7365742063726c66270d0a20202020202028446f206e6f7420747970" 179 | "652027717569742720616674657220746869732073746570290d0a2020332e20205468" 180 | "656e20707265737320796f7572205b454e5445525d206b657920545749434520746f20" 181 | "72657475726e20746f205344460d0a2020342e20205479706520276d6b616363742720" 182 | "746f2063726561746520796f7572206e657720534446206163636f756e740d0a0d0a41" 183 | "6e20616c7465726e61746976652054454c4e455420636c69656e743a2020687474703a" 184 | "2f2f736466312e6f72672f74656c6e65740d0a0d0a46455020436f6d6d616e643a206d" 185 | "6d6b6b61616363636374740d0d0a1b5b481b5b4a0d0a504c4541534520524541442054" 186 | "484953204341524546554c4c593a0d0a0d0a596f75206172652061626f757420746f20" 187 | "637265617465206120554e4958207368656c6c206163636f756e742e20205468697320" 188 | "6163636f756e74206d617920626520756e6c696b650d0a616e797468696e6720796f75" 189 | "2776652075736564206265666f72652e20205765207572676520796f7520746f206361" 190 | "726566756c6c79207265616420616c6c2074686520746578740d0a646973706c617965" 191 | "64206f6e20796f7572207465726d696e616c2c2061732069742077696c6c2061696465" 192 | "20796f7520696e20796f7572206c6561726e696e672e0d0a576520616c736f20656e63" 193 | "6f757261676520796f7520746f2074727920616c6c2074686520636f6d6d616e647320" 194 | "617661696c61626c65207769746820796f7572206e65770d0a6163636f756e742e2020" 195 | "546865726520617265206d616e79207479706573206f662067616d65732c206170706c" 196 | "69636174696f6e7320616e64207574696c69746965730d0a796f752077696c6c206265" 197 | "2061626c6520746f20696e7374616e746c792072756e20696e206a7573742061206665" 198 | "77206d6f6d656e74732e2020496620796f75206172650d0a6c6f6f6b696e6720666f72" 199 | "206120706172746963756c617220636f6d6d616e64206f722076657273696f6e206f66" 200 | "206120636f6d6d616e64207468617420776520646f206e6f740d0a686176652c207468" 201 | "65726520617265207761797320746f2072657175657374207468617420697420626520" 202 | "696e7374616c6c65642e2020576520616c736f206f666665720d0a4449414c55502061" 203 | "636365737320696e207468652055534120616e642043616e6164612077686963682079" 204 | "6f752077696c6c2062652061626c6520746f206c6561726e2061626f75740d0a73686f" 205 | "72746c792e202042652070617469656e742c2072656164207768617420697320646973" 206 | "706c61796564202d204578706c6f726520616e6420456e6a6f79210d0a0d0a5b524554" 207 | "55524e5d0d0d0a0d0a46697273742c20796f75206e65656420746f2063686f6f736520" 208 | "61204c4f47494e2e202041204c4f47494e20616c6c6f777320796f7520746f204c4f47" 209 | "20494e0d0a746f207468652073797374656d2e2020596f7572204c4f47494e2063616e" 210 | "206265203120746f2038206368617261637465727320696e206c656e67746820616e64" 211 | "0d0a63616e20626520636f6d706f736564206f6620616c706861206e756d6572696320" 212 | "636861726163746572732e0d0a0d0a5768617420776f756c6420796f75206c696b6520" 213 | "746f2075736520666f7220796f7572206c6f67696e3f20737365626562756c756c6261" 214 | "62610d0d0a0d0a436f6e67726174756c6174696f6e732c20796f75277665207069636b" 215 | "6564206120434c45414e20757365722069642e20205768617420646f65732074686973" 216 | "206d65616e3f0d0a576520706572666f726d206461696c7920617564697473206f6e20" 217 | "6f7572206d61696c73657276657220776869636820616c6c6f777320757320746f2063" 218 | "6865636b206f6e20617474656d7074730d0a6f6620656d61696c2064656c6976657279" 219 | "20666f72206e6f6e2d6578697374656e74206c6f67696e732c206c696b652027736562" 220 | "756c6261272e202049662027736562756c626127207761730d0a746172676574746564" 221 | "20666f7220656d61696c2c20697420776f756c64206c696b656c792068617665206265" 222 | "656e20554345206f72207370616d2e2020486f77657665722c2074686572650d0a6861" 223 | "7665206265656e204e4f20617474656d70747320746f20656d61696c2027736562756c" 224 | "6261407364662e6c6f6e65737461722e6f72672720696e207468652070617374203234" 225 | "3020646179732c0d0a7768696368206d65616e732069742069732061205350414d2046" 226 | "524545206c6f67696e2e2020506c656173652070726f7465637420697420616e642065" 227 | "6e6a6f79210d0a0d0a636f6e74696e75653f20287965732f6e6f29207965730d796573" 228 | "0d0a1b5b481b5b4a1b5b3f31681b3d1b5b36363b31481b5b4b0d0a0d0a2a2a6c696d69" 229 | "746174696f6e7320616e6420706f6c6963792a2a0d0a20205f5f5f5f5f5f5f5f5f5f5f" 230 | "5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f" 231 | "5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f0d0a0d0a546865" 232 | "20534446205075626c69632041636365737320554e49582053797374656d2c20612035" 233 | "303128632937206e6f6e2d70726f66697420636f72706f726174696f6e2c0d0a726573" 234 | "65727665732074686520726967687420746f2064656e792061636365737320746f2061" 235 | "6e796f6e65207265676172646c6573732069662074686520757365720d0a686173206d" 236 | "616465206120646f6e6174696f6e206f722070616964206d656d626572736869702064" 237 | "7565732e2020496620612075736572277320616374697669746965730d0a6172652069" 238 | "6e74657266657272696e67207769746820616e6f746865722075736572206f72207573" 239 | "65727320286f6e20746869732073797374656d206f72206f6e0d0a616e6f7468657229" 240 | "20746865207573657220696e207175657374696f6e2077696c6c206861766520746865" 241 | "6972206163636f756e7420616363657373206c696d697465640d0a6f7220706f737369" 242 | "626c792072656d6f7665642e20205370616d6d696e67206f6620616e7920736f727420" 243 | "6973206e6f74207065726d697474656420616e6420776f756c640d0a726573756c7420" 244 | "696e206163636f756e742072656d6f76616c2e2020496c6c6567616c20616374697669" 245 | "746965732074686174206163746976656c7920696e766f6c7665200d0a534446202869" 246 | "64206573742c207573696e672053444620746f2072756e20637261636b206f7220666f" 247 | "72206775657373696e672070617373776f72647320616e642f6f720d0a74726164696e" 248 | "6720636f70797269676874656420776f726b292077696c6c206d6f7374206c696b656c" 249 | "7920726573756c7420696e206163636f756e742072656d6f76616c2e0d0a0d0a546865" 250 | "20534446205075626c69632041636365737320554e49582053797374656d206d616b65" 251 | "73206e6f2067756172616e7465657320696e207468652072656c696162696c6974790d" 252 | "0a6f7220707265736572766174696f6e206f66206163636f756e742061636365737369" 253 | "62696c6974792c20656d61696c2073656e74206f722072656365697665642c0d0a6669" 254 | "6c65732075706c6f61646564206f722063726561746564206279206f6e6c696e652065" 255 | "646974696e67206f7220636f6d70696c6174696f6e2e2020546861740d0a6265696e67" 256 | "20736169642c2064617461206c6f73732073686f756c64206f6e6c79206f6363757220" 257 | "647572696e67206120636174617374726f706869632068617264776172650d0a666169" 258 | "6c75726520696e20776869636820637269746963616c2066696c657320776f756c6420" 259 | "626520726573746f7265642066726f6d20746170652061726368697665732e200d0a0d" 260 | "0a4d656d62657273206f662074686520534446205075626c6963204163636573732055" 261 | "4e49582053797374656d2061726520657870656374656420746f20636f6e647563740d" 262 | "0a7468656d73656c76657320696e20616e20617070726f70726961746520616e642072" 263 | "6561736f6e61626c65206d616e6e6572207768656e207573696e67206f757220666163" 264 | "696c69746965732e0d0a0d0a4c69666574696d652041525041206d656d626572736869" 265 | "70206973206261736564206f6e20746865206c69666574696d65206f66205344462c20" 266 | "6e6f74206f66207468650d0a7573657220616e64206973206e6f6e2d7472616e736665" 267 | "7261626c652e20205344462068617320657869737465642073696e6365203139383720" 268 | "616e6420776974680d0a796f757220737570706f7274206974206a757374206d696768" 269 | "74206f7574206c69766520796f752e203b2d290d0a2020200d0a416e7920696c6c6567" 270 | "616c206163746976697469657320776869636820696e636c756465732c206275742063" 271 | "65727461696e6c792069736e2774206c696d6974656420746f0d0a7370616d6d696e67" 272 | "2c20706f7274666c6f6f64696e672c20706f72747363616e6e696e672c206972632062" 273 | "6f7473206f7220756e617474656e6465642070726f6365737365730d0a696e74656e64" 274 | "6564206173206120626f742c20656e6372797074696f6e20637261636b696e672c2075" 275 | "6e617574686f726973656420636f6e6e656374696f6e7320746f0d0a72656d6f746520" 276 | "686f73747320616e6420616e7920736f7274206f66207363616d2063616e207265616c" 277 | "6c79206e6f7420626520746f6c65726174656420686572652e0d0a5768793f20426563" 278 | "6175736520746865726520617265206d616e792068657265206f6e2074686973207379" 279 | "7374656d20746861742063616e207375666665722066726f6d0d0a7468697320736f72" 280 | "74206f662061627573652e2020496620796f752077616e7420746f2075736520534446" 281 | "2c20796f75207265616c6c79206861766520746f20636172650d0a61626f7574207468" 282 | "69732073797374656d20616e64207468652070656f706c6520686572652e2020496620" 283 | "796f7520646f6e27742077616e7420746f20636172652c207468656e0d0a796f752072" 284 | "65616c6c792073686f756c646e2774207573652074686973207265736f757263652e0d" 285 | "0a1b5b36363b31481b5b4b1b5b3f316c1b3e0d0a49206167726565207769746820796f" 286 | "757220706f6c69637920616e642061636365707420697420287965732f6e6f293a2079" 287 | "79657365730d0d0a0d0a4279206167726565696e6720616e6420616363657074696e67" 288 | "206f757220706f6c69637920776520747275737420796f7520746f0d0a666f6c6c6f77" 289 | "2069742e20205468616e6b20796f7520616e6420626520726573706f6e7369626c6521" 290 | "0d0a0d0a5b52455455524e5d0d0d0a1b5b481b5b4a534556454e20564552592053494d" 291 | "504c45205155455354494f4e533a0d0a0d0a506c656173652070726f76696465207468" 292 | "6520666f6c6c6f77696e6720696e666f726d6174696f6e2e2020596f757220686f6e65" 293 | "737479206973207265717565737465640d0a617320697420697320637269746963616c" 294 | "20696e206d61696e7461696e696e672074686520696e74656772697479206f66206f75" 295 | "722073797374656d2e20204e65770d0a6163636f756e7473207769746820626f677573" 296 | "20696e666f726d6174696f6e206d617920626520707572676564202a776974686f7574" 297 | "2a207761726e696e672e0d0a0d0a4354524c2d552077696c6c20636c65617220696e70" 298 | "7574202e2e0d0a0d0a596f75722046756c6c204e616d653a202020202066666f6f6f6f" 299 | "626261617272085e48085e4808080808080808085e485e485e485e485e485e485e485e" 300 | "48035e43035e4315082008082008082008082008082008082008082008082008082008" 301 | "0820080820080820080820080820080820080820080820080820080820080820080820" 302 | "08082008082008082008082008082008082008082008082008082008" 303 | ).decode("hex") 304 | print telnet_session.parse(cap1) 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/application/tftp.py: -------------------------------------------------------------------------------- 1 | """ 2 | the Trivial FTP protocol 3 | """ 4 | 5 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/application/xdr.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ax330d/ida_pdb_loader/051b6806810d8aaa40f973442b06c3c0e4c24131/pdbparse/construct/protocols/application/xdr.py -------------------------------------------------------------------------------- /pdbparse/construct/protocols/application/xwindows.py: -------------------------------------------------------------------------------- 1 | """ 2 | X-Windows (TCP/IP protocol stack) 3 | """ 4 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/ipstack.py: -------------------------------------------------------------------------------- 1 | """ 2 | TCP/IP Protocol Stack 3 | Note: before parsing the application layer over a TCP stream, you must 4 | first combine all the TCP frames into a stream. See utils.tcpip for 5 | some solutions 6 | """ 7 | from construct import * 8 | from layer2.ethernet import ethernet_header 9 | from layer3.ipv4 import ipv4_header 10 | from layer3.ipv6 import ipv6_header 11 | from layer4.tcp import tcp_header 12 | from layer4.udp import udp_header 13 | 14 | 15 | layer4_tcp = Struct("layer4_tcp", 16 | Rename("header", tcp_header), 17 | HexDumpAdapter( 18 | Field("next", lambda ctx: 19 | ctx["_"]["header"].payload_length - ctx["header"].header_length 20 | ) 21 | ), 22 | ) 23 | 24 | layer4_udp = Struct("layer4_udp", 25 | Rename("header", udp_header), 26 | HexDumpAdapter( 27 | Field("next", lambda ctx: ctx["header"].payload_length) 28 | ), 29 | ) 30 | 31 | layer3_payload = Switch("next", lambda ctx: ctx["header"].protocol, 32 | { 33 | "TCP" : layer4_tcp, 34 | "UDP" : layer4_udp, 35 | }, 36 | default = Pass 37 | ) 38 | 39 | layer3_ipv4 = Struct("layer3_ipv4", 40 | Rename("header", ipv4_header), 41 | layer3_payload, 42 | ) 43 | 44 | layer3_ipv6 = Struct("layer3_ipv6", 45 | Rename("header", ipv6_header), 46 | layer3_payload, 47 | ) 48 | 49 | layer2_ethernet = Struct("layer2_ethernet", 50 | Rename("header", ethernet_header), 51 | Switch("next", lambda ctx: ctx["header"].type, 52 | { 53 | "IPv4" : layer3_ipv4, 54 | "IPv6" : layer3_ipv6, 55 | }, 56 | default = Pass, 57 | ) 58 | ) 59 | 60 | ip_stack = Rename("ip_stack", layer2_ethernet) 61 | 62 | 63 | if __name__ == "__main__": 64 | cap1 = ( 65 | "0011508c283c001150886b570800450001e971474000800684e4c0a80202525eedda11" 66 | "2a0050d98ec61d54fe977d501844705dcc0000474554202f20485454502f312e310d0a" 67 | "486f73743a207777772e707974686f6e2e6f72670d0a557365722d4167656e743a204d" 68 | "6f7a696c6c612f352e30202857696e646f77733b20553b2057696e646f7773204e5420" 69 | "352e313b20656e2d55533b2072763a312e382e302e3129204765636b6f2f3230303630" 70 | "3131312046697265666f782f312e352e302e310d0a4163636570743a20746578742f78" 71 | "6d6c2c6170706c69636174696f6e2f786d6c2c6170706c69636174696f6e2f7868746d" 72 | "6c2b786d6c2c746578742f68746d6c3b713d302e392c746578742f706c61696e3b713d" 73 | "302e382c696d6167652f706e672c2a2f2a3b713d302e350d0a4163636570742d4c616e" 74 | "67756167653a20656e2d75732c656e3b713d302e350d0a4163636570742d456e636f64" 75 | "696e673a20677a69702c6465666c6174650d0a4163636570742d436861727365743a20" 76 | "49534f2d383835392d312c7574662d383b713d302e372c2a3b713d302e370d0a4b6565" 77 | "702d416c6976653a203330300d0a436f6e6e656374696f6e3a206b6565702d616c6976" 78 | "650d0a507261676d613a206e6f2d63616368650d0a43616368652d436f6e74726f6c3a" 79 | "206e6f2d63616368650d0a0d0a" 80 | ).decode("hex") 81 | 82 | cap2 = ( 83 | "0002e3426009001150f2c280080045900598fd22000036063291d149baeec0a8023c00" 84 | "500cc33b8aa7dcc4e588065010ffffcecd0000485454502f312e3120323030204f4b0d" 85 | "0a446174653a204672692c2031352044656320323030362032313a32363a323520474d" 86 | "540d0a5033503a20706f6c6963797265663d22687474703a2f2f7033702e7961686f6f" 87 | "2e636f6d2f7733632f7033702e786d6c222c2043503d2243414f2044535020434f5220" 88 | "4355522041444d20444556205441492050534120505344204956416920495644692043" 89 | "4f4e692054454c6f204f545069204f55522044454c692053414d69204f54526920554e" 90 | "5269205055426920494e4420504859204f4e4c20554e49205055522046494e20434f4d" 91 | "204e415620494e542044454d20434e542053544120504f4c204845412050524520474f" 92 | "56220d0a43616368652d436f6e74726f6c3a20707269766174650d0a566172793a2055" 93 | "7365722d4167656e740d0a5365742d436f6f6b69653a20443d5f796c683d58336f444d" 94 | "54466b64476c6f5a7a567842463954417a49334d5459784e446b4563476c6b417a4578" 95 | "4e6a59794d5463314e5463456447567a64414d7742485274634777446157356b5a5867" 96 | "7462412d2d3b20706174683d2f3b20646f6d61696e3d2e7961686f6f2e636f6d0d0a43" 97 | "6f6e6e656374696f6e3a20636c6f73650d0a5472616e736665722d456e636f64696e67" 98 | "3a206368756e6b65640d0a436f6e74656e742d547970653a20746578742f68746d6c3b" 99 | "20636861727365743d7574662d380d0a436f6e74656e742d456e636f64696e673a2067" 100 | "7a69700d0a0d0a366263382020200d0a1f8b0800000000000003dcbd6977db38b200fa" 101 | "f9fa9cf90f88326dd9b1169212b5d891739cd84ed2936d1277a7d3cbf1a1484a624c91" 102 | "0c4979893bbfec7d7bbfec556121012eb29d65e6be7be7762c9240a1502854150a85c2" 103 | "c37b87af9f9c7c7873449e9dbc7c41defcf2f8c5f327a4d1ee76dff79e74bb872787ec" 104 | "43bfa3e9ddeed1ab06692cd234daed762f2e2e3a17bd4e18cfbb276fbb8b74e9f7bb49" 105 | "1a7b76da7152a7b1bff110dfed3f5cb896030f4b37b508566dbb9f56def9a4f1240c52" 106 | "3748db275791db20367b9a3452f732a5d0f688bdb0e2c44d27bf9c1cb7470830b1632f" 107 | "4a490a3578c18fd6b9c5dec2f7732b2641783109dc0b7268a56e2bd527a931497b93b4" 108 | "3f49cd493a98a4c3493a9aa4e349aa6bf01f7cd78d89d6b2ed49b3d9baf223f8b307b5" 109 | "004a67eea627ded2dddadedb78d8656de428f856305f5973779223b0fff05ebbbde1db" 110 | "67082a499289ae0f06863e1c8f4c0639eaccbdd9a3547abf798a1f0ec6c73fafd2e4f1" 111 | "51ffd5f1c9e2f9e37ff74e74fbddd941b375eadb0942b3e3d5723a69f6060373a6cff4" 112 | "9e6df586dac8b11c4d1f1afd81319b0df45e6fd4925a6cee6db4dbfb19e225bc1b12e5" 113 | "6a098aed9309715c3b74dc5fde3e7f122ea3308061dac22f4018a4f8878367af5f4f2e" 114 | "bcc001a2d187bfffbefeb2477f75026be9269165bb93d92ab0532f0cb68264fbda9b6d" 115 | "dd0b92bfff867f3abe1bccd3c5f675eca6ab3820c1caf7f7be20e05363029f93c8f7d2" 116 | "ad46a7b1bd475ff62614f2de2c8cb7f08537d93a35fed0fe9a4c1af44363fb91beabed" 117 | "790f4f0d0e7a6f67c7dbbe3eedfd01e5bcbffe9a64bf289e00307bb1f7852371dadb13" 118 | "3df0c3798efba9d93a1db44e87dbd7d8b4cf50e95c780e304be745389fbbf11ef4cddf" 119 | "dcf4b162d629fa94d7defbe2fa892b3ece2c78d8fb221a84517003476a73dc3ad535d6" 120 | "e22c7fbd0db8cf3a511ca6211d3e28933fed9d8ea54f381f66c0c7f2cb0e4c3898ad2b" 121 | "3b0de3c9e918bf25abc88d6ddf02d65581418f94174addc9ebe94717e67ce557207b6d" 122 | "45f892773ae393adc62af57c18ecd27b46e5aa2feea5b58c7c173e6d94be1d3bd5afa3" 123 | "fcf571d409ded9b1eb06ef3d275d00c36f25f4916c6ed2a911cef88b0e4c0ecfa7a5b6" 124 | "27936600b3d28d9bdbe411" 125 | ).decode("hex") 126 | 127 | obj = ip_stack.parse(cap1) 128 | print obj 129 | print repr(ip_stack.build(obj)) 130 | 131 | print "-" * 80 132 | 133 | obj = ip_stack.parse(cap2) 134 | print obj 135 | print repr(ip_stack.build(obj)) 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/layer2/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | layer 2 (data link) protocols 3 | """ 4 | 5 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/layer2/arp.py: -------------------------------------------------------------------------------- 1 | """ 2 | Ethernet (TCP/IP protocol stack) 3 | """ 4 | from construct import * 5 | from ethernet import MacAddressAdapter 6 | from construct.protocols.layer3.ipv4 import IpAddressAdapter 7 | 8 | 9 | 10 | def HwAddress(name): 11 | return IfThenElse(name, lambda ctx: ctx.hardware_type == "ETHERNET", 12 | MacAddressAdapter(Field("data", lambda ctx: ctx.hwaddr_length)), 13 | Field("data", lambda ctx: ctx.hwaddr_length) 14 | ) 15 | 16 | def ProtoAddress(name): 17 | return IfThenElse(name, lambda ctx: ctx.protocol_type == "IP", 18 | IpAddressAdapter(Field("data", lambda ctx: ctx.protoaddr_length)), 19 | Field("data", lambda ctx: ctx.protoaddr_length) 20 | ) 21 | 22 | arp_header = Struct("arp_header", 23 | Enum(UBInt16("hardware_type"), 24 | ETHERNET = 1, 25 | EXPERIMENTAL_ETHERNET = 2, 26 | ProNET_TOKEN_RING = 4, 27 | CHAOS = 5, 28 | IEEE802 = 6, 29 | ARCNET = 7, 30 | HYPERCHANNEL = 8, 31 | ULTRALINK = 13, 32 | FRAME_RELAY = 15, 33 | FIBRE_CHANNEL = 18, 34 | IEEE1394 = 24, 35 | HIPARP = 28, 36 | ISO7816_3 = 29, 37 | ARPSEC = 30, 38 | IPSEC_TUNNEL = 31, 39 | INFINIBAND = 32, 40 | ), 41 | Enum(UBInt16("protocol_type"), 42 | IP = 0x0800, 43 | ), 44 | UBInt8("hwaddr_length"), 45 | UBInt8("protoaddr_length"), 46 | Enum(UBInt16("opcode"), 47 | REQUEST = 1, 48 | REPLY = 2, 49 | REQUEST_REVERSE = 3, 50 | REPLY_REVERSE = 4, 51 | DRARP_REQUEST = 5, 52 | DRARP_REPLY = 6, 53 | DRARP_ERROR = 7, 54 | InARP_REQUEST = 8, 55 | InARP_REPLY = 9, 56 | ARP_NAK = 10 57 | 58 | ), 59 | HwAddress("source_hwaddr"), 60 | ProtoAddress("source_protoaddr"), 61 | HwAddress("dest_hwaddr"), 62 | ProtoAddress("dest_protoaddr"), 63 | ) 64 | 65 | rarp_header = Rename("rarp_header", arp_header) 66 | 67 | 68 | if __name__ == "__main__": 69 | cap1 = "00010800060400010002e3426009c0a80204000000000000c0a80201".decode("hex") 70 | obj = arp_header.parse(cap1) 71 | print obj 72 | print repr(arp_header.build(obj)) 73 | 74 | print "-" * 80 75 | 76 | cap2 = "00010800060400020011508c283cc0a802010002e3426009c0a80204".decode("hex") 77 | obj = arp_header.parse(cap2) 78 | print obj 79 | print repr(arp_header.build(obj)) 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/layer2/ethernet.py: -------------------------------------------------------------------------------- 1 | """ 2 | Ethernet (TCP/IP protocol stack) 3 | """ 4 | from construct import * 5 | 6 | 7 | class MacAddressAdapter(Adapter): 8 | def _encode(self, obj, context): 9 | return obj.replace("-", "").decode("hex") 10 | def _decode(self, obj, context): 11 | return "-".join(b.encode("hex") for b in obj) 12 | 13 | def MacAddress(name): 14 | return MacAddressAdapter(Bytes(name, 6)) 15 | 16 | ethernet_header = Struct("ethernet_header", 17 | MacAddress("destination"), 18 | MacAddress("source"), 19 | Enum(UBInt16("type"), 20 | IPv4 = 0x0800, 21 | ARP = 0x0806, 22 | RARP = 0x8035, 23 | X25 = 0x0805, 24 | IPX = 0x8137, 25 | IPv6 = 0x86DD, 26 | _default_ = Pass, 27 | ), 28 | ) 29 | 30 | 31 | if __name__ == "__main__": 32 | cap = "0011508c283c0002e34260090800".decode("hex") 33 | obj = ethernet_header.parse(cap) 34 | print obj 35 | print repr(ethernet_header.build(obj)) 36 | 37 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/layer2/mtp2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Message Transport Part 2 (SS7 protocol stack) 3 | (untested) 4 | """ 5 | from construct import * 6 | 7 | 8 | mtp2_header = BitStruct("mtp2_header", 9 | Octet("flag1"), 10 | Bits("bsn", 7), 11 | Bit("bib"), 12 | Bits("fsn", 7), 13 | Bit("sib"), 14 | Octet("length"), 15 | Octet("service_info"), 16 | Octet("signalling_info"), 17 | Bits("crc", 16), 18 | Octet("flag2"), 19 | ) 20 | 21 | 22 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/layer3/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | layer 3 (network) protocols 3 | """ 4 | 5 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/layer3/dhcpv4.py: -------------------------------------------------------------------------------- 1 | """ 2 | Dynamic Host Configuration Protocol for IPv4 3 | 4 | http://www.networksorcery.com/enp/protocol/dhcp.htm 5 | http://www.networksorcery.com/enp/protocol/bootp/options.htm 6 | """ 7 | from construct import * 8 | from ipv4 import IpAddress 9 | 10 | 11 | dhcp_option = Struct("dhcp_option", 12 | Enum(Byte("code"), 13 | Pad = 0, 14 | Subnet_Mask = 1, 15 | Time_Offset = 2, 16 | Router = 3, 17 | Time_Server = 4, 18 | Name_Server = 5, 19 | Domain_Name_Server = 6, 20 | Log_Server = 7, 21 | Quote_Server = 8, 22 | LPR_Server = 9, 23 | Impress_Server = 10, 24 | Resource_Location_Server = 11, 25 | Host_Name = 12, 26 | Boot_File_Size = 13, 27 | Merit_Dump_File = 14, 28 | Domain_Name = 15, 29 | Swap_Server = 16, 30 | Root_Path = 17, 31 | Extensions_Path = 18, 32 | IP_Forwarding_enabledisable = 19, 33 | Nonlocal_Source_Routing_enabledisable = 20, 34 | Policy_Filter = 21, 35 | Maximum_Datagram_Reassembly_Size = 22, 36 | Default_IP_TTL = 23, 37 | Path_MTU_Aging_Timeout = 24, 38 | Path_MTU_Plateau_Table = 25, 39 | Interface_MTU = 26, 40 | All_Subnets_are_Local = 27, 41 | Broadcast_Address = 28, 42 | Perform_Mask_Discovery = 29, 43 | Mask_supplier = 30, 44 | Perform_router_discovery = 31, 45 | Router_solicitation_address = 32, 46 | Static_routing_table = 33, 47 | Trailer_encapsulation = 34, 48 | ARP_cache_timeout = 35, 49 | Ethernet_encapsulation = 36, 50 | Default_TCP_TTL = 37, 51 | TCP_keepalive_interval = 38, 52 | TCP_keepalive_garbage = 39, 53 | Network_Information_Service_domain = 40, 54 | Network_Information_Servers = 41, 55 | NTP_servers = 42, 56 | Vendor_specific_information = 43, 57 | NetBIOS_over_TCPIP_name_server = 44, 58 | NetBIOS_over_TCPIP_Datagram_Distribution_Server = 45, 59 | NetBIOS_over_TCPIP_Node_Type = 46, 60 | NetBIOS_over_TCPIP_Scope = 47, 61 | X_Window_System_Font_Server = 48, 62 | X_Window_System_Display_Manager = 49, 63 | Requested_IP_Address = 50, 64 | IP_address_lease_time = 51, 65 | Option_overload = 52, 66 | DHCP_message_type = 53, 67 | Server_identifier = 54, 68 | Parameter_request_list = 55, 69 | Message = 56, 70 | Maximum_DHCP_message_size = 57, 71 | Renew_time_value = 58, 72 | Rebinding_time_value = 59, 73 | Class_identifier = 60, 74 | Client_identifier = 61, 75 | NetWareIP_Domain_Name = 62, 76 | NetWareIP_information = 63, 77 | Network_Information_Service_Domain = 64, 78 | Network_Information_Service_Servers = 65, 79 | TFTP_server_name = 66, 80 | Bootfile_name = 67, 81 | Mobile_IP_Home_Agent = 68, 82 | Simple_Mail_Transport_Protocol_Server = 69, 83 | Post_Office_Protocol_Server = 70, 84 | Network_News_Transport_Protocol_Server = 71, 85 | Default_World_Wide_Web_Server = 72, 86 | Default_Finger_Server = 73, 87 | Default_Internet_Relay_Chat_Server = 74, 88 | StreetTalk_Server = 75, 89 | StreetTalk_Directory_Assistance_Server = 76, 90 | User_Class_Information = 77, 91 | SLP_Directory_Agent = 78, 92 | SLP_Service_Scope = 79, 93 | Rapid_Commit = 80, 94 | Fully_Qualified_Domain_Name = 81, 95 | Relay_Agent_Information = 82, 96 | Internet_Storage_Name_Service = 83, 97 | NDS_servers = 85, 98 | NDS_tree_name = 86, 99 | NDS_context = 87, 100 | BCMCS_Controller_Domain_Name_list = 88, 101 | BCMCS_Controller_IPv4_address_list = 89, 102 | Authentication = 90, 103 | Client_last_transaction_time = 91, 104 | Associated_ip = 92, 105 | Client_System_Architecture_Type = 93, 106 | Client_Network_Interface_Identifier = 94, 107 | Lightweight_Directory_Access_Protocol = 95, 108 | Client_Machine_Identifier = 97, 109 | Open_Group_User_Authentication = 98, 110 | Autonomous_System_Number = 109, 111 | NetInfo_Parent_Server_Address = 112, 112 | NetInfo_Parent_Server_Tag = 113, 113 | URL = 114, 114 | Auto_Configure = 116, 115 | Name_Service_Search = 117, 116 | Subnet_Selection = 118, 117 | DNS_domain_search_list = 119, 118 | SIP_Servers_DHCP_Option = 120, 119 | Classless_Static_Route_Option = 121, 120 | CableLabs_Client_Configuration = 122, 121 | GeoConf = 123, 122 | ), 123 | Switch("value", lambda ctx: ctx.code, 124 | { 125 | # codes without any value 126 | "Pad" : Pass, 127 | }, 128 | # codes followed by length and value fields 129 | default = Struct("value", 130 | Byte("length"), 131 | Field("data", lambda ctx: ctx.length), 132 | ) 133 | ) 134 | ) 135 | 136 | dhcp_header = Struct("dhcp_header", 137 | Enum(Byte("opcode"), 138 | BootRequest = 1, 139 | BootReply = 2, 140 | ), 141 | Enum(Byte("hardware_type"), 142 | Ethernet = 1, 143 | Experimental_Ethernet = 2, 144 | ProNET_Token_Ring = 4, 145 | Chaos = 5, 146 | IEEE_802 = 6, 147 | ARCNET = 7, 148 | Hyperchannel = 8, 149 | Lanstar = 9, 150 | ), 151 | Byte("hardware_address_length"), 152 | Byte("hop_count"), 153 | UBInt32("transaction_id"), 154 | UBInt16("elapsed_time"), 155 | BitStruct("flags", 156 | Flag("boardcast"), 157 | Padding(15), 158 | ), 159 | IpAddress("client_addr"), 160 | IpAddress("your_addr"), 161 | IpAddress("server_addr"), 162 | IpAddress("gateway_addr"), 163 | IpAddress("client_addr"), 164 | Bytes("client_hardware_addr", 16), 165 | Bytes("server_host_name", 64), 166 | Bytes("boot_filename", 128), 167 | # BOOTP/DHCP options 168 | # "The first four bytes contain the (decimal) values 99, 130, 83 and 99" 169 | Const(Bytes("magic", 4), "\x63\x82\x53\x63"), 170 | Rename("options", OptionalGreedyRange(dhcp_option)), 171 | ) 172 | 173 | 174 | if __name__ == "__main__": 175 | test = ( 176 | "01" "01" "08" "ff" "11223344" "1234" "0000" 177 | "11223344" "aabbccdd" "11223444" "aabbccdd" "11223344" 178 | 179 | "11223344556677889900aabbccddeeff" 180 | 181 | "41414141414141414141414141414141" "41414141414141414141414141414141" 182 | "41414141414141414141414141414141" "41414141414141414141414141414141" 183 | 184 | "42424242424242424242424242424242" "42424242424242424242424242424242" 185 | "42424242424242424242424242424242" "42424242424242424242424242424242" 186 | "42424242424242424242424242424242" "42424242424242424242424242424242" 187 | "42424242424242424242424242424242" "42424242424242424242424242424242" 188 | 189 | "63825363" 190 | 191 | "0104ffffff00" 192 | "00" 193 | "060811223344aabbccdd" 194 | ).decode("hex") 195 | 196 | print dhcp_header.parse(test) 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/layer3/dhcpv6.py: -------------------------------------------------------------------------------- 1 | """ 2 | the Dynamic Host Configuration Protocol (DHCP) for IPv6 3 | 4 | http://www.networksorcery.com/enp/rfc/rfc3315.txt 5 | """ 6 | from construct import * 7 | from ipv6 import Ipv6Address 8 | 9 | 10 | dhcp_option = Struct("dhcp_option", 11 | Enum(UBInt16("code"), 12 | OPTION_CLIENTID = 1, 13 | OPTION_SERVERID = 2, 14 | OPTION_IA_NA = 3, 15 | OPTION_IA_TA = 4, 16 | OPTION_IAADDR = 5, 17 | OPTION_ORO = 6, 18 | OPTION_PREFERENCE = 7, 19 | OPTION_ELAPSED_TIME = 8, 20 | OPTION_RELAY_MSG = 9, 21 | OPTION_AUTH = 11, 22 | OPTION_UNICAST = 12, 23 | OPTION_STATUS_CODE = 13, 24 | OPTION_RAPID_COMMIT = 14, 25 | OPTION_USER_CLASS = 15, 26 | OPTION_VENDOR_CLASS = 16, 27 | OPTION_VENDOR_OPTS = 17, 28 | OPTION_INTERFACE_ID = 18, 29 | OPTION_RECONF_MSG = 19, 30 | OPTION_RECONF_ACCEPT = 20, 31 | SIP_SERVERS_DOMAIN_NAME_LIST = 21, 32 | SIP_SERVERS_IPV6_ADDRESS_LIST = 22, 33 | DNS_RECURSIVE_NAME_SERVER = 23, 34 | DOMAIN_SEARCH_LIST = 24, 35 | OPTION_IA_PD = 25, 36 | OPTION_IAPREFIX = 26, 37 | OPTION_NIS_SERVERS = 27, 38 | OPTION_NISP_SERVERS = 28, 39 | OPTION_NIS_DOMAIN_NAME = 29, 40 | OPTION_NISP_DOMAIN_NAME = 30, 41 | SNTP_SERVER_LIST = 31, 42 | INFORMATION_REFRESH_TIME = 32, 43 | BCMCS_CONTROLLER_DOMAIN_NAME_LIST = 33, 44 | BCMCS_CONTROLLER_IPV6_ADDRESS_LIST = 34, 45 | OPTION_GEOCONF_CIVIC = 36, 46 | OPTION_REMOTE_ID = 37, 47 | RELAY_AGENT_SUBSCRIBER_ID = 38, 48 | OPTION_CLIENT_FQDN = 39, 49 | ), 50 | UBInt16("length"), 51 | Field("data", lambda ctx: ctx.length), 52 | ) 53 | 54 | client_message = Struct("client_message", 55 | Bitwise(BitField("transaction_id", 24)), 56 | ) 57 | 58 | relay_message = Struct("relay_message", 59 | Byte("hop_count"), 60 | Ipv6Address("linkaddr"), 61 | Ipv6Address("peeraddr"), 62 | ) 63 | 64 | dhcp_message = Struct("dhcp_message", 65 | Enum(Byte("msgtype"), 66 | # these are client-server messages 67 | SOLICIT = 1, 68 | ADVERTISE = 2, 69 | REQUEST = 3, 70 | CONFIRM = 4, 71 | RENEW = 5, 72 | REBIND = 6, 73 | REPLY = 7, 74 | RELEASE_ = 8, 75 | DECLINE_ = 9, 76 | RECONFIGURE = 10, 77 | INFORMATION_REQUEST = 11, 78 | # these two are relay messages 79 | RELAY_FORW = 12, 80 | RELAY_REPL = 13, 81 | ), 82 | # relay messages have a different structure from client-server messages 83 | Switch("params", lambda ctx: ctx.msgtype, 84 | { 85 | "RELAY_FORW" : relay_message, 86 | "RELAY_REPL" : relay_message, 87 | }, 88 | default = client_message, 89 | ), 90 | Rename("options", GreedyRange(dhcp_option)), 91 | ) 92 | 93 | 94 | if __name__ == "__main__": 95 | test1 = "\x03\x11\x22\x33\x00\x17\x00\x03ABC\x00\x05\x00\x05HELLO" 96 | test2 = "\x0c\x040123456789abcdef0123456789abcdef\x00\x09\x00\x0bhello world\x00\x01\x00\x00" 97 | print dhcp_message.parse(test1) 98 | print dhcp_message.parse(test2) 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/layer3/icmpv4.py: -------------------------------------------------------------------------------- 1 | """ 2 | Internet Control Message Protocol for IPv4 (TCP/IP protocol stack) 3 | """ 4 | from construct import * 5 | from ipv4 import IpAddress 6 | 7 | 8 | echo_payload = Struct("echo_payload", 9 | UBInt16("identifier"), 10 | UBInt16("sequence"), 11 | Bytes("data", 32), # length is implementation dependent... 12 | # is anyone using more than 32 bytes? 13 | ) 14 | 15 | dest_unreachable_payload = Struct("dest_unreachable_payload", 16 | Padding(2), 17 | UBInt16("next_hop_mtu"), 18 | IpAddress("host"), 19 | Bytes("echo", 8), 20 | ) 21 | 22 | dest_unreachable_code = Enum(Byte("code"), 23 | Network_unreachable_error = 0, 24 | Host_unreachable_error = 1, 25 | Protocol_unreachable_error = 2, 26 | Port_unreachable_error = 3, 27 | The_datagram_is_too_big = 4, 28 | Source_route_failed_error = 5, 29 | Destination_network_unknown_error = 6, 30 | Destination_host_unknown_error = 7, 31 | Source_host_isolated_error = 8, 32 | Desination_administratively_prohibited = 9, 33 | Host_administratively_prohibited2 = 10, 34 | Network_TOS_unreachable = 11, 35 | Host_TOS_unreachable = 12, 36 | ) 37 | 38 | icmp_header = Struct("icmp_header", 39 | Enum(Byte("type"), 40 | Echo_reply = 0, 41 | Destination_unreachable = 3, 42 | Source_quench = 4, 43 | Redirect = 5, 44 | Alternate_host_address = 6, 45 | Echo_request = 8, 46 | Router_advertisement = 9, 47 | Router_solicitation = 10, 48 | Time_exceeded = 11, 49 | Parameter_problem = 12, 50 | Timestamp_request = 13, 51 | Timestamp_reply = 14, 52 | Information_request = 15, 53 | Information_reply = 16, 54 | Address_mask_request = 17, 55 | Address_mask_reply = 18, 56 | _default_ = Pass, 57 | ), 58 | Switch("code", lambda ctx: ctx.type, 59 | { 60 | "Destination_unreachable" : dest_unreachable_code, 61 | }, 62 | default = Byte("code"), 63 | ), 64 | UBInt16("crc"), 65 | Switch("payload", lambda ctx: ctx.type, 66 | { 67 | "Echo_reply" : echo_payload, 68 | "Echo_request" : echo_payload, 69 | "Destination_unreachable" : dest_unreachable_payload, 70 | }, 71 | default = Pass 72 | ) 73 | ) 74 | 75 | 76 | if __name__ == "__main__": 77 | cap1 = ("0800305c02001b006162636465666768696a6b6c6d6e6f70717273747576776162" 78 | "63646566676869").decode("hex") 79 | cap2 = ("0000385c02001b006162636465666768696a6b6c6d6e6f70717273747576776162" 80 | "63646566676869").decode("hex") 81 | cap3 = ("0301000000001122aabbccdd0102030405060708").decode("hex") 82 | 83 | print icmp_header.parse(cap1) 84 | print icmp_header.parse(cap2) 85 | print icmp_header.parse(cap3) 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/layer3/icmpv6.py: -------------------------------------------------------------------------------- 1 | """ 2 | Internet Control Message Protocol for IPv6 (TCP/IP protocol stack) 3 | """ 4 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/layer3/ipv4.py: -------------------------------------------------------------------------------- 1 | """ 2 | Internet Protocol version 4 (TCP/IP protocol stack) 3 | """ 4 | from construct import * 5 | 6 | 7 | class IpAddressAdapter(Adapter): 8 | def _encode(self, obj, context): 9 | return "".join(chr(int(b)) for b in obj.split(".")) 10 | def _decode(self, obj, context): 11 | return ".".join(str(ord(b)) for b in obj) 12 | 13 | def IpAddress(name): 14 | return IpAddressAdapter(Bytes(name, 4)) 15 | 16 | def ProtocolEnum(code): 17 | return Enum(code, 18 | ICMP = 1, 19 | TCP = 6, 20 | UDP = 17, 21 | ) 22 | 23 | ipv4_header = Struct("ip_header", 24 | EmbeddedBitStruct( 25 | Const(Nibble("version"), 4), 26 | ExprAdapter(Nibble("header_length"), 27 | decoder = lambda obj, ctx: obj * 4, 28 | encoder = lambda obj, ctx: obj / 4 29 | ), 30 | ), 31 | BitStruct("tos", 32 | Bits("precedence", 3), 33 | Flag("minimize_delay"), 34 | Flag("high_throuput"), 35 | Flag("high_reliability"), 36 | Flag("minimize_cost"), 37 | Padding(1), 38 | ), 39 | UBInt16("total_length"), 40 | Value("payload_length", lambda ctx: ctx.total_length - ctx.header_length), 41 | UBInt16("identification"), 42 | EmbeddedBitStruct( 43 | Struct("flags", 44 | Padding(1), 45 | Flag("dont_fragment"), 46 | Flag("more_fragments"), 47 | ), 48 | Bits("frame_offset", 13), 49 | ), 50 | UBInt8("ttl"), 51 | ProtocolEnum(UBInt8("protocol")), 52 | UBInt16("checksum"), 53 | IpAddress("source"), 54 | IpAddress("destination"), 55 | Field("options", lambda ctx: ctx.header_length - 20), 56 | ) 57 | 58 | 59 | if __name__ == "__main__": 60 | cap = "4500003ca0e3000080116185c0a80205d474a126".decode("hex") 61 | obj = ipv4_header.parse(cap) 62 | print obj 63 | print repr(ipv4_header.build(obj)) 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/layer3/ipv6.py: -------------------------------------------------------------------------------- 1 | """ 2 | Internet Protocol version 6 (TCP/IP protocol stack) 3 | """ 4 | from construct import * 5 | from ipv4 import ProtocolEnum 6 | 7 | 8 | class Ipv6AddressAdapter(Adapter): 9 | def _encode(self, obj, context): 10 | return "".join(part.decode("hex") for part in obj.split(":")) 11 | def _decode(self, obj, context): 12 | return ":".join(b.encode("hex") for b in obj) 13 | 14 | def Ipv6Address(name): 15 | return Ipv6AddressAdapter(Bytes(name, 16)) 16 | 17 | 18 | ipv6_header = Struct("ip_header", 19 | EmbeddedBitStruct( 20 | OneOf(Bits("version", 4), [6]), 21 | Bits("traffic_class", 8), 22 | Bits("flow_label", 20), 23 | ), 24 | UBInt16("payload_length"), 25 | ProtocolEnum(UBInt8("protocol")), 26 | UBInt8("hoplimit"), 27 | Alias("ttl", "hoplimit"), 28 | Ipv6Address("source"), 29 | Ipv6Address("destination"), 30 | ) 31 | 32 | 33 | if __name__ == "__main__": 34 | o = ipv6_header.parse("\x6f\xf0\x00\x00\x01\x02\x06\x80" 35 | "0123456789ABCDEF" "FEDCBA9876543210" 36 | ) 37 | print o 38 | print repr(ipv6_header.build(o)) 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/layer3/ipx.py: -------------------------------------------------------------------------------- 1 | """ 2 | Novel's IPX 3 | """ -------------------------------------------------------------------------------- /pdbparse/construct/protocols/layer3/mtp3.py: -------------------------------------------------------------------------------- 1 | """ 2 | Message Transport Part 3 (SS7 protocol stack) 3 | (untested) 4 | """ 5 | from construct import * 6 | 7 | 8 | mtp3_header = BitStruct("mtp3_header", 9 | Nibble("service_indicator"), 10 | Nibble("subservice"), 11 | ) 12 | 13 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/layer4/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | layer 4 (transporation) protocols 3 | """ 4 | 5 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/layer4/isup.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISDN User Part (SS7 protocol stack) 3 | """ 4 | from construct import * 5 | 6 | 7 | isup_header = Struct("isup_header", 8 | Bytes("routing_label", 5), 9 | UBInt16("cic"), 10 | UBInt8("message_type"), 11 | # mandatory fixed parameters 12 | # mandatory variable parameters 13 | # optional parameters 14 | ) 15 | 16 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/layer4/sctp.py: -------------------------------------------------------------------------------- 1 | """ 2 | Stream Control Transmission Protocol (SS7 and TCP/IP protocol stacks) 3 | """ 4 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/layer4/tcp.py: -------------------------------------------------------------------------------- 1 | """ 2 | Transmission Control Protocol (TCP/IP protocol stack) 3 | """ 4 | from construct import * 5 | 6 | 7 | tcp_header = Struct("tcp_header", 8 | UBInt16("source"), 9 | UBInt16("destination"), 10 | UBInt32("seq"), 11 | UBInt32("ack"), 12 | EmbeddedBitStruct( 13 | ExprAdapter(Nibble("header_length"), 14 | encoder = lambda obj, ctx: obj / 4, 15 | decoder = lambda obj, ctx: obj * 4, 16 | ), 17 | Padding(3), 18 | Struct("flags", 19 | Flag("ns"), 20 | Flag("cwr"), 21 | Flag("ece"), 22 | Flag("urg"), 23 | Flag("ack"), 24 | Flag("psh"), 25 | Flag("rst"), 26 | Flag("syn"), 27 | Flag("fin"), 28 | ), 29 | ), 30 | UBInt16("window"), 31 | UBInt16("checksum"), 32 | UBInt16("urgent"), 33 | Field("options", lambda ctx: ctx.header_length - 20), 34 | ) 35 | 36 | if __name__ == "__main__": 37 | cap = "0db5005062303fb21836e9e650184470c9bc0000".decode("hex") 38 | 39 | obj = tcp_header.parse(cap) 40 | print obj 41 | print repr(tcp_header.build(obj)) 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/layer4/udp.py: -------------------------------------------------------------------------------- 1 | """ 2 | User Datagram Protocol (TCP/IP protocol stack) 3 | """ 4 | from construct import * 5 | 6 | 7 | udp_header = Struct("udp_header", 8 | Value("header_length", lambda ctx: 8), 9 | UBInt16("source"), 10 | UBInt16("destination"), 11 | ExprAdapter(UBInt16("payload_length"), 12 | encoder = lambda obj, ctx: obj + 8, 13 | decoder = lambda obj, ctx: obj - 8, 14 | ), 15 | UBInt16("checksum"), 16 | ) 17 | 18 | if __name__ == "__main__": 19 | cap = "0bcc003500280689".decode("hex") 20 | obj = udp_header.parse(cap) 21 | print obj 22 | print repr(udp_header.build(obj)) 23 | 24 | 25 | -------------------------------------------------------------------------------- /pdbparse/construct/protocols/ss7stack.py: -------------------------------------------------------------------------------- 1 | """ 2 | SS7 Protocol Stack 3 | """ 4 | 5 | -------------------------------------------------------------------------------- /pdbparse/construct/text.py: -------------------------------------------------------------------------------- 1 | from core import * 2 | from adapters import * 3 | from macros import * 4 | 5 | 6 | #=============================================================================== 7 | # exceptions 8 | #=============================================================================== 9 | class QuotedStringError(ConstructError): 10 | __slots__ = [] 11 | 12 | 13 | #=============================================================================== 14 | # constructs 15 | #=============================================================================== 16 | class QuotedString(Construct): 17 | r""" 18 | A quoted string (begins with an opening-quote, terminated by a 19 | closing-quote, which may be escaped by an escape character) 20 | 21 | Parameters: 22 | * name - the name of the field 23 | * start_quote - the opening quote character. default is '"' 24 | * end_quote - the closing quote character. default is '"' 25 | * esc_char - the escape character, or None to disable escaping. defualt 26 | is "\" (backslash) 27 | * encoding - the character encoding (e.g., "utf8"), or None to return 28 | raw bytes. defualt is None. 29 | * allow_eof - whether to allow EOF before the closing quote is matched. 30 | if False, an exception will be raised when EOF is reached by the closing 31 | quote is missing. default is False. 32 | 33 | Example: 34 | QuotedString("foo", start_quote = "{", end_quote = "}", esc_char = None) 35 | """ 36 | __slots__ = [ 37 | "start_quote", "end_quote", "char", "esc_char", "encoding", 38 | "allow_eof" 39 | ] 40 | def __init__(self, name, start_quote = '"', end_quote = None, 41 | esc_char = '\\', encoding = None, allow_eof = False): 42 | Construct.__init__(self, name) 43 | if end_quote is None: 44 | end_quote = start_quote 45 | self.start_quote = Literal(start_quote) 46 | self.char = Char("char") 47 | self.end_quote = end_quote 48 | self.esc_char = esc_char 49 | self.encoding = encoding 50 | self.allow_eof = allow_eof 51 | 52 | def _parse(self, stream, context): 53 | self.start_quote._parse(stream, context) 54 | text = [] 55 | escaped = False 56 | try: 57 | while True: 58 | ch = self.char._parse(stream, context) 59 | if ch == self.esc_char: 60 | if escaped: 61 | text.append(ch) 62 | escaped = False 63 | else: 64 | escaped = True 65 | elif ch == self.end_quote and not escaped: 66 | break 67 | else: 68 | text.append(ch) 69 | escaped = False 70 | except FieldError: 71 | if not self.allow_eof: 72 | raise 73 | text = "".join(text) 74 | if self.encoding is not None: 75 | text = text.decode(self.encoding) 76 | return text 77 | 78 | def _build(self, obj, stream, context): 79 | self.start_quote._build(None, stream, context) 80 | if self.encoding: 81 | obj = obj.encode(self.encoding) 82 | for ch in obj: 83 | if ch == self.esc_char: 84 | self.char._build(self.esc_char, stream, context) 85 | elif ch == self.end_quote: 86 | if self.esc_char is None: 87 | raise QuotedStringError("found ending quote in data, " 88 | "but no escape char defined", ch) 89 | else: 90 | self.char._build(self.esc_char, stream, context) 91 | self.char._build(ch, stream, context) 92 | self.char._build(self.end_quote, stream, context) 93 | 94 | def _sizeof(self, context): 95 | raise SizeofError("can't calculate size") 96 | 97 | 98 | #=============================================================================== 99 | # macros 100 | #=============================================================================== 101 | class WhitespaceAdapter(Adapter): 102 | """ 103 | Adapter for whitespace sequences; do not use directly. 104 | See Whitespace. 105 | 106 | Parameters: 107 | * subcon - the subcon to adapt 108 | * build_char - the character used for encoding (building) 109 | """ 110 | __slots__ = ["build_char"] 111 | def __init__(self, subcon, build_char): 112 | Adapter.__init__(self, subcon) 113 | self.build_char = build_char 114 | def _encode(self, obj, context): 115 | return self.build_char 116 | def _decode(self, obj, context): 117 | return None 118 | 119 | def Whitespace(charset = " \t", optional = True): 120 | """whitespace (space that is ignored between tokens). when building, the 121 | first character of the charset is used. 122 | * charset - the set of characters that are considered whitespace. default 123 | is space and tab. 124 | * optional - whether or not whitespace is optional. default is True. 125 | """ 126 | con = CharOf(None, charset) 127 | if optional: 128 | con = OptionalGreedyRange(con) 129 | else: 130 | con = GreedyRange(con) 131 | return WhitespaceAdapter(con, build_char = charset[0]) 132 | 133 | def Literal(text): 134 | """matches a literal string in the text 135 | * text - the text (string) to match 136 | """ 137 | return ConstAdapter(Field(None, len(text)), text) 138 | 139 | def Char(name): 140 | """a one-byte character""" 141 | return Field(name, 1) 142 | 143 | def CharOf(name, charset): 144 | """matches only characters of a given charset 145 | * name - the name of the field 146 | * charset - the set of valid characters 147 | """ 148 | return OneOf(Char(name), charset) 149 | 150 | def CharNoneOf(name, charset): 151 | """matches only characters that do not belong to a given charset 152 | * name - the name of the field 153 | * charset - the set of invalid characters 154 | """ 155 | return NoneOf(Char(name), charset) 156 | 157 | def Alpha(name): 158 | """a letter character (A-Z, a-z)""" 159 | return CharOf(name, set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')) 160 | 161 | def Digit(name): 162 | """a digit character (0-9)""" 163 | return CharOf(name, set('0123456789')) 164 | 165 | def AlphaDigit(name): 166 | """an alphanumeric character (A-Z, a-z, 0-9)""" 167 | return CharOf(name, set("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")) 168 | 169 | def BinDigit(name): 170 | """a binary digit (0-1)""" 171 | return CharOf(name, set('01')) 172 | 173 | def HexDigit(name): 174 | """a hexadecimal digit (0-9, A-F, a-f)""" 175 | return CharOf(name, set('0123456789abcdefABCDEF')) 176 | 177 | def Word(name): 178 | """a sequence of letters""" 179 | return StringAdapter(GreedyRange(Alpha(name))) 180 | 181 | class TextualIntAdapter(Adapter): 182 | """ 183 | Adapter for textual integers 184 | 185 | Parameters: 186 | * subcon - the subcon to adapt 187 | * radix - the base of the integer (decimal, hexadecimal, binary, ...) 188 | * digits - the sequence of digits of that radix 189 | """ 190 | __slots__ = ["radix", "digits"] 191 | def __init__(self, subcon, radix = 10, digits = "0123456789abcdef"): 192 | Adapter.__init__(self, subcon) 193 | if radix > len(digits): 194 | raise ValueError("not enough digits for radix %d" % (radix,)) 195 | self.radix = radix 196 | self.digits = digits 197 | def _encode(self, obj, context): 198 | chars = [] 199 | if obj < 0: 200 | chars.append("-") 201 | n = -obj 202 | else: 203 | n = obj 204 | r = self.radix 205 | digs = self.digits 206 | while n > 0: 207 | n, d = divmod(n, r) 208 | chars.append(digs[d]) 209 | # obj2 = "".join(reversed(chars)) 210 | # filler = digs[0] * (self._sizeof(context) - len(obj2)) 211 | # return filler + obj2 212 | return "".join(reversed(chars)) 213 | def _decode(self, obj, context): 214 | return int("".join(obj), self.radix) 215 | 216 | def DecNumber(name): 217 | """decimal number""" 218 | return TextualIntAdapter(GreedyRange(Digit(name))) 219 | 220 | def BinNumber(name): 221 | """binary number""" 222 | return TextualIntAdapter(GreedyRange(Digit(name)), 2) 223 | 224 | def HexNumber(name): 225 | """hexadecimal number""" 226 | return TextualIntAdapter(GreedyRange(Digit(name)), 16) 227 | 228 | def StringUpto(name, charset): 229 | """a string that stretches up to a terminator, or EOF. unlike CString, 230 | StringUpto will no consume the terminator char. 231 | * name - the name of the field 232 | * charset - the set of terminator characters""" 233 | return StringAdapter(OptionalGreedyRange(CharNoneOf(name, charset))) 234 | 235 | def Line(name): 236 | r"""a textual line (up to "\n")""" 237 | return StringUpto(name, "\n") 238 | 239 | class IdentifierAdapter(Adapter): 240 | """ 241 | Adapter for programmatic identifiers 242 | 243 | Parameters: 244 | * subcon - the subcon to adapt 245 | """ 246 | def _encode(self, obj, context): 247 | return obj[0], obj[1:] 248 | def _decode(self, obj, context): 249 | return obj[0] + "".join(obj[1]) 250 | 251 | def Identifier(name, 252 | headset = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"), 253 | tailset = set("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_") 254 | ): 255 | """a programmatic identifier (symbol). must start with a char of headset, 256 | followed by a sequence of tailset characters 257 | * name - the name of the field 258 | * headset - charset for the first character. default is A-Z, a-z, and _ 259 | * tailset - charset for the tail. default is A-Z, a-z, 0-9 and _ 260 | """ 261 | return IdentifierAdapter( 262 | Sequence(name, 263 | CharOf("head", headset), 264 | OptionalGreedyRange(CharOf("tail", tailset)), 265 | ) 266 | ) 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | -------------------------------------------------------------------------------- /pdbparse/dbgold.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from construct import * 4 | from pdbparse.pe import IMAGE_SECTION_HEADER 5 | from pdbparse.fpo import FPO_DATA 6 | from pdbparse.info import GUID 7 | 8 | CV_RSDS_HEADER = Struct("CV_RSDS", 9 | Const(Bytes("Signature", 4), "RSDS"), 10 | GUID("GUID"), 11 | ULInt32("Age"), 12 | CString("Filename"), 13 | ) 14 | 15 | CV_NB10_HEADER = Struct("CV_NB10", 16 | Const(Bytes("Signature", 4), "NB10"), 17 | ULInt32("Offset"), 18 | ULInt32("Timestamp"), 19 | ULInt32("Age"), 20 | CString("Filename"), 21 | ) 22 | 23 | DebugDirectoryType = Enum(ULInt32("Type"), 24 | IMAGE_DEBUG_TYPE_UNKNOWN = 0, 25 | IMAGE_DEBUG_TYPE_COFF = 1, 26 | IMAGE_DEBUG_TYPE_CODEVIEW = 2, 27 | IMAGE_DEBUG_TYPE_FPO = 3, 28 | IMAGE_DEBUG_TYPE_MISC = 4, 29 | IMAGE_DEBUG_TYPE_EXCEPTION = 5, 30 | IMAGE_DEBUG_TYPE_FIXUP = 6, 31 | IMAGE_DEBUG_TYPE_OMAP_TO_SRC = 7, 32 | IMAGE_DEBUG_TYPE_OMAP_FROM_SRC = 8, 33 | IMAGE_DEBUG_TYPE_BORLAND = 9, 34 | IMAGE_DEBUG_TYPE_RESERVED = 10, 35 | _default_ = "IMAGE_DEBUG_TYPE_UNKNOWN", 36 | ) 37 | 38 | DebugMiscType = Enum(ULInt32("Type"), 39 | IMAGE_DEBUG_MISC_EXENAME = 1, 40 | _default_ = Pass, 41 | ) 42 | 43 | IMAGE_SEPARATE_DEBUG_HEADER = Struct("IMAGE_SEPARATE_DEBUG_HEADER", 44 | Const(Bytes("Signature", 2), "DI"), 45 | ULInt16("Flags"), 46 | ULInt16("Machine"), 47 | ULInt16("Characteristics"), 48 | ULInt32("TimeDateStamp"), 49 | ULInt32("CheckSum"), 50 | ULInt32("ImageBase"), 51 | ULInt32("SizeOfImage"), 52 | ULInt32("NumberOfSections"), 53 | ULInt32("ExportedNamesSize"), 54 | ULInt32("DebugDirectorySize"), 55 | ULInt32("SectionAlignment"), 56 | Array(2,ULInt32("Reserved")), 57 | ) 58 | 59 | IMAGE_DEBUG_DIRECTORY = Struct("IMAGE_DEBUG_DIRECTORY", 60 | ULInt32("Characteristics"), 61 | ULInt32("TimeDateStamp"), 62 | ULInt16("MajorVersion"), 63 | ULInt16("MinorVersion"), 64 | DebugDirectoryType, 65 | ULInt32("SizeOfData"), 66 | ULInt32("AddressOfRawData"), 67 | ULInt32("PointerToRawData"), 68 | Pointer(lambda ctx: ctx.PointerToRawData, 69 | String("Data", lambda ctx: ctx.SizeOfData) 70 | ), 71 | ) 72 | 73 | IMAGE_DEBUG_MISC = Struct("IMAGE_DEBUG_MISC", 74 | DebugMiscType, 75 | ULInt32("Length"), 76 | Byte("Unicode"), 77 | Array(3, Byte("Reserved")), 78 | Tunnel( 79 | String("Strings", lambda ctx: ctx.Length - 12), 80 | GreedyRange(CString("Strings")), 81 | ), 82 | ) 83 | 84 | IMAGE_FUNCTION_ENTRY = Struct("IMAGE_FUNCTION_ENTRY", 85 | ULInt32("StartingAddress"), 86 | ULInt32("EndingAddress"), 87 | ULInt32("EndOfPrologue"), 88 | ) 89 | 90 | DbgFile = Struct("DbgFile", 91 | IMAGE_SEPARATE_DEBUG_HEADER, 92 | Array(lambda ctx: ctx.IMAGE_SEPARATE_DEBUG_HEADER.NumberOfSections, 93 | IMAGE_SECTION_HEADER), 94 | Tunnel( 95 | String("data", 96 | lambda ctx: ctx.IMAGE_SEPARATE_DEBUG_HEADER.ExportedNamesSize), 97 | GreedyRange(CString("ExportedNames")), 98 | ), 99 | Array(lambda ctx: ctx.IMAGE_SEPARATE_DEBUG_HEADER.DebugDirectorySize 100 | / IMAGE_DEBUG_DIRECTORY.sizeof(), IMAGE_DEBUG_DIRECTORY) 101 | ) 102 | -------------------------------------------------------------------------------- /pdbparse/dbi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from construct import * 4 | 5 | _ALIGN = 4 6 | 7 | _DEBUG = False 8 | 9 | def get_parsed_size(tp,con): 10 | return len(tp.build(con)) 11 | 12 | def SymbolRange(name): 13 | return Struct(name, 14 | SLInt16("section"), 15 | Padding(2), 16 | SLInt32("offset"), 17 | SLInt32("size"), 18 | ULInt32("flags"), 19 | SLInt16("module"), 20 | Padding(2), 21 | ULInt32("dataCRC"), 22 | ULInt32("relocCRC"), 23 | ) 24 | 25 | DBIHeader = Struct("DBIHeader", 26 | Const(Bytes("magic", 4), "\xFF\xFF\xFF\xFF"), # 0 27 | ULInt32("version"), # 4 28 | ULInt32("age"), # 8 29 | SLInt16("gssymStream"), # 12 30 | ULInt16("vers"), # 14 31 | SLInt16("pssymStream"), # 16 32 | ULInt16("pdbver"), # 18 33 | SLInt16("symrecStream"), # stream containing global symbols # 20 34 | ULInt16("pdbver2"), # 22 35 | ULInt32("module_size"), # total size of DBIExHeaders # 24 36 | ULInt32("secconSize"), # 28 37 | ULInt32("secmapSize"), # 32 38 | ULInt32("filinfSize"), # 36 39 | ULInt32("tsmapSize"), # 40 40 | ULInt32("mfcIndex"), # 44 41 | ULInt32("dbghdrSize"), # 48 42 | ULInt32("ecinfoSize"), # 52 43 | ULInt16("flags"), # 56 44 | Enum(ULInt16("Machine"), # 58 45 | IMAGE_FILE_MACHINE_UNKNOWN = 0x0, 46 | IMAGE_FILE_MACHINE_I386 = 0x014c, 47 | IMAGE_FILE_MACHINE_IA64 = 0x0200, 48 | IMAGE_FILE_MACHINE_AMD64 = 0x8664, 49 | ), 50 | ULInt32("resvd"), # 60 51 | ) 52 | 53 | DBIExHeader = Struct("DBIExHeader", 54 | ULInt32("opened"), 55 | SymbolRange("range"), 56 | ULInt16("flags"), 57 | SLInt16("stream"), 58 | ULInt32("symSize"), 59 | ULInt32("oldLineSize"), 60 | ULInt32("lineSize"), 61 | SLInt16("nSrcFiles"), 62 | Padding(2), 63 | ULInt32("offsets"), 64 | ULInt32("niSource"), 65 | ULInt32("niCompiler"), 66 | CString("modName"), 67 | CString("objName"), 68 | ) 69 | 70 | DbiDbgHeader = Struct("DbiDbgHeader", 71 | SLInt16("snFPO"), 72 | SLInt16("snException"), 73 | SLInt16("snFixup"), 74 | SLInt16("snOmapToSrc"), 75 | SLInt16("snOmapFromSrc"), 76 | SLInt16("snSectionHdr"), 77 | SLInt16("snTokenRidMap"), 78 | SLInt16("snXdata"), 79 | SLInt16("snPdata"), 80 | SLInt16("snNewFPO"), 81 | SLInt16("snSectionHdrOrig"), 82 | ) 83 | 84 | sstFileIndex = Struct("sstFileIndex", 85 | ULInt16("cMod"), 86 | ULInt16("cRef"), 87 | ) 88 | 89 | def parse_stream(stream): 90 | pos = 0 91 | dbihdr = DBIHeader.parse_stream(stream) 92 | pos += get_parsed_size(DBIHeader, dbihdr) 93 | stream.seek(pos) 94 | dbiexhdr_data = stream.read(dbihdr.module_size) 95 | 96 | # sizeof() is broken on CStrings for construct, so 97 | # this ugly ugly hack is necessary 98 | dbiexhdrs = [] 99 | while dbiexhdr_data: 100 | dbiexhdrs.append(DBIExHeader.parse(dbiexhdr_data)) 101 | sz = get_parsed_size(DBIExHeader,dbiexhdrs[-1]) 102 | if sz % _ALIGN != 0: sz = sz + (_ALIGN - (sz % _ALIGN)) 103 | dbiexhdr_data = dbiexhdr_data[sz:] 104 | 105 | # "Section Contribution" 106 | stream.seek(dbihdr.secconSize, 1) 107 | # "Section Map" 108 | stream.seek(dbihdr.secmapSize, 1) 109 | # 110 | # see: http://pierrelib.pagesperso-orange.fr/exec_formats/MS_Symbol_Type_v1.0.pdf 111 | # the contents of the filinfSize section is a 'sstFileIndex' 112 | # 113 | # "File Info" 114 | end = stream.tell() + dbihdr.filinfSize 115 | fileIndex = sstFileIndex.parse_stream(stream) 116 | modStart = Array(fileIndex.cMod, ULInt16("modStart")).parse_stream(stream) 117 | cRefCnt = Array(fileIndex.cMod, ULInt16("cRefCnt")).parse_stream(stream) 118 | NameRef = Array(fileIndex.cRef, ULInt32("NameRef")).parse_stream(stream) 119 | modules = [] # array of arrays of files 120 | files = [] # array of files (non unique) 121 | Names = stream.read(end - stream.tell()) 122 | 123 | if _DEBUG: 124 | print 'len(Names): ', len(Names) # 3013624 125 | print 'len(Names)/4: ', len(Names)/4 126 | print 'len(NameRef): ', len(NameRef) # 160 127 | print 'fileIndex.cMod (i): ', fileIndex.cMod # 2282 128 | print 'len(modStart): ', len(modStart) # 2282 129 | print 'len(cRefCnt): ', len(cRefCnt) # 2282 130 | 131 | skipped_names = 0 132 | lenNameRef = len(NameRef) 133 | for i in xrange(0, fileIndex.cMod): 134 | these = [] 135 | for j in xrange(modStart[i], modStart[i] + cRefCnt[i]): 136 | if j >= lenNameRef: 137 | if _DEBUG: 138 | print "IPL: Warning - out of bound access to NameRef, index {}, NameRef length: {}".format(j, lenNameRef) 139 | print "IPL: ... modStart[i] = {}, cRefCnt[i] = {}".format(modStart[i], cRefCnt[i]) 140 | skipped_names += 1 141 | if skipped_names > 10: 142 | break 143 | continue 144 | Name = CString("Name").parse(Names[NameRef[j]:]) 145 | files.append(Name) 146 | these.append(Name) 147 | modules.append(these) 148 | 149 | if _DEBUG: 150 | print "IPL: {} skipped names".format(skipped_names) 151 | 152 | #stream.seek(dbihdr.filinfSize, 1) 153 | # "TSM" 154 | stream.seek(dbihdr.tsmapSize, 1) 155 | # "EC" 156 | stream.seek(dbihdr.ecinfoSize, 1) 157 | # The data we really want 158 | dbghdr = DbiDbgHeader.parse_stream(stream) 159 | 160 | return Container(DBIHeader=dbihdr, 161 | DBIExHeaders=ListContainer(dbiexhdrs), 162 | DBIDbgHeader=dbghdr, 163 | modules=modules, 164 | files=files) 165 | 166 | def parse(data): 167 | return parse_stream(StringIO(data)) 168 | -------------------------------------------------------------------------------- /pdbparse/fpo.py: -------------------------------------------------------------------------------- 1 | from construct import * 2 | 3 | # FPO DATA 4 | FPO_DATA = Struct("FPO_DATA", 5 | ULInt32("ulOffStart"), # offset 1st byte of function code 6 | ULInt32("cbProcSize"), # number of bytes in function 7 | ULInt32("cdwLocals"), # number of bytes in locals/4 8 | ULInt16("cdwParams"), # number of bytes in params/4 9 | Embed(BitStruct("BitValues", 10 | Octet("cbProlog"), # number of bytes in prolog 11 | BitField("cbFrame",2), # frame type 12 | Bit("reserved"), # reserved for future use 13 | Flag("fUseBP"), # TRUE if EBP has been allocated 14 | Flag("fHasSEH"), # TRUE if SEH in func 15 | BitField("cbRegs",3), # number of regs saved 16 | )), 17 | ) 18 | 19 | # New style FPO records with program strings 20 | FPO_DATA_V2 = Struct("FPO_DATA_V2", 21 | ULInt32("ulOffStart"), 22 | ULInt32("cbProcSize"), 23 | ULInt32("cbLocals"), 24 | ULInt32("cbParams"), 25 | ULInt32("maxStack"), # so far only observed to be 0 26 | ULInt32("ProgramStringOffset"), 27 | ULInt16("cbProlog"), 28 | ULInt16("cbSavedRegs"), 29 | FlagsEnum(ULInt32("flags"), 30 | SEH = 1, 31 | CPPEH = 2, # conjectured 32 | fnStart = 4, 33 | ), 34 | ) 35 | 36 | # Ranges for both types 37 | FPO_DATA_LIST = GreedyRange(FPO_DATA) 38 | FPO_DATA_LIST_V2 = GreedyRange(FPO_DATA_V2) 39 | 40 | # Program string storage 41 | # May move this to a new file; in private symbols the values 42 | # include things that are not just FPO related. 43 | FPO_STRING_DATA = Struct("FPO_STRING_DATA", 44 | Const(Bytes("Signature",4), "\xFE\xEF\xFE\xEF"), 45 | ULInt32("Unk1"), 46 | ULInt32("szDataLen"), 47 | Union("StringData", 48 | String("Data",lambda ctx: ctx._.szDataLen), 49 | Tunnel( 50 | String("Strings",lambda ctx: ctx._.szDataLen), 51 | GreedyRange(CString("Strings")), 52 | ), 53 | ), 54 | ULInt32("lastDwIndex"), # data remaining = (last_dword_index+1)*4 55 | HexDumpAdapter(String("UnkData", lambda ctx: ((ctx.lastDwIndex+1)*4))), 56 | Terminator, 57 | ) 58 | -------------------------------------------------------------------------------- /pdbparse/gdata.py: -------------------------------------------------------------------------------- 1 | from construct import * 2 | from cStringIO import StringIO 3 | from pdbparse.tpi import merge_subcon 4 | 5 | gsym = Struct("global", 6 | ULInt16("leaf_type"), 7 | Embed(Switch("data", lambda ctx: ctx.leaf_type, 8 | { 9 | 0x110E : Struct("data_v3", 10 | ULInt32("symtype"), 11 | ULInt32("offset"), 12 | ULInt16("segment"), 13 | CString("name"), 14 | 15 | ), 16 | 0x1009 : Struct("data_v2", 17 | ULInt32("symtype"), 18 | ULInt32("offset"), 19 | ULInt16("segment"), 20 | PascalString("name", length_field=ULInt8("len")), 21 | ), 22 | }, 23 | default = Pass, 24 | )) 25 | ) 26 | 27 | GlobalsData = OptionalGreedyRange( 28 | Tunnel( 29 | PascalString("globals", length_field=ULInt16("len")), 30 | gsym, 31 | ) 32 | ) 33 | 34 | def parse(data): 35 | con = GlobalsData.parse(data) 36 | return con 37 | 38 | def parse_stream(stream): 39 | con = GlobalsData.parse_stream(stream) 40 | return con 41 | -------------------------------------------------------------------------------- /pdbparse/info.py: -------------------------------------------------------------------------------- 1 | from construct import * 2 | from io import BytesIO 3 | 4 | _strarray = GreedyRange(CString("names")) 5 | 6 | class StringArrayAdapter(Adapter): 7 | def _encode(self,obj,context): 8 | return _strarray._build(BytesIO(obj), context) 9 | def _decode(self,obj,context): 10 | return _strarray._parse(BytesIO(obj), context) 11 | 12 | def GUID(name): 13 | return Struct(name, 14 | ULInt32("Data1"), 15 | ULInt16("Data2"), 16 | ULInt16("Data3"), 17 | String("Data4", 8), 18 | ) 19 | 20 | Info = Struct("Info", 21 | ULInt32("Version"), 22 | ULInt32("TimeDateStamp"), 23 | ULInt32("Age"), 24 | GUID("GUID"), 25 | ULInt32("cbNames"), 26 | StringArrayAdapter(MetaField("names", lambda ctx: ctx.cbNames)), 27 | ) 28 | 29 | def parse_stream(stream): 30 | return Info.parse_stream(stream) 31 | 32 | def parse(data): 33 | return Info.parse(data) 34 | 35 | -------------------------------------------------------------------------------- /pdbparse/omap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from construct import * 4 | from bisect import bisect 5 | 6 | OMAP_ENTRY = Struct("OmapFromSrc", 7 | ULInt32("From"), 8 | ULInt32("To"), 9 | ) 10 | 11 | OMAP_ENTRIES = GreedyRange(OMAP_ENTRY) 12 | 13 | class Omap(object): 14 | def __init__(self, omapstream): 15 | self.omap = OMAP_ENTRIES.parse(omapstream) 16 | 17 | self._froms = None 18 | 19 | def remap(self, address): 20 | if not self._froms: 21 | self._froms = [o.From for o in self.omap] 22 | 23 | pos = bisect(self._froms, address) 24 | if self._froms[pos] != address: 25 | pos = pos - 1 26 | 27 | if self.omap[pos].To == 0: 28 | return self.omap[pos].To 29 | else: 30 | return self.omap[pos].To + (address - self.omap[pos].From) 31 | -------------------------------------------------------------------------------- /pdbparse/pe.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from pdbparse.info import GUID 4 | from construct import * 5 | 6 | IMAGE_SECTION_HEADER = Struct("IMAGE_SECTION_HEADER", 7 | String("Name", 8), 8 | Union("Misc", 9 | ULInt32("PhysicalAddress"), 10 | ULInt32("VirtualSize"), 11 | ), 12 | ULInt32("VirtualAddress"), 13 | ULInt32("SizeOfRawData"), 14 | ULInt32("PointerToRawData"), 15 | ULInt32("PointerToRelocations"), 16 | ULInt32("PointerToLinenumbers"), 17 | ULInt16("NumberOfRelocations"), 18 | ULInt16("NumberOfLinenumbers"), 19 | ULInt32("Characteristics"), 20 | ) 21 | 22 | Sections = OptionalGreedyRange(IMAGE_SECTION_HEADER) 23 | -------------------------------------------------------------------------------- /pdbparse/peinfo.py: -------------------------------------------------------------------------------- 1 | from pefile import PE, DEBUG_TYPE, DIRECTORY_ENTRY 2 | import ntpath 3 | from pdbparse.dbgold import CV_RSDS_HEADER, CV_NB10_HEADER, DebugDirectoryType 4 | 5 | def get_pe_debug_data(filename): 6 | pe = PE(filename, fast_load=True) 7 | # we prefer CodeView data to misc 8 | type = u'IMAGE_DEBUG_TYPE_CODEVIEW' 9 | dbgdata = get_debug_data(pe, DEBUG_TYPE[type]) 10 | if dbgdata is None: 11 | type = u'IMAGE_DEBUG_TYPE_MISC' 12 | dbgdata = get_debug_data(pe, DEBUG_TYPE[type]) 13 | if dbgdata is None: 14 | type = None 15 | return dbgdata, type 16 | 17 | def get_external_codeview(filename): 18 | pe = PE(filename, fast_load=True) 19 | dbgdata = get_debug_data(pe, DEBUG_TYPE[u'IMAGE_DEBUG_TYPE_CODEVIEW']) 20 | if dbgdata[:4] == u'RSDS': 21 | (guid,filename) = get_rsds(dbgdata) 22 | elif dbgdata[:4] == u'NB10': 23 | (guid,filename) = get_nb10(dbgdata) 24 | else: 25 | raise TypeError(u'Invalid CodeView signature: [%s]' % dbgdata[:4]) 26 | guid = guid.upper() 27 | return guid, filename 28 | 29 | def get_debug_data(pe, type=DEBUG_TYPE[u'IMAGE_DEBUG_TYPE_CODEVIEW']): 30 | retval = None 31 | if not hasattr(pe, u'DIRECTORY_ENTRY_DEBUG'): 32 | # fast loaded - load directory 33 | pe.parse_data_directories(DIRECTORY_ENTRY[u'IMAGE_DIRECTORY_ENTRY_DEBUG']) 34 | if not hasattr(pe, u'DIRECTORY_ENTRY_DEBUG'): 35 | raise PENoDebugDirectoryEntriesError() 36 | else: 37 | for entry in pe.DIRECTORY_ENTRY_DEBUG: 38 | off = entry.struct.PointerToRawData 39 | size = entry.struct.SizeOfData 40 | if entry.struct.Type == type: 41 | retval = pe.__data__[off:off+size] 42 | break 43 | return retval 44 | 45 | def get_dbg_fname(dbgdata): 46 | from pdbparse.dbgold import IMAGE_DEBUG_MISC 47 | dbgstruct = IMAGE_DEBUG_MISC.parse(dbgdata) 48 | return ntpath.basename(dbgstruct.Strings[0]) 49 | 50 | def get_rsds(dbgdata): 51 | dbg = CV_RSDS_HEADER.parse(dbgdata) 52 | guidstr = "%08x%04x%04x%s%x" % (dbg.GUID.Data1, dbg.GUID.Data2, 53 | dbg.GUID.Data3, dbg.GUID.Data4.encode('hex'), 54 | dbg.Age) 55 | return guidstr,ntpath.basename(dbg.Filename) 56 | 57 | def get_nb10(dbgdata): 58 | dbg = CV_NB10_HEADER.parse(dbgdata) 59 | guidstr = "%x%x" % (dbg.Timestamp, dbg.Age) 60 | return guidstr,ntpath.basename(dbg.Filename) 61 | 62 | def get_pe_guid(filename): 63 | try: 64 | pe = PE(filename, fast_load=True) 65 | except IOError, e: 66 | print e 67 | sys.exit(-1) 68 | guidstr = "%x%x" % (pe.FILE_HEADER.TimeDateStamp, 69 | pe.OPTIONAL_HEADER.SizeOfImage) 70 | return guidstr 71 | 72 | 73 | -------------------------------------------------------------------------------- /pdbparse/postfix_eval.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from pprint import pprint 4 | 5 | from operator import add, sub, mul, div, mod, pow, and_, or_, lshift, rshift, xor 6 | from operator import inv 7 | 8 | global vars 9 | vars = {} 10 | 11 | def assign(a,b): 12 | global vars 13 | if not isinstance(a,str) or not a.startswith("$"): 14 | raise ValueError("Cannot assign to a constant") 15 | vars[a] = b 16 | 17 | binary_ops = { 18 | "+": add, 19 | "-": sub, 20 | "/": div, 21 | "*": mul, 22 | "%": mod, 23 | "**": pow, 24 | "<<": lshift, 25 | ">>": rshift, 26 | "&": and_, 27 | "|": or_, 28 | } 29 | 30 | # Note: 31 | # ^ means "dereference". In the airbag unit tests, this is accompanied by 32 | # a memory region that can deal with dereferencing addresses (presumably). 33 | # Their test uses FakeMemoryRegion, which appears to just return x+1 for 34 | # any pointer x that is dereferenced. So we use lambda x: x+1 as a 35 | # placeholder. 36 | unary_ops = { 37 | "~": inv, 38 | "^": lambda x: x+1, 39 | } 40 | 41 | def evaluate(pfstr): 42 | global vars 43 | stack = [] 44 | for tok in pfstr.split(): 45 | if tok == "=": 46 | try: 47 | b = stack.pop() 48 | a = stack.pop() 49 | except IndexError: 50 | raise ValueError("Not enough values on the stack for requested operation") 51 | 52 | if isinstance(b,str) and (b.startswith("$") or b.startswith(".")): 53 | try: b = vars[b] 54 | except KeyError: raise ValueError("Name %s referenced before assignment" % b) 55 | 56 | assign(a,b) 57 | elif tok in binary_ops: 58 | try: 59 | b = stack.pop() 60 | a = stack.pop() 61 | except IndexError: 62 | raise ValueError("Not enough values on the stack for requested operation") 63 | 64 | if isinstance(a,str) and (a.startswith("$") or a.startswith(".")): 65 | try: a = vars[a] 66 | except KeyError: raise ValueError("Name %s referenced before assignment" % a) 67 | if isinstance(b,str) and (b.startswith("$") or b.startswith(".")): 68 | try: b = vars[b] 69 | except KeyError: raise ValueError("Name %s referenced before assignment" % b) 70 | 71 | stack.append(binary_ops[tok](a,b)) 72 | elif tok in unary_ops: 73 | try: 74 | a = stack.pop() 75 | except IndexError: 76 | raise ValueError("Not enough values on the stack for requested operation") 77 | 78 | if isinstance(a,str) and (a.startswith("$") or a.startswith(".")): 79 | try: a = vars[a] 80 | except KeyError: raise ValueError("Name %s referenced before assignment" % a) 81 | 82 | stack.append(unary_ops[tok](a)) 83 | elif tok.startswith("$") or tok.startswith("."): 84 | stack.append(tok) 85 | else: 86 | stack.append(int(tok,0)) 87 | if stack: 88 | raise ValueError("Values remain on the stack after processing") 89 | 90 | if __name__ == "__main__": 91 | pfstrs = [ 92 | ("$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =", True), 93 | ("$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + = $ebx $T0 28 - ^ =", True), 94 | ("$T0 $ebp = $T2 $esp = $T1 .raSearchStart = $eip $T1 ^ = $ebp $T0 = $esp $T1 4 + = $L $T0 .cbSavedRegs - = $P $T1 4 + .cbParams + = $ebx $T0 28 - ^ =", True), 95 | ] 96 | 97 | vars["$ebp"] = 0xbfff0010; 98 | vars["$eip"] = 0x10000000; 99 | vars["$esp"] = 0xbfff0000; 100 | vars[".cbSavedRegs"] = 4; 101 | vars[".cbParams"] = 4; 102 | vars[".raSearchStart"] = 0xbfff0020; 103 | 104 | for (test,should_succeed) in pfstrs: 105 | try: 106 | evaluate(test) 107 | if len(repr(test)) > 50: 108 | test = test[:50] + "[...]" 109 | if not should_succeed: 110 | print 'Test %-60s FAILED.' % repr(test) 111 | else: 112 | print 'Test %-60s PASSED.' % repr(test) 113 | except ValueError: 114 | if should_succeed: 115 | print 'Test %-60s FAILED.' % repr(test) 116 | else: 117 | print 'Test %-60s PASSED.' % repr(test) 118 | 119 | validate_data_1 = {} 120 | validate_data_1["$T0"] = 0xbfff0012; 121 | validate_data_1["$T1"] = 0xbfff0020; 122 | validate_data_1["$T2"] = 0xbfff0019; 123 | validate_data_1["$eip"] = 0xbfff0021; 124 | validate_data_1["$ebp"] = 0xbfff0012; 125 | validate_data_1["$esp"] = 0xbfff0024; 126 | validate_data_1["$L"] = 0xbfff000e; 127 | validate_data_1["$P"] = 0xbfff0028; 128 | validate_data_1["$ebx"] = 0xbffefff7; 129 | validate_data_1[".cbSavedRegs"] = 4; 130 | validate_data_1[".cbParams"] = 4; 131 | 132 | for k in validate_data_1: 133 | assert vars[k] == validate_data_1[k] 134 | 135 | vars = {} 136 | 137 | validate_data_0 = {} 138 | validate_data_0["$rAdd"] = 8; 139 | validate_data_0["$rAdd2"] = 4; 140 | validate_data_0["$rSub"] = 3; 141 | validate_data_0["$rMul"] = 54; 142 | validate_data_0["$rDivQ"] = 1; 143 | validate_data_0["$rDivM"] = 3; 144 | validate_data_0["$rDeref"] = 10; 145 | 146 | pfstrs = [ 147 | ( "$rAdd 2 2 + =", True ), # $rAdd = 2 + 2 = 4 148 | ( "$rAdd $rAdd 2 + =", True ), # $rAdd = $rAdd + 2 = 6 149 | ( "$rAdd 2 $rAdd + =", True ), # $rAdd = 2 + $rAdd = 8 150 | ( "99", False ), # put some junk on the stack... 151 | ( "$rAdd2 2 2 + =", True ), # ...and make sure things still work 152 | ( "$rAdd2\t2\n2 + =", True ), # same but with different whitespace 153 | ( "$rAdd2 2 2 + = ", True ), # trailing whitespace 154 | ( " $rAdd2 2 2 + =", True ), # leading whitespace 155 | ( "$rAdd2 2 2 + =", True ), # extra whitespace 156 | ( "$T0 2 = +", False ), # too few operands for add 157 | ( "2 + =", False ), # too few operands for add 158 | ( "2 +", False ), # too few operands for add 159 | ( "+", False ), # too few operands for add 160 | ( "^", False ), # too few operands for dereference 161 | ( "=", False ), # too few operands for assignment 162 | ( "2 =", False ), # too few operands for assignment 163 | ( "2 2 + =", False ), # too few operands for assignment 164 | ( "2 2 =", False ), # can't assign into a literal 165 | ( "k 2 =", False ), # can't assign into a constant 166 | ( "2", False ), # leftover data on stack 167 | ( "2 2 +", False ), # leftover data on stack 168 | ( "$rAdd", False ), # leftover data on stack 169 | ( "0 $T1 0 0 + =", False ), # leftover data on stack 170 | ( "$T2 $T2 2 + =", False ), # can't operate on an undefined value 171 | ( "$rMul 9 6 * =", True ), # $rMul = 9 * 6 = 54 172 | ( "$rSub 9 6 - =", True ), # $rSub = 9 - 6 = 3 173 | ( "$rDivQ 9 6 / =", True ), # $rDivQ = 9 / 6 = 1 174 | ( "$rDivM 9 6 % =", True ), # $rDivM = 9 % 6 = 3 175 | ( "$rDeref 9 ^ =", True ) # $rDeref = ^9 = 10 (FakeMemoryRegion) 176 | ] 177 | 178 | for (test,should_succeed) in pfstrs: 179 | try: 180 | evaluate(test) 181 | if len(repr(test)) > 50: 182 | test = test[:50] + "[...]" 183 | if not should_succeed: 184 | print 'Test %-60s FAILED.' % repr(test) 185 | else: 186 | print 'Test %-60s PASSED.' % repr(test) 187 | except ValueError: 188 | if should_succeed: 189 | print 'Test %-60s FAILED.' % repr(test) 190 | else: 191 | print 'Test %-60s PASSED.' % repr(test) 192 | 193 | for k in validate_data_0: 194 | assert vars[k] == validate_data_0[k] 195 | -------------------------------------------------------------------------------- /pdbparse/symlookup.py: -------------------------------------------------------------------------------- 1 | import pdbparse 2 | import os 3 | from operator import itemgetter,attrgetter 4 | from bisect import bisect_right 5 | from pdbparse.undecorate import undecorate 6 | from itertools import islice, count 7 | 8 | class DummyOmap(object): 9 | def remap(self, addr): 10 | return addr 11 | 12 | class Lookup(object): 13 | def __init__(self, mods): 14 | self.addrs = {} 15 | self._cache = {} 16 | 17 | not_found = [] 18 | 19 | for pdbname,base in mods: 20 | pdbbase = ".".join(os.path.basename(pdbname).split('.')[:-1]) 21 | if not os.path.exists(pdbname): 22 | print "WARN: %s not found" % pdbname 23 | not_found.append( (base, pdbbase) ) 24 | return None 25 | 26 | #print "Loading symbols for %s..." % pdbbase 27 | try: 28 | # Do this the hard way to avoid having to load 29 | # the types stream in mammoth PDB files 30 | pdb = pdbparse.parse(pdbname, fast_load=True) 31 | pdb.STREAM_DBI.load() 32 | pdb._update_names() 33 | pdb.STREAM_GSYM = pdb.STREAM_GSYM.reload() 34 | if pdb.STREAM_GSYM.size: 35 | pdb.STREAM_GSYM.load() 36 | pdb.STREAM_SECT_HDR = pdb.STREAM_SECT_HDR.reload() 37 | pdb.STREAM_SECT_HDR.load() 38 | # These are the dicey ones 39 | pdb.STREAM_OMAP_FROM_SRC = pdb.STREAM_OMAP_FROM_SRC.reload() 40 | pdb.STREAM_OMAP_FROM_SRC.load() 41 | pdb.STREAM_SECT_HDR_ORIG = pdb.STREAM_SECT_HDR_ORIG.reload() 42 | pdb.STREAM_SECT_HDR_ORIG.load() 43 | 44 | except AttributeError, e: 45 | pass 46 | #except Exception, e: 47 | # print "WARN: error %s parsing %s, skipping" % (e,pdbbase) 48 | # not_found.append( (base, pdbbase) ) 49 | # continue 50 | 51 | try: 52 | sects = pdb.STREAM_SECT_HDR_ORIG.sections 53 | omap = pdb.STREAM_OMAP_FROM_SRC 54 | except AttributeError as e: 55 | # In this case there is no OMAP, so we use the given section 56 | # headers and use the identity function for omap.remap 57 | sects = pdb.STREAM_SECT_HDR.sections 58 | omap = DummyOmap() 59 | gsyms = pdb.STREAM_GSYM 60 | if not hasattr(gsyms, 'globals'): 61 | gsyms.globals = [] 62 | 63 | last_sect = max(sects, key=attrgetter('VirtualAddress')) 64 | limit = base + last_sect.VirtualAddress + last_sect.Misc.VirtualSize 65 | 66 | self.addrs[base,limit] = {} 67 | self.addrs[base,limit]['name'] = pdbbase 68 | self.addrs[base,limit]['addrs'] = [] 69 | for sym in gsyms.globals: 70 | if not hasattr(sym, 'offset'): continue 71 | off = sym.offset 72 | try: 73 | virt_base = sects[sym.segment-1].VirtualAddress 74 | except IndexError: 75 | continue 76 | 77 | mapped = omap.remap(off+virt_base) + base 78 | self.addrs[base,limit]['addrs'].append((mapped,sym.name)) 79 | 80 | self.addrs[base,limit]['addrs'].sort(key=itemgetter(0)) 81 | 82 | self.locs = {} 83 | self.names = {} 84 | for base,limit in self.addrs: 85 | mod = self.addrs[base,limit]['name'] 86 | symbols = self.addrs[base,limit]['addrs'] 87 | self.locs[base,limit] = [a[0] for a in symbols] 88 | self.names[base,limit] = [a[1] for a in symbols] 89 | 90 | def lookup(self, loc, no_offsets=False): 91 | if loc in self._cache: 92 | return self._cache[loc] 93 | 94 | for base,limit in self.addrs: 95 | if loc >= base and loc < limit: 96 | mod = self.addrs[base,limit]['name'] 97 | symbols = self.addrs[base,limit]['addrs'] 98 | locs = self.locs[base,limit] 99 | names = self.names[base,limit] 100 | idx = bisect_right(locs, loc) - 1 101 | diff = loc - locs[idx] 102 | if diff: 103 | ret = "%s!%s+%#x" % (mod,names[idx],diff) 104 | if no_offsets: 105 | ret = None 106 | else: 107 | ret = "%s!%s" % (mod,names[idx]) 108 | self._cache[loc] = ret 109 | return ret 110 | return "unknown" 111 | -------------------------------------------------------------------------------- /pdbparse/undecorate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | prefixes = "__imp__", "__imp_@", "__imp_", "_", "@", "\x7F" 4 | 5 | def undecorate(name): 6 | stack = -1 7 | conv = "UNDEFINED" 8 | orig_name = name 9 | for p in prefixes: 10 | if name.startswith(p): 11 | name = name[len(p):] 12 | break 13 | 14 | if name.startswith("@@") or name.startswith("?"): 15 | name = orig_name 16 | else: 17 | name_parts = name.split("@") 18 | if len(name_parts) == 2: 19 | try: 20 | stack = int(name_parts[1]) 21 | name = name_parts[0] 22 | except ValueError: 23 | stack = -1 24 | name = orig_name 25 | 26 | if len(p) == 1: 27 | if p == "_": 28 | conv = "FASTCALL" 29 | elif p == "@": 30 | if stack == -1: conv = "CDECL" 31 | else: conv = "STDCALL" 32 | return (name, stack, conv) 33 | -------------------------------------------------------------------------------- /pdbparse/undname.py: -------------------------------------------------------------------------------- 1 | import idc 2 | 3 | 4 | def undname(name): 5 | if name.startswith("?"): 6 | name = _demangle(name) 7 | elif name.startswith("_") or name.startswith("@"): 8 | name = name.rsplit('@',1)[0][1:] 9 | return name 10 | 11 | 12 | def _demangle(name, short=True): 13 | dtype = idc.INF_LONG_DN 14 | if short: 15 | dtype = idc.INF_SHORT_DN 16 | tmp = idc.Demangle(name, idc.GetLongPrm(dtype)) 17 | if tmp: 18 | name = tmp 19 | name = name.replace('__', '::') 20 | return name 21 | --------------------------------------------------------------------------------