Переменные: error - эта
надпись выдаётся в синем поле MessageBox error1,2 - сами ошибки filename -
сюда будет занесён offset на имя файла для открытия *Handle - handle
соответствующего объекта будет храниться в соответствующей
переменной memoryOffset - сюда будет занесён offset отображённой
памяти SizeRW - эта переменная нужна для процедур WriteFile/ReadFile, по идее
Microsoft - а её проверкой достигается проверка удачности произведённой
процедуры, нам же она нужна только для корректной работы функции.
.code
_start:
xor
ebx,ebx
push
ebx
call
GetModuleHandle
mov
programHandle,eax
Стандартное начало
программы, обнуление ebx и получения Handle процесса.
call
GetCommandLine
mov
edi,eax
mov
al,20h
mov
ecx,-1
repne
scasb
В этой части программы
мы получаем offset на командную строку(КС) процедурой GetCommandLine(вызывается
без параметров!). После этого еах, указывающий на КС мы помещаем в edi. При этом
нам надо учесть, что первым в КС всегда идёт имя запущенной программы, а потом
уже возможные параметры и т.д. Поэтому мы ищем первый пробел(код 20h)в этой
строке, для этого мы помещаем в al код пробела (20h), а есх максимальное его
значение и производим "сравнение символа пока не равно"(repne scasb), то есть
полученный после этого в edi offset указывает на символ после первого пробела в
строке, то есть на первый параметр в командной строке после имя запущенной
программы.
cmp
byte ptr [edi],0
jne
File_OK
mov
eax,offset error1
jmp
program_error
Здесь мы
проверяем само наличие какого-либо параметра, то есть если следующий байт в
командной строке 0, то параметра нет, если он есть (байт не равен нулю)
происходит переход на метку File_OK, если же параметр не найден, то в еах
задаётся offset на нужную в данном случае ошибку и управление передаётся на
обработчик ошибок программы, program_error
File_OK:
repe
scasb
dec
edi
Этими двумя действиями
мы находим начало первого параметра, они необходимы, если пользователь нажал
лишний пробел или два перед параметром
mov
esi,edi
mov
fileName,edi
Сохраняем
offset названия файла в esi для использования и в переменную, специально
созданную для этого
push
ebx
push
FILE_ATTRIBUTE_ARCHIVE
push
OPEN_EXISTING
push
ebx
push
FILE_SHARE_READ
push
GENERIC_READ
push
esi
call
CreateFile
Стандартная
процедура открытия файла, так как мы встречаемся с ней впервые, давайте
рассмотрим её более подробно, она получает следующие 8 параметров: - Файл с
атрибутами для других процессов, в нашем случае программа быстро совершает все
нужные её действия с файлом и закрывает его, поэтому нет нужды задавать здесь
какие-либо параметры. - Атрибуты файла, единственный реально НУЖНЫЙ атрибут,
это атрибут архивности файла, выставляем только его. - Атрибуты создания
файла, определяет как открывать файл (создать ли его или открыть в любом случае
или только существующий файл), нам нужен существующий файл, поэтому выставляем
этот флаг. - Указатель на структуру атрибутов защиты, пока нам это не нужно,
поэтому будет 0. - Разделяемость файла с другими процессами, в принципе не
особо нужно, но чтоб не быть жадиной лучше разрешить другим процессам читать
этот файл. - Доступ, нужный нам, сейчас мы собираемся лишь прочитать его
содержимое, то есть ставим "просто чтение". - Указатель на имя файла, думаю,
что комментарии не нужны;)
cmp
eax,-1
jne
file_opened_OK
mov
eax,offset error2
jmp
program_error
Проверка на
ошибку.
file_opened_OK:
mov
fileHandle,eax
push
ebx
push
eax
call
GetFileSize
Сохраняем handle
открытого файла и получаем его размер(естественно в еах).
mov
edi,eax
push
eax
push
GMEM_MOVEABLE or GMEM_ZEROINIT
call
GlobalAlloc
В начале
сохраняем размер файла в edi, так как нам понадобится использовать его
достаточно часто. После этого создаём объект памяти, способный вместить в себя
весь файл.
mov
memoryHandle,eax
push
eax
call
GlobalLock
mov
memoryOffset,eax
Сохраняем
его Handle и отображаем в память. После чего сохраняем и offset отображённой
памяти.
push
ebx
push
offset SizeRW
push
edi
push
eax
push
fileHandle
call
ReadFile
push
fileHandle
call
CloseHandle
call
CheckSymbol
В этом куске
программы мы считываем весь файл в созданную память и закрываем файл. После чего
передаём управление нашей процедуре, которая заменяет все "s" на "$" в памяти.
push
ebx
push
FILE_ATTRIBUTE_ARCHIVE
push
TRUNCATE_EXISTING
push
ebx
push
FILE_SHARE_WRITE
push
GENERIC_WRITE
push
fileName
call
CreateFile
Пересоздаём файл,
заданный в командной строке, мы открываем его и удаляем из него всё (в Паскале
это можно записать как rewrite(x)).
push
ebx
push
offset SizeRW
push
edi
push
memoryOffset
push
FileHandle
call
WriteFile
push
fileHandle
call
CloseHandle
Теперь мы
записываем прокорректированное содержание в файл и закрываем его.
program_end:
push
memoryOffset
call
GlobalUnlock
push
memoryHandle
call
GlobalFree
push
ebx
call
exitProcess
Это конец
программы, здесь мы просто закрываем все используемые нами объекты и завершаем
программу.
program_error:
push
MB_IconWarning
push
offset error
push
eax
push
ebx
call
MessageBox
jmp
program_end
На этот кусок
кода передаётся управление если произошла ошибка, тогда просто выводится
сообщение об ошибке и передаётся управление на завершение программы.
CheckSymbol
proc
push
edi
mov
al,'s'
mov
ecx,edi
inc
ecx
mov
edi,memoryOffset
В начале
процедуры мы подготавливаем регистры, здесь стоит заметить, что перед вызовом
процедуры мы сохранили размер файла, что равно размеру сохранённого в памяти, в
edi. Мы помещаем это значение в регистр счётчика (есх), в al символ, который мы
хотим заменить, а в edi offset на память с содержимым файла.
loop_check:
repne
scasb
test
ecx,ecx
jz
end_check
mov
byte ptr [edi-1],'$'
jmp
loop_check
end_check:
Это главная часть
программы, так называемое ядро программы, так как эти 7 строчек выполняю нужную
работу, а всё остальное лишь собирает информацию и передаёт её этой процедуре.
Работа нашего "ядра" происходит по следующему сценарию, программы проверяет
символ за символом на наличие заданного символа до тех пор, пока либо не будет
найден символ, либо не кончится источник, если источник кончился управление
передаётся на метку end_check, то есть происходит завершение цикла, если же
найден символ, то он заменяется на новый и цикл продолжается.