Сегодня ты сделаешь еще один шаг в деле
изучения Linux систем. Я расскажу об основных
приемах при работе с gdb. Овладев ими ты сможешь понять, как работает любая программа, писать свои эксплоиты.
Вы, наверное, все слышали про такую вещь как отладчик, gdb – это и есть отладчик. GDB – GNU
Debugger. Это некое подобие SoftICE для Windows (для тех кто не знает – самый популярный и, на мой взгляд, вообще лучший отладчик), только под
Linux системы. Дело в том, что в сети не так уж много документов, которые демонстрируют работу этой вещи и в свое время я его осваивал сам. Итак,
в документе будут описаны базовые команды gdb. Все это будет показано на примере. А в качестве примера я решил взять ненужную прогу yes. Для тех, кто не знает – это программа просто выводит символ ‘y’ до бесконечности, для начала я решил научить ее выводить не этот символ, а строку ‘XAKEP’, хоть веселее будет.
Ну а теперь все по порядку. Сам отладчик запускается так:
Но можно вводить различные параметры, у нас это будет путь к исследуемой программе:
# gdb /usr/bin/yes
Можно исследовать core файлы, для этого нужно ввести следует ввести следующее:
# gdb /usr/bin/yes core
Еще может понадобится команда для просмотра содержимого регистров:
(gdb) info registers
либо так (сокращенный вариант)
Теперь рассмотрим как делать перехваты. Существуют
точки останова, точки перехвата и точки наблюдения. Более конкретно я бы хотел остановиться на точках останова. Их можно устанавливать на:
(gdb) break function - Остановить перед входом в функцию
(gdb) break *adress - Остановить перед выполнением инструкции по адресу.
После установок можно просмотреть все точки для этого воспользуйтесь командой:
(gdb) info break
А потом можно удалить эти точки:
(gdb) clear breakpoint - где break это название точки останова
(например, функция или адрес)
Очень необходимой вещью является возможность автоматического отображения различных значений при выполнении программы. Для этого существует команда display:
(gdb) display/format value , где format – это формат отображения, а value – само выражение которое нужно отобразить.
Для работы с отображением отведены следующие команды:
(gdb) info display - выдает инфу об отображениях
(gdb) delete num - где num – удалить элементы с индексом
num
Это был небольшой справочник по командам, чтобы понять основную идею.
Далее на примере хотелось бы продемонстрировать это и еще немного. И помните – здесь я дал лишь очень маленькую часть всех возможностей gdb, на самом деле у него их в сотни раз больше, поэтому читайте и учите.
Как я и обещал, берем ненужную прогу yes. Путь на вашей машине может не совпадать с моим, все зависит от операционки которой вы пользуетесь, если что воспользуйтесь поиском (команда
find).
# gdb /usr/bin/yes
После запуска он говорит приветственное сообщение.
GNU gdb 19991004
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(no debugging symbols found)...
Так как yes выводит бесконечное число символов, то лучше бы их нам не видеть в отладчике, а вывод
программы можно направить на другую консоль. Откройте новый терминал, наберите who is i и вы получите имя консоли. Должно вылезти
что-то вроде этого:
Вот теперь просто привязываем к ней.
(gdb) tty /dev/pts/1
А теперь ставим точку останова на функцию puts(), а чтобы было понятней вот вам man-справка об функции(команда man
puts)
#include
int puts(const char *s);
puts() writes the string s and a trailing newline to std
out.
Как видно, функция посылаем строку s на поток вывода. Вот она то нам и нужна. На ней то мы пока и остановимся.
(gdb) break puts
Breakpoint 1 at 0x8048698
И запускаем саму программу, чтобы дождаться пока gdb не остановит ее выполнение на вызове функции.
(gdb) r
Starting program: /usr/bin/yes
Breakpoint 1 at 0x4006d585: file ioputs.c, line 32.
Breakpoint 1, 0x4006d585 in _IO_puts (str=0x8048e59 "y") at ioputs.c:32
32 ioputs.c: No such file or directory.
1: x/i $eip 0x4006d585 <_IO_puts+21>: mov 0x8(%ebp),%esi
О, вот и произошло чудо, сработал breakpoint. Что мы видим – а видим мы ни что иное, как параметр функции, точнее адрес, по которому он лежит. Что теперь нужно
сделать? Правильно, подправить данные по этому адресу. При этом мы затрем еще пару символов своими.
(gdb) set {char}0x8048e59="X"
(gdb) set {char}0x8048e5a="A"
(gdb) set {char}0x8048e5b="K"
(gdb) set {char}0x8048e5c="E"
(gdb) set {char}0x8048e5d="P"
Ну а теперь посмотрим на наше творение. Что там лежит в памяти:
(gdb) x/3sw 0x8048e59
0x8048e59 <_IO_stdin_used+437>: "XAKEP\004\b"
0x8048e61 <_IO_stdin_used+445>: ""
0x8048e62 <_IO_stdin_used+446>: ""
Теперь удалим наш брякпоинт:
(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y 0x4006d585 in _IO_puts at ioputs.c:32
breakpoint already hit 1 time
(gdb) clear puts
Deleted breakpoint 1
И продолжим выполнение чтобы насладится результатом:
Вот и все. Выходим.
(gdb) q
The program is running. Exit anyway? (y or n) y
На этом практика заканчивается, остальное изучайте сами и помните что главное в этой жизни – это УЧЕНЬЕ.
Вот еще некоторые примеры работы:
Присоединение к работающему процессу:
// launch gdb
hack@exploit:~ > gdb
GNU gdb 4.18
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for
details.
This GDB was configured as "i386-suse-linux".
(gdb) attach "pid"
(gdb) attach 1127 // example
Поиск в памяти:
(gdb) x/d or x "address" show decimal
(gdb) x/100s "address" show next 100 decimals
(gdb) x 0x0804846c show decimal at 0x0804846c
(gdb) x/s "address" show strings at address
(gdb) x/105 0x0804846c show 105 strings at 0x0804846c
(gdb) x/x "address" show hexadecimal address
(gdb) x/10x 0x0804846c show 10 addresses at 0x0804846c
(gdb) x/b 0x0804846c show byte at 0x0804846c
(gdb) x/10b 0x0804846c-10 show byte at 0x0804846c-10
(gdb) x/10b 0x0804846c+20 show byte at 0x0804846c+20
(gdb) x/20i 0x0804846c show 20 assembler instructions at address
Список всех секций в исполняемом файле:
(gdb) maintenance info sections // or
(gdb) mai i s
Executable file:
`/home/hack/homepage/challenge/buf/basic", file type
elf32-i386.
0x080480f4->0x08048107 at 0x000000f4: .interp ALLOC
0x08048108->0x08048128 at 0x00000108: .note.ABI-tag
ALLOC LOAD READONLY DATA HAS_CONTENTS
0x08048128->0x08048158 at 0x00000128: .hash ALLOC
LOAD READONLY DATA HAS_CONTENTS
0x08048158->0x080481c8 at 0x00000158: .dynsym ALLOC
LOAD READONLY DATA HAS_CONTENTS
0x080481c8->0x08048242 at 0x000001c8: .dynstr ALLOC
LOAD READONLY DATA HAS_CONTENTS
0x08048242->0x08048250 at 0x00000242: .gnu.version
ALLOC LOAD READONLY DATA
HAS_CONTENTS
Бряк на адрес:
(gdb) disassemble main
Dump of assembler code for function main:
0x8048400
0x8048401
0x8048403
0x8048409
0x804840c
0x804840f
0x8048412
0x8048414
0x8048415
...
(gdb) break *0x8048414 // example
Breakpoint 1 at 0x8048414
(gdb) break main // example
Breakpoint 2 at 0x8048409
(gdb)
Поговорим об отладчиках для Microsoft Windows. Их существует довольно много, вспомнить хотя бы всеми любимый OllyDbg, некогда популярный, но в настоящее время практически умерший SoftIce, а также Sycer, Immunity Debugger, x64dbg и бесчисленное количество отладчиков, встроенных в IDE. По моим наблюдениям, WinDbg нравится далеко не всем. Думаю, в основном это связано с командным интерфейсом отладчика. Любителям Linux и FreeBSD, бесспорно, он пришелся бы по душе. Но закоренелым пользователям Windows он кажется странным и неудобным. А тем временем, по функционалу WinDbg ничем не уступает другим отладчикам. Как минимум, он точно нечем не хуже классического GDB или там LLDB . В чем мы сегодня с вами и убедимся.
В мире Windows, все, как обычно, немного через жопу. Официальный инсталятор WinDbg можно скачать на сайте MS. Инсталятор этот помимо WinDbg также поставит вам свежую версию.NET Framework и перезагрузит систему без спроса. После установки не факт, что отладчик вообще заработает, особенно под старыми версиями Windows. Поэтому лучше скачать неофициальную сборку WinDbg или . Настоятельно советую воспользоваться именно одной из этих версий — это самый простой и быстрый способ установить WinDbg.
Есть две версии WinDbg, x86 и x64. Чтобы не возникало никаких проблем, отлаживайте x86 приложения с помощью x86 дебагера, а x64 приложения — с помощью x64 дебагера. После первого запуска выглядеть WinDbg будет довольно убого. Но не беспокойтесь по этому поводу. Поработав буквально несколько минут с WinDbg, вы подстроите его под себя и выглядеть он будет вполне няшненько. Например, как-то так (кликабельно, 51 Кб, 1156 x 785):
На этом скриншоте изображена отладка программы из заметки Получаем список запущенных процессов на Windows API . Как видите, WinDbg подцепил исходники программы. Справа отображаются значения локальных переменных. Внизу находится окно для ввода команд, где при помощи команды kn был выведен стэк вызовов. В верхней части отладчика находятся кнопки, при помощи которых можно выполнять простые действия вроде «шаг вперед», а также открыть дополнительные окна. При помощи этих окон можно посмотреть содержимое оперативной памяти, значения регистров, дизассемблерный листинг программы и много других интересных вещей.
Вообще, очень многое в WinDbg можно делать через GUI. Например, в окне с исходным кодом можно поставить курсор на нужном месте и нажатием на икноку с ладонью создать там точку останова. Или выполнить run to cursor. Также выполнение команды можно в любой момент остановить при помощи кнопки Break в панели вверху. На этом всем подробно останавливаться не будем. Но учтите, что такие возможности есть и они заслуживают изучения!
Прежде, чем приступить к отладке при помощи WinDbg, нужно сделать несколько несложных телодвижений. Откройте File → Symbol File Path и введите:
SRV*C:\symbols*http://msdl.microsoft.com/download/symbols
Затем нажмите Browse и укажите путь до файлов c отладочной информацией (.pdb) вашего проекта. Аналогичным образом в File → Source File Path укажите путь до каталога с исходниками. Если сомневаетесь, укажите путь, по которому находится файл проекта Visual Studio, не ошибетесь. Затем скажите File → Save Workspace, чтобы все эти пути не приходилось указывать заново при каждом запуске WinDbg.
Теперь, когда все настроено, есть несколько способов начать отладку. Можно запустить новый процесс под отладчиком, можно подключиться к уже существующему, можно открыть крэшдамп. Все это делается через меню File. Особого внимания заслуживает возможность удаленной отладки. Например, если при помощи WinDbg вы отлаживаете драйверы, то особого выбора, кроме как использовать удаленную отладку, у вас как бы и нет. Что не удивительно, ведь малейшая ошибка в коде драйвера может привести к BSOD.
Если вы уже отлаживаете процесс, но хотели бы начать делать это удаленно, говорим:
Server tcp:port=3003
Нужно проверить, что порт открыт, что особенно актуально на Windows Server. На клиенте делаем File → Connect to a Remote Session, вводим:
tcp:Port=3003,Server=10.110.0.10
Кроме того, можно запустить сервер, позволяющий отлаживать любой процесс в системе:
dbgsrv.exe -t tcp:port=3003
На клиенте подключаемся через File → Connect to Remote Stub. После этого через интерфейс можно как обычно выбрать процесс из списка или запустить новый. Только работать процессы будут на удаленной машине.
Теперь, наконец-то, рассмотрим основные команды WinDbg.
Важно! Иногда команды могут выполняется очень долго, например, если вы решили загрузить сразу все отладочные символы. Если устанете ждать, просто нажмите Ctr+C в поле ввода команды, и WinDbg тут же перестанет делать то, что он сейчас делает.
Help
.hh команда
Очистить вывод в окне Command:
Добавить путь, по которому WinDbg будет искать отладочные символы (та же команда без знака плюс затрет все ранее прописанные пути):
Sympath+ c:\pdbs
Перезагрузить символы:
Показать список модулей:
Ищем символы:
x *!Ololo::My::Namespace::*
Загрузить символы для модуля:
ld имя_модуля
Если WinDbg почему-то не находит.pdb файлы и в стектрейсах вместо имен.c/.cpp файлов с номерами строк вы видите что-то вроде module+0x19bc, попробуйте выполнить следующую последовательность команд — так вы получите больше информации о возможных причинах проблемы:
Sym noisy
.reload MyModule.dll
Указать путь к каталогу исходников:
Поставить точку останова:
bp kernel32!CreateProcessA
bp `mysorucefile.cpp:123`
bp `MyModule!mysorucefile.cpp:123`
bp @@(Full::Class:Name::method)
Поставить точку остановка, которая сработает только один раз:
Поставить точку останова, которая сработает на 5-ый раз, после 4-х проходов:
Можно автоматически выполнять команды каждый раз, когда срабатывает точка останова:
bp kernel32!LoadLibraryA ".echo \"Variables:\n\"; dv"
У аппаратных точек останова синтаксис такой:
ba
Где mode это e, r или w — выполнение, чтение, запись. При mode = e параметр size может быть только 1. Например:
ba e 1 kernel32!LoadLibraryA
Список точек останова:
Деактивировать точку останова:
Активировать точку останова:
Полное удаление точек останова:
bc номер
bc *
Показать команды, которые нужно ввести для восстановления текущих точек останова:
Писать лог в файл:
Logopen c:\1.txt
Перестать писать лог в файл:
Выполнить команды из файла:
Для сохранения точек останова в файл можно выполнить (обязательно в одну строку!) следующие команды:
Logopen c:\1.txt; .bpcmds; .logclose
Показать дизассемблерный листинг:
Показать значение регистров:
Показать стек вызовов:
То же самое с номерами фреймов:
Перемещение к фрейму:
Frame номер
Показать локальные переменные:
Показать структуру:
dt имя_переменной
Показать структуру рекурсивно:
dt -r имя_переменной
Дамп памяти по адресу:
Дамп памяти в виде word/dword/qword:
dw адрес
dd адрес
dq адрес
Дамп в виде битов:
Дамп ascii строки:
Дамп unicode строки:
Редактирование памяти.
Откровенно говоря, программа GNU GDB довольно многофункциональная. Пошаговая от-лад-ка — лишь одна из ее возможностей. В этой статье я попытался описать те лишь команды GDB, которые позволяют проводить удобную пошаговую отладку программ, на-пи-сан-ных на Free Pascal.
Чтобы программу можно было отлаживать, она должна быть откомпилирована с ключом -g .
Поскольку GDB ориентирован не на Pascal, а на C и C++, то использование GDB для от-лад-ки Pascal-программ иногда сопряжено с неудобствами.
Приведу список подводных камней, обнаруженных мною и разработчиками Free Pascal (пе-ре-чис-лен-ных в user"s manual).
var
x: dbl;
...
Все примеры отлаживались с использованием GNU GDB 5.0.
gdb [опции] [имя_файла | ID процесса]
После запуска видим "nice GDB logo" (если это почему-то раздражает, то опция -q поз-во-ля-ет не выводить это введение с ин-фор-ма-ци-ей об авторских правах и прочая). В следующей стро-ке приглашение
(gdb)
ждет ввода команды.
Ниже приводится краткий перечень команд GDB.
Краткую справку о любой команде можно получить, введя
(gdb) help [имя_команды, можно краткое]
Если при запуске GDB имя исполняемого файла не было указано (что следовало бы делать), то указать его можно командой file .
(gdb) file <имя исполняемого файла, который подлежит отладке>
Для того, чтобы пролистать содержимое исходника, используйте команду list (сокращенно l ; бо льшая часть наиболее полезных ко-манд имеют сокращения). При этом под-ра-зу-ме-ва-ет-ся, что исходник расположен в том же каталоге, что и исполняемый файл. Как правило, так оно и есть.
Пролистывает 10 строк вниз, начиная с текущей. Для пролистывания вверх следует набрать
Запускает отлаживаемую программу под GDB. Если требуется, то после команды можно ука-зать список аргументов программы. Так-же допускается перенаправление потоков ввода и вы-во-да в другие файлы, например
(gdb) run > outfile
Если никаких точек останова не определено, то программа выполняется тихо, при этом нам со-об-ща-ет-ся:
(gdb) run
Starting program: test
Program exited normally.
(gdb)
Если же отладчик встречает точку останова, он выдает ее номер, адрес и дополнительную ин-фор-ма-цию — текущую строку, имя про-це-ду-ры, и т.п.
(gdb) r
Breakpoint 1, main () at test.pp:3
Current language: auto; currently pascal
3 x:= x + 1;
(gdb)
И ожидает ввода команды.
Команда kill (k). Следует запрос
(gdb) kill
Kill the program being debugged? (y or n) y
(gdb)
Здесь введено y (то есть "да"), и отладка программы прекращается. Командой run ее можно на-чать заново, при этом все точки ос-та-но-ва (breakpoints), точки просмотра (watchpoints) и точ-ки отлова (catchpoints) сохраняются.
Команда quit (q).
(gdb) q
$
(gdb) help breakpoints
Точки останова — такие, когда GDB приостанавливает выполнение программы. Как уже упо-ми-на-лось, имеется 3 типа точек ос-та-но-ва:
Команда break
(gdb) break [аргумент]
или, сокращенно
(gdb) b [аргумент]
определяет точку останова. В качестве аргумента может выступать
Допускается использование нескольких точек останова на одной строке (функции, адресе).
Существуют различные виды точек просмотра, и задаются они различными командами:
Замечу от себя, что команды rwatch и awatch у меня почему-то капризничают — часто не ус-та-нав-ли-ва-ют точки просмотра на пе-ре-мен-ную. Зато команда watch работала всегда.
Информацию о всех установленных точках останова можно вывести командой info .
Команда info имеет много возможностей, но в данном случае воспользуемся лишь сле-дую-щим ее форматом:
(gdb) info breakpoints
или, кратко
(gdb) i b
Выводится подробная информация о всех точках останова (как breakpoints, так и watch-points), включающая - номер - breakpoint или watchpoint - активность - сколько раз прог-рам-ма натыкалась на эту точку - иные характеристики, значение которых мне не со-всем понятно
Если какая-то точка останова не нужна, то ее можно сделать неактивной с помощью ко-ман-ды disable:
(gdb) disable breakpoint <номер этой точки>
Обратно, деактивированная точка останова активируется командой enable:
(gdb) enable breakpoint <номер этой точки>
Статус точки останова — активна она или нет, легко обозреть той же командой info .
Если же точка останова не требуется вообще, то она может быть удалена насовсем.
(gdb) delete breakpoint [номер точки]
(gdb) d b [номер точки]
Ввод этой команды без аргумента удалит ВСЕ точки останова.
Информацию о командах этого раздела можно получить, введя
(gdb) help running
Продолжает выполнение остановленной программы. Выполнение будет происходить, пока сно-ва не встретится точка останова. В ка-чест-ве аргумента может использоваться целое чис-ло N. Это укажет отладчику проигнорировать (N-1) точку останова (вы-пол-не-ние остановится на N-ой).
Аналог действия клавиши F7 (Trace into) в IDE. Происходит выполнение программы до тех пор, пока не будет достигнута сле-дую-щая строка ее кода. При указании аргумента — це-ло-го чис-ла N, отладчик выполняет команду step N раз (если не останавливает вы-пол-не-ние из-за точек останова или по иным причинам).
Аналог действия клавиши F8 (Step over) в IDE. В отличие от step вызов процедуры счи-та-ет-ся единой инструкцией (не заходит в вы-зы-вае-мые процедуры, функции). Аргумент N ра-бо-та-ет так же, как и для step .
Выполняет программу до момента выхода из текущей процедуры (функции). Если функция воз-вра-ща-ет значение, то это значение вы-во-дит-ся тоже.
Производит выполнение программы до тех пор, пока не будет достигнута строка с номером, бо ль-шим текущего. Команду until удоб-но применять при отладке циклов. Остановка про-и-зой-дет также, если программа при выполнении цикла выйдет из текущей про-це-ду-ры, функ-ции.
Действие подобно step , но выполняется не строка, а ровно одна инструкция в этой строке прог-рам-мы. Аргумент N нужен, если тре-бу-ет-ся выполнить N инструкций.
Аналогична stepi , но вызовы процедур трактуются как одна инструкция.
Информацию о командах этого раздела можно получить, введя
(gdb) help data
Вывод текущего значения переменной (выражения). При использовании команды print имя пе-ре-мен-ной можно писать в сме-шан-ном регистре, то есть в этом случае использование боль-ших букв обязательным не является.
Часто требуется отслеживать значения нескольких переменных. Чтобы не утруждать себя мно-го-крат-ным вводом команды print , ис-поль-зуй-те команду display .
В качестве аргумента обычно указывают переменную или выражение. При этом указанная пе-ре-мен-ная (выражение) занесется в дис-плей, то есть станет выводиться при каждой ос-та-нов-ке программы (при попадании на точку останова, при пошаговом вы-пол-не-нии командами step и next , etc). Если вызвать display без аргументов, то GDB выдаст значения всех пе-ре-мен-ных (вы-ра-же-ний), занесенных в дисплей.
Управление списком этих переменных осуществляется аналогично точкам останова. А имен-но, команда info display
(gdb) info display
выдаст все переменные, занесенные в дисплей. Любая переменная в списке дисплея может быть дезактивирована
(gdb) disable display <номер переменной в списке дисплея>
или активирована заново
(gdb) enable display <номер переменной в списке дисплея>
Удаление переменной из списка дисплея производится командой delete или командой undisplay . Так, команда
(gdb) delete display [номер переменной в списке дисплея]
делает то же, что и
(gdb) undisplay [номер переменной в списке дисплея]
Опять-таки, если не указать номер переменной, то очистится весь список отображаемых пе-ре-мен-ных.
И последнее. Изменение значения переменной на другое можно, например, произвести с по-мощью команд set или print .
(gdb) set <оператор присваивания> (gdb) print <оператор присваивания>
Например,
(gdb) whatis x
TYPE = WORD
(gdb) p x
$1 = 1
(gdb) set x:=2
(gdb)
При использовании set присваивание происходит "тихо". То же самое можно сделать, но с по-мощью команды print .
Например,
(gdb) p x
$2 = 2
(gdb) p x:=x-2
$3 = 0
(gdb)
При этом, как видно, выводится новое значение переменной.
Вот и все.
Удачной отладки!
GDB относится к «умным» программам-отладчикам, то есть таким, которые «понимают» код и умеют выполнять его построчно, менять значения переменных, устанавливать контрольные точки и условия остановки… Словом, делать всё для того, чтобы разработчик мог проверить правильность работы своей программы.
GDB встроен во многие UNIX -подобные системы и умеет производить отладку нескольких языков программирования. Си - в их числе.
Чтобы вызвать GDB введите в терминале команду
Gdb [ имя_программы_которую_вы_хотите_отладить]
Чтобы выйти из GDB : введите команду
Quit или С –d
Давайте посмотрим, как работать с GDB на примере программы caesar.c, которую вы, скорее всего уже написали на прошлой неделе. Проверять будем на собственной версии, так что у вас результаты могут несколько отличаться в зависимости от реализации.
Итак, переходим в папку pset2 (думаем, вы уже помните, как это сделать) в «Виртуальной лаборатории cs50» или CS50 IDE. Вводим команду:
Gdb . /caesar
В программе caesar есть одна функция, main. Установим точку остановки программы на функции main:
break mainЗапустим программу caesar с аргументом «3»:
Run 13
Допустим, нам надо проверить значение argc:
Print argc
Вот как всё это должно выглядеть в окне терминала:
Теперь выполняем программу пошагово с помощью команды next . Выполним несколько раз.
Здесь переменной key присваивают значение. Проверим, какое значение она имеет этой строке:
При первом вызве next переменной key присваивается значение «0». ПОчему так, если мы ввели число 3? Дело в том, что команда ещё не была выполнена. Когда вы вводим next ещё несколько раз, программа предлагает ввести текст.
Выполнив команду next еще раз, мы зайдем внутрь цикла с условием.