js逆向爬蟲


js逆向


js常見的加密方式

  • 加密在前端開發和爬蟲中是經常遇見的。掌握了加密算法且可以將加密的密文進行解密破解的,也是你從一個編程小白到大神級別質的一個飛躍。且加密算法的熟練和剖析也是很有助於幫助我們實現高效的js逆向。下述只把我們常用的加密方法進行總結。不去深究加密的具體實現方式。

  • 常見的加密算法基本分為這幾類,

    • 線性散列算法(簽名算法)MD5
    • 對稱性加密算法 AES DES
    • 非對稱性加密算法 RSA

Md5加密

  • MD5是一種被廣泛使用的線性散列算法,可以產生出一個128位(16字節)的散列值(hash value),用於確保信息傳輸完整的一致性。且MD5加密之后產生的是一個固定長度(32位或16位)的數據。
  • 解密:
    • 常規講MD5是不存在解密的。但是理論上MD5是可以進行反向暴力破解的。暴力破解的大致原理就是用很多不同的數據進行加密后跟已有的加密數據進行對比,由此來尋找規律。理論上只要數據量足夠龐大MD5是可以被破解的。但是要注意,破解MD5是需要考慮破解的成本(時間和機器性能)。假設破解當前的MD5密碼需要目前計算能力最優秀的計算機工作100年才能破解完成。那么當前的MD5密碼就是安全的。
  • 增加破解成本的方法(方法很多,這里只說我常用的)。
    • 使用一段無意義且隨機的私匙進行MD5加密會生成一個加密串,我們暫且稱之為串1
    • 將要加密的的數據跟串1拼接,再進行一次MD5,這時會生成串2
    • 將串2再次進行MD5加密,這時生成的串3就是我們加密后的數據。
  • 我們在注冊賬號時的密碼一般都是用的MD5加密。

In [ ]:

<html>
	<script src="https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.js"></script>
    <script type="text/javascript">
		var hashCode = md5("i am bobo!"); 
		alert(hashCode)
	</script>
		
</html>

DES/AES加密

  • DES全稱為Data Encryption Standard,即數據加密標准,是一種使用密鑰加密的算法。該加密算法是一種對稱加密方式,其加密運算、解密運算需要使用的是同樣的密鑰(一組字符串)即可。
  • 注意:
    • 現在用AES這個標准來替代原先的DES。
    • AES和DES的區別:
      • 加密后密文長度的不同:
        • DES加密后密文長度是8的整數倍
        • AES加密后密文長度是16的整數倍
      • 應用場景的不同:
        • 企業級開發使用DES足夠安全
        • 如果要求高使用AES
      • DES和AES切換只需要修改 CryptoJS.AES <=> CryptoJS.DES
  • 使用DES/AES進行數據交互時要求雙方都擁有相同的私匙
  • 破解方法:
    • 暴力破解。
    • DES如果使用 56 位的密鑰,則可能的密鑰數量是 2 的 56 次方個。只要計算足夠強大是可以被破解的
  • DES算法的入口參數有三個:
    • Key、Data、Mode,padding。
      • Key為7個字節共56位,是DES算法的工作密鑰;
      • Data為8個字節64位,是要被加密或被解密的數據;
      • Mode為DES的工作方式。
      • padding為填充模式,如果加密后密文長度如果達不到指定整數倍(8個字節、16個字節),填充對應字符
        • padding的賦值固定為CryptoJS.pad.Pkcs7即可

In [ ]:

<html>
	<script src="https://cdn.bootcss.com/crypto-js/3.1.9-1/crypto-js.js"></script>
    <script type="text/javascript">
		  var aseKey = "12345678"     //定制秘鑰,長度必須為:8/16/32位
		  var message = "i am bobo,who are you ?";
		  //加密 DES/AES切換只需要修改 CryptoJS.AES <=> CryptoJS.DES
		  var encrypt = CryptoJS.DES.encrypt(message, CryptoJS.enc.Utf8.parse(aseKey), {
			mode: CryptoJS.mode.ECB,
			padding: CryptoJS.pad.Pkcs7
		  }).toString();

		  alert(encrypt); // 0Gh9NGnwOpgmB525QS0JhVJlsn5Ev9cHbABgypzhGnM

		  //解密
		  var decrypt = CryptoJS.DES.decrypt(encrypt, CryptoJS.enc.Utf8.parse(aseKey), {
			mode: CryptoJS.mode.ECB,
			padding: CryptoJS.pad.Pkcs7
		  }).toString(CryptoJS.enc.Utf8);

		  alert(decrypt); // 我是一個密碼
	</script>
		
</html>

RSA加密

  • RSA加密:
    • RSA加密算法是一種非對稱加密算法。在公開密鑰加密和電子商業中RSA被廣泛使用。
  • 非對稱加密算法:
    • 非對稱加密算法需要兩個密鑰:
      • 公開密鑰(publickey:簡稱公鑰)
      • 私有密鑰(privatekey:簡稱私鑰)
      • 公鑰與私鑰是一對,如果用公鑰對數據進行加密,只有用對應的私鑰才能解密。因為加密和解密使用的是兩個不同的密鑰,所以這種算法叫作非對稱加密算法。
  • 注意:
    • 使用時都是使用公匙加密使用私匙解密。公匙可以公開,私匙自己保留。
    • 算法強度復雜、安全性依賴於算法與密鑰但是由於其算法復雜,而使得加密解密速度沒有對稱加密解密的速度快。
  • 使用流程和場景介紹
    • 通過公匙加密,使用私匙解密。私匙是通過公匙計算生成的。假設ABC三方之間相互要進行加密通信。大家相互之間使用公匙進行信息加密,信息讀取時使用各自對應的私匙進行信息解密
    • 用戶輸入的支付密碼會通過RSA加密
  • 公鑰私鑰生成方式:

In [ ]:

<html>
	<script src="https://cdn.bootcss.com/jsencrypt/3.0.0-beta.1/jsencrypt.js"></script>
    <script type="text/javascript">
        //公鑰
        var PUBLIC_KEY = '-----BEGIN PUBLIC KEY-----MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALyBJ6kZ/VFJYTV3vOC07jqWIqgyvHulv6us/8wzlSBqQ2+eOTX7s5zKfXY40yZWDoCaIGk+tP/sc0D6dQzjaxECAwEAAQ==-----END PUBLIC KEY-----';
        //私鑰
		var PRIVATE_KEY = '-----BEGIN PRIVATE KEY-----MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAvIEnqRn9UUlhNXe84LTuOpYiqDK8e6W/q6z/zDOVIGpDb545NfuznMp9djjTJlYOgJogaT60/+xzQPp1DONrEQIDAQABAkEAu7DFsqQEDDnKJpiwYfUE9ySiIWNTNLJWZDN/Bu2dYIV4DO2A5aHZfMe48rga5BkoWq2LALlY3tqsOFTe3M6yoQIhAOSfSAU3H6jIOnlEiZabUrVGqiFLCb5Ut3Jz9NN+5p59AiEA0xQDMrxWBBJ9BYq6RRY4pXwa/MthX/8Hy+3GnvNw/yUCIG/3Ee578KVYakq5pih8KSVeVjO37C2qj60d3Ok3XPqBAiEAqGPvxTsAuBDz0kcBIPqASGzArumljkrLsoHHkakOfU0CIDuhxKQwHlXFDO79ppYAPcVO3bph672qGD84YUaHF+pQ-----END PRIVATE KEY-----';
		//使用公鑰加密
        var encrypt = new JSEncrypt();//實例化加密對象
        encrypt.setPublicKey(PUBLIC_KEY);//設置公鑰
        var encrypted = encrypt.encrypt('hello bobo!');//對指定數據進行加密
		alert(encrypted)
        //使用私鑰解密
        var decrypt = new JSEncrypt();
        decrypt.setPrivateKey(PRIVATE_KEY);//設置私鑰
        var uncrypted = decrypt.decrypt(encrypted);//解密
		alert(uncrypted);
    </script>
</html>

base64偽加密

  • Base64是一種用64個字符來表示任意二進制數據的方法。base64是一種編碼方式而不是加密算法。只是看上去像是加密而已。
  • Base64使用A--Z,a--z,0--9,+,/ 這64個字符實現對數據進行加密。

In [ ]:

<html>
    <script type="text/javascript">
		// 創建Base64對象
		var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t="";var n,r,i,s,o,u,a;var f=0;e=Base64._utf8_encode(e);while(f<e.length){n=e.charCodeAt(f++);r=e.charCodeAt(f++);i=e.charCodeAt(f++);s=n>>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+this._keyStr.charAt(s)+this._keyStr.charAt(o)+this._keyStr.charAt(u)+this._keyStr.charAt(a)}return t},decode:function(e){var t="";var n,r,i;var s,o,u,a;var f=0;e=e.replace(/[^A-Za-z0-9+/=]/g,"");while(f<e.length){s=this._keyStr.indexOf(e.charAt(f++));o=this._keyStr.indexOf(e.charAt(f++));u=this._keyStr.indexOf(e.charAt(f++));a=this._keyStr.indexOf(e.charAt(f++));n=s<<2|o>>4;r=(o&15)<<4|u>>2;i=(u&3)<<6|a;t=t+String.fromCharCode(n);if(u!=64){t=t+String.fromCharCode(r)}if(a!=64){t=t+String.fromCharCode(i)}}t=Base64._utf8_decode(t);return t},_utf8_encode:function(e){e=e.replace(/rn/g,"n");var t="";for(var n=0;n<e.length;n++){var r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r)}else if(r>127&&r<2048){t+=String.fromCharCode(r>>6|192);t+=String.fromCharCode(r&63|128)}else{t+=String.fromCharCode(r>>12|224);t+=String.fromCharCode(r>>6&63|128);t+=String.fromCharCode(r&63|128)}}return t},_utf8_decode:function(e){var t="";var n=0;var r=c1=c2=0;while(n<e.length){r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r);n++}else if(r>191&&r<224){c2=e.charCodeAt(n+1);t+=String.fromCharCode((r&31)<<6|c2&63);n+=2}else{c2=e.charCodeAt(n+1);c3=e.charCodeAt(n+2);t+=String.fromCharCode((r&15)<<12|(c2&63)<<6|c3&63);n+=3}}return t}}
		 
		// 定義字符串
		var string = 'i am bobo!';
		 
		// 加密
		var encodedString = Base64.encode(string);
		alert(encodedString); 
		 
		// 解密
		var decodedString = Base64.decode(encodedString);
		alert(decodedString); 
	</script>
		
</html>

拓展:https加密

  • https是基於http和SSL/TLS實現的一個協議,他可以保證在網絡上傳輸的數據都是加密的,從而保證數據安全。

  • 接下來我們從http協議開始,提出想法並逐步進行分析,最終實現Https。

    1. http協議是不安全的。
      • 在https誕生之前,所有網站都使用http協議,而http協議在數據傳輸的過程中都是明文,所以可能存在數據泄露和篡改。
      • image-20220331142728826
    1. 使用對稱秘鑰進行數據加密
      • 為了防止數據泄露和篡改,我們對數據進行加密,如:生成一個對稱密碼【DKUFHNAF897123F】,將對稱秘鑰分別交給瀏覽器和服務器端,他們之間傳輸的數據都使用對稱秘鑰進行加密和解密。
      • image-20220331142718097
  • 請求和響應流程如下:

    • 客戶端使用對稱秘鑰對請求進行加密,並發送給服務端。
    • 服務端接收到密文之后,使用對稱秘鑰對密文進行解密,然后處理請求。 最后再使用對稱秘鑰把要返回的內容再次加密,返回給客戶端。
    • 客戶端接收到密文之后,使用對稱秘鑰進行解密,並獲取最終的響應內容。
  • 如此一來,數據傳輸都是密文,解決了明文傳輸數據的問題。但是,這么干有bug。

    • 瀏覽器如何獲取對稱秘鑰?
    • 每個客戶端的對稱秘鑰相同,瀏覽器能拿到對稱秘鑰,那么黑客也可以拿到,所以,數據加密也就沒有意義了。
    1. 動態對稱秘鑰和非對稱秘鑰
      • 為了解決對稱秘鑰動態性以及讓客戶端和服務端安全的獲取對稱秘鑰,可以引入非對稱秘鑰機制。image-20220331142705701
  • 如此一來,解決了 動態對稱秘鑰 和 數據加密的問題,因為每個用戶的對稱秘鑰都是隨機生成且傳輸的過程中都使用公鑰加密(公鑰加密的數據只有私鑰能解密),所有黑客無法截獲對稱秘鑰。而數據傳輸是通過對稱秘鑰加密過的,所以黑客即使能獲取數據也無法去解密看到真實的內容。 看似無懈可擊,但是,這么干還是又bug。

  • 如果黑客在上圖 【步驟2】劫持,黑客把自己的公鑰返回給客客戶端,那么客戶端會使用黑客的公鑰來加密對稱秘鑰,黑客在【步驟6】截獲請求,使用自己的私鑰獲取對稱秘鑰,后面過程全都會完蛋...

    1. CA證書的應用
      • 使用 ca 證書可以解決黑客劫持的問題。
      • image-20220331142637995
  • 如此一來,就解決了黑客劫持的問題,因為即使黑客劫持后的給瀏覽器即使返回了證書也無法通過校驗,同時瀏覽器也會提示錯誤信息。

  • 以上就是Https的實現原理,https可以保證數據安全,但由過程需要反復加密解密所有訪問速度會有所下降(魚和熊掌不能兼得)。

In [ ]:

 

實踐1:微信公眾平台逆向

  • js調試工具
    • 發條js調試工具
  • PyExecJs
    • 實現使用python執行js代碼
    • 環境安裝:
      • 1.nodejs開發環境
      • 2.pip install PyExecJs
  • js算法改寫初探
    • 打斷點
    • 代碼調試時,如果發現了相關變量的缺失,一般給其定義成空字典即可。
打開微信公眾平台,用賬號登錄,打開F12

賬號123456789@163.com

密碼123456

1643440392040

發送請求,找到有賬號密碼的請求

1643440861547

用全局搜索 ctrl+shift+f 搜索pwd
1643440999989
在pwd這行打斷點,搜索pwd

1643441065873

經過斷點調試,找到生成pwd的js代碼
1643441188798
把這段js代碼復制到js生成工具中做逆向解析
1643441356091
發現n未定義,於是定義n
代碼加載成功;
改寫代碼,
function getPwd(e, t, n) {
return t ? n ? s(t, e) : o(s(t, e)) : n ? i(e) : o(i(e))
}
import execjs
#1.實例化一個node對象
node = execjs.get()

#2.js源文件編譯
ctx = node.compile(open('./wechat.js',encoding='utf-8').read())

#3.執行js函數
funcName = 'getPwd("{0}")'.format('123456')
pwd = ctx.eval(funcName)
print(pwd)

e10adc3949ba59abbe56e057f20f883e

實踐2:STEAM平台逆向

在python中進行逆向
#獲取秘鑰
import requests
import execjs

url = 'https://store.steampowered.com/login/getrsakey/'
data = {
    'donotcache': '1602552732596',
    'username': '123@qq.com',
}
headers = {
    'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',
}
response_json = requests.post(url=url,headers=headers,data=data).json()
mod = response_json['publickey_mod']
exp = response_json['publickey_exp']


#進行密碼逆向
node = execjs.get()
ctx = node.compile(open('./steam.js',encoding='utf-8').read())
funcName = 'getPwd("{0}","{1}","{2}")'.format('123456',mod,exp)
pwd = ctx.eval(funcName)
print(pwd)

Av0nQtvcBdyOZfSR5YBzsP9NIWhKOuzo6SPRjH2UPyvJtoyroy6u/eQIX4RStD6eMOg0hMTfvBNQip5th9IslLoDiKdCCshKyPmSpQVGObsm9PPsJ88duH9B5nqnWnUQ/zUADDUxWyrWswj8CPYw8HwdZQCIJ+yItYLS3Gmt4yD4Nk+c/2LenT54NUi0GEqGlNEmO1hGVIxDgfGfi8nsPVa8KlKL+ELjB9EolWWStnAfpuqPckBfb9bmY8jjHc8xDwY+RdsN6Xmfmtz6KN5cTiJ6VgJ4x9hkh+ThrfqNHatH5qHIibXi4UL7FJ2xCdejLWu/GVGLpC2PByyRN9hv2A==

實踐3:凡科網逆向

  • url:https://i.fkw.com/
  • 注意:如果需要逆向的js函數的實現是出現在一個閉包中,那么直接將閉包的整個代碼塊拷貝出進行調試即可。

1644195472860

1644195535976

import execjs
node = execjs.get()
ctx = node.compile(open('./fanke.js',encoding='utf-8').read())
funcName = "md5('{0}')".format('654321')
pwd = ctx.eval(funcName)
print(pwd)

c33367701511b4f6020ec61ded352059

實踐4:完美世界逆向

1644210030405

1644210072117

1644210125994

1644210703478

在發條工具中調試

1644217879243

需要通過·xpath拿到公鑰解密1644217964787
import requests
from lxml import etree
import execjs
headers = {
    'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'
}
#發起請求獲取公鑰
url = 'https://passport.wanmei.com/sso/login?service=passport&isiframe=1&location=2f736166652f'
page_text = requests.get(url=url,headers=headers).text
tree = etree.HTML(page_text)
key = tree.xpath('//input[@id="e"]/@value')[0]

#加密的逆向
node = execjs.get()
ctx = node.compile(open('./wanmei.js',encoding='utf-8').read())
funcName = 'getPwd("{0}","{1}")'.format('123456',key)
pwd = ctx.eval(funcName)
print(pwd)

JNTXRKgg9uuNfFhaJYrQTGEVf5kQWxFC2yGx14ywmCdgDiSn8JDUvV2PXzrP7n9C7VvbwXNEkwCNw3KGCToBi0tYpQUaVQ3QcGGF9sFfISo6UsB8YqSJ8gxFWYbWWKwCzYLsSa1HLREEQUJrQ1sEiwGZIWMi7vbrSD7mydAW+bw=

實踐5:試客聯盟逆向

  • url:http://login.shikee.com/
  • serializeArray():js函數的作用使用來實現序列化(對登錄頁面的表達中的值進行序列化,序列化成一個數組,數組元素就是表單中的數據【用戶名和密碼】)
  • key表示的是公鑰,公鑰的生成需要用rsa_n,rsa_n是什么鬼目前還不知道。后續對其做處理。
    • 基於抓包工具對rsa_n進行全局搜索。

1644224779460

1644224858370

發現有一個rsa_n,要找到rsa_n,通過全局搜索,1644224942333

通過棧跟蹤,找到加密password的代碼,全部拷貝下來

1644225069492

放到發條工具中調試

}
if (result.charCodeAt(result.length - 1) == 0) {
    result = result.substring(0, result.length - 1);
}
return (result);
}

function getPwd(p) {
setMaxDigits(131);
var rsa_n = 'DC1683EEAA2B709F97743773E18F53E3C9A15D12465CE82227A6E447E6EC590D0B569876BB631B0AB4D67881E7EC874066D6E022E2978B4C6EAA8903EC1774AAE040A3BEAF9C2B48730ADD46BEF5F0C8109DB6FCEFED0F4A84CCD7AFFDB4FB4214DA0D0FF1A8E2831E81FA4D7C2F4346184EEC87CE42230FC320B2B4E392ECDF'
var key = new RSAKeyPair("10001", '', rsa_n);
return encryptedString(key,p);
}

1644225375092

再放到python中動態獲取rsa_n

#捕獲rsa_n的值
import requests
import re
import execjs
headers = {
    'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',
}
url = 'http://login.shikee.com/getkey?v=4305e873fa87109730b17c5138e85cd8'
page_text = requests.get(url=url,headers=headers).text
ex = 'var rsa_n = "(.*?)";'
rsa_n = re.findall(ex,page_text)[0]

#實現密碼加密逆向操作
node = execjs.get()
 
pwd = '123456'
file = 'shike.js'
ctx = node.compile(open(file,encoding='utf-8').read())
funcName = 'getPwd("{0}","{1}")'.format(pwd,rsa_n)
passwd = ctx.eval(funcName)
print(passwd)

5787e6d0807f6ae002b7d2703f49f94c84bc76fb146c0970e6964821276790cf4ead90e64085d195b9fe10980898b7b02c82e5b14f260937118f8857348623476be1ea239546f3cb67629f43d98267133349d2058de625116b856efef004456600513c664fa08eb056a4fc5f1286f5c5ee7e0e2fdf64f4a644b158f10bfac23c

實踐6:空中網逆向

1.搜索password

2.找到生成加密password的代碼

3.發現是js混淆

  • js混淆:

    • 將js核心的相關代碼進行變相的加密,加密后的數據就是js混淆之后的結果。
  • js反混淆:

    • 反混淆的線上工具(不理想)
    • 瀏覽器自帶的反混淆工具設置(推薦)
      • 開發者工具的Sourse-》settings-》Sources-》第一項點上對勾
      • 進行關鍵字的全局搜索-》VMxx(就是反混淆后的代碼)
  • 發現了一個data['dc']不知道是什么,后續處理:

    • 全局搜索,直接搜索data,結果太多不便於定位

1644230993115

  • 1644231072455

    1644231317293

    4.打斷點找encrypt

    5.把生成password的代碼全部拷貝到發條

    1644231393408

    6.調試

    1644231415671

    7.放到python中逆向

#捕獲dc
import requests
import execjs
import re
import json
headers = {
    'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',
    'Referer': 'https://passport.kongzhong.com/'
}
url = 'https://sso.kongzhong.com/ajaxLogin?j=j&jsonp=j&service=https://passport.kongzhong.com/&_=1602643754066'
data = requests.get(url,headers=headers).text
ex = "KZLoginHandler.jsonpCallbackKongZ\((.*)\)"
dc = re.findall(ex,data)[0]
dc = json.loads(dc)['dc']

node = execjs.get()
pwd = '123456'
file = 'kongzhong.js'
ctx = node.compile(open(file,encoding='utf-8').read())
funcName = 'getPwd("{0}","{1}")'.format(pwd,dc)
passwd = ctx.eval(funcName)
print(passwd)

317109ccdbf60337663b

實踐7:廠房網逆向

import requests
import execjs
node = execjs.get()
pwd = '123456'
file = 'changfang.js'
ctx = node.compile(open(file,encoding='utf-8').read())
funcName = 'getPwd("{0}")'.format(pwd)
passwd = ctx.eval(funcName)
print(passwd)

䐵匠䴵LJHlwmWkl2aBSI2v8TOJ2A==

實踐8:有道翻譯逆向

  • url:http://fanyi.youdao.com/

  • 通過抓包工具抓取不同單詞對應的數據包進行請求參數的比對,發現只有如下三個參數需要動態處理:

    • salt
    • lts
    • sign
      • sign的值是經過md加密,加密的時候使用到了兩個變量:
        • e:需要翻譯的單詞
        • i:字符串形式的js時間戳+1位數的隨機整數,i是一個字符串類型的數據
          • 備注:python的時間戳*1000 = js時間戳
import requests
import execjs
import random
import time

#獲取sign
e = input('enter a English word:')
r = str(int(time.time() * 1000))
i = r +str(int(random.random()*10))
node = execjs.get()
ctx = node.compile(open('./youdao.js',encoding='utf-8').read())
funcName = "getSign('{0}','{1}')".format(e,i)
sign = ctx.eval(funcName)

url = 'http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule'
headers = {
    'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',
    'Referer': 'http://fanyi.youdao.com/',
    'Cookie':'OUTFOX_SEARCH_USER_ID=-86392255@10.108.160.19; OUTFOX_SEARCH_USER_ID_NCOO=1443501431.8073304; _ntes_nnid=313a624ab6f59f6f08cda47bce09570d,1602471473739; JSESSIONID=aaaIo9UjLVhPsfnx9UPux; ___rl__test__cookies=1602729224478'
}
data = {
    'i': e,
    'from': 'AUTO',
    'to': 'AUTO',
    'smartresult': 'dict',
    'client': 'fanyideskweb',
    'salt': i,
    'sign': sign,
    'lts': r,
    'bv': '725c1757d01ecab5adcab61daeb9d360',
    'doctype': 'json',
    'version': '2.1',
    'keyfrom': 'fanyi.web',
    'action': 'FY_BY_REALTlME',
}
result = requests.post(url,headers=headers,data=data).json()
print(result)

enter a English word:cat
{'translateResult': [[{'tgt': '貓', 'src': 'cat'}]], 'errorCode': 0, 'type': 'en2zh-CHS', 'smartResult': {'entries': ['', 'n. 貓,貓科動物\r\n'], 'type': 1}}

實踐9:空氣質量平台

js加密,js混淆,js逆向機制

  • url:https://www.aqistudy.cn/html/city_detail.html

  • 分析:

    • 1.當我們修改了頁面的查詢條件后,點擊查詢按鈕,在抓包工具中會捕獲到兩個一樣的數據包,我們只要能破解其中一個數據包的內容剩下一個也可以同理破解。
      • 請求的url:https://www.aqistudy.cn/apinew/aqistudyapi.php
      • 請求方式:post
      • 請求參數:d:一組動態變化且加密的數據值
      • 返回的響應數據就是我們想要捕獲的空氣指標數據
        • 響應數據也被加密,是一組被加密的密文數據。我們必須將密文數據解密成原文數據才可以被我們使用。
    • 2.我們在點擊查詢按鈕后,發現頁面沒有全局刷新,則表示點擊查詢按鈕后發起的是ajax請求。
      • 發起的ajax請求對應的數據包就是我們在step1表示的數據包。
      • 該ajax請求發送的時候請求的:
      • 請求回來的數據是一組密文數據,但是在前台頁面中顯示的為原文數據?
        • 原因在於前台將響應回來的密文數據進行解密。
          • 價值的信息:在該網站的相關數據包中是存在解密方案,我們只需要將其解密方法獲取,對密文進行解密即可。
    • 3.點擊查詢按鈕發起的是ajax請求,該請求在該網站中一定是用js或者jQuery寫的,在ajax請求的代碼中,可以看到那些具體的操作:
      • 請求的url
      • 請求的方式
        • 請求的url和請求方式可以從抓包工具中捕獲
      • 攜帶的請求參數
        • 可以從ajax的代碼中獲取
        • 為什么請求參數不可以從抓包工具中獲取?
          • 應為該參數是動態變化且加密
      • 請求回來的數據對應的回調函數
        • 函數的參數就是請求到的響應數據(加密的密文數據),該函數會對拿到的密文數據進行解密,將解密后的原文動態顯示在前台頁面中。
      • 重點:只要能夠找到點擊查詢按鈕對應的ajax請求代碼后,就可以知道動態變化且加密的請求參數如何生成。那么我們就可以使用該方式動態生成一組請求參數,然后基於指定的url進行請求發送,獲取加密的響應數據。獲取的密文數據就可以通過ajax請求代碼中的回調/匿名函數的操作中獲取解密方案,使用該方案對加密的響應數據解密即可。【大功告成】
  • 操作:找尋ajax請求的代碼

    • 方案:獲取搜索按鈕對應的點擊事件綁定的函數即可。
      • 最好使用火狐瀏覽器找尋點擊事件的函數代碼。

image-20220331142229337

  • 點擊搜索按鈕對應的點擊事件函數名叫做getData,接下來就進入該函數內部找尋ajax請求代碼。

    • 找到了getData函數的實現:
      • 沒有發現ajax代碼實現
      • 發現函數內部調用了另外的兩個函數 getAQIData();getWeatherData();
      • ajax代碼的實現一定是在上述的兩個函數內部
      • 有價值的信息:
        • type=="HOUR":查詢時間是以小時為單位
          • 進入到getAQIData();getWeatherData()函數內部找尋ajax代碼實現:
          • 在這兩個函數內部沒有發現ajax代碼
          • 調用了一個叫做getServerData()函數,那么ajax代碼一定存在getServerData函數中。
          • getServerData的參數:
            • method:GETDETAIL字符串
            • param:字典,有四組鍵值對
              • - param.city = city; - param.type = type; - param.startTime = startTime; - param.endTime = endTime;
            • 回調函數
            • 0.5
    • 進入到getServerData函數內部找尋ajax代碼:
      • 函數實現的找尋,需要在抓包工具中進行全局搜索。
      • 找到的結果看上去像是密文,看不懂。
      • js混淆:
        • 在網站后台,關鍵的重要的js函數的實現為了保密,一般會對這些js函數代碼進行混淆(加密),所以我們需要對js混淆的代碼進行反混淆,將加密的js函數進行解密。
      • 反混淆:
  • getServerData反混淆后的實現:

function getServerData(method, object, callback, period) {
        const key = hex_md5(method + JSON.stringify(object));
        const data = getDataFromLocalStorage(key, period);
        if (!data) {
            var param = getParam(method, object);
            $.ajax({
                url: '../apinew/aqistudyapi.php',
                data: {
                    d: param#加密且動態變化的請求參數
                },
                type: "post",
                #data請求到的加密的響應數據
                success: function (data) {
                    #decodeData就是對加密的響應數據進行解密
                    data = decodeData(data);
                    obj = JSON.parse(data);
                    if (obj.success) {
                        if (period > 0) {
                            obj.result.time = new Date().getTime();
                            localStorageUtil.save(key, obj.result)
                        }
                        callback(obj.result)
                    } else {
                        console.log(obj.errcode, obj.errmsg)
                    }
                }
            })
        } else {
            callback(data)
        }
    }
  • 分析getServerData函數實現中的ajax代碼:

    • 動態變化且加密的請求參數
      • getParam(method, object)
        • method:GETDETAIL
        • object:
          • param:字典,有四組鍵值對
            • param.city = city;
            • param.type = type;
            • param.startTime = startTime;
            • param.endTime = endTime;
    • decodeData(data):將data這個加密的響應數據進行解密
      • 是需要調用該函數就可以對加密的響應數據進行解密
    • 總結:
      • 調用getParam后就可以獲取加密變化的請求參數
      • 調用decodeData就可以將響應數據進行解密
    • 問題:
      • 上述的兩個函數是js函數,我們現在寫的爬蟲程序是python,python如何調用js函數呢?
      • python如何調用js函數
        • 方式1:
          • 可以將js函數改寫為python函數
        • 方式2:
          • 使用相關模塊進行js逆向
          • PyExecJS介紹:PyExecJS 是一個可以使用 Python 來模擬運行 JavaScript 的庫。我們需要pip install PyExecJS對其進行環境安裝。除此之外還需要安裝nodejs的開發環境。
            • js逆向:
            • 將js函數改寫為python函數
  • PyExecJS的使用

    • 1.需要將待執行的js函數的全部定義存儲到一個js源文件中
    • 2.模擬執行js源文件中的js函數
      • 1.加載js源文件中的代碼
      • 2.將js源文件中的代碼進行編譯
#使用模塊進行js逆向,模擬調用js源文件(test.js)中的getParams函數獲取動態變化加密的請求參數
import execjs
#實例化一個對象
node = execjs.get()
# Params
method = 'GETDETAIL'
city = '北京'
type = 'HOUR'
start_time = '2018-01-25 00:00:00'
end_time = '2018-01-25 23:00:00'
 
# Compile javascript
file = 'test.js'#待加載編譯的js源文件
#加載編譯js源文件中的js代碼
ctx = node.compile(open(file,encoding='utf-8').read())
 
# Get params
js = 'getPostParamCode("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, type, start_time, end_time)
#eval表示模擬執行編譯好的js函數
params = ctx.eval(js)
print(params)#返回是加密變化的請求參數

tdgHOYxwKdDSgYXe+RLPzYCgLvrddahasI5XXklB4gVLYqab+XRPpMD/oSqnJ/aEmFwzVEUhLnPzRy03+X1BIzLvxQKwu4A3YsqR3OemYgNnHqPdBwvJlbxia99YeK+xfgEPMfQ8z6n7Ztqd19fYky9NG2tISsGzZYlOuNWFekTywpTbzjA6pajfMz6X9BKKJOJNxwImjmT7z70O5l3nWcdylkn/wPjSbS9Xloz77/xzWwIyk5QlPmxlJw1rU+EeePA78mb5U7G3LQga46sQVjFHwviqZ/nUtjaJgvnE4USD6xOyhIcFRZD7VWC44QCUFfwE4YTt3CXWuEp6leCBZ3cAaZtvUBDZqmpf3QbUO9EbDMkvQaldUD1eP9v9JJUVyCq3RviKaPNVHJLsXXBYy4x6Q9oaB0VBMaO5nv467qgVkhBcBIO9xUUZsAv2rfBZMhSuxsdBeXvJKyyTN3MA1A==
  • 可以對ajax進行請求發送
import execjs
import requests

node = execjs.get()
# Params
method = 'GETDETAIL'
city = '北京'
type = 'HOUR'
start_time = '2018-01-25 00:00:00'
end_time = '2018-01-25 23:00:00'
 
# Compile javascript
file = 'test.js'#待加載編譯的js源文件
#加載編譯js源文件中的js代碼
ctx = node.compile(open(file,encoding='utf-8').read())
 
# Get params
js = 'getPostParamCode("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, type, start_time, end_time)
#eval表示模擬執行編譯好的js函數
params = ctx.eval(js)

#發起post請求
url = 'https://www.aqistudy.cn/apinew/aqistudyapi.php'
response_text = requests.post(url, data={'d': params}).text
print(response_text)#返回加密的響應數據

n8Un3OpswjBlUIgnbmQ+Npphg68NTuGGzboCX28bX4NaHfiL5LH6CmmfDxyY74+E7OD4v/JpTW7vrVHQDi9Pev5HYg9Fcuso1ZVkZhd910MsBJkkjnJuAZomwI+NTEgwhmDiZkYK6YuIWq3X0qgUrQ==
  • 加密的響應數據獲取了,接下來,模擬執行decodeData的js函數進行加密響應數據的解密
import execjs
import requests

node = execjs.get()
# Params
method = 'GETDETAIL'
city = '北京'
type = 'HOUR'
start_time = '2018-01-25 00:00:00'
end_time = '2018-01-25 23:00:00'
 
# Compile javascript
file = 'test.js'#待加載編譯的js源文件
#加載編譯js源文件中的js代碼
ctx = node.compile(open(file,encoding='utf-8').read())
 
# Get params
js = 'getPostParamCode("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, type, start_time, end_time)
#eval表示模擬執行編譯好的js函數
params = ctx.eval(js)

#發起post請求
url = 'https://www.aqistudy.cn/apinew/aqistudyapi.php'
#返回加密的響應數據
response_text = requests.post(url, data={'d': params}).text


#模擬執行decodeData來對密文數據進行解密
js = 'decodeData("{0}")'.format(response_text)
decrypted_data = ctx.eval(js)
print(decrypted_data)
  • 返回的原文形式的響應數據: {"success":true,"errcode":0,"errmsg":"success","result":{"success":true,"data":{"total":24,"rows":[{"time":"2018-01-25  00:00:00","temp":"-7","humi":"35","wse":"1","wd":"\u4e1c\u5317\u98ce","tq":"\u6674"},{"time":"2018-01-25  01:00:00","temp":"-9","humi":"38","wse":"1","wd":"\u897f\u98ce","tq":"\u6674"},{"time":"2018-01-25  02:00:00","temp":"-10","humi":"40","wse":"1","wd":"\u4e1c\u5317\u98ce","tq":"\u6674"},{"time":"2018-01-25  03:00:00","temp":"-8","humi":"27","wse":"2","wd":"\u4e1c\u5317\u98ce","tq":"\u6674"},{"time":"2018-01-25  04:00:00","temp":"-8","humi":"26","wse":"2","wd":"\u4e1c\u98ce","tq":"\u6674"},{"time":"2018-01-25  05:00:00","temp":"-8","humi":"23","wse":"2","wd":"\u4e1c\u5317\u98ce","tq":"\u6674"},{"time":"2018-01-25  06:00:00","temp":"-9","humi":"27","wse":"2","wd":"\u4e1c\u5317\u98ce","tq":"\u591a\u4e91"},{"time":"2018-01-25  07:00:00","temp":"-9","humi":"24","wse":"2","wd":"\u4e1c\u5317\u98ce","tq":"\u591a\u4e91"},{"time":"2018-01-25  08:00:00","temp":"-9","humi":"25","wse":"2","wd":"\u4e1c\u98ce","tq":"\u6674\u8f6c\u591a\u4e91\u8f6c\u591a\u4e91\u95f4\u6674"},{"time":"2018-01-25  09:00:00","temp":"-8","humi":"21","wse":"3","wd":"\u4e1c\u5317\u98ce","tq":"\u6674\u8f6c\u591a\u4e91\u8f6c\u591a\u4e91\u95f4\u6674"},{"time":"2018-01-25  10:00:00","temp":"-7","humi":"19","wse":"3","wd":"\u4e1c\u5317\u98ce","tq":"\u6674\u8f6c\u591a\u4e91\u8f6c\u591a\u4e91\u95f4\u6674"},{"time":"2018-01-25  11:00:00","temp":"-6","humi":"18","wse":"3","wd":"\u4e1c\u5317\u98ce","tq":"\u591a\u4e91"},{"time":"2018-01-25  12:00:00","temp":"-6","humi":"17","wse":"3","wd":"\u4e1c\u5317\u98ce","tq":"\u591a\u4e91"},{"time":"2018-01-25  13:00:00","temp":"-5","humi":"17","wse":"2","wd":"\u4e1c\u5317\u98ce","tq":"\u591a\u4e91"},{"time":"2018-01-25  14:00:00","temp":"-5","humi":"16","wse":"2","wd":"\u4e1c\u98ce","tq":"\u591a\u4e91"},{"time":"2018-01-25  15:00:00","temp":"-5","humi":"15","wse":"2","wd":"\u5317\u98ce","tq":"\u591a\u4e91"},{"time":"2018-01-25  16:00:00","temp":"-5","humi":"16","wse":"2","wd":"\u4e1c\u5317\u98ce","tq":"\u591a\u4e91"},{"time":"2018-01-25  17:00:00","temp":"-5","humi":"16","wse":"2","wd":"\u4e1c\u98ce","tq":"\u591a\u4e91"},{"time":"2018-01-25  18:00:00","temp":"-6","humi":"18","wse":"2","wd":"\u4e1c\u98ce","tq":"\u6674\u95f4\u591a\u4e91"},{"time":"2018-01-25  19:00:00","temp":"-7","humi":"19","wse":"2","wd":"\u4e1c\u98ce","tq":"\u6674\u95f4\u591a\u4e91"},{"time":"2018-01-25  20:00:00","temp":"-7","humi":"19","wse":"1","wd":"\u4e1c\u98ce","tq":"\u6674\u95f4\u591a\u4e91"},{"time":"2018-01-25  21:00:00","temp":"-7","humi":"19","wse":"0","wd":"\u5357\u98ce","tq":"\u6674\u95f4\u591a\u4e91"},{"time":"2018-01-25  22:00:00","temp":"-7","humi":"22","wse":"1","wd":"\u4e1c\u5317\u98ce","tq":"\u6674\u95f4\u591a\u4e91"},{"time":"2018-01-25  23:00:00","temp":"-9","humi":"27","wse":"1","wd":"\u897f\u5357\u98ce","tq":"\u6674\u95f4\u591a\u4e91"}]}}}
    

09.移動端數據爬取

前言

  • 隨着移動市場的火熱,各大平台都陸陸續續的推出了自己的移動端APP來拉攏吸引和便捷其廣大的用戶。那么在移動端的平台當時勢必會出現大量有價值的信息和數據,那這些數據我們是否可以去享用一下呢?那么接下來就進入我們的移動端APP數據的爬蟲中來吧。

今日概要

  • fiddler簡介
  • 手機APP抓包設置
    • fiddler設置
    • 安裝證書下載
    • 安全證書安裝
    • 局域網設置
    • fiddler手機抓包測試

今日詳情

1 什么是Fiddler?
Fiddler是位於客戶端和服務器端的HTTP代理,也是目前最常用的http抓包工具之一 。 它能夠記錄客戶端和服務器之間的所有 HTTP請求,可以針對特定的HTTP請求,分析請求數據、設置斷點、調試web應用、修改請求的數據,甚至可以修改服務器返回的數據,功能非常強大,是web調試的利器。

既然是代理,也就是說:客戶端的所有請求都要先經過Fiddler,然后轉發到相應的服務器,反之,服務器端的所有響應,也都會先經過Fiddler然后發送到客戶端,基於這個原因,Fiddler支持所有可以設置http代理為127.0.0.1:8888的瀏覽器和應用程序。使用了Fiddler之后,web客戶端和服務器的請求如下所示:img

利用可以設置代理的這個特點,我們就可以對手機APP進行抓包了。怎么設置?不急不急,讓我先把Fiddler安裝上吧!

Fiddler下載地址:https://www.telerik.com/fiddler

傻瓜式安裝,一鍵到底。Fiddler軟件界面如圖所示:img

2 手機APP抓包設置

a. Fiddler設置

打開Fiddler軟件,打開工具的設置。(Fiddler軟件菜單欄:Tools->Options)

在HTTPS中設置如下:

img

在Connections中設置如下,這里使用默認8888端口,當然也可以自己更改,但是注意不要與已經使用的端口沖突:

Allow remote computers to connect:允許別的機器把請求發送到fiddler上來

img

b. 安全證書下載
在電腦瀏覽器中輸入地址:http://localhost:8888/,點擊FiddlerRoot certificate,下載安全證書:

img
c. 安全證書安裝
證書是需要在手機上進行安裝的,這樣在電腦Fiddler軟件抓包的時候,手機使用電腦的網卡上網才不會報錯。

Android手機安裝:把證書放入手機的內置或外置存儲卡上,然后通過手機的"系統安全-》從存儲設備安裝"菜單安裝證書。

​ 然后找到拷貝的FiddlerRoot.cer進行安裝即可。安裝好之后,可以在信任的憑證中找到我們已經安裝好的安全證書。img

蘋果手機安裝:

​ - 保證手機網絡和fiddler所在機器網絡是同一個網段下的

​ - 在safari中訪問http://fiddle機器ip:fiddler端口,進行證書下載。然后進行安裝證書操作。

​ - 在手機中的設置-》通用-》關於本機-》證書信任設置-》開啟fiddler證書信任

d. 局域網設置
想要使用Fiddler進行手機抓包,首先要確保手機和電腦的網絡在一個內網中,可以使用讓電腦和手機都連接同一個路由器。當然,也可以讓電腦開放WIFI熱點,手機連入。這里,我使用的方法是,讓手機和電腦同時連入一個路由器中。最后,讓手機使用電腦的代理IP進行上網。
在手機上,點擊連接的WIFI進行網絡修改,添加代理。進行手動設置,ip和端口號都是fiddler機器的ip和fiddler上設置的端口號。

e. Fiddler手機抓包測試

上述步驟都設置完成之后,用手機瀏覽器打開百度首頁,我們就可以順利抓包了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM