XCTF-RE-Newbie_calculations

CTF · 03-21 · 74 人浏览

1.查壳

Pasted image 20231220171949.png
PE32 无壳

尝试运行
Pasted image 20231220172254.png
提示输入flag,但是发现输入不进去,程序也没有卡顿,光标闪烁很流畅,猜测是程序内部分代码阻止了输入。

2.IDA逆向分析

题目名为Newbie_calculations,也是一种提示,表示此题与运算相关

打开看到一堆变量定义和一堆函数调用,逆序阅读代码
Pasted image 20231220172530.png
发现程序最后直接输出flag,其值在数组v120中,并且知道其长度为32。
向上跟踪数组v120

Pasted image 20231220172739.png
在栈中修改数组长度后,代码变的好看一点了,但还是没能完全解决IDA拆分的问题
Pasted image 20231220172856.png
Pasted image 20231220172910.png

首先初始化数组v120[32], 其中32个元素全初始化为1
Pasted image 20231220173239.png
阅读代码后发现,main函数中没有执行用户输入的函数,
一大堆的函数执行中实际上只有三个函数
sub_731100 ,sub_731000 和sub_731220
分别查看

main函数代码如下:(三个函数已经经过改名)

int __cdecl main(int argc, const char **argv, const char **envp)
{
  _DWORD *v3; // eax
  _DWORD *v4; // eax
  _DWORD *v5; // eax
  _DWORD *v6; // eax
  _DWORD *v7; // eax
  _DWORD *v8; // eax
  _DWORD *v9; // eax
  _DWORD *v10; // eax
  _DWORD *v11; // eax
  _DWORD *v12; // eax
  _DWORD *v13; // eax
  _DWORD *v14; // eax
  _DWORD *v15; // eax
  _DWORD *v16; // eax
  _DWORD *v17; // eax
  _DWORD *v18; // eax
  _DWORD *v19; // eax
  _DWORD *v20; // eax
  _DWORD *v21; // eax
  _DWORD *v22; // eax
  _DWORD *v23; // eax
  _DWORD *v24; // eax
  _DWORD *v25; // eax
  _DWORD *v26; // eax
  _DWORD *v27; // eax
  _DWORD *v28; // eax
  _DWORD *v29; // eax
  _DWORD *v30; // eax
  _DWORD *v31; // eax
  _DWORD *v32; // eax
  _DWORD *v33; // eax
  _DWORD *v34; // eax
  _DWORD *v35; // eax
  _DWORD *v36; // eax
  _DWORD *v37; // eax
  _DWORD *v38; // eax
  _DWORD *v39; // eax
  _DWORD *v40; // eax
  _DWORD *v41; // eax
  _DWORD *v42; // eax
  _DWORD *v43; // eax
  _DWORD *v44; // eax
  _DWORD *v45; // eax
  _DWORD *v46; // eax
  _DWORD *v47; // eax
  _DWORD *v48; // eax
  _DWORD *v49; // eax
  _DWORD *v50; // eax
  _DWORD *v51; // eax
  _DWORD *v52; // eax
  _DWORD *v53; // eax
  _DWORD *v54; // eax
  _DWORD *v55; // eax
  _DWORD *v56; // eax
  _DWORD *v57; // eax
  _DWORD *v58; // eax
  _DWORD *v59; // eax
  _DWORD *v60; // eax
  _DWORD *v61; // eax
  _DWORD *v62; // eax
  _DWORD *v63; // eax
  _DWORD *v64; // eax
  _DWORD *v65; // eax
  _DWORD *v66; // eax
  _DWORD *v67; // eax
  _DWORD *v68; // eax
  _DWORD *v69; // eax
  _DWORD *v70; // eax
  _DWORD *v71; // eax
  _DWORD *v72; // eax
  _DWORD *v73; // eax
  _DWORD *v74; // eax
  _DWORD *v75; // eax
  _DWORD *v76; // eax
  _DWORD *v77; // eax
  _DWORD *v78; // eax
  _DWORD *v79; // eax
  _DWORD *v80; // eax
  _DWORD *v81; // eax
  _DWORD *v82; // eax
  _DWORD *v83; // eax
  _DWORD *v84; // eax
  _DWORD *v85; // eax
  _DWORD *v86; // eax
  _DWORD *v87; // eax
  _DWORD *v88; // eax
  _DWORD *v89; // eax
  _DWORD *v90; // eax
  _DWORD *v91; // eax
  _DWORD *v92; // eax
  _DWORD *v93; // eax
  _DWORD *v94; // eax
  _DWORD *v95; // eax
  _DWORD *v96; // eax
  _DWORD *v97; // eax
  _DWORD *v98; // eax
  _DWORD *v99; // eax
  _DWORD *v100; // eax
  _DWORD *v101; // eax
  _DWORD *v102; // eax
  _DWORD *v103; // eax
  _DWORD *v104; // eax
  _DWORD *v105; // eax
  _DWORD *v106; // eax
  _DWORD *v107; // eax
  _DWORD *v108; // eax
  _DWORD *v109; // eax
  _DWORD *v110; // eax
  _DWORD *v111; // eax
  _DWORD *v112; // eax
  _DWORD *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[12]; // [esp+Ch] [ebp-88h] BYREF 由程序代码可知,v120的长度为32,IDA将它拆分了,所以要在栈中修改
  int v121; // [esp+3Ch] [ebp-58h] BYREF
  int v122; // [esp+40h] [ebp-54h] BYREF
  int v123; // [esp+44h] [ebp-50h] BYREF
  int v124; // [esp+48h] [ebp-4Ch] BYREF
  int v125; // [esp+4Ch] [ebp-48h] BYREF
  int v126; // [esp+50h] [ebp-44h] BYREF
  int v127; // [esp+54h] [ebp-40h] BYREF
  int v128; // [esp+58h] [ebp-3Ch] BYREF
  int v129; // [esp+5Ch] [ebp-38h] BYREF
  int v130; // [esp+60h] [ebp-34h] BYREF
  int v131; // [esp+64h] [ebp-30h] BYREF
  int v132; // [esp+68h] [ebp-2Ch] BYREF
  int v133; // [esp+6Ch] [ebp-28h] BYREF
  int v134; // [esp+70h] [ebp-24h] BYREF
  int v135; // [esp+74h] [ebp-20h] BYREF
  int v136; // [esp+78h] [ebp-1Ch] BYREF
  int v137; // [esp+7Ch] [ebp-18h] BYREF
  int v138; // [esp+80h] [ebp-14h] BYREF
  int v139; // [esp+84h] [ebp-10h] BYREF
  _DWORD v140[2]; // [esp+88h] [ebp-Ch] BYREF

  for ( i = 0; i < 32; ++i )
    v120[i] = 1;                                // v120,32个元素全是1
  v140[1] = 0;
  puts("Your flag is:");                        // 执行的一堆函数中,实际上只有三个函数,
                                                // sub_731100,sub_731000,sub_731220
  v3 = mul(v120, 1000000000);                   // 将参数2与参数1相乘并赋值给参数1
  v4 = sub(v3, 999999950);                      // 参数1-参数2后的结果赋给参数1
  mul(v4, 2);
  v5 = add(&v120[1], 5000000);                  // 参数1的值与参数2相加后赋值给参数1
  v6 = sub(v5, 6666666);
  v7 = add(v6, 1666666);
  v8 = add(v7, 45);
  v9 = mul(v8, 2);
  add(v9, 5);
  v10 = mul(&v120[2], 1000000000);
  v11 = sub(v10, 999999950);
  v12 = mul(v11, 2);
  add(v12, 2);
  v13 = add(&v120[3], 55);
  v14 = sub(v13, 3);
  v15 = add(v14, 4);
  sub(v15, 1);
  v16 = mul(&v120[4], 100000000);
  v17 = sub(v16, 99999950);
  v18 = mul(v17, 2);
  add(v18, 2);
  v19 = sub(&v120[5], 1);
  v20 = mul(v19, 1000000000);
  v21 = add(v20, 55);
  sub(v21, 3);
  v22 = mul(&v120[6], 1000000);
  v23 = sub(v22, 999975);
  mul(v23, 4);
  v24 = add(&v120[7], 55);
  v25 = sub(v24, 33);
  v26 = add(v25, 44);
  sub(v26, 11);
  v27 = mul(&v120[8], 10);
  v28 = sub(v27, 5);
  v29 = mul(v28, 8);
  add(v29, 9);
  v30 = add(&v120[9], 0);
  v31 = sub(v30, 0);
  v32 = add(v31, 11);
  v33 = sub(v32, 11);
  add(v33, 53);
  v34 = add(&v120[10], 49);
  v35 = sub(v34, 2);
  v36 = add(v35, 4);
  sub(v36, 2);
  v37 = mul(&v120[11], 1000000);
  v38 = sub(v37, 999999);
  v39 = mul(v38, 4);
  add(v39, 50);
  v40 = add(&v121, 1);
  v41 = add(v40, 1);
  v42 = add(v41, 1);
  v43 = add(v42, 1);
  v44 = add(v43, 1);
  v45 = add(v44, 1);
  v46 = add(v45, 10);
  add(v46, 32);
  v47 = mul(&v122, 10);
  v48 = sub(v47, 5);
  v49 = mul(v48, 8);
  v50 = add(v49, 9);
  add(v50, 48);
  v51 = sub(&v123, 1);
  v52 = mul(v51, -294967296);
  v53 = add(v52, 55);
  sub(v53, 3);
  v54 = add(&v124, 1);
  v55 = add(v54, 2);
  v56 = add(v55, 3);
  v57 = add(v56, 4);
  v58 = add(v57, 5);
  v59 = add(v58, 6);
  v60 = add(v59, 7);
  add(v60, 20);
  v61 = mul(&v125, 10);
  v62 = sub(v61, 5);
  v63 = mul(v62, 8);
  v64 = add(v63, 9);
  add(v64, 48);
  v65 = add(&v126, 7);
  v66 = add(v65, 6);
  v67 = add(v66, 5);
  v68 = add(v67, 4);
  v69 = add(v68, 3);
  v70 = add(v69, 2);
  v71 = add(v70, 1);
  add(v71, 20);
  v72 = add(&v127, 7);
  v73 = add(v72, 2);
  v74 = add(v73, 4);
  v75 = add(v74, 3);
  v76 = add(v75, 6);
  v77 = add(v76, 5);
  v78 = add(v77, 1);
  add(v78, 20);
  v79 = mul(&v128, 1000000);
  v80 = sub(v79, 999999);
  v81 = mul(v80, 4);
  v82 = add(v81, 50);
  sub(v82, 1);
  v83 = sub(&v129, 1);
  v84 = mul(v83, -294967296);
  v85 = add(v84, 49);
  sub(v85, 1);
  v86 = sub(&v130, 1);
  v87 = mul(v86, 1000000000);
  v88 = add(v87, 54);
  v89 = sub(v88, 1);
  v90 = add(v89, 1000000000);
  sub(v90, 1000000000);
  v91 = add(&v131, 49);
  v92 = sub(v91, 1);
  v93 = add(v92, 2);
  sub(v93, 1);
  v94 = mul(&v132, 10);
  v95 = sub(v94, 5);
  v96 = mul(v95, 8);
  v97 = add(v96, 9);
  add(v97, 48);
  v98 = add(&v133, 1);
  v99 = add(v98, 3);
  v100 = add(v99, 3);
  v101 = add(v100, 3);
  v102 = add(v101, 6);
  v103 = add(v102, 6);
  v104 = add(v103, 6);
  add(v104, 20);
  v105 = add(&v134, 55);
  v106 = sub(v105, 33);
  v107 = add(v106, 44);
  v108 = sub(v107, 11);
  add(v108, 42);
  add(&v135, v134);
  add(&v136, v121);
  v115 = v136;
  v109 = sub(&v137, 1);
  v110 = add(v109, v115);
  sub(v110, 1);
  v116 = v132;
  v111 = sub(&v138, 1);
  v112 = mul(v111, 1000000);
  add(v112, v116);
  v117 = v136;
  v113 = add(&v139, 1);
  mul(v113, v117);
  add(v140, v139);
  sub_731C7F("CTF{");
  for ( j = 0; j < 32; ++j )                    // 输出flag,flag的值在数组v120中,继续向上查看对v120进行处理的函数
    sub_731C7F("%c", SLOBYTE(v120[j]));
  sub_731C7F("}\n");
  return 0;
}

