Skip to content

Commit

Permalink
Initial implementation of ARMv8 support
Browse files Browse the repository at this point in the history
  • Loading branch information
Thomas Dangl committed Sep 13, 2022
1 parent ea232b1 commit fe8df8d
Show file tree
Hide file tree
Showing 12 changed files with 158 additions and 40 deletions.
2 changes: 1 addition & 1 deletion vmicore/.clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
IndentCaseLabels: true
IndentCaseLabels: true
51 changes: 41 additions & 10 deletions vmicore/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions vmicore/src/VmiHub.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ uint VmiHub::run(const std::unordered_map<std::string, std::vector<std::string>>
}
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<Windows::KernelAccess>(vmiInterface);
activeProcessesSupervisor = std::make_shared<Windows::ActiveProcessesSupervisor>(
vmiInterface, kernelObjectExtractor, loggingLib, eventStream);
Expand Down
4 changes: 4 additions & 0 deletions vmicore/src/config.h.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

#cmakedefine ARM64
#cmakedefine X86_64

22 changes: 18 additions & 4 deletions vmicore/src/os/linux/SystemEventSupervisor.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "SystemEventSupervisor.h"
#include "../../GlobalControl.h"
#include "Constants.h"
#include <config.h>
#include <fmt/core.h>
#include <utility>

Expand Down Expand Up @@ -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));
Expand Down
7 changes: 4 additions & 3 deletions vmicore/src/os/windows/SystemEventSupervisor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
Expand Down Expand Up @@ -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<int64_t>(bugCheckCode));
logger->warning("BSOD detected!", {logfield::create("BugCheckCode", fmt::format("{:#x}", bugCheckCode))});
GlobalControl::endVmi = true;
Expand Down
49 changes: 34 additions & 15 deletions vmicore/src/vmi/InterruptEvent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ void InterruptEvent::initialize()
void InterruptEvent::teardown()
{
disableEvent();
interruptGuard->teardown();

if (interruptGuard)
interruptGuard->teardown();
}

InterruptEvent::~InterruptEvent()
Expand All @@ -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),
Expand All @@ -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())
{
Expand Down
15 changes: 9 additions & 6 deletions vmicore/src/vmi/InterruptEvent.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
#include "Event.h"
#include "InterruptGuard.h"
#include "SingleStepSupervisor.h"
#include <config.h>
#include <map>

#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<InterruptEvent>
{
Expand All @@ -33,11 +35,7 @@ class InterruptEvent final : public Event, public std::enable_shared_from_this<I

void teardown() override;

static uint64_t getRcx();

static uint64_t getRdi();

static uint64_t getR8();
static registers_t* getRegisters();

static void initializeInterruptEventHandling(ILibvmiInterface& vmiInterface);

Expand Down Expand Up @@ -68,9 +66,14 @@ class InterruptEvent final : public Event, public std::enable_shared_from_this<I
std::unique_ptr<InterruptGuard> interruptGuard;
std::function<InterruptResponse(InterruptEvent&)> callbackFunction;
std::function<void(vmi_event_t*)> 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;
Expand Down
10 changes: 10 additions & 0 deletions vmicore/src/vmi/InterruptFactory.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "InterruptFactory.h"
#include "../io/grpc/GRPCLogger.h"
#include "InterruptGuard.h"
#include <config.h>
#include <memory>

InterruptFactory::InterruptFactory(std::shared_ptr<ILibvmiInterface> vmiInterface,
Expand Down Expand Up @@ -33,6 +34,7 @@ std::shared_ptr<InterruptEvent> InterruptFactory::createInterruptEvent(

auto targetPA = vmiInterface->convertVAToPA(targetVA, systemCr3);

#if defined(X86_64)
auto interruptGuard = std::make_unique<InterruptGuard>(
vmiInterface, loggingLib->newNamedLogger(interruptName), targetVA, targetPA, systemCr3);

Expand All @@ -44,6 +46,14 @@ std::shared_ptr<InterruptEvent> InterruptFactory::createInterruptEvent(
std::move(interruptGuard),
callbackFunction,
loggingLib->newNamedLogger(interruptName));
#elif defined(ARM64)
auto interruptEvent = std::make_shared<InterruptEvent>(vmiInterface,
targetPA,
singleStepSupervisor,
nullptr,
callbackFunction,
loggingLib->newNamedLogger(interruptName));
#endif

interruptEvent->initialize();
return interruptEvent;
Expand Down
22 changes: 22 additions & 0 deletions vmicore/src/vmi/LibvmiInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::mutex> 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;
Expand Down Expand Up @@ -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<std::mutex> 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{};
Expand Down
Loading

0 comments on commit fe8df8d

Please sign in to comment.