-
-
Save hanbule/f8619eb1514bc0aa2ca574dff70e821b to your computer and use it in GitHub Desktop.
MCBE network debugger
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <modloader/statichook.h> | |
#include <dlfcn.h> | |
#include <string> | |
static auto handshake = (size_t) dlsym(dlopen(nullptr, RTLD_LAZY), "_ZTV29ServerToClientHandshakePacket") + 0x10; | |
struct Packet { size_t vt; }; | |
struct LoginPacket; | |
struct ClientToServerHandshakePacket; | |
struct NetworkIdentifier; | |
struct ServerNetworkHandler { | |
void handleClientToServerHandshake(NetworkIdentifier const &, ClientToServerHandshakePacket const &); | |
}; | |
TInstanceHook(void, _ZN20ServerNetworkHandler11handleLoginERK17NetworkIdentifierRK11LoginPacket, ServerNetworkHandler, NetworkIdentifier const &nid, LoginPacket const &packet) { | |
original(this, nid, packet); | |
ClientToServerHandshakePacket *pk = nullptr; | |
handleClientToServerHandshake(nid, *pk); | |
} | |
TClasslessInstanceHook(void, _ZN12PacketSender19sendToPrimaryClientERK17NetworkIdentifierRK6Packet, NetworkIdentifier const &nid, Packet const &packet) { | |
if (packet.vt != handshake) | |
original(this, nid, packet); | |
} | |
TClasslessInstanceHook(void, _ZN20EncryptedNetworkPeer16enableEncryptionERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE, std::string const &token) {} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace Frago9876543210\PacketStealer { | |
/** @noinspection PhpIncludeInspection */ | |
require_once "vendor/autoload.php"; | |
use pocketmine\network\mcpe\NetworkCompression; | |
use pocketmine\network\mcpe\PacketStream; | |
use pocketmine\network\mcpe\protocol\DataPacket; | |
use pocketmine\network\mcpe\protocol\PacketPool; | |
use raklib\protocol\{ACK, AdvertiseSystem, ConnectedPing, ConnectedPong, ConnectionRequest, | |
ConnectionRequestAccepted, Datagram, DisconnectionNotification, EncapsulatedPacket, IncompatibleProtocolVersion, | |
NACK, NewIncomingConnection, OpenConnectionReply1, OpenConnectionReply2, OpenConnectionRequest1, | |
OpenConnectionRequest2, Packet, UnconnectedPing, UnconnectedPingOpenConnections, UnconnectedPong}; | |
use raklib\server\UDPServerSocket; | |
use raklib\utils\InternetAddress; | |
class Proxy extends UDPServerSocket{ | |
/** @var Address */ | |
private $serverAddress; | |
/** @var Address */ | |
private $clientAddress; | |
/** @var bool */ | |
private $sessionCreated = false; | |
/** @var Datagram[][] */ | |
private $splitPackets = []; | |
public function __construct(Address $bindAddress, Address $serverAddress){ | |
RakNetPool::init(); | |
PacketPool::init(); | |
parent::__construct($bindAddress); | |
$this->serverAddress = $serverAddress; | |
while(true){ | |
$this->tick(); | |
} | |
} | |
public function tick() : void{ | |
if($this->readPacket($buffer, $source, $port) !== false){ | |
if(($packet = RakNetPool::getPacket($buffer)) !== null){ | |
$this->handlePacket($packet, new Address($source, $port)); | |
} | |
} | |
} | |
private function handlePacket(Packet $packet, Address $address) : void{ | |
if(!$this->sessionCreated){ | |
if($packet instanceof UnconnectedPing){ | |
$this->clientAddress = $address; | |
$this->sendToServer($packet); | |
}elseif($packet instanceof UnconnectedPong){ | |
$this->sendToClient($packet); | |
}elseif($packet instanceof OpenConnectionRequest1){ | |
$this->clientAddress = $address; | |
$this->sessionCreated = true; | |
$this->sendToServer($packet); | |
} | |
}else{ | |
if($packet instanceof Datagram){ | |
$this->handleDatagram($packet); | |
} | |
if($this->serverAddress->equals($address)){ | |
$this->sendToClient($packet); | |
}else{ | |
$this->sendToServer($packet); | |
} | |
} | |
} | |
private function handleDatagram(Datagram $datagram) : void{ | |
foreach($datagram->packets as $pk){ | |
$this->handleEncapsulatedPacket($pk); | |
} | |
} | |
private function handleEncapsulatedPacket(EncapsulatedPacket $packet) : void{ | |
if($packet->hasSplit && ($packet = $this->handleSplit($packet)) === null){ | |
return; | |
} | |
if($packet->buffer !== "" && $packet->buffer{0} === "\xfe"){ | |
$this->handleBatch($packet->buffer); | |
} | |
} | |
private function handleSplit(EncapsulatedPacket $packet) : ?EncapsulatedPacket{ | |
if($packet->splitCount >= 128 or $packet->splitIndex >= 128 or $packet->splitIndex < 0){ | |
return null; | |
} | |
if(!isset($this->splitPackets[$packet->splitID])){ | |
if(count($this->splitPackets) >= 4){ | |
return null; | |
} | |
$this->splitPackets[$packet->splitID] = [$packet->splitIndex => $packet]; | |
}else{ | |
$this->splitPackets[$packet->splitID][$packet->splitIndex] = $packet; | |
} | |
if(count($this->splitPackets[$packet->splitID]) === $packet->splitCount){ | |
$pk = new EncapsulatedPacket; | |
$pk->buffer = ""; | |
$pk->reliability = $packet->reliability; | |
$pk->messageIndex = $packet->messageIndex; | |
$pk->sequenceIndex = $packet->sequenceIndex; | |
$pk->orderIndex = $packet->orderIndex; | |
$pk->orderChannel = $packet->orderChannel; | |
for($i = 0; $i < $packet->splitCount; ++$i){ | |
$pk->buffer .= $this->splitPackets[$packet->splitID][$i]->buffer; | |
} | |
$pk->length = strlen($pk->buffer); | |
unset($this->splitPackets[$packet->splitID]); | |
return $pk; | |
} | |
return null; | |
} | |
private function handleBatch(string $payload) : void{ | |
try{ | |
$stream = new PacketStream(NetworkCompression::decompress(substr($payload, 1))); | |
}catch(\Exception $e){ | |
return; | |
} | |
while(!$stream->feof()){ | |
$this->handleDataPacket(PacketPool::getPacket($stream->getString())); | |
} | |
} | |
private function getClassName(object $class) : ?string{ | |
try{ | |
return (new \ReflectionClass($class))->getShortName(); | |
}catch(\ReflectionException $e){ | |
return null; | |
} | |
} | |
private function handleDataPacket(DataPacket $packet) : void{ | |
echo $this->getClassName($packet) . PHP_EOL; | |
} | |
private function sendToServer(Packet $packet) : void{ | |
$this->writePacket($packet->buffer, $this->serverAddress->ip, $this->serverAddress->port); | |
} | |
private function sendToClient(Packet $packet) : void{ | |
$this->writePacket($packet->buffer, $this->clientAddress->ip, $this->clientAddress->port); | |
} | |
} | |
class Address extends InternetAddress{ | |
public function __construct(string $address, int $port, int $version = 4){ | |
parent::__construct(gethostbyname($address), $port, $version); | |
} | |
} | |
class RakNetPool{ | |
/** @var Packet[] */ | |
private static $pool = []; | |
public static function init(){ | |
self::registerPacket(new ACK); | |
self::registerPacket(new AdvertiseSystem); | |
self::registerPacket(new ConnectedPing); | |
self::registerPacket(new ConnectedPong); | |
self::registerPacket(new ConnectionRequest); | |
self::registerPacket(new ConnectionRequestAccepted); | |
self::registerPacket(new DisconnectionNotification); | |
self::registerPacket(new IncompatibleProtocolVersion); | |
self::registerPacket(new NACK); | |
self::registerPacket(new NewIncomingConnection); | |
self::registerPacket(new OpenConnectionReply1); | |
self::registerPacket(new OpenConnectionReply2); | |
self::registerPacket(new OpenConnectionRequest1); | |
self::registerPacket(new OpenConnectionRequest2); | |
self::registerPacket(new UnconnectedPing); | |
self::registerPacket(new UnconnectedPingOpenConnections); | |
self::registerPacket(new UnconnectedPong); | |
} | |
/** | |
* @param Packet $packet | |
*/ | |
public static function registerPacket(Packet $packet) : void{ | |
self::$pool[$packet::$ID] = clone $packet; | |
} | |
/** | |
* @param string $buffer | |
* @return null|Packet | |
*/ | |
public static function getPacket(string $buffer) : ?Packet{ | |
$packet = isset(self::$pool[($pid = ord($buffer{0}))]) ? clone self::$pool[$pid] : new Datagram; | |
if(!$packet instanceof Packet || ($packet instanceof Datagram && $pid >= 0x8f)){ | |
return null; | |
} | |
$packet->setBuffer($buffer); | |
try{ | |
@$packet->decode(); | |
}catch(\Throwable $e){ | |
return null; | |
} | |
return $packet; | |
} | |
} | |
/** @noinspection PhpUnhandledExceptionInspection */ | |
throw new \Exception("DO NOT FORGET REPLACE IP!"); | |
/** @noinspection PhpUnreachableStatementInspection */ | |
new Proxy(new Address("0.0.0.0", 19134), new Address("192.168.x.x", 19132)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment