如何使用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 GeoIP的使用教程
Mar 09 PHP
PHP文件去掉PHP注释空格的函数分析(PHP代码压缩)
Jul 02 PHP
php一些错误处理的方法与技巧总结
Aug 10 PHP
php正则取img标记中任意属性(正则替换去掉或改变图片img标记中的任意属性)
Aug 13 PHP
完善CodeIgniter在IDE中代码提示功能的方法
Jul 19 PHP
php过滤html标记属性类用法实例
Sep 23 PHP
php结合正则获取字符串中数字
Jun 19 PHP
php使用curl详细解析及问题汇总
Aug 11 PHP
PHP简单遍历对象示例
Sep 28 PHP
PHP读取文件的常见几种方法
Nov 03 PHP
浅谈PHP封装CURL
Mar 06 PHP
laravel 解决ajax异步提交数据,并还回填充表格的问题
Oct 15 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 的ArrayAccess接口 像数组一样来访问你的PHP对象
2010/10/12 PHP
json的键名为数字时的调用方式(示例代码)
2013/11/15 PHP
Chrome Web App开发小结
2014/09/04 PHP
smarty的section嵌套循环用法示例
2016/05/28 PHP
Yii2 hasOne(), hasMany() 实现三表关联的方法(两种)
2017/02/15 PHP
jquery创建div 实现代码
2009/04/27 Javascript
基于jquery的高性能td和input切换并可修改内容实现代码
2011/01/09 Javascript
js获取通过ajax返回的map型的JSONArray的方法
2014/01/09 Javascript
Javascript 读取操作Sql中的Xml字段
2014/10/09 Javascript
JavaScript动态添加列的方法
2015/03/25 Javascript
实现点击下箭头变上箭头来回切换的两种方法【推荐】
2016/12/14 Javascript
JS高级运动实例分析
2016/12/20 Javascript
JavaScript 日期时间选择器一些小结
2018/04/02 Javascript
使用Vue-cli 3.0搭建Vue项目的方法
2018/06/07 Javascript
详解Vue项目在其他电脑npm run dev运行报错的解决方法
2018/10/29 Javascript
100行代码实现一个vue分页组功能
2018/11/06 Javascript
JavaScript遍历查找数组中最大值与最小值的方法示例
2019/05/24 Javascript
koa2 从入门到精通(小结)
2019/07/23 Javascript
vue+elementUi 实现密码显示/隐藏+小图标变化功能
2020/01/18 Javascript
Jquery $.map使用方法实例详解
2020/09/01 jQuery
从零学python系列之数据处理编程实例(二)
2014/05/22 Python
python制作企业邮箱的爆破脚本
2016/10/05 Python
django 删除数据库表后重新同步的方法
2018/05/27 Python
在ubuntu16.04中将python3设置为默认的命令写法
2018/10/31 Python
python实现word文档批量转成自定义格式的excel文档的思路及实例代码
2020/02/21 Python
keras 自定义loss损失函数,sample在loss上的加权和metric详解
2020/05/23 Python
俄罗斯在线水暖商店:Perfecto.ru
2019/10/25 全球购物
系统管理员的职责包括那些?管理的对象是什么?
2016/09/20 面试题
《自然之道》教学反思
2014/02/11 职场文书
护士岗位求职应聘自荐书范文
2014/02/12 职场文书
外贸采购员岗位职责
2014/03/08 职场文书
乡镇爱国卫生月活动总结
2014/06/25 职场文书
内勤岗位职责
2015/02/10 职场文书
学习焦裕禄观后感
2015/06/09 职场文书
MySQL 四种连接和多表查询详解
2021/07/16 MySQL
Pygame Draw绘图函数的具体使用
2021/11/17 Python