Takes the offset to an MZ header locates any available pdb headers and extracts the guid age and pdb_name from them

def get_guid_from_mz(cls, context: interfaces.context.ContextInterface, layer_name: str,
                         offset: int) -> Optional[Tuple[str, int, str]]:
        """Takes the offset to an MZ header, locates any available pdb headers, and extracts the guid, age and pdb_name from them

        Args:
            context: The context on which to operate
            layer_name: The name of the (contiguous) layer within the context that contains the MZ file
            offset: The offset in the layer at which the MZ file begins

        Returns:
            A tuple of the guid, age and pdb_name, or None if no PDB record can be found
        """
        try:
            import pefile
        except ImportError:
            vollog.error("Get_guid_from_mz requires the following python module: pefile")
            return None

        layer = context.layers[layer_name]
        mz_sig = layer.read(offset, 2)

        # Check it is actually the MZ header
        if mz_sig != b"MZ":
            return None

        nt_header_start = ord(layer.read(offset + 0x3C, 1))
        optional_header_size = struct.unpack('<H', layer.read(offset + nt_header_start + 0x14, 2))[0]
        # Just enough to tell us the max size
        pe_header = layer.read(offset, nt_header_start + 0x16 + optional_header_size)
        pe_data = pefile.PE(data = pe_header)
        max_size = pe_data.OPTIONAL_HEADER.SizeOfImage

        # Proper data
        pe_header = layer.read(offset, max_size)
        pe_data = pefile.PE(data = pe_header)
        if not hasattr(pe_data, 'DIRECTORY_ENTRY_DEBUG') or not len(pe_data.DIRECTORY_ENTRY_DEBUG):
            return None

        # Extract the data
        debug_entry = pe_data.DIRECTORY_ENTRY_DEBUG[0].entry
        pdb_name = debug_entry.PdbFileName.decode("utf-8").strip('\x00')
        age = debug_entry.Age
        guid = "{:x}{:x}{:x}{}".format(debug_entry.Signature_Data1, debug_entry.Signature_Data2,
                                       debug_entry.Signature_Data3,
                                       binascii.hexlify(debug_entry.Signature_Data4).decode('utf-8'))
        return guid, age, pdb_name


Attempts to download the PDB file convert it to an ISF file and save it to one of the symbol locations .

def download_pdb_isf(cls,
                         context: interfaces.context.ContextInterface,
                         guid: str,
                         age: int,
                         pdb_name: str,
                         progress_callback: constants.ProgressCallback = None) -> None:
        """Attempts to download the PDB file, convert it to an ISF file and
        save it to one of the symbol locations."""
        # Check for writability
        filter_string = os.path.join(pdb_name, guid + "-" + str(age))
        for path in symbols.__path__:

            # Store any temporary files created by downloading PDB files
            tmp_files = []
            potential_output_filename = os.path.join(path, "windows", filter_string + ".json.xz")
            data_written = False
            try:
                os.makedirs(os.path.dirname(potential_output_filename), exist_ok = True)
                with lzma.open(potential_output_filename, "w") as of:
                    # Once we haven't thrown an error, do the computation
                    filename = pdbconv.PdbRetreiver().retreive_pdb(guid + str(age),
                                                                   file_name = pdb_name,
                                                                   progress_callback = progress_callback)
                    if filename:
                        tmp_files.append(filename)
                        location = "file:" + request.pathname2url(tmp_files[-1])
                        json_output = pdbconv.PdbReader(context, location, progress_callback).get_json()
                        of.write(bytes(json.dumps(json_output, indent = 2, sort_keys = True), 'utf-8'))
                        # After we've successfully written it out, record the fact so we don't clear it out
                        data_written = True
                    else:
                        vollog.warning("Symbol file could not be found on remote server" + (" " * 100))
                break
            except PermissionError:
                vollog.warning("Cannot write necessary symbol file, please check permissions on {}".format(
                    potential_output_filename))
                continue
            finally:
                # If something else failed, removed the symbol file so we don't pick it up in the future
                if not data_written and os.path.exists(potential_output_filename):
                    os.remove(potential_output_filename)
                # Clear out all the temporary file if we constructed one
                for filename in tmp_files:
                    try:
                        os.remove(filename)
                    except PermissionError:
                        vollog.warning("Temporary file could not be removed: {}".format(filename))
        else:
            vollog.warning("Cannot write downloaded symbols, please add the appropriate symbols"
                           " or add/modify a symbols directory that is writable")


Scans through layer_name at ctx looking for RSDS headers that indicate one of four common pdb kernel names ( as listed in self . pdb_names ) and returns the tuple ( GUID age pdb_name signature_offset mz_offset )

def pdbname_scan(cls,
                     ctx: interfaces.context.ContextInterface,
                     layer_name: str,
                     page_size: int,
                     pdb_names: List[bytes],
                     progress_callback: constants.ProgressCallback = None,
                     start: Optional[int] = None,
                     end: Optional[int] = None) -> Generator[Dict[str, Optional[Union[bytes, str, int]]], None, None]:
        """Scans through `layer_name` at `ctx` looking for RSDS headers that
        indicate one of four common pdb kernel names (as listed in
        `self.pdb_names`) and returns the tuple (GUID, age, pdb_name,
        signature_offset, mz_offset)

        .. note:: This is automagical and therefore not guaranteed to provide correct results.

        The UI should always provide the user an opportunity to specify the
        appropriate types and PDB values themselves
        """
        min_pfn = 0

        if start is None:
            start = ctx.layers[layer_name].minimum_address
        if end is None:
            end = ctx.layers[layer_name].maximum_address

        for (GUID, age, pdb_name,
             signature_offset) in ctx.layers[layer_name].scan(ctx,
                                                              PdbSignatureScanner(pdb_names),
                                                              progress_callback = progress_callback,
                                                              sections = [(start, end - start)]):
            mz_offset = None
            sig_pfn = signature_offset // page_size

            for i in range(sig_pfn, min_pfn, -1):
                if not ctx.layers[layer_name].is_valid(i * page_size, 2):
                    break

                data = ctx.layers[layer_name].read(i * page_size, 2)
                if data == b'MZ':
                    mz_offset = i * page_size
                    break
            min_pfn = sig_pfn

            yield {
                'GUID': GUID,
                'age': age,
                'pdb_name': str(pdb_name, "utf-8"),
                'signature_offset': signature_offset,
                'mz_offset': mz_offset
            }

def pdbname_scan ( cls , ctx : interfaces . context . ContextInterface , layer_name : str , page_size : int , pdb_names : List [ bytes ] , progress_callback : constants . ProgressCallback = None , start : Optional [ int ] = None , end : Optional [ int ] = None ) -> Generator [ Dict [ str , Optional [ Union [ bytes , str , int ] ] ] , None , None ] : min_pfn = 0 if start is None : start = ctx . layers [ layer_name ] . minimum_address if end is None : end = ctx . layers [ layer_name ] . maximum_address for ( GUID , age , pdb_name , signature_offset ) in ctx . layers [ layer_name ] . scan ( ctx , PdbSignatureScanner ( pdb_names ) , progress_callback = progress_callback , sections = [ ( start , end - start ) ] ) : mz_offset = None sig_pfn = signature_offset // page_size for i in range ( sig_pfn , min_pfn , - 1 ) : if not ctx . layers [ layer_name ] . is_valid ( i * page_size , 2 ) : break data = ctx . layers [ layer_name ] . read ( i * page_size , 2 ) if data == b'MZ' : mz_offset = i * page_size break min_pfn = sig_pfn yield { 'GUID' : GUID , 'age' : age , 'pdb_name' : str ( pdb_name , "utf-8" ) , 'signature_offset' : signature_offset , 'mz_offset' : mz_offset }


Loads a PDB file into a layer within the context and returns the name of the new layer .

def load_pdb_layer(cls, context: interfaces.context.ContextInterface,
                       location: str) -> Tuple[str, interfaces.context.ContextInterface]:
        """Loads a PDB file into a layer within the context and returns the
        name of the new layer.

        Note: the context may be changed by this method
        """
        physical_layer_name = context.layers.free_layer_name("FileLayer")
        physical_config_path = interfaces.configuration.path_join("pdbreader", physical_layer_name)

        # Create the file layer
        # This must be specific to get us started, setup the config and run
        new_context = context.clone()
        new_context.config[interfaces.configuration.path_join(physical_config_path, "location")] = location

        physical_layer = physical.FileLayer(new_context, physical_config_path, physical_layer_name)
        new_context.add_layer(physical_layer)

        # Add on the MSF format layer
        msf_layer_name = context.layers.free_layer_name("MSFLayer")
        msf_config_path = interfaces.configuration.path_join("pdbreader", msf_layer_name)
        new_context.config[interfaces.configuration.path_join(msf_config_path, "base_layer")] = physical_layer_name
        msf_layer = msf.PdbMultiStreamFormat(new_context, msf_config_path, msf_layer_name)
        new_context.add_layer(msf_layer)

        msf_layer.read_streams()

        return msf_layer_name, new_context


Read streams to populate the various internal components for a PDB table .

def read_necessary_streams(self):
        """Read streams to populate the various internal components for a PDB
        table."""
        if not self.metadata['windows'].get('pdb', None):
            self.read_pdb_info_stream()
        if not self.user_types:
            self.read_tpi_stream()
        if not self.symbols:
            self.read_symbol_stream()

def read_necessary_streams ( self ) : if not self . metadata [ 'windows' ] . get ( 'pdb' , None ) : self . read_pdb_info_stream ( ) if not self . user_types : self . read_tpi_stream ( ) if not self . symbols : self . read_symbol_stream ( )


def read_tpi_stream(self) -> None:
        """Reads the TPI type steam."""
        vollog.debug("Reading TPI")
        tpi_layer = self._context.layers.get(self._layer_name + "_stream2", None)
        if not tpi_layer:
            raise ValueError("No TPI stream available")
        module = self._context.module(module_name = tpi_layer.pdb_symbol_table, layer_name = tpi_layer.name, offset = 0)
        header = module.object(object_type = "TPI_HEADER", offset = 0)

        # Check the header
        if not (56 <= header.header_size < 1024):
            raise ValueError("TPI Stream Header size outside normal bounds")
        if header.index_min < 4096:
            raise ValueError("Minimum TPI index is 4096, found: {}".format(header.index_min))
        if header.index_max < header.index_min:
            raise ValueError("Maximum TPI index is smaller than minimum TPI index, found: {} < {} ".format(
                header.index_max, header.index_min))

        # Reset the state
        self.types = []
        type_references = {}  # type: Dict[str, int]

        offset = header.header_size
        # Ensure we use the same type everywhere
        length_type = "unsigned short"
        length_len = module.get_type(length_type).size
        type_index = 1
        while tpi_layer.maximum_address - offset > 0:
            self._progress_callback(offset * 100 / tpi_layer.maximum_address, "Reading TPI layer")
            length = module.object(object_type = length_type, offset = offset)
            if not isinstance(length, int):
                raise TypeError("Non-integer length provided")
            offset += length_len
            output, consumed = self.consume_type(module, offset, length)
            leaf_type, name, value = output
            for tag_type in ['unnamed', 'anonymous']:
                if name == '<{}-tag>'.format(tag_type) or name == '__{}'.format(tag_type):
                    name = '__{}_'.format(tag_type) + hex(len(self.types) + 0x1000)[2:]
            if name:
                type_references[name] = len(self.types)
            self.types.append((leaf_type, name, value))
            offset += length
            type_index += 1
            # Since types can only refer to earlier types, assigning the name at this point is fine

        if tpi_layer.maximum_address - offset != 0:
            raise ValueError("Type values did not fill the TPI stream correctly")

        self.process_types(type_references)


