1. 문제
바이너리를 실행시키면 메시지 박스가 하나 나오고 확인을 누르거나 10초 정도 뒤에 자동으로 종료된다. 정확한 시간을 구하는게 관건이다
UPX로 패킹이 되어있기때문에 언팩을 하고 진행하자
2. 접근방법
메시지 박스가 호출되는곳이 포함된 함수를 찾아보면 다음과 같다
int __thiscall sub_40AEF0(int this, int a2)
{
...
v2 = this;
v3 = *(_DWORD *)(this + 140);
if ( v3 >= 4712 )
{
byte_48E8C8 = 0;
sub_453713(this, 0x9Au, -1);
result = 1;
}
else
{
v4 = v3 + 1;
v79 = this;
*(_DWORD *)(this + 140) = v4;
if ( v4 == 1 && (unsigned __int8)sub_4014B0(this, &v58.decVal.Hi32, &v74, &v78, &v76) == 1 )
sub_47C0DB(v2, v58.decVal.Hi32 + 1, 1);
v44 = *(_BYTE *)(v2 + 156) == 0;
*(_BYTE *)(v2 + 360) = 0;
if ( v44 )
{
do
{
if ( *(_BYTE *)(v2 + 360) )
break;
if ( !byte_48E8C9 && (!byte_49F348 || *(_DWORD *)(v2 + 152)) )
{
if ( PeekMessageW(&Msg, 0, 0, 0, 1u) )
{
while ( Msg.message != 18 )
{
if ( !sub_40E5C0(&Msg) )
{
if ( !sub_40E5E0(&Msg) )
{
TranslateMessage(&Msg);
DispatchMessageW(&Msg);
}
v2 = v79;
}
if ( !PeekMessageW(&Msg, 0, 0, 0, 1u) )
goto LABEL_10;
}
*(_BYTE *)(v2 + 156) = 1;
*(_DWORD *)(v2 + 152) = 1;
}
LABEL_10:
if ( byte_48E8CA == 1 )
{
byte_48E8D0 = 0;
byte_48E8CA = 0;
*(_DWORD *)(v2 + 152) = 1;
}
if ( *(_DWORD *)(v2 + 152) == 1 )
{
sub_40E700(0);
break;
}
}
if ( dword_49F41C )
{
v28 = *(_DWORD *)dword_49F420;
sub_43634B();
v29 = *(_DWORD *)(v2 + 468);
v30 = 0;
if ( v29 )
{
v31 = *(_DWORD ***)(v2 + 464);
do
{
if ( **v31 && *(_DWORD *)**v31 == v28 )
break;
++v30;
++v31;
}
while ( v30 < *(_DWORD *)(v2 + 468) );
}
if ( v30 != v29 )
{
sub_4014B0(v2, &v72, v92, v95, v85);
v53 = **(_DWORD **)(*(_DWORD *)(v2 + 464) + 4 * v30) + 24;
v32 = ++v72;
sub_40EE20(v53);
sub_47A82E((void *)v2, v32, 1, 0);
continue;
}
}
if ( byte_48E8D0 == 1 && !byte_48E8C9 )
{
Sleep(0xAu);
continue;
}
if ( *(_BYTE *)(v2 + 956) && *(_BYTE *)(v2 + 957) != 1 )
{
v33 = timeGetTime();
v34 = *(_DWORD *)(v2 + 960);
v35 = v33 - v34;
if ( v33 < v34 )
--v35;
if ( v35 >= *(_DWORD *)(v2 + 964) )
{
*(_DWORD *)(v2 + 960) = v33;
sub_4014B0(v2, &v73, v88, v86, v84);
...
대략 10초정도 뒤에 프로그램이 종료가 된다. 즉 현재 프로그램이 실행된 시간을 측정하고 정확한 특정 시간과 비교하는 루틴이 존재할 것이다. 위 코드를 보면 timeGetTime() 이라는 함수가 보인다.
timeGetTime() 함수 윈도우(운영체제)가 시작되어서 지금까지 흐른 시간을 1/1000 초 (milliseconds) 단위로 DWORD형을 리턴하는 함수다.
저기 뿐만 아니라 여러 곳에서 해당 함수가 사용된다. 따라서 timeGetTime() 함수가 사용되는 모든 곳에 bp를 걸고 디버깅을 해보자
timeGetTime() 함수가 사용되는 곳을 전부 찾고, bp를 건뒤에 확인해보면 다음과 같다
제일 처음 걸리는 timeGetTime() 로직이다. 현재까지의 시간을 구한뒤 esi에 넣는다. 그다음 다시 timeGetTime()을 호출하고 전에 구한 시간과 비교를 한다. 당연히 후에 구한 시간값이 더 크므로 jmp하게 된다
eax - esi 를 진행한다. 즉, 후에 구한 시간과 전에 구한 시간을 뺀다. 즉 프로그램이 현재까지 실행된 시간을 측정하고 그 시간이 ebx+4에 들어있는 값과 비교하여 더 크면 종료를 하게 된다.
해당 로직에서는 처음 걸리는 timeGetTime() 로직이므로 [ebx+4] 보다 작아서 종료는 안되고 jmp를 할 것이다. 따라서 timeGetTime() 으로 중간중간 시간을 체크하는 것으로 보인다.
결국 ebx+4에 담기는 값이 프로그램의 종료 임계점이다
3. 풀이
[ebx+4] ⇒ 0x2b70(11120)
4. 몰랐던 개념
'워게임 > CodeEngn' 카테고리의 다른 글
[CodeEngine] Malware Analysis L1 (0) | 2021.01.19 |
---|---|
[CodeEngine] Basic RCE L20 (0) | 2021.01.18 |
[CodeEngine] Basic RCE L18 (0) | 2021.01.12 |
[CodeEngine] Basic RCE L17 (0) | 2021.01.11 |
[CodeEngine] Basic RCE L16 (0) | 2021.01.04 |
Uploaded by Notion2Tistory v1.1.0