LINUX.ORG.RU

Чтение заголовка ФС USB в загружаемом модуле ядра

 , , ,


0

1

Добрый день Занимаюсь проектом, в котором мне необходимо прочитать заголовок файловой системы подключенной USB (по сути, первые 512 байт). После этого мне необходимо проверить полученную последовательность на наличие ключа - специального слова. Делаю я это в загружаемом модуле ядра Linux. Есть следующий код

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/usb/storage.h>
#include <linux/usb.h>

char *buffer;

static struct usb_endpoint_descriptor *find_bulk_in_endpoint(struct usb_device *udev) {
    struct usb_host_interface *iface_desc;
    struct usb_interface *iface;
    struct usb_endpoint_descriptor *endpoint;
    int i, j;

    for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
        iface = udev->actconfig->interface[i];
        iface_desc = iface->cur_altsetting;
        if (iface_desc->desc.bInterfaceClass == 0x08) {
            pr_info("Found storage interface:\n");
            pr_info("  Interface Number: %d\n", iface_desc->desc.bInterfaceNumber);
            pr_info("  Number of Endpoints: %d\n", iface_desc->desc.bNumEndpoints);
            for (j = 0; j < iface_desc->desc.bNumEndpoints; j++) {
                endpoint = &iface_desc->endpoint[j].desc;
                if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK &&
                    (endpoint->bEndpointAddress & USB_DIR_IN))
                    return endpoint;
            }
        }
    }
    pr_err("No bulk IN endpoint found\n");
    return NULL;
}

int read_first_sector(struct usb_device *udev) {
    struct usb_endpoint_descriptor *bulk_in_endpoint = find_bulk_in_endpoint(udev);

    if (!bulk_in_endpoint)
        return -ENODEV;

    u8 buf[512];
    int ret, actual_size;
    unsigned char scsi_cmd[10] = { 
        0x28,
        0x00,
        0x00,
        0x00,
        0x00,
        0x01,
        0x00,
        0x00,
        0x00,
        0x00
    };

    ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x28, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, 0,(void *)scsi_cmd, sizeof(scsi_cmd), 1000);
    if (ret < 0) {
        printk(KERN_ERR "USB SCSI READ command failed: %d\n", ret);
        return ret;
    }
    ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, bulk_in_endpoint->bEndpointAddress), buf, 512, &actual_size, 1000);
    if (ret < 0) {
        printk(KERN_ERR "USB bulk read failed: %d\n", ret);
        return ret;
    }
    printk(KERN_INFO "First sector data:\n");
    for (int i = 0; i < 512; i++) {
        printk(KERN_CONT "%02x ", buf[i]);
        if ((i + 1) % 16 == 0)
            printk(KERN_CONT "\n");
    }
    return 0;
}

static int usb_notify(struct notifier_block *self, unsigned long action, void *dev) {
    struct usb_device *usb_dev = dev;

    switch (action) {
        case USB_DEVICE_ADD:
            printk(KERN_INFO "USB device connected: Vendor ID=%04x, Product ID=%04x\n",
                      usb_dev->descriptor.idVendor, usb_dev->descriptor.idProduct);
            printk(KERN_INFO "Path: %s\n", dev_name(&usb_dev->dev));
            read_first_sector(usb_dev);
            break;
        case USB_DEVICE_REMOVE:
            printk(KERN_INFO "USB device disconnected: Vendor ID=%04x, Product ID=%04x\n",
                      usb_dev->descriptor.idVendor, usb_dev->descriptor.idProduct);
            break;
        default:
            break;
    }
    return NOTIFY_OK;
}

static struct notifier_block usb_notifier = {
    .notifier_call = usb_notify
};

static int __init usb_device_list_init(void) {
    printk(KERN_INFO "Listing all connected USB devices:\n");
    usb_register_notify(&usb_notifier); 
    return 0;
}

static void __exit usb_device_list_exit(void) {
    usb_unregister_notify(&usb_notifier);
    printk(KERN_INFO "USB device listing module exiting.\n");
    kfree(buffer); 
}

