Reverse(十一)

Newbie_calculations

主函数一堆唬人的计算函数,点开进去可以看到大量循环

int __cdecl main(int argc, const char **argv, const char **envp)
{
  _DWORD *v3; // eax
  _DWORD *v4; // eax
  int v5; // eax
  int v6; // eax
  int v7; // eax
  _DWORD *v8; // eax
  _DWORD *v9; // eax
  _DWORD *v10; // eax
  _DWORD *v11; // eax
  _DWORD *v12; // eax
  int v13; // eax
  int v14; // eax
  int v15; // eax
  _DWORD *v16; // eax
  _DWORD *v17; // eax
  _DWORD *v18; // eax
  _DWORD *v19; // eax
  _DWORD *v20; // eax
  int v21; // eax
  _DWORD *v22; // eax
  _DWORD *v23; // eax
  int v24; // eax
  int v25; // eax
  int v26; // eax
  _DWORD *v27; // eax
  _DWORD *v28; // eax
  _DWORD *v29; // eax
  int v30; // eax
  int v31; // eax
  int v32; // eax
  int v33; // eax
  int v34; // eax
  int v35; // eax
  int v36; // eax
  _DWORD *v37; // eax
  _DWORD *v38; // eax
  _DWORD *v39; // eax
  int v40; // eax
  int v41; // eax
  int v42; // eax
  int v43; // eax
  int v44; // eax
  int v45; // eax
  int v46; // eax
  _DWORD *v47; // eax
  _DWORD *v48; // eax
  _DWORD *v49; // eax
  int v50; // eax
  _DWORD *v51; // eax
  _DWORD *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
  _DWORD *v61; // eax
  _DWORD *v62; // eax
  _DWORD *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
  _DWORD *v79; // eax
  _DWORD *v80; // eax
  _DWORD *v81; // eax
  int v82; // eax
  _DWORD *v83; // eax
  _DWORD *v84; // eax
  int v85; // eax
  _DWORD *v86; // eax
  _DWORD *v87; // eax
  int v88; // eax
  int v89; // eax
  int v90; // eax
  int v91; // eax
  int v92; // eax
  int v93; // eax
  _DWORD *v94; // eax
  _DWORD *v95; // eax
  _DWORD *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
  _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
  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;
  v140[1] = 0;
  puts("Your flag is:");
  v3 = sub_401100(v120, 1000000000);
  v4 = sub_401220(v3, 999999950);
  sub_401100(v4, 2);
  v5 = sub_401000(&v120[1], 5000000);
  v6 = sub_401220(v5, 6666666);
  v7 = sub_401000(v6, 1666666);
  v8 = sub_401000(v7, 45);
  v9 = sub_401100(v8, 2);
  sub_401000(v9, 5);
  v10 = sub_401100(&v120[2], 1000000000);
  v11 = sub_401220(v10, 999999950);
  v12 = sub_401100(v11, 2);
  sub_401000(v12, 2);
  v13 = sub_401000(&v120[3], 55);
  v14 = sub_401220(v13, 3);
  v15 = sub_401000(v14, 4);
  sub_401220(v15, 1);
  v16 = sub_401100(&v120[4], 100000000);
  v17 = sub_401220(v16, 99999950);
  v18 = sub_401100(v17, 2);
  sub_401000(v18, 2);
  v19 = sub_401220(&v120[5], 1);
  v20 = sub_401100(v19, 1000000000);
  v21 = sub_401000(v20, 55);
  sub_401220(v21, 3);
  v22 = sub_401100(&v120[6], 1000000);
  v23 = sub_401220(v22, 999975);
  sub_401100(v23, 4);
  v24 = sub_401000(&v120[7], 55);
  v25 = sub_401220(v24, 33);
  v26 = sub_401000(v25, 44);
  sub_401220(v26, 11);
  v27 = sub_401100(&v120[8], 10);
  v28 = sub_401220(v27, 5);
  v29 = sub_401100(v28, 8);
  sub_401000(v29, 9);
  v30 = sub_401000(&v120[9], 0);
  v31 = sub_401220(v30, 0);
  v32 = sub_401000(v31, 11);
  v33 = sub_401220(v32, 11);
  sub_401000(v33, 53);
  v34 = sub_401000(&v120[10], 49);
  v35 = sub_401220(v34, 2);
  v36 = sub_401000(v35, 4);
  sub_401220(v36, 2);
  v37 = sub_401100(&v120[11], 1000000);
  v38 = sub_401220(v37, 999999);
  v39 = sub_401100(v38, 4);
  sub_401000(v39, 50);
  v40 = sub_401000(&v121, 1);
  v41 = sub_401000(v40, 1);
  v42 = sub_401000(v41, 1);
  v43 = sub_401000(v42, 1);
  v44 = sub_401000(v43, 1);
  v45 = sub_401000(v44, 1);
  v46 = sub_401000(v45, 10);
  sub_401000(v46, 32);
  v47 = sub_401100(&v122, 10);
  v48 = sub_401220(v47, 5);
  v49 = sub_401100(v48, 8);
  v50 = sub_401000(v49, 9);
  sub_401000(v50, 48);
  v51 = sub_401220(&v123, 1);
  v52 = sub_401100(v51, -294967296);
  v53 = sub_401000(v52, 55);
  sub_401220(v53, 3);
  v54 = sub_401000(&v124, 1);
  v55 = sub_401000(v54, 2);
  v56 = sub_401000(v55, 3);
  v57 = sub_401000(v56, 4);
  v58 = sub_401000(v57, 5);
  v59 = sub_401000(v58, 6);
  v60 = sub_401000(v59, 7);
  sub_401000(v60, 20);
  v61 = sub_401100(&v125, 10);
  v62 = sub_401220(v61, 5);
  v63 = sub_401100(v62, 8);
  v64 = sub_401000(v63, 9);
  sub_401000(v64, 48);
  v65 = sub_401000(&v126, 7);
  v66 = sub_401000(v65, 6);
  v67 = sub_401000(v66, 5);
  v68 = sub_401000(v67, 4);
  v69 = sub_401000(v68, 3);
  v70 = sub_401000(v69, 2);
  v71 = sub_401000(v70, 1);
  sub_401000(v71, 20);
  v72 = sub_401000(&v127, 7);
  v73 = sub_401000(v72, 2);
  v74 = sub_401000(v73, 4);
  v75 = sub_401000(v74, 3);
  v76 = sub_401000(v75, 6);
  v77 = sub_401000(v76, 5);
  v78 = sub_401000(v77, 1);
  sub_401000(v78, 20);
  v79 = sub_401100(&v128, 1000000);
  v80 = sub_401220(v79, 999999);
  v81 = sub_401100(v80, 4);
  v82 = sub_401000(v81, 50);
  sub_401220(v82, 1);
  v83 = sub_401220(&v129, 1);
  v84 = sub_401100(v83, -294967296);
  v85 = sub_401000(v84, 49);
  sub_401220(v85, 1);
  v86 = sub_401220(&v130, 1);
  v87 = sub_401100(v86, 1000000000);
  v88 = sub_401000(v87, 54);
  v89 = sub_401220(v88, 1);
  v90 = sub_401000(v89, 1000000000);
  sub_401220(v90, 1000000000);
  v91 = sub_401000(&v131, 49);
  v92 = sub_401220(v91, 1);
  v93 = sub_401000(v92, 2);
  sub_401220(v93, 1);
  v94 = sub_401100(&v132, 10);
  v95 = sub_401220(v94, 5);
  v96 = sub_401100(v95, 8);
  v97 = sub_401000(v96, 9);
  sub_401000(v97, 48);
  v98 = sub_401000(&v133, 1);
  v99 = sub_401000(v98, 3);
  v100 = sub_401000(v99, 3);
  v101 = sub_401000(v100, 3);
  v102 = sub_401000(v101, 6);
  v103 = sub_401000(v102, 6);
  v104 = sub_401000(v103, 6);
  sub_401000(v104, 20);
  v105 = sub_401000(&v134, 55);
  v106 = sub_401220(v105, 33);
  v107 = sub_401000(v106, 44);
  v108 = sub_401220(v107, 11);
  sub_401000(v108, 42);
  sub_401000(&v135, v134);
  sub_401000(&v136, v121);
  v115 = v136;
  v109 = sub_401220(&v137, 1);
  v110 = sub_401000(v109, v115);
  sub_401220(v110, 1);
  v116 = v132;
  v111 = sub_401220(&v138, 1);
  v112 = sub_401100(v111, 1000000);
  sub_401000(v112, v116);
  v117 = v136;
  v113 = sub_401000(&v139, 1);
  sub_401100(v113, v117);
  sub_401000(v140, v139);
  sub_401C7F("CTF{");
  for ( j = 0; j < 32; ++j )
    sub_401C7F("%c", SLOBYTE(v120[j]));
  sub_401C7F("}\n");
  return 0;
}

