Старый 02.10.2013, 23:55   #1
<Gh0St>
 
Аватар для <Gh0St>
 
Регистрация: 22.03.2012
Сообщений: 75
Репутация: 19
Arrow Работа с exe файлами (PE-заголовок) на Си (общие вопросы)

Всем привет!
Чтобы не создавать кучу новых тем, решил задавать интересующие вопросы в одной теме.
Интересует вопрос добавления новой секции в файл.
Есть ехе файл, открыли его, считали в массив, разобрали PE-заголовок. Нужно добавить секцию ресурсов (.rsrc).
Как это сделать, теоретически:
1) Выделить память под новую секцию, выровненную по IMAGE_OPTIONAL_HEADER->SectionAlignment.
2) заполнить для неё структуру IMAGE_SECTION_HEADER.
3) Увеличить количество секций IMAGE_FILE_HEADER->NumberOfSections на 1.
4) Увеличить размер образа IMAGE_SECTION_HEADER->SizeOfImage.
5) Добавить в массив IMAGE_DATA_DIRECTORY информацию о секции ресурсов с индексом IMAGE_DIRECTORY_ENTRY_RESOURCE. И установить VA и size.
6) Затем заполнить секцию нужными данными.

И тут возникает два вопроса:
1) Всё ли верно в теории? Ничего не упущено?
2) Как всё это сделать на практике? Особенно не понятно, как реализовать пункты №1 и №5.

Подскажите, как это сделать.
Благодарю.
__________________
- Про опыт говорят: "Мы так свои ошибки называем"
<Gh0St> вне форума   Ответить с цитированием
Старый 03.10.2013, 11:19   #2
DrakonHaSh
 
Регистрация: 05.07.2010
Сообщений: 244
Репутация: 106
По умолчанию

http://kaimi.ru/2012/09/portable-executable-library/
DrakonHaSh вне форума   Ответить с цитированием
Старый 06.10.2013, 20:02   #3
<Gh0St>
 
Аватар для <Gh0St>
 
Регистрация: 22.03.2012
Сообщений: 75
Репутация: 19
По умолчанию

Ознакомился с примером pe_bliss_1.0.0\samples\section_adder.
Что там происходит в общих чертах:
* Считывается PE-файл, создаётся объект класса, содержащий считанный файл.
* Создаётся объект класса секции (новая секция). Затем эта секция (структура IMAGE_SECTION_HEADER) заполняется. Рассчитываются адреса, размеры секции и т.д.

* IMAGE_FILE_HEADER->NumberOfSections устанавливается равным новому количеству секций .
* Увеличивается размер образа (IMAGE_SECTION_HEADER->SizeOfImage) на размер новой секции.
* Новая секция добавляется к образу PE-файла.

Дальше самое интересное - начинается пересборка PE-файла с учётом новой секции, но при этом массиву IMAGE_DATA_DIRECTORY вообще не уделяется никакого внимания, что удивительно.

* Пересчитывается bound-импорт
* Записывается DOS header
* Записывается NT header

* После записи NT заголовка, программа проходит по списку секций и на то место, где по идее (если не ошибаюсь) должен быть IMAGE_DATA_DIRECTORY записывает заголовки секции (структуры IMAGE_DATA_DIRECTORY).

* И, наконец, записывает содержимое самих секций.

Вот тут возникает проблемка... Дело в том, что в этом примере, выходной файл собирается и записывается по частям. У меня чуть иначе: берём входной файл, считываем его в массив и дальше работаем. Нужно добавить секцию ресурсов, заполнил структуру IMAGE_SECTION_HEADER, поправил значения в PE-хедере, добавил информацию о новой секции в IMAGE_DATA_DIRECTORY[IMAGE_DIRECTORY_ENTRY_RESOURCE].

Теперь возникла проблема записи. После DOS и NT заголовков идут заголовки секций, куда нужно записать заголовок секции ресурсов (IMAGE_SECTION_HEADER). Но вот как правильно это сделать?

Теоретически, можно было бы записать DOS + NT, затем в конце заголовков секций добавить свою секцию ресурсов, а после неё записать всё остальное. Понятное дело, что такой ход приведёт к увеличению выходного файла на 512 байт, что не так важно. На много важнее другое: если немножко подвинуть остальную часть файла, чтобы впихнуть секцию, пройдёт сдвиг, что может принести серьёзные проблемы.

