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

news2024/10/7 3:21:36

博客文章:深入理解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代码的行为,还能够在开发过程中提供更多的灵活性和安全性。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分数的表示和运算方法fractions.Fraction()

【小白从小学Python、C、Java】 【考研初试复试毕业设计】 【Python基础AI数据分析】 分数的表示和运算方法 fractions.Fraction() 选择题 以下代码三次输出的结果分别是? from fractions import Fraction a Fraction(1, 4) print(【显示】a ,a) b Fraction(1, 2…

免费的鼠标连点器电脑版教程!官方正版!专业鼠标连点器用户分享教程!2024最新

电脑技术的不断发展,许多用户在日常工作和娱乐中,需要用到各种辅助工具来提升效率或简化操作,而电脑办公中,鼠标连点器作为一种能够模拟鼠标点击的软件,受到了广大用户的青睐。本文将为大家介绍一款官方正版的免费鼠标…

C++_STL---list

list的相关介绍 list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。 list的底层是带头双向循环链表结构,链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。…

WAIC | 上海人形机器人创新中心 | 最新演讲 | 详细整理

前言 笔者看了7月4号的人形机器人与具身智能发展论坛的直播,并在7月5日到了上海WAIC展会现场参观。这次大会的举办很有意义,听并看了各家的最新成果,拍了很多照片视频,部分演讲也录屏了在重复观看学习 稍后会相继整理创立穹彻智…

使用RAID与LVM磁盘阵列技术

前言:本博客仅作记录学习使用,部分图片出自网络,如有侵犯您的权益,请联系删除 目录 一、RAID磁盘冗余阵列 1、部署磁盘整列 2、损坏磁盘阵列及修复 3、磁盘阵列备份盘 4、删除磁盘阵列 二、LVM逻辑卷管理器 致谢 一、RAID…

linux中可执行文件在运行过程中为什么不能拷贝覆盖

对于一个普通的文件,假如有两个文件,分别是file和file1,我们使用 cp file1 file的方式使用file1的内容来覆盖file的内容,这样是可以的。 但是对于可执行文件来说,当这个文件在执行的时候,是不能通过cp的方…

Python 算法交易实验76 QTV200日常推进

说明 最近实在太忙, 没太有空推进这个项目,我想还是尽量抽一点点时间推进具体的工程,然后更多的还是用碎片化的时间从整体上对qtv200进行设计完善。有些结构的问题其实是需要理清的,例如: 1 要先基于原始数据进行描述…

【ROS2】初级:客户端-编写一个简单的服务和客户端(Python)

目标:使用 Python 创建并运行服务节点和客户端节点。 教程级别:初学者 时间:20 分钟 目录 背景 先决条件 任务 1. 创建一个包2. 编写服务节点3. 编写客户端节点4. 构建并运行 摘要 下一步 相关内容 背景 当节点通过服务进行通信时&#xff0c…

【机器学习】机器学习重塑广告营销:精准触达,高效转化的未来之路

📝个人主页🌹:Eternity._ 🌹🌹期待您的关注 🌹🌹 ❀目录 📒1. 引言📙2. 机器学习基础与广告营销的结合🧩机器学习在广告营销中的核心应用领域🌹用…

将大型语言模型模块化打造协作智能体

B UILDING C OOPERATIVE E MBODIED A GENTS MODULARLY WITH L ARGE L ANGUAGE M ODELS 论文链接: https://arxiv.org/abs/2307.02485https://arxiv.org/abs/2307.02485 1.概述 在去中心化控制及多任务环境中,多智能体合作问题因原始感官观察、高昂…

穿梭印度风情记:维乐 Angel Revo Halo坐垫,让每一寸旅程闪耀光辉!

想象骑乘在印度的万花筒世界中,斑斓色彩与悠久历史交织,每一转轮都是对神秘东方的深刻探索。在这样的骑行之旅中,维乐Angel Revo Halo坐垫不仅是你的坐骑上的宝石,更是舒适与探险的完美媒介。    探索印度的色彩与灵魂&#x…

每日一题~oj(贪心)

对于位置 i来说,如果 不选她,那她的贡献是 vali-1 *2,如果选他 ,那么她的贡献是 ai. 每一个数的贡献 是基于前一个数的贡献 来计算的。只要保证这个数的前一个数的贡献是最优的,那么以此类推下去,整体的val…

【项目设计】负载均衡式——Online Judge

负载均衡式——Online Judge😎 前言🙌Online Judge 项目一、项目介绍二、项目技术栈三、项目使用环境四、项目宏观框架五、项目后端服务实现过程1、comm模块设计1.1 Log.hpp实现1.2 Util.hpp实现 2、compiler_server 模块设计2.1compile.hpp文件代码编写…

【QT】容器类控件

目录 概述 Group Box 核心属性 Tab Widget 核心属性 核心信号 核心方法 使用示例: 布局管理器 垂直布局 核心属性 使用示例: 水平布局 核⼼属性 (和 QVBoxLayout 属性是⼀致的) 网格布局 核心属性 使用示例: 示例&#x…

【C++ OpenCV】机器视觉-二值图像和灰度图像的膨胀、腐蚀、开运算、闭运算

原图 结果图 //包含头文件 #include <opencv2/opencv.hpp>//命名空间 using namespace cv; using namespace std;//全局函数声明部分//我的腐蚀运算 Mat Erode(Mat src, Mat Mask, uint32_t x0, uint32_t y0) {uint32_t x 0, y 0;Mat dst(src.rows, src.cols, CV_8U…

设计模式之状态机模式

一、状态机模式介绍 状态机模式&#xff08;State Machine Pattern&#xff09;是一种用于描述对象行为的软件设计模式&#xff0c;属于行为型设计模式。在状态机模式中&#xff0c;对象的行为取决于其内部状态&#xff0c;并且在不同的状态下&#xff0c;对象可能会有不同的行…

RAG 案框架(Qanything、RAGFlow、FastGPT、智谱RAG)对比

各家的技术方案 有道的QAnything 亮点在&#xff1a;rerank RAGFLow 亮点在&#xff1a;数据处理index 智谱AI 亮点在文档解析、切片、query改写及recall模型的微调 FastGPT 优点&#xff1a;灵活性更高 下面分别按照模块比较各框架的却别 功能模块QAnythingRAGFLowFastG…

【手写数据库内核组件】01 解析树的结构,不同类型的数据结构组多层的链表树,抽象类型统一引用格式

不同类型的链表 ​专栏内容&#xff1a; postgresql使用入门基础手写数据库toadb并发编程 个人主页&#xff1a;我的主页 管理社区&#xff1a;开源数据库 座右铭&#xff1a;天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物. 文章目录 不同类型…

Day05-04-持续集成总结

Day05-04-持续集成总结 1. 持续集成2. 代码上线目标项目 1. 持续集成 git 基本使用, 拉取代码,上传代码,分支操作,tag标签 gitlab 用户 用户组 项目 , 备份,https,优化. jenkins 工具平台,运维核心, 自由风格工程,maven风格项目,流水线项目, 流水线(pipeline) mavenpom.xmlta…

Android 10年,35岁,该往哪个方向发力

网上看到个网友发的帖子&#xff0c;觉的这个可能是很多开发人员都会面临和需要思考的问题。 不管怎样&#xff0c; 要对生活保持乐观&#xff0c;生活还是有很多的选择和出路的。 &#xff08;内容来自网络&#xff0c;不代表个人观点&#xff09; 《Android Camera开发入门》…