фокусы покусы реального режима - что будет если

Тема в разделе "WASM.ZEN", создана пользователем Rockphorr, 4 мар 2010.

  1. diamond

    diamond New Member

    Публикаций:
    0
    Регистрация:
    21 май 2004
    Сообщения:
    507
    Адрес:
    Russia
    Ах да, запись слова по адресу FFFF уже обсуждалась на форуме: http://wasm.ru/forum/viewtopic.php?pid=335591#p335591 , правда, там на одном компе запись почему-то успешно производилась без #GP, что я склонен списать на то, что BIOS после загрузки оставила нереальный режим (известно, что такие BIOSы бывают, а кое-где вроде даже настройка есть; противоречащих этой версии данных в том топике не было).
     
  2. SII

    SII Воин против дзена

    Публикаций:
    0
    Регистрация:
    31 окт 2007
    Сообщения:
    1.483
    Адрес:
    Подмосковье
    diamond
    Об этом не подумал. Тогда именно это и объясняет, почему закольцовка имела место на 8086/88 -- там же никаких лимитов нет, поскольку защищённый режим не предусмотрен вообще.
     
  3. Rockphorr

    Rockphorr Well-Known Member

    Публикаций:
    0
    Регистрация:
    9 июн 2004
    Сообщения:
    2.623
    Адрес:
    Russia
    diamond
    но тогда если настрить обработчик #gp кольцевание будет и в защищенном режиме
     
  4. Rockphorr

    Rockphorr Well-Known Member

    Публикаций:
    0
    Регистрация:
    9 июн 2004
    Сообщения:
    2.623
    Адрес:
    Russia
    *настроить
     
  5. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    diamond
    Классное объяснение. Пара моментов, правда смущают.
    1) Странно, что при #GP сохраняется в стек только ip, а восстанавливается уже zero-extended ip в eip.
    2)
    Если 16-битный код, расположенный в таком ip/eip-оверлее, содержит, например, call (E8 00 00) или даже простой jmp (E9 00 00 или EB 00), то на нём почему-то возникает исключение.
    Rockphorr
    Так и есть. Хотя это уже не так интересно. Вот пример 16-битного кода, работающего в защищённом режиме. Обработчик обрезает eip до ip:
    Код (Text):
    1. format PE GUI 4.0
    2. entry start
    3. include 'win32a.inc'
    4.  
    5. macro enum start*,[name]
    6. {
    7.     common
    8.             local cntr
    9.             cntr = start
    10.     forward
    11.             name = cntr
    12.             cntr = cntr + 1
    13. }
    14. enum 0,ExceptionContinueExecution,ExceptionContinueSearch,ExceptionNestedException,ExceptionCollidedUnwind
    15.  
    16. SIZE_OF_80387_REGISTERS             = 80
    17. MAXIMUM_SUPPORTED_EXTENSION         = 512
    18.  
    19. struct FLOATING_SAVE_AREA
    20.     ControlWord         dd ?
    21.     StatusWord          dd ?
    22.     TagWord             dd ?
    23.     ErrorOffset         dd ?
    24.     ErrorSelector       dd ?
    25.     DataOffset          dd ?
    26.     DataSelector        dd ?
    27.     RegisterArea        db SIZE_OF_80387_REGISTERS dup(?)
    28.     Cr0NpxState         dd ?
    29. ends
    30. struct CONTEXT
    31.     ContextFlags        dd ?
    32.     iDr0                dd ?
    33.     iDr1                dd ?
    34.     iDr2                dd ?
    35.     iDr3                dd ?
    36.     iDr6                dd ?
    37.     iDr7                dd ?
    38.     FloatSave           FLOATING_SAVE_AREA <>
    39.     regGs               dd ?
    40.     regFs               dd ?
    41.     regEs               dd ?
    42.     regDs               dd ?
    43.     regEdi              dd ?
    44.     regEsi              dd ?
    45.     regEbx              dd ?
    46.     regEdx              dd ?
    47.     regEcx              dd ?
    48.     regEax              dd ?
    49.     regEbp              dd ?
    50.     regEip              dd ?
    51.     regCs               dd ?
    52.     regFlag             dd ?
    53.     regEsp              dd ?
    54.     regSs               dd ?
    55.     ExtendedRegisters   db MAXIMUM_SUPPORTED_EXTENSION dup(?)
    56. ends
    57.  
    58. remoteCodeOffset = 10008h-remoteCode.size
    59.  
    60. LDT_Entry:
    61. dw 0FFFFh    ;Младшие 16 бит лимита
    62. .base1:
    63. db 0h,0h,0h  ;Младшие 24 бита базы
    64. .type:
    65. db 11111010b     ;P: 1, DPL: 11 (ring3), S: 1, Type: 1010
    66. db 00000000b     ;G: 0 - лимит в байтах, D/B: 0, L: 0, AVL: 0, 16-19 биты лимита: 0000
    67. .base2:
    68. db 0         ;Cтарший байт базы
    69.  
    70. start:
    71.     invoke VirtualAlloc,NULL,20000h,MEM_COMMIT,PAGE_EXECUTE_READWRITE
    72.         mov dword[mem_ptr],eax
    73.         mov dword[eax],0FEEBh
    74.  
    75.         lea edi,[eax+remoteCodeOffset]
    76.         mov esi,remoteCode
    77.         mov ecx,remoteCode.size
    78.         rep movsb
    79.  
    80.         mov edx,eax
    81.         and eax,000FFFFFFh
    82.         shr edx,24
    83.         or dword[LDT_Entry.base1],eax
    84.         or dword[LDT_Entry.base2],edx
    85.         invoke NtSetLdtEntries,27h,dword[LDT_Entry],dword[LDT_Entry+4],0,0,0
    86.         and byte[LDT_Entry.type], not 1000b
    87.         invoke NtSetLdtEntries,2Fh,dword[LDT_Entry],dword[LDT_Entry+4],0,0,0
    88.         mov bx,2Fh
    89.  
    90.         push exHandler
    91.         push dword[fs:0]
    92.         mov [fs:0],esp
    93.  
    94.         jmp 27h:remoteCodeOffset
    95.         beBack:
    96.  
    97.         pop dword[fs:0]
    98.         pop eax
    99.  
    100.         invoke MessageBox,0,gotBack,gotBack,MB_OK
    101.     invoke VirtualFree,dword[mem_ptr],0,MEM_RELEASE
    102. invoke ExitProcess,0
    103.  
    104. proc exHandler c uses ebx,exRecord:DWORD,exFrame:DWORD,exContext:DWORD,dispContext:DWORD
    105.     mov ebx,dword[exContext]
    106.    
    107.     cinvoke wsprintf,eipStr,fmtStr,[ebx+CONTEXT.regEip]
    108.     mov byte[eipStr+eax],13
    109.     invoke MessageBox,0,except1,exceptTitle,MB_ICONEXCLAMATION
    110.    
    111.     mov ecx,dword[ebx+CONTEXT.regEip]
    112.     and ecx,0FFFFh
    113.     mov dword[ebx+CONTEXT.regEip],ecx
    114.     mov eax,ExceptionContinueExecution
    115.     ret
    116. endp
    117.  
    118.  
    119. data import
    120.     library kernel32,'KERNEL32.DLL',\
    121.         user32,'USER32.DLL',\
    122.         ntdll,'ntdll.dll'
    123.     import kernel32,\
    124.         VirtualAlloc,'VirtualAlloc',\
    125.         VirtualFree,'VirtualFree',\
    126.         ExitProcess,'ExitProcess'
    127.     import ntdll,\
    128.         NtSetLdtEntries,'NtSetLdtEntries'
    129.     import user32,\
    130.         MessageBox,'MessageBoxA',\
    131.         wsprintf,'wsprintfA'
    132. end data
    133.  
    134. exceptTitle db 'Exception',0
    135. except1     db 'Caught exception. Exception eip: '
    136. eipStr      db 8 dup 0
    137. except2     db 13,10,'Truncating eip...',0
    138. fmtStr      db '%-8X',0
    139.  
    140. gotBack     db 'Got back',0
    141.  
    142. align 16
    143. mem_ptr      dd ?
    144.  
    145. align 16
    146. remoteCode:
    147.     USE16
    148.     org remoteCodeOffset
    149.     .start:
    150.         mov ss,bx
    151.             mov ebx,esp
    152.             mov sp,100h
    153.             push 'ck'
    154.             push 'Fu'
    155.             call @F      ;db 0E8h,0,0
    156.             @@:
    157.             mov esp,ebx
    158.         push 23h
    159.         pop ss
    160.     USE32
    161.         db 66h
    162.         jmp 1Bh:beBack
    163.     .end:
    164.     .size = .end-.start
    Если выставить remoteCodeOffset = 10000h-remoteCode.size вместо remoteCodeOffset = 10008h-remoteCode.size, то управление успешно возвращается в 32-битный кодовый сегмент.
     
  6. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    diamond
    Нет. Гоню. Ещё интереснее. Исключение не возникает, но вот eip обрезается до ip.
     
  7. diamond

    diamond New Member

    Публикаций:
    0
    Регистрация:
    21 май 2004
    Сообщения:
    507
    Адрес:
    Russia
    l_inc
    1) Раз уж процессору требуется установить (беззнаковый) 32-битный регистр eip по 16-битным данным, есть ровно два разумных варианта: добить нулями либо не менять старшего слова. При генерации исключения второй способ имеет очевидный недостаток - вместо адреса обработчика из таблицы управление может уйти в космос, так что логично использовать первый вариант, где адрес в таблице таки полностью определяет полный eip. После этого, собственно, поведение iret уже не имеет особого значения - старшая половина eip уже занулена, а старый вариант безвозвратно потерян.
    2) У near jmp и call есть неочевидная фича - они реагируют на атрибут размера операнда: если он равен 16, то вычисленный адрес обрезается по 16 битам. Канон по этому поводу говорит следующее (второй том интеловских мануалов, описание call; для jmp аналогично):
    Поскольку кодовый сегмент 16-битный, а префикс 66h отсутствует, то EB 00 по адресу cs:10000 вычисляет значение 10002, обрезает его до 2 и прыгает на cs:2. Дальше понятно: если специально начало сегмента не заполнять, то получается прыжок на мусор со всеми вытекающими последствиями. Инструкция с явным переопределением разрядности 66 EB 00 ведёт себя нормально (в смысле, переходит на cs:10003, а не на cs:3).
     
  8. diamond

    diamond New Member

    Публикаций:
    0
    Регистрация:
    21 май 2004
    Сообщения:
    507
    Адрес:
    Russia
    Неудачно выразился - сама по себе зависимость от разрядности в случае jmp/call imm16/imm32 сомнений не вызывает, ибо определяет длину самой команды. Неочевидно, что зависимость этим не ограничивается :)
     
  9. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    diamond
    Блин... верно. :) Не подумал, что обрезается он ещё на этапе передачи управления обработчику, а не на iret.
    Но мне всё равно больше нравится третий разумный вариант :): в 16-битном кодовом сегменте работать исключительно с ip, не трогая верхнее слово eip (соответственно инструкции исполнять тоже по cs:ip). Тогда вариантов с передачей управления на обработчик тоже бы не возникало.
    Действительно. Но красивая фича. Если в вышеприведенном коде слегка расширить лимит (бит 16, например, выставить), чтобы исключение не возникало, то достаточно перед дальним прыжком приписать вроде как ничего не меняющий EB 00, как управление уже не вернётся в 32-битный сегмент, а произойдёт прыжок на 65K назад. Всё таки хоть что-то... на антиотладку под Windows очень неплохо тянет. :)
    Ну для коротких прыжков эта зависимость также имеется хоть и не определяет длину инструкции.
    Большое спасибо за точные объяснения.
     
  10. diamond

    diamond New Member

    Публикаций:
    0
    Регистрация:
    21 май 2004
    Сообщения:
    507
    Адрес:
    Russia
    +вопрос из параллельного топика
    По сути эти вопросы означают "а почему в Intel сделали именно так". Вариант "пусть реальный режим функционирует точно так же, как в 8088/80186, полностью игнорируя старшее слово eip, а используя только ip" тоже имеет право на существование. Каких-либо кардинальных превосходств одного над другим, требующим выбрать именно этот подход, лично я не вижу. Знакомых инженеров, принимавших участие в проектировании ранних x86, у меня нет, так что точный ответ неизвестен. Но можно выдвигать гипотезы:
    1. Инженеры из Intel были не в восторге от необходимости поддержки двух экземпляров всех регистров общего назначения ax+eax, ..., di+edi (требующих соответствующей логики по склеиванию старшей и младшей частей) и решили хоть в случае с непосредственно неадресуемым eip не мучаться.
    2. Инженеры из Intel захотели сделать логику обработки eip, одинаковую для всех режимов, как реального, так и защищённого. Всё-таки eip - это не только 32-битное число, но и управление выборкой команд, очередь предвыборки и прочие связанные вещи.
    3. Инженеры из Intel подумали, что закольцовывание ip - это всё-таки дополнительная логика где-то в процессоре, а почти такого же эффекта можно добиться и без реализации этой логики, используя только логику проверки лимита, которую всё равно нужно реализовывать для защищённого режима. Ну и отключать проверки в реальном режиме не нужно.
    4. Нереальный режим (- это не баг, это фича) специально заложен ещё при проектировании, а не документирован по следующим причинам (галочки расставлять, основываясь на предпочитаемой теории заговора):
    - Intel решила, что знание - сила, и не захотела делиться всей полнотой информации;
    - Intel решила, что введение защищённого режима само по себе является неслабой нагрузкой на психику разработчиков и сообщать, что ещё и с хорошо знакомым реальным режимом можно мухлевать, будет перегрузкой нежной психики программистов, которую нужно беречь;
    - Intel взяла пример с Microsoft и решила включить побольше недокументированных фич;
    - в результате банального разгильдяйства идеологи про нереальный режим что-то сказали, а в документации написали что-то невнятно, так что инженеры это учитывали, а редакторы документации забили.
    5. Инженеры из Intel вспомнили про всякие вещи типа инструкции loadall и подумали, что пусть набор данных, полностью описывающий состояние процессора, будет одинаков во всех режимах, а раз так, то пусть уж эти данные таки используются, включая старшее слово eip и теневые сегментные регистры.

    Впрочем, нужно ещё учитывать, что защищённый режим и 32-битные регистры появились не одновременно, что ещё более замутняет картину.

    Как вариант, можно поставить вроде как вечный цикл EB FE :)
     
  11. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    diamond
    Не... :) Я это не имел в виду. Как сделали, так сделали, и меня это устраивает. Мало того: если бы и были какие-то сверхсерьёзные преимущества нереализованного в IA-32 подхода, меня бы это устраивало в той же степени: как сделали, так сделали, это право Intel. Про "третий разумный вариант" я упомянул просто в оппозицию Вашей фразе "есть ровно два разумных варианта" без рассчёта на продолжение дискуссии на эту тему. :)
    Не... в этом месте бы не подошло. Там EB FE попало бы на адрес cs:FF FE, что привело бы действительно к зацикливанию, а вот EB 00 на этом адресе как раз и приводит к сбросу eip до нуля. К тому же в целях антиотладки ИМХО разумнее пустить реверсера по длинному ложному пути, чем привести его к очевидно неверному тупику (а EB FE - это тупик). С другой стороны 74 XX, где XX — ненулевое смещение, ведущее в ip-оверлей даёт целых два неверных ложных пути потока инструкций. :)
    Кстати...
    А как насчёт обратного? Можно намухлевать, чтобы в защищённом режиме помещался в стек только ip? Подозреваю, что для этого соответствующий уровню привилегий из дескриптора прерывания селектор сегмента стека, указанный в TSS, должен указывать на 16-битный дескриптор. Или ерунду говорю?
    А то я так ответил Rockphorr, что в защищённом режиме закольцовывание можно сделать, но по сути смухлевал, т.к. реально сбрасывает верхнее слово eip у меня обработчик, а не внутренние механизмы процессора.
     
  12. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    [offtop]
    P.S. Дурацкие запятые! Постоянно забываю их ставить по окончании (дее-)причастных оборотов, а потом сам же и спотыкаюсь, т.к. нунечитаеццоже!
    [/offtop]
     
  13. diamond

    diamond New Member

    Публикаций:
    0
    Регистрация:
    21 май 2004
    Сообщения:
    507
    Адрес:
    Russia
    l_inc
    Глава 17.1 "Defining 16-bit and 32-bit program modules" канона по этому поводу говорит
    Так что при обработке прерываний и исключений (call far - отдельный случай) имеет значение только бит разрядности в шлюзе IDT - если он нулевой, то обработчик 16-битный и ожидает 16-битные же сохранённые значения cs:ip:flags, так что eip обрезается до ip, а если 1, то обработчик 32-битный и ожидает cs:eip:eflags, так что eip помещается полностью.
     
  14. Rockphorr

    Rockphorr Well-Known Member

    Публикаций:
    0
    Регистрация:
    9 июн 2004
    Сообщения:
    2.623
    Адрес:
    Russia
    тогда вариант
    в 32 разрядном сегменте перед int ставим префикс 66h и пытаемся войти в 16 шлюз
     
  15. Rockphorr

    Rockphorr Well-Known Member

    Публикаций:
    0
    Регистрация:
    9 июн 2004
    Сообщения:
    2.623
    Адрес:
    Russia
    *16 разрядный
     
  16. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    diamond
    Ясно, спасибо. Ошибочно решил, что способ сохранения информации на стек во время исключения определяется размером стека обработчика, а не разрядностью его кода.
     
  17. Rockphorr

    Rockphorr Well-Known Member

    Публикаций:
    0
    Регистрация:
    9 июн 2004
    Сообщения:
    2.623
    Адрес:
    Russia
    l_inc
    так мой вариант с префиксом у int не прокатит ???
     
  18. Black_mirror

    Black_mirror Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2002
    Сообщения:
    1.035
    Rockphorr
    В 16 разрядный шлюз войти можно из любого сегмента, только вот от eip сохраняются только младшие 16 бит, поэтому в 32х разрядный сегмент корректно вернуться получится далеко не всегда.
     
  19. Rockphorr

    Rockphorr Well-Known Member

    Публикаций:
    0
    Регистрация:
    9 июн 2004
    Сообщения:
    2.623
    Адрес:
    Russia
    Black_mirror
    так l_inc того и хотел
     
  20. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    Rockphorr
    Эм... прокатит, только независимо от того, будет ли у int префикс и от того, какой разрядности исходный сегмент. :) К тому же вариант с int не настолько интересен, как возникновение исключения. И далеко не настолько интересен, как чистое закольцовывание (т.е. без возникновения исключения) с прыжком вида EB 00 прямо под границей сегмента.
    Black_mirror
    Дык интересно то, что и в 16-разрядный сегмент тоже не всегда получится вернуться "корректно". Хотя в нашем случае такой "некорректный" возврат как раз и является целью. :)