新人发帖,请多指教
前情提要
最近在研究塔可夫的DMA辅+++助,想要直观显示游戏内各种垃圾的每日平均价格,又不想直接逆向游戏内市场的繁杂逻辑(懒,且并不是要获取实时价格),遂在UC上一通搜索找到了tarkov-market.com这个网站。
想着利用这个网站的数据搞个脚本每天缓存一遍平均价格存储在本地以供使用。
一番摸索发现此网站提供官方API但是要收费,5刀每月,还不能试用。
本着先体验后付费的精神(白嫖),本次逆向就开始了。
正篇
查未公开接口
先进页面,往下滚动发现是滚动刷新,判别是瀑布流加载。
2
按F12呼出控制台,切换到Network并点上Preserve log和
Disable Cache,然后清空一下刷新页面。
3
搜索一下12.7的弹药
1
哦豁,什么都没有,可能是加密了。直接点选XHR。
4
有点眉目,看看里面啥样
没有验签,舒服!
5
数据加密了
6
末尾是=符号,有可能是base64加密
7
找个在线解密碰碰运气,个人常用:Base64在线加解密;
什么鬼,看来是处理过的。也有可能不是base64加密,扒代码!
8
复制一下链接,不用复制后面的两个参,(已经回返的数据条数和每次返回的数据条数)
10
请求接口没有验签,不用回溯这个调用逻辑,直接对XHR下断点
9
11
搞定
12
往下滚动网页加载数据,网页断下
13
还在请求部分,跳出当前函数,Shift+F11,
14
跳到这里
15
下面开始看参数(回传的乱码)变化进行单步 F9
- 一次F9
16
- 二次F9
17
- 三、四、五次F9
18
- 六次F9
19
- 七次F9
20
有点东西了,下面的看起来像是处理Json的部分,我们重点看看
21
把XHR断电移除,在这里下个断
22
往下滚动网页加载数据,网页断下,这里就是解密的部分
23
看起来不难,就不扣JS代码,直接搞逻辑然后用Python重写, 我就在下面的代码上进行注释了(自下而上进行回溯),断点不要释放
const xa = String[mi(375)]
, km = xa(100 - 3) + xa(110 + 6) + xa(105 + 6) + xa(105 - 7)
, Tv = ()=>[(31 - 6) / (30 * 100 >> 9), parseInt(window[km](mi(385))), 36]
, Dv = e=>e.substring(...Tv().reverse()[mi(372)](1))
, bv = e=>{
const s = mi
, r = {
acjba: function(i, o) {
return i + o
},
GAVPm: function(i, o) {
return i << o
}
};
let n = null;
try {
let i = e;
i = r[s(378)](Dv(i), i[s(383)](r[s(384)](3, 2) - 2)),
// 3. r是 {acjba: function(i, o), GAVPm: function(i, o)}, r[s(378)]是 "acjba", i[s(383)]是substring(), r[s(384)]是(i,o){return i<<o},r[s(384)](3, 2) - 2是个固定值=10;
// 4. Dv(i)这个函数可以看到前面进行了一系列处理,带入一下就是i.substring(...Tv().reverse()[mi(372)](1)); 继续简化;
// 5. 其中的Tv()是个固定值, 结果为[5, 0, 36]。mi(372)是"slice";
// 6. 综合一下就是[5, 0, 36].reverse()["slice"](1),输出就是一个列表[0, 5],则Dv(i)=i.substring(0, 5)
// 7. 则i= r["acjba"](i.substring(0, 5), i.substring(10))。由于r["acjba"]等于拼接两个传入字符串,进一步简化i= i.substring(0, 5) + i.substring(10)
i = window[km](i), // 2. window[km]在控制台打一下发现是atob(),不用太纠结看前面的代码;
n = JSON[s(379)](decodeURIComponent(i)) // 1. s(379)是parse,先使用decodeURIComponent()对i进行解码,然后json解析;
} catch {}
return n
至此,已经逆向完成,我们简化一下。
- 取得接口返回的Json里面的items字符串e
- i = e
- i = i.substring(0, 5) + i.substring(10)
- i = atob(i)
- n = Json.parse(decodeURIComponent(i))
最终的n就是我们要获取的已经解密的json内容,下面用Python代码转写一下。不会的可以扔进ChatGpt,下面直接放Python代码:
import urllib.parse
import base64
def decrypt(input_string):
decrypt_text = None
try:
i = input_string[0:5] + input_string[10:]
i = base64.b64decode(i)
i = urllib.parse.unquote(i)
decrypt_text = loads(i)
except:
print('[-] ERROR When Parse')
pass
# print(decrypt_text)
return decrypt_text
逆向/Python重写JS解密逻辑
实际写爬虫进行爬取的时候发现网站有反爬机制,直接使用Python Request请求是搞不定的。具体机制请参考下面的链接,这里不再赘述。
反爬虫SSL TLS指纹识别和绕过JA3算法;
秉承着能懒就懒的原则,我给出两种方案:
--- 使用Selenium来请求,然后正则把加密内容给搞下来,代码如下(仅展现处理一页):
import undetected_chromedriver as uc
from time import sleep
from json import loads
from re import findall
import urllib.parse
import base64
def decrypt(input_string):
decrypt_text = None
try:
i = input_string[0:5] + input_string[10:]
i = base64.b64decode(i)
i = urllib.parse.unquote(i)
decrypt_text = loads(i)
except:
print('[-] ERROR When Parse')
pass
# print(decrypt_text)
return decrypt_text
def main():
options = uc.ChromeOptions()
# options.add_argument( '--headless' )
driver = uc.Chrome(options=options)
driver.get(url='https://tarkov-market.com/api/be/items?lang=en&search=&tag=&sort=change24&sort_direction=desc&trader=&skip=40&limit=20')
temp_text = driver.find_element('tag name', 'body').text
if len(temp_text)>= 300 and len(findall("\"result\":\"ok\"", temp_text)) >= 1:
with open('TarkovItemPrice.json', 'w', encoding='utf-8') as f:
f.write(decrypt(loads(temp_text)['items']))
else:
print("[-] Request failed...")
print("[+] Exit...")
_wait = input()
if __name__ == "__main__":
main()
--- 使用curl_cffi里魔改过TLS指纹的Request来请求,代码如下(仅展现处理一页):
import urllib.parse
import base64
from json import loads, dumps
from curl_cffi import requests
def decrypt(input_string):
decrypt_text = None
try:
i = input_string[0:5] + input_string[10:]
i = base64.b64decode(i)
i = urllib.parse.unquote(i)
decrypt_text = loads(i)
except:
print('[-] ERROR When Parse')
pass
# print(decrypt_text)
return decrypt_text
def main():
url = "https://tarkov-market.com/api/be/items?lang=en&search=&tag=&sort=change24&sort_direction=desc&trader=&skip=20&limit=20"
headers = {
'authority': 'tarkov-market.com',
'accept': '*/*',
'accept-language': 'en,zh-CN;q=0.9,zh;q=0.8',
'cache-control': 'no-cache',
'dnt': '1',
'pragma': 'no-cache',
'referer': 'https://tarkov-market.com/',
'sec-ch-ua': '"Google Chrome";v="110", "Chromium";v="110", "Not-A.Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-origin',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36'
}
temp_text = requests.get(url=url, headers=headers, impersonate="chrome110").text
temp_json = loads(temp_text)
with open('TarkovItemPrice.json', 'w', encoding='utf-8') as f:
f.write(dumps(decrypt(temp_json['items'])))
print("[+] Exit...")
_wait = input()
if __name__ == "__main__":
main()
总结
综合来说这个网站难度较低,逆向出了解密代码就可以写个遍历获取所有的数据了,可以设置个计划任务每天跑一遍就可以拿到最新的商品价格以供使用了。