buu [MRCTF2020]EasyCpp
上次没写出,这次认真分析了一下,发现自己的调试水平也有了上涨,也看了一些C++逆向的文章,尤其是stl,发现C++的oop还是挺复杂,这题还没考啥虚函数的还行了。
一.拖入ida,找到主函数,还是挺容易的
int __cdecl main(int argc, const char **argv, const char **envp)
{
double v3; // xmm0_8
__int64 v4; // rax
__int64 v5; // rcx
__int64 v6; // r8
__int64 v7; // r9
__int64 v8; // rbx
__int64 v9; // rax
int v10; // ebx
__int64 v11; // rax
__int64 v12; // rax
__int64 v13; // rax
__int64 v14; // rax
__int64 v15; // rax
__int64 v16; // rax
char v18; // [rsp+0h] [rbp-140h]
__int64 v19; // [rsp+28h] [rbp-118h]
__int64 v20; // [rsp+30h] [rbp-110h]
int v21; // [rsp+3Ch] [rbp-104h]
char v22; // [rsp+40h] [rbp-100h]
char v23; // [rsp+60h] [rbp-E0h]
char v24; // [rsp+90h] [rbp-B0h]
char v25; // [rsp+AFh] [rbp-91h]
char v26; // [rsp+B0h] [rbp-90h]
char v27; // [rsp+DFh] [rbp-61h]
char v28; // [rsp+E0h] [rbp-60h]
int v29; // [rsp+104h] [rbp-3Ch]
char *v30; // [rsp+108h] [rbp-38h]
int *v31; // [rsp+110h] [rbp-30h]
_DWORD *v32; // [rsp+118h] [rbp-28h]
int *v33; // [rsp+120h] [rbp-20h]
int i; // [rsp+128h] [rbp-18h]
int v35; // [rsp+12Ch] [rbp-14h]
v35 = 0;
std::vector<int,std::allocator<int>>::vector(&v24, argv, envp);
std::vector<bool,std::allocator<bool>>::vector(&v23);
std::allocator<char>::allocator(&v25);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(&v22, &unk_500E, &v25);
std::allocator<char>::~allocator(&v25);
v4 = std::operator<<<std::char_traits<char>>(&std::cout, "give me your key!");
std::ostream::operator<<(v4, &std::endl<char,std::char_traits<char>>);
for ( i = 0; i <= 8; ++i )
{
std::istream::operator>>(&std::cin, &keys[i]);
std::__cxx11::to_string((std::__cxx11 *)&v26, keys[i], (unsigned int)keys[i], v5, v6, v7);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator+=(&v22, &v26);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&v26);// 输入9组字符
}
v32 = keys;
v33 = keys;
v31 = (int *)&unk_83E4;
while ( v33 != v31 )
{
v21 = *v33;
std::vector<int,std::allocator<int>>::push_back(&v24, &v21);
++v33;
} // 将输入的字符串存入vector中
v8 = std::vector<int,std::allocator<int>>::end(&v24);
v9 = std::vector<int,std::allocator<int>>::begin(&v24);
std::for_each<__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>,main::{lambda(int &)#1}>(// 每组异或1
v8,
v9,
v8);
v30 = &v24;
v20 = std::vector<int,std::allocator<int>>::begin(&v24);
v19 = std::vector<int,std::allocator<int>>::end(v30);// 类似头指针和尾指针
while ( (unsigned __int8)__gnu_cxx::operator!=<int *,std::vector<int,std::allocator<int>>>(&v20, &v19) )
{
v29 = *(_DWORD *)__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>::operator*(&v20);
std::allocator<char>::allocator(&v27);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(&v18, &unk_500E, &v27);// 定义string
std::allocator<char>::~allocator(&v27);
depart(v29, (__int64)&v18, v3); // 一波骚操作
{lambda(std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>> &)#1}::operator()(
(__int64)&func, // 一波替换
(__int64)&v18);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(&v28, &v18);// string复制
v10 = (unsigned __int64){lambda(std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>,int)#2}::operator()(
(__int64)&check, // 比较函数
(__int64)&v28,
v35) ^ 1;
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&v28);
if ( (_BYTE)v10 )
{
v11 = std::operator<<<std::char_traits<char>>(&std::cout, "Wrong password!");
std::ostream::operator<<(v11, &std::endl<char,std::char_traits<char>>);
system("pause");
exit(0);
}
++v35;
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&v18);
__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>::operator++(&v20);// 头指针加一
}
v12 = std::operator<<<std::char_traits<char>>(&std::cout, "right!");
std::ostream::operator<<(v12, &std::endl<char,std::char_traits<char>>);
v13 = std::operator<<<std::char_traits<char>>(&std::cout, "flag:MRCTF{md5(");
v14 = std::operator<<<char,std::char_traits<char>,std::allocator<char>>(v13, &v22);
v15 = std::operator<<<std::char_traits<char>>(v14, ")}");
std::ostream::operator<<(v15, &std::endl<char,std::char_traits<char>>);
v16 = std::operator<<<std::char_traits<char>>(
&std::cout,
"md5()->{32/upper case/put the string into the function and transform into md5 hash}");
std::ostream::operator<<(v16, &std::endl<char,std::char_traits<char>>);
system("pause");
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&v22);
std::vector<bool,std::allocator<bool>>::~vector(&v23);
std::vector<int,std::allocator<int>>::~vector(&v24);
return 0;
}
把我的分析都写在注释里了,发现这个程序大体的逻辑是,输入9个数字,每个数字异或1,之后,遍历每个数字,进过depart函数,再进行替换,flag里面包含是md5加密的输入的9个数字。这里比较坑的有几点:
1.
当时一直没找到这个函数在哪。。原来点击那个Lambda就可以进入函数中了,lambda之前也百度了一下,之前一道加密题也碰到过,不过是python的语法,C++里面这个和python里面意思大体一致,相当于一个匿名函数,在主函数体里面定义,()里面是指的捕捉对象,具体,用的时候,再回忆。
2.
depart函数,这个递归感觉会劝退很多人,这个函数意思就是将数字分解,最后将除数和最后一个数字拼接成字符串。
二.根据我们的思路逆向写出一个脚本,最后的数字找个在线网站直接加密下就好了。
strs=["=zqE=z=z=z","=lzzE","=ll=T=s=s=E","=zATT","=s=s=s=E=E=E","=EOll=E","=lE=T=E=E=E","=EsE=s=z","=AT=lE=ll"]
def replacediy(str):
str=str.replace("O","0")
str=str.replace("l","1")
str=str.replace("z","2")
str=str.replace("E","3")
str=str.replace("A","4")
str=str.replace("s","5")
str=str.replace("G","6")
str=str.replace("T","7")
str=str.replace("B","8")
str=str.replace("q","9")
str=str.replace("="," ")
return str
flag=""
for i in strs:
tmp=replacediy(i).split(" ")[1:]
print tmp
sum=1
for j in range(len(tmp)):
sum*=int(tmp[j],10)
sum^=1
flag+=str(sum)
print flag