php反序列化魔术方法详解

php

php反序列化

php魔术方法总结

1. 类的构造函数 __construct()

php对象创建后首先被调用的函数。在每一个类中都有一个构造函数,可以自定义。如果没有显示声明它,则会默认存在一个没有参数并且内容为空的构造方法。

  • 触发:只有在new一个新的对象的时候会触发,在serialize 序列化和unserialize反序列化中都不会触发
1
2
3
public function __construct(){
//需要执行的操作,比如对参数初始化
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
class demo1{
private $k1;
public function __construct()
{
echo("构造函数被调用"."<br>");
}
public function f1(){
echo("f1 函数被调用");
}
}
echo("0000"."<br>");
$f=new demo1();
echo("1111"."<br>");
$a=serialize($f);
echo("2222"."<br>");
unserialize($a);
?>

2. 析构函数 __destruct()

1
2
3
public function __destrcut(){
echo("触发")
}
  • 触发:__destruct()在对象被销毁和serialize反序列化的情况下会被触发。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class demo1{
private $k1;
public function __destruct()
{
echo("析构函数被调用"."<br>");
}
}
$f=new demo1();
echo("Time_0"."<br>");
$a=serialize($f);
echo("Time_1"."<br>");
unset($f);
echo("Time_2"."<br>");
unserialize($a);
?>

image-20250116190341455

3. __toString

  • 触发:当对象类被当做字符串处理时,会触发__destruct()

  • 一般做题目时,基本是__destruct()析构函数中有echo,打印字符串,链子触发__destruct()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
header("Content-Type:text/html;charset=utf-8");
highlight_file(__FILE__);
class demo1{
private $k1;
public function f1(){
echo("f1 函数被调用");
}
public function __toString()
{
echo("__toString 触发");
return "";
}
}
$f=new demo1();
echo($f);
?>

image-20250116190942031

4. __call()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class demo1{
private $k1;
public function f1(){
echo("f1 函数被调用");
}
//当调用不存在的方法时,方法名作为参数传到$name 变量,方法名的输入参数传到arguments参数列表中
public function __call($name, $arguments)
{
// TODO: Implement __call() method.
echo($name."---".$arguments[0]);
}
}
$f=new demo1();
$f->f2("123");//调用不存在的方法f2()
?>

image-20250116191453469

  • 比如楚慧杯决赛的这里
1
2
3
4
5
6
7
8
9
10
class song{
public $banana; //banana 可控,让他指向的方法ernb()不存在,触发__call
public $abble;
public function __toString()//对象当做字符串的时候,会触发__toString
{
if($this->abble == "abble"){
return $this->banana->ernb();//banana 可控,让他指向的方法ernb()不存在,触发__call
}
}
}

5. __get()

  • 触发:访问一个对象不存在的变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class demo1{
private $test;
public function f1(){
echo("f1 函数被调用");
}
public function __get($name)//不存在的变量test会以参数传到$name
{
echo($name);
}
}
$f=new demo1();
$f->test1;//不存在的变量test
?>

image-20250116191801591

  • 在pop链题目中如下
1
2
3
4
5
6
7
class rap{
public $text;
public function __call($name, $arguments)//$test->bucunzai('123','456')调用一个不存在的方法时,会触发__call
{
return $this->text->aaabbb; //text 可控,让他指向的属性aaabbb不存在,触发__get
}
}

6. __set()

  • 触发:__set()当给一个对象不存在的变量赋值时触发
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
highlight_file(__FILE__);
class test{
private $test1;
public function setTest1(){
echo "setTest1函数被调用<br>";
}
public function __set($name,$value){
echo($name."---".$value);
}
}
$foo = new test();
$foo->test=17;
?>

image-20250116200420508

7. isset()

  • 触发:当对不可访问属性调用isset()或empty()时会触发,例如访问类的私有属性,类不存在的成员属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
header("Content-Type:text/html;charset=utf-8");
highlight_file(__FILE__);
class demo1{
private $k1;
public function f1(){
echo("f1 函数被调用");
}
public function __isset($name)
{
echo($name);
}
}
$f=new demo1();
$f2=unserialize(serialize($f));//反序列化
isset($f2->k1);//使用isset方法判断私有成员属性k1
empty($f2->k1);//使用empty方法判断私有成员属性k1
?>

image-20250116200633462

8. unset()

触发:当尝试使用unset() 销毁函数去销毁一个不可访问的成员属性时会触发,不可访问(包括私有成员属性,不存在的成员属性)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
header("Content-Type:text/html;charset=utf-8");
highlight_file(__FILE__);
class demo1{
private $XU17;
public function f1(){
echo("f1 函数被调用");
}
public function __unset($name)
{
echo($name);
}
}
$f=new demo1();
$f2=unserialize(serialize($f));//反序列化
unset($f2->XU17);//使用unset销毁私有成员属性k1
unset($f2->no_XU17);//使用unset销毁不存在的成员属性faaa
?>

image-20250116200934179

9. __sleep

  • 触发:当对象被serialize 序列化时触发调用__sleep
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
header("Content-Type:text/html;charset=utf-8");
highlight_file(__FILE__);
class demo1{
private $k1;
public function f1(){
echo("f1 函数被调用");
}
public function __sleep()
{
echo("在被序列化时被调用");
}
}
$f=new demo1();
echo("00000"."</br>");
serialize($f);
?>

image-20250116201553222

10.__wakeup()

  • 触发:当进行unserialize 反序列化对象时,__wakeup魔术方法会被触发,看起来__wakeup__sleep触发条件是相反的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
header("Content-Type:text/html;charset=utf-8");
highlight_file(__FILE__);
class demo1{
private $k1;
public function f1(){
echo("f1 函数被调用");
}
public function __wakeup()
{
echo("在被反序列化时被调用");
}
}
$f=new demo1();
$uz=serialize($f);
echo("00000"."</br>");
unserialize($uz);
?>

image-20250116201808629

11. __invoke()

  • 触发:当一个对象类中存在__invoke魔术方法,这个对象类被当作函数进行调用时,就会触发__invoke魔术方法,而不会产生错误
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
header("Content-Type:text/html;charset=utf-8");
highlight_file(__FILE__);
class demo1{
private $k1;
public function f1(){
echo("f1 函数被调用");
}
public function __invoke()
{
echo("__invoke 被触发了");
}
}
$f=new demo1();
$f();
?>

image-20250116201853179

Rerference

[1] https://www.jb51.net/article/266562.htm


php反序列化魔术方法详解
https://xu17.top/2025/01/16/php反序列化/
作者
XU17
发布于
2025年1月16日
许可协议
XU17