Add tool to modify ELF binary feature control bits
This will allow feature control bits (e.g. for ASLR, PROT_MAX) to be inspected or modified. Some clean-up and additional work is likely still required, but we can iterate on this in the tree. Submitted by: Bora Özarslan <borako.ozarslan@gmail.com> Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D19290
This commit is contained in:
parent
fc91d16ffa
commit
8af6befc4c
14
tools/tools/controlelf/Makefile
Normal file
14
tools/tools/controlelf/Makefile
Normal file
@ -0,0 +1,14 @@
|
||||
# $FreeBSD$
|
||||
|
||||
ELFTCDIR= ${SRCTOP}/contrib/elftoolchain
|
||||
|
||||
PROG= controlelf
|
||||
SRCS= controlelf.c
|
||||
|
||||
LIBADD= elftc elf
|
||||
|
||||
CFLAGS+=-I${ELFTCDIR}/libelftc -I${ELFTCDIR}/common
|
||||
|
||||
WARNS?= 6
|
||||
|
||||
.include <bsd.prog.mk>
|
92
tools/tools/controlelf/controlelf.1
Normal file
92
tools/tools/controlelf/controlelf.1
Normal file
@ -0,0 +1,92 @@
|
||||
.\" Copyright 2019 The FreeBSD Foundation.
|
||||
.\"
|
||||
.\" This software was developed by Bora Ozarslan under sponsorship from
|
||||
.\" the FreeBSD Foundation.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
|
||||
.\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd February 21, 2019
|
||||
.Dt CONTROLELF 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm controlelf
|
||||
.Nd change an ELF binary's control features
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl h | Fl -help
|
||||
.Op Fl l
|
||||
.Op Fl e Ar featurelist
|
||||
.Ar
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
utility modifies an ELF binary to change it's control features.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width indent
|
||||
.It Fl h | Fl -help
|
||||
Print a usage message and exit.
|
||||
.It Fl l
|
||||
Writes the list of all known ELF types to standard output.
|
||||
.It Fl e Ar featurelist
|
||||
Edit features from the given comma separated list
|
||||
.Ar featurelist .
|
||||
.Ar featurelist
|
||||
starts with one of the three modes:
|
||||
.Dq Li +
|
||||
to turn on the features,
|
||||
.Dq Li -
|
||||
to turn off the features,
|
||||
.Dq Li =
|
||||
to only turn on the given features.
|
||||
Followed by a comma separated features.
|
||||
.El
|
||||
.Pp
|
||||
If
|
||||
.Fl e ,
|
||||
option wasn't specified,
|
||||
.Nm
|
||||
will display the current control features for the files named by the arguments
|
||||
.Ar .
|
||||
.Sh EXIT STATUS
|
||||
Exit status is 0 on success, and 1 if the command
|
||||
fails if a file does not exist, is too short,
|
||||
or fails to find or edit features note.
|
||||
.Sh EXAMPLES
|
||||
The following is an example of a typical usage
|
||||
of the
|
||||
.Nm
|
||||
command:
|
||||
.Bd -literal -offset indent
|
||||
controlelf file
|
||||
controlelf -e +aslr file
|
||||
.Ed
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
manual page first appeared in
|
||||
.Fx 13.0 .
|
||||
.Sh AUTHORS
|
||||
This manual page was written by
|
||||
.An Bora Ozarslan Mt borako.ozarslan@gmail.com .
|
374
tools/tools/controlelf/controlelf.c
Normal file
374
tools/tools/controlelf/controlelf.c
Normal file
@ -0,0 +1,374 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 2019 The FreeBSD Foundation.
|
||||
*
|
||||
* This software was developed by Bora Ozarslan under sponsorship from
|
||||
* the FreeBSD Foundation.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/elf_common.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <gelf.h>
|
||||
#include <getopt.h>
|
||||
#include <libelf.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "_elftc.h"
|
||||
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
static bool convert_to_feature_val(char*, u_int32_t*);
|
||||
static bool edit_file_features(Elf *, int, int, char*);
|
||||
static bool get_file_features(Elf *, int, int, u_int32_t *, u_int64_t *);
|
||||
static void print_features(void);
|
||||
static bool print_file_features(Elf *, int, int, char*);
|
||||
static void usage(void);
|
||||
|
||||
struct ControlFeatures {
|
||||
const char *alias;
|
||||
unsigned long featureVal;
|
||||
const char *desc;
|
||||
};
|
||||
|
||||
static struct ControlFeatures featurelist[] = {
|
||||
{ "aslr", NT_FREEBSD_FCTL_ASLR_DISABLE, "Disable ASLR" }
|
||||
};
|
||||
|
||||
static struct option controlelf_longopts[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
GElf_Ehdr ehdr;
|
||||
Elf *elf;
|
||||
Elf_Kind kind;
|
||||
int ch, fd, listed, editfeatures, retval;
|
||||
char *features;
|
||||
|
||||
listed = 0;
|
||||
editfeatures = 0;
|
||||
retval = 0;
|
||||
|
||||
if (elf_version(EV_CURRENT) == EV_NONE)
|
||||
errx(EXIT_FAILURE, "elf_version error");
|
||||
|
||||
while ((ch = getopt_long(argc, argv, "hle:", controlelf_longopts,
|
||||
NULL)) != -1) {
|
||||
switch (ch) {
|
||||
case 'l':
|
||||
print_features();
|
||||
listed = 1;
|
||||
break;
|
||||
case 'e':
|
||||
features = optarg;
|
||||
editfeatures = 1;
|
||||
break;
|
||||
case 'h':
|
||||
usage();
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (!argc) {
|
||||
if (listed)
|
||||
exit(0);
|
||||
else {
|
||||
warnx("no file(s) specified");
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
while (argc) {
|
||||
elf = NULL;
|
||||
|
||||
if ((fd = open(argv[0], editfeatures ? O_RDWR :
|
||||
O_RDONLY, 0)) < 0) {
|
||||
warn("error opening file %s", argv[0]);
|
||||
retval = 1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
|
||||
warnx("elf_begin failed: %s", elf_errmsg(-1));
|
||||
retval = 1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((kind = elf_kind(elf)) != ELF_K_ELF) {
|
||||
if (kind == ELF_K_AR)
|
||||
warnx("file '%s' is an archive.", argv[0]);
|
||||
else
|
||||
warnx("file '%s' is not an ELF file.",
|
||||
argv[0]);
|
||||
retval = 1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (gelf_getehdr(elf, &ehdr) == NULL) {
|
||||
warnx("gelf_getehdr: %s", elf_errmsg(-1));
|
||||
retval = 1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!editfeatures) {
|
||||
if (!print_file_features(elf, ehdr.e_phnum, fd, argv[0])) {
|
||||
retval = 1;
|
||||
goto fail;
|
||||
}
|
||||
} else if (!edit_file_features(elf, ehdr.e_phnum, fd, features)) {
|
||||
retval = 1;
|
||||
goto fail;
|
||||
}
|
||||
fail:
|
||||
if (elf != NULL)
|
||||
elf_end(elf);
|
||||
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
|
||||
return (retval);
|
||||
}
|
||||
|
||||
#define USAGE_MESSAGE "\
|
||||
Usage: %s [options] file...\n\
|
||||
Set or display the control features for an ELF object.\n\n\
|
||||
Supported options are:\n\
|
||||
-l List known control features.\n\
|
||||
-e [+-=]feature,list Edit features from a comma separated list.\n\
|
||||
-h | --help Print a usage message and exit.\n"
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
|
||||
fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static bool
|
||||
convert_to_feature_val(char* feature_str, u_int32_t* feature_val)
|
||||
{
|
||||
char *feature_input, *feature;
|
||||
int i, len;
|
||||
u_int32_t input;
|
||||
bool add, set;
|
||||
|
||||
add = set = false;
|
||||
input = 0;
|
||||
|
||||
if (feature_str[0] == '+')
|
||||
add = true;
|
||||
else if (feature_str[0] == '=')
|
||||
set = true;
|
||||
else if (feature_str[0] != '-') {
|
||||
warnx("'%c' is not an operator. Use instead '+', '-', '='.",
|
||||
feature_str[0]);
|
||||
return (false);
|
||||
}
|
||||
|
||||
feature_input = feature_str + 1;
|
||||
len = nitems(featurelist);
|
||||
while ((feature = strsep(&feature_input, ",")) != NULL) {
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (strcmp(featurelist[i].alias, feature) == 0) {
|
||||
input |= featurelist[i].featureVal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == len) {
|
||||
warnx("%s is not a valid feature.", feature);
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
|
||||
if (add) {
|
||||
*feature_val |= input;
|
||||
} else if (set) {
|
||||
*feature_val = input;
|
||||
} else {
|
||||
*feature_val -= (*feature_val) & input;
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
|
||||
static bool
|
||||
edit_file_features(Elf *elf, int phcount, int fd, char *val)
|
||||
{
|
||||
u_int32_t features;
|
||||
u_int64_t off;
|
||||
|
||||
if (!get_file_features(elf, phcount, fd, &features, &off)) {
|
||||
warnx("No control features note on the file.\n");
|
||||
return (false);
|
||||
}
|
||||
|
||||
if (!convert_to_feature_val(val, &features))
|
||||
return (false);
|
||||
|
||||
lseek(fd, off, SEEK_SET);
|
||||
write(fd, &features, sizeof(u_int32_t));
|
||||
return (true);
|
||||
}
|
||||
|
||||
static void
|
||||
print_features(void)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
printf("Known features are:\n");
|
||||
for (i = 0; i < nitems(featurelist); ++i)
|
||||
printf("%s\t\t %s\n", featurelist[i].alias,
|
||||
featurelist[i].desc);
|
||||
}
|
||||
|
||||
static bool
|
||||
print_file_features(Elf *elf, int phcount, int fd, char *filename) {
|
||||
u_int32_t features;
|
||||
unsigned long i;
|
||||
|
||||
if (!get_file_features(elf, phcount, fd, &features, NULL)) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
printf("File '%s' features:\n", filename);
|
||||
for (i = 0; i < nitems(featurelist); ++i) {
|
||||
printf("%s\t\t'%s' is ", featurelist[i].alias,
|
||||
featurelist[i].desc);
|
||||
|
||||
if ((featurelist[i].featureVal & features) == 0)
|
||||
printf("un");
|
||||
|
||||
printf("set.\n");
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
|
||||
static bool
|
||||
get_file_features(Elf *elf, int phcount, int fd, u_int32_t *features, u_int64_t *off)
|
||||
{
|
||||
GElf_Phdr phdr;
|
||||
Elf_Note note;
|
||||
unsigned long read_total;
|
||||
int namesz, descsz, i;
|
||||
char *name;
|
||||
ssize_t size;
|
||||
|
||||
/*
|
||||
* Go through each program header to find one that is of type PT_NOTE
|
||||
* and has a note for feature control.
|
||||
*/
|
||||
for (i = 0; i < phcount; ++i) {
|
||||
if (gelf_getphdr(elf, i, &phdr) == NULL) {
|
||||
warnx("gelf_getphdr failed: %s", elf_errmsg(-1));
|
||||
return (false);
|
||||
}
|
||||
|
||||
if (phdr.p_type != PT_NOTE)
|
||||
continue;
|
||||
|
||||
if (lseek(fd, phdr.p_offset, SEEK_SET) < 0) {
|
||||
warn("lseek() failed:");
|
||||
return (false);
|
||||
}
|
||||
|
||||
read_total = 0;
|
||||
while (read_total < phdr.p_filesz) {
|
||||
size = read(fd, ¬e, sizeof(note));
|
||||
if (size < (ssize_t)sizeof(note)) {
|
||||
warn("read() failed:");
|
||||
return (false);
|
||||
}
|
||||
read_total += sizeof(note);
|
||||
|
||||
/*
|
||||
* XXX: Name and descriptor are 4 byte aligned, however,
|
||||
* the size given doesn't include the padding.
|
||||
*/
|
||||
namesz = roundup2(note.n_namesz, 4);
|
||||
name = malloc(namesz);
|
||||
if (name == NULL) {
|
||||
warn("malloc() failed.\n");
|
||||
return (false);
|
||||
}
|
||||
descsz = roundup2(note.n_descsz, 4);
|
||||
size = read(fd, name, namesz);
|
||||
read_total += namesz;
|
||||
|
||||
if (note.n_namesz != 8 ||
|
||||
strncmp("FreeBSD", name, 7) != 0 ||
|
||||
note.n_type != NT_FREEBSD_FEATURE_CTL) {
|
||||
/* Not the right note. Skip the description */
|
||||
if (lseek(fd, descsz, SEEK_CUR) < 0) {
|
||||
warn("lseek() failed.\n");
|
||||
free(name);
|
||||
return (false);
|
||||
}
|
||||
read_total += descsz;
|
||||
free(name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (note.n_descsz < sizeof(u_int32_t)) {
|
||||
warnx("Feature descriptor can't "
|
||||
"be less than 4 bytes");
|
||||
free(name);
|
||||
return (false);
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: For now we look at only 4 bytes of the
|
||||
* descriptor. This should respect descsz.
|
||||
*/
|
||||
if (note.n_descsz > sizeof(u_int32_t))
|
||||
warnx("Feature note is bigger than expected.");
|
||||
read(fd, features, sizeof(u_int32_t));
|
||||
if (off != NULL)
|
||||
*off = phdr.p_offset + read_total;
|
||||
free(name);
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
|
||||
warnx("Couldn't find a note header with control feature note.");
|
||||
return (false);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user