菜单

Android App 安全策略

2019年10月5日 - 编程技术
Android App 安全策略

前言对于一款成熟的App,在某个时间点一定会开始涉及或者说要开始考虑安全问题。本着一贯的风格,把自己最近一段时间以来了解和搜集的安全方面的知识整理归纳下,一是方便内部培训,再就是分享出来,希望对大家的实际开发工作能有所帮助。有兴趣的同学可以加入学习小组QQ群:
193765960
做进一步的讨论。

第九章:四大组件之Broadcast Receiver

 

一、广播的功能和特征

 

二、接收系统广播:

广播接收器可以自由地对自己感兴趣的广播进行注册,这样当有相应的广播发出时,广播接收器就能收到该广播,并在内部处理相应的逻辑。注册广播的方式有两种,在代码中注册和在清单文件中注册,前者称为动态注册,后者称为静态注册。

1、动态注册监听网络变化:

新建工程文件,首先在MainActivity中定义一个内部类netWorkChangeReceiver,并重写父类的onReceive()方法,这样每当网络状态发生变化时,onReceive()方法就会得到执行,这里使用Toast提示一段文本信息,代码如下:

class netWorkChangeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "network changes", Toast.LENGTH_SHORT).show();
        }        
    }

紧接着在onCreate方法中进行动态注册,然后在onDestroy方法中进行取消注册:

永利网站 1

永利网站 2

 1     private IntentFilter intentFilter;
 2     private netWorkChangeReceiver netWorkChangeReceiver;
 3     
 4     @Override
 5     protected void onCreate(Bundle savedInstanceState) {
 6         super.onCreate(savedInstanceState);
 7         setContentView(R.layout.activity_main);
 8         
 9         //动态注册:创建一个IntentFilter的实例,添加网络变化的广播(功能是对组件进行过滤,只获取需要的消息)
10         intentFilter = new IntentFilter();
11         intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
12         //创建NetWorkChangeReceiver的实例,并调用registerReceiver()方法进行注册
13         netWorkChangeReceiver = new netWorkChangeReceiver();
14         registerReceiver(netWorkChangeReceiver, intentFilter);
15         
16     }
17 
18     //取消注册,一定要记得,不然系统会报错
19     @Override
20     protected void onDestroy() {
21         super.onDestroy();
22         unregisterReceiver(netWorkChangeReceiver);
23     }

永利网站 3

永利网站 4

上方代码解释如下:

11行:给意图过滤器intentFilter添加一个值为android.net.conn.CONNECTIVITY_CHANGE的action。因为每当网络状态发生变化时,系统就会发出一条值为android.net.conn.CONNECTIVITY_CHANG的广播。

注:最后要记得,动态注册的广播接收器一定要取消注册才行。

运行程序,就可以了。

不过只是提醒网络发生变化还不够人性化,为了能够准确的告诉用户当前是有网络还是没有网络,我们还需要对上述代码进一步优化,修改netWorkChangeReceiver中的代码如下:

永利网站 5

永利网站 6

 1     class netWorkChangeReceiver extends BroadcastReceiver {
 2         
 3         @Override
 4         public void onReceive(Context context, Intent intent) {
 5             //通过getSystemService()方法得到connectionManager这个系统服务类,专门用于管理网络连接
 6             ConnectivityManager connectionManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
 7             NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();
 8             if(networkInfo != null && networkInfo.isAvailable()){
 9                 Toast.makeText(context, "network is available",Toast.LENGTH_SHORT).show();
10             }else{
11                 Toast.makeText(context, "network is unavailable",Toast.LENGTH_SHORT).show();
12             }
13                 
14         }
15     }

永利网站 7

永利网站 8

上方代码解释:

06行:在onReceive()方法中,首先通过通过getSystemService()方法得到connectionManager这个系统服务类,专门用于管理网络连接。

07行:然后调用它的getActiveNetworkInfo()方法可以得到NetworkInfo的实例,接着调用NetworkInfo的isAvailable()方法,就可以判断当前是否有网络了,最后通过Toast提示用户。

另外,查询系统的网络状态是需要申明权限的,打开清单文件,添加如下权限:

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

注:访问可以查看Android系统所有的可声明的权限。

现在运行程序,就可以了。

上方程序完整版代码如下:

永利网站 9

 package com.example.m05_broadcastreceiver01;
 2 import android.app.Activity;
 3 import android.content.BroadcastReceiver;
 4 import android.content.Context;
 5 import android.content.Intent;
 6 import android.content.IntentFilter;
 7 import android.net.ConnectivityManager;
 8 import android.net.NetworkInfo;
 9 import android.os.Bundle;