(1)sub_731000()--add

Pasted image 20231220181135.png
其中有两个循环次数很大的while循环,但我们始终关注v120的值的变化即可,这里有个不常见的知识点——负数作为循环条件,这里补充一些计组知识,在CPU的ALU(算术逻辑单元)中进行计算时,数字通常都是以补码形式进行计算,正数的补码和反码都跟原码相同;负数的原码为除符号位全部取反,补码为反码末位加1,即负数的补码为原码的数值位取反后末位+1。
-1的补码为:0xFFFFFFFF,
负数减正数,实际上转换为补码的加法计算:
[-1]补-[1]补=[-1]补+[-1]补=0xFFFFFFFF+0xFFFFFFFF=0x1FFFFFFFE
而在32位系统中,int型的变量长度为4个字节,上面符号位向前进的那一位1,实际上已经向下溢出,并不存在。
所以由上可知,在循环不断的减1中,被减数持续下溢,逐渐会得到正数,最终得到0,while循环结束。

将函数逻辑简化后,发现其作用就是将参数1和参数2相加后赋值给参数1。

(2)sub_73110()--mul

Pasted image 20231220174719.png
其中调用了sub_731000()--add

将此函数逻辑简化后:发现其作用就是将参数1和参数2相乘后赋值给参数1。

