Skip to content

Instantly share code, notes, and snippets.

@stek29
Created May 4, 2017
Embed
What would you like to do?
Frida Script to tweak PIP window alpha level on macOS
'use strict';
const getExport = function (module, name) {
const addr = Module.findExportByName(module, name);
if (!addr) throw ("Can't find export " + name);
else return addr;
}
const getCGFunction = function (name, returnType, argTypes) {
return (new NativeFunction(getExport('CoreGraphics', name), returnType, argTypes));
}
const getCFFunction = function (name, returnType, argTypes) {
return (new NativeFunction(getExport('CoreFoundation', name), returnType, argTypes));
}
const setWindowAlpha = function(windowId, alpha) {
// types
const CGSConnectionID = 'int';
const CGError = 'uint32';
const CGWindowID = 'uint32';
// constants
const kCGErrorSuccess = 0;
// private apis
const CGSMainConnectionID = getCGFunction('CGSMainConnectionID', CGSConnectionID, []);
const CGSSetWindowAlpha = getCGFunction('CGSSetWindowAlpha', CGError, [CGSConnectionID, CGWindowID, 'float']);
var err = CGSSetWindowAlpha(CGSMainConnectionID(), windowId, alpha);
if (err != kCGErrorSuccess)
throw("Can't set alpha for window " + windowID + ", got " + err);
}
const getWindowAlpha = function(windowId) {
// value to be returned
var alpha = -1;
// types
const CFTypeRef = 'pointer';
const CFArrayRef = 'pointer';
const CFAllocatorRef = 'pointer';
const CFIndex = 'long';
const CFArrayCallBacks = 'pointer';
// public apis
const CFArrayCreate = getCFFunction('CFArrayCreate', CFArrayRef, [CFAllocatorRef, 'pointer', CFIndex, CFArrayCallBacks]);
const CFRelease = getCFFunction('CFRelease', 'void', [CFTypeRef]);
const CGWindowListCreateDescriptionFromArray = getCGFunction('CGWindowListCreateDescriptionFromArray', CFArrayRef, [CFArrayRef]);
// keys for windowInfo dict
const createNSString = function(str) {
return ObjC.classes.NSString.stringWithString_(str);
}
var kCGWindowAlpha = createNSString('kCGWindowAlpha');
// CGWindowID is uint32 => 4 bytes
var widPtr = Memory.alloc(4);
Memory.writeU32(widPtr, windowId);
var widCFArrRef = CFArrayCreate(NULL, widPtr, 1, NULL);
var infosPtr = CGWindowListCreateDescriptionFromArray(widCFArrRef);
CFRelease(widCFArrRef);
var infos = new ObjC.Object(infosPtr);
if (infos.count() == 1) {
var info = infos.firstObject().retain();
// kCGWindowAlpha is guaranteed to exist
var alphaNSNum = info.objectForKey_(kCGWindowAlpha).retain();
alpha = alphaNSNum.floatValue();
alphaNSNum.release();
info.release();
} else {
// console.debug("Invalid count: " + infos.count());
alpha = -2;
}
rpc.exports
infos.release();
kCGWindowAlpha.release();
return alpha;
}
// seems like there could be only one PIP at a time
const getPIPWindowID = function() {
// value to be returned
var windowID = -1;
// types
const CGWindowID = 'uint32';
const CFArrayRef = 'pointer';
const CGWindowListOption = 'uint32';
// public apis
const CGWindowListCopyWindowInfo = getCGFunction('CGWindowListCopyWindowInfo', CFArrayRef, [CGWindowListOption, CGWindowID]);
// constants
const kCGWindowListOptionOnScreenOnly = (1 << 0);
const kCGWindowListExcludeDesktopElements = (1 << 4);
const kCGNullWindowID = 0;
// helper function
const createNSString = function(str) {
return ObjC.classes.NSString.stringWithString_(str);
}
// keys for windowInfo dict
// it doesn't seem to be possible to get them
// using getCGExport
var kCGWindowName = createNSString('kCGWindowName');
var kCGWindowOwnerName = createNSString('kCGWindowOwnerName');
var kCGWindowNumber = createNSString('kCGWindowNumber');
// ones we're looking for
var PIPAgentNSS = createNSString('PIPAgent');
var PIPNSS = createNSString('PIP');
var windowInfosPtr = CGWindowListCopyWindowInfo(
kCGWindowListOptionOnScreenOnly|kCGWindowListExcludeDesktopElements,
kCGNullWindowID
);
var windowInfos = new ObjC.Object(windowInfosPtr);
for (var i = windowInfos.count() - 1; windowID == -1 && i >= 0; i--) {
var windowInfo = windowInfos.objectAtIndex_(i).retain();
// Both OwnerName and Name are optional, so nil check is required
var ownerNameNSS = windowInfo.objectForKey_(kCGWindowOwnerName);
if (ownerNameNSS) ownerNameNSS = ownerNameNSS.retain();
var nameNSS = windowInfo.objectForKey_(kCGWindowName);
if (nameNSS) nameNSS = nameNSS.retain();
if (nameNSS && nameNSS.isEqualToString_(PIPNSS) && ownerNameNSS && ownerNameNSS.isEqualToString_(PIPAgentNSS)) {
var windowNumberNS = windowInfo.objectForKey_(kCGWindowNumber).retain()
windowID = windowNumberNS.intValue()
windowNumberNS.release()
}
if (ownerNameNSS) ownerNameNSS.release();
if (nameNSS) nameNSS.release();
windowInfo.release();
}
windowInfos.release();
PIPNSS.release();
PIPAgentNSS.release();
kCGWindowNumber.release();
kCGWindowOwnerName.release();
kCGWindowName.release();
return windowID;
}
rpc.exports = {
getWindowAlpha: getWindowAlpha,
setWindowAlpha: setWindowAlpha,
getPIPWindowID: getPIPWindowID
}
@stek29
Copy link
Author

stek29 commented May 4, 2017

Using NSRunningApplication runningApplicationsWithBundleIdentifier: to get PIPAgent's PID would be a good idea

Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment