-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpatch_final.js
More file actions
201 lines (176 loc) · 8.66 KB
/
Copy pathpatch_final.js
File metadata and controls
201 lines (176 loc) · 8.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
const fs = require('fs');
const path = require('path');
const os = require('os');
const asar = require('@electron/asar');
const { spawn, execSync } = require('child_process');
const HTK_DIR = path.join(os.homedir(), 'AppData', 'Local', 'Programs', 'HTTP Toolkit');
const RESOURCES_PATH = path.join(HTK_DIR, 'resources');
const ASAR_PATH = path.join(RESOURCES_PATH, 'app.asar');
const BACKUP_PATH = ASAR_PATH + '.bak';
const EXTRACT_PATH = path.join(RESOURCES_PATH, 'app.asar_extracted');
const EXE_PATH = path.join(HTK_DIR, 'HTTP Toolkit.exe');
const PAGE_INJECT_CODE = `(function(){
try{localStorage.setItem('last_jwt','patched')}catch(e){}
var SUB={status:'active',plan:'pro-annual',sku:'pro-annual',tierCode:'pro',interval:'annual',quantity:1,expiry:new Date('2099-12-31'),updateBillingDetailsUrl:'https://httptoolkit.com/',cancelSubscriptionUrl:'https://httptoolkit.com/',lastReceiptUrl:'https://httptoolkit.com/',canManageSubscription:true};
var UM={isPaidUser:true,isPastDueUser:false,userHasSubscription:true,isStatusUnexpired:true};
var SP={isLoggedIn:true,userEmail:'pro@local.user',mightBePaidUser:true,userSubscription:SUB};
var hU=new WeakSet(),hS=new WeakSet();
function pU(u){if(!u||typeof u!='object'||hU.has(u))return;hU.add(u);Object.keys(UM).forEach(function(m){try{u[m]=function(){return UM[m]}}catch(e){}});try{if(!u.subscription||typeof u.subscription!='object')Object.defineProperty(u,'subscription',{value:SUB,writable:true,configurable:true,enumerable:true})}catch(e){}}
function pS(s){if(!s||typeof s!='object'||hS.has(s))return;hS.add(s);Object.keys(SP).forEach(function(p){try{Object.defineProperty(s,p,{get:function(){return SP[p]},configurable:true,enumerable:true})}catch(e){}});if(s.user&&typeof s.user=='object')pU(s.user)}
var _dp=Object.defineProperty;Object.defineProperty=function(t,p,d){
if(p in SP&&d){_dp(t,p,{get:function(){return SP[p]},configurable:true,enumerable:'enumerable' in d?d.enumerable:true});return t}
if(p=='user'&&d){if(d.get){var og=d.get,os=d.set;_dp(t,p,{get:function(){var u=og.call(this);u&&pU(u);return u},set:os,configurable:d.configurable!==false,enumerable:'enumerable' in d?d.enumerable:true});return t}else pU(d.value)}
if(p=='subscription'&&d){try{var sd=d.get?d.get():d.value;if(sd&&typeof sd=='object')Object.keys(SUB).forEach(function(k){try{sd[k]=SUB[k]}catch(e){}})}catch(e){}}
return _dp.call(this,t,p,d)};
var _n=0,_iv=setInterval(function(){_n++;try{for(var k in window){try{var v=window[k];if(v&&typeof v=='object'){Object.keys(SP).some(function(p){return p in v})&&pS(v);if(v.user&&typeof v.user=='object'){Object.keys(SP).some(function(p){return p in v})?pS(v):pU(v.user)}}}catch(e){}}}catch(e){}if(_n>100)clearInterval(_iv)},500);
})();`;
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
async function killAllHtkProcesses() {
try { execSync('taskkill /F /IM "HTTP Toolkit.exe" /T 2>nul', { stdio: 'pipe' }); } catch (e) {}
try { execSync('taskkill /F /IM "HTTP Toolkit.exe" 2>nul', { stdio: 'pipe' }); } catch (e) {}
await sleep(3000);
}
function extractHashes(output) {
const m = output.match(/Integrity\s*check\s*failed[\s\S]*?\(\s*([0-9a-f]{64})\s*vs\s*([0-9a-f]{64})\s*\)/i);
if (!m) return null;
return { originalHash: m[1], newHash: m[2] };
}
function patchExe(originalHash, newHash) {
const binary = fs.readFileSync(EXE_PATH);
const origBuf = Buffer.from(originalHash, 'utf-8');
const newBuf = Buffer.from(newHash, 'utf-8');
let occurrences = 0;
let idx = binary.indexOf(origBuf);
while (idx !== -1) {
newBuf.copy(binary, idx);
occurrences++;
console.log(` Replaced at offset 0x${idx.toString(16)}`);
idx = binary.indexOf(origBuf, idx + origBuf.length);
}
if (occurrences === 0) {
throw new Error('Original hash not found in EXE!');
}
fs.writeFileSync(EXE_PATH, binary);
console.log(` Hash patched (${occurrences} occurrence${occurrences > 1 ? 's' : ''})`);
}
(async () => {
// --- Step 1: Backup ---
if (!fs.existsSync(BACKUP_PATH)) {
console.log('[1] Creating backup...');
fs.copyFileSync(ASAR_PATH, BACKUP_PATH);
console.log(' app.asar.bak created');
} else {
console.log('[1] Backup exists, skipping.');
}
// --- Step 2: Extract and patch preload.cjs ---
console.log('[2] Extracting app.asar...');
if (fs.existsSync(EXTRACT_PATH)) {
fs.rmSync(EXTRACT_PATH, { recursive: true, force: true });
}
asar.extractAll(ASAR_PATH, EXTRACT_PATH);
console.log(' Extracted.');
const preloadPath = path.join(EXTRACT_PATH, 'build', 'preload.cjs');
if (!fs.existsSync(preloadPath)) {
console.error('ERROR: preload.cjs not found!');
process.exit(1);
}
let preloadContent = fs.readFileSync(preloadPath, 'utf8');
{
const escapedCode = JSON.stringify(PAGE_INJECT_CODE);
const patchBlock = `
(function loadHtkPatch() {
try {
var _e = require('electron');
if (_e.webFrame && typeof _e.webFrame.executeJavaScript === 'function') {
_e.webFrame.executeJavaScript(${escapedCode}).then(function() {
console.log('[HTK] OK');
}).catch(function(e) {
console.error('[HTK] err', e);
});
}
} catch(e) {
console.error('[HTK] fail', e);
}
})();
// HTK_PATCH_INJECTED
`;
const insertIdx = preloadContent.indexOf('const { contextBridge');
if (insertIdx === -1) {
console.error('ERROR: Could not find insertion point in preload.cjs');
process.exit(1);
}
preloadContent = preloadContent.slice(0, insertIdx) + patchBlock + '\n' + preloadContent.slice(insertIdx);
fs.writeFileSync(preloadPath, preloadContent, 'utf8');
console.log(' preload.cjs patched.');
}
// --- Step 3: Repack asar ---
console.log('[3] Repacking app.asar...');
await asar.createPackage(EXTRACT_PATH, ASAR_PATH);
fs.rmSync(EXTRACT_PATH, { recursive: true, force: true });
console.log(' Repacked.');
// --- Step 4: Launch app to capture integrity hashes ---
console.log('[4] Launching HTTP Toolkit to capture integrity hashes...');
console.log(' (App crash is expected - that shows us the hashes)');
let hashes;
try {
hashes = await new Promise((resolve, reject) => {
let output = '';
let done = false;
const child = spawn(EXE_PATH, [], {
stdio: ['ignore', 'pipe', 'pipe'],
windowsHide: true,
});
const timeout = setTimeout(() => {
if (!done) { done = true; child.kill(); reject(new Error('Timed out waiting for hash output')); }
}, 30000);
function handleData(data) {
const str = data.toString();
output += str;
const truncated = str.trim().slice(0, 300);
if (truncated) console.log(' [output]', truncated);
const h = extractHashes(output);
if (h && !done) {
done = true;
clearTimeout(timeout);
child.kill();
// Aggressively kill process tree, then wait, then resolve
killAllHtkProcesses().then(() => resolve(h));
}
}
child.stdout.on('data', handleData);
child.stderr.on('data', handleData);
child.on('error', (err) => { if (!done) { done = true; clearTimeout(timeout); reject(err); } });
child.on('exit', (code) => {
if (!done) {
clearTimeout(timeout);
const h = extractHashes(output);
if (h) {
killAllHtkProcesses().then(() => resolve(h));
}
else reject(new Error('No hash found.\nOutput:\n' + output.slice(0, 2000)));
}
});
});
} catch (e) {
console.error('\nERROR:', e.message);
console.log('Restoring asar backup...');
fs.copyFileSync(BACKUP_PATH, ASAR_PATH);
process.exit(1);
}
console.log(`\n[5] Hashes captured:`);
console.log(` Original: ${hashes.originalHash}`);
console.log(` New: ${hashes.newHash}`);
// --- Step 5: Wait a bit more and patch EXE ---
console.log('\n[6] Patching EXE hash...');
await sleep(2000);
try {
patchExe(hashes.originalHash, hashes.newHash);
} catch (e) {
console.error('\nERROR:', e.message);
console.log('Restoring asar backup...');
fs.copyFileSync(BACKUP_PATH, ASAR_PATH);
process.exit(1);
}
console.log('\n[7] DONE! HTTP Toolkit is patched.');
console.log(' Launch it to test.');
})();