实现原理

  • 在拓展层实现代码加密,生成新代码
  • 在拓展层解密代码
    • hook校验
    • opcode混淆
  • 在拓展层执行解密后代码

知识储备

首先,对于一个php文件的执行,我们需要知道其大概的步骤:

  • 基础环境初始化
  • 调用zend_compile_file解析文件生成opcode
  • 调用zend_execute执行生成的opcode

相关函数

  1. static zend_op_array *(*zend_compile_string)(zval *source_string, char *filename TSRMLS_DC);
  1. static zend_op_array *(*zend_compile_string)(zval *source_string, char *filename TSRMLS_DC);
  1. static void zend_execute(zend_op_array *op_array,zval *return_value);

替换PHP默认方法

  1. PHP_MINIT_FUNCTION(decrypt_code)
  2. {
  3. zend_compile_file = decrypt_compile_file;
  4. orig_compile_string = zend_compile_string;
  5. zend_compile_string = decrypt_compile_string;
  6. return SUCCESS;
  7. }
  8. PHP_MSHUTDOWN_FUNCTION(myShut)
  9. {
  10. zend_compile_string = orig_compile_string;
  11. return SUCCESS;
  12. }

我们在php加载拓展的时候,替换了php默认的 zend_compile_fileorig_compile_string。当然,在Easyswoole中实现的执行代码的方式, 不会被这两个函数hook,这个两个可以用来破解纯php层的混淆加密。相关安全问题在注意事项章节讲解。

定义加密方法

  1. PHP_FUNCTION(easy_compiler_encrypt) {
  2. unsigned char *raw_string;
  3. size_t *raw_string_len;
  4. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &raw_string, &raw_string_len) == FAILURE) {
  5. RETURN_NULL();
  6. }
  7. unsigned char *pkcs7 = (unsigned char *)malloc(sizeof(unsigned char*)*PKCS7_MAX_LEN);
  8. memcpy(pkcs7,raw_string,raw_string_len);
  9. size_t after_padding_len = PKCS7Padding(pkcs7,raw_string_len);
  10. struct AES_ctx ctx;
  11. AES_init_ctx_iv(&ctx, AES_KEY, AES_IV_KEY);
  12. AES_CBC_encrypt_buffer(&ctx,pkcs7,after_padding_len);
  13. zend_string *zend_encode_string = zend_string_init(pkcs7,after_padding_len,0);
  14. zend_string *base64;
  15. base64 = php_base64_encode((const unsigned char*)ZSTR_VAL(zend_encode_string),ZSTR_LEN(zend_encode_string));
  16. char *res = ZSTR_VAL(base64);
  17. zend_string_release(base64);
  18. zend_string_release(zend_encode_string);
  19. free(pkcs7);
  20. RETURN_STRING(res);
  21. };

定义解密方法

  1. PHP_FUNCTION(easy_compiler_decrypt) {
  2. unsigned char *base64;
  3. size_t *base64_len;
  4. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &base64, &base64_len) == FAILURE) {
  5. RETURN_NULL();
  6. }
  7. zend_string *encrypt_z_str;
  8. encrypt_z_str = php_base64_decode(base64,base64_len);
  9. size_t encrypt_len = NULL;
  10. encrypt_len = ZSTR_LEN(encrypt_z_str);
  11. unsigned char *pkcs7 = (unsigned char *)malloc(sizeof(unsigned char*)*PKCS7_MAX_LEN);
  12. memcpy(pkcs7,(const char*)ZSTR_VAL(encrypt_z_str),encrypt_len);
  13. struct AES_ctx ctx;
  14. AES_init_ctx_iv(&ctx, AES_KEY, AES_IV_KEY);
  15. AES_CBC_decrypt_buffer(&ctx,pkcs7,encrypt_len);
  16. encrypt_len = PKCS7Cutting(pkcs7,encrypt_len);
  17. zend_string *eval_string = zend_string_init(pkcs7,encrypt_len,0);
  18. zval z_str;
  19. ZVAL_STR(&z_str,eval_string);
  20. zend_op_array *new_op_array;
  21. char *filename = zend_get_executed_filename(TSRMLS_C);
  22. new_op_array = easy_compiler_compile_string(&z_str, filename TSRMLS_C);
  23. if(new_op_array){
  24. zend_try {
  25. zend_execute(new_op_array,return_value);
  26. } zend_catch {
  27. } zend_end_try();
  28. destroy_op_array(new_op_array);
  29. efree(new_op_array);
  30. }
  31. zend_string_release(encrypt_z_str);
  32. zend_string_release(eval_string);
  33. zval_ptr_dtor(&z_str);
  34. free(pkcs7);
  35. };

就是在这一步解析加密后的代码,并执行对应的opcode

更多细节源码

EasySwoole Compiler