首页 > 编程语言 >JavaScript逆向之代码补环境(以iwencai为例)

JavaScript逆向之代码补环境(以iwencai为例)

时间:2024-03-20 18:11:21浏览次数:29  
标签:function Document XMLHttpRequest 为例 iwencai JavaScript EventTarget prototype ope

知识点

1. 浏览器环境与Node环境的区别

1.1 浏览器环境具有的功能

  1. 页面渲染功能
    (1)加载和控制页面元素的能力 -> 在js中由dom对象来完成
    (2)渲染引擎 -> 和我们基本上无关
  2. 浏览器本身的一些东西(窗口大小,url) -> BOM对象
  3. 能够执行js的能力 -> v8引擎负责执行js代码

1.2 Node环境具有的功能

  1. 包含V8引擎,还拥有一些node做后端独有的一些功能
  2. node里面是没有页面渲染的部分的(如:document)
  3. node里面也没有BOM对象的东西(如:window)

1.3 补环境的逻辑

补环境相当于自己创造的一个山寨的浏览器
在node环境中,想办法补充浏览器(document,window)的环境
补环境的时候,要注意node环境中独有的内容,如果有需要,要想办法剔除掉

2.原型链的继承

2.1 原型

JavaScript是一种基于原型的语言,任何对象和函数都有其对应的原型。
先来一个demo:

function Person(){}
var p = new Person();
console.log(Person.prototype);	// prototype是用来访问方法作为构造方法时的原型
console.log(p.__proto__);	// __proto__是用来访问对象的原型的

将其放在浏览器的控制台运行。
image

2.2 原型链的继承实现

这时候,如果有个需求,实现类似java或python里的继承关系,那么在JavaScript中该怎么实现呢?这就需要用到原型链了,直接看demo。

// 需求:让Cat方法继承Animal方法
function Animal(){
	this.name = "动物";
}	//定义Animal方法
Animal.prototype.run = function (){
	console.log("跑步");
}	//给Animal的原型中添加一个run方法

function Cat(){
	this.name = "猫";
}	//定义Cat方法
Cat.prototype.eat = function (){
	console.log("吃鱼");
}	//给Cat的原型中添加一个eat方法

//实现Cat继承Animal
Object.setPrototypeOf(Cat.prototype, Animal.prototype);

// 测试,如果Cat的对象能够访问到Animal中的run方法,说明继承成功。
var cat = new Cat();
cat.run();
console.log(cat.__proto__);

浏览器里运行一下。
image
成功调用到了Animal原型中的run方法。所以如果要实现继承关系,就要用到setPrototypeOf函数,可以看下setPrototypeOf的官方用法。
image

3.原生ajax

现在比较多见的是jQuery封装过后的ajax了,调用起来比较方便,demo如下:

$.ajax({
	// url 是接口
	url: "xxxxx",

	// type 数据操作类型
	type: "GET",

	// 获取的数据格式
	dataType: "json",

	// 获取成功后的操作
	success: function (data) {
		// data是请求成功的数据
		console.log(data);
	}
})

原生的ajax如果要发送请求就会用到一个关键的对象XMLHttpRequest,那就通过代码来看看原生ajax是如何实现发送请求的。

// 创建 XMLHttpRequest,相当于打开浏览器
const xhr = new XMLHttpRequest()

// 打开一个与网址之间的连接   相当于输入网址
// 利用open()方法,第一个参数是对数据的操作,第二个是接口
xhr.open("GET", "xxxxxx")

// 通过连接发送请求  相当于点击回车或者链接
xhr.send(null)

// 指定 xhr 状态变化事件处理函数   相当于处理网页呈现后的操作
// 全小写
xhr.onreadystatechange = function () {
	// 通过readyState的值来判断获取数据的情况
	if (this.readyState === 4) {
		// 响应体的文本 responseText
		console.log(this.responseText);
	}
}

XMLHttpRequest发送请求总共有5种状态,如下:
image
知道了以上知识点后,就可以开始实战了,还是老朋友-iwencai。

iwencai实战

上一篇关于iwencai请求头参数加密逻辑是通过抠代码的方式,接下来利用补环境的方式来解决。
通过上一篇文章已经知道需要解密的参数是Hexin-V和Cookie种的v,两个值是一样的。
image
查看initiator。
image
看到了XMLHttpRequest,点进去。
image
从这行代码往上看,可以明显的看到一个hook,对XMLHttpRequest的send函数进行了重写,肯定有猫腻。
image
打断点,让代码运行,看在哪里调用了这行代码。
image
还记得在讲原生ajax发送请求的顺序,先open再send,这里有send,那么前面肯定有open,找到后打断点。
image
找到open函数的定义。
image
为了看到hook的效果,在下面两行代码前面打上断点。
image
释放断点。运行到第一个断点时,open函数还是原生的。
image
运行到第二个断点时,open函数发生了变化。
image
说明在第一个断点的函数内对open函数进行了hook。在这个网站中采用的都是自定义的opensend函数,如果采用原生代码肯定就无效了,需要采用补环境的方式来实现。
下面就是补环境来解决加密逻辑的全部步骤。
从上篇文章得知参数的加密逻辑在如图所示的这个文件中。(这个文件名会变换,需要注意)
image
将全部的代码复制到pycharm中,用node环境跑一下。
image
报错,说缺少document,要就给它一个,加上如下代码。

Document = function Document() {};
document = new Document();

继续跑,说缺少window,再继续补。

Window = function Window(){};
window = new Window();

继续跑,说r[51].getElementsByTagName is not a function,去源代码中找到这个地方,打上断点调试。
image
可以看到getElementsByTagName是由document对象调用的,所以在document方法的原型上需要补一个getElementsByTagName方法。
image
这句话会返回一个head标签,在补的方法中也要有返回值,返回一个空即可。

Document.prototype.getElementsByTagName = function getElementsByTagName(name){
    return [{}];
};

继续跑,报s[66].createElement is not a function,找源代码打断点。
image
还是document原型上的方法,看下执行结果。
image
返回一个div标签,在补方法时需要有返回内容。

Document.prototype.createElement = function createElement(name){
    return [];
};

继续跑,报n.attachEvent is not a function,找源代码打断点。
image
q值为true,看下q定义的地方,
image
image
q值是判断window对象中有无addEventListener,官网查一下addEventListener
image
发现它是定义在EventTarget方法下的,并且Window是继承EventTarget的。
image
要补一个EventTarget方法,再补一个继承关系。

EventTarget = function EventTarget(){};
EventTarget.prototype.addEventListener = function addEventListener(type, listener){
};
Object.setPrototypeOf(Window.prototype, EventTarget.prototype);

继续跑,还是报n.attachEvent is not a function,再回去看代码,是document对象调用的addEventListener方法,还要再补一个DocumentEventTarget的继承关系。
image

Object.setPrototypeOf(Document.prototype, EventTarget.prototype);

继续跑,报navigator is not defined,找源代码打断点。
image
image
是个Navigator对象,需要补一个。

Navigator = function Navigator(){};

运行还是报navigator is not defined,继续看源代码。
image
找的是plugins属性,补上。

Navigator.prototype.plugins = {};
navigator = new Navigator();

再跑,没报错了,写一个测试代码,看看能不能过当前代码,如果前后输出的结果一致,说明就没过,如果不一致,就说明过了。完整的测试代码如下:

EventTarget = function EventTarget(){};
EventTarget.prototype.addEventListener = function addEventListener(type, listener){
};


Document = function Document() {};
Document.prototype.getElementsByTagName = function getElementsByTagName(name){
    return [{}];
};
Document.prototype.createElement = function createElement(name){
    return [];
};


Window = function Window(){};

Navigator = function Navigator(){};
Navigator.prototype.plugins = {};

Object.setPrototypeOf(Window.prototype, EventTarget.prototype);
Object.setPrototypeOf(Document.prototype, EventTarget.prototype);

document = new Document();
window = new Window();
navigator = new Navigator();

XMLHttpRequest = function XMLHttpRequest(){};
xhr = new XMLHttpRequest();
XMLHttpRequest.prototype.open = function open() {
    console.log(123456);
};
console.log(xhr.open.toString());

//这里是页面源代码,就不写了

console.log(xhr.open.toString());

运行结果如下:
image
两次结果一致,没过,那就是还有地方没补好。再回到最初通过hook cookie找到生成加密字符串的地方。
image
通过call stack往上看是哪里调用了。
image
看到了一个try...catch..的逻辑,那么有可能是报错被catch捕捉了,所以删除这个try...catch..,再跑一次测试代码。
image
果不其然,报错了。找到源代码打断点调试。但是(l[at(e[66])][at(e[67], a[68])])这行代码是不走的,往上看一行,
image
需要给Window加个localStorage属性。

Window.prototype.localStorage = {};

再跑,报错,找到源代码调试。
image
image
我们的代码中没有获取这一串字符串,看下它是哪里得到的,看call stack。
image
是从navigator对象中拿的,要给navigator补上userAgent

Navigator.prototype.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36";

跑,报如下错误。
image
根据代码,需要给navigator对象补上javaEnabled方法。

Navigator.prototype.javaEnabled = function javaEnabled(){
    return false;
};

跑,报如下错误。
image
调试。
image
需要给window对象加上navigator属性。

window.navigator = navigator;

跑,报如下错误。
image
没定义location,打断点调试。
image
需要一个location对象,在location对象中要有href属性。

Location = function Location(){};
Location.prototype.href = "http://iwencai.com/unifiedwap/result?w=20240315%E6%B6%A8%E5%81%9C&querytype="
location = new Location();

跑,报如下错误。
image
调试。
image
变量n的没获取到,通过call stack,看下是哪里传来的。
image
image
location中的hostname属性,补上。跟location相关的还有hostprotocol两个属性,一起补上。

Location.prototype.hostname = "iwencai.com";
Location.prototype.host = "iwencai.com";
Location.prototype.protocol = "http:";

跑,报如下错误。
image
调试。
image
需要Element对象和insertBefore方法,补上。

Element = function Element(){};
Element.prototype.insertBefore = function insertBefore(){};

运行。
image
没报错了,但是程序一直在运行,大概率是有定时器,搜索一下。
image
重写setInterval函数。

setInterval = function setInterval(){};

运行,两次结果还是一致,没绕过检测。
image
试了半天,才知道忘记XMLHttpRequest赋给window了,补上。

Window.prototype.XMLHttpRequest = XMLHttpRequest;

运行,两次结果不一致了,成功。
image
下面就可以直接通过这个获取Hexin-V的值了。
完整代码如下:

EventTarget = function EventTarget(){};
EventTarget.prototype.addEventListener = function addEventListener(type, listener){
};

Element = function Element(){};
Element.prototype.insertBefore = function insertBefore(){};


Document = function Document() {};
Document.prototype.getElementsByTagName = function getElementsByTagName(name){
    return [{}];
};
Document.prototype.createElement = function createElement(name){
    return [];
};


Window = function Window(){};
Window.prototype.localStorage = {};


Navigator = function Navigator(){};
Navigator.prototype.plugins = {};
Navigator.prototype.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36";
Navigator.prototype.javaEnabled = function javaEnabled(){
    return false;
};

Location = function Location(){};
Location.prototype.href = "http://iwencai.com/unifiedwap/result?w=20240315%E6%B6%A8%E5%81%9C&querytype="
Location.prototype.hostname = "iwencai.com";
Location.prototype.host = "iwencai.com";
Location.prototype.protocol = "http:";

Object.setPrototypeOf(Window.prototype, EventTarget.prototype);
Object.setPrototypeOf(Document.prototype, EventTarget.prototype);

document = new Document();
window = new Window();
navigator = new Navigator();
location = new Location();
window.navigator = navigator;


setInterval = function setInterval(){

};




XMLHttpRequest = function XMLHttpRequest(){};
xhr = new XMLHttpRequest();
Window.prototype.XMLHttpRequest = XMLHttpRequest;
XMLHttpRequest.prototype.open = function open() {
    console.log(123456);
};
console.log(xhr.open.toString());

window.test = {};
XMLHttpRequest.prototype.setRequestHeader = function setRequestHeader(k,v) {
    window.test[k] = v;
};


// 这里是页面源代码

// console.log(xhr.open.toString());

function fn() {
    xhr.open("get","http://iwencai.com/unifiedwap/suggest/V1/index/set-history")
    console.log(window.test['hexin-v']);
}

fn();

运行结果如下,直接就可以拿到hexin-v的值。
image

总结:补环境比较麻烦,需要经验的积累。平常多看看JavaScript官网

标签:function,Document,XMLHttpRequest,为例,iwencai,JavaScript,EventTarget,prototype,ope
From: https://www.cnblogs.com/sbhglqy/p/18085001

相关文章

  • JavaScript 系列教程 II JavaScript 基础知识
    ......
  • JavaScript 模块化
    JavaScript模块化JavaScript的历史问题背景JavaScript在一开始诞生的时候只是用来网页脚本的开发,其实没有模块化和命名空间的概念。JS的模块化需求日益增长。幼年期:无模块化模块化思维的萌芽。需要在页面中加载不同的js:动画库,表单库,格式化工具多种js文件被......
  • javascript:void(0);用法及常见问题解析
    javascript:void(0);是一个常见的JavaScript代码片段,通常用于在HTML中作为超链接的href属性值或者事件处理函数的返回值。下面是关于它的用法和常见问题的解析:用法:作为超链接的href属性值:<ahref="javascript:void(0);">点击这里</a>这样做的作用是让点击链......
  • YOLOV5 改进:替换backbone(MobileNet为例)
    1、前言之前介绍了yolov5如何更换C2f模块以及加入注意力机制SE模块的示例,详细请参考本专栏:YOLOV5实战项目(训练、部署、改进等等)_听风吹等浪起的博客-CSDN博客本文将详细介绍yolov5更换官方backbone,以轻量级网络mobilenet为例。因为mobilenet是轻量级的小型网络,参数量和......
  • 【编程向导】JavaScript-基础语法-语句和声明二期讲解
    switch语句switch语句允许一个程序求一个表达式的值并且尝试去匹配表达式的值到一个case标签。如果匹配成功,这个程序执行相关的语句。语法switch(expression){casevalue_1:statements_1[break;]casevalue_2:statements_2[br......
  • 前端基础之JavaScript的数据类型
    一、常用的调试语句方法说明示例归属alert(msg);警告,在浏览器中弹出一个警告框,内容为alert里面的内容alert("Surprise");浏览器closole.log(msg);控制台,在控制台内输出一些内容console.log("Surprise");浏览器prompt(问题,值);对话框,第一个参数是询问内......
  • 前端基础之JavaScript引入
    一、什么是JavaScriptJavaScript是一门跨平台、面向对象的脚本语言(不需要编译,直接解释运行即可),来控制网页的行为,它能使网页可交互。脚本语言:不需要编译,运行过程中由js解释器(js引擎)逐行来进行解释并执行。现在也可以基于Node.js技术进行服务器端编程W3C标准:网页主要由......
  • Google Earth Engine——如何实现裁剪后研究区影像的批量下载(以NDVI为例)
    简介GEE云平台(GoogleEarthEngine)是一个强大的云平台,提供了丰富的地理数据和计算资源,用于进行地理数据分析和处理。在GEE平台上,可以实现对研究区影像的单景影像(以NDVI为例)的批量下载。下面是具体的过程:1.登录GEE云平台并初始化首先,需要登录GEE云平台(https://earthengine.g......
  • 使用Selenium执行JavaScript脚本:探索Web自动化的新领域
    前言在我们使用selenium进行自动化测试的时候,selenium能够帮助我们实现元素定位和点击输入等操作,但是有的时候,我们会发现,即使我们的元素定位没有问题,元素也无法执行操作;也有部分情况是我们无法直接定位滚动条河时间控件来进行操作,这个时候,我们就需要借助JavaScript来解决问题。......
  • JavaScript笔记 01
    目录01js概述02js代码的基本使用03js变量的基本使用04变量的类型05数值类型06字符串类型07布尔类型08 使用typeof查看变量的类型09其他类型转换为字符串类型10其他数据类型转换为数值型11其他数据类型转换成布尔类型12小知识点01js概述前端的三......