static
rList
    _spotCheckProcess
    (
        rEvent isTimeToStop,
        RU32 pid,
        LibOsPerformanceProfile* perfProfile
    )
{
    rList hollowedModules = NULL;

    rList modules = NULL;
    rSequence module = NULL;
    RPNCHAR modulePath = NULL;
    RU32 fileSize = 0;
    RU64 moduleBase = 0;
    RU64 moduleSize = 0;
    HObs diskSample = NULL;
    rSequence hollowedModule = NULL;
    RU32 lastScratchIndex = 0;
    RU32 nSamplesFound = 0;
    RU32 nSamplesTotal = 0;
    RU32 tmpSamplesFound = 0;
    RU32 tmpSamplesSize = 0;
    RTIME runTime = 0;

    rpal_debug_info( "spot checking process %d", pid );

    if( NULL != ( modules = processLib_getProcessModules( pid ) ) )
    {
        while( !rEvent_wait( isTimeToStop, 0 ) &&
               rList_getSEQUENCE( modules, RP_TAGS_DLL, &module ) )
        {
            libOs_timeoutWithProfile( perfProfile, FALSE, isTimeToStop );
            runTime = rpal_time_getLocal();

            modulePath = NULL;
            lastScratchIndex = 0;

            if( ( rSequence_getSTRINGN( module, 
                                        RP_TAGS_FILE_PATH, 
                                        &modulePath ) ) &&
                rSequence_getPOINTER64( module, RP_TAGS_BASE_ADDRESS, &moduleBase ) &&
                rSequence_getRU64( module, RP_TAGS_MEMORY_SIZE, &moduleSize ) )
            {
                if( 0 != ( fileSize = rpal_file_getSize( modulePath, TRUE ) ) )
                {
                    nSamplesFound = 0;
                    nSamplesTotal = 0;
                    tmpSamplesFound = (RU32)( -1 );

                    while( !rEvent_wait( isTimeToStop, 0 ) &&
                            NULL != ( diskSample = _getModuleDiskStringSample( modulePath,
                                                                                &lastScratchIndex,
                                                                                isTimeToStop,
                                                                                perfProfile ) ) )
                    {
                        libOs_timeoutWithProfile( perfProfile, TRUE, isTimeToStop );

                        tmpSamplesSize = obsLib_getNumPatterns( diskSample );

                        if( 0 != tmpSamplesSize )
                        {
                            tmpSamplesFound = _checkMemoryForStringSample( diskSample,
                                                                            pid,
                                                                            NUMBER_TO_PTR( moduleBase ),
                                                                            moduleSize,
                                                                            isTimeToStop,
                                                                            perfProfile );
                            obsLib_free( diskSample );

                            if( (RU32)( -1 ) == tmpSamplesFound )
                            {
                                break;
                            }

                            nSamplesFound += tmpSamplesFound;
                            nSamplesTotal += tmpSamplesSize;

                            if( _MIN_DISK_SAMPLE_SIZE <= nSamplesTotal &&
                                ( _MIN_SAMPLE_MATCH_PERCENT < ( (RFLOAT)nSamplesFound /
                                                                nSamplesTotal ) * 100 ) &&
                                _MIN_DISK_BIN_COVERAGE_PERCENT <= (RFLOAT)( ( lastScratchIndex *
                                                                                _SCRATCH_SIZE ) /
                                                                            fileSize ) * 100 )
                            {
                                break;
                            }
                        }
                        else
                        {
                            obsLib_free( diskSample );
                        }
                    }
                }
                else
                {
                    rpal_debug_info( "could not get file information, not checking" );
                }

                rpal_debug_info( "process hollowing check found a match in %d ( %d / %d ) from %d passes in %d sec", 
                                    pid,  
                                    nSamplesFound, 
                                    nSamplesTotal,
                                    lastScratchIndex,
                                    rpal_time_getLocal() - runTime );
                    
                if( !rEvent_wait( isTimeToStop, 0 ) &&
                    (RU32)(-1) != tmpSamplesFound &&
                    _MIN_DISK_SAMPLE_SIZE <= nSamplesTotal &&
                    ( ( (RFLOAT)nSamplesFound / nSamplesTotal ) * 100 ) < _MIN_SAMPLE_MATCH_PERCENT )
                {
                    rpal_debug_info( "sign of process hollowing found in process %d", pid );

                    if( NULL != ( hollowedModule = rSequence_duplicate( module ) ) )
                    {
                        if( !rList_addSEQUENCE( hollowedModules, hollowedModule ) )
                        {
                            rSequence_free( hollowedModule );
                        }
                    }
                }
            }
            else
            {
                rpal_debug_info( "module missing characteristic" );
            }
        }

        rList_free( modules );
    }
    else
    {
        rpal_debug_info( "failed to get process modules, might be dead" );
    }

    return hollowedModules;
}
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
HObs
    _getModuleDiskStringSample
    (
        RPNCHAR modulePath,
        RU32* pLastScratch,
        rEvent isTimeToStop,
        LibOsPerformanceProfile* perfProfile
    )
{
    HObs sample = NULL;
    RPU8 scratch = NULL;
    rFile hFile = NULL;
    RU32 read = 0;
    RPU8 start = NULL;
    RPU8 end = NULL;
    RU32 toSkip = 0;
    RU32 longestLength = 0;
    RBOOL isUnicode = FALSE;
    RPU8 sampleNumber = 0;
    rBloom stringsSeen = NULL;
    RU64 readOffset = 0;

    UNREFERENCED_PARAMETER( isTimeToStop );

    if( NULL != modulePath &&
        NULL != pLastScratch )
    {
        readOffset = *pLastScratch * _SCRATCH_SIZE;
        if( NULL != ( stringsSeen = rpal_bloom_create( _MAX_DISK_SAMPLE_SIZE, 0.0001 ) ) )
        {
            if( NULL != ( scratch = rpal_memory_alloc( _SCRATCH_SIZE ) ) )
            {
                if( rFile_open( modulePath, &hFile, RPAL_FILE_OPEN_EXISTING |
                                                    RPAL_FILE_OPEN_READ ) )
                {
                    if( readOffset == rFile_seek( hFile, 
                                                  readOffset, 
                                                  rFileSeek_SET ) &&
                        0 != ( read = rFile_readUpTo( hFile, _SCRATCH_SIZE, scratch ) ) )
                    {
                        if( NULL != ( sample = obsLib_new( _MAX_DISK_SAMPLE_SIZE, 0 ) ) )
                        {
                            ( *pLastScratch )++;
                            start = scratch;
                            end = scratch + read;

                            // We parse for strings up to 'read', we don't care about the 
                            // memory boundary, we might truncate some strings but we're
                            // sampling anyway.
                            while( !rEvent_wait( isTimeToStop, 0 ) &&
                                   ( start >= scratch ) && ( start >= scratch ) &&
                                   ( start + _MIN_SAMPLE_STR_LEN ) < ( scratch + read ) &&
                                   _MAX_DISK_SAMPLE_SIZE >= PTR_TO_NUMBER( sampleNumber ) )
                            {
                                libOs_timeoutWithProfile( perfProfile, TRUE, isTimeToStop );

                                isUnicode = FALSE;

                                if( _longestString( (RPCHAR)start,
                                                    (RU32)( end - start ),
                                                    &toSkip,
                                                    &longestLength,
                                                    &isUnicode ) &&
                                    _MIN_SAMPLE_STR_LEN <= longestLength &&
                                    _MAX_SAMPLE_STR_LEN >= longestLength )
                                {
                                    if( rpal_bloom_addIfNew( stringsSeen,
                                                             start,
                                                             longestLength ) )
                                    {
                                        if( obsLib_addPattern( sample,
                                                               start,
                                                               longestLength,
                                                               sampleNumber ) )
                                        {
                                            sampleNumber++;
                                        }
                                    }
                                }

                                start += toSkip;
                            }
                        }
                    }

                    rFile_close( hFile );
                }

                rpal_memory_free( scratch );
            }

            rpal_bloom_destroy( stringsSeen );
        }
    }

    return sample;
}
static
RU32
    _checkMemoryForStringSample
    (
        HObs sample,
        RU32 pid,
        RPVOID moduleBase,
        RU64 moduleSize,
        rEvent isTimeToStop,
        LibOsPerformanceProfile* perfProfile
    )
{
    RPU8 pMem = NULL;
    RU8* sampleList = NULL;
    RPU8 sampleNumber = 0;
    RU32 nSamples = 0;
    RU32 nSamplesFound = (RU32)(-1);

    UNREFERENCED_PARAMETER( isTimeToStop );

    if( NULL != sample &&
        0 != pid &&
        NULL != moduleBase &&
        0 != moduleSize &&
        _MIN_DISK_SAMPLE_SIZE <= ( nSamples = obsLib_getNumPatterns( sample ) ) )
    {
        if( NULL != ( sampleList = rpal_memory_alloc( sizeof( RU8 ) * nSamples ) ) )
        {
            rpal_memory_zero( sampleList, sizeof( RU8 ) * nSamples );

            if( processLib_getProcessMemory( pid, moduleBase, moduleSize, (RPVOID*)&pMem, TRUE ) )
            {
                if( obsLib_setTargetBuffer( sample, pMem, (RU32)moduleSize ) )
                {
                    while( !rEvent_wait( isTimeToStop, 0 ) &&
                           obsLib_nextHit( sample, (RPVOID*)&sampleNumber, NULL ) )
                    {
                        libOs_timeoutWithProfile( perfProfile, TRUE, isTimeToStop );

                        if( sampleNumber < (RPU8)NUMBER_TO_PTR( nSamples ) &&
                            0 == sampleList[ (RU32)PTR_TO_NUMBER( sampleNumber ) ] )
                        {
                            sampleList[ (RU32)PTR_TO_NUMBER( sampleNumber ) ] = 1;
                            nSamplesFound++;
                        }
                    }
                }

                rpal_memory_free( pMem );
            }
            else
            {
                rpal_debug_info( "failed to get memory for %d: 0x%016X ( 0x%016X ) error %d", 
                                 pid, 
                                 moduleBase, 
                                 moduleSize,
                                 rpal_error_getLast() );
            }

            rpal_memory_free( sampleList );
        }
    }

    return nSamplesFound;
}
RPRIVATE
RVOID
    dnsUmDiffThread
    (
        rEvent isTimeToStop
    )
{
    rSequence notif = NULL;
    rBlob snapCur = NULL;
    rBlob snapPrev = NULL;
    _dnsRecord rec = { 0 };
    _dnsRecord* pCurRec = NULL;
    RU32 i = 0;
    LibOsPerformanceProfile perfProfile = { 0 };
    
#ifdef RPAL_PLATFORM_WINDOWS
    PDNSCACHEENTRY pDnsEntry = NULL;
    PDNSCACHEENTRY pPrevDnsEntry = NULL;
#endif

    perfProfile.enforceOnceIn = 1;
    perfProfile.sanityCeiling = MSEC_FROM_SEC( 10 );
    perfProfile.lastTimeoutValue = 100;
    perfProfile.targetCpuPerformance = 0;
    perfProfile.globalTargetCpuPerformance = GLOBAL_CPU_USAGE_TARGET;
    perfProfile.timeoutIncrementPerSec = 1;

    while( !rEvent_wait( isTimeToStop, 0 ) )
    {
        if( kAcq_isAvailable() )
        {
            // If kernel acquisition becomes available, try kernel again.
            return;
        }

        libOs_timeoutWithProfile( &perfProfile, FALSE, isTimeToStop );

        if( NULL != ( snapCur = rpal_blob_create( 0, 10 * sizeof( rec ) ) ) )
        {
#ifdef RPAL_PLATFORM_WINDOWS
            if( TRUE == getCache( &pDnsEntry ) )
            {
                while( NULL != pDnsEntry )
                {
                    rec.flags = pDnsEntry->dwFlags;
                    rec.type = pDnsEntry->wType;
                    if( NULL != ( rec.name = rpal_string_strdup( pDnsEntry->pszName ) ) )
                    {
                        rpal_blob_add( snapCur, &rec, sizeof( rec ) );
                    }

                    pPrevDnsEntry = pDnsEntry;
                    pDnsEntry = pDnsEntry->pNext;

                    freeCacheEntry( pPrevDnsEntry->pszName, DnsFreeFlat );
                    freeCacheEntry( pPrevDnsEntry, DnsFreeFlat );
                }

                rpal_sort_array( rpal_blob_getBuffer( snapCur ), 
                                 rpal_blob_getSize( snapCur ) / sizeof( rec ), 
                                 sizeof( rec ), 
                                 _cmpDns );
            }
#elif defined( RPAL_PLATFORM_MACOSX )
            rpal_thread_sleep( MSEC_FROM_SEC( 2 ) );
#endif

            // Do a general diff of the snapshots to find new entries.
            if( NULL != snapPrev )
            {
                i = 0;
                while( !rEvent_wait( isTimeToStop, 0 ) &&
                       NULL != ( pCurRec = rpal_blob_arrElem( snapCur, sizeof( rec ), i++ ) ) )
                {
                    if( -1 == rpal_binsearch_array( rpal_blob_getBuffer( snapPrev ), 
                                                    rpal_blob_getSize( snapPrev ) / sizeof( rec ), 
                                                    sizeof( rec ), 
                                                    pCurRec,
                                                    (rpal_ordering_func)_cmpDns ) )
                    {
                        if( NULL != ( notif = rSequence_new() ) )
                        {
                            rSequence_addSTRINGN( notif, RP_TAGS_DOMAIN_NAME, pCurRec->name );
                            rSequence_addRU16( notif, RP_TAGS_DNS_TYPE, pCurRec->type );
                            rSequence_addRU32( notif, RP_TAGS_DNS_FLAGS, pCurRec->flags );
                            hbs_timestampEvent( notif, 0 );

                            hbs_publish( RP_TAGS_NOTIFICATION_DNS_REQUEST, notif );

                            rSequence_free( notif );
                        }
                    }
                }
            }
        }

        if( NULL != snapPrev )
        {
            _freeRecords( snapPrev );
            rpal_blob_free( snapPrev );
            snapPrev = NULL;
        }

        snapPrev = snapCur;
        snapCur = NULL;

        libOs_timeoutWithProfile( &perfProfile, TRUE, isTimeToStop );
    }

    if( NULL != snapPrev )
    {
        _freeRecords( snapPrev );
        rpal_blob_free( snapPrev );
        snapPrev = NULL;
    }
}
static RPVOID
    trackerDiffThread
    (
        rEvent isTimeToStop,
        RPVOID ctx
    )
{
    _volEntry* prevVolumes = NULL;
    RU32 nVolumes = 0;
    rList snapshot = NULL;
    rList prevSnapshot = NULL;
    _volEntry* newVolumes = NULL;
    RU32 nNewVolumes = 0;
    rSequence volume = NULL;
    rBlob serial = NULL;
    RU32 i = 0;
    LibOsPerformanceProfile perfProfile = { 0 };

    UNREFERENCED_PARAMETER( ctx );

    perfProfile.enforceOnceIn = 1;
    perfProfile.sanityCeiling = MSEC_FROM_SEC( 10 );
    perfProfile.lastTimeoutValue = 10;
    perfProfile.targetCpuPerformance = 0;
    perfProfile.globalTargetCpuPerformance = GLOBAL_CPU_USAGE_TARGET;
    perfProfile.timeoutIncrementPerSec = 1;
    
    while( !rEvent_wait( isTimeToStop, 0 ) )
    {
        libOs_timeoutWithProfile( &perfProfile, FALSE );

        if( NULL != ( snapshot = libOs_getVolumes() ) )
        {
            if( NULL != ( newVolumes = rpal_memory_alloc( sizeof( *newVolumes ) *
                                                          rList_getNumElements( snapshot ) ) ) )
            {
                nNewVolumes = 0;

                while( !rEvent_wait( isTimeToStop, 0 ) &&
                       rList_getSEQUENCE( snapshot, RP_TAGS_VOLUME, &volume ) )
                {
                    libOs_timeoutWithProfile( &perfProfile, TRUE );

                    if( NULL != ( serial = rpal_blob_create( 0, 0 ) ) )
                    {
                        if( rSequence_serialise( volume, serial ) &&
                            CryptoLib_hash( rpal_blob_getBuffer( serial ),
                                            rpal_blob_getSize( serial ), 
                                            &( newVolumes[ nNewVolumes ].hash ) ) )
                        {
                            newVolumes[ nNewVolumes ].volume = volume;

                            if( NULL != prevVolumes &&
                                ( -1 ) == rpal_binsearch_array( prevVolumes,
                                                                nVolumes,
                                                                sizeof( *prevVolumes ),
                                                                &( newVolumes[ nNewVolumes ] ),
                                                                (rpal_ordering_func)_cmpHashes ) )
                            {
                                notifications_publish( RP_TAGS_NOTIFICATION_VOLUME_MOUNT, volume );
                                rpal_debug_info( "new volume mounted" );
                            }

                            nNewVolumes++;
                        }

                        rpal_blob_free( serial );
                    }
                }

                if( !rEvent_wait( isTimeToStop, 0 ) )
                {
                    rpal_sort_array( newVolumes,
                                     nNewVolumes,
                                     sizeof( *newVolumes ),
                                     (rpal_ordering_func)_cmpHashes );

                    for( i = 0; i < nVolumes; i++ )
                    {
                        libOs_timeoutWithProfile( &perfProfile, TRUE );

                        if( ( -1 ) == rpal_binsearch_array( newVolumes,
                                                            nNewVolumes,
                                                            sizeof( *newVolumes ),
                                                            &( prevVolumes[ i ].hash ),
                                                            (rpal_ordering_func)_cmpHashes ) )
                        {
                            notifications_publish( RP_TAGS_NOTIFICATION_VOLUME_UNMOUNT,
                                                   prevVolumes[ i ].volume );
                            rpal_debug_info( "volume unmounted" );
                        }
                    }
                }
            }

            if( NULL != prevSnapshot )
            {
                rList_free( prevSnapshot );
            }
            prevSnapshot = snapshot;
            if( NULL != prevVolumes )
            {
                rpal_memory_free( prevVolumes );
            }
            prevVolumes = newVolumes;
            nVolumes = nNewVolumes;
        }
    }
    
    if( NULL != prevSnapshot )
    {
        rList_free( prevSnapshot );
    }
    if( NULL != prevVolumes )
    {
        rpal_memory_free( prevVolumes );
    }

    return NULL;
}
static
RPVOID
    modUserModeDiff
    (
        rEvent isTimeToStop
    )
{
    rBlob previousSnapshot = NULL;
    rBlob newSnapshot = NULL;
    _moduleHistEntry curModule = { 0 };
    processLibProcEntry* processes = NULL;
    processLibProcEntry* curProc = NULL;
    rList modules = NULL;
    rSequence module = NULL;
    LibOsPerformanceProfile perfProfile = { 0 };
    Atom parentAtom = { 0 };
    RU64 curTime = 0;

    perfProfile.enforceOnceIn = 1;
    perfProfile.lastTimeoutValue = 10;
    perfProfile.sanityCeiling = MSEC_FROM_SEC( 10 );
    perfProfile.targetCpuPerformance = 0;
    perfProfile.globalTargetCpuPerformance = GLOBAL_CPU_USAGE_TARGET;
    perfProfile.timeoutIncrementPerSec = 1;

    while( rpal_memory_isValid( isTimeToStop ) &&
           !rEvent_wait( isTimeToStop, 0 ) &&
           ( !kAcq_isAvailable() ||
             g_is_kernel_failure ) )
    {
        if( NULL != ( processes = processLib_getProcessEntries( FALSE ) ) )
        {
            if( NULL != ( newSnapshot = rpal_blob_create( 1000 * sizeof( _moduleHistEntry ),
                                                          1000 * sizeof( _moduleHistEntry ) ) ) )
            {
                libOs_timeoutWithProfile( &perfProfile, FALSE, isTimeToStop );

                curProc = processes;
                while( rpal_memory_isValid( isTimeToStop ) &&
#ifdef RPAL_PLATFORM_WINDOWS
                       !rEvent_wait( isTimeToStop, 0 ) &&
#else
                       // Module listing outside of 
                       !rEvent_wait( isTimeToStop, MSEC_FROM_SEC( 1 ) ) &&
#endif
                       0 != curProc->pid )
                {
                    if( NULL != ( modules = processLib_getProcessModules( curProc->pid ) ) )
                    {
                        curTime = rpal_time_getGlobalPreciseTime();

                        while( rpal_memory_isValid( isTimeToStop ) &&
                               !rEvent_wait( isTimeToStop, 0 ) &&
                               rList_getSEQUENCE( modules, RP_TAGS_DLL, &module ) )
                        {
                            libOs_timeoutWithProfile( &perfProfile, TRUE, isTimeToStop );

                            if( rSequence_getPOINTER64( module,
                                                        RP_TAGS_BASE_ADDRESS, 
                                                        &( curModule.baseAddr ) ) &&
                                rSequence_getRU64( module, 
                                                   RP_TAGS_MEMORY_SIZE, 
                                                   &(curModule.size) ) )
                            {
                                curModule.procId = curProc->pid;
                                rpal_blob_add( newSnapshot, &curModule, sizeof( curModule ) );
                                if( NULL != previousSnapshot &&
                                    -1 == rpal_binsearch_array( rpal_blob_getBuffer( previousSnapshot ),
                                                                rpal_blob_getSize( previousSnapshot ) /
                                                                    sizeof( _moduleHistEntry ),
                                                                sizeof( _moduleHistEntry ),
                                                                &curModule, 
                                                                (rpal_ordering_func)_cmpModule ) )
                                {
                                    hbs_timestampEvent( module, curTime );
                                    parentAtom.key.category = RP_TAGS_NOTIFICATION_NEW_PROCESS;
                                    parentAtom.key.process.pid = curProc->pid;
                                    if( atoms_query( &parentAtom, curTime ) )
                                    {
                                        HbsSetParentAtom( module, parentAtom.id );
                                    }
                                    rpal_memory_zero( &parentAtom, sizeof( parentAtom ) );
                                    hbs_publish( RP_TAGS_NOTIFICATION_MODULE_LOAD,
                                                 module );
                                }
                            }
                        }

                        rList_free( modules );
                    }

                    curProc++;
                }

                if( !rpal_sort_array( rpal_blob_getBuffer( newSnapshot ),
                                      rpal_blob_getSize( newSnapshot ) / sizeof( _moduleHistEntry ),
                                      sizeof( _moduleHistEntry ),
                                      (rpal_ordering_func)_cmpModule ) )
                {
                    rpal_debug_warning( "error sorting modules" );
                }
            }

            rpal_memory_free( processes );
        }

        if( NULL != previousSnapshot )
        {
            rpal_blob_free( previousSnapshot );
        }
        previousSnapshot = newSnapshot;
        newSnapshot = NULL;
    }

    if( NULL != previousSnapshot )
    {
        rpal_blob_free( previousSnapshot );
    }

    return NULL;
}
static RVOID
    userModeDiff
    (
        rEvent isTimeToStop
    )
{
    processEntry snapshot_1[ MAX_SNAPSHOT_SIZE ] = { 0 };
    processEntry snapshot_2[ MAX_SNAPSHOT_SIZE ] = { 0 };
    processEntry* currentSnapshot = snapshot_1;
    processEntry* previousSnapshot = snapshot_2;
    processEntry* tmpSnapshot = NULL;
    RBOOL isFirstSnapshots = TRUE;
    RU32 i = 0;
    RBOOL isFound = FALSE;
    RU32 nTmpElem = 0;
    RU32 nCurElem = 0;
    RU32 nPrevElem = 0;
    LibOsPerformanceProfile perfProfile = { 0 };

    perfProfile.enforceOnceIn = 1;
    perfProfile.sanityCeiling = MSEC_FROM_SEC( 10 );
    perfProfile.lastTimeoutValue = 100;
    perfProfile.targetCpuPerformance = 0;
    perfProfile.globalTargetCpuPerformance = GLOBAL_CPU_USAGE_TARGET;
    perfProfile.timeoutIncrementPerSec = 10;

    while( !rEvent_wait( isTimeToStop, 0 ) &&
           ( !kAcq_isAvailable() ||
             g_is_kernel_failure ) )
    {
        libOs_timeoutWithProfile( &perfProfile, FALSE );

        tmpSnapshot = currentSnapshot;
        currentSnapshot = previousSnapshot;
        previousSnapshot = tmpSnapshot;

        nTmpElem = nCurElem;
        nCurElem = nPrevElem;
        nPrevElem = nTmpElem;

        if( getSnapshot( currentSnapshot, &nCurElem ) )
        {
            if( isFirstSnapshots )
            {
                isFirstSnapshots = FALSE;
                continue;
            }

            // Diff to find new processes
            for( i = 0; i < nCurElem; i++ )
            {
                isFound = FALSE;

                if( (RU32)( -1 ) != rpal_binsearch_array( previousSnapshot,
                                                          nPrevElem,
                                                          sizeof( processEntry ),
                                                          &(currentSnapshot[ i ].pid),
                                                          (rpal_ordering_func)rpal_order_RU32 ) )
                {
                    isFound = TRUE;
                }

                if( !isFound )
                {
                    if( !notifyOfProcess( currentSnapshot[ i ].pid,
                                          currentSnapshot[ i ].ppid,
                                          TRUE,
                                          NULL,
                                          NULL,
                                          KERNEL_ACQ_NO_USER_ID,
                                          0 ) )
                    {
                        rpal_debug_warning( "error reporting new process: %d",
                                            currentSnapshot[ i ].pid );
                    }
                }
            }

            // Diff to find terminated processes
            for( i = 0; i < nPrevElem; i++ )
            {
                isFound = FALSE;

                if( (RU32)( -1 ) != rpal_binsearch_array( currentSnapshot,
                                                          nCurElem,
                                                          sizeof( processEntry ),
                                                          &(previousSnapshot[ i ].pid),
                                                          (rpal_ordering_func)rpal_order_RU32 ) )
                {
                    isFound = TRUE;
                }

                if( !isFound )
                {
                    if( !notifyOfProcess( previousSnapshot[ i ].pid,
                                          previousSnapshot[ i ].ppid,
                                          FALSE,
                                          NULL,
                                          NULL,
                                          KERNEL_ACQ_NO_USER_ID,
                                          0 ) )
                    {
                        rpal_debug_warning( "error reporting terminated process: %d",
                                            previousSnapshot[ i ].pid );
                    }
                }
            }
        }

        libOs_timeoutWithProfile( &perfProfile, TRUE );
    }
}
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;
}