• Курсы Академии Кодебай, стартующие в мае - июне, от команды The Codeby

    1. Цифровая криминалистика и реагирование на инциденты
    2. ОС Linux (DFIR) Старт: 16 мая
    3. Анализ фишинговых атак Старт: 16 мая Устройства для тестирования на проникновение Старт: 16 мая

    Скидки до 10%

    Полный список ближайших курсов ...

Статья Вторяк про массивы и строки (БОЛЬШЕ СТРОК). Реверс инжиниринг используя radare2. Часть 5

Ку, киберрекруты. В этой статье мы немного подробнее рассмотрим массивы char и строки, чтобы заложить прочную основу темы и перейти к изучению более сложных структур данных (таких, как многомерные массивы и структуры).

part_5.png

Краткие сведения о массивах char

Как мы видели в предыдущем посте, строки могут быть объявлены как массивы char фиксированного размера, затем они могут быть выведены с помощью printf с использованием %s для формата.

C:
# include <stdio.h>

main(){
 func();
 getchar();
 getchar();
 getchar(); 
}

func(){

    char text[40];
    printf("Name?: ");
    scanf("%s", &text);
    printf("Hi, %s\n", text);

}

Но интересно отметить, что scanf будет читать все, что находится перед пробелом, давайте проверим это:

Код:
[0x5653b864f78e]> pdf
/ (fcn) sym.func 111
|   sym.func ();
|           ; var int local_30h @ rbp-0x30
|           ; var int local_8h @ rbp-0x8
|              ; CALL XREF from 0x5653b864f773 (sym.main)
|           0x5653b864f78e      55             push rbp
|           0x5653b864f78f      4889e5         mov rbp, rsp
|           0x5653b864f792      4883ec30       sub rsp, 0x30           ; '0'
|           0x5653b864f796      64488b042528.  mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40
|           0x5653b864f79f      488945f8       mov qword [local_8h], rax
|           0x5653b864f7a3      31c0           xor eax, eax
|           0x5653b864f7a5      488d3dd80000.  lea rdi, qword str.Name_: ; 0x5653b864f884 ; "Name?: "
|           0x5653b864f7ac      b800000000     mov eax, 0
|           0x5653b864f7b1      e86afeffff     call sym.imp.printf     ; int printf(const char *format)
|           0x5653b864f7b6      488d45d0       lea rax, qword [local_30h]
|           0x5653b864f7ba      4889c6         mov rsi, rax
|           0x5653b864f7bd      488d3dc80000.  lea rdi, qword [0x5653b864f88c] ; "%s"
|           0x5653b864f7c4      b800000000     mov eax, 0
|           0x5653b864f7c9      e872feffff     call sym.imp.__isoc99_scanf
|           0x5653b864f7ce      488d45d0       lea rax, qword [local_30h]
|           0x5653b864f7d2      4889c6         mov rsi, rax
|           0x5653b864f7d5      488d3db30000.  lea rdi, qword str.Hi___s ; 0x5653b864f88f ; "Hi, %s\n"
|           0x5653b864f7dc      b800000000     mov eax, 0
|           0x5653b864f7e1      e83afeffff     call sym.imp.printf     ; int printf(const char *format)
|           0x5653b864f7e6      90             nop
|           0x5653b864f7e7      488b55f8       mov rdx, qword [local_8h]
|           0x5653b864f7eb      644833142528.  xor rdx, qword fs:[0x28]
|       ,=< 0x5653b864f7f4      7405           je 0x5653b864f7fb
|       |   0x5653b864f7f6      e815feffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|       `-> 0x5653b864f7fb      c9             leave
\           0x5653b864f7fc      c3             ret
[0x5653b864f78e]> db 0x5653b864f7dc
[0x7f1e4dda4090]> dc
Name?: sample text
hit breakpoint at: 5653b864f7dc
[0x5653b864f7dc]>

После установки точки останова сразу после printf и запуска программы, нас попросят ввести данные, мы можем ввести что-то вроде "sample text", и здравый смысл подсказывает нам, что вывод после выполнения printf должен быть "Hi, sample text", но нет.

Код:
|           0x5653b864f7c9      e872feffff     call sym.imp.__isoc99_scanf
|           0x5653b864f7ce      488d45d0       lea rax, qword [local_30h]
|           0x5653b864f7d2      4889c6         mov rsi, rax
|           0x5653b864f7d5      488d3db30000.  lea rdi, qword str.Hi___s ; 0x5653b864f88f ; "Hi, %s\n"
|           ;-- rip:
|           0x5653b864f7dc b    b800000000     mov eax, 0
|           0x5653b864f7e1      e83afeffff     call sym.imp.printf     ; int printf(const char *format)
|           0x5653b864f7e6      90             nop
|           0x5653b864f7e7      488b55f8       mov rdx, qword [local_8h]
|           0x5653b864f7eb      644833142528.  xor rdx, qword fs:[0x28]
|       ,=< 0x5653b864f7f4      7405           je 0x5653b864f7fb
|       |   0x5653b864f7f6      e815feffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|       `-> 0x5653b864f7fb      c9             leave
\           0x5653b864f7fc      c3             ret
[0x5653b864f7dc]> dr
rax = 0x7ffceeaaabd0
rbx = 0x00000000
rcx = 0x7f1e4dd9e560
rdx = 0x7f1e4dd9f8d0
r8 = 0x00000000
r9 = 0x00000000
r10 = 0x00000000
r11 = 0x5653b864f88e
r12 = 0x5653b864f660
r13 = 0x7ffceeaaacf0
r14 = 0x00000000
r15 = 0x00000000
rsi = 0x7ffceeaaabd0
rdi = 0x5653b864f88f
rsp = 0x7ffceeaaabd0
rbp = 0x7ffceeaaac00
rip = 0x5653b864f7dc
rflags = 0x00000206
orax = 0xffffffffffffffff
[0x5653b864f7dc]> pxw @ 0x7ffceeaaabd0
0x7ffceeaaabd0  0x706d6173 0x0000656c 0xb864f84d 0x00005653  sample..M.d.SV..

Как мы видим, scanf сохранил только "sample", пропустив " text". Это потому, что scanf означает scan formated, поэтому нам нужно указать вход, который мы ожидаем, нам нужно определить спецификатор формата.

Format specifierDescriptionSupported data types
%cCharacterchar
unsigned char
%dSigned Integershort
unsigned short
int
long
%e or %EScientific notation of float valuesfloat
double
%fFloating pointfloat
%g or %GSimilar as %e or %Efloat
double
%hiSigned Integer(Short)short
%huUnsigned Integer(Short)unsigned short
%iSigned Integershort
unsigned short
int
long
%l or %ld or %liSigned Integerlong
%lfFloating pointdouble
%LfFloating pointlong double
%luUnsigned integerunsigned int
unsigned long
%lli, %lldSigned Integerlong long
%lluUnsigned Integerunsigned long long
%oOctal representation of Integer.short
unsigned short
int
unsigned int
long
%pAddress of pointer to void void *void *
%sStringchar *
%uUnsigned Integerunsigned int
unsigned long
%x or %XHexadecimal representation of Unsigned Integershort
unsigned short
int
unsigned int
long
%nPrints nothing
%%Prints % character

Вызов функции в таком виде:

scanf("%[^\n]",text);

Позволяет регистрировать места. scanf считывает данные из ввода на основе спецификатора, точно так же, как printf выводит содержимое.

Puts and gets​


Другими методами получения строк со входа или их печати являются gets и puts. Gets получает символы со стандартного ввода и сохраняет их в виде строки, а puts печатает символы со стандартного вывода. Оба действуют так же, как printf и scanf, главное отличие в том, что здесь нет спецификации на формат. Основное различие здесь исходит от формата ( ), так как, например, по умолчанию gets не останавливается, если встречает пробел. То же самое с puts, puts просто выгрузит содержимое массива char, интерпретируя его как строку.

Рассмотрим следующий код:

C:
# include <stdio.h>

main(){
 func();
 getchar();

}

func(){

    char text[40];
    puts("Name?: ");
    gets(text);
    printf("Ho, %s\n", text);
}

Здесь puts используется так же, как printf, а gets - для чтения из stdin, давайте посмотрим:

Код:
[0x5618704127a4]> pdf
/ (fcn) sym.func 99
|   sym.func ();
|           ; var int local_30h @ rbp-0x30
|           ; var int local_8h @ rbp-0x8
|              ; CALL XREF from 0x561870412793 (sym.main)
|           0x5618704127a4      55             push rbp
|           0x5618704127a5      4889e5         mov rbp, rsp
|           0x5618704127a8      4883ec30       sub rsp, 0x30           ; '0'
|           0x5618704127ac      64488b042528.  mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40
|           0x5618704127b5      488945f8       mov qword [local_8h], rax
|           0x5618704127b9      31c0           xor eax, eax
|           0x5618704127bb      488d3dd20000.  lea rdi, qword str.Name_: ; 0x561870412894 ; "Name?: "
|           0x5618704127c2      e859feffff     call sym.imp.puts       ; int puts(const char *s)
|           0x5618704127c7      488d45d0       lea rax, qword [local_30h]
|           0x5618704127cb      4889c7         mov rdi, rax
|           0x5618704127ce      b800000000     mov eax, 0
|           0x5618704127d3      e888feffff     call sym.imp.gets       ; char*gets(char *s)
|           0x5618704127d8      488d45d0       lea rax, qword [local_30h]
|           0x5618704127dc      4889c6         mov rsi, rax
|           0x5618704127df      488d3db60000.  lea rdi, qword str.Ho___s ; 0x56187041289c ; "Ho, %s\n"
|           0x5618704127e6      b800000000     mov eax, 0
|           0x5618704127eb      e850feffff     call sym.imp.printf     ; int printf(const char *format)
|           0x5618704127f0      90             nop
|           0x5618704127f1      488b55f8       mov rdx, qword [local_8h]
|           0x5618704127f5      644833142528.  xor rdx, qword fs:[0x28]
|       ,=< 0x5618704127fe      7405           je 0x561870412805
|       |   0x561870412800      e82bfeffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|       `-> 0x561870412805      c9             leave
\           0x561870412806      c3             ret
[0x5618704127a4]>


В этом случае строка "Name..." передается в функцию puts с помощью rdi, мы также видим, что eax обнуляется, вероятно, потому, что не используются векторные регистры. Затем у нас есть функция gets:

Код:
|           0x5618704127c7      488d45d0       lea rax, qword [local_30h]
|           0x5618704127cb      4889c7         mov rdi, rax
|           0x5618704127ce      b800000000     mov eax, 0
|           0x5618704127d3      e888feffff     call sym.imp.gets       ; char*gets(char *s)

Опять же, local_30h используется как указатель на считываемую строку и передается в функцию gets через регистр rdi.

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

Код:
|           0x5618704127d3      e888feffff     call sym.imp.gets       ; char*gets(char *s)
|           0x5618704127d8      488d45d0       lea rax, qword [local_30h]
|           0x5618704127dc      4889c6         mov rsi, rax
|           0x5618704127df      488d3db60000.  lea rdi, qword str.Ho___s ; 0x56187041289c ; "Ho, %s\n"
|           0x5618704127e6      b800000000     mov eax, 0
|           0x5618704127eb      e850feffff     call sym.imp.printf     ; int printf(const char *format)
|           0x5618704127f0      90             nop
|           0x5618704127f1      488b55f8       mov rdx, qword [local_8h]
|           0x5618704127f5      644833142528.  xor rdx, qword fs:[0x28]
|       ,=< 0x5618704127fe      7405           je 0x561870412805
|       |   0x561870412800      e82bfeffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|       `-> 0x561870412805      c9             leave
\           0x561870412806      c3             ret
[0x5618704127a4]> db 0x5618704127df
[0x5618704127a4]> dc
Name?:
SAMPLE TEXT
hit breakpoint at: 5618704127df
[0x5618704127a4]> dr
rax = 0x7ffe03c95df0
rbx = 0x00000000
rcx = 0x7f9f10693a00
rdx = 0x7f9f106958d0
r8 = 0x561870acd67c
r9 = 0x7f9f108a34c0
r10 = 0x561870acd010
r11 = 0x00000246
r12 = 0x561870412680
r13 = 0x7ffe03c95f10
r14 = 0x00000000
r15 = 0x00000000
rsi = 0x7ffe03c95df0
rdi = 0x7ffe03c95df1
rsp = 0x7ffe03c95df0
rbp = 0x7ffe03c95e20
rip = 0x5618704127df
rflags = 0x00000246
orax = 0xffffffffffffffff
[0x5618704127a4]> pxw @ 0x7ffe03c95df0
0x7ffe03c95df0  0x504d4153 0x5420454c 0x00545845 0x00005618  SAMPLE TEXT..V..¡

Вуаля! Как мы видим, на этот раз в памяти хранится полная строка. Важно отметить, что использование таких функций, как scanf и gets, вообще не рекомендуется, если мы пытаемся построить что-то серьезное, так как пользовательский ввод не контролируется вообще. Как мы видели, при использовании gets, пользовательский ввод буквально заносится в память, начиная с определенного адреса памяти, связанного с началом массива char, поэтому если пользователь введет действительно большую строку символов, что-то намного большее, чем пространство массива, это, вероятно, приведет к поломке программы, также, поскольку gets читает все, что пользователь выводит на stdin и сохраняет в памяти, пользователь может даже ввести код, который может быть выполнен.

Strlen​


Мы можем взаимодействовать с массивами char разными способами. Функция strlen, которая, вероятно, встречается во многих программах и ctf'ах, возвращает длину строки.

C:
# include <stdio.h>

main(){
 func();
 getchar();

}

func(){

    char text[40];
    puts("Name?: ");
    gets(text);
    printf("Hi, %s\n", text);
    printf("Length: %d chars", strlen(text));
}

strlen проходит через строку и считает, сколько позиций имеет хит, он проходит строку char за char, пока не встретит нулевой терминатор (\0). Сейчас мы проверим, что это значит:

Код:
[0x55df8ad0a7e4]> pdf
/ (fcn) sym.func 131
|   sym.func ();
|           ; var int local_30h @ rbp-0x30
|           ; var int local_8h @ rbp-0x8
|              ; CALL XREF from 0x55df8ad0a7d3 (sym.main)
|           0x55df8ad0a7e4      55             push rbp
|           0x55df8ad0a7e5      4889e5         mov rbp, rsp
|           0x55df8ad0a7e8      4883ec30       sub rsp, 0x30           ; '0'
|           0x55df8ad0a7ec      64488b042528.  mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40
|           0x55df8ad0a7f5      488945f8       mov qword [local_8h], rax
|           0x55df8ad0a7f9      31c0           xor eax, eax
|           0x55df8ad0a7fb      488d3df20000.  lea rdi, qword str.Name_: ; 0x55df8ad0a8f4 ; "Name?: "
|           0x55df8ad0a802      e849feffff     call sym.imp.puts       ; int puts(const char *s)
|           0x55df8ad0a807      488d45d0       lea rax, qword [local_30h]
|           0x55df8ad0a80b      4889c7         mov rdi, rax
|           0x55df8ad0a80e      b800000000     mov eax, 0
|           0x55df8ad0a813      e888feffff     call sym.imp.gets       ; char*gets(char *s)
|           0x55df8ad0a818      488d45d0       lea rax, qword [local_30h]
|           0x55df8ad0a81c      4889c6         mov rsi, rax
|           0x55df8ad0a81f      488d3dd60000.  lea rdi, qword str.Hi___s ; 0x55df8ad0a8fc ; "Hi, %s\n"
|           0x55df8ad0a826      b800000000     mov eax, 0
|           0x55df8ad0a82b      e850feffff     call sym.imp.printf     ; int printf(const char *format)
|           0x55df8ad0a830      488d45d0       lea rax, qword [local_30h]
|           0x55df8ad0a834      4889c7         mov rdi, rax
|           0x55df8ad0a837      e824feffff     call sym.imp.strlen     ; size_t strlen(const char *s)
|           0x55df8ad0a83c      4889c6         mov rsi, rax
|           0x55df8ad0a83f      488d3dbe0000.  lea rdi, qword str.Length:__d_chars ; 0x55df8ad0a904 ; "Length: %d chars"
|           0x55df8ad0a846      b800000000     mov eax, 0
|           0x55df8ad0a84b      e830feffff     call sym.imp.printf     ; int printf(const char *format)
|           0x55df8ad0a850      90             nop
|           0x55df8ad0a851      488b55f8       mov rdx, qword [local_8h]
|           0x55df8ad0a855      644833142528.  xor rdx, qword fs:[0x28]
|       ,=< 0x55df8ad0a85e      7405           je 0x55df8ad0a865
|       |   0x55df8ad0a860      e80bfeffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|       `-> 0x55df8ad0a865      c9             leave
\           0x55df8ad0a866      c3             ret
[0x55df8ad0a7e4]>

Давайте сосредоточимся на функции strlen.

Код:
|           0x55df8ad0a82b      e850feffff     call sym.imp.printf     ; int printf(const char *format)
|           0x55df8ad0a830      488d45d0       lea rax, qword [local_30h]
|           0x55df8ad0a834      4889c7         mov rdi, rax
|           0x55df8ad0a837      e824feffff     call sym.imp.strlen     ; size_t strlen(const char *s)
|           0x55df8ad0a83c      4889c6         mov rsi, rax

Как обычно, строка находится внутри local_30h. Поэтому эта ссылка загружается в rdi в качестве параметра, а затем вызывается strlen. Давайте поставим несколько точек останова и проанализируем программу.

Код:
[0x55df8ad0a7e4]> dc
Name?:
SAMPLE TEXT
Hi, SAMPLE TEXT
hit breakpoint at: 55df8ad0a834

[0x55df8ad0a834]> dr
rax = 0x7ffcfc542170
[...]
[0x55df8ad0a834]> pxw @ 0x7ffcfc542170
0x7ffcfc542170  0x504d4153 0x5420454c 0x00545845 0x000055df  SAMPLE TEXT..U..

Мы видим, как "SAMPLE TEXT" был сохранен как строка внутри local_30h, и если мы обратим немного больше внимания на дамп памяти, то увидим, что строка заканчивается 0x00, что является нулевым терминатором, поэтому strlen будет продолжать читать, пока не достигнет этого нулевого терминатора.

Если мы продолжим исследование strlen, то увидим, что функция возвращает свое значение (длину строки), используя регистр RAX.

Код:
[0x55df8ad0a834]> dc
hit breakpoint at: 55df8ad0a83c
[0x55df8ad0a834]> dr
rax = 0x0000000b

Как мы видим, rax содержит 0xb = 11dec, а "SAMPLE TEXT" имеет длину 11 (с учетом пробела).

Библиотека для работы со строками

Библиотека строк "string.h" содержит различные функции, очень полезные для работы со строками. Эти функции позволяют нам манипулировать строками различными способами, здесь мы рассмотрим, как они полезны для копирования строк.

Рассмотрим следующую программу:

C:
# include <stdio.h>
# include <string.h>


main(){
 func();
 getchar();

}

func(){

    char text1[40], text2[40], text3[10];
 
    printf("Enter a string NOW: ");
    gets(text1);
 
    strcpy(text2, text1);
    printf("Copied string =  %s\n", text2);
    strncpy(text3, text1, 4);
    printf("4 first chars = %s\n", text3);

}

Мы видим, что сверху включается файл strings.h, затем объявляются 3 массива символов и используется strcpy для копирования содержимого одного из них в другой.

Мы можем легко определить используемую библиотеку strings с помощью afl

Код:
:~/chapter5$ radare2 ./string
[0x000006d0]> 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)
[0x000006d0]> afl
0x00000000    2 25           sym.imp.__libc_start_main
0x00000630    3 23           sym._init
0x00000660    1 6            sym.imp.strncpy
0x00000670    1 6            sym.imp.strcpy
0x00000680    1 6            sym.imp.__stack_chk_fail
0x00000690    1 6            sym.imp.printf
0x000006a0    1 6            sym.imp.getchar
0x000006b0    1 6            sym.imp.gets
0x000006c0    1 6            sub.__cxa_finalize_248_6c0
0x000006d0    1 43           entry0
0x00000700    4 50   -> 40   sym.deregister_tm_clones
0x00000740    4 66   -> 57   sym.register_tm_clones
0x00000790    4 49           sym.__do_global_dtors_aux
0x000007d0    1 10           entry1.init
0x000007da    1 26           sym.main
0x000007f4    3 171          sym.func
0x000008a0    4 101          sym.__libc_csu_init
0x00000910    1 2            sym.__libc_csu_fini
0x00000914    1 9            sym._fini
[0x000006d0]>

и с ii/il

Код:
[0x7f47eeef5090]> il
[Linked libraries]
libc.so.6

1 library

[0x7f47eeef5090]> ii
[Imports]
   1 0x55896339b660  GLOBAL    FUNC strncpy
   2 0x55896339b000    WEAK  NOTYPE _ITM_deregisterTMCloneTable
   3 0x55896339b670  GLOBAL    FUNC strcpy
   4 0x55896339b680  GLOBAL    FUNC __stack_chk_fail
   5 0x55896339b690  GLOBAL    FUNC printf
   6 0x55896339b000  GLOBAL    FUNC __libc_start_main
   7 0x55896339b6a0  GLOBAL    FUNC getchar
   8 0x55896339b000    WEAK  NOTYPE __gmon_start__
   9 0x55896339b6b0  GLOBAL    FUNC gets
  10 0x55896339b000    WEAK  NOTYPE _ITM_registerTMCloneTable
  11 0x55896339b000    WEAK    FUNC __cxa_finalize
   2 0x55896339b000    WEAK  NOTYPE _ITM_deregisterTMCloneTable
   6 0x55896339b000  GLOBAL    FUNC __libc_start_main
   8 0x55896339b000    WEAK  NOTYPE __gmon_start__
  10 0x55896339b000    WEAK  NOTYPE _ITM_registerTMCloneTable
  11 0x55896339b000    WEAK    FUNC __cxa_finalize

[0x7f47eeef5090]>

И код:

Код:
[0x5585de6317f4]> pdf
/ (fcn) sym.func 171
|   sym.func ();
|           ; var int local_6ah @ rbp-0x6a
|           ; var int local_60h @ rbp-0x60
|           ; var int local_30h @ rbp-0x30
|           ; var int local_8h @ rbp-0x8
|              ; CALL XREF from 0x5585de6317e3 (sym.main)
|           0x5585de6317f4      55             push rbp
|           0x5585de6317f5      4889e5         mov rbp, rsp
|           0x5585de6317f8      4883ec70       sub rsp, 0x70           ; 'p'
|           0x5585de6317fc      64488b042528.  mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40
|           0x5585de631805      488945f8       mov qword [local_8h], rax
|           0x5585de631809      31c0           xor eax, eax
|           0x5585de63180b      488d3d120100.  lea rdi, qword str.Enter_a_string_NOW: ; 0x5585de631924 ; "Enter a string NOW: "
|           0x5585de631812      b800000000     mov eax, 0
|           0x5585de631817      e874feffff     call sym.imp.printf     ; int printf(const char *format)
|           0x5585de63181c      488d45a0       lea rax, qword [local_60h]
|           0x5585de631820      4889c7         mov rdi, rax
|           0x5585de631823      b800000000     mov eax, 0
|           0x5585de631828      e883feffff     call sym.imp.gets       ; char*gets(char *s)
|           0x5585de63182d      488d55a0       lea rdx, qword [local_60h]
|           0x5585de631831      488d45d0       lea rax, qword [local_30h]
|           0x5585de631835      4889d6         mov rsi, rdx
|           0x5585de631838      4889c7         mov rdi, rax
|           0x5585de63183b      e830feffff     call sym.imp.strcpy     ; char *strcpy(char *dest, const char *src)
|           0x5585de631840      488d45d0       lea rax, qword [local_30h]
|           0x5585de631844      4889c6         mov rsi, rax
|           0x5585de631847      488d3deb0000.  lea rdi, qword str.Copied_string_____s ; 0x5585de631939 ; "Copied string =  %s\n"
|           0x5585de63184e      b800000000     mov eax, 0
|           0x5585de631853      e838feffff     call sym.imp.printf     ; int printf(const char *format)
|           0x5585de631858      488d4da0       lea rcx, qword [local_60h]
|           0x5585de63185c      488d4596       lea rax, qword [local_6ah]
|           0x5585de631860      ba04000000     mov edx, 4
|           0x5585de631865      4889ce         mov rsi, rcx
|           0x5585de631868      4889c7         mov rdi, rax
|           0x5585de63186b      e8f0fdffff     call sym.imp.strncpy    ; char *strncpy(char *dest, const char *src, size_t  n)
|           0x5585de631870      488d4596       lea rax, qword [local_6ah]
|           0x5585de631874      4889c6         mov rsi, rax
|           0x5585de631877      488d3dd00000.  lea rdi, qword str.4_first_chars____s ; 0x5585de63194e ; "4 first chars = %s\n"
|           0x5585de63187e      b800000000     mov eax, 0
|           0x5585de631883      e808feffff     call sym.imp.printf     ; int printf(const char *format)
|           0x5585de631888      90             nop
|           0x5585de631889      488b4df8       mov rcx, qword [local_8h]
|           0x5585de63188d      6448330c2528.  xor rcx, qword fs:[0x28]
|       ,=< 0x5585de631896      7405           je 0x5585de63189d
|       |   0x5585de631898      e8e3fdffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|       `-> 0x5585de63189d      c9             leave
\           0x5585de63189e      c3             ret
[0x5585de6317f4]>

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

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

Код:
|           0x5585de63181c      488d45a0       lea rax, qword [local_60h]
|           0x5585de631820      4889c7         mov rdi, rax
|           0x5585de631823      b800000000     mov eax, 0
|           0x5585de631828      e883feffff     call sym.imp.gets       ; char*gets(char *s)
|           0x5585de63182d      488d55a0       lea rdx, qword [local_60h]
|           0x5585de631831      488d45d0       lea rax, qword [local_30h]
|           0x5585de631835      4889d6         mov rsi, rdx
|           0x5585de631838      4889c7         mov rdi, rax
|           0x5585de63183b      e830feffff     call sym.imp.strcpy     ; char *strcpy(c

Как мы видим, строка (пользовательский ввод) будет сохранена в local_60h, затем local_60h и local_30h будут переданы в strcpy, в этот момент мы уже знаем, что local_60h содержит пользовательский ввод, но local_30h? это будет уничтожение, поэтому пользовательский ввод будет скопирован туда. Давайте отладим это

Код:
[0x5585de6317f4]> db 0x5585de631840
[0x5585de6317f4]> dc
Enter a string NOW: SAMPLE TEXT
hit breakpoint at: 5585de631840
[0x5585de631840]> pdf
[...]
|           0x5585de631838      4889c7         mov rdi, rax
|           0x5585de63183b      e830feffff     call sym.imp.strcpy     ; char *strcpy(char *dest, const char *src)
|           ;-- rip:
|           0x5585de631840 b    488d45d0       lea rax, qword [local_30h]
|           0x5585de631844      4889c6         mov rsi, rax
|           0x5585de631847      488d3deb0000.  lea rdi, qword str.Copied_string_____s ;

И если мы проверим их:

Код:
[0x5585de631840]> afvd
var local_8h = 0x7fffd173cd38  0x71dec5cbb8695200   .Ri....q
var local_60h = 0x7fffd173cce0  0x5420454c504d4153   SAMPLE T @rsi ascii
var local_30h = 0x7fffd173cd10  0x5420454c504d4153   SAMPLE T @rdi ascii
var local_6ah = 0x7fffd173ccd6  0x0000000000000000   ........ r15
[0x5585de631840]>

[0x5585de631840]> pxw @ 0x7fffd173cce0
0x7fffd173cce0  0x504d4153 0x5420454c 0x00545845 0x00000000  SAMPLE TEXT.....
0x7fffd173ccf0  0x00000009 0x00000000 0x39a71660 0x00007fa0  ........`..9....
0x7fffd173cd00  0xd173cd68 0x00007fff 0x00f0b5ff 0x00000000  h.s.............
0x7fffd173cd10  0x504d4153 0x5420454c 0x00545845 0x00005585  SAMPLE TEXT..U..

Строка скопирована :) Также обратите внимание, что обе строки имеют \0-окончание, поэтому strcpy будет продолжать копирование, пока не встретит \0.

Позже, в коде, вызывается strNcpy. Это другая функция, "n" в ней относится к следующему: с помощью strncpy содержимое одной строки копируется в другую, но копируются только первые n байт. Давайте проверим это:

Код:
|           0x5585de631858      488d4da0       lea rcx, qword [local_60h]
|           0x5585de63185c      488d4596       lea rax, qword [local_6ah]
|           0x5585de631860      ba04000000     mov edx, 4
|           0x5585de631865      4889ce         mov rsi, rcx
|           0x5585de631868      4889c7         mov rdi, rax
|           0x5585de63186b      e8f0fdffff     call sym.imp.strncpy    ; char *strncpy(char *dest, const char *src, size_t  n)
|           0x5585de631870      488d4596       lea rax, qword [local_6ah]

Это очень просто, опять же, передаются оба адреса памяти происхождения/назначения, но также передается и 4, количество байт, которое нам нужно скопировать.

Код:
[0x5585de631840]> db 0x5585de631870
[0x5585de631840]> dc
Copied string =  SAMPLE TEXT
hit breakpoint at: 5585de631870
[0x5585de631840]> afvd
var local_8h = 0x7fffd173cd38  0x71dec5cbb8695200   .Ri....q
var local_60h = 0x7fffd173cce0  0x5420454c504d4153   SAMPLE T @rsi ascii
var local_30h = 0x7fffd173cd10  0x5420454c504d4153   SAMPLE T ascii
var local_6ah = 0x7fffd173ccd6  0x00000000504d4153   SAMP.... @rdi ascii

Было скопировано всего 4 байта. Важно отметить, что в случае с strncpy нулевой терминатор не вставляется по умолчанию, и это имеет смысл, потому что, возможно, наша цель - просто "слить" пару массивов, а не копировать строку в пустой массив.....

Если мы хотим разрезать массив, мы можем вручную вставить нулевой терминатор таким образом:

C:
# include <stdio.h>
# include <string.h>

main(){
 func();
 getchar();

}

func(){

    char text1[40], text2[40], text3[10];
  
    printf("ENTER A STRING: ");
    gets(text1);
 
    strcpy(text2, text1);
    printf("Copied string = %s\n", text2);
    strncpy(text3, text1, 4);
    text3[4] = '\0';
    printf("4 FIRST LETTERS %s\n", text3);
}

Как мы видим, в конце строки вручную добавляется ноль.

Код:
     0x561d7710e86b      e8f0fdffff     call sym.imp.strncpy    ; char *strncpy(char *dest, const char *src, size_t  n)
|           0x561d7710e870      c6459a00       mov byte [local_66h], 0
|           0x561d7710e874      488d4596       lea rax, qword [local_6ah]
|           0x561d7710e878      4889c6         mov rsi, rax
|           0x561d7710e87b      488d3dd70000.  lea rdi, qword str.4_FIRST_LETTERS__s ; 0x561d7710e959 ; "4 FIRST LETTERS %s\n"
|           0x561d7710e882      b800000000     mov eax, 0

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

Источник:

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

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