admin管理员组文章数量:1623599
前言
忙里偷闲做一道攻防世界的C++逆向, 最近刷了一遍pipixia师傅的blog, 感觉作为一个binaryer, reverse和pwn不应该割裂开来, 而且之前做了半年逆向, 发现不知不觉间代码能力提高了(最近写代码的时候才发现的), 估计能够读懂反编译的伪码对码力也有训练作用吧, 而且入坑安全1年了, 逐渐体会到逆向是一件很有趣的事情, 以前做复杂逆向的时候总是很急躁, 现在才知道搞技术, 最重要的还是耐心.
分析过程
直接拖进IDA64 (IDA32弹框提示ELFx86-64, 并且应该养成习惯IDA之前先查壳
看到main函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rax
__int64 v4; // rax
__int64 v5; // rax
__int64 v6; // rax
__int64 v7; // rax
__int64 v8; // rax
__int64 v9; // rax
__int64 v10; // rax
__int64 v11; // rax
__int64 v12; // rax
__int64 v13; // rax
__int64 v14; // rax
__int64 v15; // rax
__int64 v16; // rax
char v18[32]; // [rsp+10h] [rbp-130h] BYREF
char v19[32]; // [rsp+30h] [rbp-110h] BYREF
char v20[32]; // [rsp+50h] [rbp-F0h] BYREF
char v21[32]; // [rsp+70h] [rbp-D0h] BYREF
char v22[32]; // [rsp+90h] [rbp-B0h] BYREF
char v23[120]; // [rsp+B0h] [rbp-90h] BYREF
unsigned __int64 v24; // [rsp+128h] [rbp-18h]
v24 = __readfsqword(0x28u);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(v18, argv, envp);
std::operator>><char>(&std::cin, v18);
v3 = std::operator<<<std::char_traits<char>>(&std::cout, "-------------------------------------------");
std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
v4 = std::operator<<<std::char_traits<char>>(&std::cout, "Quote from people's champ");
std::ostream::operator<<(v4, &std::endl<char,std::char_traits<char>>);
v5 = std::operator<<<std::char_traits<char>>(&std::cout, "-------------------------------------------");
std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
v6 = std::operator<<<std::char_traits<char>>(
&std::cout,
"*My goal was never to be the loudest or the craziest. It was to be the most entertaining.");
std::ostream::operator<<(v6, &std::endl<char,std::char_traits<char>>);
v7 = std::operator<<<std::char_traits<char>>(&std::cout, "*Wrestling was like stand-up comedy for me.");
std::ostream::operator<<(v7, &std::endl<char,std::char_traits<char>>);
v8 = std::operator<<<std::char_traits<char>>(
&std::cout,
"*I like to use the hard times in the past to motivate me today.");
std::ostream::operator<<(v8, &std::endl<char,std::char_traits<char>>);
v9 = std::operator<<<std::char_traits<char>>(&std::cout, "-------------------------------------------");
std::ostream::operator<<(v9, &std::endl<char,std::char_traits<char>>);
HighTemplar::HighTemplar((DarkTemplar *)v23, (__int64)v18);
v10 = std::operator<<<std::char_traits<char>>(&std::cout, "Checking....");
std::ostream::operator<<(v10, &std::endl<char,std::char_traits<char>>);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(v19, v18);
func1(v20, v19);
func2(v21, v20);
func3(v21, 0LL);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v21);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v20);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v19);
HighTemplar::calculate((HighTemplar *)v23);
if ( !(unsigned int)HighTemplar::getSerial((HighTemplar *)v23) )
{
v11 = std::operator<<<std::char_traits<char>>(&std::cout, "/");
std::ostream::operator<<(v11, &std::endl<char,std::char_traits<char>>);
v12 = std::operator<<<std::char_traits<char>>(&std::cout, "Do not be angry. Happy Hacking :)");
std::ostream::operator<<(v12, &std::endl<char,std::char_traits<char>>);
v13 = std::operator<<<std::char_traits<char>>(&std::cout, "/");
std::ostream::operator<<(v13, &std::endl<char,std::char_traits<char>>);
HighTemplar::getFlag[abi:cxx11](v22, v23);
v14 = std::operator<<<std::char_traits<char>>(&std::cout, "flag{");
v15 = std::operator<<<char>(v14, v22);
v16 = std::operator<<<std::char_traits<char>>(v15, "}");
std::ostream::operator<<(v16, &std::endl<char,std::char_traits<char>>);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v22);
}
HighTemplar::~HighTemplar((HighTemplar *)v23);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v18);
return 0;
}
从flag字符串倒着分析, 有getSerial
, calculate
, HighTemplar
等可能的关键函数, 然后定位到
std::operator>><char>(&std::cin, v18);
知道v18是输入的字符串
HighTemplar::HighTemplar((DarkTemplar *)v23, (__int64)v18);
这里处理了输入字符串
分析一下HighTemplar
unsigned __int64 __fastcall HighTemplar::HighTemplar(DarkTemplar *a1, __int64 a2)
{
char v3; // [rsp+17h] [rbp-19h] BYREF
unsigned __int64 v4; // [rsp+18h] [rbp-18h]
v4 = __readfsqword(0x28u);
DarkTemplar::DarkTemplar(a1);
*(_QWORD *)a1 = &off_401EA0;
*((_DWORD *)a1 + 3) = 0;
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string((char *)a1 + 16, a2);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string((char *)a1 + 48, a2);
std::allocator<char>::allocator(&v3);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
(char *)a1 + 80,
"327a6c4304ad5938eaf0efb6cc3e53dc",
&v3);
std::allocator<char>::~allocator(&v3);
return __readfsqword(0x28u) ^ v4;
}
猜测327a6c4304ad5938eaf0efb6cc3e53dc
是一串关键字符串, 虽然直接看还不清楚这个函数的作用, 但是先往下分析
发现v23出现在了所有可能是关键函数的参数位置, 接着分析calculate
bool __fastcall HighTemplar::calculate(HighTemplar *this)
{
__int64 v1; // rax
_BYTE *v2; // rbx
bool result; // al
_BYTE *v4; // rbx
int i; // [rsp+18h] [rbp-18h]
int j; // [rsp+1Ch] [rbp-14h]
if ( std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length((char *)this + 16) != 32 )
{
v1 = std::operator<<<std::char_traits<char>>(&std::cout, "Too short or too long");
std::ostream::operator<<(v1, &std::endl<char,std::char_traits<char>>);
exit(-1);
}
for ( i = 0;
i <= (unsigned __int64)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length((char *)this + 16);
++i )
{
v2 = (_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
(char *)this + 16,
i);
*v2 = (*(_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
(char *)this + 16,
i) ^ 0x50)
+ 23;
}
for ( j = 0; ; ++j )
{
result = j <= (unsigned __int64)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length((char *)this + 16);
if ( !result )
break;
v4 = (_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
(char *)this + 16,
j);
*v4 = (*(_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
(char *)this + 16,
j) ^ 0x13)
+ 11;
}
return result;
}
这里伪码很丑, 不过大概就是一个if和两个for, 要求字符串长度==32, 然后对每个字符^0x50 + 23
, 然后^0x13 + 11
然后看看getserial
__int64 __fastcall HighTemplar::getSerial(HighTemplar *this)
{
char v1; // bl
__int64 v2; // rax
__int64 v3; // rax
__int64 v4; // rax
__int64 v5; // rax
unsigned int i; // [rsp+1Ch] [rbp-14h]
for ( i = 0;
(int)i < (unsigned __int64)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length((char *)this + 16);
++i )
{
v1 = *(_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
(char *)this + 80,
(int)i);
if ( v1 != *(_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
(char *)this + 16,
(int)i) )
{
v4 = std::operator<<<std::char_traits<char>>(&std::cout, "You did not pass ");
v5 = std::ostream::operator<<(v4, i);
std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
*((_DWORD *)this + 3) = 1;
return *((unsigned int *)this + 3);
}
v2 = std::operator<<<std::char_traits<char>>(&std::cout, "Pass ");
v3 = std::ostream::operator<<(v2, i);
std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
}
return *((unsigned int *)this + 3);
}
将上面的计算结果this + 16
的字符串和this + 80
的作比较
解题脚本
其实不需要分析得太清楚, 已经可以猜到程序的脉路了, 总体来说还是比较简单的
exp
serial = "327a6c4304ad5938eaf0efb6cc3e53dc"
flag = ""
for ch in serial:
flag += chr((ord(ch) - 11 ^ 0x13) - 23 ^ 0x50)
print("flag{" + flag + "}")
总结
cpp的反编译伪码非常丑陋, 不过不需要在意一长串函数名, 直接看关键位置就行, 比如cin, cout等
逆向还是更偏向于分析, 解题脚本往往比pwn的简单不少
逆向更多是自顶向下的分析, 从宏观把握程序逻辑, 忽略繁琐的代码细节, 猜测一个思路然后验证, 以加快逆向速度
当然除非是打pwn, pwn的逆向分析是要抠清楚漏洞点的所有细节的
(说一堆大道理其实都莫得用, 多刷题, 干就完了
最近实在太多事, 忙完这段我会继续更新BUU-pwn的wp, 并且开个BUU-reverse的新坑叭
关于打比赛的事…明年再说, 真没一大段时间肝比赛, 只能碎片时间里刷刷题维持生计的样子(
版权声明:本文标题:ADworld reverse wp - crazy 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dianzi/1728891616a1178119.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论