Skip to content

Instantly share code, notes, and snippets.

@michalbednarski
Created April 3, 2016 09:20
Show Gist options
  • Save michalbednarski/c225c968765d22affdd88f224d5ff266 to your computer and use it in GitHub Desktop.
Save michalbednarski/c225c968765d22affdd88f224d5ff266 to your computer and use it in GitHub Desktop.
Find conflicts with PRoot loader, termux/termux-packages#189
from elftools.elf.elffile import ELFFile
# Expected architecture for ELFs
EXPECTED_MACHINE = "EM_ARM"
# Paths
LOADER_FILE = "loader" # point to "~/termux/proot/src/src/loader/loader"
LINKER_FILE = "linker" # copy from "/system/bin/linker" possibly check with multiple devices
# TODO: glob + try-except NotInterestingElfException
EXEC_FILES = [
"/data/data/com.termux/files/usr/bin/php",
"/data/data/com.termux/files/usr/libexec/gcc/arm-linux-androideabi/5.3.0/cc1plus"
]
# From arch.h
EXEC_PIC_ADDRESS = 0x0f000000
INTERP_PIC_ADDRESS = 0x1f000000
# From execve/enter.c
PAGE_SIZE = 4096
PAGE_MASK = ~(PAGE_SIZE - 1)
class NotInterestingElfException(Exception):
"""
Exception raised by getElfPages when ELF file appears to be not interesting for us
(Not for ARM or not executable)
"""
pass
def getElfPages(path, picOffset):
"""
Get used pages for given ELF file
"""
usedPages = set()
with open(path, "rb") as fi:
elf = ELFFile(fi)
if elf.header["e_machine"] != EXPECTED_MACHINE:
raise NotInterestingElfException("Not matching e_machine %s in %s" % (elf.header["e_machine"], path))
# Check if this is PIC executable
# (This is required for use with Android linker anyway,
# but static executables can be non-PIC)
if elf.header["e_type"] == "ET_DYN":
if picOffset < 0:
raise Exception("PIC executable but no picOffset specified for %s" % path)
offset = picOffset
elif elf.header["e_type"] == "ET_EXEC":
offset = 0
else:
raise NotInterestingElfException("Unexpected e_type %s in %s" % (elf.header["e_type"], path))
# Iterate over segments
for seg in elf.iter_segments():
segHeader = seg.header
if segHeader["p_type"] == "PT_LOAD":
# print(segHeader)
assert segHeader["p_memsz"] >= segHeader["p_filesz"]
start_address = segHeader["p_vaddr"] & PAGE_MASK
end_address = (segHeader["p_vaddr"] + segHeader["p_memsz"] + PAGE_SIZE) & PAGE_MASK
start_address += offset
end_address += offset
usedPages |= set(range(start_address / PAGE_SIZE, end_address / PAGE_SIZE))
return frozenset(usedPages)
if __name__ == "__main__":
linkerPages = getElfPages(LINKER_FILE, INTERP_PIC_ADDRESS)
loaderPages = getElfPages(LOADER_FILE, -1) # Must be non-PIC
if not loaderPages.isdisjoint(linkerPages):
print("Loader overlaps linker")
for execFile in EXEC_FILES:
execPages = getElfPages(execFile, EXEC_PIC_ADDRESS)
if not loaderPages.isdisjoint(execPages):
print("Loader overlaps executable in " + execFile)
if not linkerPages.isdisjoint(execPages):
print("Linker overlaps executable in " + execFile)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment