
php反序列化
php魔术方法总结
1. 类的构造函数 __construct()
php对象创建后首先被调用的函数。在每一个类中都有一个构造函数,可以自定义。如果没有显示声明它,则会默认存在一个没有参数并且内容为空的构造方法。
- 触发:只有在new一个新的对象的时候会触发,在serialize 序列化和unserialize反序列化中都不会触发
1 2 3
| public function __construct(){ }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php class demo1{ private $k1; public function __construct() { echo("构造函数被调用"."<br>"); } public function f1(){ echo("f1 函数被调用"); } } echo("0000"."<br>"); $f=new demo1(); echo("1111"."<br>"); $a=serialize($f); echo("2222"."<br>"); unserialize($a); ?>
|
2. 析构函数 __destruct()
1 2 3
| public function __destrcut(){ echo("触发") }
|
- 触发:__destruct()在对象被销毁和serialize反序列化的情况下会被触发。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php class demo1{ private $k1; public function __destruct() { echo("析构函数被调用"."<br>"); } } $f=new demo1(); echo("Time_0"."<br>"); $a=serialize($f); echo("Time_1"."<br>"); unset($f); echo("Time_2"."<br>"); unserialize($a); ?>
|

3. __toString
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php header("Content-Type:text/html;charset=utf-8"); highlight_file(__FILE__); class demo1{ private $k1; public function f1(){ echo("f1 函数被调用"); } public function __toString() { echo("__toString 触发"); return ""; } } $f=new demo1(); echo($f); ?>
|

4. __call()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php class demo1{ private $k1; public function f1(){ echo("f1 函数被调用"); } public function __call($name, $arguments) { echo($name."---".$arguments[0]); } } $f=new demo1(); $f->f2("123"); ?>
|

1 2 3 4 5 6 7 8 9 10
| class song{ public $banana; public $abble; public function __toString()//对象当做字符串的时候,会触发__toString { if($this->abble == "abble"){ return $this->banana->ernb(); } } }
|
5. __get()
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?php class demo1{ private $test; public function f1(){ echo("f1 函数被调用"); } public function __get($name)//不存在的变量test会以参数传到$name { echo($name); } } $f=new demo1(); $f->test1; ?>
|

1 2 3 4 5 6 7
| class rap{ public $text; public function __call($name, $arguments)//$test->bucunzai('123','456')调用一个不存在的方法时,会触发__call { return $this->text->aaabbb; } }
|
6. __set()
- 触发:__set()当给一个对象不存在的变量赋值时触发
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?php highlight_file(__FILE__); class test{ private $test1; public function setTest1(){ echo "setTest1函数被调用<br>"; } public function __set($name,$value){ echo($name."---".$value); } } $foo = new test(); $foo->test=17; ?>
|

7. isset()
- 触发:当对不可访问属性调用isset()或empty()时会触发,例如访问类的私有属性,类不存在的成员属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?php header("Content-Type:text/html;charset=utf-8"); highlight_file(__FILE__); class demo1{ private $k1; public function f1(){ echo("f1 函数被调用"); } public function __isset($name) { echo($name); } } $f=new demo1(); $f2=unserialize(serialize($f)); isset($f2->k1); empty($f2->k1); ?>
|

8. unset()
触发:当尝试使用unset() 销毁函数去销毁一个不可访问的成员属性时会触发,不可访问(包括私有成员属性,不存在的成员属性)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?php header("Content-Type:text/html;charset=utf-8"); highlight_file(__FILE__); class demo1{ private $XU17; public function f1(){ echo("f1 函数被调用"); } public function __unset($name) { echo($name); } } $f=new demo1(); $f2=unserialize(serialize($f)); unset($f2->XU17); unset($f2->no_XU17); ?>
|

9. __sleep
- 触发:当对象被serialize 序列化时触发调用__sleep
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php header("Content-Type:text/html;charset=utf-8"); highlight_file(__FILE__); class demo1{ private $k1; public function f1(){ echo("f1 函数被调用"); } public function __sleep() { echo("在被序列化时被调用"); } } $f=new demo1(); echo("00000"."</br>"); serialize($f); ?>
|

10.__wakeup()
- 触发:当进行unserialize 反序列化对象时,
__wakeup魔术方法会被触发,看起来__wakeup与__sleep触发条件是相反的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?php header("Content-Type:text/html;charset=utf-8"); highlight_file(__FILE__); class demo1{ private $k1; public function f1(){ echo("f1 函数被调用"); } public function __wakeup() { echo("在被反序列化时被调用"); } } $f=new demo1(); $uz=serialize($f); echo("00000"."</br>"); unserialize($uz); ?>
|

11. __invoke()
- 触发:当一个对象类中存在
__invoke魔术方法,这个对象类被当作函数进行调用时,就会触发__invoke魔术方法,而不会产生错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php header("Content-Type:text/html;charset=utf-8"); highlight_file(__FILE__); class demo1{ private $k1; public function f1(){ echo("f1 函数被调用"); } public function __invoke() { echo("__invoke 被触发了"); } } $f=new demo1(); $f(); ?>
|

php反序列化
类与对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
class user{ public $username; private $password; protected $userType; public static $platform = "xu17"; public static{ } public login(){ } public logout(){ } }
|
类的定义,可以认为是设计图
实例化一个类,从设计图转换为一个可以用的变量,变量类型不再是字符串或者数字,而是一个对象
1 2 3 4 5 6 7 8 9 10 11 12
| $u = new user();
$p = new user();
$u->username;
$p->username;
public login(){ $this->password; }
|
类和对象的关系,类似于 设计图(蓝图) 与 生产出的产品 之间的关系
属性权限
- public 外部可以通过箭头访问到
- private 内部通过 $this->username 访问到
- protected 表示 自身及其子类 和父类 能够访问
类的继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class normalUser extends user{ public $score; public function play(){ echo $this->userType; } }
$n = new normalUser();
class vipUser extends user{ public $score;
}
class adminUser extends user{ public $score;
}
|
类的属性修饰符
修饰:
静态属性 static
final属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
class user{ public $username; private $password; protected $userType; public static $platform = "xu17"; public final $uuid = "0x36d"; public static{ } public final function login(){ echo $this -> password; } public function logout(){ } } class normalUser extends user{ public $score; public function play(){ echo $this->userType; } }
$n = new normalUser();
echo $n ->uuid; }
|
类的继承
普通用户 vip用户 管理员用户
都属于user类
class normalUser extends user{
public $score;
public function play(){
echo $this->userType;
}
}
$n = new normalUser();
class vipUser extends user{
public $score;
}
class adminUser extends user{
public $score;
}
方法的属性修饰符
public
private
protected
修饰:
静态属性 static
final属性 final
类的分类
1 普通类 没有任何修饰
class user{
public function login(){
}
public function logout(){
}
}
2 抽象类
abstract class user{
public function login(){
}
public function logout(){
}
abstract function play();
}
类里面的方法,有些是有详细的实现,有些就只有个方法名字,没具体实现
抽象类 不能被 new 也就是不能被直接 实例化对象
3 接口 interface
为了实现多继承效果
extends user
implements 可以实现多个接口
4 trait
可以认为是代码段 ,方便复制粘贴
5 匿名类
//一次性匿名类的例子
$v->play(new class{
public $username=”我是匿名类”;
});
//核心思路 更类似于 伪协议的data伪协议
序列化于反序列化
如果属性权限为private,那么序列化后,存储的属性名字为 %00+类名+%00+属性名
如果属性权限为protected,那么序列化后,存储的属性名字为 %00+*+%00+属性名
序列化是 将 一个对象 变为一个可以传输的字符串 serialize(对象) 返回 序列化后的字符串
反序列化 就是将 一个可以传输的字符串 变为一个 可以调用的对象 unserialize(反序列化后的字符串) 返回 对象
字符串
//O:6:" ":1:{s:4:"name";N;}
// O 有一个对象
// : 下一句
// 6 名字是6个字符
// : 下一句
//"Animal" 内容是Animal
// : 下一句
// 1 对象有一个属性
// : 下一句
// { 对象属性描述开始
// s 属性是一个字符串 string
// : 下一句
// 4 属性名字是4个字符
// : 下一句
// "name" 属性名字是name
// ; 属性名字描述完了
// N 没有值,值为null
// ; 属性值描述完了
// } 对象属性描述完了
class Animal{
public $name;
}
//结论
//反序列化和类的方法无关,不能把类方法序列化
//反序列化时,php会这么做
// 1 找到反序列化字符串规定的类名字
// 2 实例化这个类,但是不是调用构造方法
// 3 有了实例化的类对象,对它的属性进行赋值
// 4 执行魔术方法
// 5 返回构造好的对象
1 接口是否可以序列化?
2 匿名类是否可以序列化?
3 trait 是否可以序列化?
魔术方法
总结:
1 魔术方法是一类类的方法
2 会在序列化和反序列化及其他特殊情况下,自动执行
分类:
1 __construct
在实例化一个对象(new)时,会被自动调用
不允许重复声明
可以作为非public权限属性的初始化
2 __sleep 和 __wakeup方法
序列化时自动调用__sleep方法
3 __destruct方法 析构方法
类对象将要销毁,也就是脚本执行完毕后执行清理工作时自动执行
4 __call 和__callstatic
对象执行类不存在的方法时候,会自动调用__call方法
直接执行类的不存在的静态方法时,会自动调用__callstatic方法
5 __get __set 和 __isset __unset魔术方法
__get 对不可访问属性或不存在属性 进行访问引用时自动调用
__set 对不可访问属性或不存在属性 进行写入时自动调用
6 __tostring 方法
类的实例 和字符串进行拼接或者作为字符串引用时,会自动调用__tostring方法
7 __invoke方法
当类的实例被作为函数名字执行的时候,会自动调用__invoke方法
8 __set_state 方法
文档中说 执行 var_export时自动调用
9 __debugInfo 方法的属性修饰符
执行var_dump时自动调用
10 __clone方法
当使用clone关键字 ,clone一个对象时,会自动调用
php的反序列化漏洞
1 有反序列化提交的入口
2 被反序列化的类的魔术方法,有可能被利用
1 绕过__wakeup方法
条件:
1 php5至php5.6.25 之间的版本可以绕过
2 php7到php7.0.10 直接的版本可以绕过
绕过方法:
反序列化字符串中表示属性数量的值 大于 大括号内实际属性的数量时 ,wakeup方法会被绕过
2 绕过 +号正则匹配
参数有过滤,不让输入 O:数字 的形式,试图防止反序列化某个对象
O:数字 改为 O:+数字 就可以绕过上面的O:数字 过滤
3 引用绕过相等
使用&符号表示两个变量指向相同的内存引用地址
4 16进制绕过
反序列化后的字符串 不能出现某个关键单词时,可以使用大S绕过
O:8:"backdoor":1:{s:4:"name";s:10:"phpinfo();";}
O:8:"backdoor":1:{S:4:"n\97me";s:10:"phpinfo();";}
$data='O:8:"backdoor":1:{S:4:"n\97me";s:15:"system(\'calc\');";}';
5 exception 绕过
不影响析构方法执行
6 php反序列化字符逃逸
1 可以控制某个类中的属性值
2 间接控制了某个类的反序列化字符串
3 由于存在无脑过滤,字符增减,造成 描述中字符串的长度 和实际的不一致
4 从而能够逃逸出若干个字符,实现字符可控,从而闭合前面的双引号
5 实现反序列化字符串的完全可控
### Rerference
[1] [https://www.jb51.net/article/266562.htm](https://www.jb51.net/article/266562.htm)