<commit_before>// Copyright © 2014 <NAME> <EMAIL>
// 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()

#import "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
#else
    #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"

#define RDErrorLog(format, ...) fprintf(stderr, "%s:%d:\n\terror: "format"\n", \
	__FILE__, __LINE__, ##__VA_ARGS__)

#if defined(__x86_64__)
	typedef struct mach_header_64     mach_header_t;
	typedef struct segment_command_64 segment_command_t;
	#define LC_SEGMENT_ARCH_INDEPENDENT   LC_SEGMENT_64
	typedef struct nlist_64 nlist_t;
#else
	typedef struct mach_header        mach_header_t;
	typedef struct segment_command    segment_command_t;
	#define LC_SEGMENT_ARCH_INDEPENDENT   LC_SEGMENT
	typedef struct nlist nlist_t;
#endif

typedef struct rd_injection {
	mach_vm_address_t injected_mach_header;
	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 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);
static void*          _function_ptr_from_name(const char *function_name, const char *suggested_image_name);
static void*          _function_ptr_within_image(const char *function_name, void *macho_image_header, uintptr_t vm_image_slide);


int rd_route(void *function, void *replacement, void **original_ptr)
{
	if (!function || !replacement) {
		return KERN_INVALID_ARGUMENT;
	}
	if (function == replacement) {
		return KERN_INVALID_ADDRESS;
	}

	int ret = KERN_SUCCESS;
	if (original_ptr) {
		ret = rd_duplicate_function(function, original_ptr);
	}

	if (ret == KERN_SUCCESS) {
		ret = _insert_jmp(function, replacement);
	}

	return (ret);
}


int rd_route_byname(const char *function_name, const char *suggested_image_name, void *replacement, void **original)
{
	/**
	 * These cases are actually handled by rd_route() function itself, but we don't want to dig over
	 * all loaded images just to do nothing at the end.
	 */
	if (!function_name|| !replacement) {
		return KERN_INVALID_ARGUMENT;
	}
	void *function = _function_ptr_from_name(function_name, suggested_image_name);

	return rd_route(function, replacement, original);
}


int rd_duplicate_function(void *function, void **duplicate)
{
	if (!function || !duplicate) {
		return (KERN_INVALID_ARGUMENT);
	}

	void *image = NULL;
	mach_vm_size_t image_slide = 0;

	/* Obtain the macho header image which contains the function */
	Dl_info image_info = {0};
	if (dladdr(function, &image_info)) {
		image = image_info.dli_fbase;
	}
	if (!image) {
		RDErrorLog("Could not found a loaded mach-o image containing the given function.");
		return KERN_FAILURE;
	}

	for (uint32_t i = 0; i < _dyld_image_count(); i++) {
		if (image == _dyld_get_image_header(i)) {
			image_slide = _dyld_get_image_vmaddr_slide(i);
			break;
		}
	}

	static rd_injection_t *injection_history = NULL;
	static uint16_t history_size = 0;
	/* Look up the injection history if we already have this image remapped. */
	for (uint16_t i = 0; i < history_size; i++) {
		if (injection_history[i].injected_mach_header == (mach_vm_address_t)image) {
			if (duplicate) {
				*duplicate = (void *)injection_history[i].target_address + (function - image);
			}
			return KERN_SUCCESS;
		}
	}

	/**
	 * Take a note that we have already remapped this mach-o image, so won't do this
	 * again when routing another function from the image.
	 */
	size_t new_size = history_size + 1;
	injection_history = realloc(injection_history, sizeof(*injection_history) * new_size);
	injection_history[history_size].injected_mach_header = (mach_vm_address_t)image;
	injection_history[history_size].target_address = ({
		mach_vm_address_t target = 0;
		kern_return_t err = _remap_image(image, image_slide, &target);
		if (KERN_SUCCESS != err) {
			RDErrorLog("Failed to remap segements of the image. See error messages above.");
			return (err);
		}
		/* Backup an original function implementation if needed */
		if (duplicate) {
			*duplicate = (void *)(target + (function - image));
		}

		target;
	});

	history_size = new_size;

	return KERN_SUCCESS;
}


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);
	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.
	 *
	 * FIX OR DIE.
	 */
	*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

	if (KERN_SUCCESS != err) {
		RDErrorLog("Failed to allocate a memory region for the function copy - mach_vm_allocate() returned 0x%x\n", err);
		return (err);
	}

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

	/**
	 * Remap each segment of the mach-o image into a new location.
	 * New location is:
	 * -> target_image + segment.offset_in_image;
	 */
	for (uint32_t i = 0; (i < header->ncmds); i++, cmd = (void *)cmd + cmd->cmdsize) {
		if (cmd->cmd != LC_SEGMENT_ARCH_INDEPENDENT) continue;<commit_msg>Increase the safety zone before remapped image<commit_after>// Copyright © 2014 <NAME> <EMAIL>
// 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()

#import "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
#else
    #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"

#define RDErrorLog(format, ...) fprintf(stderr, "%s:%d:\n\terror: "format"\n", \
	__FILE__, __LINE__, ##__VA_ARGS__)

#if defined(__x86_64__)
	typedef struct mach_header_64     mach_header_t;
	typedef struct segment_command_64 segment_command_t;
	#define LC_SEGMENT_ARCH_INDEPENDENT   LC_SEGMENT_64
	typedef struct nlist_64 nlist_t;
#else
	typedef struct mach_header        mach_header_t;
	typedef struct segment_command    segment_command_t;
	#define LC_SEGMENT_ARCH_INDEPENDENT   LC_SEGMENT
	typedef struct nlist nlist_t;
#endif

typedef struct rd_injection {
	mach_vm_address_t injected_mach_header;
	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 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);
static void*          _function_ptr_from_name(const char *function_name, const char *suggested_image_name);
static void*          _function_ptr_within_image(const char *function_name, void *macho_image_header, uintptr_t vm_image_slide);


int rd_route(void *function, void *replacement, void **original_ptr)
{
	if (!function || !replacement) {
		return KERN_INVALID_ARGUMENT;
	}
	if (function == replacement) {
		return KERN_INVALID_ADDRESS;
	}

	int ret = KERN_SUCCESS;
	if (original_ptr) {
		ret = rd_duplicate_function(function, original_ptr);
	}

	if (ret == KERN_SUCCESS) {
		ret = _insert_jmp(function, replacement);
	}

	return (ret);
}


int rd_route_byname(const char *function_name, const char *suggested_image_name, void *replacement, void **original)
{
	/**
	 * These cases are actually handled by rd_route() function itself, but we don't want to dig over
	 * all loaded images just to do nothing at the end.
	 */
	if (!function_name|| !replacement) {
		return KERN_INVALID_ARGUMENT;
	}
	void *function = _function_ptr_from_name(function_name, suggested_image_name);

	return rd_route(function, replacement, original);
}


int rd_duplicate_function(void *function, void **duplicate)
{
	if (!function || !duplicate) {
		return (KERN_INVALID_ARGUMENT);
	}

	void *image = NULL;
	mach_vm_size_t image_slide = 0;

	/* Obtain the macho header image which contains the function */
	Dl_info image_info = {0};
	if (dladdr(function, &image_info)) {
		image = image_info.dli_fbase;
	}
	if (!image) {
		RDErrorLog("Could not found a loaded mach-o image containing the given function.");
		return KERN_FAILURE;
	}

	for (uint32_t i = 0; i < _dyld_image_count(); i++) {
		if (image == _dyld_get_image_header(i)) {
			image_slide = _dyld_get_image_vmaddr_slide(i);
			break;
		}
	}

	static rd_injection_t *injection_history = NULL;
	static uint16_t history_size = 0;
	/* Look up the injection history if we already have this image remapped. */
	for (uint16_t i = 0; i < history_size; i++) {
		if (injection_history[i].injected_mach_header == (mach_vm_address_t)image) {
			if (duplicate) {
				*duplicate = (void *)injection_history[i].target_address + (function - image);
			}
			return KERN_SUCCESS;
		}
	}

	/**
	 * Take a note that we have already remapped this mach-o image, so won't do this
	 * again when routing another function from the image.
	 */
	size_t new_size = history_size + 1;
	injection_history = realloc(injection_history, sizeof(*injection_history) * new_size);
	injection_history[history_size].injected_mach_header = (mach_vm_address_t)image;
	injection_history[history_size].target_address = ({
		mach_vm_address_t target = 0;
		kern_return_t err = _remap_image(image, image_slide, &target);
		if (KERN_SUCCESS != err) {
			RDErrorLog("Failed to remap segements of the image. See error messages above.");
			return (err);
		}
		/* Backup an original function implementation if needed */
		if (duplicate) {
			*duplicate = (void *)(target + (function - image));
		}

		target;
	});

	history_size = new_size;

	return KERN_SUCCESS;
}


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);
	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.
	 *
	 * FIX OR DIE.
	 */
	*new_location = 0;
#if defined(__x86_64__)
	err = mach_vm_allocate(mach_task_self(), new_location, image_size*4, VM_FLAGS_ANYWHERE);
	mach_vm_size_t lefover = image_size * 3;
	*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

	if (KERN_SUCCESS != err) {
		RDErrorLog("Failed to allocate a memory region for the function copy - mach_vm_allocate() returned 0x%x\n", err);
		return (err);
	}

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

	/**
	 * Remap each segment of the mach-o image into a new location.
	 * New location is:
	 * -> target_image + segment.offset_in_image;
	 */
	for (uint32_t i = 0; (i < header->ncmds); i++, cmd = (void *)cmd + cmd->cmdsize) {
		if (cmd->cmd != LC_SEGMENT_ARCH_INDEPENDENT) continue;
{'fraction_non_alphanumeric': 0.1042774739680875, 'fraction_numerical': 0.008090493669937823, 'mean_word_length': 4.393939393939394, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1109809', 'n_tokens_mistral': 5010, 'n_tokens_neox': 4656, 'n_words': 1211}