圆圈舞
圆圈舞是祖传的用毕颂(直箫)伴奏,不分季节跳的集体舞蹈,舞蹈吹奏者在中间连吹带跳,其他舞者也在外围手拉手地呼喊随着音乐节奏起舞。
出题者就如同吹奏者,希望参赛者能够跟随着出题者的意图,在算法题+轻度ANTI中玩的开心。
本道题目只有8支队伍解答出来,战队“pizzatpl”用时1天半左右的时间才破解成功,这道题目观战人数达到1900人。
出题战队
战队成员:lelfei
个人主页:https://bbs.pediy.com/user-30.htm
个人简介:lelfei,业余的crack爱好者。学生时代对电脑产生了浓厚的兴趣,经历了很长时间的游戏沉迷后,开始慢慢转向学习技术,工作后自学了ASM、VB、VC、HTML、ASP、Python等语言的入门。工作原因上网较少,对单机的逆向分析、算法比较感兴趣,但是由于缺少系统的学习,水平处于“入行较早层次较低知识较杂”的阶段。
看雪CTF crownless 评委 点评
这道题主要考察解数独的“舞蹈链”算法以及花指令、自校验等软件保护技术,总体来说难度中等偏上,这导致只有八支队伍成功破解此题。
题目设计思路
简单说一下程序流程,初始化一个数独游戏,使用DancingLinks算法计算出答案,与用户输入值比较,正确后输出“Well Done!”。
具体实现比较绕,希望这种隐藏思路的方式能带给大家一点惊喜。
第一步:对程序二处位置进行数据校验,生成大数Num1。
第二步:用Num1与一固定大数Num2相乘,得到数独初始化数据Data1。
第三步:对Data1进行解析并填入DancingLinks初始化数据DLX,同时用户输入16进制数据转换成10进制数后按位置填入一个九宫格UserData1中,一共80位。数独数据最中间位没有直接提供,根据9个CheckDebug()调试检查函数返回值计算得出,必须为7。
第四步:DLX开始Dancing,同时在DLX的cover()函数中对用户数据UserData1进行转置。转置方法为,把数据交换操作拆分为5个小步骤,每一次cover()执行一个小步骤,执行60次交换后,九宫格数据向右旋转90度。一共执行4次旋转,最后用户数据转了一圈Circle后回到原位置,只是每一位进行了6次数据变换操作,生成用户数据UserData2。一共需要执行5*60*4=1200次操作,也就是说DLX.cover()操作要多于1200次。
第五步:对UserData2查表与DLX运算结果按位比较,得到完全匹配的位数总和cnt,必须为79(比较到中间位时cnt--)。
第六步:用cnt对一个数据进行解码并计算校验值,当校验值正确时输出解码数据,正确时输出“Well Done!”。
最后修改程序,添加花指令和自校验,方法为:
1. 在代码中加了一些标志数据,用python把这些标志数据替换成花指令。
2. 在代码中加了一个大段数据的空函数,在python中把自校验代码添加花指令后填入这个空函数。
3. 在代码中3处调用空函数的位置添加标志位,通过调试得到代码自校验数据,然后在python中把自校验数据写到函数调用的标志位。
程序中使用的大数计算代码是前几年参加CTF时自己写的,源码中提供;花指令库来自于OD去花指令插件DeJunk的配置文件;加花指令和自校验代码是自己写的,详见byteenc.py;DancingLinks算法源码来自于网上,基本没做修改。
程序用CodeBlocks开发,gcc编译。程序在Win7 64位下运行正常,其他系统未做测试。
1. 首先要分析或穷举出对输出字符进行解码的校验值,得知用户数据与DLX结果必须完全匹配。
2. 分析出在DLX.cover()函数中,每5次调用为一次交换并移位操作。
3. 进一步分析出1200次交换移位最终只是转了一圈回到原位,每一位进行了6次移位操作。
4. 穷举移位操作,还原用户数据,得到答案。
5. 按惯例留了一个后门:如果能直接识别出核心算法为DancingLinks,结合数独初始化数据,能直接得到答案。
需要穷举的操作都是按字节进行查表,对字节穷举的时间基本可以忽略不计。
CBC25EF8D9F482BC1F3DA3CA1F154EC89FC3F1414EDD93A3
src.rar:程序源码
DancingCircle.rar:参赛程序
解题思路
本题解题思路由看雪论坛新手慢慢来提供
通过观察程序中代码,发现花指令主要有以下4种模式:
clc jnb stc jb call add esp, 4 j j
可以通过如下的idapython脚本去除花指令,有部分花指令没有识别到,任然需要自己手动nop掉。
bg = 0x00401000 end = 0x004BBE00 addr = bg def patch_nop(begin,end): while(end>begin): PatchByte(begin,0x90) begin=begin+1 def next_instr(addr): return addr+ItemSize(addr) while(addr<end): next =next_instr(addr) MakeCode(next) if 'j' in GetMnem(addr) and 'j' in GetMnem(next) : if GetOperandValue(addr,0) == GetOperandValue(next,0): print 'jmp %08x'%addr dest_addr = GetOperandValue(addr,0) patch_nop(addr,dest_addr) addr=dest_addr MakeCode(addr) if 'clc' == GetMnem(addr) and 'jnb' in GetMnem(next) : print 'clc %08x'%addr dest_addr = GetOperandValue(next,0) patch_nop(addr,dest_addr) addr=dest_addr MakeCode(addr) if 'stc' == GetMnem(addr) and 'jb' in GetMnem(next) : print 'clc %08x'%addr dest_addr = GetOperandValue(next,0) patch_nop(addr,dest_addr) addr=dest_addr MakeCode(addr) if 'call' in GetMnem(addr): dest_addr = GetOperandValue(addr,0) idc.del_items(next_instr(addr)) MakeCode(dest_addr) if "add esp, 4" == GetDisasm(dest_addr): print 'call %08x'%addr dest_addr=next_instr(dest_addr) patch_nop(addr,dest_addr) addr=dest_addr MakeCode(addr) addr = next_instr(addr) MakeCode(addr)
在.text:004B8DF3处创建函数并修改函数结尾为.text:004B9827,F5后得到该函数的代码如下:
int __usercall sub_4B8DF3@<eax>(char a1@<cl>, int a2@<ebx>, signed int a3@<edi>) { void *v3; // esp int v4; // ecx int v5; // eax int v6; // edx int v7; // eax char *v8; // ebx char *v9; // esi char *v10; // edx char v11; // ah char v12; // dl int v13; // eax int v14; // eax int v15; // edx int v16; // ebx int v17; // eax int v18; // ebx int v19; // eax int v20; // edx int v21; // edx int v22; // ecx signed int v23; // eax int (__cdecl *v24)(char *); // eax int v25; // ebx __int64 v26; // rax __int64 v27; // rax char v28; // al int v29; // edx int v30; // eax int v31; // ST20_4 unsigned __int8 *v32; // ST1C_4 signed int v33; // eax _BYTE *v34; // ecx int v35; // eax int v36; // ebx int v37; // edx int v38; // eax int v39; // edx int v41; // [esp+4h] [ebp-2BDCh] int v42; // [esp+8h] [ebp-2BD8h] int v43; // [esp+Ch] [ebp-2BD4h] int v44; // [esp+10h] [ebp-2BD0h] int v45; // [esp+14h] [ebp-2BCCh] int v46; // [esp+18h] [ebp-2BC8h] signed int v47; // [esp+18h] [ebp-2BC8h] int v48; // [esp+1Ch] [ebp-2BC4h] int i; // [esp+20h] [ebp-2BC0h] int v50; // [esp+24h] [ebp-2BBCh] int v51; // [esp+24h] [ebp-2BBCh] int v52; // [esp+24h] [ebp-2BBCh] int v53; // [esp+28h] [ebp-2BB8h] char *v54; // [esp+28h] [ebp-2BB8h] int v55; // [esp+28h] [ebp-2BB8h] int v56; // [esp+2Ch] [ebp-2BB4h] int v57; // [esp+2Ch] [ebp-2BB4h] signed int v58; // [esp+2Ch] [ebp-2BB4h] int v59; // [esp+2Ch] [ebp-2BB4h] signed int length; // [esp+30h] [ebp-2BB0h] char v61; // [esp+34h] [ebp-2BACh] int v62; // [esp+38h] [ebp-2BA8h] int (__cdecl *v63)(int, int, int, int, int, int); // [esp+4Ch] [ebp-2B94h] int *v64; // [esp+50h] [ebp-2B90h] char *v65; // [esp+54h] [ebp-2B8Ch] int (*v66)(); // [esp+58h] [ebp-2B88h] int *v67; // [esp+5Ch] [ebp-2B84h] int v68; // [esp+68h] [ebp-2B78h] int v69; // [esp+6Ch] [ebp-2B74h] int v70; // [esp+70h] [ebp-2B70h] int v71; // [esp+74h] [ebp-2B6Ch] int v72; // [esp+80h] [ebp-2B60h] int v73; // [esp+84h] [ebp-2B5Ch] int v74; // [esp+88h] [ebp-2B58h] int v75; // [esp+8Ch] [ebp-2B54h] int v76; // [esp+90h] [ebp-2B50h] int v77; // [esp+94h] [ebp-2B4Ch] char input[256]; // [esp+A8h] [ebp-2B38h] char v79[256]; // [esp+1A8h] [ebp-2A38h] char keydata[256]; // [esp+2A8h] [ebp-2938h] char v81; // [esp+3A8h] [ebp-2838h] char v82; // [esp+17B8h] [ebp-1428h] char v83; // [esp+2BC8h] [ebp-18h] char *v84; // [esp+2BD0h] [ebp-10h] int v85; // [esp+2BD4h] [ebp-Ch] v3 = alloca(sub_40D480(a1)); v63 = sub_4B85B0; v64 = dword_4B9D1C; v66 = sub_4B9827; v65 = &v83; v67 = (int *)&v84; sub_40D9D0(&v61); sub_40C510(); sub_401F58(0, 0x25A); sub_40299C(0x1000); sub_401BC6(&v81); sub_401BC6(&v82); v62 = 2; getinput(&dword_4BD7C0, input); for ( length = 0; ; ++length ) { v5 = length; if ( !input[length] ) break; } v6 = 0; LOBYTE(a2) = '7'; while ( v6 != length ) { LOBYTE(v5) = input[v6]; LOBYTE(v4) = '0'; if ( (char)v5 > '9' ) { LOBYTE(v4) = '='; if ( (char)v5 < 'a' ) v4 = a2; } v7 = v5 - v4; v79[v6] = v7; v5 = v7 - 1; if ( (unsigned __int8)v5 > 0xEu ) // 输入为16进制数据,且不能为0,即1-F goto LABEL_61; ++v6; } v8 = keydata; v9 = v79; v79[length] = 0; while ( length ) { v10 = &v79[length]; do { v11 = (unsigned __int8)*v10 % 10u; *v10 = (unsigned __int8)*v10 / 10u; *(v10-- - 1) += 16 * v11; } while ( v79 != v10 ); v12 = v79[0] % 10u; // 16进制转10进制,从右往左,依次为高位到低位 v79[0] /= 10u; *v8 = v12; do { a3 = length; v13 = length - 1; if ( v79[length - 1] ) break; --length; } while ( v13 ); ++v8; } v62 = 2; init_bignum(&unk_4BC080, 16); // 0xc23f6401c93adb v85 = init_bignum(&unk_4BC088, 16); // 0xeedcea3743d03a263af94f386de1 sub_401E4C(&v82); sub_401684(&v81, a3); // 10转16进制 v68 = 0; v69 = 0; v70 = 0; v44 = 0; v48 = 0; v45 = 0; v53 = 0; while ( 1 ) { v62 = 3; if ( v45 >= sub_4016AE(&v81) ) break; v50 = get_index_data(&v81, v45); v73 = 0; v14 = sub_4B77F0(1296); v15 = v14 + 1296; v16 = v14; v72 = v14; v17 = 0; v74 = v15; do { *(_DWORD *)(v16 + v17) = 0; v17 += 4; } while ( v17 != 1296 ); v73 = v15; if ( v50 ) { if ( v50 <= 9 ) { v27 = v53; *(_DWORD *)(v16 + 4 * v27) = 1; *(_DWORD *)(v16 + 4 * (v50 + 9 * (unsigned __int64)(v27 / 9) + 80)) = 1; a3 = 3; *(_DWORD *)(v16 + 4 * (v50 + 9 * (v53 % 9) + 161)) = 1; v9 = (char *)v50; *(_DWORD *)(v16 + 4 * (v50 + 9 * (3 * (v53 / 27) + v53 % 9 / 3) + 242)) = 1; v62 = 4; sub_49CF5C(&v68, (int)&v72); v46 = v48 + 1; v85 = set_index_data(&unk_4C8020, v48, v50); v84 = (char *)v85; ++v53; } else { for ( i = 0; ; ++i ) { v46 = i + v48; v56 = i + v53; if ( i >= v50 - 9 ) break; v85 = (unsigned __int8)keydata[v44 + i]; v62 = 4; set_index_data(&unk_4C8020, v46, v85); v85 = i; v84 = (char *)i; v43 = 4 * v56; v18 = 9 * (v56 / 9); v47 = 36 * (v56 / 9) + 324; v41 = 36 * (v56 / 9) + 360; v42 = 4 * (9 * (v56 % 9) - v18); v57 = 4 * (9 * (v56 % 9 / 3 + 3 * (v56 / 27)) - v18); do { v76 = 0; v62 = 4; v19 = sub_4B77F0(1296); v75 = v19; v20 = 0; v77 = v19 + 1296; do { *(_DWORD *)(v19 + v20) = 0; v20 += 4; } while ( v20 != 1296 ); a3 = v47; v76 = v19 + 1296; *(_DWORD *)(v19 + v43) = 1; *(_DWORD *)(v19 + v47) = 1; *(_DWORD *)(v47 + v19 + v42 + 324) = 1; *(_DWORD *)(v47 + v57 + v19 + 648) = 1; v62 = 5; sub_49CF5C(&v68, (int)&v75); sub_402950(&v75, v21, v22); v47 += 4; v9 = (char *)v47; } while ( v41 != v47 ); } v62 = 4; v23 = get_index_data(&v81, v45 + 1); v84 = (char *)v15; if ( v23 <= 9 ) { v44 += i; v53 += i; } else { v54 = 0; v51 = 0; do { v24 = (int (__cdecl *)(char *))dword_4BC020[v51]; v62 = 4; v54 += (unsigned int)v24(v84) < 1; ++v51; } while ( v51 != 9 ); v25 = v72; v26 = v56; *(_DWORD *)(v72 + 4 * v26) = 1; *(_DWORD *)(v25 + 4 * (_DWORD)&v54[9 * (unsigned __int64)(v26 / 9) + 80]) = 1; a3 = 3; *(_DWORD *)(v25 + 4 * (_DWORD)&v54[9 * (v56 % 9) + 161]) = 1; v9 = v54; *(_DWORD *)(v25 + 4 * (_DWORD)&v54[9 * (3 * (v56 / 27) + v56 % 9 / 3) + 242]) = 1; v62 = 4; v84 = (char *)sub_49CF5C(&v68, (int)&v72); v53 = v56 + 1; v44 += i; } } } else { v46 = v48; } sub_402950(&v72, v15, v84); ++v45; v48 = v46; } sub_402BEE(&v68, -1431655765 * ((v69 - v68) >> 2), 324); v62 = 6; v28 = sub_402DB6(0); v84 = (char *)a3; if ( v28 ) { v62 = 6; sub_49D054(&v76); v84 = v9; v55 = 0; v52 = 0; do { v84 = (char *)(v68 + 12 * (*(_DWORD *)(v71 + v52) - 1)); v62 = 8; sub_49D054(v84); v84 = (char *)v52; v29 = v72; v58 = 0; do { if ( *(_DWORD *)(v72 + 4 * v58) == 1 ) break; ++v58; } while ( v58 != 81 ); if ( v58 == 40 ) { --v55; } else { v59 = v58 - (v58 >= 41); v30 = 0; do { if ( *(_DWORD *)(v72 + 4 * v30 + 324) == 1 ) break; ++v30; } while ( v30 != 81 ); v31 = v30 % 9 + 1; v62 = 7; get_index_data(&unk_4C8020, v59); v32 = (unsigned __int8 *)off_4BC060; v33 = get_index_data(&unk_4C8020, v59); v84 = (char *)v29; v55 += v31 == v32[v33]; } sub_402950(&v72, v29, v84); v52 += 4; } while ( v52 != 324 ); v34 = off_4BC05C; v35 = 0; do { v36 = (unsigned __int8)v34[v35]; length += 9 * (v36 ^ v55) ^ 0x37; v37 = v36 ^ v55; v34[v35] = v36 ^ v55; if ( v55 == v36 ) break; ++v35; } while ( v35 != 513 ); if ( length == 0x1F1A ) { v62 = 8; v38 = sub_4B3F00(&dword_4BD9A0, (char *)off_4BC05C); sub_4B0DB0(v38); } sub_402950(&v71, v37, v84); } j_free(v75); sub_402950(&v76, v39, v84); sub_402966(&v68); LABEL_61: sub_401570(&v82); sub_401570(&v81); sub_40DA40(&v61); return 0; }
函数sub_401F58的花指令要更复杂一些,去除花指令后执行的汇编代码如下:
.text:00401F58 push ebp .text:00401F59 mov ebp, esp .text:00401F5B pusha .text:00401F5C pushf .text:00401F5D mov edx, offset unk_4BC080 .text:00401F76 mov ebx, [esp+28h] .text:00401F7F mov esi, [esp+2Ch] .text:00401F91 mov ecx, [esp+30h] .text:00401FAE test esi, esi .text:00401FB0 jnz short loc_401FBB .text:00401FB9 mov esi, ebx .text:00401FC0 add esi, 5 .text:00401FCE xor eax, eax .text:00401FDF xor eax, [esi] .text:00401FF0 add esi, 4 .text:00401FFD xor eax, 78563412h .text:0040201D ror eax, 9 .text:0040202E dec ecx .text:0040202F jnz short loc_401FD0 .text:0040203B xor eax, [ebx] .text:00402049 add ebx, 4 .text:00402060 xor ecx, ecx .text:00402070 mov cl, [ebx] .text:0040207D inc ebx .text:00402097 lea edx, [ecx+edx] .text:004020AB mov [edx], eax .text:004020BE popf .text:004020CB popa .text:004020E1 pop ebp .text:00402115 add dword ptr [esp], 5 .text:00402129 retn
伪C代码如下:
int __cdecl sub_401F58(_DWORD *a1, int a2) { _DWORD *v2; // esi int v3; // ecx _DWORD *v4; // esi int v5; // eax int v6; // eax int v7; // ecx _DWORD *retaddr; // [esp+24h] [ebp+4h] v2 = a1; v3 = a2; if ( !a1 ) v2 = retaddr; v4 = (_DWORD *)((char *)v2 + 5); v5 = 0; do { v6 = *v4 ^ v5; ++v4; v5 = __ROR4__(v6 ^ 0x78563412, 9); --v3; } while ( v3 ); v7 = *((unsigned __int8 *)retaddr + 4); *(_DWORD *)((char *)&unk_4BC080 + v7) = *retaddr ^ v5; retaddr = (_DWORD *)((char *)retaddr + 5);
004BC080 BD A3 9C 10 004BC084 46 F3 2C 00 004BC11D 01 02 23 24
.text:004B9398 call eax处的返回值并不固定,patch时,会出现问题,导致后面比较的数据和下标不对,可以通过修改00402BEE处的代码为00402BEE | EB FE | jmp dancing1.402BEE,即死循环,然后附加程序,修改回原来的代码,然后在比较的地方下断点,得到正确的结果。
知道算法后,就可以利用z3约束求解,z3脚本如下:
#coding=utf-8 from z3 import * m = [BitVec("x%d"%(i), 16) for i in xrange(58)] cs=[] for i in m: cs.append(And(i >= 1, i <= 9)) s = Solver() table1=[0x86, 0x89, 0x84, 0x8D, 0xA7, 0x83, 0xA6, 0x25, 0x47, 0x14, 0x35, 0x1D, 0x0F, 0x1E, 0x97, 0x9C, 0x95, 0x01, 0xAE, 0x3F, 0x37, 0xB6, 0x02, 0x14, 0x0B, 0x17, 0x8A, 0x1F, 0x93, 0xAC, 0x96, 0x02, 0x2D, 0x0E, 0x07, 0x9F, 0x92, 0x8C, 0x15, 0xBE, 0x0A, 0x86, 0x07, 0xA4, 0x03, 0x85, 0x2F, 0x8C, 0x8E, 0x0B, 0x9B, 0x05, 0x0F, 0x84, 0x80, 0x9E, 0x8D, 0x00, 0x16, 0x01] table2=[ 0xBD, 0x48, 0x2B, 0xAA, 0xB0, 0xA3, 0xB9, 0x42, 0xCF, 0x98, 0x4D, 0xB8, 0x3C, 0xA0, 0x32, 0x41, 0x21, 0x91, 0x3A, 0x45, 0x3B, 0x44, 0x9A, 0xBB, 0x19, 0x38, 0x10, 0x28, 0x40, 0x4C, 0xA9, 0xCD, 0x43, 0x33, 0xC6, 0x30, 0x49, 0xA2, 0xBA, 0x4E, 0xC5, 0xC9, 0xC8, 0xCB, 0xCC, 0x34, 0xB1, 0xC3, 0x41, 0xC4, 0xCA, 0x4A, 0x40, 0xCB, 0x08, 0x31, 0xC2, 0xCF, 0x39, 0x4E] S=[0x38, 0x39, 0x07, 0xE0, 0x5A, 0x18, 0x1E, 0xE7, 0x5E, 0x3B, 0xEB, 0xA5, 0xAE, 0x07, 0x06, 0x27, 0x0D, 0x89, 0xF1, 0x6E, 0xD8, 0x0D, 0x3A, 0xE0, 0x1E, 0x96, 0xA7, 0xB2, 0x7D, 0x15, 0x16, 0x37, 0x01, 0x41, 0x4B, 0x08, 0x1C, 0x1D, 0x33, 0x2B, 0x40, 0x49, 0x79, 0xE3, 0x04, 0xFA, 0x05, 0x47, 0x66, 0x1B, 0xDB, 0x9E, 0x2C, 0x2D, 0x4E, 0x20, 0x87, 0x9E, 0xBB, 0xE9, 0x14, 0x8A, 0x36, 0x7E, 0xD5, 0x63, 0x51, 0x71, 0x3C, 0x41, 0x37, 0xA8, 0x2E, 0x01, 0x02, 0x23, 0x24, 0x63, 0x46, 0xE9, 0x08, 0x09, 0x19, 0x2B, 0x48, 0x4D, 0xBD, 0xBC, 0x22, 0x11, 0x6C, 0x33, 0x34, 0x77, 0xE0, 0x27, 0x18, 0x6C, 0x35, 0x3B, 0xB6, 0x8F, 0x46, 0x10, 0xC3, 0x43, 0x22, 0x5E, 0x44, 0xB3, 0x8C, 0x53, 0x28, 0xA4, 0xCB, 0xF0, 0x81, 0xD6, 0x96, 0x0F, 0x5C, 0x31, 0x32, 0x19, 0xF0, 0xDA, 0x50, 0x4E, 0x94, 0xF7, 0x27, 0x4E, 0x40, 0x7A, 0x33, 0x1F, 0x20, 0x41, 0x42, 0x6C, 0x66, 0x05, 0x49, 0xA4, 0x48, 0x49, 0x30, 0xAC, 0x0C, 0xBE, 0x2E, 0x2F, 0x30, 0x9D, 0x18, 0x2A, 0x23, 0x23, 0x6F, 0x0B, 0x20, 0x21, 0x2A, 0x6E, 0x4F, 0x83, 0x3E, 0x3F, 0x51, 0x63, 0x9A, 0x03, 0x53, 0x25, 0x26, 0x48, 0xCF, 0x4C, 0x0A, 0x0B, 0x98, 0xB7, 0x88, 0x4F, 0xE9, 0xD3, 0x1E, 0x13, 0x57, 0x35, 0xD2, 0xF2, 0x08, 0x9A, 0x1A, 0x1B, 0x84, 0x3D, 0xC9, 0x66, 0x0E, 0x8B, 0xB4, 0x58, 0xD0, 0x45, 0xDA, 0x19, 0xA5, 0xB9, 0x2A, 0x1C, 0x4C, 0x2B, 0xDB, 0x07, 0xFF, 0x19, 0x12, 0x31, 0x1E, 0x96, 0xE2, 0x88, 0x4B, 0x19, 0x3A, 0x19, 0x1A, 0x19, 0xE6, 0xDE, 0x00, 0x21, 0xF7, 0x43, 0x4B, 0x32, 0x32, 0x07, 0x46, 0x29, 0x4A, 0x4B, 0x29, 0x97, 0x0E, 0x19, 0x10, 0xE0, 0x90, 0x73, 0x65, 0xE4, 0x4C, 0x17] r1=[0x2,0x3,0xA,0xF,0x12,0x17,0x19,0x1c,0x20,0x21,0x24,0x2a,0x2e,0x2f,0x34,0x37,0x39,0x3c,0x3f,0x46,0x4c,0x4d] result1=[7,9,3,4,9,5,6,8,2,3,6,1,3,5,4,5,1,7,2,8,3,5] r2=[0x27,0x42,0x13,0x22,0x25,0x1,0xb,0x2c,0x31,0xc,0x5,0xd,0xe,0x29,0x33,0x10,0x9,0x0,0x11,0x14,0x2b,0x2d,0x1b,0x30,0x32,0x23,0x1a,0x7,0x4,0x6,0x8,0x18,0x3d,0x44,0x3b,0x4e,0x45,0x3e,0x47,0x35,0x48,0x36,0x38,0x3a,0x28,0x1e,0x1f,0x1d,0x26,0x41,0x15,0x16,0x43,0x40,0x49,0x4a,0x4b,0x4f] result2=[0x3,0x5,0x1,0x5,0x5,0x6,0x2,0x2,0x1,0x1,0x8,0x6,0x7,0x8,0x9,0x8,0x5,0x4,0x9,0x8,0x2,0x7,0x1,0x8,0x6,0x7,0x3,0x2,0x3,0x1,0x5,0x7,0x6,0x9,0x2,0x4,0x3,0x7,0x8,0x3,0x9,0x4,0x8,0x9,0x4,0x6,0x9,0x4,0x9,0x4,0x2,0x4,0x6,0x1,0x6,0x7,0x2,0x1] in1=[0x2,0x3,0xA,0xF,0x12,0x17,0x19,0x1c,0x20,0x21,0x24,0x2a,0x2e,0x2f,0x34,0x37,0x39,0x3c,0x3f,0x46,0x4c,0x4d] in2=[0x0,0x1,0x4,0x5,0x6,0x7,0x8,0x9,0xb,0xc,0xd,0xe,0x10,0x11,0x13,0x14,0x15,0x16,0x18,0x1a,0x1b,0x1d,0x1e,0x1f,0x22,0x23,0x25,0x26,0x27,0x28,0x29,0x2b,0x2c,0x2d,0x30,0x31,0x32,0x33,0x35,0x36,0x38,0x3a,0x3b,0x3d,0x3e,0x40,0x41,0x42,0x43,0x44,0x45,0x47,0x48,0x49,0x4a,0x4b,0x4e,0x4f] input=[0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0] for i in range(22): input[in1[i]]=result1[i] for i in range(58): input[in2[i]]=m[i] for i in range(4): for j in range(60): tmp=input[table1[j]&0x7f] input[table1[j]&0x7f]=((input[table2[j]&0x7f]*9)%256)^0x37 input[table2[j]&0x7f]=((tmp*9)%256)^0x37 for i in range(22): if(result1[i]==1): cs.append(Or(input[r1[i]]== 32, input[r1[i]]== 73)) if(result1[i]==2): cs.append(input[r1[i]]==74) if(result1[i]==3): cs.append(input[r1[i]]==171) if(result1[i]==4): cs.append(input[r1[i]]==44) if(result1[i]==5): cs.append(Or(input[r1[i]]== 46, input[r1[i]]== 141)) if(result1[i]==6): cs.append(input[r1[i]]==14) if(result1[i]==7): cs.append(Or(input[r1[i]]== 2, input[r1[i]]== 13,input[r1[i]]== 239,input[r1[i]]== 215)) if(result1[i]==8): cs.append(Or(input[r1[i]]== 35, input[r1[i]]== 80,input[r1[i]]== 192)) if(result1[i]==9): cs.append(input[r1[i]]== 81) for i in range(58): if(result2[i]==1): cs.append(Or(input[r2[i]]== 32, input[r2[i]]== 73)) if(result2[i]==2): cs.append(input[r2[i]]==74) if(result2[i]==3): cs.append(input[r2[i]]==171) if(result2[i]==4): cs.append(input[r2[i]]==44) if(result2[i]==5): cs.append(Or(input[r2[i]]== 46, input[r2[i]]== 141)) if(result2[i]==6): cs.append(input[r2[i]]==14) if(result2[i]==7): cs.append(Or(input[r2[i]]== 2, input[r2[i]]== 13,input[r2[i]]== 239,input[r2[i]]== 215)) if(result2[i]==8): cs.append(Or(input[r2[i]]== 35, input[r2[i]]== 80,input[r2[i]]== 192)) if(result2[i]==9): cs.append(input[r2[i]]== 81) """ for i in range(58): cs.append(S[input[r2[i]]]==result2[i]) for i in range(22): cs.append(S[input[r1[i]]]==result1[i]) #""" #cs.append(Or(input[r1[i]]== 0, input[r1[i]]== 9)) s.add(cs) ## 去重并求出所有解 while(s.check()==sat): t = s.model() flag = "" for i in m: flag=flag+str(t[i].as_long()) print flag flag=hex(int(flag[::-1],10)) flag=(flag.upper())[2:] print flag[::-1][1:] exp = [] for val in m: exp.append(val!=t[val]) s.add(Or(exp))
最后求出结果为CBC25EF8D9F482BC1F3DA3CA1F154EC89FC3F1414EDD93A3,可以发现中间那个查表交换位置,其实就是在转圈圈,并没有修改数据,这就是题目名的含义。
文章来源:2019 KCTF 晋级赛Q1 | 第七题点评及解题思路