about summary refs log tree commit diff
path: root/frida_mode/src/ranges.c
diff options
context:
space:
mode:
Diffstat (limited to 'frida_mode/src/ranges.c')
-rw-r--r--frida_mode/src/ranges.c457
1 files changed, 318 insertions, 139 deletions
diff --git a/frida_mode/src/ranges.c b/frida_mode/src/ranges.c
index 49ef5a62..6fcbd258 100644
--- a/frida_mode/src/ranges.c
+++ b/frida_mode/src/ranges.c
@@ -1,9 +1,11 @@
-// 0x123-0x321
-// module.so
+#include "frida-gum.h"
 
-#include "ranges.h"
 #include "debug.h"
 
+#include "lib.h"
+#include "ranges.h"
+#include "stalker.h"
+
 #define MAX_RANGES 20
 
 typedef struct {
@@ -14,15 +16,11 @@ typedef struct {
 
 } convert_name_ctx_t;
 
-typedef struct {
-
-  GumStalker *stalker;
-  GArray *    array;
-
-} include_range_ctx_t;
-
-GArray * ranges = NULL;
-gboolean exclude_ranges = false;
+GArray *module_ranges = NULL;
+GArray *libs_ranges = NULL;
+GArray *include_ranges = NULL;
+GArray *exclude_ranges = NULL;
+GArray *ranges = NULL;
 
 static void convert_address_token(gchar *token, GumMemoryRange *range) {
 
@@ -159,214 +157,395 @@ static void convert_token(gchar *token, GumMemoryRange *range) {
 
 }
 
-static gboolean include_ranges(const GumRangeDetails *details,
-                               gpointer               user_data) {
+gint range_sort(gconstpointer a, gconstpointer b) {
 
-  include_range_ctx_t *ctx = (include_range_ctx_t *)user_data;
-  GArray *             array = (GArray *)ctx->array;
-  GumAddress           base = details->range->base_address;
-  GumAddress limit = details->range->base_address + details->range->size;
+  return ((GumMemoryRange *)a)->base_address -
+         ((GumMemoryRange *)b)->base_address;
 
-  OKF("Range for inclusion 0x%016" G_GINT64_MODIFIER
-      "x-0x%016" G_GINT64_MODIFIER "x",
-      base, limit);
+}
 
-  for (int i = 0; i < array->len; i++) {
+static gboolean print_ranges_callback(const GumRangeDetails *details,
+                                      gpointer               user_data) {
 
-    GumMemoryRange *range = &g_array_index(array, GumMemoryRange, i);
-    GumAddress      range_base = range->base_address;
-    GumAddress      range_limit = range->base_address + range->size;
+  if (details->file == NULL) {
 
-    /* Before the region */
-    if (range_limit < base) { continue; }
+    OKF("MAP - 0x%016" G_GINT64_MODIFIER "x - 0x%016" G_GINT64_MODIFIER "X",
+        details->range->base_address,
+        details->range->base_address + details->range->size);
 
-    /* After the region */
-    if (range_base > limit) {
+  } else {
 
-      GumMemoryRange exclude = {.base_address = base, .size = limit - base};
-      OKF("\t Excluding 0x%016" G_GINT64_MODIFIER "x-0x%016" G_GINT64_MODIFIER
-          "x",
-          base, limit);
-      gum_stalker_exclude(ctx->stalker, &exclude);
-      return true;
+    OKF("MAP - 0x%016" G_GINT64_MODIFIER "x - 0x%016" G_GINT64_MODIFIER
+        "X %s(0x%016" G_GINT64_MODIFIER "x)",
+        details->range->base_address,
+        details->range->base_address + details->range->size,
+        details->file->path, details->file->offset);
 
-    }
+  }
 
-    /* Overlap the start of the region */
-    if (range_base < base) {
+  return true;
 
-      /* Range contains the region */
-      if (range_limit > limit) {
+}
 
-        return true;
+static void print_ranges(char *key, GArray *ranges) {
 
-      } else {
+  OKF("Range: %s Length: %d", key, ranges->len);
+  for (int i = 0; i < ranges->len; i++) {
 
-        base = range_limit;
-        continue;
+    GumMemoryRange *curr = &g_array_index(ranges, GumMemoryRange, i);
+    GumAddress      curr_limit = curr->base_address + curr->size;
+    OKF("Range: %s Idx: %3d - 0x%016" G_GINT64_MODIFIER
+        "x-0x%016" G_GINT64_MODIFIER "x",
+        key, i, curr->base_address, curr_limit);
 
-      }
+  }
 
-      /* Overlap the end of the region */
+}
 
-    } else {
+static gboolean collect_module_ranges_callback(const GumRangeDetails *details,
+                                               gpointer user_data) {
 
-      GumMemoryRange exclude = {.base_address = base,
-                                .size = range_base - base};
-      OKF("\t Excluding 0x%016" G_GINT64_MODIFIER "x-0x%016" G_GINT64_MODIFIER
-          "x",
-          base, range_base);
-      gum_stalker_exclude(ctx->stalker, &exclude);
-      /* Extend past the end of the region */
-      if (range_limit >= limit) {
+  GArray *       ranges = (GArray *)user_data;
+  GumMemoryRange range = *details->range;
+  g_array_append_val(ranges, range);
+  return TRUE;
 
-        return true;
+}
 
-        /* Contained within the region */
+static GArray *collect_module_ranges(void) {
 
-      } else {
+  GArray *result;
+  result = g_array_new(false, false, sizeof(GumMemoryRange));
+  gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS,
+                               collect_module_ranges_callback, result);
+  print_ranges("Modules", result);
+  return result;
 
-        base = range_limit;
-        continue;
+}
 
-      }
+static GArray *collect_ranges(char *env_key) {
 
-    }
+  char *         env_val;
+  gchar **       tokens;
+  int            token_count;
+  GumMemoryRange range;
+  int            i;
+  GArray *       result;
+
+  result = g_array_new(false, false, sizeof(GumMemoryRange));
+
+  env_val = getenv(env_key);
+  if (env_val == NULL) return result;
+
+  tokens = g_strsplit(env_val, ",", MAX_RANGES);
+
+  for (token_count = 0; tokens[token_count] != NULL; token_count++)
+    ;
+
+  for (i = 0; i < token_count; i++) {
+
+    convert_token(tokens[i], &range);
+    g_array_append_val(result, range);
 
   }
 
-  GumMemoryRange exclude = {.base_address = base, .size = limit - base};
-  OKF("\t Excluding 0x%016" G_GINT64_MODIFIER "x-0x%016" G_GINT64_MODIFIER "x",
-      base, limit);
-  gum_stalker_exclude(ctx->stalker, &exclude);
-  return true;
+  g_array_sort(result, range_sort);
 
-}
+  /* Check for overlaps */
+  for (i = 1; i < token_count; i++) {
 
-gint range_sort(gconstpointer a, gconstpointer b) {
+    GumMemoryRange *prev = &g_array_index(result, GumMemoryRange, i - 1);
+    GumMemoryRange *curr = &g_array_index(result, GumMemoryRange, i);
+    GumAddress      prev_limit = prev->base_address + prev->size;
+    GumAddress      curr_limit = curr->base_address + curr->size;
+    if (prev_limit > curr->base_address) {
 
-  return ((GumMemoryRange *)a)->base_address -
-         ((GumMemoryRange *)b)->base_address;
+      FATAL("OVerlapping ranges 0x%016" G_GINT64_MODIFIER
+            "x-0x%016" G_GINT64_MODIFIER "x 0x%016" G_GINT64_MODIFIER
+            "x-0x%016" G_GINT64_MODIFIER "x",
+            prev->base_address, prev_limit, curr->base_address, curr_limit);
+
+    }
+
+  }
+
+  print_ranges(env_key, result);
+
+  g_strfreev(tokens);
+
+  return result;
 
 }
 
-static gboolean print_ranges(const GumRangeDetails *details,
-                             gpointer               user_data) {
+static GArray *collect_libs_ranges(void) {
 
-  if (details->file == NULL) {
+  GArray *       result;
+  GumMemoryRange range;
+  result = g_array_new(false, false, sizeof(GumMemoryRange));
 
-    OKF("MAP - 0x%016" G_GINT64_MODIFIER "x - 0x%016" G_GINT64_MODIFIER "X",
-        details->range->base_address,
-        details->range->base_address + details->range->size);
+  if (getenv("AFL_INST_LIBS") == NULL) {
+
+    range.base_address = lib_get_text_base();
+    range.size = lib_get_text_limit() - lib_get_text_base();
 
   } else {
 
-    OKF("MAP - 0x%016" G_GINT64_MODIFIER "x - 0x%016" G_GINT64_MODIFIER
-        "X %s(0x%016" G_GINT64_MODIFIER "x)",
-        details->range->base_address,
-        details->range->base_address + details->range->size,
-        details->file->path, details->file->offset);
+    range.base_address = 0;
+    range.size = G_MAXULONG;
 
   }
 
+  g_array_append_val(result, range);
+
+  print_ranges("AFL_INST_LIBS", result);
+
+  return result;
+
+}
+
+static gboolean intersect_range(GumMemoryRange *rr, GumMemoryRange *ra,
+                                GumMemoryRange *rb) {
+
+  GumAddress rab = ra->base_address;
+  GumAddress ral = rab + ra->size;
+
+  GumAddress rbb = rb->base_address;
+  GumAddress rbl = rbb + rb->size;
+
+  GumAddress rrb = 0;
+  GumAddress rrl = 0;
+
+  rr->base_address = 0;
+  rr->size = 0;
+
+  /* ra is before rb */
+  if (ral < rbb) { return false; }
+
+  /* ra is after rb */
+  if (rab > rbl) { return true; }
+
+  /* The largest of the two base addresses */
+  rrb = rab > rbb ? rab : rbb;
+
+  /* The smallest of the two limits */
+  rrl = ral < rbl ? ral : rbl;
+
+  rr->base_address = rrb;
+  rr->size = rrl - rrb;
   return true;
 
 }
 
-void ranges_init(GumStalker *stalker) {
+static GArray *intersect_ranges(GArray *a, GArray *b) {
 
-  char *         showmaps;
-  char *         include;
-  char *         exclude;
-  char *         list;
-  gchar **       tokens;
-  int            token_count;
-  GumMemoryRange range;
+  GArray *        result;
+  GumMemoryRange *ra;
+  GumMemoryRange *rb;
+  GumMemoryRange  ri;
 
-  int i;
+  result = g_array_new(false, false, sizeof(GumMemoryRange));
 
-  showmaps = getenv("AFL_FRIDA_DEBUG_MAPS");
-  include = getenv("AFL_FRIDA_INST_RANGES");
-  exclude = getenv("AFL_FRIDA_EXCLUDE_RANGES");
+  for (int i = 0; i < a->len; i++) {
 
-  if (showmaps) {
+    ra = &g_array_index(a, GumMemoryRange, i);
+    for (int j = 0; j < b->len; j++) {
 
-    gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, print_ranges, NULL);
+      rb = &g_array_index(b, GumMemoryRange, j);
 
-  }
+      if (!intersect_range(&ri, ra, rb)) { break; }
+
+      if (ri.size == 0) { continue; }
 
-  if (include != NULL && exclude != NULL) {
+      g_array_append_val(result, ri);
 
-    FATAL(
-        "Cannot specifify both AFL_FRIDA_INST_RANGES and "
-        "AFL_FRIDA_EXCLUDE_RANGES");
+    }
 
   }
 
-  if (include == NULL && exclude == NULL) { return; }
+  return result;
 
-  list = include == NULL ? exclude : include;
-  exclude_ranges = include == NULL ? true : false;
+}
 
-  tokens = g_strsplit(list, ",", MAX_RANGES);
+static GArray *subtract_ranges(GArray *a, GArray *b) {
 
-  for (token_count = 0; tokens[token_count] != NULL; token_count++)
-    ;
+  GArray *        result;
+  GumMemoryRange *ra;
+  GumAddress      ral;
+  GumMemoryRange *rb;
+  GumMemoryRange  ri;
+  GumMemoryRange  rs;
 
-  ranges = g_array_sized_new(false, false, sizeof(GumMemoryRange), token_count);
+  result = g_array_new(false, false, sizeof(GumMemoryRange));
 
-  for (i = 0; i < token_count; i++) {
+  for (int i = 0; i < a->len; i++) {
 
-    convert_token(tokens[i], &range);
-    g_array_append_val(ranges, range);
+    ra = &g_array_index(a, GumMemoryRange, i);
+    ral = ra->base_address + ra->size;
+    for (int j = 0; j < b->len; j++) {
+
+      rb = &g_array_index(b, GumMemoryRange, j);
+
+      /*
+       * If rb is after ra, we have no more possible intersections and we can
+       * simply keep the remaining range
+       */
+      if (!intersect_range(&ri, ra, rb)) { break; }
+
+      /*
+       * If there is no intersection, then rb must be before ra, so we must
+       * continue
+       */
+      if (ri.size == 0) { continue; }
+
+      /*
+       * If the intersection is part way through the range, then we keep the
+       * start of the range
+       */
+      if (ra->base_address < ri.base_address) {
+
+        rs.base_address = ra->base_address;
+        rs.size = ri.base_address - ra->base_address;
+        g_array_append_val(result, rs);
+
+      }
+
+      /*
+       * If the intersection extends past the limit of the range, then we should
+       * continue with the next range
+       */
+      if ((ri.base_address + ri.size) > ral) {
+
+        ra->base_address = ral;
+        ra->size = 0;
+        break;
+
+      }
+
+      /*
+       * Otherwise we advance the base of the range to the end of the
+       * intersection and continue with the remainder of the range
+       */
+      ra->base_address = ri.base_address + ri.size;
+      ra->size = ral - ra->base_address;
+
+    }
+
+    /*
+     * When we have processed all the possible intersections, we add what is
+     * left
+     */
+    if (ra->size != 0) g_array_append_val(result, *ra);
 
   }
 
-  g_array_sort(ranges, range_sort);
+  return result;
 
-  /* Check for overlaps */
-  for (i = 1; i < token_count; i++) {
+}
 
-    GumMemoryRange *prev = &g_array_index(ranges, GumMemoryRange, i - 1);
-    GumMemoryRange *curr = &g_array_index(ranges, GumMemoryRange, i);
-    GumAddress      prev_limit = prev->base_address + prev->size;
-    GumAddress      curr_limit = curr->base_address + curr->size;
-    if (prev_limit > curr->base_address) {
+static GArray *merge_ranges(GArray *a) {
 
-      FATAL("OVerlapping ranges 0x%016" G_GINT64_MODIFIER
-            "x-0x%016" G_GINT64_MODIFIER "x 0x%016" G_GINT64_MODIFIER
-            "x-0x%016" G_GINT64_MODIFIER "x",
-            prev->base_address, prev_limit, curr->base_address, curr_limit);
+  GArray *        result;
+  GumMemoryRange  rp;
+  GumMemoryRange *r;
+
+  result = g_array_new(false, false, sizeof(GumMemoryRange));
+  if (a->len == 0) return result;
+
+  rp = g_array_index(a, GumMemoryRange, 0);
+
+  for (int i = 1; i < a->len; i++) {
+
+    r = &g_array_index(a, GumMemoryRange, i);
+
+    if (rp.base_address + rp.size == r->base_address) {
+
+      rp.size += r->size;
+
+    } else {
+
+      g_array_append_val(result, rp);
+      rp.base_address = r->base_address;
+      rp.size = r->size;
+      continue;
 
     }
 
   }
 
-  for (i = 0; i < token_count; i++) {
+  g_array_append_val(result, rp);
 
-    GumMemoryRange *curr = &g_array_index(ranges, GumMemoryRange, i);
-    GumAddress      curr_limit = curr->base_address + curr->size;
-    OKF("Range %3d - 0x%016" G_GINT64_MODIFIER "x-0x%016" G_GINT64_MODIFIER "x",
-        i, curr->base_address, curr_limit);
+  return result;
+
+}
+
+void ranges_init(void) {
+
+  GumMemoryRange  ri;
+  GArray *        step1;
+  GArray *        step2;
+  GArray *        step3;
+  GArray *        step4;
+  GumMemoryRange *r;
+  GumStalker *    stalker;
+
+  if (getenv("AFL_FRIDA_DEBUG_MAPS") != NULL) {
+
+    gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, print_ranges_callback,
+                                 NULL);
 
   }
 
-  if (include == NULL) {
+  module_ranges = collect_module_ranges();
+  libs_ranges = collect_libs_ranges();
+  include_ranges = collect_ranges("AFL_FRIDA_INST_RANGES");
 
-    for (i = 0; i < token_count; i++) {
+  /* If include ranges is empty, then assume everything is included */
+  if (include_ranges->len == 0) {
 
-      gum_stalker_exclude(stalker, &g_array_index(ranges, GumMemoryRange, i));
+    ri.base_address = 0;
+    ri.size = G_MAXULONG;
+    g_array_append_val(include_ranges, ri);
 
-    }
+  }
 
-  } else {
+  exclude_ranges = collect_ranges("AFL_FRIDA_EXCLUDE_RANGES");
 
-    include_range_ctx_t ctx = {.stalker = stalker, .array = ranges};
-    gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, include_ranges, &ctx);
+  /* Intersect with .text section of main executable unless AFL_INST_LIBS */
+  step1 = intersect_ranges(module_ranges, libs_ranges);
+  print_ranges("step1", step1);
+
+  /* Intersect with AFL_FRIDA_INST_RANGES */
+  step2 = intersect_ranges(step1, include_ranges);
+  print_ranges("step2", step2);
+
+  /* Subtract AFL_FRIDA_EXCLUDE_RANGES */
+  step3 = subtract_ranges(step2, exclude_ranges);
+  print_ranges("step3", step3);
+
+  /*
+   * After step3, we have the total ranges to be instrumented, we now subtract
+   * that from the original ranges of the modules to configure stalker.
+   */
+
+  step4 = subtract_ranges(module_ranges, step3);
+  print_ranges("step4", step4);
+
+  ranges = merge_ranges(step4);
+  print_ranges("final", ranges);
+
+  stalker = stalker_get();
+
+  for (int i = 0; i < ranges->len; i++) {
+
+    r = &g_array_index(ranges, GumMemoryRange, i);
+    gum_stalker_exclude(stalker, r);
 
   }
 
-  g_strfreev(tokens);
+  g_array_free(step4, TRUE);
+  g_array_free(step3, TRUE);
+  g_array_free(step2, TRUE);
+  g_array_free(step1, TRUE);
 
 }
 
@@ -382,13 +561,13 @@ gboolean range_is_excluded(gpointer address) {
     GumMemoryRange *curr = &g_array_index(ranges, GumMemoryRange, i);
     GumAddress      curr_limit = curr->base_address + curr->size;
 
-    if (test < curr->base_address) { return !exclude_ranges; }
+    if (test < curr->base_address) { return false; }
 
-    if (test < curr_limit) { return exclude_ranges; }
+    if (test < curr_limit) { return true; }
 
   }
 
-  return !exclude_ranges;
+  return false;
 
 }