BSOD при перезагрузке GDT

Тема в разделе "WASM.NT.KERNEL", создана пользователем KonstantinBart, 3 дек 2007.

  1. KonstantinBart

    KonstantinBart New Member

    Публикаций:
    0
    Регистрация:
    3 дек 2007
    Сообщения:
    20
    Всем доброго времени суток!
    У меня возникла потребность поменять таблицу GDT на собственную.
    В драйвере юзаю такой вот код (нахожусь в винде в защищенном режиме соответственно), вызываю допустим initGDT():

    ---------------------------------------------------------
    typedef unsigned long long int uint64_t;
    typedef unsigned int uint32_t;
    typedef unsigned short int uint16_t;
    typedef unsigned char uint8_t;

    typedef struct
    {
    uint16_t limit;
    uint16_t baseLow;
    uint8_t baseMid;
    uint16_t flags;
    uint8_t baseHigh;
    } GDTEntry;

    typedef struct
    {
    uint16_t limit;
    uint32_t base;
    } GDTPtr;

    GDTPtr* _gdtp;

    ---------------------------------------------------------

    void initGDT()
    {
    GDTEntry gdt[4];
    setGDTEntry(&gdt[0], 0, 0, 0); //Null descriptor
    setGDTEntry(&gdt[1], 0xFFFF, 0, 0x9AAF); //64 Bit Kernel Code
    setGDTEntry(&gdt[2], 0xFFFF, 0, 0x92CF); //Kernel Data
    setGDTEntry(&gdt[3], 0xFFFF, 0, 0x9ACF); //32 Bit Kernel Code
    _gdtp = (GDTPtr*)(&gdt[3]);
    _gdtp->limit = 4 * sizeof(GDTEntry);
    _gdtp->base = (uint32_t)(gdt);

    __asm
    {
    cli
    in al, 70h
    or al, 80h
    out 70h, al

    lgdt [_gdtp]

    in al, 70h
    and al, 7Fh
    out 70h, al
    sti
    }
    }

    ---------------------------------------------------------

    void setGDTEntry(GDTEntry* gdt, uint16_t limit, uint16_t base, uint16_t flags)
    {
    gdt->limit = limit;
    gdt->baseLow = (base & 0xFFFF);
    gdt->baseMid = (uint8_t)((base & 0xFF0000)>>16);
    gdt->baseHigh = (uint8_t)((base & 0xFF000000)>>24);
    gdt->flags = flags;
    }

    ---------------------------------------------------------

    Соответственно, при выполнении команды lgdt у меня вываливается синий экран смерти, хотя вроде бы все прерывания отключены и маскируемые и немаскируемые!

    Может я что то забыл сделать до перезагрузки таблицы?
    Если можно - то приведите пожалуйста код, который я упустил.
    Заранее благодарен.
     
  2. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    ну ты химик... четвертый элемент таблицы у тебя и элемент, и сам GDTR...
    записи у тебя всего 4, хотя винде их нужно гораздо больше, например для того же fs нету.. короче логику твоего кода я гдето потерял.
    Вообще с чего это вдруг возникла потребность GDT перезагружать?
     
  3. KonstantinBart

    KonstantinBart New Member

    Публикаций:
    0
    Регистрация:
    3 дек 2007
    Сообщения:
    20
    Да вот почитал статью тут: http://sergh.pisem.net/protected/02_segments.html и похоже коряво попробовал перевести асмовский код в С-шный...
    А вообще мне нужно находясь в винде перехватить управление на себя и запустить Линух...вот я и копаю в районе переинициализации системных таблиц! Если с IDT все вроде получилось, то с GDT - никак....
    Кстати, тестировал я на реальной машине но с запущенным SoftICE и на виртуальной без оного....и в обоих случаях BSOD!
    Что можете посоветовать?
     
  4. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    ясен пень, что бсод, я уже сказал, почему предположительно.

    бсод именно на lgdt или где? ты вообще не пробовал анализ крешдампа делать? отладчики видимо не для нас писаны.
    у тебя меняется гдт на новую, но, не восстанавливается обратно, это раз.. корректность записей я вообще не смотрел еще..
    для тестирования смены гдт я предлагаю последовательность:
    - disable interrupts
    - set new GDT
    - do something (nop nop nop)
    - restore old GDT
    - enable interrupts
    разумеется и сегментные регистры стоит перезагружать, если код собирается хоть куда-нибудь обращаться.

    Не буду даже спрашивать насчет цели такого мероприятия. А не катит вариант перевестись в реальный режим и оттуда стартануть линукс, чтобы он сам все заполнил?
     
  5. KonstantinBart

    KonstantinBart New Member

    Публикаций:
    0
    Регистрация:
    3 дек 2007
    Сообщения:
    20
    Так вот именно, что после установки новой GDT мне надо уже передать управление линуксовому загрузчику и не возвращаться к "старой" таблице!

    Это тоже вариант...только у меня в невыгружаемой памяти сидят еще всякие структуры...а после перехода в реальный режим я так понимаю я к ним обратиться уже не смогу.
    В принципе моя задача, не перегружая комп стартануть Линух....а после того, как перегружусь - опять восстановилась винда!
    Может у тебя есть пример кода для нормального перехода в реальный режим?
    Я вот такое пробовал:
    mov eax, cr0
    and al, 0FEh
    mov cr0, eax
    но ничего путного не получил!
     
  6. rei3er

    rei3er maxim

    Публикаций:
    0
    Регистрация:
    15 янв 2007
    Сообщения:
    917
    Адрес:
    minsk
    KonstantinBart
    1. GDTEntry gdt[4] лежит в стеке и после выхода из initGDT вполне возможно изменение ее (таблицы GDT) содержимого (вызовами других функций например)
    2. GDT ты создал, но есть ли соответствие селекторов дескрипторам?
    т. е если к примеру селектор сегмента кода в шлюзах/дескрипторах в IDT не совпадает с 1 << 3 или 3 << 3, то при любом событии, для обработки которого процессор использует IDT, сей факт приведет сначала к #DF, а следом и к triple fault
     
  7. rei3er

    rei3er maxim

    Публикаций:
    0
    Регистрация:
    15 янв 2007
    Сообщения:
    917
    Адрес:
    minsk
    очень напоминает виртуализацию
    если никаким образом не виртуализировать (aka контролировать) среду старта и выполнения Linux, то после передачи ему управления Windows ты потом уже не восстановишь
    если CPU поддерживает VMX, это будет самый лучший вариант
     
  8. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    да у него пока бсод вроде, а не ребут..

    а ему и не надо как я понял. ребут и винда снова в памяти)
     
  9. rei3er

    rei3er maxim

    Публикаций:
    0
    Регистрация:
    15 янв 2007
    Сообщения:
    917
    Адрес:
    minsk
    ))
     
  10. KonstantinBart

    KonstantinBart New Member

    Публикаций:
    0
    Регистрация:
    3 дек 2007
    Сообщения:
    20
    Верно, винда мне больше не будет нужна...мне просто надо как-нибуь корректно избавиться от винды (не важно останется в памяти какая-либо её часть или нет)...и передать управление на Линух.
    Пытался это делать в процедуре обработки IRP_MJ_SHUTDOWN...даже переинициализировал таблицу прерываний на одну и ту же ф-цию (в ней просто идет бесконечный цикл) ... вроде всё нормально - винда висит до принудительной перезагрузки...но мне надо же как-нибудь перейти на Линух! Я вот поэтому решил избавиться от GDT заодно...
    Может есть какой-то более правильный вариант передачи управления из винды Линуху? Думаю, что задача совсем не сложная...просто моего опыта не хватает!!!
    Так что может кусок кода меня спасет? Как лучше всего это сделать?
    Заранее благодарен.
     
  11. n0name

    n0name New Member

    Публикаций:
    0
    Регистрация:
    5 июн 2004
    Сообщения:
    4.336
    Адрес:
    Russia
    а просто выйти из PM и передать управление загрузчику линуха?
    другое дело что могут не сохранится данные на диск.
     
  12. KonstantinBart

    KonstantinBart New Member

    Публикаций:
    0
    Регистрация:
    3 дек 2007
    Сообщения:
    20
    Ну просто не получится передать управление...загрузчик считывает информацию с MBR-а...
    А потом он всякие разные вещички делает, типа формирования файловой системы и т.п.
    И к тому же переписывать загрузчик - не есть хорошая идея (ИМХО)...

    Попытался перейти в реальный режим:
    mov eax, cr0
    and al, 0FEh
    mov cr0, eax
    jmp REAL_ENTRY

    REAL_ENTRY:
    mov ax, cs
    mov ds, ax
    mov ss, ax
    mov es, ax

    Ну и теперь моя виртуальная машина просто висит и всё...

    Может всё-таки кто-нибудь знает как правильно заменить виндовскую GDT на свою? Желательно на масме :)
     
  13. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    тебе ж уже сказали как улучшить твой код.
     
  14. KonstantinBart

    KonstantinBart New Member

    Публикаций:
    0
    Регистрация:
    3 дек 2007
    Сообщения:
    20
    Я вот тут нашел такой код, но он зараза валится еще когда инициализируешь "дескриптор сегмента данных" а потом на инициализации gdtr:

    .386p
    .model tiny
    public _GetCr0@0
    .code

    _GetCr0@0:
    jmp short _main

    _main:

    ; Сегментный дескриптор
    segment_descriptor struct
    limit_low dw 0 ; Младшие два байта поля Segment limit
    base_low dw 0 ; Младшие два байта поля Base Address
    base_high0 db 0 ; Второй байт поля Base Address
    type_and_permit db 0 ; Флаги
    flags db 0 ; Ещё одни флаги
    base_high1 db 0 ; Старший байт поля Base Address
    segment_descriptor ends

    ; Регистр, описывающий GDT
    table_register struct
    limit dw 0 ; Table Limit
    base dd 0 ; Linear Base Address
    table_register ends

    ; Глобальная таблица дескрипторов
    GDT label byte

    ; Нулевой дескриптор
    segment_descriptor <>

    ; Дескриптор сегмента данных, базовый адрес 0, размер 4Gb, Read-Write
    segment_descriptor <0ffffh, 0, 0, 10010010b, 10001111b, 0>
    ; 10010010b - 1001, C/D - 0, 0, R/W - 1, 0
    ; 10001111b - G - 1, 000, Limit - 1111

    ; Данные для загрузки в GDTR
    gdtr table_register <$ - GDT - 1, 0>


    ; Подготавливаем DS
    push cs
    pop ds

    ; Открываем вентиль A20
    ;call open_A20

    ; Запрещаем прерывания
    call disable_interrupts

    ; Инициалиируем GDT
    call initialize_gdt

    ; загрузить новый селектор в регистр FS
    mov ax, 8
    mov fs, ax

    call enable_interrupts

    ret

    ; Открывает вентиль A20
    open_A20:
    in al, 92h
    or al, 2
    out 92h, al
    ret

    ; Запрещает маскируемые и немаскируемые прерывания
    disable_interrupts:
    cli ; запретить прерывания
    in al, 70h ; индексный порт CMOS
    or al, 80h ; установка бита 7 в нем запрещает NMI
    out 70h, al
    ret

    ; Инициализирует GDT
    initialize_gdt:
    ; Вычисляем линейный адрес начала массива дескрипторов
    call cs_to_eax
    add eax, offset GDT
    ; Записываем его в структуру
    mov dword ptr gdtr.base, eax

    ; Загружаем GDTR
    lgdt fword ptr gdtr
    ret

    ; Разрешает маскируемые и немаскируемые прерывания
    enable_interrupts:
    in al, 70h ; индексный порт CMOS
    and al, 7Fh ; сброс бита 7 отменяет блокирование NMI
    out 70h, al
    sti ; разрешить прерывания
    ret

    ; Вычисляет линейный адрес начала сегмента кода
    cs_to_eax:
    mov eax, 0
    mov ax, cs
    shl eax, 4
    ret

    end _GetCr0@0

    Вроде бы автор обещает рабочий код...
    Я внедрил вызов _GetCr0 в драйвер в DriverEntry.
    Что можно с этим сделать?
     
  15. KonstantinBart

    KonstantinBart New Member

    Публикаций:
    0
    Регистрация:
    3 дек 2007
    Сообщения:
    20
    Ну тут посоветовали главным образом переход в реальный режим....но есть одно "но"....я из 3-го кольца собираюсь передавать ядро Линуха в неперемещаемую память винды.....а потом с этим ядром работать...
    Ну чтобы не изменять загрузчик Линуха!

    В общем у меня такие вот мысли:
    1. В 3-м кольце я формирую ядро Линуха в неперемещаемой памяти винды. Получаю физический адрес структуры для этого ядра и сохраняю в переменной драйвера.
    2. В обработчике IRP_MJ_SHUTDOWN я переинициализирую GDT и IDT. Заменяю GDT на промежуточную таблицу с 2-мя дескрипторами: нулевым и данных соответственно. Все обработчики прерываний в IDT настраиваю на одну и ту же функцию, к-я ничего не делает. Получается я перехватил управление на себя.
    3. Перемещаю дальше сразу за 1-м мегабайтом ядро Линуха из неперемещаемой памяти винды.
    4. Делаю jmp на загрузку непосредственно линусковых GDT, IDT и т.д.
    5. Передаю управление Линуху.

    Может я что-то мудрю (даже не может, а точно)....:)
     
  16. rei3er

    rei3er maxim

    Публикаций:
    0
    Регистрация:
    15 янв 2007
    Сообщения:
    917
    Адрес:
    minsk
    не считывает
    все нужные данные зашиты в код boot-сектора
    (по крайней мере в GRUB)
    не понимаю, зачем что-то менять?
    берешь boot-сектор GRUB (или LILO), записываешь его по адресу 0x7C00
    переходишь в реальный режим и передаешь управление на 0x7C00
    для перехода в реальный режим можно воспользоваться INIT#
    Код (Text):
    1. mov eax, dword [0xFEE00020]
    2. and eax, 0xFF000000
    3. mov dword [0xFEE00310], eax
    4. mov dword [0xFEE00300], 0x4500
     
  17. n0name

    n0name New Member

    Публикаций:
    0
    Регистрация:
    5 июн 2004
    Сообщения:
    4.336
    Адрес:
    Russia
    rei3er
    это же винда. а ты хочешь по физ адресу считывать/записывать.
    дял винды apic промаппен на 0xfffe0000.
     
  18. rei3er

    rei3er maxim

    Публикаций:
    0
    Регистрация:
    15 янв 2007
    Сообщения:
    917
    Адрес:
    minsk
    n0name
    у меня пример без привязки к конкретной ОС
    естественно в конкретной ОС нужно уточнять адрес
     
  19. KonstantinBart

    KonstantinBart New Member

    Публикаций:
    0
    Регистрация:
    3 дек 2007
    Сообщения:
    20
    У меня по заданию нельзя менять ни граб ни загрузчик! К тому же всё для Линуха должно находиться только в оперативке...никаких HDD!
    Думаю, что все-таки надо копать в области перезагрузки системных таблиц...
    И причем этот вариант менее гемморойный (ИМХО), чем переписывать загрузчик!
     
  20. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    KonstantinBart
    блин не надоничего менять! берешь граб и записываешь в память по физическому адресу 0x7C00
    потом делаешь #INIT или снимаешь CR0.PE и переходишь на 7c00