Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Примеры использования Win32 API

7
02 декабря 2009 года
@pixo $oft
3.4K / / 20.09.2006
Под влиянием экспериментов Kogrom'а и исследований Washington'а решил создать нечто подобное.Это,конечно,будут вовсе не уроки Iczelion'а,но всё же:)
Для компиляции используется MAsm v9,отладка производилась OllyDbg 1.10

Итак,приступим
[SIZE="3"]1.Чтение с устройства хранения данных(дамп образа в файл)[/SIZE][Highlight=Asm].386
.Model Flat,StdCall
Include ..\Include\Kernel32.inc
Include ..\Include\User32.inc
Include ..\Include\Windows.inc
IncludeLib ..\Lib\Kernel32
IncludeLib ..\Lib\User32
BufSz Equ 65536 //Размер буфера,в которые будут считываться данные с носителя
.Data
Mem DB BufSZ Dup(0)
hStdIn DD 0
hStdOut DD 0
hDev DD 0
hDmpFile DD 0
hHeap DD 0
lpMem DD 0
dwBytes DD 0
dwBytesWritten DD 0
Prompt DB '‚ўҐ¤ЁвҐ ЎгЄўг ¤ЁбЄ*:'
PromptLen DD $-Prompt
DmpFile DB 'DevDump.dat',0
DevPath DB '\\.\†:',0
FillChar DB 1
MsgCap DB 'Device data dumper',0
MsgOpenDevErr DB 'Ошибка открытия устройства',0
MsgOpenDmpErr DB 'Ошибка создания файла дампа',0
MsgWriteErr DB 'Ошибка записи в файл',0
MsgCompleted DB 'Запись завершена',0

.Code
ErrMB Macro Msg //Макрос для вывода сообщений об ошибках
Invoke MessageBox,EBX,Offset Msg,Offset MsgCap,MB_IconHand
EndM

Main:Invoke GetStdHandle,Std_Output_Handle
Mov hStdOut,EAX
Invoke GetStdHandle,Std_Input_Handle
Mov hStdIn,EAX
XOr EBX,EBX
Invoke WriteConsole,hStdOut,Offset Prompt,PromptLen,Offset dwBytesWritten,EBX
Invoke ReadConsole,hStdIn,Addr DevPath+4,1,Offset dwBytes,EBX
Invoke CreateFile,Offset DevPath,Generic_Read,EBX,EBX,Open_Existing,File_Flag_Sequential_Scan,EBX
Test EAX,EAX
JS OpenDevErr
Mov hDev,EAX
Invoke CreateFile,Offset DmpFile,Generic_Write,EBX,EBX,Create_Always,File_Attribute_Normal,EBX
Test EAX,EAX
JS OpenDmpErr
Mov hDmpFile,EAX
Dump:Invoke ReadFile,hDev,Offset Mem,BufSz,Offset dwBytes,EBX
Test EAX,EAX
JZ CloseDmp
Cmp dwBytes,EBX
JZ CloseDmp
Invoke WriteFile,hDmpFile,Offset Mem,dwBytes,Offset dwBytesWritten,EBX
Test EAX,EAX
JZ WriteErr
Invoke WriteConsole,hStdOut,Offset FillChar,1,Offset dwBytesWritten,EBX
XOr FillChar,3
Jmp Dump
WriteErr:ErrMB MsgWriteErr
Jmp CloseDmp
OpenDmpErr:ErrMB MsgOpenDmpErr
Jmp CloseDev
CloseDmp:Invoke CloseHandle,hDmpFile
Invoke MessageBox,EBX,Offset MsgCompleted,Offset MsgCap,MB_IconAsterisk
CloseDev:Invoke CloseHandle,hDev
Ret
OpenDevErr:ErrMB MsgOpenDevErr
Ret
End Main[/Highlight]Разумеется,файлы .inc и .lib у всех расположены по-разному.Следует учесть это при компиляции

