Skip to content

Commit

Permalink
integrate frida-based injector into yabai for arm64; add arm64 makefile
Browse files Browse the repository at this point in the history
  • Loading branch information
xorpse committed Jun 7, 2021
1 parent 66097ed commit 867c57a
Show file tree
Hide file tree
Showing 6 changed files with 327 additions and 2 deletions.
15 changes: 15 additions & 0 deletions README-arm64.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# ARM64 specifics

Build using the `makefile.arm64`:

```
$ make -f makefile.arm64
$ make -f makefile.arm64 sign
```

# Loading script additions

```
$ sudo ./bin/yabai --install-sa
$ sudo ./bin/yabai --load-sa
```
4 changes: 2 additions & 2 deletions makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ install: clean-build $(BINS)

$(OSAX_SRC): $(OSAX_PATH)/loader.m $(OSAX_PATH)/payload.m $(OSAX_PATH)/mach_bootstrap.c
clang $(OSAX_PATH)/loader.m -shared -O2 -mmacosx-version-min=10.13 -o $(OSAX_PATH)/loader -framework Foundation
clang $(OSAX_PATH)/payload.m -shared -fPIC -O2 -mmacosx-version-min=10.13 -o $(OSAX_PATH)/payload -arch arm64e -framework Foundation -framework Carbon
clang $(OSAX_PATH)/payload.m -shared -fPIC -O2 -mmacosx-version-min=10.13 -o $(OSAX_PATH)/payload -framework Foundation -framework Carbon
clang $(OSAX_PATH)/mach_bootstrap.c -shared -fPIC -O2 -mmacosx-version-min=10.13 -o $(OSAX_PATH)/mach_bootstrap -framework Carbon -lpthread
xxd -i -a $(OSAX_PATH)/loader $(OSAX_PATH)/sa_loader.c
xxd -i -a $(OSAX_PATH)/payload $(OSAX_PATH)/sa_payload.c
xxd -i -a $(OSAX_PATH)/mach_bootstrap $(OSAX_PATH)/sa_mach_bootstrap.c
rm -f $(OSAX_PATH)/loader
#rm -f $(OSAX_PATH)/payload
rm -f $(OSAX_PATH)/payload
rm -f $(OSAX_PATH)/mach_bootstrap

man:
Expand Down
68 changes: 68 additions & 0 deletions makefile.arm64
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
FRAMEWORK_PATH = -F/System/Library/PrivateFrameworks
FRAMEWORK = -framework Carbon -framework Cocoa -framework CoreServices -framework SkyLight -framework ScriptingBridge
BUILD_FLAGS = -std=c99 -Wall -fPIC -g -O0 -fvisibility=hidden -mmacosx-version-min=10.13 -L./frida -lfrida-core -lbsm -lm -ldl -lpthread -lresolv -Wl,-framework,Foundation,-framework,AppKit -pthread -isysroot "$(shell xcrun --sdk macosx --show-sdk-path)" -Wl,-dead_strip
BUILD_PATH = ./bin
DOC_PATH = ./doc
SCRIPT_PATH = ./scripts
ASSET_PATH = ./assets
SMP_PATH = ./examples
ARCH_PATH = ./archive
OSAX_SRC = ./src/osax/arm64/injector.o ./src/osax/sa_payload.c
YABAI_SRC = ./src/manifest.m $(OSAX_SRC)
OSAX_PATH = ./src/osax
BINS = $(BUILD_PATH)/yabai

# ARM64 injector
FRIDA_MAJOR = 14
FRIDA_MINOR = 2
FRIDA_PATCH = 18
FRIDA_DEVKIT = https://github.com/frida/frida/releases/download/${FRIDA_MAJOR}.${FRIDA_MINOR}.${FRIDA_PATCH}/frida-core-devkit-${FRIDA_MAJOR}.${FRIDA_MINOR}.${FRIDA_PATCH}-macos-arm64.tar.xz
FRIDA_DEVKIT_FILES = ./frida/frida-core.h ./frida/libfrida-core.a

.PHONY: all clean install sign archive man

all: clean-build $(BINS)

install: BUILD_FLAGS=-std=c99 -Wall -DNDEBUG -O2 -fvisibility=hidden -mmacosx-version-min=10.13
install: clean-build $(BINS)

$(FRIDA_DEVKIT_FILES):
mkdir -p ./frida
curl -L -o ./frida/devkit.tar.xz ${FRIDA_DEVKIT}
tar xJf ./frida/devkit.tar.xz -C ./frida
rm ./frida/frida-core-example.c ./frida/devkit.tar.xz

$(OSAX_SRC): $(FRIDA_DEVKIT_FILES) $(OSAX_PATH)/arm64/injector.c $(OSAX_PATH)/payload.m
clang -c $(OSAX_PATH)/arm64/injector.c -o $(OSAX_PATH)/arm64/injector.o -I./frida -isysroot "$(shell xcrun --sdk macosx --show-sdk-path)"
clang $(OSAX_PATH)/payload.m -shared -fPIC -O2 -mmacosx-version-min=10.13 -o $(OSAX_PATH)/payload -arch arm64e -framework Foundation -framework Carbon
xxd -i -a $(OSAX_PATH)/payload $(OSAX_PATH)/sa_payload.c
rm -f $(OSAX_PATH)/payload

man:
asciidoctor -b manpage $(DOC_PATH)/yabai.asciidoc -o $(DOC_PATH)/yabai.1

icon:
python $(SCRIPT_PATH)/seticon.py $(ASSET_PATH)/icon/2x/[email protected] $(BUILD_PATH)/yabai

archive: man install sign icon
rm -rf $(ARCH_PATH)
mkdir -p $(ARCH_PATH)
cp -r $(BUILD_PATH) $(ARCH_PATH)/
cp -r $(DOC_PATH) $(ARCH_PATH)/
cp -r $(SMP_PATH) $(ARCH_PATH)/
tar -cvzf $(BUILD_PATH)/$(shell $(BUILD_PATH)/yabai --version).tar.gz $(ARCH_PATH)
rm -rf $(ARCH_PATH)

sign:
codesign -fs "yabai-cert" $(BUILD_PATH)/yabai

clean-build:
rm -rf $(BUILD_PATH)

clean: clean-build
rm -f $(OSAX_SRC)
rm -f $(FRIDA_DEVKIT_FILES)

$(BUILD_PATH)/yabai: $(YABAI_SRC)
mkdir -p $(BUILD_PATH)
clang $^ $(BUILD_FLAGS) $(FRAMEWORK_PATH) $(FRAMEWORK) -o $@
209 changes: 209 additions & 0 deletions src/osax/arm64/injector.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
#include <stdlib.h>
#include <string.h>

#include "frida-core.h"

#define YABAI_PAYLOAD_PATH "/Library/ScriptingAdditions/yabai.osax/Contents/Resources/payload.bundle/Contents/MacOS/payload"

static void on_message(FridaScript *script, const gchar *message, GBytes *data, gpointer user_data);

static GMainLoop *loop = NULL;
static int return_val = 1;

const char *script_payload =
"function loadYabaiSA(sa_path) {\n"
" var RTLD_NOW = 0x02;\n"
" var _dlopen = new NativeFunction(Module.findExportByName(null, 'dlopen'), 'pointer', ['pointer', 'int']);\n\n"
" var path = Memory.allocUtf8String(sa_path);\n"
" var handle = _dlopen(path, RTLD_NOW);\n"
" if (handle.isNull()) {\n"
" console.error('[e] failed to inject scripting addition payload \"" YABAI_PAYLOAD_PATH "\" into Dock');\n"
" send('failure');\n"
" return;\n"
" }\n"
" console.log('[*] injected scripting addition payload into Dock');\n"
" send('success');\n"
"}\n\n"
"var sa_path = '" YABAI_PAYLOAD_PATH "';\n"
"loadYabaiSA(sa_path);";


int inject_yabai() {
gint num_devices, i;
GPid pid;
GError *error = NULL;

FridaDevice *device;
FridaDeviceManager *manager;
FridaDeviceList * devices;
FridaDevice *local_device;
FridaProcess *dock;
FridaScript *script;
FridaScriptOptions *options;
FridaSession *session;

frida_init();

loop = g_main_loop_new(NULL, TRUE);
manager = frida_device_manager_new();

devices = frida_device_manager_enumerate_devices_sync(manager, NULL, &error);
if (error != NULL) {
g_printerr("[e] failed to enumerate devices: %s\n", error->message);
g_error_free(error);
frida_unref(devices);
goto cleanup;
}

local_device = NULL;
num_devices = frida_device_list_size(devices);
for (i = 0; i != num_devices; i++) {
device = frida_device_list_get(devices, i);

if (frida_device_get_dtype(device) == FRIDA_DEVICE_TYPE_LOCAL) {
local_device = g_object_ref(device);
g_object_unref(device);
break;
}

g_object_unref(device);
}

if (local_device == NULL) {
g_printerr("[e] failed find a 'local' device\n");
frida_unref(devices);

return_val = 0;
goto cleanup;
}

frida_unref(devices);
devices = NULL;

dock = frida_device_get_process_by_name_sync(
local_device,
"Dock",
-1,
NULL,
&error
);

if (error != NULL) {
g_printerr("[e] failed to get Dock process information: %s\n", error->message);
g_error_free(error);
frida_unref(dock);

return_val = 0;
goto cleanup;
}

pid = frida_process_get_pid(dock);
frida_unref(dock);

session = frida_device_attach_sync(local_device, pid, FRIDA_REALM_NATIVE, NULL, &error);

if (error != NULL) {
g_printerr("[e] failed to establish session: %s\n", error->message);
g_error_free(error);
frida_unref(session);

return_val = 0;
goto cleanup;
}


if (frida_session_is_detached(session)) {
g_printerr("[e] session detached prematurely\n");
frida_unref(session);

return_val = 0;
goto cleanup;
}

g_print("[*] attached to Dock (pid = %i)\n", pid);

options = frida_script_options_new();
frida_script_options_set_name(options, "inject-yabai-sa");
frida_script_options_set_runtime(options, FRIDA_SCRIPT_RUNTIME_QJS);

script = frida_session_create_script_sync(session, script_payload, options, NULL, &error);
if (error != NULL) {
g_print("[e] failed to create script to load into Dock: %s\n", error->message);
g_error_free(error);

frida_unref(script);
frida_session_detach_sync(session, NULL, NULL);
frida_unref(session);

return_val = 0;
goto cleanup;
}

g_clear_object(&options);
g_signal_connect(script, "message", G_CALLBACK(on_message), NULL);

frida_script_load_sync(script, NULL, &error);

if (error != NULL) {
g_print("[e] failed to load script into Dock: %s\n", error->message);
g_error_free(error);

frida_unref(script);
frida_session_detach_sync(session, NULL, NULL);
frida_unref(session);

return_val = 0;
goto cleanup;
}

if (g_main_loop_is_running(loop)) {
g_main_loop_run(loop);
}

frida_script_unload_sync(script, NULL, NULL);
frida_unref(script);

frida_session_detach_sync(session, NULL, NULL);
frida_unref (session);

g_print ("[*] detached from Dock\n");

cleanup:
frida_unref(local_device);

frida_device_manager_close_sync(manager, NULL, NULL);
frida_unref(manager);

return return_val;
}

static void on_message(FridaScript *script, const gchar *message, GBytes * data, gpointer user_data) {
JsonParser *parser;
JsonObject *root;
const gchar *type;

parser = json_parser_new();
json_parser_load_from_data(parser, message, -1, NULL);
root = json_node_get_object(json_parser_get_root(parser));

type = json_object_get_string_member(root, "type");
if (strcmp(type, "log") == 0) {
const gchar * log_message;

log_message = json_object_get_string_member(root, "payload");
g_print("%s\n", log_message);
} else if (strcmp(type, "send") == 0) {
const gchar * send_message;

send_message = json_object_get_string_member(root, "payload");

if (strcmp(send_message, "success") == 0 || strcmp(send_message, "failure") == 0) {
if (strcmp(send_message, "failure") == 0) {
return_val = 0;
}
g_main_loop_quit(loop);
}
}

g_object_unref(parser);
}
3 changes: 3 additions & 0 deletions src/osax/arm64/injector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once

extern int inject_yabai(void);
Loading

0 comments on commit 867c57a

Please sign in to comment.