def read_dbi_stream(self) -> None:
        """Reads the DBI Stream."""
        vollog.debug("Reading DBI stream")
        dbi_layer = self._context.layers.get(self._layer_name + "_stream3", None)
        if not dbi_layer:
            raise ValueError("No DBI stream available")
        module = self._context.module(module_name = dbi_layer.pdb_symbol_table, layer_name = dbi_layer.name, offset = 0)
        self._dbiheader = module.object(object_type = "DBI_HEADER", offset = 0)

        if not self._dbiheader:
            raise ValueError("DBI Header could not be read")

        # Skip past sections we don't care about to get to the DBG header
        dbg_hdr_offset = (self._dbiheader.vol.size + self._dbiheader.module_size + self._dbiheader.secconSize +
                          self._dbiheader.secmapSize + self._dbiheader.filinfSize + self._dbiheader.tsmapSize +
                          self._dbiheader.ecinfoSize)
        self._dbidbgheader = module.object(object_type = "DBI_DBG_HEADER", offset = dbg_hdr_offset)

        self._sections = []
        self._omap_mapping = []

        if self._dbidbgheader.snSectionHdrOrig != -1:
            section_orig_layer_name = self._layer_name + "_stream" + str(self._dbidbgheader.snSectionHdrOrig)
            consumed, length = 0, self.context.layers[section_orig_layer_name].maximum_address
            while consumed < length:
                section = self.context.object(dbi_layer.pdb_symbol_table + constants.BANG + "IMAGE_SECTION_HEADER",
                                              offset = consumed,
                                              layer_name = section_orig_layer_name)
                self._sections.append(section)
                consumed += section.vol.size

            if self._dbidbgheader.snOmapFromSrc != -1:
                omap_layer_name = self._layer_name + "_stream" + str(self._dbidbgheader.snOmapFromSrc)
                length = self.context.layers[omap_layer_name].maximum_address
                data = self.context.layers[omap_layer_name].read(0, length)
                # For speed we don't use the framework to read this (usually sizeable) data
                for i in range(0, length, 8):
                    self._omap_mapping.append(
                        (int.from_bytes(data[i:i + 4],
                                        byteorder = 'little'), int.from_bytes(data[i + 4:i + 8], byteorder = 'little')))
        elif self._dbidbgheader.snSectionHdr != -1:
            section_layer_name = self._layer_name + "_stream" + str(self._dbidbgheader.snSectionHdr)
            consumed, length = 0, self.context.layers[section_layer_name].maximum_address
            while consumed < length:
                section = self.context.object(dbi_layer.pdb_symbol_table + constants.BANG + "IMAGE_SECTION_HEADER",
                                              offset = consumed,
                                              layer_name = section_layer_name)
                self._sections.append(section)
                consumed += section.vol.size


def read_symbol_stream(self):
        """Reads in the symbol stream."""
        self.symbols = {}

        if not self._dbiheader:
            self.read_dbi_stream()

        vollog.debug("Reading Symbols")

        symrec_layer = self._context.layers.get(self._layer_name + "_stream" + str(self._dbiheader.symrecStream), None)
        if not symrec_layer:
            raise ValueError("No SymRec stream available")
        module = self._context.module(module_name = symrec_layer.pdb_symbol_table,
                                      layer_name = symrec_layer.name,
                                      offset = 0)

        offset = 0
        max_address = symrec_layer.maximum_address

        while offset < max_address:
            self._progress_callback(offset * 100 / max_address, "Reading Symbol layer")
            sym = module.object(object_type = "GLOBAL_SYMBOL", offset = offset)
            leaf_type = module.object(object_type = "unsigned short", offset = sym.leaf_type.vol.offset)
            name = None
            address = None
            if sym.segment < len(self._sections):
                if leaf_type == 0x110e:
                    # v3 symbol (c-string)
                    name = self.parse_string(sym.name, False, sym.length - sym.vol.size + 2)
                    address = self._sections[sym.segment - 1].VirtualAddress + sym.offset
                elif leaf_type == 0x1009:
                    # v2 symbol (pascal-string)
                    name = self.parse_string(sym.name, True, sym.length - sym.vol.size + 2)
                    address = self._sections[sym.segment - 1].VirtualAddress + sym.offset
                else:
                    vollog.debug("Only v2 and v3 symbols are supported")
            if name:
                if self._omap_mapping:
                    address = self.omap_lookup(address)
                stripped_name = self.name_strip(name)
                self.symbols[stripped_name] = {"address": address}
                if name != self.name_strip(name):
                    self.symbols[stripped_name]["linkage_name"] = name
            offset += sym.length + 2

def
read_symbol_stream
(
self
)
:
self
.
symbols
=
{
}
if
not
self
.
_dbiheader
:
self
.
read_dbi_stream
(
)
vollog
.
debug
(
"Reading Symbols"
)
symrec_layer
=
self
.
_context
.
layers
.
get
(
self
.
_layer_name
+
"_stream"
+
str
(
self
.
_dbiheader
.
symrecStream
)
,
)
if
not
symrec_layer
:
raise
ValueError
(
"No SymRec stream available"
)
module
=
self
.
_context
.
module
(
module_name
=
symrec_layer
.
pdb_symbol_table
,
layer_name
=
symrec_layer
.
name
,
offset
=
0
)
offset
=
0
max_address
=
symrec_layer
.
maximum_address
while
offset
<
max_address
:
self
.
_progress_callback
(
offset
*
100
/
max_address
,
"Reading Symbol layer"
)
sym
=
module
.
object
(
object_type
=
"GLOBAL_SYMBOL"
,
offset
=
offset
)
leaf_type
=
module
.
object
(
object_type
=
"unsigned short"
,
offset
=
sym
.
leaf_type
.
vol
.
offset
)
name
=
address
=
if
sym
.
segment
<
len
(
self
.
_sections
)
:
if
leaf_type
==
0x110e
:
# v3 symbol (c-string)
name
=
self
.
parse_string
(
sym
.
name
,
False
,
sym
.
length
sym
.
vol
.
size
+
2
)
address
=
self
.
_sections
[
sym
.
segment
1
]
.
VirtualAddress
+
sym
.
offset
elif
leaf_type
==
0x1009
:
# v2 symbol (pascal-string)
name
=
self
.
parse_string
(
sym
.
name
,
True
,
sym
.
length
sym
.
vol
.
size
+
2
)
address
=
self
.
_sections
[
sym
.
segment
1
]
.
VirtualAddress
+
sym
.
offset
else
:
vollog
.
debug
(
"Only v2 and v3 symbols are supported"
)
if
name
:
if
self
.
_omap_mapping
:
address
=
self
.
omap_lookup
(
address
)
stripped_name
=
self
.
name_strip
(
name
)
self
.
symbols
[
stripped_name
]
=
{
"address"
:
address
}
if
name
!=
self
.
name_strip
(
name
)
:
self
.
symbols
[
stripped_name
]
[
"linkage_name"
]
=
name
offset
+=
sym
.
length
+
2


def read_pdb_info_stream(self):
        """Reads in the pdb information stream."""
        if not self._dbiheader:
            self.read_dbi_stream()

        vollog.debug("Reading PDB Info")
        pdb_info_layer = self._context.layers.get(self._layer_name + "_stream1", None)
        if not pdb_info_layer:
            raise ValueError("No PDB Info Stream available")
        module = self._context.module(module_name = pdb_info_layer.pdb_symbol_table,
                                      layer_name = pdb_info_layer.name,
                                      offset = 0)
        pdb_info = module.object(object_type = "PDB_INFORMATION", offset = 0)

        self.metadata['windows']['pdb'] = {
            "GUID": self.convert_bytes_to_guid(pdb_info.GUID),
            "age": pdb_info.age,
            "database": "ntkrnlmp.pdb",
            "machine_type": self._dbiheader.machine
        }

def
read_pdb_info_stream
(
self
)
:
if
not
self
.
_dbiheader
:
self
.
read_dbi_stream
(
)
vollog
.
debug
(
"Reading PDB Info"
)
pdb_info_layer
=
self
.
_context
.
layers
.
get
(
self
.
_layer_name
+
"_stream1"
,
)
if
not
pdb_info_layer
:
raise
ValueError
(
"No PDB Info Stream available"
)
module
=
self
.
_context
.
module
(
module_name
=
pdb_info_layer
.
pdb_symbol_table
,
layer_name
=
pdb_info_layer
.
name
,
offset
=
0
)
pdb_info
=
module
.
object
(
object_type
=
"PDB_INFORMATION"
,
offset
=
0
)
self
.
metadata
[
'windows'
]
[
'pdb'
]
=
{
"GUID"
:
self
.
convert_bytes_to_guid
(
pdb_info
.
GUID
)
,
"age"
:
pdb_info
.
age
,
"database"
:
"ntkrnlmp.pdb"
,
"machine_type"
:
self
.
_dbiheader
.
machine
}


Convert the bytes to the correct ordering for a GUID .

def convert_bytes_to_guid(self, original: bytes) -> str:
        """Convert the bytes to the correct ordering for a GUID."""
        orig_guid_list = [x for x in original]
        guid_list = []
        for i in [3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15]:
            guid_list.append(orig_guid_list[i])
        return str(binascii.hexlify(bytes(guid_list)), "latin-1").upper()

def convert_bytes_to_guid ( self , original : bytes ) -> str : orig_guid_list = [ x for x in original ] guid_list = [ ] for i in [ 3 , 2 , 1 , 0 , 5 , 4 , 7 , 6 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 ] : guid_list . append ( orig_guid_list [ i ] ) return str ( binascii . hexlify ( bytes ( guid_list ) ) , "latin-1" ) . upper ( )


Looks up an address using the omap mapping .

def omap_lookup(self, address):
        """Looks up an address using the omap mapping."""
        pos = bisect(self._omap_mapping, (address, -1))
        if self._omap_mapping[pos][0] > address:
            pos -= 1

        if not self._omap_mapping[pos][1]:
            return 0
        return self._omap_mapping[pos][1] + (address - self._omap_mapping[pos][0])

def omap_lookup ( self , address ) : pos = bisect ( self . _omap_mapping , ( address , - 1 ) ) if self . _omap_mapping [ pos ] [ 0 ] > address : pos -= 1 if not self . _omap_mapping [ pos ] [ 1 ] : return 0 return self . _omap_mapping [ pos ] [ 1 ] + ( address - self . _omap_mapping [ pos ] [ 0 ] )


Strips unnecessary components from the start of a symbol name .

def name_strip(self, name):
        """Strips unnecessary components from the start of a symbol name."""
        new_name = name

        if new_name[:1] in ["_", "@", "\u007F"]:
            new_name = new_name[1:]

        name_array = new_name.split("@")
        if len(name_array) == 2:
            if name_array[1].isnumeric() and name_array[0][0] != "?":
                new_name = name_array[0]
            else:
                new_name = name

        return new_name

def name_strip ( self , name ) : new_name = name if new_name [ : 1 ] in [ "_" , "@" , "\u007F" ] : new_name = new_name [ 1 : ] name_array = new_name . split ( "@" ) if len ( name_array ) == 2 : if name_array [ 1 ] . isnumeric ( ) and name_array [ 0 ] [ 0 ] != "?" : new_name = name_array [ 0 ] else : new_name = name return new_name


Returns the intermediate format JSON data from this pdb file .