Описание кода
В строках 34-37 происходит стандартная для консольного приложения операция получения дескрипторов ввода-вывода.В строке 38 обнуляется регистр EBX(в дальнейшем его можно будет подставлять в те места,куда требуется передать нулевое значение;используется для сокращения размера кода).Т.к. функции WinAPI гарантированно сохраняют этот регистр,а в нашем коде он нигде не меняется,его смело можно использовать для такого рода оптимизации
Строки 39-40–ввод буквы устройства,с которого будет производиться чтение.Т.к. вводится только одна буква(позиция буквы помечена символом "†" в переменной DevPath),задаётся смещение +4 от начала переменной и длина вводимой строки 1 символ
В строках 41-44 происходит открытие устройства и проверка успешности операции.Чтобы открыть том как файл,его имя должно быть задано в специальном формате¹.Для ускорения операций чтения применяем флаг File_Flag_Sequential_Scan(т.к. читать данные будем последовательно)
В строках 45-48 создаётся файл,в который будут сохраняться данные с тома,и проверяется успешность его создания
При возникновении ошибок в предыдущих двух блоках управление передаётся на участки кода,где вызывается макрос вывода сообщения об ошибке и закрытие открытых ресурсов(тома и/или файла дампа)
Цикл в строках 49-59–дамп с тома в файл.Данные читаются в буфер Mem пакетами по BufSz байт.Также для визуализации процесса дампа осуществляется вывод в консоль символов с кодами 1 и 2("[SIZE="4"]☺[/SIZE]" и "[SIZE="4"]☻[/SIZE]" соответственно)
В случае возникновения ошибки или завершения чтения данных с тома² происходит выход из цикла с выдачей соответствующего сообщения и закрытием используемых файловых дескрипторов(стр. 64-66)

Комментарии к коду
Текст сообщения Prompt записано в кодировке DOS,дабы не осуществлять его преобразование с помощью CharToOEM при выводе
Для уменьшения размера скомпилированной программы можно данные,имеющие нулевое значение(например,буфер) поместить в секцию неинициализированных данных .Data?.Естественно,не следует забывать о том,что в таком случае придётся указать вместо значения 0 знак "?"
Для выхода из программы в любой точке используется инструкция Ret,эквивалентная Invoke ExitProcess,0(можете проверить отладчиком,посмотрев,куда пойдёт выполнение после вызова Ret).Разумеется,перед использованием инструкции требуется,чтобы стек был сбалансирован относительно момента начала программы

Следует заметить,что этот метод работает только на Windows 2000,XP и 2003,т.к. в Windows линейки 9x CreateFile не поддерживала работу с устройствами,а в Windows Vista,2008 и старше прямой доступ к томам закрыт по соображениям безопасности³

Ссылки по теме:
1)CreateFile
2)ReadFile
3)Changes to the file system/storage stack to restrict direct disk/volume access in Windows Vista/Server 2008
[COLOR="Gray"]P.S.Критика принимается и ожидается.Буду рад советам,как можно изменить подаваемый материал
Здесь будут выкладываться не только готовые решения,но и те,на которых я сам застопорился,так что буду рад любой помощи;)[/COLOR]
7
04 декабря 2009 года
@pixo $oft
3.4K / / 20.09.2006
В дополнение к предыдещему посту(и всем последующим):теперь с каждыми постом будет выкладываться исходник,ибо копирование кода отсюда не Бог весть как удобно

Далее,здесь я выложу свой bat-файл для компиляции программ с помощью MAsm.С его помощью можно скомпилировать DLL,консольную или оконную программу.Синтаксис:
 
Код:
AsMake <имя файла.asm> <C/D/G>
2й параметр–это тип получаемого на выходе файла.C–консольное приложение(CUI),D–DLL,G–оконное приложение(GUI)
Я значительно упростил себе задачу компиляции,введя в контекстное меню для файлов .asm 3 действия–соответственно для консольного,DLL и оконного приложения.В папке с bat-файлом должны находиться файлы ML.exe и Link32.exe

Если возникнут вопросы по использованию bat-файла,задавайте их в личку или тут.Также,если вы заметили ошибки в исходниках или самих постах,просьба также сообщить мне об этом
7
07 декабря 2009 года
@pixo $oft
3.4K / / 20.09.2006
[SIZE="3"]2.Рекурсивный поиск в директориях[/SIZE]
[Highlight=Asm].386
.Model Flat,StdCall
Include ..\Include\Kernel32.inc
Include ..\Include\User32.inc
Include ..\Include\Windows.inc
IncludeLib ..\Lib\Kernel32
IncludeLib ..\Lib\User32
.Data
hStdOut DD 0
hStdIn DD 0
dwBytes DD 0
uW32FD Win32_Find_Data<>
szDir DD 65 Dup(0)
szRootDir DD 6044160
szMask DW 42
szDirUp DB '..',0
cDrvLetter DB 'Drive name:'
cbDrvLetter Equ $-cDrvLetter
cChDirFailed DB '-SetCurrentDirectory error',13,10
cbChDirFailed Equ $-cChDirFailed
cSearchFailed DB '-FindFristFile error',13,10
cbSearchFailed Equ $-cSearchFailed
.Code
Main:
Invoke GetStdHandle,Std_Output_Handle
Mov hStdOut,EAX
Invoke GetStdHandle,Std_Input_Handle
Mov hStdIn,EAX
XOr EBX,EBX
Mov EBP,Offset dwBytes
Invoke WriteConsole,hStdOut,Addr cDrvLetter,cbDrvLetter,EBP,EBX
Invoke ReadConsole,hStdIn,Addr szRootDir,1,EBP,EBX
Mov EDI,Offset uW32FD
Assume EDI:Ptr Win32_Find_Data
Push Offset szRootDir
Call SearchSubDir
Ret
SearchSubDir:
Invoke SetCurrentDirectory,[ESP+4]
Test EAX,EAX
JZ Near Ptr ErrChDir
Push ESI
Mov ESI,Offset szDir
Invoke GetCurrentDirectory,260,ESI
Push EAX
Invoke CharToOEM,ESI,ESI
Pop EAX
Invoke WriteConsole,hStdOut,ESI,EAX,EBP,EBX
Invoke WriteConsole,hStdOut,Addr cSearchFailed-2,2,EBP,EBX
Invoke FindFirstFile,Addr szMask,EDI
Test EAX,EAX
JZ ErrFind1st
Mov ESI,EAX
FindNext:
And [EDI].dwFileAttributes,File_Attribute_Directory
JZ @F
Cmp Word Ptr [EDI].cFileName,46
JE @F
Invoke lStrCmp,Addr [EDI].cFileName,Addr szDirUp
JE @F
LEA EAX,[EDI].cFileName
Push EAX
Call SearchSubDir
@@:
Invoke FindNextFile,ESI,EDI
Test EAX,EAX
JNZ FindNext
Invoke FindClose,ESI
Invoke SetCurrentDirectory,Addr szDirUp
Jmp LeaveProc
ErrChDir:
Invoke WriteConsole,hStdOut,Addr cChDirFailed,cbChDirFailed,EBP,EAX
Ret 4
ErrFind1st:
Invoke WriteConsole,hStdOut,Addr cSearchFailed,cbSearchFailed,EBP,EAX
LeaveProc:
Pop ESI
Ret 4
End Main[/Highlight][COLOR="Red"]Примечание:[/COLOR]вследствие неправильного отображения открывающей квадратной скобки в коде она заменяется на последовательность "[".Это надо учесть при работе с кодом

Описание кода
Строки 25-29 аналогичны строкам 34-38 предыдущего примера.Также для оптимизации здесь используется регистр EBP,содержащий в себе адрес переменной,в которую записывается число прочитанных/выведенных символов.В соответствии с замечанием в предыдущем примере о сохранениии API-функциями некоторых регистров за целостность содержимого регистра EBP можно не бояться,ибо в программе он не меняется
В строках 31-32 вводится буква диска,на котором будет осуществляться поиск(при необходимости программу можно расширить так,чтобы она искала в любой папке).В строках 33-34 осуществляется занесение указателя на переменную типа Win32_Find_Data и "типизация" содержимого по этому адресу(т.е. можно будет использовать точечную нотацию для доступа к полям структуры).Затем вызывается процедура SearchSubDir с единственным параметром–папкой,в которой осуществляется поиск

Процедура SearchSubDir реализована следующим образом:в строках 39-41 осуществляется попытка перейти в папку,переданную в качестве параметра.При неудаче(например,закрыт доступ или папка к тому времени была удалена) осуществляется вывод сообщения об ошибке и возврат на уровень выше
Далее осуществляется сохранение регистра ESI(т.к. он затем будет использоваться в программе,а в нём хранится дескриптор предыдущего поиска) и присвоение ему адреса буфера,в котором будет храниться полное имя текущей папки(используется для вывода его в консоль).Т.к. GetCurrentDirectory возвращает длину имени папки&#185;,она сохраняется сначала в регистре EAX,а затем в стеке для последующего использования(строки 44-45).В строке 46 осуществляется перекодировка имени папки в кодовую страницу DOS(без этого выводимое имя папки,содержащей русские буквы,будет содержать крякозябры).В строках 48-49 происходи вывод имени текущей папки и переход на следующую строку
Строки 50-53–поиск 1го файла в текущей папке и сохранение дескриптора поиска.Строки 54-67–собственно рекурсивный поиск:найденный файл проверяется на то,является ли он папкой(и не является ли он при этом папкой "." или ".."–они находятся в 1ю очередь;проверка осуществляется в строках 55-60).Затем,если это оказалась папка,рекурсивно вызывается процедура SearchSubDir,в качестве аргумента которой передаётся полное имя папки,содержащееся в поле cFileName структуры Win32_Find_Data.Если же файл папкой не является,или когда закончится процедура поиска в подпапках,происходит поиск следующего файла в текущей папке.Когда файлы в текущей папке закончатся(FindNextFile вернёт 0),происходит закрытие поиска,переход в папку уровнем выше(строки 65-69) и выход из процедуры
В конце процедуры происходит восстановление значения регистра ESI(дескриптор предыдущего поиска),возвращающее баланс стека

Примечания
Значение переменной szRootDir задано в виде числа,которое в символьной записи соответствует строке 0,':\',0(где первый 0–это место для подстановки буквы диска,а второй–завершающий строку)
Значение переменной szMask(маска поиска) также задано в виде числа,что в символьной записи соответствует строке '*',0
В строке 49 в качестве адреса буфера для вывода используется адрес строки cSearchFailed со смещением –2 для того,чтобы создать переход на новую строку,не создавая дополнительную строку с CRLF
Некоторые строки,которые используются в контексте только этого примера(например,вывод полного имени текущей папки–вряд ли он будет нужен в другой программе,ибо его всегда можно получить из структуры Win32_Find_Data) можно убрать.Разумеется,при этом надо сохранить логику программы и баланс стека
В целях оптимизации проверку на то,является ли файл папкой,лучше всего перенести в самое начало,дабы сразу отсеивать файлы и не проверять,не равно ли их имя "." или ".."(в исходном коде это была последняя проверка)

Ссылки по теме:
1)GetCurrentDirectory
2)CharToOem
3)FindFirstFile
7
12 декабря 2009 года
@pixo $oft
3.4K / / 20.09.2006
[SIZE="3"]3.Подсчёт времени работы процесса[/SIZE]
[Highlight=Asm].386
.Model Flat,StdCall
Include ..\Include\Kernel32.inc
Include ..\Include\Windows.inc
Include ..\Include\WinMM.inc
IncludeLib ..\Lib\Kernel32
IncludeLib ..\Lib\WinMM
.Code
hStdOut DD 0
hStdIn DD 0
dwBytes DD 0
szAppName DD 64 Dup(0)
uSI StartupInfo<Type StartupInfo,,,,,,,,,,,,SW_Show>
uPI ProcessInformation<>
DW 5 Dup(0)
cFNPrompt DB '‚ўҐ¤ЁвҐ Ё¬п ЇаЁ«®¦Ґ*Ёп:'
cbFNPrompt Equ $-cFNPrompt
cExecErr DB 'ЋиЁЎЄ* ЇаЁ §*ЇгбЄҐ'
cbExecErr Equ $-cExecErr
Main:Invoke GetStdHandle,Std_Output_Handle
Mov hStdOut,EAX
Invoke GetStdHandle,Std_Input_Handle
Mov hStdIn,EAX
XOr EBX,EBX
Invoke WriteConsole,hStdOut,Addr cFNPrompt,cbFNPrompt,Addr dwBytes,EBX
Invoke ReadConsole,hStdIn,Addr szAppName,255,Addr dwBytes,EBX
Mov EAX,Offset szAppName
Add EAX,dwBytes
Mov Byte Ptr[EAX-2],BL
Invoke CreateProcess,Addr szAppName,EBX,EBX,EBX,EBX,EBX,EBX,EBX,Addr uSI,Addr uPI
Test EAX,EAX
JZ RunErr
Call TimeGetTime
Push EAX
Invoke WaitForSingleObject,uPI.hProcess,-1
Call TimeGetTime
Sub EAX,[ESP]
Pop EBX
XOr EBX,EBX
Mov BL,10
Mov EDI,Offset cFNPrompt
@@:XOr EDX,EDX
Div EBX
Add DL,48
Dec EDI
Mov Byte Ptr[EDI],DL
Test EAX,EAX
JNZ @B
Mov EAX,Offset cFNPrompt
Sub EAX,EDI
Invoke WriteConsole,hStdOut,EDI,EAX,Addr dwBytes,0
Ret
RunErr:Invoke WriteConsole,hStdOut,Addr cExecErr,cbExecErr,Addr dwBytes,0
Ret
End Main[/Highlight]Описание кода
В строках 20-25 осуществляется стандартная подготовка к консольному вводу-выводу и вывод приглашения к вводу.В строке 26 происходит ввод имени запускаемой программы.В строках 27-29 введённая строка приводится к виду ASCIIZ(т.к. после ввода она оканчивается не символом с кодом 0,а символами перевода строки)
Строки 30-32–попытка запуска указанной программы&#185;.В строках 33-34 сохраняется время начала отсчёта,а в строке 35 начинается ожидание завершения запущенной программы&#178;.После её завершения вычисляется разница во времени(в миллисекундах)&#179;,а в цикле в строках 42-48 осуществляется преобразование полученного числа в строку в специально отведённый буфер между структурой ProcessInformation и первой строковой переменной.Затем в строках 49-51 в регистре EAX формируется адрес начала строки,содержащей выводимое число,и её вывод

Примечания
В структуре StartupInfo инициализированы только 2 поля–размер структуры(обязательное) и команда показа окна приложения
Пустой буфер,заданный строкой DW 5 Dup(0),служит для хранения строки,содержащей время работы программы.Алгоритм преобразования из строки в число тривиален:осуществляется последовательное деление числа миллисекунд,хранящегося в EAX,на 10 с сохранением остатка в обратном порядке.При этом указатель начала строки смещается к младшим адресам и к окончанию преобразования указывает как раз на начало полученной строки
Путь к программе может быть как относительным,так и абсолютным,и обязан содержать расширение(если таковое имеется)

Ссылки по теме:
1)CreateProcess
2)WaitForSingleObject
3)TimeGetTime
55K
24 декабря 2009 года
Arguefedeelve
5 / / 09.12.2009
Т.е. VCL не юзаешь? Из-за размеров кода?
Ну не знаю, сам пишу 99 на базе VCL - там с комбобоксами все в порядке. Здравый смысл подсказывает, что и на голом WinAPI должно работать нормально. Другое дело, что VCL иногда подменяет/добавляет функциональность стандартных API-шных контролов в своих компонентах-обертках вокруг них. Но твои проблемы - явно не тот случай.

AiK, извиняюсь, пора бежать
7
24 декабря 2009 года
@pixo $oft
3.4K / / 20.09.2006
О какой VCL может идти речь на ассемблере?Конечно,ни о какой.Я не пользуюсь ни Delphi,ни BCB
А о каких проблемах шла речь?
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог