Python PE parsing module for analyzing Portable Executable (PE) files with comprehensive header, section, and directory entry support
—
Database of ordinal to symbol name mappings for common Windows DLLs. The ordlookup package provides resolution of function names from ordinal numbers when symbolic information is not available.
Look up function names from ordinal numbers for supported DLLs.
def ordLookup(libname, ord_val, make_name=False):
"""
Look up symbol name for given ordinal in specified library.
Args:
libname (bytes): DLL name (case-insensitive)
ord_val (int): Ordinal number to look up
make_name (bool): If True, generate formatted name when lookup fails
Returns:
bytes: Function name if found, formatted ordinal if make_name=True,
or None if not found and make_name=False
"""
def formatOrdString(ord_val):
"""
Format ordinal number as standardized string.
Args:
ord_val (int): Ordinal number
Returns:
bytes: Formatted ordinal string (e.g., b"ord123")
"""The ordlookup package includes mappings for these common Windows DLLs:
import ordlookup
# Look up function names by ordinal
dll_name = b"oleaut32.dll"
ordinal = 2
function_name = ordlookup.ordLookup(dll_name, ordinal)
if function_name:
print(f"Ordinal {ordinal} in {dll_name.decode()}: {function_name.decode()}")
else:
print(f"Ordinal {ordinal} not found in {dll_name.decode()}")
# Generate formatted name if lookup fails
formatted_name = ordlookup.ordLookup(dll_name, 9999, make_name=True)
print(f"Unknown ordinal formatted: {formatted_name.decode()}")import pefile
import ordlookup
def resolve_import_ordinals(pe):
"""Resolve ordinal imports using ordlookup database."""
if not hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
return
print("Import Analysis with Ordinal Resolution:")
print("-" * 50)
for entry in pe.DIRECTORY_ENTRY_IMPORT:
dll_name = entry.dll
print(f"\nDLL: {dll_name.decode('utf-8')}")
for imp in entry.imports:
if imp.import_by_ordinal:
# Try to resolve ordinal to function name
resolved_name = ordlookup.ordLookup(dll_name, imp.ordinal)
if resolved_name:
print(f" Ordinal {imp.ordinal}: {resolved_name.decode('utf-8')}")
else:
print(f" Ordinal {imp.ordinal}: <unknown>")
else:
# Regular named import
if imp.name:
print(f" Function: {imp.name.decode('utf-8')}")
# Usage
with pefile.PE('executable.exe') as pe:
resolve_import_ordinals(pe)import ordlookup
def explore_ordinal_database():
"""Explore available ordinal mappings."""
print("Available Ordinal Databases:")
print("=" * 40)
for dll_name, ord_dict in ordlookup.ords.items():
dll_str = dll_name.decode('utf-8')
print(f"\n{dll_str}:")
print(f" Functions: {len(ord_dict)}")
# Show ordinal range
if ord_dict:
min_ord = min(ord_dict.keys())
max_ord = max(ord_dict.keys())
print(f" Ordinal range: {min_ord} - {max_ord}")
# Show first few functions
print(" Sample functions:")
for ord_num in sorted(ord_dict.keys())[:5]:
func_name = ord_dict[ord_num].decode('utf-8')
print(f" {ord_num}: {func_name}")
if len(ord_dict) > 5:
print(f" ... and {len(ord_dict) - 5} more")
# Usage
explore_ordinal_database()import ordlookup
def resolve_with_fallback(dll_name, ordinal):
"""Resolve ordinal with multiple fallback strategies."""
# Try direct lookup
result = ordlookup.ordLookup(dll_name, ordinal)
if result:
return result.decode('utf-8'), 'database'
# Check case variations of DLL name
dll_variations = [
dll_name.lower(),
dll_name.upper(),
dll_name.lower().replace(b'.dll', b'') + b'.dll'
]
for dll_var in dll_variations:
result = ordlookup.ordLookup(dll_var, ordinal)
if result:
return result.decode('utf-8'), 'case_variant'
# Generate formatted ordinal as fallback
formatted = ordlookup.formatOrdString(ordinal)
return formatted.decode('utf-8'), 'generated'
# Usage example
test_cases = [
(b"oleaut32.dll", 2), # Known mapping
(b"OLEAUT32.DLL", 2), # Case variation
(b"kernel32.dll", 1), # Unknown DLL
(b"ws2_32.dll", 999) # Unknown ordinal
]
print("Ordinal Resolution with Fallback:")
print("-" * 40)
for dll, ordinal in test_cases:
result, method = resolve_with_fallback(dll, ordinal)
print(f"{dll.decode():<15} ord {ordinal:<3}: {result:<20} ({method})")import pefile
import ordlookup
def complete_import_analysis(pe_file):
"""Complete import analysis with ordinal resolution."""
print(f"Complete Import Analysis: {pe_file}")
print("=" * 60)
with pefile.PE(pe_file) as pe:
if not hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
print("No imports found")
return
total_imports = 0
resolved_ordinals = 0
for entry in pe.DIRECTORY_ENTRY_IMPORT:
dll_name = entry.dll
dll_str = dll_name.decode('utf-8')
print(f"\n{dll_str}:")
print("-" * len(dll_str))
dll_imports = 0
dll_resolved = 0
for imp in entry.imports:
dll_imports += 1
total_imports += 1
if imp.import_by_ordinal:
# Try to resolve ordinal
resolved_name = ordlookup.ordLookup(dll_name, imp.ordinal)
if resolved_name:
function_name = resolved_name.decode('utf-8')
resolved_ordinals += 1
dll_resolved += 1
status = "✓"
else:
function_name = f"<unknown ordinal {imp.ordinal}>"
status = "✗"
print(f" {status} Ordinal {imp.ordinal:3d}: {function_name}")
else:
# Named import
if imp.name:
function_name = imp.name.decode('utf-8')
print(f" ○ Function: {function_name}")
else:
print(f" ? Import at 0x{imp.address:08x}")
print(f" Summary: {dll_imports} imports, {dll_resolved} ordinals resolved")
# Overall statistics
print(f"\nOverall Statistics:")
print("-" * 20)
print(f"Total imports: {total_imports}")
print(f"Resolved ordinals: {resolved_ordinals}")
if total_imports > 0:
resolution_rate = (resolved_ordinals / total_imports) * 100
print(f"Resolution rate: {resolution_rate:.1f}%")
# Usage
complete_import_analysis('executable.exe')import ordlookup
# Example of how to extend the database (conceptual)
def add_custom_ordinals():
"""Example of extending ordinal database with custom mappings."""
# This shows how you could add custom ordinal mappings
# Note: This modifies the runtime database, not persistent storage
custom_dll = b"custom.dll"
custom_mappings = {
1: b"CustomFunction1",
2: b"CustomFunction2",
100: b"SpecialExport"
}
# Add to ordlookup database
ordlookup.ords[custom_dll] = custom_mappings
print("Added custom ordinal mappings")
# Test the new mappings
for ordinal, expected_name in custom_mappings.items():
resolved = ordlookup.ordLookup(custom_dll, ordinal)
if resolved == expected_name:
print(f"✓ Custom ordinal {ordinal}: {resolved.decode()}")
else:
print(f"✗ Failed to resolve custom ordinal {ordinal}")
# Note: This is for demonstration - actual extension would require
# modifying the ordlookup package files or creating a custom lookup systemInstall with Tessl CLI
npx tessl i tessl/pypi-pefile