JS逆向之浏览器补环境详解

“补浏览器环境”是JS逆向者升职加薪的必备技能,也是工作中不可避免的操作。

为了让大家彻底搞懂 “补浏览器环境”的缘由及原理,本文将从以下四个部分进行描述:

  1. 什么是补环境?
  2. 为什么要补环境?
  3. 怎么补环境?
  4. 补环境实战
  5. 补环境框架成品源码

一:什么是 “补浏览器环境”?

浏览器环境: 是指 JS代码在浏览器中的运行时环境,它包括V8自动构建的对象(即ECMAScript的内容,如Date、Array),浏览器(内置)传递给V8的操作DOM和BOM的对象(如document、navigator);

Node环境:是基于V8引擎的Js运行时环境,它包括V8与其自己的内置API,如fs,http,path;

Node环境浏览器环境 的异同点可以简单概括如图:

所以我们所说的 “补浏览器环境” 其实是补浏览器有 而Node没有的环境,即 补BOM和DOM的对象;

二:为什么要 “补浏览器环境”?

对于逆向老手而言,“补环境” 这个词不会陌生,当我们每次把辛辛苦苦扣出来的 “js加密算法代码”,并且放在浏览器环境中能正确执行后,就需要将它放到Node环境 中去执行,而由于Node环境浏览器环境之间存在差异,会导致部分JS代码在浏览器中运行的结果 与在node中运行得到的结果不一样,从而影响我们最终逆向成果;eg:

function decrypt() {
document = false;
var flag = document?true:false;
if (flag) {
return "正确加密"
} else {
return "错误加密";
}
} 在浏览器环境运行时 flag为true,然后得到正常结果;
在Node环境运行时 flag为false,然后得到错误结果;

所以我们需要 “补浏览器环境”,使得扣出来的 “js加密算法代码”Node环境中运行得到的加密值,与其在 浏览器环境中运行得到的加密值一致。 即对于这段 “js加密算法代码” 而言,我们补出来的环境与浏览器环境一致。

三:怎么 “补浏览器环境”?

要想 “补浏览器环境”,首先我们得知道 “js加密算法代码” 到底使用了哪些浏览器环境API,然后再对应去补上这些环境;

那么我们该如何监测 “js加密算法代码” 对浏览器环境API的使用呢?

毫无争议:使用Proxy来监测浏览器环境API的使用,辅助补浏览器环境

Proxy是ES6提供的代理器,用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。 它可以代理任何类型的对象,包括原生数组,函数,甚至另一个代理;拥有递归套娃的能力!!

也就是说 我们代理某个对象后,我们就成了它的中间商,任何JS代码对它的任何操作都可以被我们所拦截!!

# 对navigator对象进行代理,并设置拦截后的操作
var handler = {set:funcA,get:funcB,deleteProperty:funcC,has:funcD ...};
navigator = new Proxy(navigator,handler);
# 对代理后的navigator进行各种操作都会被拦截并触发对应处理函数
navigator.userAgent 会被拦截并触发 get funcB
navigator.userAgent = "xx" 会被拦截并触发 set funcA
delete navigator; 会被拦截并触发 deleteProperty funC
"userAgent" in navigator 会被拦截并触发 has funD ...
等等... 任何操作都可以被拦截

基于Proxy的特性,衍生了两种补环境思路:

  1. 递归嵌套Proxy以此来代理浏览器所有的BOM、DOM对象及其属性,再配合node vm2模块提供的纯净V8环境,就相当于在node中,对整个浏览器环境对象进行了代理,JS代码使用任何浏览器环境 api都能被我们所拦截。然后我们针对拦截到的环境检测点去补。
  2. 搭建补环境框架,用JS模拟浏览器基于原型链去伪造实现各个BOM、DOM对象,然后将这些JS组织起来,形成一个纯JS丐版浏览器环境,我们补的纯JS丐版浏览器环境越完善,就越接近真实浏览器环境,能通杀的js环境检测就越多。最终完美通杀所有JS环境检测!!;示例:b站搜 "志远补环境"

第一种思路虽然实现简单,主要是对Proxy拦截器的使用 ,但是具备的环境监测能力有限,对较复杂的原型链等难以监测,即使是二次开发也上限不高;并且遇到JS使用了很多环境时手补也相当麻烦;

第二种思路虽然实现较为复杂,但是上限极高,且可以完美兼容第一种思路,具备可成长的通杀潜质。

所以业内补环境框架几乎都是基于第二种思路,先搭建一个补环境框架的骨架,将常见浏览器环境BOM、DOM对象补齐,如:window、location、Document、navigator等,等空闲时或工作遇到其他浏览器环境BOM、DOM对象,再将它补进来。补的越完善,我们能通杀JS环境检测越多。

优点:

  • 补的越完善,能通杀JS环境检测越多。最终完美通杀所有JS环境检测!!
  • 一键运行输出目标JS中所有环境检测点;
  • 生成的最终代码可直接用于生产环境(可直接供nodejs、v8使用);
  • 告别玄学补环境,不再一行行去debugger,极大提高工作效率。
  • 可以在Chrome浏览器进行无浏览器环境调试。
  • 新人弯道超车必备
  • .....

四:“补环境框架”实战

传统补环境格式:

// 环境头:
window = global;
navigator= {userAgent:"Mozilla/5.0 (Windows NT 1";}
// 扣出来的JS
........
......

传统补环境太简陋,而且不够通用,代码组织混乱,我们最好将其组织为一个项目:

补环境框架项目整体结构:

那么实现这么一个浏览器补环境框架需要哪些步骤哪些考虑呢?

  • 先确定框架运行主流程,即入口文件 。
  • 每个BOM、DOM对象的实现都使用一个单独的js文件,便于定位及维护。
  • 将这些BOM、DOM文件按照原型链的优先顺序进行读取,拼接成整个浏览器环境。
  • 思考如何去实现一个BOM、DOM对象使其和浏览器一致;(这个是影响框架上限的重要因素,同时也包含大量重复性人力工作)
  • 事件的处理(对行为验证码有帮助)
  • 思考如何保证JS中使用到的所有浏览器环境都能被我们所检测;(这个是影响框架上限的重要因素)
  • 如何设计优化补环境框架项目的可扩展、可维护性;(非常必要)

    ...

    还有一些其他细节思考,我们的目标框架就是 一个易于可扩展与维护、能检测到JS中所有浏览器环境API的使用、实现了常见浏览器环境方法等,让我们在之后补环境中,达到通杀效果。

如果对于原理及实现方向 思考不够全面、深入,那么实现的框架上限会有限,出现玄学的概率就大了,我也是经历了很长时间打磨,多次推倒重来、借鉴多个课程,最终实现这个理想的框架。

下面就是一些具体的实现:

以下就是主流程入口骨架:

var  fs = require('fs');
var catvm2 = require('./CatVm2/catvm2.node.js'); const {VM,VMScript} = require('vm2'); //看作纯净V8 var catvm2_code = catvm2.GetCode(); //获取所有代码(工具代码、补的所有BOM、DOM对象)
var web_js_code = fs.readFileSync(`${__dirname}/target/get_b_fz.js`) ; // 获取目标网站js代码
var log_code = "\r\ncatvm.print.getAll();debugger;\r\r";
var all_code = catvm2_code+web_js_code+log_code;
fs.writeFileSync(`${__dirname}/debugger_bak.js`,all_code);
const script = new VMScript(all_code,`${__dirname}/debugger.js`); //真实路径,浏览器打开的就是该缓存文件 const vm = new VM(); // new 一个纯净v8环境
debugger
vm.run(script); // 在V8环境中运行调试
debugger

骨架搭好之后我们就要去补对应的BOM、DOM对象,比如补Navigator

1、先在浏览器环境观察该对象:Navigator

能否进行new Navigator,不能的话则在其构造函数定义中抛出异常,能的话不抛;

var dsf_tmp_context = catvm.memory.variable.Navigator = {};
var Navigator = function Navigator() { // 构造函数
throw new TypeError("Illegal constructor");
}; catvm.safefunction(Navigator);//13

2、查看其原型Navigator.prototype 的属性、方法、原型链,

发现Navigator原型属性、方法不能通过原型调用,即

Navigator.appVersion 会抛出异常。

发现 其原型链只有一层,即Navigator.prototype.__proto__ === Object.prototype

3、在浏览器环境观察其实例对象:navigator

查看其属性、方法与 原型上的差异,发现差不多,基本都是继承原型的。

因此可以简单补成下面这样:


Object.defineProperties(Navigator.prototype, {
[Symbol.toStringTag]: {
value: "Navigator",
configurable: true
}
});
var navigator = {};
navigator.__proto__ = Navigator.prototype; Navigator.prototype.plugins = [];
Navigator.prototype.languages = ["zh-CN", "zh"];
Navigator.prototype.userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36';
Navigator.prototype.platform = 'Win32';
Navigator.prototype.maxTouchPoints = 0;
Navigator.prototype.onLine = true;
for (var _prototype in Navigator.prototype) {
navigator[_prototype] = Navigator.prototype[_prototype];
if (typeof (Navigator.prototype[_prototype]) != "function") {
Navigator.prototype.__defineGetter__(_prototype, function () {
debugger;
var e = new Error();
e.name = "TypeError";
e.message = "Illegal constructor";
e.stack = "VM988:1 Uncaught TypeError: Illegal invocation \r\n " +
"at <anonymous>:1:21";
throw e;
// throw new TypeError("Illegal constructor");
});
}
} // 加上代理
navigator = catvm.proxy(navigator);

注:上面实例只是一种补环境思路,是基于对象.属性粒度我个人用的是另一种思路,基于对象.属性.特性粒度即Object.getOwnPropertyDescriptor 的value,writable..等,虽然需要补代码更多,但是模拟的效果更完美,理论上限极高。

浏览器对象及属性实在太多了,我们不可能手动补那么对象属性,因此要想补出一个完美浏览器环境,我们需要编写浏览器环境自吐脚本。即在浏览器执行该脚本,它会将某个浏览器环境对象的所有属性与方法,拼接成我们框架所需要的补环境代码,我们直接粘贴进来,稍微改改即可。

我们可以借助:Reflect.ownKeys(real_obj)来获取该对象的所有属性与方法,

然后对其 attr进行各种判断、处理,最终拼接成我们需要的样子。

var all_attrs = Reflect.ownKeys(real_obj);
var continue_attrs = ["prototype", "constructor"];
for (let index = 0; index < all_attrs.length; index++) {
let attr_name = all_attrs[index];
// 暂时不处理在 continue_attrs 中的属性
if (continue_attrs.indexOf(attr_name) != -1) {
console.log(`遇到 ${attr_name},跳过`);
continue
}
if (attr_name == Symbol.toStringTag) {
result_code = `Object.defineProperties(${repair_obj}, {
[Symbol.toStringTag]: {
value: "${real_obj[Symbol.toStringTag]}",
configurable: true
}
});//23\n`;
symbol_code_ls.push(result_code);
continue
}
}
..........太长,略过(下面框架源码中有)

每补完一个浏览器对象之后,可以运行起来与真实浏览器进行对比,逐步优化,最终达到完美效果。

五:“补环境框架”成品源码

补环境框架俨然成为JS逆向人员的大杀器,也是众多面试官的考察点。我们已经了解了 它的原理及实现步骤,接下来我们可以尝试自己从头实现一个完善的补环境框架,但是这会花费很长一段时间来进行开发,而且其中有很多重复性工作比较无聊(复制粘贴对比等)。

走快车道:

我在这条路已经走的比较久,补了很多环境,如果你想省下大段时间极大提高效率,直接弯道超车的话,可以 微信联系我:dengshengfeng666 付费源码借鉴;

统一固定价 99,付完直接发源码(有readme可直接小白上手),后续有疑问可以直接问我。

或者直接在CSDN私信我。

部分成果展示(以头条 sign值为例):

监测到的检测点,做过的靓仔可以看看是不是都有

与真实浏览器对比



弯道超车,从我做起

JS逆向之浏览器补环境详解的更多相关文章

  1. js replace 与replaceall实例用法详解

    这篇文章介绍了js replace 与replaceall实例用法详解,有需要的朋友可以参考一下stringObj.replace(rgExp, replaceText) 参数 stringObj 必 ...

  2. JS JSOP跨域请求实例详解

    JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题.这篇文章主要介绍了JS JSOP跨域请求实例详解的相关资料,需要的朋友可以参考下 ...

  3. js插件---videojs中文文档详解

    js插件---videojs中文文档详解 一.总结 一句话总结: js插件网上都有很多参考资料,使用起来也非常简单 二.lavarel中使用实例 <video id="example_ ...

  4. Eclipse IDE for C/C++ Developers和MinGW安装配置C/C++开发学习环境详解

    Eclipse IDE for C/C++ Developers和MinGW安装配置C/C++开发学习环境详解 操作系统:Windows 7 JDK版本:1.6.0_33 Eclipse版本:Juno ...

  5. 最详尽的 JS 原型与原型链终极详解,没有「可能是」。(一)

    最详尽的 JS 原型与原型链终极详解,没有「可能是」.(一) 第二篇已更新,点击进入第三篇已更新,点击进入

  6. JS中的函数节流throttle详解和优化

    JS中的函数节流throttle详解和优化在前端开发中,有时会为页面绑定resize事件,或者为一个页面元素绑定拖拽事件(mousemove),这种事件有一个特点,在一个正常的操作中,有可能在一个短的 ...

  7. 转载 JS组件Bootstrap Select2使用方法详解

    JS组件Bootstrap Select2使用方法详解 作者:懒得安分 字体:[增加 减小] 类型:转载 时间:2016-01-26我要评论 这篇文章主要为大家介绍了JS组件Bootstrap Sel ...

  8. JS DOM对象控制HTML元素详解

    JS DOM对象控制HTML元素详解 方法: getElementsByName()  获取name getElementsByTagName()  获取元素 getAttribute()  获取元素 ...

  9. js课程 1-3 Javascript变量类型详解

    js课程 1-3  Javascript变量类型详解 一.总结 一句话总结:js对象点(属性方法),json对象冒号(属性方法).属性和方法区别只有一个括号. 1.json对象中的函数的使用? 函数名 ...

  10. 【面试题】JS第七种数据类型Symbol详解

    JS第七种数据类型Symbol详解 点击打开视频讲解更加详细 一.什么是Symbol? Symbol是ES6中引入的一种新的基本数据类型,用于表示一个独一无二的值.它是JavaScript中的第 七种 ...

随机推荐

  1. Codeforces Round #830 (Div. 2) A-D

    比赛链接 A 题解 知识点:贪心,数论. 先求出序列最大公约数 \(d\) ,如果为 \(1\) 直接输出 \(0\) . 否则,尝试用最后一个数操作, \(gcd(d,n) = 1\) 则可以,花费 ...

  2. 十七、Job与Cronjob

    Job 与 Cronjob 一.Job ​Job 负责批处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个 Pod 成功结束. 特殊说明: 1.spec.template 格式同 Pod ​2 ...

  3. Windows下pip换成清华源

    1.在C:\Users\用户名\ 下创建 pip 文件夹2.在文件夹内创建pip.ini 文件, 添加如下内容: [global] timeout = 6000 index-url = https:/ ...

  4. webpack4--按需加载

    在做单页面应用的过程中,通常利用webpack打包文件,将依赖的外部问价单独打一个vendor.js.这样就会有个问题,这个文件会随着你引用的包的增多,体积会越来越大.在路由中利用import 引用文 ...

  5. 【深入浅出 Yarn 架构与实现】2-1 Yarn 基础库概述

    了解 Yarn 基础库是后面阅读 Yarn 源码的基础,本节对 Yarn 基础库做总体的介绍.并对其中使用的第三方库 Protocol Buffers 和 Avro 是什么.怎么用做简要的介绍. 一. ...

  6. TypeScript(基础篇)day01

    一.TS介绍 1.1 简介 ts是2012年由微软开发,在js的基础上添加了类型支持 1.2 优劣势 优势 :任何位置都有代码提示,增加效率:类型系统重构更容易:使用最新的ECMAscript语法 劣 ...

  7. Spark通过打jar包形式提交任务

    idea构建项目 创建一个maven项目,配置pom依赖,以及scala编译插件. 注意一定要保证,你的scala版本和spark版本和要提交的集群版本一致,要不很多莫名其妙的问题,scala如果你在 ...

  8. mysql 多个结构不同表查询 返回相同字段名

    ( select ID,数据库原字段名1 AS 统一字段名1,数据库原字段名2 AS 统一字段名2 from 第一个表名 WHERE 1) UNION(联合表查询)( select ID,数据库原字段 ...

  9. Linux配置篇 - Vmware网络配置

    网络配置 VM 网络详解 vmware提供了三种网络工作模式,Bridged(桥接模式).NAT(网络地址转换模式).Host-Only(仅主机模式): vmware网络配置步骤:编辑 -> 虚 ...

  10. servlet包找不到,webservlet注解无效

    把tomcat/lib/  中的annotations-api.jar和servlet-api.jar复制到jdk/jre/lib/ext/目录中就行了 mine:C:\environment\apa ...