블로그 이전했습니다. https://jeongzero.oopy.io/
[reversing.kr] Position
본문 바로가기
워게임/reversing.kr

[reversing.kr] Position

728x90

1. 문제


1) 문제 확인

키젠 같은 놈이 뜬다. mfc로 만들어졌으며 76876-77776 시리얼 넘버에 해당하는 비밀번호를 입력하는게 목표이다. 그럼 아마 Correct가 뜨겠지?

2) 코드흐름 파악

Wrong을 따라서 아이다에서 보면 Correct 문자열이 포함된 로직을 찾을수 있다.

void __thiscall sub_401CD0(char *this)
{
  char *v1; // esi
  int v2; // eax
  CWnd *v3; // ecx

  v1 = this;
  v2 = sub_401740(this);
  v3 = (CWnd *)(v1 + 188);
  if ( v2 )
    CWnd::SetWindowTextW(v3, L"Correct!");
  else
    CWnd::SetWindowTextW(v3, L"Wrong");
}

sub_401740() 함수의 반환값을 1로 만들면 된다. 해당 함수를 봐보자

signed int __stdcall sub_401740(int a1)
{
  .. 생략

  if ( *(_DWORD *)(v50 - 12) == 4 )
  {
    v3 = 0;
    while ( (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&v50, v3) >= 0x61u
         && (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&v50, v3) <= 0x7Au )
    {
      if ( ++v3 >= 4 )
      {
LABEL_7:
        v4 = 0;
        while ( 1 )
        {
          if ( v1 != v4 )
          {
            v5 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&v50, v4);
            if ( (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&v50, v1) == v5 )
              goto LABEL_2;
          }
          if ( ++v4 >= 4 )
          {
            if ( ++v1 < 4 )
              goto LABEL_7;
            CWnd::GetWindowTextW(a1 + 420, &v51);
            if ( *(_DWORD *)(v51 - 12) == 11 && (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&v51, 5) == '-' )
            {
								... 생략
							  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>>>(&v52);
  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&v51);
  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&v50);
  return 0;
}

크게 비교는 4번 하고 모든 조건을 다 통고해서 return 1이 되게 해야한다. 바로 동적분석으로 확인해보자

2. 접근방법


위 4개의 조건부터 하나씩 확인해보자

  • if ( *(_DWORD *)(v50 - 12) == 4 )

    분석 결과 입력한 input name의 개수이다

  • while ( (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&v50, v3) >= 0x61u && (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&v50, v3) <= 0x7Au

    0x61 ~ 0x7a 사이의 값 즉 소문자 만 입력받는것으로 보인다

  • if ( *(_DWORD *)(v51 - 12) == 11 && (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&v51, 5) == '-' )

    이번에는 입력한 시리얼 키가 11자리 이고 00000-00000 형태인지 확인한다

  • if ( (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&v50, v1) == v5 )

    input name의 한바이트씩 서로 비교하면서 같은지 다른지 비교한다.

여기까지 알아낸 것은 input name의 값은 총 4바이트이며 소문자 영어와 각기 다른 문자를 입력해야한다는 것이다. 그다음으로 크게 2가지의 조건이 있다

							input_0 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&v50, 0);
              v7 = (input_0 & 1) + 5;
              v48 = ((input_0 >> 4) & 1) + 5;
              v42 = ((input_0 >> 1) & 1) + 5;
              v44 = ((input_0 >> 2) & 1) + 5;
              v46 = ((input_0 >> 3) & 1) + 5;
              input_1 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&v50, 1);
              v34 = (input_1 & 1) + 1;
              v40 = ((input_1 >> 4) & 1) + 1;
              v36 = ((input_1 >> 1) & 1) + 1;
              v9 = ((input_1 >> 2) & 1) + 1;
              v38 = ((input_1 >> 3) & 1) + 1;
              v10 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&v52);
              itow_s(v7 + v9, v10, 0xAu, 10);
              v11 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&v52, 0);
              if ( (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&v51, 0) == v11 )
              {
                ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&v52, -1);
                v12 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&v52);
                itow_s(v46 + v38, v12, 0xAu, 10);
                v13 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&v51, 1);
                if ( v13 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&v52, 0) )
                {
                  ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&v52, -1);
                  v14 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&v52);
                  itow_s(v42 + v40, v14, 0xAu, 10);
                  v15 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&v51, 2);
                  if ( v15 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&v52, 0) )
                  {
                    ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&v52, -1);
                    v16 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&v52);
                    itow_s(v44 + v34, v16, 0xAu, 10);
                    v17 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&v51, 3);
                    if ( v17 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&v52, 0) )
                    {
                      ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&v52, -1);
                      v18 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&v52);
                      itow_s(v48 + v36, v18, 0xAu, 10);
                      v19 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&v51, 4);
                      if ( v19 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&v52, 0) )
  • input name의 0,1 인덱스의 문자를 가져온뒤 각 인덱스마다 4번씩 특정 연산을 진행한다.
  • input_0 과 input_1 의 연산결과를 serial[0]과 비교한다 // v7+v9 == v11
  • 동일하게 serial[4] 까지 비교한다

이렇게 한번의 큰 조건이 끝난다. 정리하면, input[0], input[1] 의 특정 연산결과 값을 serial[0~4]와 비교한다. 나머지 큰 조건문 하나도 마찬가지로 input[2], input[3] 의 특정 연산결과 값을 serial[6~10]과 비교한다.

따라서 위 코드를 파이썬 코드로 포팅해서 값을 input을 알아내면 된다. 참고로 4자리 중 마지막 은 'p' 라고 힌트를 줬다

3. 풀이


serial="7687677776"
password=""
def check(serial,password):
	for input_0 in range(0x61,0x7b):
		for input_1 in range(0x61,0x7b):
			#print(chr(input_0)+' '+chr(input_1))
			v7 = (input_0 & 1) + 5;
		        v48 = ((input_0 >> 4) & 1) + 5;
		        v42 = ((input_0 >> 1) & 1) + 5;
		        v44 = ((input_0 >> 2) & 1) + 5;
		        v46 = ((input_0 >> 3) & 1) + 5;

		        v34 = (input_1 & 1) + 1;
		        v40 = ((input_1 >> 4) & 1) + 1;
			v36 = ((input_1 >> 1) & 1) + 1;
			v9 = ((input_1 >> 2) & 1) + 1;
			v38 = ((input_1 >> 3) & 1) + 1;
			#print(chr(v7+v9)+' '+serial[0])
			if str(v7+v9)==serial[0]:
				#print('first round ok')
				if str(v46+v38)==serial[1]:
					#print('second round ok')
					if str(v42+v40)==serial[2]:
						#print('third round ok')
						if str(v44+v34)==serial[3]:
							#print('final round ok')
							if str(v48+v36)==serial[4]:
								if input_0!=input_1:
									password+=chr(input_0)
									password+=chr(input_1)
									print(password)
									return password



def check2(serial,password):
	print(password)
	for input_2 in range(0x61,0x7b):
		input_3="p"
		v21 = (input_2 & 1) + 5;
                v49 = ((input_2 >> 4) & 1) + 5;
                v43 = ((input_2 >> 1) & 1) + 5;
                v45 = ((input_2 >> 2) & 1) + 5;
                v47 = ((input_2 >> 3) & 1) + 5;

                v35 = (ord(input_3) & 1) + 1;
                v41 = ((ord(input_3) >> 4) & 1) + 1;
                v37 = ((ord(input_3) >> 1) & 1) + 1;
                v23 = ((ord(input_3) >> 2) & 1) + 1;
                v39 = ((ord(input_3) >> 3) & 1) + 1;

		if str(v21+v23)==serial[5]:
			if str(v47+v39)==serial[6]:
				if str(v43+v41)==serial[7]:
					if str(v45+v35)==serial[8]:
						if str(v49+v37)==serial[9]:
							if(input_2!=input_3):
								print('ok')
								password+=chr(input_2)
								password+=input_3
								return password


print(check2(serial,check(serial,password)))

4. 몰랐던 개념


728x90

'워게임 > reversing.kr' 카테고리의 다른 글

[reversing.kr] Ransomware  (0) 2020.12.22
[reversing.kr] Direct 3D FPS  (0) 2020.12.21
[reversing.kr] ImagePrc  (0) 2020.12.15
[reversing.kr] Replace  (0) 2020.12.14
[reversing.kr] Music Player.exe  (0) 2020.12.11