Android加密之全盤加密

Android加密之全盤加密

前言

Android 的安全性問題一直備受關(guān)注朗伶,Google 在 Android 系統(tǒng)的安全方面也是一直沒有停止過更新,努力做到更加安全的手機(jī)移動(dòng)操作系統(tǒng)步咪。

在 Android 的安全性方面论皆,有很多模塊:

  1. 內(nèi)核安全性
  2. 應(yīng)用安全性
  3. 應(yīng)用簽名
  4. 身份驗(yàn)證
  5. Trusty TEE
  6. SELinux
  7. 加密
    等等

其中,加密又分全盤加密(Android 4.4 引入)和文件級(jí)加密(Android 7.0 引入)猾漫,本文將論述加密中的全盤加密的基本知識(shí)点晴。全盤加密在 Android 4.4 中引入,在 Android 5.0 中做了比較大的更新悯周。

device-2017-07-24-161321.png

本文部分片段摘自 Android 官網(wǎng)粒督,融合筆者的個(gè)人理解和知識(shí)。

什么是全盤加密

全盤加密是使用已加密的密鑰對(duì) Android 設(shè)備上的所有用戶數(shù)據(jù)進(jìn)行編碼的過程禽翼。設(shè)備經(jīng)過加密后屠橄,所有由用戶創(chuàng)建的數(shù)據(jù)在寫入磁盤之前都會(huì)自動(dòng)加密,并且所有讀取操作都會(huì)在將數(shù)據(jù)返回給調(diào)用進(jìn)程之前自動(dòng)解密數(shù)據(jù)闰挡。

Android 5.0 中又引入了以下新功能:

  • 創(chuàng)建了快速加密方式锐墙,這種加密方式只會(huì)對(duì)數(shù)據(jù)分區(qū)中已使用的分塊進(jìn)行加密,以免首次啟動(dòng)用時(shí)過長(zhǎng)长酗。目前只有 EXT4 和 F2FS 文件系統(tǒng)支持快速加密溪北。
  • 添加了 forceencrypt fstab 標(biāo)記,以便在首次啟動(dòng)時(shí)進(jìn)行加密夺脾。
  • 添加了對(duì)解鎖圖案和無密碼加密的支持之拨。
  • 添加了由硬件支持的加密密鑰存儲(chǔ)空間,該空間使用可信執(zhí)行環(huán)境(TEE咧叭,例如 TrustZone)的簽名功能蚀乔。

全盤加密運(yùn)作方式

Android 全盤加密基于在塊設(shè)備層運(yùn)行的內(nèi)核功能 dm-crypt。因此菲茬,這種加密方式適用于以塊設(shè)備的形式呈現(xiàn)給內(nèi)核的嵌入式多媒體卡 (eMMC) 和類似閃存設(shè)備吉挣。YAFFS 會(huì)直接與原始 NAND 閃存芯片交互,無法進(jìn)行全盤加密生均。

全盤加密采用的是 128 位高級(jí)加密標(biāo)準(zhǔn) (AES) 算法(搭配密碼塊鏈接 (CBC) 和 ESSIV:SHA256)。對(duì)主密鑰進(jìn)行加密時(shí)使用的是 128 位 AES 算法腥刹,并會(huì)調(diào)用 OpenSSL 庫(kù)马胧。對(duì)于該密鑰,您必須使用 128 位或更多位(可以選擇 256 位)衔峰。

Android 5.0 版中有以下 4 種加密狀態(tài):

  • 默認(rèn)
  • PIN 碼
  • 密碼
  • 解鎖圖案
device-2017-07-24-161945.png

首次啟動(dòng)時(shí)佩脊,設(shè)備會(huì)創(chuàng)建一個(gè)隨機(jī)生成的 128 位主密鑰蛙粘,然后會(huì)使用默認(rèn)密碼和存儲(chǔ)的鹽對(duì)其進(jìn)行哈希處理。默認(rèn)密碼是“default_password”威彰。不過出牧,設(shè)備還會(huì)通過 TEE(例如 TrustZone)為生成的哈希簽名。TEE 會(huì)使用相應(yīng)簽名的哈希來加密主密鑰歇盼。

您可以在 Android 開放源代碼項(xiàng)目 cryptfs.c 文件中找到定義的默認(rèn)密碼舔痕。

當(dāng)用戶在設(shè)備上設(shè)置 PIN 碼/通行碼或密碼時(shí),只有 128 位的密鑰會(huì)被重新加密并存儲(chǔ)起來(也就是說豹缀,更改用戶 PIN 碼/通行碼/解鎖圖案不會(huì)導(dǎo)致重新加密用戶數(shù)據(jù))伯复。請(qǐng)注意,受管理的設(shè)備可能受 PIN 碼邢笙、解鎖圖案或密碼限制啸如。

