이번 문제는 라업을 참고해가면서 진행했다.
마우스로 그림을 그리고 check 버튼을 눌러 검사할 수 있는 기능이 존재한다.
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
int SystemMetrics; // eax
HWND Window; // eax
int v7; // [esp-1Ch] [ebp-64h]
struct tagMSG Msg; // [esp+4h] [ebp-44h] BYREF
WNDCLASSA WndClass; // [esp+20h] [ebp-28h] BYREF
::hInstance = hInstance;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hbrBackground = (HBRUSH)GetStockObject(0);
WndClass.hCursor = LoadCursorA(0, (LPCSTR)0x7F00);
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIconA(0, (LPCSTR)0x7F00);
WndClass.lpfnWndProc = (WNDPROC)sub_401130;
WndClass.lpszClassName = lpWindowName;
WndClass.lpszMenuName = 0;
WndClass.style = 3;
RegisterClassA(&WndClass);
v7 = GetSystemMetrics(1) / 2 - 0x4B;
SystemMetrics = GetSystemMetrics(0);
Window = CreateWindowExA(
0,
lpWindowName,
lpWindowName,
0xCA0000u,
SystemMetrics / 2 - 0x64,
v7,
0xC8,
0x96,
0,
0,
hInstance,
0);
ShowWindow(Window, 5);
if ( !GetMessageA(&Msg, 0, 0, 0) )
return Msg.wParam;
do
{
TranslateMessage(&Msg);
DispatchMessageA(&Msg);
}
while ( GetMessageA(&Msg, 0, 0, 0) );
return Msg.wParam;
}
LRESULT __stdcall sub_401130(HWND hWnd, UINT Msg, WPARAM wParam, unsigned int lParam)
{
HDC v4; // eax
HGDIOBJ v6; // eax
HDC DC; // esi
void *v8; // esi
HRSRC ResourceA; // eax
HGLOBAL Resource; // eax
_BYTE *v11; // eax
int v12; // edi
_BYTE *v13; // ecx
int v14; // eax
char pv[4]; // [esp+8h] [ebp-80h] BYREF
LONG v16; // [esp+Ch] [ebp-7Ch]
UINT cLines; // [esp+10h] [ebp-78h]
struct tagBITMAPINFO bmi; // [esp+20h] [ebp-68h] BYREF
if ( Msg <= 0x111 )
{
if ( Msg != 0x111 )
{
switch ( Msg )
{
case 1u:
DC = GetDC(hWnd);
hbm = CreateCompatibleBitmap(DC, 0xC8, 0x96);
hdc = CreateCompatibleDC(DC);
h = SelectObject(hdc, hbm);
Rectangle(hdc, 0xFFFFFFFB, 0xFFFFFFFB, 0xCD, 0xCD);
ReleaseDC(hWnd, DC);
::wParam = (WPARAM)CreateFontA(0xC, 0, 0, 0, 0x190, 0, 0, 0, 0x81u, 0, 0, 0, 0x12u, pszFaceName);
dword_4084E0 = (int)CreateWindowExA(
0,
ClassName,
WindowName,
0x50000000u,
0x3C,
0x55,
0x50,
0x1C,
hWnd,
(HMENU)0x64,
hInstance,
0);
SendMessageA((HWND)dword_4084E0, 0x30u, ::wParam, 0);
return 0;
case 2u:
v6 = SelectObject(hdc, h);
DeleteObject(v6);
DeleteDC(hdc);
PostQuitMessage(0);
return 0;
case 0xFu:
v4 = BeginPaint(hWnd, (LPPAINTSTRUCT)bmi.bmiColors);
BitBlt(v4, 0, 0, 0xC8, 0x96, hdc, 0, 0, 0xCC0020u);
EndPaint(hWnd, (const PAINTSTRUCT *)bmi.bmiColors);
return 0;
}
return DefWindowProcA(hWnd, Msg, wParam, lParam);
}
if ( wParam == 0x64 )
{
GetObjectA(hbm, 0x18, pv);
memset(&bmi, 0, 0x28u);
bmi.bmiHeader.biHeight = cLines;
bmi.bmiHeader.biWidth = v16;
bmi.bmiHeader.biSize = 0x28;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 0x18;
bmi.bmiHeader.biCompression = 0;
GetDIBits(hdc, (HBITMAP)hbm, 0, cLines, 0, &bmi, 0);
v8 = operator new(bmi.bmiHeader.biSizeImage);
GetDIBits(hdc, (HBITMAP)hbm, 0, cLines, v8, &bmi, 0);
ResourceA = FindResourceA(0, (LPCSTR)0x65, (LPCSTR)0x18);
Resource = LoadResource(0, ResourceA);
v11 = LockResource(Resource);
v12 = 0;
v13 = v8;
v14 = v11 - (_BYTE *)v8;
while ( *v13 == v13[v14] )
{
++v12;
++v13;
if ( v12 >= 0x15F90 )
{
sub_401500(v8);
return 0;
}
}
MessageBoxA(hWnd, Text, Caption, 0x30u); // wrong
sub_401500(v8);
return 0;
}
return 0;
}
switch ( Msg )
{
case 0x200u:
if ( dword_47D7F8 )
{
MoveToEx(hdc, x, y, 0);
LineTo(hdc, (unsigned __int16)lParam, HIWORD(lParam));
x = (unsigned __int16)lParam;
y = HIWORD(lParam);
InvalidateRect(hWnd, 0, 0);
}
return 0;
case 0x201u:
dword_47D7F8 = 1;
y = HIWORD(lParam);
x = (unsigned __int16)lParam;
return 0;
case 0x202u:
dword_47D7F8 = 0;
return 0;
default:
return DefWindowProcA(hWnd, Msg, wParam, lParam);
}
}
gpt에게 코드 해석을 부탁했더니 bmp 파일을 가져와서 비교하는 루틴을 가지고 있다고 한다.
해당 루틴은 while으로 구성되어 있는데
비교하는 주소를 디버거로 보면 힙에 저장하고 FF가 많이 있는 것을 확인할 수 있다.
exe의 리소스 파일들은 .rsrc 섹션에 존재한다.
음.. 이건 문제를 많이 풀어보면 감각이 생길 것 같은데
이렇게 리소스의 bmp 파일과 비교하는 루틴이 있다는 것을 알았을 때
bmp 파일이 플래그라는 것을 유추할 수 있다.
bmp 파일을 복원하기 위해서 넓이, 높이 등의 정보를 알아야 한다.
ida로 확인해보면 그런 정보들을 설정해주는 것을 확인할 수 있다.
cLines와 v16의 값을 알기 위해 xdbg를 사용한다.
확인해보면 width는 0x96이고 height는 0xc8이다.
이건 라업 보고 알았는데 bitcount도 중요한 필드였다.
bitcount는 ida에서 0x18인 것을 확인할 수 있다.
이제 그림판을 열어서 해당 정보들에 맞는 파일을 생성한다.
픽셀 데이터에 해당하는 부분을 .rsrc 섹션에 있는 데이터로 덮어쓰면 복구가 완료된다.
'write-up(rev) > reversing.kr' 카테고리의 다른 글
[reversing.kr] Position write-up (1) | 2024.06.15 |
---|---|
[reversing.kr] Replace write-up (0) | 2024.06.01 |
[reversing.kr] Music Player write-up (0) | 2024.05.31 |
[reversing.kr] Easy ELF write-up (0) | 2023.08.30 |
[reversing.kr] Easy Unpack write-up (0) | 2023.08.30 |