• 15 апреля стартует «Курс «SQL-injection Master» ©» от команды The Codeby

    За 3 месяца вы пройдете путь от начальных навыков работы с SQL-запросами к базам данных до продвинутых техник. Научитесь находить уязвимости связанные с базами данных, и внедрять произвольный SQL-код в уязвимые приложения.

    На последнюю неделю приходится экзамен, где нужно будет показать свои навыки, взломав ряд уязвимых учебных сайтов, и добыть флаги. Успешно сдавшие экзамен получат сертификат.

    Запись на курс до 25 апреля. Получить промодоступ ...

Статья Рёв мотора драл клыками, словно дичь тишину. Реверс инжиниринг используя radare2. Часть 3

Ку, киберрекруты. В предыдущей главе этого курса по radare2 мы рассмотрели базовую структуру исполняемого двоичного файла, написанного на языке C, уделив внимание вызовам функций ввода/вывода, таким как printf, и базовым структурам данных, таким как переменные, также были рассмотрены базовые меры контроля потока выполнения, такие как операторы if - else. Сегодня мы немного углубимся в эти меры контроля потока выполнения, представим базовое использование оператора case, объявим и используем собственные функции и в конце проанализируем циклы с while и for.

reveng_3.png

Давайте немного освежим предыдущие понятия с помощью следующего кода, который объявляет функцию для обнаружения положительных чисел и переходит к ней.

C:
#include <stdio.h>

func2(){
    int num;
    printf("Enter a number: ");
    scanf("%d", &num);
    if(num>0) printf("The number is positive.\n");
    getchar();
}

void main(){

    func2();
    getchar();

}

Мы можем скомпилировать этот код и перейти к нему в radare2

Код:
[0x000006a0]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze len bytes of instructions for references (aar)
[x] Analyze function calls (aac)
[x] Use -AA or aaaa to perform additional experimental analysis.
[x] Constructing a function name for fcn.* and sym.func.* functions (aan)
[0x000006a0]>

После запуска начального afl, на этот раз здесь мы можем обнаружить пару интересных мест, на которые стоит обратить внимание. Как обычно, у нас есть sym.main, но на этотраз у нас также есть sym.func2, который выглядит не совсем обычно, поэтому может быть интересно взглянуть на него. Другой начальной подсказкой здесь является наличие scanf, getchar, printf или puts, которые дают общее представление о том, что должна делать программа.

Код:
[0x000006a0]> afl
0x00000000    3 72   -> 73   sym.imp.__libc_start_main
0x00000618    3 23           sym._init
0x00000640    1 6            sym.imp.puts
0x00000650    1 6            sym.imp.__stack_chk_fail
0x00000660    1 6            sym.imp.printf
0x00000670    1 6            sym.imp.getchar
0x00000680    1 6            sym.imp.__isoc99_scanf
0x00000690    1 6            sub.__cxa_finalize_248_690
0x000006a0    1 43           entry0
0x000006d0    4 50   -> 40   sym.deregister_tm_clones
0x00000710    4 66   -> 57   sym.register_tm_clones
0x00000760    4 49           sym.__do_global_dtors_aux
0x000007a0    1 10           entry1.init
0x000007aa    5 111          sym.func2
0x00000819    1 26           sym.main
0x00000840    4 101          sym.__libc_csu_init
0x000008b0    1 2            sym.__libc_csu_fini
0x000008b4    1 9            sym._fini
[0x000006a0]>

Мы начнем с перехода к основной функции:

Код:
[0x000006a0]> s main
[0x00000819]> pdb
            ;-- main:
/ (fcn) sym.main 26
|   sym.main ();
|              ; DATA XREF from 0x000006bd (entry0)
|           0x00000819      55             push rbp
|           0x0000081a      4889e5         mov rbp, rsp
|           0x0000081d      b800000000     mov eax, 0
|           0x00000822      e883ffffff     call sym.func2
|           0x00000827      e844feffff     call sym.imp.getchar        ; int getchar(void)
|           0x0000082c      b800000000     mov eax, 0
|           0x00000831      5d             pop rbp
\           0x00000832      c3             ret
[0x00000819]>

В основном функция main выполняет основные операции выравнивания стека и быстро переходит к функции func2, после выполнения которой она вызывает getchar(), возможно, для поддержания окна открытым (если программа, например, запускается под windows).

Итак, интересной функцией здесь является func2, давайте перейдем к ней.

Код:
[0x00000819]> s sym.func2
[0x000007aa]> pdf
/ (fcn) sym.func2 111
|   sym.func2 ();
|           ; var int local_ch @ rbp-0xc
|           ; var int local_8h @ rbp-0x8
|              ; CALL XREF from 0x00000822 (sym.main)
|           0x000007aa      55             push rbp
|           0x000007ab      4889e5         mov rbp, rsp
|           0x000007ae      4883ec10       sub rsp, 0x10
|           0x000007b2      64488b042528.  mov rax, qword fs:[0x28]    ; [0x28:8]=0x1a08 ; '('
|           0x000007bb      488945f8       mov qword [local_8h], rax
|           0x000007bf      31c0           xor eax, eax
|           0x000007c1      488d3dfc0000.  lea rdi, qword str.Enter_a_number: ; 0x8c4 ; "Enter a number: "
|           0x000007c8      b800000000     mov eax, 0
|           0x000007cd      e88efeffff     call sym.imp.printf         ; int printf(const char *format)
|           0x000007d2      488d45f4       lea rax, qword [local_ch]
|           0x000007d6      4889c6         mov rsi, rax
|           0x000007d9      488d3df50000.  lea rdi, qword [0x000008d5] ; "%d"
|           0x000007e0      b800000000     mov eax, 0
|           0x000007e5      e896feffff     call sym.imp.__isoc99_scanf
|           0x000007ea      8b45f4         mov eax, dword [local_ch]
|           0x000007ed      85c0           test eax, eax
|       ,=< 0x000007ef      7e0c           jle 0x7fd
|       |   0x000007f1      488d3de00000.  lea rdi, qword str.The_number_is_positive. ; 0x8d8 ; "The number is positive."
|       |   0x000007f8      e843feffff     call sym.imp.puts           ; int puts(const char *s)
|       |      ; JMP XREF from 0x000007ef (sym.func2)
|       `-> 0x000007fd      e86efeffff     call sym.imp.getchar        ; int getchar(void)
|           0x00000802      90             nop
|           0x00000803      488b55f8       mov rdx, qword [local_8h]
|           0x00000807      644833142528.  xor rdx, qword fs:[0x28]
|       ,=< 0x00000810      7405           je 0x817
|       |   0x00000812      e839feffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|       |      ; JMP XREF from 0x00000810 (sym.func2)
|       `-> 0x00000817      c9             leave
\           0x00000818      c3             ret
[0x000007aa]>

