1 // taken from http://www.codeproject.com/Articles/70302/Redirecting-functions-in-shared-ELF-libraries. 2 module elfhook; 3 4 import core.sys.linux.elf; 5 import core.sys.posix.unistd; 6 import core.sys.posix.sys.mman; 7 8 import core.stdc.errno; 9 10 import std.exception; 11 import std.string : toStringz; 12 import std.conv : to; 13 import std.range : only; 14 15 import sharedlib; 16 import elf; 17 import elf.low; 18 19 20 version(linux): 21 @system: 22 23 version(X86_64) 24 { 25 alias Elf_Rel = Elf64_Rela; 26 alias ELF_R_SYM = ELF64_R_SYM; 27 } 28 else version(X86) 29 { 30 alias Elf_Rel = Elf32_Rela; 31 alias ELF_R_SYM = ELF32_R_SYM; 32 } 33 else static assert(false, "Unsupported architecture."); 34 35 36 void* elfHook(ELF elf, in void* address, in string name, in void* substitution) 37 { 38 assert(address !is null); 39 assert(name !is null); 40 assert(substitution !is null); 41 42 size_t pagesize = sysconf(_SC_PAGESIZE); 43 44 ELFSection dynsym; // ".dynsym" 45 ELFSection rel_plt; // ".rela.plt" 46 ELFSection rel_dyn; // ".rela.dyn" 47 ELFSymbol symbol; //symbol table entry for symbol named "name" 48 49 Elf_Rel *rel_plt_table; //array with ".rel.plt" entries 50 Elf_Rel *rel_dyn_table; //array with ".rel.dyn" entries 51 52 size_t name_index = void; 53 size_t rel_plt_amount = void; // amount of ".rel.plt" entries 54 size_t rel_dyn_amount = void; // amount of ".rel.dyn" entries 55 size_t *name_address = null; 56 57 void *original; //address of the symbol being substituted 58 59 foreach (section; elf.sections) 60 { 61 if (section.type == SectionType.dynamicLoaderSymbolTable) 62 dynsym = section; 63 if (section.name.to!string == ".rela.plt") 64 rel_plt = section; 65 if (section.name.to!string == ".rela.dyn") 66 rel_dyn = section; 67 } 68 69 foreach (section; only(".symtab", ".dynsym")) 70 { 71 auto s = elf.getSection(section); 72 int i = 0; 73 foreach (sym; SymbolTable(s).symbols) 74 { 75 if (name == sym.name) 76 { 77 symbol = sym; 78 name_index = cast(size_t) i; 79 goto L0; 80 } 81 ++i; 82 } 83 } 84 85 L0: 86 87 rel_plt_table = cast(Elf_Rel*) ((cast(size_t) address) + rel_plt.address); 88 rel_plt_amount = rel_plt.size / Elf_Rel.sizeof; 89 90 rel_dyn_table = cast(Elf_Rel*) ((cast(size_t) address) + rel_dyn.address); 91 rel_dyn_amount = rel_dyn.size / Elf_Rel.sizeof; 92 93 foreach (i; 0 .. rel_plt_amount) 94 { 95 if (ELF_R_SYM(rel_plt_table[i].r_info) == name_index) 96 { 97 name_address = cast(size_t *) ((cast(size_t) address) + rel_plt_table[i].r_offset); 98 99 // mark a memory page that contains the relocation as writable. 100 mprotect(cast(void*) ((cast(size_t) name_address) & (((size_t.sizeof)-1) ^ (pagesize - 1))), pagesize, PROT_READ | PROT_WRITE); 101 102 // save the original function address. 103 original = cast(void*)* name_address; 104 105 // and replace it with the substitution. 106 *name_address = cast(size_t) substitution; 107 108 break; // the target symbol appears in ".rel.plt" only once. 109 } 110 } 111 112 if (original) 113 return original; 114 115 foreach (i; 0 .. rel_dyn_amount) 116 { 117 if (ELF_R_SYM(rel_dyn_table[i].r_info) == name_index) 118 { 119 // get the relocation address (address of a relative CALL (0xE8) instruction's argument) 120 name_address = cast(size_t*) ((cast(size_t) address) + rel_dyn_table[i].r_offset); 121 122 if (!original) 123 { 124 // calculate an address of the original function by a relative CALL (0xE8) instruction's argument. 125 original = cast(void*) (*name_address + cast(size_t) name_address + size_t.sizeof); 126 } 127 128 // mark a memory page that contains the relocation as writable. 129 mprotect(cast(void*) ((cast(size_t) name_address) & (((size_t.sizeof)-1) ^ (pagesize - 1))), pagesize, PROT_READ | PROT_WRITE); 130 131 if (errno) 132 { 133 errnoEnforce(false, "failed to mprotect."); 134 } 135 136 // calculate a new relative CALL (0xE8) instruction's argument for the substitutional function and write it down. 137 *name_address = cast(size_t) substitution - cast(size_t) name_address - size_t.sizeof; 138 139 // mark a memory page that contains the relocation back as executable. 140 mprotect(cast(void*) ((cast(size_t) name_address) & (((size_t.sizeof)-1) ^ (pagesize - 1))), pagesize, PROT_READ | PROT_EXEC); 141 142 if (errno) 143 { 144 // then restore the original function address. 145 *name_address = cast(size_t) original - cast(size_t) name_address - size_t.sizeof; 146 errnoEnforce(false, "failed to mprotect."); 147 } 148 } 149 } 150 return original; 151 } 152 153 154 void* hook(in string filename, in string functionName, in void* substitutionAddress) 155 { 156 assert(filename !is null, "No file given."); 157 158 auto lib = new SharedLibrary(filename, RTLD_LAZY); 159 const address = lib.getLoadedAddr; 160 assert(address !is null, "failed to get address that libarary loaded."); 161 return elfHook(ELF.fromFile(filename), address, functionName, substitutionAddress); 162 }