Статья Пишем службу для расшифровки паролей Wi-Fi

В штате буквально всех современных ОС имеется служебный персонал теневого фронта - в Windows его назвали службы "Services". Здесь мы рассмотрим интерфейс программирования их на ассемблере fasm, а так-же способы обмена данными с пользовательским приложением. В качестве практического руководства с нуля напишем софт, который позволит расшифровать пароли Wi-Fi на текущей машине (актуально для буков).

1. Общие сведения​
2. Диспетчер SCM - Service Control Manager​
3. Процесс службы в сессии(0)​
4. Программа управления службой в сессии(1)​
5. Заключение​



1. Общие сведения

Служба Win - это работающее в фоне приложение без привычного интерфейса (окна). Главное отличие службы от обычной программы - контекст выполнения. Их запускает процесс svchost.exe (Service Host) в закрытой сессии(0) от имени специальных/трёх учётных записей: это Local System, Local Service, и Network Service. Они живут своей жизнью независимо от того, вошёл кто-то в систему, или нет. Службы работают в адресном пространстве пользователя, а не как драйвера в пространстве системы, со всеми вытекающими.


1.1. Ключевые характеристики

• Фоновый режим: Нет окон, диалогов, иконок в трее, хотя служба может общаться с юзер-приложением.
• Автозапуск: Можно настроить на запуск при старте ОС, или в ручную.
• Привилегии: Имеют расширенные права доступа к системе и оборудованию.

1.2. Типы запуска "Start Type"

• Auto: Стартует при загрузке системы.
• Demand: Запускается через ~2 минуты после ОС, чтобы юзер быстрее увидел рабочий стол.
• Manual: Запуск только при необходимости (например, подключаем принтер).
• Disabled: Отключена - функционал полностью заморожен.

1.3. Учётные записи "Security Context"

Службы работают под спец.учётными записями, что определяет уровень их доступа к системе:

1. LocalSystem: Наивысший уровень, с полным доступом ко всей системе. В некоторых случаях опасно, т.ч. есть ещё 2 альтернативы.​
2. LocalService: Мин.привилегии на лок.машине. В сеть выходит как анонимный пользователь.​
3. NetworkService: Аналогичен (2), но в сети авторизуется от имени "MachineName$", что позволяет работать с сетевыми ресурсами, и ActiveDirectory.​

Ранее уже обсуждались сведения по сессиям и учёткам, поэтому повторяться не буду - вот линк:
https://codeby.net/threads/asm-demony-v-windows-1-sessii-stantsii-rabochiye-stoly.79421/


2. Диспетчер SCM - Service Control Manager

Управлением служб занимается "Service Control Manager" (диспетчер управления). По факту это процесс services.exe, который запускается самым первым при загрузке ОС. SCM выполняет три главные задачи:

1. Поддержка базы-данных в ветке реестра HKLM\System\CurrentControlSet\Services, где хранятся записи о каждой службе: её имя, тип запуска, путь к файлу, и параметры восстановления при сбое.​
2. Запуск и остановка. По команде админа (или типу запуска в реестре) SCM создаёт процесс службы, и управляет её жизненным циклом.​
3. Коммуникация. Службы сообщают SCM о своём состоянии: Выполняет инициализацию, Запущена, Останавливается.​

Современные версии Win активно используют концепцию совместного размещения служб. К примеру некоторое их подмножество может работать внутри одного процесса svchost.exe. Это сделано для экономии памяти (общие dll не дублируются). Но есть и обратная сторона медали: если упадёт один процесс svchost в котором живёт 10 служб, все они станут недоступны. Получить список служб для каждого из активных процессов можно командой tasklist /svc.

На схеме ниже видно, что любой запрос к службе от приложения проходит через SCM. Если служба находится в состоянии "Running", она обязана функцией SetServiceStatus() периодически сообщать диспетчеру, мол "в Багдаде всё спокойно" и я просто жду команд. Иначе SCM посчитает, что у неё имеются какие-то проблемы, и принудительно удалит из памяти (хотя может перезагрузить). Это один из основных нюансов при программировании служб Windows.

SrvTop.webp


3. Процесс службы в сессии(0)

Служба это не просто EXE-файл. Чтобы приложение стало службой, оно должно быть написано по строго определённому протоколу:

1. Уметь принимать команды SCM, типа остановка, пауза, продолжение работы.​
2. С периодом не более 30-сек (дефолт) сообщать диспетчеру о своём состоянии.​

ServiceFuncList.webp

