/**
 * @file gunra_decrypter.c
 * @brief Gunra 랜섬웨어(Conti 기반) 취약점 기반 복호화 도구(22c47ec98718ab243f2f474170366a1780368e084d1bf6adcd60450a9289e4be)
 *
 * @details
 * 이 도구는 Gunra 랜섬웨어의 키 생성 취약점을 이용하여 암호화된 파일을 복구합니다.
 * '알려진 평문 공격(Known-Plaintext Attack)'을 사용하여 256가지 경우의 수만 존재하는
 * 암호화 키 바이트를 식별한 후, 해당 키로 디렉터리 내 모든 파일을 일괄 복호화합니다.
 *
 * @version 1.1
 * @date 2025-08-26
 *
 * @note
 * 이 코드는 교육 및 연구 목적으로만 사용해야 합니다.
 * 컴파일: gcc -o gunra_decrypter gunra_decrypter.c
 * 사용법: ./gunra_decrypter <감염된_디렉터리_경로>
 */

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdint.h>
 #include <dirent.h>
 
 // --- 상수 정의 ---
 #define KEY_SIZE 32
 #define NONCE_SIZE 12
 #define RSA_METADATA_SIZE 512
 #define KEYSTREAM_BLOCK_SIZE 64
 
 // =================================================================================
 // ChaCha20 스트림 암호 구현부
 // ※ 이 함수들은 Gunra 랜섬웨어 바이너리에서 직접 추출하여 정확성을 보장함
 // =================================================================================
 
 #define ROTL32(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
 
 static uint32_t U8TO32_LE(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 U32TO8_LE(uint8_t *p, uint32_t v) {
     p[0] = (uint8_t)v; p[1] = (uint8_t)(v >> 8); p[2] = (uint8_t)(v >> 16); p[3] = (uint8_t)(v >> 24);
 }
 
 void chacha20_quarterround(uint32_t *s, int a, int b, int c, int d) {
     s[a] += s[b]; s[d] = ROTL32(s[d] ^ s[a], 16);
     s[c] += s[d]; s[b] = ROTL32(s[b] ^ s[c], 12);
     s[a] += s[b]; s[d] = ROTL32(s[d] ^ s[a], 8);
     s[c] += s[d]; s[b] = ROTL32(s[b] ^ s[c], 7);
 }
 
 void chacha20_init_state(uint32_t *s, const uint8_t *k, uint32_t ctr, const uint8_t *n) {
     s[0] = 0x61707865; s[1] = 0x3320646e; s[2] = 0x79622d32; s[3] = 0x6b206574; // "expand 32-byte k"
     for (int i = 0; i < 8; ++i) s[i + 4] = U8TO32_LE(k + 4 * i);
     s[12] = ctr;
     for (int i = 0; i < 3; ++i) s[i + 13] = U8TO32_LE(n + 4 * i);
 }
 
 void chacha20_block(const uint32_t *init_s, uint8_t *ks_block) {
     uint32_t state[16];
     memcpy(state, init_s, sizeof(state));
     for (int i = 0; i < 10; ++i) { // 10 double rounds
         chacha20_quarterround(state, 0, 4, 8, 12); chacha20_quarterround(state, 1, 5, 9, 13);
         chacha20_quarterround(state, 2, 6, 10, 14); chacha20_quarterround(state, 3, 7, 11, 15);
         chacha20_quarterround(state, 0, 5, 10, 15); chacha20_quarterround(state, 1, 6, 11, 12);
         chacha20_quarterround(state, 2, 7, 8, 13); chacha20_quarterround(state, 3, 4, 9, 14);
     }
     for (int i = 0; i < 16; ++i) state[i] += init_s[i];
     for (int i = 0; i < 16; ++i) U32TO8_LE(ks_block + 4 * i, state[i]);
 }
 
 void chacha20_xor(const uint8_t *k, uint32_t ctr, const uint8_t *n, const uint8_t *in, uint8_t *out, size_t len) {
     uint32_t state[16];
     uint8_t ks_block[KEYSTREAM_BLOCK_SIZE];
     chacha20_init_state(state, k, ctr, n);
 
     for (size_t j = 0; j < len; j += KEYSTREAM_BLOCK_SIZE) {
         chacha20_block(state, ks_block);
         state[12]++; // Increment counter for the next block
         size_t block_len = (len - j < KEYSTREAM_BLOCK_SIZE) ? (len - j) : KEYSTREAM_BLOCK_SIZE;
         for (size_t k = 0; k < block_len; ++k) {
             out[j + k] = in[j + k] ^ ks_block[k];
         }
     }
 }
 
 // =================================================================================
 // 복호화 핵심 로직
 // =================================================================================
 
 /**
  * @brief 단일 파일을 복호화하고 원본을 삭제합니다.
  * @param file_path 복호화할 파일의 전체 경로
  * @param key_byte 식별된 암호화 키 바이트
  * @return 복호화 성공 시 0, 실패 시 -1
  */
 int decrypt_single_file(const char* file_path, int key_byte) {
     FILE *infile = fopen(file_path, "rb");
     if (!infile) {
         fprintf(stderr, " [!] ERROR: Failed to open %s\n", file_path);
         return -1;
     }
 
     fseek(infile, 0, SEEK_END);
     long long total_size = ftell(infile);
 
     if (total_size <= RSA_METADATA_SIZE) {
         fprintf(stderr, " [!] WARNING: File is too small, skipping.\n");
         fclose(infile);
         return -1;
     }
 
     long long data_size = total_size - RSA_METADATA_SIZE;
     uint8_t *encrypted_data = malloc(data_size);
     if (!encrypted_data) {
         fprintf(stderr, " [!] ERROR: Memory allocation failed.\n");
         fclose(infile);
         return -1;
     }
 
     fseek(infile, 0, SEEK_SET);
     if (fread(encrypted_data, 1, data_size, infile) != data_size) {
         fprintf(stderr, " [!] WARNING: Failed to read full file data, skipping.\n");
         free(encrypted_data);
         fclose(infile);
         return -1;
     }
     fclose(infile);
 
     // 식별된 바이트로 키와 Nonce 생성
     uint8_t key[KEY_SIZE], nonce[NONCE_SIZE];
     memset(key, key_byte, KEY_SIZE);
     memset(nonce, key_byte, NONCE_SIZE);
 
     uint8_t *decrypted_data = malloc(data_size);
     if (!decrypted_data) {
         fprintf(stderr, " [!] ERROR: Memory allocation failed.\n");
         free(encrypted_data);
         return -1;
     }
 
     // ChaCha20 복호화 수행 (암호화와 동일한 XOR 연산)
     chacha20_xor(key, 1, nonce, encrypted_data, decrypted_data, data_size);
 
     // 원본 파일명에서 .ENCRT 확장자 제거
     char output_path[1024];
     strncpy(output_path, file_path, sizeof(output_path) - 1);
     output_path[sizeof(output_path) - 1] = '\0';
     char *ext = strstr(output_path, ".ENCRT");
     if (ext) {
         *ext = '\0';
     }
 
     FILE *outfile = fopen(output_path, "wb");
     if (!outfile) {
         fprintf(stderr, " [!] ERROR: Failed to create decrypted file %s\n", output_path);
         free(encrypted_data);
         free(decrypted_data);
         return -1;
     }
 
     fwrite(decrypted_data, 1, data_size, outfile);
     fclose(outfile);
     printf(" -> Decrypted to %s", output_path);
 
     if (remove(file_path) == 0) {
         printf(" (Original deleted)\n");
     } else {
         fprintf(stderr, " (Failed to delete original)\n");
     }
 
     free(encrypted_data);
     free(decrypted_data);
     return 0;
 }
 
 /**
  * @brief PDF 파일의 헤더를 이용해 암호화 키 바이트를 찾습니다.
  * @param dir_path 감염된 파일이 있는 디렉터리 경로
  * @return 성공 시 0~255 사이의 키 바이트, 실패 시 -1
  */
 int find_key_byte_with_pdf_header(const char* dir_path) {
     const uint8_t pdf_header[] = {0x25, 0x50, 0x44, 0x46}; // "%PDF"
     const size_t header_size = sizeof(pdf_header);
 
     DIR *d = opendir(dir_path);
     if (!d) {
         perror(" [!] ERROR: Could not open directory");
         return -1;
     }
 
     struct dirent *dir;
     while ((dir = readdir(d)) != NULL) {
         if (strstr(dir->d_name, ".pdf.ENCRT") == NULL) {
             continue;
         }
 
         char full_path[1024];
         snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, dir->d_name);
         printf(" [+] Found PDF probe file: %s\n", full_path);
 
         FILE *infile = fopen(full_path, "rb");
         if (!infile) continue;
 
         fseek(infile, 0, SEEK_END);
         long long file_size = ftell(infile);
         if (file_size <= RSA_METADATA_SIZE + header_size) {
             fclose(infile);
             continue;
         }
 
         uint8_t encrypted_header[header_size];
         fseek(infile, 0, SEEK_SET);
         if (fread(encrypted_header, 1, header_size, infile) != header_size) {
             fclose(infile);
             continue;
         }
         fclose(infile);
 
         printf(" [*] Starting brute-force attack with 256 key candidates...\n");
         uint8_t key[KEY_SIZE], nonce[NONCE_SIZE];
         uint8_t decrypted_block[header_size];
 
         for (int b = 0; b < 256; b++) {
             memset(key, b, KEY_SIZE);
             memset(nonce, b, NONCE_SIZE);
 
             chacha20_xor(key, 1, nonce, encrypted_header, decrypted_block, header_size);
 
             if (memcmp(decrypted_block, pdf_header, header_size) == 0) {
                 printf(" [+] SUCCESS! Key byte found: 0x%02x\n", b);
                 closedir(d);
                 return b;
             }
         }
         printf(" [!] Attack with this file failed. Trying next PDF file...\n");
     }
     closedir(d);
     return -1;
 }
 
 /**
  * @brief 지정된 디렉터리의 모든 .ENCRT 파일을 복호화합니다.
  * @param dir_path 감염된 파일이 있는 디렉터리 경로
  * @param key_byte 식별된 암호화 키 바이트
  */
 void decrypt_directory(const char* dir_path, int key_byte) {
     DIR *d = opendir(dir_path);
     if (!d) {
         perror(" [!] ERROR: Could not open directory for decryption");
         return;
     }
 
     int decrypted_count = 0;
     struct dirent *dir;
     while ((dir = readdir(d)) != NULL) {
         if (strstr(dir->d_name, ".ENCRT") != NULL) {
             char full_path[1024];
             snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, dir->d_name);
             printf(" [*] Processing %s...", dir->d_name);
             if (decrypt_single_file(full_path, key_byte) == 0) {
                 decrypted_count++;
             }
         }
     }
     closedir(d);
     printf("\n [+] Decryption finished. %d files were processed.\n", decrypted_count);
 }
 
 // =================================================================================
 // 메인 함수
 // =================================================================================
 
 void print_banner() {
     printf("=====================================================\n");
     printf("  Gunra Ransomware (Conti-based) Decryption Tool\n");
     printf("  Proof-of-Concept for Known-Plaintext Attack\n");
     printf("=====================================================\n\n");
 }
 
 int main(int argc, char *argv[]) {
     if (argc != 2) {
         fprintf(stderr, "Usage: %s <directory_path_to_decrypt>\n", argv[0]);
         return 1;
     }
 
     print_banner();
     const char* dir_path = argv[1];
 
     printf("--- PHASE 1: FINDING ENCRYPTION KEY ---\n");
     int key_byte = find_key_byte_with_pdf_header(dir_path);
 
     if (key_byte != -1) {
         printf("\n--- PHASE 2: BATCH DECRYPTING ALL FILES ---\n");
         decrypt_directory(dir_path, key_byte);
         printf("\n[+] All tasks completed.\n");
     } else {
         printf("\n--- ATTACK FAILED ---\n");
         printf(" [!] Could not find the key byte. Ensure there is at least one\n");
         printf("     encrypted PDF file (.pdf.ENCRT) in the directory.\n");
     }
 
     return 0;
 }
 