We are given an executable for the Squirtle challenge. When we run the binary we see some ascii art of Squirtle and a check for a password. If we get the password wrong, we sadly find out that we just killed a Squirtle and the program exits.

Dead Squirtle from an Incorrect Password

We can open the binary in Binary Ninja and take a look at the main function. If we look at the first branch instruction, there is a function call right before at 401070 that checks the password. We can see that it is just a string compare with the string “incorrect”.

Password Check Function

If we type the password correctly we happily find out that we didn’t kill a Squirtle and we get some more output. We have to pass some anti-debugging and anti-vm checks and then we are told that the answer is written in an answer.jpg file.

Correct Password Output

There is an answer.jpg file written after we ran the program, but it is corrupted so we need to figure out how to make the program write it correctly to disk. We can see at the end of the main function there is a loop with a multibyte xor key.

Xor Loop Writing the answer.jpg file

We can assume that if we pass each step we will get the correct key that will output the correct image. At each stage there is a check and then some fake rand() == rand() checks with some funny messages to obfuscate the code. Thankfully there are also helpful hints at each stage if we get stuck or are unsure of the correct path.

Sleep/GetTickCount Check along with fake rand checks

The first check is to see if there is a common debugger window class found.

BOOL wdw_class()
	HWND hOlly = FindWindow(_T("OLLYDBG"), NULL);
	HWND hIDA = FindWindow(_T("QWidget"), NULL);
	HWND hWdbg = FindWindow(_T("WinDbgFrameClass"), NULL);
	HWND hImm = FindWindow(_T("ID"), NULL);

	if ((hOlly) || (hIDA) || (hWdbg) || (hImm))
		return TRUE;

	return FALSE;

The second check is to look at the Process Environment Block at offset fs: [30h+2] to see if the process is being debugged.

BOOL fs_chk(VOID)
	char IsDbgPresent = 0;

	__asm {
		mov eax, fs:[30h]
			mov al, [eax + 2h]
			mov IsDbgPresent, al
	if (IsDbgPresent)
		return TRUE;
	return FALSE;

The third check uses the Windows API GetTickCount() to make sure the system hasn’t been freshly booted.

DWORD Counter = GetTickCount();
if (Counter < 0xFFFFF) {

The fourth check used Sleep along with GetTickCount() and wanted you to bypass the sleep call.

DWORD Counter2 = GetTickCount();
Counter2 -= Counter;
if (Counter2 &gt; 0xFF)

The fifth check just used the Windows API IsDebuggerPresent to find out if the process is being debugged. The sixth check similarly called the Windows API CheckRemoteDebuggerPresent to find out if the process was being debugged.

The seventh stage checked to see if there are greater than 2 cpus.

BOOL cpu_num()
	if (siSysInfo.dwNumberOfProcessors < 2)
		return TRUE;
	return FALSE;

The eighth stage checked to see if there were more than 1024 GB of RAM.

	statex.dwLength = sizeof(statex);

	if ((statex.ullTotalPhys / 1024) < 1048576)

The final check looked to see if the CPU hypervisor bit was set.

BOOL hv_bit(VOID)
	int CPUInfo[4] = { -1 };

	__cpuid(CPUInfo, 1);
	if ((CPUInfo[2] >> 31) & 1)
		return TRUE;

	return FALSE;

We can step through the program in a binary and make sure the correct path is taken. Then we will get the correct image.

Graph Trace of the Correct Path

We could also get the correct key at each stage and grab the buffer of the xor’d image and decrypt it with python.

def xor(data, key):
    l = len(key)
    return bytearray((
        (data[i] ^ key[i % l]) for i in range(0,len(data))

data = bytearray(open('xor.jpg', 'rb').read())

key = 

with open('unxor.jpg', "w") as f:
f.write(xor(data, key))

Correct Image

We can finally decode the binary and obtain the key (Sorry ).