1. 程式人生 > >通過JNI的方式 android 防止程序被殺

通過JNI的方式 android 防止程序被殺

通過JNI的方式(NDK程式設計),fork()出一個子執行緒作為守護程序,輪詢監聽服務狀態。守護程序(Daemon)是執行在後臺的一種特殊程序。它獨立於控制終端並且週期性地執行某種任務或等待處理某些發生的事件。而守護程序的會話組和當前目錄,檔案描述符都是獨立的。後臺執行只是終端進行了一次fork,讓程式在後臺執行,這些都沒有改變。

那麼我們先來看看Android4.4的原始碼,ActivityManagerService(原始碼/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java)是如何關閉在應用退出後清理記憶體的:

  1. Process.killProcessQuiet(pid);  
應用退出後,ActivityManagerService就把主程序給殺死了,但是,在Android5.0中,ActivityManagerService卻是這樣處理的:
  1. Process.killProcessQuiet(app.pid);  
  2. Process.killProcessGroup(app.info.uid, app.pid);  
就差了一句話,卻差別很大。Android5.0在應用退出後,ActivityManagerService不僅把主程序給殺死,另外把主程序所屬的程序組一併殺死,這樣一來,由於子程序和主程序在同一程序組,子程序在做的事情,也就停止了
...要不怎麼說Android5.0在安全方面做了很多更新呢...

那麼,有沒有辦法讓子程序脫離出來,不要受到主程序的影響,當然也是可以的。那麼,在C/C++層是如何實現的呢?先上關鍵程式碼:

  1. /** 
  2.  * srvname  程序名 
  3.  * sd 之前建立子程序的pid寫入的檔案路徑 
  4.  */
  5. int start(int argc, char* srvname, char* sd) {  
  6.     pthread_t id;  
  7.     int ret;  
  8.     struct rlimit r;  
  9.     int pid = fork();  
  10.     LOGI("fork pid: %d"
    , pid);  
  11.     if (pid < 0) {  
  12.         LOGI("first fork() error pid %d,so exit", pid);  
  13.         exit(0);  
  14.     } elseif (pid != 0) {  
  15.         LOGI("first fork(): I'am father pid=%d", getpid());  
  16.         //exit(0);
  17.     } else { //  第一個子程序
  18.         LOGI("first fork(): I'am child pid=%d", getpid());  
  19.         setsid();  
  20.         LOGI("first fork(): setsid=%d", setsid());  
  21.         umask(0); //為檔案賦予更多的許可權,因為繼承來的檔案可能某些許可權被遮蔽
  22.         int pid = fork();  
  23.         if (pid == 0) { // 第二個子程序
  24.             FILE  *fp;  
  25.             sprintf(sd,"%s/pid",sd);  
  26.             if((fp=fopen(sd,"a"))==NULL) {//開啟檔案 沒有就建立
  27.                 LOGI("%s檔案還未建立!",sd);  
  28.                 ftruncate(fp, 0);  
  29.                 lseek(fp, 0, SEEK_SET);  
  30.             }  
  31.             fclose(fp);  
  32.             fp=fopen(sd,"rw");  
  33.             if(fp>0){  
  34.                 char buff1[6];  
  35.                 int p = 0;  
  36.                 memset(buff1,0,sizeof(buff1));  
  37.                 fseek(fp,0,SEEK_SET);  
  38.                 fgets(buff1,6,fp);  //讀取一行(pid)
  39.                 LOGI("讀取的程序號:%s",buff1);  
  40.                 if(strlen(buff1)>1){ // 有值
  41.                     kill(atoi(buff1), SIGTERM); // 把上一次的程序幹掉,防止重複執行
  42.                     LOGI("殺死程序,pid=%d",atoi(buff1));  
  43.                 }  
  44.             }  
  45.             fclose(fp);  
  46.             fp=fopen(sd,"w");  
  47.             char buff[100];  
  48.             int k = 3;  
  49.             if(fp>0){  
  50.                 sprintf(buff,"%lu",getpid());  
  51.                 fprintf(fp,"%s\n",buff); // 把程序號寫入檔案
  52.             }  
  53.             fclose(fp);  
  54.             fflush(fp);  
  55.             LOGI("I'am child-child pid=%d", getpid());  
  56.             chdir("/"); //<span style="font-family: Arial, Helvetica, sans-serif;">修改程序工作目錄為根目錄,chdir(“/”)</span>
  57.             //關閉不需要的從父程序繼承過來的檔案描述符。
  58.             if (r.rlim_max == RLIM_INFINITY) {  
  59.                 r.rlim_max = 1024;  
  60.             }  
  61.             int i;  
  62.             for (i = 0; i < r.rlim_max; i++) {  
  63.                 close(i);  
  64.             }  
  65.             umask(0);  
  66.             ret = pthread_create(&id, NULL, (void *) thread, srvname); // 開啟執行緒,輪詢去監聽啟動服務
  67.             if (ret != 0) {  
  68.                 printf("Create pthread error!\n");  
  69.                 exit(1);  
  70.             }  
  71.             int stdfd = open ("/dev/null", O_RDWR);  
  72.             dup2(stdfd, STDOUT_FILENO);  
  73.             dup2(stdfd, STDERR_FILENO);  
  74.         } else {  
  75.             exit(0);  
  76.         }  
  77.     }  
  78.     return 0;  
  79. }  
  80. /** 
  81.  * 啟動Service 
  82.  */
  83. void Java_com_yyh_fork_NativeRuntime_startService(JNIEnv* env, jobject thiz,  
  84.         jstring cchrptr_ProcessName, jstring sdpath) {  
  85.     char * rtn = jstringTostring(env, cchrptr_ProcessName); // 得到程序名稱
  86.     char * sd = jstringTostring(env, sdpath);  
  87.     LOGI("Java_com_yyh_fork_NativeRuntime_startService run....ProcessName:%s", rtn);  
  88.     a = rtn;  
  89.     start(1, rtn, sd);  
  90. }  
這裡有幾個重點需要理解一下:

1、為什麼要fork兩次?

第一次fork的作用是為後面setsid服務。setsid的呼叫者不能是程序組組長(group leader),而第一次呼叫的時候父程序是程序組組長。

第二次呼叫後,把前面一次fork出來的子程序退出,這樣第二次fork出來的子程序,就和他們脫離了關係。

2、setsid()作用是什麼?setsid() 使得第二個子程序是會話組長(sid==pid),也是程序組組長(pgid == pid),並且脫離了原來控制終端。故不管控制終端怎麼操作,新的程序正常情況下不會收到他發出來的這些訊號。

3、umask(0)的作用:由於子程序從父程序繼承下來的一些東西,可能並未把許可權繼承下來,所以要賦予他更高的許可權,便於子程序操作。

4、chdir ("/");作用:程序活動時,其工作目錄所在的檔案系統不能卸下,一般需要將工作目錄改變到根目錄。 

5、程序從建立它的父程序那裡繼承了開啟的檔案描述符。如不關閉,將會浪費系統資源,造成程序所在的檔案系統無法卸下以及引起無法預料的錯誤。所以在最後,記得關閉掉從父程序繼承過來的檔案描述符

然後,在上面的程式碼中開啟執行緒後做的事,就是迴圈去startService(),程式碼如下:

  1. voidthread(char* srvname) {  
  2.     while(1){  
  3.         check_and_restart_service(srvname);  
  4.         sleep(4);  
  5.     }  
  6. }  
  7. /** 
  8.  * 檢測服務,如果不存在服務則啟動. 
  9.  * 通過am命令啟動一個laucher服務,由laucher服務負責進行主服務的檢測,laucher服務在檢測後自動退出 
  10.  */
  11. 相關推薦

    通過JNI方式 android 防止程序

    通過JNI的方式(NDK程式設計),fork()出一個子執行緒作為守護程序,輪詢監聽服務狀態。守護程序(Daemon)是執行在後臺的一種特殊程序。它獨立於控制終端並且週期性地執行某種任務或等待處理某些發生的事件。而守護程序的會話組和當前目錄,檔案描述符都是獨立的。後臺

    Android防止程序

    有時開啟新的app而記憶體不夠時,其它app可能會被Out Of Memory Killer清除防止程序不被殺死的辦法: 1.在AndroidManifest.xml檔案中設定persistent屬

    android 使用Service進行雙程序守護,防止程序

    public class MyService extends Service { String msg; public MyService() { msg = "Msg from MyService"; } @Override public IBind

    Android 監聽主程序

    當按多工鍵時,然後清除所有程式或者殺死單個程式時,如果要監聽這個動作的話,可以在一個service裡監聽。 效果如下: 可以看到,在多工視窗中,左滑結束程序和清除所有程序後,都會自己再開啟應用,程式碼如下: public class KeepLifeService

    Android防止Service殺死

    1. Service被殺死的兩種場景 1.2 系統回收 在系統記憶體空間不足時可能會被系統殺死以回收記憶體,記憶體不足時Android會依據Service的優先順序來清除Service。 1.2 使用者清除 使用者可以在”最近開啟”(多工視窗、任務管理視窗)中清除最近開啟

    android 防止應用lowmemorykiller殺掉

      網上關於lowmemorykiller的文章一大把,總結一下,系統會不斷更新程序的adj值,然後在記憶體緊張的情況下,adj越大的應用越可能被殺,那麼我們要防止被殺,要麼是給我們的應用設定比較小的adj值,要麼是要殺的時候過濾我們的應用,因為殺程序是比較偏底層做的,不太熟

    【安卓篇】安卓Activity程序規則

    Android手機開發與桌面開發有一個主要不同之處:通常在一部Android手機裡同時執行著多個應用(app),每個app對應一個系統程序,當系統需要更多的資源(如記憶體)而空閒資源不足時,Android系統就會選擇殺掉一些“低優先順序”的程序以便釋放所需資源。   An

    Centos執行Mysql因為記憶體不足程序

    今天剛剛申請了一個新的域名,在申請完域名剛準備繫結給小夥伴分享註冊新域名的喜悅時,剛把網站發到我們小夥伴們的討論群裡,卻發現訪問不了了,提示,資料庫連線失敗! 真的時一個尷尬.....    所有人都進不了我的網站,然後登入centos後臺,發現了這樣的提示

    android防止資料釋放獲取物件為空的幾點措施

    1.將大多數的context使用你的application替代 public class MyApplication extends Application{ private static MyApplication app; @Overr

    Android 通過JNI實現守護程序,使Service服務不殺死

    開發一個需要常住後臺的App其實是一件非常頭疼的事情,不僅要應對國內各大廠商的ROM,還需要應對各類的安全管家...  雖然不斷的研究各式各樣的方法,但是效果並不好,比如工作管理員把App幹掉,服務就起不來了... 網上搜尋一番後,主要的方法有以下幾種方法,但其實也都

    整理下 android 保活 防 守護程序

    Android 程序拉活包括兩個層面: A. 提供程序優先順序,降低程序被殺死的概率 B. 在程序被殺死後,進行拉活 本文下面就從這兩個方面做一下總結。 1. 程序的優先順序 Android 系統將盡量長時間地保持應用程序,但為了新建程序或執行更重要的程序,最終需要

    最優雅退出 Android 應用程序的 6 種方式

    home鍵 應用 一點 container new 出棧 manage 而且 rec 一、容器式建立一個全局容器,把所有的Activity存儲起來,退出時循環遍歷finish所有Activity import java.util.ArrayList; impor

    init.rc文件中面啟動c++程序通過jni調用java實現

    mini val sni ril urn runtime sport mco env </pre><p>註:假設是自己的myself.jar包,還要修改例如以下:</p><p>target/product/core_bas

    Android Studio2.2.3 通過JNI引用ffmpeg庫小結

    android studio;ffmpeg;ndk;jni修改步驟:首先通過NDK14編譯出libffmpeg.so ,將include目錄取出通過AS建立基於jni的工程項目,將include目錄放到cpp下;創建jniLibs/armeabi目錄,將libffmpeg.so放到裏邊 3.配置CMak

    Android 防止控件重復點擊

    當前時間 我們 一個 效果 tab class 觸發 事件 stat 在開發中經常會遇到這樣的情況,一個按鈕點擊後會彈出Toast或者Dialog,如果快速重復地點擊,則Toast則會重復地出現. 而我們想要的效果是一定時間內的點擊只生效一次,或者說這種快速且重復的

    Android程序app中Application回撥onCreate()方法執行多次分析及解決

    最近工作中碰到一個問題,在優化app,使用DDMS檢視Application log過程中看到,app啟動了三個程序,一個主程序,兩個附帶的程序。如下圖可看到一個app啟動的三個程序。  自定義Application回撥方法onCreate()被執行了3次。開始不知是何原因。 相

    【專案知識點彙總】二、JNI程式碼編譯方式camke 和 ndk 方式 -- Android Studio 操作

    一、介紹 Android Studio 編譯JNI程式碼有兩種方式:cmake 和 ndk 方式 使用感受: 1、cmake方式會受到所用Android sdk版本的影響,主要是ndk的版本影響,沒有深入去探究原理 2、ndk方式可以跨Android sdk 版本執行

    android如何防止應用載屏

    android如何防止應用被載屏 現在有很多的應用都是包含有使用者的隱私資訊,那如何保護使用者資料不被洩漏就是做前端需要考慮的問題。當然,關於應用安全是一個比較大的課題,不可能用一篇文章把全它講完。而且我也不是安全專家,只能把想到的在這裡記錄一下。詳細請看以下程式碼。 publ

    android通過JNI用C/C++建立本地檔案

    通過jni在本地建立檔案 1.在android studio建立基本的jni工程,並且在APP介面成功顯示"Hello from C++" 不會的可以看android studio使用jni 2.在native-lib.cpp檔案中建立檔案 為了方便,我們直接在stringFromJ

    android adb 程序埠號佔解決方法

    1、檢視adb 的程序埠號是什麼,輸入命令 adb nodaemon server 顯示埠號 2、檢視埠號對應的程序pid ,輸入命令 netstat -ano  檢視對應的埠號的程序號 3、檢視程序號對應的程序名稱或結束程序 tasklist|findstr "程序號" 或