Search

[reversing.kr] Position

Tags
reversing
Category
reversing.kr
Last edited time
2022/01/07 12:50
Visibility
Public

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"); }
C++
복사
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; }
C++
복사
크게 비교는 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) )
C++
복사
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)))
C++
복사

4. 몰랐던 개념