* BINARIES LIST *
*****************************************************************************/
+typedef int(*parse_cb_t)(const char *path);
+
static struct bin_info_t *_find_binary_no_lock(const char *path)
{
struct bin_info_t *bin_info;
return NULL;
}
+static int _decrease_size(size_t *size, size_t need)
+{
+ if (*size < need) {
+ PRINTERR("wrong buffer size");
+ return -EINVAL;
+ }
+
+ *size -= need;
+ return 0;
+}
/* Returns 0 on success, -errcode on fail */
-int add_binary(char *path)
+static int _add_binary(const char *path)
{
struct bin_info_t *bin_info;
int ret = 0;
}
/* Return 0 if binary added or already exists, -errcode otherwise */
-int add_binary_on_demand(char *path)
+static int _add_binary_on_demand(const char *path)
{
int ret;
- ret = add_binary(path);
+ ret = _add_binary(path);
if (!ret || ret == -EALREADY)
return 0;
}
/* Returns 0 on success, -errcode on fail */
-int remove_binary(char *path)
+static int _remove_binary(const char *path)
{
struct bin_info_t *bin_info;
int ret = 0;
return ret;
}
+static void _parse_bundle(char *bundle, size_t size,
+ struct ext_bininfo_t **resp, parse_cb_t cb)
+{
+ uint32_t cnt;
+ uint32_t len;
+ char *ptr = bundle;
+ char *path;
+ unsigned int i;
+ int ret;
+ struct ext_bininfo_t *res, *last, *tmp;
+
+ if (!cb) {
+ PRINTERR("Error! No callback for parsing bundle!");
+ return;
+ }
+
+ if (resp) {
+ res = *resp;
+ last = *resp;
+ }
+
+ if (_decrease_size(&size, sizeof(cnt)))
+ return;
+
+ cnt = *(uint32_t *)ptr;
+ ptr += sizeof(cnt);
+
+ for (i = 0; i < cnt; i++) {
+ if (_decrease_size(&size, sizeof(len)))
+ return;
+
+ len = *(uint32_t *)ptr;
+ ptr += sizeof(len);
+
+ if (_decrease_size(&size, len))
+ return;
+
+ path = malloc(len);
+ if (path == NULL) {
+ PRINTERR("No memory to alloc for path!");
+ return;
+ }
+
+ memcpy(path, ptr, len);
+ ptr += len;
+
+ ret = cb(path);
+ if (!ret && resp) {
+ tmp = malloc(sizeof(*tmp));
+ if (!tmp) {
+ PRINTERR("No memory to alloc for ext_bininfo");
+ return;
+ }
+ tmp->next = NULL;
+ tmp->path = path;
+ if (res) {
+ last->next = tmp;
+ last = tmp;
+ } else {
+ res = last = tmp;
+ }
+ } else {
+ PRINTERR("Adding failed, error code %d", ret);
+ }
+ }
+}
+
+
+void add_binaries(char *bundle, size_t size)
+{
+ _parse_bundle(bundle, size, NULL, _add_binary_on_demand);
+}
+
+struct ext_bininfo_t *add_binaries_get_new(char *bundle, size_t size)
+{
+ struct ext_bininfo_t *res = NULL;
+
+ _parse_bundle(bundle, size, &res, _add_binary_on_demand);
+
+ return res;
+}
+
+void remove_binaries(char *bundle, size_t size)
+{
+ _parse_bundle(bundle, size, NULL, _remove_binary);
+}
+
+struct ext_bininfo_t *remove_binaries_get_diff(char *bundle, size_t size)
+{
+ struct ext_bininfo_t *res = NULL;
+
+ _parse_bundle(bundle, size, &res, _remove_binary);
+
+ return res;
+}
+
+void free_bins_info(struct ext_bininfo_t *ebi)
+{
+ struct ext_bininfo_t *iter, *tmp = NULL;
+
+ if (!ebi)
+ return;
+
+ for (iter = ebi; iter; iter = iter->next) {
+ free(tmp);
+ free(iter->path);
+ tmp = iter;
+ }
+
+ /* Free last element */
+ free(tmp);
+}
+
/* Checks if binary is a target one */
bool check_binary(const char *path)
{
/* Used to call symbol search in different sections by one func */
typedef unsigned long (*find_name_cb_t)(const struct link_map *, const char *);
-/* Used to get main executable name */
-extern char *program_invocation_name;
-static bool inited = false;
-
-static int decrease_size(size_t *size, size_t need)
-{
- if (*size < need) {
- PRINTERR("wrong buffer size");
- return -EINVAL;
- }
-
- *size -= need;
- return 0;
-}
-
-static void _process_target_bins(char *bins_data, size_t avail_size)
-{
- uint32_t cnt;
- uint32_t len;
- char *ptr = bins_data;
- char *path;
- unsigned int i;
- int ret;
-
- if (decrease_size(&avail_size, sizeof(cnt)))
- return;
-
- cnt = *(uint32_t *)ptr;
- ptr += sizeof(cnt);
-
- for (i = 0; i < cnt; i++) {
- if (decrease_size(&avail_size, sizeof(len)))
- return;
+/* Indicate patching or unpatching */
+enum gp_indicate {
+ GP_PATCHING,
+ GP_UNPATCHING
+};
- len = *(uint32_t *)ptr;
- ptr += sizeof(len);
+/* Data struct to pass to patch_bin */
+struct gp_bin_patch {
+ struct feature_desc_t *f;
+ struct ext_bininfo_t *bins;
+ enum gp_indicate gpi;
+};
- if (decrease_size(&avail_size, len))
- return;
+/* Patch info data */
+struct gp_sym_patch {
+ struct link_map *map;
+ struct phdr_info *info;
+ struct probe_desc_t *probe;
+ enum gp_indicate gpi;
+};
- path = malloc(len);
- if (path == NULL) {
- // TODO Error! error! error!
- return;
- }
- memcpy(path, ptr, len);
- ptr += len;
+/* Used to get main executable name */
+extern char *program_invocation_name;
+static bool inited = false;
- /* TODO Think about removing binaries */
- ret = add_binary_on_demand(path);
- if (!ret)
- PRINTERR("Adding failed, error code %d", ret);
- /* add_binary_on_demand() allocates new buffer with realpath(),
- * so this path should be freed */
- free(path);
- }
-}
-static void *_get_orig_on_demand(unsigned long addr)
+static void *_get_pointed_demand(unsigned long addr, struct gp_sym_patch *gsp)
{
Dl_info sym_info;
+ bool is_probe_pointed = false;
int ret;
ret = dladdr(*(void **)addr, &sym_info);
!strcmp(probelib_screenshot, sym_info.dli_fname) ||
!strcmp(probelib_uihv, sym_info.dli_fname) ||
!strcmp(probelib_lsan, sym_info.dli_fname))
- return NULL;
-
- return sym_info.dli_saddr;
+ is_probe_pointed = true;
+
+ /* If we are patching binaries - return NULL if already patched,
+ * if we are unpatching binaries - return NULL if haven't been patched.
+ */
+ if (gsp->gpi == GP_PATCHING)
+ return is_probe_pointed ? NULL : sym_info.dli_saddr;
+ else
+ return is_probe_pointed ? sym_info.dli_saddr : NULL;
}
+
static inline bool _is_for_all_feature(struct feature_desc_t *feature)
{
unsigned int i;
return false;
}
-static inline bool _check_if_patch(const char *path)
+
+
+static inline bool _check_if_patch_by_stored(const char *path)
+{
+ return check_binary(path);
+}
+
+static inline bool _check_if_patch_by_list(const char *path,
+ struct ext_bininfo_t *bins)
+{
+ struct ext_bininfo_t *cur;
+
+ for (cur = bins; cur; cur = cur->next)
+ if (!strncmp(cur->path, path, PATH_MAX))
+ return true;
+
+ return false;
+}
+
+/* If there is no bins - check from stored */
+static inline bool _check_if_patch(const char *path,
+ struct ext_bininfo_t *bins)
{
- bool res;
- pid_t pid;
- char dest[PATH_MAX];
char real[PATH_MAX];
const char *check = NULL;
if (path[0] == '\0' && program_invocation_name[0] == '\0') {
/* If we are checking main executable and
* program_invocation_name was not inited yet */
- pid = getpid();
- snprintf(dest, sizeof(dest), "/proc/%d/exe", pid);
-
- if (readlink(dest, real, PATH_MAX) == -1) {
- PRINTERR("Readlink for <%s> has failed!");
- res = false;
+ if (readlink("/proc/self/exe", real, PATH_MAX) == -1) {
+ PRINTERR("Readlink has failed!");
goto check_if_patch_fail;
}
check = path;
}
- res = check_binary(check);
+ if (!bins)
+ return _check_if_patch_by_stored(check);
+ else
+ return _check_if_patch_by_list(check, bins);
check_if_patch_fail:
- return res;
+ return false;
+}
+
+static inline bool _if_patch_sym(struct probe_desc_t *probe, bool target,
+ enum gp_indicate gpi)
+{
+ if (gpi == GP_PATCHING) {
+ /* If feature is not always and flags doesn't satisfy binary's
+ * status - do not patch */
+ if ((probe->flags != GT_ALWAYS_PROBE) &&
+ ((probe->flags != GT_TARGET_PROBE) && target))
+ return false;
+ else
+ return true;
+ } else {
+ /* Unpatch only if it is not always probe, if it is target
+ * binary and if it is target probe */
+ if (probe->flags == GT_ALWAYS_PROBE)
+ return false;
+ if (probe->flags != GT_TARGET_PROBE)
+ return false;
+ if (!target)
+ return false;
+ return true;
+ }
}
return ret;
}
-/* Resolves memory protection on ro GOT sections and writes handler address */
-static void _unprotected_write(unsigned long dest, ElfW(Addr) handler,
+/* Resolves memory protection on ro GOT sections and writes src address */
+static void _unprotected_write(unsigned long dest, ElfW(Addr) src,
struct phdr_info *info)
{
int pagesize_mask = ~(getpagesize() - 1);
}
}
- *(ElfW(Addr) *)dest = handler;
+ *(ElfW(Addr) *)dest = src;
if (phdr->p_type == PT_GNU_RELRO) {
err = mprotect((void *)((base + phdr->p_vaddr) & pagesize_mask),
}
/* Find symbols for probe, stores original and patch them on demnad */
-static void _do_patch_sym(struct link_map *map, struct phdr_info *info,
- struct probe_desc_t *probe, find_name_cb_t cb)
+static void _do_patch_sym(struct gp_sym_patch *gsp, find_name_cb_t cb)
{
unsigned long addr;
+ struct probe_desc_t *probe = gsp->probe;
void *orig;
/* Patch symbol in .got.plt section */
- addr = cb(map, probe->orig_name);
+ addr = cb(gsp->map, probe->orig_name);
if (addr == 0)
return;
- orig = _get_orig_on_demand(addr);
+ orig = _get_pointed_demand(addr, gsp);
if (orig != NULL) {
- probe->orig_ptr = (ElfW(Addr))orig;
- _unprotected_write(addr, probe->handler_ptr, info);
+ if (gsp->gpi == GP_PATCHING) {
+ probe->orig_ptr = (ElfW(Addr))orig;
+ _unprotected_write(addr, probe->handler_ptr, gsp->info);
+ } else {
+ _unprotected_write(addr, probe->orig_ptr, gsp->info);
+ }
}
}
-static void _patch_sym(struct link_map *map, struct phdr_info *info,
- struct probe_desc_t *probe)
+static void _patch_sym(struct gp_sym_patch *gsp)
{
- _do_patch_sym(map, info, probe, lmap_reladdr_by_name);
- _do_patch_sym(map, info, probe, lmap_gotaddr_by_name);
+ _do_patch_sym(gsp, lmap_reladdr_by_name);
+ _do_patch_sym(gsp, lmap_gotaddr_by_name);
}
static struct link_map *_find_map(const char *path)
bool is_for_all;
bool is_ign;
unsigned int i;
- struct feature_desc_t *feature = (struct feature_desc_t *)data;
+ struct gp_bin_patch *gpd = (struct gp_bin_patch *)data;
+ struct feature_desc_t *feature = gpd->f;
+ struct gp_sym_patch gsp;
/* Get binary path */
path = info->dlpi_name;
/* Check if this binary a target one */
is_for_all = _is_for_all_feature(feature);
- target_bin = _check_if_patch(path);
+ target_bin = _check_if_patch(path, gpd->bins);
is_ign = _is_ignored(path);
if (is_ign || (!target_bin && !is_for_all))
return 0;
pi.phnum = info->dlpi_phnum;
for (i = 0; i < feature->cnt; i++) {
- /* If feature is not always and flags doesn't satisfy binary's status -
- * continue */
- if ((feature->probes[i].flags != GT_ALWAYS_PROBE) &&
- ((feature->probes[i].flags != GT_TARGET_PROBE) && target_bin))
+ if (!_if_patch_sym(&feature->probes[i], target_bin, gpd->gpi))
continue;
- _patch_sym(map, &pi, &feature->probes[i]);
+ gsp.map = map;
+ gsp.info = π
+ gsp.probe = &feature->probes[i];
+ gsp.gpi = gpd->gpi;
+
+ _patch_sym(&gsp);
}
return ret;
}
-static void _patch_target_bins(struct feature_desc_t *feature)
+static void _patch_target_bins(struct gp_bin_patch *gpd)
{
- dl_iterate_phdr(_patch_bin, feature);
+ dl_iterate_phdr(_patch_bin, gpd);
}
-void process_features(void)
+static void _process_features(enum gp_indicate gpi, struct ext_bininfo_t *bins)
{
+ struct gp_bin_patch gpd;
unsigned int i;
for (i = 0; i < FEATURES_CNT; i++) {
if (features[i] == NULL ||
!isOptionEnabled(features[i]->feature, 0))
continue;
- _patch_target_bins(features[i]);
+
+ gpd.f = features[i];
+ gpd.bins = bins;
+ gpd.gpi = gpi;
+ _patch_target_bins(&gpd);
}
}
// got_addr = lmap_reladdr_by_offset(l, reloc_arg);
got_addr = lmap_reladdr_by_name(l, func_name);
- target_bin = _check_if_patch(l->l_name);
+ target_bin = _check_if_patch(l->l_name, NULL);
exec_addr = dl_fixup_p(l, reloc_arg);
struct phdr_info pi;
bool target_bin;
bool is_for_all;
+ struct gp_sym_patch gsp;
int err;
- target_bin = _check_if_patch(path);
+ target_bin = _check_if_patch(path, NULL);
dl_reloc_p(l, scope, reloc_mode, consider_profiling);
target_bin))
continue;
- _patch_sym(l, &pi, &features[i]->probes[j]);
+ gsp.map = l;
+ gsp.info = π
+ gsp.probe = &features[i]->probes[j];
+ gsp.gpi = GP_PATCHING;
+
+ _patch_sym(&gsp);
}
}
}
-static int _init_linker_addr(void)
+int init_linker_addr(void)
{
struct link_map *lmap;
dl_fixup_p = (dl_fixup_t)(dl_fixup_off + lmap->l_addr);
dl_reloc_p = (dl_reloc_t)(dl_reloc_off + lmap->l_addr);
+ inited = true;
+
return 0;
}
void process_got_patching(char *data, size_t size)
{
- if (!inited) {
- _init_linker_addr();
- inited = true;
- }
- _process_target_bins(data, size);
- process_features();
+ struct ext_bininfo_t *bins;
+
+ if (!inited)
+ init_linker_addr();
+
+ bins = add_binaries_get_new(data, size);
+ _process_features(GP_PATCHING, bins);
+ free_bins_info(bins);
}
-void restore_got_patching(void)
+void restore_got_patching(char *data, size_t size)
{
- /* TODO patch all binaries back */
+ struct ext_bininfo_t *bins;
+
+ bins = remove_binaries_get_diff(data, size);
+ _process_features(GP_UNPATCHING, bins);
+ free_bins_info(bins);
- cleanup_binaries();
+ /* Repatch to apply non-target features */
+ _process_features(GP_PATCHING, NULL);
+}
+
+/* Used for initial total patching */
+void process_features(void)
+{
+ _process_features(GP_PATCHING, NULL);
}