10 import android.widget.Toast;
11 public class MainActivity extends Activity {
12     private IntentFilter intentFilter;
13     private netWorkChangeReceiver netWorkChangeReceiver;
14     @Override
15     protected void onCreate(Bundle savedInstanceState) {
16         super.onCreate(savedInstanceState);
17         setContentView(R.layout.activity_main);
18         // 动态注册:创建一个IntentFilter的实例,添加网络变化的广播
19         intentFilter = new IntentFilter();
20         intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
21         // 创建NetWorkChangeReceiver的实例,并调用registerReceiver()方法进行注册
22         netWorkChangeReceiver = new netWorkChangeReceiver();
23         registerReceiver(netWorkChangeReceiver, intentFilter);
24     }
25     // 取消注册,一定要记得,不然系统会报错
26     @Override
27     protected void onDestroy() {
28         super.onDestroy();
29         unregisterReceiver(netWorkChangeReceiver);
30     }
31     class netWorkChangeReceiver extends BroadcastReceiver {
32         @Override
33         public void onReceive(Context context, Intent intent) {
34             //通过getSystemService()方法得到connectionManager这个系统服务类,专门用于管理网络连接
35             ConnectivityManager connectionManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
36             NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();
37             if(networkInfo != null && networkInfo.isAvailable()){
38                 Toast.makeText(context, "network is available",Toast.LENGTH_SHORT).show();
39             }else{
40                 Toast.makeText(context, "network is unavailable",Toast.LENGTH_SHORT).show();
41             }
42                 
43         }
44     }
45 }

永利网站 10

 1 package com.example.m05_broadcastreceiver01;
 2 import android.app.Activity;
 3 import android.content.BroadcastReceiver;
 4 import android.content.Context;
 5 import android.content.Intent;
 6 import android.content.IntentFilter;
 7 import android.net.ConnectivityManager;
 8 import android.net.NetworkInfo;
 9 import android.os.Bundle;
10 import android.widget.Toast;
11 public class MainActivity extends Activity {
12     private IntentFilter intentFilter;
13     private netWorkChangeReceiver netWorkChangeReceiver;
14     @Override
15     protected void onCreate(Bundle savedInstanceState) {
16         super.onCreate(savedInstanceState);
17         setContentView(R.layout.activity_main);
18         // 动态注册:创建一个IntentFilter的实例,添加网络变化的广播
19         intentFilter = new IntentFilter();
20         intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
21         // 创建NetWorkChangeReceiver的实例,并调用registerReceiver()方法进行注册
22         netWorkChangeReceiver = new netWorkChangeReceiver();
23         registerReceiver(netWorkChangeReceiver, intentFilter);
24     }
25     // 取消注册,一定要记得,不然系统会报错
26     @Override
27     protected void onDestroy() {
28         super.onDestroy();
29         unregisterReceiver(netWorkChangeReceiver);
30     }
31     class netWorkChangeReceiver extends BroadcastReceiver {
32         @Override
33         public void onReceive(Context context, Intent intent) {
34             //通过getSystemService()方法得到connectionManager这个系统服务类,专门用于管理网络连接
35             ConnectivityManager connectionManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
36             NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();
37             if(networkInfo != null && networkInfo.isAvailable()){
38                 Toast.makeText(context, "network is available",Toast.LENGTH_SHORT).show();
39             }else{
40                 Toast.makeText(context, "network is unavailable",Toast.LENGTH_SHORT).show();
41             }
42                 
43         }
44     }
45 }

永利网站 11

 

2、静态注册实现开机启动:

动态注册的方式比较灵活,但缺点是:必须在程序启动之后才能接收到广播,因为注册的逻辑是写在onCreate()方法中的。为了让程序在未启动的情况下就能接收到广播,这里就需要使用到静态注册。

这里我们准备让程序接收一条开机广播,当收到这条广播时,就可以在onReceive()方法中执行相应的逻辑,从而实现开机启动的功能。

新建一个类:BootCompleteReceiver,让他继承BroadcastReceiver,在onReceive()方法中简单地Toast一下,代码如下:

public class BootCompleteReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "Boot Complete", Toast.LENGTH_SHORT).show();
    }
}

可以看到,这里不再使用内部类的方式来定义广播接收器,因为稍后我们需要在清单文件AndroidManifest.xml中将这个广播接收器的类名注册进去。

然后修改清单文件AndroidManifest.xml,代码如下:

永利网站 12

永利网站 13

 1 <uses-sdk
 2         android:minSdkVersion="8"
 3         android:targetSdkVersion="16" />
 4     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
 5     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
 6 
 7     <application
 8         android:allowBackup="true"
 9         android:icon="@drawable/ic_launcher"
10         android:label="@string/app_name"
11         android:theme="@style/AppTheme" >
12         <activity
13             android:name="com.example.m05_broadcastreceiver01.MainActivity"
14             android:label="@string/app_name" >
15             <intent-filter>
16                 <action android:name="android.intent.action.MAIN" />
17 
18                 <category android:name="android.intent.category.LAUNCHER" />
19             </intent-filter>
20         </activity>
21         
22         <receiver android:name=".BootCompleteReceiver">
23             <intent-filter >
24                 <action android:name="android.intent.action.BOOT_COMPLETED"/>
25             </intent-filter>
26         </receiver>
27     </application>

永利网站 14

永利网站 15

代码解释如下:

终于,<application>标签内多了个子标签<receiver>,所有的静态注册的广播接收器都是在这里进行注册的。

22行:name中为广播接收器的名字

24行:想要接收的广播。Android系统启动完成后,会发出这条名为android.intent.action.BOOT_COMPLETED的广播。

05行:监听系统开机广播需要声明权限。

运行程序后,将手机关机重启,就能收到这条广播了。

 

三、发送自定义广播

1、发送标准广播  

新建工程文件。在发广播之前,我们先定义一个广播接收器来接收此广播才行。因此,新建一个类:MyBroadcastReceiver,让他继承BroadcastReceiver,代码如下:

public class MyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show();
    }
}

这里,当MyBroadcastReceiver
收到自定义的广播时,就会执行onReceive()方法中的逻辑,弹出一个Toast。

紧接着,要在清单文件AndroidManifest.xml中对这个广播接收器进行注册:

永利网站 16

永利网站 17

 1     <application
 2         android:allowBackup="true"
 3         android:icon="@drawable/ic_launcher"
 4         android:label="@string/app_name"
 5         android:theme="@style/AppTheme" >
 6         <activity
 7             android:name="com.example.m05_broadcastreceiver02.MainActivity"
 8             android:label="@string/app_name" >
 9             <intent-filter>
10                 <action android:name="android.intent.action.MAIN" />
11 
12                 <category android:name="android.intent.category.LAUNCHER" />
13             </intent-filter>
14         </activity>
15         
16         <receiver android:name=".MyBroadcastReceiver">
17             <intent-filter >
18                 <action android:name="com.example.m05_broadcastreceiver02.MY_BROADCAST"/>
19             </intent-filter>
20         </receiver>
21     </application>

永利网站 18

永利网站 19

代码解释:

18行:让MyBroadcastReceiver接收一条值为om.example.m05_broadcastreceiver02.MY_BROADCAST的广播,因此待会儿在发送广播的时候,我们就需要发出这样的一条广播。

紧接着,修改activity.xml中的代码,添加一个按钮Button。

然后,修改MainActivity.java中的代码,添加Button的监听事件:点击按钮时,发送广播

 

永利网站 20

永利网站 21

        Button button1=(Button)findViewById(R.id.button1);
        button1.setOnClickListener(new OnClickListener() {            
            @Override
            public void onClick(View v) {
                Intent intent =new Intent("com.example.m05_broadcastreceiver02.MY_BROADCAST");
                sendBroadcast(intent);
            }
        });

永利网站 22

永利网站 23

总结:可以看到,点击按钮时,发送com.example.m05_broadcastreceiver02.MY_BROADCAST这条广播,这样,所有能够监听com.example.m05_broadcastreceiver02.MY_BROADCAST这条广播的广播接收器就都会同时收到消息,此时发出去的就是一条标准广播,即无序广播。所以接下来就需要讲到有序广播。

 

2、发送有序广播:

广播是一种可以跨进程的通信方式,其他应用程序是可以收到的。现在我们来发一条有序广播。

有序广播不仅有先后顺序,而且前面的广播还可以将后面的广播截断。

在3.1的代码基础之上,将按钮的监听事件修改如下:

永利网站 24

永利网站 25

1         Button button1=(Button)findViewById(R.id.button1);
2         button1.setOnClickListener(new OnClickListener() {            
3             @Override
4             public void onClick(View v) {
5                 Intent intent =new Intent("com.example.m05_broadcastreceiver02.MY_BROADCAST");
6                 sendOrderedBroadcast(intent, null);
7             }
8         });

永利网站 26

永利网站 27

即将06行代码修改一下,将sendBroadcast()方法改为sendOrderedBroadcast()方法,sendOrderedBroadcast()方法接收两个参数,第二个参数是一个与权限相关的字符串,这里传入null即可。

紧接着,修改清单文件AndroidManifest.xml中对广播接收器的注册,设置优先级:

1         <receiver android:name=".MyBroadcastReceiver">
2             <intent-filter android:priority="100">
3                 <action android:name="com.example.m05_broadcastreceiver02.MY_BROADCAST"/>
4             </intent-filter>
5         </receiver>

即添加第02行代码。可以看到,通过android:priority属性给广播接收器设置了优先级。这个属性的范围在-1000到1000,数值越大,优先级越高。

接下来,如果想要拦截这个广播,防止让后面的广播接收器也接收到了这个广播。可以修改MyBroadcastReceiver中的代码:

永利网站 28

永利网站 29

1 public class MyBroadcastReceiver extends BroadcastReceiver {
2 
3     @Override
4     public void onReceive(Context context, Intent intent) {
5         Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show();
6         abortBroadcast();//拦截广播,防止后面的接收到
7     }
8 }

永利网站 30

永利网站 31

即添加第06行代码。如果在onReceive()方法中调用了abortBroadcast()方法,就表示是将这条广播拦截,后面的广播接收器将无法再接收到。

特别关注:

 

四、使用本地广播:

之前我们发送和接收的广播全部都是属于全局广播,即发出去的广播可以被其他任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播。这样一来,必然会造成安全问题。于是便有了本地广播:即只能在本应用程序中发送和接收广播。这就要使用到了LocalBroadcastManager这个类来对广播进行管理。

我们修改2.1中动态注册广播接收器的代码,即修改MainActivity.java中的代码如下:

永利网站 32

永利网站 33

package com.example.broadcasttest;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {
    private IntentFilter intentFilter;

    private LocalReceiver localReceiver;

    private LocalBroadcastManager localBroadcastManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //通过LocalBroadcastManager的getInstance()方法得到它的一个实例
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(
                        "com.example.broadcasttest.LOCAL_BROADCAST");
                localBroadcastManager.sendBroadcast(intent);//调用sendBroadcast()方法发送广播
            }
        });
        //动态注册本地的广播接收器
        intentFilter = new IntentFilter();
        intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");
        localReceiver = new LocalReceiver();
        localBroadcastManager.registerReceiver(localReceiver, intentFilter);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        localBroadcastManager.unregisterReceiver(localReceiver);
    }

    class LocalReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "received local broadcast",
                    Toast.LENGTH_SHORT).show();
        }
    }
}

永利网站 34

永利网站 35

注:本地广播是无法通过静态注册的方式来接收的。其实也完全可以理解,因为静态注册主要就是为了让程序在未启动的情况下也能收到广播。而发送本地广播时,我们的程序肯定是已经启动了,没有必要使用到静态注册的功能。

 

五、各种各样的广播: 

在android中有很多系统自带的intent.action,通过监听这些事件我们可以完成很多功能。

  1. 开机: String BOOT_COMPLETED_ACTION
    广播:在系统启动后。这个动作被广播一次(只有一次)。监听:
    “android.intent.action.BOOT_COMPLETED”
  2. 电话拨入: String ANSWER_ACTION 动作:处理拨入的电话。监听:
    “android.intent.action.ANSWER”
  3. 电量变化: String BATTERY_CHANGED_ACTION
    广播:充电状态,或者电池的电量发生变化。监听:
    “android.intent.action.BATTERY_CHANGED”
  4. 日期改变: String DATE_CHANGED_ACTION 广播:日期被改变。
    监听:“android.intent.action.DATE_CHANGED”
  5. 取消更新下载: String FOTA_CANCEL_ACTION 广播:取消所有被挂起的
    (pending) 更新下载。 监听:“android.server.checkin.FOTA_CANCEL”
  6. 更新开始安装: String FOTA_READY_ACTION
    广播:更新已经被下载 可以开始安装。监听
    “android.server.checkin.FOTA_READY”
  7. 主屏幕: String HOME_CATEGORY 类别:主屏幕
    (activity)。设备启动后显示的第一个 activity。
    监听:”android.intent.category.HOME”
  8. 新应用: String PACKAGE_ADDED_ACTION
    广播:设备上新安装了一个应用程序包。监听:
    “android.intent.action.PACKAGE_ADDED”
  9. 删除应用: String PACKAGE_REMOVED_ACTION
    广播:设备上删除了一个应用程序包。监听:
    “android.intent.action.PACKAGE_REMOVED”
  10. 屏幕关闭: String SCREEN_OFF_ACTION 广播:屏幕被关闭。监听:
    “android.intent.action.SCREEN_OFF”
  11. 屏幕开启: String SCREEN_ON_ACTION 广播:屏幕已经被打开。
    监听:“android.intent.action.SCREEN_ON”
  12. 时区改变: String TIMEZONE_CHANGED_ACTION
    广播:时区已经改变。监听: “android.intent.action.TIMEZONE_CHANGED”
  13. 时间改变: String TIME_CHANGED_ACTION
    广播:时间已经改变(重新设置)。 “android.intent.action.TIME_SET”
  14. 时间流逝: String TIME_TICK_ACTION
    广播:当前时间已经变化(正常的时间流逝)。
    “android.intent.action.TIME_TICK”
  15. 进入大容量存储模式: String UMS_CONNECTED_ACTION 广播:设备进入
    USB 大容量存储模式。 “android.intent.action.UMS_CONNECTED”
  16. 退出大容量存储模式: String UMS_DISCONNECTED_ACTION 广播:设备从
    USB 大容量存储模式退出。 “android.intent.action.UMS_DISCONNECTED”
  17. 壁纸改变: String WALLPAPER_CHANGED_ACTION
    广播:系统的墙纸已经改变。
    “android.intent.action.WALLPAPER_CHANGED”
  18. web搜索: String WEB_SEARCH_ACTION 动作:执行 web 搜索。
    “android.intent.action.WEB_SEARCH”
  19. 网络变化: String CONNECTIVITY_CHANGE_ACTION
    动作:网络变化。“android.intent.action.CONNECTIVITY_CHANGE_ACTION”

 

