代码改变世界

[Windows编程笔记]HASH值的计算

2021-09-24 09:01  rnss  阅读(1164)  评论(0编辑  收藏  举报

《Windows黑客编程技术详解》学习笔记

HASH就是把任意长度的输入通过HASH算法变换成固定长度的输出,该输出就是HASH值。

函数介绍

// 用于获取特定加密服务提供程序(CSP)内特定密钥容器的句柄,返回的句柄使用选定CSP的CryptoAPI函数。
BOOL CryptAcquireContextA(
  HCRYPTPROV *phProv,  // 指向CSP句柄的指针。当完成CSP时,通过调用CryptReleaseContext函数释放句柄。
  LPCSTR     szContainer,  // NULL
  LPCSTR     szProvider,  // NULL
  DWORD      dwProvType,  // PROV_RSA_AES表示支持RSA、AES、HASH算法
  DWORD      dwFlags  // CRYPT_VERIFYCONTEXT表示程序不需要使用公钥/私钥对,例如只执行HASH和对称加密
);
​
BOOL CryptCreateHash(
  HCRYPTPROV hProv,  // CryptAcquireContextA创建的CSP句柄
  ALG_ID     Algid,  // 要使用的HASH算法,由用户传入
  HCRYPTKEY  hKey,  // NULL
  DWORD      dwFlags,  // NULL
  HCRYPTHASH *phHash  // 新哈希对象的地址
);
​
BOOL CryptHashData(
  HCRYPTHASH hHash,  // CryptCreateHash创建的哈希对象的句柄
  const BYTE *pbData,  // 指向要计算HASH的数据的指针
  DWORD      dwDataLen,  // 要计算的数据的字节数
  DWORD      dwFlags  // 0
);
​
BOOL CryptGetHashParam(
  HCRYPTHASH hHash,  // CryptCreateHash创建的哈希对象的句柄
  DWORD      dwParam,  // 查询类型。可以选择查询HASH结果的大小或HASH值
  BYTE       *pbData,  // 指向接收数据的缓冲区的指针
  DWORD      *pdwDataLen,  // pbData的字节数
  DWORD      dwFlags  // 0
);

 

编码实现

代码来自https://www.jb51.net/books/755116.html

// CryptoApi_Hash_Test.cpp : 定义控制台应用程序的入口点。
//
​
#include <stdio.h>
#include <Windows.h>
#include <tchar.h>
​
​
void ShowError(char *pszText)
{
    char szErr[MAX_PATH] = { 0 };
    ::wsprintf(szErr, "%s Error[%d]\n", pszText, ::GetLastError());
#ifdef _DEBUG
    ::MessageBox(NULL, szErr, "ERROR", MB_OK | MB_ICONERROR);
#endif
}
​
​
BOOL GetFileData(char *pszFilePath, BYTE **ppFileData, DWORD *pdwFileDataLength)
{
    BOOL bRet = TRUE;
    BYTE *pFileData = NULL;
    DWORD dwFileDataLength = 0;
    HANDLE hFile = NULL;
    DWORD dwTemp = 0;
​
    do
    {
        hFile = ::CreateFile(pszFilePath, GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
            FILE_ATTRIBUTE_ARCHIVE, NULL);
        if (INVALID_HANDLE_VALUE == hFile)
        {
            bRet = FALSE;
            ShowError("CreateFile");
            break;
        }
​
        dwFileDataLength = ::GetFileSize(hFile, NULL);
​
        pFileData = new BYTE[dwFileDataLength];
        if (NULL == pFileData)
        {
            bRet = FALSE;
            ShowError("new");
            break;
        }
        ::RtlZeroMemory(pFileData, dwFileDataLength);
​
        ::ReadFile(hFile, pFileData, dwFileDataLength, &dwTemp, NULL);
​
        // 返回
        *ppFileData = pFileData;
        *pdwFileDataLength = dwFileDataLength;
​
    } while (FALSE);
​
    if (hFile)
    {
        ::CloseHandle(hFile);
    }
​
    return bRet;
}
​
​
BOOL CalculateHash(BYTE *pData, DWORD dwDataLength, ALG_ID algHashType, BYTE **ppHashData, DWORD *pdwHashDataLength)
{
    HCRYPTPROV hCryptProv = NULL;
    HCRYPTHASH hCryptHash = NULL;
    BYTE *pHashData = NULL;
    DWORD dwHashDataLength = 0;
    DWORD dwTemp = 0;
    BOOL bRet = FALSE;
​
​
    do
    {
        // 获得指定CSP的密钥容器的句柄
        bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
        if (FALSE == bRet)
        {
            ShowError("CryptAcquireContext");
            break;
        }
​
        // 创建一个HASH对象, 指定HASH算法
        bRet = ::CryptCreateHash(hCryptProv, algHashType, NULL, NULL, &hCryptHash);
        if (FALSE == bRet)
        {
            ShowError("CryptCreateHash");
            break;
        }
​
        // 计算HASH数据
        bRet = ::CryptHashData(hCryptHash, pData, dwDataLength, 0);
        if (FALSE == bRet)
        {
            ShowError("CryptHashData");
            break;
        }
​
        // 获取HASH结果的大小
        dwTemp = sizeof(dwHashDataLength);
        bRet = ::CryptGetHashParam(hCryptHash, HP_HASHSIZE, (BYTE *)(&dwHashDataLength), &dwTemp, 0);
        if (FALSE == bRet)
        {
            ShowError("CryptGetHashParam");
break;
    }
​
// 申请内存
pHashData = new BYTE[dwHashDataLength];
if (NULL == pHashData)
    {
bRet = FALSE;
ShowError("new");
break;
    }
    ::RtlZeroMemory(pHashData, dwHashDataLength);
​
// 获取HASH结果数据
bRet = ::CryptGetHashParam(hCryptHash, HP_HASHVAL, pHashData, &dwHashDataLength, 0);
if (FALSE == bRet)
    {
ShowError("CryptGetHashParam");
break;
    }
​
// 返回数据
*ppHashData = pHashData;
*pdwHashDataLength = dwHashDataLength;
​
    } while (FALSE);
​
// 释放关闭
if (FALSE == bRet)
    {
if (pHashData)
    {
delete[]pHashData;
pHashData = NULL;
    }
    }
if (hCryptHash)
    {
    ::CryptDestroyHash(hCryptHash);
    }
if (hCryptProv)
    {
    ::CryptReleaseContext(hCryptProv, 0);
    }
​
return bRet;
}
​
​
int _tmain(int argc, _TCHAR* argv[])
{
BYTE *pData = NULL;
DWORD dwDataLength = 0;
DWORD i = 0;
BYTE *pHashData = NULL;
DWORD dwHashDataLength = 0;
​
// 读取文件数据
GetFileData("C:\\Users\\Administrator\\Desktop\\Project8.exe", &pData, &dwDataLength);
​
// MD5
CalculateHash(pData, dwDataLength, CALG_MD5, &pHashData, &dwHashDataLength);
printf("MD5[%d]\n", dwHashDataLength);
for (i = 0; i < dwHashDataLength; i++)
    {
printf("%x", pHashData[i]);
    }
printf("\n\n", dwHashDataLength);
if (pHashData)
    {
delete[]pHashData;
pHashData = NULL;
    }
​
// SHA1
CalculateHash(pData, dwDataLength, CALG_SHA1, &pHashData, &dwHashDataLength);
printf("SHA1[%d]\n", dwHashDataLength);
for (i = 0; i < dwHashDataLength; i++)
    {
printf("%x", pHashData[i]);
    }
printf("\n\n", dwHashDataLength);
if (pHashData)
    {
delete[]pHashData;
pHashData = NULL;
    }
​
// SHA256
CalculateHash(pData, dwDataLength, CALG_SHA_256, &pHashData, &dwHashDataLength);
printf("SHA256[%d]\n", dwHashDataLength);
for (i = 0; i < dwHashDataLength; i++)
    {
printf("%x", pHashData[i]);
    }
printf("\n\n", dwHashDataLength);
if (pHashData)
    {
delete[]pHashData;
pHashData = NULL;
    }
​
system("pause");
return 0;
}

 

实现过程

首先写一个GetFileData函数来获取文件数据,其中用了CreateFile函数来打开文件

HANDLE CreateFileA(
  LPCSTR                lpFileName,  // 文件路径和文件名
  DWORD                 dwDesiredAccess,  // GENERIC_READ | GENERIC_WRITE表示读写权限
  DWORD                 dwShareMode,  // FILE_SHARE_READ | FILE_SHARE_WRITE表示共享读写权限
  LPSECURITY_ATTRIBUTES lpSecurityAttributes,  // NULL
  DWORD                 dwCreationDisposition,  // OPEN_EXISTING表示仅当文件存在时打开文件
  DWORD                 dwFlagsAndAttributes,  // FILE_ATTRIBUTE_ARCHIVE表示标记要备份或删除的文件
  HANDLE                hTemplateFile  // NULL
);

 

然后用GetFileSize函数来获取文件长度

DWORD GetFileSize(
  HANDLE  hFile,  // 文件的句柄
  LPDWORD lpFileSizeHigh  // NULL
);

 

然后用ReadFile函数将文件内容读取到新的内存空间

BOOL ReadFile(
  HANDLE       hFile,  // 文件句柄
  LPVOID       lpBuffer,  // 指向接收文件内容的缓冲区的指针
  DWORD        nNumberOfBytesToRead,  // 读取的最大字节数
  LPDWORD      lpNumberOfBytesRead,  // 接收读取的字节数
  LPOVERLAPPED lpOverlapped  // NULL
);

 

GetFileData函数返回了文件内容和文件内容长度。

接着写了CalculateHash函数来计算HASH值,整体上就是上面的4个API函数依次用。首先用CryptAcquireContextA函数获取一个指向CSP句柄的指针,然后用CryptCreateHash函数在CSP中创建一个空的HASH对象并获取对象句柄,并可以指定HASH算法,接着使用CryptHashData函数来计算数据的HASH值,结果存放在HASH对象中,最后使用CryptGetHashParam函数来获取想要的数据,可以获取的数据有三种:HASH算法、HASH值长度、HASH值。获取完HASH值后使用CryptReleaseContext函数释放CSP句柄,使用CryptDestroyHash函数来释放HASH对象句柄。

 

 

 

 

小结

计算HASH值的操作步骤主要是创建空HASH对象,然后将数据添加到HASH对象中并计算HASH值。因为有不同的HASH算法,HASH值长度也不同,所以在调用CryptGetHashParam函数获取HASH值之前,应先获取HASH值的大小,以便申请足够的缓冲区。