Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

Tamper Aware and Self Healing Code

, 15 Nov 2007
Dynamically Detect Code Alterations and Repair In-Memory Executable Files Using Hashing and Crypto++
architectureselfhealing.zip
Towards Architecture-based Self-Healing Systems.pdf
instructionset.zip
Intel Architecture Software Developer's Manual, Volume 2 Instruction Set Reference Manual.pdf
modelbasedselfhealing.zip
Model-based adaptation for self-healing systems.pdf
selfhealing1.zip
Self Healing 1
selfhealing2.zip
Self Healing 2
selfhealing3.zip
Self Healing 3
selfhealing4.zip
Self Healing 4
selfhealing5.zip
Self Healing 5
selfhealing6.zip
Self Healing 6
selfhealing7.zip
Self Healing 7
selfhealing7relexe.zip
Self Healing 7.exe
systemprogramming.zip
Intel Architecture Software Developer Manual Volume 3 System Programming.pdf
// Self Healing 7.cpp
//

#include "stdafx.h"

#include "sha.h"        // SHA
#include "hex.h"        // HexEncoder
#include "files.h"      // FileSink
#include "filters.h"    // StringSink
#include "gzip.h"

VOID ImageInformation( HMODULE& hModule, PVOID& pVirtualAddress,
                       PVOID& pCodeStart, SIZE_T& dwCodeSize,
                       PVOID& pCodeEnd );
                       
VOID DumpImageInformation( HMODULE hModule, PVOID pVirtualAddress,
                           PVOID pCodeStart, SIZE_T dwCodeSize,
                           PVOID pCodeEnd );
              
VOID CalculateImageHash( PVOID pCodeStart, SIZE_T dwCodeSize,
                         PBYTE pcbDigest );
                         
VOID DumpHash( PBYTE pcbDigest, SIZE_T dwSize, std::string message );

VOID ExportTextImage( const std::string& filename,
                      PVOID pCodeStart, SIZE_T dwCodeSize );
                      
VOID ImportTextImage( const std::string& filename,
                      PBYTE pBuffer, SIZE_T dwBufferSize );                      

VOID HexDump( LPCVOID pcbStartAddress,
              LPCVOID pDisplayBaseAddress = (PVOID)-1,
              DWORD dwSize = DEFAULT_DUMP_SIZE );
              
VOID AlterTextImage( LPVOID pStartAddress, BYTE OpCode );

VOID HealTextImage( LPVOID pStartAddress, LPCVOID pArchivedText, SIZE_T dwSize );

// These values must be Global. Place them inside
//   main(), and you get different code generation
//   after each back patch operation.

#ifdef _DEBUG
BYTE cbExpectedImageHash[ CryptoPP::SHA224::DIGESTSIZE ] =
    { 0xE4,0x32,0xE3,0x3D,0x43,0x0B,0xD3,
      0x37,0xB9,0x5A,0xD3,0x63,0x0E,0xAB,
      0x9C,0x83,0xD9,0x4F,0x69,0xA4,0x02,
      0x7C,0x58,0xA0,0xF5,0xC1,0x09,0x49 };
#else
BYTE cbExpectedImageHash[ CryptoPP::SHA224::DIGESTSIZE ] =
    { 0xEE,0x7B,0xE9,0xD1,0xDE,0xA4,0x1B,
      0x1A,0x64,0xE2,0x7E,0xE5,0xAC,0xD8,
      0xCA,0xC5,0x4F,0xD3,0x60,0x95,0x55,
      0xA4,0x03,0x4D,0xF2,0xD7,0xAA,0x40 };
#endif

BYTE cbCalculatedImageHash[ CryptoPP::SHA224::DIGESTSIZE ];

int _tmain(int argc, _TCHAR* argv[])
{
    HMODULE hModule = NULL;
    PVOID   pVirtualAddress = NULL;
    PVOID   pCodeStart = NULL;
    PVOID   pCodeEnd = NULL;    
    SIZE_T  dwCodeSize = 0;
    
    
    // Set Up - Develop EXE Information
    ImageInformation( hModule, pVirtualAddress, pCodeStart,
                      dwCodeSize, pCodeEnd );                   
    
    // Set Up - Dump Information
    DumpImageInformation( hModule, pVirtualAddress, pCodeStart,
                          dwCodeSize, pCodeEnd );                          
    
    // Set Up - Export .text Section
    std::string filename = "TextImage.gz";      
    ExportTextImage( filename, pCodeStart, dwCodeSize );
    
    // Set Up - Import .text Section
    SIZE_T dwBufferSize = dwCodeSize;
    PBYTE pArchiveBuffer = new BYTE[ dwBufferSize ];
    if( NULL == pArchiveBuffer ) { return -1; }
    ImportTextImage( filename, pArchiveBuffer, dwBufferSize );    
    
    // Step 1: Dump Original .text
    std::tcout << _T("Original TEXT Section") << std::endl;
    HexDump( pCodeStart, pCodeStart, DUMP_SIZE );
    std::tcout << std::endl;
    
    // Step 2: Dump Archived .text
    std::tcout << _T("Archived TEXT Section") << std::endl;
    HexDump( pArchiveBuffer, (LPCVOID)NULL, DUMP_SIZE );
    std::tcout << std::endl;
    
    // Set Up: Calculate Hashes
    CalculateImageHash( pCodeStart, dwCodeSize, cbCalculatedImageHash );
    
    // Step 3: Compare Hashes
    std::tcout << std::endl;
    DumpHash( cbExpectedImageHash, CryptoPP::SHA224::DIGESTSIZE,
              "SHA-224 Expected Image Hash" );    
    DumpHash( cbCalculatedImageHash, CryptoPP::SHA224::DIGESTSIZE,
              "SHA-224 Calculated Image Hash" );   
    
    // Step 3: Compare Hashes
    if( 0 == memcmp( cbExpectedImageHash, cbCalculatedImageHash,
        CryptoPP::SHA224::DIGESTSIZE ) )
    {
        std::tcout << _T("Image is verified.") << std::endl;
    }
    else
    {
        std::tcout << _T("Image has been modified.") << std::endl;
    }
    std::tcout << std::endl;
    std::tcout << "================================" << std::endl;
    
    // Step 4: Alter Bytes
    AlterTextImage( pCodeStart, 0x09 );
    
    // Step 5: Dump Altered .text
    std::tcout << _T("Altered TEXT Section") << std::endl;
    HexDump( pCodeStart, pCodeStart, DUMP_SIZE );
    std::tcout << std::endl;  
    
    // Set Up: Calculate Hashes
    CalculateImageHash( pCodeStart, dwCodeSize, cbCalculatedImageHash );
    
    // Step 6: Compare Hashes
    std::tcout << std::endl;
    DumpHash( cbExpectedImageHash, CryptoPP::SHA224::DIGESTSIZE,
              "SHA-224 Expected Image Hash" );    
    DumpHash( cbCalculatedImageHash, CryptoPP::SHA224::DIGESTSIZE,
              "SHA-224 Calculated Image Hash" );   
    
    // Step 6: Compare Hashes
    if( 0 == memcmp( cbExpectedImageHash, cbCalculatedImageHash,
        CryptoPP::SHA224::DIGESTSIZE ) )
    {
        std::tcout << _T("Image is verified.") << std::endl;
    }
    else
    {
        std::tcout << _T("Image has been modified.") << std::endl;
    }
    std::tcout << std::endl;
    std::tcout << "================================" << std::endl;
    
    // Step 7: Heal the Code
    //   We should not fall through this
    if( 0 != memcmp( cbExpectedImageHash, cbCalculatedImageHash,
        CryptoPP::SHA224::DIGESTSIZE ) )
    {
        HealTextImage( pCodeStart, pArchiveBuffer, dwBufferSize );
    }
    
    // Step 8: Dump Healed .text
    std::tcout << _T("Healed TEXT Section") << std::endl;
    HexDump( pCodeStart, pCodeStart, DUMP_SIZE );
    std::tcout << std::endl;  
    
    // Set Up: Calculate Hashes
    CalculateImageHash( pCodeStart, dwCodeSize, cbCalculatedImageHash );
    
    // Step 9: Compare Hashes
    std::tcout << std::endl;
    DumpHash( cbExpectedImageHash, CryptoPP::SHA224::DIGESTSIZE,
              "SHA-224 Expected Image Hash" );    
    DumpHash( cbCalculatedImageHash, CryptoPP::SHA224::DIGESTSIZE,
              "SHA-224 Calculated Image Hash" );   
    
    // Step 9: Compare Hashes
    if( 0 == memcmp( cbExpectedImageHash, cbCalculatedImageHash,
        CryptoPP::SHA224::DIGESTSIZE ) )
    {
        std::tcout << _T("Image is verified.") << std::endl;
    }
    else
    {
        std::tcout << _T("Image has been modified.") << std::endl;
    }    

    // Cleanup
    if( NULL != pArchiveBuffer ) { delete[] pArchiveBuffer; }
    
    return 0;
}

VOID HealTextImage( LPVOID pStartAddress,
   LPCVOID pArchivedText, SIZE_T dwSize ) 
{
    HANDLE hProcess = NULL;
    BOOL bResult = FALSE;
    SIZE_T dwBytesWritten = 0;

    __try
    {
        hProcess = OpenProcess( PROCESS_VM_OPERATION | PROCESS_VM_WRITE,
                                FALSE, GetCurrentProcessId() );
                                
        if( NULL == hProcess )
        {
            std::tcerr << std::endl;
            std::tcerr << _T("Unable to Open Process");
            std::tcerr << std::endl;
            __leave;           
        }
        
        bResult = WriteProcessMemory( hProcess, pStartAddress,
                        pArchivedText, dwSize, &dwBytesWritten );
                        
        if( FALSE == bResult || dwSize != dwBytesWritten )
        {
            std::tcerr << std::endl;
            std::tcerr << _T("Unable to Alter .text Section");
            std::tcerr << std::endl;     
        }                        
    }

    __except( EXCEPTION_EXECUTE_HANDLER ) {
        std::tcerr << std::endl;
        std::tcerr << _T("Caught Exception in AlterTextImage");
        std::tcerr << std::endl;
    }
    
    if( NULL != hProcess ) { CloseHandle( hProcess ); }
}

VOID AlterTextImage( LPVOID pcbStartAddress, BYTE OpCode )
{
    HANDLE hProcess = NULL;
    BOOL bResult = FALSE;
    SIZE_T dwBytesWritten = 0;

    __try
    {
        hProcess = OpenProcess( PROCESS_VM_OPERATION | PROCESS_VM_WRITE,
                                FALSE, GetCurrentProcessId() );
                                
        if( NULL == hProcess )
        {
            std::tcerr << std::endl;
            std::tcerr << _T("Unable to Open Process");
            std::tcerr << std::endl;
            __leave;           
        }
        
        bResult = WriteProcessMemory( hProcess, pcbStartAddress,
                        &OpCode, sizeof( OpCode ), &dwBytesWritten );
                        
        if( FALSE == bResult || 1 != dwBytesWritten )
        {
            std::tcerr << std::endl;
            std::tcerr << _T("Unable to Alter .text Section");
            std::tcerr << std::endl;     
        }                        
    }

    __except( EXCEPTION_EXECUTE_HANDLER ) {
        std::tcerr << std::endl;
        std::tcerr << _T("Caught Exception in AlterTextImage");
        std::tcerr << std::endl;
    }
    
    if( NULL != hProcess ) { CloseHandle( hProcess ); }
}

VOID ImportTextImage( const std::string& filename,
                      PBYTE pBuffer, SIZE_T dwBufferSize )
{

    try {
        std::string RecoveredTextSection;
        CryptoPP::FileSource( filename.c_str(), true,
            new CryptoPP::Gunzip(
                new CryptoPP::StringSink( RecoveredTextSection )
            ) // Gunzip
        ); // FileSource
    
        if( RecoveredTextSection.length() > dwBufferSize )
        {
            std::tcerr << _T("ImportTextImage: Executing Buffer Overflow");
        }
        
        memcpy( pBuffer, RecoveredTextSection.c_str(), dwBufferSize );       
    }   
    
    catch( CryptoPP::Exception& e )
    {
        std::cerr << e.what() << std:: endl;
    }
    
    catch( ... )
    {
        std::tcerr << _T("Caught Unknown Exception");
        std::tcerr << std:: endl;
    }
}
                      
VOID ExportTextImage( const std::string& filename,
                      PVOID pCodeStart, SIZE_T dwCodeSize )
{
    try {             
        CryptoPP::Gzip zipper(
            new CryptoPP::FileSink (filename.c_str(), true ),
        CryptoPP::Gzip::MAX_DEFLATE_LEVEL ); // Gzip
        
        zipper.Put( (byte*)pCodeStart, dwCodeSize );
        zipper.MessageEnd( );
    }
    
    catch( CryptoPP::Exception& e )
    {
        std::cerr << e.what() << std:: endl;
    }
    
    catch( ... )
    {
        std::tcerr << _T("Caught Unknown Exception");
        std::tcerr << std:: endl;
    }
}

VOID DumpHash( PBYTE pcbDigest, SIZE_T dwSize, std::string message )
{               
    CryptoPP::HexEncoder encoder;
    std::string sink;
    
    encoder.Attach( new CryptoPP::StringSink (sink) );
    encoder.Put( pcbDigest, dwSize );
    encoder.MessageEnd();    
    
    if( 0 != message.length() )
    {
        std::cout << message << std::endl;
    }
    
    std::cout << sink << std::endl;
}   

VOID CalculateImageHash( PVOID pCodeStart, SIZE_T dwCodeSize,
                         PBYTE pcbDigest )
{
    CryptoPP::SHA224 hash;
    
    hash.Update( (PBYTE)pCodeStart, dwCodeSize );
    hash.Final( pcbDigest );    
}                

VOID DumpImageInformation( HMODULE hModule, PVOID pVirtualAddress,
                           PVOID pCodeStart, SIZE_T dwCodeSize,
                           PVOID pCodeEnd )
{
    std::tcout << _T("****************************************************");
    std::tcout << std::endl;
    std::tcout << _T("************* Memory Image Information *************");
    std::tcout << std::endl;
    std::tcout << _T("****************************************************");
    std::tcout << std::endl << std::endl;
    
    std::tcout << _T("         hModule: ");
    std::tcout << HEXADECIMAL_OUTPUT(8);
    std::tcout << hModule << std::endl;
            
    std::tcout << _T(" Virtual Address: ");
    std::tcout << HEXADECIMAL_OUTPUT(8);
    std::tcout << pVirtualAddress << std::endl;  
                    
    std::tcout << _T("     .text Start: ");
    std::tcout << HEXADECIMAL_OUTPUT(8);
    std::tcout << pCodeStart << std::endl;

    std::tcout << _T("      .text Size: ");
    std::tcout << HEXADECIMAL_OUTPUT(8);
    std::tcout << dwCodeSize << std::endl;    

    std::tcout << _T("       .text End: ");
    std::tcout << HEXADECIMAL_OUTPUT(8);
    std::tcout << pCodeEnd << std::endl;
               
    std::tcout << std::endl;
}                           

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;
    }
}

VOID HexDump( LPCVOID pcbStartAddress, LPCVOID pDisplayBaseAddress,
              DWORD dwSize )
{
    /////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////
    std::tcout << HEXADECIMAL_OUTPUT(8);

    /////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////
    DWORD dwPosition = 0;
    const UINT BYTES_PER_LINE = 16;

    if( (PVOID)-1 == pDisplayBaseAddress )
    {
        pDisplayBaseAddress = pcbStartAddress;
    }

    do
    {
        if( 0 == dwPosition % BYTES_PER_LINE )
        {
            std::tcout << HEXADECIMAL_OUTPUT(8);
            std::tcout << (SIZE_T)((PBYTE)pDisplayBaseAddress + dwPosition);
            std::tcout << _T(": ");
        }

        std::tcout << HEXADECIMAL_OUTPUT(2);
        std::tcout << (SIZE_T)(((PBYTE)pcbStartAddress)[ dwPosition ]);
        std::tcout << _T(" ");

        if( 0 == (dwPosition+1) % BYTES_PER_LINE && 0 != dwPosition )
        {
            std::tcout << std::endl;
        }
        else
        if( 0 == (dwPosition+1) % (BYTES_PER_LINE/2) && 0 != dwPosition )
        {
            std::tcout << _T(" ");
        }

    } while( ++dwPosition < dwSize );
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Jeffrey Walton
Systems / Hardware Administrator
United States United States
No Biography provided

| Advertise | Privacy | Mobile
Web03 | 2.8.140926.1 | Last Updated 15 Nov 2007
Article Copyright 2007 by Jeffrey Walton
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid