Разбор заголовков LUKS для правильного чтения полей целочисленных значений

Я пытаюсь проанализировать заголовок luks, читая необработанные данные с устройства с установленным на нем томом luks, следуя приведенной здесь спецификации: https://gitlab.com/cryptsetup/cryptsetup/wikis/LUKS-standard/on-disk-format.pdf, в частности стр. 6 с таблицей, показывающей данные, находящиеся в каждом месте, тип данных и количество этих типов данных для одного значения.

Например, строка спецификации хэша находится в ячейке 72 и содержит 32 байта типа char. Собрать это в массив и распечатать результат просто, однако, как подробно описано в таблице для числовых значений, таких как версия или ключевые байты (которые предположительно являются длиной ключа), эти значения охватывают несколько целых чисел. Версия имеет два беззнаковых шорта, а ключевые байты имеют четыре беззнаковых целых числа для представления их значений.

Я несколько смущен этим и тем, как мне следует интерпретировать его, чтобы получить правильное значение. Я написал беспорядочный тестовый скрипт для сканирования USB-накопителя, зашифрованного с помощью luks, и отображения того, что было получено при чтении этих полей.

256
25953
hash spec:
sha256
key bytes (length):
1073741824
3303950314
1405855026
1284286704

Это очень сбивает с толку, так как снова поле спецификации хэша содержит ожидаемое значение, просто строку символов, но как я должен интерпретировать поля версии или ключевого байта? Оба они кажутся совершенно случайными числами, и, насколько я могу судить, в спецификации нет ничего, что бы это объясняло. Тогда я подумал, что это может быть проблемой из-за того, как я на самом деле пишу код для этого, ниже приведен сценарий, используемый для отображения этих значений:

#include <stdio.h>

int main()  {

    unsigned short data[100];
    unsigned char data2[100];
    unsigned int data3[100];
    int i;

    FILE *fp;

    fp = fopen("/dev/sdd1", "rb");

    fseek(fp, 6, SEEK_SET);

    if (fp) {
        for (i=0; i < 2; i++)   {
            fread(&data[i], sizeof(short), 1, fp);
        }

        fseek(fp, 72, SEEK_SET);

        for (i=0; i < 32; i++)  {
            fread(&data2[i], sizeof(char), 1, fp);
        }

        fseek(fp, 108, SEEK_SET);

        for (i=0; i < 4; i++)   {
            fread(&data3[i], sizeof(int), 1, fp);
        }

        printf("version:\n");
        for (i=0; i < 2; i++)   {
            printf("%u\n", data[i]);
        }
        printf("hash spec:\n");
        for (i=0; i < 32; i++)  {
            printf("%c", data2[i]);
        }
        printf("\n");
        printf("key bytes (length):\n");
        for(i=0; i < 4; i++)    {
            printf("%u\n", data3[i]);
        }

        fclose(fp);
    }
    else {
        printf("error\n");
    }

    return 0;
}

Любая помощь будет оценена, спасибо.


person muke    schedule 24.11.2019    source источник


Ответы (1)


Проблема в том, что данные, которые вы читаете, имеют обратный порядок байтов, а компьютер, на котором вы работаете, — обратный порядок байтов. Например, байты, которые вы печатаете как 1073741824, — это 0x00, 0x00, 0x00 и 0x40 в указанном порядке. Для числа с прямым порядком байтов это 0x00000040 или 64. Для числа с прямым порядком байтов, которое обычно используется в системах x86, это 0x40000000, абсурдно большая длина.

К счастью, есть функции, которые могут преобразовать эти значения для вас. Чтобы преобразовать 32-битный формат с обратным порядком байтов (nсетевой порядок байтов) в формат вашей системы (hостальный порядок байтов), используйте ntohl, а для 16-битного целое, используйте ntohs.

Итак, когда вы читаете данные для 16-битных целых чисел, это будет выглядеть так:

for (i=0; i < 2; i++)   {
    fread(&data[i], sizeof(short), 1, fp);
    data[i] = ntohs(data[i]);
}

В качестве примечания, если вы собираетесь работать со значениями фиксированных размеров, будет немного более переносимо и легче понять, если вы сделаете #include <stdint.h>, а затем используете типы uint8_t, uint16_t и uint32_t. Они всегда будут подходящего размера, поскольку встроенные типы могут различаться в зависимости от платформы.

Если вам интересно узнать больше о порядке следования байтов, в Википедии есть статья об этом.

person bk2204    schedule 24.11.2019
comment
Ах, это сработало, спасибо. Версия перешла к 1, за которой следует 24933, а ключевые байты к 256, 25953, 0, 0. Предположительно, это только первое число каждого из них имеет значение - у вас есть какие-либо идеи, почему все еще может быть более одного целого числа выделено для этих значений, если только первое имеет значение? - person muke; 24.11.2019
comment
Вы должны получить 1 в качестве первого значения, если оно работает правильно. Но проблема в том, что я не думаю, что вы правильно читаете спецификацию; когда говорится, что длина версии равна 2, это означает 2 байта (1 uint16_t), а не 2 uint16_ts. Вы читаете имя шифра как следующее целое число. - person bk2204; 24.11.2019