Last active
February 26, 2023 08:22
-
-
Save jsimmons/cb8cdc58f8c82976ef379c126bf09efc to your computer and use it in GitHub Desktop.
Linking The Hard Way
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Attempting to find the link_map for an executable without | |
// actually using the relocations / symtab / etc so we can elide | |
// all that stuff from our ELF file. (for an executable packer) | |
// With a .got entry it should be not so difficult to do, | |
// since GOT[0] is always the link_map. (depending on what | |
// actually triggers this entry to be written by ld-linux.so.1) | |
// This code scans the aux vector for the address of the program's | |
// ELF Program Header Section. Then it scans the PHDR for the PT_DYANMIC | |
// entry which points to the .dynamic section. Then it scans that for | |
// the DT_PLTGOT entry and voila, GOT[0] is the address of our link_map | |
// which we can then use to do dynamic linking. | |
// I also tried finding the link_map through other methods that don't | |
// require the .GOT section in the ELF file. Crazy silly things like | |
// finding the Linux vsdo from the AUX vector and walking it's link_map | |
// linked-list for our own link_map. | |
// The ultimate goal was once we do our loading manually it should | |
// be possible to load symbols using their hashes instead of | |
// using the full string symbol name. | |
#include <elf.h> | |
#include <link.h> | |
#include <stdio.h> | |
int | |
main(int argc, const char **argv) | |
{ | |
const char **envp = argv + argc + 1; | |
while(*envp++); | |
Elf32_Phdr *phdr = NULL; | |
uint32_t *auxv = (uint32_t*)envp; | |
for(; *auxv; auxv += 2) | |
{ | |
if(*auxv == AT_PHDR) | |
{ | |
phdr = (void*)*(auxv + 1); | |
break; | |
} | |
} | |
while(phdr->p_type != PT_DYNAMIC) phdr++; | |
Elf32_Dyn *dyn = phdr->p_vaddr; | |
printf("PT_DYNAMIC: %p\n", dyn); | |
while(dyn->d_tag != DT_PLTGOT) dyn++; | |
struct link_map *link_map = *((uint32_t*)(dyn->d_un.d_ptr + 4)); | |
printf("our link_map : %p\n", link_map); | |
struct link_map *l = link_map; | |
for(; l != 0; l = l->l_next) | |
{ | |
printf("link_map %p base: %p dynamic: %p next %p : %s\n", | |
l, (void*)l->l_addr, l->l_ld, l->l_next, l->l_name); | |
printf("offset %p\n", (void*)l->l_ld - (void*)l->l_addr); | |
} | |
} | |
// OUTPUT: | |
// PT_DYNAMIC: 0x80496f0 | |
// our link_map : 0xf7740828 | |
// link_map 0xf7740828 base: (nil) dynamic: 0x80496f0 next 0xf7740d20 : | |
// offset 0x80496f0 | |
// link_map 0xf7740d20 base: 0xf7727000 dynamic: 0xf7729438 next 0xf7726000 : /usr/lib/libssp.so.0 | |
// offset 0x2438 | |
// link_map 0xf7726000 base: 0xf75f0000 dynamic: 0xf7722c38 next 0xf77402fc : /lib/libc.so.6 | |
// offset 0x132c38 | |
// link_map 0xf77402fc base: 0xf772b000 dynamic: 0xf7740510 next (nil) : /lib/ld-linux.so.2 | |
// offset 0x15510 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This shows how we'd ideally go backwards from the VSDO to our own | |
// link_map, but I'm not sure how to get the offset from the vsdo base | |
// address, nor what the significance of the 0x1000 bit is. | |
// I'm assuming that the variable offset between the base address and | |
// the link_map address is almost certainly due to ASLR, which kind of | |
// puts a damper on the whole party here. | |
// Need to look at this when I actually have a Linux machine to play with. | |
#include <elf.h> | |
#include <link.h> | |
#define AT_SYSINFO_EHDR 33 | |
#include <stdio.h> | |
int | |
main(int argc, const char **argv) | |
{ | |
const char **envp = argv + argc + 1; | |
while(*envp++); | |
void *vsdo = NULL; | |
Elf32_Phdr *phdr = NULL; | |
uint32_t *auxv = (uint32_t*)envp; | |
for(; *auxv; auxv += 2) | |
{ | |
if(*auxv == AT_SYSINFO_EHDR) { | |
vsdo = (void*)*(auxv + 1); | |
continue; | |
} | |
if(*auxv == AT_PHDR) | |
{ | |
phdr = (void*)*(auxv + 1); | |
continue; | |
} | |
if(*auxv == AT_NULL) | |
{ | |
break; | |
} | |
} | |
while(phdr->p_type != PT_DYNAMIC) phdr++; | |
Elf32_Dyn *dyn = (Elf32_Dyn*)phdr->p_vaddr; | |
printf("PT_DYNAMIC: %p\n", dyn); | |
while(dyn->d_tag != DT_PLTGOT) dyn++; | |
struct link_map *link_map = (struct link_map*)*((uint32_t*)(dyn->d_un.d_ptr + 4)); | |
printf("our link_map : %p\n", link_map); | |
printf("vsdo location: %p\n", vsdo); | |
size_t vsdo_link_map_offset = 0; | |
struct link_map *l = link_map; | |
for(; l != 0; l = l->l_next) | |
{ | |
printf("link_map %p base: %p dynamic: %p prev: %p next: %p | %s\n", | |
l, (void*)l->l_addr, l->l_ld, l->l_prev, l->l_next, l->l_name); | |
printf("offset %p\n", (void*)l->l_ld - (void*)l->l_addr); | |
if(l->l_next == 0) { | |
// How can I find this offset without actually going forward from | |
// our own link map? | |
// what the heck is 0x1000? | |
vsdo_link_map_offset = (uint32_t)l - (uint32_t)l->l_addr + 0x1000; | |
} | |
} | |
printf("\n\n"); | |
printf("vsdo_offset: %zu\n", vsdo_link_map_offset); | |
struct link_map *vsdo_link_map = (struct link_map*)((char *)vsdo + vsdo_link_map_offset); | |
for(l = vsdo_link_map; l != 0; l = l->l_prev) | |
{ | |
printf("link_map %p base: %p dynamic: %p prev: %p next: %p | %s\n", | |
l, (void*)l->l_addr, l->l_ld, l->l_prev, l->l_next, l->l_name); | |
printf("offset %p\n", (void*)l->l_ld - (void*)l->l_addr); | |
} | |
return 0; | |
} | |
// OUTPUT: | |
// PT_DYNAMIC: 0x80497f0 | |
// our link_map : 0xf77ee828 | |
// vsdo location: 0xf77d8000 | |
// link_map 0xf77ee828 base: (nil) dynamic: 0x80497f0 prev: (nil) next: 0xf77eed20 | | |
// offset 0x80497f0 | |
// link_map 0xf77eed20 base: 0xf77d5000 dynamic: 0xf77d7438 prev: 0xf77ee828 next: 0xf77d4000 | /usr/lib/libssp.so.0 | |
// offset 0x2438 | |
// link_map 0xf77d4000 base: 0xf769e000 dynamic: 0xf77d0c38 prev: 0xf77eed20 next: 0xf77ee2fc | /lib/libc.so.6 | |
// offset 0x132c38 | |
// link_map 0xf77ee2fc base: 0xf77d9000 dynamic: 0xf77ee510 prev: 0xf77d4000 next: (nil) | /lib/ld-linux.so.2 | |
// offset 0x15510 | |
// vsdo_offset: 90876 | |
// link_map 0xf77ee2fc base: 0xf77d9000 dynamic: 0xf77ee510 prev: 0xf77d4000 next: (nil) | /lib/ld-linux.so.2 | |
// offset 0x15510 | |
// link_map 0xf77d4000 base: 0xf769e000 dynamic: 0xf77d0c38 prev: 0xf77eed20 next: 0xf77ee2fc | /lib/libc.so.6 | |
// offset 0x132c38 | |
// link_map 0xf77eed20 base: 0xf77d5000 dynamic: 0xf77d7438 prev: 0xf77ee828 next: 0xf77d4000 | /usr/lib/libssp.so.0 | |
// offset 0x2438 | |
// link_map 0xf77ee828 base: (nil) dynamic: 0x80497f0 prev: (nil) next: 0xf77eed20 | | |
// offset 0x80497f0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment