forceFullDesktopBar icon indicating copy to clipboard operation
forceFullDesktopBar copied to clipboard

File sizes are too big

Open briankendall opened this issue 2 years ago • 5 comments

As a result of using frida-core and frida-gum, the binaries for forceFullDesktopBar have ballooned from a petite 120 KB to a ridiculous 120 MB.

Need to find an alternative library or libraries that just handles code injection and function swapping without any of the other unneeded machinery, but also supports both x86_64 and arm64.

briankendall avatar Oct 25 '21 18:10 briankendall

Kind of a self-promotion, but I rolled my own simple version that you might be interested in. It's much less flexible (currently Monterey+) because I haven't bothered to make it support anything earlier, but it might give you some ideas.

For code injection into Dock, I'm using this: https://gist.github.com/saagarjha/a70d44951cb72f82efee3317d80ac07f. I create a LauchDaemon plist that runs it, like so:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Label</key>
	<string>com.saagarjha.ForceFullDesktopBar</string>
	<key>ProgramArguments</key>
	<array>
		<string>/Users/saagarjha/bin/library_injector</string>
		<string>/System/Library/CoreServices/Dock.app/Contents/MacOS/Dock</string>
		<string>/Users/saagarjha/bin/libforce_full_desktop_bar.dylib</string>
	</array>
	<key>KeepAlive</key>
	<dict>
		<key>OtherJobEnabled</key>
		<dict>
			<key>com.apple.WindowServer</key>
			<true/>
		</dict>
	</dict>
</dict>
</plist>

Tying it to WindowServer means it runs early and can catch any launches of the Dock process. Then, I inject this code:

#import "swizzer.h"
#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>

#define dockSwipeGesturePhase 123
#define dockSwipeGestureMotion 134
#define dockSwipeEvent 30
#define kIOHIDGestureMotionVerticalY 2
#define kIOHIDGestureMotionDoubleTap 6

static int mouseOverrideCount;

static Swizzler<void, id, long long> Dock_WVExpose_changeMode_ {
	NSClassFromString(@"Dock.WVExpose"), @selector(changeMode:), [](auto self, auto mode) {
		if (mode == 1) {
			mouseOverrideCount = 1;
		}
		Dock_WVExpose_changeMode_(self, mode);
	}
};

static auto isStartOfTrackpadSwipeUpEvent(CGEventType type, CGGesturePhase phase, uint64_t direction) {
	return type == dockSwipeEvent && phase == kCGGesturePhaseBegan && direction == kIOHIDGestureMotionVerticalY;
}

static auto isDoubleTapEvent(CGEventType type, CGGesturePhase phase, uint64_t direction) {
	return type == dockSwipeEvent && phase == kCGGesturePhaseNone && direction == kIOHIDGestureMotionDoubleTap;
}

static Swizzler<void, id, CGEventRef> DOCKGestures_handleEvent_ {
	NSClassFromString(@"DOCKGestures"), @selector(handleEvent:), [](auto self, auto event) {
		if (event) {
			auto type = CGEventGetType(event);
			auto phase = (CGGesturePhase)CGEventGetIntegerValueField(event, static_cast<CGEventField>(dockSwipeGestureMotion));
			auto direction = (CGGesturePhase)CGEventGetIntegerValueField(event, static_cast<CGEventField>(dockSwipeGesturePhase));

			if (isStartOfTrackpadSwipeUpEvent(type, phase, direction) || isDoubleTapEvent(type, phase, direction)) {
				mouseOverrideCount = 2;
			}
		}
		DOCKGestures_handleEvent_(self, event);
	}
};

extern "C" {
CGPoint CGSCurrentInputPointerPosition(void);
};

static CGPoint moveToTopOfScreen(CGPoint p) {
	CGDirectDisplayID displayContainingCursor;
	uint32_t matchingDisplayCount = 0;

	CGGetDisplaysWithPoint(p, 1, &displayContainingCursor, &matchingDisplayCount);

	if (matchingDisplayCount >= 1) {
		CGRect rect = CGDisplayBounds(displayContainingCursor);
		p.y = rect.origin.y + 1;
		return p;
	} else {
		NSLog(@"forceFullDesktopBar error: could not determine which screen contains mouse coordinates (%f %f)", p.x, p.y);
		return p;
	}
}

static CGPoint overriden_CGSCurrentInputPointerPosition() {
	CGPoint result = CGSCurrentInputPointerPosition();

	if (mouseOverrideCount > 0) {
		mouseOverrideCount -= 1;
		result = moveToTopOfScreen(result);
	}

	return result;
}

__attribute__((used, section("__DATA,__interpose"))) static struct {
	CGPoint (*overridden_CGSCurrentInputPointerPosition)();
	CGPoint (*CGSCurrentInputPointerPosition)();
} CGSCurrentInputPointerPosition_overrides[] = {
    {overriden_CGSCurrentInputPointerPosition, CGSCurrentInputPointerPosition},
};

It's basically the same as your old code, except I'm using dyld interposing instead of fishhook and my own swizzling thing. This seems to work reliably for me, and because it doesn't use any dependencies it's not too bad in terms of size.

saagarjha avatar Nov 02 '21 12:11 saagarjha

Great! I may see if I can adapt this technique and make a new release. I hadn't come across dyld interposing while I was researching techniques for this project.

Were you able to inject this code when compiling it as arm64 rather than arm64e?

briankendall avatar Nov 02 '21 13:11 briankendall

No, it needs to be arm64e to match Dock.

saagarjha avatar Nov 03 '21 22:11 saagarjha

@saagarjha Have you tried using this technique in any of the macOS Ventura betas?

briankendall avatar Sep 22 '22 14:09 briankendall

Yes, the technique still works. My injection technique needs a small change in that it tries to peek at its own dyld to compute where various symbols are, but Ventura unmaps dyld after process initialization. For now I replaced that part with a quick-and-dirty patchfinder but I'll probably get around to fixing it properly at some point.

saagarjha avatar Sep 23 '22 09:09 saagarjha