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知识库 -> PHP 的 HOOK 实现原理 -> 正文阅读

[PHP知识库]PHP 的 HOOK 实现原理

PHP HOOK OPCode 是通过

int zend_set_user_opcode_handler(zend_uchar opcode, opcode_handler_t handler)

接口替换了 PHP 内置的 OPCode 的 handler (函数指针),那 PHP 是如何执行到我们替换后的 handler 的呢?

一、zend_user_opcodes 和 zend_user_opcode_handlers

以 PHP 5.1 版本为例,zend_set_user_opcode_handler() 接口的实现如下:

ZEND_API int zend_set_user_opcode_handler(zend_uchar opcode, opcode_handler_t handler)
{
??? if (opcode != ZEND_USER_OPCODE) {
??????? zend_user_opcodes[opcode] = ZEND_USER_OPCODE;
??????? zend_user_opcode_handlers[opcode] = handler;
??????? return SUCCESS;
??? }
??? return FAILURE;
}

这里面用到了两个静态数组 zend_user_opcodes 和 zend_user_opcode_handlers,这两个数组的声明和初始化是由 zend_vm_gen.php 脚本根据 zend_vm_def.h 生成的,具体生成过程可参考

PHP内核生成zend_vm_opcodes.h - 知乎 (zhihu.com)

生成的 zend_cm_execute.h 文件里有这两个数组的声明和初始化,如下:

static opcode_handler_t zend_user_opcode_handlers[256] = {(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL};
?
static zend_uchar zend_user_opcodes[256] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255};

两个数组的下标代表 opcode 的值,当我们调用 zend_set_user_opcode_handler 时,除了会改变该 opcode 对应的 zend_user_opcode_handlers 的值,对应 opcode 的 zend_user_opcodes 的数组值会全部变成150(ZEND_USER_OPCODE 对应的值)。

二、zend_opcode_handlers

由 zend_vm_gen.php 生成的 zend_vm_execute.h 文件里还有个 zend_opcode_handlers 数组,这个数组的初始化如下:

void zend_init_opcodes_handlers()
{
? static const opcode_handler_t labels[] = {
??? ZEND_NOP_SPEC_HANDLER,
??? ZEND_NOP_SPEC_HANDLER,
??? ...
??? ZEND_ADD_SPEC_CONST_CONST_HANDLER,
??? ZEND_ADD_SPEC_CONST_TMP_HANDLER,
 ???ZEND_ADD_SPEC_CONST_VAR_HANDLER,
??? ZEND_NULL_HANDLER,
??? ZEND_ADD_SPEC_CONST_CV_HANDLER,
??? ZEND_ADD_SPEC_TMP_CONST_HANDLER,
??? ZEND_ADD_SPEC_TMP_TMP_HANDLER,
??? ZEND_ADD_SPEC_TMP_VAR_HANDLER,
??? ZEND_NULL_HANDLER,
??? ZEND_ADD_SPEC_TMP_CV_HANDLER,
??? ZEND_ADD_SPEC_VAR_CONST_HANDLER,
??? ZEND_ADD_SPEC_VAR_TMP_HANDLER,
??? ...
};
??? zend_opcode_handlers = (opcode_handler_t*)labels;
}

zend_opcode_handlers 包含有 3776 个 handlers,这个数字时怎么来的呢?

PHP 的 handler 是每条 opcode 对应的 C 语言编写的处理逻辑,我们可以在每个 opcode 对应一个 handler,在一个 handler 里对两个操作数的所有类型进行处理(PHP 5.0 的处理方式),也可以根据两个操作数的类型,直接定义不同的 handler 函数,而 PHP 5.0 以上的版本采取的就是后面的方式,以 PHP 5.1 为例,每个操作数有 5 种类型,两个操作数两两组合,就有 25 种可能,所以每个 opcode 对应 25 个handler,每个handler的命令方式就变成了{OPCode_name}_SPEC_{OP1_TYPE}_{OP2_TYPE}, 一共 151 个 opcode,对应 151 * 25 = 3775 个 handler,最后数组加了一个空 handler,所以一共 3776 个 handlers。

三、zend_opcode_handlers 和 zend_user_opcode_handlers 映射关系

zend_opcode_handlers 里一已经包含了所有 opcode 的 handler,但是怎么找到 opcode 对应的 handler 呢?

这部分代码如下:

static opcode_handler_t zend_vm_get_opcode_handler(zend_uchar opcode, zend_op* op)
{
??????? static const int zend_vm_decode[] = {
??????????? _UNUSED_CODE, /* 0????????????? */
??????????? _CONST_CODE,? /* 1 = IS_CONST?? */
??????????? _TMP_CODE,??? /* 2 = IS_TMP_VAR */
??????????? _UNUSED_CODE, /* 3????????????? */
??????????? _VAR_CODE,??? /* 4 = IS_VAR???? */
??????????? _UNUSED_CODE, /* 5????????????? */
??????????? _UNUSED_CODE, /* 6????????????? */
??????????? _UNUSED_CODE, /* 7????????????? */
??????????? _UNUSED_CODE, /* 8 = IS_UNUSED? */
??????????? _UNUSED_CODE, /* 9????????????? */
??????????? _UNUSED_CODE, /* 10???????????? */
??????????? _UNUSED_CODE, /* 11???????????? */
??????????? _UNUSED_CODE, /* 12???????????? */
??????????? _UNUSED_CODE, /* 13???????????? */
??????????? _UNUSED_CODE, /* 14???????????? */
??????????? _UNUSED_CODE, /* 15???????????? */
??????????? _CV_CODE????? /* 16 = IS_CV?? ??*/
??????? };
??????? return zend_opcode_handlers[opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]];
}
?
ZEND_API void zend_vm_set_opcode_handler(zend_op* op)
{
??? op->handler = zend_vm_get_opcode_handler(zend_user_opcodes[op->opcode], op);
}
?

加入我们调用 zend_set_user_opcode_handler(ZEND_ADD, handler),对 ZEND_ADD(对应的值为 1),则 zend_user_opcodes[1] = 150,这个 opcode 的 handler 会通过 zend_vm_get_opcode_handler(zend_user_opcodes[op->opcode], op); 设置,此时相当于调用 zend_vm_get_opcode_handler(150, op);

而zend_vm_get_opcode_handler里的zend_vm_decode数组代表操作数的类型,只有五种类型生效,对应的值也是0~ 4,最后通过opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]

这个下标即可找到 opcode 对应的 handler, 假如两个操作数类型都为 const,可计算出下标值为 150 * 25 + 0 * 5 + 0 = 3750,对应的 handler 为 ZEND_USER_OPCODE_SPEC_HANDLER其实?? ZEND_USER_OPCODE 这个 opcode 的25 个handler 都是 ZEND_USER_OPCODE_SPEC_HANDLER,具体实现为:

static int ZEND_USER_OPCODE_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{?? 
????int ret = zend_user_opcode_handlers[EX(opline)->opcode](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_INTERNAL);
?
??? switch (ret) {
??????? case ZEND_USER_OPCODE_CONTINUE:
??????????? ZEND_VM_CONTINUE();
??????? case ZEND_USER_OPCODE_RETURN:
??????????? ZEND_VM_RETURN();
??????? case ZEND_USER_OPCODE_DISPATCH:
??????????? ZEND_VM_DISPATCH(EX(opline)->opcode, EX(opline));
??????? default:
??????????? ZEND_VM_DISPATCH(ret & 0xff, EX(opline));
??? }
}

可以看到里面执行了该 opcode 对应的 handler,也就是我们用 zend_set_user_handler 设置的 handler。

  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2022-02-22 20:20:15  更:2022-02-22 20:20:44 
 
开发: 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年11日历 -2024/11/23 11:01:18-

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