web逆向实战7(中国观鸟记录中心)

分析网站

分析过程

1.打开网站,我们只看xhr,点击翻页,只有一个数据包,载荷以及响应数据都是加密的,并且请求的header头也有两个加密的参数RequestidSign.

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2.我们先解决请求头的参数,我们通过搜索大法,搜索requestid,看一下只有第一个是在生成,其他地方的都是在使用,直接点进第一个,下断点翻页成功断住,requestId = getUuid(),我们抠出来getUuid函数即可。

在这里插入图片描述
在这里插入图片描述

function getUuid() {
        var s = [];
        var hexDigits = "0123456789abcdef";
        for (var i = 0; i < 32; i++) {
            s[i] = hexDigits.substr(Math.floor(Math.random() * 16), 1)
        }
        s[14] = "4";
        s[19] = hexDigits.substr(s[19] & 3 | 8, 1);
        s[8] = s[13] = s[18] = s[23];
        var uuid = s.join("");
        return uuid
}

3.我们接着看,sign = MD5(data + requestId + timestamp),经过测试是标准的MD5算法,各个字段内容如下第二个图。

在这里插入图片描述
在这里插入图片描述

4.我们看看options.data,就是我们请求时,载荷的加密的字符串,通过encrypt.encryptLong对=='{“limit”:“20”,“page”:“3”}'明文进行加密,我们扣一下encrypt.encryptLong==代码。

在这里插入图片描述

JSEncrypt.prototype.encryptLong = function(str) {
    try {
        var encrypted = this.getKey().encryptLong(str) || "";
        var uncrypted = this.getKey().decryptLong(encrypted) || "";
        var count = 0;
        var reg = /null$/g;
        while (reg.test(uncrypted)) {
            count++;
            encrypted = this.getKey().encryptLong(str) || "";
            uncrypted = this.getKey().decryptLong(encrypted) || "";
            if (count > 10) {
                break
            }
        }
        return encrypted
    } catch (ex) {
        return false
    }
}
  //这段代码分析,看到中间  this.getKey().decryptLong 这个函数还要进入内层进行扣取
  RSAKey.prototype.encryptLong = function(text) {
    var _this = this;
    var maxLength = (this.n.bitLength() + 7 >> 3) - 11;
    try {
        var ct_1 = "";
        if (text.length > maxLength) {
            var lt = text.match(/.{1,117}/g);
            lt.forEach(function(entry) {
                var t1 = _this.encrypt(entry);
                ct_1 += t1
            });
            return hex2b64(ct_1)
        }
        var t = this.encrypt(text);
        var y = hex2b64(t);
        return y
    } catch (ex) {
        return false
    }
}
//进入后可以看到这个是RSA的非对称加密中间还有个hex2b64这个算法,为了保险起见也将这个算法一起copy
function hex2b64(h) {
    var i;
    var c;
    var ret = "";
    for (i = 0; i + 3 <= h.length; i += 3) {
        c = parseInt(h.substring(i, i + 3), 16);
        ret += b64map.charAt(c >> 6) + b64map.charAt(c & 63)
    }
    if (i + 1 == h.length) {
        c = parseInt(h.substring(i, i + 1), 16);
        ret += b64map.charAt(c << 2)
    } else if (i + 2 == h.length) {
        c = parseInt(h.substring(i, i + 2), 16);
        ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4)
    }
    while ((ret.length & 3) > 0) {
        ret += b64pad
    }
    return ret
}
//这些参数也要带上不然报错
var b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var b64pad = "=";


5.看上面使用了var encrypt = new JSEncrypt;这个算法库,到时候直接导入算法库。

在这里插入图片描述

6.这里我们用const JSEncrypt = require(“jsencrypt”);这个库,安装命令npm install jsencrypt,整合一下代码如下:

const JSEncrypt = require("jsencrypt");
const CryptoJS = require("crypto-js");
function MD5(data) {
    return CryptoJS.MD5(data).toString();
  }
function getUuid() {
    var s = [];
    var a = "0123456789abcdef";
    for (var i = 0; i < 32; i++) {
        s[i] = a.substr(Math.floor(Math.random() * 0x10), 1)
    }
    s[14] = "4";
    s[19] = a.substr((s[19] & 0x3) | 0x8, 1);
    s[8] = s[13] = s[18] = s[23];
    var b = s.join("");
    return b
}
var b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var b64pad = "=";
function hex2b64(h) {
    var i;
    var c;
    var ret = "";
    for (i = 0; i + 3 <= h.length; i += 3) {
        c = parseInt(h.substring(i, i + 3), 16);
        ret += b64map.charAt(c >> 6) + b64map.charAt(c & 63);
    }
    if (i + 1 == h.length) {
        c = parseInt(h.substring(i, i + 1), 16);
        ret += b64map.charAt(c << 2);
    }
    else if (i + 2 == h.length) {
        c = parseInt(h.substring(i, i + 2), 16);
        ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4);
    }
    while ((ret.length & 3) > 0) {
        ret += b64pad;
    }
    return ret;
}
JSEncrypt.prototype.encryptLong = function(str) {
    try {
        var encrypted = this.getKey().encryptLong(str) || "";
        var uncrypted = this.getKey().decryptLong(encrypted) || "";
        var count = 0;
        var reg = /null$/g;
        while (reg.test(uncrypted)) {
            count++;
            encrypted = this.getKey().encryptLong(str) || "";
            uncrypted = this.getKey().decryptLong(encrypted) || "";
            if (count > 10) {
                break
            }
        }
        return encrypted
    } catch (ex) {
        return false
    }
}
function sort_ASCII(obj) {
        var arr = new Array;
        var num = 0;
        for (var i in obj) {
            arr[num] = i;
            num++
        }
        var sortArr = arr.sort();
        var sortObj = {};
        for (var i in sortArr) {
            sortObj[sortArr[i]] = obj[sortArr[i]]
        }
        return sortObj
    }
function get_params(page) {
    var paramPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvxXa98E1uWXnBzXkS2yHUfnBM6n3PCwLdfIox03T91joBvjtoDqiQ5x3tTOfpHs3LtiqMMEafls6b0YWtgB1dse1W5m+FpeusVkCOkQxB4SZDH6tuerIknnmB/Hsq5wgEkIvO5Pff9biig6AyoAkdWpSek/1/B7zYIepYY0lxKQIDAQAB";
    var encrypt = new JSEncrypt();
    encrypt.setPublicKey(paramPublicKey);
    var c = Date.parse(new Date());
    var d = getUuid();
    var e = JSON.stringify(sort_ASCII({"limit": "20", "page":page}));
    var data = encrypt.encryptLong(e);
    var f = MD5(e + d + c);
    return {
        "timestamp": c,
        "requestId": d,
        "sign": f,
        "data": data,
    }
}
console.log(get_params(1))

在这里插入图片描述

7.结果发现data是false,我们在扣的代码,加一句看一下是什么错误,发现this.getKey().decryptLong不是一个函数

在这里插入图片描述
在这里插入图片描述

8.我们打上断点并去网页也打上断点做一下比较,网页是一个函数,我们这是undefined,我们点进去看一下,

在这里插入图片描述
在这里插入图片描述

8.它是在RSAKey的原型链上添加函数,我们直接这样添加,发现本地和网页的不一样,修改一下就可以了,修改玩的代码如下:。

var encrypt = new JSEncrypt();
encrypt.setPublicKey(paramPublicKey);
encrypt.encryptLong = function(text) {
    var _this = this.getKey();//改动了
    var maxLength = (_this.n.bitLength() + 7 >> 3) - 11;
    try {
        var ct_1 = "";
        if (text.length > maxLength) {
            var lt = text.match(/.{1,117}/g);
            lt.forEach(function(entry) {
                var t1 = _this.encrypt(entry);
                ct_1 += t1
            });
            return hex2b64(ct_1)
        }
        var t = _this.encrypt(text);
        var y = hex2b64(t);
        return y
    } catch (ex) {
        console.log(ex)
        return false
    }
}
encrypt.decryptLong = function(text) {
    var _this = this.getKey();//改动了
    var maxLength = _this.n.bitLength() + 7 >> 3;
    text = b64tohex(text);
    try {
        if (text.length > maxLength) {
            var ct_2 = "";
            var lt = text.match(/.{1,256}/g);
            lt.forEach(function(entry) {
                var t1 = _this.decrypt(entry);
                ct_2 += t1
            });
            return ct_2
        }
        var y = _this.decrypt(text);
        return y
    } catch (ex) {
        return false
    }
}

在这里插入图片描述
在这里插入图片描述

9.到此,header以及载荷的加密已经完成了,完整代码如下:

const JSEncrypt = require("jsencrypt");
const CryptoJS = require("crypto-js");
function MD5(data) {
    return CryptoJS.MD5(data).toString();
  }
function getUuid() {
    var s = [];
    var a = "0123456789abcdef";
    for (var i = 0; i < 32; i++) {
        s[i] = a.substr(Math.floor(Math.random() * 0x10), 1)
    }
    s[14] = "4";
    s[19] = a.substr((s[19] & 0x3) | 0x8, 1);
    s[8] = s[13] = s[18] = s[23];
    var b = s.join("");
    return b
}
var b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var b64pad = "=";
function hex2b64(h) {
    var i;
    var c;
    var ret = "";
    for (i = 0; i + 3 <= h.length; i += 3) {
        c = parseInt(h.substring(i, i + 3), 16);
        ret += b64map.charAt(c >> 6) + b64map.charAt(c & 63);
    }
    if (i + 1 == h.length) {
        c = parseInt(h.substring(i, i + 1), 16);
        ret += b64map.charAt(c << 2);
    }
    else if (i + 2 == h.length) {
        c = parseInt(h.substring(i, i + 2), 16);
        ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4);
    }
    while ((ret.length & 3) > 0) {
        ret += b64pad;
    }
    return ret;
}
JSEncrypt.prototype.encryptLong = function(str) {
    try {
        var encrypted = this.getKey().encryptLong(str) || "";
        var uncrypted = this.getKey().decryptLong(encrypted) || "";
        var count = 0;
        var reg = /null$/g;
        while (reg.test(uncrypted)) {
            count++;
            encrypted = this.getKey().encryptLong(str) || "";
            uncrypted = this.getKey().decryptLong(encrypted) || "";
            if (count > 10) {
                break
            }
        }
        return encrypted
    } catch (ex) {
        console.log(ex)
        return false
    }
}
function sort_ASCII(obj) {
        var arr = new Array;
        var num = 0;
        for (var i in obj) {
            arr[num] = i;
            num++
        }
        var sortArr = arr.sort();
        var sortObj = {};
        for (var i in sortArr) {
            sortObj[sortArr[i]] = obj[sortArr[i]]
        }
        return sortObj
    }
function get_params(page) {
    var paramPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvxXa98E1uWXnBzXkS2yHUfnBM6n3PCwLdfIox03T91joBvjtoDqiQ5x3tTOfpHs3LtiqMMEafls6b0YWtgB1dse1W5m+FpeusVkCOkQxB4SZDH6tuerIknnmB/Hsq5wgEkIvO5Pff9biig6AyoAkdWpSek/1/B7zYIepYY0lxKQIDAQAB";
    var encrypt = new JSEncrypt();
    encrypt.setPublicKey(paramPublicKey);
    encrypt.encryptLong = function(text) {
            var _this = this.getKey();
            var maxLength = (_this.n.bitLength() + 7 >> 3) - 11;
            try {
                var ct_1 = "";
                if (text.length > maxLength) {
                    var lt = text.match(/.{1,117}/g);
                    lt.forEach(function(entry) {
                        var t1 = _this.encrypt(entry);
                        ct_1 += t1
                    });
                    return hex2b64(ct_1)
                }
                var t = _this.encrypt(text);
                var y = hex2b64(t);
                return y
            } catch (ex) {
                console.log(ex)
                return false
            }
        }
    encrypt.decryptLong = function(text) {
            var _this = this.getKey();
            var maxLength = _this.n.bitLength() + 7 >> 3;
            text = b64tohex(text);
            try {
                if (text.length > maxLength) {
                    var ct_2 = "";
                    var lt = text.match(/.{1,256}/g);
                    lt.forEach(function(entry) {
                        var t1 = _this.decrypt(entry);
                        ct_2 += t1
                    });
                    return ct_2
                }
                var y = _this.decrypt(text);
                return y
            } catch (ex) {
                return false
            }
        }
    var c = Date.parse(new Date());
    var d = getUuid();
    var e = JSON.stringify(sort_ASCII({"limit": "20", "page":page}));
    var data = encrypt.encryptLong(e);
    var f = MD5(e + d + c);
    return {
        "timestamp": c,
        "requestId": d,
        "sign": f,
        "data": data,
    }
}
console.log(get_params(1))

10.接下来,我们对响应数据进行解密,我们通过启动器进去,前两个直接跳过,在疑似位置打上断点,点击下一页,成功断住,发现了解密函数,我们继续;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

11.进去之后发现关键词decode,下断点,进去;

在这里插入图片描述
在这里插入图片描述

12.进去之后发现是混淆,我们还原一下,就是AES;接下来就可以写代码了。

_0x48989b[_0x1b50('0x20')]['decode'] = function(_0x291626) {
        var _0x3c6fa1 = CryptoJS['enc'][_0x1b50('0xc')][_0x1b50('0x5')](this[_0x1b50('0x27')](this[_0x1b50('0x28')]))
          , _0x3ec027 = CryptoJS[_0x1b50('0xe')][_0x1b50('0xc')][_0x1b50('0x5')](this[_0x1b50('0x27')](this['iv']));
        return CryptoJS[_0x1b50('0x13')][_0x1b50('0x1b')](_0x291626, _0x3c6fa1, {
            'iv': _0x3ec027,
            'mode': CryptoJS[_0x1b50('0x3d')]['CBC'],
            'padding': CryptoJS['pad'][_0x1b50('0x24')]
        })['toString'](CryptoJS['enc']['Utf8']);
    }

最终代码

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
'''
@Project :pythonProject 
@File    :test.py
@IDE     :PyCharm 
@Author  :haozaispider(微信同号)
@Date    :2024/12/10 下午3:59 
'''

import execjs
import requests
from loguru import logger

headers = {
    "Accept": "application/json, text/javascript, */*; q=0.01",
    "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
    "Cache-Control": "no-cache",
    "Connection": "keep-alive",
    "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
    "Origin": "https://www.birdreport.cn",
    "Pragma": "no-cache",
    "Referer": "https://www.birdreport.cn/",
    "Sec-Fetch-Dest": "empty",
    "Sec-Fetch-Mode": "cors",
    "Sec-Fetch-Site": "same-site",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0",
    # "requestId": "9aec1349d68bad4316d975cdf4a41eb8",
    "sec-ch-ua": "\"Microsoft Edge\";v=\"129\", \"Not=A?Brand\";v=\"8\", \"Chromium\";v=\"129\"",
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": "\"Windows\"",
    # "sign": "e8b0161b00e9acafdfd1b26b7cc4da6d",
    # "timestamp": "1727145614000"
}
url = "https://api.birdreport.cn/front/activity/search"
params_obj = execjs.compile(open("./test.js", 'r', encoding="utf-8").read()).call('get_params',3)
logger.info('params_obj ===》》》 '+ str(params_obj))

headers["sign"] = params_obj["sign"]
headers["timestamp"] = str(params_obj["timestamp"])
headers["requestId"] = params_obj["requestId"]
data = params_obj["data"]

response = requests.post(url, headers=headers,
                         data=data
                         )
logger.info('密文数据===》》》 ' + response.text)

decrypt_data = execjs.compile(open("./test.js", 'r', encoding="utf-8").read()).call('decrypt_data',response.json()["data"])
logger.info('明文数据===》》》 ' + decrypt_data)
const JSEncrypt = require("jsencrypt");
const CryptoJS = require("crypto-js");
function MD5(data) {
    return CryptoJS.MD5(data).toString();
  }
