1nzag

'wargame/reversing.kr'에 해당되는 글 3건

  1. [reversing.kr]Direct3D FPS Writeup
  2. [reversing.kr]Position Writeup
  3. [reversing.kr] ransomware writeup

[reversing.kr]Direct3D FPS Writeup

wargame/reversing.kr

솔직히 이번문제는 엄청난 게싱으로 푼거 같다.


바이너리를 열었더니 FPS 게임이 나왔고, 몬스터를 총으로 쏴서 잡을 수 있었다. 왠지 몬스터를 다 잡으면 플래그가 나올것 같았다.

근데 게임이 너어무 느려서 이대로 몬스터를 다 잡다간 정신이 나갈거 같아서 그냥 정적분석을 했다.


일단 바이너리가 너무 커서 바이너리에 의미있는 스트링이 있는지 확인하였다.

다행히 바이너리에서 검색된 문자열은 얼마 없었고, 의미있는 문자열이 나왔다. 이 문자열을 참조하는 코드를 확인해보았다.

뭔가 보니 위의 count 값을 result가 넘을시 게임이 클리어 되는 것을 보아 result 는 잡은 몬스터의 수와 관련이 있는 것 같다. 그리고 클리어 할 시에 arr_byte 의 스트링을 출력하는데 막상 해당 문자열을 보면 의미있는 문장이 나오지 않기 때문에 암호화 되어있는 것으로 예측했다.


위의 count 값이나 status 배열을 참조하는 코드를 찾아보았다.

status를 참조하는 코드를 찾아봤더니 다음과 같은 루틴이 있었다. arr_byte 에 byte 의 일부분을 xor 하는 것으로 보아 byte 배열 값을 가져오면 플래그 값을 복호화 하여 찾아올수 있다는 것을 알 수 있었다. 

문제는 byte 배열의 어느 인덱스를 찾아오느냐였다. 

인덱스와 관련된 sub_1323440 함수 부분에 들어가봤더니 값은 1의 배수 형태로 리턴을 한다는 것을 알 수 있었다. 아마도 내가 잡은 몬스터나 아니면 몬스터의 번호를 리턴하는것 같다. 0x84 의 크기로 몬스터의 객체가 배열에 저장되어있고, 객체에 몬스터의 HP 정보가 저장되어있는것으로 보인다.

그리고 몬스터의 HP 가 0이 되면 잡은 몬스터의 index 와 관련된 부분의 주소부분에 해당된 암호화된 플래그값을 복호화 하는 것 같다.

어쨌든 중요한건 byte 배열의 값이니, 디버거를 붙인 후에 byte 에 있는 값을 무작정 덤프했다.

몬스터가 몇마리나 있는지는 모르지만 arr_byte 가 있는 부분으로부터 적당히 0x100 개 만큼 값을 덤프하고 byte 배열부분은 해당 arr_byte의 값을 커버할 수 있는 0x100 * 4 * 0x84 만큼을 덤프 한 뒤 복호화 루틴에 돌렸다.


from pwn import *



data = open("dump", "rb").read()

key = open("key", "rb").read()



res = ""

for i in range(len(key)):

res += chr(ord(key[i]) ^ ord(data[(i * 0x84) * 4]))



print hexdump(res)

 될까 했는데 정말 플래그가 떠버렸다 ;;


이걸로 reversing.kr의 쉬운문제는(대략 600명 이상 푼 문제) mp3 문제 빼고는 다 푼것같다. 대체로 난이도가 쉬운편이거나 직감이 잘 떨어져서 빠르게 문제를 푼것 같다. 

mp3문제는 바이너리가 너무 크고 익숙하지도 않고 뭔가 감도 잘 오지 않아서 나중에 여유로우면 풀어야 겠다 ;;


나머지 문제들은 ctf 문제를 풀면서 간간히 같이 풀어야겠다.

'wargame > reversing.kr' 카테고리의 다른 글

[reversing.kr]Position Writeup  (0) 2018.08.02
[reversing.kr] ransomware writeup  (0) 2018.08.02

[reversing.kr]Position Writeup

wargame/reversing.kr

문제의 Readme.txt 를 읽어보면 다음과 같다.

ReversingKr KeygenMe



Find the Name when the Serial is 76876-77776

This problem has several answers.


Password is ***p



바이너리는 name과 serial 두가지 값을 받는데, serial 값이 위와 같을 때의 name 값을 알아내는 것이 목표이다.


처음에 바이너리를 보면 코드가 은근히 많은데, 바이너리에 나와있는 문자열 검색을 통해 핵심 루틴이 어디있는지 파악했다.

('Wrong' 이라는 문자열을 검색해보았다.)



다음과 같이 함수의 결과값에 따라 나오는 문자열이 다른것을 보아, sub_401740 부분이 시리얼과 이름값을 비교하는 것을 알 수 있다.

위의 함수 내용은 다음과 같다.

signed int __stdcall sub_401740(int a1)

{

  int index; // edi

  int alpha_loc; // esi

  int sub_index; // esi

  __int16 sub_char; // bx

  unsigned __int8 name_char_0; // al

  unsigned __int8 num_0_0; // ST2C_1

  unsigned __int8 name_char_1; // al

  unsigned __int8 num_0_1; // bl

  wchar_t *CS_buffer; // eax

  __int16 buffer_char_0; // di

  wchar_t *v12; // eax

  __int16 serial_char_1; // di

  wchar_t *v14; // eax

  __int16 v15; // di

  wchar_t *v16; // eax

  __int16 v17; // di

  wchar_t *v18; // eax

  __int16 v19; // di

  unsigned __int8 name_string_2; // al

  unsigned __int8 num_6_0; // ST2C_1

  unsigned __int8 name_string_3; // al

  unsigned __int8 num_6_1; // bl

  wchar_t *v24; // eax

  __int16 v25; // di

  wchar_t *v26; // eax

  __int16 v27; // di

  wchar_t *v28; // eax

  __int16 v29; // di

  wchar_t *v30; // eax

  __int16 v31; // di

  wchar_t *v32; // eax

  __int16 v33; // si

  unsigned __int8 num_3_1; // [esp+10h] [ebp-28h]

  unsigned __int8 num_9_1; // [esp+10h] [ebp-28h]

  unsigned __int8 num_4_1; // [esp+11h] [ebp-27h]

  unsigned __int8 num_10_1; // [esp+11h] [ebp-27h]

  unsigned __int8 num_1_1; // [esp+13h] [ebp-25h]

  unsigned __int8 num_7_1; // [esp+13h] [ebp-25h]

  unsigned __int8 num_2_1; // [esp+14h] [ebp-24h]

  unsigned __int8 num_8_1; // [esp+14h] [ebp-24h]

  unsigned __int8 num_2_0; // [esp+19h] [ebp-1Fh]

  unsigned __int8 num_8_0; // [esp+19h] [ebp-1Fh]

  unsigned __int8 num_3_0; // [esp+1Ah] [ebp-1Eh]

  unsigned __int8 num_9_0; // [esp+1Ah] [ebp-1Eh]

  unsigned __int8 num_1_0; // [esp+1Bh] [ebp-1Dh]

  unsigned __int8 num_7_0; // [esp+1Bh] [ebp-1Dh]

  unsigned __int8 num_4_0; // [esp+1Ch] [ebp-1Ch]

  unsigned __int8 num_10_0; // [esp+1Ch] [ebp-1Ch]

  int name_string; // [esp+20h] [ebp-18h]

  int serial_string; // [esp+24h] [ebp-14h]

  char buffer; // [esp+28h] [ebp-10h]

  int v53; // [esp+34h] [ebp-4h]


  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&name_string);

  index = 0;

  v53 = 0;

  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&serial_string);

  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&buffer);

  LOBYTE(v53) = 2;

  CWnd::GetWindowTextW(a1 + 304, &name_string);

  if ( *(_DWORD *)(name_string - 12) == 4 )

  {

    alpha_loc = 0;

    while ( (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&name_string, alpha_loc) >= 'a'

         && (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&name_string, alpha_loc) <= 'z' )

    {

      if ( ++alpha_loc >= 4 )

      {

LABEL_7:

        sub_index = 0;

        while ( 1 )

        {

          if ( index != sub_index )

          {

            sub_char = ATL::CSimpleStringT<wchar_t,1>::GetAt(&name_string, sub_index);

            if ( (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&name_string, index) == sub_char )

              goto LABEL_2;

          }

          if ( ++sub_index >= 4 )

          {

            if ( ++index < 4 )

              goto LABEL_7;

            CWnd::GetWindowTextW(a1 + 420, &serial_string);

            if ( *(_DWORD *)(serial_string - 12) == 11// serial string's length is 11

              && (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 5) == '-' )

            {

              name_char_0 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&name_string, 0);

              num_0_0 = (name_char_0 & 1) + 5;

              num_4_0 = ((name_char_0 >> 4) & 1) + 5;

              num_2_0 = ((name_char_0 >> 1) & 1) + 5;

              num_3_0 = ((name_char_0 >> 2) & 1) + 5;

              num_1_0 = ((name_char_0 >> 3) & 1) + 5;

              name_char_1 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&name_string, 1);

              num_3_1 = (name_char_1 & 1) + 1;

              num_2_1 = ((name_char_1 >> 4) & 1) + 1;

              num_4_1 = ((name_char_1 >> 1) & 1) + 1;

              num_0_1 = ((name_char_1 >> 2) & 1) + 1;

              num_1_1 = ((name_char_1 >> 3) & 1) + 1;

              CS_buffer = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buffer);

              itow_s(num_0_0 + num_0_1, CS_buffer, 0xAu, 10);// get integer 2

              buffer_char_0 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&buffer, 0);

              if ( (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 0) == buffer_char_0 )

              {

                ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buffer, -1);

                v12 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buffer);

                itow_s(num_1_0 + num_1_1, v12, 0xAu, 10);

                serial_char_1 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 1);

                if ( serial_char_1 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&buffer, 0) )

                {

                  ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buffer, -1);

                  v14 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buffer);

                  itow_s(num_2_0 + num_2_1, v14, 0xAu, 10);

                  v15 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 2);

                  if ( v15 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&buffer, 0) )

                  {

                    ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buffer, -1);

                    v16 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buffer);

                    itow_s(num_3_0 + num_3_1, v16, 0xAu, 10);

                    v17 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 3);

                    if ( v17 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&buffer, 0) )

                    {

                      ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buffer, -1);

                      v18 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buffer);

                      itow_s(num_4_0 + num_4_1, v18, 0xAu, 10);

                      v19 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 4);

                      if ( v19 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&buffer, 0) )

                      {

                        ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buffer, -1);

                        name_string_2 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&name_string, 2);

                        num_6_0 = (name_string_2 & 1) + 5;

                        num_10_0 = ((name_string_2 >> 4) & 1) + 5;

                        num_8_0 = ((name_string_2 >> 1) & 1) + 5;

                        num_9_0 = ((name_string_2 >> 2) & 1) + 5;

                        num_7_0 = ((name_string_2 >> 3) & 1) + 5;

                        name_string_3 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&name_string, 3);

                        num_9_1 = (name_string_3 & 1) + 1;

                        num_8_1 = ((name_string_3 >> 4) & 1) + 1;

                        num_10_1 = ((name_string_3 >> 1) & 1) + 1;

                        num_6_1 = ((name_string_3 >> 2) & 1) + 1;

                        num_7_1 = ((name_string_3 >> 3) & 1) + 1;

                        v24 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buffer);

                        itow_s(num_6_0 + num_6_1, v24, 0xAu, 10);

                        v25 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 6);

                        if ( v25 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&buffer, 0) )

                        {

                          ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buffer, -1);

                          v26 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buffer);

                          itow_s(num_7_0 + num_7_1, v26, 0xAu, 10);

                          v27 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 7);

                          if ( v27 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&buffer, 0) )

                          {

                            ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buffer, -1);

                            v28 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buffer);

                            itow_s(num_8_0 + num_8_1, v28, 0xAu, 10);

                            v29 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 8);

                            if ( v29 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&buffer, 0) )

                            {

                              ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buffer, -1);

                              v30 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buffer);

                              itow_s(num_9_0 + num_9_1, v30, 0xAu, 10);

                              v31 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 9);

                              if ( v31 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&buffer, 0) )

                              {

                                ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buffer, -1);

                                v32 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buffer);

                                itow_s(num_10_0 + num_10_1, v32, 0xAu, 10);

                                v33 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 10);

                                if ( v33 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&buffer, 0) )

                                {

                                  ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buffer, -1);

                                  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&buffer);

                                  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&serial_string);

                                  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&name_string);

                                  return 1;

                                }

                              }

                            }

                          }

                        }

                      }

                    }

                  }

                }

              }

            }

            goto LABEL_2;

          }

        }

      }

    }

  }

LABEL_2:

  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&buffer);

  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&serial_string);

  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&name_string);

  return 0;

위에서 처음 보는 함수나 자료형이 많았는데 주로 많이 나오는 CSimpleStringT 객체의 두 가지 함수는 GetAt 함수와 GetBuffer 함수는 다음과 같은 함수였다.


GetAt: https://msdn.microsoft.com/ko-kr/library/sddk80xf.aspx

GetBuffer: https://msdn.microsoft.com/ko-kr/library/sddk80xf.aspx


즉, GetAt 함수는 CSimpleStringT 객체의 문자열에서 특정 인덱스의 문자를 가져오는 함수이고 (일반 문자열에서의 [] 연산과 같다.)

GetBuffer 함수는 CSimple StringT 객체의 문자열과 일반 문자열 배열을 동기화(??) 시켜주는 함수였다.


이에 따라 위의 루틴을 분석해보면 각 name 캐릭터의 문자들을 비트연산해서 각각의 값이 시리얼과 일치하는지 확인하는 루틴이다.

근데 이 연산이 무조건 일대일 대응도 아니고 일일이 계산하는 것이 매우 머리아픈 짓이므로 z3 Solver 를 사용하기로 했다.


from z3 import *


#76876-77776

a0 = BitVec('a0', 8)

a1 = BitVec('a1', 8)

a2 = BitVec('a2', 8)

a3 = BitVec('a3', 8)

s = Solver()


s.add(a0 >= 0x60)

s.add(a1 >= 0x60)

s.add(((a0 & 1) + 5) + (((a1 >> 2) & 1) + 1) == 7)

s.add((((a0 >> 3) & 1) + 5) + (((a1 >> 3) & 1) + 1) == 6)

s.add((((a0 >> 1) & 1) + 5) + (((a1 >> 4) & 1) + 1) == 8)

s.add((((a0 >> 2) & 1) + 5) + ((a1 & 1) + 1) == 7)

s.add((((a0 >> 4) & 1) + 5) + (((a1 >> 1) & 1) + 1) == 6)

#other


s.add(a3 == 112) #letter 'p'

s.add(a2 >= 0x60)

s.add(((a2 & 1) + 5) + (((a3 >> 2) & 1) + 1) == 7)

s.add((((a2 >> 3) & 1) + 5) + (((a3 >> 3) & 1) + 1) == 7)

s.add((((a2 >> 1) & 1) + 5) + (((a3 >> 4) & 1) + 1) == 7)

s.add((((a2 >> 2) & 1) + 5) + ((a3 & 1) + 1) == 7)

s.add((((a2 >> 4) & 1) + 5) + (((a3 >> 1) & 1) + 1) == 6)


print(s.check())

print(s.model())



갓 z3 ;;


따라서 나온 결과값이 name 값이 되고, 플래그가 된다.

'wargame > reversing.kr' 카테고리의 다른 글

[reversing.kr]Direct3D FPS Writeup  (0) 2018.08.02
[reversing.kr] ransomware writeup  (0) 2018.08.02

[reversing.kr] ransomware writeup

wargame/reversing.kr

문제를 받아보면 upx 패킹이 되어있다는 것을 알 수 있다. 


패킹을 푼 후 코드를 분석해보면 hexray를 막기 위해 의미없는 코드들을 중간에 끼워 함수의 길이를 크게 늘려놓은것을 확인할 수 있다. 

보는 바와 같이 레지스터 값을 push 하고 다시 pop 하는 과정만 반복한다.

실제 의미있는 코드부분에 함수의 프롤로그가 될 수 있도록 패치를 한뒤, 함수지정을 해서 hexray를 가능하게 했다.


int sub_44A765()

{

  char file_char; // al

  int v2; // [esp+Ch] [ebp-24h]

  char *key_1_; // [esp+14h] [ebp-1Ch]

  const char *__key_end_offset__; // [esp+18h] [ebp-18h]

  FILE *new_fp; // [esp+1Ch] [ebp-14h]

  unsigned int file_size; // [esp+20h] [ebp-10h]

  unsigned int key_length; // [esp+24h] [ebp-Ch]

  unsigned int index; // [esp+28h] [ebp-8h]

  FILE *fp; // [esp+2Ch] [ebp-4h]


  printf("Key : ");

  no_mean_401000();

  scanf("%s", Key_44D370);

  __key_end_offset__ = Key_44D370;

  key_1_ = &Key_44D370[1];

  __key_end_offset__ += strlen(__key_end_offset__);

  v2 = ++__key_end_offset__ - &Key_44D370[1];

  key_length = __key_end_offset__ - &Key_44D370[1];

  no_mean_401000();

  index = 0;

  fp = fopen("file", "rb");

  no_mean_401000();

  if ( !fp )

  {

    no_mean_401000();

    printf("\n\n\n파일을 찾을수 없다!\n");

    no_mean_401000();

    exit(0);

  }

  fseek(fp, 0, 2);                              // go to end

  no_mean_401000();

  file_size = ftell(fp);

  no_mean_401000();

  rewind(fp);

  no_mean_401000();

  while ( !feof(fp) )

  {

    no_mean_401000();

    file_char = fgetc(fp);

    file_stream_5415B8[index] = file_char;

    no_mean_401000();

    ++index;

    no_mean_401000();

  }

  no_mean_401000();

  for ( index = 0; index < file_size; ++index )

  {

    file_stream_5415B8[index] ^= Key_44D370[index % key_length];

    no_mean_401000();

    file_stream_5415B8[index] = ~file_stream_5415B8[index];

    no_mean_401000();

  }

  fclose(fp);

  no_mean_401000();

  new_fp = fopen("file", "wb");

  no_mean_401000();

  no_mean_401000();

  for ( index = 0; index < file_size; ++index )

  {

    fputc(file_stream_5415B8[index], new_fp);

    no_mean_401000();

  }

  printf(asc_44C1E8);

  no_mean_401000();

  return getch();

}

 보는 바와 같이 루틴은 단순히 키값을 받고, 파일의 값을 not 한뒤 키값과 xor 해서 파일을 암호화 한다. 

readme.txt 를 보면 파일의 형식은 exe 였다. exe 파일은 보통 앞의 magic 값인 MZ 뒤에 널바이트가 꽤 많이 붙어있기 때문에 앞의 MZ를 제외한 나머지부분의 원래 값이 0이라 가정하고 나올 수 있는 key 값을 나오게 해보았다.

다음과 같이 대강 key값으로 보이는 부분이 몇개 보인다. 특히 play 부분의 단어가 같은 간격으로 반복되는 것으로 보아 key의 길이는 12바이트라는 것을 알 수 있었고 이를 통해 키 값을 게싱하여 파일을 복호화하였다.


복호화 된 exe 파일은 upx 패킹이 되어있었고, 그 파일을 실행시키면 키값을 알려준다. (나는 실행이 안되길래 그냥 프로그램을 까서 확인했다.)

'wargame > reversing.kr' 카테고리의 다른 글

[reversing.kr]Direct3D FPS Writeup  (0) 2018.08.02
[reversing.kr]Position Writeup  (0) 2018.08.02