В предыдущей главе мы славно потрудились и получили VId нашей жертвы. Наша следующая цель -- подсунуть этот VId инсталлеру Sentinel SDK (вы уже скачали 80-тонный архивчик?) и получить генератор лицензионных цепочек ИМЕННО для нашей жертвы. Правда, тут возникает одно НО, которое и служит темой для данной главы. Дело в том, что установщик не запрашивает VId напрямую, а вместо него просит ввести некий серийник (Serial Number)...
Отладчик сразу проясняет ситуацию:
Мммда... таким образом VId косвенно передаётся инсталлеру. Значит, если хотите играть по правилам, тогда соизвольте подобрать серийник для нашего VId. В противном случае можете попробовать обойти стадию с MODBIN и насильно запихнуть VId прямо в SDK. Я, честно говоря, такого делать не пробовал и вам не советую! Итак, приступим к написанию ключегенератора для SentinelLM SDK 7.1.
Внимание! Не спешите компилировать прилагаемые исходники, сначала дочитайте главу!
Во-первых, покопайтесь над установщиком и вытяните из него MODBIN.DLL. При желании можете дизассемблировать библиотеку и постараться понять принцип хеширования (серийник -> VId). Таким образом вы убедитесь в сложности построения обратной функции (VId -> серийник). Гораздо проще обойтись прямым перебором всех возможных комбинаций до получения искомого VId. На самом деле время перебора не так уж и велико (1,5 - 2 часа на среднестатистическом компе), но я предлагаю несколько иной подход, а именно: сгенерировать файл со всеми ВАЛИДНЫМИ парами (VId, серийник) для использования в ключегенераторе в качестве словаря. Размер такого файла, в зависимости от формата, может составить около 382Кб. Далее приводится исходник первой стадии генератора:
.386
.model flat, stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib
.DATA
modbin db "modbin",0 ; Имя библиотеки
GetVendorId db "GetVendorId",0 ; Импортируемая ф-ция
Err1 db "MODBIN.DLL не найден",0
Err2 db "GetVendorId не найден",0
Err3 db "Ошибка при создании файла",0
sLog db "slm_gen",0 ; Имя файла-словаря
sOK db "Программа готова к старту!",0
sDone db "Программа завершилась успешно!",0
sCaption db "MODBIN кряк",0
seed dd 0FFFFFFFFh ; текущий серийник
.DATA?
hLib dd ? ; Хендл 'MODBIN.DLL'
procAddr dd ? ; Адрес ф-ции 'MODBIN!GetVendorId'
hFile dd ? ; Хендл файла-словаря
VId dw ? ; Текущий VId
strBuf db 128 dup (?)
.CODE
start:
; 1. Задать текущий каталог
; -------------------------
invoke GetModuleFileName,0,OFFSET strBuf,128
test eax,eax
jz @F
mov edi,OFFSET strBuf
add edi,eax
xchg eax,ecx
mov al,'\'
std
repnz scasb
jnz @F
mov BYTE PTR [edi + 1],0
cld ; <- это важно в NT
invoke SetCurrentDirectory,OFFSET strBuf
@@:
; 2. Загрузить MODBIN.DLL
; -----------------------
invoke LoadLibraryEx,OFFSET modbin,0,0
test eax,eax
jnz @F
invoke MessageBox,eax,OFFSET Err1,eax,MB_ICONSTOP
jmp _end_1
@@:
mov hLib,eax
invoke GetProcAddress,eax,OFFSET GetVendorId
test eax,eax
jnz @F
invoke MessageBox,eax,OFFSET Err2,eax,MB_ICONSTOP
jmp _end_2
@@:
mov procAddr,eax
; 3. Создать файл-словарь
; -----------------------
invoke CreateFile,OFFSET sLog,GENERIC_WRITE,\
FILE_SHARE_READ,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0
cmp eax,INVALID_HANDLE_VALUE
jne @F
invoke MessageBox,0,OFFSET Err3,0,MB_ICONSTOP
jmp _end_2
@@:
mov hFile,eax
invoke MessageBox,0,OFFSET sOK,OFFSET sCaption,MB_ICONINFORMATION
; 4. Приступим!
; -------------
_mainloop:
; Переводим текущий серийник в строку
; Содрано из DWTOA + чуть-чуть подоптимизировано
dec seed
jz _end_3
mov eax,seed
mov edi,OFFSET strBuf
mov ecx,429496730
mov esi,edi
_wloop:
mov ebx,eax
mul ecx
mov eax,edx
lea edx,[edx*4+edx]
add edx,edx
sub ebx,edx
add bl,'0'
mov [edi],bl
inc edi
test eax,eax
jnz _wloop
mov BYTE PTR [edi],0
.WHILE (esi < edi)
dec edi
mov al,[esi]
mov ah,[edi]
mov [edi],al
mov [esi],ah
inc esi
.ENDW
push OFFSET strBuf
call procAddr ; Вызываем MODBIN!GetVendorId
pop ecx ; Выравниваем стек
test eax,eax
js _mainloop ; -1 значит левый серийник
mov VId,ax
push seed
pop DWORD PTR strBuf
; Добавляем в файл пару (VId, серийник)
invoke WriteFile,hFile,OFFSET VId,6,OFFSET modbin,0
jmp _mainloop
_end_3:
invoke CloseHandle,hFile
; Усё... справились!
invoke MessageBox,0,OFFSET sDone,OFFSET sCaption,MB_ICONINFORMATION
_end_2:
invoke FreeLibrary,hLib
_end_1:
invoke ExitProcess,eax
END start
По-моему, разбирать предыдущий исходник не стоит во избежание личных обид :-) В общем, запускайте его и ожидайте... ожидайте... ожидайте сообщения об успешном завершении программы. Полученный 382-килобайтный словарь (slm_gen) послужит нам многие годы и внесённый в него труд даром не пропадёт (надеюсь). Следующий шаг -- написать парсер для нашего словаря:
.386
.model flat, stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib
DlgProc PROTO :DWORD,:DWORD,:DWORD,:DWORD
.DATA
file_not_found db "Словарь не найден!",0
dwFormat db "%u",13,10,0
.DATA?
hInst dd ?
hSN dd ? ; Хендл edit'а с серийником
hFile dd ? ; Хендл словаря
hMap dd ? ; Хендл словаря в памяти
hView dd ? ; Указатель на маппированные данные
fSize dd ? ; Размер словаря
szBuf db 128 dup (?)
.CODE
start:
invoke GetModuleHandle,0
mov hInst,eax
invoke DialogBoxParam,eax,200,0,OFFSET DlgProc,0
invoke ExitProcess,eax
DlgProc PROC hDlg:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
mov eax,uMsg
cmp eax,WM_COMMAND
jne no_wm_comm
mov eax,wParam
cmp ax,102
je @F
xor eax,eax
ret
@@:
; 1. Получаем введённый VId
; -------------------------
mov DWORD PTR szBuf,0
invoke SetDlgItemText,hDlg,101,OFFSET szBuf
invoke GetDlgItemText,hDlg,100,OFFSET szBuf,5
test eax,eax
jnz @F
ret
@@:
; 2. Переводим VId из шестнадцатиричной строки в 16-битное слово
; --------------------------------------------------------------
mov edx,DWORD PTR szBuf
mov ecx,4
xor eax,eax
hex2bin:
test dl,dl
jz endloop
shl eax,4
sub dl,48
cmp dl,9
jle @F
sub dl,7
cmp dl,0Fh
jle @F
sub dl,32
@@:
or al,dl
shr edx,8
loop hex2bin
endloop:
; 3. Ищем VId в словаре
; ---------------------
mov ecx,fSize
shr ecx,1
jecxz endsrch
push ebx ; Сохранить EBX (для NT)
push edi ; Сохранить EDI (для 9x)
mov edi,hView
cld
search:
scasw
jne @F
; 4. Добавляем очередной серийник и продолжаем поиск
; --------------------------------------------------
call appendSN
@@:
dec ecx
dec ecx
jle done ; наш словарь кто-то обрезал :-)
add edi,4
loop search
done:
pop edi
pop ebx
endsrch:
ret
no_wm_comm:
cmp eax,WM_INITDIALOG
jne no_wm_init
invoke GetModuleFileName,hInst,OFFSET szBuf,127
; Убираем расширение
add eax,OFFSET szBuf - 4
mov BYTE PTR [eax],0
invoke CreateFile,OFFSET szBuf,GENERIC_READ,\
FILE_SHARE_READ,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0
cmp eax,INVALID_HANDLE_VALUE
jne @F
invoke MessageBox,0,OFFSET file_not_found,0,MB_ICONSTOP
invoke EndDialog,hDlg,0
ret
@@:
mov hFile,eax
invoke GetFileSize,eax,0
mov fSize,eax
; Отображаем словарь в память
invoke CreateFileMapping,hFile,0,PAGE_READONLY,0,0,0
mov hMap,eax
invoke MapViewOfFile,eax,FILE_MAP_READ,0,0,0
mov hView,eax
invoke GetDlgItem,hDlg,101
mov hSN,eax
ret
no_wm_init:
cmp eax,WM_CLOSE
jne no_wm_close
invoke UnmapViewOfFile,hView
invoke CloseHandle,hMap
invoke CloseHandle,hFile
invoke EndDialog,hDlg,0
ret
no_wm_close:
xor eax,eax
ret
DlgProc ENDP
; Показать найденный серийник
appendSN PROC uses edi ecx eax
mov eax,[edi]
invoke wsprintf,OFFSET szBuf,OFFSET dwFormat,eax
invoke SendMessage,hSN,WM_GETTEXTLENGTH,0,0
invoke SendMessage,hSN,EM_SETSEL,eax,eax
invoke SendMessage,hSN,EM_REPLACESEL,0,OFFSET szBuf
ret
appendSN ENDP
END start
Ну всё, компилируем вместе с RC-скриптом и наш новый генератор серийных номеров для SentinelLM SDK готов к употреблению!

Попробуйте задать ему 2A0A (это последний VId в нашем словаре) и получите целых ДВА результата (8561314 и 8430250). Почему два? Видимо SDK разделяет все VId на две категории:
Первый серийник соответсвует обычному VId (2A0A) а второй наделяет SDK дополнительными фичами. Так как большинству клиентским программам (например, нашей жертве) совершенно безразличен MSB (старший бит) её VId, вам будет всё равно какой из этих серийников использовать. Кстати, иногда может быть вплоть до восьми серийных номеров на один VId (больше восьми я ещё не встречал).
Далее, просто ставим SDK и проверяем, что VId соответствует нами заданному. Только не спрашивайте: "А как это проверить?" Медитируйте над первой главой. Обратите внимание на то, что SDK отказывается на нас работать... Не переживайте -- в следующей главе мы его уломаем :-)
[C] Quantum