Maison > interface Web > js tutoriel > Javascript inversé et anti-inverse

Javascript inversé et anti-inverse

小云云
Libérer: 2017-12-05 10:25:51
original
6769 Les gens l'ont consulté

popunderjs avait à l'origine du code open source sur github, mais plus tard, on estime que l'auteur a découvert l'énorme valeur commerciale de cette demande, il a donc simplement arrêté l'open source et facturé directement. Alors maintenant, si nous voulons étudier son plan de mise en œuvre, nous ne pouvons qu'aller sur le site officiel pour récupérer son code source.

Structure des fichiers

script.js est la fonction principale, qui implémente toutes les fonctions de popunder et définit plusieurs méthodes API

license.demo.js est un fichier d'autorisation Ce n'est qu'avec ce fichier que vous pouvez appeler avec succès les méthodes dans script.js

Empêcher d'être inversé

Pour qu'un code d'une telle valeur commerciale soit ouvertement utilisé par vous, vous devez considérer la question de son inversion. Voyons comment cela fonctionne contre l'ingénierie inverse.

Tout d'abord, ouvrez la console et trouvez 2 problèmes :

  1. Tous les contenus de la console sont effacés à plusieurs reprises, uniquement cette phrase est affichée : La console a été effacée script.js?0.5309098417125133:1

  2. Le débogage du point d'arrêt ne peut pas être effectué car une fois la fonction de débogage du point d'arrêt activée, dirigé vers un fonction anonyme (function() {debugger})

En d'autres termes, la méthode de débogage de point d'arrêt couramment utilisée ne peut plus être utilisée, nous ne pouvons que jeter un oeil au code source et voyez si vous pouvez comprendre sa logique. Cependant, son code source est comme ceci :

<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>
Copier après la connexion

On voit que le code source est impossible à lire, il faut donc encore trouver un moyen de briser ses mesures anti-retour.

Utilisez des outils pour déchiffrer intelligemment l'ingénierie anti-inverse

Tout d'abord, vérifiez quelles opérations il a effectuées étape par étape en mode de débogage de point d'arrêt, et tout à coup J'ai découvert un tel morceau de code :

<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>
Copier après la connexion

Ce code comporte principalement deux parties. La première consiste à juger si la console est ouverte via la fonction b() dans le bloc try {}. Si c'est le cas, elle s'appellera à plusieurs reprises. . Entrez le point d'arrêt du débogueur pour interférer avec notre débogage. Si la console n'est pas ouverte, l'appel du débogueur lèvera une exception. À ce moment, définissez le minuteur dans le bloc catch {} et appelez à nouveau la fonction b() après 5 secondes.

De cette façon, tout commence réellement à partir de la fonction setTimeout (car la fonction b() est entièrement un appel de fermeture et ne peut pas être interrompue depuis le monde extérieur), donc tant que setTimeout est appelé , cette boucle infinie peut être interrompue en ne la laissant pas s'exécuter.

Il nous suffit donc simplement de remplacer setTimeout... Par exemple :

<span style="font-size: 16px;">window._setTimeout = window.setTimeout;<br>window.setTimeout = function () {};<br></span>
Copier après la connexion

Mais ! Cette opération ne peut pas être effectuée dans la console ! Car lorsque vous ouvrirez la console, vous serez forcément aspiré dans la boucle infinie de la fonction b(). Il ne sert à rien de remplacer setTimeout pour le moment.

À ce moment-là, notre outil TamperMonkey entre en jeu, l'écriture du code dans le script TM peut être exécutée même sans ouvrir la console.

TM Une fois le script écrit, actualisez la page, attendez qu'elle soit complètement chargée, puis ouvrez la console. À ce moment, le débogueur n'apparaîtra plus !

Ensuite, c'est au tour de la console d'actualiser le code

Cliquez sur le lien sur le côté droit de la console a été effacé pour localiser le code spécifique . Cliquez sur {} pour embellir le code compressé et constatez qu'il utilise réellement setInterval pour appeler à plusieurs reprises console.clear() pour effacer la console et afficher le message

La console a été effacée

, mais notez que setInterval ne peut pas. être directement remplacé car cette fonction a également des utilisations importantes ailleurs.

Nous pouvons donc empêcher son comportement d'effacement de l'écran en remplaçant la fonction console.clear() et en filtrant les informations du journal.

est également écrit dans le script TamperMonkey, code :

<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>
Copier après la connexion

La raison pour laquelle l'erreur est utilisée pour afficher des informations est de visualiser sa pile d'appels, ce qui est utile pour comprendre la logique du programme.

Fondamentalement, après avoir terminé ces tâches, ce code peut être débogué normalement comme un programme ordinaire. Mais il existe un autre problème. Son code principal est souvent obscurci et chiffré, il est donc très difficile à déboguer. Parlons brièvement du processus.

Première méthode de cryptage d'obscurcissement : masquer les appels de méthode, réduire la lisibilité

De licence.demo.js, vous pouvez voir un morceau de code en bas début Ça se passe comme ça :

<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>
Copier après la connexion

通过跟踪执行,可以发现 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>
Copier après la connexion

混淆加密方法二:将函数定义加入到证书验证流程

license.demo.js 中有多处这样的代码:

<span style="font-size: 16px;">a[&#39;RegExp&#39;](&#39;/R[\S]{4}p.c\wn[\D]{5}t\wr/&#39;,&#39;g&#39;)[&#39;test&#39;](T + &#39;&#39;)<br/></span>
Copier après la connexion

这里的 a 代表 window,T 代表某个函数, T + '' 的作用是把 T 函数的定义转成字符串,所以这段代码的意思其实是,验证 T 函数的定义中是否包含某些字符。

每次成功的验证,都会返回一个特定的值,这些个特定的值就是解密核心证书的参数。

可能是因为我重新整理了代码格式,所以在重新运行的时候,这个证书一直运行不成功,所以后来就放弃了通过证书来突破的方案。

逆向思路:输出所有函数调用和参数

通过断点调试,我们可以发现,想一步一步深入地搞清楚这整个程序的逻辑,是十分困难,因为它大部分函数之间都是相互调用的关系,只是参数的不同,结果就不同。

所以我后来想了个办法,就是只查看它的系统函数的调用,通过对调用顺序的研究,也可以大致知道它执行了哪些操作。

要想输出所有系统函数的调用,需要解决以下问题:

  1. 覆盖所有内置变量及类的函数,我们既要覆盖 window.console.clear() 这样的依附在实例上的函数,也要覆盖依附在类定义上的函数,如 window.HTMLAnchorElement.__proto__.click()

  2. 需要正确区分内置函数和自定义函数

经过搜索后,找到了区分内置函数的代码:

<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>
Copier après la connexion

 

然后结合网上的资料,写出了递归覆盖内置函数的代码:

<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>
Copier après la connexion

 

使用的时候只需要:

<span style="font-size: 16px;">wrapit(window);<br>wrapit(document);<br></span>
Copier après la connexion

 

然后模拟一下正常的操作,触发 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中confirm()方法的使用介绍

JavaScript中的后退与刷新的实例详解

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal