Арифметика секций PE файла

Тема в разделе "WASM.WIN32", создана пользователем _nic, 13 май 2011.

  1. _nic

    _nic New Member

    Публикаций:
    0
    Регистрация:
    4 фев 2007
    Сообщения:
    372
    Поставил для себя задачу увеличить 1ю секцию,на размер Х, с учетом выравнивания в 4 кило-байта(его по умолчанию задает компилятор MS VS).Я конечно понимаю что секции в образе,после маппинга должны идти одна за одной.Но что то у меня всеравно не получается с пересчотом VA секций.
    Код (Text):
    1. DWORD CalcAlign(DWORD inpval,DWORD align)
    2. {
    3.     DWORD alval=0;
    4.     for(;;)
    5.     {
    6.         alval=alval+align;
    7.         if(alval==inpval | alval>inpval){break;}
    8.     }
    9.     return(alval);
    10. }
    11. void ChangeSz(char *exebody,DWORD AdSz)
    12. {
    13.     DWORD toNT=((IMAGE_DOS_HEADER*)exebody)->e_lfanew;
    14.     IMAGE_NT_HEADERS *image;
    15.     exebody+=toNT;
    16.     image=(IMAGE_NT_HEADERS *)exebody;
    17.     exebody+=sizeof(IMAGE_NT_HEADERS);
    18.     DWORD alsz=CalcAlign(AdSz,1024*4);
    19.     DWORD recalcVA=alsz-((IMAGE_SECTION_HEADER*)exebody)->Misc.VirtualSize;
    20.     ((IMAGE_SECTION_HEADER*)exebody)->Misc.VirtualSize=alsz;
    21.     ((IMAGE_SECTION_HEADER*)exebody)->Characteristics=IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_WRITE;
    22.     DWORD back=toNT+sizeof(IMAGE_NT_HEADERS)+sizeof(IMAGE_SECTION_HEADER);
    23.     for(int i=1;i<image->FileHeader.NumberOfSections;i++)
    24.     {
    25.      exebody+=sizeof(IMAGE_SECTION_HEADER);
    26.      back=back+sizeof(IMAGE_SECTION_HEADER);
    27.      ((IMAGE_SECTION_HEADER*)exebody)->VirtualAddress=((IMAGE_SECTION_HEADER*)exebody)->VirtualAddress+recalcVA;
    28.     }
    29.     exebody-=back;
    30. }
    Есть добрый человек который может сказать что не так с арефметикой в коде?
     
  2. dinoweb

    dinoweb Дмитрий

    Публикаций:
    0
    Регистрация:
    12 окт 2005
    Сообщения:
    129
    Адрес:
    Россия. Красноярск
    что это... и почему в ифе используется побитовое или, вместо логического или...
    Код (Text):
    1. DWORD CalcAlign(DWORD inpval,DWORD align)
    2. {
    3.     DWORD alval=0;
    4.     for(;;)
    5.     {
    6.         alval=alval+align;
    7.         if(alval==inpval | alval>inpval){break;}
    8.     }
    9.     return(alval);
    10. }
    так лучше (при условии, что align степень двойки)
    Код (Text):
    1. DWORD CalcAlign(DWORD inpval,DWORD align)
    2. {
    3.     --align;
    4.     return (inpval + align) & ~align;
    5. }
    читать ваш код очень сложно. используейте пробелы, и переменные нужного типа, и код станет приятнее.
    так в чём стоит задача? программа должна работать после _этого_? просто так VA менять нельзя! или нужно только изменить va в заголовках, и на этом всё? сами секции не двигаете?
     
  3. _nic

    _nic New Member

    Публикаций:
    0
    Регистрация:
    4 фев 2007
    Сообщения:
    372
    Вы намекаете на то что потом ещё нужно пересчитать RVA в секциях по релокам что бы работало?Я правильно понимаю?
    ЗЫ:пока на данном этапе винда неопознает экзешник после редактирования как валидный,так что до пересчета RVA ещё похоже рановато,надо с секциями разобратся
     
  4. mrcrown

    mrcrown Member

    Публикаций:
    0
    Регистрация:
    18 янв 2008
    Сообщения:
    227
    А Вы, случайно, на с Нью Дели?
     
  5. dinoweb

    dinoweb Дмитрий

    Публикаций:
    0
    Регистрация:
    12 окт 2005
    Сообщения:
    129
    Адрес:
    Россия. Красноярск
    А где после этого системе искать таблицу импорта, таблицу экспорта, ресурсы? Без этого программа не будет валидной.

    Как минимум, чтобы программа была валидной, нужно прибавить этот же X к RVA всех датадиректорий, которые расположены после первой секции. Но после этого программа всё равно не заработает.

    Релоки в большинстве экзешников отсутствуют, они необходимы только в библиотеках (и то, могут быть экземпляры без них)

    Обратите внимание, что значение поля VirtualSize может быть и не выровнено на 4К, выравнивание применяется для VirtualAddress, а VirtualSize может быть любым.

    Поэтому вот эти строки некорректны, они портят выровненный VirtualAddress:
    Код (Text):
    1.     uint alsz = CalcAlign(AdSz, 1024*4);
    2.     uint recalcVA = alsz - ((IMAGE_SECTION_HEADER*)exebody)->Misc.VirtualSize;
    3. ...
    4.      ((IMAGE_SECTION_HEADER*)exebody)->VirtualAddress=((IMAGE_SECTION_HEADER*)exebody)->VirtualAddress+recalcVA;
    после этого, VirtualAddress второй и следующих секций становится невыровненным


    А в отсальному вроде-бы правильно.

    upd: Забыл ещё сказать, что внутри таблицы импорта, внутри таблицы экспорта, внутри ресурсов, и других дд, всё также завязано на RVA, поэтому необходимо корректировать внутренности этих таблиц для того, чтобы всё заработало. Это задачка не на 30 строк, как у вас в примере...
     
  6. _nic

    _nic New Member

    Публикаций:
    0
    Регистрация:
    4 фев 2007
    Сообщения:
    372
    Таблицы и ресурсы через релоки исправить невозможно?
     
  7. dinoweb

    dinoweb Дмитрий

    Публикаций:
    0
    Регистрация:
    12 окт 2005
    Сообщения:
    129
    Адрес:
    Россия. Красноярск
    Таблицы и ресурсы можно исправить без релоков, и это не сложно.
    Код и остальные данные без релоков исправить возможно, но это нетривиальная задача.
    Ваша задача относительно легко решаема для DLL библиотек, которые содержат релоки, но для обычных EXE'шников... нет там релоков. Поставьте для начала себе задачу попроще, а дальше разберётесь.
     
  8. _nic

    _nic New Member

    Публикаций:
    0
    Регистрация:
    4 фев 2007
    Сообщения:
    372
    Релоки есть у экзешников которые собирались с поддержкой рандомной базы загрузки.Для меня этого достаточно.
     
  9. dinoweb

    dinoweb Дмитрий

    Публикаций:
    0
    Регистрация:
    12 окт 2005
    Сообщения:
    129
    Адрес:
    Россия. Красноярск
    _nic
    Ясно. Для начала исправьте ошибку невыровненного адреса. Вы поставили задачу "Увеличить секцию на X", а привели пример функции с параметром "Новый размер секции". И никаких проверок. Тогда передавайте сразу X в функцию, и станет проще. Пусть AdSz = CalcAlign(X). Прибавляйте AdSz к размеру первой секции, прибавляйте AdSz к адресам всех следующих секций. (Допустим, что exe'шник "нормальный", и секции идут в "нормально порядке")

    Прибавляйте AdSz ко всем RVA, что больше адреса второй секции. (entrypoint_rva, import_table_rva, resource_table_rva, ...), прибавляйте AdSz ко всем RVA, что больше адреса второй секции, внутри import_table, внутри resouce_table. Ну и про релоки не забывайте.

    Возникнут ещё вопросы, пишите исправленный код, посмотрим.
     
  10. _nic

    _nic New Member

    Публикаций:
    0
    Регистрация:
    4 фев 2007
    Сообщения:
    372
    Решил двигатся от простого к сложному.Собрал для теста небольшое приложение,таким образом что бы пока ненужно было фиксить импорт.
    Код (Text):
    1. #pragma comment(linker, "/MERGE:.data=.text")
    2. #pragma comment(linker, "/MERGE:.rdata=.text")
    3. #pragma comment(linker, "/SECTION:.text,EWR")
    iOptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress теперь соответсвенно направленно в 1ю секцию.И как раньше приплюсовал разницу размера образа, к начальным РВА последующих секций.Хотя и системный загрузчик считает теперь файл валидным.Но наблюдаю непонятный мне баг с импортом - системный загрузчик ненаходит msvcr90.dll О_о.В чем прикол - в размере образа?Или обязтаельно нужно фиксить секцию ресурсов?
    Код (Text):
    1. void ChangeSz(char *exebody,DWORD AdSz)
    2. {
    3.     DWORD toNT=((IMAGE_DOS_HEADER*)exebody)->e_lfanew;
    4.     IMAGE_NT_HEADERS *image;
    5.     exebody+=toNT;
    6.     image=(IMAGE_NT_HEADERS *)exebody;
    7.     exebody+=sizeof(IMAGE_NT_HEADERS);
    8.     IMAGE_SECTION_HEADER* sections=(IMAGE_SECTION_HEADER*)exebody;
    9.     exebody-=toNT+sizeof(IMAGE_NT_HEADERS);
    10.     DWORD newSZ=CalcAlign(AdSz,image->OptionalHeader.SectionAlignment);
    11.     image->OptionalHeader.SizeOfImage=image->OptionalHeader.SizeOfImage+newSZ;
    12.     sections[0].Misc.VirtualSize=sections[0].Misc.VirtualSize+newSZ;
    13.     for(int i=1;i<image->FileHeader.NumberOfSections;i++)
    14.     {
    15.         sections[i].VirtualAddress=sections[i].VirtualAddress+newSZ;
    16.     }
    17.     int count=0;
    18.     DWORD rawImp=RVAtoRAW(sections,image->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress,image->FileHeader.NumberOfSections);
    19.     exebody+=rawImp;
    20.     IMAGE_IMPORT_DESCRIPTOR *import=(IMAGE_IMPORT_DESCRIPTOR*)exebody;
    21.     exebody-=rawImp;
    22.     for(;;)
    23.     {
    24.         if(import[count].Name==NULL){count=0;break;}
    25.         import[count].TimeDateStamp=0;
    26.         count++;
    27.     }
    28. }
     
  11. Charlief

    Charlief New Member

    Публикаций:
    0
    Регистрация:
    17 авг 2010
    Сообщения:
    129
    Непонятно зачем пересчитывать VA секций. Вы хотите дописать секцию в начале или в конце ?

    1. Если в конце, то просто увеличиваете VirtualSize первой секции на размер Х, и SizeOfRawData на размер Х так чтобы SizeOfRawData был кратен 0x200 байтам (размеру сектора на диске). Потом нужно посмотреть чтобы PointerToRawData + SizeOfRawData первой секции был меньше PointerToRawData следующей секции в файле, иначе добавленый код затрёт данные следующей секции и т.д., тогда прийдётся сдвигать данные следующей секции в файле и пересчитывать все смещения.

    2. А если в начало секции дописываете, то прийдётся уменьшить PointerToRawData первой секции на размер Х кратный 0x200 байтам, но загрузчик тогда просто не найдёт импорт и экспорт образа (может ещё и какие-то другие данные) которые могут располагаться в первой секции, которая обычно .text. И тогда прийдётся пересчитывать VA импорта и экспорта в DataDirectory. Атак же AddressOfEntryPoint, плюс релоки наверное.

    3. Или чтобы записать в начало секции но не менять PointerToRawData первой секции можно сдвинуть данные секции на Х и записать в освободившееся место свой код. Но опять же прийдётся пересчитывать VA импорта и экспорта в DataDirectory. Атак же AddressOfEntryPoint, плюс релоки наверное.

    Так что проще записать свои данные в конец секции.
     
  12. _nic

    _nic New Member

    Публикаций:
    0
    Регистрация:
    4 фев 2007
    Сообщения:
    372
    В конец не интересно,и слишком просто.В 1м посте я ясно черным по белому написал УВЕЛИЧИВАЮ размер ПЕРВОЙ секции в пе файле,НЕ ДОПИСЫВАЮ никаких данных.Манипуляция только с РВА.
    Мне очень интересна природа возникновения исключения в системном загрузчике после такого редактирования файла(если он обрабатывает импорт по длл "с верху вниз" то получается что спотыкается только на последней,почему???? ).Кто то может обьяснить этот ньюанс?
     
  13. Charlief

    Charlief New Member

    Публикаций:
    0
    Регистрация:
    17 авг 2010
    Сообщения:
    129
    Из кода я понял что изменяются VA всех секций кроме самой первой с индексом 0 (sections[0]). А таблица импорта лежит в первой секции точно ?. Импорт msvcr90.dll обычно описан как раз в самом первом дескрипторе таблицы импорта.

    Может исключение возникает из-за того что в редактированом файле в первой секции теперь не совпадают VirtualSize и SizeOfRawData которые должны отличаться не более чем на 0x200 байт. Эти поля возможно проверяются загрузчиком.
     
  14. _nic

    _nic New Member

    Публикаций:
    0
    Регистрация:
    4 фев 2007
    Сообщения:
    372
    Код (Text):
    1. sections[0].SizeOfRawData=sections[0].SizeOfRawData+sections[0].Misc.VirtualSize;
    ничего неизменилось ,та же самая ошибка.Думаю если б загрузчик это проверял,он бы или отвергнул файл как невалид.Или все бы заработало.
     
  15. Jupiter

    Jupiter Jupiter

    Публикаций:
    0
    Регистрация:
    12 авг 2004
    Сообщения:
    530
    Адрес:
    Russia
    _nic
    Проверь секцию ресурсов и манифест (Тип ресурса 24).
     
  16. Charlief

    Charlief New Member

    Публикаций:
    0
    Регистрация:
    17 авг 2010
    Сообщения:
    129
    С чего вы взяли что это загрузчик. Возможно это уже код в программе обращается к данным в какой-то секции относительно входного параметра функции WinMainHINSTANCE hInstance, но не находит то что нужно так как увеличились адреса всех данных относительно hInstance из-за увеличеня виртуальных адресов секций с данными.

    Тоесть ошибки будут там где код обращается к данным не относительно начала секции с данными а относительно базы образа в памяти.

    hInstance - дескриптор текущего экземпляра данного приложения. Это значение используется в дальнейшем для взаимодействия с элементами рабочей среды, связанными с данным приложением: ресурсами, окнами, изображениями и многими другими. Как правило, надобность в знании этого дескриптора возникает почти сразу после запуска экземпляра приложения и сохраняется на протяжении всего срока его жизни. Этот дескриптор локален в рамках данного экземпляра приложения и не имеет смысла для других приложений и других экземпляров этого же приложения.

    Между прочим, этот дескриптор фактически представляет собой базовый адрес памяти для размещения образа приложения (image base в терминах формата PE-файла). Для самостоятельных приложений по умолчанию он обычно равен 400000h.
     
  17. _nic

    _nic New Member

    Публикаций:
    0
    Регистрация:
    4 фев 2007
    Сообщения:
    372
    насколько я знаю все смещения в этой секции считаются от её начала,а неот базы загрузки экзешника.Остается по идее только Directory Entries поправить.Но после этого системный загрузчик почему то перестает хавать этот файл :dntknw:
    Код (Text):
    1. void ChangeSz(char *exebody,DWORD AdSz)
    2. {
    3.     DWORD toNT=((IMAGE_DOS_HEADER*)exebody)->e_lfanew;
    4.     IMAGE_NT_HEADERS *image;
    5.     exebody+=toNT;
    6.     image=(IMAGE_NT_HEADERS *)exebody;
    7.     exebody+=sizeof(IMAGE_NT_HEADERS);
    8.     IMAGE_SECTION_HEADER* sections=(IMAGE_SECTION_HEADER*)exebody;
    9.     exebody-=toNT+sizeof(IMAGE_NT_HEADERS);
    10.     DWORD newSZ=CalcAlign(AdSz,image->OptionalHeader.SectionAlignment);
    11.     image->OptionalHeader.SizeOfImage=image->OptionalHeader.SizeOfImage+newSZ;
    12.     sections[0].Misc.VirtualSize=sections[0].Misc.VirtualSize+newSZ;
    13.     for(int i=1;i<image->FileHeader.NumberOfSections;i++)
    14.     {
    15.         sections[i].VirtualAddress=sections[i].VirtualAddress+newSZ;
    16.     }
    17.     image->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress=image->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress+newSZ;
    18.     image->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress=0;
    19.     int count=0;
    20.     DWORD rawImp=RVAtoRAW(sections,image->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress,image->FileHeader.NumberOfSections);
    21.     exebody+=rawImp;
    22.     IMAGE_IMPORT_DESCRIPTOR *import=(IMAGE_IMPORT_DESCRIPTOR*)exebody;
    23.     exebody-=rawImp;
    24.     for(;;)
    25.     {
    26.         if(import[count].Name==NULL){count=0;break;}
    27.         import[count].TimeDateStamp=0;
    28.         count++;
    29.     }
    30.  
    31. }