И вновь продолжается бойАктивная антиотладка и антиинжект по Рихтеру
И сердцу тревожно в груди..
(Из песни времен СССР.)
Далее будет использован синтаксис Microsoft Си. Проекты в конце статьи – под
Microsoft Visual Studio v.6.0 . Разговор далее пойдет только о Ring3.
Недавно работая над написанием плагина под Sysinternals Process Explorer, стлучайно столкнулся с функцией RtlQueryProcessDebugInformation из ntdll.dll, которая оказывается использует удаленные потоки для получения информации о стороннем процессе. Т.к. мой плагин был реализован в виде DLL, сразу вспомнились далекие годы, когда я читал Рихтера и его описание функции DllMain, а именно уведомление DLL_THREAD_ATTACH, получаемое этой процедурой при инициализации нового потока в процессе, обычно довольно редко используемое. Как оказалось вызов DllMain с этим уведомлением происходит именно из контекста вновь создаваемого потока. Это натолкунуло на естественную, в данном случае, мысль о возможности защиты своего и не только своего приложения от инжекта и выполнения чужеродного кода в своем адресном пространстве методом создания удаленнных потоков без всяких глобальных перехватов ZwWriteProcessmemory и ZwCreateThread, (как это делают многие антивирусы и программные сетевые экраны (фаерволлы)). Так для однопоточного приложения достаточно нижеприведенного кода для получения невозможности создания потока в нем:
где NtSleep – это макрос, аналог Sleep из kernel32.dll (я в основном использую нативные функции):Код (C++):
BOOL APIENTRY DllMain(HMODULE hModule, ULONG ul_reason_for_call,LPVOID lpReserved) { if(ul_reason_for_call==DLL_THREAD_ATTACH) NtSleep(INFINITE); //или RtlExitUserThread () или еще чего.. }
В данном примере мы просто замораживаем созданный поток на неопределенный период.Код (C++):
void WINAPI NtSleepEx(ULONG DurationMs,ULONG Alertable) { struct {ULONG Low; ULONG Hi; } MLI={{-10000*DurationMs},{0xFFFFFFFF}}; NtDelayExecution(Alertable,(PLARGE_INTEGER)&MLI); } #define NtSleep(_x) NtSleepEx(_x,0)
Как же быть в многопоточных приложениях, где мы рискуем «не разрешить» свой собственный (нужный нам поток). Здесь есть два простых варианта:
Вот практически полный исходный код нашей dll:
- Вызывать какую-либо созданную нами функцию из нашей DLL при создании нового потока для того что бы передать в нее ThreadID создаваемого потока, с последующем сравнении в функции DllMain с текущим (думаю все поняли, расписывать этот метод нет смысла).
- Более продвинутый метод, позволяющий защищать не только свои приложения, – это перехват функции NtCreateThread в своем процессе для получения ThreadID потоков создаваемых именно нашим приложением (сторонние потоки будут созданы из вне, а т.к. там ничего нами не перехвачено ThreadID нами получено не будет) для последующей проверки соответствия полученного ThreadID и ThreadID который мы получим в функции DllMain в контексте создаваемого потока из структуры TEB или GetCurrentThreadID. В общем нагляднее все выглядит в коде.
© sysenter 2012 sysenter@jabber.no
Активная антиотладка и антиинжект
Дата публикации 7 сен 2017
| Редактировалось 10 янв 2018