-
-
[原创]谈谈intel x86提供的Branch Trace Store的功能
-
发表于:
2008-6-20 21:09
63193
-
[原创]谈谈intel x86提供的Branch Trace Store的功能
好就没来发贴了...罪过罪过
这里说说Intel提供的BTS功能,相信对于做系统逆向分析有一定作用
本文在我blog的链接:http://www.csksoft.net/blog/post/bts_setup.html
介绍
Branch Trace Store(BTS)是目前广泛被intel CPU所提供的一种硬件辅助调试功能,因为在MSRA项目需要,所以作了一些基于它的应用。虽然Intel的CPU开发手册[1]提供了比较详细的使用方法,但是由于比较笼统,且缺少win32下的相关资料。所以我打算把其中的一些tricky的事情和大家分享下。这些内容当然也是我自己查找公开资料得到的,因此也不算什么秘密吧 :-)
BTS简单的说就是允许CPU将自己实际执行到的分支指令(jmp/jxx/call/int/etc.)的相关信息保存下来的功能。一般CPU都会保存每个分支指令的源地址和目标地址,该地址在保护模式下是虚拟地址形式表示的。利用这个功能,可以实时地了解当前CPU正在执行代码的实际流程情况,很多分析软件,如Intel的Vtune或者profiling库,如*nix平台下的perfmon[2]都用它来做一些程序性能分析。当然,还可以做很多其他有趣的事,比如逆向工程,具体我就不说了。
不过目前很少有资料具体介绍如何在win32下开启该功能并实现一个可用的BTS捕捉引擎。当然可以参考perfmon的代码,但BTS在其中只是很小一个部分,同时为了实现跨平台,对于新手来说难度较大。因此我这里重点介绍对于单核心的NetBurst构架的CPU(通俗地说就是P4这类)在win32下的具体实现细节。其他的构架,比如现在的Core Due,大家可以参考intel的开发手册举一反三。
工作框架
实际上,上面所说的BTS只是intel对于分支指令信息捕获机制的一个分支,就P4而言,大致提供了下列几种分支指令的捕获手段
Last Branch Record(LBR)
故名思意,该方式将记录最后几个分支信息,实际上NetBurst CPU中内建了若干个MSR寄存器用于记录Last Branch Record,称为LBR Stack。在该模式下运行的CPU会采用Round Robin的方式循环填充那几个MSR寄存器。
该方式常用在调试器的Call Stack分析上,我们这里就不涉及了
Branch Trace Messages
该方式和我们将介绍的Branch Trace Store大致类似,与LBR不同的是,CPU会把分支信息发送到系统总线上供第三方硬件在总线上接收数据。而BTS则直接将数据保存到由程序制定的内存单元中。我们这里也不讨论该方式。
Branch Trace Store
简单的说,BTS就是将分支信息保存到了有程序(实际就是我们)制定的一块内存空间中。而当这块内存用尽时,CPU可以采用Round Robin的方式循环填充,这就和LBR类似,但可以指定内存块的大小来决定最大捕捉量。另一种处理方式是在内存快用尽的时候,一个事先由我们设定的处理中断将触发来完成对当前内存块中BTS数据的保存工作。这样就可以记录任意多的分支信息了。我们这里主要考虑的就是这种方式的BTS。
按照intel开发手册的描述,BTS开启后的CPU执行模式如下图所示:
每当CPU执行到一个分支指令后,它就会产生一个如上左图的BTS记录项。这里举个例子:
0x80001234 mov eax,[esp]
0x80001238 or eax,eax
0x80001240 jnz 0x80002000
0x80001245 nop
...
struct BTS_ITEM_BLOCK_P4{
ULONG dwBranchFrom;
ULONG dwBranchTo;
ULONG dwFlags;
};
struct DS_BUFFER_MGR_BLOCK_P4{
PVOID pBTSBufferBase;
PVOID pBTSBufferIndex;
PVOID pBTSMaxSize;
PVOID pBTSIntThresold;
PVOID pPEBSBufferBase;
PVOID pPEBSIndex;
PVOID pPEBSMaxSize;
PVOID pPEBSIntThresold;
ULONG dwPEBSCounterReset;
ULONG Reserved;
};
DS_BUFFER_MGR_BLOCK *pDSBlk = <some allocating code>;
pDSBlk->pBTSBufferBase = pBtsBuffer;
pDSBlk->pBTSBufferIndex = pDSBlk->pBTSBufferBase;
pDSBlk->pBTSMaxSize = &pBtsBuffer[2000];
pDSBlk->pBTSIntThresold = &pBtsBuffer[1900];
void __declspec( naked ) BTS_handler(void)
{
//保存当前线程状态,恢复内核模式相关寄存器
__asm
{
cli
pushad
pushfd
push fs
push ds
push es
push gs
mov ebx,0x30
mov eax,0x23
mov fs,bx
mov ds,ax
mov es,ax
}
//关闭BTS,具体见后文
__asm{
mov ecx,MSR_DEBUGCTL
RDMSR
mov tmp_old_msr_bts,eax
xor eax,eax
WRMSR
}
//相关操作,如保存BTS到磁盘
//结束中断处理--------------------------------------
pDSBlk->pBTSBufferIndex = pDSBlk->pBTSBufferBase; //重设BTS记录指针
//开启BTS,具体见后文
__asm
{
//restore BTS
mov ecx,MSR_DEBUGCTL
mov eax,tmp_old_msr_bts
WRMSR
}
//清除local apic中preformance mon. counter的屏蔽位,具体间后文
local_apic->lvt_pc.mask=0;
//发送eoi指令字
local_apic->eoi.eoi = 0;
//恢复当前线程环境
__asm{
sti
pop gs
pop es
pop ds
pop fs
popfd
popad
iretd
}
}
PHYSICAL_ADDRESS pa;
pa.HighPart = 0;
pa.LowPart = 0xFEE00000; // 原始物理地址
local_apic = (PAPIC)MmMapIoSpace(pa,sizeof(APIC), MmNonCached);
- 初始化用于存放BTS记录的内存空间
- 设置对于上述内存空间的描述信息,即DS_BUFFER_MGR_BLOCK,并将其地址值写入IA32_DS_AREA MSR寄存器
- 编写处理BTS溢出操作的中断服务程序,并在IDT表中设立表项,创建一个中断服务
- 设置Local APIC的Performance mon. Counter项,指向上述IDT表中我们创建的中断项
- 通过对专门的MSR寄存器读写开启BTS功能
- 在进入中断后以关闭BTS为宜
- 在中断退出前,需要设置pDSBlk->pBTSBufferIndex到初始状态,即pDSBlk->pBTSBufferIndex = pDSBlk->pBTSBufferBase;
- 当BTS中断触发后,CPU将在Local APCI中的Performance Mon. Counter寄存器项上打开屏蔽位,从而阻止对中断的二次触发,所以需要在中断结束前关闭该屏蔽位
- 给APIC发送EOI命令,通知该中断结束服务
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!