Привет. Программа должна переводить десятичное число (переданное как указатель на строку) в dword и возвратить в EAX результат. Если запускать ее из винды - она вылетает с ошибкой "инструкция по адресу ........ память не может быть written". А в отладчике работает. Может кто объяснит - почему? Код (Text): format PE GUI 4.0 include '%fasminc%\win32a.inc' entry start section '.code' code readable writeable executable start: push szzz call sz2dd invoke ExitProcess, 0 szzz db '12345', 0 ; Процедура переводит строку, завершенную 0x00, в double word ; результат - в EAX proc sz2dd, param push ebx push edi push esi xor eax, eax xor ebx, ebx xor edi, edi inc edi ; множитель. сначала он равен 1 mov esi, [param] ; движемся по строке, до тех пор, пока не дойдем жо конца строки [т.е. пока не найдем символ 0x00] stc @@: lodsb ; читаем символ test al, al ; проверка на ноль [0x00] jnz @b ; если не ноль, то повторяем dec esi ; уменьшим esi, что бы он указывал на последний (не считая ноль) символ строки ; теперь будем двигаться в обратном направлении, обрабатывая строку std @@: cmp esi, [param] ; сравниваем текущее значение ESI с адресом начала строки jl @f ; если esi меньше, то значит что вся строка обработана и на выход xor eax, eax ; обнуляем eax, т.к. он мог похериться при рассчете множителя lodsb ; читаем символ test al, al ; проверяем, а не равен ли он 0x00 jz @b ; и если да, то идем на начало sub al, '0' ; иначе, вычтем из AL код нуля и получим в AL текущую цифру mul edi ; eax = edi * al. типа множитель - сначала 1*al, затем 10*al, затем 100*al и т.д. add ebx, eax ; в EBX - сумма mov eax, 10d ; < mul edi ; < - увеличение множителя - 10, 100, 1000 и т.д. mov edi, eax ; < jmp @b ; идем обрабатывать следующий символ @@: mov eax, ebx ; помещаем результат в EAX и выходим pop esi pop edi pop ebx ret endp data import library kernel32, 'kernel32.dll' include '%fasminc%\api\kernel32.inc' end data
В MASM lib есть: Код (Text): udw2str proc dwNumber:DWORD, pszString:DWORD push ebx push esi push edi mov eax, [dwNumber] mov esi, [pszString] mov edi, [pszString] mov ecx,429496730 @@redo: mov ebx,eax mul ecx mov eax,edx lea edx,[edx*4+edx] add edx,edx sub ebx,edx add bl,'0' mov [esi],bl inc esi test eax, eax jnz @@redo jmp @@chks @@invs: dec esi mov al, [edi] xchg [esi], al mov [edi], al inc edi @@chks: cmp edi, esi jb @@invs pop edi pop esi pop ebx ret udw2str endp или Код (Text): dwtoa proc dwValue:DWORD, lpBuffer:DWORD ; convert DWORD to ascii string ; dwValue is value to be converted ; lpBuffer is the address of the receiving buffer push ebx push esi push edi mov eax, dwValue mov edi, [lpBuffer] or eax,eax jnz sign zero: mov word ptr [edi],30h jmp dw2asc sign: jns pos mov byte ptr [edi],'-' neg eax inc edi pos: mov ecx,429496730 mov esi, edi .while (eax > 0) mov ebx,eax mul ecx mov eax,edx lea edx,[edx*4+edx] add edx,edx sub ebx,edx add bl,'0' mov [edi],bl inc edi .endw mov byte ptr [edi], 0 ; terminate the string .while (esi < edi) dec edi mov al, [esi] mov ah, [edi] mov [edi], al mov [esi], ah inc esi .endw dw2asc: pop edi pop esi pop ebx ret dwtoa endp
Да знаю я про них, хотя все равно спасибо, но мне интересно, почему в отладчике работает, а без него - нет. Тем более, что все проверки на границы строки есть...
Код (Text): proc sz2dd, param push ebx push edi push esi .... pop esi pop edi pop ebx ret endp возможно, стоит завершить подпрограмму как LEAVE/RET, а не просто RET? Эта байда ассебмлируется в: Код (Text): 00401018 /$ 55 PUSH EBP 00401019 |. 89E5 MOV EBP, ESP 0040101B |. 53 PUSH EBX 0040101C |. 57 PUSH EDI 0040101D |. 56 PUSH ESI ... 0040104F |. 5E POP ESI 00401050 |. 5F POP EDI 00401051 |. 5B POP EBX 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.
> Эта байда ассебмлируется в: Хм. А у меня: Код (Text): 0040102C /$ 55 PUSH EBP 0040102D |. 89E5 MOV EBP,ESP 0040102F |. 53 PUSH EBX 00401030 |. 57 PUSH EDI 00401031 |. 56 PUSH ESI ... 00401063 |. 5E POP ESI 00401064 |. 5F POP EDI 00401065 |. 5B POP EBX 00401066 |. C9 LEAVE 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]" (( Вернее, после нажатия Shift+F9, она выполняется и даже доходит до ExitProcess, но все равно фигня какая-то =(
Те не восстанавливаешь флаг направления цепочечных команд перед возвратом из sz2dd -- это одно из требований Win. Добавь после 'mov eax, ebx' 'cld', будет все Ок. Не совсем понял, зачем тебе нужна 'stc' после 'mov esi, [param]?.. В OllyDbg, кстати, ошибка никуда не исчезает (возможно, она не исчезала и в твоем отладчике, просто исключение происходило уже в ExitProcess?)
_qwerty_ Т.к. в коде программы нет никаких операций записи, баг возникает где-то в другом месте. Таким местом может быть импорт. Попробуйте переместить его в начало секции кода, попробуйте поместить его в секцию с атрибутом writeable (теоретически загрузчику должно быть наплевать на атрибуты секции, но практически это не совсем так), попробуйте обновить фасм вместе с макросами.
Quantum я уже написал, почему возникает ошибка с записью, потому что я сам ее скомпилировал и отладил. дисбаланс стека ведет за собой RET на неправильный адрес, где случайно оказывается команда записи.
Great ret - это макрос в данном случае. В частности из-за такой неоднозначности я не люблю макросы, особенно фасмовые.
Quantum а ты глянь отладчиком и увидишь, что это никакой не макрос, а команда. не веришь - убедись сам, LEAVE автоматом не генерируется хотя у него генерируется) жесть
Эти "недра" ntdll - это RtlDispatchException. Реально она дохнет после RET-а, я уже писал раза 3 наверное. MsgBox требует как и все апишки выровненного стека. проверь зы. странно, а у меня leave нету.
Mika0x65 > Те не восстанавливаешь флаг направления цепочечных команд перед возвратом из sz2dd -- это одно из требований Win. Спасибо! Заработало! > зачем тебе нужна 'stc' Да, не нужна =) Quantum Спасибо. Все это я пробовал.
Great Зачем мне куда-то ещё смотреть, если топикстартер уже привёл код, который у него генерируется? У Вас не генерируется, а у других генерируется Обяснить причину? _qwerty_ Мда, странно, что ExitProcess'у нужен этот флаг, хотя логично. Учтём-с.
MessageBox тоже не любит если кто-то по неряшливости или незнанию забыл восстановить флаг направления на дефолтное значение
Great > LEAVE автоматом не генерируется если ret - команда, то leave не генерируется =) а вот если ret - макрос, то может и генерироваться. См. в %fasminc%\macro\ вот если я напишу: "retn 4", то никакого leave не будет, а если напишу "ret", то макрос заменит это на "leave / retn 4". Если, конечно, до этого был вызов макроса proc, создающий стэковый кадр. Quantum > Мда, странно, что ExitProcess'у нужен этот флаг, хотя логично. Учтём-с. И MessageBox'у. А у Iczelion'а я про этот флаг не видел - там только про регистры, используемые виндой =(