php反序列化魔术方法详解
php反序列化
php魔术方法总结
1. 类的构造函数 __construct()
php对象创建后首先被调用的函数。在每一个类中都有一个构造函数,可以自定义。如果没有显示声明它,则会默认存在一个没有参数并且内容为空的构造方法。
- 触发:只有在new一个新的对象的时候会触发,在serialize 序列化和unserialize反序列化中都不会触发
1 |
|
1 |
|
2. 析构函数 __destruct()
1 |
|
- 触发:__destruct()在对象被销毁和serialize反序列化的情况下会被触发。
1 |
|
3. __toString
触发:当对象类被当做字符串处理时,会触发__destruct()
一般做题目时,基本是
__destruct()
析构函数中有echo
,打印字符串,链子触发__destruct()
1 |
|
4. __call()
1 |
|
- 比如楚慧杯决赛的这里
1 |
|
5. __get()
- 触发:访问一个对象不存在的变量
1 |
|
- 在pop链题目中如下
1 |
|
6. __set()
- 触发:__set()当给一个对象不存在的变量赋值时触发
1 |
|
7. isset()
- 触发:当对不可访问属性调用isset()或empty()时会触发,例如访问类的私有属性,类不存在的成员属性
1 |
|
8. unset()
触发:当尝试使用unset() 销毁函数去销毁一个不可访问的成员属性时会触发,不可访问(包括私有成员属性,不存在的成员属性)
1 |
|
9. __sleep
- 触发:当对象被serialize 序列化时触发调用__sleep
1 |
|
10.__wakeup()
- 触发:当进行unserialize 反序列化对象时,
__wakeup
魔术方法会被触发,看起来__wakeup
与__sleep
触发条件是相反的
1 |
|
11. __invoke()
- 触发:当一个对象类中存在
__invoke
魔术方法,这个对象类被当作函数进行调用时,就会触发__invoke
魔术方法,而不会产生错误
1 |
|
php反序列化
类与对象
1 |
|
类的定义,可以认为是设计图
实例化一个类,从设计图转换为一个可以用的变量,变量类型不再是字符串或者数字,而是一个对象
1 |
|
类和对象的关系,类似于 设计图(蓝图) 与 生产出的产品 之间的关系
属性权限
- public 外部可以通过箭头访问到
- private 内部通过 $this->username 访问到
- protected 表示 自身及其子类 和父类 能够访问
类的继承
- 三种用户
1 |
|
类的属性修饰符
修饰:
静态属性 static
final属性
1 |
|
类的继承
普通用户 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)