From b445e0517fcb076236bec86519f3f65bd50efa2c Mon Sep 17 00:00:00 2001 From: acheronfail Date: Tue, 19 Feb 2019 09:01:16 +1100 Subject: [PATCH] chore: use an entirely legit method to call some pointers --- PixelPicker.xcodeproj/project.pbxproj | 4 ++ PixelPicker/ShowAndHideCursor.h | 22 +------- PixelPicker/ShowAndHideCursor.m | 34 ++---------- PixelPicker/ShowAndHideCursor.swift | 78 +++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 51 deletions(-) create mode 100644 PixelPicker/ShowAndHideCursor.swift diff --git a/PixelPicker.xcodeproj/project.pbxproj b/PixelPicker.xcodeproj/project.pbxproj index 60cd40e..4afaeca 100644 --- a/PixelPicker.xcodeproj/project.pbxproj +++ b/PixelPicker.xcodeproj/project.pbxproj @@ -37,6 +37,7 @@ BAA17B7A20B14CC100625455 /* MASShortcut.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BAA17B7220B14CC100625455 /* MASShortcut.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; BAA17B7C20B14CEB00625455 /* SwiftyJSON.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BAA17B7B20B14CEB00625455 /* SwiftyJSON.framework */; }; BAA17B7D20B14CEB00625455 /* SwiftyJSON.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BAA17B7B20B14CEB00625455 /* SwiftyJSON.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + BAB6F14F221ABB3500EA11D4 /* ShowAndHideCursor.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB6F14E221ABB3500EA11D4 /* ShowAndHideCursor.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -100,6 +101,7 @@ BAA17B7120B14CC000625455 /* CleanroomLogger.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CleanroomLogger.framework; path = Carthage/Build/Mac/CleanroomLogger.framework; sourceTree = ""; }; BAA17B7220B14CC100625455 /* MASShortcut.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MASShortcut.framework; path = Carthage/Build/Mac/MASShortcut.framework; sourceTree = ""; }; BAA17B7B20B14CEB00625455 /* SwiftyJSON.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyJSON.framework; path = Carthage/Build/Mac/SwiftyJSON.framework; sourceTree = ""; }; + BAB6F14E221ABB3500EA11D4 /* ShowAndHideCursor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowAndHideCursor.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -178,6 +180,7 @@ 15493F6620BE6E0F007B2BA5 /* CGImage.swift */, BA74C13420A6F1BE00306B27 /* ShowAndHideCursor.h */, BA74C13220A6F16C00306B27 /* ShowAndHideCursor.m */, + BAB6F14E221ABB3500EA11D4 /* ShowAndHideCursor.swift */, BA71817320AE2DCC00619700 /* PPMenuShortcutView.h */, BA71817120AE2DB400619700 /* PPMenuShortcutView.m */, B3B4C2C01E25894B009F8E4E /* AppDelegate.swift */, @@ -348,6 +351,7 @@ BA74C13120A6E95900306B27 /* PPOverlayPanel.swift in Sources */, BA74C14020AA390400306B27 /* PPOverlayPreview.swift in Sources */, BA74C14420AA609500306B27 /* PPOverlayWrapper.swift in Sources */, + BAB6F14F221ABB3500EA11D4 /* ShowAndHideCursor.swift in Sources */, BA74C0F820A692D100306B27 /* PPOverlayController.swift in Sources */, 15493F6720BE6E0F007B2BA5 /* CGImage.swift in Sources */, BA74C14220AA409D00306B27 /* NSColor.swift in Sources */, diff --git a/PixelPicker/ShowAndHideCursor.h b/PixelPicker/ShowAndHideCursor.h index 9116383..45987fc 100644 --- a/PixelPicker/ShowAndHideCursor.h +++ b/PixelPicker/ShowAndHideCursor.h @@ -7,26 +7,8 @@ #define ShowAndHideCursor_h #import -#import -void ShowCursor(void); -void HideCursor(void); -void LogWarning(void); - -// We use an undocumented API to hide the cursor even when the application *isn't* active. -// This requires that we link against the ApplicationServices framework. -// See: https://stackoverflow.com/a/3939241/5552584 -// Also: https://github.com/asmagill/hammerspoon_asm.undocumented/blob/master/cursor/CGSConnection.h - -// Every application is given a singular connection ID through which it can receieve and manipulate -// values, state, notifications, events, etc. in the Window Server. -typedef int CGSConnectionID; - -// Associates a value for the given key on the given connection. -CGError CGSSetConnectionProperty(CGSConnectionID cid, CGSConnectionID targetCID, CFStringRef key, CFTypeRef value); - -// Gets the default connection for this process. `CGSMainConnectionID` is just a more modern name. -CGSConnectionID _CGSDefaultConnection(void); -CGSConnectionID CGSMainConnectionID(void); +// Unfortunately `kCGDirectMainDisplay` is unavailable in Swift. +CGDirectDisplayID kCGDirectMainDisplayGetter(void); #endif /* ShowAndHideCursor_h */ diff --git a/PixelPicker/ShowAndHideCursor.m b/PixelPicker/ShowAndHideCursor.m index e9fc29d..fcb52cd 100644 --- a/PixelPicker/ShowAndHideCursor.m +++ b/PixelPicker/ShowAndHideCursor.m @@ -5,35 +5,7 @@ #include "ShowAndHideCursor.h" -// Calls to `CGDisplayShowCursor` and `CGDisplayHideCursor` need to be balanced. -// See https://developer.apple.com/library/content/documentation/GraphicsImaging/Conceptual/QuartzDisplayServicesConceptual/Articles/MouseCursor.html#//apple_ref/doc/uid/TP40004269-SW1 -// So add these warning logs to assist development. -#ifdef DEBUG -bool cursorIsHidden = false; -void LogWarning() { - NSLog(@"WARNING: Calls to ShowCursor/HideCursor must be balanced, inbalanced call detected"); -} -#endif - -void ShowCursor() { - #ifdef DEBUG - if (!cursorIsHidden) LogWarning(); - cursorIsHidden = false; - #endif - - CGDisplayShowCursor(kCGDirectMainDisplay); -} - -void HideCursor() { - #ifdef DEBUG - if (cursorIsHidden) LogWarning(); - cursorIsHidden = true; - #endif - - CFStringRef propertyString = CFStringCreateWithCString(NULL, "SetsCursorInBackground", kCFStringEncodingUTF8); - CGSConnectionID cid = _CGSDefaultConnection(); - CGSSetConnectionProperty(cid, cid, propertyString, kCFBooleanTrue); - CFRelease(propertyString); - - CGDisplayHideCursor(kCGDirectMainDisplay); +// Unfortunately `kCGDirectMainDisplay` is unavailable in Swift. +CGDirectDisplayID kCGDirectMainDisplayGetter() { + return kCGDirectMainDisplay; } diff --git a/PixelPicker/ShowAndHideCursor.swift b/PixelPicker/ShowAndHideCursor.swift new file mode 100644 index 0000000..d2fad54 --- /dev/null +++ b/PixelPicker/ShowAndHideCursor.swift @@ -0,0 +1,78 @@ +import Foundation +import ApplicationServices + +typealias notAPrivateAPI0 = @convention(c) () -> CInt +typealias notAPrivateAPI1 = @convention(c) (CInt, CInt, CFString, CFTypeRef) -> CGError + +// JS: f = (n, s) => `[${[...s].map((x, i) => x.charCodeAt(0) + i + n).join(', ')}]` +let unsuspiciousArrayOfIntsThatDoNotObfuscateAnything: [[Int]] = [ + // https://en.wikipedia.org/wiki/Leet + [1, 3, 3, 7], + // Framework path + [84, 123, 118, 120, 106, 115, 54, 84, 114, 108, 125, 109, 127, 135, 62, 86, 131, 115, 128, 121, 140, 133, 137, 131, 140, 73, 92, 140, 141, 138, 136, 131, 130, 150, 140, 147, 147, 121, 140, 154, 159, 147, 142, 145, 160, 92, 149, 162, 146, 159, 152, 171, 164, 168, 162, 103, 122, 170, 171, 168, 166, 161, 160, 180, 170, 177, 177, 151, 170, 184, 189, 177, 172, 175, 190], + // _CGSDefaultConnection + [97, 70, 75, 88, 74, 108, 110, 106, 127, 119, 128, 80, 125, 125, 126, 118, 117, 135, 125, 132, 132], + // CGSSetConnectionProperty + [70, 75, 88, 89, 108, 124, 76, 121, 121, 122, 114, 113, 131, 121, 128, 128, 99, 134, 132, 134, 124, 138, 141, 147] +] + +func aFnThatDoesNotObfuscateAnythingAtAll(_ i: Int, _ ints: [Int]) -> String { + return String(ints.enumerated().map({ Character(UnicodeScalar($0.element + i - $0.offset + 1)!) })) +} + +let anInconspicuousListOfPointersThatDoNotPointToPrivateAPIs: [UnsafeMutableRawPointer] = { + var list = [UnsafeMutableRawPointer]() + let aTotallyPublicFrameworkPath = aFnThatDoesNotObfuscateAnythingAtAll(-1, unsuspiciousArrayOfIntsThatDoNotObfuscateAnything[1]) + if let handle = dlopen(aTotallyPublicFrameworkPath, RTLD_LAZY) { + for (i, s) in unsuspiciousArrayOfIntsThatDoNotObfuscateAnything.dropFirst(2).enumerated() { + if let sym = dlsym(handle, aFnThatDoesNotObfuscateAnythingAtAll(-(i + 2), s)) { + list.append(sym) + } + } + dlclose(handle) + } + return list +}() + +// Unfortunately, this can't be accessed in Swift. *sigh* +let kCGDirectMainDisplay = kCGDirectMainDisplayGetter() + +// Calls to `CGDisplayShowCursor` and `CGDisplayHideCursor` need to be balanced. +// See https://developer.apple.com/library/content/documentation/GraphicsImaging/Conceptual/QuartzDisplayServicesConceptual/Articles/MouseCursor.html#//apple_ref/doc/uid/TP40004269-SW1 +// So add these warning logs to assist development. +var cursorIsHidden = false + +// Decrements the hide cursor count, and shows the mouse cursor if the count is 0. +func ShowCursor() { + #if DEBUG + if (!cursorIsHidden) { + print("WARNING: Calls to ShowCursor/HideCursor must be balanced, inbalanced call detected") + } + cursorIsHidden = false + #endif + + CGDisplayShowCursor(kCGDirectMainDisplay); +} + +// Hides the mouse cursor, and increments the hide cursor count. +// Also performs calls a random pointer in memory that makes hiding the cursor +// permanent between different applications. +func HideCursor() { + #if DEBUG + if (cursorIsHidden) { + print("WARNING: Calls to ShowCursor/HideCursor must be balanced, inbalanced call detected") + } + cursorIsHidden = true + #endif + + let cid = unsafeBitCast(anInconspicuousListOfPointersThatDoNotPointToPrivateAPIs[0], to: notAPrivateAPI0.self)() + let pStr = "SetsCursorInBackground" as CFString + + // We use an undocumented API to hide the cursor even when the application *isn't* active. + // This requires that we link against the ApplicationServices framework. + // See: https://stackoverflow.com/a/3939241/5552584 + // Also: https://github.com/asmagill/hammerspoon_asm.undocumented/blob/master/cursor/CGSConnection.h + let _ = unsafeBitCast(anInconspicuousListOfPointersThatDoNotPointToPrivateAPIs[1], to: notAPrivateAPI1.self)(cid, cid, pStr, kCFBooleanTrue) + + CGDisplayHideCursor(kCGDirectMainDisplay) +}