IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> CTF_Web:反序列化学习笔记(一)php中的类与对象 -> 正文阅读

[PHP知识库]CTF_Web:反序列化学习笔记(一)php中的类与对象

0x00 前言

前期第一次遇到反序列化这方面题目的时候,也看了不少资料,都是前辈们写的总结,但是都是直接从在ctf中的运用开始的,自己在这段时间整理的过程中,发现对于php类与对象了解不是很多,导致在看一些题目、或值前辈的总结时都比较困难,下面参考php文档,结合自己对php类与对象的理解先把反序列化的基础知识做一下整理。

0x01 php类与对象

class

在php手册中这样介绍:

每个类的定义都以关键字 class 开头,后面跟着类名,后面跟着一对花括号,里面包含有类的属性与方法的定义。
类名可以是任何非 PHP保留字 的合法标签。在这一点中与变量定义相同,避免歧义。一个合法类名以字母或下划线开头,后面跟着若干字母,数字或下划线。以正则表达式表示为: ^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$。一个类可以包含有属于自己的常量,变量(称为“属性”)以及函数(称为“方法”)。

<?php
class SimpleClass
{
    // 声明属性
    public $var = 'a default value';

    // 声明方法
    public function displayVar() {
        echo $this->var;
    }
}
?>

当一个方法在类定义内部被调用时,有一个可用的伪变量 $this$this是一个到当前对象的引用。此时所有类内的$this都表示当前这个类,访问的对象与方法也是当前类的。例如 $this->var;

new

对定义的类进行创建对应的实例,必须使用new方法,new方法在创建一个实例时,不管以构造函数还是直接传入的方式,其必须为类内的属性赋初始值。当没有参数要传递给类的构造函数,类名后的括号则可以省略掉。
例如对于上面的类:

<?php
$instance = new SimpleClass();
// 也可以这样做:
$className = 'SimpleClass';
$instance = new $className(); // new SimpleClass()
//或不加括号
$instance = new $className;
?>

变量与方法

在php中类的属性和方法存在于不同的“命名空间”中,也就是说一个类的属性和方法可以使用同样的名字。 在类中访问属性和调用方法使用同样的操作符,具体是访问一个属性还是调用一个方法,取决于你的上下文,即用法是变量访问还是函数调用。
例如:

<?php
class Foo
{
    public $bar = 'property';

    public function bar() {
        return 'method';
    }
}

$obj = new Foo();
echo $obj->bar, PHP_EOL, $obj->bar(), PHP_EOL;//第一个访问类属性成员bar,第二个则访问类方法bar()。

属性

类的变量成员叫做“属性”,属性声明是由关键字 publicprotected或者private开头,然后跟一个普通的变量声明来组成。属性中的变量可以初始化,但是初始化的值必须是常数,这里的常数是指 PHP 脚本在编译阶段时就可以得到其值,而不依赖于运行时的信息才能求值。

  • 被定义为公有的类成员可以在任何地方被访问。
  • 被定义为受保护的类成员则可以被其自身以及其子类和父类访问。
  • 被定义为私有的类成员则只能被其定义所在的类访问。

而在类的成员方法里面,可以用 ->(对象运算符):$this->property(其中 property 是该属性名)这种方式来访问非静态属性。静态属性则是用 ::(双冒号):self::$property来访问。**这里的静态,一定是被static声明过的属性。**且::后的$符不可省略。
当调用的方法不是静态时会抛出错误:
Non-static method Foo::bar() should not be called statically

<?php
class Foo
{
    public static $bar = 'property';

}
$obj = new Foo();
echo $obj::$bar;

类常量

可以把在类中始终保持不变的值定义为常量。在定义和使用常量的时候不需要使用 $ 符号。且常量的值必须是一个定值,不能是变量,类属性,数学运算的结果或函数调用。

<?php
class MyClass
{
    const constant = 'constant value';

    function showConstant() {
        echo  self::constant . "\n";
    }
}

echo MyClass::constant . "\n";//注意这里没有$,与上面做对比。

当使用定义的变量$boj->constant时,会返回 Undefined property: MyClass::$constant,可见对象运算符->中不需要$是因为在访问时自动认为属性为变量。

0x02 构造函数与析构函数

具有构造函数的类会在每次创建新对象时先调用此方法,非常适合在使用对象之前做一些初始化工作。其基本格式为:
__construct(mixed ...$values = ""): void
而析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。他的基本格式为:
__destruct(): void
例如:

<?php

class MyDestructableClass 
{
    function __construct() {
        echo "In constructor\n","<br/>";
    }

    function __destruct() {
        print "Destroying " . __CLASS__ . "\n";
    }
}

$obj = new MyDestructableClass();

运行结果为:

In constructor
Destroying MyDestructableClass 

可见在整个脚本结束后析构函数会被自动调用,需要注意的是在使用exit()终止脚本运行时也会被调用。但在析构函数中调用 exit()会中止其余关闭操作的运行。

0x03 PHP魔术方法

PHP将所有以 __(两个下划线)开头的类方法保留为魔术方法。所以在定义类方法时,除了上述魔术方法,建议不要以__为前缀。且所有的魔术方法 必须 声明为 public。其中,魔术方法有:

__construct()
__destruct()
__call()
__callStatic()
__get()
__set()
__isset()
__unset()
__sleep()
__wakeup()
__serialize()
__unserialize()
__toString()
__invoke()
__set_state()
__clone()
__debugInfo()等方法
  • __construct()__destruct()为构造与析构函数,在对象被创建和销毁时调用,上面也已经介绍过了,不再多加介绍。
  • __call()__callStatic()为重载方法时调用,也就是说当调用一个当前类没有的方法时,将会调用他们(静态为callStatic)。

public __call(string $name, array $arguments): mixed·在对象中调用一个不可访问方法时,__call()会被调用。
public static __callStatic(string $name, array $arguments): mixed在静态上下文中调用一个不可访问方法时,__callStatic() 会被调用。

  • __set()、__get()、__isset()和__unset()为重载属性时调用,也就是说在访问、赋值一个不可访问、不存在的属性时,将会调用,这些魔术方法的调用时机就是我们需要掌握的内容。

public __set(string $name, mixed $value): void,在给不可访问(protected 或 private)或不存在的属性赋值时,__set()会被调用。
public __get(string `$name`): mixed,读取不可访问(protected 或 private)或不存在的属性的值时,__get()会被调用。
public __isset(string $name): bool,当对不可访问(protected 或 private)或不存在的属性调用 isset()或 empty() 时,__isset()会被调用。
public __unset(string $name): void,当对不可访问(protected 或 private)或不存在的属性调用 unset() 时,__unset()会被调用。

  • __sleep()、__wakeup()为序列化与反序列化之前调用的函数:

serialize()函数会检查类中是否存在一个魔术方法__sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。
unserialize()会检查是否存在一个__wakeup()方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。__wakeup()经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作。

  • __serialize()、__unserialize()为序列化与反序列化之前调用的函数,与 __sleep()、__wakeup()不同,他们的优先级最高,即如果同时存在__serialize()、__sleep()函数,只会调用__serialize()__sleep()会被忽略。__unserialize()同理。

public __serialize(): arrayserialize()函数会检查类中是否存在一个魔术方法__serialize()。如果存在,该方法将在任何序列化之前优先执行。它必须以一个代表对象序列化形式的 键/值 成对的关联数组形式来返回,如果没有返回数组,将会抛出一个 TypeError 错误。
public __unserialize(array $data): void,相反,unserialize()检查是否存在具有__unserialize()魔术方法。如果存在,该函数将被传递给__serialize()返回的恢复数组。然后,它可以根据需要从该数组中恢复对象的属性。

这两句话好像比上面所有的话都难以理解,简单说就是__serialize()会把要序列化的键+值变成对应的数组返回,这样其实对于最后序列化的字符串来说一目了然,其实就是换了一种表示方式,那么相反的__unserialize就是把需要反序列化的字串值传递给刚刚序列化之前的数组,是一个逆过程。

  • __toString()为一个类被当成字符串时调用,例如echo、print,此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。
  • __invoke()当尝试以调用函数的方式调用一个对象时自动调用。(只在 PHP 5.3.0 及以上版本有效。)

0x04 序列化与反序列化

serialize(mixed $value): stringserialize()返回字符串,此字符串包含了表示 value 的字节流,可以存储于任何地方。

  • 当属性成员为public时,在下面的代码中:
<?php
class MyDestructableClass 
{
    public $a = "123";
	public $b = "456";
}
$obj = new MyDestructableClass();
echo serialize($obj);

返回的字符串格式为:
O:19:"MyDestructableClass":2:{s:1:"a";s:3:"123";s:1:"b";s:3:"456";}
从左至右的字串一次表示为:

序列化对象O、名称长度19、名称内容MyDestructableClass、属性个数2、第一个属性类型s长度1、第一个属性名称a、第一个属性a的值类型s长度3、第一个属性a的值内容123,第二个属性同理。

  • 当其属性成员有private、protect等类型时,会有不同的格式,这也是我们需要注意的:
<?php

class MyDestructableClass 
{
    protected $a = "123";
	private $b = "456";
}

$obj = new MyDestructableClass();
echo serialize($obj);

输出O:19:"MyDestructableClass":2:{s:4:"*a";s:3:"123";s:22:"MyDestructableClassb";s:3:"456";}
可见当类属性的类型不同时序列化的格式也不同,这里需要注意的是:

protected属性序列化的时候格式是%00*%00成员名,所以结果为s:4:"%00*%00a"
private属性序列化的时候格式是%00类名%00成员名,所以结果为s:22:"%00MyDestructableClass%00b"

反序列化就是将上面那段话的意思反过来,生成一个有属性的对象。使用 unserialize()函数。

0x05 小结

对php类与对象学习之后,再对之前遇到的题目进行学习就会知其然也知其所以然,了解各类型魔术方法的调用时机,就能很好的利用好他们,才会有自己的思路,下面将对典型的几道题目进行练习,学习CTF中的这类题目解题套路。

  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2021-08-15 15:18:07  更:2021-08-15 15:18:34 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/15 2:04:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码