Hello, I'd like to ping this patch in that the QEMU-PCILeech plugin is now merged into the PCILeech repository: https://github.com/ufrisk/LeechCore-plugins/pull/10 The Patchew link is: https://patchew.org/QEMU/caaxnugbyhpx249duwgyxotgjkxwatrhjsq94lrvfmgp._5fgjx...@mail.gmail.com/
Kind Regards, Zero Tang On Tue, Aug 6, 2024 at 5:28 PM Zero Tang <zero.tang...@gmail.com> wrote: > This virtual PCILeech device aims to help security researchers attack the > guest via DMA and test their IOMMU defenses. > This device is intended to support any systems with PCI, but I am only > able to test x86-based guests. > For what PCILeech is, check PCILeech GitHub repository: > https://github.com/ufrisk/pcileech > The QEMU-PCILeech plugin is currently awaiting merging: > https://github.com/ufrisk/LeechCore-plugins/pull/10 > > This is my first time contributing to QEMU and I am sorry that I forgot to > include a "[PATCH]" prefix in the title from my previous email and that I > didn't cc to relevant maintainers. > If needed, add my name and contact info into the maintainer's list. > > Signed-off-by: Zero Tang <zero.tang...@gmail.com> > --- > hw/misc/Kconfig | 5 ++++ > hw/misc/meson.build | 1 + > hw/misc/pcileech.c | 291 > +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 297 insertions(+) > > diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig > index 1e08785b83..6c3ea7bf74 100644 > --- a/hw/misc/Kconfig > +++ b/hw/misc/Kconfig > @@ -30,6 +30,11 @@ config EDU > default y if TEST_DEVICES > depends on PCI && MSI_NONBROKEN > > +config PCILEECH > + bool > + default y > + depends on PCI > + > config PCA9552 > bool > depends on I2C > diff --git a/hw/misc/meson.build b/hw/misc/meson.build > index 2ca8717be2..e79931b9a6 100644 > --- a/hw/misc/meson.build > +++ b/hw/misc/meson.build > @@ -1,5 +1,6 @@ > system_ss.add(when: 'CONFIG_APPLESMC', if_true: files('applesmc.c')) > system_ss.add(when: 'CONFIG_EDU', if_true: files('edu.c')) > +system_ss.add(when: 'CONFIG_PCILEECH', if_true: files('pcileech.c')) > system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vmcoreinfo.c')) > system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c')) > system_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c')) > diff --git a/hw/misc/pcileech.c b/hw/misc/pcileech.c > new file mode 100644 > index 0000000000..252a570161 > --- /dev/null > +++ b/hw/misc/pcileech.c > @@ -0,0 +1,291 @@ > +/* > + * QEMU Virtual PCILeech Device > + * > + * Copyright (c) 2024 Zero Tang > + * > + * Permission is hereby granted, free of charge, to any person obtaining a > + * copy of this software and associated documentation files (the > "Software"), > + * to deal in the Software without restriction, including without > limitation > + * the rights to use, copy, modify, merge, publish, distribute, > sublicense, > + * and/or sell copies of the Software, and to permit persons to whom the > + * Software is furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice shall be > included in > + * all copies or substantial portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, > EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF > MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT > SHALL THE > + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER > + * DEALINGS IN THE SOFTWARE. > + */ > + > +#include "qemu/osdep.h" > +#include "qemu/units.h" > +#include "hw/pci/pci.h" > +#include "hw/hw.h" > +#include "hw/pci/msi.h" > +#include "qemu/timer.h" > +#include "hw/qdev-properties.h" > +#include "hw/qdev-properties-system.h" > +#include "qom/object.h" > +#include "qemu/main-loop.h" /* iothread mutex */ > +#include "qemu/module.h" > +#include "qapi/visitor.h" > + > +#define TYPE_PCILEECH_DEVICE "pcileech" > + > +struct LeechRequestHeader { > + uint8_t endianness; /* 0 - Little, 1 - Big */ > + uint8_t command; /* 0 - Read, 1 - Write */ > + uint8_t reserved[6]; > + /* Variable Endianness */ > + uint64_t address; > + uint64_t length; > +}; > + > +struct LeechResponseHeader { > + uint8_t endianness; /* 0 - Little, 1 - Big */ > + uint8_t reserved[3]; > + MemTxResult result; > + uint64_t length; /* Indicates length of data followed by header */ > +}; > + > +/* Verify the header length */ > +static_assert(sizeof(struct LeechRequestHeader) == 24); > +static_assert(sizeof(struct LeechResponseHeader) == 16); > + > +struct PciLeechState { > + /* Internal State */ > + PCIDevice device; > + QemuThread thread; > + QemuMutex mutex; > + bool endianness; > + bool stopping; > + /* Communication */ > + char *host; > + uint16_t port; > + int sockfd; > +}; > + > +typedef struct LeechRequestHeader LeechRequestHeader; > +typedef struct PciLeechState PciLeechState; > + > +DECLARE_INSTANCE_CHECKER(PciLeechState, PCILEECH, TYPE_PCILEECH_DEVICE) > + > +static void pci_leech_process_write_request(PciLeechState *state, > + LeechRequestHeader *request, > + int incoming) > +{ > + char buff[1024]; > + for (uint64_t i = 0; i < request->length; i += sizeof(buff)) { > + struct LeechResponseHeader response = { 0 }; > + char* response_buffer = (char *)&response; > + const uint64_t writelen = (request->length - i) <= sizeof(buff) ? > + (request->length - i) : > sizeof(buff); > + ssize_t recvlen = 0, sendlen = 0; > + while (recvlen < writelen) { > + recvlen += recv(incoming, &buff[recvlen], writelen - recvlen, > 0); > + } > + response.endianness = state->endianness; > + response.result = pci_dma_write(&state->device, request->address > + i, > + buff, > writelen); > + if (response.result) { > + printf("PCILeech: Address 0x%lX Write Error! MemTxResult: > 0x%X\n", > + request->address + i, response.result); > + } > + response.length = 0; > + while (sendlen < sizeof(struct LeechResponseHeader)) { > + sendlen += send(incoming, &response_buffer[sendlen], > + sizeof(struct LeechResponseHeader) - sendlen, > 0); > + } > + } > +} > + > +static void pci_leech_process_read_request(PciLeechState *state, > + LeechRequestHeader *request, > + int incoming) > +{ > + char buff[1024]; > + for (uint64_t i = 0; i < request->length; i += sizeof(buff)) { > + struct LeechResponseHeader response = { 0 }; > + char* response_buffer = (char *)&response; > + const uint64_t readlen = (request->length - i) <= sizeof(buff) ? > + (request->length - i) : sizeof(buff); > + ssize_t sendlen = 0; > + response.endianness = state->endianness; > + response.result = pci_dma_read(&state->device, request->address + > i, > + buff, > readlen); > + if (response.result) { > + printf("PCILeech: Address 0x%lX Read Error! MemTxResult: > 0x%X\n", > + request->address + i, response.result); > + } > + response.length = (request->endianness != state->endianness) ? > + bswap64(readlen) : readlen; > + while (sendlen < sizeof(struct LeechResponseHeader)) { > + sendlen += send(incoming, &response_buffer[sendlen], > + sizeof(struct LeechResponseHeader) - sendlen, > 0); > + } > + sendlen = 0; > + while (sendlen < readlen) { > + sendlen += send(incoming, &buff[sendlen], readlen - sendlen, > 0); > + } > + } > +} > + > +static void *pci_leech_worker_thread(void *opaque) > +{ > + PciLeechState *state = PCILEECH(opaque); > + while (1) { > + LeechRequestHeader request; > + char *request_buffer = (char *)&request; > + ssize_t received = 0; > + int incoming; > + struct sockaddr address; > + socklen_t addrlen; > + /* Check if we are stopping. */ > + qemu_mutex_lock(&state->mutex); > + if (state->stopping) { > + qemu_mutex_unlock(&state->mutex); > + break; > + } > + qemu_mutex_unlock(&state->mutex); > + /* Accept PCILeech requests. */ > + /* Use HTTP1.0-like protocol for simplicity. */ > + incoming = accept(state->sockfd, &address, &addrlen); > + if (incoming < 0) { > + puts("WARNING: Failed to accept socket for PCILeech! Skipping > " > + "Request...\n"); > + continue; > + } > + /* Get PCILeech requests. */ > + while (received < sizeof(LeechRequestHeader)) { > + received += recv(incoming, &request_buffer[received], > + sizeof(LeechRequestHeader) - received, 0); > + } > + /* Swap endianness. */ > + if (request.endianness != state->endianness) { > + request.address = bswap64(request.address); > + request.length = bswap64(request.length); > + } > + /* Process PCILeech requests. */ > + qemu_mutex_lock(&state->mutex); > + if (request.command) { > + pci_leech_process_write_request(state, &request, incoming); > + } else { > + pci_leech_process_read_request(state, &request, incoming); > + } > + qemu_mutex_unlock(&state->mutex); > + close(incoming); > + } > + return NULL; > +} > + > +static void pci_leech_realize(PCIDevice *pdev, Error **errp) > +{ > + PciLeechState *state = PCILEECH(pdev); > + struct sockaddr_in sock_addr; > + char host_ip[16]; > + struct hostent *he = gethostbyname(state->host); > + if (he == NULL) { > + puts("gethostbyname failed!"); > + exit(EXIT_FAILURE); > + } > + /* Initialize the socket for PCILeech. */ > + state->sockfd = socket(AF_INET, SOCK_STREAM, 0); > + if (state->sockfd < 0) { > + puts("Failed to initialize socket for PCILeech!"); > + exit(EXIT_FAILURE); > + } > + sock_addr.sin_family = AF_INET; > + sock_addr.sin_addr = *(struct in_addr *)he->h_addr; > + sock_addr.sin_port = htons(state->port); > + inet_ntop(AF_INET, &sock_addr.sin_addr, host_ip, sizeof(host_ip)); > + if (bind(state->sockfd, (struct sockaddr *)&sock_addr, > sizeof(sock_addr)) > + < 0) { > + puts("Failed to bind socket for PCILeech!"); > + close(state->sockfd); > + exit(EXIT_FAILURE); > + } > + if (listen(state->sockfd, 10) < 0) { > + puts("Failed to listen to socket for PCILeech!"); > + close(state->sockfd); > + exit(EXIT_FAILURE); > + } > + printf("INFO: PCILeech is listening on %s:%u...\n", host_ip, > state->port); > + /* Initialize the thread for PCILeech. */ > + qemu_mutex_init(&state->mutex); > + qemu_thread_create(&state->thread, "pcileech", > pci_leech_worker_thread, > + state, QEMU_THREAD_JOINABLE); > +} > + > +static void pci_leech_finalize(PCIDevice *pdev) > +{ > + PciLeechState *state = PCILEECH(pdev); > + puts("Stopping PCILeech Worker..."); > + qemu_mutex_lock(&state->mutex); > + state->stopping = true; > + qemu_mutex_unlock(&state->mutex); > + close(state->sockfd); > + qemu_thread_join(&state->thread); > + qemu_mutex_destroy(&state->mutex); > +} > + > +char pci_leech_default_host[] = "0.0.0.0"; > + > +static void pci_leech_instance_init(Object *obj) > +{ > + int x = 1; > + char* y = (char *)&x; > + PciLeechState *state = PCILEECH(obj); > + /* QEMU's String-Property can't specify default value. */ > + /* So we have to set the default on our own. */ > + if (state->host == NULL) { > + state->host = pci_leech_default_host; > + } > + /* Save Our Endianness. */ > + state->endianness = (*y == 0); > +} > + > +static Property leech_properties[] = { > + DEFINE_PROP_UINT16("port", PciLeechState, port, 6789), > + DEFINE_PROP_STRING("host", PciLeechState, host), > + DEFINE_PROP_END_OF_LIST(), > +}; > + > +static void pci_leech_class_init(ObjectClass *class, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(class); > + PCIDeviceClass *k = PCI_DEVICE_CLASS(class); > + k->realize = pci_leech_realize; > + k->exit = pci_leech_finalize; > + /* Change the Vendor/Device ID to your favor. */ > + /* These are the default values from PCILeech-FPGA. */ > + k->vendor_id = PCI_VENDOR_ID_XILINX; > + k->device_id = 0x0666; > + k->revision = 0; > + k->class_id = PCI_CLASS_NETWORK_ETHERNET; > + device_class_set_props(dc, leech_properties); > + set_bit(DEVICE_CATEGORY_MISC, dc->categories); > +} > + > +static void pci_leech_register_types(void) > +{ > + static InterfaceInfo interfaces[] = { > + {INTERFACE_CONVENTIONAL_PCI_DEVICE}, > + {}, > + }; > + static const TypeInfo leech_info = { > + .name = TYPE_PCILEECH_DEVICE, > + .parent = TYPE_PCI_DEVICE, > + .instance_size = sizeof(PciLeechState), > + .instance_init = pci_leech_instance_init, > + .class_init = pci_leech_class_init, > + .interfaces = interfaces, > + }; > + type_register_static(&leech_info); > +} > + > +type_init(pci_leech_register_types) >