php反序列化魔术方法

目录

系列文章

1、php面向对象基本概念、类与对象:http://t.csdnimg.cn/5fRcg

2、序列化与反序列化基础:http://t.csdnimg.cn/cZOZv

一、魔术方法

二、__construct()和__destruct()

1、__construct()

 2、__destruct()

三、__sleep()和__wakeup()

1、__sleep()

2、__wakeup()

四、__toString()和__invoke()

1、__toString()

2、__invoke()

五、错误调用相关魔术方法

1、__call()

2、__callStatic()

3、__get()

4、__set()

5、__isset()

6、__unset()

7、__clone()

总结


靶场地址:GitHub - mcc0624/php_ser_Class: php反序列化靶场课程,基于课程制作的靶场

系列文章

1、php面向对象基本概念、类与对象:http://t.csdnimg.cn/5fRcg

2、序列化与反序列化基础:http://t.csdnimg.cn/cZOZv

一、魔术方法

  • 定义:在php类保留方法中以 “__”两个下划线开头的函数称为魔术方法,是一个预定义好的,在特定情况下自动触发的行为方法。
  • 作用:魔术方法在特定条件下自动调用相关方法,最终导致触发代码
  • 类型:
__construct()    //类的构造函数
__destruct()    //类的析构函数
__call()    //在对象中调用一个不可访问方法时调用
__callStatic()    //用静态方式中调用一个不可访问方法时调用
__get()    //获得一个类的成员变量时调用
__set()    //设置一个类的成员变量时调用
__isset()    //当对不可访问属性调用isset()或empty()时调用
__unset()    //当对不可访问属性调用unset()时被调用
__sleep()    //执行serialize()时,先会调用这个函数
__wakeup()    //执行unserialize()时,先会调用这个函数
__toString()    //类被当成字符串时的回应方法
__invoke()    //调用函数的方式调用一个对象时的回应方法
__set_state()    //调用var_export()导出类时,此静态方法会被调用
__clone()    //当对象复制完成时调用
__autoload()    //尝试加载未定义的类
__debugInfo()    //打印所需调试信息

二、__construct()和__destruct()

1、__construct()

构造函数,在实例化一个对象的时候,首先会去自动执行的一个方法;在序列化和反序列化过程中不会触发;

触发时机:实例化对象        功能:提前清理不必要内容        参数:非必要        返回值:无

<?php
highlight_file(__FILE__);
class User {
    public $username;
    public function __construct($username) {
        $this->username = $username;
        echo "触发了构造函数1次" ;
    }
}
$test = new User("benben");    //触发
$ser = serialize($test);    //不触发
unserialize($ser);    //不触发

?>
-----------------------------------------------
触发了构造函数1次

 2、__destruct()

析构函数,在对象的所有引用被删除或者当对象被显示销毁时执行的魔术方法;在序列化过程中不会触发,在反序列化过程中会触发

注意:反序列化的动作本身是不会触发__destruct()的,会触发第二次的原因是反序列化生成的对象在最后释放时触发了__destruct()

触发时机:对象引用完成,反序列化之后        功能:非必要        参数:无        返回值:无

<?php
highlight_file(__FILE__);
class User {
    public function __destruct()
    {
        echo "触发了析构函数1次"."<br />" ;
    }
}
$test = new User("benben");    //触发
$ser = serialize($test);    //不触发
unserialize($ser);    //触发

?>
-------------------------------------------
触发了析构函数1次
触发了析构函数1次

__destruct()漏洞利用

<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
    var $cmd = "echo 'dazhuang666!!';" ;
    //__destruct()执行eval()
    public function __destruct()    
    {
        eval ($this->cmd);    //eval()触发代码
    }
}
$ser = $_GET["benben"];
unserialize($ser);    //unserialize()触发__destruct()

?>

unserialize()触发__destruct(); __destruct()执行eval();eval()触发代码

 

三、__sleep()和__wakeup()

1、__sleep()

serialize()函数会检查类中是否存在__sleep();如果存在,__sleep()先被调用,然后才执行序列化操作;此方法可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组;如果该方法未返回任何内容,则NULL被序列化,并产生一个E_NOTICE级别的错误。

*E_NOTICE级别参考资料链接:http://t.csdnimg.cn/3QjtQ

触发时机:序列化serialize()之前;       

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

参数:成员属性

返回值:需要被序列化储存的成员属性

<?php
highlight_file(__FILE__);
class User {
    const SITE = 'uusama';    //通常使用const修饰符号限定变量,使得其不能被更改
    public $username;    //username=a
    public $nickname;    //nickname=b
    private $password;    //password=c
    public function __construct($username, $nickname, $password)    {
        $this->username = $username;
        $this->nickname = $nickname;
        $this->password = $password;
    }
    public function __sleep() {
        //参数:return array;返回值:'username','nickname'
        return array('username', 'nickname');    //__sleep()执行返回需要序列化的变量名,过滤掉password变量                                                                              
        //return array('username');                
    }
}
$user = new User('a', 'b', 'c');
var_dump($user);
echo serialize($user);    //serialize()只序列化__sleep()返回的变量
?>
-----------------------------------------------------------------------
object(User)#1 (3) {
    ["username"]=>
    string(1) "a"
    ["nickname"]=>
    string(1) "b"
    ["password":"User":private]=>
    string(1) "c"
}
O:4:"User":2:{s:8:"username";s:1:"a";s:8:"nickname";s:1:"b";} 
//O:4:"User":2:{s:8:"username";s:1:"a";} 

2、__wakeup()

serialize()函数会检查类中是否存在__wakeup();如果存在,则先调用__wakeup()方法,预先准备对象需要的资源;预先准备对象资源,返回void,常用于反序列化操作中重新建立数据库连接或执行其他初始化操作。

触发时机:反序列化unserialize之前

对比:

__wakeup()在反序列化unserialize()之前;

__destruct()在反序列化unserialize()之后;

__sleep()在序列化serialize()之前。

<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
    const SITE = 'uusama';
    public $username;
    public $nickname;
    private $password;
    private $order;
    public function __wakeup() {
        $this->password = $this->username;    //反序列化之前触发wakeup,给password赋值
    }
}
$user_ser = 'O:4:"User":2:{s:8:"username";s:1:"a";s:8:"nickname";s:1:"b";}';
var_dump(unserialize($user_ser));
?>
----------------------------------------------------------------------------
object(User)#1 (4) { 
    ["username"]=> string(1) "a" 
    ["nickname"]=> string(1) "b" 
    ["password":"User":private]=> string(1) "a" 
    ["order":"User":private]=> NULL 
} 

四、__toString()和__invoke()

1、__toString()

表达方式错误导致魔术方法触发;常用于构造pop链

触发时机:把对象当成字符串调用

<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
    var $benben = "this is test!!";
         public function __toString()
         {
             return '格式不对,输出不了!';
          }
}
$test = new User() ;    //把类User实例化为对象
print_r($test);    //调用对象可以使用print_r或var_dump
echo "<br />";    
echo $test;    //使用echo或print只能调用字符串的方式去调用对象,把对象当成字符串使用,此时自动触发__toString()
?>
----------------------------------------------------------
User Object ( [benben] => this is test!! )
格式不对,输出不了!

2、__invoke()

格式表达错误导致魔术方法触发

触发时机:把对象当成函数调用

<?php
highlight_file(__FILE__);
error_reporting(0);
function lin(){
    echo '这是一个函数!';
}
class User {
    var $benben = "this is test!!";
         public function __invoke()
         {
             echo  '这不是个函数!';
          }
}
$test = new User() ;
lin();
echo "<br />";
$test();    //加()是把对象$test当成函数test()来调用,此时触发__invoke()
?>
--------------------------------------------
这是一个函数!
这不是个函数!

五、错误调用相关魔术方法

1、__call()

触发时机:调用一个不存在的方法

参数:调用的不存在的方法的名称;调用的不存在方法的参数

返回值:调用的不存在的方法的名称和参数

<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
    public function __call($arg1,$arg2)    
    {
        echo "$arg1,$arg2[0]";    //$arg1,调用的不存在的方法的名称
          }                       //$arg2,调用的不存在的方法的参数                     
}
$test = new User() ;
$test -> callxxx('a');    //调用的方法callxxx()不存在,触发__call()
?>
-----------------------------------------
callxxx,a    //传参$arg1,$arg2

