基本概念
PHP中把以两个下划线__开头的方法称为魔术方法,这些方法在PHP中充当了举足轻重的作用,它们服务于类和对象。
出现原因
PHP虽然是面向对象的语言,但是一些面向对象的标准并不完善,例如重载(函数或者方法有同样的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法),我们可以通过一些魔术方法弥补。
魔术方法详细解释
__construct(),类的构造函数 __destruct(),类的析构函数 __call(),在对象中调用一个不可访问方法时调用 __callStatic(),用静态方式中调用一个不可访问方法时调用 __get(),获得一个类的成员变量时调用 __set(),设置一个类的成员变量时调用 __sleep(),执行serialize()时,先会调用这个函数 __wakeup(),执行unserialize()时,先会调用这个函数 __toString(),类被当成字符串时的回应方法 __invoke(),调用函数的方式调用一个对象时的回应方法 __set_state(),调用var_export()导出类时,此静态方法会被调用。 __clone(),当对象复制完成时调用
一、__construct(),类的构造函数、__destruct(),类的析构函数 __construct 构造方法,当一个对象创建时调用此方法,而__destruct()是在一个对象销毁时调用,也就是PHP的gc机制
<?php
class FileRead
{
protected $handle = NULL;
function __construct(){
$this->handle = fopen(...);
}
function __destruct(){
fclose($this->handle);
}
}
?>
二、__call()、__callStatic() 当使用这些未定义的方法时就会进入这两个函数中。比如说我们调用$ a->b();这个方法,但其实在$a的类模板中并没有b()方法,这时就会进入__call()方法进行处理。__callStatic()则是通过静态调用时如果没有定义对应的方法,就进入到__callStatic()方法中,如A::b(),并没有定义b()方法,这时就进入了__callStatic()中进行处理。 三、__set()和__get() __set()和__get()是操作不可访问的属性。注意,这里并不是指没有定义的属性,如果定义为private的属性也可以通过这两个魔术方法来进行定义,当然,也包括未定义的属性。 四、__sleep()和__wakeup() 当我们在执行serialize()和unserialize()时,会先调用这两个函数。例如我们在序列化一个对象时,这个对象有一个数据库链接,想要在反序列化中恢复链接状态,则可以通过重构这两个函数来实现链接的恢复, 这里需要注意的是,__sleep()需要返回一个数组,而这个数组对应着类中的属性名。通常来说,它们可以在序列化前进行数据清理工作,或者反序列化前进行数据的预处理工作。比如序列化前关闭数据库连接或者反序列化前打开数据库连接。
<?php
class Connection
{
protected $link;
private $server, $username, $password, $db;
public function __construct($server, $username, $password, $db)
{
$this->server = $server;
$this->username = $username;
$this->password = $password;
$this->db = $db;
$this->connect();
}
private function connect()
{
$this->link = mysql_connect($this->server, $this->username, $this->password);
mysql_select_db($this->db, $this->link);
}
public function __sleep()
{
return array('server', 'username', 'password', 'db');
}
public function __wakeup()
{
$this->connect();
}
}
?>
五、__toString() __toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。此方法必须返回一个字符串,否则将出现致命错误。
<?php
class TestClass
{
public function __toString() {
return 'this is a object';
}
}
$class = new TestClass();
echo $class;
?>
六、__clone() 此方法在复制对象时被调用。我们知道在php中. $a为一个对象, $b= $a时。 $b为 $ a的引用。当$a发生改变时。 $b也会随之发生变化。那么为了使 $b不发生变化,我们需要用 $b=clone $a; 那么,当 $a在调用clone的时候,引擎会自动调用__clone()方法 七、Invoke 在php中这个方法用于,把对象当方法用的时候。此方法会被调用。
class Invoke {
public function __invoke()
{
echo 'I can run'.PHP_EOL;
}
}
$invoke = new Invoke();
$invoke();
七、__set_state() 了解这个方法前,需要先知道var_export()函数,var_export()和var_dump()类似,输出一个变量的字符串表示。他与var_dump的区别在于它的返回结果的是合法的 php代码.此代码可以被eval执行. 注意:此方法是一个静态方法,且在php5.1以上版本才支持。
绕过__wakeup
一、为什么要绕过__wakeup 举个例子
<?php
include 'flag.php';
error_reporting(0);
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function __wakeup(){
$this->username = 'guest';
}
function __destruct(){
if ($this->password != 100) {
echo "</br>NO!!!hacker!!!</br>";
echo "You name is: ";
echo $this->username;echo "</br>";
echo "You password is: ";
echo $this->password;echo "</br>";
die();
}
if ($this->username === 'admin') {
global $flag;
echo $flag;
}else{
echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
die();
}
}
}
我们可知__wakeup将username的值设定为guest,题中需要为admin,而且根据__wake的特性,在反序列化中会提前执行,所以需要绕过 二、绕过方法:当成员属性数目大于实际数目时可绕过 题目来源:buuctf_php
|