由於Android底層是Linux內核,故了解了Linux的權限管理后就可以知道ROOT的原理,具體可以訪問《Android系統權限和root權限》一文,而一般的Androd下的su命令只支持在ROOT用戶和SHELL用戶下才有權限讓程序以root用戶身份運行,其實看完Android源碼下的system/extras/su/su.c代碼即可清楚,而我們繞過了其中的當前運行用戶判斷來讓所有的用戶都可以將以自己身份運行的程序嘗試去設置以root用戶身份運行,即運行su命令不需判斷運行程序的當前用戶的uid,直接可以設置相應的uid和gid,而我們所做的只是修改su.c文件,把如下代碼屏蔽即可。 myuid = getuid(); if (myuid != AID_ROOT && myuid != AID_SHELL) { fprintf(stderr,"su: uid %d not allowed to su\n", myuid); return 1; }
使用android 手機很多情況下需要root權限,關於root權限獲取的原理可以參考以下文章: 1、雲中漫步博客: Android系統root破解原理分析 http://my.unix-center.net/~Simon_fu/?p=1069 2、雲中漫步 ? Android系統root破解原理分析(續) http://my.unix-center.net/~Simon_fu/?p=1100 3、zergRush - 隨想專欄 - 博客頻道 - CSDN.NET http://blog.csdn.net/tomken_zhang/article/details/6866260 4、zergRush (補充) - 隨想專欄 - 博客頻道 - CSDN.NET http://blog.csdn.net/tomken_zhang/article/details/6870104 5、結合init源碼剖析android root提權漏洞(CVE-2010-E... http://bbs.pediy.com/showthread.php?t=139738 6、Android提權代碼zergRush分析 | i, Claud http://blog.claudxiao.net/2011/10/zergrush ......... 原理是利用了android的兩個提權漏洞: CVE-2010-EASY 和 ZergRush。 我把大概原理簡單說說: 1, CVE-2010-EASY : linux的內核的模塊化程度很高,很多功能模塊是需要到時候再加載,在 android中由init進程來管理這些的。但是這個init進程不會檢測發給它的指令的來源,不管是內核發送的,還是用戶發送的,它都執行不誤,會順從的去加載或卸載一些模塊,而加載的模塊都是以root身份運行的。因此你可以給它准備一個精心制作的功能模塊(ko文件),然后觸發相應的加載條件,比如熱拔插、開關wifi等等, 該功能模塊運行后,會生成 /data/local/tmp/rootshell 一個帶s位的shell。 2,ZergRush原理: 具有root權限的vold進程使用了libsysutils.so庫,該庫有個函數存在棧溢出,因此可以root權限執行輸入的shellcode。 印象中好像還有個提權漏洞,原理大概是: 某root權限的進程在干完一些事情后會自動降權到普通權限,但是如果普通權限的進程數滿了,它就降權不成功,接着它就堂而皇之的以root權限運行了,好像是adbd進程。 扯了半天還沒扯到superuser.apk,這個程序是root成功后,專門用來管理root權限使用的,防止被惡意程序濫用。我一直很好奇他是怎么做到這一點的, 源碼地址: http://superuser.googlecode.com/svn/trunk 這個源碼有點老,不過感覺原理和最新的superuser應該是差不多的。 帶着兩個問題我們來分析源碼: 1、superuser是怎么知道誰想用root權限? 2、superuser是如何把用戶的選擇告訴su程序的那? 即superuser和su程序是如何通訊的,他們倆位於不通的時空,一個在java虛擬機中,一個在linux的真實進程中。 共有兩個active: SuperuserActivity 和 SuperuserRequestActivity ,呵呵比較簡單。 其中SuperuserActivity 主要是用來管理白名單的,就是記住哪個程序已經被允許使用root權限了,省的每次用時都問用戶。 SuperuserRequestActivity 就是用來詢問用戶目前有個程序想使用root權限,是否允許,是否一直允許,即放入白名單。 這個白名單比較關鍵,是一個sqlite數據庫文件,位置: /data/data/com.koushikdutta.superuser/databases/superuser.sqlite 看完一開始我列的文章,就能明白root的本質就是往 /system/bin/ 下放一個帶s位的,不檢查調用者權限的su文件。普通程序可以調用該su來運行root權限的命令。superuser.apk中就自帶了一個這樣的su程序。一開始superuser會檢測/system/bin/su是否存在,是否是老子自個放進去的su: File su = new File("/system/bin/su"); // 檢測su文件是否存在,如果不存在則直接返回 if (!su.exists()) { Toast toast = Toast.makeText(this, "Unable to find /system/bin/su.", Toast.LENGTH_LONG); toast.show(); return; } //檢測su文件的完整性,比較大小,太省事了吧 //如果大小一樣,則認為su文件正確,直接返回了事。 if (su.length() == suStream.available()) { suStream.close(); return; // } // 如果檢測到/system/bin/su 文件存在,但是不對頭,則把自帶的su先寫到"/data/data/com.koushikdutta.superuser/su" // 再寫到/system/bin/su。 byte[] bytes = new byte[suStream.available()]; DataInputStream dis = new DataInputStream(suStream); dis.readFully(bytes); FileOutputStream suOutStream = new FileOutputStream("/data/data/com.koushikdutta.superuser/su"); suOutStream.write(bytes); suOutStream.close(); Process process = Runtime.getRuntime().exec("su"); DataOutputStream os = new DataOutputStream(process.getOutputStream()); os.writeBytes("mount -oremount,rw /dev/block/mtdblock3 /system\n"); os.writeBytes("busybox cp /data/data/com.koushikdutta.superuser/su /system/bin/su\n"); os.writeBytes("busybox chown 0:0 /system/bin/su\n"); os.writeBytes("chmod 4755 /system/bin/su\n"); os.writeBytes("exit\n"); os.flush(); 上面提到的su肯定是動過手腳的, 我 最納悶的就是有進程使用root權限,superuser是怎么知道的,看完su 的代碼明白了,關鍵是句: sprintf(sysCmd, "am start -a android.intent.action.MAIN -n com.koushikdutta.superuser/com.koushikdutta.superuser.SuperuserRequestActivity --ei uid %d --ei pid %d > /dev/null", g_puid, ppid); if (system(sysCmd)) return executionFailure("am."); 原理是am命令,看了下am的用法,明白了: usage: am [subcommand] [options] start an Activity: am start [-D] [-W] <INTENT> -D: enable debugging -W: wait for launch to complete start a Service: am startservice <INTENT> send a broadcast Intent: am broadcast <INTENT> start an Instrumentation: am instrument [flags] <COMPONENT> -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT) -e <NAME> <VALUE>: set argument <NAME> to <VALUE> -p <FILE>: write profiling data to <FILE> -w: wait for instrumentation to finish before returning start profiling: am profile <PROCESS> start <FILE> stop profiling: am profile <PROCESS> stop <INTENT> specifications include these flags: [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>] [-c <CATEGORY> [-c <CATEGORY>] ...] [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...] [--esn <EXTRA_KEY> ...] [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...] [-e|--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...] [-n <COMPONENT>] [-f <FLAGS>] [--grant-read-uri-permission] [--grant-write-uri-permission] [--debug-log-resolution] [--activity-brought-to-front] [--activity-clear-top] [--activity-clear-when-task-reset] [--activity-exclude-from-recents] [--activity-launched-from-history] [--activity-multiple-task] [--activity-no-animation] [--activity-no-history] [--activity-no-user-action] [--activity-previous-is-top] [--activity-reorder-to-front] [--activity-reset-task-if-needed] [--activity-single-top] [--receiver-registered-only] [--receiver-replace-pending] [<URI>] 還有個疑點,就是su怎么知道用戶是允許root權限還是反對那? 原來是上面提到的白名單起來作用,superuser把用戶的選擇放入 : /data/data/com.koushikdutta.superuser/databases/superuser.sqlite 數據庫中,然后su進程再去讀該數據庫來判斷是否允許。 static int checkWhitelist() { sqlite3 *db; int rc = sqlite3_open_v2(DBPATH, &db, SQLITE_OPEN_READWRITE, NULL); if (!rc) { char *errorMessage; char query[1024]; sprintf(query, "select * from whitelist where _id=%d limit 1;", g_puid); struct whitelistCallInfo callInfo; callInfo.count = 0; callInfo.db = db; rc = sqlite3_exec(db, query, whitelistCallback, &callInfo, &errorMessage); if (rc != SQLITE_OK) { sqlite3_close(db); return 0; } sqlite3_close(db); return callInfo.count; } sqlite3_close(db); return 0; } 至此分析結束,回頭看看,原來如此,又想起初中老師的一句話:會者不難,難者不會。 其實原理都不難,只要用心。
Android的應用程序入口肯定是java程序。應用程序的啟動者是由系統臨時根據Androidmanifest.xml中定義的權限而創建的臨時用戶。而不像linux那樣是使用登陸者的身份啟動,從而使得進程具有登陸者的所有權限。這也是android的安全機制之一。 新的權限機制也帶來新的問題,android給應用程序的權限是按功能來分,java雖然可以訪問文件系統。但由於應用程序本身是臨時用戶啟動,這個臨時用戶權限十分有限。因此誕生了<越獄/root機器>這樣的產物。 其實root機器不是真正能讓你的應用程序具有root權限。它原理就跟linux下的像sudo這樣的命令。在系統的bin目錄下放個su程序並屬主是root並有suid權限。則通過su執行的命令都具有root權限。 Su的源代碼網上也有,有興趣的同學去google下。 當然使用臨時用戶權限想把su拷貝的/system/bin目錄並改屬性並不是一件容易的事情。這里用到2個工具跟2個命令。工具就是busybox。不熟悉的同學可以去網上google下。這個太有名了我就不多說了。 把busybox拷貝到你有權限訪問的目錄然后給他賦予4755權限,你就可以用它做很多事了。 當然busybox只能不能提升權限,真正提升權限的是ratc這個程序,這個程序中一鍵root包里面可以找到,作用是rooting在adb的shell。 網上介紹Ratc的文章不多,它是rage against the cage 的縮寫。是真正的提升權限的破解程序。雖然我沒看過源代碼,但估計是利用adb源代碼部分內容來實現的,原理估計跟模擬器使用adb shell登陸可以獲得root shell差不多。(因為它運行需要adb連接才會成功)。 使用busybox前先運行ratc,這樣運行busybox的UID將是0,也就是root。 首先把system目錄改成可讀性的:busybox mount -o remount,rw /system, 當然你還不能改下面的文件,因為system下文件的所有者都不是你。 但你可以偷梁換柱把system下的目錄給換掉。 使用命令Busybox mount -t tmpfs none /system/xbin,呵呵這下xbin目錄你隨便寫了。 將su跟busybox弄過去cp /data/data/xxx/su /system/xbin。然后賦權限chmod 4755 /system/xbin/su。 然后使目錄生效busybox --install -s /system/xbin, 別忘善后busybox mount -o remount,ro /system去掉system可寫。 這樣只是臨時的,只能用su跟busybox能執行一些原來系統沒有權限執行的命令而已。當系統重啟后/system/xbin又變為原來的文件。真正要改系統的話需要自己寫內核代碼(相當於windows的驅動程序)。內核文件擁有所有權限。使用busybox命令insmod /data/data/xxx/xxx.ko裝載內核文件,你想干嘛就可以干嘛了。 當然我們不是搞破解的沒必要去改別人的機器,我們只是想讓自己應用程序具有root權限而已。所以臨時的su就可以了。我們用c++寫一個可執行文件。使用socket可以跟java的程序通訊。然后將需要使用root權限才能執行的代碼放在c++程序里,然后java程序中創建新的su進程,將c++程序帶全路徑作為參數1。啟動后就可以通過socket調用c++函數去執行你想干的事了。 最后程序執行完了別忘了善后busybox umount /system/xbin。 最后說說要注意的事情,如果機器已經root過的話就不需要做這些事情了,但root過的機器都有裝有個權限管理的程序。會彈出對話框。但這個程序管理能力有限,如果不想讓他彈出的話。也許可以通過改su文件名來解決。有興趣的同學不妨試試。