首页
社区
课程
招聘
[原创] KCTF 2019 Q4 第六题 一个支点
发表于: 2019-12-16 23:05 9027

[原创] KCTF 2019 Q4 第六题 一个支点

HHHso 活跃值
22
2019-12-16 23:05
9027

K:A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF 

D:7B E6 79 E0 89 E3 D3 6B 83 ED 81 EB 7A EA 5A 0A

从F5源码分析上,可以清楚知道,从D或U到K的逆向过程,可以先从D或U得到 x6587,Ks,wS,wX,wA,wT1,wT3,wST,wR这些关键观察量,然后再计算得到K,就简单许多。


(D)关键观察量突破口,x6587和Ks

参考下述代码

摘要:样例采用内存位置无关代码编程并带混淆,经过1401次对折叠代码的层层随机释放执行,最终对userkey进行逻辑运算(或解密)后与拓展的username对比。

找到一个支点就可以突防。

一、随机释放执行机制

代码都是内存位置无关代码,使用相对地址索引相关信息;
主要是将系统的栈克隆到全局区域,然后释放相应代码执行;
栈操作主要针对 NtTib中的StackBase和StackLimit操作,这是我们要的主要支点。

dt nt!_TEB32 nttib.
ntdll!_TEB32
   +0x000 NtTib  : 
      +0x000 ExceptionList : Uint4B
      +0x004 StackBase : Uint4B
      +0x008 StackLimit : Uint4B
      +0x00c SubSystemTib : Uint4B
      +0x010 FiberData : Uint4B
      +0x010 Version : Uint4B
      +0x014 ArbitraryUserPointer : Uint4B
      +0x018 Self   : Uint4B

dt nt!_TEB32 nttib.
ntdll!_TEB32
   +0x000 NtTib  : 
      +0x000 ExceptionList : Uint4B
      +0x004 StackBase : Uint4B
      +0x008 StackLimit : Uint4B
      +0x00c SubSystemTib : Uint4B
      +0x010 FiberData : Uint4B
      +0x010 Version : Uint4B
      +0x014 ArbitraryUserPointer : Uint4B
      +0x018 Self   : Uint4B

以下是主程序入口的代码逻辑,从中我们可以看到它是如何迁移和切换栈的
(1)通过VirtualProtect开放读写属性,用于释放后擦除;
(2)通过浮点寄存器st0保存image.20hww处的随机执行区域基址;
(3)将系统栈内容复制到全局区域的m_stack,同时更新esp和ebp相对全局栈;
(4)最后给系统装上自己的全局栈。
main
-------------esp
.-04hww tmpvar
  VirtualProtect from main.start.0x401210 to 0x505678:=start+0x104468
  VirtualProtect from base.0x400000 to 0x401000:=start+0x1000
  st0 = image.20hww 0x508000
  m_stack{
    m_StackBase.bottom = NtTib.StackBase
    m_StackLimit.top   = NtTib.StackLimit
    image.24hww = m_StackSize = m_StackLimit-m_StackBase = 0x3000
  }
  m_esp,m_ebp
  g_esp = g_StackLimit+(m_esp-m_StackLimit)
  g_ebp = g_StackLimit+(m_ebp-m_StackLimit)
  g_stack{align:4;g_StackLimit,g_StackBase;m_StackSize}:=m_stack
  NtTib.StackBase = g_StackBase

main
-------------esp
.-04hww tmpvar
  VirtualProtect from main.start.0x401210 to 0x505678:=start+0x104468
  VirtualProtect from base.0x400000 to 0x401000:=start+0x1000
  st0 = image.20hww 0x508000
  m_stack{
    m_StackBase.bottom = NtTib.StackBase
    m_StackLimit.top   = NtTib.StackLimit
    image.24hww = m_StackSize = m_StackLimit-m_StackBase = 0x3000
  }
  m_esp,m_ebp
  g_esp = g_StackLimit+(m_esp-m_StackLimit)
  g_ebp = g_StackLimit+(m_ebp-m_StackLimit)
  g_stack{align:4;g_StackLimit,g_StackBase;m_StackSize}:=m_stack
  NtTib.StackBase = g_StackBase

启动时核心代码区间如下,其中username和userkey位于区间中
c_src = 0x4014E2
  username: 4FF49B
  userkey: 4FF65C
c_size = 0x104196
c_end = c_src+c_size 0x505678

c_src = 0x4014E2
  username: 4FF49B
  userkey: 4FF65C
c_size = 0x104196
c_end = c_src+c_size 0x505678

随机代码执行区间如下,代码会根据rdtsc指令结果随机释放到该区间位置
cs_s = st0 0x508000                   随机代码执行区起始基址(非执行入口),不变
cs_e = cs_s + 0x6400000               随机代码执行区尾部,不变
rdtsc_mod = rdtsc % 0x6400000         随机代码释放点偏移量,也是执行入口,
 
r_s = cs_s+rdtsc_mod                 解密释放代码到随机地址,也作为入口,每次释放由于rdtsc计算的不同而不同
r_e = r_s + c_size

cs_s = st0 0x508000                   随机代码执行区起始基址(非执行入口),不变
cs_e = cs_s + 0x6400000               随机代码执行区尾部,不变
rdtsc_mod = rdtsc % 0x6400000         随机代码释放点偏移量,也是执行入口,
 
r_s = cs_s+rdtsc_mod                 解密释放代码到随机地址,也作为入口,每次释放由于rdtsc计算的不同而不同
r_e = r_s + c_size

每次释放代码都会伴随栈的复制迁移和挂靠,也会”清除“之前的代码(全设为C3指令)

跟随一两层释放执行过程,是条不归路,unicorn伴keystone或miasm模拟执行是另一条牛刀女装路。
还是看看有没其他着力点。

二、着力点
由于代码和数据(包括username和userkey)每次都随机迁移到执行区,所以没有写自动化跟踪脚本辅助下,人工追踪的可行性没法预期。
(1)尝试
由于最后输出错误提示,我们试着断最后的prinf来回溯,看看有没发现,最好其调用点附件的上下文是明文对比,希望是美好的,万一真是呢。


很好,轻松到达错误的终点,赶紧回头看下返回地址上下文处有没明文对比userkey,如下

(A)prinf的返回点位于0x6D12DCD,我们可以看到其参数的传递,无疑就是错误信息内容地址。
(B)我们同时注意到前面还有个printf,且有个jne跳转,去看下上面的printf的参数内容。
hex(0x6012D65-0x401122+0x404B47)
的确如所料,'0x601678a'是正确信息的内容地址,当然,我们这时与其他敏感信息区擦肩而过(我们现在假装没看到下图后面方框区域内容)
正确提示后面的KCTF应该是看到的,这时拓展username,16字节,也假装没看到。

当我们看到判断的前面"C3C3C3C3"时,心凉半截,执行过的代码被擦除了!

问题变成了如何赶在擦除之前拿到代码,又回到了起初的怎么跟踪这N层释放,毕竟我们要最后一层的代码。
看到群了的”女装“,我们应该有所启示————栈的挂靠

三、不变应万变
由于每次释放前都需要先复制迁移栈并设置挂靠,所以我们选择了TEB中StackBase作为我们的硬件断点位置
如下图,我们拿到TEB内存位置

然后我们在StackBase处设置我们的硬件写断点

然后一路女装狂奔(直接点的,保持个优雅的姿势,按这F9一动不动,跟它耗上,调试器是x64dbg)
最终如下图,总共触发了1401次。

OK,马上再来一个更优雅的姿势,跑到1401次时停止 (如果忘了清除之前的硬件断点,不妨命令行里清除下bphc)
如下图,我们停在第1401次硬件断点处

