调试php源代码,重写加密函数,封装接口供lua使用

原创 2015年11月19日 16:58:55

由于项目中使用加密算法,但是接口是php写的,需要使用lua编写业务,提供加密结果给php,

先看php的代码

<?php

       // 获取PEM文件
$pubKey = file_get_contents(dirname(__FILE__).'/public_key.pem');

       // 通过xxx函数获取res, echo $res的时候 发现是个资源
$res = openssl_pkey_get_public($pubKey);
$encrypted = '';
$data = "appkey=111&val=45678";
        // 加密函数
openssl_public_encrypt($data, $encrypted, $res);

       // 然后转base64编码
$encrypted = base64_encode($encrypted);

echo $encrypted;

?>

为了要搞清楚php调用的是啥功能,需要调试php源代码

1) 下载php源代码, php.net下载.

2) 安装牛逼的cgdb工具,注意需要 flex, help2man, texinfo 

3) 查找源代码  php-5.6.15/ext/openssl/openssl.c 文件

ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkey_get_public, 0)
ZEND_ARG_INFO(0, cert)
ZEND_END_ARG_INFO()

如上,好像是找到地方了. 


PHP_FE(openssl_pkey_get_public, arginfo_openssl_pkey_get_public)
PHP_FE(openssl_pkey_get_details, arginfo_openssl_pkey_get_details)


PHP_FALIAS(openssl_free_key, openssl_pkey_free, arginfo_openssl_pkey_free)
PHP_FALIAS(openssl_get_privatekey, openssl_pkey_get_private, arginfo_openssl_pkey_get_private)
PHP_FALIAS(openssl_get_publickey, openssl_pkey_get_public, arginfo_openssl_pkey_get_public)


先不理会这里面是什么东西,直接找到函数. 


PHP_FUNCTION(openssl_pkey_get_public)
{
zval **cert;
EVP_PKEY *pkey;

        // PHP用来校验参数的, 
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &cert) == FAILURE) {
return;
}
Z_TYPE_P(return_value) = IS_RESOURCE;
pkey = php_openssl_evp_from_zval(cert, 1, NULL, 1, &Z_LVAL_P(return_value) TSRMLS_CC);
        // 最核心的就是这个函数.

if (pkey == NULL) {
RETURN_FALSE;
}
zend_list_addref(Z_LVAL_P(return_value));
}


显示调用的函数



/* {{{ EVP Public/Private key functions */


/* {{{ php_openssl_evp_from_zval
   Given a zval, coerce it into a EVP_PKEY object.
It can be:
1. private key resource from openssl_get_privatekey()
2. X509 resource -> public key will be extracted from it
3. if it starts with file:// interpreted as path to key file
4. interpreted as the data from the cert/key file and interpreted in same way as openssl_get_privatekey()
5. an array(0 => [items 2..4], 1 => passphrase)
6. if val is a string (possibly starting with file:///) and it is not an X509 certificate, then interpret as public key
NOTE: If you are requesting a private key but have not specified a passphrase, you should use an
empty string rather than NULL for the passphrase - NULL causes a passphrase prompt to be emitted in
the Apache error log!
*/
static EVP_PKEY * php_openssl_evp_from_zval(zval ** val, int public_key, char * passphrase, int makeresource, long * resourceval TSRMLS_DC)
{
EVP_PKEY * key = NULL;
X509 * cert = NULL;
int free_cert = 0;
long cert_res = -1;
char * filename = NULL;
zval tmp;


Z_TYPE(tmp) = IS_NULL;


#define TMP_CLEAN \
if (Z_TYPE(tmp) == IS_STRING) {\
zval_dtor(&tmp); \
} \
return NULL;


if (resourceval) {
*resourceval = -1;
}
if (Z_TYPE_PP(val) == IS_ARRAY) {
zval ** zphrase;

/* get passphrase */


if (zend_hash_index_find(HASH_OF(*val), 1, (void **)&zphrase) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "key array must be of the form array(0 => key, 1 => phrase)");
return NULL;
}

if (Z_TYPE_PP(zphrase) == IS_STRING) {
passphrase = Z_STRVAL_PP(zphrase);
} else {
tmp = **zphrase;
zval_copy_ctor(&tmp);
convert_to_string(&tmp);
passphrase = Z_STRVAL(tmp);
}


/* now set val to be the key param and continue */
if (zend_hash_index_find(HASH_OF(*val), 0, (void **)&val) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "key array must be of the form array(0 => key, 1 => phrase)");
TMP_CLEAN;
}
}


if (Z_TYPE_PP(val) == IS_RESOURCE) {
void * what;
int type;


what = zend_fetch_resource(val TSRMLS_CC, -1, "OpenSSL X.509/key", &type, 2, le_x509, le_key);
if (!what) {
TMP_CLEAN;
}
if (resourceval) { 
*resourceval = Z_LVAL_PP(val);
}
if (type == le_x509) {
/* extract key from cert, depending on public_key param */
cert = (X509*)what;
free_cert = 0;
} else if (type == le_key) {
int is_priv;


is_priv = php_openssl_is_private_key((EVP_PKEY*)what TSRMLS_CC);


/* check whether it is actually a private key if requested */
if (!public_key && !is_priv) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied key param is a public key");
TMP_CLEAN;
}


if (public_key && is_priv) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Don't know how to get public key from this private key");
TMP_CLEAN;
} else {
if (Z_TYPE(tmp) == IS_STRING) {
zval_dtor(&tmp);
}
/* got the key - return it */
return (EVP_PKEY*)what;
}
} else {
/* other types could be used here - eg: file pointers and read in the data from them */
TMP_CLEAN;
}
} else {
/* force it to be a string and check if it refers to a file */
/* passing non string values leaks, object uses toString, it returns NULL 
* See bug38255.phpt 
*/
if (!(Z_TYPE_PP(val) == IS_STRING || Z_TYPE_PP(val) == IS_OBJECT)) {
TMP_CLEAN;
}
convert_to_string_ex(val);


if (Z_STRLEN_PP(val) > 7 && memcmp(Z_STRVAL_PP(val), "file://", sizeof("file://") - 1) == 0) {
filename = Z_STRVAL_PP(val) + (sizeof("file://") - 1);
}
/* it's an X509 file/cert of some kind, and we need to extract the data from that */
if (public_key) {
cert = php_openssl_x509_from_zval(val, 0, &cert_res TSRMLS_CC);
free_cert = (cert_res == -1);
/* actual extraction done later */
if (!cert) {
/* not a X509 certificate, try to retrieve public key */
BIO* in;
if (filename) {
in = BIO_new_file(filename, "r");
} else {
in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val));
}
if (in == NULL) {
TMP_CLEAN;
}
key = PEM_read_bio_PUBKEY(in, NULL,NULL, NULL);
BIO_free(in);
}
} else {
/* we want the private key */
BIO *in;


if (filename) {
if (php_openssl_open_base_dir_chk(filename TSRMLS_CC)) {
TMP_CLEAN;
}
in = BIO_new_file(filename, "r");
} else {
in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val));
}


if (in == NULL) {
TMP_CLEAN;
}
key = PEM_read_bio_PrivateKey(in, NULL,NULL, passphrase);
BIO_free(in);
}
}


if (public_key && cert && key == NULL) {
/* extract public key from X509 cert */
key = (EVP_PKEY *) X509_get_pubkey(cert);
}


if (free_cert && cert) {
X509_free(cert);
}
if (key && makeresource && resourceval) {
*resourceval = ZEND_REGISTER_RESOURCE(NULL, key, le_key);
}
if (Z_TYPE(tmp) == IS_STRING) {
zval_dtor(&tmp);
}
return key;
}
/* }}} */


然后直接上php,记得编译php的时候,是否添加了-g参数,这样就可以显示 符号连接等 ( 备注: linux 有个工具 nm  ) 

php编译完之后在

/data/tools/src_php/php-5.6.15/sapi/cli/php ( 可执行文件 ) 

[root@hadoop cli]# cgdb php

然后显示cgdb的欢迎信息等等

[?1034h[?1034hGNU gdb (GDB) 7.8
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-unknown-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from php...done.
(gdb) b php_openssl_generate_private_key
Breakpoint 1 at 0x462df0: file /data/tools/src_php/php-5.6.15/ext/openssl/openssl.c, line 3378.

首先在我们核心的代码处下端点.

然后设置php 程序执行我们的php代码

(gdb) set args /data/tools/test_openssl/index.php

使用gdb的set args 功能,设置调用的php代码

然后需要运行php程序,

(gdb) r

r是运行的缩写,发现无法断下来,则换个函数.

(gdb) b zend_parse_parameters


(gdb) r
Starting program: /data/tools/src_php/php-5.6.15/sapi/cli/php /data/tools/test_openssl/index.php 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".


(gdb) b zend_parse_parameters
Breakpoint 1 at 0x6f7530: file /data/tools/src_php/php-5.6.15/Zend/zend_API.c, line 917.



(gdb) bt  查看堆栈调用,是否是我们需要的.
#0  zend_parse_parameters (num_args=1, type_spec=0x84deb3 "s") at /data/tools/src_php/php-5.6.15/Zend/zend_API.c:917
#1  0x0000000000654c9f in zif_dirname (ht=<optimized out>, return_value=0x7ffff7fc5d98, return_value_ptr=<optimized out>, this_ptr=<optimized out>, return_value_used=<optimized out>) at /data/tools/src_php/php-5.6.15/ext/standard/string.
c:1538
#2  0x000000000076b202 in zend_do_fcall_common_helper_SPEC (execute_data=<optimized out>) at /data/tools/src_php/php-5.6.15/Zend/zend_vm_execute.h:558
#3  0x000000000075aa90 in execute_ex (execute_data=0x7ffff7f91230) at /data/tools/src_php/php-5.6.15/Zend/zend_vm_execute.h:363
#4  0x00000000006ec339 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /data/tools/src_php/php-5.6.15/Zend/zend.c:1341
#5  0x000000000068bc7a in php_execute_script (primary_file=0x7fffffffdb00) at /data/tools/src_php/php-5.6.15/main/main.c:2597
#6  0x000000000079265c in do_cli (argc=2, argv=0xea1490) at /data/tools/src_php/php-5.6.15/sapi/cli/php_cli.c:994
#7  0x0000000000792df8 in main (argc=2, argv=0xea1490) at /data/tools/src_php/php-5.6.15/sapi/cli/php_cli.c:1378

说明是调用了php的函数

file_get_contents(dirname(__FILE__).'/public_key.pem');

不是我们关注的,直接跳过

(gdb) c 

c使用直接运行,直到下一个断点处

(gdb) c
Continuing.


Breakpoint 1, zend_parse_parameters (num_args=1, type_spec=0xb3a8b7 "p|br!ll") at /data/tools/src_php/php-5.6.15/Zend/zend_API.c:917
(gdb) bt
#0  zend_parse_parameters (num_args=1, type_spec=0xb3a8b7 "p|br!ll") at /data/tools/src_php/php-5.6.15/Zend/zend_API.c:917
#1  0x0000000000636835 in zif_file_get_contents (ht=1, return_value=0x7ffff7fc5dc8, return_value_ptr=<optimized out>, this_ptr=<optimized out>, return_value_used=<optimized out>) at /data/tools/src_php/php-5.6.15/ext/standard/file.c:537
#2  0x00000000005b5b8c in phar_file_get_contents (ht=1, return_value=0x7ffff7fc5dc8, return_value_ptr=0x7ffff7f911d8, this_ptr=0x0, return_value_used=1) at /data/tools/src_php/php-5.6.15/ext/phar/func_interceptors.c:225
#3  0x000000000076b202 in zend_do_fcall_common_helper_SPEC (execute_data=<optimized out>) at /data/tools/src_php/php-5.6.15/Zend/zend_vm_execute.h:558
#4  0x000000000075aa90 in execute_ex (execute_data=0x7ffff7f91230) at /data/tools/src_php/php-5.6.15/Zend/zend_vm_execute.h:363
#5  0x00000000006ec339 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /data/tools/src_php/php-5.6.15/Zend/zend.c:1341
#6  0x000000000068bc7a in php_execute_script (primary_file=0x7fffffffdb00) at /data/tools/src_php/php-5.6.15/main/main.c:2597
#7  0x000000000079265c in do_cli (argc=2, argv=0xea1490) at /data/tools/src_php/php-5.6.15/sapi/cli/php_cli.c:994
#8  0x0000000000792df8 in main (argc=2, argv=0xea1490) at /data/tools/src_php/php-5.6.15/sapi/cli/php_cli.c:1378

说明调用的php代码是

file_get_contents(dirname(__FILE__).'/public_key.pem');

继续操作

(gdb) c
Continuing.


Breakpoint 1, zend_parse_parameters (num_args=1, type_spec=0xb3e599 "Z") at /data/tools/src_php/php-5.6.15/Zend/zend_API.c:917
(gdb) bt
#0  zend_parse_parameters (num_args=1, type_spec=0xb3e599 "Z") at /data/tools/src_php/php-5.6.15/Zend/zend_API.c:917
#1  0x0000000000466fab in zif_openssl_pkey_get_public (ht=<optimized out>, return_value=0x7ffff7fc5d98, return_value_ptr=<optimized out>, this_ptr=<optimized out>, return_value_used=<optimized out>) at /data/tools/src_php/php-5.6.15/ext/
openssl/openssl.c:3788
#2  0x000000000076b202 in zend_do_fcall_common_helper_SPEC (execute_data=<optimized out>) at /data/tools/src_php/php-5.6.15/Zend/zend_vm_execute.h:558
#3  0x000000000075aa90 in execute_ex (execute_data=0x7ffff7f91230) at /data/tools/src_php/php-5.6.15/Zend/zend_vm_execute.h:363
#4  0x00000000006ec339 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /data/tools/src_php/php-5.6.15/Zend/zend.c:1341
#5  0x000000000068bc7a in php_execute_script (primary_file=0x7fffffffdb00) at /data/tools/src_php/php-5.6.15/main/main.c:2597
#6  0x000000000079265c in do_cli (argc=2, argv=0xea1490) at /data/tools/src_php/php-5.6.15/sapi/cli/php_cli.c:994
#7  0x0000000000792df8 in main (argc=2, argv=0xea1490) at /data/tools/src_php/php-5.6.15/sapi/cli/php_cli.c:1378

找到了php的调用核心代码以及确认了堆栈调用,

PHP_FUNCTION(openssl_pkey_get_public) 

实际上的函数名是zif_openssl_pkey_get_public,前面增加了zif. 

那么是否可以猜测PHP_FUNCTION(openssl_pkey_free)实际函数为 zif_openssl_pkey_free??? 

来验证一下

(gdb) b zif_openssl_pkey_free
Breakpoint 2 at 0x462b00: file /data/tools/src_php/php-5.6.15/ext/openssl/openssl.c, line 3804.

路径以及函数确实是正确的, 

那么对于调用php的函数 PHP_FUNCTION(openssl_pkey_get_public) 直接下断点应该是 zif_openssl_pkey_get_public 

有兴趣的可以直接去尝试更多的.


调试回到正题

 916│ ZEND_API int zend_parse_parameters(int num_args TSRMLS_DC, const char *type_spec, ...) /* {{{ */
 917├>{
 918│         va_list va;
 919│         int retval;
 920│
 921│         RETURN_IF_ZERO_ARGS(num_args, type_spec, 0);
 922│
 923│         va_start(va, type_spec);
 924│         retval = zend_parse_va_args(num_args, type_spec, &va, 0 TSRMLS_CC);
 925│         va_end(va);
 926│
 927│         return retval;
 928│ }
 929│ /* }}} */
/data/tools/src_php/php-5.6.15/Zend/zend_API.c  

cgbd的当前窗体,需要跳出该函数,返回核心代码

(gdb) finish
Run till exit from #0  zend_parse_parameters (num_args=1, type_spec=0xb3e599 "Z") at /data/tools/src_php/php-5.6.15/Zend/zend_API.c:917
0x0000000000466fab in zif_openssl_pkey_get_public (ht=<optimized out>, return_value=0x7ffff7fc5d98, return_value_ptr=<optimized out>, this_ptr=<optimized out>, return_value_used=<optimized out>) at /data/tools/src_php/php-5.6.15/ext/open
ssl/openssl.c:3788
Value returned is $1 = 0


cgdb的代码窗口如下:

3781│ /* {{{ proto int openssl_pkey_get_public(mixed cert)
3782│    Gets public key from X.509 certificate */
3783│ PHP_FUNCTION(openssl_pkey_get_public)
3784│ {
3785│         zval **cert;
3786│         EVP_PKEY *pkey;
3787│
3788├>        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &cert) == FAILURE) {
3789│                 return;
3790│         }
3791│         Z_TYPE_P(return_value) = IS_RESOURCE;
3792│         pkey = php_openssl_evp_from_zval(cert, 1, NULL, 1, &Z_LVAL_P(return_value) TSRMLS_CC); 需要单步跟踪进去
3793│
3794│         if (pkey == NULL) {
3795│                 RETURN_FALSE;
3796│         }
3797│         zend_list_addref(Z_LVAL_P(return_value));
3798│ }
3799│ /* }}} */

cgdb使用n为单步, s为进去子函数

使用n单步到3792行,然后使用s进如子函数

(gdb) s 
php_openssl_evp_from_zval (val=0x7ffff7f912f8, public_key=1, passphrase=0x0, makeresource=1, resourceval=0x7ffff7fc5d98) at /data/tools/src_php/php-5.6.15/ext/openssl/openssl.c:3220


3219│ static EVP_PKEY * php_openssl_evp_from_zval(zval ** val, int public_key, char * passphrase, int makeresource, long * resourceval TSRMLS_DC)
3220├>{
3221│         EVP_PKEY * key = NULL;
3222│         X509 * cert = NULL;
3223│         int free_cert = 0;
3224│         long cert_res = -1;
3225│         char * filename = NULL;
3226│         zval tmp;
3227│
3228│         Z_TYPE(tmp) = IS_NULL;
3229│
3230│ #define TMP_CLEAN \
3231│         if (Z_TYPE(tmp) == IS_STRING) {\
3232│                 zval_dtor(&tmp); \
/data/tools/src_php/php-5.6.15/ext/openssl/openssl.c                                                                                                                                                                                         


Breakpoint 1, zend_parse_parameters (num_args=1, type_spec=0xb3e599 "Z") at /data/tools/src_php/php-5.6.15/Zend/zend_API.c:917


现在只需要看看函数怎么执行的,然后把代码扣出来就行了. 

3305│         } else {
3306│                 /* force it to be a string and check if it refers to a file */
3307│                 /* passing non string values leaks, object uses toString, it returns NULL 
3308│                  * See bug38255.phpt 
3309│                  */
3310│                 if (!(Z_TYPE_PP(val) == IS_STRING || Z_TYPE_PP(val) == IS_OBJECT)) {
3311│                         TMP_CLEAN;
3312│                 }
3313├>                convert_to_string_ex(val);
3314│
3315│                 if (Z_STRLEN_PP(val) > 7 && memcmp(Z_STRVAL_PP(val), "file://", sizeof("file://") - 1) == 0) {
3316│                         filename = Z_STRVAL_PP(val) + (sizeof("file://") - 1);
3317│                 }
3318│                 /* it's an X509 file/cert of some kind, and we need to extract the data from that */
3319│                 if (public_key) {
3320│                         cert = php_openssl_x509_from_zval(val, 0, &cert_res TSRMLS_CC);
3321│                         free_cert = (cert_res == -1);
3322│                         /* actual extraction done later */
3323│                         if (!cert) {
3324│                                 /* not a X509 certificate, try to retrieve public key */
3325│                                 BIO* in;

/data/tools/src_php/php-5.6.15/ext/openssl/openssl.c   

这时候可以查看val的值

(gdb) p val
$2 = (zval **) 0x7ffff7f912f8
(gdb) p *val
$3 = (zval *) 0x7ffff7fc5dc8
(gdb) p **val
$4 = {value = {lval = 140737353908040, dval = 6.9533491652563866e-310, str = {val = 0x7ffff7fc7f48 "-----BEGIN PUBLIC KEY-----\nssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss9wrS8JH"..., len = 800}, ht = 0x7ffff7fc7f48, obj = {handle = 4160520008, handlers = 0x320}, ast = 0x7ffff7fc7f48}, refcount__gc = 2, type = 6 '\006', is_ref__gc = 0 '\000'}
(gdb) ptype val
type = struct _zval_struct {
    zvalue_value value;
    zend_uint refcount__gc;
    zend_uchar type;
    zend_uchar is_ref__gc;
} **
(gdb) whatis val
type = zval **

标红的--BEGIN读取的内容

3313│                 convert_to_string_ex(val);
3314│
3315│                 if (Z_STRLEN_PP(val) > 7 && memcmp(Z_STRVAL_PP(val), "file://", sizeof("file://") - 1) == 0) {
3316│                         filename = Z_STRVAL_PP(val) + (sizeof("file://") - 1);
3317│                 }
3318│                 /* it's an X509 file/cert of some kind, and we need to extract the data from that */
3319│                 if (public_key) {
3320│                         cert = php_openssl_x509_from_zval(val, 0, &cert_res TSRMLS_CC);
3321│                         free_cert = (cert_res == -1);
3322│                         /* actual extraction done later */
3323│                         if (!cert) {
3324│                                 /* not a X509 certificate, try to retrieve public key */
3325│                                 BIO* in;
3326├>                                if (filename) {
3327│                                         in = BIO_new_file(filename, "r");
3328│                                 } else {
3329│                                         in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val));
3330│                                 }
3331│                                 if (in == NULL) {
3332│                                         TMP_CLEAN;
3333│                                 }
3334│                                 key = PEM_read_bio_PUBKEY(in, NULL,NULL, NULL);
3335│                                 BIO_free(in);
3336│                         }

3337│                 } else {
3338│                         /* we want the private key */
/data/tools/src_php/php-5.6.15/ext/openssl/openssl.c 

上面加红的是核心代码,由于业务场景加密方式已经确定,临时先满足业务即可.

X509 * cert = NULL;  

相关openssl以及X509的知识请查询相关文档

重新写一个demo的代码

由于php_openssl_x509_from_zval里面有一些代码,不太重要.

demo.c代码片段


参数为pem文件的buf以及buf的长度


EVP_PKEY *lua_openssl_evp_from_val(char *val_buf,int val_lenth)
{
EVP_PKEY * key = NULL;
X509 * cert = NULL;
BIO  *in;
// ./bio.h:BIO *BIO_new_mem_buf(void *buf, int len);
in = BIO_new_mem_buf(val_buf,val_lenth); // 系统函数
if (in == NULL) {
return NULL;
}


// ./pem.h:#define PEM_STRING_X509         "CERTIFICATE"
// ./pem.h:void *  PEM_ASN1_read_bio(d2i_of_void *d2i, const char *name, BIO *bp,
// #ifdef TYPEDEF_D2I_OF
// cert = (X509 *) PEM_ASN1_read_bio((d2i_of_void *)d2i_X509, PEM_STRING_X509, in, NULL, NULL, NULL);
// #else
// cert = (X509 *) PEM_ASN1_read_bio((char *(*)())d2i_X509, PEM_STRING_X509, in, NULL, NULL, NULL);
cert = (X509 *) PEM_ASN1_read_bio((char *(*)())d2i_X509, PEM_STRING_X509, in, NULL, NULL, NULL);
// #endif
// ./bio.h:int     BIO_free(BIO *a);
BIO_free(in);
if(!cert){
BIO* in;
in = BIO_new_mem_buf(val_buf,val_lenth);
if (in == NULL) {
return NULL;
}
key = PEM_read_bio_PUBKEY(in, NULL,NULL, NULL);
BIO_free(in);
return key;
}
key = (EVP_PKEY *) X509_get_pubkey(cert);
X509_free(cert);
if(!key)
{
return NULL;
}
}


//// buf以及len为pem的内容以及长度

EVP_PKEY *pkey =  lua_openssl_evp_from_val(buf,len);

 调用上面的一小段代码
if(!pkey)
{
printf("test lua_openssl_evp_from_val failed!\r\n");
return 0;
}


接下来调用掉是php的代码 openssl_public_encrypt 

(gdb) b zif_openssl_public_encrypt
Breakpoint 3 at 0x4669e0: file /data/tools/src_php/php-5.6.15/ext/openssl/openssl.c, line 4583.

直接使用zif的方式 b zif_openssl_public_encrypt

4582│ PHP_FUNCTION(openssl_public_encrypt)
4583├>{
4584│         zval **key, *crypted;
4585│         EVP_PKEY *pkey;
4586│         int cryptedlen;
4587│         unsigned char *cryptedbuf;
4588│         int successful = 0;
4589│         long keyresource = -1;
4590│         long padding = RSA_PKCS1_PADDING;
4591│         char * data;
4592│         int data_len;
4593│
4594│         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE)
4595│                 return;
/data/tools/src_php/php-5.6.15/ext/openssl/openssl.c     
使用单步跟踪的方式

4594│         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE)
4595│                 return;
4596│
4597│         RETVAL_FALSE;
4598│
4599├>        pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, &keyresource TSRMLS_CC);
4600│         if (pkey == NULL) {
4601│                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "key parameter is not a valid public key");
4602│                 RETURN_FALSE;
4603│         }
4604│
4605│         cryptedlen = EVP_PKEY_size(pkey);
4606│         cryptedbuf = emalloc(cryptedlen + 1);
4607│

