某多多anti_content参数逆向(补环境)

本文讲述了如何通过分析加密参数的生成过程,定位到Webpack打包后的代码结构,利用vm2沙箱环境和补环境技术,最终解密拼多多网站的加密内容。作者详细描述了webpack的层级结构、加密函数的执行路径以及如何修复浏览器环境缺失引发的错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文章中所有内容仅供学习交流,相关链接做了脱敏处理,若有侵权,请联系我立即删除!

目标网址:aHR0cHM6Ly9waW5kdW9kdW8uY29tL2hvbWUvM2M=

加密参数:anti_content

请求带anti_content字段,结果正常,不带则异常

参数定位

anti_content 全局搜索

只有一处,定位到js文件中该位置,下断点

当t.next = 10 ,anti_content还未产生值

t.t1 = t.sent ,anti_content值已经产生

加密位置显而易见,Object(l.a)()是加密方法体,下断点,跳转到v函数

r.messagePackSync();产生我们需要结果,方法继续下断点

跳转到de函数,函数返回值下断点

加密函数的值是从 n[r("0x1bb", "A3e0")](ue)得到,n[r("0x1bb", "A3e0")]方法,参数传入一个参数,然后执行该参数,即执行ue()函数,即可得到anti_content加密值

拉到文件顶部,发现是一个典型的webpack

n[r("0x1bb", "A3e0")](ue)是在fbeZ函数内部,而fbeZ又封装一个webpack,ue()函数在第四个模块(角标从0开始)

我们目标函数是在第二层webpack fbeZ内部,我们直接来扣代码,扣之前注意:找到fbeZ调用者,调用时是否传入参数。

文件全局搜索发现只有一处调用

显而易见n是外层的导出器函数,进入函数内部看看,验证正确

在n("fbez")函数内部

跟栈,可以发现调用fbez函数传入的参数:{

serverTime: e

}

返回值为r

anti_content的加密值已经出来了

扣webpack

接下来我们扣webpack 代码,webpack打包的代码有明显的特征,很容易识别出来,webpack推荐

运行成功,运行fbez模块用到8oxB,YuTi模块,浏览器找到这两个模块放入pdd_webpack文件中。本地运行发现没有出结果(缺少浏览器环境),去浏览器浏览器运行,成功生成加密参数并可用

补环境

为了更好的体验调试,本次我们不用补环境框架

vm2沙箱介绍

本地js环境联条配置可参考

准备环境做好了,F5运行vm.js

报错

定位到浏览器W("0x3f", "LZ%H")该处位置,浏览器环境rt[z]值为document,vm2沙箱环境值为undefined,缺少document导致环境报错

在enviroment.js文件中把window,document,navigator ,history ,localStorage常检测的环境先补上,再挂上代理

rename,savefunction方法作用

没有使用rename,savefunction方法之前

使用rename,savefunction方法之后,使其和浏览器结果一致

运行vm.js,日志报错,输出日志信息

我们将属性值为undefined对照浏览器补齐,如果浏览器的属性值为也为null可直接忽略

补完之后出结果,可用

代码提供

// vm.js代码
var vm= require("vm2")
var fs = require('fs')
const {VM,VMScript} = vm
var vm = new VM()

var code = fs.readFileSync('./enviroment.js')
code += fs.readFileSync('./pdd.js')

const script = new VMScript(code, `sy_debugging.js`);
vm.run(script)

debugger;;

// function get_encrypt(){
//     var res = vm.run(code)
//     return res
// }
// debugger;
// console.log(get_encrypt());
//enviroment.js 补环境代码
debugger;;
catvm = {}
catvm.memory = {log:[]};
// 保护伪造函数toString
;(() => {
    const $toString = Function.toString
    const myFunction_toString_symbol = Symbol('('.concat('', ')_', (Math.random()) + '').toString(36))
    const myToString = function (){
        return typeof this === 'function' && this[myFunction_toString_symbol] || $toString.call(this)
    }
    function set_native(func, key, value){
        Object.defineProperty(func, key, {
            enumerable: false,
            configurable: true,
            writable: true,
            value: value
        })
    }
    delete Function.prototype.toString
    set_native(Function.prototype, "toString", myToString)
    set_native(Function.prototype.toString, myFunction_toString_symbol, "function toString() { [native code] }")
    this.catvm.savefunction = (func, funcname) => {
        //todo 系统函数没名字 native code
        set_native(func, myFunction_toString_symbol, `function ${func.name || funcname || ''}() { [native code] }`)
    }
}).call(this)

//代理
catvm.proxy = function (obj) {
    // Proxy 可以多层代理,即 a = new proxy(a); a = new proxy(a);第二次代理
    // 后代理的检测不到先代理的
    return new Proxy(obj, {
        set(target, property, value) {
            catvm.memory.log.push({"类型":"set-->","调用者":target,"调用属性":property,"设置值":value});
            console.table([{"类型":"set-->","调用者":target,"调用属性":property,"设置值":value}]);
            return Reflect.set(...arguments); //这是一种反射语句,这种不会产生死循环问题
        },
        get(target, property, receiver) {
            if(target.name!=='toString' && property !=='Math' && property !== 'isNaN' && property !=='undefined'){
                catvm.memory.log.push({"类型":"get<--","调用者":target,"调用属性":property,"获取值":target[property]});
                console.table([{"类型":"get<--","调用者":target,"调用属性":property,"获取值":target[property]}]);
            }
            return target[property];  // target中访问属性不会再被proxy拦截,所以不会死循环
        }
    });
};

