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)