static RVOID sendStartupEvent ( ) { rSequence wrapper = NULL; rSequence startupEvent = NULL; if( NULL != ( wrapper = rSequence_new() ) ) { if( NULL != ( startupEvent = rSequence_new() ) ) { if( rSequence_addSEQUENCE( wrapper, RP_TAGS_NOTIFICATION_STARTING_UP, startupEvent ) ) { if( !rSequence_addTIMESTAMP( startupEvent, RP_TAGS_TIMESTAMP, rpal_time_getGlobal() ) || !rQueue_add( g_hbs_state.outQueue, wrapper, 0 ) ) { rSequence_free( wrapper ); } } else { rSequence_free( wrapper ); rSequence_free( startupEvent ); } } else { rSequence_free( wrapper ); } } }
static RBOOL notifyOfProcess ( RU32 pid, RU32 ppid, RBOOL isStarting ) { RBOOL isSuccess = FALSE; rSequence info = NULL; rSequence parentInfo = NULL; if( !isStarting || NULL == ( info = processLib_getProcessInfo( pid ) ) ) { info = rSequence_new(); } if( rpal_memory_isValid( info ) ) { rSequence_addRU32( info, RP_TAGS_PROCESS_ID, pid ); rSequence_addRU32( info, RP_TAGS_PARENT_PROCESS_ID, ppid ); rSequence_addTIMESTAMP( info, RP_TAGS_TIMESTAMP, rpal_time_getGlobal() ); if( isStarting ) { if( NULL != ( parentInfo = processLib_getProcessInfo( ppid ) ) && !rSequence_addSEQUENCE( info, RP_TAGS_PARENT, parentInfo ) ) { rSequence_free( parentInfo ); } } if( isStarting ) { if( notifications_publish( RP_TAGS_NOTIFICATION_NEW_PROCESS, info ) ) { isSuccess = TRUE; rpal_debug_info( "new process starting: %d", pid ); } } else { if( notifications_publish( RP_TAGS_NOTIFICATION_TERMINATE_PROCESS, info ) ) { isSuccess = TRUE; rpal_debug_info( "new process terminating: %d", pid ); } } rSequence_free( info ); } return isSuccess; }
static RVOID shutdownCollectors ( ) { RU32 i = 0; if( !rEvent_wait( g_hbs_state.isTimeToStop, 0 ) ) { rpal_debug_info( "signaling to collectors to stop." ); rEvent_set( g_hbs_state.isTimeToStop ); if( NULL != g_hbs_state.hThreadPool ) { rpal_debug_info( "destroying collector thread pool." ); rThreadPool_destroy( g_hbs_state.hThreadPool, TRUE ); g_hbs_state.hThreadPool = NULL; for( i = 0; i < ARRAY_N_ELEM( g_collectors ); i++ ) { if( g_collectors[ i ].isEnabled ) { rpal_debug_info( "cleaning up collector %d.", i ); g_collectors[ i ].cleanup( &g_hbs_state, g_collectors[ i ].conf ); rSequence_free( g_collectors[ i ].conf ); g_collectors[ i ].conf = NULL; } } } } }
static int _yaraFileMatchCallback ( int message, void* message_data, void* user_data ) { YR_RULE* rule = (YR_RULE*)message_data; YaraMatchContext* context = (YaraMatchContext*)user_data; rSequence event = NULL; if( CALLBACK_MSG_RULE_MATCHING == message && NULL != message_data && NULL != user_data ) { if( NULL != ( event = rSequence_duplicate( context->fileInfo ) ) ) { rSequence_addSTRINGA( event, RP_TAGS_RULE_NAME, (char*)rule->identifier ); notifications_publish( RP_TAGS_NOTIFICATION_YARA_DETECTION, event ); rSequence_free( event ); } else { rpal_debug_warning( "error creating event from Yara match" ); } } return CALLBACK_CONTINUE; }
void test_modules ( void ) { processLibProcEntry* entries = NULL; RU32 entryIndex = 0; rList mods = NULL; rSequence mod = NULL; RPWCHAR path = NULL; entries = processLib_getProcessEntries( TRUE ); CU_ASSERT_PTR_NOT_EQUAL_FATAL( entries, NULL ); CU_ASSERT_NOT_EQUAL_FATAL( entries[ entryIndex ].pid, 0 ); mods = processLib_getProcessModules( entries[ entryIndex ].pid ); CU_ASSERT_PTR_NOT_EQUAL( mods, NULL ); CU_ASSERT_TRUE( rList_getSEQUENCE( mods, RP_TAGS_DLL, &mod ) ); CU_ASSERT_TRUE( rSequence_getSTRINGW( mod, RP_TAGS_FILE_PATH, &path ) ); CU_ASSERT_PTR_NOT_EQUAL( path, NULL ); CU_ASSERT_NOT_EQUAL( rpal_string_strlenw( path ), 0 ); rSequence_free( mods ); rpal_memory_free( entries ); }
void test_processInfo ( void ) { processLibProcEntry* entries = NULL; RU32 entryIndex = 0; rSequence proc = NULL; RPWCHAR path = NULL; entries = processLib_getProcessEntries( TRUE ); CU_ASSERT_PTR_NOT_EQUAL_FATAL( entries, NULL ); CU_ASSERT_NOT_EQUAL_FATAL( entries[ entryIndex ].pid, 0 ); proc = processLib_getProcessInfo( entries[ entryIndex ].pid ); CU_ASSERT_PTR_NOT_EQUAL( proc, NULL ); CU_ASSERT_TRUE( rSequence_getSTRINGW( proc, RP_TAGS_FILE_PATH, &path ) ); CU_ASSERT_PTR_NOT_EQUAL( path, NULL ); CU_ASSERT_NOT_EQUAL( rpal_string_strlenw( path ), 0 ); rSequence_free( proc ); rpal_memory_free( entries ); }
StatefulEvent* SMEvent_new ( rpcm_tag eventType, rSequence data ) { StatefulEvent* event = NULL; rSequence wrapper = NULL; if( NULL != data ) { if( NULL != ( wrapper = rSequence_new() ) ) { if( NULL != ( event = rpal_memory_alloc( sizeof( *event ) ) ) ) { if( NULL != ( event->ref = rRefCount_create( (rRefCount_freeFunc)_freeEvent, event, sizeof( *event ) ) ) ) { if( rSequence_addSEQUENCE( wrapper, eventType, data ) ) { event->data = wrapper; event->eventType = eventType; rSequence_getTIMESTAMP( data, RP_TAGS_TIMESTAMP, &event->ts ); } else { rSequence_free( wrapper ); } } else { rpal_memory_free( event ); event = NULL; rSequence_free( wrapper ); } } else { rSequence_free( wrapper ); } } } return event; }
static RVOID freeExfilEvent ( rSequence seq, RU32 unused ) { UNREFERENCED_PARAMETER( unused ); rSequence_free( seq ); }
static RVOID _freeEvt ( rSequence evt, RU32 unused ) { UNREFERENCED_PARAMETER( unused ); rSequence_free( evt ); }
static int _yaraMemMatchCallback ( int message, void* message_data, void* user_data ) { YR_RULE* rule = (YR_RULE*)message_data; YaraMatchContext* context = (YaraMatchContext*)user_data; rSequence event = NULL; if( CALLBACK_MSG_RULE_MATCHING == message && NULL != message_data && NULL != user_data ) { if( NULL != ( event = rSequence_new() ) ) { rSequence_addRU32( event, RP_TAGS_PROCESS_ID, context->pid ); rSequence_addPOINTER64( event, RP_TAGS_BASE_ADDRESS, context->regionBase ); rSequence_addRU64( event, RP_TAGS_MEMORY_SIZE, context->regionSize ); hbs_markAsRelated( context->fileInfo, event ); if( NULL == context->processInfo ) { context->processInfo = processLib_getProcessInfo( context->pid, NULL ); } if( NULL != context->processInfo ) { rSequence_addSEQUENCE( event, RP_TAGS_PROCESS, rSequence_duplicate( context->processInfo ) ); } if( NULL != context->moduleInfo ) { rSequence_addSEQUENCE( event, RP_TAGS_DLL, rSequence_duplicate( context->moduleInfo ) ); } rSequence_addSTRINGA( event, RP_TAGS_RULE_NAME, (char*)rule->identifier ); notifications_publish( RP_TAGS_NOTIFICATION_YARA_DETECTION, event ); rSequence_free( event ); } else { rpal_debug_warning( "error creating event from Yara match" ); } } return CALLBACK_CONTINUE; }
static RVOID sendShutdownEvent ( ) { rList cloudMessages = NULL; rSequence wrapper = NULL; rSequence shutdownEvent = NULL; if( NULL != ( wrapper = rSequence_new() ) ) { if( NULL != ( shutdownEvent = rSequence_new() ) ) { if( rSequence_addSEQUENCE( wrapper, RP_TAGS_NOTIFICATION_SHUTTING_DOWN, shutdownEvent ) ) { if( rSequence_addTIMESTAMP( shutdownEvent, RP_TAGS_TIMESTAMP, rpal_time_getGlobal() ) && rQueue_add( g_hbs_state.outQueue, wrapper, 0 ) ) { if( NULL != ( cloudMessages = beaconHome() ) ) { rList_free( cloudMessages ); } } else { rSequence_free( wrapper ); } } else { rSequence_free( wrapper ); rSequence_free( shutdownEvent ); } } else { rSequence_free( wrapper ); } } }
static RBOOL updateCollectorConfigs ( rList newConfigs ) { RBOOL isSuccess = FALSE; RU8 unused = 0; RU32 i = 0; rSequence tmpConf = NULL; RU32 confId = 0; if( rpal_memory_isValid( newConfigs ) ) { rpal_debug_info( "updating collector configurations." ); for( i = 0; i < ARRAY_N_ELEM( g_collectors ); i++ ) { if( NULL != g_collectors[ i ].conf ) { rpal_debug_info( "freeing collector %d config.", i ); rSequence_free( g_collectors[ i ].conf ); g_collectors[ i ].conf = NULL; } } while( rList_getSEQUENCE( newConfigs, RP_TAGS_HBS_CONFIGURATION, &tmpConf ) ) { if( rSequence_getRU32( tmpConf, RP_TAGS_HBS_CONFIGURATION_ID, &confId ) && confId < ARRAY_N_ELEM( g_collectors ) ) { if( rSequence_getRU8( tmpConf, RP_TAGS_IS_DISABLED, &unused ) ) { g_collectors[ confId ].isEnabled = FALSE; } else { g_collectors[ confId ].isEnabled = TRUE; g_collectors[ confId ].conf = rSequence_duplicate( tmpConf ); rpal_debug_info( "set new collector %d config.", confId ); } } } isSuccess = TRUE; } return isSuccess; }
static RVOID _freeSeq ( rSequence seq, RU32 dummySize ) { UNREFERENCED_PARAMETER( dummySize ); if( rpal_memory_isValid( seq ) ) { rSequence_free( seq ); } }
//============================================================================= // BOILERPLATE //============================================================================= static RVOID _freeEvent ( StatefulEvent* event, RU32 unused ) { UNREFERENCED_PARAMETER( unused ); if( rpal_memory_isValid( event ) ) { rSequence_free( event->data ); event->data = NULL; rpal_memory_free( event ); } }
static RBOOL _reportEvents ( StatefulMachine* machine ) { RBOOL isSuccess = FALSE; rSequence wrapper = NULL; rList events = NULL; rSequence event = NULL; RU32 i = 0; if( NULL != machine ) { if( NULL != ( wrapper = rSequence_new() ) ) { if( NULL != ( events = rList_new( RP_TAGS_EVENT, RPCM_SEQUENCE ) ) ) { for( i = 0; i < machine->history->nElements; i++ ) { event = ( (StatefulEvent*)machine->history->elements[ i ] )->data; rList_addSEQUENCE( events, rSequence_duplicate( event ) ); } if( !rSequence_addLIST( wrapper, RP_TAGS_EVENTS, events ) ) { rList_free( events ); } else { hbs_timestampEvent( wrapper, 0 ); isSuccess = hbs_publish( machine->desc->reportEventType, wrapper ); } } rSequence_free( wrapper ); } } return isSuccess; }
void test_memmap ( void ) { processLibProcEntry* entries = NULL; RU32 entryIndex = 0; rList regions = NULL; rSequence region = NULL; RU32 nRegions = 0; RU8 type = 0; RU8 protect = 0; RU64 ptr = 0; RU64 size = 0; entries = processLib_getProcessEntries( TRUE ); CU_ASSERT_PTR_NOT_EQUAL_FATAL( entries, NULL ); CU_ASSERT_NOT_EQUAL_FATAL( entries[ entryIndex ].pid, 0 ); regions = processLib_getProcessMemoryMap( entries[ entryIndex ].pid ); CU_ASSERT_PTR_NOT_EQUAL( regions, NULL ); while( rList_getSEQUENCE( regions, RP_TAGS_MEMORY_REGION, ®ion ) ) { CU_ASSERT_TRUE( rSequence_getRU8( region, RP_TAGS_MEMORY_TYPE, &type ) ); CU_ASSERT_TRUE( rSequence_getRU8( region, RP_TAGS_MEMORY_ACCESS, &protect ) ); CU_ASSERT_TRUE( rSequence_getPOINTER64( region, RP_TAGS_BASE_ADDRESS, &ptr ) ); CU_ASSERT_TRUE( rSequence_getRU64( region, RP_TAGS_MEMORY_SIZE, &size ) ); nRegions++; } CU_ASSERT_TRUE( 2 < nRegions ); rSequence_free( regions ); rpal_memory_free( entries ); }
static RVOID processCodeIdentA ( RPCHAR name, RPU8 pFileHash, RU64 codeSize, rSequence originalEvent ) { CodeIdent ident = { 0 }; rSequence notif = NULL; ident.codeSize = codeSize; if( NULL != name ) { CryptoLib_hash( name, rpal_string_strlen( name ) * sizeof( RCHAR ), ident.nameHash ); } if( NULL != pFileHash ) { rpal_memory_memcpy( ident.fileHash, pFileHash, CRYPTOLIB_HASH_SIZE ); } if( rpal_bloom_addIfNew( knownCode, &ident, sizeof( ident ) ) ) { if( NULL != ( notif = rSequence_new() ) ) { hbs_markAsRelated( originalEvent, notif ); if( rSequence_addSTRINGA( notif, RP_TAGS_FILE_NAME, name ) && rSequence_addBUFFER( notif, RP_TAGS_HASH, pFileHash, CRYPTOLIB_HASH_SIZE ) && rSequence_addRU32( notif, RP_TAGS_MEMORY_SIZE, (RU32)codeSize ) && rSequence_addTIMESTAMP( notif, RP_TAGS_TIMESTAMP, rpal_time_getGlobal() ) ) { notifications_publish( RP_TAGS_NOTIFICATION_CODE_IDENTITY, notif ); } rSequence_free( notif ); } } }
//============================================================================= // YARA Required Shims //============================================================================= static RVOID reportError ( rSequence originalRequest, RU32 errorCode, RPCHAR errorStr ) { rSequence event = NULL; if( NULL != ( event = rSequence_new() ) ) { hbs_markAsRelated( originalRequest, event ); rSequence_addRU32( event, RP_TAGS_ERROR, errorCode ); rSequence_addSTRINGA( event, RP_TAGS_ERROR_MESSAGE, errorStr ? errorStr : "" ); rSequence_addTIMESTAMP( event, RP_TAGS_TIMESTAMP, rpal_time_getGlobal() ); notifications_publish( RP_TAGS_NOTIFICATION_YARA_DETECTION, event ); rSequence_free( event ); } }
void test_servicesList ( void ) { rList svcs = NULL; rSequence svc = NULL; RU32 type = PROCESSLIB_SVCS; #if defined( RPAL_PLATFORM_WINDOWS ) || defined( RPAL_PLATFORM_LINUX ) RPWCHAR svcName = NULL; #elif defined( RPAL_PLATFORM_MACOSX ) RPCHAR svcName = NULL; #endif svcs = processLib_getServicesList( type ); CU_ASSERT_PTR_NOT_EQUAL_FATAL( svcs, NULL ); CU_ASSERT_TRUE( rList_getSEQUENCE( svcs, RP_TAGS_SVC, &svc ) ); #if defined( RPAL_PLATFORM_WINDOWS ) || defined( RPAL_PLATFORM_LINUX ) CU_ASSERT_TRUE( rSequence_getSTRINGW( svc, RP_TAGS_SVC_NAME, &svcName ) ); CU_ASSERT_PTR_NOT_EQUAL( svcName, NULL ); CU_ASSERT_NOT_EQUAL( rpal_string_strlenw( svcName ), 0 ); #elif defined( RPAL_PLATFORM_MACOSX ) CU_ASSERT_TRUE( rSequence_getSTRINGA( svc, RP_TAGS_SVC_NAME, &svcName ) ); CU_ASSERT_PTR_NOT_EQUAL( svcName, NULL ); CU_ASSERT_NOT_EQUAL( rpal_string_strlen( svcName ), 0 ); #endif rSequence_free( svcs ); }
static RVOID doScan ( rpcm_tag eventType, rSequence event ) { RU32 pid = 0; RPWCHAR fileW = NULL; RPCHAR fileA = NULL; RPWCHAR procW = NULL; RPCHAR procA = NULL; RPU8 rulesBuffer = NULL; RU32 rulesBufferSize = 0; YR_RULES* rules = NULL; YaraMatchContext matchContext = { 0 }; processLibProcEntry* processes = NULL; processLibProcEntry* curProc = NULL; RU32 scanError = 0; rSequence processInfo = NULL; RPWCHAR tmpW = NULL; RPCHAR tmpA = NULL; UNREFERENCED_PARAMETER( eventType ); if( rpal_memory_isValid( event ) ) { rSequence_getRU32( event, RP_TAGS_PROCESS_ID, &pid ); rSequence_getSTRINGW( event, RP_TAGS_FILE_PATH, &fileW ); rSequence_getSTRINGA( event, RP_TAGS_FILE_PATH, &fileA ); rSequence_getSTRINGW( event, RP_TAGS_PROCESS, &procW ); rSequence_getSTRINGA( event, RP_TAGS_PROCESS, &procA ); if( rSequence_getBUFFER( event, RP_TAGS_RULES, &rulesBuffer, &rulesBufferSize ) ) { rules = loadYaraRules( rulesBuffer, rulesBufferSize ); } if( NULL != rules ) { if( NULL != fileW ) { fileA = rpal_string_wtoa( fileW ); } if( NULL != procW ) { procA = rpal_string_wtoa( procW ); } if( NULL != fileA ) { rpal_debug_info( "scanning file with yara" ); matchContext.fileInfo = event; // Scan this file if( ERROR_SUCCESS != ( scanError = yr_rules_scan_file( rules, fileA, SCAN_FLAGS_FAST_MODE, _yaraFileMatchCallback, &matchContext, 60 ) ) ) { rpal_debug_warning( "Yara file scan error: %d", scanError ); } } else if( NULL != procA ) { // Scan processes matching if( NULL != ( processes = processLib_getProcessEntries( TRUE ) ) ) { curProc = processes; while( 0 != curProc->pid ) { if( NULL != ( processInfo = processLib_getProcessInfo( curProc->pid, NULL ) ) ) { if( rSequence_getSTRINGW( processInfo, RP_TAGS_FILE_PATH, &tmpW ) || rSequence_getSTRINGA( processInfo, RP_TAGS_FILE_PATH, &tmpA ) ) { if( NULL != tmpW ) { tmpA = rpal_string_wtoa( tmpW ); } if( NULL != tmpA ) { if( rpal_string_match( procA, tmpA, RPAL_PLATFORM_FS_CASE_SENSITIVITY ) ) { matchContext.pid = curProc->pid; matchContext.processInfo = processInfo; scanError = _scanProcessWith( curProc->pid, &matchContext, rules, NULL ); } } if( NULL != tmpW && NULL != tmpA ) { // If both are allocated it means we got a strW and converted to A // so we must free the strA version. rpal_memory_free( tmpA ); } } rSequence_free( processInfo ); } curProc++; } rpal_memory_free( processes ); } } else if( 0 != pid ) { // Scan this process matchContext.pid = pid; scanError = _scanProcessWith( pid, &matchContext, rules, NULL ); rSequence_free( matchContext.processInfo ); } else { // Scan all processes if( NULL != ( processes = processLib_getProcessEntries( TRUE ) ) ) { curProc = processes; while( 0 != curProc->pid ) { matchContext.pid = curProc->pid; scanError = _scanProcessWith( curProc->pid, &matchContext, rules, NULL ); rSequence_free( matchContext.processInfo ); curProc++; } rpal_memory_free( processes ); } } if( NULL != fileW && NULL != fileA ) { // If both are allocated it means we got a strW and converted to A // so we must free the strA version. rpal_memory_free( fileA ); } if( NULL != procW && NULL != procA ) { // If both are allocated it means we got a strW and converted to A // so we must free the strA version. rpal_memory_free( procA ); } yr_rules_destroy( rules ); } else { rpal_debug_warning( "no rules in yara scan request" ); reportError( event, RPAL_ERROR_NOT_SUPPORTED, "yara rules do not parse" ); } } rpal_debug_info( "finished on demand yara scan" ); reportError( event, scanError, "done" ); yr_finalize_thread(); }
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 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; }
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 RVOID scan_for_hollowing ( rpcm_tag eventType, rSequence event ) { RU32 pid = (RU32)( -1 ); rEvent dummy = NULL; rList hollowedModules = NULL; rSequence process = NULL; LibOsPerformanceProfile perfProfile = { 0 }; Atom parentAtom = { 0 }; UNREFERENCED_PARAMETER( eventType ); if( NULL != ( dummy = rEvent_create( TRUE ) ) ) { perfProfile.targetCpuPerformance = 10; perfProfile.globalTargetCpuPerformance = GLOBAL_CPU_USAGE_TARGET_WHEN_TASKED; perfProfile.timeoutIncrementPerSec = _PROFILE_INCREMENT; perfProfile.enforceOnceIn = 7; perfProfile.lastTimeoutValue = _INITIAL_PROFILED_TIMEOUT; perfProfile.sanityCeiling = _SANITY_CEILING; if( rSequence_getRU32( event, RP_TAGS_PROCESS_ID, &pid ) ) { if( NULL != ( process = processLib_getProcessInfo( pid, NULL ) ) || ( NULL != ( process = rSequence_new() ) && rSequence_addRU32( process, RP_TAGS_PROCESS_ID, pid ) ) ) { if( NULL != ( hollowedModules = _spotCheckProcess( dummy, pid, &perfProfile ) ) ) { if( !rSequence_addLIST( process, RP_TAGS_MODULES, hollowedModules ) ) { rList_free( hollowedModules ); } else { parentAtom.key.category = RP_TAGS_NOTIFICATION_NEW_PROCESS; parentAtom.key.process.pid = pid; if( atoms_query( &parentAtom, 0 ) ) { HbsSetParentAtom( process, parentAtom.id ); } hbs_publish( RP_TAGS_NOTIFICATION_MODULE_MEM_DISK_MISMATCH, process ); } } } if( rpal_memory_isValid( process ) ) { rSequence_free( process ); } } rEvent_free( dummy ); } }
static rList beaconHome ( ) { rList response = NULL; rList exfil = NULL; rSequence msg = NULL; RU32 nExfilMsg = 0; rSequence tmpMessage = NULL; if( NULL != ( exfil = rList_new( RP_TAGS_MESSAGE, RPCM_SEQUENCE ) ) ) { if( NULL != ( msg = rSequence_new() ) ) { if( rSequence_addBUFFER( msg, RP_TAGS_HASH, g_hbs_state.currentConfigHash, CRYPTOLIB_HASH_SIZE ) ) { if( !rList_addSEQUENCE( exfil, msg ) ) { rSequence_free( msg ); msg = NULL; } tmpMessage = msg; } else { rSequence_free( msg ); msg = NULL; } } while( rQueue_remove( g_hbs_state.outQueue, &msg, NULL, 0 ) ) { nExfilMsg++; if( !rList_addSEQUENCE( exfil, msg ) ) { rSequence_free( msg ); } } rpal_debug_info( "%d messages ready for exfil.", nExfilMsg ); if( rpHcpI_beaconHome( exfil, &response ) ) { rpal_debug_info( "%d messages received from cloud.", rList_getNumElements( response ) ); } else if( g_hbs_state.maxQueueSize > rList_getEstimateSize( exfil ) ) { rpal_debug_info( "beacon failed, re-adding %d messages.", rList_getNumElements( exfil ) ); // We will attempt to re-add the existing messages back in the queue since this failed rList_resetIterator( exfil ); while( rList_getSEQUENCE( exfil, RP_TAGS_MESSAGE, &msg ) ) { // Ignore message we generate temporarily for the beacon if( tmpMessage == msg ) { rSequence_free( msg ); continue; } if( !rQueue_add( g_hbs_state.outQueue, msg, 0 ) ) { rSequence_free( msg ); } } rList_shallowFree( exfil ); exfil = NULL; } else { rpal_debug_warning( "beacon failed but discarded exfil because of its size." ); } if( NULL != exfil ) { rList_free( exfil ); } } return response; }
static RVOID processFile ( rSequence notif ) { RPCHAR fileA = NULL; RPWCHAR fileW = NULL; RPU8 fileContent = NULL; RU32 fileSize = 0; CryptoLib_Hash hash = { 0 }; if( NULL != notif ) { obsLib_resetSearchState( matcherA ); obsLib_resetSearchState( matcherW ); if( ( rSequence_getSTRINGA( notif, RP_TAGS_FILE_PATH, &fileA ) && obsLib_setTargetBuffer( matcherA, fileA, ( rpal_string_strlen( fileA ) + 1 ) * sizeof( RCHAR ) ) && obsLib_nextHit( matcherA, NULL, NULL ) ) || ( rSequence_getSTRINGW( notif, RP_TAGS_FILE_PATH, &fileW ) && obsLib_setTargetBuffer( matcherW, fileW, ( rpal_string_strlenw( fileW ) + 1 ) * sizeof( RWCHAR ) ) && obsLib_nextHit( matcherW, NULL, NULL ) ) ) { // This means it's a file of interest. if( ( NULL != fileA && ( ( DOCUMENT_MAX_SIZE >= rpal_file_getSize( fileA, TRUE ) && rpal_file_read( fileA, (RPVOID*)&fileContent, &fileSize, TRUE ) && CryptoLib_hash( fileContent, fileSize, &hash ) ) || CryptoLib_hashFileA( fileA, &hash, TRUE ) ) ) || ( NULL != fileW && ( ( DOCUMENT_MAX_SIZE >= rpal_file_getSizew( fileW, TRUE ) && rpal_file_readw( fileW, (RPVOID*)&fileContent, &fileSize, TRUE ) && CryptoLib_hash( fileContent, fileSize, &hash ) ) || CryptoLib_hashFileW( fileW, &hash, TRUE ) ) ) ) { // We acquired the hash, either by reading the entire file in memory // which we will use for caching, or if it was too big by hashing it // sequentially on disk. rSequence_unTaintRead( notif ); rSequence_addBUFFER( notif, RP_TAGS_HASH, (RPU8)&hash, sizeof( hash ) ); notifications_publish( RP_TAGS_NOTIFICATION_NEW_DOCUMENT, notif ); } if( rMutex_lock( cacheMutex ) ) { if( NULL == fileContent || !rSequence_addBUFFER( notif, RP_TAGS_FILE_CONTENT, fileContent, fileSize ) || !HbsRingBuffer_add( documentCache, notif ) ) { rSequence_free( notif ); } rMutex_unlock( cacheMutex ); } else { rSequence_free( notif ); } if( NULL != fileContent ) { rpal_memory_free( fileContent ); } } else { rSequence_free( notif ); } } }
static RVOID getDocument ( rpcm_tag notifId, rSequence notif ) { rSequence tmp = NULL; DocSearchContext ctx = { 0 }; RU32 hashSize = 0; rList foundDocs = NULL; RBOOL isAAlloced = FALSE; RBOOL isWAlloced = FALSE; UNREFERENCED_PARAMETER( notifId ); if( NULL != notif ) { if( !rSequence_getSTRINGA( notif, RP_TAGS_STRING_PATTERN, &ctx.exprA ) ) { ctx.exprA = NULL; } if( !rSequence_getSTRINGW( notif, RP_TAGS_STRING_PATTERN, &ctx.exprW ) ) { ctx.exprW = NULL; } if( NULL != ctx.exprA && NULL == ctx.exprW ) { ctx.exprW = rpal_string_atow( ctx.exprA ); isWAlloced = TRUE; } if( NULL != ctx.exprW && NULL == ctx.exprA ) { ctx.exprA = rpal_string_wtoa( ctx.exprW ); isAAlloced = TRUE; } if( !rSequence_getBUFFER( notif, RP_TAGS_HASH, (RPU8*)&ctx.pHash, &hashSize ) || sizeof( *ctx.pHash ) != hashSize ) { // Unexpected hash size, let's not gamble ctx.pHash = NULL; } } if( rMutex_lock( cacheMutex ) ) { if( NULL != ( foundDocs = rList_new( RP_TAGS_FILE_INFO, RPCM_SEQUENCE ) ) ) { while( HbsRingBuffer_find( documentCache, (HbsRingBufferCompareFunc)findDoc, &ctx, &tmp ) ) { // TODO: optimize this since if we're dealing with large files // we will be temporarily using large amounts of duplicate memory. // We just need to do some shallow free of the datastructures // somehow. if( NULL != ( tmp = rSequence_duplicate( tmp ) ) ) { if( !rList_addSEQUENCE( foundDocs, tmp ) ) { rSequence_free( tmp ); } } } if( !rSequence_addLIST( notif, RP_TAGS_FILES, foundDocs ) ) { rList_free( foundDocs ); } } rMutex_unlock( cacheMutex ); notifications_publish( RP_TAGS_NOTIFICATION_GET_DOCUMENT_REP, notif ); } if( isAAlloced ) { rpal_memory_free( ctx.exprA ); } if( isWAlloced ) { rpal_memory_free( ctx.exprW ); } }
static RVOID processFileIo ( rpcm_tag notifType, rSequence event ) { ProcExtInfo* ctx = NULL; RPNCHAR path = NULL; RPVOID patternCtx = 0; RU8 patternId = 0; RPU8 atomId = NULL; RU32 pid = 0; rSequence newEvent = NULL; UNREFERENCED_PARAMETER( notifType ); if( rSequence_getSTRINGN( event, RP_TAGS_FILE_PATH, &path ) && HbsGetParentAtom( event, &atomId ) && rSequence_getRU32( event, RP_TAGS_PROCESS_ID, &pid ) ) { if( rMutex_lock( g_mutex ) ) { obsLib_resetSearchState( g_extensions ); if( obsLib_setTargetBuffer( g_extensions, path, rpal_string_strsize( path ) ) ) { while( obsLib_nextHit( g_extensions, &patternCtx, NULL ) ) { if( NULL != ctx || NULL != ( ctx = getProcContext( atomId ) ) ) { patternId = (RU8)PTR_TO_NUMBER( patternCtx ); if( !IS_FLAG_ENABLED( ctx->extBitMask, (RU64)1 << patternId ) ) { rpal_debug_info( "process " RF_U32 " observed file io " RF_U64, pid, patternId + 1 ); ENABLE_FLAG( ctx->extBitMask, (RU64)1 << patternId ); if( NULL != ( newEvent = rSequence_new() ) ) { HbsSetParentAtom( newEvent, atomId ); rSequence_addRU32( newEvent, RP_TAGS_PROCESS_ID, pid ); rSequence_addRU8( newEvent, RP_TAGS_RULE_NAME, patternId + 1 ); rSequence_addSTRINGN( newEvent, RP_TAGS_FILE_PATH, ctx->processPath ); hbs_publish( RP_TAGS_NOTIFICATION_FILE_TYPE_ACCESSED, newEvent ); rSequence_free( newEvent ); } } } else { rpal_debug_error( "error getting process context" ); break; } } } rMutex_unlock( g_mutex ); } } }
//============================================================================= // 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 RVOID publishCloudNotifications ( rList notifications ) { rSequence notif = NULL; RPU8 buff = NULL; RU32 buffSize = 0; RPU8 sig = NULL; RU32 sigSize = 0; rpHCPId curId = { 0 }; rSequence cloudEvent = NULL; rSequence targetId = { 0 }; RU32 eventId = 0; rSequence localEvent = NULL; RU64 expiry = 0; rpHCPId tmpId = { 0 }; rSequence receipt = NULL; while( rList_getSEQUENCE( notifications, RP_TAGS_HBS_CLOUD_NOTIFICATION, ¬if ) ) { cloudEvent = NULL; if( rSequence_getBUFFER( notif, RP_TAGS_BINARY, &buff, &buffSize ) && rSequence_getBUFFER( notif, RP_TAGS_SIGNATURE, &sig, &sigSize ) ) { if( CryptoLib_verify( buff, buffSize, hbs_cloud_pub_key, sig ) ) { if( !rpHcpI_getId( &curId ) ) { rpal_debug_error( "error getting current id for cloud notifications." ); } else { if( !rSequence_deserialise( &cloudEvent, buff, buffSize, NULL ) ) { cloudEvent = NULL; rpal_debug_warning( "error deserializing cloud event." ); } } } else { rpal_debug_warning( "cloud event signature invalid." ); } } if( rpal_memory_isValid( cloudEvent ) ) { if( rSequence_getSEQUENCE( cloudEvent, RP_TAGS_HCP_ID, &targetId ) && rSequence_getRU32( cloudEvent, RP_TAGS_HBS_NOTIFICATION_ID, &eventId ) && rSequence_getSEQUENCE( cloudEvent, RP_TAGS_HBS_NOTIFICATION, &localEvent ) ) { rSequence_getTIMESTAMP( cloudEvent, RP_TAGS_EXPIRY, &expiry ); tmpId = rpHcpI_seqToHcpId( targetId ); curId.id.configId = 0; tmpId.id.configId = 0; if( NULL != ( receipt = rSequence_new() ) ) { if( rSequence_addSEQUENCE( receipt, RP_TAGS_HBS_CLOUD_NOTIFICATION, rSequence_duplicate( cloudEvent ) ) ) { if( !rQueue_add( g_hbs_state.outQueue, receipt, 0 ) ) { rSequence_free( receipt ); receipt = NULL; } } else { rSequence_free( receipt ); receipt = NULL; } } if( curId.raw == tmpId.raw && rpal_time_getGlobal() <= expiry ) { if( !notifications_publish( eventId, localEvent ) ) { rpal_debug_error( "error publishing event from cloud." ); } } else { rpal_debug_warning( "event expired or for wrong id." ); } } if( rpal_memory_isValid( cloudEvent ) ) { rSequence_free( cloudEvent ); cloudEvent = NULL; } } } }