2、__callStatic()

触发时机:静态调用或调用成员常量时使用的方法不存在

参数:调用的不存在的方法的名称;调用的不存在方法的参数

返回值:调用的不存在的方法的名称和参数

Warning: The magic method __callStatic() must have public visibility and be static in D:\phpstudy_pro\WWW\php-unserialize\class10\2.php on line 5
<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
    public function __callStatic($arg1,$arg2)
    {
        echo "$arg1,$arg2[0]";
          }
}
$test = new User() ;
$test::callxxx('a');    //静态调用::时的方法callxxx()不存在
?>
----------------------------------------------------
callxxx,a 

3、__get()

触发时机:调用的成员属性不存在

参数:传参不存在的方法的名称
返回值:不存在的成员属性名称

<?php
class User {
    public $var1;
    //public $var1 = 'benben';
    public function __get($arg1)
    {
        echo  $arg1;
    }
}
$test = new User() ;
$test ->var2;    //调用的成员属性var2不存在
//echo $test ->var1;
?>
-------------------------------------
var2     //触发__get()把不存在的属性名称var2赋值给$arg1
//benben   

4、__set()

触发时机:给不存在的成员属性赋值

参数:传参不存在的成员属性的名称;给不存在的成员属性的赋值

返回值:不存在的成员属性的名称和赋的值

注意:先触发__get(),再触发__set()

<?php
class User {
    public $var1;
    public function __set($arg1 ,$arg2)
    {
        echo  $arg1.','.$arg2;
    }
}
$test = new User() ;
$test ->var2=1;    //给不存在的成员属性var2赋值为1
?>
-----------------------------------------
var2,1

5、__isset()

触发时机:对不可访问属性(private,protected)使用isset()或empty()时,__isset()会被调用

参数:传参不存在的成员属性的名称

返回值:不存在的成员属性名称

<?php
class User {
    private $var;
    //protected $var;
    public function __isset($arg1 )
    {
        echo  $arg1;
    }
}
$test = new User() ;
isset($test->var);    //isset()调用成员属性var不可访问或不存在
//isset($test->var1);
?>
---------------------------------------
var
//var1

6、__unset()

用法与__isset()相似

触发时机:对不可访问属性使用unset时

参数:传参不存在的成员属性的名称

返回值:不存在的成员属性名称

<?php
class User {
    private $var;
    public function __unset($arg1 )
    {
        echo  $arg1;
    }
}
$test = new User() ;
unset($test->var);    //unset()调用的成员属性var不可访问或不存在
?>
-------------------------------------
var 

7、__clone()

触发时机:当使用clone关键字拷贝完成一个对象后,新对象会自动调用定义的魔术方法__clone()

<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
    private $var;
    public function __clone( )
    {
        echo  "__clone test";
          }
}
$test = new User() ;
$newclass = clone($test)    //使用clone()克隆对象完成后,触发魔术方法__clone()
?>
----------------------------------
__clone test

总结

__construct()__destruct()__sleep()__wakeup()__toString()__invoke()__clone
触发时机实例化对象对象引用完成或对象被销毁;反序列化之后序列化之前反序列化之前把对象当成字符串调用(使用echo或print)把对象当成函数调用当使用clone关键字拷贝完成一个对象
功能提前清理不必要内容对象被序列化之前触发,返回需要被序列化储存的成员属性,删除不必要的属性
参数
返回值需要被序列化的成员属性
__call()__callStatic()__get()__set()__isset()__unset()
触发时机

调用一个不存在的方法

静态调用或调用成员常量时使用的方法不存在

调用的成员属性不存在

给不存在的成员属性赋值

对不可访问属性(private,protected)使用isset()或empty()时,__isset()会被调用

对不可访问属性使用unset时

功能
参数调用的不存在的方法的名称;调用的不存在方法的参数调用的不存在的方法的名称;调用的不存在方法的参数传参不存在的方法的名称传参不存在的成员属性的名称;给不存在的成员属性的赋值传参不存在的成员属性的名称传参不存在的成员属性的名称
返回值调用的不存在的方法的名称和参数调用的不存在的方法的名称和参数不存在的成员属性名称不存在的成员属性的名称和赋的值不存在的成员属性名称不存在的成员属性名称

  • 23
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值