소스를 보면 일단 파워빌더를 통해 모양을 잡고 윈도우를 생성하는 것으로 추측이 됩니다. 이때 lpfnWndProc에 sub_4011E2 함수를넣는 모습을 볼 수 있는데 이때는 메세지 처리 함수를 지정하는 것으로 아래와 같이 버튼을 누르고 등의 처리를 담당하는 것으로 예상이 됩니다.
이제 처리를 담당하는 sub_4011E2에 들어가 분석을 합니다.
int __stdcall sub_4011E2(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
int v5; // esi
int v6; // ecx
signed int v7; // eax
if ( Msg == 2 ) //WM_DESTROY := 0x02
{
PostQuitMessage(0);
}
else if ( Msg == 513 ) //WM_LBUTTONDOWN := 0x201
{
ReleaseCapture();
SendMessageA(hWnd, 0xA1u, 2u, 0);
}
//WM_COMMAND := 0x111 == 273 메뉴가 클릭 되었을 때 발생 wParam,lParam를 통해 어떤 메뉴인지에 대해 판단
else if ( Msg == 273 && !(wParam >> 16) && wParam == 109 )
//종료에 대한 메뉴
{
ExitProcess(0);
}
if ( Msg == 273 && !(wParam >> 16) && wParam == 113 )
//최소화 처리에 대한 메뉴
ShowWindow(hWnd, 6);
if ( Msg != 273 )
//윈도우에서 기본 처리
return DefWindowProcA(hWnd, Msg, wParam, lParam);
if ( !(wParam >> 16) && wParam == 112 )
//ABOUT에 관한 메세지 출력
MessageBoxA(0, Text, Caption, 0);
/*
Text =
+=================================+
|Key/CrackMe - 2 Created on 21/9/2001|
+=================================+
....이하생략....
Caption = Key/CrackMe #2
*/
if ( !(wParam >> 16) && wParam == 108 )
//Check 버튼이 입력 될때 처리
{
if ( !GetDlgItemTextA(hWnd, 106, dword_403038, 64) || !GetDlgItemTextA(hWnd, 107, byte_403138, 64) )
//둘중 하나라도 입력값이 없어 0이 리턴 되면 메세지 박스 출력
return MessageBoxA(0, aPleaseFillIn1M, Caption, 0);
/*
aPleaseFillIn1M = Please Fill in 1 more Char!!
Caption = Key/CrackMe #2
*/
v5 = 0;
v6 = lstrlenA(dword_403038);
//id의 길이를 저장시킴
v7 = 1;
/*
dword_403038 == id 변수
byte_403138 == password 변수
아래의 설명 이미지 참조!
*/
do
{
v5 = ((signed int)(unsigned __int8)byte_403037[v7] >> 1)
//id의 v7의 인덱스에 들어가 >> 1 연산 즉 1/2
+ (unsigned __int8)byte_403037[v7] * (unsigned __int8)byte_403037[v7]
//위의 결과에 + id의 v7의 인덱스의 값을 제곱
+ v5
//기존의 v5 추가
- (unsigned __int8)byte_403037[v7];
// - id의 v7의 인덱스의 값을 뺀다.
++v7;
//인덱스를 위한 변수 1증가
--v6;
//반복 변수 1--
}
while ( v6 );
//아이디의 길이를 반복할때 마다 v6-- 반복
if ( sub_401383(byte_403138) == v5 )
//만약 password가 v5와 같으면 성공 메세지 출력
MessageBoxA(0, aGoodJobIWishYo, Caption, 0);
else
//아니라면 실패 메세지 출력
MessageBoxA(0, aYouHaveEnterAW, Caption, 0);
}
return 0;
}
참조 이미지
위와 같이 중간에 x64dbg로 동적으로 어디에 사용하는지에 대해 분석도 하면서 IDA를 주로 사용 하며 파악을 하였는데 이 프로그램은 ID를 기준으로 특수한 알고리즘 연산을 통해 Password를 뽑아내는 것으로 보입니다. 이점을 이용하여 CodeEngn일때의 Password를 알아내봅니다.
직접 X64dbg를 통해서 ID가 CodeEngn일때의 알고리즘 후 비교하는 레지스터값을 분석 하는 방법도 있지만 한번 IDA에서 가져온 소스를 조금 수정 하여 출력 해봤습니다.
#include <stdio.h>
int main(int argc, char const *argv[])
{
int v5 = 0;
char *id = "CodeEngn";
int v6 = 8;
//id의 길이를 저장시킴
signed int v7 = 0;
/*
dword_403038 == id 변수
byte_403138 == password 변수
아래의 설명 이미지 참조!
*/
do
{
v5 = ((signed int)(unsigned __int8)id[v7] >> 1)
//id의 v7의 인덱스에 들어가 >> 1 연산 즉 1/2
+ (unsigned __int8)id[v7] * (unsigned __int8)id[v7]
//위의 결과에 + id의 v7의 인덱스의 값을 제곱
+ v5
//기존의 v5 추가
- (unsigned __int8)id[v7];
// - id의 v7의 인덱스의 값을 뺀다.
++v7;
//인덱스를 위한 변수 1증가
--v6;
//반복 변수 1--
}
while ( v6 );
printf("%d",v5);
return 0;
}
그 결과 아래의 결과가 나오는 것을 확인할 수 있는데 직접 테스트를 해봅니다.
[Running] cd "d:\OneDrive - JaeSeo\Codeengn\" && gcc 14.c -o 14 && "d:\OneDrive - JaeSeo\Codeengn\"14
76193
[Done] exited with code=0 in 0.335 seconds