Страницы

Поиск по вопросам

пятница, 6 марта 2020 г.

Исключение по адресу в программе на ассемблере

#ассемблер #исключения


Мне нужно написать программу на ассемблере х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 я в неизмененном виде взял из вопроса.

Комментариев нет:

Отправить комментарий