1. 程式人生 > >【輸出文件】 Android 加密 模組原始碼分析

【輸出文件】 Android 加密 模組原始碼分析

 

 

 

 

                           Android6.0 加密模組解析

 

 

前言:

加密流程就是用一個加密了的key對Android 系統中的使用者資料進行加密。一旦裝置被加密了,所有使用者建立的資料在提交到磁碟前都會被自動加密,並且在返回到呼叫的程序前,所有的使用者資料會被自動解密。

   Android 的disk機密是基於dm-crypt的,dm-crypt是kernl上執行在block 裝置層的一個加密工具。因此,加密實際操作物件是對嵌入式多媒體儲存裝置(eMMC)和類似的快閃記憶體裝置中儲存的資料資訊。加密是不可能基於YAFFS工具的,因為YAFFS是直接與NAND快閃記憶體晶片進行通訊的。

  1. 加密型別

加密流程對於一個裝置來說,有可能出現4中不同的情況,但是一般情況下,都是對裝置進行一次加密,然後再進行一個正常的開機流程:

    ●加密一個之前沒有加密的裝置:

      • 在fstab配置資訊中標註了forceencrypt= true,第一次開機時會強制性進行加密;

      • 在fstab配置資訊中沒有標註 forceencrypt= true,第一次開機不會強制性進行加密,需要使用者開機後手動觸發加密流程。

 

    ●一個已經加密裝置的開機:

  • 在無密碼狀態下對已加密的裝置進行開機

  • 在有密碼需求的狀態下對加密裝置的開機

針對以上4中情況,我們把加密具體分為以下4中具體的流程:

 

1.1 對強制要求加密的裝置進行加密

 

   這是從Android 5.0 開始,google原生機預設的開機模式,即在google原生機的fstab配置檔案中,/data分割槽都是標註了forceencrypt = true 資訊的。

1.1.1 檢查forceencrypt 標誌位

init 先按照fstab的配置資訊檢查 forceencrypt 配置資訊,需要加密,init Unmount /data.

1.1.2 加密/data

init服務會設定vold.decrypt = "trigger_encryption",這個屬性的變化觸發init 掛載fstab檔案中的分割槽配置時返回“FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION”值,觸發vold.decrypt = trigger_encryption,init 啟動surfaceflinger 和encrypt服務。

 

on property:vold.decrypt=trigger_encryption

    start surfaceflinger

    start encrypt

 

 

1.1.3 mount tmpfs

vold服務將掛載一個臨時的“/data”,將記錄加密進度的系統屬性vold.encrypt_progress 賦值為“0”。vold 掛載這個tmpfs data 的作用在於

1.2 對已經正常開機的裝置進行加密

對一個未加密的裝置進行加密

這個流程就是由使用者觸發的,並且將指向“inplace encryption”這個流程,這時UI 會要求手機電池是充滿狀態而且USB 線是連線狀態。

注:加密流程中如果斷電,必須恢復出廠設定後才能繼續使用

使能“inplace encryption”,vold會啟動一個loop來讀取real block device 的每個sector並且將它寫入crypto block device。vold 會在讀寫sector之前先檢查它是否有被呼叫,這個讓encryption在左右一點資料記錄的新裝置上比較迅速。

 

裝置狀態相關:設定 ro.crypto.state = “unencrypted” 並且執行init中“on nonencrypted ”觸發的流程

 

1.2.1.檢查password:

  UI會通過"cryptfs enable inplace"這個命令來要求輸入鎖屏的password來開始流程;

 

1.2.2.關閉framework:

  vold會檢查errors,返回 -1 就表示無法加密並且列印相應log。如果可以加密,將會設定 系統屬性 “vold.decrypt =trigger_shutdown_framework.”,這將會導致init.rc 關閉 late_started 和main 類的services.

 

1.2.3.解除安裝 /data分割槽:

  vold 會解除安裝“ mnt/sdcard” 和“/data” 分割槽

 

1.2.4.開始加密 /data分割槽

  vold 會開啟crupto mapping,這會建立一個虛擬的crypto block device,它會在真實的block device 上進行“map”並且會在write時進行加密,read時進行解密。vold將會建立並寫出 crypto metadata.

 

1.2.5.在加密過程中,掛載tmpfs:

  vold 會在/data分割槽掛載一個tmpfs(掛載時是option 屬性通過ro.crpto.tmpfs_option來確定)。然後設定屬性“vold.encrypt_progress = 0”。vold 將/data這個tmpfs用於啟動一個加密了的system並且設定系統屬性 vold.decrypt = trigger_restart_min_fraemwork.

 

