问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
【STARCTF2022】Simple File System & NaCI(去花F5!
CTF
关于*CTF两道逆向题的详细解析,包括去掉了NaCI这类不太常见的花,可实现F5阅读源码
0x00 日常查壳 ========= 给了三个文件,如题名一样,简单的文件系统,不过主逻辑在这个文件  0x01 分析simlefs ============== 浏览了一遍主函数无疑我们关心的是plantflag 随机数出来的v21和v22我们无法逆,不过关键点就是在参数1的那次进去  注意在这打开了我们的flag文件,然后进入了关键的函数,像参数为2调用了rand根本没法逆  观看这个Encode函数完全可逆,v4动调即得,密文通过观察image.flag获得(毕竟在这个文件找不到  0x02 GetData! ============= 设置好参数,起飞(开调  输入好参数  动调可以直接拿到v4的值和加密后的\*CTF{后的值,于是我们可以通过这个值去找密文  即可得到  0x03 GetFlag! ============= 拿这个密文直接解密即可得到flag 也可以观察整个文件发现有多出密文,从0x33000开始,每隔0x1000就会有一段密文,读取解密也可 ```php def Decrypt(encFLag): flag = "" for i in range(len(encFLag)): v5 = encFLag[i] v5 = (v5 >> 3) | (v5 << 5) & 0xFF v5 ^= 0xDE v5 = (v5 >> 4) | (v5 << 4) & 0xFF v5 ^= 0xED v5 = (v5 >> 5) | (v5 << 3) & 0xFF v5 ^= 0xBE; v5 = (v5 >> 6) | (v5 << 2) & 0xFF v5 ^= 0xEF; v5 = (v5 >> 7) | (v5 << 1) & 0xFF flag += chr(v5) return flag data = open('C:\\Users\\Pz\\Desktop\\STARCTF\\Simple File System\\image.flag', 'rb').read() encFlags = [list(data[0x33000 + i * 0x1000: 0x33000 + i * 0x1000 + 32]) for i in range(200)] for flag in encFlags: p = Decrypt(flag) if "*CTF" in p: print(flag) print(p) break # encFlags = open('C:\\Users\\Pz\\Desktop\\STARCTF\\Simple File System\\encFlag.flag', 'rb').read() # print(Decrypt(encFlags)) ``` GetFlag!  title: STARCTF2022-NaCI ----------------------- 0x00 日常查壳 ========= 无壳64位  0x01 分析主程序 ========== 我们shift + F12通过字符串查找引用,直接找到这个主函数  F5加密函数! ------- 然而这个函数的F5给不能用了,只能动调去理解这个程序,然而!我摸透了这其中的符文!  主要是两个点去修复,顺便去个无用指令 1. 奇怪的call ---------- 每个call都是这样实现的,再去看看retn  2. 奇怪的retn ---------- 可以发现每次jmp rdi就是一种回跳,而这不就是retn吗 PS:上面的call和retn不是同一组call+retn  所以源程序的特性 1. 把要回来的地址暂存入内存,再jmp到指定地址,同时jmp后还有段花指令 2. 当要跳回时,重新把地址放到rdi,再jmp跳回去 所以我们可以改成 1. 把jmp上面的所有操作去掉,直接改成call(push + jmp) 2. call后面的花全部去掉 3. 再把jmp rdi改成retn(pop + jmp),这时候直接用的是栈顶的地址其实就是回去的地址 以此为想法写个idapython ```python start = 0x807FEC0 end = 0x8080AD1 address = [0 for i in range(5)] callTarget = ["lea", "lea", "mov", "jmp"] retnTarget = ["lea", "mov", "and", "lea", "jmp"] def nop(s, e): while (s < e): patch_byte(s, 0x90) s += 1 def turnCall(s, e): # nop掉call之前的值 nop(s, e) patch_byte(e, 0xE8) # 把后面的花指令去掉 huaStart = next_head(e) huaEnd = next_head(huaStart) nop(huaStart, huaEnd) def turnRetn(s, e): nop(s, e) # 注意原来是jmp xxx # 所以前面nop掉一个 后面改成retn patch_byte(e, 0x90) patch_byte(e + 1, 0xC3) p = start while p < end: address[0] = p address[1] = next_head(p) address[2] = next_head(address[1]) address[3] = next_head(address[2]) address[4] = next_head(address[3]) for i in range(0, 4): if print_insn_mnem(address[i]) != callTarget[i]: break else: turnCall(address[0], address[3]) p = next_head(next_head(address[3])) continue for i in range(0, 5): if print_insn_mnem(address[i]) != retnTarget[i]: break else: turnRetn(address[0], address[4]) p = next_head(next_head(address[4])) continue p = next_head(p) ``` shift + F2打开idapython窗口,选择python,run一下  patch完保存一下,让ida重新解析  保存好再次打开这个文件,进入本函数,F5加密函数!  0x02 分析加密函数 =========== 可以通过创建结构体让程序很好理解,在我们不确定这些子项的具体函数,可以先用ida默认的  简单审计一下即可知道大概意思  异或加密 ---- 我们的输入分别小端序放入,经过一个可以逆的异或计算 ```C++ __int64 __fastcall XOR(__int64 input) { __int64 v1; // rbx __int64 v2; // r13 __int64 v3; // r15 _QWORD *v4; // r15 struct_v5 *v5; // r15 int v6; // ebx int v7; // ebx v4 = (v3 - 8); *v4 = v1; v5 = (v2 + (v4 - 56)); v5->input = input; v5->xorKey = sub_8080360(); v5->input1 = *(v2 + v5->input); v5->highFour = Big(HIDWORD(v5->input1)); // 高四位小端序放入 v5->lowFour = Big(v5->input1); // 低四位小端序放入 for ( v5->count = 0; v5->count <= 43; ++v5->count ) { v5->orgLowFour = v5->lowFour; v6 = ROL(v5->lowFour, 1); v7 = ROL(v5->lowFour, 8) & v6; v5->lowFour = v5->highFour ^ v7 ^ ROL(v5->lowFour, 2) ^ *(v2 + 4 * v5->count + v5->xorKey); v5->highFour = v5->orgLowFour; } v5->input1 = 0LL; v5->input1 = ((v5->input1 | v5->lowFour) << 32) | v5->highFour;// 低高互换 return v5->input1; } ``` key可以通过动调直接获取,再注意一下结尾的高低32位互换即可  魔改XTEA ------ 注意所改变的地方是 1. 轮数变了 2. delta数变了 ```C++ __int64 __fastcall XTEA(int count, __int64 a2) { __int64 v2; // r13 myst *v3; // r15 __int64 result; // rax v3[-1].t1 = count; // 轮数为传入的轮数,分别是2 4 8 16 *&v3[-2].v0 = a2; *&v3[-1].key = key; // key可以直接拿 v3[-1].v0 = *(v2 + *&v3[-2].v0); v3[-1].v1 = *(v2 + *&v3[-2].v0 + 4); v3[-1].sum = 0; v3[-1].delta = 0x10325476; // delta数变了 for ( v3[-1].t9 = 0; v3[-1].t9 < v3[-1].t1; ++v3[-1].t9 ) { v3[-1].v0 += (((v3[-1].v1 >> 5) ^ (16 * v3[-1].v1)) + v3[-1].v1) ^ (*(v2 + 4 * (v3[-1].sum & 3) + *&v3[-1].key) + v3[-1].sum); v3[-1].sum += v3[-1].delta; v3[-1].v1 += (((v3[-1].v0 >> 5) ^ (16 * v3[-1].v0)) + v3[-1].v0) ^ (*(v2 + 4 * ((v3[-1].sum >> 11) & 3) + *&v3[-1].key) + v3[-1].sum); } *(v2 + *&v3[-2].v0) = v3[-1].v0; result = v3[-1].v1; *(v2 + *&v3[-2].v0 + 4) = result; return result; } ``` 0x03 GetFlag ============ 直接从Check函数第一个参数拿到密文  EXP ```C #include <stdio.h> #include <stdint.h> #define SHL(x, n) ( ((x) & 0xFFFFFFFF) << n ) #define ROTL(x, n) ( SHL((x), n) | ((x) >> (32 - n)) ) unsigned int xorKey[44] = { 0x04050607, 0x00010203, 0x0C0D0E0F, 0x08090A0B, 0xCD3FE81B, 0xD7C45477, 0x9F3E9236, 0x0107F187, 0xF993CB81, 0xBF74166C, 0xDA198427, 0x1A05ABFF, 0x9307E5E4, 0xCB8B0E45, 0x306DF7F5, 0xAD300197, 0xAA86B056, 0x449263BA, 0x3FA4401B, 0x1E41F917, 0xC6CB1E7D, 0x18EB0D7A, 0xD4EC4800, 0xB486F92B, 0x8737F9F3, 0x765E3D25, 0xDB3D3537, 0xEE44552B, 0x11D0C94C, 0x9B605BCB, 0x903B98B3, 0x24C2EEA3, 0x896E10A2, 0x2247F0C0, 0xB84E5CAA, 0x8D2C04F0, 0x3BC7842C, 0x1A50D606, 0x49A1917C, 0x7E1CB50C, 0xFC27B826, 0x5FDDDFBC, 0xDE0FC404, 0xB2B30907 }; void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) { unsigned int i; uint32_t v0=v[0], v1=v[1], delta = 0x10325476, sum=delta*num_rounds; for (i=0; i < num_rounds; i++) { v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]); sum -= delta; v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[(sum & 3)]); } v[0]=v0; v[1]=v1; } void XorRol(uint32_t v[2]) { uint32_t encLow = v[1]; uint32_t encHigh = v[0]; uint32_t orgLow, orgHigh, v6, v7, v8; int i; for ( i = 43; i >= 0; i-- ) { orgLow = encHigh; v6 = ROTL(orgLow, 1); v7 = ROTL(orgLow, 8) & v6; v8 = v7 ^ ROTL(orgLow, 2); orgHigh = encLow ^ xorKey[i] ^ v8; encHigh = orgHigh; encLow = orgLow; } v[0] = orgLow; v[1] = orgHigh; } int main() { uint32_t v[] = { 0xFDF5C266, 0x7A328286, 0xCE944004, 0x5DE08ADC, 0xA6E4BD0A, 0x16CAADDC, 0x13CD6F0C, 0x1A75D936 }; uint32_t k[4] = { 0x03020100, 0x07060504, 0x0B0A0908, 0x0F0E0D0C }; int i, j; uint32_t teaData[8]; // uint32_t testData[] = { 0xD4C2E7AE, 0xD2E28713 }; // XorRol(testData); // printf("0x%X, 0x%X, ", testData[0], testData[1]); for ( i = 0; i <= 3; i++ ) { decipher(1 << (i + 1), v + i * 2, k); printf("0x%X, 0x%X, ", v[i * 2], v[i * 2 + 1]); teaData[i * 2] = v[i * 2]; teaData[i * 2 + 1] = v[i * 2 + 1]; } puts("\n"); for ( i = 0; i <= 3; i++ ) { XorRol(teaData + i * 2); // printf("0x%X, 0x%X, ", teaData[i * 2], teaData[i * 2 + 1]); } puts("\n"); unsigned char * t = (unsigned char *)&teaData; for ( i = 0; i < 32; i += 4 ) printf("%c%c%c%c", t[i + 3], t[i + 2], t[i + 1], t[i]); return 0; } ``` GetFlag! 
发表于 2022-04-27 09:32:17
阅读 ( 6048 )
分类:
其他
2 推荐
收藏
0 条评论
请先
登录
后评论
PZZZZ
2 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!