深入理解JS逆向代理与环境监测

博客文章:深入理解JS逆向代理与环境监测

1. 引言

首先要明确JavaScript(JS)在真实网页浏览器环境和Node.js环境中有很多使用特性的区别。尤其是在环境监测和对象原型链的检测方面。本文将探讨如何使用JS的代理(Proxy)模式来手动补充环境,Node环境和浏览器this环境,以及如何通过原型链检测来增强代码的安全性以及在。

2. 代理补环境

代理是ES6引入的一种新特性,它允许你定义对象的行为,例如属性的读取和设置。以下是一个简单的代理补环境的实现:

// 代理补环境(缺啥补啥)
function vmProxy(object, objName) {
    // 创建代理对象,捕获对对象的访问和赋值操作
    return new Proxy(object, {
        get: function(target, property, receiver) {
            // 打印属性访问信息
            console.log(objName, "get: ", property, target[property]);
            // 返回目标对象的属性值
            return target[property];
        },
        set: function(target, property, value) {
            // 打印属性赋值信息
            console.log(objName, "set: ", property, value);
            // 使用Reflect.set实现属性赋值
            return Reflect.set(...arguments);
        }
    });
}

// 模拟浏览器环境
navigator = {
    userAgent: 'qh'
};
document = {};

// 使用代理来增强navigator和document对象
navigator = vmProxy(navigator, 'navigator');
document = vmProxy(document, 'document');

// 测试代理效果
console.log(navigator.userAgent); // 应输出代理访问信息
console.log(navigator.platform); // 将触发代理的get方法
console.log(document.cookie); // 将触发代理的get方法
console.log(document.createElement); // 将触发代理的get方法

3. Node环境与浏览器this环境监测

在Node.js中,global对象是全局对象,而在浏览器中,window是全局对象。检测这些环境可以通过以下方式实现:

// 确保window指向全局对象
window = globalThis;
// 补充window对象的方法
window['addEventListener'] = function() {};
// 初始化window的navigator和document属性
window['navigator'] = {};
window['document'] = {};

// 使用代理来增强window对象
// 错误写法1:
// window = vmProxy(window, 'window'); // 代理器会影响对象本身的指向,代理window对象本身并不是错误,但关键在于不应该覆盖原有的window对象。代理可以用来增强或监测window对象的行为,但不应该改变其身份。
// 错误写法2:
// window ={'addEventListener': function() {},'navigator':{},'document' {}}; // 直接赋值window为一个新对象会覆盖原有的window对象,这会丢失所有原有的全局属性和方法,包括继承来的属性。
// 错误写法3 重新赋值会影响指向  将window赋值为一个空对象同样会覆盖原有的window对象,导致丢失所有属性和方法。
// window={}

// 测试eval.call方法是否正确指向window
!function (){
    function test(){
        // 测试eval.call的this指向
        console.log(eval["call"](undefined, this) === window); // 应输出true
    }
    test.apply(null);
}();

这段代码的功能是测试eval.call方法的this指向是否正确。在JavaScript中,eval函数可以计算一个字符串表达式的值。当使用call方法调用eval时,可以指定this的值。在这个例子中,想要测试eval.call是否能够正确地将this指向window对象得到true的结果证明现在node环境和浏览器环境是一致的。

下面是对上面代码的详细拆分和解读,如果已经理解就跳过。

错误写法1解释:

// 错误写法1:
// window = vmProxy(window, 'window'); // 代理器会影响对象本身的指向

这行代码是错误的,因为window对象是全局对象,其原型链上有很多内置属性和方法。将window重新赋值为vmProxy的返回值会切断window与原有原型链的联系,导致丢失原有的全局属性和方法。此外,这行代码试图将window对象自身作为代理的目标,这在逻辑上是有问题的,因为window对象本身不应该被代理。如下图高亮紫色是原有全局属性是继承来的,
在浏览器中打印window对象时,你可能会注意到属性颜色的差异,这通常是由于浏览器的开发者工具中的颜色编码。在Chrome的开发者工具中,全局对象window的属性通常分为两类:

原有全局属性:这些属性是window对象直接定义的,通常是浅色显示,表示它们是window对象的自有属性,而不是通过原型链继承的。

继承来的属性:这些属性是window对象通过原型链从其原型Window.prototype继承的,通常会以高亮紫色显示,以区分自有属性。

例如,navigator对象是window对象的一个自有属性,而navigator对象本身继承自Navigator的属性。在浏览器的控制台中打印window对象时,你会看到类似下面的结构:

Window {
  window: Window { ... },
  self: Window { ... },
  document: document,
  name: "name",
  location: Location { ... },
  history: History { ... },
  navigator: Navigator { ... }, // 继承自 Navigator 的属性将显示为高亮紫色
  // ... 其他自有属性和继承属性
}

在这个例子中,document、location、history等是window对象的自有属性,而navigator对象的属性,如userAgent,是继承自Navigator的属性。

代码示例:
以下是如何在控制台中查看这些属性的示例代码:

console.dir(window); // 打印window对象及其属性

使用console.dir可以打印出对象的详细信息,包括原型链上的属性。
在这里插入图片描述

在这里插入图片描述

错误写法2解释:

// 错误写法2:
// window ={'addEventListener': function() {},'navigator':{},'document' {}}; // 直接赋值window为一个新对象会覆盖原有的window对象,这会丢失所有原有的全局属性和方法,包括继承来的属性。

这行代码同样是错误的,因为它试图将window重新定义为一个具有单个属性addEventListener的对象,这个属性的值是一个空字符串。正确的做法是为window添加方法,而不是重新定义window对象。

错误写法3解释:

// 错误写法3 重新赋值会影响指向  将window赋值为一个空对象同样会覆盖原有的window对象,导致丢失所有属性和方法。
// window = {};

这行代码是错误的,因为它将window重新赋值为一个空对象,这会覆盖原有的全局window对象,导致所有原有的全局属性和方法丢失。

衡量错误的标准:

在JS逆向中,衡量错误的标准是在Node环境中是否成功模拟了浏览器环境。正确的做法应该是在不破坏原有window对象的基础上,补充或修改其属性和方法,以模拟浏览器环境。

加代理正确的做法1:

// 确保window指向全局对象
window = globalThis;

// 补充window对象的方法
if (typeof window.addEventListener === 'undefined') {
    window['addEventListener'] = function() {};
}

// 初始化window的navigator和document属性,但不要覆盖原有的属性
if (!window.navigator) {
    window['navigator'] = {};
}
if (!window.document) {
    window['document'] = {};
}

// 使用代理来增强window对象,但不要重新赋值window
window = vmProxy(window, 'window');

在这个修正的代码中,我们首先检查window对象上是否已经有addEventListener方法,如果没有,我们才添加它。同样,我们检查navigatordocument是否存在,如果不存在,我们才初始化它们。最后,我们使用vmProxy来增强window对象,而不是重新赋值它。

加代理正确的做法2:

// 创建window的代理,而不是重新赋值window
const originalWindow = window;
const proxiedWindow = new Proxy(originalWindow, {
    get(target, property, receiver) {
        if (property === 'navigator') {
            console.log('Accessing navigator');
        }
        return Reflect.get(target, property, receiver);
    },
    set(target, property, value, receiver) {
        console.log(`Setting ${property} to ${value}`);
        return Reflect.set(target, property, value, receiver);
    }
});

// 使用代理对象进行操作,而不是直接操作window
proxiedWindow.navigator.userAgent; // 这将触发get陷阱,并打印日志

创建了一个window的代理,而不是直接修改window对象。这样,我们可以在不改变window对象本身的情况下,监测和增强其行为。

测试eval.call方法:

!function (){
    function test(){
        // 测试eval.call的this指向
        console.log(eval["call"](undefined, this) === window); // 应输出true
    }
    test.apply(null);
}();

这个测试函数test使用apply方法将this指向null,然后通过eval.callthis指向window。如果eval.call正确地将this指向了window,那么console.log应该输出true

4. 原型链检测

原型链是JS中实现继承的关键机制。## 4. 原型链检测的深入分析

原型链是JavaScript中实现继承的核心机制。每个JavaScript对象都有一个原型对象,这个原型对象可以是另一个对象或者null。当访问一个对象的属性时,如果该属性在对象上不存在,JavaScript引擎会沿着原型链向上查找,直到找到该属性或到达原型链的末端(null)。

代码拆分

定义构造函数和原型属性
// 定义构造函数Navigator
Navigator = function Navigator(){};
// 在Navigator.prototype上定义userAgent属性
Object.defineProperty(
    Navigator.prototype,
    'userAgent',
    {
        set: undefined, // 不允许赋值
        enumerable: true, // 可枚举
        configurable: true, // 可配置
        get: function() {
            return "Custom UserAgent"; // 自定义getter函数
        } // 自定义getter
    }
);
// 创建navigator对象,其原型指向Navigator.prototype
navigator = {};
navigator.__proto__ = Navigator.prototype;

// 检测navigator的属性描述符
console.log(Object.getOwnPropertyDescriptors(navigator)); // 应输出undefined
console.log(Object.getOwnPropertyDescriptors(Navigator.prototype)); // 显示实际的属性描述符

截图示例:在浏览器的控制台中,可以看到Navigator构造函数和其prototype上的userAgent属性定义。

创建并链接自定义navigator对象
// 创建navigator对象,其原型指向Navigator.prototype
var navigator = {};
Object.setPrototypeOf(navigator, new Navigator()); // 更现代的方法来设置原型

截图示例:在控制台中,通过Object.getPrototypeOf(navigator)可以看到navigator的原型现在指向Navigator.prototype

属性描述符检测
// 检测navigator的属性描述符
console.log(Object.getOwnPropertyDescriptors(navigator)); // 应输出undefined,因为navigator上没有直接定义userAgent属性
console.log(Object.getOwnPropertyDescriptors(Navigator.prototype)); // 显示实际的属性描述符,包括userAgent的定义

截图示例:在控制台中,Object.getOwnPropertyDescriptors的调用结果展示了Navigator.prototype上的userAgent属性描述符。

JS逆向原型链相关知识

在JavaScript逆向工程中,了解原型链对于分析和修改代码行为至关重要。以下是一些与原型链检测相关的逆向知识点:

  1. 原型链遍历:逆向工程师可以通过遍历对象的原型链来查找对象的来源和构造方式。
  2. 原型链污染:通过修改对象的原型链,可以引入新的行为或属性,这在某些情况下可以用于绕过安全限制。
  3. 构造函数欺骗:通过修改构造函数的prototype,可以改变通过该构造函数创建的所有新对象的行为。
  4. 属性拦截:使用Proxy对象,可以在访问或设置属性时进行拦截和自定义行为,这可以用来模拟或监测对象的行为。
  5. 环境检测:通过检测对象的原型链,可以判断代码运行在何种环境中(浏览器或Node.js),并据此调整代码行为。

通过这些技术,逆向工程师可以深入了解和操纵JavaScript代码的运行时行为,实现代码审计、安全测试或功能增强。然而,这些技术也应谨慎使用,以避免潜在的安全风险和代码维护问题。

5. 环境监测点案例

以下是一些Node和浏览器环境监测点的案例:

  • 监测全局对象类型typeof global !== 'undefined' ? 'node' : 'browser'
  • 监测Node.js特有的模块require.main === module
  • 监测浏览器特有的对象typeof window !== 'undefined' && !!window.document
  • 监测浏览器的BOM和DOMtypeof document !== 'undefined' && !!document.createElement
  • 监测环境支持的ES6特性'startsWith' in String.prototype

7. 高级监测点:使用evalProxy进行环境监测

在JavaScript中,eval函数允许你执行字符串中的代码。然而,使用eval通常被认为是不安全的,因为它可以执行任意代码。但是,在某些情况下,我们可以通过修改eval的行为来增强环境监测。以下是一个示例代码,展示了如何重写eval.call方法,以监测和区分代码执行环境:

// 重写eval.call方法以监测执行环境
eval['call'] = function (){
    debugger; // 启动调试模式,便于开发者调试
    if (arguments[1].toString() === '[object Window]'){
        debugger; // 再次启动调试模式,如果this指向Window对象
        // 如果调用环境是浏览器的window对象,则直接返回window
        return window;
    } else {
        // 否则,执行原始的eval函数
        return eval(arguments);
    }
};

