strсpy не отображается при дизассемблировании

В книге Хакинг. Искусство эксплойта приведен пример дизассемблирования программы, которая выглядит так:

#include <string.h>
#include <stdio.h>
int main()
{
    char str_a[20];
    
    strcpy(str_a, "Hello, world!\n");
    printf(str_a);
}

А результат её дизассемблирования - вот такой:

(gdb) x/5i $eip
0x80483c4 <main+16>:    mov    DWORD PTR [esp+4],0x80484c4
0x80483cc <main+24>:    lea    eax,[ebp-40]
0x80483cf <main+27>:    mov    DWORD PTR [esp],eax
0x80483d2 <main+30>:    call   0x80482c4 <strcpy@plt>
0x80483d7 <main+35>:    lea    eax,[ebp-40]

Я полностью переписал код на С и дизассемблировал программу, но в результате не увидел строки с вызовом функции strcpy().

Мой результат дизассемблирования:

(gdb) disassemble main
Dump of assembler code for function main:
   0x0000555555555139 <+0>:     push   rbp
   0x000055555555513a <+1>:     mov    rbp,rsp
   0x000055555555513d <+4>:     sub    rsp,0x20
=> 0x0000555555555141 <+8>:     lea    rax,[rbp-0x20]
   0x0000555555555145 <+12>:    mov    DWORD PTR [rax],0x6c6c6548
   0x000055555555514b <+18>:    mov    WORD PTR [rax+0x4],0x6f
   0x0000555555555151 <+24>:    lea    rax,[rbp-0x20]
   0x0000555555555155 <+28>:    mov    rdi,rax
   0x0000555555555158 <+31>:    mov    eax,0x0
   0x000055555555515d <+36>:    call   0x555555555030 <printf@plt>
   0x0000555555555162 <+41>:    mov    eax,0x0
   0x0000555555555167 <+46>:    leave
   0x0000555555555168 <+47>:    ret

Подскажите, пожалуйста, в чем может быть проблема?


Ответы (2 шт):

Автор решения: Stanislav Volodarskiy

Вы не увидите вызова strcpy в объектном коде. И переменной str_a тоже. Потому что современный компилятор знает что делает strcpy и понимает что его вызов можно убрать, а передать на печать адрес строкового литерала. Оптимизация.

Конкретно, я поместил ваш код в temp.c и запустил gcc -S temp.c чтобы получить ассемблер в temp.s. Это вместо компиляции и дизассеблирования. И получил абракадабру:

...
    leaq    -32(%rbp), %rax
    movabsq $8583909746840200520, %rdx
    movq    %rdx, (%rax)
    movabsq $2851464966991735, %rcx
    movq    %rcx, 7(%rax)
    leaq    -32(%rbp), %rax
    movq    %rax, %rdi
    movl    $0, %eax
    call    printf@PLT
...

Никакого вызова strcpy тут нет. Но есть интересное. Строка "Hello, world!\n" занимает в памяти пятнадцать байт. Процессор 64-битный, он может обработать до восьми байт за раз. Инструкция movabsq $8583909746840200520, %rdx копирует первые восемь символов в регистр rdx, следующая инструкция сохраняет его в буфер на стеке. Значение 8583909746840200520 соответствует символам "Hello, w" без завершающего нуля.

Значение 2851464966991735 из следующей movabsq"world!\n" с завершающим нулём. Две строки перекрываются на один символ, потому что в распоряжении компилятора только инструкция для сохранения восьми байт, а нам нужно пятнадцать в сумме.

Так что strcpy здесь нет, и литерала нет. Переменная на стеке есть, только у неё размер – 32 байта, не двадцать.

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

Можно заставить GCC использовать strcpy командой gcc -S -fno-builtin-strcpy temp.c:

...
.LC0:
    .string "Hello, world!\n"
...
    leaq    -32(%rbp), %rax
    leaq    .LC0(%rip), %rdx
    movq    %rdx, %rsi
    movq    %rax, %rdi
    call    strcpy@PLT
    leaq    -32(%rbp), %rax
    movq    %rax, %rdi
    movl    $0, %eax
    call    printf@PLT
...

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

→ Ссылка
Автор решения: Serge3leo

Подскажите, пожалуйста, в чем может быть проблема?

В зависимости от уровня оптимизации, стандартные функции С/С++ могут подставляться в точке вызова с целью сокращения и ускорения кода.

При использовании GCC (и не только) Вы можете использовать ключи -fno-builtin (отключить все встроенные функции) или -fno-builtin-strcpy (отключить только встроенную strcpy()).

Код и результаты, в зависимости от компилятора и ключей, можно взглянуть: https://godbolt.org/z/na7zjG7e4

P.S.

Это вольный перевод ответа https://stackoverflow.com/a/13059603/8585880 @R.. GitHub STOP HELPING ICE на идентичный вопрос enSO. (На мой непросвещённый взгляд, на ruSO был бы небесполезен инструмент авторизованного перевода, но .. Нужны ли переводы на Stack Overflow? Если да, какой вы видите систему?)

→ Ссылка