Skip to content

Commit

Permalink
add microvmi plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
mtarral committed Oct 19, 2021
1 parent 542fe49 commit 342f34c
Show file tree
Hide file tree
Showing 6 changed files with 300 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
BasedOnStyle: LLVM
Language: Cpp
IndentWidth: 4
47 changes: 47 additions & 0 deletions .github/workflows/microvmi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: CI

on:
push:
branches:
- master
pull_request:

jobs:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

- name: clang-format
run: clang-format --style=file leechcore_device_microvmi/*.[ch]

lint:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2

- name: install latest libmicrovmi package
run: |
curl -s https://api.github.com/repos/Wenzel/libmicrovmi/releases/latest | jq -r '.assets[].browser_download_url' | wget -qi - -O microvmi.deb
sudo dpkg -i microvmi.deb
rm microvmi.deb
- name: install clang tools s12
run: sudo apt install clang-tools-12

- name: lint with clang static analyzer
run: scan-build-12 --status-bugs make -C leechcore_device_microvmi

build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

- name: install latest libmicrovmi package
run: |
curl -s https://api.github.com/repos/Wenzel/libmicrovmi/releases/latest | jq -r '.assets[].browser_download_url' | wget -qi - -O microvmi.deb
sudo dpkg -i microvmi.deb
rm microvmi.deb
- name: compile microvmi LeechCore device plugin
run: make -C leechcore_device_microvmi
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ all:
$(MAKE) -C leechcore_ft601_driver_linux
$(MAKE) -C leechcore_device_rawtcp
$(MAKE) -C leechcore_device_sp605tcp
$(MAKE) -C leechcore_device_microvmi

clean:
$(MAKE) -C leechcore_ft601_driver_linux clean
$(MAKE) -C leechcore_device_rawtcp clean
$(MAKE) -C leechcore_device_sp605tcp clean
$(MAKE) -C leechcore_device_microvmi clean
66 changes: 66 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ This repository contains various plugins for [LeechCore - Physical Memory Acquis

Plugins are related to various kinds of device drivers allowing for modular extensive memory acquisition in various scenarios.

## Table of Contents

- [leechcore_ft601_driver_linux](#leechcore_ft601_driver_linux)
- [leechcore_device_hvsavedstate](#leechcore_device_hvsavedstate)
- [leechcore_device_rawtcp](#leechcore_device_rawtcp)
- [leechcore_device_sp605tcp](#leechcore_device_sp605tcp)
- [leechcore_device_microvmi](#leechcore_device_microvmi)

## leechcore_ft601_driver_linux

Expand Down Expand Up @@ -76,3 +82,63 @@ Allows LeechCore to connect to a SP605 FPGA board exposing a TCP server on its n
Place leechcore_device_sp605tcp.[so|dll] alongside leechcore.[so|dll].


## leechcore_device_microvmi

#### Authors
- Ulf Frisk
- Mathieu Tarral ([@mtarral](https://github.com/mtarral)) - [ANSSI](https://www.ssi.gouv.fr/)

#### Supported Platforms
- Linux

#### Overview

Allows LeechCore to peek into the live physical memory of virtual machines
supported by [libmicrovmi](https://wenzel.github.io/libmicrovmi/reference/drivers.html)

#### Requirements

- [libmicrovmi](https://github.com/Wenzel/libmicrovmi): see the [documentation](https://wenzel.github.io/libmicrovmi/tutorial/installation.html)

#### Plugin documentation

- URL device syntax: `microvmi://param1=value1&param2=value2`
- Debugging: `export RUST_LOG=debug`

##### Xen

Parameters:
- `vm_name`: name of the VM

~~~
sudo -E ./memprocfs -mount xxx -device 'microvmi://vm_name=win10'
~~~

##### KVM

Parameters:
- `vm_name`: name of the VM
- `kvm_unix_socket`: KVMi UNIX socket (see KVM-VMI project)

~~~
./memprocfs -mount xxx -device 'microvmi://vm_name=win10&kvm_unix_socket=/tmp/introspector'
~~~

##### VirtualBox

Parameters:
- `vm_name`: name of the VM

~~~
./memprocfs -mount xxx -device 'microvmi://vm_name=win10'
~~~

##### QEMU

Parameters:
- `memflow_connector_name`: `qemu_procfs`
- `vm_name` (optional): name of the VM

~~~
sudo -E ./memprocfs -mount xxx -device 'microvmi://memflow_connector_name=qemu_procfs'
~~~
18 changes: 18 additions & 0 deletions leechcore_device_microvmi/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
CC=gcc
# -Wno-unused-variable -> unused variable in leechcore.h
CFLAGS += -I. -I../includes -D LINUX -shared -fPIC -fvisibility=hidden -g -Wall -Werror -Wextra -Wno-unused-variable
LDFLAGS += -shared -lmicrovmi
DEPS =
OBJ = leechcore_device_microvmi.o

%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)

leechcore_device_microvmi: $(OBJ)
$(CC) -o $@ $^ $(CFLAGS) -o leechcore_device_microvmi.so $(LDFLAGS)
rm *.o
mkdir -p ../files
mv leechcore_device_microvmi.so ../files/

clean:
rm -f *.o
163 changes: 163 additions & 0 deletions leechcore_device_microvmi/leechcore_device_microvmi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
#include <stdbool.h>

#include <leechcore_device.h>
#include <libmicrovmi.h>

#define PLUGIN_URL_SCHEME "microvmi://"

static VOID DeviceMicrovmi_ReadContigious(PLC_READ_CONTIGIOUS_CONTEXT ctxRC) {
// read contiguous physical memory
PLC_CONTEXT ctxLC = ctxRC->ctxLC;
void *driver = ctxLC->hDevice;
uint64_t bytes_read = 0;
if (!microvmi_read_physical(driver, ctxRC->paBase, ctxRC->pb, ctxRC->cb,
&bytes_read)) {
lcprintfvvv(ctxLC, "Failed to read physical memory at 0x%llx\n",
ctxRC->paBase);
}
ctxRC->cbRead = (DWORD)bytes_read;
}

static BOOL DeviceMicrovmi_WriteContigious(_In_ PLC_CONTEXT ctxLC,
_In_ QWORD qwAddr, _In_ DWORD cb,
_In_reads_(cb) PBYTE pb) {
// write contiguous memory
void *driver = ctxLC->hDevice;
if (!microvmi_write_physical(driver, qwAddr, pb, cb)) {
lcprintfvvv(ctxLC,
"Failed to write %d bytes in physical memory at 0x%llx\n",
cb, qwAddr);
return false;
}
return true;
}

static VOID DeviceMicrovmi_Close(_Inout_ PLC_CONTEXT ctxLC) {
// close driver
void *driver = ctxLC->hDevice;
microvmi_destroy(driver);
}

static bool parse_url_args(PLC_CONTEXT ctxLC,
DriverInitParamsFFI *init_params) {
// this function parses the device URL string and fills the init_params struct in consequence
// the URL syntax is the following
// microvmi://param1=value1&param2=value2

const char *url_device = ctxLC->Config.szDevice;
// check URL scheme
if (strncmp(url_device, PLUGIN_URL_SCHEME, strlen(PLUGIN_URL_SCHEME))) {
// no match, quit
return false;
}
// clone URL as strtok will modify it in place
char *szDevice_start_params =
strdup(url_device + strlen(PLUGIN_URL_SCHEME));
// split on '&'
char *saveptr = NULL;
char *token = NULL;
for (token = strtok_r(szDevice_start_params, "&", &saveptr); token != NULL;
token = strtok_r(NULL, "&", &saveptr)) {
// token is param1=value1
// split on '='
char *saveptr2 = NULL;
char *param_name = strtok_r(token, "=", &saveptr2);
if (!param_name)
continue;
char *param_value = strtok_r(NULL, "=", &saveptr2);
if (!param_value)
continue;
if (!strncmp(param_name, "vm_name", strlen("vm_name"))) {
// free previous value if any
if (init_params->common.vm_name) {
free(init_params->common.vm_name);
init_params->common.vm_name = NULL;
}
init_params->common.vm_name = strdup(param_value);
} else if (!strncmp(param_name, "kvm_unix_socket",
strlen("kvm_unix_socket"))) {
init_params->kvm.tag = UnixSocket;
// free previous value if any
if (init_params->kvm.unix_socket.path) {
free(init_params->kvm.unix_socket.path);
init_params->kvm.unix_socket.path = NULL;
}
init_params->kvm.unix_socket.path = strdup(param_value);
} else if (!strncmp(param_name, "memflow_connector_name",
strlen("memflow_connector_name"))) {
// free previous value if any
if (init_params->memflow.connector_name) {
free(init_params->memflow.connector_name);
init_params->memflow.connector_name = NULL;
}
init_params->memflow.connector_name = strdup(param_value);
} else {
lcprintfv(ctxLC, "MICROVMI: unhandled init parameter: %s\n",
param_name);
}
}
free(szDevice_start_params);
return true;
}

_Success_(return ) EXPORTED_FUNCTION BOOL
LcPluginCreate(_Inout_ PLC_CONTEXT ctxLC,
_Out_opt_ PPLC_CONFIG_ERRORINFO ppLcCreateErrorInfo) {
lcprintfv(ctxLC, "MICROVMI: Initializing\n");
// safety checks
if (ppLcCreateErrorInfo) {
*ppLcCreateErrorInfo = NULL;
}
if (ctxLC->version != LC_CONTEXT_VERSION) {
return false;
}

// handle init args
DriverInitParamsFFI init_params = {0};
if (!parse_url_args(ctxLC, &init_params)) {
return false;
}

// init libmicrovmi
microvmi_envlogger_init();
const char *init_error = NULL;

void *microvmi_driver = microvmi_init(NULL, &init_params, &init_error);
if (!microvmi_driver) {
lcprintfv(ctxLC, "MICROVMI: initialization failed: %s.\n", init_error);
goto error_exit;
}

// assign context
ctxLC->hDevice = (HANDLE)microvmi_driver;

// setup config
ctxLC->Config.fVolatile = true;
// set max physical address
uint64_t max_addr = 0;
if (!microvmi_get_max_physical_addr(microvmi_driver, &max_addr)) {
lcprintf(ctxLC, "Failed to get max physical address\n");
goto error_exit;
}
lcprintfvv(ctxLC, "MICROVMI: max physical address: 0x%lx\n", max_addr);
ctxLC->Config.paMax = max_addr;
// set callback functions
ctxLC->pfnReadContigious = DeviceMicrovmi_ReadContigious;
ctxLC->pfnWriteContigious = DeviceMicrovmi_WriteContigious;
ctxLC->pfnClose = DeviceMicrovmi_Close;
// status
lcprintfv(ctxLC, "MICROVMI: initialized.\n");
return true;
error_exit:
if (init_error)
rs_cstring_free((char *)init_error);
// free init_params
if (init_params.common.vm_name)
free(init_params.common.vm_name);
if (init_params.kvm.unix_socket.path)
free(init_params.kvm.unix_socket.path);
if (init_params.memflow.connector_name)
free(init_params.memflow.connector_name);
DeviceMicrovmi_Close(ctxLC);
return false;
}

0 comments on commit 342f34c

Please sign in to comment.