【JS逆向百例】携某 testab 参数补环境详解
字数 1131 2025-08-20 18:18:23

JS逆向补环境技术详解:携程testab参数逆向分析

一、前言

补环境技术是JS逆向中相对通用且易于实现的方法,相较于直接逆向算法,补环境可以更高效地解决JSVMP等复杂加密问题。本文将以携程testab参数为例,详细讲解补环境的完整流程和技术要点。

二、逆向目标分析

1. 目标参数

  • 参数名:testab
  • 生成方式:由e函数生成,通过JSVMP代码动态执行
  • 接口来源:getHotelScript接口返回的VMP代码

2. 定位关键代码

  1. 全局搜索"testab"定位到生成函数
  2. 跟栈发现是VMP代码通过eval执行
  3. VMP代码动态变化,需固定调试

三、VMP代码调试方法

1. 两种调用方式

// 方法一:动态eval执行
var code = `vmp代码`;
function decode(callback) {
  window[callback] = function(e) {
    delete window[callback];
    var e = e();
    testab = e;
    return e;
  }
  window.eval(code);
  return testab
}
decode("KLBNxcMKmI");

// 方法二:直接执行
function decode(callback) {
  window[callback] = function(e) {
    delete window[callback];
    var e = e();
    testab = e;
    return e;
  }
  // 直接放入vmp代码
  return testab
}
decode("KLBNxcMKmI");

2. JSVMP插桩调试

在VMP指令为函数调用的地方下断点,输出关键日志:

  • navigator自有属性和external的toString()检测
  • document.documentElement的getAttribute检测
  • Object.keys对document原型检测
  • navigator属性描述符检测及UA检测
  • node process检测
  • Window toString()检测
  • document检测
  • createElement检测
  • appendChild及报错检测
  • vm及其他检测

四、补环境实现

1. 基础环境搭建

创建三个文件:

  • main.js:主运行文件
  • env.js:环境补全代码
  • code.js:VMP执行代码

基础保护代码

!(function(){
  "use strict";
  const $toString = Function.toString;
  const myFunction_toString_symbol = Symbol('('.concat(Math.random()+'').toString(36)));
  const mytoString = function(){
    return typeof this == 'function' && this[myFunction_toString_symbol] || $toString.call(this);
  };
  
  function set_native(func,key,value){
    Object.defineProperty(func,key,{
      "enumerable": false,
      "configurable": true,
      "writable": true,
      "value": value
    })
  };
  
  delete Function.prototype['toString'];
  set_native(Function.prototype,"toString",mytoString);
  set_native(Function.prototype.toString,myFunction_toString_symbol,"function toString() { [native code] }");
  
  this.func_set_native = function(func){
    set_native(func,myFunction_toString_symbol,`function ${myFunction_toString_symbol,func.name}(){ [native code] }`)
  }
}).call(this);

window = this;
self = window;
self.window = window;

2. 关键环境补全

基础对象补全

Window = function Window(){};
Location = function Location(){};
Navigator = function Navigator(){};
Image = function Image(){ console.log("Image", arguments) };
document = {};
navigator = {};
location = {};
external = {};

// 外部对象补全
Object.defineProperty(external, Symbol.toStringTag, {
  value: 'External'
});
External = function External(){ console.log("External",arguments) };
func_set_native(External);
external.__proto__ = External.prototype;

Navigator补全

Object.setPrototypeOf(navigator, Navigator.prototype);
Navigator.prototype.webdriver = false;
Navigator.prototype.platform = 'Win32';
Navigator.prototype.appCodeName = 'Mozilla';
Navigator.prototype.userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36';

Document相关补全

HTMLHtmlElement = function HTMLHtmlElement(){
  this.getAttribute = function(){
    return null
  }
};
document.documentElement = new HTMLHtmlElement();

HTMLBodyElement = function HTMLBodyElement(){
  this.appendChild = function(child){
    if(child.tagName == "DIV"){
      child.offsetHeight = 20
    }
  }
};
document.body = new HTMLBodyElement();

HTMLDivElement = function HTMLDivElement(){
  this.tagName = "DIV";
  this.style = { height: "" };
  this.offsetHeight = 0;
  this.remove = function(){
    this.offsetHeight = 0
  }
};

document.createElement = function createElement(){
  console.log("createElement创建了", arguments);
  let tagName = arguments[0];
  if(tagName == "div"){
    var div = new HTMLDivElement();
    return div;
  }
  // 其他标签处理...
};

3. 对象冻结

Object.freeze(document);
Object.freeze(navigator);

4. 关键检测绕过

Object属性检测

// getOwnPropertyDescriptor检测
_getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
Object.getOwnPropertyDescriptor = function(obj, p){
  // 自定义处理逻辑
  return _getOwnPropertyDescriptor.apply(this, arguments);
};

// keys检测
_keys = Object.keys;
Object.keys = function(obj){
  // 自定义处理逻辑
  return _keys.apply(this, arguments);
};

// getOwnPropertyNames检测
_getOwnPropertyNames = Object.getOwnPropertyNames;
Object.getOwnPropertyNames = function(obj){
  // 自定义处理逻辑
  return _getOwnPropertyNames.apply(this, arguments);
};

正则检测绕过

RegExp = new Proxy(RegExp, {
  construct(target, argArray) {
    if(argArray[0] && argArray[0].indexOf('vm') !== -1){
      return new target(...['bootstrapNodeJSCoretryModuleLoadevalmachinerunInContext','g'])
    }
    return new target(...argArray)
  }
});

5. 代理检测处理

使用高级代理方案避免被检测:

dtavm = {};
dtavm.log = console.log;

function proxy(obj, objname, type) {
  // 完整代理实现...
  // 包含get/set/has/deleteProperty等处理
  // 详细代码见原文
}

// 使用方式
window = proxy(window, "window");
document = proxy(document, "document");
// 其他对象代理...

五、完整流程总结

  1. 定位testab生成位置,提取VMP代码
  2. 搭建基础环境框架(main.js, env.js, code.js)
  3. 实现基础保护代码(Function.toString等)
  4. 补全关键环境对象(window, document, navigator等)
  5. 处理DOM相关操作(createElement, appendChild等)
  6. 绕过关键检测(Object方法、正则检测等)
  7. 使用高级代理避免被检测
  8. 冻结关键对象防止修改
  9. 调试验证结果一致性

六、注意事项

  1. 补环境需要耐心,逐步验证每个补丁的效果
  2. 优先处理影响主流程的环境检测
  3. 注意浏览器异常断点的使用
  4. 代理实现要足够完善以避免被检测
  5. 关键对象如document、navigator需要冻结
  6. 所有函数都需要toString保护

通过以上步骤,可以成功补全环境并正确生成testab参数。这种方法具有通用性,可以应用于其他类似的JSVMP逆向场景。

JS逆向补环境技术详解:携程testab参数逆向分析 一、前言 补环境技术是JS逆向中相对通用且易于实现的方法,相较于直接逆向算法,补环境可以更高效地解决JSVMP等复杂加密问题。本文将以携程testab参数为例,详细讲解补环境的完整流程和技术要点。 二、逆向目标分析 1. 目标参数 参数名:testab 生成方式:由e函数生成,通过JSVMP代码动态执行 接口来源:getHotelScript接口返回的VMP代码 2. 定位关键代码 全局搜索"testab"定位到生成函数 跟栈发现是VMP代码通过eval执行 VMP代码动态变化,需固定调试 三、VMP代码调试方法 1. 两种调用方式 2. JSVMP插桩调试 在VMP指令为函数调用的地方下断点,输出关键日志: navigator自有属性和external的toString()检测 document.documentElement的getAttribute检测 Object.keys对document原型检测 navigator属性描述符检测及UA检测 node process检测 Window toString()检测 document检测 createElement检测 appendChild及报错检测 vm及其他检测 四、补环境实现 1. 基础环境搭建 创建三个文件: main.js:主运行文件 env.js:环境补全代码 code.js:VMP执行代码 基础保护代码 2. 关键环境补全 基础对象补全 Navigator补全 Document相关补全 3. 对象冻结 4. 关键检测绕过 Object属性检测 正则检测绕过 5. 代理检测处理 使用高级代理方案避免被检测: 五、完整流程总结 定位testab生成位置,提取VMP代码 搭建基础环境框架(main.js, env.js, code.js) 实现基础保护代码(Function.toString等) 补全关键环境对象(window, document, navigator等) 处理DOM相关操作(createElement, appendChild等) 绕过关键检测(Object方法、正则检测等) 使用高级代理避免被检测 冻结关键对象防止修改 调试验证结果一致性 六、注意事项 补环境需要耐心,逐步验证每个补丁的效果 优先处理影响主流程的环境检测 注意浏览器异常断点的使用 代理实现要足够完善以避免被检测 关键对象如document、navigator需要冻结 所有函数都需要toString保护 通过以上步骤,可以成功补全环境并正确生成testab参数。这种方法具有通用性,可以应用于其他类似的JSVMP逆向场景。