diff --git a/BUILD.md b/BUILD.md index 52960aa..3ad3cd4 100644 --- a/BUILD.md +++ b/BUILD.md @@ -19,7 +19,7 @@ If you installed SysinternalsEBPF via make install, you may need to add /usr/loc ``` sudo apt update dotnet tool install --global dotnet-t4 --version 2.3.1 -sudo apt -y install build-essential gcc g++ make cmake libelf-dev llvm clang libxml2 libxml2-dev libzstd1 git libgtest-dev apt-transport-https dirmngr googletest google-mock libgmock-dev libjson-glib-dev libc6-dev-i386 +sudo apt -y install build-essential gcc g++ make cmake libelf-dev llvm clang libxml2 libxml2-dev libzstd1 git libgtest-dev apt-transport-https dirmngr googletest google-mock libgmock-dev libjson-glib-dev libc6-dev-i386 libssl-dev ``` ### Rocky 9 @@ -30,7 +30,7 @@ sudo dnf install epel-release sudo dnf update dotnet tool install --global dotnet-t4 --version 2.3.1 -sudo yum install gcc gcc-c++ make cmake llvm clang elfutils-libelf-devel rpm-build json-glib-devel python3 libxml2-devel gtest-devel gmock gmock-devel glibc-devel.i686 +sudo yum install gcc gcc-c++ make cmake llvm clang elfutils-libelf-devel rpm-build json-glib-devel python3 libxml2-devel gtest-devel gmock gmock-devel glibc-devel.i686 openssl-devel ``` ### Rocky 8 @@ -41,7 +41,7 @@ sudo dnf config-manager --set-enabled powertools sudo dnf update dotnet tool install --global dotnet-t4 --version 2.3.1 -sudo yum install gcc gcc-c++ make cmake llvm clang elfutils-libelf-devel rpm-build json-glib-devel python3 libxml2-devel gtest-devel gmock gmock-devel glibc-devel.i686 +sudo yum install gcc gcc-c++ make cmake llvm clang elfutils-libelf-devel rpm-build json-glib-devel python3 libxml2-devel gtest-devel gmock gmock-devel glibc-devel.i686 openssl-devel ``` ### Debian 11 @@ -51,7 +51,7 @@ sudo dpkg -i packages-microsoft-prod.deb rm packages-microsoft-prod.deb sudo apt update dotnet tool install --global dotnet-t4 --version 2.3.1 -sudo apt -y install build-essential gcc g++ make cmake libelf-dev llvm clang libzstd1 git libjson-glib-dev libxml2 libxml2-dev googletest google-mock libgmock-dev libc6-dev-i386 +sudo apt -y install build-essential gcc g++ make cmake libelf-dev llvm clang libzstd1 git libjson-glib-dev libxml2 libxml2-dev googletest google-mock libgmock-dev libc6-dev-i386 libssl-dev ``` ## Build @@ -102,4 +102,4 @@ make packages The directories build/deb and build/rpm will be populated with the required files. If dpkg-deb is available, the build/deb directory will be used to create a deb package. Similarly if rpmbuild is available, the build/rpm directory will -be used to create an rpm package. \ No newline at end of file +be used to create an rpm package. diff --git a/CMakeLists.txt b/CMakeLists.txt index 399d11b..ef33a63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,6 +93,11 @@ set(CMAKE_CXX_FLAGS "-std=gnu++17") # find_package(LibXml2 2.0.0 REQUIRED) +# +# rely on openssl having been installed +# +find_package(OpenSSL REQUIRED) + # # source directories # @@ -329,7 +334,7 @@ set(EBPF_DEPENDS # # link sysmon # -target_link_libraries(sysmon sysinternalsEBPF "${LIBXML2_LIBRARIES}" pthread) +target_link_libraries(sysmon sysinternalsEBPF "${LIBXML2_LIBRARIES}" pthread OpenSSL::Crypto) # @@ -398,7 +403,7 @@ add_executable(sysmonUnitTests "${PROJECT_BINARY_DIR}/yoursleep" ) -target_link_libraries(sysmonUnitTests "${LIBXML2_LIBRARIES}" ${GTEST_LIBRARIES} pthread) +target_link_libraries(sysmonUnitTests "${LIBXML2_LIBRARIES}" ${GTEST_LIBRARIES} pthread OpenSSL::Crypto) target_include_directories(sysmonUnitTests PUBLIC "${CMAKE_SOURCE_DIR}" "${SYSMON_COMMON_SOURCE_DIR}" @@ -616,4 +621,4 @@ foreach(EBPF_PROG IN LISTS EBPF_PROGS) set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${EBPF_PROG}${EBPF_CORE_PROG_SUFFIX}.o) endif() -endforeach(EBPF_PROG) \ No newline at end of file +endforeach(EBPF_PROG) diff --git a/linuxHelpers.cpp b/linuxHelpers.cpp index 5634a22..1cb02f7 100644 --- a/linuxHelpers.cpp +++ b/linuxHelpers.cpp @@ -33,6 +33,7 @@ #include #include #include +#include //-------------------------------------------------------------------- // @@ -874,6 +875,121 @@ VOID LargeIntegerToSystemTimeString( } } +//-------------------------------------------------------------------- +// +// LinuxGetFileHash +// +// Calculates image hash. +// +//-------------------------------------------------------------------- +void LinuxGetFileHash(uint32_t hashType, PTCHAR imagePath, char *stringBuffer, size_t stringBufferSize) +{ + unsigned char tmpReadBuffer[4096] = {}; + unsigned char tmpHashBuffer[ALGO_MAX][256] = {}; + unsigned char tmpHashPrefixBuffer[16] = {}; + unsigned char tmpStringBuffer[256] = {}; + char hashSeparator[] = ","; + char hashPrefix[3][8] = {"SHA1=", "MD5=", "SHA256="}; + unsigned int hashSize[ALGO_MAX] = {}; + unsigned int hashFlag[ALGO_MAX] = {}; + size_t n; + FILE *filePtr; + EVP_MD_CTX *sha1_ctx, *md5_ctx, *sha256_ctx; + + // Allocate digests context + sha1_ctx = EVP_MD_CTX_new(); + md5_ctx = EVP_MD_CTX_new(); + sha256_ctx = EVP_MD_CTX_new(); + if ( !sha1_ctx || !md5_ctx || !sha256_ctx ) return; + + EVP_DigestInit(sha1_ctx , EVP_sha1()); + EVP_DigestInit(md5_ctx , EVP_md5()); + EVP_DigestInit(sha256_ctx, EVP_sha256()); + + hashFlag[ALGO_SHA1] = (((hashType>>(ALGO_SHA1-1))&1) && (hashType & ALGO_MULTIPLE)) || (hashType == ALGO_SHA1); + hashFlag[ALGO_MD5] = (((hashType>>(ALGO_MD5-1))&1) && (hashType & ALGO_MULTIPLE)) || (hashType == ALGO_MD5); + hashFlag[ALGO_SHA256] = (((hashType>>(ALGO_SHA256-1))&1) && (hashType & ALGO_MULTIPLE)) || (hashType == ALGO_SHA256); + + filePtr = fopen(imagePath, "rb"); + if( !filePtr ) return; + + // Read and hash image + while((n = fread(tmpReadBuffer, 1, sizeof(tmpReadBuffer), filePtr))){ + + if( hashFlag[ALGO_SHA1] ){ + if (!EVP_DigestUpdate(sha1_ctx, tmpReadBuffer, n)){ + fclose(filePtr); + return; + } + if ( !(hashType & ALGO_MULTIPLE) ) continue; + } + + if( hashFlag[ALGO_MD5] ){ + if (!EVP_DigestUpdate(md5_ctx, tmpReadBuffer, n)){ + fclose(filePtr); + return; + } + if ( !(hashType & ALGO_MULTIPLE) ) continue; + } + + if( hashFlag[ALGO_SHA256] ){ + if (!EVP_DigestUpdate(sha256_ctx, tmpReadBuffer, n)){ + fclose(filePtr); + return; + } + if ( !(hashType & ALGO_MULTIPLE) ) continue; + } + + } + fclose(filePtr); + + // Retrieve digest and cleanup + EVP_DigestFinal(sha1_ctx , tmpHashBuffer[ALGO_SHA1] , &hashSize[ALGO_SHA1]); + EVP_DigestFinal(md5_ctx , tmpHashBuffer[ALGO_MD5] , &hashSize[ALGO_MD5]); + EVP_DigestFinal(sha256_ctx, tmpHashBuffer[ALGO_SHA256], &hashSize[ALGO_SHA256]); + + memset(tmpStringBuffer, 0, sizeof(tmpStringBuffer)); + memset(tmpHashPrefixBuffer, 0, sizeof(tmpHashPrefixBuffer)); + + for(unsigned int algo=0;algo strnlen((char *)tmpHashPrefixBuffer, sizeof(tmpHashPrefixBuffer)) + strnlen((char *)tmpStringBuffer, sizeof(tmpStringBuffer))){ + strcat(stringBuffer, (char *)tmpHashPrefixBuffer); + strcat(stringBuffer, (char *)tmpStringBuffer); + } + + if( !(hashType & ALGO_MULTIPLE) ) return; + } + } +} + //-------------------------------------------------------------------- // // GetTid diff --git a/linuxHelpers.h b/linuxHelpers.h index 42ae153..c6facdf 100644 --- a/linuxHelpers.h +++ b/linuxHelpers.h @@ -54,6 +54,7 @@ unsigned int LargeTimeMilliseconds( CONST PLARGE_INTEGER timestamp ); unsigned int LargeTimeNanoseconds( CONST PLARGE_INTEGER timestamp ); VOID LinuxFileTimeToLargeInteger( PLARGE_INTEGER timestamp, const my_statx_timestamp *filetime ); VOID LargeIntegerToSystemTimeString( char *s, size_t sLen, CONST PLARGE_INTEGER timestamp ); +void LinuxGetFileHash(uint32_t hashType, PTCHAR imagePath, char *stringBuffer, size_t stringBufferSize); pid_t GetTid(); #ifdef __cplusplus diff --git a/package/DEBIAN.in/control.in b/package/DEBIAN.in/control.in index a6b7867..5aefe3d 100644 --- a/package/DEBIAN.in/control.in +++ b/package/DEBIAN.in/control.in @@ -3,6 +3,6 @@ Version: @PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@ Architecture: amd64 Maintainer: Sysinternals Description: A system monitor based on eBPF, ported from Windows, that outputs events to Syslog -Depends: libc6 (>= 2.14), libgcc1 (>= 1:3.0), libstdc++6 (>= 5), libxml2 (>= 2.7.4), sysinternalsebpf (>= 1.2.0) +Depends: libc6 (>= 2.14), libgcc1 (>= 1:3.0), libstdc++6 (>= 5), libxml2 (>= 2.7.4), libssl-dev, sysinternalsebpf (>= 1.2.0) Installed-Size: 58934 diff --git a/sysmonCommon b/sysmonCommon index c6f3f0d..c1a02f4 160000 --- a/sysmonCommon +++ b/sysmonCommon @@ -1 +1 @@ -Subproject commit c6f3f0d15f4dadb1813b768bcd26d8ad271e969c +Subproject commit c1a02f4c73c81b591272ce927d8cf83f6edadf1b diff --git a/sysmonforlinux.c b/sysmonforlinux.c index 3378e5b..342a26f 100644 --- a/sysmonforlinux.c +++ b/sysmonforlinux.c @@ -543,6 +543,141 @@ void processProcessAccess(CONST PSYSMON_EVENT_HEADER eventHdr) DispatchEvent(newEventHdr); } +//-------------------------------------------------------------------- +// +// processProcessCreate +// +// Handles process create events +// +//-------------------------------------------------------------------- +void processProcessCreate(CONST PSYSMON_EVENT_HEADER eventHdr) +{ + if(eventHdr == NULL) { + fprintf(stderr, "processProcessCreate invalid params\n"); + return; + } + + char newData[65536]; + + PSYSMON_EVENT_HEADER newEventHdr = (PSYSMON_EVENT_HEADER)newData; + newEventHdr->m_FieldFiltered = 0; + newEventHdr->m_PreFiltered = 0; + newEventHdr->m_SequenceNumber = 0; + newEventHdr->m_SessionId = 0; + + PSYSMON_PROCESS_CREATE event = (PSYSMON_PROCESS_CREATE)&(eventHdr->m_EventBody); + + newEventHdr->m_EventType = ProcessCreate; + PSYSMON_PROCESS_CREATE newEvent = &newEventHdr->m_EventBody.m_ProcessCreateEvent; + newEvent->m_ProcessId = event->m_ProcessId; + newEvent->m_ProcessObject = event->m_ProcessObject; + newEvent->m_ParentProcessObject = event->m_ParentProcessObject; + newEvent->m_ParentProcessId = event->m_ParentProcessId; + newEvent->m_AuditUserId = event->m_AuditUserId; + newEvent->m_SessionId = event->m_SessionId; + newEvent->m_AuthenticationId.LowPart = event->m_AuthenticationId.LowPart; + newEvent->m_AuthenticationId.HighPart = event->m_AuthenticationId.HighPart; + newEvent->m_ProcessKey = event->m_ProcessKey; + newEvent->m_CreateTime.QuadPart = event->m_CreateTime.QuadPart; + + if(OPT_SET( HashAlgorithms )){ + unsigned int *hashTypePtr = OPT_VALUE( HashAlgorithms ); + newEvent->m_HashType = *hashTypePtr; + } + else{ + newEvent->m_HashType = 0; + } + + memset(newEvent->m_Extensions, 0, sizeof(newEvent->m_Extensions)); + const char *ptr = (char *)(event + 1); + char *newPtr = (char *)(newEvent + 1); + + memcpy(newPtr, ptr, event->m_Extensions[PC_Sid]); + newEvent->m_Extensions[PC_Sid] = event->m_Extensions[PC_Sid]; + ptr += event->m_Extensions[PC_Sid]; + newPtr += event->m_Extensions[PC_Sid]; + + memcpy(newPtr, ptr, event->m_Extensions[PC_ImagePath]); + newEvent->m_Extensions[PC_ImagePath] = event->m_Extensions[PC_ImagePath]; + ptr += event->m_Extensions[PC_ImagePath]; + newPtr += event->m_Extensions[PC_ImagePath]; + + memcpy(newPtr, ptr, event->m_Extensions[PC_CommandLine]); + newEvent->m_Extensions[PC_CommandLine] = event->m_Extensions[PC_CommandLine]; + ptr += event->m_Extensions[PC_CommandLine]; + newPtr += event->m_Extensions[PC_CommandLine]; + + memcpy(newPtr, ptr, event->m_Extensions[PC_CurrentDirectory]); + newEvent->m_Extensions[PC_CurrentDirectory] = event->m_Extensions[PC_CurrentDirectory]; + newPtr += event->m_Extensions[PC_CurrentDirectory]; + + newEventHdr->m_EventSize = (uint32_t)((void *)newPtr - (void *)newEventHdr); + + DispatchEvent(newEventHdr); +} + +//-------------------------------------------------------------------- +// +// processFileDelete +// +// Handles file delete events +// +//-------------------------------------------------------------------- +void processFileDelete(CONST PSYSMON_EVENT_HEADER eventHdr) +{ + if(eventHdr == NULL) { + fprintf(stderr, "processFileDelete invalid params\n"); + return; + } + + char newData[65536]; + + PSYSMON_EVENT_HEADER newEventHdr = (PSYSMON_EVENT_HEADER)newData; + newEventHdr->m_FieldFiltered = 0; + newEventHdr->m_PreFiltered = 0; + newEventHdr->m_SequenceNumber = 0; + newEventHdr->m_SessionId = 0; + + PSYSMON_FILE_DELETE event = (PSYSMON_FILE_DELETE)&(eventHdr->m_EventBody); + + newEventHdr->m_EventType = FileDelete; + PSYSMON_FILE_DELETE newEvent = &newEventHdr->m_EventBody.m_FileDeleteEvent; + newEvent->m_ProcessId = event->m_ProcessId; + newEvent->m_DeleteTime.QuadPart = event->m_DeleteTime.QuadPart; + newEvent->m_IsExecutable = event->m_IsExecutable; + newEvent->m_Archived[0] = event->m_Archived[0]; + newEvent->m_TrackerId = event->m_TrackerId; + + newEvent->m_HashType = 0xFF; // 0xFF will not be interpreted as any ALGO_*. Therefore, Hashes field will always be blank. + + memset(newEvent->m_Extensions, 0, sizeof(newEvent->m_Extensions)); + const char *ptr = (char *)(event + 1); + char *newPtr = (char *)(newEvent + 1); + + memcpy(newPtr, ptr, event->m_Extensions[FD_Sid]); + newEvent->m_Extensions[FD_Sid] = event->m_Extensions[FD_Sid]; + ptr += event->m_Extensions[FD_Sid]; + newPtr += event->m_Extensions[FD_Sid]; + + memcpy(newPtr, ptr, event->m_Extensions[FD_FileName]); + newEvent->m_Extensions[FD_FileName] = event->m_Extensions[FD_FileName]; + ptr += event->m_Extensions[FD_FileName]; + newPtr += event->m_Extensions[FD_FileName]; + + memcpy(newPtr, ptr, event->m_Extensions[FD_ImagePath]); + newEvent->m_Extensions[FD_ImagePath] = event->m_Extensions[FD_ImagePath]; + ptr += event->m_Extensions[FD_ImagePath]; + newPtr += event->m_Extensions[FD_ImagePath]; + + memcpy(newPtr, ptr, event->m_Extensions[FD_Hash]); + newEvent->m_Extensions[FD_Hash] = event->m_Extensions[FD_Hash]; + newPtr += event->m_Extensions[FD_Hash]; + + newEventHdr->m_EventSize = (uint32_t)((void *)newPtr - (void *)newEventHdr); + + DispatchEvent(newEventHdr); +} + //-------------------------------------------------------------------- // // handleEvent @@ -565,12 +700,18 @@ static void handleEvent(void *ctx, int cpu, void *data, uint32_t size) case LinuxFileOpen: processFileOpen(eventHdr); break; + case FileDelete: + processFileDelete(eventHdr); + break; case LinuxNetworkEvent: processNetworkEvent(eventHdr); break; case ProcessAccess: processProcessAccess(eventHdr); break; + case ProcessCreate: + processProcessCreate(eventHdr); + break; case ProcessTerminate: { PSYSMON_PROCESS_TERMINATE event = (PSYSMON_PROCESS_TERMINATE)&eventHdr->m_EventBody.m_ProcessTerminateEvent;