標籤:des android style blog http io ar os 使用
[轉]通過apk簽名使應用程式有系統許可權 (2013-01-08 13:40:50)
轉載▼
標籤:
it
分類: Android
出處:http://blog.csdn.net/doom66151/article/details/7085464
問題:
系統預裝軟體,需要訪問一些設定檔,設定檔的owner都是設定為system。所以應用程式需要有授權才可以讀寫。
google搜尋找到以下文章,解決了這個問題。
以下內容解釋了:
android系統許可權規則?
如何使應用程式擷取系統許可權?
apk簽名的過程?
apk簽名的原理?
#################################################################
以下內容轉自:
http://hi.baidu.com/donghaozheng/blog/item/23ca75ec0028bbdc2e2e21c5.html
Android系統是運行在Linux核心上的,Android與Linux分別有自己的一套嚴格的安全及許可權機制,
很多像我這樣的新手,尤其是習慣了windows低安全限制的使用者,很容易在這方面弄混淆,下面是我總結的Android系統許可權相關的內容,
作為這段時間對android許可權學習的總結,也希望能對大家有所協助,不正確之處請指出。
首先分清兩個概念:
要區分apk運行時的擁有的許可權與在檔案系統上被訪問(讀寫執行)的許可權兩個概念。
apk程式是運行在虛擬機器上的,對應的是Android獨特的許可權機制,只有體現到檔案系統上時才使用linux的使用權限設定。
(一)linux檔案系統上的許可權
-rwxr-x--x system system 4156 2010-04-30 16:13 test.apk
代表的是相應的使用者/使用者組及其他人對此檔案的存取權限,與此檔案運行起來具有的許可權完全不相關。
比如上面的例子只能說明system使用者擁有對此檔案的讀寫執行許可權;system組的使用者對此檔案擁有讀、執行許可權;其他人對此檔案只具有執行許可權。
而test.apk運行起來後可以幹哪些事情,跟這個就不相關了。
千萬不要看apk檔案系統上屬於system/system使用者及使用者組,或者root/root使用者及使用者組,就認為apk具有system或root許可權。
(二)Android的許可權規則
(1)Android中的apk必須簽名
這種簽名不是基於權威認證的,不會決定某個應用允不允許安裝,而是一種自我簽署憑證。
重要的是,android系統有的許可權是基於簽名的。比如:system等級的許可權有專門對應的簽名,簽名不對,許可權也就擷取不到。
預設產生的APK檔案是debug簽名的。
擷取system許可權時用到的簽名,見:如何使Android應用程式擷取系統許可權
(2)基於UserID的進程層級的安全機制
大家都知道,進程有獨立的地址空間,進程與進程間預設是不能互相訪問的,是一種很可靠的保護機制。
Android通過為每一個安裝在裝置上的包(apk)分配唯一的linux userID來實現,名稱為"app_"加一個數字,比如app_43
不同的UserID,運行在不同的進程,所以apk之間預設便不能相互訪問。
Android提供了如下的一種機制,可以使兩個apk打破前面講的這種壁壘。
在AndroidManifest.xml中利用sharedUserId屬性給不同的package分配相同的userID,通過這樣做,兩個package可以被當做同一個程式,
系統會分配給兩個程式相同的UserID。當然,基於安全考慮,兩個package需要有相同的簽名,否則沒有驗證也就沒有意義了。
(這裡補充一點:並不是說分配了同樣的UserID,兩程式就運行在同一進程, 下面為PS指令摘取的,
顯然,system、app_2分別對應的兩個進程的PID都不同,不知Android到底是怎樣實現它的機制的)
User PID PPID
system 953 883 187340 55052 ffffffff afe0cbcc S system_server
app_2 1072 883 100264 19564 ffffffff afe0dcc4 S com.android.inputmethod.
system 1083 883 111808 23192 ffffffff afe0dcc4 S android.process.omsservi
app_2 1088 883 156464 45720 ffffffff afe0dcc4 S android.process.acore
(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應用程式擷取系統許可權),
至於是否有root級的,如何提升至root級不得而知,知道的朋友勞煩告知,感激不盡。
##############################################################################
以下整理自:《如何使Android應用程式擷取系統許可權》
http://hi.baidu.com/donghaozheng/blog/item/30a00d4f9fca873baec3ab69.html
1. 在應用程式的AndroidManifest.xml中的manifest節點中加入android:sharedUserId="android.uid.system"這個屬性。
2. 使用eclipse編譯出apk檔案,但是這個apk檔案是不能用的。
3. 使用目標系統的platform密鑰來重新給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運行在一個進程中,這樣可以共用資料,應該會很有用的。
總結簡單三步
>>>>>>>使用platform金鑰組apk進行簽名
1 建立一個目錄
2 整理必須的檔案:
密鑰檔案:進入build/target/product/security ,找到【platform.pk8】和【platform.x509.pem】系統預設使用的密鑰。
signapk工具:進入build\tools\signapk找到SignApk.java,運行 javac或者直接mm編譯。
signapk.jar的源碼位置build/tools/signapk,編譯以後產生的檔案路徑:out/host/linux-x86/framework/signapk.jar
3.執行命令: java -jar signapk.jar platform.x509.pem platform.pk8 your.apk your_signed.apk
這條命令的意義是:通過signapk.jar這個可執行jar包,以“platform.x509.pem”這個公開金鑰檔案和“platform.pk8”這個私密金鑰檔案對“your.apk”進行簽名,簽名後的檔案儲存為“your_signed.apk”。
對於此處所使用的私密金鑰和公開金鑰的產生方式,這裡就不做進一步介紹了。可以參考 http://blog.csdn.net/absurd/article/details/5002763
#############################################################################
程式簽名的具體過程和原理
此筆記主要參考了 下面的文章整理出來的。
http://www.blogjava.net/zh-weir/archive/2011/07/19/354663.html
這裡還有一篇文章將apk破解的,也許對你的理解有協助。
http://www.blogjava.net/zh-weir/archive/2011/06/11/352099.html
signapk.jar是Android源碼包中的一個簽名工具。由於Android是個開源項目,所以我們可以直接找到signapk.jar的源碼,路徑為/build/tools/signapk/SignApk.java。通過閱讀signapk源碼,我們可以理清簽名APK包的整個過程。
簽名好的APK包中多了一個叫做META-INF的檔案夾。裡面有三個檔案,分別名為MANIFEST.MF、CERT.SF和CERT.RSA。signapk.jar就是產生了這幾個檔案.
1、 產生MANIFEST.MF檔案:
程式遍曆update.apk包中的所有檔案(entry),對非檔案夾非簽名檔案的檔案,逐個產生SHA1的數位簽章資訊,再用Base64進行編碼。具體代碼見這個方法:
private static Manifest addDigestsToManifest(JarFile jar)
關鍵代碼如下:
1 for (JarEntry entry: byName.values()) {
2 String name = entry.getName();
3 if (!entry.isDirectory() && !name.equals(JarFile.MANIFEST_NAME) &&
4 !name.equals(CERT_SF_NAME) && !name.equals(CERT_RSA_NAME) &&
5 (stripPattern == null ||!stripPattern.matcher(name).matches())) {
6 InputStream data = jar.getInputStream(entry);
7 while ((num = data.read(buffer)) > 0) {
8 md.update(buffer, 0, num);
9 }
10 Attributes attr = null;
11 if (input != null) attr = input.getAttributes(name);
12 attr = attr != null ? new Attributes(attr) : new Attributes();
13 attr.putValue("SHA1-Digest", base64.encode(md.digest()));
14 output.getEntries().put(name, attr);
15 }
16 }
之後將產生的簽名寫入MANIFEST.MF檔案。關鍵代碼如下:
1 Manifest manifest = addDigestsToManifest(inputJar);
2 je = new JarEntry(JarFile.MANIFEST_NAME);
3 je.setTime(timestamp);
4 outputJar.putNextEntry(je);
5 manifest.write(outputJar);
這裡簡單介紹下SHA1數位簽章。簡單地說,它就是一種安全雜湊演算法,類似於MD5演算法。它把任意長度的輸入,通過散列演算法變成固定長度的輸出(這裡我們稱作“摘要資訊”)。你不能僅通過這個摘要資訊複原原來的資訊。另外,它保證不同資訊的摘要資訊彼此不同。因此,如果你改變了apk包中的檔案,那麼在apk安裝校正時,改變後的檔案摘要資訊與MANIFEST.MF的檢驗資訊不同,於是程式就不能成功安裝。
2、 產生CERT.SF檔案:
對前一步產生的Manifest,使用SHA1-RSA演算法,用私密金鑰進行簽名。關鍵代碼如下:
1 Signature signature = Signature.getInstance("SHA1withRSA");
2 signature.initSign(privateKey);
3 je = new JarEntry(CERT_SF_NAME);
4 je.setTime(timestamp);
5 outputJar.putNextEntry(je);
6 writeSignatureFile(manifest,
7 new SignatureOutputStream(outputJar, signature));
RSA是一種非對稱式加密演算法。用私密金鑰通過RSA演算法對摘要資訊進行加密。在安裝時只能使用公開金鑰才能解密它。解密之後,將它與未加密的摘要資訊進行對比,如果相符,則表明內容沒有被異常修改。
3、 產生CERT.RSA檔案:
產生MANIFEST.MF沒有使用密鑰資訊,產生CERT.SF檔案使用了私密金鑰檔案。那麼我們可以很容易猜測到,CERT.RSA檔案的產生肯定和公開金鑰相關。
CERT.RSA檔案中儲存了公開金鑰、所採用的密碼編譯演算法等資訊。核心代碼如下:
1 je = new JarEntry(CERT_RSA_NAME);
2 je.setTime(timestamp);
3 outputJar.putNextEntry(je);
4 writeSignatureBlock(signature, publicKey, outputJar);
其中writeSignatureBlock的代碼如下:
1 private static void writeSignatureBlock(
2 Signature signature, X509Certificate publicKey, OutputStream out)
3 throws IOException, GeneralSecurityException {
4 SignerInfo signerInfo = new SignerInfo(
5 new X500Name(publicKey.getIssuerX500Principal().getName()),
6 publicKey.getSerialNumber(),
7 AlgorithmId.get("SHA1"),
8 AlgorithmId.get("RSA"),
9 signature.sign());
10
11 PKCS7 pkcs7 = new PKCS7(
12 new AlgorithmId[] { AlgorithmId.get("SHA1") },
13 new ContentInfo(ContentInfo.DATA_OID, null),
14 new X509Certificate[] { publicKey },
15 new SignerInfo[] { signerInfo });
16
17 pkcs7.encodeSignedData(out);
18 }
好了,分析完APK包的簽名流程,我們可以清楚地意識到:
1、 Android簽名機制其實是對APK包完整性和發布機構唯一性的一種校正機制。
2、 Android簽名機制不能阻止APK包被修改,但修改後的再簽名無法與原先的簽名保持一致。(擁有私密金鑰的情況除外)。
3、 APK包加密的公開金鑰就打包在APK包內,且不同的私密金鑰對應不同的公開金鑰。換句話言之,不同的私密金鑰簽名的APK公開金鑰也必不相同。所以我們可以根據公開金鑰的對比,來判斷私密金鑰是否一致。
[轉]通過apk簽名使應用程式有系統許可權