• Познакомьтесь с пентестом веб-приложений на практике в нашем новом бесплатном курсе

    «Анализ защищенности веб-приложений»

    🔥 Записаться бесплатно!

  • CTF с учебными материалами Codeby Games

    Обучение кибербезопасности в игровой форме. Более 200 заданий по Active Directory, OSINT, PWN, Веб, Стеганографии, Реверс-инжинирингу, Форензике и Криптографии. Школа CTF с бесплатными курсами по всем категориям.

Статья Сильный я и ловкий, ветра проучу. Реверс инжиниринг используя radare2. Часть 2

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

Первая часть

rev_small.jpg

Переменные

Код


Что касается числовых переменных, то первая представляет собой int, вторая - float, третья - double, а также char 'a'. В конце эти значения складываются и результат выводится на печать. Мы видим, как первая переменная хранится непосредственно в регистре (общего назначения), вторая тоже хранится в памяти, но в другом месте, а третья работает так же, за исключением того, что ей требуется в 2 раза больше места, чем второй (double).

C:
#include <stdio.h>

int main() {
  char ab ='a';
  int a = 3;
  float b = 4.5;
  double c = 5.25;
  float sum;
 
  sum = a+b+c;

  printf("The sum of a, b, and c is %f.", sum);
  return 0;
}

Как всегда, мы можем скомпилировать код с помощью gcc, ничего сложного.

Бинарь

Мы открываем двоичный файл и анализируем его содержимое, aaa должен работать нормально.

Код:
 -- sudo make me a pancake
[0x08048310]> aaa
[Cannot analyze at 0x08048300g with sym. and entry0 (aa)
[x] Analyze all flags starting with sym. and entry0 (aa)
[Cannot analyze at 0x08048300ac)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for objc references
[x] Check for vtables
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.
[0x08048310]>

Поскольку мы компилировали двоичный файл с помощью GCC, мы можем определить некоторые "типичные" функции, связанные с инициализацией программы.

Код:
[0x08048310]> afl
0x08048310    1 33           entry0
0x080482f0    1 6            sym.imp.__libc_start_main
0x08048350    4 43           sym.deregister_tm_clones
0x08048380    4 53           sym.register_tm_clones
0x080483c0    3 30           entry.fini0
0x080483e0    4 43   -> 40   entry.init0
0x080484f0    1 2            sym.__libc_csu_fini
0x08048340    1 4            sym.__x86.get_pc_thunk.bx
0x080484f4    1 20           sym._fini
0x08048490    4 93           sym.__libc_csu_init
0x0804840b    1 123          main
0x080482e0    1 6            sym.imp.printf
0x080482ac    3 35           sym._init
[0x08048310]>

Единственная интересная функция здесь - main, поскольку она явно принадлежит к главной функции программы. Отсюда мы также можем определить, что в программе используется printf.

Код:
[0x08048310]> sf main
[0x0804840b]> pdf
            ; DATA XREF from entry0 @ 0x8048327
┌ 123: int main (int32_t arg_4h, char **argv, char **envp);
│           ; var char var_1dh @ ebp-0x1d
│           ; var int32_t var_1ch @ ebp-0x1c
│           ; var int32_t var_4h @ ebp-0x4
│           ; arg int32_t arg_4h @ esp+0x4c
│           0x0804840b      8d4c2404       lea ecx, [arg_4h]
│           0x0804840f      83e4f0         and esp, 0xfffffff0
│           0x08048412      ff71fc         push dword [ecx - 4]
│           0x08048415      55             push ebp
│           0x08048416      89e5           mov ebp, esp
│           0x08048418      51             push ecx
│           0x08048419      83ec34         sub esp, 0x34
│           0x0804841c      c645e361       mov byte [var_1dh], 0x61    ; 'a' ; 97
│           0x08048420      c745e4030000.  mov dword [var_1ch], 3
│           0x08048427      d9054c850408   fld dword [0x804854c]
│           0x0804842d      d95de8         fstp dword [ebp - 0x18]
│           0x08048430      dd0550850408   fld qword [0x8048550]
│           0x08048436      dd5df0         fstp qword [ebp - 0x10]
│           0x08048439      d9ee           fldz
│           0x0804843b      d95dec         fstp dword [ebp - 0x14]
│           0x0804843e      db45e4         fild dword [var_1ch]
│           0x08048441      d845e8         fadd dword [ebp - 0x18]
│           0x08048444      dc45f0         fadd qword [ebp - 0x10]
│           0x08048447      d95dec         fstp dword [ebp - 0x14]
│           0x0804844a      d945ec         fld dword [ebp - 0x14]
│           0x0804844d      83ec04         sub esp, 4
│           0x08048450      8d6424f8       lea esp, [esp - 8]
│           0x08048454      dd1c24         fstp qword [esp]
│           0x08048457      6810850408     push str.The_sum_of_a__b__and_c_is__f. ; 0x8048510 ; "The sum of a, b, and c is %f." ; const char *format
│           0x0804845c      e87ffeffff     call sym.imp.printf         ; int printf(const char *format)
│           0x08048461      83c410         add esp, 0x10
│           0x08048464      0fbe45e3       movsx eax, byte [var_1dh]
│           0x08048468      83ec08         sub esp, 8
│           0x0804846b      50             push eax
│           0x0804846c      682e850408     push str.The_value_of_char_ab_is__c. ; 0x804852e ; "The value of char ab is %c." ; const char *format
│           0x08048471      e86afeffff     call sym.imp.printf         ; int printf(const char *format)
│           0x08048476      83c410         add esp, 0x10
│           0x08048479      b800000000     mov eax, 0
│           0x0804847e      8b4dfc         mov ecx, dword [var_4h]
│           0x08048481      c9             leave
│           0x08048482      8d61fc         lea esp, [ecx - 4]
└           0x08048485      c3             ret
[0x0804840b]>

Просматривая основной код программы, мы видим здесь несколько новых инструкций, таких как fstp, fild, fadd и т.д. Путем чистой дедукции мы можем утверждать, что они могут быть связаны с операциями с плавающей точкой, поскольку здесь используются float и double. Мы также можем определить, как вызывается функция print, мы видим, как некоторые параметры заталкиваются в стек.

Поскольку здесь мы имеем дело с переменными, одна из вещей, которую мы можем захотеть сделать, это посмотреть, как radare2 идентифицирует переменные и, возможно, дать им красивое имя. Мы можем сделать это с помощью afv.

Код:
[0x0804840b]> afv
arg int32_t arg_4h @ esp+0x4c
var char var_1dh @ ebp-0x1d
var int32_t var_1ch @ ebp-0x1c
var int32_t var_4h @ ebp-0x4
[0x0804840b]>

Поскольку мы явно идентифицируем char с radare2 (var char var_1...), мы можем переименовать эту переменную в char

Код:
[0x0804840b]> afvn  char1 var_1dh
[0x0804840b]> afvn
char1
var_1ch
var_4h
arg_4h
[0x0804840b]>

Интересна переменная char. При использовании переменных char происходит шестнадцатеричное кодирование символов. В шестнадцатеричной кодировке 'a' соответствует 61 в таблице ascii. В нашем примере мы просто видим, как программа использует mov для перемещения байта 0x61 в позицию переменной.

Код:
0x0804841c      c645e361       mov byte [var_1dh], 0x61    ; 'a' ; 97
0x08048420      c745e4030000.  mov dword [var_1ch], 3

Теперь, когда мы определили переменную char и уже должны быть в состоянии определить переменную int, давайте посмотрим, как программа работает с переменными с плавающей точкой.

Лучший способ проверить это - запустить программу в режиме отладки. В radare2 мы можем открыть программу в режиме отладки, используя опцию -d.

В режиме отладки мы можем использовать такие команды, как db memaddress для установки точки останова, dc для продолжения потока выполнения до этой/этих точек останова и dt для выполнения текущей инструкции и перехода к следующей сразу после нее.

В нашей программе мы можем установить несколько интересных точек перед fldz, flstp и т.п.

Код:
[0x0804840b]> db 0x08048427
[0x0804840b]> db 0x0804842d
[0x0804840b]> db 0x08048430
[0x0804840b]> dc
hit breakpoint at: 8048427
[0x08048427]>

После достижения первой точки останова мы переходим к следующему пункту:

Код:
│           0x08048420      c745e4030000.  mov dword [var_1ch], 3
│           0x08048427      d9054c850408   fld dword [0x804854c]
│           0x0804842d      d95de8         fstp dword [ebp - 0x18]

Мы можем определить, что значение 3 было перемещено в var_1ch, а затем выполняется какая-то странная инструкция fld dword. Инструкция fld загружает в стек 32-битное, 64-битное или 80-битное значение с плавающей точкой. Эта инструкция преобразует 32- и 64-битный операнд в 80-битное значение расширенной точности, прежде чем поместить значение в стек с плавающей точкой. Таким образом, если мы проверим, какое значение fld берет для загрузки, мы увидим что-то вроде:

Код:
[0x08048427]> px 32 @ 0x804854c
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x0804854c  0000 9040 0000 0000 0000 1540 011b 033b  ...@.......@...;
0x0804855c  2800 0000 0400 0000 78fd ffff 4400 0000  (.......x...D...
[0x08048427]>

0000 9040 0000 0000 0000 Теперь мы можем попытаться просмотреть содержимое той позиции в памяти, которая явно соответствует переменной

Код:
[0x08048430]> px @ ebp-0x18
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0xbfe7a7e0  0000 9040 a4a8 e7bf aca8 e7bf b184 0408  ...@............
0xbfe7a7f0  dc93 f6b7 10a8 e7bf 0000 0000 37f6 dcb7  ............7...
0xbfe7a800  0090 f6b7 0090 f6b7 0000 0000 37f6 dcb7  ............7...

Мы можем сделать вывод немного более приятным и человекочитаемым (для нашего случая), добавив w и таким образом запустив pxw, когда w приходит из слова (два байта)

Код:
[0x08048430]> pxw @ ebp-0x18
0xbfe7a7e0  0x40900000 0xbfe7a8a4 0xbfe7a8ac 0x080484b1  ...@............
0xbfe7a7f0  0xb7f693dc 0xbfe7a810 0x00000000 0xb7dcf637  ............7...

0x40900000 должно быть значением. Но это значение дает нам мало информации, по крайней мере, в этом формате. Поскольку мы подозреваем, что это число представляет собой закодированное число с плавающей точкой, мы можем опробовать использовать инструмент rax2 для его чтения.

Код:
[0xbfe7a7f0]> rax2  Fx40900000
4.500000f

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

Мы можем проделать то же самое со второй переменной, я оставлю это на ваше усмотрение, вы найдете значение 5.25, но так как это double значение, вместо одного слова у нас будет размер 32.

Наконец, мы видим, как эти параметры попадают в стек. Строка "The sum of..." заталкивается непосредственно в стек, просто выполнив команду push, а переменная sum вставляется в стек с помощью инструкции fstp. Инструкция FST копирует значение в регистре ST(0) в операндназначения, который может быть ячейкой памяти или другим регистром в стеке регистров FPU. При сохранении значения в памяти оно преобразуется в одно- или двухреальный формат.

Код:
|           0x08048447      d95dec         fstp dword [ebp - 0x14]
│           0x0804844a      d945ec         fld dword [ebp - 0x14]
│           0x0804844d      83ec04         sub esp, 4
│           0x08048450      8d6424f8       lea esp, [esp - 8]
│           0x08048454      dd1c24         fstp qword [esp]
│           0x08048457      6810850408     push str.The_sum_of_a__b__and_c_is__f. ; 0x8048510 ; "The sum of a, b, and c is %f." ; const char *format
│           0x0804845c      e87ffeffff     call sym.imp.printf         ; int printf(const char *format)

Мы видим, как значение sum приходит из ebp-0x14 в результате предыдущей операции:

Код:
[0x08048444]> pxw @ ebp-0x14
0xbf8e0ba4  0x00000000 0x00000000 0x40150000 0xb7f573dc  ...........@.s..

И это четко соответствует реальному результату:

Код:
[0x0804844d]> rax2 Fx414c0000
12.750000f (as float)

И затем он появляется в стеке вместе с адресом, указывающим на строку, которая будет напечатана

Код:
[0x0804845c]> pxw @ esp
0xbf8e0b70  0x08048510 0x00000000 0x40298000 0xb7f711b0  ..........)@....
0xbf8e0b80  0x00008000 0xb7f57000 0xb7f55244 0xb7dbd0ec  .....p..DR......
0xbf8e0b90  0x00000001 0x00000000 0x61dd3a50 0x00000003  ........P:.a....

заметьте, что здесь используется little endian и 0x4029800000000000 = 12.75 как double = sum

Условные структуры

Код


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

C:
#include <stdio.h>
int main() {
    signed int number;
    printf("Enter an integer: ");
    scanf("%d", &number);
    if (number > 0) {
        printf("You entered %d.\n", number);
    }
    else{
                printf("You entered a negative number %d.\n", number);
        }
    printf("The if statement is easy.");
    return 0;

Бинарь

Начнем, как обычно, с запуска программы с r2 -d, анализируя ее, переключимся на главную функцию и запустим pdf, чтобы заглянуть в нее:

Код:
[0x080484bb]> pdf
            ; DATA XREF from entry0 @ 0x80483d7
┌ 159: int main (int argc, char **argv, char **envp);
│           ; var int32_t var_10h @ ebp-0x10
│           ; var int32_t var_ch @ ebp-0xc
│           ; var int32_t var_4h @ ebp-0x4
│           ; arg int32_t arg_4h @ esp+0x34
│           0x080484bb      8d4c2404       lea ecx, [arg_4h]
│           0x080484bf      83e4f0         and esp, 0xfffffff0
│           0x080484c2      ff71fc         push dword [ecx - 4]
│           0x080484c5      55             push ebp
│           0x080484c6      89e5           mov ebp, esp
│           0x080484c8      51             push ecx
│           0x080484c9      83ec14         sub esp, 0x14
│           0x080484cc      65a114000000   mov eax, dword gs:[0x14]
│           0x080484d2      8945f4         mov dword [var_ch], eax
│           0x080484d5      31c0           xor eax, eax
│           0x080484d7      83ec0c         sub esp, 0xc
│           0x080484da      68e0850408     push str.Enter_an_integer:  ; 0x80485e0 ; "Enter an integer: "
│           0x080484df      e88cfeffff     call sym.imp.printf         ; int printf(const char *format)
│           0x080484e4      83c410         add esp, 0x10
│           0x080484e7      83ec08         sub esp, 8
│           0x080484ea      8d45f0         lea eax, [var_10h]
│           0x080484ed      50             push eax
│           0x080484ee      68f3850408     push 0x80485f3
│           0x080484f3      e8a8feffff     call sym.imp.__isoc99_scanf ; int scanf(const char *format)
│           0x080484f8      83c410         add esp, 0x10
│           0x080484fb      8b45f0         mov eax, dword [var_10h]
│           0x080484fe      85c0           test eax, eax
│       ┌─< 0x08048500      7e16           jle 0x8048518
│       │   0x08048502      8b45f0         mov eax, dword [var_10h]
│       │   0x08048505      83ec08         sub esp, 8
│       │   0x08048508      50             push eax
│       │   0x08048509      68f6850408     push str.You_entered__d.    ; 0x80485f6 ; "You entered %d.\n"
│       │   0x0804850e      e85dfeffff     call sym.imp.printf         ; int printf(const char *format)
│       │   0x08048513      83c410         add esp, 0x10
│      ┌──< 0x08048516      eb14           jmp 0x804852c
│      │└─> 0x08048518      8b45f0         mov eax, dword [var_10h]
│      │    0x0804851b      83ec08         sub esp, 8
│      │    0x0804851e      50             push eax
│      │    0x0804851f      6808860408     push str.You_entered_a_negative_number__d. ; 0x8048608 ; "You entered a negative number %d.\n"
│      │    0x08048524      e847feffff     call sym.imp.printf         ; int printf(const char *format)
│      │    0x08048529      83c410         add esp, 0x10
│      │    ; CODE XREF from main @ 0x8048516
│      └──> 0x0804852c      83ec0c         sub esp, 0xc
│           0x0804852f      682b860408     push str.The_if_statement_is_easy. ; 0x804862b ; "The if statement is easy."
│           0x08048534      e837feffff     call sym.imp.printf         ; int printf(const char *format)
│           0x08048539      83c410         add esp, 0x10
│           0x0804853c      b800000000     mov eax, 0
│           0x08048541      8b55f4         mov edx, dword [var_ch]
│           0x08048544      653315140000.  xor edx, dword gs:[0x14]
│       ┌─< 0x0804854b      7405           je 0x8048552
│       │   0x0804854d      e82efeffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
│       └─> 0x08048552      8b4dfc         mov ecx, dword [var_4h]
│           0x08048555      c9             leave
│           0x08048556      8d61fc         lea esp, [ecx - 4]
└           0x08048559      c3             ret

Итак, здесь мы можем увидеть стрелки, движущиеся вверх и вниз по коду. Эти стрелки указывают на поток выполнения, которому может следовать программа в зависимости от определенных условий, поскольку у нас уже есть код, мы можем предположить, что эти направления потока каким-то образом связаны с частью кода IF-ELSE. Мы также видим, что в верхней части кода объявлены некоторые переменные.

Следующее, что может привлечь наше внимание - это вызов функции SCANF. Давайте рассмотрим его, установив несколько точек останова с помощью команды db:

Код:
│           0x080484df      e88cfeffff     call sym.imp.printf         ; int printf(const char *format)
│           0x080484e4      83c410         add esp, 0x10
│           0x080484e7      83ec08         sub esp, 8
│           0x080484ea b    8d45f0         lea eax, [input]
│           0x080484ed      50             push eax
│           0x080484ee      68f3850408     push 0x80485f3
│           0x080484f3 b    e8a8feffff     call sym.imp.__isoc99_scanf ; int scanf(const char *format)
│           0x080484f8 b    83c410         add esp, 0x10
│           0x080484fb      8b45f0         mov eax, dword [input]
│           0x080484fe      85c0           test eax, eax

Итак, после вызова PRINTF происходит некоторая корректировка стека, после чего мы видим, что адрес одной из переменных, которые мы видели в верхней части кода, загружается в eax (можно назвать ее как-нибудь вроде input с помощью afvn из чисто логических соображений), затем eax выталкивается в стек для передачи в качестве параметра (можно догадаться) функции SCANF, также в стек выталкивается адрес памяти, в данном случае мы видим, что этот адрес содержит строку ("please enter...:").

Если мы переместимся после первой точки останова, то сможем осмотреть регистры и увидеть, как загружается адрес:

Код:
[0x080484ed]> dr
eax = 0xbfa9a688
ebx = 0x00000000
ecx = 0x0910b01a
edx = 0xb7f0c870
esi = 0xb7f0b000
edi = 0xb7f0b000
esp = 0xbfa9a678
ebp = 0xbfa9a698
eip = 0x080484ed
eflags = 0x00000296
oeax = 0xffffffff

Затем мы можем перейти к следующей точке останова и осмотреть стек, мы увидим, как там появляются эти два параметра:

Код:
[0x080484ed]> dc
hit breakpoint at: 80484f3
[0x080484f3]> pxr @ esp
0xbfa9a670 0x080485f3  .... @esp (/home/lab/c_examples/bin/ifelse) (.rodata) program R X 'and eax, 0x6f590064' 'ifelse' (%d)
0xbfa9a674 0xbfa9a688  .... ([stack]) stack R W 0xbfa9a74c -->  ([stack]) stack R W 0xbfa9c288 -->  ([stack]) stack R W 0x5f474458 (XDG_VTNR=7) -->  ascii ('X')
0xbfa9a678 0xb7d87a50  Pz.. (/lib/i386-linux-gnu/libc-2.23.so) library R X 'add ebx, 0x1835b0' 'libc-2.23.so'
0xbfa9a67c 0x080485ab  .... (/home/lab/c_examples/bin/ifelse) (.text) sym.__libc_csu_init program R X 'add edi, 1' 'ifelse'
0xbfa9a680 0x00000001  .... 1 (.comment)
0xbfa9a684 0xbfa9a744  D... ([stack]) stack R W 0xbfa9c27f -->  ([stack]) stack R W 0x66692f2e (./ifelse) -->  ascii ('.')

Затем, если мы попадем в следующую точку останова еще раз, программа запросит значение, а затем попадет в последнюю точку останова:

Код:
[0x080484f3]> dc
Enter an integer: 10
hit breakpoint at: 80484f8
[0x080484f8]>

Если мы проверим регистры, то увидим, что EAX установлен в значение 0x00000001, что означает, что вызов SCANF был выполнен правильно без каких-либо серьезных проблем.

Код:
[0x080484f8]> dr
eax = 0x00000001
ebx = 0x00000000
ecx = 0x00000001
edx = 0xb7f0c87c
esi = 0xb7f0b000
edi = 0xb7f0b000
esp = 0xbfa9a670
ebp = 0xbfa9a698
eip = 0x080484f8
eflags = 0x00000246
oeax = 0xffffffff
[0x080484f8]>

Теперь мы можем посмотреть адрес переменной input и увидеть, как число (10 в нашем случае) хранится там в шестнадцатеричном формате.

Код:
[0x080484f8]> pxw @ 0xbfa9a688
0xbfa9a688  0x0000000a 0x630f3300 0xb7f0b3dc 0xbfa9a6b0  .....3.c........

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

Код:
[0x080484f8]> rax2 0xa
10

В этой точке программы мы имеем правильно сохраненное входное значение, после чего программа должна будет проверить, является ли это значение положительным или отрицательным, и в зависимости от этого переместить выполнение в то или иное место. Поэтому мы можем установить еще одну точку останова прямо перед первой стрелкой, связанной с потоком выполнения. Поскольку стрелки могут появляться не всегда (это всегда зависит от используемого вами дизасма/фреймворка), другой способ обнаружить точки переключения в потоке выполнения - искать JLE, JE и подобные им инструкции.

Код:
[0x080484f8]>


│           0x080484f8 b    83c410         add esp, 0x10
│           0x080484fb      8b45f0         mov eax, dword [input]
│           0x080484fe      85c0           test eax, eax
│       ┌─< 0x08048500      7e16           jle 0x8048518
│       │   0x08048502      8b45f0         mov eax, dword [input]
│       │   0x08048505      83ec08         sub esp, 8

Итак, мы видим, что значение 10, в десятичной системе, загружается в EAX, а затем выполняется тестовая инструкция. После выполнения тестовой инструкции состояние регистров флагов следующее:

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

По сути, инструкция TEST устанавливает флаги ZF(флаг нуля) и SF(флаг знака) на основе логического И (AND) между операндами и очищает флаг OF(флаг переполнения). Так что в данном случае эти флаги будут установлены в ноль. Тогда инструкция JLE означает JUMP (на адрес памяти, который указан сразу после) Если меньше чем и Меньше чем определяется как: ZF=1 или SF != OF

Таким образом, мы видим, что если мы перезагрузим программу и введем отрицательное число, то флаг SF активизируется:

Код:
[0x080484bb]> db 0x08048500
[0x080484bb]> dc
Enter an integer: -20
hit breakpoint at: 8048500
[0x08048500]> dr 1
cf = 0x00000000
pf = 0x00000000
af = 0x00000000
zf = 0x00000000
sf = 0x00000001
tf = 0x00000000
if = 0x00000001
df = 0x00000000
of = 0x00000000
[0x08048500]>
And one more time, as we enter a positive number the SF flag will not be activated.
[0x080484bb]> db 0x08048500
[0x080484bb]> dc
Enter an integer: 20
hit breakpoint at: 8048500
[0x08048500]> dr 1
cf = 0x00000000
pf = 0x00000001
af = 0x00000000
zf = 0x00000000
sf = 0x00000000
tf = 0x00000000
if = 0x00000001
df = 0x00000000
of = 0x00000000
[0x08048500]>

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

64-битная версия

64-битная версия практически такая же, можете отметить различия? В основном регистры (esi,rdi...) используются для передачи параметров вместо стека! Кроме того, адреса памяти 64-битные.

Код:
[0x7f584b7b9090]> sf main
[0x561302d2871a]> pdf
            ;-- main:
/ (fcn) main 161
|   main ();
|           ; var int local_ch @ rbp-0xc
|           ; var int local_8h @ rbp-0x8
|              ; DATA XREF from 0x561302d2862d (entry0)
|           0x561302d2871a      55             push rbp
|           0x561302d2871b      4889e5         mov rbp, rsp
|           0x561302d2871e      4883ec10       sub rsp, 0x10
|           0x561302d28722      64488b042528.  mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40
|           0x561302d2872b      488945f8       mov qword [local_8h], rax
|           0x561302d2872f      31c0           xor eax, eax
|           0x561302d28731      488d3d100100.  lea rdi, qword str.Enter_an_integer: ; 0x561302d28848 ; "Enter an integer: "
|           0x561302d28738      b800000000     mov eax, 0
|           0x561302d2873d      e89efeffff     call sym.imp.printf     ; int printf(const char *format)
|           0x561302d28742      488d45f4       lea rax, qword [local_ch]
|           0x561302d28746      4889c6         mov rsi, rax
|           0x561302d28749      488d3d0b0100.  lea rdi, qword [0x561302d2885b] ; "%d"
|           0x561302d28750      b800000000     mov eax, 0
|           0x561302d28755      e896feffff     call sym.imp.__isoc99_scanf
|           0x561302d2875a      8b45f4         mov eax, dword [local_ch]
|           0x561302d2875d      85c0           test eax, eax
|       ,=< 0x561302d2875f      7e18           jle 0x561302d28779
|       |   0x561302d28761      8b45f4         mov eax, dword [local_ch]
|       |   0x561302d28764      89c6           mov esi, eax
|       |   0x561302d28766      488d3df10000.  lea rdi, qword str.You_entered__d. ; 0x561302d2885e ; "You entered %d.\n"
|       |   0x561302d2876d      b800000000     mov eax, 0
|       |   0x561302d28772      e869feffff     call sym.imp.printf     ; int printf(const char *format)
|      ,==< 0x561302d28777      eb16           jmp 0x561302d2878f
|      |`-> 0x561302d28779      8b45f4         mov eax, dword [local_ch]
|      |    0x561302d2877c      89c6           mov esi, eax
|      |    0x561302d2877e      488d3deb0000.  lea rdi, qword str.You_entered_a_negative_number__d. ; 0x561302d28870 ; "You entered a negative number %d.\n"
|      |    0x561302d28785      b800000000     mov eax, 0
|      |    0x561302d2878a      e851feffff     call sym.imp.printf     ; int printf(const char *format)
|      |       ; JMP XREF from 0x561302d28777 (main)
|      `--> 0x561302d2878f      488d3dfd0000.  lea rdi, qword str.The_if_statement_is_easy. ; 0x561302d28893 ; "The if statement is easy."
|           0x561302d28796      b800000000     mov eax, 0
|           0x561302d2879b      e840feffff     call sym.imp.printf     ; int printf(const char *format)
|           0x561302d287a0      b800000000     mov eax, 0
|           0x561302d287a5      488b55f8       mov rdx, qword [local_8h]
|           0x561302d287a9      644833142528.  xor rdx, qword fs:[0x28]
|       ,=< 0x561302d287b2      7405           je 0x561302d287b9
|       |   0x561302d287b4      e817feffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|       `-> 0x561302d287b9      c9             leave
\           0x561302d287ba      c3             ret
[0x561302d2871a]>

Успехов :)

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

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