I am trying to implement self integrity check in MFC exe.
Ref : - Tamper Aware and Self Healing Code : Codeproject
I dumped text section
ofstream myfile;
myfile.open ("textsec.bin");
myfile.write((const char*)pCodeStart,dwCodeSize);
myfile.close();
every time its different so hash for it is also different. Please Help
I want to check text i.e. code section in memory should be same as that in file present on disk. So we can be sure that our exe file in memory is not patched. we can use while distributing product exe.
We will get start of code section in memory by
VOID ImageInformation( HMODULE& hModule, PVOID& pVirtualAddress,
PVOID& pCodeStart, SIZE_T& dwCodeSize,
PVOID& pCodeEnd )
{
const UINT PATH_SIZE = 2 * MAX_PATH;
TCHAR szFilename[ PATH_SIZE ] = { 0 };
__try {
/////////////////////////////////////////////////
/////////////////////////////////////////////////
if( 0 == GetModuleFileName( NULL, szFilename, PATH_SIZE ) )
{
std::tcerr << _T("Error Retrieving Process Filename");
std::tcerr << std::endl;
__leave;
}
hModule = GetModuleHandle( szFilename );
if( NULL == hModule )
{
std::tcerr << _T("Error Retrieving Process Module Handle");
std::tcerr << std::endl;
__leave;
}
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
PIMAGE_DOS_HEADER pDOSHeader = NULL;
pDOSHeader = static_cast<PIMAGE_DOS_HEADER>( (PVOID)hModule );
if( pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE )
{
std::tcerr << _T("Error - File is not EXE Format");
std::tcerr << std::endl;
__leave;
}
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
PIMAGE_NT_HEADERS pNTHeader = NULL;
pNTHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(
(PBYTE)hModule + pDOSHeader->e_lfanew );
if( pNTHeader->Signature != IMAGE_NT_SIGNATURE )
{
std::tcerr << _T("Error - File is not PE Format") << std::endl;
__leave;
}
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
PIMAGE_FILE_HEADER pFileHeader = NULL;
pFileHeader = reinterpret_cast<PIMAGE_FILE_HEADER>(
(PBYTE)&pNTHeader->FileHeader );
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL;
pOptionalHeader = reinterpret_cast<PIMAGE_OPTIONAL_HEADER>(
(PBYTE)&pNTHeader->OptionalHeader );
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
if( IMAGE_NT_OPTIONAL_HDR32_MAGIC !=
pNTHeader->OptionalHeader.Magic )
{
std::tcerr << _T("Error - File is not 32 bit") << std::endl;
__leave;
}
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
pSectionHeader = reinterpret_cast<PIMAGE_SECTION_HEADER>(
(PBYTE)&pNTHeader->OptionalHeader +
pNTHeader->FileHeader.SizeOfOptionalHeader );
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
const CHAR TEXT[] = ".text";
const CHAR BSSTEXT[] = ".textbss";
UINT nSectionCount = pNTHeader->FileHeader.NumberOfSections;
CHAR szSectionName[ IMAGE_SIZEOF_SHORT_NAME + 1 ];
szSectionName[ IMAGE_SIZEOF_SHORT_NAME ] = '\0';
for( UINT i = 0; i < nSectionCount; i++ )
{
memcpy( szSectionName, pSectionHeader->Name,
IMAGE_SIZEOF_SHORT_NAME );
if( 0 == strncmp( TEXT, szSectionName,
IMAGE_SIZEOF_SHORT_NAME ) )
{
std::tcout << std::endl;
break;
}
pSectionHeader++;
}
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
if( 0 != strncmp( TEXT, szSectionName, IMAGE_SIZEOF_SHORT_NAME ) )
{
std::tcerr << _T("Error - Unable to locate ");
std::cerr << TEXT;
std::tcerr << _T(" TEXT") << std::endl;
__leave;
}
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
pVirtualAddress = (PVOID)(pSectionHeader->VirtualAddress);
dwCodeSize = pSectionHeader->Misc.VirtualSize;
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
pCodeStart = (PVOID)(((PBYTE)hModule) +
(SIZE_T)((PBYTE)pVirtualAddress) );
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
pCodeEnd = (PVOID)((PBYTE)pCodeStart + dwCodeSize );
}
__except( EXCEPTION_EXECUTE_HANDLER ) {
std::tcerr << std::endl << _T("Caught Exception") << std::endl;
}
}
Now we can calculate hash of code section
CalculateImageHash( pCodeStart, dwCodeSize, cbCalculatedImageHash );
then we compare with cbExpectedImageHash. cbExpectedImageHash is predefined global
if( 0 == memcmp( cbExpectedImageHash, cbCalculatedImageHash,
CryptoPP::SHA224::DIGESTSIZE ) )
{
std::tcout << _T("Image is verified.") << std::endl;
}
In MFC cbCalculatedImageHash is every time different . :( but not in console app.
cbExpectedImageHash and cbCalculatedImageHash are globel .
here is full code of my dialog
// UserAppDlg.cpp : implementation file
//
#include "stdafx.h"
#include "UserApp.h"
#include "UserAppDlg.h"
#include "sha.h" // SHA
#include "hex.h" // HexEncoder
#include "files.h" // FileSink
#include "filters.h" // StringSink
#include "gzip.h"
using namespace std;
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CAboutDlg dialog used for App About
// we can add cbExpectedImageHash new values if we get calcualted hash same every
BYTE cbExpectedImageHash[ CryptoPP::SHA224::DIGESTSIZE ] =
{
0xBA, 0x71, 0x29, 0xD8, 0x79, 0x4E, 0xA3, 0x5A, 0x2C, 0xE0
, 0xC5, 0x3A, 0x68, 0x9E, 0xE8, 0x29, 0x09, 0x44, 0xFA, 0x71
, 0xAF, 0xDB,0x3E,0x88,0xAD,0x65,0x67,0x78
};
BYTE cbCalculatedImageHash[ CryptoPP::SHA224::DIGESTSIZE ] ;
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()
// CUserAppDlg dialog
CUserAppDlg::CUserAppDlg(CWnd* pParent /*=NULL*/)
: CDialog(CUserAppDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CUserAppDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CUserAppDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
// CUserAppDlg message handlers
int CUserAppDlg::ImageInformation( HMODULE& hModule, PVOID& pVirtualAddress,
PVOID& pCodeStart, SIZE_T& dwCodeSize,
PVOID& pCodeEnd )
{
const UINT PATH_SIZE = 2 * MAX_PATH;
TCHAR szFilename[ PATH_SIZE ] = { 0 };
__try {
/////////////////////////////////////////////////
/////////////////////////////////////////////////
if( 0 == GetModuleFileName( NULL, szFilename, PATH_SIZE ) )
{
// std::tcerr << _T("Error Retrieving Process Filename");
// std::tcerr << std::endl;
__leave;
}
hModule = GetModuleHandle( szFilename );
if( NULL == hModule )
{
// std::tcerr << _T("Error Retrieving Process Module Handle");
// std::tcerr << std::endl;
__leave;
}
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
PIMAGE_DOS_HEADER pDOSHeader = NULL;
pDOSHeader = static_cast<PIMAGE_DOS_HEADER>( (PVOID)hModule );
if( pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE )
{
// std::tcerr << _T("Error - File is not EXE Format");
// std::tcerr << std::endl;
__leave;
}
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
PIMAGE_NT_HEADERS pNTHeader = NULL;
pNTHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(
(PBYTE)hModule + pDOSHeader->e_lfanew );
if( pNTHeader->Signature != IMAGE_NT_SIGNATURE )
{
// std::tcerr << _T("Error - File is not PE Format") << std::endl;
__leave;
}
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
PIMAGE_FILE_HEADER pFileHeader = NULL;
pFileHeader = reinterpret_cast<PIMAGE_FILE_HEADER>(
(PBYTE)&pNTHeader->FileHeader );
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL;
pOptionalHeader = reinterpret_cast<PIMAGE_OPTIONAL_HEADER>(
(PBYTE)&pNTHeader->OptionalHeader );
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
if( IMAGE_NT_OPTIONAL_HDR32_MAGIC !=
pNTHeader->OptionalHeader.Magic )
{
// std::tcerr << _T("Error - File is not 32 bit") << std::endl;
__leave;
}
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
pSectionHeader = reinterpret_cast<PIMAGE_SECTION_HEADER>(
(PBYTE)&pNTHeader->OptionalHeader +
pNTHeader->FileHeader.SizeOfOptionalHeader );
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
const CHAR TEXT[] = ".text";
const CHAR BSSTEXT[] = ".textbss";
UINT nSectionCount = pNTHeader->FileHeader.NumberOfSections;
CHAR szSectionName[ IMAGE_SIZEOF_SHORT_NAME + 1 ];
szSectionName[ IMAGE_SIZEOF_SHORT_NAME ] = '\0';
for( UINT i = 0; i < nSectionCount; i++ )
{
memcpy( szSectionName, pSectionHeader->Name,
IMAGE_SIZEOF_SHORT_NAME );
if( 0 == strncmp( TEXT, szSectionName,
IMAGE_SIZEOF_SHORT_NAME ) )
{
// std::tcout << std::endl;
break;
}
pSectionHeader++;
}
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
if( 0 != strncmp( TEXT, szSectionName, IMAGE_SIZEOF_SHORT_NAME ) )
{
// std::tcerr << _T("Error - Unable to locate ");
// std::cerr << TEXT;
// std::tcerr << _T(" TEXT") << std::endl;
__leave;
}
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
pVirtualAddress = (PVOID)(pSectionHeader->VirtualAddress);
dwCodeSize = pSectionHeader->Misc.VirtualSize;
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
pCodeStart = (PVOID)(((PBYTE)hModule) +
(SIZE_T)((PBYTE)pVirtualAddress) );
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
pCodeEnd = (PVOID)((PBYTE)pCodeStart + dwCodeSize );
}
__except( EXCEPTION_EXECUTE_HANDLER ) {
// std::tcerr << std::endl << _T("Caught Exception") << std::endl;
}
return 0;
}
void CUserAppDlg::CalculateImageHash( PVOID pCodeStart, SIZE_T dwCodeSize,
PBYTE pcbDigest )
{
CryptoPP::SHA224 hash;
hash.Update( (PBYTE)pCodeStart, dwCodeSize );
hash.Final( pcbDigest );
}
BOOL CUserAppDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
HMODULE hModule = NULL;
PVOID pVirtualAddress = NULL;
PVOID pCodeStart = NULL;
PVOID pCodeEnd = NULL;
SIZE_T dwCodeSize = 0;
ImageInformation( hModule, pVirtualAddress, pCodeStart,
dwCodeSize, pCodeEnd );
ofstream myfile;
myfile.open ("textsec.bin"); //textsec.bin changes everytime
myfile.write((const char*)pCodeStart,dwCodeSize);
myfile.close();
CalculateImageHash( pCodeStart, dwCodeSize, cbCalculatedImageHash ); //cbCalculatedImageHash changes everytime
myfile.open ("CalculateImageHash.bin");
myfile.write((const char*)cbCalculatedImageHash,CryptoPP::SHA224::DIGESTSIZE);
myfile.close();
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
return TRUE; // return TRUE unless you set the focus to a control
}
void CUserAppDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CUserAppDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CUserAppDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}