某水利网请求响应加密算法还原

目标网站: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)
复制代码