diff options
author | 2014-07-30 15:40:36 -0400 | |
---|---|---|
committer | 2014-07-30 15:40:36 -0400 | |
commit | c206aab32dce7db944996628f160a3dcbaeb6c6f (patch) | |
tree | 808d1e0bd0877f367182b1ffc072fa9c4f9a5da0 /pocs | |
parent | configure.ac: replace tabs with 4 spaces (diff) | |
download | elfix-c206aab32dce7db944996628f160a3dcbaeb6c6f.tar.gz elfix-c206aab32dce7db944996628f160a3dcbaeb6c6f.tar.bz2 elfix-c206aab32dce7db944996628f160a3dcbaeb6c6f.zip |
Split misc/ into misc/ for production and poc/ for experimental stuff.
Diffstat (limited to 'pocs')
25 files changed, 2287 insertions, 0 deletions
diff --git a/pocs/change-interp/.gitignore b/pocs/change-interp/.gitignore new file mode 100644 index 0000000..2460008 --- /dev/null +++ b/pocs/change-interp/.gitignore @@ -0,0 +1 @@ +!Makefile diff --git a/pocs/change-interp/Makefile b/pocs/change-interp/Makefile new file mode 100644 index 0000000..c5d8fcb --- /dev/null +++ b/pocs/change-interp/Makefile @@ -0,0 +1,7 @@ +all: change-interp + +%: %.c + gcc -o $@ $^ -lelf + +clean: + rm -rf change-interp diff --git a/pocs/change-interp/change-interp.c b/pocs/change-interp/change-interp.c new file mode 100644 index 0000000..3c21c26 --- /dev/null +++ b/pocs/change-interp/change-interp.c @@ -0,0 +1,105 @@ + +#include <stdio.h> // printf +#include <stdlib.h> // EXIT_FAILURE +#include <error.h> // error +#include <string.h> // strncpy + +#include <gelf.h> // elf_* and gelf_* + +#include <sys/types.h> // open +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> // close + + +int main( int argc, char *argv[]) +{ + int fd, cmd; + char *scn_name; + + Elf *elf; + GElf_Ehdr ehdr; + Elf_Scn *scn; + GElf_Shdr shdr; + Elf_Data *data; + GElf_Dyn dyn; + + if(argc != 3) + error(EXIT_FAILURE, 0, "Usage: %s <elf> <ld.so>", argv[0]); + + if(elf_version(EV_CURRENT) == EV_NONE) + error(EXIT_FAILURE, 0, "Library out of date."); + + if((fd = open(argv[1], O_RDWR)) == -1) + error(EXIT_FAILURE, 0, "Failed open file."); + + if((elf = elf_begin(fd, ELF_C_RDWR_MMAP, elf)) != NULL) + { + if(gelf_getehdr(elf,&ehdr) != NULL) + { + scn = NULL; + while((scn = elf_nextscn(elf, scn)) != NULL) + { + gelf_getshdr(scn, &shdr); + scn_name = elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name); + + if(strcmp(scn_name, ".interp")) + continue; + + printf("Section name: %s\n", elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name)); + + if((data = elf_getdata(scn, data)) != NULL) + { + printf("Old ld.so: %s\n", (char *)data->d_buf); + printf("Data size: %zu\n", data->d_size); + /* + printf( "Data:\t\t%s\nType:\t\t%d\nSize:\t\t%lu\n" + "Off:\t\t%lu\nAlign:\t\t%lu\nVersion:\t%u\n", + (char *)data->d_buf, + data->d_type, + data->d_size, + data->d_off, + data->d_align, + data->d_version + ); + */ + + if(data->d_size >= strlen(argv[2])) + { + memset(data->d_buf, 0, data->d_size); + strncpy(data->d_buf, argv[2], data->d_size); + + if(!gelf_update_shdr(scn, &shdr)) + { + elf_end(elf); + close(fd); + error(EXIT_FAILURE, 0, "\tELF ERROR: gelf_update_shdr(): %s", elf_errmsg(elf_errno())); + } + } + else + { + elf_end(elf); + close(fd); + error(EXIT_FAILURE, 0, "\tld.so path too long: max=%zu", data->d_size); + } + + printf( "New ld.so: %s\n", (char *)data->d_buf); + /* + printf( "Data:\t\t%s\nType:\t\t%d\nSize:\t\t%lu\n" + "Off:\t\t%lu\nAlign:\t\t%lu\nVersion:\t%u\n", + (char *)data->d_buf, + data->d_type, + data->d_size, + data->d_off, + data->d_align, + data->d_version + ); + */ + } + } + } + } + + elf_end(elf); + close(fd); +} diff --git a/pocs/eclass/pax-utils.eclass b/pocs/eclass/pax-utils.eclass new file mode 100644 index 0000000..719e15b --- /dev/null +++ b/pocs/eclass/pax-utils.eclass @@ -0,0 +1,218 @@ +# Copyright 1999-2014 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: /var/cvsroot/gentoo-x86/eclass/pax-utils.eclass,v 1.22 2014/07/11 08:21:58 ulm Exp $ + +# @ECLASS: pax-utils.eclass +# @MAINTAINER: +# The Gentoo Linux Hardened Team <hardened@gentoo.org> +# @AUTHOR: +# Original Author: Kevin F. Quinn <kevquinn@gentoo.org> +# Modifications for bug #365825, @ ECLASS markup: Anthony G. Basile <blueness@gentoo.org> +# Modifications for bug #431092: Anthony G. Basile <blueness@gentoo.org> +# @BLURB: functions to provide pax markings +# @DESCRIPTION: +# +# This eclass provides support for manipulating PaX markings on ELF binaries, +# whether the system is using legacy PT_PAX markings or the newer XATTR_PAX. +# The eclass wraps the use of paxctl-ng, paxctl, set/getattr and scanelf utilities, +# deciding which to use depending on what's installed on the build host, and +# whether we're working with PT_PAX, XATTR_PAX or both. +# +# To control what markings are made, set PAX_MARKINGS in /etc/portage/make.conf +# to contain either "PT", "XT" or "none". The default is to attempt both +# PT_PAX and XATTR_PAX. + +if [[ -z ${_PAX_UTILS_ECLASS} ]]; then +_PAX_UTILS_ECLASS=1 + +# @ECLASS-VARIABLE: PAX_MARKINGS +# @DESCRIPTION: +# Control which markings are made: +# PT = PT_PAX markings, XT = XATTR_PAX markings +# Default to PT markings. +PAX_MARKINGS=${PAX_MARKINGS:="PT"} + +# @FUNCTION: pax-mark +# @USAGE: <flags> {<ELF files>} +# @RETURN: Shell true if we succeed, shell false otherwise +# @DESCRIPTION: +# Marks <ELF files> with provided PaX <flags> +# +# Flags are passed directly to the utilities unchanged +# +# p: disable PAGEEXEC P: enable PAGEEXEC +# e: disable EMUTRAMP E: enable EMUTRAMP +# m: disable MPROTECT M: enable MPROTECT +# r: disable RANDMMAP R: enable RANDMMAP +# s: disable SEGMEXEC S: enable SEGMEXEC +# +# Default flags are 'PeMRS', which are the most restrictive settings. Refer +# to http://pax.grsecurity.net/ for details on what these flags are all about. +# +# Please confirm any relaxation of restrictions with the Gentoo Hardened team. +# Either ask on the gentoo-hardened mailing list, or CC/assign hardened@g.o on +# the bug report. +pax-mark() { + + local f # loop over paxables + local flags # pax flags + local pt_fail=0 pt_failures="" # record PT_PAX failures + local xt_fail=0 xt_failures="" # record xattr PAX marking failures + local ret=0 # overal return code of this function + + # Only the actual PaX flags and z are accepted + # 1. The leading '-' is optional + # 2. -C -c only make sense for paxctl, but are unnecessary + # because we progressively do -q -qc -qC + # 3. z is allowed for the default + + flags="${1//[!zPpEeMmRrSs]}" + [[ "${flags}" ]] || return 0 + shift + + # z = default. For XATTR_PAX, the default is no xattr field at all + local dodefault="" + [[ "${flags//[!z]}" ]] && dodefault="yes" + + if has PT ${PAX_MARKINGS}; then + + #First try paxctl -> this might try to create/convert program headers + if type -p paxctl > /dev/null; then + einfo "PT PaX marking -${flags} with paxctl" + _pax_list_files einfo "$@" + for f in "$@"; do + # First, try modifying the existing PAX_FLAGS header + paxctl -q${flags} "${f}" && continue + # Second, try creating a PT_PAX header (works on ET_EXEC) + # Even though this is less safe, most exes need it, eg bug #463170 + paxctl -qC${flags} "${f}" && continue + # Third, try stealing the (unused under PaX) PT_GNU_STACK header + paxctl -qc${flags} "${f}" && continue + pt_fail=1 + pt_failures="${pt_failures} ${f}" + done + + #Next try paxctl-ng -> this will not create/convert any program headers + elif type -p paxctl-ng > /dev/null && paxctl-ng -L ; then + einfo "PT PaX marking -${flags} with paxctl-ng" + flags="${flags//z}" + _pax_list_files einfo "$@" + for f in "$@"; do + [[ ${dodefault} == "yes" ]] && paxctl-ng -L -z "${f}" + [[ "${flags}" ]] || continue + paxctl-ng -L -${flags} "${f}" && continue + pt_fail=1 + pt_failures="${pt_failures} ${f}" + done + + #Finally fall back on scanelf + elif type -p scanelf > /dev/null && [[ ${PAX_MARKINGS} != "none" ]]; then + einfo "Fallback PaX marking -${flags} with scanelf" + _pax_list_files einfo "$@" + scanelf -Xxz ${flags} "$@" + + #We failed to set PT_PAX flags + elif [[ ${PAX_MARKINGS} != "none" ]]; then + pt_failures="$*" + pt_fail=1 + fi + + if [[ ${pt_fail} == 1 ]]; then + elog "Failed to set PT_PAX markings -${flags} for:" + _pax_list_files elog ${pt_failures} + ret=1 + fi + fi + + if has XT ${PAX_MARKINGS}; then + + flags="${flags//z}" + + #First try paxctl-ng + if type -p paxctl-ng > /dev/null && paxctl-ng -l ; then + einfo "XT PaX marking -${flags} with paxctl-ng" + _pax_list_files einfo "$@" + for f in "$@"; do + [[ ${dodefault} == "yes" ]] && paxctl-ng -d "${f}" + [[ "${flags}" ]] || continue + paxctl-ng -l -${flags} "${f}" && continue + xt_fail=1 + xt_failures="${tx_failures} ${f}" + done + + #Next try setfattr + elif type -p setfattr > /dev/null; then + [[ "${flags//[!Ee]}" ]] || flags+="e" # bug 447150 + einfo "XT PaX marking -${flags} with setfattr" + _pax_list_files einfo "$@" + for f in "$@"; do + [[ ${dodefault} == "yes" ]] && setfattr -x "user.pax.flags" "${f}" + setfattr -n "user.pax.flags" -v "${flags}" "${f}" && continue + xt_fail=1 + xt_failures="${tx_failures} ${f}" + done + + #We failed to set XATTR_PAX flags + elif [[ ${PAX_MARKINGS} != "none" ]]; then + xt_failures="$*" + xt_fail=1 + fi + + if [[ ${xt_fail} == 1 ]]; then + elog "Failed to set XATTR_PAX markings -${flags} for:" + _pax_list_files elog ${xt_failures} + ret=1 + fi + fi + + # [[ ${ret} == 1 ]] && elog "Executables may be killed by PaX kernels." + + return ${ret} +} + +# @FUNCTION: list-paxables +# @USAGE: {<files>} +# @RETURN: Subset of {<files>} which are ELF executables or shared objects +# @DESCRIPTION: +# Print to stdout all of the <files> that are suitable to have PaX flag +# markings, i.e., filter out the ELF executables or shared objects from a list +# of files. This is useful for passing wild-card lists to pax-mark, although +# in general it is preferable for ebuilds to list precisely which ELFS are to +# be marked. Often not all the ELF installed by a package need remarking. +# @EXAMPLE: +# pax-mark -m $(list-paxables ${S}/{,usr/}bin/*) +list-paxables() { + file "$@" 2> /dev/null | grep -E 'ELF.*(executable|shared object)' | sed -e 's/: .*$//' +} + +# @FUNCTION: host-is-pax +# @RETURN: Shell true if the build process is PaX enabled, shell false otherwise +# @DESCRIPTION: +# This is intended for use where the build process must be modified conditionally +# depending on whether the host is PaX enabled or not. It is not intedened to +# determine whether the final binaries need PaX markings. Note: if procfs is +# not mounted on /proc, this returns shell false (e.g. Gentoo/FBSD). +host-is-pax() { + grep -qs ^PaX: /proc/self/status +} + + +# INTERNAL FUNCTIONS +# ------------------ +# +# These functions are for use internally by the eclass - do not use +# them elsewhere as they are not supported (i.e. they may be removed +# or their function may change arbitratily). + +# Display a list of things, one per line, indented a bit, using the +# display command in $1. +_pax_list_files() { + local f cmd + cmd=$1 + shift + for f in "$@"; do + ${cmd} " ${f}" + done +} + +fi diff --git a/pocs/elf-manipulate/.gitignore b/pocs/elf-manipulate/.gitignore new file mode 100644 index 0000000..2460008 --- /dev/null +++ b/pocs/elf-manipulate/.gitignore @@ -0,0 +1 @@ +!Makefile diff --git a/pocs/elf-manipulate/Makefile b/pocs/elf-manipulate/Makefile new file mode 100644 index 0000000..265cf25 --- /dev/null +++ b/pocs/elf-manipulate/Makefile @@ -0,0 +1,7 @@ +all: clear-dt-path parse-elf print-sections remove-ptpax + +%: %.c + gcc -o $@ $^ -lelf + +clean: + rm -rf clear-dt-path parse-elf print-sections remove-ptpax diff --git a/pocs/elf-manipulate/clear-dt-path.c b/pocs/elf-manipulate/clear-dt-path.c new file mode 100644 index 0000000..ae5ff9a --- /dev/null +++ b/pocs/elf-manipulate/clear-dt-path.c @@ -0,0 +1,80 @@ + +#include <stdio.h> // printf +#include <stdlib.h> // EXIT_FAILURE +#include <error.h> // error + +#include <gelf.h> // elf_* and gelf_* + +#include <sys/types.h> // open +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> // close + + +int main( int argc, char *argv[]) +{ + int fd, cmd; + size_t i, n; + char *p; + + Elf *arf, *elf; + GElf_Ehdr ehdr; + Elf_Scn *scn; + GElf_Shdr shdr; + Elf_Data *data; + GElf_Dyn dyn; + + if(elf_version(EV_CURRENT) == EV_NONE) + error(EXIT_FAILURE, 0, "Library out of date."); + + if(argc != 2) + error(EXIT_FAILURE, 0, "Usage: %s <filename>", argv[0]); + + if((fd = open(argv[1], O_RDONLY)) == -1) + error(EXIT_FAILURE, 0, "Failed open file."); + + + cmd = ELF_C_READ; + + if((arf = elf_begin(fd, cmd, (Elf *)0)) == NULL) + error(EXIT_FAILURE, 0, "Failed open elf: %s", elf_errmsg ( -1)); + + + while((elf = elf_begin(fd, cmd, arf)) != NULL) + { + if(gelf_getehdr(elf,&ehdr) != NULL) + { + scn = NULL; + while((scn = elf_nextscn(elf, scn)) != NULL) + { + gelf_getshdr(scn, &shdr); + + if(shdr.sh_type != SHT_DYNAMIC) + continue; + + printf("Section name: %s\n", elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name)); + + data = NULL; + while((data = elf_getdata(scn, data)) != NULL) + { + if(data != NULL) + for( i=0; gelf_getdyn(data, i, &dyn) != NULL; i++) + { + if(dyn.d_tag == DT_RPATH) + printf("DT_RPATH found\n"); + if(dyn.d_tag == DT_RUNPATH) + printf("DT_RUNPATH found\n"); + } + } + } + } + + cmd = elf_next(elf); + elf_end(elf); + + } + + elf_end(arf); + + close(fd); +} diff --git a/pocs/elf-manipulate/parse-elf.c b/pocs/elf-manipulate/parse-elf.c new file mode 100644 index 0000000..1ba8024 --- /dev/null +++ b/pocs/elf-manipulate/parse-elf.c @@ -0,0 +1,262 @@ + +#include <stdio.h> // printf +#include <stdlib.h> // EXIT_FAILURE +#include <error.h> // error + +#include <gelf.h> // elf_* and gelf_* + +#include <sys/types.h> // open +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> // close + + +int main( int argc, char *argv[]) +{ + int fd, cmd; + size_t i, n; + char *p; + + Elf *arf, *elf; + Elf_Arhdr *arhdr; + GElf_Ehdr ehdr; + GElf_Phdr phdr; + Elf_Scn *scn; + GElf_Shdr shdr; + Elf_Data *data; + + if(elf_version(EV_CURRENT) == EV_NONE) + error(EXIT_FAILURE, 0, "Library out of date."); + + if(argc != 2) + error(EXIT_FAILURE, 0, "Usage: %s <filename>", argv[0]); + + if((fd = open(argv[1], O_RDONLY)) == -1) + error(EXIT_FAILURE, 0, "Failed open file."); + + + cmd = ELF_C_READ; + + if((arf = elf_begin(fd, cmd, (Elf *)0)) == NULL) + error(EXIT_FAILURE, 0, "Failed open elf: %s", elf_errmsg ( -1)); + + + switch(elf_kind(arf)) + { + case ELF_K_AR: + printf("This is an archive.\n"); + arhdr = elf_getarhdr(arf); + /* + printf("\n ********** ARCHIVE HEADER ********** \n"); + printf( "Name:\t\t%s\nDate:\t\t%lu\nUID:\t\t%d\nGID:\t\t%d\n" + "Mode:\t\t%d\n:Size:\t\t%lu\nRaw:\t\t%s\n\n ", + arhdr->ar_name, + arhdr->ar_date, + arhdr->ar_uid, + arhdr->ar_gid, + arhdr->ar_mode, + arhdr->ar_size, + arhdr->ar_rawname + ); + */ + break; + case ELF_K_COFF: + printf("This is a COFF.\n"); break; + case ELF_K_ELF: + printf("This is an ELF.\n"); break; + default: + case ELF_K_NONE: + printf("This is an unknown.\n"); break; + } + + while((elf = elf_begin(fd, cmd, arf)) != NULL) + { + if(gelf_getehdr(elf,&ehdr) != NULL) + { + printf("\n ********** HEADER ********** \n"); + switch(ehdr.e_type) + { + case ET_NONE: printf("This is a ET_NONE Type.\n"); break; + case ET_REL: printf("This is a ET_REL Type.\n"); break; + case ET_EXEC: printf("This is a ET_EXEC Type.\n"); break; + case ET_DYN: printf("This is a ET_DYN Type.\n"); break; + case ET_CORE: printf("This is a ET_CORE Type.\n"); break; + case ET_NUM: printf("This is a ET_NUM Type.\n"); break; + case ET_LOOS: printf("This is a ET_LOOS Type.\n"); break; + case ET_HIOS: printf("This is a ET_HIOS Type.\n"); break; + case ET_LOPROC: printf("This is a ET_LOPROC Type.\n"); break; + case ET_HIPROC: printf("This is a ET_HIPROC Type.\n"); break; + } + + printf( "Ident:\t\t%d\nType:\t\t%d\nMachine:\t%d\nVersion:\t%d\n" + "Entry:\t\t%lu\nPHoff:\t\t%lu\nSHoff:\t\t%lu\n" + "Flags:\t\t%d\nEHSize:\t\t%d\nPHentsize:\t%d\n" + "PHnum:\t\t%d\nSHentsize\t%d\nSHnum\t\t%d\nSHstrndx\t%d\n\n", + ehdr.e_ident[EI_NIDENT], + ehdr.e_type, + ehdr.e_machine, + ehdr.e_version, + + ehdr.e_entry, + ehdr.e_phoff, + ehdr.e_shoff, + + ehdr.e_flags, + ehdr.e_ehsize, + ehdr.e_phentsize, + + ehdr.e_phnum, + ehdr.e_shentsize, + ehdr.e_shnum, + ehdr.e_shstrndx + ); + + elf_getphdrnum(elf, &n); + printf("NOTE: elf_getphdrnum=%lu ehdr.e_phnum=%d\n", n, ehdr.e_phnum ); + + for (i = 0; i < ehdr.e_phnum; ++i) + { + if(gelf_getphdr(elf, i, &phdr) != &phdr) + error(EXIT_FAILURE, 0, "Failed getphdr: %s", elf_errmsg ( -1)); + + printf("\n ********** PROGRAM HEADER TABLE ENTRY ********** \n"); + switch(phdr.p_type) + { + case PT_NULL: printf("This is a PT_NULL type\n"); break; + case PT_LOAD: printf("This is a PT_LOAD type\n"); break; + case PT_DYNAMIC: printf("This is a PT_DYNAMIC type\n"); break; + case PT_INTERP: printf("This is a PT_INTERP type\n"); break; + case PT_NOTE: printf("This is a PT_NOTE type\n"); break; + case PT_SHLIB: printf("This is a PT_SHLIB type\n"); break; + case PT_PHDR: printf("This is a PT_PHDR type\n"); break; + case PT_TLS: printf("This is a PT_TLS type\n"); break; + case PT_NUM: printf("This is a PT_NUM type\n"); break; + case PT_LOOS: printf("This is a PT_LOOS type\n"); break; + case PT_GNU_EH_FRAME: printf("This is a PT_GNU_EH_FRAME type\n"); break; + case PT_GNU_STACK: printf("This is a PT_GNU_STACK type\n"); break; + case PT_GNU_RELRO: printf("This is a PT_GNU_RELRO type\n"); break; + case PT_PAX_FLAGS: printf("This is a PT_PAX_FLAGS type\n"); break; + case PT_LOSUNW: printf("This is a PT_LOSUNW type\n"); break; + //case PT_SUNWBSS: printf("This is a PT_SUNWBSS type\n"); break; + case PT_SUNWSTACK: printf("This is a PT_SUNWSTACK type\n"); break; + case PT_HISUNW: printf("This is a PT_HISUNW type\n"); break; + //case PT_HIOS: printf("This is a PT_HIOS type\n"); break; + case PT_LOPROC: printf("This is a PT_LOPROC type\n"); break; + case PT_HIPROC: printf("This is a PT_HIPROC type\n"); break; + } + + printf("Flags:\t\t"); + if( phdr.p_flags & PF_R ) printf("R"); + if( phdr.p_flags & PF_W ) printf("W"); + if( phdr.p_flags & PF_X ) printf("X"); + printf("\n"); + + printf( "Type:\t\t%d\nFlags:\t\t%d\nOffset:\t\t%lu\nVaddr:\t\t%lu\nPaddr:\t\t%lu\n" + "Filesz:\t\t%lu\nMemsz:\t\t%lu\nAlign:\t\t%lu\n", + phdr.p_type, + phdr.p_flags, + phdr.p_offset, + phdr.p_vaddr, + phdr.p_paddr, + phdr.p_filesz, + phdr.p_memsz, + phdr.p_align + ); + + printf("\n\n"); + } + + scn = NULL; + while((scn = elf_nextscn(elf, scn)) != NULL) + { + gelf_getshdr(scn, &shdr); + printf("\n ********** SECTION ********** \n"); + + printf("Section name: %s\n", elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name)); + + switch(shdr.sh_type) + { + case SHT_DYNAMIC: printf("This is a SHT_DYNAMIC type\n"); break; + case SHT_DYNSYM: printf("This is a SHT_DYNSYM type\n"); break; + case SHT_HASH: printf("This is a SHT_HASH type\n"); break; + case SHT_NOBITS: printf("This is a SHT_NOBITS type\n"); break; + case SHT_NOTE: printf("This is a SHT_NOTE type\n"); break; + case SHT_NULL: printf("This is a SHT_NULL type\n"); break; + case SHT_PROGBITS: printf("This is a SHT_PROGBITS type\n"); break; + case SHT_REL: printf("This is a SHT_REL type\n"); break; + case SHT_RELA: printf("This is a SHT_RELA type\n"); break; + case SHT_STRTAB: printf("This is a SHT_STRTAB type\n"); break; + case SHT_SYMTAB: printf("This is a SHT_SYMTAB type\n"); break; + default: printf("This is an unknown section type\n"); break; + } + + printf( + "Name:\t\t%d\nType:\t\t%d\nFlags:\t\t%lu\n" + "Addr:\t\t%lu\nOffset:\t\t%lu\nSize:\t\t%lu\n" + "Link:\t\t%d\nInfo:\t\t%d\nAddrAlign:\t%lu\nEntsize:\t%lu\n", + shdr.sh_name, + shdr.sh_type, + shdr.sh_flags, + shdr.sh_addr, + shdr.sh_offset, + shdr.sh_size, + shdr.sh_link, + shdr.sh_info, + shdr.sh_addralign, + shdr.sh_entsize + ); + + if(shdr.sh_type == SHT_NOBITS) + continue; + + data = NULL; + while((data = elf_getdata(scn, data)) != NULL) + { + printf("\n ***** DATA ***** \n"); + printf( "Data:\t\t%s\nType:\t\t%d\nSize:\t\t%lu\n" + "Off:\t\t%lu\nAlign:\t\t%lu\nVersion:\t%u\n", + (char *)data->d_buf, + data->d_type, + data->d_size, + data->d_off, + data->d_align, + data->d_version + ); + } + printf("\n\n"); + } + + + //Print out data in .shstrtab section + if((scn = elf_getscn(elf, ehdr.e_shstrndx)) == NULL) + error(EXIT_FAILURE, 0, "getscn() failed: %s.", elf_errmsg(-1)); + + if(gelf_getshdr( scn, &shdr ) != &shdr) + error(EXIT_FAILURE, 0, "getshdr(ehdr.e_shstrndx) failed: %s.", elf_errmsg(-1)); + + printf(" .shstrab: size=%jd\n", (uintmax_t)shdr.sh_size); + + data = NULL; + n = 0; + while( n < shdr.sh_size && (data = elf_getdata(scn, data)) != NULL ) + { + p = (char *)data->d_buf ; + while(p < (char *)data->d_buf + data->d_size) + { + printf("%c", *p); + n++; + p++; + if(!(n%16)) printf("\n"); + } + } + printf("\n"); + + } + cmd = elf_next(elf); + elf_end(elf); + } + + elf_end(arf); + + close(fd); +} diff --git a/pocs/elf-manipulate/print-sections.c b/pocs/elf-manipulate/print-sections.c new file mode 100644 index 0000000..ba8d4ee --- /dev/null +++ b/pocs/elf-manipulate/print-sections.c @@ -0,0 +1,94 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <error.h> + +#include <gelf.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + + +int main( int argc, char *argv[]) +{ + int fd, cmd; + size_t i; + char shname[1024]; + + Elf *arf, *elf; + GElf_Ehdr ehdr; + Elf_Scn *scn; + GElf_Shdr shdr; + Elf_Data *data; + + if(elf_version(EV_CURRENT) == EV_NONE) + error(EXIT_FAILURE, 0, "Library out of date."); + + if(argc != 2) + error(EXIT_FAILURE, 0, "Usage: %s <filename>", argv[0]); + + if((fd = open(argv[1], O_RDONLY)) == -1) + error(EXIT_FAILURE, 0, "Failed open file."); + + + cmd = ELF_C_READ; + + if((arf = elf_begin(fd, cmd, (Elf *)0)) == NULL) + error(EXIT_FAILURE, 0, "Failed open elf: %s", elf_errmsg ( -1)); + + + while((elf = elf_begin(fd, cmd, arf)) != NULL) + { + if(gelf_getehdr(elf,&ehdr) != NULL) + { + scn = NULL; + while((scn = elf_nextscn(elf, scn)) != NULL) + { + gelf_getshdr(scn, &shdr); + + strcpy(shname, elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name)); + printf("%s\n", shname); + + /* + if( !strcmp(shname, ".bss") || !strcmp(shname, ".tbss") ) + continue; + */ + if(shdr.sh_type == SHT_NOBITS) + continue; + + data = NULL; + while((data = elf_getdata(scn, data)) != NULL) + { + printf( "Type:\t\t%d\nSize:\t\t%lu\n" + "Off:\t\t%lu\nAlign:\t\t%lu\nVersion:\t%u\n", + data->d_type, + data->d_size, + data->d_off, + data->d_align, + data->d_version + ); + + for(i = 0; i < (int)data->d_size; i++) + { + printf("%2x ", (uint8_t)((char *)(data->d_buf))[i]); + if((i+1)%16 == 0) + printf("\n"); + } + } + + printf("\n\n"); + } + } + + cmd = elf_next(elf); + elf_end(elf); + + } + + elf_end(arf); + + close(fd); +} diff --git a/pocs/elf-manipulate/remove-ptpax.c b/pocs/elf-manipulate/remove-ptpax.c new file mode 100644 index 0000000..ba441a5 --- /dev/null +++ b/pocs/elf-manipulate/remove-ptpax.c @@ -0,0 +1,103 @@ +/* + remove-ptpax.c: this file is part of the elfix package + Copyright (C) 2013 Anthony G. Basile + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <gelf.h> + +#ifndef PT_PAX_FLAGS + #define PT_PAX_FLAGS 0x65041580 +#endif + +int +remove_ptpax(int fd) +{ + Elf *elf; + GElf_Phdr phdr; + size_t i, phnum; + + if(elf_version(EV_CURRENT) == EV_NONE) + { + printf("\tELF ERROR: Library out of date.\n"); + return EXIT_FAILURE; + } + + if((elf = elf_begin(fd, ELF_C_RDWR_MMAP, NULL)) == NULL) + { + printf("\tELF ERROR: elf_begin() fail: %s\n", elf_errmsg(elf_errno())); + return EXIT_FAILURE; + } + + if(elf_kind(elf) != ELF_K_ELF) + { + elf_end(elf); + printf("\tELF ERROR: elf_kind() fail: this is not an elf file.\n"); + return EXIT_FAILURE; + } + + elf_getphdrnum(elf, &phnum); + + for(i=0; i<phnum; i++) + { + if(gelf_getphdr(elf, i, &phdr) != &phdr) + { + elf_end(elf); + printf("\tELF ERROR: gelf_getphdr(): %s\n", elf_errmsg(elf_errno())); + return EXIT_FAILURE; + } + + if(phdr.p_type == PT_PAX_FLAGS) + { + phdr.p_type = PT_NULL; + if(!gelf_update_phdr(elf, i, &phdr)) + { + elf_end(elf); + printf("\tELF ERROR: gelf_update_phdr(): %s", elf_errmsg(elf_errno())); + return EXIT_FAILURE; + } + } + } + + elf_end(elf); + return EXIT_SUCCESS; +} + + +int +main( int argc, char *argv[]) +{ + int fd; + + if(argc != 2) + { + fprintf(stderr, "Usage: %s <filename>\n", argv[0]) ; + exit(EXIT_SUCCESS); + } + + if((fd = open(argv[1], O_RDWR)) < 0) + { + printf("\topen(O_RDWR) failed: cannot change PT_PAX flags\n"); + exit(EXIT_FAILURE); + } + else + exit(remove_ptpax(fd)); + +} diff --git a/pocs/ldd/ldd.py b/pocs/ldd/ldd.py new file mode 100755 index 0000000..447614e --- /dev/null +++ b/pocs/ldd/ldd.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python + +import os, sys +import re, glob +from optparse import OptionParser + +from elftools import __version__ +from elftools.common.exceptions import ELFError +from elftools.common.py3compat import bytes2str +from elftools.elf.elffile import ELFFile +from elftools.elf.dynamic import DynamicSection +from elftools.elf.descriptions import describe_ei_class + +class ReadElf(object): + def __init__(self, file): + """ file: stream object with the ELF file to read + """ + self.elffile = ELFFile(file) + + + def elf_class(self): + """ Return the ELF Class + """ + header = self.elffile.header + e_ident = header['e_ident'] + return describe_ei_class(e_ident['EI_CLASS']) + + def dynamic_dt_needed(self): + """ Return a list of the DT_NEEDED + """ + dt_needed = [] + for section in self.elffile.iter_sections(): + if not isinstance(section, DynamicSection): + continue + + for tag in section.iter_tags(): + if tag.entry.d_tag == 'DT_NEEDED': + dt_needed.append(bytes2str(tag.needed)) + #sys.stdout.write('\t%s\n' % bytes2str(tag.needed) ) + + return dt_needed + + +def ldpaths(ld_so_conf='/etc/ld.so.conf'): + """ Generate paths to search for libraries from ld.so.conf. Recursively + parse included files. We assume correct syntax and the ld.so.cache + is in sync with ld.so.conf. + """ + with open(ld_so_conf, 'r') as path_file: + lines = path_file.read() + lines = re.sub('#.*', '', lines) # kill comments + lines = list(re.split(':+|\s+|\t+|\n+|,+', lines)) # man 8 ldconfig + + paths = [] + include_globs = [] + for i in range(0,len(lines)): + if lines[i] == '': + continue + if lines[i] == 'include': + f = lines[i + 1] + include_globs.append(f) + continue + if lines[i] not in include_globs: + real_path = os.path.realpath(lines[i]) + if os.path.exists(real_path): + paths.append(real_path) + + include_files = [] + for g in include_globs: + include_files = include_files + glob.glob('/etc/' + g) + for c in include_files: + paths = paths + ldpaths(os.path.realpath(c)) + + paths = list(set(paths)) + paths.sort() + return paths + + +# We cache the dependencies for speed. The structure is +# { ELFClass : { SONAME : library, ... }, ELFClass : ... } +cache = {} + +def dynamic_dt_needed_paths( dt_needed, eclass, paths): + """ Search library paths for the library file corresponding + to the given DT_NEEDED and ELF Class. + """ + global cache + if not eclass in cache: + cache[eclass] = {} + + dt_needed_paths = {} + for n in dt_needed: + if n in cache[eclass].keys(): + dt_needed_paths[n] = cache[eclass][n] + else: + for p in paths: + lib = p + os.sep + n + if os.path.exists(lib): + with open(lib, 'rb') as file: + try: + readlib = ReadElf(file) + if eclass == readlib.elf_class(): + dt_needed_paths[n] = lib + cache[eclass][n] = lib + except ELFError as ex: + sys.stderr.write('ELF error: %s\n' % ex) + sys.exit(1) + + return dt_needed_paths + + +def all_dynamic_dt_needed_paths(f, paths): + """ Return a dictionary of all the DT_NEEDED => Library Paths for + a given ELF file obtained by recursively following linkage. + """ + with open(f, 'rb') as file: + try: + readelf = ReadElf(file) + eclass = readelf.elf_class() + # This needs to be iterated until we traverse the entire linkage tree + dt_needed = readelf.dynamic_dt_needed() + dt_needed_paths = dynamic_dt_needed_paths(dt_needed, eclass, paths) + for n, lib in dt_needed_paths.items(): + dt_needed_paths = dict(all_dynamic_dt_needed_paths(lib, paths), **dt_needed_paths) + except ELFError as ex: + sys.stderr.write('ELF error: %s\n' % ex) + sys.exit(1) + + return dt_needed_paths + + +SCRIPT_DESCRIPTION = 'Print shared library dependencies' +VERSION_STRING = '%%prog: based on pyelftools %s' % __version__ + +def main(): + optparser = OptionParser( + usage='usage: %prog <elf-file>', + description=SCRIPT_DESCRIPTION, + add_help_option=False, # -h is a real option of readelf + prog='ldd.py', + version=VERSION_STRING) + optparser.add_option('-h', '--help', + action='store_true', dest='help', + help='Display this information') + options, args = optparser.parse_args() + + if options.help or len(args) == 0: + optparser.print_help() + sys.exit(0) + + paths = ldpaths() + + for f in args: + if len(args) > 1: + sys.stdout.write('%s : \n' % f) + all_dt_needed_paths = all_dynamic_dt_needed_paths(f, paths) + for n, lib in all_dt_needed_paths.items(): + sys.stdout.write('\t%s => %s\n' % (n, lib)) + +if __name__ == '__main__': + main() diff --git a/pocs/link-maps/link_map.py b/pocs/link-maps/link_map.py new file mode 100755 index 0000000..5b0e822 --- /dev/null +++ b/pocs/link-maps/link_map.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python +# +# LinkMap.py: this file is part of the elfix package +# Copyright (C) 2011 Anthony G. Basile +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import re +import portage + +class LinkMap: + + def __init__(self): + """ Put all the NEEDED.ELF.2 files for all installed packages + into a dictionary of the form + + { pkg : line_from_NEEDED.ELF.2, ... } + + where the line has the following form: + + echo "${arch:3};${obj};${soname};${rpath};${needed}" >> \ + "${PORTAGE_BUILDDIR}"/build-info/NEEDED.ELF.2 + + See /usr/lib/portage/bin/misc-functions.sh ~line 520 + """ + + vardb = portage.db[portage.root]["vartree"].dbapi + + self.pkgs = [] + self.pkgs_needed = {} + + for pkg in vardb.cpv_all(): + needed = vardb.aux_get(pkg, ['NEEDED.ELF.2'])[0].strip() + if needed: # Some packages have no NEEDED.ELF.2 + self.pkgs.append(pkg) + for line in re.split('\n', needed): + self.pkgs_needed.setdefault(pkg,[]).append(re.split(';', line)) + + + def get_object_needed(self): + """ Return object_needed dictionary which has structure + + { + abi1 : { full_path_to_ELF_object : [ soname1, soname2, ... ], ... }, + abi2 : { full_path_to_ELF_object : [ soname1, soname2, ... ], ... }, + .... + } + + Here the sonames were obtained from the ELF object by scanelf -nm + (like readelf -d) during emerge. + """ + object_needed = {} + + for pkg in self.pkgs: + for link in self.pkgs_needed[pkg]: + abi = link[0] + elf = link[1] + sonames = re.split(',', link[4]) + object_needed.setdefault(abi,{}).update({elf:sonames}) + + return object_needed + + + def get_libraries(self): + """ Return library2soname dictionary which has structure + + { full_path_to_library : (soname, abi), ... } + + and its inverse which has structure + + { (soname, abi) : full_path_to_library, ... } + """ + library2soname = {} + soname2library = {} + + for pkg in self.pkgs: + for link in self.pkgs_needed[pkg]: + abi = link[0] + elf = link[1] + soname = link[2] + if soname: #no soname => executable + library2soname[elf] = (soname,abi) + soname2library[(soname,abi)] = elf + + return ( library2soname, soname2library ) + + + def get_soname_needed(self, object_needed, library2soname ): + """ Return soname_needed dictionary which has structure: + + { + abi1: { soname: [ soname1, soname2, ... ], .... }, + abi2: { soname: [ soname1, soname2, ... ], .... }, + } + + Here the soname1, soname2,... were obtained from soname's corresponding + ELF object by scanelf -n during emerge. + """ + soname_needed = {} + + for abi in object_needed: + for elf in object_needed[abi]: + try: + (soname, abi_check) = library2soname[elf] + assert abi == abi_check # We should get the same abi associated with the soname + soname_needed.setdefault(abi,{}).update({soname:object_needed[abi][elf]}) + except KeyError: + continue # no soname, its probably an executable + + return soname_needed + + + def expand_linkings(self, object_needed, soname2library): + """ Expands the object_needed dictionary which has structure + + { + abi1 : { full_path_to_ELF_object : [ soname1, soname2, ... ], ... }, + abi2 : { full_path_to_ELF_object : [ soname1, soname2, ... ], ... }, + .... + } + + such that the soname's are traced all the way to the end of + the link chain. Here the sonames should be the same as those + obtained from the ELF object by ldd. + """ + for abi in object_needed: + for elf in object_needed[abi]: + while True: + found_new_soname = False + for so in object_needed[abi][elf]: # For all the first links ... + try: + for sn in object_needed[abi][soname2library[(so,abi)]]: # go to the next links ... + if sn in object_needed[abi][elf]: # skip if already included ... + continue + if not (sn,abi) in soname2library: # skip if vdso ... + continue + + # This appends to the object_needed and soname_needed lists. No copy was + # done so its the same lists in memory for both, and its modified for both. + + object_needed[abi][elf].append(sn) # otherwise collapse it back into + found_new_soname = True # first links of the chain. + + except KeyError: # Not all nodes in the chain have a next node + continue + + if not found_new_soname: # We're done, that last iteration found + break # no new nodes + + + def get_object_reverse_linkings(self, object_linkings): + """ Return object_reverse_linkings dictionary which has structure + + { + abi1 : { soname : [ path_to_elf1, path_to_elf2, ... ], ... }, + abi2 : { soname : [ path_to_elf3, path_to_elf4, ... ], ... }, + .... + } + """ + object_reverse_linkings = {} + + for abi in object_linkings: + for elf in object_linkings[abi]: + for soname in object_linkings[abi][elf]: + object_reverse_linkings.setdefault(abi,{}).setdefault(soname,[]).append(elf) + + return object_reverse_linkings + + + def get_maps(self): + """ Generate the full forward and reverse links using the above functions """ + + # After get_object_needed() and get_soname_needed(), both object_linkings and + # soname_linkings are only one step into the entire link chain. + + object_linkings = self.get_object_needed() + ( library2soname, soname2library ) = self.get_libraries() + soname_linkings = self.get_soname_needed( object_linkings, library2soname ) + + # After the appending in expand_linkings(), forward_linkings and soname_linkings + # have been extended through the entire chain of linking. expand_linkings() is + # a "side-effect" function, so we note it here. + self.expand_linkings( soname_linkings, soname2library ) + object_reverse_linkings = self.get_object_reverse_linkings( object_linkings ) + + return ( object_linkings, object_reverse_linkings, library2soname, soname2library ) + diff --git a/pocs/link-maps/link_map_test b/pocs/link-maps/link_map_test new file mode 100755 index 0000000..9a1af8e --- /dev/null +++ b/pocs/link-maps/link_map_test @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# +# link_map_test: this file is part of the elfix package +# Copyright (C) 2011 Anthony G. Basile +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import sys +from link_map import LinkMap + +def main(): + + # Run as root to be able to real all files + uid = os.getuid() + if uid != 0: + print('RUN AS ROOT: cannot read all flags') + sys.exit(0) + + link_map = LinkMap() + ( object_linkings, object_reverse_linkings, library2soname, soname2library ) = link_map.get_maps() + + layout = "{0:<30} => {1:<30}" + + #Print out all ELF objects and the NEEDED sonames and full library paths + for abi in object_linkings: + for elf in object_linkings[abi]: + sonames = object_linkings[abi][elf] + print('%s: %s' % (abi,elf)) + for soname in sorted(object_linkings[abi][elf]): + try: + print('\t%s' % layout.format(soname, soname2library[(soname,abi)])) + except KeyError: + print('\t%s' % layout.format(soname, '***' )) + print('') + + # Print out all ELF objects and the NEEDED sonames and full library paths + for abi in object_linkings: + for soname in object_reverse_linkings[abi]: + try: + print('%s: %s' % (abi, layout.format(soname, soname2library[(soname,abi)]))) + except KeyError: + print('%s: %s' % (abi, layout.format(soname, '***' ))) + for elf in sorted(object_reverse_linkings[abi][soname]): + print('\t%s' % elf) + print('') + + +if __name__ == '__main__': + main() diff --git a/pocs/mangle-paxflags/.gitignore b/pocs/mangle-paxflags/.gitignore new file mode 100644 index 0000000..2460008 --- /dev/null +++ b/pocs/mangle-paxflags/.gitignore @@ -0,0 +1 @@ +!Makefile diff --git a/pocs/mangle-paxflags/Makefile b/pocs/mangle-paxflags/Makefile new file mode 100644 index 0000000..9c2789d --- /dev/null +++ b/pocs/mangle-paxflags/Makefile @@ -0,0 +1,7 @@ +all: bad-mmap mangle-paxflags + +%: %.c + gcc -o $@ $^ -lelf + +clean: + rm -rf bad-mmap mangle-paxflags diff --git a/pocs/mangle-paxflags/bad-mmap.c b/pocs/mangle-paxflags/bad-mmap.c new file mode 100644 index 0000000..04df26d --- /dev/null +++ b/pocs/mangle-paxflags/bad-mmap.c @@ -0,0 +1,33 @@ +/* + bad-mmap.c: create 4k anonymous mmap with RWX protection + Copyright (C) 2011 Anthony G. Basile + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <errno.h> +#include <string.h> + +int +main() +{ + if( mmap(NULL, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) != MAP_FAILED ) + printf("mmap(): succeeded\n"); + else + printf("mmap(): %s\n", strerror(errno)); + return 0; +} diff --git a/pocs/mangle-paxflags/mangle-paxflags.c b/pocs/mangle-paxflags/mangle-paxflags.c new file mode 100644 index 0000000..5e41cd1 --- /dev/null +++ b/pocs/mangle-paxflags/mangle-paxflags.c @@ -0,0 +1,266 @@ +/* + mangle-paxflags.c: check and optionally remove EI_PAX and/or PT_PAX_FLAGS + Copyright (C) 2011 Anthony G. Basile + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <error.h> +#include <libgen.h> + +#include <gelf.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + + +// From chpax.h +#define EI_PAX 14 // Index in e_ident[] where to read flags +#define HF_PAX_PAGEEXEC 1 // 0: Paging based non-exec pages +#define HF_PAX_EMUTRAMP 2 // 0: Emulate trampolines +#define HF_PAX_MPROTECT 4 // 0: Restrict mprotect() +#define HF_PAX_RANDMMAP 8 // 0: Randomize mmap() base +#define HF_PAX_RANDEXEC 16 // 1: Randomize ET_EXEC base +#define HF_PAX_SEGMEXEC 32 // 0: Segmentation based non-exec pages + +#define PRINT(E,F,I) printf("%s:\t%s\n", #E, E&F? (I? "enabled" : "disabled") : (I? "disabled" : "enabled")); +#define SPRINT(E,F,A,B) printf("%c", E&F? A : B); +#define CPRINT(N,P) case P: printf("%d: %s\n", (int)N, #P); break +#define FPRINT(N,D,F,A,B) printf("%c", N&F? (D&F? '*' : B) : (D&F? A : '-')) + + +void +print_help(char *v) +{ + printf( + "Program Name : %s\n" + "Description : Check for, or conditionally remove, executable flag from PT_GNU_STACK\n\n" + "Usage : %s [-e] [-p] [-v] [-q] ELFfile | [-h]\n" + "options : Print out EI_PAX and PT_PAX_FLAGS information\n" + " : -e Set all EI_PAX flags to least secure setting, pEmrXs\n" + " : -p Remove PT_PAX_FLAGS program header\n" + " : -v Verbose expanation of flags (rather than short list)\n" + " : -q Surpress all output to stdout (negates verbose)\n" + " : -h Print out this help\n", + basename(v), + basename(v) + ); + + exit(EXIT_SUCCESS); +} + + +char * +parse_cmd_args(int c, char *v[], int *flag_ei_pax, int *flag_pt_pax_flags, int *verbose, int *quiet) +{ + int i, oc; + + if((c != 2)&&(c != 3)&&(c != 4)) + error(EXIT_FAILURE, 0, "Usage: %s {[-e] [-p] [-v] [-q] ELFfile | [-h]}", v[0]); + + *flag_ei_pax = 0; + *flag_pt_pax_flags = 0; + *verbose = 0; + *quiet = 0; + + while((oc = getopt(c, v,":epvqh")) != -1) + switch(oc) + { + case 'e': + *flag_ei_pax = 1; + break ; + case 'p': + *flag_pt_pax_flags = 1; + break; + case 'v': + *verbose = 1; + break; + case 'q': + *quiet = 1; + break; + case 'h': + print_help(v[0]); + break; + case '?': + default: + error(EXIT_FAILURE, 0, "option -%c is invalid: ignored.", optopt ) ; + } + + return v[optind] ; +} + + +int +main( int argc, char *argv[]) +{ + int fd, found_ei_pax; + int flag_ei_pax, flag_pt_pax_flags, verbose, quiet; + char *f_name; + size_t i, phnum; + + Elf *elf; + GElf_Ehdr ehdr; + GElf_Phdr phdr; + + f_name = parse_cmd_args(argc, argv, &flag_ei_pax, &flag_pt_pax_flags, &verbose, &quiet); + + if(elf_version(EV_CURRENT) == EV_NONE) + error(EXIT_FAILURE, 0, "Library out of date."); + + if( flag_ei_pax || flag_pt_pax_flags ) + { + if((fd = open(f_name, O_RDWR)) < 0) + error(EXIT_FAILURE, 0, "open() fail."); + if((elf = elf_begin(fd, ELF_C_RDWR_MMAP, NULL)) == NULL) + error(EXIT_FAILURE, 0, "elf_begin() fail: %s", elf_errmsg(elf_errno())); + } + else + { + if((fd = open(f_name, O_RDONLY)) < 0) + error(EXIT_FAILURE, 0, "open() fail."); + if((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) + error(EXIT_FAILURE, 0, "elf_begin() fail: %s", elf_errmsg(elf_errno())); + } + + if(elf_kind(elf) != ELF_K_ELF) + error(EXIT_FAILURE, 0, "elf_kind() fail: this is not an elf file."); + + if(gelf_getehdr(elf,&ehdr) != &ehdr) + error(EXIT_FAILURE, 0, "gelf_getehdr(): %s", elf_errmsg(elf_errno())); + + found_ei_pax = ((u_long) ehdr.e_ident[EI_PAX + 1] << 8) + (u_long) ehdr.e_ident[EI_PAX]; + + if(!quiet) + { + printf("==== EI_PAX ====\n") ; + if(verbose) + { + PRINT(HF_PAX_PAGEEXEC, found_ei_pax, 0); + PRINT(HF_PAX_EMUTRAMP, found_ei_pax, 1); + PRINT(HF_PAX_MPROTECT, found_ei_pax, 0); + PRINT(HF_PAX_RANDMMAP, found_ei_pax, 0); + PRINT(HF_PAX_RANDEXEC, found_ei_pax, 1); + PRINT(HF_PAX_SEGMEXEC, found_ei_pax, 0); + printf("\n"); + } + else + { + SPRINT(HF_PAX_PAGEEXEC, found_ei_pax, 'p', 'P'); + SPRINT(HF_PAX_EMUTRAMP, found_ei_pax, 'E', 'e'); + SPRINT(HF_PAX_MPROTECT, found_ei_pax, 'm', 'M'); + SPRINT(HF_PAX_RANDMMAP, found_ei_pax, 'r', 'R'); + SPRINT(HF_PAX_RANDEXEC, found_ei_pax, 'X', 'x'); + SPRINT(HF_PAX_SEGMEXEC, found_ei_pax, 's', 'S'); + printf("\n\n"); + } + } + + if( flag_ei_pax ) + { + if(!quiet) + printf("Disabling EI_PAX\n\n"); + ehdr.e_ident[EI_PAX] = 0xFF; + ehdr.e_ident[EI_PAX + 1] = 0xFF; + if(!gelf_update_ehdr(elf, &ehdr)) + error(EXIT_FAILURE, 0, "gelf_update_ehdr(): %s", elf_errmsg(elf_errno())); + } + + if(!quiet) + printf("==== PHRDs ====\n") ; + elf_getphdrnum(elf, &phnum); + for(i=0; i<phnum; ++i) + { + if(gelf_getphdr(elf, i, &phdr) != &phdr) + error(EXIT_FAILURE, 0, "gelf_getphdr(): %s", elf_errmsg(elf_errno())); + + if(!quiet) + { + if(verbose) + { + switch(phdr.p_type) + { + CPRINT(i,PT_NULL); + CPRINT(i,PT_LOAD); + CPRINT(i,PT_DYNAMIC); + CPRINT(i,PT_INTERP); + CPRINT(i,PT_NOTE); + CPRINT(i,PT_SHLIB); + CPRINT(i,PT_PHDR); + CPRINT(i,PT_TLS); + CPRINT(i,PT_NUM); + CPRINT(i,PT_LOOS); + CPRINT(i,PT_GNU_EH_FRAME); + CPRINT(i,PT_GNU_STACK); + CPRINT(i,PT_GNU_RELRO); + CPRINT(i,PT_PAX_FLAGS); + CPRINT(i,PT_LOSUNW); + //CPRINT(i,PT_SUNWBSS); + CPRINT(i,PT_SUNWSTACK); + CPRINT(i,PT_HISUNW); + //CPRINT(i,PT_HIOS); + CPRINT(i,PT_LOPROC); + CPRINT(i,PT_HIPROC); + } + } + + if(phdr.p_type == PT_PAX_FLAGS) + { + if(verbose) + { + PRINT(PF_PAGEEXEC, phdr.p_flags, 1); + PRINT(PF_NOPAGEEXEC, phdr.p_flags, 1); + PRINT(PF_SEGMEXEC, phdr.p_flags, 1); + PRINT(PF_NOSEGMEXEC, phdr.p_flags, 1); + PRINT(PF_MPROTECT, phdr.p_flags, 1); + PRINT(PF_NOMPROTECT, phdr.p_flags, 1); + PRINT(PF_RANDEXEC, phdr.p_flags, 1); + PRINT(PF_NORANDEXEC, phdr.p_flags, 1); + PRINT(PF_EMUTRAMP, phdr.p_flags, 1); + PRINT(PF_NOEMUTRAMP, phdr.p_flags, 1); + PRINT(PF_RANDMMAP, phdr.p_flags, 1); + PRINT(PF_NORANDMMAP, phdr.p_flags, 1); + } + else + { + printf("%d: PT_PAX_FLAGS\n", (int)i); + FPRINT(PF_PAGEEXEC, PF_NOPAGEEXEC, phdr.p_flags, 'p', 'P'); + FPRINT(PF_EMUTRAMP, PF_NOEMUTRAMP, phdr.p_flags, 'e', 'E'); + FPRINT(PF_MPROTECT, PF_NOMPROTECT, phdr.p_flags, 'm', 'M'); + FPRINT(PF_RANDMMAP, PF_NORANDMMAP, phdr.p_flags, 'r', 'R'); + FPRINT(PF_RANDEXEC, PF_NORANDEXEC, phdr.p_flags, 'x', 'X'); + FPRINT(PF_SEGMEXEC, PF_NOSEGMEXEC, phdr.p_flags, 's', 'S'); + } + } + } + + if((phdr.p_type == PT_PAX_FLAGS) && flag_pt_pax_flags ) + { + if(!quiet) + printf("CONVERTED -> PT_NULL\n\n"); + phdr.p_type = PT_NULL; + if(!gelf_update_phdr(elf, i, &phdr)) + error(EXIT_FAILURE, 0, "gelf_update_phdr(): %s", elf_errmsg(elf_errno())); + } + } + if(!quiet) + printf("\n\n"); + + elf_end(elf); + close(fd); +} diff --git a/pocs/mangle-paxflags/poc.sh b/pocs/mangle-paxflags/poc.sh new file mode 100755 index 0000000..f268f33 --- /dev/null +++ b/pocs/mangle-paxflags/poc.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +echo "================================================================================" +echo +./mangle-paxflags bad-mmap +./bad-mmap +echo +echo "========================================" +echo +./mangle-paxflags -p bad-mmap +./mangle-paxflags bad-mmap +./bad-mmap +echo +echo "========================================" +echo +./mangle-paxflags -e bad-mmap +./mangle-paxflags bad-mmap +./bad-mmap +echo +echo "================================================================================" diff --git a/pocs/paxmark-libs/Makefile.am b/pocs/paxmark-libs/Makefile.am new file mode 100644 index 0000000..3e87910 --- /dev/null +++ b/pocs/paxmark-libs/Makefile.am @@ -0,0 +1,94 @@ +ACLOCAL_AMFLAGS = -I m4 + +AM_CPPFLAGS = -DPLUGIN='"$(libdir)/libmypax.so"' + +bin_PROGRAMS = testpax testdlpax +testpax_SOURCES = testpax.c +testpax_LDFLAGS = -lmypax +testdlpax_SOURCES = testdlpax.c +testdlpax_LDFLAGS = -ldl + +lib_LTLIBRARIES = libmypax.la +libmypax_la_SOURCES = libmypax.c + +check_SCRIPTS = poc.sh + +poc.sh: + @/sbin/ldconfig + @echo "================================================================================" + @echo "= TESTING DYNAMIC LINKING ======================================================" + @echo + @/sbin/paxctl -z $(libdir)/libmypax.so $(bindir)/testpax + @/sbin/paxctl -M $(libdir)/libmypax.so + @/sbin/paxctl -M $(bindir)/testpax + @/sbin/paxctl -Qv $(libdir)/libmypax.so $(bindir)/testpax 2>/dev/null + @echo + $(bindir)/testpax + @echo + @echo "========================================" + @echo + @/sbin/paxctl -z $(libdir)/libmypax.so $(bindir)/testpax + @/sbin/paxctl -m $(libdir)/libmypax.so + @/sbin/paxctl -M $(bindir)/testpax + @/sbin/paxctl -Qv $(libdir)/libmypax.so $(bindir)/testpax 2>/dev/null + @echo + $(bindir)/testpax + @echo + @echo "========================================" + @echo + @/sbin/paxctl -z $(libdir)/libmypax.so $(bindir)/testpax + @/sbin/paxctl -M $(libdir)/libmypax.so + @/sbin/paxctl -m $(bindir)/testpax + @/sbin/paxctl -Qv $(libdir)/libmypax.so $(bindir)/testpax 2>/dev/null + @echo + $(bindir)/testpax + @echo + @echo "========================================" + @echo + @/sbin/paxctl -z $(libdir)/libmypax.so $(bindir)/testpax + @/sbin/paxctl -m $(libdir)/libmypax.so + @/sbin/paxctl -m $(bindir)/testpax + @/sbin/paxctl -Qv $(libdir)/libmypax.so $(bindir)/testpax 2>/dev/null + @echo + $(bindir)/testpax + @echo + @echo + @echo "================================================================================" + @echo "= TESTING DLOPENING ============================================================" + @echo + @/sbin/paxctl -z $(libdir)/libmypax.so $(bindir)/testdlpax + @/sbin/paxctl -M $(libdir)/libmypax.so + @/sbin/paxctl -M $(bindir)/testdlpax + @/sbin/paxctl -Qv $(libdir)/libmypax.so $(bindir)/testdlpax 2>/dev/null + @echo + $(bindir)/testdlpax + @echo + @echo "========================================" + @echo + @/sbin/paxctl -z $(libdir)/libmypax.so $(bindir)/testdlpax + @/sbin/paxctl -m $(libdir)/libmypax.so + @/sbin/paxctl -M $(bindir)/testdlpax + @/sbin/paxctl -Qv $(libdir)/libmypax.so $(bindir)/testdlpax 2>/dev/null + @echo + $(bindir)/testdlpax + @echo + @echo "========================================" + @echo + @/sbin/paxctl -z $(libdir)/libmypax.so $(bindir)/testdlpax + @/sbin/paxctl -M $(libdir)/libmypax.so + @/sbin/paxctl -m $(bindir)/testdlpax + @/sbin/paxctl -Qv $(libdir)/libmypax.so $(bindir)/testdlpax 2>/dev/null + @echo + $(bindir)/testdlpax + @echo + @echo "========================================" + @echo + @/sbin/paxctl -z $(libdir)/libmypax.so $(bindir)/testdlpax + @/sbin/paxctl -m $(libdir)/libmypax.so + @/sbin/paxctl -m $(bindir)/testdlpax + @/sbin/paxctl -Qv $(libdir)/libmypax.so $(bindir)/testdlpax 2>/dev/null + @echo + $(bindir)/testdlpax + @echo + @echo "================================================================================" + diff --git a/pocs/paxmark-libs/autogen.sh b/pocs/paxmark-libs/autogen.sh new file mode 100755 index 0000000..917d1a7 --- /dev/null +++ b/pocs/paxmark-libs/autogen.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +aclocal && \ +autoheader && \ +autoconf && \ +libtoolize --copy && \ +automake --add-missing --copy diff --git a/pocs/paxmark-libs/configure.ac b/pocs/paxmark-libs/configure.ac new file mode 100644 index 0000000..bedcbbb --- /dev/null +++ b/pocs/paxmark-libs/configure.ac @@ -0,0 +1,27 @@ + +AC_PREREQ([2.68]) +AC_INIT([testpax],[0.1]) +AC_CONFIG_SRCDIR([testpax.c]) +AC_CONFIG_HEADERS([config.h]) + +AM_INIT_AUTOMAKE([1.11 foreign]) +AM_SILENT_RULES([yes]) + +LT_PREREQ([2.4]) +LT_INIT([dlopen]) + +AC_CONFIG_MACRO_DIR([m4]) + +# Checks for programs. +AC_PROG_CC + +# Checks for libraries. + +# Checks for header files. + +# Checks for typedefs, structures, and compiler characteristics. + +# Checks for library functions. + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/pocs/paxmark-libs/libmypax.c b/pocs/paxmark-libs/libmypax.c new file mode 100644 index 0000000..a53b62e --- /dev/null +++ b/pocs/paxmark-libs/libmypax.c @@ -0,0 +1,11 @@ +#include <stdio.h> +#include <sys/mman.h> +#include <errno.h> +#include <string.h> + +void +doit() { + size_t m = (size_t) mmap( NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 ); + if( m == (size_t) MAP_FAILED ) + printf("%s\n", strerror(errno)); +} diff --git a/pocs/paxmark-libs/testdlpax.c b/pocs/paxmark-libs/testdlpax.c new file mode 100644 index 0000000..fde3245 --- /dev/null +++ b/pocs/paxmark-libs/testdlpax.c @@ -0,0 +1,17 @@ +#include <dlfcn.h> + +#ifndef PLUGIN +#define PLUGIN "/usr/local/lib/libmypax.so" +#endif + +int +main() { + void *handle; + void (*doit)(); + + handle = dlopen(PLUGIN, RTLD_LAZY); + doit = dlsym(handle, "doit"); + (*doit)(); + dlclose(handle); + return 0; +} diff --git a/pocs/paxmark-libs/testpax.c b/pocs/paxmark-libs/testpax.c new file mode 100644 index 0000000..dd1c0b7 --- /dev/null +++ b/pocs/paxmark-libs/testpax.c @@ -0,0 +1,6 @@ + +int +main() { + doit(); + return 0; +} diff --git a/pocs/revdep-pax-ng/revdep-pax-ng b/pocs/revdep-pax-ng/revdep-pax-ng new file mode 100755 index 0000000..7767563 --- /dev/null +++ b/pocs/revdep-pax-ng/revdep-pax-ng @@ -0,0 +1,500 @@ +#!/usr/bin/env python +# +# revdep-pax-ng: this file is part of the elfix package +# Copyright (C) 2011, 2012 Anthony G. Basile +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + + +import sys +import getopt +import os +import subprocess +import re +import pax + + +#python2/3 compat input +def get_input(prompt): + if sys.hexversion > 0x03000000: + return input(prompt) + else: + return raw_input(prompt) + + +def get_ldd_linkings(binary): + ldd_output = subprocess.Popen(['/usr/bin/ldd', binary], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + ldd_lines = ldd_output.stdout.read().decode().split('\n') + + linkings = [] + mappings = {} + for m in range(0,len(ldd_lines)): + if not re.search('=>', ldd_lines[m]): + continue + ldd_lines[m] = ldd_lines[m].strip() + mapp = re.split('=>', ldd_lines[m] ) + soname = mapp[0].strip() + soname = os.path.basename(soname) # This is for ./libSDL-1.2.so.0 + library = re.sub('\(.*$', '', mapp[1]).strip() + if library == '': + continue + library = os.path.realpath(library) + linkings.append(soname) + mappings[soname] = library + + return ( linkings, mappings ) + + +def get_forward_linkings(): + #TODO: I'm still not sure we want to use /var/db/pkg vs some path of binaries + var_db_pkg = '/var/db/pkg' + + forward_linkings = {} + so2library_mappings = {} + for cat in os.listdir(var_db_pkg): + catdir = '%s/%s' % (var_db_pkg, cat) + for pkg in os.listdir(catdir): + pkgdir = '%s/%s' % (catdir, pkg) + need = '%s/%s' % (pkgdir, 'NEEDED') + try: + g = open(need, 'r') + needs = g.readlines() + for line in needs: + line = line.strip() + link = re.split('\s', line) + binary = link[0] + ( linkings, mappings ) = get_ldd_linkings(binary) + forward_linkings[binary] = linkings + so2library_mappings.update(mappings) + except IOError: + continue #File probably doesn't exist, which is okay + + return ( forward_linkings, so2library_mappings ) + + +def invert_linkings( forward_linkings ): + reverse_linkings = {} + + for binary in forward_linkings: + for library in forward_linkings[binary]: + reverse_linkings.setdefault(library,[]).append(binary) + + return reverse_linkings + + +def print_forward_linkings( forward_linkings, so2library_mappings, verbose ): + missing_binaries = [] + missing_links = [] + for binary in forward_linkings: + + try: + ( binary_str_flags, binary_bin_flags ) = pax.getflags(binary) + sv = '%s ( %s )\n' % ( binary, binary_str_flags ) + s = sv + except pax.PaxError: + missing_binaries.append(binary) + continue + + count = 0 + for soname in forward_linkings[binary]: + try: + library = so2library_mappings[soname] + ( library_str_flags, library_bin_flags ) = pax.getflags(library) + sv = '%s\n\t%s\t%s ( %s )' % ( sv, soname, library, library_str_flags ) + if binary_str_flags != library_str_flags: + s = '%s\n\t%s\t%s ( %s )' % ( s, soname, library, library_str_flags ) + count = count + 1 + except pax.PaxError: + missing_links.append(soname) + + if verbose: + print('%s\n' % sv) + if count == 0: + print('\tNo mismatches\n\n') + else: + print('\tMismatches\n\n') + else: + if count != 0: + print('%s\n\n' % s) + + missing_binaries = set(missing_binaries) + print('\n**** Missing binaries ****\n') + for m in missing_binaries: + print('\t%s\n' % m) + + missing_links = set(missing_links) + print('\n**** Missing forward linkings ****\n') + for m in missing_links: + print('\t%s\n' % m) + + +def print_reverse_linkings( reverse_linkings, so2library_mappings, verbose, executable_only ): + shell_path = path = os.getenv('PATH').split(':') + missing_sonames = [] + missing_links = [] + + for soname in reverse_linkings: + try: + library = so2library_mappings[soname] + ( library_str_flags, library_bin_flags ) = pax.getflags(library) + sv = '%s\t%s ( %s )\n' % ( soname, library, library_str_flags ) + s = sv + except pax.PaxError: + missing_sonames.append(soname) + continue + + count = 0 + for binary in reverse_linkings[soname]: + try: + ( binary_str_flags, binary_bin_flags ) = pax.getflags(binary) + if executable_only: + if os.path.dirname(binary) in shell_path: + sv = '%s\n\t%s ( %s )' % ( sv, binary, binary_str_flags ) + if library_str_flags != binary_str_flags: + s = '%s\n\t%s ( %s )' % ( s, binary, binary_str_flags ) + count = count + 1 + else: + sv = '%s\n\t%s ( %s )' % ( sv, binary, binary_str_flags ) + if library_str_flags != binary_str_flags: + s = '%s\n\t%s ( %s )' % ( s, binary, binary_str_flags ) + count = count + 1 + except pax.PaxError: + missing_links.append(binary) + + if verbose: + print('%s\n' % sv) + if count == 0: + print('\tNo mismatches\n\n') + else: + print('\tMismatches\n\n') + else: + if count != 0: + print('%s\n\n' % s) + + missing_sonames = set(missing_sonames) + print('\n**** Missing sonames ****\n') + for m in missing_sonames: + print('\t%s\n' % m) + + missing_links = set(missing_links) + print('\n**** Missing reverse linkings ****\n') + for m in missing_links: + print('\t%s\n' % m) + + +def run_forward(verbose): + ( forward_linkings, so2library_mappings ) = get_forward_linkings() + print_forward_linkings( forward_linkings, so2library_mappings, verbose) + + +def run_reverse(verbose, executable_only): + ( forward_linkings, so2library_mappings ) = get_forward_linkings() + reverse_linkings = invert_linkings( forward_linkings ) + print_reverse_linkings( reverse_linkings, so2library_mappings, verbose, executable_only) + + +def migrate_flags(importer, exporter_str_flags, exporter_bin_flags): + # We implement the following logic for setting the pax flags + # on the target elf object, the IMPORTER, given that the flags + # from the elf object we want it to match to, the EXPORTER. + # + # EXPORTER IMPORTER RESULT + # On On On + # On Off On + Warn + # On - On + # Off On On + Warn + # Off Off Off + # Off - Off + # - On On + # - Off Off + # - - - + + #See /usr/include/elf.h for these values + pf_flags = { + 'P':1<<4, 'p':1<<5, + 'S':1<<6, 's':1<<7, + 'M':1<<8, 'm':1<<9, + 'X':1<<10, 'x':1<<11, + 'E':1<<12, 'e':1<<13, + 'R':1<<14, 'r':1<<15 + } + + ( importer_str_flags, importer_bin_flags ) = pax.getflags(importer) + + # Start with the exporter's flags + result_bin_flags = exporter_bin_flags + + for i in range(len(importer_str_flags)): + + # The exporter's flag contradicts the importer's flag, so do nothing + if (exporter_str_flags[i].isupper() and importer_str_flags[i].islower()) or \ + (exporter_str_flags[i].islower() and importer_str_flags[i].isupper()): + + # Revert the exporter's flag, use the importer's flag and warn + result_bin_flags = result_bin_flags ^ pf_flags[exporter_str_flags[i]] + result_bin_flags = result_bin_flags | pf_flags[importer_str_flags[i]] + print('\t\tWarning: %s has %s, refusing to set to %s' % ( + importer, importer_str_flags[i], exporter_str_flags[i] )), + + # The exporter's flags is off, so use the importer's flag + if (exporter_str_flags[i] == '-' and importer_str_flags[i] != '-'): + result_bin_flags = result_bin_flags | pf_flags[importer_str_flags[i]] + + pax.setbinflags(importer, result_bin_flags) + + +def run_binary(binary, verbose, mark, allyes): + if not os.path.exists(binary): + print('%s\tNo such OBJECT' % binary) + return + ( linkings, mappings ) = get_ldd_linkings(binary) + ( binary_str_flags, binary_bin_flags ) = pax.getflags(binary) + print('%s (%s)\n' % ( binary, binary_str_flags )) + + mismatched_libraries = [] + + for soname in linkings: + try: + library = mappings[soname] + ( library_str_flags, library_bin_flags ) = pax.getflags(library) + if verbose: + print('\t%s\t%s ( %s )' % ( soname, library, library_str_flags )) + if binary_str_flags != library_str_flags: + mismatched_libraries.append(library) + if not verbose: + print('\t%s\t%s ( %s )' % ( soname, library, library_str_flags )) + except pax.PaxError: + print('file for soname %s not found' % soname) + + if len(mismatched_libraries) == 0: + if not verbose: + print('\tNo mismatches\n') + else: + print('\n'), + if mark: + print('\tWill mark libraries with %s\n' % binary_str_flags) + for library in mismatched_libraries: + do_marking = False + while True: + if allyes: + ans = 'y' + else: + ans = get_input('\tSet flags for %s (y/n): ' % library) + if ans == 'y': + do_marking = True + break + elif ans == 'n': + do_marking = False + break + else: + print('\t\tPlease enter y or n') + + if do_marking: + try: + migrate_flags(library, binary_str_flags, binary_bin_flags) + except pax.PaxError: + print("\n\tCould not set pax flags on %s, file is probably busy" % library) + print("\tShut down all processes that use it and try again") + ( library_str_flags, library_bin_flags ) = pax.getflags(library) + print('\n\t\t%s ( %s )\n' % ( library, library_str_flags )) + + +def invert_so2library_mappings( so2library_mappings ): + library2soname_mappings = {} + for soname, library in so2library_mappings.items(): + library2soname_mappings[library] = soname + return library2soname_mappings + + +def run_soname(name, verbose, use_soname, mark, allyes, executable_only): + shell_path = path = os.getenv('PATH').split(':') + + ( forward_linkings, so2library_mappings ) = get_forward_linkings() + reverse_linkings = invert_linkings( forward_linkings ) + + if use_soname: + soname = name + else: + library2soname_mappings = invert_so2library_mappings(so2library_mappings) + try: + soname = library2soname_mappings[name] + except KeyError: + print('%s\tNo such LIBRARY' % name) + return + + try: + linkings = reverse_linkings[soname] + except KeyError: + print('%s\tNo such SONAME' % soname) + return + + library = so2library_mappings[soname] + + ( library_str_flags, library_bin_flags ) = pax.getflags(library) + print('%s\t%s (%s)\n' % ( soname, library, library_str_flags )) + + mismatched_binaries = [] + for binary in linkings: + try: + ( binary_str_flags, binary_bin_flags ) = pax.getflags(binary) + if verbose: + if executable_only: + if os.path.dirname(binary) in shell_path: + print('\t%s ( %s )' % ( binary, binary_str_flags )) + else: + print('\t%s ( %s )' % ( binary, binary_str_flags )) + if library_str_flags != binary_str_flags: + if executable_only: + if os.path.dirname(binary) in shell_path: + mismatched_binaries.append(binary) + if not verbose: + print('\t%s ( %s )' % ( binary, binary_str_flags )) + else: + mismatched_binaries.append(binary) + if not verbose: + print('\t%s ( %s )' % ( binary, binary_str_flags )) + except pax.PaxError: + print('cannot obtain pax flags for %s' % binary) + + if len(mismatched_binaries) == 0: + if not verbose: + print('\tNo mismatches\n') + else: + print('\n'), + if mark: + print('\tWill mark binaries with %s\n' % library_str_flags) + for binary in mismatched_binaries: + if executable_only: + if not os.path.dirname(binary) in shell_path: + continue + do_marking = False + while True: + if allyes: + ans = 'y' + else: + ans = get_input('\tSet flags for %s (y/n): ' % binary) + if ans == 'y': + do_marking = True + break + elif ans == 'n': + do_marking = False + break + else: + print('\t\tPlease enter y or n') + if do_marking: + try: + migrate_flags(binary, library_str_flags, library_bin_flags) + except pax.PaxError: + print('\n\tCould not set pax flags on %s, file is probably busy' % binary) + print('\tShut down all processes that use it and try again') + ( binary_str_flags, binary_bin_flags ) = pax.getflags(binary) + print('\n\t\t%s ( %s )\n' % ( binary, binary_str_flags )) + + +def run_usage(): + print('Package Name : elfix') + print('Bug Reports : http://bugs.gentoo.org/') + print('Program Name : revdep-pax') + print('Description : Get or set pax flags on an ELF object') + print('') + print('Usage : revdep-pax -f [-v] print out all forward mappings for all system binaries') + print(' : revdep-pax -r [-ve] print out all reverse mappings for all system sonames') + print(' : revdep-pax -b OBJECT [-myv] print all forward mappings only for OBJECT') + print(' : revdep-pax -s SONAME [-myve] print all reverse mappings only for SONAME') + print(' : revdep-pax -l LIBRARY [-myve] print all reverse mappings only for LIBRARY file') + print(' : revdep-pax [-h] print out this help') + print(' : -v verbose, otherwise just print mismatching objects') + print(' : -e only print out executables in shell $PATH') + print(' : -m don\'t just report, but mark the mismatching objects') + print(' : -y assume "yes" to all prompts for marking (USE CAREFULLY!)') + print('') + + +def main(): + try: + opts, args = getopt.getopt(sys.argv[1:], 'hfrb:s:l:vemy') + except getopt.GetoptError as err: + print(str(err)) # will print something like 'option -a not recognized' + run_usage() + sys.exit(1) + + if len(opts) == 0: + run_usage() + sys.exit(1) + + do_usage = False + do_forward = False + do_reverse = False + + binary = None + soname = None + library = None + + verbose = False + executable_only = False + mark = False + allyes = False + + opt_count = 0 + + for o, a in opts: + if o == '-h': + do_usage = True + opt_count += 1 + elif o == '-f': + do_forward = True + opt_count += 1 + elif o == '-r': + do_reverse = True + opt_count += 1 + elif o == '-b': + binary = a + opt_count += 1 + elif o == '-s': + soname = a + opt_count += 1 + elif o == '-l': + library = a + opt_count += 1 + elif o == '-v': + verbose = True + elif o == '-e': + executable_only = True + elif o == '-m': + mark = True + elif o == '-y': + allyes = True + else: + print('Option included in getopt but not handled here!') + print('Please file a bug') + sys.exit(1) + + # Only allow one of -h, -f -r -b -s + if opt_count > 1 or do_usage: + run_usage() + elif do_forward: + run_forward(verbose) + elif do_reverse: + run_reverse(verbose, executable_only) + elif binary != None: + run_binary(binary, verbose, mark, allyes) + elif soname != None: + run_soname(soname, verbose, True, mark, allyes, executable_only) + elif library != None: + library = os.path.realpath(library) + run_soname(library, verbose, False, mark, allyes, executable_only) + +if __name__ == '__main__': + main() |