rodionovd / rd_route

Function hooking for macOS
MIT License
184 stars 26 forks source link

Support for arm64 #7

Open trueToastedCode opened 1 year ago

trueToastedCode commented 1 year ago

Apple doesn't make x86_64 machines anymore, it would be nice, if we could update this code for Apple Silicon. Thx!

rodionovd commented 1 year ago

Hey there, thanks for the comment. I'm not doing low-level system programming anymore so it's not that interesting for me to work on arm64 targets support. I'd be happy to merge a PR tho!

trueToastedCode commented 1 year ago

Hey there, thanks for the comment. I'm not doing low-level system programming anymore so it's not that interesting for me to work on arm64 targets support. I'd be happy to merge a PR tho!

Mhh... I am currently trying to make rd_route function work... rd_duplicate_function already returns KERN_SUCCESS (after adding some defined(aarch64)). However _insert_jmp fails when calling _patch_memory.

I am currently using it with this code:

mach_msg_type_number_t size_of_jump = (sizeof(int) + 1); ...

int offset = (int)(to - where - size_of_jump);
opcodes[0] = 0x14; // unconditional branch with relative offset
*((int*)&opcodes[1]) = offset;
err = _patch_memory((void *)where, size_of_jump, opcodes);

Gives me this error:

../rd_route.c:343:
    error: mach_vm_protect() failed with error: 0x2
Assertion failed: (0 == strcmp(strerror(err), my_strerror(err))), function test_rd_route, file test_route.c, line 94.
Test clang_arm64: FAIL
rodionovd commented 1 year ago

I strongly suspect that making arbitrary pages both writable and executable is not allowed for arm64. Could be even a macOS restriction nowadays.

While it's not clear from the error message which of two mach_vm_protect() invocation fails, I've came across this answer that suggest making a page writable-only first, writing data to it, and making it executable-only afterwards. Worth a shot 🤞

trueToastedCode commented 1 year ago

I strongly suspect that making arbitrary pages both writable and executable is not allowed for arm64. Could be even a macOS restriction nowadays.

While it's not clear from the error message which of two mach_vm_protect() invocation fails, I've came across this answer that suggest making a page writable-only first, writing data to it, and making it executable afterwards. Worth a shot 🤞

Thanks 👍 This was indeed necessary to make the mach_vm_protect work. But now the code still fails without any specific error. Probably my branch instruction doesn't work...

rodionovd commented 1 year ago

Glad it helped. Unfortunately I have zero knowledge of arm64 assembly so can't help with the jump opcodes here...

trueToastedCode commented 1 year ago

Glad it helped. Unfortunately I have zero knowledge of arm64 assembly so can't help with the jump opcodes here...

The old branch instruction was indeed wrong... gave me illegal hardware instruction in console.

This is the "jump" code used on ChickenHook...

// aarch64
jump.resize(TRAMPOLINE_SIZE); // = 16

jump[0] = 0x49;
jump[1] = 0x00;
jump[2] = 0x00;
jump[3] = 0x58;
jump[4] = 0x20;
jump[5] = 0x01;
jump[6] = 0x1f;
jump[7] = 0xd6;

//int insOff = TRAMPOLINE_SIZE;
uint64_t absoluteAddr = (uint64_t) dest;

// log says relative, guess they meant absolute
log("Calculate relative jump addr <%d> - src <%p> dest <%p>", absoluteAddr, src,
    dest);

memcpy(&jump[8], &absoluteAddr, 8);

translating this...

mach_msg_type_number_t size_of_jump = (sizeof(uintptr_t) + 8); // = 16 ...

opcodes[0] = 0x49;
opcodes[1] = 0x00;
opcodes[2] = 0x00;
opcodes[3] = 0x58;
opcodes[4] = 0x20;
opcodes[5] = 0x01;
opcodes[6] = 0x1f;
opcodes[7] = 0xd6;
// 49 00 00 58    ldr x9, #8
// 20 01 1F D6    br  x9

// uint64_t absoluteAddr = (uint64_t)to;
// memcpy(&opcodes[8], &absoluteAddr, 8);
// -> same thing as
*((uintptr_t*)&opcodes[8]) = (uintptr_t)to;
// write address to array, starting at index 8

Since this is tested code from another repository, it should work. Tho code still faisl, but I do have crash reports from running the build file...

lldb

Process 85050 launched: '/Users/lennard/Desktop/rd_route/tests/build/clang_arm64' (arm64)
Process 85050 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
    frame #0: 0x0000000000000000
error: memory read failed for 0x0

console https://pastebin.com/FYr2a1vS

trueToastedCode commented 1 year ago

Glad it helped. Unfortunately I have zero knowledge of arm64 assembly so can't help with the jump opcodes here...

First partial success 🎉

Doing the usage example:

static char* my_strerror(int err)
{
    return "It's OK";
}

int main(int argc, char const *argv[])
{
    int err = 2;
    rd_route(strerror, my_strerror, NULL);
    printf("%s\n", strerror(2));
    return 0;
}

does actually print It's OK.

rodionovd commented 1 year ago

Yay! So what was the issue with the jump instruction?

trueToastedCode commented 1 year ago

Yay! So what was the issue with the jump instruction?

Nothing wrong with these opcodes now... think it's rd_duplicate_function that doesn't work properly.

T-thanha commented 3 months ago

Yay! So what was the issue with the jump instruction?

Nothing wrong with these opcodes now... think it's rd_duplicate_function that doesn't work properly.

Have you been able to fix the rd_duplicate_function yet? If so, could you let me know?

rodionovd commented 3 months ago

@T-thanha I think you might be interested in https://github.com/trueToastedCode/rd_route which contains a bunch of ARM-specific fixes. Not sure how functional this whole fork is tho

T-thanha commented 3 months ago

@T-thanha I think you might be interested in https://github.com/trueToastedCode/rd_route which contains a bunch of ARM-specific fixes. Not sure how functional this whole fork is tho

Thank you for the information! I’ll check out the link and see if it helps.

trueToastedCode commented 3 months ago

@T-thanha I think you might be interested in https://github.com/trueToastedCode/rd_route which contains a bunch of ARM-specific fixes. Not sure how functional this whole fork is tho

Thank you for the information! I’ll check out the link and see if it helps.

test fail, but I never had issues using rd_duplicate_function in real world applications.

however https://github.com/jmpews/Dobby is the better choice overall.

I use commit 0932d69 (newer wouldn't compile for me but maybe that has a changed) and then you can use the dylib, assuming you have a "HelloWorld.m" and "HelloWorld.h"

[...]

- (void)patch {
  // whatever ...
}

+ (void)load {
  HelloWorld *helloWorld = [[HelloWorld alloc] init];
  [helloWorld  patch];
}

[...]
# Compiler and linker settings
CC = clang
CFLAGS = -fobjc-arc -Wall
LDFLAGS = -dynamiclib -framework Foundation -L../Dobby/build/macos/universal -ldobby -rpath "@executable_path"

# Source files
SRC =  HelloWorld.c
HEADER = ../Dobby/include/dobby.h HelloWorld.h

# Output dylib
OUT = libHelloWorld.dylib

# Architectures to build (arm64 and x86_64 for a universal binary)
ARCHS = -arch arm64 -arch x86_64

all: $(OUT)

$(OUT): $(SRC) $(HEADER)
    $(CC) $(CFLAGS) $(ARCHS) $(LDFLAGS) -o $(OUT) $(SRC)

clean:
    rm -f $(OUT)

That's how I do my patches currently.