设为首页 收藏本站
查看: 445|回复: 0

[经验分享] 使用PHP Embed SAPI实现Opcodes查看器

[复制链接]

尚未签到

发表于 2017-4-5 10:29:10 | 显示全部楼层 |阅读模式
  使用PHP Embed SAPI实现Opcodes查看器
  

  ·作者:laruence(http://www.laruence.com/)
  ·本文地址: http://www.laruence.com/2008/09/23/539.html
  ·转载请注明出处
  PHP提供了一个Embed SAPI,也就是说,PHP容许你在C/C++语言中调用PHP/ZE提供的函数。本文就通过基于Embed SAPI实现一个PHP的opcodes查看器。
  首先,下载PHP源码以供编译, 我现在使用的是PHP5.3 alpha2
  进入源码目录:
  ./configure --enable-embed 
  ./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);
}
}
}

//最后,就是程序的主函数了:
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
  

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

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-360452-1-1.html 上篇帖子: 使用PHP创建一个REST API(Create a REST API with PHP) 下篇帖子: 让你的PHP同时支持GIF、png、JPEG
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表