static RBOOL isMemInModule ( RU64 memBase, RU64 memSize, _MemRange* memRanges, RU32 nRanges ) { RBOOL isInMod = FALSE; RU32 i = 0; if( NULL != memRanges ) { i = rpal_binsearch_array_closest( memRanges, nRanges, sizeof( _MemRange ), &memBase, (rpal_ordering_func)rpal_order_RU64, TRUE ); if( (RU32)(-1) != i ) { if( IS_WITHIN_BOUNDS( NUMBER_TO_PTR( memBase ), memSize, NUMBER_TO_PTR( memRanges[ i ].base ), memRanges[ i ].size ) ) { isInMod = TRUE; } } } return isInMod; }
static RBOOL isMemInModule ( RU64 memBase, RU64 memSize, rList modules ) { RBOOL isInMod = FALSE; rSequence mod = NULL; RU64 modBase = 0; RU64 modSize = 0; if( NULL != modules ) { rList_resetIterator( modules ); while( rList_getSEQUENCE( modules, RP_TAGS_DLL, &mod ) ) { if( rSequence_getPOINTER64( mod, RP_TAGS_BASE_ADDRESS, &modBase ) && rSequence_getRU64( mod, RP_TAGS_MEMORY_SIZE, &modSize ) ) { if( IS_WITHIN_BOUNDS( NUMBER_TO_PTR( memBase ), memSize, NUMBER_TO_PTR( modBase ), modSize ) ) { isInMod = TRUE; break; } } } rList_resetIterator( modules ); } return isInMod; }
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; }
RBOOL collector_22_init ( HbsState* hbsState, rSequence config ) { RBOOL isSuccess = FALSE; rList patterns = NULL; rSequence pattern = NULL; RPCHAR strA = NULL; RPWCHAR strW = NULL; RPNCHAR tmpN = NULL; RU8 patternId = 0; RU32 i = 0; if( NULL != hbsState && NULL != ( g_extensions = obsLib_new( 0, 0 ) ) ) { if( rSequence_getLIST( config, RP_TAGS_PATTERNS, &patterns ) ) { while( rList_getSEQUENCE( patterns, RP_TAGS_RULE, &pattern ) ) { if( rSequence_getRU8( pattern, RP_TAGS_RULE_NAME, &patternId ) ) { if( 64 < patternId || 0 == patternId ) { rpal_debug_critical( "rule id must be below 64 and 1-based." ); continue; } // Base the pattern id to 0 patternId--; if( rSequence_getSTRINGA( pattern, RP_TAGS_EXTENSION, &strA ) && NULL != ( tmpN = rpal_string_aton( strA ) ) ) { _addPattern( g_extensions, tmpN, TRUE, NUMBER_TO_PTR( patternId ) ); rpal_memory_free( tmpN ); } if( rSequence_getSTRINGW( pattern, RP_TAGS_EXTENSION, &strW ) && NULL != ( tmpN = rpal_string_wton( strW ) ) ) { _addPattern( g_extensions, tmpN, TRUE, NUMBER_TO_PTR( patternId ) ); rpal_memory_free( tmpN ); } if( rSequence_getSTRINGA( pattern, RP_TAGS_STRING_PATTERN, &strA ) && NULL != ( tmpN = rpal_string_aton( strA ) ) ) { _addPattern( g_extensions, tmpN, FALSE, NUMBER_TO_PTR( patternId ) ); rpal_memory_free( tmpN ); } if( rSequence_getSTRINGW( pattern, RP_TAGS_STRING_PATTERN, &strW ) && NULL != ( tmpN = rpal_string_wton( strW ) ) ) { _addPattern( g_extensions, tmpN, FALSE, NUMBER_TO_PTR( patternId ) ); rpal_memory_free( tmpN ); } } } if( NULL != ( g_mutex = rMutex_create() ) && NULL != ( g_procContexts = rpal_vector_new() ) && notifications_subscribe( RP_TAGS_NOTIFICATION_FILE_CREATE, NULL, 0, NULL, processFileIo ) && notifications_subscribe( RP_TAGS_NOTIFICATION_FILE_DELETE, NULL, 0, NULL, processFileIo ) && notifications_subscribe( RP_TAGS_NOTIFICATION_FILE_MODIFIED, NULL, 0, NULL, processFileIo ) && notifications_subscribe( RP_TAGS_NOTIFICATION_FILE_READ, NULL, 0, NULL, processFileIo ) && notifications_subscribe( RP_TAGS_NOTIFICATION_NEW_PROCESS, NULL, 0, NULL, processNewProcesses ) && notifications_subscribe( RP_TAGS_NOTIFICATION_EXISTING_PROCESS, NULL, 0, NULL, processNewProcesses ) && notifications_subscribe( RP_TAGS_NOTIFICATION_TERMINATE_PROCESS, NULL, 0, NULL, processTerminateProcesses ) ) { isSuccess = TRUE; } } } if( !isSuccess ) { notifications_unsubscribe( RP_TAGS_NOTIFICATION_FILE_CREATE, NULL, processFileIo ); notifications_unsubscribe( RP_TAGS_NOTIFICATION_FILE_DELETE, NULL, processFileIo ); notifications_unsubscribe( RP_TAGS_NOTIFICATION_FILE_MODIFIED, NULL, processFileIo ); notifications_unsubscribe( RP_TAGS_NOTIFICATION_FILE_READ, NULL, processFileIo ); notifications_unsubscribe( RP_TAGS_NOTIFICATION_NEW_PROCESS, NULL, processNewProcesses ); notifications_unsubscribe( RP_TAGS_NOTIFICATION_EXISTING_PROCESS, NULL, processNewProcesses ); notifications_unsubscribe( RP_TAGS_NOTIFICATION_TERMINATE_PROCESS, NULL, processTerminateProcesses ); obsLib_free( g_extensions ); g_extensions = NULL; if( NULL != g_procContexts ) { for( i = 0; i < g_procContexts->nElements; i++ ) { rpal_memory_free( ( (ProcExtInfo*)g_procContexts->elements[ i ] )->processPath ); rpal_memory_free( g_procContexts->elements[ i ] ); } } rpal_vector_free( g_procContexts ); g_procContexts = NULL; rMutex_free( g_mutex ); g_mutex = NULL; } return isSuccess; }
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 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; }
static RPVOID lookForHiddenModulesIn ( rEvent isTimeToStop, RU32 processId ) { rList mods = NULL; 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 if( NULL != ( mods = processLib_getProcessModules( processId ) ) ) { 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, ®ion ) ) ) { 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; } if( !isMemInModule( memBase, memSize, mods ) ) { // 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 ) ) { 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, ®ion ) ) { 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 ) ) ) { if( !rSequence_addSEQUENCE( region, RP_TAGS_PROCESS, procInfo ) ) { rSequence_free( procInfo ); } } rSequence_addTIMESTAMP( region, RP_TAGS_TIMESTAMP, rpal_time_getGlobal() ); notifications_publish( RP_TAGS_NOTIFICATION_HIDDEN_MODULE_DETECTED, region ); break; } rpal_thread_sleep( 10 ); } } } } } rList_free( map ); } rList_free( mods ); } return NULL; }