MRCTF 2021
谭总:这波是re带飞
RE real_signIn cmp
换的表
MRCTF{wElc0Me_t0_MRCTF_2o21!!!}
dynamic_debug main 很简单
里面的 check 看到了其他的引用 在引用处还有异或
遂动调至此处 果然其代码被更改
去掉一些垃圾指令以后 创建函数就可以F5了
随便分析一下就知道是 tea 了
直接把他代码复制粘贴反着写一遍输出就完事了
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 #include <stdio.h> #define det 0x9E3779B9 unsigned char cmp[] ={ 0x99 , 0xA1 , 0x85 , 0x55 , 0x68 , 0x5D , 0x82 , 0x7E , 0x39 , 0x00 , 0x4D , 0x94 , 0x43 , 0x69 , 0x72 , 0x71 , 0x06 , 0x43 , 0x51 , 0x6A , 0x00 , 0xAD , 0x14 , 0x4B , 0x3F , 0x0D , 0xD2 , 0x64 , 0x15 , 0xDB , 0x37 , 0x9F , }; unsigned char key[]="cdbgnamie_dyilik" ;int main () { for (int i=0 ;i<8 ;i+=2 ) { int sum=det*32 ; unsigned int a=((unsigned int *)cmp)[i],b=((unsigned int *)cmp)[i+1 ]; for (int j=0 ;j<32 ;++j) { b -= ((a + sum) ^ (16 * a + ((unsigned int *)key)[1 ]) ^ ((a >> 5 ) + ((unsigned int *)key)[0 ])); a -= (b + sum) ^ (16 * b + ((unsigned int *)key)[3 ]) ^ ((b >> 5 ) + ((unsigned int *)key)[2 ]); sum -=det; } printf ("%.4s%.4s" ,(char *)&a,(char *)&b); } }
MRCTF{Dyn4m1c_d3buG_1s_a_ki11eR}
EzGame 玩出来
条件是只死一次 捡到105个星星 捡到曲奇 找到外星人 回家
如果不开挂的话是完不成的 因为捡曲奇就会死 然后105个星星有的星星太高了
所以至少需要一个能够更改高度的挂
然后跳起来为0 可以跳起来即平地状态搜1 可以搜到一个地址
然后一直锁写1就可以连跳了
然后正常完成任务只死一次就可以getflag了
不过后来研究了一下
在点击getflag的时候
会判断是否吃饼干 是否回家 是否找到外星人 星星是否是105 是否只死1次
然后用一个key去解密 再判断解密后的flag前5位是否是MRCTF 如果不是就说你作弊
有一个关键就是捡星星的时候key会被改变
于是逆出了算法
然后用转换过一次的key再转换104次即得到正确的key
再把其他条件hack一下 点击getflag就可以得到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 #include <defs.h> #include <stdio.h> void fun (unsigned long long & key) { char v13; int v11; long long v10,v12; unsigned long long v14; for (int i=0 ;i<4 ;++i) { v10 = 0 ; v11 = 0 ; v12 = 1 ; for (int j=0 ;j<64 ;++j) { v13 = v11++ & 0x3F ; v14 = v12 & key; v12 = __ROL8__(v12, 1 ); v10 ^= v14 >> v13; } key = v10 | (2 * key); } } int main () { unsigned long long key=0xEADBEEFAA114514E ; for (int i=0 ;i<104 ;++i) fun(key); printf ("%llx" ,key); }
MRCTF{Ez_G@mE_MaY_Be_Ha3d_4_y0U}
MR_register 这题很有意思
打开主main后,主main创建管道
然后用环境变量 hWrite 保存管道句柄
然后打开子main
然后主main一直等待debug事件
debug事件有下
如果 rip 为 int 3, rip+1 为 0x25 则让 rip+=2
0x25是作者故意写在子main流程的字节 用来标识
如果 rip 为0x25,rip+1 也为0x25 就读一段字节 异或后 写回 rip
这里动调可以知道这里异或的是子main的下面
用ida脚本异或一下即可 再把他的那些没用的0x25 0xC3 nop掉即可反编译
1 2 3 4 5 6 7 8 9 import idcst = 0x401E1f i = 0 while st <= 0x401E1f +0x57d : value = ida_bytes.get_byte(st) value ^= (i%256 ) ida_bytes.patch_byte(st, value) st += 1 i += 1
还有一个除零异常
里面有一个 call 扰乱了ida分析 nop 掉即可反编译
这里又是和上面的 读 异或 写 流程很像
动调这里就可以得到异或后的字节 是后面会用到的 cmp
子main流程如下
将 cmp 的内容写入管道 然后引发除零异常 异常处理完后 cmp 已经被修改过了
sub_401CA7 中引发了 int 3 修改了字节码 用上面那个脚本处理过后(并nop掉没用字节)可以比较清晰的反编译
这个函数将两个输入运算后将结果写到 WINDIR\temp\sign 中
而下面那个 sub_40239E 就是 check 函数
初步逆了一下正向算法如下
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 <stdio.h> #include <string.h> char table[]="ABCDEFGH123456780IJKLMNO+OPQRStu\\vwxyzTUabcdefghVWXYZijklmnopqrs" ;char cmp[]="GZ5yExGiGXEx5zGXExGWHlHlExGX5wGVExGZ5wGYExGk5vGjEx5xHl5yExGWGZ5vGWExGXGWHl5yExGYGj5vGkExGiHlGjGZExHl5THlGVEx5zHlGZGYExGWGkHlGXGkExGXGjGVHm5yExGY5yHmGXGjExGjGY5vGYHlEx5vGWGY5xGiExGWGVGZ5zGi5xExGW5vGjGXGiGXExGX5v5wGWGXGXExGZGiGWGY5wGYExGj5T5xGiGYGZEx5wGZ5yHmGiGiExGWGXGZHm5z5wHmExGW5yHmGkHlGj5yExGX5T5zGWGkHm5yExGZ5yGkHmGVGj5wExGk5yGi5vHlGZGWEx5x5v5yGYHl5x5yExGWGZHlGX5zGWHl5wEx" ;char mail[1000 ];char crit[1000 ];char v8[]={0 ,2 ,4 ,6 };char v10[1000 ];char v11[1000 ];char Buf[1000 ];char Des[1000 ];unsigned int v21[1000 ];int main () { unsigned char cha1,cha2,cha3; int i,j,lv10,lv11,_pos; for ( i = 0 ; i < strlen (mail); ++i ) { cha1 = (mail[i] >> 6 ) & 1 ^ v8[i % 4 ]; cha2 = (mail[i] >> 3 ) & 7 ; cha3 = mail[i] & 7 ; v10[j] = table[9 * cha1 + cha2]; v10[j + 1 ] = table[9 * cha2 + cha3]; j += 2 ; } v21[0 ] = *crit; v21[1 ] = crit[1 ]; sprintf (Buf, "%x#" , (unsigned int )v21[0 ]); sprintf (&Buf[strlen (Buf)], "%x#" , (unsigned int )v21[1 ]); for ( i = 2 ; i < strlen (crit); ++i ) { v21[i] = v21[i - 2 ] + v21[i - 1 ] + crit[i]; sprintf (&Buf[strlen (Buf)], "%x#" , (unsigned int )v21[i]); } j = 0 ; for ( i = 0 ; i < strlen (Buf); ++i ) { cha1 = (Buf[i] >> 6 ) & 1 ; cha2 = (Buf[i] >> 3 ) & 7 ; cha3 = Buf[i] & 7 ; v11[j] = table[9 * cha1 + cha2]; v11[j + 1 ] = table[9 * cha2 + cha3]; j += 2 ; } lv11 = strlen (v11); lv10 = strlen (v10); strcpy (Des, v10); Des[strlen (Des)] = '-' ; for ( i = lv10 + 1 ; i <= lv11 + lv10; ++i ) Des[i] = v11[i - lv10 - 1 ] ^ v10[(i - lv10 - 1 ) % lv10]; for ( i = 0 ; Des[i]; ++i ) { if ( Des[i] == '-' ) { _pos = i; break ; } } j = 0 ; for ( i = _pos + 1 ; i < strlen (Des) ; ++i ) { Des[i] ^= Des[j % _pos]; if ( Des[i] != cmp[j] ) return 0 ; ++j; } }
仔细看看可以发现mail被异或两次 相当于mail无所谓
尝试解发现table有问题 仔细看了一下才发现 每8位他加了一个空格
后面用的编辑器把#去掉 换成正确的格式 再解掉斐波那契算法即得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 #include <stdio.h> char flag[100 ]={0 };char table[]="ABCDEFGH 12345678 0IJKLMNO +OPQRStu \\vwxyzTU abcdefgh VWXYZijk lmnopqrs" ;char cmp[]="GZ5yExGiGXEx5zGXExGWHlHlExGX5wGVExGZ5wGYExGk5vGjEx5xHl5yExGWGZ5vGWExGXGWHl5yExGYGj5vGkExGiHlGjGZExHl5THlGVEx5zHlGZGYExGWGkHlGXGkExGXGjGVHm5yExGY5yHmGXGjExGjGY5vGYHlEx5vGWGY5xGiExGWGVGZ5zGi5xExGW5vGjGXGiGXExGX5v5wGWGXGXExGZGiGWGY5wGYExGj5T5xGiGYGZEx5wGZ5yHmGiGiExGWGXGZHm5z5wHmExGW5yHmGkHlGj5yExGX5T5zGWGkHm5yExGZ5yGkHmGVGj5wExGk5yGi5vHlGZGWEx5x5v5yGYHl5x5yExGWGZHlGX5zGWHl5wEx" ;int a[650 ];unsigned char buf[1000 ];int main () { for (int i = 0 ;cmp[i];i++) for (int j = 0 ;j<65 ;j++) if (cmp[i] == table[j]) { a[i] = j; break ; } int k = 0 ; for (int i = 0 ;i<377 ;i+=2 ) { unsigned char cha1 = (a[i] > 9 ); unsigned char cha2 = a[i ] - 9 * cha1; unsigned char cha3 = a[i+1 ] - 9 * cha2; buf[k++]=(cha1<<6 )|(cha2<<3 )|cha3; } int tmp[]={0x4d ,0x52 ,0xe2 ,0x188 ,0x2b0 ,0x4b3 ,0x7a6 ,0xc8d ,0x14a1 ,0x218d ,0x36a7 ,0x5864 ,0x8f80 ,0xe843 ,0x17827 ,0x2609d ,0x3d926 ,0x63a38 ,0xa13c5 ,0x104e5c ,0x1a6252 ,0x2ab122 ,0x4513b3 ,0x6fc534 ,0xb4d955 ,0x1249eb9 ,0x1d9786d ,0x2fe179d ,0x4d7906b ,0x7d5a841 ,0xcad38cd ,0x1482e18b }; flag[0 ]=0x4d ;flag[1 ]=0x52 ; for (int i=2 ;tmp[i];++i) flag[i]=tmp[i]-tmp[i-1 ]-tmp[i-2 ]; printf ("%s" ,flag); }
MRCTF{C4n_y0u_d3bug_1t?_n0_wa9!}
MRCheckin 首先第一个框框明文比较 MRCTF
然后切片(6,13]的md5解得为 Andr01d
然后格式 MRCTF{Andr01d+xxx}
又由cmp的数组长度可知xxx长度为25
然后动调出key数组即可
1 2 3 4 5 6 7 8 9 10 #include <stdio.h> int main () { char cmp[]={110 , 82 , 89 , 87 , 86 , 0x5F , 6 , 94 , 105 , 71 , 80 , 92 , 83 , 4 , 93 , 85 , 0x6F , 65 , 93 , 0x6F , 0x7C , 98 , 0x73 , 100 , 0x76 }; char key[100 ]={0x31 ,0x31 ,0x31 ,0x32 ,0x35 ,0x34 ,0x37 ,0x30 ,0x36 ,0x30 ,0x35 ,0x30 ,0x30 ,0x34 ,0x30 ,0x30 ,0x30 ,0x35 ,0x32 ,0x30 ,0x31 ,0x30 ,0x30 ,0x30 ,0x30 ,0 }; for (int i=0 ;key[i];++i) printf ("%c" ,key[i]^cmp[i]); }
MRCTF{Andr01d_check1n_welc0me_to_MRCTF}