static
RVOID
    processCodeIdentW
    (
        RPWCHAR name,
        RPU8 pFileHash,
        RU64 codeSize,
        rSequence originalEvent
    )
{
    CodeIdent ident = { 0 };
    rSequence notif = NULL;
    
    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, CRYPTOLIB_HASH_SIZE );
    }

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

            if( rSequence_addSTRINGW( notif, RP_TAGS_FILE_PATH, 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 );
        }
    }
}
static 
RPVOID
    dnsDiffThread
    (
        rEvent isTimeToStop,
        RPVOID ctx
    )
{
    RU32 nThLoop = 0;
    RU32 currentTimeout = 0;
    rSequence notif = NULL;
    rBlob snapCur = NULL;
    rBlob snapPrev = NULL;
    _dnsRecord rec = { 0 };
    _dnsRecord* pCurRec = NULL;
    _dnsRecord* pPrevRec = NULL;
    RU32 i = 0;
    RU32 j = 0;
    RBOOL isNew = FALSE;
    
#ifdef RPAL_PLATFORM_WINDOWS
    PDNSCACHEENTRY pDnsEntry = NULL;
    PDNSCACHEENTRY pPrevDnsEntry = NULL;
#endif

    UNREFERENCED_PARAMETER( ctx );

    while( !rEvent_wait( isTimeToStop, currentTimeout ) )
    {
        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_strdupw( pDnsEntry->pszName ) ) )
                    {
                        rpal_blob_add( snapCur, &rec, sizeof( rec ) );
                    }

                    pPrevDnsEntry = pDnsEntry;
                    pDnsEntry = pDnsEntry->pNext;

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

            // Do a general diff of the snapshots to find new entries.
            if( NULL != snapPrev )
            {
                i = 0;
                while( NULL != ( pCurRec = rpal_blob_arrElem( snapCur, sizeof( rec ), i++ ) ) )
                {
                    isNew = TRUE;
                    j = 0;
                    while( NULL != ( pPrevRec = rpal_blob_arrElem( snapPrev, sizeof( rec ), j++ ) ) )
                    {
                        if( pCurRec->flags == pPrevRec->flags &&
                            pCurRec->type == pPrevRec->type &&
                            0 == rpal_string_strcmpw( pCurRec->name, pPrevRec->name ) )
                        {
                            isNew = FALSE;
                            break;
                        }
                    }

                    if( isNew &&
                        !rEvent_wait( isTimeToStop, 0 ) )
                    {
                        if( NULL != ( notif = rSequence_new() ) )
                        {
                            rSequence_addSTRINGW( 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 );
                            rSequence_addTIMESTAMP( notif, RP_TAGS_TIMESTAMP, rpal_time_getGlobal() );

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

        nThLoop++;
        if( 0 == nThLoop % 20 )
        {
            currentTimeout = libOs_getUsageProportionalTimeout( MSEC_FROM_SEC( 10 ) ) + MSEC_FROM_SEC( 5 );
        }

        rpal_thread_sleep( currentTimeout );
    }

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

    return NULL;
}
RPRIVATE
RVOID
    _searchForStrings
    (
        rList stringsFound,
        rList searchStrings,
        RPU8 pBuff,
        RU64 size,
        RU64 baseAddr,
        RU32 minLength,
        RU32 maxLength
    )
{
    RPU8 pCurr;
    RPU8 pEnd;
    RPCHAR pStartStr = NULL;
    RBOOL isChar;
    RPU16 pwCurr;
    RPU16 pwEnd;
    RBOOL isWChar;
    RPWCHAR pwStartStr = NULL;
    RPWCHAR thisStrW = NULL;
    rSequence newFoundStr;

    pCurr = pBuff;
    pEnd = pBuff + size;

    // currently we only deal with NULL terminated strings
    // start with ascii strings...
    while( pCurr < pEnd )
    {
        isChar = rpal_string_isprint( *pCurr );

        if( NULL == pStartStr && isChar )  // found the begining of a string
        {
            pStartStr = (RPCHAR)pCurr;
        }
        else if( NULL != pStartStr && ( !isChar || 0 == *pCurr ) ) // found the end of a string
        {
            // is string NULL or Non-Ascii terminated
            if( 0 == *pCurr || !rpal_string_charIsAscii( *pCurr ) )
            {
                // Null terminate it so we can use it like a normal string
                *pCurr = 0;

                // strlen is really pCurr - pStartStr
                if( (RU32)( (RPCHAR)pCurr - pStartStr ) >= minLength &&
                    (RU32)( (RPCHAR)pCurr - pStartStr ) <= maxLength ) // is string long enough
                {
                    // convert string to wide char for comparision
                    if( NULL != ( thisStrW = rpal_string_atow( pStartStr ) ) )
                    {
                        if( _isStringInList( searchStrings, thisStrW ) && NULL != ( newFoundStr = rSequence_new() ) )
                        {
                            rSequence_addSTRINGW( newFoundStr, RP_TAGS_STRING, thisStrW );
                            rSequence_addRU64( newFoundStr, RP_TAGS_MEMORY_ADDRESS, baseAddr + ( (RPU8)pStartStr - pBuff ) );

                            if( !rList_addSEQUENCE( stringsFound, newFoundStr ) )
                            {
                                rSequence_free( newFoundStr );
                            }
                        }
                        rpal_memory_free( thisStrW );
                    }
                }
            }
            pStartStr = NULL;
        }
        pCurr++;
    }
    // Now look for Unicode strings
    pwCurr = (RPU16)pBuff;
    pwEnd = pwCurr + ( size / 2 );
    while( pwCurr < pwEnd )
    {
        isWChar = rpal_string_isprintW( *pwCurr );

        if( NULL == pwStartStr && isWChar )  // found the begining of a string
        {
            pwStartStr = (RPWCHAR)pwCurr;
        }
        else if( NULL != pwStartStr && ( !isWChar || 0 == *pwCurr ) ) // found the end of a string
        {
            // is string NULL terminated
            if( 0 == *pwCurr )
            {
                // wcslen is really pCurr - pStartStr
                if( (RU32)( (RPWCHAR)pwCurr - pwStartStr ) >= minLength &&
                    (RU32)( (RPWCHAR)pwCurr - pwStartStr ) <= maxLength ) // is string long enough
                {

                    if( _isStringInList( searchStrings, pwStartStr ) && NULL != ( newFoundStr = rSequence_new() ) )
                    {
                        rSequence_addSTRINGW( newFoundStr, RP_TAGS_STRING, pwStartStr );
                        rSequence_addRU64( newFoundStr, RP_TAGS_MEMORY_ADDRESS, baseAddr + ( (RPU8)pwStartStr - pBuff ) );

                        if( !rList_addSEQUENCE( stringsFound, newFoundStr ) )
                        {
                            rSequence_free( newFoundStr );
                        }
                    }
                }
            }
            pwStartStr = NULL;
        }
        pwCurr++;
    }
}
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 );
        }
    }
}