剩下就是一路F7单步进入了,F8很容易跑飞,飞了就只能重来。


来到下面这部分,看到老相识lodsb,和后面一堆猛操作,就知道是解密代码的业务逻辑,

记下代码地址(0x53c3A8C) 和大小(大小 0x3b33+1= 0x3b34 ),并在后面的循环结束处,断下,等解密完后,dump出代码

来到断点后,代码已解密完,我们可以 运行命令dump出来:
savedata "C:\CTF2019\Q4\CTF06\last_3B34.dec",0x53c3A8C,0x3b34 
savedata "C:\CTF2019\Q4\CTF06\last_3B34.dec",0x53c3A8C,0x3b34 
参考附件内容。继续F7,于是我们碰见老朋友rdtsc和0x6400000,这些都是前面完成栈操作后,实现代码迁移到随机执行区,
最后通过call eax 转移执行,这部分代码才是我们的主角,
其中ecx=0x53C3B17,大小位0x9E56,同样,我们dump出来备用
savedata "C:\CTF2019\Q4\CTF06\last_9e56.dec",0x66988A,0x9e56

savedata "C:\CTF2019\Q4\CTF06\last_9e56.dec",0x66988A,0x9e56




四、主角
由call eax跳转后,没几条指令就进入关键步骤,开头是擦除之前的代码(有兴趣也可以在擦除前dump出来)
然后就是计算esi地址,存放username和userkey的地方,曾记否,我们在前面曾与它擦肩而过
从 push ebp;mov ebp,esp开始是完整函数的序言操作(enter),这个很重要,能不能有效去掉混淆,还原核心代码逻辑就靠它。


在进入序言之前,我们看下计算出的esi指向的内容,特别是序言开始后,操作的esi+0x1B0这些,就是我们输入的userkey


五、IDA表演时间

为了方便,我们就近取材,在主函数里,第一次解密0x4016AC出的代码位置,
我们将我们最后dump出的核心代码加载到0x4016AC处,
IDAPython执行 loadfile(r"C:\CTF2019\Q4\CTF06\last_9e56.dec",0,0x4016AC,0x9e56)

IDAPython执行 loadfile(r"C:\CTF2019\Q4\CTF06\last_9e56.dec",0,0x4016AC,0x9e56)




【四中】的IDA版,其中几处,简单运用了混淆清除,多此一举,有兴趣可以参考后面内容。



重建核心函数基本操作就是,按”U“(undefine push ebp前面的代码),从序言开始一直使用”C“ (code)反汇编。
一直到尾部;其实到pop ebp实际也已经可以,可以后面修改ret就行,直接全面反编译也可以,影响不大。

函数的尾部将变换后(或解密出)的userkey与拓展username比较,相等正确,比较长度时0x10



其中看到121212这些是最初使用的输入的测试userkey=12121212121212121212121212121212 ,如果使用A0A1A2A3A4A5A6A7A8A9AAABACADAEAF,dump出来加载后此处看到的就是A0A1A2A3A4A5A6A7A8A9AAABACADAEAF,但算法换算(解密)后,如果正确就会和拓展的username一样,
其中username是覆盖补齐的16字节

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

最后于 2019-12-17 10:44 被HHHso编辑 ,原因:
上传的附件:
收藏
免费 6
支持
分享
最新回复 (4)
雪    币: 11705
活跃值: (975)
能力值: ( LV12,RANK:779 )
在线值:
发帖
回帖
粉丝
2
分析精彩,文章排版也精美,典范之作。学习一个。
2019-12-17 20:50
0
雪    币: 8447
活跃值: (5041)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
3
学习了
2019-12-20 13:27
0
雪    币: 189
活跃值: (154)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
4
太牛了
2019-12-20 22:11
0
雪    币: 0
活跃值: (1003)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
2020-11-30 09:15
0
游客
登录 | 注册 方可回帖
返回
//