REVERSE-PRACTICE-BUUCTF-18
- [SWPU2019]ReverseMe
- [FlareOn1]Bob Doge
- [FlareOn5]Ultimate Minesweeper
- [GKCTF2020]Chelly's identity
[SWPU2019]ReverseMe
exe程序,运行后提示输入flag,输入错误打印“Try again”,无壳,ida分析
交叉引用字符串“Please input your flag:”来到sub_C22810函数
主要的逻辑为
获取输入,输入的长度为32,输入与字符串"SWPU_2019_CTF"循环异或,sub_C225C0函数对异或后的input进行处理,结果放在v27和v28,最后v27和v28与v36~v43比较,验证输入
int __thiscall sub_C22810(void *this)
{int v1; // eaxint v2; // ecxint v3; // eaxint v4; // esisigned int v5; // ediunsigned int v6; // kr00_4int *v7; // ecxint *input_copy; // ecxint *v9; // ecx__int128 *v10; // edxunsigned int v11; // ediint v12; // eaxint v13; // eaxbool v14; // cfunsigned __int8 v15; // alunsigned __int8 v16; // alunsigned __int8 v17; // alconst char *v18; // edxint v19; // eaxint v20; // eaxint v22; // [esp-14h] [ebp-D8h]int v23; // [esp-10h] [ebp-D4h]int *input; // [esp+24h] [ebp-A0h]int v25; // [esp+34h] [ebp-90h]unsigned int v26; // [esp+38h] [ebp-8Ch]__int128 v27; // [esp+3Ch] [ebp-88h]__int128 v28; // [esp+4Ch] [ebp-78h]int v29; // [esp+5Ch] [ebp-68h]__int128 v30; // [esp+60h] [ebp-64h]__int128 v31; // [esp+70h] [ebp-54h]int v32; // [esp+80h] [ebp-44h]__int64 v33; // [esp+84h] [ebp-40h]int v34; // [esp+8Ch] [ebp-38h]__int16 v35; // [esp+90h] [ebp-34h]int v36; // [esp+94h] [ebp-30h]int v37; // [esp+98h] [ebp-2Ch]int v38; // [esp+9Ch] [ebp-28h]int v39; // [esp+A0h] [ebp-24h]int v40; // [esp+A4h] [ebp-20h]int v41; // [esp+A8h] [ebp-1Ch]int v42; // [esp+ACh] [ebp-18h]int v43; // [esp+B0h] [ebp-14h]int v44; // [esp+C0h] [ebp-4h]v25 = 0;v26 = 15;LOBYTE(input) = 0;v44 = 0;LOBYTE(v44) = 1;v1 = printf(this, "Please input your flag: ");printf_(v1);sub_C237B0(&dword_C50068, &input); // 获取输入inputv34 = 'TC_9'; // v33~v35:"SWPU_2019_CTF"v33 = qword_C4B9A0;v35 = 'F';if ( v25 == 32 ) // 输入的长度为32{v40 = 0xBA143D17;v41 = 0x1D730350;v42 = 0x9404607A;v43 = 0x290AF070;v5 = 0;v30 = 0i64;v32 = 0;v31 = 0i64;v6 = strlen((const char *)&v33);do // do循环体是input和"SWPU_2019_CTF"循环异或{v7 = (int *)&input;if ( v26 >= 16 )v7 = input;*((_BYTE *)v7 + v5) ^= *((_BYTE *)&v33 + v5 % v6);++v5;}while ( v5 < 32 );input_copy = (int *)&input;v4 = (int)input;if ( v26 >= 0x10 )input_copy = input;v29 = 0;v27 = 0i64;v28 = 0i64;*(_QWORD *)&v30 = *(_QWORD *)input_copy;*((_QWORD *)&v30 + 1) = *((_QWORD *)input_copy + 1);*(_QWORD *)&v31 = *((_QWORD *)input_copy + 2);*((_QWORD *)&v31 + 1) = *((_QWORD *)input_copy + 3);sub_C225C0(v22, v23, 256, (unsigned int)&v30, (unsigned int)&v27);// 对异或后的input进行处理,结果放在v27和v28v36 = 0xF80F37B3;v37 = 0x5DAEBCBC;v9 = &v36;v38 = 0x864D5ABA;v10 = &v27;v39 = 0xD3629744;v11 = 28;v40 = 0x1624BA4F;v41 = 0x1A729F0B;v42 = 0x266D6865;v43 = 0x67C86BBA;while ( 1 ) // while循环体是sub_C225C0函数对异或后input的处理结果与v36~v43比较{v12 = *v9;if ( *v9 != *(_DWORD *)v10 )break;++v9;v10 = (__int128 *)((char *)v10 + 4);v14 = v11 < 4;v11 -= 4;if ( v14 ) // v14为1,即v11<4时,验证正确{v13 = 0;goto LABEL_19;}}v14 = (unsigned __int8)v12 < *(_BYTE *)v10;if ( (_BYTE)v12 != *(_BYTE *)v10|| (v15 = *((_BYTE *)v9 + 1), v14 = v15 < *((_BYTE *)v10 + 1), v15 != *((_BYTE *)v10 + 1))|| (v16 = *((_BYTE *)v9 + 2), v14 = v16 < *((_BYTE *)v10 + 2), v16 != *((_BYTE *)v10 + 2))|| (v17 = *((_BYTE *)v9 + 3), v14 = v17 < *((_BYTE *)v10 + 3), v17 != *((_BYTE *)v10 + 3)) ){v13 = -v14 | 1;}else{v13 = 0;}
LABEL_19:if ( v13 ) // v13为0时,验证正确v18 = "Try again!\r\n";elsev18 = "Congratulations! I always knew you could do it.";v19 = printf(v9, v18);printf_(v19);sub_C2ADBE("pause");}else{v3 = printf(v2, "Try again!\r\n");printf_(v3);sub_C2ADBE("pause");v4 = (int)input;}if ( v26 >= 0x10 ){v20 = v4;if ( v26 + 1 >= 0x1000 ){v4 = *(_DWORD *)(v4 - 4);if ( (unsigned int)(v20 - v4 - 4) > 0x1F )sub_C2AFF7(v26 + 36);}sub_C264DE(v4);}return 0;
}
sub_C225C0函数对异或后的input进行处理,可通过下内存断点得知具体情况
首先在调用sub_C225C0函数前下断点,开始调试
输入为abcdefghijklmnopqrstuvwxyz123456,经过与"SWPU_2019_CTF"的循环异或及变量赋值后,v30即为input循环异或后的结果
在0x4FF858处按F2下内存断点
按F9执行,来到一个v30(下图中为v25)与v28异或的地方
可知sub_C225C0函数是对循环异或后的input的结果的再一次异或,但是不知道异或的是哪些值
由于我们输入是“abcdefghijklmnopqrstuvwxyz123456”,与"SWPU_2019_CTF"异或后的结果v30是确定的,可以通过调试得到sub_C225C0函数调用完后,结果v27和v28的值,再用v30异或v27和v28得到sub_C225C0函数中参与异或运算的另一些值
调试得到v27和v28为
写逆运算脚本即可得到flag
#coding:utf-8
#a是两次异或后,v27和v28的数据
a=[0xB4, 0x39, 0x0D, 0xFB, 0xA2, 0x83, 0xF9, 0x40, 0xB2, 0x42,0x43, 0x9E, 0x41, 0x9C, 0x4F, 0x90, 0x4D, 0xBC, 0x76, 0x41,0x3E, 0xB6, 0x53, 0x0B, 0x6E, 0x66, 0x29, 0x75, 0xE5, 0x1C,0xBE, 0x2C]
#b是input第一次异或后,v30的数据
b=[0x32, 0x35, 0x33, 0x31, 0x3A, 0x54, 0x57, 0x59, 0x50, 0x35,0x28, 0x38, 0x2B, 0x3D, 0x38, 0x20, 0x24, 0x2D, 0x41, 0x44,0x44, 0x4F, 0x28, 0x3B, 0x2D, 0x3C, 0x62, 0x65, 0x63, 0x61,0x6A, 0x04]
#a^b即可得到sub_C225C0函数中参与异或运算的另一些值
#cipher是最后要比较的数据
cipher=[0xB3, 0x37, 0x0F, 0xF8, 0xBC, 0xBC, 0xAE, 0x5D, 0xBA, 0x5A,0x4D, 0x86, 0x44, 0x97, 0x62, 0xD3, 0x4F, 0xBA, 0x24, 0x16,0x0B, 0x9F, 0x72, 0x1A, 0x65, 0x68, 0x6D, 0x26, 0xBA, 0x6B,0xC8, 0x67]
s="SWPU_2019_CTF"
flag=""
for i in range(len(cipher)):flag+=chr(a[i]^b[i]^cipher[i]^ord(s[i%len(s)]))
print(flag)
#flag{Y0uaretheB3st!#@_VirtualCC}
[FlareOn1]Bob Doge
exe程序,运行后点击“DECODE”,看到一段不明白意义的字符串
查壳,发现是.Net程序,用dnSpy打开
找到“DECODE”按钮的响应函数,看到该函数会得到三个字符串text,text2和text3,调试看看这三个字符串分别是什么
可以看到,字符串text即为flag
[FlareOn5]Ultimate Minesweeper
exe程序,运行后是个扫雷游戏,一共30x30=900个格子,897个雷,只有3个不是雷,提示说找到那3个不是雷的格子就能得到flag
查壳,发现是.Net程序,用dnSpy打开
先找到决定弹出失败(FailurePopup)或成功(SuccessPopup)对话框(ShowDialog)的SquareRevealedCallback函数
选中SquareRevealedCallback函数名,右键->分析->被使用,来到MainForm函数
挨着点进去分析,发现在AllocateMemory函数会对900个格子,每个格子赋一个bool值,雷的格子赋为true,非雷的格子赋为false
在this.VALLOC_TYPES数组中就保存着非零格子的信息,但是不知道具体下标
调试,在执行完“this.mineFieldControl.DataSource = this.MineField;”后,在GarbageCollect数组中可以看到,雷的格子为true,非雷为false
找到3个非雷格子的位置分别为[7,20],[24,28],[28,7]
运行exe程序,点出3个非雷的格子(注意[]中第一个坐标为列,第二个坐标为行,且均从0开始),即可得到flag
[GKCTF2020]Chelly’s identity
exe程序,运行后输入,无壳,ida分析
交叉引用字符串“flag is flag{(your answer)}!”来到sub_41C290函数
主要逻辑为,获取输入,验证输入长度是否为16,对输入进行处理(实际上为对输入进行异或运算),验证处理后的输入
sub_411721->sub_41B3B0
首先获取2~128之间的素数,存到v12
然后进入while循环体,while里面有个小的for循环,for循环中的i小于输入字符的ascii码,且i每次的取值是2~128之间的素数,for循环中v9从0开始累加i,当i的取值大于等于输入字符的ascii码时,退出for循环,输入和累加好的v9异或,再进入下一次while循环,直到输入的每个字符都完成异或运算
从sub_411852函数中取出最后要比较的数据,写逆脚本即可得到flag
#coding:utf-8
import gmpy2
res=[438,1176,1089,377,377,1600,924,377,1610,924,637,639,376,566,836,830]
sushu=[]
#获取2~128之间的素数
for i in range(2,128):if gmpy2.is_prime(i):sushu.append(i)
flag=""
for i in range(len(res)):for j in range(32,128):#爆破input的字符tmp=0for k in range(len(sushu)):if sushu[k]<j: #sushu[k]相当于i,小于input字符的ascii码且为2~128之间的素数tmp+=sushu[k] #v9累加else:breakif j^tmp==res[i]: #异或后比较相等flag+=chr(j)
print(flag)
#Che11y_1s_EG0IST