В прошлом номере мы начали знакомиться с языком программирования низкого уровня — Ассемблером, и создали собственную DLL. Теперь мы продолжим знакомство — используем созданную DLL`ку в программе аутентификации пользователя.
Для начала давайте поподробнее разберемся с исходным текстом нашей DLL’ки. Обратим особое внимание на секцию данных. Переменная hash содержит зашифрованный пароль — mycomp. Шифрование производилось операцией xor с буквой a, то есть:
xor ‘y’,‘a’ = 18h
xor ‘c’,‘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:
bad db "Введен неправильный пароль!",0
С секцией данных разобрались, теперь приступаем к секции кода. На процедуру dll_proc внимание не обращаем, сконцентрируемся на процедуре dll_out.
Это, можно сказать, набор процедур, которые могут использовать программы. В этом участке кода производится сравнение введенного пароля. Вернее, не пароля, а хешей пароля. Далее объясню, почему мы поступаем именно так. В esi мы заносим адрес введенного пароля, а в edi — адрес нашего хеша. В ecx записывается количество байт, необходимых для сравнения, в нашем случае пароль имеет длину 6 байт (mycomp). Команда cmpsb выполняет побайтное сравнение.
mov esi,mess
lea edi,hash
mov ecx,6
cmpsb
Если введен правильный пароль, флаг ZF будет установлен в 1, а если ZF сброшен в 0, тогда пароль неверен, чем мы пользуемся. В случае ошибки прыгаем на метку и выводим сообщение об ошибке, а иначе вызываем функцию WinAPI ShellExecute:
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 с таким вот содержанием:
.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_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:
mov hInstance,eax
Далее мы вызываем функцию, в результате чего создается окно. Когда окно закрывается, мы выходим, используя функцию ExitProcess с кодом выхода в регистре eax.
Смотрим на вызов функции DialogBoxParam
В переменной hInstance мы передаем handle нашего приложения;
window_name — адрес на строку, которая показывает имя окна; третий
параметр — handle родительского окна (у нас его нету, значит, ставим
NULL :-)); window_proc — адрес на функцию, которая будет обрабатывать
сообщения, посылаемые окну; последний, пятый параметр используется при
инициализации
Вкратце рассмотрим функцию обработки сообщений окна. На самом деле мы
создаем не просто окно, а диалоговое окно, притом используем его как
основное. Только что созданному нами окну посылается сообщение
WM_INITDIALOG, мы его обрабатываем и устанавливаем активным поле ввода:
invoke GetDlgItem, hWnd,IDC_EDIT
invoke SetFocus,eax
Если окну посылается сообщение WM_CLOSE, необходимо вызвать функцию EndDialog и закрыть окно:
invoke EndDialog,hWnd,NULL
Опускаемся ниже по исходному тексту. Нас интересует вот этот участок:
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 и выходим из программы:
invoke EndDialog, hWnd, NULL
Несколько слов по поводу содержимого файла window.rc. В этом файле
находятся ресурсы нашей программы. Под ресурсами стоит понимать поле
ввода и две наши кнопки. В этот файл мы подключили также свойства для
окна, почерпнутые нами из справочника.
Автор: Игорь КУЛИШ aka Igor_K