Brutal Oldskull
首先看到程序需要输入4个code和最终flag
首先通过查找字符串定位到关键函数:sub_4017D5()
void sub_4017D5()
{
char *v0; // eax
CHAR v1; // [esp+30h] [ebp-498h]
unsigned int v2; // [esp+130h] [ebp-398h]
CHAR String[4]; // [esp+134h] [ebp-394h]
int v4; // [esp+138h] [ebp-390h]
int v5; // [esp+13Ch] [ebp-38Ch]
int v6; // [esp+140h] [ebp-388h]
DWORD ExitCode; // [esp+144h] [ebp-384h]
CHAR v8; // [esp+148h] [ebp-380h]
struct _PROCESS_INFORMATION ProcessInformation; // [esp+248h] [ebp-280h]
struct _STARTUPINFOA StartupInfo; // [esp+258h] [ebp-270h]
char Buffer[512]; // [esp+29Ch] [ebp-22Ch]
unsigned __int16 v12; // [esp+49Ch] [ebp-2Ch]
unsigned __int16 v13; // [esp+49Eh] [ebp-2Ah]
unsigned __int16 v14; // [esp+4A0h] [ebp-28h]
unsigned __int16 v15; // [esp+4A2h] [ebp-26h]
BOOL v16; // [esp+4A4h] [ebp-24h]
FILE *v17; // [esp+4A8h] [ebp-20h]
void *v18; // [esp+4ACh] [ebp-1Ch]
void *v19; // [esp+4B0h] [ebp-18h]
void *v20; // [esp+4B4h] [ebp-14h]
void *v21; // [esp+4B8h] [ebp-10h]
int i; // [esp+4BCh] [ebp-Ch]
sub_4016C2("Processing...");
for ( i = 0; i <= 3; ++i )
{
*(_DWORD *)String = 0;
v4 = 0;
v5 = 0;
v6 = 0;
GetWindowTextA(*(&dword_40C034 + i), String, 8);
if ( sscanf(String, "%x", &v2) != 1 )
{
sprintf(&v1, "Incorrect Code %i format (16-bit HEX)", i + 1);
sub_4016C2(&v1);
return;
}
if ( v2 > 0xFFFF )
{
sub_4016C2("Incorrect Code.");
return;
}
*(&v12 + i) = v2;
}
v21 = sub_4016E4((int)&unk_405020, 0x4C8Eu, v12);
if ( v21 )
{
v20 = sub_4016E4((int)v21, 0x4C6Eu, v13);
if ( v20 )
{
v19 = sub_4016E4((int)v20, 0x4C4Eu, v14);
if ( v19 )
{
v18 = sub_4016E4((int)v19, 0x4C2Eu, v15);
if ( v18 )
{
free(v21);
free(v20);
free(v19);
memset(Buffer, 0, sizeof(Buffer));
GetTempPathA(0x100u, Buffer);
v0 = &Buffer[strlen(Buffer)];
*(_DWORD *)v0 = 'dlo\\';
*((_DWORD *)v0 + 1) = 'luks';
*((_DWORD *)v0 + 2) = 'hc_l';
*((_DWORD *)v0 + 3) = 'ekce';
*((_DWORD *)v0 + 4) = 'xe.r';
*((_WORD *)v0 + 10) = 'e';
v17 = fopen(Buffer, "wb");
if ( v17 )
{
fwrite(v18, 1u, 0x4C0Eu, v17);
fclose(v17);
free(v18);
memset(&StartupInfo, 0, sizeof(StartupInfo));
StartupInfo.cb = 68;
ProcessInformation.hProcess = 0;
ProcessInformation.hThread = 0;
ProcessInformation.dwProcessId = 0;
ProcessInformation.dwThreadId = 0;
memset(&v8, 0, 0x100u);
GetWindowTextA(dword_40C044, &v8, 64);
*(_WORD *)&Buffer[strlen(Buffer)] = 32;
strcat(Buffer, &v8);
v16 = CreateProcessA(0, Buffer, 0, 0, 0, 0x8000000u, 0, 0, &StartupInfo, &ProcessInformation);
if ( v16 )
{
if ( WaitForSingleObject(ProcessInformation.hProcess, 0x7D0u) != 0 )
{
TerminateProcess(ProcessInformation.hProcess, 9u);
CloseHandle(ProcessInformation.hProcess);
CloseHandle(ProcessInformation.hThread);
sub_4016C2("Checker crashed. Sorry.");
}
else
{
ExitCode = -1;
v16 = GetExitCodeProcess(ProcessInformation.hProcess, &ExitCode);
if ( v16 )
{
if ( ExitCode )
sub_4016C2("Wrong Flag.");
else
sub_4016C2("Well Done! But you know that :)");
CloseHandle(ProcessInformation.hProcess);
CloseHandle(ProcessInformation.hThread);
}
else
{
CloseHandle(ProcessInformation.hProcess);
CloseHandle(ProcessInformation.hThread);
sub_4016C2("Checker failed. Sorry.");
}
}
}
else
{
sub_4016C2("Couldn't spawn checker");
}
}
else
{
sub_4016C2("Couldn't write the checker file.");
free(v18);
}
}
else
{
sub_4016C2("Wrong Code 4.");
free(v21);
free(v20);
free(v19);
}
}
else
{
sub_4016C2("Wrong Code 3.");
free(v21);
free(v20);
}
}
else
{
sub_4016C2("Wrong Code 2.");
free(v21);
}
}
else
{
sub_4016C2("Wrong Code 1.");
}
}
首先注意到:
.text:00401A7A mov dword ptr [eax], 'dlo\'
.text:00401A80 mov dword ptr [eax+4], 'luks'
.text:00401A87 mov dword ptr [eax+8], 'hc_l'
.text:00401A8E mov dword ptr [eax+0Ch], 'ekce'
.text:00401A95 mov dword ptr [eax+10h], 'xe.r'
.text:00401A9C mov word ptr [eax+14h], 'e'
.text:00401AA2 mov dword ptr [esp+4], offset aWb ; "wb"
.text:00401AAA lea eax, [ebp+Buffer]
.text:00401AB0 mov [esp], eax ; char *
.text:00401AB3 call fopen
程序在此处创建了可执行文件oldskull_checker.exe
同时分析关键的判断函数sub_4016E4:
void *__cdecl sub_4016E4(int encode_data, size_t size, int input_code)
{
__int16 _input_code; // [esp+1Ch] [ebp-3Ch]
int v5; // [esp+20h] [ebp-38h]
void *decode_data; // [esp+48h] [ebp-10h]
size_t i; // [esp+4Ch] [ebp-Ch]
_input_code = input_code;
decode_data = malloc(size);
memset(decode_data, 0, size);
for ( i = 0; i < size; ++i )
{
*((_BYTE *)decode_data + i) = (*(_BYTE *)(encode_data + i) ^ _input_code) - HIBYTE(_input_code);
_input_code *= 0x62F3;
}
memset(&v5, 0, 0x28u);
md5_func(decode_data, size - 32, (int)&v5);
if ( !memcmp(&v5, (char *)decode_data + size - 32, 0x20u) )
return decode_data;
free(decode_data);
return 0;
}
通过md5_func中调用的sub_403579函数中预定义的数组:
a1[2] = 0x67452301;
a1[3] = 0xEFCDAB89;
a1[4] = 0x98BADCFE;
a1[5] = 0x10325476;
判断这里是标准的md5 function
可以看到sub_4016E4函数的流程:
传入预先在data段定义的加密过的长为size的encode_data
利用我们输入的input_code对其进行解密
而后对解密后的decode_data前部分decode_data[:-32]进行md5加密
加密后产生的hash字符串存到栈中
而后将栈中hash字符串与decode_data[-32:]比较
以此进行四次循环对预定义数据连续四次解密
我们需要使得每次data经input_code解密后:
md5(decode_data[:-32])=decode_data[-32:]
PS:看到md5_func中:
int __cdecl md5_func(void *a1, size_t a2, int a3)
{
int result; // eax
unsigned __int8 v4[16]; // [esp+14h] [ebp-B4h]
int v5; // [esp+24h] [ebp-A4h]
int i; // [esp+BCh] [ebp-Ch]
sub_403579(&v5);
sub_4035BA((int)&v5, a1, a2);
result = sub_4036F1((int)v4, &v5);
for ( i = 0; i <= 15; ++i )
result = sprintf((char *)(2 * i + a3), "%.2x", v4[i]);
return result;
}
最后存储形式为%.2x,即32位md5值,每一位转ascii,最终32位以string形式存储(类似在内存中进行一次hex编码,"\x11"->"\x31\x31")
若四个code正确,预先定义的data段中的encode_data便经四次正确解密后写入oldskull_checker.exe中,而后程序调用oldskull_checker.exe判断最终flag
由此我们需要先获得4个正确code:
这里可以通过爆破获得:
import hashlib
file=open(r"","rb")
file.seek(0x4020)
str_data=file.read(0x4c8e)
data=[]
for i in str_data:
data.append(ord(i))
key1=[0]*0x4c8e
ans=""
for i in range(0x10000):
l=i
print i
for j in range(0x4c8e):
key1[j]=(data[j]^(l&0x00ff))-(l>>8)
l*=0x62f3
l=l&0xffff
key1[j]=(key1[j]+0x100 if key1[j]<0 else key1[j])
ans="".join(chr(k) for k in key1)
hash=hashlib.md5(ans[:-32]).hexdigest()
if hash==ans[-32:]:
print hex(i)
break
file.close()
//此处爆破code1,爆破后更新data而以此类推爆破出code2、3、4
虽然这里也很快,不过感觉可以优化一下
四次解密每次产生的最后32字节每一位都在16个字符"0123456789abcdef"中(md5->%.2x->16进制)
所以我们可以只解密后面的字节,判断是否为md5的hash字符串,以此缩小范围,不过这里比较简单,判断后每次只有一个解,而后利用此解爆破一下中间不断累乘的数值即可,最终几秒即可得出4个code:
def check(l):
for i in l:
if chr(i) not in key:
return 0
return 1
file=open(r"","rb")
file.seek(0x4020)
key="0123456789abcdef"
str_data=file.read(0x4c8e)
data=[]
for i in str_data:
data.append(ord(i))
key1=[0]*0x4c8e
ans=[]
final_ans=[]
for n in range(4):
for i in range(0x10000):
l=i
for j in range(32*(4-n)):
key1[0x4c8e-32*4+j]=(data[0x4c8e-32*4+j]^(l&0x00ff))-(l>>8)
l*=0x62f3
l=l&0xffff
key1[0x4c8e-32*4+j]=(key1[0x4c8e-32*4+j]+0x100 if key1[0x4c8e-32*4+j]<0 else key1[0x4c8e-32*4+j])
if check(key1[0x4c8e-32*(n+1):0x4c8e-32*n])==1:
ans.append(i)
for q in range(32*4):
data[0x4c8e-32*4+q]=key1[0x4c8e-32*4+q]
break
for n in range(4):
for i in range(0x10000):
if (pow(0x62f3,0x4c8e-32*4,0x10000)*i)&0xffff==ans[n]:
final_ans.append(str(n+1)+"->"+hex(i))
print final_ans
file.close()
得到4个code后
动态调试时通过fopen的文件名参数找到程序写入磁盘的最终check flag的oldskull_checker.exe
ida载入后看到oldskull_checker.exe中的main:
int __cdecl main(int argc, const char **argv, const char **envp)
{
const char *v4; // [esp+18h] [ebp-8h]
unsigned int i; // [esp+1Ch] [ebp-4h]
sub_402B90();
if ( argc != 2 )
return 1;
v4 = argv[1];
if ( strlen(argv[1]) != 20 )
return 2;
for ( i = 0; i <= 0x13; ++i )
{
if ( v4[i] != (byte_404008[i] ^ 0x8F) )
return 3;
}
return 0;
}
很简单的异或加密,提取出数据再异或0x8F即得最终flag:
key=[0xCB, 0xFD, 0xE8, 0xE1, 0xDC, 0xF4, 0xD8, 0xEE, 0xEE, 0xEE,0xF6, 0xDB, 0xE0, 0xE0, 0xCA, 0xD5, 0xAE, 0xAE, 0xBE, 0xF2]
flag=""
for i in key:
flag+=chr(i^0x8f)
print flag
网友评论