学破解第129天,《攻防世界reverse练习区key》学习
前言:
从小学到大专(计算机网络技术专业),玩过去的,所以学习成绩惨不忍睹,什么证书也没考,直到找不到工作才后悔,不知道怎么办才好。
2017年12月16日,通过19元注册码注册论坛账号,开始做伸手党,潜水一年多,上来就是找软件。(拿论坛高大上的软件出去装X)
2018年8月某一天,报名了华中科技大学网络教育本科(计算机科学与技术专业)2018级秋季。(开始提升学历)
2019年6月17日,不愿再做小菜鸟一枚,开始零基础学习破解。(感谢小糊涂虫大哥在我刚开始学习脱壳时,录制视频解答我的问题)
2020年7月7日,感谢H大对我的鼓励,拥有了第一篇获得优秀的文章。(接下来希望学习逆向,逆天改命)
坛友们,年轻就是资本,和我一起逆天改命吧,我的学习过程全部记录及学习资源:https://www.52pojie.cn/thread-1278021-1-1.html
立帖为证!--------记录学习的点点滴滴
0x1下载软件观察行为
1.去攻防世界找到这个key题目,下载文件,PEID查壳,Microsoft Visual C++程序
2.运行程序看一看,直接黑窗口一闪而过,会自动退出,好像看不出什么?
0x2动静结合调试分析
1.既然会退出,那就找原因,丢到OD里面下一个exitprocess断点,停在了:
0031F7FC 7079F4ED /CALL 到 ExitProcess 来自 ucrtbase.7079F4E7
0031F800 FFFFFFFF \ExitCode = 0xFFFFFFFF
0031F804 00000000
0031F808 7079F474 返回到 ucrtbase.7079F474 来自 ucrtbase.7079F4B4
不知道怎么回溯到用户代码,可能经过多层调用,换成exit断点试试
00C3123E |. /74 25 je short key.00C31265
00C31240 |. |8B0D C850C300 mov ecx,dword ptr ds:[<&MSVCP140.std::ce>; msvcp140.std::cerr
00C31246 |. |BA E452C300 mov edx,key.00C352E4 ; ?W?h?a?t h?a?p?p?e?n?
00C3124B |. |68 502CC300 push key.00C32C50
00C31250 |. |E8 AB170000 call key.00C32A00
00C31255 |. |8BC8 mov ecx,eax ; msvcp140.std::cerr
00C31257 |. |FF15 9850C300 call dword ptr ds:[<&MSVCP140.std::basic>; msvcp140.std::basic_ostream<wchar_t,std::char_traits<wchar_t> >::operator<<
00C3125D |. |6A FF push -0x1 ; /status = FFFFFFFF (-1.)
00C3125F |. |FF15 6851C300 call dword ptr ds:[<&api-ms-win-crt-runt>; \exit
00C31265 |> \8D55 AC lea edx,[local.21]
往上翻,记下段首00C31100
2.看退出函数的返回后的用户代码段首是00C31100 ,用IDA F5反编译一下401100这个函数,看,主要用户代码都出来了,省时省力。
int sub_401100()
{
signed int v0; // esi@1
signed int v1; // esi@3
unsigned int v2; // edi@3
void **v3; // ebx@3
void **v4; // eax@4
int v5; // ecx@7
int v6; // ST04_4@7
int v7; // ST08_4@7
int v8; // ST0C_4@7
int v9; // eax@8
char *v10; // esi@10
int v11; // ecx@16
void **v12; // eax@18
int v13; // eax@20
int v14; // ecx@20
int v15; // eax@21
int v16; // eax@21
int v17; // eax@21
int v18; // eax@21
int v19; // eax@21
int v20; // eax@21
int v21; // eax@21
const char *v22; // edx@21
int v23; // eax@23
int result; // eax@27
int (__cdecl *v25)(int); // [sp-4h] [bp-13Ch]@20
int Dst; // [sp+14h] [bp-124h]@7
char v27[4]; // [sp+20h] [bp-118h]@7
char v28; // [sp+24h] [bp-114h]@10
int v29; // [sp+5Ch] [bp-DCh]@16
char v30; // [sp+61h] [bp-D7h]@16
int v31; // [sp+64h] [bp-D4h]@16
int v32; // [sp+68h] [bp-D0h]@16
char v33; // [sp+6Ch] [bp-CCh]@16
FILE *File; // [sp+70h] [bp-C8h]@10
char v35; // [sp+84h] [bp-B4h]@23
void *v36; // [sp+CCh] [bp-6Ch]@1
int v37; // [sp+DCh] [bp-5Ch]@1
unsigned int v38; // [sp+E0h] [bp-58h]@1
void *v39; // [sp+E4h] [bp-54h]@1
int v40; // [sp+F4h] [bp-44h]@1
unsigned int v41; // [sp+F8h] [bp-40h]@1
void *Memory[4]; // [sp+FCh] [bp-3Ch]@1
int v43; // [sp+10Ch] [bp-2Ch]@1
unsigned int v44; // [sp+110h] [bp-28h]@1
__int128 v45; // [sp+114h] [bp-24h]@1
__int16 v46; // [sp+124h] [bp-14h]@1
char v47; // [sp+126h] [bp-12h]@1
int v48; // [sp+134h] [bp-4h]@1
v41 = 15;
v40 = 0;
LOBYTE(v39) = 0;
v48 = 0;
v38 = 15;
v37 = 0;
LOBYTE(v36) = 0;
LOBYTE(v48) = 1;
v0 = 0;
v43 = 1684630885;
LOWORD(v44) = 97;
*(_OWORD *)Memory = xmmword_40528C;
v46 = 11836;
v47 = 0;
v45 = xmmword_4052A4;
do
{
sub_4021E0(1u, (*((_BYTE *)Memory + v0) ^ *((_BYTE *)&v45 + v0)) + 22);
++v0;
}
while ( v0 < 18 );
v1 = 0;
v44 = 15;
v43 = 0;
LOBYTE(Memory[0]) = 0;
LOBYTE(v48) = 2;
v2 = v38;
v3 = (void **)v36;
do
{
v4 = &v36;
if ( v2 >= 0x10 )
v4 = v3;
sub_4021E0(1u, *((_BYTE *)v4 + v1++) + 9);
}
while ( v1 < 18 );
memset(&Dst, 0, 0xB8u);
sub_401620(v5, v6, v7, v8);
LOBYTE(v48) = 3;
if ( v27[*(_DWORD *)(Dst + 4)] & 6 )
{
v9 = sub_402A00(std::cerr, "?W?h?a?t h?a?p?p?e?n?");
std::basic_ostream<char,std::char_traits<char>>::operator<<(v9, sub_402C50);
exit(-1);
}
sub_402E90(&Dst, &v39);
v10 = &v28;
if ( File )
{
if ( !(unsigned __int8)sub_4022F0(&v28) )
v10 = 0;
if ( fclose(File) )
v10 = 0;
}
else
{
v10 = 0;
}
v33 = 0;
v30 = 0;
std::basic_streambuf<char,std::char_traits<char>>::_Init(&v28);
v31 = dword_408590;
File = 0;
v32 = dword_408594;
v29 = 0;
if ( !v10 )
std::basic_ios<char,std::char_traits<char>>::setstate((char *)&Dst + *(_DWORD *)(Dst + 4), 2, 0);
v12 = Memory;
if ( v44 >= 0x10 )
v12 = (void **)Memory[0];
v13 = sub_4020C0(v11, v40, v12, v43);
v14 = std::cout;
v25 = sub_402C50;
if ( v13 )
{
v22 = "=W=r=o=n=g=K=e=y=";
}
else
{
v15 = sub_402A00(std::cout, "|------------------------------|");
std::basic_ostream<char,std::char_traits<char>>::operator<<(v15, sub_402C50);
v16 = sub_402A00(std::cout, "|==============================|");
std::basic_ostream<char,std::char_traits<char>>::operator<<(v16, sub_402C50);
v17 = sub_402A00(std::cout, "|==============================|");
std::basic_ostream<char,std::char_traits<char>>::operator<<(v17, sub_402C50);
v18 = sub_402A00(std::cout, "|==============================|");
std::basic_ostream<char,std::char_traits<char>>::operator<<(v18, sub_402C50);
v19 = sub_402A00(std::cout, "\\ /\\ /\\ /\\ /\\==============|");
std::basic_ostream<char,std::char_traits<char>>::operator<<(v19, sub_402C50);
v20 = sub_402A00(std::cout, " \\/ \\/ \\/ \\/ \\=============|");
std::basic_ostream<char,std::char_traits<char>>::operator<<(v20, sub_402C50);
v21 = sub_402A00(std::cout, " |-------------|");
std::basic_ostream<char,std::char_traits<char>>::operator<<(v21, sub_402C50);
std::basic_ostream<char,std::char_traits<char>>::operator<<(std::cout, sub_402C50);
v14 = std::cout;
v22 = "Congrats You got it!";
v25 = sub_402C50;
}
v23 = sub_402A00(v14, v22);
std::basic_ostream<char,std::char_traits<char>>::operator<<(v23, v25);
sub_401570(&v35);
std::basic_ios<char,std::char_traits<char>>::~basic_ios<char,std::char_traits<char>>(&v35);
if ( v44 >= 0x10 )
sub_402630(Memory[0], v44 + 1);
if ( v2 >= 0x10 )
sub_402630(v3, v2 + 1);
result = v41;
if ( v41 >= 0x10 )
result = sub_402630(v39, v41 + 1);
return result;
}
3.代码是有了,但是还是没办法看出为什么退出了,搜一下可疑字符串,看到了:
一个文件路径比较可疑,点开看看,有一个API函数在尝试打开这个文件。
01052583 |. 6A 40 push 0x40
01052585 |. 6A 01 push 0x1
01052587 |. 68 B8520501 push key.010552B8 ; C:\Users\CSAW2016\haha\flag_dir\flag.txt
0105258C |. FF15 BC500501 call dword ptr ds:[<&MSVCP140.std::_Fiopen>] ; msvcp140.std::_Fiopen
4.那我就去对应目录创建一个对应的文件,内容随便写一个52pojie试试,打开cmd窗口,输入:
mkdir C:\Users\CSAW2016\haha\flag_dir\
echo 52pojie>C:\Users\CSAW2016\haha\flag_dir\flag.txt
然后可以看到跳过了0004126E 这个退出函数,然后再看IDA中的代码,v13为1,就提示错误的key,可以看到最近的地方是000420C0影响了v13的值。
5.OD下断点跑过来一看:
00041317 |. FF75 D4 push [local.11] ; 0x12
0004131A |. 0F4345 C4 cmovnb eax,[local.15]
0004131E |. 50 push eax ; idg_cni~bjbfi|gsxb
0004131F |. FF75 BC push [local.17] ; 0x7
00041322 |. 51 push ecx ; 52pojie
00041323 |. 8D4D AC lea ecx,[local.21]
00041326 |. E8 950D0000 call key.000420C0
咦,为什么IDA里面会有5个参数看一下thiscall函数调用约定,从右往左传递,第一个参数是this指针。
6.又是一段很长的代码,我们记住自己的目的就好,返回值必须为0。
signed int __thiscall sub_4020C0(int this, int a2, unsigned int a3, int a4, unsigned int a5)
{
unsigned int v5; // edi@1
unsigned int v6; // edx@5
int v7; // esi@8
unsigned int v8; // edx@8
bool v9; // cf@12
unsigned __int8 v10; // al@14
unsigned __int8 v11; // al@16
unsigned __int8 v12; // al@18
signed int result; // eax@19
v5 = a3;
if ( *(_DWORD *)(this + 16) < a3 )
v5 = *(_DWORD *)(this + 16);
if ( *(_DWORD *)(this + 20) >= 0x10u )
this = *(_DWORD *)this;
v6 = a5;
if ( v5 < a5 )
v6 = v5;
if ( v6 )
{
v7 = a4;
v9 = v6 < 4;
v8 = v6 - 4;
if ( v9 )
{
LABEL_11:
if ( v8 == -4 )
goto LABEL_20;
}
else
{
while ( *(_DWORD *)this == *(_DWORD *)v7 )
{
this += 4;
v7 += 4;
v9 = v8 < 4;
v8 -= 4;
if ( v9 )
goto LABEL_11;
}
}
v9 = *(_BYTE *)this < *(_BYTE *)v7;
if ( *(_BYTE *)this != *(_BYTE *)v7
|| v8 != -3
&& ((v10 = *(_BYTE *)(this + 1), v9 = v10 < *(_BYTE *)(v7 + 1), v10 != *(_BYTE *)(v7 + 1))
|| v8 != -2
&& ((v11 = *(_BYTE *)(this + 2), v9 = v11 < *(_BYTE *)(v7 + 2), v11 != *(_BYTE *)(v7 + 2))
|| v8 != -1 && (v12 = *(_BYTE *)(this + 3), v9 = v12 < *(_BYTE *)(v7 + 3), v12 != *(_BYTE *)(v7 + 3)))) )
{
result = -v9 | 1;
goto LABEL_21;
}
LABEL_20:
result = 0;
LABEL_21:
if ( result )
return result;
}
if ( v5 >= a5 )
result = v5 != a5;
else
result = -1;
return result;
}
从result 0反向推导,然后定位到LABEL_11:,能看到v8不能等于-4,继续往下看result = -v9 | 1;上面计算中v9必须为0,再继续往下看result = v5 != a5;,可以知道这里v5必须等于a5,这个过程参数很多,有点头皮发麻,动态调试走一下,看看能不能看出点信息。
7.可以看到是拿52pojie和idg_cni~bjbfi|gsxb相比较,这里比较完,都相等,然后走je跳到xor eax,eax就是正确的流程。
00A52104 |. /74 34 je short key.00A5213A
00A52106 |> |8A01 mov al,byte ptr ds:[ecx] ; ecx寄存器:52pojie
00A52108 |. |3A06 cmp al,byte ptr ds:[esi] ; eax寄存器:idg_cni~bjbfi|gsxb
00A5210A |. |75 27 jnz short key.00A52133
00A5210C |. |83FA FD cmp edx,-0x3
00A5210F |. |74 29 je short key.00A5213A
00A52111 |. |8A41 01 mov al,byte ptr ds:[ecx+0x1]
00A52114 |. |3A46 01 cmp al,byte ptr ds:[esi+0x1]
00A52117 |. |75 1A jnz short key.00A52133
00A52119 |. |83FA FE cmp edx,-0x2
00A5211C |. |74 1C je short key.00A5213A
00A5211E |. |8A41 02 mov al,byte ptr ds:[ecx+0x2]
00A52121 |. |3A46 02 cmp al,byte ptr ds:[esi+0x2]
00A52124 |. |75 0D jnz short key.00A52133
00A52126 |. |83FA FF cmp edx,-0x1
00A52129 |. |74 0F je short key.00A5213A
00A5212B |. |8A41 03 mov al,byte ptr ds:[ecx+0x3]
00A5212E |. |3A46 03 cmp al,byte ptr ds:[esi+0x3]
00A52131 |. |74 07 je short key.00A5213A
00A52133 |> |1BC0 sbb eax,eax
00A52135 |. |83C8 01 or eax,0x1
00A52138 |. |EB 02 jmp short key.00A5213C
00A5213A |> \33C0 xor eax,eax
8.那我把文件的内容替换成idg_cni~bjbfi|gsxb,然后运行程序发现提示成功了,提交flag,正确:
0x3总结
1.这道题看上去很难,想要返回值0,好似要经过多番运算,最后OD一调试,实际上就是把两个传进来的字符串作比较。
2.刚开始下断点想着exitprocess,没回溯过去,习惯了点OD上面的API断点,后来想到exit就断下,回溯过去了。
3.有时候看不出反编译的那一段代码作用时,用OD跑起来看看,会有意外发现。
4.OD和IDA都用起来,便于定位和分析问题。
0x4参考资料
加密解密第四版-函数的调用约定
PS:善于总结,善于发现,找到分析问题的思路和解决问题的办法。虽然我现在还是零基础的小菜鸟一枚,也许学习逆向逆天改命我会失败,但也有着成功的可能,只要还有希望,就决不放弃!