ctfshow_reverse
主要是写一些逆向的题目试一试
reverse1 拿到一个附件之后,放到 ida 中查看:
。。。
直接就有 flag 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int __fastcall main (int argc, const char **argv, const char **envp) { char s2[104 ]; unsigned __int64 v5; v5 = __readfsqword(0x28u ); puts ("plz input the key:" ); __isoc99_scanf("%s" , s2); if ( !strcmp ("flag{7ujm8ikhy6}" , s2) ) puts ("flag{7ujm8ikhy6}" ); else puts ("key error" ); return 0 ; }
flag{7ujm8ikhy6}
reverse2 附件包含一个 勒索病毒.exe 和 enflag.txt
看了一眼 enflag ,是乱码的,应该是被加密的。
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 do { sub_401037(asc_406BA0, v4); sub_401037((char *)&byte_406BFC, v5); sub_401037(asc_406C48, v6); sub_401037((char *)&byte_406CA8, v7); sub_401037((char *)&byte_406D0C, v8); sub_401037(a1, v9); sub_401073("%d" , (char )v10); if ( *(_DWORD *)v10 == 1 ) { v13 = fopen("flag.txt" , "r" ); if ( !v13 ) { sub_401037((char *)&byte_406D48, v4); getchar(); exit (0 ); } v12 = fopen("enflag.txt" , "w" ); if ( !v12 ) { sub_401037((char *)&byte_406D70, v4); getchar(); exit (0 ); } sub_401037(asc_406D84, v4); sub_401073("%s" , (char )Str); sub_401069(Str, Str1); sub_401028((int )Str, (int )v15, (int )v14, (int )v13, (int )v12); } else if ( *(_DWORD *)v10 == 2 ) { v11 = 0 ; } else { sub_401037(asc_406D98, v4); } } while ( v11 ); return 0 ;
应该是先在显示上打印提示字符,然后获取输入。
并且对 flag.txt 进行加密之后拿到 enflag.txt 。初步想法是拿到密钥之后根据算法反推原来的 flag,
看看主要的加密函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 char __cdecl sub_401A70 (char *Str, char *Str1) { char v3; signed int i; signed int v5; __CheckForDebuggerJustMyCode(byte_40B027); v5 = strlen (Str); for ( i = 0 ; i < v5; ++i ) Str1[i] += Str[i] ^ 0x1F ; if ( !strcmp (Str1, "DH~mqqvqxB^||zll@Jq~jkwpmvez{" ) ) sub_401037((char *)&byte_406B80, v3); else sub_401037("Error!\n" , v3); return *Str1; }
从这里可以知道密钥的处理方法是明文异或(XOR) 0x1F 得到密文
1 DH~mqqvqxB^||zll@Jq~jkwpmvez{
啧,有段字符被 ~ 包着了,md 格式识别成划去的字符了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int __cdecl sub_4014E0 (char *Str, int a2, int a3, FILE *Stream, FILE *a5) { char v6; size_t v7; __CheckForDebuggerJustMyCode(byte_40B027); v7 = strlen (Str); sub_4010F0(a2, Str, v7); sub_4010C8(a3); sub_40116D(a3, a2); sub_4010EB(a3, Stream, a5); fclose(Stream); fclose(a5); return sub_401037(asc_406B34, v6); }
根据这个结构,ai直接看出是典型的 RC4 加密,而 RC4 是对称加密,于是我们不需要这么麻烦的再写解密脚本,直接使用原来的程序帮我们解密即可。
下面是破解密钥的脚本:
1 2 3 4 5 cipher = "DH~mqqvqxB^||zll@Jq~jkwpmvez{" key = "" for c in cipher: key += chr (ord (c) ^ 0x1F )print (key)
运行后得到:[Warnning]Access_Unauthorized
然后复制一份 enflag.txt ,改名为 flag.txt
运行原程序:
此时打开 enflag.txt ,得到 flag :
flag{RC4&->ENc0d3F1le}
reverse3 嗯
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 v21 = __readfsqword(0x28u ); v7 = 80 ; v8 = 64227 ; v9 = 226312059 ; v10 = -1540056586 ; v11 = 5 ; v12 = 16 ; v13 = 3833 ; v5 = 0 ; puts ("plz input the key:" ); __isoc99_scanf("%s" , s); v3 = strlen (s); strncpy (dest, v19, v3 - 6 ); dest[strlen (s) - 6 ] = 0 ; __isoc99_sscanf(dest, "%x" , &v5); v17[0 ] = v7; v17[1 ] = v8; v17[2 ] = v9; v17[3 ] = v10; v17[4 ] = (v11 << 12 ) + v12; v17[5 ] = v13; v17[6 ] = v5; v16 = 0 ; for ( i = 0 ; i <= 6 ; ++i ) { for ( v16 += (unsigned int )v17[i]; v16 > 0xFFFF ; v16 = v15 + (unsigned int )(unsigned __int16)v16 ) { v14 = (unsigned __int16)v16; v15 = v16 >> 16 ; } } if ( v16 == 0xFFFF ) puts ("OK" ); else puts ("Error" ); return 0 ; }
前面的一段是标准的处理 flag 的格式,拿到 flag 里面的东西。
具体的算法是网络校验和,把 v17 的元素加到 v16 里面,只要 v16 大于 0xFFFF ,就把高 16 位和低 16 位拆开,然后加在一起折叠回去。
下面是拿 v5 的脚本:
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 to_uint32 (n ): return n & 0xFFFFFFFF v17_constants = [ 80 , 64227 , 226312059 , to_uint32(-1540056586 ), (5 << 12 ) + 16 , 3833 ] v16 = 0 for val in v17_constants: v16 += val while v16 > 0xFFFF : v16 = (v16 & 0xFFFF ) + (v16 >> 16 ) target_v5 = 0xFFFF - v16print (f"算出来的 v5 (Hex) 是: {hex (target_v5)} " )
算出来的 v5 (Hex) 是: 0x1a9f
flag{1a9f}
题目提示了 4 位即可
reverse4 1 2 3 4 5 6 7 8 9 int __fastcall __noreturn main (int argc, const char **argv, const char **envp) { qword_140004618 = (__int64)malloc (0x10u ); qword_140004620 = qword_140004618; *(_QWORD *)(qword_140004618 + 8 ) = 0 ; sub_140001020((char *)&Format); sub_140001080("%lld" ); ((void (__fastcall __noreturn *)())sub_1400010E0)(); }
初步判断是输入东西,然后通过 sub_1400010E0
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 void __fastcall __noreturn sub_1400010E0 (char *a1, __int64 a2) { int v2; __int64 v3; char *v4; char v5; __int64 v6; unsigned __int8 v7; char v8; char v9; v2 = 0 ; v3 = (__int64)a1; if ( a1 ) { v4 = &v9; do { ++v4; ++v2; a1 = &a4890572163qwe[-26 * (v3 / 26 )]; v5 = a1[v3]; v3 /= 26 ; a2 = v3; *(v4 - 1 ) = v5; } while ( v3 ); } v6 = v2; while ( v6 ) { v7 = *(&v8 + v6--); sub_1400011E0(v7 ^ 7u , a2, v3); } sub_140001220(a1, a2, v3); }
这段:
1 2 a1 = &a4890572163qwe[-26 * (v3 / 26 )]; v5 = a1[v3];
是 ida 反汇编取余,本质上是 v5 = table[ v3 % 26 ]
这里实际上是一个 26 进制转换,但是 26 进制的表被改了。
实际上是 )(*&^%489$!057@#><:2163qwe 中
也就是余 1 ,对应的是 )
然后
1 2 v7 = *(&v8 + v6--); sub_1400011E0(v7 ^ 7u , a2, v3);
倒序提取余数 ( 因为取余数的时候低位在前面 ) ,然后异或 7 。
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 void __noreturn sub_140001220 () { __int64 v0; int v1; __int64 v2; char v3; int v4; __int64 v5; char v6; int v7; v0 = qword_140004620; v1 = 0 ; v2 = 0 ; while ( 1 ) { v3 = *(_BYTE *)v0; v4 = v1 + 1 ; v5 = *(_QWORD *)(v0 + 8 ); if ( v3 != aV4pY59[v2] ) v4 = v1; qword_140004620 = v5; if ( !v5 ) break ; v6 = *(_BYTE *)v5; v7 = v4 + 1 ; v0 = *(_QWORD *)(v5 + 8 ); if ( v6 != aV4pY59[v2 + 1 ] ) v7 = v4; qword_140004620 = v0; if ( v0 ) { v2 += 2 ; v1 = v7; if ( v2 < 14 ) continue ; } goto LABEL_11; } v7 = v4; LABEL_11: if ( v7 == 14 ) sub_1400012E0(); sub_1400012B0(); }
这里是遍历链表然后与硬编码字符串 aV4pY59 比较
1 .rdata:00000001400032F 8 aV4pY59 db '/..v4p$$!>Y59-' ,0 ; DATA XREF: sub_140001220+B↑o
所以比对的密文是:/..v4p$$!>Y59-
以下是解密脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 TABLE = ")(*&^%489$!057@#><:2163qwe" target_cipher = "/..v4p$$!>Y59-" def decrypt (cipher ): num = 0 for char in cipher: original_char = chr (ord (char) ^ 7 ) digit = TABLE.index(original_char) num = num * 26 + digit return num flag_num = decrypt(target_cipher)print (f"目标密文: {target_cipher} " )print (f"解密出的最终 Flag: {flag_num} " )
得到数字:
2484524302484524302
运行源程序输入后验证正确。
reverse5