A(User-Agent)是一個特殊字符抬頭,通過服務器進行識別應用的操作系統、版本類別及標識、CPU型號、瀏覽器內核、硬件信息及相關型號、瀏覽器渲染引擎、瀏覽器語言等。
通常網站會通過 UA(User-Agent)來給不同的操作系統、不同的瀏覽器發送不同的頁面,識別這些內容就需要UA識別系統來進行。
UA字串的標準格式:瀏覽器標識(包括:操作系統標識;加密等級標識;瀏覽器語言)渲染引擎標識 版本信息
API接口:根據用戶user-agent信息識別操作系統、瀏覽器信息、瀏覽器內核、硬件信息及相關型號,識別率達90%以上
后附:user-agent(UA)識別Api接口代碼:
Api文檔:
子接口:
接口地址: https://登錄后顯示/njs/167/319(支持:http/https)
返回格式:json,xml
請求方式:GET,POST
POST 請求需要設置Header頭:Content-Type: application/x-www-form-urlencoded;charset=utf-8
請求說明:
Md5驗證方式-加密順序:
返回參數說明:
名稱 | 必填 | 類型 | 說明 | 示例 參數另存 |
alias | 否 | String | 7 | |
browser | 否 | Array | ||
camouflage | 否 | Boolean | ||
channel | 否 | String | Nightly | |
codeid | 否 | Integer | 狀態碼,返回10000狀態都會進行計費。具體說明可查看狀態碼說明 | 10000 |
curtime | 否 | String | 當前服務器時間戳 | 1633915763 |
detectCamouflage | 否 | Boolean | 1 | |
device | 否 | Array | ||
engine | 否 | Array | ||
features | 否 | Array | ||
hidden | 否 | Boolean | ||
identified | 否 | Boolean | ||
message | 否 | String | 請求狀態說明 | 返回成功 |
name | 否 | String | 姓名 | Windows |
options | 否 | Array | ||
original | 否 | String | 6.1 | |
os | 否 | Array | ||
retdata | 否 | Array | 回數據集合,可能是數據、對象或者字符串 | |
stock | 否 | Boolean | ||
type | 否 | String | desktop | |
useFeatures | 否 | Boolean | ||
version | 否 | Array |
JSON返回示例:
狀態碼說明:
狀態碼 | 說明 |
10000 | 返回成功 |
10001 | appid必須指定,可以我的應用里面查看 |
10002 | sign值必須指定,加密規則請前往幫助中心查看 |
10003 | sign值驗證不通過,加密規則請前往幫助中心查看 |
10004 | 時差不能超過10分鐘,可以不傳遞這個參數,注意時間戳單位是秒 |
10005 | appid錯誤,請檢查appid值,前往會員中心->我的應用查看或添加 |
10006 | 當前IP地址未授權,請前往用戶中心->我的應用添加ip{@info} |
10007 | 應用被禁用,請聯系客服處理 |
10008 | 應用內沒有該接口,請到我的應用里面添加這個接口 |
10009 | api接口不存在 |
10010 | 您沒有添加該api接口 |
10011 | api已經到期 |
10012 | 沒有訂購任何api,請前往購買后再操作 |
10013 | 該接口已經暫停使用 |
10014 | 未知的錯誤,可以聯系客服處理 |
10015 | 參數個數錯誤 |
10019 | {@info} |
10017 | time必須是整型 |
10018 | 次數不足 |
10020 | 子接口不存在,可能已經被關閉 |
10021 | 服務器發生錯誤 |
10022 | 帳戶余額不足,請充值! |
10023 | 訂單提交成功,等待回調結果 |
10024 | 調試模式數據 |
10025 | 查無數據 |
請求示例:
以下內容轉載自:http://madong.net.cn/index.php/2016/05/557/
(這個文章對UA系統的以生動的故事方式寫得非常明白、透澈,借來說明一下枯燥的代碼用途。)
很久很久以前有一個瀏覽器名字叫 NCSA Mosaic
很久很久以前有一個瀏覽器名字叫 NCSA Mosaic
緊接著也出現了一個 Mozilla 的瀏覽器 (Mozilla 的意思是 Mosaic 終結者)
后來 Mozilla 的正式發布版本是 Netscape 它把自己標稱為Mozilla/1.0 (Win3.1)
由于 Netscape支持框架顯示,后來框架在大家中間流行起來了,但Mosaic不支持框架。
所以網站管理員們則通過 User-Agent 判斷,如果是Netscape瀏覽器則進入框架(html frame)的頁面,如果不是Netscape 瀏覽器則進入沒有框架的頁面.
Netscape 沒有風光多久,微軟也推出了自己的 IE瀏覽器 。IE瀏覽器也支持 框架 但是很遺憾,網站管理員們不認識它呀,因為IE的 User-Agent 沒有Mozilla 所有沒有人理它。后來微軟抓狂了,你們不就是只認識User-Agent 頭里有 Mozilla 字符的瀏覽器么,于是宣稱自己是“兼容Mozilla”的,開始模仿Netscape,把自己標稱為Mozilla/1.22 (compatible; MSIE 2.0; Windows 95)。這樣一來 IE 也有了Mozilla (其實是偽裝的…) 所以那些框架也能看到了.
后來隨著微軟把瀏覽器捆綁進自己的操作系統里一起賣,隨就爆發了瀏覽器大戰。結果大家都很清楚,Netscape失敗了。而微軟大勝了,至今微軟的IE依然影響著 w3c 影響著所有瀏覽器。
Netscape 失敗后重生為Mozilla 構造了Gecko,標稱其為Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.1) Gecko/20020826,Gecko屬于渲染引擎,表現優異。Mozilla開發了Firefox,標稱為Mozilla/5.0 (Windows; U; Windows NT 5.1; sv-SE; rv:1.7.5) Gecko/20041108 Firefox/1.0,并且Firefox表現也非常優秀.
由于 Gecko 的優秀,網站管理員們會判斷瀏覽器是否是 Gecko 的,如果是則把更先進 更漂亮頁面顯示給這個瀏覽器,其他瀏覽器就沒有這個待遇了。
(比如現在的HTML5 , 如果我判斷是你firefox的新版本則跳轉到地址1,如果是ie則去地址2)
看到 Gecko 能看到這么漂亮的頁面 Linux 的平臺的孩子們很桑心,因為他們創建了基于KHTML引擎支持的Konqueror也跟 Gecko 一樣優秀,但卻不帶有Gecko而被識別。結果Konquerer開始偽裝自己“像Gecko”那樣以看到更漂亮的網頁,并標稱自己為 Mozilla/5.0 (compatible; Konqueror/3.2; FreeBSD)(KHTML, like Gecko),這個世界就抽風了….
Apple開發了Safari,使用了KHTML,同時也增加了很多新特性,后來另起爐灶叫了WebKit,但是它有希望能夠看到那些為KHTML編寫的網頁,于是Safari標稱自己為Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/85.7 (KHTML, like Gecko) Safari/85.5,就更加混亂了.
Google也開發了自己的瀏覽器Chrome,使用了Webkit,有點像Safari,希望能看到為Safari編寫的網頁,于是決定裝成Safari。Chrome使用了WebKit渲染引擎,想裝成Safari,Chrome宣稱自己是Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko)Chrome/0.2.149.27 Safari/525.13
最后:
IE偽裝成 Mozilla
webKit 偽裝成 KHTML
KHTML 偽裝成 Gecko
最后 opera 偽裝成上面任何瀏覽器
同時所有的瀏覽器又都宣稱自己是 Mozilla
般來說,識別navigator.userAgent即可識別出用戶所使用的瀏覽器,但是UA是可以偽造的。
我們的思路是根據各個瀏覽器的獨特功能來識別瀏覽器,比如:
if(window.ActiveXObject){ //IE6或更低版本 }
以上代碼即可識別IE瀏覽器。
本文由ISMY博客發布
轉載請注明出處。
注微信公眾號:K哥爬蟲,持續分享爬蟲進階、JS/安卓逆向等技術干貨!
本文章中所有內容僅供學習交流,抓包內容、敏感網址、數據接口均已做脫敏處理,嚴禁用于商業用途和非法用途,否則由此產生的一切后果均與作者無關,若有侵權,請聯系我立即刪除!
OB 混淆全稱 Obfuscator,Obfuscator 其實就是混淆的意思,官網:https://obfuscator.io/ ,其作者是一位叫 Timofey Kachalov 的俄羅斯 JavaScript 開發工程師,早在 2016 年就發布了第一個版本。
一段正常的代碼如下:
function hi() {
console.log("Hello World!");
}
hi();
經過 OB 混淆后的結果:
function _0x3f26() {
var _0x2dad75=['5881925kTCKCP', 'Hello\x20World!', '600mDvfGa', '699564jYNxbu', '1083271cEvuvT', 'log', '18sKjcFY', '214857eMgFSU', '77856FUKcuE', '736425OzpdFI', '737172JqcGMg'];
_0x3f26=function () {
return _0x2dad75;
};
return _0x3f26();
}
(function (_0x307c88, _0x4f8223) {
var _0x32807d=_0x1fe9, _0x330c58=_0x307c88();
while (!![]) {
try {
var _0x5d6354=parseInt(_0x32807d(0x6f)) / 0x1 + parseInt(_0x32807d(0x6e)) / 0x2 + parseInt(_0x32807d(0x70)) / 0x3 + -parseInt(_0x32807d(0x69)) / 0x4 + parseInt(_0x32807d(0x71)) / 0x5 + parseInt(_0x32807d(0x6c)) / 0x6 * (parseInt(_0x32807d(0x6a)) / 0x7) + -parseInt(_0x32807d(0x73)) / 0x8 * (parseInt(_0x32807d(0x6d)) / 0x9);
if (_0x5d6354===_0x4f8223) break; else _0x330c58['push'](_0x330c58['shift']());
} catch (_0x3f18e4) {
_0x330c58['push'](_0x330c58['shift']());
}
}
}(_0x3f26, 0xaa023));
function _0x1fe9(_0xa907e7, _0x410a46) {
var _0x3f261f=_0x3f26();
return _0x1fe9=function (_0x1fe950, _0x5a08da) {
_0x1fe950=_0x1fe950 - 0x69;
var _0x82a06=_0x3f261f[_0x1fe950];
return _0x82a06;
}, _0x1fe9(_0xa907e7, _0x410a46);
}
function hi() {
var _0x12a222=_0x1fe9;
console[_0x12a222(0x6b)](_0x12a222(0x72));
}
hi();
OB 混淆具有以下特征:
1、一般由一個大數組或者含有大數組的函數、一個自執行函數、解密函數和加密后的函數四部分組成;
2、函數名和變量名通常以 _0x 或者 0x 開頭,后接 1~6 位數字或字母組合;
3、自執行函數,進行移位操作,有明顯的 push、shift 關鍵字;
例如在上面的例子中,_0x3f26() 方法就定義了一個大數組,自執行函數里有 push、shift 關鍵字,主要是對大數組進行移位操作,_0x1fe9() 就是解密函數,hi() 就是加密后的函數。
點擊登陸抓包,可以看到有個 ua 參數,經過了加密,每次登陸是會改變的,如下圖所示:
如果直接搜索 ua 的話,結果太多,不方便篩選,通過 XHR 斷點比較容易找到加密的位置,如下圖所示,最后提交的 r 參數包含 ua 值,往上找可以看到是 i 的值經過了 URL 編碼,再往上看,i 的值通過 window.getUa() 獲取,這個實際上是 uad.js 里面的一個匿名函數。
跟進到 uad.js,可以看到調用了 window[_0x4651('0x710')] 這個方法,最后返回的 _0x261229 就是加密后的 ua 值,用鼠標把類似 _0x4651('0x710')、_0x4651('0x440') 的值選中,可以看到實際上是一些字符串,這些字符串通過直接搜索,可以發現是在頭部的一個大數組里,如下圖所示:
一個大數組,一個有明顯的 push、shift 關鍵字的進行移位操作的自執行函數,是 OB 混淆無疑了,那么我們應該怎樣去處理,讓其看起來更順眼一些呢?
你可以手動在瀏覽器選中查看值,在本地去替換,當然不用全部去替換,跟棧走,用到的地方替換就行了,不要傻傻的全部去挨個手動替換,這種方法適用于不太復雜的代碼。
如果遇到代碼很多的情況,建議使用反混淆工具去處理,這里推薦國內的猿人學OB混淆專解工具和國外的 de4js,猿人學的工具還原程度很高,但是部分 OB 混淆還原后運行會報錯,實測本案例的 OB 混淆經過猿人學的工具處理后就不能正常運行,可能需要自己預先處理一下才行,de4js 這個工具是越南的一個作者開發的,開源的,你可以部署到自己的機器上,它支持多種混淆還原,包括 Eval、OB、JSFuck、AA、JJ 等,可以直接粘貼代碼,自動識別混淆方式,本案例推薦使用 de4js,如下圖所示:
我們將還原后的結果復制到本地文件,使用 Fiddler 的 Autoresponder 功能對響應進行替換,如下圖所示:
如果此時開啟抓包,刷新頁面,你會發現請求狀態 status 顯示的是 CORS error,JS 替換不成功,在控制臺里還可以看到報錯 No 'Access-Control-Allow-Origin' header is present on the requested resource. 如下圖所示:
CORS (Cross-Origin Resource Sharing,跨域資源共享)是一個 W3C 標準,該標準使用附加的 HTTP 頭來告訴瀏覽器,允許運行在一個源上的 Web 應用訪問位于另一不同源的資源。一個請求 URL 的協議、域名、端口三者之間任意與當前頁面地址不同即為跨域。常見的跨域問題就是瀏覽器提示在 A 域名下不可以訪問 B 域名的 API,有關 CORS 的進一步理解,可以參考 W3C CORS Enabled。
簡要流程如下:
1、消費者發送一個 Origin 報頭到提供者端:Origin: http://www.site.com; 2、提供者發送一個 Access-Control-Allow-Origin 響應報頭給消費者,如果值為 * 或 Origin 對應的站點,則表示允許共享資源給消費者,如果值為 null 或者不存在,則表示不允許共享資源給消費者; 3、除了 Access-Control-Allow-Origin 以外,部分站點還有可能檢測 Access-Control-Allow-Credentials,為 true 表示允許; 4、瀏覽器根據提供者的響應報文判斷是否允許消費者跨域訪問到提供者源;
我們根據前面在控制臺的報錯信息,可以知道是響應頭缺少 Access-Control-Allow-Origin 導致的,在 Fiddler 里面有兩種方法為響應頭添加此參數,下面分別介紹一下:
第一種是利用 Fiddler 的 Filter 功能,在 Response Headers 里設置即可,分別填入 Access-Control-Allow-Origin 和允許的域名,如下圖所示:
第二種是修改 CustomRules.js 文件,依次選擇 Rules —> Customize Rules,在 static function OnBeforeResponse(oSession: Session) 模塊下增加以下代碼:
if(oSession.uriContains("要處理的 URL")){
oSession.oResponse["Access-Control-Allow-Origin"]="允許的域名";
}
兩種方法二選一,設置完畢后,就可以成功替換了,刷新再次調試就可以看到是還原后的 JS 了,如下圖所示:
很明顯 window.getUa 是主要的加密函數,所以我們先來分析一下這個函數:
window.getUa=function() {
var _0x7dfc34=new Date().getTime();
if (_0x4a9622) {
_0x2644f4();
}
_0x55b608();
var _0x261229=_0x1722c3(_0x2e98dd) + '|' + _0x1722c3(_0x420004) + '|' + _0x7dfc34.toString(0x10);
_0x261229=btoa(_0x570bef.gzip(_0x261229, {
'to': 'string'
}));
return _0x261229;
};
_0x7dfc34 是時間戳,接著一個 if 判斷,我們可以鼠標放到判斷里去看看,發現判斷的 _0x4a9622 是 false,那么 _0x2644f4() 就不會被執行,然后執行了 _0x55b608() 方法,_0x261229 的值,主要調用了 _0x1722c3() 方法得到的,前后依次傳入了 _0x2e98dd 和 _0x420004,很明顯這兩個值比較關鍵,分別搜索一下,可以發現:
_0x2e98dd 定義了一些 header、瀏覽器的信息、屏幕信息、系統字體信息等,這些信息可以作為定值直接傳入,如下圖所示:
_0x420004 搜索有用的結果就是僅定義了一個空對象,在控制臺輸出一下可以看到實際上包含了一些鍵盤、鼠標點擊移動的數據,實際上經過測試發現, _0x420004 的值并不是強校驗的,可以使用隨機數模擬生成,也可以直接復制一個定值。
_0x2e98dd 和 _0x420004 這兩個參數都沒有進行強校驗,完全可以以定值的方式傳入,這兩個值都是 JSON 格式,我們可以直接在控制臺使用 copy 語句復制其值,或者使用 JSON.stringify() 語句輸出結果再手動復制。
里面各個函數相互調用,比較多,可以直接把整個 JS copy 下來,我們注意到整個函數是一個自執行函數,在本地調用時,我們可以定義一個全局變量,然后在 window.getUa 函數里,將 _0x261229 的值賦值給全局變量,也就相當于導出值,最后取這個全局變量即可,還有一種方法就是不讓它自執行,改寫成正常一般的函數,然后調用 window.getUa 方法得到 ua 值。
首先我們把 _0x2e98dd 和 _0x420004 的值在本地定義一下,這里有個小細節,需要把原 JS 代碼里這兩個值定義的地方注釋掉,防止起沖突。
在本地調試時,會提示 window、location、document 未定義,定義一下為空對象即可,然后又提示 attachEvent 未定義,搜索一下,是 _0x13cd5a 的一個原型對象,除了 attachEvent 以外,還有個 addEventListener,addEventListener() 方法用于向指定元素添加事件句柄,在 IE 中使用 attachEvent() 方法來實現,我們在 Google Chrome 里面埋下斷點調試一下,刷新頁面會直接進入 addEventListener() 方法,其中的事件是 keydown,即鍵盤按下,就調用后面的 _0x5cec90 方法,輸出一下后面返回的 this,實際上并沒有產生什么有用的值,所以 _0x13cd5a.prototype.bind 方法我們可以直接將其注釋掉,實際測試也沒有影響。
接著本地調試,又會提示 btoa 未定義,btoa 和 atob 是 window 對象的兩個函數,其中 btoa 是 binary to ascii,用于將 binary 的數據用 ascii 碼表示,即 Base64 的編碼過程,而 atob 則是 ascii to binary,用于將 ascii 碼解析成 binary 數據,即 Base64 的解碼過程。
在 NodeJS 里,提供了一個稱為 Buffer 的本地模塊,可用于執行 Base64 編碼和解碼,這里不做詳細介紹,可自行百度,window.getUa 方法里的原 btoa 語句是這樣的:
_0x261229=btoa(_0x570bef.gzip(_0x261229, {'to': 'string'}));
在 NodeJS 里,我們可以這樣寫:
_0x261229=Buffer.from(_0x570bef.gzip(_0x261229, {'to': 'string'}), "latin1").toString('base64');
注意:Buffer.from() 傳入了一個 latin1 參數,這是由于 _0x570bef.gzip(_0x261229, {'to': 'string'}) 的結果是 Latin1(ISO-8859-1 的別名)編碼,如果不傳,或者傳入其他參數,則最終結果可能和 btoa 方法得出的結果不一樣!
自此,本地聯調完畢,就可以得到正確的 ua 值了!
GitHub 關注 K 哥爬蟲,持續分享爬蟲相關代碼!歡迎 star !https://github.com/kgepachong/
以下只演示部分關鍵代碼,不能直接運行! 完整代碼倉庫地址:https://github.com/kgepachong/crawler/
var window={};
var location={};
var document={};
var _0x5a577d=function () {}();
var _0xe26ae=function () {}();
var _0x3204b9=function () {}();
var _0x3c7e70=function () {}();
var _0x4a649b=function () {}();
var _0x21524f=function () {}();
var _0x2b0d61=function () {}();
var _0x53634a=function () {}();
var _0x570bef=function () {}();
var _0xd05c32=function (_0x5c6c0c) {};
window.CHLOROFP_STATUS='start';
// 此處省略 N 個函數
var _0x2e98dd={
// 對象具體的值已省略
"basic": {},
"header": {},
"navigator": {},
"screenData": {},
"sysfonts": [],
"geoAndISP": {},
"browserType": {},
"performanceTiming": {},
"canvasFp": {},
"visTime": [],
"other": {}
}
var _0x420004={
// 對象具體的值已省略
"keypress": true,
"scroll": true,
"click": true,
"mousemove": true,
"mousemoveData": [],
"keypressData": [],
"mouseclickData": [],
"wheelDeltaData": []
}
window.getUa=function () {
var _0x7dfc34=new Date().getTime();
if (_0x4a9622) {
_0x2644f4();
}
_0x55b608();
var _0x261229=_0x1722c3(_0x2e98dd) + '|' + _0x1722c3(_0x420004) + '|' + _0x7dfc34.toString(0x10);
// _0x261229=btoa(_0x570bef.gzip(_0x261229, {'to': 'string'}));
_0x261229=Buffer.from(_0x570bef.gzip(_0x261229, {'to': 'string'}), "latin1").toString('base64');
return _0x261229;
};
// 測試輸出
// console.log(window.getUa())
*請認真填寫需求信息,我們會在24小時內與您取得聯系。