当前位置:   article > 正文

XXXX直聘聊天机器人_直聘聊天协议逆向

直聘聊天协议逆向

XXXX直聘聊天机器人

想转岗到爬虫工程师,由于面试机会太少,而boss直聘又无法海投,决定做一个boss直聘机器人来帮我找工作。

一、基本需求

一个爬虫程序爬取需要的工作信息,存入数据库。
聊天机器人每天上午10点向未聊天的boss打招呼。
如果有boss回复了,聊天机器人向boss发送简历(因为boss直聘设定必须双方说话过后才能发简历)。

二、涉及知识点

  • protobuf
  • MQTT
  • websocket

三、聊天机器人实现过程

1. 查看boss直聘网页版即时通讯实现方式。

查看聊天界面ws下有无建立websocket。
在这里插入图片描述
发现无websocket建立,考虑难道是ajax轮训不会这么low吧?
但是在与别人聊天时候,下面并无新增ajax请求。
打开抓包工具charles进行抓包查看。

在这里插入图片描述
发现是有ws建立的。改用chrome浏览器,
在这里插入图片描述
可以看到是有ws建立。(第一个坑,以后默认使用chrome浏览器)。

2.websocket连接建立方式及参数

查看连接建立的调用栈
在这里插入图片描述
将关联的js都下载到本地,使用webstorm格式化代码。再使用charles的map local功能将这三个文件代理。这样boss直聘使用的js就是本地格式化后的代码了(否则在使用查看调用时,所有代码都堆在2行,不可读)。
仔细查看调用堆栈确认是app.js的connection处传入的连接参数。
在这里插入图片描述
搜索关键词paho-mqtt了解mqtt的功能。所以boss直聘是使用的paho.mqtt这个第三方库。
在本地使用paho.mqtt第三方库做个连接的demo。参数通过修改app.js,在建立连接前打印所有的参数获得。
代码:

var hostname = 'ws.zhipin.com', //'192.168.1.2',
            port = 443,
            ssl = true,
            topic = '/chatws';
        client = new Paho.MQTT.Client(hostname, port, topic, "ws-CD090DC8307DE0AC");
        //建立客户端实例
        var options = {

            password: "Xxxxxx",
            userName: "Xxxxxxxx",
            useSSL: ssl,
            onSuccess: onConnect,
            onFailure: function (e) {
                console.log(e);
            }
        };
        client.connect(options);
        //发送消息  
        message = new Paho.MQTT.Message("hello");  
        message.destinationName = topic;  
        client.send(message);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

运行后:
在这里插入图片描述
连接已经可以建立。但是在发送信息后,会报错并断开连接。因为数据格式有误。

3、通信数据格式

websocket连接已经可以建立,接下来就是查看boss直聘的通信数据格式了。
通过charles抓包,取到当我对一个boss发送“你好”时的数据:

3357 0004 6368 6174 0001 0801 1A4B 0A02 0800 1220 0800 121C 3531 6465 3762 6632 6234 3732 6139 3566 3148 525F 3039 5739 4556 6F7E 1801 20EE FBC8 FB92 2D28 B8B7 8EF2 922D 320C 0801 1001 1A06 E4BD A0E5 A5BD 58EE FBC8 FB92 2D
  • 1

简单的16进制转字符串后获得:

3W�chat���K
�� ��51de7bf2b472a95f1HR_09W9EVo~�� �����-(����-2�����你好X�����-
  • 1
  • 2

这不是一个简单的json数据,查看数据发送时的调用堆栈
在这里插入图片描述
发现protobuf.js关键字,搜索并学习了protobuf。通过打印发送的数据。
在这里插入图片描述
获得数据:

 [8, 1, 26, 75, 10, 2, 8, 0, 18, 32, 8, 0, 18, 28, 53, 49, 100, 101, 55, 98, 102, 50, 98, 52, 55, 50, 97, 57, 53, 102, 49, 72, 82, 95, 48, 57, 87, 57, 69, 86, 111, 126, 24, 1, 32, 233, 193, 149, 232, 145, 45, 40, 179, 253, 218, 222, 145, 45, 50, 12, 8, 1, 16, 1, 26, 6, 228, 189, 160, 229, 165, 189, 88, 233, 193, 149, 232, 145, 45]
  • 1

写一个使用protobuf转化的demo。

var protobuf = require("protobufjs");

protobuf.load("proto.proto")
    .then(function (root) {
        var Protocol = root.lookupType("TechwolfChatProtocol");
        // var data = Buffer.from([8,2,34,87,8,1,16,182,196,186,9,26,76,10,0,18,0,26,0,34,0,42,18,49,53,53,48,56,49,55,53,57,51,55,49,49,49,52,53,50,53,50,12,53,57,46,49,48,57,46,55,55,46,57,52,56,187,70,66,3,119,101,98,74,2,45,49,82,0,90,0,97,0,0,0,0,0,0,0,0,105,0,0,0,0,0,0,0,0,40,0]);
        var data = Buffer.from([8, 1, 26, 75, 10, 2, 8, 0, 18, 32, 8, 0, 18, 28, 53, 49, 100, 101, 55, 98, 102, 50, 98, 52, 55, 50, 97, 57, 53, 102, 49, 72, 82, 95, 48, 57, 87, 57, 69, 86, 111, 126, 24, 1, 32, 233, 193, 149, 232, 145, 45, 40, 179, 253, 218, 222, 145, 45, 50, 12, 8, 1, 16, 1, 26, 6, 228, 189, 160, 229, 165, 189, 88, 233, 193, 149, 232, 145, 45]);
        // Decode an Uint8Array (browser) or Buffer (node) to a message
        var message = Protocol.decode(data);
        console.log(message);

    });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

打印数据为:

TechwolfChatProtocol {
  messages:
   [ TechwolfMessage {
       from: [TechwolfUser],
       to: [TechwolfUser],
       type: 1,
       mid: [Long],
       time: [Long],
       body: [TechwolfMessageBody],
       cmid: [Long] } ],
  messageSync: [],
  messageRead: [],
  type: 1 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

可以解析数据。这样数据格式就找到了。

4、发送一条消息。

考虑node运行聊天机器人,但是 paho-mqtt.js不支持node方式,仅支持浏览器。而python同时支持paho-mqtt和protobuf。
所以使用python来结合完成。
但是在python中建立socket连接时总会报错:
在这里插入图片描述
给出了报错,但是没有具体信息。以为是tls版本太低,最后在mqtt源码报错处添加打印,获得信息

b"bytearray(b'http/1.1 403 forbidden\\r\\n')"
b"bytearray(b'date: wed, 27 feb 2019 10:03:06 gmt\\r\\n')"
b"bytearray(b'transfer-encoding: chunked\\r\\n')"
b"bytearray(b'connection: keep-alive\\r\\n')"
  • 1
  • 2
  • 3
  • 4

发现是在websocket握手时,http请求直接403了,考虑是否与cookies有关,在源码找到header设置的地方,添加cookies参数。
运行后可以正常连接。
结合protobuf后,最终代码:

chat = {
    'type': 1,
    'messages': [
        {
            'from': {'uid': 0},
            'to': {'uid': 0, 'name': 'xxxxxxxxx~'},#name为boss的id
            'type': 1,
            'mid': 1550970085609,
            'time': 1550950252211,
            'body': {'type': 1, 'templateId': 1, 'text': '你好'},
            'cmid': 1550970085609
        }
    ]
}
chat_protocol = protobuf_json.json2pb(TechwolfChatProtocol(), chat)

hostname = 'ws.zhipin.com'
port = 443
clientId = '19833398'
timeout = 60
keepAlive = 100
topic = '/chatws'

# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
    print("Connected with result code " + str(rc))

    # Subscribing in on_connect() means that if we lose the connection and
    # reconnect then subscriptions will be renewed.
    client.subscribe(topic)
    client.publish(topic,payload=chat_protocol.SerializeToString(),qos=0)


def on_disconnect(client, userdata, rc):
    if rc != 0:
        print("Unexpected disconnection.")

# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
    print('received message -----'+client)
    protocol = TechwolfChatProtocol()
    protocol.ParseFromString(msg.payload)
    print(protocol)

client = mqtt.Client(client_id="ws-CD090DC8307DE0AC", clean_session=True,
                     transport="websockets")
client.username_pw_set("xxxxxxx", "xxxxx")  # 参数分别boss为用户生的mqtt账号密码。
client.ws_set_options(path=topic)
client.tls_set()
client.on_connect = on_connect
client.on_message = on_message
client.on_disconnect = on_disconnect

client.connect(hostname, port, 60)
client.loop_forever()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

运行后,手机端以及可以看到给boss发送了“你好”。

5.总结

接下来只需要结合爬虫,就可以完成最初的设想啦。
完成这个聊天机器学到了protobuf、mqtt、websocket相关的知识。
中间有卡壳的地方也很多。
但是一定要坚信,web端所有的内容,都是可爬的。完成的所有功能,都是可以模拟的。
ps:
原文链接:XXXX直聘聊天机器人

前言

时隔小一年,之前为了找工作做了部分boss直聘web端的逆向工作,没有进行下去,期间挺多小伙伴邮箱联系我,有这方面的需求。所以对之前的工作做了完善及封装。
详情见github项目:bossbot,可以几行代码实现自己的boss机器人。
这里记录下逆向过程,补充上篇。

逆向过程:

上篇抓包工具替换app.js,在需要的地方增加打印及调试。但是现在因为app.js资源是跨域的,替换后会有跨域问题。我这里用的charles,所以需要使用rewrite添加允许跨域请求头

上篇提到python代码中,websocket建立,在mqtt源码中修改了headers。因为当时看的急,mqtt是可以设置headers的。例如:

self.client = mqtt.Client(client_id="ws-CD090DC8307DE0AC", clean_session=True,
                                  transport="websockets")
        headers = {
            "Cookie": "t=%s; wt=%s;" % (self.user_id, self.user_id)
        }
        self.client.ws_set_options(path=self.topic, headers=headers)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

心跳时间需要设置小于60s。

client.connect(hostname, port, 30)
  • 1

过无限debugger
替换代码中的debugger
替换后还是有很多debugger,因为有debugger是通过混淆及eval方法
生成并定时调用的。可以重写eval函数或者给将生成的debugger代码,传入eval执行时,替换传入的代码。

后记:

这里是当时逆向过程中的一些细节,但是并不关键,关键的在上一篇。
上面给的github项目,在我想结合爬虫,试能否自动每天定时搜索职位并沟通、发简历时发现,boss搜索有加密字段zp_token。并且加密也挺复杂的,所以之后会把这个算法也添加进项目支持里。就可以无痛搜索加沟通啦~~
原文链接:XXXX直聘聊天机器人(二)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/455655
推荐阅读
相关标签
  

闽ICP备14008679号