Skip to content

Instantly share code, notes, and snippets.

Created November 9, 2017 17:20
Show Gist options
  • Save anonymous/03e7af851824306d406b8f0c316c9e3a to your computer and use it in GitHub Desktop.
Save anonymous/03e7af851824306d406b8f0c316c9e3a to your computer and use it in GitHub Desktop.
#!/bin/bash
# Exit the script immediately on error
set -e
# We'll work in /tmp
cd /tmp
# Clone mach_override unless we already have it
if [ ! -d ~/mach_override ]; then
git -C ~ clone https://github.com/rentzsch/mach_override
git -C ~/mach_override apply - <<"PATCH"
diff --git a/third_party/mach_override/mach_override.c b/third_party/mach_override/mach_override.c
index 85a75e5c2067..ca68c0e90e61 100644
--- a/mach_override.c
+++ b/mach_override.c
@@ -8,6 +8,7 @@
#include "udis86.h"
#endif
+#include <libkern/OSAtomic.h>
#include <mach-o/dyld.h>
#include <mach/mach_init.h>
#include <mach/vm_map.h>
@@ -41,7 +42,7 @@ long kIslandTemplate[] = {
#define kInstructionHi 10
#define kInstructionLo 11
-#elif defined(__i386__)
+#elif defined(__i386__)
#define kOriginalInstructionsSize 16
@@ -61,6 +62,7 @@ char kIslandTemplate[] = {
#define kOriginalInstructionsSize 32
#define kJumpAddress kOriginalInstructionsSize + 6
+#define kMaxJumpOffset (0x7fffffffUL)
char kIslandTemplate[] = {
// kOriginalInstructionsSize nop instructions so that we
@@ -93,6 +95,14 @@ typedef struct {
int allocatedHigh;
} BranchIsland;
+/**************************
+*
+* Statistics
+*
+**************************/
+static volatile int64_t __attribute__((__aligned__((sizeof(int64_t)))))
+ g_mach_override_allocation_attempts = 0;
+
/**************************
*
* Funky Protos
@@ -101,6 +111,10 @@ typedef struct {
#pragma mark -
#pragma mark (Funky Protos)
+u_int64_t mach_override_ptr_allocation_attempts() {
+ return OSAtomicAdd64(0, &g_mach_override_allocation_attempts);
+}
+
mach_error_t
allocateBranchIsland(
BranchIsland **island,
@@ -267,7 +281,13 @@ mach_override_ptr(
#if defined(__i386__) || defined(__x86_64__)
if (!err) {
- uint32_t addressOffset = ((char*)escapeIsland - (char*)originalFunctionPtr - 5);
+ // TODO: On 64-bit, move to opcode FF/4 (jmp 64-bit absolute indirect)
+ // instead of E9 (jmp 32-bit relative to RIP). Then we should update
+ // allocateBranchIsland to simply allocate any page in the address space.
+ // See the 64-bit version of kIslandTemplate array.
+ int64_t addressOffset64 = ((char*)escapeIsland - (char*)originalFunctionPtr - 5);
+ int32_t addressOffset = addressOffset64;
+ assert(addressOffset64 == addressOffset);
addressOffset = OSSwapInt32(addressOffset);
jumpRelativeInstruction |= 0xE900000000000000LL;
@@ -385,13 +405,14 @@ allocateBranchIsland(
void *originalFunctionAddress)
{
assert( island );
-
+
mach_error_t err = err_none;
if( allocateHigh ) {
assert( sizeof( BranchIsland ) <= PAGE_SIZE );
vm_address_t page = 0;
#if defined(__i386__)
+ OSAtomicAdd64(1, &g_mach_override_allocation_attempts);
err = vm_allocate( mach_task_self(), &page, PAGE_SIZE, VM_FLAGS_ANYWHERE );
if( err == err_none )
*island = (BranchIsland*) page;
@@ -401,23 +422,42 @@ allocateBranchIsland(
vm_address_t first = 0xfeffffff;
vm_address_t last = 0xfe000000 + PAGE_SIZE;
#elif defined(__x86_64__)
- // 64-bit ASLR is in bits 13-28
- vm_address_t first = ((uint64_t)originalFunctionAddress & ~( (0xFUL << 28) | (PAGE_SIZE - 1) ) ) | (0x1UL << 31);
- vm_address_t last = (uint64_t)originalFunctionAddress & ~((0x1UL << 32) - 1);
+ // This logic is more complex due to the 32-bit limit of the jump out
+ // of the original function. Once that limitation is removed, we can
+ // use vm_allocate with VM_FLAGS_ANYWHERE as in the i386 code above.
+ const uint64_t kPageMask = ~(PAGE_SIZE - 1);
+ vm_address_t first = (uint64_t)originalFunctionAddress - kMaxJumpOffset;
+ first = (first & kPageMask) + PAGE_SIZE; // Align up to the next page start
+ if (first > (uint64_t)originalFunctionAddress) first = 0;
+ vm_address_t last = (uint64_t)originalFunctionAddress + kMaxJumpOffset;
+ if (last < (uint64_t)originalFunctionAddress) last = ULONG_MAX;
#endif
page = first;
int allocated = 0;
vm_map_t task_self = mach_task_self();
- while( !err && !allocated && page != last ) {
+ while( !err && !allocated && page < last ) {
+ OSAtomicAdd64(1, &g_mach_override_allocation_attempts);
err = vm_allocate( task_self, &page, PAGE_SIZE, 0 );
if( err == err_none )
allocated = 1;
- else if( err == KERN_NO_SPACE ) {
+ else if( err == KERN_NO_SPACE || err == KERN_INVALID_ADDRESS) {
#if defined(__x86_64__)
- page -= PAGE_SIZE;
+ // This memory region is not suitable, skip it:
+ vm_size_t region_size;
+ mach_msg_type_number_t int_count = VM_REGION_BASIC_INFO_COUNT_64;
+ vm_region_basic_info_data_64_t vm_region_info;
+ mach_port_t object_name;
+ // The call will move 'page' to the beginning of the region:
+ err = vm_region_64(task_self, &page, &region_size,
+ VM_REGION_BASIC_INFO_64, (vm_region_info_t)&vm_region_info,
+ &int_count, &object_name);
+ if (err == KERN_SUCCESS)
+ page += region_size;
+ else
+ break;
#else
page += PAGE_SIZE;
#endif
@@ -430,6 +470,7 @@ allocateBranchIsland(
err = KERN_NO_SPACE;
#endif
} else {
+ OSAtomicAdd64(1, &g_mach_override_allocation_attempts);
void *block = malloc( sizeof( BranchIsland ) );
if( block )
*island = block;
@@ -438,7 +479,7 @@ allocateBranchIsland(
}
if( !err )
(**island).allocatedHigh = allocateHigh;
-
+
return err;
}
diff --git a/mach_override.h b/mach_override.h
index ecd319c1cd7a..253f273d16b6 100644
--- a/mach_override.h
+++ b/mach_override.h
@@ -37,6 +37,18 @@ mach_override_ptr(
const void *overrideFunctionAddress,
void **originalFunctionReentryIsland );
+/****************************************************************************************
+ mach_override_ptr makes multiple allocation attempts with vm_allocate or malloc,
+ until a suitable address is found for the branch islands. This method returns the
+ global number of such attempts made by all mach_override_ptr calls so far. This
+ statistic is provided for testing purposes and it can be off, if mach_override_ptr
+ is called by multiple threads.
+
+ @result <- Total number of vm_allocate calls so far.
+
+ ************************************************************************************/
+u_int64_t mach_override_ptr_allocation_attempts();
+
__END_DECLS
/****************************************************************************************
PATCH
fi
git -C ~/mach_override pull
# ============================
# Compile the override library
# ============================
clang -g -dynamiclib -framework Cocoa -x objective-c -o monacoize.dylib -I"$HOME" ~/mach_override/mach_override.c ~/mach_override/libudis86/*.c /dev/stdin <<"SOURCE"
#import <Cocoa/Cocoa.h>
#import <dlfcn.h>
#import <objc/runtime.h>
#import "mach_override/mach_override.h"
// SourceEditor.SourceEditorLineLayer.init(attributedString: __ObjC.NSAttributedString, referencedLine: SourceEditor.SourceEditorLineIdentifier, lineLayoutManager: SourceEditor.LineLayoutManager, fontSmoothingAttributes: SourceEditor.SourceEditorFontSmoothingAttributes, textRenderingColorSpace: Swift.Optional<__ObjC.CGColorSpace>, images: Swift.Array<(image: __ObjC.CGImage, columnRange: Swift.Range<Swift.Int>)>) -> SourceEditor.SourceEditorLineLayer
#define ARGS void *a, void *b, void *c, void *d, unsigned *fontSmoothingAttributes, void *e, void *f
void *(*originalLineLayerInit)(ARGS);
void *replacementLineLayerInit(ARGS) {
fprintf(stderr, "Initing line layer\n");
*fontSmoothingAttributes = 0;
return originalLineLayerInit(a, b, c, d, fontSmoothingAttributes, e, f);
}
CTTypesetterRef (*originalCTTypesetterCreateWithAttributedString)(CFAttributedStringRef string);
CTTypesetterRef replacementCTTypesetterCreateWithAttributedString(CFAttributedStringRef string) {
CFMutableAttributedStringRef mutable = CFAttributedStringCreateMutableCopy(NULL, 0, string);
CFRange range = CFRangeMake(0, CFAttributedStringGetLength(mutable));
CFAttributedStringSetAttribute(mutable, range, kCTKernAttributeName, (void *)@0.75);
CTTypesetterRef result = originalCTTypesetterCreateWithAttributedString(mutable);
CFRelease(mutable);
return result;
}
void LoadMonacoize(void *lineLayerInit) {
fprintf(stderr, "lineLayerInit = %p\n", lineLayerInit);
mach_error_t result = mach_override_ptr(lineLayerInit, replacementLineLayerInit, (void **)&originalLineLayerInit);
if(result != 0) {
fprintf(stderr, "ERROR %d TRYING TO OVERRIDE LayerInit\n", result);
abort();
}
result = mach_override_ptr(CTTypesetterCreateWithAttributedString, replacementCTTypesetterCreateWithAttributedString, (void **)&originalCTTypesetterCreateWithAttributedString);
if(result != 0) {
fprintf(stderr, "ERROR %d TRYING TO OVERRIDE CTTypesetter\n", result);
fprintf(stderr, "%d\n", err_cannot_override);
abort();
}
}
SOURCE
# =======================
# End of override library
# =======================
# =================================================
# Run Xcode in lldb and invoke the override library
# =================================================
lldb /Applications/Xcode.app -s <(cat <<"COMMANDS"
b SourceEditor.SourceEditorLayoutManager.makeLineLayerForLine
break command add
expr (void *)dlopen("/tmp/monacoize.dylib", 0x8)
expr void *$fptr = (void *)(void (*)(void))_T012SourceEditor0aB9LineLayerCACSo18NSAttributedStringC010attributedF0_AA0abC10IdentifierV010referencedC0AA0C13LayoutManager_p04linejK0AA0aB23FontSmoothingAttributesV04fontnO0So12CGColorSpaceCSg018textRenderingColorR0SaySo7CGImageC5image_s5RangeVySiG06columnX0tG6imagestcfc
expr -i0 -u0 -- (void)LoadMonacoize($fptr)
br del 1
cont
DONE
run
#cont
#exit
COMMANDS
)
# ======================
# End of lldb invocation
# ======================
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment