RBOOL stopBeacons ( ) { RBOOL isSuccess = FALSE; if( NULL != g_hcpContext.isBeaconTimeToStop ) { rEvent_set( g_hcpContext.isBeaconTimeToStop ); if( 0 != g_hcpContext.hBeaconThread ) { rpal_thread_wait( g_hcpContext.hBeaconThread, MSEC_FROM_SEC( 40 ) ); rpal_thread_free( g_hcpContext.hBeaconThread ); isSuccess = TRUE; } rEvent_free( g_hcpContext.isBeaconTimeToStop ); g_hcpContext.isBeaconTimeToStop = NULL; } return isSuccess; }
RBOOL hbs_whenCpuBelow ( RU8 percent, RTIME timeoutSeconds, rEvent abortEvent ) { RBOOL isCpuIdle = FALSE; RTIME end = rpal_time_getLocal() + timeoutSeconds; do { if( libOs_getCpuUsage() <= percent ) { isCpuIdle = TRUE; break; } if( NULL == abortEvent ) { rpal_thread_sleep( MSEC_FROM_SEC( 1 ) ); } else { if( rEvent_wait( abortEvent, MSEC_FROM_SEC( 1 ) ) ) { break; } } } while( end > rpal_time_getLocal() ); return isCpuIdle; }
static RPVOID lookForHiddenModulesConstantly ( rEvent isTimeToStop, RPVOID ctx ) { rSequence originalRequest = (rSequence)ctx; processLibProcEntry* procs = NULL; processLibProcEntry* proc = NULL; LibOsPerformanceProfile perfProfile = { 0 }; perfProfile.enforceOnceIn = 4; perfProfile.sanityCeiling = MSEC_FROM_SEC( 20 ); perfProfile.lastTimeoutValue = 200; perfProfile.targetCpuPerformance = 0; perfProfile.globalTargetCpuPerformance = GLOBAL_CPU_USAGE_TARGET; perfProfile.timeoutIncrementPerSec = 50; while( rpal_memory_isValid( isTimeToStop ) && !rEvent_wait( isTimeToStop, 0 ) ) { if( NULL != ( procs = processLib_getProcessEntries( TRUE ) ) ) { proc = procs; while( 0 != proc->pid && rpal_memory_isValid( isTimeToStop ) && !rEvent_wait( isTimeToStop, _TIMEOUT_BETWEEN_CONSTANT_PROCESSS ) ) { if( hbs_whenCpuBelow( _CPU_WATERMARK, _MAX_CPU_WAIT, isTimeToStop ) ) { lookForHiddenModulesIn( isTimeToStop, proc->pid, originalRequest, &perfProfile ); } proc++; } rpal_memory_free( procs ); } } return NULL; }
RPRIVATE RU32 RPAL_THREAD_FUNC thread_quitAndCleanup ( RPVOID context ) { UNREFERENCED_PARAMETER( context ); if( 0 == rInterlocked_decrement32( &g_hcpContext.isRunning ) ) { rpal_thread_sleep( MSEC_FROM_SEC( 1 ) ); stopAllModules(); stopBeacons(); if( NULL != g_hcpContext.enrollmentToken && 0 != g_hcpContext.enrollmentTokenSize ) { rpal_memory_free( g_hcpContext.enrollmentToken ); } // If the default crashContext is still present, remove it since // we are shutting down properly. If it's non-default leave it since // somehow we may have had a higher order crash we want to keep // track of but we are still leaving through our normal code path. if( 1 == getCrashContextSize() ) { cleanCrashContext(); } rpal_Context_cleanup(); rpal_Context_deinitialize(); } else { rInterlocked_increment32( &g_hcpContext.isRunning ); } return 0; }
static RBOOL startCollectors ( ) { RBOOL isSuccess = FALSE; RU32 i = 0; rEvent_unset( g_hbs_state.isTimeToStop ); if( NULL != ( g_hbs_state.hThreadPool = rThreadPool_create( 1, 15, MSEC_FROM_SEC( 10 ) ) ) ) { isSuccess = TRUE; for( i = 0; i < ARRAY_N_ELEM( g_collectors ); i++ ) { if( g_collectors[ i ].isEnabled ) { if( !g_collectors[ i ].init( &g_hbs_state, g_collectors[ i ].conf ) ) { isSuccess = FALSE; rpal_debug_warning( "collector %d failed to init.", i ); } else { rpal_debug_info( "collector %d started.", i ); } } else { rpal_debug_info( "collector %d disabled.", i ); } } } return isSuccess; }
static RPVOID parseDocuments ( rEvent isTimeToStop, RPVOID ctx ) { rSequence createEvt = NULL; UNREFERENCED_PARAMETER( ctx ); while( rpal_memory_isValid( isTimeToStop ) && !rEvent_wait( isTimeToStop, 0 ) ) { if( rQueue_remove( createQueue, &createEvt, NULL, MSEC_FROM_SEC( 1 ) ) ) { processFile( createEvt ); } } return NULL; }
static RVOID scan_for_hidden_module ( rpcm_tag eventType, rSequence event ) { RU32 pid = (RU32)(-1); rEvent dummy = NULL; LibOsPerformanceProfile perfProfile = { 0 }; UNREFERENCED_PARAMETER( eventType ); perfProfile.enforceOnceIn = 4; perfProfile.sanityCeiling = MSEC_FROM_SEC( 20 ); perfProfile.lastTimeoutValue = 100; perfProfile.targetCpuPerformance = 10; perfProfile.globalTargetCpuPerformance = GLOBAL_CPU_USAGE_TARGET_WHEN_TASKED; perfProfile.timeoutIncrementPerSec = 50; rSequence_getRU32( event, RP_TAGS_PROCESS_ID, &pid ); if( NULL != ( dummy = rEvent_create( TRUE ) ) ) { if( (RU32)( -1 ) == pid ) { lookForHiddenModules( dummy, event ); } else { lookForHiddenModulesIn( dummy, pid, event, &perfProfile ); } rEvent_free( dummy ); } }
//============================================================================= // Entry Point //============================================================================= RU32 RPAL_THREAD_FUNC RpHcpI_mainThread ( rEvent isTimeToStop ) { RU32 ret = 0; RU64 nextBeaconTime = 0; rList cloudMessages = NULL; rSequence msg = NULL; rList newConfigurations = NULL; rList newNotifications = NULL; RPU8 newConfigurationHash = NULL; RU32 newHashSize = 0; rSequence staticConfig = NULL; RU8* tmpBuffer = NULL; RU32 tmpSize = 0; FORCE_LINK_THAT( HCP_IFACE ); CryptoLib_init(); getPrivileges(); // This is the event for the collectors, it is different than the // hbs proper event so that we can restart the collectors without // signaling hbs as a whole. if( NULL == ( g_hbs_state.isTimeToStop = rEvent_create( TRUE ) ) ) { return (RU32)-1; } // By default, no collectors are running rEvent_set( g_hbs_state.isTimeToStop ); // We attempt to load some initial config from the serialized // rSequence that can be patched in this binary. if( NULL != ( staticConfig = getStaticConfig() ) ) { if( rSequence_getBUFFER( staticConfig, RP_TAGS_HBS_ROOT_PUBLIC_KEY, &tmpBuffer, &tmpSize ) ) { hbs_cloud_pub_key = rpal_memory_duplicate( tmpBuffer, tmpSize ); if( NULL == hbs_cloud_pub_key ) { hbs_cloud_pub_key = hbs_cloud_default_pub_key; } rpal_debug_info( "loading hbs root public key from static config" ); } if( rSequence_getRU32( staticConfig, RP_TAGS_MAX_QUEUE_SIZE, &g_hbs_state.maxQueueNum ) ) { rpal_debug_info( "loading max queue size from static config" ); } else { g_hbs_state.maxQueueNum = HBS_EXFIL_QUEUE_MAX_NUM; } if( rSequence_getRU32( staticConfig, RP_TAGS_MAX_SIZE, &g_hbs_state.maxQueueSize ) ) { rpal_debug_info( "loading max queue num from static config" ); } else { g_hbs_state.maxQueueSize = HBS_EXFIL_QUEUE_MAX_SIZE; } rSequence_free( staticConfig ); } else { hbs_cloud_pub_key = hbs_cloud_default_pub_key; g_hbs_state.maxQueueNum = HBS_EXFIL_QUEUE_MAX_NUM; g_hbs_state.maxQueueSize = HBS_EXFIL_QUEUE_MAX_SIZE; } if( !rQueue_create( &g_hbs_state.outQueue, freeExfilEvent, g_hbs_state.maxQueueNum ) ) { rEvent_free( g_hbs_state.isTimeToStop ); return (RU32)-1; } // We simply enqueue a message to let the cloud know we're starting sendStartupEvent(); while( !rEvent_wait( isTimeToStop, (RU32)nextBeaconTime ) ) { nextBeaconTime = MSEC_FROM_SEC( HBS_DEFAULT_BEACON_TIMEOUT + ( rpal_rand() % HBS_DEFAULT_BEACON_TIMEOUT_FUZZ ) ); if( NULL != ( cloudMessages = beaconHome() ) ) { while( rList_getSEQUENCE( cloudMessages, RP_TAGS_MESSAGE, &msg ) ) { // Cloud message indicating next requested beacon time, as a Seconds delta if( rSequence_getTIMEDELTA( msg, RP_TAGS_TIMEDELTA, &nextBeaconTime ) ) { nextBeaconTime = MSEC_FROM_SEC( nextBeaconTime ); rpal_debug_info( "received set_next_beacon" ); } if( NULL == newConfigurations && rSequence_getLIST( msg, RP_TAGS_HBS_CONFIGURATIONS, &newConfigurations ) ) { rpal_debug_info( "received a new profile" ); if( rSequence_getBUFFER( msg, RP_TAGS_HASH, &newConfigurationHash, &newHashSize ) && newHashSize == CRYPTOLIB_HASH_SIZE ) { newConfigurations = rList_duplicate( newConfigurations ); rpal_memory_memcpy( &( g_hbs_state.currentConfigHash ), newConfigurationHash, CRYPTOLIB_HASH_SIZE ); g_hbs_state.isProfilePresent = TRUE; } else { newConfigurations = NULL; rpal_debug_error( "profile hash received is invalid" ); } } if( NULL == newNotifications && rSequence_getLIST( msg, RP_TAGS_HBS_CLOUD_NOTIFICATIONS, &newNotifications ) ) { rpal_debug_info( "received cloud events" ); newNotifications = rList_duplicate( newNotifications ); } } rList_free( cloudMessages ); } // If this is the initial boot and we have no profile yet, we'll load a dummy // blank profile and use our defaults. if( NULL == newConfigurations && !g_hbs_state.isProfilePresent && !rEvent_wait( isTimeToStop, 0 ) ) { newConfigurations = rList_new( RP_TAGS_HCP_MODULES, RPCM_SEQUENCE ); rpal_debug_info( "setting empty profile" ); } if( NULL != newConfigurations ) { // We try to be as responsive as possible when asked to quit // so if we happen to have received the signal during a beacon // we will action the quit instead of the config change. if( !rEvent_wait( isTimeToStop, 0 ) ) { rpal_debug_info( "begining sensor update on new profile" ); shutdownCollectors(); updateCollectorConfigs( newConfigurations ); rList_free( newConfigurations ); newConfigurations = NULL; startCollectors(); } else { rList_free( newConfigurations ); } newConfigurations = NULL; } if( NULL != newNotifications ) { if( !rEvent_wait( isTimeToStop, 0 ) ) { publishCloudNotifications( newNotifications ); } rList_free( newNotifications ); newNotifications = NULL; } } // We issue one last beacon indicating we are stopping sendShutdownEvent(); // Shutdown everything shutdownCollectors(); // Cleanup the last few resources rEvent_free( g_hbs_state.isTimeToStop ); rQueue_free( g_hbs_state.outQueue ); CryptoLib_deinit(); if( hbs_cloud_default_pub_key != hbs_cloud_pub_key && NULL != hbs_cloud_pub_key ) { rpal_memory_free( hbs_cloud_pub_key ); hbs_cloud_pub_key = NULL; } return ret; }
static RU32 uninstallService ( ) { RWCHAR destPath[] = _WCH( "%SYSTEMROOT%\\system32\\rphcp.exe" ); SC_HANDLE hScm = NULL; SC_HANDLE hSvc = NULL; RWCHAR svcName[] = { _SERVICE_NAMEW }; SERVICE_STATUS svcStatus = { 0 }; RU32 nRetries = 10; rpal_debug_info( "uninstalling service" ); if( NULL != ( hScm = OpenSCManagerA( NULL, NULL, SC_MANAGER_ALL_ACCESS ) ) ) { if( NULL != ( hSvc = OpenServiceW( hScm, svcName, SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE ) ) ) { if( ControlService( hSvc, SERVICE_CONTROL_STOP, &svcStatus ) ) { while( SERVICE_STOPPED != svcStatus.dwCurrentState && 0 != nRetries ) { rpal_debug_error( "waiting for service to stop..." ); rpal_thread_sleep( 1000 ); if( !QueryServiceStatus( hSvc, &svcStatus ) ) { break; } nRetries--; } if( 0 == nRetries ) { rpal_debug_error( "timed out waiting for service to stop, moving on..." ); } else { rpal_debug_info( "service stopped" ); } } else { rpal_debug_error( "could not stop service: %d", GetLastError() ); } if( DeleteService( hSvc ) ) { rpal_debug_info( "service deleted" ); } else { rpal_debug_error( "could not delete service: %d", GetLastError() ); } CloseServiceHandle( hSvc ); } else { rpal_debug_error( "could not open service: %d", GetLastError() ); } CloseServiceHandle( hScm ); } else { rpal_debug_error( "could not open SCM: %d", GetLastError() ); } rpal_thread_sleep( MSEC_FROM_SEC( 1 ) ); if( rpal_file_delete( destPath, FALSE ) ) { rpal_debug_info( "service executable deleted" ); } else { rpal_debug_error( "could not delete service executable: %d", GetLastError() ); } return GetLastError(); }
static RPVOID spotCheckNewProcesses ( rEvent isTimeToStop, RPVOID ctx ) { RU32 pid = 0; RTIME timestamp = 0; RTIME timeToWait = 0; RTIME now = 0; rSequence newProcess = NULL; rList hollowedModules = NULL; LibOsPerformanceProfile perfProfile = { 0 }; UNREFERENCED_PARAMETER( ctx ); perfProfile.sanityCeiling = _SANITY_CEILING; perfProfile.lastTimeoutValue = _INITIAL_PROFILED_TIMEOUT; perfProfile.targetCpuPerformance = 0; perfProfile.globalTargetCpuPerformance = GLOBAL_CPU_USAGE_TARGET; perfProfile.enforceOnceIn = 7; perfProfile.timeoutIncrementPerSec = _PROFILE_INCREMENT; while( !rEvent_wait( isTimeToStop, 0 ) ) { if( rQueue_remove( g_newProcessNotifications, &newProcess, NULL, MSEC_FROM_SEC( 5 ) ) ) { if( rSequence_getRU32( newProcess, RP_TAGS_PROCESS_ID, &pid ) && rSequence_getTIMESTAMP( newProcess, RP_TAGS_TIMESTAMP, ×tamp ) ) { timeToWait = timestamp + _CHECK_SEC_AFTER_PROCESS_CREATION; now = rpal_time_getGlobalPreciseTime(); if( now < timeToWait ) { timeToWait = timeToWait - now; if( _CHECK_SEC_AFTER_PROCESS_CREATION < timeToWait ) { // Sanity check timeToWait = _CHECK_SEC_AFTER_PROCESS_CREATION; } rpal_thread_sleep( (RU32)timeToWait ); } if( NULL != ( hollowedModules = _spotCheckProcess( isTimeToStop, pid, &perfProfile ) ) ) { if( !rSequence_addLIST( newProcess, RP_TAGS_MODULES, hollowedModules ) ) { rList_free( hollowedModules ); } else { hbs_publish( RP_TAGS_NOTIFICATION_MODULE_MEM_DISK_MISMATCH, newProcess ); } } } rSequence_free( newProcess ); } } return NULL; }
RPRIVATE RPVOID osTrackerDiffThread ( rEvent isTimeToStop, RPVOID ctx ) { CryptoLib_Hash* prevServices = NULL; RU32 prevNServices = 0; CryptoLib_Hash* prevDrivers = NULL; RU32 prevNDrivers = 0; CryptoLib_Hash* prevAutoruns = NULL; RU32 prevNAutoruns = 0; rList snapshot = NULL; UNREFERENCED_PARAMETER( ctx ); while( !rEvent_wait( isTimeToStop, g_diff_timeout ) ) { rpal_debug_info( "looking for changes in os snapshots" ); if( NULL != ( snapshot = libOs_getServices( TRUE ) ) ) { _processSnapshot( snapshot, &prevServices, &prevNServices, RP_TAGS_SVC, RP_TAGS_NOTIFICATION_SERVICE_CHANGE ); rList_free( snapshot ); } if( rEvent_wait( isTimeToStop, MSEC_FROM_SEC( 5 ) ) ) { break; } #ifdef RPAL_PLATFORM_WINDOWS // Drivers are only available on Windows if( NULL != ( snapshot = libOs_getDrivers( TRUE ) ) ) { _processSnapshot( snapshot, &prevDrivers, &prevNDrivers, RP_TAGS_SVC, RP_TAGS_NOTIFICATION_DRIVER_CHANGE ); rList_free( snapshot ); } if( rEvent_wait( isTimeToStop, MSEC_FROM_SEC( 5 ) ) ) { break; } #endif #if defined( RPAL_PLATFORM_WINDOWS ) || defined( RPAL_PLATFORM_MACOSX ) // Services are currently only available on OSX and Windows if( NULL != ( snapshot = libOs_getAutoruns( TRUE ) ) ) { _processSnapshot( snapshot, &prevAutoruns, &prevNAutoruns, RP_TAGS_SVC, RP_TAGS_NOTIFICATION_AUTORUN_CHANGE ); rList_free( snapshot ); } rpal_debug_info( "finished updating snapshots" ); #endif } FREE_AND_NULL( prevServices ); FREE_AND_NULL( prevDrivers ); FREE_AND_NULL( prevAutoruns ); return NULL; }
RPRIVATE RVOID processDnsPacket ( KernelAcqDnsPacket* pDns ) { rSequence notification = NULL; RU32 i = 0; DnsLabel* pLabel = NULL; DnsHeader* dnsHeader = NULL; DnsResponseInfo* pResponseInfo = NULL; RCHAR domain[ DNS_LABEL_MAX_SIZE ] = { 0 }; RU16 recordType = 0; RU64 timestamp = 0; Atom parentAtom = { 0 }; if( NULL == pDns ) { return; } dnsHeader = (DnsHeader*)( (RPU8)pDns + sizeof( *pDns ) ); pLabel = (DnsLabel*)dnsHeader->data; // We are parsing DNS packets coming from the kernel. They may: // 1- Be requests and not responses, check there are Answers. // 2- Be maliciously crafter packets so we need extra checking for sanity. if( 0 == dnsHeader->anCount || 0 == dnsHeader->qr || DNS_SANITY_MAX_RECORDS < rpal_ntoh16( dnsHeader->qdCount ) || DNS_SANITY_MAX_RECORDS < rpal_ntoh16( dnsHeader->anCount ) ) { return; } // We need to walk the Questions first to get to the Answers // but we don't really care to record them since they'll be repeated // in the Answers. for( i = 0; i < rpal_ntoh16( dnsHeader->qdCount ); i++ ) { DnsQuestionInfo* pQInfo = NULL; pLabel = dnsReadLabels( pLabel, NULL, (RPU8)dnsHeader, pDns->packetSize, 0, 0 ); pQInfo = (DnsQuestionInfo*)( pLabel ); if( !IS_WITHIN_BOUNDS( pQInfo, sizeof( *pQInfo ), dnsHeader, pDns->packetSize ) ) { rpal_debug_warning( "error parsing dns packet" ); break; } pLabel = (DnsLabel*)( (RPU8)pQInfo + sizeof( *pQInfo ) ); } if( !IS_WITHIN_BOUNDS( pLabel, sizeof( RU16 ), dnsHeader, pDns->packetSize ) ) { rpal_debug_warning( "error parsing dns packet" ); return; } // This is what we care about, the Answers (which also point to each Question). // We will emit one event per Answer so as to keep the DNS_REQUEST event flat and atomic. for( i = 0; i < rpal_ntoh16( dnsHeader->anCount ); i++ ) { pResponseInfo = NULL; // This was the Question for this answer. rpal_memory_zero( domain, sizeof( domain ) ); pLabel = dnsReadLabels( pLabel, domain, (RPU8)dnsHeader, pDns->packetSize, 0, 0 ); pResponseInfo = (DnsResponseInfo*)pLabel; pLabel = (DnsLabel*)( (RPU8)pResponseInfo + sizeof( *pResponseInfo ) + rpal_ntoh16( pResponseInfo->rDataLength ) ); if( !IS_WITHIN_BOUNDS( pResponseInfo, sizeof( *pResponseInfo ), dnsHeader, pDns->packetSize ) ) { rpal_debug_warning( "error parsing dns packet" ); break; } if( NULL == ( notification = rSequence_new() ) ) { rpal_debug_warning( "error parsing dns packet" ); break; } // This is a timestamp coming from the kernel so it is not globally adjusted. // We'll adjust it with the global offset. timestamp = pDns->ts; timestamp += MSEC_FROM_SEC( rpal_time_getGlobalFromLocal( 0 ) ); // Try to relate the DNS request to the owner process, this only works on OSX // at the moment (since the kernel does not expose the PID at the packet capture // stage), and even on OSX it's the DNSResolver process. So it's not super useful // but regardless we have the mechanism here as it's better than nothing and when // we add better resolving in the kernel it will work transparently. parentAtom.key.process.pid = pDns->pid; parentAtom.key.category = RP_TAGS_NOTIFICATION_NEW_PROCESS; if( atoms_query( &parentAtom, timestamp ) ) { HbsSetParentAtom( notification, parentAtom.id ); } rSequence_addTIMESTAMP( notification, RP_TAGS_TIMESTAMP, timestamp ); rSequence_addSTRINGA( notification, RP_TAGS_DOMAIN_NAME, domain ); rSequence_addRU32( notification, RP_TAGS_PROCESS_ID, pDns->pid ); recordType = rpal_ntoh16( pResponseInfo->recordType ); rSequence_addRU16( notification, RP_TAGS_MESSAGE_ID, rpal_ntoh16( dnsHeader->msgId ) ); rSequence_addRU16( notification, RP_TAGS_DNS_TYPE, recordType ); if( DNS_A_RECORD == recordType ) { rSequence_addIPV4( notification, RP_TAGS_IP_ADDRESS, *(RU32*)pResponseInfo->rData ); } else if( DNS_AAAA_RECORD == recordType ) { rSequence_addIPV6( notification, RP_TAGS_IP_ADDRESS, pResponseInfo->rData ); } else if( DNS_CNAME_RECORD == recordType ) { // CNAME records will have another label as a value and not an IP. rpal_memory_zero( domain, sizeof( domain ) ); dnsReadLabels( (DnsLabel*)pResponseInfo->rData, domain, (RPU8)dnsHeader, pDns->packetSize, 0, 0 ); rSequence_addSTRINGA( notification, RP_TAGS_CNAME, domain ); } else { // Right now we only care for A, CNAME and AAAA records. rSequence_free( notification ); notification = NULL; continue; } hbs_publish( RP_TAGS_NOTIFICATION_DNS_REQUEST, notification ); rSequence_free( notification ); notification = 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 RBOOL notifyOfKernelModule ( KernelAcqModule* module ) { RBOOL isSuccess = FALSE; rSequence notif = NULL; RU32 pathLength = 0; RU32 i = 0; RPNCHAR dirSep = RPAL_FILE_LOCAL_DIR_SEP_N; RPNCHAR cleanPath = NULL; Atom parentAtom = { 0 }; if( NULL != module ) { if( NULL != ( notif = rSequence_new() ) ) { module->ts += MSEC_FROM_SEC( rpal_time_getGlobalFromLocal( 0 ) ); hbs_timestampEvent( notif, module->ts ); parentAtom.key.category = RP_TAGS_NOTIFICATION_NEW_PROCESS; parentAtom.key.process.pid = module->pid; if( atoms_query( &parentAtom, module->ts ) ) { HbsSetParentAtom( notif, parentAtom.id ); } rSequence_addRU32( notif, RP_TAGS_PROCESS_ID, module->pid ); rSequence_addPOINTER64( notif, RP_TAGS_BASE_ADDRESS, (RU64)module->baseAddress ); rSequence_addRU64( notif, RP_TAGS_MEMORY_SIZE, module->imageSize ); if( 0 != ( pathLength = rpal_string_strlen( module->path ) ) ) { cleanPath = rpal_file_clean( module->path ); rSequence_addSTRINGN( notif, RP_TAGS_FILE_PATH, cleanPath ? cleanPath : module->path ); rpal_memory_free( cleanPath ); // For compatibility with user mode we extract the module name. for( i = pathLength - 1; i != 0; i-- ) { if( dirSep[ 0 ] == module->path[ i ] ) { i++; break; } } rSequence_addSTRINGN( notif, RP_TAGS_MODULE_NAME, &( module->path[ i ] ) ); if( hbs_publish( RP_TAGS_NOTIFICATION_MODULE_LOAD, notif ) ) { isSuccess = TRUE; } } rSequence_free( notif ); } } return isSuccess; }
static RPVOID continuousFileScan ( rEvent isTimeToStop, RPVOID ctx ) { rSequence event = NULL; RU32 timeout = 0; RPWCHAR strW = NULL; RPCHAR strA = NULL; YaraMatchContext matchContext = { 0 }; RU32 scanError = 0; rBloom knownFiles = NULL; UNREFERENCED_PARAMETER( ctx ); if( NULL == ( knownFiles = rpal_bloom_create( 100000, 0.00001 ) ) ) { return NULL; } while( !rEvent_wait( isTimeToStop, timeout ) ) { if( rQueue_remove( g_async_files_to_scan, (RPVOID*)&event, NULL, MSEC_FROM_SEC( 2 ) ) ) { if( rSequence_getSTRINGW( event, RP_TAGS_FILE_PATH, &strW ) ) { strA = rpal_string_wtoa( strW ); } else { rSequence_getSTRINGA( event, RP_TAGS_FILE_PATH, &strA ); } if( NULL != strA && rpal_bloom_addIfNew( knownFiles, strA, rpal_string_strlen( strA ) ) ) { rpal_debug_info( "yara scanning %s", strA ); matchContext.fileInfo = event; if( rMutex_lock( g_global_rules_mutex ) ) { if( NULL != g_global_rules ) { rpal_debug_info( "scanning continuous file with yara" ); if( ERROR_SUCCESS != ( scanError = yr_rules_scan_file( g_global_rules, strA, SCAN_FLAGS_FAST_MODE, _yaraFileMatchCallback, &matchContext, 60 ) ) ) { rpal_debug_warning( "Yara file scan error: %d", scanError ); } } rMutex_unlock( g_global_rules_mutex ); } } if( NULL != strA && NULL != strW ) { // If both are allocated it means we got a strW and converted to A // so we must free the strA version. rpal_memory_free( strA ); } strA = NULL; strW = NULL; rSequence_free( event ); timeout = _TIMEOUT_BETWEEN_FILE_SCANS; } else { timeout = 0; } } rpal_bloom_destroy( knownFiles ); yr_finalize_thread(); return NULL; }
static RU32 _scanProcessWith ( RU32 pid, YaraMatchContext* matchContext, YR_RULES* rules, rEvent isTimeToStop ) { RU32 scanError = (RU32)(-1); rList modules = NULL; rSequence module = NULL; _MemRange* memRanges = NULL; RU32 i = 0; rList memoryMap = NULL; rSequence memoryRegion = NULL; RU8 memAccess = 0; RU64 mem = 0; RU64 memSize = 0; RPU8 buffer = NULL; // First pass is to scan known modules if( NULL != ( modules = processLib_getProcessModules( pid ) ) ) { rpal_debug_info( "scanning process %d module memory with yara", pid ); // We also generate an optimized list of module ranges for later if( NULL != ( memRanges = rpal_memory_alloc( sizeof( _MemRange ) * rList_getNumElements( modules ) ) ) ) { while( ( NULL == isTimeToStop || !rEvent_wait( isTimeToStop, MSEC_FROM_SEC( 5 ) ) ) && rList_getSEQUENCE( modules, RP_TAGS_DLL, &module ) ) { if( rSequence_getPOINTER64( module, RP_TAGS_BASE_ADDRESS, &mem ) && rSequence_getRU64( module, RP_TAGS_MEMORY_SIZE, &memSize ) ) { memRanges[ i ].base = mem; memRanges[ i ].size = memSize; i++; matchContext->regionBase = mem; matchContext->regionSize = memSize; matchContext->moduleInfo = module; if( processLib_getProcessMemory( pid, (RPVOID)NUMBER_TO_PTR( mem ), memSize, (RPVOID*)&buffer, TRUE ) ) { rpal_debug_info( "yara scanning module region of size 0x%lx", memSize ); if( NULL != rules || rMutex_lock( g_global_rules_mutex ) ) { if( ERROR_SUCCESS != ( scanError = yr_rules_scan_mem( NULL == rules ? g_global_rules : rules, buffer, (size_t)memSize, SCAN_FLAGS_FAST_MODE | SCAN_FLAGS_PROCESS_MEMORY, _yaraMemMatchCallback, matchContext, 60 ) ) ) { rpal_debug_warning( "Yara module scan error: %d 0x%lx 0x%lx: %d", pid, mem, memSize, scanError ); } if( NULL == rules ) { rMutex_unlock( g_global_rules_mutex ); } } rpal_memory_free( buffer ); rpal_debug_info( "finished region" ); } } } } rList_free( modules ); } // Optimize the memory ranges if( rpal_memory_isValid( memRanges ) && !rpal_sort_array( memRanges, i, sizeof( _MemRange ), (rpal_ordering_func)rpal_order_RU64 ) ) { rpal_memory_free( memRanges ); memRanges = NULL; } // Second pass is to go through executable non-module areas if( NULL != ( memoryMap = processLib_getProcessMemoryMap( pid ) ) ) { rpal_debug_info( "scanning process %d non-module memory with yara", pid ); while( ( NULL == isTimeToStop || !rEvent_wait(isTimeToStop, MSEC_FROM_SEC( 5 ) ) ) && rList_getSEQUENCE( memoryMap, RP_TAGS_MEMORY_REGION, &memoryRegion ) ) { if( rSequence_getPOINTER64( memoryRegion, RP_TAGS_BASE_ADDRESS, &mem ) && rSequence_getRU64( memoryRegion, RP_TAGS_MEMORY_SIZE, &memSize ) && rSequence_getRU8( memoryRegion, RP_TAGS_MEMORY_ACCESS, &memAccess ) && ( PROCESSLIB_MEM_ACCESS_EXECUTE == memAccess || PROCESSLIB_MEM_ACCESS_EXECUTE_READ == memAccess || PROCESSLIB_MEM_ACCESS_EXECUTE_READ_WRITE == memAccess || PROCESSLIB_MEM_ACCESS_EXECUTE_WRITE_COPY == memAccess ) ) { // If it's in a known module, skip if( (RU32)( -1 ) != rpal_binsearch_array_closest( memRanges, i, sizeof( _MemRange ), &mem, (rpal_ordering_func)rpal_order_RU64, TRUE ) ) { continue; } matchContext->regionBase = mem; matchContext->regionSize = memSize; matchContext->moduleInfo = NULL; if( processLib_getProcessMemory( pid, (RPVOID)NUMBER_TO_PTR(mem), memSize, (RPVOID*)&buffer, TRUE ) ) { rpal_debug_info( "yara scanning memory region of size 0x%lx", memSize ); if( NULL != rules || rMutex_lock( g_global_rules_mutex ) ) { if( ERROR_SUCCESS != ( scanError = yr_rules_scan_mem( NULL == rules ? g_global_rules : rules, buffer, (size_t)memSize, SCAN_FLAGS_FAST_MODE | SCAN_FLAGS_PROCESS_MEMORY, _yaraMemMatchCallback, matchContext, 60 ) ) ) { rpal_debug_warning( "Yara memory scan error: %d 0x%lx 0x%lx: %d", pid, mem, memSize, scanError ); } if( NULL == rules ) { rMutex_unlock( g_global_rules_mutex ); } } rpal_memory_free( buffer ); } } } rList_free( memoryMap ); } if( rpal_memory_isValid( memRanges ) ) { rpal_memory_free( memRanges ); } return scanError; }
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; }
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 RPVOID continuousMemScan ( rEvent isTimeToStop, RPVOID ctx ) { processLibProcEntry* processes = NULL; processLibProcEntry* curProc = NULL; RU32 thisProcId = 0; YaraMatchContext matchContext = { 0 }; RU32 scanError = 0; UNREFERENCED_PARAMETER( ctx ); thisProcId = processLib_getCurrentPid(); while( !rEvent_wait( isTimeToStop, 0 ) ) { // Wait until we have global rules to look for. if( rMutex_lock( g_global_rules_mutex ) ) { if( NULL == g_global_rules ) { rMutex_unlock( g_global_rules_mutex ); rEvent_wait( isTimeToStop, MSEC_FROM_SEC( 30 ) ); continue; } rMutex_unlock( g_global_rules_mutex ); } if( NULL != ( processes = processLib_getProcessEntries( TRUE ) ) ) { curProc = processes; while( 0 != curProc->pid ) { // We can't examine our own memory for the risk of tripping on the sigs themselves. if( curProc->pid == thisProcId ) continue; rpal_debug_info( "yara scanning pid %d", curProc->pid ); matchContext.pid = curProc->pid; matchContext.processInfo = NULL; matchContext.moduleInfo = NULL; scanError = _scanProcessWith( curProc->pid, &matchContext, NULL, isTimeToStop ); rSequence_free( matchContext.processInfo ); if( rEvent_wait( isTimeToStop, MSEC_FROM_SEC( 30 ) ) ) { break; } curProc++; } rpal_memory_free( processes ); } } yr_finalize_thread(); return 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 osTrackerDiffThread ( rEvent isTimeToStop, RPVOID ctx ) { CryptoLib_Hash* prevServices = NULL; RU32 prevNServices = 0; CryptoLib_Hash* prevDrivers = NULL; RU32 prevNDrivers = 0; CryptoLib_Hash* prevAutoruns = NULL; RU32 prevNAutoruns = 0; rList snapshot = NULL; UNREFERENCED_PARAMETER( ctx ); while( !rEvent_wait( isTimeToStop, g_diff_timeout ) ) { rpal_debug_info( "looking for changes in os snapshots" ); if( NULL != ( snapshot = libOs_getServices( TRUE ) ) ) { _processSnapshot( snapshot, &prevServices, &prevNServices, RP_TAGS_SVC, RP_TAGS_NOTIFICATION_SERVICE_CHANGE ); rList_free( snapshot ); } if( rEvent_wait( isTimeToStop, MSEC_FROM_SEC( 5 ) ) ) { break; } if( NULL != ( snapshot = libOs_getDrivers( TRUE ) ) ) { _processSnapshot( snapshot, &prevDrivers, &prevNDrivers, RP_TAGS_SVC, RP_TAGS_NOTIFICATION_DRIVER_CHANGE ); rList_free( snapshot ); } if( rEvent_wait( isTimeToStop, MSEC_FROM_SEC( 5 ) ) ) { break; } if( NULL != ( snapshot = libOs_getAutoruns( TRUE ) ) ) { _processSnapshot( snapshot, &prevAutoruns, &prevNAutoruns, RP_TAGS_SVC, RP_TAGS_NOTIFICATION_AUTORUN_CHANGE ); rList_free( snapshot ); } rpal_debug_info( "finished updating snapshots" ); } FREE_AND_NULL( prevServices ); FREE_AND_NULL( prevDrivers ); FREE_AND_NULL( prevAutoruns ); return NULL; }
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; } }