Значит точкой входа в службу является процедура обратного вызова Main(), которая через StartServiceCtrlDispatcher() сразу должна зарегистрировать основной поток нашей службы. Суть в том, что внутри одной службы может быть контейнер до 64 дочерних служб, как показано на рис.ниже. При этом SCM каждому выделяет свои потоки Thread. Но ничто не мешает собрать весь перечисленный ниже функционал типа сеть, FS, мониторинг и прочие внутри одного основного потока. То-есть это просто такая архитектура, без навязывания прогерам:

SrvDspTable.webp

У функции StartServiceCtrlDispatcher() всего 1 параметр - это указатель на таблицу, которая содержит в себе по 2 указателя для описания каждой службы: первый линк на имя, а второй на точку-входа ServiceMain(). Терминальный нуль прихлопывает таблицу. Вот фрагмент, как это реализовано у меня:

C-подобный:
format   pe64 gui
include 'win64ax.inc'
include 'equates\services.inc'
entry    start
;//----------
.data
dspTable   dq  srvName          ;//<---- Таблица для StartServiceCtrlDispatcher()
           dq  ServiceMain      ;// в данном случае всего 1 запись.
           dq  0                ;//<---- терминальный нуль
srvName    db  'WiFiService',0
srvHndl    dq  0
.....
;//----------

section '.code' code readable executable
start:   pop     rax
         push    rax rax
         invoke  StartServiceCtrlDispatcher,dspTable
         xor     eax,eax
         ret

align 16
proc  ServiceMain argNum,argStr   ;//<---- Точка входа в службу
         mov     [argNum],rcx
         mov     [argStr],rdx
         invoke  RegisterServiceCtrlHandlerEx,\
                                      srvName,\
                             ServiceRoutineEx,0   ;//<---- Важно! Линк на обработчик событий
         mov     [srvHndl],rax
.....

На точке-входа в службу ServiceMain(), происходит уже инициализация прочих компонентов, и наконец регистрация обработчика событий, для которого SCM выделит отдельный поток. Именно здесь нужно через SetServiceStatus() периодически уведомлять SCM о различных этапах типа SERVICE_START_PENDING (инициализация), после чего переход в состояние RUNNING (активен). Процедуру оповещения лучше оформить как универсальную, т.к. один из её параметров указывает на структуру SERVICE_STATUS аж с 7 полями, и всего 2 динамические (5 остальных не меняются). В общем вот пример, как продолжение кода выше:

C-подобный:
;// Заполняем структуру SERVICE_STATUS основными значениями
;//---------------
         mov     [srvStatus.dwServiceType],     SERVICE_WIN32_OWN_PROCESS  ;// Частная служба нашего юзер-процесса
         mov     [srvStatus.dwControlsAccepted],SERVICE_ACCEPT_STOP        ;// Какие команды будем принимать (см.ниже)
         mov     [srvStatus.dwWin32ExitCode],   ERROR_SERVICE_SPECIFIC_ERROR
         mov     [srvStatus.dwServiceSpecificExitCode],0
         mov     [srvStatus.dwCheckPoint],0              ;//<--- Если не нуль, значит служба чем-то занята
         mov     [srvStatus.dwWaitHint],3000             ;//<--- Важно! Период отправки оповещений SCM = макс 3 сек.

        stdcall  UpdateStatus, SERVICE_START_PENDING, 1  ;// Сообщаем SCM о начале запуска.. (1 = dwCheckPoint)

;// Создаём доп.события для юзер-команд (не нужно перечислять в поле dwControlsAccepted)
;//---------------
         invoke  CreateEvent, 0, 1, 0, 0    ;// стд.событие остановки службы
         mov     [hStopEvent], rax
         invoke  CreateEvent, 0, 1, 0, 0    ;// юзер-чтение из Pipe
         mov     [hReadEvent], rax
         invoke  CreateEvent, 0, 1, 0, 0    ;// юзер-запись в Pipe
         mov     [hWriteEvent],rax

;// Открываем канал Pipe для обмена данными с юзером (пайп создаёт сам софт управления службой)
;//---------------
         invoke  CreateFile,<'\\.\pipe\WiFiPipe',0>,\
                                GENERIC_READ + GENERIC_WRITE,\
                                0,0,OPEN_EXISTING,0,0
         mov     [hPipe],rax
      stdcall    UpdateStatus, SERVICE_RUNNING, 0   ;//<---- Служба запущена!

         call    @ServiceWorkedLoop
         ret
endp

Из значимых моментов, выше создаются три объекта синхронизации "Event", по сигналу которых будем реагировать на команды управления от юзер-софта. Контроль этих ивентов осуществляется в колбек-процедуре ServiceRoutineEx(), адрес которой мы передавали в RegisterServiceCtrlHandlerEx(). На время её работы накладываются жёсткие ограничения, т.к. служб в системе много, а диспетчер один. Поэтому мы просто проверяем код события, и по нему переводим соответствующий ивент в сигнальное состояние. А вот сама обработка события происходит уже в основном потоке службы, а не в потоке SCM, и её макс.длительность определяет значение поля dwWaitHint структуры SERVICE_STATUS.

Для отправки кодов-управления в службу используется ControlService(). Первые 127 кодов забрала себе система, а пользовательские должны начинаться с 128=80h. Здесь нужно отметить, что колбек обычной RegisterServiceCtrlHandler() в упор не видит юзер-коды, и нужно обязательно звать её расширенную версию с суфиксом Ex() в конце. Вот фрагмент:

C-подобный:
;// Обработчик команд SCM (запускается в отдельном потоке)
;//---------------
proc  ServiceRoutineEx opCode, dwEventType, lpEventData, lpContext
         mov     [opCode],rcx

         cmp     [opCode],SERVICE_CONTROL_STOP
         jne     @f
         invoke  SetEvent,[hStopEvent]    ;// Сигнал, если команда "стоп"
         jmp     @endRoutine

@@:      cmp     [opCode],0x80            ;// Если юзер-код "ReadPipe"
         jne     @f
         invoke  SetEvent,[hReadEvent]
         jmp     @endRoutine

@@:      cmp     [opCode],0x81            ;// Если это "WritePipe"
         jne     @endRoutine
         invoke  SetEvent,[hWriteEvent]

@endRoutine:
        stdcall  UpdateStatus,0,0         ;// Просто подтверждаем текущий статус
         xor     eax,eax
         ret
endp

Ну и под занавес службе требуется бесконечный цикл, который мы вызывали в хвосте предыдущего фрагмента call @ServiceWorkedLoop. На входе сразу ждём сигналы от трёх своих ивентов через WaitForMultipleObjects() и если да, то прыжок на метку. Обратите внимание на аргумент(3) функции Wait() - он задаёт число дескрипторов в массиве EventArray. Поскольку проверки следуют от 0 до 2, то важен порядок следования дескрипторов в массиве, который лежит в секции-данных:

C-подобный:
section 'data' data readable writeable
EventArray:
  hStopEvent   dq  ?   ;// WAIT_OBJECT_0
  hReadEvent   dq  ?
  hWriteEvent  dq  ?
......

section 'code' code readable executable
......
@ServiceWorkedLoop:
         invoke  WaitForMultipleObjects,3,EventArray,0,5000
  
         cmp     rax, 0         ;// WAIT_OBJECT_0
         je      @stopService
         cmp     rax, 1         ;// WAIT_OBJECT_0 + 1
         je      @readPipe
         cmp     rax, 2         ;// WAIT_OBJECT_0 + 2
         je      @writePipe
         cmp     eax,WAIT_TIMEOUT     ;// истёк таймаут 5000 м/сек
         jne     @ServiceWorkedLoop

         invoke  Sleep,500            ;// немного разгрузим CPU
         jmp     @ServiceWorkedLoop

@stopService:
        stdcall  UpdateStatus,SERVICE_STOP_PENDING,1
         invoke  ResetEvent, [hStopEvent]
         invoke  CloseHandle,[hPipe]
        stdcall  UpdateStatus,SERVICE_STOPPED,0
         invoke  ExitProcess,0

@readPipe:
         invoke  ResetEvent,[hReadEvent]
         invoke  ReadFile,[hPipe],rdBuff,strLen,byteRetn,0
         jmp     @ServiceWorkedLoop

@writePipe:
         invoke  ResetEvent,[hWriteEvent]
         invoke  WriteFile,[hPipe],wrBuff,strLen,byteRetn,0
         jmp     @ServiceWorkedLoop

4. Программа управления службой в сессии(1)

sControlFuncList.webp

Как видно из таблицы выше, работу на стороне софта поддерживает довольно внушительная агитбригада WinAPI. На что здесь нужно обратить внимание, это запуск службы функцией StartService(). Дело в том, что после отправки запроса в SCM, необходимо сделать небольшую паузу типа Sleep(1000), т.к. в отличии от прикладного софта службы не могут стартовать мгновенно и им нужна "раскачка". А в остальном всё по классической схеме (обработка ошибок опущена):