实际分析只有三个函数:sub_401000、sub_401100、sub_401220;下面静态详细分析代码

_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;
  v6 = 1231;                                    // 没用
  v5 = a2 + 1231;                               // 没用
  while ( v3 )                                  // 这里要考虑a2情况,如果a2=2^31,那么v3=2^31-1,共减2^31-1;其他v3=-a2,共减2^32-a2-1;结合来算相当于2^32-a2-1
  {
    ++v6;
    --*a1;                                      // a1指向的数减1
    --v3;
    --v5;
  }
  while ( v4 )                                  // 减到-2^31,再-1溢出得到2^31-1,之后减到0:共减2^32-2
  {
    --v5;
    ++*a1;                                      // a1指向的数加1
    --v4;
  }                                             // 综合上述两个循环a1-2^32+a2+1+2^32-2=a1+a2-1
  ++*a1;
  return a1;                                    // a1+a2
}
int *__cdecl sub_401100(int *a1, int a2)
{
  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 = *a1;
  v5 = a2;                                      // 没用
  v3 = -1;
  v8 = 0;
  v7 = a2 * v4;                                 // 没用
  while ( a2 )
  {
    v6 = v7 * v4;
    sub_401000(&v8, *a1);                       // v8=v8+a1
    ++v7;
    --a2;
    v5 = v6 - 1;
  }                                             // 由于v8初始为0,相当于v8=a2*a1
  while ( v3 )                                  // 但是还是要分类讨论:a2为正数,加a2次,v8=(a2*a1+2^31)%(2^32-1)-2^31;a2为负数,减2^32+a2-1次,v8=((2^32+a2-1)*a1+2^31)%(2^32-1)-2^31=(a2*a1+2^31)%(2^32-1)-2^31
  {
    ++v7;
    ++*a1;
    --v3;
    --v5;
  }
  ++*a1;
  *a1 = v8;                                     // 直接说明最后一个循环没用
  return a1;                                    // a1=(a2*a1+2^31)%(2^32-1)-2^31=a1*a2
}
_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 )                                  // 这里要考虑a2情况,如果a2=2^31,那么v3=2^31-1,共减2^31-1;其他v3=-a2,共减2^32-a2-1;结合来算相当于2^32-a2-1
  {
    ++*a1;
    --v3;
    --v5;
  }
  v6 = v5 * v5;
  while ( v4 )                                  // 减到-2^31,再-1溢出得到2^31-1,之后减到0:共减2^32-2
  {
    v6 *= 123;
    ++*a1;
    --v4;
  }                                             // 综合上述两个循环(a1+2^32-a2-1+2^32-2)%(2^32-1)=a1-a2-1
  ++*a1;
  return a1;                                    // a1-a2
}

上面三个函数特别绕,涉及大量溢出计算,但可以简化为a1+a2、a1*a2、a1-a2

第一版代码:由于不涉及地址传参,改动很大,特别不好

#include<iostream>
using namespace std;

int sub_401000(int a, int b) {
    return a + b;
}

int sub_401220(int a, int b) {
    return a - b;
}

int sub_401100(int a, int b) {
    return a * b;
}

int __cdecl 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[32]; // [esp+Ch] [ebp-88h] BYREF

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

第二版代码:按照原来模式进行地址传参,这个代码相对好些

#include<iostream>
using namespace std;

int *sub_401000(int *a, int b) {
    *a = *a + b;
    return a;
}

int *sub_401220(int *a, int b) {
    *a = *a - b;
    return a;
}

int *sub_401100(int *a, int b) {
    *a = *a * b;
    return a;
}

int __cdecl 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[32]; // [esp+Ch] [ebp-88h] BYREF

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

easyre-153

这道题目生动的说明了选择错误版本ida是如何急剧增大题目难度的

ida7.7

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int pipedes[2]; // [esp+18h] [ebp-38h] BYREF
  __pid_t v5; // [esp+20h] [ebp-30h]
  int v6; // [esp+24h] [ebp-2Ch] BYREF
  char buf[30]; // [esp+2Eh] [ebp-22h] BYREF
  unsigned int v8; // [esp+4Ch] [ebp-4h]

  v8 = __readgsdword(0x14u);
  pipe(pipedes);
  v5 = fork();
  if ( !v5 )
  {
    puts("\nOMG!!!! I forgot kid's id");
    write(pipedes[1], "69800876143568214356928753", 0x1Du);
    puts("Ready to exit     ");
    exit(0);
  }
  read(pipedes[0], buf, 0x1Du);
  __isoc99_scanf("%d", &v6);
  if ( v6 == v5 )
  {
    if ( (unsigned __int8)*(_DWORD *)((char *)lol + 3) == 204 )
    {
      puts(":D");
      exit(1);
    }
    printf("\nYou got the key\n ");
    lol(buf);
  }
  wait(0);
  return 0;
}

int lol()
{
  return printf("flag_is_not_here");
}

ida7.0

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int pipedes[2]; // [esp+18h] [ebp-38h]
  __pid_t v5; // [esp+20h] [ebp-30h]
  int v6; // [esp+24h] [ebp-2Ch]
  char buf; // [esp+2Eh] [ebp-22h]
  unsigned int v8; // [esp+4Ch] [ebp-4h]

  v8 = __readgsdword(0x14u);
  pipe(pipedes);
  v5 = fork();
  if ( !v5 )
  {
    puts("\nOMG!!!! I forgot kid's id");
    write(pipedes[1], "69800876143568214356928753", 0x1Du);
    puts("Ready to exit     ");
    exit(0);
  }
  read(pipedes[0], &buf, 0x1Du);
  __isoc99_scanf("%d", &v6);
  if ( v6 == v5 )
  {
    if ( (*(_DWORD *)((_BYTE *)lol + 3) & 0xFF) == 204 )
    {
      puts(":D");
      exit(1);
    }
    printf("\nYou got the key\n ");
    lol(&buf);
  }
  wait(0);
  return 0;
}

int __cdecl lol(_BYTE *a1)
{
  char v2; // [esp+15h] [ebp-13h]
  char v3; // [esp+16h] [ebp-12h]
  char v4; // [esp+17h] [ebp-11h]
  char v5; // [esp+18h] [ebp-10h]
  char v6; // [esp+19h] [ebp-Fh]
  char v7; // [esp+1Ah] [ebp-Eh]
  char v8; // [esp+1Bh] [ebp-Dh]

  v2 = 2 * a1[1];
  v3 = a1[4] + a1[5];
  v4 = a1[8] + a1[9];
  v5 = 2 * a1[12];
  v6 = a1[18] + a1[17];
  v7 = a1[10] + a1[21];
  v8 = a1[9] + a1[25];    // 下面不是flag说明这几个字符是
  return printf("flag_is_not_here");
}

可以看到更新的版本在反编译过程中忽略了很多,反而老点的版本反编译较完整,看了网上的wp分析是新版ida会识别无效汇编代码不进行反编译

吸取经验:代码逻辑很怪做不出来换旧版本ida试试

image-20240201145019807.png

外面加个RCTF{}

testre

ida64打开

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  void *ptr; // [rsp+10h] [rbp-30h]
  __int64 v5; // [rsp+18h] [rbp-28h] BYREF
  char v6[28]; // [rsp+20h] [rbp-20h] BYREF
  int v7; // [rsp+3Ch] [rbp-4h]

  v7 = 0;
  v5 = 256LL;
  sub_400D00((__int64)v6, 0x11uLL);             // v6接受了输入的字符串
  ptr = malloc(0x100uLL);
  sub_400700(ptr, &v5, (__int64)v6, 0x10uLL);   // 加密比较
  free(ptr);
  return 0LL;
}

__int64 __fastcall sub_400D00(__int64 a1, unsigned __int64 a2)
{
  char buf; // [rsp+17h] [rbp-19h] BYREF
  unsigned __int64 i; // [rsp+18h] [rbp-18h]
  unsigned __int64 v5; // [rsp+20h] [rbp-10h]
  __int64 v6; // [rsp+28h] [rbp-8h]

  v6 = a1;
  v5 = a2;                                      // 17
  for ( i = 0LL; i < v5; ++i )
  {
    read(0, &buf, 1uLL);                        // 每次输入一个字符
    *(_BYTE *)(v6 + i) = buf;                   // 赋给v6
  }
  *(_BYTE *)(v6 + v5 - 1) = 0;                  // 取前16个字符
  fflush(stdout);
  return (unsigned int)i;
}

关键函数的详细分析,其中存在大量冗余代码,很明显是混淆

__int64 __fastcall sub_400700(void *a1, _QWORD *a2, __int64 a3, size_t a4)
{
  unsigned __int8 *v4; // rcx
  _DWORD v6[2]; // [rsp+0h] [rbp-C0h] BYREF
  int c; // [rsp+8h] [rbp-B8h]
  char v8; // [rsp+Fh] [rbp-B1h]
  int v9; // [rsp+10h] [rbp-B0h]
  bool v10; // [rsp+17h] [rbp-A9h]
  unsigned __int8 *v11; // [rsp+18h] [rbp-A8h]
  char v12; // [rsp+27h] [rbp-99h]
  int v13; // [rsp+28h] [rbp-98h]
  int v14; // [rsp+2Ch] [rbp-94h]
  unsigned __int64 i; // [rsp+30h] [rbp-90h]
  size_t n; // [rsp+38h] [rbp-88h]
  size_t v17; // [rsp+40h] [rbp-80h]
  size_t v18; // [rsp+48h] [rbp-78h]
  size_t j; // [rsp+50h] [rbp-70h]
  size_t v20; // [rsp+58h] [rbp-68h]
  int v21; // [rsp+64h] [rbp-5Ch]
  unsigned __int64 v22; // [rsp+68h] [rbp-58h]
  int v23; // [rsp+74h] [rbp-4Ch]
  _DWORD *v24; // [rsp+78h] [rbp-48h]
  __int64 v25; // [rsp+80h] [rbp-40h]
  void *v26; // [rsp+88h] [rbp-38h]
  int v27; // [rsp+94h] [rbp-2Ch]
  size_t v28; // [rsp+98h] [rbp-28h]
  __int64 v29; // [rsp+A0h] [rbp-20h]
  _QWORD *v30; // [rsp+A8h] [rbp-18h]
  void *s; // [rsp+B0h] [rbp-10h]
  char v32; // [rsp+BFh] [rbp-1h]

  s = a1;
  v30 = a2;
  v29 = a3;
  v28 = a4;                                     // 16
  v27 = -559038737;
  v26 = malloc(0x100uLL);
  v25 = v29;
  v24 = v6;
  v22 = 0LL;
  v17 = 0LL;
  for ( i = 0LL; i < v28; ++i )
  {
    v13 = *(unsigned __int8 *)(v25 + i);
    *((_BYTE *)v26 + i) = byte_400E90[i % 0x1D] ^ v13;// fake_secret_makes_you_annoyed,这里已经对混淆有所暗示
    *((_BYTE *)v26 + i) += *(_BYTE *)(v25 + i);    // 没用
  }
  while ( 1 )
  {
    v12 = 0;
    if ( v17 < v28 )
      v12 = ~(*(_BYTE *)(v25 + v17) != 0);      // 遍历字符,实际上第一字符不等于0,因此v12=0
    if ( (v12 & 1) == 0 )                       // 直接break
      break;
    ++v17;
  }                                             // v17=0
  n = 138 * (v28 - v17) / 0x64 + 1;             // 23(而最终比较字符长度是22,有联系)
  v23 = ((v17 + v28) << 6) / 0x30 - 1;          // 后面都没用到算它干啥
  v11 = (unsigned __int8 *)v6 - ((138 * (v28 - v17) / 0x64 + 16) & 0xFFFFFFFFFFFFFFF0LL);// 不知所云
  memset(v11, 0, n);
  v20 = v17;                                    // 0
  v18 = n - 1;                                  // 22
  while ( v20 < v28 )                           // 可以看到遍历v11,对应后面的s
  {
    v21 = *(unsigned __int8 *)(v25 + v20);      // 遍历输入字符串
    for ( j = n - 1; ; --j )
    {
      v10 = 1;
      if ( j <= v18 )
        v10 = v21 != 0;
      if ( !v10 )
        break;
      v22 = v11[j] << 6;                        // v22也是混淆
      v21 += v11[j] << 8;
      v9 = 64;                                  // 混淆
      v11[j] = v21 % 58;                        // base58特征
      *((_BYTE *)v26 + j) = v22 & 0x3F;         // 后面都没有引用,可以很明显看出来是混淆代码
      v22 >>= 6;                                // 混淆
      v21 /= 58;
      v27 /= v9;                                // 混淆
      if ( !j )
        break;
    }
    ++v20;
    v18 = j;
  }
  for ( j = 0LL; ; ++j )                        // 和上面一样,没用
  {
    v8 = 0;
    if ( j < n )
      v8 = ~(v11[j] != 0);
    if ( (v8 & 1) == 0 )
      break;
  }
  if ( *v30 > n + v17 - j )                     // v30值为256,毫无疑问会进入if
  {
    if ( v17 )                                  // v17=0,所以压根不会进去
    {
      c = 61;
      memset(s, 49, v17);
      memset(v26, c, v17);
    }
    v20 = v17;
    while ( j < n )
    {
      v4 = v11;
      *((_BYTE *)s + v20) = byte_400EB0[v11[j]];// 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
      *((_BYTE *)v26 + v20++) = byte_400EF0[v4[j++]];// ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/,用base64来混淆
    }
    *((_BYTE *)s + v20) = 0;
    *v30 = v20 + 1;                             // 没用
    if ( !strncmp((const char *)s, "D9", 2uLL)
      && !strncmp((const char *)s + 20, "Mp", 2uLL)
      && !strncmp((const char *)s + 18, "MR", 2uLL)
      && !strncmp((const char *)s + 2, "cS9N", 4uLL)
      && !strncmp((const char *)s + 6, "9iHjM", 5uLL)
      && !strncmp((const char *)s + 11, "LTdA8YS", 7uLL) )// s="D9cS9N9iHjMLTdA8YSMRMp"
    {
      v6[1] = puts("correct!");
    }
    v32 = 1;                                    // 没用
    v14 = 1;
  }
  else                                          // 没用
  {
    *v30 = n + v17 - j + 1;
    v32 = 0;
    v14 = 1;
  }
  return v32 & 1;
}

去掉混淆代码后如下:

__int64 __fastcall sub_400700(void *a1, _QWORD *a2, __int64 a3, size_t a4)
{
    bool v10; // [rsp+17h] [rbp-A9h]
    unsigned __int8 *v11; // [rsp+18h] [rbp-A8h]
    size_t n; // [rsp+38h] [rbp-88h]
    size_t v17; // [rsp+40h] [rbp-80h]
    size_t v18; // [rsp+48h] [rbp-78h]
    size_t j; // [rsp+50h] [rbp-70h]
    size_t v20; // [rsp+58h] [rbp-68h]
    int v21; // [rsp+64h] [rbp-5Ch]
    __int64 v25; // [rsp+80h] [rbp-40h]
    size_t v28; // [rsp+98h] [rbp-28h]
    __int64 v29; // [rsp+A0h] [rbp-20h]
    void *s; // [rsp+B0h] [rbp-10h]

    s = a1;
    v29 = a3;
    v28 = a4;                                     // 16
    v25 = v29;
    v17 = 0LL;
    n = 138 * (v28 - v17) / 0x64 + 1;             // 23(而最终比较字符长度是22,有联系)
    memset(v11, 0, n);                              // 初始化
    v20 = v17;                                    // 0
    v18 = n - 1;                                  // 22
    while ( v20 < v28 )                           // 可以看到遍历v11,对应后面的s
      {
        v21 = *(unsigned __int8 *)(v25 + v20);      // 遍历输入字符串
        for ( j = n - 1; ; --j )
        {
            v10 = 1;
            if ( j <= v18 )
                v10 = v21 != 0;
            if ( !v10 )
                break;
            v21 += v11[j] << 8;
            v11[j] = v21 % 58;                        // base58特征
            v21 /= 58;
            if ( !j )
                break;
        }
        ++v20;
        v18 = j;
    }    
    j = 0;    // 原来的for循环初始化了
    v20 = v17;
    while ( j < n )
    {
        *((_BYTE *)s + v20) = byte_400EB0[v11[j]];// 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
        v20++;
        j++;
    }
    *((_BYTE *)s + v20) = 0;
    if ( !strncmp((const char *)s, "D9", 2uLL)
      && !strncmp((const char *)s + 20, "Mp", 2uLL)
      && !strncmp((const char *)s + 18, "MR", 2uLL)
      && !strncmp((const char *)s + 2, "cS9N", 4uLL)
      && !strncmp((const char *)s + 6, "9iHjM", 5uLL)
      && !strncmp((const char *)s + 11, "LTdA8YS", 7uLL) )// s="D9cS9N9iHjMLTdA8YSMRMp"
    {
      v6[1] = puts("correct!");
    }
    return v32 & 1;
}

基本可以看出对输入做了base58加密并和"D9cS9N9iHjMLTdA8YSMRMp"对比

image-20240201163337977.png

crazy百越杯2018

ida64找到主函数,可以发现很难阅读代码,是因为是使用了C++的STL;为了方便分析代码,已省略无关代码

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::string::basic_string((__int64)v18, (__int64)argv, (__int64)envp);
  std::operator>><char>(&std::cin, v18);    // cin说明是输入
  HighTemplar::HighTemplar((DarkTemplar *)v23, (__int64)v18);
  HighTemplar::calculate((HighTemplar *)v23);    // 字面理解是做了计算处理
  if ( !(unsigned int)HighTemplar::getSerial((HighTemplar *)v23) )    // getSerial字面意思是获取序列号,是判断函数
  {
    HighTemplar::getFlag[abi:cxx11](v22, v23);
    v14 = std::operator<<<std::char_traits<char>>(&std::cout, "flag{");    // 打印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::string::~string(v22);
  }
  HighTemplar::~HighTemplar((HighTemplar *)v23);
  std::string::~string(v18);
  return 0;
}

第一个函数:将输入转移到第一个参数中

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::string::basic_string((char *)a1 + 16, a2);    //拼接操作?
  std::string::basic_string((char *)a1 + 48, a2);
  std::allocator<char>::allocator(&v3);
  std::string::basic_string((char *)a1 + 80, "327a6c4304ad5938eaf0efb6cc3e53dc", &v3);    // 初始16位 + 输入字符串(32位)* 2 + "327a6c4304ad5938eaf0efb6cc3e53dc"
  std::allocator<char>::~allocator(&v3);
  return __readfsqword(0x28u) ^ v4;
}

第二个函数:v23加密

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::string::length((char *)this + 16) != 32 )// 长度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::string::length((char *)this + 16); ++i )
  {
    v2 = (_BYTE *)std::string::operator[]((char *)this + 16, i);    // 先获取地址
    *v2 = (*(_BYTE *)std::string::operator[]((char *)this + 16, i) ^ 0x50) + 23;    // 对字符串做了个加密处理,原来的也变化
  }
  for ( j = 0; ; ++j )
  {
    result = j <= (unsigned __int64)std::string::length((char *)this + 16);
    if ( !result )
      break;
    v4 = (_BYTE *)std::string::operator[]((char *)this + 16, j);
    *v4 = (*(_BYTE *)std::string::operator[]((char *)this + 16, j) ^ 0x13) + 11;    // 又对字符串做了个加密处理
  }
  return result;
}

第三个函数:

__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::string::length((char *)this + 16); ++i )
  {
    v1 = *(_BYTE *)std::string::operator[]((char *)this + 80, (int)i);    // 遍历第一个函数里的"327a6c4304ad5938eaf0efb6cc3e53dc"
    if ( v1 != *(_BYTE *)std::string::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);
}

至此很简单了,逆向计算回去就可以了。==注意异或运算符优先性较低,一定要带上括号!!!==

image-20240201175153293.png

Replace

ida分析

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char Buffer[40]; // [esp+4h] [ebp-2Ch] BYREF

  memset(Buffer, 0, sizeof(Buffer));
  printf("Welcome The System\nPlease Input Key:");
  gets_s(Buffer, 0x28u);
  if ( strlen(Buffer) - 35 <= 2 )               // 长度不超过37
  {
    if ( sub_401090(Buffer) == 1 )
      printf("Well Done!\n");
    else
      printf("Your Wrong!\n");
  }
  return 0;
}

int __fastcall sub_401090(int a1, int a2)
{
  int v4; // edx
  char v5; // al
  int v6; // esi
  int v7; // edi
  char v8; // al
  int v9; // eax
  char v10; // cl
  int v11; // eax
  int v12; // ecx

  if ( a2 != 35 )
    return -1;
  v4 = 0;
  while ( 1 )
  {
    v5 = *(_BYTE *)(v4 + a1);                   // 遍历字符串
    v6 = (v5 >> 4) % 16;
    v7 = ((16 * v5) >> 4) % 16;                 // v5%16
    v8 = byte_402150[2 * v4];                   // 实际不够,应该是溢出取到byte_402151
    if ( v8 < 48 || v8 > 57 )
      v9 = v8 - 87;
    else
      v9 = v8 - 48;
    v10 = byte_402151[2 * v4];                  // a49f69c38395cde96d6de96d6f4e025484954d6195448def6e2dad67786e21d5adae6
    v11 = 16 * v9;
    if ( v10 < 48 || v10 > 57 )
      v12 = v10 - 87;
    else
      v12 = v10 - 48;
    if ( (unsigned __int8)byte_4021A0[16 * v6 + v7] != ((v11 + v12) ^ 0x19) )
      break;
    if ( ++v4 >= 35 )
      return 1;
  }
  return -1;
}

查看byte_402150发现根本不够,因此只可能是溢出到下一个里

image-20240201220833098.png

搞定这个问题就可以照着原来的代码编写脚本解密

#include<iostream>
using namespace std;

int __fastcall main()
{
  int a1;
  int v4; // edx
  char v5; // al
  int v6; // esi
  int v7; // edi
  char v8; // al
  int v9; // eax
  char v10; // cl
  int v11; // eax
  int v12; // ecx

  v4 = 0;
  char byte_402150[] = "2a49f69c38395cde96d6de96d6f4e025484954d6195448def6e2dad67786e21d5adae6";
  char byte_402151[] = "a49f69c38395cde96d6de96d6f4e025484954d6195448def6e2dad67786e21d5adae6";
  unsigned char byte_4021A0[256] = {
    0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 
    0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 
    0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 
    0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, 
    0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 
    0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 
    0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 
    0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 
    0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, 
    0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, 
    0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 
    0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, 
    0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 
    0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 
    0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, 
    0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
};
  while ( 1 )
  {
    // v5 = *(_BYTE *)(v4 + a1);                   // 遍历字符串
    // v6 = (v5 >> 4) % 16;                     // v6 = v5 / 16
    // v7 = ((16 * v5) >> 4) % 16;              // v7 = v5 % 16 所以16 * v6 + v7就是v5
    v8 = byte_402150[2 * v4];                   // 实际不够,应该是溢出取到byte_402151
    if ( v8 < 48 || v8 > 57 )
      v9 = v8 - 87;
    else
      v9 = v8 - 48;
    v10 = byte_402151[2 * v4];                  // a49f69c38395cde96d6de96d6f4e025484954d6195448def6e2dad67786e21d5adae6
    v11 = 16 * v9;
    if ( v10 < 48 || v10 > 57 )
      v12 = v10 - 87;
    else
      v12 = v10 - 48;
    for (int i = 0; i < 256; i++) {
      if ( (unsigned __int8)byte_4021A0[i] == ((v11 + v12) ^ 0x19) ) {
        printf("%c", i);
        break;
      }
    }
    if ( ++v4 >= 35 )
      return 1;
  }
  printf("\n");
  return -1;    // flag{Th1s_1s_Simple_Rep1ac3_Enc0d3}
}

Mine- XSCTF

exe打开可以看到要完成扫雷游戏,ida64分析主函数找到能够成功打通游戏到最后ans的地方

int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned int v3; // eax
  std::ostream *v4; // rax
  std::ostream *v5; // rax
  _BYTE *v6; // rax
  std::istream *v7; // rax
  std::ostream *v8; // rax
  std::ostream *v9; // rax
  std::ostream *v10; // rax
  std::ostream *v11; // rax
  int v13; // [rsp+28h] [rbp-58h]
  int v14; // [rsp+2Ch] [rbp-54h]
  int v15; // [rsp+30h] [rbp-50h]
  unsigned int v16; // [rsp+34h] [rbp-4Ch]
  unsigned int v17; // [rsp+38h] [rbp-48h]
  int v18; // [rsp+3Ch] [rbp-44h]
  int v19; // [rsp+40h] [rbp-40h]
  int i3; // [rsp+44h] [rbp-3Ch]
  int i2; // [rsp+48h] [rbp-38h]
  int i1; // [rsp+4Ch] [rbp-34h]
  int nn; // [rsp+50h] [rbp-30h]
  int i4; // [rsp+54h] [rbp-2Ch]
  int mm; // [rsp+58h] [rbp-28h]
  int kk; // [rsp+5Ch] [rbp-24h]
  int jj; // [rsp+60h] [rbp-20h]
  int ii; // [rsp+64h] [rbp-1Ch]
  int n; // [rsp+68h] [rbp-18h]
  int m; // [rsp+6Ch] [rbp-14h]
  int k; // [rsp+70h] [rbp-10h]
  int v32; // [rsp+74h] [rbp-Ch]
  int j; // [rsp+78h] [rbp-8h]
  int i; // [rsp+7Ch] [rbp-4h]

  _main();
  memset(grid, 0, 0x190ui64);
  memset(randMark, 0, 0x9C40ui64);
  memset(vis, 0, sizeof(vis));
  for ( i = 0; i <= 9; ++i )    // 初始化扫雷矩阵
  {
    for ( j = 0; j <= 9; ++j )
      showUs[100 * i + j] = '*';
  }
  v3 = time(0i64);
  srand(v3);
  v32 = 0;
  do    // 随机设置雷
  {
    v19 = rand() % 10;
    v18 = rand() % 10;
    if ( randMark[100 * v19 + v18] != 1 )
    {
      randMark[100 * v19 + v18] = 1;
      ++v32;
    }
  }
  while ( v32 != mine_sum );
  res = 0;
  for ( k = 0; k <= 9; ++k )
  {
    for ( m = 0; m <= 9; ++m )
    {
      if ( randMark[100 * k + m] )
        grid[10 * k + m] = -1;
    }
  }
  for ( n = 0; n <= 9; ++n )
  {
    for ( ii = 0; ii <= 9; ++ii )
    {
      if ( grid[10 * n + ii] != -1 )
      {
        for ( jj = 0; jj <= 7; ++jj )
        {
          v17 = *((_DWORD *)&dir + 2 * jj) + n;
          v16 = dword_475044[2 * jj] + ii;
          if ( v17 <= 9 && v16 <= 9 && grid[10 * v17 + v16] == -1 )
            ++grid[10 * n + ii];
        }
      }
    }
  }
  for ( kk = 0; kk <= 9; ++kk )    // 打印扫雷矩阵
  {
    for ( mm = 0; mm <= 9; ++mm )
    {
      v4 = (std::ostream *)std::operator<<<std::char_traits<char>>(
                             refptr__ZSt4cout,
                             (unsigned int)(char)showUs[100 * kk + mm]);
      std::operator<<<std::char_traits<char>>(v4, " ");
    }
    refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(refptr__ZSt4cout);
  }
LABEL_40:    // 核心,后面清屏会重新跳转到这里,说明是无雷继续输入
  v5 = (std::ostream *)std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, (char *)&byte_48B002);
  refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(v5);
  while ( 100 - mine_sum != res )    // 输入前要先判断是否扫完所有雷,所以这里是突破点,动态调试可以断在这里跳过循环
  {
    v7 = (std::istream *)std::istream::operator>>(refptr__ZSt3cin);// 输入
    std::istream::operator>>(v7);
    if ( grid[10 * v14 + v13] == -1 )
    {
      v8 = (std::ostream *)std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, (char *)&byte_48B01D);
      refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(v8);
      goto LABEL_67;    // 有雷就会退出程序
    }
    if ( !vis[100 * v14 + v13] && grid[10 * v14 + v13] > 0 )
    {
      ++res;
      vis[100 * v14 + v13] = 1;
      showUs[100 * v14 + v13] = LOBYTE(grid[10 * v14 + v13]) + 48;
      system("cls");                            // 清屏重新打印
      for ( nn = 0; nn <= 9; ++nn )
      {
        for ( i1 = 0; i1 <= 9; ++i1 )
        {
          v9 = (std::ostream *)std::operator<<<std::char_traits<char>>(
                                 refptr__ZSt4cout,
                                 (unsigned int)(char)showUs[100 * nn + i1]);
          std::operator<<<std::char_traits<char>>(v9, " ");
        }
        refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(refptr__ZSt4cout);
      }
      goto LABEL_40;
    }
    if ( !vis[100 * v14 + v13] && !grid[10 * v14 + v13] )
    {
      bfs(v14, v13);
      system("cls");
      for ( i2 = 0; i2 <= 9; ++i2 )
      {
        for ( i3 = 0; i3 <= 9; ++i3 )
        {
          v10 = (std::ostream *)std::operator<<<std::char_traits<char>>(
                                  refptr__ZSt4cout,
                                  (unsigned int)(char)showUs[100 * i2 + i3]);
          std::operator<<<std::char_traits<char>>(v10, " ");
        }
        refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(refptr__ZSt4cout);
      }
      v11 = (std::ostream *)std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, (char *)&byte_48B002);
      refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(v11);
    }
  }
  v15 = std::string::length((std::string *)&ans);    // 循环结束就进入这里,很明显ans是我们要找的,但是双击会发现没有赋值
  for ( i4 = 0; i4 < v15; ++i4 )    // 遍历字符串
  {
    v6 = (_BYTE *)std::string::operator[](&ans, i4);
    std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, (unsigned int)(char)((v15 - i4) ^ *v6));    // 字符串做了异或处理
  }
  refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(refptr__ZSt4cout);    // 打印
LABEL_67:
  system("pause");
  return 0;
}

分析完可以知道动态调试可以绕开游戏,所以ida断在while循环。调试运行到汇编判断语句,这时候修改ZF0为1即可跳出循环

image-20240202165601913.png

跳出循环让它继续执行,发现打印了一串乱七八糟的字符串

image-20240202165710337.png

试了发现不是flag(看了wp才知道这个是base58加密),但不知为何试了几个base58解密网发现只有http://www.atoolbox.net/Tool.php?Id=932这个可以解密出正确flag,别的都乱码(flag{h4pp4-M1n3-G4m3})

image-20240202165812834.png

babymips XCTF

这道题逻辑特别简单,但是写脚本逆向时得多加注意

int __fastcall main(int a1, char **a2, char **a3)
{
  int i; // [sp+18h] [+18h] BYREF
  char v5[36]; // [sp+1Ch] [+1Ch] BYREF

  setbuf((FILE *)stdout, 0);
  setbuf((FILE *)stdin, 0);
  printf("Give me your flag:");
  scanf("%32s", v5);
  for ( i = 0; i < 32; ++i )    // 32位,分别异或
    v5[i] ^= 32 - (_BYTE)i;
  if ( !strncmp(v5, fdata, 5u) )                // Q|j{g
    return sub_4007F0(v5);
  else
    return puts("Wrong");
}

int __fastcall sub_4007F0(const char *a1)
{
  char v1; // $v1
  size_t i; // [sp+18h] [+18h]

  for ( i = 5; i < strlen(a1); ++i )
  {
    if ( (i & 1) != 0 )                         // 奇数
      v1 = (a1[i] >> 2) | (a1[i] << 6);            // 高6位到低6位,低2位到高2位
    else                                        // 偶数
      v1 = (4 * a1[i]) | (a1[i] >> 6);            // 低6位到低高位,高2位到低2位
    a1[i] = v1;
  }
  if ( !strncmp(a1 + 5, (const char *)off_410D04, 0x1Bu) )
    return puts("Right!");
  else
    return puts("Wrong!");
}

写脚本时要注意的是,源代码中的a1是字符数组,每个字符8位,所以左移的时候溢出需要去除,与0xff做一个与运算即可

data = [ord(i) for i in "Q|j{g"]
data.extend([0x52, 0xFD, 0x16, 0xA4, 0x89, 0xBD, 0x92, 0x80, 0x13, 0x41, 0x54, 0xA0, 0x8D, 0x45, 0x18, 0x81, 0xDE, 0xFC, 0x95, 0xF0, 0x16, 0x79, 0x1A, 0x15, 0x5B, 0x75, 0x1F])
for i in range(5, len(data)):   # 注意要控制data[i]在一字节长度范围内
    if (i & 1) != 0:
       data[i] = (data[i] << 2) & 0xff | (data[i] >> 6)
    else:
       data[i] = (data[i] >> 2) | ((data[i] << 6) & 0xff)
flag = ''
for i in range(32):
    flag += chr(data[i] ^ (32 - i))
print(flag)    # qctf{ReA11y_4_B@89_mlp5_4_XmAn_}

zorropub nullcon

由于我本机c语言跑不起来md5加密,因此这道题目我用的爆破法+限定字符范围,没有去核对md5值

int __fastcall main(int a1, char **a2, char **a3)
{
  size_t v3; // rax
  int v5; // [rsp+1Ch] [rbp-104h] BYREF
  int v6; // [rsp+20h] [rbp-100h] BYREF
  int i; // [rsp+24h] [rbp-FCh]
  unsigned int seed; // [rsp+28h] [rbp-F8h]
  unsigned int v9; // [rsp+2Ch] [rbp-F4h]
  char v10[96]; // [rsp+30h] [rbp-F0h] BYREF
  char v11[16]; // [rsp+90h] [rbp-90h] BYREF
  char v12[32]; // [rsp+A0h] [rbp-80h] BYREF
  char s[32]; // [rsp+C0h] [rbp-60h] BYREF
  char s1[40]; // [rsp+E0h] [rbp-40h] BYREF
  unsigned __int64 v15; // [rsp+108h] [rbp-18h]

  v15 = __readfsqword(0x28u);
  seed = 0;
  puts("Welcome to Pub Zorro!!");
  printf("Straight to the point. How many drinks you want?");
  __isoc99_scanf("%d", &v5);
  if ( v5 <= 0 )
  {
    printf("You are too drunk!! Get Out!!");
    exit(-1);
  }
  printf("OK. I need details of all the drinks. Give me %d drink ids:", (unsigned int)v5);
  for ( i = 0; i < v5; ++i )
  {
    __isoc99_scanf("%d", &v6);
    if ( v6 <= 16 || v6 > 0xFFFF )              // 0~16
    {
      puts("Invalid Drink Id.");
      printf("Get Out!!");
      exit(-1);
    }
    seed ^= v6;
  }
  i = seed;
  v9 = 0;
  while ( i )
  {
    ++v9;
    i &= i - 1;
  }
  if ( v9 != 10 )
  {
    puts("Looks like its a dangerous combination of drinks right there.");
    puts("Get Out, you will get yourself killed");
    exit(-1);
  }
  srand(seed);
  MD5_Init(v10);                                // 初始化MD5上下文结构
  for ( i = 0; i <= 29; ++i )
  {
    v9 = rand() % 1000;
    sprintf(s, "%d", v9);
    v3 = strlen(s);
    MD5_Update(v10, s, v3);                     // 向MD5上下文输入字节流
    v12[i] = v9 ^ LOBYTE(dword_6020C0[i]);
  }
  v12[i] = 0;
  MD5_Final(v11, v10);                          // 生成最终的MD5摘要串,传给v11
  for ( i = 0; i <= 15; ++i )
    sprintf(&s1[2 * i], "%02x", (unsigned __int8)v11[i]);
  if ( strcmp(s1, "5eba99aff105c9ff6a1a913e343fec67") )
  {
    puts("Try different mix, This mix is too sloppy");
    exit(-1);
  }
  return printf("\nYou choose right mix and here is your reward: The flag is nullcon{%s}\n", v12);
}