1.2.6.啟動framework來展示加密進度

trigger_restart_min_framework 會喚起init.rc來start main class services.當famework監聽到vold.encrypt_progress = 0時,它會啟動顯示進度條的UI,它每隔5秒鐘就檢查這個系統屬性並且跟新進度條。encryptiom loop每次加密完成其他的partition後,它都會更新vold.encrypt_progress這個屬性。

 

1.2.7.當/data加密完成,重啟

當/data分割槽加密成功,vold會清除metadata中的ENCRYPTION_IN_PROGRESS這個flags,並且重啟system

如果重啟失敗,vold會設定屬性vold.encrypt_progress = error_failed 並且在UI 中展示一條資訊,要求使用者重啟。

 

1.3 已加密的裝置有密碼狀態下的開機

這種情況指的是: 你啟動的是一個設定了密碼的加密系統,這個系統的加密密碼 可能是一個pin ,pattern 或者密碼

1.3.1.檢測加密裝置的密碼
通過判斷標誌位 ro.crypto.state = "encrypted"來確定裝置已經被加密了
vold 會設定 “vold.decrypt = trigger_restart_min_framework”,因為 /data 已經被一個密碼加密了

1.3.2.掛載tmpfs
init.rc中傳遞下來的關於/data 掛載的options 被init程序用5個系統屬性儲存下來。vold使用這些系統屬性來啟動加密流程:
  1.ro.crypto.fs_type
  2.ro.crypto.fs_real_blkdev
  3.ro.crypto.fs_mnt_point
  4.ro.crypto.fs_options
  5.ro.crypto.fs_flags

1.2.3.啟動framework來要求輸入password
framework啟動後會監聽vold.decrypt = trigger_restart_min_framework.此時framerwork就知道現在是正在啟動一個tmpfs /data並且它需要獲取使用者的password。
然而,首先我們必須確定disk已經被正確的加密了。我們會向vold傳送命令“cryptfs cryptocomplete ”,如果加密成功,vold會返回0;如果出現錯誤,會返回 -1;如果加密沒有完全完成,會返回-2.
vold是通過檢查crypto metadata的CRYPTO_ENCRYPTION_IN_PROCESS標誌位。如果crypto metadata中的這個標誌位被設定了,加密的流程就被打斷,並且裝置上就不再有可用的資料了。如果vold返回一
個錯誤,UI會要求使用者重啟並對裝置恢復出廠設定。

1.2.4.獲取password後對data分割槽進行解密
  一旦cryptfs crytocomplete 成功了,framework會展示一個UI 介面來請求disk的password。UI 會將使用者傳入的password通過下發cryptfs checkpw命令到vold來檢查。如果password是正確的(檢查password正確的方法是可以成功的在一個暫時的區域掛載加密了的/data並且可以解除安裝它),vold將會儲存加密了的塊裝置的名稱在屬性ro.crypto.fs_crypto_blkdev中並且返回0 到UI 程序,如果passworf是錯誤的,返回-1 到UI 程序。
 
1.2.5.關閉framework
  UI將會啟動一個crypto boot graphic 並且通過cryptfs restart.vold 這個命令來設定系統屬性 vold.decrypt = trigger_restart_min_main,這導致init.rc來啟動class_reset main.這會停止所有的main類的service,而這些service保證了/data 這個tmpf處於未掛載狀態。
 
1.2.6. 掛載/data分割槽
  vold 會掛載已經加密了的/data 分割槽並且準備新的分割槽,如果這個分割槽是按照wipe命令進行加密的,這個分割槽可能未初始化好的。然後會修改系統屬性 vold.post_fs_data_done = 0 然後設定vold.decrypt = trigger_post_fs_data.這導致init.rc來執行post-fs-data命令。它們會建立必要的路徑或者連結然後設定vold.post_fs_data_done = 1.一旦vold 監聽到這個屬性等於1,vold將設定系統屬性 vold.decrypt = trigger_restart_framework.這導致init.rc重新啟動main 類的service並且同時啟動lte_start類的services。
1.2.7.啟動整個framework

1.4 已加密的裝置無密碼狀態下的開機

與1.3 相同,唯一的區別在於啟動framework層後不會初始化輸入密碼的介面。

  1. 具體模組解析

流程圖:

  1. 機密流程

3.1 do_mount_all 分析

3.1.1 do_mount_all 程式碼

init 會呼叫fs_mgr中do_mount_all 解析fstab檔案,並對其中的配置資訊進行掛載:

 

system/core/init/builtins.cpp

 

int do_mount_all(int nargs, char **args)

{

... ...

//fork 一個子程序,執行mount

    pid = fork();

    if (pid > 0) {

        /* Parent.  Wait for the child to return */

        int wp_ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));

... ...

ret = WEXITSTATUS(status);

... ...

    } else if (pid == 0) {

//子程序,args[1]引數就是”/fstab.flo”,fs_mgr_read_fstab將解析fstab檔案

        fstab = fs_mgr_read_fstab(args[1]);

        child_ret = fs_mgr_mount_all(fstab);

        fs_mgr_free_fstab(fstab);

... ...

    }

if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {

    //fstab 中設定了預設強制加密

        property_set("vold.decrypt", "trigger_encryption");

} else if (ret == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {

    //裝置已經加密

        property_set("ro.crypto.state", "encrypted");

        property_set("ro.crypto.type", "block");

        property_set("vold.decrypt", "trigger_default_encryption");

} else if (ret == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {

    //裝置未加密,什麼都不用處理

        property_set("ro.crypto.state", "unencrypted");

        /* If fs_mgr determined this is an unencrypted device, then trigger

         * that action.

         */

        action_for_each_trigger("nonencrypted", action_add_queue_tail);

    } else if (ret == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {

... ...

    } else if (ret == FS_MGR_MNTALL_DEV_DEFAULT_FILE_ENCRYPTED) {

... ...

    } else if (ret == FS_MGR_MNTALL_DEV_NON_DEFAULT_FILE_ENCRYPTED) {

... ...

    } else if (ret > 0) {

        ERROR("fs_mgr_mount_all returned unexpected error %d\n", ret);

    }

}

 

● do_mount_all 的作用就是:

   •系統第一次開機,而且系統設定為強制加密,就走FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION

   •系統機密後開機,發現裝置加密了,FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED

   •系統正常開機,不用加密,

FS_MGR_MNTALL_DEV_NOT_ENCRYPTED

 

3.1.2 fs_mgr_mount_all

system/core/fs_mgr/fs_mgr.c

 

int fs_mgr_mount_all(struct fstab *fstab)

{

... ...

    int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;

... ...

for (i = 0; i < fstab->num_entries; i++) {

... ...

        mret = mount_with_alternatives(fstab, i, &last_idx_inspected, &attempted_idx);

... ...

//如果裝置已機密,mret = -1/data 分割槽是無法正常掛載的

//如果裝置未加密,正常情況下,mret = 0

        if (!mret) {

// 1.可以正常掛載,並且已經正常掛載了,這裡就需要考慮是否是第一次開機,是否需要強制加密

            int status = handle_encryptable(fstab, &fstab->recs[attempted_idx]);

... ...

            if (status != FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {

                encryptable = status;

            }

            continue;

        }

// 2.未掛載成功,判斷裝置是否已經加密了,這種情況下,無法通過mount掛載

        bool wiped = partition_wiped(fstab->recs[top_idx].blk_device);

        if (mret && mount_errno != EBUSY && mount_errno != EACCES &&

            fs_mgr_is_formattable(&fstab->recs[top_idx]) && wiped) {

... ...

        }

        if (mret && mount_errno != EBUSY && mount_errno != EACCES &&

            fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) {

... ...

         //已判斷裝置已加密,在/data 分割槽下掛載tmpfs裝置,而不是指定的實際裝置

         if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < 0) {

... ...

            continue;

         } 

         encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;

        }

... ...

    }

... ...

}

 

●fs_mgr_mount_all 的流程

  •先mount fstab中的各個項。如果成功的話,表明對應項的塊裝置肯定沒有被加密(加密的裝置沒法被mount)

  •如果是強制加密,則先解除安裝這個裝置,然後設定返回值FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION

  •如果mount失敗,則需要判斷是不是因為加密裝置導致。如果該裝置沒有被清空(wiped),則先把tmpfs掛載到/data目錄下。然後設定返回值為FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED

 

3.1.3 handle_encryptable

static int handle_encryptable(struct fstab *fstab, const struct fstab_rec* rec)

{

//如果裝置是可加密的或者是強制加密的,那麼需要引導加密流程     

    if (   (rec->fs_mgr_flags & MF_FORCECRYPT)

        || (device_is_force_encrypted() && fs_mgr_is_encryptable(rec))) {

        if (umount(rec->mount_point) == 0) {

            return FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;

        } else {

            return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;

        }

    }

    //處理檔案級別的加密

     if (rec->fs_mgr_flags & MF_FILEENCRYPTION)

     {

      //忽略

     }

     return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;

}

 

掛載tmpfs/data/目錄下的原因:

    因為沒有這個/data目錄,系統根本沒辦法起來。因為系統很多執行時儲存的資訊,app自己的快取目錄等都放在/data下。為了保證系統能啟動,所以這裡先弄一個臨時/data目錄。等到後續把加密裝置通過device-mapper方式準備好後,我們再把加密裝置掛載到/data/目錄下。注意,加密裝置的mount不是直接掛載加密裝置,而是通過掛載一個device-mapper來實現的。

3.1.4 處理mount結果

●如果是強制裝置第一次開機,設定“vold.decrypt”屬性值為“trigger_encryption”。

●如果是已經加密裝置的後續開機,設定“ro.crypto.state”屬性值為“encrypted”,同時設定"vold.decrypt"屬性值為"trigger_default_encryption"。

●非加密裝置,設定"ro.crypto.state"值為"unencrypted"。

 

3.2 強制加密裝置的第一次開機

do_mount_all 的執行結果為設定了vold.decrypt = trigger_encryption,會觸發加密:

 

init.rc:

on property:vold.decrypt=trigger_encryption

    start surfaceflinger

start encrypt

 

# One shot invocation to encrypt unencrypted volumes

service encrypt /system/bin/vdc --wait cryptfs enablecrypto inplace default

    disabled

    oneshot

    # vold will set vold.decrypt to trigger_restart_framework (default

# encryption)

 

service surfaceflinger /system/bin/surfaceflinger

    class core

    user system

    group graphics drmrpc

    onrestart restart zygote

    writepid /dev/cpuset/system-background/tasks

 

強制加密裝置的首次加密將由vdc來處理。vdc其實不過是給vold傳送enablecrypto命令罷了,真正的加密工作是由vold來完成的。

vold 進行加密的流程後續分析。

 

3.3 已加密裝置的開機

init.rc

on property:vold.decrypt=trigger_default_encryption

start defaultcrypto

# One shot invocation to deal with encrypted volume.

service defaultcrypto /system/bin/vdc --wait cryptfs mountdefaultencrypted

    disabled

    oneshot

    # vold will set vold.decrypt to trigger_restart_framework (default

# encryption) or trigger_restart_min_framework (other encryption)

 

也是最後有vold來完成的,後續再分析vold的加密流程。

3.4 正常開機後主動進行加密

3.4.1 Settings 流程

使用者通過在Settings應用“安全”-->“加密手機”開啟加密流程,因為加密耗時,Settings會需求手機連線USB線並且手機電池處於100%狀態。

     選擇加密

3.4.1.1 CryptKeeperSettings

CryptKeeperSettings.java::onCreateView

mInitiateButton = (Button) mContentView.findViewById(R.id.initiate_encrypt);

即為加密按鈕點選加密後觸發:

private Button.OnClickListener mInitiateListener = new Button.OnClickListener() {

   public void onClick(View v) {

     if (!runKeyguardConfirmation(KEYGUARD_REQUEST))

   }

 

 private boolean runKeyguardConfirmation(int request) {

   Resources res = getActivity().getResources();

   ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(getActivity(), this);

    //DevicePolicyManager那獲取裝置的密碼控制要求,如果沒有指定的話,將直接進入

   if (helper.utils().getKeyguardStoredPasswordQuality(UserHandle.myUserId())

                == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {

      //一般是沒有設定密碼控制級別的,所以加密的時候將使用預設加密,密碼為””

      showFinalConfirmation(StorageManager.CRYPT_TYPE_DEFAULT, "");

      return true;

    }

      return helper.launchConfirmationActivity(request,

                res.getText(R.string.crypt_keeper_encrypt_title), true);

  }

 

  從Android 5.1 開始,系統支援一種預設的加密方式,即加密預設使用者傳入的密碼是“default_password”。這是在使用者沒有設定解鎖方法的情況下,系統預設的行為。如果有DevicePolicyManager或者使用者自己設定瞭解鎖密碼,則系統會用它們做為裝置加密密碼。

 

CryptKeeperSettings.java::showFinalConfirmation

private void showFinalConfirmation(int type,String password) {

   Preference preference = new Preference(getActivity());

    //啟動CrypteKeeperConfirm介面

   preference.setFragment(CryptKeeperConfirm.class.getName());

   preference.setTitle(R.string.crypt_keeper_confirm_title);

    //type為預設的加密方法,password為””

   preference.getExtras().putInt("type", type);

   preference.getExtras().putString("password", password);

   ((SettingsActivity) getActivity()).onPreferenceStartFragment(null,

         preference);

}

 

3.4.1.2CryptKeeperConfirm

CryptKeeperConfirm.java::mFinalClickListener

private Button.OnClickListener mFinalClickListener= new

                Button.OnClickListener() {

   publicvoid onClick(View v) {

        .....

        //啟動一個Blank Activity,程式碼也在此檔案中

       Intent intent = new Intent(getActivity(), Blank.class);

       intent.putExtras(getArguments());

       startActivity(intent);

 

        //2. The system locale.

        try{

        //呼叫MountServicesetField功能,設定系統語言

       IBinder service = ServiceManager.getService("mount");

       IMountService mountService = IMountService.Stub.asInterface(service);

       mountService.setField("SystemLocale",

                    Locale.getDefault().toLanguageTag());

        }......

    }

 };

 

CryptKeeperConfirm.java::Blank

    public static class Blank extends Activity {

        private Handler mHandler = new Handler();

 

        @Override

        public void onCreate(Bundle savedInstanceState) {

            setContentView(R.layout.crypt_keeper_blank);

 .. ... ...

             //禁止使用狀態列的功能,加密過程最好是不要被打攪!

            StatusBarManager sbm = (StatusBarManager) getSystemService(Context.STATUS_BAR_SERVICE);

            sbm.disable(StatusBarManager.DISABLE_EXPAND

                    | StatusBarManager.DISABLE_NOTIFICATION_ICONS

                    | StatusBarManager.DISABLE_NOTIFICATION_ALERTS

                    | StatusBarManager.DISABLE_SYSTEM_INFO

                    | StatusBarManager.DISABLE_HOME

                    | StatusBarManager.DISABLE_SEARCH

                    | StatusBarManager.DISABLE_RECENT

                    | StatusBarManager.DISABLE_BACK);

... ...

            mHandler.postDelayed(new Runnable() {

                public void run() {

                    IBinder service = ServiceManager.getService("mount");

... ...

                   //呼叫MountServiceencrytStorage方法

                     mountService.encryptStorage(args.getInt("type", -1), args.getString("password"));

                    }

... ...

        }

    MountService encryptStorage 方法最後呼叫vold服務進行後續的加密流程,我們在後續裡面分析。

 

     加密進度顯示

●當vold在加密裝置的過程中,這個時間可能很長很長,根據裝置中之前是否有很多資料有關。

 

●另外,掛載加密後的裝置時,可能使用者用得不是預設密碼,那這個時候需要啟動framework,然後彈出一個密碼輸入框讓使用者輸入密碼。這也就是為什麼加密裝置掛載時會先mount一個tmpfs/data分割槽下。因為這個時候framework裡那些service是要啟動的,還有輸入法等所謂的core app

 

在加密過程中或者加密裝置掛載前,framework有一些特殊的處理:

 

SystemServer.java::startBootstrapServices

private void startBootstrapServices() {

... ...

StringcryptState = SystemProperties.get("vold.decrypt");

    // ENCRYPTING_STATE值為“trigger_restart_min_framework

    if(ENCRYPTING_STATE.equals(cryptState)) {

       mOnlyCore= true;

    } elseif (ENCRYPTED_STATE.equals(cryptState)) {

     // ENCRYPTED_STATE值為“1

       mOnlyCore = true;

    }

... ...

}

 

當屬性vold.decrypt值為“trigger_restart_min_framework或者為”1的時候,mOnlyCoretruemOnlyCoretrue的時候,系統啟動後只會解析在AndroidManifest.xml中標記coreApp = trueactivity

SettingsCryptKepper.java 會接受這個Home Intent。如下圖:

             

為了防止在加密過程中使用者亂動,狀態列和虛擬按鍵欄都沒法使用了。

在加密前,還會判斷是否應該讓使用者輸入密碼驗證是否允許掛載加密裝置。這個時候UI介面是:

               

 

對於掛載加密裝置來說,系統其實並不是真的去解密儲存裝置,然後再掛載。而僅是判斷使用者輸入的密碼是不是對的。如果是對的話,它會通過device-mapper去掛載一個虛擬裝置到/data下,而這個虛擬裝置一方面連著實際的block裝置。這樣,當用戶從/data讀取、寫入資料時,這個虛擬裝置都會把資料進行加解密。比如使用者讀資料時,它會從真實裝置裡讀取加密後的資料,然後解密返回給使用者。使用者寫資料時候,它會加密後再寫到真實裝置中!

3.5 vold 加密流程

 

 

 

MountService中encryptStorage會將根據傳入密碼type直接傳入vold中,這裡,密碼的type有如下幾種:

    /// Consts to match the password types in cryptfs.h

    /** @hide */

    public static final int CRYPT_TYPE_PASSWORD = 0;

    /** @hide */

    public static final int CRYPT_TYPE_DEFAULT = 1;

    /** @hide */

    public static final int CRYPT_TYPE_PATTERN = 2;

    /** @hide */

public static final int CRYPT_TYPE_PIN = 3;

 

如果未設定密碼,這對於的密碼type為CRYPT_TYPE_DEFAULT。

 

enablecrypto對應的處理函式入口是cryptfs_enable(設定了密碼)或者是cryptfs_enable_default(未設定密碼)。它們內部都會呼叫cryptfs_enable_internal

3.5.1 cryptfs_enable_internal

/system/vold/cryptfs.c::cryptfs_enable_internal

 

 ●howarg :wipe 或者 inplace

          • wipe:表示先清乾淨儲存裝置,然後加密

          • inplace:對現有裝置進行加密

 ●crypt_type :加密密碼的種類,password ,pin 或者patter。沒設定密碼則為default。

 ●passwd : 加密密碼,如果沒有設定密碼,則為“default_passwd”

 

int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,

                            int no_ui)

{

... ...

    if(!strcmp(howarg, "wipe")) {

      how =CRYPTO_ENABLE_WIPE;

    } elseif (! strcmp(howarg, "inplace")) {

      how = CRYPTO_ENABLE_INPLACE; //我們對應這種情況

}

... ...

    /*

     不知道什麼原因,上一次加密還沒處理完,系統就重啟或者vold就重啟了。為了處理這種情況,vold會把加密過程都寫到一個地方去,然後要每次加密前都需要檢查。因為一旦加密被幹擾而又沒正確處理的話,使用者資料就會丟失,這是非常嚴重的事故!

    */

if (how == CRYPTO_ENABLE_INPLACE

          && get_crypt_ftr_and_key(&crypt_ftr) == 0

          && (crypt_ftr.flags & CRYPT_ENCRYPTION_IN_PROGRESS)) {

 

}

 

... ...

}

 

     3.5.1.1 get_crypt_ftr_and_key

/system/vold/cryptfs.c::get_crypt_ftr_and_key

static int get_crypt_ftr_and_key(struct crypt_mnt_ftr *crypt_ftr) {

... ...

  /*

   get_crypt_ftr_info用於獲取加密資訊的儲存位置。按Android的設計,這個資訊可以儲存在

   兩個地方。一個是fstab裡encryptable=xxxx中xxx這個儲存裝置裡,也可以儲存在

   需要掛載的裝置裡最後一段空間裡。比如fstab.flo中,

   1 /dev/block/platform/msm_sdcc.1/by-name/userdata是要掛載的裝置,所以加密資訊

     儲存在這個裝置的最後一段空間裡。但是由於我們還有下面這句話:

   2 encryptable=/dev/block/platform/msm_sdcc.1/by-name/metadata

     所以實際的加密資訊儲存在metadata檔案裡

  */

 

//fname:指明加密資訊儲存在哪個檔案中

//starting_off:指明加密資訊儲存在檔案的那個位置

  if (get_crypt_ftr_info(&fname, &starting_off)) {

    SLOGE("Unable to get crypt_ftr_info\n");

    return -1;

  }

... ...

//將加密資訊儲存在crypt_mnt_ftr 這個結構體中

  if ( (cnt = read(fd, crypt_ftr, sizeof(struct crypt_mnt_ftr))) != sizeof(struct crypt_mnt_ftr))     

... ...

}

 

以上:

加密所用的上下文資訊(包括加密版本號,金鑰資訊,加密方法,加密進度等)都是儲存在某個地方。這個地方可以是加密裝置最後一塊區域,也可以單獨指定一個檔案(通過fstab encryptable=xxx來指定)。

 

我們繼續分析cryptfs_enable_internel:

 

int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,

                            int no_ui)

{

 //確認是wipe資料還是對現有資料進行加密

 //過去加密密匙位置和其儲存位置

 

 

  property_get("ro.crypto.state", encrypted_state, "");

  if(!strcmp(encrypted_state, "encrypted") &&!previously_encrypted_upto) {

     //previously_encrypted_upto表示上一次加密的進度,如果是0的話,而加密狀態

       又是”encrypted”,就表示有問題

        goto error_unencrypted;

   }

   //取出key的儲存路徑和待加密的路徑

   fs_mgr_get_crypt_info(fstab, key_loc, 0, sizeof(key_loc));

   fs_mgr_get_crypt_info(fstab, 0, real_blkdev, sizeof(real_blkdev));

   ... ...

       //開啟真實裝置並獲取它的誇個數!

       fd =open(real_blkdev, O_RDONLY);

       nr_sec = get_blkdev_size(fd));

       close(fd);

       ... ...

       //加密耗電,所以要搞一個WakeLock

       acquire_wake_lock(PARTIAL_WAKE_LOCK, lockid);

       ... ...

       //設定init屬性,vold.decrypt = trigger_shutdown_frameworkmainmlalate_start 型別的服務關閉,開始進行加密前,需要解除安裝/data分割槽,因為系統資料都儲存在/data下面,需要先關閉主要服務來避免系統崩潰。

 

 

        property_set("vold.decrypt","trigger_shutdown_framework");

        //呼叫VolumeManager 解除安裝vold維護的所有的volume

        if (vold_unmountAll())

        ... ...

        //解除安裝 /data 分割槽

        if (wait_and_unmount(DATA_MNT_POINT, false))

        ... ...

        if (how == CRYPTO_ENABLE_INPLACE) {

          //inplace 代表隊原資料進行加密

          //現在 /data分割槽已經被解除安裝了,我們就需要掛載一個臨時替代的data分割槽,呼叫fs_mgr_do_tmpfs_mount 實現。

          if (fs_mgr_do_tmpfs_mount(DATA_MNT_POINT)) {

            goto error_shutting_down;

          }

          //用系統屬性vold.encrypt_progress 來記錄加密的進度

          property_set("vold.encrypt_progress", "0");

          if (prep_data_fs()) {

            // prep_data_fs的作用初始之前tmp_data分割槽掛載後,系統在tmp_data 區中建立系統執行的必要資料夾,為後續系統在tmp_data下與執行做準備。

            goto error_shutting_down;

          }

        }

    ... ...

}

 

     3.5.1.2 prep_data_fs

prep_data_fs() 的作用就是保證init中/data區中應該建立的檔案都正常建立:

 

init 會在建立完所有的檔案後,賦值vold.post_fs_data_done = 1

 

static int prep_data_fs(void)

{

    property_set("vold.post_fs_data_done", "0");

    property_set("vold.decrypt", "trigger_post_fs_data");

... ...

    for (i=0; i<DATA_PREP_TIMEOUT; i++) {

        char p[PROPERTY_VALUE_MAX];

 

        property_get("vold.post_fs_data_done", p, "0");

        //檢查到屬性值為1 ,確認data分割槽下檔案建立完畢

        if (*p == '1') {

            break;

        } else {

            usleep(50000);

        }

... ...

}

 

 

繼續分析crypfs_enable_internel

 

int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,

                            int no_ui)

{

     ... ... ...

     ... ... ...

      // 首次加密,進度為0

相關推薦

輸出 Android 加密 模組原始碼分析

                                   Android6.0 加密模組解析

輸出 Android 儲存模組 解析

  【儲存模組培訓文件,很認真寫的,現在轉移到部落格上】                                

輸出 Android 6 USB 模組解析

          USB模組解析 簡介 主要完成USB 功能的切換和狀態的更新。通過接受kernel傳送過來的event訊息來確定USB狀態的切換並向外界傳送廣播訊息。  

輸出 Android MountService 原始碼分析

Android 儲存裝置管理框架 在android之VOLD程序啟動原始碼分析一文中介紹了儲存裝置的管控中心Vold程序,Vold屬於native後臺程序,通過netlink方式接收kernel的uevent訊息,並通過socket方式將uevent訊息傳送給MountService,同時實時接

android官方android AIDL

概述      AIDL(安卓介面解釋語言)和其他的IDLs類似。可以定義程式介面讓客戶端和service進行跨程序的通訊(IPC)。在android中,一個程序通常不能訪問另一個程序的記憶體。所以,他們的物件需要被分解成更原始的單位,直到系統可以理解,並且集結這些物件穿

渡者 逆向通關分析報告配置

資源管理 遊戲數據 功能 ollydbg 文件 更新 exe 管理 olly 一、工具及遊戲介紹 使用工具:Beyond Compare,010Editor,Ollydbg 實現功能:全部通關。 ferryman 渡者 二、逆向邏

Mybatis配置就是這麽簡單

nds util tin 資源 選擇 settings body 公眾號 正常 配置文件和映射文件還有挺多的屬性我還沒有講的,現在就把它們一一補全 映射文件 在mapper.xml文件中配置很多的sql語句,執行每個sql語句時,封裝為MappedStatement對象,m

zabbix配置

zabbixzabbxi中nginx配置文件worker_processes 1;events { worker_connections 1024;}http { include mime.types; default_type application/octet-

技術jeecg3.8-maven 開發環境搭建入門

JEECG 微雲快速開發平臺(3.8)Eclipse-Maven版本手把手入門手冊 官方標準開發工具: 1. IDE         Eclipse Java EE IDE for Web Developers.         Version: Helios

docker安裝telnet 命令官方

1.Install telnet use this command in terminal(Applications/Accessories/Terminal): sudo apt-get install xinetd telnetd 2.Edit /et

kubernetes/k8s概念CNI host-local原始碼分析

接著上章節假設host-local成功分配IP,這章節講解host-local 原始碼地址: https://github.com/containernetworking/plugins 引數 { "name": "macvlannet", "cniVers

kubernetes/k8s概念CNI plugin calico原始碼分析

       calico解決不同物理機上容器之間的通訊,而calico-plugin是在k8s建立Pod時為Pod設定虛擬網絡卡(容器中的eth0和lo網絡卡),calico-plugin是由兩個靜態的二進位制檔案組成,由kubelet以命令列的形式呼叫,這兩個二進位制

Python練習7對比並輸出可讀性較強的html

spl error images 拷貝 tex https 打開 try odi 題目: 利用python寫出一個可在shell中執行的命令mydiff,用於比對兩個文件之間的區別,輸出可讀性較強的html頁面源碼,可保存到html文件中進行查看。 格式: mydiff

Android官方翻譯Android官方-Activities(一)

Activity是可以給使用者提供互動操作的程式元件,例如打電話,拍照,傳送郵件,抑或者是顯示地圖。通常視窗會填滿螢幕,但是也可以做到比螢幕小或者是懸浮在視窗頂部。 App通常由多個Activities組成,它們之間支援相互跳轉。一般情況下,每個Activit

轉載使用Response.WriteFile輸出以及圖片

net 路徑 ref href write 技術 文件流 使用 targe Response對象是Asp.Net應用程序中非常重要的一個內置對象,其作用為負責將服務器執行好的信息輸出給客戶端,可以使用Response.WriteFile方法來像客戶端輸出文件或者圖片,輸出圖

用戶空間和內核空間通訊之proc系統

page ext4 sha 依靠 全局變量 dmesg 設備 net url 今天我們介紹還有一種用戶內核空間通信的方法:proc文件系統。 proc文件系統作為linux提供的一種虛擬文件系統並不占用實際外圍存儲空間,它僅存在於內存中。系統斷電即消失。proc

FileSizeUtil獲取夾或的大小

如果 folder exce XML director con 目錄 pri Coding 版權聲明:本文為博主原創文章,未經博主允許不得轉載。 前言 獲取文件夾或者文件的大小,可以指定單位,也可以自動計算合適的單位值。 效果圖 代碼分析 常用的方法: g

python-ConfigParser模塊讀寫配置

target new start 需要 details string 如何 設置變量 board http://www.codesky.net/article/201003/122500.html http://www.linuxso.com/linuxbiancheng/

Ajax 提交表單包括上傳

color .ajax log clas multipart slim turn tex size 利用js插件實現 <script src="@Url.Content("~/js/layer/jquery.form.min.js")"></script&

java 讀寫操作 生成隨機數,寫入txt,然後從txt中讀出

進行 rac 直接 生成隨機數 catch trace buffered 代碼 tac 1.把生成的隨機數寫入到文件中 1 public static void WriterFun(){ 2 //獲得路徑 3 String filep