首页
社区
课程
招聘
[原创]KCTF 2023 第三题 秘密计划 解题过程
2023-9-6 14:00 8369

[原创]KCTF 2023 第三题 秘密计划 解题过程

xym 活跃值
4
2023-9-6 14:00
8369
1
2
3
4
下载后发现exe文件本身还挺大的,除了一个txt说明文件外还带了一个code.dat数据文件。首先查看exe文件本身的构成,在资源节发现一个zip包,dump下来后发现里面是一堆xml和image图片,是程序的主界面相关资源。里面没有代码之类的感兴趣的东西。
直接运行发现需要输入32位字符串才有验证的提示,而且输入无法多于32位,因此随意输入一组字符串进行测试。
ida打开后发现字符串没有加密,里面可以搜索到code.dat,并定位到sub_40DA90这个函数。加载调试后发现会报异常,根据报异常的线程反溯到sub_405F10函数。该函数调用CreateTimerQueueTimer启动反调试,而调用该函数前需要判断是否成功调用CreateTimerQueue函数,因此修改CreateTimerQueue的返回值就可以实现去掉反调试。
去掉反调试后继续跟踪sub_40DA90,发现调用该函数的代码下方启动了虚拟机并运行了一段arm代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
uc_mem_map((int)uc, 0, 0, (int)sub_A00000, 7, (int)lpAddress);
            v21 = v41;
            uc_mem_write((int)uc, 0x43000i64, (int)v40, v41);
            v22 = Src;
            if ( v56 >= 0x10 )
              v22 = (void **)Src[0];
            uc_mem_write((int)uc, 0x4033i64, (int)v22, v55);
            if ( uc_emu_start(a1, v17, (int)uc, 0x43000, 0, v21 + 0x43000, 0, 0i64, 0) )
            {
              v9 = v47;
              v23 = (void (__stdcall *)(HWND, UINT, WPARAM, LPARAM))SendMessageW;
            }
            else
            {
              memset(wParam, 0, sizeof(wParam));
              uc_mem_read((int)uc, 0x14390u, 0, (LPBYTE)wParam, 0x20u);
              v9 = v47;
              v23 = (void (__stdcall *)(HWND, UINT, WPARAM, LPARAM))SendMessageW;
              v48 = 1;                          // 虚拟机执行成功
              SendMessageW((HWND)v47[7], 0x47Eu, (WPARAM)wParam, 0);// 保存结果
            }
            sub_44E300((int)uc, 0, 0, (int)sub_A00000);
            uc_close((char *)uc);
