手把手教你逆向分析 Android 程序(連載二)

1. Android 的簽名保護(hù)機(jī)制到底是什么?

Android 系統(tǒng)禁止更新安裝簽名不一致的 Apk晃听,如果我們修改了 Apk 又用別的簽名文件簽名唇聘,肯定是不一致的。

我們從簽名工具 autoSign 分析讨韭,看一下 sign.bat 文件內(nèi)容:

-----------------------------------

@ECHO OFF

Echo Auto-sign Created By Dave Da illest 1

Echo Update.zip is now being signed and will be renamed to update_signed.zip

java -jar signapk.jar testkey.x509.pem testkey.pk8 update.apk update_signed.apk

Echo Signing Complete

Pause

EXIT

-----------------------------------

看一下 java -jar signapk.jar testkey.x509.pem testkey.pk8 update.apk update_signed.apk 這行的意義:

以testkey.x509.pem 這個(gè)公鑰文件和 testkey.pk8 這個(gè)私鑰文件對(duì) update.apk 進(jìn)行簽名脂信,簽名后保存為 update_signed.apk。

我們可以看到簽名前和簽名后比較透硝,簽名后的文件中多了一個(gè)文件夾“META-INF”狰闪,里面有三個(gè)文件 MANIFEST.MF 、 CERT.SF 濒生、 CERT.RSA埋泵。

我們通過 jd-gui 工具打開 signapk.jar,找到 main 函數(shù)罪治,通過這個(gè)函數(shù)跟蹤代碼


1.addDigestsToManifest 這個(gè)函數(shù)丽声,遍歷 Apk 中所有文件,對(duì)非文件夾非簽名文件的文件逐個(gè)生成 SHA1 數(shù)字簽名信息觉义,再 base64 編碼雁社。

然后再寫入 MANIFEST.MF 文件中,生成文件如下:

-----------------------------------

Manifest-Version: 1.0

Created-By: 1.0 (Android)

Name: res/drawable-xhdpi/ic_launcher.png

SHA1-Digest: AfPh3OJoypH966MludSW6f1RHg4=

Name: AndroidManifest.xml

SHA1-Digest: NaPhUBH5WO7uGk/CfRu/SHsCvW0=

Name: res/drawable-mdpi/ic_launcher.png

SHA1-Digest: RRxOSvpmhVfCwiprVV/wZlaqQpw=

Name: res/drawable-hdpi/ic_launcher.png

SHA1-Digest: Nq8q3HeTluE5JNCBpVvNy3BXtJI=

Name: res/layout/activity_main.xml

SHA1-Digest: kxwMyILwF2K+n9ziNhcQqcCGWIU=

Name: resources.arsc

SHA1-Digest: q7Ystu6WoSWih53RGKXtE3LeTdc=

Name: classes.dex

SHA1-Digest: Ao1WOs5PXMxsWTDsjSijS2tfnHo=

Name: res/drawable-xxhdpi/ic_launcher.png

SHA1-Digest: GVIfdEOBv4gEny2T1jDhGGsZOBo=

-----------------------------------

SHA1 生成的摘要信息晒骇,如果你修改了某個(gè)文件霉撵,Apk 安裝校驗(yàn)時(shí)磺浙,取到的該文件的摘要與 MANIFEST.MF 中對(duì)應(yīng)的摘要不同,則安裝不成功徒坡。

2.接下來對(duì)之前生成的 manifest 使用 SHA1withRSA 算法撕氧, 用私鑰簽名,writeSignatureFile 這個(gè)函數(shù)喇完,最后生成 CERT.SF 文件呵曹,如下:

-----------------------------------

Signature-Version: 1.0

Created-By: 1.0 (Android)

SHA1-Digest-Manifest: pNZ9UXN9GMqTgqAwKD6uEN6aD34=

Name: res/drawable-xhdpi/ic_launcher.png

SHA1-Digest: cIga++hy5wqjHl9IHSfbg8tqCug=

Name: AndroidManifest.xml

SHA1-Digest: oRzzLkwuvxC78suvJcAEvTqcjSA=

