机设备上安装终端模拟器

下载地址是:https://jackpal.github.io/Android-Terminal-Emulator/
 打开链接,有个term.apk
共同连接同一个局域网
将手机设备与本地要运行 adb 的电脑连接到同一个局域网,比如连到同一个 WiFi。
打开 Android 设备上的终端模拟器
在里面依次运行命令:

1
2
su
setprop service.adb.tcp.port 5555

看ip

1
2
之后ifconfig看下android 设备的ip
此时如果直接adb connect ip:5555 可能会出现Connection refused

你需要做的是:

1
2
stop adbd
start adbd

之后在重新连接即可

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

####1.准备一个前台BackGroundService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package com.guoshikeji.xiaoxiangDriver.services;

import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;

import com.guoshikeji.xiaoxiangDriver.MainActivity;
import com.guoshikeji.xiaoxiangDriver.R;

import static android.app.Notification.PRIORITY_MAX;
/**
* Created by tyl
* 2019/11/12/012
* Describe:
*/
public class BackGroundService extends Service {
Notification notification;
private Context mContext;
private static Thread uploadGpsThread;
private MediaPlayer bgmediaPlayer;
private boolean isrun = true;

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mContext = this;
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//1.通知栏占用,不清楚的看官网或者音乐类APP的效果
notification = new Notification.Builder(mContext)
.setSmallIcon(R.mipmap.icon_notifacation_log)
.setWhen(System.currentTimeMillis())
.setTicker(getResources().getString(R.string.app_name))
.setContentTitle(getResources().getString(R.string.app_name))
.setContentText("正在后台运行")
.setOngoing(true)
.setPriority(PRIORITY_MAX)
.setContentIntent(pendingIntent)
.setAutoCancel(false)
.build();
/*使用startForeground,如果id为0,那么notification将不会显示*/
startForeground(2479, buildNotification());
//2.最关键的神来之笔,也是最投机的动作,没办法要骗过CPU
//这就是播放音乐类APP不被杀的做法,自己找个无声MP3放进来循环播放
if(bgmediaPlayer == null){
bgmediaPlayer = MediaPlayer.create(this,R.raw.slient);
bgmediaPlayer.setLooping(true);
bgmediaPlayer.start();
}
return START_STICKY;
}

@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}

@Override
public void onDestroy() {
isrun = false;
stopForeground(true);
bgmediaPlayer.release();
stopSelf();
super.onDestroy();
}

private NotificationManager notificationManager;
private boolean isCreateChannel = false;
@SuppressLint("NewApi")
private Notification buildNotification() {
Notification.Builder builder = null;
Notification notification = null;

if (android.os.Build.VERSION.SDK_INT >= 26) {
if (null == notificationManager) {
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}
String channelId = getPackageName();
if (!isCreateChannel) {
NotificationChannel notificationChannel = new NotificationChannel(channelId,
"BackgroundLocation", NotificationManager.IMPORTANCE_DEFAULT);
notificationChannel.enableLights(false);//是否在桌面icon右上角展示小圆点
notificationChannel.setShowBadge(true); //是否在久按桌面图标时显示此渠道的通知
notificationManager.createNotificationChannel(notificationChannel);
isCreateChannel = true;
}
builder = new Notification.Builder(getApplicationContext(), channelId);
} else {
builder = new Notification.Builder(getApplicationContext());
}
builder.setSmallIcon(R.mipmap.icon_notifacation_log)
.setColor(getResources().getColor(R.color.main_color))
.setContentTitle(getResources().getString(R.string.app_name))
.setContentText("正在后台运行")
.setWhen(System.currentTimeMillis());
if (android.os.Build.VERSION.SDK_INT >= 16) {
notification = builder.build();
} else {
return builder.getNotification();
}
return notification;
}
}

###2. 在对应的activity里显示开启service

1
2
Intent forgroundService = new Intent(this,BackGroundService.class);
startService(forgroundService);

###3. 在AndroidManifest.xml文件里申明service

1
2
3
4
<service
android:name=".BackGroundService"
android:enabled="true"
android:exported="true" />
1
2
3
4
5
6
7
//itemName要处理的字符串,.split(分割的标识字符',')
String itemName="项目1,项目2,项目3";
List<String> nameList = new ArrayList<String>(Arrays.asList(itemName.split(",")));
for (int i = 0; i <nameList.size() ; i++) {
Log.e("tyl","name="+nameList.get(i));
}
//结果:项目1 项目2 项目3

0×01 五大在线检测平台

腾讯的金刚审计系统 http://service.security.tencent.com/kingkong

图片1.png

360的捉虫猎手 http://appscan.360.cn/

图片2.png

阿里巴巴的聚安全http://jaq.alibaba.com/gc/appsec/index.htm

图片3.png

百度的移动云测试中心http://mtc.baidu.com/

图片4.png

