この記事では、JavaScript のデバッグ対策テクニックをいくつか要約する予定です。これらの手法の一部はマルウェアでサイバー犯罪者によって広く使用されているので、困っている友人は参考にしてください
これに先立って、私は JavaScript 関連のデバッグ対策手法を研究してきました。しかし、インターネットで関連情報を検索してみると、これに関する記事はインターネット上にあまりなく、あっても非常に不完全であることがわかりました。そこでこの記事では、JavaScript のデバッグ対策テクニックをいくつかまとめてみたいと思います。これらの手法の一部はサイバー犯罪者によってマルウェアで広く使用されていることに言及する価値があります。
JavaScript の場合、デバッグと分析に少し時間を費やすだけで、JavaScript コードセグメントの機能ロジックを理解できるようになります。これから説明する内容は、JavaScript コードを分析したい人にとってはさらに困難になる可能性があります。ただし、私たちのテクノロジーはコードの難読化とは何の関係もありません。私たちの主な焦点は、コードのアクティブなデバッグをより困難にする方法です。
この記事で紹介されている技術的な方法は次のとおりです:
1. 未知の実行環境を検出します (コードはブラウザー内でのみ実行されます)。 3. コードの整合性制御
4. フローの整合性制御
つまり、「異常な」状況を検出すると、プログラムの実行フローが変更され、偽のコード ブロックにジャンプします。そして実際の関数コードを「隠す」。
1. 関数の再定義これは、最も基本的で一般的に使用されるコードのデバッグ対策手法の 1 つです。 JavaScript では、情報を収集するために使用される関数を再定義できます。たとえば、console.log() 関数を使用すると、関数や変数などの情報を収集し、コンソールに表示できます。この関数を再定義すると、その動作を変更して、特定の情報を非表示にしたり、偽の情報を表示したりすることができます。
この関数を DevTools で直接実行して、その機能を理解することができます:
console.log("HelloWorld"); var fake = function() {}; window['console']['log']= fake; console.log("Youcan't see me!");
実行後、次のように表示されます:
VM48:1 Hello World
2 番目のメッセージが表示されていないことがわかります。この関数を再定義しました。つまり、元の関数を「無効」にしました。ただし、偽の情報を表示させることもできます。例:
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!");
すべてが正常の場合:
通常の関数
VM117:11 これは変更されていません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:1 これが表示されるはずです…VM147:10
VM147:11VM147: 12
VM147:13VM147:14
VM147:15
VM147:16
VM147:17
VM147:18
VM147:19
前に述べたように、この方法は非常に賢いですが、非常に基本的で一般的な方法でもあります。比較的簡単に検出できます。
2. ブレークポイント
コードの機能を理解しやすくするために、JavaScript デバッグ ツール (DevTools など) はブレークポイントを設定することでスクリプト コードの実行を防ぐことができます。また、ブレークポイントはコード デバッグの最も基本的なものでもあります。 。
デバッガーや x86 アーキテクチャを学習したことがある場合は、0xCC 命令に精通しているかもしれません。 JavaScript には、debugger と呼ばれる同様のディレクティブがあります。コード内でデバッガ関数を宣言すると、スクリプト コードはデバッガの命令で実行を停止します。例:
console.log("Seeme!"); debugger; console.log("Seeme!");
多くの商用製品はコード内に無限ループ デバッガー命令を定義しますが、このコードをブロックするブラウザーとブロックしないブラウザーがあります。このメソッドの主な目的は、コードをデバッグしたい人を困らせることです。無限ループにより、コードはスクリプト コードの実行を続行するかどうかを尋ねるウィンドウが常にポップアップ表示されるためです。
setTimeout(function(){while (true) {eval("debugger")
これは、従来のアンチリバース技術から借用した時間ベースのアンチデバッグ技術です。 DevToolsなどのツール環境でスクリプトを実行すると、実行速度が非常に遅くなる(時間がかかる)ため、実行時間からデバッグ中かどうかを判断できます。たとえば、コード内の 2 つの設定ポイント間の実行時間を測定し、この値を基準として使用できます。実行時間がこの値を超えている場合、スクリプトは現在デバッガで実行されています。
デモコードは次のとおりです:
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);
这项技术利用的是p元素中的id属性,当p元素被发送至控制台(例如console.log(p))时,浏览器会自动尝试获取其中的元素id。如果代码在调用了console.log之后又调用了getter方法,说明控制台当前正在运行。
简单的概念验证代码如下:
let p = document.createElement('p'); 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('p');
接下来,我们可以在控制台中记录下相关参数和信息:
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开关的实例
以上がjs エクスペリエンスを共有する JavaScript のデバッグ対策スキルの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。