1nzag

[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