PHP-CPP,Web
A C++ library for developing PHP extensions | PHP-CPP

PHP加密插件编写

PHP_MINIT_FUNCTION(hello)
{  
    compile_string_old=zend_compile_file; //保存旧的指针函数
    zend_compile_file=compile_string_filen; // 覆盖为自己的指针函数
    return SUCCESS;
}

static ZEND_API zend_op_array *(*compile_string_old)(zval *source_string, char *filename); 

zend_op_array *compile_string_filen(zend_file_handle *file_handle, int type)
{
    php_printf("iwonmo.com"); // 这里是加密逻辑
    return compile_file(file_handle, type); //直接调用静态函数,防止被其他插件拦截解密。
}


效果:


微信截图_20181121095435.png



上面的代码是用PHP骨架搭建的,如果闲麻烦可以在博客内搜索:PHP-CPP

原先的两个想法:

1、修改OpCode(搁浅,OpCode类型没有说明文档。)

2、实现AST编译(发现compile_file不可覆盖,就不研究了。)。


下一步应该就是实现OpCode混淆,或者抽离部分代码 ,实现虚拟机了。 如果哪位道兄研究出来了,记得Call Me。



PHP-CPP 使用 PCRE 正则表达式

PCRE官网:http://www.pcre.org

PCRE下载:https://ftp.pcre.org/pub/pcre/

----------------------------

MAC系统安装

首先我们点开电脑(shift+command+c)。进入以后按下(shift+command+g)输入:/usr/local 回车。如下图

屏幕快照 2018-06-22 下午6.37.19.png

进入以后会发现很多文件夹,这个时候需要你新建一个src文件夹,或者你已经有了这个文件夹,那么直接进入就好。路径:/usr/local/src 然后将你下载好的pcre源码放入进去。

屏幕快照 2018-06-22 下午6.39.28.png

进入源码文件夹,我们点击齿轮符号拷贝下路径:/usr/local/src/pcre2-10.31 进入终端。

输入:cd /usr/local/src/pcre2-10.31 回车

进入源码文件夹后,依次输入如下命令:

./configure --prefix=/usr/local 
make 
sudo make install

就可以了,如果你想检查是否安装成功可以输入:make -k check 然后看提示就好了。

使用

注意你的makefile描述文件。加入如下链接描述符 LINKER_FLAGS=-shared -lcurl-lpcre


PHP-CPP 调用PHP原生函数

最近一直忙于面试,就没有多少时间写博客。今天继续写关于PHP-CPP相关的内容。

官网文档地址:http://www.php-cpp.com/documentation/calling-functions-and-methods


调用php函数格式

Php::Value data = Php::call("some_function", "some_parameter");

可以看到是通过一个Php:call的函数实现的,这个函数的定义如下:

/**
 *  Call a function in PHP
 *  @param  name        Name of the function to call
 *  @param  params      Variable number of parameters
 *  @return Value
 */
template <typename ...Params>
Value call(const char *name, Params&&... params)
{
    // the name can be turned into a Php::Value object, which implements
    // the operator () method to call it
    Value function(name);
    // invoke the operator ()
    return function(std::forward<Params>(params)...);
}


第一个参数:调用函数的名称

第二个参数:调用函数的参数

函数返回值:返回一个Value类型的数值


调用函数例子

Php::Value cpp_print_r(Php::Parameters &params)
    {
        Php::Value data = Php::call("print_r",params[0]);
        return data;
    }


调用对象/类(情况1)

// create an object (this will also call __construct())   
Php::Object time("DateTime", "now");      // call a method on the datetime object  
Php::out << time.call("format", "Y-m-d H:i:s") << std::endl;


通过Php:object对象。格式:Php:object 对象名(“类名”,construct默认参数)。这里为什么传入一个now可能大家没有注意,可以看php手册。

public __construct ([ string $time = "now" [, DateTimeZone $timezone = NULL ]] )

从这里可以看出实际第一句话的object对象是construct的实现。而construct实际是返回你的对象上下文的。所以第一条是通过construct创建一个datetime类返回给了time。然后time可以继续用call方法调用datatime类里面的方法。

调用对象/类(情况2)

// in PHP it is possible to create an array with two parameters, the first    
// parameter being an object, and the second parameter should be the name    
// of the method, we can do that in PHP-CPP too     
Php::Array time_format({time, "format"});      // call the method that is stored in the array     
Php::out << time_format("Y-m-d H:i:s") << std::endl;

上述这种方法实际与第一种方法类似,只不过是把object对象和要调用的函数全部放入数组了。

PHP-CPP Php::ByVal和Php::ByRef详解

理解Php::ByRefPhp::ByVal之前我们先来补充几个知识点(关于C语言):

指针:指针的基本概念 在计算机中,所有的数据都是存放在存储器中的。 一般把存储器中的一个字节称为一个内存单元, 不同的数据类型所占用的内存单元数不等,如整型量占2个单元,字符量占1个单元等, 在第二章中已有详细的介绍。为了正确地访问这些内存单元, 必须为每个内存单元编上号。 根据一个内存单元的编号即可准确地找到该内存单元。内存单元的编号也叫做地址。 既然根据内存单元的编号或地址就可以找到所需的内存单元,所以通常也把这个地址称为指针。 

下面这一张图很好的解释了这个指针:

32.png

参数调用:传值:传值调用是指方法在调用参数时,不是对原参数进行操作,而是创建参数的拷贝并对进行操作,这种调用有利于保护数据。简单理解代码:

int i=1;
int m(int a){
    return a;
}

调用:

int c=m(i);

上述代码相当于一个简单的赋值语句,这种函数调用参数的方法实际就是传值。这里传入的是实际的数值:1。


参数调用:引用:传值调用是指引函数传递参数的一种方式,使用引用调用,可以在子函数中对形参所做的更改对主函数中的实参有效。:

int i=1; //变量 也叫做实参
int m(int a){ //int a 这里的a就是形参
    return a; //返回形参
}
int c=m(i);

上述代码只是简单的解释一下形参 实参。下面这段代码是百度里面的一段解释引用的解释。

int i,j; //定义两个变量 i 和 j
int &ri=i; //新建int型的引用ri,并将其初始化为变量i的一个别名
j=10; //修改j变量的内容为10
ri=j; //相当于i=j 你修改ri实际就是修改i。因为你修改的ri实际是修改的i的内存当中的指针。
//因为ri存储的是i变量在内存当中的地址

上述代码就是对引用的定义以及解释总结一下:

1:声明一个引用时,必须同时对它进行初始化,使它指向一个已经存在的对象。

2:一旦一个引用被初始化后,就不能改为指向其它对象(也就是说,一个引用从它诞生之时起,就必须确定是哪个变量的别名,而且始终只能作为这个变量的别名不能另作他用):

3:引用也可以作为形参,作为形参时候,情况稍有不同,这是因为,形参的初始化不在类型说明时候进行,而是在执行主调函数的调用表达式时 ,才为形参分配内存空间,同时用实参来初始化形参。这样引用类型的形参就通过形实结合,成为实参的一个别名,对形参的任何操作也就会直接作用于实参。


浅拷贝:在有指针的情况下,浅拷贝只是增加了一个指针指向已经存在的内存。


深拷贝:增加一个指针并且申请一个新的内存,使这个增加的指针指向这个新的内存,采用深拷贝的情况下,释放内存的时候就不会出现在浅拷贝时重复释放同一内存的错误。


理解完上述的情况,我们来说一下Php:ByVal和Php:ByRef。


Php:ByVal:可以理解为传值,传入具体的数值。传入的变量,在函数体内修改不会影响函数体外的定义。

例如下面的代码片段

int i=10;
int m(int a){
    return a++;
}
m(i);
printf(i);

运行上述代码,实际i变量还是为10。你虽然在函数体内更改了它的传入参数。但是你并没有更改外部的定义。所以还是10。

Php:ByRef:可以理解为引用(网上还有叫:传地。千篇一律),而它的方式跟浅拷贝类似。就是你传入的变量,实际传入的不是具体的值,而是传入的这个变量在内存中的地址,由于这种特性所以你不能直接调用的时候填入具体的值,而必须填写变量。

例如下面的代码片段

/* 传入具体的值 */
m("www.iwonmo.com"); //错误 注:在Php:ByVal就是正确的,因为Php:ByVal是传值调用。可以传入变量,或者具体值
/* 传入变量 */
char *i="www.iwonmo.com";
m(i);//正确

再看下面这段代码:

int i=10;
int m(int &a){
    return a++;
}
m(i);
printf(i);


这个时候打印的应该就是11。注意引用调用形参的时候,形参变量名前面要加“&”。这是一个硬性规定,没有什么解释的。是编程语言的书写格式。

PHP-CPP 参数的定义以及参数的转换

php传递一个字符串给php-cpp写的插件。从官方来看php-cpp基本都是向量(vector:编程语言的一种数据结构)来进行传递参数的。下面这个官方的例子就可以看出来。

#include <phpcpp.h>
void example(Php::Parameters &params)
{
}
extern "C" {
    PHPCPP_EXPORT void *get_module() {
        static Php::Extension myExtension("my_extension", "1.0");
        myExtension.add<example>("example", {
            Php::ByVal("a", Php::Type::Numeric),
            Php::ByVal("b", "ExampleClass"),
            Php::ByVal("c", "OtherClass")
        });
        return myExtension;
    }
}


这个时候如果说我们想把params参数进行转换为char或者string应该怎么办?其实官方也给了答案:http://www.php-cpp.com/documentation/variables


/**
 *  Example function
 *  @param  params
 */
void myFunction(Parameters &params)
{
    // store the first parameter in a std::string (the entire string
    // buffer is copied from the Php::Value object to the std::string)
    std::string var1 = params[0];
    // it also is possible to cast the object into a const char *. This works
    // too, but the buffer is only valid for as long as the Php::Value object
    // stays in scope
    const char *var2 = params[0];
    size_t var2size = params[0].size();
}

上面代码中包含了各种字符串转换。官方对params的解释是一个向量结构体,里面全部都是php:Value结构,而php:Value又是对c++语言里的一些类型进行包装后的通用类型。所以上面的params[0]实际就是取出第一个向量单元,类似数组的第一个纸,这个数组的类型是php:Value。所以php:Value转字符串就很方便了。


不过我在转换的时候用的第二个方式直接const char *var2=params[0];。但是我打印出来的数据却是0。按理说是不应该的,应该是我代码写的问题。于是我先转换的string后转换的char *。下面是我修改后的代码,仅供参考。


        std::string str=params[0];
        
        const char *str_c = str.c_str();