在 JavaScript 逆向时,有时候反爬代码会对console进行更改,导致使用 console.log 输出时没有结果,如下面的例子

1
2
3
console.log = function log(t) {
}
console.log('ssss')

这时我们可以使用 Object.freeze 方法把 console 冻结了,不让它更改,这样就会有输出了,例子如下

1
2
3
4
Object.freeze(console)
console.log = function log(t) {
}
console.log('ssss')

在MDN文档里可以看到,Object.freeze() 静态方法可以使一个对象被冻结。冻结对象可以防止扩展,并使现有的属性不可写入和不可配置。被冻结的对象不能再被更改:不能添加新的属性,不能移除现有的属性,不能更改它们的可枚举性、可配置性、可写性或值,对象的原型也不能被重新指定。freeze() 返回与传入的对象相同的对象。

当然反爬也可以使用 Object.isFrozen() 方法查看 console 是否被冻结, 发现被冻结了,就知道大概率是爬虫了,因为正常用户谁会好端端的冻结 console 呢?

参考资料:

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze

联系作者

声明: 本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码。抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!若有侵权,请在公众号 【静夜随想】 联系作者立即删除!

目标站点: aHR0cHM6Ly9kcHB0LmJlaWppbmcuY2hpbmF0YXguZ292LmNuOjg0NDMvZGlnaXRhbC10YXgtYWNjb3VudA==, 目标参数为请求头中的 lzkqow23819 参数和 请求url 中 6eMrZlPH 参数。

很早之前就听过盾山加密,说是要逆向出算法很有挑战,补环境则相对简单些,于是一直没有尝试。今年抽空试了下补环境,以备不时之需。

之前因为在补环境框架跑的太慢了,于是好久没搞补环境框架了,现在都直接用Node 补了。补的过程中就会发现这个补环境确实相对容易,没有各种花里胡哨的检测,就缺啥补啥完事。唯一需要注意的一点是 typeof 检测,typeof 没法用代理捕获到,得看代码分析了才知道。这个反爬缺啥补啥后就能直接跑起来,没有一点坏心思。

我对反爬的一些理解有提到过,补环境是可以无视混淆的,也不管算法。算法加密再厉害,混淆的再花里胡哨,反爬代码对环境的检测强度不够,一样是无效的。

联系作者

道友遇到一个无壳APP,有个加密token不知道怎么生成的,jadx反编译之后也搜索不到参数,于是请教我怎么搞。猜测有可能是在JavaScript里,因为之前遇到过一个Weex开发的APP,也是怎么搜索都找不到加密参数,最后在JavaScript里找到了。

那次Weex开发的APP,找了老半天都不知道咋回事,后面是通过hook string还是hook url的办法,发现它会去下载一个JavaScript脚本,也就是 app-service.js,在这个脚本里才找到了加密参数。

于是道友就去找JavaScript代码,在反编译的apk里的assets目录里,找到了一个 uniapp 开发的app-service.js,在代码里找到了相关接口,但还是找不到相关的加密参数。于是道友又困惑了,只好来请教我。

此时我才知道这是使用 uniapp 开发的APP,于是去应用宝里下载了一个apk, 安装后发现它提示APP需要更新,点击更新后,APP又去下载了一些东西。拿着apk 里的 app-service.js 看了之后,接口确实都有,怎么会没有参数呢?想到刚才 APP 提示更新,莫非是会更新了uniapp? 于是去找APP临时目录,在/data/data目录下找到 APP对应的目录,在里面找到了一个app-service.js, 和apk里的app-service.js 大小不一样,搜索加密参数,果然在这里。剩下的就交给道友去解决了。

联系作者

任何动态的事物都是基于静态产生的,所以只要找到静态点,那么动态就会坍缩为静态。

刚入行的时候,从https://www.52pojie.cn/thread-1208999-1-1.html 里知道了这句,顿时惊为天人,感悟良多。

滑块只能seleinum过,大环境就这样

这句是来自金角大王的,老实说滑块用自动化过是真舒服,但前提是得解决自动化检测,当遇到难的反爬时,不仔细分析代码都不知道咋检测的。

嘲讽金角,理解金角,成为金角,超越金角

金角就是上面的金角大王,也泛指自动化。刚入行时看不上自动化,现在越来越觉得自动化舒服。之前遇到阿里v2滑块时,搞代码得好久,自动化解决下轨迹就完事了。

不是学了点ast基础你就能搞出某验,某数。。能搞出来的不用ast也能搞出来,甚至不用花时间去写复杂的还原控制流等

AST是真舒服,刚入行的时候都是对着混淆代码调试的,人都调试麻了。后来从蔡老板那里学了AST还原混淆之后,调试起来舒服的很,极大提高生产力。所以这句话就挺莫名其妙。

因为你的不礼貌,我说说我的想法。你我只是开发领域不一样,但你尽然不能理解作为程序员时间成本概念我到感到很惊讶我很清楚我的需求是什么才来联系你,我有自己非常擅长的领域,所以我没那么多时间去自学-门很成熟的技术,我要的是最快时间能解决我的需求,达到我的目的。手上有一堆项目技术问题需要处理,联系你只是一个偶然,你自己看低你的客户价值,那是你自己问题。如果你的爬虫技术满足我们项目的需求,我更愿意你卖接口服务的方式深度合作。既然你关上了让我了解你的门,那没必要聊下去。我想说你299的价格,我一个月薪水可以买断你好多年提供的教程,不可能花精力去深度学习你的经验。这个思想我在开始跟你聊就委婉表达出来,只是你没看懂。你的无知应该是最可笑的。

这句话估计在爬虫圈流传最广。

联系作者

最近听道友说一个颇具难度的某数样本又更新了,补环境跑着跑着通过率会变得很低,不知道错在哪里。于是只好尝试算法方案,尝试之后发现生成核心45位数组的算法有一点变化,之前的方法不能用了,而因为之前没有认真跟过这部分代码,一时半会估计是搞不定了,于是只好先请大佬出山。

跑起来之后,还是得自己研究下这部分逻辑,发现关键点还是一个8位数组,也就是时光在瑞数vmp算法还原流程分析里生成wIlwQR28aVgbT参数的大概步骤里步骤4中说到的8位数组,从打印的日志可以看到这个数组会从 [0, 1, 2, 3, 4, 5, 6, 7] 经过一系列运算生成如 [6, 155, 3, 0, 5, 2, 7, 4] 这样的数组。生成的逻辑在vmp运算里,因为不会还不会反编译vmp,调试了老半天都看不出来生成逻辑,头皮发麻。

后续有时间了打算尝试用补环境方案生成核心45位数组后给算法方案使用,希望能成。也可以排查下补环境为啥会从通过率100%变成20%,解决了这个问题就更舒服了。

不得不感叹,在没有进行vmp反编译的情况下,分析日志还原vmp算法的大佬都太有毅力了。

联系作者

听圈子里的人说,现在都人均rs了,rs不再像20年那会那样,像一座大山。但我看了一些补环境方案,发现很多补环境方案连ActiveXObject对象还没搞明白,只是走了神奇的window.ActiveXObject=undefined 这个bug, 这种方案只能说能过,所以对人均rs我有些怀疑。

问了公司的配置开发,他们能解决4代和5代,不能解决vmp版本。公司的配置开发能力已相当不错, 会解决常用加密如AES,DES等,一些入门加密如 jsl, hexin-v也能做,有一些能力强的还学了下AST,但遇到vmp版本时就没法拿捏。

有一些人用了开源的sdenv, 不不的cynode, 挽风的node-sandbox,就觉得rs简单,但这只是因为工具的能力,并不代表自己会,得知其然,知其所以然才行。

就拿sdenv来说,如果你只会用sdenv, 万一遇到 aHR0cDovL2Nob25ncWluZy5jaGluYXRheC5nb3YuY24veHhna3h0L3BhZ2VzL3F5d2gvemR3ZmFqY3guaHRtbCAK,aHR0cDovL3d3dy5jemNlLmNvbS5jbi9jbi9qeXNqL3lkanloei9INzcwMzE1aW5kZXhfMS5odG0K 等站点怎么办?更不用说aHR0cHM6Ly96eGdrLmNvdXJ0Lmdvdi5jbi94Z2wvCg==,aHR0cHM6Ly93d3cuY2Vid20uY29tL3dlYWx0aC9qZ2xjMzkvaW5kZXguaHRtbCA=等站点了。

联系作者

之前在神奇的window.ActiveXObject=undefined里写过,加上window.ActiveXObject=undefined后,某数的校验就降级了,很多环境校验就都没走了。但神奇的是,在我的一个补环境方案里,去掉这行后,很多环境校验一样没走,一样能过反爬,于是我懵,我困惑了,这到底是什么鬼bug啊。

去网上查了下ActiveXObject这个对象,仅支持微软 Internet Explorer 浏览器,在其它现代浏览器(如 Chrome、Firefox、Safari 等)中无法使用,包括微软的 Edge 浏览器(Chromium 内核)也无法使用。也就是说,只有在微软 Internet Explorer 浏览器里,’ActiveXObject’ in window才是true, 在其它现代浏览器里,’ActiveXObject’ in window 都是false。

而在补环境的时候,userAgent 是Chrome, Edge等浏览器时,如果加上 window.ActiveXObject = undefined; 这行,’ActiveXObject’ in window 就会变成true了,就不符合上述的结果,是可以被检测出来的。

很多人补环境不严格,补环境代码里有 window.ActiveXObject = undefined; userAgent是 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0,这种是Chromium 内核的,换成是我做反爬,就要干它了,悄咪咪的把它记下来,然后在夜深人静的时候给它返回点假数据。

参考资料:

https://webaim.org/blog/user-agent-string-history/

联系作者

一个月前有道友反馈,过不去aHR0cHM6Ly93d3cuY2Vid20uY29tL3dlYWx0aC9qZ2xjMzkvaW5kZXguaHRtbCA=这个样本获取token的接口,于是掏出祖传补环境代码去测试,首页能过,接口竟然过不去,于是把所有的备用方案拿去测试,竟然都过不去,我惊了,这是难得一见的补环境过不去的网站了。

此时只好拿出算法方案,意外发现以前用的找20取4位数组的方法竟然失效了。于是拿出尘封已久的AST代码,也没办法还原控制流混淆了,估计是控制流混淆有点变化。于是索性不还原,直接在vmp循环里打日志慢慢调试,搞了一天,愣是没搞定。

于是求助小黄鱼大佬们,问了一圈补环境方案,竟然没找到一个能搞定的,看来大家的补环境水平都差不多了。于是求助算法大佬们,终于找到解决的办法。其实算法没变化,只是我用的20取4位数组的办法还不够通用,在这个站点上正好失效了。

有了算法方案后,对比之后发现这个站点上之所以过不去是因为校验指纹数组了,于是想起2年前曾研究过这个指纹数组,找到2年前的补环境,发现竟然能过,收工。

头疼的是,第二天补环境就要跑不动了,测试通过率只有60%,算法方案才能稳稳的跑通。结果前几天,补环境和算法方案都跑不动了,仔细一看,url加密,表单加密,响应加密都上了,这属实是全家桶了,头皮发麻。

联系作者

一般反爬在生成加密参数的时候,都会取navigator里的userAgent属性,然后生成加密参数。在使用requests库的时候,爬虫也会设置下headers里的User-Agent值。但很多时候爬虫并没有保持这两个值一致。

在使用补环境生成参数的时候,如果要支持动态传入userAgent,就得在调用参数生成接口的时候增加userAgent参数,并传到补环境代码里设置navigator.userAgent,爬虫偷懒的时候就会省掉这一步,直接固定写死navigator.userAgent。

还有在使用jsdom补环境生成参数的时候,会错误设置userAgent。如果像以下代码一样设置userAgent就是错的

1
2
3
4
5
6
7
8
9
10
11
const { JSDOM } = require('jsdom');

// 自定义的 userAgent 字符串
const myUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3';

// 创建 jsdom 实例并设置 userAgent
const dom = new JSDOM(``, {
userAgent: myUserAgent
});

console.log(dom.window.navigator.userAgent); // 输出类似Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/19.0.0 这种包含jsdom的userAgent

jsdom里正确的做法是使用ResourceLoader设置userAgent

1
2
3
4
5
6
7
8
9
10
11
12
13
const jsdom = require("jsdom");  // 引入 jsdom
const { JSDOM } = jsdom; // 引出 JSDOM 类, 等同于 JSDOM = jsdom.JSDOM
const userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
const resourceLoader = new jsdom.ResourceLoader({
userAgent: userAgent
});
const dom = new JSDOM(``, {
url: "https://example.org/",
referrer: "https://example.com/",
resources: resourceLoader,
});
window = dom.window
console.log(window.navigator.userAgent) // 输出是Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36

有一些反爬在不严格的时候即便userAgent不一致也让爬虫通过,但风控严格的时候userAgent不一致又不让爬虫通过,让爬虫摸不着头脑,得排查老半天。所以爬虫为了稳妥起见,还是得保持navigator里的userAgent属性和headers里的User-Agent值一致。

联系作者

通常我们写好一个接口后,需要做接口性能测试,知道接口的QPS(Queries Per Second),百分位响应时间,我们可以用Python库Locust来做

pip install locust库后,新建locustfile.py,写入要测试的接口,demo如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from locust import HttpUser, task, between
import json

class MyUser(HttpUser):
# 设置用户的等待时间(模拟用户在操作之间的思考时间)
wait_time = between(1, 2) # 每次任务之间等待 1~2 秒

@task
def post_request(self):
# 定义请求的 URL 和数据

url = 'https://www.baidu.com'

payload = {
"url": url,
}
headers = {
"Content-Type": "application/json"
}

# 发起 POST 请求
with self.client.post('http://%s/api/generate_cookies' % '127.0.0.1:5009', json=payload, headers=headers, catch_response=True) as response:
# 验证响应状态码是否为 200
if response.status_code == 200:
try:
# 如果需要验证返回内容,可以解析 JSON 数据
response.success() # 标记请求成功
except ValueError:
response.failure("Response is not valid JSON")
else:
# 如果状态码不是 200,标记请求失败
response.failure(f"Status code: {response.status_code}")

locust -f locustfile.py –web-port=8089,之后浏览器里打开 http://localhost:8089/ 就可以新建一个测试

测试结果中,RPS就是我们常说的QPS,95%ile 指的是 95百分位响应时间 (Percentile Response Times),也就是95%的请求的响应时间不超过某个值。

联系作者