13-哈希md5案例:My Token
之前我们讲了扣代码,看起来很复杂,很麻烦,其实都是补环境不用动啥脑子,本文的标准加密算法很简单,但是需要花功夫找加密位置
本文我么讲的是哈希算法其中之一的md5,回顾一下,哈希加密有几个特点:
- 明文不变,哈希后的密文不变(但有特殊的哈希Hmac,它改进了哈希不变的特点,加密过程中多了一个叫密钥的参数,密钥一变,密文就会变)
- 哈希加密出的数据长度是固定的(比如:md5是32位,sha1是40位,Hmac也无法改变位数)
- 哈希加密后的数据是十六进制的,即由0-9和小写字母a-f组成(16进制)
- js中使用哈希加密需要安装crypto-js库,安装命令:
npm install crypto-js --save // --save是在本地安装以防调用找不到包
下面开始本文,本文案例所用到的网站:MD5加密 my token
找数据包
我们进入网站,打开开发者工具,如果发现这个网站数据包很多,可以先把数据包清掉,然后翻页触发接口,但是你会发现还有好多数据包啊,这时我们搜索页面上的数据来找数据包:webp结尾都是图片,找到数据包之后我们可以筛选数据包:
截取数据包的一段(可以像xhr截取的一样)填进去然后筛选数据包:
筛选之后可能有好几个相同数据包,强迫症可以全清除然后重新翻页,多翻几页对比负载数据:
你会发现只有code,timestamp 以及页数变化,除了code是密文,其他都可以自己实现,
那我们就找code的加密逻辑
找加密位置
我们先尝试一下关键字搜索:26个其实不算多,但是有可能不是等号赋值,所以看一看冒号:
46个,这样一算其实并不好找,做案例之前其实我打上过断点但是翻页竟然没断下来,可能我漏了,想尝试的同学们可以试一下
既然这个不太好找我们可以想一想有没有什么可以hook的,我想过hook赋值或者hook-md5加密(因为我看过加密长度是32位,可能是md5加密),但是貌似都不行,hook应该先想函数名,知道函数名才能重写函数来hook(个人总结),所以放弃吧,直接上绝招——xhr吧:
发现了一个问题没有,这里用的是fetch,不是send,其实这是fetch接口:
不过对我们这次案例影响不大哈,看一下断点的入参:
加密过了,ok找上一个栈…么??
这么多栈欸,这次我们直接先看最后一个栈,万一最后一个栈是加密位置,顺着分析栈前面不白分析了么
那我们二分法找吧(差不多二分就行)有些栈很短没传参,或者参数和我们找的一点关系都没有那乱点吧(不开玩笑),多点点,找找就能找到或者找到线索,找了两年半发现一个栈:
这里数据很眼熟:
ok,那我们就在这个栈周围找,这里是明文,优先去下一个栈(即浏览器位置上来看的上面一个站)找:
然后找到了:
返回数据带有加密,怎么说,加密逻辑就在这个函数了,找!!
一点一点看,看到这个n是加密的:
再向上找这个n生成的位置:
打上断点运行过来:
然后将逗号表达式整理一下:
n = c.Fk((r.Z)({}, e, c.xZ()(null === e || void 0 === e ? void 0 : e.mytoken)))
null === e || void 0 === e ? void 0 : e.mytoken,这一段是一段防御型编程,为防止e不存在抛出异常用的,不用管啊(这里其实我没有分析,直接扔给豆包)
所以最终整理成:
n = c.Fk((r.Z)({}, e, c.xZ()))
然后我们从最里面的参数开始看:
首先是c.xZ()和e:
然后是函数r.Z({}, e, c.xZ()):
发现r.Z()函数是将其中的参数拼接在一起,这跟加密没啥关系哈,然后发现c.xZ()中有加密参数code,进函数中看看:
找到了加密位置,打断点进来:
然后验证我们猜测的md5加密猜想,用他的函数加密字符串123:
这里讲一下这种函数调用:
var aa = function(num) {console.log(num)
}var bb = function() {return aa
}bb()(123)
下面开始扣代码
扣代码
下面我们将核心代码先扣下来:整理一下:
var code = s()(e + "9527" + e.substr(0, 6))
然后函数s既然知道是md5加密,我们可以自己写一下:
var crypto_js = require('crypto-js')function get_md5() {return crypto_js.MD5(e + "9527" + e.substr(0, 6)).toString // 一定记得加toString
然后我们看一下e是哪儿来的:
那我们写一下
var t = Date.now()
把时间戳传进去,并调用函数:
// n = c.Fk((r.Z)({}, e, c.xZ()))
var crypto_js = require('crypto-js')
function get_md5() {var t = Date.now()return crypto_js.MD5(t + "9527" + t.substr(0, 6)).toString()
}
console.log(get_md5())
然后运行:
回去看一眼:
那我们问问豆包看看怎么说:
那我们加一个toString就好了:
这就好了,其实网页中就有:
这样写其实想告诉我自己和大家:
- 有时候直接扣网页中的更好,要平衡扣代码和写代码
- 善用AI,或者多查资料
到此加密参数就完成了,我们可以验证一下:
运行后对照一下:
这次才算真的完工(加密部分)下面来用py拿数据吧
py调用js爬取数据
我们直接套公式然后再去[工具网站](https://www.lddgo.net/convert/curl-to-code)生成基础爬虫,然后封装一下:粘贴到编程工具中之后先运行一下看看会不会出错,然后将密文那一行注释掉看看能不被不过拿到数据(看看后端校不校验参数,这些其实应该先做的,不校验直接拿到数据就不用逆向了),这里肯定拿不到,那我们就开始调用js生成密文然后写入py吧:
import os
import time
import execjs
import requestsdef get_response(code, page, trans_time):headers = {'accept': '*/*','accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6','cache-control': 'no-cache','origin': 'https://mytokencap.com','pragma': 'no-cache','priority': 'u=1, i','referer': 'https://mytokencap.com/','sec-ch-ua': '"Chromium";v="140", "Not=A?Brand";v="24", "Microsoft Edge";v="140"','sec-ch-ua-mobile': '?0','sec-ch-ua-platform': '"Windows"','sec-fetch-dest': 'empty','sec-fetch-mode': 'cors','sec-fetch-site': 'cross-site', # ua自己写一下}params = {'pages': f'{page},1','sizes': '100,100','subject': 'market_cap','language': 'en_US','legal_currency': 'USD','code': code,'timestamp': trans_time,'platform': 'web_pc','v': '0.1.0','mytoken': '',}return requests.get('https://api.mytoken.info/ticker/currencyranklist', params=params, headers=headers).textclass JSExecutor:def __init__(self, js_file_path):if not os.path.isfile(js_file_path):raise FileNotFoundError(js_file_path)with open(js_file_path, 'r') as f:self.js_code = f.read()self.js_code = execjs.compile(self.js_code)def call(self, func_name, *args):return self.js_code.call(func_name, *args)if __name__ == '__main__':js_executor = JSExecutor('11-my_token_md5.js')for get_page in range(1, 5):get_time = str(int(round(time.time() * 1000)))params_code = js_executor.call('get_md5', f'{get_time}')print(params_code, type(params_code))print(get_response(params_code, get_page, get_time))
// n = c.Fk((r.Z)({}, e, c.xZ()))
var crypto_js = require('crypto-js')
function get_md5(t) {// var t = Date.now().toString()return crypto_js.MD5(t + "9527" + t.substring(0, 6)).toString()
}
// console.log(get_md5(t))
细心的同学都发现了,js代码有变动,因为运行py时的时间戳和运行js的时间戳是不一样的,运行每一步代码都有时间,当运行到js时可能时间戳已经变化了,这样加密出来的参数的密文就不一样了,网页后端就校验不上了,另外py调用js时,将js调用最终函数的测试代码(console.log(get_md5(t)))注释掉以防有冲突
最后拿到数据:
结语
声明:本文只做技术交流本文讲述的加密并不难,掌握流程和思路更重要,如文章有问题请及时交流,加油加油