前程无忧接口分析

前程无忧接口分析

  • 所需用到的工具
  • URL解析
  • 通过抓包软件或者开发者选项抓取数据包
  • 对代码中的参数解析分析
  • 对acw_sc__v2进行分析
  • 对acw_sc__v2进行转换代码生成
    • 生成outPutList数组
    • 生成arg2参数
    • 生成arg3参数
    • 最终的效果
  • 对详情页面的分析
    • 对timestamp__1258的生成分析

所需用到的工具

  • Charles抓包工具
  • https://curlconverter.com/python/

URL解析

先访问网页https://www.51job.com/
前程无忧接口分析_第1张图片输入关键字点击搜索
前程无忧接口分析_第2张图片通过查看这个网址
https://we.51job.com/pc/search?jobArea=260200&keyword=%E5%85%BC%E8%81%8C&searchType=2&sortType=0&metro=

有以下参数

参数名 说明
jobArea 260200 城市代码
keyword 兼职 关键字(UrlEncode 编码)
sortType 0 0代表综合排序
1代表最新优先
3代表薪资优先
5代表活跃职位优先

通过抓包软件或者开发者选项抓取数据包

前程无忧接口分析_第3张图片复制cURL Request通过https://curlconverter.com/go/转为Python代码
前程无忧接口分析_第4张图片

运行代码查看是否能获取成功

前程无忧接口分析_第5张图片

对代码中的参数解析分析

通过对代码中的参数分析,最终发现修改acw_sc__v2的值会导致访问失败

前程无忧接口分析_第6张图片前程无忧接口分析_第7张图片

所以基本上确定acw_sc__v2为我们需要破解的

对acw_sc__v2进行分析

现在我们需要去网站对acw_sc__v2逆向分析
先检查一下acw_sc__v2,是本地生成的还是服务器生成
通过如下图可知它是由本地js文件生成的
前程无忧接口分析_第8张图片现在我们需要把这个值删除,并编写一个hook使其能够快速的定位

cookie_cache = document.cookie;
Object.defineProperty(document, "cookie", {
        get: function () {
            console.log(cookie_cache);
        // 在获取document.cookie时,执行你想要的操作
        return cookie_cache; // 返回原始的cookie值
    },
    set: function(value) {
        // 在设置document.cookie时,执行你想要的操作
        if(value.includes('acw_sc__v2')){
           debugger;
        }

    }
});

前程无忧接口分析_第9张图片最终定位如下图
前程无忧接口分析_第10张图片开始对栈分析,定位到上一栈
前程无忧接口分析_第11张图片通过查看我们可以知道arg3就是acw_sc__v2的值
前程无忧接口分析_第12张图片
在这里打上一个断点,并重新加载页面

前程无忧接口分析_第13张图片
搜寻arg3
前程无忧接口分析_第14张图片
通过阅读这份代码可知arg3是通过一个循环来生成的,但是循环的条件与arg2有关,arg2又与列表outPutList有关,outPutListarg1有关

所以基本的思路为通过arg1生成outPutListoutPutList生成arg2arg2生成arg3

搜寻arg1
前程无忧接口分析_第15张图片在这里打上断点,重新加载网页
前程无忧接口分析_第16张图片前程无忧接口分析_第17张图片通过如上两张图片可知arg1来自于访问一个URL所返回的body

通过抓包软件抓取并转为Python代码
前程无忧接口分析_第18张图片前程无忧接口分析_第19张图片我们可以通过正则表达式取出这个值

arg1=re.findall("arg1=('[^']*')",response.text)
arg1=arg1[0].replace("'","")

对acw_sc__v2进行转换代码生成

生成outPutList数组

通过arg1生成outPutList

for (var i = 0; i < arg1[_0x1e8e("0x1")]; i++) {
    var this_i = arg1[i];
    for (var j = 0; j < posList[_0x1e8e("0x1")]; j++) {
        if (posList[j] == i + 1) {
            outPutList[j] = this_i
        }
    }
}

分析这个js代码可知这是通过循环来生成的
arg1[_0x1e8e(“0x1”)]是arg1的长度
posList[_0x1e8e(“0x1”)]是posList数组的长度
posList数组是固定值
转为Python代码如下

arg1='6AA3E7F5214AEB580A31B0B254C4795589509422'
posList = [15, 35, 29, 24, 33, 16, 1, 38, 10, 9, 19, 31, 40, 27, 22, 23, 25, 13, 6, 11, 39, 18, 20, 8, 14, 21, 32, 26, 2, 30, 7, 4, 17, 5, 3, 28, 34, 37, 12, 36]
outPutList = []
for i in range(len(arg1)):
    outPutList.append(0)

for i in range(len(arg1)):
    this_i = arg1[i]
    for j in range(len(posList)):
        if posList[j] == i + 1:
            outPutList[j] = this_i

print(outPutList)

生成arg2参数

经过对代码的分析可知arg2是由outPutList数组转为字符串实现的

arg2 = outPutList[_0x1e8e("0x2")]("");
arg2  = ''.join(outPutList)

生成arg3参数

for (var i = 0; i < arg2[_0x1e8e("0x1")] && i < mask[_0x1e8e("0x1")]; i += 2) {
    var GxjQsM = _0x1e8e("0x3")[_0x1e8e("0x4")]("|")
        , QoWazb = 0;
    while (!![]) {
        switch (GxjQsM[QoWazb++]) {
            case "0":
                if (xorChar[_0x1e8e("0x1")] == 1) {
                    xorChar = "0" + xorChar
                }
                continue;
            case "1":
                var strChar = parseInt(arg2[_0x1e8e("0x5")](i, i + 2), 16);
                continue;
            case "2":
                arg3 += xorChar;
                continue;
            case "3":
                var xorChar = (strChar ^ maskChar)[_0x1e8e("0x6")](16);
                continue;
            case "4":
                var maskChar = parseInt(mask[_0x1e8e("0x5")](i, i + 2), 16);
                continue
        }
        break
    }
}

arg2[_0x1e8e(“0x1”)]是arg2的长度
mask[_0x1e8e(“0x1”)]是mask的长度
mask是固定值3000176000856006061501533003690027800375
GxjQsM是固定的列表[‘1’, ‘4’, ‘3’, ‘0’, ‘2’]

转为Python代码

arg3=''
for i in range(0, 40, 2):
    strChar = int(arg2[i:i + 2], 16)
    maskChar = int(mask[i:i + 2], 16)
    xorChar = hex(strChar ^ maskChar)[2:]
    if len(xorChar) == 1:
        xorChar = '0' + xorChar
    arg3 += xorChar

最终的效果

前程无忧接口分析_第20张图片

对详情页面的分析

随便点击一个页面进入详情页面,抓包抓取数据
前程无忧接口分析_第21张图片复制CURL命令转为Python代码,运行代码
前程无忧接口分析_第22张图片可以获取数据
开始分析,最终发现只有修改reqtimestamp__1258才会获取失败,并且这两个是一对的,任何一个被修改都会导致获取失败

前程无忧接口分析_第23张图片

对timestamp__1258的生成分析

通过抓包软件发现在跳转页面之前会访问一个url获取js代码

前程无忧接口分析_第24张图片把整个js代码复制下来到浏览器去调试
前程无忧接口分析_第25张图片

前程无忧接口分析_第26张图片搜寻_0x3baf44
前程无忧接口分析_第27张图片搜寻_0x30f62c
前程无忧接口分析_第28张图片最中发现这个_0x56d97c是一个函数,提交的参数为下面这种结构

-1837977873|0|1702035443735
number|0|number
0之后的数字的为时间戳,第一个数字暂不知道,所以去搜寻

前程无忧接口分析_第29张图片大致阅读可知_0x318558通过for循环来生成的,其中与_0x1117c9有关
搜寻_0x1117c9

前程无忧接口分析_第30张图片打印_0x1117c9
前程无忧接口分析_第31张图片这里你会很熟悉,这些数据都是搜索接口里所抓取的数据
前程无忧接口分析_第32张图片也就是说timestamp__1258的生成思路为
构造如下的列表

{
    "protocol": "https:",
    "host": "jobs.51job.com",
    "hostname": "jobs.51job.com",
    "port": "",
    "pathname": "/guiyang-gshq/152094046.html",
    "search": "?s=sou_sou_soulb&t=0_0&req=04608edfce87b123a2d4951514d63dc4",
    "hash": "",
    "original": "https://jobs.51job.com/guiyang-gshq/152094046.html?s=sou_sou_soulb&t=0_0&req=04608edfce87b123a2d4951514d63dc4"
}

通过for循环生成_0x318558,在通过_0x318558构造如下结构的数据

_0x318558|0|时间戳

提交到_0x56d97c函数生成timestamp__1258的值

_0x56d97c函数太过庞大并且这个js代码是混淆后的,最终选择补环境的方式来实现这个生成,让后通过Python调用js代码
补环境之后的效果
前程无忧接口分析_第33张图片前程无忧接口分析_第34张图片

你可能感兴趣的:(爬虫,python,httpx,pip,爬虫,网络爬虫,数据挖掘,爬山算法)