popunderjs hatte ursprünglich Open-Source-Code auf Github, aber später wurde geschätzt, dass der Autor den enormen kommerziellen Wert dieser Nachfrage erkannte, also stoppte er einfach Open Source und berechnete direkt. Wenn wir nun den Implementierungsplan studieren möchten, können wir nur auf die offizielle Website gehen, um den Quellcode abzurufen.
script.js ist die Hauptfunktion, die alle Funktionen von Popunder implementiert und mehrere API-Methoden definiert
license.demo.js ist eine Autorisierungsdatei. Nur mit dieser Datei können Sie die Methoden in script.js erfolgreich aufrufen
Damit ein solch kommerziell wertvoller Code von Ihnen offen verwendet werden kann, müssen Sie die Frage der Umkehrung in Betracht ziehen. Mal sehen, wie es gegen Reverse Engineering wirkt.
Öffnen Sie zunächst die Konsole und finden Sie zwei Probleme:
Alle Inhalte der Konsole werden wiederholt gelöscht, Nur Dieser Satz wird ausgegeben: Konsole wurde gelöscht script.js?0.5309098417125133:1
Das Debuggen von Haltepunkten kann nicht durchgeführt werden, da, sobald die Funktion zum Debuggen von Haltepunkten aktiviert ist, an eine weitergeleitet wird anonyme Funktion (function() {debugger})
Mit anderen Worten, die häufig verwendete Haltepunkt-Debugging-Methode kann nicht mehr verwendet werden, wir können nur einen Blick darauf werfen Sehen Sie sich den Quellcode an und prüfen Sie, ob Sie seine Logik verstehen können. Der Quellcode lautet jedoch wie folgt:
<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>
Es ist ersichtlich, dass der Quellcode nicht lesbar ist, sodass wir noch einen Weg finden müssen, seine Anti-Reverse-Maßnahmen zu durchbrechen.
Überprüfen Sie zunächst Schritt für Schritt, welche Vorgänge im Haltepunkt-Debugging-Modus ausgeführt wurden Plötzlich entdeckte ich so einen 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>
Dieser Code besteht hauptsächlich aus zwei Teilen. Der eine besteht darin, über die b()-Funktion im try {}-Block zu beurteilen, ob er sich selbst wiederholt Geben Sie den Debugger-Haltepunkt ein, um unser Debuggen zu beeinträchtigen. Wenn die Konsole nicht geöffnet ist, löst der Aufruf des Debuggers eine Ausnahme aus. Stellen Sie zu diesem Zeitpunkt den Timer im Catch {}-Block ein und rufen Sie die Funktion b() nach 5 Sekunden erneut auf.
Auf diese Weise beginnt eigentlich alles mit der Funktion setTimeout (da die Funktion b() nur ein Abschlussaufruf ist und nicht von der Außenwelt unterbrochen werden kann), also solange setTimeout heißt, kann diese Endlosschleife unterbrochen werden, indem man sie nicht ausführen lässt.
Wir müssen also einfach setTimeout überschreiben... Zum Beispiel:
<span style="font-size: 16px;">window._setTimeout = window.setTimeout;<br>window.setTimeout = function () {};<br></span>
Aber! Dieser Vorgang kann nicht in der Konsole durchgeführt werden! Denn wenn Sie die Konsole öffnen, werden Sie unweigerlich in die Endlosschleife der Funktion b() hineingezogen. Es macht derzeit keinen Sinn, setTimeout zu überschreiben.
Zu diesem Zeitpunkt kommt unser Tool TamperMonkey ins Spiel, das das Schreiben des Codes in das TM-Skript auch ohne Öffnen der Konsole ausführen kann.
TM Nachdem das Skript geschrieben wurde, aktualisieren Sie die Seite, warten Sie, bis sie vollständig geladen ist, und öffnen Sie dann die Konsole. Zu diesem Zeitpunkt wird der Debugger nicht mehr angezeigt!
Dann ist die Konsole an der Reihe, den Code zu aktualisieren
Klicken Sie auf den Link auf der rechten Seite von „Konsole wurde gelöscht“, um den spezifischen Code zu finden Klicken Sie auf {}, um den komprimierten Code zu verschönern, und stellen Sie fest, dass er tatsächlich setInterval verwendet, um console.clear() wiederholt aufzurufen, um die Konsole zu löschen und die Meldung Konsole wurde gelöscht
So können wir das Verhalten beim Löschen des Bildschirms verhindern, indem wir die Funktion console.clear() überschreiben und die Protokollinformationen filtern.
ist auch in das TamperMonkey-Skript geschrieben, 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>
Der Grund, warum Fehler zur Ausgabe von Informationen verwendet werden, besteht darin, den Aufrufstapel anzuzeigen, der für das Verständnis der Programmlogik hilfreich ist.
Grundsätzlich kann dieser Code nach Abschluss dieser Aufgaben normal wie ein normales Programm debuggt werden. Es gibt jedoch noch ein weiteres Problem. Der Hauptcode ist häufig verschleiert und verschlüsselt, sodass das Debuggen sehr schwierig ist. Lassen Sie uns kurz über den Prozess sprechen.
In der Datei „licence.demo.js“ können Sie einen Codeausschnitt sehen Anfang Es geht so:
<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>
通过跟踪执行,可以发现 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>
license.demo.js 中有多处这样的代码:
<span style="font-size: 16px;">a['RegExp']('/R[\S]{4}p.c\wn[\D]{5}t\wr/','g')['test'](T + '')<br/></span>
这里的 a 代表 window,T 代表某个函数, T + '' 的作用是把 T 函数的定义转成字符串,所以这段代码的意思其实是,验证 T 函数的定义中是否包含某些字符。
每次成功的验证,都会返回一个特定的值,这些个特定的值就是解密核心证书的参数。
可能是因为我重新整理了代码格式,所以在重新运行的时候,这个证书一直运行不成功,所以后来就放弃了通过证书来突破的方案。
通过断点调试,我们可以发现,想一步一步深入地搞清楚这整个程序的逻辑,是十分困难,因为它大部分函数之间都是相互调用的关系,只是参数的不同,结果就不同。
所以我后来想了个办法,就是只查看它的系统函数的调用,通过对调用顺序的研究,也可以大致知道它执行了哪些操作。
要想输出所有系统函数的调用,需要解决以下问题:
覆盖所有内置变量及类的函数,我们既要覆盖 window.console.clear() 这样的依附在实例上的函数,也要覆盖依附在类定义上的函数,如 window.HTMLAnchorElement.__proto__.click()
需要正确区分内置函数和自定义函数
经过搜索后,找到了区分内置函数的代码:
<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>
然后结合网上的资料,写出了递归覆盖内置函数的代码:
<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>
使用的时候只需要:
<span style="font-size: 16px;">wrapit(window);<br>wrapit(document);<br></span>
然后模拟一下正常的操作,触发 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的区别详解
Das obige ist der detaillierte Inhalt vonJavascript-Reverse und Anti-Reverse. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!