Skip to content

Commit

Permalink
rewrite handleExfiltrations (neptune being broken is my fault lmao)
Browse files Browse the repository at this point in the history
  • Loading branch information
twnlink committed Sep 11, 2023
1 parent cda0283 commit 65c850e
Showing 1 changed file with 134 additions and 124 deletions.
258 changes: 134 additions & 124 deletions src/handleExfiltrations.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,146 +32,156 @@ Object.defineProperty(window, "webpackChunk_tidal_web", {
get: () => webpackObject,
set(val) {
if (webpackObject) return true;

loadStyles();

/*
We replace webpackObject with a proxy that waits for .push to be gotten.
This could be replaced with an Object.defineProperty call that assigns push to a getter,
but I didn't feel like it. Fuck you.
*/
webpackObject = new Proxy(val, {
get(obj, prop) {
if (prop == "push" && !patchedPush) {
patchedPush = true;

// This patcher call patches .push.
patcher.before(prop, obj, (args) => {
// Push can be called with multiple chunks, so instead of only doing the first argument we handle all of them.
for (let arg of args) {
if (!arg[1]) continue;

// Each chunk contains an object containing a list of modules, so we iterate over each module id.
for (let id in arg[1]) {
// Modules get passed webpackRequire as their third argument, so we patch over each module to essentially patch webpackRequire.
patcher.before(id, arg[1], (args) => {
const wpRequire = args[2];

if (!windowObject.hasOwnProperty("modules"))
Object.defineProperty(windowObject, "modules", {
get: () => Object.values(getModulesFromWpRequire(wpRequire)),
});

// See above: patching webpackRequire.
patcher.after(2, args, (_, resp) => {
try {
/*
This entire block exfiltrates modules we need / patches them.
The only modules we need export an object, so we return if they aren't an object.
*/
if (typeof resp != "object") return;

try {
if (!exfiltratedStore) {
// We sift through the modules to find a module that exports a function that gets the global Redux store.
const [key] = Object.entries(resp).find(([, e]) =>
e?.toString?.().includes?.('Error("No global store set"'),
);

if (key) exfiltratedStore = true;

/*
Because we patch so early, the global Redux store is not defined yet,
so we wait for the getStore function to be called.
*/
patcher.after(key, resp, (_, resp) => {
if (!typeof resp == "object" && windowObject.store) return;

windowObject.store = resp;
});
}
} catch {}

try {
if (patchedPrepareAction) return;

let found = Object.entries(resp).find(([, possiblyPrepareAction]) =>
possiblyPrepareAction
?.toString?.()
?.includes?.(`new Error("prepareAction did not return an object");`),
);

if (!found) return;

patchedPrepareAction = true;
patcher.after(found[0], resp, ([type], resp) => {
if (!interceptors[type]) interceptors[type] = [];

const [parent, child] = type
.split("/")
.map((n) =>
n.toUpperCase() == n
? n.toLowerCase().replace(/_([a-z])/g, (_, i) => i.toUpperCase())
: n,
webpackObject = val;

const originalPush = webpackObject.push;
let newPush;
Object.defineProperty(webpackObject, "push", {
set(v) {
if (!newPush) newPush = v;
},
get: () =>
newPush
? function () {
try {
for (const chunk of arguments) {
const [, modules] = chunk;
if (!modules) continue;

for (const moduleId in modules) {
const originalModule = modules[moduleId];

modules[moduleId] = function () {
const wpRequire = arguments[2];

if (!windowObject.hasOwnProperty("modules"))
Object.defineProperty(windowObject, "modules", {
get: () => Object.values(getModulesFromWpRequire(wpRequire)),
});

// thinking i'll add a try { } catch { } finally { } to this.
arguments[2] = new Proxy(wpRequire, {
apply(target, thisArg, args) {
const originalResponse = target.apply(thisArg, args);
if (typeof originalResponse != "object") return originalResponse;

// neptune.store exfiltration code
try {
if (!exfiltratedStore) {
const [key] = Object.entries(originalResponse).find(([, e]) =>
e?.toString?.().includes?.('Error("No global store set"'),
);

if (key) exfiltratedStore = true;

const unpatch = patcher.after(key, originalResponse, (_, resp) => {
if (!typeof resp == "object" && windowObject.store) return;

windowObject.store = resp;
unpatch();
});
}
} catch {}

// neptune.intercept setup code
try {
if (patchedPrepareAction) return originalResponse;

let found = Object.entries(originalResponse).find(
([, possiblyPrepareAction]) =>
possiblyPrepareAction
?.toString?.()
?.includes?.(
`new Error("prepareAction did not return an object");`,
),
);

const builtAction = (...args) => {
const act = resp(...args);
if (!found) return originalResponse;

if (!(act.__proto__.toString() == "[object Promise]"))
return windowObject.store.dispatch(act);
patchedPrepareAction = true;

return new Promise(async (res, rej) => {
try {
res(windowObject.store.dispatch(await act));
} catch (e) {
rej(e);
}
});
};
patcher.after(found[0], originalResponse, ([type], resp) => {
if (!interceptors[type]) interceptors[type] = [];

if (child) {
if (!actions[parent]) actions[parent] = {};
const [parent, child] = type
.split("/")
.map((n) =>
n.toUpperCase() == n
? n
.toLowerCase()
.replace(/_([a-z])/g, (_, i) => i.toUpperCase())
: n,
);

actions[parent][child] = builtAction;
} else {
actions[parent] = builtAction;
}
const builtAction = (...args) => {
const act = resp(...args);

return new Proxy(resp, {
apply(orig, ctxt, [payload]) {
let shouldDispatch = true;
if (!(act.__proto__.toString() == "[object Promise]"))
return windowObject.store.dispatch(act);

const interceptorData = [payload, type];
return new Promise(async (res, rej) => {
try {
res(windowObject.store.dispatch(await act));
} catch (e) {
rej(e);
}
});
};

for (let interceptor of interceptors[type]) {
try {
const resp = interceptor(interceptorData);
if (child) {
if (!actions[parent]) actions[parent] = {};

if (resp === true) shouldDispatch = false;
} catch (e) {
console.error("Failed to run interceptor!\n", e);
}
actions[parent][child] = builtAction;
} else {
actions[parent] = builtAction;
}

return shouldDispatch ? orig.apply(ctxt, interceptorData[0]) : { type: "NOOP" };
},
});
});
} catch {}
} catch (e) {
console.error(e);
}
});
});
return new Proxy(resp, {
apply(orig, ctxt, [payload]) {
try {
let shouldDispatch = true;

const interceptorData = [payload, type];

for (let interceptor of interceptors[type]) {
try {
const resp = interceptor(interceptorData);

if (resp === true) shouldDispatch = false;
} catch (e) {
console.error("Failed to run interceptor!\n", e);
}
}

return shouldDispatch
? orig.apply(ctxt, [interceptorData[0]])
: { type: "NOOP" };
} catch (e) {
console.log(e);
}
},
});
});
} catch {}

return originalResponse;
},
});

return originalModule.apply(this, arguments);
};
}
}
} catch (e) {
console.error("[neptune] failed to hook properly", e);
}
}
});
}

return obj[prop];
},
return newPush.apply(this, arguments);
}
: originalPush,
});

return true;
},
});

0 comments on commit 65c850e

Please sign in to comment.