diff --git a/vmicore/.clang-format b/vmicore/.clang-format index 86bf28dc..c68bd8a0 100644 --- a/vmicore/.clang-format +++ b/vmicore/.clang-format @@ -18,4 +18,4 @@ AllowShortEnumsOnASingleLine: false AllowShortFunctionsOnASingleLine: Empty AllowShortIfStatementsOnASingleLine: Never AllowShortLoopsOnASingleLine: false -IndentCaseLabels: true \ No newline at end of file +IndentCaseLabels: true diff --git a/vmicore/CMakeLists.txt b/vmicore/CMakeLists.txt index feb8fb8c..fe52867e 100644 --- a/vmicore/CMakeLists.txt +++ b/vmicore/CMakeLists.txt @@ -1,6 +1,16 @@ cmake_minimum_required(VERSION 3.16) project(vmicore) +set(X86_64 OFF) +set(ARM64 OFF) + +if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64") + set(X86_64 ON) +elseif (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64") + set(ARM64 ON) +else () + message(FATAL_ERROR "Unknown architecture ${CMAKE_SYSTEM_PROCESSOR}") +endif () # Options @@ -15,7 +25,11 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(core_compile_flags -m64 -Wall) +if (${X86_64}) + set(core_compile_flags -m64 -Wall) +elseif (${ARM64}) + set(core_compile_flags -march=armv8-a -Wall) +endif () set(extra_compile_flags -Wunused -Wunreachable-code -Wextra -Wpedantic -Wno-dollar-in-identifier-extension) # Toolchain checks @@ -64,26 +78,40 @@ FetchContent_MakeAvailable(googletest) # Setup libvmi -FetchContent_Declare( - libvmi - GIT_REPOSITORY https://github.com/GDATASoftwareAG/libvmi - GIT_TAG test -) -set(libvmi_ENABLE_STATIC OFF) -set(libvmi_BUILD_EXAMPLES OFF) +set(ENABLE_STATIC OFF CACHE INTERNAL "") +set(BUILD_EXAMPLES OFF CACHE INTERNAL "") +set(ENABLE_VMIFS OFF CACHE INTERNAL "") +set(ENABLE_FILE OFF CACHE INTERNAL "") +set(ENABLE_BAREFLANK OFF CACHE INTERNAL "") +set(ENABLE_PROFILES OFF CACHE INTERNAL "") +set(ENABLE_TESTING OFF CACHE INTERNAL "") + +if (${X86_64}) + FetchContent_Declare( + libvmi + GIT_REPOSITORY https://github.com/GDATASoftwareAG/libvmi + GIT_TAG test + ) +elseif (${ARM64}) + FetchContent_Declare( + libvmi + GIT_REPOSITORY https://gitlab.sec.uni-passau.de/sis/vmi-on-arm/libvmi.git + GIT_TAG master + ) +endif () FetchContent_MakeAvailable(libvmi) include_directories(BEFORE SYSTEM ${libvmi_SOURCE_DIR}) # Setup yaml-cpp +set(YAML_BUILD_SHARED_LIBS OFF CACHE INTERNAL "") +set(YAML_CPP_BUILD_TOOLS OFF CACHE INTERNAL "") FetchContent_Declare( yaml-cpp GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git GIT_TAG yaml-cpp-0.7.0 ) -set(yaml-cpp_YAML_BUILD_SHARED_LIBS OFF) -set(yaml-cpp_YAML_CPP_BUILD_TOOLS OFF) FetchContent_MakeAvailable(yaml-cpp) set_property(TARGET yaml-cpp PROPERTY POSITION_INDEPENDENT_CODE TRUE) @@ -164,6 +192,9 @@ set(test_files test/vmi/LibvmiInterface_UnitTest.cpp test/vmi/SingleStepSupervisor_UnitTest.cpp) +configure_file(src/config.h.in ${PROJECT_BINARY_DIR}/config.h) +include_directories(${PROJECT_BINARY_DIR}) + # Link libraries set(libraries rust_grpc_server vmicore_public_headers vmi_shared dl yaml-cpp GSL fmt-header-only) diff --git a/vmicore/src/VmiHub.cpp b/vmicore/src/VmiHub.cpp index 49ae5c9e..d5c320c8 100644 --- a/vmicore/src/VmiHub.cpp +++ b/vmicore/src/VmiHub.cpp @@ -151,6 +151,9 @@ uint VmiHub::run(const std::unordered_map> } case VMI_OS_WINDOWS: { +#if defined(ARM64) + throw new std::runtime_error("No support for Windows on ARM yet."); +#endif auto kernelObjectExtractor = std::make_shared(vmiInterface); activeProcessesSupervisor = std::make_shared( vmiInterface, kernelObjectExtractor, loggingLib, eventStream); diff --git a/vmicore/src/config.h.in b/vmicore/src/config.h.in new file mode 100644 index 00000000..3a9ad430 --- /dev/null +++ b/vmicore/src/config.h.in @@ -0,0 +1,4 @@ + +#cmakedefine ARM64 +#cmakedefine X86_64 + diff --git a/vmicore/src/os/linux/SystemEventSupervisor.cpp b/vmicore/src/os/linux/SystemEventSupervisor.cpp index e1ab6f88..54402193 100644 --- a/vmicore/src/os/linux/SystemEventSupervisor.cpp +++ b/vmicore/src/os/linux/SystemEventSupervisor.cpp @@ -1,6 +1,7 @@ #include "SystemEventSupervisor.h" #include "../../GlobalControl.h" #include "Constants.h" +#include #include #include @@ -74,20 +75,33 @@ namespace Linux InterruptEvent::InterruptResponse SystemEventSupervisor::procForkConnectorCallback(InterruptEvent& interruptEvent) { - activeProcessesSupervisor->addNewProcess(interruptEvent.getRdi()); +#if defined(X86_64) + uint64_t base = interruptEvent.getRegisters()->x86.rdi; +#elif defined(ARM64) + uint64_t base = interruptEvent.getRegisters()->arm.regs[0]; +#endif + activeProcessesSupervisor->addNewProcess(base); return InterruptEvent::InterruptResponse::Continue; } InterruptEvent::InterruptResponse SystemEventSupervisor::procExecConnectorCallback(InterruptEvent& interruptEvent) { - activeProcessesSupervisor->removeActiveProcess(interruptEvent.getRdi()); - activeProcessesSupervisor->addNewProcess(interruptEvent.getRdi()); +#if defined(X86_64) + uint64_t base = interruptEvent.getRegisters()->x86.rdi; +#elif defined(ARM64) + uint64_t base = interruptEvent.getRegisters()->arm.regs[0]; +#endif + activeProcessesSupervisor->addNewProcess(base); return InterruptEvent::InterruptResponse::Continue; } InterruptEvent::InterruptResponse SystemEventSupervisor::procExitConnectorCallback(InterruptEvent& interruptEvent) { - auto taskStructBase = interruptEvent.getRdi(); +#if defined(X86_64) + uint64_t taskStructBase = interruptEvent.getRegisters()->x86.rdi; +#elif defined(ARM64) + uint64_t taskStructBase = interruptEvent.getRegisters()->arm.regs[0]; +#endif pluginSystem->passProcessTerminationEventToRegisteredPlugins( activeProcessesSupervisor->getProcessInformationByBase(taskStructBase)); diff --git a/vmicore/src/os/windows/SystemEventSupervisor.cpp b/vmicore/src/os/windows/SystemEventSupervisor.cpp index c2af46f6..2ec0aae2 100644 --- a/vmicore/src/os/windows/SystemEventSupervisor.cpp +++ b/vmicore/src/os/windows/SystemEventSupervisor.cpp @@ -65,8 +65,9 @@ namespace Windows InterruptEvent::InterruptResponse SystemEventSupervisor::pspCallProcessNotifyRoutinesCallback(InterruptEvent& interruptEvent) { - auto eprocessBase = interruptEvent.getRcx(); - bool isTerminationEvent = interruptEvent.getR8() == 0; + auto& regs = interruptEvent.getRegisters()->x86; + auto eprocessBase = regs.rcx; + bool isTerminationEvent = regs.r8 == 0; logger->debug(fmt::format("{} called", __func__), { logfield::create("_EPROCESS_base ", fmt::format("{:#x}", eprocessBase)), @@ -94,7 +95,7 @@ namespace Windows InterruptEvent::InterruptResponse SystemEventSupervisor::keBugCheckExCallback(InterruptEvent& interruptEvent) { - auto bugCheckCode = interruptEvent.getRcx(); + auto bugCheckCode = interruptEvent.getRegisters()->x86.rcx; eventStream->sendBSODEvent(static_cast(bugCheckCode)); logger->warning("BSOD detected!", {logfield::create("BugCheckCode", fmt::format("{:#x}", bugCheckCode))}); GlobalControl::endVmi = true; diff --git a/vmicore/src/vmi/InterruptEvent.cpp b/vmicore/src/vmi/InterruptEvent.cpp index 651f38ab..bed018e9 100644 --- a/vmicore/src/vmi/InterruptEvent.cpp +++ b/vmicore/src/vmi/InterruptEvent.cpp @@ -69,7 +69,9 @@ void InterruptEvent::initialize() void InterruptEvent::teardown() { disableEvent(); - interruptGuard->teardown(); + + if (interruptGuard) + interruptGuard->teardown(); } InterruptEvent::~InterruptEvent() @@ -89,33 +91,32 @@ void InterruptEvent::setupVmiInterruptEvent() void InterruptEvent::enableEvent() { +#if defined(X86_64) vmiInterface->write8PA(targetPA, INT3_BREAKPOINT); +#elif defined(ARM64) + vmiInterface->write32PA(targetPA, BRK64_BREAKPOINT); +#endif logger->debug("Enabled interrupt event", {logfield::create("targetPA", targetPAString)}); } void InterruptEvent::disableEvent() { +#if defined(X86_64) vmiInterface->write8PA(targetPA, originalValue); +#elif defined(ARM64) + vmiInterface->write32PA(targetPA, originalValue); +#endif logger->debug("Disabled interrupt event", {logfield::create("targetPA", targetPAString)}); } -uint64_t InterruptEvent::getRcx() -{ - return event.x86_regs->rcx; -} - -uint64_t InterruptEvent::getRdi() -{ - return event.x86_regs->rdi; -} - -uint64_t InterruptEvent::getR8() +registers_t* InterruptEvent::getRegisters() { - return event.x86_regs->r8; + return (registers_t*)event.x86_regs; } void InterruptEvent::storeOriginalValue() { +#if defined(X86_64) originalValue = vmiInterface->read8PA(targetPA); logger->debug("Save original value", {logfield::create("targetPA", targetPAString), @@ -125,16 +126,34 @@ void InterruptEvent::storeOriginalValue() throw VmiException(fmt::format( "{}: InterruptEvent originalValue @ {} is already an INT3 breakpoint.", __func__, targetPAString)); } +#elif defined(ARM64) + originalValue = vmiInterface->read32PA(targetPA); + logger->debug("Save original value", + {logfield::create("targetPA", targetPAString), + logfield::create("originalValue", fmt::format("{:#x}", originalValue))}); + if (originalValue == BRK64_BREAKPOINT) + { + throw VmiException(fmt::format( + "{}: InterruptEvent originalValue @ {} is already an BRK64 breakpoint.", __func__, targetPAString)); + } +#endif } -event_response_t InterruptEvent::_defaultInterruptCallback(__attribute__((unused)) vmi_instance_t vmi, - vmi_event_t* event) +event_response_t InterruptEvent::_defaultInterruptCallback(vmi_instance_t vmi, vmi_event_t* event) { auto eventResponse = VMI_EVENT_RESPONSE_NONE; try { +#if defined(X86_64) + (void)(vmi); auto eventPA = (event->interrupt_event.gfn << PagingDefinitions::numberOfPageIndexBits) + event->interrupt_event.offset; +#elif defined(ARM64) + addr_t eventPA; + if (VMI_SUCCESS != + vmi_pagetable_lookup(vmi, event->arm_regs->ttbr1 & VMI_BIT_MASK(12, 47), event->arm_regs->pc, &eventPA)) + throw std::runtime_error("Failed address translation of breakpoint hit."); +#endif auto interruptEventIterator = interruptsByPA.find(eventPA); if (interruptEventIterator != interruptsByPA.end()) { diff --git a/vmicore/src/vmi/InterruptEvent.h b/vmicore/src/vmi/InterruptEvent.h index 1a6b76fd..ae7c40ae 100644 --- a/vmicore/src/vmi/InterruptEvent.h +++ b/vmicore/src/vmi/InterruptEvent.h @@ -5,11 +5,13 @@ #include "Event.h" #include "InterruptGuard.h" #include "SingleStepSupervisor.h" +#include #include #define DONT_REINJECT_INTERRUPT 0 #define REINJECT_INTERRUPT 1 #define INT3_BREAKPOINT 0xCC +#define BRK64_BREAKPOINT 0xD4200000 class InterruptEvent final : public Event, public std::enable_shared_from_this { @@ -33,11 +35,7 @@ class InterruptEvent final : public Event, public std::enable_shared_from_this interruptGuard; std::function callbackFunction; std::function singleStepCallbackFunction; - uint8_t originalValue = 0; std::string targetPAString; +#if defined(X86_64) + uint8_t originalValue = 0; +#elif defined(ARM64) + uint32_t originalValue = 0; +#endif + void enableEvent() override; void disableEvent() override; diff --git a/vmicore/src/vmi/InterruptFactory.cpp b/vmicore/src/vmi/InterruptFactory.cpp index 706c2ecc..9984cb29 100644 --- a/vmicore/src/vmi/InterruptFactory.cpp +++ b/vmicore/src/vmi/InterruptFactory.cpp @@ -1,6 +1,7 @@ #include "InterruptFactory.h" #include "../io/grpc/GRPCLogger.h" #include "InterruptGuard.h" +#include #include InterruptFactory::InterruptFactory(std::shared_ptr vmiInterface, @@ -33,6 +34,7 @@ std::shared_ptr InterruptFactory::createInterruptEvent( auto targetPA = vmiInterface->convertVAToPA(targetVA, systemCr3); +#if defined(X86_64) auto interruptGuard = std::make_unique( vmiInterface, loggingLib->newNamedLogger(interruptName), targetVA, targetPA, systemCr3); @@ -44,6 +46,14 @@ std::shared_ptr InterruptFactory::createInterruptEvent( std::move(interruptGuard), callbackFunction, loggingLib->newNamedLogger(interruptName)); +#elif defined(ARM64) + auto interruptEvent = std::make_shared(vmiInterface, + targetPA, + singleStepSupervisor, + nullptr, + callbackFunction, + loggingLib->newNamedLogger(interruptName)); +#endif interruptEvent->initialize(); return interruptEvent; diff --git a/vmicore/src/vmi/LibvmiInterface.cpp b/vmicore/src/vmi/LibvmiInterface.cpp index be120f7d..32425806 100644 --- a/vmicore/src/vmi/LibvmiInterface.cpp +++ b/vmicore/src/vmi/LibvmiInterface.cpp @@ -90,6 +90,18 @@ uint8_t LibvmiInterface::read8PA(const uint64_t physicalAddress) return extractedValue; } +uint32_t LibvmiInterface::read32PA(const uint64_t physicalAddress) +{ + uint32_t extractedValue = 0; + auto accessContext = createPhysicalAddressAccessContext(physicalAddress); + std::lock_guard lock(libvmiLock); + if (vmi_read_32(vmiInstance, &accessContext, &extractedValue) == VMI_FAILURE) + { + throw VmiException(fmt::format("{}: Unable to read four byte from PA: {:#x}", __func__, physicalAddress)); + } + return extractedValue; +} + uint8_t LibvmiInterface::read8VA(const uint64_t virtualAddress, const uint64_t cr3) { uint8_t extractedValue = 0; @@ -147,6 +159,16 @@ void LibvmiInterface::write8PA(const uint64_t physicalAddress, uint8_t value) } } +void LibvmiInterface::write32PA(const uint64_t physicalAddress, uint32_t value) +{ + auto accessContext = createPhysicalAddressAccessContext(physicalAddress); + std::lock_guard lock(libvmiLock); + if (vmi_write_32(vmiInstance, &accessContext, &value) == VMI_FAILURE) + { + throw VmiException(fmt::format("{}: Unable to write {:#x} to PA {:#x}", __func__, value, physicalAddress)); + } +} + access_context_t LibvmiInterface::createPhysicalAddressAccessContext(uint64_t physicalAddress) { access_context_t accessContext{}; diff --git a/vmicore/src/vmi/LibvmiInterface.h b/vmicore/src/vmi/LibvmiInterface.h index 531825ca..a4c0433d 100644 --- a/vmicore/src/vmi/LibvmiInterface.h +++ b/vmicore/src/vmi/LibvmiInterface.h @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -33,6 +32,8 @@ class ILibvmiInterface virtual uint8_t read8PA(uint64_t pyhsicalAddress) = 0; + virtual uint32_t read32PA(uint64_t pyhsicalAddress) = 0; + virtual uint8_t read8VA(const uint64_t virtualAddress, const uint64_t cr3) = 0; virtual uint32_t read32VA(uint64_t virtualAddress, uint64_t cr3) = 0; @@ -43,6 +44,8 @@ class ILibvmiInterface virtual void write8PA(uint64_t physicalAddress, uint8_t value) = 0; + virtual void write32PA(uint64_t physicalAddress, uint32_t value) = 0; + virtual void waitForEvent() = 0; virtual void registerEvent(vmi_event_t& event) = 0; @@ -107,6 +110,8 @@ class LibvmiInterface : public ILibvmiInterface uint8_t read8PA(uint64_t pyhsicalAddress) override; + uint32_t read32PA(uint64_t pyhsicalAddress) override; + uint8_t read8VA(const uint64_t virtualAddress, const uint64_t cr3) override; uint32_t read32VA(uint64_t virtualAddress, uint64_t cr3) override; @@ -117,6 +122,8 @@ class LibvmiInterface : public ILibvmiInterface void write8PA(uint64_t physicalAddress, uint8_t value) override; + void write32PA(uint64_t physicalAddress, uint32_t value) override; + void waitForEvent() override; void registerEvent(vmi_event_t& event) override; diff --git a/vmicore/test/vmi/mock_LibvmiInterface.h b/vmicore/test/vmi/mock_LibvmiInterface.h index 9eaa0656..68011b7d 100644 --- a/vmicore/test/vmi/mock_LibvmiInterface.h +++ b/vmicore/test/vmi/mock_LibvmiInterface.h @@ -13,6 +13,8 @@ class MockLibvmiInterface : public ILibvmiInterface MOCK_METHOD(uint8_t, read8PA, (const uint64_t pyhsicalAddress), (override)); + MOCK_METHOD(uint32_t, read32PA, (const uint64_t pyhsicalAddress), (override)); + MOCK_METHOD(uint8_t, read8VA, (const uint64_t virtualAddress, const uint64_t cr3), (override)); MOCK_METHOD(uint32_t, read32VA, (const uint64_t virtualAddress, const uint64_t cr3), (override)); @@ -26,6 +28,8 @@ class MockLibvmiInterface : public ILibvmiInterface MOCK_METHOD(void, write8PA, (const uint64_t physicalAddress, const uint8_t value), (override)); + MOCK_METHOD(void, write32PA, (const uint64_t physicalAddress, const uint32_t value), (override)); + MOCK_METHOD(void, waitForEvent, (), (override)); MOCK_METHOD(void, registerEvent, (vmi_event_t & event), (override));