Assembler - язык неограниченных возможностей

         

Динамические библиотеки


Кроме обычных приложений в Windows появился специальный тип файла — динамические библиотеки (DLL). DLL — это файл, содержащий процедуры и данные, которые доступны программам, обращающимся к нему. Например, все системные функции Windows, которыми мы пользовались, на самом деле были процедурами, входящими в состав таких библиотек, — kernel32.dll, user32.dll, comdlg32.dll и т.д. Динамические библиотеки позволяют уменьшить использование памяти и размер исполнимых файлов для тех случаев, когда несколько программ (или даже несколько копий одной и той же программы) используют одну и ту же процедуру. Можно считать, что DLL — это аналог пассивной резидентной программы, с тем лишь отличием, что DLL не находится в памяти, если ни одна программа, его использующая, не загружена.

С точки зрения программирования на ассемблере DLL — это самый обычный исполнимый файл формата РЕ, отличающийся только тем, что при входе в него в стеке находятся три параметра (идентификатор DLL-модуля, причина вызова процедуры и зарезервированный параметр), которые надо удалить, например командой ret 12. Кроме этой процедуры в DLL входят и другие, часть которых можно вызывать из других программ. Список этих экспортируемых процедур должен быть задан во время компиляции DLL, и поэтому команды для компиляции нашего следующего примера будут отличаться от обычных.

Компиляция MASM:

ml /с /coff /Cp /D_MASM_ dllrus.asm link dllrus.obj @dllrus.lnk

Содержимое файла dllrus.lnk:

/DLL /entry:start /subsystem:windows /export:koi2win_asm /export:koi2win /export:koi2wins_asm /export:koi2wins

Компиляция TASM:

tasm /m /x /ml /D_TASM_ dllrus.asm tlink32 -Tpd -c dllrus.obj,,,,dllrus.def

Содержимое файла dllrus.def:

EXPORTS koi2win_asm koi2win koi2wins koi2wins_asm

Компиляция WASM:

wasm dllrus.asm wlink @dllrus.dir

Содержимое dllrus.dir:

file dllrus.obj form windows nt DLL exp koi2win_asm,koi2win,koi2wins_asm,koi2wins

; dllrus.asm ; DLL для Win32 - перекодировщик из koi8 в ср1251 .386 .model flat ; функции, определяемые в этом DLL ifdef _MASM_ public _koi2win_asm@0 ; koi2win_asm - перекодирует символ в AL public _koi2win@4 ; CHAR WINAPI koi2win(CHAR symbol) public _koi2wins_asm@0 ; koi2wins_asm - перекодирует строку в [ЕАХ] public _koi2wins@4 ; VOID WINAPI koi2win(CHAR * string) else public koi2win_asm ; те же функции для TASM и WASM public koi2win public koi2wins_asm public koi2wins endif


.const ; таблица для перевода символа из кодировки KOI8-r (RFC1489) ; в кодировку Windows (cp1251), ; таблица только для символов 80h - FFh ; (то есть надо будет вычесть 80h из символа, преобразовать его командой xlat ; и снова добавить 80h) k2w_tbl db 16 dup(0) ; символы, не существующие в ср1251, db 16 dup(0) ; перекодируются в 80h db 00h, 00h, 00h, 38h, 00h, 00h, 00h, 00h db 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h db 00h, 00h, 00h, 28h, 00h, 00h, 00h, 00h db 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h db 7Eh, 60h, 61h, 76h, 64h, 65h, 74h, 63h db 75h, 68h, 69h, 6Ah, 6Bh, 6Ch, 6Dh, 6Eh db 6Fh, 7Fh, 70h, 71h, 72h, 73h, 66h, 62h db 7Ch, 7Bh, 67h, 78h, 7Dh, 79h, 77h, 7Ah db 5Eh, 40h, 41h, 56h, 44h, 45h, 54h, 43h db 55h, 48h, 49h, 4Ah, 4Bh, 4Ch, 4Dh, 4Eh db 4Fh, 5Fh, 50h, 51h, 52h, 53h, 46h, 42h db 5Ch, 5Bh, 47h, 58h, 5Dh, 59h, 57h, 5Ah

.code ; процедура DLLEntry. Получает три параметра - идентификатор, причину вызова ; и зарезервированный параметр. Нам не нужен ни один из них _start@12: mov al,1 ; надо вернуть ненулевое число в ЕАХ ret 12

; процедура BYTE WINAPI koi2win (BYTE symbol) - ; точка входа для вызова из С ifdef _MASM_ _koi2win@4 proc else koi2win proc endif pop ecx ; обратный адрес в ЕСХ pop eax ; параметр в ЕСХ (теперь стек очищен ; от параметров!) push ecx ; обратный адрес вернуть в стек для RET ; здесь нет команды RET - управление передается следующей процедуре ifdef _MASM_ _koi2win@4 endp else koi2win endp endif

; процедура koi2win_asm ; точка входа для вызова из ассемблерных программ: ; ввод: AL - код символа в KOI ; вывод: AL - код этого же символа в WIN ifdef _MASM_ _koi2win_asm@0 proc else koi2win_asm proc endif test al,80h ; если символ меньше 80h (старший бит 0), jz dont_decode ; не перекодировать, push ebx ; иначе - mov ebx,offset k2w_tbl sub al,80h ; вычесть 80h, xlat ; перекодировать add al,80h ; и прибавить 80h pop ebx dont_decode: ret ; выйти ifdef _MASM_ _koi2win_asm@0 endp else koi2win_asm endp endif

; VOID koi2wins(BYTE * koistring) - ; точка входа для вызова из С ifdef _MASM_ _koi2wins@4 proc else koi2wins proc endif pop ecx ; адрес возврата из стека pop eax ; параметр в ЕАХ push ecx ; адрес возврата в стек ifdef _MASM_ _koi2wins@4 endp else koi2wins endp endif ; точка входа для вызова из ассемблера: ; ввод: ЕАХ - адрес строки, которую надо преобразовать из KOI в WIN ifdef _MASM_ _koi2wins_asm@0 proc else koi2wins_asm proc endif push esi ; сохранить регистры, которые ; нельзя изменять push edi push ebx mov esi,eax ; приемник строк mov edi,eax ; и источник совпадают mov ebx,offset k2w_tbl decode_string: lodsb ; прочитать байт, test al,80h ; если старший бит 0, jz dont_decode2 ; не перекодировать, sub al,80h ; иначе - вычесть 80h, xlat ; перекодировать add al,80h ; и добавить 80h dont_decode2: stosb ; вернуть байт на место, test al,al ; если байт - не ноль, jnz decode_string ; продолжить pop ebx pop edi pop esi ret ifdef _MASM_ _koi2wins_asm@0 endp else koi2wins_asm endp endif end _start@l2



Как видно из примера, нам пришлось назвать все процедуры по-разному для различных ассемблеров. В случае MASM понятно, что все функции должны иметь имена типа _start@12, а иначе программам, использующим их, придется обращаться к функциям с именами типа _Jmp_start, то есть такой DLL нельзя будет использовать из программы, написанной на Microsoft С. В случае TASM и WASM процедуры могут иметь неискаженные имена (и более того, wlink.exe не позволяет экспортировать имя переменной, содержащее символ @), так как их компиляторы берут имена процедур не из библиотечного файла, а прямо из DLL при помощи соответствующей программы — implib или wlib.

Итак, чтобы воспользоваться полученным DLL, напишем простую программу, которая перекодирует одну строку из КОI-8r в Windows ср1251.

; dlldemo.asm ; Графическое приложение для Win32, демонстрирующее работу с dllrus.dll, ; выводит строку в KOI8 и затем в ср1251, перекодированную функцией koi2wins include def32.inc include user32.inc include kernel32.inc includelib dllrus.lib ifndef _MASM_ extrn koi2win__asm:near ; определения для функций из DLL для extrn koi2win:near ; TASM и WASM extrn koi2wins_asm:near ; (хотя для WASM было бы эффективнее extrn koi2wins:near ; использовать __imp__koi2win, выделив else ; его в отдельный условный блок), extrn __imp__koi2win_asm@0:dword ; а это для MASM extrn __imp__koi2win@4:dword extrn __imp__koi2wins_asm@0:dword extrn __imp__koi2wins@4: dword koi2win_asm equ __imp__koi2win_asm@0 koi2win equ __imp__koi2win@4 koi2wins_asm equ __imp__koi2wins_asm@0 koi2wins equ __imp__koi2wins@4 endif

.386 .model flat .const title_string1 db "koi2win demo: string in KOI8",0 title_string2 db "koi2win demo: string in cp1251",0

.data koi_string db 0F3h,0D4h,0D2h,0CFh,0CBh,0C1h,20h,0CEh,0C1h db 20h,0EBh,0EFh,0E9h,2Dh,28h,0 .code _start: push MB_OK push offset title_string1 ; заголовок окна MessageBox push offset koi_string ; строка на KOI push 0 call MessageBox mov eax,offset koi_string push eax call koi2wins push MB_OK push offset title_string2 push offset koi_string push 0 call MessageBox push 0 ; код выхода call ExitProcess ; конец программы

end _start

Этот небольшой DLL может оказаться очень полезным для расшифровки текстов, приходящих из сети Internet или других систем, в которых используется кодировка KOI8. Воспользовавшись таблицами из приложения 1, вы можете расширить набор функций dllrus.dll, вплоть до перекодировки из любого варианта в какой угодно.


Содержание раздела