魔术方法构造和析构

什么是魔术方法

一个预定义好的,在特定情况下自动触发的行为方法

魔术方法的作用

反序列化漏洞的成因:反序列化过程中,unserialize()接收的值(字符串)可控;通过修改这个值(字符串),得到所需要的代码;通过调用方法,触发代码执行。

魔术方法在特定条件下自动调用相关方法,最终导致触发代码

相关机制

  1. 触发时机(动作不同,触发的魔术方法也不同)
  2. 功能
  3. 参数(一些特殊魔术方法会传参)
  4. 返回值

魔术方法

image-20250411104653627

__construct()

触发时机:

构造函数,在实例化一个对象的时候,首先会去自动执行的一个方法;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
highlight_file(__FILE__);
class User {
public $username;
public function __construct($username) {
$this->username = $username;
echo "触发了构造函数1次" ;
}
}
$test = new User("benben"); //实例化对象时触发构造函数_construct()
$ser = serialize($test);
unserialize($ser);

?>

__destruct()

触发时机

析构函数,在对象的所有引用被删除或者当对象被显式销毁时执行的魔术方法

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
highlight_file(__FILE__);
class User {
public function __destruct()
{
echo "触发了析构函数1次"."<br />" ;
}
}
$test = new User("benben"); //实例化对象结束后,代码运行完会销毁,触发析构函数__destruct()
$ser = serialize($test); //无法触发
unserialize($ser); // 销毁对象,再次触发析构函数__destruct()

?>

?benben=O:4:"User":1:{s:3:"cmd";s:13:"system("id");";}

__sleep()

触发时机

序列化serialize()函数会检查类中是否存在魔术方法__sleep()

如果存在,该方法会先被调用,然后猜执行序列化操作

此功能可以用于清理对象, 并返回一个包含对象中所有应被序列化的变量名称的数组。

如果该方法未返回任何内容,则NULL被序列化,并产生一个E_NOTICE级别的错误

功能

对象被序列化被触发,返回需要被序列化存储的成员属性,删除不必要的属性

参数

成员属性

返回值

需要被序列化存储的成员属性

例题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
highlight_file(__FILE__);
class User {
const SITE = 'uusama';
public $username;
public $nickname;
private $password;
public function __construct($username, $nickname, $password) {
$this->username = $username;
$this->nickname = $nickname;
$this->password = $password;
}
public function __sleep() {
return array('username', 'nickname');
}
}
$user = new User('a', 'b', 'c'); //触发__construct,接收参数
echo serialize($user); //先于serialize()之前触发,直接return
?>

O:4:"User":2:{s:8:"username";s:1:"a";s:8:"nickname";s:1:"b";}