【问题标题】:Can PHP7 opcached files be distributed without source code?PHP7 opcached 文件可以在没有源代码的情况下分发吗?
【发布时间】:2017-04-01 17:19:08
【问题描述】:

PHP7 提供了一种称为opcache 的字节码缓存机制。我想知道是否有任何方法可以分发和运行 PHP 脚本的“opcached”版本(.bin 文件扩展名)而不分发其源代码。 (我在php.ini中启用了opcache.file_cache指令来获取.bin文件。)

我假设在执行脚本时,PHP7 将检查 opcache 目录中是否存在具有匹配名称、时间戳的 .bin 文件,甚至可能会比较校验和或哈希值。如果一切都匹配,PHP7 将执行 .bin 文件而不是解析 .php 文件。即使相应的 .php 脚本不存在,是否也可以“欺骗”PHP 执行 .bin 文件?

【问题讨论】:

  • 有趣。我认为可以。 gosecure.net/2016/04/27/…
  • 你试过了吗?
  • 在 7 中不是新的,它是在 5 中。重要的问题是你为什么要这样做?

标签: php php-7 opcache


【解决方案1】:

PHP 需要能够打开文件才能调用 opcache;如果不存在,则无法加载...

让我们详细look,看看我们可能会玩什么花样:

if (!file_handle->filename || !ZCG(enabled) || !accel_startup_ok) {
    /* The Accelerator is disabled, act as if without the Accelerator */
    return accelerator_orig_compile_file(file_handle, type);
#ifdef HAVE_OPCACHE_FILE_CACHE
} else if (ZCG(accel_directives).file_cache_only) {
    return file_cache_compile_file(file_handle, type);
#endif
} else if ((!ZCG(counted) && !ZCSG(accelerator_enabled)) ||
           (ZCSG(restart_in_progress) && accel_restart_is_active())) {
#ifdef HAVE_OPCACHE_FILE_CACHE
    if (ZCG(accel_directives).file_cache) {
        return file_cache_compile_file(file_handle, type);
    }
#endif
    return accelerator_orig_compile_file(file_handle, type);
}

我们可以看到启用文件缓存的地方,它优先于共享内存缓存。

接下来,我们要看file_cache_compile_file

  1. block signals
  2. protect shared memory
  3. zend_file_cache_script_load

现在我们看看zend_file_cache_script_load:

  1. open
  2. read header (layout)
  3. verify magic "OPCACHE"
  4. verify system id
  5. optionally validate timestamp
  6. perform read of cached file
  7. verify checksum

所以我们遇到的第一个问题是system id 不是唯一的,而是由以下元素组成:

  1. PHP 版本
  2. Zend 扩展构建标识符
  3. 二进制标识符,包含以下内容:
    1. sizeof(char)
    2. sizeof(int)
    3. sizeof(long)
    4. sizeof(size_t)
    5. sizeof(zend_long)
    6. ZEND_MM_ALIGNMENT
  4. 如果不使用开发版 PHP(未发布,来自 git):
    1. ___DATE__二进制编译日期
    2. ___TIME___二进制编译时间

需要 PHP 版本和构建标识符,因为至少以下内容可能会在版本或构建之间发生变化:

  • 操作码的整体标识符
  • 内部结构布局
  • VM 期望的指令序列(现有控制结构的细节可能会改变 fe.foreach)
  • opcache 执行的优化(因为之前的优化可能会被发现是不安全的)

二进制标识符是必需的,因为至少 zval 的布局会随着字节序和架构而变化:架构可能会影响一些基本编译器类型(long、size_t 等)的大小以及上限和下限在这些类型中,字节序会影响结构中成员的顺序,以及基本编译器类型的二进制表示。

请注意,识别当前系统花费了相当多的精力,这应该让您停下来思考......

禁用时间戳opcache.validate_timestamps=0 的验证将允许加载文件缓存条目,即使文件系统上的当前文件为空。

标头中包含的校验和仅用于验证文件的脚本部分(位于标头之后),它不(也不能)包含系统标识符的标头或写入校验和本身.

因此,您可以通过将缓存文件的header 中的系统标识符更改为与目标机器标识符相对应来欺骗 PHP 从另一台机器加载缓存文件。

你应该吗?

也许是为了好玩,但作为部署软件的一种方法,绝对不是

文件缓存不适用于此目的,从不同架构和/或构建加载缓存会导致 PHP 崩溃。

【讨论】:

  • 感谢您的详细细分! opcache.validate_timestamps=0 和一个长度为零的 .php 文件一起完成了将字节码复制到另一台机器的技巧(假设两台机器都有相同的 arch 和 PHP 版本)。我知道这不适合“生产”。有没有其他方法来部署字节码?也许这是softwarerecs的东西。
  • 如果要部署混淆代码,请使用混淆纯 PHP 代码的工具。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-09
  • 1970-01-01
相关资源
最近更新 更多