国产99久久精品_欧美日本韩国一区二区_激情小说综合网_欧美一级二级视频_午夜av电影_日本久久精品视频

最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
當前位置: 首頁 - 科技 - 知識百科 - 正文

Javascript逆向與反逆向

來源:懂視網 責編:小采 時間:2020-11-27 20:10:36
文檔

Javascript逆向與反逆向

Javascript逆向與反逆向:popunderjs 原來在 github 上是有開源代碼的,但后來估計作者發現這個需求巨大的商業價值,索性不開源了,直接收費。所以現在要研究它的實現方案,只能上官網扒它源碼了。文件結構script.js 是功能主體,實現了 popunder 的所有功能以及定義了多個 API
推薦度:
導讀Javascript逆向與反逆向:popunderjs 原來在 github 上是有開源代碼的,但后來估計作者發現這個需求巨大的商業價值,索性不開源了,直接收費。所以現在要研究它的實現方案,只能上官網扒它源碼了。文件結構script.js 是功能主體,實現了 popunder 的所有功能以及定義了多個 API

popunderjs 原來在 github 上是有開源代碼的,但后來估計作者發現這個需求巨大的商業價值,索性不開源了,直接收費。所以現在要研究它的實現方案,只能上官網扒它源碼了。

文件結構

script.js 是功能主體,實現了 popunder 的所有功能以及定義了多個 API 方法

license.demo.js 是授權文件,有這個文件你才能順利調用 script.js 里的方法

防止被逆向

這么具有商業價值的代碼,就這么公開地給你們用,肯定要考慮好被逆向的問題。我們來看看它是怎么反逆向的。

首先,打開控制臺,發現2個問題:

  1. 控制臺所有內容都被反復清空,只輸出了這么一句話: Console was cleared script.js?0.5309098417125133:1

  2. 無法斷點調試,因為一旦啟用斷點調試功能,就會被定向到一個匿名函數 (function() {debugger})

也就是說,常用的斷點調試方法已經無法使用了,我們只能看看源代碼,看能不能理解它的邏輯了。但是,它源代碼是這樣的:

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() 函數來判斷是否打開了控制臺,如果是的話就進行自我調用,反復進入 debugger 這個斷點,從而達到干擾我們調試的目的。如果沒有打開控制臺,那調用 debugger 就會拋出異常,這時就在 catch {} 塊內設置定時器,5秒后再調用一下 b() 函數。

這么說來其實一切的一切都始于 setTimeout 這個函數(因為 b() 函數全是閉包調用,無法從外界破掉),所以只要在 setTimeout 被調用的時候,不讓它執行就可以破解掉這個死循環了。

所以我們只需要簡單地覆蓋掉 setTimeout 就可以了……比如:

window._setTimeout = window.setTimeout;
window.setTimeout = function () {};

但是!這個操作無法在控制臺里面做!因為當你打開控制臺的時候,你就必然會被吸入到 b() 函數的死循環中。這時再來覆蓋 setTimeout 已經沒有意義了。

這時我們的工具 TamperMonkey 就上場了,把代碼寫到 TM 的腳本里,就算不打開控制臺也能執行了。

TM 腳本寫好之后,刷新頁面,等它完全加載完,再打開控制臺,這時 debugger 已經不會再出現了!

接下來就輪到控制臺刷新代碼了

通過 Console was cleared 右側的鏈接點進去定位到具體的代碼,點擊 {} 美化一下被壓縮過的代碼,發現其實就是用 setInterval 反復調用 console.clear() 清空控制臺并輸出了 <p>Console was cleared</p> 信息,但是注意了,不能直接覆蓋 setInterval 因為這個函數在其他地方也有重要的用途。

所以我們可以通過覆蓋 console.clear() 函數和過濾 log 信息來阻止它的清屏行為。

同樣寫入到 TamperMonkey 的腳本中,代碼:

window.console.clear = function() {};
window.console._log = window.console.log;
window.console.log = function (e) {
if (e['nodeName'] && e['nodeName'] == 'p') {
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 變量的內容其實是本程序所有要用到的類名、函數名的集合,類似于 var S = ['console', 'clear', 'console', 'log'] 。如果要調用 console.clear() 和 console.log() 函數的話,就這樣

var a = window;
a[S[0]][S[1]]();
a[S[2]][S[3]]();

混淆加密方法二:將函數定義加入到證書驗證流程

license.demo.js 中有多處這樣的代碼:

a['RegExp']('/R[\S]{4}p.c\wn[\D]{5}t\wr/','g')['test'](T + '')

這里的 a 代表 window,T 代表某個函數, T + '' 的作用是把 T 函數的定義轉成字符串,所以這段代碼的意思其實是,驗證 T 函數的定義中是否包含某些字符。

每次成功的驗證,都會返回一個特定的值,這些個特定的值就是解密核心證書的參數。

可能是因為我重新整理了代碼格式,所以在重新運行的時候,這個證書一直運行不成功,所以后來就放棄了通過證書來突破的方案。

逆向思路:輸出所有函數調用和參數

通過斷點調試,我們可以發現,想一步一步深入地搞清楚這整個程序的邏輯,是十分困難,因為它大部分函數之間都是相互調用的關系,只是參數的不同,結果就不同。

所以我后來想了個辦法,就是只查看它的系統函數的調用,通過對調用順序的研究,也可以大致知道它執行了哪些操作。

要想輸出所有系統函數的調用,需要解決以下問題:

  1. 覆蓋所有內置變量及類的函數,我們既要覆蓋 window.console.clear() 這樣的依附在實例上的函數,也要覆蓋依附在類定義上的函數,如 window.HTMLAnchorElement.__proto__.click()

  2. 需要正確區分內置函數和自定義函數

經過搜索后,找到了區分內置函數的代碼:

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

然后模擬一下正常的操作,觸發 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中的后退與刷新的實例詳解

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

文檔

Javascript逆向與反逆向

Javascript逆向與反逆向:popunderjs 原來在 github 上是有開源代碼的,但后來估計作者發現這個需求巨大的商業價值,索性不開源了,直接收費。所以現在要研究它的實現方案,只能上官網扒它源碼了。文件結構script.js 是功能主體,實現了 popunder 的所有功能以及定義了多個 API
推薦度:
標簽: js javascript 逆向
  • 熱門焦點

最新推薦

猜你喜歡

熱門推薦

專題
Top
主站蜘蛛池模板: 国产喷水在线观看 | 真实和子乱视频 | 一区一精品 | 亚洲一区二区三区久久精品 | 亚洲国产成人精品久久 | 91视频国产一区 | 唯美清纯另类亚洲 | 亚洲国产精品成人久久 | 日韩欧美国产高清在线观看 | 久久亚洲国产成人影院 | 国产日韩视频 | 欧美 日韩 成人 | 国产精品特级毛片一区二区三区 | 亚洲一区二区综合 | 特黄日韩免费一区二区三区 | 看全色黄大色大片免费久久久 | 国产一区二区高清 | 一区二区三区福利 | 国产一区二区三区在线 | 日韩欧美在线综合 | 操比网站 | 欧美成a人片在线观看 | 国产精品免费精品自在线观看 | 日韩国产电影 | 99视频九九精品视频在线观看 | 国产一区二区三区欧美精品 | 日韩黄色网页 | 九九久久国产精品大片 | 91精品国产91热久久p | 国产精品久久久久国产精品 | 国产亚洲欧美日韩综合另类 | 91啪国自产在线高清观看 | 国产手机在线国内精品 | 国产一区二区在线观看视频 | 亚洲偷 | 香蕉视频免费在线看 | 亚洲欧洲视频在线 | 欧美日韩 在线播放 | 国产精品毛片va一区二区三区 | 日韩经典第一页 | 久久91精品国产99久久yfo |