05--JS05--逆向01:抓包工具、PyExecjs模块、扣JS代码

JS逆向01:浏览器工具、抓包工具、PyExeJs模块、扣JS代码

# 逆向的关键点:
加密 或解密 的入口

一、浏览器工具

1.1 显示请求协议

# protocal   n.协议


# http协议的版本:
http/1.1  、h2    # 大多数网站是两者协议兼容,极少数的网站 会检测http协议的版本,则请求需要一致


# request模块 使用的是 http/1.1


# 发送h2协议的 则需要安装模块 httpx
pip install httpx



# httpx简单使用(与 aiohttp模块使用相似)  不得万不得已的情况,不做这个h2的尝试

client = httpx.Client(http2=True,)

client.get(参数与request一致)
client.post(参数与request一致)

1.2 事件启动器

# 查找加密的入口


### 方案一:顺序查找
1.在发送请求的 Initiator(启动器)中,点击第一个JS方法     # 这是发送请求的最后一步,参数一定是加密形式
  会自动跳转到 第一个JS中的 具体发送请求方法的行
    
2.依次向下 点击右侧 的Call Stack(调用栈),观察参数的变化  # 这是最后 具体发送请求方法的调用栈

3.参数什么时候,由加密形式 变化为成正常状态
  说明这一层的函数调用之前,一定进行过参数加密的处理        # 加密的入口,就找到了
    
4.剩下就是 分析、扣加密的代码

1.3 搜索窗口

# 右上角 三点 --->  Search (Ctrl + Shift + F 是全站的资源搜索)


# 搜索的小技巧:搜索url时,别搜索整个url,只搜索url中的 关键词
eg: https://fanyi.baidu.com/v2transapi?from=en&to=zh  ---->  搜索 v2transapi 即可



# 查找加密的入口


### 方案二:搜索查找     按照经验来搜索某个关键词,有一定的随机性
根据 请求参数 "encSecKey",一看就是跟加密有关的

1.在 全局的 Search中,搜索,会出现 各个JS文件与关键词有关 方法

2.依次点击每个JS文件, 观看,第一个只是 一个函数定义  不是
  第二个发现 有赋值的操作,打上 断点,调试一下   # 发现加密入口

3.在每个JS文件中,再Ctrl+F 搜索一下关键词  # 一个JS文件,可能出现多个位置的关键词
  不确定是否是入口,就都打上断点,调试 看看值变化

1.4 XHR/fetch 断点

# 查找加密的入口


### 方案三:自动打断点     在发送每个Ajax请求  或 请求url包含某个关键词
1.在 Sources中,右侧 XHR/fetch Breakpoints,
  勾选 Any XHR or fetch(所有ajax请求,打断点)  或者 点击加号 Break when URL contains:(url包含什么时,打断点)
    
2.再依次向下 点击右侧 的Call Stack(调用栈),观察参数的变化

3.参数什么时候,由加密形式 变化为成正常状态
  说明这一层的函数调用之前,一定进行过参数加密的处理        # 加密的入口,就找到了
    
4.剩下就是 分析、扣加密的代码

二、Fiddler和Charles

在处理一些网站的时候,会遇到一些屏蔽F12,以及只要按出浏览器的开发者工具,就会关闭甚至死机的现象

在遇到这类网站的时候,可以使用抓包工具,把页面上屏蔽开发者工具的代码给干掉

这两款工具是非常优秀的抓包工具。它们可以监听到计算机上所有的http网络请求。利用这种特性,可以获取到页面加载过程中的所有内容

2.1 fiddler简单配置(HTTPS)

接下来,打开www.vmgirls.com,尝试抓包看看,发现https的请求默认是不可用的,需要配置一下证书。

2.2 fiddler简单使用

清理一下信息,然后重新刷新浏览器, 就看到被响应的html内容了

vmgirl之所以不可以打开开发者工具,是因为在其html中,导入了一个disable-devtools.js。

需要想办法把这个东西给干掉,只要干掉了它,就又可以打开F12的开发者工具了

那么如何干掉它呢? 这就需要了解一下fiddler和charles的工作原理了.

fiddler和charles本质,其实就是一个安装在这台计算机上的一个代理,就像这样:

本质上和代理没啥区别,但是, 由于fiddler是自己本地的软件,那么可以在fiddler里对请求过来的内容,进行截取和替换

做这样一个事情,把vmgirls的页面源代码捕获,单独写入一个html文件,然后把其中的disable-devtools.js部分注释掉.

现在这个页面源代码在本地了,就可以进行替换了.... 或者 替换 disable-devtool.min.js文件,为本地的一个空文件 也可以!

接下来. 重新刷新页面(ctrl+shift+r) , 发现右键有效果了.

2.3 charles安装使用

charles官网: https://www.charlesproxy.com/download/

charles激活: https://www.zzzmode.com/mytools/charles/

  • 1.安装证书

注意:

在windows环境下,如果出现证书不可用的情况,需要手工去windows的证书管理器中,

把Charles的证书拖拽到可信任证书那一栏,然后重启浏览器和charles就可以用了

如果证书默认就可用. 就不要胡乱拖拽

window证书管理器: win+r: 输入certlm.msc

  • 2.配置443端口

Mac

  • 3.替换为本地文件

如果想要实现请求拦截,怎么办?

三、PyExecJS模块

pyexecjs是一个可以运行js代码的一个第三方模块,其使用是非常容易上手的

但是它的运行是要依赖能运行js的第三方环境的,这里选择用node作为运行js的位置.

3.1 安装Nodejs

一路下一步,修改安转位置,最后控制台输入:node -v npm -v 即可

在pycharm中,也安装 nodejs 插件,方便调试JS文件

3.2 安装pyexecjs

pip install pyexecjs

测试一下:

import execjs

print(execjs.get().name)  # Node.js (V8)  是V8引擎就正确

3.3 简单使用

import execjs

print(execjs.get().name)

# execjs.eval 可以直接运行js代码并得到结果
js = """
    "鲁班_王昭君_猴子_亚瑟_蔡文姬".split("_")
"""
res = execjs.eval(js)
print(res)


# execjs.compile() 与 call()
# execjs.compile():事先加载好一段js代码     先编译
jj = execjs.compile("""
    function an(a, b){
        return a + b    
    }
""")


# call():运行代码中的xxx函数. 后续的参数是xxx的参数  再执行js字符串编译后 js代码
ret = jj.call("an", 10, 20)
print(ret)

windows中如果出现编码错误. 在引入execjs之前. 插入以下代码即可.

import subprocess
from functools import partial     # functools 函数库 提供一堆为高阶函数服务的偏函数 (工具函数、辅助函数的意思)

# execjs原理是用 window 终端 执行 node xxx.js   
# 而window终端 默认字符编码是"GBK",故修改为 utf-8
subprocess.Popen = partial(subprocess.Popen, encoding='utf-8')

# partial(fn函数名,参数=值)      partial n.部分
作用:为fn函数,固定 某个参数值


import execjs

完事儿,execjs就这几个功能就够咱使用

3.4 实战案例(调试工具)

接下来,来破解真正的百度翻译

通过抓包可以发现:之前抓取的百度翻译,其实是个伪翻译

真正的一句话的翻译,其实它的url应该是:https://fanyi.baidu.com/v2transapi?from=en&to=zh

接下来就是分析这一坨

y = {
    from: d.fromLang,  // 从xxxx
    to: d.toLang,      // 翻译成xxxx
    query: e,          // 被翻译的内容
    transtype: r,      // 固定的翻译类型
    simple_means_flag: 3,  // 常量
    sign: L(e),       // ????
    token: window.common.token,  // token. 这个在页面源代码里能找到
    domain: R.getCurDomain()    // 固定值. 
};

现在有3个值,无法直接确定。一个是L(e)。 一个是token。另一个是R.getCurDomain()

先看 token

由于在进行翻译的时候,页面是没有刷新的,所以这个token一般在进行翻译的时候,是不会改变的

再看 domain

在页面中设置一个断点,并重新发送请求

点进去返回的是个M. 找M

OK, M是常量,"common"

最后看 L(e)

首先e是被翻译的内容,所以这里是把"要翻译的内容"传递给了L.。但是这个L在哪里,不好找。很简单,设置一个断点,就可以

程序停在这里了,接下来就是这三个按钮,从左到右:

第一个: 释放掉当前debug,程序继续向后运行,直到结束或下一个断点

第二个: 运行下一步,相当于一行代码。

​ 咱们这里,是在一个{}中间进行的断点。而整个{}; 被认为是一行代码,所以下面的 token等会被过掉

第三个: 运行到当前行内部。

​ 咱们这里,就是运行到L(e)里面,看看L(e)里面是干什么的

这里就是那个L(e)了,但这东西属实看不明白,也不容易看明白。里面是一个签名算法,十分难搞

仔细观察下,发现该函数内部只调用了n() a()函数。其他函数都是js内置的东西。OK 向上简单寻找就发现了,这里是一个大闭包

也就是说整个签名的计算,就这些功能。没有调用外界的其他功能。那就好办了。可以直接把这个闭包内的三个函数搞出来,变成我们的js文件

尝试着签个名试试看:

import requests
import execjs

f = open("bbd.js", mode="r", encoding='utf-8')
js = execjs.compile(f.read())

# execjs._exceptions.ProgramError: ReferenceError: window is not defined. 
ret = js.call('e', 'love')
print(ret)

报错了,说这里面没有window

确实,由于我们的js是在node里面运行的,node是没有window对象的. 怎么办?

整合起来这里要用到window['gtk'],这玩意哪里找? 老实说,真不好找。在前面找token的时候,偶然间,发现了这样一段代码:

OK了 . 搞定... 只要把js里面的window['gtk'],修改成这个就好了

至此, 所有参数到位. 写代码就好了.

代码:

import requests
import execjs

f = open("baidu加密.js", mode="r", encoding='utf-8')
js = execjs.compile(f.read())


url = "https://fanyi.baidu.com/v2transapi?from=en&to=zh"
content = input("请输入一段英文:")
data = {
    "from": "en",
    "to": "zh",
    "query": content,
    "transtype": "realtime",
    "simple_means_flag": "3",
    "sign": js.call("e", content),
    "token": "c63d27ea65fdf259bf4d56b792e47b17",
    "domain": "common",
}
headers = {
    "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
    "Referer": "https://fanyi.baidu.com/",
    "Cookie": "REALTIME_TRANS_SWITCH=1; FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; BIDUPSID=7214BF57BA799CC46A76979BF673523E; PSTM=1623064780; __yjs_duid=1_7946e25e70873bbe5515a663714cef991623067825398; BAIDUID=C434B9AC411BEFD0688E2DD206C8429A:FG=1; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; MCITY=-131%3A; BDUSS=kl4RWZySlNaNlRrdWNkSVRXYjYtNGkyUzFCZlhmeklhMDFtcjRLM01GdG4zMVZoRVFBQUFBJCQAAAAAAAAAAAEAAACh3e-019TDvczlwfnB-bjnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGdSLmFnUi5hM; BDUSS_BFESS=kl4RWZySlNaNlRrdWNkSVRXYjYtNGkyUzFCZlhmeklhMDFtcjRLM01GdG4zMVZoRVFBQUFBJCQAAAAAAAAAAAEAAACh3e-019TDvczlwfnB-bjnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGdSLmFnUi5hM; H_PS_PSSID=34438_34440_34380_34496_33848_34450_34092_34107_26350_34425_22160; delPer=0; PSINO=2; BA_HECTOR=a08k202181a52h208d1gj6e0m0q; BAIDUID_BFESS=DDAD1DC127ED72714BFB37825559ACDB:FG=1; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1628330239,1628390196,1629451250,1630747372; Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1630747372; ab_sr=1.0.1_ODBhYjIwYzRhMjk4NDNhZDYwYjRmZWZjM2QwY2E3MGNlMDRjYTI1ZjViODM2MDgxODk5NzJjNDFkZTAxYzc0OTVkYmNhYjZiMWMzZDFmNWFkN2QxMzAwMzZlZjhjYmE3ODkxZGRkNDJkNTY2N2M0NTQxODU5YmU4YTA1NGZjYTJkMDUzN2ZkMGVkMzlmOTU1M2ZlZTRhYWY4ZDQ5YTZmNTJjYmExYzk1ZTdiY2I5MTA4YjcyZDE3MWE1MTE5YjBj",
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"
}

resp = requests.post(url, data=data, headers=headers)
print(resp.json())

四、JS逆向方案一:扣JS代码

# 扣JS代码   缺啥补啥   缺点:麻烦 繁琐   优点:万能
根据加密入口,一步步 将 函数 及 参数 补全,
直到只剩 JS原生代码 及 函数方法调试成功


# 若遇到第三方库时,就不需要在网站的js中 复制扣了,
采用node中的 npm来安装

# eg: CryptoJS库,是JS常用来 加密的库

# 注意:需要在你 项目文件 右键 ---> open in terminal
npm install crypto-jS   # 好处是 安装的第三库,直接在你项目根目录下


# 引入/导入 安装的第三库
var CryptoJS = require("crypto-js");
posted @ 2024-05-21 01:28  Edmond辉仔  阅读(129)  评论(0编辑  收藏  举报