dlldiag/subcommands/graph.py
def constructGraph(logEntries):
		'''
		Constructs a directed graph from the supplied list of log entries
		'''
		
		# Create a new directed graph with support for parallel edges
		graph = nx.OrderedMultiDiGraph()
		
		# Maintain a list of function calls for which we've not yet seen a return value
		pending = []
		
		# Gather all LdrLoadDll() calls and process them last
		loadDllEntries = list([e for e in logEntries if e['function'] == 'LdrLoadDll'])
		logEntries = list([e for e in logEntries if e['function'] != 'LdrLoadDll']) + loadDllEntries
		
		# Process each log entry in turn
		for entry in logEntries:
			
			# Determine if the log entry is for the start of a function call or its return value
			if entry['type'] == 'enter':
				
				# Add the entry to our stack of pending function calls
				pending.append(entry)
				
			elif entry['type'] == 'return':
				
				# Identify which pending function call this entry represents the return value for
				match = GraphHelpers.findMatch(pending, entry)
				if match is None:
					OutputFormatting.printWarning('encountered a return value before the function call!')
					continue
				
				# If this is a LdrLoadDll() call then examine the stack trace to determine the appropriate module to treat as the caller
				# (Note that we filter out the resolved module in addition to KERNELBASE.DLL and NTDLL.DLL, to help prevent self-loops that provide little valuable information)
				if entry['function'] == 'LdrLoadDll':
					candidates = [m for m in entry['stack'] if m != entry['result'] and m.upper() not in ['C:\\WINDOWS\\SYSTEM32\\KERNELBASE.DLL', 'C:\\WINDOWS\\SYSTEM32\\NTDLL.DLL']]
					if len(candidates) > 0:
						entry['module'] = candidates[0]
				
				# Create a vertex for the calling module if we don't already have one
				if entry['module'] not in graph:
					graph.add_node(entry['module'], non_loadlibrary_calls=[])
				
				# Determine if this is a LoadLibrary function call
				if entry['function'].startswith('LoadLibrary') or entry['function'] == 'LdrLoadDll':
					
					# Create a vertex for the module resolved by the LoadLibrary() call if we don't already have one
					# (Note that this will create a vertex called "NULL" that all failed calls will have outbound edges pointing to)
					if entry['result'] not in graph:
						graph.add_node(entry['result'], non_loadlibrary_calls=[])
					
					# Create an edge between the calling module vertex and the resolved module vertex, annotated with the call details
					graph.add_edge(entry['module'], entry['result'], details=entry)
					
				else:
					
					# For all other function calls, just add an entry to the list in the metadata for the vertex
					nx.get_node_attributes(graph, 'non_loadlibrary_calls')[entry['module']].append(entry)
				
			else:
				raise RuntimeError('unsupported log entry type "{}"!'.format(entry['type']))
		
		# Print a warning if there were any function calls for which we did not encounter a return value
		if len(pending) > 0:
			for entry in pending:
				OutputFormatting.printWarning('return value not found for function call: {}'.format(entry))
		
		return graph

		Constructs a directed graph from the supplied list of log entries
		
Constructs a directed graph from the supplied list of log entries
Constructs a directed graph from the supplied list of log entries
def constructGraph(logEntries):
		graph = nx.OrderedMultiDiGraph()
		pending = []
		loadDllEntries = list([e for e in logEntries if e['function'] == 'LdrLoadDll'])
		logEntries = list([e for e in logEntries if e['function'] != 'LdrLoadDll']) + loadDllEntries
		for entry in logEntries:
			if entry['type'] == 'enter':
				pending.append(entry)
			elif entry['type'] == 'return':
				match = GraphHelpers.findMatch(pending, entry)
				if match is None:
					OutputFormatting.printWarning('encountered a return value before the function call!')
					continue
				if entry['function'] == 'LdrLoadDll':
					candidates = [m for m in entry['stack'] if m != entry['result'] and m.upper() not in ['C:\\WINDOWS\\SYSTEM32\\KERNELBASE.DLL', 'C:\\WINDOWS\\SYSTEM32\\NTDLL.DLL']]
					if len(candidates) > 0:
						entry['module'] = candidates[0]
				if entry['module'] not in graph:
					graph.add_node(entry['module'], non_loadlibrary_calls=[])
				if entry['function'].startswith('LoadLibrary') or entry['function'] == 'LdrLoadDll':
					if entry['result'] not in graph:
						graph.add_node(entry['result'], non_loadlibrary_calls=[])
					graph.add_edge(entry['module'], entry['result'], details=entry)
				else:
					nx.get_node_attributes(graph, 'non_loadlibrary_calls')[entry['module']].append(entry)
			else:
				raise RuntimeError('unsupported log entry type "{}"!'.format(entry['type']))
		if len(pending) > 0:
			for entry in pending:
				OutputFormatting.printWarning('return value not found for function call: {}'.format(entry))
		return graph
def constructGraph ( logEntries ) : graph = nx . OrderedMultiDiGraph ( ) pending = [ ] loadDllEntries = list ( [ e for e in logEntries if e [ 'function' ] == 'LdrLoadDll' ] ) logEntries = list ( [ e for e in logEntries if e [ 'function' ] != 'LdrLoadDll' ] ) + loadDllEntries for entry in logEntries : if entry [ 'type' ] == 'enter' : pending . append ( entry ) elif entry [ 'type' ] == 'return' : match = GraphHelpers . findMatch ( pending , entry ) if match is None : OutputFormatting . printWarning ( 'encountered a return value before the function call!' ) continue if entry [ 'function' ] == 'LdrLoadDll' : candidates = [ m for m in entry [ 'stack' ] if m != entry [ 'result' ] and m . upper ( ) not in [ 'C:\\WINDOWS\\SYSTEM32\\KERNELBASE.DLL' , 'C:\\WINDOWS\\SYSTEM32\\NTDLL.DLL' ] ] if len ( candidates ) > 0 : entry [ 'module' ] = candidates [ 0 ] if entry [ 'module' ] not in graph : graph . add_node ( entry [ 'module' ] , non_loadlibrary_calls = [ ] ) if entry [ 'function' ] . startswith ( 'LoadLibrary' ) or entry [ 'function' ] == 'LdrLoadDll' : if entry [ 'result' ] not in graph : graph . add_node ( entry [ 'result' ] , non_loadlibrary_calls = [ ] ) graph . add_edge ( entry [ 'module' ] , entry [ 'result' ] , details = entry ) else : nx . get_node_attributes ( graph , 'non_loadlibrary_calls' ) [ entry [ 'module' ] ] . append ( entry ) else : raise RuntimeError ( 'unsupported log entry type "{}"!' . format ( entry [ 'type' ] ) ) if len ( pending ) > 0 : for entry in pending : OutputFormatting . printWarning ( 'return value not found for function call: {}' . format ( entry ) ) return graph
Constructs a directed graph from the supplied list of log entries
Constructs a directed graph from the supplied list of log entries
'''
		Constructs a directed graph from the supplied list of log entries
		''' # Create a new directed graph with support for parallel edges # Maintain a list of function calls for which we've not yet seen a return value # Gather all LdrLoadDll() calls and process them last # Process each log entry in turn # Determine if the log entry is for the start of a function call or its return value # Add the entry to our stack of pending function calls # Identify which pending function call this entry represents the return value for # If this is a LdrLoadDll() call then examine the stack trace to determine the appropriate module to treat as the caller # (Note that we filter out the resolved module in addition to KERNELBASE.DLL and NTDLL.DLL, to help prevent self-loops that provide little valuable information) # Create a vertex for the calling module if we don't already have one # Determine if this is a LoadLibrary function call # Create a vertex for the module resolved by the LoadLibrary() call if we don't already have one # (Note that this will create a vertex called "NULL" that all failed calls will have outbound edges pointing to) # Create an edge between the calling module vertex and the resolved module vertex, annotated with the call details # For all other function calls, just add an entry to the list in the metadata for the vertex # Print a warning if there were any function calls for which we did not encounter a return value
logEntries