Android 4.1 Jelly Bean安全機制探討
By loda
有關手機或作業系統的安全議題一直就像是潘朵拉的盒子一樣,帶點神祕但卻又很吸引大家的目光.由於智慧型手機幾乎是現代人必備的隨身電子產品,若在智慧型手機上被找到安全漏洞,相對於原本個人電腦的世界,所引起的漣漪也將非比尋常.曾在“我是亞桑傑”這本書中讀到如下印象深刻的話語
”我憎恨對安全的歸類,他們的分類就是,’凡是沒有被明確允許就是不准’,他們以為他們是誰? 想用科技來掩護他們野蠻的行為.”
熟悉安全技術的演進,可讓我們除了理解系統核心外,還多了不同看系統的角度.或許有天,真有那樣的機會,這些安全技術真的可以幫助到各位當下所需解決的棘手難題也說不定.
Jelly Bean可說是目前為止,最安全的Android作業系統.
首先,本文將著重在Android 4.1 Jelly Bean中新增的安全機制,而討論Android安全議題最佳的入口網站是Google的網頁 “Android Security Overview” (http://source.android.com/tech/security/),在這網頁中有說明過去以來每一版本Android在安全Features上的差異點,對Android安全議題有興趣的開發者,絕對不可以錯過.
總結來說,Android主要的安全特徵演進簡述如下表
Android版本 | 說明 |
1.5之後 | 1,加上Stack Overflow Protector的編譯器選項 -fstack-protector,可在程式碼編譯過程中,在函式進入與離開時,檢查所加上的Stack Pattern是否有被改變,若有就表示有Stack Overflow的汙染,進而就位觸發Abort,讓開發者有機會即早預防. (可參考 build/core/combo/linux-arm.mk中有關GLOBAL_CFLAGS編譯參數的設定.) 2, 採用 safe_iop (a safe integer operation library for C)的C函式庫. 並可提供相關乘法/除法/加法與減法的安全整數操作巨集(例如:safe_mul(),safe_div(),safe_add()與 safe_sub() ) (可參考網頁http://code.google.com/p/safe-iop/source/browse/tags/r0.3.0/README) 3, 採用dlmalloc記憶體配置與管理機制.這是由Doug Lea 從1987年開始開發的dlmalloc (全名為 Doug Lea’s Malloc).所有透過dlmalloc配置的Memory Chunk都會是8bytes Aignment,並根據Memory Chunk的使用狀況(例如:是否為Free)而有額外8或16 bytes的Chunk Header Struct成本.所配置的記憶體區塊小於256bytes就會置入SmallBin結構,如果大於256bytes就會導入TreeBin的結構中.預設的dlmalloc會以256kbytes作為要透過MMAP (以4kPage為單位配置)或BRK (延伸Data Segment的記憶體配置方式,效率較高)的判斷依據.但在Android系統中,則預設改為大於64kbytes會走MMAP,小於則走BRK. |
2.3(GinberBread)之後 | 1, 加上format-security編譯選項,用以在使用像是printf/scanf這類會用到字串型態的函式中,可針對非字串格式的參數進行警告.在編譯參數上,設定的方式為 “-Werror=format-security”,v讓屬於 format-security 的警告,在編譯過程中會以Error的方式呈現. 2, 加上GCC編譯參數–noexecstack , 用以讓程式載入後Stack會透過MMU設定不可執行. (Hardware-based No eXecute (NX)),如此可避免像是透過Stack-Overflow將程式碼注入的軟體攻擊問題. 3, mmap_min_addr 的設定是在Linux Kernel 2.6.23之後導入的機制,主要目的為避免非特權等級的使用者行程在非許可的低記憶體位址空間中產生記憶體映射,而發生Linux潛在的安全問題發生(像是跳到鄰近0×00000000附近的記憶體位址執行程式碼或讀取資料). 設定Kernel “vm.mmap_min_addr”後, 像是 Stack Top與MMAP的位址會被確保不低於所許可的記憶體範圍(mmap_min_addr).以最新的Jelly Bean來說,可以參考檔案”system/core/rootdir/init.rc”,其中有關於如下的設定 write /proc/sys/vm/mmap_min_addr 32768 也就是說在Jelly Bean中 mmap_min_addr的值預設為32768. (預設MMAP不小於32kbytes). |
4.0(ICS)之後 | 1,在ICS中就已經開啟 Address Space Layout Randomization (ASLR) 的支援,可以看到 ICS 原始碼中的檔案 ” system/core/rootdir/init.rc”, 有如下的設定 write /proc/sys/kernel/randomize_va_space 2 ASLR 參數設定為2,表示包括 MMAP, Stack, VDSO(Virtual Dynamic Shared Object)與Heap使用的BRK (To Change Data Segment Size),都會因此而進行Memory Address的Random. |
4.1 (Jelly Bean)之後 | 1,支援PIE (Position Independent Executable)讓應用程式能以ELF Dynamic Shared Object方式編譯後產生檔案格式 ,搭配ASLR可讓應用程式的起點不限於0×8000,可以由高位址到低位址,或是基於MMAP 0×40000000的起點開始載入應用程式. 2,透過GCC編譯選項 “-Wl,-z,relro -Wl,-z,now” 支援 Read-only relocations / immediate binding ,一般來說,.plt表格屬性會是RELRO (RELocation Read-Only),在載入到記憶體後會是落在唯讀的分頁中,但.got.plt 表格卻是Read/Writeable以便支援Linker Lazy Loading,加速應用程式載入的效率. 而開啟這功能就會關閉Linker Lazy Loading機制,讓原本可讀可寫的.got.plt表格,會在載入時就先填完.got.plt 表格,並分配到唯讀的分頁中,用以避免惡意程式修改.got.plt表格改變程式的執行流程,而導致不可預期的結果. 3,支援dmesg_restrict ,可避免系統訊息被未經可能的使用者讀取. 4,支援kptr_restrict enabled (avoid leaking kernel addresses),可避免系統核心模組的對應Symbol記憶體位址,被未經許可的操作讀取出來,而造成可能的安全攻擊操作. |
而除上述安全特徵演進外, Jelly Bean還悄悄的加入了對同裝置“多”使用者操作環境的預備,在Jelly Bean前,每個應用程式的使用者ID都會是 “app_” 開頭的命名,並不會有User ID 數字的進一步區分.但到了Jelly Bean,這樣的命名方式改為 “u0_a”開頭,也就是說預設的第一個使用者為User-ID 0,隨後再以所安裝的應用程式編號來加以區分.
而其它包括到Linux Kernel層級的安全特徵支援,筆者將在隨後章節加以介紹.
Read-only relocations / immediate binding
由於要能支援Lazy Loading機制,.got.plt (Global Offset Table & Procedure Linkage Table) Section 在應用程式載入後,還會維持可寫入的狀態,如下圖所示,程式所呼叫的printf函式,會透過.plt (Procedure Linkage Table)參考.got.plt中的_GLOBAL_OFFSET_TABLE_ 執行在應用程式啟動時,動態載入libc.so函式庫中的printf函式,透過這個Entry可以記錄當下動態載入printf函式的記憶體位址,以供後續執行. 但也因為這樣的需求存在,會讓惡意的應用程式有機會在RunTime運作過程中透過修改.got.plt表格而影響到正常應用程式的執行.
也因此,可以在編譯的過程加上“-Wl,-z,relro -Wl,-z,now” 選項, 其中 “–Wl” 是讓其後的編譯選項可被傳入給Link,而“-z,relro” 是指讓編譯器Enable .got.plt RELRO (RELocation Read-Only),“-z now” 是指讓編譯器關閉 lazy loading.
我們可以如下範例程式,來驗證對 .got.plt Entry的Read-Only分頁配置
int main (int argc, char *argv[]) { int *vpGOTPLT; if(argc<2) { printf("Plz enter target .got.plt entry address!\n"); return 0; } vpGOTPLT = (int *)strtol (argv[1], NULL, 16); vpGOTPLT[0] = 0×12345678; printf (".got.plt entry address: %xh\n", vpGOTPLT); return 0; } |
在不打開"Read-only relocations / immediate binding "的情況下編譯,
#gcc -g -o testA test.c
讀取.got.plt中屬於printf函式Entry的位址
#readelf -r testA|grep printf
08049734 00000407 R_386_JUMP_SLOT 00000000 printf
透過 gdb執行testA,並透過指令 “r” 把printf函式Entry位址0×08049734帶入給執行檔
# gdb testA
(gdb) r 0×08049734
Starting program: /home/loda/test/testA 0×08049734
Program received signal SIGSEGV, Segmentation fault.
0×12345678 in ?? ()
=>可以發現printf函式Entry被改為0×12345678,也就是說.got.plt被程式更改了.
(gdb)
再來我們重新以打開"Read-only relocations / immediate binding "的情況下編譯,
#gcc -Wl,-z,relro -Wl,-z,now -g -o testB test.c
讀取.got.plt中屬於printf函式Entry的位址
#readelf -r testB|grep printf
08049ff4 00000407 R_386_JUMP_SLOT 00000000 printf
透過 gdb執行testB,並透過指令 “r” 把printf函式Entry位址0x08049ff4帶入給執行檔
# gdb testB
(gdb) r 0x08049ff4
Starting program: /home/loda/test/testB 0x08049ff4
Program received signal SIGSEGV, Segmentation fault.
0x0804848e in main (argc=2, argv=0xbffff744) at test.c:14
14 vpGOTPLT[0] = 0×12345678;
=>可以發現因為.got.plt 為Read-Only所以對printf函式Entry寫入的動作就被Segment Fault阻擋了.
(gdb)
基於上述驗證的結果,下圖為在ICS上沒開啟"Read-only relocations / immediate binding "的記憶體示意圖,可以看到.got.plt Section會跟Writable Content同樣被配置到可寫入的分頁中.
但在Jelly Bean上基於"Read-only relocations / immediate binding "的開啟,可以看到.got.plt Section會被配置到Read-Only分頁中,而跟Writable Content分開.
基於這部份的說明,我們可知道Jelly Bean在記憶體分頁上,有相當不錯的安全層級改善.
Android 2.3 GB之後支援的Stack Hardware-based No eXecute (NX)
GCC 支援的noexecstack編譯屬性,可用以讓程式載入後Stack會透過MMU設定不可執行. (Hardware-based No eXecute (NX)),
如下為Android 2.2 Froyo的Process Maps
……….. afc00000-afc01000 r-xp 00000000 1f:00 356 /system/lib/libstdc++.so afc01000-afc02000 rwxp 00001000 1f:00 356 /system/lib/libstdc++.so afd00000-afd3f000 r-xp 00000000 1f:00 368 /system/lib/libc.so afd3f000-afd42000 rwxp 0003f000 1f:00 368 /system/lib/libc.so afd42000-afd4d000 rwxp afd42000 00:00 0 b0001000-b000c000 r-xp 00001000 1f:00 615 /system/bin/linker b000c000-b000d000 rwxp 0000c000 1f:00 615 /system/bin/linker b000d000-b0016000 rwxp b000d000 00:00 0 be8de000-be8f3000 rwxp befeb000 00:00 0 [stack] =>Stack這區塊的記憶體是設定為可執行的. “eXecute”. |
如下為Android 2.3 GingerBread的Process Maps
afc00000-afc01000 r-xp 00000000 1f:00 326 /system/lib/libstdc++.so afc01000-afc02000 rwxp 00001000 1f:00 326 /system/lib/libstdc++.so afd00000-afd40000 r-xp 00000000 1f:00 351 /system/lib/libc.so afd40000-afd43000 rwxp 00040000 1f:00 351 /system/lib/libc.so afd43000-afd4e000 rwxp afd43000 00:00 0 b0001000-b0009000 r-xp 00001000 1f:00 606 /system/bin/linker b0009000-b000a000 rwxp 00009000 1f:00 606 /system/bin/linker b000a000-b0013000 rwxp b000a000 00:00 0 bea0a000-bea1f000 rw-p befeb000 00:00 0 [stack] =>Stack這區塊的記憶體,可執行 “eXecute” 選項被關閉了. |
由此可知,要透過Stack-Overflow打入程式碼到目標手機或程式中執行的難度就大幅提高了.
GCC 支援的stack-protector機制簡述
以ARM GCC來說,如果有加上-fstack-protector參數,當一個函式有配置8bytes或大於8bytes以上的Array時 (例如 char vpTmp[8]),就會在編譯這函式時,在這函式的進入點與離開加入Stack是否有Overflow的檢查,例如
000084c4 <FuncC>: //函式起點. 84c4: e92d4800 push {fp, lr} 84c8: e28db004 add fp, sp, #4 …….. 84d4: e59f3060 ldr r3, [pc, #96] ; 853c <FuncC+0×78> 84d8: e5933000 ldr r3, [r3] //取得__stack_chk_guard的數字 84dc: e50b3008 str r3, [fp, #-8]//把__stack_chk_guard數字寫入Stack中. …….. 851c: e59f3018 ldr r3, [pc, #24] ; 853c <FuncC+0×78> 8520: e51b2008 ldr r2, [fp, #-8]//從Stack中取回__stack_chk_guard數字 8524: e5933000 ldr r3, [r3]//取得原本的__stack_chk_guard數字 8528: e1520003 cmp r2, r3 //比對是否Stack有被污染 852c: 0a000000 beq 8534 <FuncC+0×70>//如果比對成功,就完成函式FuncC的執行,順便返回 8530: ebffffb5 bl 840c <_init+0×50>//比對失敗,就呼叫函式__stack_chk_fail,進行後續錯誤處理!! 8534: e24bd004 sub sp, fp, #4 8538: e8bd8800 pop {fp, pc} 853c: 000108e0 andeq r0, r1, r0, ror #17 //儲存原本 __stack_chk_guard值在記憶體中的位址 //函式結尾. |
如上所示,就可以知道Stack Protector的保護機制與它施行的方式.
APP User-ID 命名的改變 (for Multi-User Usage)
相信大家最近一定也有看到這則新聞 “傳谷歌將為Android增加多用戶登錄功能” (可參考http://news.zol.com.cn/311/3113094.html),雖然這件事情尚未成為Jelly Bean一個主要的亮點,但如果有注意到Android User-ID based Access Control機制的開發者,一定也會注意到Android Application Sandbox 中所採取的User-ID命名規則在Jelly Bean中已經改變了.
主要的差異在於過往並沒有考慮不同使用者帳號登入的問題,只會以應用程式安裝的APP ID當做APP使用者ID的定義,但到了Jelly Bean中這些APP User –ID命名方式有了全然的改變.
首先參考ICS上應用程式安裝後,每個應用程式所對應的使用者帳號命名如下所示
app_6 146 37 158720 34664 ffffffff 400113c0 S com.android.inputmethod.latin radio 162 37 177336 35884 ffffffff 400113c0 S com.android.phone app_13 176 37 177672 55180 ffffffff 400113c0 S com.android.launcher system 197 37 167584 40008 ffffffff 400113c0 S com.android.settings app_1 218 37 177756 48400 ffffffff 400113c0 S android.process.acore app_21 241 37 162396 32096 ffffffff 400113c0 S com.android.calendar app_34 275 37 159648 31712 ffffffff 400113c0 S com.android.deskclock app_1 291 37 164348 34864 ffffffff 400113c0 S com.android.contacts app_11 304 37 160416 32108 ffffffff 400113c0 S com.android.providers.calendar app_7 333 37 160556 32752 ffffffff 400113c0 S android.process.media app_17 349 37 161956 31188 ffffffff 400113c0 S com.android.exchange app_27 363 37 170720 35940 ffffffff 400113c0 S com.android.email app_28 384 37 159412 32068 ffffffff 400113c0 S com.android.mms app_2 439 37 165780 34304 ffffffff 400113c0 S com.android.browser app_14 460 37 156692 29776 ffffffff 400113c0 S com.android.sharedstoragebackup |
可以看到每個應用程式都會對應到一個 “app_” 開頭的應用程式User-ID命名,而同樣的情況到了Jelly Bean時則變成如下的情況
u0_a28 206 37 184872 50852 ffffffff 40033a40 S com.android.systemui u0_a3 220 37 173036 35052 ffffffff 40033a40 S com.android.inputmethod.latin radio 233 37 181864 35064 ffffffff 40719100 R com.android.phone u0_a15 248 37 172876 34932 ffffffff 40032b64 S com.android.launcher system 271 37 180932 38708 ffffffff 40033a40 S com.android.settings u0_a13 297 37 176084 33016 ffffffff 40033c74 S android.process.acore u0_a13 330 37 177340 33016 ffffffff 40033a40 S com.android.contacts u0_a0 353 37 165644 25096 ffffffff 4075cda2 R com.android.providers.calendar |
會嘗試區分不同User-ID 所安裝的第幾個APP ID,而這樣的設計概念就可符合未來有可能支援的多使用者同裝置的使用行為.
防止 Kernel Message洩漏 (dmesg)
Linux Kernel DMESG 是我們在除錯Kernel核心或是模組時,一個非常好用的功能與機制,但在Linux Kernel 2.6.37 之後有支援新的Linux Kernel作業系統核心設定選項 “CONFIG_DMESG_RESTRICT” 可開啟用以限定是否一般使用者無法透過像dmesg指令去讀取Linux Kernel核心所吐出的訊息,而導致潛在的作業系統安全資訊外露.
可以透過設定
echo “0” > /proc/sys/kernel/dmesg_restrict
用以解除dmesg取得Linux Kernel核心訊息的限制,讓開發者可讀取即時的作業系統核心訊息.
echo “1” > /proc/sys/kernel/dmesg_restrict
可用以關閉透過dmesg取得Linux Kernel核心訊息的路徑
我們可以參考Jelly Bean 原始碼內的檔案 ”system/core/rootdir/init.rc” 有如下設定
write /proc/sys/kernel/dmesg_restrict 1
可知Jelly Bean中對DMESG的保護是預設打開的.
禁止 Kernel Symbol 位址洩漏 (kptr)
原本我們可以透過 “/proc/kallsyms”去查詢Linux Kernel Symbol所對應的記憶體位址,不論是出於除錯的或解決問題的目的,或是站在破解系統的角度,希望可以洞悉作業系統內部的祕密或有關的系統核心函式記憶體位址,讓有其他意圖的系統開發者可以有施力點,此時就會透過這些核心的記憶體位址來做為參考與評估依據.
但是在新版的Jelly Bean中,搭配新版的Linux Kernel就可以透過Proc檔案系統註冊的Device Node “/proc/sys/kernel/kptr_restrict” 來設定對於這機制的開啟與否,
如果要關閉這機制就可以透過
Echo “0” > /proc/sys/kernel/kptr_restrict
如果要開啟就可以透過
Echo “1” > /proc/sys/kernel/kptr_restrict
我們可以參考Jelly Bean 原始碼內的檔案 ”system/core/rootdir/init.rc” 有如下設定
write /proc/sys/kernel/kptr_restrict 2
可知Jelly Bean中對Kernel Symbol的保護是預設打開的.
PIE (Position independent executable)
基於GCC的PIE編譯選項,可讓應用程式執行時不用被限制在固定的記憶體起點,可以根據搭配的亂數記憶體起點機制,讓不同的Process行程有不同的載入執行位址,大幅度提高系統的安全性.
首先,可以透過像是arm-eabi-readelf 或是去CoreSourcery下載的arm-none-linux-gnueabi- readelf來對Android Jelly Bean PIE檔案進行比對,以便瞭解在ICS與Jelly Bean上兩邊檔案格式的差異.如下所示為我們透過Read-ELF工具比對後的結果,
Android 4.0.3 | Android 4.1.1 | |
/init | Type: EXEC (Executable file) Entry point address: 0x80c0 |
Type: EXEC (Executable file) Entry point address: 0x80e0 |
/system/bin/app_process | Type: EXEC (Executable file) Entry point address: 0x8bd0 |
Type: DYN (Shared object file) Entry point address: 0xb20 |
/system/bin/linker | Type: EXEC (Executable file) Entry point address: 0xb0001000 |
Type: DYN (Shared object file) Entry point address: 0x36e0 |
/system/bin/ rild | Type: EXEC (Executable file) Entry point address: 0x8ab0 |
Type: DYN (Shared object file) Entry point address: 0xa20 |
/system/xbin/showmap | Type: EXEC (Executable file) Entry point address: 0×8860 |
Type: DYN (Shared object file) Entry point address: 0x7a0 |
/system/lib/libc.so | Type: DYN (Shared object file) Entry point address: 0×0 |
Type: DYN (Shared object file) Entry point address: 0×0 |
/system/lib/libdvm.so | Type: DYN (Shared object file) Entry point address: 0×0 |
Type: DYN (Shared object file) Entry point address: 0×0 |
我們可以看到相對於Android 4.0 ICS,在Android 4.1 Jelly Bean上多數的應用程式都已經變成DYN (Shared Object File)檔案格式,也就是說這些ELF執行檔是可以被隨機載入到不同的記憶體位址中執行,而不像是Android 4.0 ICS上的執行檔,必須以0×8000分頁為起點被載入執行.
以ARM Linux來說,一般的ELF可執行檔載入記憶體的起點會是在0×8000,參考上表可以看到包括Android應用的原生碼載入程式app_process,或RIL Daemon或像是ShowMap這類原生工具程式,載入點都會是以0×8000為基礎去計算.但在Jelly Bean的環境下,除了少數像 init 啟動程式還維持0×8000為基礎的載入外,像是Java應用,或其他原生應用工具程式都會以 PIE 參數進行編譯,並產生Dynamic Loading的格式,
如下圖所示,為一般Arm Linux上Linux Kernel/Modules 與 應用程式的記憶體Mapping,
如下圖所示,則為基於PIE (Position independent executable)技術下,ARM ELF應用程式執行檔就可以根據需求跳脫0×8000執行檔載入記憶體的限制,
PIE機制是可以透過編譯選項關閉的. 在Disable PIE機制後,應用程式的起點就會重新回到0×8000,但也就因此少了基於PIE所增加的系統安全性優點了. 至於如何取捨,還是以開發者實際的需求為主.
ASLR (Address space layout randomization)
其實在ICS中,init.rc中就已經有如下的設定
write /proc/sys/kernel/randomize_va_space 2
也就是說早在ICS時,就已經有預備要對User-Space應用程式的MMAP, Stack, VDSO(Virtual Dynamic Shared Object)與Heap使用的BRK (Extend Data Segment)進行Memory Address Random的預備. 但直到了Jelly Bean,搭配改善後的Linker機制,才讓ASLR正式的在Android上完整的發揮實力.
提到ASLR通常最直接的兩個問題就是,
1,程式Fork出去後,新產生的Task由於他的父行程已經把相關的Symbol Run-Time Link好了,是否也會因此而被影響到呢?
=>答案是不會的,ASLR主要是在行程初始化時進行Random位址的選擇,所有從這父行程Fork出去的子行程,或是透過pthread產生的執行緒都會延續父行程的記憶體Mapping結果. (這也是讓問題最簡單處理的方式)
2,記憶體位址Random機制,是否會在每次記憶體Mmap時都做一次呢?
=>答案是不會的,只會在行程初始時Random一次MMAP起點與Stack位址,之後就不會再去反覆的Random.原因在於,就算沒有ASLR其實每次去觀察應用程式動態函式庫或是MMAP得到的位址,我們都會發現就算是同樣的操作行為,因為每次執行時多工環境下的種種因素,這些動態環境的差異就會導致這些記憶體位址的改變了,也因此只要在啟動時有一個Random,再加上後續的時間與動態環境變動,就會讓ASLR有非常棒的效果. (足以讓想要鎖定特定記憶體位址呼叫特定函式攻擊的手法無力可施).
目前的ASLR共有兩種記憶體Random Mapping的方式,
1, 由高位址往低位址開始Random
2, 由0×40000000 的起點1MB範圍內 開始往上 Random (缺點是會導致應用程式可用記憶體變少,所以……實際應用上是需要調整一下的..)
Jelly Bean 預設為路徑 1, 運作概念可參考下圖所示
如下圖所示,為由0×40000000 開始做Random Mapping的運作概念示意圖,而Random的範圍為0×40000000-0×40100000.
接下來不免俗的,還是要稍微帶一下程式碼,首先我們參考函式arch_pick_mmap_layout (source code in arch/arm/mm/mmap.c),可看到如下的程式碼,當目前Process的Flags有被標記為Randomize,且Process的Personality沒有Disable Randomize選項. 會先透過get_random_int取得一個32-bits整數,然後除256 (也就是1<<8)取餘數,如此會得到一個介於0-255的隨機數字. 搭配12-bits的PAGE_SHIFT,就可以得到一個介於1MB範圍內的記憶體Random範圍.
258 void arch_pick_mmap_layout(struct mm_struct *mm) 259 { 260 unsigned long random_factor = 0UL; 261 262 /* 8 bits of randomness in 20 address space bits */ 263 if ((current->flags & PF_RANDOMIZE) && 264 !(current->personality & ADDR_NO_RANDOMIZE)) 265 random_factor = (get_random_int () % (1 << 8)) << PAGE_SHIFT; 266 267 if (mmap_is_legacy ()) { 268 mm->mmap_base = TASK_UNMAPPED_BASE + random_factor; 269 mm->get_unmapped_area = arch_get_unmapped_area; 270 mm->unmap_area = arch_unmap_area; 271 } else { 272 mm->mmap_base = mmap_base(random_factor); 273 mm->get_unmapped_area = arch_get_unmapped_area_topdown; 274 mm->unmap_area = arch_unmap_area_topdown; 275 } 276 } |
同樣的,另一個也會一起Random的就是Stack,可以參考如下randomize_stack_top (Source Code in fs/binfmt_elf.c)程式碼,其中STACK_RND_MASK在檔案”fs/binfmt_elf.c”中的宣告如下所示
#define STACK_RND_MASK (0x7ff >> (PAGE_SHIFT – 12)) /* 8MB of VA */
(由此可知,Stack Top隨機Random範圍是8MB.)
540 static unsigned long randomize_stack_top(unsigned long stack_top) 541 { 542 unsigned int random_variable = 0; 543 544 if ((current->flags & PF_RANDOMIZE) && 545 !(current->personality & ADDR_NO_RANDOMIZE)) { 546 random_variable = get_random_int() & STACK_RND_MASK; 547 random_variable <<= PAGE_SHIFT; 548 } 549 #ifdef CONFIG_STACK_GROWSUP 550 return PAGE_ALIGN(stack_top) + random_variable; 551 #else 552 return PAGE_ALIGN(stack_top) - random_variable; 553 #endif 554 } |
如果想要關閉ASLR的核心運作機制,可以透過
echo “0” > /proc/sys/kernel/randomize_va_space
來進行關閉.
結語
本文簡要的說明最新Android Jelly Bean安全機制的演進,而這些安全機制其實也隱含了對於作業系統額外的執行成本.舉例來說,以Kernel Memory Management Code Path路徑來看,當記憶體要改為Top-Down的計算方式,所要執行配置記憶體路徑也會稍微變長,增加系統的執行成本.
但如果是站在系統安全的角度去看問題,越能以出於一般人Down-Top思維的概念去設計一個防堵安全機制的作業系統,其實就越讓這個作業系統的安全組態配置立於不敗之地.畢竟,想要破解它的人思維就必須要透過傳統Down-Top思維,改用以Top-Down + Random的角度去看問題.
從古至今,絕對沒有一個作業系統是可以聲稱100%安全的,原因在於人的思想與創造力總是遠超過當下看文章或寫文章的你我.但也因為如此,Linux Kernel作業系統才會不斷的演進,用更多新的機制去補強這些核心的機制,讓安全問題可以在每一版的作業系統核心中,取得當時最適當的解決.
本文到此結束,希望對各位有所幫助,加油!!!!