關注微信公眾號:K哥爬蟲,持續分享爬蟲進階、JS/安卓逆向等技術乾貨!
宣告
本文章中所有內容僅供學習交流,抓包內容、敏感網址、資料介面均已做脫敏處理,嚴禁用於商業用途和非法用途,否則由此產生的一切後果均與作者無關,若有侵權,請聯絡我立即刪除!
逆向目標
- 目標:Ether Rock(一種數字貨幣)空投介面 AES256 加密分析
- 主頁:
aHR0cHM6Ly9ldGhlcnJvY2submV0L2FpcmRyb3Av
- 介面:
aHR0cHM6Ly9ldGhlcnJvY2submV0L2FpcmRyb3Atc3VibWl0
- 逆向引數:Form Data:
content: U2FsdGVkX1/XnffSPZONOHb...
key: jrwBwX2ll38bu/FFql+bAUYrRG8Ij...
逆向分析
來到空投頁面,隨便輸入一個 ETH 錢包地址,點選提交,可抓包到提交介面,POST 請求,Form Data 裡 content 和 key 引數均經過了加密處理,如下圖所示:
老方法,嘗試直接搜尋,結果很多,不利於快速定位,XHR 斷點,很容易定位到加密位置,如下圖所示:
一步一步分析,首先定義了 content 物件:
var content={
address:$(this).find('input[name=address]').val(),
ref:$(this).find('input[name=ref]').val(),
uuid:uuid,
tz:tz,
tz_offset:tz_offset,
screen:window.screen.width+'x'+window.screen.height+'x'+window.screen.colorDepth,
user_agent:navigator.userAgent,
cpu:navigator.hardwareConcurrency,
lang:navigator.language||navigator.userLanguage,
};
address 是錢包地址,ref、uuid 為空,tz 是時區,tz_offset 是時區偏移量,即當前時區與格林尼治標準時間(GMT)的差,screen 是螢幕相關資訊,user_agent 是瀏覽器資訊,cpu 是處理器數量,lang 是語言。這些值除了 address 以外都可以固定。
接下來定義了一個 key:var key=random_string(36);
,跟進 random_string()
方法,可以看到是進行了一些取隨機值和冪運算,可以直接 copy 下來,如下圖所示:
接著將定義的 content 和生成的 key 進行了一個叫做 AES256 的加密:content=AES256.encrypt(JSON.stringify(content),key);
這裡 AES256 一般是指的金鑰長度為 32 bytes(256 bit / 8)的 AES 加密,但是不要被名稱迷惑,我們跟進去看看:
可以看到實際上是呼叫了 h.AES.encrypt()
方法,往上看這個 h,可以看到是引用了 node-cryptojs-aes,支援 AES 對稱金鑰加密,這裡就比較簡單了,我們在本地也直接引入這個庫即可,至此,content 的加密方式就找到了。
接下來看 key 值,這個就更簡單了,很明顯用的是 jsencrypt 庫,對原來生成的 36 位字串的 key 進行了 RSA 加密,同樣在本地直接引用庫即可。
完整程式碼
GitHub 關注 K 哥爬蟲,持續分享爬蟲相關程式碼!歡迎 star !https://github.com/kgepachong/
以下只演示部分關鍵程式碼,不能直接執行! 完整程式碼倉庫地址:https://github.com/kgepachong...
JavaScript 加密程式碼
function randomString(N) {
if (!parseInt(N, 10)) N = 6;
var rs = Math.floor(Math.pow(36, N) * Math.random()).toString(36);
return (Math.pow(10, N) + rs).substr(-N);
}
var h = require("node-cryptojs-aes").CryptoJS
, p = {
stringify: function (b) {
var e = h.enc.Hex.parse(b.salt.toString()).toString(h.enc.Latin1);
b = b.ciphertext.toString(h.enc.Latin1);
return h.enc.Latin1.parse("Salted__" + e + b).toString(h.enc.Base64)
},
parse: function (b) {
b = h.enc.Base64.parse(b).toString(h.enc.Latin1);
if ("Salted__" !== b.substr(0, 8))
throw Error("Error parsing salt");
var e = b.substr(8, 8);
b = b.substr(16);
return h.lib.CipherParams.create({
ciphertext: h.enc.Latin1.parse(b),
salt: h.enc.Latin1.parse(e)
})
}
};
var e = randomString(36);
function getContent(address) {
var b = JSON.stringify({
"address": address,
"ref": "",
"uuid": "",
"tz": "Asia/Shanghai",
"tz_offset": 8,
"screen": "1920x1080x24",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36",
"cpu": 8,
"lang": "zh"
})
return h.AES.encrypt(b, e, {
format: p
}).toString()
}
function getKey() {
JSEncrypt = require("jsencrypt")
var crypt = new JSEncrypt();
var pub = [
'-----BEGIN PUBLIC KEY-----',
'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVmYQhCYTnnkTPRMI5Ad3vfad9',
'lhjzOU92FZ3reUiN/vmqP/wC1DKKExYDsqa+w5xBP0AjGkfDWk3q4PlWu0UsBGZx',
'62Gvt0ds75u8FnmLv+ufMimF4962/9Lx7uyh9g1H3/ze5ZXscWYy3gtts9d2Ga0R',
'pl0X49Cz0JhYYicuGwIDAQAB',
'-----END PUBLIC KEY-----',
];
crypt.setPublicKey(pub.join('\n'));
key = crypt.encrypt(e);
return key
}
function getContentAndKey(address) {
result = {
"key": getKey(),
"content": getContent(address)
}
return result
}
// 測試樣例
// console.log(getContentAndKey("xxxxxxxxxxxxxxxx"))
Python 程式碼
# ==================================
# --*-- coding: utf-8 --*--
# @Time : 2021-11-24
# @Author : 微信公眾號:K哥爬蟲
# @FileName: airdrop_submit.py
# @Software: PyCharm
# ==================================
import execjs
import requests
def get_content_and_key(address):
with open("get_content_and_key.js", encoding="utf-8") as f:
ether_rock_js = f.read()
content_and_key_dict = execjs.compile(ether_rock_js).call('getContentAndKey', address)
return content_and_key_dict
def airdrop_submit(content_and_key_dict):
submit_url = "脫敏處理,完整程式碼關注 GitHub:https://github.com/kgepachong/crawler"
headers = {
"Accept": "text/html, */*; q=0.01",
"Accept-Language": "zh,zh-CN;q=0.9,en-US;q=0.8,en;q=0.7",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36",
"X-Requested-With": "XMLHttpRequest",
"Host": "脫敏處理,完整程式碼關注 GitHub:https://github.com/kgepachong/crawler",
"Origin": "脫敏處理,完整程式碼關注 GitHub:https://github.com/kgepachong/crawler",
}
data = {
"content": content_and_key_dict["content"],
"key": content_and_key_dict["key"]
}
response = requests.post(url=submit_url, data=data, headers=headers)
print(response.text)
def main():
address = input("請輸入ETH錢包地址領取空投: ")
content_and_key_dict = get_content_and_key(address)
airdrop_submit(content_and_key_dict)
if __name__ == '__main__':
main()