Программа под отладчиком работает не так как без отладчика. Магия? - Assembler
Формулировка задачи:
Ситуёвина такова, что прога правильно работает под отладчиком, но неправильно без него.
Вот код:
Прога запускается в консоли. Получает параметр. Если параметр не передать, то выводится "Укажите имя файла". Если параметр передан, то прога должна вывести "Нет такого файла" в том случае, если параметр не является именем существующего файла. Если же параметр является именем существующего файла, то никакого вывода не должно быть, прога просто отрабатывает и завершается.
Но! Если я не передаю параметр, то всё происходит правильно, и прога выдаёт "Укажите имя файла". А вот если я передаю ей параметр, то она в любом случае выдаёт "Нет такого файла", даже если параметр является именем существующего файла. Но если запустить прогу под отладчиком и передать правильный параметр, то она завершается правильно (если проследить за поиском строки, то видно, что название параметра обрабатывается верно, и в регистре EAX действительно передаётся верный адрес.. и прога переходит на нужную ветку исполнения, и завершается верно). В качестве отладчика использую OllyDbg.
Я разломал себе уже весь мозг, ибо только ещё начинаю учиться и пока не могу разбираться в таких нюансах.
Надеюсь, у меня получилось понятно объяснить проблему.
.386 .model flat, stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\kernel32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib GetFName proto .data fHandle DWORD ? stdout DWORD ? cWritten DWORD ? error BYTE "Нет такого файла" noname BYTE "Укажите имя файла" .code start: main proc invoke GetStdHandle, STD_OUTPUT_HANDLE mov stdout, eax invoke GetFName cmp eax, -1 jz empty invoke CreateFile, eax, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 cmp eax,INVALID_HANDLE_VALUE jz exit mov fHandle, eax invoke CloseHandle, fHandle invoke ExitProcess, 0 exit: invoke WriteConsoleA, stdout, ADDR error, sizeof error, ADDR cWritten, NULL invoke ExitProcess, 0 empty: invoke WriteConsoleA, stdout, ADDR noname, sizeof noname, ADDR cWritten, NULL invoke ExitProcess, 0 main endp GetFName proc invoke GetCommandLine mov edi, eax push edi mov al, 0 mov ecx,-1 cld repne scasb ;ищем нулевой символ, чтобы определить конец командной строки not ecx pop edi mov al,20h repne scasb ;ищем пробел, чтобы отделить параметр, переданный в командной строке dec edi ;проверяем .. cmp byte ptr [edi],0 ;.. не ноль ли мы нашли вместо пробела jz empty ;.. если ноль, то параметра не было.. inc edi ;.. если не ноль, то переходим к первому символу параметра mov eax,edi ;возвращаем адрес начала строки с параметром ret empty: mov eax,-1 ret GetFName endp end start
Решение задачи: «Программа под отладчиком работает не так как без отладчика. Магия?»
textual
Листинг программы
GetFName proc invoke GetCommandLine mov edi, eax xor eax,eax;al, 0 or ecx,-1 repne scasb ;ищем нулевой символ, чтобы определить конец командной строки not ecx mov al,20h std repne scasb ;ищем последний пробел, чтобы отделить параметр, переданный в командной строке add edi,2 cld jecxz empty ;.. если ноль, то параметра не было.. mov eax,edi ;возвращаем адрес начала параметра ret empty: or eax,-1 ret GetFName endp
Объяснение кода листинга программы
В коде представлено две процедуры: GetFName и empty.
- Процедура GetFName:
- Получает адрес начала параметра, переданного в командной строке.
- Выполняет следующие действия: 1.1. Применяет функцию GetCommandLine для получения адреса командной строки. 1.2. Сохраняет адрес командной строки в регистре edi. 1.3. Очищает регистр eax. 1.4. Использует инструкцию or для поиска последнего нулевого символа в строке (задание конца строки). 1.5. Использует инструкцию not для изменения знака регистра ecx. 1.6. Перемещает значение 20h в регистр al (используется для проверки наличия пробелов). 1.7. Использует инструкцию std для перехода к следующему байту в строке. 1.8. Использует инструкцию repne scasb для поиска последнего пробела в строке (задание начала параметра). 1.9. Добавляет 2 к значению регистра edi (переход к следующему байту после найденного пробела). 1.10. Использует инструкцию cld для перехода к следующему байту в строке. 1.11. Проверяет значение регистра ecx. Если оно равно нулю, то параметр отсутствует. 1.12. Возвращает адрес начала параметра в регистре eax.
- Возвращает управление.
- Процедура empty:
- Выполняет следующие действия: 2.1. Использует инструкцию or для поиска последнего нулевого символа в строке (задание конца строки). 2.2. Возвращает управление. Обратите внимание, что в коде используются регистры edi, eax, ecx, которые являются регистрами общего назначения и могут использоваться для хранения различных значений в процессе выполнения программы.