//定义原型名称
catvm.rename = function rename(obj,proValue)
{
    //定义原型名称
    Object.defineProperties(obj,{
        [Symbol.toStringTag]:{
            value:proValue,
            configurable:true
        }
   });
}

delete global;
delete Buffer;

window = globalThis;
window.outerHeight = 1080;
window.chrome = catvm.proxy({}) 
setTimeout = function setTimeout(){console.log(arguments)}
var Window = function Window(){};
Object.setPrototypeOf(window,Window.prototype);
catvm.rename(Window.prototype,'Window');
catvm.savefunction(Window)

var HTMLDocument = function HTMLDocument(){};
document = {
    cookie:'_nano_fp=XpmaXqdaXqdbXqXJXo_ShM7Br8OTCzqBDo6OVbYc',
    referrer:'https://xxx.com/home/3c',
    getElementById:function getElementById(){
        console.log(arguments);
    },
    addEventListener:function addEventListener(){
        console.log(arguments);
    }
};
Object.setPrototypeOf(document,HTMLDocument.prototype);
catvm.rename(HTMLDocument.prototype,'HTMLDocument');
catvm.savefunction(HTMLDocument)

var Location = function Location(){};
location = {
    href:'https://xxx.com/home/3c',
    port:'',
};
Object.setPrototypeOf(location,Location.prototype);
catvm.rename(Location.prototype,'Location');
catvm.savefunction(Location)

var Navigator = function Navigator(){};
navigator = {
    plugins:[],//插件直接补为空列表就ok,有的浏览器可能就是没有插件
    languages:['zh-CN', 'zh'],
    userAgent:'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
};
Object.setPrototypeOf(navigator,Navigator.prototype);
catvm.rename(Navigator.prototype,'Navigator')
catvm.savefunction(Navigator)

var History = function History(){};
history = {
    back:function back(){
        console.log(arguments);
    }
};
Object.setPrototypeOf(history,History.prototype);
catvm.rename(History.prototype,'History')
catvm.savefunction(History)

var Storage = function Storage(){};
localStorage = {
    getItem:function getItem(){
        console.log(arguments)
    }
};
Object.setPrototypeOf(localStorage,Storage.prototype);
catvm.rename(Storage.prototype,'Storage')
catvm.savefunction(Storage)

var Screen = function Screen(){};
screen = {
    availHeight:1080,

};
Object.setPrototypeOf(screen,Screen.prototype);
catvm.rename(Screen.prototype,'Screen')
catvm.savefunction(Screen)

window = catvm.proxy(window)
document = catvm.proxy(document)
location = catvm.proxy(location)
navigator = catvm.proxy(navigator)
history = catvm.proxy(history)
localStorage = catvm.proxy(localStorage)
screen = catvm.proxy(screen)

结语

拼多多补环境比较简单,这样补的环境并不完善,仅仅是针对pdd网站,可以搭建一个补环境框架逐步向框架里面添加浏览器环境,补的环境越多越完善,通杀的网站越多

爬虫(Web Crawler)是一种自动化程序,用于从互联网上收集信息。其主要功能是访问网页、提取数据并存储,以便后续分析或展示。爬虫通常由搜索引擎、数据挖掘工具、监测系统等应用于网络数据抓取的场景。 爬虫的工作流程包括以下几个关键步骤: URL收集: 爬虫从一个或多个初始URL开始,递归或迭代地发现新的URL,构建一个URL队列。这些URL可以通过链接分析、站点地图、搜索引擎等方式获取。 请求网页: 爬虫使用HTTP或其他协议向目标URL发起请求,获取网页的HTML内容。这通常通过HTTP请求库实现,如Python中的Requests库。 解析内容: 爬虫对获取的HTML进行解析,提取有用的信息。常用的解析工具有正则表达式、XPath、Beautiful Soup等。这些工具帮助爬虫定位和提取目标数据,如文本、图片、链接等。 数据存储: 爬虫将提取的数据存储到数据库、文件或其他存储介质中,以备后续分析或展示。常用的存储形式包括关系型数据库、NoSQL数据库、JSON文件等。 遵守规则: 为避免对网站造成过大负担或触发反爬虫机制,爬虫需要遵守网站的robots.txt协议,限制访问频率和深度,并模拟人类访问行为,如设置User-Agent。 反爬虫应对: 由于爬虫的存在,一些网站采取了反爬虫措施,如验证码、IP封锁等。爬虫工程师需要设计相应的策略来应对这些挑战。 爬虫在各个领域都有广泛的应用,包括搜索引擎索引、数据挖掘、价格监测、新闻聚合等。然而,使用爬虫需要遵守法律和伦理规范,尊重网站的使用政策,并确保对被访问网站的服务器负责。
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CgfFan

感谢看官老爷赏的咖啡钱

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值