随缘刷题
[网鼎杯 2020 青龙组]singal
链接: https://buuoj.cn/challenges#[网鼎杯 2020 青龙组]singal
EXE 32
IDA中 _main 函数反编译结果如下
1 2 3 4 5 6 7 8 9 10 int __cdecl main (int argc, const char **argv, const char **envp) { int v4[117 ]; __main(); qmemcpy(v4, dword_403040, 456u ); vm_operad(v4, 114 ); puts ("good,The answer format is:flag {}" ); return 0 ; }
这里dword_403040
只运算最低一字节
1 2 3 4 5 6 7 8 9 dword_403040 = [ 0x0A , 0x04 , 0x10 , 0x08 , 0x03 , 0x05 , 0x01 , 0x04 , 0x20 , 0x08 , 0x05 , 0x03 , 0x01 , 0x03 , 0x02 , 0x08 , 0x0B , 0x01 , 0x0C , 0x08 , 0x04 , 0x04 , 0x01 , 0x05 , 0x03 , 0x08 , 0x03 , 0x21 , 0x01 , 0x0B , 0x08 , 0x0B , 0x01 , 0x04 , 0x09 , 0x08 , 0x03 , 0x20 , 0x01 , 0x02 , 0x51 , 0x08 , 0x04 , 0x24 , 0x01 , 0x0C , 0x08 , 0x0B , 0x01 , 0x05 , 0x02 , 0x08 , 0x02 , 0x25 , 0x01 , 0x02 , 0x36 , 0x08 , 0x04 , 0x41 , 0x01 , 0x02 , 0x20 , 0x08 , 0x05 , 0x01 , 0x01 , 0x05 , 0x03 , 0x08 , 0x02 , 0x25 , 0x01 , 0x04 , 0x09 , 0x08 , 0x03 , 0x20 , 0x01 , 0x02 , 0x41 , 0x08 , 0x0C , 0x01 , 0x07 , 0x22 , 0x07 , 0x3F , 0x07 , 0x34 , 0x07 , 0x32 , 0x07 , 0x72 , 0x07 , 0x33 , 0x07 , 0x18 , 0x07 , 0xA7 , 0x07 , 0x31 , 0x07 , 0xF1 , 0x07 , 0x28 , 0x07 , 0x84 , 0x07 , 0xC1 , 0x07 , 0x1E , 0x07 , 0x7A ]
跟进到 vm_operad 函数
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 int __cdecl vm_operad (int *a1, int a2) { int result; char Str[200 ]; char v4; int v5; int v6; int v7; int v8; int v9; v9 = 0 ; v8 = 0 ; v7 = 0 ; v6 = 0 ; v5 = 0 ; while ( 1 ) { result = v9; if ( v9 >= a2 ) return result; switch ( a1[v9] ) { case 1 : Str[v6 + 100 ] = v4; ++v9; ++v6; ++v8; break ; case 2 : v4 = a1[v9 + 1 ] + Str[v8]; v9 += 2 ; break ; case 3 : v4 = Str[v8] - LOBYTE(a1[v9 + 1 ]); v9 += 2 ; break ; case 4 : v4 = a1[v9 + 1 ] ^ Str[v8]; v9 += 2 ; break ; case 5 : v4 = a1[v9 + 1 ] * Str[v8]; v9 += 2 ; break ; case 6 : ++v9; break ; case 7 : if ( Str[v7 + 100 ] != a1[v9 + 1 ] ) { printf ("what a shame..." ); exit (0 ); } ++v7; v9 += 2 ; break ; case 8 : Str[v5] = v4; ++v9; ++v5; break ; case 10 : read(Str); ++v9; break ; case 11 : v4 = Str[v8] - 1 ; ++v9; break ; case 12 : v4 = Str[v8] + 1 ; ++v9; break ; default : continue ; } } }
根据这里的switch语句来分析
switch里对于各个数值的操作有些与CPU指令相似
原来这就是虚拟机逆向啊
0x01
和0x08
分别决定计算结果的存储位置
0x02
计算加法, 0x03
计算减法, 0x04
计算异或, 0x05
计算乘法
0x0B
自增, 0x1C
自减
0x0A
输入, 0x07
用于校验加密后的结果
然后照着代码把虚拟机函数反着写一遍即可获得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 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 a1 = [ 0x0A , 0x04 , 0x10 , 0x08 , 0x03 , 0x05 , 0x01 , 0x04 , 0x20 , 0x08 , 0x05 , 0x03 , 0x01 , 0x03 , 0x02 , 0x08 , 0x0B , 0x01 , 0x0C , 0x08 , 0x04 , 0x04 , 0x01 , 0x05 , 0x03 , 0x08 , 0x03 , 0x21 , 0x01 , 0x0B , 0x08 , 0x0B , 0x01 , 0x04 , 0x09 , 0x08 , 0x03 , 0x20 , 0x01 , 0x02 , 0x51 , 0x08 , 0x04 , 0x24 , 0x01 , 0x0C , 0x08 , 0x0B , 0x01 , 0x05 , 0x02 , 0x08 , 0x02 , 0x25 , 0x01 , 0x02 , 0x36 , 0x08 , 0x04 , 0x41 , 0x01 , 0x02 , 0x20 , 0x08 , 0x05 , 0x01 , 0x01 , 0x05 , 0x03 , 0x08 , 0x02 , 0x25 , 0x01 , 0x04 , 0x09 , 0x08 , 0x03 , 0x20 , 0x01 , 0x02 , 0x41 , 0x08 , 0x0C , 0x01 ]a1.reverse() a2 = [ 0x07 , 0x22 , 0x07 , 0x3F , 0x07 , 0x34 , 0x07 , 0x32 , 0x07 , 0x72 , 0x07 , 0x33 , 0x07 , 0x18 , 0x07 , 0xA7 , 0x07 , 0x31 , 0x07 , 0xF1 , 0x07 , 0x28 , 0x07 , 0x84 , 0x07 , 0xC1 , 0x07 , 0x1E , 0x07 , 0x7A ]cipher = [] for _ in range (len (a2)): if a2[_] == 0x07 : cipher.append(a2[_+1 ]) cipher.reverse() flag = "" v4 = 0 v6 = 0 v8 = 0 v9 = 0 while v9 < len (a1)-1 : if a1[v9] == 0x01 : v4 = cipher[v6] v6 += 1 v8 += 1 v9 += 1 if a1[v9+1 ] == 0x02 : v8 = v4 - a1[v9] v9 += 2 elif a1[v9+1 ] == 0x03 : v8 = v4 + a1[v9] v9 += 2 elif a1[v9+1 ] == 0x04 : v8 = v4 ^ a1[v9] v9 += 2 elif a1[v9+1 ] == 0x05 : v8 = v4 // a1[v9] v9 += 2 elif a1[v9] == 0x06 : v9 += 1 elif a1[v9] == 0x0B : v8 = v4 + 1 v9 += 1 elif a1[v9] == 0x0C : v8 = v4 - 1 v9 += 1 elif a1[v9] == 0x08 : v4 = v8 v9 += 1 if a1[v9+1 ] == 0x02 : v8 = v4 - a1[v9] v9 += 2 elif a1[v9+1 ] == 0x03 : v8 = v4 + a1[v9] v9 += 2 elif a1[v9+1 ] == 0x04 : v8 = v4 ^ a1[v9] v9 += 2 elif a1[v9+1 ] == 0x05 : v8 = v4 // a1[v9] v9 += 2 elif a1[v9] == 0x06 : v9 += 1 elif a1[v9] == 0x0B : v8 = v4 + 1 v9 += 1 elif a1[v9] == 0x0C : v8 = v4 - 1 v9 += 1 flag += chr (v8) print("flag{" + flag[::-1 ] + "}" )
flag{757515121f3d478}
[GXYCTF2019]simple CPP
链接: https://buuoj.cn/challenges#[GXYCTF2019]simple CPP
EXE 64
IDA中 main 函数反编译结果如下
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 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 int __cdecl main (int argc, const char **argv, const char **envp) { bool v3; __int64 v4; __int64 v5; __int64 v6; unsigned __int8 *v7; unsigned __int8 *v8; int v9; __int64 v10; void **v11; void **v12; __int64 v13; __int64 v14; __int64 v15; __int64 v16; int v17; unsigned __int8 *v18; __int64 v19; __int64 *v20; __int64 v21; __int64 v22; __int64 *v23; __int64 v24; __int64 v25; __int64 v26; __int64 v27; __int64 v28; __int64 v29; __int64 v30; __int64 v31; __int64 v32; __int64 v33; bool v34; __int64 v35; void **v36; __int64 v37; __int64 v38; __int64 v39; void *v40; __int64 v42; void *Block[2 ]; unsigned __int64 v44; unsigned __int64 v45; v3 = 0 ; v44 = 0 i64; v45 = 15 i64; LOBYTE(Block[0 ]) = 0 ; v4 = sub_7FF69CF519C0(std ::cout , "I'm a first timer of Logic algebra , how about you?" , envp); std ::ostream::operator <<(v4, sub_7FF69CF51B90); sub_7FF69CF519C0(std ::cout , "Let's start our game,Please input your flag:" , v5); sub_7FF69CF51DE0(std ::cin , Block); std ::ostream::operator <<(std ::cout , sub_7FF69CF51B90); if ( v44 - 5 > 0x19 ) { v39 = sub_7FF69CF519C0(std ::cout , "Wrong input ,no GXY{} in input words" , v6); std ::ostream::operator <<(v39, sub_7FF69CF51B90); goto LABEL_43; } v7 = (unsigned __int8 *)operator new (0x20 ui64); v8 = v7; if ( v7 ) { *(_QWORD *)v7 = 0 i64; *((_QWORD *)v7 + 1 ) = 0 i64; *((_QWORD *)v7 + 2 ) = 0 i64; *((_QWORD *)v7 + 3 ) = 0 i64; } else { v8 = 0 i64; } v9 = 0 ; if ( v44 ) { v10 = 0 i64; do { v11 = Block; if ( v45 >= 0x10 ) v11 = (void **)Block[0 ]; v12 = &qword_7FF69CF56048; if ( (unsigned __int64)qword_7FF69CF56060 >= 0x10 ) v12 = (void **)qword_7FF69CF56048; v8[v10] = *((byte_0 *)v11 + v10) ^ *((byte_0 *)v12 + v9 % 27 ); ++v9; ++v10; } while ( v9 < v44 ); } v13 = 0 i64; v14 = 0 i64; v15 = 0 i64; v16 = 0 i64; if ( (int )v44 > 30 ) goto LABEL_27; v17 = 0 ; if ( (int )v44 <= 0 ) goto LABEL_27; v18 = v8; do { v19 = *v18 + v13; ++v17; ++v18; switch ( v17 ) { case 8 : v16 = v19; goto LABEL_23; case 16 : v15 = v19; goto LABEL_23; case 24 : v14 = v19; LABEL_23: v19 = 0 i64; break ; case 32 : sub_7FF69CF519C0(std ::cout , "ERRO,out of range" , (unsigned int )v44); exit (1 ); } v13 = v19 << 8 ; } while ( v17 < (int )v44 ); if ( v16 ) { v20 = (__int64 *)operator new (0x20 ui64); *v20 = v16; v20[1 ] = v15; v20[2 ] = v14; v20[3 ] = v13; goto LABEL_28; } LABEL_27: v20 = 0 i64; LABEL_28: v42 = v20[2 ]; v21 = v20[1 ]; v22 = *v20; v23 = (__int64 *)operator new (0x20 ui64); if ( IsDebuggerPresent() ) { sub_7FF69CF519C0(std ::cout , "Hi , DO not debug me !" , v24); Sleep(0x7D0 u); exit (0 ); } v25 = v21 & v22; *v23 = v21 & v22; v26 = v42 & ~v22; v23[1 ] = v26; v27 = ~v21; v28 = v42 & v27; v23[2 ] = v42 & v27; v29 = v22 & v27; v23[3 ] = v29; if ( v26 != 0x11204161012 i64 ) { v23[1 ] = 0 i64; v26 = 0 i64; } v30 = v26 | v25 | v28 | v29; v31 = v20[1 ]; v32 = v20[2 ]; v33 = v28 & *v20 | v32 & (v25 | v31 & ~*v20 | ~(v31 | *v20)); v34 = 0 ; if ( v33 == 0x8020717153E3013 i64 ) v34 = v30 == 0x3E3A4717373E7F1F i64; if ( (v30 ^ v20[3 ]) == 0x3E3A4717050F791F i64 ) v3 = v34; if ( (v26 | v25 | v31 & v32) == (~*v20 & v32 | 0xC00020130082C0C i64) && v3 ) { v35 = sub_7FF69CF519C0(std ::cout , "Congratulations!flag is GXY{" , v33); v36 = Block; if ( v45 >= 0x10 ) v36 = (void **)Block[0 ]; v37 = sub_7FF69CF51FD0(v35, v36, v44); sub_7FF69CF519C0(v37, "}" , v38); j_j_free(v8); } else { sub_7FF69CF519C0(std ::cout , "Wrong answer!try again" , v33); j_j_free(v8); } LABEL_43: if ( v45 >= 0x10 ) { v40 = Block[0 ]; if ( v45 + 1 >= 0x1000 ) { v40 = (void *)*((_QWORD *)Block[0 ] - 1 ); if ( (unsigned __int64)(Block[0 ] - v40 - 8 ) > 0x1F ) invalid_parameter_noinfo_noreturn(); } j_j_free(v40); } return 0 ; }
主函数一堆的运算过程看起来有点无从下手
仔细分析一遍之后可以得出下面的流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 v12 = "i_will_check_is_debug_or_not" v8 = plain ^ v12 v16 = v8[:8] v15 = v8[8:16] v14 = v8[16:24] v13 = v8[24:] v25 = v15 & v16 v26 = v14 & ~v16 v27 = ~v15 v28 = v14 & ~v15 v29 = v16 & ~v15 v23[0] = v15 & v16 v23[1] = v14 & ~v16 v23[2] = v14 & ~v15 v23[3] = v16 & ~v15 v14 & ~v16 == 0x11204161012 v30 = v25 | v26 | v28 | v29 == 0x3E3A4717373E7F1F v31 = v15 v32 = v14 v33 = v28 & v16 | v32 & (v25 | v31 & ~v16 | ~(v31 | v16)) == 0x8020717153E3013 v30 ^ v13 == 0x3E3A4717050F791F (v26 | v25 | v31 & v32) == (~v16 & v32 | 0xC00020130082C0C)
其中v12需要使用动调来获得内容
1 v8[v10] = *((byte_0 *)v11 + v10) ^ *((byte_0 *)v12 + v9 % 27 );
同时我们可以根据这条代码来推测flag内容的长度应该为27
Z3解方程组
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 from z3 import *from Crypto.Util.number import *v13, v14, v15, v16 = BitVecs("v13 v14 v15 v16" , 64 ) s = Solver() s.add(v14 & ~v16 == 0x11204161012 ) s.add((v15 & v16)|(v14 & ~v16)|(v14 & ~v15)|(v16 & ~v15) == 0x3E3A4717373E7F1F ) s.add(((v14 & ~v15) & v16)| (v14 & (v15 & v16 | v15 & ~v16 | ~(v15 | v16))) == 0x8020717153E3013 ) s.add(0x3E3A4717373E7F1F ^ v13 == 0x3E3A4717050F791F ) s.add(((v14 & ~v16) | (v15 & v16) | (v15 & v14)) == (~v16 & v14 | 0xC00020130082C0C )) s.check() m = s.model() """ print(m) [v13 = 842073600, v16 = 4483973367147818765, v14 = 577031497978884115, v15 = 864693882343402508] """ cipher = b"" v12 = "i_will_check_is_debug_or_no" flag = "flag{" m = [4483973367147818765 , 864693882343402508 , 577031497978884115 , 842073600 ] for _ in m: cipher += long_tobyte_0s(_) print(cipher) for _ in range (len (cipher)): flag += chr (cipher[_] ^ ord (v12[_])) flag += "}" print(flag)
貌似是因为多解的原因
导致最后得到的flag不正确
所以比赛时给出了flag的第二部分"e!P0or_a"
flag{We1l_D0ne!P0or_algebra_am_i}
[WUSTCTF2020]level4
链接: https://buuoj.cn/challenges#[WUSTCTF2020]level4
ELF 64
IDA中 main 函数反编译结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int __cdecl main (int argc, const char **argv, const char **envp) { puts ("Practice my Data Structure code....." ); puts ("Typing....Struct.....char....*left....*right............emmmmm...OK!" ); init("Typing....Struct.....char....*left....*right............emmmmm...OK!" , argv); puts ("Traversal!" ); printf ("Traversal type 1:" ); type1(&unk_601290); printf ("\nTraversal type 2:" ); type2(&unk_601290); printf ("\nTraversal type 3:" ); puts (" //type3(&x[22]); No way!" ); puts (&byte_400A37); return 0 ; }
二叉树中序遍历
1 2 3 4 5 6 7 8 9 10 11 12 __int64 __fastcall type1 (char *a1) { __int64 result; if ( a1 ) { type1(*((_QWORD *)a1 + 1 )); putchar (*a1); result = type1(*((_QWORD *)a1 + 2 )); } return result; }
二叉树后序遍历
1 2 3 4 5 6 7 8 9 10 11 12 int __fastcall type2 (char *a1) { int result; if ( a1 ) { type2(*((_QWORD *)a1 + 1 )); type2(*((_QWORD *)a1 + 2 )); result = putchar (*a1); } return result; }
结合输出结果来看
先序遍历的输出结果应该就是flag
这里不会写转换脚本
直接在IDA里面动调 看unk_601290
的数据结构了
数据+8Byte
是左子节点的地址
数据+16Byte
是右子节点的地址
flag{This_IS_A_7reE}
[GUET-CTF2019]number_game
链接: https://buuoj.cn/challenges#[GUET-CTF2019]number_game
ELF 64
IDA中 main 函数反编译结果如下
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 unsigned __int64 __fastcall main (int a1, char **a2, char **a3) { _QWORD *v4; __int64 v5; __int16 v6; __int64 v7; __int16 v8; char v9; unsigned __int64 v10; v10 = __readfsqword(0x28 u); v5 = 0LL ; v6 = 0 ; v7 = 0LL ; v8 = 0 ; v9 = 0 ; __isoc99_scanf("%s" , &v5); if ( (unsigned int )sub_4006D6((const char *)&v5) ) { v4 = sub_400758((__int64)&v5, 0 , 0xA u); sub_400807((__int64)v4, (__int64)&v7); v9 = 0 ; sub_400881((char *)&v7); if ( (unsigned int )sub_400917() ) { puts ("TQL!" ); printf ("flag{" ); printf ("%s" , (const char *)&v5); puts ("}" ); } else { puts ("your are cxk!!" ); } } return __readfsqword(0x28 u) ^ v10; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 __int64 __fastcall sub_4006D6 (const char *a1) { __int64 result; int i; if ( strlen (a1) == 10 ) { for ( i = 0 ; i <= 9 ; ++i ) { if ( a1[i] > 52 || a1[i] <= 47 ) goto LABEL_2; } result = 1LL ; } else { LABEL_2: puts ("Wrong!" ); result = 0LL ; } return result; }
函数sub_4006D6
要求输入的字符串长度为10且字符集为"01234"
通过控制输入, 动态调试sub_400807
得到重新排序之后的字符串
因为实在懒得看代码了, 曲线救国
1 2 3 4 5 0123401234 2331440021 0011223344 3140420213
根据两组输入输出则可以得出解密序列为
1 seq = [6 , 3 , 8 , 1 , 5 , 7 , 9 , 0 , 2 , 4 ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 __int64 __fastcall sub_400881 (char *a1) { __int64 result; byte_601062 = *a1; byte_601067 = a1[1 ]; byte_601069 = a1[2 ]; byte_60106B = a1[3 ]; byte_60106E = a1[4 ]; byte_60106F = a1[5 ]; byte_601071 = a1[6 ]; byte_601072 = a1[7 ]; byte_601076 = a1[8 ]; result = (unsigned __int8)a1[9 ]; byte_601077 = a1[9 ]; return result; }
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 .data:0000000000601060 unk_601060 db 31h ; 1 .data:0000000000601061 db 34h ; 4 .data:0000000000601062 byte_601062 db 23h ; DATA XREF: sub_400881+F↑w .data:0000000000601063 db 32h ; 2 .data:0000000000601064 db 33h ; 3 .data:0000000000601065 db 33h ; 3 .data:0000000000601066 db 30h ; 0 .data:0000000000601067 byte_601067 db 23h ; DATA XREF: sub_400881+1D↑w .data:0000000000601068 db 31h ; 1 .data:0000000000601069 byte_601069 db 23h ; DATA XREF: sub_400881+2B↑w .data:000000000060106A db 30h ; 0 .data:000000000060106B byte_60106B db 23h ; DATA XREF: sub_400881+39↑w .data:000000000060106C db 32h ; 2 .data:000000000060106D db 33h ; 3 .data:000000000060106E byte_60106E db 23h ; DATA XREF: sub_400881+47↑w .data:000000000060106F byte_60106F db 23h ; DATA XREF: sub_400881+55↑w .data:0000000000601070 db 33h ; 3 .data:0000000000601071 byte_601071 db 23h ; DATA XREF: sub_400881+63↑w .data:0000000000601072 byte_601072 db 23h ; DATA XREF: sub_400881+71↑w .data:0000000000601073 db 30h ; 0 .data:0000000000601074 db 34h ; 4 .data:0000000000601075 db 32h ; 2 .data:0000000000601076 byte_601076 db 23h ; DATA XREF: sub_400881+7F↑w .data:0000000000601077 byte_601077 db 23h ; DATA XREF: sub_400881+8D↑w .data:0000000000601078 db 31h ; 1
可以看到通过v7赋值的地址都是恰好是空值的位置, 则这里是在填空
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 __int64 sub_400917 () { unsigned int v1; int i; int j; int k; v1 = 1 ; for ( i = 0 ; i <= 4 ; ++i ) { for ( j = 0 ; j <= 4 ; ++j ) { for ( k = j + 1 ; k <= 4 ; ++k ) { if ( *((byte_0 *)&unk_601060 + 5 * i + j) == *((byte_0 *)&unk_601060 + 5 * i + k) ) v1 = 0 ; if ( *((byte_0 *)&unk_601060 + 5 * j + i) == *((byte_0 *)&unk_601060 + 5 * k + i) ) v1 = 0 ; } } } return v1; }
看到这里就差不多明白了
横纵不能出现相同的数字, 那么这个程序就是个数独游戏了
且数独的布局如下
1 2 3 4 5 6 sudoku = [ 1 , 4 , X, 2 , 3 ,3 , 0 , X, 1 , X,0 , X, 2 , 3 , X,X, 3 , X, X, 0 , 4 , 2 , X, X, 1 ]
解出后得出序列为
1 cipher = [0 , 4 , 2 , 1 , 4 , 2 , 1 , 4 , 3 , 0 ]
1 2 3 4 5 6 7 cipher = [0 , 4 , 2 , 1 , 4 , 2 , 1 , 4 , 3 , 0 ] seq = [6 , 3 , 8 , 1 , 5 , 7 , 9 , 0 , 2 , 4 ] flag = [0 for _ in range (10 )] for _ in range (len (cipher)): flag[_] = str (cipher[seq[_]]) print("flag{" +"" .join(flag)+"}" )
flag{1134240024}
[GWCTF 2019]re3
链接: https://buuoj.cn/challenges#[GWCTF 2019]re3
ELF 64
IDA中 main 函数反编译结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void __fastcall __noreturn main (int a1, char **a2, char **a3) { int i; char s[40 ]; unsigned __int64 v5; v5 = __readfsqword(0x28 u); __isoc99_scanf("%39s" , s); if ( (unsigned int )strlen (s) != 32 ) { puts ("Wrong!" ); exit (0 ); } mprotect(&dword_400000, 0xF000 uLL, 7 ); for ( i = 0 ; i <= 223 ; ++i ) *((byte_0 *)sub_402219 + i) ^= 0x99 u; sub_40207B(&unk_603170); sub_402219(s); }
1 2 for ( i = 0 ; i <= 223 ; ++i ) *((byte_0 *)sub_402219 + i) ^= 0x99 u;
注意看着一段代码
这里在对函数sub_402219
的内容进行异或运算
而sub_402219
的内容如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 .text:0000000000402219 sub_402219 proc near ; CODE XREF: main+CA↑p .text:0000000000402219 ; DATA XREF: main:loc_40217C↑o .text:0000000000402219 ; __unwind { .text:0000000000402219 int 3 ; Trap to Debugger .text:0000000000402219 sub_402219 endp .text:0000000000402219 .text:0000000000402219 ; --------------------------------------------------------------------------- .text:000000000040221A dw 10D1h, 0D17Ch, 7518h .text:0000000000402220 dq 812410D199999969h, 0BC9D12D1FD666666h, 61DC10D1999999B1h .text:0000000000402220 dq 6666A91C14D159A8h, 10D199F9A8E92766h, 12D1666671BA715Eh .text:0000000000402220 dq 1C14D1666666810Ch, 0D14F10D1666666A9h, 0D166666E9E715E10h .text:0000000000402220 dq 14D1666666811C12h, 6666A91C14D189C9h, 715E10D14F10D166h .text:0000000000402220 dq 66B11C5E66666F73h, 1C5E999999986666h, 99999999666666B5h .text:0000000000402220 dq 666666B51C12A372h, 66811C12D149FAD1h, 892F964998D16666h .text:0000000000402220 dq 1D1666666B51C12h, 0A199F9A939192F96h, 6666B11C5E93ED5Bh .text:0000000000402220 dq 0B51C1A9999999966h, 66B5241A98666666h, 0B11C1224E7866666h .text:0000000000402220 dq 0FD61D412D1666666h, 999999B1BC95AAD1h, 5066667AC0719CEDh .text:0000000000402220 dq 801F0F5Ah .text:0000000000402300
那么可以看到函数中内容无法被IDA解析, 所以被认为是数据
所以我们需要使用IDC脚本来对这部分数据进行还原
1 2 3 4 5 6 7 8 9 10 #include <idc.idc> static main () { auto addr = 0x402219 ; auto i; for (i = 0 ; i <= 223 ; ++i) { PatchByte(addr+i, Byte(addr+i)^0x99 ); } }
先全部转为数据, 再强制转为代码, 最后再创建函数, 且main
函数里nop
掉那段循环异或的代码
同时main函数需要包含loc_402205
, loc_40220F
这两段代码, 操作同上
还原之后的函数main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void __fastcall __noreturn main (int a1, char **a2, char **a3) { char s[40 ]; unsigned __int64 v4; v4 = __readfsqword(0x28 u); __isoc99_scanf("%39s" , s); if ( (unsigned int )strlen (s) != 32 ) { puts ("Wrong!" ); exit (0 ); } mprotect(&dword_400000, 0xF000 uLL, 7 ); sub_40207B((__int64)&unk_603170); if ( (unsigned int )sub_402219((__int64)s) ) puts ("Correct!" ); else puts ("Wrong!" ); exit (0 ); }
先分析函数sub_40207B
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 unsigned __int64 __fastcall sub_40207B (__int64 a1) { char v2[16 ]; __int64 v3; __int64 v4; __int64 v5; unsigned __int64 v6; v6 = __readfsqword(0x28 u); sub_401CF9(&BASE64_table_603120, 0x40 uLL, (__int64)v2); sub_401CF9(&CRC32_table_603100, 0x14 uLL, (__int64)&v3); sub_401CF9(&Prime_Constants_char_6030C0, 0x35 uLL, (__int64)&v4); sub_401CF9(MD5_Constants_4025C0, 0x100 uLL, (__int64)&v5); sub_401CF9(v2, 0x40 uLL, a1); return __readfsqword(0x28 u) ^ v6; }
BASE64_table_603120
经过函数sub_401CF9
的运行之后结果保存于v2
v2
经过函数sub_401CF9
的运行之后结果保存于a1
, 即unk_603170
代码过于复杂, 动调得到unk_603170
的内容
1 2 3 _603170 = [ 0xCB , 0x8D , 0x49 , 0x35 , 0x21 , 0xB4 , 0x7A , 0x4C , 0xC1 , 0xAE , 0x7E , 0x62 , 0x22 , 0x92 , 0x66 , 0xCE ]
分析sub_402219
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 __int64 __fastcall sub_402219 (__int64 a1) { unsigned int v2; int i; char v4[200 ]; unsigned __int64 v5; v5 = __readfsqword(0x28 u); sub_400A71((__int64)v4, (__int64)&unk_603170); sub_40196E((__int64)v4, a1); sub_40196E((__int64)v4, a1 + 16 ); v2 = 1 ; for ( i = 0 ; i <= 31 ; ++i ) { if ( *(byte_0 *)(i + a1) != qword_6030A0[i] ) v2 = 0 ; } return v2; }
sub_400A71
处理unk_603170
继续跟进看到一个名为RijnDael_AES_LONG_4023A0
数据段, 那么基本可以判断这是一个AES加密程序了
sub_40196E
也就是用于加密用户所输入的字符串, 且加密模式为ECB(32字节的数据分两次单独加密), 最后的密文存于qword_6030A0
1 2 3 4 5 _6030A0 = [ 0xBC , 0x0A , 0xAD , 0xC0 , 0x14 , 0x7C , 0x5E , 0xCC , 0xE0 , 0xB1 , 0x40 , 0xBC , 0x9C , 0x51 , 0xD5 , 0x2B , 0x46 , 0xB2 , 0xB9 , 0x43 , 0x4D , 0xE5 , 0x32 , 0x4B , 0xAD , 0x7F , 0xB4 , 0xB3 , 0x9C , 0xDB , 0x4B , 0x5B ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from Crypto.Cipher import AES_603170 = [ 0xCB , 0x8D , 0x49 , 0x35 , 0x21 , 0xB4 , 0x7A , 0x4C , 0xC1 , 0xAE , 0x7E , 0x62 , 0x22 , 0x92 , 0x66 , 0xCE ]_6030A0 = [ 0xBC , 0x0A , 0xAD , 0xC0 , 0x14 , 0x7C , 0x5E , 0xCC , 0xE0 , 0xB1 , 0x40 , 0xBC , 0x9C , 0x51 , 0xD5 , 0x2B , 0x46 , 0xB2 , 0xB9 , 0x43 , 0x4D , 0xE5 , 0x32 , 0x4B , 0xAD , 0x7F , 0xB4 , 0xB3 , 0x9C , 0xDB , 0x4B , 0x5B ]cipher = "" for _ in _6030A0: cipher += chr (_) key = "" for _ in _603170: key += chr (_) aes = AES.new(key.encode("ISO-8859-1" ), AES.MODE_ECB) print(aes.decrypt(cipher.encode("ISO-8859-1" )).decode())
flag{924a9ab2163d390410d0a1f670}
[2019红帽杯]xx
链接: https://buuoj.cn/challenges#[2019红帽杯]xx
EXE 64
IDA中 main 函数反编译结果如下
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 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 int __cdecl main (int argc, const char **argv, const char **envp) { __int64 v3; __int64 v4; __int128 *v5; __int64 v6; __int128 *v7; int v8; __int128 *v9; char v10; int v11; __int64 v12; unsigned __int64 v13; __int64 v14; unsigned __int64 v15; unsigned __int64 i; __int64 v17; size_t v18; _BYTE *v19; _BYTE *v20; int v21; char *v22; __int64 v23; char v24; __int64 v25; __int64 v26; __int64 v27; size_t Size; __int128 v30; int v31; int v32; int Code[4 ]; int v34; *(_OWORD *)Code = 0 i64; v34 = 0 ; sub_1400018C0(std ::cin , argv, Code); v3 = -1 i64; v4 = -1 i64; do ++v4; while ( *((_BYTE *)Code + v4) ); if ( v4 != 19 ) { sub_140001620(std ::cout , "error\n" ); _exit((int )Code); } v5 = (__int128 *)operator new (5u i64); v6 = *(_QWORD *)&::Code; v7 = v5; v8 = 0 ; v9 = v5; do { v10 = *((_BYTE *)v9 + (char *)Code - (char *)v5); v11 = 0 ; *(_BYTE *)v9 = v10; v12 = 0 i64; v13 = -1 i64; do ++v13; while ( *(_BYTE *)(v6 + v13) ); if ( v13 ) { do { if ( v10 == *(_BYTE *)(v6 + v12) ) break ; ++v11; ++v12; } while ( v11 < v13 ); } v14 = -1 i64; do ++v14; while ( *(_BYTE *)(v6 + v14) ); if ( v11 == v14 ) _exit(v6); v9 = (__int128 *)((char *)v9 + 1 ); } while ( (char *)v9 - (char *)v5 < 4 ); *((_BYTE *)v5 + 4 ) = 0 ; do ++v3; while ( *((_BYTE *)Code + v3) ); v15 = 0 i64; v30 = *v7; while ( *((_BYTE *)&v30 + v15) ) { if ( !*((_BYTE *)&v30 + v15 + 1 ) ) { ++v15; break ; } if ( !*((_BYTE *)&v30 + v15 + 2 ) ) { v15 += 2 i64; break ; } if ( !*((_BYTE *)&v30 + v15 + 3 ) ) { v15 += 3 i64; break ; } v15 += 4 i64; if ( v15 >= 0x10 ) break ; } for ( i = v15 + 1 ; i < 0x10 ; ++i ) *((_BYTE *)&v30 + i) = 0 ; v17 = (__int64)sub_140001AB0((__int64)Code, v3, (unsigned __int8 *)&v30, &Size); v18 = Size; v19 = (_BYTE *)v17; v20 = operator new (Size); v21 = 1 ; *v20 = v19[2 ]; v22 = v20 + 1 ; v20[1 ] = *v19; v20[2 ] = v19[3 ]; v20[3 ] = v19[1 ]; v20[4 ] = v19[6 ]; v20[5 ] = v19[4 ]; v20[6 ] = v19[7 ]; v20[7 ] = v19[5 ]; v20[8 ] = v19[10 ]; v20[9 ] = v19[8 ]; v20[10 ] = v19[11 ]; v20[11 ] = v19[9 ]; v20[12 ] = v19[14 ]; v20[13 ] = v19[12 ]; v20[14 ] = v19[15 ]; v20[15 ] = v19[13 ]; v20[16 ] = v19[18 ]; v20[17 ] = v19[16 ]; v20[18 ] = v19[19 ]; v20[19 ] = v19[17 ]; v20[20 ] = v19[22 ]; v20[21 ] = v19[20 ]; v20[22 ] = v19[23 ]; for ( v20[23 ] = v19[21 ]; v21 < v18; ++v22 ) { v23 = 0 i64; if ( v21 / 3 > 0 ) { v24 = *v22; do { v24 ^= v20[v23++]; *v22 = v24; } while ( v23 < v21 / 3 ); } ++v21; } *(_QWORD *)&v30 = 0xC0953A7C6B40BCCE ui64; v25 = v20 - (_BYTE *)&v30; *((_QWORD *)&v30 + 1 ) = 0x3502F79120209BEF i64; v26 = 0 i64; v31 = 0xC8021823 ; v32 = 0xFA5656E7 ; do { if ( *((_BYTE *)&v30 + v26) != *((_BYTE *)&v30 + v26 + v25) ) _exit(v8 * v8); ++v8; ++v26; } while ( v26 < 24 ); v27 = sub_140001620(std ::cout , "You win!" ); std ::ostream::operator <<(v27, sub_1400017F0); return 0 ; }
IDA中使用插件FindCrypt
可以看到程序使用了TEA加密
结合题目可以猜出是XXTEA加密
这题基本是看着别人的WP做出来的
1 .text:0000000140001C4B global TEA_DELTA_140001C4B $c0 b'\xb9y7\x9e'
从上往下分析
用户输入字符串, 长度为19
前四个字符在字符集"qwertyuiopasdfghjklzxcvbnm1234567890"中
取前四字节为密钥
加密结果乱序
迭代异或
校验加密结果
迭代异或的方式如下
1 2 3 4 5 6 7 cipher[3] = cipher[3] ^ cipher[0] cipher[4] = cipher[4] ^ cipher[0] cipher[5] = cipher[5] ^ cipher[0] cipher[6] = cipher[6] ^ cipher[0] ^ cipher[1] cipher[7] = cipher[7] ^ cipher[0] ^ cipher[1] cipher[8] = cipher[8] ^ cipher[0] ^ cipher[1] ...
密文如下(大端赋值, 之后需要转为小端的字符串)
1 2 3 4 *(_QWORD *)&v30 = 0xC0953A7C6B40BCCEui64; *((_QWORD *)&v30 + 1) = 0x3502F79120209BEFi64; v31 = 0xC8021823; v32 = 0xFA5656E7;
且这三个变量的内存分布如下
1 2 3 __int128 v30; // [rsp+28h] [rbp-40h] BYREF int v31; // [rsp+38h] [rbp-30h] int v32; // [rsp+3Ch] [rbp-2Ch]
所以四个数据的整体才是密文部分
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 import binasciiimport xxteacipher = [ 0xC0953A7C6B40BCCE , 0x3502F79120209BEF , 0xC8021823 , 0xFA5656E7 ]_byte = b"" for _ in cipher: _byte += binascii.unhexlify(hex (_)[2 :].encode("ISO-8859-1" ))[::-1 ] __byte = [_ for _ in _byte] for _ in range (len (__byte)-1 , -1 , -1 ): for __ in range (_//3 ): __byte[_] = __byte[_] ^ __byte[__] ___byte = "" for _ in __byte: ___byte += chr (_) ___byte = ___byte.encode("ISO-8859-1" ) seq_enc = [2 , 0 , 3 , 1 , 6 , 4 , 7 , 5 , 10 , 8 , 11 , 9 , 14 , 12 , 15 , 13 , 18 , 16 , 19 , 17 , 22 , 20 , 23 , 21 ] seq_dec = [-1 for _ in seq_enc] for _ in range (len (seq_enc)): seq_dec[_] = seq_enc.index(_) ____byte = b"" for _ in range (len (___byte)): ____byte += chr (___byte[seq_dec[_]]).encode("ISO-8859-1" ) key = "flag" flag = xxtea.decrypt(____byte, key).decode() print(flag)
flag{CXX_and_++tea}
findKey
链接: https://buuoj.cn/challenges#findKey
EXE 32
一开始没找到主函数
1 .rdata:00423028 aFlag db 'flag{}',0 ; DATA XREF: .text:00401A3A↑o
找到这样的一个字符串
搜索文本之后找到位置loc_401A37
但是这里并没有被定义成函数
粗略看下有一段疑似插入了花指令的代码
1 2 3 4 5 6 7 8 9 10 11 12 .text:0040191D loc_40191D: ; CODE XREF: .text:0040193D↓j .text:0040191D push offset byte_428C54 .text:00401922 call _strlen .text:00401927 add esp, 4 .text:0040192A push eax .text:0040192B push offset byte_428C54 .text:00401930 call sub_40101E .text:00401935 add esp, 0Ch .text:00401938 nop .text:00401939 jz short loc_401948 .text:0040193B jnz short loc_401948 .text:0040193D jmp short near ptr loc_40191D+2
根据我做的那些逆向题目的经验
按理说jz的前一条指令是能够对ZF产生影响的, 例如cmp, test
更奇怪的是后面跟了一条jnz和一条jmp
把这三条nop掉就可以定义为函数了
反编译后了下伪代码, 感觉差不多就是main 函数了
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 107 108 109 110 111 112 113 114 115 116 117 int __cdecl main (int argc, const char **argv, const char **envp) { int v4; size_t v5; DWORD v6; HWND v7; int v8; int v9; const CHAR *v10; const CHAR *v11; UINT v12; CHAR v13[256 ]; char v14[7 ]; __int16 v15; char v16; char Str[33 ]; char v18[220 ]; __int16 v19; char v20; CHAR v21[256 ]; CHAR String[4 ]; int v23; __int16 v24; CHAR Text[32 ]; struct tagRECT Rect ; CHAR Buffer[100 ]; HDC hdc; struct tagPAINTSTRUCT Paint ; int v30; int v31; LPARAM lParam; LoadStringA(hInstance, 0x6A u, Buffer, 0x64 ); if ( (unsigned int )argv > 0x111 ) { if ( argv == (const char **)517 ) { if ( strlen ((const char *)String1) > 6 ) ExitProcess(0 ); if ( strlen ((const char *)String1) ) { memset (v21, 0 , sizeof (v21)); v5 = strlen ((const char *)String1); memcpy (v21, String1, v5); v6 = strlen ((const char *)String1); v7 = (HWND)sub_40101E(String1, v6, (LPSTR)String1); MessageBoxA(v7, v10, v11, v12); strcpy (Str, "0kk`d1a`55k222k2a776jbfgd`06cjjb" ); memset (v18, 0 , sizeof (v18)); v19 = 0 ; v20 = 0 ; strcpy (v14, "SS" ); *(_DWORD *)&v14[3 ] = 0 ; v15 = 0 ; v16 = 0 ; v8 = strlen (Str); sub_401005(v14, (int )Str, v8); if ( _strcmpi((const char *)String1, Str) ) { SetWindowTextA((HWND)argc, "flag{}" ); MessageBoxA((HWND)argc, "Are you kidding me?" , "^_^" , 0 ); ExitProcess(0 ); } memcpy (v13, &unk_423030, 0x32 u); v9 = strlen (v13); sub_401005(v21, (int )v13, v9); MessageBoxA((HWND)argc, v13, 0 , 0x32 u); } ++dword_428D54; } else { if ( argv != (const char **)520 ) return DefWindowProcA((HWND)argc, (UINT)argv, (WPARAM)envp, lParam); if ( dword_428D54 == 16 ) { strcpy (String, "ctf" ); v23 = 0 ; v24 = 0 ; SetWindowTextA((HWND)argc, String); strcpy (Text, "Are you kidding me?" ); MessageBoxA((HWND)argc, Text, Buffer, 0 ); } ++dword_428D54; } } else if ( argv == (const char **)273 ) { v31 = (unsigned __int16)envp; v30 = HIWORD(envp); if ( (unsigned __int16)envp == 104 ) { DialogBoxParamA(hInstance, (LPCSTR)0x67 , (HWND)argc, (DLGPROC)DialogFunc, 0 ); } else { if ( (unsigned __int16)envp != 105 ) return DefWindowProcA((HWND)argc, (UINT)argv, (WPARAM)envp, lParam); DestroyWindow((HWND)argc); } } else if ( argv == (const char **)2 ) { PostQuitMessage(0 ); } else { if ( argv != (const char **)15 ) return DefWindowProcA((HWND)argc, (UINT)argv, (WPARAM)envp, lParam); hdc = BeginPaint((HWND)argc, &Paint); GetClientRect((HWND)argc, &Rect); v4 = strlen (Buffer); DrawTextA(hdc, Buffer, v4, &Rect, 1u ); EndPaint((HWND)argc, &Paint); } return 0 ; }
先跟进sub_40101E
1 if ( CryptCreateHash(phProv, 0x8003 u, 0 , 0 , &phHash) )
看到这句就知道是md5加密了
再跟进sub_401005
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 unsigned int __cdecl sub_401590 (LPCSTR lpString, int a2, int a3) { unsigned int result; unsigned int i; unsigned int v5; v5 = lstrlenA(lpString); for ( i = 0 ; ; ++i ) { result = i; if ( i >= a3 ) break ; *(_BYTE *)(i + a2) ^= lpString[i % v5]; } return result; }
简单的异或
1 sub_401005(v14, (int )Str, v8);
v14 = "SS"
Str = "0kk`d1a`55k222k2a776jbfgd`06cjjb"
v8 = 32
也就是说Str
在异或之后与String1
的md5结果比较
1 2 3 4 5 6 xor = "0kk`d1a`55k222k2a776jbfgd`06cjjb" dexor = "" for _ in xor: dexor += chr (ord (_) ^ ord ("S" )) print(dexor)
得到c8837b23ff8aaa8a2dde915473ce0991
md5解密结果为123321
再跟进函数DialogFunc
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 int __stdcall sub_401B20 (HWND hDlg, int a2, int a3, int a4) { switch ( a2 ) { case 0x110 : return 1 ; case 0x111 : if ( (unsigned __int16)a3 != 1 && (unsigned __int16)a3 != 2 ) goto LABEL_6; EndDialog(hDlg, (unsigned __int16)a3); return 1 ; case 0x202 : LABEL_6: String1[dword_428C50++] = 49 ; return 0 ; case 0x205 : String1[dword_428C50++] = 51 ; return 0 ; case 0x208 : if ( dword_428D58 < 3 ) ++dword_428D5C; String1[dword_428C50++] = 50 ; return 0 ; default : return 0 ; } }
这里的a2
一开始没太懂
后来看了别人的wp才知道貌似是Windows Message
1 2 3 #define WM_LBUTTONUP 0x0202 #define WM_MBUTTONUP 0x0208 #define WM_RBUTTONUP 0x0205
https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-lbuttonup
https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-mbuttonup
https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-rbuttonup
那么也就是按鼠标左键, 鼠标中键, 鼠标右键, 鼠标右键, 鼠标中键, 鼠标左键
值得一提的是, 调用函数DialogFunc
代码
1 DialogBoxParamA(hInstance, (LPCSTR)103 , (HWND)argc, (DLGPROC)DialogFunc, 0 );
在Resource Hacker
中可以看到编号103
对应着About
对话框
也就是说需要在About对话框中按上述顺序点击鼠标
不是做这道题我都不会意识到我的鼠标中键已经报废了, 最后动调改String1
的值
flag{n0_Zu0_n0_die}
[FlareOn5]Minesweeper Championship Registration
链接: https://buuoj.cn/challenges#[FlareOn5]Minesweeper Championship Registration
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import javax.swing.JOptionPane;public class InviteValidator { public static void main (String[] args) { String response = JOptionPane.showInputDialog(null , "Enter your invitation code:" , "Minesweeper Championship 2018" , 3 ); if (response.equals("GoldenTicket2018@flare-on.com" )) { JOptionPane.showMessageDialog(null , "Welcome to the Minesweeper Championship 2018!\nPlease enter the following code to the ctfd.flare-on.com website to compete:\n\n" + response, "Success!" , -1 ); } else { JOptionPane.showMessageDialog(null , "Incorrect invitation code. Please try again next year." , "Failure" , 0 ); } } }
flag写脸上了
flag{GoldenTicket2018@flare-on.com}
[网鼎杯 2020 青龙组]jocker
链接: https://buuoj.cn/challenges#[网鼎杯 2020 青龙组]jocker
EXE 32
主函数看到对函数进行异或就知道不对劲了
1 2 3 4 5 6 7 8 9 10 #include <idc.idc> static main () { auto addr = 0x00401500 ; auto i; for (i = 0 ; i <= 186 ; ++i) { PatchByte(addr+i, Byte(addr+i)^0x41 ); } }
具体步骤参照这里
http://yoloyolo.top/2021/05/31/Reverse-3/#gwctf-2019re3
函数finally
的前一部分也有被异或过, 需要重新定义为函数
函数main
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 int __cdecl main (int argc, const char **argv, const char **envp) { char Str[50 ]; char Destination[80 ]; DWORD flOldProtect; size_t v7; int v8; __main(); puts ("please input you flag:" ); if ( !VirtualProtect(encrypt, 0xC8 u, 4u , &flOldProtect) ) exit (1 ); scanf ("%40s" , Str); v7 = strlen (Str); if ( v7 != 24 ) { puts ("Wrong!" ); exit (0 ); } strcpy (Destination, Str); wrong(Str); omg(Str); v8 = 0 ; if ( encrypt(Destination) ) finally(Destination); return 0 ; }
跟进函数wrong
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 char *__cdecl wrong (char *a1) { char *result; int i; for ( i = 0 ; i <= 23 ; ++i ) { result = &a1[i]; if ( (i & 1 ) != 0 ) a1[i] -= i; else a1[i] ^= i; } return result; }
简单的加密
继续看函数omg
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int __cdecl omg (char *a1) { int result; int v2[24 ]; int i; int v4; v4 = 1 ; qmemcpy(v2, dword_4030C0, sizeof (v2)); for ( i = 0 ; i <= 23 ; ++i ) { if ( a1[i] != v2[i] ) v4 = 0 ; } if ( v4 == 1 ) result = puts ("hahahaha_do_you_find_me?" ); else result = puts ("wrong ~~ But seems a little program" ); return result; }
1 2 3 4 5 6 7 8 9 10 11 12 13 _4030C0 = [ 0x66 , 0x6B , 0x63 , 0x64 , 0x7F , 0x61 , 0x67 , 0x64 , 0x3B , 0x56 , 0x6B , 0x61 , 0x7B , 0x26 , 0x3B , 0x50 , 0x63 , 0x5F , 0x4D , 0x5A , 0x71 , 0x0C , 0x37 , 0x66 ]flag = "" for _ in range (len (_4030C0)): if _ % 2 == 0 : flag += chr (_4030C0[_] ^ _) else : flag += chr (_4030C0[_] + _) print(flag)
得到flag{fak3_alw35_sp_me!!}
然而是个假flag
继续看函数encrypt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int __cdecl encrypt (char *a1) { int v2[19 ]; int v3; int i; v3 = 1 ; qmemcpy(v2, dword_403040, sizeof (v2)); for ( i = 0 ; i <= 18 ; ++i ) { if ( (char )(a1[i] ^ Buffer[i]) != v2[i] ) { puts ("wrong ~" ); v3 = 0 ; exit (0 ); } } puts ("come here" ); return v3; }
1 2 3 4 5 6 7 8 9 10 flag = "" _buffer = "hahahaha_do_you_find_me?" _403040 = [ 0x0E , 0x0D , 0x09 , 0x06 , 0x13 , 0x05 , 0x58 , 0x56 , 0x3E , 0x06 , 0x0C , 0x3C , 0x1F , 0x57 , 0x14 , 0x6B , 0x57 , 0x59 , 0x0D ]for _ in range (len (_403040)): flag += chr (_403040[_] ^ ord (_buffer[_])) print(flag)
得到flag{d07abccf8a410c
缺了五个字符
看函数finally
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int __cdecl finally (char *a1) { unsigned int v1; int result; char v3[9 ]; int v4; strcpy (v3, "%tp&:" ); v1 = time(0 ); srand(v1); v4 = rand() % 100 ; if ( (v3[*(_DWORD *)&v3[5 ]] != a1[*(_DWORD *)&v3[5 ]]) == v4 ) result = puts ("Really??? Did you find it?OMG!!!" ); else result = puts ("I hide the last part, you will not succeed!!!" ); return result; }
看着有点发蒙, 感觉也没有在做什么正经的运算
v3的赋值算是给了一点点提示吧
奇奇怪怪的脑洞
1 2 hex (ord (":" )^ord ('}' ))'0x47'
1 2 3 4 5 6 7 key = 0x47 flag = "flag{d07abccf8a410c" v3 = "%tp&:" for _ in v3: flag += chr (ord (_) ^ key) print(flag)
flag{d07abccf8a410cb37a}
equation
链接: https://buuoj.cn/challenges#equation
Javascript
看着这道题莫名地会想到"高明的黑客"
没啥好说的, 直接放代码
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 import reimport execjsraw = open ("equation.html" , "r" ).read() data = re.findall("if\((.*)\)" , raw)[0 ] data_list = data.split("&&" ) exp_sum = [] res_sum = [] for cnt in range (len (data_list)): test = data_list[cnt].split("==" )[0 ] equ = data_list[cnt].split("==" )[1 ] res = [int (execjs.eval (equ))] res_sum.append(res) symbol = [] if test.startswith("-" ): symbol.append("-" ) else : symbol.append("+" ) test = re.sub("\]\+l\[" , " + " , test) test = re.sub("\]\-l\[" , " - " , test) test = test.replace("l[" , "" )[:-1 ] symbol_temp = re.findall(" ([\+\-]) " , test) for _ in symbol_temp: symbol.append(_) test = re.sub(" [\+\-] " , " " ,test) jscode = test.split(" " ) exp = [0 for _ in range (42 )] for _ in range (len (jscode)): seq = int (execjs.eval (jscode[_])) if symbol[_] == "+" : exp[seq] = 1 elif symbol[_] == "-" : exp[seq] = -1 exp_sum.append(exp) b = Matrix(res_sum) x = Matrix(exp_sum) out = x.solve_right(b) flag = "" for _ in out: flag += chr (_[0 ]) print(flag)
execjs.eval()
运行的速度好慢, 也懒得找能够代替这个函数的函数了
100次17s, 1638个字符串, 5分钟左右
[FlareOn5]Ultimate Minesweeper
链接: https://buuoj.cn/challenges#[FlareOn5]Ultimate Minesweeper
.NET
先放IDA里看看
看到字符串"Congratulations!"
查看引用这个字符串的函数, 然后用dnspy看这些函数
MainForm::SquareRevealedCallback
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 private void SquareRevealedCallback (uint column, uint row ) { if (this .MineField.BombRevealed) { this .stopwatch.Stop(); Application.DoEvents(); Thread.Sleep(1000 ); new FailurePopup().ShowDialog(); Application.Exit(); } this .RevealedCells.Add(row * MainForm.VALLOC_NODE_LIMIT + column); if (this .MineField.TotalUnrevealedEmptySquares == 0 ) { this .stopwatch.Stop(); Application.DoEvents(); Thread.Sleep(1000 ); new SuccessPopup(this .GetKey(this .RevealedCells)).ShowDialog(); Application.Exit(); } }
把判断失败的代码删掉再保存程序
找出三个点就能够进入过关的逻辑
但是flag的内容应该是与点击的格子相关
而且点的位置不是随机生成的
记下位置再玩一遍就能得到flag
flag{Ch3aters_Alw4ys_W1n@flare-on.com}
[2019红帽杯]childRE
链接: https://buuoj.cn/challenges#[2019红帽杯]childRE
EXE 64
IDA中 main 函数反编译结果如下
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 int __cdecl main (int argc, const char **argv, const char **envp) { __int64 v3; __int64 v4; const CHAR *v5; __int64 v6; int v7; const CHAR *v8; __int64 v9; __int64 v10; int result; unsigned int v12; __int64 v13; __int128 v14[2 ]; v14[0 ] = 0 i64; v14[1 ] = 0 i64; sub_140001080("%s" ); v3 = -1 i64; do ++v3; while ( *((_BYTE *)v14 + v3) ); if ( v3 != 31 ) { while ( 1 ) Sleep(0x3E8 u); } v4 = sub_140001280(v14); v5 = name; if ( v4 ) { sub_1400015C0(*(_QWORD *)(v4 + 8 )); sub_1400015C0(*(_QWORD *)(v6 + 16 )); v7 = dword_1400057E0; v5[dword_1400057E0] = *v8; dword_1400057E0 = v7 + 1 ; } UnDecorateSymbolName(v5, outputString, 0x100 u, 0 ); v9 = -1 i64; do ++v9; while ( outputString[v9] ); if ( v9 == 62 ) { v12 = 0 ; v13 = 0 i64; do { if ( a1234567890Qwer[outputString[v13] % 23 ] != *(_BYTE *)(v13 + 0x140003478 i64) ) _exit(v12); if ( a1234567890Qwer[outputString[v13] / 23 ] != *(_BYTE *)(v13 + 0x140003438 i64) ) _exit(v12 * v12); ++v12; ++v13; } while ( v12 < 0x3E ); sub_140001020("flag{MD5(your input)}\n" ); result = 0 ; } else { v10 = sub_1400018A0(std ::cout ); std ::ostream::operator <<(v10, sub_140001A60); result = -1 ; } return result; }
0x140003478i64
即
1 .rdata:0000000140003478 a46200860044218 db '(_@4620!08!6_0*0442!@186%%0@3=66!!974*3234=&0^3&1@=&0908!6_0*&',0
先还原出outputString
1 2 3 4 5 6 7 8 9 10 11 12 import hashlib_003478 = "(_@4620!08!6_0*0442!@186%%0@3=66!!974*3234=&0^3&1@=&0908!6_0*&" _003438 = "55565653255552225565565555243466334653663544426565555525555222" _0033A0 = "1234567890-=!@#$%^&*()_+qwertyuiop[]QWERTYUIOP{}asdfghjkl;'ASDFGHJKL:\"ZXCVBNM<>?zxcvbnm,./" output = [0 for _ in _003478] for _ in range (len (_003478)): b = _0033A0.index(_003478[_]) a = _0033A0.index(_003438[_]) output[_] = chr (23 * a + b) cipher = "" .join(output) print(cipher)
得到结果如下
1 private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)Traceback (most recent call last)
然后是C++名字修饰
在一块实在没怎么了解过
https://www.freesion.com/article/6515734088/
中间的函数貌似有替换字符串顺序
断点下到UnDecorateSymbolName(v5, outputString, 0x100u, 0);
就能在寄存器看到替换顺序之后字符串
1 2 3 4 5 6 7 8 9 10 decorate = "?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z" seq1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" seq2 = "PQHRSIDTUJVWKEBXYLZ[MF\\]N^_OGCA" cipher = [0 for _ in decorate] for _ in range (len (cipher)): cipher[_] = decorate[seq2.index(seq1[_])] print("" .join(cipher)) flag = "flag{" + hashlib.md5("" .join(cipher).encode()).hexdigest() + "}" print(flag)
flag{63b148e750fed3a33419168ac58083f5}
[ACTF新生赛2020]SoulLike
链接: https://buuoj.cn/challenges#[ACTF新生赛2020]SoulLike
ELF 64
IDA中 main 函数反编译结果如下
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 __int64 __fastcall main (int a1, char **a2, char **a3) { __int64 result; char v5; int i; int j; int v8[14 ]; char v9[110 ]; unsigned __int64 v10; v10 = __readfsqword(0x28 u); printf ("input flag:" ); scanf ("%s" , &v9[6 ]); strcpy (v9, "actf{" ); v5 = 1 ; for ( i = 0 ; i <= 4 ; ++i ) { if ( v9[i] != v9[i + 6 ] ) { v5 = 0 ; goto LABEL_6; } } if ( !v5 ) goto LABEL_16; LABEL_6: for ( j = 0 ; j <= 11 ; ++j ) v8[j] = v9[j + 11 ]; if ( (unsigned __int8)sub_83A(v8) && v9[23 ] == 125 ) { printf ("That's true! flag is %s" , &v9[6 ]); result = 0LL ; } else { LABEL_16: printf ("Try another time..." ); result = 0LL ; } return result; }
函数sub_83A
代码过长
IDA无法反编译
需要修改文件hexrays.cfg
1 MAX_FUNCSIZE = 64 // Functions over 64K are not decompiled
这个数值改大点就行
反编译了sub_83A
就知道该干嘛了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import reimport osdata = open ("data" , "r" ).read().replace(" " , "" ).replace("u;" , "" ).replace(";" , "" ).replace("*a1" , "a1[0]" ) data = re.sub("\+\+(.*)" , "\\1 -= 1" , data).split("\n" ) write = open ("data_re.py" , "w" ) write.write("a1 = [126, 50, 37, 88, 89, 107, 53, 110, 0, 19, 30, 56]\n" ) for _ in data[::-1 ]: write.write(_+"\n" ) write.write("""flag = "flag{" for _ in a1: flag += chr(_) flag += "}" print(flag)""" )write.close() os.system("python3 data_re.py" )
flag{b0Nf|Re_LiT!}
[MRCTF2020]PixelShooter
链接: https://buuoj.cn/challenges#[MRCTF2020]PixelShooter
Android
貌似是用了U3D, 改成zip解压, 找到Assembly-CSharp.dll
在IDA里看字符串就行
flag{Unity_1S_Fun_233}
[安洵杯 2019]crackMe
链接: https://buuoj.cn/challenges#[安洵杯 2019]crackMe
EXE 32
IDA中 _main_0 函数反编译结果如下
1 2 3 4 5 6 7 8 9 10 11 int __cdecl __noreturn main_0 (int argc, const char **argv, const char **envp) { int (__cdecl *v3)(_DWORD *); printf ("please Input the flag:\n" ); scanf_s("%s" , &unk_41A1E4); MessageBoxW(0 , L"Exception" , L"Warning" , 0 ); v3 = sub_41100F; MEMORY[0 ] = 1 ; sub_411136(HIWORD(v3)); }
运行的时候回弹一个"hooked"的窗口
动调发现函数MessageBoxW
被hook到了sub_412AB0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int __stdcall sub_412AB0 (int a1, int a2, int a3, int a4) { size_t i; for ( i = 0 ; i < j_strlen(Str); ++i ) { if ( Str[i] <= 122 && Str[i] >= 97 ) { Str[i] -= 32 ; } else if ( Str[i] <= 90 && Str[i] >= 65 ) { Str[i] += 32 ; } } MessageBoxA(0 , "hooked" , "successed" , 0 ); AddVectoredExceptionHandler(0 , Handler); return 0 ; }
做了一个VEH的异常处理
1 2 3 4 5 6 7 8 9 10 11 12 int __stdcall Handler_0 (_DWORD **a1) { char v2[20 ]; if ( **a1 == -1073741819 ) { qmemcpy(v2, "where_are_u_now?" , 16 ); sub_411172(&unk_41A218, v2); SetUnhandledExceptionFilter(TopLevelExceptionFilter); } return 0 ; }
VEH异常处理又套了一个SEH异常处理
运行完函数sub_41100F
之后
这条语句会触发异常
从而进入VEH的异常处理函数
理一遍顺序
Base64表内容变换
函数sub_411172
使用SM4加密输入内容(搜一下0xA3B1BAC6
)
Str2顺序变换
函数sub_4110FF
变换Base64表顺序
Str2与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 import base64import binasciiimport pysm4cipher = "1UTAOIkpyOSWGv/mOYFY4R!!" cipher_list = [0 for _ in cipher ] for _ in range (0 , len (cipher_list), 2 ): cipher_list[_] = cipher[_+1 ] cipher_list[_+1 ] = cipher[_] cipher = "" .join(cipher_list).replace("!" , "" ) base64_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" base64_table_re = [0 for _ in base64_table] for _ in range (len (base64_table_re)): if 97 <= ord (base64_table[(_+24 )%64 ]) <= 122 : base64_table_re[_] = chr (ord (base64_table[(_+24 )%64 ])-32 ) elif 65 <= ord (base64_table[(_+24 )%64 ]) <= 90 : base64_table_re[_] = chr (ord (base64_table[(_+24 )%64 ])+32 ) else : base64_table_re[_] = base64_table[(_+24 )%64 ] base64_table_re = "" .join(base64_table_re) cipher_list = [0 for _ in cipher] for _ in range (len (cipher)): cipher_list[_] = base64_table[base64_table_re.index(cipher[_])] cipher = "" .join(cipher_list) cipher = base64.b64decode(cipher+"==" ) key = b"where_are_u_now?" flag = pysm4.decrypt(int (binascii.hexlify(cipher).decode(), 16 ), (int (binascii.hexlify(key).decode(), 16 ))) print(binascii.unhexlify(hex (flag)[2 :].encode()))
flag{SM4foRExcepioN?!}
[FlareOn1]Bob Doge
链接: https://buuoj.cn/challenges#[FlareOn1]Bob Doge
EXE 64
下载下来是一个安装程序
得到Challenge1.exe
丢进dnspy64
在函数btnDecode_Click
里打断点看字符串
可以看到flag
flag{3rmahg3rd.b0b.d0ge@flare-on.com}
EOF