那些年Android黑科技①:只要活着,就有希望
前言:
這個世界上手機(jī)有三大系統(tǒng),蘋果、 安卓、 中國安卓 。本篇強(qiáng)烈呼吁大家不要去做哪些違反用戶體驗(yàn)的黑科技功能,研究研究玩玩就好了啦。全當(dāng)增長技術(shù),在真實(shí)的項(xiàng)目開發(fā)中盡量能不用就不要用得好。道理大家都懂的。
目錄
那些年Android黑科技①:只要活著,就有希望
- android應(yīng)用內(nèi)執(zhí)行shell
- 雙進(jìn)程保活aidl版
- 雙進(jìn)程保活jni版
- 保活JobService版
那些年Android黑科技②:欺騙的藝術(shù)
- hook技術(shù)
- 欺騙系統(tǒng)之偷梁換柱
那些年Android黑科技③:干大事不擇手段
- 應(yīng)用卸載反饋
- Home鍵監(jiān)聽
- 桌面添加快捷方式
- 無法卸載app(DevicePolicManager)
- 無網(wǎng)絡(luò)權(quán)限偷偷上傳數(shù)據(jù)
android應(yīng)用內(nèi)執(zhí)行shell
android系統(tǒng)本身是Linux作為內(nèi)核,我們一般開發(fā)中使用 adb shell 命令來操作。但其實(shí)本身在應(yīng)用內(nèi)也是可以執(zhí)行的。強(qiáng)大的地方是在root的情況下,可以實(shí)現(xiàn)靜默安裝和操作一切你想在設(shè)備內(nèi)做事情。其方法如下。
調(diào)用工具代碼:
/*** 是否是在root下執(zhí)行命令** @param commands 命令數(shù)組* @param isRoot 是否需要root權(quán)限執(zhí)行*/public static void execCmd(String[] commands, boolean isRoot) {//便于觀看刪除來不影響的部分代碼,完整的可以在文中的github里找到。process = Runtime.getRuntime().exec(isRoot ? "su" : "sh");os = new DataOutputStream(process.getOutputStream());for (String command : commands) {if (command == null) continue;os.write(command.getBytes());os.writeBytes("\n");os.flush();}os.writeBytes("exit\n");os.flush();result = process.waitFor();successMsg = new StringBuilder();errorMsg = new StringBuilder();successResult = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));errorResult = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8"));}沒有root權(quán)限的情況下在屏幕上操作,實(shí)測可被執(zhí)行的命令只有swipe和部分keyevent可以生效,其余的可以通過adb的方式調(diào)用成功。但是一但在應(yīng)用內(nèi)通過shell是不可以的。這確保了android手機(jī)的安全。
其中keyevent 返回鍵 音量鍵可以調(diào)用 而home按鍵這種則不可以。
如果你試圖調(diào)用dumpsys activity activities 來查看。會拋出權(quán)限的異常如下。實(shí)測中我有申請權(quán)限,但一樣無法在應(yīng)用內(nèi)部調(diào)起。
image.png
使用參考:
Root情況下靜默安裝:
String command = "LD_LIBRARY_PATH=/vendor/lib:/system/lib pm install " +"apk路徑";ShellUtils.execCmd(command, ture);代碼:https://github.com/BolexLiu/AndroidShell
雙進(jìn)程保活aidl版 (android5.0以下)
原理介紹:實(shí)現(xiàn)的機(jī)制并不復(fù)雜,通過AIDL的方式開啟兩個服務(wù)分別在不同進(jìn)程中啟動,然后互相守護(hù)監(jiān)聽對方是否被關(guān)閉,如果有一方被斷開連接,另一方測重啟服務(wù)。因?yàn)閍ndroid在5.0之前銷毀進(jìn)程是一個一個銷毀的,他并不能同時(shí)銷毀兩個。所以可以做這件事。(被修改的rom除外,比如華為4.4就不行,但三星可以。)
1.配置服務(wù)進(jìn)程。注意process屬性會獨(dú)立在另一個進(jìn)程中。
<service android:name=".Service.LocalService" /><service android:name=".Service.RemoteService" android:process=".Remote"/>2.我們擁有兩個服務(wù)LocalService RemoteService。項(xiàng)目運(yùn)行后第一件事,同時(shí)啟動服務(wù)。
startService(new Intent(this, LocalService.class));startService(new Intent(this, RemoteService.class));3.在LocalService中綁定RemoteService并監(jiān)聽對方的創(chuàng)建和銷毀,RemoteService中的實(shí)現(xiàn)也一樣。
@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {super.onStartCommand(intent, flags, startId);Log.e(TAG, TAG + " onStartCommand");// 綁定遠(yuǎn)程服務(wù)bindService(new Intent(this, RemoteService.class), mLocalServiceConnection, Context.BIND_IMPORTANT);return START_STICKY;}//連接遠(yuǎn)程服務(wù)class localServiceConnection implements ServiceConnection {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {try {// 與遠(yuǎn)程服務(wù)通信MyProcessAIDL process = MyProcessAIDL.Stub.asInterface(service);Log.e(TAG, "連接" + process.getServiceName() + "服務(wù)成功");} catch (RemoteException e) {e.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName name) {// RemoteException連接過程出現(xiàn)的異常,才會回調(diào),unbind不會回調(diào)// 監(jiān)測,遠(yuǎn)程服務(wù)已經(jīng)死掉,則重啟遠(yuǎn)程服務(wù)Log.e(TAG, "遠(yuǎn)程服務(wù)掛掉了,遠(yuǎn)程服務(wù)被殺死");// 啟動遠(yuǎn)程服務(wù)startService(new Intent(LocalService.this, RemoteService.class));// 綁定遠(yuǎn)程服務(wù)bindService(new Intent(LocalService.this, RemoteService.class), mLocalServiceConnection, Context.BIND_IMPORTANT);}}代碼:https://github.com/BolexLiu/DoubleProcess
雙進(jìn)程保活jni版 (android5.0以下)
原理介紹:這種雙進(jìn)程守利用了Linux子進(jìn)程在父進(jìn)程被干掉后還能運(yùn)行而實(shí)現(xiàn)。所以我們要做的是通過java去fork一段C的代碼。通過動態(tài)鏈接庫封裝起來。然后在C代碼里不斷輪訓(xùn)父進(jìn)程的ppid是否存活。如果掛掉了側(cè)重新喚醒。
1.配置服務(wù)進(jìn)程。注意process屬性會獨(dú)立在另一個進(jìn)程中。
<serviceandroid:name=".service.DaemonService"android:process=":daemon"></service>2.在DaemonService里利用靜態(tài)代碼塊調(diào)起so。
public class DaemonService extends Service{// 便于閱讀省略無關(guān)代碼,詳情去移步至github···static {System.loadLibrary("daemon");} }3.so中的C代碼輪訓(xùn)進(jìn)程判斷是否存活。
//便于閱讀省略無關(guān)代碼,詳情去移步至github··· //fork子進(jìn)程,以執(zhí)行輪詢?nèi)蝿?wù)pid_t pid = fork();LOGI("fork=%d", pid);if (pid < 0) { // fork失敗了} else if (pid == 0) { // 可以一直采用一直判斷文件是否存在的方式去判斷,但是這樣效率稍低,下面使用監(jiān)聽的方式,死循環(huán),每個一秒判斷一次,這樣太浪費(fèi)資源了。int check = 1;while (check) {pid_t ppid = getppid();LOGI("pid=%d", getpid());LOGI("ppid=%d", ppid);if (ppid == 1) {LOGI("ppid == 1");if (sdkVersion >= 17) {LOGI("> 17");int ret = execlp("am", "am", "startservice", "--user", "0","-n", name,(char *) NULL);} else {execlp("am", "am", "startservice", "-n",name,(char *) NULL);LOGI("else");}check = 0;} else {}sleep(1);}}感謝CharonChui開源代碼。處應(yīng)該有掌聲!
代碼:https://github.com/CharonChui/DaemonService
保活 JobService版 (android5.0++)
原理: JobService是官方推薦的方式,即使app完成被殺死的狀態(tài)下也能調(diào)用起來,本質(zhì)是向系統(tǒng)注冊一個任務(wù)。通過getSystemService拿到系統(tǒng)的JobScheduler。然后通過JobInfo.Buidler進(jìn)行構(gòu)造。需要注意的是一定要指定被觸發(fā)的條件。比如:設(shè)備充電中、空閑狀態(tài)、連接wifi... 非常類似以前的廣播保護(hù)原理。但是實(shí)現(xiàn)不一樣。這次是我們反向注冊給系統(tǒng),而不是接收系統(tǒng)的廣播。
1.在AndroidManifest進(jìn)行配置添加permission屬性
<serviceandroid:name=".MyJobService"android:permission="android.permission.BIND_JOB_SERVICE" />2.MyJobServer繼承JobService類:
@Overridepublic boolean onStartJob(JobParameters params) {//該方法被觸發(fā)調(diào)用 可以做喚醒其他服務(wù)的操作return true;}@Overridepublic boolean onStopJob(JobParameters params) {return true;}3.在合適的地方向系統(tǒng)注冊
JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); ComponentName componentName = new ComponentName(MainActivity.this, MyJobService.class); JobInfo.Builder builder = new JobInfo.Builder(++mJobId, componentName); String delay = mDelayEditText.getText().toString(); if (delay != null && !TextUtils.isEmpty(delay)) { //設(shè)置JobService執(zhí)行的最小延時(shí)時(shí)間 builder.setMinimumLatency(Long.valueOf(delay) * 1000); } String deadline = mDeadlineEditText.getText().toString(); if (deadline != null && !TextUtils.isEmpty(deadline)) { //設(shè)置JobService執(zhí)行的最晚時(shí)間 builder.setOverrideDeadline(Long.valueOf(deadline) * 1000); } boolean requiresUnmetered = mWiFiConnectivityRadioButton.isChecked(); boolean requiresAnyConnectivity = mAnyConnectivityRadioButton.isChecked(); //設(shè)置執(zhí)行的網(wǎng)絡(luò)條件 if (requiresUnmetered) { builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED); } else if (requiresAnyConnectivity) { builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); } builder.setRequiresDeviceIdle(mRequiresIdleCheckbox.isChecked());//是否要求設(shè)備為idle狀態(tài) builder.setRequiresCharging(mRequiresChargingCheckBox.isChecked());//是否要設(shè)備為充電狀態(tài) scheduler.schedule(builder.build());注意jobScheduler無法兼容Android 5.0以下的設(shè)備,可以參考下面的項(xiàng)目,在低版本中也可以使用。
實(shí)際測試 :
研究了一段時(shí)間發(fā)現(xiàn)這個玩意,在國內(nèi)的廠商定制過后的rom好多不起作用。 比如魅族 和小米上 如果把a(bǔ)pp殺死以后,這個服務(wù)也調(diào)用不起來了。但是在模擬器和aosp版本的Rom上是可行的。我測試時(shí)用的電池充電狀態(tài)來調(diào)用job服務(wù)。
代碼:https://github.com/evant/JobSchedulerCompat
第一部分就先到這里。后續(xù)還有兩篇續(xù)集會緊接著營養(yǎng)跟上,如果你覺得不錯可以關(guān)注我一波點(diǎn)個喜歡神馬的哈哈。
作者:香脆的大雞排
鏈接:http://www.jianshu.com/p/cb2deed0f2d8
來源:簡書
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。
總結(jié)
以上是生活随笔為你收集整理的那些年Android黑科技①:只要活着,就有希望的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何通过数据包套接字攻击Linux内核
- 下一篇: Android版本的Wannacry文件