Сейчас на форуме: jinoweb, bartolomeo, rmn (+5 невидимых)

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

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

Создано: 01 августа 2017 23:13
· Личное сообщение · #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
Ок, получил. Что с ним делать?

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




Ранг: 331.1 (мудрец), 561thx
Активность: 0.190.06
Статус: Участник

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

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

-----
Everything is relative...




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

Создано: 02 августа 2017 12:58
· Личное сообщение · #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 и в нулевой ячейке выделил память для необходимого числа переменных. Скорее всего, я что-то сделал не так.




Ранг: 338.5 (мудрец), 348thx
Активность: 2.112.42
Статус: Участник

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

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

STATUS_PORT_CONNECTION_REFUSED

-----
vx




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

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

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

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




Ранг: 338.5 (мудрец), 348thx
Активность: 2.112.42
Статус: Участник

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

Aoizora

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

-----
vx




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

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

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




Ранг: 338.5 (мудрец), 348thx
Активность: 2.112.42
Статус: Участник

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

Aoizora

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

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

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

9a91_07.08.2017_EXELAB.rU.tgz - Tls.zip

-----
vx


| Сообщение посчитали полезным: plutos
 eXeL@B —› Программирование —› Обработка TLS
:: Ваш ответ
Жирный  Курсив  Подчеркнутый  Перечеркнутый  {mpf5}  Код  Вставить ссылку 
:s1: :s2: :s3: :s4: :s5: :s6: :s7: :s8: :s9: :s10: :s11: :s12: :s13: :s14: :s15: :s16:


Максимальный размер аттача: 500KB.
Ваш логин: german1505 » Выход » ЛС
   Для печати Для печати