week3_RE1

惊闻神船病痛噩耗,于佳节喜庆之时送还产地,静待康复。谁料假日竞赛层出不穷,皆大神你追我赶,愚独自一人苟于家中,愁思重重,忧深难忘,苦于无道以与同门兄妹修炼。俶尔喜得PC,乃借此不可多求之时日做题一道,题笔记于此。

拿到bin载入dbg发现动态基地址,使用CFF_EXPLORER去除OPTIONAL_HEADER->DllCharacteristics中0x40标志(DLL can move)方便调试。

用IDA分析,WinMain中得到 WndClass.lpfnWndProc = sub_4014C0; 进入sub_4014C0,略加分析得知主窗体会将一定数量的按键记录到一个mem(暂且命名为no)中:

if ( dword_4043B8 < 32 )
        no[dword_4043B8] = wParam;
继续分析,按下回车键(wParam == 13)时触发条件判断:
if ( sub_401000() == 1 )
          MessageBoxA(0, "Ashen one, hearest thou my voice, still?", "Firekeeper: ", 0);
        else
          MessageBoxA(0, "Ashen one, what is wrong?", "Firekeeper: ", 0);
查看sub_401000,知所需数据长度应为13(不包括enter)。 整理并分析函数得到no数组前9位算法: no[0]=0x56 xor 0x13='E' no[2]=0x23 xor 0x69='J' no[4]=0x53 xor 0x64='7' no[6]=0x61 xor 0x37='V' no[8]=0x72 xor 0x1c='n' 奇数位由一组keys亦或相减可求出:
((no[v3] + no[v3 + 1]) ^ 0x76) == keys[v5]
//v5=0-3
//keys[0-3]=0xce,0x11,0xc3,0x92
得前九位: E n J 0 7 _ V v n

接着分析余下四位,看见奇妙的运算后懒癌发作,开始进行爆破尝试。

c++编译一个test函数,函数如下:

char code[4] = { 0 };
void test()
{
    _asm {
        mov eax, eax
    }
    for (code[0] = 0x21; code[0] < 0x7f; code[0]++)
        for (code[1] = 0x21; code[1] < 0x7f; code[1]++)
            for (code[2] = 0x21; code[2] < 0x7f; code[2]++)
                for (code[3] = 0x21; code[3] < 0x7f; code[3]++)
                {
                    printf("test");
                    _asm {
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                        mov eax, eax;
                    }
                }
}
mov eax,eax指令的作用其一是方便定位,其二是为自行添加的代码留下空间(虽然后来并没有用到这么多空间)。

x32dbg载入,定位到test函数,copy出整个函数段:

push ebp
mov ebp,esp
sub esp,C0
push ebx
push esi
push edi
lea edi,dword ptr ss:[ebp-C0]
mov ecx,30
mov eax,CCCCCCCC
rep stosd dword ptr es:[edi],eax
mov eax,eax
mov eax,1
imul ecx,eax,0
mov byte ptr ds:[ecx+<code>],21
jmp consoleapplication1.381740
mov eax,1
imul ecx,eax,0
mov dl,byte ptr ds:[ecx+<code>]
add dl,1
mov eax,1
imul ecx,eax,0
mov byte ptr ds:[ecx+<code>],dl
mov eax,1
imul ecx,eax,0
movsx edx,byte ptr ds:[ecx+<code>]
cmp edx,7F
jge consoleapplication1.3818A5
mov eax,1
shl eax,0
mov byte ptr ds:[eax+<code>],21
jmp consoleapplication1.381788
mov eax,1
shl eax,0
mov cl,byte ptr ds:[eax+<code>]
add cl,1
mov edx,1
shl edx,0
mov byte ptr ds:[edx+<code>],cl
mov eax,1
shl eax,0
movsx ecx,byte ptr ds:[eax+<code>]
cmp ecx,7F
jge consoleapplication1.3818A0
mov eax,1
shl eax,1
mov byte ptr ds:[eax+<code>],21
jmp consoleapplication1.3817CD
mov eax,1
shl eax,1
mov cl,byte ptr ds:[eax+<code>]
add cl,1
mov edx,1
shl edx,1
mov byte ptr ds:[edx+<code>],cl
mov eax,1
shl eax,1
movsx ecx,byte ptr ds:[eax+<code>]
cmp ecx,7F
jge consoleapplication1.38189B
mov eax,1
imul ecx,eax,3
mov byte ptr ds:[ecx+<code>],21
jmp consoleapplication1.381814
mov eax,1
imul ecx,eax,3
mov dl,byte ptr ds:[ecx+<code>]
add dl,1
mov eax,1
imul ecx,eax,3
mov byte ptr ds:[ecx+<code>],dl
mov eax,1
imul ecx,eax,3
movsx edx,byte ptr ds:[ecx+<code>]
cmp edx,7F
jge consoleapplication1.381896
push consoleapplication1.386B30
call <[email protected]+800(_printf)>
add esp,4
mov eax,eax
...
mov eax,eax
jmp consoleapplication1.3817F5
jmp consoleapplication1.3817B0
jmp consoleapplication1.381769
jmp consoleapplication1.381721
pop edi
pop esi
pop ebx
add esp,C0
cmp ebp,esp
call <[email protected]+270(__RTC_CheckEsp)>
mov esp,ebp
pop ebp
ret
将code地址替换为no[10]地址4043A1并稍作修改:
push ebp
mov ebp,esp
sub esp,C0
push ebx
push esi
push edi
lea edi,dword ptr ss:[ebp-C0]
mov ecx,30
mov eax,CCCCCCCC
rep stosd dword ptr es:[edi],eax
mov eax,eax
mov eax,1
imul ecx,eax,0
mov byte ptr ds:[ecx+4043A1],21
jmp waifu2.402732
mov eax,1
imul ecx,eax,0
mov dl,byte ptr ds:[ecx+4043A1]
add dl,1
mov eax,1
imul ecx,eax,0
mov byte ptr ds:[ecx+4043A1],dl
mov eax,1
imul ecx,eax,0
movsx edx,byte ptr ds:[ecx+4043A1]
cmp edx,7F
jge waifu2.402897
mov eax,1
shl eax,0
mov byte ptr ds:[eax+4043A1],21
jmp waifu2.40277A
mov eax,1
shl eax,0
mov cl,byte ptr ds:[eax+4043A1]
add cl,1
mov edx,1
shl edx,0
mov byte ptr ds:[edx+4043A1],cl
mov eax,1
shl eax,0
movsx ecx,byte ptr ds:[eax+4043A1]
cmp ecx,7F
jge waifu2.402892
mov eax,1
shl eax,1
mov byte ptr ds:[eax+4043A1],21
jmp waifu2.4027BF
mov eax,1
shl eax,1
mov cl,byte ptr ds:[eax+4043A1]
add cl,1
mov edx,1
shl edx,1
mov byte ptr ds:[edx+4043A1],cl
mov eax,1
shl eax,1
movsx ecx,byte ptr ds:[eax+4043A1]
cmp ecx,7F
jge waifu2.40288D
mov eax,1
imul ecx,eax,3
mov byte ptr ds:[ecx+4043A1],21
jmp waifu2.402806
mov eax,1
imul ecx,eax,3
mov dl,byte ptr ds:[ecx+4043A1]
add dl,1
mov eax,1
imul ecx,eax,3
mov byte ptr ds:[ecx+4043A1],dl
mov eax,1
imul ecx,eax,3
movsx edx,byte ptr ds:[ecx+4043A1]
cmp edx,7F
jge waifu2.402888
pushal //保存寄存器
pushfd //保存EFLAGS
call <waifu2.sub_401000>    //魔幻调用sub_401000
cmp eax,0   //判断返回值
jne success
popfd //恢复环境
popal 
jmp fail
nop 
nop 
nop 
nop //success
nop //fail
nop 
nop 
mov eax,eax
...
mov eax,eax
jmp waifu2.4027E7
jmp waifu2.4027A2
jmp waifu2.40275B
jmp waifu2.402713
pop edi
pop esi
pop ebx
add esp,C0
cmp ebp,esp
nop     //nop checkesp
nop 
nop 
nop 
nop 
mov esp,ebp
pop ebp
ret 

//hex:
//558BEC81ECC00000005356578DBD40FFFFFFB930000000B8CCCCCCCCF3AB8BC0B8010000006BC800C681A143400021EB1FB8010000006BC8008A91A143400080C201B8010000006BC8008891A1434000B8010000006BC8000FBE91A143400083FA7F0F8D4D010000B801000000C1E000C680A143400021EB1FB801000000C1E0008A88A143400080C101BA01000000C1E200888AA1434000B801000000C1E0000FBE88A143400083F97F0F8D00010000B801000000D1E0C680A143400021EB1DB801000000D1E08A88A143400080C101BA01000000D1E2888AA1434000B801000000D1E00FBE88A143400083F97F0F8DB7000000B8010000006BC803C681A143400021EB1FB8010000006BC8038A91A143400080C201B8010000006BC8038891A1434000B8010000006BC8030FBE91A143400083FA7F7D6E609CE8DFE7FFFF83F80075119D61EB0F909090909090908BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC08BC0E95FFFFFFFE915FFFFFFE9C9FEFFFFE97CFEFFFF5F5E5B81C4C00000003BEC90909090908BE55DC3
将这段函数写在0x4026e2处(源程序内存由于需要对齐才空出了空间,因此写在此处无法保存到PE文件中,如需保存可以新建一个节区),在401000/success处下断点,主窗体输入EnJ07_Vvnabcd[enter]后断下,将eip设置为0x4026e2后开始跑,在success处断下,此时查看no内存就能得到flag了。

后四位的具体算法可能会采用writeup学习法,但可能会懒到水过去=。=

OriginalFile: Waifu.7z

后续补充: 后四位官方就是爆破的