XCTF-攻防世界CTF平台-Reverse逆向类——53easyCpp
Posted 大灬白
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了XCTF-攻防世界CTF平台-Reverse逆向类——53easyCpp相关的知识,希望对你有一定的参考价值。
下载附件easyCpp,先查看文件信息:
是Linux下的64位ELF文件,运行程序:
是一个输入字符串然后判断是否正确的程序,错误就输入“You failed!”
之后用IDA64打开:
main函数源代码及注释:
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v3; // r15
__int64 v4; // r12
__int64 v5; // rbx
__int64 v6; // rax
__int64 v7; // rbx
__int64 v8; // rax
__int64 v9; // r8
__int64 v10; // r9
char v11; // al
unsigned int *v12; // rax
int i; // [rsp+1Ch] [rbp-174h]
int j; // [rsp+20h] [rbp-170h]
char v16[32]; // [rsp+30h] [rbp-160h] BYREF
char v17[32]; // [rsp+50h] [rbp-140h] BYREF
char v18[32]; // [rsp+70h] [rbp-120h] BYREF
char v19[32]; // [rsp+90h] [rbp-100h] BYREF
char v20[32]; // [rsp+B0h] [rbp-E0h] BYREF
__int64 a4[4]; // [rsp+D0h] [rbp-C0h] BYREF
__int64 a1[4]; // [rsp+F0h] [rbp-A0h] BYREF
char v23[72]; // [rsp+110h] [rbp-80h] BYREF
unsigned __int64 v24; // [rsp+158h] [rbp-38h]
v24 = __readfsqword(0x28u);
std::vector<int>::vector((__int64)v16);
std::vector<int>::vector((__int64)v17);
std::vector<int>::vector((__int64)v18);
std::vector<int>::vector((__int64)v19);
std::vector<int>::vector((__int64)v20);
for ( i = 0; i <= 15; ++i ) // 16个字符
{
scanf("%d", &v23[4 * i]);
std::vector<int>::push_back(v17, &v23[4 * i]);// 输入的字符插入到v17字符数组最后
}
for ( j = 0; j <= 15; ++j )
{
LODWORD(a1[0]) = fib(j); // fib在c语言中为斐波那契数列,获得斐波那契数列第j个数的值
std::vector<int>::push_back(v16, a1); // 把斐波那契数列前16个数插入到v16字符数组
}
std::vector<int>::push_back(v18, v23); // v23就是第一个字符插入v18向量最后,v18是一个新的地址存储计算后的字符串
v4 = std::back_inserter<std::vector<int>>(v18);// 产生一个v18向量的迭代器v4
v5 = std::vector<int>::end(v17); // 返回v17向量尾指针指向向量最后一个元素的下一个位置,给v5
a1[0] = std::vector<int>::begin(v17); // 返回v17向量头指针,指向第一个元素,给a1[0]
v6 = __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator+(a1, 1LL);// 重载操作符,v6 = a1 +1*4,也就是v6是从输入字符串的第二个64位值开始
std::transform<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::back_insert_iterator<std::vector<int>>,main::{lambda(int)#1}>(
v6, // 参数逆序入栈,v6输入字符串的第二个值的开始地址存放在rdi,v5字符串结束地址存放在rsi
v5,
v4, // v4新的中间字符串
(__int64)v23); // 输入字符串的第一个64位
std::vector<int>::vector((__int64)a4);
v7 = std::vector<int>::end(v18);
v8 = std::vector<int>::begin(v18); // 中间字符串v18的起始、结束位置
std::accumulate<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::vector<int>,main::{lambda(std::vector<int>,int)#2}>(
(__int64)a1, // 开始输入的字符
v8,
v7, // 中间字符串v18的起始、结束位置
(__int64)a4, // 新的向量
v9,
v10,
v3);
std::vector<int>::operator=(v19, a1); // 逆序后的字符串给v19
std::vector<int>::~vector(a1);
std::vector<int>::~vector(a4);
if ( (unsigned __int8)std::operator!=<int,std::allocator<int>>(v19, v16) )// 判断v14的值是否等于斐波那契数列前16个数
{
puts("You failed!");
exit(0);
}
std::back_inserter<std::vector<int>>(v20);
std::vector<int>::end(v17);
v11 = std::vector<int>::begin(v17);
std::copy_if<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::back_insert_iterator<std::vector<int>>,main::{lambda(int)#3}>(v11);
puts("You win!");
printf("Your flag is:flag{");
a4[0] = std::vector<int>::begin(v20);
a1[0] = std::vector<int>::end(v20);
while ( (unsigned __int8)__gnu_cxx::operator!=<int *,std::vector<int>>(a4, a1) )
{
v12 = (unsigned int *)__gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator*(a4);
std::ostream::operator<<(&std::cout, *v12);
__gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator++(a4);
}
putchar(125);
std::vector<int>::~vector(v20);
std::vector<int>::~vector(v19);
std::vector<int>::~vector(v18);
std::vector<int>::~vector(v17);
std::vector<int>::~vector(v16);
return 0;
}
push_back()函数:将一个新的元素加到vector的最后面,位置为当前最后一个元素的下一个元素。
程序的逻辑就是首先保存输入的字符串到v17,然后计算斐波那契数列前16个数保存在v16,之后将v17经过主要的变换过程就是transform函数和accumulate函数,之后得到的结果和v16比较,通过就输出“You win!”,失败就输出“You failed!”。
首先查看transform函数:
transform函数源代码及注释:
__int64 __fastcall std::transform<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::back_insert_iterator<std::vector<int>>,main::{lambda(int)#1}>(__int64 a1, __int64 a2, __int64 a3, __int64 a4)
{
unsigned int *v4; // rax
__int64 v5; // rax
__int64 v7; // [rsp+0h] [rbp-30h] BYREF
__int64 v8; // [rsp+8h] [rbp-28h] BYREF
__int64 v9; // [rsp+10h] [rbp-20h] BYREF
__int64 v10; // [rsp+18h] [rbp-18h] BYREF
int v11; // [rsp+24h] [rbp-Ch] BYREF
unsigned __int64 v12; // [rsp+28h] [rbp-8h]
v10 = a1; // 输入的字符串起始位置
v9 = a2;
v8 = a3;
v7 = a4; // 输入的第一个字符
v12 = __readfsqword(0x28u);
while ( (unsigned __int8)__gnu_cxx::operator!=<int *,std::vector<int>>(&v10, &v9) )// &v10字符串开始的地址,不等于,&v9字符串结束的位置
{
v4 = (unsigned int *)__gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator*(&v10);// 返回v10的指针给v4
v11 = main::{lambda(int)#1}::operator()(&v7, *v4);// v11 = v7的值 加上 v4的值
v5 = std::back_insert_iterator<std::vector<int>>::operator*(&v8);// v5=v8
std::back_insert_iterator<std::vector<int>>::operator=(v5, &v11);// 把v11的值插入到v5向量的最后
__gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator++(&v10);// v10地址加4LL
std::back_insert_iterator<std::vector<int>>::operator++(&v8);// v8地址加4LL
}
return v8;
}
std::transform<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::back_insert_iterator<std::vector<int>>,main::{lambda(int)#1}>(v6,v5,v4,(__int64)v23);
transform函数函数的作用就是把输入的字符串从第二个值开始v6逐个加上第一个值v23,直到最后一个值结束v5,结果保存在新的中间地址v4。
之后是accumulate函数:
accumulate函数源代码及注释
__int64 __fastcall std::accumulate<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::vector<int>,main::{lambda(std::vector<int>,int)#2}>(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5, __int64 a6, char a7)
{
unsigned int v7; // ebx
__int64 v10; // [rsp+8h] [rbp-68h] BYREF
__int64 v11; // [rsp+10h] [rbp-60h] BYREF
__int64 v12; // [rsp+18h] [rbp-58h]
char v13[32]; // [rsp+20h] [rbp-50h] BYREF
char v14[24]; // [rsp+40h] [rbp-30h] BYREF
unsigned __int64 v15; // [rsp+58h] [rbp-18h]
v12 = a1; // 开始输入的字符串
v11 = a2; // 中间字符串的开始地址
v10 = a3; // 结束
v15 = __readfsqword(0x28u); //
// 这个循环的作用是把中间字符串逆序,逆序后的结果保存在v12返回
while ( (unsigned __int8)__gnu_cxx::operator!=<int *,std::vector<int>>(&v11, &v10) )// 字符串的开始地址和结束地址不相等
{
v7 = *(_DWORD *)__gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator*((__int64)&v11);// v7等于字符串开始的第一个值
std::vector<int>::vector(v13, a4); // 把a4的值copy给v13。作用是在循环中暂时保存上一轮计算得到的a4字符串
main::{lambda(std::vector<int>,int)#2}::operator()(v14, &a7, v13, v7);// 把当前输入的值v7放在结果v14的第一个位置,把上一轮的结果v13放在v14第一个位置后面。
// 也就是每次把第二个值插在第一个前面,第三个之插在第二个前面
std::vector<int>::operator=(a4, v14); // 把v14的状态move给a4,获得计算之后值
std::vector<int>::~vector(v14);
std::vector<int>::~vector(v13);
__gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator++(&v11);// 开始地址加4LL
}
std::vector<int>::vector(v12, a4); // a4的结果字符串,move给原来的输入的v12字符串
return v12;
}
std::accumulate<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::vector<int>,main::{lambda(std::vector<int>,int)#2}>((__int64)a1,v8,v7,(__int64)a4,v9,v10,v3);
accumulate函数函数的作用就是前面transform函数计算得到的中间字符串v18,从起始地址v8到结束地址v7,转换顺序逆序排列,将结果再保存到输入字符串的地址a1中。
最后比较结果字符串v19和斐波那契数列前16个数v16是否逐个数相等。
程序的计算流程就是:
1、接收16个数字输入;
2、计算斐波那契数列前16项;
3、计算把输入的16个数字,从第二个数字开始,都加上第一个值;
4、将步骤3的计算的结果顺序,逆向排序
5、将步骤4计算的结果和步骤2的结果比较,完全相同则输出“You win!”
所以我们的逆向算法的过程就是:
1、计算斐波那契数列前16项;
2、将斐波那契数列前16项顺序,逆向排序 ;
3、把步骤2中得到的结果,从第二个数字开始,都减去第一个值,得到的数组就是正确的输入。
python代码:
#模拟斐波那契数列
def fib(num):
if not num or num == 1:
return 1
j = fib(num - 1)
return j + fib(num - 2)
# 1、flag列表获得斐波那契数列前16个数
flag = []
for i in range(16):
flag.append(fib(i))
print(flag)
# 2、flag列表获得斐波那契数列前16个数的逆序列表
flag = flag[::-1]
print(flag)
# 3、把步骤2中得到的结果flag列表,从第二个数字开始,都减去第一个值,得到的数组就是正确的输入
for i in range(1,len(flag)):
flag[i] = flag[i] - flag[0]
print(flag)
for i in range(16):
print(flag[i],end=" ")
运行结果:
将结果987 -377 -610 -754 -843 -898 -932 -953 -966 -974 -979 -982 -984 -985 -986 -986输入程序:
得到flag:flag{987-377-843-953-979-985}
以上是关于XCTF-攻防世界CTF平台-Reverse逆向类——53easyCpp的主要内容,如果未能解决你的问题,请参考以下文章
XCTF-攻防世界CTF平台-Reverse逆向类——53easyCpp
XCTF-攻防世界CTF平台-Reverse逆向类——65reverse-box
XCTF-攻防世界CTF平台-Reverse逆向类——59mfc逆向-200
XCTF-攻防世界CTF平台-Reverse逆向类——52handcrafted-pyc