需要注意的是seed值必须在linux生成,windows不一样;用的kali:

g++ -m32 zorropub.cpp -o zorropub 
./zorropub
#include<iostream>
using namespace std;

int main()
{
    int i; // [rsp+24h] [rbp-FCh]
    unsigned int seed; // [rsp+28h] [rbp-F8h]
    unsigned int v9; // [rsp+2Ch] [rbp-F4h]
    int v12[32]; // [rsp+A0h] [rbp-80h] BYREF
    unsigned int dword_6020C0[30] = {
        0x000003C8, 0x00000032, 0x000002CE, 0x00000302, 0x0000007F, 0x000001B8, 0x0000037E, 0x00000188, 
        0x00000349, 0x0000027F, 0x0000005E, 0x00000234, 0x00000354, 0x000001A3, 0x00000096, 0x00000340, 
        0x00000128, 0x000002FC, 0x00000300, 0x0000028E, 0x00000126, 0x0000001B, 0x0000032A, 0x000002F5, 
        0x0000015F, 0x00000368, 0x000001EB, 0x00000079, 0x0000011D, 0x0000024E
    };
    for (int j = 1; j <= 65536; j++) {
        v9 = 0;
        i = j;
        while ( i )
        {
            ++v9;
            i &= i - 1;
            if (v9 > 10)
                break;
        }
        if (v9 == 10) {
            seed = j;
            srand(seed);
            for ( i = 0; i <= 29; ++i )
            {
                v9 = rand() % 1000;
                v12[i] = v9 ^ dword_6020C0[i];
                if (v12[i] > 127 || v12[i] < 32)   // 限定在可显示字符
                    break;
            }
            if (i != 30)
                continue;
            v12[i] = 0;
            printf("seed: %d\nnullcon{", seed);
            for (i = 0; i < 30; i++) {
            printf("%c", v12[i]);
        }
        printf("}\n");
        //printf("\nYou choose right mix and here is your reward: The flag is nullcon{%s}\n", v12);
        }
    }
}

image-20240204103004957.png

catch-me ASIS

首先看文件头查找到FD377A58,是xz文件,两次7zip解压得到 Catch_me 64位文件,ida64反编译

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  int v3; // ebx
  __int64 i; // rax
  __m128i si128; // xmm0
  __m128i v6; // xmm1
  __m128i v7; // xmm3
  __m128i v8; // xmm0
  __m128i v9; // xmm3
  __m128i v10; // xmm1
  __m128i v11; // xmm4
  __m128i v12; // xmm0
  __m128i v13; // xmm0

  v3 = sub_400820(dword_6012B4);                // 动态调试可得到具体值:0xB11924E1
  byte_6012A8[0] = HIBYTE(v3);                  // 0xb1
  byte_6012A9 = BYTE2(v3) & 0xFD;               // 0x19
  byte_6012AA = BYTE1(v3) & 0xDF;               // 0x04
  byte_6012AB = v3 & 0xBF;                      // 0xa1
  if ( getenv("ASIS") && (*(_DWORD *)getenv("CTF") ^ v3) == 0xFEEBFEEB )// 很明显这里必须进去才能修改haystack,否则就会导致后面乱码
    dword_6012AC = *(_DWORD *)getenv("ASIS");   // 求不出来,只能猜和getenv("CTF")值相同
  for ( i = 0LL; i != 33; ++i )
    haystack[i] ^= byte_6012A8[i & 7];          // i&7的值不超过7
  si128 = _mm_load_si128((const __m128i *)haystack);// 从这行到判断处都没用,可以动态调试绕开
  v6 = _mm_unpacklo_epi8(si128, (__m128i)0LL);
  v7 = _mm_unpackhi_epi8(si128, (__m128i)0LL);
  v8 = _mm_add_epi32(
         _mm_unpackhi_epi16(v7, (__m128i)0LL),
         _mm_add_epi32(
           _mm_add_epi32(_mm_unpackhi_epi16(v6, (__m128i)0LL), _mm_unpacklo_epi16(v6, (__m128i)0LL)),
           _mm_unpacklo_epi16(v7, (__m128i)0LL)));
  v9 = _mm_load_si128((const __m128i *)&xmmword_601290);
  v10 = _mm_unpackhi_epi8(v9, (__m128i)0LL);
  v11 = _mm_unpacklo_epi8(v9, (__m128i)0LL);
  v12 = _mm_add_epi32(
          _mm_add_epi32(
            _mm_add_epi32(
              _mm_add_epi32(v8, _mm_unpacklo_epi16(v11, (__m128i)0LL)),
              _mm_unpackhi_epi16(v11, (__m128i)0LL)),
            _mm_unpacklo_epi16(v10, (__m128i)0LL)),
          _mm_unpackhi_epi16(v10, (__m128i)0LL));
  v13 = _mm_add_epi32(v12, _mm_srli_si128(v12, 8));
  if ( _mm_cvtsi128_si32(_mm_add_epi32(v13, _mm_srli_si128(v13, 4))) != 2388 )// 必须绕开,否则haystack会被覆盖,但是动态调试绕开会发现haystack字符部分乱码,说明其中有些值不对
    strcpy(haystack, "bad_bad_bad_bad_bad_bad");
  printf("Flag: ASIS{%s}\n", haystack);
  if ( strstr(haystack, "bad_") )
    puts("this flag is absolutely fake ");
  return 0LL;
}

直接运行如下

image-20240204165428700.png

编写脚本逆向

haystack = [0x87, 0x29, 0x34, 0xC5, 0x55, 0xB0, 0xC2, 0x2D, 0xEE, 0x60, 0x34, 0xD4, 0x55, 0xEE, 0x80, 0x7C, 0xEE, 0x2F, 0x37, 0x96, 0x3D, 0xEB, 0x9C, 0x79, 0xEE, 0x2C, 0x33, 0x95, 0x78, 0xED, 0xC1, 0x2B]
b = [0xB1, 0x19 & 0xFD, 0x24 & 0xDF, 0xB11924E1 & 0xbf] + list((0xFEEBFEEB ^ 0xB11924E1).to_bytes(4,'little'))
flag = ''
for i in range(32):
    flag += chr(haystack[i] ^ b[i & 7])
print('ASIS{'+flag+'}')    # ASIS{600d_j0b_y0u_4r3_63771n6_574r73d}

reverse-for-the-holy-grail-350

ida64反编译

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int valid; // ebx
  int v4; // ebx
  __int64 v5; // rbx
  void *v7[2]; // [rsp+0h] [rbp-70h] BYREF
  _BYTE v8[16]; // [rsp+10h] [rbp-60h] BYREF
  void *v9[2]; // [rsp+20h] [rbp-50h] BYREF
  _BYTE v10[16]; // [rsp+30h] [rbp-40h] BYREF
  void *v11[2]; // [rsp+40h] [rbp-30h] BYREF
  char v12[24]; // [rsp+50h] [rbp-20h] BYREF

  v11[0] = v12;
  v11[1] = 0LL;
  v12[0] = 0;
  std::__ostream_insert<char,std::char_traits<char>>(&std::cout, "What... is your name?", 21LL);
  std::endl<char,std::char_traits<char>>(&std::cout);
  std::operator>><char>(&std::cin, v11);
  std::__ostream_insert<char,std::char_traits<char>>(&std::cout, "What... is your quest?", 22LL);
  std::endl<char,std::char_traits<char>>(&std::cout);
  std::istream::ignore((std::istream *)&std::cin);
  std::getline<char,std::char_traits<char>,std::allocator<char>>(&std::cin, v11);
  std::__ostream_insert<char,std::char_traits<char>>(&std::cout, "What...  is the secret password?", 32LL);
  std::endl<char,std::char_traits<char>>(&std::cout);
  std::operator>><char>(&std::cin, &userIn[abi:cxx11]);// 只有这个输入比较重要
  v7[0] = v8;
  std::string::_M_construct<char *>(v7, userIn[abi:cxx11], qword_601AE8 + userIn[abi:cxx11]);
  valid = validChars(v7);
  if ( v7[0] != v8 )
    operator delete(v7[0]);
  if ( valid < 0 )                              // valid必须大于等于0
    goto LABEL_8;
  v9[0] = v10;
  std::string::_M_construct<char *>(v9, userIn[abi:cxx11], qword_601AE8 + userIn[abi:cxx11]);
  v4 = stringMod(v9);
  if ( v9[0] != v10 )
    operator delete(v9[0]);
  if ( v4 < 0 )                                 // v4必须大于等于0
  {
LABEL_8:
    std::__ostream_insert<char,std::char_traits<char>>(&std::cout, "Auuuuuuuugh", 11LL);
    std::endl<char,std::char_traits<char>>(&std::cout);
  }
  else
  {
    std::__ostream_insert<char,std::char_traits<char>>(&std::cout, "Go on. Off you go. tuctf{", 25LL);
    v5 = std::__ostream_insert<char,std::char_traits<char>>(&std::cout, userIn[abi:cxx11], qword_601AE8);
    std::__ostream_insert<char,std::char_traits<char>>(v5, "}", 1LL);
    std::endl<char,std::char_traits<char>>(v5);
  }
  if ( v11[0] != v12 )
    operator delete(v11[0]);
  return 0;
}

第一个关键函数:验证字符是否有效,其实没啥用

__int64 __fastcall validChars(__int64 *a1)
{
  __int64 v1; // r8
  unsigned __int64 v2; // rdi
  __int64 v3; // rdx
  int v4; // ecx
  __int64 result; // rax

  v1 = *a1;
  v2 = a1[1];
  if ( !v2 )
    return 0LL;
  v3 = 0LL;
  v4 = 0;
  result = 0LL;
  do
  {
    if ( (unsigned __int8)((*(_BYTE *)(v1 + v3) & 0xDF) - 65) > 0x19u && *(_BYTE *)(v1 + v3) != '?' )// 不能满足if
      result = 0xFFFFFFFFLL;
    v3 = ++v4;
  }
  while ( v4 < v2 );
  return result;
}

第二个关键函数:根据模3不同结果变换原来字符串

__int64 __fastcall stringMod(__int64 *a1)
{
  __int64 v1; // r9
  __int64 v2; // r10
  __int64 v3; // rcx
  int v4; // r8d
  int *v5; // rdi
  int *v6; // rsi
  int v7; // ecx
  int v8; // r9d
  int v9; // r10d
  unsigned int v10; // eax
  int v11; // esi
  int v12; // esi
  int v14[18]; // [rsp+0h] [rbp-60h] BYREF
  __int64 v15; // [rsp+48h] [rbp-18h] BYREF

  memset(v14, 0, sizeof(v14));
  v1 = a1[1];                                   // 长度
  if ( v1 )
  {
    v2 = *a1;
    v3 = 0LL;
    v4 = 0;
    do
    {
      v12 = *(char *)(v2 + v3);
      v14[v3] = v12;                            // v14=v2也就是a1,除了3的倍数位会修改为firstchar
      if ( 3 * ((unsigned int)v3 / 3) == (_DWORD)v3 && v12 != firstchar[(unsigned int)v3 / 3] )// v3为3的倍数时,v12==firstchar[v3/3],firstchar='AinEoa'
        v4 = -1;                                // 不能进if
      ++v3;
    }
    while ( v3 != v1 );
  }
  else
  {
    v4 = 0;
  }
  v5 = v14;
  v6 = v14;
  v7 = 666;
  do
  {
    *v6 = v7 ^ *(unsigned __int8 *)v6;          // 这里v6变v14、v5也会变,因为指针指向同一地址
    v7 += v7 % 5;
    ++v6;
  }
  while ( &v15 != (__int64 *)v6 );              // v15的地址正好是v14的末尾+1,也就是说遍历完v6即v14,两者地址相等时,退出循环
  v8 = 1;
  v9 = 0;
  v10 = 1;
  v11 = 0;
  do
  {
    if ( v11 == 2 )                             // index mod 3 == 2时取字符
    {
      if ( *v5 != thirdchar[v9] )               // 需满足*v5 == thirdchar[v9] 'rarre?'
        v4 = -1;
      if ( v10 % *v5 != masterArray[v9] )       // 需满足(mod3 = 0) * (mod3 = 1) % (mod3 = 2) == masterArray[v9]
        v4 = -1;
      ++v9;
      v10 = 1;                                  // 初始化
      v11 = 0;
    }
    else
    {
      v10 *= *v5;                               // (mod3 = 0) * (mod3 = 1)
      if ( ++v11 == 3 )
        v11 = 0;
    }
    ++v8;
    ++v5;
  }
  while ( v8 != 19 );                           // 字符串长18
  return (unsigned int)(v7 * v4);
}

解密脚本

firstchar = [0x41, 0x69, 0x6e, 0x45, 0x6f, 0x61]
masterArray = [0x1d7, 0xc, 0x244, 0x25e, 0x93, 0x6c]
thirdchar = [0x2ef, 0x2c4, 0x2dc, 0x2c7, 0x2de, 0x2fc]
v7 = [666]
for i in range(18):
    v7 += [v7[-1]+v7[-1]%5]
flag = ''
for i in range(6):
    for j in range(33, 127): # 可打印字符
        if (firstchar[i]^v7[3*i]) * (j^v7[3*i+1]) % thirdchar[i] == masterArray[i]:
            flag += chr(firstchar[i])+chr(j)+chr(thirdchar[i]^v7[3*i+2])
            break;
print('tuctf{'+flag+'}')    # tuctf{AfricanOrEuropean?}
最后修改:2024 年 02 月 05 日
如果觉得我的文章对你有用,请随意赞赏