Создание, открытие и закрытие файла

Последнее обновление: 03.07.2023

Для создания файла в WinAPI можно использовать функцию CreateFileA. Она имеет следующий прототип на C++:

HANDLE CreateFileA(
  [in]           LPCSTR                lpFileName,
  [in]           DWORD                 dwDesiredAccess,
  [in]           DWORD                 dwShareMode,
  [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  [in]           DWORD                 dwCreationDisposition,
  [in]           DWORD                 dwFlagsAndAttributes,
  [in, optional] HANDLE                hTemplateFile
);

Она принимает следующие параметры:

  • lpFileName: строка - имя файла для открытия, передается через регистр RCX

  • dwDesiredAccess: представляет 32-битное число и задает режим доступа (открытие на чтение, запись или на то и другое), передается через регистр RDX

    Для создания и открытия файла на запись, устанавливаливается 30-й бит числа, а для чтения - 31-й бит. Примеры:

    • Открытие для записи: 040000000h

    • Открытие для чтения: 080000000h

    • Открытие для записи и чтения: 0C0000000h

    • Открытие для записи и дозаписи (если файл существует): 080000004h

  • dwShareMode: режим общего доступа, передается через регистр R8

  • lpSecurityAttributes: указатель на атрибуты безопасности, передается через регистр R9

  • dwCreationDisposition: флаг, который указывает, надо ли создавать новый файл или открывать уже существующий. Передается через стек по адресу [rsp + 32]

    Возможные значения:

    • 1: создавать новый, если только файл не существует, иначе открыть существующий

    • 2: всегда создавать заново, даже если файл с таким именем существует

    • 3: открыть существующий файл

    • 4: всегда открывать файл. Если он не существует, то он создается

  • dwFlagsAndAttributes: атрибуты файла, передается через стек по адресу [rsp + 40]. В частности, для создания обычного файла без атрибутов пережается число 128.

  • hTemplateFile: шаблон дескриптора файла, передается через стек по адресу [rsp + 48]

После выполнения функция ReadFile помещает в регистр RAX ненулевое значение, если запись прошла успешно, и ноль, если произошла ошибка.

Полное описание функции можно прочитать в документации.

К примеру создадим в текущей папке файл "test.txt":

includelib kernel32.lib   ; подключаем библиотеку kernel32.lib

; подключаем функцию CreateFileA
extrn CreateFileA: PROC
.data
filename byte "test.txt",0    ; имя файла

.code
main proc
  sub rsp, 56
  
  lea rcx, filename ; имя файла
  mov rdx, 0C0000000h ; доступ на запись и чтение 1100 0000 0000 0000 0000 0000 0000 0000
  xor r8, r8 ; Эксклюзивный доступ
  xor r9, r9 ; атрибуты безопасности отсутствуют
  mov r10, 2 ; Всегда создавать новый файл
  mov [rsp + 32], r10
  mov r10, 128  ; обычный файл без атрибутов
  mov [rsp + 40], r10 
  mov [rsp + 48], r9    ; NULL - шаблон не используется
  call CreateFileA
  add rsp, 56
  ret
main endp
end

В данном случае создаваемый файл будет называться "test.txt". Он будет доступен для чтения и для записи. Для чтения надо установить 31-й бит 32-разрядного числа, а для записи - 30-й бит, поэтому в регистр RDX помещаем число 1100 0000 0000 0000 0000 0000 0000 0000 или 0C0000000h в шестнадцатеричной системе.

Общий доступ к файлу нам не нужен, поэтому в регистр R8 помещаем число 0. Атрибуты безопасности в данном случае тоже не нужны, поэтому в регистр R9 также помещается число 0.

Чтобы файл создавался всегда, в стек по адресу [rsp + 32] помещаем число 2.

Файл будет создаваться обычный без каких-то атрибутов, поэтому по адресу [rsp + 40] в стек помещаем число 128.

Шаблон дескриптора файла тоже не важен, поэтому по адресу [rsp + 48] в стек помещается число 0.

После вызова функции CreateFileA будет создан файл "test.txt", а в регистр RAX при успешном открытии файла будет помещен дескриптор этого файла. В дальнейшем, используя данный дескриптор, мы можем записывать или считывать файл. Если же в процессе открытия произошла ошибка, то в RAX помещается число -1.

Если у нас уже есть файл, который мы хотим открыть для операций чтения-записи, то в стек по адресу [rsp + 32] передается число 3.

Закрытие файла

Для закрытия файла применяется функция CloseHandle со следуюшим прототипом на языке C++:

BOOL CloseHandle(
  [in] HANDLE hObject
);

В качестве параметра ей передается через регистр RCX дескриптор закрываемого файла. Результатом функции является ненулевое значение, если закрытие завершилось удачно, и 0, если произошла ошибка. Например:

includelib kernel32.lib   ; подключаем библиотеку kernel32.lib

extrn CreateFileA: proc
extrn CloseHandle: proc
.data
filename byte "test.txt",0    ; имя файла
.code
; Процедура открытия существующего файла
; Параметр - в RSI - имя файла
; Результат - в RAX - дескриптор открытого файла
openFile proc
  sub rsp, 56
  mov rcx, rsi ; имя файла
  mov rdx, 0C0000000h ; доступ на запись и чтение
  xor r8, r8 ; Эксклюзивный доступ
  xor r9, r9 ; атрибуты безопасности отсутствуют
  mov r10, 3 ; открыть существующий файл
  mov [rsp + 32], r10
  mov r10, 128  ; обычный файл без атрибутов
  mov [rsp + 40], r10 
  mov [rsp + 48], r9    ; NULL - шаблон не используется
  call CreateFileA

  add rsp, 56
  ret
openFile endp

; Процедура закрытия существующего файла
; Параметр - в RAX - дескриптор открытого файла
; Результат - в RAX - состояние операции (ошибка или нет)
closeFile proc
  sub rsp, 40
  mov rcx, rax      ; В RCX помещаем дескриптор файла
  call CloseHandle
  add rsp, 40
  ret
closeFile endp

main proc
  lea rsi, filename
  call openFile   ; открываем файл

  call closeFile  ; закрываем файл
  ret
main endp
end

Здесь открытие файла вынесено в процедуру openFile, а закрытие - в closeFile. Перед вызовом closeFile предполагается, что в регистре RAX содержится дескриптор файла, полученный после вызова openFile.

Получение ошибки

В процессе работы с файлами могут возникать ошибки. Чтобы идентифицировать их WinAPI предоставляет функцию GetLastError:

DWORD GetLastError();

Данная функция не принимает параметров, лишь возвращает код ошибки. Применим эту функцию:

includelib kernel32.lib   ; подключаем библиотеку kernel32.lib

extrn CreateFileA: proc
extrn CloseHandle: proc
extrn GetLastError: proc
.data
filename byte "test.txt",0    ; имя файла
.code
; Процедура открытия существующего файла
; Параметр - в RSI - имя файла
; Результат - в RAX - дескриптор открытого файла
openFile proc
  sub rsp, 56
  mov rcx, rsi ; имя файла
  mov rdx, 0C0000000h ; доступ на запись и чтение
  xor r8, r8 ; Эксклюзивный доступ
  xor r9, r9 ; атрибуты безопасности отсутствуют
  mov r10, 3 ; открыть существующий файл
  mov [rsp + 32], r10
  mov r10, 128  ; обычный файл без атрибутов
  mov [rsp + 40], r10 
  mov [rsp + 48], r9    ; NULL - шаблон не используется
  call CreateFileA

  add rsp, 56
  ret
openFile endp

; Процедура закрытия существующего файла
; Параметр - в RAX - дескриптор открытого файла
; Результат - в RAX - состояние операции (ошибка или нет)
closeFile proc
  sub rsp, 40
  mov rcx, rax
  call CloseHandle
  add rsp, 40
  ret
closeFile endp

; Процедура получения последней ошибки Windows
; Результат - в RAX код ошибки
getError proc
  sub rsp, 40
  call GetLastError
  add rsp, 40
  ret
getError endp

main proc
  lea rsi, filename
  call openFile   ; открываем файл
  
  mov rax, 0   ; помещаем в RAX некорректный дескриптор файла

  call closeFile  ; закрываем файл
  call getError ; получаем последнюю ошибку
  ret
main endp
end

Для имитации ошибки в процедуру closeFile передается некорректный дескриптор файла. Для получения ошибки после закрытия файла вызывается процедура getError, в которой получаем код ошибки. После выполнения программы с помощью команды echo %ERRORLEVEL% можно получить код ошибки, который хранится в RAX:

c:\asm>hello.exe

c:\asm>echo %ERRORLEVEL%
6

c:\asm>

Здесь видно, что код ошибки - 6. Что это значит? Мы можем обратиться к документации, в частности, свериться по списку ошибок - https://learn.microsoft.com/ru-ru/windows/win32/debug/system-error-codes--0-499-. И мы увидим, что 6 - это код ошибки "ERROR_INVALID_HANDLE", то есть передан корректный дескриптор файла.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850