Возникли проблемы с обработкой TLS загружаемого образа. Перед обработкой TLS я проделал такие шаги:1) Загрузил дисковый PE-образ в память2) Выделил память в куче и спроецировал туда секции и заголовки3) Обработал релоки и импортТеперь надо обработать TLS, если он имеется. Я написал такой код: https://ideone.com/VAI5ppЧитаю статью с демеджлаба: Обработка TLS. Освежим в памяти структуру этой каталога: StartAddressOfRawData EndAddressOfRawData AddressOfIndex AddressOfCallbacks SizeOfZeroFill Characteristics Нам интересны первые три поля. Первые два указывают на сами данные - это статические и глобальные переменные, которые необходимо сделать уникальными для каждого потока в текущем процессе. Создаем секцию .tls для данных размера EndAddressOfRawData - StartAddressOfRawData, чтобы загрузчик выделил необходимую память для их размещения. Сами данные не копируем (обычно эти переменные равны нулю), так как это может послужить сигнатурой для детекта. Третье поле AddressOfIndex - это адрес, куда загрузчик запишет индекс блока данных потока, создаваемого при запуске программы, этот индекс, соответсвенно будет равен нулю. В это поле необходимо поместить адрес из секции данных. После загрузки защищаемого файла, мы находим его секцию TLS. Первым делом нужно найти указатель на блок данных, чтобы скопировать реальные данные. Указатель на массив адресов (адресов блоков данных) можно получить из структуры TIB, адрес TIB находится в сегментном регистре FS. Сам указатель на массив адресов находится по смещению 2ch, то есть нам нужно выполнить следующий код: mov edi, dword ptr FS:[2ch] mov edi, dword prt [edi + Index 4], но помня, что индекс первого потока равен нулю, получаем: mov edi, dword ptr FS:[2ch] mov edi, dword prt [edi] После копирования данных необходимо проверить наличие функций обратного вызова (TLS callbacks). Функция обратного вызова имеет тот же прототип что и DllMain: typedef VOID (NTAPI PIMAGE_TLS_CALLBACK) ( PVOID DllHandle, // дескриптор модуля DWORD Reason, // причина вызова PVOID Reserved // зарезервировано ); При вызове функции обратного вызова необходимо передать параметр Reason равным D LL_PROCESS_ATTACH. На это обработака TLS заканчивается.Что копировать, откуда и куда? Непонятно.>Третье поле AddressOfIndex - это адрес, куда загрузчик запишет индекс блока данных потокаКакого блока данных? Как получить его адрес?>создаваемого при запуске программы, этот индекс, соответсвенно будет равен нулюА потом как будет меняться этот индекс?>В это поле необходимо поместить адрес из секции данныхСекция данных большая, какой именно адрес?>Первым делом нужно найти указатель на блок данных, чтобы скопировать реальные данныеКопировать откуда и куда?>Указатель на массив адресов (адресов блоков данных) можно получить из структуры TIBОк, получил. Что с ним делать?>но помня, что индекс первого потока равен нулю, получаемА если потоков много, кто будет именять индекс?
> Какого блока данныхМожет, ты сначала прочитаешь, как TLS работает? Ну там про TlsAlloc хотя бы?Вкратце: у системного лоадера есть массив рандомных значений, лежит в TEB ((вот это самое ThreadLocalStoragePointer), причем при создании каждого нового потока создается новая копия, а при переключении потока, соответственно, адрес меняется на принадлежащий потоку. Индексы внутри этого массива лоадер либо назначает сам, по количеству загруженных модулей, использующих TLS (статический TLS), либо ты используешь TlsAlloc, чтобы получить в единоличное пользование одну из ячеек, функция вернет тебе ее индекс (динамический TLS). Самого по себе индекса для хранения данных мало (может у тебя там полсотни килобайт thread-local переменных, а система дает тебе всего 4 байта). Допустим, у тебя динамический TLS (то есть, им рулит написанный тобой код, а не лоадер), но максимально приблизим поведение к статическому. В DllMain по DLL_PROCESS_ATTACH ты выделяешь себе свой индекс TLS с помощью TlsAlloc, по DLL_THREAD_ATTACH выделяешь кусок памяти, чтобы все thread-local переменные влезли, и кладешь его в выделенную тебе ячейку с помощью TlsSetValue (ну так-то можешь и руками, получив адрес массива через TEB). Очевидно, что некоторые твои thread-local переменные инициализированы, поэтому при назначении им смещений ты втыкаешь их ближе к началу, а куда-нибудь в секцию данных кладешь инициализационные данные, которые ты просто копируешь в начало куска памяти, тем самым инициализируя переменные. Алсо, кроме инициализированных переменных у тебя локальными для потока могут быть экземпляры классов: ты уже выделил им память, теперь самое время позвать конструкторы. У тебя есть массив функций, которые вызывают конструкторы, и ты вызываешь их поочередно, а они получают адрес thread-local переменной (см. ниже) и вызывают для нее конструктор. Очистку по DLL_x_DETACH додумаешь сам. Дальше все банально. Твой код, когда он хочет локальную переменную, вызывает TlsGetValue (индекс, напомню, не меняется во время жизни модуля, поэтому код просто читает глобальную переменную, в которую DllMain его записал) или просто парсит TEB (чаще всего используется этот вариант - компилятор инлайнит getfsdword плюс немного арифметики). Получив адрес куска памяти, содержащего thread-local переменные, код читает из него по заранее назначенному смещению нужную переменную.Теперь ты знаешь все базовые понятия и можешь проследить параллели с полями директории TLS. Но чтобы все работало, тебе нужно что? Правильно, тебе нужна инициализация при создании потока. И если ты грузишь не DLL, у тебя есть один основной способ: ты делаешь у загрузчика фэйковый TLS с одним колбеком, в котором делаешь то же, что и DllMain делает по DLL_THREAD_ATTACH в примере с динамическим TLS, в качестве бонуса тебе автоматически дадут TLS-индекс и уже выделенную область памяти, ну и ты можешь заранее попросить размер побольше, чтобы точно хватило (можешь освобождать выделенное системой и выделять сам, так тоже норм, ибо HeapAlloc - это просто форвардинг для RtlAllocateHead, которой пользуется лоадер). Твой колбек инициализирует область thread-local переменных и вызывает колбеки загружаемого модуля. Можно еще восстанавливать TLS, регистрировать модуль в PEB_LDR_DATA, как положено, и пусть лоадер сам ебется при создании потока, но инициализация при загрузке все равно остается на твоей совести, и, в общем-то, решение не стоит всей этой возни.Что касается кучи вопросов, то ты теперь сам можешь на них ответить.