惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cloudbric
Cloudbric
E
Exploit-DB.com RSS Feed
SecWiki News
SecWiki News
Forbes - Security
Forbes - Security
N
News | PayPal Newsroom
S
Security @ Cisco Blogs
Schneier on Security
Schneier on Security
V
V2EX - 技术
S
Secure Thoughts
W
WeLiveSecurity
Google DeepMind News
Google DeepMind News
C
CERT Recently Published Vulnerability Notes
NISL@THU
NISL@THU
S
Securelist
S
Security Archives - TechRepublic
Know Your Adversary
Know Your Adversary
V
Vulnerabilities – Threatpost
Security Latest
Security Latest
Recent Commits to openclaw:main
Recent Commits to openclaw:main
G
GRAHAM CLULEY
H
Hacker News: Front Page
Microsoft Azure Blog
Microsoft Azure Blog
I
Intezer
Google Online Security Blog
Google Online Security Blog
美团技术团队
阮一峰的网络日志
阮一峰的网络日志
T
The Exploit Database - CXSecurity.com
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Webroot Blog
Webroot Blog
Jina AI
Jina AI
Engineering at Meta
Engineering at Meta
P
Proofpoint News Feed
The Cloudflare Blog
I
InfoQ
L
LangChain Blog
U
Unit 42
P
Proofpoint News Feed
S
Schneier on Security
S
Security Affairs
Y
Y Combinator Blog
T
Tenable Blog
N
News and Events Feed by Topic
MyScale Blog
MyScale Blog
量子位
Google DeepMind News
Google DeepMind News
Cyberwarzone
Cyberwarzone
博客园 - 聂微东
D
Darknet – Hacking Tools, Hacker News & Cyber Security
GbyAI
GbyAI
AWS News Blog
AWS News Blog

Comments for 风雪之隅

深入理解PHP7内核之OBJECT - 风雪之隅 关于调用约定(cdecl、fastcall、stcall、thiscall) 的一点知识 - 风雪之隅 PHP 8新特性之Attributes(注解) - 风雪之隅 博客迁移到腾讯云 - 风雪之隅 提升PHP性能之改变Zend引擎分发方式 - 风雪之隅 如何调试PHP的Core之获取基本信息 - 风雪之隅 请手动释放你的资源(Please release resources manually) - 风雪之隅 在Qcon 2015 北京上的演讲PPT - PHP7 Linux上配置Nginx+PHP5(FastCGI) - 风雪之隅 在PHP Module中获取$_GET/$_POST/$_COOKIE的方法研究 - 风雪之隅 深入理解Javascript之this关键字 - 风雪之隅 Yar-2.1 新功能介绍 - 风雪之隅 Yaf and Phalcon, which is faster? PHP CLI模式下的多进程应用 - 风雪之隅 PHP5.2.*防止Hash冲突拒绝服务攻击的Patch - 风雪之隅 PHP中的Hash算法 - 风雪之隅 PHP单引号和双引号的区别 - 风雪之隅 深入理解PHP之数组(遍历顺序) - 风雪之隅 深入理解PHP原理之变量分离/引用(Variables Separation) - 风雪之隅 PHP Taint - 一个用来检测XSS/SQL/Shell注入漏洞的扩展 - 风雪之隅 BTwitter(Twitter In Bash) - 风雪之隅 automake,autoconf使用详解 - 风雪之隅 HTTPOXY漏洞说明 - 风雪之隅 Nginx(PHP/fastcgi)的PATH_INFO问题 - 风雪之隅 关于PHP浮点数你应该知道的(All 'bogus' about the float in PHP) 一个关于Zend O+的小分享 - 风雪之隅 深入理解PHP原理之对象(一) - 风雪之隅 在PHP中使用协程实现多任务调度 - 风雪之隅 一些PHP Coding Tips[2011/04/02最后更新] - 风雪之隅 Curl的毫秒超时的一个"Bug" - 风雪之隅 Javascript作用域原理 - 风雪之隅 深入理解PHP原理之Opcodes - 风雪之隅 深入理解Zend SAPIs(Zend SAPI Internals) - 风雪之隅 Nginx + PHP CGI的一个可能的安全漏洞 - 风雪之隅 PHP 8新特性之JIT简介 - 风雪之隅 PHP FFI详解 - 一种全新的PHP扩展方式 - 风雪之隅 令人困惑的strtotime - 风雪之隅 PHP的性能演进(从PHP5.0到PHP7.1的性能全评测) - 风雪之隅 让PHP7达到最高性能的几个Tips - 风雪之隅
使用PHP Embed SAPI实现Opcodes查看器 - 风雪之隅
Prestige par · 2023-07-12 · via Comments for 风雪之隅

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

呵呵,怎么样,是不是很好玩呢?
源码地址:http://code.google.com/p/opcodesdumper/