function getUuid() {
    var s = [];
    var a = "0123456789abcdef";
    for (var i = 0; i < 32; i++) {
        s[i] = a.substr(Math.floor(Math.random() * 0x10), 1)
    }
    s[14] = "4";
    s[19] = a.substr((s[19] & 0x3) | 0x8, 1);
    s[8] = s[13] = s[18] = s[23];
    var b = s.join("");
    return b
}
var b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var b64pad = "=";
function hex2b64(h) {
    var i;
    var c;
    var ret = "";
    for (i = 0; i + 3 <= h.length; i += 3) {
        c = parseInt(h.substring(i, i + 3), 16);
        ret += b64map.charAt(c >> 6) + b64map.charAt(c & 63);
    }
    if (i + 1 == h.length) {
        c = parseInt(h.substring(i, i + 1), 16);
        ret += b64map.charAt(c << 2);
    }
    else if (i + 2 == h.length) {
        c = parseInt(h.substring(i, i + 2), 16);
        ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4);
    }
    while ((ret.length & 3) > 0) {
        ret += b64pad;
    }
    return ret;
}
JSEncrypt.prototype.encryptLong = function(str) {
    try {
        var encrypted = this.getKey().encryptLong(str) || "";
        var uncrypted = this.getKey().decryptLong(encrypted) || "";
        var count = 0;
        var reg = /null$/g;
        while (reg.test(uncrypted)) {
            count++;
            encrypted = this.getKey().encryptLong(str) || "";
            uncrypted = this.getKey().decryptLong(encrypted) || "";
            if (count > 10) {
                break
            }
        }
        return encrypted
    } catch (ex) {
        console.log(ex)
        return false
    }
}
function sort_ASCII(obj) {
        var arr = new Array;
        var num = 0;
        for (var i in obj) {
            arr[num] = i;
            num++
        }
        var sortArr = arr.sort();
        var sortObj = {};
        for (var i in sortArr) {
            sortObj[sortArr[i]] = obj[sortArr[i]]
        }
        return sortObj
    }
function get_params(page) {
    var paramPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvxXa98E1uWXnBzXkS2yHUfnBM6n3PCwLdfIox03T91joBvjtoDqiQ5x3tTOfpHs3LtiqMMEafls6b0YWtgB1dse1W5m+FpeusVkCOkQxB4SZDH6tuerIknnmB/Hsq5wgEkIvO5Pff9biig6AyoAkdWpSek/1/B7zYIepYY0lxKQIDAQAB";
    var encrypt = new JSEncrypt();
    encrypt.setPublicKey(paramPublicKey);
    encrypt.encryptLong = function(text) {
            var _this = this.getKey();
            var maxLength = (_this.n.bitLength() + 7 >> 3) - 11;
            try {
                var ct_1 = "";
                if (text.length > maxLength) {
                    var lt = text.match(/.{1,117}/g);
                    lt.forEach(function(entry) {
                        var t1 = _this.encrypt(entry);
                        ct_1 += t1
                    });
                    return hex2b64(ct_1)
                }
                var t = _this.encrypt(text);
                var y = hex2b64(t);
                return y
            } catch (ex) {
                console.log(ex)
                return false
            }
        }
    encrypt.decryptLong = function(text) {
            var _this = this.getKey();
            var maxLength = _this.n.bitLength() + 7 >> 3;
            text = b64tohex(text);
            try {
                if (text.length > maxLength) {
                    var ct_2 = "";
                    var lt = text.match(/.{1,256}/g);
                    lt.forEach(function(entry) {
                        var t1 = _this.decrypt(entry);
                        ct_2 += t1
                    });
                    return ct_2
                }
                var y = _this.decrypt(text);
                return y
            } catch (ex) {
                return false
            }
        }
    var c = Date.parse(new Date());
    var d = getUuid();
    var e = JSON.stringify(sort_ASCII({"limit": "20", "page":page}));
    var data = encrypt.encryptLong(e);
    var f = MD5(e + d + c);
    return {
        "timestamp": c,
        "requestId": d,
        "sign": f,
        "data": data,
    }
}

function decrypt_data(data) {
    const key = "C8EB5514AF5ADDB94B2207B08C66601C"
    const iv = "55DD79C6F04E1A67"
    var b = CryptoJS.enc.Utf8.parse(key);
    var c = CryptoJS.enc.Utf8.parse(iv);
    var d = CryptoJS.AES.decrypt(data, b, {
        iv: c,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    });
    return d.toString(CryptoJS.enc.Utf8)
}
// console.log(get_params(1))

结果展示

在这里插入图片描述

完结撒花!!!!!有不懂的欢迎私信看到都会回复,大佬可以直接跳过。欢迎+v一起交流学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蛋蛋のCrawler日记

感谢老板的支持!!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值