
Образ мышления: Assembler
То, что не удается запрограммировать на Ассемблере, приходится паять
Образ мышления: Assembler - RSS-канал
Образ мышления: Assembler - Карта сайта

Рекурсивный обход дерева каталогов
19.01.2009 | Категория: Образ мышления: Assembler | Автор: ManHunter
Обход дерева каталогов является одной из классических прикладных задач на применение рекурсии. В Windows штатных API-функций для этого нет, поэтому поиск файлов выполняется при помощи пары API-функций FindFirstFile и FindNextFile. Совершенно непонятно, почему разработчики Windows не дали возможность точно настраивать критерии поиска, ограничившись только маской имени файла. Даже в MS-DOS для решения аналогичной задачи можно было задать по крайней мере атрибуты файлов, например для поиска только каталогов. Более расширенные возможности для поиска предоставляет API-функция FindFirstFileEx, но она доступна только в операционной системе Windows 2000 и выше.Для рекурсивного обхода дерева каталогов я написал следующую функцию. Она сканирует дерево каталогов, начиная с указанного, и передает все найденные имена файлов в заданную функцию-обработчик. Все действия с найденными файлами выполняются уже в ней.
Code (Assembler) : Убрать нумерацию
- ;------------------------------------------------------------------
- ; Функция рекурсивного обхода дерева каталогов
- ; (C) ManHunter / PCL
- ; http://www.manhunter.ru
- ;
- ; Параметры вызова:
- ; lpFStr - указатель на начальный каталог без завершающего слеша.
- ; он должен быть в буфере размером не менее MAX_PATH байт
- ; lpProc - указатель на callback-функцию для передачи имен файлов,
- ; это обязательный параметр
- ; dFlag - флаг "передавать в callback-функцию имена найденных
- ; каталогов" (TRUE/FALSE)
- ;------------------------------------------------------------------
- proc FindFileRecursive lpFStr:dword,lpProc:dword,dFlag:dword
- local hFind:DWORD ; Локальный хэндл текущего поиска
- locals
- FndData WIN32_FIND_DATA ; Локальная структура WIN32_FIND_DATA
- endl
- ; Сохранить изменяемые регистры
- push ebx ecx edx
- ; Добавить к пути поиска '\*.*'
- invoke lstrcat,[lpFStr],ff_mask
- ; Найти первый файл
- lea eax,[FndData]
- push eax
- invoke FindFirstFile,[lpFStr]
- ; В случае ошибки полностью прекратить дальнейшее сканирование
- cmp eax,INVALID_HANDLE_VALUE
- jne @f
- xor eax,eax
- jmp ff_exit
- @@:
- ; Сохранить хэндл текущего поиска
- mov [hFind],eax
- ff_chk_file:
- ; Проверить имя файла на недопустимое
- lea eax,[FndData.cFileName]
- push eax
- ; Имя файла '.'
- invoke lstrcmp,ff_skip1
- or eax,eax
- ; Да, пропустить
- jz ff_next_file
- lea eax,[FndData.cFileName]
- push eax
- ; Имя файла '..'
- invoke lstrcmp,ff_skip2
- or eax,eax
- ; Да, пропустить
- jz ff_next_file
- ; Если установлен флаг dFlag=TRUE, то передавать в callback-процедуру
- ; все найденные результаты, в том числе и каталоги
- cmp [dFlag],0
- jne @f
- ; Установлен флаг передавать только файлы. Проверить атрибуты
- ; найденного файла
- mov eax,[FndData.dwFileAttributes]
- and eax,FILE_ATTRIBUTE_DIRECTORY
- ; Это каталог, пропустить
- jnz ff_do_not_callback
- @@:
- ; Вычислить длину текущей строки поиска и обрезать '*.*'
- invoke lstrlen,[lpFStr]
- sub eax,3
- add eax,[lpFStr]
- mov byte [eax],0
- push eax
- ; Дописать к пути имя найденного файла или каталога
- lea eax,[FndData.cFileName]
- push eax
- invoke lstrcat,[lpFStr]
- ; Передать имя файла в callback-функцию
- stdcall [lpProc],[lpFStr]
- ; Вернуть маску поиска на место
- pop ecx
- mov dword [ecx],'*.*'
- ; Если callback-функция вернула 0, то прекратить сканирование
- or eax,eax
- jz ff_stop_scan
- ; Это каталог?
- mov eax,[FndData.dwFileAttributes]
- and eax,FILE_ATTRIBUTE_DIRECTORY
- je ff_next_file
- ff_do_not_callback:
- ; Вычислить длину текущей строки поиска и обрезать '*.*'
- invoke lstrlen,[lpFStr]
- sub eax,3
- add eax,[lpFStr]
- mov byte [eax],0
- push eax
- ; Дописать к пути имя найденного каталога
- lea eax,[FndData.cFileName]
- push eax
- invoke lstrcat,[lpFStr]
- ; Рекурсивный вызов поиска файлов в новом каталоге
- stdcall FindFileRecursive,[lpFStr],[lpProc],[dFlag]
- ; Вернуть маску поиска на место
- pop ecx
- mov dword [ecx],'*.*'
- ; Если callback-функция вернула 0, то прекратить сканирование
- or eax,eax
- jz ff_stop_scan
- ff_next_file:
- ; Найти следующий файл
- lea eax,[FndData]
- push eax
- invoke FindNextFile,[hFind]
- or eax,eax
- ; Файл найден, обработать его
- jnz ff_chk_file
- ; По умолчанию установить флаг "продолжать сканирование"
- mov eax,TRUE
- ff_stop_scan:
- ; Закрыть хэндл текущего поиска
- push eax
- invoke FindClose,[hFind]
- pop eax
- ff_exit:
- ; Восстановить измененные регистры
- pop edx ecx ebx
- ; Возврат из процедуры.
- ; Код возврата EAX=1 - продолжать сканирование, EAX=0 - стоп
- ret
- ff_mask db '\*.*',0 ; Маска файлов для поиска
- ff_skip1 db '.',0 ; Запрещенное имя файла
- ff_skip2 db '..',0 ; Запрещенное имя файла
- endp
Читать статью целиком »
Просмотров: 9225 | Комментариев: 2

Функции base64 на Ассемблере
09.01.2009 | Категория: Образ мышления: Assembler | Автор: ManHunter
Алгоритм Base64 может использоваться в пользовательских почтовых приложениях, в качестве одного из уровней защиты шифрованием, для хранения двоичных данных и для решения многих других задачах. В большинстве языков высокого уровня используются штатные функции, в Ассемблере приходится все реализовывать самостоятельно.Алгоритм Base64 обратимый, то есть из закодированного текста можно в точности получить исходные данные. Начнем с функции кодирования.
Code (Assembler) : Убрать нумерацию
- ;---------------------------------------------------------------
- ; Функция кодирования Base64
- ;---------------------------------------------------------------
- ; Параметры:
- ; lpFrom - указатель на исходные данные
- ; lpTo - указатель на буфер для приема кодированных данных
- ; dSize - размер исходных данных
- ; Функция ничего не возвращает
- ;---------------------------------------------------------------
- proc base64_encode lpFrom:dword, lpTo:dword, dSize:dword
- pusha
- mov ebx,.base64
- mov esi,[lpFrom]
- mov edi,[lpTo]
- mov ecx,[dSize]
- or ecx,ecx
- jz .r3
- .encode_loop:
- lodsd
- mov edx,eax
- bswap edx
- xor eax,eax
- shld eax,edx,6
- shl edx,6
- xlatb
- stosb
- xor eax,eax
- shld eax,edx,6
- shl edx,6
- xlatb
- stosb
- dec ecx
- jz .r2
- xor eax,eax
- shld eax,edx,6
- shl edx,6
- xlatb
- stosb
- dec ecx
- jz .r1
- xor eax,eax
- shld eax,edx,6
- shl edx,6
- xlatb
- stosb
- dec esi
- dec ecx
- jnz .encode_loop
- jmp .r3
- .r2:
- mov al,'='
- stosb
- .r1:
- mov al,'='
- stosb
- .r3:
- xor eax,eax
- stosb
- popa
- ret
- .base64 db 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
- db 'abcdefghijklmnopqrstuvwxyz'
- db '0123456789+/'
- endp
Читать статью целиком »
Просмотров: 5973 | Комментариев: 9

Запрет запуска нескольких копий программы
25.12.2008 | Категория: Образ мышления: Assembler | Автор: ManHunter
Запрет запуска нескольких копий программы бывает полезен если может возникнуть конфликт из-за занятых системных ресурсов, монопольно открытых файлов или если задачи приложения подразумевают наличие только одного его экземпляра. Проверка наличия работающей копии программы реализуется несколькими способами в зависимости от поставленной задачи.Первый способ основан на том, что в приложении можно определить расшаренную секцию, данные из которой будут доступны для всех его запущенных экземпляров. Достаточно прописать в ней некоторую переменную и присвоить ей уникальное значение. При старте выполняется проверка, и если значение переменной равно начальному, то считается что это старт первой копии, иначе приложение является второй копией и должно завершить свою работу. Поэтому первая копия приложения сразу после запуска и проверки должна заменить значение переменной на другое.
Code (Assembler) : Убрать нумерацию
- ; Расшаренная секция, общая для всех копий данного приложения
- section '.shared' data readable writeable shareable
- started dd 1 ; Флаг первого запуска
- ; Сегмент кода
- section '.code' code readable executable
- cmp [started],1 ; Уже запущен экземпляр программы?
- jne already_started ; Да, на выход
- xor eax,eax
- ; Префикс LOCK и команда XCHG используются для предотвращения
- ; возможных конфликтов на многопроцессорных машинах
- lock xchg eax,[started] ; Сбросить флаг
- ; Нормальный запуск программы
- ...
- already_started:
- ; Выход из программы
- ...
Читать статью целиком »
Просмотров: 16342 | Комментариев: 7

Задача на применение логических инструкций
16.12.2008 | Категория: Образ мышления: Assembler | Автор: ManHunter
Когда-то кому-то помогал с решением задачи на ассемблере, формулировка задания была такая:
Дан массив из 5 байт. Рассматривая его как массив из восьми 5-битных слов, посчитать количество слов с четным числом единиц в слове.
Алгоритм хитровывернутый, комментарии специально не прописывал, чтобы тупая и ленивая школота не смогла объяснить преподу, откуда взялось такое решение и как оно работает.
Code (Assembler) : Убрать нумерацию
- ;---------------------------------------------------------------
- ; Задача на применение логических инструкций
- ;
- ; Дан массив из 5 байт. Рассматривая его как массив из восьми
- ; 5-битных слов, посчитать количество слов с четным числом
- ; единиц в слове.
- ;
- ; Решение: ManHunter / PCL
- ;---------------------------------------------------------------
- format PE GUI 4.0
- entry start
- include 'win32a.inc'
- ;---------------------------------------------------------------
- section '.data' data readable writeable
- xbytes db 00111000b ; Данные для задачки, взяты с потолка :)
- db 11111110b
- db 01010101b
- db 00001001b
- db 00000110b
- mask db 'Count: %i',13,10,13,10
- db '%i%i%i%i%i - %i%i%i%i%i - %i%i%i%i%i - %i%i%i%i%i',13,10
- db '%i%i%i%i%i - %i%i%i%i%i - %i%i%i%i%i - %i%i%i%i%i',13,10
- db 0
- title db 'Solution',0
- tmp rb 100
- ;---------------------------------------------------------------
- section '.code' code readable executable
- start:
- mov esi,xbytes+4
- loc_1:
- lodsb
- mov ecx,8
- loc_2:
- xor edx,edx
- test al,00000001b
- jz loc_3
- inc edx
- loc_3:
- push edx
- shr al,1
- loop loc_2
- dec esi
- dec esi
- cmp esi,xbytes
- jnb loc_1
- xor eax,eax
- xor esi,esi
- loc_4:
- xor edi,edi
- mov ecx,5
- loc_5:
- add edi,[esp+eax*4]
- inc eax
- loop loc_5
- test edi,edi
- jz loc_6
- test edi,1
- jnz loc_6
- inc esi
- loc_6:
- cmp eax,40
- jb loc_4
- invoke wsprintf,tmp,mask,esi
- add esp,12+(8*5*4)
- invoke MessageBox,HWND_DESKTOP,tmp,title,MB_OK
- invoke ExitProcess,0
- ;---------------------------------------------------------------
- section '.idata' import data readable writeable
- library kernel32,"KERNEL32.DLL",\
- user32,"USER32.DLL"
- include "apia\kernel32.inc"
- include "apia\user32.inc"
Читать статью целиком »
Просмотров: 5977 | Комментариев: 3

Обработка перетаскивания файлов (Drag'n'Drop)
10.12.2008 | Категория: Образ мышления: Assembler | Автор: ManHunter
Если в вашем приложении используется обработка файлов, то кроме открытия через стандартные диалоги выбора файла и каталога, можно получать их из Проводника Windows перетаскиванием. Обработка перетаскивания файлов выполняется в два этапа. При инициализации диалогового окна приложения должна вызываться функция DragAcceptFiles. Параметр функции TRUE разрешает принятие файлов окном, а FALSE его запрещает, так что прием файлов можно регулировать динамически. Непосредственно прием файлов окном выполняется функцией DragQueryFile.Code (Assembler) : Убрать нумерацию
- ; Сегмент кода
- section '.code' code readable executable
- ...
- ; Процедура обработчика окна
- proc DialogProc hwnddlg,msg,wparam,lparam
- ...
- ; Инициализация окна
- cmp [msg],WM_INITDIALOG
- je wminitdialog
- ; Обработка перетаскивания файлов
- cmp [msg],WM_DROPFILES
- je wmdropfiles
- ...
- wminitdialog:
- ; Разрешить окну принимать файлы
- invoke DragAcceptFiles,[hwnddlg],TRUE
- jmp processed
- wmdropfiles:
- ; Обработка полученных файлов. Функция DragQueryFile возвращает имя
- ; файла с указанным индексом (нумерация индексов начинается с нуля).
- ; Для получения общего количества переданных файлов ее надо вызвать с
- ; индексом равным 0FFFFFFFFh
- invoke DragQueryFile,[wparam],0FFFFFFFFh,NULL,NULL
- ; В регистре EAX количество переданных файлов
- ; Перебрать по очереди все переданные окну файлы
- xor ecx,ecx
- process_file:
- push ecx eax
- ; Получить имя файла или каталога в буфер fname
- invoke DragQueryFile,[wparam],ecx,fname,100h
- ...
- ; Тут будет обработчик переданных файлов и каталогов
- ...
- pop eax ecx
- inc ecx
- cmp ecx,eax
- jne process_file
- ; Освободить дескриптор операции
- invoke DragFinish,[wparam]
- ...
После выполнения всех необходимых действий с файлом, надо освободить память, выделенную под файлы, при помощи функции DragFinish.
Читать статью целиком »
Просмотров: 5780 | Комментариев: 2
