Русский / Russian English / Английский

Сейчас на форуме: astranexus, strannyi (+4 невидимых)
 · Начало · Статистика · Регистрация · Поиск · ПРАВИЛА ФОРУМА · Язык · RSS ·

 eXeL@B —› Программирование —› Обработка TLS
Посл.ответ Сообщение

Ранг: 9.0 (гость)
Статус: Участник

Создано: 1 августа 2017 23:13 New!
Цитата · Личное сообщение · #1

Возникли проблемы с обработкой TLS загружаемого образа. Перед обработкой TLS я проделал такие шаги:

1) Загрузил дисковый PE-образ в память
2) Выделил память в куче и спроецировал туда секции и заголовки
3) Обработал релоки и импорт

Теперь надо обработать TLS, если он имеется. Я написал такой код:

Code:
  1. bool LoadPE_DirectoryExists(LoadPE_CONTEXT* ctx, unsigned long id)
  2. {
  3.          return (ctx->pPeHdr->OptionalHeader.NumberOfRvaAndSizes - 1) >= id
  4.                  && ctx->pPeHdr->OptionalHeader.DataDirectory[id].VirtualAddress;
  5. }
  6.  
  7. bool LoadPE_HasTLS(LoadPE_CONTEXT* ctx)
  8. {
  9.     return LoadPE_DirectoryExists(ctx, IMAGE_DIRECTORY_ENTRY_TLS);
  10. }
  11.  
  12. DWORD LoadPE_GetTLS(LoadPE_CONTEXT* ctx)
  13. {
  14.     return ctx->pPeHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress;
  15. }
  16.  
  17. void LoadPE_ProcessTLS(LoadPE_CONTEXT* ctx)
  18. {
  19.     if (LoadPE_HasTLS(ctx))
  20.     {
  21.         PIMAGE_TLS_DIRECTORY32 tls = PIMAGE_TLS_DIRECTORY32(ctx->pbRealImageBase + LoadPE_GetTLS(ctx));
  22.         std::cout << "StartAddress: " << std::hex << tls->StartAddressOfRawData << std::endl;
  23.         std::cout << "EndAddress: " << std::hex << tls->EndAddressOfRawData << std::endl;
  24.         std::cout << "CallbackAddress: " << std::hex << tls->AddressOfCallBacks << std::endl;
  25.         std::cout << "Callbacks: " << std::endl;
  26.         PIMAGE_TLS_CALLBACK* cb_addr = (PIMAGE_TLS_CALLBACK *)(ctx->pbRealImageBase + tls->AddressOfCallBacks);
  27.         while (*cb_addr++)
  28.         {
  29.             std::cout << cb_addr << std::endl;
  30.         }
  31.     }
  32. }


Пытался исправить его и написал так:

Code:
  1. void LoadPE_ProcessTLS(LoadPE_CONTEXT* ctx)
  2. {
  3.          if (LoadPE_HasTLS(ctx))
  4.          {
  5.                  PIMAGE_TLS_DIRECTORY32 tls = PIMAGE_TLS_DIRECTORY32(ctx->pbRealImageBase + LoadPE_GetTLS(ctx));
  6.                  std::cout << "StartAddress: " << std::hex << tls->StartAddressOfRawData << std::endl;
  7.                  std::cout << "EndAddress: " << std::hex << tls->EndAddressOfRawData << std::endl;
  8.                  std::cout << "CallbackAddress: " << std::hex << tls->AddressOfCallBacks << std::endl;
  9.                  std::cout << "Callbacks: " << std::endl;
  10.                  PIMAGE_TLS_CALLBACK* cb_addr = (PIMAGE_TLS_CALLBACK *)(tls->AddressOfCallBacks);
  11.                  while (*cb_addr++)
  12.                  {
  13.                         std::cout << cb_addr << std::endl;
  14.                  }
  15.  
  16.                  DWORD* lpDataBlock = (DWORD *)__readfsdword(0x2C);
  17.                  std::cout << std::hex << *lpDataBlock << std::endl;
  18.  
  19.                  auto data_begin = tls->StartAddressOfRawData;
  20.                  auto data_end = tls->EndAddressOfRawData;
  21.                  auto i = 0;
  22.                  while (data_begin != data_end)
  23.                  {
  24.                         std::cout << *(DWORD*)data_begin << " at " << data_begin << std::endl;
  25.                         lpDataBlock[i++] = data_begin;
  26.                         data_begin += 4;
  27.                  }
  28.          }
  29. }


Но при вычислении условия цикла программа крашится. Что у меня не так?

Кроме того, я не до конца понимаю механизм работы TLS, хотя сегодня и прочитал несколько статей. В директории TLS имеется структура с указателями на данные и на массив адресов коллбэков. Коллбэки вызываются загрузчиком, а что делать с адресами данных? Где и для чего выделять память под индекс? Блок данных можно получить при помощи _readfswdord(0x2C). Что с ним делать дальше?

Code:
  1. Обработка TLS.
  2. Освежим в памяти структуру этой каталога:
  3. StartAddressOfRawData
  4. EndAddressOfRawData
  5. AddressOfIndex
  6. AddressOfCallbacks
  7. SizeOfZeroFill
  8. Characteristics
  9.  
  10. Нам интересны первые три поля. Первые два указывают на сами данные - это статические и глобальные переменные, которые необходимо сделать уникальными для каждого потока в текущем процессе. Создаем секцию .tls для данных размера EndAddressOfRawData - StartAddressOfRawData, чтобы загрузчик выделил необходимую память для их размещения. Сами данные не копируем (обычно эти переменные равны нулю), так как это может послужить сигнатурой для детекта. Третье поле AddressOfIndex - это адрес, куда загрузчик запишет индекс блока данных потока, создаваемого при запуске программы, этот индекс, соответсвенно будет равен нулю. В это поле необходимо поместить адрес из секции данных.
  11. После загрузки защищаемого файла, мы находим его секцию TLS. Первым делом нужно найти указатель на блок данных, чтобы скопировать реальные данные. Указатель на массив адресов (адресов блоков данных) можно получить из структуры TIB, адрес TIB находится в сегментном регистре FS. Сам указатель на массив адресов находится по смещению 2ch, то есть нам нужно выполнить следующий код:
  12.  
  13. mov edi, dword ptr FS:[2ch]
  14. mov edi, dword prt [edi + Index * 4], но помня, что индекс первого потока равен нулю, получаем:
  15.  
  16. mov edi, dword ptr FS:[2ch]
  17. mov edi, dword prt [edi]
  18.  
  19. После копирования данных необходимо проверить наличие функций обратного вызова (TLS callbacks). Функция обратного вызова имеет тот же прототип что и DllMain:
  20. typedef VOID (NTAPI *PIMAGE_TLS_CALLBACK) (
  21. PVOID DllHandle, // дескриптор модуля
  22. DWORD Reason, // причина вызова
  23. PVOID Reserved // зарезервировано
  24. );
  25. При вызове функции обратного вызова необходимо передать параметр Reason равным DLL_PROCESS_ATTACH. На это обработака TLS заканчивается.


Что копировать, откуда и куда? Непонятно.

>Третье поле AddressOfIndex - это адрес, куда загрузчик запишет индекс блока данных потока
Какого блока данных? Как получить его адрес?

>создаваемого при запуске программы, этот индекс, соответсвенно будет равен нулю
А потом как будет меняться этот индекс?

>В это поле необходимо поместить адрес из секции данных
Секция данных большая, какой именно адрес?

>Первым делом нужно найти указатель на блок данных, чтобы скопировать реальные данные
Копировать откуда и куда?

>Указатель на массив адресов (адресов блоков данных) можно получить из структуры TIB
Ок, получил. Что с ним делать?

>но помня, что индекс первого потока равен нулю, получаем
А если потоков много, кто будет именять индекс?


Ранг: 329.6 (мудрец)
Статус: Участник

Создано: 2 августа 2017 08:41 New!
Цитата · Личное сообщение · #2

Почитайте инфу от разработчика сей штуки, тогда вам станет ясно что за данные и для чего. --> Link <--

Ранг: 9.0 (гость)
Статус: Участник

Создано: 2 августа 2017 12:58 New!
Цитата · Личное сообщение · #3

Прочитал эту статью и мало что понял.

Code:
  1. void LoadPE_ProcessTLS(LoadPE_CONTEXT* ctx)
  2. {
  3.          if (LoadPE_HasTLS(ctx))
  4.          {
  5.                  PIMAGE_TLS_DIRECTORY32 tls = PIMAGE_TLS_DIRECTORY32(ctx->pbRealImageBase + LoadPE_GetTLS(ctx));
  6.                  std::cout << "Callbacks: " << std::endl;
  7.                  PIMAGE_TLS_CALLBACK* cb_addr = (PIMAGE_TLS_CALLBACK *)(tls->AddressOfCallBacks);
  8.                  while (*cb_addr++)
  9.                  {
  10.                         std::cout << cb_addr << std::endl;
  11.                  }
  12.  
  13.                  PTEB teb = (PTEB)__readfsdword(0x18);
  14.                  teb->ThreadLocalStoragePointer = HeapAlloc(
  15.                         GetProcessHeap(),
  16.                         HEAP_ZERO_MEMORY,
  17.                         tls->EndAddressOfRawData - tls->StartAddressOfRawData
  18.                  );
  19.  
  20.                  auto data_begin = tls->StartAddressOfRawData;
  21.                  auto data_end = tls->EndAddressOfRawData;
  22.                  while (data_begin != data_end)
  23.                  {
  24.                         std::cout << *(DWORD*)data_begin << " at " << data_begin << std::endl;
  25.                         ((DWORD *)teb->ThreadLocalStoragePointer)[0] = data_begin;
  26.                         data_begin += 4;
  27.                  }
  28.          }
  29. }


Я получил таблицу TLS-слотов из TEB и в нулевой ячейке выделил память для необходимого числа переменных. Скорее всего, я что-то сделал не так.


Ранг: 331.0 (мудрец)
Статус: Участник

Создано: 6 августа 2017 02:07 · Поправил: difexacaw New!
Цитата · Личное сообщение · #4

Эта задача не может быть корректно решена. Это частный случай Run-PE через реинит процесса. Вы можите только эмулировать среду(TLS), но такая обработка не полноценна. Для норм реализации загрузчик должен быть вызван повторно, для такого вызова должны быть сброшены его переменные. И первый облом при этом происходит на установке соединения с csrss --> Link <--

STATUS_PORT_CONNECTION_REFUSED

Ранг: 9.0 (гость)
Статус: Участник

Создано: 6 августа 2017 22:02 New!
Цитата · Личное сообщение · #5

>Эта задача не может быть корректно решена. Это частный случай Run-PE через реинит процесса

Ты путаешь понятия. RunPE это инжект в другой процесс, иначе называется process hollowing.
Я пишу код, который проецирует PE-файл на адресное пространство того же процесса. Хотя я согласен насчет того, что такой загрузчик реализуется рекурсивно: например, для того, чтобы этим же загрузчиком загрузить необходимые DLL (ведь подразумевается, что другого загрузчика нет, а DLL грузить надо).


Ранг: 331.0 (мудрец)
Статус: Участник

Создано: 6 августа 2017 23:14 New!
Цитата · Личное сообщение · #6

Aoizora

Нет, не путаю. Это вы не понимаете - что бы запустить полноценную обработку некоторых механизмов, нужно рестартить загрузчик. Тоесть если он уже отработал, то механизм уже не запустить или это можно сделать сложным путём, доставать интернал структуры из модуля. В случае с тлс эмулировать его видимо лучший способ.

Ранг: 9.0 (гость)
Статус: Участник

Создано: 6 августа 2017 23:58 New!
Цитата · Личное сообщение · #7

Эмуляция TLS? Каким образом это реализуется?


Ранг: 331.0 (мудрец)
Статус: Участник

Создано: 7 августа 2017 03:40 New!
Цитата · Личное сообщение · #8

Aoizora

Это как вы и делаете, тоесть не система это всё обрабатывает.

Я тут покопался, получается следующее. В 10-ке тлс обрабатывается при загрузке модуля LdrpSnapModule -> LdrpDoPostSnapWork -> LdrHandleTlsData, таким образом вызываются тлс колбеки при обычной загрузке, даже если exe не содержит тлс. Аналогично в 7 и 8.

Это значит что если загрузчик сделан грамотно, как обёртка над системным загрузчиком, то тлс механизм поддерживается без проблем. Иначе можно попробовать вызвать принудительно загрузочные апи для обработки тлс.

{ Атач доступен только для участников форума } - Tls.zip

| Сообщение посчитали полезным: plutos

 eXeL@B —› Программирование —› Обработка TLS

Видеокурс ВЗЛОМ