jsvmp逆向记录xhs

前言

本次逆向的目的主要是感受xhs的jsvmp,完善一下自己的补环境框架,并无相关业务,所有具体各个参数获取的流程以及cookie啥的就不分析了。上手之前,先找相关文章看看,所谓砍柴不费磨刀工功。目前有三种方式:RPC,补环境,纯算法还原,各种优缺点就不赘述了。

目标接口

aHR0cHM6Ly93d3cueHh4LmNvbS9hcGkvc2VjL3YxL3NoaWVsZC93ZWJwcm9maWxl

逆向目标

X-s, profileData

结果展示

jsvmp逆向记录xhs_第1张图片

相关环境准备

python包

pip isntall node-vm2: 因为本人用的vm2补环境的方案,此包运行补好的js;

nodejs相关

node: 我的node版本v18.15.0
npm install vm2: vm2沙盒

加密入口

因为看过相关文章。所有知道X-swindow._webmsxyw方法生产,profileDatawindow.xhsFingerprintV3.getV18

// X-s
window._webmsxyw('/api/sns/web/v1/feed', {"source_note_id":"xxxxxxxx"});

//profileData
window.xhsFingerprintV3.getV18(function (params) {
    console.log(params)
})

打开页面看看,果然都是各种异步任务,不管开干

补环境

既然是补环境就不用在乎这些异步细节,只要你用的XMLHttpRequest发请求,直接在框架里给你拦截下来就行。

0x0

下载一个html文件,让框架自行按顺序下载对应的js文件

0x1

执行所有的js文件,果然框架还有很多功能没去实现,xhr并没有捕获到任何请求, 看下框架还有哪些功能要完善的,mdn上查找一下,并在框架中简单实现具体的功能。

  • window.queueMicrotask, 微任务队列,既然是一个存入微任务的队列,可以用async函数封装一下,然后丢给系统自行调用,或者保存起来js执行完后手动调用。这里用第二种方式实现。
  • window.Crypto, 直接将node的Crypto包引进vm2沙盒里,在框架里包装一下避免原型链检测
  • window.AudioContext, 简单补下原型链之类的,功能后面用到在说

注意:以上我为了完善框架,并不代表检测点

ok,将这些以前没实现的功能实现下,继续跑js。我擦,xhr还没拦截到

0x2

经过一顿掉头发,发现我的js文件的执行流程和浏览器不一样,一顿无语,给浏览器下脚本断点,调整js文件执行顺序。搞定,成功拦截到了xhr请求。

0x3

用requests发送拦截的xhr请求参数,果不其然没通过检测,{"code":300015,"success":false,"msg":"浏览器异常,请尝试关闭/卸载风险插件或重启试试!","data":{}}

0x4

框架的Proxy开起来,先观察一下都干了,为了避免干扰,手动调用window._webmsxyw('/api/sns/web/v1/feed', {"source_note_id":"xxxxxxxx"});方法,
并将加密无关的js全部不执行,看看都检测了啥。

  • 自动化浏览器相关的检测:
window._phantom
window.__nightmare
window._selenium
window.callPhantom
window.callSelenium
window._Selenium_IDE_Recorder
document.__webdriver_evaluate
document.__selenium_evaluate
document.__webdriver_script_function
document.__webdriver_script_func
document.__webdriver_script_fn
document.__fxdriver_evaluate
document.__driver_unwrapped
document.__webdriver_unwrapped
document.__driver_evaluate
document.__selenium_unwrapped
document.__fxdriver_unwrapped

我是vm2,肯定没问题,pass

  • PluginArray
    new PluginArray看是否有异常,pass
  • cookie
    发现有拿cookie,但是我cookie用的是浏览器的,并且实现的相关功能,pass
  • localStorage
    使用的localStorage中的sdt_source_storage_key,去浏览器粘贴一个,pass
  • 各种dom操作
    创建div, ul, li, span等之类的元素,实现了dom操作相关的功能 pass
  • div元素的offsetHeight检测
    逻辑是先创建一个div,设置高度为20px,然后获取offsetHeight值,再添加到body里面,然后再次获取offsetHeight值, pass
  • 字体offsetWidth,offsetHeight检测
    和akm一样,创建span标签填充内容mmmmmmmmmmlli,设置字体大小72px, 不停的换字体然后获取宽高之类的, pass
  • canvas相关的检测
    太常规了,pass
  • 堆栈检测
    抛一个异常,看看调用栈里面是否有/vm|bootstrapNodeJSCore|tryModuleLoad|evalmachine|runInContext/g这些关键词,框架早就已处理了,pass

通过代理Proxy日志,关键位置放置的debugger,暂时分析出这些检测,再跑一边,没通过!我日**。
睡觉睡觉,反正没业务,明天再说。

0x5

第二回,jsvmp调试,看到这vmp混淆就一顿烦躁,简单跟几步代码,整体规律还是挺明显,来来回回执行那么几个函数,找个位置插桩看下,随机数和时间戳啥的先不固定,代码和浏览器都跑一边插座,保存日志,对比分析一下发现检测环境结果的字符串有一个不一样:
x2=0|0|0|1|0|0|1|0|0|0|1|0|0|0|0 -> 浏览器中的结果;
x2=1|0|0|1|0|0|1|0|0|0|1|0|0|0|0 -> 代码中的结果。
条件断点到大致位置,我的返回是true,浏览器返回是false。具体执行了一下发现是一个appendChild操作,具体分析发现检测是这样的:

div1 = document.createElement('div');
div2 = document.createElement('div');
div1.appendChild(div2);

// 浏览器中这句会抛一个异常,我的框架没有异常,大意了
div2.appendChild(div1);

将框架的bug补上,再跑一遍代码。通过了!通过了!通过了!

node-vm2使用遇到的问题

这个坑整了我好久!搞得我一直以为是我补的环境有问题,其实node跑出来的一直是正确的没问题的结果,偶然从调试控制台粘贴结果去跑才发现的,白掉了好多头发。

  1. 现象:使用node执行vm2得出的结果没问题,但是通过python的node-vm2包执行得出得结果过不了检测,js代码都是同一份,得出得结果字符长度都是一样的。
  2. 分析:阅读python的node-vm2源码,发现这个包得原理就是起一个进程去调用node执行js代码,通过node的readline读取进程的输入process.stdin,与python进程的process.stdin,process.stdout实现交互。进一步对比分析发现,我的补环境框架使用的new vm2.VMScript(code, 'xxx.js');对js进行了一个预处理,而node-vm2包是直接vm.run(code)
  3. 解决: 改python包node-vm2源码,添加这步预处理操作vm2.VMScript。这样就没问题了,至于VMScript具体干了啥下次再去看吧。

代码

按照惯例是会放出成品demo的,但是国内这环境,大家还是自己动手吧,更有成就感。都看到这里了就随手帮我点个star吧!! https://github.com/conlin-huang/aio-scrapy

关于爬虫的一点感慨

随着对抗强度的不断升级,爬虫要学的东西也越来越多了,web逆向,app逆向。爬虫也从最初的脚本小子,逐渐变成分工明确的团队,有专门做web逆向的,有专门做app端逆向的,做爬虫架构的,这似乎成了一个趋势。但是目前绝大多数公司就一个爬虫,然而人的精力是有限的,你我皆凡人,两个都研究太耗费心神了,除非天赋异禀,而且容易造成两个都是半吊子。所有在做规划的时候请选择一条路线,要么专精web,要么专精app。最后的最后,欢迎交流爬虫相关的知识,wx: h995018884。

如果觉得本文不错,就请抽根华子吧
jsvmp逆向记录xhs_第2张图片

你可能感兴趣的:(爬虫,python,javascript)