JavaScript的反调试技术(下篇) - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

JavaScript的反调试技术(下篇)

fanyeee web安全 2018-02-27 09:10:20
310369
收藏

导语:

在本文的上篇中,我们为读者介绍了与JavaScript反调试有关的一些技巧,其中包括函数重定义、断点、时间差异、DevTools检测和执行流程完整性的隐式控制等,接下来,我们将为读者介绍更多的反调试技巧。

0x06  代码完整性的隐式控制

在前面的“0x01函数重定义”部分,我们提到可以在JavaScript中使用toString()方法检索函数的代码。正如我们所说的,这对检查一个函数是否被重新定义是非常有用的,事实上,同样也可以使用这个思路来检测函数的代码是否被修改过。

其中,一种有效的方法是计算函数或代码块的哈希值,并将其与预知的哈希表进行比较。但是,这种方法确实有点笨拙。 更现实和有效的方法,是之前使用的跟踪堆栈的策略。 我们可以计算一段代码的哈希值,并将其用作解密其他代码块的密钥。

为了实现隐式完整性控制,最巧妙的想法是md5碰撞。这个想法是由@cgvwzq创造的,就在去年夏天几杯啤酒下肚后,一个天才的想法就这样诞生了。简单来说,我们可以创建这样的函数:在函数内部检测自己的md5。为了执行函数内部的检查,我们需要使用碰撞(我们想创建类似function(){ if (md5(arguments.callee.toString() === '<md5>') code_function; }这样的函数)。

这种技术背后的思想与用于生成图像文件的概念相同,这些图像文件显示的就是自己的md5校验和。 这是一个经典的例子:一个显示自己的md5校验和的gif。

md5.gif

关于如何创建这种类型的碰撞,有大量的文章(甚至在PoC||GTFO中还有实际例子)可供参考,但是,我阅读的关于这方面的第一篇文章,使用的是PHP语言。您可以预先计算生成碰撞所需的块。事实上,这是由@cgvwzq创建的一个例子,就是通过这种方式来检查函数内容的完整性。

正如我们之前所说的,这种技术想要发挥作用,必须结合使用强大的混淆技术。

0x07  代理对象

代理对象是JavaScript世界中引入的最有用的工具之一。 这个对象可以用来窥探其他对象,改变其行为(如hook),或者在某些情况下触发一个动作。 例如,如果我们想跟踪每个对document.createElement的调用并记录这些信息,我们就可以创建一个代理对象:

const handler = { // Our hook to keep the track
    apply: function (target, thisArg, args){
        console.log("Intercepted a call to createElement with args: " + args);
        return target.apply(thisArg, args)
    }
}
document.createElement = new Proxy(document.createElement, handler) // Create our proxy object with our hook ready to intercept
document.createElement('div');

然后,当调用createElement时,我们会发现其参数将被显示到控制台中:

VM64:3 Intercepted a call to createElement with args: div

太棒了!这样的话,我们就可以通过拦截一些众所周知的函数(làstrace/ltrace)来调试代码了。但正如在“0x01函数重定义”一节中看到的那样,我们可以使用该方法来隐藏或伪造信息,或者只是运行与我们所看到的代码不同的代码(可以直接替换示例中hook内部的逻辑)。这种函数hooking技术的威力远胜于简单的重定义技术。

但是,在本文中,我们关注的重点是提供一些反调试方面的思路和方法,所以......我们可以检测分析人员是否正在使用代理对象吗?是的,我们可以,但这是一个猫捉老鼠的游戏。例如,使用相同的代码片段,我们可以尝试调用toString方法来捕获异常:

// Call a "virgin" createElement:
try {
    document.createElement.toString();
} catch(e){
    console.log("I saw your proxy!");
}

如果一切正常,则:

"function createElement() { [native code] }"

但是,如果我们使用代理……

//Then apply the hook
const handler = {
    apply: function (target, thisArg, args){
        console.log("Intercepted a call to createElement with args: " + args);
        return target.apply(thisArg, args)
    }
}
document.createElement = new Proxy(document.createElement, handler);
//Call our not-so-virgin-after-that-party createElement
try {
    document.createElement.toString();
} catch(e) {
    console.log("I saw your proxy!");
}

是的,它的确可以检测到这个代理:

VM391:13 I saw your proxy!

如前所说,这只是一个鼠猫游戏。实际上,我们可以添加toString方法:

const handler = {
    apply: function (target, thisArg, args){
        console.log("Intercepted a call to createElement with args: " + args);
        return target.apply(thisArg, args)
    }
}
document.createElement = new Proxy(document.createElement, handler);
document.createElement = Function.prototype.toString.bind(document.createElement); //Add toString
//Call our not-so-virgin-after-that-party createElement
try {
    document.createElement.toString();
} catch(e) {
    console.log("I saw your proxy!");
}

现在,我们的检测将失败:

"function createElement() { [native code] }"

0x07限制环境

正如前文所述,有时需要检测代码是否在正确的环境中执行。我们所说的“正确的环境”是:

·代码运行在浏览器中(不是模拟器、不是NodeJS,...)

·代码运行在目标域/资源中(不是本地服务器)

例如,我们可以通过进行一些简单的检查工作来验证代码是否在本地执行:

// Pretty stupid idea found in commercial software
if (location.hostname === "localhost" || location.hostname === "127.0.0.1" || location.hostname === "") {
    console.log("Don't run me here!")
}

如果我们在本地html中运行上面的JavaScript代码段,将看到以下消息:

VM28:3 Don't run me here!

按照这个思路,另一个方法是检测用来打开文档的handler(类似于if(location.protocol =='file:'){...}),或者尝试通过HTTP请求检测其他资源(图像、css等)是否可用。 当然,所有这些方法都非常容易绕过。

一个更有趣的想法是避免代码在NodeJS中执行(或者就像我们在本文前面所做的那样:修改执行流程,使其进入伪造的路径)。 虽然这很危险,但已经有人在使用NodeJS解决JavaScript的各种挑战并绕过反暴力破解缓解措施

我们可以设法检测只存在于浏览器上下文中的对象:

//Under NodeJS
   try {
..   console.log(window);
   } catch(e){
..      console.log("NodeJS detected!!!!");
   }
NodeJS detected!!!!

反之亦然:在NodeJS中,有一些对象也是浏览器上下文所不具备的。

//Under the browser
console.log(global)
VM104:1 Uncaught ReferenceError: global is not defined
    at <anonymous>:1:13
//Under NodeJS
  console.log(global)
{ console:
   Console {
     log: [Function: bound log],...
     ...

我们也可以搜索只存在于浏览器中的各种元数据。 在Panopticlick项目中可以看到这类想法的影子。

0x08 WebGL

这里,我们不会介绍WebGL内部的反逆向或混淆技术,因为这方面的内容可以从网上找到。相反,本文所介绍的是如何使用WebGL处理数据并与JavaScript交互:如果有人试图“模拟”我们的JavaScript代码,他就需要为其模拟器提供WebGL支持。

我们可以实现一个简单的算法(例如多色分形)来创建基于各种“种子”的图像,然后在预定位置提取像素的值,并将其用作密钥来解码代码块。我想在将来条件成熟的时候再深入讨论这个话题,所以这里只是一个存根:P

小结

我希望上文中介绍的各种技巧能够对读者有所帮助。如果读者了解其他技巧,或者发现文中的错误和待改进之处,请随时在我的twitter @TheXC3LL上发表评论。

最后,对@cgvwzq提供的帮助致以深深的谢意:)

Byt3z!

  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

 
本站4hou.com,所使用的字体和图片文字等素材部分来源于原作者或互联网共享平台。如使用任何字体和图片文字有侵犯其版权所有方的,嘶吼将配合联系原作者核实,并做出删除处理。
©2022 北京嘶吼文化传媒有限公司 京ICP备16063439号-1 本站由 提供云计算服务