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试试
外面加个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"对比
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);
}
至此很简单了,逆向计算回去就可以了。==注意异或运算符优先性较低,一定要带上括号!!!==
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发现根本不够,因此只可能是溢出到下一个里
搞定这个问题就可以照着原来的代码编写脚本解密
#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即可跳出循环
跳出循环让它继续执行,发现打印了一串乱七八糟的字符串
试了发现不是flag(看了wp才知道这个是base58加密),但不知为何试了几个base58解密网发现只有http://www.atoolbox.net/Tool.php?Id=932这个可以解密出正确flag,别的都乱码(flag{h4pp4-M1n3-G4m3})
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);
}
}
}
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;
}
直接运行如下
编写脚本逆向
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?}