openssl3.2 - exp - calc PE file checksum and SHA3-512

openssl3.2 - exp - calc PE file checksum and SHA3-512

概述

想在程序中, 对自身的PE内容算校验和和HASH, 然后送给服务端判断PE文件是否被修改了.

前几天, 看了一个资料, 里面有算PE校验和的实现. 迁移到自己工程.

但是没有算HASH, 正好已经将openssl官方demo过了一遍, 有个官方demo正好是对buffer算hash(openssl3.2 - 官方demo学习 - encode - rsa_encode.c), 也迁移到自己工程.

程序中释放openssl资源时, 开始判断条件写错了. 如下:

c 复制代码
    if (NULL == _ossl_lib_ctx)
    {
        OSSL_LIB_CTX_free(_ossl_lib_ctx);
        _ossl_lib_ctx = NULL;
    }

导致有内存泄漏.

但是我前几天做了openssl3.2检测内存泄漏的实验(openssl3.2 - crypto-mdebug被弃用后, 内存泄漏检查的替代方法), 非常好使.

现在我的工程模板中都加入了内存检测的实现, 程序跑完, 退出时, 如果有内存泄漏, 直接断言, 然后用代码块注释法, 可以很快的定位修复内存泄漏问题.

c 复制代码
void* my_CRYPTO_malloc(size_t num, const char* file, int line)
{
	void* p = NULL;
	It_mem_hook it;
	CMemHookRec* rec = NULL;

	p = malloc(num);
	if (NULL != p)
	{
		it = g_mem_hook_map.find((uint64_t)p);
		if (it != g_mem_hook_map.end()) {
			// printf("find key\n");
			assert(false);
		}
		else {
			// printf("not find key\n");
			rec = new CMemHookRec();
			if (NULL != rec)
			{
				rec->rec_sn = ++g_u64_malloc_cnt_all;
				// 观察UI上显示的内存分配记录的序号, 然后在具体序号上下断点, 然后跟进库里面, 再跟出到自己的应用代码处, 就基本知道是啥没释放:) 
				if (2232 == rec->rec_sn)
				{
					rec->rec_sn = rec->rec_sn; // 如果哪里泄漏了, 可以单步到openssl库代码中
					// 如果是自己没调用释放函数, 用代码块的注释排除法, 很快能确定问题.
				}

俺居然预判了可能会发生内存泄漏时如何检测的场景, 提前将检测措施搞定了. 真机智啊:P

笔记

main.cpp

c 复制代码
/*!
* \file main.cpp
* \note openssl3.2 - exp - calc PE file checksum and SHA3-512
*/

#include "my_openSSL_lib.h"
#include <openssl/crypto.h>
#include <openssl/bio.h>

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#include "CMemHookRec.h"

#include <imagehlp.h>
#pragma comment(lib, "imagehlp.lib")

#include "CPeFileCheck.h"

void my_openssl_app();

const char* psz_argv0 = NULL;

int main(int argc, char** argv)
{
	psz_argv0 = argv[0];

	setvbuf(stdout, NULL, _IONBF, 0); // 清掉stdout缓存, 防止调用printf时阻塞
	mem_hook();

	my_openssl_app();

	mem_unhook();

	return 0;

	/*! run result
	PE calc ok
	PE checksum old  = 0x0
	PE checksum calc = 0x23776
	SHA3 512 len = 64
	SHA3 512 data below:
	0C F4 16 D4 4E 2A 9B 20
	6A 9C B4 22 4F A8 3D 19
	25 D8 6E 7F 7C 45 20 6B
	70 78 77 4B FF A6 94 B5
	D1 EE 16 EB 6D 0A B3 97
	19 0B 1D 7D EE 63 0A D6
	1D 01 F9 02 1D 93 2C 91
	F5 00 39 CC 82 9D 65 92

	END
	free map, g_mem_hook_map.size() = 0
	*/
}

void my_openssl_app()
{
	CPeFileCheck* pe = NULL;
	DWORD dwPeCheckSum = 0;
	bool b_rc = false;

	DWORD dwPeCheckSumOrg = 0;
	DWORD dwPeCheckSumCalc = 0;

	uint8_t* pHash = NULL;
	int HashLen = 0;

	int i = 0;
	int col_cnt = 0;

	do {
		pe = new CPeFileCheck();
		if (NULL == pe)
		{
			break;
		}

		// 必须是全路径名称
		b_rc = pe->PE_calc(psz_argv0);
		if (!b_rc)
		{
			printf("error\n");
		}
		else {
			printf("PE calc ok\n");

			if (pe->get_PE_checkSum(dwPeCheckSumOrg, dwPeCheckSumCalc))
			{
				printf("PE checksum old  = 0x%X\n", dwPeCheckSumOrg);
				printf("PE checksum calc = 0x%X\n", dwPeCheckSumCalc);
			}

			if (pe->get_PE_Hash_SHA3_512(&pHash, HashLen))
			{
				printf("SHA3 512 len = %d\n", HashLen);

				printf("SHA3 512 data below:\n");
				for (i = 0; i < HashLen; i++)
				{
					printf("%2.2X", pHash[i]);
					if (8 == ++col_cnt)
					{
						col_cnt = 0;
						printf("\n");
					}
					else {
						printf(" ");
					}
				}
			}
		}
	} while (false);
		
	if (NULL != pe)
	{
		delete pe;
		pe = NULL;
	}

	printf("\nEND\n");
}

CPeFileCheck.h

c 复制代码
/*!
\file CPeFileCheck.h
*/

#ifndef __CPEFILECHECK_H__
#define __CPEFILECHECK_H__

#include "my_openSSL_lib.h"
#include <cstdint>

#ifndef BYTE_ORDER
#define LITTLE_ENDIAN    1234
#define BIG_ENDIAN       4321
#define BYTE_ORDER       LITTLE_ENDIAN
#endif /* BYTE_ORDER */

#if BYTE_ORDER == BIG_ENDIAN
#define LE_UINT16(x) ((((x) >> 8) & 0x00FF) | \
                     (((x) << 8) & 0xFF00))
#define LE_UINT32(x) (((x) >> 24) | \
                     (((x) & 0x00FF0000) >> 8) | \
                     (((x) & 0x0000FF00) << 8) | \
                     ((x) << 24))
#else
#define LE_UINT16(x) (x)
#define LE_UINT32(x) (x)
#endif /* BYTE_ORDER == BIG_ENDIAN */

#define SIZE_64K 65536       /* 2^16 */
#define SIZE_16M 16777216    /* 2^24 */

#define GET_UINT8_LE(p) ((const u_char *)(p))[0]

#define GET_UINT16_LE(p) (uint16_t)(((const u_char *)(p))[0] | \
                                   (((const u_char *)(p))[1] << 8))

#define GET_UINT32_LE(p) (uint32_t)(((const u_char *)(p))[0] | \
                                   (((const u_char *)(p))[1] << 8) | \
                                   (((const u_char *)(p))[2] << 16) | \
                                   (((const u_char *)(p))[3] << 24))

#define PUT_UINT8_LE(i, p) ((u_char *)(p))[0] = (u_char)((i) & 0xff);

#define PUT_UINT16_LE(i,p) ((u_char *)(p))[0] = (u_char)((i) & 0xff); \
                           ((u_char *)(p))[1] = (u_char)(((i) >> 8) & 0xff)

#define PUT_UINT32_LE(i,p) ((u_char *)(p))[0] = (u_char)((i) & 0xff); \
                           ((u_char *)(p))[1] = (u_char)(((i) >> 8) & 0xff); \
                           ((u_char *)(p))[2] = (u_char)(((i) >> 16) & 0xff); \
                           ((u_char *)(p))[3] = (u_char)(((i) >> 24) & 0xff)

class CPeFileCheck
{
public:
	CPeFileCheck();
	virtual ~CPeFileCheck();

public:
	bool PE_calc(const char* pszFilePathName);

	bool get_PE_checkSum(DWORD& dwPECheckSumOrg, DWORD& dwPECheckSumCalc);
	bool get_PE_Hash_SHA3_512(uint8_t** ppdata, int& len);

private:
	uint32_t get_file_size(const char* infile);
	uint8_t* map_file(const char* infile, const size_t size);
	void unmap_file(uint8_t* indata);

	bool is_PE_format_ok();
	bool calc_PE_checksum(DWORD& dwPeCheckSumCalc);
	bool calc_hash_SHA3_512();

private:
	uint32_t m_u32_file_size;
	uint8_t* m_pu8_map_file;
	DWORD m_dwPeCheckSumOrg;
	DWORD m_dwPeCheckSumCalc;

	uint32_t m_header_size;
	uint32_t m_pe32plus;
	uint32_t m_magic;

	uint32_t m_nrvas;
	uint32_t m_sigpos;
	uint32_t m_siglen;

	unsigned int m_digest_length;
	uint8_t m_ary_digest_value[1024];

	bool m_b_calc_ok;
};

#endif // #ifndef __CPEFILECHECK_H__

CPeFileCheck.cpp

c 复制代码
//! \file CPeFileCheck.cpp

#include "CPeFileCheck.h"
#include <sys/stat.h>

#include "my_openSSL_lib.h"
#include <openssl/crypto.h>
#include <openssl/bio.h>

#include <openssl/err.h>
#include <openssl/evp.h>
#include <cassert>

CPeFileCheck::CPeFileCheck()
{
    m_u32_file_size = 0;
    m_pu8_map_file = NULL;

    m_dwPeCheckSumOrg = 0;
    m_dwPeCheckSumCalc = 0;

    m_header_size = 0;
    m_pe32plus = 0;
    m_magic = 0;

    m_nrvas = 0;
    m_sigpos = 0;
    m_siglen = 0;

    m_digest_length = 0;
    memset(m_ary_digest_value, 0, sizeof(m_ary_digest_value));

    m_b_calc_ok = false;
}

CPeFileCheck::~CPeFileCheck()
{
    unmap_file(this->m_pu8_map_file);
}

bool CPeFileCheck::get_PE_checkSum(DWORD& dwPECheckSumOrg, DWORD& dwPECheckSumCalc)
{
    dwPECheckSumOrg = this->m_dwPeCheckSumOrg;
    dwPECheckSumCalc = this->m_dwPeCheckSumCalc;
    return m_b_calc_ok;
}

bool CPeFileCheck::get_PE_Hash_SHA3_512(uint8_t** ppdata, int& len)
{
    bool b_rc = false;
    do {
        if (NULL == ppdata)
        {
            break;
        }

        *ppdata = this->m_ary_digest_value;
        len = this->m_digest_length;

        b_rc = m_b_calc_ok;
    } while (false);
    return b_rc;
}

bool CPeFileCheck::PE_calc(const char* pszFilePathName)
{
	bool b_rc = false;

	do {
		if (NULL == pszFilePathName)
		{
			break;
		}

        m_b_calc_ok = false;

        if (NULL != m_pu8_map_file)
        {
            unmap_file(m_pu8_map_file);
        }

        m_u32_file_size = get_file_size(pszFilePathName);
        m_pu8_map_file = map_file(pszFilePathName, m_u32_file_size);
        if (NULL == m_pu8_map_file)
        {
            break;
        }

        if (!is_PE_format_ok())
        {
            break;
        }

        if (!calc_PE_checksum(m_dwPeCheckSumCalc))
        {
            break;
        }

        if (!calc_hash_SHA3_512())
        {
            break;
        }

        m_b_calc_ok = true;
        b_rc = true;
	} while (false);

	return b_rc;
}

