2018 ROOT CTF Reversing Writeup
ROOT_Process_1
int sub_EA1860()
{
int v0; // edx
int v1; // ecx
int v2; // edx
int v3; // ecx
int v4; // edx
int v5; // ecx
int v6; // edx
int v7; // ecx
int v8; // edx
int v9; // ecx
int v10; // edx
int v11; // ecx
int v12; // edx
int v13; // ecx
int v14; // edx
int v15; // ecx
int v16; // edx
int v17; // edx
int v18; // ecx
int v19; // ST08_4
DWORD v21; // [esp+250h] [ebp-448h]
HWND hWnd; // [esp+25Ch] [ebp-43Ch]
int j; // [esp+268h] [ebp-430h]
CHAR *v24; // [esp+274h] [ebp-424h]
unsigned int i; // [esp+280h] [ebp-418h]
BOOL v26; // [esp+28Ch] [ebp-40Ch]
PROCESSENTRY32 pe; // [esp+298h] [ebp-400h]
char Dst[280]; // [esp+3C8h] [ebp-2D0h]
size_t v29; // [esp+4E0h] [ebp-1B8h]
int v30; // [esp+4ECh] [ebp-1ACh]
int v31; // [esp+4F8h] [ebp-1A0h]
int v32; // [esp+504h] [ebp-194h]
DWORD dwProcessId; // [esp+510h] [ebp-188h]
HANDLE hSnapshot; // [esp+528h] [ebp-170h]
int v35; // [esp+640h] [ebp-58h]
int v36; // [esp+644h] [ebp-54h]
int v37; // [esp+648h] [ebp-50h]
int v38; // [esp+64Ch] [ebp-4Ch]
int v39; // [esp+650h] [ebp-48h]
int v40; // [esp+654h] [ebp-44h]
int v41; // [esp+658h] [ebp-40h]
int v42; // [esp+65Ch] [ebp-3Ch]
int v43; // [esp+660h] [ebp-38h]
int v44; // [esp+664h] [ebp-34h]
int v45; // [esp+668h] [ebp-30h]
int v46; // [esp+66Ch] [ebp-2Ch]
int v47; // [esp+670h] [ebp-28h]
int v48; // [esp+674h] [ebp-24h]
int v49; // [esp+678h] [ebp-20h]
int v50; // [esp+67Ch] [ebp-1Ch]
int v51; // [esp+680h] [ebp-18h]
int v52; // [esp+684h] [ebp-14h]
int v53; // [esp+688h] [ebp-10h]
int v54; // [esp+68Ch] [ebp-Ch]
int v55; // [esp+694h] [ebp-4h]
int savedregs; // [esp+698h] [ebp+0h]
sub_EA1235(&unk_EAC017);
system("title Very_easy_Reversing!");
sub_EA123F(v1, v0);
v35 = 31;
v36 = 41;
v37 = 66;
v38 = 15;
v39 = 58;
v40 = 50;
v41 = 40;
v42 = 29;
v43 = 23;
v44 = 49;
v45 = 19;
v46 = 21;
v47 = 71;
v48 = 87;
v49 = 65;
v50 = 69;
v51 = 71;
v52 = 11;
v53 = 31;
v54 = 68;
hSnapshot = j_CreateToolhelp32Snapshot(2u, 0);
GetCurrentProcessId();
dwProcessId = sub_EA123F(v3, v2);
GetCurrentThread();
v32 = sub_EA123F(v5, v4);
OpenProcess(0x2000000u, 1, dwProcessId);
v31 = sub_EA123F(v7, v6);
GetCurrentProcessId();
v30 = sub_EA123F(v9, v8);
j_memset(Dst, 0, 0x104u);
if ( hSnapshot )
{
pe.dwSize = 296;
v26 = j_Process32First(hSnapshot, &pe);
while ( v26 )
{
v26 = j_Process32Next(hSnapshot, &pe);
v29 = j_strlen(pe.szExeFile);
for ( i = 0; i < 0x14; ++i )
pe.szExeFile[i] ^= *(&v35 + 4 * i);
j_memset(Dst, 0, 4u);
v24 = pe.szExeFile;
for ( j = 0; j < 20; ++j )
Dst[j] = v24[j];
FindWindowA(0, Dst);
hWnd = sub_EA123F(v11, v10);
if ( hWnd )
{
GetWindowThreadProcessId(hWnd, &v21);
sub_EA123F(v13, v12);
if ( v21 == v30 )
{
sub_EA104B("Correct\n");
system("pause");
sub_EA123F(v15, v14);
goto LABEL_15;
}
}
}
}
system("pause");
sub_EA123F(v18, v17);
LABEL_15:
sub_EA1262(&savedregs, &dword_EA1B9C, 0, v16);
return sub_EA123F(&savedregs ^ v55, v19);
}
중간에 보면 0x14길이 만큼 테이블값과 xor해주는 연산이 있다.
프로세스 이름과 *(&v35 + 4 * i)이 xor해준다.
table=[0x1f,0x29,0x42,0xf,0x3a,0x32,0x28,0x1d,0x17,0x31,0x13,0x15,0x47,0x57,0x41,0x45,0x47,0xb,0x1f,0x44]
process = "Very_easy_Reversing!"
print ''.join(chr(table[i]^ord(process[i])) for i in range(20))
FLAG : IL0veWInnnAp1236.exe
ROOT_Process_2
위에 변수가 9195개 선언되어있는데 생략했다.
너무 커서 헥스레이가 안돌아가는데 hexrays.cfg를 고쳐서 10000넘게 고쳐주면 헥스레이로 볼 수 있다.
memset(&Dst, 0, 0x44u);
ProcessHandle = 0;
ThreadHandle = 0;
v11 = 0;
v12 = 0;
Context.ContextFlags = 65543;
sub_401020("input : ", savedregs);
v9199 = &v14;
sub_401050("%[^\n]s", &v14);
GetModuleFileNameA(0, &Filename, 0x104u);
CreateProcessA(&Filename, 0, 0, 0, 0, 4u, 0, 0, &Dst, &ProcessHandle);
lpAddress = VirtualAlloc(0, 0x240Au, 0x3000u, 4u);
v9198 = &Src;
v9197 = lpAddress;
memcpy(lpAddress, &Src, 0x2400u);
if ( *lpAddress == 23117 )
{
v6 = lpAddress + lpAddress[15];
NtGetContextThread(ThreadHandle, &Context);
NtReadVirtualMemory(ProcessHandle, (Context.Ebx + 8), &Buffer, 4u, 0);
if ( Buffer == *(v6 + 52) )
NtUnmapViewOfSection(ProcessHandle, Buffer);
v9199 = 64;
v9198 = 12288;
v9197 = *(v6 + 80);
v9196 = *(v6 + 52);
BaseAddress = VirtualAllocEx(ProcessHandle, v9196, v9197, 0x3000u, 0x40u);
if ( BaseAddress )
{
v9199 = 0;
NtWriteVirtualMemory(ProcessHandle, BaseAddress, lpAddress, *(v6 + 84), 0);
for ( i = 0; i < *(v6 + 6); ++i )
{
v2 = (&lpAddress[10 * i + 62] + lpAddress[15]);
v9199 = 0;
NtWriteVirtualMemory(ProcessHandle, &BaseAddress[v2[3]], lpAddress + v2[5], v2[4], 0);
}
Context.Eax = &BaseAddress[*(v6 + 40)];
NtWriteVirtualMemory(ProcessHandle, (Context.Ebx + 8), (v6 + 52), 4u, 0);
NtSetContextThread(ThreadHandle, &Context);
NtResumeThread(ThreadHandle, 0);
NtWaitForSingleObject(ProcessHandle, 0, 0);
NtClose(ThreadHandle);
NtClose(ProcessHandle);
VirtualFree(lpAddress, 0, 0x8000u);
result = 0;
}
else
{
NtTerminateProcess(ProcessHandle, 1);
result = -1;
}
}
else
{
NtTerminateProcess(ProcessHandle, 1);
result = 1;
}
return result;
}
Codegate Open CTF에서도 나온 Process Hollowing
기법이다.
아래 처럼 동작한다. 나는 동적 디버깅해서 win32_remote.exe가 뜨는건데 그 밑에 ROOT_Process2.exe 보면 된다.
Process를 하나 생성해주니까 대충 생성되어 쓴 곳에서 브레이크 걸고 그때 프로세스 pid 가져와서 메모리 덤프 떠주면 된다.
그러면 그 덤프뜬 파일을 보면 제대로 덤프를 떠졌다.
signed int __cdecl sub_401000(int a1, int a2)
{
signed int result; // eax
int v3; // edi
int v4; // esi
char v5; // bl
char v6; // al
int v7; // esi
int v8; // eax
int v9; // edx
char v10[260]; // [esp+4h] [ebp-11Ch]
__int128 v11; // [esp+108h] [ebp-18h]
int v12; // [esp+118h] [ebp-8h]
char v13; // [esp+11Ch] [ebp-4h]
const char **v14; // [esp+128h] [ebp+8h]
srand(1u);
v12 = 139343166;
v13 = 123;
v11 = xmmword_402130;
if ( a1 == 1 )
{
MessageBoxA(0, "Incorrect", "ROOTCTF", 0);
result = -1;
}
else
{
v3 = a1 - 1;
v4 = 0;
if ( a1 - 1 > 0 )
{
v14 = (a2 + 4);
do
{
v5 = rand() % 127;
v6 = atoi(*v14);
++v14;
v10[v4++] = v5 ^ v6;
}
while ( v4 < v3 );
}
v7 = 0;
v8 = 0;
if ( v3 <= 0 )
goto LABEL_15;
do
{
v9 = v7 + 1;
if ( v10[v8] != *(&v11 + v8) )
v9 = v7;
++v8;
v7 = v9;
}
while ( v8 < v3 );
if ( v9 != 21 )
{
LABEL_15:
MessageBoxA(0, "Incorrect", "ROOTCTF", 0);
result = 0;
}
else
{
MessageBoxA(0, "Correct", "ROOTCTF", 0);
result = 0;
}
}
return result;
}
srand(1)이고 rand() % 127 값과 테이블이 xor 연산을 하는 것을 알 수 있다. 쉽게 역연산해서 구할 수 있다.
from ctypes import *
xmmword_402130 = [0x6f,0x78,0x2e,0x13,0x0c,0x35,0x00,0x7a,0x72,0x0f,0x44,0x20,0x62,0x5a,0x54,0x2e,0x3e,0x35,0x4e,0x08,0x7b]
libc = CDLL('msvcrt')
libc.srand(1)
flag = []
for i in range(len(xmmword_402130)):
flag.append(xmmword_402130[i]^(libc.rand()%127))
print ''.join(chr(flag[i]) for i in range(len(xmmword_402130)))
FLAG : FLAG{R0oT_1nJec@t1On}