Давайте пройдемся по шагам.

Код:
|           0x000007ae      4883ec10       sub rsp, 0x10
|           0x000007b2      64488b042528.  mov rax, qword fs:[0x28]    ; [0x28:8]=0x1a08 ; '('
|           0x000007bb      488945f8       mov qword [local_8h], rax
|           0x000007bf      31c0           xor eax, eax

Поскольку мы здесь играем на преимуществе, мы уже знаем, что первое, что делает программа на этом этапе, это объявляет числовую переменную, которая будет использоваться для хранения пользовательского ввода. Первые две строки здесь могут показаться немного запутанными и, на мой взгляд, не так уж сильно связаны с реальным "алгоритмом", который мы пытаемся проанализировать, первая строка присутствует во многих for'ах в большом количестве вызовов функций и в основном выделяет место в стеке для работы там с переменными и структурами данных, поскольку мы используем здесь переменные, программа "резервирует" некоторое пространство, конечно, мы углубимся в это позже, но вы и так знаете, что это нормально.

Затем мы находим эту странную инструкцию mov rax, qword, которая немного выходит за рамки этой статьи. Она вставлена туда нашим компилятором gcc, и делает это для того, чтобы установить проверку защиты стека от потенциальных уязвимостей переполнения буфера. Если мы присмотримся к коду, то увидим операцию XOR в том же самом месте в конце процедуры. В общих чертах, коротко описанных, программа проверяет, не был ли поврежден стек, если да, то запускает __stack_chk_fail для безопасного решения проблемы. Чтобы узнать поподробнее о переполнении буффера, можете прочитать статью Mogen - Эксплуатация бинарных уязвимостей (PWN) для почти начинающих: простое переполнение буфера [0x01].Переменная, помеченная как local_8h, будет использоваться для хранения Canary.

Идем дальше

Код:
|           0x000007c1      488d3dfc0000.  lea rdi, qword str.Enter_a_number: ; 0x8c4 ; "Enter a number: "
|           0x000007c8      b800000000     mov eax, 0
|           0x000007cd      e88efeffff     call sym.imp.printf         ; int printf(const char *format)
|           0x000007d2      488d45f4       lea rax, qword [local_ch]
|           0x000007d6      4889c6         mov rsi, rax
|           0x000007d9      488d3df50000.  lea rdi, qword [0x000008d5] ; "%d"
|           0x000007e0      b800000000     mov eax, 0
|           0x000007e5      e896feffff     call sym.imp.__isoc99_scanf

Как мы уже знаем, на системах x64 параметры или аргументы передаются функциям через регистры, поэтому при выполнении lea rdi, "enter a number" мы загружаем в регистр эффективный адрес, по которому строка "Enter a number:" хранится в памяти. Мы не передаем функции строку целиком, вместо этого мы передаем функции ссылку на ее местоположение, эта концепция очень важна, потому что если функция каким-то образом модифицирует строку, ее исходное значение также будет изменено, если мы передаем копию строки, а не ссылку, любые операции, выполняемые внутри функции с копией, не повлияют на оригинал.

Немного о SEE и AVX

Итак, мы видим, что строка для печати передается по ссылке в качестве параметра функции printf, а затем регистр eax обнуляется. В ABI x86_64, если функция (например, printf) имеет переменные аргументы, то AL (который является частью EAX) должен содержать количество векторных регистров (SSE, AVX), используемых для хранения аргументов этой функции, в нашем случае это число равно нулю.

SSE и AVX имеют по 16 регистров. В SSE они обозначаются как XMM0-XMM15, а в AVX - YMM0-YMM15. Регистры XMM имеют длину 128 бит, а YMM - 256 бит.

SSE добавляет три типизации: m128 , m128d и __m128i. Float, double (d) и integer (i) соответственно.

AVX добавляет три типизации: m256 , m256d и __m256i. Float, double (d) и integer (i) соответственно.

После передачи этих параметров вызывается функция printf, которая должна вывести строку на stdout (экран).

1683547609090.png


ПРИМЕЧАНИЕ: XMM и YMM перекрываются! Регистры XMM рассматриваются как нижняя половина соответствующего регистра YMM. Это может вызвать некоторые проблемы с производительностью при смешивании SSE и AVX кода.

У типов данных с плавающей запятой (m128, m128d, m256 и m256d) есть только один вид структуры данных. Из-за этого GCC позволяет обращаться к компонентам данных в виде массива. То есть: это допустимо:

Код:
 __m256 myvar = _mm256_set1_ps(6.665f); //Set all vector values to a single float
  myvar[0] = 2.22f;                       //This is valid in GCC compiler
  float f = (3.4f + myvar[0]) * myvar[7]; //This is valid in GCC compiler

m128i и m256i являются союзами, поэтому на тип данных нужно ссылаться. Я не нашел подходящего способа получить объявление союза, поэтому я использую функции _mm_extract_epiXX() для получения отдельных значений данных из целочисленных векторов.

Пример работы AVX

Когда выполняется инструкция AVX, процесс происходит следующим образом:

1683547827901.png


Все операции выполняются одновременно. С точки зрения производительности, стоимость выполнения одной операции Add над плавающей точкой аналогична выполнению VAdd над 8 плавающими точками в AVX. В таблицах инструкций Агнера Фога есть больше информации о задержках и пропускной способности инструкций. На архитектуре Sandy Bridge VADDPS/D имеет задержку 3 и пропускную способность 1, как и FADD(P).

Вернемся обратно к теме

Сразу после вызова мы видим ссылку на тег local_ch, который загружается сначала в rax, а затем в rsi. local_ch ссылается на выделенное пространство, обычно на переменную, в данном случае это переменная "number". После этого мы видим, что "%d" также загружается в качестве параметра, в данном случае в rdi, затем eax обнуляется, и программа вызывает функцию scanf.

В общепринятых терминах этот блок кода означает printf("Enter a number:"), а затем scanf(%d, &number).

Следующий этап программы связан с проверкой значения для пользовательского ввода.

Код:
|           0x000007e5      e896feffff     call sym.imp.__isoc99_scanf
|           0x000007ea      8b45f4         mov eax, dword [local_ch]
|           0x000007ed      85c0           test eax, eax
|       ,=< 0x000007ef      7e0c           jle 0x7fd
|       |   0x000007f1      488d3de00000.  lea rdi, qword str.The_number_is_positive. ; 0x8d8 ; "The number is positive."
|       |   0x000007f8      e843feffff     call sym.imp.puts           ; int puts(const char *s)
|       |      ; JMP XREF from 0x000007ef (sym.func2)
|       `-> 0x000007fd      e86efeffff     call sym.imp.getchar        ; int getchar(void)

После выполнения scanf пользовательский ввод (local_ch) перемещается в eax, затем, как мы видели в предыдущем примере, выполняется сравнение с 0 с помощью test и jle. Если пользовательский ввод >0, выполнение продолжится и будет выведено "The number is positive", если это условие не выполняется, программа переходит в конец блока.

После этого мы можем сказать, что наш простой анализ закончен, давайте продолжим с дополнительным шагом.

Попробуем теперь сделать дополнительный шаг, добавим условие else и посмотрим, как с ним справится компилятор.

C:
#include <stdio.h>

func2(){
    int num;
    printf("Enter a number: ");
    scanf("%d", &num);

    if(num>0) printf("The number is positive.\n");
    else printf("The number is negative.\n");
    getchar();
}

main(){
    func2();
    getchar();
}

Теперь мы можем перейти непосредственно к делу

Код:
|           0x000007e5      e896feffff     call sym.imp.__isoc99_scanf
|           0x000007ea      8b45f4         mov eax, dword [local_ch]
|           0x000007ed      85c0           test eax, eax
|       ,=< 0x000007ef      7e0e           jle 0x7ff
|       |   0x000007f1      488d3df00000.  lea rdi, qword str.The_number_is_positive. ; 0x8e8 ; "The number is positive."
|       |   0x000007f8      e843feffff     call sym.imp.puts           ; int puts(const char *s)
|      ,==< 0x000007fd      eb0c           jmp 0x80b
|      ||      ; JMP XREF from 0x000007ef (sym.func2)
|      |`-> 0x000007ff      488d3dfa0000.  lea rdi, qword str.The_number_is_negative. ; 0x900 ; "The number is negative."
|      |    0x00000806      e835feffff     call sym.imp.puts           ; int puts(const char *s)
|      |       ; JMP XREF from 0x000007fd (sym.func2)
|      `--> 0x0000080b      e860feffff     call sym.imp.getchar        ; int getchar(void)

Как мы видим, в данном случае, если число не положительное, мы будем считать, что оно отрицательное, и выведем соответствующее сообщение вместо того, чтобы сразу перейти к getchar, в остальной части программы все работает так же.

Внутри radare2 есть более чистый способ проверки бифуркаций кода, таких как та, которую мы только что видели. Если вы напечатаете VV внутри функции, вы сможете визуально проверить ее.

Код:
                                                   | test eax, eax                              |                                                 
                                                   | jle 0x7ff;[gc]                             |                                                 
                                                   `--------------------------------------------'                                                 
                                                           | |                                                                                   
                                                           | '--------------------.                                                               
                              .----------------------------'                      |                                                               
                              |                                                   |                                                               
                              |                                                   |                                                               
                      .------------------------------------------------.    .------------------------------------------------.                   
                      |  0x7f1 ;[gg]                                   |    |  0x7ff ;[gc]                                   |                   
                      |   ; 0x8e8                                      |    |      ; JMP XREF from 0x000007ef (sym.func2)    |                   
                      |   ; "The number is positive."                  |    |   ; 0x900                                      |                   
                      | lea rdi, qword str.The_number_is_positive.     |    |   ; "The number is negative."                  |                   
                      | call sym.imp.puts;[ge]                         |    | lea rdi, qword str.The_number_is_negative.     |                   
                      | jmp 0x80b;[gf]                                 |    | call sym.imp.puts;[ge]                         |                   
                      `------------------------------------------------'    `------------------------------------------------'                   
                          |                                                     |                                                                 
                          '----------------------------.                        |                                                                 
                                                       .------------------------'                                                                 
                                                       |                                                                                         
                                                       |                                                                                         
                                                   .---------------------------------------------.

SWITCH CASE

Случай switch относится к более продвинутому сценарию, чем if else. Оператор if else отлично работает, если у нас есть пара-тройка способов перенаправить поток выполнения программы, но если у нас много разных случаев, мы можем захотеть
использовать что-то более продвинутое, например, switch case.

Мы будем работать со следующим кодом:

C:
#include <stdio.h>

func2(){
    printf("Enter a key and then press enter: ");
    char key;
    scanf("%c",&key);

    switch(key){
        case ' ':
            printf("Space. \n");
        break;
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
        case '0':
            printf("Digit.\n");
            break;
        default: printf("Neither space nor digit.\n");
}

}

main(){
    func2();
    getchar();
}

Как мы видим, код довольно прост, как обычно, все волшебство происходит внутри функции func2. Здесь программа считывает символ со стандартного ввода и передает его в функцию switch. Затем значение символа будет проанализировано через все case'ы. Если введенным символом является пробел, то будет вызвана функция print, а затем проверка завершится инструкцией break, то есть в этом случае программа сразу выйдет из блока switch.

Эти следующие утверждения case можно интерпретировать как очень длинное предложение if, каждое из которых представляет собой что-то вроде if key == 'X', за которым следует символ and (&&), а затем следующее условие. Это означает следующее: если входной сигнал изменяется от '0' до '9', то это означает, что пользователь ввел цифру, и будет напечатано "Digit.\n", будет выполнен break и поток программы будетнаправлен за пределы блока switch. Если условие case не выполняется, программа выполнит то, что было в случае по умолчанию, “Neither space nor digit”.

Теперь, когда мы знаем, что делает эта программа, давайте перейдем непосредственно к radare2. На этот раз мы проанализируем корзину как статически, так и динамически

Код:
red@blue:~/c/chapter3$ radare2 -d case
Process with PID 7901 started...
= attach 7901 7901
bin.baddr 0x560c23c18000
Using 0x560c23c18000
asm.bits 64
[0x7ff6a3b32090]> aaa
[ WARNING : block size exceeding max block size at 0x560c23e18fe0
[+] Try changing it with e anal.bb.maxsize
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze len bytes of instructions for references (aar)
[x] Analyze function calls (aac)
[x] Use -AA or aaaa to perform additional experimental analysis.
[x] Constructing a function name for fcn.* and sym.func.* functions (aan)
= attach 7901 7901
7901
[0x7ff6a3b32090]>

Как мы видим, мы запустили программу с флагом -d, чтобы мы могли ее отлаживать. То, что мы хотим проанализировать, находится внутри функции func2, поэтому мы можем начать с нее.

Код:
[0x7ff6a3b32090]> s sym.func2
[0x560c23c187aa]> pdf
/ (fcn) sym.func2 154
|   sym.func2 ();
|           ; var int local_9h @ rbp-0x9
|           ; var int local_8h @ rbp-0x8
|              ; CALL XREF from 0x560c23c1884d (sym.main)
|           0x560c23c187aa      55             push rbp
|           0x560c23c187ab      4889e5         mov rbp, rsp
|           0x560c23c187ae      4883ec10       sub rsp, 0x10
|           0x560c23c187b2      64488b042528.  mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40
|           0x560c23c187bb      488945f8       mov qword [local_8h], rax
|           0x560c23c187bf      31c0           xor eax, eax
|           0x560c23c187c1      488d3d200100.  lea rdi, qword str.Enter_a_key_and_then_press_enter: ; 0x560c23c188e8 ; "Enter a key and then press enter: "
|           0x560c23c187c8      b800000000     mov eax, 0
|           0x560c23c187cd      e88efeffff     call sym.imp.printf     ; int printf(const char *format)
|           0x560c23c187d2      488d45f7       lea rax, qword [local_9h]
|           0x560c23c187d6      4889c6         mov rsi, rax
|           0x560c23c187d9      488d3d2b0100.  lea rdi, qword [0x560c23c1890b] ; "%c"
|           0x560c23c187e0      b800000000     mov eax, 0
|           0x560c23c187e5      e896feffff     call sym.imp.__isoc99_scanf
|           0x560c23c187ea      0fb645f7       movzx eax, byte [local_9h]
|           0x560c23c187ee      0fbec0         movsx eax, al
|           0x560c23c187f1      83f820         cmp eax, 0x20           ; 32
|       ,=< 0x560c23c187f4      740f           je 0x560c23c18805
|       |   0x560c23c187f6      83f820         cmp eax, 0x20           ; 32
|      ,==< 0x560c23c187f9      7c26           jl 0x560c23c18821
|      ||   0x560c23c187fb      83e830         sub eax, 0x30           ; '0'
|      ||   0x560c23c187fe      83f809         cmp eax, 9              ; 9
|     ,===< 0x560c23c18801      771e           ja 0x560c23c18821
|    ,====< 0x560c23c18803      eb0e           jmp 0x560c23c18813
|    |||`-> 0x560c23c18805      488d3d020100.  lea rdi, qword str.Space. ; 0x560c23c1890e ; "Space. "
|    |||    0x560c23c1880c      e82ffeffff     call sym.imp.puts       ; int puts(const char *s)
|    |||,=< 0x560c23c18811      eb1a           jmp 0x560c23c1882d
|    ||||      ; JMP XREF from 0x560c23c18803 (sym.func2)
|    `----> 0x560c23c18813      488d3dfc0000.  lea rdi, qword str.Digit. ; 0x560c23c18916 ; "Digit."
|     |||   0x560c23c1881a      e821feffff     call sym.imp.puts       ; int puts(const char *s)
|    ,====< 0x560c23c1881f      eb0c           jmp 0x560c23c1882d
|    |``--> 0x560c23c18821      488d3df50000.  lea rdi, qword str.Neither_space_nor_digit. ; 0x560c23c1891d ; "Neither space nor digit."
|    |  |   0x560c23c18828      e813feffff     call sym.imp.puts       ; int puts(const char *s)
|    |  |      ; JMP XREF from 0x560c23c18811 (sym.func2)
|    |  |      ; JMP XREF from 0x560c23c1881f (sym.func2)
|    `--`-> 0x560c23c1882d      90             nop
|           0x560c23c1882e      488b55f8       mov rdx, qword [local_8h]
|           0x560c23c18832      644833142528.  xor rdx, qword fs:[0x28]
|       ,=< 0x560c23c1883b      7405           je 0x560c23c18842
|       |   0x560c23c1883d      e80efeffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|       `-> 0x560c23c18842      c9             leave
\           0x560c23c18843      c3             ret
[0x560c23c187aa]>

Как мы уже говорили, этот блок начинается с механизма защиты стека, который мы представили ранее. Как мы видим, управление потоком выполнения здесь немного сложнее. Это то, что иногда случается с вами, когда вы занимаетесь ctfs или даже
реальными проектами реверс-инжиниринга. Есть два обычных решения, которые можно применить в таких ситуациях. Мы можем либо перейти в визуальный режим, либо проверить строки или интересные вызовы функций. Здесь мы можем легко обнаружить строки Space, Digit и Neither space or digit, что говорит о многом и в принципе решает проблему, поскольку мы легко определили три основных случая.

Магия здесь начинается со scanf

Код:
|           0x560c23c187d2      488d45f7       lea rax, qword [local_9h]
|           0x560c23c187d6      4889c6         mov rsi, rax
|           0x560c23c187d9      488d3d2b0100.  lea rdi, qword [0x560c23c1890b] ; "%c"
|           0x560c23c187e0      b800000000     mov eax, 0
|           0x560c23c187e5      e896feffff     call sym.imp.__isoc99_scanf
|           0x560c23c187ea      0fb645f7       movzx eax, byte [local_9h]
|           0x560c23c187ee      0fbec0         movsx eax, al
|           0x560c23c187f1      83f820         cmp eax, 0x20           ; 32
|       ,=< 0x560c23c187f4      740f           je 0x560c23c18805

В этом примере мы начинаем с передачи нескольких параметров функции scanf через регистры, с помощью %c мы указываем, что собираемся прочитать символ, а local_9h будет местом расположения этого символа в памяти.

После считывания значения символа с пользовательского ввода программа готовит его к сравнению с набором символов в первом операторе case. Поскольку символ в C хранится в одном (1) байте, программе нужно только содержимое AL (RAX/EAX нижний). После загрузки "правильного" значения программа сравнивает его с 0x20, которое представляет собой значение для "пробела" в соответствии с таблицей ASCII.

Код:
|           0x560c23c187f1      83f820         cmp eax, 0x20           ; 32
|       ,=< 0x560c23c187f4      740f           je 0x560c23c18805
|       |   0x560c23c187f6      83f820         cmp eax, 0x20           ; 32
|      ,==< 0x560c23c187f9      7c26           jl 0x560c23c18821
|      ||   0x560c23c187fb      83e830         sub eax, 0x30           ; '0'
|      ||   0x560c23c187fe      83f809         cmp eax, 9              ; 9
|     ,===< 0x560c23c18801      771e           ja 0x560c23c18821
|    ,====< 0x560c23c18803      eb0e           jmp 0x560c23c18813
|    |||`-> 0x560c23c18805      488d3d020100.  lea rdi, qword str.Space. ; 0x560c23c1890e ; "Space. "
|    |||    0x560c23c1880c      e82ffeffff     call sym.imp.puts       ; int puts(const char *s)
|    |||,=< 0x560c23c18811      eb1a           jmp 0x560c23c1882d
|    ||||      ; JMP XREF from 0x560c23c18803 (sym.func2)
|    `----> 0x560c23c18813      488d3dfc0000.  lea rdi, qword str.Digit. ; 0x560c23c18916 ; "Digit."
|     |||   0x560c23c1881a      e821feffff     call sym.imp.puts       ; int puts(const char *s)
|    ,====< 0x560c23c1881f      eb0c           jmp 0x560c23c1882d
|    |``--> 0x560c23c18821      488d3df50000.  lea rdi, qword str.Neither_space_nor_digit. ; 0x560c23c1891d ; "Neither space nor digit."
|    |  |   0x560c23c18828      e813feffff     call sym.imp.puts       ; int puts(const char *s)
|    |  |      ; JMP XREF from 0x560c23c18811 (sym.func2)
|    |  |      ; JMP XREF from 0x560c23c1881f (sym.func2)
|    `--`-> 0x560c23c1882d      90             nop

Как видите, если содержимое eax (ввод) равно значению space, программа перейдет на 0x560c23c18805 и там выдаст запрос "space", после чего выйдет из блока, перейдя сразу на 0x560c23c1882d (nop).

Самое интересное здесь происходит сразу после этого. После этого первого сравнения, если условие перехода не выполнено, программа снова сравнит значение с пробелом, на этот раз проверяя, меньше ли значение пробела, если меньше пробела, программа перейдет к "Neither space nor digit" и выйдет из блока, почему? Это простой трюк для компиляторов, чтобы проверить, находится ли какой-либо символ вне диапазона символов. Как вы сами можете проверить, все цифры от 0 до 9 имеют значения выше 0x20 в таблице ascii, поэтому все, что ниже 0x20, не должно быть цифрой.

Затем выполняется последнее сравнение:

Код:
|      ||   0x560c23c187fb      83e830         sub eax, 0x30           ; '0'
|      ||   0x560c23c187fe      83f809         cmp eax, 9              ; 9
|     ,===< 0x560c23c18801      771e           ja 0x560c23c18821
|    ,====< 0x560c23c18803      eb0e           jmp 0x560c23c18813

Программа вычитает 0x30 из eax и сравнивает его с 0x9, почему? Ну, это еще один вычислительно простой способ для компилятора сделать это, ascii значение из char '9' равно 0x39, так что минус 0x30 возвращает 0x9. После сравнения выполняется ja для перехода к printf "Digit", если условие (key = '9') выполнено, в противном случае программа переходит в зону "neither space or digit" и выходит.

Давайте рассмотрим это более подробно, отладив программу, вводящую цифру.

Начнем с установки нескольких точек останова здесь и там

Код:
[0x560c23c187aa]> db 0x560c23c187f1
[0x560c23c187aa]> db 0x560c23c187fb
[0x560c23c187aa]>

Затем мы запускаем выполнение до первой точки останова

Код:
[0x560c23c187aa]> dc
Enter a key and then press enter: 5
hit breakpoint at: 560c23c187f1
[0x560c23c187f1]> pdb
/ (fcn) sym.func2 154
|   sym.func2 ();
|           ; var int local_9h @ rbp-0x9
|           ; var int local_8h @ rbp-0x8
|              ; CALL XREF from 0x560c23c1884d (sym.main)
|           0x560c23c187aa      55             push rbp
|           0x560c23c187ab      4889e5         mov rbp, rsp
|           0x560c23c187ae      4883ec10       sub rsp, 0x10
|           0x560c23c187b2      64488b042528.  mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40
|           0x560c23c187bb      488945f8       mov qword [local_8h], rax
|           0x560c23c187bf      31c0           xor eax, eax
|           0x560c23c187c1      488d3d200100.  lea rdi, qword str.Enter_a_key_and_then_press_enter: ; 0x560c23c188e8 ; "Enter a key and then press enter: "
|           0x560c23c187c8      b800000000     mov eax, 0
|           0x560c23c187cd      e88efeffff     call sym.imp.printf     ; int printf(const char *format)
|           0x560c23c187d2      488d45f7       lea rax, qword [local_9h]
|           0x560c23c187d6      4889c6         mov rsi, rax
|           0x560c23c187d9      488d3d2b0100.  lea rdi, qword [0x560c23c1890b] ; "%c"
|           0x560c23c187e0      b800000000     mov eax, 0z
|           0x560c23c187e5      e896feffff     call sym.imp.__isoc99_scanf
|           0x560c23c187ea      0fb645f7       movzx eax, byte [local_9h]
|           0x560c23c187ee      0fbec0         movsx eax, al
|           ;-- rip:
|           0x560c23c187f1 b    83f820         cmp eax, 0x20           ; 32
|       ,=< 0x560c23c187f4      740f           je 0x560c23c18805
[0x560c23c187f1]>

Таким образом, значение '5' было загружено в переменную и теперь находится в AL (RAX)

Код:
|           0x560c23c187f1 b    83f820         cmp eax, 0x20           ; 32
|       ,=< 0x560c23c187f4      740f           je 0x560c23c18805
[0x560c23c187f1]> dr
rax = 0x00000035

Итак, rax = 0x35, что соответствует '5' в ascii. '5' будет сравниваться с ' ', и поскольку это не одно и то же число, флаг нуля останется нулевым.

Код:
[0x560c23c187f1]> ds
[0x560c23c187f1]> dr 1
cf = 0x00000000
pf = 0x00000000
af = 0x00000000
zf = 0x00000000
sf = 0x00000000
tf = 0x00000000
if = 0x00000001
df = 0x00000000
of = 0x00000000
[0x560c23c187f1]>

После этого поток выполнения пойдет дальше, и мы встретим другой cmp.

cmp работает следующим образом с флагами

Код:
Assume result = op1 - op2

CF - 1 if unsigned op2 > unsigned op1
OF - 1 if sign bit of OP1 != sign bit of result
SF - 1 if MSB (aka sign bit) of result = 1
ZF - 1 if Result = 0 (i.e. op1=op2)
AF - 1 if Carry in the low nibble of result
PF - 1 if Parity of Least significant byte is even

Поскольку ничего из этого не выполнено, флаги останутся нулевыми. После этого из
нашего значения будет вычтено 0x30, так что 0x35 - 0x30 = 0x5.

Код:
      ||   0x560c23c187fb b    83e830         sub eax, 0x30           ; '0'
|      ||   0x560c23c187fe      83f809         cmp eax, 9              ; 9
|     ,===< 0x560c23c18801      771e           ja 0x560c23c18821
|     |||   ;-- rip:
|    ,====< 0x560c23c18803      eb0e           jmp 0x560c23c18813
|    |||`-> 0x560c23c18805      488d3d020100.  lea rdi, qword str.Space. ; 0x560c23c1890e ; "Space. "

Затем значение будет сравниваться с 9, поэтому флаги будут выглядеть следующим образом

Код:
[0x560c23c187f1]> dr 1
cf = 0x00000001
pf = 0x00000001
af = 0x00000001
zf = 0x00000000
sf = 0x00000001
tf = 0x00000000
if = 0x00000001
df = 0x00000000
of = 0x00000000
[0x560c23c187f1]>

А поскольку ja = Jump short if выше (CF=0 и ZF=0), поток выполнения перейдет прямо к printf("Digit. \n").

Код:
|    ||||      ; JMP XREF from 0x560c23c18803 (sym.func2)
|    `----> 0x560c23c18813      488d3dfc0000.  lea rdi, qword str.Digit. ; 0x560c23c18916 ; "Digit."
|     |||   0x560c23c1881a      e821feffff     call sym.imp.puts       ; int puts(const char *s)
|    ,====< 0x560c23c1881f      eb0c           jmp 0x560c23c1882d

На данный момент мы уже знаем, чем закончится программа, поэтому перейдем к нашему последнему примеру с switch-case.

C:
#include <stdio.h>

func2(){
    printf("Enter a key and then press enter: ");
    int val;

    printf("Select a fruit: \n");
    printf("1: Apple\n");
    printf("2: Orange\n");
    printf("3: Banana\n");
    printf("4: Pear\n");

    scanf("%d",&val);

    switch(val){
        case 1:
            printf("Apple. \n");
            break;
        case 2:
            printf("Orange. \n");
            break;
        case 3:
            printf("Banana. \n");
            break;
        case 4:
            printf("Pear. \n");
            break;
        default: printf("Nothing selected.\n");
    }

}

main(){
    func2();
    getchar();
}

Мы можем легко проверить его с помощью визуального режима radare2

Код:
                                                                                                       | cmp eax, 2                                               |                                             
                                                                                                        | je 0x854;[gd]                                            |                                             
                                                                                                        `----------------------------------------------------------'                                             
                                                                                                                | |                                                                                             
                                                                                                                | '--------------------------------------------.                                                 
                                                                                         .----------------------'                                              |                                                 
                                                                                         |                                                                     |                                                 
                                                                                         |                                                                     |                                                 
                                                                                 .--------------------.                                                  .---------------------------------------------.         
                                                                                 |  0x82e ;[gg]       |                                                  |  0x854 ;[gd]                                |         
                                                                                 | cmp eax, 2         |                                                  |      ; JMP XREF from 0x0000082c (sym.func2) |         
                                                                                 | jg 0x83a;[gf]      |                                                  |   ; 0x9ac                                   |         
                                                                                 `--------------------'                                                  |   ; "Orange. "                              |         
                                                                                         | |                                                             | lea rdi, qword str.Orange.                  |         
                                                                                         | |                                                             | call sym.imp.puts;[gb]                      |         
                                                                                         | |                                                             | jmp 0x88a;[gp]                              |         
                                                                                         | |                                                             `---------------------------------------------'         
                                                                                         | |                                                                 |                                                   
                                                                                         | '---------------------.                                           |                                                   
                                                   .-------------------------------------'                       |                                           |                                                   
                                                   |                                                             |                                           '------------------.                               
                                                   |                                                             |                                                              |                               
                                                   |                                                             |                                                              |                               
                                           .--------------------.                                          .---------------------------------------------.                      |                               
                                           |  0x833 ;[gi]       |                                          |  0x83a ;[gf]                                |                      |                               
                                           | cmp eax, 1         |                                          |      ; JMP XREF from 0x00000831 (sym.func2) |                      |                               
                                           | je 0x846;[gh]      |                                          | cmp eax, 3                                  |                      |                               
                                           `--------------------'                                          | je 0x862;[gl]                               |                      |                               
                                                   | |                                                     `---------------------------------------------'                      |                               
                                                   | |                                                             | |                                                          |                               
                                                   | '--.                                                          | |                                                          |                               
                                .------------------'    |                                                          | |                                                          |                               
                                |                       |                                                          | '--------------.                                           |                               
                                |                       |                                                   .------'                |                                           |                               
                                |                       |                                                   |                       |                                           |                               
                                |                       |                                                   |                       |                                           |                               
                        .--------------------.    .---------------------------------------------.   .--------------------.    .---------------------------------------------.   |                               
                        |  0x838 ;[gk]       |    |  0x846 ;[gh]                                |   |  0x83f ;[gn]       |    |  0x862 ;[gl]                                |   |                               
                        | jmp 0x87e;[gj]     |    |      ; JMP XREF from 0x00000836 (sym.func2) |   | cmp eax, 4         |    |      ; JMP XREF from 0x0000083d (sym.func2) |   |                               
                        `--------------------'    |   ; 0x9a4                                   |   | je 0x870;[gm]      |    |   ; 0x9b5                                   |   |                               
                            |                     |   ; "Apple. "                               |   `--------------------'    |   ; "Banana. "                              |   |                               
                            |                     | lea rdi, qword str.Apple.                   |           | |               | lea rdi, qword str.Banana.                  |   |                               
                            |                     | call sym.imp.puts;[gb]                      |           | |               | call sym.imp.puts;[gb]                      |   |                               
                            |                     | jmp 0x88a;[gp]                              |           | |               | jmp 0x88a;[gp]                              |   |                               
                            |                     `---------------------------------------------'           | |               `---------------------------------------------'   |


Как вы можете видеть здесь, программа считывает выбор пользователя и затем сравнивает значение с одним из вариантов, если нет, то продолжает сравнивать, пока не будут оценены все условия. Вы должны быть в состоянии выполнить анализ
самостоятельно без каких-либо серьезных проблем.

Циклы While и For

Другой случай очень распространенной динамики, которую вы увидите почти в каждом бинарнике, - это циклы, обычно представленные операторами while и for. Код, расположенный внутри цикла while, будет продолжать циклическое выполнение до техпор, пока условие цикла истинно, код, расположенный внутри for, будет выполняться N раз.

C:
#include <stdio.h>

func2(){
int num;

printf("Enter a num, (exit with 0):");

scanf("%d", &num);

while(num != 0){

        if(num > 0) printf("Positive num\n");
        else printf("Negative num\n");

        printf("Enter another num (exit with 0):");

        scanf("%d", &num);

    }

}

main(){

    func2();
    getchar();

}

Внутри radare2 функция func2 будет выглядеть следующим образом:

Код:
[0x7fba89d68090]> s sym.func2
[0x55b75b1ba7aa]> pdf
/ (fcn) sym.func2 170
|   sym.func2 ();
|           ; var int local_ch @ rbp-0xc
|           ; var int local_8h @ rbp-0x8
|              ; CALL XREF from 0x55b75b1ba85d (sym.main)
|           0x55b75b1ba7aa      55             push rbp
|           0x55b75b1ba7ab      4889e5         mov rbp, rsp
|           0x55b75b1ba7ae      4883ec10       sub rsp, 0x10
|           0x55b75b1ba7b2      64488b042528.  mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40
|           0x55b75b1ba7bb      488945f8       mov qword [local_8h], rax
|           0x55b75b1ba7bf      31c0           xor eax, eax
|           0x55b75b1ba7c1      488d3d300100.  lea rdi, qword str.Enter_a_num___exit_with_0_: ; 0x55b75b1ba8f8 ; "Enter a num, (exit with 0):"
|           0x55b75b1ba7c8      b800000000     mov eax, 0
|           0x55b75b1ba7cd      e88efeffff     call sym.imp.printf     ; int printf(const char *format)
|           0x55b75b1ba7d2      488d45f4       lea rax, qword [local_ch]
|           0x55b75b1ba7d6      4889c6         mov rsi, rax
|           0x55b75b1ba7d9      488d3d340100.  lea rdi, qword [0x55b75b1ba914] ; "%d"
|           0x55b75b1ba7e0      b800000000     mov eax, 0
|           0x55b75b1ba7e5      e896feffff     call sym.imp.__isoc99_scanf
|       ,=< 0x55b75b1ba7ea      eb4a           jmp 0x55b75b1ba836
|      .--> 0x55b75b1ba7ec      8b45f4         mov eax, dword [local_ch]
|      :|   0x55b75b1ba7ef      85c0           test eax, eax
|     ,===< 0x55b75b1ba7f1      7e0e           jle 0x55b75b1ba801
|     |:|   0x55b75b1ba7f3      488d3d1d0100.  lea rdi, qword str.Positive_num ; 0x55b75b1ba917 ; "Positive num"
|     |:|   0x55b75b1ba7fa      e841feffff     call sym.imp.puts       ; int puts(const char *s)
|    ,====< 0x55b75b1ba7ff      eb0c           jmp 0x55b75b1ba80d
|    |`---> 0x55b75b1ba801      488d3d1c0100.  lea rdi, qword str.Negative_num ; 0x55b75b1ba924 ; "Negative num"
|    | :|   0x55b75b1ba808      e833feffff     call sym.imp.puts       ; int puts(const char *s)
|    | :|      ; JMP XREF from 0x55b75b1ba7ff (sym.func2)
|    `----> 0x55b75b1ba80d      488d3d240100.  lea rdi, qword str.Enter_another_num__exit_with_0_: ; 0x55b75b1ba938 ; "Enter another num (exit with 0):"
|      :|   0x55b75b1ba814      b800000000     mov eax, 0
|      :|   0x55b75b1ba819      e842feffff     call sym.imp.printf     ; int printf(const char *format)
|      :|   0x55b75b1ba81e      488d45f4       lea rax, qword [local_ch]
|      :|   0x55b75b1ba822      4889c6         mov rsi, rax
|      :|   0x55b75b1ba825      488d3de80000.  lea rdi, qword [0x55b75b1ba914] ; "%d"
|      :|   0x55b75b1ba82c      b800000000     mov eax, 0
|      :|   0x55b75b1ba831      e84afeffff     call sym.imp.__isoc99_scanf
|      :|      ; JMP XREF from 0x55b75b1ba7ea (sym.func2)
|      :`-> 0x55b75b1ba836      8b45f4         mov eax, dword [local_ch]
|      :    0x55b75b1ba839      85c0           test eax, eax
|      `==< 0x55b75b1ba83b      75af           jne 0x55b75b1ba7ec
|           0x55b75b1ba83d      90             nop
|           0x55b75b1ba83e      488b55f8       mov rdx, qword [local_8h]
|           0x55b75b1ba842      644833142528.  xor rdx, qword fs:[0x28]
|       ,=< 0x55b75b1ba84b      7405           je 0x55b75b1ba852
|       |   0x55b75b1ba84d      e8fefdffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|       `-> 0x55b75b1ba852      c9             leave
\           0x55b75b1ba853      c3             ret
[0x55b75b1ba7aa]>

Как видите, логика программы здесь в общих чертах похожа на предыдущие блоки if-else. Разница лишь в том, что от инструкции, расположенной по адресу 0x55b75b1ba83b, вверх идет стрелка, которая отражает суть цикла. Давайте теперь пройдем шаг за шагом.

Прежде всего, программа устанавливает некоторое пространство и, как мы видим, использует пару переменных, одна из которых связана с защитой стека, а другая, вероятно, связана с пользовательским вводом.

Код:
|           ; var int local_ch @ rbp-0xc
|           ; var int local_8h @ rbp-0x8
|              ; CALL XREF from 0x55b75b1ba85d (sym.main)
|           0x55b75b1ba7aa      55             push rbp
|           0x55b75b1ba7ab      4889e5         mov rbp, rsp
|           0x55b75b1ba7ae      4883ec10       sub rsp, 0x10
|           0x55b75b1ba7b2      64488b042528.  mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40
|           0x55b75b1ba7bb      488945f8       mov qword [local_8h], rax
|           0x55b75b1ba7bf      31c0           xor eax, eax

Затем программа запрашивает ввод пользователя и сохраняет его в переменной local_ch.

Код:
|           0x55b75b1ba7cd      e88efeffff     call sym.imp.printf     ; int printf(const char *format)
|           0x55b75b1ba7d2      488d45f4       lea rax, qword [local_ch]
|           0x55b75b1ba7d6      4889c6         mov rsi, rax
|           0x55b75b1ba7d9      488d3d340100.  lea rdi, qword [0x55b75b1ba914] ; "%d"
|           0x55b75b1ba7e0      b800000000     mov eax, 0
|           0x55b75b1ba7e5      e896feffff     call sym.imp.__isoc99_scanf

Мы можем даже переименовать эту переменную, чтобы сделать все это более читабельным.

Код:
[0x55b75b1ba7aa]> afvn local_ch input
[0x55b75b1ba7aa]> afvn
local_8h
input
[0x55b75b1ba7aa]>

Давайте продолжим. Сразу после scanf мы выполняем прыжок в самом низу этого блока кода, вот так:

Код:
|       ,=< 0x55b75b1ba7ea      eb4a           jmp 0x55b75b1ba836
|      .--> 0x55b75b1ba7ec      8b45f4         mov eax, dword [input]

Если мы помним, условием выхода из цикла было равенство входного значения нулю, поэтому в данном случае, если входное значение не равно нулю, сработает jne и вернет нас в начало цикла.

Код:
|      :`-> 0x55b75b1ba836      8b45f4         mov eax, dword [input]
|      :    0x55b75b1ba839      85c0           test eax, eax
|      `==< 0x55b75b1ba83b      75af           jne 0x55b75b1ba7ec

Следующий блок кода? Мы уже знаем его

Код:
|      .--> 0x55b75b1ba7ec      8b45f4         mov eax, dword [input]
|      :|   0x55b75b1ba7ef      85c0           test eax, eax
|     ,===< 0x55b75b1ba7f1      7e0e           jle 0x55b75b1ba801
|     |:|   0x55b75b1ba7f3      488d3d1d0100.  lea rdi, qword str.Positive_num ; 0x55b75b1ba917 ; "Positive num"
|     |:|   0x55b75b1ba7fa      e841feffff     call sym.imp.puts       ; int puts(const char *s)
|    ,====< 0x55b75b1ba7ff      eb0c           jmp 0x55b75b1ba80d
|    |`---> 0x55b75b1ba801      488d3d1c0100.  lea rdi, qword str.Negative_num ; 0x55b75b1ba924 ; "Negative num"
|    | :|   0x55b75b1ba808      e833feffff     call sym.imp.puts       ; int puts(const char *s)
|    | :|      ; JMP XREF from 0x55b75b1ba7ff (sym.func2)

Программа проверяет входные данные, положительные или отрицательные, выполняя тест и переход.

И сразу после этого она снова запрашивает входные данные и проверяет условие выхода в нижней части блока

Код:
|    `----> 0x55b75b1ba80d      488d3d240100.  lea rdi, qword str.Enter_another_num__exit_with_0_: ; 0x55b75b1ba938 ; "Enter another num (exit with 0):"
|      :|   0x55b75b1ba814      b800000000     mov eax, 0
|      :|   0x55b75b1ba819      e842feffff     call sym.imp.printf     ; int printf(const char *format)
|      :|   0x55b75b1ba81e      488d45f4       lea rax, qword [input]
|      :|   0x55b75b1ba822      4889c6         mov rsi, rax
|      :|   0x55b75b1ba825      488d3de80000.  lea rdi, qword [0x55b75b1ba914] ; "%d"
|      :|   0x55b75b1ba82c      b800000000     mov eax, 0
|      :|   0x55b75b1ba831      e84afeffff     call sym.imp.__isoc99_scanf
|      :|      ; JMP XREF from 0x55b75b1ba7ea (sym.func2)
|      :`-> 0x55b75b1ba836      8b45f4         mov eax, dword [input]
|      :    0x55b75b1ba839      85c0           test eax, eax
|      `==< 0x55b75b1ba83b      75af           jne 0x55b75b1ba7ec

И это практически все.

Другой распространенный способ создания циклов - использование for, как показано здесь:

C:
#include <stdio.h>


func2(){

int counter = 0;

for(counter=1; counter <=10; counter++){

        printf("%d ", counter);

}

}

main(){

        func2();
        getchar();

}

А дизазм radare2 будет выглядеть следующим образом:

Код:
[0x0000068a]> pdf
/ (fcn) sym.func2 59
|   sym.func2 ();
|           ; var int input @ rbp-0x4
|              ; CALL XREF from 0x000006ce (sym.main)
|           0x0000068a      55             push rbp
|           0x0000068b      4889e5         mov rbp, rsp
|           0x0000068e      4883ec10       sub rsp, 0x10
|           0x00000692      c745fc000000.  mov dword [input], 0
|           0x00000699      c745fc010000.  mov dword [input], 1
|       ,=< 0x000006a0      eb1a           jmp 0x6bc
|       |      ; JMP XREF from 0x000006c0 (sym.func2)
|      .--> 0x000006a2      8b45fc         mov eax, dword [input]
|      :|   0x000006a5      89c6           mov esi, eax
|      :|   0x000006a7      488d3db60000.  lea rdi, qword [0x00000764] ; "%d "
|      :|   0x000006ae      b800000000     mov eax, 0
|      :|   0x000006b3      e898feffff     call sym.imp.printf         ; int printf(const char *format)
|      :|   0x000006b8      8345fc01       add dword [input], 1
|      :|      ; JMP XREF from 0x000006a0 (sym.func2)
|      :`-> 0x000006bc      837dfc0a       cmp dword [input], 0xa      ; [0xa:4]=0
|      `==< 0x000006c0      7ee0           jle 0x6a2
|           0x000006c2      90             nop
|           0x000006c3      c9             leave
\           0x000006c4      c3             ret
[0x0000068a]>

Обратите внимание, что мы не видим здесь никакой защиты стека, можете сказать почему? Наверное, потому что мы не получаем здесь никаких входных данных!

Итак, программа начинается с установки значения в ноль (счетчик = 0), затем она устанавливает значение в 1, чтобы запустить процесс for

Код:
|           0x00000692      c745fc000000.  mov dword [input], 0
|           0x00000699      c745fc010000.  mov dword [input], 1
|       ,=< 0x000006a0      eb1a           jmp 0x6bc

Затем он сравнивает значение с 10 (0xA).

Код:
|      :`-> 0x000006bc      837dfc0a       cmp dword [input], 0xa      ; [0xa:4]=0
|      `==< 0x000006c0      7ee0           jle 0x6a2

И когда значение меньше его, он возвращается в начало, чтобы выполнить код цикла.

Код в основном печатает значение переменной и прибавляет к нему 1

Код:
|      .--> 0x000006a2      8b45fc         mov eax, dword [input]
|      :|   0x000006a5      89c6           mov esi, eax
|      :|   0x000006a7      488d3db60000.  lea rdi, qword [0x00000764] ; "%d "
|      :|   0x000006ae      b800000000     mov eax, 0
|      :|   0x000006b3      e898feffff     call sym.imp.printf         ; int printf(const char *format)
|      :|   0x000006b8      8345fc01       add dword [input], 1

Легко, да?

Ну, это все, о чем я хотел поговорить здесь. В следующей части мы начнем изучать некоторые структуры данных, такие как массивы и матрицы.

Источник:
 
Последнее редактирование модератором:
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!