C-подобный:
;// Подготовка службы к запуску..
;//---------------
          invoke  OpenSCManager,0,0,SC_MANAGER_ALL_ACCESS
          mov     [hScm],rax

;// Получаем полный путь до бинаря своей службы (если лежит рядом с прожкой)
;//---------------
          invoke  GetFullPathName,<'WiFiService64.exe',0>,256,buff,rsp
          invoke  CreateService,[hScm],\
                                <'WiFiService64',0>,\
                                <'Частная служба процесса Codeby',0>,\
                                SERVICE_ALL_ACCESS,\
                                SERVICE_WIN32_OWN_PROCESS,\
                                SERVICE_DEMAND_START,\
                                SERVICE_ERROR_NORMAL,\
                                buff,0,0,0,0,0
          mov     [hServ],rax

;// Создаём пайп для обмена данными со службой
;//---------------
          invoke  CreateNamedPipe,<'\\.\pipe\WiFiPipe',0>,\
                                  PIPE_ACCESS_DUPLEX,\
                                  PIPE_TYPE_MESSAGE,\
                                  1,1024,1024,0,0
          mov     [hPipe],rax

;// Запуск службы с обязательной паузой после
;//---------------
          invoke  StartService,[hServ],0,0
          invoke  Sleep,1000

;// Теперь можно отправить любую команду
;//---------------
          invoke  ControlService,[hServ],\
                                    0x81,\   ;// юзер-код "WritePipe"
                                 sStatus
......

;// Обработчик "WM_CLOSE/COMMAND" в диалоговой процедуре окна
;//---------------
@command:
     .if  [wparam] = BN_CLICKED shl 16 + IDCANCEL

          invoke  ControlService,[hServ],SERVICE_CONTROL_STOP,sStatus
          invoke  DeleteService,[hServ]
          invoke  CloseServiceHandle,[hServ]
          invoke  CloseServiceHandle,[hScm]
          invoke  CloseHandle,[hPipe]
          invoke  Sleep,500
          jmp     @close
  .endif
          jmp     @next

@close:   invoke  EndDialog,[hwnddlg],0
@next:    xor     eax,eax
          ret
endp

Основная проблема при написании служб - это их отладка. В сети предлагают различные методы типа "юзай WinDbg" и прочие, но все они мутные и лично у меня не работают. Поэтому приходится отлавливать блох в слепую, а результат проверять например в программе "ProcessHacker". Эта крутая тулза выдаст всю необходимую инфу о службе, включая просмотр свойств по Enter. Как видим наша служба исправно запустилась и находится с состоянии Running, ожидая от нас команд.

pHacker.webp

Обратите внимание с какими правами она запустилась - это LocalSystem, что является псевдонимом SYSTEM, т.е. наивысшие права, которых нет даже у админа системы. Как уже упоминалось ранее, это может представлять уязвимость, однако в частных случаях требуется именно System, например для декрипта паролей функцией CryptUnprotectData(), о чём пойдёт речь далее.

А вообще права задаются в аргументе lpServiceStartName функции CreateService() - это указатель на строку вида "NT AUTHORITY\LocalService". Если в этом аргументе нуль, то в дефолте назначается именно System.

SrvAccount.webp


4.1. Поиск и чтение паролей Wi-Fi

Если кто забыл, вся эта заваруха со службами нужна была для декрипта паролей Wi-Fi на локальной машине. Забегая вперёд скажу, что с основным функционалом придётся повозиться - это работа с файлами, и нудный поиск в них текстовых строк (без этого никак). В общем зайдём из далека..

В Win-XP настройки сети Wi-Fi прописывались в следующей ветке реестра.
Здесь каждый интерфейс представлен уникальным GUID, и все его настройки хранятся в значении "ActiveSettings". HKLM\Software\Microsoft\WZCSVC\Parameters\Interfaces\{GUID}

Но начиная с Vista майки отказались от реестра - теперь все параметры хранятся в файле конфигов XML по пути:
C:\ProgramData\Microsoft\Wlansvc\Profiles\Interfaces\{GUID}\Random-GUID.xml

В каждом профиле беспроводной сети хранится инфа о названии сети Wi-Fi, параметрах безопасности (аутентификация, шифрование), и собственно сам зашифрованный пароль. Вот типичное содержимое одного из таких файлов:

