吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2822|回复: 19
收起左侧

[Web逆向] 【js逆向】码上爬-题16 补环境,第一次补环境成功记录一下!

  [复制链接]
YYYYnb 发表于 2025-9-12 11:10
前言:
前几道都是补到一半,补不下去就去搜的详细教程才完成的。但是这个16题我找了半天硬是没看一个补完整的教程。所以只能自己硬着补完,被各种报错给折磨了一晚上。最终还是补成功了记录一下。



直接开始:
首先来到网址:aHR0cHM6Ly93d3cubWFzaGFuZ3BhLmNvbS9wcm9ibGVtLWRldGFpbC8xNi8=
直接看数据接口:

数据接口data

数据接口data

其中接口的负载数据就是一个时间戳和一个加密值h5(然后其余位置都没有看见明显加密位置,所以这一个h5值就是我们要逆向的点)

接口负载数据

接口负载数据

知道是这个数据加密了,我们就直接开始跟栈找加密位置,使用xhr或者启动器来找都可以,我这里就直接使用启动器来找了(找的到加密位置的过程很简单,这里就不一一写出了)
最后我们会定位到这么一个函数,在这里时我们单步步过后,加密值就生成,所以就可以判断这个值肯定就是在这里生成的了,我们可以直接步进观察函数内部细节逻辑。

定位函数

定位函数

步进之后我们就能看到这函数,其实这里就是真正的加密位置的函数了,_$lR 传入的是一个翻页的页数和一个时间戳信息,然后 加密值就通过 btoa(_$lw.h5st+String(_$lw.t))  函数返回给 _$lw 值里。

真加密位置

真加密位置

成功加密的返回

成功加密的返回


这里我们就直接开始把代码全部扣下来,在node里去补环境。(上环境代理,那里报了补那里)。

第一步步环境

第一步步环境

一点点

一点点

才补了一点点,我们的代码就没有报错了,然后我们就需要去模拟浏览器调用我们执行加密的方法。通过观察也是很不容易的找到了他的调用逻辑,有一个大的Sign方法,在这个大的Sign里面有一个_$lk 方法,我们的加密函数就只是在_$lk.prototype.sign 中,然后调用就是最下面的一个window.PcSign = new window.Sign()这个方法,我们可以直接使用这个window.PcSign.sign({page:2,t:12345648479}),就可以直接调用我们的加密函数。

调用逻辑

调用逻辑

sign中的_$lk

sign中的_$lk
  
在调用这个方法后,我们其实最好是不要改变代码的逻辑,所以我们还需要再加密函数里面去定义一个自己的变量去接受我们加密的结果比如:window.YYYYY = _$lw

接受加密结果

接受加密结果

调用函数的构造:
[JavaScript] 纯文本查看 复制代码
function get_res(page_,t_){
    window.PcSign = new window.Sign({page:page_,t:t_})
    console.log(window.YYYYY)
    return window.YYYYY
}
get_res(3,123456789)


然后运行发现没有值,很正常,因为环境肯定是不正常的,我们还需要通过我们的代理去补环境,这里我主要就写我在里面遇到的第一个问题,就是在补到一半时,他一直报一个错误 load rac js fail!

fail

fail

这个就是因为没有去和浏览器的环境做对比,我一直以为是我自己补的环境出问题,我还把各种AI都拷打了一遍死活没有找到正确的解法,后来只能放弃去补其他的环境。(其实后面补着补着才发现浏览器一样也是报的这个load rac js fail!错误,所以。。。确实有些无语,在这个地方卡了很久)

浏览器报错fail

浏览器报错fail

最后就直接上我的成品补环境的代码了,我也不知道为什么他们都不发出来,哎!估计确实是补的很多吧。其中的canvas的代码是直接是前面有道题需要的环境(canvas的代码AI生成的,我写不来,代码中的cookie,localStorage 的值在自己的浏览器上copy就可以了)
[JavaScript] 纯文本查看 复制代码
window = global
window.setInterval = function setInterval(selector) {
    console.log('setInterval的值:',selector)
}
window.setTimeout = function setTimeout(selector) {
    console.log('setTimeout的值:',selector)
}

// 创建一个Canvas元素模拟类
class Canvas {
  constructor(width, height) {
    this.width = width || 300;
    this.height = height || 150;
    this._context = null;
  }

  getContext(contextType) {
    if (contextType === '2d') {
      if (!this._context) {
        this._context = new CanvasRenderingContext2D(this);
      }
      return this._context;
    }
    return null;
  }

  toDataURL() {
    return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==';
  }

  // 添加其他Canvas方法
  addEventListener() {}
  removeEventListener() {}
  getBoundingClientRect() {
    return {
      left: 0,
      top: 0,
      right: this.width,
      bottom: this.height,
      width: this.width,
      height: this.height
    };
  }
}

// 创建CanvasRenderingContext2D模拟类
class CanvasRenderingContext2D {
  constructor(canvas) {
    this.canvas = canvas;
    this._fillStyle = '#000000';
    this._strokeStyle = '#000000';
    this._lineWidth = 1;
    this._font = '10px sans-serif';
    this._textAlign = 'start';
    this._textBaseline = 'alphabetic';
    this._globalAlpha = 1.0;
    this._globalCompositeOperation = 'source-over';
    this._lineCap = 'butt';
    this._lineJoin = 'miter';
    this._miterLimit = 10;
    this._lineDashOffset = 0;
    this._shadowBlur = 0;
    this._shadowColor = 'rgba(0, 0, 0, 0)';
    this._shadowOffsetX = 0;
    this._shadowOffsetY = 0;

    // 初始化状态栈
    this._stateStack = [];
  }

  // 属性getters和setters
  get fillStyle() {
    return this._fillStyle;
  }

  set fillStyle(value) {
    this._fillStyle = value;
  }

  get strokeStyle() {
    return this._strokeStyle;
  }

  set strokeStyle(value) {
    this._strokeStyle = value;
  }

  get lineWidth() {
    return this._lineWidth;
  }

  set lineWidth(value) {
    this._lineWidth = value;
  }

  get font() {
    return this._font;
  }

  set font(value) {
    this._font = value;
  }

  get textAlign() {
    return this._textAlign;
  }

  set textAlign(value) {
    this._textAlign = value;
  }

  get textBaseline() {
    return this._textBaseline;
  }

  set textBaseline(value) {
    this._textBaseline = value;
  }

  get globalAlpha() {
    return this._globalAlpha;
  }

  set globalAlpha(value) {
    this._globalAlpha = value;
  }

  get globalCompositeOperation() {
    return this._globalCompositeOperation;
  }

  set globalCompositeOperation(value) {
    this._globalCompositeOperation = value;
  }

  get lineCap() {
    return this._lineCap;
  }

  set lineCap(value) {
    this._lineCap = value;
  }

  get lineJoin() {
    return this._lineJoin;
  }

  set lineJoin(value) {
    this._lineJoin = value;
  }

  get miterLimit() {
    return this._miterLimit;
  }

  set miterLimit(value) {
    this._miterLimit = value;
  }

  get lineDashOffset() {
    return this._lineDashOffset;
  }

  set lineDashOffset(value) {
    this._lineDashOffset = value;
  }

  get shadowBlur() {
    return this._shadowBlur;
  }

  set shadowBlur(value) {
    this._shadowBlur = value;
  }

  get shadowColor() {
    return this._shadowColor;
  }

  set shadowColor(value) {
    this._shadowColor = value;
  }

  get shadowOffsetX() {
    return this._shadowOffsetX;
  }

  set shadowOffsetX(value) {
    this._shadowOffsetX = value;
  }

  get shadowOffsetY() {
    return this._shadowOffsetY;
  }

  set shadowOffsetY(value) {
    this._shadowOffsetY = value;
  }

  // 路径方法
  beginPath() {}
  closePath() {}
  moveTo(x, y) {}
  lineTo(x, y) {}
  bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {}
  quadraticCurveTo(cpx, cpy, x, y) {}
  arc(x, y, radius, startAngle, endAngle, anticlockwise) {}
  arcTo(x1, y1, x2, y2, radius) {}
  ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise) {}
  rect(x, y, width, height) {}

  // 绘制方法
  fill() {}
  stroke() {}
  drawFocusIfNeeded() {}
  scrollPathIntoView() {}
  clip() {}
  resetClip() {}

  // 变换方法
  rotate(angle) {}
  scale(x, y) {}
  translate(x, y) {}
  transform(a, b, c, d, e, f) {}
  setTransform(a, b, c, d, e, f) {}
  resetTransform() {}

  // 合成方法
  save() {
    this._stateStack.push({
      fillStyle: this._fillStyle,
      strokeStyle: this._strokeStyle,
      lineWidth: this._lineWidth,
      font: this._font,
      textAlign: this._textAlign,
      textBaseline: this._textBaseline,
      globalAlpha: this._globalAlpha,
      globalCompositeOperation: this._globalCompositeOperation,
      lineCap: this._lineCap,
      lineJoin: this._lineJoin,
      miterLimit: this._miterLimit,
      lineDashOffset: this._lineDashOffset,
      shadowBlur: this._shadowBlur,
      shadowColor: this._shadowColor,
      shadowOffsetX: this._shadowOffsetX,
      shadowOffsetY: this._shadowOffsetY
    });
  }

  restore() {
    if (this._stateStack.length > 0) {
      const state = this._stateStack.pop();
      this._fillStyle = state.fillStyle;
      this._strokeStyle = state.strokeStyle;
      this._lineWidth = state.lineWidth;
      this._font = state.font;
      this._textAlign = state.textAlign;
      this._textBaseline = state.textBaseline;
      this._globalAlpha = state.globalAlpha;
      this._globalCompositeOperation = state.globalCompositeOperation;
      this._lineCap = state.lineCap;
      this._lineJoin = state.lineJoin;
      this._miterLimit = state.miterLimit;
      this._lineDashOffset = state.lineDashOffset;
      this._shadowBlur = state.shadowBlur;
      this._shadowColor = state.shadowColor;
      this._shadowOffsetX = state.shadowOffsetX;
      this._shadowOffsetY = state.shadowOffsetY;
    }
  }

  // 文本方法
  fillText(text, x, y, maxWidth) {}
  strokeText(text, x, y, maxWidth) {}
  measureText(text) {
    return {
      width: text.toString().length * 6,
      actualBoundingBoxAscent: 10,
      actualBoundingBoxDescent: 2,
      actualBoundingBoxLeft: 0,
      actualBoundingBoxRight: text.toString().length * 6,
      fontBoundingBoxAscent: 10,
      fontBoundingBoxDescent: 2
    };
  }

  // 图像方法
  drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) {}
  createImageData(width, height) {
    return {
      width: width,
      height: height,
      data: new Uint8ClampedArray(width * height * 4)
    };
  }

  getImageData(sx, sy, sw, sh) {
    return this.createImageData(sw, sh);
  }

  putImageData(imagedata, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight) {}

  // 像素操作
  createPattern(image, repetition) {
    return {};
  }

  createLinearGradient(x0, y0, x1, y1) {
    return {
      addColorStop: function(offset, color) {}
    };
  }

  createRadialGradient(x0, y0, r0, x1, y1, r1) {
    return {
      addColorStop: function(offset, color) {}
    };
  }

  // 其他方法
  setLineDash(segments) {
    this._lineDash = segments;
  }

  getLineDash() {
    return this._lineDash || [];
  }

  clearRect(x, y, width, height) {}
  fillRect(x, y, width, height) {}
  strokeRect(x, y, width, height) {}

  isPointInPath(x, y) {
    return false;
  }

  isPointInStroke(x, y) {
    return false;
  }
}

canvas = new Canvas(800, 600);
location={
    "ancestorOrigins": {},
    "href": "https://www.mashangpa.com/problem-detail/16/",
    "origin": "https://www.mashangpa.com",
    "protocol": "https:",
    "host": "www.mashangpa.com",
    "hostname": "www.mashangpa.com",
    "port": "",
    "pathname": "/problem-detail/16/",
    "search": "",
    "hash": ""
}

localStorage = {
    //这里需要拷贝浏览器中的值 、、、、、、、
    
    getItem : function getItem(selector) {
        console.log('getItem的值:',selector)
        if (selector === 'WQ_gather_cv1' ) {
            return localStorage['WQ_gather_cv1']
        }
        if (selector === 'WQ_gather_wgl1') {
            return localStorage['WQ_gather_wgl1']
        }
    },
    setItem : function setItem(selector,value) {
        console.log('setItem的值:',selector,value)
    }
}
head = {}
script = {
    parentNode : head
}
script.parentNode.removeChild  = function removeChild(selector) {
    console.log('removeChild的值:',selector)
}
document = {
    querySelector : function querySelector(selector) {},
    createElement : function createElement(selector) {
        console.log('createElement的值:',selector)
        if ( selector === 'script') {
            return script
        }
        if (selector === 'canvas'){
            return canvas
        }
    },
    createEvent : function createEvent(selector) {
        console.log('createEvent的值:',selector)
    },
    getElementsByTagName : function getElementsByTagName(selector) {
        console.log('getElementsByTagName的值:',selector)
        if ( selector === 'head') {
            return {
                "0": {
                    appendChild : function appendChild(selector) {
                        console.log('appendChild的值:',selector)
                        return script;
                    }
                }
            }
        }
    },
    cookie :"", //自己的cookie,
    documentElement: {
        html:{
            lang : 'zh-CN'
        },
    },
    body :{
        nav :{
            class: 'navbar',
        }
    },
    head :{
        meta:{
            charset : 'UTF-8'
        },
        title: '\n' +
            '        \n' +
            '            题十六:仿电商加密 - 码上爬\n' +
            '        \n' +
            '    '
    }
}

navigator = {
    userAgent : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36',
    hardwareConcurrency : 20,
    webdriver : false,
    platform : 'Win32',
    languages : [
        "zh-CN",
        "ja-JP",
        "ja",
        "zh"
    ],
    plugins:{
    "0": {
        "0": {},
        "1": {}
    },
    "1": {
        "0": {},
        "1": {}
    },
    "2": {
        "0": {},
        "1": {}
    },
    "3": {
        "0": {},
        "1": {}
    },
    "4": {
        "0": {},
        "1": {}
    }
}

}

补到这一步之后,其实还是有问题的,我们会发现生成出来的加密值和,浏览器中的少一一大截,然后我通过一步一步的调试发现了这么一个位置:

大window

大window

这里的函数明显是push了一个大Window进去,而我的环境里并没有大Window这个属性,因为我并没有按照补原型的方式来部环境,所以这里我们还要把这个大Window,直接在代码上改成 小 window就能够和浏览器输出一样的加密长度的值了,然后用python测试,结果也是通过的,这里就不写python代码了,很简单就没有写的必要了。
最终的结果:

最终结果

最终结果

和浏览器的结果对比:一致

结果对比

结果对比





结尾:
第一次写帖子,写的不好还请见谅。

启动器

启动器

免费评分

参与人数 3威望 +1 吾爱币 +21 热心值 +3 收起 理由
涛之雨 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
zhangxg + 1 用心讨论,共获提升!
无名 + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

kevinluo2025 发表于 2025-11-1 12:57
楼主代码没问题吗?扣下来的 PcSign.js 加载的时候还会调用一个外网的JS ,js-security-v3-rac
这个应该也要扣下来吧,不扣下来执行有问题的,目前的问题是能加密成功就是请求异常不对,肯定是环境还不对
还有你这代码里面也没有toDataURL (canvas的方法),是不是代码没有贴完整,这个问题搞了3天一直这样
 楼主| YYYYnb 发表于 2025-11-3 20:26
kevinluo2025 发表于 2025-11-1 12:57
楼主代码没问题吗?扣下来的 PcSign.js 加载的时候还会调用一个外网的JS ,js-security-v3-rac
这个应该也 ...

我的js代码localStorage和cookie里面的值需要复制自己的哦!这里是删掉的了,需要复制自己浏览器的, image.png image.png ,其他都是一样的,,我刚刚又用这个试了一下,还是可以的. image.png
无名 发表于 2025-9-12 19:56
 楼主| YYYYnb 发表于 2025-9-12 21:55
无名 发表于 2025-9-12 19:56
是不是多了张图

那图片不知道怎么就跑下面去了
无名 发表于 2025-9-12 23:28
YYYYnb 发表于 2025-9-12 21:55
那图片不知道怎么就跑下面去了

没有使用但是上传了的图会自动出现在最后
y294945022 发表于 2025-9-14 18:26
问下,看到别人发的逆向帖子,发的网址都要加个密,这是为什么。

像你的这个,aHR0cHM6Ly93d3cubWFzaGFuZ3BhLmNvbS9wcm9ibGVtLWRldGFpbC8xNi8=
 楼主| YYYYnb 发表于 2025-9-14 18:28
y294945022 发表于 2025-9-14 18:26
问下,看到别人发的逆向帖子,发的网址都要加个密,这是为什么。

像你的这个,aHR0cHM6Ly93d3cubWFzaGFu ...

其实就为过审核

点评

与其说为了过审,倒不如说防搜索 和很多教程和帖子会说“某平台”一样,直接域名或者点名容易被找或者修复。。。  详情 回复 发表于 2025-9-14 21:53
涛之雨 发表于 2025-9-14 21:53
YYYYnb 发表于 2025-9-14 18:28
其实就为过审核

与其说为了过审,倒不如说防搜索

和很多教程和帖子会说“某平台”一样,直接域名或者点名容易被找或者修复。。。
HaveAnOrange 发表于 2025-9-15 14:28
恭喜恭喜
walykyy 发表于 2025-9-15 14:54
写的不错,向楼主学习
anning666 发表于 2025-9-15 17:35
我一看到补环境这种事情,就头大,感觉没完没了,要一直补...一直补...好麻烦.......
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-1-3 00:08

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表