前言
眾所周知,Normal World的用戶態(tài)與內(nèi)核態(tài)的地址空間隔離是基于MMU分頁來實現(xiàn)的,那么Normal World與Secure World的地址空間隔離是如何實現(xiàn)的呢?
這篇文章將從CPU和OS的角度進行深入分析,并分析其中存在的安全風險。
硬件隔離機制
閱讀ARM TrustZone手冊可知,內(nèi)存的隔離是由TZASC(TrustZone Address Space Controller)來控制 ,TZASC可以把外部DDR分成多個區(qū)域,每個區(qū)域可以單獨配置為安全區(qū)域或非安全區(qū)域 ,Normal World的代碼只能訪問非安全區(qū)域。
下面以TZC-380這款地址空間控制器來進行說明,其它型號控制器的原理也大同小異。
通過配置 TZASC的寄存器來設置不同屬性的region,
?一個region表示 一段連續(xù)的物理地址空間,
?TZASC給每個region提供了一個可編程的安全屬性域,
?只有在Secure狀態(tài)下才允許修改這些寄存器,
?TZASC的基址不是固定的,不同廠商實現(xiàn)可能不同,但是每個寄存器的offset是固定的,如下所示:
CODE-TEEOS內(nèi)存管理
下面結合【OP-TEE代碼】[1]對配置 TZASC進行分析:
core/drivers/tzc380.c
通過對region對應的控制寄存器進行設置來配置安全內(nèi)存地址空間:tzc_configure_region
/* *`tzc_configure_region`isusedtoprogramregionsintotheTrustZone *controller. */ voidtzc_configure_region(uint8_tregion,vaddr_tregion_base,uint32_tattr) { assert(tzc.base); assert(regionRegister //注意:regionn的基址寄存器的[14:0]永遠為0,因為TZASC不允許regionsize小于32KB tzc_write_region_base_low(tzc.base,region, addr_low(region_base)); //設置RegionSetupHighRegister,第n個region基址的[63:32]位 //和上面的lowaddr拼成完整的region基址 tzc_write_region_base_high(tzc.base,region, addr_high(region_base)); //設置RegionAttributes Register //控制permissions,regionsize,subregiondisable,andregionenable tzc_write_region_attributes(tzc.base,region,attr); }else{ //第0個region的基址不需要設置,只需要設置region的屬性 tzc_write_region_attributes(tzc.base,region, attr&TZC_ATTR_SP_MASK); } }
//設置RegionSetupLowRegister的值 staticvoidtzc_write_region_base_low(vaddr_tbase,uint32_tregion, uint32_tval) { //定位到第region個Region對應的寄存器,即上圖中的region_setup_low_n //tzasc基址寄存器+regioncontrol寄存器的偏移(0x100)+regionn寄存器的size io_write32(base+REGION_SETUP_LOW_OFF(region),val); }
通過閱讀代碼可知,tzc_configure_region是對第n個region的基址、大小和屬性進行設置 ,其中屬性寄存器的格式如下:
?sp
: 第n個region的權限設置 ,當發(fā)生訪問region時,sp控制TZASC是否允許訪問region。
?size
:第n個region的大小 。
?subregion_disable
: region被劃分為8個相同大小的sub-regions,第一位表示相應的subregion是否disabled。
?en
: 第n個region是否開啟。
在imx_configure_tzasc函數(shù)中對region進行了配置 :
staticTEE_Resultimx_configure_tzasc(void) { vaddr_taddr[2]={0}; intend=1; inti=0; //TZASC基址 addr[0]=core_mmu_get_va(TZASC_BASE,MEM_AREA_IO_SEC); ...... for(i=0;i
Region 0用來設置整個地址空間的默認屬性,它的基址為0,Size是由AXI_ADDRESS_MSB來配置,因此Region 0除了安全屬性字段之外,其它字段不允許設置。
下面以第一個region為例,對安全屬性進行分析: 第一個region的屬性為TZC_ATTR_SP_NS_RW:
#defineTZC_SP_NS_WBIT(0) #defineTZC_SP_NS_RBIT(1) #defineTZC_SP_S_WBIT(2) #defineTZC_SP_S_RBIT(3) #defineTZC_ATTR_SP_SHIFT28//屬性位[28:31] #defineTZC_ATTR_SP_NS_RW((TZC_SP_NS_W|TZC_SP_NS_R)< ????????????????TZC_ATTR_SP_SHIFT)
根據(jù)手冊可知,TZC_SP_NS_W(b0001)是Non-secure write和 Secure write,TZC_SP_NS_R(b0010)是Non-secure read和Secure read,所以TZC_ATTR_SP_NS_RW 表示 Non-secure和Secure狀態(tài)可讀寫,即配置了DRAM地址空間的屬性為非安全和安全狀態(tài)都可以讀寫。
總結:
以上代碼配置了CFG_TZDRAM_START開始的CFG_TZDRAM_SIZE大小的地址空間為安全內(nèi)存,即只有安全狀態(tài)下(TCR.NS=0)可以訪問。
CFG_DRAM_BASE開始的CFG_DRAM_SIZE大小的地址空間為普通內(nèi)存,安全和非安全狀態(tài)下都可以訪問。
CFG_SHMEM_START開始的CFG_SHMEM_SIZE大小的地址空間為共享內(nèi)存,安全和非安全狀態(tài)都可以 訪問 。
【OP-TEE物理內(nèi)存布局:】[2]
core/arch/arm/include/mm/generic_ram_layout.h
*TEERAMlayoutwithoutCFG_WITH_PAGER *_ *+----------------------------------+<--?CFG_TZDRAM_START ?*??|?TEE?core?secure?RAM?(TEE_RAM)????| ?*??+----------------------------------+ ?*??|?Trusted?Application?RAM?(TA_RAM)?| ?*??+----------------------------------+ ?*??|?SDP?test?memory?(optional)???????| ?*??+----------------------------------+?<--?CFG_TZDRAM_START?+?CFG_TZDRAM_SIZE ?* ?*??+----------------------------------+?<--?CFG_SHMEM_START ?*??|?Non-secure?static?SHM????????????| ?*??+----------------------------------+?<--?CFG_SHMEM_START?+?CFG_SHMEM_SIZE
至此,已經(jīng)完成了安全內(nèi)存的配置,接下來我們再來看下安全OS是如何使用這些物理內(nèi)存的。
CODE-TEEOS內(nèi)存管理
core/arch/arm/kernel/entry_a64.S
【TEE OS啟動時會調(diào)用core_init_mmu_map對安全內(nèi)存地址空間進行映射 :】[3]
#ifdefCFG_CORE_ASLR movx0,x20 blget_aslr_seed#x0用來保存開啟aslr的seed #else movx0,#0 #endif adrx1,boot_mmu_config blcore_init_mmu_map#記錄PA和VA的對應關系,并初始化頁表 ...... bl__get_core_pos blenable_mmu#設置ttbr0_el1、tcr_el1,開啟分頁,在開啟頁之前,VA==PA
core_init_mmu_map函數(shù)根據(jù)編譯時注冊的物理內(nèi)存地址信息對頁表進行初始化,也就是對物理內(nèi)存進行內(nèi)存映射:
void__weakcore_init_mmu_map(unsignedlongseed,structcore_mmu_config*cfg) { #ifndefCFG_VIRTUALIZATION //__nozi_start在鏈接腳本中指定,一級頁表的地址 vaddr_tstart=ROUNDDOWN((vaddr_t)__nozi_start,SMALL_PAGE_SIZE); #else vaddr_tstart=ROUNDDOWN((vaddr_t)__vcore_nex_rw_start, SMALL_PAGE_SIZE); #endif vaddr_tlen=ROUNDUP((vaddr_t)__nozi_end,SMALL_PAGE_SIZE)-start; structtee_mmap_region*tmp_mmap=get_tmp_mmap(); unsignedlongoffs=0; //檢查安全和非安全區(qū)域是否有重疊,如果有重疊,則系統(tǒng)panic check_sec_nsec_mem_config(); //static_memory_map記錄mmap_region的PA、VA,用來PA/VA轉(zhuǎn)換 //第一個mmap_region記錄的是一級頁表的PA和VA static_memory_map[0]=(structtee_mmap_region){ .type=MEM_AREA_TEE_RAM,//region的內(nèi)存類型 .region_size=SMALL_PAGE_SIZE,//內(nèi)存粒度 .pa=start, .va=start, .size=len, .attr=core_mmu_type_to_attr(MEM_AREA_IDENTITY_MAP_RX), }; COMPILE_TIME_ASSERT(CFG_MMAP_REGIONS>=13); //初始化內(nèi)存信息表,即記錄下各region的PA/VA,用來PV/VA轉(zhuǎn)換 //后面也會根據(jù)這些信息對頁表進行初始化 offs=init_mem_map(tmp_mmap,ARRAY_SIZE(static_memory_map),seed); check_mem_map(tmp_mmap); core_init_mmu(tmp_mmap);//初始化頁表,進行內(nèi)存映射 dump_xlat_table(0x0,1); core_init_mmu_regs(cfg);//記錄頁表基址,用來設置TTBR0 cfg->load_offset=offs; memcpy(static_memory_map,tmp_mmap,sizeof(static_memory_map)); }
上面函數(shù)首先調(diào)用init_mem_map初始化一個內(nèi)存信息表,記錄下各Region的PA和VA,此表用來物理地址和虛擬地址轉(zhuǎn)換,后面頁表初始化時也會根據(jù)此表進行填充。
staticunsignedlonginit_mem_map(structtee_mmap_region*memory_map, size_tnum_elems,unsignedlongseed) { /* *@id_map_startand@id_map_enddescribesaphysicalmemoryrange *thatmustbemappedRead-OnlyeXecutableatidenticalvirtual *addresses. */ vaddr_tid_map_start=(vaddr_t)__identity_map_init_start; vaddr_tid_map_end=(vaddr_t)__identity_map_init_end; unsignedlongoffs=0; size_tlast=0; //根據(jù)已注冊的物理地址空間信息來設置memory_map中tee_mmap_region的物理地址范圍(即PA、SIZE) last=collect_mem_ranges(memory_map,num_elems); //設置memory_map中tee_mmap_region的region_size(內(nèi)存粒度) //如果是tee側(cè)的安全內(nèi)存,則設置region_size為SMALL_PAGE_SIZE(4K) assign_mem_granularity(memory_map); /* *Toeasemappingandloweruseofxlattables,sortmapping *descriptionmovingsmall-pageregionsafterthepgdirregions. */ qsort(memory_map,last,sizeof(structtee_mmap_region), cmp_init_mem_map); //添加一個MEM_AREA_PAGER_VASPACE類型的tee_mmap_region add_pager_vaspace(memory_map,num_elems,&last); if(IS_ENABLED(CFG_CORE_ASLR)&&seed){ //如果開啟了ASLR,則將安全內(nèi)存起始地址加上一個隨機值 vaddr_tbase_addr=TEE_RAM_START+seed; constunsignedintva_width=get_va_width(); constvaddr_tva_mask=GENMASK_64(va_width-1, SMALL_PAGE_SHIFT); vaddr_tba=base_addr; size_tn=0; for(n=0;n3;?n++)?{ ????????????if?(n) ????????????????ba?=?base_addr?^?BIT64(va_width?-?n); ????????????ba?&=?va_mask;??//?得到一個有效的VA,按頁對齊并且高位無效位清零 ????????????if?(assign_mem_va(ba,?memory_map)?&&?//?設置memory_map中PA對應的VA為ba,已經(jīng)隨機化 ????????????????mem_map_add_id_map(memory_map,?num_elems,?&last,? ???????????????????????????id_map_start,?id_map_end))?{?////?向memory_map數(shù)組中添加一個region,?PA為id_map_start ????????????????offs?=?ba?-?TEE_RAM_START; ????????????????DMSG("Mapping?core?at?%#"PRIxVA"?offs?%#lx", ?????????????????????ba,?offs); ????????????????goto?out; ????????????}?else?{ ????????????????DMSG("Failed?to?map?core?at?%#"PRIxVA,?ba); ????????????} ????????} ????????EMSG("Failed?to?map?core?with?seed?%#lx",?seed); ????} ????//?未開啟ASLR,則設置memory_map中PA對應的VA為TEE_RAM_START,即PA==VA ????//?注意,va和size必須以region_size對齊,memory_map中region的PA可能不是連續(xù)的, ????//?但是VA地址空間是連續(xù)的 ????if?(!assign_mem_va(TEE_RAM_START,?memory_map)) ????????panic(); out: ????qsort(memory_map,?last,?sizeof(struct?tee_mmap_region), ??????????cmp_mmap_by_lower_va); ????dump_mmap_table(memory_map); ????return?offs; }
其中,collect_mem_ranges根據(jù)編譯時保存到phys_mem_map節(jié)中物理內(nèi)存信息來設置memory_map中tee_mmap_region的物理地址范圍 。
//根據(jù)已注冊的物理地址空間信息設置memory_map數(shù)組中tee_mmap_region的物理地址范圍 staticsize_tcollect_mem_ranges(structtee_mmap_region*memory_map, size_tnum_elems) { conststructcore_mmu_phys_mem*mem=NULL; size_tlast=0; //根據(jù)phys_mem_map設置memory_map(用于記錄region的PA/VA的對應關系)的PA、SIZE和attr //phys_mem_map_begin是phys_mem_map數(shù)組的起始地址 for(mem=phys_mem_map_begin;mem
通過 register_phys_mem 這個宏將TEE、非安全的共享內(nèi)存的物理地址空間編譯到phys_mem_map這個section。
register_phys_mem(MEM_AREA_TEE_RAM,TEE_RAM_START,TEE_RAM_PH_SIZE); register_phys_mem(MEM_AREA_TA_RAM,TA_RAM_START,TA_RAM_SIZE); register_phys_mem(MEM_AREA_NSEC_SHM,TEE_SHMEM_START,TEE_SHMEM_SIZE); register_sdp_mem(CFG_TEE_SDP_MEM_BASE,CFG_TEE_SDP_MEM_SIZE); register_phys_mem_ul(MEM_AREA_TEE_RAM_RW,VCORE_UNPG_RW_PA,VCORE_UNPG_RW_SZ); .....
register_phys_mem這個宏使用關鍵字”section”將修飾的變量按照core_mmu_phys_mem結構體編譯到phys_mem_map這個section中。
phys_mem_map_begin指向phys_mem_map這個section的起始地址 。
collect_mem_ranges會根據(jù)這個section的信息初始化static_memory_map內(nèi)存信息數(shù)組,這個數(shù)組用來 記錄各region的PA、VA、內(nèi)存屬性、地址空間范圍等信息。
#defineregister_phys_mem(type,addr,size) __register_memory(#addr,(type),(addr),(size), phys_mem_map) #define__register_memory(_name,_type,_addr,_size,_section) SCATTERED_ARRAY_DEFINE_ITEM(_section,structcore_mmu_phys_mem)= {.name=(_name),.type=(_type),.addr=(_addr), .size=(_size)}
值得注意的是,上面注冊的TEE_RAM_START開始的物理地址空間就是TZC-380配置的Region 2,即安全內(nèi)存地址空間。
#defineTEE_RAM_STARTTZDRAM_BASE #defineTEE_RAM_PH_SIZETEE_RAM_VA_SIZE #defineTZDRAM_BASECFG_TZDRAM_START #defineTZDRAM_SIZECFG_TZDRAM_SIZE
接下來, core_init_mmu調(diào)用core_init_mmu_ptn來對整個注冊的內(nèi)存地址空間進行VA到PA的映射,即根據(jù)PA和VA填充頁表。
voidcore_init_mmu_prtn(structmmu_partition*prtn,structtee_mmap_region*mm) { size_tn; assert(prtn&&mm); for(n=0;!core_mmap_is_end_of_table(mm+n);n++){ debug_print("%010"PRIxVA"%010"PRIxPA"%10zx%x", mm[n].va,mm[n].pa,mm[n].size,mm[n].attr); if(!IS_PAGE_ALIGNED(mm[n].pa)||!IS_PAGE_ALIGNED(mm[n].size)) panic("unalignedregion"); } /*Cleartablebeforeuse*/ memset(prtn->l1_tables,0,sizeof(l1_xlation_table)); for(n=0;!core_mmap_is_end_of_table(mm+n);n++) //如果不是動態(tài)虛擬地址空間,則進行填充頁表(映射內(nèi)存) if(!core_mmu_is_dynamic_vaspace(mm+n)) //根據(jù)PA/VA填充頁表,即做內(nèi)存映射 core_mmu_map_region(prtn,mm+n); /* *Primarymappingtableisreadyatindex`get_core_pos()` *whosevaluemaynotbeZERO.Takethisindexascopysource. */ //根據(jù)已設置的頁表設置所有核的頁表 for(n=0;nl1_tables[0][n], prtn->l1_tables[0][get_core_pos()], XLAT_ENTRY_SIZE*NUM_L1_ENTRIES); } }
到這里,TEE側(cè)OS已經(jīng)完成了對物理內(nèi)存的映射,包括安全內(nèi)存和共享內(nèi)存。在開啟分頁后,TEEOS就可以訪問這些虛擬內(nèi)存地址空間了。
CODE-安全側(cè)地址校驗
下面以符合GP規(guī)范的TEE接口為例,簡單介紹下CA和TA的通信流程:
篇幅所限,這里僅分析Secure World側(cè)的調(diào)用流程,重點關注TA_InvokeCommandEntryPoint調(diào)用流程,此函數(shù)用來處理所有來自Normal World側(cè)的請求,安全側(cè)可信應用的漏洞挖掘也是從這個函數(shù)開始入手,這里我們只分析地址校驗相關流程。
?1.在TEEC_OpenSession中會去加載TA的elf文件,并設置相應的函數(shù)操作表,最終調(diào)用目標TA的TA_OpenSessionEntryPoint。__tee_entry_std
-->entry_open_session
-->tee_ta_open_session
-->tee_ta_init_session-->tee_ta_init_user_session-->set_ta_ctx_ops
-->ctx->ops->enter_open_session(user_ta_enter_open_session)
-->user_ta_enter
-->tee_mmu_map_param
-->thread_enter_user_mode
-->__thread_enter_user_mode//返回到S_EL0,調(diào)用目標TA的TA_OpenSessionEntryPoint?2.TA_InvokeCommandEntryPoint調(diào)用流程如下,在此函數(shù)中會對REE傳入的地址進行校驗。__tee_entry_std
-->entry_invoke_command
-->copy_in_param
-->set_tmem_param//如果是memref類型,則調(diào)用set_tmem_param分配共享內(nèi)存
-->msg_param_mobj_from_nocontig
-->mobj_mapped_shm_alloc
-->mobj_reg_shm_alloc//最終會調(diào)用core_pbuf_is來檢查RRE傳入的PA是否在非安全內(nèi)存地址范圍內(nèi)
-->tee_ta_get_session
-->tee_ta_invoke_command
-->check_params
-->sess->ctx->ops->enter_invoke_cmd(user_ta_enter_invoke_cmd)
-->user_ta_enter
-->tee_mmu_map_param//映射用戶空間地址(S_EL0)
-->tee_ta_push_current_session
-->thread_enter_user_mode//返回S_EL0相應的TA中執(zhí)行TA_InvokeCommandEntryPoint通過以上代碼分析可知,在調(diào)用TA的TA_InvokeCommandEntryPoint函數(shù)之前會對REE側(cè)傳入的參數(shù)類型進行檢查 ,在TA代碼中使用REE傳入?yún)?shù)作為內(nèi)存地址的場景下,如果未校驗對應的參數(shù)類型或者參數(shù)類型為TEEC_VALUE_INPUT(與實際使用參數(shù)類型不匹配),則會繞過上面core_pbuf_is對REE傳入PA的檢查 ,可以傳入任意值,這個值可以為安全內(nèi)存PA,這樣就可以導致以S_EL0權限讀寫任意安全內(nèi)存。
總結
TEE作為可信執(zhí)行環(huán)境,通常用于運行處理指紋、人臉、PIN碼等關鍵敏感信息的可信應用,即使手機被ROOT,攻擊者也無法獲取這些敏感數(shù)據(jù)。
因此TEE側(cè)程序的安全至關重要,本文深入分析了TRUSTZONE物理內(nèi)存隔離、TEEOS內(nèi)存管理及TEE側(cè)對REE傳入地址的校驗。
在了解了這些原理之后,我們就可以進行漏洞挖掘了, 當然也能寫出簡單有效的FUZZ工具。只有對漏洞原理、攻擊方法進行深入的理解 ,才能進行有效的防御。
FUZZ是一個模糊測試工具,用于在漏洞挖掘過程中進行重要的一步。它能夠檢查常見的漏洞,如緩沖區(qū)溢出、格式串漏洞、整數(shù)溢出等。
模糊測試是一種通過輸入大量隨機產(chǎn)生的數(shù)據(jù)來檢測程序異常的自動化測試方法。在模糊測試中,測試用例是隨機生成的,而不是手動創(chuàng)建的。這種方法可以幫助發(fā)現(xiàn)一些常見的問題,如邊界條件錯誤、類型錯誤、內(nèi)存泄漏等。
FUZZ-COV是另一個流行的模糊測試工具,它使用覆蓋引導技術來提高模糊測試的效率。該工具能夠檢測出更多的漏洞,并且比其他工具更快。
除了FUZZ和FUZZ-COV,還有其他一些模糊測試工具,如BETA、BAP和LibFuzzer。這些工具在設計和實現(xiàn)上略有不同,但它們的目標是相同的,即發(fā)現(xiàn)程序中的漏洞。
審核編輯:湯梓紅
-
控制器
+關注
關注
112文章
16442瀏覽量
179017 -
寄存器
+關注
關注
31文章
5363瀏覽量
120926 -
cpu
+關注
關注
68文章
10901瀏覽量
212640 -
內(nèi)存管理
+關注
關注
0文章
168瀏覽量
14165
原文標題:參考
文章出處:【微信號:談思實驗室,微信公眾號:談思實驗室】歡迎添加關注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論