Name: res/drawable-mdpi/ic_launcher.png

SHA1-Digest: VY7kOF8E3rn8EUTvQC/DcBEN6kQ=

Name: res/drawable-hdpi/ic_launcher.png

SHA1-Digest: stS7pUucSY0GgAVoESyO3Y7SanU=

Name: res/layout/activity_main.xml

SHA1-Digest: Yr3img6SqiKB+1kwcg/Fga2fwcc=

Name: resources.arsc

SHA1-Digest: j1g8I4fI9dM9hAFKEtS9dHsqo5E=

Name: classes.dex

SHA1-Digest: Sci9MmGXNGnZ1d04rCrEEV7MWn4=

Name: res/drawable-xxhdpi/ic_launcher.png

SHA1-Digest: KKqaLh/DVvFp+v1KoaDw7xETvrI=

-----------------------------------

用私鑰通過 RSA 算法對(duì) manifest 里的摘要信息進(jìn)行加密,安裝的時(shí)候只能通過公鑰解密何暮,解密之后才能獲得正確的摘要奄喂,再對(duì)比。

3.最后就是如何生成 CERT.RSA海洼,打開這個(gè)文件看到的是亂碼跨新,說明整個(gè)文件都被編碼加密了,而且這個(gè)文件和公鑰有關(guān)坏逢,從源碼中看出他是通過 PKCS7 將整個(gè)文件加密了域帐。

總結(jié):1.簽名只是對(duì)完整性和簽名發(fā)布機(jī)構(gòu)的校驗(yàn)機(jī)制 2.不能阻止 Apk 被修改,只是簽名無(wú)法保持一致 3.不同私鑰對(duì)應(yīng)著不同的公鑰是整,實(shí)質(zhì)上不同的公鑰就代表了不同的簽名肖揣。

2. Android 系統(tǒng)如何獲取簽名

我們從獲取下面一段代碼開始分析:

@Override

public PackageManager getPackageManager() {

if (mPackageManager != null) {

return mPackageManager ;

}

IPackageManager pm = ActivityThread.getPackageManager ();

if (pm != null) {

// Doesn't matter if we make more than one instance.

return (mPackageManager = new ApplicationPackageManager(this, pm));

}

return null ;

}

可以看到 ActivityThread 中方法 getPackageManager 獲取? IPackageManager。

繼續(xù)看 ActivityThread 的代碼:

public static IPackageManager getPackageManager() {

if (sPackageManager != null) {

//Slog.v("PackageManager", "returning cur default = " + sPackageManager);

return sPackageManager ;

}

IBinder b = ServiceManager.getService("package");

//Slog.v("PackageManager", "default service binder = " + b);

sPackageManager = IPackageManager.Stub.asInterface(b);

//Slog.v("PackageManager", "default service = " + sPackageManager);

return sPackageManager;

}

看源碼知道是通過 ServiceManager.getService(“package”);獲取 PackageManagerService 并得到 IBinder對(duì)象浮入,然后通過? asInterface 函數(shù)取得接口類 IPackageManager 實(shí)例龙优。

然后作為參數(shù)構(gòu)造 ApplicationPackageManager,再看 ApplicationPackageManager 這個(gè)類里的方法:

@Override

public PackageInfo getPackageInfo(String packageName, int flags)

throws NameNotFoundException {

try {

PackageInfo pi = mPM.getPackageInfo(packageName, flags, mContext.getUserId());

if (pi != null) {

return pi;

}

} catch (RemoteException e) {

throw new RuntimeException( "Package manager has died" , e);

}

throw new NameNotFoundException(packageName);

}

這里的mPM就是上面構(gòu)造函數(shù)傳進(jìn)來的 IPackageManager事秀,就可以調(diào)用 PackageManagerService 的方法了彤断。

下圖是 PackagerManager 靜態(tài)類結(jié)構(gòu)圖:


ok,看完上圖的結(jié)構(gòu)圖易迹,繼續(xù)跟代碼 宰衙,看到這里是 mPM 繼續(xù)調(diào)用的getPackageInfo(代理模式),通過進(jìn)程通信到 PackageManagerService 中執(zhí)行響應(yīng)操作:

