Implement injector 91/195591/2
authorVyacheslav Cherkashin <v.cherkashin@samsung.com>
Tue, 13 Nov 2018 14:14:43 +0000 (17:14 +0300)
committerVyacheslav Cherkashin <v.cherkashin@samsung.com>
Fri, 28 Dec 2018 08:57:59 +0000 (11:57 +0300)
This injector is able to replace calls to the original functions in
the GOT section in runtime

Change-Id: I2f3cbe322b346a8ade027ac3942c922fb8e68fbf
Signed-off-by: Vyacheslav Cherkashin <v.cherkashin@samsung.com>
src/CMakeLists.txt
src/core/injector.cpp [new file with mode: 0644]
src/core/injector.h [new file with mode: 0644]
src/core/internal_deps.h
src/core/internal_libc.cpp [new file with mode: 0644]
src/core/internal_libc.h [new file with mode: 0644]

index e4d62e0ad88d850716390266e599bf22b091acd9..a7cac44ce82c1982a1fb9af1d8ef7a612657c463 100644 (file)
@@ -14,6 +14,8 @@ include_directories(
 # setup sources
 set(SRC
   core/core.cpp
+  core/injector.cpp
+  core/internal_libc.cpp
 )
 
 add_library(${PROJECT_NAME} SHARED ${SRC})
diff --git a/src/core/injector.cpp b/src/core/injector.cpp
new file mode 100644 (file)
index 0000000..12edc13
--- /dev/null
@@ -0,0 +1,168 @@
+#include <link.h>
+#include <sys/mman.h>
+#include "injector.h"
+#include "internal_libc.h"
+
+
+#if __WORDSIZE == 64
+# define ELF_R_SYM(i) ELF64_R_SYM(i)
+#elif __WORDSIZE == 32
+# define ELF_R_SYM(i) ELF32_R_SYM(i)
+#else
+# error "Unsupported word size"
+#endif
+
+
+namespace __swap {
+namespace injector {
+
+namespace elf {
+using Addr = ElfW(Addr);
+using Dyn = ElfW(Dyn);
+using Rel = ElfW(Rel);
+using Rela = ElfW(Rela);
+using Sym = ElfW(Sym);
+using Sxword = ElfW(Sxword);
+using Xword = ElfW(Xword);
+
+// TODO: Add prelinked libraries support
+#if defined(__arm__) || defined(__i386__)
+// ARM and x86 never uses Elf32_Rela relocations for the dynamic linker.
+// Prelinked libraries may use Elf32_Rela though.
+using PltRel = Rel;
+#else
+using PltRel = Rela;
+#endif
+
+
+template <typename T, Sxword TAG_ADDR, Sxword TAG_SIZE>
+struct Table
+{
+    using Type = T;
+    T *table = nullptr;
+    Xword size = {};
+
+    bool consume(const Dyn *dyn) noexcept
+    {
+        if (dyn->d_tag == TAG_ADDR) {
+            table = reinterpret_cast<T*>(dyn->d_un.d_ptr);
+            return true;
+        } else if (dyn->d_tag == TAG_SIZE) {
+            size = dyn->d_un.d_val;
+            return true;
+        }
+        return false;
+    }
+};
+
+
+using RelTable = Table<Rel, DT_REL, DT_RELSZ>;
+using RelaTable = Table<Rela, DT_RELA, DT_RELASZ>;
+using JmprelTable = Table<PltRel, DT_JMPREL, DT_PLTRELSZ>;
+using SymbolTable = Table<Sym, DT_SYMTAB, DT_SYMENT>;
+using StringTable = Table<const char, DT_STRTAB, DT_STRSZ>;
+
+} // namespace elf
+
+
+static void rewrite_addr(unsigned long *ptr, unsigned long value) noexcept
+{
+    if (*ptr == value)
+        return;
+
+    auto addr = reinterpret_cast<unsigned long>(ptr);
+
+    // TODO: Replace it.
+    //       To check protect status, read it from "/proc/self/maps"
+
+    // Try to make the page read/write accessible, which is hackish.
+    // Required for some shared libraries.
+    auto page = reinterpret_cast<void *>(addr & ~(0x1000 - 1));
+    internal::mprotect(page, 0x1000, PROT_READ | PROT_WRITE);
+    *ptr = value;
+}
+
+
+struct injection_data {
+    struct injection_info *info;
+    unsigned long size;
+};
+
+template <typename Table>
+static void rewrite_elftable(const Table &jumps,
+                             const elf::StringTable &strings,
+                             const elf::SymbolTable &symbols,
+                             const elf::Addr base,
+                             const injection_data *data) noexcept
+{
+    const auto jumps_table_end = reinterpret_cast<const char *>(jumps.table) + jumps.size;
+    const auto rela_end = reinterpret_cast<const typename Table::Type *>(jumps_table_end);
+    for (auto rela = jumps.table; rela < rela_end; ++rela) {
+        const auto index = ELF_R_SYM(rela->r_info);
+        const char* symname = strings.table + symbols.table[index].st_name;
+        auto addr = rela->r_offset + base;
+
+        // optimization
+        if (symname[0] == '\0')
+            continue;
+
+        for (auto info = data->info, end = data->info + data->size; info != end; ++info) {
+            if (!internal::strcmp(symname, info->name)) {
+                auto *ptr = reinterpret_cast<unsigned long *>(addr);
+                rewrite_addr(ptr, info->addr);
+            }
+        }
+    }
+}
+
+static void rewrite_symbols(const elf::Dyn *dyn,
+                            const elf::Addr base,
+                            const injection_data *data) noexcept
+{
+    elf::SymbolTable symbols;
+    elf::RelTable rels;
+    elf::RelaTable relas;
+    elf::JmprelTable jmprels;
+    elf::StringTable strings;
+
+    // initialize the elf tables
+    for (; dyn->d_tag != DT_NULL; ++dyn) {
+        symbols.consume(dyn)
+                || strings.consume(dyn)
+                || rels.consume(dyn)
+                || relas.consume(dyn)
+                || jmprels.consume(dyn);
+    }
+
+    // find symbols to rewrite
+    rewrite_elftable(rels, strings, symbols, base, data);
+    rewrite_elftable(relas, strings, symbols, base, data);
+    rewrite_elftable(jmprels, strings, symbols, base, data);
+}
+
+static int phdr_callback(dl_phdr_info *info, size_t /*size*/, void *data) noexcept
+{
+    auto *inject_data = reinterpret_cast<const injection_data *>(data);
+
+    for (auto phdr = info->dlpi_phdr, phdr_end = phdr + info->dlpi_phnum; phdr != phdr_end; ++phdr) {
+        if (phdr->p_type == PT_DYNAMIC) {
+            auto *dyn = reinterpret_cast<const elf::Dyn *>(phdr->p_vaddr + info->dlpi_addr);
+            rewrite_symbols(dyn, info->dlpi_addr, inject_data);
+        }
+    }
+
+    return 0;
+}
+
+void inject(injection_info info[], unsigned long size) noexcept
+{
+    const injection_data inject_data = {
+        .info = info,
+        .size = size,
+    };
+
+    dl_iterate_phdr(phdr_callback, const_cast<injection_data *>(&inject_data));
+}
+
+} // namespace injector
+} // namespace __swap
diff --git a/src/core/injector.h b/src/core/injector.h
new file mode 100644 (file)
index 0000000..e1c2c44
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef ELF_INJECTOR_H
+#define ELF_INJECTOR_H
+
+namespace __swap {
+namespace injector {
+
+struct injection_info {
+    const char *name;
+    unsigned long addr;
+};
+
+#define INJECTION_INFO_INIT(name_, addr_) { .name = (name_), .addr = (unsigned long)(addr_) }
+
+
+void inject(injection_info info[], unsigned long size) noexcept;
+
+} // namespace injector
+} // namespace __swap
+
+#endif // ELF_INJECTOR_H
index 25281aa856f9b0a763ea92f9d5f4d381f8c84d80..ebad5009e02445472c52614cdf8a1e294e359364 100644 (file)
@@ -3,4 +3,19 @@
 
 #define SWAP_INTERFACE_ATTRIBUTE __attribute__((visibility("default")))
 
+namespace __swap {
+
+typedef unsigned long      uptr;
+typedef signed   long      sptr;
+typedef unsigned char      u8;
+typedef unsigned short     u16;
+typedef unsigned int       u32;
+typedef unsigned long long u64;
+typedef signed   char      s8;
+typedef signed   short     s16;
+typedef signed   int       s32;
+typedef signed   long long s64;
+
+} // namespace __swap
+
 #endif // INTERNAL_DEPS_H
diff --git a/src/core/internal_libc.cpp b/src/core/internal_libc.cpp
new file mode 100644 (file)
index 0000000..9f744a5
--- /dev/null
@@ -0,0 +1,28 @@
+#include <sys/mman.h>       // for ::mprotect()
+#include "internal_libc.h"
+
+
+namespace __swap {
+namespace internal {
+
+int strcmp(const char *s1, const char *s2) noexcept
+{
+    for (;; ++s1, ++s2) {
+        unsigned c1 = *s1;
+        unsigned c2 = *s2;
+        if (c1 != c2)
+            return (c1 < c2) ? -1 : 1;
+        if (c1 == 0)
+            break;
+    }
+    return 0;
+}
+
+int mprotect(void *addr, uptr len, int prot) noexcept
+{
+    // TODO: Replace with custom system call.
+    return ::mprotect(addr, len, prot);
+}
+
+} // namespace internal
+} // namespace __swap
diff --git a/src/core/internal_libc.h b/src/core/internal_libc.h
new file mode 100644 (file)
index 0000000..f842d9b
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef INTERNAL_LIBC_H
+#define INTERNAL_LIBC_H
+
+#include "internal_deps.h"
+
+namespace __swap {
+namespace internal {
+
+int strcmp(const char *s1, const char *s2) noexcept;
+int mprotect(void *addr, uptr len, int prot) noexcept;
+
+} // namespace internal
+} // namespace __swap
+
+#endif // INTERNAL_LIBC_H