目标网站:aHR0cDovL3ljLndzd2oubmV0L2Foc3h4L0xPTC9wdWJsaWMvcHVibGljLmh0bWw=
1.抓包分析
通过控制台查看,请求会发送两次包,相应的内容是加密后的,经过测试第一个请求跟第二个请求是独立的,可以只单独发送第二个请求。
这里分析第一个请求,第二个请求也是同理
要找到相应解密的逻辑,可以使用搜索大法
但是搜索data的数据量有点多
可以参考关联法“如果检索参数名得到的结果过多,可以检索 request/response 的其他参数,参数名约特殊越好”
搜索respCode,两个位置出现,都打上断点
再次请求后进行调试,可以看到waterSecurity.decode就是解密函数了
这是利用搜索定位到解密位置,那要是代码搜不到或者被混淆了
还有没有别的方法
这里换另一种比较科学的
找它请求的堆栈
经过百度知道$.ajax是一个异步加载,请求成功后有回调函数
跟进success函数里
继续跟进i函数
这样也是定位到解密函数的位置
2.分析请求参数来源
回到刚才的waterSecurity.decode 解密函数位置
整段复制出来,进行调用运行看看
成功调用
第二个请求解密
既然可以成功调用了,接下来就分析请求参数的来源
那就找到还没请求之前的位置,然后通过它的一个堆栈
打上断点,加密完后多了个waterEncode字段,点击加密函数查看
加密结束后跟进tools.httpManager.http方法
对加密后增加了random字段,随机值
第一次请求的所有字段都找到了
按这种方法也能找到第二次请求需要的参数,就不演示了
3.算法还原
简单点的就直接黑盒调用就可以了。
看它的加密逻辑并不多,也不复杂,一步步用python还原
import base64
import json
import requests
class waterSecurity:
version = "2.1"
def gblen(self, s):
r = 0
for i in range(0, len(s)):
if ord(s[i]) > 127 or ord(s[i]) == 94:
r += 2
else:
r += 1
return r
def getTagsPosition(self, r, t):
e = []
for i in range(0, len(t)):
e.append(r.find(t[i]))
e.sort()
return e
def parityTransposition(self, r):
t = []
for e in range(0, len(r), 2):
t.append(r[e+1])
t.append(r[e])
return "".join(t)
def s_encode(self, r):
if isinstance(r, int):
return ""
if r+"" == "":
return ""
r = r.replace("%", "&25").replace("+", "%2B")
if self.gblen(r) % 2 != 0:
r = r + "*"
r = self.parityTransposition(r)
t = self.version+base64.b64encode(r.encode()).decode()
return t
def s_decode(self, r):
if len(r) < 5 and ("" == r or None == r):
return "[]"
r = r[3:]
h = {}
s = []
t = r[len(r) - 4:]
e = r[r.find(t):]
e = e[4:len(e) - 4]
for i in range(0, len(e)):
if 4 * i < len(e):
tt = e[4*i:4*i+4]
s.append(tt)
h[tt] = None
n = self.getTagsPosition(r, s)
i = 0
for c in range(0, len(n)):
a = r[i: n[c]]
h[r[n[c]:n[c]+4]] = a
i = n[c] + 4
o = []
for c in range(0, len(s)):
o.append(h[s[c]])
o = "".join(o)
o = base64.b64decode(o.encode()).decode()
return o
def paramEncode(self, e):
for t in e.keys():
e[t] = self.s_encode(e[t])
e["waterEncode"] = self.s_encode("true")
# print(e)
return e
if __name__ == '__main__':
waterSecurity = waterSecurity()
headers = {
'Connection': 'keep-alive',
'Pragma': 'no-cache',
'Cache-Control': 'no-cache',
'Accept': 'application/json, text/javascript, */*; q=0.01',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Origin': 'http://yc.wswj.net',
'Referer': 'http://yc.wswj.net/',
'Accept-Language': 'zh-CN,zh;q=0.9',
}
data = {
"time": "",
"hourClass": "1,2,3,6,12",
"name": "SelectRainWarnInfo",
"snsw": "sn"
}
data = waterSecurity.paramEncode(data)
print("第一次加密:", data)
data["random"] = "0.41182418674135346"
response = requests.post('http://61.191.22.196:5566/AHSXX/service/PublicBusinessHandler.ashx', headers=headers, data=data, verify=False)
data = waterSecurity.s_decode(response.json()["data"])
data = json.loads(data)
print("第一次请求返回的data解密:", data)
m = {
"name": "SelectRainMapData",
"btime": "202111022200",
"etime": "202111030000",
"rainlevel": "A:10,25,50,100",
"isoline": "N",
"heatRange": 50,
"stcdtype": "1,0,0,0,0,0",
"fresh": 0,
"points": ""
}
m = waterSecurity.paramEncode(m)
print("第二次加密:", m)
response = requests.post('http://61.191.22.196:5566/AHSXX/service/PublicBusinessHandler.ashx', headers=headers, data=m, verify=False)
m = waterSecurity.s_decode(response.json()["data"])
m = json.loads(m)
print("第二次请求返回的data解密:", m)