如何使用PHP Embed SAPI实现Opcodes查看器


Posted in PHP onNovember 10, 2015

PHP提供了一个Embed SAPI,也就是说,PHP容许你在C/C++语言中调用PHP/ZE提供的函数。本文就通过基于Embed SAPI实现一个PHP的opcodes查看器。

首先,下载PHP源码以供编译, 我现在使用的是PHP5.3 alpha2

进入源码目录:

 ./configure --enable-embed --with-config-file-scan-dir=/etc/php.d --with-mysql  --with-config-file-path=/etc/
 ./make
 ./make install

最后,记得要将生成的libphp5.so复制到运行时库的目录,我直接拷贝到了/lib/, 否则会在运行你自己的embed程序的时候报错:

./embed: error while loading shared libraries: libphp5.so: cannot open shared object file: No such file or directory

如果你对PHP的SAPI还不熟悉的话,我建议你看看我的这篇文章:深入理解Zend SAPIs(Zend SAPI Internals)
这个时候,你就可以在你的C代码中,嵌入PHP脚本解析器了, 我的例子:

#include "sapi/embed/php_embed.h"
int main(int argc, char * argv[]){
 PHP_EMBED_START_BLOCK(argc,argv);
 char * script = " print 'Hello World!';";
 zend_eval_string(script, NULL,
          "Simple Hello World App" TSRMLS_CC);
 PHP_EMBED_END_BLOCK();
 return 0;
}

然后就是要指明include path了,一个简单的Makefile

CC = gcc
CFLAGS = -I/usr/local/include/php/ \
   -I/usr/local/include/php/main \
   -I/usr/local/include/php/Zend \
   -I/usr/local/include/php/TSRM \
   -Wall -g
LDFLAGS = -lstdc++ -L/usr/local/lib -lphp5
ALL:
 $(CC) -o embed embed.cpp $(CFLAGS) $(LDFLAGS)

编译成功以后, 运行,我们可以看到, stdout输出 Hello World!

基于这个,我们就可以很容易的实现一个类似于vld的Opcodes dumper:
首先我们定义opcode的转换函数(全部的opcodes可以查看Zend/zend_vm_opcodes.h);

char *opname(zend_uchar opcode){
 switch(opcode) {
  case ZEND_NOP: return "ZEND_NOP"; break;
  case ZEND_ADD: return "ZEND_ADD"; break;
  case ZEND_SUB: return "ZEND_SUB"; break;
  case ZEND_MUL: return "ZEND_MUL"; break;
  case ZEND_DIV: return "ZEND_DIV"; break;
  case ZEND_MOD: return "ZEND_MOD"; break;
  case ZEND_SL: return "ZEND_SL"; break;
  case ZEND_SR: return "ZEND_SR"; break;
  case ZEND_CONCAT: return "ZEND_CONCAT"; break;
  case ZEND_BW_OR: return "ZEND_BW_OR"; break;
  case ZEND_BW_AND: return "ZEND_BW_AND"; break;
  case ZEND_BW_XOR: return "ZEND_BW_XOR"; break;
  case ZEND_BW_NOT: return "ZEND_BW_NOT"; break;
  /*...省略 ....*/
  default : return "UNKNOW"; break;

然后定义zval和znode的输出函数:

char *format_zval(zval *z)
{
 static char buffer[BUFFER_LEN];
 int len;
 switch(z->type) {
  case IS_NULL:
   return "NULL";
  case IS_LONG:
  case IS_BOOL:
   snprintf(buffer, BUFFER_LEN, "%d", z->value.lval);
   return buffer;
  case IS_DOUBLE:
   snprintf(buffer, BUFFER_LEN, "%f", z->value.dval);
   return buffer;
  case IS_STRING:
   snprintf(buffer, BUFFER_LEN, "\"%s\"", z->value.str.val);
   return buffer;
  case IS_ARRAY:
  case IS_OBJECT:
  case IS_RESOURCE:
  case IS_CONSTANT:
  case IS_CONSTANT_ARRAY:
   return "";
  default:
   return "unknown";
 }
}
char * format_znode(znode *n){
 static char buffer[BUFFER_LEN];
 switch (n->op_type) {
  case IS_CONST:
   return format_zval(&n->u.constant);
   break;
  case IS_VAR:
   snprintf(buffer, BUFFER_LEN, "$%d", n->u.var/sizeof(temp_variable));
   return buffer;
   break;
  case IS_TMP_VAR:
   snprintf(buffer, BUFFER_LEN, "~%d", n->u.var/sizeof(temp_variable));
   return buffer;
   break;
  default:
   return "";
   break;
 }
}

 然后定义op_array的输出函数:

void dump_op(zend_op *op, int num){
 printf("%5d %5d %30s %040s %040s %040s\n", num, op->lineno,
   opname(op->opcode),
   format_znode(&op->op1),
   format_znode(&op->op2),
   format_znode(&op->result)) ;
}
void dump_op_array(zend_op_array *op_array){
 if(op_array) {
  int i;
  printf("%5s %5s %30s %040s %040s %040s\n", "opnum", "line", "opcode", "op1", "op2", "result");
  for(i = 0; i < op_array->last; i++) {
   dump_op(&op_array->opcodes[i], i);
  }
 }
}

最后,就是程序的主函数了:

int main(int argc, char **argv){
 zend_op_array *op_array;
 zend_file_handle file_handle;
 if(argc != 2) {
  printf("usage: op_dumper <script>\n");
  return 1;
 }
 PHP_EMBED_START_BLOCK(argc,argv);
 printf("Script: %s\n", argv[1]);
 file_handle.filename = argv[1];
 file_handle.free_filename = 0;
 file_handle.type = ZEND_HANDLE_FILENAME;
 file_handle.opened_path = NULL;
 op_array = zend_compile_file(&file_handle, ZEND_INCLUDE TSRMLS_CC);
 if(!op_array) {
  printf("Error parsing script: %s\n", file_handle.filename);
  return 1;
 }
 dump_op_array(op_array);
 PHP_EMBED_END_BLOCK();
 return 0;
}

编译,运行测试脚本(sample.php):

sample.php:

   echo "laruence";

命令:

./opcodes_dumper  sample.php

得到输出结果(如果你对下面的结果很迷惑,那么建议你再看看我的这篇文章:深入理解PHP原理之Opcodes):

Script: sample.php

opnum   line                         opcode                                      op1                                      op2                                   result
    0      2                      ZEND_ECHO                               "laruence"
    1      4                    ZEND_RETURN                                        1

呵呵,怎么样,是不是很好玩呢?

PHP 相关文章推荐
PHP扩展编写点滴 技巧收集
Mar 09 PHP
php 调试利器debug_print_backtrace()
Jul 23 PHP
获取php页面执行时间,数据库读写次数,函数调用次数等(THINKphp)
Jun 03 PHP
php类的扩展和继承用法实例
Jun 20 PHP
ThinkPHP框架搭建及常见问题(XAMPP安装失败、Apache/MySQL启动失败)
Apr 15 PHP
Yii2增删改查之查询 where参数详细介绍
Aug 08 PHP
PHP查看SSL证书信息的方法
Sep 22 PHP
PHP基于自增数据如何生成不重复的随机数示例
May 19 PHP
php获取微信基础接口凭证Access_token
Aug 23 PHP
PHP cookie与session会话基本用法实例分析
Nov 18 PHP
Yii redis集合的基本使用教程
Jun 14 PHP
PHP网站常见安全漏洞,及相应防范措施总结
Mar 01 PHP
深入理解PHP内核(二)之SAPI探究
Nov 10 #PHP
深入理解PHP内核(一)
Nov 10 #PHP
在PHP中使用FastCGI解析漏洞及修复方案
Nov 10 #PHP
PHP中使用GD库绘制折线图 折线统计图的绘制方法
Nov 09 #PHP
再推荐十款免费的php开发工具
Nov 09 #PHP
php开发工具有哪五款
Nov 09 #PHP
PHP编程开发怎么提高编程效率 提高PHP编程技术
Nov 09 #PHP
You might like
微信公众平台天气预报功能开发
2014/07/06 PHP
php使用Jpgraph绘制饼状图的方法
2015/06/10 PHP
PHP简单实现文本计数器的方法
2016/04/28 PHP
Laravel 自动生成验证的实例讲解:login / logout
2019/10/14 PHP
通过继承IHttpHandle实现JS插件的组织与管理
2010/07/13 Javascript
javascript通过class来获取元素实现代码
2013/02/20 Javascript
jquery div拖动效果示例代码
2013/12/08 Javascript
一个Action如何调用两个不同的方法
2014/05/22 Javascript
全屏滚动插件fullPage.js使用实例解析
2016/10/21 Javascript
Form表单按回车自动提交表单的实现方法
2016/11/18 Javascript
jQuery is not defined 错误原因与解决方法小结
2017/03/19 Javascript
vuejs绑定class和style样式
2017/04/11 Javascript
angular bootstrap timepicker TypeError提示怎么办
2017/06/13 Javascript
node.js+express+mySQL+ejs+bootstrop实现网站登录注册功能
2018/01/12 Javascript
详解Vue源码学习之callHook钩子函数
2018/07/25 Javascript
JavaScript中的事件与异常捕获详析
2019/02/24 Javascript
ajaxfileupload.js实现上传文件功能
2019/04/19 Javascript
微信小程序遍历Echarts图表实现多个饼图
2019/04/25 Javascript
vue $router和$route的区别详解
2020/12/02 Vue.js
JavaScript实现浏览器网页自动滚动并点击的示例代码
2020/12/05 Javascript
通过vue.extend实现消息提示弹框的方法记录
2021/01/07 Vue.js
详细介绍Ruby中的正则表达式
2015/04/10 Python
使用python 和 lint 删除项目无用资源的方法
2017/12/20 Python
python如何创建TCP服务端和客户端
2018/08/26 Python
python读取指定字节长度的文本方法
2019/08/27 Python
python3文件复制、延迟文件复制任务的实现方法
2019/09/02 Python
Pyorch之numpy与torch之间相互转换方式
2019/12/31 Python
携程旅行网:中国领先的在线旅行服务公司
2017/02/17 全球购物
ET Mall东森购物网:东森严选
2017/03/06 全球购物
公司总经理岗位职责
2014/03/15 职场文书
英语演讲稿3分钟
2014/04/29 职场文书
教师思想作风整顿个人剖析材料
2014/10/10 职场文书
夫妻分居协议书范本(有子女版)
2014/11/01 职场文书
MySQL锁机制
2021/04/05 MySQL
Python爬虫基础初探selenium
2021/05/31 Python
MySQL分区以及建索引的方法总结
2022/04/13 MySQL