【輸出文件】 Android 加密 模組原始碼分析
Android6.0 加密模組解析
前言:
加密流程就是用一個加密了的key對Android 系統中的使用者資料進行加密。一旦裝置被加密了,所有使用者建立的資料在提交到磁碟前都會被自動加密,並且在返回到呼叫的程序前,所有的使用者資料會被自動解密。
Android 的disk機密是基於dm-crypt的,dm-crypt是kernl上執行在block 裝置層的一個加密工具。因此,加密實際操作物件是對嵌入式多媒體儲存裝置(eMMC)和類似的快閃記憶體裝置中儲存的資料資訊。加密是不可能基於YAFFS工具的,因為YAFFS是直接與NAND快閃記憶體晶片進行通訊的。
- 加密型別
加密流程對於一個裝置來說,有可能出現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層後不會初始化輸入密碼的介面。
- 具體模組解析
流程圖:
- 機密流程
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{
//呼叫MountService的setField功能,設定系統語言
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");
... ...
//呼叫MountService的encrytStorage方法
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“的時候,mOnlyCore為true。mOnlyCore為true的時候,系統啟動後只會解析在AndroidManifest.xml中標記coreApp = true的activity。
Settings中CryptKepper.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_framework會main,mla和late_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