Javascript逆向与反逆向
popunderjs 原来在 github 上是有开源代码的,但后来估计作者发现这个需求巨大的商业价值,索性不开源了,直接收费。所以现在要研究它的实现方案,只能上官网扒它源码了。
文件结构
script.js 是功能主体,实现了 popunder 的所有功能以及定义了多个 API 方法
license.demo.js 是授权文件,有这个文件你才能顺利调用 script.js 里的方法
防止被逆向
这么具有商业价值的代码,就这么公开地给你们用,肯定要考虑好被逆向的问题。我们来看看它是怎么反逆向的。
首先,打开控制台,发现2个问题:
控制台所有内容都被反复清空,只输出了这么一句话: Console was cleared script.js?0.5309098417125133:1
无法断点调试,因为一旦启用断点调试功能,就会被定向到一个匿名函数 (function() {debugger})
也就是说,常用的断点调试方法已经无法使用了,我们只能看看源代码,看能不能理解它的逻辑了。但是,它源代码是这样的:
<span style="font-size: 16px;">var a = typeof window === S[0] && typeof window[S[1]] !== S[2] ? window : global;<br> try {<br> a[S[3]](S[4]);<br> return function() {}<br> ;<br> } catch (a) {<br> try {<br> (function() {}<br> [S[11]](S[12])());<br> return function() {}<br> ;<br> } catch (a) {<br> if (/TypeError/[S[15]](a + S[16])) {<br> return function() {}<br> ;<br> }<br> }<br> }<br></span>
可见源代码是根本不可能阅读的,所以还是得想办法破掉它的反逆向措施。
利用工具巧妙破解反逆向
首先在断点调试模式一步步查看它都执行了哪些操作,突然就发现了这么一段代码:
<span style="font-size: 16px;">(function() {<br> (function a() {<br> try {<br> (function b(i) {<br> if (('' + (i / i)).length !== 1 || i % 20 === 0) {<br> (function() {}<br> ).constructor('debugger')();<br> } else {<br> debugger ;<br> }<br> b(++i);<br> }<br> )(0);<br> } catch (e) {<br> setTimeout(a, 5000);<br> }<br> }<br> )()<br>}<br>)();<br></span>
这段代码主要有2部分,一是通过 try {} 块内的 b() 函数来判断是否打开了控制台,如果是的话就进行自我调用,反复进入 debugger 这个断点,从而达到干扰我们调试的目的。如果没有打开控制台,那调用 debugger 就会抛出异常,这时就在 catch {} 块内设置定时器,5秒后再调用一下 b() 函数。
这么说来其实一切的一切都始于 setTimeout 这个函数(因为 b() 函数全是闭包调用,无法从外界破掉),所以只要在 setTimeout 被调用的时候,不让它执行就可以破解掉这个死循环了。
所以我们只需要简单地覆盖掉 setTimeout 就可以了……比如:
<span style="font-size: 16px;">window._setTimeout = window.setTimeout;<br>window.setTimeout = function () {};<br></span>
但是!这个操作无法在控制台里面做!因为当你打开控制台的时候,你就必然会被吸入到 b() 函数的死循环中。这时再来覆盖 setTimeout 已经没有意义了。
这时我们的工具 TamperMonkey 就上场了,把代码写到 TM 的脚本里,就算不打开控制台也能执行了。
TM 脚本写好之后,刷新页面,等它完全加载完,再打开控制台,这时 debugger 已经不会再出现了!
接下来就轮到控制台刷新代码了
通过 Console was cleared 右侧的链接点进去定位到具体的代码,点击 {} 美化一下被压缩过的代码,发现其实就是用 setInterval 反复调用 console.clear() 清空控制台并输出了 Console was cleared
所以我们可以通过覆盖 console.clear() 函数和过滤 log 信息来阻止它的清屏行为。
同样写入到 TamperMonkey 的脚本中,代码:
<span style="font-size: 16px;">window.console.clear = function() {};<br>window.console._log = window.console.log;<br>window.console.log = function (e) {<br> if (e['nodeName'] && e['nodeName'] == 'p') {<br> return ;<br> }<br> return window.console.error.apply(window.console._log, arguments);<br>};<br></span>
之所以用 error 来输出信息,是为了查看它的调用栈,对理解程序逻辑有帮助。
基本上,做完这些的工作之后,这段代码就可以跟普通程序一样正常调试了。但还有个问题,它主要代码是经常混淆加密的,所以调试起来很有难度。下面简单讲讲过程。
混淆加密方法一:隐藏方法调用,降低可读性
从 license.demo.js 可以看到开头有一段代码是这样的:
<span style="font-size: 16px;">var zBCa = function T(f) {<br> for (var U = 0, V = 0, W, X, Y = (X = decodeURI("+TR4W%17%7F@%17.....省略若干"),<br> W = '',<br> 'D68Q4cYfvoqAveD2D8Kb0jTsQCf2uvgs'); U < X.length; U++,<br/> V++) {<br/> if (V === Y.length) {<br/> V = 0;<br/> }<br/> W += String["fromCharCode"](X["charCodeAt"](U) ^ Y["charCodeAt"](V));<br/> }<br/> var S = W.split("&&");<br/></span>
通过跟踪执行,可以发现 S 变量的内容其实是本程序所有要用到的类名、函数名的集合,类似于 var S = ['console', 'clear', 'console', 'log'] 。如果要调用 console.clear() 和 console.log() 函数的话,就这样
<span style="font-size: 16px;">var a = window;<br/>a[S[0]][S[1]]();<br/>a[S[2]][S[3]]();<br/></span>
混淆加密方法二:将函数定义加入到证书验证流程
license.demo.js 中有多处这样的代码:
<span style="font-size: 16px;">a['RegExp']('/R[\S]{4}p.c\wn[\D]{5}t\wr/','g')['test'](T + '')<br/></span>
这里的 a 代表 window,T 代表某个函数, T + '' 的作用是把 T 函数的定义转成字符串,所以这段代码的意思其实是,验证 T 函数的定义中是否包含某些字符。
每次成功的验证,都会返回一个特定的值,这些个特定的值就是解密核心证书的参数。
可能是因为我重新整理了代码格式,所以在重新运行的时候,这个证书一直运行不成功,所以后来就放弃了通过证书来突破的方案。
逆向思路:输出所有函数调用和参数
通过断点调试,我们可以发现,想一步一步深入地搞清楚这整个程序的逻辑,是十分困难,因为它大部分函数之间都是相互调用的关系,只是参数的不同,结果就不同。
所以我后来想了个办法,就是只查看它的系统函数的调用,通过对调用顺序的研究,也可以大致知道它执行了哪些操作。
要想输出所有系统函数的调用,需要解决以下问题:
覆盖所有内置变量及类的函数,我们既要覆盖 window.console.clear() 这样的依附在实例上的函数,也要覆盖依附在类定义上的函数,如 window.HTMLAnchorElement.__proto__.click()
需要正确区分内置函数和自定义函数
经过搜索后,找到了区分内置函数的代码:
<span style="font-size: 16px;">// Used to resolve the internal `[[Class]]` of values<br/> var toString = Object.prototype.toString;<br/><br/> // Used to resolve the decompiled source of functions<br/> var fnToString = Function.prototype.toString;<br/><br/> // Used to detect host constructors (Safari > 4; really typed array specific)<br> var reHostCtor = /^\[object .+?Constructor\]$/;<br><br> // Compile a regexp using a common native method as a template.<br> // We chose `Object#toString` because there's a good chance it is not being mucked with.<br> var reNative = RegExp('^' +<br> // Coerce `Object#toString` to a string<br> String(toString)<br> // Escape any special regexp characters<br> .replace(/[.*+?^${}()|[\]\/\\]/g, '\\$&')<br> // Replace mentions of `toString` with `.*?` to keep the template generic.<br> // Replace thing like `for ...` to support environments like Rhino which add extra info<br> // such as method arity.<br> .replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'<br> );<br><br> function isNative(value) {<br> var type = typeof value;<br> return type == 'function'<br> // Use `Function#toString` to bypass the value's own `toString` method<br> // and avoid being faked out.<br> ? reNative.test(fnToString.call(value))<br> // Fallback to a host object check because some environments will represent<br> // things like typed arrays as DOM methods which may not conform to the<br> // normal native pattern.<br> : (value && type == 'object' && reHostCtor.test(toString.call(value))) || false;<br> }<br></span>
然后结合网上的资料,写出了递归覆盖内置函数的代码:
<span style="font-size: 16px;">function wrapit(e) {<br> if (e.__proto__) {<br> wrapit(e.__proto__);<br> }<br> for (var a in e) {<br> try {<br> e[a];<br> } catch (e) {<br> // pass<br> continue;<br> }<br> var prop = e[a];<br> if (!prop || prop._w) continue;<br><br> prop = e[a];<br> if (typeof prop == 'function' && isNative(prop)) {<br> e[a] = (function (name, func) {<br> return function () {<br> var args = [].splice.call(arguments,0); // convert arguments to array<br> if (false && name == 'getElementsByTagName' && args[0] == 'iframe') {<br> } else {<br> console.error((new Date).toISOString(), [this], name, args);<br> }<br> if (name == 'querySelectorAll') {<br> //alert('querySelectorAll');<br> }<br> return func.apply(this, args);<br> };<br> })(a, prop);<br> e[a]._w = true;<br> };<br> }<br>}<br></span>
使用的时候只需要:
<span style="font-size: 16px;">wrapit(window);<br>wrapit(document);<br></span>
然后模拟一下正常的操作,触发 PopUnder 就可以看到它的调用过程了。
参考资料:
A Beginners’ Guide to Obfuscation Detect if function is native to browser Detect if a Function is Native Code with JavaScript
以上内容就是Javascript逆向与反逆向的教程,希望能帮助到大家。
相关推荐:
JavaScript中undefined与null的区别详解
以上是Javascript逆向与反逆向的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

如何使用WebSocket和JavaScript实现在线语音识别系统引言:随着科技的不断发展,语音识别技术已经成为了人工智能领域的重要组成部分。而基于WebSocket和JavaScript实现的在线语音识别系统,具备了低延迟、实时性和跨平台的特点,成为了一种被广泛应用的解决方案。本文将介绍如何使用WebSocket和JavaScript来实现在线语音识别系

人脸检测识别技术已经是一个比较成熟且应用广泛的技术。而目前最为广泛的互联网应用语言非JS莫属,在Web前端实现人脸检测识别相比后端的人脸识别有优势也有弱势。优势包括减少网络交互、实时识别,大大缩短了用户等待时间,提高了用户体验;弱势是:受到模型大小限制,其中准确率也有限。如何在web端使用js实现人脸检测呢?为了实现Web端人脸识别,需要熟悉相关的编程语言和技术,如JavaScript、HTML、CSS、WebRTC等。同时还需要掌握相关的计算机视觉和人工智能技术。值得注意的是,由于Web端的计

股票分析必备工具:学习PHP和JS绘制蜡烛图的步骤,需要具体代码示例随着互联网和科技的快速发展,股票交易已经成为许多投资者的重要途径之一。而股票分析是投资者决策的重要一环,其中蜡烛图被广泛应用于技术分析中。学习如何使用PHP和JS绘制蜡烛图将为投资者提供更多直观的信息,帮助他们更好地做出决策。蜡烛图是一种以蜡烛形状来展示股票价格的技术图表。它展示了股票价格的

WebSocket与JavaScript:实现实时监控系统的关键技术引言:随着互联网技术的快速发展,实时监控系统在各个领域中得到了广泛的应用。而实现实时监控的关键技术之一就是WebSocket与JavaScript的结合使用。本文将介绍WebSocket与JavaScript在实时监控系统中的应用,并给出代码示例,详细解释其实现原理。一、WebSocket技

如何利用JavaScript和WebSocket实现实时在线点餐系统介绍:随着互联网的普及和技术的进步,越来越多的餐厅开始提供在线点餐服务。为了实现实时在线点餐系统,我们可以利用JavaScript和WebSocket技术。WebSocket是一种基于TCP协议的全双工通信协议,可以实现客户端与服务器的实时双向通信。在实时在线点餐系统中,当用户选择菜品并下单

如何使用WebSocket和JavaScript实现在线预约系统在当今数字化的时代,越来越多的业务和服务都需要提供在线预约功能。而实现一个高效、实时的在线预约系统是至关重要的。本文将介绍如何使用WebSocket和JavaScript来实现一个在线预约系统,并提供具体的代码示例。一、什么是WebSocketWebSocket是一种在单个TCP连接上进行全双工

逆向坍塌面包房行动将于3月22日发售,很多玩家想知道逆向坍塌面包房行动多少钱,首周有折扣,价格为基础版80.36元,豪华版178.76元,具体内容一起来看看这篇逆向坍塌面包房行动价格介绍。逆向坍塌面包房行动价格答:首发折扣价格为基础版80.36元,豪华版178.76元。1、逆向坍塌面包房行动已经确定将于3月22日发售,首周有折扣,价格为基础版80.36元,豪华版178.76元。2、这是云母组早期作品面包房少女的重制版本,在画面、剧情、玩法上有很大的创新。3、故事发生在近未来时代,由于坍塌液污染所

JavaScript和WebSocket:打造高效的实时天气预报系统引言:如今,天气预报的准确性对于日常生活以及决策制定具有重要意义。随着技术的发展,我们可以通过实时获取天气数据来提供更准确可靠的天气预报。在本文中,我们将学习如何使用JavaScript和WebSocket技术,来构建一个高效的实时天气预报系统。本文将通过具体的代码示例来展示实现的过程。We
