From 7ca09aa9ba228a22f6acd19520f520067327efca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=88=9A=28noham=29=C2=B2?= <100566912+NohamR@users.noreply.github.com> Date: Fri, 10 Jul 2026 17:23:52 +0200 Subject: [PATCH] push --- .github/workflows/build.yml | 25 +++++++++++++++ .gitignore | 4 +++ .gitmodules | 3 ++ Makefile | 51 ++++++++++++++++++++++++++++++ README.md | 29 +++++++++++++++++ main.m | 63 +++++++++++++++++++++++++++++++++++++ tinyhook | 1 + 7 files changed, 176 insertions(+) create mode 100644 .github/workflows/build.yml create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 Makefile create mode 100644 README.md create mode 100644 main.m create mode 160000 tinyhook diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..93a4f93 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,25 @@ +name: Build + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +jobs: + build: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Build dylib + run: make + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: MasterPDFEditor.dylib + path: MasterPDFEditor.dylib \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..796e279 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.dylib +*.a +!tinyhook/*.a +.DS_Store diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..79022f7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tinyhook"] + path = tinyhook + url = https://github.com/Antibioticss/tinyhook.git diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8994033 --- /dev/null +++ b/Makefile @@ -0,0 +1,51 @@ +TARGET = MasterPDFEditor.dylib +TINYHOOK = tinyhook +TINYHOOK_LIB_ARM64 = $(TINYHOOK)/libtinyhook_arm64.a +TINYHOOK_LIB_X86_64 = $(TINYHOOK)/libtinyhook_x86_64.a +TARGET_ARM64 = $(TARGET:.dylib=_arm64.dylib) +TARGET_X86_64 = $(TARGET:.dylib=_x86_64.dylib) + +all: $(TARGET) + +$(TINYHOOK_LIB_ARM64): $(TINYHOOK)/Makefile + $(MAKE) -C $(TINYHOOK) clean + $(MAKE) -C $(TINYHOOK) static ARCH=arm64 NO_EXPORT=1 CFLAGS="-arch arm64 -Iinclude -fvisibility=hidden -Os -Wall -Wshadow -DNO_EXPORT" + mv $(TINYHOOK)/libtinyhook.a $@ + +$(TINYHOOK_LIB_X86_64): $(TINYHOOK)/Makefile + $(MAKE) -C $(TINYHOOK) clean + $(MAKE) -C $(TINYHOOK) static ARCH=x86_64 NO_EXPORT=1 CFLAGS="-arch x86_64 -Iinclude -fvisibility=hidden -Os -Wall -Wshadow -DNO_EXPORT" + mv $(TINYHOOK)/libtinyhook.a $@ + +$(TARGET_ARM64): main.m $(TINYHOOK_LIB_ARM64) + clang -arch arm64 \ + -framework Foundation \ + -fobjc-arc \ + main.m \ + -L$(TINYHOOK) -ltinyhook_arm64 \ + -I$(TINYHOOK) \ + -dynamiclib \ + -o $@ \ + -current_version 1.0 \ + -compatibility_version 1.0 + +$(TARGET_X86_64): main.m $(TINYHOOK_LIB_X86_64) + clang -arch x86_64 \ + -framework Foundation \ + -fobjc-arc \ + main.m \ + -L$(TINYHOOK) -ltinyhook_x86_64 \ + -I$(TINYHOOK) \ + -dynamiclib \ + -o $@ \ + -current_version 1.0 \ + -compatibility_version 1.0 + +$(TARGET): $(TARGET_ARM64) $(TARGET_X86_64) + lipo -create -output $@ $^ + +clean: + $(MAKE) -C $(TINYHOOK) clean + rm -f $(TARGET) $(TARGET_ARM64) $(TARGET_X86_64) $(TINYHOOK_LIB_ARM64) $(TINYHOOK_LIB_X86_64) + +.PHONY: all clean \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..8a36190 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# Master PDF Editor Patch + +Bypasses license validation for **Master PDF Editor 5.9.98** using inline hooking via [tinyhook](https://github.com/Antibioticss/tinyhook). + +Supports both **Apple Silicon (arm64)** and **Intel (x86_64)** architectures. + +## How it works + +The dylib hooks `MainWindow::ValidateLicense` to force license flags, then calls `QDocTab::SetRegProgram` to complete activation. + +## Build + +```sh +git clone --recurse-submodules https://github.com/nohamelin/MasterPDFEditor-patch.git +cd MasterPDFEditor-patch +make +``` + +Output: `MasterPDFEditor.dylib` + +## Usage + +```sh +DYLD_INSERT_LIBRARIES=/path/to/MasterPDFEditor.dylib '/Applications/Master PDF Editor.app/Contents/MacOS/Master PDF Editor' +``` + +## Analysis + +Full reverse engineering write-up: [noham.dev](https://noh.am/en/posts/master-pdf-editor-patch-analysis/) \ No newline at end of file diff --git a/main.m b/main.m new file mode 100644 index 0000000..fcbe800 --- /dev/null +++ b/main.m @@ -0,0 +1,63 @@ +#import +#import +#include "tinyhook/include/tinyhook.h" + +#if defined(__arm64__) || defined(__aarch64__) +#define kValidateLicenseAddress 0x10034D4F0 +#define kSetRegProgramAddress 0x10026C430 +#elif defined(__x86_64__) +#define kValidateLicenseAddress 0x10038E2D0 +#define kSetRegProgramAddress 0x100296D90 +#endif + +#define kGuardFlagOffset 107 +#define kLicenseFlagOffset 105 +#define kQDocTabPointerOffset 184 + +static void (*setRegProgram)(void *, BOOL); + +static void hooked_ValidateLicense(void *self) { + *((uint8_t *)self + kGuardFlagOffset) = 1; + *((uint8_t *)self + kLicenseFlagOffset) = 1; + + void **qdoctab = (void **)((char *)self + kQDocTabPointerOffset); + if (qdoctab && *qdoctab) { + setRegProgram(*qdoctab, YES); + } +} + +static int imageIndex(const char *name) { + uint32_t count = _dyld_image_count(); + for (uint32_t i = 0; i < count; i++) { + const char *path = _dyld_get_image_name(i); + const char *last = strrchr(path, '/'); + if (last && strcmp(last + 1, name) == 0) return i; + if (!last && strcmp(path, name) == 0) return i; + } + return -1; +} + +__attribute__((constructor)) +static void init() { + @autoreleasepool { + NSLog(@"[MPE] loaded"); + + int idx = imageIndex("Master PDF Editor"); + if (idx < 0) { + NSLog(@"[MPE] ERROR: image not found"); + return; + } + + intptr_t slide = _dyld_get_image_vmaddr_slide(idx); + + void *target = (void *)(kValidateLicenseAddress + slide); + if (tiny_hook(target, hooked_ValidateLicense, NULL) != 0) { + NSLog(@"[MPE] ERROR: hook failed"); + return; + } + + setRegProgram = (void (*)(void *, BOOL))(kSetRegProgramAddress + slide); + + NSLog(@"[MPE] ready"); + } +} diff --git a/tinyhook b/tinyhook new file mode 160000 index 0000000..ff87982 --- /dev/null +++ b/tinyhook @@ -0,0 +1 @@ +Subproject commit ff87982efb8d8314939a1dd8b48e8b87d5a3513c