Skip to content

Commit

Permalink
Feat: patch dylib for game dlopen calls
Browse files Browse the repository at this point in the history
Now that game can directly load macOS's JNA native library,
libjnidispatch is removed.
  • Loading branch information
khanhduytran0 committed Jun 9, 2024
1 parent 77fa8b1 commit ec04885
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 2 deletions.
1 change: 1 addition & 0 deletions Natives/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ target_link_libraries(PojavAccountJNI)
# PojavLauncher
add_executable(PojavLauncher
dyld_bypass_validation.m
dyld_patch_platform.m
main.m
main_hook.m
JavaLauncher.m
Expand Down
1 change: 0 additions & 1 deletion Natives/JavaLauncher.m
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,6 @@ int launchJVM(NSString *username, id launchTarget, int width, int height, int mi
margv[++margc] = "-Xms128M";
margv[++margc] = [NSString stringWithFormat:@"-Xmx%dM", allocmem].UTF8String;
margv[++margc] = [NSString stringWithFormat:@"-Djava.library.path=%@/Frameworks", NSBundle.mainBundle.bundlePath].UTF8String;
margv[++margc] = [NSString stringWithFormat:@"-Djna.boot.library.path=%@/Frameworks", NSBundle.mainBundle.bundlePath].UTF8String;
margv[++margc] = [NSString stringWithFormat:@"-Duser.dir=%@", gameDir].UTF8String;
margv[++margc] = [NSString stringWithFormat:@"-Duser.home=%s", getenv("POJAV_HOME")].UTF8String;
margv[++margc] = [NSString stringWithFormat:@"-Duser.timezone=%@", NSTimeZone.localTimeZone.name].UTF8String;
Expand Down
70 changes: 70 additions & 0 deletions Natives/dyld_patch_platform.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#import <Foundation/Foundation.h>
#include <libgen.h>
#include <mach-o/fat.h>
#include <mach-o/loader.h>
#include <sys/mman.h>
#include <sys/stat.h>

extern int dyld_get_active_platform();

static BOOL PLPatchMachOPlatformForSlice(const char *path, struct mach_header_64 *header) {
uint8_t *imageHeaderPtr = (uint8_t*)header + sizeof(struct mach_header_64);

struct load_command *command = (struct load_command *)imageHeaderPtr;
for(int i = 0; i < header->ncmds; i++) {
if (command->cmd == LC_BUILD_VERSION) {
struct build_version_command *buildver = (struct build_version_command *)command;
int activePlatform = dyld_get_active_platform();
if (buildver->platform == activePlatform) return NO; // it is already set, stop
buildver->platform = activePlatform; // set to current platform
} else if (command->cmd == LC_LOAD_DYLIB) {
struct dylib_command *dylib = (struct dylib_command *)command;
char *dylibName = (void *)dylib + dylib->dylib.name.offset;
char *verPtr = strstr(dylibName, "/Versions/");
if (verPtr) {
// Remove "/Versions/X"
int lastComponentLen = strlen(dylibName) - (verPtr - dylibName) - 11;
memmove(verPtr, verPtr + 11, lastComponentLen);
verPtr[lastComponentLen] = '\0';
}
}
command = (struct load_command *)((void *)command + command->cmdsize);
}
return YES;
}


BOOL PLPatchMachOPlatformForFile(const char *path) {
int fd = open(path, O_RDWR, (mode_t)0600);
if (fd == -1) return NO;
struct stat s;
fstat(fd, &s);

void *map = mmap(NULL, s.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (!map) {
close(fd);
return NO;
}

BOOL patched = NO;
uint32_t magic = *(uint32_t *)map;
if (magic == FAT_CIGAM) {
// Find compatible slice
struct fat_header *header = (struct fat_header *)map;
struct fat_arch *arch = (struct fat_arch *)(map + sizeof(struct fat_header));
for (int i = 0; i < OSSwapInt32(header->nfat_arch); i++) {
if (OSSwapInt32(arch->cputype) == CPU_TYPE_ARM64) {
NSLog(@"[PojavLauncher] Patching %s", path);
patched |= PLPatchMachOPlatformForSlice(path, (struct mach_header_64 *)(map + OSSwapInt32(arch->offset)));
}
arch = (struct fat_arch *)((void *)arch + sizeof(struct fat_arch));
}
} else if (magic == MH_MAGIC_64 && ((struct mach_header_64 *)map)->cputype == CPU_TYPE_ARM64) {
patched = PLPatchMachOPlatformForSlice(path, (struct mach_header_64 *)map);
}

if (patched) msync(map, s.st_size, MS_SYNC);
munmap(map, s.st_size);
close(fd);
return patched;
}
16 changes: 16 additions & 0 deletions Natives/main_hook.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

void (*orig_abort)();
void (*orig_exit)(int code);
void* (*orig_dlopen)(const char* path, int mode);
int (*orig_open)(const char *path, int oflag, ...);

void handle_fatal_exit(int code) {
Expand Down Expand Up @@ -54,13 +55,27 @@ void hooked_exit(int code) {
}
handle_fatal_exit(code);

/*
if (runtimeJavaVMPtr != NULL) {
(*runtimeJavaVMPtr)->DestroyJavaVM(runtimeJavaVMPtr);
}
*/

orig_exit(code);
}

void* hooked_dlopen(const char* path, int mode) {
const char *home = getenv("POJAV_HOME");
// Only proceed to check if dylib is in the home dir
// Path input from jvm is absolute path(?)
if (!path || strncmp(path, home, strlen(home))) {
return orig_dlopen(path, mode);
}

PLPatchMachOPlatformForFile(path);
return orig_dlopen(path, mode);
}

int hooked_open(const char *path, int oflag, ...) {
va_list args;
va_start(args, oflag);
Expand All @@ -78,6 +93,7 @@ void init_hookFunctions() {
{"abort", hooked_abort, (void *)&orig_abort},
{"__assert_rtn", hooked___assert_rtn, NULL},
{"exit", hooked_exit, (void *)&orig_exit},
{"dlopen", hooked_dlopen, (void *)&orig_dlopen},
{"open", hooked_open, (void *)&orig_open}
};
rebind_symbols(rebindings, sizeof(rebindings)/sizeof(struct rebinding));
Expand Down
Binary file removed Natives/resources/Frameworks/libjnidispatch.dylib
Binary file not shown.
2 changes: 2 additions & 0 deletions Natives/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ void init_hookFunctions();
void init_hookUIKitConstructor();
void init_setupMultiDir();

BOOL PLPatchMachOPlatformForFile(const char *path);

UIWindow* currentWindow();
UIWindow* currentWindowInScene(BOOL external);
UIViewController* currentVC();
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ PojavLauncher is amazing, and surprisingly stable, and it wouldn't be this way w
- [LWJGLX](https://github.com/PojavLauncherTeam/lwjglx) (LWJGL2 API compatibility layer for LWJGL3): unknown license.
- [DBNumberedSlider](https://github.com/khanhduytran0/DBNumberedSlider): [Apache 2.0 License](https://github.com/immago/DBNumberedSlider/blob/master/LICENSE)
- [fishhook](https://github.com/khanhduytran0/fishhook): [BSD-3 License](https://github.com/facebook/fishhook/blob/main/LICENSE).
- [Java Native Access](https://github.com/java-native-access/jna): [Apache 2.0 License](https://github.com/java-native-access/jna/blob/master/LICENSE).
- [shaderc](https://github.com/khanhduytran0/shaderc) (used by Vulkan rendering mods): [Apache 2.0 License](https://github.com/google/shaderc/blob/main/LICENSE).
- [NRFileManager](https://github.com/mozilla-mobile/firefox-ios/tree/b2f89ac40835c5988a1a3eb642982544e00f0f90/ThirdParty/NRFileManager): [MPL-2.0 License](https://www.mozilla.org/en-US/MPL/2.0)
- [AltKit](https://github.com/rileytestut/AltKit)
Expand Down

0 comments on commit ec04885

Please sign in to comment.