October 13

Пишем программы используя Wow64Transition или системные вызовы на x86

Изначально я хотел показать, как можно использовать системные вызовы на Windows для написания полноценных программ, но системные вызовы доступны только на x64, да и про них все знают, и есть уже куча готовых таблиц с номерами функций и документациями:

https://j00ru.vexillium.org/syscalls/nt/32/

А поэтому я подумал, а как использовать системные вызовы на x86? И тут-то оно, никто не писал документации и статьи на эту тему, и нет готовых таблиц с номерами для сборок на Wow64 номера.

Для начала объясню, что 32-х битные программы в 64-х битных версиях Windows исполняются в среде Wow64, надеюсь это все знают. И если в x64 можно напрямую обращаться к SSDT (таблица с функциями Windows) с помощью системных вызовов, вызывая какой-нибудь NtTerminateProcess, то на 32-х битной программе Вы должны обращаться к функциями через Wow64. У Wow64 есть функция для перехода в ядро - Wow64Transition, и в неё также как и в системных вызовах надо передавать номера.

Поскольку постоянно лезть в отладчик и смотреть номер функции неудобно, я сдампил все номера с помощью скрипта и захостил на сайт. Это первая версия таблицы, и в ней могут быть неточности, но после пары тестов всё совпадало. Есть вероятность, что номера различаются для каждой сборки Windows, позже я обновлю номера и напишу об этом в своём канале, а пока номера доступны для версии Windows 10 - 21H2.

По поводу соглашения о вызовах...

Как Вы уже догадались, вся прелесть состоит в том, что теперь Ваши программы могут состоять лишь из одной функции - Wow64Transition. Через эту функцию будут проходить все обращения к, например, NtAllocateVirtualMemory (для выделения памяти), что достаточно интересно.

Соглашение о вызовах - стандартный 32-х битный stdcall с одним нюансом - вы должны положить в стек адрес возврата из Wow64, иначе будет появляться исключение.

Пример с выделением (NtAllocateVirtualMemory) и освобождением (NtFreeVirtualMemory) памяти:

format PE GUI entry start include 'win32a.inc'

section '' readable writeable executable data 1 library ntdll, 'ntdll.dll' import ntdll,\ Wow64Transition, 'Wow64Transition' end data

size: dd 1024 addr: dd 0

start: push PAGE_READWRITE push MEM_COMMIT or MEM_RESERVE push size push 0 push addr push 0xFFFFFFFF ; CurrentProcess mov eax, 24 ; http://fasmgenius.prohosts.org/w10_21h2.php?search=NtAllocateVirtualMemory mov edx, dword[Wow64Transition] push @f call dword[edx] @@: add esp, 4*7

mov eax, dword[addr] mov dword[size], 0

push MEM_RELEASE push size push addr push 0xFFFFFFFF ; CurrentProcess mov eax, 30 ; http://fasmgenius.prohosts.org/w10_21h2.php?search=NtFreeVirtualMemory mov edx, dword[Wow64Transition] push @f call dword[edx] @@: add esp, 4*5 ret

Как видим, аргументы кладутся как обычно, если бы использовалась обычная функция NtAllocateVirtualMemory, только мы обращаемся сразу напрямую к Wow64.

Данный подход может использоваться для защиты, обфускации, или просто для написания интересного кода.