代码解释

  • eval['call']: 我们通过eval['call']访问eval函数的call方法,并对其进行重写。
  • debugger: 这是一个调试语句,当代码执行到这里时,如果正在调试模式下,执行会暂停,允许开发者检查当前的执行环境和变量状态。
  • arguments[1].toString(): arguments对象包含了调用函数时传入的所有参数。在这里,我们检查arguments[1](即this的值),并使用toString()方法获取它的类型描述。
  • '[object Window]': 这是一个特定的字符串,用来检测this是否指向浏览器的window对象。
  • return window: 如果检测到thiswindow对象,我们直接返回window,这样eval.call调用的结果将总是指向全局的window对象,而不是局部作用域中的this

使用场景

这种技术可以用于确保在执行动态代码时,this总是指向预期的全局对象,从而避免潜在的作用域问题。此外,它还可以用于调试和测试,帮助开发者更好地理解代码在不同环境下的行为。

7.与局部加密方法对象导出到全局对象的区别

使用代理补环境和原型链检测是一种在运行时动态地修改对象的行为和结构的方法。与之相比,将局部加密方法对象导出到全局对象是一种静态的修改,通常在代码的编写阶段就已经确定。代理补环境提供了一种灵活的方式来监测和修改对象的行为,而局部加密方法对象的导出则是一种更静态、更难以在运行时改变的方法。

结语

通过本文的探讨,我们了解到了JS代理的强大功能以及如何使用它来监测和增强JS运行环境。同时,我们也学习了如何通过原型链检测来提高代码的安全性。这些技术不仅能够帮助开发者更好地理解和控制JS代码的行为,还能够在开发过程中提供更多的灵活性和安全性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/780095.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024亚太杯中文赛数学建模B题word+PDF+代码

2024年第十四届亚太地区大学生数学建模竞赛(中文赛项)B题洪水灾害的数据分析与预测:建立指标相关性与多重共线性分析模型、洪水风险分层与预警评价模型、洪水发生概率的非线性预测优化模型,以及大规模样本预测与分布特征分析模型 …

算法011:最大连续的1的个数

最大连续的1的个数. - 备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/max-consecutive-ones-iii/ 乍一看,这道题很奇怪,什么叫最多翻转k个0&a…

自动控制:反馈控制

自动控制:反馈控制 反馈控制(Feedback Control)是一种在控制系统中通过测量输出信号,并将其与期望信号进行比较,产生误差信号,再根据误差信号调整输入来达到控制目标的方法。反馈控制是自动控制系统中最常…

揭秘Conda:Python开发者必备的包管理神器

conda 简介 Conda 是一个开源的包管理系统和环境管理系统,用于安装和管理软件包以及创建和维护不同的软件环境。 它最初是为 Python 语言设计的,但现在已经支持多种编程语言,包括 R、Ruby、Lua、Scala 等。 1、Anaconda:是一个…

HCIE之IPV6和OSPFv6(十四)

IPV6 1、IPv6基础1.1 Ipv6地址静态配置、Eui 641.1.1 Ipv6地址静态配置1.1.2、Ipv6地址计算总结1.1.2.1、IEEE eui 64计算1.1.2.1.1、作用1.1.2.1.2、计算方法1.1.2.1.3、计算过程 1.1.2.2、被请求加入的组播组地址计算(三层)1.1.2.2.1、 作用1.1.2.2.2、…

在pycharm里如何使用Jetbrains AI Assistant

ai assistant激活成功后,如图 ai assistant渠道:https://web.52shizhan.cn/activity/ai-assistant 在去年五月份的 Google I/O 2023 上,Google 为 Android Studio 推出了 Studio Bot 功能,使用了谷歌编码基础模型 Codey,Codey 是…

浪潮信息元脑服务器支持英特尔®至强®6能效核处理器 展现强劲性能

如今,服务器作为数字经济的核心基础设施,正面临着前所未有的挑战和机遇。作为服务器领域的领军企业,浪潮信息始终站在行业前沿,不断推陈出新,以满足客户日益增长的需求。近日,浪潮信息再次展现技术实力&…

从零开始学习网络安全渗透测试之Linux基础篇——(六)Linux网络及防火墙配置

从零开始学习网络安全渗透测试之Linux基础篇 第六章 Linux网络及防火墙配置 1、Linux网络配置文件 查看第一张网卡的网卡信息: [rootlocalhost yum.repos.d]# cat vi /etc/sysconfig/network-scripts/ifcfg-ens33 cat: vi: 没有那个文件或目录TYPEEthernet PR…

【高中数学/基本不等式】已知:x,y皆为正实数,且满足2x+y=1 求:1/x+1/y的最小值?

【问题】 已知:x,y皆为正实数,且满足2xy1 求:1/x1/y的最小值? 【解答】 解法一:(基本不等式法) 这个问题貌似无从下手,实际把分子的1替换成2xy就出现我们熟悉的适合基本不等式发…

数据自动备份方法分享!

现在很多朋友对于第三方软件颇为青睐,因为它们具备许多电脑自带备份工具所不具备的功能。例如,自动备份数据的需求。尽管你已经备份了电脑数据,但日常使用中数据常会增加,你可能无暇顾及每天的备份工作。因此,使用数据…

alibaba EasyExcel 简单导出数据到Excel

导入依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>4.0.1</version> </dependency> 1、alibaba.excel.EasyExcel导出工具类 import com.alibaba.excel.EasyExcel; import …

c++ primer plus 第15章友,异常和其他: 15.2.1 嵌套类和访问权限系

c primer plus 第15章友&#xff0c;异常和其他&#xff1a; 15.2.1 嵌套类和访问权限系 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;c primer plus 第15章友&#xff0c;异常和其他&#xff1a; 15.2.1 嵌套类和…

Kubernetes分享

幂等性(Idempotency) 介绍 简单来说&#xff0c;幂等性幂等性(Idempotency)是计算机科学中的一个重要概念&#xff0c;特别是在分布式系统和网络应用中。指的是某个操作可以重复执行多次&#xff0c;但其结果是相同的&#xff0c;不会因为多次执行而改变系统的状态。 https://…

rkmpp移植与测试

一、mpp交叉编译 MPP(Media Process Platform )是Rockchip提供的一款硬件编解码媒体处理软件平台&#xff0c;适用于Rockchip芯片系列。它屏蔽了有关芯片的复杂底层处理&#xff0c;屏蔽了不同芯片的差异&#xff0c;为使用者提供了一组MPI统一接口。如果想达到最好的效果&…

打造属于自己的脚手架工具并发布到npm仓库

一、创建项目 使用 npm init -y 创建项目创建项目入口文件 index.js在 package.json 中添加 bin 字段使用 npm link 命令将文件映射至全局&#xff0c;使可以在本地测试 zp 命令 // "zp" 为用于全局执行脚手架的命令&#xff0c;vue-cli中使用的是vue命令 "bi…

QT滑块图片验证程序

使用QT实现滑块验证程序&#xff0c;原理是画个图片&#xff0c;然后在图片上画个空白区域&#xff0c;再画个滑块图片。 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widg…

物联网的技术和应用有哪些?

随着科技的飞速发展&#xff0c;物联网已经成为连接世界的重要纽带&#xff0c;塑造着我们未来的生活。我们一起深入探索物联网的前沿技术和前瞻性应用&#xff0c;一窥未来的可能性。 获取物联网解决方案&#xff0c;YesPMP平台一站式物联网开发服务。 提示&#xff1a;智慧家…

Google Earth Engine(GEE)——ui.Panel添加到地图上

结果 函数 ui.root.add(widget) 将一个widget添加到根面板上。 返回根面板。 参数。 widget&#xff08;ui.Widget&#xff09;。 要添加的widget。 返回&#xff1a; ui.Panel 代码 //label var label ui.Label({ value: "text label", style: {fontSi…

java 公共字段填充

公共字段填充 1、mybatis-plus2、mybatis 使用注解加aop2.1 自定义注解2.2 自定义切面类2.3 在mapper上添加上自定义的注解 1、mybatis-plus 通过在类上使用如下的注解 TableField(fill FieldFill.INSERT) 是 MyBatis-Plus 中的注解&#xff0c;用于自动填充字段的值。MyBat…

上海外贸建站公司wordpress模板推荐

Sora索啦高端制造业wordpress主题 红色高端制造业wordpress主题&#xff0c;适合外贸企业出海建独立站的wordpress模板。 https://www.jianzhanpress.com/?p5885 Yamal外贸独立站wordpress主题 绿色的亚马尔Yamal外贸独立站wordpress模板&#xff0c;适用于外贸公司建独立站…