使用VS自带宏进行排查
#define _CRTDBG_MAP_ALLOC //此宏需要在之前<crtdbg.h>
#include <crtdbg.h>
在代码的开头加上
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
这种方式只能定位到函数地址。
使用openssl自带的函数,开启内存检测时,所有的内存分配会被记录。
内存管理函数
这些函数要起作用,需要在编译openssl的时候开启debug选项
主要函数
- CRYPTO_mem_ctrl
本函数主要用于控制内存分配时,是否记录内存信息。如果不记录内存信息,将不能查找内存泄露。开启内存记录调用CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON),关闭内存记录调用CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_OFF)。一旦CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON)被调用,直到用户调用CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_OFF)前,用户 所有的opessl内存分配都会被记录。
- CRYPTO_is_mem_check_on
查询内存记录标记是否开启。
3) CRYPTO_dbg_malloc
本函数用于分配内存空间,如果内存记录标记开启,则记录用户申请的内存。当需要记录内存信息时,该函数本身也需要申请内存插入哈希表,为了防止递归申请错误,它申请内存记录信息前必须暂时关闭内存记录标记,申请完毕再放开。
4) CRYPTO_dbg_free
释放内存,如果内存记录标记开启,还需要删除哈希表中对应的记录。
5) CRYPTO_mem_leaks
将内存泄露输出到BIO中。
6) CRYPTO_mem_leaks_fp
将内存泄露输出到FILE中(文件或者标准输出),该函数调用了CRYPTO_mem_leaks。
7) CRYPTO_mem_leaks_cb
处理内存泄露,输入参数为用户自己实现的处理内存泄露的函数地址。该函数只需要处理一个内存泄露,openssl通过lh_doall_arg调用用户函数来处理所有记录(泄露的内存)。
使用列子
#define _CRT_SECURE_NO_WARNINGS
//#define _CRTDBG_MAP_ALLOC //此宏需要在之前<crtdbg.h>
//#include <crtdbg.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
//#ifdef _DEBUG
//#ifndef DBG_NEW
//#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
//#define new DBG_NEW
//#endif
//
//#endif // _DEBUG
void InitLeaks()
{
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
}
void LogLeaks()
{
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_OFF);
BIO* bLog = BIO_new_file("D:/leak.log", "w"); //临时关闭。
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
//leaks会自动调用CRYPTO_MEM_CHECK_OFF
CRYPTO_mem_leaks(bLog);//内部有线程锁,调用此方法后,锁被释放,不能再调用CRYPTO_mem_ctrl方法了。
BIO_free(bLog);
}
int testcertreq2();
//https://blog.csdn.net/yinxu2008/article/details/120242857
int main() {
//windows内存泄露问题
//_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
//OPENSSL内存泄露查找
//https://www.bbsmax.com/A/1O5EZM1yd7/
InitLeaks();
testcertreq2();
LogLeaks();
return 0;
}
int testcertreq2() {
X509_REQ* req = NULL;
RSA* rsa = NULL;
BIGNUM* bn = NULL;
int ret = 1;
EVP_PKEY* key = NULL;
X509_NAME* name = NULL;
X509_NAME_ENTRY* entry2 = NULL, * entry = NULL;
do {
req = X509_REQ_new();
X509_REQ_set_version(req, 1);//设置版本
//生成RSA秘钥对
rsa = RSA_new();
bn = BN_new();
BN_set_word(bn, RSA_3);
ret = RSA_generate_key_ex(rsa, 1024, bn, NULL);
if (ret != 1) {
printf("RSA_generate_key_ex error");
break;
}
key = EVP_PKEY_new();
ret = EVP_PKEY_set1_RSA(key, rsa);
if (ret != 1) {
printf("EVP_PKEY_set1_RSA error");
break;
}
//设置签名公钥
ret = X509_REQ_set_pubkey(req, key);
name = X509_NAME_new();
const unsigned char cn[] = { "fdsfsdfsfsf" };
entry = X509_NAME_ENTRY_create_by_NID(NULL, NID_commonName, V_ASN1_UTF8STRING, cn, sizeof(cn) - 1);
if (entry == NULL) {
printf("create NID_commonName entry error");
break;
}
ret = X509_NAME_add_entry(name, entry, 0, 1);
const unsigned char country[] = { "China" };
entry2 = X509_NAME_ENTRY_create_by_NID(NULL, NID_countryName, V_ASN1_UTF8STRING, country, sizeof(country) - 1);
if (entry2 == NULL) {
printf("create NID_commonName entry error");
break;
}
ret = X509_NAME_add_entry(name, entry2, 0, 1);
//设置证书请求者信息
ret = X509_REQ_set_subject_name(req, name);
//成功返回签名的字节数,失败返回0
int len = X509_REQ_sign(req, key, EVP_sha1());
if (len == 0) {
ret = -1;
printf("X509_REQ_sign error");
break;
}
//输出成PEM格式
ret = PEM_write_X509_REQ(stdout, req);
if (ret != 1) {
printf("PEM_write_X509_REQ error");
break;
}
//验证证书请求
ret = X509_REQ_verify(req, key);
if (ret != 1) {
printf("X509_REQ_verify error");
break;
}
} while (0);
//释放你所有NEW的对象
BN_free(bn);
RSA_free(rsa);
EVP_PKEY_free(key);
X509_NAME_free(name);
X509_REQ_free(req);
X509_NAME_ENTRY_free(entry);
X509_NAME_ENTRY_free(entry2);
return ret;
}