bool CPeFileCheck::calc_hash_SHA3_512()
{
    //! \ref https://blog.csdn.net/LostSpeed/article/details/135581192
    
    bool b_rc = false;

    OSSL_LIB_CTX* _ossl_lib_ctx;
    int ret = 0;
    const char* _psz_option_properties = NULL;
    EVP_MD* _evp_md = NULL;
    EVP_MD_CTX* _evp_md_ctx = NULL;
    unsigned int digest_length;
    uint8_t* _p_digest_value = NULL;

    do {
        _ossl_lib_ctx = OSSL_LIB_CTX_new();
        if (NULL == _ossl_lib_ctx) {
            // fprintf(stderr, "OSSL_LIB_CTX_new() returned NULL\n");
            break;
        }

        /*
         * Fetch a message digest by name
         * The algorithm name is case insensitive.
         * See providers(7) for details about algorithm fetching
         */
        _evp_md = EVP_MD_fetch(_ossl_lib_ctx,"SHA3-512", _psz_option_properties);
        if (NULL == _evp_md) {
            // fprintf(stderr, "EVP_MD_fetch could not find SHA3-512.");
            break;
        }

        /* Determine the length of the fetched digest type */
        digest_length = EVP_MD_get_size(_evp_md);
        if (digest_length <= 0) {
            // fprintf(stderr, "EVP_MD_get_size returned invalid size.\n");
            break;
        }

        _p_digest_value = (uint8_t*)OPENSSL_malloc(digest_length);
        if (_p_digest_value == NULL) {
            // fprintf(stderr, "No memory.\n");
            break;
        }

        /*
         * Make a message digest context to hold temporary state
         * during digest creation
         */
        _evp_md_ctx = EVP_MD_CTX_new();
        if (NULL == _evp_md_ctx) {
            // fprintf(stderr, "EVP_MD_CTX_new failed.\n");
            break;
        }

        /*
         * Initialize the message digest context to use the fetched
         * digest provider
         */
        if (EVP_DigestInit(_evp_md_ctx, _evp_md) != 1) {
            // fprintf(stderr, "EVP_DigestInit failed.\n");
            break;
        }

        /* Digest parts one and two of the soliloqy */
        if (EVP_DigestUpdate(_evp_md_ctx, this->m_pu8_map_file, this->m_u32_file_size) != 1) {
            // fprintf(stderr, "EVP_DigestUpdate(hamlet_1) failed.\n");
            break;
        }

        if (EVP_DigestFinal(_evp_md_ctx, _p_digest_value, &digest_length) != 1) {
            // fprintf(stderr, "EVP_DigestFinal() failed.\n");
            break;
        }

        // hash in _p_digest_value[], len = digest_length
        if (digest_length > sizeof(m_ary_digest_value))
        {
            assert(false);
            break;
        }

        m_digest_length = digest_length;
        memcpy(m_ary_digest_value, _p_digest_value, digest_length);

        b_rc = true;
    } while (false);

    // 咱这次做的openssl内存泄漏检查的措施挺NB的, 如果下面的指针判断写错了(e.g. if (NULL == _evp_md_ctx) { ... }), 导致内存泄漏, 程序结束时, 就能有断言:P

    /* OpenSSL free functions will ignore NULL arguments */
    if (NULL != _evp_md_ctx)
    {
        EVP_MD_CTX_free(_evp_md_ctx);
        _evp_md_ctx = NULL;
    }
    
    if (NULL != _p_digest_value)
    {
        OPENSSL_free(_p_digest_value);
        _p_digest_value = NULL;
    }
    
    if (NULL != _evp_md)
    {
        EVP_MD_free(_evp_md);
        _evp_md = NULL;
    }
    
    if (NULL != _ossl_lib_ctx)
    {
        OSSL_LIB_CTX_free(_ossl_lib_ctx);
        _ossl_lib_ctx = NULL;
    }

    return b_rc;
}

