学破解第83天,《攻防世界reverse新手练习区csaw2013reversing2分析》
前言:
一直对黑客充满了好奇,觉得黑客神秘,强大,无所不能,来论坛两年多了,天天看各位大佬发帖,自己只能做一个伸手党。也看了官方的入门视频教程,奈何自己基础太差,看不懂。自我反思之下,决定从今天(2019年6月17日)开始定下心来,从简单的基础教程开始学习,希望能从照抄照搬,到能独立分析,能独立破解。
不知不觉学习了好几个月,发现自己离了教程什么都不会,不懂算法,不懂编程。随着破解学习的深入,楼主这个半吊子迷失了自我,日渐沉迷水贴装X,不能自拔。
==========申明:从第71天楼主开始水贴装X,帖子不再具有连续性,仅供参考,后续帖子为楼主YY专用贴!!!==========
立帖为证!--------记录学习的点点滴滴
0x1查壳
1.丢进exeinfoPE查壳,Microsoft Visual C++ v.10 - 2010程序。
2.再看一下题目描述:听说运行就能拿到Flag,不过菜鸡运行的结果不知道为什么是乱码。
3.运行一下程序,提示如图!
0x2上IDA
1.将程序丢进IDA,找到main函数,F5转换一下:
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // ecx
CHAR *lpMem; // [esp+8h] [ebp-Ch]
HANDLE hHeap; // [esp+10h] [ebp-4h]
hHeap = HeapCreate(0x40000u, 0, 0);
lpMem = (CHAR *)HeapAlloc(hHeap, 8u, MaxCount + 1);
memcpy_s(lpMem, MaxCount, &unk_409B10, MaxCount);
if ( sub_40102A() || IsDebuggerPresent() )
{
__debugbreak();
sub_401000(v3 + 4, lpMem);
ExitProcess(0xFFFFFFFF);
}
MessageBoxA(0, lpMem + 1, "Flag", 2u);
HeapFree(hHeap, 0, lpMem);
HeapDestroy(hHeap);
ExitProcess(0);
}
2.可以看到main函数比较简单,里面就一个if语句,然后就弹窗内容为 lpMem + 1!
3.接着去百度各个函数的意义:
HeapCreate:创建堆栈
HeapAlloc:在堆上分配内存
memcpy_s:内存拷贝函数
IsDebuggerPresent():检测调试器,成功返回1,失败返回0。
ExitProcess:退出进程
MessageBoxA:弹窗口
HeapFree:释放堆内存
HeapDestroy:销毁堆
4.我们理一下程序的执行流程:
第一步:把409B10这个地址内的数据复制给 lpMem。
第二步:执行if语句,sub_40102A()这个函数里面return 0;再加上我们直接运行程序,IsDebuggerPresent()返回值也是0。所以这个if里面的代码块是一定不会被执行的。
第三步:弹窗,此时弹出来的是乱码!
5. 所以我就可以猜测如果这个if执行,会不会就能输出正确的flag。if里面只有一个函数sub_401000,代码如下:
unsigned int __fastcall sub_401000(int a1, int a2)
{
int v2; // esi
unsigned int v3; // eax
unsigned int v4; // ecx
unsigned int result; // eax
v2 = dword_409B38;
v3 = a2 + 1 + strlen((const char *)(a2 + 1)) + 1;
v4 = 0;
result = ((v3 - (a2 + 2)) >> 2) + 1;
if ( result )
{
do
*(_DWORD *)(a2 + 4 * v4++) ^= v2;
while ( v4 < result );
}
return result;
}
a2是我们最开始的那个lpMem参数。
0x3上OD
1.将程序丢进OD,在左下角数据窗口中搜索一下给lpMem赋值的内容,看到“惶牸苎靖拖井夷珎屹摮赞摡铀競铀竟氉梯”。
2.接下来Ctrl+G定位0040102A 到这个函数,然后查看信息窗口调用来自00401083,再次定位过去,我们看到了:
00401083 . E8 A2FFFFFF call 0453d212.0040102A ;这个call直接返回0
00401088 . 85C0 test eax,eax ;eax不为0则跳转
0040108A . 75 0A jnz short 0453d212.00401096
0040108C . FF15 14604000 call dword ptr ds:[<&KERNEL32.IsDebugger>; [IsDebuggerPresent
00401092 . 85C0 test eax,eax ;eax为0则跳转
00401094 . 74 23 je short 0453d212.004010B9
00401096 > 41 inc ecx
00401097 . 41 inc ecx
00401098 . 41 inc ecx
00401099 . 41 inc ecx
0040109A . CC int3
0040109B . 8B55 F4 mov edx,dword ptr ss:[ebp-0xC] ; kernel32.7C839AD8
0040109E . E8 5DFFFFFF call 0453d212.00401000 /;对初始字符串解密处理
004010A3 . EB 4A jmp short 0453d212.004010EF ;退出程序
004010A5 . 6A 02 push 0x2 ; /Style = MB_ABORTRETRYIGNORE|MB_APPLMODAL
004010A7 . 68 20784000 push 0453d212.00407820 ; |Flag
004010AC . FF75 F4 push dword ptr ss:[ebp-0xC] ; |Text = "U嬱冹SVWU鼖]婨鰼"
004010AF . 6A 00 push 0x0 ; |hOwner = NULL
004010B1 . FF15 E4604000 call dword ptr ds:[<&USER32.MessageBoxA>>; \MessageBoxA
004010B7 . EB 14 jmp short 0453d212.004010CD
004010B9 > 6A 02 push 0x2 ; /Style = MB_ABORTRETRYIGNORE|MB_APPLMODAL
004010BB . 68 20784000 push 0453d212.00407820 ; |Flag
004010C0 . 8B45 F4 mov eax,dword ptr ss:[ebp-0xC] ; |kernel32.7C839AD8
004010C3 . 40 inc eax ; |
004010C4 . 50 push eax ; |Text = 91CB4D30 ???
004010C5 . 6A 00 push 0x0 ; |hOwner = NULL
004010C7 . FF15 E4604000 call dword ptr ds:[<&USER32.MessageBoxA>>; \MessageBoxA
004010CD > FF75 F4 push dword ptr ss:[ebp-0xC] ; /pMemory = kernel32.7C839AD8
004010D0 . 6A 00 push 0x0 ; |Flags = 0
004010D2 . FF75 FC push dword ptr ss:[ebp-0x4] ; |hHeap = NULL
004010D5 . FF15 08604000 call dword ptr ds:[<&KERNEL32.HeapFree>] ; \HeapFree
004010DB . 8945 F8 mov dword ptr ss:[ebp-0x8],eax
004010DE . FF75 FC push dword ptr ss:[ebp-0x4] ; /hHeap = NULL
004010E1 . FF15 0C604000 call dword ptr ds:[<&KERNEL32.HeapDestro>; \HeapDestroy
004010E7 . 6A 00 push 0x0 ; /ExitCode = 0x0
004010E9 . FF15 00604000 call dword ptr ds:[<&KERNEL32.ExitProces>; \ExitProcess
004010EF > 6A FF push -0x1 ; /ExitCode = 0xFFFFFFFF
004010F1 . FF15 00604000 call dword ptr ds:[<&KERNEL32.ExitProces>; \ExitProcess
3.看上面这段代码,我就知道需要在0040108A 或者00401094 这里改为jmp,跳转到0040109B 这里,前面有int3中断,得跳过它执行00401000这个call。
4.我们可以看到执行完这个call后,程序就退出了,所以我们需要跟进这个call,看看代码:
00401000 /$ 56 push esi
00401001 |. 8B35 389B4000 mov esi,dword ptr ds:[0x409B38] ; 华梯
00401007 |. 8D42 01 lea eax,dword ptr ds:[edx+0x1] ;eax存储着解密后的flag
0040100A |. 57 push edi ;eax+1
0040100B |. 8D78 01 lea edi,dword ptr ds:[eax+0x1]
0040100E |> 8A08 /mov cl,byte ptr ds:[eax]
00401010 |. 40 |inc eax
00401011 |. 84C9 |test cl,cl
00401013 |.^ 75 F9 \jnz short 0453d212.0040100E
00401015 |. 2BC7 sub eax,edi
00401017 |. C1E8 02 shr eax,0x2
0040101A |. 33C9 xor ecx,ecx
0040101C |. 40 inc eax
0040101D |. 74 08 je short 0453d212.00401027
0040101F |> 31348A /xor dword ptr ds:[edx+ecx*4],esi
00401022 |. 41 |inc ecx
00401023 |. 3BC8 |cmp ecx,eax
00401025 |.^ 72 F8 \jb short 0453d212.0040101F
00401027 |> 5F pop edi
00401028 |. 5E pop esi
00401029 \. C3 retn
右侧的寄存器edi显示的是008F1E92,edi在eax的值上面加一了,所以在数据窗口中跟随向上翻或者看eax的值就能看到flag。
5.出了这个call之后,就退出程序了,因为上面那段代码有两个MessageBOX,我需要判断出来后jmp需要改跳到哪一个,所以去看看原程序,是从00401094 这里跳转到004010B9 这里弹窗,综上所述,需要修改两处为:
00401094 /74 23 je short 0453d212.0040109B
004010A3 /EB 4A jmp short 0453d212.004010B9
运行程序,弹出正确的flag!
0x4总结
1.IDA和OD结合使用,确定变量的值和程序的流程。
2. 把那个初始字符串加1弹出来的内容和原程序不一样
char arr[] = {0xBB,0xCC,0xA0,0xBC,0xDC,0xD1,0xBE,0xB8,0xCD,0xCF,0xBE,0xAE,0xD2,0xC4,0xAB,0x82,0xD2,
0xD9,0x93,0xB3,0xD4,0xDE,0x93,0xA9,0xD3,0xCB,0xB8,0x82,0xD3,0xCB,0xBE,0xB99A,0xD7,0xCC,0xDD,'\0'};
MessageBoxA(0, arr + 1, "Flag", 2u);
3.调用00401000时传入的两个参数不太明白,v3+4看不懂,后面一个传的好像是初始字符串。
4.00401000函数里面的那段解密过程也看不懂,希望有大佬不良赐教!