六、实例:使用动态注册,监听手机的电量变化。

完整版代码如下:

永利网站 36

永利网站 37

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:paddingBottom="@dimen/activity_vertical_margin"
 6     android:paddingLeft="@dimen/activity_horizontal_margin"
 7     android:paddingRight="@dimen/activity_horizontal_margin"
 8     android:paddingTop="@dimen/activity_vertical_margin"
 9     tools:context=".MainActivity" >
10 
11     <TextView
12         android:id="@+id/textView1"
13         android:layout_width="match_parent"
14         android:layout_height="wrap_content"
15         android:textSize="30dp"
16         android:gravity="center"/>
17 
18 </LinearLayout> 

永利网站 38

永利网站 39

activity_main.xml代码如下:

永利网站 40

永利网站 41

 1 package com.example.m05_broadcastreceiver02;
 2 
 3 import android.app.Activity;
 4 import android.content.BroadcastReceiver;
 5 import android.content.Context;
 6 import android.content.Intent;
 7 import android.content.IntentFilter;
 8 import android.os.Bundle;
 9 import android.widget.TextView;
10 
11 public class MainActivity extends Activity {
12 
13     
14     private BatteryBroadcastReceiver batteryBroadcastReceiver;
15     private TextView textView;
16     @Override
17     protected void onCreate(Bundle savedInstanceState) {
18         super.onCreate(savedInstanceState);
19         setContentView(R.layout.activity_main);
20         textView=(TextView)findViewById(R.id.textView1);
21         
22         //动态注册监听电量的广播接收器
23         IntentFilter intentFilter = new IntentFilter();
24         intentFilter.addAction("android.intent.action.BATTERY_CHANGED");
25         batteryBroadcastReceiver = new BatteryBroadcastReceiver();
26         registerReceiver(batteryBroadcastReceiver, intentFilter);       
27     }
28     
29     //取消注册监听电量的广播接收器
30     @Override
31     protected void onDestroy() {
32         super.onDestroy();
33         unregisterReceiver(batteryBroadcastReceiver);
34     }
35 
36     //新建一个广播接收器,监听电量的变化
37     public class BatteryBroadcastReceiver extends BroadcastReceiver {
38         @Override
39         public void onReceive(Context context, Intent intent) {
40             if(intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
41                 //获取当前电量
42                 int level = intent.getIntExtra("level", 0);
43                 //电量的总刻度
44                 int scale = intent.getIntExtra("scale", 100);
45                 textView.setText("电池电量为"+((level*100) / scale)+"%");
46                 
47                 //当电量低时,可以进行一些操作,例如弹出通知等
48 /*                if(level<15){
49                     do something
50                 }*/
51             }
52         }
53         
54     }
55     
56 }

永利网站 42

永利网站 43

紧接着,在清单文件中进行权限声明:

<uses-permission android:name="android.permission.BATTERY_STATS"/>

MainActivity.java的代码解释如下:

40至45行:固定代码,用于获取当前电量

48至50行:当电量低时,可以进行一些操作,例如弹出通知等

运行后,界面如下:

永利网站 44

版权归作者所有,转发请注明出处:

1.1.1 ProGuard

在Android
Studio当中混淆APK,借助SDK中自带的Proguard工具,只需要修改build.gradle中的一行配置即可。如下所示:

release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}

其中minifyEnabled用于设置是否启用混淆,proguardFiles用于选定混淆配置文件。build.gradle中minifyEnabled的值,这里我们只需要把值改成true,打出来的APK包就会是混淆过的了。

使用ProGuard混淆的应用,通过apktool还是可以看到Manifest和res资源,使用dex2jar也可以看到混淆后的源码,虽然大部分代码已经混淆了。还是可以看个大概,而且通过smail的修改,重新进行逆向apk。对于一般的应用足够,而对于应用中用了很多开源项目的,在业务层进行混淆也应该足够了。

参考文章:Android安全攻防战,反编译与混淆技术完全解析

1.1.2 DexGuard

DexGuard是收费的,是在Proguard基础上,加入了更多的保护措施。使用DexGuard混淆后,生成的apk文件,就无法正常使用apktool反编译了。DexGuard:

即使做了混淆处理。面对那群让人心生敬畏的黑客和逆向工作者,我们还是太脆弱了。那群鸟人,即使你的代码搞成.so他们也能给你反编译了,更别说他们还有大把的手段直接从内存下手。肿么办?凉拌。因为安全本来就是一个相对的概念,没有绝对的安全,就看谁的成本更大,手段更高超而已。

除了常规的apk混淆之外,我更推荐大家进一步使用一款专业的加固工具对APK进行加固,如果公司肯花点钱,搞个收费版的效果更佳。市场上加固工具也有很多,哪位同学比较熟悉各种工具之间效能对比的话,还希望能够给代价讲讲,我再这里不对说了。

黑客为了解析我们的代码逻辑,从日志下手对他们来说是非常常见而且有效的手段。日志中往往隐藏了对黑客来说非常重要的关键词和代码逻辑。所以为了减少信息的泄漏或者说增加代码被解析的难读,release版本的日志屏蔽是非常重要的。成熟的做法是APP构建自己的日志框架,在打release包时,能够方便的屏蔽掉所有的日志信息。关于日志框架(LogUtil类等等),网上一大堆,读者自行百度吧。

数据存储安全是APP安全策略的重中之重,敏感数据像用户名、密码、秘钥、令牌以及通讯录等等的保护都是需要重点考虑的。常见的问题:

下面介绍了将数据保存在设备上的三种基本方法。

默认情况下,您在内部存储空间中创建的文件仅供您的应用访问。这项保护措施由
Android 实现,而且这对于大多数应用来说足够了。

要为敏感数据提供额外的保护,您可以选择使用该应用无法直接访问的密钥来对本地文件进行加密。例如,您可以将密钥存储在
[KeyStore](https://developer.android.com/reference/java/security/KeyStore.html)
中,并使用未存储在相应设备上的用户密码加以保护。不过,如果攻击者获得超级用户权限,就可以在用户输入密码时进行监控,数据也就失去了这层保护屏障;但是,这种方式可以保护丢失设备上的数据,而无需进行文件系统加密。

在外部存储设备上创建的文件不受任何读取和写入权限的限制。对于外部存储设备中的内容,不仅用户可以将其移除,而且任何应用都可以对其进行修改,因此最好不要使用外部存储设备来存储敏感信息。

就像处理来源不受信任的数据一样,您应对外部存储设备中的数据执行输入验证。

如果您不打算向其他应用授予访问您的
ContentProvider的权限,请在应用清单中将其标记为
[android:exported=false];要允许其他应用访问存储的数据,请将
[android:exported]属性设置为 "true"

在创建要导出以供其他应用使用的
ContentProvider时,您可以在清单中指定允许读取和写入的单一权限,也可以针对读取和写入操作分别指定权限。

如果您要使用内容提供程序仅在自己的应用之间共享数据,最好将
[android:protectionLevel]属性设置为 "signature" 保护级别。

访问内容提供程序时,请使用参数化的查询方法(例如 query()update()

delete,以免产生来源不受信任的 SQL 注入风险。请注意,如果以组合用户数据的方式构建 selection
参数,然后再将其提交至参数化方法,则使用参数化方法可能不够安全

数字签名技术是Android
APP的安全基石之一,APP开发者使用私钥,以证书的形式对APP进行签名。

对关键的代码或者数据进行数字签名验证,可以有效的降低apk被篡改带来的风险。

将重要的数据比如秘钥等存储在.so中,以APP的数字签名进行提取之前的验证条件,可以较好地降低敏感数据暴露的风险。另外,如果需要使用动态的秘钥,则可以采用RSA非对称加密,从服务器获取动态秘钥;获取动态秘钥后,使用AES对数据进行秘钥加密。

保护APP组件的途径有两条,其一是正确地使用AndroidManifest.xml文件,其二是在代码级别上强制进行权限检查。

参考书籍:Android安全技术解密与规范 周圣涛著

3.1.1 最小化组件暴露

检查AndroidManifest.xml中对组件的导出设置,如果组件不允许被其他APP调用,则android:exported的属性值需要设置为false。需要注意的是,如果APP的minsdkVersion的值设置<=16或组件设置了intent-filter,则android:exported的属性值默认是为true的。

3.1.2 设置组件访问权限

如果你的组件导出了,为了降低被任意APP滥用或攻击,你需要考虑是否需要对调用方做过滤和筛选。如果需要过滤的话,比较好的做法是对组件声明android:permission;对permission可根据需要进一步设置android:protectedLevel,特别是当设置为signature级别表明只允许相同数字签名的APP访问组件。

3.1.3 组件传输数据验证

对组件之间,特别是跨应用的组件之间的数据传入和返回做数据验证,防止恶意数据传入,更要防止敏感数据的返回。对于导出的组件,最好做intent的校验:数据格式或数据结构校验;异常处理:防止crash攻击。

3.1.4 暴露组件的代码检查

Android SDK
提供了很多API,我们能够利用这些API在程序运行时检查、执行、授予和撤销权限。

使用activity的风险和选择取决于需求对activity的定义。这里我们基于activity的使用方式将其分为4中类型:

3.2.1 私有activity

私有activity不能由其他应用程序启动,因此它是最安全的。当一个activity只在本APP内部使用时,只要将activity声明为显式intent调用方式,那么就不需要担心其他应用程序调用开启。然而,有一个风险是第三方APP可以读取一个开启activity的intent。因此,为了避免第三方应用程序读取Intent来复制intent,我们可以在intent中放置一些extra做判断,避免第三方应用程序调用。

一个私有activity必须做到的关键几点如下:1)不声明taskAffinity2)不声明launchMode3)设置exported属性为false4)保证intent发送时的安全性,确定intent是来自本应用程序(签名验证和包名验证)5)在确保是本应用程序发送Intent的时候,可以防止一些敏感信息6)启动activity的时候不设置FLAG_ACTIVITY_NEW_TASK7)使用显示的intent和指定的类的方式来调用一个activity8)敏感信息放在extra中发送9)在onActivityResult的时候需要对发挥的data小心处理

在AndroidManifest.xml中的声明如下:

<activity android:name=".PrivateActivity" android:exported="false" android:label="@string/app_name" />

在java代码中的操作如下:

private final static int REQUEST_CODE = 1;@Overridepublic void onUseAactivityClick(View view){ //显示intent调用,直接写类名 Intent intent = new Intent(this, PrivateActivity.class); //设置包名 intent.setPackage(getPackageName; intent.putExtra("PARAM","敏感数据"); startActivityForResult(intent,REQUEST_CODE);}@Overridepublic void onActivityResult(int requestCode, int resultCode, intent data){ super.onActivityResult(requestCode,resultCode,data); //判断result的状态 if(resultCode != RESULT_OK){ return; } switch(requestCode){ case REQUEST_CODE: //注意返回数据的处理 String result = data.getStringExtra; ... break; }}

3.2.2 公共activity

我能要知道,一旦一个activity声明为公共activity,那么,任何一个APP都可以发送一个intent来启动它,他的安全性也必须要更加注意了。

一个公共activity必须做到的关键几点如下:1)设置exported属性为true2)接收到intent的时候要小心处理3)finish的时候,不要再返回intent中放置敏感信息

在AndroidManifest.xml中的声明如下:

<activity android:name=".PublicActivity" android:exported="true" android:label="@string/app_name" > <!--定义一个action--> <intent-filter> <action android:name="com.example.PUBLIC_ACTIVITY_ACTION"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter></activity>

在java代码中的操作如下:

public calss PublicActivity extends Activity{ @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); //判断确保param来源的正确性,发送之前可以适当做一些加密处理 String param = getIntent().getStringExtra; } public void onReturnResultClick(View view){ Intent intent = new Intent(); //finish时不要在intent中放置敏感信息,放置被截取 intent.putExtra("RESULT","非敏感信息"); setResult(RESULT_OK,intent); finish(); }}

3.2.3 伙伴activity

伙伴activity,顾名思义是只能由特定的应用程序使用的activity,即得到授权的APP,共享其信息和方法。这样的activity会存在一个风险,第三方应用程序可以读取启动这个activity的intent信息。因此,尽量不要在intent中放置一些敏感信息,也有必要做一些操作不让第三方应用读取到intent。

一个伙伴activity必须做到的关键几点如下:1)不声明taskAffinity2)不声明launchMode3)设置exported属性为true4)不添加intent-filter5)使用白名单机制验证签名6)处理partner
activity来的intent的时候要小心注意7)只返回给partner
activity一些公开信息

在AndroidManifest.xml中的声明如下:

<activity android:name=".PartnerActivity" android:exported="true" android:label="@string/app_name" />

在activity中做签名验证的操作如下:

public calss PartnerActivity extends Activity{ //伙伴应用的签名 private static final String PARTNER_SIGNATURE = "0de0f9c90dd0ed0e8d0edd..."; //伙伴应用的包名 private static final String PARTNER_SIGNATURE = "com.xxxx.yyyyyy"; //检查是否是伙伴应用 private static boolean checkPartner(Context context, String pkgname){ //包名不正确 if(!TextUtils.equals(pkgname,PARTNER_SIGNATURE )){ return false; } //签名不正确 if(!getSignature.eauals(PARTNER_SIGNATURE ))){ return false; } } @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); //判断确保param来源的正确性,发送之前可以适当做一些加密处理 String param = getIntent().getStringExtra; } public void onReturnResultClick(View view){ Intent intent = new Intent(); //finish时不要在intent中放置敏感信息,放置被截取 intent.putExtra("RESULT","非敏感信息"); setResult(RESULT_OK,intent); finish(); }}

3.2.4 内部activity

内部activity是禁止由内部APP之外的其他APP使用的activity。一般都是设置一个signature的权限,如系统内部使用的应用程序,都具有相同的签名。

这样的activity也会存在一个风险,第三方应用可以读取启动这个activity的intent信息。因此,尽量不要在intent中放置一些敏感的信息,也有必要做一些操作不让第三方应用读取到intent。

一个内部activity必须做到的关键几点如下:1)定义activity的权限为signature2)不声明taskAffinity3)不声明launchMode4)设置exported属性为true5)不添加intent-filter6)验证签名7)通过intent传输的数据要小心

在AndroidManifest.xml中的声明如下:

......<!--自定义一个signature的permission--><permission android:name="com.xxxx.activity.inhouseactivity.MY_PERMISSION" android:protectionLevel="signature"/>......<!--声明内部activity--><activity android:name=".InHouseActivity" android:exported="true" android:permission="com.xxxx.activity.inhouseactivity.MY_PERMISSION" />

在activity中的操作如下:

public class InHouseActivity extends Activity{ //启动权限 private static final String MY_PERMISSION = "com.xxxx.activity.inhouseactivity.MY_PERMISSION"; @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); //检查permission if(!TextUtils.equals(MY_PERMISSION,getPermission{ //权限不正确 finish(); return; } ...... } public void onReturnResultClick(View view){ Intent intent = new Intent(); intent.putExtra("RESULT","敏感信息"); setResult(RESULT_OK,intent); finish(); }}

永利网站,Broadcast
Receiver是专注于接收广播通知信息,并做出对应处理的组件。APP可以拥有任意数量的广播接收器以对所有它感兴趣的通知信息予以响应。

对于broadcast
receiver的创建和接收,需要根据不同的需求与风险做不同的选择决策。根据广播的发送接收流程和广播的使用方式,broadcast也可以分为三个不同的级别:

broadcast receiver根据注册声明方式可以分为两种:静态广播和动态广播。

Google官方也意识到了广播的安全性问题,在supportv4包中提供了一个类LocalBroadcastManager,主要负责程序内部广播的注册与发送。使用LocalBroadcastManager有如下好处:

但是,它只适用于在代码中注册发送广播,在Androidmanifest中注册的广播接收则不适用。

3.3.1 私有Broadcast Receiver

私有广播是最安全的广播,其发出来的广播只能有APP内部能够接收。动态广播无法注册为私有广播,所以私有广播只存在于静态广播中。

私有广播的几个关键点如下:1)不添加intent-filter2)exported属性为false3)广播处理完毕后要终止掉广播

私有广播的声明(Androidmanifest):

<!--私有广播--><receiver android:name=".PrivateReceiver" android:exported="false"/>

私有广播的定义:

public class PrivateReceiver extends BraodcastReceiver{ @Override public void onReceive(Context context,Intent intent){ String param = intent.getStringExtra; setResultCode(Activity.RESULT_OK); //处理一些重要敏感信息 setResultData; //终止掉广播,不需要再继续接收广播了 abortBroadcast(); }}

//普通广播public void onSendNormalClick(View view){ Intent intent = new Intent(this,PrivateReceiver.class); intent.putExtra("PARAM","敏感信息"); sendBroadcast;}//顺序广播public void onSendOrderedClick(View view){ Intent intent = new Intent(this,PrivateReceiver.class); intent.putExtra("PARAM","敏感信息"); sendOrderedBroadcast(intent,null,mResultReceiver,null,0,null,null);}

3.3.2 公共Broadcast Receiver

公共广播,没有确定的接收方,可以被一些未确定的APP接收。所以,使用公共广播的时候必须要注意此类广播被一些恶意应用接收造成数据泄露。

公共广播的关键点如下:1)exported属性为true。2)获取intent的时候小心处理。3)return
result的时候别放置敏感信息。

公共广播应该是通用的,所以能够被静态广播接收器和动态广播接收器所接收。

public class PublicReceiver extends BroadcastReceiver{ private static final String MY_BRROADCAST_PUBLIC="com.xxx.MY_BRROADCAST_PUBLIC"; @Override public void onReceive(Context context,Intent intent){ //判断action if(MY_BRROADCAST_PUBLIC.equals(intent.getAction{ String param = intent.getStringExtra; //数据小心处理 } setResultCode(Activity.RESULT_OK); //存放非敏感信息 setResultData; //终止掉广播 abortBroadcast(); }}

静态公共广播声明(Androidmanifest):

<!--静态公共广播接收器--><receiver android:name=".PublicReceiver" android:exported="true"> <intent-filter> <action android:name="com.xxx.MY_BRROADCAST_PUBLIC"/> </intent-filter></receiver>

动态公共广播声明:动态的公共广播我们在service中声明,代码如下:

public class DynamicReceiverService extends Service{ private static final String MY_BROADCAST_PUBLIC = "com.xxx.MY_BRROADCAST_PUBLIC"; private PublicReceiver mReceiver; @Override public IBinder onBind(Intent intent){ return null; } @Override public void onCreat(){ super.onCreate(); //动态注册公共广播接收器 mReceiver = new PublicReceiver(); InterFilter filter = new IntentFilter(); filter.addAction(MY_BROADCAST_PUBLIC); filter.setPriority;//优先考虑动态广播而不是静态广播 registerReceiver(mReceiver,filter); } @Override public void onDestroy(){ super.onDestroy(); //注销广播接收器 unregisterReceiver(mReceiver); mReceiver = null; }}

未完待续

使用Https和ssl证书校验。

需要特别注意的点:

removeJavascriptInterface(“accessibility”); removeJavascriptInterface(“accessibilityTraversal”); removeJavascriptInterface(“searchBoxJavaBridge_”);

b) 自动保存用户名密码检查: webview.getsettings().setsavePassword

以上资料描述上可能存在错误,也不够详细,在后期会逐渐完善。

参考资料:

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图