DEV Community

Cover image for Hacking Python functions by changing their source code
pikoTutorial
pikoTutorial

Posted on • Originally published at pikotutorial.com

Hacking Python functions by changing their source code

Welcome to the next pikoTutorial!

Changing function behavior by modifying its implementation manually is obvious, but can we somehow mess around with the implementations of the functions at runtime of the application? Let's organize this process in 3 steps:

  • obtaining function's source code at runtime
  • converting string with source code to a callable object
  • modify function's source code before calling it

Obtaining function's source code at runtime

Let's first learn how to obtain a source code of the function:

# Import inspect module
import inspect
# Define some callback function
def function():
    print('Do something')

source_code = inspect.getsource(function)
print(source_code)
Enter fullscreen mode Exit fullscreen mode

Output:

def callback():
    print('Do something')
Enter fullscreen mode Exit fullscreen mode

Converting string with source code to a callable object

Now let's see how to convert some arbitrary Python code provided in a string to a callable Python object:

# Source code that we want to execute
source_code = 'print("Hello from the inside of the string!")'
# Wrap the source code into a function definition, so that it can be accessed by name
function_name = 'print_hello'
function_definition = f'def {function_name}():\n    {source_code}'

namespace = {}

# Execute code with a function definition within the given namespace, so that the function definition is created
exec(function_definition, namespace)
# Retrieve function from the namespace and save to a callable variable
print_hello = namespace[function_name]
# Call the function
print_hello()
Enter fullscreen mode Exit fullscreen mode

Output:

Hello from the inside of the string!
Enter fullscreen mode Exit fullscreen mode

Modifying function's source code before calling it

Now let's implement a function which takes as an input a function pointer and returns a callable object with the modified source code:

import inspect

def get_hacked_function(function):
    # Get the source code of the given function
    original_function_source_code = inspect.getsource(function)
    # Append a new line to the function source code
    modified_function_source_code = f'{original_function_source_code}    print("You didn\'t expect me here!")'
    # Call the function within the namespace
    namespace = {}
    exec(modified_function_source_code, namespace)
    # Parse function name by taking everything what's between "def " and "(" at the first line
    function_name = original_function_source_code.split('(')[0].split()[1]
    # Retrieve modified function
    modified_function = namespace[function_name]
    # Return modified function
    return modified_function
Enter fullscreen mode Exit fullscreen mode

It's time to test it!

# This is the function passed as an input
def original_function():
    print("Hello")
# Call our hacking function
hacked_function = get_hacked_function(original_function)
# Call the modified function
hacked_function() 
Enter fullscreen mode Exit fullscreen mode

Output:

Hello
You didn't expect me here!
Enter fullscreen mode Exit fullscreen mode

Note for beginners: please keep in mind that such experiments are done mainly for educational purposes. Using the exec() function can introduce serious security issues, so it is not recommended to use it in a production environment. If you need to modify the behavior of a function whose source code you don't have access to, consider using function decorators instead. Always be cautious and ensure you fully understand the security implications before using exec().

Top comments (0)