• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Bypassing Antivirus Userland hooks with direct system calls in x64 bit with syswow64

mose3c

RAID-массив
Забанен
Регистрация
20.11.2021
Сообщения
53
Реакции
14
Пожалуйста, обратите внимание, что пользователь заблокирован
Reversing Ntdll.dll and Hardcoding Syscalls directly from c++.

In this tutorial I will show you how to reverse Ntdll.dll functions and find the System call then hardcoded it and use it in your c++ project to bypass Userland hooks.

1 – reverse NtCreateFile and NtWriteFile functions and find the system call.
2 – Create function prototype
3 – Hardcode the function system call.
4 – Use the function in main and All done.

So lets start ,
first open your IDA and drag and drop the Ntdll.dll from c:\windows\syswow64\ntdll.dll
then make sure you import idb file .
the next step go to Exports tap and search for Ntcreatefile function .
Now we see :
Код:
mov    eax, 55h ; 'U'  ; NtCreateFile
mov    edx, offset _Wow64SystemServiceCall@0 ; Wow64SystemServiceCall()
call    edx ; Wow64SystemServiceCall() ; Wow64SystemServiceCall()
retn 2Ch; ','

move eax,55h 55 is the system call number for NtCreateFile function ,
and becouse we are in 64bit system and our application is 86bit so we need to switch to wow64bit ,
so we need to make some changes to this asm code , what we ganna do is make switch to syswow64 manualy using call dword ptr fs :[0xC0]
so the code will be like this :
Код:
mov    eax,55h
call dword ptr fs:[0xC0] ; wow64cpu.dll!X86SwitchTo64BitMode
retn 2Ch
now we have our asm code for NtcreateFile , now do the same for NtwriteFile function.

Ok we are done save the asm code to text file any were in your disk and close IDA.
Go to visual studio create new c++ empty project and follow me .
What we need now is define the typedef of our functions and ofcurse we need to define others structs and STRING , UNICODE , functions like RtlAnsiStringToUnicodeString to convert the char to string then string to unicode with less code and liss missing.
and becouse we are using Nt functions and we are hardcoding every thing we need to pay attention to the file path we want to write to it.
if we use Winapi and need to create file using CreateFileA Or CreateFileW the path will be like this . c:\users\mose3c\Desktop\my.jpg
and the winapi function will make every thing for you like converting the path to Nt Path . you can see this closely if you open ApiMonotring and trace the CreateFile function you will know what this function do for you and how much make your life easy.
now our Nt path will be like this : \\??\\c:\\users\\mose3c\\Desktop\\my.jpg
other wise will not work.
ok lets define our functions prototype and others .
Код:
typedef _Return_type_success_(return >= 0) LONG NTSTATUS;
typedef struct _STRING { USHORT Length; USHORT MaximumLength; PCHAR Buffer; } STRING;
typedef STRING * PSTRING;
typedef STRING ANSI_STRING;
typedef PSTRING PANSI_STRING;

typedef struct _UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR  Buffer;
} UNICODE_STRING, * PUNICODE_STRING;
typedef struct _OBJECT_ATTRIBUTES
{
ULONG          Length;
HANDLE          RootDirectory;
PUNICODE_STRING ObjectName;
ULONG          Attributes;
PVOID          SecurityDescriptor;
PVOID          SecurityQualityOfService;
} OBJECT_ATTRIBUTES, * POBJECT_ATTRIBUTES;
typedef struct _IO_STATUS_BLOCK
{
union
{
NTSTATUS Status;
VOID* Pointer;
};
ULONG_PTR Information;
} IO_STATUS_BLOCK, * PIO_STATUS_BLOCK;

typedef NTSTATUS(NTAPI* _RtlGetVersion)(PRTL_OSVERSIONINFOW lpVersionInformation);

typedef void (NTAPI* _RtlInitUnicodeString)(PUNICODE_STRING DestinationString, PCWSTR SourceString);
typedef DWORD(NTAPI* _RtlAnsiStringToUnicodeString)(PUNICODE_STRING, PANSI_STRING, BOOL);
typedef VOID(NTAPI* _RtlInitString)(PSTRING DestinationString, char* SourceString);
_RtlAnsiStringToUnicodeString RtlAnsiStringToUnicodeString = (_RtlAnsiStringToUnicodeString)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlAnsiStringToUnicodeString");
_RtlInitUnicodeString RtlInitUnicodeString = (_RtlInitUnicodeString)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlInitUnicodeString");
_RtlInitString RtlInitString = (_RtlInitString)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlInitString");


typedef VOID(NTAPI* PIO_APC_ROUTINE)(_In_ PVOID ApcContext, _In_ PIO_STATUS_BLOCK IoStatusBlock, _In_ ULONG Reserved);


/******* functions *******/
typedef NTSTATUS(NTAPI* _NtCreateFile)(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize OPTIONAL, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer OPTIONAL, ULONG EaLength);
typedef NTSTATUS(NTAPI* _NtWriteFile)(HANDLE  FileHandle, HANDLE Event, PIO_APC_ROUTINE  ApcRoutine, PVOID  ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID  Buffer, ULONG Length, PLARGE_INTEGER  ByteOffset, PULONG  Key);
typedef NTSTATUS(NTAPI* _NtResumeThread)(HANDLE  ThreadHandle, PULONG  SuspendCount);
typedef NTSTATUS(NTAPI* _NtClose)(HANDLE Handle);

_NtCreateFile NtCreateFile;
_NtWriteFile NtWriteFile;
_NtResumeThread NtResumeThread;
_NtClose NtClose;
now lets write our inline asm code .
Код:
_declspec(naked) NTSTATUS _stdcall WIN10_64_NtCreateFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize OPTIONAL, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer OPTIONAL, ULONG EaLength)
{
_asm
{
mov    eax, 55h
call    dword ptr fs : [0xC0]
retn 2Ch
}
}

_declspec(naked) NTSTATUS _stdcall WIN10_64_NtWriteFile(HANDLE  FileHandle, HANDLE Event, PIO_APC_ROUTINE  ApcRoutine, PVOID  ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID  Buffer, ULONG Length, PLARGE_INTEGER  ByteOffset, PULONG  Key)
{
_asm
{
mov  eax, 1A0008h
call  dword ptr fs : [0xC0]
retn 24h
}
}
ok done.whats next , lets go to to our main function.
and pass the address of WIN10_64_NtCreateFile = to NtCreateFile and WIN10_64_NtWriteFile to NtWriteFile
like this
Код:
NtCreateFile  = &WIN10_64_NtCreateFile
NtWriteFile = &WIN10_64_NtWriteFile
ok . and now we will define attributes and others args but its not our tutorial will skip it and if you need any help just replay and i will try to help you .
Код:
NTSTATUS status;
UNICODE_STRING Ustring;
ANSI_STRING as;
HANDLE hFile;
IO_STATUS_BLOCK risb;
OBJECT_ATTRIBUTES robj;

char szDir[MAX_PATH];
strcpy(szDir, "\\??\\c:\users\mose3c\Desktop\my.txt");

as.Buffer = (char*)malloc(strlen(szDir) + 1);
strcpy(as.Buffer, szDir);
as.Length = as.MaximumLength = Ustring.MaximumLength = Ustring.Length = strlen(szDir);

// convert directory name from ANSI to UNICODE
_RtlAnsiStringToUnicodeString RtlAnsiStringToUnicodeString = (_RtlAnsiStringToUnicodeString)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlAnsiStringToUnicodeString");
RtlAnsiStringToUnicodeString(&Ustring, &as, TRUE);


RtlFillMemory(&obj, sizeof(OBJECT_ATTRIBUTES), 0);
robj.Length = sizeof(obj);
robj.Attributes = OBJ_CASE_INSENSITIVE;
robj.RootDirectory = NULL;
robj.SecurityDescriptor = NULL;
robj.SecurityQualityOfService = NULL;
robj.ObjectName = &Ustring;

status = NtCreateFile(&hFile, FILE_GENERIC_READ | FILE_GENERIC_WRITE | SYNCHRONIZE, &obj, &risb, 0, FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (!NT_SUCCESS(status))
      return 1 ;

IO_STATUS_BLOCK WFISB;
status = NtWriteFile(hFile, NULL, NULL, NULL, &WFISB, /*your Buffer here*/, BufLen, 0, NULL);

if (!NT_SUCCESS(status))
return 1;

now Its bingo we have done build the project and test it if you need to run it in windows 7 you need to reverse the ntdll.dll for windows 7
becouse its wow64 you can't find alot of content and you have to done it by your self

i realy want to make more tutorials what do you like the next tutorial to be about .?
its up to you let me know in replay pleas .
 
Зачем же напрямую сервисы вызывать, в этом нет никакого смысла. Это лишь плюс к детектам, авер на эти странные вызовы запалит приложение, впрочем вирта это не пройдёт. Переключение между разрядностями compat/legacy может только отладчик сломать, тк не все могут раскодировать cf в зависимости от мода. NtCallbackReturn пройти юзер отладчиком нельзя. А что бы авер вирту сломать есть тн с-анклавы.
 

Вложения

  • anti.zip
    56.4 КБ · Просмотры: 52
Последнее редактирование:
А что бы авер вирту сломать есть тн с-анклавы.
Вопрос на засыпку, сломается ли авер вирта, если полезную нагрузку собрать по кускам из Х буферов импортированных win библиотек?)
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Зачем же напрямую сервисы вызывать, в этом нет никакого смысла
На удивление многие аверы просто инжектят dll во все процессы и на тупую ставят сплайсинг на функции в юзер моде. Я для обнаружения и вывода дизасма таких перехватов специальную тулзу делал тут: https://xss.pro/threads/43097/ (кстати, в этой тулзе есть баг: если в процессе есть dll без экспортов, то она завалится на попытке экспорты этой dll распарсить, я на него наткнулся уже после публикации, но он легко фикситься).

Там на самом деле забавно посмотреть. Например, Симантек ставит сплайсит обычным джампом на соответствующий обработчик, а Доктор Веб вкорячивает сразу несколько инструкций: в eax кладет видимо внутренний номер обработчика, а потом вызывает универсальный обработчик для всех перехватов. Кстати, у Симантека, насколько я помню, в 64-битном процессе есть хуки на нативной ntdll, в 32-битных процессах таких хуков на ntdll нет, видимо, где то внутри wow64 инфраструктуры стоят перехваты.
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
Любопытный проект с перечнем аверов и функций (в том числе Nt), которые авер хукает.

 
Пожалуйста, обратите внимание, что пользователь заблокирован
Why call services directly, it makes no sense. This is just a plus for detectives, the aver for these strange calls will light up the application, however, this will not work. Switching between compat / legacy bit depths can only break the debugger, because not everyone can decode cf depending on the mod. NtCallbackReturn cannot be traversed by the user with a debugger. And that would break aver virtual there are so-called enclaves.
As i mentiond before direct sys calls is for bypassing userland hooks not for kernel callbacks ,

and its make not sense when you use NTAPI as example the AV can hooked your function so easly but when you use direct syscalls there is no API to hook in userland
i hope you understand the idea of using direct system calls
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Любопытный проект с перечнем аверов и функций (в том числе Nt), которые авер хукает.
Такой себе метод, не обязательно первой инструкцией в патче будет jmp, как, например, у Доктора Веба.
Код:
if((*opcode << 24) >> 24 == 0xe9)
Доктор Веб вкорячивает сразу несколько инструкций: в eax кладет видимо внутренний номер обработчика, а потом вызывает универсальный обработчик для всех перехватов
 
Обьясню просто. Авер читает адрес, а авер это атом. Вирта это прочитать не может и завершается. Не по числу итераций, а тк не прошла выборка в память.
Я понимаю, что работаем над тем, чтобы авер не смог прочитать ничего по адресу, с работами твоими ознакомился. Но нужны минимально рабочие примеры, без твоего мотора, просто минималистичная реализация, пускай даже для 1 адреса, выборку к которому скрываем. (Делаем для него анклав). Мысли у меня были такие, интересно что скажешь ты на этот счёт.

1) механизм hardware breakpoints. В моём представлении, что сейчас опишу, возможно колхоз, но все же:
Имеем простейший случай. 2 апп, назовём их X, Y.

X - это «дебаггер», все что умеет, ловить прерывания во время некой выборки в память приложения Y и перезаписывать адрес этой выборки, по адресу, где находятся реальные данные.
Y - программа, находящаяся под дебагом, составленная определенным образом, чтобы в ней изначально были выборка по невалидному адресу(о котором знает дебаггер Х и «подменяет» выборку на «валид» адрес)

Это ли ты имел ввиду, описывая «харверную ловушку» в своих работах? Или есть принципиальные отличия, если да, какие? Есть ли строгое доказательство, что перенаправление выборки в данном случае словить невозможно?

2) лично мои предположения, не касается твоих работ, но мне интересно, что ты думаешь об этом.

Есть апп, в котором эксплуатируется переполнение буфера на стеке. Соответственно генерируем ROP цепочку, гаджеты берём из импортированных dll. kernel32/ntdll/user32/etc. ASLR так и стек канарейки в данном примере не волнуют, для упрощения диалога.

Вопросы:

1) произойдёт ли выборка вообще в авер эмуле, учитывая, что тут явная атака на ресурсы эмулятора. Что имею ввиду - буфер переполняется данными, которые могут прийти, по сути, откуда угодно, например, кейс - опрашиваем сервер в цикле, пока сервер через N количество времени не вернёт ROP цепочку и принимаемый буфер на клиенте не переполнится. Плюс, сможет ли понять вообще авер, что мы данными перетираем адрес возврата?

2) если и произойдёт, сможет ли авер эмуль обращаться к памяти импортированных либ или же для авера их несуществует? (Когда то читал исследования по этому вопросу, мол у аверов с эмуляцией environment все туго), но тебе виднее, слышал ты изучал эмули KIS. Я же не ресерчил, исхожу из общих описаний, интересно, что ты скажешь по этому поводу.
 
Последнее редактирование:
Haunt

Для вирт машины ав всякая апи предполагает прямой вызов, анклав же работает на разложении апи на компоненты, авер при этом падает.

Работы по этой теме были это моторы по защите памяти DYP-a/b/c может там можно что то сгрузить. Модеры конечно накосячили и ресурс уже не живой. Это когда есчо полноценный визор не был собран тн прототипы. Затем была попытка решить по ядру, написать полноценную защиту. Но ушло это в очень сложную тему по наследованию указателей, оказалось что произвольную секцию нельзя запротектить. В принципе если рабочее собрать можно провести атаку на само ядро, Но это довольно всё сложно.

> Плюс, сможет ли понять вообще авер, что мы данными перетираем адрес возврата?

А причём тут OP-атаки ?

Они на иных принципах работают. Их раскрутит авер без каких то трудностей. Эмуляция хипа не проблема.
 
А причём тут OP-атаки ?
Ну как, некая апп, в которой переполнили буфер, импортирует системные библиотеки, мы анализируем секции этих либ, они должны быть с правами на исполнение, находим там последовательности instruction;ret; реализующие интересные нам примитивы.К примеру, позволяющие загрузить данные из стека в регистры, либо иные примитивы вроде write-what-where. Далее перетираем адрес возврата, для того, чтобы передать управление на адрес instr;ret; в исполняемом адресном пространстве некой библиотеки.
Атака состоит в том, что если предположить, что авер эмуль плохо работает с окружением(к примеру вместо загрузки системных либ - там заглушки или ещё чего) - соответственно при OP атаке передача управления в эмуляторе по адресам гаджетов в библиотеке не произойдёт. Поскольку сами библиотеки не загружены/не существуют, а значит при OP атаке нет куда передавать управление. Это не утверждение, это скорее вопрос к тебе о том, как это работает в ав эмулях на самом деле и справедливо ли то, что описано выше. Ведь если да - атака сработает. Ты же реверсил там KIS и прочее..
По этому я задал вопрос, сможет ли ав эмуль переварить передачу управления по адресам импортированных библиотек и сможет ли вовсе дождаться, пока по сети придёт payload, в буфер, размеры которого апкой не контролируются. И вообще, захочет ли он эмулировать подобное, к примеру. В принципе. Учитывая, что сам по себе, например этот код не подозрителен с точки зрения «малварности».
C:
void vulnerable_function(char* string) {
    char buffer[100];
    strcpy(buffer, string);
}

int main(int argc, char** argv) {
    vulnerable_function(argv[1]);
    return 0;
}

Целое поле для исследований =)
 
Последнее редактирование:

Haunt


Какой то бред имхо. Найдёшь в памяти цепочку, может не найдёшь и что с того, эмулятору всё равно. Задача вирты крутить семпл пока сканер сигнатуру не обнаружит. Он скорее всего завершится по числу итераций, пока будет искаться rop-последовательность. Никакой к примеру паблик пе-лодер не проходит вирту по числу итераций, можно сказать по таймингу".

> vulnerable_function

