Мне нужно написать программу на ассемблере х64 в VS 2017, Windows 10.
Нужно ввести в окошко строку, а программа должна заменить * на пробелы и вывести
в строку результат.
Пример нашей преподавательницы запускается, но при нажатии на кнопку RUN происходит
исключение по адресу.
Не знаете, в чем проблема? Сама она тоже не знает. Код ниже.
Заранее спасибо.
ExitProcess PROTO
DlgProc PROTO :QWORD,:QWORD, :QWORD, :QWORD
PostQuitMessage PROTO
GetModuleHandleA PROTO
DialogBoxParamA PROTO
GetDlgItemTextA PROTO
SendDlgItemMessageA PROTO
include windows.inc
.data
DName db "dial",0
hinst dq ?
result_buffer db 20 dup ("0")
hwnd dq 0
uMsg dq 0
wParam dq 0
lParam dq 0
.code
main proc
sub rsp, 56
mov rcx,0
call GetModuleHandleA
mov hinst,rax
mov rcx,hinst
lea rdx, DName
mov r8,0
lea r9,DlgProc
mov dword ptr [rsp+32], 0
call DialogBoxParamA
mov rcx,0
call ExitProcess
main endp
DlgProc proc q:QWORD,w:QWORD, e:QWORD, r:QWORD
cmp edx, WM_CLOSE ;10h
je Exit_Button
cmp edx, WM_COMMAND ;111h
je go_button
jne End_it
go_button:
cmp r8,4
je Exit_Button
cmp r8,3
je next
jne End_it
next:
mov hwnd,rcx
mov uMsg,rdx
mov wParam,r8
mov lParam,r9
mov rdx,1
lea r8,result_buffer
mov r9,20
call GetDlgItemTextA
mov rcx, hwnd
mov rdx, uMsg
mov r8, wParam
mov r9, lParam
mov hwnd,rcx
mov rcx,rax
lea rsi,result_buffer
m1:
cmp byte ptr [rsi],'*'
je m2
jne m3
m2:
mov byte ptr [rsi],' '
m3:
inc rsi
loop m1
mov rcx,hwnd
mov rdx,2
mov r8,LB_DELETESTRING ; 182h - LB_DELETESTRING
mov r9,0
lea rsi,result_buffer
mov qword ptr [rsp+32],rsi
call SendDlgItemMessageA
mov rcx, hwnd
mov rdx, uMsg
mov r8, wParam
mov r9, lParam
mov rdx,2
mov r8,LB_ADDSTRING ; 182h - LB_DELETESTRING
mov r9,0
lea rsi,result_buffer
mov qword ptr [rsp+32],rsi
call SendDlgItemMessageA
jmp End_it
Exit_Button:
mov rcx,0
call PostQuitMessage
End_it:
mov rax,0
ret
DlgProc endp
end
Файл RSRC.RC
dial DIALOG 0, 0, 309, 95
CAPTION "mY dialog"
BEGIN
EDITTEXT 1, 48,7,222,14
ListBox 2,48,24,222,14
PUSHBUTTON "Run", 3, 167,41,50,14
PUSHBUTTON "Exit",4, 220,41,50,12
LTEXT "Input string:", -1, 7,10,40,8
END
Ошибка в отладчике:
Ответы
Ответ 1
В коде две проблемы, приводящих к вылетам:
Не проверяется что введена пустая строка (значение, возвращенное из GetDlgItemTextA,
равно нулю), из-за чего при первом попадании на инструкцию loop 0 в регистре rcx превращается
в -1, получаем "бесконечный" цикл и попытку записи за пределы секции данных.
В начале процедуры DlgProc нужно выделить на стеке место под параметры процедуры
SendDlgItemMessageA, при выходе из DlgProc стек восстановить. Из-за того что это не
выполнено, скорее всего при выполнении SendDlgItemMessageA (которое происходит только
при нажатии кнопки "Run") происходит затирание каких-то данных вызывающего кода, при
выходе из процедуры происходит вылет.
Вот рабочий код, адаптированный под fasm, с моими замечаниями:
include 'win64a.inc'
format MS64 COFF
public main
extrn PostQuitMessage
extrn SendDlgItemMessageA
extrn GetModuleHandleA
extrn GetDlgItemTextA
extrn DialogBoxParamA
extrn ExitProcess
section '.data' readable writeable
DName db "dial",0
result_buffer db 20 dup ("0")
section '.code' code readable executable
main:
sub rsp, 5*8 ; место под 5 параметров для вызова DialogBoxParamA
mov rcx,0
call GetModuleHandleA
mov rcx, rax ; [hinst]
mov rdx, DName
mov r8, 0
mov r9, DlgProc
mov dword [rsp+32], 0
call DialogBoxParamA
mov rcx,0
call ExitProcess
proc DlgProc hwnd, uMsg, wParam, lParam
; место под параметры на стеке выделено как раз для того,
; чтобы можно было сохранить там значения тех же параметров из регистров
mov [hwnd], rcx
mov [uMsg], rdx
mov [wParam], r8
mov [lParam], r9
sub rsp, 5*8 ; резервируем на стеке место под 5 параметров для вызова SendDlgItemMessageA
cmp edx, WM_CLOSE ; 10h
je Exit_Button
cmp edx, WM_COMMAND ; 111h
je go_button
jne End_it
go_button:
cmp r8,4
je Exit_Button
cmp r8,3
; je next ; лишняя проверка
jne End_it
; next:
mov rdx,1
mov r8, result_buffer
mov r9,20
call GetDlgItemTextA
; Не нужно постоянно гонять параметры между памятью и регистрами
; mov rcx, hwnd
; mov rdx, uMsg
; mov r8, wParam
; mov r9, lParam
; mov hwnd,rcx
test rax, rax ; не забываем проверить, что введена не пустая строка
jz End_it
mov rcx, rax
mov rsi, result_buffer
m1:
cmp byte [rsi], '*'
; je m2 ; лишняя проверка
jne m3
; m2:
mov byte [rsi], '+' ; заменяю на +, чтобы была заметнее разница
m3:
inc rsi
loop m1
mov rcx, [hwnd]
mov rdx, 2 ; ListBox
mov r8, LB_DELETESTRING ; 182h - LB_DELETESTRING
mov r9, 0
mov qword [rsp+32], result_buffer ; mov qword ptr [rsp+32], offset result_buffer
для masm
call SendDlgItemMessageA
; mov rcx, hwnd
; mov rdx, uMsg
; mov r8, wParam
; mov r9, lParam
mov rcx, [hwnd]
mov rdx,2
mov r8, LB_ADDSTRING
mov r9,0
mov qword [rsp+32], result_buffer
call SendDlgItemMessageA
jmp End_it
Exit_Button:
mov rcx,0
call PostQuitMessage
End_it:
mov rax,0
add rsp, 5*8 ; восстанавливаем стек
ret
endp
Для компиляции нужен fasm, а также GoRC и GoLink (см. The Go tools for
Windows + Assembler, к языку Go не имеют никакого отношения). Для отладки использовал
x64dbg.
Собственно, компиляция:
fasm dialog.asm
gorc /machine X64 rsrc.rc
golink dialog.obj rsrc.res user32.dll kernel32.dll /entry main
Ну и наконец скриншот (состояние после ввода текста и нажатия на кнопку Run):
P.S. "криво расставленные котлы" авторские, rsrc.rc я в неизмененном виде взял из
вопроса.