可以在没有源代码的情况下分发PHP7 opcached文件吗?

das*_*sup 9 php opcache php-7

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

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

Joe*_*ins 11

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

让我们仔细看看,看看我们可能会玩什么技巧:

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);
}
Run Code Online (Sandbox Code Playgroud)

我们可以看到启用文件缓存的位置,它优先于共享内存缓存.

接下来,我们要查看file_cache_compile_file:

  1. 阻止信号
  2. 保护共享内存
  3. zend_file_cache_script_load

现在我们来看看zend_file_cache_script_load:

  1. 打开
  2. 读标题(布局)
  3. 验证魔法"OPCACHE"
  4. 验证系统ID
  5. 可选地验证时间戳
  6. 执行缓存文件的读取
  7. 验证校验和

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

  1. PHP版本
  2. Zend Extension构建标识符
  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的布局随着endianess和体系结构而变化:体系结构可能影响一些基本编译器类型(long,size_t等)的大小以及这些类型的上限和下限,而endianess可以影响结构中成员的顺序,以及基本编译器类型的二进制表示.

请注意,相当多的努力用于识别当前系统,这应该让你暂停思考......

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

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

因此,您可以通过更改缓存文件标头中的系统标识符以与目标计算机标识符相对应,欺骗PHP从另一台计算机加载缓存文件.

你应该 ?

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

文件缓存不是为此目的,从不同的体系结构和/或构建加载缓存会使PHP崩溃.