Проблемы с обработкой статического TLS

Тема в разделе "WASM.BEGINNERS", создана пользователем PaperMoon, 13 авг 2017.

Метки:
  1. PaperMoon

    PaperMoon New Member

    Публикаций:
    0
    Регистрация:
    13 авг 2017
    Сообщения:
    16
    Тут уже была похожая тема, однако я столкнулся с такой проблемой, я пытаюсь статический TLS перенести из загружаемой проги в свой загрузчик, делаю так:

    Код (C):
    1. BOOL HandleTls(DWORD dwImageBase) {
    2.  
    3.  
    4.  
    5.     PIMAGE_DOS_HEADER pTargetDosHeader = (PIMAGE_DOS_HEADER)dwImageBase;
    6.     PIMAGE_NT_HEADERS pTargetNtHeaders = (PIMAGE_NT_HEADERS)(dwImageBase + pTargetDosHeader->e_lfanew);
    7.  
    8.     if (!pTargetNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress)
    9.         return FALSE;
    10.  
    11.     PIMAGE_TLS_DIRECTORY pTargetTlsDirectory = (PIMAGE_TLS_DIRECTORY)(dwImageBase +
    12.         pTargetNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress);
    13.  
    14.     DWORD dwSize = pTargetTlsDirectory->EndAddressOfRawData - pTargetTlsDirectory->StartAddressOfRawData;
    15.  
    16.     PVOID lpTls = VirtualAlloc(NULL, dwSize, MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE);
    17.  
    18.     DWORD dwStartAddr = pTargetTlsDirectory->StartAddressOfRawData;
    19.  
    20.     memcpy(lpTls, (PVOID)dwStartAddr, dwSize);
    21.  
    22.     DWORD dwAddr = pTargetTlsDirectory->AddressOfIndex;
    23.  
    24.     DWORD dwArraySize = *(DWORD*)dwAddr;
    25.  
    26.     DWORD * dwArray = (DWORD*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (1  + size) * sizeof(DWORD*));
    27.     dwArray[0] = (DWORD)lpTls;
    28.     _asm {
    29.         mov eax, dwArray
    30.         mov fs:[0x2C], eax
    31.     }
    32.  
    33. }
    Tls обрабатываю после релокации, копирую данные из адреса в поле StartAddressOfRawData в свой выделенный кусок памяти

    Код (C):
    1.  PVOID lpTls = VirtualAlloc(NULL, dwSize, MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE);
    Индекс tls модуля всегда нулевой, поэтому дальше выделяю память под массив DWORD'ов,

    Код (C):
    1. DWORD * dwArray = (DWORD*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (1  + size) * sizeof(DWORD*));
    В нулевой элемент копирую адрес lpTls , дальше меняю массив в teb->ThreadLocalStoragePointer через:

    Код (ASM):
    1.  mov eax, dwArray
    2. mov fs:[0x2C], eax
    Далее запускаю цель, в ней в main вывожу через MessageBox переменную из TLS, всё нормально. Но вот когда я создаю поток в целевом приложении и в нём вывожу эту же переменную там мусор, вот код тестируемого приложения:

    Код (C):
    1. __declspec(thread) int var = 777;
    2.  
    3.  
    4. void NTAPI tls_callback(PVOID DllHandle, DWORD dwReason, PVOID)
    5. {
    6.  
    7.     return;
    8. }
    9.  
    10. #ifdef _WIN64
    11. #pragma comment (linker, "/INCLUDE:_tls_used")
    12. #pragma comment (linker, "/INCLUDE:tls_callback_func")
    13. #else
    14. #pragma comment (linker, "/INCLUDE:__tls_used")
    15. #pragma comment (linker, "/INCLUDE:_tls_callback_func")
    16. #endif
    17.  
    18.  
    19. #ifdef _WIN64
    20. #pragma const_seg(".CRT$XLF")
    21. EXTERN_C const
    22. #else
    23. #pragma data_seg(".CRT$XLF")
    24. EXTERN_C
    25. #endif
    26. PIMAGE_TLS_CALLBACK tls_callback_func = tls_callback;
    27. #ifdef _WIN64
    28. #pragma const_seg()
    29. #else
    30. #pragma data_seg()
    31. #endif
    32.  
    33.  
    34. DWORD WINAPI ThreadProc(CONST LPVOID lpParam)
    35. {
    36.     char buf[16] = "";
    37.     itoa(var, buf, 10);
    38.     MessageBoxA(0, buf, buf, 0);
    39.  
    40.     ExitThread(0);
    41. }
    42.  
    43. int main(void)
    44. {
    45.  
    46.     char buf[16] = "";
    47.     itoa(var, buf, 10);
    48.  
    49.     MessageBoxA(0, buf, buf, 0);
    50.  
    51.     CreateThread(NULL, 0, &ThreadProc, 0, 0, NULL);
    52.     Sleep(3000);
    53.     return 0;
    54. }
    Я посмотрел в WinDbg, по бряку на запись в fs:[0x2C], когда создаётся поток функция ntdll!LdrpAllocateTls пишет в fs:[0x2C], расширяя массив, но данные с TLS туда не копируются почему-то, в итоге в переменных мусор. Как-то можно решить эту проблему? Ну кроме хука создания потока и правки руками fs:[0x2c]
     
  2. PaperMoon

    PaperMoon New Member

    Публикаций:
    0
    Регистрация:
    13 авг 2017
    Сообщения:
    16
    Потестировал ещё, создал аналогичного размера tls секцию в лоадере, положил в переменную по тому же смещению другое число, в итоге в мейне выводит как надо, в новом потоке выводятся данные из tls секции лоадера. Откуда загрузчик заполняет данными teb->ThreadLocalStoragePointer ? Я думал сначала что он лезет в tls директорию у лоадера, но подменив в ней адреса StartAddressOfRawData/
    EndAddressOfRawData

    я вижу что при создании потока системные функции всё равно заполняют
    teb->ThreadLocalStoragePointer данными с tls секции лоадера. Я так понимаю нужно где-то что-то поравить в TEB/ntdll ещё, но я в упор этого не вижу. Пока что решил проблему просто созданием в лоадере секции такого же размера как и в цели и копированием туда всего содержимого. Наверное это лучше чем хуки CreateThread всё-таки
     
  3. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    PaperMoon,

    Я уже писал на кл:

    Код (Text):
    1. LdrpMapDllWithSectionHandle ->
    2. LdrpMapAndSnapDependency ->
    3. LdrpSnapModule ->
    4. LdrpDoPostSnapWork ->
    5. LdrpHandleTlsData ->
    6. LdrpAllocateTlsEntry
    Это значит что тлс настраивается при загрузке модуля.
     

    Вложения:

    • Tls.zip
      Размер файла:
      15,7 КБ
      Просмотров:
      490
    PaperMoon и maestro-ant нравится это.
  4. PaperMoon

    PaperMoon New Member

    Публикаций:
    0
    Регистрация:
    13 авг 2017
    Сообщения:
    16
    Я понял, получается нужно будет составлять паттерн для каждой ос и искать в памяти нтдлл LdrpHandleTlsData, которая перенесёт тлс из модуля в переменную соотвествующую и настроит fs:2ch. Жаль что нельзя без этого обойтись, P.S. Я большой фанат ваших работа по антиэмуляции, могу я вам задать технический вопрос и если да где это лучше сделать в личке или прямо здесь тему создать?
     
  5. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    PaperMoon,

    Нет смысла какие то переменные искать, есть общие решения. Эмулировать механизм (тлс) - зачем его исполнять непосредственно, если загрузка имитируется и не совместима с нтлдр :preved:

    > могу я вам задать технический вопрос и если да где это лучше сделать в личке или прямо здесь тему создать?

    Спросите тут, я не использую лк.
     
  6. PaperMoon

    PaperMoon New Member

    Публикаций:
    0
    Регистрация:
    13 авг 2017
    Сообщения:
    16
    Хорошо я тут спрошу, немного не по теме конечно:

    1) Я читал ваши статьи про детект атомов через Data Fetch. Правильно ли я понял, что при эмуляции некоего приложения avvm эмулирует функции используемые в импортах, меняя вызов апи на некий свой обработчик который эмулирует вызов апи, если допустим импорты отсутствуют то как я понял, по паттернам в коде можно задетекить использование той или иной апи, так как аввм имеет куски кода функций системных длл. Исходя из всего этого я накидал такое:

    Код (C):
    1. PCHAR lpStr = "test";
    2. BOOL bFlag = FALSE;
    3.  
    4.  
    5. LONG NTAPI OnHit(PEXCEPTION_POINTERS ExceptionInfo)
    6. {
    7.  
    8.    UINT_PTR uBase = GetVirtualBaseByAddress(hNtdllAddr); // адрес по которому загружена ntdll
    9.    DWORD dwModuleSize = GetModuleSize(uBase);
    10.  
    11.    CONTEXT ctx;
    12.  
    13.    if (ExceptionInfo->ExceptionRecord->ExceptionAddress >= uBase && ExceptionInfo->ExceptionRecord->ExceptionAddress < uBase + dwModuleSize) {
    14.  
    15.      GetThreadContext(hThread, &ctx);
    16.    
    17.      ctx.Dr0 = 0
    18.      ctx.Dr7 = 0;
    19.    
    20.      ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
    21.  
    22.      SetThreadContext(hThread, &ctx);
    23.  
    24.      RemoveVectoredExceptionHandler(handler);
    25.  
    26.    }
    27.    else {
    28.      GetThreadContext(hThread, &ctx);
    29.    
    30.      ctx.Dr0 = 0;  
    31.      ctx.Dr7 = 0;
    32.    
    33.      ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
    34.  
    35.      SetThreadContext(hThread, &ctx);
    36.  
    37.      RemoveVectoredExceptionHandler(handler);
    38.  
    39.      lpStr[0] = '\0';
    40.    
    41.      bFlag = TRUE;
    42.    }
    43.  
    44.    return EXCEPTION_CONTINUE_EXECUTION;
    45. }
    46.  
    47. PCHAR lpStr = "test";
    48. BOOL bFlag = FALSE;
    49.  
    50. DWORD Test(PVOID pv)
    51. {
    52.    HANDLE hFile = 0;
    53.  
    54.    hFile = OpenEvent(0,0,str);
    55.  
    56.    ExitThread(0);
    57.  
    58. }
    59.  
    60. void Check()
    61. {
    62.  
    63.  
    64.    if (handler = AddVectoredExceptionHandler(TRUE, OnHit))
    65.    {
    66.  
    67.    
    68.      if (hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)Test, 0, CREATE_SUSPENDED, 0))
    69.      {
    70.        CONTEXT ctx;
    71.      
    72.        GetThreadContext(hThread, &ctx);
    73.      
    74.        ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
    75.      
    76.        ctx.Dr3 = (ULONG_PTR)str;
    77.      
    78.        ctx.Dr7 = 0xF0000040;
    79.  
    80.        SetThreadContext(hThread, &ctx);
    81.  
    82.        ResumeThread(hThread);
    83.  
    84.        WaitForSingleObject(hThread, INFINITE);
    85.  
    86.        CloseHandle(hThread);
    87.      }
    88.  
    89.  
    90.    }
    91.  
    92. }
    93.  
    94. int main()
    95. {
    96.         Check();
    97.        if(bFlag){
    98.                   //атом?
    99.       }
    100.  
    101. }
    102.  
    Ставится хардвейр бряк на чтение из переменной lpStr, устанавливается хэндлер эксепшонов OnHit. Далее запускается поток в котором вызывается OpenEvent. Я это понял так, при нормальных условиях, я получу бряк в ntdll, при вызове OpenEvent. При выполнении OpenEvent, срабатывает бряк, вызывается эксепшон. Я проверяю в каком модуле бряк произошёл, если он в пределах Ntdll, значит всё ок. Но если оно будет проэмулировано, то бряк будет в некоем другом модуле аввм? Правильно ли я понял вашу концепцию?

    2) Заодно уже) Вы упоминали на rohitab в теме http://www.rohitab.com/discuss/topic/42697-the-best-ever-method-keylogger/?p=10106007

    Что есть RIT/rit-apc? Я знаю про Raw Input NtUserRegisterRawInputDevices/NtUserGetRawInputData, но что есть Rit-Apc? Я не вижу в win32k.sys Apc апи связанных с Raw Input'ом
     
  7. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    PaperMoon,

    RIT - поток сырого ввода, это рекурсивный цикл ожидания сообщений от устройств ввода через APC. Это базовый цикл, который принимает запросы от мыша етц и передаёт запросы в тень(shadow). Именно там можно хорошо всториться кейлоггеру, так что не обнаружить. Но помимо этого важен есчо и виртуальный ввод данных, он на этом уровне не присутствует, например посылка сообщения окну.

    > Правильно ли я понял, что при эмуляции некоего приложения avvm эмулирует функции используемые в импортах

    Нет. Та серия статей не закончена, но они малое отношение к вм аверской имеют. Атомы это концепт, общий принцип. То что их использует вм лишь частный случай. Вы не поняли суть к сожалению, наверно я плохо умею описывать, хотя будет конечная статья обьединяющая это всё. Идея весьма хорошо проработана, если кратко - приложение запускается под dye(реалтайм разновидность визора), всё адр пространство не исполняемо. Отслеживаются все выборки данных(DF), формируется полный DFG через маркеры в памяти. Таким образом наследуются указатели и вероятные указатели(так как иногда данные не отличимы от указателя). Это делает невозможным передачу управления на произвольный адрес(классический инжект невозможен, так как ап NX) и из за валидации DFG делает практически невозможным OP-инжект(ROP/COP etc). Как следствие любой атом обнаруживается.

    > Правильно ли я понял, что при эмуляции некоего приложения avvm эмулирует функции используемые в импортах, меняя вызов апи на некий свой обработчик который эмулирует вызов

    Вы не правильно представляете всё это. Замены функций нет, просто системный код иной, чем в вм. В ней сложная обработка выделяется как атом, спец вызов, который обрабатывается не вм. При этом не происходит выборка данных по адресам аргументов, что нарушает поток данных(DFG).

    На счёт обработки hwb - не знаю что вы хотите сделать, но это приведёт к ошибке в вм, для этого нужно множество серьёзных тестов с аналитикой, разобрать как оно работает. Иначе поведение вм не предсказуемо.
     
    PaperMoon нравится это.
  8. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    Код весьма старый, но он мало изменялся и главное суть:

    Код (Text):
    1. /***************************************************************************\
    2. * RawInputThread (RIT)
    3. *
    4. * This is the RIT.  It gets low-level/raw input from the device drivers
    5. * and posts messages the appropriate queue.  It gets the input via APC
    6. * calls requested by calling NtReadFile() for the keyboard and mouse
    7. * drivers.  Basically it makes the first calls to Start*Read() and then
    8. * sits in an NtWaitForSingleObject() loop which allows the APC calls to
    9. * occur.
    10. *
    11. * All functions called exclusively on the RIT will have (RIT) next to
    12. * the name in the header.
    13. *
    14. * History:
    15. * 10-18-90 DavidPe      Created.
    16. * 11-26-90 DavidPe      Rewrote to stop using POS layer.
    17. \***************************************************************************/
     

    Вложения:

    • ntinput.rar
      Размер файла:
      33,6 КБ
      Просмотров:
      324
    PaperMoon нравится это.