目录
1、php面向对象基本概念、类与对象:http://t.csdnimg.cn/5fRcg
2、序列化与反序列化基础:http://t.csdnimg.cn/cZOZv
靶场地址: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