Loading... # Reverse(十一) ## Newbie_calculations 主函数一堆唬人的计算函数,点开进去可以看到大量循环 ~~~c 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;下面静态详细分析代码 ~~~c _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 } ~~~ ~~~c 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 } ~~~ ~~~c _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 **第一版代码**:由于不涉及地址传参,改动很大,特别不好 ~~~c++ #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} ~~~ **第二版代码**:按照原来模式进行地址传参,这个代码相对好些 ~~~c++ #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 ~~~c 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 ~~~c 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](http://xherlock.top/usr/uploads/2024/02/46628229.png) 外面加个RCTF{} ## testre ida64打开 ~~~c __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; } ~~~ 关键函数的详细分析,其中存在大量冗余代码,很明显是混淆 ~~~c __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; } ~~~ 去掉混淆代码后如下: ~~~c __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](http://xherlock.top/usr/uploads/2024/02/1073471341.png) ## crazy百越杯2018 ida64找到主函数,可以发现很难阅读代码,是因为是使用了C++的STL;为了方便分析代码,已省略无关代码 ~~~c 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; } ~~~ 第一个函数:将输入转移到第一个参数中 ~~~c 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加密 ~~~c 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; } ~~~ 第三个函数: ~~~c __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](http://xherlock.top/usr/uploads/2024/02/1056238936.png) ## Replace ida分析 ~~~c 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](http://xherlock.top/usr/uploads/2024/02/532390313.png) 搞定这个问题就可以照着原来的代码编写脚本解密 ~~~c++ #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的地方 ~~~c 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](http://xherlock.top/usr/uploads/2024/02/203852070.png) 跳出循环让它继续执行,发现打印了一串乱七八糟的字符串 ![image-20240202165710337.png](http://xherlock.top/usr/uploads/2024/02/2448084237.png) 试了发现不是flag(看了wp才知道这个是base58加密),但不知为何试了几个base58解密网发现只有http://www.atoolbox.net/Tool.php?Id=932这个可以解密出正确flag,别的都乱码(flag{h4pp4-M1n3-G4m3}) ![image-20240202165812834.png](http://xherlock.top/usr/uploads/2024/02/3707868462.png) ## babymips XCTF 这道题逻辑特别简单,但是写脚本逆向时得多加注意 ~~~c 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做一个与运算即可 ~~~python 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值 ~~~c 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: ~~~shell g++ -m32 zorropub.cpp -o zorropub ./zorropub ~~~ ~~~c++ #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](http://xherlock.top/usr/uploads/2024/02/3516099387.png) ## catch-me ASIS 首先看文件头查找到FD377A58,是xz文件,两次7zip解压得到 Catch_me 64位文件,ida64反编译 ~~~c __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](http://xherlock.top/usr/uploads/2024/02/3877119665.png) 编写脚本逆向 ~~~python 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反编译 ~~~c 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; } ~~~ 第一个关键函数:验证字符是否有效,其实没啥用 ~~~c __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不同结果变换原来字符串 ~~~c __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); } ~~~ 解密脚本 ~~~python 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 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 0 如果觉得我的文章对你有用,请随意赞赏
1 条评论
看的我热血沸腾啊https://www.ea55.com/