前言
眾所周知,Normal World的用戶態(tài)與內(nèi)核態(tài)的地址空間隔離是基于MMU分頁(yè)來(lái)實(shí)現(xiàn)的,那么Normal World與Secure World的地址空間隔離是如何實(shí)現(xiàn)的呢?
這篇文章將從CPU和OS的角度進(jìn)行深入分析,并分析其中存在的安全風(fēng)險(xiǎn)。
硬件隔離機(jī)制
閱讀ARM TrustZone手冊(cè)可知,內(nèi)存的隔離是由TZASC(TrustZone Address Space Controller)來(lái)控制 ,TZASC可以把外部DDR分成多個(gè)區(qū)域,每個(gè)區(qū)域可以單獨(dú)配置為安全區(qū)域或非安全區(qū)域 ,Normal World的代碼只能訪問(wèn)非安全區(qū)域。
下面以TZC-380這款地址空間控制器來(lái)進(jìn)行說(shuō)明,其它型號(hào)控制器的原理也大同小異。
通過(guò)配置 TZASC的寄存器來(lái)設(shè)置不同屬性的region,
?一個(gè)region表示 一段連續(xù)的物理地址空間,
?TZASC給每個(gè)region提供了一個(gè)可編程的安全屬性域,
?只有在Secure狀態(tài)下才允許修改這些寄存器,
?TZASC的基址不是固定的,不同廠商實(shí)現(xiàn)可能不同,但是每個(gè)寄存器的offset是固定的,如下所示:
CODE-TEEOS內(nèi)存管理
下面結(jié)合【OP-TEE代碼】[1]對(duì)配置 TZASC進(jìn)行分析:
core/drivers/tzc380.c
通過(guò)對(duì)region對(duì)應(yīng)的控制寄存器進(jìn)行設(shè)置來(lái)配置安全內(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]永遠(yuǎn)為0,因?yàn)門ZASC不允許regionsize小于32KB tzc_write_region_base_low(tzc.base,region, addr_low(region_base)); //設(shè)置RegionSetupHighRegister,第n個(gè)region基址的[63:32]位 //和上面的lowaddr拼成完整的region基址 tzc_write_region_base_high(tzc.base,region, addr_high(region_base)); //設(shè)置RegionAttributes Register //控制permissions,regionsize,subregiondisable,andregionenable tzc_write_region_attributes(tzc.base,region,attr); }else{ //第0個(gè)region的基址不需要設(shè)置,只需要設(shè)置region的屬性 tzc_write_region_attributes(tzc.base,region, attr&TZC_ATTR_SP_MASK); } }
//設(shè)置RegionSetupLowRegister的值 staticvoidtzc_write_region_base_low(vaddr_tbase,uint32_tregion, uint32_tval) { //定位到第region個(gè)Region對(duì)應(yīng)的寄存器,即上圖中的region_setup_low_n //tzasc基址寄存器+regioncontrol寄存器的偏移(0x100)+regionn寄存器的size io_write32(base+REGION_SETUP_LOW_OFF(region),val); }
通過(guò)閱讀代碼可知,tzc_configure_region是對(duì)第n個(gè)region的基址、大小和屬性進(jìn)行設(shè)置 ,其中屬性寄存器的格式如下:
?sp
: 第n個(gè)region的權(quán)限設(shè)置 ,當(dāng)發(fā)生訪問(wèn)region時(shí),sp控制TZASC是否允許訪問(wèn)region。
?size
:第n個(gè)region的大小 。
?subregion_disable
: region被劃分為8個(gè)相同大小的sub-regions,第一位表示相應(yīng)的subregion是否disabled。
?en
: 第n個(gè)region是否開啟。
在imx_configure_tzasc函數(shù)中對(duì)region進(jìn)行了配置 :
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用來(lái)設(shè)置整個(gè)地址空間的默認(rèn)屬性,它的基址為0,Size是由AXI_ADDRESS_MSB來(lái)配置,因此Region 0除了安全屬性字段之外,其它字段不允許設(shè)置。
下面以第一個(gè)region為例,對(duì)安全屬性進(jìn)行分析: 第一個(gè)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ù)手冊(cè)可知,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)都可以讀寫。
總結(jié):
以上代碼配置了CFG_TZDRAM_START開始的CFG_TZDRAM_SIZE大小的地址空間為安全內(nèi)存,即只有安全狀態(tài)下(TCR.NS=0)可以訪問(wèn)。
CFG_DRAM_BASE開始的CFG_DRAM_SIZE大小的地址空間為普通內(nèi)存,安全和非安全狀態(tài)下都可以訪問(wèn)。
CFG_SHMEM_START開始的CFG_SHMEM_SIZE大小的地址空間為共享內(nèi)存,安全和非安全狀態(tài)都可以 訪問(wèn) 。
【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)存的配置,接下來(lái)我們?cè)賮?lái)看下安全OS是如何使用這些物理內(nèi)存的。
CODE-TEEOS內(nèi)存管理
core/arch/arm/kernel/entry_a64.S
【TEE OS啟動(dòng)時(shí)會(huì)調(diào)用core_init_mmu_map對(duì)安全內(nèi)存地址空間進(jìn)行映射 :】[3]
#ifdefCFG_CORE_ASLR movx0,x20 blget_aslr_seed#x0用來(lái)保存開啟aslr的seed #else movx0,#0 #endif adrx1,boot_mmu_config blcore_init_mmu_map#記錄PA和VA的對(duì)應(yīng)關(guān)系,并初始化頁(yè)表 ...... bl__get_core_pos blenable_mmu#設(shè)置ttbr0_el1、tcr_el1,開啟分頁(yè),在開啟頁(yè)之前,VA==PA
core_init_mmu_map函數(shù)根據(jù)編譯時(shí)注冊(cè)的物理內(nèi)存地址信息對(duì)頁(yè)表進(jìn)行初始化,也就是對(duì)物理內(nèi)存進(jìn)行內(nèi)存映射:
void__weakcore_init_mmu_map(unsignedlongseed,structcore_mmu_config*cfg) { #ifndefCFG_VIRTUALIZATION //__nozi_start在鏈接腳本中指定,一級(jí)頁(yè)表的地址 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,用來(lái)PA/VA轉(zhuǎn)換 //第一個(gè)mmap_region記錄的是一級(jí)頁(yè)表的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,用來(lái)PV/VA轉(zhuǎn)換 //后面也會(huì)根據(jù)這些信息對(duì)頁(yè)表進(jìn)行初始化 offs=init_mem_map(tmp_mmap,ARRAY_SIZE(static_memory_map),seed); check_mem_map(tmp_mmap); core_init_mmu(tmp_mmap);//初始化頁(yè)表,進(jìn)行內(nèi)存映射 dump_xlat_table(0x0,1); core_init_mmu_regs(cfg);//記錄頁(yè)表基址,用來(lái)設(shè)置TTBR0 cfg->load_offset=offs; memcpy(static_memory_map,tmp_mmap,sizeof(static_memory_map)); }
上面函數(shù)首先調(diào)用init_mem_map初始化一個(gè)內(nèi)存信息表,記錄下各Region的PA和VA,此表用來(lái)物理地址和虛擬地址轉(zhuǎn)換,后面頁(yè)表初始化時(shí)也會(huì)根據(jù)此表進(jìn)行填充。
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ù)已注冊(cè)的物理地址空間信息來(lái)設(shè)置memory_map中tee_mmap_region的物理地址范圍(即PA、SIZE) last=collect_mem_ranges(memory_map,num_elems); //設(shè)置memory_map中tee_mmap_region的region_size(內(nèi)存粒度) //如果是tee側(cè)的安全內(nèi)存,則設(shè)置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); //添加一個(gè)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)存起始地址加上一個(gè)隨機(jī)值 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;??//?得到一個(gè)有效的VA,按頁(yè)對(duì)齊并且高位無(wú)效位清零 ????????????if?(assign_mem_va(ba,?memory_map)?&&?//?設(shè)置memory_map中PA對(duì)應(yīng)的VA為ba,已經(jīng)隨機(jī)化 ????????????????mem_map_add_id_map(memory_map,?num_elems,?&last,? ???????????????????????????id_map_start,?id_map_end))?{?////?向memory_map數(shù)組中添加一個(gè)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,則設(shè)置memory_map中PA對(duì)應(yīng)的VA為TEE_RAM_START,即PA==VA ????//?注意,va和size必須以region_size對(duì)齊,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ù)編譯時(shí)保存到phys_mem_map節(jié)中物理內(nèi)存信息來(lái)設(shè)置memory_map中tee_mmap_region的物理地址范圍 。
//根據(jù)已注冊(cè)的物理地址空間信息設(shè)置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設(shè)置memory_map(用于記錄region的PA/VA的對(duì)應(yīng)關(guān)系)的PA、SIZE和attr //phys_mem_map_begin是phys_mem_map數(shù)組的起始地址 for(mem=phys_mem_map_begin;mem
通過(guò) register_phys_mem 這個(gè)宏將TEE、非安全的共享內(nèi)存的物理地址空間編譯到phys_mem_map這個(gè)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這個(gè)宏使用關(guān)鍵字”section”將修飾的變量按照core_mmu_phys_mem結(jié)構(gòu)體編譯到phys_mem_map這個(gè)section中。
phys_mem_map_begin指向phys_mem_map這個(gè)section的起始地址 。
collect_mem_ranges會(huì)根據(jù)這個(gè)section的信息初始化static_memory_map內(nèi)存信息數(shù)組,這個(gè)數(shù)組用來(lái) 記錄各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)}
值得注意的是,上面注冊(cè)的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
接下來(lái), core_init_mmu調(diào)用core_init_mmu_ptn來(lái)對(duì)整個(gè)注冊(cè)的內(nèi)存地址空間進(jìn)行VA到PA的映射,即根據(jù)PA和VA填充頁(yè)表。
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++) //如果不是動(dòng)態(tài)虛擬地址空間,則進(jìn)行填充頁(yè)表(映射內(nèi)存) if(!core_mmu_is_dynamic_vaspace(mm+n)) //根據(jù)PA/VA填充頁(yè)表,即做內(nèi)存映射 core_mmu_map_region(prtn,mm+n); /* *Primarymappingtableisreadyatindex`get_core_pos()` *whosevaluemaynotbeZERO.Takethisindexascopysource. */ //根據(jù)已設(shè)置的頁(yè)表設(shè)置所有核的頁(yè)表 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)完成了對(duì)物理內(nèi)存的映射,包括安全內(nèi)存和共享內(nèi)存。在開啟分頁(yè)后,TEEOS就可以訪問(wèn)這些虛擬內(nèi)存地址空間了。
CODE-安全側(cè)地址校驗(yàn)
下面以符合GP規(guī)范的TEE接口為例,簡(jiǎn)單介紹下CA和TA的通信流程:
篇幅所限,這里僅分析Secure World側(cè)的調(diào)用流程,重點(diǎn)關(guān)注TA_InvokeCommandEntryPoint調(diào)用流程,此函數(shù)用來(lái)處理所有來(lái)自Normal World側(cè)的請(qǐng)求,安全側(cè)可信應(yīng)用的漏洞挖掘也是從這個(gè)函數(shù)開始入手,這里我們只分析地址校驗(yàn)相關(guān)流程。
?1.在TEEC_OpenSession中會(huì)去加載TA的elf文件,并設(shè)置相應(yīng)的函數(shù)操作表,最終調(diào)用目標(biā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)用目標(biāo)TA的TA_OpenSessionEntryPoint?2.TA_InvokeCommandEntryPoint調(diào)用流程如下,在此函數(shù)中會(huì)對(duì)REE傳入的地址進(jìn)行校驗(yàn)。__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//最終會(huì)調(diào)用core_pbuf_is來(lái)檢查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相應(yīng)的TA中執(zhí)行TA_InvokeCommandEntryPoint通過(guò)以上代碼分析可知,在調(diào)用TA的TA_InvokeCommandEntryPoint函數(shù)之前會(huì)對(duì)REE側(cè)傳入的參數(shù)類型進(jìn)行檢查 ,在TA代碼中使用REE傳入?yún)?shù)作為內(nèi)存地址的場(chǎng)景下,如果未校驗(yàn)對(duì)應(yīng)的參數(shù)類型或者參數(shù)類型為TEEC_VALUE_INPUT(與實(shí)際使用參數(shù)類型不匹配),則會(huì)繞過(guò)上面core_pbuf_is對(duì)REE傳入PA的檢查 ,可以傳入任意值,這個(gè)值可以為安全內(nèi)存PA,這樣就可以導(dǎo)致以S_EL0權(quán)限讀寫任意安全內(nèi)存。
總結(jié)
TEE作為可信執(zhí)行環(huán)境,通常用于運(yùn)行處理指紋、人臉、PIN碼等關(guān)鍵敏感信息的可信應(yīng)用,即使手機(jī)被ROOT,攻擊者也無(wú)法獲取這些敏感數(shù)據(jù)。
因此TEE側(cè)程序的安全至關(guān)重要,本文深入分析了TRUSTZONE物理內(nèi)存隔離、TEEOS內(nèi)存管理及TEE側(cè)對(duì)REE傳入地址的校驗(yàn)。
在了解了這些原理之后,我們就可以進(jìn)行漏洞挖掘了, 當(dāng)然也能寫出簡(jiǎn)單有效的FUZZ工具。只有對(duì)漏洞原理、攻擊方法進(jìn)行深入的理解 ,才能進(jìn)行有效的防御。
FUZZ是一個(gè)模糊測(cè)試工具,用于在漏洞挖掘過(guò)程中進(jìn)行重要的一步。它能夠檢查常見的漏洞,如緩沖區(qū)溢出、格式串漏洞、整數(shù)溢出等。
模糊測(cè)試是一種通過(guò)輸入大量隨機(jī)產(chǎn)生的數(shù)據(jù)來(lái)檢測(cè)程序異常的自動(dòng)化測(cè)試方法。在模糊測(cè)試中,測(cè)試用例是隨機(jī)生成的,而不是手動(dòng)創(chuàng)建的。這種方法可以幫助發(fā)現(xiàn)一些常見的問(wèn)題,如邊界條件錯(cuò)誤、類型錯(cuò)誤、內(nèi)存泄漏等。
FUZZ-COV是另一個(gè)流行的模糊測(cè)試工具,它使用覆蓋引導(dǎo)技術(shù)來(lái)提高模糊測(cè)試的效率。該工具能夠檢測(cè)出更多的漏洞,并且比其他工具更快。
除了FUZZ和FUZZ-COV,還有其他一些模糊測(cè)試工具,如BETA、BAP和LibFuzzer。這些工具在設(shè)計(jì)和實(shí)現(xiàn)上略有不同,但它們的目標(biāo)是相同的,即發(fā)現(xiàn)程序中的漏洞。
審核編輯:湯梓紅
-
控制器
+關(guān)注
關(guān)注
112文章
16361瀏覽量
178071 -
寄存器
+關(guān)注
關(guān)注
31文章
5343瀏覽量
120377 -
cpu
+關(guān)注
關(guān)注
68文章
10863瀏覽量
211781 -
內(nèi)存管理
+關(guān)注
關(guān)注
0文章
168瀏覽量
14141
原文標(biāo)題:參考
文章出處:【微信號(hào):談思實(shí)驗(yàn)室,微信公眾號(hào):談思實(shí)驗(yàn)室】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論