
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)