NSSCTF [NSSRound#4 SWPU]hide_and_seek 打开调试后,发现有反调试,把一开始的exit给nop掉
然后调试跳到后面发现有flag:SCTF{wud3_0n@,34p}
但是结果不对??
再次调试,在hex视图中alt+t搜索NSSCTF,发现flag
[NSSRound#X Basic]ez_z3 先用die查壳,发现是upx,尝试用upx-d脱壳,然后失败,应该是改过什么东西,打开ida看看,结果发现节区名从UPX变成了XYU,所以尝试还原,还原后还是失败,在03E0处还需修改为UPX,这次UPX-d成功,参考UPX防脱壳机脱壳、去除特征码、添加花指令小探 - 『脱壳破解区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
打开ida查看main函数并尝试调试
1 2 3 4 5 Please input the flag:1212121 Can you calculated out z3? Please input z3:121212 YOU are wrong oh no!!!!!your flag is wrong,try again
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 int __fastcall main_0 (int argc, const char **argv, const char **envp) { __int64 v3; __int64 v4; print (std::cout, "Please input the flag:" ); getin (std::cin, flag); v3 = print (std::cout, "Can you calculated out z3?" ); std::ostream::operator <<(v3, guessNoUse); print (std::cout, "Please input z3:" ); getin (std::cin, z3num); for ( i = 0 ; i < j_strlen (flag); ++i ) storn[i] = change (flag[i], exenum[i]); z3check1 (z3num); z3check2 (z3num); check = sub_7FF7CCBC10E6 (); if ( check == 1 ) v4 = print (std::cout, "yeah!!!!!!you get the flag" ); else v4 = print (std::cout, "oh no!!!!!your flag is wrong,try again" ); std::ostream::operator <<(v4, guessNoUse); system ("pause" ); return 0 ; }
根据流程大致是先对flag变换为k,然后检测输入一个z3序列是否满足条件,然后让z3序列和k变换,检查最后结果
那么先找到z3num:
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 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 from z3 import *f = Solver() z3num = [Int(f'z3num[{i} ]' ) for i in range (20 )] for num in z3num: f.add(num >= 0 ) f.add(num <= 255 ) f.add(20 * z3num[19 ] * 19 * z3num[18 ] + 14 * z3num[13 ] + 13 * z3num[12 ] + 11 * z3num[10 ] * 10 * z3num[9 ] + 30 * z3num[5 ] + 5 * z3num[4 ] + z3num[0 ] + 2 * z3num[1 ] - 3 * z3num[2 ] - 4 * z3num[3 ] - 7 * z3num[6 ] + 8 * z3num[7 ] - 9 * z3num[8 ] - 12 * z3num[11 ] - 16 * z3num[15 ] * 15 * z3num[14 ] - 17 * z3num[16 ] - 18 * z3num[17 ] == 2582239 ) f.add(20 * z3num[19 ] * 19 * z3num[18 ] + 14 * z3num[13 ] + 13 * z3num[12 ] + 11 * z3num[10 ] * 10 * z3num[9 ] + 30 * z3num[5 ] - 7 * z3num[6 ] + 8 * z3num[7 ] - 9 * z3num[8 ] + 5 * z3num[4 ] + 3 * z3num[2 ] + 2 * z3num[1 ] * z3num[0 ] - 4 * z3num[3 ] - 12 * z3num[11 ] - 16 * z3num[15 ] * 15 * z3num[14 ] - (18 * z3num[17 ] + 17 * z3num[16 ]) == 2602741 ) f.add(19 * z3num[18 ] + 18 * z3num[17 ] + 14 * z3num[13 ] * 13 * z3num[12 ] + 12 * z3num[11 ] * 11 * z3num[10 ] + 9 * z3num[8 ] + 7 * z3num[6 ] * 30 * z3num[5 ] + z3num[0 ] - 2 * z3num[1 ] - 4 * z3num[3 ] * 3 * z3num[2 ] - 5 * z3num[4 ] + 8 * z3num[7 ] - 10 * z3num[9 ] - 15 * z3num[14 ] - 17 * z3num[16 ] * 16 * z3num[15 ] - 20 * z3num[19 ] == 2668123 ) f.add(20 * z3num[19 ] * 19 * z3num[18 ] + 14 * z3num[13 ] + (13 * z3num[12 ] + 11 * z3num[10 ] - 12 * z3num[11 ]) * 10 * z3num[9 ] + 30 * z3num[5 ] + 5 * z3num[4 ] + z3num[0 ] + 2 * z3num[1 ] - 3 * z3num[2 ] - 4 * z3num[3 ] - 7 * z3num[6 ] + 8 * z3num[7 ] - 9 * z3num[8 ] - 16 * z3num[15 ] * 15 * z3num[14 ] - 17 * z3num[16 ] - 18 * z3num[17 ] == 2520193 ) f.add(18 * z3num[17 ] + 17 * z3num[16 ] + 15 * z3num[14 ] + 13 * z3num[12 ] * 12 * z3num[11 ] + 10 * z3num[9 ] + 9 * z3num[8 ] * 8 * z3num[7 ] + 3 * z3num[2 ] * 2 * z3num[1 ] * z3num[0 ] - 4 * z3num[3 ] - 5 * z3num[4 ] - 30 * z3num[5 ] - 7 * z3num[6 ] - 11 * z3num[10 ] - 14 * z3num[13 ] - 16 * z3num[15 ] - 19 * z3num[18 ] - 20 * z3num[19 ] == 8904587 ) f.add(18 * z3num[17 ] + 7 * z3num[6 ] * 30 * z3num[5 ] * 5 * z3num[4 ] + 4 * z3num[3 ] + 8 * z3num[7 ] + z3num[0 ] - 2 * z3num[1 ] - 3 * z3num[2 ] - 9 * z3num[8 ] - 11 * z3num[10 ] * 10 * z3num[9 ] - 16 * z3num[15 ] * (13 * z3num[12 ] + 12 * z3num[11 ] - 14 * z3num[13 ] - 15 * z3num[14 ]) - 17 * z3num[16 ] - 19 * z3num[18 ] - 20 * z3num[19 ] == 1227620874 ) f.add(20 * z3num[19 ] * 19 * z3num[18 ] + 17 * z3num[16 ] + 14 * z3num[13 ] + 13 * z3num[12 ] + 12 * z3num[11 ] * 11 * z3num[10 ] * 10 * z3num[9 ] + 7 * z3num[6 ] * 30 * z3num[5 ] + 5 * z3num[4 ] + 3 * z3num[2 ] + z3num[0 ] + 2 * z3num[1 ] + 4 * z3num[3 ] + 8 * z3num[7 ] - 9 * z3num[8 ] - 16 * z3num[15 ] * 15 * z3num[14 ] - 18 * z3num[17 ] == 1836606059 ) f.add(20 * z3num[19 ] * 19 * z3num[18 ] + 16 * z3num[15 ] * 15 * z3num[14 ] + 14 * z3num[13 ] + 13 * z3num[12 ] + 12 * z3num[11 ] + 7 * z3num[6 ] * 30 * z3num[5 ] + 5 * z3num[4 ] + 2 * z3num[1 ] * z3num[0 ] - 3 * z3num[2 ] + 4 * z3num[3 ] + 8 * z3num[7 ] - 9 * z3num[8 ] - 10 * z3num[9 ] - 11 * z3num[10 ] - 17 * z3num[16 ] - 18 * z3num[17 ] == 8720560 ) f.add(20 * z3num[19 ] * 19 * z3num[18 ] + 14 * z3num[13 ] + 13 * z3num[12 ] + 11 * z3num[10 ] * (10 * z3num[9 ] + 30 * z3num[5 ] + 5 * z3num[4 ] + 4 * z3num[3 ] - 7 * z3num[6 ] + 8 * z3num[7 ] - 9 * z3num[8 ]) + z3num[0 ] + 2 * z3num[1 ] - 3 * z3num[2 ] - 12 * z3num[11 ] - (16 * z3num[15 ] - 17 * z3num[16 ] - 18 * z3num[17 ]) * 15 * z3num[14 ] == 11387045 ) f.add(20 * z3num[19 ] * 19 * z3num[18 ] + 16 * z3num[15 ] * 15 * z3num[14 ] + 14 * z3num[13 ] + 11 * z3num[10 ] * 10 * z3num[9 ] + 9 * z3num[8 ] + 3 * z3num[2 ] + z3num[0 ] - 2 * z3num[1 ] + 4 * z3num[3 ] - 5 * z3num[4 ] - 30 * z3num[5 ] - 7 * z3num[6 ] + 8 * z3num[7 ] - 12 * z3num[11 ] - 13 * z3num[12 ] - 17 * z3num[16 ] - 18 * z3num[17 ] == 7660269 ) f.add(20 * z3num[19 ] * 19 * z3num[18 ] + 14 * z3num[13 ] + 13 * z3num[12 ] + 11 * z3num[10 ] * 10 * z3num[9 ] - 12 * z3num[11 ] + z3num[0 ] + 2 * z3num[1 ] - (4 * z3num[3 ] * 3 * z3num[2 ] - 5 * z3num[4 ] - 30 * z3num[5 ]) - 7 * z3num[6 ] + 8 * z3num[7 ] - 9 * z3num[8 ] - 16 * z3num[15 ] * 15 * z3num[14 ] - 17 * z3num[16 ] - 18 * z3num[17 ] == 2461883 ) f.add(14 * z3num[13 ] + 11 * z3num[10 ] * 10 * z3num[9 ] + 9 * z3num[8 ] * 8 * z3num[7 ] + 7 * z3num[6 ] + 2 * z3num[1 ] * z3num[0 ] - 4 * z3num[3 ] * 3 * z3num[2 ] - 5 * z3num[4 ] - 30 * z3num[5 ] - 12 * z3num[11 ] - 13 * z3num[12 ] - 15 * z3num[14 ] - 17 * z3num[16 ] * 16 * z3num[15 ] - 18 * z3num[17 ] - 19 * z3num[18 ] - 20 * z3num[19 ] == -966296 ) f.add(14 * z3num[13 ] + 13 * z3num[12 ] + (11 * z3num[10 ] * 10 * z3num[9 ] + 30 * z3num[5 ] + 5 * z3num[4 ] + 3 * z3num[2 ] + 4 * z3num[3 ] - 7 * z3num[6 ] + 8 * z3num[7 ] - 9 * z3num[8 ]) * 2 * z3num[1 ] + z3num[0 ] - 12 * z3num[11 ] - 15 * z3num[14 ] - 16 * z3num[15 ] - 17 * z3num[16 ] - 18 * z3num[17 ] - 20 * z3num[19 ] * 19 * z3num[18 ] == 254500223 ) f.add(16 * z3num[15 ] * 15 * z3num[14 ] + 14 * z3num[13 ] + 11 * z3num[10 ] * 10 * z3num[9 ] + 7 * z3num[6 ] * 30 * z3num[5 ] + z3num[0 ] - 2 * z3num[1 ] - 3 * z3num[2 ] - 5 * z3num[4 ] * 4 * z3num[3 ] + 8 * z3num[7 ] - 9 * z3num[8 ] - 12 * z3num[11 ] - 13 * z3num[12 ] - 17 * z3num[16 ] - 18 * z3num[17 ] - 19 * z3num[18 ] - 20 * z3num[19 ] == 6022286 ) f.add(18 * z3num[17 ] + 16 * z3num[15 ] - 17 * z3num[16 ] + 14 * z3num[13 ] + 12 * z3num[11 ] + 11 * z3num[10 ] * 10 * z3num[9 ] + 30 * z3num[5 ] + 5 * z3num[4 ] + 4 * z3num[3 ] * 3 * z3num[2 ] + 2 * z3num[1 ] * z3num[0 ] - 9 * z3num[8 ] * 8 * z3num[7 ] * 7 * z3num[6 ] - 13 * z3num[12 ] - 15 * z3num[14 ] - 19 * z3num[18 ] - 20 * z3num[19 ] == -636956022 ) f.add(20 * z3num[19 ] * 19 * z3num[18 ] + 13 * z3num[12 ] + 12 * z3num[11 ] + 11 * z3num[10 ] * 10 * z3num[9 ] + 7 * z3num[6 ] + 30 * z3num[5 ] + 5 * z3num[4 ] + 3 * z3num[2 ] * 2 * z3num[1 ] * z3num[0 ] - 4 * z3num[3 ] - 9 * z3num[8 ] * 8 * z3num[7 ] - 14 * z3num[13 ] - 15 * z3num[14 ] - 16 * z3num[15 ] - 17 * z3num[16 ] - 18 * z3num[17 ] == 10631829 ) f.add(20 * z3num[19 ] * 19 * z3num[18 ] + 16 * z3num[15 ] - 17 * z3num[16 ] - 18 * z3num[17 ] + 15 * z3num[14 ] * 14 * z3num[13 ] + 13 * z3num[12 ] + 11 * z3num[10 ] * 10 * z3num[9 ] - 12 * z3num[11 ] + 7 * z3num[6 ] + (4 * z3num[3 ] - 5 * z3num[4 ] - 30 * z3num[5 ]) * 3 * z3num[2 ] + z3num[0 ] + 2 * z3num[1 ] + 8 * z3num[7 ] - 9 * z3num[8 ] == 6191333 ) f.add(14 * z3num[13 ] + 10 * z3num[9 ] * 9 * z3num[8 ] * 8 * z3num[7 ] + 5 * z3num[4 ] + 4 * z3num[3 ] * 3 * z3num[2 ] + 2 * z3num[1 ] * z3num[0 ] - 7 * z3num[6 ] * 30 * z3num[5 ] - 11 * z3num[10 ] - 13 * z3num[12 ] * 12 * z3num[11 ] - 16 * z3num[15 ] * 15 * z3num[14 ] - 18 * z3num[17 ] * 17 * z3num[16 ] - 20 * z3num[19 ] * 19 * z3num[18 ] == 890415359 ) f.add(20 * z3num[19 ] + 19 * z3num[18 ] + 18 * z3num[17 ] + 16 * z3num[15 ] - 17 * z3num[16 ] + 12 * z3num[11 ] + 11 * z3num[10 ] + 10 * z3num[9 ] + 9 * z3num[8 ] + 30 * z3num[5 ] + z3num[0 ] + 4 * z3num[3 ] * 3 * z3num[2 ] * 2 * z3num[1 ] - 5 * z3num[4 ] - 7 * z3num[6 ] + 8 * z3num[7 ] - 13 * z3num[12 ] - 14 * z3num[13 ] - 15 * z3num[14 ] == 23493664 ) f.add(20 * z3num[19 ] * 19 * z3num[18 ] + 13 * z3num[12 ] + 12 * z3num[11 ] + 10 * z3num[9 ] + 3 * z3num[2 ] * 2 * z3num[1 ] + z3num[0 ] - 4 * z3num[3 ] - 5 * z3num[4 ] + 8 * z3num[7 ] * 7 * z3num[6 ] * 30 * z3num[5 ] - 9 * z3num[8 ] - 11 * z3num[10 ] - 14 * z3num[13 ] - 16 * z3num[15 ] * 15 * z3num[14 ] - 17 * z3num[16 ] - 18 * z3num[17 ] == 1967260144 ) print (f.check())while (f.check()==sat): condition = [] m = f.model() p ="" for i in range (20 ): p += chr (int ("%s" % (m[z3num[i]]))) condition.append(z3num[i]!=int ("%s" % (m[z3num[i]]))) print (p) f.add(Or(condition))
这里奇怪的是一开始用或逻辑解不出来正确值,但是把所有都与之后却可以得到正确的结果,同时这个不是flag是中间用做加密的值
又因为后来的算法是
1 2 3 4 5 6 7 8 void __fastcall sub_7FF7CCBC4830 (char *z3num) { __int64 v1; j___CheckForDebuggerJustMyCode(&unk_7FF7CCBDB069, v1); for ( i = 0 ; i < j_strlen(flag); ++i ) last[i] = z3num[j_strlen(flag) - i - 1 ] ^ storn[i]; }
最后的检查是
1 2 3 4 5 6 7 8 9 10 11 12 size_t sub_7FF7CCBC4770 (__int64 a1) { __int64 v1; size_t len; j___CheckForDebuggerJustMyCode(&unk_7FF7CCBDB069, v1); zer0 = 0 ; len = j_strlen(flag); if ( len ) return last[zer0] == strange_num[zer0]; return len; }
这个可能错了什么,可能前两位相等相当于全部相等。
1 last = [0x00001207 , 0 x00004CA0, 0x00004F21 , 0x00000039 , 0 x0001A523 , 0 x0000023A, 0x00000926 , 0 x00004CA7, 0x00006560 , 0x00000036 , 0 x0001A99B, 0 x00004CA8, 0 x0001BBE0, 0x00003705 , 0x00000926 , 0 x000077D3 , 0x00009A98 , 0 x0000657B, 0x00000018 , 0x00000B11 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 ]
前面的变换是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 __int64 __fastcall change2 (int flag, __int64 exenum) { unsigned int a; int exei; exei = exenum; j___CheckForDebuggerJustMyCode(&unk_7FF7CCBDB069, exenum); a = 1 ; while ( exei ) { if ( (exei & 1 ) != 0 ) a *= flag; flag = flag * flag % 1000 ; exei >>= 2 ; } return a; }
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 exe = [0x7 , 0x7 , 0x7 , 0x9 , 0x5 , 0x6 , 0x7 , 0x7 , 0x7 , 0x9 , 0x7 , 0x7 , 0x5 , 0x7 , 0x7 , 0x7 , 0x5 , 0x7 , 0x9 , 0x7 ] last = [0x1207 , 0x4CA0 , 0x4F21 , 0x0039 , 0x1A523 , 0x023A , 0x0926 , 0x4CA7 , 0x6560 , 0x0036 , 0x1A99B , 0x4CA8 , 0x1BBE0 , 0x3705 , 0x0926 , 0x77D3 , 0x9A98 , 0x657B , 0x0018 , 0x0B11 ] z3num = list ('hahahathisisfackflag' ) stron = [0 ]*20 for i in range (20 ): stron[i] = ord(z3num[19 - i]) ^ last[i] flag = [0 ] * 20 def po(flag,b): a = 1 while (b): if (b & 1 != 0 ): a *= flag flag = flag * flag % 1000 b >>= 2 return a for i in range(20 ): for chrr in range(32 ,127 ): if (po(chrr,exe[i]) == stron[i]): flag[i] = chr(chrr) print(''.join(flag)) #T1e_z3_1s_v1r9_3asy!
爆破得出答案
总结:
z3遇到或时计算非常慢,用bitvec型或把或逻辑改成与加快速度
最后一步遇到不好逆向的也不好z3的,可以用爆破求解因为字符一定在32到127
一个疑问是:最后一步的b都是一位数,说明a一定是1或者flag的值,stron里面就应该是这个数为什么stron里不是呢
[NSSCTF 2nd]MyBase 打开ida后发现有tls,需要注意一下
ida打开之后
main函数有两个子函数
1 2 3 4 5 6 int __fastcall main (int argc, const char **argv, const char **envp) { _main(); test(); return 0 ; }
先调试看看有没有反调试,然后发现并没有
第一个_main看不懂,直接进test,里面有一个base64
打开后根据码表直接解密,但是解不出来。。
去data里面看看,发现还有码表,进交叉引用的函数发现有一个打乱的代码
是个c语言函数,有srand函数,所以调试把他找出来
真:
1 YsVO0tvT2o4puZ38j1dwf7MArGPNeQLDRHUK +SChbFanmklWEcgixXJIq6y5B/9 z
一开始加密的:
1 +86420ywusqomkigecaYWUSQOMKIGECABDFHJLNPRTVXZbdfhjlnprtvxz13579/
然后发现这个打乱代码执行了很多次
感觉哪里漏了,最后查找交叉引用发现打乱代码在加密里面有一行:
1 2 3 if ( !setjmp(env) ) exception_handler(); }
经过查阅:
setjmp和longjmp成对使用实现函数间跳转
setjmp将函数在此处的上下文保存在jmp_buf结构体中,以供longjmp从此结构体中恢复
1 int setjmp (jmp_buf env) ;
env为保存的结构体变量的位置,若直接调用返回0,否则返回非0(主要指通过longjmp跳转)
1 void longjmp (jmp_buf env, int val) ;
env为由setjmp保存的上下文,val表示longjmp传给setjmp的返回值,如果val=0,则setjmp返回1,否则返回val
longjmp 不直接返回,而是从 setjmp 函数中返回,longjmp 执行完之后,程序就像刚从 setjmp 函数返回一样。
所以;
1 2 3 if ( !setjmp(env) ) exception_handler(); }
这段代码先执行setjmp(env),返回0
进入exception_handler();
1 2 3 4 5 void __noreturn exception_handler () { generate_base64_table(base64_table); longjmp_0(env, 1 ); }
然后更新base64码表
所以逻辑是:
1 2 3 4 5 6 7 对第一组加密 更新 对第二组加密 更新 对第三组加密 更新 ...
所以密文
1 2 3 4 5 6 7 8 9 YkLY v1Xj 23 X7 N0E5 eoFg UveK eos1 XS8K 9 r4g
码表
1 2 3 4 5 6 7 8 9 +86420ywusqomkigecaYWUSQOMKIGECABDFHJLNPRTVXZbdfhjlnprtvxz13579/ YsVO0tvT2o4puZ38j1dwf7MArGPNeQLDRHUK+SChbFanmklWEcgixXJIq6y5B/9z xDfpNE4LYH5Tk+MRtrlv1oFbQm0gP37eqIajh2syUnZcSV8iBK6O/XWuzdCwA9GJ YvHeOZECmTyg0Mw2i7PIGKblsfF59rzUk6p3hVdW1qaQ+xRANnXLj48BcJDotS/u xDfpNE4LYH5Tk+MRtrlv1oFbQm0gP37eqIajh2syUnZcSV8iBK6O/XWuzdCwA9GJ YvHeOZECmTyg0Mw2i7PIGKblsfF59rzUk6p3hVdW1qaQ+xRANnXLj48BcJDotS/u xDfpNE4LYH5Tk+MRtrlv1oFbQm0gP37eqIajh2syUnZcSV8iBK6O/XWuzdCwA9GJ YvHeOZECmTyg0Mw2i7PIGKblsfF59rzUk6p3hVdW1qaQ+xRANnXLj48BcJDotS/u xDfpNE4LYH5Tk+MRtrlv1oFbQm0gP37eqIajh2syUnZcSV8iBK6O/XWuzdCwA9GJ
然后看了半天
1 2 3 4 v10 = (bbb << 8 ) + (aa << 16 ) + cc; basenum[v14] = base64_table[cc & 0x3F ]; basenum[v14 + 1 ] = base64_table[(v10 >> 6 ) & 0x3F ]; basenum[v14 + 2 ] = base64_table[(v10 >> 12 ) & 0x3F ];
发现是倒序输出
那么就直接倒序解码:
1 2 3 4 5 6 7 8 9 YLkY jX1v 7 X325E0 NgFoe KevU T0_ 1 soe Re_K8SX g4r9 ld }
1 NSSCTF {Welc0me_T0_Re_World}
[NSSRound#3 Team]jump_by_jump_revenge 打开后发现main函数不能反编译,感觉有花指令
然后nop掉两行代码
获得正确的main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int __cdecl main_0 (int argc, const char **argv, const char **envp) { int i; char Str1[36 ]; sub_411037("%s" , (char )Str1); for ( i = 0 ; i < 29 ; ++i ) Str1[i] = (Str1[i] + Str1[(i * i + 123 ) % 21 ]) % 96 + 32 ; if ( !j_strcmp(Str1, "~4G~M:=WV7iX,zlViGmu4?hJ0H-Q*" ) ) puts ("right!" ); else puts ("nope!" ); return 0 ; }
这个算法要注意要从后面往前,因为前面的已经被覆盖了
非常抽象(TAT)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 last = "~4G~M:=WV7iX,zlViGmu4?hJ0H-Q*" flag = [0 ]*29 c = [0 ]*29 for i in range (29 ): c[i] = (i * i + 123 ) % 21 for i in range (28 ,-1 ,-1 ): if (i > c[i]): for j in range (2 ): uuk = (ord (last[i])-32 )+ j * 96 -ord (last[c[i]]) if (31 < uuk < 127 ): flag[i]=uuk else : for j in range (2 ): uuk = (ord (last[i])-32 )+ j * 96 -flag[c[i]] if (31 < uuk < 127 ): flag[i]=uuk u = '' for i in range (29 ): u += chr (flag[i]) print (u)
总结:逆向加密算法时要注意是递推式的还是映射式的
看了看大佬的wp,好简洁:
1 2 3 4 5 6 7 8 9 ciper='~4G~M:=WV7iX,zlViGmu4?hJ0H-Q*' for i in range(len(ciper)-1 ,-1 ,-1 ): tmp=ord(ciper[i])-32 -ord(ciper[(i*i+123 )%21 ]) while (tmp<33 ): tmp+=96 ciper=ciper[:i]+chr(tmp)+ciper[i+1 :] print(ciper)
[NSSRound#2 Able]findxenny 打开main函数看看,对输入的flag的检查的函数返回值是本身??没理解暂时跳过,看到最后的检查有三个check
1 2 3 4 5 6 7 8 9 10 if ( (unsigned int )check1 (*v14) || (unsigned int )check2 (*v15) || (unsigned int )check3 (*v16) ) { v10 = sub_7FF72A641262 (std::cout, "Try harder" ); std::ostream::operator <<(v10, sub_7FF72A6411FE); } else { v9 = sub_7FF72A641262 (std::cout, "I can't believe my golden doge eye! we are comarde!" ); std::ostream::operator <<(v9, sub_7FF72A6411FE); }
打开之后是一组数,通过交叉引用查看这个数据是通过函数生成的,但是没看懂。。。
看了wp发现这个是SMC,三个check是函数,双击进入对应地址,发现都是数据,这时要c加p组合再f5进入反编译结果检查的值就在这里
1 2 3 4 5 6 7 __int64 __fastcall sub_278FBE97370 (__int64 a1) { if ( a1 == 0x665F756F795F686F i64 ) return 0 i64; else return -1 i64; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 __int64 __fastcall sub_278FBEA01C0 (__int64 a1) { __int64 v1; __int64 v3[2 ]; int v4[12 ]; v3[0 ] = a1; qmemcpy(v4, "ound_our" , 8 ); v1 = 0 i64; while ( *((_BYTE *)v3 + v1) == *((_BYTE *)v4 + v1) ) { if ( ++v1 >= 8 ) return 0 i64; } return -1 i64; }
1 2 3 4 5 6 7 __int64 __fastcall sub_278FBE999B4 (__int64 a1) { if ( a1 == 0x796E6E33785F i64 ) return 0 i64; else return -1 i64; }
直接得出结果
总结,如果看到virtulprotect和数据当作地址,尝试把数据改成代码
[NSSCTF 2nd]Bytecode 打开后,是py字节码
反编译一下
安洵杯 mobilego jadx打开mainactivity
模拟器中尝试输入,返回nonono
发现判断函数:
1 2 3 4 5 6 7 8 public void m141lambda$onCreate$0 $comexamplemobilegoMainActivity(View v) { if (Game.checkflag(this .editText.getText().toString()).equals(getResources().getString(C0569R.string.cmp))) { Toast.makeText(this , "yes your flag is right" , 0 ).show(); } else { Toast.makeText(this , "No No No" , 0 ).show(); } } }
在C0569R中有
1 2 3 4 public static final class string { public static int app_name = 2131689500 ; public static int cmp = 2131689512 ; }
这里的cmp是它的id值而不是它的具体值
所以动态调试找到终值
1 49021 }5 f919038b440139g74b7Dc88330e5d{6
这里使用了Game.checkflag来加密
所以先看这个
根据game
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package game;import p004go.Seq;public abstract class Game { private static native void _init () ; public static native String checkflag (String str) ; static { Seq.touch(); _init(); } private Game () { } public static void touch () { } }
怀疑checkflag是通过lib加载的
打开ida,反汇编so文件
搜索checkflag
找到5个函数
函数是这几个
好复杂,看不懂。。
看了看wp,发现一个一直没注意的东西,如果输入
1 2 1234567890 变为6143580927 ,{qwertyuiopasdfghjklzxcvbnm}变为bhgsruc{iojqkdpwnezyvxa}lfmt
说明这只是字符交换,交换之后是
1 49021 }5 f919038b440139g74b7Dc88330e5d{6
一共38个字符
那么尝试输入
1 t2 F7 GTglISYLnMzc6 CqhDN5 OdX8 wPjsKufVbE}
变为
1 NVLjz}ShscYPndXT62qlDFO5I8tgEKw7ufbMGC
根据这个对应关系,逆向解出原来的关系
1 2 3 4 5 6 7 8 9 10 11 12 d1 = 't2F7GTglISYLnMzc6CqhDN5OdX8wPjsKufVbE}' d2 = 'NVLjz}ShscYPndXT62qlDFO5I8tgEKw7ufbMGC' f2 = '49021}5f919038b440139g74b7Dc88330e5d{6' f1 = [0] *38 for i in range (38 ): for j in range (38 ): if (d2[j] == d1[i] ): f1[i] = f2[j] print ('' .join(f1) )#D0g3{4 c3b5903d11461f94478b7302980e958}
总结:
猜测输入时尽量使用有规律的字符,更容易看清
如果不容易逆向可以尝试看或者猜
你见过蓝色的小鲸鱼吗 打开文件后,发现是win程序,说明进入主窗口程序和判断函数不一定在一个位置
所以打开后需要找到判断代码,搜索string无效
根据弹窗函数是MessageBox,尝试搜索
然后找到判断函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 int __thiscall lastcall (void *this) { char wrong[28 ]; CHAR yes[20 ]; CHAR Caption[24 ]; void *v5; v5 = this; __CheckForDebuggerJustMyCode(&unk_52102F); strcpy (Caption, "tip" ); strcpy (yes, "You Get It!" ); strcpy (wrong, "Wrong user/passwd" ); if ( *((_DWORD *)v5 + 2 ) != *((_DWORD *)v5 + 3 ) || j__memcmp(*(const void **)v5, *((const void **)v5 + 1 ), *((_DWORD *)v5 + 3 )) ) { return MessageBoxA(0 , wrong, Caption, 0 ); } else { return MessageBoxA(0 , yes, Caption, 0 ); } }
这里的v5+2和v5+3一定指向一个是输入(可能加密了),另一个说不准,因为有一个密码一个用户名
由于需要进入else分支,所以条件必须为假
即:
1 2 *((_DWORD *)v5 + 2 ) == *((_DWORD *)v5 + 3 ) && !j__memcmpv5, *((const void **)v5 + 1 ), *((_DWORD *)v5 + 3 ))
既然如此,需要找出能改变v5+2和+3的地址的函数(修改了this),向上找:
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 int __cdecl main (int argc, const char **argv, const char **envp) { int result; void *v4; void *v5; CHAR *v6; CHAR *lpString; HWND DlgItem; HWND hWnd; int v10; int WindowTextLengthA; __CheckForDebuggerJustMyCode(&unk_52105E); hWnd = GetDlgItem((HWND)argc, 1003 ); DlgItem = GetDlgItem((HWND)argc, 1004 ); WindowTextLengthA = GetWindowTextLengthA(hWnd); v10 = GetWindowTextLengthA(DlgItem); lpString = (CHAR *)j__malloc(__CFADD__(WindowTextLengthA, 16 ) ? -1 : WindowTextLengthA + 16 ); result = (int )j__malloc(__CFADD__(v10, 16 ) ? -1 : v10 + 16 ); v6 = (CHAR *)result; if ( lpString && result ) { GetWindowTextA(hWnd, lpString, WindowTextLengthA + 16 ); GetWindowTextA(DlgItem, v6, v10 + 16 ); v5 = operator new(0x10 u); if ( v5 ) { sub_451B43(0x10 u); v4 = (void *)sub_450CE3(v5); } else { v4 = 0 ; } sub_44FC2B(&unk_51D38C, 0x10 u); sub_45126F(lpString, WindowTextLengthA, (int )v6, v10); j_lastcall(v4); j__free(lpString); j__free(v6); result = (int )v4; if ( v4 ) return sub_44F77B(1 ); } 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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 int __cdecl main (int argc, const char **argv, const char **envp) { int result; void *v4; void *v5; CHAR *password; CHAR *username; HWND DlgItem; HWND hWnd; int lenOFpass; int lenOFname; __CheckForDebuggerJustMyCode(&unk_52105E); hWnd = GetDlgItem((HWND)argc, 1003 ); DlgItem = GetDlgItem((HWND)argc, 1004 ); lenOFname = GetWindowTextLengthA(hWnd); lenOFpass = GetWindowTextLengthA(DlgItem); username = (CHAR *)j__malloc(__CFADD__(lenOFname, 16 ) ? -1 : lenOFname + 16 ); result = (int )j__malloc(__CFADD__(lenOFpass, 16 ) ? -1 : lenOFpass + 16 ); password = (CHAR *)result; if ( username && result ) { GetWindowTextA(hWnd, username, lenOFname + 16 ); GetWindowTextA(DlgItem, password, lenOFpass + 16 ); v5 = operator new(0x10 u); if ( v5 ) { sub_451B43(0x10 u); v4 = (void *)sub_450CE3(v5); } else { v4 = 0 ; } sub_44FC2B(&specialloc, 0x10 u); encode(username, lenOFname, (int )password, lenOFpass); j_lastcall(v4); j__free(username); j__free(password); result = (int )v4; if ( v4 ) return sub_44F77B(1 ); } return result; }
后面实在分析不出来了,里面有一些涉及c++的理解的
在后面有加密函数是blowfish
在cmp时下断点buf1就是最后的值
1 11A51F049550E2508F17E16CF1632B47
写blowfish的解密:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from Crypto.Cipher import Blowfishimport binasciiciphertext_hex = "11A51F049550E2508F17E16CF1632B47" ciphertext = binascii.unhexlify(ciphertext_hex) key = "UzBtZTBuZV9EMGcz" cipher = Blowfish.new(key.encode('utf-8' ), Blowfish.MODE_ECB) plaintext_bytes = cipher.decrypt(ciphertext) plaintext = plaintext_bytes.decode('utf-8' ) print (plaintext)
这个答案是个base64,解密后是@thebluef1sh
(和蓝鲸的唯一关系吗。。。)
所以
D0g3{UzBtZTBuZV9EMGczQHRoZWJsdWVmMXNo}