【STARCTF2022】Simple File System & NaCI(去花F5!

关于*CTF两道逆向题的详细解析,包括去掉了NaCI这类不太常见的花,可实现F5阅读源码

0x00 日常查壳

给了三个文件,如题名一样,简单的文件系统,不过主逻辑在这个文件

image-20220418192736295.png

0x01 分析simlefs

浏览了一遍主函数无疑我们关心的是plantflag

随机数出来的v21和v22我们无法逆,不过关键点就是在参数1的那次进去

image-20220418193323238.png

注意在这打开了我们的flag文件,然后进入了关键的函数,像参数为2调用了rand根本没法逆

image-20220418193926873.png

观看这个Encode函数完全可逆,v4动调即得,密文通过观察image.flag获得(毕竟在这个文件找不到

image-20220418193939286.png

0x02 GetData!

设置好参数,起飞(开调

image-20220418194139609.png

输入好参数

image-20220418194524815.png

动调可以直接拿到v4的值和加密后的*CTF{后的值,于是我们可以通过这个值去找密文

image-20220418195115666.png

即可得到

image-20220418195214843.png

0x03 GetFlag!

拿这个密文直接解密即可得到flag

也可以观察整个文件发现有多出密文,从0x33000开始,每隔0x1000就会有一段密文,读取解密也可

  1. def Decrypt(encFLag):
  2. flag = ""
  3. for i in range(len(encFLag)):
  4. v5 = encFLag[i]
  5. v5 = (v5 >> 3) | (v5 << 5) &amp; 0xFF
  6. v5 ^= 0xDE
  7. v5 = (v5 >> 4) | (v5 << 4) &amp; 0xFF
  8. v5 ^= 0xED
  9. v5 = (v5 >> 5) | (v5 << 3) &amp; 0xFF
  10. v5 ^= 0xBE;
  11. v5 = (v5 >> 6) | (v5 << 2) &amp; 0xFF
  12. v5 ^= 0xEF;
  13. v5 = (v5 >> 7) | (v5 << 1) &amp; 0xFF
  14. flag += chr(v5)
  15. return flag
  16. data = open('C:\\Users\\Pz\\Desktop\\STARCTF\\Simple File System\\image.flag', 'rb').read()
  17. encFlags = [list(data[0x33000 + i * 0x1000: 0x33000 + i * 0x1000 + 32]) for i in range(200)]
  18. for flag in encFlags:
  19. p = Decrypt(flag)
  20. if "*CTF" in p:
  21. print(flag)
  22. print(p)
  23. break
  24. # encFlags = open('C:\\Users\\Pz\\Desktop\\STARCTF\\Simple File System\\encFlag.flag', 'rb').read()
  25. # print(Decrypt(encFlags))

GetFlag!

image-20220418195454640.png

title: STARCTF2022-NaCI

0x00 日常查壳

无壳64位

image-20220419152650981.png

0x01 分析主程序

我们shift + F12通过字符串查找引用,直接找到这个主函数

image-20220419153100353.png

F5加密函数!

然而这个函数的F5给不能用了,只能动调去理解这个程序,然而!我摸透了这其中的符文!

image-20220419154430259.png

主要是两个点去修复,顺便去个无用指令

1. 奇怪的call

每个call都是这样实现的,再去看看retn

image-20220419153452942.png

2. 奇怪的retn

可以发现每次jmp rdi就是一种回跳,而这不就是retn吗

PS:上面的call和retn不是同一组call+retn

image-20220419153623495.png

所以源程序的特性

  1. 把要回来的地址暂存入内存,再jmp到指定地址,同时jmp后还有段花指令
  2. 当要跳回时,重新把地址放到rdi,再jmp跳回去

所以我们可以改成

  1. 把jmp上面的所有操作去掉,直接改成call(push + jmp)
  2. call后面的花全部去掉
  3. 再把jmp rdi改成retn(pop + jmp),这时候直接用的是栈顶的地址其实就是回去的地址

以此为想法写个idapython

  1. start = 0x807FEC0
  2. end = 0x8080AD1
  3. address = [0 for i in range(5)]
  4. callTarget = ["lea", "lea", "mov", "jmp"]
  5. retnTarget = ["lea", "mov", "and", "lea", "jmp"]
  6. def nop(s, e):
  7. while (s < e):
  8. patch_byte(s, 0x90)
  9. s += 1
  10. def turnCall(s, e):
  11. # nop掉call之前的值
  12. nop(s, e)
  13. patch_byte(e, 0xE8)
  14. # 把后面的花指令去掉
  15. huaStart = next_head(e)
  16. huaEnd = next_head(huaStart)
  17. nop(huaStart, huaEnd)
  18. def turnRetn(s, e):
  19. nop(s, e)
  20. # 注意原来是jmp xxx
  21. # 所以前面nop掉一个 后面改成retn
  22. patch_byte(e, 0x90)
  23. patch_byte(e + 1, 0xC3)
  24. p = start
  25. while p < end:
  26. address[0] = p
  27. address[1] = next_head(p)
  28. address[2] = next_head(address[1])
  29. address[3] = next_head(address[2])
  30. address[4] = next_head(address[3])
  31. for i in range(0, 4):
  32. if print_insn_mnem(address[i]) != callTarget[i]:
  33. break
  34. else:
  35. turnCall(address[0], address[3])
  36. p = next_head(next_head(address[3]))
  37. continue
  38. for i in range(0, 5):
  39. if print_insn_mnem(address[i]) != retnTarget[i]:
  40. break
  41. else:
  42. turnRetn(address[0], address[4])
  43. p = next_head(next_head(address[4]))
  44. continue
  45. p = next_head(p)

shift + F2打开idapython窗口,选择python,run一下

image-20220419154550063.png

patch完保存一下,让ida重新解析

image-20220419154355090.png

保存好再次打开这个文件,进入本函数,F5加密函数!

image-20220419154720870.png

0x02 分析加密函数

可以通过创建结构体让程序很好理解,在我们不确定这些子项的具体函数,可以先用ida默认的

image-20220419155017422.png

简单审计一下即可知道大概意思

image-20220419155448997.png

异或加密

我们的输入分别小端序放入,经过一个可以逆的异或计算

  1. __int64 __fastcall XOR(__int64 input)
  2. {
  3. __int64 v1; // rbx
  4. __int64 v2; // r13
  5. __int64 v3; // r15
  6. _QWORD *v4; // r15
  7. struct_v5 *v5; // r15
  8. int v6; // ebx
  9. int v7; // ebx
  10. v4 = (v3 - 8);
  11. *v4 = v1;
  12. v5 = (v2 + (v4 - 56));
  13. v5->input = input;
  14. v5->xorKey = sub_8080360();
  15. v5->input1 = *(v2 + v5->input);
  16. v5->highFour = Big(HIDWORD(v5->input1)); // 高四位小端序放入
  17. v5->lowFour = Big(v5->input1); // 低四位小端序放入
  18. for ( v5->count = 0; v5->count <= 43; ++v5->count )
  19. {
  20. v5->orgLowFour = v5->lowFour;
  21. v6 = ROL(v5->lowFour, 1);
  22. v7 = ROL(v5->lowFour, 8) &amp; v6;
  23. v5->lowFour = v5->highFour ^ v7 ^ ROL(v5->lowFour, 2) ^ *(v2 + 4 * v5->count + v5->xorKey);
  24. v5->highFour = v5->orgLowFour;
  25. }
  26. v5->input1 = 0LL;
  27. v5->input1 = ((v5->input1 | v5->lowFour) << 32) | v5->highFour;// 低高互换
  28. return v5->input1;
  29. }

key可以通过动调直接获取,再注意一下结尾的高低32位互换即可

image-20220419160146104.png

魔改XTEA

注意所改变的地方是

  1. 轮数变了
  2. delta数变了
  1. __int64 __fastcall XTEA(int count, __int64 a2)
  2. {
  3. __int64 v2; // r13
  4. myst *v3; // r15
  5. __int64 result; // rax
  6. v3[-1].t1 = count; // 轮数为传入的轮数,分别是2 4 8 16
  7. *&amp;v3[-2].v0 = a2;
  8. *&amp;v3[-1].key = key; // key可以直接拿
  9. v3[-1].v0 = *(v2 + *&amp;v3[-2].v0);
  10. v3[-1].v1 = *(v2 + *&amp;v3[-2].v0 + 4);
  11. v3[-1].sum = 0;
  12. v3[-1].delta = 0x10325476; // delta数变了
  13. for ( v3[-1].t9 = 0; v3[-1].t9 < v3[-1].t1; ++v3[-1].t9 )
  14. {
  15. v3[-1].v0 += (((v3[-1].v1 >> 5) ^ (16 * v3[-1].v1)) + v3[-1].v1) ^ (*(v2 + 4 * (v3[-1].sum &amp; 3) + *&amp;v3[-1].key)
  16. + v3[-1].sum);
  17. v3[-1].sum += v3[-1].delta;
  18. v3[-1].v1 += (((v3[-1].v0 >> 5) ^ (16 * v3[-1].v0)) + v3[-1].v0) ^ (*(v2
  19. + 4 * ((v3[-1].sum >> 11) &amp; 3)
  20. + *&amp;v3[-1].key)
  21. + v3[-1].sum);
  22. }
  23. *(v2 + *&amp;v3[-2].v0) = v3[-1].v0;
  24. result = v3[-1].v1;
  25. *(v2 + *&amp;v3[-2].v0 + 4) = result;
  26. return result;
  27. }

0x03 GetFlag

直接从Check函数第一个参数拿到密文

image-20220419161345315.png

EXP

  1. #include <stdio.h>
  2. #include <stdint.h>
  3. #define SHL(x, n) ( ((x) &amp; 0xFFFFFFFF) << n )
  4. #define ROTL(x, n) ( SHL((x), n) | ((x) >> (32 - n)) )
  5. unsigned int xorKey[44] = {
  6. 0x04050607, 0x00010203, 0x0C0D0E0F, 0x08090A0B, 0xCD3FE81B, 0xD7C45477, 0x9F3E9236, 0x0107F187,
  7. 0xF993CB81, 0xBF74166C, 0xDA198427, 0x1A05ABFF, 0x9307E5E4, 0xCB8B0E45, 0x306DF7F5, 0xAD300197,
  8. 0xAA86B056, 0x449263BA, 0x3FA4401B, 0x1E41F917, 0xC6CB1E7D, 0x18EB0D7A, 0xD4EC4800, 0xB486F92B,
  9. 0x8737F9F3, 0x765E3D25, 0xDB3D3537, 0xEE44552B, 0x11D0C94C, 0x9B605BCB, 0x903B98B3, 0x24C2EEA3,
  10. 0x896E10A2, 0x2247F0C0, 0xB84E5CAA, 0x8D2C04F0, 0x3BC7842C, 0x1A50D606, 0x49A1917C, 0x7E1CB50C,
  11. 0xFC27B826, 0x5FDDDFBC, 0xDE0FC404, 0xB2B30907
  12. };
  13. void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
  14. unsigned int i;
  15. uint32_t v0=v[0], v1=v[1], delta = 0x10325476, sum=delta*num_rounds;
  16. for (i=0; i < num_rounds; i++) {
  17. v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) &amp; 3]);
  18. sum -= delta;
  19. v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[(sum &amp; 3)]);
  20. }
  21. v[0]=v0; v[1]=v1;
  22. }
  23. void XorRol(uint32_t v[2])
  24. {
  25. uint32_t encLow = v[1];
  26. uint32_t encHigh = v[0];
  27. uint32_t orgLow, orgHigh, v6, v7, v8;
  28. int i;
  29. for ( i = 43; i >= 0; i-- )
  30. {
  31. orgLow = encHigh;
  32. v6 = ROTL(orgLow, 1);
  33. v7 = ROTL(orgLow, 8) &amp; v6;
  34. v8 = v7 ^ ROTL(orgLow, 2);
  35. orgHigh = encLow ^ xorKey[i] ^ v8;
  36. encHigh = orgHigh;
  37. encLow = orgLow;
  38. }
  39. v[0] = orgLow; v[1] = orgHigh;
  40. }
  41. int main()
  42. {
  43. uint32_t v[] = { 0xFDF5C266, 0x7A328286, 0xCE944004, 0x5DE08ADC, 0xA6E4BD0A, 0x16CAADDC, 0x13CD6F0C, 0x1A75D936 };
  44. uint32_t k[4] = { 0x03020100, 0x07060504, 0x0B0A0908, 0x0F0E0D0C };
  45. int i, j;
  46. uint32_t teaData[8];
  47. // uint32_t testData[] = { 0xD4C2E7AE, 0xD2E28713 };
  48. // XorRol(testData);
  49. // printf("0x%X, 0x%X, ", testData[0], testData[1]);
  50. for ( i = 0; i <= 3; i++ )
  51. {
  52. decipher(1 << (i + 1), v + i * 2, k);
  53. printf("0x%X, 0x%X, ", v[i * 2], v[i * 2 + 1]);
  54. teaData[i * 2] = v[i * 2];
  55. teaData[i * 2 + 1] = v[i * 2 + 1];
  56. }
  57. puts("\n");
  58. for ( i = 0; i <= 3; i++ )
  59. {
  60. XorRol(teaData + i * 2);
  61. // printf("0x%X, 0x%X, ", teaData[i * 2], teaData[i * 2 + 1]);
  62. }
  63. puts("\n");
  64. unsigned char * t = (unsigned char *)&amp;teaData;
  65. for ( i = 0; i < 32; i += 4 )
  66. printf("%c%c%c%c", t[i + 3], t[i + 2], t[i + 1], t[i]);
  67. return 0;
  68. }

GetFlag!

image-20220419161304529.png

  • 发表于 2022-04-27 09:32:17
  • 阅读 ( 6363 )
  • 分类:其他

0 条评论

PZZZZ
PZZZZ

2 篇文章

站长统计