[Windows編程筆記]HASH值的計算


《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值的大小,以便申請足夠的緩沖區。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM