首页 > 运营 > 建站经验 > 正文

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

2021-11-25 12:58:24
字体:
来源:转载
供稿:网友

这篇文章主要介绍了如何使用PHP Embed SAPI实现Opcodes查看器的相关资料,需要的朋友可以参考下。

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

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

进入源码目录:

  1. ./configure --enable-embed --with-config-file-scan-dir=/etc/php.d --with-mysql  --with-config-file-path=/etc/ 
  2. ./make 
  3. ./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脚本解析器了, 我的例子:

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

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

  1. CC = gcc 
  2. CFLAGS = -I/usr/local/include/php/ / 
  3.    -I/usr/local/include/php/main / 
  4.    -I/usr/local/include/php/Zend / 
  5.    -I/usr/local/include/php/TSRM / 
  6.    -Wall -g 
  7. LDFLAGS = -lstdc++ -L/usr/local/lib -lphp5 
  8. ALL: 
  9.  $(CC) -o embed embed.cpp $(CFLAGS) $(LDFLAGS) 

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

基于这个,我们就可以很容易的实现一个类似于vld的Opcodes dumper:

首先我们定义opcode的转换函数(全部的opcodes可以查看Zend/zend_vm_opcodes.h);

  1. char *opname(zend_uchar opcode){ 
  2.  switch(opcode) { 
  3.   case ZEND_NOP: return "ZEND_NOP"break
  4.   case ZEND_ADD: return "ZEND_ADD"break
  5.   case ZEND_SUB: return "ZEND_SUB"break
  6.   case ZEND_MUL: return "ZEND_MUL"break
  7.   case ZEND_DIV: return "ZEND_DIV"break
  8.   case ZEND_MOD: return "ZEND_MOD"break
  9.   case ZEND_SL: return "ZEND_SL"break
  10.   case ZEND_SR: return "ZEND_SR"break
  11.   case ZEND_CONCAT: return "ZEND_CONCAT"break
  12.   case ZEND_BW_OR: return "ZEND_BW_OR"break
  13.   case ZEND_BW_AND: return "ZEND_BW_AND"break
  14.   case ZEND_BW_XOR: return "ZEND_BW_XOR"break
  15.   case ZEND_BW_NOT: return "ZEND_BW_NOT"break
  16.   /*...省略 ....*/ 
  17.   default : return "UNKNOW"break

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

  1. char *format_zval(zval *z) 
  2.  static char buffer[BUFFER_LEN]; 
  3.  int len; 
  4.  switch(z->type) { 
  5.   case IS_NULL
  6.    return "NULL"
  7.   case IS_LONG
  8.   case IS_BOOL
  9.    snprintf(buffer, BUFFER_LEN, "%d", z->value.lval); 
  10.    return buffer; 
  11.   case IS_DOUBLE
  12.    snprintf(buffer, BUFFER_LEN, "%f", z->value.dval); 
  13.    return buffer; 
  14.   case IS_STRING
  15.    snprintf(buffer, BUFFER_LEN, "/"%s/"", z->value.str.val); 
  16.    return buffer; 
  17.   case IS_ARRAY
  18.   case IS_OBJECT
  19.   case IS_RESOURCE
  20.   case IS_CONSTANT: 
  21.   case IS_CONSTANT_ARRAY: 
  22.    return ""
  23.   default
  24.    return "unknown"
  25.  } 
  26. char * format_znode(znode *n){ 
  27.  static char buffer[BUFFER_LEN]; 
  28.  switch (n->op_type) { 
  29.   case IS_CONST: 
  30.    return format_zval(&n->u.constant); 
  31.    break
  32.   case IS_VAR: 
  33.    snprintf(buffer, BUFFER_LEN, "$%d", n->u.var/sizeof(temp_variable)); 
  34.    return buffer; 
  35.    break
  36.   case IS_TMP_VAR: 
  37.    snprintf(buffer, BUFFER_LEN, "~%d", n->u.var/sizeof(temp_variable)); 
  38.    return buffer; 
  39.    break
  40.   default
  41.    return ""
  42.    break
  43.  } 

然后定义op_array的输出函数:

  1. void dump_op(zend_op *op, int num){ 
  2.  printf("%5d %5d %30s %040s %040s %040s/n", num, op->lineno, 
  3.    opname(op->opcode), 
  4.    format_znode(&op->op1), 
  5.    format_znode(&op->op2), 
  6.    format_znode(&op->result)) ; 
  7. void dump_op_array(zend_op_array *op_array){ 
  8.  if(op_array) { 
  9.   int i; 
  10.   printf("%5s %5s %30s %040s %040s %040s/n""opnum""line""opcode""op1""op2""result"); 
  11.   for(i = 0; i < op_array->last; i++) { 
  12.    dump_op(&op_array->opcodes[i], i); 
  13.   } 
  14.  } 

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

  1. int main(int argc, char **argv){ 
  2.  zend_op_array *op_array; 
  3.  zend_file_handle file_handle; 
  4.  if(argc != 2) { 
  5.   printf("usage: op_dumper <script>/n"); 
  6.   return 1; 
  7.  } 
  8.  PHP_EMBED_START_BLOCK(argc,argv); 
  9.  printf("Script: %s/n", argv[1]); 
  10.  file_handle.filename = argv[1]; 
  11.  file_handle.free_filename = 0; 
  12.  file_handle.type = ZEND_HANDLE_FILENAME; 
  13.  file_handle.opened_path = NULL; 
  14.  op_array = zend_compile_file(&file_handle, ZEND_INCLUDE TSRMLS_CC); 
  15.  if(!op_array) { 
  16.   printf("Error parsing script: %s/n", file_handle.filename); 
  17.   return 1; 
  18.  } 
  19.  dump_op_array(op_array); 
  20.  PHP_EMBED_END_BLOCK(); 
  21.  return 0; 

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

sample.php:

echo "laruence";

命令:./opcodes_dumper  sample.php

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

  1. Script: sample.php 
  2. opnum   line                         opcode                                      op1                                      op2                                   result 
  3.     0      2                      ZEND_ECHO                               "laruence" 
  4.     1      4                    ZEND_RETURN                                        1 

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

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表