Как удалить ключ реестра Windows с помощью ассемблера?

Мне нужно с помощью любого языка ассемблера (но желательно MASM или NASM) удалить какой-нибудь ключ реестра Windows. Как я понял, для этого нужно сделать или системный вызов (в случае MASM я не нашел, как это сделать, ведь там нет инструкции, подобной syscall), или использовать функцию из какой-то библиотеки. Итак, вот что я пробовал:

  1. я попытался совершить системный вызов с помощью NASM, поместив в регистр EAX значение 212 (потому что в моей сборке Windows 10 такой номер у функции NtDeleteKey). Я не нашел официальной документации к этой функции (номер найден на неофициальном источнике), но нашел подобную функцию ZwDeleteKey. Но я не смог передать в нее параметры - потому что нужен дескриптор, а он получается из функции ZwOpenKey, а в ней один из аргументов является указателем на структуру, а у меня ноль идей как создать такое на языке ассемблера. И я не смог найти однозначного ответа на то, как вообще передавать аргументы - все через стек или часть через регистры (на разных источниках почему-то разные ответы)?
  2. я пробовал подключать библиотеку на 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 шт):

Автор решения: PRODIGY

для этого нужно сделать или системный вызов (в случае 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'
→ Ссылка