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

相关推荐
Lazy Dave11 天前
gmssl私钥文件格式
网络安全·ssl·openssl
沉在嵌入式的鱼1 个月前
RK3588移植Openssl库
linux·rk3588·openssl
黑屋里的马1 个月前
ssl相关命令生成证书
服务器·网络·ssl·openssl·gmssl
fangeqin1 个月前
ubuntu源码安装python3.13遇到Could not build the ssl module!解决方法
linux·python·ubuntu·openssl
API开发2 个月前
苹果芯片macOS安装版Homebrew(亲测) ,一键安装node、python、vscode等,比绿色软件还干净、无污染
vscode·python·docker·nodejs·openssl·brew·homebrew
码农不惑2 个月前
Rust使用tokio(二)HTTPS相关
https·rust·web·openssl
liulilittle2 个月前
通过高级处理器硬件指令集AES-NI实现AES-256-CFB算法并通过OPENSSL加密验证算法正确性。
linux·服务器·c++·算法·安全·加密·openssl
liulilittle2 个月前
OpenSSL 的 AES-NI 支持机制
linux·运维·服务器·算法·加密·openssl·解密
liulilittle2 个月前
通过高级处理器硬件指令集AES-NI实现AES-256-CFB算法。
linux·服务器·c++·算法·安全·加密·openssl
花花少年2 个月前
Ubuntu系统下交叉编译openssl
openssl·交叉编译