4608│         switch (pkey->type) {
4609│                 case EVP_PKEY_RSA:
4610│                 case EVP_PKEY_RSA2:
4611│                         successful = (RSA_public_encrypt(data_len,
/data/tools/src_php/php-5.6.15/ext/openssl/openssl.c   

进入上次进去的函数php_openssl_evp_from_zval 

3265│         if (Z_TYPE_PP(val) == IS_RESOURCE) {
3266│                 void * what;
3267│                 int type;
3268│
3269│                 what = zend_fetch_resource(val TSRMLS_CC, -1, "OpenSSL X.509/key", &type, 2, le_x509, le_key);
3270│                 if (!what) {
3271│                         TMP_CLEAN;
3272│                 }
3273│                 if (resourceval) {
3274│                         *resourceval = Z_LVAL_PP(val);
3275│                 }
3276├>                if (type == le_x509) {
3277│                         /* extract key from cert, depending on public_key param */
3278│                         cert = (X509*)what;
3279│                         free_cert = 0;
3280│                 } else if (type == le_key) {
3281│                         int is_priv;
3282│
3283│                         is_priv = php_openssl_is_private_key((EVP_PKEY*)what TSRMLS_CC);
3284│
3285│                         /* check whether it is actually a private key if requested */
3286│                         if (!public_key && !is_priv) {
3287│                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied key param is a public key");
3288│                                 TMP_CLEAN;

/data/tools/src_php/php-5.6.15/ext/openssl/openssl.c    

(gdb) p type 
$6 = 5
(gdb) p le_key
$7 = 5

使用p查看变量的值.

说白了进入当前函数,主要是确认是否为public还是private的key

3451│
3452│ /* {{{ php_openssl_is_private_key
3453│         Check whether the supplied key is a private key by checking if the secret prime factors are set */
3454│ static int php_openssl_is_private_key(EVP_PKEY* pkey TSRMLS_DC)
3455│ {
3456│         assert(pkey != NULL);
3457│
3458├>        switch (pkey->type) {
3459│ #ifndef NO_RSA
3460│                 case EVP_PKEY_RSA:
3461│                 case EVP_PKEY_RSA2:
3462│                         assert(pkey->pkey.rsa != NULL);
3463│                         if (pkey->pkey.rsa != NULL && (NULL == pkey->pkey.rsa->p || NULL == pkey->pkey.rsa->q)) {
3464│                                 return 0;
3465│                         }
3466│                         break;
3467│ #endif
3468│ #ifndef NO_DSA
3469│                 case EVP_PKEY_DSA:
3470│                 case EVP_PKEY_DSA1:
/data/tools/src_php/php-5.6.15/ext/openssl/openssl.c        

(gdb) p pkey->type 
$8 = 6

查看pkey->type为6 ,就是需要确认pkey的type值是否正常. 

所以代码精简(由于是业务单一,可以先实现功能哦 :)  > 

// int is_priv = php_openssl_is_private_key((EVP_PKEY*)pkey);
// 
if(pkey->type != 6 )
{
// current is private key
// php_openssl_is_private_key
printf("current evp_pkey is private , check failed!\r\n");
return 0;
}

目前代码



/* {{{ proto bool openssl_public_encrypt(string data, string &crypted, mixed key [, int padding])
   Encrypts data with public key */
PHP_FUNCTION(openssl_public_encrypt)
{
zval **key, *crypted;
EVP_PKEY *pkey;
int cryptedlen;
unsigned char *cryptedbuf;
int successful = 0;
long keyresource = -1;
long padding = RSA_PKCS1_PADDING;
char * data;
int data_len;


if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE)
return;


RETVAL_FALSE;

pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, &keyresource TSRMLS_CC);
if (pkey == NULL) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "key parameter is not a valid public key");
RETURN_FALSE;
}


cryptedlen = EVP_PKEY_size(pkey);
cryptedbuf = emalloc(cryptedlen + 1);


switch (pkey->type) {
case EVP_PKEY_RSA:
case EVP_PKEY_RSA2:
successful = (RSA_public_encrypt(data_len, 
(unsigned char *)data, 
cryptedbuf, 
pkey->pkey.rsa, 
padding) == cryptedlen);
break;
default:
php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!");

}



if (successful) {
zval_dtor(crypted);
cryptedbuf[cryptedlen] = '\0';
ZVAL_STRINGL(crypted, (char *)cryptedbuf, cryptedlen, 0);
cryptedbuf = NULL;
RETVAL_TRUE;
}
if (keyresource == -1) {
EVP_PKEY_free(pkey);
}
if (cryptedbuf) {
efree(cryptedbuf);
}
}
/* }}} */

说明

(gdb) p cryptedlen 
$9 = 512
(gdb) p cryptedbuf 
$10 = (unsigned char *) 0x7ffff7fc7020 "\020p\374\367\377\177"
(gdb) p pkey->type 
$11 = 6


(gdb) p cryptedbuf 
$15 = (unsigned char *) 0x7ffff7fc7020 "Vp\235\v\023\241\354}\243\374\351\371r\tQ\301\367\236\344\313\020\266y\243\\\257d\006[\034\260\262\306\344\336=8\266\004\217\260R\322%\035\060L3m\265+\355\347\325\330\021l0i$}a\270\213Cq\341\212\01

6\245\275\274\061\250I\036\372\066\242\334p\026\211t7m#\220]>C\245\334M\254\267:\317\342\266&;\236\027\202\346Y\v\031V\031\177\321\035m\277\203\252\061\250JF\f\212L\331\"U?\242\b\016\323\017\r7F\256t=~7\020M\267\210s\\C/\360\333E\330\372
J\241\003a\200\266Y\316\312\307R\200\371\367\270\204\362\260\247\326 \240\331sLJ\330\340\301\t\212\240\034\215", <incomplete sequence \336>...
(gdb) p successful
$16 = 1


继续的demo代码

char data[] = "appkey=111&val=45678";
int data_len = strlen(data);

int cryptedlen;
unsigned char *cryptedbuf;

cryptedlen = EVP_PKEY_size(pkey);
cryptedbuf = malloc(cryptedlen + 1);


long padding = RSA_PKCS1_PADDING;
switch (pkey->type) {
case EVP_PKEY_RSA:
case EVP_PKEY_RSA2:
// ok
successful = (RSA_public_encrypt(data_len, 
(unsigned char *)data, 
cryptedbuf, 
pkey->pkey.rsa, 
padding) == cryptedlen);
break;
default:
{
//php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!");
printf("pkey->type:%d is not support \n",pkey->type);
}
}


if(successful)
{
char base64[4096];
base64_encode( cryptedbuf, base64, cryptedlen );
printf("%s",base64);
printf("\r\n----base64 lenth:%d\r\n",strlen(base64));
printf("\r\nsuccessful!\r\n");
}
else
{
printf("failed!!");
}

demo代码基本差不多,剩下的就是封装一个so库给lua使用

注意编译的时候添加参数

gcc -g -o test  demo_open_ssl.c -lssl -lcrypto -O3 -DUSE_MMAP -fPIC


cgdb 调试php就结束了.

效率比较

[root@hadoop test_openssl]# time php index.php 

xxx 结果省略

real         0m0.014s
user 0m0.009s
sys         0m0.004s


[root@hadoop libOpenSSLPaySign]# time luajit test.lua 
xxx 结果省略
real         0m0.005s
user 0m0.003s
sys         0m0.003s

luajit调用so库的性能居然比php的性能更高. 


其实解决问题的本质很简单,以及动机也简单,

关键是看一个人的主动性以及一个人的积极性, 再简单的问题,针对某些习惯打酱油的人是不可能往复杂的地方去推荐.

单一的功能中尚且如此,谈何高效团队合作,高效沟通. 


相关文章推荐

PHP使用Redis+Lua脚本操作的注意事项

以前只是简单的用下 Reids 存点数据而已,最近尝试优化性能,做了些测试才发现很多以前完全忽略的问题,总结在下面:一、Redis的一般新手注意事项: 连接本地Reids时,host 要填写 127....

lua处理PHP序列化过后的数据为table

代码来自互联网,稍有修改: -- unserialize.lua -- author:(from internet) --[[ @file Lua port of PHP serializ...
  • hwhjava
  • hwhjava
  • 2015年07月17日 18:28
  • 1138

【案例】使用PHP的内置函数,通过DES算法对数据加密和解密

PHP中内置了一个功能强大的函数库,即Mcrypt。     其实,mcrypt本身就提供了强大的加密解密方法,并且支持很多流行的公开的加密算法,如DES,  TripleDES, Blowfish...

lua 与 php 通过AES数据加密进行通讯

最近公司有款《围住神经猫》的微信小游戏火爆的不行!公司又决定开发一系列的神经猫的小游戏,于是,我被拉过来了。 后来使用cocos-2dx 开发一款小游戏,客户端用的是lua脚本,为了服务器与客户端交...

php加密函数使用

代码效果图:   header("content-type: text/html; charset=utf8" ) ; $str="xiaokai"; /*md5加密43af19c4...

php页面代码加密函数

  • 2013年05月26日 18:04
  • 922B
  • 下载

windows下用c写php扩展(加密解密php源代码)(1)

首先用hello world试手一下。 下载php源码包,ext目录就是扩展目录了里面有2个重要的文件是ext_skel以及ext_skel_win32.php. 下载cygwin.有了这个就可以...
  • bjash
  • bjash
  • 2013年03月27日 17:07
  • 747

php加密函数

  • 2014年10月17日 14:18
  • 16KB
  • 下载

js packer PHP版,加密js源代码文件

  • 2009年03月30日 19:07
  • 17KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:调试php源代码,重写加密函数,封装接口供lua使用
举报原因:
原因补充:

(最多只允许输入30个字)