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 是一个浏览器插件,当它监控到目标文件时,就不进行网络请求,直接返回本地修改过的文件进行返回。其实也是篡改响应接口的一种方式。
重写关键函数
这种方式算是比较稳妥,使用比较多的一种方式。需要在函数声明之后打断点,然后再重写目标函数。
对于 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)
}
只要我们可以想到的都可以去重写,感兴趣可以自己去尝试一下。