Skip to content

Reverse Engineering

Patching a Binary to Disable ASLR Using Python

Leveraging Python's pefile package to flip the ASLR bit in a binary and save the patched file — handy for deterministic addresses while developing (and for backdooring).

3 min readOriginally on deceiveyour.team
  • python
  • PE
  • ASLR

Hey everyone,

Back today with a tip on how you can leverage the pefile package for Python to disable ASLR within a binary and save the patched file. This can make it easier to make modifications to a binary, such as backdooring it with malicious code. So without further ado, let’s dig in!

Installing our prerequisites

So before we get started, we have to make sure that you have everything installed to follow along. Simply put, you’ll need Python 2 or 3, Pip, and the pefile package. Installation of Python is outside the scope of this discussion. If you need to install Pip on Kali linux, simply run the following (note: the command below installs Pip for Python 2 and Python 3, adjust the command accordingly):

sudo apt-get update && sudo apt-get -y install python-pip python3-pip

With pip installed, we can install the pefile package:

python -m pip install -U pefile
python3 -m pip install -U pefile

Detecting ASLR within a binary

A little about ASLR

So with our dependency installed, let’s look at how we can detect ASLR within a binary. In this case, we’ll look at the ncat.exe file usually located on Kali at the path /usr/share/ncat-w32/ncat.exe.

So how does ASLR work? Windows Vista and later can have ASLR enabled on them. Executable files which contains a PE header,
such as executable binaries (.exe) and dynamic link libraries (.dll), can elect to participate in address
space layout randomization. This election is made by setting a bit (0x40) in the DllCharacteristics of the PE header fields. More about the PE file format can be seen at https://docs.microsoft.com/en-us/windows/desktop/Debug/pe-format.

The basics of what this does is, the system will choose a global image offset (most commonly selected at reboot time), and then all processes get loaded in relationship to this offset, causing them to appear at different virtual address spaces throughout the computer lifecycle.

Detecting it via automation

So with the information that 0x40 in the DllCharacteristics header field will be used to determine if ASLR is enabled, we can verify this by accessing the value in this header and performing a bitwise AND operation to see if it matches. This can be done like so:

#!/usr/bin/env python

from pefile import PE
from logging import getLogger, DEBUG
from sys import argv, exit
# installed with pip install -U colorlog
from colorlog import ColoredFormatter, StreamHandler

logger = getLogger(__name__)
logger.setLevel(DEBUG)
f = ColoredFormatter('%(log_color)s[%(levelname)s] %(message)s')
h = StreamHandler()
h.setLevel(DEBUG)
h.setFormatter(f)
logger.addHandler(h)

def get_pe_aslr_status(pe):
    DYNAMIC_BASE = 0x40
    is_aslr = pe.OPTIONAL_HEADER.DllCharacteristics & DYNAMIC_BASE
    if is_aslr:
        logger.warning('the binary has ASLR enabled!')
    else:
        logger.info('the binary does not have ASLR enabled')

if __name__ == '__main__':
    if len(argv) != 2:
        logger.error('usage: ./is_aslr_enabled.py <PE file>')
        exit(1)
    path = argv[1]
    logger.debug('opening PE file with path {path}'.format(path=path))
    pe = PE(path)
    logger.info('checking status of ASLR within the binary')
    get_pe_aslr_status(pe)

When running this on the command line, we’ll see output like this, depending on the binary we are looking at:

Disabling ASLR on affected binaries

Because we may not want ASLR, we can toggle it’s status using pefile as well. The way that we do this is using a bitwise and with the inverse of the ASLR enabled status. Luckily, Python has an easy way to get the inverse, using the tilde (~) operator. This operator is called the bitwise complement operator, and essentially calculates -x – 1

For example:

| i | ~i | | --- | --- | | 0 | -1 | | 1 | -2 | | 2 | -3 | | 3 | -4 | | 4 | -5 | | 5 | -6 |

As a result, if we use this to create the inverse, and then perform a bitwise and we will end up unsetting the value, and thus disabling ASLR. This is performed like so:

#!/usr/bin/env python

from pefile import PE
from logging import getLogger, DEBUG
from sys import argv, exit
# installed with pip install -U colorlog
from colorlog import ColoredFormatter, StreamHandler

logger = getLogger(__name__)
logger.setLevel(DEBUG)
f = ColoredFormatter('%(log_color)s[%(levelname)s] %(message)s')
h = StreamHandler()
h.setLevel(DEBUG)
h.setFormatter(f)
logger.addHandler(h)

def get_pe_aslr_status(pe):
    DYNAMIC_BASE = 0x40
    is_aslr = pe.OPTIONAL_HEADER.DllCharacteristics & DYNAMIC_BASE
    if is_aslr:
        logger.warning('the binary has ASLR enabled! disabling...')
        pe.OPTIONAL_HEADER.DllCharacteristics &= ~DYNAMIC_BASE
        return (True, pe)
    else:
        logger.info('the binary does not have ASLR enabled')
        return (False, pe)

if __name__ == '__main__':
    if len(argv) != 2:
        logger.error('usage: ./is_aslr_enabled.py <PE file>')
        exit(1)
    path = argv[1]
    logger.debug('opening PE file with path {path}'.format(path=path))
    pe = PE(path)
    logger.info('checking status of ASLR within the binary')
    (changed, pe) = get_pe_aslr_status(pe)
    if changed:
        new_path = '{orig_exe}-patched.exe'.format(orig_exe=path)
        pe.write(filename=new_path)
        logger.info('updated file written. verifying ASLR has been disabled')
        pe = PE(new_path)
        get_pe_aslr_status(pe)

And will look like this when run:

And with that, we’ve successfully disabled ASLR on our binary, and can now begin our patching process to backdoor the file.