Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
2015 lines (1685 sloc) 70.6 KB
#!/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2009-2018, Mario Vilas
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice,this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
"""
Module instrumentation.
@group Instrumentation:
Module
@group Warnings:
DebugSymbolsWarning
"""
from __future__ import with_statement
__all__ = ['Module', 'DebugSymbolsWarning']
import win32
from textio import HexInput, HexDump
from util import PathOperations
# delayed imports
Process = None
import warnings
import traceback
# Cygwin compatibility.
try:
WindowsError
except NameError:
from winappdbg.win32 import WindowsError
#==============================================================================
class DebugSymbolsWarning (UserWarning):
"""
This warning is issued if the support for debug symbols
isn't working properly.
"""
#==============================================================================
class Module (object):
"""
Interface to a DLL library loaded in the context of another process.
@group Properties:
get_base, get_filename, get_name, get_size, get_entry_point,
get_process, set_process, get_pid,
get_handle, set_handle, open_handle, close_handle
@group Labels:
get_label, get_label_at_address, is_address_here,
resolve, resolve_label, match_name
@group Symbols:
load_symbols, unload_symbols, get_symbols, iter_symbols,
resolve_symbol, get_symbol_at_address
@group Modules snapshot:
clear
@type unknown: str
@cvar unknown: Suggested tag for unknown modules.
@type lpBaseOfDll: int
@ivar lpBaseOfDll: Base of DLL module.
Use L{get_base} instead.
@type hFile: L{FileHandle}
@ivar hFile: Handle to the module file.
Use L{get_handle} instead.
@type fileName: str
@ivar fileName: Module filename.
Use L{get_filename} instead.
@type SizeOfImage: int
@ivar SizeOfImage: Size of the module.
Use L{get_size} instead.
@type EntryPoint: int
@ivar EntryPoint: Entry point of the module.
Use L{get_entry_point} instead.
@type process: L{Process}
@ivar process: Process where the module is loaded.
Use the L{get_process} method instead.
"""
unknown = '<unknown>'
class _SymbolEnumerator (object):
"""
Internally used by L{Module} to enumerate symbols in a module.
"""
def __init__(self, undecorate = False):
self.symbols = list()
self.undecorate = undecorate
def __call__(self, SymbolName, SymbolAddress, SymbolSize, UserContext):
"""
Callback that receives symbols and stores them in a Python list.
"""
if self.undecorate:
try:
SymbolName = win32.UnDecorateSymbolName(SymbolName)
except Exception:
pass # not all symbols are decorated!
self.symbols.append( (SymbolName, SymbolAddress, SymbolSize) )
return win32.TRUE
def __init__(self, lpBaseOfDll, hFile = None, fileName = None,
SizeOfImage = None,
EntryPoint = None,
process = None):
"""
@type lpBaseOfDll: str
@param lpBaseOfDll: Base address of the module.
@type hFile: L{FileHandle}
@param hFile: (Optional) Handle to the module file.
@type fileName: str
@param fileName: (Optional) Module filename.
@type SizeOfImage: int
@param SizeOfImage: (Optional) Size of the module.
@type EntryPoint: int
@param EntryPoint: (Optional) Entry point of the module.
@type process: L{Process}
@param process: (Optional) Process where the module is loaded.
"""
self.lpBaseOfDll = lpBaseOfDll
self.fileName = fileName
self.SizeOfImage = SizeOfImage
self.EntryPoint = EntryPoint
self.__symbols = list()
self.set_handle(hFile)
self.set_process(process)
# Not really sure if it's a good idea...
## def __eq__(self, aModule):
## """
## Compare two Module objects. The comparison is made using the process
## IDs and the module bases.
##
## @type aModule: L{Module}
## @param aModule: Another Module object.
##
## @rtype: bool
## @return: C{True} if the two process IDs and module bases are equal,
## C{False} otherwise.
## """
## return isinstance(aModule, Module) and \
## self.get_pid() == aModule.get_pid() and \
## self.get_base() == aModule.get_base()
def get_handle(self):
"""
@rtype: L{Handle}
@return: File handle.
Returns C{None} if unknown.
"""
# no way to guess!
return self.__hFile
def set_handle(self, hFile):
"""
@type hFile: L{Handle}
@param hFile: File handle. Use C{None} to clear.
"""
if hFile == win32.INVALID_HANDLE_VALUE:
hFile = None
self.__hFile = hFile
hFile = property(get_handle, set_handle, doc="")
def get_process(self):
"""
@rtype: L{Process}
@return: Parent Process object.
Returns C{None} if unknown.
"""
# no way to guess!
return self.__process
def set_process(self, process = None):
"""
Manually set the parent process. Use with care!
@type process: L{Process}
@param process: (Optional) Process object. Use C{None} for no process.
"""
if process is None:
self.__process = None
else:
global Process # delayed import
if Process is None:
from process import Process
if not isinstance(process, Process):
msg = "Parent process must be a Process instance, "
msg += "got %s instead" % type(process)
raise TypeError(msg)
self.__process = process
process = property(get_process, set_process, doc="")
def get_pid(self):
"""
@rtype: int or None
@return: Parent process global ID.
Returns C{None} on error.
"""
process = self.get_process()
if process is not None:
return process.get_pid()
def get_base(self):
"""
@rtype: int or None
@return: Base address of the module.
Returns C{None} if unknown.
"""
return self.lpBaseOfDll
def get_size(self):
"""
@rtype: int or None
@return: Base size of the module.
Returns C{None} if unknown.
"""
if not self.SizeOfImage:
self.__get_size_and_entry_point()
return self.SizeOfImage
def get_entry_point(self):
"""
@rtype: int or None
@return: Entry point of the module.
Returns C{None} if unknown.
"""
if not self.EntryPoint:
self.__get_size_and_entry_point()
return self.EntryPoint
def __get_size_and_entry_point(self):
"Get the size and entry point of the module using the Win32 API."
process = self.get_process()
if process:
try:
handle = process.get_handle( win32.PROCESS_VM_READ |
win32.PROCESS_QUERY_INFORMATION )
base = self.get_base()
mi = win32.GetModuleInformation(handle, base)
self.SizeOfImage = mi.SizeOfImage
self.EntryPoint = mi.EntryPoint
except WindowsError, e:
warnings.warn(
"Cannot get size and entry point of module %s, reason: %s"\
% (self.get_name(), e.strerror), RuntimeWarning)
def get_filename(self):
"""
@rtype: str or None
@return: Module filename.
Returns C{None} if unknown.
"""
if self.fileName is None:
if self.hFile not in (None, win32.INVALID_HANDLE_VALUE):
fileName = self.hFile.get_filename()
if fileName:
fileName = PathOperations.native_to_win32_pathname(fileName)
self.fileName = fileName
return self.fileName
def __filename_to_modname(self, pathname):
"""
@type pathname: str
@param pathname: Pathname to a module.
@rtype: str
@return: Module name.
"""
filename = PathOperations.pathname_to_filename(pathname)
if filename:
filename = filename.lower()
filepart, extpart = PathOperations.split_extension(filename)
if filepart and extpart:
modName = filepart
else:
modName = filename
else:
modName = pathname
return modName
def get_name(self):
"""
@rtype: str
@return: Module name, as used in labels.
@warning: Names are B{NOT} guaranteed to be unique.
If you need unique identification for a loaded module,
use the base address instead.
@see: L{get_label}
"""
pathname = self.get_filename()
if pathname:
modName = self.__filename_to_modname(pathname)
if isinstance(modName, unicode):
try:
modName = modName.encode('cp1252')
except UnicodeEncodeError, e:
warnings.warn(str(e))
else:
modName = "0x%x" % self.get_base()
return modName
def match_name(self, name):
"""
@rtype: bool
@return:
C{True} if the given name could refer to this module.
It may not be exactly the same returned by L{get_name}.
"""
# If the given name is exactly our name, return True.
# Comparison is case insensitive.
my_name = self.get_name().lower()
if name.lower() == my_name:
return True
# If the given name is a base address, compare it with ours.
try:
base = HexInput.integer(name)
except ValueError:
base = None
if base is not None and base == self.get_base():
return True
# If the given name is a filename, convert it to a module name.
# Then compare it with ours, case insensitive.
modName = self.__filename_to_modname(name)
if modName.lower() == my_name:
return True
# No match.
return False
#------------------------------------------------------------------------------
def open_handle(self):
"""
Opens a new handle to the module.
The new handle is stored in the L{hFile} property.
"""
if not self.get_filename():
msg = "Cannot retrieve filename for module at %s"
msg = msg % HexDump.address( self.get_base() )
raise Exception(msg)
hFile = win32.CreateFile(self.get_filename(),
dwShareMode = win32.FILE_SHARE_READ,
dwCreationDisposition = win32.OPEN_EXISTING)
# In case hFile was set to an actual handle value instead of a Handle
# object. This shouldn't happen unless the user tinkered with hFile.
if not hasattr(self.hFile, '__del__'):
self.close_handle()
self.hFile = hFile
def close_handle(self):
"""
Closes the handle to the module.
@note: Normally you don't need to call this method. All handles
created by I{WinAppDbg} are automatically closed when the garbage
collector claims them. So unless you've been tinkering with it,
setting L{hFile} to C{None} should be enough.
"""
try:
if hasattr(self.hFile, 'close'):
self.hFile.close()
elif self.hFile not in (None, win32.INVALID_HANDLE_VALUE):
win32.CloseHandle(self.hFile)
finally:
self.hFile = None
def get_handle(self):
"""
@rtype: L{FileHandle}
@return: Handle to the module file.
"""
if self.hFile in (None, win32.INVALID_HANDLE_VALUE):
self.open_handle()
return self.hFile
def clear(self):
"""
Clears the resources held by this object.
"""
try:
self.set_process(None)
finally:
self.close_handle()
#------------------------------------------------------------------------------
# XXX FIXME
# I've been told sometimes the debugging symbols APIs don't correctly
# handle redirected exports (for example ws2_32!recv).
# I haven't been able to reproduce the bug yet.
def load_symbols(self):
"""
Loads the debugging symbols for a module.
Automatically called by L{get_symbols}.
"""
if win32.PROCESS_ALL_ACCESS == win32.PROCESS_ALL_ACCESS_VISTA:
dwAccess = win32.PROCESS_QUERY_LIMITED_INFORMATION
else:
dwAccess = win32.PROCESS_QUERY_INFORMATION
hProcess = self.get_process().get_handle(dwAccess)
hFile = self.hFile
BaseOfDll = self.get_base()
SizeOfDll = self.get_size()
Enumerator = self._SymbolEnumerator()
try:
win32.SymInitialize(hProcess)
SymOptions = win32.SymGetOptions()
SymOptions |= (
win32.SYMOPT_ALLOW_ZERO_ADDRESS |
win32.SYMOPT_CASE_INSENSITIVE |
win32.SYMOPT_FAVOR_COMPRESSED |
win32.SYMOPT_INCLUDE_32BIT_MODULES |
win32.SYMOPT_UNDNAME
)
SymOptions &= ~(
win32.SYMOPT_LOAD_LINES |
win32.SYMOPT_NO_IMAGE_SEARCH |
win32.SYMOPT_NO_CPP |
win32.SYMOPT_IGNORE_NT_SYMPATH
)
win32.SymSetOptions(SymOptions)
try:
win32.SymSetOptions(
SymOptions | win32.SYMOPT_ALLOW_ABSOLUTE_SYMBOLS)
except WindowsError:
pass
try:
try:
success = win32.SymLoadModule64(
hProcess, hFile, None, None, BaseOfDll, SizeOfDll)
except WindowsError:
success = 0
if not success:
ImageName = self.get_filename()
success = win32.SymLoadModule64(
hProcess, None, ImageName, None, BaseOfDll, SizeOfDll)
if success:
try:
win32.SymEnumerateSymbols64(
hProcess, BaseOfDll, Enumerator)
finally:
win32.SymUnloadModule64(hProcess, BaseOfDll)
finally:
win32.SymCleanup(hProcess)
except WindowsError, e:
msg = "Cannot load debug symbols for process ID %d, reason:\n%s"
msg = msg % (self.get_pid(), traceback.format_exc(e))
warnings.warn(msg, DebugSymbolsWarning)
self.__symbols = Enumerator.symbols
def unload_symbols(self):
"""
Unloads the debugging symbols for a module.
"""
self.__symbols = list()
def get_symbols(self):
"""
Returns the debugging symbols for a module.
The symbols are automatically loaded when needed.
@rtype: list of tuple( str, int, int )
@return: List of symbols.
Each symbol is represented by a tuple that contains:
- Symbol name
- Symbol memory address
- Symbol size in bytes
"""
if not self.__symbols:
self.load_symbols()
return list(self.__symbols)
def iter_symbols(self):
"""
Returns an iterator for the debugging symbols in a module,
in no particular order.
The symbols are automatically loaded when needed.
@rtype: iterator of tuple( str, int, int )
@return: Iterator of symbols.
Each symbol is represented by a tuple that contains:
- Symbol name
- Symbol memory address
- Symbol size in bytes
"""
if not self.__symbols:
self.load_symbols()
return self.__symbols.__iter__()
def resolve_symbol(self, symbol, bCaseSensitive = False):
"""
Resolves a debugging symbol's address.
@type symbol: str
@param symbol: Name of the symbol to resolve.
@type bCaseSensitive: bool
@param bCaseSensitive: C{True} for case sensitive matches,
C{False} for case insensitive.
@rtype: int or None
@return: Memory address of symbol. C{None} if not found.
"""
if bCaseSensitive:
for (SymbolName, SymbolAddress, SymbolSize) in self.iter_symbols():
if symbol == SymbolName:
return SymbolAddress
for (SymbolName, SymbolAddress, SymbolSize) in self.iter_symbols():
try:
SymbolName = win32.UnDecorateSymbolName(SymbolName)
except Exception:
continue
if symbol == SymbolName:
return SymbolAddress
else:
symbol = symbol.lower()
for (SymbolName, SymbolAddress, SymbolSize) in self.iter_symbols():
if symbol == SymbolName.lower():
return SymbolAddress
for (SymbolName, SymbolAddress, SymbolSize) in self.iter_symbols():
try:
SymbolName = win32.UnDecorateSymbolName(SymbolName)
except Exception:
continue
if symbol == SymbolName.lower():
return SymbolAddress
def get_symbol_at_address(self, address):
"""
Tries to find the closest matching symbol for the given address.
@type address: int
@param address: Memory address to query.
@rtype: None or tuple( str, int, int )
@return: Returns a tuple consisting of:
- Name
- Address
- Size (in bytes)
Returns C{None} if no symbol could be matched.
"""
found = None
for (SymbolName, SymbolAddress, SymbolSize) in self.iter_symbols():
if SymbolAddress > address:
continue
if SymbolAddress + SymbolSize > address:
if not found or found[1] < SymbolAddress:
found = (SymbolName, SymbolAddress, SymbolSize)
return found
#------------------------------------------------------------------------------
def get_label(self, function = None, offset = None):
"""
Retrieves the label for the given function of this module or the module
base address if no function name is given.
@type function: str
@param function: (Optional) Exported function name.
@type offset: int
@param offset: (Optional) Offset from the module base address.
@rtype: str
@return: Label for the module base address, plus the offset if given.
"""
return _ModuleContainer.parse_label(self.get_name(), function, offset)
def get_label_at_address(self, address, offset = None):
"""
Creates a label from the given memory address.
If the address belongs to the module, the label is made relative to
it's base address.
@type address: int
@param address: Memory address.
@type offset: None or int
@param offset: (Optional) Offset value.
@rtype: str
@return: Label pointing to the given address.
"""
# Add the offset to the address.
if offset:
address = address + offset
# Make the label relative to the base address if no match is found.
module = self.get_name()
function = None
offset = address - self.get_base()
# Make the label relative to the entrypoint if no other match is found.
# Skip if the entry point is unknown.
start = self.get_entry_point()
if start and start <= address:
function = "start"
offset = address - start
# Enumerate exported functions and debug symbols,
# then find the closest match, if possible.
try:
symbol = self.get_symbol_at_address(address)
if symbol:
(SymbolName, SymbolAddress, SymbolSize) = symbol
new_offset = address - SymbolAddress
if new_offset <= offset:
function = SymbolName
offset = new_offset
except WindowsError:
pass
# Parse the label and return it.
return _ModuleContainer.parse_label(module, function, offset)
def is_address_here(self, address):
"""
Tries to determine if the given address belongs to this module.
@type address: int
@param address: Memory address.
@rtype: bool or None
@return: C{True} if the address belongs to the module,
C{False} if it doesn't,
and C{None} if it can't be determined.
"""
base = self.get_base()
size = self.get_size()
if base and size:
return base <= address < (base + size)
return None
def resolve(self, function):
"""
Resolves a function exported by this module.
@type function: str or int
@param function:
str: Name of the function.
int: Ordinal of the function.
@rtype: int
@return: Memory address of the exported function in the process.
Returns None on error.
"""
# Unknown DLL filename, there's nothing we can do.
filename = self.get_filename()
if not filename:
return None
# If the DLL is already mapped locally, resolve the function.
try:
hlib = win32.GetModuleHandle(filename)
address = win32.GetProcAddress(hlib, function)
except WindowsError:
# Load the DLL locally, resolve the function and unload it.
try:
hlib = win32.LoadLibraryEx(filename,
win32.DONT_RESOLVE_DLL_REFERENCES)
try:
address = win32.GetProcAddress(hlib, function)
finally:
win32.FreeLibrary(hlib)
except WindowsError:
return None
# A NULL pointer means the function was not found.
if address in (None, 0):
return None
# Compensate for DLL base relocations locally and remotely.
return address - hlib + self.lpBaseOfDll
def resolve_label(self, label):
"""
Resolves a label for this module only. If the label refers to another
module, an exception is raised.
@type label: str
@param label: Label to resolve.
@rtype: int
@return: Memory address pointed to by the label.
@raise ValueError: The label is malformed or impossible to resolve.
@raise RuntimeError: Cannot resolve the module or function.
"""
# Split the label into it's components.
# Use the fuzzy mode whenever possible.
aProcess = self.get_process()
if aProcess is not None:
(module, procedure, offset) = aProcess.split_label(label)
else:
(module, procedure, offset) = _ModuleContainer.split_label(label)
# If a module name is given that doesn't match ours,
# raise an exception.
if module and not self.match_name(module):
raise RuntimeError("Label does not belong to this module")
# Resolve the procedure if given.
if procedure:
address = self.resolve(procedure)
if address is None:
# If it's a debug symbol, use the symbol.
address = self.resolve_symbol(procedure)
# If it's the keyword "start" use the entry point.
if address is None and procedure == "start":
address = self.get_entry_point()
# The procedure was not found.
if address is None:
if not module:
module = self.get_name()
msg = "Can't find procedure %s in module %s"
raise RuntimeError(msg % (procedure, module))
# If no procedure is given use the base address of the module.
else:
address = self.get_base()
# Add the offset if given and return the resolved address.
if offset:
address = address + offset
return address
#==============================================================================
# TODO
# An alternative approach to the toolhelp32 snapshots: parsing the PEB and
# fetching the list of loaded modules from there. That would solve the problem
# of toolhelp32 not working when the process hasn't finished initializing.
# See: http://pferrie.host22.com/misc/lowlevel3.htm
class _ModuleContainer (object):
"""
Encapsulates the capability to contain Module objects.
@note: Labels are an approximated way of referencing memory locations
across different executions of the same process, or different processes
with common modules. They are not meant to be perfectly unique, and
some errors may occur when multiple modules with the same name are
loaded, or when module filenames can't be retrieved.
@group Modules snapshot:
scan_modules,
get_module, get_module_bases, get_module_count,
get_module_at_address, get_module_by_name,
has_module, iter_modules, iter_module_addresses,
clear_modules
@group Labels:
parse_label, split_label, sanitize_label, resolve_label,
resolve_label_components, get_label_at_address, split_label_strict,
split_label_fuzzy
@group Symbols:
load_symbols, unload_symbols, get_symbols, iter_symbols,
resolve_symbol, get_symbol_at_address
@group Debugging:
is_system_defined_breakpoint, get_system_breakpoint,
get_user_breakpoint, get_breakin_breakpoint,
get_wow64_system_breakpoint, get_wow64_user_breakpoint,
get_wow64_breakin_breakpoint, get_break_on_error_ptr
"""
def __init__(self):
self.__moduleDict = dict()
self.__system_breakpoints = dict()
# Replace split_label with the fuzzy version on object instances.
self.split_label = self.__use_fuzzy_mode
def __initialize_snapshot(self):
"""
Private method to automatically initialize the snapshot
when you try to use it without calling any of the scan_*
methods first. You don't need to call this yourself.
"""
if not self.__moduleDict:
try:
self.scan_modules()
except WindowsError:
pass
def __contains__(self, anObject):
"""
@type anObject: L{Module}, int
@param anObject:
- C{Module}: Module object to look for.
- C{int}: Base address of the DLL to look for.
@rtype: bool
@return: C{True} if the snapshot contains
a L{Module} object with the same base address.
"""
if isinstance(anObject, Module):
anObject = anObject.lpBaseOfDll
return self.has_module(anObject)
def __iter__(self):
"""
@see: L{iter_modules}
@rtype: dictionary-valueiterator
@return: Iterator of L{Module} objects in this snapshot.
"""
return self.iter_modules()
def __len__(self):
"""
@see: L{get_module_count}
@rtype: int
@return: Count of L{Module} objects in this snapshot.
"""
return self.get_module_count()
def has_module(self, lpBaseOfDll):
"""
@type lpBaseOfDll: int
@param lpBaseOfDll: Base address of the DLL to look for.
@rtype: bool
@return: C{True} if the snapshot contains a
L{Module} object with the given base address.
"""
self.__initialize_snapshot()
return lpBaseOfDll in self.__moduleDict
def get_module(self, lpBaseOfDll):
"""
@type lpBaseOfDll: int
@param lpBaseOfDll: Base address of the DLL to look for.
@rtype: L{Module}
@return: Module object with the given base address.
"""
self.__initialize_snapshot()
if lpBaseOfDll not in self.__moduleDict:
msg = "Unknown DLL base address %s"
msg = msg % HexDump.address(lpBaseOfDll)
raise KeyError(msg)
return self.__moduleDict[lpBaseOfDll]
def iter_module_addresses(self):
"""
@see: L{iter_modules}
@rtype: dictionary-keyiterator
@return: Iterator of DLL base addresses in this snapshot.
"""
self.__initialize_snapshot()
return self.__moduleDict.iterkeys()
def iter_modules(self):
"""
@see: L{iter_module_addresses}
@rtype: dictionary-valueiterator
@return: Iterator of L{Module} objects in this snapshot.
"""
self.__initialize_snapshot()
return self.__moduleDict.itervalues()
def get_module_bases(self):
"""
@see: L{iter_module_addresses}
@rtype: list( int... )
@return: List of DLL base addresses in this snapshot.
"""
self.__initialize_snapshot()
return self.__moduleDict.keys()
def get_module_count(self):
"""
@rtype: int
@return: Count of L{Module} objects in this snapshot.
"""
self.__initialize_snapshot()
return len(self.__moduleDict)
#------------------------------------------------------------------------------
def get_module_by_name(self, modName):
"""
@type modName: int
@param modName:
Name of the module to look for, as returned by L{Module.get_name}.
If two or more modules with the same name are loaded, only one
of the matching modules is returned.
You can also pass a full pathname to the DLL file.
This works correctly even if two modules with the same name
are loaded from different paths.
@rtype: L{Module}
@return: C{Module} object that best matches the given name.
Returns C{None} if no C{Module} can be found.
"""
# Convert modName to lowercase.
# This helps make case insensitive string comparisons.
modName = modName.lower()
# modName is an absolute pathname.
if PathOperations.path_is_absolute(modName):
for lib in self.iter_modules():
if modName == lib.get_filename().lower():
return lib
return None # Stop trying to match the name.
# Get all the module names.
# This prevents having to iterate through the module list
# more than once.
modDict = [ ( lib.get_name(), lib ) for lib in self.iter_modules() ]
modDict = dict(modDict)
# modName is a base filename.
if modName in modDict:
return modDict[modName]
# modName is a base filename without extension.
filepart, extpart = PathOperations.split_extension(modName)
if filepart and extpart:
if filepart in modDict:
return modDict[filepart]
# modName is a base address.
try:
baseAddress = HexInput.integer(modName)
except ValueError:
return None
if self.has_module(baseAddress):
return self.get_module(baseAddress)
# Module not found.
return None
def get_module_at_address(self, address):
"""
@type address: int
@param address: Memory address to query.
@rtype: L{Module}
@return: C{Module} object that best matches the given address.
Returns C{None} if no C{Module} can be found.
"""
bases = self.get_module_bases()
bases.sort()
bases.append(0x10000000000000000L) # max. 64 bit address + 1
if address >= bases[0]:
i = 0
max_i = len(bases) - 1
while i < max_i:
begin, end = bases[i:i+2]
if begin <= address < end:
module = self.get_module(begin)
here = module.is_address_here(address)
if here is False:
break
else: # True or None
return module
i = i + 1
return None
# XXX this method musn't end up calling __initialize_snapshot by accident!
def scan_modules(self):
"""
Populates the snapshot with loaded modules.
"""
# The module filenames may be spoofed by malware,
# since this information resides in usermode space.
# See: http://www.ragestorm.net/blogs/?p=163
# Ignore special process IDs.
# PID 0: System Idle Process. Also has a special meaning to the
# toolhelp APIs (current process).
# PID 4: System Integrity Group. See this forum post for more info:
# http://tinyurl.com/ycza8jo
# (points to social.technet.microsoft.com)
# Only on XP and above
# PID 8: System (?) only in Windows 2000 and below AFAIK.
# It's probably the same as PID 4 in XP and above.
dwProcessId = self.get_pid()
if dwProcessId in (0, 4, 8):
return
# It would seem easier to clear the snapshot first.
# But then all open handles would be closed.
found_bases = set()
with win32.CreateToolhelp32Snapshot(win32.TH32CS_SNAPMODULE,
dwProcessId) as hSnapshot:
me = win32.Module32First(hSnapshot)
while me is not None:
lpBaseAddress = me.modBaseAddr
fileName = me.szExePath # full pathname
if not fileName:
fileName = me.szModule # filename only
if not fileName:
fileName = None
else:
fileName = PathOperations.native_to_win32_pathname(fileName)
found_bases.add(lpBaseAddress)
## if not self.has_module(lpBaseAddress): # XXX triggers a scan
if lpBaseAddress not in self.__moduleDict:
aModule = Module(lpBaseAddress, fileName = fileName,
SizeOfImage = me.modBaseSize,
process = self)
self._add_module(aModule)
else:
aModule = self.get_module(lpBaseAddress)
if not aModule.fileName:
aModule.fileName = fileName
if not aModule.SizeOfImage:
aModule.SizeOfImage = me.modBaseSize
if not aModule.process:
aModule.process = self
me = win32.Module32Next(hSnapshot)
## for base in self.get_module_bases(): # XXX triggers a scan
for base in self.__moduleDict.keys():
if base not in found_bases:
self._del_module(base)
def clear_modules(self):
"""
Clears the modules snapshot.
"""
for aModule in self.__moduleDict.itervalues():
aModule.clear()
self.__moduleDict = dict()
#------------------------------------------------------------------------------
@staticmethod
def parse_label(module = None, function = None, offset = None):
"""
Creates a label from a module and a function name, plus an offset.
@warning: This method only creates the label, it doesn't make sure the
label actually points to a valid memory location.
@type module: None or str
@param module: (Optional) Module name.
@type function: None, str or int
@param function: (Optional) Function name or ordinal.
@type offset: None or int
@param offset: (Optional) Offset value.
If C{function} is specified, offset from the function.
If C{function} is C{None}, offset from the module.
@rtype: str
@return:
Label representing the given function in the given module.
@raise ValueError:
The module or function name contain invalid characters.
"""
# TODO
# Invalid characters should be escaped or filtered.
# Convert ordinals to strings.
try:
function = "#0x%x" % function
except TypeError:
pass
# Validate the parameters.
if module is not None and ('!' in module or '+' in module):
raise ValueError("Invalid module name: %s" % module)
if function is not None and ('!' in function or '+' in function):
raise ValueError("Invalid function name: %s" % function)
# Parse the label.
if module:
if function:
if offset:
label = "%s!%s+0x%x" % (module, function, offset)
else:
label = "%s!%s" % (module, function)
else:
if offset:
## label = "%s+0x%x!" % (module, offset)
label = "%s!0x%x" % (module, offset)
else:
label = "%s!" % module
else:
if function:
if offset:
label = "!%s+0x%x" % (function, offset)
else:
label = "!%s" % function
else:
if offset:
label = "0x%x" % offset
else:
label = "0x0"
return label
@staticmethod
def split_label_strict(label):
"""
Splits a label created with L{parse_label}.
To parse labels with a less strict syntax, use the L{split_label_fuzzy}
method instead.
@warning: This method only parses the label, it doesn't make sure the
label actually points to a valid memory location.
@type label: str
@param label: Label to split.
@rtype: tuple( str or None, str or int or None, int or None )
@return: Tuple containing the C{module} name,
the C{function} name or ordinal, and the C{offset} value.
If the label doesn't specify a module,
then C{module} is C{None}.
If the label doesn't specify a function,
then C{function} is C{None}.
If the label doesn't specify an offset,
then C{offset} is C{0}.
@raise ValueError: The label is malformed.
"""
module = function = None
offset = 0
# Special case: None
if not label:
label = "0x0"
else:
# Remove all blanks.
label = label.replace(' ', '')
label = label.replace('\t', '')
label = label.replace('\r', '')
label = label.replace('\n', '')
# Special case: empty label.
if not label:
label = "0x0"
# * ! *
if '!' in label:
try:
module, function = label.split('!')
except ValueError:
raise ValueError("Malformed label: %s" % label)
# module ! function
if function:
if '+' in module:
raise ValueError("Malformed label: %s" % label)
# module ! function + offset
if '+' in function:
try:
function, offset = function.split('+')
except ValueError:
raise ValueError("Malformed label: %s" % label)
try:
offset = HexInput.integer(offset)
except ValueError:
raise ValueError("Malformed label: %s" % label)
else:
# module ! offset
try:
offset = HexInput.integer(function)
function = None
except ValueError:
pass
else:
# module + offset !
if '+' in module:
try:
module, offset = module.split('+')
except ValueError:
raise ValueError("Malformed label: %s" % label)
try:
offset = HexInput.integer(offset)
except ValueError:
raise ValueError("Malformed label: %s" % label)
else:
# module !
try:
offset = HexInput.integer(module)
module = None
# offset !
except ValueError:
pass
if not module:
module = None
if not function:
function = None
# *
else:
# offset
try:
offset = HexInput.integer(label)
# # ordinal
except ValueError:
if label.startswith('#'):
function = label
try:
HexInput.integer(function[1:])
# module?
# function?
except ValueError:
raise ValueError("Ambiguous label: %s" % label)
# module?
# function?
else:
raise ValueError("Ambiguous label: %s" % label)
# Convert function ordinal strings into integers.
if function and function.startswith('#'):
try:
function = HexInput.integer(function[1:])
except ValueError:
pass
# Convert null offsets to None.
if not offset:
offset = None
return (module, function, offset)
def split_label_fuzzy(self, label):
"""
Splits a label entered as user input.
It's more flexible in it's syntax parsing than the L{split_label_strict}
method, as it allows the exclamation mark (B{C{!}}) to be omitted. The
ambiguity is resolved by searching the modules in the snapshot to guess
if a label refers to a module or a function. It also tries to rebuild
labels when they contain hardcoded addresses.
@warning: This method only parses the label, it doesn't make sure the
label actually points to a valid memory location.
@type label: str
@param label: Label to split.
@rtype: tuple( str or None, str or int or None, int or None )
@return: Tuple containing the C{module} name,
the C{function} name or ordinal, and the C{offset} value.
If the label doesn't specify a module,
then C{module} is C{None}.
If the label doesn't specify a function,
then C{function} is C{None}.
If the label doesn't specify an offset,
then C{offset} is C{0}.
@raise ValueError: The label is malformed.
"""
module = function = None
offset = 0
# Special case: None
if not label:
label = "0x0"
else:
# Remove all blanks.
label = label.replace(' ', '')
label = label.replace('\t', '')
label = label.replace('\r', '')
label = label.replace('\n', '')
# Special case: empty label.
if not label:
label = "0x0"
# If an exclamation sign is present, we know we can parse it strictly.
if '!' in label:
return self.split_label_strict(label)
## # Try to parse it strictly, on error do it the fuzzy way.
## try:
## return self.split_label(label)
## except ValueError:
## pass
# * + offset
if '+' in label:
try:
prefix, offset = label.split('+')
except ValueError:
raise ValueError("Malformed label: %s" % label)
try:
offset = HexInput.integer(offset)
except ValueError:
raise ValueError("Malformed label: %s" % label)
label = prefix
# This parses both filenames and base addresses.
modobj = self.get_module_by_name(label)
if modobj:
# module
# module + offset
module = modobj.get_name()
else:
# TODO
# If 0xAAAAAAAA + 0xBBBBBBBB is given,
# A is interpreted as a module base address,
# and B as an offset.
# If that fails, it'd be good to add A+B and try to
# use the nearest loaded module.
# offset
# base address + offset (when no module has that base address)
try:
address = HexInput.integer(label)
if offset:
# If 0xAAAAAAAA + 0xBBBBBBBB is given,
# A is interpreted as a module base address,
# and B as an offset.
# If that fails, we get here, meaning no module was found
# at A. Then add up A+B and work with that as a hardcoded
# address.
offset = address + offset
else:
# If the label is a hardcoded address, we get here.
offset = address
# If only a hardcoded address is given,
# rebuild the label using get_label_at_address.
# Then parse it again, but this time strictly,
# both because there is no need for fuzzy syntax and
# to prevent an infinite recursion if there's a bug here.
try:
new_label = self.get_label_at_address(offset)
module, function, offset = \
self.split_label_strict(new_label)
except ValueError:
pass
# function
# function + offset
except ValueError:
function = label
# Convert function ordinal strings into integers.
if function and function.startswith('#'):
try:
function = HexInput.integer(function[1:])
except ValueError:
pass
# Convert null offsets to None.
if not offset:
offset = None
return (module, function, offset)
@classmethod
def split_label(cls, label):
"""
Splits a label into it's C{module}, C{function} and C{offset}
components, as used in L{parse_label}.
When called as a static method, the strict syntax mode is used::
winappdbg.Process.split_label( "kernel32!CreateFileA" )
When called as an instance method, the fuzzy syntax mode is used::
aProcessInstance.split_label( "CreateFileA" )
@see: L{split_label_strict}, L{split_label_fuzzy}
@type label: str
@param label: Label to split.
@rtype: tuple( str or None, str or int or None, int or None )
@return:
Tuple containing the C{module} name,
the C{function} name or ordinal, and the C{offset} value.
If the label doesn't specify a module,
then C{module} is C{None}.
If the label doesn't specify a function,
then C{function} is C{None}.
If the label doesn't specify an offset,
then C{offset} is C{0}.
@raise ValueError: The label is malformed.
"""
# XXX
# Docstring indentation was removed so epydoc doesn't complain
# when parsing the docs for __use_fuzzy_mode().
# This function is overwritten by __init__
# so here is the static implementation only.
return cls.split_label_strict(label)
# The split_label method is replaced with this function by __init__.
def __use_fuzzy_mode(self, label):
"@see: L{split_label}"
return self.split_label_fuzzy(label)
## __use_fuzzy_mode.__doc__ = split_label.__doc__
def sanitize_label(self, label):
"""
Converts a label taken from user input into a well-formed label.
@type label: str
@param label: Label taken from user input.
@rtype: str
@return: Sanitized label.
"""
(module, function, offset) = self.split_label_fuzzy(label)
label = self.parse_label(module, function, offset)
return label
def resolve_label(self, label):
"""
Resolve the memory address of the given label.
@note:
If multiple modules with the same name are loaded,
the label may be resolved at any of them. For a more precise
way to resolve functions use the base address to get the L{Module}
object (see L{Process.get_module}) and then call L{Module.resolve}.
If no module name is specified in the label, the function may be
resolved in any loaded module. If you want to resolve all functions
with that name in all processes, call L{Process.iter_modules} to
iterate through all loaded modules, and then try to resolve the
function in each one of them using L{Module.resolve}.
@type label: str
@param label: Label to resolve.
@rtype: int
@return: Memory address pointed to by the label.
@raise ValueError: The label is malformed or impossible to resolve.
@raise RuntimeError: Cannot resolve the module or function.
"""
# Split the label into module, function and offset components.
module, function, offset = self.split_label_fuzzy(label)
# Resolve the components into a memory address.
address = self.resolve_label_components(module, function, offset)
# Return the memory address.
return address
def resolve_label_components(self, module = None,
function = None,
offset = None):
"""
Resolve the memory address of the given module, function and/or offset.
@note:
If multiple modules with the same name are loaded,
the label may be resolved at any of them. For a more precise
way to resolve functions use the base address to get the L{Module}
object (see L{Process.get_module}) and then call L{Module.resolve}.
If no module name is specified in the label, the function may be
resolved in any loaded module. If you want to resolve all functions
with that name in all processes, call L{Process.iter_modules} to
iterate through all loaded modules, and then try to resolve the
function in each one of them using L{Module.resolve}.
@type module: None or str
@param module: (Optional) Module name.
@type function: None, str or int
@param function: (Optional) Function name or ordinal.
@type offset: None or int
@param offset: (Optional) Offset value.
If C{function} is specified, offset from the function.
If C{function} is C{None}, offset from the module.
@rtype: int
@return: Memory address pointed to by the label.
@raise ValueError: The label is malformed or impossible to resolve.
@raise RuntimeError: Cannot resolve the module or function.
"""
# Default address if no module or function are given.
# An offset may be added later.
address = 0
# Resolve the module.
# If the module is not found, check for the special symbol "main".
if module:
modobj = self.get_module_by_name(module)
if not modobj:
if module == "main":
modobj = self.get_main_module()
else:
raise RuntimeError("Module %r not found" % module)
# Resolve the exported function or debugging symbol.
# If all else fails, check for the special symbol "start".
if function:
address = modobj.resolve(function)
if address is None:
address = modobj.resolve_symbol(function)
if address is None:
if function == "start":
address = modobj.get_entry_point()
if address is None:
msg = "Symbol %r not found in module %s"
raise RuntimeError(msg % (function, module))
# No function, use the base address.
else:
address = modobj.get_base()
# Resolve the function in any module.
# If all else fails, check for the special symbols "main" and "start".
elif function:
for modobj in self.iter_modules():
address = modobj.resolve(function)
if address is not None:
break
if address is None:
if function == "start":
modobj = self.get_main_module()
address = modobj.get_entry_point()
elif function == "main":
modobj = self.get_main_module()
address = modobj.get_base()
else:
msg = "Function %r not found in any module" % function
raise RuntimeError(msg)
# Return the address plus the offset.
if offset:
address = address + offset
return address
def get_label_at_address(self, address, offset = None):
"""
Creates a label from the given memory address.
@warning: This method uses the name of the nearest currently loaded
module. If that module is unloaded later, the label becomes
impossible to resolve.
@type address: int
@param address: Memory address.
@type offset: None or int
@param offset: (Optional) Offset value.
@rtype: str
@return: Label pointing to the given address.
"""
if offset:
address = address + offset
modobj = self.get_module_at_address(address)
if modobj:
label = modobj.get_label_at_address(address)
else:
label = self.parse_label(None, None, address)
return label
#------------------------------------------------------------------------------
# The memory addresses of system breakpoints are be cached, since they're
# all in system libraries it's not likely they'll ever change their address
# during the lifetime of the process... I don't suppose a program could
# happily unload ntdll.dll and survive.
def __get_system_breakpoint(self, label):
try:
return self.__system_breakpoints[label]
except KeyError:
try:
address = self.resolve_label(label)
except Exception:
return None
self.__system_breakpoints[label] = address
return address
# It's in kernel32 in Windows Server 2003, in ntdll since Windows Vista.
# It can only be resolved if we have the debug symbols.
def get_break_on_error_ptr(self):
"""
@rtype: int
@return:
If present, returns the address of the C{g_dwLastErrorToBreakOn}
global variable for this process. If not, returns C{None}.
"""
address = self.__get_system_breakpoint("ntdll!g_dwLastErrorToBreakOn")
if not address:
address = self.__get_system_breakpoint(
"kernel32!g_dwLastErrorToBreakOn")
# cheat a little :)
self.__system_breakpoints["ntdll!g_dwLastErrorToBreakOn"] = address
return address
def is_system_defined_breakpoint(self, address):
"""
@type address: int
@param address: Memory address.
@rtype: bool
@return: C{True} if the given address points to a system defined
breakpoint. System defined breakpoints are hardcoded into
system libraries.
"""
if address:
module = self.get_module_at_address(address)
if module:
return module.match_name("ntdll") or \
module.match_name("kernel32")
return False
# FIXME
# In Wine, the system breakpoint seems to be somewhere in kernel32.
def get_system_breakpoint(self):
"""
@rtype: int or None
@return: Memory address of the system breakpoint
within the process address space.
Returns C{None} on error.
"""
return self.__get_system_breakpoint("ntdll!DbgBreakPoint")
# I don't know when this breakpoint is actually used...
def get_user_breakpoint(self):
"""
@rtype: int or None
@return: Memory address of the user breakpoint
within the process address space.
Returns C{None} on error.
"""
return self.__get_system_breakpoint("ntdll!DbgUserBreakPoint")
# On some platforms, this breakpoint can only be resolved
# when the debugging symbols for ntdll.dll are loaded.
def get_breakin_breakpoint(self):
"""
@rtype: int or None
@return: Memory address of the remote breakin breakpoint
within the process address space.
Returns C{None} on error.
"""
return self.__get_system_breakpoint("ntdll!DbgUiRemoteBreakin")
# Equivalent of ntdll!DbgBreakPoint in Wow64.
def get_wow64_system_breakpoint(self):
"""
@rtype: int or None
@return: Memory address of the Wow64 system breakpoint
within the process address space.
Returns C{None} on error.
"""
return self.__get_system_breakpoint("ntdll32!DbgBreakPoint")
# Equivalent of ntdll!DbgUserBreakPoint in Wow64.
def get_wow64_user_breakpoint(self):
"""
@rtype: int or None
@return: Memory address of the Wow64 user breakpoint
within the process address space.
Returns C{None} on error.
"""
return self.__get_system_breakpoint("ntdll32!DbgUserBreakPoint")
# Equivalent of ntdll!DbgUiRemoteBreakin in Wow64.
def get_wow64_breakin_breakpoint(self):
"""
@rtype: int or None
@return: Memory address of the Wow64 remote breakin breakpoint
within the process address space.
Returns C{None} on error.
"""
return self.__get_system_breakpoint("ntdll32!DbgUiRemoteBreakin")
#------------------------------------------------------------------------------
def load_symbols(self):
"""
Loads the debugging symbols for all modules in this snapshot.
Automatically called by L{get_symbols}.
"""
for aModule in self.iter_modules():
aModule.load_symbols()
def unload_symbols(self):
"""
Unloads the debugging symbols for all modules in this snapshot.
"""
for aModule in self.iter_modules():
aModule.unload_symbols()
def get_symbols(self):
"""
Returns the debugging symbols for all modules in this snapshot.
The symbols are automatically loaded when needed.
@rtype: list of tuple( str, int, int )
@return: List of symbols.
Each symbol is represented by a tuple that contains:
- Symbol name
- Symbol memory address
- Symbol size in bytes
"""
symbols = list()
for aModule in self.iter_modules():
for symbol in aModule.iter_symbols():
symbols.append(symbol)
return symbols
def iter_symbols(self):
"""
Returns an iterator for the debugging symbols in all modules in this
snapshot, in no particular order.
The symbols are automatically loaded when needed.
@rtype: iterator of tuple( str, int, int )
@return: Iterator of symbols.
Each symbol is represented by a tuple that contains:
- Symbol name
- Symbol memory address
- Symbol size in bytes
"""
for aModule in self.iter_modules():
for symbol in aModule.iter_symbols():
yield symbol
def resolve_symbol(self, symbol, bCaseSensitive = False):
"""
Resolves a debugging symbol's address.
@type symbol: str
@param symbol: Name of the symbol to resolve.
@type bCaseSensitive: bool
@param bCaseSensitive: C{True} for case sensitive matches,
C{False} for case insensitive.
@rtype: int or None
@return: Memory address of symbol. C{None} if not found.
"""
if bCaseSensitive:
for (SymbolName, SymbolAddress, SymbolSize) in self.iter_symbols():
if symbol == SymbolName:
return SymbolAddress
else:
symbol = symbol.lower()
for (SymbolName, SymbolAddress, SymbolSize) in self.iter_symbols():
if symbol == SymbolName.lower():
return SymbolAddress
def get_symbol_at_address(self, address):
"""
Tries to find the closest matching symbol for the given address.
@type address: int
@param address: Memory address to query.
@rtype: None or tuple( str, int, int )
@return: Returns a tuple consisting of:
- Name
- Address
- Size (in bytes)
Returns C{None} if no symbol could be matched.
"""
# Any module may have symbols pointing anywhere in memory, so there's
# no easy way to optimize this. I guess we're stuck with brute force.
found = None
for (SymbolName, SymbolAddress, SymbolSize) in self.iter_symbols():
if SymbolAddress > address:
continue
if SymbolAddress == address:
found = (SymbolName, SymbolAddress, SymbolSize)
break
if SymbolAddress < address:
if found and (address - found[1]) < (address - SymbolAddress):
continue
else:
found = (SymbolName, SymbolAddress, SymbolSize)
return found
#------------------------------------------------------------------------------
# XXX _notify_* methods should not trigger a scan
def _add_module(self, aModule):
"""
Private method to add a module object to the snapshot.
@type aModule: L{Module}
@param aModule: Module object.
"""
## if not isinstance(aModule, Module):
## if hasattr(aModule, '__class__'):
## typename = aModule.__class__.__name__
## else:
## typename = str(type(aModule))
## msg = "Expected Module, got %s instead" % typename
## raise TypeError(msg)
lpBaseOfDll = aModule.get_base()
## if lpBaseOfDll in self.__moduleDict:
## msg = "Module already exists: %d" % lpBaseOfDll
## raise KeyError(msg)
aModule.set_process(self)
self.__moduleDict[lpBaseOfDll] = aModule
def _del_module(self, lpBaseOfDll):
"""
Private method to remove a module object from the snapshot.
@type lpBaseOfDll: int
@param lpBaseOfDll: Module base address.
"""
try:
aModule = self.__moduleDict[lpBaseOfDll]
del self.__moduleDict[lpBaseOfDll]
except KeyError:
aModule = None
msg = "Unknown base address %d" % HexDump.address(lpBaseOfDll)
warnings.warn(msg, RuntimeWarning)
if aModule:
aModule.clear() # remove circular references
def __add_loaded_module(self, event):
"""
Private method to automatically add new module objects from debug events.
@type event: L{Event}
@param event: Event object.
"""
lpBaseOfDll = event.get_module_base()
hFile = event.get_file_handle()
## if not self.has_module(lpBaseOfDll): # XXX this would trigger a scan
if lpBaseOfDll not in self.__moduleDict:
fileName = event.get_filename()
if not fileName:
fileName = None
if hasattr(event, 'get_start_address'):
EntryPoint = event.get_start_address()
else:
EntryPoint = None
aModule = Module(lpBaseOfDll, hFile, fileName = fileName,
EntryPoint = EntryPoint,
process = self)
self._add_module(aModule)
else:
aModule = self.get_module(lpBaseOfDll)
if not aModule.hFile and hFile not in (None, 0,
win32.INVALID_HANDLE_VALUE):
aModule.hFile = hFile
if not aModule.process:
aModule.process = self
if aModule.EntryPoint is None and \
hasattr(event, 'get_start_address'):
aModule.EntryPoint = event.get_start_address()
if not aModule.fileName:
fileName = event.get_filename()
if fileName:
aModule.fileName = fileName
def _notify_create_process(self, event):
"""
Notify the load of the main module.
This is done automatically by the L{Debug} class, you shouldn't need
to call it yourself.
@type event: L{CreateProcessEvent}
@param event: Create process event.
@rtype: bool
@return: C{True} to call the user-defined handle, C{False} otherwise.
"""
self.__add_loaded_module(event)
return True
def _notify_load_dll(self, event):
"""
Notify the load of a new module.
This is done automatically by the L{Debug} class, you shouldn't need
to call it yourself.
@type event: L{LoadDLLEvent}
@param event: Load DLL event.
@rtype: bool
@return: C{True} to call the user-defined handle, C{False} otherwise.
"""
self.__add_loaded_module(event)
return True
def _notify_unload_dll(self, event):
"""
Notify the release of a loaded module.
This is done automatically by the L{Debug} class, you shouldn't need
to call it yourself.
@type event: L{UnloadDLLEvent}
@param event: Unload DLL event.
@rtype: bool
@return: C{True} to call the user-defined handle, C{False} otherwise.
"""
lpBaseOfDll = event.get_module_base()
## if self.has_module(lpBaseOfDll): # XXX this would trigger a scan
if lpBaseOfDll in self.__moduleDict:
self._del_module(lpBaseOfDll)
return True