js逆向 - Proxy代理

声明 本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请在CSDN 私信 联系作者立即删除!

  • Proxy 是 JavaScript 中的一种强大的元编程工具,用于捕获并拦截对象的各种操作。在逆向工程中,我们可以使用 Proxy 对象来修改 JavaScript 对象的行为,以实现一些特殊的需求

Proxy是什么:

  • Proxy可以对目标对象的读取,函数调用等操作进行拦截,并对其进行操作处理。
  • Proxy不直接操作对象,而是像代理模式一样,对对象进行操作,同时还可以添加一些额外的操作

Proxy基础用法

1. 首先定义一个目标对象(需要被代理的对象) 和一个处理器对象(handler)。处理器对象声明了对目标对象的指定行为

// 目标对象
var target = {
     name: 'JACK',
     age: 18,
}


// 代理行为对象
var handler = {
    get:function (target,property,receiver) {
        /*
        * target: 被代理对象
        * property: 属性名
        * receiver: 代理后的对象
        * */
        console.log("get: ",target,property,target[property])
        return target[property]
    },
    set:function (target,property,value) {
        /*
        * target: 被代理对象
        * property: 属性名
        * value: 重新设置的值
        * */
        console.log("set:",target,property,value)
        return Reflect.set(...arguments)
    }
}


// 使用Proxy构造函数实例化新的target对象
let proxiedTarget = new Proxy(target,handler)

// todo 如果进行取值操作,就会触发代理行为的get方法
console.log(proxiedTarget.name)

// todo 如果会值进行设置,就会触发代理行为的set方法
proxiedTarget.name = "雷点"
console.log(proxiedTarget.name)

Proxy在补环境中的作用

在js逆向中,对某个加密算法确定之后,要对其导出到本地进行调试直到能准确的出值。但是如果某个网站检测了浏览器指纹,等浏览器环境,我们就需要深入分析进行补环境,以得到导出的算法能准确的使用

通常,运行导出的算法后,通过undefined或报错来逐步分析和补环境,这样比较耗时

我们可以使用 Proxy 对全局遍历 windowdocumentnavigator 等常见环境检测点进行代理,拦截读取、函数调用等操作,并通过控制台输出。这样,我们就能够实现检测环境自吐的功能,后续再针对吐出来的环境进行统一的补环境,更加方便

Proxy代理学习

一.  Proxy代理与Reflect反射简介

Proxy (代理): 

  • Proxy是ES6中引入的新特性,允许拦截并自定义对象的操作
  • 通过常见一个代理对象,可以在访问目标对象时添加额外的操作或修改行为
  • 代理对象与被代理对象实现相同的接口,接受并处理对被代理的访问操作
  • 主要用于拦截对象的,读取,设置,调用等操作,从而实现预处理或附加操作

Proxy (代理) 在js逆向中的使用场景:

  • 环境补全:在js逆向中,可以使用 Proxy对常见的环境监测点(如 windowdocumentnavigator 等)进行代理,拦截并读取和函数调用等操作,并输出环境信息,达到高效的补全环境

Reflect (反射):

  • Refect是ES6中的另一个内置对象,提供了一系列方法,让开发者通过调用这些方法访问底层功能
  • 反射允许我们在运行时获取,操作,修改对象的状态和行为
  • 反射可以用于判断属性是否存在,调用函数,设置属性等操作

Reflect (反射) 常见使用场景:

  • 属性操作:使用 Reflect 可以读取和设置对象的属性值,判断属性是否存在,删除属性等。
  • 函数调用:可以动态调用函数,绑定 this 和参数列表。
  • 构造函数:通过 Reflect.construct 创建对象实例。
  • 属性描述符:获取和设置属性的描述符。

二.  Proxy基本使用

语法const p = new Proxy(target,handler)

  • target:  要使用Proxy代理的目标对象(可以是任何类型的对象,包括原生数组,函数等)
  • handler:函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为
    • handler对象的方法:
      • getPrototypeOf: Object.getPrototypeOf 方法的拦截器
      • setPrototypeOf: Object.setPrototypeOf 方法的拦截器
      • isExtensible:Object.isExtensible() 方法的拦截器
      • preventExtensions:Object.preventExtensions() 方法的拦截器
      • getOwnPropertyDescriptor:Object.getOwnPropertyDescriptor() 方法的拦截器
      • defineProperty:Object.defineProperty() 方法的拦截器
      • get: 属性读取操作的拦截器
      • set: 属性设置操作的拦截器
      • deleteProperty: deleteProperty()是 delete操作符的拦截器
      • apply: 函数调用操作时的拦截器
      • construct:new 时的拦截器
  • 想要了解更多关于Proxy的方法请前往Proxy - JavaScript | MDN
  •  想要了解更多关于Reflect的方法请前往Proxy - JavaScript | MDNReflect - JavaScript | MDNProxy - JavaScript | MDN

代理对象:

// 使用Proxy拦截Obj对象
let Obj = {
    name:"小明"
}
// 定义拦截器
let handler = {
    get(target,property,obj){
        /*
        * target: 原对象
        * property: 属性名
        * obj: 代理后的对象
        * */
        console.log(`读取了: ${property}属性`)
        return target[property]
    },
    set(target,property,value){
        /*
        * target: 原对象
        * property: 属性名
        * value: 要修改的值
        * */
        console.log(`设置了: ${property}属性为${value}`);
        target[property] = value
    }
}

// 实例化代理对象
let proxy = new Proxy(Obj,handler)

// 读取操作是触发get方法
console.log(proxy.name)

// 设置操作触发set方法
proxy.name = "雷点"

代理函数:

function sum(a,b){
    return a + b
}

// 定义拦截器
let handler = {
    apply:function (func,thisArg,argumentsList) {
        /*
        * func: 代理的原函数
        * thisArg: func调用者
        * argumentsList: 函数参数
        * */
        console.log(func)
        console.log(thisArg)
        console.log(argumentsList)
        return func.apply(thisArg, argumentsList);
    }
}

let proxy = new Proxy(sum,handler)

console.log(proxy(1, 2));

三.  Proxy与Reflect基本使用

/*
* Proxy作用: 监控对象操作
* Reflect作用: 执行原始操作
* */

let symbol = Symbol(123)

// 定义一个需要被代理的对象
let user = {
    "name":"小明",
    1:2,
    [symbol]:"symbol111"
}

// 参数1: 需要代理的对象或函数等
// 参数2: 拦截器
let proxy = new Proxy(user,{
    get:function (target, prop, receiver) {
        /*
        * target: 原始对象
        * prop: 属性名,只有两种类型,string|symbol,其他传入的类型都会转为string类型
        * receiver: 被代理后的对象
        * */

        // 直接通过原始对象target[prop]获取属性值
        // 不会递归
        // let result = target[prop]

        // 通过Reflect.get(target, prop, receiver)获取属性值
        // 可以递归
        let result = Reflect.get(target,prop,receiver)
        console.log(`get方法 对象:${JSON.stringify(target)} 属性:${prop.toString()} 返回值:${result}`)

        // if (typeof prop=== 'string'){
        //     console.log(`get方法 对象:${JSON.stringify(target)} 属性:${prop} 返回值:${result}`)
        // }else if (typeof prop === "symbol"){
        //     // symbol不能使用模板字符串,不然会报错,所以这里做了一层判断
        //     console.log("正在获取: ",prop)
        // }

        return result
    },
    set:function (target, prop, newValue, receiver) {
        /*
        * target: 原始对象
        * prop: 属性名
        * newValue: 修改的值
        * receiver: 被代理后的对象
        * */
        console.log(`set方法 对象:${JSON.stringify(target)} 属性:${prop} 修改的值:${newValue}`)
        target[prop] = newValue
    }
})


// 使用name属性时触发get方法
console.log(proxy.name);

// 设置name属性时触发set方法
proxy.name = "雷点1"

// ===================================

console.log(proxy[symbol])

// ===================================
console.log(proxy[1])

// ===================================
// 输出undefined,因为当前代理的对象中不存在这个属性,
// 在补环境中使用代理时,当一个属性或者方法被调用得到的结果是undefined时就需要去进行补环境
console.log(proxy.age)

四. 简单模拟proxy应用场景


let window = {
    
}

// 代理,window
window = new Proxy(window,{
    get(target, p, receiver) {
        let result = Reflect.get(target,p,receiver)
        console.log(`get方法 对象:${JSON.stringify(target)} 属性:${p.toString()} 返回值:${result}`)
        return result
    },
    set:function (target, p, newValue, receiver) {
        console.log(`set方法 对象:${JSON.stringify(target)} 属性:${p.toString} 修改的值:${newValue}`)
        target[p] = newValue
    }
})

// 假设以下是抠出来的代码,然后调用href,但是href我们在window中没有定义,那么就会输出 返回值undefined,这时候我们就要去补这个href属性
// 根据调用的属性去当前目标网站根据控制台输出,拿到结果然后补到window身上,也可以使用脱环境的方式去拿到浏览器中的环境
console.log(window.href);

这时候window.href属性是undefined,我们就要去当前调试的目标网站将href属性抠出来补到window身上

注:如果还不了解的话到后面会有实战案例,尽情期待!

五. Proxy封装-1

function BrokerProxy(obj,objName){
    /*
    * obj: 需要代理的对象
    * objName: 代理对象的名字
    * 疑问点: 为什么还要传入对象的名字,因为不能直接通过对象来获取对象的名字,所以需要传进来
    * */
    return new Proxy(
        obj,
        {  // 拦截器方法
            get(target, prop, receiver){
                /*
                * target: 目标对象
                * prop: 被获取的属性名
                * receiver: 被代理后的对象
                * */
                let result = Reflect.get(target, prop, receiver) // 反射,执行原始的操作
                console.debug(`get方法: 对象: ${objName} 属性名: ${prop.toString()} 属性值: ${result}`)
                return result
            },
            set(target, prop, value){
                /*
                * target: 目标对象
                * prop: 被修改的属性名
                * value: 新属性值
                * */
                console.debug(`set方法: 对象: ${objName} 设置的属性名: ${prop.toString()} 设置的属性值: ${value}`)
                target[prop] = value
            }
        }
    )
}


let symbol = Symbol(123)


let window = {
    name:"雷点",
    true:2,
    [symbol]:"symbol123",
    age:123
}

window = BrokerProxy(window,'window')
console.log(window.name);
window.name = "javascript"

代码解读:

  1. 首先定义一个函数:BrokerProxy
  2. BrokerProxy函数接收两个参数:obj,objName
    1. obj: 需要被代理的对象
    2. objName: 被代理对象的名称
  3. 进入函数内部后,return new Proxy(obj,{get,set})  返回代理后的对象
    1. new Proxy(obj,{get,set})
      1. obj:当前被代理的对象
      2. get 拦截器方法:调用被代理对象的属性时触发
      3. set 拦截器方法:设置被代理对象的属性时触发
  4. 在get拦截器方法中接收三个参数:target,prop,receiver 
    1. target:目标对象
    2. prop: 被获取的属性名
    3. receiver:被代理后的对象
  5. 在get拦截器中的操作:
    1. 通过Reflect.get() 执行原始操作,返回被获取的属性
      1. let result = Reflect.get(target,prop,receiver)
    2. 日志输出:详细输出了,被调用的对象名,调用的属性名,属性值
      1. console.debug(`get方法: 对象: ${objName} 属性名: ${prop.toString()} 属性值: ${result}`)  
  6. 在set拦截器方法中接收三个参数:target,prop,value
    1. target:目标对象
    2. prop: 被设置的属性名
    3. value:新属性值你
  7. 在set拦截器中的操作
    1. 日志输出:详细输出了,被设置的对象名,设置的属性名,设置的值
      • console.debug(`set方法: 对象: ${objName} 设置的属性名: ${prop.toString()} 设置的属性值: ${value}`)
  8. 走到函数外部:
    • 定义了一个window对象:window = BrokerProxy(window,'window')
    • 在window对象加载后使用自定义封装的BrokerProxy函数对window进行代理,返回被代理后的window对象
    • 通过返回被代理的window对象获取name操作:console.log(window.name); 这时候就会触发 拦截器方法get方法,并且执行get方法里面的操作
    • 通过返回被代理的window对象设置name操作:window.name = "javascript"; 这时候就会触发 拦截器方法set,并且执行set方法里面的操作

六. Proxy封装-2

关于proxy封装的第二篇不在做多余的讲解,详细的注释已经在代码中写上,如果还不了解可以前往:Proxy() 构造函数 - JavaScript | MDN 进一步详细的了解

/*
* 代理器方法封装
* */

function getType(obj){
    return Object.prototype.toString.call(obj)
}

function BrokerProxy(obj,objName){
    /*
    * obj: 需要代理的对象
    * objName: 代理对象的名字
    * 疑问点: 为什么还要传入对象的名字,因为不能直接通过对象来获取对象的名字,所以需要传进来
    * */
    return new Proxy(
        obj,{
            // 用于拦截对象的 获取值的 操作
            get(target, prop, receiver){
                /*
                * target: 目标对象
                * prop: 被获取的属性名
                * receiver: 被代理后的对象
                * */
                let result;
                try{
                    result = Reflect.get(target, prop, receiver) // 反射,执行原始的操作
                    let type = getType(result)
                    if(result instanceof Object){
                        console.debug(`get方法[获取]: ${objName}.${prop.toString()} [属性值]: ${JSON.stringify(result)} [类型]:${type} `)
                        // 如果属性是一个对象的话就进行递归代理
                        result = BrokerProxy(result,`${objName}.${prop.toString()}`)
                    }else if(typeof result === "symbol"){
                        console.debug(`get方法[获取]: ${objName}.${prop.toString()} [属性值]: ${result.toString()}`)
                    }else{
                        console.debug(`get方法[获取]: ${objName}.${prop.toString()} [属性值]: ${result}`)
                    }
                }catch (e) {
                    console.error(`get方法[获取]: 对象: ${objName} [属性]:${prop.toString()} [错误信息]: ${e.message}`)
                }
                return result
            },

            // 用于拦截对象的 设置值的 操作
            set(target, prop, value){
                /*
                * target: 目标对象
                * prop: 被修改的属性名
                * value: 新属性值
                * */
                // 使用 Reflect.set
                try{
                     let success = Reflect.set(target, prop, value);
                     let type = getType(value)
                     if(value instanceof Object){
                         console.debug(`set方法[设置]: ${objName}.${prop.toString()} [设置的属性值]: ${JSON.stringify(value)} [类型]: ${type}`)
                     }else if (typeof value === "symbol"){
                          console.debug(`set方法[设置]: ${objName}.${prop.toString()} [设置的属性值]: ${value.toString()}`)
                     }else{
                         console.debug(`set方法[设置]: ${objName}.${prop.toString()} [设置的属性值]: ${value}`)
                     }
                }catch (e) {
                    console.error(`设置属性失败: ${objName}.${prop.toString()}`);
                }
            },

            // 用于拦截对象的 Object.getOwnPropertyDescriptor() 的操作
            // Object.getOwnPropertyDescriptor() 方法,用于获取属性描述符信息
            getOwnPropertyDescriptor(target, prop) {
                /*
                * target: 目标对象
                * prop: 需要返回属性名称的描述符的,属性名
                * getOwnPropertyDescriptor 方法必须返回一个 object 或 undefined。
                * */
                try {
                    result = Reflect.getOwnPropertyDescriptor(target, prop)
                    if (typeof result !== "undefined"){
                        console.debug(`getOwnPropertyDescriptor方法[获取属性描述符]: ${objName}.${prop.toString()} [获取属性描述符]: ${JSON.stringify(result)}`)
                        // result = BrokerProxy(result,`${objName}.${prop.toString()}`)
                    }else{
                        console.debug(`getOwnPropertyDescriptor方法[获取属性描述符]: ${objName}.${prop.toString()} [获取属性描述符]: ${JSON.stringify(result)} [提示]: ${prop.toString()}属性不存在`)
                    }
                }catch (e) {
                    console.error(`getOwnPropertyDescriptor方法[获取属性描述符]: 对象: ${objName} [获取属性描述符]:${prop.toString()} [错误信息]: ${e.message}`)
                }
                return result
            },

            // 用于拦截对象的 Object.defineProperty() 操作
            // Object.defineProperty() 方法,用于设置属性描述符信息
            defineProperty(target, prop, descriptor) {
               /*
                * target: 目标对象
                * prop: 需要修改属性描述符的,属性名称
                * descriptor: 需要重新定义属性描述符对象
                * */
                try {
                    let result = Reflect.defineProperty(target, prop, descriptor);
                    console.debug(`defineProperty方法[设置属性描述符]: ${objName}.${prop.toString()} [设置的属性描述符]: ${JSON.stringify(descriptor)}`)
                }catch (e) {
                    console.error(`defineProperty方法[获取属性描述符]: 对象: ${objName} [设置属性描述符]:${prop.toString()} [错误信息]: ${e.message}`)
                }
                return result
            },

            // 当代理的对象是一个函数时,返回的函数对象触发apply方法
            // 或当调用对象中的属性值是一个函数时,触发apply方法
            apply(target, thisArg, argArray) {
                /*
                * target: 目标对象
                * thisArg: 被调用时的上下文对象
                * argArray: 被调用时的参数数组,例如:[2,3]
                * */
                let result
                try {
                    result = Reflect.apply(target, thisArg, argArray)
                    // console.debug(`apply方法[调用函数]: ${objName} [函数参数]: ${argArray} [返回结果]: ${JSON.stringify(result)}`)
                    if (result instanceof Object){
                        console.debug(`apply方法[调用函数]: ${objName} [返回结果]: ${JSON.stringify(result)}`)
                    }else if (typeof result === "symbol"){
                        console.debug(`apply方法[调用函数]: ${objName} [返回结果]: ${result.toString()}`)
                    }else{
                        console.debug(`apply方法[调用函数]: ${objName} [返回结果]: ${result}`)
                    }
                }catch (e) {
                    console.error(`apply方法[调用函数]: ${objName} [函数参数]: ${argArray} [错误信息]: ${e.message}`)
                }
                return result
            },

            // construct 方法用于拦截 new 操作符
            construct(target, argArray, newTarget) {
                 /*
                * target: 目标对象
                * argArray: constructor 的参数列表。
                * newTarget: 代理后的对象
                * */
              let result
                try {
                    result = Reflect.construct(target, argArray, newTarget)
                    let type = getType(result)
                    console.debug(`construct方法[new ${objName}]: ${objName} [类型]: ${type}`)
                }catch (e) {
                    console.error(`construct方法[new ${objName}]: ${objName} [错误信息]: ${e.message}`)
                }
                return result
            },

            // 拦截对象自身属性的 delete 操作
            // 当对对象某个属性进行删除: delete user.name 时触发
            deleteProperty(target, prop) {
                /*
                * target: 目标对象
                * prop: 要进行delete操作的属性名
                * */
                let result = Reflect.deleteProperty(target,prop)
                console.debug(`deleteProperty方法[删除]: delete ${objName}.${prop} [是否可删除]: ${result}`)
                return result
            },

            // 当对自身属性进行遍历时触发
            ownKeys(target) {
                /*
                * target: 目标对象
                * */
                let result = Reflect.ownKeys(target)
                console.debug(`ownKeys方法[正在属性遍历的对象]: ${objName}`)
                return result
            },

            // 当获取代理对象的原型时,触发
            // 对象.__proto__时触发
            // 或 Object.getPrototypeOf 时触发
            getPrototypeOf(target) {
                /*
                * target: 目标对象
                * */
                let result = Reflect.getPrototypeOf(target)
                console.debug(`getPrototypeOf方法[获取原型对象] ${objName}.__proto__`)
                return result
            },

            // 当Object.setPrototypeOf设置对象原型时,触发
            setPrototypeOf(target, value) {
                /*
                * target: 目标对象
                * value: 要设置的值
                * */
                let result = Reflect.setPrototypeOf(target,value)
                console.debug(`setPrototypeOf方法 [设置原型对象] ${objName}.__proto__ = [设置内容]: ${value}`)
                return result
            }
        }
    )
}


let window = {
    name:"雷点",
    info:{
        name:"小红",
        age:10,
        info2:{
            name:"红",
            age:10,
        }
    },
    add:function (a, b) {
        return a + b
    }
}

// 代理的使用: 需要在window对象加载后使用
window = BrokerProxy(window,'window')

console.log("==============get方法===================")

window.name
window.info.name
window.info.info2.name

console.log("==============set方法===================")

window.name = "javascript"
window.clas = {name:"小星星"}
window.clas
window.clas.name

console.log("==============getOwnPropertyDescriptor方法===================")

// 获取window对象的name属性操作符时,触发getOwnPropertyDescriptor拦截器
// 当获取的是一个不存在的属性时,得到的属性描述符结果是一个: undefined
// Object.getOwnPropertyDescriptor(window,"aaaaaaaaaaa")

// 当获取的是存在的属性时,得到的属性描述符结果是对应的描述符信息:  {"value":"javascript","writable":true,"enumerable":true,"configurable":true}
Object.getOwnPropertyDescriptor(window,"name")


console.log("==============defineProperty方法===================")

// 当window给name属性设置属性描述符的时候触发 defineProperty拦截器
Object.defineProperty(window,"name",{
    writable:false,
    configurable:true,
    value:"设置属性值"
})

console.log("==============apply方法===================")

// 情况1: 当调用的是window对象中的函数时,触发apply方法
window.add(10,20)

// 情况2: 单独代理的是一个函数时,调用函数,触发apply方法
function add2(a,b) {
    return a * b
}

add2 = BrokerProxy(add2,"add2")
add2(100,200)


function add3(a,b) {
    return a
}
add3 = BrokerProxy(add3,"add3")
add3({"name":"小红"},200)
add3(Symbol(),200)



console.log("==============construct方法===================")

function LeiDian() {

}

LeiDian = BrokerProxy(LeiDian,"LeiDian")
l = new LeiDian()



console.log("================deleteProperty=================")

// 触发代理对象的deleteProperty拦截器
// 如果属性描述符configurable是false那么就不能delete操作
delete window.name


console.log("================ownKeys=================")

// 触发代理对象的ownKeys拦截器
// 获取对象所有自身可枚举属性组成的一个数组
console.log(Object.keys(window));

console.log("================getPrototypeOf=================")


window.__proto__
Object.getPrototypeOf(window)

console.log("================setPrototypeOf=================")

let aaaaa = {

}

aaaaa = BrokerProxy(aaaaa,"aaaaa")
Object.setPrototypeOf(aaaaa, {});

Proxy实战

首先抠出来的js代码,我提前导出了出值的方法

将代码拖到浏览器进行执行,可以看到是可以拿到结果的并且是动态的,由于浏览器自带的环境所以浏览器运行是没有报错的

接下来拖到本地node环境中执行,报错了一个window未定义,我们在顶部定义一下:

window = globalThis;

补了windnow之后继续运行,又报userAgent未定义

这时候发现都是每次运行才报错什么什么未定义,能不能直接知道他做了什么我们直接去补呢,这时候就需要使用到proxy代理,

将window挂到代理上后,运行就能看到下面的js调用了那些方法,做了什么,然后哪里undefined就补哪里

最后这是我补的

然后运行和网站的对比,这个导出的函数生成的值不是固定的,长度一样

还有如果你正在调试某个网站,根据proxy输出的方法或者函数不懂的话,可以在当前网站的控制台调试输出,或者百度,例如:window.setTimeout等......

### JavaScript Proxy 代理实现原理 在 ES6 中引入了 `Proxy` 构造函数,允许开发者创建一个对象的代理版本。这个代理可以在访问目标对象之前拦截并自定义其基本操作(如获取属性、设置属性等)。这种机制对于调试工具、数据绑定库以及逆向工程等领域非常有用。 当使用 `new Proxy(target, handler)` 创建一个新的代理实例时: - **target**: 被代理的目标对象。 - **handler**: 定义了一系列陷阱方法(trap methods),这些方法会在特定情况下被调用来捕获对原始对象的操作行为[^2]。 #### 基本用法示例 ```javascript const target = { message: 'Hello' }; // Handler contains traps that intercept operations on the proxy. const handler = { get(obj, prop) { console.log(`Getting ${prop}`); return obj[prop]; }, set(obj, prop, value) { console.log(`Setting ${prop} to ${value}`); obj[prop] = value; return true; // Indicate success } }; // Create a new proxy object with specified behavior defined by handlers. const proxyObj = new Proxy(target, handler); console.log(proxyObj.message); // Output: Getting message\n Hello proxyObj.message = 'Hi'; // Output: Setting message to Hi ``` 这段代码展示了如何利用 `get` 和 `set` 方法来监控对 `message` 属性的读取和写入操作。 ### JS逆向中的应用案例 在一个典型的逆向场景里,比如解析拼多多(PDD)平台上的某些保护措施时,可以通过构建复杂的 `Proxy` 结构模拟浏览器环境,从而绕过客户端验证逻辑。具体来说就是通过链式的 `Proxy` 设计模式模仿真实的 DOM 环境,使得服务器端下发给前端执行的安全脚本能正常运行于本地环境中[^1]。 另外,在处理像有道词典这样的服务时,如果遇到基于 MD5 的签名算法作为请求的一部分,则可能需要深入研究 API 请求流程,并尝试重构类似的哈希计算过程以生成有效的查询字符串[^4]。 ### 高级技巧:Chainable Proxies 为了更精确地控制整个应用程序的状态变化或者更好地伪装成真正的 Web 浏览器,有时会采用多层嵌套的方式建立多个相互关联的 `Proxy` 实体形成链条结构。例如下面的例子显示了怎样为窗口(Window)对象及其子组件设立一系列代理节点,以便全面接管页面内的交互事件流[^3]: ```javascript let window2 = { name: "CC11001100", location: { document:{ all:[{name: ""}], all2: false, all3(){} } } }; function chainedProxy(obj, keyPath){ var keys = keyPath.split('.'); function createProxies(currentObject, remainingKeys){ if (!remainingKeys.length) return currentObject; let nextKey = remainingKeys.shift(); let proxiedChild = new Proxy( currentObject[nextKey], { get(target, property){ console.log(`${keyPath}.${property} was accessed`); return Reflect.get(target, property); }, set(target, property, value){ console.log(`${keyPath}.${property} is being set to`, value); return Reflect.set(target, property, value); } }); currentObject[nextKey] = proxiedChild; return createProxies(proxiedChild, remainingKeys); } return createProxies(obj, [...keys]); } window2 = chainedProxy(window2, 'window2'); ``` 此段代码片段说明了如何递归地遍历指定路径下的每一个成员变量,并为其分配相应的监视功能,最终达到全方位跟踪的目的。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值