Главная / Блог / Создание окон KeyGen: анализ упаковки, поиск OEP и интерфейс на GDI32 (демо-код)
Материал предназначен исключительно в образовательных целях. Автор не призывает к нарушению авторских прав и использованию нелегального ПО.
Окна генерации паролей могут иметь отличные от стандартных весьма причудливые формы, и здесь мы с нуля рассмотрим, как взломщики их создают.
Модификация чужого программного обеспечения – это статья с обвинениями типа «несанкционированный доступ к информации» и всё такое. Но можем‑ли мы сломать софт не прикасаясь к нему? Пусть под замком находится хак кода и данных приложения, но жонглировать регистрами ЦП нам никто не запрещает, ведь ЦП продается (а не лицензируется), а значит мы вольны делать с ним всё, что вздумается. То‑есть если модифицировать код защищённого приложения уже после того‑как оно загрузится в память (т.н. бит‑хак), то вроде всё должно пройти гладко. Однако на практике юристов интересует результат, а не способы его достижения, поэтому здесь не спасёт даже «адвокат дьявола», и от правосудия всё‑равно уйти будет сложно.
👉 Если интересно разобраться глубже в том, как процессор выполняет команды, посмотри наш материал про скрипты и функции в x64dbg.
Именно в таких случаях и спасают генераторы ключей активации, что на буржуйском звучит как «KeyGen». Когда жалко платить за софт честно‑заработанные, мы на свой страх и риск прибегаем к услугам непонятно кем написанных кейгенов, которые запросто могут подселить на нашу машину «непрошенных жильцов». В то же время нужно учитывать, что авторы кейгенов бесплатно предоставляют нам валидные ключи/пароли, и если‑уж на то пошло, мы должны быть благодарны им за это, ведь активация приложения при помощи ключа (пусть и левого) никак не противоречит с законом.
Но это всё лирика, и здесь хотелось‑бы поговорить не о проблемах с грозной Фемидой, а о внешней оболочке, т.е. об интерфейсе самого окна кейгена. Учитывая, что сам по себе софт данного класса достаточно не стандартен и его пишут опытные прогеры, то в игровой форме внешнему виду этих форточек они придают особое значение, и вы никогда не встретите в природе двух одинаковых окон. Так что‑же в них особенного?
Чтобы не тыкать пальцем в небо, возьмём любой кейген и положив его на операционный стол, проведём трепанацию во‑всевозможных инструментах анализа, типа редактор ресурсов (у меня «eXeScope»), отладчик и прочие. Если создать аналогичное окно в IDE стандартными средствами, оно будет отличаться и станет очевидно, что прогер не создавал, а рисовал его вручную, для чего используются функции из системной библиотеки GDI32.dll (Graphics Device Interface). Вернее диалоговое окно‑то создаётся в ресурсах как обычно (по другому никак), а вот все наложенные на него компоненты уже прорисовываются самостоятельно.
Ладно, отложим пока в ящик этот вопрос, и вскормив клиента детектору «DiE» посмотрим, что там внутри. Ого… достопочтенный Дельфи, причём сжат компрессором «PECompact2х». В общем всё по штатной схеме для софта подобного класса, однако некоторые вопросы всё‑же имеются.
Посмотрим на предпоследнюю строку отчёта «DiE» где сказано, что пожата OEP + секция импорта, хотя вьювер «PEAnatomist» имеет другое мнение на этот счёт. Судя по его логам, весь импорт открыт, только лежит в секции ресурсов, причём там‑же находится весьма подозрительная и требующая к себе особого внимания секция TLS (Thread Local Storage), которая при любых обстоятельствах получает управление первой задолго до точки‑входа OEP. Так‑же видим бешенную энтропию под 8 балов у обоих секций, и что на старте из Kernel32.dll статически прилинкованы лишь функции LoadLibrary() + GetProcAddress(), для динамической подгрузки остальных API. Поскольку ресурсы тоже сжаты (см. энтропию ниже), то редактор «eXeScope» кроме иконки ничего там не распознаёт, хотя весит секция порядка 8 КБ. В такой двоякой ситуации спасает только отладчик, на которым мы и натравим нашего пациента Keygen.exe.
👉 Про похожие трюки с упаковкой и обходом защиты мы писали в гайде по CTF‑заданиям 2025, где эти знания можно применить на практике.
Значит запускаем отладчик x32dbg, и т.к. у кейгена имеется секция TLS, сразу в параметрах выставляем ему галку «Прерываться на вызове TLS». Теперь загружаем в отладчик клиента и видим, что TLS‑колбек не сработал и мы прямиком попали на фиктивную точку‑входа пакера по адресу 0x00401000. По сути этого и стоило ожидать, т.к. колбек находится в ресурсах, а они пока ещё сжаты.
На входе‑же видим, что через регистр FS и ESP пакер «PECompact» сразу устанавливает пользовательский обработчик исключений SEH (Structured Exception Handler), после чего записью по адресу нуль тут‑же сам генерит исключение. Как результат, управление получает только‑что установленный обработчик, который отфутболивает нас прямиком в ворота распаковщика по адресу 0x00446CA7. Это древний, неинтересный, да к тому‑же легко обнаруживаемый трюк. «Классика – это не только Пушкин с Лермонтовым, но и прыжки через SEH».
Получив управление, конструкцией POP FS:[0] --> ADD ESP,4 анпакер теперь снимает установленный только‑что SEH, иначе если позже возникнет исключение (от этого никто не застрахован), то получим «мёртвый цикл». Далее подготавливаются регистры и через VirtualAlloc() выделяется память для распаковки данных. Если по F8 шагать в отладчике дальше, то можно обнаружить много интересного: алгоритм распаковки (в это время появляются читабельные строки), динамическая загрузка прочих DLL и функций из них, и в какой‑то момент наконец создаётся само окно кейгена, о чём собственно здесь и разговор.
В Windows имеется всего пара функций для создания форточек — это массивная CreateWindow() для основных окон, а так‑же компактная DialogBoxParam/Indirect() для диалоговых окон. Отличие их в том, что первая может на поверхности основного окна создавать и дочерние окна (типа кнопки, поля ввода), а вторая для этого использует описание элементов управления из ресурсов. Однако второй не нужен цикл обработки ввода с клавиатуры из трех функций GetMessage/TranslateMessage/DispatchMessage, что выгодно отличает её от первой. В общем если мы хотим поймать в отладчике момент создания окна, то достаточно поставить брейкпоинт на первую, и если не сработает — на вторую. В данном случае наш подопытный кейген использует именно DialogBoxParam().
Как видим, прогер этого кейгена использовал не прямой вызов API-функций, а таблицу переходов из 34 указателей (остальные не вместились в окно моего отладчика), что является довольно умным и распространённым приёмом. Если посмотреть на список, то большинство функций именно из бибилотеки графики Gdi32.dll.
Как уже упоминалось выше, для рисования в окне используются функции из либы Gdi32.dll. Их огромное количество, но в основном достаточно CreatePen() для линий, CreateBrush() для кисти и заливки выделенной области, и многое другое. Таким образом, чтобы создать окно кейген, можно или просто залить его одним или градиентным цветом (как в примере выше), или же наложить на окно фоновую картинку, что будет смотреться эффектней.
Далее располагаем в окне нужные элементы управления: текст, поля ввода/вывода и кнопки, приправив всё это дело полезной нагрузкой кода. У себя в примере, картинку фона я просто указываю как статический элемент в ресурсах, при этом формат её должен быть строго *.bmp, что увеличивает размер всего кейгена на выходе. Однако это не единственно возможный вариант, и если задействовать функции GDI+, то можно отображать любые форматы, типа PNG или JPG. Вот парочка ссылок с примерами от «Manhuntera»:
Ниже код демки, где пароль просто взят «от фонаря» и читается из секции данных. Код приведён для иллюстрации интерфейсной части окна:
; format pe gui
entry start
include 'win32ax.inc'
;--------------
.data
ID_Name = 1001
ID_Key = 1002
ID_Keygen = 1003
ID_Cancel = 1004
strKey db 'Y13H-88F2-KQ37-158X-W4S2',0
hModule dd 0
buff db 0
;--------------
.code
start: invoke InitCommonControls
invoke GetModuleHandle,0
mov [hModule],eax
invoke DialogBoxParam, 0, 100, 0, DialogProc, 0
invoke ExitProcess, 0
proc DialogProc hdlg, msg, wparam, lparam
cmp [msg],WM_COMMAND
je @cmd
cmp [msg],WM_INITDIALOG
jne @next
;// WM_INITDIALOG
@init: invoke LoadImage,[hModule],101,IMAGE_BITMAP,0,0,LR_DEFAULTCOLOR+LR_DEFAULTSIZE
mov [hBitmap],eax
jmp @next
;// WM_CLOSE/COMMAND"
@cmd: cmp [wparam],BN_CLICKED shl 16 + ID_Keygen
jne @f
invoke SetDlgItemText,[hdlg],ID_Key,strKey
@@: cmp [wparam],BN_CLICKED shl 16 + ID_Cancel
jne @next
@close: invoke EndDialog,[hdlg], 0
@next: xor eax,eax
ret
endp
;-------------
section '.idata' import data readable writeable
library kernel32,'kernel32.DLL',user32,'user32.DLL',comctl32,'comctl32.dll'
include 'api\kernel32.inc'
include 'api\user32.inc'
include 'api\comctl32.inc'
;-------------
section '.rsrc' data resource readable
directory RT_DIALOG, dialogs,\
RT_BITMAP, bitmaps
resource dialogs, 100, LANG_ENGLISH + SUBLANG_DEFAULT, mainform
resource bitmaps, 101, LANG_NEUTRAL, logo
dialog mainform,'',150,100,220,300, DS_3DLOOK + DS_CENTER + DS_MODALFRAME + WS_POPUP,,,'Verdana',8
dialogitem 'Static','logo',-1,005,005,200,200,WS_VISIBLE + SS_BITMAP + SS_CENTERIMAGE + SS_REALSIZEIMAGE
dialogitem 'BUTTON',' CODEBY-SOFT ',-1,5,165,210,130,WS_VISIBLE + BS_GROUPBOX + BS_FLAT + WS_GROUP
dialogitem 'Static','Name', -1,015,190,020,013,WS_VISIBLE + SS_LEFT
dialogitem 'Static','HackerLab',ID_Name, 015,205,190,010,WS_VISIBLE + SS_CENTER + SS_SUNKEN
dialogitem 'Static','Serial',-1,015,230,035,013,WS_VISIBLE + SS_LEFT
dialogitem 'Static','',ID_Key, 015,245,190,010,WS_VISIBLE + SS_CENTER + SS_SUNKEN
dialogitem 'BUTTON','Keygen',ID_Keygen,050,275,050,013,WS_VISIBLE + WS_TABSTOP + BS_PUSHBUTTON
dialogitem 'BUTTON','Exit', ID_Cancel,125,275,050,013,WS_VISIBLE + WS_TABSTOP + BS_PUSHBUTTON
enddialog
bitmap logo,'HlLogo.png'
Конечно, это «топорная» демонстрация интерфейса. В реальной практике основное внимание уделяется анализу и алгоритмам генерации серийников, а работа над окном — приятный бонус. При желании можно добавить фоновые эффекты, музыку через WaveOutWrite() из Winmm.dll, анимации и т.д. Главное — помнить о правовых ограничениях и использовать материал исключительно в образовательных целях.
Все права защищены. © 2016 - 2025