20230811rev

ZLP042·2023년 8월 14일
post-thumbnail

challenge 105

압축을 풀어보면, baseball(실행파일임), flag_out.txt, text_in.txt, text_out.txt 이렇게 3개의 파일이 나오는데, 메인함수에 나온 사용법을 보면, 파일을 넘기면 암호화해서 결과파일을 만들어주는것 같다.

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  FILE *stream; // [rsp+10h] [rbp-30h]
  __int64 v5; // [rsp+18h] [rbp-28h]
  FILE *v6; // [rsp+20h] [rbp-20h]
  __int64 size; // [rsp+28h] [rbp-18h]
  void *s; // [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);
  v5 = ftell(stream);
  fseek(stream, 0LL, 0);
  if ( v5 != 64 )
  {
    fwrite("Invalid table\n", 1uLL, 0xEuLL, stderr);
    exit(-1);
  }
  fread(&unk_4040, 0x41uLL, 1uLL, stream);
  fclose(stream);
  v6 = fopen(a2[2], "rb");
  if ( !v6 )
  {
    fwrite("File not found\n", 1uLL, 0xFuLL, stderr);
    exit(-1);
  }
  fseek(v6, 0LL, 2);
  size = ftell(v6);
  if ( !size )
  {
    fwrite("Invalid input\n", 1uLL, 0xEuLL, stderr);
    exit(-1);
  }
  fseek(v6, 0LL, 0);
  s = malloc(size + 1);
  if ( !s )
  {
    fwrite("Allocation failed\n", 1uLL, 0x12uLL, stderr);
    exit(-1);
  }
  memset(s, 0, size + 1);
  fread(s, size, 1uLL, v6);
  fclose(v6);
  ptr = (char *)sub_1289(s, (unsigned int)size);
  printf("%s", ptr);
  free(s);
  free(ptr);
  return 0LL;
}

sub_1289는 아래와 같이 정의되어 있었다.

_BYTE *__fastcall sub_1289(_BYTE *a1, int a2)
{
  _BYTE *v3; // rax
  _BYTE *v4; // rax
  _BYTE *v5; // rax
  _BYTE *v6; // rax
  _BYTE *v7; // rax
  _BYTE *v8; // [rsp+18h] [rbp-28h]
  _BYTE *v9; // [rsp+18h] [rbp-28h]
  _BYTE *v10; // [rsp+18h] [rbp-28h]
  _BYTE *v11; // [rsp+20h] [rbp-20h]
  unsigned __int64 size; // [rsp+28h] [rbp-18h]
  _BYTE *v13; // [rsp+30h] [rbp-10h]
  _BYTE *v14; // [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;
  v14 = &a1[a2];
  v11 = a1;
  v8 = v13;
  while ( v14 - v11 > 2 )
  {
    *v8 = byte_4040[*v11 >> 2];
    v8[1] = byte_4040[(v11[1] >> 4) | (16 * *v11) & 0x30];
    v8[2] = byte_4040[(v11[2] >> 6) | (4 * v11[1]) & 0x3C];
    v3 = v8 + 3;
    v8 += 4;
    *v3 = byte_4040[v11[2] & 0x3F];
    v11 += 3;
  }
  if ( v14 != v11 )
  {
    v4 = v8;
    v9 = v8 + 1;
    *v4 = byte_4040[*v11 >> 2];
    if ( v14 - v11 == 1 )
    {
      *v9 = byte_4040[(16 * *v11) & 0x30];
      v5 = v9 + 1;
      v10 = v9 + 2;
      *v5 = 61;
    }
    else
    {
      *v9 = byte_4040[(v11[1] >> 4) | (16 * *v11) & 0x30];
      v6 = v9 + 1;
      v10 = v9 + 2;
      *v6 = byte_4040[(4 * v11[1]) & 0x3C];
    }
    v7 = v10;
    v8 = v10 + 1;
    *v7 = 61;
  }
  *v8 = 0;
  return v13;
}

일단 코드는 모르겠고, 일대일 치환인것 같으니 한번 해보자.

컴파일 rustc main.rs --edition 2021

fn main() {
  let tout = "7/OkZQIau/jou/R1by9acyjjutd0cUdlWshecQhkZUn1cUH1by9g4/9qNAn1byGaby9pbQSjWshgbUmqZAF+JtOBZUn1b8e1YoMPYoM1ny95ZAO+J/jaNAOB2vhrNLhVNDO0cshWNDIjbnrnZQhj4AM1S/Fmu/jou/GjN/n1bUm5JUFpNte1NyH1VA9yZUqLZQu13VR=".chars().collect::<Vec<char>>();
  let tin = "UGVwZXJvIGlzIGEgY29va2llIHN0aWNrLCBkaXBwZWQgaW4gY29tcG91bmQgY2hvY29sYXRlLCBtYW51ZmFjdHVyZWQgYnkgPz8/Pz8gQ29uZmVjdGlvbmVyeSBpbiBTb3V0aCBLb3JlYQpQZXBlcm8gRGF5IGlzIGhlbGQgYW5udWFsbHkgb24gTm92ZW1iZXIgMTE=".chars().collect::<Vec<char>>();
  let fout = "S/jeutjaJvhlNA9Du/GaJBhLbQdjd+n1Jy9BcD3=".chars().collect::<Vec<char>>();
  let mut fin = String::new();
  for i in 0..fout.len() {
    for j in 0..tout.len() {
      if tout[j] == fout[i] {
        fin.push(tin[j]);
        break;
      }
    }
  }
  println!("DH{{{}}}", base64_decode(&fin));
}

fn base64_decode(input: &str) -> String {
  let mut output = Vec::new();
  let mut num_bits = 0;
  let mut value = 0;
  for ch in input.chars().filter(|&ch| ch != '=') {
    let code = match ch {
      'A'..='Z' => ch as u8 - b'A',
      'a'..='z' => ch as u8 - b'a' + 26,
      '0'..='9' => ch as u8 - b'0' + 52,
      '+' => 62,
      '/' => 63,
      _ => continue,
    };
    value = (value << 6) | code as u64;
    num_bits += 6;
    if num_bits >= 8 {
      num_bits -= 8;
      output.push((value >> num_bits) as u8);
    }
  }
  String::from_utf8(output).unwrap()
}

이게되네...?ㅋㅋ 플래그 획득!

challenge 744

upx -d <file> 명령어로 언팩해줘야된다.

메인함수에는 3개의 함수가 있는데, 각 함수는 아래와 같이 생겼다

int __cdecl main(int argc, const char **argv, const char **envp)
{
  sub_401080();
  sub_401610();
  sub_4011D0();
  return 0;
}
BOOL sub_401080()
{
  HANDLE hSnapshot; // [esp+0h] [ebp-234h]
  PROCESSENTRY32W pe; // [esp+4h] [ebp-230h] BYREF

  memset(&pe, 0, sizeof(pe));
  pe.dwSize = 556;
  hSnapshot = CreateToolhelp32Snapshot(0xFu, 0);
  if ( Process32FirstW(hSnapshot, &pe) )
  {
    do
    {
      if ( !wcsicmp(pe.szExeFile, L"x32dbg.exe") )
      {
        sub_4019C0("[+] Detect it!!", (char)hSnapshot);
        system("pause");
        exit(0);
      }
      if ( !wcsicmp(pe.szExeFile, L"x64dbg.exe") )
      {
        sub_4019C0("[+] Detect it!!", (char)hSnapshot);
        system("pause");
        exit(0);
      }
      if ( !wcsicmp(pe.szExeFile, L"Ollydbg.exe") )
      {
        sub_4019C0("[+] Detect it!!", (char)hSnapshot);
        system("pause");
        exit(0);
      }
    }
    while ( Process32NextW(hSnapshot, &pe) );
  }
  return CloseHandle(hSnapshot);
}
int sub_401610()
{
  char v1; // [esp+0h] [ebp-450h]
  int v2; // [esp+0h] [ebp-450h]
  int Offset; // [esp+4h] [ebp-44Ch]
  size_t ElementCount; // [esp+Ch] [ebp-444h]
  size_t i; // [esp+10h] [ebp-440h]
  size_t j; // [esp+10h] [ebp-440h]
  FILE *Stream; // [esp+14h] [ebp-43Ch] BYREF
  char Buffer[2]; // [esp+18h] [ebp-438h] BYREF
  char v9; // [esp+1Ah] [ebp-436h]
  char v10; // [esp+1Bh] [ebp-435h]
  char v11; // [esp+1Fh] [ebp-431h]
  _WORD v12[8]; // [esp+418h] [ebp-38h] BYREF
  char v13[20]; // [esp+428h] [ebp-28h] BYREF
  char v14[16]; // [esp+43Ch] [ebp-14h] BYREF

  strcpy(v13, "abc!@#qwe012fgh456");
  strcpy(v14, "1@!@#a134234");
  *(_WORD *)&v14[13] = 0;
  v14[15] = 0;
  strcpy((char *)v12, "zBNdlwi102394");
  v12[7] = 0;
  if ( sub_401000() )
  {
    sub_4019C0("[+] Detect it!!", v1);
    system("pause");
    exit(0);
  }
  sub_4019C0("[+] Good!", v1);
  if ( fopen_s(&Stream, "flag.txt", "r+b") )
    return -1;
  while ( !feof(Stream) )
  {
    Offset = ftell(Stream);
    ElementCount = fread(Buffer, 1u, 0x400u, Stream);
    if ( !ElementCount )
      break;
    for ( i = 0; i < ElementCount; ++i )
      Buffer[i] ^= v13[i % 0x10];
    for ( j = 0; j < ElementCount; ++j )
    {
      if ( !(j % 2) )
        ++Buffer[j];
      if ( j % 2 == 1 )
        Buffer[j] += 2;
      Buffer[0] ^= v10;
      v9 ^= v11;
    }
    v2 = ftell(Stream);
    fseek(Stream, Offset, 0);
    fwrite(Buffer, 1u, ElementCount, Stream);
    fseek(Stream, v2, 0);
  }
  fclose(Stream);
  return 1;
}
int sub_4011D0()
{
  int v1; // [esp+0h] [ebp-430h]
  int Offset; // [esp+4h] [ebp-42Ch]
  unsigned int ElementCount; // [esp+Ch] [ebp-424h]
  char v4[20]; // [esp+10h] [ebp-420h] BYREF
  unsigned int i; // [esp+24h] [ebp-40Ch]
  FILE *Stream; // [esp+28h] [ebp-408h] BYREF
  char Buffer[3]; // [esp+2Ch] [ebp-404h] BYREF
  char v8; // [esp+2Fh] [ebp-401h]
  char v9; // [esp+30h] [ebp-400h]
  char v10; // [esp+31h] [ebp-3FFh]
  char v11; // [esp+33h] [ebp-3FDh]
  char v12; // [esp+34h] [ebp-3FCh]
  char v13; // [esp+35h] [ebp-3FBh]
  char v14; // [esp+37h] [ebp-3F9h]

  if ( IsDebuggerPresent() )
  {
    system("pause");
    exit(0);
  }
  qmemcpy(v4, "Pp btXHE", 8);
  v4[8] = -112;
  v4[9] = -112;
  v4[10] = 112;
  v4[11] = 64;
  v4[12] = 54;
  v4[13] = 69;
  v4[14] = 85;
  v4[15] = 113;
  v4[16] = 24;
  v4[17] = 25;
  v4[18] = 112;
  if ( fopen_s(&Stream, "flag.txt", "r+b") )
    return -1;
  while ( !feof(Stream) )
  {
    Offset = ftell(Stream);
    ElementCount = fread(Buffer, 1u, 0x400u, Stream);
    if ( !ElementCount )
      break;
    for ( i = 0; i < ElementCount; ++i )
    {
      Buffer[i] ^= v4[i];
      Buffer[0] -= 21;
      v8 ^= 0x18u;
      v12 -= 51;
      v14 ^= 0x44u;
      v13 += 71;
      v9 ^= 0x88u;
      v11 ^= 0x68u;
      Buffer[0] += 21;
      v14 ^= 0x44u;
      v13 -= 71;
      v12 += 51;
      v10 += 17;
      v8 ^= 0x18u;
      v9 ^= 0x88u;
      v11 ^= 0x68u;
    }
    v1 = ftell(Stream);
    fseek(Stream, Offset, 0);
    fwrite(Buffer, 1u, ElementCount, Stream);
    fseek(Stream, v1, 0);
  }
  return fclose(Stream);
}

sub_401080는 올리디버거, x32dbg, x64dbg를 감지하는 것이다.
sub_401080는 암호화와 비슷한 무언가를 수행하며, (암호화라 하기에는 무리가 좀 있다. 왜냐하면, 유효한 글자가 아닌 글자들이 보이기 때문이다. 일단 이 함수가 하는 일은, 파일 내용을 0x400 바이트씩 읽어서 버퍼에 저장하고, 각 바이트를 v13의 해당 위치의 값으로 XOR 연산한다. 그리고 버퍼의 각 짝수 인덱스 바이트를 1 증가시키고, 각 홀수 인덱스 바이트를 2 증가시킨다. 버퍼의 첫번째 바이트를 v10 값으로 XOR 연산하고, v9와 v11 값을 XOR 연산한다. 또한 연산이 끝나면 버퍼를 파일에 쓴다.

또한 sub_4011D0 함수에서 한번 더 암호화를 수행하는데, 파일 내용을 0x400 바이트씩 읽어서 버퍼에 저장하고, 버퍼의 각 바이트를 v4 배열의 해당 인덱스 값으로 XOR 연산한다. 버퍼의 첫 번째 바이트를 21 감소시키고, v8, v12, v14, v13, v9, v11, v14, v13, v12, v10, v8, v9, v11 순서로 XOR 한다. v4는 처음에 -112를 char 타입으로 표현하면 뭔지 몰라서 좀 해멨는데, char 타입으로 표현하면 부호 있는 8비트 정수에서 -112에 해당하는 값인 0x90이 나온다고 한다. 그러면 v4는 \x50\x70\x20\x62\x74\x58\x48\x45\x90\x90\x70\x40\x36\x45\x55\x71\x18\x19\x70\x00가 될 것이다.

그럼 아래와 같이 코드를 짜면 될 것이다
컴파일 rustc main.rs --edition 2021

static V4: [u8; 20] = [0x50, 0x70, 0x20, 0x62, 0x74, 0x58, 0x48, 0x45, 0x90, 0x90, 0x70, 0x40, 0x36, 0x45, 0x55, 0x71, 0x18, 0x19, 0x70, 0x00];
static V12: &str = "abc!@#qwe012fgh456";

fn dec(data: &mut Vec<u8>) -> String {
  for i in (0..data.len()).rev() {
    data[5] = data[5].wrapping_sub(0x11); // 언더플로우 방지
    data[i] ^= V4[i];
  }
  for i in (0..data.len()).rev() {
    data[2] ^= data[7];
    data[0] ^= data[3];
    data[i] -= ((i%2)+1) as u8;
  }
  for i in 0..data.len() { data[i] ^= V12.as_bytes()[i % 0x10]; }
  data.iter().map(|&byte| byte as char).collect()
}

fn enc(data: &mut Vec<u8>) -> String {
  for i in 0..data.len() { data[i] ^= V12.as_bytes()[i % 0x10]; }
  for i in (0..data.len()).rev() {
    data[i] += ((i%2)+1) as u8;
    data[0] ^= data[3];
    data[2] ^= data[7];
  }
  for i in (0..20).rev() {
    data[i] ^= V4[i];
    data[5] = data[5].wrapping_add(0x11); // 오버플로우 방지
  }
  data.iter().map(|&byte| byte as char).collect()
}

fn main() {
  // 버퍼는 다음 링크에서 연산함.
  // https://onlinehextools.com/convert-utf8-to-hex?input=t%5C7%05D%EB%91%85%0C%EA%B3%A2%0A%1E%3CWm%0C%08%14-%5E&prefix=true&padding=true&spacing=true
  let mut buffer = b"\x74\x5C\x37\x05\x44\x8A\x41\x0C\x81\xE1\x0B\x1E\x3C\x57\x6D\x0C\x08\x14\x2D\x5E".to_vec();
  let result = dec(&mut buffer);
  println!("Flag: {}", result);
  println!("Encrypted: {}", enc(&mut (result.as_bytes().to_vec())));
}

1개의 댓글

comment-user-thumbnail
2023년 8월 14일

개발자로서 배울 점이 많은 글이었습니다. 감사합니다.

답글 달기