<2024年12月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

文章分类

导航

订阅

CpuWhere

IA-32 CPU从P6开始便支持分支监视和记录机制。说白了这种机制就是记录下CPU曾经执行的分支。把这些分支信息串联起来便可以得到CPU的执行轨迹。这一机制的名称被称为BTS,Branch Tracing Store,它与Debug Store,简称DS有着紧密关系。

以下是使用BTS机制所编写的一个示例程序CpuWhere的工作画面。

窗口左侧是一系列控制按钮,编辑框用来指定BTS缓冲区可以容纳的BTS记录数,也就是SetupDSArea函数的参数。窗口右侧的列表框用来显示从驱动程序读取到的BTS记录。显示的顺序与栈回溯类似,最近发生的在上方。或者说,CPU的运行轨迹是从下到上。

在列表框中,每条BTS记录显示为两行,上面一行用来显示分支的目标地址(方括号中),地址前以>符号表示,地址后为这个地址所对应的符号;下面一行为分支的发起地址,地址前以<表示,大括号中是本条BTS记录的标志字段(dwFlags)。每一行的开头是以#开始的流水号。

以图中的第2行为例,#00004365 - [<0x80526bed]: nt!PsGetCurrentProcessId + d {flags 0x0}。其中,地址前的小于号代表这是一个BTS记录的发起行,0x80526bedBtsRecord中的dwFrom字段的值,nt!PsGetCurrentProcessId + d是这个地址所对应的符号和位移(displacement)。

观察nt!PsGetCurrentProcessId函数的反汇编,可以看到地址0x80526bedret指令的下一条指令的地址,因此,CPU是在执行ret指令时产生这条BTS记录的。

lkd> u nt!PsGetCurrentProcessId

nt!PsGetCurrentProcessId:

80526be0 64a124010000    mov     eax,dword ptr fs:[00000124h]

80526be6 8b80ec010000    mov     eax,dword ptr [eax+1ECh]

80526bec c3              ret

80526bed cc              int     3

观察第一行(#00004365 - [>0xbf801a73]: win32k!HmgLock + 2e),它是这个BTS记录的目标地址,于是,可以推测出这个BTS记录的记载的是从PsGetCurrentProcessId函数返回HmgLock这一事件。#00004365记载的是HmgLock函数调用PsGetCurrentProcessId时的分支。

在图所示的列表框的下方,记录了调用系统服务时从用户态向内核态的转移过程。#00004365记载了是从用户态(#00004376 - [<0x7c90eb8f]: ntdll!KiFastSystemCallRet + 0 {flag 0x0})跳转到内核态(#00004376 - [>0x8053cad0]: nt!KiFastCallEntry + 0)的起始(0x7c90eb8f)和目标地址(0x8053cad0)。

CpuWhere.exe的大多数实现都是非常简单的。比较复杂的地方就是如何查找BTS记录所对应的符号。因为BTS记录中的地址有内核态的地址,也有用户态的地址,简单的使用DbgHelp库中的符号函数(SymFromAddr等)是不能满足我们的需要的。

实际上,这里使用的方法是使用WinDBG的调试引擎。通过调试引擎所输出的接口,我们启动了一个本地内核调试会话。然后调用调试引擎的服务来查找符号。

posted on 2007年10月28日 22:29 由 Raymond

# re: CpuWhere @ 2007年10月29日 16:49

shocked...

WANGyu

# re: CpuWhere @ 2008年7月10日 10:00

老兄,别人买你一本书,你竟连书里完整的Souce都不愿意提供,也太不厚道了吧。

shl

# re: CpuWhere @ 2009年1月15日 14:13

超强,超顶!!
没调试呢,有个疑问,你怎么知道一个指令流是属于同一个线程呢?

safendis

Powered by Community Server Powered by CnForums.Net