关于python:JS-逆向百例浏览器插件-Hook-实战亚航加密参数分析

60次阅读

共计 7598 个字符,预计需要花费 19 分钟才能阅读完成。

关注微信公众号:K 哥爬虫,QQ 交换群:808574309,继续分享爬虫进阶、JS/ 安卓逆向等技术干货!

申明

本文章中所有内容仅供学习交换,抓包内容、敏感网址、数据接口均已做脱敏解决,严禁用于商业用途和非法用处,否则由此产生的所有结果均与作者无关,若有侵权,请分割我立刻删除!

逆向指标

  • 指标:亚航 airasia 航班状态查问,申请头 Authorization 参数
  • 主页:aHR0cHM6Ly93d3cuYWlyYXNpYS5jb20vZmxpZ2h0c3RhdHVzLw==
  • 接口:aHR0cHM6Ly9rLmFwaWFpcmFzaWEuY29tL2ZsaWdodHN0YXR1cy9zdGF0dXMvb2QvdjMv
  • 逆向参数:

    • Request Headers:authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI......

逆向过程

抓包剖析

来到航班状态查问页面,轻易输出出发地和目的地,点击查找航班,例如查问澳门到吉隆坡的航班,MFM 和 KUL 别离是澳门和吉隆坡国内机场的代码,查问接口由最根本的 URL + 机场代码 + 日期组成,相似于:https://xxxxxxxxxx/MFM/KUL/28…,其中申请头 Request Headers 里有个 authorization 参数,通过观察发现,不论是革除 cookie 还是更换浏览器,此参数的值是始终不变的,通过测试,间接复制该参数到代码里也是可行的,但本次咱们的目标是通过编写浏览器插件来 Hook 这个参数,找到它生成的中央。

无关 Hook 的具体常识,在 K 哥后期的文章有具体介绍:JS 逆向之 Hook,吃着火锅唱着歌,忽然就被麻匪劫了!

浏览器插件 Hook

浏览器插件事实上叫做浏览器扩大(extensions),它可能加强浏览器性能,比方屏蔽广告、治理浏览器代理、更改浏览器外观等。

既然是通过编写浏览器插件的形式进行 Hook,那么首先咱们必定是要简略理解一下如何编写浏览器插件了,编写浏览器插件也有对应的标准,在以前,不同浏览器的插件编写形式都不太一样,到当初基本上都和 Google Chrome 插件的编写形式一样了,Google Chrome 的插件除了能运行在 Chrome 浏览器之外,还能够运行在所有 webkit 内核的国产浏览器,比方 360 极速浏览器、360 平安浏览器、搜狗浏览器、QQ 浏览器等等,另外,Firefox 火狐浏览器也有很多人应用,火狐浏览器插件的开发方式变动了很屡次,然而从 2017 年 11 月底开始,插件必须应用 WebExtensions APIs 进行构建,其目标也是为了和其余浏览器对立,个别的 Google Chrome 插件也能间接运行在火狐浏览器上,然而火狐浏览器插件须要要通过 Mozilla 签名后能力装置,否则只能长期调试,重启浏览器后插件就没有了,这一点较为不便。

一个浏览器插件的开发说简略也简略,说简单也简单,不过对于咱们做爬虫逆向的开发人员来说,咱们次要是利用插件对代码进行 Hook, 咱们只须要晓得一个插件是由一个 manifest.json 和一个 JavaScript 脚本文件组成的就够了 ,接下来 K 哥以本案例中申请头的 authorization 参数为例,率领大家开发一个 Hook 插件。当然,如果你想深入研究浏览器插件的开发,能够参考 Google Chrome 扩大文档和 Firefox Browser 扩大文档。

依照 Google Chrome 插件的开发标准,首先新建一个文件夹,该文件夹下蕴含一个 manifest.json 文件和一个 JS Hook 脚本,当然,如果你想为你的插件配置一个图标的话,也能够将图标放到该文件夹下,图标格局官网倡议 PNG,也能够是 WebKit 反对的任何格局,包含 BMP、GIF、ICO 和 JPEG 等,留神:manifest.json 文件名不可更改!失常的插件目录相似如下构造:

JavaScript Hook
    ├─ manifest.json        // 配置文件,文件名不可更改
    ├─ icons.png            // 图标
    └─ javascript_hook.js   // Hook 脚本,文件名顺便取 

manifest.json

manifest.json 是一个 Chrome 插件中最重要也是必不可少的文件,它用来配置所有和插件相干的配置,必须放在根目录。其中,manifest_version、name、version 这 3 个参数是必不可少的,本案例中,manifest.json 文件配置如下:(残缺配置参考 Chrome manifest file format)

{
    "name": "JavaScript Hook",          // 插件名称
    "version": "1.0",                   // 插件版本
    "description": "JavaScript Hook",   // 插件形容
    "manifest_version": 2,              // 清单版本,必须是 2 或者 3
    "icons": {                          // 插件图标
        "16": "/icons.png",             // 图标门路,插件图标不同尺寸也能够是同一张图
        "48": "/icons.png",
        "128": "/icons.png"
    },
    "content_scripts": [{"matches": ["<all_urls>"],      // 匹配所有地址
        "js": ["javascript_hook.js"],   // 注入的代码文件名和门路,如果有多个,则顺次注入
        "all_frames": true,             // 容许将内容脚本嵌入页面的所有框架中
        "permissions": ["tabs"],        // 权限申请,tabs 示意标签
        "run_at": "document_start"      // 代码注入的工夫
    }]
}

这里须要留神以下几点:

  • manifest_version:配置清单版本,目前反对 2 和 3,2 将会在未来被逐渐淘汰,未来也可能推出 4 或者更高版本。能够在官网查看 Manifest V2 和 Manifest V3 的区别,3 有更高的隐衷平安要求,这里举荐应用 2。
  • content_scripts:Chrome 插件中向页面注入脚本的一种模式,包含地址匹配(反对正则表达式),要注入的 JS、CSS 脚本,代码注入的工夫(倡议 document_start,网页开始加载时就注入)等。

javascript_hook.js

javascript_hook.js 文件里就是 Hook 代码了:

var hook = function () {
    var org = window.XMLHttpRequest.prototype.setRequestHeader;
    window.XMLHttpRequest.prototype.setRequestHeader = function (key, value) {if (key == 'Authorization') {debugger;}
        return org.apply(this, arguments);
    }
}
var script = document.createElement('script');
script.textContent = '(' + hook + ')()';
(document.head || document.documentElement).appendChild(script);
script.parentNode.removeChild(script);

XMLHttpRequest.setRequestHeader() 是设置 HTTP 申请头部的办法,定义了一个变量 org 来保留原始办法,window.XMLHttpRequest.prototype.setRequestHeader 这里有个原型对象 prototype,所有的 JavaScript 对象都会从一个 prototype 原型对象中继承属性和办法,具体能够参考菜鸟教程 JavaScript prototype 的介绍。一旦程序在设置申请头中的 Authorization 时,就会进入咱们的 Hook 代码,通过 debugger 断下,最初仍然将所有参数返回给 org,也就是 XMLHttpRequest.setRequestHeader() 这个原始办法,保证数据失常传输。而后创立 script 标签,script 标签内容是将 Hook 函数变成 IIFE 自执行函数,而后将其插入到网页中。

到此咱们浏览器插件就编写实现了,接下来介绍如何在 Google Chrome 和 Firefox Browser 中应用。

Google Chrome

在浏览器地址栏输出 chrome://extensions 或者顺次点击右上角【自定义及管制 Google Chrome】—>【更多工具】—>【扩大程序】,进入扩大程序页面,再顺次抉择开启【开发者模式】—>【加载已解压的扩大程序】,抉择整个 Hook 插件文件夹(文件夹里应蕴含 manifest.json、javascript_hook.js 和图标文件),如下图所示:

Firefox Browser

