Would like to have an executable save its state by modifying its own global constants. Just for the kicks of having a totally self-contained executable.
A few solutions/hacks that come to mind:
It would be neat to have the linker provide this info.
Is it possible to have the linker provide the offset of a read-only section in the executable file?
Thanks
What you want to do is tricky and unportable.
You could however study GNU emacs unexec
function e.g. in src/unexelf.c (for Linux; other OSes have a similar file).
You could also play linker tricks, e.g. with your own ld
script.
Beware that these tricks may be processor-, compiler-, kernel-, and libc- version specific.
Perhaps you want application checkpointing. In particular, BLCR might be useful to you.
You are essentially talking about binary rewriting. One method to achieve this without fiddling with the compile process is to map a virtual address to a physical one and then patch it. Interestingly, this is something I covered in my master's thesis. The following images and text are pulled from that document:
Note that the concept behind my original project was to rewrite code in other binaries assuming that the compile process could not be modified. If your requirements and assumptions are different, this may well not be the easiest and best approach.
The most important idea here is that a section in the disk representation is preserved (not split) when it is mapped into memory. This means data that is at a certain offset into the section in the disk representation will be offset by the same amount after loaded into memory.
In libelf
, similarly to libbfd
, an executable contains a set of sections in which both code and data can reside. When the operating system loads the executable into memory, each section is based at some base address. We can reverse this to map a virtual memory address to a physical file offset. If a physical file offset can be found, the bytes can be patched as a regular file.
libelf
.
This allows us to obtain a set of sections and most importantly, for
each section libelf
can tell us three things:
(virtual_memory_address - section_base_address)
.(section_disk_offset + (virtual_memory_address - section_base_address))
.This process allows an arbitrary virtual memory address to be mapped to its corresponding disk file offset. This offset can then be patched with regular C file IO functions such as fopen
/fseek
/fwrite
/fclose
.
This is my code for mapping a virtual address to a physical file offset using the above steps:
/*
* Returns the corresponding 32 bit executable file offset of a virtual memory
* address.
*/
uint32_t vaddr32_to_file_offset(char * filepath, uint32_t vaddr)
{
int fd = open(filepath, O_RDONLY);
Elf * e = elf_begin(fd, ELF_C_READ, NULL);
uint32_t offset = 0;
Elf_Scn * scn = NULL;
while((scn = elf_nextscn(e, scn)) != NULL) {
Elf32_Shdr * shdr = elf32_getshdr(scn);
if(vaddr >= shdr->sh_addr &&
(vaddr <= (shdr->sh_addr + shdr->sh_size))) {
offset = shdr->sh_offset + (vaddr - shdr->sh_addr);
break;
}
}
elf_end(e);
close(fd);
return offset;
}
/*
* Returns the corresponding 64 bit executable file offset of a virtual memory
* address.
*/
uint64_t vaddr64_to_file_offset(char * filepath, uint64_t vaddr)
{
int fd = open(filepath, O_RDONLY);
Elf * e = elf_begin(fd, ELF_C_READ, NULL);
uint64_t offset = 0;
Elf_Scn * scn = NULL;
while((scn = elf_nextscn(e, scn)) != NULL) {
Elf64_Shdr * shdr = elf64_getshdr(scn);
if(vaddr >= shdr->sh_addr &&
(vaddr <= (shdr->sh_addr + shdr->sh_size))) {
offset = shdr->sh_offset + (vaddr - shdr->sh_addr);
break;
}
}
elf_end(e);
close(fd);
return offset;
}
This is the code to patch an ELF executable given an offset:
/*
* Sets the bytes at an arbitrary offset of a file to the contents of buffer.
*/
static bool patch_file(char * filepath, uint64_t offset, void * buffer,
size_t size)
{
FILE * pFile = fopen(filepath, "r+");
if(pFile == NULL) {
return FALSE;
}
fseek(pFile, offset, SEEK_SET);
fwrite(buffer, 1, size, pFile);
fclose(pFile);
return TRUE;
}
More detailed information can be found in the report itself which is publicly available here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With