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; }
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 RBOOL notifyOfProcess ( RU32 pid, RU32 ppid, RBOOL isStarting, RNATIVESTR optFilePath, RNATIVESTR optCmdLine, RU32 optUserId, RU64 optTs ) { RBOOL isSuccess = FALSE; rSequence info = NULL; rSequence parentInfo = NULL; RU32 tmpUid = 0; RNATIVESTR cleanPath = NULL; // We prime the information with whatever was provided // to us by the kernel acquisition. If not available // we generate using the UM only way. if( 0 != rpal_string_strlenn( optFilePath ) && ( NULL != info || NULL != ( info = rSequence_new() ) ) ) { cleanPath = rpal_file_cleann( optFilePath ); rSequence_addSTRINGN( info, RP_TAGS_FILE_PATH, cleanPath ? cleanPath : optFilePath ); rpal_memory_free( cleanPath ); } if( 0 != rpal_string_strlenn( optCmdLine ) && ( NULL != info || NULL != ( info = rSequence_new() ) ) ) { rSequence_addSTRINGN( info, RP_TAGS_COMMAND_LINE, optCmdLine ); } if( NULL != info ) { info = processLib_getProcessInfo( pid, info ); } else if( !isStarting || NULL == ( info = processLib_getProcessInfo( pid, info ) ) ) { 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 ); if( 0 != optTs ) { rSequence_addTIMESTAMP( info, RP_TAGS_TIMESTAMP, rpal_time_getGlobalFromLocal( optTs ) ); } else { rSequence_addTIMESTAMP( info, RP_TAGS_TIMESTAMP, rpal_time_getGlobal() ); } if( isStarting ) { if( NULL != ( parentInfo = processLib_getProcessInfo( ppid, NULL ) ) && !rSequence_addSEQUENCE( info, RP_TAGS_PARENT, parentInfo ) ) { rSequence_free( parentInfo ); } } if( isStarting ) { if( KERNEL_ACQ_NO_USER_ID != optUserId && !rSequence_getRU32( info, RP_TAGS_USER_ID, &tmpUid ) ) { rSequence_addRU32( info, RP_TAGS_USER_ID, optUserId ); } if( notifications_publish( RP_TAGS_NOTIFICATION_NEW_PROCESS, info ) ) { isSuccess = TRUE; rpal_debug_info( "new process starting: %d / %d", pid, ppid ); } } else { if( notifications_publish( RP_TAGS_NOTIFICATION_TERMINATE_PROCESS, info ) ) { isSuccess = TRUE; rpal_debug_info( "new process terminating: %d / %d", pid, ppid ); } } rSequence_free( info ); } else { rpal_debug_error( "could not allocate info on new process" ); } return isSuccess; }