Skip to content
Please note that GitHub no longer supports Internet Explorer.

We recommend upgrading to the latest Microsoft Edge, Google Chrome, or Firefox.

Learn more

Instantly share code, notes, and snippets.

@wdormann wdormann/checkaslr.py
Last active Jan 24, 2020

Embed
What would you like to do?
Check for running processes on Windows that have components that do not utilize ASLR
#!/usr/bin/env python
'''
Utility to check for processes running with non-ASLR-compatible components.
Run with Administrative privileges to get visibility into all processes.
(1a) psutil: https://pypi.org/project/psutil/
Installed via PIP
-OR-
(1b) Sysinternals ListDLLs: https://docs.microsoft.com/en-us/sysinternals/downloads/listdlls
Installed in the Windows PATH
(2a) pefile: https://github.com/erocarrera/pefile
Installed via PIP or just by placing pefile.py in the same directory as this script
-OR-
(2b) Microsoft dumpbin.exe
If testing a 64-bit version of Windows, then 64-bit Python will get most accurate results.
32-bit Python on 64-bit Windows will lack visibility into directories such as SYSWOW64
'''
import subprocess
import os
import sys
import csv
import struct
import platform
have_pefile = False
have_psutil = False
if struct.calcsize('P') == 4:
# 32-bit Python
if platform.machine() != 'x86':
# Not 32-bit Windows
print('You are running 32-bit Python on 64-bit Windows. Results may not be complete!')
try:
import pefile
have_pefile = True
except:
print('Python pefile not found. Falling back to using dumpbin.exe...')
try:
import psutil
have_psutil = True
except:
print('Python psutil not found. Falling back to using ListDLLs.exe...')
IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x0040
IMAGE_FILE_RELOCS_STRIPPED = 0x0001
processes = {}
all_libraries = []
library_headers = {}
non_aslr_libraries = []
non_aslr_reasons = {}
library_imagebase = {}
results = []
csvoutput = 'checkaslr.csv'
def check_aslr(libpath):
DYNAMICBASE = False
StrippedReloc = False
dotnet = False
wibu = False
imagebase = 0
aslrcompat = True
reason = ''
if have_pefile:
try:
pe = pefile.PE(libpath, fast_load=True)
pe.parse_data_directories([pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG']])
if pe.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR']].VirtualAddress != 0:
# .NET binary. These are relocated similarly to "Force ASLR", even without a relocation table
dotnet = True
if pe.sections[0].Name.decode('utf-8') == u'__wibu00':
wibu = True
if pe.FILE_HEADER.Characteristics & IMAGE_FILE_RELOCS_STRIPPED:
StrippedReloc = True
if pe.OPTIONAL_HEADER.DllCharacteristics & IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE:
DYNAMICBASE = True
if pe.OPTIONAL_HEADER.ImageBase:
imagebase = hex(pe.OPTIONAL_HEADER.ImageBase).replace('L', '')
if DYNAMICBASE and StrippedReloc:
if dotnet:
aslrcompat = False
reason = '.NET code with relocations stripped is only relocated on Windows 8 or newer'
else:
aslrcompat = False
reason = 'Relocations stripped'
elif DYNAMICBASE and wibu:
aslrcompat = False
reason = 'WIBU-protected code may not be relocated.'
elif dotnet:
aslrcompat = True
reason = '.NET code is always rebased on Windows 8 or later'
elif not DYNAMICBASE:
aslrcompat = False
reason = 'No Dynamic Base'
except:
# Non-PE, bad permissions, etc...
pass
else:
# no pefile. Fall back to dumpbin, which isn't as effective/complete
try:
header_info = subprocess.check_output(['dumpbin', '/headers', libpath.encode('utf-8')])
except subprocess.CalledProcessError as e:
header_info = e.output
if b'FILE HEADER VALUES' and b' image base (' not in header_info:
# Not a library or otherwise not readable
reason = 'dumpbin error'
for line in header_info.splitlines():
line = line.strip()
if line == b'Relocations stripped':
StrippedReloc = True
reason = 'Relocations stripped'
elif line == b'Dynamic base':
DYNAMICBASE = True
elif b' image base (' in line:
imagebase = '0x%s' % line.split()[0].decode('utf-8')
elif b'RVA [size] of COM Descriptor Directory' in line:
comdir = line.split()[0].decode('utf-8')
if comdir != '0':
dotnet = True
elif line == b'__wibu00 name':
wibu = True
aslrcompat = False
reason = 'WIBU-protected code may not be relocated.'
if StrippedReloc and DYNAMICBASE:
aslrcompat = False
elif dotnet == True:
aslrcompat = True
reason = '.NET code is always rebased on Windows 8 or later'
elif not DYNAMICBASE and reason == '':
reason = 'No Dynamic base'
aslrcompat = False
return (aslrcompat, imagebase, reason)
def get_modules():
if have_psutil:
for proc in psutil.process_iter():
try:
currentproc = proc.name()
currentpid = proc.pid
currentprocpath = ' '.join(proc.cmdline())
except:
# Some processes aren't friendly
continue
processes[currentpid] = {}
processes[currentpid]['proc'] = currentproc
processes[currentpid]['libraries'] = []
processes[currentpid]['procpath'] = currentprocpath
try:
for library in proc.memory_maps():
if library.path not in all_libraries:
all_libraries.append(library.path)
processes[currentpid]['libraries'].append(library.path)
except:
# You can't get all maps
pass
else:
# Fall back to using listdlls.exe
dlls = subprocess.check_output(['listdlls'])
#print(dlls)
for line in dlls.splitlines():
if b' pid: ' in line:
# Current PID
splitline = line.split(b' pid: ')
currentpid = splitline[-1].decode('utf-8')
currentproc = splitline[0]
#print(currentpid)
#print(currentproc)
processes[currentpid] = {}
processes[currentpid]['proc'] = currentproc.decode('utf-8')
processes[currentpid]['libraries'] = []
elif b'Command line: ' in line:
# Command line
currentprocpath = line.split(b'Command line: ')[-1].decode('utf-8')
processes[currentpid]['procpath'] = currentprocpath
#print(currentprocpath)
elif b'0x' in line:
# Loaded library
#print(line)
pathparts = line.split()[2:]
if len(pathparts) == 1:
path = pathparts[0].decode('utf-8')
else:
path = b' '.join(pathparts).decode('utf-8')
# print(path)
if path not in processes[currentpid]['libraries']:
# ListDLLs sometimes shows duplicates
processes[currentpid]['libraries'].append(path)
if path not in all_libraries:
all_libraries.append(path)
return all_libraries
all_libraries = get_modules()
for library in all_libraries:
#(aslr_compat, reason) = aslr_library(library)
(aslr_compat, imagebase, reason) = check_aslr(library)
if not aslr_compat:
non_aslr_libraries.append(library)
non_aslr_reasons[library] = reason
library_imagebase[library] = imagebase
for pid in processes:
reason = ''
aslr_incompat = False
for library in processes[pid]['libraries']:
if library in non_aslr_libraries:
aslr_incompat = True
if aslr_incompat and processes[pid]['proc'].lower() != 'listdlls.exe':
print('*** %s (%s) is not ASLR compliant! ***' % (processes[pid]['proc'], pid))
print('Command line: %s' % processes[pid]['procpath'])
print('Faulting module(s):')
for library in processes[pid]['libraries']:
if library in non_aslr_libraries:
print('%s %s (%s)' %(library, library_imagebase[library], non_aslr_reasons[library]))
result = {}
result['proc'] = processes[pid]['proc']
result['pid'] = pid
result['cmdline'] = processes[pid]['procpath']
result['module'] = library
result['imagebase'] = library_imagebase[library]
result['reason'] = non_aslr_reasons[library]
results.append(result)
print('')
print('Writing output to %s ...' % csvoutput)
if sys.version_info.major > 2:
# Avoid newlines in CSV: https://bugs.python.org/issue30324
try:
with open(csvoutput, 'w', newline='') as csvfile:
writer = csv.writer(csvfile, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
writer.writerow(['Process', 'PID', 'Command line', 'Module', 'Image Base', 'Reason'])
for result in results:
writer.writerow([result['proc'], result['pid'], result['cmdline'], result['module'],result['imagebase'],result['reason']])
except:
print('Error writing to %s' % csvoutput)
else:
try:
with open(csvoutput, 'wb') as csvfile:
writer = csv.writer(csvfile, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
writer.writerow(['Process', 'PID', 'Command line', 'Module', 'Image Base', 'Reason'])
for result in results:
writer.writerow([result['proc'], result['pid'], result['cmdline'], result['module'],result['imagebase'],result['reason']])
except:
print('Error writing to %s' % csvoutput)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.