Skip to content

Commit

Permalink
Merge branch 'release-1.1'
Browse files Browse the repository at this point in the history
* Fix a bug with __DATA segement remapping (#6)
* Minor code changes: add asserts, replace #imports
  with #includes, add extern "C" wrapper for C++
  compatibility;
  • Loading branch information
rodionovd committed Oct 12, 2014
2 parents a633141 + b87ae6e commit 259054c
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 48 deletions.
131 changes: 84 additions & 47 deletions rd_route.c
Expand Up @@ -2,35 +2,36 @@
// This work is free. You can redistribute it and/or modify it
// under the terms of the Do What The Fuck You Want To Public License, Version 2,
// as published by Sam Hocevar. See the COPYING file for more details.
#import <stdlib.h> // realloc()
#import <libgen.h> // basename()
#import <stdio.h> // fprintf()
#import <dlfcn.h> // dladdr()
#include <stdlib.h> // realloc()
#include <libgen.h> // basename()
#include <assert.h> // assert()
#include <stdio.h> // fprintf()
#include <dlfcn.h> // dladdr()

#import "TargetConditionals.h"
#include "TargetConditionals.h"
#if defined(__i386__) || defined(__x86_64__)
#if !(TARGET_IPHONE_SIMULATOR)
#import <mach/mach_vm.h> // mach_vm_*
#else
#import <mach/vm_map.h> // vm_*
#define mach_vm_address_t vm_address_t
#define mach_vm_size_t vm_size_t
#define mach_vm_allocate vm_allocate
#define mach_vm_deallocate vm_deallocate
#define mach_vm_write vm_write
#define mach_vm_remap vm_remap
#define mach_vm_protect vm_protect
#define NSLookupSymbolInImage(...) NULL
#define NSAddressOfSymbol(...) NULL
#endif
#if !(TARGET_IPHONE_SIMULATOR)
#include <mach/mach_vm.h> // mach_vm_*
#else
#include <mach/vm_map.h> // vm_*
#define mach_vm_address_t vm_address_t
#define mach_vm_size_t vm_size_t
#define mach_vm_allocate vm_allocate
#define mach_vm_deallocate vm_deallocate
#define mach_vm_write vm_write
#define mach_vm_remap vm_remap
#define mach_vm_protect vm_protect
#define NSLookupSymbolInImage(...) ((void)0)
#define NSAddressOfSymbol(...) ((void)0)
#endif
#else
#error rd_route doesn't work on iOS
#error rd_route doesn't work on iOS
#endif

#import <mach-o/dyld.h> // _dyld_*
#import <mach-o/nlist.h> // nlist/nlist_64
#import <mach/mach_init.h> // mach_task_self()
#import "rd_route.h"
#include <mach-o/dyld.h> // _dyld_*
#include <mach-o/nlist.h> // nlist/nlist_64
#include <mach/mach_init.h> // mach_task_self()
#include "rd_route.h"

#define RDErrorLog(format, ...) fprintf(stderr, "%s:%d:\n\terror: "format"\n", \
__FILE__, __LINE__, ##__VA_ARGS__)
Expand All @@ -52,7 +53,7 @@ typedef struct rd_injection {
mach_vm_address_t target_address;
} rd_injection_t;

static mach_vm_size_t _get_image_size(void *image, mach_vm_size_t image_slide);
static mach_vm_size_t _image_size(void *image, mach_vm_size_t image_slide, mach_vm_address_t *data_segment_offset);
static kern_return_t _remap_image(void *image, mach_vm_size_t image_slide, mach_vm_address_t *new_location);
static kern_return_t _insert_jmp(void* where, void* to);
static kern_return_t _patch_memory(void *address, mach_msg_type_number_t count, uint8_t *new_bytes);
Expand Down Expand Up @@ -165,23 +166,42 @@ int rd_duplicate_function(void *function, void **duplicate)

static kern_return_t _remap_image(void *image, mach_vm_size_t image_slide, mach_vm_address_t *new_location)
{
mach_vm_size_t image_size = _get_image_size(image, image_slide);
assert(image);
assert(new_location);

mach_vm_address_t data_segment_offset = 0;
mach_vm_size_t image_size = _image_size(image, image_slide, &data_segment_offset);

kern_return_t err = KERN_FAILURE;
/**
* For some reason we need more free space when for 64-bit code.
* Looks like _remap_image() remaps a "__DATA" section BEFORE target — SO BUG.
/*
* On x86_64 for some images __DATA segment is mapped far from other segments.
* To handle it we need to allocate a memory zone that has enough capacity
* for both __DATA's island and the rest of the image.
*
* Since __DATA is mapped even *before* the actual image, we are going to have
* a "safety zone" (the space between __TEXT and __DATA segments) by skipping
* the first data_segment_offset bytes. Thus, __DATA will be remapped into a
* valid (i.e. owned by user) memory space.
* Here's a map of whole memory zone:
* image_size bytes
* ---------------------
* __DATA island / \
* / \ / \
* >--------*-----*-----------------------*------(...)------*-------*--->
* | | |
* | | |
* __DATA __TEXT __LINKEDIT
* | |
* | |
* *new_location *new_location + data_segment_offset
*
* FIX OR DIE.
* NOTE: vmaddr(__TEXT) - vmaddr(__DATA) = const, so we can't remap __DATA
* into any place we want.
*/
*new_location = 0;
#if defined(__x86_64__)
err = mach_vm_allocate(mach_task_self(), new_location, image_size*3, VM_FLAGS_ANYWHERE);
mach_vm_size_t lefover = image_size * 2;
*new_location += lefover;
mach_vm_deallocate(mach_task_self(), (*new_location - lefover), lefover);
#else
err = mach_vm_allocate(mach_task_self(), new_location, image_size, VM_FLAGS_ANYWHERE);
#endif
err = mach_vm_allocate(mach_task_self(), new_location,
(image_size + data_segment_offset), VM_FLAGS_ANYWHERE);
*new_location += data_segment_offset;

if (KERN_SUCCESS != err) {
RDErrorLog("Failed to allocate a memory region for the function copy - mach_vm_allocate() returned 0x%x\n", err);
Expand Down Expand Up @@ -212,7 +232,7 @@ static kern_return_t _remap_image(void *image, mach_vm_size_t image_slide, mach_

err = mach_vm_remap(
/* Target information */
mach_task_self(), &seg_target,vmsize, 0x0,
mach_task_self(), &seg_target, vmsize, 0x0,
/* Flags */
(VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE),
/* Source information */
Expand All @@ -233,29 +253,44 @@ static kern_return_t _remap_image(void *image, mach_vm_size_t image_slide, mach_
}


static mach_vm_size_t _get_image_size(void *image, mach_vm_size_t image_slide)
static mach_vm_size_t _image_size(void *image, mach_vm_size_t image_slide, mach_vm_address_t *data_segment_offset)
{
assert(image);

const mach_header_t *header = (mach_header_t *)image;
struct load_command *cmd = (struct load_command *)(header + 1);

mach_vm_address_t image_addr = (mach_vm_address_t)image - image_slide;
mach_vm_address_t image_end = image_addr;
mach_vm_address_t data_vmaddr = 0, text_vmaddr = 0;

for (uint32_t i = 0; (i < header->ncmds); i++, cmd = (void *)cmd + cmd->cmdsize) {
if (cmd->cmd != LC_SEGMENT_ARCH_INDEPENDENT) continue;

segment_command_t *segment = (segment_command_t *)cmd;
if (0 == strcmp("__DATA", segment->segname)) {
data_vmaddr = segment->vmaddr;
} else if (0 == strcmp("__TEXT", segment->segname)) {
text_vmaddr = segment->vmaddr;
}

if ((segment->vmaddr + segment->vmsize) > image_end) {
image_end = segment->vmaddr + segment->vmsize;
}
}

if (data_vmaddr < text_vmaddr) {
*data_segment_offset = text_vmaddr - data_vmaddr;
}

return (image_end - image_addr);
}


static kern_return_t _insert_jmp(void* where, void* to)
{
assert(where);
assert(to);
/**
* We are going to use an absolute JMP instruction for x86_64
* and a relative one for i386.
Expand Down Expand Up @@ -287,9 +322,9 @@ static kern_return_t _insert_jmp(void* where, void* to)

static kern_return_t _patch_memory(void *address, mach_msg_type_number_t count, uint8_t *new_bytes)
{
if (count == 0) {
return KERN_SUCCESS;
}
assert(address);
assert(count > 0);
assert(new_bytes);

kern_return_t kr = 0;
kr = mach_vm_protect(mach_task_self(), (mach_vm_address_t)address, (mach_vm_size_t)count, FALSE, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE | VM_PROT_COPY);
Expand All @@ -314,6 +349,8 @@ static kern_return_t _patch_memory(void *address, mach_msg_type_number_t count,

static void* _function_ptr_from_name(const char *function_name, const char *suggested_image_name)
{
assert(function_name);

for (uint32_t i = 0; i < _dyld_image_count(); i++) {
void *header = (void *)_dyld_get_image_header(i);
uintptr_t vmaddr_slide = _dyld_get_image_vmaddr_slide(i);
Expand All @@ -339,6 +376,8 @@ static void* _function_ptr_from_name(const char *function_name, const char *sugg

static void* _function_ptr_within_image(const char *function_name, void *macho_image_header, uintptr_t vmaddr_slide)
{
assert(function_name);
assert(macho_image_header);
/**
* Try the system NSLookup API to find out the function's pointer withing the specifed header.
*/
Expand Down Expand Up @@ -395,10 +434,8 @@ static void* _function_ptr_within_image(const char *function_name, void *macho_i
for (uint32_t i = 0; i < symtab->nsyms; i++, sym++) {
if (!sym->n_value) continue;
const char *symbol_name = (const char *)strings + sym->n_un.n_strx;
if (0 == strcmp(symbol_name, function_name) ||
/* ignore leading "_" char */
0 == strcmp(symbol_name+1, function_name))
{
/* Ignore the leading underscore ("_") character for a real symbol name */
if (0 == strcmp(symbol_name+1, function_name)) {
return (void *)(sym->n_value + vmaddr_slide);
}
}
Expand Down
9 changes: 8 additions & 1 deletion rd_route.h
Expand Up @@ -7,6 +7,10 @@
#ifndef RD_ROUTE
#define RD_ROUTE

#ifdef __cplusplus
extern "C" {
#endif

/**
* Override `function` to jump directly into `replacement` location. Caller can later
* access an original function's implementation via `original_ptr` (if passed).
Expand Down Expand Up @@ -45,6 +49,9 @@
* @return KERN_SUCCESS if succeeded, or other value if failed
*/
int rd_duplicate_function(void *function, void **duplicate);
#endif

#ifdef __cplusplus
}
#endif

#endif /* RD_ROUTE */

0 comments on commit 259054c

Please sign in to comment.