尋找深圳爬蟲工作,微信:cjh-18888
function test(aa,bb){
cc = aa + bb;
return cc;
}
Hook程式碼:
var _test = test; // 拿到test
test = function(x,y){
console.log(x,y); //輸出拿到的引數
var retval = _test(x,y); // retval 是原來的計算結果
console.log( retval)
return retval + 1 // 修改返回結果
}
此時重新呼叫test, 結果比正常值多了1
//Hook setInterval
var _setInterval = setInterval;
setInterval = function(a,b){
console.log(a + '',b)
return 'setInterval is Kill'
}
//Hook JSON.stringify
stringify = JSON.stringify;
JSON.stringify = function(a){
console.log('Hook JSON.stringify ->' + stringify(a))
return stringify(a)
}
案例程式碼:
function test(aa,bb){var cc = aa + bb;return cc;}
function checkTest(func){
test + '' == 'function test(aa,bb){var cc = aa + bb;return cc;}'?console.log('func未被修改'):console.log('func被修改了')
}
此時我們可以把hook程式碼置入到瀏覽器
//控制檯置入的程式碼
var _test = test;
test = function(x,y){
console.log(x,y);
var retval = _test(x,y);
console.log( retval)
return retval
}
Function.prototype.toString=function(){
return "function test(x,y){z=x+y;return z;}";
}
反偵錯程式碼:
var fuck=["\u0068\u0065\u006e\u0076\u0061\u0074","\u005f\u006b\u0070\u006f\u0076\u0074\u0071\u005f\u0076\u006b\u0074","\u0066\u006b\u005f\u0071\u0069\u0061\u0070\u0076","\u0068\u0071\u0070\u005f\u0076\u0065\u006b\u0070\u0022\u006f\u0076\u0074\u0056\u006b\u0051\u0065\u0070\u0076\u003a\u002a\u006f\u0076\u0074\u0025\u0077\u0068\u006b\u0074\u002a\u0078\u005d\u0074\u0022\u0065\u0039\u0032\u002e\u005d\u0074\u0074\u0039\u0057\u0059\u0037\u0065\u003e\u006f\u0076\u0074\u0030\u006e\u0061\u0070\u0063\u0076\u006a\u0037\u0065\u0027\u0027\u0025\u0077\u0078\u005d\u0074\u0022\u005d\u0039\u006f\u0076\u0074\u0030\u005f\u006a\u005d\u0074\u003f\u006b\u0066\u0061\u003d\u0076\u002a\u0065\u0025\u0037\u005d\u0074\u0074\u0030\u0072\u0071\u006f\u006a\u002a\u005d\u0021\u0034\u003b\u005d\u0029\u0036\u003c\u005d\u0027\u0034\u0025\u0037\u0079\u0074\u0061\u0076\u0071\u0074\u0070\u0022\u0070\u0061\u0073\u0022\u0051\u0065\u0070\u0076\u003a\u003d\u0074\u0074\u005d\u0075\u002a\u005d\u0074\u0074\u0025\u0037\u0079","\u0076\u006b\u004f\u0076\u0074\u0065\u0070\u0063","\u0074\u0061\u0076\u0071\u0074\u0070\u0022\u0076\u006a\u0065\u006f","\u0068\u0071\u005f\u0067\u0055\u006b\u0071","\u0070\u0067\u005b\u0059\u0078\u0061\u0067\u0072","\u006c\u0076\u005d\u006a","\u0066\u0061\u0059\u0072\u005b\u006c\u005d\u0072\u005f\u0065\u0059\u0067","\u0066\u0066\u0066","\u005b\u0067\u0072\u006b\u0067\u0070\u005d\u0032\u0070\u0067\u005f\u002c\u0026\u005d\u0076\u0076\u0026\u0021","\u006a\u0059\u0068\u0069\u005b\u005b\u0059\u0078\u002f","\u006c\u0069\u0074\u0057\u007a\u005d\u0063\u0074\u0026\u0067\u0059\u007a\u003d\u0074\u007a\u0059\u0078\u007c\u0055\u0072\u002e\u001d\u0026\u006f\u0026\u004f\u0074\u0055\u007a\u005d\u007c\u0059\u0026\u0057\u0063\u006a\u0059\u0051\u0026\u0071","\u006f\u0061\u0076\u0045\u0070\u0076\u0061\u0074\u0078\u005d\u006e\u002a\u0068\u0071\u005f\u0067\u002d\u0057\u0027\u0057\u0059\u0059\u0057\u006f\u0056\u006b\u004f\u002a\u0068\u0071\u005f\u0067\u0057\u0038\u0059\u0025\u0059\u002a\u0068\u0071\u005f\u0067\u002d\u0057\u0027\u0057\u0059\u0059\u0057\u006f\u0056\u006b\u004f\u002a\u0068\u0071\u005f\u0067\u0057\u0038\u0059\u0025\u0059\u002a\u0068\u0071\u005f\u0067\u002d\u0057\u0027\u0057\u0059\u0059\u0057\u006f\u0056\u006b\u004f\u002a\u0068\u0071\u005f\u0067\u0057\u0038\u0059\u0025\u0059\u002a\u0068\u0071\u005f\u0067\u0057\u002d\u0034\u0059\u0025\u0025\u0025\u002e\u002d\u0032\u0032\u0032\u0025","\u006a\u006d\u005b\u0063\u0029\u0053\u0023\u0053\u0055\u0055\u0053\u006b\u0058\u0067\u004b\u002c\u006b\u0058\u0067\u004b\u002c\u006a\u006d\u005b\u0063\u0053\u0031\u0055\u0021\u0021\u0055\u0035\u0035\u002b\u0037\u005b\u0067\u0072\u006b\u0067\u0070\u005d\u0032\u0070\u0067\u005f\u002c\u0026\u0067\u006e\u0066\u0063\u003e\u002f\u0038\u002f\u002f\u002f\u003a\u0026\u0023\u006a\u006d\u005b\u0063\u0029\u0053\u0023\u0053\u0055\u0055\u0053\u006b\u0058\u0067\u004b\u002c\u006b\u0058\u0067\u004b\u002c\u006a\u006d\u005b\u0063\u0053\u0031\u0055\u0021\u0021\u0055\u0023\u0026\u002f\u0038\u0026\u0021\u003e\u005b\u0067\u0072\u006b\u0067\u0070\u005d\u0032\u0070\u0067\u005f\u002c\u0026\u005d\u0076\u0076\u0026\u0021"],bianchengmao=-1,fff=-1;var fuck1=[1,2,3,4];console.log("過了檢測就會給你正確答案哦!");function Uint8ToStr_(arr){for(var i=0,str="";i<arr.length;i++){var a=arr[i];str+=String.fromCharCode(a)}return str}function strToUint8_(str){for(var i=0,arr=[];i<str.length;i++){var a=str.charCodeAt(i);arr.push(a)}return new Uint8Array(arr)}function strToUint8(str){for(var i=0,arr=[];i<str.length;i++){var a=str.charCodeAt(i);arr.push(a%2?a-4:a+2)}return new Uint8Array(arr)}function Uint8ToStr(arr){for(var i=0,str="";i<arr.length;i++){var a=arr[i];str+=String.fromCharCode(a%2?a+4:a-2)}return str}function sToS(x){return Uint8ToStr(strToUint8_(x))}fuck1[!+[]+!+[]]=[][sToS(fuck[0])][sToS(fuck[1])];fuck1[+[]]=fuck1[!+[]+!+[]](sToS(fuck[5]))(),fuck1[+[]][sToS(fuck[6])]=sToS;fuck1[!+[]+!+[]](fuck1[+[]][sToS(fuck[6])](fuck[14]))();fuck1[+[]][fuck1[+[]]["fuckYou"](fuck1[+[]]["fuckYou"](fuck[7]))][fuck1[+[]]["fuckYou"](fuck1[+[]]["fuckYou"](fuck[8]))]==fuck1[+[]]["fuckYou"](fuck1[+[]]["fuckYou"](fuck[9]))?fuck1[+[]][(![]+[])[+[]]+(![]+[])[+[]]+(![]+[])[+[]]]=1:fuck1[+[]][[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+[]]+(![]+[])[+[]]+(![]+[])[+[]])()]=0;fuck1[+[]][(![]+[])[+[]]+(![]+[])[+[]]+(![]+[])[+[]]]==+!+[]?fuck1[+[]][sToS(sToS(fuck[9]))]=1:fuck1[!+[]+!+[]](fuck1[+[]][sToS(fuck[6])](fuck[14]))();setInterval+""==fuck1[+[]][sToS(fuck[6])](fuck1[+[]][sToS(fuck[6])](fuck1[+[]][sToS(fuck[6])](fuck[13])))?fuck1[+[]][sToS(sToS(fuck[9]))]=fuck1[+[]][sToS(sToS(fuck[9]))]+2:fuck1[!+[]+!+[]](fuck1[+[]][sToS(fuck[6])](fuck[14]))();fuck1[!+[]+!+[]](fuck1[+[]][sToS(fuck[6])](fuck1[+[]][sToS(fuck[6])](fuck[15])))(+!+[])+(+!+[]);
我們直接看到這塊
fuck1[!+[] + !+[]] = [][sToS(fuck[0])][sToS(fuck[1])];
// fuck[2] = Function;
fuck1[+[]] = fuck1[!+[] + !+[]](sToS(fuck[5]))(),
// fuck[0] = Function('return this')()
fuck1[+[]][sToS(fuck[6])] = sToS;
fuck1[!+[] + !+[]](fuck1[+[]][sToS(fuck[6])](fuck[14]))();
// fuck1[2]("setInterval(debugger;,1000)")()
通過上面的程式碼解析, 我們可以看到,debugger其實就是通過setInterval方法來呼叫的. 那麼我們其實可以寫這個過偵錯程式碼.
Hook物件屬性需要使用到
Object.defineProperty() //方法會直接在一個物件上定義一個新屬性,或者修改一個物件的現有屬性,並返回此物件。 | 這個常用一些.
Object.defineProperties() // 方法直接在一個物件上定義新的屬性或修改現有屬性,並返回該物件。
下面這個是定義了一個案例. 我們使用Object.defindProperty()
來修改屬性物件的set
var obj = {
'name': function(){
return 'xiaopang';
}
}
然後我們編寫Hook程式碼.
Object.defineProperty(obj,'name',{
'set':function(x){
console.log(x)
return x;
}
})
Hook注意點:
document.cookie
部分資料參照於 https://www.cnblogs.com/liuxianan/p/chrome-plugin-develop.html
嚴格來講,我們正在說的東西應該叫Chrome擴充套件(
Chrome Extension
),真正意義上的Chrome外掛是更底層的瀏覽器功能擴充套件,可能需要對瀏覽器原始碼有一定掌握才有能力去開發。鑑於Chrome外掛的叫法已經習慣,本文也全部採用這種叫法,但讀者需深知本文所描述的Chrome外掛實際上指的是Chrome擴充套件。 Chrome外掛是一個用Web技術開發、用來增強瀏覽器功能的軟體,它其實就是一個由HTML、CSS、JS、圖片等資源組成的一個
.crx
字尾的壓縮包. 另外,其實不只是前端技術,Chrome外掛還可以配合C++編寫的dll動態連結庫實現一些更底層的功能(NPAPI),比如全螢幕幕截圖。由於安全原因,Chrome瀏覽器42以上版本已經陸續不再支援NPAPI外掛,取而代之的是更安全的PPAPI。
增強瀏覽器功能, 實現屬於自己的「客製化版」瀏覽器。然後再本文中,我們則是需要通過學習Chrome瀏覽器外掛的開發,來實現 JSHOOK 程式碼的注入.
Chrome提供了非常多實用的API,包括但不限於:
Chrome外掛沒有嚴格的專案結構要求,只要保證本目錄有一個manifast.json
即可.也不需要專門的IDE,普通的web開發工具即可。
從右上角選單->更多工具->擴充套件程式可以進入 外掛管理頁面,也可以直接在位址列輸入 chrome://extensions
存取。
勾選開發者模式
即可以資料夾的形式直接載入外掛,否則只能安裝.crx
格式的檔案。Chrome要求外掛必須從它的Chrome應用商店安裝,其它任何網站下載的都無法直接安裝,所以,其實我們可以把crx
檔案解壓,然後通過開發者模式直接載入。
開發中,程式碼有任何改動都必須重新載入外掛,只需要在外掛管理頁按下Ctrl+R
即可,以防萬一最好還把頁面重新整理一下。
這是一個Chrome外掛最重要也是必不可少的檔案,用來設定所有和外掛相關的設定,必須放在根目錄。其中,manifest_version
、name
、version
3個是必不可少的,description
和icons
是推薦的。
{
// 清單檔案的版本,這個必須寫,而且必須是2
"manifest_version": 2,
// 外掛的名稱
"name": "demo",
// 外掛的版本
"version": "1.0.0",
// 外掛描述
"description": "簡單的Chrome擴充套件demo",
// 圖示,一般偷懶全部用一個尺寸的也沒問題
"icons":
{
"16": "img/icon.png",
"48": "img/icon.png",
"128": "img/icon.png"
},
// 會一直常駐的後臺JS或後臺頁面
"background":
{
// 2種指定方式,如果指定JS,那麼會自動生成一個背景頁
"page": "background.html"
//"scripts": ["js/background.js"]
},
// 瀏覽器右上角圖示設定,browser_action、page_action、app必須三選一
"browser_action":
{
"default_icon": "img/icon.png",
// 圖示懸停時的標題,可選
"default_title": "這是一個範例Chrome外掛",
"default_popup": "popup.html"
},
// 當某些特定頁面開啟才顯示的圖示
/*"page_action":
{
"default_icon": "img/icon.png",
"default_title": "我是pageAction",
"default_popup": "popup.html"
},*/
// 需要直接注入頁面的JS
"content_scripts":
[
{
//"matches": ["http://*/*", "https://*/*"],
// "<all_urls>" 表示匹配所有地址
"matches": ["<all_urls>"],
// 多個JS按順序注入
"js": ["js/jquery-1.8.3.js", "js/content-script.js"],
// JS的注入可以隨便一點,但是CSS的注意就要千萬小心了,因為一不小心就可能影響全域性樣式
"css": ["css/custom.css"],
// 程式碼注入的時間,可選值: "document_start", "document_end", or "document_idle",最後一個表示頁面空閒時,預設document_idle
"run_at": "document_start"
},
// 這裡僅僅是為了演示content-script可以設定多個規則
{
"matches": ["*://*/*.png", "*://*/*.jpg", "*://*/*.gif", "*://*/*.bmp"],
"js": ["js/show-image-content-size.js"]
}
],
// 許可權申請
"permissions":
[
"contextMenus", // 右鍵選單
"tabs", // 標籤
"notifications", // 通知
"webRequest", // web請求
"webRequestBlocking",
"storage", // 外掛本地儲存
"http://*/*", // 可以通過executeScript或者insertCSS存取的網站
"https://*/*" // 可以通過executeScript或者insertCSS存取的網站
],
// 普通頁面能夠直接存取的外掛資源列表,如果不設定是無法直接存取的
"web_accessible_resources": ["js/inject.js"],
// 外掛主頁,這個很重要,不要浪費了這個免費廣告位
"homepage_url": "https://www.baidu.com",
// 覆蓋瀏覽器預設頁面
"chrome_url_overrides":
{
// 覆蓋瀏覽器預設的新分頁
"newtab": "newtab.html"
},
// Chrome40以前的外掛設定頁寫法
"options_page": "options.html",
// Chrome40以後的外掛設定頁寫法,如果2個都寫,新版Chrome只認後面這一個
"options_ui":
{
"page": "options.html",
// 新增一些預設的樣式,推薦使用
"chrome_style": true
},
// 向位址列註冊一個關鍵字以提供搜尋建議,只能設定一個關鍵字
"omnibox": { "keyword" : "go" },
// 預設語言
"default_locale": "zh_CN",
// devtools頁面入口,注意只能指向一個HTML檔案,不能是JS檔案
"devtools_page": "devtools.html"
}
所謂content-scripts,其實就是Chrome外掛中向頁面注入指令碼的一種形式(雖然名為script,其實還可以包括css的),藉助content-scripts
我們可以實現通過設定的方式輕鬆向指定頁面注入JS和CSS(如果需要動態注入,可以參考下文),最常見的比如:廣告遮蔽、頁面CSS客製化,等等。
{
// 需要直接注入頁面的JS
"content_scripts":
[
{
//"matches": ["http://*/*", "https://*/*"],
// "<all_urls>" 表示匹配所有地址
"matches": ["<all_urls>"],
// 多個JS按順序注入
"js": ["js/jquery-1.8.3.js", "js/content-script.js"],
// JS的注入可以隨便一點,但是CSS的注意就要千萬小心了,因為一不小心就可能影響全域性樣式
"css": ["css/custom.css"],
// 程式碼注入的時間,可選值: "document_start", "document_end", or "document_idle",最後一個表示頁面空閒時,預設document_idle
"run_at": "document_start"
}
],
}
特別注意,如果沒有主動指定run_at
為document_start
(預設為document_idle
),下面這種程式碼是不會生效的:
document.addEventListener('DOMContentLoaded', function()
{
console.log('我被執行了!');
});
content-scripts
和原始頁面共用DOM,但是不共用JS,如要存取頁面JS(例如某個JS變數),只能通過injected js
來實現。content-scripts
不能存取絕大部分chrome.xxx.api
,除了下面這4種:
其實看到這裡不要悲觀,這些API絕大部分時候都夠用了,非要呼叫其它API的話,你還可以通過通訊來實現讓background來幫你呼叫(關於通訊,後文有詳細介紹)。
好了,Chrome外掛給我們提供了這麼強大的JS注入功能,剩下的就是發揮你的想象力去玩弄瀏覽器了。
後臺(姑且這麼翻譯吧),是一個常駐的頁面,它的生命週期是外掛中所有型別頁面中最長的,它隨著瀏覽器的開啟而開啟,隨著瀏覽器的關閉而關閉,所以通常把需要一直執行的、啟動就執行的、全域性的程式碼放在background裡面。
background的許可權非常高,幾乎可以呼叫所有的Chrome擴充套件API(除了devtools),而且它可以無限制跨域,也就是可以跨域存取任何網站而無需要求對方設定CORS
。
經過測試,其實不止是background,所有的直接通過
chrome-extension://id/xx.html
這種方式開啟的網頁都可以無限制跨域
設定中,background
可以通過page
指定一張網頁,也可以通過scripts
直接指定一個JS,Chrome會自動為這個JS生成一個預設的網頁:
{
// 會一直常駐的後臺JS或後臺頁面
"background":
{
// 2種指定方式,如果指定JS,那麼會自動生成一個背景頁
"page": "background.html"
//"scripts": ["js/background.js"]
},
}
需要特別說明的是,雖然你可以通過chrome-extension://xxx/background.html
直接開啟後臺頁,但是你開啟的後臺頁和真正一直在後臺執行的那個頁面不是同一個,換句話說,你可以開啟無數個background.html
,但是真正在後臺常駐的只有一個,而且這個你永遠看不到它的介面,只能偵錯它的程式碼。
這裡順帶介紹一下event-pages,它是一個什麼東西呢?鑑於background生命週期太長,長時間掛載後臺可能會影響效能,所以Google又弄一個event-pages
,在組態檔上,它與background的唯一區別就是多了一個persistent
引數:
{
"background":
{
"scripts": ["event-page.js"],
"persistent": false
},
}
它的生命週期是:在被需要時載入,在空閒時被關閉,什麼叫被需要時呢?比如第一次安裝、外掛更新、有content-script向它傳送訊息,等等。
除了組態檔的變化,程式碼上也有一些細微變化,個人這個簡單瞭解一下就行了,一般情況下background也不會很消耗效能的。
popup
是點選browser_action
或者page_action
圖示時開啟的一個小視窗網頁,焦點離開網頁就立即關閉,一般用來做一些臨時性的互動。
popup
可以包含任意你想要的HTML內容,並且會自適應大小。可以通過default_popup
欄位來指定popup頁面,也可以呼叫setPopup()
方法。
設定方式:
{
"browser_action":
{
"default_icon": "img/icon.png",
// 圖示懸停時的標題,可選
"default_title": "這是一個範例Chrome外掛",
"default_popup": "popup.html"
}
}
需要特別注意的是,由於單擊圖示開啟popup,焦點離開又立即關閉,所以popup頁面的生命週期一般很短,需要長時間執行的程式碼千萬不要寫在popup裡面。
在許可權上,它和background非常類似,它們之間最大的不同是生命週期的不同,popup中可以直接通過chrome.extension.getBackgroundPage()
獲取background的window物件。
這裡的injected-script
是我給它取的,指的是通過DOM操作的方式向頁面注入的一種JS。為什麼要把這種JS單獨拿出來討論呢?又或者說為什麼需要通過這種方式注入JS呢?
這是因為content-script
有一個很大的「缺陷」,也就是無法存取頁面中的JS,雖然它可以操作DOM,但是DOM卻不能呼叫它,也就是無法在DOM中通過繫結事件的方式呼叫content-script
中的程式碼(包括直接寫onclick
和addEventListener
2種方式都不行),但是,「在頁面上新增一個按鈕並呼叫外掛的擴充套件API」是一個很常見的需求,那該怎麼辦呢?其實這就是本小節要講的。
在content-script
中通過DOM方式向頁面注入inject-script
程式碼範例:
// 向頁面注入JS
function injectCustomJs(jsPath)
{
jsPath = jsPath || 'js/inject.js';
var temp = document.createElement('script');
temp.setAttribute('type', 'text/javascript');
// 獲得的地址類似:chrome-extension://ihcokhadfjfchaeagdoclpnjdiokfakg/js/inject.js
temp.src = chrome.extension.getURL(jsPath);
temp.onload = function()
{
// 放在頁面不好看,執行完後移除掉
this.parentNode.removeChild(this);
};
document.head.appendChild(temp);
}
你以為這樣就行了?執行一下你會看到如下報錯:
Denying load of chrome-extension://efbllncjkjiijkppagepehoekjojdclc/js/inject.js. Resources must be listed in the web_accessible_resources manifest key in order to be loaded by pages outside the extension.
意思就是你想要在web中直接存取外掛中的資源的話必須顯示宣告才行,組態檔中增加如下:
{
// 普通頁面能夠直接存取的外掛資源列表,如果不設定是無法直接存取的
"web_accessible_resources": ["js/inject.js"],
}
更多關於Chrome Extension的開發請看部落格https://www.cnblogs.com/liuxianan/p/chrome-plugin-develop.html
在2.2中,介紹了許許多多的Chrome的頁面各個作用. 接下來進行實戰.
由於我們是需要在網頁開啟的時候, 立馬將我們的程式碼注入進去,這樣才能毫無遺漏的把一些操作給Hook出來。因此,我們的manifest.json
檔案應該如下設定
{
"manifest_version": 2,
"name": "小胖JS自動注入外掛",
"version": "1.0",
"description": "小胖JS自動注入外掛,QQ2625112940",
"author": "xiaopang",
"icons":
{
"16":"ico.png",
"48": "icon.png",
"128": "icon.png"
},
"browser_action":
{
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"content_scripts":
[
{
"matches": ["<all_urls>"],
"js": ["content-script.js"],
"run_at": "document_start",
"all_frames": true
}
],
"permissions":
[
"<all_urls>",
"webRequest",
"webRequestBlocking",
"tabs",
"http://*/*",
"https://*/*",
"contextMenus",
"cookies",
"unlimitedStorage",
"notifications",
"storage",
"clipboardWrite"
]
}
那麼在瀏覽器中, 我們應該要如何載入js檔案呢?可以參照下面程式碼
(function() {
var spt = document.createElement('script');
spt.innerHTML = `
// ---- Cookie 監聽
var cookie_cache = document.cookie; // 獲取到原來的cookie
Object.defineProperty(document,'cookie',{
// 獲取Cookie時,觸發的動作
get: function(){
return cookie_cache;
},
//當Cookie被設定的時候,觸發的動作
set: function(val){
console.log('Cookies Setting',val);
// debugger;
var cookie = val.split(';')[0];
var ncookie = cookie.split("=");
var flag = false;
var cache = cookie_cache.split("; ");
cache = cache.map(function(a){
if (a.split("=")[0] === ncookie[0]){
flag = true;
return cookie;
}
return a;
})
cookie_cache = cache.join("; ");
if (!flag){
cookie_cache += cookie + "; ";
}
this._value = val;
return cookie_cache;
}
})
// ----
`
document.documentElement.appendChild(spt);
})();
上面的例子是對Cookie進行監控的程式碼,暫且忽略功能的實現問題. 就單純看建立Script
的過程, 其實就是下面這點而已.
(function() {
var spt = document.createElement('script');
spt.innerHTML = `
// 業務邏輯程式碼
`
document.documentElement.appendChild(spt);
})();
下面再祭出一些注入的業務邏輯程式碼
//HOOK JSON stringify
var rstringify = JSON.stringify;
JSON.stringify = function(a){
console.log("Detect Json.stringify", a);
//debugger;
return rstringify(a);
}
//HOOK json parse
//var strparse = JSON.parse
//JSON.parse = function(b){
//console.log("Detect Json.Parse", b);
//return strparse(b);
//}
//var plugins_cache = window.navigator
//Object.defineProperty(navigator, 'plugins', {
// get: function() {
// console.log('Getting plugins');
// //debugger;
// return plugins_cache;
// },
// set: function(val) {
// console.log('獲取資訊');
// console.log(val);
// debugger;
// },
//});
var _eval = eval;
eval = function(e){
_eval(e.replace("debugger",""));
}
eval.toString = _eval.toString;
var _Function = Function;
Function = function(e){
_Function(e.replace("debugger",""));
}
Function.toString = _Function.toString;
var _constructor = constructor;
Function.prototype.constructor = function(s) {
if (s == "debugger"){
console.log(s);
return null;
}
return _constructor(s);
}
在程式碼左邊的行號 - > 右鍵 -> Edit breakpoints -> 然後輸入表示式, 結果=true的時候會自動斷下
筆者使用該外掛一般情況:
一般情況下是遇到大檔案的時候,會使用Reres外掛,或者需要修改程式碼進行偵錯的時候
Github地址:https://github.com/annnhan/ReRes
新增規則
點選「新增規則」按鈕,輸入以下資訊,然後儲存:
/
和結束的/gi
,如/.*/gi
請寫成.*
http://cssha.com
或file:///D:/a.js
練習網站:
其實就是在webpack命名的函數. webpack中,會有需要的
var aaa = n(12);
var bbb = n(45);
我們對於webpack的網站,我自認為不適合用扣演演算法,不適合用缺少補啥的方法,誰能知道n(*)裡面還有沒有巢狀其他的n呢. 所以我認為用整體改寫就是一個好的辦法.
類似這種的就拿下.
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push(
"aaa":function(e,t,r){},
"bbb":functino(e,t,r){}
)()
一般類似於這樣, 具體如何說我好像沒法表達. 也是整個檔案拿下一般.
!function(e) {
function r(r) {
for (var n, a, i = r[0], c = r[1], l = r[2], p = 0, s = []; p < i.length; p++)
a = i[p],
Object.prototype.hasOwnProperty.call(o, a) && o[a] && s.push(o[a][0]),
o[a] = 0;
for (n in c)
Object.prototype.hasOwnProperty.call(c, n) && (e[n] = c[n]);
for (f && f(r); s.length; )
s.shift()();
return u.push.apply(u, l || []),
t()
}
function t() {
for (var e, r = 0; r < u.length; r++) {
for (var t = u[r], n = !0, i = 1; i < t.length; i++) {
var c = t[i];
0 !== o[c] && (n = !1)
}
n && (u.splice(r--, 1),
e = a(a.s = t[0]))
}
return e
}
var n = {}
, o = {
1: 0
}
, u = [];
function a(r) {
if (n[r])
return n[r].exports;
var t = n[r] = {
i: r,
l: !1,
exports: {}
}
, o = !0;
try {
e[r].call(t.exports, t, t.exports, a),
o = !1
} finally {
o && delete n[r]
}
return t.l = !0,
t.exports
}
a.e = function(e) {
var r = []
, t = o[e];
if (0 !== t)
if (t)
r.push(t[2]);
else {
var n = new Promise((function(r, n) {
t = o[e] = [r, n]
}
));
r.push(t[2] = n);
var u, i = document.createElement("script");
i.charset = "utf-8",
i.timeout = 120,
a.nc && i.setAttribute("nonce", a.nc),
i.src = function(e) {
return a.p + "static/chunks/" + ({}[e] || e) + "." + {
53: "6d99d4eacdc1f6ea047f",
54: "cbec7184fead9e811bbf"
}[e] + ".js"
}(e);
var c = new Error;
u = function(r) {
i.onerror = i.onload = null,
clearTimeout(l);
var t = o[e];
if (0 !== t) {
if (t) {
var n = r && ("load" === r.type ? "missing" : r.type)
, u = r && r.target && r.target.src;
c.message = "Loading chunk " + e + " failed.\n(" + n + ": " + u + ")",
c.name = "ChunkLoadError",
c.type = n,
c.request = u,
t[1](c)
}
o[e] = void 0
}
}
;
var l = setTimeout((function() {
u({
type: "timeout",
target: i
})
}
), 12e4);
i.onerror = i.onload = u,
document.head.appendChild(i)
}
return Promise.all(r)
}
,
a.m = e,
a.c = n,
a.d = function(e, r, t) {
a.o(e, r) || Object.defineProperty(e, r, {
enumerable: !0,
get: t
})
}
,
a.r = function(e) {
"undefined" !== typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, {
value: "Module"
}),
Object.defineProperty(e, "__esModule", {
value: !0
})
}
,
a.t = function(e, r) {
if (1 & r && (e = a(e)),
8 & r)
return e;
if (4 & r && "object" === typeof e && e && e.__esModule)
return e;
var t = Object.create(null);
if (a.r(t),
Object.defineProperty(t, "default", {
enumerable: !0,
value: e
}),
2 & r && "string" != typeof e)
for (var n in e)
a.d(t, n, function(r) {
return e[r]
}
.bind(null, n));
return t
}
,
a.n = function(e) {
var r = e && e.__esModule ? function() {
return e.default
}
: function() {
return e
}
;
return a.d(r, "a", r),
r
}
,
a.o = function(e, r) {
return Object.prototype.hasOwnProperty.call(e, r)
}
,
a.p = "",
a.oe = function(e) {
throw console.error(e),
e
}
;
var i = window.webpackJsonp = window.webpackJsonp || []
, c = i.push.bind(i);
i.push = r,
i = i.slice();
for (var l = 0; l < i.length; l++)
r(i[l]);
var f = c;
t()
}([]);
!function(e) {
function r(r) {
for (var n, a, i = r[0], c = r[1], l = r[2], p = 0, s = []; p < i.length; p++)
a = i[p],
Object.prototype.hasOwnProperty.call(o, a) && o[a] && s.push(o[a][0]),
o[a] = 0;
for (n in c)
Object.prototype.hasOwnProperty.call(c, n) && (e[n] = c[n]);
for (f && f(r); s.length; )
s.shift()();
return u.push.apply(u, l || []),
t()
}
function t() {
for (var e, r = 0; r < u.length; r++) {
for (var t = u[r], n = !0, i = 1; i < t.length; i++) {
var c = t[i];
0 !== o[c] && (n = !1)
}
n && (u.splice(r--, 1),
e = a(a.s = t[0]))
}
return e
}
var n = {}
, o = {
1: 0
}
, u = [];
function a(r) {
if (n[r])
return n[r].exports;
var t = n[r] = {
i: r,
l: !1,
exports: {}
}
, o = !0;
try {
e[r].call(t.exports, t, t.exports, a),
o = !1
} finally {
o && delete n[r]
}
return t.l = !0,
t.exports
}
a.e = function(e) {
var r = []
, t = o[e];
if (0 !== t)
if (t)
r.push(t[2]);
else {
var n = new Promise((function(r, n) {
t = o[e] = [r, n]
}
));
r.push(t[2] = n);
var u, i = document.createElement("script");
i.charset = "utf-8",
i.timeout = 120,
a.nc && i.setAttribute("nonce", a.nc),
i.src = function(e) {
return a.p + "static/chunks/" + ({}[e] || e) + "." + {
53: "6d99d4eacdc1f6ea047f",
54: "cbec7184fead9e811bbf"
}[e] + ".js"
}(e);
var c = new Error;
u = function(r) {
i.onerror = i.onload = null,
clearTimeout(l);
var t = o[e];
if (0 !== t) {
if (t) {
var n = r && ("load" === r.type ? "missing" : r.type)
, u = r && r.target && r.target.src;
c.message = "Loading chunk " + e + " failed.\n(" + n + ": " + u + ")",
c.name = "ChunkLoadError",
c.type = n,
c.request = u,
t[1](c)
}
o[e] = void 0
}
}
;
var l = setTimeout((function() {
u({
type: "timeout",
target: i
})
}
), 12e4);
i.onerror = i.onload = u,
document.head.appendChild(i)
}
return Promise.all(r)
}
,
a.m = e,
a.c = n,
a.d = function(e, r, t) {
a.o(e, r) || Object.defineProperty(e, r, {
enumerable: !0,
get: t
})
}
,
a.r = function(e) {
"undefined" !== typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, {
value: "Module"
}),
Object.defineProperty(e, "__esModule", {
value: !0
})
}
,
a.t = function(e, r) {
if (1 & r && (e = a(e)),
8 & r)
return e;
if (4 & r && "object" === typeof e && e && e.__esModule)
return e;
var t = Object.create(null);
if (a.r(t),
Object.defineProperty(t, "default", {
enumerable: !0,
value: e
}),
2 & r && "string" != typeof e)
for (var n in e)
a.d(t, n, function(r) {
return e[r]
}
.bind(null, n));
return t
}
,
a.n = function(e) {
var r = e && e.__esModule ? function() {
return e.default
}
: function() {
return e
}
;
return a.d(r, "a", r),
r
}
,
a.o = function(e, r) {
return Object.prototype.hasOwnProperty.call(e, r)
}
,
a.p = "",
a.oe = function(e) {
throw console.error(e),
e
}
;
var i = window.webpackJsonp = window.webpackJsonp || []
, c = i.push.bind(i);
i.push = r,
i = i.slice();
for (var l = 0; l < i.length; l++)
r(i[l]);
var f = c;
t()
// 重要****
window.n = a;
// 重要****
}([]);
案例地址:https://www.sojson.com/beian/
如何定位不說了, 直接走到關鍵的地方
window[b('96', 'lInO')](function() { //b('96', 'lInO') == "setInterval"
var cf = {
'gSHOk': function(cg) {
return cg(); //cg()其實就是一個檢測debug的函數.
}
};
cf['gSHOk'](en);
}, 0x7d0);
為了不影響到其他setInterval函數的執行. 這裡可以加一點條件.
var _setInterval = setInterval;
setInterval = function(a,b){
console.log(a + '',b)
if(a.indexOf("gSHOk':function(cg){return cg();}")!= -1){
return 'setInterval is Kill'
}
_setInterval(a,b)
}
這裡開啟偵錯工具會直接跳到下面這一塊,我們這節對debugger;
行號欄目, 右鍵 add conditional breakpoints
, 輸入false . 當條件為false的時候,就不會執行此條件.
function eC(eD) {
var eE = {
'oihUc': b('161', '!9L9'),
'YSZMe': ep[b('162', 'bhfu')]
};
if (ep[b('163', 'QS!f')](b('164', '1$&&'), ep[b('165', 'C5IH')])) {
en();
} else {
if (typeof eD === ep[b('166', '18LM')]) {
var eG = function() {
if (eE[b('167', 'tEyN')] !== eE[b('168', 'Gq^E')]) {
debugger ;
} else {
so[b('169', 'eOuM')](res[b('16a', 'w2W4')]);
}
};
return ep['bzKJh'](eG);
} else {
if (ep[b('16b', 'IIR5')](ep[b('16c', 'BDu]')]('', eD / eD)[ep[b('16d', '(igu')]], 0x1) || eD % 0x14 === 0x0) {
debugger ;
} else {
debugger ;
}
}
ep['RwYcr'](eC, ++eD);
}
}
直接快捷鍵. Ctrl + F8
(貌似失效了)
簡單點的
Function.prototype.constructor = function(){}
完善點
Function.prototype.__constructor_back = Function.prototype.constructor;
Function.prototype.constructor = function() {
if(arguments && typeof arguments[0]==='string'){
//alert("new function: "+ arguments[0]);
if("debugger" === arguments[0]){
//arguments[0]="console.log(\"anti debugger\");";
//arguments[0]=";";
return
}
}
return Function.prototype.__constructor_back.apply(this,arguments);
}
http://peng3.com/vip?a=https%3A%2F%2F2.08bk.com%2F%3Furl%3D%0D%0A&url=http%3A%2F%2F1.zhananhome.applinzi.com%2Fwx
//定位到這邊.
jdetects.create(function(e) {
var a = 0;
var n = setInterval(function() {
if ("on" === e) {
setTimeout(function() {
if (a === 0) {
a = 1;
setTimeout(Base64.decode(code));
}
}, 200);
}
}, 100);
注入JS程式碼
var _setInterval = setInterval;
setInterval = function(a,b){
console.log(a + '',b)
if(a+''.indexOf("setTimeout(Base64.decode(code));")!= -1){
return 'Debugger is Kill'
}
_setInterval(a,b)
}
解決這個以後又發現個驚人的操作.在控制檯發現Console was cleared
function o() {
window.Firebug && window.Firebug.chrome && window.Firebug.chrome.isInitialized ? t("on") : (a = "off",
console.log(d),
("undefined" !== typeof console.clear) && console.clear(),
t(a));
}
修改下o方法
o = function(){}
或者在上面的setInterval程式碼加多個條件. 原因是向上跟蹤發現以下程式碼
var f = setInterval(o, i);
var _setInterval = setInterval;
setInterval = function(a,b){
console.log(a + '',b)
if( a + ''.indexOf("setTimeout(Base64.decode(code));")!= -1){
return 'Debugger is Kill'
}
if (a + ''.indexOf('console.clear')!=-1){
return 'console.clear is Kill'
}
_setInterval(a,b)
}