Last active
August 4, 2021 02:57
-
-
Save pudquick/d71e151156d72676fb22e5438136627f to your computer and use it in GitHub Desktop.
Calling a non-external local C function (dlsym does not resolve it) with ctypes in python (or by using an address directly)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// helper.h | |
// two functions in this dylib - helper and helpersecret | |
void helper(); | |
void helpersecret(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// helper.c | |
// Compile into example dylib with helper.c and helper.h in the same directory: | |
// clang -dynamiclib helper.c -o libhelper.dylib | |
#include <stdio.h> | |
#include "helper.h" | |
// helper is externally visible by default | |
void helper () { | |
puts("helper"); | |
} | |
// helpersecret is marked to stay local | |
__attribute__ ((visibility("hidden"))) void helpersecret () { | |
puts("you called a secret"); | |
helper(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Running nm against the resulting libhelper.dylib and you can see: | |
# | |
# nm libhelper.dylib | |
# 0000000000008008 d __dyld_private | |
# 0000000000003f30 T _helper | |
# 0000000000003f50 t _helpersecret | |
# U _puts | |
# U dyld_stub_binder | |
# T = resolvable, t = will not resolve | |
# Proof in python: | |
import ctypes | |
libhelper = ctypes.CDLL('libhelper.dylib') | |
# >>> libhelper | |
# <CDLL 'libhelper.dylib', handle 7f8c93c043a0 at 0x7f8c980b4b50> | |
# >>> libhelper.helper | |
# <_FuncPtr object at 0x7f8cb814f040> | |
# >>> libhelper.helper.restype = None | |
# >>> libhelper.helper() | |
# helper | |
# >>> libhelper.helpersecret | |
# Traceback (most recent call last): | |
# File "<stdin>", line 1, in <module> | |
# File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/ctypes/__init__.py", line 386, in __getattr__ | |
# func = self.__getitem__(name) | |
# File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/ctypes/__init__.py", line 391, in __getitem__ | |
# func = self._FuncPtr((name_or_ordinal, self)) | |
# AttributeError: dlsym(0x7f8c93c043a0, helpersecret): symbol not found |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import subprocess | |
import ctypes | |
# dylib we want to work with | |
lib_path = 'libhelper.dylib' | |
# grab raw symbol map for native architecture | |
raw_symbols = subprocess.check_output(['/usr/bin/nm', lib_path]).decode('utf-8').rstrip().split('\n') | |
# filter nm output for one known resolvable symbol and the desired to calculate offsets | |
known_public_symbol = '_helper' | |
desired_symbol = '_helpersecret' | |
filter_names = [known_public_symbol, desired_symbol] | |
filtered_symbols = [x.split(' ', 3) for x in raw_symbols if x.split(' ', 3)[-1] in filter_names] | |
syms = dict() | |
for x in filtered_symbols: | |
syms[x[-1]] = int('0x'+x[0], 0) | |
# calculate the offset | |
offset = syms[desired_symbol] - syms[known_public_symbol] | |
# load the library | |
lib_obj = ctypes.CDLL(lib_path) | |
# get the address of the public symbol (we strip off the leading '_' for the real name) | |
public_addr = ctypes.cast(lib_obj[known_public_symbol[1:]], ctypes.c_void_p).value | |
private_addr = public_addr + offset | |
# create a C function connection from a raw address | |
# The arguments to CFUNCTYPE are: restype, argtype1 (ex: ctypes.c_int, etc.), argtype2, etc. | |
# helpersecret has a void return type (so restype = None) and no args (don't provide anything) | |
priv_func = ctypes.CFUNCTYPE(None)(private_addr) | |
# >>> priv_func | |
# <CFunctionType object at 0x7f8c98225c40> | |
# >>> priv_func() | |
# you called a secret | |
# helper |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment