Skip to content

Instantly share code, notes, and snippets.

@holly-hacker
Created August 4, 2018 12:44
Show Gist options
  • Save holly-hacker/d996c69fb830533af0395cd3e17767c6 to your computer and use it in GitHub Desktop.
Save holly-hacker/d996c69fb830533af0395cd3e17767c6 to your computer and use it in GitHub Desktop.
// Inspired by DiscordInjections, but written from scratch
// https://github.com/DiscordInjections/DiscordInjections/blob/ea8cd3bfcbe2d1bf233f5b002df1830d9eafd245/engine/plugins/hooks/index.js
declare interface Window {
webpackJsonp: WebpackHelper.WebpackJsonpObject[];
}
namespace WebpackHelper {
declare interface InputConditions {
conditions?: [(x: any) => boolean];
functions?: string[];
members?: string[];
}
export declare interface WebpackJsonpObject {
[0]: number[];
[1]: Array<(i: {i: number, l: boolean, exports: object}, b: boolean, r: RequireObject) => any>;
[2]?: number[][];
}
declare interface RequireObject {
c: {
[idx: number]: {
exports?: any;
i: number;
l: boolean;
},
};
m: Array<(e: any, t: any, n?: any) => any>;
p: string;
s: number;
}
let require: RequireObject;
let queue: Array<(req: RequireObject) => void> = [];
let hookInstalled = false;
export async function getModuleAsync(conditions: InputConditions) {
return new Promise<any>((r) => getModule(conditions, r));
}
export function getModule(conditions: InputConditions, callback: (obj: any) => void) {
// install the hook
ensureHookInstalled().then(() => {
// if we don't have require yet, add to a queue of sorts
if (!require) {
queue.push((req) => getModule(conditions, callback));
return;
}
// we have require, actually do something
let i = 0;
let runThroughAll = () => {
for (; i < Object.values(require.c).length; i++) {
const modDef = require.c[i];
if (!modDef || !modDef.exports)
continue;
const exp = modDef.exports;
// run all checks
if (conditions.functions && !conditions.functions.every((x) => typeof exp[x] === "function"))
continue;
if (conditions.members && !conditions.members.every((x) => typeof exp[x] !== "function"))
continue;
if (conditions.conditions && !conditions.conditions.every((x) => x(exp)))
continue;
// winner winner chicken dinner
return callback(exp);
}
// didn't find it, wait and loop
setTimeout(runThroughAll, 1);
};
runThroughAll();
});
}
async function ensureHookInstalled() {
// wait for the object to exist
while (!window.webpackJsonp)
await new Promise((resolve) => setTimeout(resolve, 1));
// check if the hook is already installed
if (!hookInstalled) {
// install the hook
let origPush = window.webpackJsonp.push;
window.webpackJsonp.push = (jsonp) => {
// if needed, make hook to steal the require object
if (!require) {
const i = Number(Object.keys(jsonp[1])[0]);
const origCall = jsonp[1][i];
jsonp[1][i] = (m, e, r) => {
require = r;
// TODO: DiscordInjections uses setImmediate, should we too?
while (queue.length)
queue.pop()(require);
return origCall(m, e, r);
};
}
// return the original
return origPush(jsonp);
};
hookInstalled = true;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment