PopUnder研究:Javascript逆方向と逆方向

8637 ワード

縁起
最近PopUnderの実現方案を研究しています.Google検索js popunderを通じて出てきた第一ページの中にpopunderjs.comのウェブサイトがあります.その時見ましたが、これはpopunderソリューションを提供する会社です.また何ページかめくりました.市場でこの問題を解決できるのは2社だけです.この市場は基本的に独占型です.popundersjsは元々githubでソースコードがありましたが、後で作者がこの需要の巨大な商業価値を発見しました.だから今はその実現案を研究したいです.公式サイトでソースをかき集めるしかないです.
これはその一例ページです.http://code.ptcong.com/demos/bjp/demo.htmlはそれぞれいくつかの重要なファイルをロードしました.
http://code.ptcong.com/demos/bjp/script.js?0.3687041198903791
http://code.ptcong.com/demos/bjp/license.demo.js?0.31109710863616447
ファイル構造
script.jsは機能の主体で、popunderのすべての機能を実現して、複数のAPIの方法license.demo.jsを定義しました.このファイルがあれば、script.jsの中の方法をスムーズに呼び出すことができます.
逆行を防ぐ
このような商業的価値のあるコードは、このように公開して使っています.逆行される問題をよく考えなければなりません.私たちはどのように逆行しているかを見に来ました.まず、コンソールを開けて、2つの問題を発見します.
  • コンソールのすべての内容は繰り返しクリアされ、このような言葉だけが出力されました.
  • は、ブレークポイントデバッグ機能が有効になると、匿名関数Console was cleared script.js?0.5309098417125133:1
  • に向けられますので、ブレークポイントデバッグ機能を有効にすることができません.
    つまり、よく使われるブレークポイントのデバッグ方法はもう使えなくなりました.ソースコードを見て、そのロジックを理解できるかどうかを確認するしかないです.しかし、ソースコードはこうです.
        var a = typeof window === S[0] && typeof window[S[1]] !== S[2] ? window : global;
        try {
            a[S[3]](S[4]);
            return function() {}
            ;
        } catch (a) {
            try {
                (function() {}
                [S[11]](S[12])());
                return function() {}
                ;
            } catch (a) {
                if (/TypeError/[S[15]](a + S[16])) {
                    return function() {}
                    ;
                }
            }
        }
    
    ソースコードは全く読めないので、なんとかしてその逆対策を破らなければなりません.
    ツールを使って巧みに反対を解読します.
    まずブレークポイントデバッグモードで一歩ずつ確認してみたら、どのような操作が行われていますか?
    (function() {
        (function a() {
            try {
                (function b(i) {
                    if (('' + (i / i)).length !== 1 || i % 20 === 0) {
                        (function() {}
                        ).constructor('debugger')();
                    } else {
                        debugger ;
                    }
                    b(++i);
                }
                )(0);
            } catch (e) {
                setTimeout(a, 5000);
            }
        }
        )()
    }
    )();
    
    このコードは主に2つの部分があります.一つはtry{}ブロック内のb()関数によってコンソールが開いたかどうかを判断します.もしそうであれば、自己起動を行い、debuggarというブレークポイントに繰り返し入って、私たちのデバッグを妨害する目的を達成します.コンソールを開けていないとデバッグガーを呼び出して異常を投げます.その時はcatch{}ブロックにタイマーをセットして、5秒後にb()関数を呼び出します.
    このように、実はすべてのものはsetTimeoutという関数(b(b)関数は全部クローズドコールですので、外部からは壊れられません)から始まります.だから、setTimeoutが呼び出された時に、それを実行させない限り、このデッドサイクルを破ることができます.
    だから、私たちはsetTimeoutを簡単にカバーするだけでいいです.例えば、
    window._setTimeout = window.setTimeout;
    window.setTimeout = function () {};
    
    でも!この操作はコンソールの中ではできません.コンソールを開くと、必然的にb()関数のデッドサイクルに吸い込まれるからです.この時にsetTimeoutを上書きするのはもう意味がないです.
    この時に私達のツールのTamperMonkeyは登場して、コードをTMのシナリオの中で書いて、コンソールを開けませんとしても実行することができました.
    TMスクリプトを書き終わったら、ページを更新して、完全にロードし終わったら、またコンソールを開けてください.この時、debuggerはもう現れません.
    次はコンソールがコードを更新する番です.(function() {debugger})の右側のリンクポイントを通して具体的なコードを特定し、Console was clearedをクリックして圧縮されたコードを美化してみると、実はsetIntervalでconsolie.clear()コンソールを繰り返し呼び出して{} 情報を出力していることが分かりました.set Intervalを直接カバーできません.この関数は他のところにも重要な用途があります.
    したがって、我々は、consolie.clear関数をカバーし、ロゴ情報をフィルタすることによって、そのクリーンな動作を阻止することができる.
    同じくTamperMonkeyのシナリオに書き込まれています.コード:
    window.console.clear = function() {};
    window.console._log = window.console.log;
    window.console.log = function (e) {
        if (e['nodeName'] && e['nodeName'] == 'DIV') {
            return ;
        }
        return window.console.error.apply(window.console._log, arguments);
    };
    
    errorで情報を出力するのは、その呼び出しスタックを見るためであり、プログラムの論理を理解するのに役立ちます.
    基本的に、これらの仕事を終えたら、このコードは普通のプログラムと同じように正常にデバッグできます.しかし、もう一つの問題があります.主要コードは常に暗号化を混同しているので、調整が難しいです.次に簡単に過程を説明します.
    暗号化方法を混同します.一つは隠し方法で起動し、読み取り可能性を低減します.
    license.demo.jsから先頭のコードが見えます.
    var zBCa = function T(f) {
        for (var U = 0, V = 0, W, X, Y = (X = decodeURI("+TR4W%17%7F@%17.....    "),
        W = '',
        'D68Q4cYfvoqAveD2D8Kb0jTsQCf2uvgs'); U < X.length; U++,
        V++) {
            if (V === Y.length) {
                V = 0;
            }
            W += String["fromCharCode"](X["charCodeAt"](U) ^ Y["charCodeAt"](V));
        }
        var S = W.split("&&");
    
    追跡実行によって、S変数の内容は、本プログラムで使用するクラス名、関数名のセットであることが分かります.Console was clearedと同じです.consolie.clear()とconsolie.log()関数を呼び出すなら、このままでいいです.
    var a = window;
    a[S[0]][S[1]]();
    a[S[2]][S[3]]();
    
    混淆暗号化方法2:関数定義を証明書検証プロセスに追加する
    license.demo.jsにはこのようなコードがいくつかあります.
    a['RegExp']('/R[\S]{4}p.c\wn[\D]{5}t\wr/','g')['test'](T + '')
    
    ここのaはwindowを表し、Tは関数を表し、var S = ['console', 'clear', 'console', 'log']の役割はT関数の定義を文字列に変換するので、このコードの意味は実は、T関数の定義にある文字が含まれているかどうかを検証することです.
    検証が成功するたびに、特定の値を返します.これらの特定の値は、コア証明書を復号するパラメータです.
    コードフォーマットを再整理したせいか、再実行中にこの証明書がうまく動かなくなりました.証明書を通じて突破する方案を放棄しました.
    逆方向の考え方:すべての関数の呼び出しとパラメータを出力します.
    ブレークポイントのデバッグによって、このプログラム全体のロジックを一歩深く解明するのは非常に難しいことが分かります.それはほとんどの関数の間で相互呼出しの関係です.パラメータの違いだけで、結果が違います.
    だから、後に考えたのですが、システム関数の呼び出しだけを確認して、呼び出し順序の研究を通して、どのような操作が行われているかを大体知ることができます.
    すべてのシステム関数の呼び出しを出力するには、以下の問題を解決する必要があります.
  • は、すべての内蔵変数およびクラスの関数をカバーし、T + ''のような実例に依存する関数をカバーし、window.console.clear()
  • のようなクラス定義に依存する関数をカバーします.
  • は、内蔵関数とカスタム関数を正しく区別する必要がある
  • 検索後、内蔵関数を区別するコードが見つかりました.
      // Used to resolve the internal `[[Class]]` of values
      var toString = Object.prototype.toString;
    
      // Used to resolve the decompiled source of functions
      var fnToString = Function.prototype.toString;
    
      // Used to detect host constructors (Safari > 4; really typed array specific)
      var reHostCtor = /^\[object .+?Constructor\]$/;
    
      // Compile a regexp using a common native method as a template.
      // We chose `Object#toString` because there's a good chance it is not being mucked with.
      var reNative = RegExp('^' +
        // Coerce `Object#toString` to a string
        String(toString)
        // Escape any special regexp characters
        .replace(/[.*+?^${}()|[\]\/\\]/g, '\\$&')
        // Replace mentions of `toString` with `.*?` to keep the template generic.
        // Replace thing like `for ...` to support environments like Rhino which add extra info
        // such as method arity.
        .replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
      );
    
      function isNative(value) {
        var type = typeof value;
        return type == 'function'
          // Use `Function#toString` to bypass the value's own `toString` method
          // and avoid being faked out.
          ? reNative.test(fnToString.call(value))
          // Fallback to a host object check because some environments will represent
          // things like typed arrays as DOM methods which may not conform to the
          // normal native pattern.
          : (value && type == 'object' && reHostCtor.test(toString.call(value))) || false;
      }
    
    そしてネット上の資料を結び付けて、再帰的に内蔵関数をカバーするコードを書きました.
    function wrapit(e) {
        if (e.__proto__) {
            wrapit(e.__proto__);
        }
        for (var a in e) {
            try {
                e[a];
            } catch (e) {
                // pass
                continue;
            }
            var prop = e[a];
            if (!prop || prop._w) continue;
    
            prop = e[a];
            if (typeof prop == 'function' && isNative(prop)) {
                e[a] = (function (name, func) {
                    return function () {
                        var args = [].splice.call(arguments,0); // convert arguments to array
                        if (false && name == 'getElementsByTagName' && args[0] == 'iframe') {
                        } else {
                            console.error((new Date).toISOString(), [this], name, args);
                        }
                        if (name == 'querySelectorAll') {
                            //alert('querySelectorAll');
                        }
                        return func.apply(this, args);
                    };
                })(a, prop);
                e[a]._w = true;
            };
        }
    }
    
    使う時は必要です.
    wrapit(window);
    wrapit(document);
    
    その後、通常の操作をシミュレートして、PopUderを触発すると、その起動プロセスが見られます.
    参考資料:
    A Beginner’Guide to Obfuscation Detect if function is to browser Detectif a Function is Native Code with JavaScript
    次は広告時間です.私のです.http://www.jianshu.com/u/0708f50bcf26 知っていますかhttps://www.zhihu.com/people/never-younger 私の公式番号:OutOfRange