Прога в отладчике работает, а без него - нет :(

Тема в разделе "WASM.BEGINNERS", создана пользователем _qwerty_, 24 фев 2007.

  1. _qwerty_

    _qwerty_ New Member

    Публикаций:
    0
    Регистрация:
    24 фев 2007
    Сообщения:
    17
    Привет.
    Программа должна переводить десятичное число (переданное как указатель на строку) в dword и возвратить в EAX результат. Если запускать ее из винды - она вылетает с ошибкой "инструкция по адресу ........ память не может быть written". А в отладчике работает. Может кто объяснит - почему? :)

    Код (Text):
    1. format PE GUI 4.0
    2. include '%fasminc%\win32a.inc'
    3. entry start
    4.  
    5. section '.code' code readable writeable executable
    6. start:
    7.  
    8.  
    9.     push            szzz
    10.     call            sz2dd
    11.  
    12.     invoke      ExitProcess, 0
    13.  
    14.  
    15. szzz        db  '12345', 0
    16.  
    17.  
    18.  
    19. ; Процедура переводит строку, завершенную 0x00, в double word
    20. ; результат - в EAX
    21. proc        sz2dd, param
    22.     push        ebx
    23.     push        edi
    24.     push        esi
    25.  
    26.     xor     eax, eax
    27.     xor     ebx, ebx
    28.     xor     edi, edi
    29.     inc     edi         ; множитель. сначала он равен 1
    30.  
    31.     mov     esi, [param]
    32.  
    33.     ; движемся по строке, до тех пор, пока не дойдем жо конца строки [т.е. пока не найдем символ 0x00]
    34.     stc
    35. @@:
    36.     lodsb               ; читаем символ
    37.     test        al, al      ; проверка на ноль [0x00]
    38.     jnz     @b          ; если не ноль, то повторяем
    39.  
    40.     dec     esi         ; уменьшим esi, что бы он указывал на последний (не считая ноль) символ строки
    41.  
    42.     ; теперь будем двигаться в обратном направлении, обрабатывая строку
    43.     std
    44. @@:
    45.     cmp     esi, [param]    ; сравниваем текущее значение ESI с адресом начала строки
    46.     jl      @f          ; если esi меньше, то значит что вся строка обработана и на выход
    47.     xor     eax, eax        ; обнуляем eax, т.к. он мог похериться при рассчете множителя
    48.     lodsb               ; читаем символ
    49.     test        al, al      ; проверяем, а не равен ли он 0x00
    50.     jz      @b          ; и если да, то идем на начало
    51.     sub     al, '0'     ; иначе, вычтем из AL код нуля и получим в AL текущую цифру
    52.     mul     edi         ; eax = edi * al. типа множитель - сначала 1*al, затем 10*al, затем 100*al и т.д.
    53.     add     ebx, eax        ; в EBX - сумма
    54.     mov     eax, 10d        ; <
    55.     mul     edi         ; < - увеличение множителя - 10, 100, 1000 и т.д.
    56.     mov     edi, eax        ; <
    57.     jmp     @b          ; идем обрабатывать следующий символ
    58.  
    59. @@:
    60.     mov     eax, ebx        ; помещаем результат в EAX и выходим
    61.  
    62.     pop     esi
    63.     pop     edi
    64.     pop     ebx
    65.     ret
    66. endp
    67.  
    68. data import
    69.     library kernel32, 'kernel32.dll'
    70.     include '%fasminc%\api\kernel32.inc'
    71. end data
     
  2. RamMerLabs

    RamMerLabs Well-Known Member

    Публикаций:
    0
    Регистрация:
    11 сен 2006
    Сообщения:
    1.426
    В MASM lib есть:
    Код (Text):
    1. udw2str proc dwNumber:DWORD, pszString:DWORD
    2.     push ebx
    3.     push esi
    4.     push edi
    5.     mov     eax, [dwNumber]
    6.     mov     esi, [pszString]
    7.     mov     edi, [pszString]
    8.     mov ecx,429496730
    9.   @@redo:
    10.     mov ebx,eax
    11.     mul ecx
    12.     mov eax,edx
    13.     lea edx,[edx*4+edx]
    14.     add edx,edx
    15.     sub ebx,edx
    16.     add bl,'0'
    17.     mov [esi],bl
    18.     inc esi
    19.     test    eax, eax
    20.     jnz     @@redo
    21.     jmp     @@chks
    22.   @@invs:
    23.     dec     esi
    24.     mov     al, [edi]
    25.     xchg    [esi], al
    26.     mov     [edi], al
    27.     inc     edi
    28.   @@chks:
    29.     cmp     edi, esi
    30.     jb      @@invs
    31.     pop edi
    32.     pop esi
    33.     pop ebx
    34.     ret
    35. udw2str endp
    или

    Код (Text):
    1. dwtoa proc dwValue:DWORD, lpBuffer:DWORD
    2. ; convert DWORD to ascii string
    3. ; dwValue is value to be converted
    4. ; lpBuffer is the address of the receiving buffer
    5.     push ebx
    6.     push esi
    7.     push edi
    8.     mov eax, dwValue
    9.     mov edi, [lpBuffer]
    10.     or eax,eax
    11.     jnz sign
    12.   zero:
    13.     mov word ptr [edi],30h
    14.     jmp dw2asc
    15.   sign:
    16.     jns pos
    17.     mov byte ptr [edi],'-'
    18.     neg eax
    19.     inc edi
    20.   pos:      
    21.     mov ecx,429496730
    22.     mov esi, edi
    23.     .while (eax > 0)
    24.       mov ebx,eax
    25.       mul ecx
    26.       mov eax,edx
    27.       lea edx,[edx*4+edx]
    28.       add edx,edx
    29.       sub ebx,edx
    30.       add bl,'0'
    31.       mov [edi],bl
    32.       inc edi
    33.     .endw
    34.     mov byte ptr [edi], 0       ; terminate the string
    35.     .while (esi < edi)
    36.       dec edi
    37.       mov al, [esi]
    38.       mov ah, [edi]
    39.       mov [edi], al
    40.       mov [esi], ah
    41.       inc esi
    42.     .endw
    43.     dw2asc:
    44.     pop edi
    45.     pop esi
    46.     pop ebx
    47.     ret
    48. dwtoa endp
     
  3. _qwerty_

    _qwerty_ New Member

    Публикаций:
    0
    Регистрация:
    24 фев 2007
    Сообщения:
    17
    Да знаю я про них, хотя все равно спасибо, но мне интересно, почему в отладчике работает, а без него - нет. Тем более, что все проверки на границы строки есть...
     
  4. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Код (Text):
    1. proc        sz2dd, param
    2.     push        ebx
    3.     push        edi
    4.     push        esi
    5. ....
    6.     pop     esi
    7.     pop     edi
    8.     pop     ebx
    9.     ret
    10. endp
    возможно, стоит завершить подпрограмму как LEAVE/RET, а не просто RET?
    Эта байда ассебмлируется в:
    Код (Text):
    1. 00401018  /$  55            PUSH EBP
    2. 00401019  |.  89E5          MOV EBP, ESP
    3. 0040101B  |.  53            PUSH EBX
    4. 0040101C  |.  57            PUSH EDI
    5. 0040101D  |.  56            PUSH ESI
    6. ...
    7. 0040104F  |.  5E            POP ESI
    8. 00401050  |.  5F            POP EDI
    9. 00401051  |.  5B            POP EBX
    10. 00401052  \.  C3            RET
    Как видно, в начале стандартный пролог - PUSH EBP / MOV EBP,ESP,заботливо сгенерированный фасмом.
    На выходе у тебя в стеке лежит EBP, сохранненный через PUSH EBP вначале, а потом ты делаешь RET и программа прыгает вникуда (точнее, в стек).
    В стеке по адресу EBP оказываются нули, которые интерпретируются как команды ADD [EAX],...., а в EAX лежит неверный адрес (12345 или 0x3039 - результат работы подпрограммы). Возникает исключение, потому что записать по этому адресу нельзя. Результат - назойливый месажбокс the memory could not be written.

    А ошибка кроется в неверном выходе из подпрограммы.
    Повторюсь еще раз, не RET, а LEAVE / RET.
     
  5. _qwerty_

    _qwerty_ New Member

    Публикаций:
    0
    Регистрация:
    24 фев 2007
    Сообщения:
    17
    > Эта байда ассебмлируется в:
    Хм. А у меня:
    Код (Text):
    1. 0040102C  /$ 55             PUSH EBP
    2. 0040102D  |. 89E5           MOV EBP,ESP
    3. 0040102F  |. 53             PUSH EBX
    4. 00401030  |. 57             PUSH EDI
    5. 00401031  |. 56             PUSH ESI
    6. ...
    7. 00401063  |. 5E             POP ESI
    8. 00401064  |. 5F             POP EDI
    9. 00401065  |. 5B             POP EBX
    10. 00401066  |. C9             LEAVE
    11. 00401067  \. C2 0400        RETN 4
    Ну и в отладчике стэк после выполнения процедуры выглядит вполне нормально...
    И еще - ради интереса вставил после call sz2dd - invoke MessageBox. Вроде все нормально - параметры в стэке, все дела, но после call'а на MsgBox программа тихо дохнет где-то в недрах ntdll:
    7C903105 F3:AB REP STOS DWORD PTR ES:[EDI]
    7C903107 5F POP EDI
    7C903108 C2 0C00 RETN 0C
    "Access violation when writing to [0008FFFC]" :dntknw:((
    Вернее, после нажатия Shift+F9, она выполняется и даже доходит до ExitProcess, но все равно фигня какая-то =(
     
  6. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    Те не восстанавливаешь флаг направления цепочечных команд перед возвратом из sz2dd -- это одно из требований Win. Добавь после 'mov eax, ebx' 'cld', будет все Ок. Не совсем понял, зачем тебе нужна 'stc' после 'mov esi, [param]?.. В OllyDbg, кстати, ошибка никуда не исчезает (возможно, она не исчезала и в твоем отладчике, просто исключение происходило уже в ExitProcess?)
     
  7. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    _qwerty_
    Т.к. в коде программы нет никаких операций записи, баг возникает где-то в другом месте. Таким местом может быть импорт. Попробуйте переместить его в начало секции кода, попробуйте поместить его в секцию с атрибутом writeable (теоретически загрузчику должно быть наплевать на атрибуты секции, но практически это не совсем так), попробуйте обновить фасм вместе с макросами.
     
  8. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Quantum
    я уже написал, почему возникает ошибка с записью, потому что я сам ее скомпилировал и отладил. дисбаланс стека ведет за собой RET на неправильный адрес, где случайно оказывается команда записи.
     
  9. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    Great
    ret - это макрос в данном случае. В частности из-за такой неоднозначности я не люблю макросы, особенно фасмовые.
     
  10. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Quantum
    а ты глянь отладчиком и увидишь, что это никакой не макрос, а команда. не веришь - убедись сам, LEAVE автоматом не генерируется
    хотя у него генерируется) жесть
     
  11. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Эти "недра" ntdll - это RtlDispatchException. Реально она дохнет после RET-а, я уже писал раза 3 наверное.

    MsgBox требует как и все апишки выровненного стека. проверь

    зы. странно, а у меня leave нету.
     
  12. _qwerty_

    _qwerty_ New Member

    Публикаций:
    0
    Регистрация:
    24 фев 2007
    Сообщения:
    17
    Mika0x65

    > Те не восстанавливаешь флаг направления цепочечных команд перед возвратом из sz2dd -- это одно из требований Win.
    Спасибо! Заработало! :)

    > зачем тебе нужна 'stc'
    Да, не нужна =)

    Quantum
    Спасибо. Все это я пробовал.
     
  13. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    Great
    Зачем мне куда-то ещё смотреть, если топикстартер уже привёл код, который у него генерируется?

    У Вас не генерируется, а у других генерируется :) Обяснить причину?

    _qwerty_
    Мда, странно, что ExitProcess'у нужен этот флаг, хотя логично. Учтём-с.
     
  14. Asterix

    Asterix New Member

    Публикаций:
    0
    Регистрация:
    25 фев 2003
    Сообщения:
    3.576
    MessageBox тоже не любит если кто-то по неряшливости или незнанию забыл восстановить флаг
    направления на дефолтное значение
     
  15. _qwerty_

    _qwerty_ New Member

    Публикаций:
    0
    Регистрация:
    24 фев 2007
    Сообщения:
    17
    Great
    > LEAVE автоматом не генерируется
    если ret - команда, то leave не генерируется =)
    а вот если ret - макрос, то может и генерироваться. См. в %fasminc%\macro\

    вот если я напишу: "retn 4", то никакого leave не будет,
    а если напишу "ret", то макрос заменит это на "leave / retn 4". Если, конечно, до этого был вызов макроса proc, создающий стэковый кадр.

    Quantum
    > Мда, странно, что ExitProcess'у нужен этот флаг, хотя логично. Учтём-с.
    И MessageBox'у. А у Iczelion'а я про этот флаг не видел - там только про регистры, используемые виндой =(
     
  16. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    прихоти ) как и выравнивание стека.