题好难…
Reverse
ezre
当时分析这个题不知道这是SM4,后来才知道就是一个标准的SM4.
以后遇到不认识的加密首先尝试去查找特征值!
这个题的真正逻辑不在main中,后来经分析貌似是一个子进程调试父进程的玩意…
检查逻辑在这里:
进入true_check0_3580()中(我自己重命名的函数名),可以看到加密过程:
实际上就是一个ECB-NoPadding模式的纯SM4…
其中v8是16byte的key;v7是加密后的32byte的密文.
提取出数据后写代码整理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <stdio.h>
int main() { unsigned long long v7[4]; v7[0] = 0x7C88631647197506LL; v7[1] = 0x4A0D7D3FFF55668BLL; v7[2] = 0xDEC2E93F384ED2F5LL; v7[3] = 0x3C1FB1746F7F7CDBLL; unsigned long long v8[2]; v8[0] = 0xEFCDAB8967452301LL; v8[1] = 0xEFCDAB8967452301LL; unsigned char *ptr = (unsigned char *) v8; for(int i=0;i<sizeof(v8);++i) printf("%02x",ptr[i]); putchar('\n'); ptr=(unsigned char *)v7; for(int i=0;i<sizeof(v7);++i) printf("%02x",ptr[i]); return 0; }
|
输出:
1 2
| 0123456789abcdef0123456789abcdef 067519471663887c8b6655ff3f7d0d4af5d24e383fe9c2dedb7c7f6f74b11f3c
|
直接在线解密即可:
强网先锋-ezre
两人合作做出来的…
其中
分析过程比较费劲,而且实际上完全没有必要…
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| __int64 __fastcall main(int a1, char **a2, char **a3) { int v3; size_t v4; int v5; int k; int j; int i; int v10; char v11[64]; char v12[64]; char v13[64]; char s[52]; int v15; size_t v16;
v15 = 0; printf("Welcome to the CTF world:"); memset(s, 0, 0x32uLL); __isoc99_scanf("%s", s); v16 = strlen(s); if (v16 != 34) { printf("Wrong!"); exit(-1); } v3 = strlen(s); v10 = 0; sub_401980((__int64)s, (__int64)v11, v3); while (v10 < 4) { srand(aLUsn4j5rfj0tav[2]); v4 = strlen((const char *)(unsigned int)aLUsn4j5rfj0tav); sub_401D10(aLUsn4j5rfj0tav, v4); if ((v10 & 1) != 0) { v5 = strlen(v11); sub_401980((__int64)v11, (__int64)v12, v5); } else { sub_401250(v11, v12); } memset(v11, 0, 0x32uLL); memcpy(v11, v12, 0x32uLL); ++v10; } if (check_debug == 1) { sub_402EE0(aLUsn4j5rfj0tav, &aLUsn4j5rfj0tav[64]); for (i = 0; i < 64; ++i) aLUsn4j5rfj0tav[i] = (5 * (aLUsn4j5rfj0tav[i] + 3)) ^ 0x15; } else { for (j = 0; j < 64; ++j) aLUsn4j5rfj0tav[j] ^= 0x27u; } sub_401EB0(v12, v13); for (k = 0; ; ++k) { if (k >= strlen(v12)) { printf("right!"); return 0; } if (aKqhfyc[k] != v13[k]) break; } printf("wrong!"); return 0; }
|
接下来就是该死的sub_401250(),后来发现这玩意就是一个标准至极的base64_decode()…只不过变表了而已…
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| __int64 __fastcall sub_401250(__int64 str, __int64 res) { int v2; int v3; int v4; unsigned __int8 idx_1; unsigned __int8 idx_2; unsigned __int8 idx_3; unsigned __int8 idx_4; unsigned __int8 i; unsigned int res_idx; int str_idx; __int64 res_p; __int64 str_p;
str_p = str; res_p = res; str_idx = 0; res_idx = 0; while ( *(_BYTE *)(str_p + str_idx) ) { memset(&idx_1, 0, 4uLL); for ( i = 0; i < 64u; ++i ) { if ( *(char *)(str_p + str_idx) == aLUsn4j5rfj0tav[i] ) { idx_1 = i; break; } } for ( i = 0; i < 64u; ++i ) { if ( *(char *)(str_p + str_idx + 1) == aLUsn4j5rfj0tav[i] ) { idx_2 = i; break; } } v2 = res_idx++; *(_BYTE *)(res_p + v2) = ((int)idx_2 >> 4) & 3 | (4 * idx_1); if ( *(_BYTE *)(str_p + str_idx + 2) == '=' ) break; for ( i = 0; i < 64u; ++i ) { if ( *(char *)(str_p + str_idx + 2) == aLUsn4j5rfj0tav[i] ) { idx_3 = i; break; } } v3 = res_idx++; *(_BYTE *)(res_p + v3) = ((int)idx_3 >> 2) & 0xF | (16 * idx_2); if ( *(_BYTE *)(str_p + str_idx + 3) == 61 ) break; for ( i = 0; i < 64u; ++i ) { if ( *(char *)(str_p + str_idx + 3) == aLUsn4j5rfj0tav[i] ) { idx_4 = i; break; } } v4 = res_idx++; *(_BYTE *)(res_p + v4) = idx_4 & 0x3F | (idx_3 << 6); str_idx += 4; } *(_BYTE *)(res_p + (int)res_idx) = 0; return res_idx; }
|
整个加密主要就是变表的base64,动调跑出来所有用到的base64表:
1 2 3 4 5
| "l+USN4J5Rfj0TaVOcnzXiPGZIBpoAExuQtHyKD692hwmqe7/Mgk8v1sdCW3bYFLr", "FGseVD3ibtHWR1czhLnUfJK6SEZ2OyPAIpQoqgY0w49u+7rad5CxljMXvNTBkm/8", "Hc0xwuZmy3DpQnSgj2LhUtrlVvNYks+BX/MOoETaKqR4eb9WF8ICGzf6id1P75JA", "pnHQwlAveo4DhGg1jE3SsIqJ2mrzxCiNb+Mf0YVd5L8c97/WkOTtuKFZyRBUPX6a", "plxXOZtaiUneJIhk7qSYEjD1Km94o0FTu52VQgNL3vCBH8zsA/b+dycGPRMwWfr6"
|
最后的那个sub_401EB0(v12,v13);需要写出逆函数,分析过程没了,总之就是使用最后的base64表做一个处理,生成一个新的表,然后用它来把数据做最后一次加密.
但是其中用求模等等的运算,后来还是想办法逆出来了.
所以总的加密过程就是:
- 将flag做一次base64编码.
- 然后交替的进行base64解码和编码各2次,一共4次.
- 最后做一次sub_401EB0()加密.
其中base64表就是用上面提取出来的表.
解密过程就反向即可,首先是对最后一个加密函数sub_401EB0()的逆变换:
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| #include <iostream> #include <string> #include <algorithm> #include <cstring> #include <cctype> #include <stdint.h>
int main() { unsigned char aKqhfyc[48] = { 0x3A, 0x2C, 0x4B, 0x51, 0x68, 0x46, 0x59, 0x63, 0x24, 0x04, 0x5E, 0x5F, 0x00, 0x0C, 0x2B, 0x03, 0x29, 0x5C, 0x74, 0x70, 0x6A, 0x62, 0x7F, 0x3D, 0x2C, 0x4E, 0x6F, 0x13, 0x06, 0x0D, 0x06, 0x0C, 0x4D, 0x56, 0x0F, 0x28, 0x4D, 0x51, 0x76, 0x70, 0x2B, 0x05, 0x51, 0x68, 0x48, 0x55, 0x24, 0x19 };
int v7 = 2023; int v7_arr[100]; for (int v6 = 0; v6 < 48; ++v6) { if (v6 % 3 == 1) { v7 = (v7 + 5) % 20; v7_arr[v6] = v7; } else if (v6 % 3 == 2) { v7 = (v7 + 7) % 19; v7_arr[v6] = v7; } else { v7 = (v7 + 3) % 17; v7_arr[v6] = v7; } }
char base64_table[] = "plxXOZtaiUneJIhk7qSYEjD1Km94o0FTu52VQgNL3vCBH8zsA/b+dycGPRMwWfr6"; for (int i = 0; i < 64; ++i) base64_table[i] ^= 0x27;
char v5[30]; strncpy(v5, &base64_table[6], 21); printf("table:\n"); for (int i = 0; i < 21; ++i) printf("%d,", v5[i]); char v3; for (int i = 46; i >= 0; --i) { if (i % 3 == 1) { v3 = v5[v7_arr[i] + 1]; } else if (i % 3 == 2) { v3 = v5[v7_arr[i] + 2]; } else { v3 = v5[v7_arr[i] + 3]; } aKqhfyc[i + 1] ^= aKqhfyc[i]; aKqhfyc[i] ^= v3; } printf("\nenc(last):\n"); for (int i = 0; i < 48; ++i) printf("%c", aKqhfyc[i]); return 0; }
|
然后将该结果依次进行对应的base64编码解码:
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 46 47 48 49 50 51 52 53 54 55 56 57
| import binascii import urllib.parse import base64
def base64_decode(enc='', new_table='') -> bytes: if new_table == '': print('basic base64 decode!') return base64.b64decode(enc) else: print('base64 decode with new_table: ' + new_table) orignal_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' dec = base64.b64decode(enc.translate(str.maketrans(new_table, orignal_table))) return dec
def base64_encode(src=b'', new_table='') -> str: if new_table == b'': print('basic base64 encode!') return base64.b64encode(src).decode() else: print('base64 encode with new_table: ' + new_table) orignal_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' enc = base64.b64encode(src).decode().translate(str.maketrans(orignal_table, new_table)) return enc
def run_decode(): fir_table = "l+USN4J5Rfj0TaVOcnzXiPGZIBpoAExuQtHyKD692hwmqe7/Mgk8v1sdCW3bYFLr" tables = [ "FGseVD3ibtHWR1czhLnUfJK6SEZ2OyPAIpQoqgY0w49u+7rad5CxljMXvNTBkm/8", "Hc0xwuZmy3DpQnSgj2LhUtrlVvNYks+BX/MOoETaKqR4eb9WF8ICGzf6id1P75JA", "pnHQwlAveo4DhGg1jE3SsIqJ2mrzxCiNb+Mf0YVd5L8c97/WkOTtuKFZyRBUPX6a", "plxXOZtaiUneJIhk7qSYEjD1Km94o0FTu52VQgNL3vCBH8zsA/b+dycGPRMwWfr6" ] flag = 'WZqSWcUtWBLlOriEfcajWBSRstLlkEfFWR7j/R7dMCDGnp==' print(flag) for i in reversed(range(4)): if i % 2 != 0: flag = base64_decode(flag, tables[i]) print(flag) else: flag = base64_encode(flag, tables[i]) print(flag) flag = base64_decode(flag, fir_table) print(flag.decode())
if __name__ == '__main__': run_decode()
|
最终跑出的结果缺一个右花括号"}"
,加上即可:
结果:flag{3ea590ccwxehg715264fzxnzepqz}
结束…
附注当时写的sub_401250()的逆函数:(后来发现纯闲的…这个逆函数是否正确也忘记了…)
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
| #include <stdio.h> #include <string.h> char table[64];
int reverse_sub_401250(char *str, char *res) {
int idx_1, idx_2, idx_3, idx_4; int res_idx = 0;
int len = strlen(str); for (int str_idx = 0; str_idx < len; str_idx += 3) { int cur_case = 0; if (str[str_idx + 2] == '\0') cur_case = 1; else if (str[str_idx + 1] == '\0') cur_case = 2;
if (cur_case == 0) { unsigned char result; unsigned char tmp1, tmp2;
tmp1 = str[str_idx]; result = (tmp1 >> 2); res[res_idx++] = table[result];
tmp1 = str[str_idx]; tmp2 = str[str_idx + 1]; result = ((tmp1 & 0x3) << 4) | (tmp2 >> 4); res[res_idx++] = table[result];
tmp1 = str[str_idx + 1]; tmp2 = str[str_idx + 2]; result = ((tmp1 & 0xf) << 2) | (tmp2 >> 6); res[res_idx++] = table[result];
tmp1 = str[str_idx + 2]; result = (tmp1 & 0x3f); res[res_idx++] = table[result];
} else if (cur_case == 1) { unsigned char result; unsigned char tmp1, tmp2;
tmp1 = str[str_idx]; result = (tmp1 >> 2); res[res_idx++] = table[result];
tmp1 = str[str_idx]; tmp2 = str[str_idx + 1]; result = ((tmp1 & 0x3) << 4) | (tmp2 >> 4); res[res_idx++] = table[result];
tmp2 = str[str_idx + 2]; result = ((tmp2 & 0xf) << 2); res[res_idx++] = table[result];
res[res_idx++] = '='; } else if (cur_case == 2) { unsigned char result; unsigned char tmp1;
tmp1 = str[str_idx]; result = (tmp1 >> 2); res[res_idx++] = table[result];
tmp1 = str[str_idx]; result = ((tmp1 & 0x3) << 4); res[res_idx++] = table[result];
res[res_idx++] = '='; res[res_idx++] = '='; } } }
|