攻防世界逆向高手题之Newbie_calculations
继续开启全栈梦想之逆向之旅~
这题是攻防世界逆向高手题的Newbie_calculations
下载附件,照例扔入exinfope中查看信息:
32位无壳,照例扔入IDA32中查看信息:
浮上眼前的是一堆自定义函数,而且数量很多,吓傻了,快速浏览并随便点进函数看来一下,函数代码还多,以为是混淆,但是又想不出是什么混淆。
.
.
运行程序看一下:
输入也输入不了,还以为是程序的什么限制,更慌了,后来查了资料才决定定下心来好好分析。
.
.
首先回顾一下以前积累的经验:
复杂代码本质应该是简洁的,这样才叫出题。
仔细一看,发现繁多的代码结果只有三个函数,sub_401100函数、sub_401000函数、sub_401220函数。
加上运行程序时输入不了不是因为程序有问题,每一个意料之外的事情都有它存在的道理和过程,不要总是怀疑题目本身。繁多的代码和巨大的数字大概率是有很多没用的冗余代码占用了程序运行的时间,才导致没有光标可以输入。
最后一行代码应该就是前面运行完后输出的flag,该伪代码中没有输入,时间到了就会输出flag,但是要修改前面的无用代码。题目是Newbie_calculations,这种题目暗示要注意,表示往运算方面去想函数。
.
.
开始分析:
这里给每个v120赋值1,v120是一个32的数组。
.
.
分析第一个sub_401100函数:(这里传入参数被我改名了)
_DWORD *__cdecl sub_401100(_DWORD *v120, int _1000000000)
{
int v3; // [esp+Ch] [ebp-1Ch]
int v4; // [esp+14h] [ebp-14h]
int v5; // [esp+18h] [ebp-10h]
int v6; // [esp+18h] [ebp-10h]
int v7; // [esp+1Ch] [ebp-Ch]
int v8; // [esp+20h] [ebp-8h] BYREF
v4 = *v120;
v5 = _1000000000;
v3 = -1;
v8 = 0;
v7 = _1000000000 * v4;
while ( _1000000000 ) //这里积累第一个经验:虽然这里循环1000000000次,但是程序返回的是v120,这里有很多和v120没有关系的其它变量,是用来混淆的,找与120有关的才是关键。
{
v6 = v7 * v4;
sub_401000(&v8, *v120); //由下面代码知道这是一个相加函数,初始值v8=0,循环1000000000次就是1000000000个v120想加,就是v120 * 1000000000 ,就是传入参数a1*a2结果赋值给第一个参数a1。
++v7;
--_1000000000;
v5 = v6 - 1;
}
while ( v3 )
{
++v7;
++*v120;
--v3;
--v5;
}
++*v120;
*v120 = v8; //这里最后是赋值v8给v120,所以while(v3)循环根本不用管,前面说过这些和v120没有关系的变量是用来混淆的,不用管。
return v120;
}
.
.
分析第二个函数 sub_401000,这也是上面的嵌套函数:
_DWORD *__cdecl sub_401000(_DWORD *a1, int a2)
{
int v3; // [esp+Ch] [ebp-18h]
int v4; // [esp+10h] [ebp-14h]
int v5; // [esp+18h] [ebp-Ch]
int v6; // [esp+1Ch] [ebp-8h]
v4 = -1;
v3 = -1 - a2 + 1; //v3=-a2
v6 = 1231;
v5 = a2 + 1231;
while ( v3 ) //这里积累第二个经验:负数做循环条件的知识,v3=-a2,然后在循环体里又--v3,一开始我也以为是死循环,因为0才是false。但是查了资料后说在32位里 -1 就是 FFFFFFFF,就是100000000 - 1。所以这一下子就转正了!所以如果while(-1)就循环100000000 - 1次,这里while(-a2),所以就循环100000000 - a2次。
{
++v6;
--*a1; //同样的返回a1我们只关注a1即可,这个循环中a1=a1-(100000000 - a2)
--v3;
--v5;
}
while ( v4 ) //这里while(-1)循环100000000 - 1次
{
--v5;
++*a1; //这里加上上面的循环变成a1=a1-(100000000 - a2) + (100000000 - 1) = a1+a2-1
--v4;
}
++*a1; //这里+1,最后结果就变成a1=a1+a2-1+1=a1+a2
return a1; //所以这个函数的作用就是a1=a1+a2,就是把传入的两个参数想加,结果赋值给第一个参数
}
.
.
分析最后一个函数sub_401220函数:
_DWORD *__cdecl sub_401220(_DWORD *a1, int a2)
{
int v3; // [esp+8h] [ebp-10h]
int v4; // [esp+Ch] [ebp-Ch]
int v5; // [esp+14h] [ebp-4h]
int v6; // [esp+14h] [ebp-4h]
v4 = -1;
v3 = -1 - a2 + 1;
v5 = -1;
while ( v3 ) //前面说过这里循环100000000 - a2次
{
++*a1; //所以这里a1=a1+100000000 - a2
--v3;
--v5;
}
v6 = v5 * v5;
while ( v4 ) //这里循环100000000 - 1次
{
v6 *= 123;
++*a1; //这里a1=a1+100000000 - a2+100000000 - 1
--v4;
}
++*a1; //这里a1=a1+100000000 - a2+100000000 - 1+1,这里积累第三个经验,在32位系统中100000000就是0了,所以上面要写成a1=a1-a2,所以在运算题型中,程序的系统位数也是关键内容
return a1; //所以这个函数就是简单的参数相减操作a1-a2,结果赋值给第一个参数
}
.
.
.
那么到这里已经理清程序了,三个函数都可以提取成简单的相乘、相减、相加操作,然后就修改程序了。两个办法,第一个复制成C语言代码,第二个手动计算写python脚本。
.
.
第一种手动计算写python脚本:
这里积累第4个经验,IDA反汇编代码中可能把一个连续的数组拆成好多个变量,这些变量在函数栈中是连续的。但是后面整理数组时你很难发现和很难梳理他们是不是同一个数组的内容。此时应该在IDA函数栈中修改变量数组大小为它真正的数组大小。
.
举个例子,下面明明是打印v120[32]数组的:
.
可是IDA变量却只给了V120[12]数组和一堆其它变量,就是它把数组拆分了:
.
导致的后果就是后面的代码因为用的是连续变量代替数组下标,所以很难理解哪个变量对应哪个下标:(变量的间隔还不同!)
.
.
所以我们要在IDA栈中修改v120[12]为v120[32]:
.
.
这样修改之后就好看多了,不过手动计算好像还是很麻烦,算了,不手动计算了。(笑~)
.
.
直接复制到C语言中修改代码吧,很简单的,首先修改_Dword为int,然后把三个函数都改个函数名,打印函数换成Printf就好啦!(注意!这里如果没有像前面那样修改栈v120[32]数字的话,多个拆分变量在dev中就会造成变量之间的空间不连续,不连续就没法作为一个连续数组输出了,就会输出个四不像出来。):
#include<iostream>
using namespace std;
int *first(int *a1,int a2) //函数题要在Main函数外声明,返回类型是指针,所以int *做返回类型。
{
*a1=*a1*a2;
return a1;
}
int *second(int *a1,int a2)
{
*a1=*a1-a2;
return a1;
}
int *third(int *a1,int a2)
{
*a1=*a1+a2;
return a1;
}
int main(int argc, const char **argv, const char **envp)
{
int *v3; // eax
int *v4; // eax
int *v5; // eax
int *v6; // eax
int *v7; // eax
int *v8; // eax
int *v9; // eax
int *v10; // eax
int *v11; // eax
int *v12; // eax
int *v13; // eax
int *v14; // eax
int *v15; // eax
int *v16; // eax
int *v17; // eax
int *v18; // eax
int *v19; // eax
int *v20; // eax
int *v21; // eax
int *v22; // eax
int *v23; // eax
int *v24; // eax
int *v25; // eax
int *v26; // eax
int *v27; // eax
int *v28; // eax
int *v29; // eax
int *v30; // eax
int *v31; // eax
int *v32; // eax
int *v33; // eax
int *v34; // eax
int *v35; // eax
int *v36; // eax
int *v37; // eax
int *v38; // eax
int *v39; // eax
int *v40; // eax
int *v41; // eax
int *v42; // eax
int *v43; // eax
int *v44; // eax
int *v45; // eax
int *v46; // eax
int *v47; // eax
int *v48; // eax
int *v49; // eax
int *v50; // eax
int *v51; // eax
int *v52; // eax
int *v53; // eax
int *v54; // eax
int *v55; // eax
int *v56; // eax
int *v57; // eax
int *v58; // eax
int *v59; // eax
int *v60; // eax
int *v61; // eax
int *v62; // eax
int *v63; // eax
int *v64; // eax
int *v65; // eax
int *v66; // eax
int *v67; // eax
int *v68; // eax
int *v69; // eax
int *v70; // eax
int *v71; // eax
int *v72; // eax
int *v73; // eax
int *v74; // eax
int *v75; // eax
int *v76; // eax
int *v77; // eax
int *v78; // eax
int *v79; // eax
int *v80; // eax
int *v81; // eax
int *v82; // eax
int *v83; // eax
int *v84; // eax
int *v85; // eax
int *v86; // eax
int *v87; // eax
int *v88; // eax
int *v89; // eax
int *v90; // eax
int *v91; // eax
int *v92; // eax
int *v93; // eax
int *v94; // eax
int *v95; // eax
int *v96; // eax
int *v97; // eax
int *v98; // eax
int *v99; // eax
int *v100; // eax
int *v101; // eax
int *v102; // eax
int *v103; // eax
int *v104; // eax
int *v105; // eax
int *v106; // eax
int *v107; // eax
int *v108; // eax
int *v109; // eax
int *v110; // eax
int *v111; // eax
int *v112; // eax
int *v113; // eax
int v115; // [esp-8h] [ebp-9Ch]
int v116; // [esp-4h] [ebp-98h]
int v117; // [esp-4h] [ebp-98h]
int i; // [esp+4h] [ebp-90h]
int j; // [esp+8h] [ebp-8Ch]
int v120[33]; // [esp+Ch] [ebp-88h] BYREF
for ( i = 0; i < 32; ++i )
v120[i] = 1; // 最后操作的是v120,直接跟踪v120即可,这里赋值v120[32]都为1
v120[32] = 0;
puts("Your flag is:");
v3 = first(v120, 1000000000);
v4 = second(v3, 999999950);
first(v4, 2); // v120=100
v5 = third(&v120[1], 5000000);
v6 = second(v5, 6666666);
v7 = third(v6, 1666666);
v8 = third(v7, 45);
v9 = first(v8, 2);
third(v9, 5); // 97
v10 = first(&v120[2], 1000000000);
v11 = second(v10, 999999950);
v12 = first(v11, 2);
third(v12, 2); // 104
v13 = third(&v120[3], 55);
v14 = second(v13, 3);
v15 = third(v14, 4);
second(v15, 1); // 56
v16 = first(&v120[4], 100000000);
v17 = second(v16, 99999950);
v18 = first(v17, 2);
third(v18, 2); // 102
v19 = second(&v120[5], 1);
v20 = first(v19, 1000000000);
v21 = third(v20, 55);
second(v21, 3); // 58
v22 = first(&v120[6], 1000000);
v23 = second(v22, 999975);
first(v23, 4); // 100
v24 = third(&v120[7], 55);
v25 = second(v24, 33);
v26 = third(v25, 44);
second(v26, 11); // 56
v27 = first(&v120[8], 10);
v28 = second(v27, 5);
v29 = first(v28, 8);
third(v29, 9); // 49
v30 = third(&v120[9], 0);
v31 = second(v30, 0);
v32 = third(v31, 11);
v33 = second(v32, 11);
third(v33, 53); // 54
v34 = third(&v120[10], 49);
v35 = second(v34, 2);
v36 = third(v35, 4);
second(v36, 2); // 50
v37 = first(&v120[11], 1000000);
v38 = second(v37, 999999);
v39 = first(v38, 4);
third(v39, 50); // 54
v40 = third(&v120[12], 1);
v41 = third(v40, 1);
v42 = third(v41, 1);
v43 = third(v42, 1);
v44 = third(v43, 1);
v45 = third(v44, 1);
v46 = third(v45, 10);
third(v46, 32); // 49
v47 = first(&v120[13], 10);
v48 = second(v47, 5);
v49 = first(v48, 8);
v50 = third(v49, 9);
third(v50, 48); // 97
v51 = second(&v120[14], 1);
v52 = first(v51, -294967296);
v53 = third(v52, 55);
second(v53, 3); // 52
v54 = third(&v120[15], 1);
v55 = third(v54, 2);
v56 = third(v55, 3);
v57 = third(v56, 4);
v58 = third(v57, 5);
v59 = third(v58, 6);
v60 = third(v59, 7);
third(v60, 20); // 48
v61 = first(&v120[16], 10);
v62 = second(v61, 5);
v63 = first(v62, 8);
v64 = third(v63, 9);
third(v64, 48); // 97
v65 = third(&v120[17], 7);
v66 = third(v65, 6);
v67 = third(v66, 5);
v68 = third(v67, 4);
v69 = third(v68, 3);
v70 = third(v69, 2);
v71 = third(v70, 1);
third(v71, 20); // 49
v72 = third(&v120[18], 7);
v73 = third(v72, 2);
v74 = third(v73, 4);
v75 = third(v74, 3);
v76 = third(v75, 6);
v77 = third(v76, 5);
v78 = third(v77, 1);
third(v78, 20); // 49
v79 = first(&v120[19], 1000000);
v80 = second(v79, 999999);
v81 = first(v80, 4);
v82 = third(v81, 50);
second(v82, 1); // 53
v83 = second(&v120[20], 1);
v84 = first(v83, -294967296);
v85 = third(v84, 49);
second(v85, 1);
v86 = second(&v120[21], 1); // 48
v87 = first(v86, 1000000000);
v88 = third(v87, 54);
v89 = second(v88, 1);
v90 = third(v89, 1000000000);
second(v90, 1000000000); // 53
v91 = third(&v120[22], 49);
v92 = second(v91, 1);
v93 = third(v92, 2);
second(v93, 1); // 50
v94 = first(&v120[23], 10);
v95 = second(v94, 5);
v96 = first(v95, 8);
v97 = third(v96, 9);
third(v97, 48); // 97
v98 = third(&v120[24], 1);
v99 = third(v98, 3);
v100 = third(v99, 3);
v101 = third(v100, 3);
v102 = third(v101, 6);
v103 = third(v102, 6);
v104 = third(v103, 6);
third(v104, 20); // 49
v105 = third(&v120[25], 55);
v106 = second(v105, 33);
v107 = third(v106, 44);
v108 = second(v107, 11);
third(v108, 42); // 97
third(&v120[26], v120[25]); // 56
third(&v120[27], v120[12]);
v115 = v120[27];
v109 = second(&v120[28], 1);
v110 = third(v109, v115);
second(v110, 1);
v116 = v120[23];
v111 = second(&v120[29], 1);
v112 = first(v111, 1000000);
third(v112, v116);
v117 = v120[27];
v113 = third(&v120[30], 1);
first(v113, v117);
third(&v120[31], v120[30]);
printf("CTF{");
for ( j = 0; j < 32; ++j )
printf("%c", (v120[j]));
printf("}");
return 0;
}
.
.
结果:
.
.
.
总结:
1:
这里积累第一个经验:虽然这里循环1000000000次,但是程序返回的是v120,这里有很多和v120没有关系的其它变量,是用来混淆的,找与120有关的才是关键。
2:
这里积累第二个经验:负数做循环条件的知识,v3=-a2,然后在循环体里又–v3,一开始我也以为是死循环,因为0才是false。但是查了资料后说在32位里
-1 就是 FFFFFFFF,就是100000000 - 1。所以这一下子就转正了!所以如果while(-1)就循环100000000 - 1次,这里while(-a2),所以就循环100000000 - a2次。
3:
这里a1=a1+100000000 - a2+100000000 -
1+1,这里积累第三个经验,在32位系统中100000000就是0了,所以上面要写成a1=a1-a2,所以在运算题型中,程序的系统位数也是关键内容
4:
这里积累第4个经验,IDA反汇编代码中可能把一个连续的数组拆成好多个变量,这些变量在函数栈中是连续的。但是后面整理数组时你很难发现和很难梳理他们是不是同一个数组的内容。此时应该在IDA函数栈中修改变量数组大小为它真正的数组大小。
解毕!敬礼!