late_initcall(usb_device_list_init);
module_exit(usb_device_list_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("USB Device Module");
MODULE_AUTHOR("MyName");

Собственно я обрабатываю подключение нового USB устройства, оно происходит нормально. Затем я пытаюсь прочитать первые 512 байт с флешки с помощью SCSI, но возникает ошибка. А именно 55 строка (я вызываю usb_control_msg) возвращает код ошибки -11. Может ли это возникать из-за неправильного значения параметра bRequest, не уверен, что должен передавать туда 0х28 Вывод dmesg длинный, поэтому залил на пастбин: https://pastebin.com/XG1rESHy

Записывал слово на флешку с помощью:

echo -n "USBSECRET" | sudo dd of=/dev/sdb bs=1 count=10 conv=notrunc

Заранее благодарю!


Меня смущает, что вы посылаете scsi-cmd через control message устройству в целом, тогда как оно должно отправляться конечной точке (bulk-out) MSD-интерфейса. Control-запросы в MSD нужны только чтобы узнать количество LUN-ов и поресетить все.

Вы же ознакомились с BBB-протоколом? Ну, что в out-точку посылается запрос, потом out/in для передачи/приема данных и in для статуса. Он ведь тем и хорош, что вообще весь обмен с MSD (кроме, как я в начале сказал, количества LUN и reset) идет только через две bulk-точки.

Но я с MSD-устройствами работал с другой стороны, со стороны устройств.

UPD. А ведь перед scsi запросом еще MSC_CBW передается…

COKPOWEHEU
()
Последнее исправление: COKPOWEHEU (всего исправлений: 1)
Ответ на: комментарий от COKPOWEHEU

Под стандартными вы подразумеваете не через загружаемый модуль? Я смотрел первые 512 байт в том же терминале через hexdump, но мне нужен именно загружаемый модуль ядра.

Мне не обязательно использовать SCSI, можно работать как с блочным устройством, но тогда вопрос, как получить block_device имея на руках только usb_device

GMOrk
() автор топика
Ответ на: комментарий от GMOrk

Под стандартными вы подразумеваете не через загружаемый модуль?

Да. Я имею в виду запустить wireshark, а потом через какой-нибудь dd считать первый сектор. И смотреть какие именно байты будут переданы по USB. По сути, получите шаблон MSC_CBW и SCSI-запроса.

Мне не обязательно использовать SCSI, можно работать как с блочным устройством

А с ним вы иначе работать и не сможете.

как получить block_device имея на руках только usb_device

У вас же есть код получения номера интерфейса MSD. У вас есть код получения номеров endpoint-ов. Через них и работайте.

Я ж говорю - посмотрите как работа с MSD идет штатными средствами ОС, и повторите ту часть, которая вам нужна.

COKPOWEHEU
()
Ответ на: комментарий от COKPOWEHEU

Вот, например, как происходит чтение средствами ОС при втыкании флешки

Host -> 1.16.2 (bulk out)
//Служебные байты wireshark. Для остальных ответов буду их удалять. ЭТИ БАЙТЫ НЕ ПЕРЕДАЮТСЯ
0000   c0 1c 11 52 80 91 ff ff 53 03 02 11 01 00 2d 00   ...R....S.....-.
0010   05 bd 84 67 00 00 00 00 00 a6 01 00 8d ff ff ff   ...g............
0020   1f 00 00 00 1f 00 00 00 00 00 00 00 00 00 00 00   ................
0030   00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00   ................
//Собственно запрос Read 10. Вот эти данные уже передаются
0040   55 53 42 43 0d 00 00 00 00 10 00 00 80 00 0a 28   USBC...........(
0050   00 00 00 00 00 00 00 08 00 00 00 00 00 00 00      ...............

1.16.1 (bulk in) -> Host
//Ответ (Read 10). Поскольку ОС запросила аж 8 секторов, большую часть я поскипал
0000   80 74 92 0e 81 91 ff ff 43 03 81 11 01 00 2d 00   .t......C.....-.
0010   05 bd 84 67 00 00 00 00 83 a8 01 00 00 00 00 00   ...g............
0020   00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00   ................
0030   00 00 00 00 00 00 00 00 01 02 00 00 00 00 00 00   ................
0040   eb 58 90 6d 6b 66 73 2e 66 61 74 00 02 08 20 00   .X.mkfs.fat... .
...
1030   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................

1.16.1 (bulk in) -> host
//Подтверждение
0040   55 53 42 53 0d 00 00 00 00 00 00 00 00            USBS.........
COKPOWEHEU
()
Ответ на: комментарий от COKPOWEHEU

Посмотрел трафик через wireshark, перечитал BOT. Получается я должен выполнить следующую последовательность, чтобы прочитать первый сектор. Для начала нужно отправить MSC_CBW на OUT endpoint

struct usb_msc_cbw cbw = {
    .dSignature = 0x43425355,  // "USBC"
    .dTag = 0x12345678,
    .dDataLength = 512,
    .bmFlags = 0x80,           // IN
    .bLun = 0, 
    .bLength = 10,
    .CB = { 0x28, 0, 0, 0, 0, 0, 0, 0, 1, 0 }, // прочитать первый сектор
};

Отправляю через usb_bulk_msg, в качестве третьего параметра функции передаю как раз экземпляр структуры msc_cbw

Затем читаю данные в буфер из IN endpoint-а

struct usb_endpoint_descriptor *bulk_in_endpoint = find_bulk_in_endpoint(udev);
...
ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, bulk_in_endpoint->bEndpointAddress), buf, 512, &actual_size, 1000);

После прочтения данных, нужно получить MSC_CSW с результатом выполнения

struct usb_msc_csw {
    uint32_t dSignature;   // 0x53425355 (USBS)
    uint32_t dTag;         // должно совпадать с тегом в MSC_CBW
    uint32_t dDataResidue; // Остаток данных (если чтение/запись не завершены)
    uint8_t  bStatus;      // Статус (0x00 = SUCCESS, 0x01 = FAIL)
};

читаю так же через usb_bulk_msg из IN endpoint-а

GMOrk
() автор топика
Ответ на: комментарий от GMOrk

Получается я должен выполнить следующую последовательность

Не знаю. Я работал со стороны девайса, а не со стороны хоста.

.dDataLength = 512,

Оно разве не в секторах задается? Даже в моем примере там 0x08 было, кажется, то есть 4096 байт.

Отправляю через usb_bulk_msg

Затем читаю данные в буфер из IN endpoint-а

После прочтения данных, нужно получить MSC_CSW

И? Ваш алгоритм не работает или что?

COKPOWEHEU
()