Опустим пояснения за шмот (что это, зачем и нахуя ты это выложил) и перейдём сразу к делу.
Коротко о том как сейчас работает последняя версия анти-стиллера.
> WIN API хуки на интернет функции и тд. (После обновления их число несколько уменьшилось в целях оптимизации, часть хуков на опасные функции были перенесено в ntdll.dll (NT API)
> NATIVE API хуки (запись в память стороннего процесса, выделение и смена прав памяти в процессах, создание своего процесса, создание удалённых потоков, Windows хуки, переборы списков запущенных процессов а так же поиски окон.
В кратце сейчас анти-стиллер делает всё чтобы не позволить даже такие обходы как инжект дллки в левый процесс а так же создание своего собственного каким либо образом.
Даже попытки получить адрес какой либо интересующей функции NT/WIN API через GetProcAddress либо непосредственно через свой кастомный парсер Export TABLE обречены на провал поскольку анти-стиллер хукает таблицу экспорта в загруженных системных модулях. При попытке достать какой либо адрес на захуканую им функцию вы получите не её реальный адрес а сразу же адрес хука анти-стиллера(Здесь сразу же отсев долбоёбов, весьма умно).
Так же в нём весьма распространено перекрёстное перехвачиванние внутренних функций из реализации какой либо WIN/NT API функции чтобы наверняка не помог даже перепрыг хука.
Его самозащита организована следующим образом - отнимаются права памяти на перехваченную функцию благодаря чему нельзя удалить хук, но как же VirtualProtect? Опять таки не всё так просто, он тоже не имеет в себе прав на запись в память и сам же захукан чтобы им нельзя было менять атрибуты памяти там где не нужно.
Так какие варианты у нас есть?
> Эмуляция прямого системного вызова на Native API функции (Но это чревато проблемами с разными номерами сервисов от версии и даже от её номера текущего билда а так же различной архитектурой на x32-x64 системах)
> Эмуляция пролога системного вызова (первых 5 байт) с последующим переходом в оригинал но на 5 байт дальше (перепрыгивание хука) - разумеется что данный способ лучше за предыдущий но всё равно требует чтения номера сервиса в функциях которые находятся в коде выше/ниже текущего тела функции но в отличии от способа выше нам не нужно эмулировать остальные инструкции, достаточно лишь узнать номер сервиса. Но всё равно такой способ часто конфликтует, по непонятным мне причинам с некоторым перечнем .asi плагинов. Да и на разных ОС - порядок расположения функций может отличатся а некоторых и во все может не быть.
> Manual Mapping дубликатной системной библиотеки, например ntdll.dll но не уверен что она будет корректно работать под хуками анти-стиллера по скольку все её функции имеют тот же RVA что и в оригинале и внутренние функции из реализации экспортированного интерфейса могут вызывать не свои а "оригинальные". Вообщем у меня хватает сомнений касательно этого способа.
> Инжект своей DLL в сторонний процесс (например samp.exe который почти всегда запущен вместе с игрой до конца её сеанса), сама процедура инжекта будет производится посредством Native API.
(NtOpenProcess + NtAllocateVirtualMemory + NtWriteVirtualMemory + NtCreateThreadEx) но опять таки требует другого способа получения ида интересующегося процесса.
Каким путём пойдём мы?
Лично я выбрал последний способ потому что он самый простой и удобный в реализации а главное стабильный на различных версиях ОС. Я решил его несколько модифицировать чтобы у нас была возможность использовать весь тот перечисленный функционал не смотря на хуки анти-стиллера.
Открываем отладчик, зайдём внутрь тела хука и посмотрим что же там происходит и каким образом происходит отсев исключений для разрешенных вызовов.
В качестве примера выступит ZwWriteVirtualMemory
Залетаем внутрь тела хука и наблюдаем такую картину, по скольку анти-стиллер позволяет вызывать Nt/ZwWriteVirtualMemory для собственного процесса, ебашим у себя вызов, ставим железный бряк и трассируем, проделав тоже самое с хендлом к левому процессу можно увидеть что решающий момент определяет вот этот условный переход который и является фильтром вызова после которого нас перекидает в место откуда передаются параметры функции и осуществляется переход на трамплин.
Но как же нам его пропатчить если в предыдущем месте у нас не было прав на запись?
Переходим к карте памяти и смотрим какие права у нас есть на само тело хука
Благодаря тому что автор забыл забирать права на запись внутри самого хука - его можно патчить.
Накидаем простенькую функцию патчущую нужную нам функцию. Но стоит помнить что это нам поможет только в том случае если у функции есть трамплин.
C++:
Код:
void
PatchAS
(
const
char
*
libname
,
const
char
*
funcName
)
{
DWORD JE_Offset
=
(
DWORD
)
GetProcAddress
(
GetModuleHandleA
(
libname
)
,
funcName
)
;
// Поскольку // гетпрокадрес захукан - он вернёт нам сразу же базовый адрес хука который нам и нужен.
while
(
*
(
byte
*
)
JE_Offset
!=
0x74
)
JE_Offset
++
;
// ищем первую встречную JE инструкцию
*
(
byte
*
)
JE_Offset
=
0xEB
;
// патчим её на короткий безусловный прыжок
}
Отлично! Теперь можно пропатчить все необходимые нам NT функции из цепочки необходимой для DLL инжекта. А уже когда наша длл попадёт в сторонний процесс - мы можем с неё творить всё что душе влезит
Проблему с поиском ида нужного нам процесса решим следующим образом, анти-стиллер патчит перебор процессов но не патчит перебор потоков внутри структуры которых можно узнать ид процесса к которому он пренадлежит и через OpenProcess + GetModuleInformationEx узнать его имя что даст нам возможность найти нужный ид процесса.
C++:
Код:
DWORD
GetProcID
(
const
char
*
ProcName
)
{
THREADENTRY32 th32
;
HANDLE hSnapshot
=
NULL
;
th32
.
dwSize
=
sizeof
(
THREADENTRY32
)
;
hSnapshot
=
CreateToolhelp32Snapshot
(
TH32CS_SNAPTHREAD
,
0
)
;
if
(
Thread32First
(
hSnapshot
,
&
th32
)
)
{
do
{
if
(
th32
.
th32OwnerProcessID
==
GetCurrentProcessId
(
)
)
continue
;
HANDLE hProc
=
OpenProcess
(
PROCESS_ALL_ACCESS
,
FALSE
,
th32
.
th32OwnerProcessID
)
;
if
(
!
hProc
)
continue
;
if
(
!
strcmp
(
GetProcName
(
hProc
)
.
c_str
(
)
,
ProcName
)
)
{
CloseHandle
(
hProc
)
;
CloseHandle
(
hSnapshot
)
;
return
th32
.
th32OwnerProcessID
;
}
else
CloseHandle
(
hProc
)
;
}
while
(
Thread32Next
(
hSnapshot
,
&
th32
)
)
;
}
if
(
hSnapshot
!=
INVALID_HANDLE_VALUE
)
CloseHandle
(
hSnapshot
)
;
return
-
1
;
}
Гайд by ЯedЯuM специально для портала blast.hk