@Override

public PackageInfo getPackageInfo(String packageName, int flags, int userId) {

if (!sUserManager.exists(userId)) return null;

enforceCrossUserPermission(Binder.getCallingUid (), userId, false, "get package info");

// reader

synchronized (mPackages) {

PackageParser.Package p = mPackages.get(packageName);

if (DEBUG_PACKAGE_INFO)

Log.v (TAG, "getPackageInfo " + packageName + ": " + p);

if (p != null) {

return generatePackageInfo(p, flags, userId);

}

if((flags & PackageManager. GET_UNINSTALLED_PACKAGES ) != 0) {

return generatePackageInfoFromSettingsLPw(packageName, flags, userId);

}

}

return null ;

}

這里 mPackages 是 hashMap睹欲,其調(diào)用put方法的時(shí)機(jī)是在 scanPackageLI 方法中供炼,而 scanPackageLI 的調(diào)用地方是在程序安裝和替換函數(shù)中,還有就是 scanDirLi 中窘疮,代碼略袋哼。

scanDirLi 是用來掃描一些系統(tǒng)目錄的的,在 PackageManagerService 的構(gòu)造函數(shù)中調(diào)用的:

File dataDir = Environment. getDataDirectory();

mAppDataDir = new File(dataDir, "data");

mAppInstallDir = new File(dataDir, "app");

mAppLibInstallDir = new File(dataDir, "app-lib" );

mAsecInternalPath = new File(dataDir, "app-asec" ).getPath();

mUserAppDataDir = new File(dataDir, "user");

mDrmAppPrivateInstallDir = new File(dataDir, "app-private" );

上面是掃描的位置↑↑↑↑↑↑↑

PackageManagerService 處理各種應(yīng)用的安裝考余、卸載先嬉、管理等工作,開機(jī)時(shí)由 systemServer 啟動(dòng)此服務(wù)楚堤。就是說之前安裝過的應(yīng)用或者系統(tǒng)應(yīng)用信息都會(huì)在開機(jī)掃描過程中存到 mPackages 這個(gè) hashMap 中疫蔓。開機(jī)后用戶的安裝操作也會(huì)同樣存到這個(gè) hashMap 里面含懊。

繼續(xù)看 getPackageInfo,調(diào)用的 generatePackageInfo 衅胀, 里面調(diào)用的是 PackageParser 中的 generatePackageInfo岔乔,繼續(xù)跟。

這個(gè)函數(shù)的代碼比較長(zhǎng)滚躯,只貼出部分關(guān)鍵代碼:

if ((flags&PackageManager. GET_SIGNATURES ) != 0) {

int N = (p.mSignatures != null) ? p.mSignatures.length : 0;

if (N > 0) {

pi.signatures = new Signature[N];

System.arraycopy (p.mSignatures, 0, pi. signatures, 0 , N);

}

}

return pi;

這段代碼之前主要是做一些復(fù)制的操作雏门,就是 new 一個(gè) PackageInfo,然后把 PackageParser.Package 對(duì)象中的一些內(nèi)容復(fù)制到這個(gè) PackageInfo 中掸掏。

從這段代碼可以看出來茁影,最終得到的 signatures 信息就是中 p(PackageParser.Package )中的成員 mSignatures 中得來的。

好了丧凤,現(xiàn)在就是看這個(gè)PackageParser.Package是從哪來的了募闲,通過跟蹤代碼,installPackageLI 和 scanPackageLI 中的:

final PackageParser.Package pkg = pp.parsePackag (tmpPackageFile, null, mMetrics, parseFlags);

這行代碼只是生成了 pkg愿待,并沒有賦值里面的 mSignatures浩螺,繼續(xù)跟蹤,找到函數(shù) collectCertificatesLI仍侥,找到 pp.collectCertificates(pkg , parseFlags)要出。