bool CPeFileCheck::is_PE_format_ok()
{
    bool b_rc = false;

    do {
        if (0 != memcmp(m_pu8_map_file, "MZ", 2)) {
            break;
        }

        if (this->m_u32_file_size < 64) {
            // printf("Corrupt DOS file - too short\n");
            break;
        }

		/* SizeOfHeaders field specifies the combined size of an MS-DOS stub, PE header,
         * and section headers rounded up to a multiple of FileAlignment.
         * SizeOfHeaders must be < filesize and cannot be < 0x0000002C (44) in Windows 7
         * because of a bug when checking section names for compatibility purposes */
		m_header_size = GET_UINT32_LE(this->m_pu8_map_file + 60);
		if (m_header_size < 44 || m_header_size > this->m_u32_file_size) {
			// printf("Unexpected SizeOfHeaders field: 0x%08X\n", header_size);
            break;
		}

        if (this->m_u32_file_size < m_header_size + 176) {
            // printf("Corrupt PE file - too short\n");
            break;
        }

        if (0 != memcmp(this->m_pu8_map_file + m_header_size, "PE\0\0", 4)) {
            // printf("Unrecognized DOS file type\n");
            break;
        }

        /* Magic field identifies the state of the image file. The most common number is
        * 0x10B, which identifies it as a normal executable file,
        * 0x20B identifies it as a PE32+ executable,
        * 0x107 identifies it as a ROM image (not supported) */
        m_magic = GET_UINT16_LE(this->m_pu8_map_file + m_header_size + 24);
        if (m_magic == 0x20b) {
            m_pe32plus = 1;
        }
        else if (m_magic == 0x10b) {
            m_pe32plus = 0;
        }
        else {
            // printf("Corrupt PE file - found unknown magic %04X\n", magic);
            break;
        }

        /* The image file checksum */
        this->m_dwPeCheckSumOrg = GET_UINT32_LE(this->m_pu8_map_file + m_header_size + 88);

        /* NumberOfRvaAndSizes field specifies the number of data-directory entries
         * in the remainder of the optional header. Each describes a location and size. */
        m_nrvas = GET_UINT32_LE(this->m_pu8_map_file + m_header_size + 116 + m_pe32plus * 16);
        if (m_nrvas < 5) {
            // printf("Can not handle PE files without certificate table resource\n");
            break;
        }

        /* Certificate Table field specifies the attribute certificate table address (4 bytes) and size (4 bytes) */
        m_sigpos = GET_UINT32_LE(this->m_pu8_map_file + m_header_size + 152 + m_pe32plus * 16);
        m_siglen = GET_UINT32_LE(this->m_pu8_map_file + m_header_size + 152 + m_pe32plus * 16 + 4);

        /* Since fix for MS Bulletin MS12-024 we can really assume
           that signature should be last part of file */
        if (((m_sigpos != 0) || (m_siglen != 0)) &&
            ((m_sigpos == 0) || (m_siglen == 0) || (m_sigpos >= this->m_u32_file_size) || ((m_sigpos + m_siglen) != this->m_u32_file_size))) {
            // printf("Ignoring PE signature not at the end of the file\n");
            m_sigpos = 0;
            m_siglen = 0;
        }

        b_rc = true;
    } while (false);

    return b_rc;
}

bool CPeFileCheck::calc_PE_checksum(DWORD& dwPeCheckSumCalc)
{
    bool b_rc = false;
    uint32_t n = 0, checkSum = 0, offset = 0;
    BIO* bio = BIO_new(BIO_s_mem());
    uint8_t* buf = (uint8_t*)OPENSSL_malloc(SIZE_64K);

    do {
        dwPeCheckSumCalc = 0;

        /* calculate the checkSum */
        while (n < this->m_u32_file_size) {
            size_t i, written, nread;
            size_t left = m_u32_file_size - n;
            unsigned short val = 0;

            if (left > SIZE_64K)
                left = SIZE_64K;
            if (!BIO_write_ex(bio, this->m_pu8_map_file + n, left, &written))
            {
                break;
            }

            // 如果只想移动指针, 就用BIO_seek
            (void)BIO_seek(bio, 0);
            // BIO_reset(bio); // BIO_reset对于不是只读的BIO, 会清掉内容

            n += (uint32_t)written;
            if (!BIO_read_ex(bio, buf, written, &nread))
            {
                break;
            }

            for (i = 0; i < nread / 2; i++) {
                val = LE_UINT16(buf[i]);
                if (offset == m_header_size + 88
                    || offset == m_header_size + 90) {
                    /* The image file checksum */
                    val = 0;
                }
                checkSum += val;
                checkSum = LOWORD(LOWORD(checkSum) + HIWORD(checkSum));
                offset += 2;
            }
        }

        checkSum = LOWORD(LOWORD(checkSum) + HIWORD(checkSum));
        checkSum += offset;

        dwPeCheckSumCalc = checkSum;
        b_rc = true;
    } while (false);

    if (NULL != buf)
    {
        OPENSSL_free(buf);
        buf = NULL;
    }
    
    if (NULL != bio)
    {
        BIO_free(bio);
        bio = NULL;
    }
    
    return b_rc;
}

uint32_t CPeFileCheck::get_file_size(const char* infile)
{
    int ret = 0;

    struct _stat64 st;
    ret = _stat64(infile, &st); // 必须是全路径名称, 否则返回-1

    if (ret) {
        // printf("Failed to open file: %s\n", infile);
        return 0;
    }

    if (st.st_size < 4) {
        // printf("Unrecognized file type - file is too short: %s\n", infile);
        return 0;
    }
    if (st.st_size > UINT32_MAX) {
        // printf("Unsupported file - too large: %s\n", infile);
        return 0;
    }

    return (uint32_t)st.st_size;
}

uint8_t* CPeFileCheck::map_file(const char* infile, const size_t size)
{
    uint8_t* indata = NULL;

    HANDLE fhandle, fmap;
    (void)size;
    fhandle = CreateFileA(infile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
    if (fhandle == INVALID_HANDLE_VALUE) {
        return NULL;
    }
    fmap = CreateFileMapping(fhandle, NULL, PAGE_READONLY, 0, 0, NULL);
    CloseHandle(fhandle);
    if (fmap == NULL) {
        return NULL;
    }
    indata = (uint8_t*)MapViewOfFile(fmap, FILE_MAP_READ, 0, 0, 0);
    CloseHandle(fmap);

    return indata;
}

void CPeFileCheck::unmap_file(uint8_t* indata)
{
    if (!indata)
        return;

    UnmapViewOfFile(indata);
}

END

相关推荐
什么名字都被用了23 天前
编译openssl源码
c++·openssl
toooooop81 个月前
openssl_error_string() 不要依赖错误信息作为逻辑判断
php·openssl
whoarethenext1 个月前
加密认证库openssl初始附带c/c++的使用源码
c语言·网络·c++·openssl
好记忆不如烂笔头abc2 个月前
centos7.9升级OpenSSL 1.1.1
openssl
宁静致远20212 个月前
openssl交叉编译
openssl·嵌入式linux
漫步企鹅2 个月前
【漏洞修复】Android 10 系统源码中的 glibc、curl、openssl、cups、zlib 更新到最新版本
android·glibc·openssl·curl·zlib·漏洞修复·cups
Winter_Sun灬3 个月前
curl库+openssl库windows编译
c++·windows·openssl·curl
ScilogyHunter3 个月前
使用 OpenSSL 构建安全的网络应用
安全·openssl
dreadp3 个月前
使用 OpenSSL 和 Python 实现 AES-256-CBC 加密与解密(安全密钥管理)
python·安全·网络安全·密码学·openssl
初级代码游戏3 个月前
编写一个基于OpenSSL的SSL/TLS服务端(HTTPS)可运行的完整示例
网络协议·https·ssl·openssl·tls