По сути, новая секция готова, осталось лишь правильно её добавить.
Подскажите, как это сделать?
Благодарю.
__________________
- Про опыт говорят: "Мы так свои ошибки называем"

Последний раз редактировалось <Gh0St>; 06.10.2013 в 20:22..
<Gh0St> вне форума   Ответить с цитированием
Старый 07.10.2013, 21:39   #4
DrakonHaSh
 
Регистрация: 05.07.2010
Сообщений: 244
Репутация: 106
По умолчанию

http://vxheavens.com/lib/?index=WI&lang=ru
рекомендую дальше самому копать
* От зеленого к красному (!)
* О PE файлах и длинах секций
* Инфектор PE-файлов
* ...
offline wasm.ru тоже будет полезна
DrakonHaSh вне форума   Ответить с цитированием
Старый 20.10.2013, 22:40   #5
<Gh0St>
 
Аватар для <Gh0St>
 
Регистрация: 22.03.2012
Сообщений: 75
Репутация: 19
По умолчанию

Подскажите, как добавить TLS в программы на Visual Studio без дополнительных секций?
Ознакомился с документацией, создаю локальную переменную потока:
Код:
__declspec(thread) int tls_i = 1;
Выходному файлу добавляется секция .tls. Но вместе с ней добавляется секция .data, видимо потому, что переменная tls_i объявляется, как глобальная. Так же, не понятно, почему добавляется секция .CRT (C Runtime Library), хотя Сишные функции в программе не используются, лишь WinAPI.
Пробовал объявлять переменную по разному: как глобальную, как локальную статическую, результат всё тот же, 2 лишние секции.
В таблице импорта лишь библиотека KERNEL32.dll и несколько функций из неё.
Не понятно, зачем .data нужна, если переменная объявлена, как статическая, а данные для инициализации tls переменной хранятся в секции .tls.

При использовании динамической TLS, секция .tls не создаётся.
Ещё одна особенность: если явно указать точку входа
Код:
#pragma comment(linker, "/ENTRY:main")
То .CRT создаётся. Если точку входа не указывать, .CRT не будет. Но явное указание точки входа необходимо...

Нужно создать только секцию .tls, без .data и .CRT.
Подскажите, как это сделать?
Благодарю.
__________________
- Про опыт говорят: "Мы так свои ошибки называем"

Последний раз редактировалось <Gh0St>; 21.10.2013 в 00:43..
<Gh0St> вне форума   Ответить с цитированием
Старый 21.10.2013, 20:10   #6
DrakonHaSh
 
Регистрация: 05.07.2010
Сообщений: 244
Репутация: 106
По умолчанию

Цитата:
Так же, не понятно, почему добавляется секция .CRT (C Runtime Library), хотя Сишные функции в программе не используются, лишь WinAPI.
crt-шные функции используются в "загрузчике main" stub.

можешь почитать это:
http://www.rsdn.ru/article/cpp/crt.xml
http://wasm.ru/forum/viewtopic.php?id=31966
http://xaknotdie.org/22h/12/05.html
http://www.cyberforum.ru/visual-cpp/thread49399.html

чем более новая версия vs тем больше заморочек и некоторые методы перестают работать. на vs 2005 все, помнится, работало.

про .tls ваще не подскажу.
DrakonHaSh вне форума   Ответить с цитированием
Старый 24.10.2013, 01:38   #7
<Gh0St>
 
Аватар для <Gh0St>
 
Регистрация: 22.03.2012
Сообщений: 75
Репутация: 19
По умолчанию

Вариантов там не много:
1) Использовать CRT, которая добавит кучу кода для инициализации, и прибавит вес файлу.
2) Отказаться от CRT, установить свою точку входа (main). Тогда появится секция .CRT

Короче, тут два варианта, и оба лажа.
Сделал по другому: создал секцию .data, переделал её в секцию .tls, в image_data_directory добавил запись о секции .tls.
Короче, сделал так, чтобы заработало и без лишнего библиотечного кода.
Теперь возникает вопрос: нужно зарегистрировать callback, пусть пустую функцию, лишь бы был сам callback.
Подскажите, как это сделать?
__________________
- Про опыт говорят: "Мы так свои ошибки называем"
<Gh0St> вне форума   Ответить с цитированием
Старый 27.10.2013, 20:15   #8
<Gh0St>
 
Аватар для <Gh0St>
 
Регистрация: 22.03.2012
Сообщений: 75
Репутация: 19
По умолчанию

Всё ещё мучаюсь с tls колбэками.
Гуглил-гулил, толку мало. Смотрел васм и ехелаб, но инфы по этому поводу очень мало. В лучшем случае советуют держаться от tls подальше, в худшем, молчат.

Нашёл хорошую статью (http://www.insidepro.com/kk/300/300r.shtml), где объясняется, как создать tls вручную. Сделал всё, как надо, но результат получился не совсем верным.
Индекс, как в статье, обнулился, а вот колбэк не выполнился.
В секции .tls записываю таблицу tls и сам колбэк. Даже дал секции права на выполнение, результат всё тот же, колбэк не выполняется. Есть одна зацепка: в программах собранных VS с tls, колбэки находятся в секции .text. Возможно свой колбэк следовало бы записать туда же, но в статье колбэк работает даже из других секций.
Забавы ради записал код колбэка в конец секции кода, изменил указатель на колбэк, результат всё тот же.
Вот тут показан код .tls:


Вот тут tls в таблице директорий:


А вот результат работы:


Вывод очевиден: колбэк не вызывается, или же вызывается, но его код не выполняется.
Подскажите, что тут можно сделать?
__________________
- Про опыт говорят: "Мы так свои ошибки называем"
<Gh0St> вне форума   Ответить с цитированием
Старый 28.10.2013, 16:44   #9
<Gh0St>
 
Аватар для <Gh0St>
 
Регистрация: 22.03.2012
Сообщений: 75
Репутация: 19
По умолчанию

В общем есть кое-какие результаты, если кому интересно.
Для изучения собрал в VS 2010 бинарник с TLS.
Сборка стандартная DEBUG, только базовый адрес установил с 0х00400000.
Код программы следующий:
Код:
#include <stdio.h>
#include <conio.h>
#include <Windows.h>

__declspec(thread) int tls_i = 0; // to make TLS section

typedef VOID (NTAPI *PIMAGE_TLS_CALLBACK)(PVOID DllHandle, DWORD Reason, PVOID Reserved);

void NTAPI tls_callback(PVOID DllHandle, DWORD Reason, PVOID Reserved) { 
	_asm {int 3}
	tls_i = 1; 
	_asm {int 3}
}

#pragma data_seg(".CRT$XLB")
PIMAGE_TLS_CALLBACK p_thread_callback = tls_callback;
#pragma data_seg()

int main() {
	printf("test TLS : %d\n", tls_i);

	_getch();
	return 0;
}
В процессе изучения, выяснилось, что в секции .tls по смещению 0х104 находится значение для инициализации переменной tls_i. Начало данных TLS таблицы равно виртуальному адресу секции .tls, а конец таблицы равен началу+0х208, т.е. 2 блока по 0х104 байта.
Может кто знает, почему именно по 0х104 байта?

Идём дальше. Адрес TLS индекса находится в .data и содержит нули.

Адрес массива колбэков находится в .rdata и содержит адрес, указывающий на первый колбэк, который находится в секции кода (.text). Но это не функция колбэка, а хрен знает, что. Там прыжки какие-то, не углублялся.
Экспериментальным путём выяснилось, что настоящая функция колбэка находится в другом месте, Оля останавливается на _asm {int 3} , давая возможность изучить код.
Таким образом получается, что адрес, указанный в массиве колбэков указывает не на саму функцию колбэка, а на какие-то прыжки, следовательно, он не нужен, можно сразу передать управление на нужный участок кода.

Что сделал:
1) Перенёс TLS таблицу из .rdata в .tls.
2) Изменил адрес конца таблицы с 0x208 на 0х18 (размер структуры), значение по смещению в .tls+0x104 обнулил.

Вот фрагмент кода функции колбэка, пришлось его не много изменить.
Код:
004190BB   90						NOP
004190A0   CC						INT3
004190A1   A1 3C714100				MOV EAX,DWORD PTR DS:[_tls_index]
004190A6   64:8B0D 2C000000			MOV ECX,DWORD PTR FS:[2C]
004190AD   8B1481					MOV EDX,DWORD PTR DS:[ECX+EAX*4]
004190B0   C782 04010000 01000000	MOV DWORD PTR DS:[EDX+tls_i],1
004190BA   C3						RETN
004190BB   90						NOP
3) Записал байт-код в секцию .tls, сделав её исполняемой:
Код:
90 CC A1 3C - 71 41 00 64 - 8B 0D 2C 00 - 00 00 8B 14 
81 C7 82 04 - 01 00 00 01 - 00 00 00 C3 - 90 00 00 00
4) В массиве колбэков изменил адрес на собственный код в .tls (из пункта 3).

В результате, в секции .tls располагается TLS таблица, чуть дальше находится код колбэка, на который будет передано управление.
Запускаем, и, что видим? А видим, что код колбэка, НАКОНЕЦ-ТО ВЫПОЛНИЛСЯ!

Но не всё так гладко.
Теперь полученные знания применяем на другом примере, бинарник собран без TLS. Секция .tls создана вручную. Выполнил все те же действия, проверяю и... не сработало.
Код колбэка просто не выполняется, даже Оля не останавливается на брейке.
У кого-нибудь есть соображения по этому поводу?

Пока что есть только одна зацепка: при сборке бинарника, как дебаг, колбэк вызывается. А если собрать, как релиз - нет. Но это справедливо для файла, который собирается с TLS.
Если TLS прикрутить вручную после сборки, то хоть дебаг, хоть резил, всё равно ничего не работает.
__________________
- Про опыт говорят: "Мы так свои ошибки называем"

Последний раз редактировалось <Gh0St>; 28.10.2013 в 17:07..
<Gh0St> вне форума   Ответить с цитированием
Старый 31.10.2013, 21:46   #10
<Gh0St>
 
Аватар для <Gh0St>
 
Регистрация: 22.03.2012
Сообщений: 75
Репутация: 19
По умолчанию

Есть ещё кое-какие результаты.
Если нужно вручную прикрутить TLS и сделать всё правильно, например, так, чтобы переменные вроде этой
Код:
 __declspec(thread) int tls_i = 0;
правильно инициализировались, следует делать так:
1) Создаём .tls секцию, хотя можно и без неё. Или же используем уже имеющуюся .tls.
Допустим, параметры .tls следующие
Код:
Name: .tls
Virtual Size: 0x1000
Virtual Address: 0x1A000
Raw Size: 0x200
Raw Address: 0x6C00
2) В массиве IMAGE_DATA_DIRECTORY в по индексу 9 (IMAGE_DIRECTORY_ENTRY_TLS) пишем следующее:
Код:
Virtual Address: 0x1A000 (VA .tls, если директория TLS размещена в начале секции)
Size: 0x18 (может быть любым, но указываем 0x18, т.к. это размер IMAGE_TLS_DIRECTORY32)
3) Самое интересное, где я допустил ошибку, тривиальную ошибку, которая принесла не мало хлопот.
Сначала описал структуру TLS следующим образом:
Код:
StartAddressOfRawData: 0x0041A000 (VA .tls)
EndAddressOfRawData 0x0041A018 (start + 0x18)
AddressOfIndex: 0x0041A030 (не важно)
AddressOfCallBacks: 0x0041A070 (не так важно)
Итак, самое интересное, в чём была ошибка. Дело в том, что переменные TLS для инициализиализации обращаются к памяти в диапазоне от StartAddressOfRawData до EndAddressOfRawData, а в этот диапазон попадает структура IMAGE_TLS_DIRECTORY32, именно от сюда пошли все беды.
Всё это выяснилось опытно-экспериментальным путём.

Оказалось, всё, что нужно сделать - это вывести структуру IMAGE_TLS_DIRECTORY32 из этого диапазона и сделать это оказалось элементарно:
Код:
StartAddressOfRawData: 0x0041A018 (VA .tls + 0х18)
EndAddressOfRawData 0x0041A220 (start + 0х104 * 2)
AddressOfIndex: 0x0041A038 (не важно, т.к. перезапишется загрузчиком)
AddressOfCallBacks: 0x0041A070 (не важно, т.к. по этому адресу ноль, нет колбэков).
Т.е. структуру IMAGE_TLS_DIRECTORY32 размещаем вначале .tls, но указываем начало данных за пределами структуры.
После этого всё чудесным образом заработало, и тестовая переменная инициализировалась, как надо.

Таким образом решёна проблема добавления TLS вручную. Над проблемой добавления собственных колбэков ещё работаю.

P.S. Рад, что мои... "исследования" интересны.
__________________
- Про опыт говорят: "Мы так свои ошибки называем"
<Gh0St> вне форума   Ответить с цитированием
Ответ

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход



Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd. Перевод: zCarot