Как удалить ключ реестра Windows с помощью ассемблера?
Мне нужно с помощью любого языка ассемблера (но желательно MASM или NASM) удалить какой-нибудь ключ реестра Windows. Как я понял, для этого нужно сделать или системный вызов (в случае MASM я не нашел, как это сделать, ведь там нет инструкции, подобной syscall), или использовать функцию из какой-то библиотеки. Итак, вот что я пробовал:
- я попытался совершить системный вызов с помощью NASM, поместив в регистр EAX значение 212 (потому что в моей сборке Windows 10 такой номер у функции NtDeleteKey). Я не нашел официальной документации к этой функции (номер найден на неофициальном источнике), но нашел подобную функцию ZwDeleteKey. Но я не смог передать в нее параметры - потому что нужен дескриптор, а он получается из функции ZwOpenKey, а в ней один из аргументов является указателем на структуру, а у меня ноль идей как создать такое на языке ассемблера. И я не смог найти однозначного ответа на то, как вообще передавать аргументы - все через стек или часть через регистры (на разных источниках почему-то разные ответы)?
- я пробовал подключать библиотеку на MASM с помощью инструкции include/includelib, но код не компилировался. А именно я писал это:
include \masm32\include\masm32rt.inc
include \masm32\include\advapi32.inc
includelib \masm32\lib\advapi32.lib
И я не нашел этих файлов на своем компьютере. Каким образом они вообще подключаются? Нужно ли скачивать эти библиотеки? И когда использовать библиотечные функции, а когда системные вызовы? Как совершить системный вызов в MASM? И как же все-таки удалить ключ реестра Windows на языке ассемблера?
нужно удалить именно ключ - то есть какой-либо раздел в реестре, следующий сразу за одной из одной из пяти директорий - HKEY_CURRENT_USER, HKEY_CLASSES_ROOT и т.д. Код на языке ассемблера мне нужен для того, чтобы сделать из него шелл-код и использовать для эксплуатации уязвимости "переполнение буфера" в рамках лабораторной работы.
Ответы (1 шт):
для этого нужно сделать или системный вызов (в случае MASM я не нашел, как это сделать, ведь там нет инструкции, подобной syscall), или использовать функцию из какой-то библиотеки.
Чтобы разобраться в теме, вам нужно установить отладчик WinDbg, и скачать справочник "Гарри Неббет: Native API Reference".
В справочнике найдёте описание большинства нативных API, а в отладчике можно будет просмотреть форматы всех структур, включая нужную для ZwOpenKey()
"OBJECT_ATTRIBUTES".
В поле "RootDirectory" этой структуры вы должны записать указатель на строку с разделом реестра типа "HKEY_CURRENT_USER", а в поле "ObjectName" указатель на структуру с unicode-строкой подраздела реестра. В логе отладчика видно, что на х64 размер этой структуры равен 30h байт - его прописываете в поле "Length". Параметры функции ZwOpenKey()
и константы атрибутов найдёте в справочнике Г.Неббета:
NTSTATUS ZwOpenKey(
OUT PHANDLE KeyHandle,
IN ACCESS_MASK DesiredAccess, // <--- указать "KEY_ALL_ACCESS"
IN POBJECT_ATTRIBUTES ObjectAttributes );
----------------------------------------------
0: kd> dt _Object_Attributes -v
struct _OBJECT_ATTRIBUTES, <------ 6 elements, 0x30 bytes
+0x000 Length : Uint4B
+0x008 RootDirectory : Ptr64 to Void
+0x010 ObjectName : Ptr64 to struct _UNICODE_STRING (3 elements, 0x10 bytes)
+0x018 Attributes : Uint4B
+0x020 SecurityDescriptor : Ptr64 to Void
+0x028 SecurityQualityOfService : Ptr64 to Void
Здесь нужно помнить, что на системах х64 параметры функциям передаются через регистры RCX/RDX/R8/R9
(соглашение "fastcall"), а на х32 только через стек (stdcall). Если всё сделаете правильно, то с ~50-ой попытки должно всё получиться.
Номера системных вызовов отличаются не только на разных версиях Win, но даже на разных сборках Build внутри одной версии. Если нужна кроссплатформенность, то придётся вычислять номер syscall
динамически, через поиск "адреса функции" в таблице-экспорта Ntdll.dll, и дизассма первых шести байт на предмет сигнатуры b8
(см.вторую строку) - после неё и будет лежать слово со-значением номера syscall
. Здесь выручает отладчик, логи которого представлены ниже (у меня Win7-x64, Build 6.1.7601):
0: kd> uf ZwOpenKey ;//<--- Unassemble Function
ntdll!NtOpenKey:
00000000`7774bc50 4c8bd1 mov r10,rcx
00000000`7774bc53 b80f000000 mov eax,0Fh ;//<-- ZwOpenKey() = 16
00000000`7774bc58 0f05 syscall
00000000`7774bc5a c3 ret
0: kd> uf ZwOpenKeyEx
ntdll!NtOpenKeyEx:
00000000`7774ca80 4c8bd1 mov r10,rcx
00000000`7774ca83 b8f2000000 mov eax,0F2h ;//<-- ZwOpenKeyEx() = 242
00000000`7774ca88 0f05 syscall
00000000`7774ca8a c3 ret
Когда функция отработает, в указанный вами аргумент "PHANDLE" она вернёт дескриптор ветки реестра, через который можно будет удалить её вызовом уже ZwDeleteKey()
- номер этого вызова так-же знает отладчик:
0: kd> uf ZwDeleteKey
ntdll!NtDeleteKey:
00000000`7774c690 4c8bd1 mov r10,rcx
00000000`7774c693 b8b3000000 mov eax,0B3h ;//<-- ZwDeleteKey() = 179
00000000`7774c698 0f05 syscall
00000000`7774c69a c3 ret
А вообще, связываться с syscall
не советую. Если шелл для своего процесса, лучше создавать его документированным кодом Win32-API. Всё-что для этого нужно - вычислить дельту в памяти, и поместить все строки в сам шелл, перепрыгивая их джампами. Правда размер при этом получается больше, но для учебных примеров это не критично, ведь главное в них сама техника создания.
Вот консольное приложение х64, чтобы продемонстрировать последовательность вызовов функций из либы "advapi32.dll". Код написан на ассемблере fasm. Чтобы воспроизвести его, нужно создать в разделе реестра "HKCU" дир для тестов "TestSybkey", после чего запустить прожку. По окончании обновить окно реестра по F5, и созданная папка исчезнет. Если удалить из этого исходника лишнее (от метки start, и до exet), то получим бинарник в виде шелла для своего процесса.
format pe64 console
entry start
include 'win64ax.inc'
;//------------------
.data
Hndl dd 0 ;// вернётся дескриптор ветки
subKey db 'TestSybkey',0 ;// ветка реестра
;//------------------
section '.code' code readable executable
start:
;// Открывем ветку реестра, чтобы получить её дескриптор
invoke RegOpenKeyEx,HKEY_CURRENT_USER,subKey,0,KEY_ALL_ACCESS,Hndl
or eax,eax
jz @next
cinvoke printf,' Error code: %08x',eax
jmp @exit
;// Вырезать этот блок, если пишем шелл
@next: cinvoke printf,' Delete key "HKCU\TestSybkey\" ??? y/n.. '
@inp: cinvoke getch
cmp al,'y' ;// вдруг юзер передумает
je @yes
cmp al,'n'
je @ex
jmp @inp
;// Удалить ветку в разделе реестра
@yes: invoke RegDeleteKey,[Hndl],subKey
or eax,eax
jz @exit
cinvoke printf,' Error code: %08x',eax
;// Обязательно закрыть дескриптор, иначе раздел не удалится!
@exit: invoke RegCloseKey,[Hndl]
cinvoke getch
@ex: cinvoke exit,0
;//-------------------------------------
section '.idata' import data readable
library msvcrt,'msvcrt.dll', advapi32,'advapi32.dll'
import msvcrt, printf,'printf', scanf,'scanf', getch,'_getch', exit,'exit'
import advapi32, RegOpenKeyEx,'RegOpenKeyExA',RegDeleteKey,'RegDeleteKeyA',RegCloseKey,'RegCloseKey'