首页 > 编程语言 >php反序列化魔术方法

php反序列化魔术方法

时间:2024-03-26 15:30:03浏览次数:27  
标签:__ 触发 存在 调用 魔术 序列化 php 属性

目录

系列文章

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

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

一、魔术方法

二、__construct()和__destruct()

1、__construct()

 2、__destruct()

三、__sleep()和__weakup()

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()和__weakup()

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时

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

 

标签:__,触发,存在,调用,魔术,序列化,php,属性
From: https://blog.csdn.net/2301_80913334/article/details/136880866

相关文章

  • 使用PHP语言, 如何 只需一步调用,创建支付宝代扣
    使用php语言,如何只需一步调用,创建支付宝代扣  目标原理"#alilite_php"<?phprequire_once'GatewaySdk.php';$appId="999999";//AppID向客户经理申请$sdk=newGatewaySdk($appId);$payload=['timestamp'=>1711347......
  • 【lnmp一键安装】--php环境
    1.官网:lnmp.org 一、一键安装wgethttps://soft.lnmp.com/lnmp/lnmp2.0.tar.gz-Olnmp2.0.tar.gz&&tarzxflnmp2.0.tar.gz&&cdlnmp2.0&&./install.sh lnmp 二、若是已经安装过,如何新增PHP版本呢?1.执行命令:wgethttps://soft.lnmp.com/lnmp/lnmp2.0.tar.gz-Ol......
  • thinkphp导出word【 直接生成word文件 】
    PHPWord中文文档  使用composer安装PHPword:composerrequirephpoffice/phpword直接生成word文件:publicfunctiontest(){$phpWord=new\PhpOffice\PhpWord\PhpWord();//设置默认样式$phpWord->setDefaultFontName('宋体');//字体......
  • 2024西湖论剑-phpems-代码审计
    前言2024西湖论剑数据安全题,太菜了当时没看明白,系统是phpems,修改了默认密码,需要利用CVE登上去CVE-2023-6654,菜鸟学习,大佬多指点0x01环境搭建https://phpems.net/index.php 源码config.inc.php修改相应数据库配置数据库运行pe9.sql文件建立数据库0x02代码审计根据题目......
  • thinkphp添加request()->only(['user_id'=>0]);
    某些版本的thinkphp没有request()->only(['user_id'=>0]);只需要项目目录下的thinkphp/library/think/Request.php文件;找到only方法,重新改写为即可publicfunctiononly($name,$type='param'){$param=self::$type();if(is_strin......
  • day1:PHP语言基础、变量和常量
    一、PHP语言基础一种创建动态交互性站点的、强有力的服务器端脚本语言。1.PHP文件格式一个完整的PHP文件由HTML标记、PHP标记、PHP代码、注释和空格等元素构成。<Html><head><title>HelloWorld!</title></head><body><?php //输出Hello,World echo"Hello,World";......
  • Ajax 发送json格式数据以及发送文件(FormData)和自带的序列化组件: serializers
    前后端传输数据的编码格式(contentType)get请求数据就是直接放在url?后面的url?usernmae=junjie&password=123...可以向后端发送post请求的方式form请求ajax请求前后端传输数据的编码格式urlencodedformdatajson研究form表单:默认的数据编码格式是(urlencod......
  • drf : 序列化类使用many参数的作用,源码解析
    序列化类使用many参数的作用views.pyfromrest_framework.viewsimportAPIViewfrom.serizlizerimportBookSerializersfromrest_framework.responseimportResponsefrom.modelsimportBooksclassBookView(APIView):defpost(self,request):print(r......
  • drf : 模型类序列化器 以及扩展用法。
    模型类序列化器:serializer的升级。注意,此时表模型自身的校验规则也将映射过来。只需要在serializers中写一个模型类序列化器即可。serializer.py#模型类序列化器#此序列化类和表模型有对应关系,映射classPublishModelSerializer(serializers.ModelSerializer):class......
  • drf : source,定制序列化字段以及反序列化新增。局部钩子(validate_字段名),全局钩子(va
    source,SerializerMethodField,局部钩子,全局钩子serialzer.py:source用处对应字段:起别名,用处2对应方法:在表模型中定义一个方法,source可以与其关联用处3对应方法:可以当做字段第三种方法的扩展用法:使用程度高。model.pyfromdjango.dbimportmodels#Createyourmo......