Sunday, December 20, 2015

Better visualization of data formats using assembly POC's to better implement them in C

Are you tired of staring at hex dumps, white papers and manual pages? Want to feel like you truly know how a data type like a file format, or a disk partition LOOKS like. Well I can't help you with the manual pages and white papers, (https://staff.washington.edu/dittrich/misc/fatgen103.pdf, http://www.eit.lth.se/fileadmin/eit/courses/eitn50/Projekt1/FAT12Description.pdf) but here is a great example of implementing a fat12 floppy image in assembly.

;////////////////////////////////////////////////////////////////////////////////
;// THE SCOTCH-WARE LICENSE (Revision 0):
;// <aaronryool@gmail.com> wrote this file. As long as you retain this notice you
;// can do whatever you want with this stuff. If we meet some day, and you think
;// this stuff is worth it, you can buy me a shot of scotch in return
;////////////////////////////////////////////////////////////////////////////////
; This assembles to a floppy disk image
[bits 16]
[org 0x7C00]

;;;;;;;;;;;;;;;;;;;;;;;;
; BIOS Parameter Block
jmp short boot_code
nop
db "MadOSv01"          ; OEM identifier
dw 512                 ; Bytes per sector
db 1                   ; sectors per cluster
dw 1                   ; reserved sectors
db 1                   ; number of fats
dw 112                 ; directory entries
dw 1280                ; logical sectors
db 0xFB                ; media descriptor type
dw 1                   ; sectors per fat, fat12/fat16 only
dw 18                  ; sectors per track
dw 2                   ; number of heads / sides on media
dd 0                   ; number of hidden sectors
dd 0                   ; Large amount of sector on media. This field is set if there are more than 65535 sectors in the volume.

; Extended Boot Record
db 0                   ; drive number
db 0                   ; reserved / NT flags (maybe use for MadOS, More research needed)
db 0x29                ; signiture, 0x28 or 0x29 for fat12 /fat16
dd 0x1337              ; volume serial number
db "MadOS Disk "       ; volume label
db "FAT12   "          ; identifier string

; Boot code
boot_code:
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, boot_code_end + (1024 * 4)

    lea si, [message]
    call print_string
    jmp $

print_string:
    lodsb
    or al, al
    jz done
    mov ah, 0x0E
    int 0x10
    jmp print_string
done:
   ret

message:
    db "MadOS disk not bootable...", 0xd, 0xa

;dw 0xAA55 ; bootable partition signiture, uncomment to make the floppy boot
boot_code_end:
times 450 - (boot_code_end - boot_code) db 0

;;;;;;;;;;;;;;;;;;;;;
; DATA AREA START

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; FAT entries
; an entry is 3 nibbles:
;   000         -> unused
;   0xFF0-0xFF6 -> reserved cluster
;   0xFF7       -> bad cluster
;   0xFF8-0xFFF -> last cluster in file
;   ***         -> next data cluster

fat1:
    db 0xFB, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0x00, 0x00
    ;    0     1     2     3     4     5     6     7     8     9     10    11    12    13
    times 512 - ($ - fat1) db 0
fat2:
    times 512 - ($ - fat2) db 0
;;;;;;;;;;;;;;;;;;
; root directory
root:
    folder:
        db "folder     "
        db 0x20 | 0x10         ; attributes: a, d
        dw 0                   ; reserved
        dw 0xA496              ; creation time (h,m,s)
        dw 0x4793              ; creation date
        dw 0x4793              ; access date
        dw 0                   ; high order bits of first cluster address, 0 on fat12
        dw 0xA496              ; write time (h,m,s)
        dw 0x4793              ; write date
        dw 3                   ; low order bits of first cluster address
        dd 64                  ; file size

    file:
        db "file       "
        db 0x20                ; attributes: a
        dw 0                   ; reserved
        dw 0xA496              ; creation time (h,m,s)
        dw 0x4793              ; creation date
        dw 0x4793              ; access date
        dw 0                   ; high order bits of first cluster address, 0 on fat12
        dw 0xA496              ; write time (h,m,s)
        dw 0x4793              ; write date
        dw 2                   ; low order bits of first cluster address
        dd 40                  ; file size

; padd out the rest of the root directory
times (512 * 4) - ($ - root) db 0


times 1024 db 0     ; first two data blocks are reserved

; 2 file
file_data:
    db "This is a file in the root directory :D", 0xa
; cluster padding
times 512 - ($ - file_data) db 0

; 3 folder
folder_data:
    file2:
        db "file2      "
        db 0x20                ; attributes: a
        dw 0                   ; reserved
        dw 0xA496              ; creation time (h,m,s)
        dw 0x4793              ; creation date
        dw 0x4793              ; access date
        dw 0                   ; high order bits of first cluster address, 0 on fat12
        dw 0xA496              ; write time (h,m,s)
        dw 0x4793              ; write date
        dw 4                   ; low order bits of first cluster address
        dd 34                  ; file size
    folder2:
        db "folder2    "
        db 0x20 | 0x10         ; attributes: a, d
        dw 0                   ; reserved
        dw 0xA496              ; creation time (h,m,s)
        dw 0x4793              ; creation date
        dw 0x4793              ; access date
        dw 0                   ; high order bits of first cluster address, 0 on fat12
        dw 0xA496              ; write time (h,m,s)
        dw 0x4793              ; write date
        dw 5                   ; low order bits of first cluster address
        dd 32                  ; file size
; cluster padding
times 512 - ($ - file2) db 0

; 4 file2
file2_data:
    db "This is a file in a sub directory", 0xa
; cluster padding
times 512 - ($ - file2_data) db 0


; 5 folder2_data
folder2_data:
    file3:
        db "file3      "
        db 0x20                ; attributes: a
        dw 0                   ; reserved
        dw 0xA496              ; creation time (h,m,s)
        dw 0x4793              ; creation date
        dw 0x4793              ; access date
        dw 0                   ; high order bits of first cluster address, 0 on fat12
        dw 0xA496              ; write time (h,m,s)
        dw 0x4793              ; write date
        dw 6                   ; low order bits of first cluster address
        dd 1024                ; file size
; cluster padding
times 512 - ($ - folder2_data) db 0

; 6 file3_data
file3_data:
times 512 - ($ - file3_data) db 'A'

; 7 file3_data2
file3_data2:
times 512 - ($ - file3_data2) db 'B'




;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; padd out the rest of the floppy
times ((1024 * 1024) + 461373) - ($ - $$) db 0

So from this exploration, we can construct the following header file as a start:

////////////////////////////////////////////////////////////////////////////////
// THE SCOTCH-WARE LICENSE (Revision 0):
//  wrote this file. As long as you retain this notice you
// can do whatever you want with this stuff. If we meet some day, and you think
// this stuff is worth it, you can buy me a shot of scotch in return
////////////////////////////////////////////////////////////////////////////////

#ifndef __FAT_H__
#define __FAT_H__

#include <stdint.h>
#include <machine.h>

typedef struct bpb {
    char jmp_code[3];
    char oem[8];
    uint16_t bytes_per_sector;
    uint8_t sectors_per_cluster;
    uint16_t reserved_sectors;
    uint8_t fats;
    uint16_t dir_entries;
    uint16_t logical_sectors;
    uint8_t media_type;
    uint16_t sectors_per_fat;
    uint16_t sectors_per_track;
    uint16_t heads;
    uint32_t hidden_sectors;
    uint32_t large_sectors;
} bpb_t;

typedef struct ebr {
    uint8_t drive;
    uint8_t rflags;
    uint8_t signiture;
    uint32_t serial;
    char label[11];
    char identifier[8];
    char boot_code[448];
    uint16_t boot_sign;
} ebr_t;

typedef struct fat12_partition_info {
// see fat12.s for an implementation
// in assembly for visualization :D

    bpb_t bpb;
    ebr_t ebr;
    char data[];
} fat12_partition_info_t;

typedef struct fat_dir_entry {
    char name[11];
    uint8_t attributes;
    uint16_t reserved;
    uint16_t creation_time;
    uint16_t creation_date;
    uint16_t access_date;
    uint16_t caddr_high;
    uint16_t write_time;
    uint16_t write_date;
    uint16_t caddr_low;
    uint32_t size;
} fat_dir_entry_t;

const uint8_t fat12_format_stub[122] = {
  0xEB, 0x3C, 0x90, 0x4D, 0x61, 0x64, 0x4F, 0x53, 0x76, 0x30,
  0x31, 0x00, 0x02, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x37,
  0x13, 0x00, 0x00, 0x4D, 0x61, 0x64, 0x4F, 0x53, 0x20, 0x44,
  0x69, 0x73, 0x6B, 0x20, 0x46, 0x41, 0x54, 0x31, 0x32, 0x20,
  0x20, 0x20, 0x31, 0xC0, 0x8E, 0xD8, 0x8E, 0xC0, 0x8E, 0xD0,
  0xBC, 0x7A, 0x8C, 0x8D, 0x36, 0x5E, 0x7C, 0xE8, 0x02, 0x00,
  0xEB, 0xFE, 0xAC, 0x08, 0xC0, 0x74, 0x06, 0xB4, 0x0E, 0xCD,
  0x10, 0xEB, 0xF5, 0xC3, 0x4D, 0x61, 0x64, 0x4F, 0x53, 0x20,
  0x64, 0x69, 0x73, 0x6B, 0x20, 0x6E, 0x6F, 0x74, 0x20, 0x62,
  0x6F, 0x6F, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x2E, 0x2E, 0x2E,
  0x0D, 0x0A
};


/////////////////////////////////////////////
// FAT12 entries                           //
/////////////////////////////////////////////
// an entry is 3 nibbles:                  //
//   000         -> unused                 //
//   0xFF0-0xFF6 -> reserved cluster       //
//   0xFF7       -> bad cluster            //
//   0xFF8-0xFFF -> last cluster in file   //
//   ***         -> next data cluster      //
/////////////////////////////////////////////

typedef char fat12_t[];
#define GET_FAT12_ENTRY(n, fat)\
    (n % 2 == 0 ? fat[((3 * n) / 2)] | (fat[1 + (3 * n) / 2] & 0x0F) << 8 : \
    (fat[(3 * n) / 2] & 0xF0) >> 4 | fat[1 + (3 * n) / 2] << 4)


#endif


No comments :

Post a Comment