def get_json(self):
        """Returns the intermediate format JSON data from this pdb file."""
        self.read_necessary_streams()

        # Set the time/datestamp for the output
        self.metadata["producer"] = {
            "datetime": datetime.datetime.now().isoformat(),
            "name": "volatility3",
            "version": constants.PACKAGE_VERSION
        }

        return {
            "user_types": self.user_types,
            "enums": self.enumerations,
            "base_types": self.bases,
            "symbols": self.symbols,
            "metadata": self.metadata,
        }

def
get_json
(
self
)
:
self
.
read_necessary_streams
(
)
# Set the time/datestamp for the output
self
.
metadata
[
"producer"
]
=
{
"datetime"
:
datetime
.
datetime
.
now
(
)
.
isoformat
(
)
,
"name"
:
"volatility3"
,
"version"
:
constants
.
PACKAGE_VERSION
}
return
{
"user_types"
:
self
.
user_types
,
"enums"
:
self
.
enumerations
,
"base_types"
:
self
.
bases
,
"symbols"
:
self
.
symbols
,
"metadata"
:
self
.
metadata
,
}


Takes a type index and returns appropriate dictionary .

def get_type_from_index(self, index: int) -> Union[List[Any], Dict[str, Any]]:
        """Takes a type index and returns appropriate dictionary."""
        if index < 0x1000:
            base_name, base = primatives[index & 0xff]
            self.bases[base_name] = base
            result = {"kind": "base", "name": base_name}  # type: Union[List[Dict[str, Any]], Dict[str, Any]]
            indirection = (index & 0xf00)
            if indirection:
                pointer_name, pointer_base = indirections[indirection]
                if self.bases.get('pointer', None) and self.bases['pointer'] == pointer_base:
                    result = {"kind": "pointer", "subtype": result}
                else:
                    self.bases[pointer_name] = pointer_base
                    result = {"kind": "pointer", "base": pointer_name, "subtype": result}
            return result
        else:
            leaf_type, name, value = self.types[index - 0x1000]
            result = {"kind": "struct", "name": name}
            if leaf_type in [leaf_type.LF_MODIFIER]:
                result = self.get_type_from_index(value.subtype_index)
            elif leaf_type in [leaf_type.LF_ARRAY, leaf_type.LF_ARRAY_ST, leaf_type.LF_STRIDED_ARRAY]:
                result = {
                    "count": ForwardArrayCount(value.size, value.element_type),
                    "kind": "array",
                    "subtype": self.get_type_from_index(value.element_type)
                }
            elif leaf_type in [leaf_type.LF_BITFIELD]:
                result = {
                    "kind": "bitfield",
                    "type": self.get_type_from_index(value.underlying_type),
                    "bit_length": value.length,
                    "bit_position": value.position
                }
            elif leaf_type in [leaf_type.LF_POINTER]:
                # Since we use the base['pointer'] to set the size for pointers, update it and check we don't get conflicts
                size = self.get_size_from_index(index)
                if self.bases.get("pointer", None) is None:
                    self.bases['pointer'] = {"endian": "little", "kind": "int", "signed": False, "size": size}
                else:
                    if size != self.bases['pointer']['size']:
                        raise ValueError("Native pointers with different sizes!")
                result = {"kind": "pointer", "subtype": self.get_type_from_index(value.subtype_index)}
            elif leaf_type in [leaf_type.LF_PROCEDURE]:
                return {"kind": "function"}
            elif leaf_type in [leaf_type.LF_UNION]:
                result = {"kind": "union", "name": name}
            elif leaf_type in [leaf_type.LF_ENUM]:
                result = {"kind": "enum", "name": name}
            elif leaf_type in [leaf_type.LF_FIELDLIST]:
                result = value
            elif not name:
                raise ValueError("No name for structure that should be named")
            return result


Returns the size of the structure based on the type index provided .

def get_size_from_index(self, index: int) -> int:
        """Returns the size of the structure based on the type index
        provided."""
        result = -1
        name = ''  # type: Optional[str]
        if index < 0x1000:
            if (index & 0xf00):
                _, base = indirections[index & 0xf00]
            else:
                _, base = primatives[index & 0xff]
            result = base['size']
        else:
            leaf_type, name, value = self.types[index - 0x1000]
            if leaf_type in [
                leaf_type.LF_UNION, leaf_type.LF_CLASS, leaf_type.LF_CLASS_ST, leaf_type.LF_STRUCTURE,
                leaf_type.LF_STRUCTURE_ST, leaf_type.LF_INTERFACE
            ]:
                if not value.properties.forward_reference:
                    result = value.size
            elif leaf_type in [leaf_type.LF_ARRAY, leaf_type.LF_ARRAY_ST, leaf_type.LF_STRIDED_ARRAY]:
                result = value.size
            elif leaf_type in [leaf_type.LF_MODIFIER, leaf_type.LF_ENUM, leaf_type.LF_ARGLIST]:
                result = self.get_size_from_index(value.subtype_index)
            elif leaf_type in [leaf_type.LF_MEMBER]:
                result = self.get_size_from_index(value.field_type)
            elif leaf_type in [leaf_type.LF_BITFIELD]:
                result = self.get_size_from_index(value.underlying_type)
            elif leaf_type in [leaf_type.LF_POINTER]:
                result = value.size
                if not result:
                    if value.pointer_type == 0x0a:
                        return 4
                    elif value.pointer_type == 0x0c:
                        return 8
                    else:
                        raise ValueError("Pointer size could not be determined")
            elif leaf_type in [leaf_type.LF_PROCEDURE]:
                raise ValueError("LF_PROCEDURE size could not be identified")
            else:
                raise ValueError("Unable to determine size of leaf_type {}".format(leaf_type.lookup()))
        if result <= 0:
            raise ValueError("Invalid size identified: {} ({})".format(index, name))
        return result


Reads the TPI and symbol streams to populate the reader s variables .

def process_types(self, type_references: Dict[str, int]) -> None:
        """Reads the TPI and symbol streams to populate the reader's
        variables."""

        self.bases = {}
        self.user_types = {}
        self.enumerations = {}

        max_len = len(self.types)
        for index in range(max_len):
            self._progress_callback(index * 100 / max_len, "Processing types")
            leaf_type, name, value = self.types[index]
            if leaf_type in [
                leaf_type.LF_CLASS, leaf_type.LF_CLASS_ST, leaf_type.LF_STRUCTURE, leaf_type.LF_STRUCTURE_ST,
                leaf_type.LF_INTERFACE
            ]:
                if not value.properties.forward_reference and name:
                    self.user_types[name] = {
                        "kind": "struct",
                        "size": value.size,
                        "fields": self.convert_fields(value.fields - 0x1000)
                    }
            elif leaf_type in [leaf_type.LF_UNION]:
                if not value.properties.forward_reference and name:
                    # Deal with UNION types
                    self.user_types[name] = {
                        "kind": "union",
                        "size": value.size,
                        "fields": self.convert_fields(value.fields - 0x1000)
                    }
            elif leaf_type in [leaf_type.LF_ENUM]:
                if not value.properties.forward_reference and name:
                    base = self.get_type_from_index(value.subtype_index)
                    if not isinstance(base, Dict):
                        raise ValueError("Invalid base type returned for Enumeration")
                    constants = self.get_type_from_index(value.fields)
                    if not isinstance(constants, list):
                        raise ValueError("Enumeration fields type not a list")
                    self.enumerations[name] = {
                        'base': base['name'],
                        'size': self.get_size_from_index(value.subtype_index),
                        'constants': dict([(name, enum.value) for _, name, enum in constants])
                    }

        # Re-run through for ForwardSizeReferences
        self.user_types = self.replace_forward_references(self.user_types, type_references)


Returns a ( leaf_type name object ) Tuple for a type and the number of bytes consumed .

def consume_type(
            self, module: interfaces.context.ModuleInterface, offset: int, length: int
    ) -> Tuple[Tuple[Optional[interfaces.objects.ObjectInterface], Optional[str], Union[
        None, List, interfaces.objects.ObjectInterface]], int]:
        """Returns a (leaf_type, name, object) Tuple for a type, and the number
        of bytes consumed."""
        leaf_type = self.context.object(module.get_enumeration("LEAF_TYPE"),
                                        layer_name = module._layer_name,
                                        offset = offset)
        consumed = leaf_type.vol.base_type.size
        remaining = length - consumed

        if leaf_type in [
            leaf_type.LF_CLASS, leaf_type.LF_CLASS_ST, leaf_type.LF_STRUCTURE, leaf_type.LF_STRUCTURE_ST,
            leaf_type.LF_INTERFACE
        ]:
            structure = module.object(object_type = "LF_STRUCTURE", offset = offset + consumed)
            name_offset = structure.name.vol.offset - structure.vol.offset
            name, value, excess = self.determine_extended_value(leaf_type, structure.size, module,
                                                                remaining - name_offset)
            structure.size = value
            structure.name = name
            consumed += remaining
            result = leaf_type, name, structure
        elif leaf_type in [leaf_type.LF_MEMBER, leaf_type.LF_MEMBER_ST]:
            member = module.object(object_type = "LF_MEMBER", offset = offset + consumed)
            name_offset = member.name.vol.offset - member.vol.offset
            name, value, excess = self.determine_extended_value(leaf_type, member.offset, module,
                                                                remaining - name_offset)
            member.offset = value
            member.name = name
            result = leaf_type, name, member
            consumed += member.vol.size + len(name) + 1 + excess
        elif leaf_type in [leaf_type.LF_ARRAY, leaf_type.LF_ARRAY_ST, leaf_type.LF_STRIDED_ARRAY]:
            array = module.object(object_type = "LF_ARRAY", offset = offset + consumed)
            name_offset = array.name.vol.offset - array.vol.offset
            name, value, excess = self.determine_extended_value(leaf_type, array.size, module, remaining - name_offset)
            array.size = value
            array.name = name
            result = leaf_type, name, array
            consumed += remaining
        elif leaf_type in [leaf_type.LF_ENUMERATE]:
            enum = module.object(object_type = 'LF_ENUMERATE', offset = offset + consumed)
            name_offset = enum.name.vol.offset - enum.vol.offset
            name, value, excess = self.determine_extended_value(leaf_type, enum.value, module, remaining - name_offset)
            enum.value = value
            enum.name = name
            result = leaf_type, name, enum
            consumed += enum.vol.size + len(name) + 1 + excess
        elif leaf_type in [leaf_type.LF_ARGLIST, leaf_type.LF_ENUM]:
            enum = module.object(object_type = "LF_ENUM", offset = offset + consumed)
            name_offset = enum.name.vol.offset - enum.vol.offset
            name = self.parse_string(enum.name, leaf_type < leaf_type.LF_ST_MAX, size = remaining - name_offset)
            enum.name = name
            result = leaf_type, name, enum
            consumed += remaining
        elif leaf_type in [leaf_type.LF_UNION]:
            union = module.object(object_type = "LF_UNION", offset = offset + consumed)
            name_offset = union.name.vol.offset - union.vol.offset
            name = self.parse_string(union.name, leaf_type < leaf_type.LF_ST_MAX, size = remaining - name_offset)
            result = leaf_type, name, union
            consumed += remaining
        elif leaf_type in [leaf_type.LF_MODIFIER, leaf_type.LF_POINTER, leaf_type.LF_PROCEDURE]:
            obj = module.object(object_type = leaf_type.lookup(), offset = offset + consumed)
            result = leaf_type, None, obj
            consumed += remaining
        elif leaf_type in [leaf_type.LF_FIELDLIST]:
            sub_length = remaining
            sub_offset = offset + consumed
            fields = []
            while length > consumed:
                subfield, sub_consumed = self.consume_type(module, sub_offset, sub_length)
                sub_consumed += self.consume_padding(module.layer_name, sub_offset + sub_consumed)
                sub_length -= sub_consumed
                sub_offset += sub_consumed
                consumed += sub_consumed
                fields.append(subfield)
            result = leaf_type, None, fields
        elif leaf_type in [leaf_type.LF_BITFIELD]:
            bitfield = module.object(object_type = "LF_BITFIELD", offset = offset + consumed)
            result = leaf_type, None, bitfield
            consumed += remaining
        else:
            raise TypeError("Unhandled leaf_type: {}".format(leaf_type))

        return result, consumed

