From aae04b8dfd4f599a81964ee3cc00317f7ca20f1d Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Pueyo Date: Wed, 21 Nov 2012 23:19:16 +0100 Subject: [PATCH] Data packet handling!!! --- classes/CustomPacketHandler.class.php | 93 +++++++++++++++++++------ classes/MinecraftInterface.class.php | 36 ++++++++-- classes/Packet.class.php | 13 +++- classes/PocketMinecraftClient.class.php | 35 ++++++++-- classes/PocketMinecraftServer.class.php | 17 ++++- classes/Session.class.php | 21 ++++-- classes/Utils.class.php | 11 ++- client.php | 1 + common/config.php | 2 +- pstruct/RakNet.php | 8 +-- server.php | 1 + 11 files changed, 184 insertions(+), 54 deletions(-) diff --git a/classes/CustomPacketHandler.class.php b/classes/CustomPacketHandler.class.php index 985b07b6c..9ff68eedc 100644 --- a/classes/CustomPacketHandler.class.php +++ b/classes/CustomPacketHandler.class.php @@ -26,7 +26,7 @@ the Free Software Foundation, either version 3 of the License, or */ class CustomPacketHandler{ - var $data, $name = ""; + var $offset, $raw, $c, $data, $name = ""; private function get($len = true, $check = true){ if($len === true){ @@ -49,48 +49,95 @@ class CustomPacketHandler{ $this->offset = 0; $this->c = (bool) $create; switch($pid){ - case 0x400090: - case "clientHandshake": - $this->name = "clientHandshake"; + case 0x60: + case 0x40: if($this->c === false){ $this->data["counter"] = Utils::readTriad($this->get(3)); - $this->data["id"] = ord($this->get(1)); + if($pid === 0x60){ + $this->data["unknown1"] = $this->get(4); + } + $this->data["packets"] = array(); + while($this->offset < strlen($this->raw)){ + $id = ord($this->get(1)); + $raw = $this->get(true); + $pk = new CustomPacketHandler($id, $raw); + $pk->data["id"] = $id; + $pk->data["packetName"] = $pk->name; + $this->data["packets"][] = array($pid, $pk->data, $raw); + } + } + break; + case 0x82: + if($this->c === false){ + $this->data["username"] = $this->get(Utils::readShort($this->get(2), false)); + $this->data["unknown1"] = $this->get(5); + }else{ + $this->raw .= Utils::writeShort(strlen($this->data["username"])).$this->data["username"]; + $this->raw .= "\x00\x00\x00\x08\x00"; + } + break; + break; + case 0x09: + if($this->c === false){ $this->data["clientID"] = $this->get(8); $this->data["unknown1"] = $this->get(1); $this->data["unknown2"] = $this->get(4); $this->data["session"] = $this->get(4); }else{ - $this->raw .= Utils::writeTriad(0); //counter - $this->raw .= chr(9); $this->raw .= $this->data["clientID"]; $this->raw .= "\x00"; $this->raw .= "\x00\x00\x00\x00"; $this->raw .= $this->data["session"]; } break; - case 0x600300: - case "serverHandshake": - $this->name = "serverHandshake"; + case 0x10: if($this->c === false){ - $this->data["counter"] = Utils::readTriad($this->get(3)); - $this->data["id"] = ord($this->get(1)); - $this->data["unknown1"] = $this->get(4); $this->data["cookie"] = $this->get(4); // 043f57ff - $this->data["unknown2"] = ord($this->get(1)); - $this->data["port"] = Utils::readShort($this->get(2)); - $this->data["dataArray"] = Utils::readDataArray($this->get(true, false), 10); - $this->data["unknown3"] = $this->get(1); - $this->data["unknown4"] = $this->get(4); + $this->data["unknown1"] = $this->get(1); + $this->data["port"] = Utils::readShort($this->get(2), false); + $this->data["dataArray"] = Utils::readDataArray($this->get(true, false), 10, $offset); + $this->get($offset); + $this->data["unknown2"] = $this->get(7); $this->data["session"] = $this->get(4); - $this->data["unknown5"] = $this->get(8); + $this->data["unknown3"] = $this->get(7); }else{ - $this->raw .= Utils::writeTriad(0); //counter - $this->raw .= chr(0); - $this->raw .= "\x00\x00\x00\x10"; $this->raw .= "\x04\x3f\x57\xff"; $this->raw .= "\x00"; - $this->raw .= "\x7f"; $this->raw .= Utils::writeShort($this->data["port"]); + $this->raw .= Utils::writeDataArray(array( + "\x80\xff\xff\xfe", + "\xff\xff\xff\xff", + "\xff\xff\xff\xff", + "\xff\xff\xff\xff", + "\xff\xff\xff\xff", + "\xff\xff\xff\xff", + "\xff\xff\xff\xff", + "\xff\xff\xff\xff", + "\xff\xff\xff\xff", + "\xff\xff\xff\xff", + )); + $this->raw .= "\x00\x00\x00\x00\x00\x00\x00"; + $this->raw .= $this->data["session"]; + $this->raw .= "\x00\x00\x00\x00\x00\x00\x00"; + } + break; + case 0x13: + if($this->c === false){ + $this->data["cookie"] = $this->get(4); // 043f57ff + $this->data["unknown1"] = $this->get(1); + $this->data["port"] = Utils::readShort($this->get(2), false); + $this->data["dataArray0"] = $this->get(ord($this->get(1))); + $this->data["dataArray"] = Utils::readDataArray($this->get(true, false), 9, $offset); + $this->get($offset); + $this->data["unknown2"] = $this->get(13); + }else{ + $this->raw .= "\x04\x3f\x57\xff"; + $this->raw .= "\x3d"; + $this->raw .= Utils::writeShort($this->data["port"]); + $w = array_shift($this->data["dataArray"]); + $this->raw .= chr(strlen($w)).$w; + $this->raw .= Utils::writeDataArray($this->data["dataArray"]); + $this->raw .= "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; } break; } diff --git a/classes/MinecraftInterface.class.php b/classes/MinecraftInterface.class.php index 66ac4702c..e3f583df4 100644 --- a/classes/MinecraftInterface.class.php +++ b/classes/MinecraftInterface.class.php @@ -26,7 +26,7 @@ the Free Software Foundation, either version 3 of the License, or */ class MinecraftInterface{ - var $pstruct, $name, $server, $protocol, $client; + var $pstruct, $name, $server, $protocol, $client, $buffer; function __construct($server, $protocol = CURRENT_PROTOCOL, $port = 25565, $listen = false, $client = true){ $this->server = new Socket($server, $port, (bool) $listen); @@ -35,7 +35,9 @@ class MinecraftInterface{ require("pstruct/packetName.php"); $this->pstruct = $pstruct; $this->name = $packetName; + $this->buffer = array(); $this->client = (bool) $client; + $this->start = microtime(true); } public function close(){ @@ -51,7 +53,7 @@ class MinecraftInterface{ protected function writeDump($pid, $raw, $data, $origin = "client", $ip = "", $port = 0){ if(LOG === true and DEBUG >= 2){ - $p = "[".microtime(true)."] [".((($origin === "client" and $this->client === true) or ($origin === "server" and $this->client === false)) ? "CLIENT->SERVER":"SERVER->CLIENT")." ".$ip.":".$port."]: ".$this->name[$pid]." (0x".Utils::strTohex(chr($pid)).") [lenght ".strlen($raw)."]".PHP_EOL; + $p = "[".(microtime(true) - $this->start)."] [".((($origin === "client" and $this->client === true) or ($origin === "server" and $this->client === false)) ? "CLIENT->SERVER":"SERVER->CLIENT")." ".$ip.":".$port."]: ".$this->name[$pid]." (0x".Utils::strTohex(chr($pid)).") [lenght ".strlen($raw)."]".PHP_EOL; $p .= Utils::hexdump($raw); if(is_array($data)){ foreach($data as $i => $d){ @@ -64,7 +66,11 @@ class MinecraftInterface{ } - public function readPacket($port = false){ + public function readPacket(){ + $p = $this->popPacket(); + if($p !== false){ + return $p; + } if($this->server->connected === false){ //return array("pid" => "ff", "data" => array(0 => 'Connection error')); } @@ -80,7 +86,7 @@ class MinecraftInterface{ $struct = $this->getStruct($pid); if($struct === false){ console("[ERROR] Bad packet id 0x".Utils::strTohex(chr($pid)), true, true, 0); - $p = "[".microtime(true)."] [".((($origin === "client" and $this->client === true) or ($origin === "server" and $this->client === false)) ? "CLIENT->SERVER":"SERVER->CLIENT")." ".$ip.":".$port."]: Error, bad packet id 0x".Utils::strTohex(chr($pid))." [lenght ".strlen($raw)."]".PHP_EOL; + $p = "[".(microtime(true) - $this->start)."] [".((($origin === "client" and $this->client === true) or ($origin === "server" and $this->client === false)) ? "CLIENT->SERVER":"SERVER->CLIENT")." ".$ip.":".$port."]: Error, bad packet id 0x".Utils::strTohex(chr($pid))." [lenght ".strlen($raw)."]".PHP_EOL; $p .= Utils::hexdump($data[0]); $p .= PHP_EOL; logg($p, "packets", true, 2); @@ -94,9 +100,25 @@ class MinecraftInterface{ $packet = new Packet($pid, $struct, $data[0]); $packet->protocol = $this->protocol; - $packet->parse(); - $this->writeDump($pid, $data[0], $packet->data, "server", $data[1], $data[2]); - return array("pid" => $pid, "data" => $packet->data, "raw" => $data[0], "ip" => $data[1], "port" => $data[2]); + $packet->parse(); + $this->data[] = array($pid, $packet->data, $data[0], $data[1], $data[2]); + if(isset($packet->data["packets"]) and is_array($packet->data["packets"])){ + foreach($packet->data["packets"] as $p){ + $this->data[] = array($pid, $p[1], $p[2], $data[1], $data[2]); + } + } + return $this->popPacket(); + } + + public function popPacket(){ + if(count($this->data) > 0){ + $p = array_shift($this->data); + $c = (isset($p[1]["id"]) ? true:false); + $p[2] = $c ? chr($p[1]["id"]).$p[2]:$p[2]; + $this->writeDump(($c ? $p[1]["id"]:$p[0]), $p[2], $p[1], "server", $p[3], $p[4]); + return array("pid" => $p[0], "data" => $p[1], "raw" => $p[2], "ip" => $p[3], "port" => $p[4]); + } + return false; } public function writePacket($pid, $data = array(), $raw = false, $dest = false, $port = false){ diff --git a/classes/Packet.class.php b/classes/Packet.class.php index 85282f3cd..9ca073078 100644 --- a/classes/Packet.class.php +++ b/classes/Packet.class.php @@ -71,7 +71,10 @@ class Packet{ } break; case "customData": - $reply = new CustomPacketHandler($this->data[1], "", $this->data[$field], true); + $reply = new CustomPacketHandler($this->data[$field]["id"], "", $this->data[$field], true); + $this->addRaw(Utils::writeShort(strlen($reply->raw) << 3)); + $this->addRaw(Utils::writeTriad($this->data[$field]["count"])); + $this->addRaw(chr($this->data[$field]["id"])); $this->addRaw($reply->raw); break; case "magic": @@ -162,9 +165,13 @@ class Packet{ } break; case "customData": - $d = new CustomPacketHandler($this->data[1], $this->get(true)); + $d = new CustomPacketHandler($this->data[1], $this->get(Utils::readShort($this->get(2), false) >> 3)); $d->data["packetName"] = $d->name; - $this->data[] = $d->data; + if(isset($d->data["packets"])){ + $this->data["packets"] = $d->data["packets"]; + }else{ + $this->data[] = $d->data; + } break; case "magic": $this->data[] = $this->get(16); diff --git a/classes/PocketMinecraftClient.class.php b/classes/PocketMinecraftClient.class.php index bf62a4cc9..c57b162e2 100644 --- a/classes/PocketMinecraftClient.class.php +++ b/classes/PocketMinecraftClient.class.php @@ -114,8 +114,9 @@ class PocketMinecraftClient{ $serverID = $data[1]; $this->send(0x84, array( $this->counter[0], - 0x400090, + 0x40, array( + "id" => 0x09, "clientID" => $this->clientID, "session" => "\x00\x0c\x98\x00", ), @@ -123,13 +124,33 @@ class PocketMinecraftClient{ ++$this->counter[0]; break; case 0x84: - $this->counter[1] = $data[0]; - $this->send(0xc0, array(1, true, $data[0])); - switch($custom->name){ - case "serverHandshake": - + if(isset($data[0])){ + $this->counter[1] = $data[0]; + $this->send(0xc0, array(1, true, $data[0])); + } + switch($data["id"]){ + case 0x10: + $this->send(0x84, array( + $this->counter[0], + 0x40, + array( + "id" => 0x13, + "port" => 19132, + "dataArray" => $data["dataArray"], + ), + )); + ++$this->counter[0]; + $this->send(0x84, array( + $this->counter[0], + 0x40, + array( + "id" => 0x82, + "username" => $this->username, + ), + )); + ++$this->counter[0]; break; - } + } break; } } diff --git a/classes/PocketMinecraftServer.class.php b/classes/PocketMinecraftServer.class.php index 0fd034a63..c5f0a0753 100644 --- a/classes/PocketMinecraftServer.class.php +++ b/classes/PocketMinecraftServer.class.php @@ -28,7 +28,7 @@ the Free Software Foundation, either version 3 of the License, or require_once("classes/Session.class.php"); class PocketMinecraftServer{ - protected $interface, $protocol, $entities, $player, $cnt, $events, $username, $version, $clients; + protected $interface, $protocol, $entities, $player, $cnt, $events, $username, $version, $clients, $serverType; function __construct($username, $protocol = CURRENT_PROTOCOL, $version = CURRENT_VERSION){ //$this->player = new Player($username); $this->version = (int) $version; @@ -39,6 +39,7 @@ class PocketMinecraftServer{ $this->actions = array(); $this->clients = array(); $this->protocol = (int) $protocol; + $this->setType("normal"); $this->interface = new MinecraftInterface("255.255.255.255", $this->protocol, 19132, true, false); console("[INFO] Creating Minecraft Server"); console("[INFO] Username: ".$this->username); @@ -46,6 +47,18 @@ class PocketMinecraftServer{ $this->stop = false; } + public function setType($type = "demo"){ + switch($type){ + case "normal": + $this->serverType = "MCCPP;Demo;"; + break; + case "minecon": + $this->serverType = "MCCPP;MINECON;"; + break; + } + + } + public function action($microseconds, $code){ $this->actions[] = array($microseconds / 1000000, microtime(true), $code); console("[INTERNAL] Attached to action ".$microseconds, true, true, 3); @@ -86,7 +99,7 @@ class PocketMinecraftServer{ $data[0], $this->serverID, MAGIC, - "MCCPP;Demo;". $this->username, + $this->serverType. $this->username, ), false, $packet["ip"], $packet["port"]); break; case 0x05: diff --git a/classes/Session.class.php b/classes/Session.class.php index ab9bfe554..70536cdb6 100644 --- a/classes/Session.class.php +++ b/classes/Session.class.php @@ -28,7 +28,7 @@ the Free Software Foundation, either version 3 of the License, or class Session{ protected $server, $serverID, $timeout, $eventID, $connected; - var $clientID, $ip, $port, $counter; + var $clientID, $ip, $port, $counter, $username; function __construct($server, $clientID, $ip, $port){ $this->server = $server; $this->clientID = $clientID; @@ -67,19 +67,28 @@ class Session{ )); break; case 0x84: - $this->counter[1] = $data[0]; - $this->send(0xc0, array(1, true, $data[0])); - switch($data[2]["packetName"]){ - case "clientHandshake": + if(isset($data[0])){ + $this->counter[1] = $data[0]; + $this->send(0xc0, array(1, true, $data[0])); + } + switch($data["id"]){ + case 0x09: $this->send(0x84, array( $this->counter[0], - 0x600300, + 0x40, array( + "id" => 0x10, + "count" => 0, "port" => $this->port, + "session" => $data[2]["session"], ), )); ++$this->counter[0]; break; + case 0x82: + $this->username = $data["username"]; + console("[INFO] User ".$this->username." connected from ".$this->ip.":".$this->port); + break; } break; case 0x8c: diff --git a/classes/Utils.class.php b/classes/Utils.class.php index 93e00c904..c6c024cac 100644 --- a/classes/Utils.class.php +++ b/classes/Utils.class.php @@ -280,7 +280,7 @@ class Utils{ return substr(pack("N", $value), 1); } - public static function readDataArray($str, $len = 10){ + public static function readDataArray($str, $len = 10, &$offset = null){ $data = array(); $offset = 0; for($i = 1; $i <= $len; ++$i){ @@ -292,6 +292,15 @@ class Utils{ return $data; } + public static function writeDataArray($data){ + $raw = ""; + foreach($data as $v){ + $raw .= Utils::writeTriad(strlen($v)); + $raw .= $v; + } + return $raw; + } + public static function readInt($str){ list(,$unpacked) = unpack("N", $str); return (int) $unpacked; diff --git a/client.php b/client.php index 3039ebab0..22b29013d 100644 --- a/client.php +++ b/client.php @@ -30,6 +30,7 @@ require_once("classes/PocketMinecraftClient.class.php"); file_put_contents("packets.log", ""); $client = new PocketMinecraftClient("shoghicp"); +console("[INFO] Searching servers..."); $list = $client->getServerList(); foreach($list as $i => $info){ console("[Server] #".$i." ".$info["ip"]." ".$info["username"]); diff --git a/common/config.php b/common/config.php index fa58cddc2..f764b8829 100644 --- a/common/config.php +++ b/common/config.php @@ -34,6 +34,6 @@ set_include_path(get_include_path() . PATH_SEPARATOR . FILE_PATH . PATH_SEPARATO ini_set("memory_limit", "512M"); define("CURRENT_PROTOCOL", 1); define("CURRENT_VERSION", 5); -define("DEBUG", 3); +define("DEBUG", 2); define("LOG", true); define("MAGIC", "\x00\xff\xff\x00\xfe\xfe\xfe\xfe\xfd\xfd\xfd\xfd\x12\x34\x56\x78"); \ No newline at end of file diff --git a/pstruct/RakNet.php b/pstruct/RakNet.php index ec8873157..9636af2a1 100644 --- a/pstruct/RakNet.php +++ b/pstruct/RakNet.php @@ -82,25 +82,25 @@ $pstruct = array( 0x80 => array( "triad", - "triad", + "ubyte", "customData", ), 0x84 => array( "triad", - "triad", + "ubyte", "customData", ), 0x88 => array( "triad", - "triad", + "ubyte", "customData", ), 0x8c => array( "triad", - "triad", + "ubyte", "customData", ), diff --git a/server.php b/server.php index 256faa32f..3680a6b84 100644 --- a/server.php +++ b/server.php @@ -30,4 +30,5 @@ require_once("classes/PocketMinecraftServer.class.php"); file_put_contents("packets.log", ""); $server = new PocketMinecraftServer("shoghicp"); +$server->setType("minecon"); $server->start(); \ No newline at end of file