client packet verification

This commit is contained in:
extremeheat 2021-02-20 01:14:59 -05:00
commit 57290cd180
4 changed files with 96 additions and 15 deletions

View file

@ -1,4 +1,4 @@
const RakClient = require('@jsprismarine/raknet/client')
const RakClient = require('jsp-raknet/client')
const { Worker, isMainThread, parentPort } = require('worker_threads')
const EncapsulatedPacket = require('@jsprismarine/raknet/protocol/encapsulated_packet')

View file

@ -119,18 +119,55 @@ class Client extends Connection {
process.exit(1)
}
tryRencode(name, params, actual) {
if (name == 'level_chunk') {
console.log("Skipping chunk validation, it's broken right now")
return
}
const packet = this.serializer.createPacketBuffer({ name, params })
console.assert(packet.toString('hex') == actual.toString('hex'))
if (packet.toString('hex') !== actual.toString('hex')) {
const ours = packet.toString('hex').match(/.{1,16}/g).join('\n')
const theirs = actual.toString('hex').match(/.{1,16}/g).join('\n')
fs.writeFileSync('ours.txt', ours)
fs.writeFileSync('theirs.txt', theirs)
fs.writeFileSync('ours.json', serialize(params))
fs.writeFileSync('theirs.json', serialize(this.deserializer.parsePacketBuffer(packet).data.params))
throw new Error(name + ' Packet comparison failed!')
}
}
readPacket(packet) {
// console.log('packet', packet)
const des = this.deserializer.parsePacketBuffer(packet)
const pakData = { name: des.data.name, params: des.data.params }
console.log('->', pakData.name, serialize(pakData.params).slice(0, 100))
// No idea what this exotic 0xA0 packet is, it's not implemented anywhere
// and seems empty. Possible gibberish from the raknet impl
if (pakData.name == '160' || !pakData.name) { // eslint-ignore-line
console.warn('?? Ignoring extraneous packet ', des)
return
}
// Packet verifying (decode + re-encode + match test)
if (pakData.name) {
this.tryRencode(pakData.name, pakData.params, packet)
}
// console.info('->', JSON.stringify(pakData, (k,v) => typeof v == 'bigint' ? v.toString() : v))
// Packet dumping
try {
if (!fs.existsSync(`./packets/${pakData.name}.json`)) {
fs.writeFileSync(`./packets/${pakData.name}.json`, serialize(pakData.params, 2))
fs.writeFileSync(`./packets/${pakData.name}.txt`, packet.toString('hex'))
}
} catch {}
} catch { }
switch (des.data.name) {
case 'server_to_client_handshake':
this.emit('client.server_handshake', des.data.params)
@ -143,6 +180,10 @@ class Client extends Connection {
break
case 'start_game':
fs.writeFileSync('start_game.json', JSON.stringify(des.data.params, (k, v) => typeof v == 'bigint' ? v.toString() : v))
break
case 'level_chunk':
fs.writeFileSync(`./chunks/chunk-${chunks++}.txt`, packet.toString('hex'))
break
default:
// console.log('Sending to listeners')
}
@ -151,6 +192,8 @@ class Client extends Connection {
}
}
var chunks = 0;
function serialize(obj = {}, fmt) {
return JSON.stringify(obj, (k, v) => typeof v == 'bigint' ? v.toString() : v, fmt)
}

View file

@ -1,20 +1,42 @@
// process.env.DEBUG = 'minecraft-protocol raknet'
const { Client } = require('./client')
const fs = require('fs')
// console.log = () =>
async function test() {
const client = new Client({
hostname: '127.0.0.1',
port: 19132
const client = new Client({
hostname: '127.0.0.1',
port: 19132
})
client.once('resource_packs_info', (packet) => {
client.write('resource_pack_client_response', {
response_status: 'completed',
resourcepackids: []
})
client.once('resource_packs_info', (packet) => {
client.write('resource_pack_client_response', {
response_status: 'completed',
resourcepackids: []
})
client.once('resource_pack_stack', (stack) => {
client.write('resource_pack_client_response', {
response_status: 'completed',
resourcepackids: []
})
})
// client.once('resource_packs_info', (packet) => {
// client.write('resource_pack_client_response', {
// response_status: 'completed',
// resourcepackids: []
// })
// })
})
// var read = 0;
// client.on('level_chunk', (packet) => {
// read++
// fs.writeFileSync(`level_chunk-${read}.json`, JSON.stringify(packet, null, 2))
// })
}
test()

View file

@ -2,8 +2,8 @@ const BinaryStream = require('@jsprismarine/jsbinaryutils').default
const BatchPacket = require('./datatypes/BatchPacket')
const cipher = require('./transforms/encryption')
const { EventEmitter } = require('events')
const EncapsulatedPacket = require('@jsprismarine/raknet/protocol/encapsulated_packet')
const EncapsulatedPacket = require('jsp-raknet/protocol/encapsulated_packet')
const debug = require('debug')('minecraft-protocol')
class Connection extends EventEmitter {
startEncryption(iv) {
@ -15,8 +15,10 @@ class Connection extends EventEmitter {
write(name, params) { // TODO: Batch
console.log('Need to encode', name, params)
// console.log('<-', name)
const batch = new BatchPacket()
const packet = this.serializer.createPacketBuffer({ name, params })
console.log('Sending buf', packet.toString('hex'))
batch.addEncodedPacket(packet)
if (this.encryptionEnabled) {
@ -45,6 +47,19 @@ class Connection extends EventEmitter {
}
}
/**
* Sends a MCPE packet buffer
*/
sendBuffer(buffer) {
const batch = new BatchPacket()
batch.addEncodedPacket(buffer)
if (this.encryptionEnabled) {
this.sendEncryptedBatch(batch)
} else {
this.sendDecryptedBatch(batch)
}
}
sendDecryptedBatch(batch) {
const buf = batch.encode()
// send to raknet
@ -53,10 +68,11 @@ class Connection extends EventEmitter {
sendEncryptedBatch(batch) {
const buf = batch.stream.getBuffer()
console.log('Sending encrypted batch', batch)
debug('Sending encrypted batch', batch)
this.encrypt(buf)
}
// TODO: Rename this to sendEncapsulated
sendMCPE(buffer, immediate) {
if (this.worker) {
console.log('-> buf', buffer)