From 20624d01f8b94a513637104d3de8c78818d2bb13 Mon Sep 17 00:00:00 2001 From: Yangff Date: Wed, 21 Feb 2024 02:36:28 -0600 Subject: [PATCH] move platform specific codes --- .../first/SinglePassSigScanner/CMakeLists.txt | 2 + .../include/SigScanner/Linux/DLData.hpp | 26 - .../SigScanner/SinglePassSigScanner.hpp | 346 ++++++++- .../SigScanner/SinglePassSigScannerLinux.hpp | 356 +-------- .../SigScanner/SinglePassSigScannerWin32.hpp | 332 +-------- .../src/SinglePassSigScanner.cpp | 676 +++++++++++++++++ .../src/SinglePassSigScannerLinux.cpp | 668 +---------------- .../src/SinglePassSigScannerWin32.cpp | 690 +----------------- deps/first/Unreal | 2 +- 9 files changed, 1097 insertions(+), 2001 deletions(-) delete mode 100644 deps/first/SinglePassSigScanner/include/SigScanner/Linux/DLData.hpp create mode 100644 deps/first/SinglePassSigScanner/src/SinglePassSigScanner.cpp diff --git a/deps/first/SinglePassSigScanner/CMakeLists.txt b/deps/first/SinglePassSigScanner/CMakeLists.txt index 3e3eabee8..1dd3eafc8 100644 --- a/deps/first/SinglePassSigScanner/CMakeLists.txt +++ b/deps/first/SinglePassSigScanner/CMakeLists.txt @@ -7,10 +7,12 @@ option(UE4SS_${TARGET}_BUILD_SHARED "Build as a shared lib" OFF) if (WIN32) set(${TARGET}_Sources + "${CMAKE_CURRENT_SOURCE_DIR}/src/SinglePassSigScanner.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/SinglePassSigScannerWin32.cpp" ) else() set(${TARGET}_Sources + "${CMAKE_CURRENT_SOURCE_DIR}/src/SinglePassSigScanner.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/SinglePassSigScannerLinux.cpp" ) endif() diff --git a/deps/first/SinglePassSigScanner/include/SigScanner/Linux/DLData.hpp b/deps/first/SinglePassSigScanner/include/SigScanner/Linux/DLData.hpp deleted file mode 100644 index cd39a1938..000000000 --- a/deps/first/SinglePassSigScanner/include/SigScanner/Linux/DLData.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -struct DLData -{ - std::string libname; - uint8_t* base_address; - uint64_t map_start, map_end; - size_t size; - // sorted by start address - std::set> phdrs; - std::tuple find_phdr(uint8_t* addr) { - for (auto& phdr : phdrs) { - // use end address to find the phdr - if (std::get<0>(phdr) + std::get<1>(phdr) > addr) { - return phdr; - } - } - return std::make_tuple(nullptr, 0, 0); - } -}; diff --git a/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScanner.hpp b/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScanner.hpp index bdd83425f..65479d1a6 100644 --- a/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScanner.hpp +++ b/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScanner.hpp @@ -1,7 +1,351 @@ #pragma once +#include +#include +#include +#include + +#include + + #ifdef WIN32 #include "SinglePassSigScannerWin32.hpp" #else #include "SinglePassSigScannerLinux.hpp" -#endif \ No newline at end of file +#endif + +#define HI_NIBBLE(b) (((b) >> 4) & 0x0F) +#define LO_NIBBLE(b) ((b)&0x0F) + +namespace RC +{ + enum class ScanTarget + { + MainExe, + + AIModule, + Analytics, + AnalyticsET, + AnimationCore, + AnimGraphRuntime, + AppFramework, + ApplicationCore, + AssetRegistry, + AudioCaptureCore, + AudioCaptureRtAudio, + AudioExtensions, + AudioMixer, + AudioMixerCore, + AudioMixerXAudio2, + AudioPlatformConfiguration, + AugmentedReality, + AVEncoder, + AVIWriter, + BuildPatchServices, + BuildSettings, + Cbor, + CEF3Utils, + Chaos, + ChaosCore, + ChaosSolverEngine, + ChaosSolvers, + CinematicCamera, + ClothingSystemRuntimeCommon, + ClothingSystemRuntimeInterface, + ClothingSystemRuntimeNv, + Core, + CoreUObject, + CrunchCompression, + D3D11RHI, + D3D12RHI, + Engine, + EngineMessages, + EngineSettings, + EyeTracker, + FieldSystemCore, + FieldSystemEngine, + FieldSystemSimulationCore, + Foliage, + GameplayMediaEncoder, + GameplayTags, + GameplayTasks, + GeometryCollectionCore, + GeometryCollectionEngine, + GeometryCollectionSimulationCore, + HeadMountedDisplay, + HTTP, + HttpNetworkReplayStreaming, + HTTPServer, + Icmp, + ImageCore, + ImageWrapper, + ImageWriteQueue, + InputCore, + InputDevice, + InstallBundleManager, + InstancedSplines, + InteractiveToolsFramework, + Json, + JsonUtilities, + Landscape, + LauncherCheck, + LauncherPlatform, + LevelSequence, + LocalFileNetworkReplayStreaming, + MaterialShaderQualitySettings, + Media, + MediaAssets, + MediaUtils, + MeshDescription, + MeshUtilitiesCommon, + Messaging, + MessagingCommon, + MoviePlayer, + MovieScene, + MovieSceneCapture, + MovieSceneTracks, + MRMesh, + NavigationSystem, + Navmesh, + NetCore, + Networking, + NetworkReplayStreaming, + NonRealtimeAudioRenderer, + NullDrv, + NullNetworkReplayStreaming, + OpenGLDrv, + Overlay, + PacketHandler, + PakFile, + PerfCounters, + PhysicsCore, + PhysicsSQ, + PhysXCooking, + PreLoadScreen, + Projects, + PropertyPath, + RawMesh, + ReliabilityHandlerComponent, + RenderCore, + Renderer, + RHI, + RSA, + SandboxFile, + Serialization, + SessionMessages, + SessionServices, + SignalProcessing, + Slate, + SlateCore, + SlateNullRenderer, + SlateRHIRenderer, + Sockets, + SoundFieldRendering, + SSL, + StaticMeshDescription, + StreamingPauseRendering, + SynthBenchmark, + TimeManagement, + TraceLog, + UELibSampleRate, + UMG, + VectorVM, + Voice, + Voronoi, + VulkanRHI, + WebBrowser, + WindowsPlatformFeatures, + XAudio2, + + Max, + }; + + auto RC_SPSS_API ScanTargetToString(ScanTarget scan_target) -> std::string; + auto RC_SPSS_API ScanTargetToString(size_t scan_target) -> std::string; + + class RC_SPSS_API ScanTargetArray + { + public: + std::array(ScanTarget::Max)> array{}; + + auto operator[](ScanTarget index) -> ModuleOS&; + }; + + // Static storage to be used across all sig scanner types + // At the moment the only scanner type that exists is SinglePassScanner + // In the future there might be a multi-threaded version of SinglePassScanner + class RC_SPSS_API SigScannerStaticData + { + public: + // Store all of the MODULEINFO structs for all of the scan targets + // Can a vector of something non-windows be stored here and then a static MODULEINFO can be created in the cpp file ? + static ScanTargetArray m_modules_info; + static bool m_is_modular; + }; + + struct RC_SPSS_API SignatureContainerLight + { + // The scanner will set this to whichever signature a match was found for + size_t index_into_signatures{}; + + // The scanner will set this to the address of the current match (if a match was found) + // This is guaranteed to be non-null when 'callable' is called + uint8_t* match_address{}; + }; + + struct RC_SPSS_API SignatureData + { + std::string signature{}; + + // TODO: Add 'ScanTarget' in here and remove 'ScanTarget' from the 'start_scan' function + + // The user-code can cast the custom data to their own enum type before using + // It will be zero-defaulted in the event of no custom data being supplied + int32_t custom_data{}; + + // A mask that's used for the StdFind scanning method. + std::string mask{}; + }; + + class SignatureContainer + { + private: + // Member variables in this class shouldn't be mutable outside of the scanner internals + // They cannot simply be private because this class isn't part of the scanner + // They cannot be const because they need to be mutated by the scanner + // The solution is to make everything private and give the scanner access by using the 'friend' keyword + friend class SinglePassScanner; + + private: + std::vector signatures; + const std::function on_match_found; + const std::function on_scan_finished; + + // Whether to store the results and pass them to on_scan_completed + const bool store_results{}; + + // The scanner will store results in here if 'store_results' is true + std::vector result_store{}; + + // True if the scan was successful, otherwise false + bool did_succeed{false}; + + // The scanner will use this to cancel all future calls to 'callable' if the 'callable' signaled + bool ignore{}; + + // The scanner will set this to whichever signature a match was found for + size_t index_into_signatures{}; + + // The scanner will set this to the address of the current match (if a match was found) + // This is guaranteed to be non-null when 'callable' is called + uint8_t* match_address{}; + + // The scanner will set this to the size of the signature that was matched + size_t match_signature_size{}; + + public: + template + SignatureContainer(std::vector sig_param, OnMatchFound on_match_found_param, OnScanFinished on_scan_finished_param) + : signatures(std::move(sig_param)), on_match_found(on_match_found_param), on_scan_finished(on_scan_finished_param) + { + } + + template + SignatureContainer(std::vector sig_param, OnMatchFound on_match_found_param, OnScanFinished on_scan_finished_param, bool store_results_param) + : signatures(std::move(sig_param)), on_match_found(on_match_found_param), on_scan_finished(on_scan_finished_param), store_results(store_results_param) + { + } + + public: + [[nodiscard]] auto get_match_address() const -> uint8_t* + { + return match_address; + } + [[nodiscard]] auto get_index_into_signatures() const -> size_t + { + return index_into_signatures; + } + [[nodiscard]] auto get_did_succeed() -> bool& + { + return did_succeed; + } + [[nodiscard]] auto get_did_succeed() const -> bool + { + return did_succeed; + } + [[nodiscard]] auto get_signatures() const -> const std::vector& + { + return signatures; + } + [[nodiscard]] auto get_result_store() const -> const std::vector& + { + return result_store; + } + [[nodiscard]] auto get_match_signature_size() const -> size_t + { + return match_signature_size; + } + }; + + class SinglePassScanner + { + private: + static std::mutex m_scanner_mutex; + + public: + enum class ScanMethod + { + Scalar, + StdFind, + }; + + public: + RC_SPSS_API static uint32_t m_num_threads; + RC_SPSS_API static ScanMethod m_scan_method; + + // The minimum size a module has to be for multi-threading to be enabled + // Smaller modules might increase the cost of scanning due to the cost of creating threads + RC_SPSS_API static uint32_t m_multithreading_module_size_threshold; + + private: + RC_SPSS_API auto static string_to_vector(std::string_view signature) -> std::vector; + RC_SPSS_API auto static string_to_vector(const std::vector& signatures) -> std::vector>; + RC_SPSS_API auto static format_aob_strings(std::vector& signature_containers) -> void; + + RC_SPSS_API auto static get_system_info() -> SystemInfo; + + public: + RC_SPSS_API auto static scanner_work_thread(uint8_t* start_address, + uint8_t* end_address, + SystemInfo& info, + std::vector& signature_containers) -> void; + RC_SPSS_API auto static scanner_work_thread_scalar(uint8_t* start_address, + uint8_t* end_address, + SystemInfo& info, + std::vector& signature_containers) -> void; + RC_SPSS_API auto static scanner_work_thread_stdfind(uint8_t* start_address, + uint8_t* end_address, + SystemInfo& info, + std::vector& signature_containers) -> void; + + using SignatureContainerMap = std::unordered_map>; + RC_SPSS_API auto static start_scan(SignatureContainerMap& signature_containers) -> void; + RC_SPSS_API auto static string_scan(std::wstring_view string_to_scan_for, ScanTarget = ScanTarget::MainExe) -> void*; + }; + + + namespace Platform { + // Get the start address of the system + auto get_start_address(SystemInfo &info) -> uint8_t*; + + // Get the end address of the system + auto get_end_address(SystemInfo &info) -> uint8_t*; + + // Get the size of the module + auto get_module_size(ModuleOS &info) -> uint32_t; + + // Get the base address of the module + auto get_module_base(ModuleOS &info) -> uint8_t*; + }; // namespace Platform + +}; // namespace RC diff --git a/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScannerLinux.hpp b/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScannerLinux.hpp index c50f68f09..2c87ceec9 100644 --- a/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScannerLinux.hpp +++ b/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScannerLinux.hpp @@ -1,336 +1,32 @@ #pragma once -#include -#include -#include -#include - -#include - -#include - -#define HI_NIBBLE(b) (((b) >> 4) & 0x0F) -#define LO_NIBBLE(b) ((b)&0x0F) +#include +#include +#include +#include namespace RC { - enum class ScanTarget - { - MainExe, - - AIModule, - Analytics, - AnalyticsET, - AnimationCore, - AnimGraphRuntime, - AppFramework, - ApplicationCore, - AssetRegistry, - AudioCaptureCore, - AudioCaptureRtAudio, - AudioExtensions, - AudioMixer, - AudioMixerCore, - AudioMixerXAudio2, - AudioPlatformConfiguration, - AugmentedReality, - AVEncoder, - AVIWriter, - BuildPatchServices, - BuildSettings, - Cbor, - CEF3Utils, - Chaos, - ChaosCore, - ChaosSolverEngine, - ChaosSolvers, - CinematicCamera, - ClothingSystemRuntimeCommon, - ClothingSystemRuntimeInterface, - ClothingSystemRuntimeNv, - Core, - CoreUObject, - CrunchCompression, - D3D11RHI, - D3D12RHI, - Engine, - EngineMessages, - EngineSettings, - EyeTracker, - FieldSystemCore, - FieldSystemEngine, - FieldSystemSimulationCore, - Foliage, - GameplayMediaEncoder, - GameplayTags, - GameplayTasks, - GeometryCollectionCore, - GeometryCollectionEngine, - GeometryCollectionSimulationCore, - HeadMountedDisplay, - HTTP, - HttpNetworkReplayStreaming, - HTTPServer, - Icmp, - ImageCore, - ImageWrapper, - ImageWriteQueue, - InputCore, - InputDevice, - InstallBundleManager, - InstancedSplines, - InteractiveToolsFramework, - Json, - JsonUtilities, - Landscape, - LauncherCheck, - LauncherPlatform, - LevelSequence, - LocalFileNetworkReplayStreaming, - MaterialShaderQualitySettings, - Media, - MediaAssets, - MediaUtils, - MeshDescription, - MeshUtilitiesCommon, - Messaging, - MessagingCommon, - MoviePlayer, - MovieScene, - MovieSceneCapture, - MovieSceneTracks, - MRMesh, - NavigationSystem, - Navmesh, - NetCore, - Networking, - NetworkReplayStreaming, - NonRealtimeAudioRenderer, - NullDrv, - NullNetworkReplayStreaming, - OpenGLDrv, - Overlay, - PacketHandler, - PakFile, - PerfCounters, - PhysicsCore, - PhysicsSQ, - PhysXCooking, - PreLoadScreen, - Projects, - PropertyPath, - RawMesh, - ReliabilityHandlerComponent, - RenderCore, - Renderer, - RHI, - RSA, - SandboxFile, - Serialization, - SessionMessages, - SessionServices, - SignalProcessing, - Slate, - SlateCore, - SlateNullRenderer, - SlateRHIRenderer, - Sockets, - SoundFieldRendering, - SSL, - StaticMeshDescription, - StreamingPauseRendering, - SynthBenchmark, - TimeManagement, - TraceLog, - UELibSampleRate, - UMG, - VectorVM, - Voice, - Voronoi, - VulkanRHI, - WebBrowser, - WindowsPlatformFeatures, - XAudio2, - - Max, - }; - - auto RC_SPSS_API ScanTargetToString(ScanTarget scan_target) -> std::string; - auto RC_SPSS_API ScanTargetToString(size_t scan_target) -> std::string; - -#ifdef WIN32 - class RC_SPSS_API ScanTargetArray - { - public: - std::array(ScanTarget::Max)> array{}; - - auto operator[](ScanTarget index) -> MODULEINFO&; - }; -#else - class RC_SPSS_API ScanTargetArray - { - public: - std::array(ScanTarget::Max)> array{}; - - auto operator[](ScanTarget index) -> DLData&; - }; -#endif - // Static storage to be used across all sig scanner types - // At the moment the only scanner type that exists is SinglePassScanner - // In the future there might be a multi-threaded version of SinglePassScanner - class RC_SPSS_API SigScannerStaticData - { - public: - // Store all of the MODULEINFO structs for all of the scan targets - // Can a vector of something non-windows be stored here and then a static MODULEINFO can be created in the cpp file ? - static ScanTargetArray m_modules_info; - static bool m_is_modular; - }; - - struct RC_SPSS_API SignatureContainerLight - { - // The scanner will set this to whichever signature a match was found for - size_t index_into_signatures{}; - - // The scanner will set this to the address of the current match (if a match was found) - // This is guaranteed to be non-null when 'callable' is called - uint8_t* match_address{}; - }; - - struct RC_SPSS_API SignatureData - { - std::string signature{}; - - // TODO: Add 'ScanTarget' in here and remove 'ScanTarget' from the 'start_scan' function - - // The user-code can cast the custom data to their own enum type before using - // It will be zero-defaulted in the event of no custom data being supplied - int32_t custom_data{}; - - // A mask that's used for the StdFind scanning method. - std::string mask{}; - }; - - class SignatureContainer - { - private: - // Member variables in this class shouldn't be mutable outside of the scanner internals - // They cannot simply be private because this class isn't part of the scanner - // They cannot be const because they need to be mutated by the scanner - // The solution is to make everything private and give the scanner access by using the 'friend' keyword - friend class SinglePassScanner; - - private: - std::vector signatures; - const std::function on_match_found; - const std::function on_scan_finished; - - // Whether to store the results and pass them to on_scan_completed - const bool store_results{}; - - // The scanner will store results in here if 'store_results' is true - std::vector result_store{}; - - // True if the scan was successful, otherwise false - bool did_succeed{false}; - - // The scanner will use this to cancel all future calls to 'callable' if the 'callable' signaled - bool ignore{}; - - // The scanner will set this to whichever signature a match was found for - size_t index_into_signatures{}; - - // The scanner will set this to the address of the current match (if a match was found) - // This is guaranteed to be non-null when 'callable' is called - uint8_t* match_address{}; - - // The scanner will set this to the size of the signature that was matched - size_t match_signature_size{}; - - public: - template - SignatureContainer(std::vector sig_param, OnMatchFound on_match_found_param, OnScanFinished on_scan_finished_param) - : signatures(std::move(sig_param)), on_match_found(on_match_found_param), on_scan_finished(on_scan_finished_param) - { - } - - template - SignatureContainer(std::vector sig_param, OnMatchFound on_match_found_param, OnScanFinished on_scan_finished_param, bool store_results_param) - : signatures(std::move(sig_param)), on_match_found(on_match_found_param), on_scan_finished(on_scan_finished_param), store_results(store_results_param) - { - } - - public: - [[nodiscard]] auto get_match_address() const -> uint8_t* - { - return match_address; - } - [[nodiscard]] auto get_index_into_signatures() const -> size_t - { - return index_into_signatures; - } - [[nodiscard]] auto get_did_succeed() -> bool& - { - return did_succeed; - } - [[nodiscard]] auto get_did_succeed() const -> bool - { - return did_succeed; - } - [[nodiscard]] auto get_signatures() const -> const std::vector& - { - return signatures; - } - [[nodiscard]] auto get_result_store() const -> const std::vector& - { - return result_store; - } - [[nodiscard]] auto get_match_signature_size() const -> size_t - { - return match_signature_size; - } - }; - - class SinglePassScanner - { - private: - static std::mutex m_scanner_mutex; - - public: - enum class ScanMethod - { - Scalar, - StdFind, - }; - - public: - RC_SPSS_API static uint32_t m_num_threads; - RC_SPSS_API static ScanMethod m_scan_method; - - // The minimum size a module has to be for multi-threading to be enabled - // Smaller modules might increase the cost of scanning due to the cost of creating threads - RC_SPSS_API static uint32_t m_multithreading_module_size_threshold; - - private: - RC_SPSS_API auto static string_to_vector(std::string_view signature) -> std::vector; - RC_SPSS_API auto static string_to_vector(const std::vector& signatures) -> std::vector>; - RC_SPSS_API auto static format_aob_strings(std::vector& signature_containers) -> void; - - public: - RC_SPSS_API auto static scanner_work_thread(uint8_t* start_address, - uint8_t* end_address, - DLData& info, - std::vector& signature_containers) -> void; - RC_SPSS_API auto static scanner_work_thread_scalar(uint8_t* start_address, - uint8_t* end_address, - DLData& info, - std::vector& signature_containers) -> void; - RC_SPSS_API auto static scanner_work_thread_stdfind(uint8_t* start_address, - uint8_t* end_address, - DLData& info, - std::vector& signature_containers) -> void; - - using SignatureContainerMap = std::unordered_map>; - RC_SPSS_API auto static start_scan(SignatureContainerMap& signature_containers) -> void; - }; + struct DLData + { + std::string libname; + uint8_t* base_address; + uint64_t map_start, map_end; + size_t size; + // sorted by start address + std::set> phdrs; + std::tuple find_phdr(uint8_t* addr) { + for (auto& phdr : phdrs) { + // use end address to find the phdr + if (std::get<0>(phdr) + std::get<1>(phdr) > addr) { + return phdr; + } + } + return std::make_tuple(nullptr, 0, 0); + } + }; + + using ModuleInfo = DLData; + using ModuleOS = DLData; + using SystemInfo = DLData; } // namespace RC diff --git a/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScannerWin32.hpp b/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScannerWin32.hpp index b82b3e5bb..ed27eeedf 100644 --- a/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScannerWin32.hpp +++ b/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScannerWin32.hpp @@ -1,15 +1,7 @@ #pragma once -#include -#include -#include -#include - #include -#define HI_NIBBLE(b) (((b) >> 4) & 0x0F) -#define LO_NIBBLE(b) ((b)&0x0F) - // Windows.h forward declarations struct _SYSTEM_INFO; typedef _SYSTEM_INFO SYSTEM_INFO; @@ -26,325 +18,7 @@ namespace RC void* EntryPoint; RC_SPSS_API auto operator=(MODULEINFO) -> WIN_MODULEINFO&; }; - - enum class ScanTarget - { - MainExe, - - AIModule, - Analytics, - AnalyticsET, - AnimationCore, - AnimGraphRuntime, - AppFramework, - ApplicationCore, - AssetRegistry, - AudioCaptureCore, - AudioCaptureRtAudio, - AudioExtensions, - AudioMixer, - AudioMixerCore, - AudioMixerXAudio2, - AudioPlatformConfiguration, - AugmentedReality, - AVEncoder, - AVIWriter, - BuildPatchServices, - BuildSettings, - Cbor, - CEF3Utils, - Chaos, - ChaosCore, - ChaosSolverEngine, - ChaosSolvers, - CinematicCamera, - ClothingSystemRuntimeCommon, - ClothingSystemRuntimeInterface, - ClothingSystemRuntimeNv, - Core, - CoreUObject, - CrunchCompression, - D3D11RHI, - D3D12RHI, - Engine, - EngineMessages, - EngineSettings, - EyeTracker, - FieldSystemCore, - FieldSystemEngine, - FieldSystemSimulationCore, - Foliage, - GameplayMediaEncoder, - GameplayTags, - GameplayTasks, - GeometryCollectionCore, - GeometryCollectionEngine, - GeometryCollectionSimulationCore, - HeadMountedDisplay, - HTTP, - HttpNetworkReplayStreaming, - HTTPServer, - Icmp, - ImageCore, - ImageWrapper, - ImageWriteQueue, - InputCore, - InputDevice, - InstallBundleManager, - InstancedSplines, - InteractiveToolsFramework, - Json, - JsonUtilities, - Landscape, - LauncherCheck, - LauncherPlatform, - LevelSequence, - LocalFileNetworkReplayStreaming, - MaterialShaderQualitySettings, - Media, - MediaAssets, - MediaUtils, - MeshDescription, - MeshUtilitiesCommon, - Messaging, - MessagingCommon, - MoviePlayer, - MovieScene, - MovieSceneCapture, - MovieSceneTracks, - MRMesh, - NavigationSystem, - Navmesh, - NetCore, - Networking, - NetworkReplayStreaming, - NonRealtimeAudioRenderer, - NullDrv, - NullNetworkReplayStreaming, - OpenGLDrv, - Overlay, - PacketHandler, - PakFile, - PerfCounters, - PhysicsCore, - PhysicsSQ, - PhysXCooking, - PreLoadScreen, - Projects, - PropertyPath, - RawMesh, - ReliabilityHandlerComponent, - RenderCore, - Renderer, - RHI, - RSA, - SandboxFile, - Serialization, - SessionMessages, - SessionServices, - SignalProcessing, - Slate, - SlateCore, - SlateNullRenderer, - SlateRHIRenderer, - Sockets, - SoundFieldRendering, - SSL, - StaticMeshDescription, - StreamingPauseRendering, - SynthBenchmark, - TimeManagement, - TraceLog, - UELibSampleRate, - UMG, - VectorVM, - Voice, - Voronoi, - VulkanRHI, - WebBrowser, - WindowsPlatformFeatures, - XAudio2, - - Max, - }; - - auto RC_SPSS_API ScanTargetToString(ScanTarget scan_target) -> std::string; - auto RC_SPSS_API ScanTargetToString(size_t scan_target) -> std::string; - -#ifdef WIN32 - class RC_SPSS_API ScanTargetArray - { - public: - std::array(ScanTarget::Max)> array{}; - - auto operator[](ScanTarget index) -> MODULEINFO&; - }; -#else - class RC_SPSS_API ScanTargetArray - { - public: - std::array(ScanTarget::Max)> array{}; - - auto operator[](ScanTarget index) -> DLData&; - }; -#endif - // Static storage to be used across all sig scanner types - // At the moment the only scanner type that exists is SinglePassScanner - // In the future there might be a multi-threaded version of SinglePassScanner - class RC_SPSS_API SigScannerStaticData - { - public: - // Store all of the MODULEINFO structs for all of the scan targets - // Can a vector of something non-windows be stored here and then a static MODULEINFO can be created in the cpp file ? - static ScanTargetArray m_modules_info; - static bool m_is_modular; - }; - - struct RC_SPSS_API SignatureContainerLight - { - // The scanner will set this to whichever signature a match was found for - size_t index_into_signatures{}; - - // The scanner will set this to the address of the current match (if a match was found) - // This is guaranteed to be non-null when 'callable' is called - uint8_t* match_address{}; - }; - - struct RC_SPSS_API SignatureData - { - std::string signature{}; - - // TODO: Add 'ScanTarget' in here and remove 'ScanTarget' from the 'start_scan' function - - // The user-code can cast the custom data to their own enum type before using - // It will be zero-defaulted in the event of no custom data being supplied - int32_t custom_data{}; - - // A mask that's used for the StdFind scanning method. - std::string mask{}; - }; - - class SignatureContainer - { - private: - // Member variables in this class shouldn't be mutable outside of the scanner internals - // They cannot simply be private because this class isn't part of the scanner - // They cannot be const because they need to be mutated by the scanner - // The solution is to make everything private and give the scanner access by using the 'friend' keyword - friend class SinglePassScanner; - - private: - std::vector signatures; - const std::function on_match_found; - const std::function on_scan_finished; - - // Whether to store the results and pass them to on_scan_completed - const bool store_results{}; - - // The scanner will store results in here if 'store_results' is true - std::vector result_store{}; - - // True if the scan was successful, otherwise false - bool did_succeed{false}; - - // The scanner will use this to cancel all future calls to 'callable' if the 'callable' signaled - bool ignore{}; - - // The scanner will set this to whichever signature a match was found for - size_t index_into_signatures{}; - - // The scanner will set this to the address of the current match (if a match was found) - // This is guaranteed to be non-null when 'callable' is called - uint8_t* match_address{}; - - // The scanner will set this to the size of the signature that was matched - size_t match_signature_size{}; - - public: - template - SignatureContainer(std::vector sig_param, OnMatchFound on_match_found_param, OnScanFinished on_scan_finished_param) - : signatures(std::move(sig_param)), on_match_found(on_match_found_param), on_scan_finished(on_scan_finished_param) - { - } - - template - SignatureContainer(std::vector sig_param, OnMatchFound on_match_found_param, OnScanFinished on_scan_finished_param, bool store_results_param) - : signatures(std::move(sig_param)), on_match_found(on_match_found_param), on_scan_finished(on_scan_finished_param), store_results(store_results_param) - { - } - - public: - [[nodiscard]] auto get_match_address() const -> uint8_t* - { - return match_address; - } - [[nodiscard]] auto get_index_into_signatures() const -> size_t - { - return index_into_signatures; - } - [[nodiscard]] auto get_did_succeed() -> bool& - { - return did_succeed; - } - [[nodiscard]] auto get_did_succeed() const -> bool - { - return did_succeed; - } - [[nodiscard]] auto get_signatures() const -> const std::vector& - { - return signatures; - } - [[nodiscard]] auto get_result_store() const -> const std::vector& - { - return result_store; - } - [[nodiscard]] auto get_match_signature_size() const -> size_t - { - return match_signature_size; - } - }; - - class SinglePassScanner - { - private: - static std::mutex m_scanner_mutex; - - public: - enum class ScanMethod - { - Scalar, - StdFind, - }; - - public: - RC_SPSS_API static uint32_t m_num_threads; - RC_SPSS_API static ScanMethod m_scan_method; - - // The minimum size a module has to be for multi-threading to be enabled - // Smaller modules might increase the cost of scanning due to the cost of creating threads - RC_SPSS_API static uint32_t m_multithreading_module_size_threshold; - - private: - RC_SPSS_API auto static string_to_vector(std::string_view signature) -> std::vector; - RC_SPSS_API auto static string_to_vector(const std::vector& signatures) -> std::vector>; - RC_SPSS_API auto static format_aob_strings(std::vector& signature_containers) -> void; - - public: - RC_SPSS_API auto static scanner_work_thread(uint8_t* start_address, - uint8_t* end_address, - SYSTEM_INFO& info, - std::vector& signature_containers) -> void; - RC_SPSS_API auto static scanner_work_thread_scalar(uint8_t* start_address, - uint8_t* end_address, - SYSTEM_INFO& info, - std::vector& signature_containers) -> void; - RC_SPSS_API auto static scanner_work_thread_stdfind(uint8_t* start_address, - uint8_t* end_address, - SYSTEM_INFO& info, - std::vector& signature_containers) -> void; - - using SignatureContainerMap = std::unordered_map>; - RC_SPSS_API auto static start_scan(SignatureContainerMap& signature_containers) -> void; - RC_SPSS_API auto static string_scan(std::wstring_view string_to_scan_for, ScanTarget = ScanTarget::MainExe) -> void*; - }; + using ModuleInfo = WIN_MODULEINFO; + using ModuleOS = MODULEINFO; + using SystemInfo = SYSTEM_INFO; } // namespace RC diff --git a/deps/first/SinglePassSigScanner/src/SinglePassSigScanner.cpp b/deps/first/SinglePassSigScanner/src/SinglePassSigScanner.cpp new file mode 100644 index 000000000..9f40e296e --- /dev/null +++ b/deps/first/SinglePassSigScanner/src/SinglePassSigScanner.cpp @@ -0,0 +1,676 @@ +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace RC +{ + ScanTargetArray SigScannerStaticData::m_modules_info; + bool SigScannerStaticData::m_is_modular; + + uint32_t SinglePassScanner::m_num_threads = 8; + SinglePassScanner::ScanMethod SinglePassScanner::m_scan_method = ScanMethod::Scalar; + uint32_t SinglePassScanner::m_multithreading_module_size_threshold = 0x1000000; + std::mutex SinglePassScanner::m_scanner_mutex{}; + + auto ScanTargetArray::operator[](ScanTarget index) -> ModuleOS& + { + return array[static_cast(index)]; + } + + auto ScanTargetToString(ScanTarget scan_target) -> std::string + { + switch (scan_target) + { + case ScanTarget::MainExe: + return {"MainExe"}; + case ScanTarget::AIModule: + return {"AIModule"}; + case ScanTarget::Analytics: + return {"Analytics"}; + case ScanTarget::AnalyticsET: + return {"AnalyticsET"}; + case ScanTarget::AnimationCore: + return {"AnimationCore"}; + case ScanTarget::AnimGraphRuntime: + return {"AnimGraphRuntime"}; + case ScanTarget::AppFramework: + return {"AppFramework"}; + case ScanTarget::ApplicationCore: + return {"ApplicationCore"}; + case ScanTarget::AssetRegistry: + return {"AssetRegistry"}; + case ScanTarget::AudioCaptureCore: + return {"AudioCaptureCore"}; + case ScanTarget::AudioCaptureRtAudio: + return {"AudioCaptureRtAudio"}; + case ScanTarget::AudioExtensions: + return {"AudioExtensions"}; + case ScanTarget::AudioMixer: + return {"AudioMixer"}; + case ScanTarget::AudioMixerCore: + return {"AudioMixerCore"}; + case ScanTarget::AudioMixerXAudio2: + return {"AudioMixerXAudio2"}; + case ScanTarget::AudioPlatformConfiguration: + return {"AudioPlatformConfiguration"}; + case ScanTarget::AugmentedReality: + return {"AugmentedReality"}; + case ScanTarget::AVEncoder: + return {"AVEncoder"}; + case ScanTarget::AVIWriter: + return {"AVIWriter"}; + case ScanTarget::BuildPatchServices: + return {"BuildPatchServices"}; + case ScanTarget::BuildSettings: + return {"BuildSettings"}; + case ScanTarget::Cbor: + return {"Cbor"}; + case ScanTarget::CEF3Utils: + return {"CEF3Utils"}; + case ScanTarget::Chaos: + return {"Chaos"}; + case ScanTarget::ChaosCore: + return {"ChaosCore"}; + case ScanTarget::ChaosSolverEngine: + return {"ChaosSolverEngine"}; + case ScanTarget::ChaosSolvers: + return {"ChaosSolvers"}; + case ScanTarget::CinematicCamera: + return {"CinematicCamera"}; + case ScanTarget::ClothingSystemRuntimeCommon: + return {"ClothingSystemRuntimeCommon"}; + case ScanTarget::ClothingSystemRuntimeInterface: + return {"ClothingSystemRuntimeInterface"}; + case ScanTarget::ClothingSystemRuntimeNv: + return {"ClothingSystemRuntimeNv"}; + case ScanTarget::Core: + return {"Core"}; + case ScanTarget::CoreUObject: + return {"CoreUObject"}; + case ScanTarget::CrunchCompression: + return {"CrunchCompression"}; + case ScanTarget::D3D11RHI: + return {"D3D11RHI"}; + case ScanTarget::D3D12RHI: + return {"D3D12RHI"}; + case ScanTarget::Engine: + return {"Engine"}; + case ScanTarget::EngineMessages: + return {"EngineMessages"}; + case ScanTarget::EngineSettings: + return {"EngineSettings"}; + case ScanTarget::EyeTracker: + return {"EyeTracker"}; + case ScanTarget::FieldSystemCore: + return {"FieldSystemCore"}; + case ScanTarget::FieldSystemEngine: + return {"FieldSystemEngine"}; + case ScanTarget::FieldSystemSimulationCore: + return {"FieldSystemSimulationCore"}; + case ScanTarget::Foliage: + return {"Foliage"}; + case ScanTarget::GameplayMediaEncoder: + return {"GameplayMediaEncoder"}; + case ScanTarget::GameplayTags: + return {"GameplayTags"}; + case ScanTarget::GameplayTasks: + return {"GameplayTasks"}; + case ScanTarget::GeometryCollectionCore: + return {"GeometryCollectionCore"}; + case ScanTarget::GeometryCollectionEngine: + return {"GeometryCollectionEngine"}; + case ScanTarget::GeometryCollectionSimulationCore: + return {"GeometryCollectionSimulationCore"}; + case ScanTarget::HeadMountedDisplay: + return {"HeadMountedDisplay"}; + case ScanTarget::HTTP: + return {"HTTP"}; + case ScanTarget::HttpNetworkReplayStreaming: + return {"HttpNetworkReplayStreaming"}; + case ScanTarget::HTTPServer: + return {"HTTPServer"}; + case ScanTarget::Icmp: + return {"Icmp"}; + case ScanTarget::ImageCore: + return {"ImageCore"}; + case ScanTarget::ImageWrapper: + return {"ImageWrapper"}; + case ScanTarget::ImageWriteQueue: + return {"ImageWriteQueue"}; + case ScanTarget::InputCore: + return {"InputCore"}; + case ScanTarget::InputDevice: + return {"InputDevice"}; + case ScanTarget::InstallBundleManager: + return {"InstallBundleManager"}; + case ScanTarget::InstancedSplines: + return {"InstancedSplines"}; + case ScanTarget::InteractiveToolsFramework: + return {"InteractiveToolsFramework"}; + case ScanTarget::Json: + return {"Json"}; + case ScanTarget::JsonUtilities: + return {"JsonUtilities"}; + case ScanTarget::Landscape: + return {"Landscape"}; + case ScanTarget::LauncherCheck: + return {"LauncherCheck"}; + case ScanTarget::LauncherPlatform: + return {"LauncherPlatform"}; + case ScanTarget::LevelSequence: + return {"LevelSequence"}; + case ScanTarget::LocalFileNetworkReplayStreaming: + return {"LocalFileNetworkReplayStreaming"}; + case ScanTarget::MaterialShaderQualitySettings: + return {"MaterialShaderQualitySettings"}; + case ScanTarget::Media: + return {"Media"}; + case ScanTarget::MediaAssets: + return {"MediaAssets"}; + case ScanTarget::MediaUtils: + return {"MediaUtils"}; + case ScanTarget::MeshDescription: + return {"MeshDescription"}; + case ScanTarget::MeshUtilitiesCommon: + return {"MeshUtilitiesCommon"}; + case ScanTarget::Messaging: + return {"Messaging"}; + case ScanTarget::MessagingCommon: + return {"MessagingCommon"}; + case ScanTarget::MoviePlayer: + return {"MoviePlayer"}; + case ScanTarget::MovieScene: + return {"MovieScene"}; + case ScanTarget::MovieSceneCapture: + return {"MovieSceneCapture"}; + case ScanTarget::MovieSceneTracks: + return {"MovieSceneTracks"}; + case ScanTarget::MRMesh: + return {"MRMesh"}; + case ScanTarget::NavigationSystem: + return {"NavigationSystem"}; + case ScanTarget::Navmesh: + return {"Navmesh"}; + case ScanTarget::NetCore: + return {"NetCore"}; + case ScanTarget::Networking: + return {"Networking"}; + case ScanTarget::NetworkReplayStreaming: + return {"NetworkReplayStreaming"}; + case ScanTarget::NonRealtimeAudioRenderer: + return {"NonRealtimeAudioRenderer"}; + case ScanTarget::NullDrv: + return {"NullDrv"}; + case ScanTarget::NullNetworkReplayStreaming: + return {"NullNetworkReplayStreaming"}; + case ScanTarget::OpenGLDrv: + return {"OpenGLDrv"}; + case ScanTarget::Overlay: + return {"Overlay"}; + case ScanTarget::PacketHandler: + return {"PacketHandler"}; + case ScanTarget::PakFile: + return {"PakFile"}; + case ScanTarget::PerfCounters: + return {"PerfCounters"}; + case ScanTarget::PhysicsCore: + return {"PhysicsCore"}; + case ScanTarget::PhysicsSQ: + return {"PhysicsSQ"}; + case ScanTarget::PhysXCooking: + return {"PhysXCooking"}; + case ScanTarget::PreLoadScreen: + return {"PreLoadScreen"}; + case ScanTarget::Projects: + return {"Projects"}; + case ScanTarget::PropertyPath: + return {"PropertyPath"}; + case ScanTarget::RawMesh: + return {"RawMesh"}; + case ScanTarget::ReliabilityHandlerComponent: + return {"ReliabilityHandlerComponent"}; + case ScanTarget::RenderCore: + return {"RenderCore"}; + case ScanTarget::Renderer: + return {"Renderer"}; + case ScanTarget::RHI: + return {"RHI"}; + case ScanTarget::RSA: + return {"RSA"}; + case ScanTarget::SandboxFile: + return {"SandboxFile"}; + case ScanTarget::Serialization: + return {"Serialization"}; + case ScanTarget::SessionMessages: + return {"SessionMessages"}; + case ScanTarget::SessionServices: + return {"SessionServices"}; + case ScanTarget::SignalProcessing: + return {"SignalProcessing"}; + case ScanTarget::Slate: + return {"Slate"}; + case ScanTarget::SlateCore: + return {"SlateCore"}; + case ScanTarget::SlateNullRenderer: + return {"SlateNullRenderer"}; + case ScanTarget::SlateRHIRenderer: + return {"SlateRHIRenderer"}; + case ScanTarget::Sockets: + return {"Sockets"}; + case ScanTarget::SoundFieldRendering: + return {"SoundFieldRendering"}; + case ScanTarget::SSL: + return {"SSL"}; + case ScanTarget::StaticMeshDescription: + return {"StaticMeshDescription"}; + case ScanTarget::StreamingPauseRendering: + return {"StreamingPauseRendering"}; + case ScanTarget::SynthBenchmark: + return {"SynthBenchmark"}; + case ScanTarget::TimeManagement: + return {"TimeManagement"}; + case ScanTarget::TraceLog: + return {"TraceLog"}; + case ScanTarget::UELibSampleRate: + return {"UELibSampleRate"}; + case ScanTarget::UMG: + return {"UMG"}; + case ScanTarget::VectorVM: + return {"VectorVM"}; + case ScanTarget::Voice: + return {"Voice"}; + case ScanTarget::Voronoi: + return {"Voronoi"}; + case ScanTarget::VulkanRHI: + return {"VulkanRHI"}; + case ScanTarget::WebBrowser: + return {"WebBrowser"}; + case ScanTarget::WindowsPlatformFeatures: + return {"WindowsPlatformFeatures"}; + case ScanTarget::XAudio2: + return {"XAudio2"}; + case ScanTarget::Max: + return {"Max"}; + } + + throw std::runtime_error{std::format("Invalid param for ScanTargetToString, param: {}", static_cast(scan_target))}; + } + + auto ScanTargetToString(size_t scan_target) -> std::string + { + return ScanTargetToString(static_cast(scan_target)); + } + + static auto ConvertHexCharToInt(char ch) -> int + { + if (ch >= '0' && ch <= '9') return ch - '0'; + if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10; + if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10; + return -1; + } + + auto SinglePassScanner::string_to_vector(std::string_view signature) -> std::vector + { + std::vector bytes; + char* const start = const_cast(signature.data()); + char* const end = const_cast(signature.data()) + strlen(signature.data()); + + for (char* current = start; current < end; current++) + { + if (*current == '?') + { + bytes.push_back(-1); + } + else if (std::isxdigit(*current)) + { + bytes.push_back(ConvertHexCharToInt(*current)); + } + } + + return bytes; + } + + auto SinglePassScanner::string_to_vector(const std::vector& signatures) -> std::vector> + { + std::vector> vector_of_signatures; + vector_of_signatures.reserve(signatures.size()); + + for (const auto& signature_data : signatures) + { + vector_of_signatures.emplace_back(string_to_vector(signature_data.signature)); + } + + return vector_of_signatures; + } + + struct PatternData + { + std::vector pattern{}; + std::vector mask{}; + SignatureContainer* signature_container{}; + }; + + static auto CharToByte(char symbol) -> uint8_t + { + if (symbol >= 'a' && symbol <= 'z') + { + return symbol - 'a' + 0xA; + } + else if (symbol >= 'A' && symbol <= 'Z') + { + return symbol - 'A' + 0xA; + } + else if (symbol >= '0' && symbol <= '9') + { + return symbol - '0'; + } + else + { + return 0; + } + } + + static auto make_mask(std::string_view pattern, SignatureContainer& signature_container, const size_t data_size) -> PatternData + { + PatternData pattern_data{}; + + if (pattern.length() < 1 || pattern[0] == '?') + { + throw std::runtime_error{std::format("[make_mask] A pattern cannot start with a wildcard.\nPattern: {}", pattern)}; + } + + for (size_t i = 0; i < pattern.length(); i++) + { + char symbol = pattern[i]; + char next_symbol = ((i + 1) < pattern.length()) ? pattern[i + 1] : 0; + if (symbol == ' ') + { + continue; + } + + if (symbol == '?') + { + pattern_data.pattern.push_back(0x00); + pattern_data.mask.push_back(0x00); + + if (next_symbol == '?') + { + ++i; + } + continue; + } + + uint8_t byte = CharToByte(symbol) << 4 | CharToByte(next_symbol); + + pattern_data.pattern.push_back(byte); + pattern_data.mask.push_back(0xff); + + ++i; + } + + static constexpr size_t Alignment = 32; + size_t count = (size_t)std::ceil((float)data_size / Alignment); + size_t padding_size = count * Alignment - data_size; + + for (size_t i = 0; i < padding_size; i++) + { + pattern_data.pattern.push_back(0x00); + pattern_data.mask.push_back(0x00); + } + + pattern_data.signature_container = &signature_container; + return pattern_data; + } + + auto SinglePassScanner::scanner_work_thread(uint8_t* start_address, uint8_t* end_address, SystemInfo& info, std::vector& signature_containers) + -> void + { + ProfilerSetThreadName("UE4SS-ScannerWorkThread"); + ProfilerScope(); + + switch (m_scan_method) + { + case ScanMethod::Scalar: + scanner_work_thread_scalar(start_address, end_address, info, signature_containers); + break; + case ScanMethod::StdFind: + scanner_work_thread_stdfind(start_address, end_address, info, signature_containers); + break; + } + } + + static auto format_aob_string(std::string& str) -> void + { + if (str.size() < 4) + { + return; + } + if (str[3] != '/') + { + return; + } + + std::erase_if(str, [&](const char c) { + return c == ' '; + }); + + std::ranges::transform(str, str.begin(), [&str](const char c) { + return c == '/' ? ' ' : c; + }); + } + + auto SinglePassScanner::format_aob_strings(std::vector& signature_containers) -> void + { + std::lock_guard safe_scope(m_scanner_mutex); + for (auto& signature_container : signature_containers) + { + for (auto& signature : signature_container.signatures) + { + format_aob_string(signature.signature); + } + } + } + + auto SinglePassScanner::scanner_work_thread_stdfind(uint8_t* start_address, + uint8_t* end_address, + SystemInfo& info, + std::vector& signature_containers) -> void + { + ProfilerScope(); + + if (!start_address) + { + start_address = Platform::get_start_address(info); + } + if (!end_address) + { + end_address = Platform::get_end_address(info); + } + + format_aob_strings(signature_containers); + + std::vector> pattern_datas{}; + for (auto& signature_container : signature_containers) + { + auto& pattern_data = pattern_datas.emplace_back(); + for (auto& signature : signature_container.signatures) + { + pattern_data.emplace_back(make_mask(signature.signature, signature_container, end_address - start_address)); + } + } + + // Loop everything + for (size_t container_index = 0; const auto& patterns : pattern_datas) + { + for (size_t signature_index = 0; const auto& pattern_data : patterns) + { + // If the container is refusing more calls then skip to the next container + if (pattern_data.signature_container->ignore) + { + break; + } + + auto it = start_address; + auto end = it + (end_address - start_address) - (pattern_data.pattern.size()) + 1; + uint8_t needle = pattern_data.pattern[0]; + + bool skip_to_next_container{}; + while (end != (it = std::find(it, end, needle))) + { + bool found = true; + for (size_t pattern_offset = 0; pattern_offset < pattern_data.pattern.size(); ++pattern_offset) + { + if ((it[pattern_offset] & pattern_data.mask[pattern_offset]) != pattern_data.pattern[pattern_offset]) + { + found = false; + break; + } + } + + if (found) + { + { + std::lock_guard safe_scope(m_scanner_mutex); + + // Checking for the second time if the container is refusing more calls + // This is required when multi-threading is enabled + if (pattern_data.signature_container->ignore) + { + skip_to_next_container = true; + break; + } + + // One of the signatures have found a full match so lets forward the details to the callable + pattern_data.signature_container->index_into_signatures = signature_index; + pattern_data.signature_container->match_address = it; + pattern_data.signature_container->match_signature_size = pattern_data.pattern.size(); + + skip_to_next_container = pattern_data.signature_container->on_match_found(*pattern_data.signature_container); + pattern_data.signature_container->ignore = skip_to_next_container; + + // Store results if the container at the containers request + if (pattern_data.signature_container->store_results) + { + pattern_data.signature_container->result_store.emplace_back( + SignatureContainerLight{.index_into_signatures = signature_index, .match_address = it}); + } + } + } + + it++; + } + + if (skip_to_next_container) + { + // A match was found and signaled to skip to the next container + break; + } + + ++signature_index; + } + ++container_index; + } + } + + auto SinglePassScanner::start_scan(SignatureContainerMap& signature_containers) -> void + { + SystemInfo info = get_system_info(); + + // If not modular then the containers get merged into one scan target + // That way there are no extra scans + // If modular then loop the containers and retrieve the scan target for each and pass everything to the do_scan() lambda + fprintf(stderr, "signature_containers.size() = %d\n", signature_containers.size()); + if (!SigScannerStaticData::m_is_modular) + { + ModuleOS merged_module_info{}; + std::vector merged_containers; + + for (const auto& [scan_target, outer_container] : signature_containers) + { + merged_module_info = *std::bit_cast(&SigScannerStaticData::m_modules_info[scan_target]); + fprintf(stderr, "outer_container len = %d\n", outer_container.size()); + for (const auto& signature_container : outer_container) + { + merged_containers.emplace_back(signature_container); + } + } + + if (merged_containers.empty()) + { + fprintf(stderr, "No containers to merge\n"); + return ; + //throw std::runtime_error{"[SinglePassScanner::start_scan] Could not merge containers. Either there were not containers to merge or there was " + // "an internal error."}; + } + + uint8_t* module_start_address = Platform::get_module_base(merged_module_info); + + if (Platform::get_module_size(merged_module_info) >= m_multithreading_module_size_threshold) + { + // Module is large enough to make it overall faster to scan with multiple threads + std::vector> scan_threads; + + // Higher values are better for debugging + // You will get a bigger diminishing returns the faster your computer is (especially in release mode) + uint32_t range = Platform::get_module_size(merged_module_info) / m_num_threads; + + // Calculating the ranges for each thread to scan and starting the scan + uint32_t last_range{}; + for (uint32_t thread_id = 0; thread_id < m_num_threads; ++thread_id) + { + scan_threads.emplace_back(std::async(std::launch::async, + &scanner_work_thread, + module_start_address + last_range, + module_start_address + last_range + range, + std::ref(info), + std::ref(merged_containers))); + + last_range += range; + } + + for (const auto& scan_thread : scan_threads) + { + scan_thread.wait(); + } + } + else + { + // Module is too small to make it overall faster to scan with multiple threads + uint8_t* module_end_address = static_cast(module_start_address + Platform::get_module_size(merged_module_info)); + scanner_work_thread(module_start_address, module_end_address, merged_module_info, merged_containers); + } + + for (auto& container : merged_containers) + { + container.on_scan_finished(container); + } + } + else + { + // This ranged for loop is performing a copy of unordered_map> + // Is this required ? Would it be worth trying to avoid copying here ? + // Right now it can't be auto& or const auto& because the do_scan function takes a non-const since it needs to mutate the values inside the vector + auto info = SigScannerStaticData::m_modules_info[ScanTarget::MainExe]; + for (auto& [scan_target, signature_container] : signature_containers) + { + uint8_t* module_start_address = static_cast(Platform::get_module_base(SigScannerStaticData::m_modules_info[scan_target])); + uint8_t* module_end_address = static_cast(module_start_address + Platform::get_module_size(SigScannerStaticData::m_modules_info[scan_target])); + + scanner_work_thread(module_start_address, module_end_address, info, signature_container); + + for (auto& container : signature_container) + { + container.on_scan_finished(container); + } + } + } + } +}; // namespace RC + diff --git a/deps/first/SinglePassSigScanner/src/SinglePassSigScannerLinux.cpp b/deps/first/SinglePassSigScanner/src/SinglePassSigScannerLinux.cpp index b29b3aa9b..6fea0c84f 100644 --- a/deps/first/SinglePassSigScanner/src/SinglePassSigScannerLinux.cpp +++ b/deps/first/SinglePassSigScanner/src/SinglePassSigScannerLinux.cpp @@ -11,446 +11,39 @@ #include -#define byte uint8_t - namespace RC { - ScanTargetArray SigScannerStaticData::m_modules_info; - bool SigScannerStaticData::m_is_modular; - - uint32_t SinglePassScanner::m_num_threads = 8; - SinglePassScanner::ScanMethod SinglePassScanner::m_scan_method = ScanMethod::Scalar; - uint32_t SinglePassScanner::m_multithreading_module_size_threshold = 0x1000000; - std::mutex SinglePassScanner::m_scanner_mutex{}; - - auto ScanTargetArray::operator[](ScanTarget index) -> DLData& - { - return array[static_cast(index)]; - } - - - auto ScanTargetToString(ScanTarget scan_target) -> std::string - { - switch (scan_target) - { - case ScanTarget::MainExe: - return {"MainExe"}; - case ScanTarget::AIModule: - return {"AIModule"}; - case ScanTarget::Analytics: - return {"Analytics"}; - case ScanTarget::AnalyticsET: - return {"AnalyticsET"}; - case ScanTarget::AnimationCore: - return {"AnimationCore"}; - case ScanTarget::AnimGraphRuntime: - return {"AnimGraphRuntime"}; - case ScanTarget::AppFramework: - return {"AppFramework"}; - case ScanTarget::ApplicationCore: - return {"ApplicationCore"}; - case ScanTarget::AssetRegistry: - return {"AssetRegistry"}; - case ScanTarget::AudioCaptureCore: - return {"AudioCaptureCore"}; - case ScanTarget::AudioCaptureRtAudio: - return {"AudioCaptureRtAudio"}; - case ScanTarget::AudioExtensions: - return {"AudioExtensions"}; - case ScanTarget::AudioMixer: - return {"AudioMixer"}; - case ScanTarget::AudioMixerCore: - return {"AudioMixerCore"}; - case ScanTarget::AudioMixerXAudio2: - return {"AudioMixerXAudio2"}; - case ScanTarget::AudioPlatformConfiguration: - return {"AudioPlatformConfiguration"}; - case ScanTarget::AugmentedReality: - return {"AugmentedReality"}; - case ScanTarget::AVEncoder: - return {"AVEncoder"}; - case ScanTarget::AVIWriter: - return {"AVIWriter"}; - case ScanTarget::BuildPatchServices: - return {"BuildPatchServices"}; - case ScanTarget::BuildSettings: - return {"BuildSettings"}; - case ScanTarget::Cbor: - return {"Cbor"}; - case ScanTarget::CEF3Utils: - return {"CEF3Utils"}; - case ScanTarget::Chaos: - return {"Chaos"}; - case ScanTarget::ChaosCore: - return {"ChaosCore"}; - case ScanTarget::ChaosSolverEngine: - return {"ChaosSolverEngine"}; - case ScanTarget::ChaosSolvers: - return {"ChaosSolvers"}; - case ScanTarget::CinematicCamera: - return {"CinematicCamera"}; - case ScanTarget::ClothingSystemRuntimeCommon: - return {"ClothingSystemRuntimeCommon"}; - case ScanTarget::ClothingSystemRuntimeInterface: - return {"ClothingSystemRuntimeInterface"}; - case ScanTarget::ClothingSystemRuntimeNv: - return {"ClothingSystemRuntimeNv"}; - case ScanTarget::Core: - return {"Core"}; - case ScanTarget::CoreUObject: - return {"CoreUObject"}; - case ScanTarget::CrunchCompression: - return {"CrunchCompression"}; - case ScanTarget::D3D11RHI: - return {"D3D11RHI"}; - case ScanTarget::D3D12RHI: - return {"D3D12RHI"}; - case ScanTarget::Engine: - return {"Engine"}; - case ScanTarget::EngineMessages: - return {"EngineMessages"}; - case ScanTarget::EngineSettings: - return {"EngineSettings"}; - case ScanTarget::EyeTracker: - return {"EyeTracker"}; - case ScanTarget::FieldSystemCore: - return {"FieldSystemCore"}; - case ScanTarget::FieldSystemEngine: - return {"FieldSystemEngine"}; - case ScanTarget::FieldSystemSimulationCore: - return {"FieldSystemSimulationCore"}; - case ScanTarget::Foliage: - return {"Foliage"}; - case ScanTarget::GameplayMediaEncoder: - return {"GameplayMediaEncoder"}; - case ScanTarget::GameplayTags: - return {"GameplayTags"}; - case ScanTarget::GameplayTasks: - return {"GameplayTasks"}; - case ScanTarget::GeometryCollectionCore: - return {"GeometryCollectionCore"}; - case ScanTarget::GeometryCollectionEngine: - return {"GeometryCollectionEngine"}; - case ScanTarget::GeometryCollectionSimulationCore: - return {"GeometryCollectionSimulationCore"}; - case ScanTarget::HeadMountedDisplay: - return {"HeadMountedDisplay"}; - case ScanTarget::HTTP: - return {"HTTP"}; - case ScanTarget::HttpNetworkReplayStreaming: - return {"HttpNetworkReplayStreaming"}; - case ScanTarget::HTTPServer: - return {"HTTPServer"}; - case ScanTarget::Icmp: - return {"Icmp"}; - case ScanTarget::ImageCore: - return {"ImageCore"}; - case ScanTarget::ImageWrapper: - return {"ImageWrapper"}; - case ScanTarget::ImageWriteQueue: - return {"ImageWriteQueue"}; - case ScanTarget::InputCore: - return {"InputCore"}; - case ScanTarget::InputDevice: - return {"InputDevice"}; - case ScanTarget::InstallBundleManager: - return {"InstallBundleManager"}; - case ScanTarget::InstancedSplines: - return {"InstancedSplines"}; - case ScanTarget::InteractiveToolsFramework: - return {"InteractiveToolsFramework"}; - case ScanTarget::Json: - return {"Json"}; - case ScanTarget::JsonUtilities: - return {"JsonUtilities"}; - case ScanTarget::Landscape: - return {"Landscape"}; - case ScanTarget::LauncherCheck: - return {"LauncherCheck"}; - case ScanTarget::LauncherPlatform: - return {"LauncherPlatform"}; - case ScanTarget::LevelSequence: - return {"LevelSequence"}; - case ScanTarget::LocalFileNetworkReplayStreaming: - return {"LocalFileNetworkReplayStreaming"}; - case ScanTarget::MaterialShaderQualitySettings: - return {"MaterialShaderQualitySettings"}; - case ScanTarget::Media: - return {"Media"}; - case ScanTarget::MediaAssets: - return {"MediaAssets"}; - case ScanTarget::MediaUtils: - return {"MediaUtils"}; - case ScanTarget::MeshDescription: - return {"MeshDescription"}; - case ScanTarget::MeshUtilitiesCommon: - return {"MeshUtilitiesCommon"}; - case ScanTarget::Messaging: - return {"Messaging"}; - case ScanTarget::MessagingCommon: - return {"MessagingCommon"}; - case ScanTarget::MoviePlayer: - return {"MoviePlayer"}; - case ScanTarget::MovieScene: - return {"MovieScene"}; - case ScanTarget::MovieSceneCapture: - return {"MovieSceneCapture"}; - case ScanTarget::MovieSceneTracks: - return {"MovieSceneTracks"}; - case ScanTarget::MRMesh: - return {"MRMesh"}; - case ScanTarget::NavigationSystem: - return {"NavigationSystem"}; - case ScanTarget::Navmesh: - return {"Navmesh"}; - case ScanTarget::NetCore: - return {"NetCore"}; - case ScanTarget::Networking: - return {"Networking"}; - case ScanTarget::NetworkReplayStreaming: - return {"NetworkReplayStreaming"}; - case ScanTarget::NonRealtimeAudioRenderer: - return {"NonRealtimeAudioRenderer"}; - case ScanTarget::NullDrv: - return {"NullDrv"}; - case ScanTarget::NullNetworkReplayStreaming: - return {"NullNetworkReplayStreaming"}; - case ScanTarget::OpenGLDrv: - return {"OpenGLDrv"}; - case ScanTarget::Overlay: - return {"Overlay"}; - case ScanTarget::PacketHandler: - return {"PacketHandler"}; - case ScanTarget::PakFile: - return {"PakFile"}; - case ScanTarget::PerfCounters: - return {"PerfCounters"}; - case ScanTarget::PhysicsCore: - return {"PhysicsCore"}; - case ScanTarget::PhysicsSQ: - return {"PhysicsSQ"}; - case ScanTarget::PhysXCooking: - return {"PhysXCooking"}; - case ScanTarget::PreLoadScreen: - return {"PreLoadScreen"}; - case ScanTarget::Projects: - return {"Projects"}; - case ScanTarget::PropertyPath: - return {"PropertyPath"}; - case ScanTarget::RawMesh: - return {"RawMesh"}; - case ScanTarget::ReliabilityHandlerComponent: - return {"ReliabilityHandlerComponent"}; - case ScanTarget::RenderCore: - return {"RenderCore"}; - case ScanTarget::Renderer: - return {"Renderer"}; - case ScanTarget::RHI: - return {"RHI"}; - case ScanTarget::RSA: - return {"RSA"}; - case ScanTarget::SandboxFile: - return {"SandboxFile"}; - case ScanTarget::Serialization: - return {"Serialization"}; - case ScanTarget::SessionMessages: - return {"SessionMessages"}; - case ScanTarget::SessionServices: - return {"SessionServices"}; - case ScanTarget::SignalProcessing: - return {"SignalProcessing"}; - case ScanTarget::Slate: - return {"Slate"}; - case ScanTarget::SlateCore: - return {"SlateCore"}; - case ScanTarget::SlateNullRenderer: - return {"SlateNullRenderer"}; - case ScanTarget::SlateRHIRenderer: - return {"SlateRHIRenderer"}; - case ScanTarget::Sockets: - return {"Sockets"}; - case ScanTarget::SoundFieldRendering: - return {"SoundFieldRendering"}; - case ScanTarget::SSL: - return {"SSL"}; - case ScanTarget::StaticMeshDescription: - return {"StaticMeshDescription"}; - case ScanTarget::StreamingPauseRendering: - return {"StreamingPauseRendering"}; - case ScanTarget::SynthBenchmark: - return {"SynthBenchmark"}; - case ScanTarget::TimeManagement: - return {"TimeManagement"}; - case ScanTarget::TraceLog: - return {"TraceLog"}; - case ScanTarget::UELibSampleRate: - return {"UELibSampleRate"}; - case ScanTarget::UMG: - return {"UMG"}; - case ScanTarget::VectorVM: - return {"VectorVM"}; - case ScanTarget::Voice: - return {"Voice"}; - case ScanTarget::Voronoi: - return {"Voronoi"}; - case ScanTarget::VulkanRHI: - return {"VulkanRHI"}; - case ScanTarget::WebBrowser: - return {"WebBrowser"}; - case ScanTarget::WindowsPlatformFeatures: - return {"WindowsPlatformFeatures"}; - case ScanTarget::XAudio2: - return {"XAudio2"}; - case ScanTarget::Max: - return {"Max"}; - } - - throw std::runtime_error{std::format("Invalid param for ScanTargetToString, param: {}", static_cast(scan_target))}; - } - - auto ScanTargetToString(size_t scan_target) -> std::string - { - return ScanTargetToString(static_cast(scan_target)); - } - - static auto ConvertHexCharToInt(char ch) -> int - { - if (ch >= '0' && ch <= '9') return ch - '0'; - if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10; - if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10; - return -1; - } - - auto SinglePassScanner::string_to_vector(std::string_view signature) -> std::vector - { - std::vector bytes; - char* const start = const_cast(signature.data()); - char* const end = const_cast(signature.data()) + strlen(signature.data()); - - for (char* current = start; current < end; current++) - { - if (*current == '?') - { - bytes.push_back(-1); - } - else if (std::isxdigit(*current)) - { - bytes.push_back(ConvertHexCharToInt(*current)); - } - } - return bytes; - } - - auto SinglePassScanner::string_to_vector(const std::vector& signatures) -> std::vector> + auto SinglePassScanner::get_system_info() -> SystemInfo { - std::vector> vector_of_signatures; - vector_of_signatures.reserve(signatures.size()); - - for (const auto& signature_data : signatures) - { - vector_of_signatures.emplace_back(string_to_vector(signature_data.signature)); - } - - return vector_of_signatures; + return SigScannerStaticData::m_modules_info[ScanTarget::MainExe]; } - struct PatternData - { - std::vector pattern{}; - std::vector mask{}; - SignatureContainer* signature_container{}; - }; - static auto CharToByte(char symbol) -> uint8_t - { - if (symbol >= 'a' && symbol <= 'z') - { - return symbol - 'a' + 0xA; - } - else if (symbol >= 'A' && symbol <= 'Z') - { - return symbol - 'A' + 0xA; + namespace Platform { + auto get_start_address(DLData &info) -> uint8_t* { + return info.base_address; } - else if (symbol >= '0' && symbol <= '9') - { - return symbol - '0'; - } - else - { - return 0; - } - } - static auto make_mask(std::string_view pattern, SignatureContainer& signature_container, const size_t data_size) -> PatternData - { - PatternData pattern_data{}; - - if (pattern.length() < 1 || pattern[0] == '?') - { - throw std::runtime_error{std::format("[make_mask] A pattern cannot start with a wildcard.\nPattern: {}", pattern)}; + auto get_end_address(DLData &info) -> uint8_t* { + return info.base_address + info.size; } - for (size_t i = 0; i < pattern.length(); i++) - { - char symbol = pattern[i]; - char next_symbol = ((i + 1) < pattern.length()) ? pattern[i + 1] : 0; - if (symbol == ' ') - { - continue; - } - - if (symbol == '?') - { - pattern_data.pattern.push_back(0x00); - pattern_data.mask.push_back(0x00); - - if (next_symbol == '?') - { - ++i; - } - continue; - } - - uint8_t byte = CharToByte(symbol) << 4 | CharToByte(next_symbol); - - pattern_data.pattern.push_back(byte); - pattern_data.mask.push_back(0xff); - - ++i; + auto get_module_size(DLData &info) -> uint32_t { + return info.size; } - static constexpr size_t Alignment = 32; - size_t count = (size_t)std::ceil((float)data_size / Alignment); - size_t padding_size = count * Alignment - data_size; - - for (size_t i = 0; i < padding_size; i++) - { - pattern_data.pattern.push_back(0x00); - pattern_data.mask.push_back(0x00); + auto get_module_base(DLData &info) -> uint8_t* { + return info.base_address; } + }; // namespace Platform - pattern_data.signature_container = &signature_container; - return pattern_data; + auto SinglePassScanner::string_scan(std::wstring_view string_to_scan_for, ScanTarget scan_target) -> void* { + throw std::runtime_error{"[SinglePassSigScanner::string_scan] Not implemented for Linux"}; } - auto SinglePassScanner::scanner_work_thread(uint8_t* start_address, uint8_t* end_address, DLData& info, std::vector& signature_containers) - -> void - { - ProfilerSetThreadName("UE4SS-ScannerWorkThread"); - ProfilerScope(); - - switch (m_scan_method) - { - case ScanMethod::Scalar: - scanner_work_thread_scalar(start_address, end_address, info, signature_containers); - break; - case ScanMethod::StdFind: - scanner_work_thread_stdfind(start_address, end_address, info, signature_containers); - break; - } - } + // This function may have problem becasue we're not checking the memory region's permissions auto SinglePassScanner::scanner_work_thread_scalar(uint8_t* start_address, uint8_t* end_address, DLData& info, @@ -537,8 +130,8 @@ namespace RC for (size_t sig_i = 0; sig_i < sig.size(); sig_i += 2) { - if (sig.at(sig_i) != -1 && sig.at(sig_i) != HI_NIBBLE(*(byte*)(region_start + (sig_i / 2))) || - sig.at(sig_i + 1) != -1 && sig.at(sig_i + 1) != LO_NIBBLE(*(byte*)(region_start + (sig_i / 2)))) + if (sig.at(sig_i) != -1 && sig.at(sig_i) != HI_NIBBLE(*(uint8_t*)(region_start + (sig_i / 2))) || + sig.at(sig_i + 1) != -1 && sig.at(sig_i + 1) != LO_NIBBLE(*(uint8_t*)(region_start + (sig_i / 2)))) { break; } @@ -593,231 +186,4 @@ namespace RC } } - - static auto format_aob_string(std::string& str) -> void - { - if (str.size() < 4) - { - return; - } - if (str[3] != '/') - { - return; - } - - std::erase_if(str, [&](const char c) { - return c == ' '; - }); - - std::ranges::transform(str, str.begin(), [&str](const char c) { - return c == '/' ? ' ' : c; - }); - } - - auto SinglePassScanner::format_aob_strings(std::vector& signature_containers) -> void - { - std::lock_guard safe_scope(m_scanner_mutex); - for (auto& signature_container : signature_containers) - { - for (auto& signature : signature_container.signatures) - { - format_aob_string(signature.signature); - } - } - } - - auto SinglePassScanner::scanner_work_thread_stdfind(uint8_t* start_address, - uint8_t* end_address, - DLData& info, - std::vector& signature_containers) -> void - { - ProfilerScope(); - - if (!start_address) - { - start_address = static_cast(info.base_address); - } - if (!end_address) - { - end_address = static_cast(info.base_address + info.size); - } - - format_aob_strings(signature_containers); - - std::vector> pattern_datas{}; - for (auto& signature_container : signature_containers) - { - auto& pattern_data = pattern_datas.emplace_back(); - for (auto& signature : signature_container.signatures) - { - pattern_data.emplace_back(make_mask(signature.signature, signature_container, end_address - start_address)); - } - } - - // Loop everything - for (size_t container_index = 0; const auto& patterns : pattern_datas) - { - for (size_t signature_index = 0; const auto& pattern_data : patterns) - { - // If the container is refusing more calls then skip to the next container - if (pattern_data.signature_container->ignore) - { - break; - } - - auto it = start_address; - auto end = it + (end_address - start_address) - (pattern_data.pattern.size()) + 1; - uint8_t needle = pattern_data.pattern[0]; - - bool skip_to_next_container{}; - while (end != (it = std::find(it, end, needle))) - { - bool found = true; - for (size_t pattern_offset = 0; pattern_offset < pattern_data.pattern.size(); ++pattern_offset) - { - if ((it[pattern_offset] & pattern_data.mask[pattern_offset]) != pattern_data.pattern[pattern_offset]) - { - found = false; - break; - } - } - - if (found) - { - { - std::lock_guard safe_scope(m_scanner_mutex); - - // Checking for the second time if the container is refusing more calls - // This is required when multi-threading is enabled - if (pattern_data.signature_container->ignore) - { - skip_to_next_container = true; - break; - } - - // One of the signatures have found a full match so lets forward the details to the callable - pattern_data.signature_container->index_into_signatures = signature_index; - pattern_data.signature_container->match_address = it; - pattern_data.signature_container->match_signature_size = pattern_data.pattern.size(); - - skip_to_next_container = pattern_data.signature_container->on_match_found(*pattern_data.signature_container); - pattern_data.signature_container->ignore = skip_to_next_container; - - // Store results if the container at the containers request - if (pattern_data.signature_container->store_results) - { - pattern_data.signature_container->result_store.emplace_back( - SignatureContainerLight{.index_into_signatures = signature_index, .match_address = it}); - } - } - } - - it++; - } - - if (skip_to_next_container) - { - // A match was found and signaled to skip to the next container - break; - } - - ++signature_index; - } - ++container_index; - } - } - - auto SinglePassScanner::start_scan(SignatureContainerMap& signature_containers) -> void - { - //DLData info = SigScannerStaticData::m_modules_info[ScanTarget::MainExe]; - - // If not modular then the containers get merged into one scan target - // That way there are no extra scans - // If modular then loop the containers and retrieve the scan target for each and pass everything to the do_scan() lambda - fprintf(stderr, "signature_containers.size() = %d\n", signature_containers.size()); - if (!SigScannerStaticData::m_is_modular) - { - DLData merged_module_info{}; - std::vector merged_containers; - - for (const auto& [scan_target, outer_container] : signature_containers) - { - merged_module_info = *std::bit_cast(&SigScannerStaticData::m_modules_info[scan_target]); - fprintf(stderr, "outer_container len = %d\n", outer_container.size()); - for (const auto& signature_container : outer_container) - { - merged_containers.emplace_back(signature_container); - } - } - - if (merged_containers.empty()) - { - fprintf(stderr, "No containers to merge\n"); - return ; - //throw std::runtime_error{"[SinglePassScanner::start_scan] Could not merge containers. Either there were not containers to merge or there was " - // "an internal error."}; - } - - uint8_t* module_start_address = static_cast(merged_module_info.base_address); - - if (merged_module_info.size >= m_multithreading_module_size_threshold) - { - // Module is large enough to make it overall faster to scan with multiple threads - std::vector> scan_threads; - - // Higher values are better for debugging - // You will get a bigger diminishing returns the faster your computer is (especially in release mode) - uint32_t range = merged_module_info.size / m_num_threads; - - // Calculating the ranges for each thread to scan and starting the scan - uint32_t last_range{}; - for (uint32_t thread_id = 0; thread_id < m_num_threads; ++thread_id) - { - scan_threads.emplace_back(std::async(std::launch::async, - &scanner_work_thread, - module_start_address + last_range, - module_start_address + last_range + range, - std::ref(merged_module_info), - std::ref(merged_containers))); - - last_range += range; - } - - for (const auto& scan_thread : scan_threads) - { - scan_thread.wait(); - } - } - else - { - // Module is too small to make it overall faster to scan with multiple threads - uint8_t* module_end_address = static_cast(module_start_address + merged_module_info.size); - scanner_work_thread(module_start_address, module_end_address, merged_module_info, merged_containers); - } - - for (auto& container : merged_containers) - { - container.on_scan_finished(container); - } - } - else - { - // This ranged for loop is performing a copy of unordered_map> - // Is this required ? Would it be worth trying to avoid copying here ? - // Right now it can't be auto& or const auto& because the do_scan function takes a non-const since it needs to mutate the values inside the vector - auto info = SigScannerStaticData::m_modules_info[ScanTarget::MainExe]; - for (auto& [scan_target, signature_container] : signature_containers) - { - uint8_t* module_start_address = static_cast(SigScannerStaticData::m_modules_info[scan_target].base_address); - uint8_t* module_end_address = static_cast(module_start_address + SigScannerStaticData::m_modules_info[scan_target].size); - - scanner_work_thread(module_start_address, module_end_address, info, signature_container); - - for (auto& container : signature_container) - { - container.on_scan_finished(container); - } - } - } - } } // namespace RC \ No newline at end of file diff --git a/deps/first/SinglePassSigScanner/src/SinglePassSigScannerWin32.cpp b/deps/first/SinglePassSigScanner/src/SinglePassSigScannerWin32.cpp index 63ec115a0..381ca5a4a 100644 --- a/deps/first/SinglePassSigScanner/src/SinglePassSigScannerWin32.cpp +++ b/deps/first/SinglePassSigScanner/src/SinglePassSigScannerWin32.cpp @@ -16,358 +16,42 @@ namespace RC { - ScanTargetArray SigScannerStaticData::m_modules_info; - bool SigScannerStaticData::m_is_modular; - - uint32_t SinglePassScanner::m_num_threads = 8; - SinglePassScanner::ScanMethod SinglePassScanner::m_scan_method = ScanMethod::Scalar; - uint32_t SinglePassScanner::m_multithreading_module_size_threshold = 0x1000000; - std::mutex SinglePassScanner::m_scanner_mutex{}; -#ifdef WIN32 - auto WIN_MODULEINFO::operator=(MODULEINFO other) -> WIN_MODULEINFO& + + auto SinglePassScanner::get_system_info() -> SystemInfo { - lpBaseOfDll = other.lpBaseOfDll; - SizeOfImage = other.SizeOfImage; - EntryPoint = other.EntryPoint; - return *this; - } - - auto ScanTargetArray::operator[](ScanTarget index) -> MODULEINFO& - { - return *std::bit_cast(&array[static_cast(index)]); - } -#else - auto ScanTargetArray::operator[](ScanTarget index) -> DLData& - { - return array[static_cast(index)]; + SYSTEM_INFO info{}; + GetSystemInfo(&info); + return info; } -#endif - - auto ScanTargetToString(ScanTarget scan_target) -> std::string - { - switch (scan_target) - { - case ScanTarget::MainExe: - return {"MainExe"}; - case ScanTarget::AIModule: - return {"AIModule"}; - case ScanTarget::Analytics: - return {"Analytics"}; - case ScanTarget::AnalyticsET: - return {"AnalyticsET"}; - case ScanTarget::AnimationCore: - return {"AnimationCore"}; - case ScanTarget::AnimGraphRuntime: - return {"AnimGraphRuntime"}; - case ScanTarget::AppFramework: - return {"AppFramework"}; - case ScanTarget::ApplicationCore: - return {"ApplicationCore"}; - case ScanTarget::AssetRegistry: - return {"AssetRegistry"}; - case ScanTarget::AudioCaptureCore: - return {"AudioCaptureCore"}; - case ScanTarget::AudioCaptureRtAudio: - return {"AudioCaptureRtAudio"}; - case ScanTarget::AudioExtensions: - return {"AudioExtensions"}; - case ScanTarget::AudioMixer: - return {"AudioMixer"}; - case ScanTarget::AudioMixerCore: - return {"AudioMixerCore"}; - case ScanTarget::AudioMixerXAudio2: - return {"AudioMixerXAudio2"}; - case ScanTarget::AudioPlatformConfiguration: - return {"AudioPlatformConfiguration"}; - case ScanTarget::AugmentedReality: - return {"AugmentedReality"}; - case ScanTarget::AVEncoder: - return {"AVEncoder"}; - case ScanTarget::AVIWriter: - return {"AVIWriter"}; - case ScanTarget::BuildPatchServices: - return {"BuildPatchServices"}; - case ScanTarget::BuildSettings: - return {"BuildSettings"}; - case ScanTarget::Cbor: - return {"Cbor"}; - case ScanTarget::CEF3Utils: - return {"CEF3Utils"}; - case ScanTarget::Chaos: - return {"Chaos"}; - case ScanTarget::ChaosCore: - return {"ChaosCore"}; - case ScanTarget::ChaosSolverEngine: - return {"ChaosSolverEngine"}; - case ScanTarget::ChaosSolvers: - return {"ChaosSolvers"}; - case ScanTarget::CinematicCamera: - return {"CinematicCamera"}; - case ScanTarget::ClothingSystemRuntimeCommon: - return {"ClothingSystemRuntimeCommon"}; - case ScanTarget::ClothingSystemRuntimeInterface: - return {"ClothingSystemRuntimeInterface"}; - case ScanTarget::ClothingSystemRuntimeNv: - return {"ClothingSystemRuntimeNv"}; - case ScanTarget::Core: - return {"Core"}; - case ScanTarget::CoreUObject: - return {"CoreUObject"}; - case ScanTarget::CrunchCompression: - return {"CrunchCompression"}; - case ScanTarget::D3D11RHI: - return {"D3D11RHI"}; - case ScanTarget::D3D12RHI: - return {"D3D12RHI"}; - case ScanTarget::Engine: - return {"Engine"}; - case ScanTarget::EngineMessages: - return {"EngineMessages"}; - case ScanTarget::EngineSettings: - return {"EngineSettings"}; - case ScanTarget::EyeTracker: - return {"EyeTracker"}; - case ScanTarget::FieldSystemCore: - return {"FieldSystemCore"}; - case ScanTarget::FieldSystemEngine: - return {"FieldSystemEngine"}; - case ScanTarget::FieldSystemSimulationCore: - return {"FieldSystemSimulationCore"}; - case ScanTarget::Foliage: - return {"Foliage"}; - case ScanTarget::GameplayMediaEncoder: - return {"GameplayMediaEncoder"}; - case ScanTarget::GameplayTags: - return {"GameplayTags"}; - case ScanTarget::GameplayTasks: - return {"GameplayTasks"}; - case ScanTarget::GeometryCollectionCore: - return {"GeometryCollectionCore"}; - case ScanTarget::GeometryCollectionEngine: - return {"GeometryCollectionEngine"}; - case ScanTarget::GeometryCollectionSimulationCore: - return {"GeometryCollectionSimulationCore"}; - case ScanTarget::HeadMountedDisplay: - return {"HeadMountedDisplay"}; - case ScanTarget::HTTP: - return {"HTTP"}; - case ScanTarget::HttpNetworkReplayStreaming: - return {"HttpNetworkReplayStreaming"}; - case ScanTarget::HTTPServer: - return {"HTTPServer"}; - case ScanTarget::Icmp: - return {"Icmp"}; - case ScanTarget::ImageCore: - return {"ImageCore"}; - case ScanTarget::ImageWrapper: - return {"ImageWrapper"}; - case ScanTarget::ImageWriteQueue: - return {"ImageWriteQueue"}; - case ScanTarget::InputCore: - return {"InputCore"}; - case ScanTarget::InputDevice: - return {"InputDevice"}; - case ScanTarget::InstallBundleManager: - return {"InstallBundleManager"}; - case ScanTarget::InstancedSplines: - return {"InstancedSplines"}; - case ScanTarget::InteractiveToolsFramework: - return {"InteractiveToolsFramework"}; - case ScanTarget::Json: - return {"Json"}; - case ScanTarget::JsonUtilities: - return {"JsonUtilities"}; - case ScanTarget::Landscape: - return {"Landscape"}; - case ScanTarget::LauncherCheck: - return {"LauncherCheck"}; - case ScanTarget::LauncherPlatform: - return {"LauncherPlatform"}; - case ScanTarget::LevelSequence: - return {"LevelSequence"}; - case ScanTarget::LocalFileNetworkReplayStreaming: - return {"LocalFileNetworkReplayStreaming"}; - case ScanTarget::MaterialShaderQualitySettings: - return {"MaterialShaderQualitySettings"}; - case ScanTarget::Media: - return {"Media"}; - case ScanTarget::MediaAssets: - return {"MediaAssets"}; - case ScanTarget::MediaUtils: - return {"MediaUtils"}; - case ScanTarget::MeshDescription: - return {"MeshDescription"}; - case ScanTarget::MeshUtilitiesCommon: - return {"MeshUtilitiesCommon"}; - case ScanTarget::Messaging: - return {"Messaging"}; - case ScanTarget::MessagingCommon: - return {"MessagingCommon"}; - case ScanTarget::MoviePlayer: - return {"MoviePlayer"}; - case ScanTarget::MovieScene: - return {"MovieScene"}; - case ScanTarget::MovieSceneCapture: - return {"MovieSceneCapture"}; - case ScanTarget::MovieSceneTracks: - return {"MovieSceneTracks"}; - case ScanTarget::MRMesh: - return {"MRMesh"}; - case ScanTarget::NavigationSystem: - return {"NavigationSystem"}; - case ScanTarget::Navmesh: - return {"Navmesh"}; - case ScanTarget::NetCore: - return {"NetCore"}; - case ScanTarget::Networking: - return {"Networking"}; - case ScanTarget::NetworkReplayStreaming: - return {"NetworkReplayStreaming"}; - case ScanTarget::NonRealtimeAudioRenderer: - return {"NonRealtimeAudioRenderer"}; - case ScanTarget::NullDrv: - return {"NullDrv"}; - case ScanTarget::NullNetworkReplayStreaming: - return {"NullNetworkReplayStreaming"}; - case ScanTarget::OpenGLDrv: - return {"OpenGLDrv"}; - case ScanTarget::Overlay: - return {"Overlay"}; - case ScanTarget::PacketHandler: - return {"PacketHandler"}; - case ScanTarget::PakFile: - return {"PakFile"}; - case ScanTarget::PerfCounters: - return {"PerfCounters"}; - case ScanTarget::PhysicsCore: - return {"PhysicsCore"}; - case ScanTarget::PhysicsSQ: - return {"PhysicsSQ"}; - case ScanTarget::PhysXCooking: - return {"PhysXCooking"}; - case ScanTarget::PreLoadScreen: - return {"PreLoadScreen"}; - case ScanTarget::Projects: - return {"Projects"}; - case ScanTarget::PropertyPath: - return {"PropertyPath"}; - case ScanTarget::RawMesh: - return {"RawMesh"}; - case ScanTarget::ReliabilityHandlerComponent: - return {"ReliabilityHandlerComponent"}; - case ScanTarget::RenderCore: - return {"RenderCore"}; - case ScanTarget::Renderer: - return {"Renderer"}; - case ScanTarget::RHI: - return {"RHI"}; - case ScanTarget::RSA: - return {"RSA"}; - case ScanTarget::SandboxFile: - return {"SandboxFile"}; - case ScanTarget::Serialization: - return {"Serialization"}; - case ScanTarget::SessionMessages: - return {"SessionMessages"}; - case ScanTarget::SessionServices: - return {"SessionServices"}; - case ScanTarget::SignalProcessing: - return {"SignalProcessing"}; - case ScanTarget::Slate: - return {"Slate"}; - case ScanTarget::SlateCore: - return {"SlateCore"}; - case ScanTarget::SlateNullRenderer: - return {"SlateNullRenderer"}; - case ScanTarget::SlateRHIRenderer: - return {"SlateRHIRenderer"}; - case ScanTarget::Sockets: - return {"Sockets"}; - case ScanTarget::SoundFieldRendering: - return {"SoundFieldRendering"}; - case ScanTarget::SSL: - return {"SSL"}; - case ScanTarget::StaticMeshDescription: - return {"StaticMeshDescription"}; - case ScanTarget::StreamingPauseRendering: - return {"StreamingPauseRendering"}; - case ScanTarget::SynthBenchmark: - return {"SynthBenchmark"}; - case ScanTarget::TimeManagement: - return {"TimeManagement"}; - case ScanTarget::TraceLog: - return {"TraceLog"}; - case ScanTarget::UELibSampleRate: - return {"UELibSampleRate"}; - case ScanTarget::UMG: - return {"UMG"}; - case ScanTarget::VectorVM: - return {"VectorVM"}; - case ScanTarget::Voice: - return {"Voice"}; - case ScanTarget::Voronoi: - return {"Voronoi"}; - case ScanTarget::VulkanRHI: - return {"VulkanRHI"}; - case ScanTarget::WebBrowser: - return {"WebBrowser"}; - case ScanTarget::WindowsPlatformFeatures: - return {"WindowsPlatformFeatures"}; - case ScanTarget::XAudio2: - return {"XAudio2"}; - case ScanTarget::Max: - return {"Max"}; + namespace Platform { + // Get the start address of the system + auto get_start_address(SystemInfo &info) -> uint8_t* { + return static_cast(info.lpMinimumApplicationAddress); } - throw std::runtime_error{std::format("Invalid param for ScanTargetToString, param: {}", static_cast(scan_target))}; - } - - auto ScanTargetToString(size_t scan_target) -> std::string - { - return ScanTargetToString(static_cast(scan_target)); - } - - static auto ConvertHexCharToInt(char ch) -> int - { - if (ch >= '0' && ch <= '9') return ch - '0'; - if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10; - if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10; - return -1; - } - - auto SinglePassScanner::string_to_vector(std::string_view signature) -> std::vector - { - std::vector bytes; - char* const start = const_cast(signature.data()); - char* const end = const_cast(signature.data()) + strlen(signature.data()); - - for (char* current = start; current < end; current++) - { - if (*current == '?') - { - bytes.push_back(-1); - } - else if (std::isxdigit(*current)) - { - bytes.push_back(ConvertHexCharToInt(*current)); - } + // Get the end address of the system + auto get_end_address(SystemInfo &info) -> uint8_t* { + return static_cast(info.lpMaximumApplicationAddress); } - return bytes; - } - - auto SinglePassScanner::string_to_vector(const std::vector& signatures) -> std::vector> - { - std::vector> vector_of_signatures; - vector_of_signatures.reserve(signatures.size()); - - for (const auto& signature_data : signatures) - { - vector_of_signatures.emplace_back(string_to_vector(signature_data.signature)); + // Get the size of the module + auto get_module_size(ModuleOS &info) -> size_t { + return info.SizeOfImage; } - return vector_of_signatures; + // Get the base address of the module + auto get_module_base(ModuleOS &info) -> uint8_t* { + return static_cast(info.lpBaseOfDll); + } + }; // namespace Platform + + auto WIN_MODULEINFO::operator=(MODULEINFO other) -> WIN_MODULEINFO& + { + lpBaseOfDll = other.lpBaseOfDll; + SizeOfImage = other.SizeOfImage; + EntryPoint = other.EntryPoint; + return *this; } auto SinglePassScanner::string_scan(std::wstring_view string_to_scan_for, ScanTarget scan_target) -> void* @@ -420,102 +104,6 @@ namespace RC return address_found; } - struct PatternData - { - std::vector pattern{}; - std::vector mask{}; - SignatureContainer* signature_container{}; - }; - - static auto CharToByte(char symbol) -> uint8_t - { - if (symbol >= 'a' && symbol <= 'z') - { - return symbol - 'a' + 0xA; - } - else if (symbol >= 'A' && symbol <= 'Z') - { - return symbol - 'A' + 0xA; - } - else if (symbol >= '0' && symbol <= '9') - { - return symbol - '0'; - } - else - { - return 0; - } - } - - static auto make_mask(std::string_view pattern, SignatureContainer& signature_container, const size_t data_size) -> PatternData - { - PatternData pattern_data{}; - - if (pattern.length() < 1 || pattern[0] == '?') - { - throw std::runtime_error{std::format("[make_mask] A pattern cannot start with a wildcard.\nPattern: {}", pattern)}; - } - - for (size_t i = 0; i < pattern.length(); i++) - { - char symbol = pattern[i]; - char next_symbol = ((i + 1) < pattern.length()) ? pattern[i + 1] : 0; - if (symbol == ' ') - { - continue; - } - - if (symbol == '?') - { - pattern_data.pattern.push_back(0x00); - pattern_data.mask.push_back(0x00); - - if (next_symbol == '?') - { - ++i; - } - continue; - } - - uint8_t byte = CharToByte(symbol) << 4 | CharToByte(next_symbol); - - pattern_data.pattern.push_back(byte); - pattern_data.mask.push_back(0xff); - - ++i; - } - - static constexpr size_t Alignment = 32; - size_t count = (size_t)std::ceil((float)data_size / Alignment); - size_t padding_size = count * Alignment - data_size; - - for (size_t i = 0; i < padding_size; i++) - { - pattern_data.pattern.push_back(0x00); - pattern_data.mask.push_back(0x00); - } - - pattern_data.signature_container = &signature_container; - return pattern_data; - } - - auto SinglePassScanner::scanner_work_thread(uint8_t* start_address, uint8_t* end_address, SYSTEM_INFO& info, std::vector& signature_containers) - -> void - { - ProfilerSetThreadName("UE4SS-ScannerWorkThread"); - ProfilerScope(); - - switch (m_scan_method) - { - case ScanMethod::Scalar: - scanner_work_thread_scalar(start_address, end_address, info, signature_containers); - break; - case ScanMethod::StdFind: - scanner_work_thread_stdfind(start_address, end_address, info, signature_containers); - break; - } - } - auto SinglePassScanner::scanner_work_thread_scalar(uint8_t* start_address, uint8_t* end_address, SYSTEM_INFO& info, @@ -663,228 +251,4 @@ namespace RC } } } - - static auto format_aob_string(std::string& str) -> void - { - if (str.size() < 4) - { - return; - } - if (str[3] != '/') - { - return; - } - - std::erase_if(str, [&](const char c) { - return c == ' '; - }); - - std::ranges::transform(str, str.begin(), [&str](const char c) { - return c == '/' ? ' ' : c; - }); - } - - auto SinglePassScanner::format_aob_strings(std::vector& signature_containers) -> void - { - std::lock_guard safe_scope(m_scanner_mutex); - for (auto& signature_container : signature_containers) - { - for (auto& signature : signature_container.signatures) - { - format_aob_string(signature.signature); - } - } - } - - auto SinglePassScanner::scanner_work_thread_stdfind(uint8_t* start_address, - uint8_t* end_address, - SYSTEM_INFO& info, - std::vector& signature_containers) -> void - { - ProfilerScope(); - - if (!start_address) - { - start_address = static_cast(info.lpMinimumApplicationAddress); - } - if (!end_address) - { - start_address = static_cast(info.lpMaximumApplicationAddress); - } - - format_aob_strings(signature_containers); - - std::vector> pattern_datas{}; - for (auto& signature_container : signature_containers) - { - auto& pattern_data = pattern_datas.emplace_back(); - for (auto& signature : signature_container.signatures) - { - pattern_data.emplace_back(make_mask(signature.signature, signature_container, end_address - start_address)); - } - } - - // Loop everything - for (size_t container_index = 0; const auto& patterns : pattern_datas) - { - for (size_t signature_index = 0; const auto& pattern_data : patterns) - { - // If the container is refusing more calls then skip to the next container - if (pattern_data.signature_container->ignore) - { - break; - } - - auto it = start_address; - auto end = it + (end_address - start_address) - (pattern_data.pattern.size()) + 1; - uint8_t needle = pattern_data.pattern[0]; - - bool skip_to_next_container{}; - while (end != (it = std::find(it, end, needle))) - { - bool found = true; - for (size_t pattern_offset = 0; pattern_offset < pattern_data.pattern.size(); ++pattern_offset) - { - if ((it[pattern_offset] & pattern_data.mask[pattern_offset]) != pattern_data.pattern[pattern_offset]) - { - found = false; - break; - } - } - - if (found) - { - { - std::lock_guard safe_scope(m_scanner_mutex); - - // Checking for the second time if the container is refusing more calls - // This is required when multi-threading is enabled - if (pattern_data.signature_container->ignore) - { - skip_to_next_container = true; - break; - } - - // One of the signatures have found a full match so lets forward the details to the callable - pattern_data.signature_container->index_into_signatures = signature_index; - pattern_data.signature_container->match_address = it; - pattern_data.signature_container->match_signature_size = pattern_data.pattern.size(); - - skip_to_next_container = pattern_data.signature_container->on_match_found(*pattern_data.signature_container); - pattern_data.signature_container->ignore = skip_to_next_container; - - // Store results if the container at the containers request - if (pattern_data.signature_container->store_results) - { - pattern_data.signature_container->result_store.emplace_back( - SignatureContainerLight{.index_into_signatures = signature_index, .match_address = it}); - } - } - } - - it++; - } - - if (skip_to_next_container) - { - // A match was found and signaled to skip to the next container - break; - } - - ++signature_index; - } - ++container_index; - } - } - - auto SinglePassScanner::start_scan(SignatureContainerMap& signature_containers) -> void - { - SYSTEM_INFO info{}; - GetSystemInfo(&info); - - // If not modular then the containers get merged into one scan target - // That way there are no extra scans - // If modular then loop the containers and retrieve the scan target for each and pass everything to the do_scan() lambda - - if (!SigScannerStaticData::m_is_modular) - { - MODULEINFO merged_module_info{}; - std::vector merged_containers; - - for (const auto& [scan_target, outer_container] : signature_containers) - { - merged_module_info = *std::bit_cast(&SigScannerStaticData::m_modules_info[scan_target]); - for (const auto& signature_container : outer_container) - { - merged_containers.emplace_back(signature_container); - } - } - - if (merged_containers.empty()) - { - throw std::runtime_error{"[SinglePassScanner::start_scan] Could not merge containers. Either there were not containers to merge or there was " - "an internal error."}; - } - - uint8_t* module_start_address = static_cast(merged_module_info.lpBaseOfDll); - - if (merged_module_info.SizeOfImage >= m_multithreading_module_size_threshold) - { - // Module is large enough to make it overall faster to scan with multiple threads - std::vector> scan_threads; - - // Higher values are better for debugging - // You will get a bigger diminishing returns the faster your computer is (especially in release mode) - uint32_t range = merged_module_info.SizeOfImage / m_num_threads; - - // Calculating the ranges for each thread to scan and starting the scan - uint32_t last_range{}; - for (uint32_t thread_id = 0; thread_id < m_num_threads; ++thread_id) - { - scan_threads.emplace_back(std::async(std::launch::async, - &scanner_work_thread, - module_start_address + last_range, - module_start_address + last_range + range, - std::ref(info), - std::ref(merged_containers))); - - last_range += range; - } - - for (const auto& scan_thread : scan_threads) - { - scan_thread.wait(); - } - } - else - { - // Module is too small to make it overall faster to scan with multiple threads - uint8_t* module_end_address = static_cast(module_start_address + merged_module_info.SizeOfImage); - scanner_work_thread(module_start_address, module_end_address, info, merged_containers); - } - - for (auto& container : merged_containers) - { - container.on_scan_finished(container); - } - } - else - { - // This ranged for loop is performing a copy of unordered_map> - // Is this required ? Would it be worth trying to avoid copying here ? - // Right now it can't be auto& or const auto& because the do_scan function takes a non-const since it needs to mutate the values inside the vector - for (auto& [scan_target, signature_container] : signature_containers) - { - uint8_t* module_start_address = static_cast(SigScannerStaticData::m_modules_info[scan_target].lpBaseOfDll); - uint8_t* module_end_address = static_cast(module_start_address + SigScannerStaticData::m_modules_info[scan_target].SizeOfImage); - - scanner_work_thread(module_start_address, module_end_address, info, signature_container); - - for (auto& container : signature_container) - { - container.on_scan_finished(container); - } - } - } - } } // namespace RC \ No newline at end of file diff --git a/deps/first/Unreal b/deps/first/Unreal index 692093b15..b7b9d2176 160000 --- a/deps/first/Unreal +++ b/deps/first/Unreal @@ -1 +1 @@ -Subproject commit 692093b157e51a226e06bdf60a7167a0077c6979 +Subproject commit b7b9d2176a23b251928c48dd8b1b6336607ab056