Honeypots mailing list archives

Counter measures to VMware fingerprinting


From: Kostya KORTCHINSKY <kostya.kortchinsky () renater fr>
Date: Mon, 12 Jan 2004 10:27:04 +0100

Hi,

Included with this mail is a patch that addresses a few of the most obvious ways to fingerprint locally a guest OS running under VMware.

The modifications done are :
- names of the IDE devices (HD & CDROM)
- names of the SCSI devices (HD & CDROM)
- PCI vendor and device ID of the video adapter
- I/O backdoor (feel free to modify the magic number !)

This version targets VMware Workstation for Linux version 4.0.5.

Only constants are modified (except for the SCSI CDROM where a little code injection was needed since vendor and revision strings are originally the same as for the SCSI HD), which shouldn't raise any security issue.

This is only an early version of the patch, and the one being developped has more features, including BIOS replacements. Anyway, I would like to have some return from experienced people regarding this, perhaps other things to patch, or other ways to fingerprint VMware.

I stress the fact that you should _backup_ your *vmware-vmx* binary before using this, and preferably your guest OS, in case things goes wrong.

Regards,

Kostya KORTCHINSKY
French HoneyNet Project
http://www.frenchhoneynet.org

/*
 * VMware fingerprinting counter measures
 * by Kostya KORTCHINSKY <kostya (dot) kortchinsky (at) renater (dot) fr>
 *
 * Many thanks to the people of the French HoneyNet Project who helped me !
 *
 * gcc -Wall -lz -o vmpatch vmpatch.c # zlib needed !
 *
 * -- BACKUP YOUR VMWARE-VMX BINARY BEFORE USING THIS ! --
 *
 * ./vmpatch
 *
 * This program is based on some reverse-engineering work carried out on
 * VMware Workstation binaries for Linux, and has for only aim to improve
 * the "furtivity" of VMware.
 *
 * Please, understand that there is a chance to screw up guest OS, and
 * therefore this program should only be used by power users (and preferably
 * for text mode oriented guest OS). I stress that there is no need to use
 * this outside the context of HoneyPots.
 *
 * Since this program patches mostly constants, it shouldn't raise any
 * security issue, and only addresses some _very_ obvious things :
 * - strings for IDE devices (HD & CDROM)
 * - strings for SCSI devices (HD & CDROM)
 * - video adaptor vendor & device IDs
 * - I/O backdoor
 *
 * I am aware that there are a lot of things remaining, and a more advanced
 * version is being developped and used with deeper changes such as whole
 * BIOSes replacement.
 *
 * I would be glad to get some feedback about changes to bring, fingerprinting
 * methods to counter, ... And yes, verbosity is minimal.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <linux/elf.h>
#include <zlib.h>

#define VERSION 0.1.1

typedef struct
{
        unsigned long int vendor;
        unsigned long int model;
        unsigned long int revision;
} offsetsScsiHD;

typedef struct
{
        unsigned long int model;
        unsigned long int nops[4];
} offsetsScsiCDROM;

typedef struct
{
        char *vendor;
        char *model;
        char *revision;
} scsiDevice;

typedef struct
{
        char *name;
        unsigned short int vendor;
        unsigned short int device;
} videoAdapter;

typedef struct
{
        char *name;
        offsetsScsiHD scsiHD;
        offsetsScsiCDROM scsiCDROM;
        unsigned long int videoAdapter[2];
        unsigned long int magicNumber;
        unsigned long int ideHD;
        unsigned long int ideCDROM;
} vmwareVersion;

scsiDevice scsiHDs[] =
{
        { "IBM", "DDRS-34560", "S97B" },
        { "QUANTUM", "FIREBALL ST4.3S", "0F0C" },
        { "SEAGATE", "ST32155N", "0532" }
};

scsiDevice scsiCDROMs[] =
{
        { "PLEXTOR", "CD-ROM PX-40TS", "1.02" },
        { "YAMAHA", "CRW6416S", "1.0c" }
};

videoAdapter videoAdapters[] =
{
        { "nVidia Corporation NV10 [GeForce 256 SDR]", 0x10de, 0x0100 },
        { "ATI Technologies Inc 3D Rage Pro", 0x1002, 0x4747 }
};

char *ideHDs[] =
{
        "Maxtor 88400D8",
        "IBM-DTNA-22110",
        "FUJITSU MHT2030AT"
};

char *ideCDROMs[] =
{
        "SONY CD-RW CRX140E",
        "_NEC CD-2800D",
        "PLEXTOR CD-R PX-W8432T"
};

vmwareVersion vmwareVersions[] =
{
        {
                "VMware Workstation 4.0.5 build-6030",
                { 0x5dba7, 0x5dbc5, 0x5dbf3 },
                { 0x5f188, { 0x5eb13, 0x5eb83, 0x5ec13, 0x5ee74 } },
                { 0x7f683, 0x7f5a9 },
                0x10aff,
                0x9240,
                0x91e0
        }
};

int version = -1;
unsigned char *vmxBinary = NULL, *vmmBinary = NULL;
Elf32_Ehdr *vmxEhdr = NULL, *vmmEhdr = NULL;
Elf32_Shdr *vmxShdr = NULL, *vmmShdr = NULL;
char *vmxShstrtab = NULL, *vmmShstrtab = NULL;
int indexVbios = -1, indexVmm = -1, indexZtext = -1, indexZrodata = -1;
unsigned char *sectionVbios = NULL, *sectionZtext = NULL, *sectionZrodata = NULL, *sectionVmm = NULL;

int getChoice(void)
{
        int result;
        char *s, input[16];

        fgets(input, sizeof(input), stdin);
        s = strchr(input, '\n');
        if (s)
                *s = '\0';
        result = atoi(input) - 1;
        if (result < 0)
                result = 0;

        return result;
}

char vendor[8], model[16], revision[4];

void initScsiDevice(scsiDevice *device)
{
        int size;

        memset(&vendor[0], ' ', sizeof(vendor));
        size = strlen(device->vendor);
        if (size > sizeof(vendor))
                size = sizeof(vendor);
        memcpy(&vendor[0], device->vendor, size);
        memset(&model[0], ' ', sizeof(model));
        size = strlen(device->model);
        if (size > sizeof(model))
                size = sizeof(model);
        memcpy(&model[0], device->model, size);
        memset(&revision[0], ' ', sizeof(revision));
        size = strlen(device->revision);
        if (size > sizeof(revision))
                size = sizeof(revision);
        memcpy(&revision[0], device->revision, size); 
}

void patchScsiDevices(void)
{
        int i, number;
        unsigned long int offset;

        fprintf(stdout, "Choose a new virtual SCSI hard drive from the following list:\n\n");
        for (i = 0; i < sizeof(scsiHDs) / sizeof(scsiDevice); i++)
                fprintf(stdout, "%3d.  %s %s\n", i + 1, scsiHDs[i].vendor, scsiHDs[i].model);
        fprintf(stdout, "\nEnter the number: ");
        number = getChoice();

        initScsiDevice(&scsiHDs[number]);

        offset = vmwareVersions[version].scsiHD.vendor;
        memcpy(&vmxBinary[offset + 3], &vendor[0], 4);
        memcpy(&vmxBinary[offset + 10], &vendor[4], 4);
        offset = vmwareVersions[version].scsiHD.model;
        memcpy(&vmxBinary[offset + 3], &model[0], 4);
        memcpy(&vmxBinary[offset + 10], &model[4], 4);
        memcpy(&vmxBinary[offset + 17], &model[8], 4);
        memcpy(&vmxBinary[offset + 24], &model[12], 4);
        offset = vmwareVersions[version].scsiHD.revision;
        memcpy(&vmxBinary[offset + 3], &revision[0], 4);

        fprintf(stdout, "Choose a new virtual SCSI CDROM drive from the following list:\n\n");
        for (i = 0; i < sizeof(scsiCDROMs) / sizeof(scsiDevice); i++)
                fprintf(stdout, "%3d.  %s %s\n", i + 1, scsiCDROMs[i].vendor, scsiCDROMs[i].model);
        fprintf(stdout, "\nEnter the number: ");
        number = getChoice();

        initScsiDevice(&scsiCDROMs[number]);

        offset = vmwareVersions[version].scsiCDROM.model;
        memcpy(&vmxBinary[offset + 3], &model[0], 4);
        memcpy(&vmxBinary[offset + 10], &model[4], 4);
        memcpy(&vmxBinary[offset + 17], &model[8], 4);
        vmxBinary[offset + 21] = '\xe9';
        *(unsigned long int *)(&vmxBinary[offset + 22]) = vmwareVersions[version].scsiCDROM.nops[0] - (offset + 26);
        offset = vmwareVersions[version].scsiCDROM.nops[0];
        vmxBinary[offset] = '\xc7';
        vmxBinary[offset + 1] = '\x47';
        vmxBinary[offset + 2] = '\x1c';
        memcpy(&vmxBinary[offset + 3], &model[12], 4);
        vmxBinary[offset + 7] = '\xe9';
        *(unsigned long int *)(&vmxBinary[offset + 8]) = vmwareVersions[version].scsiCDROM.nops[1] - (offset + 12);
        offset = vmwareVersions[version].scsiCDROM.nops[1];
        vmxBinary[offset] = '\xc7';
        vmxBinary[offset + 1] = '\x47';
        vmxBinary[offset + 2] = '\x08';
        memcpy(&vmxBinary[offset + 3], &vendor[0], 4);
        vmxBinary[offset + 7] = '\xe9';
        *(unsigned long int *)(&vmxBinary[offset + 8]) = vmwareVersions[version].scsiCDROM.nops[2] - (offset + 12);
        offset = vmwareVersions[version].scsiCDROM.nops[2];
        vmxBinary[offset] = '\xc7';
        vmxBinary[offset + 1] = '\x47';
        vmxBinary[offset + 2] = '\x0c';
        memcpy(&vmxBinary[offset + 3], &vendor[4], 4);
        vmxBinary[offset + 7] = '\xe9';
        *(unsigned long int *)(&vmxBinary[offset + 8]) = vmwareVersions[version].scsiCDROM.nops[3] - (offset + 12);
        offset = vmwareVersions[version].scsiCDROM.nops[3];
        vmxBinary[offset] = '\xc7';
        vmxBinary[offset + 1] = '\x47';
        vmxBinary[offset + 2] = '\x20';
        memcpy(&vmxBinary[offset + 3], &revision[0], 4);
        vmxBinary[offset + 7] = '\xe9';
        *(unsigned long int *)(&vmxBinary[offset + 8]) = vmwareVersions[version].scsiCDROM.model + 28 - (offset + 12);
}

int patchVideoAdapter(void)
{
        int i, number, error = EXIT_FAILURE;
        unsigned long int length, newLength;
        unsigned char checksum, *data = NULL;
        unsigned short int offset;

        fprintf(stdout, "Choose a new virtual video adapter from the following list:\n\n");
        for (i = 0; i < sizeof(videoAdapters) / sizeof(videoAdapter); i++)
                fprintf(stdout, "%3d.  %s\n", i + 1, videoAdapters[i].name);
        fprintf(stdout, "\nEnter the number: ");
        number = getChoice();

        length = 48 * 1024;
        if ((data = malloc(length)) == NULL)
                return error;
        if (uncompress(data, &length, &vmxBinary[vmxShdr[indexVbios].sh_offset], vmxShdr[indexVbios].sh_size) != Z_OK)
                goto end;

        if (data[0] != 0x55 || data[1] != 0xaa)
                goto end;
        offset = *(unsigned short int *)(&data[24]);
        if (memcmp(&data[offset], "PCIR", 4) != 0)
                goto end;
        *(unsigned short int *)(&data[offset + 4]) = videoAdapters[number].vendor;
        *(unsigned short int *)(&data[offset + 6]) = videoAdapters[number].device;
        for (i = 0, checksum = 0; i < length; i++)
                if (i != 6)
                        checksum += data[i];
        data[6] = (unsigned char)(-checksum);

        newLength = 16 * 1024;
        if ((sectionVbios = malloc(newLength)) == NULL)
                goto end;
        if (compress2(sectionVbios, &newLength, data, length, Z_BEST_COMPRESSION) != Z_OK)
                goto end;
        vmxShdr[indexVbios].sh_size = newLength;
        vmxShdr[indexVbios].sh_entsize = length;

        *(unsigned short int *)(&vmxBinary[vmwareVersions[version].videoAdapter[0]]) = videoAdapters[number].vendor;
        *(unsigned short int *)(&vmxBinary[vmwareVersions[version].videoAdapter[1]]) = videoAdapters[number].device;

        error = EXIT_SUCCESS;
end:
        free(data);
        return error;
}

int patchIoBackdoor(void)
{
        int error = EXIT_FAILURE;
        unsigned long int length, newLength;
        unsigned char *data = NULL;

        length = 384 * 1024;
        if ((data = malloc(length)) == NULL)
                return error;
        if (uncompress(data, &length, &vmmBinary[vmmShdr[indexZtext].sh_offset], vmmShdr[indexZtext].sh_size) != Z_OK)
                goto end;

        *(unsigned long int *)(&data[vmwareVersions[version].magicNumber]) = 0x48506f74;

        newLength = 192 * 1024;
        if ((sectionZtext = malloc(newLength)) == NULL)
                goto end;
        if (compress2(sectionZtext, &newLength, data, length, Z_BEST_COMPRESSION) != Z_OK)
                goto end;
        vmmShdr[indexZtext].sh_size = newLength;
        vmmShdr[indexZtext].sh_entsize = length;

        error = EXIT_SUCCESS;
end:
        free(data);
        return error;
}

int patchIdeDevices(void)
{
        int i, number, size, error = EXIT_FAILURE;
        unsigned long int length, newLength;
        unsigned char *data = NULL;

        length = 48 * 1024;
        if ((data = malloc(length)) == NULL)
                return error;
        if (uncompress(data, &length, &vmmBinary[vmmShdr[indexZrodata].sh_offset], vmmShdr[indexZrodata].sh_size) != 
Z_OK)
                goto end;

        fprintf(stdout, "Choose a new virtual IDE hard drive from the following list:\n\n");
        for (i = 0; i < sizeof(ideHDs) / sizeof(char *); i++)
                fprintf(stdout, "%3d.  %s\n", i + 1, ideHDs[i]);
        fprintf(stdout, "\nEnter the number: ");
        number = getChoice();

        memset(&data[vmwareVersions[version].ideHD], ' ', 31);
        size = strlen(ideHDs[number]);
        if (size > 31)
                size = 31;
        memcpy(&data[vmwareVersions[version].ideHD], ideHDs[number], size);

        fprintf(stdout, "Choose a new virtual IDE CDROM from the following list:\n\n");
        for (i = 0; i < sizeof(ideCDROMs) / sizeof(char *); i++)
                fprintf(stdout, "%3d.  %s\n", i + 1, ideCDROMs[i]);
        fprintf(stdout, "\nEnter the number: ");
        number = getChoice();

        memset(&data[vmwareVersions[version].ideCDROM], ' ', 31);
        size = strlen(ideCDROMs[number]);
        if (size > 31)
                size = 31;
        memcpy(&data[vmwareVersions[version].ideCDROM], ideCDROMs[number], size);

        newLength = 16 * 1024;
        if ((sectionZrodata = malloc(newLength)) == NULL)
                goto end;
        if (compress2(sectionZrodata, &newLength, data, length, Z_BEST_COMPRESSION) != Z_OK)
                goto end;
        vmmShdr[indexZrodata].sh_size = newLength;
        vmmShdr[indexZrodata].sh_entsize = length;

        error = EXIT_SUCCESS;
end:
        free(data);
        return error;
}

int main(int argc, char *argv[])
{
        FILE *file, *process;
        char buffer[64];
        int i, error = EXIT_FAILURE;
        unsigned long int fileSize, size;
        unsigned char *newBinary = NULL;

        if ((process = popen("/usr/bin/vmware -v", "r")) == NULL)
                return error;
        if (fgets(buffer, sizeof(buffer), process) == NULL)
                return error;
        if (pclose(process) == -1)
                return error;
        for (i = 0; i < sizeof(vmwareVersions) / sizeof(vmwareVersion); i++)
                if (strncmp(buffer, vmwareVersions[i].name, strlen(vmwareVersions[i].name)) == 0)
                        version = i;
        if (version == -1)
                return error;

        if ((file = fopen("/usr/lib/vmware/bin/vmware-vmx", "rb")) == NULL)
                return error;
        fseek(file, 0, SEEK_END);
        fileSize = ftell(file);
        if ((vmxBinary = malloc(fileSize)) == NULL)
                goto end;
        fseek(file, 0, SEEK_SET);
        if (fread(vmxBinary, 1, fileSize, file) != fileSize)
                goto end;
        vmxEhdr = (Elf32_Ehdr *)(&vmxBinary[0]);
        vmxShdr = (Elf32_Shdr *)(&vmxBinary[vmxEhdr->e_shoff]);
        vmxShstrtab = &vmxBinary[vmxShdr[vmxEhdr->e_shstrndx].sh_offset];
        for (i = 1; i < vmxEhdr->e_shnum; i++)
        {
                if (!strcmp(&vmxShstrtab[vmxShdr[i].sh_name], ".vbios"))
                        indexVbios = i;
                else if (!strcmp(&vmxShstrtab[vmxShdr[i].sh_name], ".vmm"))
                        indexVmm = i;
                if (indexVbios != -1 && indexVmm != -1)
                        break;
        }
        if (i == vmxEhdr->e_shnum)
                goto end;

        patchScsiDevices();
        if (patchVideoAdapter() != EXIT_SUCCESS)
                goto end;

        vmmBinary = &vmxBinary[vmxShdr[indexVmm].sh_offset];
        vmmEhdr = (Elf32_Ehdr *)(&vmmBinary[0]);
        vmmShdr = (Elf32_Shdr *)(&vmmBinary[vmmEhdr->e_shoff]);
        vmmShstrtab = &vmmBinary[vmmShdr[vmmEhdr->e_shstrndx].sh_offset];
        for (i = 0; i < vmmEhdr->e_shnum; i++)
        {
                if(!strcmp(&vmmShstrtab[vmmShdr[i].sh_name], ".ztext"))
                        indexZtext = i;
                else if (!strcmp(&vmmShstrtab[vmmShdr[i].sh_name], ".zrodata"))
                        indexZrodata = i;
                if (indexZtext != -1 && indexZrodata != -1)
                        break;
        }
        if (i == vmmEhdr->e_shnum)
                goto end;

        if (patchIoBackdoor() != EXIT_SUCCESS)
                goto end;
        if (patchIdeDevices() != EXIT_SUCCESS)
                goto end;

        if ((sectionVmm = malloc(256 * 1024)) == NULL)
                goto end;
        memset(sectionVmm, '\0', 256 * 1024);
        size = sizeof(Elf32_Ehdr);
        for (i = 0; i < vmmEhdr->e_shnum; i++)
        {
                if(strcmp(&vmmShstrtab[vmmShdr[i].sh_name], ".bss"))
                {
                        if (i == indexZtext && sectionZtext)
                                memcpy(&sectionVmm[size], sectionZtext, vmmShdr[i].sh_size);
                        else if (i == indexZrodata && sectionZrodata)
                                memcpy(&sectionVmm[size], sectionZrodata, vmmShdr[i].sh_size);
                        else
                                memcpy(&sectionVmm[size], &vmmBinary[vmmShdr[i].sh_offset], vmmShdr[i].sh_size);
                        vmmShdr[i].sh_offset = size;
                        size += vmmShdr[i].sh_size;
                }
        }
        vmmEhdr->e_shoff = size;
        memcpy(&sectionVmm[0], vmmEhdr, sizeof(Elf32_Ehdr));
        memcpy(&sectionVmm[size], vmmShdr, vmmEhdr->e_shentsize * vmmEhdr->e_shnum);
        size += vmmEhdr->e_shentsize * vmmEhdr->e_shnum;
        vmxShdr[indexVmm].sh_size = size;

        if ((file = freopen("/usr/lib/vmware/bin/vmware-vmx", "wb", file)) == NULL)
                goto end;
        if ((newBinary = malloc(2560 * 1024)) == NULL)
                goto end;
        memset(newBinary, '\0', 2560 * 1024);
        i = (indexVbios < indexVmm) ? indexVbios : indexVmm;
        size = vmxShdr[i].sh_offset;
        memcpy(&newBinary[0], &vmxBinary[0], size);
        for (; i < vmxEhdr->e_shnum; i++)
        {
                if (i == indexVbios && sectionVbios)
                        memcpy(&newBinary[size], sectionVbios, vmxShdr[i].sh_size);
                else if (i == indexVmm && sectionVmm)
                        memcpy(&newBinary[size], sectionVmm, vmxShdr[i].sh_size);
                else
                        memcpy(&newBinary[size], &vmxBinary[vmxShdr[i].sh_offset], vmxShdr[i].sh_size);
                vmxShdr[i].sh_offset = size;
                size += (((vmxShdr[i].sh_size - 1) / vmxShdr[i].sh_addralign) + 1) * vmxShdr[i].sh_addralign;
        }
        vmxEhdr->e_shoff = size;
        memcpy(&newBinary[0], vmxEhdr, sizeof(Elf32_Ehdr));
        memcpy(&newBinary[size], vmxShdr, vmxEhdr->e_shentsize * vmxEhdr->e_shnum);
        size += vmxEhdr->e_shentsize * vmxEhdr->e_shnum;

        fseek(file, 0, SEEK_SET);
        if (fwrite(newBinary, 1, size, file) != size)
                goto end;

        error = EXIT_SUCCESS;
end:
        if (newBinary)
                free(newBinary);
        if (sectionVmm)
                free(sectionVmm);
        if (sectionZrodata)
                free(sectionZrodata);
        if (sectionZtext)
                free(sectionZtext);
        if (sectionVbios)
                free(sectionVbios);
        if (vmxBinary)
                free(vmxBinary);
        fclose(file);

        return error;
}

Current thread: