英特尔PT(处理器跟踪)是一项最新的英特尔CPU的技术,Intel Skylake和更高版本的CPU型号都已经具有了此功能。你可以使用触发和过滤功能在指令级别跟踪代码执行。通过本文,我们希望探索该技术在漏洞利用分析中的实际应用。
0x01 在Windows上使用Intel PT
在Windows上记录Intel PT的信息主要可以使用三种方法。
[WindowsIntelPT](https://github.com/intelpt/WindowsIntelPT) [WinIPT](https://github.com/ionescu007/winipt) [Intel®DebugExtensionsforWinDbg*forIntel®ProcessorTrace](https://software.intel.com/en-us/intel-system-studio-2019-windbg-pt-user-guide-windows-introducing-the-intel-debug-extensions-for-windbg-for-intel-processor-trace)
要分析记录的数据包,可以使用Intel的libipt,Libipt是可以解码Intel PT数据包的标准库,它提供了ptdump和ptexd基本工具。
https://github.com/intel/libipt
Instruction Source
Intel PT仅记录控制流更改。要解码Intel PT跟踪,我们需要执行指令的image文件。如果在代码执行的某些区域没有匹配的image,则可能会丢失一些执行信息。在没有静态image文件可用的JIT代码执行中,可能会发生这种情况,甚至shellcode都很难跟踪,因为shellcode指令仅存在于内存中。
因为Intel PT不会保存指令字节或内存内容,所以你需要为每个IP(指令指针)提供指令字节。例如,下面显示了ptxed命令的工作方式。
记录压缩
在现实世界中使用Intel PT的一个障碍是处理Intel PT跟踪文件所需的大量CPU时间,跟踪文件已被压缩,在使用之前都需要先对其进行解压缩,Libipt库可用于解码过程,但它更多是单线程操作。
方法
类似于LBR,英特尔PT通过记录分支来工作。在运行时,当CPU遇到任何分支指令(如“ je”,“ call”,“ ret”)时,它将记录对该分支采取的操作。使用附加跳转指令,它将使用1位记录已占用(T)或未占用(NT)。通过间接调用和跳转,它将记录目标地址。对于无条件分支(如跳转或调用),它不会记录更改,因为你可以从指令中推断出目标跳转地址。将使用FUP,TIP,TIP.PGE或TIP.PGD数据包之一将要记录的IP(指令指针)与上次IP记录进行比较。如果地址字节的高位部分重叠,则那些匹配的字节将在当前数据包中被解析。同样,对于临近返回指令,如果返回目标是调用指令的下一条指令,
数据包
IPT压缩中使用的数据包的说明可以从《英特尔®64和IA-32体系结构软件开发人员手册》中找到。
https://software.intel.com/en-us/articles/intel-sdm
有许多数据包用于实现记录机制,但是,很少有重要的数据包类型起主要作用。
PSB数据包
PSB数据包用作跟踪数据包解码的同步点。它是跟踪日志中的边界,在其中可以独立执行减压过程而没有任何副作用,此偏移在libipt库代码中称为“同步偏移”,因为这是跟踪文件中的偏移,你可以在其中安全地开始解码以下数据包。
TIP (Target IP)
TIP数据包指示目标IP,该信息可用作指令指针的基地址。
TNT (Taken Not-Taken)
TNT数据包用于指示是否进行条件转移。因为可以从过程映像中推断出那些流量控制,所以不会记录任何无条件的分支跳转。
总体而言,解压缩过程如下图所示。这更多是过于简化的视图,但是它可以向你展示解压缩的工作原理,IntelPT日志可用于重建完整的指令执行并在指令字节的帮助下控制流的变化,没有指令字节,它仅给出完整指令执行的部分视图。
跟踪日志
这是IPT跟踪日志的一个片段,该片段使用libipt中的ptdump转换为文本形式。它以PSB数据包开头,该位置指示你可以安全地解码后续数据包的位置,目前有一些与填充和定时相关的数据包可以忽略。
000000000000001cpsb 000000000000002cpad 000000000000002dpad 000000000000002epad
在偏移量3db处,有一个tip.pge数据包。这意味着指令指针位于数据包指示的位置00007ffbb7d63470。
... 00000000000003dbtip.pge3:00007ffbb7d63470 00000000000003e2pad 00000000000003e3pad
从过程映像中,我们可以确定tip.pge的地址00007ffbb7d63470指向以下说明。
seg000:00007FFBB7D63470movrcx,[rsp+20h] seg000:00007FFBB7D63475movedx,[rsp+28h] seg000:00007FFBB7D63479movr8d,[rsp+2Ch] seg000:00007FFBB7D6347Emovrax,gs:60h seg000:00007FFBB7D63487movr9,[rax+58h] seg000:00007FFBB7D6348Bmovrax,[r9+r8*8] seg000:00007FFBB7D6348Fcallsub_7FFBB7D63310
提示数据包指示该代码从地址00007ffbb7d63470开始执行,并继续执行直到遇到00007FFBB7D6348F处的调用指令为止。由于调用不是间接调用,因此调用目标是在编译时预先确定的,因此该tip.pge数据包扩展为内部调用指令,来自调用目标地址00007FFBB7D63310的其他指令将被解码。
seg000:00007FFBB7D63310subrsp,48h seg000:00007FFBB7D63314mov[rsp+48h+var_28],rcx seg000:00007FFBB7D63319mov[rsp+48h+var_20],rdx seg000:00007FFBB7D6331Emov[rsp+48h+var_18],r8 seg000:00007FFBB7D63323mov[rsp+48h+var_10],r9 seg000:00007FFBB7D63328movrcx,rax seg000:00007FFBB7D6332Bmovrax,cs:7FFBB7E381E0h seg000:00007FFBB7D63332callrax
此时,在地址00007FFBB7D63332处发生了间接调用,下一个提示包将在此调用跳转的位置提供必要的信息。压缩会删除地址的前4个字节以节省空间,从3ee的数据包中,我们可以得出调用目标是00007ffbb7d4fb70。
... 00000000000003eetip2:????????b7d4fb70 00000000000003f3pad ...
解码从00007ffbb7d4fb70继续,直到它在00007FFBB7D4FB8C处有条件跳转指令为止。
seg000:00007FFBB7D4FB70movrdx,cs:7FFBB7E38380h seg000:00007FFBB7D4FB77movrax,rcx seg000:00007FFBB7D4FB7Ashrrax,9 seg000:00007FFBB7D4FB7Emovrdx,[rdx+rax*8] seg000:00007FFBB7D4FB82movrax,rcx seg000:00007FFBB7D4FB85shrrax,3 seg000:00007FFBB7D4FB89testcl,0Fh seg000:00007FFBB7D4FB8Cjnzshortloc_7FFBB7D4FB95 seg000:00007FFBB7D4FB8Ebtrdx,rax seg000:00007FFBB7D4FB92jnbshortloc_7FFBB7D4FBA0 seg000:00007FFBB7D4FB94retn
此时,tnt数据包将为你提供是否执行条件跳转的信息。以下带有2个“ ..”的tnt.8数据包表示,它没有进行两次无条件跳转。
00000000000003fetnt.8..
接下来,它将在00007FFBB7D4FB94处遇到ret指令。
seg000:00007FFBB7D4FB94retn
即使可以通过某种模拟进行计算,也无法从image本身可靠地确定返回地址,“ ret”是间接跳转,它从当前SP(堆栈指针)中检索跳转地址,下一个提示包将为你提供此ret指令返回的地址。
00000000000003fftip2:????????b7d63334
返回的地址将按照以下方式反汇编,并且代码执行将继续。
seg000:00007FFBB7D63334movrax,rcx seg000:00007FFBB7D63337movrcx,[rsp+48h+var_28] seg000:00007FFBB7D6333Cmovrdx,[rsp+48h+var_20] seg000:00007FFBB7D63341movr8,[rsp+48h+var_18] seg000:00007FFBB7D63346movr9,[rsp+48h+var_10] seg000:00007FFBB7D6334Baddrsp,48h
IPTA 分析工具
IPT压缩机制非常有效,需要拆卸引擎的帮助来重建完整指令。即使是很短的IPT跟踪记录,也需要大量CPU资源进行解压缩。一种方法是,可以应用IP过滤来限制输出,以最大程度地减少跟踪输出量。有时出于研究目的,不可避免的需要处理大量跟踪日志。
IPTAnalyzer是用于并行处理IPT跟踪日志的工具。该工具可以使用Python多处理库处理Intel PT跟踪,并创建基本的块缓存文件,该块信息对于控制流变化的整体分析很有用。例如,如果要从特定的image或地址范围收集指令,则可以在检索完整的指令之前查询此基本块缓存文件以查找属于该范围的位置。
https://github.com/ohjeongwook/iptanalyzer
案例研究:CVE-2017-11882
CVE-2017-11882是Microsoft Office公式编辑器中的漏洞,这是练习将IPT用于漏洞分析的良好练习目标。我们将说明如何使用IPT和IPTAnalyzer高效执行漏洞利用分析。
https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2017-11882
IPT 日志收集
你可以使用各种方法来生成IPT跟踪日志。我使用WinIPT生成跟踪日志。
https://github.com/ionescu007/winipt https://www.virustotal.com/gui/file/abbdd98106284eb83582fa08e3452cf43e22edde9e86ffb8e9386c8e97440624/detection
我们使用了恶意样本abbdd98106284eb83582fa08e3452cf43e22edde9e86ffb8e9386c8e97440624来复制利用条件,使用进程ID和日志文件名运行ipttool.exe,进程ID 2736是易受攻击的公式编辑器进程,跟踪输出将保存到EQNEDT32.pt文件中。
C:\Analysis\DebuggingPackage\TargetMachine\WinIPT>ipttool.exe--trace2736EQNEDT32.pt /-----------------------------------------\ |===Windows10RS51809IPTTestTool===| |===Copyright(c)2018AlexIonescu===| |===http://github.com/ionescu007===| |===http://www.windows-internals.com===| \-----------------------------------------/ [+]Foundactivetracewith1476395324bytessofar [+]Tracecontains11threadheaders [+]TraceEntry0forTID2520 TraceSize:134217728[RingBufferOffset:4715184] TimingMode:MTCPackets[MTCFrequency:3,ClockTscRatio:83] [+]TraceEntry1forTID1CA8 TraceSize:134217728[RingBufferOffset:95936] TimingMode:MTCPackets[MTCFrequency:3,ClockTscRatio:83] [+]TraceEntry2forTID8AC TraceSize:134217728[RingBufferOffset:63152] TimingMode:MTCPackets[MTCFrequency:3,ClockTscRatio:83] [+]TraceEntry3forTID1A88 TraceSize:134217728[RingBufferOffset:4560] TimingMode:MTCPackets[MTCFrequency:3,ClockTscRatio:83] [+]TraceEntry4forTID1964 TraceSize:134217728[RingBufferOffset:45184] TimingMode:MTCPackets[MTCFrequency:3,ClockTscRatio:83] [+]TraceEntry5forTID22D0 TraceSize:134217728[RingBufferOffset:6768] TimingMode:MTCPackets[MTCFrequency:3,ClockTscRatio:83] [+]TraceEntry6forTID73C TraceSize:134217728[RingBufferOffset:32480] TimingMode:MTCPackets[MTCFrequency:3,ClockTscRatio:83] [+]TraceEntry7forTID1684 TraceSize:134217728[RingBufferOffset:285264] TimingMode:MTCPackets[MTCFrequency:3,ClockTscRatio:83] [+]TraceEntry8forTID3C4 TraceSize:134217728[RingBufferOffset:99056] TimingMode:MTCPackets[MTCFrequency:3,ClockTscRatio:83] [+]TraceEntry9forTID610 TraceSize:134217728[RingBufferOffset:4812464] TimingMode:MTCPackets[MTCFrequency:3,ClockTscRatio:83] [+]TraceEntry10forTID1CD8 TraceSize:134217728[RingBufferOffset:7424] TimingMode:MTCPackets[MTCFrequency:3,ClockTscRatio:83] [+]TraceforPID2736writtentoEQNEDT32.pt
进程内存转储
你可以使用ProcDump或Process Explorer甚至Windbg来获取方程编辑器(EQNEDT32.exe)的内存转储,IPTAnalyzer可以使用进程内存转储来自动检索指令字节,而不是将单独的image文件提供给libipt。
https://docs.microsoft.com/en-us/sysinternals/downloads/procdump https://docs.microsoft.com/en-us/sysinternals/downloads/process-explorer
运行iptanalyzer
为了方便起见,在以下示例中,将%IPTANALYZERTOOL%设置为IPTAnalyzer文件夹的根目录。通过使用decode_blocks.py,可以生成块缓存文件。你需要提供带有IPT跟踪文件名的-p选项和带有进程内存转储文件的-d选项。
python%IPTANALYZER%\pyipttool\decode_blocks.py-pPT\EQNEDT32.pt-dProcessMemory\EQNEDT32.dmp-cblock.cache
下面显示了并行的Python进程,它们对解码文件进行解码。
转储EQNEDT32模块数据
由于EQNEDT32主模块存在漏洞,并且模块地址范围内或附近将发生异常代码执行模式,因此我们希望枚举EQNEDT32主模块范围内的块,该块介于00400000和0048e000之间。
0:011>lmvmEQNEDT32 Browsefullmodulelist startendmodulename 00000000`0040000000000000`0048e000EQNEDT32(deferred) ...
dump_blocks.py工具可用于枚举特定地址范围内的任何基本块。
python%IPTANALYZER%\pyipttool\dump_blocks.py-pPT\EQNEDT32.pt-dProcessMemory\EQNEDT32.dmp-C0-cblocks.cache-s0x00400000-e0x0048e000
该命令将生成与地址范围匹配的基本块的完整日志。从易受攻击的模块执行代码结束时,可能会过渡到shellcode,我们将重点放在日志末尾的基本块模式。注意,“ sync_offset = 2d236c”显示了这些最后的基本块命中的PSB数据包的位置。此sync_offset值可用于检索该点附近的指令。
... >00000000004117d3()(sync_offset=2d236c,offset=2d26f4) EQNEDT32!EqnFrameWinProc+0x2cf3: 00000000`004117d30fbf45c8movsxeax,wordptr[rbp-38h] >000000000041181e()(sync_offset=2d236c,offset=2d26f4) EQNEDT32!EqnFrameWinProc+0x2d3e: 00000000`0041181e0fbf45fcmovsxeax,wordptr[rbp-4] >0000000000411869()(sync_offset=2d236c,offset=2d26f4) EQNEDT32!EqnFrameWinProc+0x2d89: 00000000`0041186933c0xoreax,eax >000000000042fad6()(sync_offset=2d236c,offset=2d26fc) EQNEDT32!MFEnumFunc+0x12d9: 00000000`0042fad6c3ret
转储EQNEDT32模块指令
现在,我们知道EQNEDT32模块的最后一个基本块是在“ sync_offset = 2d236c” PSB块内执行的。dump_instructions.py脚本可用于转储完整指令。-S(开始sync_offset)和-E(结束sync_offset)之类的选项可用于指定sync_offset范围。
python%IPTANALYZER%\pyipttool\dump_instructions.py-p..\PT\EQNEDT32.pt-d..\ProcessMemory\EQNEDT32.dmp-S0x2d236c-E0x2d307c
查找ret代码
使用dump_instructions.py的输出,你可以轻松确定从EQNEDT32到Shellcode的代码的位置。
... Instruction:EQNEDT32!EqnFrameWinProc+0x2d8b: 00000000`0041186be900000000jmpEQNEDT32!EqnFrameWinProc+0x2d90(00000000`00411870) Instruction:EQNEDT32!EqnFrameWinProc+0x2d90: 00000000`004118705fpoprdi Instruction:EQNEDT32!EqnFrameWinProc+0x2d91: 00000000`004118715epoprsi Instruction:EQNEDT32!EqnFrameWinProc+0x2d92: 00000000`004118725bpoprbx Instruction:EQNEDT32!EqnFrameWinProc+0x2d93: 00000000`00411873c9leave Instruction:EQNEDT32!EqnFrameWinProc+0x2d94: 00000000`00411874c3ret Instruction:EQNEDT32!MFEnumFunc+0x12d9: 00000000`0042fad6c3ret Instruction:00000000`0019ee9cbac342baffmovedx,0FFBA42C3h Instruction:00000000`0019eea1f7d2notedx Instruction:00000000`0019eea38b0amovecx,dwordptr[rdx] Instruction:00000000`0019eea58b29movebp,dwordptr[rcx] Instruction:00000000`0019eea7bb3a7057f4movebx,0F457703Ah Instruction:00000000`0019eeac81eb8a0811f4subebx,0F411088Ah Instruction:00000000`0019eeb28b1bmovebx,dwordptr[rbx] Instruction:00000000`0019eeb455pushrbp Instruction:00000000`0019eeb5ffd3callrbx ...
从上面的指令清单中,你可以注意到00411874和0042fad6有两个“ ret”指令。
Instruction:EQNEDT32!EqnFrameWinProc+0x2d94: 00000000`00411874c3ret Instruction:EQNEDT32!MFEnumFunc+0x12d9: 00000000`0042fad6c3ret
在这两个“ ret”指令之后,代码将转移到非映像地址空间中。
Instruction:00000000`0019ee9cbac342baffmovedx,0FFBA42C3h Instruction:00000000`0019eea1f7d2notedx Instruction:00000000`0019eea38b0amovecx,dwordptr[rdx] Instruction:00000000`0019eea58b29movebp,dwordptr[rcx]
注意,在00000000`0019ee9c处的指令没有检索到任何匹配的模块名称,这意味着它很有可能被shellcode加载到动态内存中。
执行下一阶段的Shellcode
在shellcode之后,我们可以使用“ jmp rax”指令在0019eec1处定位执行下一级shellcode的位置,我们在Intel PT日志中有完整的Shellcode执行列表。
Instruction:00000000`0019eeb70567946d03addeax,36D9467h Instruction:00000000`0019eebc2d7e936d03subeax,36D937Eh Instruction:00000000`0019eec1ffe0jmprax
这些是dump_instructions.py脚本转储的下一阶段shellcode。
Instruction:00000000`006181119cpushfq Instruction:00000000`0061811256pushrsi Instruction:00000000`0061811357pushrdi Instruction:00000000`00618114eb07jmp00000000`0061811d Instruction:00000000`0061811d9cpushfq Instruction:00000000`0061811e57pushrdi Instruction:00000000`0061811f57pushrdi Instruction:00000000`0061812081ef40460000subedi,4640h Instruction:00000000`0061812681ef574b0000subedi,4B57h Instruction:00000000`0061812c8dbfbc610000leaedi,[rdi+61BCh] Instruction:00000000`0061813281c73b080000addedi,83Bh Instruction:00000000`006181385fpoprdi Instruction:00000000`006181395fpoprdi
分析总结
英特尔PT是一项非常有用的技术,可用于防御性和攻击性安全性研究。IPTAnalyzer是一个使用libipt库来使用IPT跟踪日志加快分析速度的工具。这里的漏洞利用示例显示了使用IPTAnalyzer工具生成块缓存文件并将其用于基本漏洞利用调查的好处。没有英特尔PT的帮助,此过程可能很繁琐,可能更多地取决于研究人员的直觉。使用Intel PT,可以自动执行此过程并自动检测恶意代码活动。
本文翻
转载请注明:IT运维空间 » 安全防护 » 使用Intel PT与IPTAnalyzer进行漏洞利用
发表评论