mirror of
https://github.com/xenos1337/httptoolkit-patcher.git
synced 2026-07-15 16:20:02 +02:00
refactor(patcher): use local inject.js and simplify injection mechanism
This commit is contained in:
@@ -51,19 +51,6 @@ function prompt(question) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to fetch content from URL
|
|
||||||
function fetchUrl(url) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
https
|
|
||||||
.get(url, res => {
|
|
||||||
let data = "";
|
|
||||||
res.on("data", chunk => (data += chunk));
|
|
||||||
res.on("end", () => resolve(data));
|
|
||||||
})
|
|
||||||
.on("error", reject);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to remove directory recursively
|
// Helper function to remove directory recursively
|
||||||
function rm(dirPath) {
|
function rm(dirPath) {
|
||||||
if (!fs.existsSync(dirPath)) return;
|
if (!fs.existsSync(dirPath)) return;
|
||||||
@@ -78,7 +65,7 @@ function rm(dirPath) {
|
|||||||
|
|
||||||
// Find HTTP Toolkit installation path
|
// Find HTTP Toolkit installation path
|
||||||
async function findAppPath() {
|
async function findAppPath() {
|
||||||
const possiblePaths = isWin ? [path.join("C:", "Program Files", "HTTP Toolkit", "resources"), path.join("C:", "Program Files (x86)", "HTTP Toolkit", "resources")] : isMac ? ["/Applications/HTTP Toolkit.app/Contents/Resources"] : ["/opt/HTTP Toolkit/resources", "/opt/httptoolkit/resources"];
|
const possiblePaths = isWin ? [path.join("C:", "Program Files", "HTTP Toolkit", "resources"), path.join("C:", "Program Files (x86)", "HTTP Toolkit", "resources"), path.join(process.env.LOCALAPPDATA || path.join(process.env.USERPROFILE || "", "AppData", "Local"), "Programs", "HTTP Toolkit", "resources")] : isMac ? ["/Applications/HTTP Toolkit.app/Contents/Resources"] : ["/opt/HTTP Toolkit/resources", "/opt/httptoolkit/resources"];
|
||||||
|
|
||||||
for (const p of possiblePaths) {
|
for (const p of possiblePaths) {
|
||||||
if (fs.existsSync(path.join(p, "app.asar"))) {
|
if (fs.existsSync(path.join(p, "app.asar"))) {
|
||||||
@@ -346,37 +333,71 @@ async function patchApp() {
|
|||||||
asar.extractAll(asarPath, extractPath);
|
asar.extractAll(asarPath, extractPath);
|
||||||
console.log(chalk.greenBright`[+] Extracted to ${extractPath}`);
|
console.log(chalk.greenBright`[+] Extracted to ${extractPath}`);
|
||||||
|
|
||||||
// Step 6: Check if preload.cjs or preload.js exists
|
// Step 6: Check if preload.cjs exists
|
||||||
let preloadPath = path.join(extractPath, "build", "preload.cjs");
|
const preloadPath = path.join(extractPath, "build", "preload.cjs");
|
||||||
if (!fs.existsSync(preloadPath)) {
|
if (!fs.existsSync(preloadPath)) {
|
||||||
console.log(chalk.yellowBright`[!] preload.cjs not found, checking for preload.js...`);
|
console.error(chalk.redBright`[-] preload.cjs not found in ${path.join(extractPath, "build")}`);
|
||||||
preloadPath = path.join(extractPath, "build", "preload.js");
|
console.error(chalk.yellowBright`[!] Please download the latest version of HTTP Toolkit from https://httptoolkit.com/`);
|
||||||
if (!fs.existsSync(preloadPath)) {
|
|
||||||
console.error(chalk.redBright`[-] Neither preload.cjs nor preload.js found in ${path.join(extractPath, "build")}`);
|
|
||||||
rm(extractPath);
|
rm(extractPath);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
console.log(chalk.greenBright`[+] Found preload.js`);
|
|
||||||
} else {
|
|
||||||
console.log(chalk.greenBright`[+] Found preload.cjs`);
|
console.log(chalk.greenBright`[+] Found preload.cjs`);
|
||||||
}
|
|
||||||
|
|
||||||
// Step 7: Fetch inject.js from GitHub
|
// Step 7: Read inject.js from local file (same directory as this script)
|
||||||
console.log(chalk.yellowBright`[+] Fetching inject code from GitHub...`);
|
console.log(chalk.yellowBright`[+] Reading inject code from local file...`);
|
||||||
const injectCode = await fetchUrl("https://raw.githubusercontent.com/xenos1337/httptoolkit-patcher/refs/heads/master/inject.js");
|
const injectJsPath = path.join(path.dirname(__filename), "inject.js");
|
||||||
if (!injectCode || !injectCode.includes("injectPageContextHooks")) {
|
if (!fs.existsSync(injectJsPath)) {
|
||||||
console.error(chalk.redBright`[-] Failed to fetch inject.js from GitHub`);
|
console.error(chalk.redBright`[-] inject.js not found at ${injectJsPath}`);
|
||||||
rm(extractPath);
|
rm(extractPath);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
console.log(chalk.greenBright`[+] Inject code fetched successfully`);
|
const injectCode = fs.readFileSync(injectJsPath, "utf-8");
|
||||||
|
if (!injectCode || !injectCode.includes("PAGE-INJECT")) {
|
||||||
|
console.error(chalk.redBright`[-] Invalid inject.js file`);
|
||||||
|
rm(extractPath);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
console.log(chalk.greenBright`[+] Inject code loaded successfully`);
|
||||||
|
|
||||||
// Step 8: Read preload file and check if already patched
|
const preloadPatchCode = `
|
||||||
|
(function loadInjectScript() {
|
||||||
|
const injectCode = ${JSON.stringify(injectCode)};
|
||||||
|
|
||||||
|
function injectViaWebFrame() {
|
||||||
|
try {
|
||||||
|
const { webFrame } = electron_1;
|
||||||
|
if (webFrame && webFrame.executeJavaScript) {
|
||||||
|
webFrame.executeJavaScript(injectCode).then(() => console.log("[PRELOAD] Injected via webFrame.executeJavaScript")).catch(err => console.error("[PRELOAD] webFrame injection failed:", err));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("[PRELOAD] webFrame not available:", e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!injectViaWebFrame()) {
|
||||||
|
const tryInject = () => {
|
||||||
|
if (!injectViaWebFrame()) {
|
||||||
|
console.error("[PRELOAD] All injection methods failed");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
||||||
|
tryInject();
|
||||||
|
} else {
|
||||||
|
document.addEventListener('DOMContentLoaded', tryInject, { once: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Step 8: Read files and check if already patched
|
||||||
let preloadContent = fs.readFileSync(preloadPath, "utf-8");
|
let preloadContent = fs.readFileSync(preloadPath, "utf-8");
|
||||||
const isPatched = preloadContent.includes("injectPageContextHooks");
|
const isPreloadPatched = preloadContent.includes("loadInjectScript");
|
||||||
|
|
||||||
if (isPatched) {
|
if (isPreloadPatched) {
|
||||||
console.log(chalk.yellowBright`[!] File already patched`);
|
console.log(chalk.yellowBright`[!] Files already patched`);
|
||||||
const answer = await prompt("Do you want to repatch? (y/n): ");
|
const answer = await prompt("Do you want to repatch? (y/n): ");
|
||||||
|
|
||||||
if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
|
if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
|
||||||
@@ -385,37 +406,40 @@ async function patchApp() {
|
|||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace the existing injectPageContextHooks function
|
// Remove existing patches
|
||||||
console.log(chalk.yellowBright`[+] Replacing existing patch...`);
|
console.log(chalk.yellowBright`[+] Replacing existing patches...`);
|
||||||
const functionRegex = /\(function injectPageContextHooks\(\) \{[\s\S]*?\}\)\(\);/;
|
|
||||||
preloadContent = preloadContent.replace(functionRegex, injectCode);
|
|
||||||
} else {
|
|
||||||
// Find line with electron_1 and insert inject code below it
|
|
||||||
console.log(chalk.yellowBright`[+] Applying patch...`);
|
|
||||||
const lines = preloadContent.split("\n");
|
|
||||||
let insertIndex = -1;
|
|
||||||
|
|
||||||
for (let i = 0; i < lines.length; i++) {
|
// Remove preload patch
|
||||||
if (lines[i].includes("electron_1")) {
|
const preloadPatchRegex = /\n?\(function loadInjectScript\(\) \{[\s\S]*?\}\)\(\);/;
|
||||||
insertIndex = i + 1;
|
preloadContent = preloadContent.replace(preloadPatchRegex, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 9: Patch preload file - find line with electron_1 and insert patch code below it
|
||||||
|
console.log(chalk.yellowBright`[+] Applying preload patch...`);
|
||||||
|
const preloadLines = preloadContent.split("\n");
|
||||||
|
let preloadInsertIndex = -1;
|
||||||
|
|
||||||
|
for (let i = 0; i < preloadLines.length; i++) {
|
||||||
|
if (preloadLines[i].includes("electron_1")) {
|
||||||
|
preloadInsertIndex = i + 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (insertIndex === -1) {
|
if (preloadInsertIndex === -1) {
|
||||||
console.error(chalk.redBright`[-] Could not find insertion point (electron_1) in ${path.basename(preloadPath)}`);
|
console.error(chalk.redBright`[-] Could not find insertion point (electron_1) in ${path.basename(preloadPath)}`);
|
||||||
rm(extractPath);
|
rm(extractPath);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
lines.splice(insertIndex, 0, injectCode);
|
preloadLines.splice(preloadInsertIndex, 0, preloadPatchCode);
|
||||||
preloadContent = lines.join("\n");
|
preloadContent = preloadLines.join("\n");
|
||||||
}
|
|
||||||
|
|
||||||
// Step 9: Write patched preload file
|
// Write patched preload file
|
||||||
fs.writeFileSync(preloadPath, preloadContent, "utf-8");
|
fs.writeFileSync(preloadPath, preloadContent, "utf-8");
|
||||||
console.log(chalk.greenBright`[+] ${path.basename(preloadPath)} patched successfully`);
|
console.log(chalk.greenBright`[+] ${path.basename(preloadPath)} patched successfully`);
|
||||||
|
|
||||||
|
|
||||||
// Step 10: Repackage app.asar
|
// Step 10: Repackage app.asar
|
||||||
console.log(chalk.yellowBright`[+] Repackaging app.asar...`);
|
console.log(chalk.yellowBright`[+] Repackaging app.asar...`);
|
||||||
await asar.createPackage(extractPath, asarPath);
|
await asar.createPackage(extractPath, asarPath);
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
(function injectPageContextHooks() {
|
|
||||||
const injectionCode = `
|
|
||||||
(function () {
|
(function () {
|
||||||
console.log("[PAGE-INJECT] Installing hooks in page context");
|
console.log("[PAGE-INJECT] Installing hooks in page context");
|
||||||
|
|
||||||
@@ -10,19 +8,21 @@
|
|||||||
userEmail: "hi@httptoolkit.com",
|
userEmail: "hi@httptoolkit.com",
|
||||||
mightBePaidUser: true,
|
mightBePaidUser: true,
|
||||||
isPastDueUser: false,
|
isPastDueUser: false,
|
||||||
|
isStatusUnexpired: true,
|
||||||
userSubscription: {
|
userSubscription: {
|
||||||
|
state: "fulfilled",
|
||||||
status: "active",
|
status: "active",
|
||||||
plan: "pro",
|
plan: "pro",
|
||||||
sku: "sku",
|
sku: "sku",
|
||||||
tierCode: "pro",
|
tierCode: "pro",
|
||||||
interval: "monthly",
|
interval: "monthly",
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
expiry: new Date("2999-01-01"),
|
expiry: new Date(new Date().setFullYear(new Date().getFullYear() + 10)),
|
||||||
updateBillingDetailsUrl: "https://httptoolkit.com/",
|
updateBillingDetailsUrl: "https://httptoolkit.com/",
|
||||||
cancelSubscriptionUrl: "https://httptoolkit.com/",
|
cancelSubscriptionUrl: "https://httptoolkit.com/",
|
||||||
lastReceiptUrl: "https://httptoolkit.com/",
|
lastReceiptUrl: "https://httptoolkit.com/",
|
||||||
canManageSubscription: true
|
canManageSubscription: true,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const hookedObjects = new WeakSet();
|
const hookedObjects = new WeakSet();
|
||||||
@@ -74,12 +74,7 @@
|
|||||||
// Periodically scan and patch existing objects
|
// Periodically scan and patch existing objects
|
||||||
function scanAndPatch() {
|
function scanAndPatch() {
|
||||||
// Search through window and common store locations
|
// Search through window and common store locations
|
||||||
const searchPaths = [
|
const searchPaths = [window, window.accountStore, window.stores && window.stores.accountStore, window.appState && window.appState.accountStore];
|
||||||
window,
|
|
||||||
window.accountStore,
|
|
||||||
window.stores && window.stores.accountStore,
|
|
||||||
window.appState && window.appState.accountStore,
|
|
||||||
];
|
|
||||||
|
|
||||||
searchPaths.forEach((obj, idx) => {
|
searchPaths.forEach((obj, idx) => {
|
||||||
if (!obj || hookedObjects.has(obj)) return;
|
if (!obj || hookedObjects.has(obj)) return;
|
||||||
@@ -101,7 +96,7 @@
|
|||||||
},
|
},
|
||||||
set: desc.set,
|
set: desc.set,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
enumerable: desc.enumerable
|
enumerable: desc.enumerable,
|
||||||
});
|
});
|
||||||
} else if (desc.writable) {
|
} else if (desc.writable) {
|
||||||
obj[prop] = propertyHooks[prop];
|
obj[prop] = propertyHooks[prop];
|
||||||
@@ -124,7 +119,7 @@
|
|||||||
for (let key in window) {
|
for (let key in window) {
|
||||||
try {
|
try {
|
||||||
const obj = window[key];
|
const obj = window[key];
|
||||||
if (obj && typeof obj === 'object' && 'accountStore' in obj) {
|
if (obj && typeof obj === "object" && "accountStore" in obj) {
|
||||||
console.log("[PAGE-INJECT] Found accountStore in window." + key);
|
console.log("[PAGE-INJECT] Found accountStore in window." + key);
|
||||||
const store = obj.accountStore;
|
const store = obj.accountStore;
|
||||||
if (store && !hookedObjects.has(store)) {
|
if (store && !hookedObjects.has(store)) {
|
||||||
@@ -141,7 +136,7 @@
|
|||||||
},
|
},
|
||||||
set: desc.set,
|
set: desc.set,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
enumerable: desc.enumerable
|
enumerable: desc.enumerable,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
@@ -163,7 +158,7 @@
|
|||||||
scanCount++;
|
scanCount++;
|
||||||
scanAndPatch();
|
scanAndPatch();
|
||||||
|
|
||||||
if (scanCount >= 10) {
|
if (scanCount >= 50) {
|
||||||
clearInterval(scanInterval);
|
clearInterval(scanInterval);
|
||||||
console.log("[PAGE-INJECT] Stopped periodic scanning after 10 attempts");
|
console.log("[PAGE-INJECT] Stopped periodic scanning after 10 attempts");
|
||||||
}
|
}
|
||||||
@@ -171,36 +166,3 @@
|
|||||||
|
|
||||||
console.log("[PAGE-INJECT] Hooks installed successfully");
|
console.log("[PAGE-INJECT] Hooks installed successfully");
|
||||||
})();
|
})();
|
||||||
`;
|
|
||||||
|
|
||||||
function injectScript() {
|
|
||||||
try {
|
|
||||||
const script = document.createElement('script');
|
|
||||||
script.textContent = injectionCode;
|
|
||||||
(document.head || document.documentElement).appendChild(script);
|
|
||||||
script.remove(); // Clean up the script tag
|
|
||||||
console.log("[PRELOAD] Injected page context hooks");
|
|
||||||
} catch (e) {
|
|
||||||
console.error("[PRELOAD] Failed to inject hooks:", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to inject immediately if DOM is ready, otherwise wait
|
|
||||||
if (document.documentElement) {
|
|
||||||
injectScript();
|
|
||||||
} else {
|
|
||||||
// Wait for document to be created
|
|
||||||
const observer = new MutationObserver(() => {
|
|
||||||
if (document.documentElement) {
|
|
||||||
observer.disconnect();
|
|
||||||
injectScript();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
observer.observe(document, { childList: true, subtree: true });
|
|
||||||
|
|
||||||
// Also try on DOMContentLoaded as backup
|
|
||||||
if (document.addEventListener) {
|
|
||||||
document.addEventListener('DOMContentLoaded', injectScript, { once: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
Reference in New Issue
Block a user