1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
| .nolist
option dotname
option casemap: none
include \masm64\Include64\win64.inc
include \masm64\Include64\kernel32.inc
include \masm64\Include64\user32.inc
include \masm64\Include64\msvcrt.inc
includelib \masm64\Lib64\kernel32.lib
includelib \masm64\Lib64\user32.lib
includelib \masm64\Lib64\msvcrt.lib
option prologue: none
option epilogue: none
.list
.data
FILETIME struc
dwLowDateTime DWORD ?
dwHighDateTime DWORD ?
FILETIME ends
SYSTEMTIME struc
wYear WORD ?
wMonth WORD ?
wDayOfWeek WORD ?
wDay WORD ?
wHour WORD ?
wMinute WORD ?
wSecond WORD ?
wMilliseconds WORD ?
SYSTEMTIME ends
;строки идентификации сообщений об ошибке
szFail db "CreateFile", 0
;строки форматирования результатов работы программы - времени создания файла
szFmtDateTime db "%02d/%02d/%d %02d:%02d", 0
szFmtLastWrite db "Last write time is: %s", 0Dh, 0Ah, 0
;имя обрабатываемого файла
szFileName db "hello.asm", 0
.data?
szBuffer db MAX_PATH dup(?)
hFile HANDLE ?
.code
;-------------------------------------------------------------------------
; Всплывающее окно с сообщением об ошибке.
; В окно выводится:
; 1. сообщение, передаваемое в функцию,
; 2. текст "failed with error",
; 3. код ошибки,
; 4. расшифровка кода ошибки в текстовом виде
; Код процедуры взят с небольшими изменениями из примера
; https://docs.microsoft.com/ru-ru/windows/win32/fileio/listing-the-files-in-a-directory
; Изменения:
; 1. функция StringCchPrintf заменена на wsprintf, т.к. StringCchPrintf отсутствует
; в библиотеках masm32, а кроме того, она только описывается в strsafe.h и, вероятно,
; является частью библиотек Visual Studio.
; 2. в DisplayErrorBox переменная dw переименована в LastError.
;-------------------------------------------------------------------------
.data
szErrorMessage db "%s failed with error %d: %s", 0
szMBError db "Error", 0
.code
DisplayErrorBox proc lpszFunction:LPVOID
local lpMsgBuf :LPVOID
local lpDisplayBuf :LPVOID
local LastError :QWORD
;т.к. в опциях задано отсутствие эпилога и пролога,
;то вычисления выполняем в коде
;вычисляем размер фрейма стека - он состоит из
; - локальных переменных (округляем до 8 байт)
LocalsSize = ((2*(SIZEOF LPVOID)+1*(SIZEOF QWORD)+7)/8)*8
; - фрейма для вызываемых функций (максимальное число параметров у функции FormatMessage - 7 шт.)
;размер округляем до 16, т.к. перед вызовом стек был выровнен на границу 16, потом в стек
;поместили 8 байт адреса возврата, а при формировании фрейма будет помещён rbp (8 байт)
FrameSize = (((LocalsSize+7*8)+15)/16)*16
;и самостоятельно формируем фрейм
push rbp
mov rbp, rsp
sub rsp, FrameSize
;сохраняем контекст в месте, предоставленном вызывающим кодом
mov [rbp+10h], rcx
mov [rbp+18h], rdx
mov [rbp+20h], r8
mov [rbp+28h], r9
;Retrieve the system error message for the last-error code
call GetLastError
mov LastError, rax
comment*
-------------------------------------------------------------------------------------------------------
invoke FormatMessage,\
FORMAT_MESSAGE_ALLOCATE_BUFFER OR FORMAT_MESSAGE_FROM_SYSTEM OR FORMAT_MESSAGE_IGNORE_INSERTS,\
NULL,\
LastError,\
LANG_NEUTRAL OR (SUBLANG_DEFAULT SHL 10),\
ADDR lpMsgBuf,\
0,\
NULL
-------------------------------------------------------------------------------------------------------
*
mov rcx, FORMAT_MESSAGE_ALLOCATE_BUFFER OR FORMAT_MESSAGE_FROM_SYSTEM OR FORMAT_MESSAGE_IGNORE_INSERTS
xor edx, edx
mov r8, rax
mov r9d, LANG_NEUTRAL OR (SUBLANG_DEFAULT SHL 10)
lea rax, lpMsgBuf
mov [rsp+20h], rax
xor eax, eax
mov [rsp+28h], rax
mov [rsp+30h], rax
call FormatMessage
;Display the error message and clean up
;lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
; (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR));
mov rbx, rax ;rbx=lstrlen((LPCTSTR)lpMsgBuf)
lea ecx, lpszFunction
call lstrlen
add rbx, rax
add rbx, 40
mov ecx, LMEM_ZEROINIT
mov edx, ebx
call LocalAlloc
mov lpDisplayBuf, rax
;
mov rcx, lpDisplayBuf
lea rdx, [szErrorMessage]
mov r8, lpszFunction
mov r9, LastError
mov rax, lpMsgBuf
mov [rsp+20h], rax
call wsprintf
;invoke MessageBox, NULL, lpDisplayBuf, ADDR szMBError, MB_OK
xor ecx, ecx
mov rdx, lpDisplayBuf
xor r8, r8 ;lea r8, szMBError
mov r9d, MB_OK
call MessageBox
mov rcx, lpMsgBuf
call LocalFree
mov rcx, lpDisplayBuf
call LocalFree
add rsp, FrameSize
pop rbp
ret
DisplayErrorBox endp
;// GetLastWriteTime - Retrieves the last-write time and converts
;// the time to a string
;//
;// Return value - TRUE if successful, FALSE otherwise
;// @hFile - Valid file handle
;// lpszString - Pointer to buffer to receive string
GetLastWriteTime proc @hFile:HANDLE, lpszString:LPTSTR, dwSize:DWORD
LOCAL ftCreate: FILETIME
LOCAL ftAccess: FILETIME
LOCAL ftWrite: FILETIME
LOCAL stUTC: SYSTEMTIME
LOCAL stLocal: SYSTEMTIME
LOCAL dwRet: QWORD
;т.к. в опциях задано отсутствие эпилога и пролога,
;то вычисления выполняем в коде
;вычисляем размер фрейма стека - он состоит из
; - локальных переменных (округляем до 8 байт)
LocalsSize = ((3*(SIZEOF FILETIME)+2*(SIZEOF SYSTEMTIME)+1*(SIZEOF QWORD)+7)/8)*8
; - фрейма для вызываемых функций (максимальное число параметров у функции FormatMessage - 7 шт.)
;размер округляем до 16, т.к. перед вызовом стек был выровнен на границу 16, потом в стек
;поместили 8 байт адреса возврата, а при формировании фрейма будет помещён rbp (8 байт)
FrameSize = (((LocalsSize+7*8)+15)/16)*16
;и самостоятельно формируем фрейм
push rbp
mov rbp, rsp
sub rsp, FrameSize
;сохраняем контекст в месте, предоставленном вызывающим кодом
mov [rbp+10h], rcx
mov [rbp+18h], rdx
mov [rbp+20h], r8
mov [rbp+28h], r9
;// Retrieve the file times for the file.
;invoke GetFileTime, @hFile, ADDR ftCreate, ADDR ftAccess, ADDR ftWrite
mov rcx, @hFile
lea rdx, ftCreate
lea r8, ftAccess
lea r9, ftWrite
call GetFileTime
test rax, rax
jz @@Exit
;// Convert the last-write time to local time.
;invoke FileTimeToSystemTime, ADDR ftWrite, ADDR stUTC
lea rcx, ftWrite
lea rdx, stUTC
call FileTimeToSystemTime
;invoke SystemTimeToTzSpecificLocalTime, NULL, ADDR stUTC, ADDR stLocal
xor ecx, ecx
lea rdx, stUTC
lea r8, stLocal
call SystemTimeToTzSpecificLocalTime
;// Build a string showing the date and time.
;invoke wsprintf, lpszString, ADDR szFmtDateTime,\
; stLocal.wMonth, stLocal.wDay, stLocal.wYear,\
; stLocal.wHour, stLocal.wMinute
mov rcx, lpszString
lea rdx, szFmtDateTime
movzx r8, stLocal.wDay
movzx r9, stLocal.wMonth
movzx rax, stLocal.wYear
mov [rsp+20h], rax
movzx rax, stLocal.wHour
mov [rsp+28h], rax
movzx rax, stLocal.wMinute
mov [rsp+30h], rax
xor eax, eax
call wsprintf
@@Exit:
add rsp, FrameSize
pop rbp
ret
GetLastWriteTime endp
WinMain proc
;формирование фрейма стека на весь код основной программы
;с учётом максимального количества параметров среди вызываемых функций
sub rsp, 48h
mov rbp, rsp
;открытие файла на чтение
;invoke CreateFile, ADDR szFileName, GENERIC_READ, FILE_SHARE_READ, NULL,\
; OPEN_EXISTING, 0, NULL
lea rcx, szFileName
mov edx, GENERIC_READ
mov r8d, FILE_SHARE_READ
xor r9d, r9d
mov eax, OPEN_EXISTING
mov [rsp+20h], rax
xor eax, eax
mov [rsp+28h], rax
mov [rsp+30h], rax
call CreateFile
mov [hFile], rax
;проверка отсутствия ошибок при открытии файла
cmp rax, INVALID_HANDLE_VALUE
jne @f
;вывод диагностического сообщения об ошибке и завершение программы
;invoke DisplayErrorBox, ADDR szFail
lea rcx, szFail
call DisplayErrorBox
jmp @@Exit
@@:
;вывод времени последней записи в файл
;invoke GetLastWriteTime, hFile, ADDR szBuffer, MAX_PATH
mov rcx, hFile
lea rdx, szBuffer
mov r8d, MAX_PATH
call GetLastWriteTime
;invoke __imp_printf, ADDR szFmtLastWrite, ADDR szBuffer
lea rcx, szFmtLastWrite
lea rdx, szBuffer
xor eax, eax
call __imp_printf
;завершение программы
call __imp__getch
@@Exit:
xor ecx, ecx
call ExitProcess
WinMain endp
end |