В вирте тела апи нет, там атомы - сигнатуры в вирте, по которым проходит эмуляция. Это какие то невалид инструкции с ID, по которым идёт индексация для обработки на подобие сервисной. Сделано спецом что бы стаб, целиком такую апи можно было перемещать в памяти. eg mov r,ID/ничего не делающая последовательность инструкций/simulation_event.

Говорил ведь каспера вирту глянь там нет крипты, годный обучающий материал. И апи по именам отладчик находит.
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
Он скорее всего завершится по числу итераций, пока будет искаться rop-последовательность.
Никто ничего не крутит в апп, процесс поиска гаджетов и построения ROP производится заранее для уязвимого апп, ты видимо что-то не так понял.
Задача вирты крутить семпл пока сканер сигнатуру не обнаружит.
Так сигнатуры то и нет по сути в апп, получается и крутить эмулятору нечего? Тому пример - тысячи уязвимых белых приложений. Но тем не менее из instr;ret; разбросанных по всему коду, вполне себе можно скрафтить последовательность, которая исполняет шеллкод атакующего. В коде dll в r-x секциях уже находится нужные порции кода(гаджеты), либо в теле самого апп. Мы лишь находим эти участки и пытаемся с них построить алгоритм, грубо говоря, прыгая по адресам в r-x памяти.
 
Haunt

> никто ничего не крутит в апп

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

> находится нужные порции кода(гаджеты),

Есчо раз, зачем? в вирте нет что искать, там шлюзы для эмуляции, атомы. Нет системной памяти. Откуда ты этот бред вообще взял ?
 
Пожалуйста, обратите внимание, что пользователь заблокирован
сюда меня пригласили
Ну никто себя тапком в грудь за всякий бред не лупил на форуме, пришлось мне тебя пригласить)
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Это сделал тут модер, а не ты. Так что не пиши фейки.
Ну вообще-то я его попросил это сделать, но да ладно, может, ощущение своей исключительности положительно повлияет на твои депресивные состояния.
 
Так например утечки данных по ядру ищут товарищи с вуд.
При чем тут вообще это к обсуждаемой выше теме. Я сказал, что роп гаджеты не ищет само приложение внутри себя, которое будет под эмуляцией ав.

Соответственно твоя вот эта фраза не имеет смысла, ты не понял концепт:
«Он скорее всего завершится по числу итераций, пока будет искаться rop-последовательность.»

Это делается заранее. Для уже готового приложение, уязвимого к переполнению буфера. Само приложение - изначально позиционируется как белое, чистое, без вредоносного умысла. Единственный бонус для нас, так как приложение наше - мы можем сами подготовить в нем все так, чтобы упростить эксплуатацию для нас же. Ты же мне начал доказывать вообще что-то левое, о чем я не спрашивал =)
Есчо раз, зачем? в вирте нет что искать, там шлюзы для эмуляции, атомы. Нет системной памяти. Откуда ты этот бред вообще взял ?
В вирте не надо ничего искать. Ещё раз, у приложения стек будет выглядеть допустим так:
9C68CB4F-5ABE-4437-BC61-ABEB333E38DA.png

Соответственно процик будет прыгать по этим адресам и выполнять инструкции. Вопрос был к тебе конкретный, сможет ли это проэмулировать авер либо как то ещё понять вредоносный умысел, если упрощать постановку вопроса. Если вредоносный код находится не в .text секции заранее, инструкция за инструкцией. А ROP последовательность формирует вредоносный умысел из порций кода, которые УЖЕ содержаться в библиотеках, используемых приложением. Вопрос, сможет ли ав запалить вредонос в таком случае и как он будет это делать. А если не будет, является ли данный подход фундаментальным обходом эмулятора/сигнатурного анализа. Если нет - почему. Как ещё понятнее поставить вопрос, чтобы ты понял я хз. Больше вопросов к тебе не было, ни про DBI фреймворки, ни про дебаггеры, ни про твои достижения. Тем более, я не интересовался твоим мнением про мой уровень. Ты не можешь даже понять, что я у тебя спрашиваю, постановку задачи, какая проблема решается, приходится разжевывать по 5 раз…
 
> у приложения стек будет выглядеть

Нет не будет, у вирты нет кодовой секции.

> разжевывать по 5 раз…

Действительно, кури матчасть прежде чем утверждать что либо.

> Ты же мне начал доказывать вообще что-то левое

Я ничего не доказывал, а отвечал на ошибочное понимание. Смотри анклавы и забудь про ОП атаки, в вирте это не работает.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх