[Dreamhack] baseball

chrmqgozj·2025년 2월 19일

DreamHack

목록 보기
38/39

이 문제는... 제목 보자마자 기억이 났다.
작년 여름방학 동아리 과제였는데 못 풀었었다. 풀이를 봐도 이해가 안 됐었는데 그때보다는 발전했을지! 검증해보자 :)

  1. main
__int64 __fastcall main(int a1, char **a2, char **a3)
{
  FILE *stream; // [rsp+10h] [rbp-30h]
  __int64 table_len; // [rsp+18h] [rbp-28h]
  FILE *input_file; // [rsp+20h] [rbp-20h]
  __int64 size; // [rsp+28h] [rbp-18h]
  void *input; // [rsp+30h] [rbp-10h]
  char *ptr; // [rsp+38h] [rbp-8h]

  if ( a1 != 3 )
  {
    fwrite("Usage : ./baseball <table filename> <input filename>\n", 1uLL, 0x35uLL, stderr);
    exit(-1);
  }
  stream = fopen(a2[1], "rb");
  if ( !stream )
  {
    fwrite("File not found\n", 1uLL, 0xFuLL, stderr);
    exit(-1);
  }
  fseek(stream, 0LL, 2);
  table_len = ftell(stream);
  fseek(stream, 0LL, 0);
  if ( table_len != 64 )
  {
    fwrite("Invalid table\n", 1uLL, 0xEuLL, stderr);
    exit(-1);
  }
  fread(&table, 0x41uLL, 1uLL, stream);
  fclose(stream);
  input_file = fopen(a2[2], "rb");
  if ( !input_file )
  {
    fwrite("File not found\n", 1uLL, 0xFuLL, stderr);
    exit(-1);
  }
  fseek(input_file, 0LL, 2);
  size = ftell(input_file);
  if ( !size )
  {
    fwrite("Invalid input\n", 1uLL, 0xEuLL, stderr);
    exit(-1);
  }
  fseek(input_file, 0LL, 0);
  input = malloc(size + 1);
  if ( !input )
  {
    fwrite("Allocation failed\n", 1uLL, 0x12uLL, stderr);
    exit(-1);
  }
  memset(input, 0, size + 1);
  fread(input, size, 1uLL, input_file);
  fclose(input_file);
  ptr = (char *)change(input, (unsigned int)size);
  printf("%s", ptr);
  free(input);
  free(ptr);
  return 0LL;
}

table 파일을 읽어서 table 변수에 저장
input 파일을 읽어서 input 변수에 저장

change 함수에 input과 size 전달 후 함수 결과 출력

  1. change
_BYTE *__fastcall sub_1289(_BYTE *a1, int a2)
{
  _BYTE *v3; // rax
  _BYTE *v4; // rax
  _BYTE *v5; // rax
  _BYTE *v6; // rax
  _BYTE *v7; // rax
  _BYTE *ans; // [rsp+18h] [rbp-28h]
  _BYTE *v9; // [rsp+18h] [rbp-28h]
  _BYTE *v10; // [rsp+18h] [rbp-28h]
  _BYTE *input; // [rsp+20h] [rbp-20h]
  unsigned __int64 size; // [rsp+28h] [rbp-18h]
  _BYTE *v13; // [rsp+30h] [rbp-10h]
  _BYTE *input_end; // [rsp+38h] [rbp-8h]

  size = (4 * a2 / 3 + 4) / 0x48uLL + 4 * a2 / 3 + 4 + 1;
  if ( size < a2 )
    return 0LL;
  v13 = malloc(size);
  if ( !v13 )
    return 0LL;
  input_end = &a1[a2];
  input = a1;
  ans = v13;
  while ( input_end - input > 2 )
  {
    *ans = table[*input >> 2];
    ans[1] = table[(input[1] >> 4) | (16 * *input) & 0x30];
    ans[2] = table[(input[2] >> 6) | (4 * input[1]) & 0x3C];
    v3 = ans + 3;
    ans += 4;
    *v3 = table[input[2] & 0x3F];
    input += 3;
  }
  if ( input_end != input )
  {
    v4 = ans;
    v9 = ans + 1;
    *v4 = table[*input >> 2];
    if ( input_end - input == 1 )
    {
      *v9 = table[(16 * *input) & 0x30];
      v5 = v9 + 1;
      v10 = v9 + 2;
      *v5 = 61;
    }
    else
    {
      *v9 = table[(input[1] >> 4) | (16 * *input) & 0x30];
      v6 = v9 + 1;
      v10 = v9 + 2;
      *v6 = table[(4 * input[1]) & 0x3C];
    }
    v7 = v10;
    ans = v10 + 1;
    *v7 = 61;
  }
  *ans = 0;
  return v13;
}

python 정리

input = []
output = []
table = [] # size = 64

idx = 0
size = len(input)

while size - idx > 2:
    output.append(table[input[idx] >> 2])
    output.append(table[((input[idx+1] >> 4) | ((16 * input[idx]) & 0x30))])
    output.append(table[((input[idx+2] >> 6) | ((4 * input[idx+1]) & 0x3c))])
    output.append(table[input[idx+2] & 0x3f])
    
    idx += 3
    
if size != idx:
    output.append(table[input[idx] >> 2])
    if size - idx == 1:
        output.append(table[(16 * input[idx]) & 0x30])
        output.append('=')
    else:
        output.append(table[((input[idx+1] >> 4) | ((16 * input[idx]) & 0x30))])
        output.append(table[(4 * input[idx+1]) & 0x3C])
    output.append('=')

우리의 목표는 table을 찾고 flag_out.txt를 역산하는 것이다.

  1. table 찾기
    input은 idx, output은 table 값을 뜻한다.
    각 output 값에 대해 table[input] = output으로 정의해주면 table이 완성된다.
f1 = open('D:\\code\\dh\\rev\\baseball\\text_in.txt', 'r')
f2 = open('D:\\code\\dh\\rev\\baseball\\text_out.txt', 'r')

input = f1.read()
output = f2.read()
table = [0 for i in range(64)]

size = len(input)
idx = 0
idx2 = 0

while size - idx > 2:
    table[ord(input[idx]) >> 2] = output[idx2]
    table[((ord(input[idx+1]) >> 4) | ((16 * ord(input[idx])) & 0x30))] = output[idx2+1]
    table[((ord(input[idx+2]) >> 6) | ((4 * ord(input[idx+1])) & 0x3c))] = output[idx2+2]
    table[ord(input[idx+2]) & 0x3f] = output[idx2+3]
    
    idx += 3
    idx2 += 4
    
if size != idx:
    table[ord(input[idx]) >> 2] = output[idx2]
    if size - idx == 1:
        table[(16 * input[idx]) & 0x30] = output[idx2+1]
    else:
        table[((ord(input[idx+1]) >> 4) | ((16 * ord(input[idx])) & 0x30))] = output[idx2+1]
        table[(4 * ord(input[idx+1])) & 0x3C] = output[idx2+2]

print(table)    

table = [0, 'h', 's', 0, 'R', 'F', '/', 't', 'u', 'I', 0, 'W', '3', 'd', 0, 'Y', 'n', 'S', 'v', 'V', '7', 'O', 'U', 'Q', 'b', 'Z', 'c', 'N', '4', 'J', '2', 0, '1', 'G', 'L', '+', 'e', 'j', 'A', '8', 0, 'r', 0, 'l', 'p', 'g', '5', 'a', 'k', 0, 'B', 'o', '0', 'q', 'y', 'D', 'H', 'm', 0, 0, 'M', '9', 0, 'P']

  1. exploit.py
    flag_out.txt를 이용하여 table에 해당하는 값의 idx가 곧 input이다.
    중간에 헷갈린만한 것은 하나의 idx를 알아냈다고 해서 input의 값이 온전히 나오지 않는다는 것이다.

output[0] = table[input 앞 6bit]
output[1] = table[input 뒤 4bit + input[1] 앞 4bit]
output[2] = table[input[1] 뒤 6bit + input[2] 앞 2bit]
output[3] = table[input[2] 뒤 6bit]

with open('D:\\code\\dh\\rev\\baseball\\flag_out.txt', 'r') as f:
    output = f.read()
    input = []
    table = [0, 'h', 's', 0, 'R', 'F', '/', 't', 'u', 'I', 0, 'W', '3', 'd', 0, 'Y', 'n', 'S', 'v', 'V', '7', 'O', 'U', 'Q', 'b', 'Z', 'c', 'N', '4', 'J', '2', 0, '1', 'G', 'L', '+', 'e', 'j', 'A', '8', 0, 'r', 0, 'l', 'p', 'g', '5', 'a', 'k', 0, 'B', 'o', '0', 'q', 'y', 'D', 'H', 'm', 0, 0, 'M', '9', 0, 'P']
    
    size = len(output)
    idx = 0
    
    while size - idx > 7:
        input.append(((table.index(output[idx]) << 2) | (table.index(output[idx+1]) >> 4)) & 0xff)
        input.append(((table.index(output[idx+1]) << 4) | (table.index(output[idx+2]) >> 2)) & 0xff)
        input.append(((table.index(output[idx+2]) << 6) | (table.index(output[idx+3]))) & 0xff)
        
        idx += 4
    
    if size != idx:
        if output[-1] == '=' and output[-2] == '=':
            input.append((table.index(output[idx]) << 2) | (table.index(output[idx+1]) >> 4))
            input.append((table.index(output[idx+1]) << 4) | (table.index(output[idx+2]) >> 2))
        else:
            input.append((table.index(output[idx]) << 2) | (table.index(output[idx+1]) >> 4))
            
    for i in input:
        print(chr(i),end='')

굉장히 말이 되는 문장이 나왔는데 (누가 봐도 플래그 같은데...) 왜 답이 아니라는 거지
아, = 갯수를 거꾸로 생각했다. ㅋㅋㅋㅋ

with open('D:\\code\\dh\\rev\\baseball\\flag_out.txt', 'r') as f:
    output = f.read()
    input = []
    table = [0, 'h', 's', 0, 'R', 'F', '/', 't', 'u', 'I', 0, 'W', '3', 'd', 0, 'Y', 'n', 'S', 'v', 'V', '7', 'O', 'U', 'Q', 'b', 'Z', 'c', 'N', '4', 'J', '2', 0, '1', 'G', 'L', '+', 'e', 'j', 'A', '8', 0, 'r', 0, 'l', 'p', 'g', '5', 'a', 'k', 0, 'B', 'o', '0', 'q', 'y', 'D', 'H', 'm', 0, 0, 'M', '9', 0, 'P']
    
    size = len(output)
    idx = 0
    
    while size - idx > 7:
        input.append(((table.index(output[idx]) << 2) | (table.index(output[idx+1]) >> 4)) & 0xff)
        input.append(((table.index(output[idx+1]) << 4) | (table.index(output[idx+2]) >> 2)) & 0xff)
        input.append(((table.index(output[idx+2]) << 6) | (table.index(output[idx+3]))) & 0xff)
        
        idx += 4
    
    if size != idx:
        if output[-1] == '=' and output[-2] == '=':
            input.append(((table.index(output[idx]) << 2) | (table.index(output[idx+1]) >> 4)) & 0xff)
            
        else:
            input.append(((table.index(output[idx]) << 2) | (table.index(output[idx+1]) >> 4)) & 0xff)
            input.append(((table.index(output[idx+1]) << 4) | (table.index(output[idx+2]) >> 2)) & 0xff)
            
    for i in input:
        print(chr(i),end='')
  1. 다른 사람 풀이
    문제에서 계속 base64라고 했는데 실제로 cyberchef를 써서 base64로 변환하면 flag_out이랑 text_out과 값이 다르다. 그래서 다른 사람들 풀이를 찾아보니, 치환이었다.
    실제 base64 값과 text_out 값을 일대일로 대응시키고 flag_out에 이를 적용시켜 치환 후, base64 decoding을 하면 플래그 값이 바로 나온다.

이전에는 풀이를 봐도 무슨 말인지 몰랐는데 이제는 바로 풀린다 ㅎㅎ
그래도 이전보다는 발전했다는 사실이 기분이 참 좋다.

0개의 댓글