(3)sub_7317220()--sub

Pasted image 20231220181438.png
同理将此函数逻辑简化后:发现其作用就是将参数1减去参数2后赋值给参数1。

3.解题

将main函数用python重写后,代码如下:、

def add(a,b):  
    return a + b  
def sub(a,b):  
    return a - b  
def mul(a,b):  
    return a * b  
  
v120 = [ 1 for i in range(32)]  
v121=0  
  
v3 = mul(v120[0], 1000000000)  
v4 = sub(v3, 999999950)  
v120[0]=mul(v4, 2)  
v5 = add(  v120[1], 5000000)  
v6 = sub(v5, 6666666)  
v7 = add(v6, 1666666)  
v8 = add(v7, 45)  
v9 = mul(v8, 2)  
v120[1]=add(v9, 5)  
v10 = mul(  v120[2], 1000000000)  
v11 = sub(v10, 999999950)  
v12 = mul(v11, 2)  
v120[2]=add(v12, 2)  
v13 = add(  v120[3], 55)  
v14 = sub(v13, 3)  
v15 = add(v14, 4)  
v120[3]=sub(v15, 1)  
v16 = mul(  v120[4], 100000000)  
v17 = sub(v16, 99999950)  
v18 = mul(v17, 2)  
v120[4]=add(v18, 2)  
v19 = sub(  v120[5], 1)  
v20 = mul(v19, 1000000000)  
v21 = add(v20, 55)  
v120[5]=sub(v21, 3)  
v22 = mul(  v120[6], 1000000)  
v23 = sub(v22, 999975)  
v120[6]=mul(v23, 4)  
v24 = add(  v120[7], 55)  
v25 = sub(v24, 33)  
v26 = add(v25, 44)  
v120[7]=sub(v26, 11)  
v27 = mul(  v120[8], 10)  
v28 = sub(v27, 5)  
v29 = mul(v28, 8)  
v120[8]=add(v29, 9)  
v30 = add(  v120[9], 0)  
v31 = sub(v30, 0)  
v32 = add(v31, 11)  
v33 = sub(v32, 11)  
v120[9]=add(v33, 53)  
v34 = add(  v120[10], 49)  
v35 = sub(v34, 2)  
v36 = add(v35, 4)  
v120[10]=sub(v36, 2)  
v37 = mul(  v120[11], 1000000)  
v38 = sub(v37, 999999)  
v39 = mul(v38, 4)  
v120[11]=add(v39, 50)  
v40 = add(  v120[12], 1)  
v41 = add(v40, 1)  
v42 = add(v41, 1)  
v43 = add(v42, 1)  
v44 = add(v43, 1)  
v45 = add(v44, 1)  
v46 = add(v45, 10)  
v120[12]=add(v46, 32)  
v47 = mul(  v120[13], 10)  
v48 = sub(v47, 5)  
v49 = mul(v48, 8)  
v50 = add(v49, 9)  
v120[13]=add(v50, 48)  
v51 = sub(  v120[14], 1)  
v52 = mul(v51, -294967296)  
v53 = add(v52, 55)  
v120[14]=sub(v53, 3)  
v54 = add(  v120[15], 1)  
v55 = add(v54, 2)  
v56 = add(v55, 3)  
v57 = add(v56, 4)  
v58 = add(v57, 5)  
v59 = add(v58, 6)  
v60 = add(v59, 7)  
v120[15]=add(v60, 20)  
v61 = mul(  v120[16], 10)  
v62 = sub(v61, 5)  
v63 = mul(v62, 8)  
v64 = add(v63, 9)  
v120[16]=add(v64, 48)  
v65 = add(  v120[17], 7)  
v66 = add(v65, 6)  
v67 = add(v66, 5)  
v68 = add(v67, 4)  
v69 = add(v68, 3)  
v70 = add(v69, 2)  
v71 = add(v70, 1)  
v120[17]=add(v71, 20)  
v72 = add(  v120[18], 7)  
v73 = add(v72, 2)  
v74 = add(v73, 4)  
v75 = add(v74, 3)  
v76 = add(v75, 6)  
v77 = add(v76, 5)  
v78 = add(v77, 1)  
v120[18]=add(v78, 20)  
v79 = mul(  v120[19], 1000000)  
v80 = sub(v79, 999999)  
v81 = mul(v80, 4)  
v82 = add(v81, 50)  
v120[19]=sub(v82, 1)  
v83 = sub(  v120[20], 1)  
v84 = mul(v83, -294967296)  
v85 = add(v84, 49)  
v120[20]=sub(v85, 1)  
v86 = sub(  v120[21], 1)  
v87 = mul(v86, 1000000000)  
v88 = add(v87, 54)  
v89 = sub(v88, 1)  
v90 = add(v89, 1000000000)  
v120[21]=sub(v90, 1000000000)  
v91 = add(  v120[22], 49)  
v92 = sub(v91, 1)  
v93 = add(v92, 2)  
v120[22]=sub(v93, 1)  
v94 = mul(  v120[23], 10)  
v95 = sub(v94, 5)  
v96 = mul(v95, 8)  
v97 = add(v96, 9)  
v120[23]=add(v97, 48)  
v98 = add(  v120[24], 1)  
v99 = add(v98, 3)  
v100 = add(v99, 3)  
v101 = add(v100, 3)  
v102 = add(v101, 6)  
v103 = add(v102, 6)  
v104 = add(v103, 6)  
v120[24]=add(v104, 20)  
v105 = add(  v120[25], 55)  
v106 = sub(v105, 33)  
v107 = add(v106, 44)  
v108 = sub(v107, 11)  
v120[25]=add(v108, 42)  
v120[26]=add(  v120[26], v120[25])  
v120[27]=add(  v120[27],  v120[12])  
v115 = v120[27]  
v109 = sub(  v120[28], 1)  
v110 = add(v109, v115)  
v120[28]=sub(v110, 1)  
v116 = v120[23]  
v111 = sub(  v120[29], 1)  
v112 = mul(v111, 1000000)  
v120[29]=add(v112, v116)  
v117 = v120[27]  
v113 = add(  v120[30], 1)  
v120[30]=mul(v113, v117)  
v120[31]=add(v120[31], v120[30])  
  
print("CTF{"+"".join(map(chr,v120))+"}")

运行得到flag:
CTF{daf8f4d816261a41a115052a1bc21ade}

CTF RE XCTF while循环 补码 原码 负数
Theme Jasmine by Kent Liao