XML:
<?xml version="1.0"?>
<WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1">
    <name>COMNET_16</name>
    <SSIDConfig>
        <SSID>
            <hex>434F4D4E45545F3136</hex>
            <name>COMNET_16</name>
        </SSID>
    </SSIDConfig>
    <connectionType>ESS</connectionType>
    <connectionMode>manual</connectionMode>
    <MSM>
        <security>
            <authEncryption>
                <authentication>WPA2PSK</authentication>
                <encryption>AES</encryption>
                <useOneX>false</useOneX>
            </authEncryption>
            <sharedKey>
                <keyType>passPhrase</keyType>
                <protected>true</protected>
                <keyMaterial>01000000D08C9DDF0115D1118C7A00C04FC297EB01000000</keyMaterial>
            </sharedKey>
        </security>
    </MSM>
    <MacRandomization xmlns="http://www.microsoft.com/networking/WLAN/profile/v3">
        <enableRandomization>false</enableRandomization>
        <randomizationSeed>1038085264</randomizationSeed>
    </MacRandomization>
</WLANProfile>

Дадим определение основным узлам этого xml-файла:

1. Имя сети Wi-Fi прописывается в разделе <SSID>, которое хранится как в ASCII (здесь COMNET_16), так и в HEX-формате.​
2. В узле <authEncryption> найдём методы аутентификации (здесь WPA2PSK) и шифрования (почти всегда AES).​
3. Инфа о пароле лежит в узле <sharedKey> - значение <protected> указывает, зашифрован пароль(true), или хранится в открытом виде(false).​
4. Сам пасс зарыт в узле <keyMaterial> и это то, что нам предстоит вытащить из xml, и передать его в нашу службу.​
5. В некоторых роутерах есть опция рандома МАС-адреса, тогда можно заглянуть в узел <MacRandomization> (здесь enable=false, и других я не встречал).​

На резонный вопрос "зачем передавать готовый HEX-пасс службе" можно ответить, что он шифруется функцией CryptProtectData() из либы Crypt32.dll, при этом ни соль, ни какой-либо доп.ключ не используются, что на первый взгяд делает декрипт простым. Однако защита в том, что само шифрование производится одной из системных служб под учётной записью именно System, так-что даже под админом мы не сможем его расшифровать. Тут у нас всего 2 варианта - или внедрять шелл через удалённый поток в системный процесс Lsass.exe (метод довольно рискованный, т.к. любая ошибка может привести к краху всей системы), или-же просто создать свою службу в контексте System, что собственно мы и выбрали.

Имейте в виду, что крипт/декрипт должен осуществляться на одной машине, т.е. нельзя утащить чужой пароль, и пытаться расшифровать его на своём компьютере. Это механизм защиты DPAPI, который при шифровании под капотом формирует сессионный ключ текущей машины (имя юзера, GUID сеанса, и прочее), после чего сохраняет эту инфу прямо внутри зашифрованных данных. Если в момент расшифровки эти сведения не совпадут, то получим прокол.

Проблемы при работе с xml-файлом:

1. Поиск самого файла по адресу: C:\ProgramData\Microsoft\Wlansvc\Profiles\Interfaces\{GUID}\Random-GUID.xml
Ладно до папки ..\Interface у нас есть постоянный путь, а вложенную нужно вычислять, т.к. ей назначается произвольное имя {GUID}, впрочем как и самому файлу. Значит придётся через FindFirstFile() найти имя папки, и вручную добавить его к известной строке ..\Interface (конкатенация). После этого повторить запрос FindFirstFile() уже для xml-файла. Только теперь его можно будет прочитать ReadFile() предварительно выделив память HeapAlloc().

2. Если всё ок, приступаем к поиску нужных узлов внутри xml.
Здесь вычислив общий размер файла GetFileSize(), от начала и до конца ищем символ(<) и через repe cmpsb сравниваем строки, заранее определив её длину. Готовые API использовать нельзя, т.к. в xml-строках нет терминальных нулей. Поскольку придётся искать разные строки для вывода их в окно, я написал универсальную процедуру. На входе она ожидает имя и длину строки для поиска:

C-подобный:
.data
strSsid    db  04,'name'          ;// первый байт длина
strPass    db  11,'keyMaterial'
strAuth    db  14,'authentication'
strCrypt   db  10,'encryption'
strRnd     db  19,'enableRandomization'
strSeed    db  17,'randomizationSeed'

.code
;//----------------------------------------
;// Процедура поиска заданной строки в XML
;//----------------------------------------
align 16
proc  ParseWiFiXml lpName,strLen
          mov     [lpName],rcx
          mov     [strLen],rdx
;// Очистить приёмный буфер
          xor     eax,eax
          mov     ecx,256/8
          mov     edi,buff
          rep     stosq
;// Парсим файл датабазы *.XML
          mov     rsi,[fBuff]    ;// адрес файла от HeapAlloc()
          mov     rdx,[fSize]    ;// его длина GetFileSize()
@@:       cmp     byte[rsi],'<'
          je      @testStr
          inc     rsi
          dec     rdx
          jnz     @b
          jmp     @endParse
;// Сравнить строки !
@testStr: mov     rcx,[strLen]
          mov     rdi,[lpName]
          inc     rsi
          repe    cmpsb
          or      ecx,ecx
          jnz     @b
;// Если нашли, скопировать в буфер для вывода
          inc     rsi
          mov     rdi,buff
@@:       lodsb
          cmp     al,'<'         ;// конец строки?
          je      @endParse
          stosb
          jmp     @b
@endParse:
          ret
endp
;//----------- Вызов процедуры -------------------
.....
          movzx   eax,byte[strSsid]     ;// первый байт в строке = размер
          mov     ebx,strSsid+1         ;// ..и далее линк на саму строку
         stdcall  ParseWiFiXml,ebx,eax
          invoke  SetDlgItemText,[hwnddlg],ID_Ssid,buff

          movzx   eax,byte[strAuth]
          mov     ebx,strAuth+1
         stdcall  ParseWiFiXml,ebx,eax
          invoke  SetDlgItemText,[hwnddlg],ID_Auth,buff
... etc ...

3. Пароль в xml-файле хранится в виде HEX-строки, в то время как функция расшифровки CryptUnprotectData() ожидает бинарные данные в структуре DATA_BLOB. Значит получаем длину HEX-строки lstrlen(), и переводим строку произвольной длины в число по такой схеме:

C-подобный:
;//-----------------------------------------
;// Процедура преобразования строки в число
;//-----------------------------------------
align 16
proc StringToHex
           invoke  lstrlen,buff
           shr     eax,1
           mov     word[writeBuff],ax  ;// длина

           mov     esi,buff
           mov     edi,buff
           xor     eax,eax
@@:        lodsb
           or      al,al
           jz      @stop
           cmp     al,'9'
           jbe     @01
           or      al,0x20     ;// A-F в нижний регистр
           sub     al,'a'
           add     al,10
           jmp     @02
@01:       sub     al,'0'
@02:       stosb
           jmp     @b

@stop:     mov     esi,buff            ;// источник
           mov     edi,writeBuff+2     ;// приёмник
           movzx   ecx,word[writeBuff]
@@:        lodsw
           xchg    ah,al
           shl     al,4
           shr     ax,4
           stosb
           loop    @b
           ret
endp

Полный исходник положу в скрепку, а результат его работы выглядит так. Поскольку у меня нет бука под рукой (а только стационар с инетом по кабелю), для тестов я нашёл несколько xml с чужих машин, поэтому в окне "Pass" служба сообщила об ошибке декрипта пароля (причина обсуждалась выше). Но главное вся эта конструкция исправно воркает, и я вытащил из файла наиболее информативные строки.

Result.webp

5. Заключение

В скрепку положил исходник + готовый файл для тестов (запускать правой клавишей от админа). Семпл сырой и толком не протестированный. Поэтому при желании можете допилить его сами. В принципе подправить нужно будет только один аргумент в функции декрипта службы. Если коротко, то CryptUnprotectData() использует мастер-ключи, которые могут быть привязаны или к конкретной учётной записи (флаг 0), или к конкретному компьютеру (флаг 4). Этот флаг указывается в предпоследнем аргументе функции, поэтому если код вернёт ошибку, нужно попробовать оба варианта в службе:

C-подобный:
;// Вариант 1: для текущего пользователя
invoke CryptUnprotectData, DataIn, 0, 0, 0, 0, 0, DataOut

;// Вариант 2: для локальной машины (скорее всего нужен)
invoke CryptUnprotectData, DataIn, 0, 0, 0, 0, 4, DataOut

Ссылки по теме:

• Описание сервисных функций от майков


• Про учётную запись System


• Детали DPAPI


• Софт для декрипта паролей (внедрение в lsass.exe)
 

Вложения

  • Нравится
Реакции: Luxkerr и Edmon Dantes
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab