глобализация перехвата с помощью ZwResumeThread

Тема в разделе "WASM.BEGINNERS", создана пользователем daemion, 5 мар 2008.

  1. daemion

    daemion New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2008
    Сообщения:
    7
    Доброго всем времени суток. Имеется следующая проблема. Руководствуясь статьей уважаемого Ms-rem'а, перехватываю функцию ZwQueryDirectoryFile для скрытия определенных файлов на диске (в исключительно мирных целях :) ) Данный перехват устанавливается процессу explorer.exe и для пресечения обнаружения файлов с помощью иных файловых менеджеров, перехватывается ZwResumeThread, т.е при создании нового процесса их экспорера ему (процессу) также устанавливается перехват.
    После подсадки дллки запускаемому процессу, управление передается реальной функции ZwResumeThread.
    Однако, в этом случае, появляются странные глюки в запущеном процессе, начиная от проблем с отображением окна, заканчивая аварийным завершением оного. Если же передавать управление реальной функции до установки перехвата, то имеется шанс на успешную работу, обусловленный порядком выполнения потоков. При установке перехвата запущеному процессу, никаких проблем нет. То есть если дождаться полной загрузки приложения, а после этого устанавливать перехват, то все работает как часы. Вопрос - каковы причины некорректной работы в первом случае? Заранее благодарен за ответ
     
  2. n0name

    n0name New Member

    Публикаций:
    0
    Регистрация:
    5 июн 2004
    Сообщения:
    4.336
    Адрес:
    Russia
    код?
     
  3. daemion

    daemion New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2008
    Сообщения:
    7
    Код (Text):
    1. NewZwCreateThread proc ThreadHandle:PHANDLE, DesiredAccess: ACCESS_MASK, ObjectAttributes:DWORD, \
    2.                   ProcessHandle:HANDLE, ClientId: DWORD, ThreadContext:DWORD, \
    3.                   UserStack:DWORD, CreateSuspended:BOOL
    4.     LOCAL Written:DWORD
    5.     LOCAL Res:DWORD
    6.     invoke WriteProcessMemory, hCurrentProcess, AdrCreateThread, addr OldCodeCreate, sizeof OldCode_t, \
    7.                                addr Written
    8.     invoke ZwCreateThread, ThreadHandle, DesiredAccess, ObjectAttributes, ProcessHandle, \
    9.                            ClientId, ThreadContext, UserStack, TRUE
    10.     mov Res, eax
    11.     mov eax, ClientId
    12.     mov eax, [eax]
    13.     .IF (CurrentProcessId != eax)
    14.         mov NewProcess, 1
    15.     .ELSE
    16.         mov NewProcess, 0
    17.     .ENDIF
    18.     .IF (!CreateSuspended)
    19.         mov eax, ThreadHandle
    20.         mov eax, [eax]
    21.         invoke ResumeThread, eax
    22.     .ENDIF
    23.     invoke WriteProcessMemory, hCurrentProcess, AdrCreateThread, addr JumpCreate, sizeof Jump_t, \
    24.                                addr Written
    25.     mov eax, Res
    26.     ret
    27.    
    28. NewZwCreateThread endp
    29.  
    30.  
    31. NewZwResumeThread proc ThreadHandle:DWORD, PreviousSuspendCount:PDWORD
    32.     LOCAL ThreadInfo:THREAD_BASIC_INFORMATION
    33.     LOCAL Res:DWORD
    34.     LOCAL Written:DWORD
    35.     invoke WriteProcessMemory, hCurrentProcess, AdrResumeThread, addr OldCodeResume, SizeOf OldCode_t, \
    36.                        addr Written                      
    37.     invoke ZwQueryInformationThread, ThreadHandle, THREAD_BASIC_INFO, addr ThreadInfo, \
    38.         sizeof THREAD_BASIC_INFORMATION, NULL
    39.     mov eax, ThreadInfo.ClientId.UniqueProcess
    40.     .IF (eax != CurrentProcessId)
    41.             .IF (NewProcess)
    42.                 mov NewProcessId, eax
    43.                 mov eax, ThreadInfo.ClientId.UniqueThread
    44.                 invoke OpenProcess, PROCESS_ALL_ACCESS, FALSE, NewProcessId
    45.              mov hNewProcess, eax
    46.                 invoke InjectDll, hNewProcess, addr BothlibName
    47.                 invoke CloseHandle, hNewProcess
    48.                 invoke MessageBoxA, 0, 0, 0, 0
    49.                 mov NewProcess, 0                      
    50.             .ENDIF
    51.     .ENDIF
    52.     invoke ZwResumeThread, ThreadHandle, PreviousSuspendCount
    53.     mov Res, eax
    54.     invoke WriteProcessMemory, hCurrentProcess, AdrResumeThread, addr JumpResume, SizeOf Jump_t, \
    55.                         addr Written
    56.     mov eax, Res
    57.     ret
    58. NewZwResumeThread endp
    59.  
    60. InjectDll proc Process:HANDLE, ModulePath:LPCTSTR
    61.         LOCAL hKernel32:HANDLE
    62.         LOCAL hThread: HANDLE
    63.         LOCAL ThreadId:DWORD
    64.         invoke VirtualAllocEx, Process, NULL, sizeof Inject_t, MEM_COMMIT, PAGE_EXECUTE_READWRITE
    65.         mov Memory, eax
    66.         .IF (!EAX)
    67.             invoke ExitProcess, 0
    68.         .ENDIF
    69.         mov Inject.PushCommand, 68h
    70.         mov eax, Memory
    71.         add eax, 1Eh
    72.         mov Inject.PushArg, eax
    73.         mov ax, 15FFh
    74.         mov Inject.CallCommand, 15FFh
    75.         mov eax, Memory
    76.         add eax, 16h
    77.         mov Inject.CallAddr, eax
    78.         mov Inject.PushExitThread, 68h
    79.         mov eax, 0
    80.         mov Inject.ExitThreadArg, 0
    81.         mov eax, 15FFh
    82.         mov Inject.CallExitThread, 15FFh
    83.         mov eax, Memory
    84.         add eax, 1Ah
    85.         mov Inject.CallExitThreadAddr, eax
    86.         invoke GetModuleHandle, addr Kernel32Name
    87.         mov hKernel32, eax
    88.         invoke GetProcAddress, hKernel32, addr LoadLibraryName
    89.         mov Inject.AddrLoadLibrary, eax
    90.         invoke GetProcAddress, hKernel32, addr ExitThreadName
    91.         mov Inject.AddrExitThread, eax
    92.         invoke lstrcpy, addr Inject.LibraryName, ModulePath
    93.         invoke WriteProcessMemory, Process, Memory, addr Inject, sizeof Inject_t, NULL
    94.         invoke CreateRemoteThread, Process, NULL, 0, Memory, NULL, 0, addr ThreadId
    95.         mov hThread, eax
    96.         invoke WaitForSingleObject, hThread, INFINITE
    97.         invoke CloseHandle, hThread
    98.         mov eax, 1
    99.         ret
    100.        
    101. InjectDll endp
     
  4. zoool

    zoool New Member

    Публикаций:
    0
    Регистрация:
    1 дек 2007
    Сообщения:
    412
    Те же траблы

    Сделал сплайс на Get/PostMessage
    Начались глюки с отображением

    Как выяснилось, в буффер (первые байты функции) попадали инструкции с относительной адресацией. Пришлось привинчивать дизасм.
     
  5. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Во первых не понятно зачем перехватывать ZwResumeThread ?
     
  6. daemion

    daemion New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2008
    Сообщения:
    7
    потому что любая функция создания процесса после создания главного потока и инициализации контекста запускает этот главный поток как раз с помощью ZwResumeThread


    2 zoool:

    глюки были постоянными?
     
  7. Freeman

    Freeman New Member

    Публикаций:
    0
    Регистрация:
    10 фев 2005
    Сообщения:
    1.385
    Адрес:
    Ukraine
    совершенно тупой код для многопоточных приложений. хоть бы потоки тормозил перед установкой/снятием хука.
     
  8. EvilPhreak

    EvilPhreak New Member

    Публикаций:
    0
    Регистрация:
    29 дек 2007
    Сообщения:
    154
    В статье Ms-Rem'а код не рабочий (полностью - в смысле не полностью рабочий - т.к. возможны глюки), поэтому советую прочитать вот эту тему по поводу чистой глобализации перехвата - http://www.wasm.ru/forum/viewtopic.php?id=17433 и последний ответ в ней как чистое решение вопроса.
     
  9. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    daemion
    Строго говоря, смотрите сами, что Вы делаете. При создании нового процесса вызывается ZwCreateThread. Управление получает Ваш перехватчик, который сам же и вызывает ResumeThread, после которого практически сразу получает управление Ваш второй перехватчик. И где здесь, подскажите, происходит инициализация процесса (объяснение Ms-Rem'а на этот счет я так и не понял)?
    Я не проверял, будет ли работать CreateRemoteThread в момент перехвата ZwCreateThread, но это и не нужно. Там в принципе достаточно eip (а лучше eax, т.к. eip как раз указывает на инициализатор, без которого как минимум SEH работать не будет) подменить в аргументе ThreadContext и пойдет выполняться сначала Ваш код, в котором Вы и поставите перехватчики в новом процессе.
    Для Вас это играет небольшую роль, т.к. zoool, видимо, ставил неснимаемый перехват, а Вы снимаете и ставите каждый раз заново, так что относительная адресация Вам не важна.
     
  10. daemion

    daemion New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2008
    Сообщения:
    7
    спасибо за столь конструктивную критику.
    З.Ы. Потоки останавливаются, при инициализации загружаемой библиотеки, хоть это и не принципиально, потому что основной поток еще не запущен, а других быть до запуска основного не может в данном случае.


    Большое спасибо, я посмотрю.

    не совсем так. при вызове функции, так или иначе создающей процесс, создается главный поток, но он создается
    с CreateSuspended = TRUE, поэтому он не запускается сразу же. Обработчик выходит в тело функции создания процесса, где и происходит инициализация процесса. Только потом вызывается ZwResumeThread. Я также не проверял работоспособность CreateRemoteThread в ZwCP, но мне интересен не обходной путь, а именно почему в случае перехвата до запуска основного потока запускаемый поток глючит.
     
  11. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Это не универсальный способ, перехватывай лучше ZwCreateThread. А ещё лучше ZwCreateProcess(Ex)
     
  12. daemion

    daemion New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2008
    Сообщения:
    7
    Насчет ZwCreateThread уже было сказано, а вот ZwCreateProcess, это действительно должно помочь, спасибо.
    Но тем не менее интересно, что не так с первым способом
     
  13. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Ты канкретно цель скажи, перехватить функцию ZwQueryDirectoryFile во всех процессах ?
    Получаеш список процессов через ZwQuerySystemInformation и перечисляеш их. Внедряй код с помощью выделения памяти или проецирования секции и записи в неё обработчиков, а не спомощью LoadLibrary. Можно записывать обработчики в свободное межсекционное пространство модулей, что и желательно делать для скрытости. Перехватывай ZwCreateProcess, когда эта функция будет выполнена в процессе нет потоков, внедряй туда код(перехватывай что надо). Для перехвата не используй сплайсинг, а используй перехват самих переходников ZwXX что обладает очень высокой гибкостью. При создании удалённых потоков используй предварительно сплайсинг KiUserApcDispatcher, а затем только создание потока в процессе, иначе могут поймать. Использование сплайсинга KiFastSystemCallRet тоже обеспечивает гибкость и для этого не надо определять значение регистра Eip всех потоков в процессе, тк изменяется однобайтовая команда. Для остановки всех потоков в процессе, кроме твоего используй ZwSuspendProcess/ZwResumeThread(MyThread), а не перечисление потоков с их остановкой. Сплайсинг неоднобайтовых команд влечёт некоторые трудности, вероятность неверного выполнения команд при записи, поэтому придётся проверять где находятся Eip потоков в текущий момент времени.
     
  14. EvilPhreak

    EvilPhreak New Member

    Публикаций:
    0
    Регистрация:
    29 дек 2007
    Сообщения:
    154
    В момент вызова ZwResumeThread процесс может быть не полностью проинициализирован по причине не полной отработки UserAPC на LdrInitializeProcess(). Поэтому в твоем случае самым простым способом будет следующий вариант - все оставляешь также, только в момент вызова ZwResumeThread (т.е. в твоем обработчике) не делаешь никаких инжектов LoadLibrary(что и является самым опасным), не создаешь удаленных потоков, а пишешь свой код на EP - там может быть твой вызов LoadLibrary, восстановление EP и последующая передача управления на него. В этом случае когда код на EP получил управление, ты точно будешь знать что процесс инициализирован, и можно уже делать все что угодно.
     
  15. daemion

    daemion New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2008
    Сообщения:
    7
    Возможно и так. То что инициализация не завершена не критично для выполнения главной нити?

    Завтра испробую что-нибудь из посоветованного. Всем огромное спасибо за помощь!
     
  16. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    Clerk
    Ужас. Ну и накрутили. А если еще понаперехватить кучу функций, да перейти на нулевое кольцо, да там все DT со всеми SS-сами позаменять, да еще пол-ядра перепатчить, то можно Windows в FreeBSD превратить.
    И ничего он не повлечет. Другое дело, что сплайсинг неоднобайтовЫМИ командами влечет за собой трудности при патчинге команд с большей длиной, но и это не так для данного случая. Вы строго знаете, что сплайсите в ZwCreateThread пятибайтовый mov пятибайтовым jmp и никаких трудностей здесь не возникнет.
    EvilPhreak
    И все-таки проще было бы вообще удалить перехват ZwResumeThread, оставить только ZwCreateThread. И раз уж на "пишешь свой код на EP" пошло, то вообще отказаться от внедрения dll, а внедрять только чистый базонезависимый код.
     
  17. EvilPhreak

    EvilPhreak New Member

    Публикаций:
    0
    Регистрация:
    29 дек 2007
    Сообщения:
    154
    l_inc
    Не-не, тогда будет сложно, базонезависимый код это лишние ненужные сложности. Просто внедряем вызов LoadLibrary на EP.

    это ничего не даст. надо поймать момент когда процесс полностью проинициализирован, но еще не начал исполнения.

    daemion
    Критично конечно. Но когда первичный поток начинает исполнятся процесс уже проинициализирован.
     
  18. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    В последнем моём посте, в разделе Projects, был выложен код, перехватывающий некоторые фцнкции в csrss.
    Код рабочий, но возник вопрос по удалению обработчика, что приводило к падению системы с BSOD. В последствии я отказался от классического сплайсинга, изза вероятности выполнения не до конца записанных команд потоком, при пересечении(например сплайсинг, когда размер всех заменяемых на jmp команд не равен размеру команды jmp) на однопроцессорной машине проблем не возникает, но таких уже мало, на двухъядерных приводит к ошибкам. Надо либо пересчитывать контексты всех потоков, ожидая освобождения данного диапазона памяти, либо запрещать потокам выполнятся на остальных процессорах(ProcessAffinityMask). Да, сплайсинг команды размером как у команды jmp на однопроцессорной машине выполняется успешно, на многопроцессорных машинах существует вероятность ошибки.
     
  19. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    В рассуждения насчёт сплайсинга надо пираться на то, что запись памяти с помощью сервиса NtWriteVirtualMemory не является АТОМАРНОЙ операцией.
     
  20. daemion

    daemion New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2008
    Сообщения:
    7
    Как в таком случае на момент вызова ZwResumeThread процесс может быть не полностью инициализирован? Это же по сути начало выполнения ЕР. Или я не прав?