PE. Оставшиеся вопросы.

Тема в разделе "WASM.BEGINNERS", создана пользователем l_inc, 1 мар 2008.

  1. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    Я тут пару дней назад почитал про PE. Из всех статей больше всего понравилась вторая часть "От зеленого к красному". На проверку знаний написал небольшой инфектор методом добавления секции на C++... вроде работает неплохо. Плюс в качестве учебного материала, как граничный случай PE, разбирал 97 байт Mikl__'а. Вот в основном по ним и вопросы.
    1) В нескольких статьях, в том числе "От зеленого к красному", написано о Bound-импорте, как о директории, которая может мешать дозаписать заголовок очередной секции, но больше ни о каких директориях не написано. Может ли какая-нибудь другая директория следовать сразу за таблицей секций? Может Certificate Table или TLS Table...
    Или часто ли сам Bound-импорт принадлежит какой-то другой области файла?
    2) Насколько критично наличие оврелея у PE при его инфицировании методами расширения или добавления секций? Т.е., наверняка, очень критично. Есть ли вообще общие методики инфицирования таких PE этими методами?
    3) Уверен, что на этот вопрос ответ "Да", но на всякий случай переспрошу. В вышеупомянутой статье написано, что флаги характеристик секций IMAGE_SCN_MEM_EXECUTE и IMAGE_SCN_MEM_READ эквивалентны. Означает ли это, что Windows не различает и не может различить читаемую и исполняемую память и при этом не может контролировать исполнение только читаемой секции (области памяти) или чтение только исполняемой?
    4) В статье написано, что FileAlignment должно быть между 200h и 10000h. Но кем это контролируется и верно ли это вообще? Вряд ли загрузчиком, т.к. 97-байтный PE Mikl__'а успешно исполняется при SectionAlignment = FileAlignment = 4 байтам! Или это связано с отсутствием там таблицы секций?
    5) Каким значением ограничивается пустота между таблицей секций и началом первой секции в файле? SizeOfHeaders, выравненный на FileAlignment? Но в статье написано, что SizeOfHeaders = DOS Stub + PE Header + Object Table. А как же тогда учитывается тот же Bound-импорт в этой пустоте? Или он обязательно должен умоститься в этот промежуток?
    6) Я так понял, что любая директория данных (DataDirectory) может либо принадлежать одной из секций (в частности являться секцией) либо находиться в "пустоте" между таблицей секций и началом первой секции. Есть еще какие-нибудь варианты расположения какой-либо директории вне секций?
    7) Можно ли между секциями оставлять пустоты (не считая тех, которые остаются от FileAlignment в файле или от SectionAlignment в RAM)? Т.е. пусть при FileAlignment = 200h первая секция имеет атрибуты PointerToRawData = 400h и SizeOfRawData = 200h. Корректным (об экономности речь не идет) ли будет тогда вариант, когда ближайшая к ней секция будет иметь PointerToRawData = 1000h? Считается ли тогда пробел между этими секциями оверлеем, и будет ли этот пробел загружен в память? Можно ли там хранить одну из директорий данных? Аналогичный вопрос для SectionAlignment = 200h, VirtualAddress (первой секции) = 400h, VirtualSize (первой секции) = 200h и VirtualAddress (ближайшей к первой секции) = 1000h. Будет ли такой PE корректным?
    8) Зачем нужно было делать отдельно директории данных и отдельно секции, причем часто с пересекающимися функциями? Т.е. я понимаю, что загрузчик может обращаться к IMAGE_DATA_DIRECTORY ради ускорения работы, но зачем тогда было делать отдельную секцию .reloc или .rsrc? Не легче было выделить в директории данных основные куски файла, а все остальное, что захочется пользователю, называть секциями? И как объяснить, что ресурсы согласно IMAGE_DATA_DIRECTORY могут иметь один размер, а согласно IMAGE_SECTION_HEADER другой?
    Вроде еще вопросы были... забыл. :)
     
  2. n0name

    n0name New Member

    Публикаций:
    0
    Регистрация:
    5 июн 2004
    Сообщения:
    4.336
    Адрес:
    Russia
    1. Это как решит тот кто собирает файл, можно хоть директорию импорта разместить сразу после.
    3. неа, читай про DEP.
    4. FileAlignment должен быть кратен 0x200, либо должен быть равен SectioinAlignment. Ну и конечно они должны быть кратны 2ке. Проверяется загрузчиком конечно.
    5. bound-импорт это никак не хидер, что хочу то и пишу в этот промежуток.
    6. Теоретически нет. Однако практически можно попробовать разместить между секциями.
    7. корректным будет, однако не уверен что загрузится в память.
     
  3. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    n0name
    1. Ясно... единственное, что... в общем там еще допвопрос в этом пункте.
    3. Про DEP почитал. Но почему тогда пишут, что "IMAGE_SCN_MEM_EXECUTE и IMAGE_SCN_MEM_READ эквивалентны"? Или это только там, где NX не поддреживается аппаратно?
    4. Т.е. он может быть любым, если равен SectionAlignment? Да... и не кратным двойке, а, я так понял, быть степенью двойки.
    5. Здесь вроде ясно, хотя довольно нестрого получается. Т.е. как будто при разработке формата как раз расчитывалось на существование дыр, связанных с FileAlignment, в которые будет распихиваться все что нужно файлу, причем без рассчета на то, что дыр в принципе может и не хватить. Хотя в принципе ничего не мешает выделить отдельную секцию, если дыр не хватает.
    6. Как же между секциями, если в седьмом пункте Вы сами пишете, что не уверены, что этот пробел загрузится в память? А если в этом пробеле таблица импорта, то это будет довольно критично для работоспособности программы.
    7. Ясно.

    Ну и все еще в ожидании ответов на нераскрытые вопросы...
     
  4. diamond

    diamond New Member

    Публикаций:
    0
    Регистрация:
    21 май 2004
    Сообщения:
    507
    Адрес:
    Russia
    Прежде всего, следует заметить, что PE-загрузчик в семействе 9x и в семействе NT немного различаются. В частности, 9x сочтёт инвалидным "учебный материал, как граничный случай PE, - 97 байт Mikl__'а" (ну хотя бы потому, что там SectionAlignment обязано быть не менее страницы). Далее считаем, что 9x уже давно не в моде, и работаем исключительно с NT.
    1. Здесь есть два различных вопроса: что в принципе может происходить, и что происходит в реальных программах, создаваемых реальными компиляторами. В принципе любая директория может следовать сразу за таблицей секций, и винда такой бинарник прекрасно загрузит. На практике такое бывает только с Bound Import (по крайней мере, за примерами с Bound Import далеко лазить не надо - \windows\system32), но закладываться на это в общем случае может быть чревато.
    2. С оверлеями проблема не в загрузке, проблема в следующем. Винда при загрузке вообще не обращает внимание на так называемый оверлей, а грузит без него. И если он всё же присутствует, значит, уже сама программа самостоятельно открывает свой exe-шник и считывает из него данные. И, скорее всего, смещение начала данных жёстко прошито где-то в самой программе. Так что при наличии мощного дизассемблера, способного определить, где же это смещение сидит, - пожалуйста, его можно изменить :) (...а ведь таких смещений может быть и больше одного...)
    3. Статья писалась до появления DEP. Раньше это было действительно так. Сейчас - нет.
    4. Есть два существенно различных случая: SectionAlignment >= 0x1000 (PageSize) и SectionAlignment < 0x1000. Во втором случае обязано выполняться равенство FileAlignment=SectionAlignment и все адреса секций в файле обязаны совпадать с RVA.
    5. Если мне не изменяет память, то SizeOfHeaders, выравненный вверх на SectionAlignment. Утверждение, что SizeOfHeaders равен сумме - неверно.
    6. Любая используемая директория (за исключением Security) должна быть загружена в память (Security существует исключительно в файле, и у неё в таблице директорий FileOffset вместо RVA). Далее - пункт 7.
    7. Если SectionAlignment<0x1000, то см. п.4. В противном случае файловые смещения могут быть абсолютно произвольными (кажется, они всё же должны быть выравнены на FileAlignment, хотя не уверен), могут быть дыры, могут быть перекрытия. Но в памяти всегда программа должна идти одним цельным куском (с учётом выравнивания вверх на SectionAlignment), начиная с PE-заголовка размером SizeOfHeaders (выравненного).
    8. Это две разные концепции, используемые разными участками загрузчика:
    - таблица секций используется ядром при проецировании PE в память, назначения страницам нужных атрибутов, установки соответствия RVA <-> FileOffset
    - таблица директорий используется user-mode частью загрузчика (ntdll.dll)
    Секции и директории могут быть не связаны напрямую. Единственное исключение - если используется ресурс TYPELIB, то директория ресурсов обязана располагаться в начале секции с именем ".rsrc".
    Размер, указанный в таблице секций, означает размер секции и существенен для загрузки PE, но, вообще говоря, не имеет отношения к структурам файла. Размер, указанный в таблице директорий, наоборот, не имеет отношения к загрузке PE (директория потенциально может размещаться даже в двух или больше секциях, идущих подряд), но задаёт размер соответствующей структуры. Впрочем, последний размер не обязан быть всегда корректным - он имеет значение для Export, но не для остальных директорий, так что загрузчик его имеет полное право вообще игнорировать.
     
  5. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    diamond
    Вау! Классный ответ! Почти все ясно. Теперь что осталось:
    1. Здесь ничего. Вопрос исчерпан.
    2. Насчет того, что оверлей не грузится загрузчиком и используется исключительно согласно нуждам самой программы - очевидно. Только... в общем это скорее вопрос к пункту о дырах в образе. И надо понимать, что общей методики для инфицирования подобных файлов нет, если не считать способ с меганавороченным дизассемблером с серьезными претензиями на звание ИИ.
    3. Вопрос исчерпан.
    4.
    В смысле совпадать с RVA? А если DOS Stub - это огроменная мегапрограмма под дос, то файловое смещение первой секции же не будет совпадать с FileAlignment (SectionAlignment). Но сам PE-то грузится по ImageBase только начиная с PE-сигнатуры. Таким образом файловые смещения секций по идее должны быть больше, чем RVA. К тому же, как Вы сами написали далее, между секциями в файле могут присутствовать дыры, что в еще большей степени увеличит различия между RVA и PointerToRawData. Или я ошибаюсь?
    В остальном исчерпано, хотя насчет разграничивания случаев SectionAlignment >= 0x1000 и SectionAlignment < 0x1000 я не видел даже в pecoff_v8.doc. Попробую попроверять необходимость равенства SectionAlignment и FileAlignment для случаев SectionAlignment < 0x1000.
    5. В pecoff_v8.doc написано вот что:
    Этот файл - разве не самый достоверный из всех официальных источников?
    6. Вопрос напрямую связан с дырами в седьмом. Т.е. я так и не понял, можно ли помещать директорию между секциями? И будет ли она загружена в память, если я ее помещу в оверлей?
    7. Так и не понял, можно ли считать дыры между секциями в файле оверлеем и загрузятся ли эти дыры в память. Насчет цельного куска в памяти... это по крайней мере логично и в некоторой степени подтверждается тем, что SizeOfImage проверяется загрузчиком, как VirtualSize + VirtualAddress последней секции.
    8. Почти исчерпано. Мне только не понравилась фраза
    , т.к. во многих статьях убедительно настаивают на том, что имена секций вообще никакого значения не имеют. Просто подтвердите, пожалуйста, Вашу фразу, если имя ".rsrc" действительно важно.
     
  6. asmfan

    asmfan New Member

    Публикаций:
    0
    Регистрация:
    10 июл 2006
    Сообщения:
    1.004
    Адрес:
    Abaddon
    diamond откуда такие подробности? (реверсили загрузчик?)
    А лучше на практике попробовать, а не принимать на веру. Создать в фасме секций 5 разных с разным физ. и вирт.размером, а затем в hex-редакторе понаделать дырок и оверлеев в вирт.секциях и проверить загрузится или нет. Потом можно пропробовать повыделять (Alloc/commit) эти самые страницы-дырки в рантайме получится или нет.
    Тем самым и будет проверена инфа по непрерывности вирт.простр. под загруженным ехе.
    Про ".rsrc" тут много на форуме написано.

    2all: В каких системах DEP по дефолту работает для каждого загруженного файла?
     
  7. Joes

    Joes New Member

    Публикаций:
    0
    Регистрация:
    5 янв 2008
    Сообщения:
    98
    Про 7: Если я не ошибаюсь, старый добрый CIH себя между секциями записывал. Но это было во времена 9x...
     
  8. diamond

    diamond New Member

    Публикаций:
    0
    Регистрация:
    21 май 2004
    Сообщения:
    507
    Адрес:
    Russia
    4. Образ PE-файла в памяти начинается с MZ-заголовка, а не с PE-заголовка.
    6. Нельзя. У дыр, которые есть в файле, но не принадлежат ни одной секции, просто нет RVA.
    7. В файле между секциями могут быть дыры, которые тоже можно считать оверлеем, которые не принадлежат ни одной из секций и, следовательно, не будут загружены в память.
    8. Например, http://wasm.ru/article.php?article=packlast01 - только там неточное утверждение, что секция ".rsrc" нужна вообще всегда - в действительности она нужна только для ресурса TYPELIB.

    Самый достоверный из вообще всех источников - это ntoskrnl.exe (MiCreateImageFileMap) :) Все эти сведения я почерпнул именно оттуда :)
    Вот точные проверки:
    1) Первые два байта файла - сигнатура MZ (если не так, MiCreateImageFileMap и NtCreateSection вернут STATUS_INVALID_IMAGE_NOT_MZ);
    2) Смещение PE-заголовка не больше размера файла и не больше (unsigned)-0x378 (STATUS_INVALID_IMAGE_PROTECT);
    3) Первые 4 байта PE-заголовка - сигнатура PE\0\0 (STATUS_INVALID_IMAGE_PROTECT либо STATUS_INVALID_IMAGE_WIN_16 либо STATUS_INVALID_IMAGE_NE_FORMAT);
    4) поле Machine ненулевое (дальше будет более конкретная проверка) либо SizeOfOptionalHeader ненулевой (STATUS_INVALID_IMAGE_PROTECT);
    5) PE-заголовок должен быть выравнен на границу dword (STATUS_INVALID_IMAGE_FORMAT);
    6) бит IMAGE_FILE_EXECUTABLE_IMAGE в поле Characteristics должен быть установлен (STATUS_INVALID_IMAGE_FORMAT);
    7) Сигнатура Magic в OptionalHeader должна быть IMAGE_NT_OPTIONAL_HDR_MAGIC (я реверсил 32-битную систему; в 64-битной, надо полагать, допустимо ещё одно значение);
    8) FileAlignment >= 0x200 либо FileAlignment == SectionAlignment (STATUS_INVALID_IMAGE_FORMAT);
    9) FileAlignment ненулевой, степень двойки и не превосходит SectionAlignment (STATUS_INVALID_IMAGE_FORMAT);
    10) SizeOfImage <= 0x77000000 (повторюсь, для 32-битной системы);
    11) NumberOfSections <= 96;
    12) Machine == IMAGE_FILE_MACHINE_I386 (если быть точным, то в XP это проверяется здесь, в 2k это проверяется в другом месте, но проверяется);
    13) В XP если в таблице директорий присутствует 14-й элемент (COM-дескриптор) (в смысле, VirtualAddress и Size оба ненулевые), то в LoaderFlags считается установленным 0-й бит;
    Дальше начинается собственно работа с секциями. Если ImageAlignment < 0x1000 (размера страницы), создаётся одна большая подсекция; в противном случае число подсекций равно числу секций + 1 (на заголовок).
    Под XP принимается во внимание поле LoaderFlags (возможно, поORенное с 1).
    14) ImageBase должно быть выравнено на границу 0x10000 (STATUS_INVALID_IMAGE_FILE_FORMAT);
    15) SizeOfHeaders < SizeOfImage (STATUS_INVALID_IMAGE_FORMAT);
    I. Если ImageAlignment < 0x1000:
    Единственная подсекция, включающая в себя весь файл, атрибуты защиты - MM_EXECUTE_WRITECOPY; для каждой секции в файле должно быть выполнено следующее: сложение PointerToRawData+SizeOfRawData не вызывает (беззнакового) переполнения; PointerToRawData == VirtualAddress; VirtualSize <= SizeOfRawData (STATUS_INVALID_IMAGE_FORMAT).
    II. Если SectionAlignment >= 0x1000:
    первая подсекция - PE-заголовок, размер - SizeOfHeaders, выравненный вверх на границу SectionAlignment, атрибуты защиты - MM_READONLY;
    остальные подсекции - это PE-секции, для каждой из которых проверяется следующее:
    SectionVirtualSize = ALIGN_UP((VirtualSize==0)?SizeOfRawData:VirtualSize, SectionAlignment);
    сложение PointerToRawData с SizeOfRawData не вызывает переполнения; (STATUS_INVALID_IMAGE_FORMAT, как и везде далее);
    VirtualAddress должен совпадать с текущим рассматриваемым RVA, то есть (a) VirtualAddress выравнен на SectionAlignment и (b) все подсекции (начиная с заголовка) в памяти идут последовательно;
    VirtualSize ненулевой либо SizeOfRawData ненулевой;
    у PointerToRawData отбрасываются младшие 9 бит (округление вниз до границы 0x200);
    атрибуты защиты берутся из атрибутов секции;
    если SizeOfRawData ненулевой, то PointerToRawData + SizeOfRawData <= размера файла.
    И финальная проверка - ImageSize должно совпадать с размером образа исходя из созданных подсекций.
    P.S. Когда-то пытался написать плагин-эмулятор для IDA, который бы загружал программу точно так, как это делает реальная система. Собственно, эмулятор есть, причём быстрее прочих известных мне, а вот с загрузкой дело до конца не дошло :dntknw:
     
  9. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    Joes
    CIH вроде записывается не между секциями, а в конец секции, т.е. в то место, которое остается свободным из-за FileAlignment. А между секциями он вряд ли бы записался, т.к. нужно рассчитывать на очень извращенный компоновщик, который бы оставлял непонятно зачем специально дыры между секциями.
    diamond
    Извиняюсь. Мой прогон. Не знаю, че так написал. Но тем не менее из-за теоретически возможных дырок в образе RVA секций может не совпадать с PointerToRawData. Как же тогда проходит описаная Вами проверка
    ?

    С остальным все ясно. Большое спасибо. Несмотря на и так большое количество статей о PE, Ваш бы пост, да отдельной статьей на wasm.

    P.S. Ну и вообще всем спасибо. Думал, что из-за размера вопроса, вообще никто не захочет вникать.
     
  10. diamond

    diamond New Member

    Публикаций:
    0
    Регистрация:
    21 май 2004
    Сообщения:
    507
    Адрес:
    Russia
    А она в этом случае и не проходит - проваливается :) Следовательно, в случае SectionAlignment < 0x1000 дырок в файле быть не может. А вот в нормальном случае - пожалуйста.
     
  11. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    diamond
    И все-таки SizeOfHeaders выравнивается на FileAlignment, а не на SectionAlignment. В подтверждение можно глянуть там, куда "далеко лазить не надо - \windows\system32". Большинство образов прикладных программ имеют SectionAlignment == 1000h, FileAlignment == 200h и SizeOfHeaders == 400h, сразу за которым идет секция .text с PointerToRawData == 400h.
     
  12. Mikl_

    Mikl_ New Member

    Публикаций:
    0
    Регистрация:
    14 ноя 2006
    Сообщения:
    907
    l_inc
    Вот здесь (пример lesson31-5) показано, как ресурсы засунуть в секцию кода:)
     
  13. diamond

    diamond New Member

    Публикаций:
    0
    Регистрация:
    21 май 2004
    Сообщения:
    507
    Адрес:
    Russia
    Мы немного о разных вещах говорим. Вы имеете в виду, что значение поля SizeOfHeaders в PE-заголовке реальных exeшников выравнено на FileAlignment. Я имею в виду, что загрузчик винды при проецировании PE создаёт первую подсекцию образа размером ALIGN_UP(SizeOfHeaders, SectionAlignment) (дополняя её нулями), а выравнено это значение на FileAlignment или нет, его не интересует. Оба этих утверждения верны. В вышеописанном случае в файле получается заголовок размера 0x400 байт, за которым идёт секция .text, а в образе в памяти получается заголовок размера 0x1000 байт, за которым также идёт секция .text.
    Mikl__
    Это называется "выдёргивание из контекста" - одно из самых страшных преступлений против цитаты :) Полная цитата звучит так:
     
  14. Mikl_

    Mikl_ New Member

    Публикаций:
    0
    Регистрация:
    14 ноя 2006
    Сообщения:
    907
    /offtop
    diamond я не хотел обидеть ни лично Вас, ни многоуважаемую Цитату, но, на всякий случай, примите мои извинения:)
     
  15. MSoft

    MSoft New Member

    Публикаций:
    0
    Регистрация:
    16 дек 2006
    Сообщения:
    2.854
    +1 :)))
     
  16. diamond

    diamond New Member

    Публикаций:
    0
    Регистрация:
    21 май 2004
    Сообщения:
    507
    Адрес:
    Russia
    /offtop
    Я и не обиделся :) Но Цитату на всякий случай решил повторить :)
     
  17. synus0ida

    synus0ida New Member

    Публикаций:
    0
    Регистрация:
    25 фев 2008
    Сообщения:
    20
    7) Возник встречный вопрос, хотя возможно ты это и подразумеваешь. Можно ли собрать на лету при этом собрать из кусочков разных файликов. Т.е. эти файлики были предварительно модифицированы. В статейке подобное, но с одним(нужный код дописан в конец файлика), как сделать сборку из нескольки файликов?
     
  18. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    synus0ida
    Что-то я не понимаю вопроса, но вот к седьмому пункту он вряд ли какое-то отношение имеет. Если вопрос состоит в дозаписи нескольких файлов в инфицируемый, то примените то, что описано в "статейке", но пять раз (метод расширения последней секции или добавления дополнительной).
     
  19. KeSqueer

    KeSqueer Сергей

    Публикаций:
    0
    Регистрация:
    19 июл 2007
    Сообщения:
    1.183
    Адрес:
    Москва
    ИМХО, в статье неправильно описывается расширение последней секции. Простейший пример:
    Код (Text):
    1. section '.data' data readable writeable
    2.  
    3.         pCount  dd Count
    4.         Count   dd ?
    FASM соберет это в секцию с VirtualSize=8 и SizeOfRawData=4. Дописывается секция по статье начиная с PointerToRawData+SizeOfRawData, т.е. изначально в Count после расширения секции с большой вероятностью будет находиться число, отличное от нуля, а в большинстве случаев программист ожидает, что в неинициализированных переменных находится как раз нули из-за zero-padding'а.