-
-
[原创] 看雪 2025 KCTF 第二题 初窥门径
-
发表于: 2025-8-16 17:46 5209
-
_main(0x7014C0)函数获取输入字符串,长度不超过 112
从 sub_701280 进入,有两处跳转
到达 0x70129F ,是主要逻辑所在。
创建一个函数反编译:
(以下结合AI辅助分析)
中间的while循环将输入的字符v15转换为0-35的整数v12("0123456789abcdefghijklmnopqrstuvwxyz"的索引),按照 0-11, 12-23, 24-35 分别对应 z 的值 0, 1, 2;偶数索引的值 % 12 作为 y 坐标,奇数索引的值 % 12 作为 x 坐标;每两个输入字符在奇数索引处进行合法性检查(dword_703020 数组在坐标位置的值满足 sub_7011C0 的要求)。
sub_701000 是牛顿法计算平方根,sub_701170 是质数检验,sub_7011C0 前半部分内联了 sub_701170 ,整体作用是尝试把参数拆分为两个质数相乘。
dword_703020 全局常量变量是 3 * 10 * 10 的表,对应 x, y, z 坐标,并且可以离线按照 sub_7011C0 的逻辑找到所有满足条件的位置。
符合条件的位置有 56 个,为 [0, 1, 11, 35, 45, 53, 55, 56, 57, 63, 67, 71, 72, 73, 101, 102, 103, 107, 108, 113, 117, 121, 122, 123, 125, 126, 127, 131, 141, 142, 143, 154, 155, 156, 157, 160, 161, 170, 198, 208, 209, 219, 229, 238, 239, 244, 248, 254, 258, 260, 261, 262, 263, 264, 268, 269] (注意输入长度限制为112,恰好是56的两倍,大致能对应两个输入值做一次检测)
sub_70129F 需要转换后的输入坐标位于符合条件的位置,且相邻坐标的距离等于1,且固定首位的位置。将56个位置转换到坐标并重新排列后得到 [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (2, 1, 1), (3, 1, 1), (3, 2, 1), (3, 3, 1), (2, 3, 1), (1, 3, 1), (1, 4, 1), (1, 5, 1), (2, 5, 1), (3, 5, 1), (3, 5, 0), (3, 6, 0), (3, 7, 0), (2, 7, 0), (1, 7, 0), (1, 7, 1), (0, 7, 1), (0, 8, 1), (0, 8, 2), (1, 8, 2), (2, 8, 2), (3, 8, 2), (4, 8, 2), (4, 7, 2), (4, 6, 2), (4, 6, 1), (5, 6, 1), (6, 6, 1), (7, 6, 1), (7, 6, 0), (7, 5, 0), (6, 5, 0), (5, 5, 0), (5, 4, 0), (5, 3, 0), (5, 3, 1), (6, 3, 1), (7, 3, 1), (7, 2, 1), (7, 1, 1), (8, 1, 1), (8, 1, 2), (8, 2, 2), (9, 2, 2), (9, 3, 2), (9, 4, 2), (9, 5, 2), (8, 5, 2), (8, 6, 2), (8, 7, 2), (8, 8, 2), (9, 8, 2)]
最后反向转换回输入的格式,验证通过。
一个正确的输入: 0001111d1e1f2f3f3e3d4d5d5e5f53637372717d7c8c8o8p8q8r8s7s6s6g6h6i6j6757565545353h3i3j2j1j1k1w2w2x3x4x5x5w6w7w8w8x
多解:y 坐标的转换很松散,只检查了 % 12 却没有限制 // 12 ,所以输入序列的每个偶数位置都有两到三个合法的取值。例如 c001111d1e1f2f3f3e3d4d5d5e5f53637372717d7c8c8o8p8q8r8s7s6s6g6h6i6j6757565545353h3i3j2j1j1k1w2w2x3x4x5x5w6w7w8w8x。另外,x 坐标转换时 % 的是 12 ,而 dword_703020 的横坐标范围是 10 ,也可能造成影响。
.text:007011B6 ; void sub_7011B6().text:007011B6 sub_7011B6 proc near ; CODE XREF: sub_701280+14↓p.text:007011B6.text:007011B6 arg_0 = dword ptr 4.text:007011B6.text:007011B6 mov [esp+arg_0], offset sub_701EEB.text:007011BE retn.text:007011BE sub_7011B6 endp.text:007011B6 ; void sub_7011B6().text:007011B6 sub_7011B6 proc near ; CODE XREF: sub_701280+14↓p.text:007011B6.text:007011B6 arg_0 = dword ptr 4.text:007011B6.text:007011B6 mov [esp+arg_0], offset sub_701EEB.text:007011BE retn.text:007011BE sub_7011B6 endp.text:00701B25 sub_701B25 proc near ; CODE XREF: sub_701EEB+B↓p.text:00701B25.text:00701B25 arg_0 = dword ptr 4.text:00701B25.text:00701B25 mov [esp+arg_0], offset loc_70129F.text:00701B2D retn.text:00701B2D sub_701B25 endp.text:00701B25 sub_701B25 proc near ; CODE XREF: sub_701EEB+B↓p.text:00701B25.text:00701B25 arg_0 = dword ptr 4.text:00701B25.text:00701B25 mov [esp+arg_0], offset loc_70129F.text:00701B2D retn.text:00701B2D sub_701B25 endp// positive sp value has been detected, the output may be wrong!char __usercall sub_70129F@<al>( int a1@<eax>, int a2@<edx>, int a3@<ebp>, const char *a4@<esi>, int a5, int a6, int a7, int a8, int a9, int a10, int a11){ int v11; // edi int v12; // ecx char *index; // edx int y; // ebx char v15; // al int z; // esi char *v18; // ecx int v19; // edx int x; // edi char *v21; // ecx char *retaddr; // [esp+0h] [ebp+0h] int input_length; // [esp+4h] [ebp+4h] int v24; // [esp+8h] [ebp+8h] int lastx; // [esp+Ch] [ebp+Ch] int lasty; // [esp+10h] [ebp+10h] int lastz; // [esp+14h] [ebp+14h] if ( a1 != a2 ) { v11 = strlen(a4); v12 = 0; index = 0; y = 0; input_length = v11; lastz = 0; lasty = 0; lastx = 0; v24 = 0; retaddr = 0; if ( v11 > 0 ) { while ( 1 ) { v15 = a4[(_DWORD)index]; while ( a0123456789abcd[v12] != v15 ) { if ( a0123456789abcd[v12 + 1] == v15 ) { ++v12; break; } if ( a0123456789abcd[v12 + 2] == v15 ) { v12 += 2; break; } if ( a0123456789abcd[v12 + 3] == v15 ) { v12 += 3; break; } if ( a0123456789abcd[v12 + 4] == v15 ) { v12 += 4; break; } if ( a0123456789abcd[v12 + 5] == v15 ) { v12 += 5; break; } v12 += 6; if ( v12 >= 36 ) return 0; } if ( v12 >= 36 ) break; if ( v12 >= 12 ) z = (v12 >= 24) + 1; else z = 0; if ( ((unsigned __int8)index & 1) != 0 ) { v19 = v24 + 1; x = v12 % 12; v24 = v19; if ( v19 == 1 && dword_703020[80 * z + 10 * z + 10 * y + x] != 0x2C0E ) return 0; v18 = retaddr; if ( retaddr == (char *)(input_length - 1) && dword_703020[80 * z + 10 * z + 10 * y + x] != 0x1FB2 ) return 0; if ( v19 > 1 ) { if ( (int)retaddr < input_length && (!sub_7011C0(dword_703020[80 * z + 10 * z + 10 * y + x], &a10, &a11) || a10 < 2 || a11 < 2 || a10 == a11) || abs32(x - lastx) + abs32(y - lasty) + abs32(z - lastz) != 1 ) { return 0; } v18 = retaddr; } lastx = x; lasty = y; lastz = z; if ( v18 == (char *)(input_length - 1) && dword_703020[80 * z + 10 * z + 10 * y + x] == 8114 ) return 1; v11 = input_length; } else { y = v12 % 12; v18 = retaddr; } v21 = v18 + 1; retaddr = v21; if ( (int)v21 >= v11 ) return 0; a4 = *(const char **)(a3 + 8); index = v21; v12 = 0; } } } return 0;}// positive sp value has been detected, the output may be wrong!char __usercall sub_70129F@<al>( int a1@<eax>, int a2@<edx>, int a3@<ebp>, const char *a4@<esi>, int a5, int a6, int a7, int a8, int a9, int a10, int a11){ int v11; // edi int v12; // ecx char *index; // edx int y; // ebx char v15; // al int z; // esi char *v18; // ecx int v19; // edx int x; // edi char *v21; // ecx char *retaddr; // [esp+0h] [ebp+0h] int input_length; // [esp+4h] [ebp+4h] int v24; // [esp+8h] [ebp+8h] int lastx; // [esp+Ch] [ebp+Ch] int lasty; // [esp+10h] [ebp+10h] int lastz; // [esp+14h] [ebp+14h] if ( a1 != a2 ) { v11 = strlen(a4); v12 = 0; index = 0; y = 0; input_length = v11; lastz = 0; lasty = 0; lastx = 0; v24 = 0; retaddr = 0; if ( v11 > 0 ) { while ( 1 ) { v15 = a4[(_DWORD)index]; while ( a0123456789abcd[v12] != v15 ) { if ( a0123456789abcd[v12 + 1] == v15 ) { ++v12; break; } if ( a0123456789abcd[v12 + 2] == v15 ) { v12 += 2; break; } if ( a0123456789abcd[v12 + 3] == v15 ) { v12 += 3; break; } if ( a0123456789abcd[v12 + 4] == v15 ) { v12 += 4; break; } if ( a0123456789abcd[v12 + 5] == v15 ) { v12 += 5; break; } v12 += 6; if ( v12 >= 36 ) return 0; } if ( v12 >= 36 ) break; if ( v12 >= 12 ) z = (v12 >= 24) + 1; else z = 0; if ( ((unsigned __int8)index & 1) != 0 ) { v19 = v24 + 1; x = v12 % 12; v24 = v19; if ( v19 == 1 && dword_703020[80 * z + 10 * z + 10 * y + x] != 0x2C0E ) return 0; v18 = retaddr; if ( retaddr == (char *)(input_length - 1) && dword_703020[80 * z + 10 * z + 10 * y + x] != 0x1FB2 ) return 0; if ( v19 > 1 ) { if ( (int)retaddr < input_length && (!sub_7011C0(dword_703020[80 * z + 10 * z + 10 * y + x], &a10, &a11) || a10 < 2 || a11 < 2 || a10 == a11) || abs32(x - lastx) + abs32(y - lasty) + abs32(z - lastz) != 1 ) { return 0; } v18 = retaddr; } lastx = x; lasty = y; lastz = z; if ( v18 == (char *)(input_length - 1) && dword_703020[80 * z + 10 * z + 10 * y + x] == 8114 ) return 1; v11 = input_length; } else { y = v12 % 12; v18 = retaddr; } v21 = v18 + 1; retaddr = v21; if ( (int)v21 >= v11 ) return 0; a4 = *(const char **)(a3 + 8); index = v21; v12 = 0; } } } return 0;}char __cdecl sub_7011C0(int a1, int *a2, int *a3){ int v4; // edi int v5; // ecx int v6; // esi int v7; // ecx int v9; // [esp+1Ch] [ebp-4h] int v10; // [esp+24h] [ebp+4h] if ( a1 < 4 ) return 0; v4 = 2; v9 = 2; v5 = (int)isqrt((double)a1); // sub_701000 v10 = v5; if ( v5 < 2 ) return 0; while ( 1 ) { if ( a1 % v4 || v4 < 2 ) goto LABEL_10; v6 = 2; v7 = (int)isqrt((double)v9); // sub_701000 if ( v7 >= 2 ) { while ( v4 % v6 ) { if ( ++v6 > v7 ) goto LABEL_8; } goto LABEL_9; }LABEL_8: if ( is_prime(a1 / v4) ) // sub_701170 break;LABEL_9: v5 = v10;LABEL_10: v9 = ++v4; if ( v4 > v5 ) return 0; } if ( a2 ) *a2 = v4; if ( a3 ) *a3 = a1 / v4; return 1;}char __cdecl sub_7011C0(int a1, int *a2, int *a3){ int v4; // edi int v5; // ecx int v6; // esi int v7; // ecx int v9; // [esp+1Ch] [ebp-4h] int v10; // [esp+24h] [ebp+4h] if ( a1 < 4 ) return 0; v4 = 2; v9 = 2; v5 = (int)isqrt((double)a1); // sub_701000 v10 = v5; if ( v5 < 2 )[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!