梆梆加固测试平台(http://dev.bangcle.com/apps/index

图片5.png

效果对比

这里选用墨迹天气app的测试结果

百度移动测试中心

| 漏洞名称 | 风险级别 | 说明 | 修复建议 | 详情 |
| 组件暴露——Activity | 中危 | 当应用程序的组件被导出后,导出的组件可以被第三方app任意调用,从而导致敏感信息泄露,而且恶意攻击者也可以通过精心构造数据来达到攻击目标应用的的目的。 | 如果组件不需要与其他应用共享数据或进行交互,则在AndroidManifest.xml文件中设置该组件为 exported = “false”,反之,则需要对导出的组件进行权限控制并且严格校验传入的参数。 | com.moji.mjweather.activity.main.MainActivity com.moji.mjweather.CSplashScreen com.moji.mjweather.activity.share.ManualShareActivity com.moji.mjweather.activity.skinshop.SkinSelectorActivity com.kepler.jd.login.AuthSuccessActivity com.moji.mjweather.activity.liveview.MessageDetailActivity com.moji.mjweather.activity.liveview.OwnerMessageCenterActivity com.moji.mjweather.activity.account.SnsLoginActivity com.moji.mjweather.activity.liveview.HomePageActivity com.moji.mjweather.activity.voiceclock.AlarmAlertActivity com.moji.mjweather.activity.voiceclock.AlarmAlertFullScreenActivity com.moji.mjweather.activity.share.SharePlatformDialog com.tencent.tauth.AuthActivity com.moji.mjweather.activity.liveview.LauncherCameraActivity com.moji.mjweather.activity.bindapp.InstallAppActivity com.moji.mjweather.activity.settings.WidgetConfigureActivity com.igexin.sdk.GActivity com.moji.mjweather.wxapi.WXPayEntryActivity com.moji.mjweather.wxapi.WXEntryActivity com.moji.mjweather.activity.forum.TopicActivity com.moji.mjweather.x5webview.BrowserActivity 共:21个。 |
| 组件暴露——Service | 中危 | 当应用程序的组件被导出后,导出的组件可以被第三方app任意调用,从而导致敏感信息泄露,而且恶意攻击者也可以通过精心构造数据来达到攻击目标应用的的目的。 | 如果组件不需要与其他应用共享数据或进行交互,则在AndroidManifest.xml文件中设置该组件为 exported = “false”,反之,则需要对导出的组件进行权限控制并且严格校验传入的参数。 | com.moji.mjweather.service.ScreenService com.igexin.sdk.PushService com.igexin.sdk.PushServiceUser com.moji.mjweather.authaccount.AuthenticationService com.moji.mjweather.authaccount.SyncService 共:5个。 |
| 组件暴露——BroadcastReceiver | 中危 | 当应用程序的组件被导出后,导出的组件可以被第三方app任意调用,从而导致敏感信息泄露,而且恶意攻击者也可以通过精心构造数据来达到攻击目标应用的的目的。 | 如果组件不需要与其他应用共享数据或进行交互,则在AndroidManifest.xml文件中设置该组件为 exported = “false”,反之,则需要对导出的组件进行权限控制并且严格校验传入的参数。 | com.moji.mjweather.receiver.PackageReceiver com.moji.mjweather.receiver.MojiReceiver com.moji.mjweather.CMojiWidget4x1 com.moji.mjweather.CMojiWidget4x2 com.moji.mjweather.CMojiWidget5x1 com.moji.mjweather.CMojiWidget5x2 com.igexin.sdk.PushReceiver com.igexin.download.DownloadReceiver com.baidu.bottom.service.BottomReceiver com.zk.drivermonitor.reciever.SystemStartReceiver 共:10个。 |
| 应用数据任意备份风险 | 中危 | 当AndroidManifest.xml配置文件中没有有设置allowBackup标志(默认为true)或将allowBackup标志设置为true时,应用程序的数据可以被任意备份和恢复,恶意攻击者可以通过adb工具备份复制应用程序的数据。 | 在AndroidManifest.xml文件中设置application的属性 android:allowBackup=”false” |   |
| 权限滥用风险 | 中危 | 自定义权限的保护级别过低,导致任意应用程序都可以使用此权限,无法起到保护作用。 | 如非必要,自定义权限的保护级别至少要设置为:signature。 |   |

| 漏洞名称 | 风险级别 | 说明 | 修复建议 | 详情 |
| WebView组件系统隐藏接口未移除漏洞 | 高危 | 使用Android WebView组件时,没有移除其中内置的searchBoxJavaBridge_,accessibility和accessibilityTraversal等导出接口, 可能导致远程代码任意执行 | 使用Android WebView组件时,通过调用removeJavascriptInterface方法移除searchBoxJavaBridge_, accessibility和accessibilityTraversal等导出接口,防止被恶意利用 | 源文件: 类:com.baidu.mobad.feeds.remote.BaiduActivity 方法:a 行数:-1 |
| WebView组件系统隐藏接口未移除漏洞 | 高危 | 使用Android WebView组件时,没有移除其中内置的searchBoxJavaBridge_,accessibility和accessibilityTraversal等导出接口, 可能导致远程代码任意执行 | 使用Android WebView组件时,通过调用removeJavascriptInterface方法移除searchBoxJavaBridge_, accessibility和accessibilityTraversal等导出接口,防止被恶意利用 | 源文件: 类:com.qq.e.comm.plugin.j.a 方法:onAfterCreate 行数:-1 |
| Dex文件动态加载风险 | 中危 | Android提供的DexClassLoader动态加载方法,并没有对DEX文件和路径进行安全校验,可能导致加载文件或者优化文件被恶意替换 | 使用DexClassLoader方法动态加载DEX文件时,对DEX文件进行安全校验,并保证加载路径和优化路径的安全 | 源文件: 类:com.baidu.mobad.feeds.remote.AdManager 方法:getPatchClassLoader 行数:-1 |
| SSL证书验证不当漏洞 | 中危 | 应用忽略证书校验错误或信任任意证书,会导致中间人攻击,造成隐私泄露 | 禁止使用ALLOW_ALL_HOSTNAME_VERIFIER;禁止使用X509TrustManager.checkServerTrusted方法来忽略证书验证错误;在使用HostnameVerifier时verify合理处理,禁止直接返回true | 源文件: 类:com.qq.e.comm.plugin.k.d$a$1 方法:verify 行数:-1 |
| WebView密码明文保存漏洞 | 低危 | 在默认情况下,如果用户选择保存在WebView中输入的用户名和密码,则会被明文保存到应用数据目录的databases/webview.db文件中,存在密码被泄露的风险 | 使用WebView.getSettings().setSavePassword(false)来禁止保存密码 | 源文件: 类:com.qq.e.comm.plugin.m.f 方法:a 行数:-1 |
| WebView密码明文保存漏洞 | 低危 | 在默认情况下,如果用户选择保存在WebView中输入的用户名和密码,则会被明文保存到应用数据目录的databases/webview.db文件中,存在密码被泄露的风险 | 使用WebView.getSettings().setSavePassword(false)来禁止保存密码 | 源文件: 类:com.qq.e.comm.plugin.m.f 方法:b 行数:-1 |
| WebView密码明文保存漏洞 | 低危 | 在默认情况下,如果用户选择保存在WebView中输入的用户名和密码,则会被明文保存到应用数据目录的databases/webview.db文件中,存在密码被泄露的风险 | 使用WebView.getSettings().setSavePassword(false)来禁止保存密码 | 源文件: 类:com.qq.e.comm.plugin.j.a 方法:onAfterCreate 行数:-1 |
| WebView密码明文保存漏洞 | 低危 | 在默认情况下,如果用户选择保存在WebView中输入的用户名和密码,则会被明文保存到应用数据目录的databases/webview.db文件中,存在密码被泄露的风险 | 使用WebView.getSettings().setSavePassword(false)来禁止保存密码 | 源文件: 类:com.baidu.mobad.feeds.remote.BaiduActivity 方法:a 行数:-1 |
| WebView密码明文保存漏洞 | 低危 | 在默认情况下,如果用户选择保存在WebView中输入的用户名和密码,则会被明文保存到应用数据目录的databases/webview.db文件中,存在密码被泄露的风险 | 使用WebView.getSettings().setSavePassword(false)来禁止保存密码 | 源文件: 类:com.baidu.mobad.feeds.remote.BaiduActivity$1 方法:shouldOverrideUrlLoading 行数:-1 |
| WebView密码明文保存漏洞 | 低危 | 在默认情况下,如果用户选择保存在WebView中输入的用户名和密码,则会被明文保存到应用数据目录的databases/webview.db文件中,存在密码被泄露的风险 | 使用WebView.getSettings().setSavePassword(false)来禁止保存密码 | 源文件: 类:com.baidu.mobad.feeds.remote.DownloaderTask 方法:a 行数:-1 |
| PendingIntent包含隐式Intent风险 | 低危 | PendingIntent以其发送方应用的权限使用该PendingIntent包含的Intent,如果该Intent为隐式的,可能造成隐私泄露和权限泄露 | 使用PendingIntent时,建议使用显示Intent | 源文件: 类:com.baidu.mobad.feeds.remote.download.e 方法:run 行数:-1 |
| PendingIntent包含隐式Intent风险 | 低危 | PendingIntent以其发送方应用的权限使用该PendingIntent包含的Intent,如果该Intent为隐式的,可能造成隐私泄露和权限泄露 | 使用PendingIntent时,建议使用显示Intent | 源文件: 类:com.qq.e.comm.plugin.a.b.c 方法:d 行数:-1 |
| PendingIntent包含隐式Intent风险 | 低危 | PendingIntent以其发送方应用的权限使用该PendingIntent包含的Intent,如果该Intent为隐式的,可能造成隐私泄露和权限泄露 | 使用PendingIntent时,建议使用显示Intent | 源文件: 类:com.qq.e.comm.plugin.a.i 方法:b 行数:-1 |
| WebView密码明文保存漏洞 | 低危 | 在默认情况下,如果用户选择保存在WebView中输入的用户名和密码,则会被明文保存到应用数据目录的databases/webview.db文件中,存在密码被泄露的风险 | 使用WebView.getSettings().setSavePassword(false)来禁止保存密码 | 源文件: 类:com.qq.e.comm.plugin.m.c 方法:d 行数:-1 |
| 日志泄露隐私风险 | 低危 | 调试输出接口未关闭可能导致敏感信息泄露 | 关闭调试接口,禁止输出敏感信息 | 源文件: 类:com.baidu.mobads.location.BDLocManager 方法:a 行数:-1 |
| 日志泄露隐私风险 | 低危 | 调试输出接口未关闭可能导致敏感信息泄露 | 关闭调试接口,禁止输出敏感信息 | 源文件: 类:com.qq.e.comm.plugin.g.a 方法:a 行数:-1 |
| 日志泄露隐私风险 | 低危 | 调试输出接口未关闭可能导致敏感信息泄露 | 关闭调试接口,禁止输出敏感信息 | 源文件:src/com/qihoo/util/StubApplication.java 类:com.qihoo.util.StubApplication 方法:initCrashReport 行数:93 |

梆梆加固测试结果:****

| 内网测试信息残留漏洞 |
| 评估项 | 内网测试信息残留漏洞 |
| 漏洞描述 | 检测程序代码内部是否包含残留测试信息,例如内网url地址等。 |
| 漏洞影响 | 低 |
| 评估方案 | 通过检测是否包含内网URl地址,判断是否发布包中是否包含测试数据。残留的测试数据,例如URL地址,测试账号,密码,可能会被盗取并恶意利用在正式服务器上进行攻击,例如账号重试,攻击安全薄弱的测试服务器以获取服务器安全漏洞或者逻辑漏洞。 |
| 评估结果 | 安全 |
| 漏洞分析 | 该App应用中未包含测试数据信息。 |
| 漏洞详情 | N/A |
| 解决方案 | N/A |

| 下载任意apk漏洞 |
| 评估项 | 下载任意apk漏洞 |
| 漏洞描述 | 检测应用中是否存在下载任意apk的漏洞。 |
| 漏洞影响 | 中 |
| 评估方案 | 具有下载apk功能的组件存在导出漏洞,并且未对组件调用者进行校验。攻击者可利用导出组件的手段下载攻击者指定的任意apk文件,并且在下载过程中伪装apk文件的下载信息,例如图标、描述等,导致用户被诱导下载安装恶意应用。 |
| 评估结果 | 安全 |
| 漏洞分析 | 该App应用中不存在可被导出的具有下载apk功能的组件。 |
| 漏洞详情 | N/A |
| 解决方案 | N/A |

| HTTPS未校验服务器证书漏洞 |
| 评估项 | HTTPS未校验服务器证书漏洞 |
| 漏洞描述 | 检测App程序在使用HTTPS协议传输数据时是否对服务器证书进行完整校验。 |
| 漏洞影响 | 中 |
| 评估方案 | 使用HTTPS协议时,客户端必须对服务器证书进行完整校验,以验证服务器是真实合法的目标服务器。如果没有校验,客户端可能与仿冒的服务 器建立通信链接,即“中间人攻击”。仿冒的中间人可以冒充服务器与银行客户端进行交互,同时冒充银行客户端与银行服务器进行交互,在充当中间人转发信息的 时候,窃取手机号,账号,密码等敏感信息。 |
| 评估结果 | 存在漏洞 |
| 漏洞分析 | 该App应用在使用HTTPS进行数据传输时未校验服务器证书或者未校验主机名。 |
| 漏洞详情 | [“com.moji.mjweather.util.log.InstalledAppTrackerSDK.a:(ILjava/lang/String;Lorg/apache/http/client/methods/HttpPost;)Ljava/lang/String;”] |
| 解决方案 | 在使用https时对服务器证书进行校验,并且使用STRICT_HOSTNAME_VERIFIER严格校验主机名。 |

| Webview远程代码执行漏洞 |
| 评估项 | Webview远程代码执行漏洞 |
| 漏洞描述 | 检测app应用的webview组件中是否存在远程代码执行漏洞。 |
| 漏洞影响 | 高 |
| 评估方案 | Webview是Android用于浏览网页的组件,其包含的接口函数addJavascriptInterface可以将Java类或方 法导出以供JavaScript调用,实现网页JS与本地JAVA的交互。由于系统没有限制已注册JAVA类的方法调用,因此未注册的其它任何JAVA类 也可以被反射机制调用,这样可能导致被篡改的URL中存在的恶意代码被执行,用户手机被安装木马程序,发送扣费短信,通信录或者短信被窃取,甚至手机被远 程控制。 |
| 评估结果 | 存在漏洞 |
| 漏洞分析 | 该App应用中可能存在被addJavascriptInterface接口导出的未注册Java类函数。 |
| 漏洞详情 | [“com.tencent.bugly.crashreport.CrashReport.setJavascriptMonitor:(Landroid/webkit/WebView;ZZ)Z”] |
| 解决方案 | 取消使用addJavascriptInterface接口,以其他Java与 JavaScript互通方案代替;若必须使用,则应对访问的url进行过滤限制或对html页面进行完整性校验,同时显示移除对指定的 javascript接口的调用: removeJavascriptInterface(searchBoxJavaBridge_) emoveJavascriptInterface(accessibility);removeJavascriptInterface(accessibilityTraversal);。 |

| Webview绕过证书校验漏洞 |
| 评估项 | Webview绕过证书校验漏洞 |
| 漏洞描述 | 检测App应用的webview组件是否在发现https网页证书错误后继续加载页面。 |
| 漏洞影响 | 低 |
| 评估方案 | 客户端的Webview组件访问使用HTTPS协议加密的url时,如果服务器证书校验错误,客户端应该拒绝继续加载页面。但如果重载 WebView的onReceivedSslError()函数并在其中执行handler.proceed(),客户端可以绕过证书校验错误继续访问此 非法URL。这样将会导致“中间人攻击”,攻击者冒充服务器与银行客户端进行交互,同时冒充银行客户端与银行服务器进行交互,在充当中间人转发信息的时 候,窃取手机号,账号,密码等敏感信息。 |
| 评估结果 | 存在漏洞 |
| 漏洞分析 | 该App应用的webview组件中存在忽略证书校验错误的漏洞。 |
| 漏洞详情 | [    “com.alipay.sdk.app.b.onReceivedSslError:(Landroid/webkit/WebView;Landroid/webkit/SslErrorHandler;Landroid/net/http/SslError;)V”,    “com.alipay.sdk.app.d.onClick:(Landroid/content/DialogInterface;I)V”,    “com.alipay.sdk.auth.AuthActivity.b.onReceivedSslError:(Landroid/webkit/WebView;Landroid/webkit/SslErrorHandler;Landroid/net/http/SslError;)V”,    “com.alipay.sdk.auth.f.onClick:(Landroid/content/DialogInterface;I)V”,    “com.tencent.smtt.sdk.aw.b.proceed:()V”] |
| 解决方案 | 取消在Webveiw组件中对onReceivedSslError()函数的重载。 |

360捉虫猎手检测结果:****

图片6.png

图片7.png

因结果扫了很久还没出来,就直接来张其他app的扫描结果吧

图片8.png

阿里聚对墨迹天气的安全检测结果:****

| 漏洞详情 | 风险等级 | 修复建议 |
| webview远程代码执行漏洞(2个)addJavascriptInterface存在高危远程代码执行漏洞,应尽量避免使用,API 17中用@JavascriptInterface 代替addjavascriptInterface;移除系统webkit内置的危险接口searchBoxJavaBridge_,accessibility,accessibilityTraversal [ 了解更多 ] [ 实际案例 ] | 高危触及安全红线 | 应尽量避免使用,API 17中用@JavascriptInterface 代替addjavascriptInterface;移除系统webkit内置的危险接口searchBoxJavaBridge_,accessibility,accessibilityTraversal |
| WebView不校验证书漏洞(1个)调用了android/webkit/SslErrorHandler类的proceed方法,可能导致WebView忽略校验证书的步骤 | 高危触及安全红线 | 不要调用android.webkit.SslErrorHandler的proceed方法 |
| 中间人攻击漏洞(1个)HTTPS禁止使用ALLOW_ALL_HOSTNAME_VERIFIER,因为这样会存在中间人攻击的风险 [ 了解更多 ] | 高危触及安全红线 | 必须使用STRIC_HOSTNAME_VERIFIER并校验证书 |
| 备份标识配置风险(1个)当这个标志被设置为true或不设置该标志时应用程序数据可以备份和恢复,adb调试备份允许恶意攻击者复制应用程序数据。 [ 了解更多 ] [ 实际案例 ] | 中危触及安全红线 | 在AndroidManifest.xml中设置android:allowBackup=”false” |
| 拒绝服务漏洞(22个)不校验导出组件(Activity,Service等)的传递参数,导致拒绝服务,需注意空值判定以及类型转换判断。 [ 了解更多 ] [ 实际案例 ] | 中危触及安全红线 | 请严格校验输入参数,注意空值判定和类型转换判断,防止由于异常输入导致的应用崩溃. |
| SharedPrefs任意读写漏洞(2个)存在内容被替换的风险,SharedPreference禁止使用MODE_WORLD_READABLE和MODE_WORLD_WRITABLE [ 了解更多 ] | 中危触及安全红线 | 不要使用MODE_WORLD_READABLE和MODE_WORLD_WRITABLE。 |
| 主机名弱效验(3个)在实现的HostnameVerifier子类中未对主机名做效验,这样会导致恶意程序利用中间人攻击绕过主机名效验。利用HostnameVerifier子类中的verify函数效验服务器主机名的合法性。 | 中危触及安全红线 | 在实现的HostnameVerifier子类verify函数中校验主机名的合法性。 |
| 证书弱校验(3个)在实现的HostnameVerifier子类中未对主机名做效验,这样会导致恶意程序利用中间人攻击绕过主机名效验。利用HostnameVerifier子类中的verify函数效验服务器主机名的合法性。 | 中危触及安全红线 | 在实现的X509TrustManager子类中checkServerTrusted函数效验服务器端证书的合法性。 |
| File任意读写漏洞(7个)存在内容被替换的风险,openFileOutput禁止使用MODE_WORLD_READABLE和MODE_WORLD_WRITABLE [ 了解更多 ] | 中危触及安全红线 | 不要使用MODE_WORLD_READABLE和MODE_WORLD_WRITABLE。 |
| 随机数生成函数使用错误(1个)使用SecureRandom时不要使用SecureRandom (byte[] seed)这个构造函数,会造成生成的随机数不随机。 [ 了解更多 ] | 高危 | 建议通过/dev/urandom或者/dev/random获取的熵值来初始化伪随机数生成器PRNG |
| AES/DES弱加密风险(19个)使用AES/DES加密算法时,应显式指定使用CBC或CFB模式.否则容易受到选择明文攻击(CPA)的风险,造成信息泄露。 | 高危 | 使用AES/DES加密算法时应使用CBC或CFB模式。或者使用安全组件的安全加密接口SecurityCipher进行加密。 |
| Native动态调试(1个)so文件存在被调试的风险,攻击者可以利用此风险对应用进行动态调试,造成核心逻辑和敏感数据等信息泄漏。 | 高危 | 聚安全安全组件通过监控进程的多种调试状态特征,给应用提供全方位的反调试保护。 |
| 密钥硬编码风险(9个)本地存储密钥存在被攻击者利用并通过密钥构造伪数据的风险。 [ 实际案例 ] | 高危 | 1、禁止把密钥写死在程序中,2、使用聚安全提供的安全加密组件。 |
| 初始化IvParameterSpec函数错误(7个)使用固定初始化向量,结果密码文本可预测性会高得多,容易受到字典式攻击。修复建议:1、禁止使用常量初始化矢量参数构建IvParameterSpec,2、推荐使用聚安全提供的安全组件。 [ 实际案例 ] | 中危 | 修复建议:1、禁止使用常量初始化矢量参数构建IvParameterSpec,2、推荐使用聚安全提供的安全组件。 |
| 未进行安全加固风险(1个)应用没有被安全加固,攻击者可以利用重打包等手段修改程序的原始逻辑和内容,并上传仿冒app到第三方应用市场,欺骗用户。 [ 实际案例 ] | 中危 | 建议使用聚安全的应用加固方案,聚安全应用加固提供对dex、so等文件的保护以及混淆。 |
| PendingIntent误用风险(5个)使用PendingIntent的时候,如果使用了一个空Intent,会导致恶意用户劫持修改Intent的内容。禁止使用一个空Intent去构造PendingIntent,构造PendingIntent的Intent一定要设置ComponentName或者action。 | 中危 | 禁止使用一个空Intent去构造PendingIntent,构造PendingIntent的Intent一定要设置ComponentName或者action。 |
| Webview明文存储密码漏洞(5个)使用Webview时需要关闭webview的自动保存密码功能,防止用户密码被webview明文存储。 | 中危 | 显示设置webView.getSetting().setSavePassword(false) |
| 未移除有风险的Webview系统隐藏接口(17个)android webview组件包含3个隐藏的系统接口:searchBoxJavaBridge_, accessibilityTraversal以及accessibility,恶意程序可以利用它们实现远程代码执行。请通过显示调用removeJavascriptInterface移除这三个系统隐藏接口。 [ 实际案例 ] | 中危 | 请通过显示调用removeJavascriptInterface移除这三个系统隐藏接口。 |
| 数据弱保护(1个)数据安全保护级别较低,攻击者可以通过逆向分析等手段,较容易得获取应用的关键数据,比如签名算法、加密密钥、加密数据等。 | 中危 | 推荐使用安全组件的数据加签和安全存储功能,提高应用的安全保护级别。 |
| 日志泄漏风险(20个)使用System.out.print等标准输出打印日志信息或转存日志信息,容易泄漏敏感信息。建议删除所有使用System.out.print等标准输出打印日志或转存日志信息的代码 [ 实际案例 ] | 低危 | 建议删除所有使用System.out.print等标准输出打印日志或转存日志信息的代码 |

关于阿里巴巴的聚安全,聚安全会给代码详情打码,如下图(4.1号以后的新规则,需要验证app的签名,会给你一个demo,你需要把keystore签到阿里官方给的demo中,验证应用开发者,然后才能看到详情)如下图:

图片9.png

有些朋友说,出现了这个 “为保护应用隐私,查看详情漏洞位置请先申请应用所有权认证”,

如何签名

如何签名:

`**jarsigner** **-verbose** **-keystore** [keystorePath] **-signedjar** [apkOut] [apkin] [alias]`

命令格式及参数意义:

-verbose -> 输出签名过程的详细信息

-keystore [keystorePath] -> 密钥的库的位置

-signedjar [apkOut] -> 签名后的输出文件名

[apkin] -> 待签名的文件名

[alias] -> 证书别名

示例:

`**D**:\>**jarsigner** **-verbose** **-keystore** **demo**.keystore **-signedjar** **jaq_demo_signed**.apk **jaq_demo**.apk **demo**.keystore`

下面讲下如何对app的应用开发者进行认证:

图片10.png

图片11.png

那么如何签名呢

图片12.png

创建一个keystore,用来存放签名app时要用的:

`**keytool** **-genkey** **-v** **-keystore** **relax**.keystore **-alias** **rela** **-keyalg** **RSA** 生成私钥`

用私钥对apk进行重新签名

`root@kali:~/Desktop# jarsigner -verbose -sigalg MD5withRSA --digestalg SHA1 -keystore /root/Desktop/relax.keystore jaq_demo_1460103308355.apk  rela`

图片13.png

就是说,使用开发者的keystore对聚安全的那个demo.apk进行签名,然后就完成了认证

聚安全的一些其他看点

聚安全结合乌云,里面有很多实例,有很多常用漏洞的集合,是新手快速解决问题的好去处。

图片14.png

聚安全还有一个仿冒监测:(这里说下为什么会出现仿冒软件,因为app没有加固,导致被反编译,被打包后,植入而已代码后又在其他地方上线,所以这里忠告一下,下app,一定要去官方网站上下载,能提供验证MD5,尽量要验证一下)

图片15.png

图片16.png

检测结果总结:

阿里聚安全问题汇总:

webview远程代码执行漏洞

WebView不校验证书漏洞

中间人攻击漏洞

备份标识配置风险

拒绝服务漏洞

SharedPrefs任意读写漏洞

主机名弱效验

证书弱校验

File任意读写漏洞

随机数生成函数使用错误

AES/DES弱加密风险

Native动态调试

密钥硬编码风险

初始化IvParameterSpec函数错误

未进行安全加固风险

PendingIntent误用风险

Webview明文存储密码漏洞

未移除有风险的Webview系统隐藏接口

数据弱保护

日志泄露隐私风险(logcat日志输出)

百度移动测试中心问题汇总:

组件暴露——Activity

组件暴露——Service

组件暴露——BroadcastReceiver

应用数据任意备份风险

权限滥用风险

WebView组件系统隐藏接口未移除漏洞

Dex文件动态加载风险

SSL证书验证不当漏洞

WebView密码明文保存漏洞

PendingIntent包含隐式Intent风险

日志泄露隐私风险

最后看下梆梆的检测结果:****

Java代码保护风险

组件导出风险

敏感函数调用风险

调试日志函数调用风险

应用数据任意备份风险

明文数字证书风险

未使用HTTPS协议的数据传输风险

Webview明文存储密码风险

HTTPS未校验服务器证书漏洞

Webview远程代码执行漏洞

Webview绕过证书校验漏洞

梆梆的新鲜的亮点: 到一处 直接打印出app里涉及到的url列表地址了,是不是涉及到很多新鲜的子域名和url。

图片17.png

图片18.png

总结和一些争议

评估一下APP的安全性可以综合参考以上的检测,然后综合性的评估,阿里的需要验证开发者权限,百度那个要花钱的,还不错,梆梆也可以(很方便渗透额),360怎么一直扫描不出报告。开发不一定能改第三方的包的安全问题,所以本包的问题能改的尽量改,咱们能做的就是给app进行加固。

图片19.png

图片20.png

加固前后效果对比

图片21.png

图片22.png

加固后,可以看到数据备份还是没有打钩,因为,我没对apk中的(在AndroidManifest.xml中设置android:allowBackup=”false”)这个设置项进行更改。

后记

本次在线检测实战旨在帮助开发者更快的评估自己的android问题,作为一个菜鸟app检测人员,希望带给大家的是让自己的app更加安全,当然安全从开发开始构思时,就该考虑是否使用第三方包,这样,对app的安全更加可控。

阅读拓展

http://www.droidsec.cn/ 安卓安全中文站

http://blog.nsfocus.net/mobile-app-security-security-test/ 移动app检测要点

http://zbc.baijia.baidu.com/article/365622 10大移动安全威胁

#####在大佬MarqueeViewLibrary框架的基础上实现控件的各项参数动态配置;

1
2
原文地址:
https://gitcode.net/mirrors/gongwen/MarqueeViewLibrary

实现后效果图:

####配置参数 动态替换下面的值即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Boolean isPlayAnim = true;//是否滚动
int width = 600;//控件宽度dp
int heigh = 80;//控件高度dp
int margin_left = 5;//左边距 dp
int margin_right = 0;//右边距 dp
int margin_top = 5;//上边距 dp
int margin_bottom = 0;//下边距 dp
String backgroundColor = "#cccccc";//控件背景色
int fontSize = 14;//字体大小dp
String textColor = "#000000";//字体颜色
int scrollSpeed = 4000;//翻页速度 毫秒
int scrollDirection = 4;//滚动方向:1上2下3左4右 其他默认左;
int animSpeed = 2000;//动画持续时间 毫秒
boolean isSingleLine = true;//是否单行
int textAlineType = 1;//文字居中方式:1居中,2横向居中3纵向居中 默认1
//设置数据源
final List<String> datas = Arrays.asList("《赋得古原草送别》", "离离原上草,一岁一枯荣。", "野火烧不尽,春风吹又生。", "远芳侵古道,晴翠接荒城。",
"又送王孙去,萋萋满别情。", "文字显示不下时,系统的处理方式(可选:none,start,middle,end)");

####配置控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//本地父布局
RelativeLayout rl_main_layout = findViewById(R.id.rl_main_layout);
int widthPx = MyUtils.px2dip(this, width);
int heighPx = MyUtils.px2dip(this, heigh);

SimpleMarqueeView marqueeView = new SimpleMarqueeView(this);
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) rl_main_layout.getLayoutParams();
layoutParams.width = widthPx;
layoutParams.height = heighPx;
layoutParams.setMargins(MyUtils.dip2px(this, margin_left), MyUtils.dip2px(this, margin_top),
MyUtils.dip2px(this, margin_right), MyUtils.dip2px(this, margin_bottom));
marqueeView.setLayoutParams(layoutParams);//设置宽高及margin
rl_main_layout.setBackgroundColor(Color.parseColor(backgroundColor));//设置背景色
rl_main_layout.addView(marqueeView);
/**
* 设置动画
* SimpleMarqueeView marqueeView SimpleMarqueeView控件
* int scrollDirection 滚动方向
* int animSpeed 动画持续时间
* int width控件宽度
* */
setAnimation(marqueeView, scrollDirection, animSpeed, widthPx, heighPx);
marqueeView.setFlipInterval(scrollSpeed);//翻页时间间隔
marqueeView.setTextColor(Color.parseColor(textColor));//字体颜色
marqueeView.setTextSize(MyUtils.dip2px(this, fontSize));
marqueeView.setTextEllipsize(TextUtils.TruncateAt.END);//文字显示不下时,系统的处理方式
switch (textAlineType) {//1居中,2横向居中3纵向居中 默认1
case 1:
marqueeView.setTextGravity(Gravity.CENTER);
break;
case 2:
marqueeView.setTextGravity(Gravity.CENTER_HORIZONTAL);
break;
case 3:
marqueeView.setTextGravity(Gravity.CENTER_VERTICAL);
break;
default:
marqueeView.setTextGravity(Gravity.CENTER);
break;
}
marqueeView.setTextSingleLine(isSingleLine);//是否单行
SimpleMF<String> marqueeFactory = new SimpleMF(this);
marqueeFactory.setData(datas);
marqueeView.setMarqueeFactory(marqueeFactory);
if (isPlayAnim && datas != null || datas.size() > 0) {
marqueeView.startFlipping();
} else {
marqueeView.stopFlipping();
}

####动画配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
private void setAnimation(SimpleMarqueeView marqueeView, int scrollDirection, int animSpeed, int width, int heigh) {
switch (scrollDirection) {
case 1://上
setBottomToTop(marqueeView, animSpeed, heigh);
break;
case 2://下
setTopToBottom(marqueeView, animSpeed, heigh);
break;
case 3://左
setRightToLeftAnim(marqueeView, animSpeed, width);
break;
case 4://右
setLeftToRoght(marqueeView, animSpeed, width);
break;
default://左
setRightToLeftAnim(marqueeView, animSpeed, width);
break;
}
}

//从下到上
private void setBottomToTop(SimpleMarqueeView marqueeView, int animSpeed, int heigth) {
AlphaAnimation alphaAnim = new AlphaAnimation(0f, 1f);
TranslateAnimation translateAnim = new TranslateAnimation(0, 0f, heigth, 0);
AnimationSet in_bottom = new AnimationSet(true);
in_bottom.addAnimation(alphaAnim);
in_bottom.addAnimation(translateAnim);
in_bottom.setDuration(animSpeed);

AlphaAnimation alphaAnim1 = new AlphaAnimation(1, 0f);
TranslateAnimation translateAnim1 = new TranslateAnimation(0f, 0, 0, -heigth);
AnimationSet out_top = new AnimationSet(true);
out_top.addAnimation(alphaAnim1);
out_top.addAnimation(translateAnim1);
out_top.setDuration(animSpeed);
marqueeView.setInAndOutAnim(in_bottom, out_top);
}
//从上到下
private void setTopToBottom(SimpleMarqueeView marqueeView, int animSpeed, int heigth) {
AlphaAnimation alphaAnim = new AlphaAnimation(0f, 1f);
TranslateAnimation translateAnim = new TranslateAnimation(0, 0f, -heigth, 0);
AnimationSet in_bottom = new AnimationSet(true);
in_bottom.addAnimation(alphaAnim);
in_bottom.addAnimation(translateAnim);
in_bottom.setDuration(animSpeed);

AlphaAnimation alphaAnim1 = new AlphaAnimation(1, 0f);
TranslateAnimation translateAnim1 = new TranslateAnimation(0f, 0, 0,heigth);
AnimationSet out_top = new AnimationSet(true);
out_top.addAnimation(alphaAnim1);
out_top.addAnimation(translateAnim1);
out_top.setDuration(animSpeed);
marqueeView.setInAndOutAnim(in_bottom, out_top);
}
//从右到左
private void setRightToLeftAnim(SimpleMarqueeView marqueeView, int animSpeed, int width) {
TranslateAnimation translateAnim = new TranslateAnimation(width, 0f, 0, 0);
AnimationSet in_right = new AnimationSet(true);
in_right.addAnimation(translateAnim);
in_right.setDuration(animSpeed);

TranslateAnimation translateAnim1 = new TranslateAnimation(0f, -width, 0, 0);
AnimationSet out_left = new AnimationSet(true);
out_left.setDuration(animSpeed);
out_left.addAnimation(translateAnim1);
marqueeView.setInAndOutAnim(in_right, out_left);
}
//从左到右
private void setLeftToRoght(SimpleMarqueeView marqueeView, int animSpeed, int width) {
TranslateAnimation translateAnim = new TranslateAnimation(-width, 0, 0, 0);
AnimationSet in_right = new AnimationSet(true);
in_right.addAnimation(translateAnim);
in_right.setDuration(animSpeed);

TranslateAnimation translateAnim1 = new TranslateAnimation(0, width, 0, 0);
AnimationSet out_left = new AnimationSet(true);
out_left.setDuration(animSpeed);
out_left.addAnimation(translateAnim1);
marqueeView.setInAndOutAnim(in_right, out_left);
}

#####常用事件消息传递

  • 一个实现了监听器接口的类,必须把它自身注册到它想要监听的类中去
  • 使用广播,内部的实现都需要IPC,从传递效率上来讲,可能并不太适合上层的组件间通信
  • Activity间的消息传递便是通过startActivityForResult和onActivityResult,会产生较多的状态和逻辑判断,而且intent或Bundle传值还得检测类型,容易发生错误

#####集成

1
compile 'org.greenrobot:eventbus:3.1.1'

EventBus是一款针对Anddoid优化的发布/订阅事件总线。主要是替代intent,Handler,
BroadCast在Fragment,activity,service线程之间传递消息。
优点:开销小,代码更优雅,以及将发送者和节后这解耦

#####基本用法

  • 注册
    1
    2
    EventBus.getDefault.register(this);
    EventBus.getDefault.register(this,methodName,Event.class);
  • 取消注册
    1
    EventBus.getDefault.unregister(this);
  • 订阅处理数据
    1
    2
    3
    onEventMainThread onEvent
    onEventPostThread onEventAsync
    onEventBackgroundThread
  • 发布数据
    1
    EventBus.getDefault().post(new Event(msg));

#####简单的demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class MainActivity extends AppCompatActivity {
//第一个界面
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EventBus.getDefault().register(this);//注册
findViewById(R.id.bn_main).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(new Intent(MainActivity.this,SecondActivity.class));
}
});
}
@Subscribe(threadMode = ThreadMode.MAIN)//需要加上注解
public void onEventMainThred(MyEvent event){
Log.e("tyl","masg="+event.getEvent());

}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);//注销
}
}
//第二个界面
public class SecondActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.bn_main).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
MyEvent myEvent = new MyEvent();
myEvent.setEvent("hello");
EventBus.getDefault().post(myEvent);
finish();
}
});
}
}
//消息源
public class MyEvent {
private String event;

public String getEvent() {
return event;
}

public void setEvent(String event) {
this.event = event;
}
}

#####ThreadMode分类
在EventBus中,您可以使用四个ThreadModes中的一个来定义将调用事件处理方法的线程。

  • ThreadMode.POSTING
    订阅者将在发布事件的同一线程中调用。这是默认值。事件传递是同步完成的,一旦发布完成,所有订阅者都将被调用。此ThreadMode意味着开销最小,因为它完全避免了线程切换。因此,这是已知完成的简单任务的推荐模式,是一个非常短的时间而不需要主线程。使用此模式的事件处理程序应该快速返回以避免阻止发布线程,这可能是主线程。
    1
    2
    3
    4
    5
    6
    //在同一个线程中调用(默认)
    // ThreadMode在这里是可选的
    @Subscribe(threadMode = ThreadMode.POSTING)
    public void onMessageEvent(MessageEvent event) {
    textView.setText(event.message);
    }
  • ThreadMode.MAIN
    订阅者将在Android的主线程(有时称为UI线程)中调用。如果发布线程是主线程,则将直接调用事件处理程序方法(与ThreadMode.POSTING所描述的同步)。使用此模式的事件处理程序必须快速返回以避免阻塞主线程。
    1
    2
    3
    4
    5
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(MessageEvent event) {
    Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
    textView.setText(event.message);
    }
  • ThreadMode.MAIN_ORDERED
    订阅者将在Android的主线程中调用。该事件总是排队等待以后交付给订阅者,因此对post的调用将立即返回。这为事件处理提供了更严格且更一致的顺序(因此名称为MAIN_ORDERED)。例如,如果您在具有MAIN线程模式的事件处理程序中发布另一个事件,则第二个事件处理程序将在第一个事件处理程序之前完成(因为它是同步调用的 - 将其与方法调用进行比较)。使用MAIN_ORDERED,第一个事件处理程序将完成,然后第二个事件处理程序将在稍后的时间点调用(一旦主线程具有容量),使用此模式的事件处理程序必须快速返回以避免阻塞主线程。
    1
    2
    3
    4
    5
    6
    //在Android UI主线程中调用
    @Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
    public void onMessageEvent(MessageEvent event) {
    Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
    textView.setText(event.message);
    }
  • ThreadMode.BACKGROUND
    订阅者将在后台线程中调用。如果发布线程不是主线程,则将在发布线程中直接调用事件处理程序方法。如果发布线程是主线程,则EventBus使用单个后台线程,该线程将按顺序传递其所有事件。使用此模式的事件处理程序应尝试快速返回以避免阻塞后台线程。
    1
    2
    3
    4
    5
    //在后台线程中调用
    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void onMessage(MessageEvent event){
    saveToDisk(event.message);
    }
  • ThreadMode.ASYNC
    事件处理程序方法在单独的线程中调用。这始终独立于发布线程和主线程。发布事件永远不会等待使用此模式的事件处理程序方法。如果事件处理程序的执行可能需要一些时间,例如用于网络访问,则应使用此模式。避免同时触发大量长时间运行的异步处理程序方法来限制并发线程数。EventBus使用线程池从已完成的异步事件处理程序通知中有效地重用线程。
    1
    2
    3
    4
    5
    //在一个单线程中调用
    @Subscribe(threadMode = ThreadMode.ASYNC)
    public void onMessage(MessageEvent event){
    backend.send(event.message);
    }

#####总结
我们根据EventBus中的不同ThreadMode的方式来进行对我们项目的使用,在什么情况中使用哪种线程将是我们所需要考虑的。
根据Android 中的线程不同,来使用不同的EventBus的不同线程。

#####android 原生提供的API带来的不方便的地方:

  • 要手动拼装sql
  • 要自己写操作数据库的常规代码
  • 不能自动把数据库中的数据映射成对象
  • 没有实现级联查询

#####什么是GreenDao:

GreenDao是一个高效的数据库访问ORM框架,节省了自己编写SQL的时间,快速的增删查改等操作。
#####什么是ORM:
对象关系映射,,是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换
#####GreenDao优点:

  • 让业务代码访问对象,而不是数据库源
  • 隐藏了面向对象的逻辑SQL查询详情
  • 无需处理数据库实现
  • 最高性能(可能是Android上最快的ORM); 我们的基准也是开源的
  • 易于使用的强大API,涵盖关系和连接
  • 最小的内存消耗
  • 小库大小(<100KB)以保持较低的构建时间并避免65k方法限制
  • 数据库加密:greenDAO支持SQLCipher,以确保用户的数据安全
  • 强大的社区:超过5000个GitHub Starts表明有一个强大而活跃的社区

#####集成
GreenDao 3.0采用注解的方式来定义实体类,通过gradle插件生成相应的代码。您可以使用greenDAO Gradle插件,无需任何其他配置,但至少要设置schema的版本等;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// In your root build.gradle file: 项目的build.gradle中
buildscript {
repositories {
jcenter()
mavenCentral() // add repository
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.1'
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' // add plugin 这里添加plugin
}
}

// In your app projects build.gradle file: 在app的build.gradle中
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao' // apply plugin 顶部添加

dependencies {
implementation 'org.greenrobot:greendao:3.2.2' // add library 引入包
}
//无需混淆

####为什么要使用RxJava和RxAndroid

  1. RxJava能提高动作效率
  2. RxJava能优雅的解决复杂业务场景
  3. RxJava使用越来越流行
    RxJava文档
    RxJava中文文档
    Rxjava经典资料

####响应式编程
是一种基于异步数据流概念的编程模式!
浅谈响应式编程
####RxJava概述

  1. 异步数据处理库
  2. 扩展的观察者模式

####RxJava特点

  1. <1MB jar
  2. 轻量级框架
  3. 支持Java8 lambda表达式
  4. 支持java6+ & Android 2.3+
  5. 支持异步和同步

####RxAndroid概述

  1. RxAndroid是RxJava针对Android平台的一个扩展,用于Android开发
  2. 提高响应式扩展组件快速、易于开发android应用程序

Sentry是一个实时事件日志记录和汇集的平台。其专注于错误监控以及提取一切事后处理所需信息而不依赖于麻烦的用户反馈。
####集成:

1
2
//app-build.gradle:
implementation 'io.sentry:sentry-android:1.7.27'

####初始化:

1
2
3
//MyApplication:
String sentryDsn = "http://6c89c12e4d4e4ee7a007ee5166637642@183.230.93.80:9100/8";
Sentry.init(sentryDsn, new AndroidSentryClientFactory(getApplicationContext()));

####获取Dsn:
//项目-设置
项目-设置
####混淆:

1
2
3
-keepattributes LineNumberTable,SourceFile
-dontwarn org.slf4j.**
-dontwarn javax.**

####官方文档:
https://docs.sentry.io/clients/java/integrations/#android
####官方demo: https://github.com/getsentry/examples/tree/master/android
####PS:
1 其实很简单的集成,建议不要去看官方的集成文档很坑,直接看demo都好!
2 另外就是异常信息如果是同类型的会归为1个异常里面 需要点异常信息进去查看 我之前就是一直以为没有集成成功,最好才发现是筛选后遭成的显示问题