如何使用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抓即时股票信息
Oct 09 PHP
实用函数8
Nov 08 PHP
PHP中函数内引用全局变量的方法
Oct 20 PHP
PHP has encountered an Access Violation at 7C94BD02解决方法
Aug 24 PHP
用mysql触发器自动更新memcache的实现代码
Oct 11 PHP
关于php fread()使用技巧
Jan 22 PHP
mayfish 数据入库验证代码
Apr 30 PHP
PHP 年龄计算函数(精确到天)
Jun 07 PHP
PHP中使用file_get_contents抓取网页中文乱码问题解决方法
Dec 17 PHP
Yii净化器CHtmlPurifier用法示例(过滤不良代码)
Jul 15 PHP
PHP巧妙利用位运算实现网站权限管理的方法
Mar 12 PHP
asp.net和php的区别点总结
Oct 10 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
PHP抓取、分析国内视频网站的视频信息工具类
2014/04/02 PHP
PHP实现根据设备类型自动跳转相应页面的方法
2014/07/24 PHP
ThinkPHP后台首页index使用frameset时的注意事项分析
2014/08/22 PHP
replace()方法查找字符使用示例
2013/10/28 Javascript
Js操作树节点自动折叠展开的几种方法
2014/05/05 Javascript
js点击button按钮跳转到另一个新页面
2014/10/10 Javascript
30个经典的jQuery代码开发技巧
2014/12/15 Javascript
jQuery动态效果显示人物结构关系图的方法
2015/05/07 Javascript
在JavaScript中处理字符串之fontcolor()方法的使用
2015/06/08 Javascript
AngularJS 过滤器的简单实例
2016/07/27 Javascript
bootstrap提示标签、提示框实现代码
2016/12/28 Javascript
老生常谈jacascript DOM节点获取
2017/04/17 Javascript
js实现股票实时刷新数据案例
2017/05/14 Javascript
浅谈关于.vue文件中style的scoped属性
2017/08/19 Javascript
原生js中ajax访问的实例详解
2017/09/19 Javascript
微信小程序之swiper滑动面板用法示例
2018/12/04 Javascript
vue中v-model对select的绑定操作
2020/08/31 Javascript
[01:03]悬念揭晓 11月26日DOTA2完美盛典不见不散
2017/11/23 DOTA
Python 匹配任意字符(包括换行符)的正则表达式写法
2009/10/29 Python
Python实现以时间换空间的缓存替换算法
2016/02/19 Python
微信跳一跳游戏python脚本
2020/04/01 Python
python的staticmethod与classmethod实现实例代码
2018/02/11 Python
python使用numpy读取、保存txt数据的实例
2018/10/14 Python
Python multiprocessing多进程原理与应用示例
2019/02/28 Python
Django框架实现的分页demo示例
2019/05/25 Python
Django 接收Post请求数据,并保存到数据库的实现方法
2019/07/12 Python
Python中常用的高阶函数实例详解
2020/02/21 Python
如何在Canvas上的图形/图像绑定事件监听的实现
2020/09/16 HTML / CSS
Abbacino官网:包、钱包和女士配饰
2019/04/15 全球购物
意大利体育用品和运动服网上商店:Maxi Sport
2019/09/14 全球购物
应届生幼儿园求职信
2013/11/12 职场文书
四年的大学生生活自我评价
2013/12/09 职场文书
公务员保密工作承诺书
2015/05/04 职场文书
乡镇团委工作总结2015
2015/05/26 职场文书
Python中非常使用的6种基本变量的操作与技巧
2022/03/22 Python
Linux安装Docker详细教程
2022/07/07 Servers