64-bit checksec + README

This commit is contained in:
2025-09-07 13:20:19 +02:00
parent 489d35a65b
commit cc1883b1e5
3 changed files with 231 additions and 7 deletions

View File

@@ -1,9 +1,48 @@
# todo # helpelf: an open and lightweight ELF recon tool
- display e_type and rest of header for 32/64 helpelf aims to be a replacement to the `file` and `checksec` utilities that are used by many hackers. The tool does not rely on readelf like `checksec` does, but instead includes its own ELF parser. Works for both 32 and 64-bit versions of the standard.
- display protections
- colorful
- maybe display elf sections if command entered (??)
- security: ## Installation
enforce malloc secure fail
This will install `helpelf` and the alias `he` on the command line. No dependencies are required besides the C toolchain & make utility.
```
make
sudo make install
```
## Usage
The output is colorful (not visible here).
```
$ helpelf
Usage: helpelf <file> [options]
-v --verbose displays more information
$ he a.out
a.out: 64-bit ELF (little-endian) amd64, NX enabled, PIE disabled, Stack canary enabled, Partial RELRO, Not stripped
$ he a.out -v
a.out: 64-bit ELF (little-endian) version 1 (executable file), amd64, statically linked, NX enabled, PIE disabled, Stack canary enabled, Partial RELRO, Not stripped
```
## Documentation
The tool was developed according to the ELF 32 and 64 bit standards that can be found here:
- [ELF-64 Object File Format](https://uclibc.org/docs/elf-64-gen.pdf)
- [Tool Interface Standard (TIS) Executable and Linking Format (ELF) Specification](https://refspecs.linuxfoundation.org/elf/elf.pdf)
## License
This project is under the Unlicense (part of public domain).
## TODO
- optimization: find a workaround for 32/64 sec differentiation
- verbose: display detailed header info
- verbose: display elf sections
## Known issues
- the RELRO check gives false-negatives on Full RELRO (instead shows Partial-RELRO)
- any contribution and/or feedback is highly appreciated. Open an issue/pull request if you'd like to add something.

184
main.c
View File

@@ -468,6 +468,189 @@ void check_sec32(Elf32_Ehdr* header, FILE* fp)
free(p_headers); free(p_headers);
} }
// Duplicate code. Should find a workaround
void check_sec64(Elf64_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
*/
fseek(fp, header->e_phoff, SEEK_SET);
for (size_t i=0; i<header->e_phnum; i++)
{
Elf64_Phdr p_header;
if (!fread(&p_header, 1, sizeof(Elf64_Phdr), fp))
{
puts("Read error.");
exit(-EIO);
}
if (p_header.p_type == PT_GNU_STACK)
{
if (p_header.p_flags & PF_X)
{
nx = false;
}
else
{
nx = true;
}
}
}
/* 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)
*/
fseek(fp, header->e_shoff, SEEK_SET);
// Read section headers
Elf64_Shdr *s_headers = malloc(header->e_shnum * sizeof(Elf64_Shdr));
if (!s_headers)
{
puts("Memory allocation error.");
exit(-ENOMEM);
}
if (!fread(s_headers, sizeof(Elf64_Shdr), header->e_shnum, fp))
{
puts("Read error.");
exit(-EIO);
}
// Find section name string table (at e_shstrndx) and read it
Elf64_Shdr s_header_str = s_headers[header->e_shstrndx];
char* s_header_strtab = malloc(s_header_str.sh_size);
if (!s_header_strtab)
{
puts("Memory allocation error.");
exit(-ENOMEM);
}
fseek(fp, s_header_str.sh_offset, SEEK_SET);
if (!fread(s_header_strtab, 1, s_header_str.sh_size, fp))
{
puts("Read error.");
exit(-EIO);
}
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
unsigned int nsyms = s_headers[i].sh_size / s_headers[i].sh_entsize;
Elf64_Sym *syms = malloc(s_headers[i].sh_size);
if (!syms)
{
puts("Memory allocation error.");
exit(-EIO);
}
fseek(fp, s_headers[i].sh_offset, SEEK_SET);
if (!fread(syms, s_headers[i].sh_size, 1, fp))
{
puts("Read error.");
exit(-EIO);
}
// load string table
Elf64_Shdr str_header = s_headers[s_headers[i].sh_link];
char* strtab = malloc(str_header.sh_size);
if (!strtab)
{
puts("Memory allocation error.");
exit(-ENOMEM);
}
fseek(fp, str_header.sh_offset, SEEK_SET);
if (!fread(strtab, 1, str_header.sh_size, fp))
{
puts("Read error.");
exit(-EIO);
}
// iterate through symbols, check for __stack_chk_fail
for (size_t j=0; j<nsyms; j++)
{
const char* name = strtab + syms[j].st_name;
if (strcmp(name, "__stack_chk_fail") == 0)
{
canary = true;
}
}
free(syms);
free(strtab);
}
}
/* RELRO:
* Segment type PT_GNU_RELRO should be present
*/
// Read program headers
fseek(fp, header->e_phoff, SEEK_SET);
Elf64_Phdr *p_headers = malloc(header->e_phnum * sizeof(Elf64_Phdr));
if (!p_headers)
{
puts("Memory allocation error.");
exit(-ENOMEM);
}
if (!fread (p_headers, sizeof(Elf64_Phdr), header->e_phnum, fp))
{
puts("Read error.");
exit(-EIO);
}
Elf64_Off dyn_off = 0;
Elf64_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(Elf64_Dyn);
Elf64_Dyn *dyns = malloc(dyn_size);
if (!dyns)
{
puts("Memory allocation error.");
exit(-ENOMEM);
}
fseek(fp, dyn_off, SEEK_SET);
if (!fread(dyns, sizeof(Elf64_Dyn), n, fp))
{
puts("Read error.");
exit(-EIO);
}
for (int i=0; i<n; i++)
{
// Has full RELRO?
if (dyns[i].d_tag == DT_BIND_NOW)
{
bind_now = true;
break;
}
}
free(dyns);
}
free(s_headers);
free(s_header_strtab);
free(p_headers);
}
unsigned char read_elf_obj_size(FILE* fp) unsigned char read_elf_obj_size(FILE* fp)
{ {
unsigned char local_elf_obj_size = 0; unsigned char local_elf_obj_size = 0;
@@ -550,6 +733,7 @@ int main(int argc, char* argv[])
return -EINVAL; return -EINVAL;
} }
display_elf64(&elf64_header, elf_file); display_elf64(&elf64_header, elf_file);
check_sec64(&elf64_header, elf_file);
break; break;
default: default:
puts("Invalid ELF object size"); puts("Invalid ELF object size");

View File

@@ -3,6 +3,7 @@ all:
install: install:
sudo cp helpelf /usr/bin/helpelf sudo cp helpelf /usr/bin/helpelf
sudo ln -sf /usr/bin/helpelf /usr/bin/he
test: test:
make make