allasm.ru

    Меню

 

В предыдущей главе мы славно потрудились и получили VId нашей жертвы. Наша следующая цель -- подсунуть этот VId инсталлеру Sentinel SDK (вы уже скачали 80-тонный архивчик?) и получить генератор лицензионных цепочек ИМЕННО для нашей жертвы. Правда, тут возникает одно НО, которое и служит темой для данной главы. Дело в том, что установщик не запрашивает VId напрямую, а вместо него просит ввести некий серийник (Serial Number)...

Отладчик сразу проясняет ситуацию:

  1. Инсталлятор разжимает библиотеку MODBIN.DLL
  2. Введённый серийник передаётся функции MODBIN!GetVendorId (не смейтесь :-)
  3. Функция возвращает VId соответствующий серийнику или -1, если таковой не найден.

Мммда... таким образом 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 на две категории:

  1. [0000 - 7FFF] для обычных клиентов
  2. [8000 - FFFF] для крутых разработчиков (вроде нас с вами)

Первый серийник соответсвует обычному VId (2A0A) а второй наделяет SDK дополнительными фичами. Так как большинству клиентским программам (например, нашей жертве) совершенно безразличен MSB (старший бит) её VId, вам будет всё равно какой из этих серийников использовать. Кстати, иногда может быть вплоть до восьми серийных номеров на один VId (больше восьми я ещё не встречал).

Далее, просто ставим SDK и проверяем, что VId соответствует нами заданному. Только не спрашивайте: "А как это проверить?" Медитируйте над первой главой. Обратите внимание на то, что SDK отказывается на нас работать... Не переживайте -- в следующей главе мы его уломаем :-)

  [C] Quantum