def
consume_type
(
self
,
module
:
interfaces
.
context
.
ModuleInterface
,
offset
:
int
,
length
:
int
)
->
Tuple
[
Tuple
[
Optional
[
interfaces
.
objects
.
ObjectInterface
]
,
Optional
[
str
]
,
Union
[
,
List
,
interfaces
.
objects
.
ObjectInterface
]
]
,
int
]
:
leaf_type
=
self
.
context
.
object
(
module
.
get_enumeration
(
"LEAF_TYPE"
)
,
layer_name
=
module
.
_layer_name
,
offset
=
offset
)
consumed
=
leaf_type
.
vol
.
base_type
.
size
remaining
=
length
consumed
if
leaf_type
in
[
leaf_type
.
LF_CLASS
,
leaf_type
.
LF_CLASS_ST
,
leaf_type
.
LF_STRUCTURE
,
leaf_type
.
LF_STRUCTURE_ST
,
leaf_type
.
LF_INTERFACE
]
:
structure
=
module
.
object
(
object_type
=
"LF_STRUCTURE"
,
offset
=
offset
+
consumed
)
name_offset
=
structure
.
name
.
vol
.
offset
structure
.
vol
.
offset
name
,
value
,
excess
=
self
.
determine_extended_value
(
leaf_type
,
structure
.
size
,
module
,
remaining
name_offset
)
structure
.
size
=
value
structure
.
name
=
name
consumed
+=
remaining
result
=
leaf_type
,
name
,
structure
elif
leaf_type
in
[
leaf_type
.
LF_MEMBER
,
leaf_type
.
LF_MEMBER_ST
]
:
member
=
module
.
object
(
object_type
=
"LF_MEMBER"
,
offset
=
offset
+
consumed
)
name_offset
=
member
.
name
.
vol
.
offset
member
.
vol
.
offset
name
,
value
,
excess
=
self
.
determine_extended_value
(
leaf_type
,
member
.
offset
,
module
,
remaining
name_offset
)
member
.
offset
=
value
member
.
name
=
name
result
=
leaf_type
,
name
,
member
consumed
+=
member
.
vol
.
size
+
len
(
name
)
+
1
+
excess
elif
leaf_type
in
[
leaf_type
.
LF_ARRAY
,
leaf_type
.
LF_ARRAY_ST
,
leaf_type
.
LF_STRIDED_ARRAY
]
:
array
=
module
.
object
(
object_type
=
"LF_ARRAY"
,
offset
=
offset
+
consumed
)
name_offset
=
array
.
name
.
vol
.
offset
array
.
vol
.
offset
name
,
value
,
excess
=
self
.
determine_extended_value
(
leaf_type
,
array
.
size
,
module
,
remaining
name_offset
)
array
.
size
=
value
array
.
name
=
name
result
=
leaf_type
,
name
,
array
consumed
+=
remaining
elif
leaf_type
in
[
leaf_type
.
LF_ENUMERATE
]
:
enum
=
module
.
object
(
object_type
=
'LF_ENUMERATE'
,
offset
=
offset
+
consumed
)
name_offset
=
enum
.
name
.
vol
.
offset
enum
.
vol
.
offset
name
,
value
,
excess
=
self
.
determine_extended_value
(
leaf_type
,
enum
.
value
,
module
,
remaining
name_offset
)
enum
.
value
=
value
enum
.
name
=
name
result
=
leaf_type
,
name
,
enum
consumed
+=
enum
.
vol
.
size
+
len
(
name
)
+
1
+
excess
elif
leaf_type
in
[
leaf_type
.
LF_ARGLIST
,
leaf_type
.
LF_ENUM
]
:
enum
=
module
.
object
(
object_type
=
"LF_ENUM"
,
offset
=
offset
+
consumed
)
name_offset
=
enum
.
name
.
vol
.
offset
enum
.
vol
.
offset
name
=
self
.
parse_string
(
enum
.
name
,
leaf_type
<
leaf_type
.
LF_ST_MAX
,
size
=
remaining
name_offset
)
enum
.
name
=
name
result
=
leaf_type
,
name
,
enum
consumed
+=
remaining
elif
leaf_type
in
[
leaf_type
.
LF_UNION
]
:
union
=
module
.
object
(
object_type
=
"LF_UNION"
,
offset
=
offset
+
consumed
)
name_offset
=
union
.
name
.
vol
.
offset
union
.
vol
.
offset
name
=
self
.
parse_string
(
union
.
name
,
leaf_type
<
leaf_type
.
LF_ST_MAX
,
size
=
remaining
name_offset
)
result
=
leaf_type
,
name
,
union
consumed
+=
remaining
elif
leaf_type
in
[
leaf_type
.
LF_MODIFIER
,
leaf_type
.
LF_POINTER
,
leaf_type
.
LF_PROCEDURE
]
:
obj
=
module
.
object
(
object_type
=
leaf_type
.
lookup
(
)
,
offset
=
offset
+
consumed
)
result
=
leaf_type
,
,
obj
consumed
+=
remaining
elif
leaf_type
in
[
leaf_type
.
LF_FIELDLIST
]
:
sub_length
=
remaining
sub_offset
=
offset
+
consumed
fields
=
[
]
while
length
>
consumed
:
subfield
,
sub_consumed
=
self
.
consume_type
(
module
,
sub_offset
,
sub_length
)
sub_consumed
+=
self
.
consume_padding
(
module
.
layer_name
,
sub_offset
+
sub_consumed
)
sub_length
-=
sub_consumed
sub_offset
+=
sub_consumed
consumed
+=
sub_consumed
fields
.
append
(
subfield
)
result
=
leaf_type
,
,
fields
elif
leaf_type
in
[
leaf_type
.
LF_BITFIELD
]
:
bitfield
=
module
.
object
(
object_type
=
"LF_BITFIELD"
,
offset
=
offset
+
consumed
)
result
=
leaf_type
,
,
bitfield
consumed
+=
remaining
else
:
raise
TypeError
(
"Unhandled leaf_type: {}"
.
format
(
leaf_type
)
)
return
result
,
consumed


Returns the amount of padding used between fields .

def consume_padding(self, layer_name: str, offset: int) -> int:
        """Returns the amount of padding used between fields."""
        val = self.context.layers[layer_name].read(offset, 1)
        if not ((val[0] & 0xf0) == 0xf0):
            return 0
        return (int(val[0]) & 0x0f)

def consume_padding ( self , layer_name : str , offset : int ) -> int : val = self . context . layers [ layer_name ] . read ( offset , 1 ) if not ( ( val [ 0 ] & 0xf0 ) == 0xf0 ) : return 0 return ( int ( val [ 0 ] ) & 0x0f )


Converts a field list into a list of fields .

def convert_fields(self, fields: int) -> Dict[Optional[str], Dict[str, Any]]:
        """Converts a field list into a list of fields."""
        result = {}  # type: Dict[Optional[str], Dict[str, Any]]
        _, _, fields_struct = self.types[fields]
        if not isinstance(fields_struct, list):
            vollog.warning("Fields structure did not contain a list of fields")
            return result
        for field in fields_struct:
            _, name, member = field
            result[name] = {"offset": member.offset, "type": self.get_type_from_index(member.field_type)}
        return result


Finds all ForwardArrayCounts and calculates them once ForwardReferences have been resolved .

def replace_forward_references(self, types, type_references):
        """Finds all ForwardArrayCounts and calculates them once
        ForwardReferences have been resolved."""
        if isinstance(types, dict):
            for k, v in types.items():
                types[k] = self.replace_forward_references(v, type_references)
        elif isinstance(types, list):
            new_types = []
            for v in types:
                new_types.append(self.replace_forward_references(v, type_references))
            types = new_types
        elif isinstance(types, ForwardArrayCount):
            element_type = types.element_type
            # If we're a forward array count, we need to do the calculation now after all the types have been processed
            loop = True
            while loop:
                loop = False
                if element_type > 0x1000:
                    _, name, toplevel_type = self.types[element_type - 0x1000]
                    # If there's no name, the original size is probably fine as long as we're not indirect (LF_MODIFIER)
                    if not name and isinstance(
                            toplevel_type,
                            interfaces.objects.ObjectInterface) and toplevel_type.vol.type_name.endswith('LF_MODIFIER'):
                        # We have check they don't point to a forward reference, so we go round again with the subtype
                        element_type = toplevel_type.subtype_index
                        loop = True
                    elif name:
                        # If there is a name, look it up so we're not using a reference but the real thing
                        element_type = type_references[name] + 0x1000
            return types.size // self.get_size_from_index(element_type)
        return types


Consumes either a c - string or a pascal string depending on the leaf_type .

def parse_string(structure: interfaces.objects.ObjectInterface,
                     parse_as_pascal: bool = False,
                     size: int = 0) -> str:
        """Consumes either a c-string or a pascal string depending on the
        leaf_type."""
        if not parse_as_pascal:
            name = structure.cast("string", max_length = size, encoding = "latin-1")
        else:
            name = structure.cast("pascal_string")
            name = name.string.cast("string", max_length = name.length, encoding = "latin-1")
        return str(name)

def parse_string ( structure : interfaces . objects . ObjectInterface , parse_as_pascal : bool = False , size : int = 0 ) -> str : if not parse_as_pascal : name = structure . cast ( "string" , max_length = size , encoding = "latin-1" ) else : name = structure . cast ( "pascal_string" ) name = name . string . cast ( "string" , max_length = name . length , encoding = "latin-1" ) return str ( name )


Reads a value and potentially consumes more data to construct the value .

def determine_extended_value(self, leaf_type: interfaces.objects.ObjectInterface,
                                 value: interfaces.objects.ObjectInterface, module: interfaces.context.ModuleInterface,
                                 length: int) -> Tuple[str, interfaces.objects.ObjectInterface, int]:
        """Reads a value and potentially consumes more data to construct the
        value."""
        excess = 0
        if value >= leaf_type.LF_CHAR:
            sub_leaf_type = self.context.object(self.context.symbol_space.get_enumeration(leaf_type.vol.type_name),
                                                layer_name = leaf_type.vol.layer_name,
                                                offset = value.vol.offset)
            # Set the offset at just after the previous size type
            offset = value.vol.offset + value.vol.data_format.length
            if sub_leaf_type in [leaf_type.LF_CHAR]:
                value = module.object(object_type = 'char', offset = offset)
            elif sub_leaf_type in [leaf_type.LF_SHORT]:
                value = module.object(object_type = 'short', offset = offset)
            elif sub_leaf_type in [leaf_type.LF_USHORT]:
                value = module.object(object_type = 'unsigned short', offset = offset)
            elif sub_leaf_type in [leaf_type.LF_LONG]:
                value = module.object(object_type = 'long', offset = offset)
            elif sub_leaf_type in [leaf_type.LF_ULONG]:
                value = module.object(object_type = 'unsigned long', offset = offset)
            else:
                raise TypeError("Unexpected extended value type")
            excess = value.vol.data_format.length
            # Updated the consume/offset counters
        name = module.object(object_type = "string", offset = value.vol.offset + value.vol.data_format.length)
        name_str = self.parse_string(name, leaf_type < leaf_type.LF_ST_MAX, size = length - excess)
        return name_str, value, excess


def __call__(self, context: interfaces.context.ContextInterface, symbol_table: str) -> bool:
        """

        Args:
            context: The context that contains the symbol table named `symbol_table`
            symbol_table: Name of the symbol table within the context to distinguish the version of

        Returns:
            True if the symbol table is of the required version
        """

        try:
            pe_version = context.symbol_space[symbol_table].metadata.pe_version
            major, minor, revision, build = pe_version
            return self._version_check((major, minor, revision, build))
        except (AttributeError, ValueError, TypeError):
            vollog.log(constants.LOGLEVEL_VVV, "Windows PE version data is not available")

        # fall back to the backup method, if necessary
        for name, member, response in self._fallback_checks:
            if member is None:
                if (context.symbol_space.has_symbol(symbol_table + constants.BANG + name)
                        or context.symbol_space.has_type(symbol_table + constants.BANG + name)) != response:
                    return False
            else:
                try:
                    symbol_type = context.symbol_space.get_type(symbol_table + constants.BANG + name)
                    if symbol_type.has_member(member) != response:
                        return False
                except exceptions.SymbolError:
                    if not response:
                        return False

        return True


Traverse the VAD tree determining each underlying VAD node type by looking up the pool tag for the structure and then casting into a new object .

def traverse(self, visited = None, depth = 0):
        """Traverse the VAD tree, determining each underlying VAD node type by
        looking up the pool tag for the structure and then casting into a new
        object."""

        # TODO: this is an arbitrary limit chosen based on past observations
        if depth > 100:
            vollog.log(constants.LOGLEVEL_VVV, "Vad tree is too deep, something went wrong!")
            raise RuntimeError("Vad tree is too deep")

        if visited is None:
            visited = set()

        vad_address = self.vol.offset

        if vad_address in visited:
            vollog.log(constants.LOGLEVEL_VVV, "VAD node already seen!")
            return

        visited.add(vad_address)
        tag = self.get_tag()

        if tag in ["VadS", "VadF"]:
            target = "_MMVAD_SHORT"
        elif tag != None and tag.startswith("Vad"):
            target = "_MMVAD"
        elif depth == 0:
            # the root node at depth 0 is allowed to not have a tag
            # but we still want to continue and access its right & left child
            target = None
        else:
            # any node other than the root that doesn't have a recognized tag
            # is just garbage and we skip the node entirely
            vollog.log(constants.LOGLEVEL_VVV,
                       "Skipping VAD at {} depth {} with tag {}".format(self.vol.offset, depth, tag))
            return

        if target:
            vad_object = self.cast(target)
            yield vad_object

        try:
            for vad_node in self.get_left_child().dereference().traverse(visited, depth + 1):
                yield vad_node
        except exceptions.InvalidAddressException as excp:
            vollog.log(constants.LOGLEVEL_VVV, "Invalid address on LeftChild: {0:#x}".format(excp.invalid_address))

        try:
            for vad_node in self.get_right_child().dereference().traverse(visited, depth + 1):
                yield vad_node
        except exceptions.InvalidAddressException as excp:
            vollog.log(constants.LOGLEVEL_VVV, "Invalid address on RightChild: {0:#x}".format(excp.invalid_address))


def get_right_child(self):
        """Get the right child member."""

        if self.has_member("RightChild"):
            return self.RightChild

        elif self.has_member("Right"):
            return self.Right

        # this is for windows 8 and 10
        elif self.has_member("VadNode"):
            if self.VadNode.has_member("RightChild"):
                return self.VadNode.RightChild
            if self.VadNode.has_member("Right"):
                return self.VadNode.Right

        # also for windows 8 and 10
        elif self.has_member("Core"):
            if self.Core.has_member("VadNode"):
                if self.Core.VadNode.has_member("RightChild"):
                    return self.Core.VadNode.RightChild
                if self.Core.VadNode.has_member("Right"):
                    return self.Core.VadNode.Right

        raise AttributeError("Unable to find the right child member")

def
get_right_child
(
self
)
:
if
self
.
has_member
(
"RightChild"
)
:
return
self
.
RightChild
elif
self
.
has_member
(
"Right"
)
:
return
self
.
Right
# this is for windows 8 and 10
elif
self
.
has_member
(
"VadNode"
)
:
if
self
.
VadNode
.
has_member
(
"RightChild"
)
:
return
self
.
VadNode
.
RightChild
if
self
.
VadNode
.
has_member
(
"Right"
)
:
return
self
.
VadNode
.
Right
# also for windows 8 and 10
elif
self
.
has_member
(
"Core"
)
:
if
self
.
Core
.
has_member
(
"VadNode"
)
:
if
self
.
Core
.
VadNode
.
has_member
(
"RightChild"
)
:
return
self
.
Core
.
VadNode
.
RightChild
if
self
.
Core
.
VadNode
.
has_member
(
"Right"
)
:
return
self
.
Core
.
VadNode
.
Right
raise
AttributeError
(
"Unable to find the right child member"
)


def get_left_child(self):
        """Get the left child member."""

        if self.has_member("LeftChild"):
            return self.LeftChild

        elif self.has_member("Left"):
            return self.Left

        # this is for windows 8 and 10
        elif self.has_member("VadNode"):
            if self.VadNode.has_member("LeftChild"):
                return self.VadNode.LeftChild
            if self.VadNode.has_member("Left"):
                return self.VadNode.Left

        # also for windows 8 and 10
        elif self.has_member("Core"):
            if self.Core.has_member("VadNode"):
                if self.Core.VadNode.has_member("LeftChild"):
                    return self.Core.VadNode.LeftChild
                if self.Core.VadNode.has_member("Left"):
                    return self.Core.VadNode.Left

        raise AttributeError("Unable to find the left child member")

def
get_left_child
(
self
)
:
if
self
.
has_member
(
"LeftChild"
)
:
return
self
.
LeftChild
elif
self
.
has_member
(
"Left"
)
:
return
self
.
Left
# this is for windows 8 and 10
elif
self
.
has_member
(
"VadNode"
)
:
if
self
.
VadNode
.
has_member
(
"LeftChild"
)
:
return
self
.
VadNode
.
LeftChild
if
self
.
VadNode
.
has_member
(
"Left"
)
:
return
self
.
VadNode
.
Left
# also for windows 8 and 10
elif
self
.
has_member
(
"Core"
)
:
if
self
.
Core
.
has_member
(
"VadNode"
)
:
if
self
.
Core
.
VadNode
.
has_member
(
"LeftChild"
)
:
return
self
.
Core
.
VadNode
.
LeftChild
if
self
.
Core
.
VadNode
.
has_member
(
"Left"
)
:
return
self
.
Core
.
VadNode
.
Left
raise
AttributeError
(
"Unable to find the left child member"
)


def get_parent(self):
        """Get the VAD's parent member."""

        # this is for xp and 2003
        if self.has_member("Parent"):
            return self.Parent

        # this is for vista through windows 7
        elif self.has_member("u1") and self.u1.has_member("Parent"):
            return self.u1.Parent & ~0x3

        # this is for windows 8 and 10
        elif self.has_member("VadNode"):

            if self.VadNode.has_member("u1"):
                return self.VadNode.u1.Parent & ~0x3

            elif self.VadNode.has_member("ParentValue"):
                return self.VadNode.ParentValue & ~0x3

        # also for windows 8 and 10
        elif self.has_member("Core"):

            if self.Core.VadNode.has_member("u1"):
                return self.Core.VadNode.u1.Parent & ~0x3

            elif self.Core.VadNode.has_member("ParentValue"):
                return self.Core.VadNode.ParentValue & ~0x3

        raise AttributeError("Unable to find the parent member")

def
get_parent
(
self
)
:
# this is for xp and 2003
if
self
.
has_member
(
"Parent"
)
:
return
self
.
Parent
# this is for vista through windows 7
elif
self
.
has_member
(
"u1"
)
and
self
.
u1
.
has_member
(
"Parent"
)
:
return
self
.
u1
.
Parent
&
~
0x3
# this is for windows 8 and 10
elif
self
.
has_member
(
"VadNode"
)
:
if
self
.
VadNode
.
has_member
(
"u1"
)
:
return
self
.
VadNode
.
u1
.
Parent
&
~
0x3
elif
self
.
VadNode
.
has_member
(
"ParentValue"
)
:
return
self
.
VadNode
.
ParentValue
&
~
0x3
# also for windows 8 and 10
elif
self
.
has_member
(
"Core"
)
:
if
self
.
Core
.
VadNode
.
has_member
(
"u1"
)
:
return
self
.
Core
.
VadNode
.
u1
.
Parent
&
~
0x3
elif
self
.
Core
.
VadNode
.
has_member
(
"ParentValue"
)
:
return
self
.
Core
.
VadNode
.
ParentValue
&
~
0x3
raise
AttributeError
(
"Unable to find the parent member"
)


Get the VAD s starting virtual address .

def get_start(self):
        """Get the VAD's starting virtual address."""

        if self.has_member("StartingVpn"):

            if self.has_member("StartingVpnHigh"):
                return (self.StartingVpn << 12) | (self.StartingVpnHigh << 44)
            else:
                return self.StartingVpn << 12

        elif self.has_member("Core"):

            if self.Core.has_member("StartingVpnHigh"):
                return (self.Core.StartingVpn << 12) | (self.Core.StartingVpnHigh << 44)
            else:
                return self.Core.StartingVpn << 12

        raise AttributeError("Unable to find the starting VPN member")


def get_end(self):
        """Get the VAD's ending virtual address."""

        if self.has_member("EndingVpn"):

            if self.has_member("EndingVpnHigh"):
                return (((self.EndingVpn + 1) << 12) | (self.EndingVpnHigh << 44)) - 1
            else:
                return ((self.EndingVpn + 1) << 12) - 1

        elif self.has_member("Core"):
            if self.Core.has_member("EndingVpnHigh"):
                return (((self.Core.EndingVpn + 1) << 12) | (self.Core.EndingVpnHigh << 44)) - 1
            else:
                return ((self.Core.EndingVpn + 1) << 12) - 1

        raise AttributeError("Unable to find the ending VPN member")

def
get_end
(
self
)
:
if
self
.
has_member
(
"EndingVpn"
)
:
if
self
.
has_member
(
"EndingVpnHigh"
)
:
return
(
(
(
self
.
EndingVpn
+
1
)
<<
12
)
|
(
self
.
EndingVpnHigh
<<
44
)
)
1
else
:
return
(
(
self
.
EndingVpn
+
1
)
<<
12
)
1
elif
self
.
has_member
(
"Core"
)
:
if
self
.
Core
.
has_member
(
"EndingVpnHigh"
)
:
return
(
(
(
self
.
Core
.
EndingVpn
+
1
)
<<
12
)
|
(
self
.
Core
.
EndingVpnHigh
<<
44
)
)
1
else
:
return
(
(
self
.
Core
.
EndingVpn
+
1
)
<<
12
)
1
raise
AttributeError
(
"Unable to find the ending VPN member"
)


Get the VAD s commit charge ( number of committed pages )

def get_commit_charge(self):
        """Get the VAD's commit charge (number of committed pages)"""

        if self.has_member("u1") and self.u1.has_member("VadFlags1"):
            return self.u1.VadFlags1.CommitCharge

        elif self.has_member("u") and self.u.has_member("VadFlags"):
            return self.u.VadFlags.CommitCharge

        elif self.has_member("Core"):
            return self.Core.u1.VadFlags1.CommitCharge

        raise AttributeError("Unable to find the commit charge member")


def get_private_memory(self):
        """Get the VAD's private memory setting."""

        if self.has_member("u1") and self.u1.has_member("VadFlags1") and self.u1.VadFlags1.has_member("PrivateMemory"):
            return self.u1.VadFlags1.PrivateMemory

        elif self.has_member("u") and self.u.has_member("VadFlags") and self.u.VadFlags.has_member("PrivateMemory"):
            return self.u.VadFlags.PrivateMemory

        elif self.has_member("Core"):
            if (self.Core.has_member("u1") and self.Core.u1.has_member("VadFlags1")
                    and self.Core.u1.VadFlags1.has_member("PrivateMemory")):
                return self.Core.u1.VadFlags1.PrivateMemory

            elif (self.Core.has_member("u") and self.Core.u.has_member("VadFlags")
                  and self.Core.u.VadFlags.has_member("PrivateMemory")):
                return self.Core.u.VadFlags.PrivateMemory

        raise AttributeError("Unable to find the private memory member")


Get the VAD s protection constants as a string .

def get_protection(self, protect_values, winnt_protections):
        """Get the VAD's protection constants as a string."""

        protect = None

        if self.has_member("u"):
            protect = self.u.VadFlags.Protection

        elif self.has_member("Core"):
            protect = self.Core.u.VadFlags.Protection

        try:
            value = protect_values[protect]
        except IndexError:
            value = 0

        names = []

        for name, mask in winnt_protections.items():
            if value & mask != 0:
                names.append(name)

        return "|".join(names)

def get_protection ( self , protect_values , winnt_protections ) : protect = None if self . has_member ( "u" ) : protect = self . u . VadFlags . Protection elif self . has_member ( "Core" ) : protect = self . Core . u . VadFlags . Protection try : value = protect_values [ protect ] except IndexError : value = 0 names = [ ] for name , mask in winnt_protections . items ( ) : if value & mask != 0 : names . append ( name ) return "|" . join ( names )


Only long ( er ) vads have mapped files .

def get_file_name(self):
        """Only long(er) vads have mapped files."""
        return renderers.NotApplicableValue()

def get_file_name ( self ) : return renderers . NotApplicableValue ( )


Get the name of the file mapped into the memory range ( if any )

def get_file_name(self):
        """Get the name of the file mapped into the memory range (if any)"""

        file_name = renderers.NotApplicableValue()

        try:
            # this is for xp and 2003
            if self.has_member("ControlArea"):
                file_name = self.ControlArea.FilePointer.FileName.get_string()

            # this is for vista through windows 7
            else:
                file_name = self.Subsection.ControlArea.FilePointer.dereference().cast(
                    "_FILE_OBJECT").FileName.get_string()

        except exceptions.InvalidAddressException:
            pass

        return file_name

def
get_file_name
(
self
)
:
file_name
=
renderers
.
NotApplicableValue
(
)
try
:
# this is for xp and 2003
if
self
.
has_member
(
"ControlArea"
)
:
file_name
=
self
.
ControlArea
.
FilePointer
.
FileName
.
get_string
(
)
# this is for vista through windows 7
else
:
file_name
=
self
.
Subsection
.
ControlArea
.
FilePointer
.
dereference
(
)
.
cast
(
"_FILE_OBJECT"
)
.
FileName
.
get_string
(
)
except
exceptions
.
InvalidAddressException
:
pass
return
file_name


def is_valid(self) -> bool:
        """Determine if the object is valid."""
        return True

def is_valid ( self ) -> bool : return True


def is_valid(self) -> bool:
        """Determine if the object is valid."""
        return self.FileName.Length > 0 and self._context.layers[self.FileName.Buffer.vol.native_layer_name].is_valid(
            self.FileName.Buffer)

def is_valid ( self ) -> bool : return self . FileName . Length > 0 and self . _context . layers [ self . FileName . Buffer . vol . native_layer_name ] . is_valid ( self . FileName . Buffer )


Get the object s name from the object header .

def get_name(self) -> str:
        """Get the object's name from the object header."""
        header = self.get_object_header()
        return header.NameInfo.Name.String

def get_name ( self ) -> str : header = self . get_object_header ( ) return header . NameInfo . Name . String


Return the EPROCESS that owns this thread .

def owning_process(self, kernel_layer: str = None) -> interfaces.objects.ObjectInterface:
        """Return the EPROCESS that owns this thread."""
        return self.ThreadsProcess.dereference(kernel_layer)

def owning_process ( self , kernel_layer : str = None ) -> interfaces . objects . ObjectInterface : return self . ThreadsProcess . dereference ( kernel_layer )


def is_valid(self) -> bool:
        """Determine if the object is valid."""

        try:
            name = objects.utility.array_to_string(self.ImageFileName)
            if not name or len(name) == 0 or name[0] == "\x00":
                return False

            # The System/PID 4 process has no create time
            if not (str(name) == "System" and self.UniqueProcessId == 4):
                if self.CreateTime.QuadPart == 0:
                    return False

                ctime = self.get_create_time()
                if not isinstance(ctime, datetime.datetime):
                    return False

                if not (1998 < ctime.year < 2030):
                    return False

            # NT pids are divisible by 4
            if self.UniqueProcessId % 4 != 0:
                return False

            # check for all 0s besides the PCID entries
            if isinstance(self.Pcb.DirectoryTableBase, objects.Array):
                dtb = self.Pcb.DirectoryTableBase.cast("pointer")
            else:
                dtb = self.Pcb.DirectoryTableBase

            if dtb == 0:
                return False

            # check for all 0s besides the PCID entries
            if dtb & ~0xfff == 0:
                return False

            ## TODO: we can also add the thread Flink and Blink tests if necessary

        except exceptions.InvalidAddressException:
            return False

        return True


Constructs a new layer based on the process s DirectoryTableBase .

def add_process_layer(self, config_prefix: str = None, preferred_name: str = None):
        """Constructs a new layer based on the process's DirectoryTableBase."""

        parent_layer = self._context.layers[self.vol.layer_name]

        if not isinstance(parent_layer, intel.Intel):
            # We can't get bits_per_register unless we're an intel space (since that's not defined at the higher layer)
            raise TypeError("Parent layer is not a translation layer, unable to construct process layer")

        # Presumably for 64-bit systems, the DTB is defined as an array, rather than an unsigned long long
        dtb = 0  # type: int
        if isinstance(self.Pcb.DirectoryTableBase, objects.Array):
            dtb = self.Pcb.DirectoryTableBase.cast("unsigned long long")
        else:
            dtb = self.Pcb.DirectoryTableBase
        dtb = dtb & ((1 << parent_layer.bits_per_register) - 1)

        if preferred_name is None:
            preferred_name = self.vol.layer_name + "_Process{}".format(self.UniqueProcessId)

        # Add the constructed layer and return the name
        return self._add_process_layer(self._context, dtb, config_prefix, preferred_name)


def get_peb(self) -> interfaces.objects.ObjectInterface:
        """Constructs a PEB object"""
        if constants.BANG not in self.vol.type_name:
            raise ValueError("Invalid symbol table name syntax (no {} found)".format(constants.BANG))

        # add_process_layer can raise InvalidAddressException.
        # if that happens, we let the exception propagate upwards
        proc_layer_name = self.add_process_layer()

        proc_layer = self._context.layers[proc_layer_name]
        if not proc_layer.is_valid(self.Peb):
            raise exceptions.InvalidAddressException(proc_layer_name, self.Peb,
                                                     "Invalid address at {:0x}".format(self.Peb))

        sym_table = self.vol.type_name.split(constants.BANG)[0]
        peb = self._context.object("{}{}_PEB".format(sym_table, constants.BANG),
                                   layer_name = proc_layer_name,
                                   offset = self.Peb)
        return peb


Generator for DLLs in the order that they were loaded .

def load_order_modules(self) -> Iterable[interfaces.objects.ObjectInterface]:
        """Generator for DLLs in the order that they were loaded."""

        try:
            peb = self.get_peb()
            for entry in peb.Ldr.InLoadOrderModuleList.to_list(
                    "{}{}_LDR_DATA_TABLE_ENTRY".format(self.get_symbol_table_name(), constants.BANG),
                    "InLoadOrderLinks"):
                yield entry
        except exceptions.InvalidAddressException:
            return

def load_order_modules ( self ) -> Iterable [ interfaces . objects . ObjectInterface ] : try : peb = self . get_peb ( ) for entry in peb . Ldr . InLoadOrderModuleList . to_list ( "{}{}_LDR_DATA_TABLE_ENTRY" . format ( self . get_symbol_table_name ( ) , constants . BANG ) , "InLoadOrderLinks" ) : yield entry except exceptions . InvalidAddressException : return


def init_order_modules(self) -> Iterable[interfaces.objects.ObjectInterface]:
        """Generator for DLLs in the order that they were initialized"""

        try:
            peb = self.get_peb()
            for entry in peb.Ldr.InInitializationOrderModuleList.to_list(
                    "{}{}_LDR_DATA_TABLE_ENTRY".format(self.get_symbol_table_name(), constants.BANG),
                    "InInitializationOrderLinks"):
                yield entry
        except exceptions.InvalidAddressException:
            return

def init_order_modules ( self ) -> Iterable [ interfaces . objects . ObjectInterface ] : try : peb = self . get_peb ( ) for entry in peb . Ldr . InInitializationOrderModuleList . to_list ( "{}{}_LDR_DATA_TABLE_ENTRY" . format ( self . get_symbol_table_name ( ) , constants . BANG ) , "InInitializationOrderLinks" ) : yield entry except exceptions . InvalidAddressException : return


def mem_order_modules(self) -> Iterable[interfaces.objects.ObjectInterface]:
        """Generator for DLLs in the order that they appear in memory"""

        try:
            peb = self.get_peb()
            for entry in peb.Ldr.InMemoryOrderModuleList.to_list(
                    "{}{}_LDR_DATA_TABLE_ENTRY".format(self.get_symbol_table_name(), constants.BANG),
                    "InMemoryOrderLinks"):
                yield entry
        except exceptions.InvalidAddressException:
            return

def mem_order_modules ( self ) -> Iterable [ interfaces . objects . ObjectInterface ] : try : peb = self . get_peb ( ) for entry in peb . Ldr . InMemoryOrderModuleList . to_list ( "{}{}_LDR_DATA_TABLE_ENTRY" . format ( self . get_symbol_table_name ( ) , constants . BANG ) , "InMemoryOrderLinks" ) : yield entry except exceptions . InvalidAddressException : return


def environment_variables(self):
        """Generator for environment variables.

        The PEB points to our env block - a series of null-terminated
        unicode strings. Each string cannot be more than 0x7FFF chars.
        End of the list is a quad-null.
        """
        context = self._context
        process_space = self.add_process_layer()

        try:
            block = self.get_peb().ProcessParameters.Environment
            try:
                block_size = self.get_peb().ProcessParameters.EnvironmentSize
            except AttributeError:  # Windows XP
                block_size = self.get_peb().ProcessParameters.Length
            envars = context.layers[process_space].read(block, block_size).decode("utf-16-le",
                                                                                errors = 'replace').split('\x00')[:-1]
        except exceptions.InvalidAddressException:
            return renderers.UnreadableValue()

        for envar in envars:
            split_index = envar.find('=')
            env = envar[:split_index]
            var = envar[split_index + 1:]

            # Exlude parse problem with some types of env
            if env and var:
                yield env, var


Returns an iterator of the entries in the list .

def to_list(self,
                symbol_type: str,
                member: str,
                forward: bool = True,
                sentinel: bool = True,
                layer: Optional[str] = None) -> Iterator[interfaces.objects.ObjectInterface]:
        """Returns an iterator of the entries in the list."""

        layer = layer or self.vol.layer_name

        relative_offset = self._context.symbol_space.get_type(symbol_type).relative_child_offset(member)

        direction = 'Blink'
        if forward:
            direction = 'Flink'

        trans_layer = self._context.layers[layer]

        try:
            trans_layer.is_valid(self.vol.offset)
            link = getattr(self, direction).dereference()
        except exceptions.InvalidAddressException:
            return

        if not sentinel:
            yield self._context.object(symbol_type,
                                       layer,
                                       offset = self.vol.offset - relative_offset,
                                       native_layer_name = layer or self.vol.native_layer_name)

        seen = {self.vol.offset}
        while link.vol.offset not in seen:
            obj_offset = link.vol.offset - relative_offset

            try:
                trans_layer.is_valid(obj_offset)
            except exceptions.InvalidAddressException:
                return

            obj = self._context.object(symbol_type,
                                       layer,
                                       offset = obj_offset,
                                       native_layer_name = layer or self.vol.native_layer_name)
            yield obj

            seen.add(link.vol.offset)

            try:
                link = getattr(link, direction).dereference()
            except exceptions.InvalidAddressException:
                return

def to_list ( self , symbol_type : str , member : str , forward : bool = True , sentinel : bool = True , layer : Optional [ str ] = None ) -> Iterator [ interfaces . objects . ObjectInterface ] : layer = layer or self . vol . layer_name relative_offset = self . _context . symbol_space . get_type ( symbol_type ) . relative_child_offset ( member ) direction = 'Blink' if forward : direction = 'Flink' trans_layer = self . _context . layers [ layer ] try : trans_layer . is_valid ( self . vol . offset ) link = getattr ( self , direction ) . dereference ( ) except exceptions . InvalidAddressException : return if not sentinel : yield self . _context . object ( symbol_type , layer , offset = self . vol . offset - relative_offset , native_layer_name = layer or self . vol . native_layer_name ) seen = { self . vol . offset } while link . vol . offset not in seen : obj_offset = link . vol . offset - relative_offset try : trans_layer . is_valid ( obj_offset ) except exceptions . InvalidAddressException : return obj = self . _context . object ( symbol_type , layer , offset = obj_offset , native_layer_name = layer or self . vol . native_layer_name ) yield obj seen . add ( link . vol . offset ) try : link = getattr ( link , direction ) . dereference ( ) except exceptions . InvalidAddressException : return


Yield a sid for the current token object .

def get_sids(self) -> Iterable[str]:
        """Yield a sid for the current token object."""

        if self.UserAndGroupCount < 0xFFFF:
            layer_name = self.vol.layer_name
            kvo = self._context.layers[layer_name].config["kernel_virtual_offset"]
            symbol_table = self.get_symbol_table_name()
            ntkrnlmp = self._context.module(symbol_table, layer_name = layer_name, offset = kvo)
            UserAndGroups = ntkrnlmp.object(object_type = "array",
                                            offset = self.UserAndGroups.dereference().vol.get("offset") - kvo,
                                            subtype = ntkrnlmp.get_type("_SID_AND_ATTRIBUTES"),
                                            count = self.UserAndGroupCount)
            for sid_and_attr in UserAndGroups:
                try:
                    sid = sid_and_attr.Sid.dereference().cast("_SID")
                    # catch invalid pointers (UserAndGroupCount is too high)
                    if sid is None:
                        return
                    # this mimics the windows API IsValidSid
                    if sid.Revision & 0xF != 1 or sid.SubAuthorityCount > 15:
                        return
                    id_auth = ""
                    for i in sid.IdentifierAuthority.Value:
                        id_auth = i
                    SubAuthority = ntkrnlmp.object(object_type = "array",
                                                   offset = sid.SubAuthority.vol.offset - kvo,
                                                   subtype = ntkrnlmp.get_type("unsigned long"),
                                                   count = int(sid.SubAuthorityCount))
                    yield "S-" + "-".join(str(i) for i in (sid.Revision, id_auth) + tuple(SubAuthority))
                except exceptions.InvalidAddressException:
                    vollog.log(constants.LOGLEVEL_VVVV, "InvalidAddressException while parsing for token sid")


Return a list of privileges for the current token object .

def privileges(self):
        """Return a list of privileges for the current token object."""

        try:
            for priv_index in range(64):
                yield (priv_index, bool(self.Privileges.Present & (2 ** priv_index)),
                       bool(self.Privileges.Enabled & (2 ** priv_index)),
                       bool(self.Privileges.EnabledByDefault & (2 ** priv_index)))
        except AttributeError:  # Windows XP
            if self.PrivilegeCount < 1024:
                # This is a pointer to an array of _LUID_AND_ATTRIBUTES
                for luid in self.Privileges.dereference().cast(
                        "array",
                        count = self.PrivilegeCount,
                        subtype = self._context.symbol_space[self.get_symbol_table_name()].get_type(
                            "_LUID_AND_ATTRIBUTES")):
                    # The Attributes member is a flag
                    enabled = luid.Attributes & 2 != 0
                    default = luid.Attributes & 1 != 0
                    yield luid.Luid.LowPart, True, enabled, default
            else:
                vollog.log(constants.LOGLEVEL_VVVV, "Broken Token Privileges.")


Carve out the NT header from this DOS header . This reflects on the PE file s Machine type to create a 32 - or 64 - bit NT header structure .

def get_nt_header(self) -> interfaces.objects.ObjectInterface:
        """Carve out the NT header from this DOS header. This reflects on the
        PE file's Machine type to create a 32- or 64-bit NT header structure.

        Returns:
            <_IMAGE_NT_HEADERS> or <_IMAGE_NT_HEADERS64> instance
        """

        if self.e_magic != 0x5a4d:
            raise ValueError("e_magic {0:04X} is not a valid DOS signature.".format(self.e_magic))

        layer_name = self.vol.layer_name
        symbol_table_name = self.get_symbol_table_name()

        nt_header = self._context.object(symbol_table_name + constants.BANG + "_IMAGE_NT_HEADERS",
                                         layer_name = layer_name,
                                         offset = self.vol.offset + self.e_lfanew)

        if nt_header.Signature != 0x4550:
            raise ValueError("NT header signature {0:04X} is not a valid".format(nt_header.Signature))

        # this checks if we need a PE32+ header
        if nt_header.FileHeader.Machine == 34404:
            nt_header = nt_header.cast("_IMAGE_NT_HEADERS64")

        return nt_header


Replaces a member in an _IMAGE_SECTION_HEADER structure .

def replace_header_field(self, sect: interfaces.objects.ObjectInterface, header: bytes,
                             item: interfaces.objects.ObjectInterface, value: int) -> bytes:
        """Replaces a member in an _IMAGE_SECTION_HEADER structure.

        Args:
            sect: the section instance
            header: raw data for the section
            item: the member of the section to replace
            value: new value for the member

        Returns:
            The raw data with the replaced header field
        """

        member_size = self._context.symbol_space.get_type(item.vol.type_name).size
        start = item.vol.offset - sect.vol.offset
        newval = objects.convert_value_to_data(value, int, item.vol.data_format)
        result = header[:start] + newval + header[start + member_size:]
        return result

def replace_header_field ( self , sect : interfaces . objects . ObjectInterface , header : bytes , item : interfaces . objects . ObjectInterface , value : int ) -> bytes : member_size = self . _context . symbol_space . get_type ( item . vol . type_name ) . size start = item . vol . offset - sect . vol . offset newval = objects . convert_value_to_data ( value , int , item . vol . data_format ) result = header [ : start ] + newval + header [ start + member_size : ] return result


Fix the _OPTIONAL_HEADER . ImageBase value ( which is either an unsigned long for 32 - bit PE s or unsigned long long for 64 - bit PE s ) to match the address where the PE file was carved out of memory .

def fix_image_base(self, raw_data: bytes, nt_header: interfaces.objects.ObjectInterface) -> bytes:
        """Fix the _OPTIONAL_HEADER.ImageBase value (which is either an
        unsigned long for 32-bit PE's or unsigned long long for 64-bit PE's) to
        match the address where the PE file was carved out of memory.

        Args:
            raw_data: a bytes object of the PE's data
            nt_header: <_IMAGE_NT_HEADERS> or <_IMAGE_NT_HEADERS64> instance

        Returns:
             <bytes> patched with the correct address
        """

        image_base_offset = nt_header.OptionalHeader.ImageBase.vol.offset - self.vol.offset
        image_base_type = nt_header.OptionalHeader.ImageBase.vol.type_name
        member_size = self._context.symbol_space.get_type(image_base_type).size
        newval = objects.convert_value_to_data(self.vol.offset, int, nt_header.OptionalHeader.ImageBase.vol.data_format)
        return raw_data[:image_base_offset] + newval + raw_data[image_base_offset + member_size:]

def fix_image_base ( self , raw_data : bytes , nt_header : interfaces . objects . ObjectInterface ) -> bytes : image_base_offset = nt_header . OptionalHeader . ImageBase . vol . offset - self . vol . offset image_base_type = nt_header . OptionalHeader . ImageBase . vol . type_name member_size = self . _context . symbol_space . get_type ( image_base_type ) . size newval = objects . convert_value_to_data ( self . vol . offset , int , nt_header . OptionalHeader . ImageBase . vol . data_format ) return raw_data [ : image_base_offset ] + newval + raw_data [ image_base_offset + member_size : ]


This method generates the content necessary to reconstruct a PE file from memory . It preserves slack space ( similar to the old -- memory ) and automatically fixes the ImageBase in the output PE file .

def reconstruct(self) -> Generator[Tuple[int, bytes], None, None]:
        """This method generates the content necessary to reconstruct a PE file
        from memory. It preserves slack space (similar to the old --memory) and
        automatically fixes the ImageBase in the output PE file.

        Returns:
            <tuple> of (<int> offset, <bytes> data)
        """

        nt_header = self.get_nt_header()

        layer_name = self.vol.layer_name
        symbol_table_name = self.get_symbol_table_name()

        section_alignment = nt_header.OptionalHeader.SectionAlignment

        sect_header_size = self._context.symbol_space.get_type(symbol_table_name + constants.BANG +
                                                               "_IMAGE_SECTION_HEADER").size

        size_of_image = nt_header.OptionalHeader.SizeOfImage

        # no legitimate PE is going to be larger than this
        if size_of_image > (1024 * 1024 * 100):
            raise ValueError("The claimed SizeOfImage is too large: {}".format(size_of_image))

        read_layer = self._context.layers[layer_name]

        raw_data = read_layer.read(self.vol.offset, nt_header.OptionalHeader.SizeOfImage, pad = True)

        # fix the PE image base before yielding the initial view of the data
        fixed_data = self.fix_image_base(raw_data, nt_header)
        yield 0, fixed_data

        start_addr = nt_header.FileHeader.SizeOfOptionalHeader + \
                     (nt_header.OptionalHeader.vol.offset - self.vol.offset)

        counter = 0
        for sect in nt_header.get_sections():

            if sect.VirtualAddress > size_of_image:
                raise ValueError("Section VirtualAddress is too large: {}".format(sect.VirtualAddress))

            if sect.Misc.VirtualSize > size_of_image:
                raise ValueError("Section VirtualSize is too large: {}".format(sect.Misc.VirtualSize))

            if sect.SizeOfRawData > size_of_image:
                raise ValueError("Section SizeOfRawData is too large: {}".format(sect.SizeOfRawData))

            if sect is not None:
                # It doesn't matter if this is too big, because it'll get overwritten by the later layers
                sect_size = conversion.round(sect.Misc.VirtualSize, section_alignment, up = True)
                sectheader = read_layer.read(sect.vol.offset, sect_header_size)
                sectheader = self.replace_header_field(sect, sectheader, sect.PointerToRawData, sect.VirtualAddress)
                sectheader = self.replace_header_field(sect, sectheader, sect.SizeOfRawData, sect_size)
                sectheader = self.replace_header_field(sect, sectheader, sect.Misc.VirtualSize, sect_size)

                offset = start_addr + (counter * sect_header_size)
                yield offset, sectheader
                counter += 1


Iterate through the section headers for this PE file .

def get_sections(self) -> Generator[interfaces.objects.ObjectInterface, None, None]:
        """Iterate through the section headers for this PE file.

        Yields:
            <_IMAGE_SECTION_HEADER> objects
        """
        layer_name = self.vol.layer_name
        symbol_table_name = self.get_symbol_table_name()

        sect_header_size = self._context.symbol_space.get_type(symbol_table_name + constants.BANG +
                                                               "_IMAGE_SECTION_HEADER").size
        start_addr = self.FileHeader.SizeOfOptionalHeader + self.OptionalHeader.vol.offset

        for i in range(self.FileHeader.NumberOfSections):
            sect_addr = start_addr + (i * sect_header_size)
            yield self._context.object(symbol_table_name + constants.BANG + "_IMAGE_SECTION_HEADER",
                                       offset = sect_addr,
                                       layer_name = layer_name)

def get_sections ( self ) -> Generator [ interfaces . objects . ObjectInterface , None , None ] : layer_name = self . vol . layer_name symbol_table_name = self . get_symbol_table_name ( ) sect_header_size = self . _context . symbol_space . get_type ( symbol_table_name + constants . BANG + "_IMAGE_SECTION_HEADER" ) . size start_addr = self . FileHeader . SizeOfOptionalHeader + self . OptionalHeader . vol . offset for i in range ( self . FileHeader . NumberOfSections ) : sect_addr = start_addr + ( i * sect_header_size ) yield self . _context . object ( symbol_table_name + constants . BANG + "_IMAGE_SECTION_HEADER" , offset = sect_addr , layer_name = layer_name )


def dual_stack_sockets(self):
        """Handle Windows dual-stack sockets"""

        # If this pointer is valid, the socket is bound to
        # a specific IP address. Otherwise, the socket is
        # listening on all IP addresses of the address family.

        # Note the remote address is always INADDR_ANY or
        # INADDR6_ANY for sockets. The moment a client
        # connects to the listener, a TCP_ENDPOINT is created
        # and that structure contains the remote address.

        inaddr = self.get_in_addr()

        if inaddr:
            if self.get_address_family() == AF_INET:
                yield "v4", inet_ntop(socket.AF_INET, inaddr.addr4), inaddr_any
            elif self.get_address_family() == AF_INET6:
                yield "v6", inet_ntop(socket.AF_INET6, inaddr.addr6), inaddr6_any
        else:
            yield "v4", inaddr_any, inaddr_any
            if self.get_address_family() == AF_INET6:
                yield "v6", inaddr6_any, inaddr6_any


def get_object(self,
                   type_name: str,
                   use_top_down: bool,
                   executive: bool = False,
                   kernel_symbol_table: Optional[str] = None,
                   native_layer_name: Optional[str] = None) -> Optional[interfaces.objects.ObjectInterface]:
        """Carve an object or data structure from a kernel pool allocation

        Args:
            type_name: the data structure type name
            native_layer_name: the name of the layer where the data originally lived
            object_type: the object type (executive kernel objects only)
            kernel_symbol_table: in case objects of a different symbol table are scanned for

        Returns:
            An object as found from a POOL_HEADER
        """

        symbol_table_name = self.vol.type_name.split(constants.BANG)[0]
        if constants.BANG in type_name:
            symbol_table_name, type_name = type_name.split(constants.BANG)[0:2]

        # when checking for symbols from a table other than nt_symbols grab _OBJECT_HEADER from the kernel
        # because symbol_table_name will be different from kernel_symbol_table.
        if kernel_symbol_table:
            object_header_type = self._context.symbol_space.get_type(kernel_symbol_table + constants.BANG +
                                                                     "_OBJECT_HEADER")
        else:
            # otherwise symbol_table_name *is* the kernel symbol table, so just use that.
            object_header_type = self._context.symbol_space.get_type(symbol_table_name + constants.BANG +
                                                                     "_OBJECT_HEADER")

        pool_header_size = self.vol.size

        # if there is no object type, then just instantiate a structure
        if not executive:
            mem_object = self._context.object(symbol_table_name + constants.BANG + type_name,
                                              layer_name = self.vol.layer_name,
                                              offset = self.vol.offset + pool_header_size,
                                              native_layer_name = native_layer_name)
            return mem_object

        # otherwise we have an executive object in the pool
        else:
            if symbols.symbol_table_is_64bit(self._context, symbol_table_name):
                alignment = 16
            else:
                alignment = 8

            # use the top down approach for windows 8 and later
            if use_top_down:
                body_offset = object_header_type.relative_child_offset('Body')
                infomask_offset = object_header_type.relative_child_offset('InfoMask')
                pointercount_offset = object_header_type.relative_child_offset('PointerCount')
                pointercount_size = object_header_type.members['PointerCount'][1].size
                optional_headers, lengths_of_optional_headers = self._calculate_optional_header_lengths(
                    self._context, symbol_table_name)
                padding_available = None if 'PADDING_INFO' not in optional_headers else optional_headers.index(
                    'PADDING_INFO')
                max_optional_headers_length = sum(lengths_of_optional_headers)

                # define the starting and ending bounds for the scan
                start_offset = self.vol.offset + pool_header_size
                addr_limit = min(max_optional_headers_length, self.BlockSize * alignment)

                # A single read is better than lots of little one-byte reads.
                # We're ok padding this, because the byte we'd check would be 0 which would only be valid if there
                # were no optional headers in the first place (ie, if we read too much for headers that don't exist,
                # but the bit we could read were valid)
                infomask_data = self._context.layers[self.vol.layer_name].read(start_offset,
                                                                               addr_limit + infomask_offset,
                                                                               pad = True)

                # Addr stores the offset to the potential start of the OBJECT_HEADER from just after the POOL_HEADER
                # It will always be aligned to a particular alignment
                for addr in range(0, addr_limit, alignment):
                    infomask_value = infomask_data[addr + infomask_offset]
                    pointercount_value = int.from_bytes(
                        infomask_data[addr + pointercount_offset:addr + pointercount_offset + pointercount_size],
                        byteorder = 'little',
                        signed = True)
                    if not 0x1000000 > pointercount_value >= 0:
                        continue

                    padding_present = False
                    optional_headers_length = 0
                    for i in range(len(lengths_of_optional_headers)):
                        if infomask_value & (1 << i):
                            optional_headers_length += lengths_of_optional_headers[i]
                            if i == padding_available:
                                padding_present = True

                    # PADDING_INFO is a special case (4 bytes that contain the total padding length)
                    padding_length = 0
                    if padding_present:
                        # Read the four bytes from just before the next optional_headers_length minus the padding_info size
                        #
                        #  ---------------
                        #  POOL_HEADER
                        #  ---------------
                        #
                        #  start of PADDING_INFO
                        #  ---------------
                        #  End of other optional headers
                        #  ---------------
                        #  OBJECT_HEADER
                        #  ---------------
                        if addr - optional_headers_length < 0:
                            continue
                        padding_length = struct.unpack(
                            "<I", infomask_data[addr - optional_headers_length:addr - optional_headers_length + 4])[0]
                        padding_length -= lengths_of_optional_headers[padding_available or 0]

                    # Certain versions of windows have PADDING_INFO lengths that are too long
                    # So we now check that the padding length is at a minimum the right length
                    # and that it doesn't go beyond the entirety of the data
                    if addr - optional_headers_length >= padding_length > addr:
                        continue

                    try:
                        mem_object = self._context.object(symbol_table_name + constants.BANG + type_name,
                                                          layer_name = self.vol.layer_name,
                                                          offset = addr + body_offset + start_offset,
                                                          native_layer_name = native_layer_name)

                        if mem_object.is_valid():
                            return mem_object

                    except (TypeError, exceptions.InvalidAddressException):
                        pass

            # use the bottom up approach for windows 7 and earlier
            else:
                type_size = self._context.symbol_space.get_type(symbol_table_name + constants.BANG + type_name).size
                rounded_size = conversion.round(type_size, alignment, up = True)

                mem_object = self._context.object(symbol_table_name + constants.BANG + type_name,
                                                  layer_name = self.vol.layer_name,
                                                  offset = self.vol.offset + self.BlockSize * alignment - rounded_size,
                                                  native_layer_name = native_layer_name)

                try:
                    if mem_object.is_valid():
                        return mem_object
                except (TypeError, exceptions.InvalidAddressException):
                    return None
        return None


def get_key(self) -> str:
        """Returns the Key value as a 4 character string"""
        tag_bytes = objects.convert_value_to_data(self.Key, int, objects.DataFormatInfo(4, "little", False))
        return "".join([chr(x) if 32 < x < 127 else '' for x in tag_bytes])

def get_key ( self ) -> str : tag_bytes = objects . convert_value_to_data ( self . Key , int , objects . DataFormatInfo ( 4 , "little" , False ) ) return "" . join ( [ chr ( x ) if 32 < x < 127 else '' for x in tag_bytes ] )


def get_pool_type(self) -> Union[str, interfaces.renderers.BaseAbsentValue]:
        """Returns the enum name for the PoolType value on applicable systems"""
        # Not applicable until Vista
        if hasattr(self, 'PoolType'):
            if not self.pool_type_lookup:
                self._generate_pool_type_lookup()
            return self.pool_type_lookup.get(self.PoolType, "Unknown choice {}".format(self.PoolType))
        else:
            return renderers.NotApplicableValue()

def
get_pool_type
(
self
)
->
Union
[
str
,
interfaces
.
renderers
.
BaseAbsentValue
]
:
# Not applicable until Vista
if
hasattr
(
self
,
'PoolType'
)
:
if
not
self
.
pool_type_lookup
:
self
.
_generate_pool_type_lookup
(
)
return
self
.
pool_type_lookup
.
get
(
self
.
PoolType
,
"Unknown choice {}"
.
format
(
self
.
PoolType
)
)
else
:
return
renderers
.
NotApplicableValue
(
)


def get_number_of_bytes(self) -> Union[int, interfaces.renderers.BaseAbsentValue]:
        """Returns the NumberOfBytes value on applicable systems"""
        # Not applicable until Vista
        try:
            return self.NumberOfBytes
        except AttributeError:
            return renderers.NotApplicableValue()

def
get_number_of_bytes
(
self
)
->
Union
[
int
,
interfaces
.
renderers
.
BaseAbsentValue
]
:
# Not applicable until Vista
try
:
return
self
.
NumberOfBytes
except
AttributeError
:
return
renderers
.
NotApplicableValue
(
)