[Cryptanalysis] Code - Pseudo Random Number Generator

주어진 파일은 그냥 무난한 바이너리 파일이고, 동봉된 코드를 확인해봅시다.
Given file is just ordinary binary file, and let's check the code.

 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define KEY_SIZE 32
#define BUFF_SIZE 1024

unsigned int holdrand = 0;

static void Srand (unsigned int seed) {
  holdrand = seed;
}

static int Rand (void) {
  return(((holdrand = holdrand * 214013L + 2531011L) >> 16) & 0x7fff);
}

char* genere_key(void) {
  int i;
  static char key[KEY_SIZE+1];
  const char charset[] = 
    "abcdefghijklmnopqrstuvwxyz"
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    "123456789";

  for(i = 0; i < KEY_SIZE; i++) {
    key[i] = charset[Rand() % (sizeof(charset) - 1)];
  }
  key[KEY_SIZE] = '\0';

  return key;
}

void crypt_buffer(unsigned char *buffer, size_t size, char *key) {
  size_t i;
  int j;

  j = 0;
  for(i = 0; i < size; i++) {
    if(j >= KEY_SIZE)
      j = 0;
    buffer[i] ^= key[j];
    j++;
  }
}

void crypt_file(FILE *in, FILE *out) {
  unsigned char buffer[BUFF_SIZE];
  char *key;
  size_t size;

  key = genere_key();

  printf("[+] Using key : %s\n", key);

  do {
    size = fread(buffer, 1, BUFF_SIZE, in);
    crypt_buffer(buffer, size, key);
    fwrite(buffer, 1, size, out);

  }while(size == BUFF_SIZE);  
}

int main(int argc, char **argv) {
  char path[128];
  FILE *in, *out;

  Srand(time(NULL));

  if(argc != 2) {
    printf("[-] Usage : %s <file>\n", argv[0]);
    return EXIT_FAILURE;
  }

  snprintf(path, sizeof(path)-1, "%s.crypt", argv[1]);

  if((in = fopen(argv[1], "r")) == NULL) {
    perror("[-] fopen (in) ");
    return EXIT_FAILURE;
  }

  if((out = fopen(path, "w")) == NULL) {
    perror("[-] fopen (out) ");
    return EXIT_FAILURE;
  }

  crypt_file(in, out);

  printf("[+] File %s crypted !\n", path);
  printf("[+] DONE.\n");
  return EXIT_SUCCESS;
}

코드에서 time(NULL)로 난수의 시작을 잡는 것을 알 수 있고 time(NULL)은 초 단위로 변화하는 함수이기 때문에 문제에서 2012년 12월에 암호화를 진행했다는 힌트를 준 이상 가능한 시작 값은 대략 300만개 정도이기 때문에 모든 경우에 대해 다 해보면 답을 알 수 있습니다. zip / gzip / bz2 / rar 등의 다양한 압축파일의 포맷과 일치하는지를 확인해야 하는게 조금 짜증났습니다. Timestamp 값은 이 사이트의 도움을 받았습니다.
In the code, time(NULL) is used to make random number, and value of time(NULL) determined by current time in seconds. Since the encryption is occured in Dec 2012, possible time(NULL) value is about 3,000,000. So we can check every possibilities.
It is littlebit annoying to check the format of various compress file such as zip / gzip /bz2 /rar. I refer the Timestamp value on this site.

 cipher = open('cipher.crypt','rb').read()
def brute(srand):
  bytearr = b''
  val = srand
  key = [None]*32
#  keys = ''
  charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789"
  for i in range(32):
    val = (val*214013+2531011) & 0xffffffff    
  #  print(val)
    ext = (val >> 16)&0x7fff
    key[i] = ord(charset[ext%61])
#    keys += charset[ext%61]
#  print(keys)
#  exit(0)
  for i in range(len(cipher)):
    c = key[i%32] ^ cipher[i]
    #if c > 128: return None
    if i == 0 and c != 66: return None
    if i == 1 and c != 90: return None
    if i == 2 and c != 104: return None

    bytearr += bytes([c])
  plain = open(str(srand)+'.bz2','wb')
  plain.write(bytearr)
  plain.close()
  return True
T = 1354003999
while True:
#  brute(100)
#  exit(0)
  if brute(T): print("get ", T)
  T += 1
  if (T-1354003998) % 100000 == 0:
    print(T-1354003998) 

  Comments