[metro] Rewrite patches
This commit is contained in:
parent
9809ef2cd4
commit
01c1694446
3 changed files with 80 additions and 49 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { instead } from "@lib/api/patcher";
|
||||
import { after, instead } from "@lib/api/patcher";
|
||||
|
||||
export type MetroModules = { [id: string]: any; };
|
||||
export type PropIntellisense<P extends string | symbol> = Record<P, any> & Record<PropertyKey, any>;
|
||||
|
|
@ -17,21 +17,33 @@ const blacklist = (id: string) => Object.defineProperty(window.modules, id, {
|
|||
writable: true
|
||||
});
|
||||
|
||||
const functionToString = Function.prototype.toString;
|
||||
|
||||
for (const id in window.modules) {
|
||||
const moduleDefinition = window.modules[id];
|
||||
|
||||
if (moduleDefinition.factory) {
|
||||
after("factory", moduleDefinition, ({ 4: moduleObject }) => {
|
||||
if (moduleObject.exports) onModuleRequire(moduleObject.exports);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Blacklist any "bad-actor" modules, e.g. the dreaded null proxy, the window itself, or undefined modules
|
||||
for (const id in window.modules) {
|
||||
const module = window.modules[id]?.publicModule?.exports;
|
||||
const module = requireModule(id);
|
||||
|
||||
if (!module || module === window || module.proxygone === null) {
|
||||
if (!module || module === window || module["Revenge EOL when?"] === null) {
|
||||
blacklist(id);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const checked = new WeakSet();
|
||||
const onModuleCheck = (exports: any) => {
|
||||
if (typeof exports !== "object" || checked.has(exports)) return;
|
||||
checked.add(exports);
|
||||
let patchedInspectSource = false;
|
||||
|
||||
function onModuleRequire(exports: any) {
|
||||
// There are modules registering the same native component
|
||||
if (exports?.default?.name === "requireNativeComponent") {
|
||||
instead("default", exports, (args, orig) => {
|
||||
try {
|
||||
|
|
@ -42,6 +54,7 @@ const onModuleCheck = (exports: any) => {
|
|||
});
|
||||
}
|
||||
|
||||
// Hook DeveloperExperimentStore
|
||||
if (exports?.default?.constructor?.displayName === "DeveloperExperimentStore") {
|
||||
exports.default = new Proxy(exports.default, {
|
||||
get: (target, property, receiver) => {
|
||||
|
|
@ -55,35 +68,32 @@ const onModuleCheck = (exports: any) => {
|
|||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function maybeInitializeModule(id: string) {
|
||||
if (modules[id].isInitialized) return;
|
||||
// Funny infinity recursion caused by a race condition
|
||||
if (!patchedInspectSource && window["__core-js_shared__"]) {
|
||||
const inspect = (f: Function) => typeof f === "function" && functionToString.apply(f, []);
|
||||
window["__core-js_shared__"].inspectSource = inspect;
|
||||
patchedInspectSource = true;
|
||||
}
|
||||
}
|
||||
|
||||
function requireModule(id: string) {
|
||||
if (modules[id].isInitialized) return __r(id);
|
||||
|
||||
// Disable Internal Metro error reporting logic
|
||||
const originalHandler = window.ErrorUtils.getGlobalHandler();
|
||||
window.ErrorUtils.setGlobalHandler(null);
|
||||
|
||||
try {
|
||||
// There's a dum Function.prototype.toString polyfill somewhere in Discord's codebase
|
||||
const orig = Function.prototype.toString;
|
||||
Object.defineProperty(Function.prototype, "toString", {
|
||||
value: orig,
|
||||
configurable: true,
|
||||
writable: false
|
||||
});
|
||||
var exports = __r(id); // metroRequire(id);
|
||||
} catch {
|
||||
var exports = undefined;
|
||||
}
|
||||
|
||||
// Disable Internal Metro error reporting logic
|
||||
const originalHandler = window.ErrorUtils.getGlobalHandler();
|
||||
window.ErrorUtils.setGlobalHandler(null);
|
||||
|
||||
__r(id); // metroRequire(id);
|
||||
|
||||
// Done initializing! Now, revert our hacks
|
||||
window.ErrorUtils.setGlobalHandler(originalHandler);
|
||||
Object.defineProperty(Function.prototype, "toString", {
|
||||
value: orig,
|
||||
configurable: true,
|
||||
writable: true
|
||||
});
|
||||
} catch { }
|
||||
// Done initializing! Now, revert our hacks
|
||||
window.ErrorUtils.setGlobalHandler(originalHandler);
|
||||
|
||||
return exports;
|
||||
}
|
||||
|
||||
// Function to filter through modules
|
||||
|
|
@ -91,25 +101,16 @@ const filterModules = (modules: MetroModules, single = false) => (filter: (m: an
|
|||
const found = [];
|
||||
|
||||
for (const id in modules) {
|
||||
const module = modules[id]?.publicModule?.exports;
|
||||
const exports = requireModule(id);
|
||||
|
||||
maybeInitializeModule(id);
|
||||
|
||||
if (!module) {
|
||||
blacklist(id);
|
||||
continue;
|
||||
if (exports.default && exports.__esModule && filter(exports.default)) {
|
||||
if (single) return exports.default;
|
||||
found.push(exports.default);
|
||||
}
|
||||
|
||||
onModuleCheck(module);
|
||||
|
||||
if (module.default && module.__esModule && filter(module.default)) {
|
||||
if (single) return module.default;
|
||||
found.push(module.default);
|
||||
}
|
||||
|
||||
if (filter(module)) {
|
||||
if (single) return module;
|
||||
else found.push(module);
|
||||
if (filter(exports)) {
|
||||
if (single) return exports;
|
||||
else found.push(exports);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { findByProps } from "@metro/filters";
|
||||
import { findByName } from "@metro/filters";
|
||||
|
||||
export type LoggerFunction = (...messages: any[]) => void;
|
||||
export interface Logger {
|
||||
|
|
@ -11,5 +11,5 @@ export interface Logger {
|
|||
verbose: LoggerFunction;
|
||||
}
|
||||
|
||||
export const logModule = findByProps("setLogFn").default;
|
||||
export const logModule = findByName("Logger");
|
||||
export const logger: Logger = new logModule("Bunny");
|
||||
|
|
|
|||
30
src/lib/utils/onceDefined.ts
Normal file
30
src/lib/utils/onceDefined.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { LiteralUnion } from "type-fest";
|
||||
|
||||
type KeyOfOrAny<P, T extends object> = P extends keyof T ? T[P] : any;
|
||||
|
||||
export function hookDefineProperty<
|
||||
T extends object,
|
||||
P extends LiteralUnion<keyof T, PropertyKey>
|
||||
>(target: T, property: LiteralUnion<keyof T, PropertyKey>, cb: (val: KeyOfOrAny<P, T>) => KeyOfOrAny<P, T>) {
|
||||
const targetAsAny = target as any;
|
||||
|
||||
if (property in target) {
|
||||
return void cb(targetAsAny[property]);
|
||||
}
|
||||
|
||||
let value: any;
|
||||
|
||||
Object.defineProperty(targetAsAny, property, {
|
||||
get: () => value,
|
||||
set(v) {
|
||||
value = cb(v) ?? v;
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: false
|
||||
});
|
||||
|
||||
return () => {
|
||||
delete targetAsAny[property];
|
||||
targetAsAny[property] = value;
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue