绕过 PHP 模块的 API 兼容性检查之实践

博客是用 WordPress 搭建的,最新版的 WordPress 有个功能是健康度检查,报告有一个问题为 PHP 版本过低。实话说并不算低,最新是 7.4 而我用的是 7.2。但看着总是不顺眼,于是安装了 PHP 7.4,准备迁移。

安装好之后,没有动搏客,而是用另一个虚拟站点做测试,加载页面直接显示“Mcrypt PHP extension required.”

这个 mcrypt 库对于很多 PHP 用户是个爱憎交加的玩意儿,从 7.1 以后官方就不再支持了,但会有不少老的开发库依赖它。其实目前的 PHP 7.2 能够正常运行也是做了额外的努力才安装上的。作为一个懒懒的程序员,本能上极其抗拒再大动干戈来一遍,思忖可以和当前的 PHP 7.2 共享。

于是把 /etc/php/7.2/cli/conf.d/20-mcrypt.ini7.4 的相应路径下复制了一份,fpm 下的也同样处理。service php7.4-fpm restart 后刷新页面,错误仍在。

只好执行 php7.4 -v 查看,发现报一个警告如下:

PHP Warning: PHP Startup: Unable to load dynamic library ‘mcrypt.so’ (tried: /usr/lib/php/20190902/mcrypt.so (/usr/lib/php/20190902/mcrypt.so: cannot open shared object file: No such file or directory), /usr/lib/php/20190902/mcrypt.so.so (/usr/lib/php/20190902/mcrypt.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0

作为一个聪明的程序员,这也难不住咱。到 /usr/lib/php 下查看,另有一个目录名为 20170718,其中就有 mcrypt.so 文件,显然是 7.2 所用的,复制一份到 20190902 目录好了。

不料还不成,又报以下错:

PHP Warning: PHP Startup: mcrypt: Unable to initialize module
Module compiled with module API=20170718
PHP compiled with module API=20190902
These options need to match
in Unknown on line 0

看来文件里自带了硬编码的版本兼容信息,用 stings mcrypt.so | grep 2017 得到如下结果:

API20170718,NTS
/usr/include/php/20170718/Zend
/usr/include/php/20170718/main
/usr/include/php/20170718/main/streams
/usr/include/php/20170718/ext/standard
/usr/include/php/20170718/Zend
/usr/include/php/20170718/main
/usr/include/php/20170718/main/streams

推测第一行就是关键所在。作为一个坏坏的程序员,俺直觉把这个字符串改了就可以。

最简单的办法是,scp 把文件拉回本地,用一个十六进制编辑器改掉后再推到远程。但作为一个时不时挑战自我的程序员,就地在主机上解决掉问题才有成就感。用哪一套组合拳呢?

首先当然要知道那个 API 字串的便宜,直觉又一次灵验,看了看 strings 命令的帮助,发现加上 -td 的参数(d 意为十进制,ox 也可以,对应八进制和十六进制)即可,偏移值为 30041

其次是要想办法把新串覆盖进去。这种指定位置覆盖内容的活儿,dd 命令显然是首选:
printf 'API20190902' | dd of=mcrypt.so seek=30041 bs=1 count=11 conv=notrunc

执行完毕后使用 dhex 工具进行了文件的比较确认,修补成功。但用 php -v 验证,API 不兼容的错误信息依旧,应该是在其他地方还有需要处理的细节,本次实践暂告失败,但似乎也并非全无收获。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注