1
该虚拟机内存分为3段,其中0x43000地址处为代码,保持不变,0x4033地址处为输入,和界面上的输入相关,而且变化很大,0x14390地址处为输出,运行后读出0x20字节大小并通过0x47E消息发送给主程序。继续分析虚拟机的代码。
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
void __noreturn sub_4363C()
{
  int v0; // r1
  char *v1; // r0
  const char *v2; // r2
  int v3; // r5
  int v4; // r3
  int v5; // r4
 
  v0 = 12;
  while ( 1 )
  {
    v1 = "e1b85b27d6bcb05846c18e6a48f118e89f0c0587140de9fb3359f8370d0dba08";
    v2 = "4fc82b26aecb47d2868c4efbe3581732a3e7cbcc6c2efb32062c08170a05eeb8";
    v3 = 0;
    --v0;
    do
    {
      v4 = *(_DWORD *)v1;
      v5 = *(_DWORD *)v2;
      if ( ++v3 >= 16 )
      {
        if ( "4fc82b26aecb47d2868c4efbe3581732a3e7cbcc6c2efb32062c08170a05eeb8" == "6749dae311865d64db83d5ae75bac3c9e36b3aa6f24caba655d9682f7f071023" )
        {
          dword_14390 = 1;
          dword_143A8 = 1;
        }
LABEL_9:
        JUMPOUT(0x43754);
      }
      v1 += 4;
      v2 += 4;
    }
    while ( v4 == v5 );
    if ( !v0 )
      goto LABEL_9;
  }
}
1
2
3
4
代码内包含了24段字符串,并且分成了12组,每组两个。虽然貌似将输入和每组的第一个字符串进行了循环比较,但是在结尾处还是确认了输入字符串必须为“6749dae311865d64db83d5ae75bac3c9e36b3aa6f24caba655d9682f7f071023”才会将输出的dword_14390和dword_143A8置1。也就是说这段代码的实际功能是判断输入是否为指定字符串。
于是反推输入字符串的来源,根据输入字符串的硬件断点,将算法聚焦到了sub_436900函数。该函数在对输入进行运算时还使用到了2个固定数组,通过分析,可以确定这是一个sha256函数。而arm的输入“e1b85b27d6bcb05846c18e6a48f118e89f0c0587140de9fb3359f8370d0dba08”正是界面输入“12345678901234567890123456789012”的sha256值。
感觉这个题目从逆向题转向了HASH碰撞方向。于是对照sha256的源码和相关文章进行分析,发现32字节的sha256基本不可逆,因此很快放弃了这条思路。判断答案只能从exe自身寻找(或者去付费HASH网站寻找?)。
重新回到arm代码中提取的24个字符串,感觉到目标字符串在字符串数组中的位置有些奇怪,而且分组分得有些刻意。于是对每个字符串分别进行了正向和反向的sha256。
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
字符串                                                           对应注释
e0bc614e4fd035a488619799853b075143deea596c477b8dc077e309c0fe42e9    sha256(sha256(1))
6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b    sha256(1)
d8bdf9a0cb27a193a1127de2924b6e5a9e4c2d3b3fe42e935e160c011f3df1fc    sha256(sha256(2))
d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35    sha256(2)
6749dae311865d64db83d5ae75bac3c9e36b3aa6f24caba655d9682f7f071023    目标字符串
ea96b41c1f9365c2c9e6342f5faaeab2a44471efe1e65a2356a974646d2588fd    未知
5b65712d565c1551340998102d418ceccb35db8dbfb45f9041c4cae483d8717b    sha256(sha256(3))
4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce    sha256(3)
033c339a7975542785be7423a5b32fa8047813689726214143cdd7939747709c    sha256(sha256(4))
4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a    sha256(4)
c81d40dbeed369f1476086cf882dd36bf1c3dc35e07006f0bec588b983055487    sha256(sha256(5))
ef2d127de37b942baad06145e54b0c619a1f22327b2ebbcfbec78f5564afe39d    sha256(5)
9e259b7f6b4c741937a96a9617b3e6b84e166ff6e925e414e7b72936f5a2a51f    sha256(sha256(6))
e7f6c011776e8db7cd330b54174fd76f7d0216b612387a5ffcfb81e6f0919683    sha256(6)
1048f03db5d45f654b955eae20d84b72673680fb13b318e7da22e8dce58df21c    sha256(sha256(7))
7902699be42c8a8e46fbbb4501726517e86b22c56a189f7625a6da49081b2451    sha256(7)
8f0703d406fdb0ea8011d5de342c3aca62214758a8a2b5b8a4e9f1c8c6c42462    sha256(sha256(8))
2c624232cdd221771294dfbb310aca000a0df6ac8b66b696d90ef06fdefb64a3    sha256(8)
182b32359558eb092511b7166867503ddd83fbe5b42f2545e1903016e721393d    sha256(sha256(9))
19581e27de7ced00ff1ce50b2047e7a567c76b1cbaebabe5ef03f7c3017bb5b7    sha256(9)
fe5f0fc640cbbc113406f042d08cc60ba784c775f7c3299985665323c5fbcdc4    sha256(sha256(10))
4a44dc15364204a80fe80e9039455cc1608281820fe2b24f1e5233ade6af1dd5    sha256(10)
4fc82b26aecb47d2868c4efbe3581732a3e7cbcc6c2efb32062c08170a05eeb8    sha256(11)
1ba586c0b89202f7307b61f1229330978a843afc98589ffc6a62f209225d3528    sha256(sha256(11))
1
2
3
根据分析结果可以发现每组字符串之间均有一个sha256的关系,因此可以推测目标字符串和未知的字符串字节也存在相应关系,直接计算sha256("ea96b41c1f9365c2c9e6342f5faaeab2a44471efe1e65a2356a974646d2588fd")发现结果不符合目标字符串,考虑到输入只有32个字节,因此将字符串截断后发现sha256("ea96b41c1f9365c2c9e6342f5faaeab2")即为结果。输入程序输入框可得验证成功的提示。
提交flag时发现提交ea96b41c1f9365c2c9e6342f5faaeab2不正确,最后提交完整的字符串ea96b41c1f9365c2c9e6342f5faaeab2a44471efe1e65a2356a974646d2588fd显示成功。
ps:虽然直接把flag放到程序内存中也不是不可以,但是Reverse题最后变得有点像Misc了。全题通篇对flag只有sha256一种运算,是否可以考虑在sha256的算法前后多加点变化呢?

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2023-9-7 12:20 被kanxue编辑 ,原因:
收藏
免费 1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回