zoukankan      html  css  js  c++  java
  • 爬虫05 /js加密/js逆向、常用抓包工具、移动端数据爬取

    爬虫05 /js加密/js逆向、常用抓包工具、移动端数据爬取

    1. js加密、js逆向:案例1

    • 需求: 将这个网页中的代理ip和端口号进行爬取

    • 分析:

      1. 爬取的数据是动态加载

      2. 并且我们进行了抓包工具的全局搜索,没有查找到结果

        意味着:爬取的数据从服务端请求到的是加密的密文数据

      3. 页面每10s刷新一次,刷新后发现数据更新,但是浏览器地址栏的url没有变,说明加载出的数据是由ajax请求到的

        动态加载出来的数据是由ajax请求到的,并且请求到的数据为加密数据

      4. 定位到ajax数据包,从中可以看到url和动态变化的请求参数和加密的相应数据

      5. 将ajax请求到的密文数据捕获

        动态的获取动态变化的请求参数

        基于抓包工具进行了动态变化请求参数taken的全局搜索,定位到了taken产生的源头,就是如下js代码:

        var token = md5(String(page) + String(num) + String(timestamp));
        
      6. 对密文数据进行解密

        通过分析找到了解密的js函数:decode_str(encode_str),encode_str就是密文数据

      7. 查找encode_str的实现:

        js逆向:将js代码转换成python代码。开发环境只能执行python代码

    • js加密/js逆向源码相关代码:

      function get_proxy_ip(page, num, click_btn) {
          var timestamp = Date.parse(new Date());
          timestamp = timestamp / 1000;
          var token = md5(String(page) + String(num) + String(timestamp));
          $.get('../proxy?page=' + page + '&num=' + num + '&token=' + token + '&t=' + timestamp, function (result) {
              if (result.status === 'true') {
                  var setHtml = "";
                  $("#ip-list").html(setHtml);
                  var encode_str = result.list;
                  var items = str_to_json(decode_str(encode_str));
                  for (var index = 0; index < items.length; ++index) {
                      item = items[index];
                      setHtml += "<tr>
      <td>" + (index + 1) + "</td>
      ";
                      setHtml += "<td>" + item.ip.toString() + "</td>
      ";
                      setHtml += "<td>" + item.port.toString() + "</td>
      ";
                      setHtml += "<td>" + item.time.toString() + "</td>
      </tr>
      ";
                  }
                  $("#ip-list").html(setHtml);
                  if (click_btn === 'next') {
                      document.getElementById("last-page").disabled = false;
                      if (items.length < 15) {
                          document.getElementById("next-page").disabled = true;
                      }
                  } else {
                      document.getElementById("next-page").disabled = false;
                      if (page === 1) {
                          document.getElementById("last-page").disabled = true;
                      }
                  }
      
              }
          });
      }
      

      js解密代码:

      function decode_str(scHZjLUh1) {
          // Base64.decode(scHZjLUh1)
          scHZjLUh1 = Base64["x64x65x63x6fx64x65"](scHZjLUh1);
          key = 'x6ex79x6cx6fx6ex65x72';#key = 'b'nyloner'
          len = key["x6cx65x6ex67x74x68"];
          code = '';
          for (i = 0; i < scHZjLUh1["x6cx65x6ex67x74x68"]; i++) {
              var coeFYlqUm2 = i % len;
              code += window["x53x74x72x69x6ex67"]["x66x72x6fx6dx43x68x61x72x43x6fx64x65"](scHZjLUh1["x63x68x61x72x43x6fx64x65x41x74"](i) ^ key["x63x68x61x72x43x6fx64x65x41x74"](coeFYlqUm2))
          }
          return Base64["x64x65x63x6fx64x65"](code)
      }
      

      对以上代码分析结果如下:

      function decode_str(scHZjLUh1) {
          // Base64.decode(scHZjLUh1)
          scHZjLUh1 = Base64.decode(scHZjLUh1);
          key = 'nyloner';
          len = key.length;
          code = '';
          for (i = 0; i < scHZjLUh1.length; i++) {
              var coeFYlqUm2 = i % len;
              code += window.String.fromCharCode(scHZjLUh1.charCodeAt(i) ^ key.charCodeAt(coeFYlqUm2))
          }
          return Base64.decode(code)
      }
      
      // 'x64x65x63x6fx64x65'.encode('utf-8')
      // 注意:遇到类似字节的代码,一般不是加密数据,通过encode编码尝试是否可以得到有效信息
      
    • 代码实现:

      导入所需模块:

      import time
      import hashlib
      import requests
      import base64
      

      js逆向/将js代码转换成python代码:

      # js逆向之后的结果
      def decode_str(scHZjLUh1):
          # 解密成字符串
          scHZjLUh1 = base64.decodestring(scHZjLUh1.encode())
          key = 'nyloner'
          lenth = len(key)
          code = ''
          sch_lenth = len(scHZjLUh1)
          for i in range(sch_lenth):
              coeFYlqUm2 = i % lenth
              # chr(0-255)返回对应编码的字符
              # ord(a-z)返回编码数值
              code += chr(scHZjLUh1[i] ^ ord(key[coeFYlqUm2]))
          code = base64.decodestring(code.encode())
          code = code.decode('utf-8')
          return code
      

      生成请求所需的token值:

      def getToken():
          page = str(1)
          num = str(15)
          t = str(int(time.time()))
          md5 = hashlib.md5()
          md5.update((page+num+t).encode('utf-8'))
          token = md5.hexdigest()
          return token
      

      对响应数据解密:

      token = getToken()
      url = 'https://nyloner.cn/proxy'
      param = {
          'num':'15',
          'page':'1',
          't':str(int(time.time())),
          'token':token
          
      }
      headers = {
          'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36',
          'Cookie':'sessionid=20ryihg87smnkko2kx6634jbcf4umhfp'
      }
      code = requests.get(url,headers=headers,params=param).json().get('list')
      str_code = decode_str(code)
      str_code
      

    2. js加密、js逆向:案例2

    • 需求: url:https://www.aqistudy.cn/html/city_detail.html ,对其气象数据进行爬取

    • 分析:

      1. 点击不同气象指标的选项卡,发现没有相关的请求发送,说明当页面加载出来的时候,所有的气象数据已经加载完毕。

      2. 分析数据是否为动态加载 ,得出数据是动态加载出来的

      3. 修改查询的条件(城市的切换,时间的修改),点击搜索按钮就会加载出新数据。

      4. 在抓包工具的XHR中捕获到了两个数据包

        • url一样
        • 都有一个d这样的请求参数
        • 两个数据包的请求参数d的数据值不同
        • d这个请求参数是经过加密且动态变化
      5. 处理动态变化且加密的请求参数d

        对请求参数d进行全局搜索(不可行)

        点击页面中的搜索按钮后,在抓包工具中就捕获到了那两个ajax请求的数据包

        • 点击按钮发起ajax请求,找按钮的点击事件(click)
        • 借助火狐浏览器的开发者工具进行按钮点击事件的定位 ,根据点击事件定位到 getData()js函数
      6. 分析getData这个js函数的实现:目的就是为了找到ajax请求对应的js代码

        type=="HOUR":按照小时为时间单位进行查询

        并没有在该函数的实现中发现ajax请求对应的代码,但是发现了另外的两个函数调用getAQIData();getWeatherData();

      7. 分析getWeatherData();和getAQIData()这两个函数的定义,想要去找到ajax请求对应的代码:

        这两个函数实现的区别: method变量赋值的字符串不一样 GETDETAIL / GETCITYWEATHER

        相同: 都没有出现ajax请求对应的代码,但是发现了另一个函数的调用: getServerData(method,param,匿名函数,0.5)

      8. 对getServerData(method,param,匿名函数,0.5) 参数进行分析

        method:GETDETAIL或者GETCITYWEATHER

        param:字典,有四组键值对

        • city:查询城市的名称
        • type:HOUR
        • startTime:查询开始的时间
        • endTIme:查询结束的时间
      9. 对getServerData(method,param,匿名函数,0.5)函数进行分析

        getServerData(method,param,匿名函数,0.5)这个函数的实现,还是为了找到ajax请求对应的代码:

        基于抓包工具的全局搜索才可以找到

        发现这个函数的实现代码看不懂,函数的实现好像是一组密文 / 网站对js函数的实现加密

      10. 插入两个概念/上面密文用到了js混淆:

        JS混淆:对js函数的实现代码进行加密

        JS反混淆:将加密的js代码解密成原文

      11. 暴力破解: https://www.bm8.com.cn/jsConfusion/

      12. 分析getServerData函数的实现:

        终于找到了ajax请求对应的代码

        参数d是由getParam(method, object)函数返回的

        method:method

        object:param字典,四个键值分别是城市名称。type,起始时间,结束时间

      13. 请求到的密文数据的解密方式:

        decodeData(data):data参数就是响应的密文数据,返回值就是解密后的原文数据

      14. JS逆向:python可以使用 PyExecJS 库来实现模拟JavaScript代码执行。

      15. 环境的安装:

      ```python
      pip install PyExecJS
      ```
      
      1. 必须还要安装nodeJs的开发环境 /PyExecJS是依赖nodeJs环境运行的

      2. 请求到加密的响应数据

      3. 将加密的响应数据进行解密

    • 代码实现:

      import execjs
      import requests
      headers = {
          'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
      }
      node = execjs.get()
      file = 'test.js'
      ctx = node.compile(open(file,encoding='utf-8').read())
      
      # 如何五个变量会作为getPostParamCode的参数
      method = 'GETDETAIL'  # GETCITYWEATHER
      city = '北京'
      type = 'HOUR'
      start_time = '2018-01-25 00:00:00'
      end_time = '2018-01-25 23:00:00'
      # 模拟执行getPostParamCode函数
      js = 'getPostParamCode("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, type, start_time, end_time)
      params = ctx.eval(js)
      # print(params)   # 请求参数d
      url = 'https://www.aqistudy.cn/apinew/aqistudyapi.php'
      data = {
          'd':params
      }
      # 获取了加密的响应数据
      response_code = requests.post(url=url,headers=headers,data=data).text
      # response_code
      
      # 模拟执行decodeData函数对密文数据进行解密
      js = 'decodeData("{0}")'.format(response_code)
      page_text = ctx.eval(js)
      print(page_text)
      

    3. 常用的抓包工具

    • 抓包工具概述:

      本质就是一款代理服务器,适用于实现请求和相应的拦截

    • fiddler

      • 默认只可以抓起http协议的请求和相应
      • https:
        • tools-> options-> https-> detrypt HTTPS traffic
        • 含义:将fiddle的证书安装到本地
    • 青花瓷

    • miteproxy

    4. 移动端数据的爬取

    • 配置相关的环境

      • fiddler的配置:
        • tools-> options-> connections-> Allow remote computers to connect/选中
        • 查看fiddler的端口号,并且记住端口号
      • 测试配置是否生效:
        • 在电脑的浏览器访问:http://localhost:端口号/,如果可以访问成共则表示配置成功
    • 网络设置:

      • fiddler所在的电脑开启一个热点,手机连接热点
        • 保证手机和电脑同处于同一个网段
    • 在手机中安装fiddler的证书

      • 手机浏览器访问:http://电脑的ip:端口号/
        • 点击下载证书的链接,进行证书下载
      • 在手机中新任且安装证书
    • 开启手机网络代理

      • wifi-> 点击感叹号-> 开启代理-> 代理的ip就是电脑的ip,端口就是fiddler的端口
    • appnium是一个基于手机应用自动化的模块

    总结:

    1. js加密:为了防止爬虫,对js代码进行加密
    2. js逆向:对于python来说,就是将js代码转换成python代码
    3. js混淆:对js函数的实现代码进行加密
    4. JS反混淆:将加密的js代码解密成原文
    5. 看js函数是在定义函数还是在调用函数,查看后面是否跟着;号,有的话就是在调用/没有的话就是在定义
  • 相关阅读:
    IEnumerable<T>接口主要成员
    SQL LinqToSql Lambda QA
    css网页布局兼容性有哪些要点与诀窍
    【转】解决大量图片造成的页面延迟
    C# override,new 的区别+C# 接口的显示实现和隐示实现
    ap_invoice_distributions_all到xla_ae_lines
    Mysql从索引原理对SQL分析优化实战
    如何实现IM表情、图片、文件之间的通讯?
    五步教你如何使用k8s快速部署ES
    ClickHouse MergeTree引擎的简单介绍
  • 原文地址:https://www.cnblogs.com/liubing8/p/11998380.html
Copyright © 2011-2022 走看看