├── .gitattributes ├── .gitignore ├── Bundles └── Alt-Zoom │ ├── Makefile │ ├── bundle │ ├── AZHSettings.h │ ├── AZHSettings.m │ ├── Info.plist │ └── hooks.m │ └── settings │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── AppIcon.icns │ ├── Info.plist │ ├── MainMenu.xib │ └── main.m ├── LICENSE ├── MIP ├── Makefile ├── injector │ ├── inject.c │ ├── inject │ │ ├── inject.c │ │ └── inject.h │ ├── injectd.entitlements │ ├── injectd.m │ ├── injectd_client │ │ ├── MIPProtocol.h │ │ ├── injectd_client.h │ │ └── injectd_client.m │ ├── lsd_injector.c │ └── payloads │ │ ├── injected.h │ │ ├── injected_arm64e.c │ │ ├── injected_i386.c │ │ ├── injected_x86_64.c │ │ └── order ├── libSystem.tbd ├── loader │ ├── loader.h │ ├── loader.m │ └── loader_public.h ├── local.injectd.plist ├── local.lsdinjector.plist └── third-party │ ├── markgc.cpp │ └── substitute │ ├── LICENSE.txt │ ├── generated │ ├── darwin-inject-asm.S │ ├── generic-dis-arm64.inc.h │ └── manual-mach.inc.h │ ├── lib │ ├── arm64 │ │ ├── arch-dis.h │ │ ├── arch-transform-dis.inc.h │ │ ├── assemble.h │ │ ├── dis-main.inc.h │ │ ├── jump-patch.h │ │ └── misc.h │ ├── cbit │ │ ├── htab.h │ │ ├── misc.h │ │ ├── vec.c │ │ └── vec.h │ ├── darwin │ │ ├── execmem.c │ │ ├── find-syms.c │ │ ├── inject.c │ │ ├── interpose.c │ │ ├── mach-decls.h │ │ ├── manual-syscall.h │ │ ├── objc-asm.S │ │ ├── objc.c │ │ ├── objc.h │ │ ├── read.c │ │ ├── read.h │ │ └── substrate-compat.c │ ├── dis.h │ ├── execmem.h │ ├── hook-functions.c │ ├── jump-dis.c │ ├── jump-dis.h │ ├── ptrauth_helpers.h │ ├── strerror.c │ ├── substitute-internal.h │ ├── substitute.h │ ├── transform-dis.c │ ├── transform-dis.h │ └── x86 │ │ ├── arch-dis.h │ │ ├── arch-transform-dis.inc.h │ │ ├── dis-main.inc.h │ │ ├── jump-patch.h │ │ └── misc.h │ └── vendor │ └── dyld_cache_format.h └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | */third-party/* linguist-vendored 2 | MIP/injector/*.h linguist-language=C 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /Bundles/Alt-Zoom/Makefile: -------------------------------------------------------------------------------- 1 | # Make hacks 2 | .INTERMEDIATE: 3 | .PHONY: all install 4 | 5 | MIP_ROOT ?= /Library/Apple/System/Library/Frameworks/mip 6 | 7 | all: build/Alt-Zoom.bundle build/Alt-Zoom.app 8 | 9 | CC := clang 10 | CFLAGS += -mmacosx-version-min=10.10 -Werror -Wall -I. -I/usr/local/include -g -O3 11 | LDFLAGS += -mmacosx-version-min=10.10 -framework AppKit 12 | 13 | SYSROOT ?= $(shell xcodebuild -sdk macosx -version Path 2> /dev/null) 14 | CFLAGS += -isysroot $(SYSROOT) 15 | LDFLAGS += -isysroot $(SYSROOT) 16 | 17 | BUNDLE_CFLAGS := $(CFLAGS) 18 | BUNDLE_LDFLAGS := $(LDFLAGS) 19 | 20 | BUNDLE_CFLAGS += -arch x86_64 21 | BUNDLE_LDFLAGS += -arch x86_64 22 | 23 | ifeq ($(shell uname -m),arm64) 24 | BUNDLE_CFLAGS += -arch arm64e 25 | BUNDLE_LDFLAGS += -arch arm64e 26 | else 27 | ifeq ($(shell arch -i386 2>&1 | grep Unknown),) 28 | BUNDLE_CFLAGS += -arch i386 29 | BUNDLE_LDFLAGS += -arch i386 30 | endif 31 | endif 32 | 33 | SIGN_IDENTITY ?= CodeSign 34 | CODESIGN_TARGET := codesign -fs "$(SIGN_IDENTITY)" 35 | 36 | BUNDLE_SOURCES := $(shell ls bundle/*.m) 37 | SETTINGS_SOURCES := $(shell ls settings/*.m) 38 | 39 | BUNDLE_OBJECTS := $(patsubst %,build/%.o,$(BUNDLE_SOURCES)) 40 | SETTINGS_OBJECTS := $(patsubst %,build/%.o,$(SETTINGS_SOURCES)) 41 | ALL_OBJECTS := $(BUNDLE_OBJECTS) $(SETTINGS_OBJECTS) 42 | 43 | # Automatic dependency generation 44 | 45 | ifneq ($(MAKECMDGOALS),clean) 46 | -include $(ALL_OBJECTS:.o=.dep) 47 | endif 48 | 49 | build/%.dep: % 50 | -@mkdir -p $(dir $@) 51 | $(CC) $(CFLAGS) -MT build/$^.o -M $^ -c -o $@ 52 | 53 | build/settings/%.o: settings/% 54 | -@mkdir -p $(dir $@) 55 | $(CC) $(CFLAGS) -fobjc-arc -c $< -o $@ 56 | 57 | build/bundle/%.o: bundle/% 58 | -@mkdir -p $(dir $@) 59 | $(CC) $(BUNDLE_CFLAGS) -fobjc-arc -c $< -o $@ 60 | 61 | build/Alt-Zoom.bundle: build/Alt-Zoom.bundle/Contents/MacOS/Alt-Zoom build/Alt-Zoom.bundle/Contents/Info.plist 62 | $(CODESIGN_TARGET) $@ 63 | build/Alt-Zoom.app: build/Alt-Zoom.app/Contents/MacOS/Alt-Zoom\ 64 | build/Alt-Zoom.app/Contents/Info.plist\ 65 | build/Alt-Zoom.app/Contents/Resources/Base.lproj/MainMenu.nib\ 66 | build/Alt-Zoom.app/Contents/Resources/AppIcon.icns 67 | $(CODESIGN_TARGET) $@ 68 | 69 | build/Alt-Zoom.bundle/Contents/MacOS/Alt-Zoom: $(BUNDLE_OBJECTS) 70 | -@mkdir -p $(dir $@) 71 | $(CC) $(BUNDLE_LDFLAGS) -bundle $(MIP_ROOT)/loader.dylib $^ -o $@ 72 | 73 | build/Alt-Zoom.bundle/Contents/%: bundle/% 74 | -@mkdir -p $(dir $@) 75 | cp -f $^ $@ 76 | 77 | build/Alt-Zoom.app/Contents/Resources/Base.lproj/%.nib: settings/%.xib 78 | -@mkdir -p $(dir $@) 79 | ibtool --compile $@ $^ 80 | 81 | build/Alt-Zoom.app/Contents/%: settings/% 82 | -@mkdir -p $(dir $@) 83 | cp -f $^ $@ 84 | 85 | build/Alt-Zoom.app/Contents/Resources/%: settings/% 86 | -@mkdir -p $(dir $@) 87 | cp -f $^ $@ 88 | 89 | build/Alt-Zoom.app/Contents/MacOS/Alt-Zoom: $(SETTINGS_OBJECTS) 90 | -@mkdir -p $(dir $@) 91 | $(CC) $(LDFLAGS) $^ -o $@ 92 | 93 | 94 | clean: 95 | rm -rf build 96 | 97 | install: all 98 | sudo cp -rf build/Alt-Zoom.app /Applications/ 99 | sudo rm -rf $(MIP_ROOT)/Bundles/Alt-Zoom.bundle 100 | sudo cp -rf build/Alt-Zoom.bundle $(MIP_ROOT)/Bundles/ 101 | 102 | uninstall: 103 | sudo rm -rf $(MIP_ROOT)/Bundles/Alt-Zoom.bundle 104 | sudo rm -rf /Applications/Alt-Zoom.app 105 | -------------------------------------------------------------------------------- /Bundles/Alt-Zoom/bundle/AZHSettings.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | typedef enum { 4 | AZH_SAME_AS_DEFAULT, 5 | AZH_ZOOM, 6 | AZH_FULLSCREEN, 7 | AZH_MAXIMIZE, 8 | } AZHBehavior; 9 | 10 | @interface AZHSettings : NSObject 11 | 12 | + (AZHBehavior) behaviorForModifiers: (NSEventModifierFlags) modifiers; 13 | + (void) updateSettings: (NSDictionary *)settings; 14 | + (NSDictionary *)settings; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Bundles/Alt-Zoom/bundle/AZHSettings.m: -------------------------------------------------------------------------------- 1 | #import 2 | #include 3 | #import "AZHSettings.h" 4 | 5 | NSString *plist_location = nil; 6 | NSDictionary *settings = nil; 7 | 8 | @implementation AZHSettings 9 | 10 | + (AZHBehavior) _behaviorForModifiers: (NSEventModifierFlags) modifiers 11 | { 12 | switch (modifiers & (NSEventModifierFlagShift | NSEventModifierFlagControl | NSEventModifierFlagOption | NSEventModifierFlagCommand)) { 13 | case NSEventModifierFlagShift: 14 | return [[settings objectForKey:@"Shift"] intValue]; 15 | 16 | case NSEventModifierFlagControl: 17 | return [[settings objectForKey:@"Control"] intValue]; 18 | 19 | case NSEventModifierFlagOption: 20 | return [[settings objectForKey:@"Alt"] intValue]; 21 | 22 | case NSEventModifierFlagCommand: 23 | return [[settings objectForKey:@"Command"] intValue]; 24 | 25 | default: 26 | return [[settings objectForKey:@"Default"] intValue]; 27 | } 28 | } 29 | 30 | + (AZHBehavior) behaviorForModifiers: (NSEventModifierFlags) modifiers 31 | { 32 | AZHBehavior ret = [self _behaviorForModifiers:modifiers]; 33 | if (ret == AZH_SAME_AS_DEFAULT) { 34 | return [self _behaviorForModifiers:0]; 35 | } 36 | return ret; 37 | } 38 | 39 | + (void) reloadSettings 40 | { 41 | settings = [NSDictionary dictionaryWithContentsOfFile:plist_location]; 42 | if (!settings) { 43 | settings = @{ 44 | @"Default": @(AZH_FULLSCREEN), 45 | @"Alt": @(AZH_ZOOM), 46 | }; 47 | } 48 | } 49 | 50 | + (void) updateSettings: (NSDictionary *)settings 51 | { 52 | [settings writeToFile:plist_location atomically:YES]; 53 | [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"AZHSettingsUpdated" object:nil]; 54 | } 55 | 56 | + (void) load 57 | { 58 | plist_location = [[@(MIP_user_data_path()) 59 | stringByAppendingPathComponent:[[NSBundle bundleForClass:self] bundleIdentifier]] 60 | stringByAppendingPathExtension:@"plist"]; 61 | [[NSDistributedNotificationCenter defaultCenter] addObserver:self 62 | selector:@selector(reloadSettings) 63 | name:@"AZHSettingsUpdated" 64 | object:nil]; 65 | [self reloadSettings]; 66 | } 67 | 68 | + (NSDictionary *)settings 69 | { 70 | return settings; 71 | } 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /Bundles/Alt-Zoom/bundle/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MIPUseBlacklistMode 6 | 7 | MIPBundleNames 8 | 9 | com.apple.AppKit 10 | 11 | MIPExcludedBundleNames 12 | 13 | com.apple.dock 14 | 15 | 16 | CFBundleExecutable 17 | Alt-Zoom 18 | CFBundleIdentifier 19 | local.alt-zoom 20 | CFBundleInfoDictionaryVersion 21 | 6.0 22 | CFBundleName 23 | Alt-Zoom 24 | CFBundlePackageType 25 | BNDL 26 | CFBundleShortVersionString 27 | 1.0 28 | CFBundleVersion 29 | 1 30 | 31 | 32 | -------------------------------------------------------------------------------- /Bundles/Alt-Zoom/bundle/hooks.m: -------------------------------------------------------------------------------- 1 | #import 2 | #include 3 | #include "AZHSettings.h" 4 | 5 | static bool checking_for_fullscreen = false; 6 | 7 | /* Hook NSEvent methods to report a different status of the Alt/Option key when checking_for_fullscreen is true*/ 8 | @implementation NSEvent (AltZoomHook) 9 | 10 | - (NSEventModifierFlags) AZH_modifierFlags 11 | { 12 | NSEventModifierFlags ret = [self AZH_modifierFlags]; 13 | 14 | if (checking_for_fullscreen) { 15 | if ([AZHSettings behaviorForModifiers:ret] == AZH_FULLSCREEN) { 16 | ret &= ~NSEventModifierFlagOption; 17 | } 18 | else { 19 | ret |= NSEventModifierFlagOption; 20 | } 21 | } 22 | 23 | return ret; 24 | } 25 | 26 | + (NSEventModifierFlags) AZH_modifierFlags 27 | { 28 | NSEventModifierFlags ret = [self AZH_modifierFlags]; 29 | 30 | if (checking_for_fullscreen) { 31 | if ([AZHSettings behaviorForModifiers:ret] == AZH_FULLSCREEN) { 32 | ret &= ~NSEventModifierFlagOption; 33 | } 34 | else { 35 | ret |= NSEventModifierFlagOption; 36 | } 37 | } 38 | 39 | return ret; 40 | } 41 | 42 | 43 | + (void) load 44 | { 45 | method_exchangeImplementations(class_getClassMethod(self, @selector(modifierFlags)), 46 | class_getClassMethod(self, @selector(AZH_modifierFlags))); 47 | 48 | method_exchangeImplementations(class_getInstanceMethod(self, @selector(modifierFlags)), 49 | class_getInstanceMethod(self, @selector(AZH_modifierFlags))); 50 | 51 | } 52 | @end 53 | 54 | 55 | /* Hook _NSThemeWidgetCell to set checking_for_fullscreen (This controls the icon drawing) */ 56 | @interface _NSThemeWidgetCell : NSCell 57 | - (void *) coreUIWidgetType; 58 | @end 59 | 60 | @implementation _NSThemeWidgetCell (AltZoomHook) 61 | - (void *) AZH_coreUIWidgetType 62 | { 63 | checking_for_fullscreen = true; 64 | void *ret = [self AZH_coreUIWidgetType]; 65 | checking_for_fullscreen = false; 66 | return ret; 67 | } 68 | 69 | + (void) load 70 | { 71 | method_exchangeImplementations(class_getInstanceMethod(self, @selector(coreUIWidgetType)), 72 | class_getInstanceMethod(self, @selector(AZH_coreUIWidgetType))); 73 | 74 | } 75 | @end 76 | 77 | @interface NSWindow () 78 | - (void) _setNeedsZoom:(id) sender; 79 | - (struct CGRect)_standardFrame; 80 | @end 81 | 82 | /* Hook NSWindow to set checking_for_fullscreen (This controls the actual action taken) */ 83 | @implementation NSWindow (AltZoomHook) 84 | - (void) AZH__setNeedsZoom:(id) sender; 85 | { 86 | checking_for_fullscreen = true; 87 | [self AZH__setNeedsZoom:sender]; 88 | checking_for_fullscreen = false; 89 | } 90 | 91 | - (struct CGRect)AZH__standardFrame 92 | { 93 | /* Disable hooking, but for modifierFlags and for application callbacks triggered by standardFrame */ 94 | checking_for_fullscreen = false; 95 | 96 | if ([AZHSettings behaviorForModifiers:[NSEvent modifierFlags]] != AZH_MAXIMIZE) { 97 | return [self AZH__standardFrame]; 98 | } 99 | 100 | id old_delegate = self.delegate; 101 | self.delegate = nil; 102 | struct CGRect ret = [self AZH__standardFrame]; 103 | self.delegate = old_delegate; 104 | return ret; 105 | } 106 | 107 | + (void) load 108 | { 109 | method_exchangeImplementations(class_getInstanceMethod(self, @selector(_setNeedsZoom:)), 110 | class_getInstanceMethod(self, @selector(AZH__setNeedsZoom:))); 111 | method_exchangeImplementations(class_getInstanceMethod(self, @selector(_standardFrame)), 112 | class_getInstanceMethod(self, @selector(AZH__standardFrame))); 113 | 114 | 115 | } 116 | @end 117 | -------------------------------------------------------------------------------- /Bundles/Alt-Zoom/settings/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface AppDelegate : NSObject 4 | 5 | - (IBAction)settingChanged:(id)sender; 6 | 7 | @end 8 | 9 | -------------------------------------------------------------------------------- /Bundles/Alt-Zoom/settings/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "AppDelegate.h" 3 | 4 | @interface AppDelegate () 5 | @property (weak) IBOutlet NSWindow *window; 6 | @property (weak) IBOutlet NSPopUpButtonCell *defaultButton; 7 | @property (weak) IBOutlet NSPopUpButton *shiftButton; 8 | @property (weak) IBOutlet NSPopUpButton *altButton; 9 | @property (weak) IBOutlet NSPopUpButton *commandButton; 10 | @property (weak) IBOutlet NSPopUpButton *controlButton; 11 | @end 12 | 13 | @implementation AppDelegate 14 | 15 | - (Class) getSettingsClass 16 | { 17 | Class $AZHSettings = NSClassFromString(@"AZHSettings"); 18 | if (!$AZHSettings) { 19 | NSAlert *alert = [[NSAlert alloc] init]; 20 | [alert setMessageText:@"Alt Zoom does not appear to be installed."]; 21 | [alert addButtonWithTitle:@"Close"]; 22 | [alert runModal]; 23 | [NSApp terminate:nil]; 24 | } 25 | 26 | return $AZHSettings; 27 | } 28 | 29 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 30 | { 31 | Class $AZHSettings = [self getSettingsClass]; 32 | 33 | NSDictionary *settings = [$AZHSettings settings]; 34 | [self.defaultButton selectItemWithTag: [settings[@"Default"] intValue]]; 35 | [self.shiftButton selectItemWithTag: [settings[@"Shift"] intValue]]; 36 | [self.altButton selectItemWithTag: [settings[@"Alt"] intValue]]; 37 | [self.commandButton selectItemWithTag: [settings[@"Command"] intValue]]; 38 | [self.controlButton selectItemWithTag: [settings[@"Control"] intValue]]; 39 | } 40 | 41 | - (IBAction)settingChanged:(NSPopUpButton *)sender 42 | { 43 | Class $AZHSettings = [self getSettingsClass]; 44 | 45 | NSMutableDictionary *new_settings = [[$AZHSettings settings] mutableCopy]; 46 | new_settings[@((char *[]){ 47 | "Default", 48 | "Shift", 49 | "Alt", 50 | "Command", 51 | "Control", 52 | }[sender.tag])] = @(sender.selectedTag); 53 | 54 | [$AZHSettings updateSettings:new_settings]; 55 | } 56 | 57 | - (void)windowWillClose:(NSNotification *)notification 58 | { 59 | [NSApp terminate: nil]; 60 | } 61 | @end 62 | -------------------------------------------------------------------------------- /Bundles/Alt-Zoom/settings/AppIcon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LIJI32/MIP/04b4675cc205cf0cd04a7733bce9725bbc6b783e/Bundles/Alt-Zoom/settings/AppIcon.icns -------------------------------------------------------------------------------- /Bundles/Alt-Zoom/settings/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | Alt-Zoom 9 | CFBundleIconFile 10 | AppIcon.icns 11 | CFBundleIdentifier 12 | local.alt-zoom.settings 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | Alt-Zoom Settings 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | NSMainNibFile 24 | MainMenu 25 | NSPrincipalClass 26 | NSApplication 27 | 28 | 29 | -------------------------------------------------------------------------------- /Bundles/Alt-Zoom/settings/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | int main(int argc, const char * argv[]) { 4 | return NSApplicationMain(argc, argv); 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | X11 License 2 | 3 | Copyright (c) 2017-2025 Lior Halphon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /MIP/Makefile: -------------------------------------------------------------------------------- 1 | .SECONDARY: 2 | .PHONY: all install uninstall 3 | 4 | LDFLAGS := -g -mmacosx-version-min=10.10 -lc -F/System/Library/PrivateFrameworks 5 | CFLAGS := -g -mmacosx-version-min=10.10 -I. -Werror -O3 -Wno-unknown-warning-option -Wno-deprecated-declarations -Wno-interrupt-service-routine 6 | SYSROOT ?= $(shell xcodebuild -sdk macosx -version Path 2> /dev/null) 7 | ifeq ($(SYSROOT),) 8 | SYSROOT := $(shell find /Library/Developer/CommandLineTools/SDKs -maxdepth 1 -name "MacOSX1*.sdk" | sort -V | tail -n 1) 9 | endif 10 | CFLAGS += -isysroot $(SYSROOT) 11 | SUBSTITUTE ?= YES 12 | MIP_ROOT ?= /Library/Apple/System/Library/Frameworks/mip 13 | 14 | ifneq ($(SUBSTITUTE),YES) 15 | ifneq ($(SUBSTITUTE),NO) 16 | $(error SUBSTITUTE must be either YES or NO) 17 | endif 18 | endif 19 | 20 | LOADER_SOURCES := loader/loader.m 21 | ifeq ($(SUBSTITUTE),YES) 22 | SUBSTITUTE_CFLAGS := -Ithird-party/substitute/lib -Ithird-party/substitute/vendor 23 | LOADER_SOURCES += $(shell find third-party/substitute -name "*.c" -o -name "*.S") 24 | endif 25 | 26 | ifeq ($(shell uname -m),arm64) 27 | ARCH1 := arm64e 28 | ARCH2 := x86_64 29 | DUAL_ARCH ?= 1 30 | CFLAGS += -DROSETTA 31 | else 32 | ARCH1 := x86_64 33 | ARCH2 := i386 34 | endif 35 | 36 | 37 | SIGN_IDENTITY ?= CodeSign 38 | CODESIGN_TARGET := codesign -s "$(SIGN_IDENTITY)" 39 | 40 | all: build/lsdinjector.dylib build/loader.dylib build/inject build/injectd local.lsdinjector.plist 41 | 42 | build/lsdinjector.dylib: build/injector/lsd_injector.c.o build/injector/injectd_client/injectd_client.m.o 43 | $(CC) $(LDFLAGS) -arch $(ARCH1) $^ -lbsm -framework Foundation -shared -o $@ 44 | $(CODESIGN_TARGET) $@ 45 | 46 | build/loader.dylib: $(LOADER_SOURCES) 47 | $(CC) $(CFLAGS) $(SUBSTITUTE_CFLAGS) $(LDFLAGS) -install_name @loader_path/../../../../loader.dylib -framework Foundation -shared -arch $(ARCH1) $(if $(DUAL_ARCH), -arch $(ARCH2)) $^ -o $@ 48 | $(CODESIGN_TARGET) $@ 49 | 50 | build/injectd: build/injector/injector.o build/injector/injectd.m.o | injector/injectd.entitlements 51 | mkdir -p $(dir $@) 52 | $(CC) $(LDFLAGS) -framework Foundation $^ -o $@ -arch $(ARCH1) 53 | $(CODESIGN_TARGET) --entitlements injector/injectd.entitlements $@ 54 | 55 | build/inject: build/injector/inject.c.o build/injector/injectd_client/injectd_client.m.o 56 | mkdir -p $(dir $@) 57 | $(CC) $(LDFLAGS) -framework Foundation $^ -o $@ -arch $(ARCH1) 58 | $(CODESIGN_TARGET) $@ 59 | 60 | build/injector/injector.o: build/injector/inject/inject.c.o build/injector/payloads/injected_$(ARCH1).c.bin build/injector/payloads/injected_$(ARCH2).c.bin 61 | ld -r $(filter %.o,$^) -o $@ -sectcreate __INJ_$(ARCH1) __inj_$(ARCH1) build/injector/payloads/injected_$(ARCH1).c.bin -sectcreate __INJ_$(ARCH2) __inj_$(ARCH2) build/injector/payloads/injected_$(ARCH2).c.bin 62 | 63 | build/%.c.o: %.c 64 | mkdir -p $(dir $@) 65 | $(CC) $(CFLAGS) -arch $(ARCH1) -c $^ -o $@ 66 | 67 | build/%.m.o: %.m 68 | mkdir -p $(dir $@) 69 | $(CC) $(CFLAGS) -fobjc-arc -arch $(ARCH1) -c $^ -o $@ 70 | 71 | 72 | build/injector/payloads/injected_i386.c.dylib: injector/payloads/injected_i386.c 73 | mkdir -p $(dir $@) 74 | 75 | @# Fix the unaligned movaps LLVM bug, and convert iret to ret. 76 | $(CC) $(CFLAGS) -fno-exceptions -arch i386 -Oz $^ -S -o - \ 77 | | sed "s/^ iret/ ret/g" \ 78 | | sed "s/^ movaps %xmm7, -32(%ebp)/ movaps %xmm7, -40(%ebp)/g" \ 79 | | sed "s/^ movaps -32(%ebp), %xmm7/ movaps -40(%ebp), %xmm7/g" \ 80 | | clang -L. -Wl,-sectalign,__TEXT,__text,4000 -Wl,-order_file,injector/payloads/order -shared -isysroot $(SYSROOT) -xassembler - -arch i386 -o $@ 81 | 82 | build/injector/payloads/injected_x86_64.c.dylib: injector/payloads/injected_x86_64.c 83 | mkdir -p $(dir $@) 84 | $(CC) $(CFLAGS) -Wl,-sectalign,__TEXT,__text,4000 -fno-exceptions -shared -arch x86_64 -Oz -Wl,-order_file,injector/payloads/order $^ -o $@ 85 | 86 | build/injector/payloads/injected_arm64e.c.dylib: injector/payloads/injected_arm64e.c 87 | mkdir -p $(dir $@) 88 | $(CC) $(CFLAGS) -Wl,-sectalign,__TEXT,__text,4000 -fno-exceptions -shared -arch arm64e -Oz -Wl,-order_file,injector/payloads/order $^ -o $@ 89 | 90 | build/injector/payloads/%.bin: build/injector/payloads/%.dylib 91 | gobjcopy -Obinary $^ $@ 92 | 93 | 94 | 95 | install: all 96 | sudo mkdir -p $(MIP_ROOT)/user_data 97 | sudo mkdir -p $(MIP_ROOT)/Bundles 98 | sudo mkdir -p /usr/local/include/mip 99 | @# We remove the old libraries before copying, overwriting causes codesigning issues. 100 | -@sudo rm -f $(MIP_ROOT)/{lsdinjector.dylib,loader.dylib,injectd} 101 | sudo cp build/lsdinjector.dylib build/loader.dylib build/injectd $(MIP_ROOT) 102 | sudo cp build/inject /usr/local/bin/ 103 | sudo cp loader/loader_public.h /usr/local/include/mip/loader.h 104 | sed "s=@MIP_ROOT@=$(MIP_ROOT)=g" local.injectd.plist | sudo dd of=/Library/LaunchDaemons/local.injectd.plist 105 | sed "s=@MIP_ROOT@=$(MIP_ROOT)=g" local.lsdinjector.plist | sudo dd of=/Library/LaunchDaemons/local.lsdinjector.plist 106 | 107 | sudo defaults write /Library/Preferences/com.apple.security.libraryvalidation.plist DisableLibraryValidation -bool true 108 | -sudo launchctl bootstrap system /Library/LaunchDaemons/local.injectd.plist 109 | if nvram boot-args | grep -v tss_should_crash=0; then \ 110 | sudo nvram boot-args="`nvram boot-args | cut -c 11-` tss_should_crash=0"; \ 111 | if nvram boot-args | grep -v tss_should_crash=0; then\ 112 | echo "Failed to add tss_should_crash=0. If you're running macOS Ventura or newer in a VM, you will need to update this bootarg manually."; \ 113 | else \ 114 | echo "Added tss_should_crash=0 boot argument. If you're running macOS Ventura or newer, a reboot is required"; \ 115 | fi \ 116 | fi 117 | if nvram boot-args | grep -v amfi_get_out_of_my_way=1; then \ 118 | sudo nvram boot-args="`nvram boot-args | cut -c 11-` amfi_get_out_of_my_way=1"; \ 119 | if nvram boot-args | grep -v amfi_get_out_of_my_way=1; then\ 120 | echo "Failed to add amfi_get_out_of_my_way=1. If you're running macOS Sonoma or newer in a VM, you will need to update this bootarg manually."; \ 121 | else \ 122 | echo "Added amfi_get_out_of_my_way=1 boot argument. If you're running macOS Sonoma or newer, a reboot is required"; \ 123 | fi \ 124 | fi 125 | (read -p "Inject MIP to launchservicesd without a restart? [y/N] " -n 1 -r; echo ; if [[ $$REPLY =~ ^[Yy]$$ ]]; then sudo inject launchservicesd $(MIP_ROOT)/lsdinjector.dylib; fi;) 126 | 127 | uninstall: 128 | -sudo rm -rf $(MIP_ROOT) 129 | -sudo rm -rf /usr/include/mip 130 | -sudo rm /Library/LaunchDaemons/local.lsdinjector.plist 131 | -sudo defaults delete /Library/Preferences/com.apple.security.libraryvalidation.plist DisableLibraryValidation 132 | 133 | clean: 134 | rm -rf build 135 | -------------------------------------------------------------------------------- /MIP/injector/inject.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "injectd_client/injectd_client.h" 11 | 12 | int main(int argc, const char **argv) 13 | { 14 | bool wait_for_process = false; 15 | unsigned delay = 0; 16 | if (argc == 4 && argv[3][0] == '-' && argv[3][1] == 'w') { 17 | argc = 3; 18 | delay = atoi(argv[3] + 2); 19 | wait_for_process = true; 20 | } 21 | 22 | if (argc != 3) { 23 | fprintf(stderr, "Usage: %s pid/name dylib [-w[delay]]\n", argv[0]); 24 | exit(-1); 25 | } 26 | 27 | mach_port_t task = 0; 28 | int pid = atoi(argv[1]); 29 | if (pid == 0) { 30 | do { 31 | fprintf(stderr, "Searching...\n"); 32 | if (strlen(argv[1]) > 16) { 33 | fprintf(stderr, "Searching for process by name is currently not supported " 34 | "for names longer than 16 characters. Use PID instead. \n"); 35 | exit(-1); 36 | } 37 | 38 | int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }; 39 | size_t buf_size = 0; 40 | 41 | if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &buf_size, NULL, 0) < 0) { 42 | perror("Failure calling sysctl"); 43 | return errno; 44 | } 45 | 46 | struct kinfo_proc *kprocbuf = malloc(buf_size); 47 | if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), kprocbuf, &buf_size, NULL, 0) < 0) { 48 | perror("Failure calling sysctl"); 49 | return errno; 50 | } 51 | 52 | size_t count = buf_size / sizeof(kprocbuf[0]); 53 | for (int i = 0; i < count; i++) { 54 | if (strcmp(kprocbuf[i].kp_proc.p_comm, argv[1]) == 0) { 55 | pid = kprocbuf[i].kp_proc.p_pid; 56 | break; 57 | } 58 | } 59 | 60 | free(kprocbuf); 61 | } while (pid == 0 && wait_for_process); 62 | } 63 | 64 | else if (wait_for_process) { 65 | fprintf(stderr, "-w must be used with a process name, not PID\n"); 66 | exit(-1); 67 | } 68 | 69 | if (pid == 0) { 70 | fprintf(stderr, "Failed to find process named %s.\n", argv[1]); 71 | exit(-1); 72 | } 73 | 74 | if (delay) { 75 | sleep(delay); 76 | } 77 | 78 | fprintf(stderr, "Injecting to process %d\n", pid); 79 | 80 | const char *error = inject_to_pid(pid, argv[2], true); 81 | if (error) { 82 | fprintf(stderr, "Injection failed: %s\n", error); 83 | exit(1); 84 | } 85 | 86 | return 0; 87 | } 88 | -------------------------------------------------------------------------------- /MIP/injector/inject/inject.h: -------------------------------------------------------------------------------- 1 | #include 2 | kern_return_t inject_to_task(mach_port_t task, const char *argument); 3 | kern_return_t get_thread_port_for_task(mach_port_t task, mach_port_t *thread); 4 | -------------------------------------------------------------------------------- /MIP/injector/injectd.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.private.thread-set-state 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /MIP/injector/injectd.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "injectd_client/MIPProtocol.h" 3 | #import "inject/inject.h" 4 | 5 | @interface MIPConnection : NSObject 6 | @end 7 | 8 | @implementation MIPConnection 9 | 10 | - (void)injectDylib:(const char *)dylib toPID:(pid_t)pid interruptSyscalls:(bool)interrupt withReply:(void (^)(NSString *error))reply 11 | { 12 | kern_return_t ret = KERN_SUCCESS; 13 | mach_port_t task = 0; 14 | 15 | if ((ret = task_for_pid(mach_task_self(), pid, &task))) { 16 | reply(@"Failed to obtain task for PID."); 17 | return; 18 | } 19 | 20 | ret = inject_to_task(task, dylib); 21 | if (ret) { 22 | reply(@"Injection failed, check Console for details."); 23 | return; 24 | } 25 | 26 | if (!interrupt) { 27 | reply(nil); 28 | return; 29 | } 30 | 31 | /* This interrupts blocking system calls to ensure execution. */ 32 | mach_port_t thread; 33 | 34 | ret = get_thread_port_for_task(task, &thread); 35 | if (!ret) { 36 | ret = thread_abort(thread); 37 | } 38 | if (ret) { 39 | reply(@"Injection succeeded, but the injected library will only run after the main thread wakes up."); 40 | return; 41 | } 42 | 43 | reply(nil); 44 | } 45 | 46 | @end 47 | 48 | @interface ServiceDelegate : NSObject 49 | @end 50 | 51 | @implementation ServiceDelegate 52 | 53 | - (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection 54 | { 55 | if (newConnection.effectiveUserIdentifier != 0) { 56 | [newConnection invalidate]; 57 | return false; 58 | } 59 | 60 | newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MIPProtocol)]; 61 | MIPConnection *exportedObject = [[MIPConnection alloc] init]; 62 | newConnection.exportedObject = exportedObject; 63 | 64 | [newConnection resume]; 65 | return true; 66 | } 67 | 68 | @end 69 | 70 | int main(int argc, const char *argv[]) 71 | { 72 | ServiceDelegate *delegate = [ServiceDelegate new]; 73 | 74 | NSXPCListener *listener = [[NSXPCListener alloc] initWithMachServiceName:@"local.injectd"]; 75 | listener.delegate = delegate; 76 | 77 | [listener resume]; 78 | [[NSRunLoop mainRunLoop] run]; 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /MIP/injector/injectd_client/MIPProtocol.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @protocol MIPProtocol 4 | - (void)injectDylib:(const char *)dylib toPID:(pid_t)pid interruptSyscalls:(bool)interrupt withReply:(void (^)(NSString *error))reply; 5 | @end 6 | -------------------------------------------------------------------------------- /MIP/injector/injectd_client/injectd_client.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | const char *inject_to_pid(pid_t pid, const char *dylib, bool interrupt_syscalls); 5 | -------------------------------------------------------------------------------- /MIP/injector/injectd_client/injectd_client.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "MIPProtocol.h" 4 | 5 | const char *inject_to_pid(pid_t pid, const char *dylib, bool interrupt_syscalls) 6 | { 7 | __block NSString *ret = @"Could not connect to injectd"; 8 | 9 | @autoreleasepool { 10 | static id remoteObject = nil; 11 | static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 12 | 13 | 14 | __block bool shouldRetry = false; 15 | __block bool didRetry = false; 16 | 17 | retry: 18 | pthread_mutex_lock(&lock); 19 | if (!remoteObject) { 20 | static NSXPCConnection *connection = nil; 21 | if (connection) { 22 | [connection invalidate]; 23 | connection = nil; 24 | } 25 | 26 | connection = [[NSXPCConnection alloc] initWithMachServiceName:@"local.injectd" options:0]; 27 | connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MIPProtocol)]; 28 | [connection resume]; 29 | 30 | remoteObject = [connection synchronousRemoteObjectProxyWithErrorHandler:^(NSError *error) { 31 | NSLog(@"injectd connection error: %@", error.localizedDescription); 32 | ret = error.localizedDescription; 33 | if (!didRetry) { 34 | shouldRetry = true; 35 | } 36 | }]; 37 | } 38 | 39 | [remoteObject injectDylib:dylib toPID:pid interruptSyscalls:interrupt_syscalls withReply:^(NSString *reply) { 40 | ret = reply; 41 | }]; 42 | 43 | pthread_mutex_unlock(&lock); 44 | 45 | if (shouldRetry) { 46 | shouldRetry = false; 47 | didRetry = true; 48 | remoteObject = nil; 49 | goto retry; 50 | } 51 | } 52 | 53 | return ret.UTF8String; 54 | } 55 | -------------------------------------------------------------------------------- /MIP/injector/lsd_injector.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "injectd_client/injectd_client.h" 11 | 12 | // Private APIs 13 | extern mach_port_t xpc_dictionary_copy_mach_send(xpc_object_t, const char *); 14 | extern void xpc_dictionary_get_audit_token(xpc_object_t xdict, audit_token_t *token); 15 | 16 | static const char *MIP_injector_path(void) 17 | { 18 | Dl_info info; 19 | dladdr("", &info); // Get own info 20 | return info.dli_fname; 21 | } 22 | 23 | static const char *MIP_loader_path(void) 24 | { 25 | static char *ret = NULL; 26 | if (ret) return ret; 27 | 28 | ret = strdup(MIP_injector_path()); 29 | *strrchr(ret, '/') = 0; 30 | 31 | // strlen("loader.dylib") < strlen("lsdinjector.dylib") 32 | strcat(ret, "/loader.dylib"); 33 | 34 | return ret; 35 | } 36 | 37 | static const char *MIP_root_path(void) 38 | { 39 | static char *ret = NULL; 40 | if (ret) return ret; 41 | 42 | ret = strdup(MIP_injector_path()); 43 | *strrchr(ret, '/') = 0; 44 | return ret; 45 | } 46 | 47 | /* We want the user data to be accessible from all processes, but some processes (for 48 | example, Chrome's sub-processes) have a very strict sandbox profile so putting the 49 | data in the user's home folder won't always work. /usr/lib, on the other hand, is 50 | accessible from all processes (Otherwise they're pretty much worthless to hook in 51 | the first place), so we put our data in /usr/lib/mip/user_data//. We also set 52 | a symlink in the user's Library folder, for easier access. */ 53 | static void create_user_data_folder(pid_t pid) 54 | { 55 | struct proc_bsdinfo proc; 56 | proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &proc, sizeof(proc)); 57 | 58 | char path[PATH_MAX]; 59 | sprintf(path, "%s/user_data/%d", MIP_root_path(), proc.pbi_uid); 60 | 61 | if (mkdir(path, 0755)) return; // Might already exist 62 | chown(path, proc.pbi_uid, proc.pbi_gid); 63 | 64 | struct passwd *pw = getpwuid(proc.pbi_uid); 65 | char library_path[PATH_MAX]; 66 | if (snprintf(library_path, sizeof(library_path), "%s/Library/MIP", pw->pw_dir) < sizeof(library_path)) { 67 | symlink(path, library_path); 68 | lchown(library_path, proc.pbi_uid, proc.pbi_gid); 69 | } 70 | } 71 | 72 | static void handleClientMessageHook_common(uint64_t command, xpc_object_t dict) 73 | { 74 | /* Command 500 is sent by all GUI processes to launchservicesd when they launch. 75 | The process will block until it receives an answer from launchservicesd. 76 | We will inject our code before we send a reply. Once we send the actual reply, 77 | by calling the original method, the process resumes, runs the injected dlopen, 78 | and continues normal execution. */ 79 | 80 | if (command == 500) { 81 | audit_token_t token; 82 | xpc_dictionary_get_audit_token(dict, &token); 83 | pid_t pid = audit_token_to_pid(token); 84 | 85 | /* While strictly speaking this should be loader's responsibility to create this folder, 86 | this function must run as root, so it is done by the injector. */ 87 | create_user_data_folder(pid); 88 | inject_to_pid(pid, MIP_loader_path(), false); 89 | } 90 | } 91 | 92 | static uint64_t xpc_dictionary_get_int64_hook(xpc_object_t dict, const char *key) 93 | { 94 | uint64_t ret = xpc_dictionary_get_int64(dict, key); 95 | if (strcmp(key, "command") == 0) { 96 | handleClientMessageHook_common(ret, dict); 97 | } 98 | return ret; 99 | } 100 | 101 | #if __arm64__ 102 | 103 | // Derived from code by khanhduytran0 { 104 | #define _COMM_PAGE_START_ADDRESS (0x0000000FFFFFC000ULL) 105 | #define _COMM_PAGE_TPRO_WRITE_ENABLE (_COMM_PAGE_START_ADDRESS + 0x0D0) 106 | #define _COMM_PAGE_TPRO_WRITE_DISABLE (_COMM_PAGE_START_ADDRESS + 0x0D8) 107 | 108 | static bool os_tpro_is_supported(void) 109 | { 110 | if (*(uint64_t*)_COMM_PAGE_TPRO_WRITE_ENABLE) { 111 | return true; 112 | } 113 | return false; 114 | } 115 | 116 | __attribute__((naked)) bool os_thread_self_tpro_is_writeable(void) 117 | { 118 | __asm__ __volatile__ ( 119 | "mrs x0, s3_6_c15_c1_5\n" 120 | "ubfx x0, x0, #0x24, #1;\n" 121 | "ret\n" 122 | ); 123 | } 124 | 125 | void os_thread_self_restrict_tpro_to_rw(void) 126 | { 127 | __asm__ __volatile__ ( 128 | "mov x0, %0\n" 129 | "ldr x0, [x0]\n" 130 | "msr s3_6_c15_c1_5, x0\n" 131 | "isb sy\n" 132 | :: "r" (_COMM_PAGE_TPRO_WRITE_ENABLE) 133 | : "memory", "x0" 134 | ); 135 | return; 136 | } 137 | 138 | void os_thread_self_restrict_tpro_to_ro(void) 139 | { 140 | __asm__ __volatile__ ( 141 | "mov x0, %0\n" 142 | "ldr x0, [x0]\n" 143 | "msr s3_6_c15_c1_5, x0\n" 144 | "isb sy\n" 145 | :: "r" (_COMM_PAGE_TPRO_WRITE_DISABLE) 146 | : "memory", "x0" 147 | ); 148 | return; 149 | } 150 | 151 | // } 152 | 153 | static void unprotect_page(void *page) 154 | { 155 | if (mprotect(page, 0x4000, PROT_READ | PROT_WRITE) == 0) { 156 | return; 157 | } 158 | void *temp = malloc(0x4000); 159 | memcpy(temp, page, 0x4000); 160 | vm_deallocate(mach_task_self(), (vm_address_t)page, 0x4000); 161 | vm_allocate(mach_task_self(), (vm_address_t *)&page, 0x4000, VM_FLAGS_FIXED); 162 | memcpy(page, temp, 0x4000); 163 | free(temp); 164 | } 165 | #endif 166 | 167 | static void __attribute__((constructor)) hook_lsd(void) 168 | { 169 | struct mach_header_64 *header = (typeof (header))_dyld_get_image_header(0); 170 | const struct load_command *cmd = (typeof(cmd))(header + 1); 171 | const struct segment_command_64 *first = NULL; 172 | const struct segment_command_64 *data = NULL; 173 | for (unsigned i = 0; i < header->ncmds; i++, cmd = (typeof(cmd)) ((char*) cmd + cmd->cmdsize)) { 174 | if (cmd->cmd == LC_SEGMENT_64) { 175 | if (!first && ((typeof(first))cmd)->filesize ) { 176 | first = (typeof(first)) cmd; 177 | } 178 | #ifndef __arm64__ 179 | if (strcmp(((typeof(data))cmd)->segname, "__DATA") == 0) { 180 | #else 181 | if (strcmp(((typeof(data))cmd)->segname, "__DATA_CONST") == 0) { 182 | #endif 183 | data = (typeof(data))cmd; 184 | break; 185 | } 186 | } 187 | } 188 | uintptr_t slide = (uintptr_t)header - first->vmaddr; 189 | void **address = (void **)(data->vmaddr + slide); 190 | void **end = (void **)(data->vmaddr + data->vmsize + slide); 191 | #if __arm64__ 192 | bool has_tpro = os_tpro_is_supported(); 193 | #endif 194 | while (address < end) { 195 | #if __arm64__ 196 | if ((uintptr_t)__builtin_ptrauth_strip(*address, 0) == (uintptr_t)__builtin_ptrauth_strip(&xpc_dictionary_get_int64, 0)) { 197 | bool revert_tpro_state = false; 198 | if (!has_tpro) { 199 | unprotect_page((void *)(((uintptr_t)address) & ~0x3FFF)); 200 | } 201 | else if (!os_thread_self_tpro_is_writeable()) { 202 | os_thread_self_restrict_tpro_to_rw(); 203 | revert_tpro_state = true; 204 | } 205 | *address = ptrauth_sign_unauthenticated((void *)((uintptr_t)__builtin_ptrauth_strip(&xpc_dictionary_get_int64_hook, 0)), ptrauth_key_function_pointer, address); 206 | if (revert_tpro_state) { 207 | os_thread_self_restrict_tpro_to_ro(); 208 | } 209 | } 210 | #else 211 | if (*address == xpc_dictionary_get_int64) { 212 | *address = xpc_dictionary_get_int64_hook; 213 | } 214 | #endif 215 | address++; 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /MIP/injector/payloads/injected.h: -------------------------------------------------------------------------------- 1 | #ifndef injected_x86_h 2 | #define injected_x86_h 3 | 4 | #define DYLD_MAGIC_32 ('DYLD') 5 | #define DYLD_MAGIC_64 ('DYLD'* 0x100000001) 6 | #define ARGUMENT_MAGIC_STR "ARGUMENT" 7 | #define ARGUMENT_MAX_LENGTH 256 8 | 9 | extern char injected_arm_start __asm("section$start$__INJ_arm64e$__inj_arm64e"); 10 | extern char injected_arm_end __asm("section$end$__INJ_arm64e$__inj_arm64e"); 11 | extern char injected_x86_64_start __asm("section$start$__INJ_x86_64$__inj_x86_64"); 12 | extern char injected_x86_64_end __asm("section$end$__INJ_x86_64$__inj_x86_64"); 13 | extern char injected_i386_start __asm("section$start$__INJ_i386$__inj_i386"); 14 | extern char injected_i386_end __asm("section$end$__INJ_i386$__inj_i386"); 15 | 16 | #endif /* injected_x86_h */ 17 | -------------------------------------------------------------------------------- /MIP/injector/payloads/injected_arm64e.c: -------------------------------------------------------------------------------- 1 | #include "injected.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* These values are overwritten on a per-injection basis by the injector */ 9 | /* Must not be defined as consts, or the compiler will optimize them. */ 10 | __attribute__((section("__TEXT,__const"))) struct dyld_all_image_infos *dyld_info = (typeof(dyld_info)) DYLD_MAGIC_64; 11 | 12 | __attribute__((section("__TEXT,__const"))) char argument[ARGUMENT_MAX_LENGTH] = ARGUMENT_MAGIC_STR; 13 | 14 | static const struct mach_header_64 *get_header_by_path(const char *name) 15 | { 16 | for (unsigned i = 0; i < dyld_info->infoArrayCount; i++) { 17 | if (strcmp(dyld_info->infoArray[i].imageFilePath, name) == 0) { 18 | return (const struct mach_header_64 *) dyld_info->infoArray[i].imageLoadAddress; 19 | } 20 | } 21 | return NULL; 22 | } 23 | 24 | static const void *get_symbol_from_header(const struct mach_header_64 *header, const char *symbol) 25 | { 26 | if (!header) { 27 | return NULL; 28 | } 29 | 30 | /* Get the required commands */ 31 | 32 | const struct symtab_command *symtab = NULL; 33 | const struct segment_command_64 *first = NULL; 34 | const struct segment_command_64 *linkedit = NULL; 35 | const struct load_command *cmd = (typeof(cmd))(header + 1); 36 | 37 | for (unsigned i = 0; i < header->ncmds; i++, cmd = (typeof(cmd)) ((char*) cmd + cmd->cmdsize)) { 38 | if (cmd->cmd == LC_SEGMENT_64) { 39 | if (!first && ((typeof(first))cmd)->filesize ) { 40 | first = (typeof(first)) cmd; 41 | } 42 | if (strcmp(((typeof(linkedit)) cmd)->segname, "__LINKEDIT") == 0) { 43 | linkedit = (typeof(linkedit)) cmd; 44 | } 45 | } 46 | else if (cmd->cmd == LC_SYMTAB) { 47 | symtab = (typeof (symtab)) cmd; 48 | } 49 | if (symtab && linkedit) break; 50 | } 51 | 52 | if (!symtab || !linkedit) return NULL; 53 | 54 | const char *string_table = 55 | ((const char *) header + symtab->stroff - linkedit->fileoff + linkedit->vmaddr - first->vmaddr); 56 | const struct nlist_64 *symbols = (typeof (symbols)) 57 | ((const char *) header + symtab->symoff - linkedit->fileoff + linkedit->vmaddr - first->vmaddr); 58 | 59 | for (unsigned i = 0; i < symtab->nsyms; i++) { 60 | if (strcmp(string_table + symbols[i].n_un.n_strx, symbol) == 0) { 61 | return (char *)header + symbols[i].n_value - first->vmaddr; 62 | } 63 | } 64 | 65 | return NULL; 66 | } 67 | 68 | void c_entry(void) 69 | { 70 | typeof(dlopen) *$dlopen = NULL; 71 | 72 | /* We can't call dyld`dlopen when dyld3 is being used, so we must find libdyld`dlopen and call that instead */ 73 | $dlopen = get_symbol_from_header(get_header_by_path("/usr/lib/system/libdyld.dylib"), "_dlopen"); 74 | $dlopen = ptrauth_sign_unauthenticated($dlopen, ptrauth_key_function_pointer, 0); 75 | 76 | 77 | if ($dlopen) { 78 | $dlopen((const char *)argument, RTLD_NOW); 79 | } 80 | } 81 | 82 | void __attribute__((naked)) entry(void) 83 | { 84 | __asm__( 85 | "and x29, x29, 0xFFFFFFFFFFF\n" // Strip PAC error bit set by thread_set_state 86 | "stp x0, x1, [sp,#-16]!\n" 87 | "stp x2, x3, [sp,#-16]!\n" 88 | "stp x4, x5, [sp,#-16]!\n" 89 | "stp x6, x7, [sp,#-16]!\n" 90 | "stp x8, x9, [sp,#-16]!\n" 91 | "stp x10, x11, [sp,#-16]!\n" 92 | "stp x12, x13, [sp,#-16]!\n" 93 | "stp x14, x15, [sp,#-16]!\n" 94 | "stp x16, x17, [sp,#-16]!\n" 95 | "stp x18, x19, [sp,#-16]!\n" 96 | "stp x20, x21, [sp,#-16]!\n" 97 | "stp x22, x23, [sp,#-16]!\n" 98 | "stp x24, x25, [sp,#-16]!\n" 99 | "stp x26, x27, [sp,#-16]!\n" 100 | "stp x28, x29, [sp,#-16]!\n" 101 | "stp q0, q1, [sp,#-32]!\n" 102 | "stp q2, q3, [sp,#-32]!\n" 103 | "stp q4, q5, [sp,#-32]!\n" 104 | "stp q6, q7, [sp,#-32]!\n" 105 | "stp q8, q9, [sp,#-32]!\n" 106 | "stp q10, q11, [sp,#-32]!\n" 107 | "stp q12, q13, [sp,#-32]!\n" 108 | "stp q14, q15, [sp,#-32]!\n" 109 | "stp q16, q17, [sp,#-32]!\n" 110 | "stp q18, q19, [sp,#-32]!\n" 111 | "stp q20, q21, [sp,#-32]!\n" 112 | "stp q22, q23, [sp,#-32]!\n" 113 | "stp q24, q25, [sp,#-32]!\n" 114 | "stp q26, q27, [sp,#-32]!\n" 115 | "stp q28, q29, [sp,#-32]!\n" 116 | "stp q30, q31, [sp,#-32]!\n" 117 | "mrs x0, nzcv\n" 118 | "stp x0, lr, [sp,#-16]!\n" 119 | "bl _c_entry\n" 120 | "ldp x0, lr, [sp], #16\n" 121 | "msr nzcv, x0\n" 122 | "ldp q30, q31, [sp], #32\n" 123 | "ldp q28, q29, [sp], #32\n" 124 | "ldp q26, q27, [sp], #32\n" 125 | "ldp q24, q25, [sp], #32\n" 126 | "ldp q22, q23, [sp], #32\n" 127 | "ldp q20, q21, [sp], #32\n" 128 | "ldp q18, q19, [sp], #32\n" 129 | "ldp q16, q17, [sp], #32\n" 130 | "ldp q14, q15, [sp], #32\n" 131 | "ldp q12, q13, [sp], #32\n" 132 | "ldp q10, q11, [sp], #32\n" 133 | "ldp q8, q9, [sp], #32\n" 134 | "ldp q6, q7, [sp], #32\n" 135 | "ldp q4, q5, [sp], #32\n" 136 | "ldp q2, q3, [sp], #32\n" 137 | "ldp q0, q1, [sp], #32\n" 138 | "ldp x28, x29, [sp], #16\n" 139 | "ldp x26, x27, [sp], #16\n" 140 | "ldp x24, x25, [sp], #16\n" 141 | "ldp x22, x23, [sp], #16\n" 142 | "ldp x20, x21, [sp], #16\n" 143 | "ldp x18, x19, [sp], #16\n" 144 | "ldp x16, x17, [sp], #16\n" 145 | "ldp x14, x15, [sp], #16\n" 146 | "ldp x12, x13, [sp], #16\n" 147 | "ldp x10, x11, [sp], #16\n" 148 | "ldp x8, x9, [sp], #16\n" 149 | "ldp x6, x7, [sp], #16\n" 150 | "ldp x4, x5, [sp], #16\n" 151 | "ldp x2, x3, [sp], #16\n" 152 | "ldp x0, x1, [sp], #16\n" 153 | "br x29\n" 154 | ); 155 | } 156 | 157 | 158 | /* Taken from Apple's libc */ 159 | 160 | int strcmp(const char *s1, const char *s2) 161 | { 162 | while (*s1 == *s2++) 163 | if (*s1++ == 0) 164 | return (0); 165 | return (*(const unsigned char *)s1 - *(const unsigned char *)(s2 - 1)); 166 | } 167 | -------------------------------------------------------------------------------- /MIP/injector/payloads/injected_i386.c: -------------------------------------------------------------------------------- 1 | #include "injected.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* These values are overwritten on a per-injection basis by the injector. */ 9 | /* Must not be defined as consts, or the compiler will optimize them. */ 10 | __attribute__((section("__TEXT,__const"))) struct dyld_all_image_infos *dyld_info = (typeof(dyld_info)) DYLD_MAGIC_32; 11 | 12 | __attribute__((section("__TEXT,__const"))) char argument[ARGUMENT_MAX_LENGTH] = ARGUMENT_MAGIC_STR; 13 | 14 | /* The interrupt calling convention does not save the flags register, so we use 15 | these ASM functions to save and restore it outselves. They must be called as 16 | early and as late as possible, respectively, so instructions that modify flags 17 | won't destory the state. */ 18 | 19 | uint32_t get_flags(void); 20 | /* Use fastcall for set_flags, parameters on registers are easier to work with. */ 21 | __attribute__((fastcall)) void set_flags(uint32_t); 22 | 23 | __asm__ ( 24 | "_get_flags: \n" 25 | " pushfd \n" 26 | " pop %eax \n" 27 | " ret \n" 28 | 29 | "_set_flags: \n" 30 | " push %ecx \n" 31 | " popfd \n" 32 | " ret \n" 33 | ); 34 | 35 | static const struct mach_header *get_header_by_path(const char *name) 36 | { 37 | for (unsigned i = 0; i < dyld_info->infoArrayCount; i++) { 38 | if (strcmp(dyld_info->infoArray[i].imageFilePath, name) == 0) { 39 | return (const struct mach_header *) dyld_info->infoArray[i].imageLoadAddress; 40 | } 41 | } 42 | return NULL; 43 | } 44 | 45 | static const void *get_symbol_from_header(const struct mach_header *header, const char *symbol) 46 | { 47 | if (!header) { 48 | return NULL; 49 | } 50 | 51 | /* Get the required commands */ 52 | 53 | const struct symtab_command *symtab = NULL; 54 | const struct segment_command *first = NULL; 55 | const struct segment_command *linkedit = NULL; 56 | const struct load_command *cmd = (typeof(cmd))(header + 1); 57 | 58 | for (unsigned i = 0; i < header->ncmds; i++, cmd = (typeof(cmd)) ((char*) cmd + cmd->cmdsize)) { 59 | if (cmd->cmd == LC_SEGMENT) { 60 | if (!first && ((typeof(first))cmd)->filesize ) { 61 | first = (typeof(first)) cmd; 62 | } 63 | if (strcmp(((typeof(linkedit)) cmd)->segname, "__LINKEDIT") == 0) { 64 | linkedit = (typeof(linkedit)) cmd; 65 | } 66 | } 67 | else if (cmd->cmd == LC_SYMTAB) { 68 | symtab = (typeof (symtab)) cmd; 69 | } 70 | if (symtab && linkedit) break; 71 | } 72 | 73 | if (!symtab || !linkedit) return NULL; 74 | 75 | const char *string_table = 76 | ((const char *) header + symtab->stroff - linkedit->fileoff + linkedit->vmaddr - first->vmaddr); 77 | const struct nlist *symbols = (typeof (symbols)) 78 | ((const char *) header + symtab->symoff - linkedit->fileoff + linkedit->vmaddr - first->vmaddr); 79 | 80 | for (unsigned i = 0; i < symtab->nsyms; i++) { 81 | if (strcmp(string_table + symbols[i].n_un.n_strx, symbol) == 0) { 82 | return (char *)header + symbols[i].n_value - first->vmaddr; 83 | } 84 | } 85 | 86 | return NULL; 87 | } 88 | 89 | 90 | void _entry() 91 | { 92 | uint32_t flags = get_flags(); 93 | 94 | typeof(dlopen) *$dlopen = NULL; 95 | 96 | /* We can't call dyld`dlopen when dyld3 is being used, so we must find libdyld`dlopen and call that instead */ 97 | $dlopen = get_symbol_from_header(get_header_by_path("/usr/lib/system/libdyld.dylib"), "_dlopen"); 98 | 99 | if ($dlopen) { 100 | $dlopen(argument, RTLD_NOW); 101 | } 102 | 103 | set_flags(flags); 104 | } 105 | 106 | /* Clang on x86-32 does not support the preserve_all convention. Instead, we use 107 | * the interrupt attribute. The Makefile takes care of replacing iret with ret. 108 | Additionally, Clang sometimes stores xmm7 on an unaligned address and crashes, 109 | and since LLVM's bug tracker has been down for ages, the Makefile fixes that 110 | as well. -_- */ 111 | void __attribute__((interrupt)) entry(void * __attribute__((unused)) unused) 112 | { 113 | _entry(); 114 | } 115 | 116 | /* Taken from Apple's libc */ 117 | 118 | int strcmp(const char *s1, const char *s2) 119 | { 120 | while (*s1 == *s2++) 121 | if (*s1++ == 0) 122 | return (0); 123 | return (*(const unsigned char *)s1 - *(const unsigned char *)(s2 - 1)); 124 | } -------------------------------------------------------------------------------- /MIP/injector/payloads/injected_x86_64.c: -------------------------------------------------------------------------------- 1 | #include "injected.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* These values are overwritten on a per-injection basis by the injector */ 9 | /* Must not be defined as consts, or the compiler will optimize them. */ 10 | __attribute__((section("__TEXT,__const"))) struct dyld_all_image_infos *dyld_info = (typeof(dyld_info)) DYLD_MAGIC_64; 11 | 12 | __attribute__((section("__TEXT,__const"))) char argument[ARGUMENT_MAX_LENGTH] = ARGUMENT_MAGIC_STR; 13 | 14 | /* The preserve_all calling convention does not save the flags register, so we use 15 | these ASM functions to save and restore it outselves. They must be called as 16 | early and as late as possible, respectively, so instructions that modify flags 17 | won't destory the state. */ 18 | 19 | static const struct mach_header_64 *get_header_by_path(const char *name) 20 | { 21 | for (unsigned i = 0; i < dyld_info->infoArrayCount; i++) { 22 | if (strcmp(dyld_info->infoArray[i].imageFilePath, name) == 0) { 23 | return (const struct mach_header_64 *) dyld_info->infoArray[i].imageLoadAddress; 24 | } 25 | } 26 | return NULL; 27 | } 28 | 29 | static const void *get_symbol_from_header(const struct mach_header_64 *header, const char *symbol) 30 | { 31 | if (!header) { 32 | return NULL; 33 | } 34 | 35 | /* Get the required commands */ 36 | 37 | const struct symtab_command *symtab = NULL; 38 | const struct segment_command_64 *first = NULL; 39 | const struct segment_command_64 *linkedit = NULL; 40 | const struct load_command *cmd = (typeof(cmd))(header + 1); 41 | 42 | for (unsigned i = 0; i < header->ncmds; i++, cmd = (typeof(cmd)) ((char*) cmd + cmd->cmdsize)) { 43 | if (cmd->cmd == LC_SEGMENT_64) { 44 | if (!first && ((typeof(first))cmd)->filesize ) { 45 | first = (typeof(first)) cmd; 46 | } 47 | if (strcmp(((typeof(linkedit)) cmd)->segname, "__LINKEDIT") == 0) { 48 | linkedit = (typeof(linkedit)) cmd; 49 | } 50 | } 51 | else if (cmd->cmd == LC_SYMTAB) { 52 | symtab = (typeof (symtab)) cmd; 53 | } 54 | if (symtab && linkedit) break; 55 | } 56 | 57 | if (!symtab || !linkedit) return NULL; 58 | 59 | const char *string_table = 60 | ((const char *) header + symtab->stroff - linkedit->fileoff + linkedit->vmaddr - first->vmaddr); 61 | const struct nlist_64 *symbols = (typeof (symbols)) 62 | ((const char *) header + symtab->symoff - linkedit->fileoff + linkedit->vmaddr - first->vmaddr); 63 | 64 | for (unsigned i = 0; i < symtab->nsyms; i++) { 65 | if (strcmp(string_table + symbols[i].n_un.n_strx, symbol) == 0) { 66 | return (char *)header + symbols[i].n_value - first->vmaddr; 67 | } 68 | } 69 | 70 | return NULL; 71 | } 72 | 73 | #ifdef ROSETTA 74 | void __attribute__((naked)) late_inject(void) 75 | { 76 | __asm__ ("push %rsp\n" 77 | "push %r11\n" 78 | "pushfq\n" 79 | "call _c_late_inject\n" 80 | "popfq\n" 81 | "pop %r11\n" 82 | "ret"); 83 | } 84 | 85 | /* In Rosetta, __TEXT segments are RWX, so we can put our data in __TEXT, 86 | and to properly generate payloads, everything must be in one segment. */ 87 | static __attribute__((section("__TEXT,__data"))) uintptr_t *stack_ret = NULL; 88 | static __attribute__((section("__TEXT,__data"))) uintptr_t ret_address = 0; 89 | 90 | void __attribute__((preserve_all)) c_late_inject(void) 91 | { 92 | *stack_ret = ret_address; 93 | typeof(dlopen) *$dlopen = NULL; 94 | $dlopen = get_symbol_from_header(get_header_by_path("/usr/lib/system/libdyld.dylib"), "_dlopen"); 95 | 96 | if ($dlopen) { 97 | $dlopen(argument, RTLD_NOW); 98 | } 99 | } 100 | #endif 101 | 102 | void __attribute__((preserve_all)) c_entry(void) 103 | { 104 | #ifdef ROSETTA 105 | /* 106 | For some reason, calling `dlopen` from the usual `lsdinjector` context, 107 | specifically while using the Rosetta runtime, some Mach port connection 108 | gets screwed up, which ends up crashing the next time it is used. If we 109 | detect this scenario, we inject the dlopen call to after we return to 110 | _LSApplicationCheckIn, which is safe. 111 | */ 112 | uintptr_t _LSApplicationCheckIn = (uintptr_t) get_symbol_from_header( 113 | get_header_by_path("/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices"), 114 | "__LSApplicationCheckIn"); 115 | 116 | if (_LSApplicationCheckIn) { 117 | uintptr_t *stack = __builtin_frame_address(4); 118 | for (unsigned i = 0; i < 128; i++) { 119 | /* TODO: This will work for lsdinjector, but this can probably be improved. 120 | It might have false positives (and even crashes) on some manual `inject` 121 | scenarios. */ 122 | if (stack[i] > _LSApplicationCheckIn && stack[i] < _LSApplicationCheckIn + 8192) { 123 | ret_address = stack[i]; 124 | stack_ret = &stack[i]; 125 | stack[i] = (uintptr_t)&late_inject; 126 | return; 127 | } 128 | } 129 | } 130 | #endif 131 | 132 | typeof(dlopen) *$dlopen = NULL; 133 | 134 | /* We can't call dyld`dlopen when dyld3 is being used, so we must find libdyld`dlopen and call that instead */ 135 | $dlopen = get_symbol_from_header(get_header_by_path("/usr/lib/system/libdyld.dylib"), "_dlopen"); 136 | 137 | if ($dlopen) { 138 | $dlopen(argument, RTLD_NOW); 139 | } 140 | } 141 | 142 | void __attribute__((naked)) entry(void) 143 | { 144 | __asm__ ("push %rax\n" // Alignment dummy 145 | "push %r11\n" 146 | "pushfq\n" 147 | "call _c_entry\n" 148 | "popfq\n" 149 | "pop %r11\n" 150 | "pop %rax\n" // Alignment dummy 151 | "ret\n"); 152 | } 153 | 154 | 155 | /* Taken from Apple's libc */ 156 | 157 | int strcmp(const char *s1, const char *s2) 158 | { 159 | while (*s1 == *s2++) 160 | if (*s1++ == 0) 161 | return (0); 162 | return (*(const unsigned char *)s1 - *(const unsigned char *)(s2 - 1)); 163 | } 164 | -------------------------------------------------------------------------------- /MIP/injector/payloads/order: -------------------------------------------------------------------------------- 1 | _entry -------------------------------------------------------------------------------- /MIP/libSystem.tbd: -------------------------------------------------------------------------------- 1 | --- !tapi-tbd 2 | tbd-version: 4 3 | targets: [ i386-macos ] 4 | install-name: '/usr/lib/libSystem.B.dylib' 5 | exports: 6 | - targets: [ i386-macos ] 7 | symbols: [ ] 8 | ... 9 | -------------------------------------------------------------------------------- /MIP/loader/loader.h: -------------------------------------------------------------------------------- 1 | #include "loader_public.h" 2 | -------------------------------------------------------------------------------- /MIP/loader/loader.m: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "loader.h" 9 | 10 | #define objc_collectingEnabled() (@selector(retain) == @selector(release)) 11 | 12 | 13 | /* Exported so they can be used by loaded bundles */ 14 | const char *MIP_loader_path(void) 15 | { 16 | Dl_info info; 17 | dladdr("", &info); // Get own info 18 | return info.dli_fname; 19 | } 20 | 21 | const char *MIP_root_path(void) 22 | { 23 | static char *ret = NULL; 24 | if (ret) return ret; 25 | 26 | ret = strdup(MIP_loader_path()); 27 | *strrchr(ret, '/') = 0; 28 | return ret; 29 | } 30 | 31 | const char *MIP_user_data_path(void) 32 | { 33 | static char user_data_path[PATH_MAX] = ""; 34 | 35 | if (!user_data_path[0]) { 36 | sprintf(user_data_path, "%s/user_data/%d", MIP_root_path(), getuid()); 37 | } 38 | return user_data_path; 39 | } 40 | 41 | bool should_inject_bundle(NSBundle *tweakBundle, 42 | NSString *mainExecutableName, 43 | NSArray *globalyDisabledBundles) 44 | { 45 | NSDictionary *plist = tweakBundle.infoDictionary; 46 | bool blacklistMode = [plist[@"MIPUseBlacklistMode"] boolValue]; 47 | 48 | // Skip tweak if it is globally disabled 49 | if ([globalyDisabledBundles containsObject:tweakBundle.bundleIdentifier]) { 50 | // Not affected by blacklist mode 51 | return false; 52 | } 53 | 54 | // Skip tweak if a matching bundle is excluded 55 | for (NSString *entry in plist[@"MIPExcludedBundleNames"]) { 56 | if (CFBundleGetBundleWithIdentifier((CFStringRef)entry)) { 57 | // Match found; skip loading 58 | return false; 59 | } 60 | } 61 | 62 | // Check if process matches bundle filter 63 | for (NSString *entry in plist[@"MIPBundleNames"]) { 64 | if (CFBundleGetBundleWithIdentifier((CFStringRef)entry)) { 65 | // Match found; invert if blacklist mode enabled 66 | return !blacklistMode; 67 | } 68 | } 69 | 70 | // Check if process matches executable filter 71 | for (NSString *entry in plist[@"MIPExecutableNames"]) { 72 | if ([mainExecutableName isEqualToString:entry]) { 73 | // Match found; invert if blacklist mode enabled 74 | return !blacklistMode; 75 | } 76 | } 77 | 78 | // No match find, return true if we're in blacklist mode 79 | return blacklistMode; 80 | } 81 | 82 | static void __attribute__((constructor)) loader(void) 83 | { 84 | @autoreleasepool { 85 | @try { 86 | NSDictionary *user_preferences = [NSDictionary dictionaryWithContentsOfFile: 87 | [@(MIP_user_data_path()) stringByAppendingPathComponent:@"settings.plist"] 88 | ]; 89 | 90 | NSFileManager *fm = NSFileManager.defaultManager; 91 | 92 | NSURL *tweakBundlesURL = [NSURL fileURLWithPath:[@(MIP_root_path()) stringByAppendingPathComponent:@"Bundles"]]; 93 | NSArray *tweakBundles = [fm contentsOfDirectoryAtURL:tweakBundlesURL 94 | includingPropertiesForKeys:nil 95 | options:NSDirectoryEnumerationSkipsHiddenFiles 96 | error:nil]; 97 | 98 | char executable_path[PATH_MAX]; 99 | uint32_t executable_path_length = sizeof(executable_path); 100 | _NSGetExecutablePath(executable_path, &executable_path_length); 101 | 102 | NSString *executable_name = @(executable_path).lastPathComponent; 103 | NSArray *disabled_bundles = user_preferences[@"MIPDisabledBundles"]; 104 | 105 | // Enumerate tweak bundles and determine whether or not to load each 106 | for (NSURL *bundle_url in tweakBundles) { 107 | 108 | NSBundle *tweakBundle = [NSBundle bundleWithURL:bundle_url]; 109 | bool should_inject = should_inject_bundle(tweakBundle, 110 | executable_name, 111 | disabled_bundles); 112 | 113 | if (should_inject) { 114 | bool tweakSupportsGC = [tweakBundle.infoDictionary[@"MIPSupportsGC"] boolValue]; 115 | if (objc_collectingEnabled() && !tweakSupportsGC) { 116 | // Skip loading if tweak doesn't support GC 117 | NSLog(@"MIP: Bundle %@ was not loaded: %s required GC", 118 | tweakBundle.bundlePath, executable_path); 119 | } 120 | else { 121 | // Attempt loading tweak bundle 122 | NSError *error = nil; 123 | if (![tweakBundle loadAndReturnError:&error]) { 124 | NSLog(@"MIP: Bundle %@ was not loaded: %@", tweakBundle.bundlePath, error); 125 | } 126 | } 127 | } 128 | } 129 | } 130 | @catch (NSException *exception) { 131 | NSLog(@"MIP: Aborting load due to exception: %@\n%@", 132 | exception, exception.callStackSymbols); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /MIP/loader/loader_public.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | const char *MIP_loader_path(void); 4 | const char *MIP_root_path(void); 5 | const char *MIP_user_data_path(void); 6 | 7 | typedef const void *MSImageRef; 8 | MSImageRef MSGetImageByName(const char *file); 9 | void *MSFindSymbol(MSImageRef image, const char *name); 10 | 11 | void MSHookFunction(void *symbol, void *replace, void **result); 12 | 13 | void MSHookMemory(void *target, const void *data, size_t size); 14 | 15 | void MSHookMessageEx(Class _class, SEL sel, IMP imp, IMP *result); 16 | -------------------------------------------------------------------------------- /MIP/local.injectd.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | EnablePressuredExit 6 | 7 | EnableTransactions 8 | 9 | Label 10 | local.injectd 11 | MachServices 12 | 13 | local.injectd 14 | 15 | 16 | POSIXSpawnType 17 | Adaptive 18 | ProgramArguments 19 | 20 | @MIP_ROOT@/injectd 21 | 22 | ThrottleInterval 23 | 1 24 | 25 | 26 | -------------------------------------------------------------------------------- /MIP/local.lsdinjector.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GroupName 6 | wheel 7 | KeepAlive 8 | 9 | SuccessfulExit 10 | 11 | 12 | Label 13 | local.lsdinjector 14 | ProgramArguments 15 | 16 | /usr/local/bin/inject 17 | launchservicesd 18 | @MIP_ROOT@/lsdinjector.dylib 19 | -w4 20 | 21 | RunAtLoad 22 | 23 | ThrottleInterval 24 | 1 25 | UserName 26 | root 27 | 28 | 29 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Some files in this repository contain their own licensing info in a header: 2 | substrate.h, which is based on an older version of CydiaSubstrate, is under 3 | LGPLv3, and substitute.h and the generated files are in the public domain. (So 4 | if you want to take advantage of the more lax permission below, you can't use 5 | the Substrate compatibility layer; also, the Debian package includes the 6 | obligatory copy of the (L)GPLv3.) 7 | 8 | For all other files: 9 | 10 | Copyright Nicholas Allegra (comex) 11 | 12 | This library is free software; you can redistribute it and/or modify it under 13 | the terms of the GNU Lesser General Public License as published by the Free 14 | Software Foundation; either version 2.1 of the License, or (at your option) any 15 | later version. 16 | 17 | ** You may optionally consider the license amended with the following special 18 | exception: When statically linking, it is not necessary to include materials 19 | specifically for relinking with a modified version of the library (i.e. 20 | subsection 6a of the LGPLv2.1, or subsection 4d0 of version 3.0). 21 | 22 | This library is distributed in the hope that it will be useful, but WITHOUT ANY 23 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 24 | PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 25 | details. 26 | 27 | You should not have received a copy of the GNU Lesser General Public License 28 | along with this library, as it can easily be found on the World Wide Web. If, 29 | due to a temporal anomaly, you are trapped in some time period after 1999 (the 30 | release date of the LGPL v2.1) but before 1991 (the debut of the Web), you may 31 | get a copy by writing to the Free Software Foundation, Inc., 51 Franklin 32 | Street, Fifth Floor, Boston, MA 02110-1301 USA. For all other users, not 33 | having to distribute all 5,000 words of the thing may be considered another 34 | optional license exception. 35 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/generated/darwin-inject-asm.S: -------------------------------------------------------------------------------- 1 | /* Generated by script/gen-inject-asm.sh. The relevant source is in-tree (make 2 | * out/darwin-inject-asm.S), but this file has been checked in too, in case 3 | * your C compiler doesn't support all of these architectures. 4 | * This file contains code for 4 architectures in one text page; it's remapped 5 | * into the target process and the appropriate thunk executed. Having ARM code 6 | * here on x86 and whatnot is currently pointless (and use of that code is 7 | * disabled in case any future Rosetta-like emulator breaks naive attempts to 8 | * inject into foreign-architecture processes), but we need two architectures 9 | * anyway, so the rest are included in case doing so is useful someday. */ 10 | .align 14 11 | .private_extern _inject_page_start 12 | _inject_page_start: 13 | .align 2 14 | .private_extern _inject_start_x86_64 15 | _inject_start_x86_64: 16 | .byte 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x30, 0x48, 0x8d, 0x45, 0xf4, 0x31, 0xc9, 0x89, 0xce, 0x48, 0x8d, 0x15, 0x99, 0x00, 0x00, 0x00, 0x48, 0x89, 0x7d, 0xf8, 0xc7, 0x45, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x7d, 0xf8, 0x48, 0x8b, 0x3f, 0x4c, 0x8b, 0x45, 0xf8, 0x48, 0x89, 0x7d, 0xe8, 0x48, 0x89, 0xc7, 0x4c, 0x89, 0xc1, 0x48, 0x8b, 0x45, 0xe8, 0xff, 0xd0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0x8b, 0x49, 0x08, 0x8b, 0x7d, 0xf4, 0x89, 0x45, 0xe4, 0xff, 0xd1, 0x31, 0xff, 0x89, 0xf9, 0x31, 0xd2, 0x48, 0x8b, 0x75, 0xf8, 0x48, 0x8b, 0x76, 0x30, 0x89, 0xf7, 0x89, 0x7d, 0xe0, 0x48, 0x89, 0xcf, 0x48, 0x89, 0xce, 0x8b, 0x4d, 0xe0, 0x89, 0x45, 0xdc, 0xe8, 0x1f, 0x00, 0x00, 0x00, 0xb9, 0xad, 0x0b, 0x00, 0x00, 0x89, 0xce, 0x89, 0x45, 0xd8, 0xb0, 0x00, 0xff, 0xd6, 0x48, 0x83, 0xc4, 0x30, 0x5d, 0xc3, 0x66, 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x69, 0x01, 0x00, 0x02, 0x49, 0x89, 0xca, 0x0f, 0x05, 0xc3, 0xb8, 0x24, 0x00, 0x00, 0x01, 0x49, 0x89, 0xca, 0x0f, 0x05, 0xc3, 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x40, 0x31, 0xf6, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x8b, 0x7d, 0xf8, 0x48, 0x89, 0x7d, 0xf0, 0x48, 0x8b, 0x7d, 0xf0, 0x48, 0x8b, 0x7f, 0x10, 0x48, 0x8b, 0x45, 0xf0, 0x48, 0x8b, 0x40, 0x28, 0x48, 0x89, 0x7d, 0xd0, 0x48, 0x89, 0xc7, 0x48, 0x8b, 0x45, 0xd0, 0xff, 0xd0, 0x48, 0x89, 0x45, 0xe8, 0x48, 0x81, 0x7d, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x84, 0x48, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x35, 0x80, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x45, 0xf0, 0x48, 0x8b, 0x40, 0x18, 0x48, 0x8b, 0x7d, 0xe8, 0xff, 0xd0, 0x48, 0x89, 0x45, 0xe0, 0x48, 0x81, 0x7d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x84, 0x1c, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x45, 0xe0, 0x48, 0x8b, 0x4d, 0xf0, 0x48, 0x81, 0xc1, 0x40, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x72, 0x38, 0x48, 0x89, 0xcf, 0xff, 0xd0, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x45, 0xf0, 0x48, 0x8b, 0x40, 0x30, 0x89, 0xc1, 0x89, 0xcf, 0xe8, 0x4d, 0xff, 0xff, 0xff, 0xb9, 0x00, 0x20, 0x00, 0x00, 0x89, 0xce, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x81, 0xe2, 0x00, 0xf0, 0xff, 0xff, 0x48, 0x89, 0x55, 0xd8, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x52, 0x20, 0x48, 0x8b, 0x7d, 0xd8, 0x89, 0x45, 0xcc, 0xff, 0xd2, 0x48, 0x83, 0xc4, 0x40, 0x5d, 0xc3, 0x90, 0x73, 0x75, 0x62, 0x73, 0x74, 0x69, 0x74, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x00 17 | .align 2 18 | .private_extern _inject_start_i386 19 | _inject_start_i386: 20 | .byte 0x55, 0x89, 0xe5, 0x57, 0x56, 0x83, 0xec, 0x30, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x58, 0x8d, 0x55, 0xf0, 0x31, 0xf6, 0x8d, 0x80, 0xf3, 0x00, 0x00, 0x00, 0x89, 0x4d, 0xf4, 0xc7, 0x45, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x4d, 0xf4, 0x8b, 0x09, 0x8b, 0x7d, 0xf4, 0x89, 0x14, 0x24, 0xc7, 0x44, 0x24, 0x04, 0x00, 0x00, 0x00, 0x00, 0x89, 0x44, 0x24, 0x08, 0x89, 0x7c, 0x24, 0x0c, 0x89, 0x75, 0xec, 0xff, 0xd1, 0x8b, 0x4d, 0xf4, 0x8b, 0x49, 0x04, 0x8b, 0x55, 0xf0, 0x89, 0x14, 0x24, 0x89, 0x45, 0xe8, 0xff, 0xd1, 0x31, 0xc9, 0x8b, 0x55, 0xf4, 0x8b, 0x52, 0x18, 0xc7, 0x04, 0x24, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x44, 0x24, 0x04, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x44, 0x24, 0x08, 0x00, 0x00, 0x00, 0x00, 0x89, 0x54, 0x24, 0x0c, 0x89, 0x45, 0xe4, 0x89, 0x4d, 0xe0, 0xe8, 0x1e, 0x00, 0x00, 0x00, 0xb9, 0xad, 0x0b, 0x00, 0x00, 0x89, 0x45, 0xdc, 0xff, 0xd1, 0x83, 0xc4, 0x30, 0x5e, 0x5f, 0x5d, 0xc3, 0x66, 0x66, 0x66, 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x69, 0x01, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x81, 0xc2, 0x0b, 0x00, 0x00, 0x00, 0x89, 0xe1, 0x0f, 0x34, 0xc3, 0xb8, 0xdc, 0xff, 0xff, 0xff, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x81, 0xc2, 0x0b, 0x00, 0x00, 0x00, 0x89, 0xe1, 0x0f, 0x34, 0xc3, 0x55, 0x89, 0xe5, 0x81, 0xed, 0x00, 0x04, 0x00, 0x00, 0x8b, 0x54, 0x24, 0x08, 0x8b, 0x42, 0x10, 0x81, 0xe2, 0x00, 0xf0, 0xff, 0xff, 0x89, 0x55, 0x08, 0xc7, 0x45, 0x0c, 0x00, 0x20, 0x00, 0x00, 0x83, 0xc0, 0x03, 0xff, 0xe0, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x89, 0xe5, 0x56, 0x83, 0xec, 0x24, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x58, 0x8b, 0x4d, 0x08, 0x31, 0xd2, 0x89, 0x4d, 0xf8, 0x8b, 0x4d, 0xf8, 0x89, 0x4d, 0xf4, 0x8b, 0x4d, 0xf4, 0x8b, 0x49, 0x08, 0x8b, 0x75, 0xf4, 0x8b, 0x76, 0x14, 0x89, 0x34, 0x24, 0xc7, 0x44, 0x24, 0x04, 0x00, 0x00, 0x00, 0x00, 0x89, 0x45, 0xe8, 0x89, 0x55, 0xe4, 0xff, 0xd1, 0x89, 0x45, 0xf0, 0x81, 0x7d, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x84, 0x4b, 0x00, 0x00, 0x00, 0x8b, 0x45, 0xe8, 0x8d, 0x88, 0xac, 0x00, 0x00, 0x00, 0x8b, 0x55, 0xf4, 0x8b, 0x52, 0x0c, 0x8b, 0x75, 0xf0, 0x89, 0x34, 0x24, 0x89, 0x4c, 0x24, 0x04, 0xff, 0xd2, 0x89, 0x45, 0xec, 0x81, 0x7d, 0xec, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x84, 0x1b, 0x00, 0x00, 0x00, 0x8b, 0x45, 0xec, 0x8b, 0x4d, 0xf4, 0x81, 0xc1, 0x20, 0x00, 0x00, 0x00, 0x8b, 0x55, 0xf4, 0x8b, 0x52, 0x1c, 0x89, 0x0c, 0x24, 0x89, 0x54, 0x24, 0x04, 0xff, 0xd0, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x45, 0xf4, 0x8b, 0x40, 0x18, 0x89, 0x04, 0x24, 0xe8, 0x13, 0xff, 0xff, 0xff, 0x8b, 0x4d, 0xf4, 0x89, 0x0c, 0x24, 0x89, 0x45, 0xe0, 0xe8, 0x1b, 0xff, 0xff, 0xff, 0x83, 0xc4, 0x24, 0x5e, 0x5d, 0xc3, 0x90, 0x73, 0x75, 0x62, 0x73, 0x74, 0x69, 0x74, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x00 21 | .align 2 22 | .private_extern _inject_start_arm 23 | _inject_start_arm: 24 | .byte 0x80, 0xb5, 0x6f, 0x46, 0x87, 0xb0, 0x05, 0xa9, 0x00, 0x22, 0x40, 0xf2, 0x6f, 0x03, 0xc0, 0xf2, 0x00, 0x03, 0x7b, 0x44, 0x06, 0x90, 0x05, 0x92, 0x06, 0x98, 0x00, 0x68, 0xdd, 0xf8, 0x18, 0x90, 0x04, 0x90, 0x08, 0x46, 0x11, 0x46, 0x1a, 0x46, 0x4b, 0x46, 0xdd, 0xf8, 0x10, 0x90, 0xc8, 0x47, 0x06, 0x99, 0x49, 0x68, 0x05, 0x9a, 0x03, 0x90, 0x10, 0x46, 0x88, 0x47, 0x00, 0x21, 0x06, 0x9a, 0x93, 0x69, 0x02, 0x90, 0x08, 0x46, 0x01, 0x91, 0x01, 0x9a, 0x00, 0xf0, 0x07, 0xf8, 0x40, 0xf6, 0xad, 0x31, 0x00, 0x90, 0x88, 0x47, 0x07, 0xb0, 0x80, 0xbd, 0x00, 0xbf, 0xec, 0x46, 0x70, 0xb4, 0x9c, 0xe8, 0x70, 0x00, 0x40, 0xf2, 0x69, 0x1c, 0x80, 0xdf, 0x70, 0xbc, 0x70, 0x47, 0x00, 0xbf, 0xec, 0x46, 0x70, 0xb4, 0x9c, 0xe8, 0x70, 0x00, 0x6f, 0xf0, 0x23, 0x0c, 0x80, 0xdf, 0x70, 0xbc, 0x70, 0x47, 0x00, 0xbf, 0x80, 0xb5, 0x6f, 0x46, 0x89, 0xb0, 0x00, 0x21, 0x08, 0x90, 0x08, 0x98, 0x07, 0x90, 0x07, 0x98, 0x80, 0x68, 0x07, 0x9a, 0x52, 0x69, 0x03, 0x90, 0x10, 0x46, 0x03, 0x9a, 0x90, 0x47, 0x00, 0x21, 0x06, 0x90, 0x06, 0x98, 0x88, 0x42, 0x1b, 0xd0, 0x40, 0xf2, 0x58, 0x01, 0xc0, 0xf2, 0x00, 0x01, 0x79, 0x44, 0x07, 0x98, 0xc0, 0x68, 0x06, 0x9a, 0x02, 0x90, 0x10, 0x46, 0x02, 0x9a, 0x90, 0x47, 0x00, 0x21, 0x05, 0x90, 0x05, 0x98, 0x88, 0x42, 0x09, 0xd0, 0x05, 0x98, 0x07, 0x99, 0x20, 0x31, 0x07, 0x9a, 0xd2, 0x69, 0x01, 0x90, 0x08, 0x46, 0x11, 0x46, 0x01, 0x9a, 0x90, 0x47, 0xff, 0xe7, 0x07, 0x98, 0x80, 0x69, 0xff, 0xf7, 0xc2, 0xff, 0x42, 0xf2, 0x00, 0x01, 0x07, 0x9a, 0x4f, 0xf2, 0x00, 0x03, 0xcf, 0xf6, 0xff, 0x73, 0x1a, 0x40, 0x04, 0x92, 0x07, 0x9a, 0x12, 0x69, 0x04, 0x9b, 0x00, 0x90, 0x18, 0x46, 0x90, 0x47, 0x09, 0xb0, 0x80, 0xbd, 0x00, 0xbf, 0x73, 0x75, 0x62, 0x73, 0x74, 0x69, 0x74, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x00 25 | .align 2 26 | .private_extern _inject_start_arm64 27 | _inject_start_arm64: 28 | .byte 0xf4, 0x4f, 0xbe, 0xa9, 0xfd, 0x7b, 0x01, 0xa9, 0xfd, 0x43, 0x00, 0x91, 0xff, 0x43, 0x00, 0xd1, 0xf3, 0x03, 0x00, 0xaa, 0xff, 0x0f, 0x00, 0xb9, 0x68, 0x02, 0x40, 0xf9, 0x42, 0x03, 0x00, 0x10, 0x1f, 0x20, 0x03, 0xd5, 0xe0, 0x33, 0x00, 0x91, 0x01, 0x00, 0x80, 0xd2, 0xe3, 0x03, 0x13, 0xaa, 0x00, 0x01, 0x3f, 0xd6, 0x68, 0x06, 0x40, 0xf9, 0xe0, 0x0f, 0x40, 0xb9, 0x00, 0x01, 0x3f, 0xd6, 0x63, 0x32, 0x40, 0xb9, 0x00, 0x00, 0x80, 0xd2, 0x01, 0x00, 0x80, 0xd2, 0x02, 0x00, 0x80, 0x52, 0x07, 0x00, 0x00, 0x94, 0xa8, 0x75, 0x81, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0xbf, 0x43, 0x00, 0xd1, 0xfd, 0x7b, 0x41, 0xa9, 0xf4, 0x4f, 0xc2, 0xa8, 0xc0, 0x03, 0x5f, 0xd6, 0x30, 0x2d, 0x80, 0xd2, 0x01, 0x10, 0x00, 0xd4, 0xc0, 0x03, 0x5f, 0xd6, 0x70, 0x04, 0x80, 0x92, 0x01, 0x10, 0x00, 0xd4, 0xc0, 0x03, 0x5f, 0xd6, 0xf4, 0x4f, 0xbe, 0xa9, 0xfd, 0x7b, 0x01, 0xa9, 0xfd, 0x43, 0x00, 0x91, 0xf3, 0x03, 0x00, 0xaa, 0x68, 0x0a, 0x40, 0xf9, 0x60, 0x16, 0x40, 0xf9, 0x01, 0x00, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x40, 0x01, 0x00, 0xb4, 0x68, 0x0e, 0x40, 0xf9, 0x01, 0x02, 0x00, 0x10, 0x1f, 0x20, 0x03, 0xd5, 0x00, 0x01, 0x3f, 0xd6, 0xe8, 0x03, 0x00, 0xaa, 0x88, 0x00, 0x00, 0xb4, 0x60, 0x02, 0x01, 0x91, 0x61, 0x1e, 0x40, 0xf9, 0x00, 0x01, 0x3f, 0xd6, 0x60, 0x32, 0x40, 0xb9, 0xea, 0xff, 0xff, 0x97, 0x60, 0xc6, 0x72, 0x92, 0x62, 0x12, 0x40, 0xf9, 0xe1, 0x03, 0x11, 0x32, 0xfd, 0x7b, 0x41, 0xa9, 0xf4, 0x4f, 0xc2, 0xa8, 0x40, 0x00, 0x1f, 0xd6, 0x73, 0x75, 0x62, 0x73, 0x74, 0x69, 0x74, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x00 29 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/generated/generic-dis-arm64.inc.h: -------------------------------------------------------------------------------- 1 | /* Generated code; do not edit! 2 | generated by tables/gen.js from imaon2 'f0e220720bbfb8f8e00e76af56806a28fc8739a2' 3 | https://github.com/comex/imaon2 4 | arguments: '--gen-hook-disassembler --dis-pattern=P(XXX) out/out-AArch64.json' 5 | (fair warning: at present the main (Rust) code in that repository is barely 6 | started, embarrassingly so; no need to look at it ;p) 7 | In case it's copyrightable in any way, consider the generated code in the 8 | public domain. 9 | */ 10 | 11 | /* adrlabel_label_unk_Xd_1_ADR: ADR */ 12 | /* adrplabel_label_unk_Xd_1_ADRP: ADRP */ 13 | /* am_b_target_addr_B_1_B: B */ 14 | /* am_bl_target_addr_1_BL: BL */ 15 | /* GPR64_Rn_2_BLR: BLR, RET */ 16 | /* ccode_cond_am_brcond_target_B_1_Bcc: Bcc */ 17 | /* am_brcond_target_B_4_CBNZW: CBNZW, CBNZX, CBZW, CBZX */ 18 | /* am_ldrlit_label_unk_Rt_6_LDRDl: LDRDl, LDRQl, LDRSWl, LDRSl, LDRWl, LDRXl */ 19 | /* am_tbrcond_target_B_4_TBNZW: TBNZW, TBNZX, TBZW, TBZX */ 20 | switch ((op >> 29) & 0x7) { 21 | case 0: { 22 | switch ((op >> 26) & 0x3) { 23 | case 0: { 24 | if ((op & 0x9f000000) == 0x10000000) { 25 | insn_adrlabel_label_unk_Xd_1_ADR:; 26 | struct bitslice Xd = {.nruns = 1, .runs = (struct bitslice_run[]) {{0,0,5}}}; 27 | struct bitslice label = {.nruns = 2, .runs = (struct bitslice_run[]) {{5,2,19}, {29,0,2}}}; 28 | return P(adrlabel_label_unk_Xd_1_ADR)(ctx, Xd, label); /* 0x10000000 | 0x60ffffff */ 29 | } else { 30 | return P(unidentified)(ctx); 31 | } 32 | } 33 | case 1: { 34 | if ((op & 0xfc000000) == 0x14000000) { 35 | struct bitslice addr = {.nruns = 1, .runs = (struct bitslice_run[]) {{0,0,26}}}; 36 | return P(am_b_target_addr_B_1_B)(ctx, addr); /* 0x14000000 | 0x03ffffff */ 37 | } else { 38 | return P(unidentified)(ctx); 39 | } 40 | } 41 | case 2: 42 | case 3: { 43 | if ((op & 0xbb000000) == 0x18000000) { 44 | insn_am_ldrlit_label_unk_Rt_6_LDRDl:; 45 | struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{0,0,5}}}; 46 | struct bitslice label = {.nruns = 1, .runs = (struct bitslice_run[]) {{5,0,19}}}; 47 | return P(am_ldrlit_label_unk_Rt_6_LDRDl)(ctx, Rt, label); /* 0x18000000 | 0x44ffffff */ 48 | } else { 49 | return P(unidentified)(ctx); 50 | } 51 | } 52 | } 53 | } 54 | case 1: { 55 | switch ((op >> 25) & 0x3) { 56 | case 0: { 57 | if ((op & 0x9f000000) == 0x10000000) { 58 | goto insn_adrlabel_label_unk_Xd_1_ADR; /* 0x10000000 | 0x60ffffff */ 59 | } else { 60 | return P(unidentified)(ctx); 61 | } 62 | } 63 | case 1: 64 | return P(unidentified)(ctx); 65 | case 2: { 66 | if ((op & 0x7e000000) == 0x34000000) { 67 | insn_am_brcond_target_B_4_CBNZW:; 68 | struct bitslice target = {.nruns = 1, .runs = (struct bitslice_run[]) {{5,0,19}}}; 69 | return P(am_brcond_target_B_4_CBNZW)(ctx, target); /* 0x34000000 | 0x81ffffff */ 70 | } else { 71 | return P(unidentified)(ctx); 72 | } 73 | } 74 | case 3: { 75 | if ((op & 0x7e000000) == 0x36000000) { 76 | insn_am_tbrcond_target_B_4_TBNZW:; 77 | struct bitslice target = {.nruns = 1, .runs = (struct bitslice_run[]) {{5,0,14}}}; 78 | return P(am_tbrcond_target_B_4_TBNZW)(ctx, target); /* 0x36000000 | 0x81ffffff */ 79 | } else { 80 | return P(unidentified)(ctx); 81 | } 82 | } 83 | } 84 | } 85 | case 2: { 86 | switch ((op >> 26) & 0x3) { 87 | case 0: { 88 | if ((op & 0x9f000000) == 0x10000000) { 89 | goto insn_adrlabel_label_unk_Xd_1_ADR; /* 0x10000000 | 0x60ffffff */ 90 | } else { 91 | return P(unidentified)(ctx); 92 | } 93 | } 94 | case 1: { 95 | if ((op & 0xff000010) == 0x54000000) { 96 | struct bitslice cond = {.nruns = 1, .runs = (struct bitslice_run[]) {{0,0,4}}}; 97 | struct bitslice target = {.nruns = 1, .runs = (struct bitslice_run[]) {{5,0,19}}}; 98 | return P(ccode_cond_am_brcond_target_B_1_Bcc)(ctx, cond, target); /* 0x54000000 | 0x00ffffef */ 99 | } else { 100 | return P(unidentified)(ctx); 101 | } 102 | } 103 | case 2: 104 | case 3: { 105 | if ((op & 0xbb000000) == 0x18000000) { 106 | goto insn_am_ldrlit_label_unk_Rt_6_LDRDl; /* 0x18000000 | 0x44ffffff */ 107 | } else { 108 | return P(unidentified)(ctx); 109 | } 110 | } 111 | } 112 | } 113 | case 3: { 114 | if ((op & 0x9f000000) == 0x10000000) { 115 | goto insn_adrlabel_label_unk_Xd_1_ADR; /* 0x10000000 | 0x60ffffff */ 116 | } else { 117 | return P(unidentified)(ctx); 118 | } 119 | } 120 | case 4: { 121 | switch ((op >> 26) & 0x3) { 122 | case 0: { 123 | if ((op & 0x9f000000) == 0x90000000) { 124 | insn_adrplabel_label_unk_Xd_1_ADRP:; 125 | struct bitslice Xd = {.nruns = 1, .runs = (struct bitslice_run[]) {{0,0,5}}}; 126 | struct bitslice label = {.nruns = 2, .runs = (struct bitslice_run[]) {{5,2,19}, {29,0,2}}}; 127 | return P(adrplabel_label_unk_Xd_1_ADRP)(ctx, Xd, label); /* 0x90000000 | 0x60ffffff */ 128 | } else { 129 | return P(unidentified)(ctx); 130 | } 131 | } 132 | case 1: { 133 | if ((op & 0xfc000000) == 0x94000000) { 134 | struct bitslice addr = {.nruns = 1, .runs = (struct bitslice_run[]) {{0,0,26}}}; 135 | return P(am_bl_target_addr_1_BL)(ctx, addr); /* 0x94000000 | 0x03ffffff */ 136 | } else { 137 | return P(unidentified)(ctx); 138 | } 139 | } 140 | case 2: 141 | case 3: { 142 | if ((op & 0xfb000000) == 0x98000000) { 143 | goto insn_am_ldrlit_label_unk_Rt_6_LDRDl; /* 0x98000000 | 0x04ffffff */ 144 | } else { 145 | return P(unidentified)(ctx); 146 | } 147 | } 148 | } 149 | } 150 | case 5: { 151 | switch ((op >> 25) & 0x3) { 152 | case 0: { 153 | if ((op & 0x9f000000) == 0x90000000) { 154 | goto insn_adrplabel_label_unk_Xd_1_ADRP; /* 0x90000000 | 0x60ffffff */ 155 | } else { 156 | return P(unidentified)(ctx); 157 | } 158 | } 159 | case 1: 160 | return P(unidentified)(ctx); 161 | case 2: { 162 | if ((op & 0x7e000000) == 0x34000000) { 163 | goto insn_am_brcond_target_B_4_CBNZW; /* 0x34000000 | 0x81ffffff */ 164 | } else { 165 | return P(unidentified)(ctx); 166 | } 167 | } 168 | case 3: { 169 | if ((op & 0x7e000000) == 0x36000000) { 170 | goto insn_am_tbrcond_target_B_4_TBNZW; /* 0x36000000 | 0x81ffffff */ 171 | } else { 172 | return P(unidentified)(ctx); 173 | } 174 | } 175 | } 176 | } 177 | case 6: { 178 | switch ((op >> 22) & 0xf) { 179 | case 0: 180 | case 1: 181 | case 2: 182 | case 3: { 183 | if ((op & 0x9f000000) == 0x90000000) { 184 | goto insn_adrplabel_label_unk_Xd_1_ADRP; /* 0x90000000 | 0x60ffffff */ 185 | } else { 186 | return P(unidentified)(ctx); 187 | } 188 | } 189 | case 4: 190 | case 5: 191 | case 6: 192 | case 7: 193 | case 10: 194 | case 11: 195 | case 12: 196 | case 13: 197 | case 14: 198 | case 15: 199 | return P(unidentified)(ctx); 200 | case 8: { 201 | if ((op & 0xfffffc1f) == 0xd63f0000) { 202 | insn_GPR64_Rn_2_BLR:; 203 | struct bitslice Rn = {.nruns = 1, .runs = (struct bitslice_run[]) {{5,0,5}}}; 204 | return P(GPR64_Rn_2_BLR)(ctx, Rn); /* 0xd63f0000 | 0x000003e0 */ 205 | } else { 206 | return P(unidentified)(ctx); 207 | } 208 | } 209 | case 9: { 210 | if ((op & 0xfffffc1f) == 0xd65f0000 || (op & 0xfffffbff) == 0xd65f0bff) { 211 | goto insn_GPR64_Rn_2_BLR; /* 0xd65f0000 | 0x000003e0 */ 212 | } else { 213 | return P(unidentified)(ctx); 214 | } 215 | } 216 | } 217 | } 218 | case 7: { 219 | if ((op & 0x9f000000) == 0x90000000) { 220 | goto insn_adrplabel_label_unk_Xd_1_ADRP; /* 0x90000000 | 0x60ffffff */ 221 | } else { 222 | return P(unidentified)(ctx); 223 | } 224 | } 225 | } 226 | /* 227 | static INLINE tdis_ret P(GPR64_Rn_2_BLR)(struct bitslice ctx, struct bitslice Rn) {} 228 | static INLINE tdis_ret P(adrlabel_label_unk_Xd_1_ADR)(struct bitslice ctx, struct bitslice Xd, struct bitslice label) {} 229 | static INLINE tdis_ret P(adrplabel_label_unk_Xd_1_ADRP)(struct bitslice ctx, struct bitslice Xd, struct bitslice label) {} 230 | static INLINE tdis_ret P(am_b_target_addr_B_1_B)(struct bitslice ctx, struct bitslice addr) {} 231 | static INLINE tdis_ret P(am_bl_target_addr_1_BL)(struct bitslice ctx, struct bitslice addr) {} 232 | static INLINE tdis_ret P(am_brcond_target_B_4_CBNZW)(struct bitslice ctx, struct bitslice target) {} 233 | static INLINE tdis_ret P(am_ldrlit_label_unk_Rt_6_LDRDl)(struct bitslice ctx, struct bitslice Rt, struct bitslice label) {} 234 | static INLINE tdis_ret P(am_tbrcond_target_B_4_TBNZW)(struct bitslice ctx, struct bitslice target) {} 235 | static INLINE tdis_ret P(ccode_cond_am_brcond_target_B_1_Bcc)(struct bitslice ctx, struct bitslice cond, struct bitslice target) {} 236 | */ 237 | 238 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/arm64/arch-dis.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define MIN_INSN_SIZE 4 3 | #define TD_MAX_REWRITTEN_SIZE (7 * 2 * 4) /* also conservative */ 4 | #define ARCH_MAX_CODE_ALIGNMENT 4 5 | 6 | struct arch_pcrel_info { 7 | unsigned reg; 8 | enum pcrel_load_mode load_mode; 9 | }; 10 | 11 | struct arch_dis_ctx { 12 | /* For transform_dis only - used to get temporary registers. We assume 13 | * that we can use any caller-saved or IP register which was not written, 14 | * so r9-r18. 15 | * This is a massive overestimate: we just OR in each instruction's bits 16 | * 4:0 (Rd for data, Rt for loads, most common), 14:10 (Rt2 for load-pair 17 | * instructions), and 20:16 (Rs for store-exclusive insturctions). It 18 | * would be easy to restrict the latter two to the few instructions that 19 | * actually use them, but with 10 available registers, and a patch of at 20 | * most 3 instructions (and none of the instructions that require a temp 21 | * use Rt2/Rs or could read their Rd, so the third doesn't count), we won't 22 | * run out even with the dumbest possible thing. */ 23 | uint32_t regs_possibly_written; 24 | }; 25 | 26 | static inline void arch_dis_ctx_init(struct arch_dis_ctx *ctx) { 27 | ctx->regs_possibly_written = 0; 28 | } 29 | 30 | static inline int arm64_get_unwritten_temp_reg(struct arch_dis_ctx *ctx) { 31 | uint32_t avail = ~ctx->regs_possibly_written & ((1 << 18) - (1 << 9)); 32 | if (!avail) 33 | __builtin_abort(); 34 | return 31 - __builtin_clz(avail); 35 | } 36 | 37 | #define CC_ARMCC (CC_CONDITIONAL | 0x400) 38 | #define CC_XBXZ (CC_CONDITIONAL | 0x800) 39 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/arm64/arch-transform-dis.inc.h: -------------------------------------------------------------------------------- 1 | #include "arm64/assemble.h" 2 | 3 | static NOINLINE UNUSED 4 | void transform_dis_pcrel(struct transform_dis_ctx *ctx, uint_tptr dpc, 5 | struct arch_pcrel_info info) { 6 | ctx->write_newop_here = NULL; 7 | void **codep = ctx->rewritten_ptr_ptr; 8 | if (info.load_mode >= PLM_U32_SIMD) { 9 | int temp = arm64_get_unwritten_temp_reg(&ctx->arch); 10 | MOVi64(codep, 0, dpc); 11 | LDRxi(codep, temp, 0, 0, true, info.load_mode); 12 | } else { 13 | MOVi64(codep, info.reg, dpc); 14 | LDRxi(codep, info.reg, info.reg, 0, true, info.load_mode); 15 | } 16 | } 17 | 18 | static NOINLINE UNUSED 19 | void transform_dis_branch(struct transform_dis_ctx *ctx, uint_tptr dpc, int cc) { 20 | /* TODO fix BL */ 21 | #ifdef TRANSFORM_DIS_VERBOSE 22 | printf("transform_dis (0x%llx): branch => 0x%llx\n", 23 | (unsigned long long) ctx->base.pc, 24 | (unsigned long long) dpc); 25 | #endif 26 | transform_dis_branch_top(ctx, dpc, cc); 27 | ctx->write_newop_here = NULL; 28 | int mov_br_size = size_of_MOVi64(dpc) + 4; 29 | 30 | void **codep = ctx->rewritten_ptr_ptr; 31 | if ((cc & CC_ARMCC) == CC_ARMCC) { 32 | int icc = (cc & 0xf) ^ 1; 33 | Bccrel(codep, icc, 4 + mov_br_size); 34 | } else if ((cc & CC_XBXZ) == CC_XBXZ) { 35 | ctx->base.modify = true; 36 | ctx->base.newval[0] = ctx->base.pc + 4 + mov_br_size; 37 | ctx->base.newval[1] = 1; /* do invert */ 38 | ctx->write_newop_here = *codep; *codep += 4; 39 | } 40 | int reg = arm64_get_unwritten_temp_reg(&ctx->arch); 41 | MOVi64(codep, reg, dpc); 42 | BR(codep, reg, /*link*/ cc & CC_CALL); 43 | } 44 | 45 | static void transform_dis_pre_dis(UNUSED struct transform_dis_ctx *ctx) {} 46 | static void transform_dis_post_dis(struct transform_dis_ctx *ctx) { 47 | uint32_t op = ctx->base.op; 48 | ctx->arch.regs_possibly_written |= 1 << (op & 31); 49 | ctx->arch.regs_possibly_written |= 1 << (op >> 10 & 31); 50 | ctx->arch.regs_possibly_written |= 1 << (op >> 16 & 31); 51 | } 52 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/arm64/assemble.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "dis.h" 3 | 4 | static inline int size_of_MOVi64(uint64_t val) { 5 | int num_nybbles = val == 0 ? 1 : ((64 - __builtin_clzll(val) + 15) / 16); 6 | return 4 * num_nybbles; 7 | } 8 | 9 | static inline void MOVi64(void **codep, int Rd, uint64_t val) { 10 | int shift_nybbles = 0; 11 | do { 12 | int k = shift_nybbles != 0; 13 | op32(codep, 0xd2800000 | k << 29 | Rd | (val & 0xffff) << 5 | 14 | shift_nybbles << 21); 15 | shift_nybbles++; 16 | val >>= 16; 17 | } while(val); 18 | } 19 | 20 | static inline void LDRxi(void **codep, int Rt, int Rn, uint32_t off, 21 | bool regsize_64, enum pcrel_load_mode load_mode) { 22 | int size, opc; 23 | bool sign, simd; 24 | switch (load_mode) { 25 | case PLM_ADR: return; 26 | case PLM_U8: size = 0; sign = false; simd = false; break; 27 | case PLM_S8: size = 0; sign = true; simd = false; break; 28 | case PLM_U16: size = 1; sign = false; simd = false; break; 29 | case PLM_S16: size = 1; sign = true; simd = false; break; 30 | case PLM_U32: size = 2; sign = false; simd = false; break; 31 | case PLM_S32: size = 2; sign = true; simd = false; break; 32 | case PLM_U64: size = 3; sign = false; simd = false; break; 33 | case PLM_U32_SIMD: size = 2; opc = 1; simd = true; break; 34 | case PLM_U64_SIMD: size = 3; opc = 1; simd = true; break; 35 | case PLM_U128_SIMD: size = 0; opc = 3; simd = true; break; 36 | default: __builtin_abort(); 37 | } 38 | if (simd) { 39 | off /= 1 << (size | (opc & 1) << 2); 40 | } else { 41 | off /= 1 << size; 42 | opc = sign ? (regsize_64 ? 2 : 3) : 1; 43 | } 44 | op32(codep, 0x39000000 | Rt | Rn << 5 | off << 10 | opc << 22 | simd << 26 | 45 | size << 30); 46 | } 47 | 48 | static inline void ADRP_ADD(void **codep, int reg, uint64_t pc, uint64_t dpc) { 49 | uint64_t diff = (dpc & ~0xfff) - (pc & ~0xfff); 50 | /* ADRP reg, dpc */ 51 | op32(codep, 0x90000000 | reg | (diff & 0x3000) << 17 | 52 | (diff & 0x1ffffc000) >> 9); 53 | uint32_t lo = dpc & 0xfff; 54 | if (lo) { 55 | /* ADD reg, reg, #lo */ 56 | op32(codep, 0x91000000 | reg | reg << 5 | lo << 10); 57 | } 58 | } 59 | 60 | static inline void BR(void **codep, int reg, bool link) { 61 | op32(codep, 0xd61f0000 | reg << 5 | link << 21); 62 | } 63 | 64 | static inline void Bccrel(void **codep, int cc, int offset) { 65 | op32(codep, 0x54000000 | (offset / 4) << 5 | cc); 66 | } 67 | 68 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/arm64/dis-main.inc.h: -------------------------------------------------------------------------------- 1 | static INLINE void P(adrlabel_label_unk_Xd_1_ADR)(tdis_ctx ctx, struct bitslice Xd, struct bitslice label) { 2 | return P(pcrel)(ctx, ctx->base.pc + sext(bs_get(label, ctx->base.op), 21), 3 | (struct arch_pcrel_info) {bs_get(Xd, ctx->base.op), PLM_ADR}); 4 | } 5 | static INLINE void P(adrplabel_label_unk_Xd_1_ADRP)(tdis_ctx ctx, struct bitslice Xd, struct bitslice label) { 6 | return P(pcrel)(ctx, 7 | (ctx->base.pc & ~0xfff) + 8 | (sext(bs_get(label, ctx->base.op), 21) << 12), 9 | (struct arch_pcrel_info) {bs_get(Xd, ctx->base.op), PLM_ADR}); 10 | } 11 | static INLINE void P(am_b_target_addr_B_1_B)(tdis_ctx ctx, struct bitslice addr) { 12 | return P(branch)(ctx, ctx->base.pc + sext(bs_get(addr, ctx->base.op), 26) * 4, 13 | /*cc*/ 0); 14 | } 15 | static INLINE void P(am_bl_target_addr_1_BL)(tdis_ctx ctx, struct bitslice addr) { 16 | return P(branch)(ctx, ctx->base.pc + sext(bs_get(addr, ctx->base.op), 26) * 4, 17 | /*cc*/ CC_CALL); 18 | } 19 | static INLINE void P(ccode_cond_am_brcond_target_B_1_Bcc)(tdis_ctx ctx, struct bitslice cond, struct bitslice target) { 20 | int bits = bs_get(cond, ctx->base.op); 21 | /* Bcc with AL/NV (which is actually just another AL) is useless but possible. */ 22 | int cc = bits >= 0xe ? 0 : (CC_ARMCC | bits); 23 | return P(branch)(ctx, ctx->base.pc + sext(bs_get(target, ctx->base.op), 19) * 4, cc); 24 | } 25 | static INLINE void P(am_tbrcond_target_B_4_TBNZW)(tdis_ctx ctx, struct bitslice target) { 26 | P(branch)(ctx, ctx->base.pc + sext(bs_get(target, ctx->base.op), 14) * 4, CC_XBXZ); 27 | if (ctx->base.modify) { 28 | /* ditto CBNZ on ARM */ 29 | int new_target = (ctx->base.newval[0] - ctx->base.pc) / 4; 30 | unsigned new = bs_set(target, new_target, ctx->base.op); 31 | if (ctx->base.newval[1]) 32 | new ^= 1 << 24; 33 | *(uint32_t *) ctx->base.newop = new; 34 | } 35 | } 36 | static INLINE void P(am_brcond_target_B_4_CBNZW)(tdis_ctx ctx, struct bitslice target) { 37 | /* both have the same bit to control Z/NZ */ 38 | return P(am_tbrcond_target_B_4_TBNZW)(ctx, target); 39 | } 40 | static INLINE void P(am_ldrlit_label_unk_Rt_6_LDRDl)(tdis_ctx ctx, struct bitslice Rt, struct bitslice label) { 41 | enum pcrel_load_mode mode; 42 | if ((ctx->base.op >> 26) & 1) { 43 | switch (ctx->base.op >> 30) { 44 | case 0: mode = PLM_U32_SIMD; break; 45 | case 1: mode = PLM_U64_SIMD; break; 46 | case 2: mode = PLM_U128_SIMD; break; 47 | default: __builtin_abort(); 48 | } 49 | } else { 50 | switch (ctx->base.op >> 30) { 51 | case 0: mode = PLM_U32; break; 52 | case 1: mode = PLM_U64; break; 53 | case 2: mode = PLM_S32; break; 54 | default: __builtin_abort(); 55 | } 56 | } 57 | return P(pcrel)(ctx, ctx->base.pc + sext(bs_get(label, ctx->base.op), 19) * 4, 58 | (struct arch_pcrel_info) {bs_get(Rt, ctx->base.op), mode}); 59 | } 60 | 61 | static INLINE void P(GPR64_Rn_2_BLR)(tdis_ctx ctx, UNUSED struct bitslice Rn) { 62 | int op = ctx->base.op >> 21 & 3; 63 | if (op == 1) 64 | return P(indirect_call)(ctx); 65 | else 66 | return P(ret)(ctx); 67 | } 68 | 69 | static INLINE void P(dis)(tdis_ctx ctx) { 70 | uint32_t op = ctx->base.op = unaligned_r32(ctx->base.ptr); 71 | ctx->base.op_size = ctx->base.newop_size = 4; 72 | /* clang doesn't realize that this is unreachable and generates code like 73 | * "and ecx, 0x1f; cmp ecx, 0x1f; ja abort". Yeah, nice job there. */ 74 | #include "../generated/generic-dis-arm64.inc.h" 75 | __builtin_abort(); 76 | } 77 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/arm64/jump-patch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "arm64/assemble.h" 3 | #define MAX_JUMP_PATCH_SIZE 20 4 | #define MAX_EXTENDED_PATCH_SIZE MAX_JUMP_PATCH_SIZE 5 | 6 | static inline int jump_patch_size(uint_tptr pc, uint_tptr dpc, 7 | UNUSED struct arch_dis_ctx arch, 8 | bool force) { 9 | intptr_t diff = (dpc & ~0xfff) - (pc & ~0xfff); 10 | if (!(diff >= -0x100000000 && diff < 0x100000000)) 11 | return force ? (size_of_MOVi64(dpc) + 4) : -1; 12 | else if (!(dpc & 0xfff)) 13 | return 8; 14 | else 15 | return 12; 16 | } 17 | 18 | static inline void make_jump_patch(void **codep, uint_tptr pc, uint_tptr dpc, 19 | struct arch_dis_ctx arch) { 20 | int reg = arm64_get_unwritten_temp_reg(&arch); 21 | intptr_t diff = (dpc & ~0xfff) - (pc & ~0xfff); 22 | if (!(diff >= -0x100000000 && diff < 0x100000000)) 23 | MOVi64(codep, reg, dpc); 24 | else 25 | ADRP_ADD(codep, reg, pc, dpc); 26 | BR(codep, reg, false); 27 | } 28 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/arm64/misc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define TARGET_POINTER_SIZE 8 3 | #define TARGET_DIS_SUPPORTED 4 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/cbit/htab.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "misc.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct htab_internal { 9 | size_t length; 10 | size_t capacity; 11 | void *base; 12 | char hi_storage[1]; // see vec.h 13 | }; 14 | 15 | /* Declare the helper functions for a htab key type without static - put this 16 | * in the .h and IMPL_HTAB_KEY in a .c. */ 17 | #define DECL_EXTERN_HTAB_KEY( \ 18 | name, \ 19 | /* The key type. */ \ 20 | key_ty \ 21 | ) \ 22 | DO_DECL_HTAB_KEY(name, key_ty, ) 23 | 24 | /* Declare and implement the helper functions static inline. For the argument 25 | * meanings, see IMPL_HTAB_KEY. */ 26 | #define DECL_STATIC_HTAB_KEY( \ 27 | name, \ 28 | key_ty, \ 29 | hash_func, \ 30 | eq_func, \ 31 | null_func, \ 32 | nil_byte \ 33 | ) \ 34 | DO_DECL_HTAB_KEY(name, key_ty, UNUSED_STATIC_INLINE); \ 35 | IMPL_HTAB_KEY(name, hash_func, eq_func, null_func, nil_byte) 36 | 37 | #define DO_DECL_HTAB_KEY(name, key_ty, func_decl) \ 38 | typedef key_ty __htab_key_key_ty_##name; \ 39 | DO_DECL_HTAB_KEY_(name, __htab_key_key_ty_##name, func_decl) 40 | #define DO_DECL_HTAB_KEY_(name, key_ty, func_decl) \ 41 | func_decl \ 42 | void *__htab_key_lookup_##name(struct htab_internal *restrict hi, \ 43 | const key_ty *restrict key, \ 44 | size_t entry_size, \ 45 | bool add); \ 46 | func_decl \ 47 | void __htab_key_removeat_##name(struct htab_internal *restrict hi, \ 48 | void *op, \ 49 | size_t entry_size); \ 50 | func_decl \ 51 | void __htab_key_memset_##name(void *ptr, size_t size); \ 52 | func_decl \ 53 | bool __htab_key_is_null_##name(const key_ty *bucket); \ 54 | func_decl \ 55 | void __htab_key_resize_##name(struct htab_internal *restrict hi, \ 56 | size_t size, \ 57 | size_t entry_size) \ 58 | 59 | #define IMPL_HTAB_KEY(name, \ 60 | /* size_t hash_func(const ty *) - hash. \ 61 | * May be a macro. */ \ 62 | hash_func, \ 63 | /* bool eq_func(const ty *stored, const ty *queried) - check whether the \ 64 | * stored key is equal to the requested key (which is assumed to be valid). \ 65 | * May be a macro. */ \ 66 | eq_func, \ 67 | /* bool null_func(const ty *) - check whether the stored key is a \ 68 | * special invalid marker. \ 69 | * May be a macro. */ \ 70 | null_func, \ 71 | /* uint8_t nil_byte - which byte to memset to initially. \ 72 | * null_func() must be true. */ \ 73 | nil_byte \ 74 | ) \ 75 | DO_IMPL_HTAB_KEY(name, __htab_key_key_ty_##name, hash_func, \ 76 | eq_func, null_func, nil_byte) 77 | #define DO_IMPL_HTAB_KEY(name, key_ty, hash_func, eq_func, null_func, nil_byte) \ 78 | void __htab_key_resize_##name(struct htab_internal *restrict hi, \ 79 | size_t capacity, \ 80 | size_t entry_size); \ 81 | void *__htab_key_lookup_##name(struct htab_internal *restrict hi, \ 82 | const key_ty *restrict key, \ 83 | size_t entry_size, \ 84 | bool add) { \ 85 | if (add && \ 86 | hi->capacity * 2 <= hi->length * 3) \ 87 | __htab_key_resize_##name(hi, hi->capacity * 2, entry_size); \ 88 | size_t capacity = hi->capacity; \ 89 | size_t hash = (hash_func(key)) % capacity; \ 90 | size_t i = hash; \ 91 | char *buckets = hi->base; \ 92 | do { \ 93 | key_ty *bucket = (void *) (buckets + i * entry_size); \ 94 | if (null_func(bucket)) { \ 95 | if (add) { \ 96 | hi->length++; \ 97 | return bucket; \ 98 | } else { \ 99 | return NULL; \ 100 | } \ 101 | } \ 102 | if (eq_func(bucket, key)) \ 103 | return bucket; \ 104 | } while (i = (i + 1) == capacity ? 0 : (i + 1), i != hash); \ 105 | return NULL; \ 106 | } \ 107 | \ 108 | /* a bit inefficient */ \ 109 | void __htab_key_removeat_##name(struct htab_internal *restrict hi, \ 110 | void *op, \ 111 | size_t entry_size) { \ 112 | size_t capacity = hi->capacity; \ 113 | char *buckets = (char *) hi->base; \ 114 | key_ty *end = (void *) buckets + capacity * entry_size; \ 115 | key_ty *hole = op; \ 116 | key_ty *cur = hole; \ 117 | while (1) { \ 118 | if (cur == end) \ 119 | cur = (void *) buckets; \ 120 | cur = (void *) ((char *) cur + entry_size); \ 121 | if (cur == hole || null_func(cur)) { \ 122 | /* all of the elements in this chain have starting \ 123 | * positions (hashes) strictly 'after' the hole position, \ 124 | * so we can't move any of them into the hole. but that \ 125 | * also means it's safe to just erase the hole. */ \ 126 | memset(hole, nil_byte, sizeof(key_ty)); \ 127 | break; \ 128 | } \ 129 | size_t cur_hash = (hash_func(cur)) % capacity; \ 130 | key_ty *cur_chain_first = (void *) buckets + cur_hash * entry_size; \ 131 | bool cf_after_hole /* cyclically */ = hole <= cur ? \ 132 | (hole < cur_chain_first) : \ 133 | (cur < cur_chain_first && cur_chain_first <= hole); \ 134 | if (!cf_after_hole) { \ 135 | memcpy(hole, cur, entry_size); \ 136 | hole = cur; \ 137 | } \ 138 | } \ 139 | hi->length--; \ 140 | } \ 141 | void __htab_key_memset_##name(void *ptr, size_t size) { \ 142 | memset(ptr, (nil_byte), size); \ 143 | } \ 144 | bool __htab_key_is_null_##name(const key_ty *bucket) { \ 145 | return null_func(bucket); \ 146 | } \ 147 | void __htab_key_resize_##name(struct htab_internal *restrict hi, \ 148 | size_t size, \ 149 | size_t entry_size) { \ 150 | size_t old_size = hi->capacity * entry_size; \ 151 | size_t new_size = safe_mul(size, entry_size); \ 152 | void *new_buf = malloc(new_size); \ 153 | memset(new_buf, (nil_byte), new_size); \ 154 | struct htab_internal temp; \ 155 | temp.length = 0; \ 156 | temp.capacity = size; \ 157 | temp.base = new_buf; \ 158 | for (size_t i = 0; i < old_size; i += entry_size) { \ 159 | key_ty *bucket = (void *) ((char *) hi->base + i); \ 160 | if (!null_func(bucket)) { \ 161 | memcpy( \ 162 | __htab_key_lookup_##name(&temp, bucket, entry_size, \ 163 | true), \ 164 | bucket, \ 165 | entry_size); \ 166 | } \ 167 | } \ 168 | hi->capacity = size; \ 169 | if (hi->base != hi->hi_storage) \ 170 | free(hi->base); \ 171 | hi->base = new_buf; \ 172 | } \ 173 | typedef char __htab_want_semicolon_here_##name 174 | 175 | #define DECL_HTAB( \ 176 | name, \ 177 | /* The name parameter to DECL_HTAB_KEY */ \ 178 | key_name, \ 179 | value_ty) \ 180 | typedef __htab_key_key_ty_##key_name __htab_key_ty_##name; \ 181 | typedef value_ty __htab_value_ty_##name; \ 182 | \ 183 | DO_DECL_HTAB(name, \ 184 | __htab_key_ty_##name, \ 185 | __htab_value_ty_##name, \ 186 | struct htab_bucket_##name, \ 187 | struct htab_##name, \ 188 | key_name) 189 | 190 | #define DO_DECL_HTAB(name, key_ty, value_ty, bucket_ty, htab_ty, key_name) \ 191 | bucket_ty { \ 192 | key_ty key; \ 193 | value_ty value; \ 194 | }; \ 195 | htab_ty { \ 196 | union { \ 197 | struct htab_internal hi; \ 198 | struct { \ 199 | size_t length; \ 200 | size_t capacity; \ 201 | bucket_ty *base; \ 202 | bucket_ty storage[1]; \ 203 | }; \ 204 | }; \ 205 | }; \ 206 | UNUSED_STATIC_INLINE \ 207 | bucket_ty *htab_getbucket_##name(htab_ty *restrict ht, \ 208 | const key_ty *restrict key) { \ 209 | return __htab_key_lookup_##key_name(&ht->hi, key, sizeof(bucket_ty), \ 210 | false); \ 211 | } \ 212 | UNUSED_STATIC_INLINE \ 213 | value_ty *htab_getp_##name(const htab_ty *restrict ht, \ 214 | const key_ty *restrict key) { \ 215 | bucket_ty *bucket = htab_getbucket_##name((void *) ht, key); \ 216 | return bucket ? &bucket->value : NULL; \ 217 | } \ 218 | UNUSED_STATIC_INLINE \ 219 | bucket_ty *htab_setbucket_##name(htab_ty *restrict ht, \ 220 | const key_ty *restrict key) { \ 221 | return __htab_key_lookup_##key_name(&ht->hi, key, sizeof(bucket_ty), \ 222 | true); \ 223 | } \ 224 | UNUSED_STATIC_INLINE \ 225 | value_ty *htab_setp_##name(const htab_ty *restrict ht, \ 226 | const key_ty *restrict key, \ 227 | bool *new_p) { \ 228 | bucket_ty *bucket = htab_setbucket_##name((void *) ht, key); \ 229 | bool new = false; \ 230 | if (__htab_key_is_null_##key_name(&bucket->key)) { \ 231 | bucket->key = *key; \ 232 | new = true; \ 233 | } else { \ 234 | new = false; \ 235 | } \ 236 | if (new_p) \ 237 | *new_p = new; \ 238 | return &bucket->value; \ 239 | } \ 240 | UNUSED_STATIC_INLINE \ 241 | bool htab_remove_##name(htab_ty *restrict ht, const key_ty *restrict key) { \ 242 | void *op = __htab_key_lookup_##key_name(&ht->hi, key, sizeof(bucket_ty), \ 243 | false); \ 244 | if (!op) \ 245 | return false; \ 246 | __htab_key_removeat_##key_name(&ht->hi, op, sizeof(bucket_ty)); \ 247 | return true; \ 248 | } \ 249 | UNUSED_STATIC_INLINE \ 250 | void htab_removeat_##name(htab_ty *restrict ht, bucket_ty *op) { \ 251 | __htab_key_removeat_##key_name(&ht->hi, op, sizeof(bucket_ty)); \ 252 | } \ 253 | UNUSED_STATIC_INLINE \ 254 | void __htab_memset_##name(void *ptr, size_t size) { \ 255 | return __htab_key_memset_##key_name(ptr, size); \ 256 | } \ 257 | UNUSED_STATIC_INLINE \ 258 | bool __htab_is_null_##name(const key_ty *bucket) { \ 259 | return __htab_key_is_null_##key_name(bucket); \ 260 | } \ 261 | UNUSED_STATIC_INLINE \ 262 | void htab_resize_##name(htab_ty *ht, size_t size) { \ 263 | return __htab_key_resize_##key_name(&ht->hi, size, sizeof(bucket_ty)); \ 264 | } \ 265 | UNUSED_STATIC_INLINE \ 266 | void htab_free_storage_##name(htab_ty *ht) { \ 267 | if (ht->base != ht->storage) \ 268 | free(ht->base); \ 269 | } \ 270 | typedef char __plz_end_decl_htab_with_semicolon_##name 271 | 272 | #define HTAB_STORAGE_CAPA(name, n) \ 273 | struct { \ 274 | struct htab_##name h; \ 275 | struct htab_bucket_##name rest[(n)-1]; \ 276 | } 277 | 278 | #define HTAB_STORAGE(name) \ 279 | HTAB_STORAGE_CAPA(name, 5) 280 | 281 | #define HTAB_STORAGE_INIT(hs, name) do { \ 282 | struct htab_##name *h = &(hs)->h; \ 283 | h->length = 0; \ 284 | h->capacity = (sizeof((hs)->rest) / sizeof(struct htab_bucket_##name)) + 1; \ 285 | h->base = h->storage; \ 286 | __htab_memset_##name(h->base, \ 287 | h->capacity * sizeof(struct htab_bucket_##name)); \ 288 | } while (0) 289 | 290 | /* only works if nil_byte is 0 */ 291 | #define HTAB_STORAGE_INIT_STATIC(hs, name) \ 292 | {{0, \ 293 | (sizeof((hs)->rest) / sizeof(struct htab_bucket_##name)) + 1, \ 294 | (hs)->h.storage \ 295 | }} 296 | 297 | #define HTAB_FOREACH(ht, key_var, val_var, name) \ 298 | LET(struct htab_##name *__htfe_ht = (ht)) \ 299 | for (size_t __htfe_bucket = 0; \ 300 | __htfe_bucket < __htfe_ht->capacity; \ 301 | __htfe_bucket++) \ 302 | if(__htab_is_null_##name(&__htfe_ht->base[__htfe_bucket].key)) \ 303 | continue; \ 304 | else \ 305 | LET_LOOP(key_var = &__htfe_ht->base[__htfe_bucket].key) \ 306 | LET_LOOP(val_var = &__htfe_ht->base[__htfe_bucket].value) 307 | 308 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/cbit/misc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define UNUSED_STATIC_INLINE __attribute__((unused)) static inline 4 | 5 | #define LET_LOOP__(expr, ctr) \ 6 | if (0) \ 7 | __done_##ctr: continue; \ 8 | else if (0) \ 9 | __break_##ctr: break; \ 10 | else \ 11 | for (expr; ;) \ 12 | if (1) \ 13 | goto __body_##ctr; \ 14 | else \ 15 | for (;;) \ 16 | if (1) \ 17 | goto __break_##ctr; \ 18 | else \ 19 | for (;;) \ 20 | if (1) \ 21 | goto __done_##ctr; \ 22 | else \ 23 | __body_##ctr: 24 | 25 | #define LET_LOOP_(expr, ctr) LET_LOOP__(expr, ctr) 26 | #define LET_LOOP(expr) LET_LOOP_(expr, __COUNTER__) 27 | 28 | #define LET__(expr, ctr) \ 29 | if (0) \ 30 | __done_##ctr:; \ 31 | else \ 32 | for (expr; ;) \ 33 | if (1) \ 34 | goto __body_##ctr; \ 35 | else \ 36 | for (;;) \ 37 | if (1) \ 38 | goto __done_##ctr; \ 39 | else \ 40 | __body_##ctr: 41 | 42 | #define LET_(expr, ctr) LET__(expr, ctr) 43 | #define LET(expr) LET_(expr, __COUNTER__) 44 | 45 | /* XXX */ 46 | #define safe_mul(a, b) ((a) * (b)) 47 | #define safe_add(a, b) ((a) + (b)) 48 | 49 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/cbit/vec.c: -------------------------------------------------------------------------------- 1 | #include "vec.h" 2 | #include 3 | #include 4 | 5 | void vec_realloc_internal(struct vec_internal *vi, size_t new_capacity, 6 | size_t esize) { 7 | if (new_capacity == 0) 8 | abort(); 9 | size_t new_size = safe_mul(new_capacity, esize); 10 | if (vi->els == vi->storage) { 11 | void *new = malloc(new_size); 12 | size_t min_cap = new_capacity < vi->capacity ? new_capacity : vi->capacity; 13 | memcpy(new, vi->els, min_cap * esize); 14 | vi->els = new; 15 | } else { 16 | vi->els = realloc(vi->els, new_size); 17 | } 18 | vi->capacity = new_capacity; 19 | } 20 | void vec_realloc_internal_as_necessary(struct vec_internal *vi, 21 | size_t min_capacity, 22 | size_t esize) { 23 | if (min_capacity > vi->capacity) 24 | vec_realloc_internal(vi, safe_mul(vi->capacity, 2), esize); 25 | else if (min_capacity < vi->capacity / 3) 26 | vec_realloc_internal(vi, vi->capacity / 3, esize); 27 | } 28 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/cbit/vec.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "misc.h" 3 | #include 4 | #include 5 | 6 | struct vec_internal { 7 | size_t length; 8 | size_t capacity; 9 | void *els; 10 | char storage[1]; // must be at least one byte so vec_free works correctly 11 | }; 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | void vec_realloc_internal(struct vec_internal *vi, size_t new_capacity, 18 | size_t esize); 19 | void vec_realloc_internal_as_necessary(struct vec_internal *vi, 20 | size_t min_capacity, 21 | size_t esize); 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | 26 | #define DECL_VEC(ty, name) \ 27 | typedef ty __VEC_TY_##name; \ 28 | struct vec_##name { \ 29 | union { \ 30 | struct vec_internal vi; \ 31 | struct { \ 32 | size_t length; \ 33 | size_t capacity; \ 34 | VEC_TY(name) *els; \ 35 | VEC_TY(name) storage[1]; \ 36 | }; \ 37 | }; \ 38 | }; \ 39 | UNUSED_STATIC_INLINE \ 40 | void vec_free_storage_##name(struct vec_##name *v) { \ 41 | if (v->els != v->storage) \ 42 | free(v->els); \ 43 | } \ 44 | UNUSED_STATIC_INLINE \ 45 | struct vec_##name vec_borrow##name(VEC_TY(name) *els, size_t length) { \ 46 | struct vec_##name v; \ 47 | v.length = v.capacity = length; \ 48 | v.els = els; \ 49 | return v; \ 50 | } \ 51 | UNUSED_STATIC_INLINE \ 52 | void vec_realloc_##name(struct vec_##name *v, size_t new_capacity) { \ 53 | vec_realloc_internal(&v->vi, new_capacity, sizeof(v->els[0])); \ 54 | } \ 55 | UNUSED_STATIC_INLINE \ 56 | void vec_resize_##name(struct vec_##name *v, size_t new_length) { \ 57 | if (new_length >= v->capacity || new_length < v->capacity / 3) \ 58 | vec_realloc_internal_as_necessary(&v->vi, new_length, \ 59 | sizeof(v->els[0])); \ 60 | v->length = new_length; \ 61 | } \ 62 | UNUSED_STATIC_INLINE \ 63 | VEC_TY(name) *vec_appendp_##name(struct vec_##name *v) { \ 64 | size_t i = v->length++; \ 65 | if (i == v->capacity) \ 66 | vec_realloc_internal_as_necessary(&v->vi, i + 1, sizeof(v->els[0])); \ 67 | return &v->els[i]; \ 68 | } \ 69 | UNUSED_STATIC_INLINE \ 70 | void vec_append_##name(struct vec_##name *v, VEC_TY(name) val) { \ 71 | *vec_appendp_##name(v) = val; \ 72 | } \ 73 | UNUSED_STATIC_INLINE \ 74 | VEC_TY(name) vec_pop_##name(struct vec_##name *v) { \ 75 | size_t i = v->length - 1; \ 76 | VEC_TY(name) ret = v->els[i]; \ 77 | if (v->els != v->storage && i < v->capacity / 3) \ 78 | vec_realloc_internal_as_necessary(&v->vi, i, sizeof(v->els[0])); \ 79 | v->length = i; \ 80 | return ret; \ 81 | } \ 82 | UNUSED_STATIC_INLINE \ 83 | void vec_concat_##name(struct vec_##name *v1, const struct vec_##name *v2) { \ 84 | size_t l1 = v1->length, l2 = v2->length; \ 85 | vec_resize_##name(v1, safe_add(l1, l2)); \ 86 | memcpy(&v1->els[l1], v2->els, l2 * sizeof(v1->els[0])); \ 87 | } \ 88 | UNUSED_STATIC_INLINE \ 89 | void vec_add_space_##name(struct vec_##name *v, size_t idx, size_t num) { \ 90 | /* todo optimize */ \ 91 | size_t orig = v->length; \ 92 | vec_resize_##name(v, orig + num); \ 93 | memmove(&v->els[idx + num], &v->els[idx], \ 94 | (orig - idx) * sizeof(v->els[0])); \ 95 | } \ 96 | UNUSED_STATIC_INLINE \ 97 | void vec_remove_##name(struct vec_##name *v, size_t idx, size_t num) { \ 98 | /* todo optimize */ \ 99 | size_t orig = v->length; \ 100 | memmove(&v->els[idx], &v->els[idx + num], \ 101 | (orig - (idx + num)) * sizeof(v->els[0])); \ 102 | vec_resize_##name(v, orig - num); \ 103 | } \ 104 | typedef char __plz_end_decl_vec_with_semicolon_##name 105 | 106 | #define VEC_TY(name) __VEC_TY_##name 107 | 108 | #define VEC_STORAGE_CAPA(name, n) \ 109 | struct { \ 110 | struct vec_##name v; \ 111 | VEC_TY(name) rest[(n)-1]; \ 112 | } 113 | 114 | #define VEC_STORAGE(ty) \ 115 | VEC_STORAGE_CAPA(ty, 5) 116 | 117 | #define VEC_STORAGE_INIT(vs, name) do { \ 118 | struct vec_##name *v = &(vs)->v; \ 119 | v->length = 0; \ 120 | v->capacity = (sizeof((vs)->rest) / sizeof(VEC_TY(name))) + 1; \ 121 | v->els = v->storage; \ 122 | } while (0) 123 | 124 | #define VEC_STORAGE_INIT_STATIC(vs, name) \ 125 | {{0, \ 126 | (sizeof((vs)->rest) / sizeof((vs)->rest[0])) + 1, \ 127 | (vs)->v.storage \ 128 | }} 129 | 130 | /* guaranteed to *not* cache vec->length - pretty simple */ 131 | 132 | #define VEC_FOREACH(vec, idx_var, ptr_var, name) \ 133 | LET(struct vec_##name *__vfe_v = (vec)) \ 134 | for (size_t idx_var = 0; idx_var < __vfe_v->length; idx_var++) \ 135 | LET_LOOP(ptr_var = &__vfe_v->els[idx_var]) 136 | 137 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/darwin/interpose.c: -------------------------------------------------------------------------------- 1 | #ifdef __APPLE__ 2 | 3 | #include 4 | #include 5 | 6 | #include "substitute.h" 7 | #include "substitute-internal.h" 8 | #include "darwin/read.h" 9 | 10 | struct interpose_state { 11 | size_t nsegments; 12 | segment_command_x **segments; 13 | size_t max_segments; 14 | uintptr_t slide; 15 | const struct substitute_import_hook *hooks; 16 | size_t nhooks; 17 | segment_command_x *stack_segments[32]; 18 | }; 19 | 20 | static int try_bind_section(void *bind, size_t size, 21 | const struct interpose_state *st, bool lazy) { 22 | void *ptr = bind, *end = bind + size; 23 | char *sym = NULL; 24 | uint8_t type = lazy ? BIND_TYPE_POINTER : 0; 25 | uint64_t addend = 0; 26 | uint64_t offset = 0, added_offset; 27 | void *segment = NULL; 28 | while (ptr < end) { 29 | uint8_t byte = *(uint8_t *) ptr; 30 | ptr++; 31 | uint8_t immediate = byte & BIND_IMMEDIATE_MASK; 32 | uint8_t opcode = byte & BIND_OPCODE_MASK; 33 | 34 | uint64_t count, stride; 35 | 36 | switch(opcode) { 37 | case BIND_OPCODE_DONE: 38 | case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: 39 | case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: 40 | break; 41 | case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: 42 | read_leb128(&ptr, end, false, NULL); 43 | break; 44 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: 45 | read_cstring(&ptr, end, &sym); 46 | /* ignoring flags for now */ 47 | break; 48 | case BIND_OPCODE_SET_TYPE_IMM: 49 | type = immediate; 50 | break; 51 | case BIND_OPCODE_SET_ADDEND_SLEB: 52 | read_leb128(&ptr, end, true, &addend); 53 | break; 54 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: 55 | if (immediate < st->nsegments) 56 | segment = (void *) (st->segments[immediate]->vmaddr + st->slide); 57 | read_leb128(&ptr, end, false, &offset); 58 | break; 59 | case BIND_OPCODE_ADD_ADDR_ULEB: 60 | read_leb128(&ptr, end, false, &added_offset); 61 | offset += added_offset; 62 | break; 63 | case BIND_OPCODE_DO_BIND: 64 | count = 1; 65 | stride = sizeof(void *); 66 | goto bind; 67 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: 68 | count = 1; 69 | read_leb128(&ptr, end, false, &stride); 70 | stride += sizeof(void *); 71 | goto bind; 72 | case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: 73 | count = 1; 74 | stride = immediate * sizeof(void *) + sizeof(void *); 75 | goto bind; 76 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: 77 | read_leb128(&ptr, end, false, &count); 78 | read_leb128(&ptr, end, false, &stride); 79 | stride += sizeof(void *); 80 | goto bind; 81 | bind: 82 | if (segment && sym) { 83 | const struct substitute_import_hook *h; 84 | size_t i; 85 | for (i = 0; i < st->nhooks; i++) { 86 | h = &st->hooks[i]; 87 | if (!strcmp(sym, h->name)) 88 | break; 89 | } 90 | if (i != st->nhooks) { 91 | while (count--) { 92 | uintptr_t new = (uintptr_t) h->replacement + 93 | (intptr_t) addend; 94 | uintptr_t old; 95 | void *p = (void *) (segment + offset); 96 | switch (type) { 97 | case BIND_TYPE_POINTER: { 98 | old = __atomic_exchange_n((uintptr_t *) p, 99 | new, __ATOMIC_RELAXED); 100 | break; 101 | } 102 | case BIND_TYPE_TEXT_ABSOLUTE32: { 103 | if ((uint32_t) new != new) { 104 | /* text rels should only show up on i386, where 105 | * this is impossible... */ 106 | substitute_panic("bad TEXT_ABSOLUTE32 rel\n"); 107 | } 108 | old = __atomic_exchange_n((uint32_t *) p, 109 | (uint32_t) new, 110 | __ATOMIC_RELAXED); 111 | break; 112 | } 113 | case BIND_TYPE_TEXT_PCREL32: { 114 | uintptr_t pc = (uintptr_t) p + 4; 115 | uintptr_t rel = new - pc; 116 | if ((uint32_t) rel != rel) { 117 | /* ditto */ 118 | substitute_panic("bad TEXT_ABSOLUTE32 rel\n"); 119 | } 120 | old = __atomic_exchange_n((uint32_t *) p, 121 | (uint32_t) rel, 122 | __ATOMIC_RELAXED); 123 | old += pc; 124 | break; 125 | } 126 | default: 127 | substitute_panic("unknown relocation type\n"); 128 | break; 129 | } 130 | if (h->old_ptr) 131 | *(uintptr_t *) h->old_ptr = old - addend; 132 | offset += stride; 133 | } 134 | break; 135 | } 136 | } 137 | offset += count * stride; 138 | break; 139 | } 140 | } 141 | return SUBSTITUTE_OK; 142 | } 143 | 144 | static void *off_to_addr(const struct interpose_state *st, uint32_t off) { 145 | for (size_t i = 0; i < st->nsegments; i++) { 146 | const segment_command_x *sc = st->segments[i]; 147 | if ((off - sc->fileoff) < sc->filesize) 148 | return (void *) (sc->vmaddr + st->slide + off - sc->fileoff); 149 | } 150 | return NULL; 151 | } 152 | 153 | EXPORT 154 | int substitute_interpose_imports(const struct substitute_image *image, 155 | const struct substitute_import_hook *hooks, 156 | size_t nhooks, 157 | struct substitute_import_hook_record **recordp, 158 | int options) { 159 | int ret = SUBSTITUTE_OK; 160 | 161 | if (options != 0) 162 | substitute_panic("%s: unrecognized options\n", __func__); 163 | 164 | if (recordp) 165 | *recordp = NULL; 166 | 167 | struct interpose_state st; 168 | st.slide = image->slide; 169 | st.nsegments = 0; 170 | st.hooks = hooks; 171 | st.nhooks = nhooks; 172 | st.segments = st.stack_segments; 173 | st.max_segments = sizeof(st.stack_segments) / sizeof(*st.stack_segments); 174 | 175 | const mach_header_x *mh = image->image_header; 176 | const struct load_command *lc = (void *) (mh + 1); 177 | for (uint32_t i = 0; i < mh->ncmds; i++) { 178 | if (lc->cmd == LC_SEGMENT_X) { 179 | segment_command_x *sc = (void *) lc; 180 | if (st.nsegments == st.max_segments) { 181 | segment_command_x **new = calloc(st.nsegments * 2, 182 | sizeof(*st.segments)); 183 | if (!new) 184 | substitute_panic("%s: out of memory\n", __func__); 185 | memcpy(new, st.segments, st.nsegments * sizeof(*st.segments)); 186 | if (st.segments != st.stack_segments) 187 | free(st.segments); 188 | st.segments = new; 189 | } 190 | st.segments[st.nsegments++] = sc; 191 | } 192 | lc = (void *) lc + lc->cmdsize; 193 | } 194 | 195 | lc = (void *) (mh + 1); 196 | for (uint32_t i = 0; i < mh->ncmds; i++) { 197 | if (lc->cmd == LC_DYLD_INFO || lc->cmd == LC_DYLD_INFO_ONLY) { 198 | struct dyld_info_command *dc = (void *) lc; 199 | int ret; 200 | if ((ret = try_bind_section(off_to_addr(&st, dc->bind_off), 201 | dc->bind_size, &st, false)) || 202 | (ret = try_bind_section(off_to_addr(&st, dc->weak_bind_off), 203 | dc->weak_bind_size, &st, false)) || 204 | (ret = try_bind_section(off_to_addr(&st, dc->lazy_bind_off), 205 | dc->lazy_bind_size, &st, true))) 206 | goto fail; 207 | 208 | break; 209 | } 210 | lc = (void *) lc + lc->cmdsize; 211 | } 212 | fail: 213 | if (st.segments != st.stack_segments) 214 | free(st.segments); 215 | return ret; 216 | } 217 | 218 | #endif /* __APPLE__ */ 219 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/darwin/mach-decls.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | kern_return_t mach_vm_read_overwrite(vm_map_t, mach_vm_address_t, 6 | mach_vm_size_t, mach_vm_address_t, 7 | mach_vm_size_t *); 8 | kern_return_t mach_vm_remap(vm_map_t, mach_vm_address_t *, mach_vm_size_t, 9 | mach_vm_offset_t, int, vm_map_t, mach_vm_address_t, 10 | boolean_t, vm_prot_t *, vm_prot_t *, vm_inherit_t); 11 | kern_return_t mach_vm_write(vm_map_t, mach_vm_address_t, vm_offset_t, 12 | mach_msg_type_number_t); 13 | kern_return_t mach_vm_allocate(vm_map_t, mach_vm_address_t *, mach_vm_size_t, int); 14 | kern_return_t mach_vm_deallocate(vm_map_t, mach_vm_address_t, mach_vm_size_t); 15 | kern_return_t mach_vm_region(vm_map_t, mach_vm_address_t *, mach_vm_size_t *, 16 | vm_region_flavor_t, vm_region_info_t, 17 | mach_msg_type_number_t *, mach_port_t *); 18 | 19 | /* bootstrap.h */ 20 | extern mach_port_t bootstrap_port; 21 | typedef char name_t[128]; 22 | kern_return_t bootstrap_check_in(mach_port_t, const name_t, mach_port_t *); 23 | kern_return_t bootstrap_look_up(mach_port_t, const name_t, mach_port_t *); 24 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/darwin/manual-syscall.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define GEN_SYSCALL(name, num) \ 4 | __asm__(".private_extern _manual_" #name "\n" \ 5 | ".pushsection __TEXT,__text,regular,pure_instructions\n" \ 6 | GEN_SYSCALL_PRE(name) \ 7 | "_manual_" #name ":\n" \ 8 | ".set num, " #num "\n" \ 9 | GEN_SYSCALL_INNER() \ 10 | ".popsection\n") 11 | #define GEN_SYSCALL_PRE(name) 12 | 13 | #if defined(__x86_64__) 14 | /* Look at me, I'm different! */ 15 | #define GEN_SYSCALL_INNER() \ 16 | ".if num < 0\n" \ 17 | "mov $(-num | 1 << 24), %eax\n" \ 18 | ".else\n" \ 19 | "mov $( num | 2 << 24), %eax\n" \ 20 | ".endif\n" \ 21 | "mov %rcx, %r10\n" \ 22 | "syscall\n" \ 23 | "ret\n" 24 | 25 | #elif defined(__i386__) 26 | #define GEN_SYSCALL_INNER() \ 27 | "mov $num, %eax\n" \ 28 | "call 0f\n" \ 29 | "0: pop %edx\n" \ 30 | "add $(1f-0b), %edx\n" \ 31 | "mov %esp, %ecx\n" \ 32 | "sysenter\n" \ 33 | "1: ret\n" 34 | #elif defined(__arm__) 35 | #ifdef __thumb__ 36 | #undef GEN_SYSCALL_PRE 37 | #define GEN_SYSCALL_PRE(name) \ 38 | ".thumb_func _manual_" #name "\n" \ 39 | ".align 2\n" 40 | #endif 41 | #define GEN_SYSCALL_INNER() \ 42 | "mov r12, sp\n" \ 43 | "push {r4-r6}\n" \ 44 | "ldm r12, {r4-r6}\n" \ 45 | "mov r12, #num\n" \ 46 | "svc #0x80\n" \ 47 | "pop {r4-r6}\n" \ 48 | "bx lr\n" 49 | #elif defined(__arm64__) 50 | #define GEN_SYSCALL_INNER() \ 51 | "mov x16, #num\n" \ 52 | "svc #0x80\n" \ 53 | "ret\n" 54 | #else 55 | #error ? 56 | #endif 57 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/darwin/objc-asm.S: -------------------------------------------------------------------------------- 1 | #include "objc.h" 2 | .text 3 | .align _PAGE_SHIFT 4 | #ifdef __arm__ 5 | .thumb_func _remap_start 6 | .thumb 7 | #endif 8 | .private_extern _remap_start 9 | _remap_start: 10 | 11 | .set i, 0 12 | #define my_rpe (0b + (_PAGE_SIZE \ 13 | - i * TRAMPOLINE_SIZE \ 14 | + i * TRAMP_INFO_PAGE_ENTRY_SIZE)) 15 | .rept TRAMPOLINES_PER_PAGE 16 | 0: 17 | #if defined(__x86_64__) 18 | /* double push for align */ 19 | push %rdi; push %rsi; push %rdx; push %rcx; push %r8; push %r9; push %r9 20 | lea my_rpe(%rip), %rdx 21 | mov 8(%rdx), %rdi 22 | mov 16(%rdx), %rsi 23 | call *(%rdx) 24 | pop %r9; pop %r9; pop %r8; pop %rcx; pop %rdx; pop %rsi; pop %rdi 25 | jmp *%rax 26 | #elif defined(__i386__) 27 | call 1f 28 | 1: 29 | pop %edx 30 | lea my_rpe-1b(%edx), %edx 31 | push 8(%edx) 32 | push 4(%edx) 33 | call *(%edx) 34 | add $$8, %esp 35 | jmp *%eax 36 | #elif defined(__arm__) 37 | push {r0-r4, lr} /* r4 for align */ 38 | mov r3, #:lower16:(my_rpe - (1f + 2)) 39 | add r3, pc 40 | 1: 41 | ldr r0, [r3, #4] 42 | ldr r1, [r3, #8] 43 | ldr r2, [r3] 44 | blx r2 45 | mov r9, r0 46 | pop {r0-r4, lr} 47 | bx r9 48 | #elif defined(__arm64__) 49 | stp x30, x8, [sp, #-0x10]! 50 | stp x7, x6, [sp, #-0x10]! 51 | stp x5, x4, [sp, #-0x10]! 52 | stp x3, x2, [sp, #-0x10]! 53 | stp x1, x0, [sp, #-0x10]! 54 | 55 | ldr x0, my_rpe+8 56 | ldr x1, my_rpe+0x10 57 | ldr x2, my_rpe 58 | #if defined(__arm64e__) 59 | blraaz x2 60 | #else 61 | blr x2 62 | #endif 63 | mov x9, x0 64 | 65 | ldp x1, x0, [sp], #0x10 66 | ldp x3, x2, [sp], #0x10 67 | ldp x5, x4, [sp], #0x10 68 | ldp x7, x6, [sp], #0x10 69 | ldp x30, x8, [sp], #0x10 70 | 71 | #if defined(__arm64e__) 72 | braaz x9 73 | #else 74 | br x9 75 | #endif 76 | #else 77 | #error No forwarding assembly definition for this arch 78 | #endif 79 | 80 | .set i, i + 1 81 | .endr 82 | 83 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/darwin/objc.c: -------------------------------------------------------------------------------- 1 | #if defined(__APPLE__) 2 | #include "substitute.h" 3 | #include "substitute-internal.h" 4 | #include "objc.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | /* These trampolines will call e->func(e->arg1, e->arg2), and jump to there, 14 | * preserving all arguments. imp_implementationWithBlock would be easier and 15 | * maybe a bit faster, but it's impossible to avoid throwing away registers 16 | * without having to ask whether the selector is stret or not. */ 17 | 18 | struct tramp_info_page_header { 19 | uint32_t magic; 20 | uint32_t version; 21 | struct tramp_info_page_entry *first_free; 22 | size_t nfree; 23 | LIST_ENTRY(tramp_info_page_header) free_pages; 24 | }; 25 | 26 | enum { 27 | TRAMP_MAGIC = 0xf00df17e, 28 | TRAMP_VERSION = 0, 29 | }; 30 | 31 | struct tramp_info_page_entry { 32 | union { 33 | struct tramp_info_page_entry *next_free; 34 | void *func; 35 | }; 36 | void *arg1; 37 | void *arg2; 38 | }; 39 | 40 | _Static_assert(TRAMP_INFO_PAGE_ENTRY_SIZE == sizeof(struct tramp_info_page_entry), 41 | "TRAMP_INFO_PAGE_ENTRY_SIZE"); 42 | _Static_assert(sizeof(struct tramp_info_page_header) + 43 | TRAMPOLINES_PER_PAGE * sizeof(struct tramp_info_page_entry) 44 | <= _PAGE_SIZE, 45 | "header+entries too big"); 46 | 47 | static pthread_mutex_t tramp_mutex = PTHREAD_MUTEX_INITIALIZER; 48 | LIST_HEAD(tramp_info_page_list, tramp_info_page_header) 49 | tramp_free_page_list = LIST_HEAD_INITIALIZER(tramp_info_page_list); 50 | 51 | extern char remap_start[]; 52 | 53 | static int get_trampoline(void *func, void *arg1, void *arg2, void *tramp_ptr) { 54 | int ret, rerrno = 0; 55 | pthread_mutex_lock(&tramp_mutex); 56 | 57 | struct tramp_info_page_header *header = LIST_FIRST(&tramp_free_page_list); 58 | if (!header) { 59 | if (PAGE_SIZE > _PAGE_SIZE) 60 | substitute_panic("%s: strange PAGE_SIZE %lx\n", 61 | __func__, (long) PAGE_SIZE); 62 | void *new_pages = mmap(NULL, _PAGE_SIZE * 2, PROT_READ | PROT_WRITE, 63 | MAP_SHARED | MAP_ANON, -1, 0); 64 | if (new_pages == MAP_FAILED) { 65 | ret = SUBSTITUTE_ERR_OOM; 66 | rerrno = errno; 67 | goto out; 68 | } 69 | vm_address_t tramp_page = (vm_address_t) new_pages; 70 | vm_prot_t cur_prot, max_prot; 71 | kern_return_t kr = vm_remap( 72 | mach_task_self(), 73 | &tramp_page, 74 | _PAGE_SIZE, 75 | _PAGE_SIZE - 1, 76 | VM_FLAGS_OVERWRITE | VM_FLAGS_FIXED, 77 | mach_task_self(), 78 | (vm_address_t) remap_start, 79 | FALSE, /* copy */ 80 | &cur_prot, 81 | &max_prot, 82 | VM_INHERIT_NONE); 83 | if (kr != KERN_SUCCESS || tramp_page != (vm_address_t) new_pages) { 84 | ret = SUBSTITUTE_ERR_VM; 85 | goto out; 86 | } 87 | header = new_pages + _PAGE_SIZE * 2 - sizeof(*header); 88 | header->magic = TRAMP_MAGIC; 89 | header->version = TRAMP_VERSION; 90 | header->first_free = NULL; 91 | header->nfree = TRAMPOLINES_PER_PAGE; 92 | LIST_INSERT_HEAD(&tramp_free_page_list, header, free_pages); 93 | } 94 | 95 | void *page = (void *) (((uintptr_t) header) & ~(_PAGE_SIZE - 1)); 96 | struct tramp_info_page_entry *entries = page; 97 | struct tramp_info_page_entry *entry = header->first_free; 98 | if (entry == NULL) { 99 | entry = &entries[TRAMPOLINES_PER_PAGE - header->nfree]; 100 | entry->next_free = NULL; 101 | } 102 | 103 | header->first_free = entry->next_free; 104 | if (--header->nfree == 0) 105 | LIST_REMOVE(header, free_pages); 106 | 107 | entry->func = func; 108 | entry->arg1 = arg1; 109 | entry->arg2 = arg2; 110 | void *tramp = (page - PAGE_SIZE) + (entry - entries) * TRAMPOLINE_SIZE; 111 | #ifdef __arm__ 112 | tramp += 1; 113 | #endif 114 | *(void **) tramp_ptr = tramp; 115 | ret = SUBSTITUTE_OK; 116 | out: 117 | pthread_mutex_unlock(&tramp_mutex); 118 | errno = rerrno; 119 | return ret; 120 | } 121 | 122 | static void free_trampoline(void *tramp) { 123 | pthread_mutex_lock(&tramp_mutex); 124 | void *page = (void *) (((uintptr_t) tramp) & ~(_PAGE_SIZE - 1)); 125 | size_t i = (tramp - page) / TRAMPOLINE_SIZE; 126 | struct tramp_info_page_entry *entries = page + _PAGE_SIZE; 127 | struct tramp_info_page_entry *entry = &entries[i]; 128 | struct tramp_info_page_header *header = page + 2 * _PAGE_SIZE - sizeof(*header); 129 | 130 | if (header->magic != TRAMP_MAGIC) 131 | substitute_panic("%s: bad pointer\n", __func__); 132 | if (header->version != TRAMP_VERSION) { 133 | /* shouldn't happen, but just in case multiple versions of this library 134 | * are mixed up */ 135 | return; 136 | } 137 | 138 | entry->next_free = header->first_free; 139 | header->first_free = entry; 140 | header->nfree++; 141 | if (header->nfree == 1) 142 | LIST_INSERT_HEAD(&tramp_free_page_list, header, free_pages); 143 | else if (header->nfree == TRAMPOLINES_PER_PAGE && 144 | /* have others? */ 145 | (LIST_FIRST(&tramp_free_page_list) != header || 146 | LIST_NEXT(header, free_pages))) { 147 | /* free the trampoline and info pages */ 148 | LIST_REMOVE(header, free_pages); 149 | munmap(page, 2 * _PAGE_SIZE); 150 | } 151 | 152 | pthread_mutex_unlock(&tramp_mutex); 153 | } 154 | 155 | static IMP dereference(IMP *old_ptr, UNUSED void *_) { 156 | return *old_ptr; 157 | } 158 | 159 | EXPORT 160 | int substitute_hook_objc_message(Class class, SEL selector, void *replacement, 161 | void *old_ptr, bool *created_imp_ptr) { 162 | int ret; 163 | Method meth = class_getInstanceMethod(class, selector); 164 | if (meth == NULL) { 165 | LOG("Attempted to hook non-existant selector \"%s\" in class \"%s\"", sel_getName(selector), class_getName(class)); 166 | return SUBSTITUTE_ERR_NO_SUCH_SELECTOR; 167 | } 168 | const char *types = method_getTypeEncoding(meth); 169 | 170 | if (created_imp_ptr) 171 | *created_imp_ptr = false; 172 | 173 | /* temporary trampoline just tries again */ 174 | IMP temp = NULL; 175 | if (old_ptr) { 176 | if ((ret = get_trampoline(dereference, old_ptr, NULL, &temp))) 177 | return ret; 178 | *(void **) old_ptr = make_sym_callable(temp); 179 | } 180 | 181 | IMP old = class_replaceMethod(class, selector, make_sym_callable(replacement), types); 182 | if (old) { 183 | if (old_ptr) 184 | *(IMP *) old_ptr = old; 185 | } else { 186 | if (old_ptr) { 187 | Class super = class_getSuperclass(class); 188 | if (!super) { 189 | /* this ought to only be possible if the method was removed in 190 | * the meantime, since we found the method above and it 191 | * couldn't have been found in a superclass, but the objc2 192 | * runtime doesn't allow removing methods. */ 193 | substitute_panic("%s: no superclass but the method didn't exist\n", 194 | __func__); 195 | } 196 | void *unsigned_ptr = 0; 197 | ret = get_trampoline(class_getMethodImplementation, super, 198 | selector, &unsigned_ptr); 199 | *(void **)old_ptr = make_sym_callable(unsigned_ptr); 200 | if (created_imp_ptr) 201 | *created_imp_ptr = true; 202 | } 203 | } 204 | 205 | if (temp) 206 | free_trampoline(temp); 207 | return SUBSTITUTE_OK; 208 | } 209 | 210 | EXPORT 211 | void substitute_free_created_imp(IMP imp) { 212 | free_trampoline(imp); 213 | } 214 | #endif 215 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/darwin/objc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /* PAGE_SIZE is not actually a constant on iOS */ 3 | #if defined(__arm64__) 4 | #define _PAGE_SHIFT 14 5 | #else 6 | #define _PAGE_SHIFT 12 7 | #endif 8 | #define _PAGE_SIZE (1 << _PAGE_SHIFT) 9 | #if defined(__x86_64__) 10 | #define TRAMPOLINE_SIZE 0x27 11 | #elif defined(__i386__) 12 | #define TRAMPOLINE_SIZE 0x19 13 | #elif defined(__arm__) 14 | #define TRAMPOLINE_SIZE 0x18 15 | #elif defined(__arm64__) 16 | #define TRAMPOLINE_SIZE 0x40 17 | #endif 18 | #ifdef __LP64__ 19 | #define TRAMP_INFO_PAGE_ENTRY_SIZE 24 20 | #else 21 | #define TRAMP_INFO_PAGE_ENTRY_SIZE 12 22 | #endif 23 | #define TRAMPOLINES_PER_PAGE (_PAGE_SIZE / TRAMPOLINE_SIZE) 24 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/darwin/read.c: -------------------------------------------------------------------------------- 1 | #include "darwin/read.h" 2 | bool read_leb128(void **ptr, void *end, bool is_signed, uint64_t *out) { 3 | uint64_t result = 0; 4 | uint8_t *p = *ptr; 5 | uint8_t bit; 6 | unsigned int shift = 0; 7 | do { 8 | if (p >= (uint8_t *) end) 9 | return false; 10 | bit = *p++; 11 | uint64_t k = bit & 0x7f; 12 | if (shift < 64) 13 | result |= k << shift; 14 | shift += 7; 15 | } while (bit & 0x80); 16 | if (is_signed && (bit & 0x40) && shift < 64) 17 | result |= ~((uint64_t) 0) << shift; 18 | *ptr = p; 19 | if (out) 20 | *out = result; 21 | return true; 22 | } 23 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/darwin/read.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | bool read_leb128(void **ptr, void *end, bool is_signed, uint64_t *out); 7 | 8 | static inline bool read_cstring(void **ptr, void *end, char **out) { 9 | char *s = *ptr; 10 | size_t maxlen = (char *) end - s; 11 | size_t len = strnlen(s, maxlen); 12 | if (len == maxlen) 13 | return false; 14 | *out = s; 15 | *ptr = s + len + 1; 16 | return true; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/darwin/substrate-compat.c: -------------------------------------------------------------------------------- 1 | #include "substitute.h" 2 | #include "substitute-internal.h" 3 | #include "execmem.h" 4 | #include "ptrauth_helpers.h" 5 | #include 6 | #include 7 | 8 | EXPORT 9 | void *SubGetImageByName(const char *filename) __asm__("_MSGetImageByName"); 10 | void *SubGetImageByName(const char *filename) { 11 | return substitute_open_image(filename); 12 | } 13 | 14 | EXPORT 15 | void *SubFindSymbol(void *image, const char *name) __asm__("_MSFindSymbol"); 16 | void *SubFindSymbol(void *image, const char *name) { 17 | if (!image) { 18 | const char *s = "SubFindSymbol: 'any image' specified, which is incredibly slow - like, 2ms on a fast x86. I'm going to do it since it seems to be somewhat common, but you should be ashamed of yourself."; 19 | LOG("%s", s); 20 | fprintf(stderr, "%s\n", s); 21 | /* and it isn't thread safe, but neither is MS */ 22 | for(uint32_t i = 0; i < _dyld_image_count(); i++) { 23 | const char *im_name = _dyld_get_image_name(i); 24 | struct substitute_image *im = substitute_open_image(im_name); 25 | if (!im) { 26 | fprintf(stderr, "(btw, couldn't open %s?)\n", im_name); 27 | continue; 28 | } 29 | void *r = SubFindSymbol(im, name); 30 | substitute_close_image(im); 31 | if (r) 32 | return r; 33 | } 34 | return NULL; 35 | } 36 | 37 | void *ptr; 38 | if (substitute_find_private_syms(image, &name, &ptr, 1)) 39 | return NULL; 40 | return ptr; 41 | } 42 | 43 | #ifdef TARGET_DIS_SUPPORTED 44 | EXPORT 45 | void SubHookFunction(void *symbol, void *replace, void **result) 46 | __asm__("_MSHookFunction"); 47 | void SubHookFunction(void *symbol, void *replace, void **result) { 48 | if (symbol == NULL || replace == NULL) { 49 | substitute_info("SubHookFunction: called with a NULL pointer. Don't do that.\n"); 50 | } 51 | struct substitute_function_hook hook = {symbol, replace, result}; 52 | int ret = substitute_hook_functions(&hook, 1, NULL, 53 | SUBSTITUTE_NO_THREAD_SAFETY); 54 | if (ret && ret != SUBSTITUTE_ERR_VM) { 55 | substitute_info("SubHookFunction: substitute_hook_functions returned %s (%p)\n", 56 | substitute_strerror(ret), make_sym_readable(symbol)); 57 | } 58 | } 59 | #endif 60 | 61 | EXPORT 62 | void SubHookMemory(void *target, const void *data, size_t size) 63 | __asm__("_MSHookMemory"); 64 | 65 | void SubHookMemory(void *target, const void *data, size_t size) { 66 | if (size == 0) return; 67 | 68 | if (target == NULL || data == NULL) { 69 | substitute_info("SubHookMemory: called with a NULL pointer. Don't do that.\n"); 70 | } 71 | struct execmem_foreign_write write = {target, data, size}; 72 | int ret = execmem_foreign_write_with_pc_patch(&write, 1, NULL, NULL); 73 | 74 | if (ret) { 75 | substitute_info("SubHookMemory: execmem_foreign_write_with_pc_patch returned %s\n", 76 | substitute_strerror(ret)); 77 | } 78 | } 79 | 80 | EXPORT 81 | void SubHookMessageEx(Class _class, SEL sel, IMP imp, IMP *result) 82 | __asm__("_MSHookMessageEx"); 83 | 84 | void SubHookMessageEx(Class _class, SEL sel, IMP imp, IMP *result) { 85 | int ret = substitute_hook_objc_message(_class, sel, imp, result, NULL); 86 | if (ret) { 87 | if (ret != SUBSTITUTE_ERR_NO_SUCH_SELECTOR) { 88 | substitute_info("SubHookMessageEx: substitute_hook_objc_message returned %s\n", 89 | substitute_strerror(ret)); 90 | } 91 | if (result) *result = nil; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/dis.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "substitute-internal.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #define INLINE __attribute__((always_inline)) 10 | #define NOINLINE __attribute__((noinline)) 11 | 12 | static INLINE inline void unaligned_w64(void *ptr, uint64_t val) { 13 | __builtin_memcpy(ptr, &val, 8); 14 | } 15 | static INLINE inline void unaligned_w32(void *ptr, uint32_t val) { 16 | __builtin_memcpy(ptr, &val, 4); 17 | } 18 | static INLINE inline void unaligned_w16(void *ptr, uint16_t val) { 19 | __builtin_memcpy(ptr, &val, 2); 20 | } 21 | static INLINE inline uint64_t unaligned_r64(const void *ptr) { 22 | uint64_t val; 23 | __builtin_memcpy(&val, ptr, 8); 24 | return val; 25 | } 26 | static INLINE inline uint32_t unaligned_r32(const void *ptr) { 27 | uint32_t val; 28 | __builtin_memcpy(&val, ptr, 4); 29 | return val; 30 | } 31 | static INLINE inline uint16_t unaligned_r16(const void *ptr) { 32 | uint16_t val; 33 | __builtin_memcpy(&val, ptr, 2); 34 | return val; 35 | } 36 | 37 | struct bitslice_run { 38 | int inpos, outpos, len; 39 | }; 40 | 41 | struct bitslice { 42 | int nruns; 43 | const struct bitslice_run *runs; 44 | }; 45 | 46 | static inline uint_tptr sext(unsigned val, int bits) { 47 | return val & (1 << (bits - 1)) ? ((int)val - (1 << bits)) : (int)val; 48 | } 49 | 50 | static inline unsigned bs_get(struct bitslice bs, unsigned op) { 51 | unsigned ret = 0; 52 | for(int i = 0; i < bs.nruns; i++) { 53 | const struct bitslice_run *run = &bs.runs[i]; 54 | unsigned val = (op >> run->inpos) & ((1 << run->len) - 1); 55 | ret |= val << run->outpos; 56 | } 57 | return ret; 58 | } 59 | 60 | static inline unsigned bs_set(struct bitslice bs, unsigned new, unsigned op) { 61 | for(int i = 0; i < bs.nruns; i++) { 62 | const struct bitslice_run *run = &bs.runs[i]; 63 | unsigned mask = (1 << run->len) - 1; 64 | unsigned val = (new >> run->outpos) & mask; 65 | op = (op & ~(mask << run->inpos)) | (val << run->inpos); 66 | } 67 | return op; 68 | } 69 | 70 | static inline struct bitslice bs_slice_(struct bitslice bs, 71 | struct bitslice_run *runs, 72 | int lo, int size) { 73 | int nruns = 0; 74 | for(int i = 0; i < bs.nruns; i++) { 75 | struct bitslice_run inr = bs.runs[i]; 76 | inr.outpos -= lo; 77 | if(inr.outpos < 0) { 78 | inr.len += inr.outpos; 79 | inr.inpos -= inr.outpos; 80 | inr.outpos = 0; 81 | } 82 | if(inr.outpos + inr.len > size) 83 | inr.len = size - inr.outpos; 84 | if(inr.len > 0) 85 | runs[nruns++] = (struct bitslice_run) {inr.inpos, inr.outpos, inr.len}; 86 | } 87 | return (struct bitslice) {nruns, runs}; 88 | } 89 | #define bs_slice(bs, lo, size) \ 90 | bs_slice_(bs, alloca((bs).nruns * sizeof(struct bitslice_run)), lo, size) 91 | 92 | enum pcrel_load_mode { 93 | PLM_ADR, /* just want the address */ 94 | PLM_U8, PLM_S8, 95 | PLM_U16, PLM_S16, 96 | PLM_U32, PLM_S32, 97 | PLM_U64, 98 | PLM_U128, /* i.e. LDRD */ 99 | PLM_U32_SIMD, 100 | PLM_U64_SIMD, 101 | PLM_U128_SIMD, 102 | }; 103 | 104 | static const struct bitslice null_bs = { 0, NULL }; 105 | static const unsigned null_op = -0x100; 106 | #define r(nn) nn, false, true 107 | #define rs(nn, l, s) bs_slice(nn, l, s), false, true 108 | #define rout(nn) nn, true, true 109 | #define rsout(nn, l, s) bs_slice(nn, l, s), true, true 110 | #define rnull null_bs, false, false 111 | 112 | #define data(...) data_flags(0, __VA_ARGS__) 113 | #define data_flags(...) data_(__VA_ARGS__, rnull, rnull, rnull, rnull) 114 | #define data_(...) data__(__VA_ARGS__) 115 | #define data__(fl, b1, o1, v1, b2, o2, v2, b3, o3, v3, b4, o4, v4, ...) do { \ 116 | unsigned op = ctx->base.op; \ 117 | P(data)(ctx, \ 118 | v1 ? bs_get(b1, op) : null_op, \ 119 | v2 ? bs_get(b2, op) : null_op, \ 120 | v3 ? bs_get(b3, op) : null_op, \ 121 | v4 ? bs_get(b4, op) : null_op, \ 122 | (o1 << 0) | \ 123 | (o2 << 1) | \ 124 | (o3 << 2) | \ 125 | (o4 << 3) | \ 126 | fl); \ 127 | if (DIS_MAY_MODIFY && ctx->base.modify) { \ 128 | uint32_t new = ctx->base.op; \ 129 | new = bs_set(b1, ctx->base.newval[0], new); \ 130 | new = bs_set(b2, ctx->base.newval[1], new); \ 131 | new = bs_set(b3, ctx->base.newval[2], new); \ 132 | new = bs_set(b4, ctx->base.newval[3], new); \ 133 | *(uint32_t *) ctx->base.newop = new; \ 134 | } \ 135 | return; \ 136 | } while (0) 137 | 138 | #ifndef TARGET_DIS_SUPPORTED 139 | #error "no disassembler for the target architecture yet" 140 | #endif 141 | 142 | static inline void op64(void **codep, uint64_t op) { 143 | unaligned_w64(*codep, op); 144 | *codep += 8; 145 | } 146 | 147 | static inline void op32(void **codep, uint32_t op) { 148 | unaligned_w32(*codep, op); 149 | *codep += 4; 150 | } 151 | 152 | static inline void op16(void **codep, uint16_t op) { 153 | unaligned_w16(*codep, op); 154 | *codep += 2; 155 | } 156 | 157 | static inline void op8(void **codep, uint8_t op) { 158 | *(uint8_t *) *codep = op; 159 | (*codep)++; 160 | } 161 | 162 | #define CC_CONDITIONAL 0x100 163 | #define CC_CALL 0x200 164 | 165 | struct dis_ctx_base { 166 | uint_tptr pc; 167 | const void *ptr; 168 | #if defined(TARGET_x86_64) || defined(TARGET_i386) 169 | uint8_t newop[32]; 170 | #else 171 | uint8_t newop[4]; 172 | uint32_t op; 173 | #endif 174 | uint32_t newval[4]; 175 | bool modify; 176 | int op_size, newop_size; 177 | }; 178 | 179 | #include stringify(TARGET_DIR/arch-dis.h) 180 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/execmem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | /* For allocating trampolines - this is just a mmap wrapper. */ 4 | int execmem_alloc_unsealed(uintptr_t hint, void **page_p, size_t *size_p); 5 | int execmem_seal(void *page); 6 | void execmem_free(void *page); 7 | 8 | /* Write to "foreign" (i.e. owned by another library, not out-of-process) pages 9 | * which are already RX or have unknown permissions. 10 | * If callback is not NULL, run it on all other threads 'atomically', in the 11 | * sense that it will be called on any thread which executed any of the old 12 | * instructions in the write region. 13 | * Oh, and it might mutate 'writes' (to sort it). */ 14 | struct execmem_foreign_write { 15 | void *dst; 16 | const void *src; 17 | size_t len; 18 | }; 19 | typedef uintptr_t (*execmem_pc_patch_callback)(void *ctx, uintptr_t pc); 20 | int execmem_foreign_write_with_pc_patch(struct execmem_foreign_write *writes, 21 | size_t nwrites, 22 | execmem_pc_patch_callback callback, 23 | void *callback_ctx); 24 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/hook-functions.c: -------------------------------------------------------------------------------- 1 | #include "substitute-internal.h" 2 | #ifdef TARGET_DIS_SUPPORTED 3 | #include "substitute.h" 4 | #include "jump-dis.h" 5 | #include "transform-dis.h" 6 | #include "execmem.h" 7 | #include stringify(TARGET_DIR/jump-patch.h) 8 | #include 9 | #include "ptrauth_helpers.h" 10 | 11 | struct hook_internal { 12 | int offset_by_pcdiff[MAX_EXTENDED_PATCH_SIZE + 1]; 13 | uint8_t jump_patch[MAX_JUMP_PATCH_SIZE]; 14 | size_t jump_patch_size; 15 | void *code; 16 | void *outro_trampoline; 17 | /* page allocated with execmem_alloc_unsealed - only if we had to allocate 18 | * one when processing this hook */ 19 | void *trampoline_page; 20 | struct arch_dis_ctx arch_dis_ctx; 21 | }; 22 | 23 | struct pc_callback_info { 24 | struct hook_internal *his; 25 | size_t nhooks; 26 | bool encountered_bad_pc; 27 | }; 28 | 29 | static uintptr_t pc_callback(void *ctx, uintptr_t pc) { 30 | struct pc_callback_info *restrict info = ctx; 31 | uintptr_t real_pc = pc; 32 | #ifdef __arm__ 33 | real_pc = pc & ~1; 34 | #endif 35 | for (size_t i = 0; i < info->nhooks; i++) { 36 | struct hook_internal *hi = &info->his[i]; 37 | uintptr_t diff = real_pc - (uintptr_t) hi->code; 38 | if (diff < hi->jump_patch_size) { 39 | int offset = hi->offset_by_pcdiff[diff]; 40 | if (offset == -1) { 41 | info->encountered_bad_pc = true; 42 | return pc; 43 | } 44 | return (uintptr_t) hi->outro_trampoline + offset; 45 | } 46 | } 47 | return pc; 48 | } 49 | 50 | /* Figure out the size of the patch we need to jump from pc_patch_start 51 | * to hook->replacement. 52 | * On ARM, we can jump anywhere in 8 bytes. On ARM64, we can only do it in two 53 | * or three instructions if the destination PC is within 4GB or so of the 54 | * source. We *could* just brute force it by adding more instructions, but 55 | * this increases the chance of problems caused by patching too much of the 56 | * function. Instead, since we should be able to mmap a trampoline somewhere 57 | * in that range, we'll stop there on the way to. 58 | * In order of preference: 59 | * - Jump directly. 60 | * - Jump using a trampoline to be placed at our existing trampoline_ptr. 61 | * - Allocate a new trampoline_ptr, using the target as a hint, and jump there. 62 | * If even that is out of range, then return an error code. 63 | */ 64 | 65 | static int check_intro_trampoline(void **trampoline_ptr_p, 66 | size_t *trampoline_size_left_p, 67 | uintptr_t pc, 68 | uintptr_t dpc, 69 | int *patch_size_p, 70 | bool *need_intro_trampoline_p, 71 | void **trampoline_page_p, 72 | struct arch_dis_ctx arch) { 73 | void *trampoline_ptr = *trampoline_ptr_p; 74 | size_t trampoline_size_left = *trampoline_size_left_p; 75 | 76 | /* Try direct */ 77 | *need_intro_trampoline_p = false; 78 | *patch_size_p = jump_patch_size(pc, dpc, arch, /*force*/ false); 79 | if (*patch_size_p != -1) 80 | return SUBSTITUTE_OK; 81 | 82 | *need_intro_trampoline_p = true; 83 | 84 | if (trampoline_ptr) { 85 | /* Try existing trampoline */ 86 | *patch_size_p = jump_patch_size(pc, (uintptr_t) trampoline_ptr, arch, 87 | false); 88 | 89 | if (*patch_size_p != -1 && (size_t) *patch_size_p 90 | <= *trampoline_size_left_p) 91 | return SUBSTITUTE_OK; 92 | } 93 | 94 | /* Allocate new trampoline - try after pc. If this fails, we can try 95 | * before pc before giving up. */ 96 | int ret = execmem_alloc_unsealed(pc, &trampoline_ptr, &trampoline_size_left); 97 | if (!ret) { 98 | *patch_size_p = jump_patch_size(pc, (uintptr_t) trampoline_ptr, arch, 99 | false); 100 | if (*patch_size_p != -1) { 101 | ret = SUBSTITUTE_OK; 102 | goto end; 103 | } 104 | 105 | execmem_free(trampoline_ptr); 106 | } 107 | 108 | /* Allocate new trampoline - try before pc (xxx only meaningful on arm64) */ 109 | uintptr_t start_address = pc - 0x80000000; 110 | ret = execmem_alloc_unsealed(start_address, 111 | &trampoline_ptr, &trampoline_size_left); 112 | if (!ret) { 113 | /* Last chance so set force to fall back to MOVi64 if ADRP+ADD can't reach it still */ 114 | *patch_size_p = jump_patch_size(pc, (uintptr_t) trampoline_ptr, arch, 115 | true); 116 | if (*patch_size_p != -1) { 117 | *trampoline_ptr_p = trampoline_ptr; 118 | *trampoline_size_left_p = trampoline_size_left; 119 | *trampoline_page_p = trampoline_ptr; 120 | return SUBSTITUTE_OK; 121 | } 122 | 123 | execmem_free(trampoline_ptr); 124 | ret = SUBSTITUTE_ERR_OUT_OF_RANGE; 125 | } 126 | 127 | end: 128 | *trampoline_ptr_p = trampoline_ptr; 129 | *trampoline_size_left_p = trampoline_size_left; 130 | *trampoline_page_p = trampoline_ptr; 131 | return ret; 132 | } 133 | 134 | 135 | EXPORT 136 | int substitute_hook_functions(const struct substitute_function_hook *hooks, 137 | size_t nhooks, 138 | struct substitute_function_hook_record **recordp, 139 | int options) { 140 | bool thread_safe = !(options & SUBSTITUTE_NO_THREAD_SAFETY); 141 | if (thread_safe && !pthread_main_np()) 142 | return SUBSTITUTE_ERR_NOT_ON_MAIN_THREAD; 143 | 144 | if (recordp) 145 | *recordp = NULL; 146 | 147 | struct execmem_foreign_write *fws; 148 | struct hook_internal *his = malloc(nhooks * sizeof(*his) + 149 | nhooks * sizeof(*fws)); 150 | if (!his) 151 | return SUBSTITUTE_ERR_OOM; 152 | fws = (void *) (his + nhooks); 153 | 154 | for (size_t i = 0; i < nhooks; i++) 155 | his[i].trampoline_page = NULL; 156 | 157 | int ret = SUBSTITUTE_OK; 158 | 159 | void *trampoline_ptr = NULL; 160 | size_t trampoline_size_left = 0; 161 | 162 | /* First run through and (a) ensure all the functions are OK to hook, (b) 163 | * allocate memory for the trampolines. */ 164 | for (size_t i = 0; i < nhooks; i++) { 165 | const struct substitute_function_hook *hook = &hooks[i]; 166 | struct hook_internal *hi = &his[i]; 167 | void *code = make_sym_readable(hook->function); 168 | struct arch_dis_ctx arch; 169 | arch_dis_ctx_init(&arch); 170 | #ifdef __arm__ 171 | if ((uintptr_t) code & 1) { 172 | arch.pc_low_bit = true; 173 | code--; 174 | } 175 | #endif 176 | hi->code = code; 177 | hi->arch_dis_ctx = arch; 178 | uintptr_t pc_patch_start = (uintptr_t) code; 179 | uintptr_t replacement_dat = (uintptr_t) make_sym_readable(hook->replacement); 180 | int patch_size; 181 | bool need_intro_trampoline; 182 | if ((ret = check_intro_trampoline(&trampoline_ptr, &trampoline_size_left, 183 | pc_patch_start, 184 | replacement_dat, 185 | &patch_size, &need_intro_trampoline, 186 | &hi->trampoline_page, arch))) 187 | goto end; 188 | 189 | uint_tptr pc_patch_end = pc_patch_start + patch_size; 190 | uintptr_t initial_target; 191 | if (need_intro_trampoline) { 192 | initial_target = (uintptr_t) trampoline_ptr; 193 | make_jump_patch(&trampoline_ptr, (uintptr_t) trampoline_ptr, 194 | replacement_dat, arch); 195 | } else { 196 | initial_target = replacement_dat; 197 | } 198 | 199 | /* Make the real jump patch for the target function. */ 200 | void *jp = hi->jump_patch; 201 | make_jump_patch(&jp, pc_patch_start, initial_target, arch); 202 | hi->jump_patch_size = (uint8_t *) jp - hi->jump_patch; 203 | 204 | size_t outro_est = TD_MAX_REWRITTEN_SIZE + MAX_JUMP_PATCH_SIZE; 205 | 206 | if (outro_est > trampoline_size_left) { 207 | /* Not enough space left in our existing block... */ 208 | if ((ret = execmem_alloc_unsealed(0, &trampoline_ptr, 209 | &trampoline_size_left))) 210 | goto end; 211 | hi->trampoline_page = trampoline_ptr; 212 | } 213 | 214 | void *outro_trampoline_real = trampoline_ptr; 215 | hi->outro_trampoline = outro_trampoline_real; 216 | #ifdef __arm__ 217 | if (arch.pc_low_bit) 218 | hi->outro_trampoline++; 219 | #endif 220 | if (hook->old_ptr) 221 | *(void **) hook->old_ptr = make_sym_callable(hi->outro_trampoline); 222 | 223 | /* Generate the rewritten start of the function for the outro 224 | * trampoline (complaining if any bad instructions are found) 225 | * (on arm64, this modifies arch.regs_possibly_written, which is used 226 | * by the later make_jump_patch call) */ 227 | if ((ret = transform_dis_main(code, &trampoline_ptr, pc_patch_start, 228 | &pc_patch_end, (uintptr_t) trampoline_ptr, 229 | &arch, hi->offset_by_pcdiff, 230 | thread_safe ? TRANSFORM_DIS_BAN_CALLS : 0))) 231 | goto end; 232 | 233 | uintptr_t dpc = pc_patch_end; 234 | #ifdef __arm__ 235 | if (arch.pc_low_bit) 236 | dpc++; 237 | #endif 238 | 239 | /* Now that transform_dis_main has given us the final pc_patch_end, 240 | * check some of the rest of the function for jumps back into the 241 | * patched region. */ 242 | if ((ret = jump_dis_main(code, pc_patch_start, pc_patch_end, arch))) 243 | goto end; 244 | /* Okay, continue with the outro. */ 245 | make_jump_patch(&trampoline_ptr, (uintptr_t) trampoline_ptr, dpc, arch); 246 | trampoline_ptr += -(uintptr_t) trampoline_ptr % ARCH_MAX_CODE_ALIGNMENT; 247 | trampoline_size_left -= (uint8_t *) trampoline_ptr 248 | - (uint8_t *) outro_trampoline_real; 249 | } 250 | 251 | /* Now commit. */ 252 | for (size_t i = 0; i < nhooks; i++) { 253 | struct hook_internal *hi = &his[i]; 254 | void *page = hi->trampoline_page; 255 | if (page) 256 | execmem_seal(page); 257 | fws[i].dst = hi->code; 258 | fws[i].src = hi->jump_patch; 259 | fws[i].len = hi->jump_patch_size; 260 | } 261 | 262 | struct pc_callback_info info = {his, nhooks, false}; 263 | if ((ret = execmem_foreign_write_with_pc_patch( 264 | fws, nhooks, thread_safe ? pc_callback : NULL, &info))) { 265 | /* Too late to free the trampolines. Chances are this is fatal anyway. */ 266 | goto end_dont_free; 267 | } 268 | if (info.encountered_bad_pc) { 269 | ret = SUBSTITUTE_ERR_UNEXPECTED_PC_ON_OTHER_THREAD; 270 | goto end_dont_free; 271 | } 272 | 273 | goto end_dont_free; 274 | end: 275 | /* if we failed, get rid of the trampolines. */ 276 | for (size_t i = 0; i < nhooks; i++) { 277 | void *page = his[i].trampoline_page; 278 | if (page) 279 | execmem_free(page); 280 | } 281 | end_dont_free: 282 | free(his); 283 | return ret; 284 | } 285 | 286 | #endif /* TARGET_DIS_SUPPORTED */ 287 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/jump-dis.c: -------------------------------------------------------------------------------- 1 | #include "cbit/vec.h" 2 | #include "substitute-internal.h" 3 | #ifdef TARGET_DIS_SUPPORTED 4 | #define DIS_MAY_MODIFY 0 5 | #include "dis.h" 6 | #include 7 | #include 8 | #include 9 | 10 | /* This pass tries to look through the function to find jumps back to the 11 | * patched code at the beginning to the function. It does not deal with jump 12 | * tables, and has a limited range, so it is only heuristic. If such jumps are 13 | * found, the hook is aborted. In the future it might be possible to fix up 14 | * the jumps rather than merely detect them, but that would require doing 15 | * something weird like extending the patch region to add trampolines... */ 16 | 17 | enum { 18 | JUMP_ANALYSIS_MAX_INSNS = 512, 19 | JUMP_ANALYSIS_MAX_SIZE = JUMP_ANALYSIS_MAX_INSNS * MIN_INSN_SIZE, 20 | }; 21 | 22 | DECL_VEC(uint_tptr, uint_tptr); 23 | 24 | struct jump_dis_ctx { 25 | /* outputs */ 26 | bool bad_insn; 27 | bool continue_after_this_insn; 28 | 29 | struct dis_ctx_base base; 30 | 31 | uint_tptr pc_patch_start; 32 | uint_tptr pc_patch_end; 33 | /* For now, this is pretty hacky. Once we find a ret, we don't process any 34 | * instructions after it, because they might be calls to __stack_chk_fail. 35 | * That means any function with a ret midway will only have part of it 36 | * checked, but whatever; heuristic, remember. Instructions are not 37 | * actually guaranteed to be processed in sorted order, but we'll follow 38 | * the straight line before branches, which should be good enough. */ 39 | uint_tptr pc_ret; 40 | 41 | uint8_t seen_mask[JUMP_ANALYSIS_MAX_INSNS / 8]; 42 | /* queue of instructions to visit (well, stack) */ 43 | VEC_STORAGE_CAPA(uint_tptr, 10) queue; 44 | 45 | struct arch_dis_ctx arch; 46 | }; 47 | 48 | #undef P 49 | #define P(x) jump_dis_##x 50 | 51 | #define tdis_ctx struct jump_dis_ctx * 52 | 53 | static void jump_dis_add_to_queue(struct jump_dis_ctx *ctx, uint_tptr pc) { 54 | size_t diff = (pc - ctx->pc_patch_start) / MIN_INSN_SIZE; 55 | if (diff >= JUMP_ANALYSIS_MAX_INSNS) { 56 | #ifdef JUMP_DIS_VERBOSE 57 | fprintf(stderr, "jump-dis: not adding %llx - out of range\n", 58 | (unsigned long long) pc); 59 | #endif 60 | return; 61 | } 62 | if (ctx->seen_mask[diff / 8] & 1 << (diff % 8)) { 63 | #ifdef JUMP_DIS_VERBOSE 64 | fprintf(stderr, "jump-dis: not adding %llx - already seen\n", 65 | (unsigned long long) pc); 66 | #endif 67 | return; 68 | } 69 | ctx->seen_mask[diff / 8] |= 1 << (diff % 8); 70 | 71 | vec_append_uint_tptr(&ctx->queue.v, pc); 72 | } 73 | 74 | static INLINE UNUSED 75 | void jump_dis_data(UNUSED struct jump_dis_ctx *ctx, 76 | UNUSED unsigned o0, UNUSED unsigned o1, UNUSED unsigned o2, 77 | UNUSED unsigned o3, UNUSED unsigned out_mask) { 78 | /* on ARM, ignore mov PC jumps, as they're unlikely to be in the same 79 | * function */ 80 | } 81 | 82 | static INLINE UNUSED 83 | void jump_dis_pcrel(struct jump_dis_ctx *ctx, uint_tptr dpc, 84 | UNUSED struct arch_pcrel_info info) { 85 | #if !defined(TARGET_x86_64) && !defined(TARGET_i386) 86 | if ((ctx->base.op & 0x9f000000) == 0x90000000) return; // ignore ADRP 87 | #endif 88 | ctx->bad_insn = dpc >= ctx->pc_patch_start && dpc < ctx->pc_patch_end; 89 | } 90 | 91 | static INLINE UNUSED 92 | void jump_dis_indirect_call(UNUSED struct jump_dis_ctx *ctx) { 93 | } 94 | 95 | static INLINE UNUSED 96 | void jump_dis_ret(struct jump_dis_ctx *ctx) { 97 | if (ctx->pc_ret > ctx->base.pc) 98 | ctx->pc_ret = ctx->base.pc; 99 | ctx->continue_after_this_insn = false; 100 | } 101 | 102 | static NOINLINE UNUSED 103 | void jump_dis_branch(struct jump_dis_ctx *ctx, uint_tptr dpc, int cc) { 104 | if (dpc >= ctx->pc_patch_start && dpc < ctx->pc_patch_end) { 105 | ctx->bad_insn = true; 106 | return; 107 | } 108 | #ifdef JUMP_DIS_VERBOSE 109 | fprintf(stderr, "jump-dis: enqueueing %llx\n", (unsigned long long) dpc); 110 | #endif 111 | jump_dis_add_to_queue(ctx, dpc); 112 | ctx->continue_after_this_insn = cc & (CC_CONDITIONAL | CC_CALL); 113 | } 114 | 115 | static INLINE UNUSED 116 | void jump_dis_unidentified(UNUSED struct jump_dis_ctx *ctx) { 117 | } 118 | 119 | static INLINE UNUSED 120 | void jump_dis_bad(struct jump_dis_ctx *ctx) { 121 | ctx->continue_after_this_insn = false; 122 | } 123 | 124 | static INLINE UNUSED 125 | void jump_dis_thumb_it(UNUSED struct jump_dis_ctx *ctx) { 126 | } 127 | 128 | static void jump_dis_dis(struct jump_dis_ctx *ctx); 129 | 130 | bool jump_dis_main(void *code_ptr, uint_tptr pc_patch_start, 131 | uint_tptr pc_patch_end, 132 | struct arch_dis_ctx initial_dis_ctx) { 133 | bool ret; 134 | struct jump_dis_ctx ctx; 135 | memset(&ctx, 0, sizeof(ctx)); 136 | ctx.pc_patch_start = pc_patch_start; 137 | ctx.pc_patch_end = pc_patch_end; 138 | ctx.pc_ret = -1; 139 | ctx.base.pc = pc_patch_end; 140 | ctx.arch = initial_dis_ctx; 141 | VEC_STORAGE_INIT(&ctx.queue, uint_tptr); 142 | while (1) { 143 | ctx.bad_insn = false; 144 | ctx.continue_after_this_insn = true; 145 | ctx.base.ptr = code_ptr + (ctx.base.pc - pc_patch_start); 146 | jump_dis_dis(&ctx); 147 | #ifdef JUMP_DIS_VERBOSE 148 | fprintf(stderr, "jump-dis: pc=%llx op=%08x size=%x bad=%d continue_after=%d\n", 149 | (unsigned long long) ctx.base.pc, 150 | #if defined(TARGET_x86_64) || defined(TARGET_i386) 151 | 0, 152 | #else 153 | ctx.base.op, 154 | #endif 155 | ctx.base.op_size, 156 | ctx.bad_insn, 157 | ctx.continue_after_this_insn); 158 | #endif 159 | if (ctx.bad_insn) { 160 | ret = true; 161 | goto fail; 162 | } 163 | if (ctx.continue_after_this_insn) 164 | jump_dis_add_to_queue(&ctx, ctx.base.pc + ctx.base.op_size); 165 | 166 | /* get next address */ 167 | do { 168 | if (!ctx.queue.v.length) 169 | goto done; 170 | ctx.base.pc = vec_pop_uint_tptr(&ctx.queue.v); 171 | } while (ctx.base.pc > ctx.pc_ret); 172 | } 173 | done: 174 | /* no bad instructions! */ 175 | ret = false; 176 | fail: 177 | vec_free_storage_uint_tptr(&ctx.queue.v); 178 | return ret; 179 | } 180 | 181 | #include stringify(TARGET_DIR/dis-main.inc.h) 182 | #endif /* TARGET_DIS_SUPPORTED */ 183 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/jump-dis.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "dis.h" 5 | 6 | bool jump_dis_main(void *code_ptr, uintptr_t pc_patch_start, uintptr_t pc_patch_end, 7 | struct arch_dis_ctx initial_dis_ctx); 8 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/ptrauth_helpers.h: -------------------------------------------------------------------------------- 1 | #ifndef PTRAUTH_HELPERS_H 2 | #define PTRAUTH_HELPERS_H 3 | // Helpers for PAC archs. 4 | 5 | // If the compiler understands __arm64e__, assume it's paired with an SDK that has 6 | // ptrauth.h. Otherwise, it'll probably error if we try to include it so don't. 7 | #if __arm64e__ 8 | #include 9 | #endif 10 | 11 | // Given a pointer to instructions, sign it so you can call it like a normal fptr. 12 | static void *make_sym_callable(void *ptr) { 13 | #if __arm64e__ 14 | ptr = ptrauth_sign_unauthenticated(ptrauth_strip(ptr, ptrauth_key_function_pointer), ptrauth_key_function_pointer, 0); 15 | #endif 16 | return ptr; 17 | } 18 | 19 | // Given a function pointer, strip the PAC so you can read the instructions. 20 | static void *make_sym_readable(void *ptr) { 21 | #if __arm64e__ 22 | ptr = ptrauth_strip(ptr, ptrauth_key_function_pointer); 23 | #endif 24 | return ptr; 25 | } 26 | 27 | #endif -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/strerror.c: -------------------------------------------------------------------------------- 1 | #include "substitute.h" 2 | #include "substitute-internal.h" 3 | 4 | EXPORT 5 | const char *substitute_strerror(int err) { 6 | #define CASE(code) case code: (void) __COUNTER__; return #code 7 | switch (err) { 8 | /* substitute.h */ 9 | enum { _start = __COUNTER__ }; 10 | CASE(SUBSTITUTE_OK); 11 | CASE(SUBSTITUTE_ERR_FUNC_TOO_SHORT); 12 | CASE(SUBSTITUTE_ERR_FUNC_BAD_INSN_AT_START); 13 | CASE(SUBSTITUTE_ERR_FUNC_CALLS_AT_START); 14 | CASE(SUBSTITUTE_ERR_FUNC_JUMPS_TO_START); 15 | CASE(SUBSTITUTE_ERR_OOM); 16 | CASE(SUBSTITUTE_ERR_VM); 17 | CASE(SUBSTITUTE_ERR_NOT_ON_MAIN_THREAD); 18 | CASE(SUBSTITUTE_ERR_UNEXPECTED_PC_ON_OTHER_THREAD); 19 | CASE(SUBSTITUTE_ERR_OUT_OF_RANGE); 20 | CASE(SUBSTITUTE_ERR_UNKNOWN_RELOCATION_TYPE); 21 | CASE(SUBSTITUTE_ERR_NO_SUCH_SELECTOR); 22 | CASE(SUBSTITUTE_ERR_ADJUSTING_THREADS); 23 | _Static_assert(__COUNTER__ - _start == 24 | _SUBSTITUTE_CURRENT_MAX_ERR_PLUS_ONE + 1, 25 | "not all errors named in strerror.c"); 26 | /* substitute-internal.h */ 27 | CASE(SUBSTITUTE_ERR_TASK_FOR_PID); 28 | CASE(SUBSTITUTE_ERR_MISC); 29 | default: 30 | return "(unknown libsubstitute error)"; 31 | } 32 | #undef CASE 33 | } 34 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/substitute-internal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #undef __TVOS_PROHIBITED 4 | #define __TVOS_PROHIBITED 5 | #undef __WATCHOS_PROHIBITED 6 | #define __WATCHOS_PROHIBITED 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #define LOG(...) os_log(OS_LOG_DEFAULT, "SubstituteLog: " __VA_ARGS__); 13 | 14 | #define substitute_panic(...) do { \ 15 | LOG(__VA_ARGS__); \ 16 | abort(); \ 17 | __builtin_unreachable(); \ 18 | } while(0) 19 | 20 | #define substitute_info(...) do { \ 21 | LOG(__VA_ARGS__); \ 22 | } while(0) 23 | 24 | #define EXPORT __attribute__ ((visibility("default"))) 25 | #define UNUSED __attribute__((unused)) 26 | 27 | #ifdef __APPLE__ 28 | #include 29 | #include 30 | #include 31 | #include 32 | #ifdef __LP64__ 33 | typedef struct mach_header_64 mach_header_x; 34 | typedef struct segment_command_64 segment_command_x; 35 | typedef struct section_64 section_x; 36 | #define LC_SEGMENT_X LC_SEGMENT_64 37 | #else 38 | typedef struct mach_header mach_header_x; 39 | typedef struct segment_command segment_command_x; 40 | typedef struct section section_x; 41 | #define LC_SEGMENT_X LC_SEGMENT 42 | #endif 43 | /* instead of 'nlist_x', use substitute_sym */ 44 | #endif 45 | 46 | /* FORCE_* are for tests */ 47 | #if defined(FORCE_TARGET_x86_64) 48 | #define TARGET_x86_64 49 | #elif defined(FORCE_TARGET_i386) 50 | #define TARGET_i386 51 | #elif defined(FORCE_TARGET_arm) 52 | #define TARGET_arm 53 | #elif defined(FORCE_TARGET_arm64) 54 | #define TARGET_arm64 55 | #elif defined(__x86_64__) 56 | #define TARGET_x86_64 57 | #elif defined(__i386__) 58 | #define TARGET_i386 59 | #elif defined(__arm__) 60 | #define TARGET_arm 61 | #elif defined(__arm64__) 62 | #define TARGET_arm64 63 | #else 64 | #error target? 65 | #endif 66 | 67 | #if defined(TARGET_arm) 68 | #define TARGET_DIR arm 69 | #elif defined(TARGET_arm64) 70 | #define TARGET_DIR arm64 71 | #elif defined(TARGET_x86_64) || defined(TARGET_i386) 72 | #define TARGET_DIR x86 73 | #endif 74 | #define stringify_(x) #x 75 | #define stringify(x) stringify_(x) 76 | #include stringify(TARGET_DIR/misc.h) 77 | 78 | #if TARGET_POINTER_SIZE == 8 79 | typedef uint64_t uint_tptr; 80 | #elif TARGET_POINTER_SIZE == 4 81 | typedef uint32_t uint_tptr; 82 | #endif 83 | 84 | 85 | #ifdef __APPLE__ 86 | /* This could graduate to a public API but is not yet. Needs more 87 | * functionality. */ 88 | 89 | enum { 90 | /* substitute_dlopen_in_pid: task_for_pid failed; on OS X the reasons this 91 | * can happen are really complicated and dumb, but generally one solution 92 | * is to be root */ 93 | SUBSTITUTE_ERR_TASK_FOR_PID = 1000, 94 | SUBSTITUTE_ERR_MISC, 95 | }; 96 | 97 | enum shuttle_type { 98 | SUBSTITUTE_SHUTTLE_MACH_PORT, 99 | /* ... */ 100 | }; 101 | 102 | struct shuttle { 103 | int type; 104 | union { 105 | struct { 106 | mach_port_t port; 107 | mach_msg_type_name_t right_type; 108 | } mach; 109 | } u; 110 | }; 111 | 112 | int substitute_dlopen_in_pid(int pid, const char *filename, int options, 113 | const struct shuttle *shuttle, size_t nshuttle, 114 | char **error); 115 | 116 | int substitute_ios_unrestrict(task_t task, char **error); 117 | #endif 118 | 119 | static inline const char *xbasename(const char *path) { 120 | const char *slash = strrchr(path, '/'); 121 | return slash ? slash + 1 : path; 122 | } 123 | 124 | #define substitute_assert(x) do { \ 125 | if (!(x)) { __builtin_abort(); } \ 126 | } while(0) 127 | 128 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/transform-dis.c: -------------------------------------------------------------------------------- 1 | #include "substitute-internal.h" 2 | #ifdef TARGET_DIS_SUPPORTED 3 | #define DIS_MAY_MODIFY 1 4 | 5 | #include "substitute.h" 6 | #include "dis.h" 7 | #include "transform-dis.h" 8 | #include 9 | #include 10 | 11 | #define P(x) transform_dis_##x 12 | 13 | struct transform_dis_ctx { 14 | /* outputs */ 15 | int err; 16 | struct dis_ctx_base base; 17 | 18 | uint_tptr pc_trampoline; 19 | uint_tptr pc_patch_start; 20 | /* this is only tentative - it will be updated to include parts of 21 | * instructions poking out, and instructions forced to be transformed by IT */ 22 | uint_tptr pc_patch_end; 23 | /* for IT - eww */ 24 | bool force_keep_transforming; 25 | 26 | bool ban_calls; /* i.e. trying to be thread safe */ 27 | 28 | void **rewritten_ptr_ptr; 29 | void *write_newop_here; 30 | 31 | struct arch_dis_ctx arch; 32 | }; 33 | 34 | #define tdis_ctx struct transform_dis_ctx * 35 | 36 | /* largely similar to jump_dis */ 37 | 38 | static NOINLINE UNUSED 39 | void transform_dis_indirect_call(struct transform_dis_ctx *ctx) { 40 | /* see error description */ 41 | if (ctx->ban_calls && ctx->base.pc + ctx->base.op_size < ctx->pc_patch_end) 42 | ctx->err = SUBSTITUTE_ERR_FUNC_CALLS_AT_START; 43 | } 44 | 45 | static NOINLINE UNUSED 46 | void transform_dis_ret(struct transform_dis_ctx *ctx) { 47 | /* ret is okay if it's at the end of the required patch (past the original 48 | * patch size is good too) */ 49 | if (ctx->base.pc + ctx->base.op_size < ctx->pc_patch_end) 50 | ctx->err = SUBSTITUTE_ERR_FUNC_TOO_SHORT; 51 | } 52 | 53 | static UNUSED 54 | void transform_dis_unidentified(UNUSED struct transform_dis_ctx *ctx) { 55 | #ifdef TRANSFORM_DIS_VERBOSE 56 | printf("transform_dis (0x%llx): unidentified\n", 57 | (unsigned long long) ctx->base.pc); 58 | #endif 59 | /* this isn't exhaustive, so unidentified is fine */ 60 | } 61 | 62 | static NOINLINE UNUSED 63 | void transform_dis_bad(struct transform_dis_ctx *ctx) { 64 | ctx->err = SUBSTITUTE_ERR_FUNC_BAD_INSN_AT_START; 65 | } 66 | 67 | static INLINE UNUSED 68 | void transform_dis_thumb_it(UNUSED struct transform_dis_ctx *ctx) { 69 | /* ignore, since it was turned into B */ 70 | } 71 | 72 | static void transform_dis_branch_top(struct transform_dis_ctx *ctx, 73 | uintptr_t dpc, int cc) { 74 | if (dpc >= ctx->pc_patch_start && dpc < ctx->pc_patch_end) { 75 | /* don't support this for now */ 76 | ctx->err = SUBSTITUTE_ERR_FUNC_BAD_INSN_AT_START; 77 | return; 78 | } 79 | if (cc & CC_CALL) { 80 | transform_dis_indirect_call(ctx); 81 | } else if (!(cc & CC_CONDITIONAL)) { 82 | transform_dis_ret(ctx); 83 | } 84 | } 85 | 86 | static void transform_dis_dis(struct transform_dis_ctx *ctx); 87 | static void transform_dis_pre_dis(struct transform_dis_ctx *ctx); 88 | static void transform_dis_post_dis(struct transform_dis_ctx *ctx); 89 | 90 | int transform_dis_main(const void *restrict code_ptr, 91 | void **restrict rewritten_ptr_ptr, 92 | uint_tptr pc_patch_start, 93 | uint_tptr *pc_patch_end_p, 94 | uint_tptr pc_trampoline, 95 | struct arch_dis_ctx *arch_ctx_p, 96 | int *offset_by_pcdiff, 97 | int options) { 98 | struct transform_dis_ctx ctx; 99 | memset(&ctx, 0, sizeof(ctx)); 100 | ctx.pc_patch_start = pc_patch_start; 101 | ctx.pc_patch_end = *pc_patch_end_p; 102 | ctx.base.pc = pc_patch_start; 103 | ctx.arch = *arch_ctx_p; 104 | ctx.ban_calls = options & TRANSFORM_DIS_BAN_CALLS; 105 | /* data is written to rewritten both by this function directly and, in case 106 | * additional scaffolding is needed, by arch-specific transform_dis_* */ 107 | ctx.rewritten_ptr_ptr = rewritten_ptr_ptr; 108 | void *rewritten_start = *rewritten_ptr_ptr; 109 | int written_pcdiff = 0; 110 | offset_by_pcdiff[written_pcdiff++] = 0; 111 | while (ctx.base.pc < ctx.pc_patch_end && !ctx.force_keep_transforming) { 112 | ctx.base.modify = false; 113 | ctx.err = 0; 114 | ctx.base.ptr = code_ptr + (ctx.base.pc - pc_patch_start); 115 | ctx.pc_trampoline = pc_trampoline + 116 | (*rewritten_ptr_ptr - rewritten_start); 117 | const void *start = ctx.base.ptr; 118 | 119 | transform_dis_pre_dis(&ctx); 120 | 121 | void *rewritten_ptr = *rewritten_ptr_ptr; 122 | ctx.write_newop_here = rewritten_ptr; 123 | 124 | transform_dis_dis(&ctx); 125 | 126 | #ifdef TRANSFORM_DIS_VERBOSE 127 | printf("transform_dis (0x%llx): >> op_size=%d newop_size=%d\n", 128 | (unsigned long long) ctx.base.pc, 129 | ctx.base.op_size, 130 | ctx.base.newop_size); 131 | #endif 132 | 133 | if (ctx.err) 134 | return ctx.err; 135 | if (ctx.write_newop_here != NULL) { 136 | if (ctx.base.modify) 137 | memcpy(ctx.write_newop_here, ctx.base.newop, ctx.base.newop_size); 138 | else 139 | memcpy(ctx.write_newop_here, start, ctx.base.op_size); 140 | if (*rewritten_ptr_ptr == rewritten_ptr) 141 | *rewritten_ptr_ptr += ctx.base.op_size; 142 | } 143 | ctx.base.pc += ctx.base.op_size; 144 | 145 | transform_dis_post_dis(&ctx); 146 | 147 | int pcdiff = ctx.base.pc - ctx.pc_patch_start; 148 | while (written_pcdiff < pcdiff) 149 | offset_by_pcdiff[written_pcdiff++] = -1; 150 | offset_by_pcdiff[written_pcdiff++] = 151 | (int) (*rewritten_ptr_ptr - rewritten_start); 152 | } 153 | *pc_patch_end_p = ctx.base.pc; 154 | *arch_ctx_p = ctx.arch; 155 | return SUBSTITUTE_OK; 156 | } 157 | 158 | #include stringify(TARGET_DIR/arch-transform-dis.inc.h) 159 | #include stringify(TARGET_DIR/dis-main.inc.h) 160 | 161 | #endif /* TARGET_DIS_SUPPORTED */ 162 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/transform-dis.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "dis.h" 5 | 6 | #define TRANSFORM_DIS_BAN_CALLS 1 7 | 8 | int transform_dis_main(const void *restrict code_ptr, 9 | void **restrict rewritten_ptr_ptr, 10 | uint_tptr pc_patch_start, 11 | uint_tptr *pc_patch_end_p, 12 | uint_tptr pc_trampoline, 13 | struct arch_dis_ctx *arch_ctx_p, 14 | int *offset_by_pcdiff, 15 | int options); 16 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/x86/arch-dis.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define MIN_INSN_SIZE 1 3 | /* min([18 * 3, 4 | * 4 + 18 + 15 + 18, 5 | * 6 + 12]) 6 | * See transform_dis_* for size figures. Technically unsafe, since we don't 7 | * check for overlong x86 instructions. */ 8 | #define TD_MAX_REWRITTEN_SIZE 55 9 | #define ARCH_MAX_CODE_ALIGNMENT 1 10 | 11 | struct arch_pcrel_info { 12 | int reg; 13 | bool is_jump; 14 | }; 15 | 16 | struct arch_dis_ctx {}; 17 | static inline void arch_dis_ctx_init(UNUSED struct arch_dis_ctx *ctx) {} 18 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/x86/arch-transform-dis.inc.h: -------------------------------------------------------------------------------- 1 | /* Pretty trivial, but in its own file to match the other architectures. */ 2 | #include "x86/jump-patch.h" 3 | 4 | static inline void push_mov_head(void **code, uint64_t imm, bool rax) { 5 | /* push */ 6 | op8(code, rax ? 0x50 : 0x51); 7 | /* mov */ 8 | #ifdef TARGET_x86_64 9 | op8(code, 0x48); 10 | op8(code, rax ? 0xb8 : 0xb9); 11 | op64(code, imm); 12 | #else 13 | op8(code, rax ? 0xb8 : 0xb9); 14 | op32(code, imm); 15 | #endif 16 | } 17 | 18 | static inline void push_mov_tail(void **code, bool rax) { 19 | /* pop */ 20 | op8(code, rax ? 0x58 : 0x59); 21 | } 22 | 23 | UNUSED 24 | static void transform_dis_pcrel(struct transform_dis_ctx *ctx, uint64_t dpc, 25 | struct arch_pcrel_info info) { 26 | /* push %reg; mov $dpc, %reg; ; pop %reg 27 | * -or, if jump- 28 | * push %reg; mov $dpc, %reg; mov %reg, -8(%rsp); pop %reg; 29 | * 30 | * reg is rcx, or rax if the instruction might be using rcx. 31 | * Max size: 11 + orig + 1 32 | * Minimum size is 6 bytes, so there could be at most 1 in a patch area. */ 33 | void *code = *ctx->rewritten_ptr_ptr; 34 | if (info.is_jump) { 35 | push_mov_head(&code, dpc, true); 36 | memcpy(code, ((uint8_t[]) {0x48, 0x89, 0x44, 0x24, 0xf8}), 5); 37 | code += 5; 38 | push_mov_tail(&code, true); 39 | ctx->write_newop_here = code; 40 | code += ctx->base.op_size - 2; 41 | ctx->base.newval[0] = 4; /* esp */ 42 | ctx->base.newval[1] = -0x10; 43 | } else { 44 | bool rax = info.reg == 1; 45 | push_mov_head(&code, dpc, rax); 46 | ctx->write_newop_here = code; 47 | code += ctx->base.op_size - 4 /* see dis-main.inc.h */; 48 | push_mov_tail(&code, rax); 49 | ctx->base.newval[0] = rax ? 0 /* rcx */ : 1 /* rax */; 50 | ctx->base.newval[1] = 0; 51 | } 52 | *ctx->rewritten_ptr_ptr = code; 53 | ctx->base.modify = true; 54 | } 55 | 56 | static void transform_dis_branch(struct transform_dis_ctx *ctx, uint_tptr dpc, 57 | int cc) { 58 | if (dpc == ctx->base.pc + ctx->base.op_size && (cc & CC_CALL)) { 59 | /* Probably a poor man's PC-rel - 'call .; pop %some'. 60 | * Push the original address. 61 | * Max size: orig + 1 + 11 + 5 + 1 62 | * Minimum call size is 4 bytes; at most 2. */ 63 | void *code = *ctx->rewritten_ptr_ptr; 64 | ctx->write_newop_here = NULL; 65 | 66 | /* push %whatever */ 67 | op8(&code, 0x50); 68 | /* push %rax; mov $dpc, %rax */ 69 | push_mov_head(&code, dpc, true); 70 | /* mov %rax, 8(%rsp) / mov %eax, 4(%esp) */ 71 | #ifdef TARGET_x86_64 72 | memcpy(code, ((uint8_t[]) {0x48, 0x8b, 0x44, 0x24, 0x08}), 5); 73 | code += 5; 74 | #else 75 | memcpy(code, ((uint8_t[]) {0x89, 0x44, 0x24, 0x04}), 4); 76 | code += 4; 77 | #endif 78 | /* pop %rax */ 79 | push_mov_tail(&code, true); 80 | 81 | *ctx->rewritten_ptr_ptr = code; 82 | return; 83 | } 84 | transform_dis_branch_top(ctx, dpc, cc); 85 | void *code = *ctx->rewritten_ptr_ptr; 86 | struct arch_dis_ctx arch; 87 | 88 | if (cc & CC_CONDITIONAL) { 89 | ctx->write_newop_here = code; 90 | code += ctx->base.op_size; 91 | 92 | uint_tptr source = ctx->pc_trampoline + ctx->base.op_size + 2; 93 | int size = jump_patch_size(source, dpc, arch, true); 94 | 95 | /* If not taken, jmp past the big jump - this is a bit suboptimal but not 96 | * that bad. 97 | * Max size: orig + 2 + 14 98 | * Minimum jump size is 2 bytes; at most 3. */ 99 | op8(&code, 0xeb); 100 | op8(&code, size); 101 | 102 | make_jump_patch(&code, source, dpc, arch); 103 | 104 | ctx->base.newval[0] = 2; 105 | ctx->base.modify = true; 106 | transform_dis_ret(ctx); 107 | } else { 108 | ctx->write_newop_here = NULL; 109 | 110 | make_jmp_or_call(&code, ctx->pc_trampoline, dpc, cc & CC_CALL); 111 | } 112 | *ctx->rewritten_ptr_ptr = code; 113 | } 114 | 115 | static void transform_dis_pre_dis(UNUSED struct transform_dis_ctx *ctx) {} 116 | static void transform_dis_post_dis(UNUSED struct transform_dis_ctx *ctx) {} 117 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/x86/dis-main.inc.h: -------------------------------------------------------------------------------- 1 | /* 2 | random notes: 3 | 4 | REX: 0100wrxb 5 | 6 | prefixes REX opc ModR/M SIB displacement immediate 7 | 8 | 1A/C: modrm stuff 9 | i64: 32 only 10 | o64: 64 only 11 | 12 | CDEGMNPQRSUVW: modrm 13 | EMQW: modrm w/ address 14 | IJO: immediate 15 | L: 8-bit immediate 16 | 17 | VEX last byte 1:0: {none, 66, f3, f2} 18 | 19 | */ 20 | 21 | 22 | /* This is probably not the most efficient implementation, but hopefully good 23 | * enough... */ 24 | 25 | #define REP4(x) x, x, x, x 26 | #define REP8(x) REP4(x), REP4(x) 27 | #define REP16(x) REP8(x), REP8(x) 28 | #define I_8 0x01 29 | #define I_16 0x02 30 | #define I_24 0x03 31 | #define I_32 0x04 32 | #define I_v 0x05 33 | #define I_z 0x06 34 | #define I_p 0x07 35 | #define I_IMM_MASK 0x07 36 | #define I_MOD 0x08 37 | #define I_ADDR 0x10 38 | #define I_MODA (I_MOD|I_ADDR) 39 | /* mutually exclusive types */ 40 | #define I_PFX 0x20 /* prefix */ 41 | #define I_JMP 0x40 /* execution does not continue after this */ 42 | #define I_SPEC 0x60 /* special case */ 43 | #define I_TYPE_MASK 0x60 44 | #define I_JIMM_ONLY 0x80 /* imm is jump offset */ 45 | #define I_JIMM (0x80|I_JMP) 46 | #define I_BAD 0x80 47 | #define I_CALL 0x100 /* not really in the table */ 48 | #ifdef TARGET_x86_64 49 | #define if64(_64, _32) _64 50 | #else 51 | #define if64(_64, _32) _32 52 | #endif 53 | #define i64(x) if64(I_BAD, x) 54 | #define o64(x) if64(x, I_BAD) 55 | 56 | static const uint8_t onebyte_bits[] = { 57 | /*00*/ REP4(I_MODA), I_8, I_z, i64(0), i64(0), REP4(I_MODA), I_8, I_z, i64(0), I_SPEC, 58 | /*10*/ REP4(I_MODA), I_8, I_z, i64(0), i64(0), REP4(I_MODA), I_8, I_z, i64(0), i64(0), 59 | /*20*/ REP4(I_MODA), I_8, I_z, I_PFX, i64(0), REP4(I_MODA), I_8, I_z, I_PFX, i64(0), 60 | /*30*/ REP4(I_MODA), I_8, I_z, I_PFX, i64(0), REP4(I_MODA), I_8, I_z, I_PFX, i64(0), 61 | /*40*/ REP16(if64(I_PFX, 0)), 62 | /*50*/ REP16(0), 63 | /*60*/ i64(0), i64(0), i64(I_MOD), I_MODA, I_PFX, I_PFX, I_PFX, I_PFX, 64 | /*68*/ I_z, I_MODA|I_z, I_8, I_MODA|I_8, REP4(0), 65 | /*70*/ REP16(I_8|I_JIMM), 66 | /*80*/ I_MODA|I_8, I_MODA|I_v, i64(I_MODA|I_8), I_MODA, I_MODA, I_MODA, I_MODA, I_MODA, 67 | /*88*/ REP4(I_MODA), I_MODA, I_MOD, I_MODA, if64(I_PFX, I_MODA), 68 | /*90*/ REP8(0), 0, 0, i64(I_p), 0, 0, 0, 0, 0, 69 | /*A0*/ I_8, I_v, I_8, I_v, REP4(0), I_8, I_z, 0, 0, 0, 0, 0, 0, 70 | /*B0*/ REP8(I_8), REP8(I_v), 71 | /*C0*/ I_MODA|I_8, I_MODA|I_8, I_16|I_JMP, I_JMP, 72 | /*C4*/ if64(I_PFX, I_MODA), if64(I_PFX, I_MODA), I_MODA|I_8, I_MODA|I_8, 73 | /*C8*/ I_24, 0, I_16|I_JMP, I_JMP, 0, I_8, i64(0), I_JMP, 74 | /*D0*/ REP4(I_MODA), i64(I_8), i64(I_8), I_BAD, 0, REP8(I_SPEC), 75 | /* don't treat ljmp as a jump for now */ 76 | /*E0*/ REP4(I_8|I_JIMM), REP4(I_8), 77 | /*E8*/ I_z|I_JIMM_ONLY, I_z|I_JIMM, i64(I_p), I_8|I_JIMM, 0, 0, 0, 0, 78 | /*F0*/ I_PFX, I_BAD, I_PFX, I_PFX, 0, 0, I_MODA, I_MODA, 79 | /*F8*/ 0, 0, 0, 0, 0, 0, I_MODA, I_SPEC, 80 | }; 81 | _Static_assert(sizeof(onebyte_bits) == 256, "onebyte_bits"); 82 | 83 | /* Note: 84 | *All* currently defined 0f 38 opcodes are I_MODA. Assuming that any 85 | unknown such opcodes are also I_MODA is probably better than generic 86 | unknown. 87 | Similarly, all defined 0f 3a opcodes are I_MODA|I_8. 88 | */ 89 | 90 | static const uint8_t _0f_bits[] = { 91 | /*00*/ I_MODA, I_MODA, 0, 0, I_BAD, o64(0), 0, o64(0), 92 | /*08*/ 0, 0, I_BAD, 0, 0, I_MODA, 0, 0, 93 | /*10*/ REP8(I_MODA), I_MODA, I_BAD, I_BAD, I_BAD, I_BAD, I_BAD, I_BAD, I_MODA, 94 | /*20*/ REP4(I_MOD), REP4(I_BAD), REP8(I_MODA), 95 | /*30*/ 0, 0, 0, 0, 0, 0, I_BAD, 0, I_MODA, I_BAD, I_MODA|I_8, I_BAD, REP4(I_BAD), 96 | /*40*/ REP16(I_MODA), 97 | /*50*/ I_MOD, I_MODA, I_MODA, I_MODA, REP4(I_MODA), REP8(I_MODA), 98 | /*60*/ REP16(I_MODA), 99 | /*70*/ I_MODA, I_MOD|I_8, I_MOD|I_8, I_MOD|I_8, I_MODA, I_MODA, I_MODA, 0, 100 | /*78*/ I_MODA, I_MODA, I_BAD, I_BAD, REP4(I_MODA), 101 | /*80*/ REP16(I_z|I_JIMM), 102 | /*90*/ REP16(I_MODA), 103 | /*Ax*/ 0, 0, 0, 0, 0, 0, I_BAD, I_BAD, 104 | /*A8*/ 0, 0, 0, I_MODA, I_MODA|I_8, I_MODA, I_MODA, I_MODA, 105 | /*B0*/ REP8(I_MODA), I_MODA, 0, I_MODA|I_8, I_MODA, REP4(I_MODA), 106 | /*C0*/ I_MODA, I_MODA, I_MODA|I_8, I_MODA, I_MODA|I_8, I_MOD|I_8, I_MODA|I_8, I_MODA|I_z, 107 | /*C8*/ REP8(0), 108 | /*D0*/ REP4(I_MODA), I_MODA, I_MODA, I_MODA, I_MOD, REP8(I_MODA), 109 | /*E0*/ REP16(I_MODA), 110 | /*F0*/ REP4(I_MODA), I_MODA, I_MODA, I_MODA, I_MOD, 111 | /*F8*/ REP4(I_MODA), I_MODA, I_MODA, I_MODA, I_BAD, 112 | }; 113 | _Static_assert(sizeof(_0f_bits) == 256, "_0f_bits"); 114 | 115 | static void P(dis)(tdis_ctx ctx) { 116 | const uint8_t *orig = ctx->base.ptr; 117 | const uint8_t *ptr = ctx->base.ptr; 118 | 119 | int opnd_size = 4; 120 | int mod, rm = 0; 121 | restart:; 122 | uint8_t byte1 = *ptr++; 123 | int bits = onebyte_bits[byte1]; 124 | /* printf("b1=%x bytes=%x\n", byte1, bits); */ 125 | if ((bits & I_TYPE_MASK) == I_SPEC) { 126 | if (byte1 == 0x0f) { 127 | uint8_t byte2 = *ptr++; 128 | bits = _0f_bits[byte2]; 129 | } else if ((byte1 & 0xf8) == 0xd8) { 130 | /* ESC */ 131 | ptr++; 132 | bits = I_MODA; 133 | } else if (byte1 == 0xff) { 134 | uint8_t modrm = *ptr; 135 | int subop = modrm >> 3 & 7; 136 | if (subop == 4 || subop == 5) /* JMP */ 137 | bits = I_JMP | I_MODA; 138 | else if (subop == 2 || subop == 3) /* CALL */ 139 | bits = I_CALL | I_MODA; 140 | else 141 | bits = I_MODA; 142 | } else { 143 | __builtin_abort(); 144 | } 145 | } 146 | got_bits: UNUSED 147 | if (bits == I_BAD) 148 | return P(bad)(ctx); 149 | if ((bits & I_TYPE_MASK) == I_PFX) { 150 | if (byte1 == 0x66) { 151 | opnd_size = 2; 152 | goto restart; 153 | #ifdef TARGET_x86_64 154 | } else if ((byte1 & 0xf0) == 0x40) { /* REX */ 155 | if (byte1 & 8) /* W */ 156 | opnd_size = 8; 157 | if (byte1 & 1) /* B */ 158 | rm = 8; 159 | goto restart; 160 | } else if (byte1 == 0xc4) { /* VEX 3 */ 161 | uint8_t byte2 = *ptr++; 162 | if (!(byte2 & 0x20)) /* VEX.~B */ 163 | rm = 8; 164 | UNUSED uint8_t byte3 = *ptr++; 165 | ptr++; 166 | int map = byte2 & 0x1f; 167 | switch (map) { 168 | case 1: 169 | bits = _0f_bits[byte2]; 170 | break; 171 | case 2: 172 | bits = _0f_bits[0x38]; 173 | break; 174 | case 3: 175 | bits = _0f_bits[0x3a]; 176 | break; 177 | default: 178 | bits = I_BAD; 179 | break; 180 | } 181 | goto got_bits; 182 | } else if (byte1 == 0xc5) { /* VEX 2 */ 183 | uint8_t byte2 = *ptr++; 184 | bits = _0f_bits[byte2]; 185 | goto got_bits; 186 | } else if (byte1 == 0x8f) { /* XOP (AMD only) */ 187 | uint8_t byte2 = *ptr; 188 | /* could be modrm */ 189 | if ((byte2 >> 3 & 7) == 0) 190 | goto modrm; 191 | ptr++; /* ok, definitely XOP */ 192 | if (!(byte2 & 0x20)) /* VEX.~B */ 193 | rm = 8; 194 | int map = byte2 & 0x1f; 195 | switch (map) { 196 | case 8: 197 | bits = I_MODA|I_8; 198 | break; 199 | case 9: 200 | bits = I_MODA; 201 | break; 202 | case 10: 203 | bits = I_MODA|I_32; 204 | break; 205 | default: 206 | bits = I_BAD; 207 | break; 208 | } 209 | goto got_bits; 210 | #endif 211 | } else { 212 | /* other prefix we don't care about */ 213 | goto restart; 214 | } 215 | } 216 | UNUSED int modrm_off = ptr - orig; 217 | UNUSED uint8_t modrm; 218 | if (bits & I_MOD) { 219 | modrm: UNUSED; 220 | modrm = *ptr++; 221 | mod = modrm >> 6; 222 | rm |= modrm & 7; 223 | if (rm == 4) { 224 | /* sib */ 225 | ptr++; 226 | } 227 | /* displacement */ 228 | #ifdef TARGET_x86_64 229 | if (mod == 0 && rm == 5) 230 | ptr += 4; 231 | #endif 232 | else if (mod == 1) 233 | ptr++; 234 | else if (mod == 2) 235 | ptr += 4; 236 | } 237 | 238 | int imm_off = ptr - orig; 239 | 240 | /* disp */ 241 | int imm_bits = bits & I_IMM_MASK; 242 | int imm_size; 243 | if (imm_bits <= I_32) 244 | imm_size = imm_bits; 245 | else if (imm_bits == I_v) 246 | imm_size = opnd_size; 247 | else if (imm_bits == I_z) 248 | imm_size = opnd_size == 2 ? 2 : 4; 249 | else if (imm_bits == I_p) 250 | imm_size = opnd_size == 2 ? 4 : 6; 251 | else /* because GCC is stupid */ 252 | __builtin_abort(); 253 | ptr += imm_size; 254 | 255 | ctx->base.ptr = ptr; 256 | ctx->base.newop_size = ctx->base.op_size = ptr - orig; 257 | /* printf("bits=%x\n", bits); */ 258 | 259 | if (bits & I_JIMM_ONLY) { 260 | int32_t imm; 261 | const void *imm_ptr = orig + imm_off; 262 | switch (imm_size) { 263 | case 1: imm = *(int8_t *) imm_ptr; break; 264 | case 2: imm = *(int16_t *) imm_ptr; break; 265 | case 4: imm = *(int32_t *) imm_ptr; break; 266 | default: __builtin_abort(); 267 | } 268 | 269 | bool cond = !(byte1 == 0xe2 || (byte1 >= 0xe8 && byte1 <= 0xeb)); 270 | bool call = !(bits & I_JMP); 271 | P(branch)(ctx, ctx->base.pc + ctx->base.op_size + imm, 272 | cond * CC_CONDITIONAL | call * CC_CALL); 273 | if (DIS_MAY_MODIFY && ctx->base.modify) { 274 | /* newval[0] should be the new immediate */ 275 | int32_t new_imm = ctx->base.newval[0]; 276 | uint8_t *new_op = ctx->base.newop; 277 | memcpy(new_op, orig, ctx->base.op_size); 278 | uint8_t *new_imm_ptr = new_op + imm_off; 279 | switch (imm_size) { 280 | case 1: *(int8_t *) new_imm_ptr = new_imm; break; 281 | case 2: *(int16_t *) new_imm_ptr = new_imm; break; 282 | case 4: *(int32_t *) new_imm_ptr = new_imm; break; 283 | default: __builtin_abort(); 284 | } 285 | } 286 | #ifdef TARGET_x86_64 287 | } else if ((bits & I_MODA) == I_MODA && mod == 0 && rm == 5) { 288 | int32_t disp = *(int32_t *) (orig + modrm_off + 1); 289 | if (bits & I_CALL) 290 | P(indirect_call)(ctx); 291 | /* unlike ARM, we can always switch to non-pcrel without making the 292 | * instruction from scratch, so we don't have 'reg' and 'lm' */ 293 | struct arch_pcrel_info info = { 294 | .reg = modrm >> 3 & 7, 295 | .is_jump = !!(bits & I_JMP), 296 | }; 297 | P(pcrel)(ctx, ctx->base.pc + ctx->base.op_size + disp, info); 298 | if (DIS_MAY_MODIFY && ctx->base.modify) { 299 | uint8_t *new_op = ctx->base.newop; 300 | memcpy(new_op, orig, ctx->base.op_size); 301 | /* newval[0] should be the new register; 302 | * newval[1] should be the new displacement */ 303 | int new_reg = ctx->base.newval[0]; 304 | uint32_t new_disp = ctx->base.newval[1]; 305 | uint8_t *new_modrm_ptr = new_op + modrm_off; 306 | 307 | int new_disp_size = new_disp ? 1 : 0; 308 | 309 | *new_modrm_ptr = (*new_modrm_ptr & ~0xc7) | 310 | (new_disp_size ? 1 : 0) << 6 | 311 | ctx->base.newval[0]; 312 | uint8_t *memspec_end = new_modrm_ptr + 1; 313 | if (new_reg == 4) { 314 | /* rsp - need SIB */ 315 | *memspec_end++ = 0x24; 316 | } 317 | if (new_disp_size) 318 | *memspec_end++ = new_disp; 319 | 320 | memmove(memspec_end, new_modrm_ptr + 5, 321 | ctx->base.op_size - modrm_off - 1); 322 | ctx->base.newop_size -= 5 - (memspec_end - new_modrm_ptr); 323 | } 324 | #endif 325 | } else if ((bits & I_TYPE_MASK) == I_JMP) { 326 | P(ret)(ctx); 327 | } else if (bits & I_CALL) { 328 | P(indirect_call)(ctx); 329 | } else { 330 | P(unidentified)(ctx); 331 | } 332 | } 333 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/x86/jump-patch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define MAX_JUMP_PATCH_SIZE 14 3 | #define MAX_EXTENDED_PATCH_SIZE (MAX_JUMP_PATCH_SIZE+14) 4 | #include "dis.h" 5 | 6 | static inline int jump_patch_size(uint_tptr pc, uint_tptr dpc, 7 | UNUSED struct arch_dis_ctx arch, 8 | bool force) { 9 | uint_tptr diff = dpc - (pc + 5); 10 | /* fits in 32? */ 11 | if (diff == (uint_tptr) (int32_t) diff) 12 | return 5; 13 | else 14 | return force ? (2+4+8) : -1; 15 | } 16 | 17 | static inline void make_jmp_or_call(void **codep, uint_tptr pc, uint_tptr dpc, 18 | bool call) { 19 | uint_tptr diff = dpc - (pc + 5); 20 | void *code = *codep; 21 | if (diff == (uint_tptr) (int32_t) diff) { 22 | op8(&code, call ? 0xe8 : 0xe9); 23 | op32(&code, diff); 24 | } else { 25 | /* jmpq *(%rip) */ 26 | op8(&code, 0xff); 27 | op8(&code, call ? 0x15 : 0x25); 28 | op32(&code, 0); 29 | op64(&code, dpc); 30 | } 31 | *codep = code; 32 | } 33 | 34 | static inline void make_jump_patch(void **codep, uint_tptr pc, uint_tptr dpc, 35 | UNUSED struct arch_dis_ctx arch) { 36 | make_jmp_or_call(codep, pc, dpc, false); 37 | } 38 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/lib/x86/misc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef TARGET_x86_64 3 | #define TARGET_POINTER_SIZE 8 4 | #else 5 | #define TARGET_POINTER_SIZE 4 6 | #endif 7 | #define TARGET_DIS_SUPPORTED 8 | -------------------------------------------------------------------------------- /MIP/third-party/substitute/vendor/dyld_cache_format.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 2 | * 3 | * Copyright (c) 2006-2009 Apple Inc. All rights reserved. 4 | * 5 | * @APPLE_LICENSE_HEADER_START@ 6 | * 7 | * This file contains Original Code and/or Modifications of Original Code 8 | * as defined in and that are subject to the Apple Public Source License 9 | * Version 2.0 (the 'License'). You may not use this file except in 10 | * compliance with the License. Please obtain a copy of the License at 11 | * http://www.opensource.apple.com/apsl/ and read it before using this 12 | * file. 13 | * 14 | * The Original Code and all software distributed under the License are 15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 19 | * Please see the License for the specific language governing rights and 20 | * limitations under the License. 21 | * 22 | * @APPLE_LICENSE_HEADER_END@ 23 | */ 24 | #ifndef __DYLD_CACHE_FORMAT__ 25 | #define __DYLD_CACHE_FORMAT__ 26 | 27 | #include 28 | 29 | 30 | struct dyld_cache_header 31 | { 32 | char magic[16]; // e.g. "dyld_v0 i386" 33 | uint32_t mappingOffset; // file offset to first dyld_cache_mapping_info 34 | uint32_t mappingCount; // number of dyld_cache_mapping_info entries 35 | uint32_t imagesOffset; // file offset to first dyld_cache_image_info 36 | uint32_t imagesCount; // number of dyld_cache_image_info entries 37 | uint64_t dyldBaseAddress; // base address of dyld when cache was built 38 | uint64_t codeSignatureOffset; // file offset of code signature blob 39 | uint64_t codeSignatureSize; // size of code signature blob (zero means to end of file) 40 | uint64_t slideInfoOffset; // file offset of kernel slid info 41 | uint64_t slideInfoSize; // size of kernel slid info 42 | uint64_t localSymbolsOffset; // file offset of where local symbols are stored 43 | uint64_t localSymbolsSize; // size of local symbols information 44 | uint8_t uuid[16]; // unique value for each shared cache file 45 | uint64_t cacheType; // 0 for development, 1 for production 46 | uint32_t branchPoolsOffset; // file offset to table of uint64_t pool addresses 47 | uint32_t branchPoolsCount; // number of uint64_t entries 48 | uint64_t accelerateInfoAddr; // (unslid) address of optimization info 49 | uint64_t accelerateInfoSize; // size of optimization info 50 | uint64_t imagesTextOffset; // file offset to first dyld_cache_image_text_info 51 | uint64_t imagesTextCount; // number of dyld_cache_image_text_info entries 52 | uint64_t patchInfoAddr; // (unslid) address of dyld_cache_patch_info 53 | uint64_t patchInfoSize; // Size of all of the patch information pointed to via the dyld_cache_patch_info 54 | uint64_t otherImageGroupAddrUnused; // unused 55 | uint64_t otherImageGroupSizeUnused; // unused 56 | uint64_t progClosuresAddr; // (unslid) address of list of program launch closures 57 | uint64_t progClosuresSize; // size of list of program launch closures 58 | uint64_t progClosuresTrieAddr; // (unslid) address of trie of indexes into program launch closures 59 | uint64_t progClosuresTrieSize; // size of trie of indexes into program launch closures 60 | uint32_t platform; // platform number (macOS=1, etc) 61 | uint32_t formatVersion : 8, // dyld3::closure::kFormatVersion 62 | dylibsExpectedOnDisk : 1, // dyld should expect the dylib exists on disk and to compare inode/mtime to see if cache is valid 63 | simulator : 1, // for simulator of specified platform 64 | locallyBuiltCache : 1, // 0 for B&I built cache, 1 for locally built cache 65 | builtFromChainedFixups : 1, // some dylib in cache was built using chained fixups, so patch tables must be used for overrides 66 | padding : 20; // TBD 67 | uint64_t sharedRegionStart; // base load address of cache if not slid 68 | uint64_t sharedRegionSize; // overall size of region cache can be mapped into 69 | uint64_t maxSlide; // runtime slide of cache can be between zero and this value 70 | uint64_t dylibsImageArrayAddr; // (unslid) address of ImageArray for dylibs in this cache 71 | uint64_t dylibsImageArraySize; // size of ImageArray for dylibs in this cache 72 | uint64_t dylibsTrieAddr; // (unslid) address of trie of indexes of all cached dylibs 73 | uint64_t dylibsTrieSize; // size of trie of cached dylib paths 74 | uint64_t otherImageArrayAddr; // (unslid) address of ImageArray for dylibs and bundles with dlopen closures 75 | uint64_t otherImageArraySize; // size of ImageArray for dylibs and bundles with dlopen closures 76 | uint64_t otherTrieAddr; // (unslid) address of trie of indexes of all dylibs and bundles with dlopen closures 77 | uint64_t otherTrieSize; // size of trie of dylibs and bundles with dlopen closures 78 | uint32_t mappingWithSlideOffset; // file offset to first dyld_cache_mapping_and_slide_info 79 | uint32_t mappingWithSlideCount; // number of dyld_cache_mapping_and_slide_info entries 80 | uint64_t field_140; 81 | uint64_t field_148; 82 | uint64_t field_150; 83 | uint64_t field_158; 84 | uint64_t field_160; 85 | uint32_t field_168; 86 | uint32_t field_16C; 87 | uint32_t field_170; 88 | uint32_t field_174; 89 | uint32_t field_178; 90 | uint32_t field_17C; 91 | uint32_t field_180; 92 | uint32_t field_184; 93 | uint32_t field_188; 94 | uint32_t field_18C; 95 | uint8_t symbolSubCacheUUID[16]; 96 | uint64_t field_1A0; 97 | uint64_t field_1A8; 98 | uint64_t field_1B0; 99 | uint32_t field_1B8; 100 | uint32_t field_1BC; 101 | uint32_t field_1C0; 102 | uint32_t images_count; 103 | }; 104 | 105 | struct dyld_cache_mapping_info { 106 | uint64_t address; 107 | uint64_t size; 108 | uint64_t fileOffset; 109 | uint32_t maxProt; 110 | uint32_t initProt; 111 | }; 112 | 113 | struct dyld_cache_image_info 114 | { 115 | uint64_t address; 116 | uint64_t modTime; 117 | uint64_t inode; 118 | uint32_t pathFileOffset; 119 | uint32_t pad; 120 | }; 121 | 122 | struct dyld_cache_slide_info 123 | { 124 | uint32_t version; // currently 1 125 | uint32_t toc_offset; 126 | uint32_t toc_count; 127 | uint32_t entries_offset; 128 | uint32_t entries_count; 129 | uint32_t entries_size; // currently 128 130 | // uint16_t toc[toc_count]; 131 | // entrybitmap entries[entries_count]; 132 | }; 133 | 134 | 135 | struct dyld_cache_local_symbols_info 136 | { 137 | uint32_t nlistOffset; // offset into this chunk of nlist entries 138 | uint32_t nlistCount; // count of nlist entries 139 | uint32_t stringsOffset; // offset into this chunk of string pool 140 | uint32_t stringsSize; // byte count of string pool 141 | uint32_t entriesOffset; // offset into this chunk of array of dyld_cache_local_symbols_entry 142 | uint32_t entriesCount; // number of elements in dyld_cache_local_symbols_entry array 143 | }; 144 | 145 | struct dyld_cache_local_symbols_entry_32 146 | { 147 | uint32_t dylibOffset; // offset in cache file of start of dylib 148 | uint32_t nlistStartIndex; // start index of locals for this dylib 149 | uint32_t nlistCount; // number of local symbols for this dylib 150 | }; 151 | 152 | struct dyld_cache_local_symbols_entry_64 153 | { 154 | uint64_t dylibOffset; // offset in cache file of start of dylib 155 | uint32_t nlistStartIndex; // start index of locals for this dylib 156 | uint32_t nlistCount; // number of local symbols for this dylib 157 | }; 158 | 159 | 160 | #define MACOSX_DYLD_SHARED_CACHE_DIR "/var/db/dyld/" 161 | #define IPHONE_DYLD_SHARED_CACHE_DIR "/System/Library/Caches/com.apple.dyld/" 162 | #define DYLD_SHARED_CACHE_BASE_NAME "dyld_shared_cache_" 163 | 164 | 165 | 166 | #endif // __DYLD_CACHE_FORMAT__ 167 | 168 | 169 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MIP 2 | MIP, macOS Injection Platform, is a platform that lets macOS developers create operating system tweaks by allowing them to create code that can be injected automatically to any GUI process. MIP was originally written as part of my unreleased macOS theming mechanism, because other techniques could not inject code in an early enough and reliable timing which allows all theming features. MIP was later rewritten to be more stable, generic and versatile, as well as easier to maintain, and became open source (MIT). 3 | 4 | ## Disclaimer 5 | Code injection is dangerous, and might make your computer unstable or unsable if you don't know what you're doing. Use MIP with care. In case of emergency, delete `/Library/LaunchDaemons/local.lsdinjector.plist` using the recovery boot. 6 | 7 | MIP should work on all 64-bit versions of macOS, but it's deliberately limited to Yosemite and newer; released versions were not tested on versions older than Sierra. 8 | 9 | ## MIP's Advantages 10 | MIP has the following advantages when comparing to other injection techniques: 11 | 12 | * Injects to every single GUI process, including non-apps processes such as system processes with GUI, the dock, tab processes of browsers, etc. 13 | * Injects to restricted binaries (binaries using a `__RESTRICT,__restrict` section or special entitlements, where dyld enviroment variables are ignored) 14 | * The injected code runs on the main thread, and blocks it until it finishes initializing; preventing race bugs. 15 | * The injected code is always injected at a deterministic time, in the same code flow for every app, making code easier to debug 16 | * The injected code runs at a very early stage in the process's lifetime, before the UI frameworks start running, making advanced UI customizations possible 17 | * Can be installed without reboot 18 | * Does not modify any system file on disk and can be easily uninstalled without rebooting 19 | * Does not use `DYLD_INSERT_LIBRARIES`, which may break the system if a file is deleted. 20 | * Works with both 32- and 64-bit applications, and allows injection to Garbage Collected processes (On El Capitan and older, GC was removed in Sierra) 21 | * Supports every macOS major up to and including Sonoma 22 | * Supports ARM64-based Macs 23 | * Supports injection to translated Rosetta 2 processes 24 | 25 | ## How To Compile 26 | You will need Xcode's command-line tools, as well as binutils for `gobjcopy` (`brew install binutils`), which should be linked as `gobjcopy`. You will also need a signing identity, which may be self-signed. Not signing MIP binaries properly will make your system unstable! On Intel Macs, you will need the [10.13 SDK](https://github.com/phracker/MacOSX-SDKs/releases/download/11.3/MacOSX10.13.sdk.tar.xz), to compile the 32-bit portions of MIP. 27 | 28 | To compile, simply run `make SIGN_IDENTITY=` inside the MIP folder, or `make SYSROOT=path/to/MacOSX10.13.sdk SIGN_IDENTITY=` on Intel Macs. 29 | 30 | ## How To Install/Uninstall 31 | MIP requires disabling SIP (System Integrity Protection) both during installation and during use. On ARM64 Macs, you will also need to enable the arm64e preview ABI (`sudo nvram boot-args=-arm64e_preview_abi`) 32 | 33 | To install, simply run `make install` inside the MIP folder. You can uninstall by running `make uninstall"`. If you can't get a terminal to open due a misconfiguration of MIP, you can disable it by deleting `/Library/LaunchDaemons/local.lsdinjector.plist` using the recovery boot, which will disable MIP. 34 | 35 | Bundles are installed to `/Library/Apple/System/Library/Frameworks/mip/Bundles`. 36 | 37 | ### Why Must I Disable SIP? 38 | SIP not only prevents system files and folders from being modified, but also prevents debugging of any SIP-protected binary. Code injection, by definition, requires static (on filesystem) or dynamic (via debugging) modification of binary files, and MIP obviously cannot operate with such limitations. Even if you do not intend to inject code to Apple provided binaries, MIP operates by injecting code to a system process (launchservicesd), which later injects code to all other processes. 39 | 40 | In El Capitan, MIP can be modified to run with SIP enabled as long as it was disabled during installation, due to task ports being leaked to launchservicesd via XPC messages, but this is neither recommended nor supported, and requires modifying launchservicesd's launchd plist file. This potential vulnerability was fixed in Sierra. 41 | 42 | ## Sample Bundles 43 | MIP includes Alt-Zoom as both a useful tweak and a bundle development reference. Alt-Zoom is a bundle that lets you modify the default behavior of the zoom button and the way modifier keys affect its behavior. You can install it by running `make SIGN_IDENTITY=` and `make install` in Alt-Zoom's folder in the repository. It has a setting app to control its configuration. 44 | 45 | ## Injection Filters 46 | The processes a bundle is loaded into are determined by that bundle's `Info.plist` file. By default MIP filters in a white-list manner. The following keys are used to control filtering: 47 | 48 | * `MIPBundleNames` - Array, lists bundle names to inject alongside (or to ignore in black-list mode). 49 | * This can be either an app or framework bundle identifier, such as `com.apple.AppKit` 50 | * `MIPExcludedBundleNames` - Array, lists bundle names to _never_ inject alongside. 51 | * Takes precedence over all other filters 52 | * Not affected by black-list mode 53 | * `MIPExecutableNames` - Array, controls the executable basenames to inject to (or to ignore in black-list mode). 54 | * `MIPUseBlacklistMode` - Boolean, sets the filtering mode to black-list mode if true. 55 | * `MIPSupportsGC` - Boolean, tells MIP the injected bundle supports Garbage Collection and may be injected to GC-enabled processes. If incorrectly set to true while the injected bundle does not actually support GC, the injected bundle will crash the process. Garbage Collection is not available in macOS Sierra's Objective-C runtime, no need to support it if you're targeting Sierra and newer. 56 | 57 | Additionally, because bundles are installed on a system-wide basis (For security reasons, some Apple-signed binarys will intentionally crash when loading libraries not owned by root), a user may disable a specific bundle by creating a plist file at `~/Library/MIP/settings.plist` with an array `MIPDisabledBundles` set to a list of the disabled bundle identifiers. 58 | 59 | ## How It Works 60 | During installation, MIP installs 4 files; lsdinjector.dylib and loader.dylib, a command line utility called `inject`, and a launch daemon. 61 | 62 | `inject` is a command line utility that allows injecting a dylib file to a running process by PID. The launch daemon MIP installs runs `inject` as root when the system boots, and injects lsdinjector.dylib to launchservicesd. 63 | 64 | When a Cocoa process launches, one of the early things it does is calling `_LSApplicationCheckIn`. This function sends an XPC message to launchserivcesd, and blocks until it receives a reply. lsdinjector.dylib hooks the function in launchservicesd that handles that XPC message. The hook will inject loader.dylib to the process before it sends a reply, so the process is still blocked during the injection. This uses the same code as the `inject` utility. 65 | 66 | When the reply is sent, the process resumes running at the injected code, running loader.dylib's initializer which loads all tweak bundles. When loader.dylib finishes, the process' normal operation resumes. 67 | 68 | This method of injection ensures the injected code *always* runs in the same flow and in the same thread. 69 | 70 | To make sure all libraries, bundles and user settings and data are accessible from every process the user runs, even under very strict sandboxing, all MIP data is located in /Library/Apple/System/Library/Frameworks/mip. User data is located in /Library/Apple/System/Library/Frameworks/mip/user_data/UID, with the correct owner. A symlink to this folder is created in ~/Library/MIP for each user for convenience, but bundles should use the real path directly. 71 | 72 | ### How The Inject Function Works 73 | The inject function both lsdinjector.dylib and `inject` use works by modifying the main thread's state to simulate a `call` instruction. 74 | 75 | First, it copies a payload bootstrap code to the process (On Intel Macs, x86 or x86-64 code, depending on the processes), as well as a pointer to dyld's load address and the path of the dylib to inject. Then, it pauses the thread (to ensure atomicity) and modifies its PC/IP, SP and stack contents to simulate a call instruction to the entry function of the payload, and resumes the thread. 76 | 77 | The payload function is a compiled but unlinked C code, so it can't used any external symbols such as dlopen directly. It is declared in a way that saves and restores all registers, and does additional calls to save and restore the flags register as well. The function uses the dyld pointer provided by the injector to find a pointer to dyld's dlopen function, and then calls it with the provided dylib path. 78 | 79 | ## Upgrading Notes 80 | 81 | If you were using an old version on MIP that used `/usr/lib/mip` as its data directory on macOS Mojave or older, upon upgrading to macOS Catalina or newer MIP bundles that linked against `/usr/lib/mip/loader.dylib` will cease functioning. They must be recompiled and linked against `/Library/Apple/System/Library/Frameworks/mip/loader.dylib` instead. 82 | --------------------------------------------------------------------------------