В прошлом номере мы начали знакомиться с языком программирования низкого уровня — Ассемблером, и создали собственную DLL. Теперь мы продолжим знакомство — используем созданную DLL`ку в программе аутентификации пользователя.

Для начала давайте поподробнее разберемся с исходным текстом нашей DLL’ки. Обратим особое внимание на секцию данных. Переменная hash содержит зашифрованный пароль — mycomp. Шифрование производилось операцией xor с буквой a, то есть:

xor ‘m’,‘a’ = 0Ch
xor ‘y’,‘a’ = 18h
xorc,‘a’ = 02h
xor ‘o’,‘a’ = 0Eh
xor ‘m’,‘a’ = 0Ch
xor ‘p’,‘a’ = 11h

В итоге переменная hash имеет следующее содержимое:

hash db 0Ch,18h,02h,0Eh,0Ch,11h,0

Примечание: если вы хотите использовать в своих программах другие пароли :-), то вы можете написать программу, создающую хеш пароля, который можно посмотреть в отладчике (Turbo Debugger, SoftICE).

Смотрим далее. Переменная site содержит адрес сайта, куда мы перейдем в случае успешного введения пароля, а в случае неправильного пароля — выдаем MessageBox со строкой bad:

site db "http://www.mycomputer.ua",0
bad db "Введен неправильный пароль!",0

С секцией данных разобрались, теперь приступаем к секции кода. На процедуру dll_proc внимание не обращаем, сконцентрируемся на процедуре dll_out.

Это, можно сказать, набор процедур, которые могут использовать программы. В этом участке кода производится сравнение введенного пароля. Вернее, не пароля, а хешей пароля. Далее объясню, почему мы поступаем именно так. В esi мы заносим адрес введенного пароля, а в edi — адрес нашего хеша. В ecx записывается количество байт, необходимых для сравнения, в нашем случае пароль имеет длину 6 байт (mycomp). Команда cmpsb выполняет побайтное сравнение.

cld
mov esi,mess
lea edi,hash
mov ecx,6
cmpsb

Если введен правильный пароль, флаг ZF будет установлен в 1, а если ZF сброшен в 0, тогда пароль неверен, чем мы пользуемся. В случае ошибки прыгаем на метку и выводим сообщение об ошибке, а иначе вызываем функцию WinAPI ShellExecute:

jnz not_right
invoke ShellExecute,NULL,NULL,addr site,NULL,NULL,NULL
jmp exit
not_right:
invoke MessageBox,NULL,addr bad,addr dll_name, MB_OK
exit:
ret

С функцией MessageBox мы уже разбирались, а вот ShellExecute видим в первый раз. Расписывать ее параметры нет смысла — почитайте в MSDN. Оговорю лишь, что третий параметр — адрес строки, которую необходимо выполнить операционной системе. Мы передаем в эту функцию адрес строки с названием сайта «Моего Компьютера». Результатом работы этой функции будет открытый браузер с назначенным сайтом.

Теперь, когда создана DLL, неплохо было бы научиться использовать ее функции из своих программ. Создаем файл window.asm в папке C:\masm32\bin с таким вот содержанием:

.386
.model flat,stdcall
; подключаем прототипы функций
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc

includelib auth.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

; прототип функции
window_proc proto :DWORD,:DWORD,:DWORD,:DWORD
dll_out proto :DWORD
.data
window_name db "auth",0
app db "Аутентификация пользователя",0

.data?
hInstance HINSTANCE ?
buffer db 6 dup(?)

.const
IDC_EDIT equ 3000
IDC_BUTTON equ 3001
IDC_EXIT equ 3002
IDM_EXIT equ 32002

.code
start:
invoke GetModuleHandle, NULL ; получаем handle приложения
mov hInstance,eax ; сохраняем его в переменной
invoke DialogBoxParam, hInstance, addr window_name,NULL,addr window_proc, NULL
invoke ExitProcess,eax ; выходим из программы

window_proc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.if uMsg==WM_INITDIALOG
invoke GetDlgItem, hWnd,IDC_EDIT ; устанавливаем поле ввода
invoke SetFocus,eax ; активным
.elseif uMsg==WM_CLOSE
invoke EndDialog,hWnd,NULL
.elseif uMsg==WM_COMMAND
mov eax,wParam
mov edx,wParam
shr edx,16
.if dx==BN_CLICKED ; если нажали кнопку "Ввести пароль"
.if ax==IDC_BUTTON
; тогда читаем в буфер введенный текст
invoke GetDlgItemText,hWnd,IDC_EDIT,ADDR buffer,512
mov ecx,6 ; шифруем пароль
xor esi,esi
next:
xor buffer[si],61h
inc si
loop next
invoke dll_out,addr buffer ; вызываем процедуру проверки из dll
.elseif ax==IDC_EXIT
invoke EndDialog, hWnd, NULL ; если нажали кнопку "Выход", выходим
.endif
.endif
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
window_proc endp
end start

Еще мы создадим файл window.rc в папке C:\masm32\bin с таким содержанием:

#define DS_CENTER 0x0800L
#define DS_MODALFRAME 0x80L
#define DS_3DLOOK 0x0004L

#define ES_AUTOHSCROLL 0x0080L
#define ES_LEFT 0x0000L
#define WS_OVERLAPPED 0x00000000L
#define WS_CAPTION 0x00C00000L
#define WS_VISIBLE 0x10000000L
#define WS_MINIMIZEBOX 0x00020000L
#define WS_SYSMENU 0x00080000L

#define IDC_EDIT 3000
#define IDC_BUTTON 3001
#define IDC_EXIT 3002

Создание файла ресурсов:

rc window.rc

auth DIALOG 10, 10, 205, 60
STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX |
WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK
CAPTION "Аутентификация"
BEGIN
EDITTEXT IDC_EDIT, 15,17,111,13, ES_AUTOHSCROLL | ES_LEFT
DEFPUSHBUTTON "Ввести пароль", IDC_BUTTON, 141,10,52,13
PUSHBUTTON "В&ыход", IDC_EXIT, 141,26,52,13
END

Ассемблирование:

ml /c /coff /Cp window.asm

Компоновка:

link /subsystem:windows window.obj window.res

Если все правильно, создается файл window.exe, готовый к выполнению. Запускаем и наслаждаемся :-).  А теперь попробуем разобраться, хотя и длинновато получилось.
В следующей строке подключаем библиотеку нашей DLL’ки.

includelib auth.lib

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

window_proc proto :DWORD,:DWORD,:DWORD,:DWORD
dll_out proto :DWORD

В секции .data? описываем две неинициализированные переменные:
• hInstance — будет содержать handle окна;
• buffer — память под введенный пароль.
В секции .const содержится набор констант, используемых в нашей программе. Самая интересная — секция .code :-). Сначала мы вызываем функцию, с помощью которой узнаем handle приложения и сохраняем его в переменной hInstance:

invoke GetModuleHandle, NULL
mov hInstance,eax

Далее мы вызываем функцию, в результате чего создается окно. Когда окно закрывается, мы выходим, используя функцию ExitProcess с кодом выхода в регистре eax.
Смотрим на вызов функции DialogBoxParam

invoke DialogBoxParam, hInstance, addr window_name,NULL,addr window_proc, NULL

В переменной hInstance мы передаем handle нашего приложения; window_name — адрес на строку, которая показывает имя окна; третий параметр — handle родительского окна (у нас его нету, значит, ставим NULL :-)); window_proc — адрес на функцию, которая будет обрабатывать сообщения, посылаемые окну; последний, пятый параметр используется при инициализации
Вкратце рассмотрим функцию обработки сообщений окна. На самом деле мы создаем не просто окно, а диалоговое окно, притом используем его как основное. Только что созданному нами окну посылается сообщение WM_INITDIALOG, мы его обрабатываем и устанавливаем активным поле ввода:

if uMsg==WM_INITDIALOG
invoke GetDlgItem, hWnd,IDC_EDIT
invoke SetFocus,eax

Если окну посылается сообщение WM_CLOSE, необходимо вызвать функцию EndDialog и закрыть окно:

.elseif uMsg==WM_CLOSE
invoke EndDialog,hWnd,NULL

Опускаемся ниже по исходному тексту. Нас интересует вот этот участок:

.if ax==IDC_BUTTON
invoke GetDlgItemText,hWnd,IDC_EDIT,ADDR buffer,512
mov ecx,6
xor esi,esi
next:
xor buffer[si],61h
inc si
loop next
invoke dll_out,addr buffer

При нажатии пользователем кнопки копируем введенный текст в буфер, затем шифруем этот текст и вызываем функцию dll_out из нашей DLL-ки. Зачем мы пароль шифруем? В целях безопасности. Безопасней будет, если пересылаться будет зашифрованный пароль, а не открытый.
При нажатии кнопки Выход мы вызываем функцию EndDialog и выходим из программы:

.elseif ax==IDC_EXIT
invoke EndDialog, hWnd, NULL

Несколько слов по поводу содержимого файла window.rc. В этом файле находятся ресурсы нашей программы. Под ресурсами стоит понимать поле ввода и две наши кнопки. В этот файл мы подключили также свойства для окна, почерпнутые нами из справочника.

Автор: Игорь КУЛИШ aka Igor_K