Linux X86 LBR和BTS功能分析與實現

X86 處理器支持最近分支記錄(Last branch record),也就是記錄CPU的跳轉記錄(jmp,jcc,call,ret等指令,中斷和異常). 通過操作MSR寄存器(model specific register)來配置分支記錄功能.

1. 分支記錄格式(branch record)
有如下兩個函數:

int add_fun(int a,int b)
{
return (a+b);
}

int main(void)
{
 return add_func(1,2);
}

分支記錄格式爲from -> to 形式,上面兩個函數調用,可以生成兩條branch record:
main -> add_fun /* call 指令*/
add_fun->main /* ret指令*/

2 分支記錄方式

X86提供LBR, BTM兩種方式來記錄分支信息, LBR把分支信息記錄到特定模型寄存器(MSR),而BTM把分支記錄通過消息的形式發送給系統總線或者寫到內存中(Branch Trace Store).

2.1 LBR(last branch record)

把分支信息保存在稱爲"LBR stack"的MSR寄存器組裏面,且用棧頂指針寄存器(top-of-stack (TOS) pointer
)來記錄當前index. 這種方式明顯缺點就是寄存器組數量有限,也就是保存的記錄有限.根據CPU型號不同,LBR stack大小也有差異,目前最多支持32組寄存器. 可以通過CPU的DisplayFamily和DisplayModel值來查詢LBR Stack的地址和大小.

2.2 BTM(Branch Trace Messages)

   目前LBR stack方式記錄的分支記錄條數最多爲32組,通過BTM機制,CPU把分支信息通過消息的形式,發送到系統總線或者寫到內存,發送到系統總線時,需要在總線上外接調試設備來接受BTM消息.  而我們更多采用寫到內存的方式(BTS).BTS 把BTM消息保存在預先分配內存區(Debug Store)中,這樣能夠保存更多LBR條目.實際上DS區分爲三部分:內存管理區,BTS內存區,PEBS區(性能檢測)
內存管理區: 保存BTS內存和PEBS內存的信息

BTS buffer base :BTS內存首地址
BTS index:cpu當前寫入地址
BTS absolute maximum: BTS內存區結束地址
BTS interrupt threshold: 觸發中斷閾值,也就是BTS index=BTS interrupt threshold時,會產生中斷
用於通知從BTS內存區讀取分支信息,一條Branch record記錄,又包含三個部分:

 

3. MSR寄存器(model specific register)

特殊模型寄存器與CPU的體系結構強相關,必須通過CPUID指令查詢cpu的DispalyFamily和DisplayModel,從而得到CPU的MSR寄存器地址和功能(Feature Informatio)信息,這裏介紹與LBR和BTS相關的MSR寄存器.

3.1 CPUID.01指令
執行CPUID指令後,可以得到CPU的DF_DM和支持的LBR功能信息,返回信息存放在eax,ecx,edx中.
ECX返回的重要標誌位:
DTES64: 採用BTS內存方式時,cpu是否支持使用64bit存放分支記錄
DS-CP: cpu是否支持根據指令運行級別,對LBR進行過濾
PDCM: cpu是否支持調試功能

EDX返回的重要標誌位
MSR:CPU是否支持指令rdmsr/wrmsr來讀寫MSR寄存器.
DS: Debug Store,是否支持把BTM消息寫到內存,也可以理解爲是否支持BTS功能.
在使用LBR和BTS功能時,必須先用CPUID指令查詢CPU對LBR和BTS功能是否支持.

3.2 DEBUGCTL MSR(調試控制寄存器)
調試控制寄存器在不同CPU族中,有不同的名字,地址都是0x01D9,主要用來控制LBR,BTS,BTM,中斷等
IA32_DEBUGCTL:  Nehalem, Core Solo, Core Duo
MSR_DEBUGCTLA: Pentium 4 and Intel Xeo
MSR_DEBUGCTLB: Pentium M
DEBUGCTLMSR: P6 Family

IA32_PERF_CAPABILITIES MSR
定義了LBR stack保存格式和系統進入SMM模式時是否自動關閉LBR功能,對於分支記錄功能,我們只用到底5位,
用來識別LBR stack存放格式.

IA32_MISC_ENABLE MSR

系統是否支持BTS功能,除了使用cpuid指令外,還需要查詢IA32_MISC_ENABLE MSR寄存器的BTS_UNAVAILABLE位
如果位0,表示支持BTS功能.

IA32_DS_AREA MSR
在採用BTS記錄分支信息時,需要預先分配內存(Debug Store),把DS內存地址寫到IA32_DS_AREA寄存器後,CPU就可以自動往內存中填充分支記錄信息.

MSR_LASTBRANCH_TOS MSR

在使用LBR stack記錄分支信息時,TOS寄存器指示stack當前位置. 從而可以從LBR stack中正確讀取分支記錄.

MSR_LASTBRANCH_x_FROM_IP/MSR_LASTBRANCH_x_TO_IP MSR
分支記錄寄存器,保存最近的分支記錄信息。

4 LBR功能實現
4.1 操作MSR寄存器
1. CPUID指令查詢DF_DM信息,獲取對相應MSR寄存器地址
cpuid(1,&eax,&ebx,&ecx,&edx);
2.查詢LBR stack存儲格式
rdmsrl(MSR_PERF_CAPABILITIES,lbr_fmt);
3.開啓LBR功能,DBUGCTL MSR的bit0寫1
wrmsrl(MSR_DEBUGCTL,ctl)
4.讀取TOS指針位置
rdmsrl(MSR_LBR_TOS_R,tos);
5. 讀取LBR stack寄存器
rdmsrl(MSR_LBR_FROM_IP+tos,from);
rdmsrl(MSR_LBR_TO_IP+tos,to);

其中注意點有:
1. 必須查詢LBR stack的存儲格式,才能解析分支記錄
2. 讀取LBR stack時,需要關閉LBR功能,避免讀取程序和CPU產生競爭.
3. 針對LBR還可以使用過濾功能(MSR_LBR_SELECT),根據branch type來過濾相關跳轉.
4.2 log輸出
可以看到能夠正常捕獲從LKM的init到關閉LBR的調用過程.
rootkits_lbr_init:from:rootkit_lbr_onoff [lbrs],to:native_write_msr
rootkits_lbr_init:from:rootkit_lbr_onoff [lbrs],to:rootkit_lbr_onoff [lbrs]
rootkits_lbr_init:from:native_read_msr,to:rootkit_lbr_onoff [lbrs]
rootkits_lbr_init:from:rootkit_lbr_onoff [lbrs],to:native_read_msr
rootkits_lbr_init:from:rootkits_lbr_init [lbrs],to:rootkit_lbr_onoff [lbrs]

5 BTS功能實現
5.1 操作MSR寄存器
1.BTS支持查詢
rdmsrl(MSR_MISC_ENABLE,val);/*查詢BTS_UNAVAILABLE */
edx&DS_MODE /*查詢DS功能是否支持 */
如果有一個不支持,則BTS功能無法實現.
2.內存分配和填充

把DS首地址寫到MSR_DS_AREA寄存器.
3. 開啓BTS和BTM功能
這裏採用poll的方式來獲取分支信息,所以禁止了中斷.

4.讀取BTS內存
在讀取之前,也要先關閉BTS和BTM功能

5.BTS與KPTI

在開啓頁表隔離時,BTS是無法使用的,因爲當程序運行在ring3時,CPU是無法訪問到驅動申請的DS內存.

解決辦法:

1.關閉KPTI

sudo vim /etc/default/grub

在command linux添加nopti或者"pti=off"

GRUB_CMDLINE_LINUX=" nopti"

然後sudo update-grub,sudo reboot

2. 對特定進程進行內存映射

把BTS相關的內存映射到特定進程空間.

6. Intel-PT

intel-pt是intel 在2015推出的實時指令採集,功能上更強大,性能影響也較小(5%)

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章