仕事を説明します。逆にJS
アウトライン
url:https://nyloner.cn/proxy
需求:将这个网页中的代理ip和端口号进行爬取
难点:
动态变化的请求参数
js加密
需要js逆向
分析
クロールされたデータは、動的ローディングあります
そして、私たちは、グローバルな検索のためのパケットキャプチャツールを持っていた結果が見つかりませんでした
- 手段:サーバーからのデータ要求をクロールは、暗号文データに暗号化されています
たびにページが発見データ更新をリフレッシュした後、10秒に更新されますが、ブラウザのアドレスバーのURLは、データがへのAjaxリクエストによってロードされていることを示す、変更されません。
- 動的AJAXリクエストに出たデータによりロードされ、データがデータを暗号化された要求
アヤックスは、パケットをターゲットとし、そのあなたが暗号化された要求のURLパラメータと動的応答データを見ることができます
暗号文データの取り込みにAjaxリクエスト
動的動的にアクセスリクエストパラメータを変更します
キャプチャツールを変更し、次のようにダイナミックに基づきjsのコードのソースを見つけることがトークンとトークンのグローバル検索パラメータ要求は、次のとおりです。
var token = md5(String(page) + String(num) + String(timestamp));
暗号文データを復号化
- decode_str(encode_str):復号化機能JSを見つけるために解決することによって、
のリアライズのencode_strを探します:
- リバースJS:JS Pythonコードに変換するコード。開発環境はPythonのコードを実行することができます
答え
動的パラメータ解析
# 我们抓包看到 token是加密生成 则把它进行加密
var token = md5(String(page) + String(num) + String(timestamp));
# 进行加密
def getToken():
page = str(1)
num = str(15)
t = str(int(time.time()))
md5 = hashlib.md5()
md5.update((page+num+t).encode('utf-8'))
token = md5.hexdigest()
return token
WebページのソースJS
# 动态参数加密
function get_proxy_ip(page, num, click_btn) {
var timestamp = Date.parse(new Date());
timestamp = timestamp / 1000;
# token的加密
var token = md5(String(page) + String(num) + String(timestamp));
# ajax 请求 类似这样的url:https://nyloner.cn/proxy?page=1&num=15&token=26c8bc7&t=1575
$.get('../proxy?page=' + page + '&num=' + num + '&token=' + token + '&t=' + timestamp, function (result) {
# 判断 是否为True
if (result.status === 'true') {
var setHtml = "";
$("#ip-list").html(setHtml);
var encode_str = result.list;
# decode_str 是解密函数
var items = str_to_json(decode_str(encode_str));
for (var index = 0; index < items.length; ++index) {
item = items[index];
setHtml += "<tr>\n<td>" + (index + 1) + "</td>\n";
setHtml += "<td>" + item.ip.toString() + "</td>\n";
setHtml += "<td>" + item.port.toString() + "</td>\n";
setHtml += "<td>" + item.time.toString() + "</td>\n</tr>\n";
}
$("#ip-list").html(setHtml);
if (click_btn === 'next') {
document.getElementById("last-page").disabled = false;
if (items.length < 15) {
document.getElementById("next-page").disabled = true;
}
} else {
document.getElementById("next-page").disabled = false;
if (page === 1) {
document.getElementById("last-page").disabled = true;
}
}
}
});
}
取得データを要求すると、URLにトークンを暗号化して取得します
import time
import hashlib
import requests
import base64
#js逆向之后的结果
def decode_str(scHZjLUh1):
#解密成字符串
scHZjLUh1 = base64.decodestring(scHZjLUh1.encode())
key = 'nyloner'
lenth = len(key)
code = ''
sch_lenth = len(scHZjLUh1)
for i in range(sch_lenth):
coeFYlqUm2 = i % lenth
#chr(0-255)返回对应编码的字符
#ord(a-z)返回编码数值
code += chr(scHZjLUh1[i] ^ ord(key[coeFYlqUm2]))
code = base64.decodestring(code.encode())
code = code.decode('utf-8')
code
# return code
暗号化されたデータの解析
token = getToken()
url = 'https://nyloner.cn/proxy'
param = {
'num':'15',
'page':'1',
't':str(int(time.time())),
'token':token
}
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36',
'Cookie':'sessionid=2cj1fd0hpbv64qrxe6xckek8tu3uad4m'
}
code = requests.get(url,headers=headers,params=param).json().get('list')
str_code = decode_str(code)
str_code
JSの暗号化、混乱
需要の概要
url:https://www.aqistudy.cn/html/city_detail.html
获取该url的数据
要求分析
- 異なる気象インデックスタブ上の1.をクリックして、すべての気象データがロードされた、現在のページの読み込み時間を示し、該当の要求は認められませんでした
- 2.データが動的にロードされているかどうか
- データは動的にロードされ、
- 3.変更クエリ条件(市の切り替え時間変更)、検索ボタンをクリックすると、新しいデータをロードします
- キャプチャツールのXHRで二つのパケットへのアクセス4
- 次のようなURL
- 私たちは、このような要求パラメータdを持っています
- 二つの異なる要求のパラメータ・データ・パケットのD値
- Dこの要求は暗号化されたパラメータと動的に変化
- そして、プロセスの動的に変化暗号化されたリクエストのパラメータd
- リクエストパラメータはグローバル検索をD(実行不可能)
- 2つのAJAX要求にキャプチャデータパケットにパケットキャプチャツールでは、ページ上の[検索]ボタンをクリックします
- AJAX要求、ボタンのクリックイベントの外観を開始するためのボタンをクリックします(クリック)
- Firefoxのブラウザの開発者向けツールでボタンのクリック時間を検索します
- getDate()jsの機能
- getDate()jsの機能
- 分析のgetDateは、このjsの機能を実現する:目的は、要求に対応するAJAX jsのコードを見つけることです
- type=='HOUR',按照小时为时间单位进行查询
- 并没有在该函数的实现中发现ajax请求对应的代码,但是发现了另外的两个函数调用getAQIData();getWeatherData();
- 分析getWeatherData();和getAQIData()这两个函数的定义,想要去找到ajax请求对应的代码:
- 这两个函数实现的区别
- method变量赋值的字符串不一样
- GETDETAIL
- GETCITYWEATHER
- method变量赋值的字符串不一样
- 相同:
- 都没有出现ajax请求对应的代码,但是发现另一个函数的调用:
- getServerData(method,param,匿名函数,0.5)
- method:GETDETAIL或者GETCITYWEATHER
- param:字典,有四组键值对
- city:查询城市的名称
- type:HOUR
- starttime:查询开始时间
- endTIME:查询结束的时间
- 分析getServerDate(method,param,匿名函数,0.5)这个函数的实现,还是为了找到ajax请求对应的代码
基于抓包工具的全局搜索才可以找到
- 发现这个函数的实现代码看不懂,函数的实现好像是一组密文
- 说明:网站对js函数的实现加密
- js混淆:对js函数的实现代码进行加密
- js反混淆:将加密的js代码解密成原文
- 暴力破解:https://www.bm8.com.cn/jsConfusion/
- 分析getServerData函数的实现:
- 终于找到了ajax请求对应的代码
- 参数d的构成:getParam(method,object)返回
- method:method
- object:param字典,四个键值分别是城市名称。type,起始时间,结束时间
- 请求到的密文数据的解密方式
- decodeData(data):data参数就是响应的密文数据,返回值就是解密后的原文数据
- JS逆向
- 使用PyExecJS库来实现模拟JavaScript代码执行
- 环境的安装:
- pip install PyExecJS
- 必须还要安装nodeJS的开发环境
- getServerData(method,param,匿名函数,0.5)
- 都没有出现ajax请求对应的代码,但是发现另一个函数的调用:
- 请求到加密的响应数据
- 将加密的响应数据进行解密
- 这两个函数实现的区别
- 分析getWeatherData();和getAQIData()这两个函数的定义,想要去找到ajax请求对应的代码:
- 获取动态变化且加密的请求参数(d)
import execjs
import requests
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
}
node = execjs.get()
file = 'test.js'
ctx = node.compile(open(file,encoding='utf-8').read())
#如何五个变量会作为getPostParamCode的参数
method = 'GETDETAIL'#GETCITYWEATHER
city = '北京'
type = 'HOUR'
start_time = '2018-01-25 00:00:00'
end_time = '2018-01-25 23:00:00'
#模拟执行getPostParamCode函数
js = 'getPostParamCode("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, type, start_time, end_time)
params = ctx.eval(js)
# print(params)#请求参数d
url = 'https://www.aqistudy.cn/apinew/aqistudyapi.php'
data = {
'd':params
}
#获取了加密的响应数据
response_code = requests.post(url=url,headers=headers,data=data).text
# response_code
#模拟执行decodeData函数对密文数据进行解密
js = 'decodeData("{0}")'.format(response_code)
page_text = ctx.eval(js)
print(page_text)