1.redemption_code 分析: 这题的重点在于理解验证兑换码函数 server_check_redemption_code 的算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 int server_check_redemption_code (char *str, char *input) { int len1 = strlen (str); int len2 = strlen (input); int tmp = len2; char s[14 *256 ]={0 }; int maxsize = tmp << 10 ; s[input[0 ]] = 1 ; int i,j; int v4; for (i = 1 ; i < tmp; ++i) { for (j = 0 ; j < 256 ; ++j) { if (j != input[i]) s[256 * i + j] = s[256 * v4 + j]; else s[256 * i + j] = i + 1 ; } v4 = s[256 * v4 + input[i]]; } int v7 = 0 ,k; for (k = 0 ; k < len1; ++k) { v7 = s[256 * v7 + str[k]]; if (v7 == tmp) return k - tmp + 1 ; } return -1 ; }
用图来解释一下
s数组相当于一个14*256的二维数组(输入长度得是14),函数内双重循环代表在以 flag[i] 为下标的地方存储当前的 i+1 值(第几个字符)
str1[] = “Ninja Must Die 3 Is A Cruel Game, So Hard For Me” str2[] = “I Love Ninja Must Die 3. Beautiful Art And Motive Operation Is Creative.”
pre 函数检测 str1,根据check函数最后的循环返回结果不能是 -1,确定 flag 字符串在 str1中
检测 str2 的时候因为返回值要是7,所以 flag 结尾字符是 str2[20] = ‘e’,flag = “Ninja Must Die”
验证: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <stdio.h> #include <stdlib.h> #include <string.h> int server_check_redemption_code (char *str, char *input) { int len1 = strlen (str); int len2 = strlen (input); int tmp = len2; char s[14 *256 ]={0 }; int maxsize = tmp << 10 ; s[input[0 ]] = 1 ; int i,j; int v4; for (i = 1 ; i < tmp; ++i) { for (j = 0 ; j < 256 ; ++j) { if (j != input[i]) s[256 * i + j] = s[256 * v4 + j]; else s[256 * i + j] = i + 1 ; } v4 = s[256 * v4 + input[i]]; } int v7 = 0 ,k; for (k = 0 ; k < len1; ++k) { v7 = s[256 * v7 + str[k]]; if (v7 == tmp) return k - tmp + 1 ; } return -1 ; } int main () { char str1[]="Ninja Must Die 3 Is A Cruel Game, So Hard For Me" ; char str2[]="I Love Ninja Must Die 3. Beautiful Art And Motive Operation Is Creative." ; char flag[]="Ninja Must Die" ; printf ("%d" ,server_check_redemption_code(str1,flag)); printf ("%d" ,server_check_redemption_code(str2,flag)); system("pause" ); return 0 ; }
2.GoEncrypt 分析: go 语言逆向,main_main 函数反汇编后可以看到有个 main_check ,main_NewCipher ,两个 Encrypt 加密,最后 internal_bytealg_Equal 比较 check 里面有 flag 的格式
^flag{([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})}
可以构造 flag 进行调试
flag{abababab-abab-abab-abab-abababababab}
图中的函数将输入转为16进制,如 0xabababababababababababababababab,之后再分割成4个16进制数 加密的函数根据特点可以知道是 XTEA 加密 调试到 encrypt 函数时可以发现两个 encrypt 分别对两个4位的16进制数进行加密,密钥如下
之后可以在 internal_bytealg_Equal 函数内找到 flag 的密文
前面那些看起来像是汇编代码的实际调试时并不执行,关键只在这一部分
脚本: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <stdio.h> #include <stdlib.h> #define uint32_t unsigned int void decipher (unsigned int num_rounds, uint32_t v[2 ], uint32_t const key[4 ]) { unsigned int i; uint32_t v0 = v[0 ], v1 = v[1 ], delta = 0x12345678 , sum = delta * num_rounds; for (i = 0 ; i < num_rounds; i++) { v1 -= (((v0 << 4 ) ^ (v0 >> 5 )) + v0) ^ (sum + key[(sum >> 11 ) & 3 ]); sum -= delta; v0 -= (((v1 << 4 ) ^ (v1 >> 5 )) + v1) ^ (sum + key[sum & 3 ]); } v[0 ] = v0; v[1 ] = v1; } int main () { uint32_t v[2 ] = { 0xedf5d910 ,0x542702cb }; uint32_t const k[4 ] = { 0x10203 ,0x4050607 ,0x8090a0b ,0xc0d0e0f }; unsigned int r = 32 ; decipher(r, v, k); printf ("解密后的数据:0x%x 0x%x\n" , v[0 ], v[1 ]); return 0 ; }
3.Crackme 反汇编出来 500 多行,要调试吐了。。。
分析: 先是输入一个长度为17的字符串,然后输入一个数字,对输入的数字进行运算后再判断
虽然这个函数 ida 提示说结果可能错误,但是实际运算跟反汇编出来的是一样的(比赛当天因为这个卡了好久,一直在试图调试出正确的运算过程)
想要得到这个数字可以采取爆破的方法,多次输入并调试来缩小范围
之后把第一个输入的字符串分为前7个和后10个,分别异或加密
脚本: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <stdio.h> #include <math.h> double cal (double a1,double a2) { double temp = pow (a1, a2 - 1.0 ); double result = temp / exp (a1); return result; } int main () { int v94 = 0.0 ; for (v94=90000 ;v94<100000000 ;v94++) { double v15 = 0.0 ; double v16 = 0.0 ; double v17 = 0.0 ; double v18 = (double )((int )v94 / 12379 ) + 1.0 ; do { v16 = v16 + cal(v17, v18) * 0.001 ; v17 = v17 + 0.001 ; } while ( v17 <= 100.0 ); double v19 = (int )(v16 + v16 + 3.0 ); double v20 = 0.0 ; double v21 = (double )(v94 % 12379 ) + 1.0 ; do { v15 = v15 + cal(v20, v21) * 0.001 ; v20 = v20 + 0.001 ; } while ( v20 <= 100.0 ); if (v19 == 0x13b03 && (int )(v15 + v15 + 3.0 ) == 0x5a2 ) { printf ("%d" ,v94); break ; } } system("pause" ); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 a7 = [0x08 , 0x4D , 0x59 , 0x06 , 0x73 , 0x02 , 0x40 ] number = "99038198076198076198076198076198076" k = [0xe0 ,0x95 ,0xba ,0x60 ,0xc9 ,0x66 ,0x2a ,0x24 ,0xb2 ,0x36 ] a10 = [0xB2 , 0xD6 , 0x8E , 0x3F , 0xAA , 0x14 , 0x53 , 0x54 , 0xC6 , 0x06 ] flag = [] for i in range (7 ): c = chr (a7[i] ^ ord (number[i])) flag.append(c) flag1 = '' .join(flag) print (flag1)for i in range (10 ): d = chr (k[i] ^ a10[i]) flag.append(d) flag_last = '' .join(flag) print (flag_last)