火狐浏览器不能间接装置未通过 Mozilla 签名认证的插件,只能通过调试附加组件的形式进行装置。插件的格局必须是 .xpi、.jar、.zip 的,所以须要咱们将 manifest.json、javascript_hook.js 和图标文件一起打包,打包须要留神不要蕴含顶层目录,间接全选右键压缩即可,否则在装置时会提醒 does not contain a valid manifest。

在浏览器地址栏输出 about:addons 或者顺次点击右上角【关上应用程序菜单】—>【扩大和主题】,也能够间接应用快捷键 Ctrl + Shift + A 来到扩大页面,在治理您的扩大目录旁有个设置按钮,点击抉择【调试附加组件】,在长期扩大我的项目下,抉择【长期载入附加组件】,抉择 Hook 插件的压缩包即可。

也能够间接在浏览器地址栏输出 about:debugging#/runtime/this-firefox,间接进入到长期扩大页面,如下图所示:

自此,浏览器 Hook 插件咱们就开发装置结束了,从新来到航班查问页面,轻易输出出发地和目的地,点击查找航班,就能够看到此时曾经胜利断下:

TamperMonkey 插件 Hook

后面咱们曾经介绍了如何本人编写一个浏览器插件,然而不同浏览器插件的编写始终是大同小异的,有可能你编写的某个插件在其余浏览器上运行不了,而 TamperMonkey 就能够帮忙咱们解决这个问题,TamperMonkey 俗称油猴插件,它自身就是一个浏览器扩大,是最为风行的用户脚本管理器,基本上反对所有带有扩大性能的浏览器,实现了脚本的一次编写,所有平台都能运行,用户能够在 GreasyFork、OpenUserJS 等平台间接获取他人公布的脚本,性能泛滥且弱小,同样的,咱们也能够利用 TamperMonkey 来实现 Hook。

TamperMonkey 能够间接在各大浏览器扩大商店外面装置,也能够去 TamperMonkey 官网进行装置,装置过程这里不再赘述。

装置实现后点击图标,增加新脚本,或者点击治理面板,再点击加号新建脚本,写入以下 Hook 代码:

// ==UserScript==
// @name         JavaScript Hook
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  JavaScript Hook 脚本
// @author       K 哥爬虫
// @include      *://*airasia.com/*
// @icon         https://profile.csdnimg.cn/1/B/8/3_kdl_csdn
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function () {
    'use strict';
    var org = window.XMLHttpRequest.prototype.setRequestHeader;
    window.XMLHttpRequest.prototype.setRequestHeader = function (key, value) {if (key == 'Authorization') {debugger;}
        return org.apply(this, arguments);
    };
})();

整个代码 JavaScript 局部是个 IIFE 立刻执行函数,具体含意就不解释了,后面浏览器插件开发时曾经讲过,重要的是下面几行正文,千万不要认为这只是简略的正文,可有可无,在 TamperMonkey 中,能够将这部分视为根本的配置选项,各项都有其具体含意,残缺的配置选项参考 TamperMonkey 官网文档,常见配置项如下表所示(其中须要特地留神 @match@include@run-at 选项):

选项 含意
@name 脚本的名称
@namespace 命名空间,用来辨别雷同名称的脚本,个别写作者名字或者网址就能够
@version 脚本版本,油猴脚本的更新会读取这个版本号
@description 形容这个脚本是干什么用的
@author 编写这个脚本的作者的名字
@match 从字符串的起始地位匹配正则表达式,只有匹配的网址才会执行对应的脚本,例如 * 匹配所有,https://www.baidu.com/* 匹配百度等,能够参考 Python re 模块外面的 re.match() 办法,容许多个实例
@include 和 @match 相似,只有匹配的网址才会执行对应的脚本,然而 @include 不会从字符串起始地位匹配,例如 *://*baidu.com/* 匹配百度,具体区别能够参考 TamperMonkey 官网文档
@icon 脚本的 icon 图标
@grant 指定脚本运行所需权限,如果脚本领有相应的权限,就能够调用油猴扩大提供的 API 与浏览器进行交互。如果设置为 none 的话,则不应用沙箱环境,脚本会间接运行在网页的环境中,这时候无奈应用大部分油猴扩大的 API。如果不指定的话,油猴会默认增加几个最罕用的 API
@require 如果脚本依赖其余 JS 库的话,能够应用 require 指令导入,在运行脚本之前先加载其它库
@run-at 脚本注入机会,该选项是能不能 hook 到的要害,有五个值可选:document-start:网页开始时;document-body:body 呈现时;document-end:载入时或者之后执行;document-idle:载入实现后执行,默认选项;context-menu:在浏览器上下文菜单中单击该脚本时,个别将其设置为 document-start

从新来到航班查问页面,启用 TamperMonkey 脚本,如果配置正确的话,就能够看到咱们编写的 Hook 脚本已开启,轻易输出出发地和目的地,点击查找航班,就能够看到此时曾经胜利断下:

参数逆向

不论你是应用浏览器插件还是 TamperMonkey 进行 Hook,此时 Hook 到的是设置申请头 Authorization 的中央,也就是说 Authorization 的值是产生必定通过了之前的某个函数或者办法,那么咱们跟进开发者工具的 Call Stack 调用栈,就肯定可能找到这个办法,跟调用栈是一个考验急躁的过程,破费工夫也比拟多。

通常状况下,咱们是挨个函数查看其传递的参数有没有蕴含咱们指标参数,如果上一个函数里没有而下一个函数里呈现了,那么大概率加密过程就在这两个函数之间,进入上一个函数再进行单步调试,个别就能找到加密代码,在本案例中,咱们跟到 t.getData 函数埋下断点进行单步调试,能够看到其实前面在重复调用 t.subscribet.call,之所以不在这两个函数处埋下断点,是因为循环过多不好调试,而且 t.getData 通过名称判断也比拟可疑。

从新点击登陆,来到咱们刚刚埋下断点的中央,F11 或者点击向下箭头,进入函数外部进行单步调试,调试大概 7 步后,来到一个 t.getHttpHeader 函数,能够看到 Authorization 的值就是 "Bearer" + r.accessToken,咱们在控制台打印 r.accessToken 能够看到就是咱们想要的值,如下图所示:

那么重点是这个 r.accessToken,如果你尝试间接往上找,你会发现找了很多行也没有找到,间接搜寻关键字 accessToken,能够发现在 zUnb 对象外面是间接定义死了的,间接拿来用即可,如下图所示:

对于出发地、目的地的各个中央的代码,是通过 JSON 传递过去的,很容易找到,可依据理论需要灵活处理,如下图所示:

这个案例自身不难,间接搜寻还能更快定位参数地位,然而本案例重点在于如何应用浏览器插件进行 Hook 操作,这对于某些无奈通过搜寻失去的参数,或者搜寻后果太多难以定位的状况来说,是一个很好的解决办法。

残缺代码

GitHub 关注 K 哥爬虫,继续分享爬虫相干代码!欢送 star!https://github.com/kgepachong/

以下只演示局部要害代码,不能间接运行! 残缺代码仓库地址:https://github.com/kgepachong…

Python 示例代码

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import requests


status_url = '脱敏解决,残缺代码关注 GitHub:https://github.com/kgepachong/crawler'


def get_flight_status(departure, destination, date):
    headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36',
        'authorization': '脱敏解决,残缺代码关注 GitHub:https://github.com/kgepachong/crawler'
    }
    complete_url = status_url + departure + '/' + destination + '/' + date
    response = requests.get(url=complete_url, headers=headers)
    print(response.text)


if __name__ == '__main__':
    departure = input('请输出出发地代码:')
    destination = input('请输出目的地代码:')
    date = input('请输出日期(例如:29/09/2021):')

    # departure = 'MFM'
    # destination = 'KUL'
    # date = '29/09/2021'
    get_flight_status(departure, destination, date)

正文完
 0