Skip to content

Instantly share code, notes, and snippets.

@0181532686cf4a31163be0bf3e6bb6732bf
Last active July 18, 2022 02:33
Show Gist options
  • Save 0181532686cf4a31163be0bf3e6bb6732bf/f51579ae8d93c8657a5564aefc2ffbca to your computer and use it in GitHub Desktop.
Save 0181532686cf4a31163be0bf3e6bb6732bf/f51579ae8d93c8657a5564aefc2ffbca to your computer and use it in GitHub Desktop.
#! /usr/bin/env python
#-*-coding: utf-8 -*-
import sys
import ctypes
import struct
ELF_MAGIC = [0x7F, ord('E'), ord('L'), ord('F')]
PT_INTERP = 3
ELFCLASS32 = 1
ELFCLASS64 = 2
def string_compat(s):
return s.decode('ascii') if not isinstance(s, str) else s
def sized_read(format, handle):
value = struct.unpack(format, handle.read(struct.calcsize(format)))
return value[0] if len(format) == 1 else value
def extract_interpreter(elf):
# read header
ei_ident = sized_read('16B', elf)
ei_magic = ei_ident[:4]
ei_class, ei_data, ei_version, ei_pad = ei_ident[4:8]
# validate initial header
if not all([ ei_magic[i] == b for i, b in enumerate(ELF_MAGIC) ]):
raise RuntimeError('Executable is not ELF')
if ei_class not in [ ELFCLASS32, ELFCLASS64 ]:
raise RuntimeError('Invalid ELF class')
# skip file type, machine, version
sized_read('HHI', elf)
# continue parsing Elf32/64 header:
# get program and section headers info
if ei_class == ELFCLASS32:
ei_entry, e_phoff, e_shoff, e_flags, e_ehsize, e_phentsize, e_phnum = sized_read('IIIIHHH', elf)
elif ei_class == ELFCLASS64:
ei_entry, e_phoff, e_shoff, e_flags, e_ehsize, e_phentsize, e_phnum = sized_read('QQQIHHH', elf)
else:
raise RuntimeError('Invalid ELF class')
# iterate sections of program header
for i in range(0, e_phnum):
elf.seek(e_phoff + e_phentsize * i)
# parse section
if ei_class == ELFCLASS32:
p_type, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz, p_flags, p_align = sized_read('IIIIIIII', elf)
elif ei_class == ELFCLASS64:
p_type, p_flags, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz, p_align = sized_read('IIQQQQQQ', elf)
else:
raise RuntimeError('Invalid ELF class')
# interpreter section found
if p_type == PT_INTERP:
elf.seek(p_offset)
return string_compat(elf.read(p_filesz))
return None
def have_compatible_glibc():
process_namespace = ctypes.CDLL(None)
try:
gnu_get_libc_version = process_namespace.gnu_get_libc_version
# Call gnu_get_libc_version, which returns a string like "2.5".
gnu_get_libc_version.restype = ctypes.c_char_p
return string_compat(gnu_get_libc_version())
except AttributeError:
# Symbol doesn't exist -> therefore, we are not linked to
# glibc.
return False
def guess_runtime():
with open(sys.executable, 'rb') as elf:
print('Python {}'.format(sys.version))
env = extract_interpreter(elf)
if env:
print('Interpreter extracted: {}'.format(env))
if 'musl' in env:
print('Running on musl')
else:
gtest = have_compatible_glibc()
if gtest:
print('Running on glibc version: {}'.format(gtest))
else:
print('Not linked against glibc')
else:
print('Failed to extract interpreter. Static python?')
if __name__ == '__main__':
guess_runtime()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment