Home >Web Front-end >JS Tutorial >js experience sharing JavaScript anti-debugging skills

js experience sharing JavaScript anti-debugging skills

亚连
亚连Original
2018-05-31 10:41:191879browse

In this article, I plan to summarize with you some content about JavaScript anti-debugging techniques. It is worth mentioning that some of these methods have been widely used by cybercriminals in malware. Friends in need can refer to

Before this, I have been studying JavaScript-related anti-debugging techniques. . But when I searched for relevant information on the Internet, I found that there were not many articles about this on the Internet, and even if there were, they were very incomplete. So in this article, I plan to summarize with you some JavaScript anti-debugging techniques. It is worth mentioning that some of these methods have been widely used by cybercriminals in malware.

For JavaScript, you only need to spend a little time debugging and analyzing, and you will be able to understand the functional logic of the JavaScript code segment. What we are going to discuss can make it more difficult for those who want to analyze your JavaScript code. However, our technology has nothing to do with code obfuscation. Our main focus is on how to make active debugging of the code more difficult.

The technical methods to be introduced in this article are roughly as follows:

1. Detect unknown execution environments (our code only wants to be executed in the browser);

2. Detect debugging tools (such as DevTools);

3. Code integrity control;

4. Flow integrity control;

5. Anti-emulation;

In short, if we detect an "abnormal" situation, the running flow of the program will change and jump to the fake code block and "hide" the real function code.

1. Function redefinition

This is one of the most basic and commonly used code anti-debugging techniques. In JavaScript, we can redefine functions used to collect information. For example, the console.log() function can be used to collect information such as functions and variables and display it in the console. If we redefine this function, we can modify its behavior and hide specific information or display fake information.

We can run this function directly in DevTools to understand its function:

console.log("HelloWorld");
var fake = function() {};
window['console']['log']= fake;
console.log("Youcan't see me!");

After running we will see:

VM48:1 Hello World

You will find that the second message is not displayed because we have redefined this function, that is, "disabled" its original function. But we can also make it display fake information. For example:

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!");

If everything is normal:

Normal function
VM117:11 This is unaltered
VM117: 9 Spoofed!
VM117:11 Bye bye!

In fact, in order to control the way the code is executed, we can also modify the function of the function in a smarter way. For example, we can build a code snippet based on the above code and redefine the eval function. We can pass JavaScript code to the eval function, and the code will be evaluated and executed. If we redefine this function, we can run different code:

//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(&#39;Weshould see this...&#39;)");
//Now we should see the execution of a for loop instead of what is expected
eval("console.log(&#39;Too1337 for you!&#39;)");

The results are as follows:

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

As said before, although this method is very clever, it is also a very basic and common method, so Relatively easy to detect.

2. Breakpoints

In order to help us understand the function of the code, JavaScript debugging tools (such as DevTools) can prevent script code execution by setting breakpoints. Breakpoints are also the most basic in code debugging.

If you have studied debuggers or x86 architecture, you may be familiar with the 0xCC instruction. In JavaScript, we have a similar directive called debugger. When we declare the debugger function in the code, the script code will stop running at the debugger instruction. For example:

console.log("Seeme!");
debugger;
console.log("Seeme!");

Many commercial products will define an infinite loop debugger instruction in the code, but some browsers will block this code, and some No. The main purpose of this method is to annoy people who want to debug your code, because the infinite loop means that the code will constantly pop up a window asking you if you want to continue running the script code:

setTimeout(function(){while (true) {eval("debugger")

3. Time difference

This is a time-based anti-debugging technique borrowed from traditional anti-reverse technology. When the script is executed in a tool environment such as DevTools, the running speed will be very slow (it takes a long time), so we can judge whether the script is currently being debugged based on the running time. For example, we can measure the running time between two set points in the code and then use this value as a reference. If the running time exceeds this value, the script is currently running in the debugger.

The demo code is as follows:

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);

4. DevTools detection (Chrome)

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

简单的概念验证代码如下:

let p = document.createElement(&#39;p&#39;);
let loop = setInterval(() => {
  console.log(p);
  console.clear();
});
Object.defineProperty(p,"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(&#39;p&#39;);

接下来,我们可以在控制台中记录下相关参数和信息:

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

我们可以利用这些信息并通过拦截某些特定函数来调试代码,但是本文的主要目的是为了介绍反调试技术,那么我们如何检测“对方”是否使用了代理对象呢?其实这就是一场“猫抓老鼠”的游戏,比如说,我们可以使用相同的代码段,然后尝试调用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] }"

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

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

解决Vue.js 2.0 有时双向绑定img src属性失败的问题

iview table render集成switch开关的实例

JavaScript实现区块链


The above is the detailed content of js experience sharing JavaScript anti-debugging skills. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn