ELF32: RELRO, strip, linking info

This commit is contained in:
2025-09-06 12:18:27 +02:00
parent ae889caae4
commit f3496b103e
5 changed files with 132 additions and 33 deletions

View File

@@ -4,3 +4,6 @@
- display protections
- colorful
- maybe display elf sections if command entered (??)
- security:
enforce malloc secure fail

BIN
a.out

Binary file not shown.

BIN
helpelf

Binary file not shown.

132
main.c
View File

@@ -44,9 +44,14 @@
#define PT_GNU_STACK 0x6474e551
#define SHT_DYNSYM 11
#define SHT_SYMTAB 2
#define PT_GNU_RELRO 0x6474e552
#define PT_DYNAMIC 2
#define DT_BIND_NOW 24
#define PT_INTERP 3
#define Elf32_Half uint16_t
#define Elf32_Word uint32_t
#define Elf32_Sword int32_t
#define Elf32_Addr uint32_t
#define Elf32_Off uint32_t
@@ -59,6 +64,10 @@ bool verbose = false;
bool pie = false;
bool nx = false;
bool canary = false;
bool relro = false;
bool bind_now = false;
bool stripped = true;
bool interp = false;
struct Elf32_Ehdr
{
@@ -114,6 +123,16 @@ struct Elf32_Sym
Elf32_Half st_shndx;
};
struct Elf32_Dyn
{
Elf32_Sword d_tag;
union
{
Elf32_Word d_val;
Elf32_Addr d_ptr;
} d_un;
} Elf32_Dyn;
struct Elf64_Ehdr
{
unsigned char e_ident[EI_NIDENT];
@@ -181,7 +200,41 @@ void display_elf_common(unsigned char e_ident[EI_NIDENT])
}
}
void display_elf32(struct Elf32_Ehdr* header)
void check_dynamic32(struct Elf32_Ehdr* header, FILE* fp)
{
// Is dynamically linked? (has PT_INTERP program header?)
// read program headers
fseek(fp, header->e_phoff, SEEK_SET);
struct Elf32_Phdr *p_headers = malloc(header->e_phnum * sizeof(struct Elf32_Phdr));
fread(p_headers, sizeof(struct Elf32_Phdr), header->e_phnum, fp);
const char* interpstr = NULL;
for (int i=0; i<header->e_phnum; i++)
{
if (p_headers[i].p_type == PT_INTERP)
{
interp = true;
// Get interpreter path
interpstr = malloc(p_headers[i].p_filesz);
fseek(fp, p_headers[i].p_offset, SEEK_SET);
fread((char*)interpstr, 1, p_headers[i].p_filesz, fp);
break;
}
}
if (interp)
{
printf("dynamically linked (interpreter: %s), ", interpstr);
}
else
{
printf("statically linked, ");
}
}
void display_elf32(struct Elf32_Ehdr* header, FILE* fp)
{
printf("32-bit ELF ");
display_elf_common(header->e_ident);
@@ -246,30 +299,24 @@ void display_elf32(struct Elf32_Ehdr* header)
printf("MIPS RS4000, ");
break;
default:
printf("Unknown architecture");
printf("Unknown architecture, ");
break;
}
if (verbose)
{
printf("\ne_version: %d\n", header->e_version);
printf("e_entry: 0x%08x\n", header->e_entry);
printf("e_phoff: 0x%08x\n", header->e_phoff);
printf("e_shoff: 0x%08x\n", header->e_shoff);
printf("e_flags: 0x%08x\n", header->e_flags);
printf("e_ehsize: 0x%08x\n", header->e_ehsize);
printf("e_phentsize: 0x%08x\n", header->e_phentsize);
printf("e_phnum: 0x%08x\n", header->e_phnum);
printf("e_shentsize: 0x%08x\n", header->e_shentsize);
printf("e_shnum: 0x%08x\n", header->e_shnum);
printf("e_shstrndx: 0x%08x\n", header->e_shstrndx);
check_dynamic32(header, fp);
}
}
void check_nx32(struct Elf32_Ehdr* header, FILE* fp)
{
}
void check_sec32(struct Elf32_Ehdr* header, FILE* fp)
{
/*
/* NX:
* Browse thru program header table (e_phnum items) from e_phoff (program header offset)
* if its stack and has executable bit then we know NX isnt there
*/
@@ -291,7 +338,7 @@ void check_sec32(struct Elf32_Ehdr* header, FILE* fp)
}
}
/*
/* Stack canary:
* Same for section header table (e_shnum items) from e_shoff (section header offset)
* find sections .dynsym or .symtab:
* and look for __stack_chk_fail (call for stack smashing)
@@ -310,6 +357,9 @@ void check_sec32(struct Elf32_Ehdr* header, FILE* fp)
for (size_t i=0; i<header->e_shnum; i++)
{
// additional small check for stripping as we're already browsing section headers
if (s_headers[i].sh_type == SHT_SYMTAB && s_headers[i].sh_size > 0) stripped = false;
if (s_headers[i].sh_type == SHT_DYNSYM || s_headers[i].sh_type == SHT_SYMTAB)
{
// load symbol table
@@ -335,6 +385,50 @@ void check_sec32(struct Elf32_Ehdr* header, FILE* fp)
}
}
}
/* RELRO:
* Segment type PT_GNU_RELRO should be present
*/
// Read program headers
fseek(fp, header->e_phoff, SEEK_SET);
struct Elf32_Phdr *p_headers = malloc(header->e_phnum * sizeof(struct Elf32_Phdr));
fread (p_headers, sizeof(struct Elf32_Phdr), header->e_phnum, fp);
Elf32_Off dyn_off = 0;
Elf32_Word dyn_size = 0;
for (int i=0; i<header->e_phnum; i++)
{
if (p_headers[i].p_type == PT_GNU_RELRO)
{
relro = true;
}
if (p_headers[i].p_type == PT_DYNAMIC)
{
dyn_off = p_headers[i].p_offset;
dyn_size = p_headers[i].p_filesz;
}
}
if (dyn_off && dyn_size)
{
int n = dyn_size / sizeof(struct Elf32_Dyn);
struct Elf32_Dyn *dyns = malloc(dyn_size);
fseek(fp, dyn_off, SEEK_SET);
fread(dyns, sizeof(struct Elf32_Dyn), n, fp);
for (int i=0; i<n; i++)
{
// Has full RELRO?
if (dyns[i].d_tag == DT_BIND_NOW)
{
bind_now = true;
break;
}
}
}
}
void display_elf64(struct Elf64_Ehdr* header)
@@ -356,7 +450,9 @@ void display_sec_common()
{
printf("%s, ", nx ? "\x1b[32mNX enabled\x1b[0m" : "\x1b[31mNX disabled\x1b[0m");
printf("%s, ", pie ? "\x1b[32mPIE enabled\x1b[0m" : "\x1b[31mPIE disabled\x1b[0m");
printf("%s\n", canary ? "\x1b[32mStack canary enabled\x1b[0m" : "\x1b[31mStack canary disabled\x1b[0m");
printf("%s, ", canary ? "\x1b[32mStack canary enabled\x1b[0m" : "\x1b[31mStack canary disabled\x1b[0m");
printf("%s, ", relro && bind_now ? "\x1b[32mFull RELRO\x1b[0m" : relro ? "\x1b[33mPartial RELRO\x1b[0m" : "\x1b[31mNo RELRO\x1b[0m");
printf("%s\n", stripped ? "\x1b[32mStripped\x1b[0m" : "\x1b[31mNot stripped\x1b[0m");
}
int main(int argc, char* argv[])
@@ -408,7 +504,7 @@ int main(int argc, char* argv[])
printf("Failed to parse 32-bit ELF header\n");
return -EINVAL;
}
display_elf32(&elf32_header);
display_elf32(&elf32_header, elf_file);
check_sec32(&elf32_header, elf_file);
break;
case 0x02:

View File

@@ -6,7 +6,7 @@ install:
test:
make
gcc -m32 -fstack-protector main.c
gcc -m32 -fstack-protector -static -Wl,-z,relro,-z,now main.c
./helpelf a.out
clean: