/* Copyright (C) 2026 cartwright */ /* Only can decode printable text */ #include #include #include #include #define BUFFER_SIZE 65536 /* 64KB max file size for simplicity */ const char *B64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const char *B32_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; /* Strips whitespace, newlines, and padding from a string in-place */ size_t clean_string(char *str, size_t len) { size_t write_idx = 0; for(size_t i = 0; i < len; i++) { if (!isspace((unsigned char)str[i]) && str[i] != '=') { str[write_idx++] = str[i]; } } str[write_idx] = '\0'; return write_idx; } int is_hex_string(const char *str, size_t len) { if(len == 0 || len % 2 != 0) return 0; for(size_t i = 0; i < len; i++) { if(!isxdigit((unsigned char)str[i])) return 0; } return 1; } int is_b64_string(const char *str, size_t len) { if (len == 0 || len % 4 == 1) return 0; int has_upper = 0; int has_lower = 0; int has_b64_specials = 0; // '+' or '/' for(size_t i = 0; i < len; i++) { if (!strchr(B64_CHARS, str[i])) return 0; if (isupper((unsigned char)str[i])) has_upper = 1; if (islower((unsigned char)str[i])) has_lower = 1; if (str[i] == '+' || str[i] == '/') has_b64_specials = 1; } int mod = len % 8; int fits_b32_length = (mod != 1 && mod != 3 && mod != 6); if(((has_upper && !has_lower) || (!has_upper && has_lower)) && !has_b64_specials && fits_b32_length) { for(size_t i = 0; i < len; i++) { char c = str[i]; if(c == '0' || c == '1' || c == '8' || c == '9') return 1; } return 0; } return 1; } int is_b32_string(const char *str, size_t len) { if(len == 0) return 0; int mod = len % 8; if(mod == 1 || mod == 3 || mod == 6) return 0; int has_upper = 0; int has_lower = 0; for(size_t i = 0; i < len; i++) { char c = str[i]; if(!strchr(B32_CHARS, toupper((unsigned char)c))) return 0; if(isupper((unsigned char)c)) has_upper = 1; if(islower((unsigned char)c)) has_lower = 1; } if(has_upper && has_lower) return 0; return 1; } int is_printable_buffer(const char *buf, size_t len) { if(len == 0) return 0; for(size_t i = 0; i < len; i++) { unsigned char c = (unsigned char)buf[i]; if (c == '\n' || c == '\r' || c == '\t') { continue; } if (c < 32 || c >= 127) { return 0; } } return 1; } int decode_hex(const char *src, size_t src_len, char *dest, size_t *dest_len) { *dest_len = src_len / 2; for(size_t i = 0; i < *dest_len; i++) { unsigned int val; if(sscanf(&src[i * 2], "%2x", &val) != 1) return 0; dest[i] = (char)val; } dest[*dest_len] = '\0'; return 1; } int decode_base64(const char *src, size_t src_len, char *dest, size_t *dest_len) { int T[256]; for(int i = 0; i < 64; i++) T[(int)B64_CHARS[i]] = i; size_t out_idx = 0; size_t i = 0; for(; i + 3 < src_len; i += 4) { int n = (T[(int)src[i]] << 18) | (T[(int)src[i+1]] << 12) | (T[(int)src[i+2]] << 6) | T[(int)src[i+3]]; dest[out_idx++] = (n >> 16) & 0xFF; dest[out_idx++] = (n >> 8) & 0xFF; dest[out_idx++] = n & 0xFF; } size_t rem = src_len - i; if(rem == 2) { int n = (T[(int)src[i]] << 18) | (T[(int)src[i+1]] << 12); dest[out_idx++] = (n >> 16) & 0xFF; } else if(rem == 3) { int n = (T[(int)src[i]] << 18) | (T[(int)src[i+1]] << 12) | (T[(int)src[i+2]] << 6); dest[out_idx++] = (n >> 16) & 0xFF; dest[out_idx++] = (n >> 8) & 0xFF; } else if(rem == 1) { return 0; } *dest_len = out_idx; dest[*dest_len] = '\0'; return 1; } int decode_base32(const char *src, size_t src_len, char *dest, size_t *dest_len) { int T[256]; memset(T, -1, sizeof(T)); for(int i = 0; i < 32; i++) T[(int)B32_CHARS[i]] = i; size_t out_idx = 0; unsigned int buffer = 0; int bits_left = 0; for(size_t i = 0; i < src_len; i++) { int val = T[(int)toupper((unsigned char)src[i])]; if(val < 0) return 0; buffer = (buffer << 5) | val; bits_left += 5; if(bits_left >= 8) { dest[out_idx++] = (buffer >> (bits_left - 8)) & 0xFF; bits_left -= 8; } } *dest_len = out_idx; dest[*dest_len] = '\0'; return 1; } int main(int argc, char *argv[]) { if(argc < 2) { printf("Usage: %s \n", argv[0]); return 1; } FILE *file = fopen(argv[1], "rb"); if(!file) { perror("Error opening file"); return 1; } char *buffer_A = malloc(BUFFER_SIZE); char *buffer_B = malloc(BUFFER_SIZE); if(!buffer_A || !buffer_B) { printf("Memory allocation failed.\n"); fclose(file); return 1; } /* Read initial data into Buffer A */ size_t current_len = fread(buffer_A, 1, BUFFER_SIZE - 1, file); buffer_A[current_len] = '\0'; fclose(file); /* Setup active pointers for our ping-pong decoding */ char *src_buf = buffer_A; char *dest_buf = buffer_B; int keep_decoding = 1; int iterations = 0; printf("Starting decoding process...\n"); while(keep_decoding) { current_len = clean_string(src_buf, current_len); keep_decoding = 0; size_t next_len = 0; if(is_hex_string(src_buf, current_len)) { printf("Layer %d detected: HEX. Decoding...\n", ++iterations); if(decode_hex(src_buf, current_len, dest_buf, &next_len)) { keep_decoding = 1; } } else if(is_b64_string(src_buf, current_len)) { printf("Layer %d detected: Base64. Decoding...\n", ++iterations); if(decode_base64(src_buf, current_len, dest_buf, &next_len)) { keep_decoding = 1; } } else if(is_b32_string(src_buf, current_len)) { printf("Layer %d detected: Base32. Decoding...\n", ++iterations); if(decode_base32(src_buf, current_len, dest_buf, &next_len)) { keep_decoding = 1; } } if(keep_decoding) { if(!is_printable_buffer(dest_buf, next_len)) { printf("False positive detected (binary garbage generated). Rolling back to step %i.\n", iterations-1); keep_decoding = 0; iterations--; break; } current_len = next_len; /* PING-PONG SWAP: Destination buffer becomes source for the next layer */ char *temp = src_buf; src_buf = dest_buf; dest_buf = temp; } } printf("\n--> DECODED RESULT (%zu bytes) <--\n", current_len); fwrite(src_buf, 1, current_len, stdout); printf("\n----------------------------------\n"); free(buffer_A); free(buffer_B); return 0; }