Maison >développement back-end >tutoriel php >Analyse détaillée PHP de phar

Analyse détaillée PHP de phar

WBOY
WBOYavant
2022-04-22 12:14:036475parcourir

Cet article vous apporte des connaissances pertinentes sur PHP, qui présente principalement le contenu pertinent sur phar. Le nom complet de phar est PHP Archive. L'extension phar fournit un moyen de mettre l'intégralité de l'application PHP dans un fichier phar pour faciliter. déménagement et installation. Jetons un coup d'oeil à eux. J'espère que cela sera utile à tout le monde.

Analyse détaillée PHP de phar

Apprentissage recommandé : "Tutoriel vidéo PHP"

一 Qu'est-ce qu'un fichier phar

Le fichier Jar (Java Archive), une application, comprenant tous les fichiers exécutables et accessibles, est regroupée dans un JAR fichier, ce qui rend le processus de déploiement très simple.

                                 Semblable à JAR. Le nom complet de phar est PHP Archive. L'extension phar permet de placer l'intégralité de l'application PHP dans un fichier .phar pour faciliter le déplacement et l'installation. La plus grande fonctionnalité des fichiers phar est un moyen pratique de combiner plusieurs fichiers en un seul. Les fichiers .phar permettent de distribuer un programme PHP complet dans un seul fichier et de l'exécuter à partir de ce fichier. P La différence avec jar est que PHP lui-même peut être traité par PHP lui-même, il n'est donc pas nécessaire d'utiliser des outils supplémentaires pour le créer ou l'utiliser. Il peut être créé ou extrait avec un script PHP.

                                                                                                                                                                                                                                                                    .

II Création de phar

1 Modifier le fichier de configuration php.ini

Le statut par défaut du fichier PHAR est en lecture seule, et aucune configuration n'est requise pour utiliser le fichier Phar. Le déploiement est très simple. Parce que nous devons maintenant créer notre propre fichier Phar, nous devons autoriser l'écriture dans le fichier Phar. Cela nécessite de modifier php.ini

Dans mon fichier php.ini, phar.readonly = On. php.ini

我的php.ini文件中,phar.readonly = On。

[Phar]
; http://php.net/phar.readonly
;phar.readonly = On

        首先在php.ini中修改phar.readonly这个选项,去掉前面的分号,并改值为off,由于安全原因该选项默认是on,如果在php.ini中是禁用的(值为0或off),那么在用户脚本中可以开启或关闭,如果在php.ini中是开启的,那么用户脚本是无法关闭的,所以这里设置为off来展示示例。

现在,我们就可以来把PHP应用打包成Phar文件了。

2  创建我们自己的PHP文件项目

这里我都是借助别人博客的项目直接Copy的,并没有进行演示,因为我整理本篇博客的初衷是为了解phar://漏洞打ctf的。所以其中的文件名就按照原作者的不进行修改了。最后会加上参考文章的。

首先我要按按照一个的规则创建应用的目录结构,根目录为project,project下的目录如下面这样:

file
    -yunek.js
    -yunke.css
lib
    -lib_a.php
template
    -msg.html
index.php
Lib.php

其中file文件夹有两个内容为空的js和css文件,仅仅演示phar可以包含多种文件格式

lib_a.php内容如下:

<?php
/**
 * Created by yunke.
 * User: yunke
 * Date: 2017/2/10
 * Time: 9:23
 */
function show(){
    echo "l am show()";
}

msg.html内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>phar</title>
</head>
<body>
<?=$str; ?>
</body>
</html>

index.php内容如下:

<?php
/**
 * Created by yunke.
 * User: yunke
 * Date: 2017/2/10
 * Time: 9:17
 */
require "lib/lib_a.php";
show();
 
$str = isset($_GET["str"]) ? $_GET["str"] : "hello world";
include "template/msg.html";

Lib.php内容如下:

<?php
/**
 * Created by yunke.
 * User: yunke
 * Date: 2017/2/10
 * Time: 9:20
 */
function yunke()
{
    echo "l am yunke()";
}

3 创建phar文件

项目文件准备好了,现在在project文件夹同级目录建立一个yunkeBuild.php,用于产生phar格式文件,内容如下:

<?php
/**
 * Created by yunke.
 * User: yunke
 * Date: 2017/2/10
 * Time: 9:36
 */
 
//产生一个yunke.phar文件
$phar = new Phar(&#39;yunke.phar&#39;, 0, &#39;yunke.phar&#39;);
// 添加project里面的所有文件到yunke.phar归档文件
$phar->buildFromDirectory(dirname(__FILE__) . '/project');
//设置执行时的入口文件,第一个用于命令行,第二个用于浏览器访问,这里都设置为index.php
$phar->setDefaultStub('index.php', 'index.php');

然后在浏览器中访问这个yunkeBuild.php文件,将产生一个yunke.phar文件,此时服务器根目录结构如下:

project

yunkeBuild.php

yunke.phar

这就是产生一个phar归档文件最简单的过程了。

这里我再做一些其他的补充,方便更好的理解:

1)phar文件的产生是通过访问yunkeBuild.php,相当于执行。因此可以在终端执行如下代码产生

aabouzekry@platinum:~/myapp$ php yunkeBuild.php

然后就产生了yunke.phar文件。

2)new phar()产生phar对象。对其中的参数进行一下解读。

<?php
$phar = new Phar("/yunke.phar", 
        FilesystemIterator::CURRENT_AS_FILEINFO |
        FilesystemIterator::KEY_AS_FILENAME, "yunke.phar");

解释:

一个新 Phar 对象的创建通常需要三个参数。

第一个参数是Phar文件的路径。你不仅可以通过它创建Phar文件,还可以对现存的Phar文件进行操作。

第二个参数是设定 Phar 对象如何处理文件。Phar 对象继承了 PHP RecursiveDirectoryIterator 对象,这个参数是直接传递到父类里。这里提供的值是RecursiveDirectoryIterator

<?php
	$phar = new Phar(&#39;yunke.phar&#39;);
	$phar->addFile('test.php');
	include('phar://yunke.phar/test.php') // in test.php
?>
                                                                                  supprimez le point-virgule précédent et modifiez la valeur sur off Pour des raisons de sécurité, cette option est activée par défaut, si elle est désactivée dans php.ini (la valeur est 0 ou off), elle peut être activée ou désactivée dans. le script utilisateur. S'il est activé dans php.ini, le script utilisateur ne peut pas être désactivé, il est donc désactivé ici pour montrer l'exemple.

Maintenant, nous pouvons empaqueter l'application PHP dans un fichier Phar.

2 Créez notre propre projet de fichier PHP

Ici, j'ai copié directement à partir des projets de blog d'autres personnes, et je n'ai pas fait de démonstration, car mon intention initiale en organisant ce blog était de comprendre la vulnérabilité phar:// et de créer ctf . Par conséquent, les noms de fichiers ne seront pas modifiés selon ceux de l'auteur original. Des articles de référence seront ajoutés à la fin.

🎜🎜Tout d'abord, je dois créer la structure de répertoires de l'application selon une règle. Le répertoire racine est le projet, et le répertoire sous le projet est le suivant : 🎜
<?php
	$phar = new Phar(&#39;yunke.phar&#39;);
	$phar->addFromString('test.php','<?php echo \&#39;in test.php\&#39;?>');
	include('phar://yunke.phar/test.php'); // in test.php
?>
🎜Le dossier de fichiers contient deux fichiers js et css vides. , seul Demo phar peut contenir une variété de formats de fichiers 🎜🎜le contenu de lib_a.php est le suivant : 🎜
<?php
	$phar = new Phar(&#39;yunke.phar&#39;);
	$phar->addEmptyDir('test'); // yunke.phar/test/
?>
🎜le contenu de msg.html est le suivant : 🎜
<?php
	$phar = new Phar(&#39;yunke.phar&#39;);
	$phar->buildFromDirectory('test'); // test.php in test/
	include('phar://yunke.phar/test/test.php'); // in test/test.php
?>
🎜le contenu de index.php est le suivant : 🎜
<?php
	$phar = new Phar(&#39;yunke.phar&#39;);
	$phar->setStub('<?php echo \&#39;in stub!\&#39;;__HALT_COMPILER();?>');
	include('phar://yunke.phar');	// in stub!
?>
🎜le contenu de Lib.php comme suit : 🎜
$phar->setStub($phar->createDefaultStub("index.php"));
🎜3 Créez un fichier phar 🎜🎜Le fichier du projet est prêt. Créez maintenant un yunkeBuild.php dans le répertoire de même niveau du dossier du projet pour générer un fichier au format phar. Le contenu est le suivant : 🎜
<?php
Phar::mapPhar();
include "phar://yunke.phar/index.php";
__HALT_COMPILER();
🎜Ensuite. accédez à ce fichier yunkeBuild.php dans le navigateur et à un fichier yunke.phar, à ce stade, la structure du répertoire racine du serveur est la suivante : 🎜🎜project🎜🎜yunkeBuild.php🎜🎜yunke.phar🎜🎜C'est le processus le plus simple à générer un fichier d'archive phar. 🎜🎜Ici, je vais faire quelques autres ajouts pour une meilleure compréhension :🎜🎜1) Le fichier 🎜phar est généré en accédant à yunkeBuild.php, qui est tout à fait destiné à l'exécution . Par conséquent, vous pouvez exécuter le code suivant dans le terminal pour générer 🎜🎜
<?php
$phar->setStub(file_get_contents("stub.php"));
🎜, puis le fichier yunke.phar est généré. 🎜🎜2)🎜new phar() génère un objet phar. Interprétons les paramètres. 🎜🎜
<?php
    $phar = new Phar(&#39;yunke.phar&#39;);
    $phar->setDefaultStub();
    print_r($phar->getStub()); // 2, 'c' => 'text/plain', 'cc' => 'text/plain', ...
?>
🎜Explication : 🎜🎜La création d'un nouvel objet Phar nécessite généralement trois paramètres. 🎜🎜Le premier paramètre est le chemin d'accès au fichier Phar. Non seulement vous pouvez créer des fichiers Phar grâce à lui, mais vous pouvez également opérer sur des fichiers Phar existants. 🎜🎜Le deuxième paramètre consiste à définir la manière dont l'objet Phar gère les fichiers. L'objet Phar hérite de l'objet PHP RecursiveDirectoryIterator, et ce paramètre est passé directement à la classe parent. Les valeurs fournies ici sont les valeurs par défaut pour RecursiveDirectoryIterator et répondent aux exigences actuelles. 🎜🎜Le troisième paramètre est l'alias du fichier Phar. Cet alias doit être utilisé lors du référencement de ce fichier Phar en interne. 🎜🎜Habituellement, transmettez simplement le nom du fichier. C'est le troisième paramètre. 🎜🎜3)🎜 Ajouter des fichiers au phar. Il existe plusieurs façons d'ajouter des fichiers comme suit : 🎜🎜
  • 手动添加已有文件

调用类方法Phar::addFile($filepath,$localpath=?)添加文件,参数是文件绝对路径和(可选)存储到phar的相对路径

<?php
	$phar = new Phar(&#39;yunke.phar&#39;);
	$phar->addFile('test.php');
	include('phar://yunke.phar/test.php') // in test.php
?>

这里出现的phar://就是访问phar文件的一种方法,所以不需要太在意。

  • 以字符串添加文件内容

调用类方法Phar::addFromString($localpath,$contents)以字符串形式添加文件

<?php
	$phar = new Phar(&#39;yunke.phar&#39;);
	$phar->addFromString('test.php','<?php echo \&#39;in test.php\&#39;?>');
	include('phar://yunke.phar/test.php'); // in test.php
?>
  • 添加空目录

调用类方法Phar::addEmptyDir($dirname)添加空目录,使用方法Phar::getContent()获取文件结构

<?php
	$phar = new Phar(&#39;yunke.phar&#39;);
	$phar->addEmptyDir('test'); // yunke.phar/test/
?>
  • 手动选择添加已有目录

调用类方法Phar::buildFromDirectory($dir,$pattern = "")添加整个目录

<?php
	$phar = new Phar(&#39;yunke.phar&#39;);
	$phar->buildFromDirectory('test'); // test.php in test/
	include('phar://yunke.phar/test/test.php'); // in test/test.php
?>

4) 存根文件Stub,理解这个很重要。

归档文件中有一个存根文件stub,其实就是一段php执行代码,在制作归档时可以设置,直接执行归档文件时,其实就是执行它,所以它是启动文件;在脚本中包含归档文件时就像包含普通php文件一样包含它并运行,但直接以phar://的方式包含归档中某一个文件时不会执行存根代码, 往往在存根文件里面require包含要运行的其他文件,对存根文件的限制仅为以__HALT_COMPILER(); 结束,默认的存根设计是为在没有phar扩展时能够运行,它提取phar文件内容到一个临时目录再执行,不过从php5.3开始该扩展默认内置启用了。

stub是phar文件的文件头,格式为...<?php ...;__HALT_COMPILER();?>,…可以是任意字符,包括留空,且php闭合符与最后一个分号之间不能有多于一个的空格符。另外php闭合符也可省略。最短省略闭合符的stub是__HALT_COMPILER();?></p> <p>运行Phar文件时,stub文件被当做一个meta文件来初始化Phar, 并告诉Phar文件在被调用时该做什么。</p> <p>在我们的例子中,使用的是 <code>createDefaultStub() 方法。

其他的方式如下:

方法一:调用类方法Phar::setStub($string)为实例创建自定义stub

<?php
	$phar = new Phar(&#39;yunke.phar&#39;);
	$phar->setStub('<?php echo \&#39;in stub!\&#39;;__HALT_COMPILER();?>');
	include('phar://yunke.phar');	// in stub!
?>

也可以

$phar->setStub($phar->createDefaultStub("index.php"));

生成的缺省stub文件包含如下的代码:

<?php
Phar::mapPhar();
include "phar://yunke.phar/index.php";
__HALT_COMPILER();

createDefaultStub() 方法缺省创建的stub文件的内容很简单。 Phar::mapPhar() 用来分析Phar文件的元数据,并初始化它。stub文件的结尾处需要调用 __HALT_COMPILER() 方法,这个方法后不能留空格。__HALT_COMPILER() 会立即终止PHP的运行,防止include的文件在此方法后仍然执行。这是Phar必须的,没有它Phar将不能正常运行。

除此之外,我们还可以创建自己的stub文件来执行自定义的初始化过程,像这样加载自定义文件

<?php
$phar->setStub(file_get_contents("stub.php"));

方法二:使用默认stub,调用类方法Phar::setDefaultStub()为实例设置默认stub,使用方法Phar::getStub()获取实例的stub

<?php
    $phar = new Phar(&#39;yunke.phar&#39;);
    $phar->setDefaultStub();
    print_r($phar->getStub()); // 2, 'c' => 'text/plain', 'cc' => 'text/plain', ...
?>

如果缺省创建stub,PHP会使用默认stub

<?php
	$phar = new Phar(&#39;yunke.phar&#39;);
	$phar[&#39;demo.txt&#39;] = &#39;demo&#39;;
	print_r($phar->getStub()); // 2, 'c' => 'text/plain', 'cc' => 'text/plain', ...
?>

4 phar文件的运行

我们在服务器根目录建立一个index.php文件来演示如何使用上面创建的phar文件,内容如下:

<?php
 
/**
 * Created by yunke.
 * User: yunke
 * Date: 2017/2/8
 * Time: 9:33
 */
 
require "yunke.phar";
require "phar://yunke.phar/Lib.php";
yunke();

如果index.php文件中只有第一行,那么和不使用归档文件时,添加如下代码完全相同:

require "project/index.php";

如果没有第二行,那么第三行的yunke()将提示未定义,所以可见require一个phar文件时并不是导入了里面所有的文件,而只是导入了入口执行文件而已,但在实际项目中往往在这个入口文件里导入其他需要使用的文件,在本例中入口执行文件为project/index.php。

补充:

可以为归档设置别名,别名保存在归档文件中永久保存,它可以用一个简短的名字引用归档,而不管归档文件在文件系统中存储在那里,设置别名:

$phar = new Phar(&#39;lib/yunke.phar&#39;, 0);
$phar->setAlias ( "yun.phar");

设置别名后可以如下使用:

<?php
require "lib/yunke.phar";
require "phar://yun.phar/Lib.php";  //使用别名访问归档文件
require "phar://lib/yunke.phar/Lib.php"; //当然仍然可以使用这样的方式去引用

如果在制作phar文件时没有指定别名,也可以在存根文件里面使用Phar::mapPhar('yunke.phar');指定。

5 phar文件的提取还原

我们有时候会好奇phar里面包含的文件源码,这个时候就需要将phar文件还原,如果只是看一看的话可以使用一些ide工具,比如phpstorm 10就能直接打开它,如果需要修改那么就需要提取操作了,为了演示,我们下载一个composer.phar放在服务器目录,在根目录建立一个get.php文件,内容如下

<?php
/**
 * Created by yunke.
 * User: yunke
 * Date: 2017/2/9
 * Time: 19:02
 */
 
$phar = new Phar(&#39;composer.phar&#39;);
$phar->extractTo('composer'); //提取一份原项目文件
$phar->convertToData(Phar::ZIP); //另外再提取一份,和上行二选一即可

用浏览器访问这个文件,即可提取出来,以上列子展示了两种提取方式:

La deuxième ligne créera un répertoire composer et y placera le contenu extrait ;

La troisième ligne générera un fichier composer.zip, qui pourra être extrait et restauré en le décompressant.

Quant au contournement de la vérification du serveur provoqué par le pseudo-protocole phar:// :

Apprentissage recommandé : "Tutoriel vidéo PHP"

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer