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