加密操作由 init 和 vold 管理。 init 負(fù)責(zé)調(diào)用 vold氮惯,然后 vold 會(huì)設(shè)置相關(guān)屬性以觸發(fā) init 中的事件叮雳。系統(tǒng)的其他部分也會(huì)查看這些屬性以執(zhí)行各項(xiàng)任務(wù),例如報(bào)告狀態(tài)妇汗、提示輸入密碼帘不,或有嚴(yán)重錯(cuò)誤發(fā)生時(shí)提示恢復(fù)出廠設(shè)置。為了調(diào)用 vold 中的加密功能铛纬,系統(tǒng)會(huì)使用命令行工具 vdc 的 cryptfs 命令:checkpw厌均、restart、enablecrypto告唆、changepw棺弊、cryptocomplete、verifypw擒悬、setfield模她、getfield、mountdefaultencrypted懂牧、getpwtype侈净、getpw 以及 clearpw。

要加密僧凤、解密或清空 /data畜侦,/data 不得處于裝載狀態(tài)。但要顯示任何界面躯保,框架都必須啟動(dòng)旋膳,而框架需要 /data 才能運(yùn)行。為了解決這一沖突途事,/data 上會(huì)裝載一個(gè)臨時(shí)文件系統(tǒng)验懊。通過該文件系統(tǒng)擅羞,Android 可以提示輸入密碼、顯示進(jìn)度或根據(jù)需要建議清除數(shù)據(jù)义图。不過减俏,該文件系統(tǒng)會(huì)帶來以下限制:要從臨時(shí)文件系統(tǒng)切換到實(shí)際的 /data 文件系統(tǒng),系統(tǒng)必須停止臨時(shí)文件系統(tǒng)中打開了文件的所有進(jìn)程碱工,并在實(shí)際的 /data 文件系統(tǒng)中重啟這些進(jìn)程娃承。為此,所有服務(wù)都必須位于以下其中一個(gè)組內(nèi):core痛垛、main 和 late_start草慧。

  • core:?jiǎn)?dòng)后一直不會(huì)關(guān)閉。
  • main:關(guān)閉匙头,然后在用戶輸入磁盤密碼后會(huì)重啟漫谷。
  • late_start:在 /data 未解密并裝載之前,一直不會(huì)啟動(dòng)蹂析。

為了觸發(fā)這些操作舔示,vold.decrypt 屬性會(huì)被設(shè)為多種字符串。要結(jié)束和重啟服務(wù)电抚,請(qǐng)使用以下 init 命令:

  • class_reset:停止相應(yīng)服務(wù)惕稻,但允許通過 class_start 重啟該服務(wù)。
  • class_start:重啟相應(yīng)服務(wù)蝙叛。
  • class_stop:停止相應(yīng)服務(wù)并添加 SVC_DISABLED 標(biāo)記俺祠。被停止的服務(wù)不會(huì)對(duì)。
  • class_start 做出響應(yīng)借帘。

加密流程和啟動(dòng)流程

使用 forceencrypt 加密新設(shè)備

這是 Android 5.0 設(shè)備首次啟動(dòng)時(shí)的常規(guī)流程蜘渣。

  1. 檢測(cè)帶有 forceencrypt 標(biāo)記的未加密文件系統(tǒng)

    /data 未加密,但需要加密肺然,因?yàn)?forceencrypt 強(qiáng)制要求進(jìn)行此項(xiàng)加密蔫缸。卸載 /data。

  2. 開始加密 /data

    vold.decrypt = "trigger_encryption" 會(huì)觸發(fā) init.rc际起,從而使 vold 對(duì) /data 進(jìn)行無密碼加密拾碌。(因?yàn)檫@應(yīng)該是新設(shè)備,還沒有設(shè)置密碼街望。)

  3. 裝載 tmpfs

    vold 會(huì)裝載一個(gè) tmpfs /data(使用 ro.crypto.tmpfs_options 中的 tmpfs 選項(xiàng))校翔,并會(huì)將 vold.encrypt_progress 屬性設(shè)為 0。 vold 會(huì)準(zhǔn)備 tmpfs /data 以便啟動(dòng)已加密的系統(tǒng)灾前,并會(huì)將 vold.decrypt 屬性設(shè)為 trigger_restart_min_framework

  4. 啟動(dòng)框架以顯示進(jìn)度

    由于設(shè)備上幾乎沒有要加密的數(shù)據(jù)防症,加密過程很快就會(huì)完成,因此實(shí)際上通常并不會(huì)顯示進(jìn)度條。如需關(guān)于進(jìn)度界面的更多詳細(xì)信息告希,請(qǐng)參閱加密現(xiàn)有設(shè)備。

  5. /data 加密后烧给,關(guān)閉框架

    vold 會(huì)將 vold.decrypt 設(shè)為 trigger_default_encryption燕偶,這會(huì)啟動(dòng) defaultcrypto 服務(wù)。(這會(huì)啟動(dòng)以下流程來裝載默認(rèn)的已加密用戶數(shù)據(jù)础嫡。)trigger_default_encryption 會(huì)檢查加密類型指么,以了解 /data 加密是否使用了密碼。由于 Android 5.0 設(shè)備是在首次啟動(dòng)時(shí)加密榴鼎,應(yīng)該沒有設(shè)置任何密碼伯诬,因此我們要解密并裝載 /data。

  6. 裝載 /data

    接下來巫财,init 會(huì)使用從 ro.crypto.tmpfs_options(在 init.rc 中設(shè)置)中選取的參數(shù)在 tmpfs RAMDisk 中裝載 /data盗似。

  7. 啟動(dòng)框架

    將 vold 設(shè)為 trigger_restart_framework,這會(huì)繼續(xù)常規(guī)啟動(dòng)過程平项。

啟動(dòng)未進(jìn)行默認(rèn)加密的已加密設(shè)備

當(dāng)您啟動(dòng)設(shè)有密碼的已加密設(shè)備時(shí)赫舒,則會(huì)發(fā)生該流程。設(shè)備的密碼可以是 PIN 碼闽瓢、解鎖圖案或密碼接癌。

  1. 檢測(cè)設(shè)有密碼的已加密設(shè)備

    會(huì)發(fā)現(xiàn) Android 設(shè)備已加密,因?yàn)樵O(shè)置了 ro.crypto.state = "encrypted" 標(biāo)記

    由于 /data 是使用密碼加密的扣讼,因此 vold 會(huì)將 vold.decrypt 設(shè)為 trigger_restart_min_framework缺猛。

  2. 裝載 tmpfs

    init 會(huì)設(shè)置 5 個(gè)屬性,以保存為 /data(包含從 init.rc 傳入的參數(shù))提供的初始裝載選項(xiàng)椭符。 vold 會(huì)使用這些屬性來設(shè)置加密映射:
    ro.crypto.fs_type
    ro.crypto.fs_real_blkdev
    ro.crypto.fs_mnt_point
    ro.crypto.fs_options
    ro.crypto.fs_flags (ASCII 碼 8 位十六進(jìn)制數(shù)字荔燎,以 0x 開頭)

  3. 啟動(dòng)框架以提示輸入密碼

    框架會(huì)啟動(dòng)并看到 vold.decrypt 已設(shè)為 trigger_restart_min_framework。這讓框架知道自己是在 tmpfs /data 磁盤中啟動(dòng)的艰山,并且需要獲取用戶密碼湖雹。

    不過,它首先需要確認(rèn)磁盤是否已經(jīng)過適當(dāng)加密曙搬。它會(huì)向 vold 發(fā)送 cryptfs cryptocomplete 命令摔吏。 如果加密已成功完成,vold 會(huì)返回 0纵装;如果發(fā)生內(nèi)部錯(cuò)誤征讲,則會(huì)返回 -1;如果加密未成功完成橡娄,則會(huì)返回 -2诗箍。vold 通過查看 CRYPTO_ENCRYPTION_IN_PROGRESS 標(biāo)記的加密元數(shù)據(jù)來確定應(yīng)返回的值。如果設(shè)置了此標(biāo)記挽唉,則表示加密過程中斷了滤祖,并且設(shè)備上沒有可用的數(shù)據(jù)筷狼。如果 vold 返回錯(cuò)誤,界面中應(yīng)顯示一條消息匠童,提示用戶重新啟動(dòng)設(shè)備并將其恢復(fù)出廠設(shè)置埂材,并且界面中應(yīng)為用戶提供一個(gè)用于執(zhí)行該操作的按鈕。

  4. 通過密碼解密數(shù)據(jù)

    cryptfs cryptocomplete 成功后汤求,框架會(huì)顯示一個(gè)界面俏险,提示用戶輸入磁盤密碼。界面會(huì)向 vold 發(fā)送 cryptfs checkpw 命令來檢查用戶輸入的密碼扬绪。如果密碼正確(通過以下方式判定:在臨時(shí)位置成功裝載已解密的 /data竖独,然后將其卸載),vold 會(huì)將已解密塊設(shè)備的名稱保存在 ro.crypto.fs_crypto_blkdev 屬性中挤牛,并向界面返回狀態(tài) 0莹痢。如果密碼不正確,則向界面返回 -1墓赴。

  5. 停止框架

    界面會(huì)顯示加密啟動(dòng)圖形格二,然后使用 cryptfs restart 命令調(diào)用 vold。vold 會(huì)將 vold.decrypt 屬性設(shè)為 trigger_reset_main竣蹦,這會(huì)使 init.rc 執(zhí)行 class_reset main 命令顶猜。此命令會(huì)停止 main 類中的所有服務(wù),以便卸載 tmpfs /data痘括。

  6. 裝載 /data

    然后长窄,vold 會(huì)裝載已解密的實(shí)際 /data 分區(qū),并準(zhǔn)備新的分區(qū)(如果加密時(shí)采用了首次發(fā)布不支持的數(shù)據(jù)清除選項(xiàng)纲菌,則可能永遠(yuǎn)無法準(zhǔn)備就緒)挠日。它會(huì)將 vold.post_fs_data_done 屬性設(shè)為 0,接著將 vold.decrypt 設(shè)為 trigger_post_fs_data翰舌。這會(huì)使 init.rc 運(yùn)行其 post-fs-data 命令嚣潜。這些命令會(huì)創(chuàng)建所有必要的目錄或鏈接,然后將 vold.post_fs_data_done 設(shè)為 1椅贱。當(dāng) vold 看到該屬性中的 1 時(shí)懂算,會(huì)將 vold.decrypt 屬性設(shè)為 trigger_restart_framework。這會(huì)使 init.rc 再次啟動(dòng) main 類中的服務(wù)庇麦,并啟動(dòng) late_start 類中的服務(wù)(這是設(shè)備啟動(dòng)后首次啟動(dòng)這些服務(wù))计技。

  7. 啟動(dòng)整個(gè)框架

    現(xiàn)在,框架會(huì)使用已解密的 /data 文件系統(tǒng)啟動(dòng)其所有服務(wù)山橄,接下來系統(tǒng)就可以使用了垮媒。

代碼解讀

結(jié)合上章節(jié)的流程,下面用代碼來解析啟動(dòng)未進(jìn)行默認(rèn)加密的已加密設(shè)備這個(gè)流程。

# Android fstab file.
#<src>                                         <mnt_point>  <type> 
...... 
/dev/block/platform/soc.0/f9824900.sdhci/by-name/userdata     /data           ext4    noatime,nosuid,nodev,barrier=1,data=ordered,nomblk_io_submit,noauto_da_alloc,errors=panic wait,check,fileencryption  
......
defaults

這個(gè)配置定義在 device/lge/bullhead/fstab_fbe.bullhead 文件中睡雇。

如上面的代碼萌衬,在 /data 的末尾加上 fileencryption,便會(huì)進(jìn)行全盤加密它抱。

步驟1:檢測(cè)設(shè)有密碼的已加密設(shè)備

//設(shè)置ro.crypto.state標(biāo)記奄薇,手機(jī)已被用戶加密
static int do_mount_all(const std::vector<std::string>& args) {
    ......
    if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
        ActionManager::GetInstance().QueueEventTrigger("encrypt");
    } else if (ret == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
        // 全盤加密,ro.crypto.state = encrypted抗愁, ro.crypto.type = block
        property_set("ro.crypto.state", "encrypted");
        property_set("ro.crypto.type", "block");
        //發(fā)送vdc 命令 defaultcrypto
        ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
    ......
    } else if (ret == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
        if (e4crypt_install_keyring()) {
            return -1;
        }
        property_set("ro.crypto.state", "encrypted");
        property_set("ro.crypto.type", "file");
        ......

    return ret;
}

這個(gè)方法定義在文件 system/core/init/builtins.cpp 中。

# One shot invocation to deal with encrypted volume.
# do_mount_all 中寫入命令 defaultcrypto呵晚,執(zhí)行 vdc 發(fā)送命令 mountdefaultencrypted
on defaultcrypto
    exec - root -- /system/bin/vdc --wait cryptfs mountdefaultencrypted
    # vold will set vold.decrypt to trigger_restart_framework (default
    # encryption) or trigger_restart_min_framework (other encryption)

# One shot invocation to encrypt unencrypted volumes
on encrypt
    start surfaceflinger
    exec - root -- /system/bin/vdc --wait cryptfs enablecrypto inplace default noui
    # vold will set vold.decrypt to trigger_restart_framework (default
    # encryption)

這個(gè)服務(wù)定義在文件 system/vold/vdc.rc 中蜘腌。

int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
                                                 int argc, char **argv) {
    ......
    } else if (subcommand == "mountdefaultencrypted") {
        ......
        //執(zhí)行cryptfs_mount_default_encrypted
        std::thread(&cryptfs_mount_default_encrypted).detach();
    }
    ......
}

這個(gè)方法定義在文件 vold/CryptCommandListener.cpp 中。

int cryptfs_mount_default_encrypted(void)
{
    int crypt_type = cryptfs_get_password_type();
    ......
    } else if (crypt_type != CRYPT_TYPE_DEFAULT) {
        SLOGD("Password is not default - "
              "starting min framework to prompt");
        //不是默認(rèn)加密饵隙, 設(shè)置 vold.decrypt = trigger_restart_min_framework
        property_set("vold.decrypt", "trigger_restart_min_framework");
        return 0;
    } else if (cryptfs_check_passwd(DEFAULT_PASSWORD) == 0) {
    ......

這個(gè)方法定義在文件 system/vold/cryptfs.c 中撮珠。

#屬性vold.decrypt==trigger_restart_min_framework 時(shí)執(zhí)行
on property:vold.decrypt=trigger_restart_min_framework
    # A/B update verifier that marks a successful boot.
    exec - root -- /system/bin/update_verifier trigger_restart_min_framework
    class_start main

這個(gè)服務(wù)定義在服務(wù) system/core/rootdir/init.rc 中。

class_start main 可知重啟 main 類別的服務(wù)金矛。main 類別的服務(wù)包括:

Screenshot from 2017-08-02 17:51:53.png

會(huì)重啟 zygote芯急。

步驟2:裝載 tmpfs

Zygote 啟動(dòng)后,會(huì) fork() system_process 進(jìn)程驶俊,就是運(yùn)行 SystemServer 代碼了娶耍。但是 system_process 的運(yùn)行需要正常的用戶空間(/data),所以饼酿,需要臨時(shí)掛載 tmpfs 分區(qū)榕酒,這個(gè)分區(qū)是在內(nèi)存里分配的臨時(shí)空間。

int CommandListener::StorageCmd::runCommand(SocketClient *cli,
                                                      int argc, char **argv) {
    ......
    if (!strcmp(argv[1], "mountall")) {
        if (argc != 2) {
            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: mountall", false);
            return 0;
        }
        // 掛載所有設(shè)備
        fs_mgr_mount_all(fstab);
        cli->sendMsg(ResponseCode::CommandOkay, "Mountall ran successfully", false);
        return 0;
    }

這個(gè)方法定義在文件 system/vold/CommandListener.cpp 中故俐。

int fs_mgr_mount_all(struct fstab *fstab)
{
    ......
    /* mount(2) returned an error, handle the encryptable/formattable case */
    bool wiped = partition_wiped(fstab->recs[top_idx].blk_device);
    //掛載 tmpfs 臨時(shí)分區(qū)
    if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < 0) {
       ++error_count;
       continue;
    }
    //全盤加密
    encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
    ......
}
        
        

這個(gè)方法定義在文件 system/core/fs_mgr/fs_mgr.c 中想鹰。

步驟3:?jiǎn)?dòng)框架以提示輸入密碼

private void startBootstrapServices() {
    // Only run "core" apps if we're encrypting the device.
    //啟動(dòng)min-framework 顯示密碼輸入界面,僅啟動(dòng) coreApp药版, 在AndroidManifest.xml中聲明辑舷。
    //此時(shí)啟動(dòng)的 APP 在 tmpfs 臨時(shí)分區(qū),所以槽片,所有app都是原始安裝狀態(tài)何缓,不包含任何用戶使用產(chǎn)生的數(shù)據(jù)。
    String cryptState = SystemProperties.get("vold.decrypt");
    if (ENCRYPTING_STATE.equals(cryptState)) {
        Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
        mOnlyCore = true;
    } else if (ENCRYPTED_STATE.equals(cryptState)) {
        Slog.w(TAG, "Device encrypted - only parsing core apps");
        mOnlyCore = true;
    }
    ......
    mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
    mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
    ......
                
}

這個(gè)方法定義在文件frameworks/base/services/java/com/android/server/SystemServer.java 中还栓。

private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
            long currentTime, UserHandle user) throws PackageManagerException {
        if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
        PackageParser pp = new PackageParser();
        pp.setSeparateProcesses(mSeparateProcesses);
        // 設(shè)置僅解析 core app only,
        pp.setOnlyCoreApps(mOnlyCore);
        .....
}

這個(gè)方法定義在文件 frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java 中

private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
        final PackageLite lite = parseClusterPackageLite(packageDir, 0);

        // 如果不是 !lite.coreApp歌殃, 跳過該 app,即啟動(dòng)時(shí)蝙云,不會(huì)安裝該app
        if (mOnlyCoreApps && !lite.coreApp) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                    "Not a coreApp: " + packageDir);
        }

這個(gè)方法定義在文件 frameworks/base/core/java/android/content/pm/PackageParser.java 中氓皱。

安卓中定義為 coreApp 的應(yīng)用有:

Screenshot from 2017-08-03 09:52:54.png

Framework-res.apk 的 manifest 配置文件如下:

Screenshot from 2017-08-03 09:53:55.png

步驟4:通過密碼解密數(shù)據(jù)

這個(gè)過程不再闡述。

步驟5:停止框架

#重啟 main 類別服務(wù)
on property:vold.decrypt=trigger_reset_main
    class_reset main

這個(gè) setion 定義在文件 system/core/rootdir/init.rc 中。

步驟6:裝載 /data

static int do_mount_all(const std::vector<std::string>& args) {
    pid_t pid;
    .....
    //
    if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
        // 發(fā)送 encrypt 事件到 vdc
        ActionManager::GetInstance().QueueEventTrigger("encrypt");
    } else if
    .....
}

這個(gè)方法定義在文件 system/core/init/builtins.cpp 中波材。

on encrypt
    start surfaceflinger
    exec - root -- /system/bin/vdc --wait cryptfs enablecrypto inplace default noui
    # vold will set vold.decrypt to trigger_restart_framework (default
    # encryption)

這個(gè) setion 定義在文件 system/vold/vdc.rc 中股淡。

on encrypt
    start surfaceflinger
    #發(fā)送命令 enablecrypto 到 vold
    exec - root -- /system/bin/vdc --wait cryptfs enablecrypto inplace default noui
    # vold will set vold.decrypt to trigger_restart_framework (default
    # encryption)

這個(gè) setion 定義在文件 system/vold/vdc.rc 中。

int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
                                                 int argc, char **argv) {
    } else if (subcommand == "cryptocomplete") {
        if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
        dumpArgs(argc, argv, -1);
        rc = cryptfs_crypto_complete();
    // 命令是 enablecrypto
    } else if (subcommand == "enablecrypto") {
        .....

        // Spawn as thread so init can issue commands back to vold without
        // causing deadlock, usually as a result of prep_data_fs.
        char* arg2 = argc > 2 ? strdup(argv[2]) : NULL;
        char* arg4 = argc > 4 ? strdup(argv[4]) : NULL;
        // 執(zhí)行 do_enablecrypto 方法
        std::thread(&do_enablecrypto, arg2, arg4, type, no_ui).detach();
}

這個(gè)方法定義在文件 system/vold/CryptCommandListener.cpp 中廷区。

static int do_enablecrypto(char* arg2, char* arg4, int type, bool no_ui) {
    int rc;
    int tries;
    for (tries = 0; tries < 2; ++tries) {
        // 不是默認(rèn)加密唯灵,運(yùn)行方法 cryptfs_enable()
        if (type == CRYPT_TYPE_DEFAULT) {
            rc = cryptfs_enable_default(arg2, no_ui);
        } else {
            rc = cryptfs_enable(arg2, type, arg4, no_ui);
        }
        .....
    return -1;
}

這個(gè)方法定義在文件 system/vold/CryptCommandListener.cpp 中。

int cryptfs_enable(char *howarg, int type, char *passwd, int no_ui)
{
    return cryptfs_enable_internal(howarg, type, passwd, no_ui);
}

這個(gè)方法定義在文件 system/vold/cryptfs.c 中隙轻。

int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
                            int no_ui)
{
    /* restart the framework. */
    /* Create necessary paths on /data */
    if (prep_data_fs()) {
        goto error_shutting_down;
    }
}

這個(gè)方法定義在文件 system/vold/cryptfs.c 中幢踏。

static int prep_data_fs(void)
{
    property_set("vold.post_fs_data_done", "0");
    // 設(shè)置 vold.decrypt = trigger_post_fs_data,觸發(fā) init.rc
    property_set("vold.decrypt", "trigger_post_fs_data");
    SLOGD("Just triggered post_fs_data\n");
    
    /* Wait a max of 50 seconds, hopefully it takes much less */
    for (i=0; i<DATA_PREP_TIMEOUT; i++) {
        char p[PROPERTY_VALUE_MAX];
        // 等待 init 設(shè)置 vold.post_fs_data_done = 1
        property_get("vold.post_fs_data_done", p, "0");
        if (*p == '1') {
            break;
        } else {
            usleep(50000);
        }
    }
}

這個(gè)方法定義在文件 system/vold/cryptfs.c 中闹获。

on property:vold.decrypt=trigger_post_fs_data
    trigger post-fs-data

這個(gè) setion 定義在文件 system/core/rootdir/init.rc 中学赛。

#創(chuàng)建/data 子目錄和鏈接,啟動(dòng)服務(wù)
on post-fs-data
    # We chown/chmod /data again so because mount is run as root + defaults
    chown system system /data
    chmod 0771 /data
    # We restorecon /data in case the userdata partition has been reset.
    restorecon /data

    # start debuggerd to make debugging early-boot crashes easier.
    start debuggerd
    start debuggerd64

    #task4597305  added by xiwu.peng to output logcat to uart
    start logcat2uart

    # Make sure we have the device encryption key.
    start vold
    installkey /data

    # Start bootcharting as soon as possible after the data partition is
    # mounted to collect more data.
    mkdir /data/bootchart 0755 shell shell
    bootchart_init

    .....

    mkdir /data/system_de 0770 system system
    mkdir /data/system_ce 0770 system system

    mkdir /data/misc_de 01771 system misc
    mkdir /data/misc_ce 01771 system misc

    mkdir /data/user 0711 system system
    mkdir /data/user_de 0711 system system
    symlink /data/data /data/user/0

    mkdir /data/media 0770 media_rw media_rw
    mkdir /data/media/obb 0770 media_rw media_rw

    init_user0
    # If there is no fs-post-data action in the init.<device>.rc file, you
    # must uncomment this line, otherwise encrypted filesystems
    # won't work.
    # Set indication (checked by vold) that we have finished this action
    #setprop vold.post_fs_data_done 1

這個(gè) setion 定義在文件 system/core/rootdir/init.rc 中斑匪。

static int cryptfs_restart_internal(int restart_main)
{
    // init 做完 post-fs-data呐籽,繼續(xù)往下執(zhí)行代碼
    if (prep_data_fs()) {
            return -1;
    }
    //init 做完 post-fs-data, vold 將 vold.decrypt 設(shè)為 trigger_restart_framework蚀瘸, 觸發(fā)init
    /* startup service classes main and late_start */
    property_set("vold.decrypt", "trigger_restart_framework");
}

這個(gè)方法定義在文件 system/vold/cryptfs.c 中狡蝶。

#重啟所有服務(wù)
on property:vold.decrypt=trigger_restart_framework
    # A/B update verifier that marks a successful boot.
    exec - root -- /system/bin/update_verifier trigger_restart_framework
    class_start main
    class_start late_start

步驟7:?jiǎn)?dòng)整個(gè)框架

vold.decrypt = trigger_restart_framework, framework 就可以正常啟動(dòng)了贮勃。

加密屬性

vold 和 init 之間通過設(shè)置屬性進(jìn)行通信贪惹。下面列出了可用的加密屬性。

vold 屬性

屬性  說明
vold.decrypt trigger_encryption     以無密碼方式加密存儲(chǔ)卷寂嘉。
vold.decrypt trigger_default_encryption     檢查存儲(chǔ)卷是否采用了無密碼加密馍乙。如果是,則解密并裝載存儲(chǔ)卷垫释;如果不是丝格,則將 vold.decrypt 設(shè)為 trigger_restart_min_framework。
vold.decrypt trigger_reset_main     由 vold 設(shè)置棵譬,用于關(guān)閉提示輸入磁盤密碼的界面显蝌。
vold.decrypt trigger_post_fs_data   由 vold 設(shè)置,用于準(zhǔn)備具有必要目錄等內(nèi)容的 /data订咸。
vold.decrypt trigger_restart_framework  由 vold 設(shè)置曼尊,用于啟動(dòng)實(shí)際框架和所有服務(wù)。
vold.decrypt trigger_shutdown_framework     由 vold 設(shè)置脏嚷,用于關(guān)閉整個(gè)框架以開始加密骆撇。
vold.decrypt trigger_restart_min_framework  由 vold 設(shè)置,用于啟動(dòng)加密進(jìn)度條界面或提示輸入密碼父叙,具體取決于 ro.crypto.state 的值神郊。
vold.encrypt_progress   框架啟動(dòng)時(shí)肴裙,如果設(shè)置了此屬性,則會(huì)進(jìn)入進(jìn)度條界面模式涌乳。
vold.encrypt_progress 0 to 100  進(jìn)度條界面中應(yīng)按照設(shè)置顯示百分比值蜻懦。
vold.encrypt_progress error_partially_encrypted     進(jìn)度條界面中應(yīng)顯示一條消息,告訴用戶加密失敗夕晓,并且界面中應(yīng)為用戶提供一個(gè)用于將設(shè)備恢復(fù)出廠設(shè)置的按鈕宛乃。
vold.encrypt_progress error_reboot_failed   進(jìn)度條界面中應(yīng)顯示一條消息,告訴用戶加密已完成蒸辆,并且界面中應(yīng)為用戶提供一個(gè)用于重新啟動(dòng)設(shè)備的按鈕征炼。此錯(cuò)誤不應(yīng)發(fā)生。
vold.encrypt_progress error_not_encrypted   進(jìn)度條界面中應(yīng)顯示一條消息躬贡,告訴用戶發(fā)生錯(cuò)誤谆奥,沒有已加密的數(shù)據(jù)或數(shù)據(jù)已丟失,并且界面中應(yīng)為用戶提供一個(gè)用于重新啟動(dòng)系統(tǒng)的按鈕逗宜。
vold.encrypt_progress error_shutting_down   進(jìn)度條界面未運(yùn)行,因此不清楚誰將響應(yīng)此錯(cuò)誤空骚。在任何情況下纺讲,都不應(yīng)發(fā)生此錯(cuò)誤。
vold.post_fs_data_done 0    由 vold 在將 vold.decrypt 設(shè)為 trigger_post_fs_data 的前一刻設(shè)置囤屹。
vold.post_fs_data_done 1    由 init.rc 或 init.rc 在完成 post-fs-data 任務(wù)之后立即設(shè)置熬甚。

init 屬性

屬性  說明
ro.crypto.fs_crypto_blkdev  由 vold 命令 checkpw 設(shè)置,供 vold 命令 restart 以后使用肋坚。
ro.crypto.state unencrypted     由 init 設(shè)置乡括,用于說明相應(yīng)系統(tǒng)正在未加密的 /data ro.crypto.state encrypted 中運(yùn)行。由 init 設(shè)置智厌,用于說明相應(yīng)系統(tǒng)正在已加密的 /data 中運(yùn)行诲泌。

ro.crypto.fs_type
ro.crypto.fs_real_blkdev
ro.crypto.fs_mnt_point
ro.crypto.fs_options
ro.crypto.fs_flags
這 5 個(gè)屬性由 init 在嘗試裝載 /data(包含從 init.rc 傳入的參數(shù))時(shí)設(shè)置。vold 會(huì)使用這些屬性來設(shè)置加密映射铣鹏。
ro.crypto.tmpfs_options     由 init.rc 設(shè)置敷扫,包含 init 在裝載 tmpfs /data 文件系統(tǒng)時(shí)應(yīng)使用的選項(xiàng)。

init 操作

on post-fs-data
on nonencrypted
on property:vold.decrypt=trigger_reset_main
on property:vold.decrypt=trigger_post_fs_data
on property:vold.decrypt=trigger_restart_min_framework
on property:vold.decrypt=trigger_restart_framework
on property:vold.decrypt=trigger_shutdown_framework
on property:vold.decrypt=trigger_encryption
on property:vold.decrypt=trigger_default_encryption.

Android 全盤加密分析到此為止诚卸。

參考 https://source.android.com/security/encryption/full-disk

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末葵第,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子合溺,更是在濱河造成了極大的恐慌卒密,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棠赛,死亡現(xiàn)場(chǎng)離奇詭異哮奇,居然都是意外死亡膛腐,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門屏镊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來依疼,“玉大人,你說我怎么就攤上這事而芥÷砂眨” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵棍丐,是天一觀的道長(zhǎng)误辑。 經(jīng)常有香客問我,道長(zhǎng)歌逢,這世上最難降的妖魔是什么巾钉? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮秘案,結(jié)果婚禮上砰苍,老公的妹妹穿的比我還像新娘。我一直安慰自己阱高,他們只是感情好赚导,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著赤惊,像睡著了一般吼旧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上未舟,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天圈暗,我揣著相機(jī)與錄音,去河邊找鬼裕膀。 笑死员串,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的昼扛。 我是一名探鬼主播昵济,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼野揪!你這毒婦竟也來了访忿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤斯稳,失蹤者是張志新(化名)和其女友劉穎海铆,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挣惰,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡卧斟,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年殴边,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片珍语。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡锤岸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出板乙,到底是詐尸還是另有隱情是偷,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布募逞,位于F島的核電站蛋铆,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏放接。R本人自食惡果不足惜刺啦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望纠脾。 院中可真熱鬧玛瘸,春花似錦、人聲如沸苟蹈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽汉操。三九已至再来,卻和暖如春蒙兰,著一層夾襖步出監(jiān)牢的瞬間磷瘤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工搜变, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留采缚,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓挠他,卻偏偏與公主長(zhǎng)得像扳抽,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子殖侵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,524評(píng)論 25 707
  • Android加密之文件級(jí)加密 前置文章 《Android加密之全盤加密》 《Android系統(tǒng)之System S...
    FamilyYuan閱讀 18,109評(píng)論 3 18
  • 1:InputChannel提供函數(shù)創(chuàng)建底層的Pipe對(duì)象 2: 1)客戶端需要新建窗口 2)new ViewRo...
    自由人是工程師閱讀 5,253評(píng)論 0 18
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理贸呢,服務(wù)發(fā)現(xiàn),斷路器拢军,智...
    卡卡羅2017閱讀 134,601評(píng)論 18 139
  • 在很多年前茉唉,網(wǎng)上盛傳一種觀點(diǎn)固蛾,說男生喜歡誰就去表白吧结执,不要怕會(huì)挨一大耳刮子,你想想艾凯,一個(gè)女孩多一個(gè)追求者献幔,是自己魅...
    云思潮閱讀 431評(píng)論 0 1