Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial implementation of ARMv8 support #35

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 "")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer the scoped notation instead of internal cache vars.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those are configurable options and it would therefore be confusing if they were not displayed by ccmake.

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)
Copy link
Member

@rageagainsthepc rageagainsthepc Sep 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already add some definitions via add_definitions(). It would make sense to consolidate these two methods.

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.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
throw new std::runtime_error("No support for Windows on ARM yet.");
throw 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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is problematic since it breaks abstraction from libvmi types which was kinda the goal here. Afaik @K-Mayer and @cakeless are currently working on a PR that will expose the InterruptEvent to plugins.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the getRegisters() approach though. It would probably make sense to create an abstract IRegisters class and implement that. (yes, I know it's a lot of boiler plate, I am not happy about that either).

}

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))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer making the bitmask a named constant so it becomes clear what it is supposed to accomplish

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