阅读更多

2顶
0踩

编程语言

转载新闻 JavaScript反调试技巧

2018-03-09 10:28 by 副主编 jihong10102006 评论(0) 有10333人浏览
写在前面的话

在此之前,我一直都在研究JavaScript相关的反调试技巧。但是当我在网上搜索相关资料时,我发现网上并没有多少关于这方面的文章,而且就算有也是非常不完整的那种。所以在这篇文章中,我打算跟大家总结一下关于JavaScript反调试技巧方面的内容。值得一提的是,其中有些方法已经被网络犯罪分子广泛应用到恶意软件之中了。

对于JavaScript来说,你只需要花一点时间进行调试和分析,你就能够了解到JavaScript代码段的功能逻辑。而我们所要讨论的内容,可以给那些想要分析你JavaScript代码的人增加一定的难度。不过我们的技术跟代码混淆无关,我们主要针对的是如何给代码主动调试增加困难。

本文所要介绍的技术方法大致如下:
  • 检测未知的执行环境(我们的代码只想在浏览器中被执行);
  • 检测调试工具(例如DevTools);
  • 代码完整性控制;
  • 流完整性控制;
  • 反模拟;
简而言之,如果我们检测到了“不正常”的情况,程序的运行流程将会改变,并跳转到伪造的代码块,并“隐藏”真正的功能代码。

一、函数重定义

这是一种最基本也是最常用的代码反调试技术了。在JavaScript中,我们可以对用于收集信息的函数进行重定义。比如说,console.log()函数可以用来收集函数和变量等信息,并将其显示在控制台中。如果我们重新定义了这个函数,我们就可以修改它的行为,并隐藏特定信息或显示伪造的信息。

我们可以直接在DevTools中运行这个函数来了解其功能:
console.log("HelloWorld");
var fake = function() {};
window['console']['log']= fake;
console.log("Youcan't see me!");

运行后我们将会看到:
VM48:1 Hello World

你会发现第二条信息并没有显示,因为我们重新定义了这个函数,即“禁用”了它原本的功能。但是我们也可以让它显示伪造的信息。比如说这样:
console.log("Normalfunction");
//First we save a reference to the original console.log function
var original = window['console']['log'];
//Next we create our fake function
//Basicly we check the argument and if match we call original function with otherparam.
// If there is no match pass the argument to the original function
var fake = function(argument) {
    if (argument === "Ka0labs") {
        original("Spoofed!");
    } else {
        original(argument);
    }
}
// We redefine now console.log as our fake function
window['console']['log']= fake;
//Then we call console.log with any argument
console.log("Thisis unaltered");
//Now we should see other text in console different to "Ka0labs"
console.log("Ka0labs");
//Aaaand everything still OK
console.log("Byebye!");

如果一切正常的话:
Normal function
VM117:11 This is unaltered
VM117:9 Spoofed!
VM117:11 Bye bye!

实际上,为了控制代码的执行方式,我们还能够以更加聪明的方式来修改函数的功能。比如说,我们可以基于上述代码来构建一个代码段,并重定义eval函数。我们可以把JavaScript代码传递给eval函数,接下来代码将会被计算并执行。如果我们重定义了这个函数,我们就可以运行不同的代码了:
//Just a normal eval
eval("console.log('1337')");
//Now we repat the process...
var original = eval;
var fake = function(argument) {
    // If the code to be evaluated contains1337...
    if (argument.indexOf("1337") !==-1) {
        // ... we just execute a different code
        original("for (i = 0; i < 10;i++) { console.log(i);}");
    }
    else {
        original(argument);
    }
}
eval= fake;
eval("console.log('Weshould see this...')");
//Now we should see the execution of a for loop instead of what is expected
eval("console.log('Too1337 for you!')");

运行结果如下:
1337
VM146:1We should see this…
VM147:10
VM147:11
VM147:12
VM147:13
VM147:14
VM147:15
VM147:16
VM147:17
VM147:18
VM147:19

正如之前所说的那样,虽然这种方法非常巧妙,但这也是一种非常基础和常见的方法,所以比较容易被检测到。

二、断点

为了帮助我们了解代码的功能,JavaScript调试工具(例如DevTools)都可以通过设置断点的方式阻止脚本代码执行,而断点也是代码调试中最基本的了。

如果你研究过调试器或者x86架构,你可能会比较熟悉0xCC指令。在JavaScript中,我们有一个名叫debugger的类似指令。当我们在代码中声明了debugger函数后,脚本代码将会在debugger指令这里停止运行。比如说:
console.log("Seeme!");
debugger;
console.log("Seeme!");

很多商业产品会在代码中定义一个无限循环的debugger指令,不过某些浏览器会屏蔽这种代码,而有些则不会。这种方法的主要目的就是让那些想要调试你代码的人感到厌烦,因为无限循环意味着代码会不断地弹出窗口来询问你是否要继续运行脚本代码:
setTimeout(function(){while (true) {eval("debugger")

三、时间差异

这是一种从传统反逆向技术那里借鉴过来的基于时间的反调试技巧。当脚本在DevTools等工具环境下执行时,运行速度会非常慢(时间久),所以我们就可以根据运行时间来判断脚本当前是否正在被调试。比如说,我们可以通过测量代码中两个设置点之间的运行时间,然后用这个值作为参考,如果运行时间超过这个值,说明脚本当前在调试器中运行。

演示代码如下:
set Interval(function(){
  var startTime = performance.now(), check,diff;
  for (check = 0; check < 1000; check++){
    console.log(check);
    console.clear();
  }
  diff = performance.now() - startTime;
  if (diff > 200){
    alert("Debugger detected!");
  }
},500);

四、DevTools检测(Chrome)

这项技术利用的是div元素中的id属性,当div元素被发送至控制台(例如console.log(div))时,浏览器会自动尝试获取其中的元素id。如果代码在调用了console.log之后又调用了getter方法,说明控制台当前正在运行。

简单的概念验证代码如下:
let div = document.createElement('div');
let loop = setInterval(() => {
    console.log(div);
    console.clear();
});
Object.defineProperty(div,"id", {get: () => {
    clearInterval(loop);
    alert("Dev Tools detected!");
}});

五、隐式流完整性控制

当我们尝试对代码进行反混淆处理时,我们首先会尝试重命名某些函数或变量,但是在JavaScript中我们可以检测函数名是否被修改过,或者说我们可以直接通过堆栈跟踪来获取其原始名称或调用顺序。

arguments.callee.caller可以帮助我们创建一个堆栈跟踪来存储之前执行过的函数,演示代码如下:
function getCallStack() {
    var stack = "#", total = 0, fn =arguments.callee;
    while ( (fn = fn.caller) ) {
        stack = stack + "" +fn.name;
        total++
    }
    return stack
}
function test1() {
    console.log(getCallStack());
}
function test2() {
    test1();
}
function test3() {
    test2();
}
function test4() {
    test3();
}
test4();

注意:源代码的混淆程度越强,这个技术的效果就越好。

六、代理对象

代理对象是目前JavaScript中最有用的一个工具,这种对象可以帮助我们了解代码中的其他对象,包括修改其行为以及触发特定环境下的对象活动。比如说,我们可以创建一个嗲哩对象并跟踪每一次document.createElemen调用,然后记录下相关信息:
const handler = { // Our hook to keep the track
    apply: function (target, thisArg, args){
        console.log("Intercepted a call tocreateElement with args: " + args);
        return target.apply(thisArg, args)
    }
} 
document.createElement= new Proxy(document.createElement, handler) // Create our proxy object withour hook ready to intercept
document.createElement('div');

接下来,我们可以在控制台中记录下相关参数和信息:
VM64:3 Intercepted a call to createElement with args: div

我们可以利用这些信息并通过拦截某些特定函数来调试代码,但是本文的主要目的是为了介绍反调试技术,那么我们如何检测“对方”是否使用了代理对象呢?其实这就是一场“猫抓老鼠”的游戏,比如说,我们可以使用相同的代码段,然后尝试调用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
consthandler = {
    apply: function (target, thisArg, args){
        console.log("Intercepted a call tocreateElement with args: " + args);
        return target.apply(thisArg, args)
    }
}
document.createElement= new Proxy(document.createElement, handler);
 
//Callour 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 tocreateElement 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
//Callour not-so-virgin-after-that-party createElement
try {
    document.createElement.toString();
}catch(e) {
    console.log("I saw your proxy!");
}

现在我们就没办法检测到了:
"function createElement() { [native code] }"

就像我说的,这就是一场“猫抓老鼠“的游戏。

总结

希望我所收集到的这些技巧可以对大家有所帮助,如果你有更好的技巧想跟大家分享,可以直接在文章下方的评论区留言,或者在Twitter上艾特我(@TheXC3LL)。
  • 大小: 52 KB
来自: freebuf
2
0
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • 设置来电铃声android,设置默认来电铃声 android

    public void setMyRingtone(){File k = new File("/sdcard/Shall We ... // 设置歌曲路径ContentValues values = new ContentValues();values.put(MediaStore.MediaColumns.DATA, k.getAbsolutePath());values.put...

  • android 设置默认铃声,我的Android进阶之旅------&gt;Android系统设置默认来电铃声、闹钟铃声、通知铃声,android进阶...

    Android系统设置默认来电铃声、闹钟铃声、通知铃声,android进阶首先了解Android系统本身提供的默认铃声文件,这些文件都放在 /system/media/audio 目录下。/system/media/audio/ringtones系统来电铃声/system/media...

  • android 来电播放铃声,android 播放来电铃声

    上网查了一下,可以用mediaplayer播放自己的闹钟和来电铃声。十分感谢这万能的网络,这里也和大家分享一下。Uri alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);MediaPlayer ...

  • Android音频相关(八)来电铃声播放流程

    Android音频相关(四)设置铃声流程总结 一、铃声播放准备 二、铃声播放 三、总结 本文主要介绍的下面标红的播放流程,SystemUI的播放流程我们在另一篇文章进行介绍。 一、铃声播放准备 1.来电后最先会...

  • android 关闭来电铃声,Android删除除自定义铃声后,来电铃声显示是一串数字

    设置--声音--手机铃声--添加铃声--自定义铃声--进入Files音频--查看Ringtones列表,删除刚才设置的铃声或者进入File Manager--Internal shared storage--Ringtones,删除刚才设置的铃声[SOLUTION]出现之前现象的原因...

  • android闹钟铃声编码,Android 设置来电铃声、通知铃声、闹钟铃声中的坑

    其实这个功能百度一下有很多,但大部分都是同一篇文章,我们项目里最开始就是参考的百度的代码片段来实现的,但是有个bug,对同一个mp3文件设置来电铃声后,然后再设置成通知铃声或者闹钟铃声,那原来设置的来电铃声...

  • android 手机铃声设置铃声设置,Android 修改系统来电铃声

    // Set up the bell RingtoneManager.setActualDefaultRingtoneUri(this,RingtoneManager.TYPE_RINGTONE, newUri ); Toast.makeText(this, R.string.ring_set_succeed, Toast.LENGTH_SHORT).show(); } }如果不成功...

  • android 5.0 设置铃声,android5.0联系人铃声设置和来电读取分析

    android5.0联系人铃声设置和来电读取一,单个联系人的铃声设置流程1,联系人编辑界面下菜单设置来电铃声,会弹出一个ringtone列表供用户选择。从ContactEditorFragment开始,在doPickRingtone()中启动了...

  • Android 代码设置来电铃声

    1、如果读到的是音频文件路径,需要先将音乐...//设置--铃声的具体方法 public void setMyRingtone(String path) { File sdfile = new File(path); ContentValues values = new ContentValues(); values

  • Android RingtoneManager铃声管理

    Android RingtoneManager铃声管理 ringtonemanager提供铃声,通知,和其他类型的声音。它管理查询不同的媒体提供商和结合成一个光标。它还为每一个铃声提供了Ringtone 。我们通常称这些铃声 ringtones,然而TYPE_...

  • android6.0 来电流程,Android4.22来电铃声流程

    r = RingtoneManager.getRingtone(mContext, mCustomRingtoneUri); synchronized (Ringer.this) { if (!hasMessages(STOP_RING)) { mRingtone = r; } } } r = mRingtone; if (r != null && !hasMessages(STOP_RING)...

  • android 设置来电铃声

    亲测可用(记得加权限)... * 设置铃声  */  public static void setMyAlarm(Context ct,String path)  {  File sdfile = new File(path);   Uri uri = MediaStore.Audio.Media.getContentUriForPath(sdfi

  • 如何编写程序设置Android来电铃声

     我们在拿到新手机后通常会为其设置来年铃声,那么怎样通过代码来设置Android来电铃声,本文就为大家实例讲解下。  如果读到的是音频文件路径,需要先将音乐文件插入到多媒体库。 //设置--铃声的具体方法 ...

  • java设置铃声_我的Android进阶之旅------&gt;Android系统设置默认来电铃声、闹钟铃声、通知铃声...

    /system/media/audio/ringtones系统来电铃声/system/media/audio/notifications系统通知铃声/system/media/audio/alarms系统闹钟铃声自己下载的铃声。能够放在SD卡的music文件夹。主要包含:一般的铃...

  • android设置铃声 雪夜,Android 设置铃声

    // 设置显示的标题 intent.putExtra(android.media.RingtoneManager.EXTRA_RINGTONE_TITLE, " 设置来电铃声 " ); startActivityForResult(intent, Ringtone); } } }); btn2.setOnClickListener( new Button....

  • Android 仿通话来电界面

    **简介:**开发中需要模拟来电时的通话界面,仿照来电界面实现来电时播放铃声,界面通过动画模拟来电动效。 效果图: 自定义图片背景,图片由小变大的动态效果。 shap_circle.xml &lt;?xml version="1.0" ...

  • Android平台实现卡1卡2铃声分开设置

    Android从5.0之后开始支持双卡,但手机铃声不能分开设置,要实现此功能,可参考默认铃声增加一项,来电时根据sim卡获取对应的设置项即可。 文件frameworks/base/core/java/android/provider/Settings.java 中定义了...

  • python源码基于YOLOV5安全帽检测系统及危险区域入侵检测告警系统源码.rar

    本资源提供了一个基于YOLOv5的安全帽检测系统及危险区域入侵检测告警系统的Python源码 该系统主要利用深度学习和计算机视觉技术,实现了安全帽和危险区域入侵的实时检测与告警。具体功能如下: 1. 安全帽检测:系统能够识别并检测工人是否佩戴安全帽,对于未佩戴安全帽的工人,系统会发出告警信号,提醒工人佩戴安全帽。 2. 危险区域入侵检测:系统能够实时监测危险区域,如高空作业、机械设备等,对于未经授权的人员或车辆进入危险区域,系统会立即发出告警信号,阻止入侵行为,确保安全。 本资源采用了YOLOv5作为目标检测算法,该算法基于深度学习和卷积神经网络,具有较高的检测精度和实时性能。同时,本资源还提供了详细的使用说明和示例代码,便于用户快速上手和实现二次开发。 运行测试ok,课程设计高分资源,放心下载使用!该资源适合计算机相关专业(如人工智能、通信工程、自动化、软件工程等)的在校学生、老师或者企业员工下载,适合小白学习或者实际项目借鉴参考! 当然也可作为毕业设计、课程设计、课程作业、项目初期立项演示等。如果基础还行,可以在此代码基础之上做改动以实现更多功能,如增加多种安全帽和危险区域的识别、支持多种传感器数据输入、实现远程监控等。

  • 基于SpringBoot的响应式技术博客的设计和实现(源码+文档)

    本课题将许多当前比较热门的技术框架有机的集合起来,比如Spring boot、Spring data、Elasticsearch等。同时采用Java8作为主要开发语言,利用新型API,改善传统的开发模式和代码结构,实现了具有实时全文搜索、博客编辑、分布式文件存贮和能够在浏览器中适配移动端等功能的响应式技术博客。 本毕业设计选用SpringBoot框架,结合Thymeleaf,SpringData,SpringSecurity,Elasticsearch等技术,旨在为技术人员设计并实现一款用于记录并分享技术文档的技术博客。通过该技术博客,方便技术人员记录自己工作和学习过程中的点滴,不断地进行技术的总结和积累,从而提升自己的综合能力,并通过博客这一平台,把自己的知识、经验、教训分享给大家,为志同道合者提供一个相互交流、共同学习的平台,促使更多的人共同进步[9]。学习到别人的一些良好的设计思路、编码风格和优秀的技术能力,使笔者的设计初衷。本系统主要面向web端的用户,希望能给用户更多的学习和交流的选择。

  • javalab 3.zip

    javalab 3.zip

Global site tag (gtag.js) - Google Analytics