PS:最終又進(jìn)行了一系列的跟代碼,找到了 JarVerifier.java 這個(gè)類的 readCertificates 這個(gè)就是用來讀取.RSA 文件的,最終我們看到的里面的代碼是通過 loadCertificates 取得的 certs 賦值給了 pkg.mSignatures农渊。

至此患蹂,獲取簽名的所有邏輯就算是簡(jiǎn)單的過一遍了,以下是簡(jiǎn)略流程圖:(發(fā)現(xiàn) ppt 畫圖比 Windows 畫圖工具好用多了腿时,哈哈)


在跟代碼的過程中也找到了簽名對(duì)比的函數(shù) compareSignatures 况脆,有空自己看看就好了。

OK批糟,繞了這么久我們終于找到源頭了,獲取簽名就是在 META-INF 中尋找看铆,并解析徽鼎。試想一下,如果我們修改了這個(gè)函數(shù)弹惦,讓他解析原來正版的 META-INF 中的 CERT.RSA 文件否淤,這樣就可以偽造為真正的簽名了。

那么我們就想到了 HOOK棠隐,(很多人都是從看雪論壇上找到的一篇文章看到的http://bbs.pediy.com/showthread.php?t=186054hook 的原來簡(jiǎn)單來說就是石抡,找到原函數(shù)和新函數(shù)的指針位置,然后兌換內(nèi)容助泽,將新函數(shù)替代原函數(shù)啰扛。

關(guān)于 hook 呢嚎京,網(wǎng)上也有很多框架可以使用,比如:

1.Cydia substrate :http://www.cydiasubstrate.com/

2.Xposed :http://repo.xposed.info/

網(wǎng)上也有很多教程可以看看隐解。

烏云(wooyun)上有一篇很有意思的教程鞍帝,就是利用 hook 進(jìn)行微信運(yùn)動(dòng)作弊,原帖地址:http://drops.wooyun.org/tips/8416

下圖就是我用了上面的方法產(chǎn)生的效果煞茫,還差點(diǎn)被微信部門的人請(qǐng)去喝茶帕涌。



這里用的是 Xposed 框架,原理就是 hook 了手機(jī)的計(jì)步傳感器的隊(duì)列函數(shù)续徽,然后把步數(shù)的返回值每步乘1000返回蚓曼,前提是,你的手機(jī)硬件本身有計(jì)步傳感器功能钦扭,這里微信運(yùn)動(dòng)里面列出了支持的手機(jī)列表:https://kf.qq.com/touch/sappfaq/151013AvyyeQ151013r63qmq.html?platform=15好像最高就是98800了纫版,可能是微信做了步數(shù)限制吧。

我這里用的是小米4聯(lián)通版土全,發(fā)現(xiàn)雖然是1000基數(shù)的加捎琐,但是好像隔了很久才變化,估計(jì)又是 MIUI 做了一些省電策略裹匙,傳感器的采集做了對(duì)齊吧瑞凑?

Xposed 框架,很多玩機(jī)愛好者概页,會(huì)拿它修改一些主題籽御,字體之類的,或者系統(tǒng)界面惰匙,定制自己想要的系統(tǒng)插件等等技掏。然而,也有缺點(diǎn)项鬼,需要手機(jī)root哑梳,而且這個(gè)框架,還有可能讓手機(jī)變磚绘盟,還有的系統(tǒng)可能對(duì)這個(gè)框架支持的不好鸠真,或者不支持。XDA 論壇里面也有很多大神把 Xposed 對(duì)某些機(jī)型做了適配龄毡,大神一般都是說吠卷,如果手機(jī)變磚他們不負(fù)責(zé),哈哈沦零。

正式由于這些框架的諸多不便祭隔,root 等等的問題,于是就有了一些非 root 的 hook 的黑科技路操,比如阿里巴巴的開源框架 Dexposed(https://github.com/alibaba/dexposed)其也是根據(jù) Xposed 框架修改而來的疾渴,不過看 github 上他們也好久沒更新了千贯。

也有像其他個(gè)人寫的和這種比較類似的框架,這里就不介紹了程奠。但是這類框架的缺點(diǎn)就是丈牢,只能在該進(jìn)程下hook,不能全局 hook瞄沙,即只對(duì)這個(gè)進(jìn)程的應(yīng)用起作用己沛,不能對(duì)另一個(gè)應(yīng)用起作用,優(yōu)點(diǎn)是可以 hook 自定義函數(shù)也能hook 系統(tǒng)函數(shù)距境,并且不用 root 和重啟申尼。阿里用這個(gè)框架來打在線熱補(bǔ)丁。

那對(duì)于 App 內(nèi)部簽名校驗(yàn)的就不用再搜相應(yīng)的代碼了垫桂,直接 hook 就一步到位了师幕,android.app.ApplicationPackageManager 這個(gè)類的 getPackageInfo 這個(gè)方法直接把正確的簽名返回就好了,接下來我們就需要把hook的代碼注入到某個(gè) App 里就好了诬滩。

未完待續(xù)霹粥。。疼鸟。后控。。空镜。


歡迎閱讀:手把手教你逆向分析 Android 程序(連載三)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末浩淘,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子吴攒,更是在濱河造成了極大的恐慌张抄,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洼怔,死亡現(xiàn)場(chǎng)離奇詭異署惯,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)镣隶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門泽台,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人矾缓,你說我怎么就攤上這事〉九溃” “怎么了嗜闻?”我有些...
    開封第一講書人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)桅锄。 經(jīng)常有香客問我琉雳,道長(zhǎng)样眠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任翠肘,我火速辦了婚禮檐束,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘束倍。我一直安慰自己被丧,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開白布绪妹。 她就那樣靜靜地躺著甥桂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪邮旷。 梳的紋絲不亂的頭發(fā)上黄选,一...
    開封第一講書人閱讀 51,365評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音婶肩,去河邊找鬼办陷。 笑死,一個(gè)胖子當(dāng)著我的面吹牛律歼,可吹牛的內(nèi)容都是我干的民镜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼苗膝,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼殃恒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起辱揭,我...
    開封第一講書人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤离唐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后问窃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體亥鬓,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年域庇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嵌戈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡听皿,死狀恐怖熟呛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情尉姨,我是刑警寧澤庵朝,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響九府,放射性物質(zhì)發(fā)生泄漏椎瘟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一侄旬、第九天 我趴在偏房一處隱蔽的房頂上張望肺蔚。 院中可真熱鬧,春花似錦儡羔、人聲如沸宣羊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)段只。三九已至,卻和暖如春鉴扫,著一層夾襖步出監(jiān)牢的瞬間赞枕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工坪创, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留炕婶,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓莱预,卻偏偏與公主長(zhǎng)得像柠掂,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子依沮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,111評(píng)論 25 707
  • 很多人寫文章涯贞,喜歡把什么行業(yè)現(xiàn)狀啊,研究現(xiàn)狀啊什么的寫了一大通危喉,感覺好像在寫畢業(yè)論文似的宋渔,我這不廢話,先直接上幾個(gè)...
    騰訊bugly閱讀 1,752評(píng)論 0 5
  • 很多人寫文章辜限,喜歡把什么行業(yè)現(xiàn)狀啊皇拣,研究現(xiàn)狀啊什么的寫了一大通,感覺好像在寫畢業(yè)論文似的薄嫡,我這不廢話氧急,先直接上幾個(gè)...
    龐哈哈哈12138閱讀 2,937評(píng)論 1 8
  • ■文/芊墨之戀 云英入色的時(shí)候 我走過池塘 那一隅鳥語(yǔ)蟲鳴 可唯獨(dú),蟈先生 最為獨(dú)特 不疾不徐毫深,不喑不啞 有一只情...
    芊墨之戀閱讀 386評(píng)論 3 5
  • 剛剛完成作業(yè)3吩坝,用SWOT法將自己分析了一下,感覺思路清晰了哑蔫,希望以后能揚(yáng)長(zhǎng)避短钾恢,精進(jìn)技能手素,在工作上取得突破...
    小花貓和多多閱讀 544評(píng)論 1 0