web逆向应用

138 阅读3分钟

JavaScript 逆向应用

Hook 技术。将原本执行的函数替换成我们自定义的函数,自定义函数会保持原有函数的功能,并为其附加新功能。不改变程序执行效果的前提下,实现自定义的效果。

// ==UserScript==
// @name         HookBase64
// @namespace    http://xxx.com
// @version      0.1
// @description  Hook Base64 encode function
// @author       heora
// @match        http://xxx.com/login
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    function hook(object, attr) {
      const func = object[attr]

      object[attr] = function() {
        console.log('hooked', object, attr)
        const ret = func.apply(object, arguments)
        debugger
        return ret
      }
    }

    hook(window, 'btoa')
})();

原理就是代理方法,对方法进行重写,调试。例如上述代理的 btoa 方法,用于 base64 编码。

无限 debugger

Hook 技术。将原本执行的函数替换成我们自定义的函数,自定义函数会保持原有函数的功能,并为其附加新功能。不改变程序执行效果的前提下,实现自定义的效果。

什么情况下会碰到 debugger

  • 分析网络请求、查看元素的事件监听器、跟踪 js 等需求第一步就要打开浏览器的开发者工具,只要打开开发者工具就可能会碰到无限 debugger 死循环,或者在调试过程中也可能会出现无限 debugger 的死循环。

为什么反爬虫会用到无限 debugger

  • 分析代码逻辑、调试跟踪代码是 js 破解的必要手段,分析调试主要就是使用开发者工具,使用无限 debugger 属于精准防控手段。

debugger 反爬虫的优势在哪里

  • 实现比较简单,不必写复杂的反人类的反爬虫代码,写无限 debugger 应该是基本操作
  • 效果比较明显,如果破解不了,不能进行下一步
  • 一定程序可以提高代码逻辑的安全性,因为它可以阻止我们调试分析代码逻辑

作用

反调试:阻止调试和分析目标代码的运行逻辑

实现

debugger 关键字的应用

  • Function/eval "debugger"
  • function debugger

中间人工具替换特征字符串

我们可以利用 fiddler、charles 等工具将 debugger 关键字换成 "debugger" 或者替换为空。

GoRes 替换本地修改过的文件

GoRes 是一个浏览器插件,当它监控到目标文件时,就不进行网络请求,直接返回本地修改过的文件进行返回。其实也是篡改响应接口的一种方式。

image.png

重写关键函数

这种方式算是比较稳妥,使用比较多的一种方式。需要在函数声明之后打断点,然后再重写目标函数。

对于 function 关键字声明或 var 声明的函数是有效的,不过对于 const 关键字声明的箭头函数是无效的,函数不能被重写。

重写关键函数可以指定方法名,或者使用 Function.prototype.constructor = function() {} ,这种方法只有在 (function(){}).constructor === Function 时才会生效。

// 重写 eval 案例

 console.log(eval + '')
// 'function eval() { [native code] }'

// 重写 eval
window._origin_eval = window.eval

function $eval(src) {
  console.log(
    `==== eveal begin: length=${src.length}, caller=~${$eval.caller && $eval.caller.name} ====`
  )
  console.log(`injected ${document.location}`)
  console.log(src)
  console.log(`==== eval end ====`)

  return window._origin_eval(src)
}

Object.defineProperty(window, 'eval', { value: $eval })

console.log(eval + '')
// 'function $eval(src) {\n  console.log(\n    `==== eveal begin: length=${src.length}, caller=~${$eval.caller && $eval.caller.name} ====`\n  )\n  console.log(`injected ${document.location}`)\n  console.log(src)\n  console.log(`==== eval end ====`)\n\n  return window._origin_eval(src)\n}'

$eval.toString = function () {
  return 'function eval() { [native code] }'
}

console.log(eval + '')
// 'function eval() { [native code] }'

快速定位 - hook

json

const origin_stringify = JSON.stringify
JSON.stringify = function (params) {
  console.log('stringify', params)
  return origin_stringify(params)
}

const origin_parse = JSON.parse
JSON.parse = function (params) {
  console.log('parse', params)
  return origin_parse(params)
}

cookie

 let origin_cookie = document.cookie
Object.defineProperty(document, 'cookie', {
  get() {
    console.log('getter cookie')
    return origin_cookie
  },
  set(value) {
    console.log('setter cookie', value)

    const cookie = value.split(';')[0]
    const cookieArr = cookie.split('=')

    let flag = false
    let cache = origin_cookie.split('; ')

    cache = cache.map(item => {
      if (item.split('=')[0] === cookieArr[0]) {
        flag = true
        return cookie
      }
      return item
    })

    origin_cookie = cache.join('; ')
    if (!flag) {
      origin_cookie += cookie + '; '
    }
    this._value = value

    return origin_cookie
  }
})

window attr

比如我们有一个加密参数是 window 属性,我们需要找到属性谁在哪里被赋值的。

function hook(key) {
  const origin_window = window

  Object.defineProperty(window, key, {
    get() {
      console.log('getter window', key)
      return origin_window[key]
    },
    set(value) {
      console.log('setter window', value)
      origin_window.value = value
    }
  })
}

eval/Function

// eval

const origin_eval = window.eval
function $eval(src) {
  console.log(
    `==== eveal begin: length=${src.length}, caller=~${$eval.caller && $eval.caller.name} ====`
  )
  console.log(`injected ${document.location}`)
  console.log(src)
  console.log(`==== eval end ====`)

  return origin_eval(src)
}

$eval.toString = () => 'function eval() { [native code] }'
Object.defineProperty(window, 'eval', { value: $eval })
// Function

const origin_function = window.Function
function $func() {
  const args = [].slice.call(arguments, 0, -1).join(',')
  const src = [].slice.call(arguments).at(-1)

  console.log('function start')
  console.log(src)
  console.log('function end')

  return origin_function.apply(this, arguments)
}

$func.toString = () => origin_function + ''
Object.defineProperty(window, 'Function', { value: $func })

websocket

const origin_ws = WebSocket.prototype.send
WebSocket.prototype.send = function (data) {
  console.log('websocket', data)
  return origin_ws(data)
}

只要我们可以想到的都可以去重写,感兴趣可以自己去尝试一下。