自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

2020年Android最新?;顚崿F(xiàn)原理揭秘

新聞 Android
一直以來,App進程?;疃际歉鞔髲S商,特別是頭部應(yīng)用開發(fā)商永恒的追求。

2020年Android最新?;顚崿F(xiàn)原理揭秘

?;顚崿F(xiàn)原理

本文由鴻洋大神提供,作者引用分享。

一直以來,App進程?;疃际歉鞔髲S商,特別是頭部應(yīng)用開發(fā)商永恒的追求。

畢竟App 進程死了,就什么也干不了了;一旦 App進程死亡,那就再也無法在用戶的手機上開展任何業(yè)務(wù),所有的商業(yè)模型在【用戶方】都沒有用武之地了。

早期的 Android 系統(tǒng)不完善,導(dǎo)致很多App有很多空子可以鉆,因此它們有著各種各樣的姿勢進行保活。

譬如說在 Android 5.0 以前,App 內(nèi)部通過 native 方式 fork 出來的進程是不受系統(tǒng)管控的,系統(tǒng)在殺 App 進程的時候,只會去殺 App 啟動的 Java 進程。

因此誕生了一大批“毒瘤”,他們通過 fork native 進程,在 App 的 Java 進程被殺死的時候通過 am命令拉起自己從而實現(xiàn)永生。

那時候的 Android 可謂是魑魅橫行,群魔亂舞;系統(tǒng)根本管不住應(yīng)用,因此長期以來被人詬病耗電、卡頓。

同時,系統(tǒng)的軟弱導(dǎo)致了 Xposed 框架、阻止運行、綠色守護、黑域、冰箱等一系列管制系統(tǒng)后臺進程的框架和 App 出現(xiàn)。

不過,隨著 Android 系統(tǒng)的發(fā)展,這一切都在往好的方向演變。

  • Android 5.0 以上,系統(tǒng)殺進程以 uid 為標識,通過殺死整個進程組來殺進程,因此 native 進程也躲不過系統(tǒng)的法眼。
  • Android 6.0 引入了待機模式(doze),一旦用戶拔下設(shè)備的電源插頭,并在屏幕關(guān)閉后的一段時間內(nèi)使其保持不活動狀態(tài),設(shè)備會進入低電耗模式,在該模式下設(shè)備會嘗試讓系統(tǒng)保持休眠狀態(tài)。
  • Android 7.0 加強了之前雞肋的待機模式(不再要求設(shè)備靜止狀態(tài)),同時對開啟了 Project Svelte,Project Svelte 是專門用來優(yōu)化 Android 系統(tǒng)后臺的項目,在 Android 7.0 上直接移除了一些隱式廣播,App 無法再通過監(jiān)聽這些廣播拉起自己。
  • Android 8.0 進一步加強了應(yīng)用后臺執(zhí)行限制:一旦應(yīng)用進入已緩存狀態(tài)時,如果沒有活動的組件,系統(tǒng)將解除應(yīng)用具有的所有喚醒鎖。另外,系統(tǒng)會限制未在前臺運行的應(yīng)用的某些行為,比如說應(yīng)用的后臺服務(wù)的訪問受到限制,也無法使用 Mainifest 注冊大部分隱式廣播。
  • Android 9.0 進一步改進了省電模式的功能并加入了應(yīng)用待機分組,長時間不用的 App 會被打入冷宮;另外,系統(tǒng)監(jiān)測到應(yīng)用消耗過多資源時,系統(tǒng)會通知并詢問用戶是否需要限制該應(yīng)用的后臺活動。

然而,道高一尺,魔高一丈。系統(tǒng)在不斷演進,?;罘椒ㄒ苍诓粩喟l(fā)展。大約在 4 年前出現(xiàn)過一個 MarsDaemon,這個庫通過雙進程守護的方式實現(xiàn)保活,一時間風(fēng)頭無兩。

不過好景不長,進入 Android 8.0 時代之后,這個庫就逐漸消亡。

一般來說,Android 進程?;罘譃閮蓚€方面:

  • 保持進程不被系統(tǒng)殺死。
  • 進程被系統(tǒng)殺死之后,可以重新復(fù)活。

隨著 Android 系統(tǒng)變得越來越完善,單單通過自己拉活自己逐漸變得不可能了;因此后面的所謂「保活」基本上是兩條路:

  • 提升自己進程的優(yōu)先級,讓系統(tǒng)不要輕易弄死自己;
  • App 之間互相結(jié)盟,一個兄弟死了其他兄弟把它拉起來。

當(dāng)然,還有一種終極方法,那就是跟各大系統(tǒng)廠商建立 PY【朋友,然而我一直覺得是(pi yan)】 關(guān)系,把自己加入系統(tǒng)內(nèi)存清理的白名單;比如說國民應(yīng)用微信。當(dāng)然這條路一般人是沒有資格走的。

大約一年以前,大神 gityuan 在其博客上公布了 TIM 使用的一種可以稱之為「終極永生術(shù)」的?;罘椒ǎ贿@種方法在當(dāng)前 Android 內(nèi)核的實現(xiàn)上可以大大提升進程的存活率。筆者研究了這種?;钏悸返膶崿F(xiàn)原理,并且提供了一個參考實現(xiàn) Leoric。

接下來就給大家分享一下這個終極?;詈诳萍嫉膶崿F(xiàn)原理。

?;畹牡讓蛹夹g(shù)原理

知己知彼,百戰(zhàn)不殆。

既然我們想要?;?,那么首先得知道我們是怎么死的。

一般來說,系統(tǒng)殺進程有兩種方法,這兩個方法都通過 ActivityManagerService 提供:

  • killBackgroundProcesses
  • forceStopPackage

在原生系統(tǒng)上,很多時候殺進程是通過第一種方式,除非用戶主動在 App 的設(shè)置界面點擊「強制停止」。

2020年Android最新?;顚崿F(xiàn)原理揭秘

強制停止

不過國內(nèi)各廠商以及一加三星等 ROM 現(xiàn)在一般使用第二種方法。

第一種方法太過溫柔,根本治不住想要搞事情的應(yīng)用。

第二種方法就比較強力了,一般來說被 force-stop 之后,App 就只能乖乖等死了。

因此,要實現(xiàn)?;?,我們就得知道 force-stop 到底是如何運作的。既然如此,我們就跟蹤一下系統(tǒng)的 forceStopPackage 這個方法的執(zhí)行流程:

首先是 ActivityManagerService里面的 forceStopPackage 這方法:

2020年Android最新?;顚崿F(xiàn)原理揭秘

ActivityManagerService forceStopPackage

在這里我們可以知道,系統(tǒng)是通過 uid 為單位 force-stop 進程的,因此不論你是 native 進程還是 Java 進程,force-stop 都會將你統(tǒng)統(tǒng)殺死。我們繼續(xù)跟蹤 forceStopPackageLocked 這個方法:

2020年Android最新?;顚崿F(xiàn)原理揭秘

forceStopPackageLocked

這個方法實現(xiàn)很清晰:

先殺死這個 App 內(nèi)部的所有進程,然后清理殘留在 system_server 內(nèi)的四大組件信息;我們關(guān)心進程是如何被殺死的,因此繼續(xù)跟蹤 killPackageProcessesLocked,這個方法最終會調(diào)用到 ProcessList 內(nèi)部的 removeProcessLocked 方法, removeProcessLocked 會調(diào)用 ProcessRecord 的 kill 方法,我們看看這個 kill:

2020年Android最新?;顚崿F(xiàn)原理揭秘

這里我們可以看到,首先殺掉了目標進程,然后會以 uid為單位殺掉目標進程組。

如果只殺掉目標進程,那么我們可以通過雙進程守護的方式實現(xiàn)?;?;

關(guān)鍵就在于這個 killProcessGroup,繼續(xù)跟蹤之后發(fā)現(xiàn)這是一個 native 方法,它的最終實現(xiàn)在 libprocessgroup中,代碼如下:

2020年Android最新?;顚崿F(xiàn)原理揭秘

注意這里有個奇怪的數(shù)字:40。

我們繼續(xù)跟蹤:

2020年Android最新?;顚崿F(xiàn)原理揭秘

瞧瞧我們的系統(tǒng)做了什么騷操作?循環(huán) 40 遍不停滴殺進程,每次殺完之后等 5ms,循環(huán)完畢之后就算過去了。

看到這段代碼,我想任何人都會蹦出一個疑問:假設(shè)經(jīng)歷連續(xù) 40 次的殺進程之后,如果 App 還有進程存在,那不就僥幸逃脫了嗎?

實現(xiàn)方法

那么,如何實現(xiàn)這個目的呢?

我們看這個關(guān)鍵的 5ms。假設(shè),App 進程在被殺掉之后,能夠以足夠快的速度(5ms 內(nèi))啟動一堆新的進程,那么系統(tǒng)在一次循環(huán)殺掉老的所有進程之后,sleep 5ms 之后又會遇到一堆新的進程;如此循環(huán) 40 次,只要我們每次都能夠拉起新的進程,那我們的 App 就能逃過系統(tǒng)的追殺,實現(xiàn)永生。

是的,煉獄般的 200ms,只要我們熬過 200ms 就能渡劫成功,得道飛升。

不知道大家有沒有玩過打地鼠這個游戲,整個過程非常類似,按下去一個又冒出一個,只要每次都能足夠快地冒出來,我們就贏了。

現(xiàn)在問題的關(guān)鍵就在于:

如何在 5ms 內(nèi)啟動一堆新的進程?

再回過頭來看原來的保活方式,它們拉起進程最開始通過 am命令,這個命令實際上是一個 java 程序,它會經(jīng)歷啟動一個進程然后啟動一個 ART 虛擬機,接著獲取 ams 的 binder 代理,然后與 ams 進行 binder 同步通信。

這個過程實在是太慢了,在這與死神賽跑的 5ms 里,它的速度的確是不敢恭維。

后來,MarsDaemon 提出了一種新的方式,它用 binder 引用直接給 ams 發(fā)送 Parcel,這個過程相比 am命令快了很多,從而大大提高了成功率。其實這里還有改進的空間,畢竟這里還是在 Java 層調(diào)用,Java 語言在這種實時性要求極高的場合有一個非常令人詬病的特性:

垃圾回收(GC);雖然我們在這 5ms 內(nèi)直接碰上 gc 引發(fā)停頓的可能性非常小,但是由于 GC 的存在,ART 中的 Java 代碼存在非常多的 checkpoint;

想象一下你現(xiàn)在是一個信使有重要軍情要報告,但是在路上卻碰到很多關(guān)隘,而且很可能被勒令暫時停止一下,這種情況是不可接受的。因此,最好的方法是通過 native code 給 ams 發(fā)送 binder 調(diào)用;

當(dāng)然,如果再底層一點,我們甚至可以通過 ioctl 直接給 binder 驅(qū)動發(fā)送數(shù)據(jù)進而完成調(diào)用,但是這種方法的兼容性比較差,沒有用 native 方式省心。

通過在 native 層給 ams 發(fā)送 binder 消息拉起進程,我們算是解決了「快速拉起進程」這個問題。但是這個還是不夠。還是回到打地鼠這個游戲,假設(shè)你摁下一個地鼠,會冒起一個新的地鼠,那么你每次都能摁下去最后獲取勝利的概率還是比較高的;

但如果你每次摁下一個地鼠,其他所有地鼠都能冒出來呢?這個難度系數(shù)可是要高多了。如果我們的進程能夠在任意一個進程死亡之后,都能讓把其他所有進程全部拉起,這樣系統(tǒng)就很難殺死我們了。

新的保活技術(shù)中通過 2 個機制來保證進程之間的互相拉起:

  1. 2 個進程通過互相監(jiān)聽文件鎖的方式,來感知彼此的死亡。
  2. 通過 fork 產(chǎn)生子進程,fork 的進程同屬一個進程組,一個被殺之后會觸發(fā)另外一個進程被殺,從而被文件鎖感知。

具體來說,創(chuàng)建 2 個進程 p1, p2,這兩個進程通過文件鎖互相關(guān)聯(lián),一個被殺之后拉起另外一個;同時 p1 經(jīng)過 2 次 fork 產(chǎn)生孤兒進程 c1,p2 經(jīng)過 2 次 fork 產(chǎn)生孤兒進程 c2,c1 和 c2 之間建立文件鎖關(guān)聯(lián)。這樣假設(shè) p1 被殺,那么 p2 會立馬感知到,然后 p1 和 c1 同屬一個進程組,p1 被殺會觸發(fā) c1 被殺,c1 死后 c2 立馬感受到從而拉起 p1,因此這四個進程三三之間形成了鐵三角,從而保證了存活率。

分析到這里,這種方案的大致原理我們已經(jīng)清晰了。

基于以上原理,我寫了一個簡單的 PoC,代碼在這里:

https://github.com/xinjianteng/Leoric

有興趣的可以看一下。

AMS 在執(zhí)行殺進程時是一個 ProcessRecord 一個地來的( https://android.googlesource.com/platform/frameworks/base/+/4f868ed/services/core/java/com/android/server/am/ActivityManagerService.java#5766),也就是最終會執(zhí)行多次 libprocessgroup 里的 killProcessgroup。

這樣只要在殺死屬于某個 cgroup 的進程時,另外的進程只要成功啟動一次 android:process 是另外的的進程即可活下來。因為新對應(yīng)新的 ProcessRecord,不會在上面那個循環(huán)里被殺死。此外,循環(huán)四十次反而給了超長的時間來啟動新的,觀察 log 可以發(fā)現(xiàn) killProcessgroup 的間隔長達幾十到一百多 ms。

改進空間

本方案的原理還是比較簡單直觀的,但是要實現(xiàn)穩(wěn)定的?;?,還需要很多細節(jié)要補充;特別是那與死神賽跑的 5ms,需要不計一切代價去優(yōu)化才能提升成功率。

具體來說,就是當(dāng)前的實現(xiàn)是在 Java 層用 binder 調(diào)用的,我們應(yīng)該在 native 層完成。筆者曾經(jīng)實現(xiàn)過這個方案,但是這個庫本質(zhì)上是有損用戶利益的,因此并不打算公開代碼,這里簡單提一下實現(xiàn)思路供大家學(xué)習(xí):

如何在 native 層進行 binder 通信?

libbinder 是 NDK 公開庫,拿到對應(yīng)頭文件,動態(tài)鏈接即可。

難點:依賴繁多,剝離頭文件是個體力活。

如何組織 binder 通信的數(shù)據(jù)?

通信的數(shù)據(jù)其實就是二進制流;具體表現(xiàn)就是 (C++/Java) Parcel 對象。native 層沒有對應(yīng)的 Intent Parcel,兼容性差。

方案:

  1. Java 層創(chuàng)建 Parcel (含 Intent),拿到 Parcel 對象的 mNativePtr(native peer),傳到 Native 層。
  2. native 層直接把 mNativePtr 強轉(zhuǎn)為結(jié)構(gòu)體指針。
  3. fork 子進程,建立管道,準備傳輸 parcel 數(shù)據(jù)。
  4. 子進程讀管道,拿到二進制流,重組為 parcel。

如何應(yīng)對?

今天我把這個實現(xiàn)原理公開,并且提供 PoC 代碼,并不是鼓勵大家使用這種方式?;?,而是希望各大系統(tǒng)廠商能感知到這種黑科技的存在,推動自己的系統(tǒng)徹底解決這個問題。

兩年前我就知道了這個方案的存在,不過當(dāng)時鮮為人知。

最近一個月我發(fā)現(xiàn)很多 App 都使用了這種方案,把我的 Android 手機折騰的慘不忍睹;畢竟本人手機上安裝了將近 800 個 App,假設(shè)每個 App 都用這個方案?;?,那這系統(tǒng)就沒法用了。

系統(tǒng)如何應(yīng)對?

如果我們把系統(tǒng)殺進程比喻為斬首,那么這個?;罘桨傅木柙谟谀芸焖匍L出一個新的頭;因此應(yīng)對之法也很簡單,只要我們在斬殺一個進程的時候,讓別的進程老老實實呆著別搞事情就 OK 了。具體的實現(xiàn)方法多種多樣,不贅述。

用戶如何應(yīng)對?

在廠商沒有推出解決方案之前,用戶可以有一些方案來緩解使用這個方案進行?;畹牧髅?App。

這里推薦兩個應(yīng)用給大家:

  • 冰箱
  • Island

通過冰箱的凍結(jié)和 Island 的深度休眠可以徹底阻止 App 的這種?;钚袨?。當(dāng)然,如果你喜歡別的這種“凍結(jié)”類型的應(yīng)用,比如小黑屋或者太極的陰陽之門也是可以的。

其他不是通過“凍結(jié)”這種機制來壓制后臺的應(yīng)用理論上對這種保活方案的作用非常有限。

總結(jié)

1. 對技術(shù)來說,黑科技沒有什么黑的,不過是對系統(tǒng)底層原理的深入了解從而反過來對抗系統(tǒng)的一種手段。很多人會說,了解系統(tǒng)底層有什么用,本文應(yīng)該可以給出一個答案:可以實現(xiàn)別人永遠也無法實現(xiàn)的功能,通過技術(shù)推動產(chǎn)品,從而產(chǎn)生巨大的商業(yè)價值。

2. 黑科技雖強,但是它不該存在于這世上。沒有規(guī)矩,不成方圓。黑科技黑的了一時,黑不了一世。要提升產(chǎn)品的存活率,終歸要落到產(chǎn)品本身上面來,尊重用戶,提升體驗方是正途。

 

責(zé)任編輯:張燕妮 來源: 今日頭條
相關(guān)推薦

2022-10-24 08:03:04

MySQL數(shù)據(jù)庫

2020-03-19 10:13:13

OkHttpWebSocket

2019-09-23 08:27:15

TCP長連接心跳

2019-12-31 09:11:01

后臺Android系統(tǒng)

2020-09-22 08:52:50

平臺實現(xiàn)消息

2020-03-02 15:05:56

Java前端學(xué)習(xí)路線

2012-12-27 09:40:41

大數(shù)據(jù)云端存儲數(shù)據(jù)分析

2021-07-19 09:25:19

數(shù)據(jù)庫MySQL技術(shù)

2022-04-08 08:32:40

mobx狀態(tài)管理庫redux

2024-10-10 17:46:06

2023-01-26 23:44:41

C++代碼生命周期

2016-08-11 09:19:36

AndroidService通訊應(yīng)用

2021-05-13 23:30:17

JavaScript 原理揭秘

2024-10-11 16:57:18

2012-09-24 10:03:04

Intel零能耗計算

2016-06-13 09:18:21

2022-11-18 18:36:24

2019-11-15 15:12:19

Windows激活KMS

2020-02-11 17:17:54

RSAC信息安全會議RSA大會

2010-09-17 15:25:03

JAVAJVM
點贊
收藏

51CTO技術(shù)棧公眾號