static
int 
    _yaraMemMatchCallback
    (
        int message,
        void* message_data,
        void* user_data
    )
{
    YR_RULE* rule = (YR_RULE*)message_data;
    YaraMatchContext* context = (YaraMatchContext*)user_data;
    rSequence event = NULL;

    if( CALLBACK_MSG_RULE_MATCHING == message &&
        NULL != message_data &&
        NULL != user_data )
    {
        if( NULL != ( event = rSequence_new() ) )
        {
            rSequence_addRU32( event, RP_TAGS_PROCESS_ID, context->pid );
            rSequence_addPOINTER64( event, RP_TAGS_BASE_ADDRESS, context->regionBase );
            rSequence_addRU64( event, RP_TAGS_MEMORY_SIZE, context->regionSize );

            hbs_markAsRelated( context->fileInfo, event );

            if( NULL == context->processInfo )
            {
                context->processInfo = processLib_getProcessInfo( context->pid, NULL );
            }

            if( NULL != context->processInfo )
            {
                rSequence_addSEQUENCE( event, RP_TAGS_PROCESS, rSequence_duplicate( context->processInfo ) );
            }

            if( NULL != context->moduleInfo )
            {
                rSequence_addSEQUENCE( event, RP_TAGS_DLL, rSequence_duplicate( context->moduleInfo ) );
            }

            rSequence_addSTRINGA( event, RP_TAGS_RULE_NAME, (char*)rule->identifier );

            notifications_publish( RP_TAGS_NOTIFICATION_YARA_DETECTION, event );

            rSequence_free( event );
        }
        else
        {
            rpal_debug_warning( "error creating event from Yara match" );
        }
    }

    return CALLBACK_CONTINUE;
}
static
RVOID
    processCodeIdentA
    (
        RPCHAR name,
        RPU8 pFileHash,
        RU64 codeSize,
        rSequence originalEvent
    )
{
    CodeIdent ident = { 0 };
    rSequence notif = NULL;

    ident.codeSize = codeSize;

    if( NULL != name )
    {
        CryptoLib_hash( name, rpal_string_strlen( name ) * sizeof( RCHAR ), ident.nameHash );
    }

    if( NULL != pFileHash )
    {
        rpal_memory_memcpy( ident.fileHash, pFileHash, CRYPTOLIB_HASH_SIZE );
    }

    if( rpal_bloom_addIfNew( knownCode, &ident, sizeof( ident ) ) )
    {
        if( NULL != ( notif = rSequence_new() ) )
        {
            hbs_markAsRelated( originalEvent, notif );

            if( rSequence_addSTRINGA( notif, RP_TAGS_FILE_NAME, name ) &&
                rSequence_addBUFFER( notif, RP_TAGS_HASH, pFileHash, CRYPTOLIB_HASH_SIZE ) &&
                rSequence_addRU32( notif, RP_TAGS_MEMORY_SIZE, (RU32)codeSize ) &&
                rSequence_addTIMESTAMP( notif, RP_TAGS_TIMESTAMP, rpal_time_getGlobal() ) )
            {
                notifications_publish( RP_TAGS_NOTIFICATION_CODE_IDENTITY, notif );
            }
            rSequence_free( notif );
        }
    }
}
//=============================================================================
// YARA Required Shims
//=============================================================================
static
RVOID
    reportError
    (
        rSequence originalRequest,
        RU32 errorCode,
        RPCHAR errorStr
    )
{
    rSequence event = NULL;

    if( NULL != ( event = rSequence_new() ) )
    {
        hbs_markAsRelated( originalRequest, event );
        rSequence_addRU32( event, RP_TAGS_ERROR, errorCode );
        rSequence_addSTRINGA( event, RP_TAGS_ERROR_MESSAGE, errorStr ? errorStr : "" );
        rSequence_addTIMESTAMP( event, RP_TAGS_TIMESTAMP, rpal_time_getGlobal() );
        notifications_publish( RP_TAGS_NOTIFICATION_YARA_DETECTION, event );
        rSequence_free( event );
    }
}
static
RPVOID
    spotCheckProcessConstantly
    (
        rEvent isTimeToStop,
        RPVOID ctx
    )
{
    rSequence originalRequest = (rSequence)ctx;
    processLibProcEntry* procs = NULL;
    processLibProcEntry* proc = NULL;
    rList hollowedModules = NULL;
    rSequence processInfo = NULL;
    LibOsPerformanceProfile perfProfile = { 0 };
    Atom parentAtom = { 0 };

    perfProfile.targetCpuPerformance = 0;
    perfProfile.globalTargetCpuPerformance = GLOBAL_CPU_USAGE_TARGET;
    perfProfile.timeoutIncrementPerSec = _PROFILE_INCREMENT;
    perfProfile.enforceOnceIn = 1;
    perfProfile.lastTimeoutValue = _INITIAL_PROFILED_TIMEOUT;
    perfProfile.sanityCeiling = _SANITY_CEILING;

    while( rpal_memory_isValid( isTimeToStop ) &&
           !rEvent_wait( isTimeToStop, 0 ) )
    {
        libOs_timeoutWithProfile( &perfProfile, FALSE, isTimeToStop );

        if( NULL != ( procs = processLib_getProcessEntries( TRUE ) ) )
        {
            proc = procs;

            while( 0 != proc->pid &&
                   rpal_memory_isValid( isTimeToStop ) &&
                   !rEvent_wait( isTimeToStop, 0 ) )
            {
                libOs_timeoutWithProfile( &perfProfile, TRUE, isTimeToStop );

                if( NULL != ( hollowedModules = _spotCheckProcess( isTimeToStop, proc->pid, &perfProfile ) ) )
                {
                    if( NULL != ( processInfo = processLib_getProcessInfo( proc->pid, NULL ) ) ||
                        ( NULL != ( processInfo = rSequence_new() ) &&
                        rSequence_addRU32( processInfo, RP_TAGS_PROCESS_ID, proc->pid ) ) )
                    {
                        if( !rSequence_addLIST( processInfo, RP_TAGS_MODULES, hollowedModules ) )
                        {
                            rList_free( hollowedModules );
                        }
                        else
                        {
                            parentAtom.key.category = RP_TAGS_NOTIFICATION_NEW_PROCESS;
                            parentAtom.key.process.pid = proc->pid;
                            if( atoms_query( &parentAtom, 0 ) )
                            {
                                HbsSetParentAtom( processInfo, parentAtom.id );
                            }

                            hbs_markAsRelated( originalRequest, processInfo );
                            hbs_publish( RP_TAGS_NOTIFICATION_MODULE_MEM_DISK_MISMATCH, 
                                         processInfo );
                        }

                        rSequence_free( processInfo );
                    }
                    else
                    {
                        rList_free( hollowedModules );
                    }
                }

                proc++;
            }

            rpal_memory_free( procs );
        }
    }

    return NULL;
}
static
RVOID
    processCodeIdentW
    (
        RPWCHAR name,
        CryptoLib_Hash* pFileHash,
        RU64 codeSize,
        rSequence originalEvent
    )
{
    CodeIdent ident = { 0 };
    rSequence notif = NULL;
    rSequence sig = NULL;
    RBOOL isSigned = FALSE;
    RBOOL isVerifiedLocal = FALSE;
    RBOOL isVerifiedGlobal = FALSE;
    
    ident.codeSize = codeSize;

    if( NULL != name )
    {
        CryptoLib_hash( name, rpal_string_strlenw( name ) * sizeof( RWCHAR ), &ident.nameHash );
    }

    if( NULL != pFileHash )
    {
        rpal_memory_memcpy( &ident.fileHash, pFileHash, sizeof( *pFileHash ) );
    }

    if( rMutex_lock( g_mutex ) )
    {
        if( rpal_bloom_addIfNew( g_knownCode, &ident, sizeof( ident ) ) )
        {
            rMutex_unlock( g_mutex );

            if( NULL != ( notif = rSequence_new() ) )
            {
                hbs_markAsRelated( originalEvent, notif );

                if( ( rSequence_addSTRINGW( notif, RP_TAGS_FILE_PATH, name ) ||
                      rSequence_addSTRINGW( notif, RP_TAGS_DLL, name ) ||
                      rSequence_addSTRINGW( notif, RP_TAGS_EXECUTABLE, name ) ) &&
                    rSequence_addRU32( notif, RP_TAGS_MEMORY_SIZE, (RU32)codeSize ) &&
                    rSequence_addTIMESTAMP( notif, RP_TAGS_TIMESTAMP, rpal_time_getGlobal() ) )
                {
                    if( NULL != pFileHash )
                    {
                        rSequence_addBUFFER( notif, RP_TAGS_HASH, (RPU8)pFileHash, sizeof( *pFileHash ) );
                    }

                    if( libOs_getSignature( name,
                                            &sig,
                                            ( OSLIB_SIGNCHECK_NO_NETWORK | OSLIB_SIGNCHECK_CHAIN_VERIFICATION ),
                                            &isSigned,
                                            &isVerifiedLocal,
                                            &isVerifiedGlobal ) )
                    {
                        if( !rSequence_addSEQUENCE( notif, RP_TAGS_SIGNATURE, sig ) )
                        {
                            rSequence_free( sig );
                        }
                    }

                    notifications_publish( RP_TAGS_NOTIFICATION_CODE_IDENTITY, notif );
                }
                rSequence_free( notif );
            }
        }
        else
        {
            rMutex_unlock( g_mutex );
        }
    }
}
static
RPVOID
    lookForHiddenModulesIn
    (
        rEvent isTimeToStop,
        RU32 processId,
        rSequence originalRequest,
        LibOsPerformanceProfile* perfProfile
    )
{
    rList mods = NULL;
    _MemRange* memRanges = NULL;
    RU32 nRanges = 0;
    rList map = NULL;
    rSequence region = NULL;
    RU8 memType = 0;
    RU8 memProtect = 0;
    RU64 memBase = 0;
    RU64 memSize = 0;

    RPU8 pMem = NULL;

    RBOOL isPrefetched = FALSE;
    RBOOL isCurrentExec = FALSE;
    RBOOL isHidden = FALSE;

    rSequence procInfo = NULL;

#ifdef RPAL_PLATFORM_WINDOWS
    PIMAGE_DOS_HEADER pDos = NULL;
    PIMAGE_NT_HEADERS pNt = NULL;
#endif

    rpal_debug_info( "looking for hidden modules in process %d.", processId );

    if( NULL != ( mods = processLib_getProcessModules( processId ) ) )
    {
        if( assembleRanges( mods, &memRanges, &nRanges ) )
        {
            if( NULL != ( map = processLib_getProcessMemoryMap( processId ) ) )
            {
                // Now we got all the info needed for a single process, compare
                while( rpal_memory_isValid( isTimeToStop ) &&
                       !rEvent_wait( isTimeToStop, 0 ) &&
                       ( isPrefetched || rList_getSEQUENCE( map, RP_TAGS_MEMORY_REGION, &region ) ) )
                {
                    libOs_timeoutWithProfile( perfProfile, FALSE );

                    if( isPrefetched )
                    {
                        isPrefetched = FALSE;
                    }

                    if( rSequence_getRU8( region, RP_TAGS_MEMORY_TYPE, &memType ) &&
                        rSequence_getRU8( region, RP_TAGS_MEMORY_ACCESS, &memProtect ) &&
                        rSequence_getPOINTER64( region, RP_TAGS_BASE_ADDRESS, &memBase ) &&
                        rSequence_getRU64( region, RP_TAGS_MEMORY_SIZE, &memSize ) )
                    {
                        if( PROCESSLIB_MEM_TYPE_PRIVATE == memType ||
                            PROCESSLIB_MEM_TYPE_MAPPED == memType )
                        {
                            if( PROCESSLIB_MEM_ACCESS_EXECUTE == memProtect ||
                                PROCESSLIB_MEM_ACCESS_EXECUTE_READ == memProtect ||
                                PROCESSLIB_MEM_ACCESS_EXECUTE_READ_WRITE == memProtect ||
                                PROCESSLIB_MEM_ACCESS_EXECUTE_WRITE_COPY == memProtect )
                            {
                                isCurrentExec = TRUE;
                            }
                            else
                            {
                                isCurrentExec = FALSE;
                            }

                            // This check is somewhat redundant since it the memory
                            // regions are already filtered by not allowing TYPE_IMAGE.
                            // It's now not that expensive so running it in parallel
                            // might catch some edge case.(?)
                            if( !isMemInModule( memBase, memSize, memRanges, nRanges ) )
                            {
                                // Exec memory found outside of a region marked to belong to
                                // a module, keep looking in for module.
                                if( ( 1024 * 1024 * 10 ) >= memSize &&
                                    processLib_getProcessMemory( processId,
                                                                 NUMBER_TO_PTR( memBase ),
                                                                 memSize,
                                                                 (RPVOID*)&pMem,
                                                                 TRUE ) )
                                {
                                    isHidden = FALSE;
#ifdef RPAL_PLATFORM_WINDOWS
                                    // Let's just check for MZ and PE for now, we can get fancy later.
                                    pDos = (PIMAGE_DOS_HEADER)pMem;
                                    if( IS_WITHIN_BOUNDS( (RPU8)pMem, 
                                                          sizeof( IMAGE_DOS_HEADER ), 
                                                          pMem, 
                                                          memSize ) &&
                                        IMAGE_DOS_SIGNATURE == pDos->e_magic )
                                    {
                                        pNt = (PIMAGE_NT_HEADERS)( (RPU8)pDos + pDos->e_lfanew );

                                        if( IS_WITHIN_BOUNDS( pNt, sizeof( *pNt ), pMem, memSize ) &&
                                            IMAGE_NT_SIGNATURE == pNt->Signature )
                                        {
                                            if( isCurrentExec )
                                            {
                                                // If the current region is exec, we've got a hidden module.
                                                isHidden = TRUE;
                                            }
                                            else
                                            {
                                                // We need to check if the next section in memory is
                                                // executable and outside of known modules since the PE
                                                // headers may have been marked read-only before the .text.
                                                if( rList_getSEQUENCE( map, RP_TAGS_MEMORY_REGION, &region ) )
                                                {
                                                    isPrefetched = TRUE;

                                                    if( ( PROCESSLIB_MEM_TYPE_PRIVATE == memType ||
                                                        PROCESSLIB_MEM_TYPE_MAPPED == memType ) &&
                                                        ( PROCESSLIB_MEM_ACCESS_EXECUTE == memProtect ||
                                                        PROCESSLIB_MEM_ACCESS_EXECUTE_READ == memProtect ||
                                                        PROCESSLIB_MEM_ACCESS_EXECUTE_READ_WRITE == memProtect ||
                                                        PROCESSLIB_MEM_ACCESS_EXECUTE_WRITE_COPY == memProtect ) )
                                                    {
                                                        isHidden = TRUE;
                                                    }
                                                }
                                            }
                                        }
                                    }
#elif defined( RPAL_PLATFORM_LINUX ) || defined( RPAL_PLATFORM_MACOSX )
                                    if( isCurrentExec &&
                                        0x7F == ( pMem )[ 0 ] &&
                                        'E' == ( pMem )[ 1 ] &&
                                        'L' == ( pMem )[ 2 ] &&
                                        'F' == ( pMem )[ 3 ] )
                                    {
                                        isHidden = TRUE;
                                    }
#endif

                                    rpal_memory_free( pMem );

                                    if( isHidden &&
                                        !rEvent_wait( isTimeToStop, 0 ) )
                                    {
                                        rpal_debug_info( "found a hidden module in %d.", processId );

                                        if( NULL != ( procInfo = processLib_getProcessInfo( processId, NULL ) ) )
                                        {
                                            if( !rSequence_addSEQUENCE( region, RP_TAGS_PROCESS, procInfo ) )
                                            {
                                                rSequence_free( procInfo );
                                            }
                                        }

                                        rSequence_addTIMESTAMP( region, 
                                                                RP_TAGS_TIMESTAMP, 
                                                                rpal_time_getGlobal() );
                                        hbs_markAsRelated( originalRequest, region );
                                        notifications_publish( RP_TAGS_NOTIFICATION_HIDDEN_MODULE_DETECTED, 
                                                               region );
                                        break;
                                    }

                                    libOs_timeoutWithProfile( perfProfile, TRUE );
                                }
                            }
                        }
                    }
                }

                rList_free( map );
            }

            rpal_memory_free( memRanges );
        }

        rList_free( mods );
    }

    return NULL;
}