R0 CREW

Adventure with Stack Smashing Protector (SSP)

expdev
linux
reverse
ru
#1

Оригинал: pi3.com.pl (PDF)

(Рекомендация почтению от переводчика: в статье присутствует несколько очень больших логов, их лучше смотреть в оригинале, там есть несколько выделений и сохранены все отступы)

Введение

Я вовсю игрался со Stack Smashing Protector’ом пару лет назад. Некоторые мои исследования (наблюдения) я решил опубликовать с журнале phrack, но не все. Два года назад моя профессиональная жизнь перешла к среде Windows и, к сожалению, у меня не было так много времени для игр с миром UNIX как раньше. В один уикэнд я решил повторно исследовать код SSP, и этот отчет описывает несколько моих наблюдений, сделанных в это время…

… и которые можно кратко обобщить как (детали можно найти в секции “Случайные идеи”):

Не связанные с безопасностью…

  1. Мы можем изменять имя программы (с позиции SSP) через перезапись области памяти, куда указывает указатель “argv[0]”.

  2. Мы можем обрушить код Stack Smashing Protector’а многими путями:

    • Через порчу области памяти, на которую указывает переменная “__environ”.
    • Через установку “LIBC_FATAL_STDERR_” на край валидных адресов.
    • Через принуждение “alloca()” к провалу – например, через исчерпание стэка.
    • Есть еще одна ошибка, которую я исследую более комплексно на шаге 4. Она может косвенно заставить обрушиться SSP. Она находится в стэк-машине DWARF, которая отвечает за сбор информации о трассировке стэка ("__backtrace()") и ее распечатку.
  3. Мы можем слегка изменять поток исполнения SSP. К (не)счастью, это никак не влияет на основной поток (но что на счет безопасности?). Возможны следующие сценарии:

    • Заставить SSP открыть “/dev/tty”
    • Заставить SSP не открывать “/dev/tty” и присвоить дескриптору “fd” значение “STDERR_FILENO”:
      #define STDERR_FILENO 2 /* Standard error output. */
      
    • Обрушение SSP через сценарий 2b
  4. Мы можем косвенно обрушить SSP через раскрутку алгоритма (read-AV или нас могут убить функции “gcc_unreachable” или “gcc_assert”) – стэк-машины DWARF:

    • Имитацией FDE object was not found
    • Имитацией FDE object was found.

Как-то связано с безопасностью… (детали приведены в секции “Случайные идеи”):

  1. Мы можем заставить SSP выделить много памяти и привести к Denial of Service через атаку Resource Exhaustion.
  2. Теоретическая утечка инфы:
    • Утечка инфы о печеньке в стэке.
    • Любые виды утечки инфы
    • Порча файлов.

Stack Smashing Protector (также известный как ProPolice) под микроскопом.

GNU Compiler Collection (GCC) используется очень часто и (эта коллекция) включает в себя реализацию SSP. Давайте исследуем всю цепочку кода, вызываемую во время исполнения SSP. Если порча стэка обнаружена (стэковая канарейка не является действительной), вызывается следующая функция:

"debug/stack_chk_fail.c"

void __attribute__ ((noreturn)) __stack_chk_fail (void)
{
  __fortify_fail ("stack smashing detected");
}

Нечего здесь предполагать, так что двигаемся дальше:

"sysdeps/unix/sysv/linux/libc_fatal.c"

/* Abort with an error message. */
void __libc_message (int do_abort, const char *fmt, ...)
{
  va_list ap;
  va_list ap_copy;
  int fd = -1;
  va_start (ap, fmt);
  va_copy (ap_copy, ap);
  
  #ifdef FATAL_PREPARE
    FATAL_PREPARE;
  #endif

  /* Open a descriptor for /dev/tty unless the user explicitly requests errors on standard error. */
  const char *on_2 = __libc_secure_getenv ("LIBC_FATAL_STDERR_");
  if (on_2 == NULL || *on_2 == '\0')
    fd = open_not_cancel_2 (_PATH_TTY, O_RDWR | O_NOCTTY | O_NDELAY);
  if (fd == -1)
    fd = STDERR_FILENO;
  struct str_list *list = NULL;
  int nlist = 0;
  const char *cp = fmt;
  while (*cp != '\0')
  {
    /* Find the next "%s" or the end of the string. */
    const char *next = cp;
    while (next[0] != '%' || next[1] != 's')
    {
      next = __strchrnul (next + 1, '%');
      if (next[0] == '\0')
      break;
    }
    /* Determine what to print. */
    const char *str;
    size_t len;
    if (cp[0] == '%' && cp[1] == 's')
    {
      str = va_arg (ap, const char *);
      len = strlen (str);
      cp += 2;
    }
    else
    {
      str = cp;
      len = next - cp;
      cp = next;
    }
    struct str_list *newp = alloca (sizeof (struct str_list));
    newp->str = str;
    newp->len = len;
    newp->next = list;
    list = newp;
    ++nlist;
  }
  bool written = false;
  if (nlist > 0)
  {
    struct iovec *iov = alloca (nlist * sizeof (struct iovec));
    ssize_t total = 0;
    for (int cnt = nlist - 1; cnt >= 0; --cnt)
    {
      iov[cnt].iov_base = (void *) list->str;
      iov[cnt].iov_len = list->len;
      total += list->len;
      list = list->next;
    }
    INTERNAL_SYSCALL_DECL (err);
    ssize_t cnt;
    do
      cnt = INTERNAL_SYSCALL (writev, err, 3, fd, iov, nlist);
    while (INTERNAL_SYSCALL_ERROR_P (cnt, err) && INTERNAL_SYSCALL_ERRNO (cnt, err) == EINTR);
    if (cnt == total)
      written = true;
    if (do_abort)
    {
      total = ((total + 1 + GLRO(dl_pagesize) - 1) & ~(GLRO(dl_pagesize) - 1));
      struct abort_msg_s *buf = __mmap (NULL, total, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
      if (__builtin_expect (buf != MAP_FAILED, 1))
      {
        buf->size = total;
        char *wp = buf->msg;
        for (int cnt = 0; cnt < nlist; ++cnt)
          wp = mempcpy (wp, iov[cnt].iov_base, iov[cnt].iov_len);
        *wp = '\0';
        /* We have to free the old buffer since the application might catch the SIGABRT signal. */
        struct abort_msg_s *old = atomic_exchange_acq(&__abort_msg, buf);
        if (old != NULL)
          __munmap (old, old->size);
      }
    }
  }
  va_end (ap);
  /* If we had no success writing the message, use syslog. */
  if (! written)
    vsyslog (LOG_ERR, fmt, ap_copy);
  va_end (ap_copy);
  if (do_abort)
  {
    if (do_abort > 1 && written)
    {
      void *addrs[64];
      #define naddrs (sizeof (addrs) / sizeof (addrs[0]))
      int n = __backtrace (addrs, naddrs);
      if (n > 2)
      {
        #define strnsize(str) str, strlen (str)
        #define writestr(str) write_not_cancel (fd, str)
        writestr (strnsize ("======= Backtrace: =========\n"));
        __backtrace_symbols_fd (addrs + 1, n - 1, fd);
        writestr (strnsize ("======= Memory map: ========\n"));
        int fd2 = open_not_cancel_2 ("/proc/self/maps", O_RDONLY);
        char buf[1024];
        ssize_t n2;
        while ((n2 = read_not_cancel (fd2, buf, sizeof (buf))) > 0)
          if (write_not_cancel (fd, buf, n2) != n2)
            break;
        close_not_cancel_no_status (fd2);
      }
    }
    /* Terminate the process. */
    abort ();
  }
}

Что интересного в этой функции? Во-первых, до вызова функции abort() выполняется много (слишком много ;)) кода. Эта идея не удивительна, т.к. нельзя доверять испорченному процессу, и выполнение любого кода (особенно кода, полагающегося на указатель[и]) является неожиданным. Давайте поближе рассмотрим следующую строку:

const char *on_2 = __libc_secure_getenv ("LIBC_FATAL_STDERR_");

Которая, по сути, выполняет:

"stdlib/secure-getenv.c"

char * __libc_secure_getenv (name) 
  const char *name;
{
  return __libc_enable_secure ? NULL : getenv (name);
}

Двигаемся дальше:

"stdlib/getenv.c"

char * getenv (name) 
  const char *name;
{
  size_t len = strlen (name);
  char **ep;
  uint16_t name_start;
  
  if (__environ == NULL || name[0] == '\0')
    return NULL;
    
  if (name[1] == '\0')
  {
    /* The name of the variable consists of only one character. Therefore
    the first two characters of the environment entry are this character
    and a '=' character. */
    
    #if __BYTE_ORDER == __LITTLE_ENDIAN || !_STRING_ARCH_unaligned
      name_start = ('=' << 8) | *(const unsigned char *) name;
    #else
      #if __BYTE_ORDER == __BIG_ENDIAN
        name_start = '=' | ((*(const unsigned char *) name) << 8);
      #else
        #error "Funny byte order."
      #endif
    #endif
    for (ep = __environ; *ep != NULL; ++ep)
    {
      #if _STRING_ARCH_unaligned
        uint16_t ep_start = *(uint16_t *) *ep;
      #else
        uint16_t ep_start = (((unsigned char *) *ep)[0] | (((unsigned char *) *ep)[1] << 8));
      #endif
      if (name_start == ep_start)
        return &(*ep)[2];
    }
  }
  else
  {
    #if _STRING_ARCH_unaligned
      name_start = *(const uint16_t *) name;
    #else
      name_start = (((const unsigned char *) name)[0] | (((const unsigned char *) name)[1] << 8));
    #endif
    len -= 2;
    name += 2;
    for (ep = __environ; *ep != NULL; ++ep)
    {
      #if _STRING_ARCH_unaligned
        uint16_t ep_start = *(uint16_t *) *ep;
      #else
        uint16_t ep_start = (((unsigned char *) *ep)[0] | (((unsigned char *) *ep)[1] << 8));
      #endif
      if (name_start == ep_start && !strncmp (*ep + 2, name, len) && (*ep)[len + 2] == '=')
        return &(*ep)[len + 3];
    }
  }
  return NULL;
}
libc_hidden_def (getenv)

По существу эта функция идет через блок/массив среды (указатель на указатели) и проверяет если первые два символа эквивалентны таковым от аргументов. Если да, то функция проверяет оставшуюся часть строки. Последний шаг этой функции - проверить присутствие символа ‘=’ и, в этом случае, вернуть адрес следующего байта.

Опять-таки, этот код полагается на указатели, которые могут быть испорчены. Мы легко можем здесь обрушить этот код когда он ссылается на блоки из блок/массива среды (что я докажу позже).

Дополнительно, байты после символа ‘=’ не проверяются и возвращается просто адрес. После исполнения функции getenv(), выполняется следующий код:

/* Open a descriptor for /dev/tty unless the user explicitly requests errors on standard error. */
const char *on_2 = __libc_secure_getenv ("LIBC_FATAL_STDERR_");
if (on_2 == NULL || *on_2 == '\0')
  fd = open_not_cancel_2 (_PATH_TTY, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
  fd = STDERR_FILENO;

Если функция __libc_secure_getenv() вернула не NULL, код ссылается на адрес и проверяется не указывает ли этот адрес на NULL. После прохождения обеих проверок, вызывается функция open_not_cancel_2(). В конце просто выполняется функция open(). Что за _PATH_TTY?

#define _PATH_TTY "/dev/tty"

Мы можем выполнить (управлять) 3 возможных сценария:

  1. Заставить SSP открыть /dev/tty.
  2. Заставить SSP НЕ открывать /dev/tty и присвоить дескриптору “fd” значение “STDERR_FILENO”, что означает:
    #define STDERR_FILENO 2 /* Standard error output. */
    
  3. Обрушить SSP через установку LIBC_FATAL_STDERR_ на край валидных адресов.

SSP может быть обрушен гораздо более легким (чем 3-ий сценарий) путем, просто полностью испортив блок среды (переменную “__environ”).

Другой интересный кусок кода динамически выделяет память через функцию alloca(). Для примера:

...
  struct str_list *newp = alloca (sizeof (struct str_list));
  newp->str = str;
  newp->len = len;
  newp->next = list;
  list = newp;
  ++nlist;
...
  struct iovec *iov = alloca (nlist * sizeof (struct iovec));
  ssize_t total = 0;
  for (int cnt = nlist - 1; cnt >= 0; --cnt)
  {
    iov[cnt].iov_base = (void *) list->str;
    iov[cnt].iov_len = list->len;
    total += list->len;
    list = list->next;
  }
...

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

и продолжая:

Ладно. Давайте сейчас исследуем кое-что более интересное. Давайте взглянем на следующий код:

--- CUT ---

  struct str_list *list = NULL;
  int nlist = 0;
  const char *cp = fmt;
  while (*cp != '\0')
  {
    ...
    ...
    /* Determine what to print. */
    const char *str;
    size_t len;
    if (cp[0] == '%' && cp[1] == 's')
    {
      str = va_arg (ap, const char *);
  [1] len = strlen (str);
      cp += 2;
    }
    ...
    ...
  }
  bool written = false;
  if (nlist > 0)
  {
    struct iovec *iov = alloca (nlist * sizeof (struct iovec));
    ssize_t total = 0;
    for (int cnt = nlist - 1; cnt >= 0; --cnt)
    {
      iov[cnt].iov_base = (void *) list->str;
      iov[cnt].iov_len = list->len;
  [2] total += list->len;
      list = list->next;
    }
    ...
    ...
    if (do_abort)
    {
  [3] total = ((total + 1 + GLRO(dl_pagesize) - 1) & ~(GLRO(dl_pagesize) - 1));
  [4] struct abort_msg_s *buf = __mmap (NULL, total, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
      if (__builtin_expect (buf != MAP_FAILED, 1))
      {
  [5]   buf->size = total;
  [6]   char *wp = buf->msg;
        for (int cnt = 0; cnt < nlist; ++cnt)
  [7]     wp = mempcpy (wp, iov[cnt].iov_base, iov[cnt].iov_len);
        *wp = '\0';
        /* We have to free the old buffer since the application might catch the SIGABRT signal. */
        struct abort_msg_s *old = atomic_exchange_acq(&__abort_msg, buf);
        if (old != NULL)
          __munmap (old, old->size);
      }
    }
  }
  ...
  ...
  ...
  }
  
--- CUT ---

Я добавил несколько тэгов с номерами. Начнем с первого [1] тэга. Если формат функции включает какую-либо последовательность строкового аргумента ("%s"), она будет извлечена (va_arg()), присвоена переменной “str”, будет вычислена длина (через strlen()) и присвоена перменной “len”. Этот сценарий будет выполнен для каждого форматировщика “%s”.

Это очень важно, т.к. мы можем контролировать один из аргументов строкового форматировщика. Если мы посмотрим ближе на начало этой работы, мы поймем почему. Короткое напоминание:

__libc_message (2, "*** %s ***: %s terminated\n", msg, __libc_argv[0] ?: "<unknown>");

Мы можем переполнить память там, куда указывает указатель “__libc_argv[0]” и изменить отображаемое имя обрушенной программы. Что даже еще важнее - мы можем изменить поведение переменных “len” и “str”. На самом деле, лишь в какой-то мере мы можем управлять переменной “len”.

Далее, переходя ко второму [2] тэгу, мы обнаружим, что мы также можем напрямую управлять переменной “total”. Эта переменная обновляется при каждой итерации цикла. Код внутри просто проходит через список (построенный ранее) и для каждой подстроки вычисляет длину и обновляет “базу данных” “iov”. Переменная “total” хранит полную длину, вычисленную из каждой подстроки.

На строке [3], переменная “total” пересчитывается (выровнен к странице) и на строке [4] используется как аргумент размера для функции “__mmap” (динамическое выделение памяти). Большинство из нас, вероятно, понимает, что мы можем решать сколько памяти будет динамически выделено. Далее (строка [5]) “total” хранится в метаданных свеже-выделенного буфера. На строке [6] динамический буфер назначается временному указателю. Строка [7] находится внутри цикла, который “извлекает” созданную раннее “базу данных” “iov” и копирует все данные в свеже-выделенную память.

Что мы можем получить через этот сценарий? Мы можем заставить SSP выделить большой кусок памяти, на который можно потом сослаться и куда можно что-то скопировать. Это может привести к маленькой атаке “истощения ресурсов”.

Последний этап функции “__libc_message” заключается в выполнении следующего кода в случае если “do_abort” объявлен (как в нашем случае):

if (do_abort)
{
  if (do_abort > 1 && written)
  {
    void *addrs[64];
    #define naddrs (sizeof (addrs) / sizeof (addrs[0]))
    int n = __backtrace (addrs, naddrs);
    if (n > 2)
    {
      #define strnsize(str) str, strlen (str)
      #define writestr(str) write_not_cancel (fd, str)
      writestr (strnsize ("======= Backtrace: =========\n"));
      __backtrace_symbols_fd (addrs + 1, n - 1, fd);
      writestr (strnsize ("======= Memory map: ========\n"));
      int fd2 = open_not_cancel_2 ("/proc/self/maps", O_RDONLY);
      char buf[1024];
      ssize_t n2;
      while ((n2 = read_not_cancel (fd2, buf, sizeof (buf))) > 0)
        if (write_not_cancel (fd, buf, n2) != n2)
          break;
      close_not_cancel_no_status (fd2);
    }
  }
  /* Terminate the process. */
  abort ();
}

Самое важное и интересное - функция “__backtrace”. Очень просто обрушить код SSP на read access violation (AV) в глубине вызовов функции через функцию “__backtrace”. Бэктрейсинг в gcc вовсю использует DWARF. Прежде чем мы исследуем исходные коды в деталях, следует рассказать немного больше о том, как и для чего используется DWARF…

“Debugging With Attributed Record Formats” – DWARF

DWARF - формат отладки, используемый для описания программ на C и других подобных языках программирования. Он наиболее широко связан с объектным форматом ELF, но он использовался и с другими объектными форматами. Дополнительно, gcc в основном использует механизм DWARF для раскрутки стэка, а также в C++ для обработки исключений.

Чтобы обработать исключение, стэк должен быть размотан. К сожалению, эта проблема не может быть сведена просто к обходу стэка вызовов, следующим за указателями адресов возврата, в поиске всех кадров вызовов. В основном потому, что этой информации не достаточно для восстановления выполнения для обработчика исключения, т.к. этот процесс не смотрит на состояние регистров. Чтобы решить эти проблемы, секция Call-Frame Information (инфа по размотке) стандарта DWARF была адаптирована (с некоторыми изменениями) для обработки исключений.

Вот цитата из великолепной исследовательской работы “Exploiting the hard-working dwarf” от James Oakley и Sergey Bratus:

"Концептуально то, что описывает размотанная информация - это огромная таблица. Строки таблицы соотносятся с машинными инструкциями в тексте программы, а столбцы соотносятся с регистрами и каноническим адресом кадра (Canonical Frame Address - CFA). Каждая строка описывает как восстановить состояние машины (значения регистров и CFA) для каждой инструкции на предыдущем кадре вызова так, будто бы управление возвращалось на стэк по этой инструкции. DWARF позволяет идентифицировать произвольное количество регистров всего лишь номерами. Это зависит от индивидуальных ABI, которые определяют соответствие между номерами регистров DWARF и аппаратными регистрами. DWARF-регистры не обязаны указывать на настоящие аппаратные регистры, но могут использоваться для внутренних нужд, как это часто делается с DWARF-регистром для адреса возврата.

Каждая клетка этой таблицы содержит правило, описывающее как будет восстановлено содержимое регистра для предыдущего кадра вызова. DWARF поддерживает несколько типов правил и любопытный читатель волен найти их в стандарте DWARF. Большинство регистров восстанавливаются либо из другого регистра, либо из позиции памяти, доступ к которой ведется по какому-то смещению от CFA.

Мы заметили что эта таблица, будучи полностью сформированной, была бы обсурдно большой; большей, чем текст самой программы. В ней много пустых клеток и много дуплицированных записей в столбцах. Очень многое в стандарте DWARF для вызова кадра относится к технике сжатия, которая позволяет предоставить достаточно информации во время работы программы для сборки должным образом частей таблицы без полной, а стало быть огромной, таблицы, без необходимости в ее полном формировании и хранении. Это сжатие выполняется с помощью
введения концепции Frame Description Entities (FDE или Описательные Сущности Кадра) и DWARF-инструкций. FDE соответствует логическому блоку программного текста и описывает как может быть выполнено развертывание в рамках этого блока. Каждая FDE содержит последова-
тельность DWARF-инструкций. Каждая инструкция либо определяет правила (регистры) одного из столбцов (как в нашей таблице выше), либо определяет к каким позициям текста будут применены правила."

Больше информации можно найти на сайте DWARF Debugging Standard.

Лучше всего понять как обработчик кода закодирован и вызывается, т.к. концепция обратной трассировки в gcc очень похожа (честно говоря, почти то же самое, за исключением вызова EH - индивидуальный код) на обратную трассировку, которую мы хотим проанализировать (здесь про функцию “__backtrace”). Я снова хочу процитировать работу Джеймса Оклей и Сергея Братуса:

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

"sysdeps/x86_64/backtrace.c"

int __backtrace (array, size) 
  void **array;
  int size;
{
  struct trace_arg arg = { .array = array, .cfa = 0, .size = size, .cnt = -1 };
  #ifdef SHARED
    __libc_once_define (static, once);
    __libc_once (once, init);
    if (unwind_backtrace == NULL)
      return 0;
  #endif
  if (size >= 1)
    unwind_backtrace (backtrace_helper, &arg);
  /* _Unwind_Backtrace seems to put NULL address above _start. Fix it up here. */
  if (arg.cnt > 1 && arg.array[arg.cnt - 1] == NULL)
    --arg.cnt;
  return arg.cnt != -1 ? arg.cnt : 0;
}
weak_alias (__backtrace, backtrace)
libc_hidden_def (__backtrace)

Где:

static _Unwind_Reason_Code (*unwind_backtrace) (_Unwind_Trace_Fn, void *);
...
static void *libgcc_handle;
...
...
libgcc_handle = __libc_dlopen ("libgcc_s.so.1");
...
unwind_backtrace = __libc_dlsym (libgcc_handle, "_Unwind_Backtrace");
...

Прежде чем мы двинемся к функции “_Unwind_Backtrace”, рассмотрим вспомогательную функцию, переданную ей как аргумент:

static _Unwind_Reason_Code backtrace_helper (struct _Unwind_Context *ctx, void *a)
{
  struct trace_arg *arg = a;
  /* We are first called with address in the __backtrace function. Skip it. */
  if (arg->cnt != -1)
  {
    arg->array[arg->cnt] = (void *) unwind_getip (ctx);
    /* Check whether we make any progress. */
    _Unwind_Word cfa = unwind_getcfa (ctx);
    if (arg->cnt > 0 && arg->array[arg->cnt - 1] == arg->array[arg->cnt] && cfa == arg->cfa)
      return _URC_END_OF_STACK;
    arg->cfa = cfa;
  }
  if (++arg->cnt == arg->size)
    return _URC_END_OF_STACK;
  return _URC_NO_REASON;
}

Где:

unwind_getip = __libc_dlsym (libgcc_handle, "_Unwind_GetIP");
unwind_getcfa = (__libc_dlsym (libgcc_handle, "_Unwind_GetCFA")? : dummy_getcfa);

inline _Unwind_Ptr _Unwind_GetIP (struct _Unwind_Context *context)
{
  return (_Unwind_Ptr) context->ra;
}

_Unwind_Word _Unwind_GetCFA (struct _Unwind_Context *context)
{
  return (_Unwind_Ptr) context->cfa;
}

Короче говоря, вспомогательная функция ответственна за проверку есть ли какой-нибудь “прогресс” в раскрутке стэка через анализ CFA. Она также предотвращает от зацикливания вокруг одних и тех же кадров. Возвращение в основную функцию раскрутки:

"libgcc/unwind.inc"

/* Perform stack backtrace through unwind data. */

_Unwind_Reason_Code LIBGCC2_UNWIND_ATTRIBUTE _Unwind_Backtrace(_Unwind_Trace_Fn trace, void * trace_argument)
{
  struct _Unwind_Context context;
  _Unwind_Reason_Code code;
  uw_init_context (&context);
  while (1)
  {
    _Unwind_FrameState fs;
    /* Set up fs to describe the FDE for the caller of context. */
    code = uw_frame_state_for (&context, &fs);
    if (code != _URC_NO_REASON && code != _URC_END_OF_STACK)
      return _URC_FATAL_PHASE1_ERROR;
    /* Call trace function. */
    if ((*trace) (&context, trace_argument) != _URC_NO_REASON)
      return _URC_FATAL_PHASE1_ERROR;
    /* We're done at end of stack. */
    if (code == _URC_END_OF_STACK)
      break;
    /* Update context to describe the same frame as fs. */
    uw_update_context (&context, &fs);
  }
  return code;
}

Короче говоря, эта функция ответственна за установку текущего кадра состояния (переменная “fs”) на основе текущего контекста. После обновления “контекста” для следующего кадра, парсинг запускается снова. Этот бесконечный цикл прервется если алгоритм обнаружит, что текущий кадр является последним ("_URC_END_OF_STACK"). Давайте перейдем к самой важной функции этого алгоритма:

"libgcc/unwind-dw2.c"

/* Given the _Unwind_Context CONTEXT for a stack frame, look up the FDE for its
caller and decode it into FS. This function also sets the args_size and lsda members
of CONTEXT, as they are really information about the caller's frame. */

static _Unwind_Reason_Code uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs)
{
  const struct dwarf_fde *fde;
  const struct dwarf_cie *cie;
  const unsigned char *aug, *insn, *end;
  memset (fs, 0, sizeof (*fs));
  context->args_size = 0;
  context->lsda = 0;
  if (context->ra == 0)
    return _URC_END_OF_STACK;
  fde = _Unwind_Find_FDE (context->ra + _Unwind_IsSignalFrame(context) - 1, &context->bases);
  if (fde == NULL)
  {
    #ifdef MD_FALLBACK_FRAME_STATE_FOR
      /* Couldn't find frame unwind info for this function. Try a target-specific 
      fallback mechanism. This will necessarily not provide a personality routine or LSDA. */
      return MD_FALLBACK_FRAME_STATE_FOR (context, fs);
    #else
      return _URC_END_OF_STACK;
    #endif
  }
  fs->pc = context->bases.func;
  cie = get_cie (fde);
  insn = extract_cie_info (cie, context, fs);
  if (insn == NULL)
    /* CIE contained unknown augmentation. */
    return _URC_FATAL_PHASE1_ERROR;
  /* First decode all the insns in the CIE. */
  end = (const unsigned char *) next_fde ((const struct dwarf_fde*) cie);
  execute_cfa_program (insn, end, context, fs);
  /* Locate augmentation for the fde. */
  aug = (const unsigned char *) fde + sizeof (*fde);
  aug += 2 * size_of_encoded_value (fs->fde_encoding);
  insn = NULL;
  if (fs->saw_z)
  {
    _uleb128_t i;
    aug = read_uleb128 (aug, &i);
    insn = aug + i;
  }
  if (fs->lsda_encoding != DW_EH_PE_omit)
  {
    _Unwind_Ptr lsda;
    aug = read_encoded_value (context, fs->lsda_encoding, aug, &lsda);
    context->lsda = (void *) lsda;
  }

  /* Then the insns in the FDE up to our target PC. */
  if (insn == NULL)
  insn = aug;
  end = (const unsigned char *) next_fde (fde);
  execute_cfa_program (insn, end, context, fs);
  return _URC_NO_REASON;
}

Если адрес возврата текущего контекста равен 0 (что указывает на конец стэка) функция немедленно возвращается. Иначе вызывается мудренная функция “_Unwind_Find_FDE”. Основная задача этой функции – найти FDE-объект на основе текущего контекста, и вернуть адрес возврата:

const fde * _Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
{
  struct object *ob;
  const fde *f = NULL;
  init_object_mutex_once ();
  __gthread_mutex_lock (&object_mutex);
  /* Linear search through the classified objects, to find the one containing the pc.
  Note that pc_begin is sorted descending, and we expect objects to be non-overlapping. */
  for (ob = seen_objects; ob; ob = ob->next)
    if (pc >= ob->pc_begin)
    {
      f = search_object (ob, pc);
      if (f)
        goto fini;
      break;
    }
  /* Classify and search the objects we've not yet processed. */
  while ((ob = unseen_objects))
  {
    struct object **p;
    unseen_objects = ob->next;
    f = search_object (ob, pc);
    /* Insert the object into the classified list. */
    for (p = &seen_objects; *p ; p = &(*p)->next)
      if ((*p)->pc_begin < ob->pc_begin)
        break;
    ob->next = *p;
    *p = ob;
    if (f)
      goto fini;
  }
  fini:
  __gthread_mutex_unlock (&object_mutex);
  if (f)
  {
    int encoding;
    _Unwind_Ptr func;
    bases->tbase = ob->tbase;
    bases->dbase = ob->dbase;
    encoding = ob->s.b.encoding;
    if (ob->s.b.mixed_encoding)
      encoding = get_fde_encoding (f);
    read_encoded_value_with_base (encoding, base_from_object (encoding, ob), f->pc_begin, &func);
    bases->func = (void *) func;
  }
  return f;
}

Эта функция ответственна за нахождение FDE-объекта на основе текущего адреса возрата из кадра (который мы можем полностью контролировать). Ключевой является функция “search_object”:

static const fde * search_object (struct object* ob, void *pc)
{
  /* If the data hasn't been sorted, try to do this now. We may
  have   more memory available than last time we tried. */
  if (! ob->s.b.sorted)
  {
    init_object (ob);
    /* Despite the above comment, the normal reason to get here is that we've 
    not processed this object before. A quick range check is in order. */
    if (pc < ob->pc_begin)
      return NULL;
  }

  if (ob->s.b.sorted)
  {
    if (ob->s.b.mixed_encoding)
      return binary_search_mixed_encoding_fdes (ob, pc);
    else if (ob->s.b.encoding == DW_EH_PE_absptr)
      return binary_search_unencoded_fdes (ob, pc);
    else
      return binary_search_single_encoding_fdes (ob, pc);
  }
  else
  {
    /* Long slow laborious linear search, cos we've no memory. */
    if (ob->s.b.from_array)
    {
      fde **p;
      for (p = ob->u.array; *p ; p++)
      {
        const fde *f = linear_search_fdes (ob, *p, pc);
        if (f)
          return f;
      }
      return NULL;
    }
    else
      return linear_search_fdes (ob, ob->u.single, pc);
  }
}

В основном, выполняются различного рода алгоритмы поиска (“binary_search_mixed_encoding_fdes”, “binary_search_unencoded_fdes”, “binary_search_single_encoding_fdes”, “linear_search_fdes”). Каждая из функций зависит от адреса возврата как от поля поиска. Так как при ошибках переполнения стэка мы полностью управляем адресом возврата, мы можем указать его в памяти, где специально подготовленные байты могут быть приняты за действительные (или нет), и можем выбрать конкретный FDE-объект, находящийся в процессе.

Далее, основываясь на том, что нашла (или не нашла) “_Unwind_Find_FDE”, может быть вычислен CIE-объект. Привожу внутренние комментарии исходного кода gcc:

Это довольно интересная ситуация, т.к. мы можем выбирать какую ветвь кода выполнять. Давайте сперва сэмулируем (исследуем) что полегче – ни один из FDE-объектов не найден. В этом случае выполняются следующие строки:

if (fde == NULL)
{
  #ifdef MD_FALLBACK_FRAME_STATE_FOR
    /* Couldn't find frame unwind info for this function. Try a target-specific fallback 
    mechanism. This will necessarily not provide a personality routine or LSDA. */
    return MD_FALLBACK_FRAME_STATE_FOR (context, fs);
  #else
    return _URC_END_OF_STACK;
  #endif
}

“MD_FALLBACK_FRAME_STATE_FOR” определен по-умолчанию так:

obj-x86_64-redhat-linux/x86_64-redhat-linux/libgcc/md-unwindsupport.h:
#define MD_FALLBACK_FRAME_STATE_FOR x86_64_fallback_frame_state

obj-x86_64-redhat-linux/x86_64-redhat-linux/libgcc/md-unwindsupport.h:
#define MD_FALLBACK_FRAME_STATE_FOR x86_fallback_frame_state

Я использую 64-битный VM для этой работы, так что этот случай будет проанализирован. К счастью, между ними нет слишком много различий:

static _Unwind_Reason_Code x86_64_fallback_frame_state (struct _Unwind_Context *context, _Unwind_FrameState *fs)
{
  unsigned char *pc = context->ra;
  struct sigcontext *sc;
  long new_cfa;
  /* movq $__NR_rt_sigreturn, %rax ; syscall. */
  #ifdef __LP64__
    #define RT_SIGRETURN_SYSCALL 0x050f0000000fc0c7ULL
  #else
    #define RT_SIGRETURN_SYSCALL 0x050f40000201c0c7ULL
  #endif
  if (*(unsigned char *)(pc+0) == 0x48 && *(unsigned long long *)(pc+1) == RT_SIGRETURN_SYSCALL)
  {
    struct ucontext *uc_ = context->cfa;
    /* The void * cast is necessary to avoid an aliasing warning. The aliasing warning 
    is correct, but should not be a problem because it does not alias anything. */
    sc = (struct sigcontext *) (void *) &uc_->uc_mcontext;
  }
  else
    return _URC_END_OF_STACK;
  new_cfa = sc->rsp;
  fs->regs.cfa_how = CFA_REG_OFFSET;
  /* Register 7 is rsp */
  fs->regs.cfa_reg = 7;
  fs->regs.cfa_offset = new_cfa - (long) context->cfa;
  /* The SVR4 register numbering macros aren't usable in libgcc. */
  fs->regs.reg[0].how = REG_SAVED_OFFSET;
  fs->regs.reg[0].loc.offset = (long)&sc->rax - new_cfa;
  fs->regs.reg[1].how = REG_SAVED_OFFSET;
  fs->regs.reg[1].loc.offset = (long)&sc->rdx - new_cfa;
  fs->regs.reg[2].how = REG_SAVED_OFFSET;
  fs->regs.reg[2].loc.offset = (long)&sc->rcx - new_cfa;
  fs->regs.reg[3].how = REG_SAVED_OFFSET;
  fs->regs.reg[3].loc.offset = (long)&sc->rbx - new_cfa;
  fs->regs.reg[4].how = REG_SAVED_OFFSET;
  fs->regs.reg[4].loc.offset = (long)&sc->rsi - new_cfa;
  fs->regs.reg[5].how = REG_SAVED_OFFSET;
  fs->regs.reg[5].loc.offset = (long)&sc->rdi - new_cfa;
  fs->regs.reg[6].how = REG_SAVED_OFFSET;
  fs->regs.reg[6].loc.offset = (long)&sc->rbp - new_cfa;
  fs->regs.reg[8].how = REG_SAVED_OFFSET;
  fs->regs.reg[8].loc.offset = (long)&sc->r8 - new_cfa;
  fs->regs.reg[9].how = REG_SAVED_OFFSET;
  fs->regs.reg[9].loc.offset = (long)&sc->r9 - new_cfa;
  fs->regs.reg[10].how = REG_SAVED_OFFSET;
  fs->regs.reg[10].loc.offset = (long)&sc->r10 - new_cfa;
  fs->regs.reg[11].how = REG_SAVED_OFFSET;
  fs->regs.reg[11].loc.offset = (long)&sc->r11 - new_cfa;
  fs->regs.reg[12].how = REG_SAVED_OFFSET;
  fs->regs.reg[12].loc.offset = (long)&sc->r12 - new_cfa;
  fs->regs.reg[13].how = REG_SAVED_OFFSET;
  fs->regs.reg[13].loc.offset = (long)&sc->r13 - new_cfa;
  fs->regs.reg[14].how = REG_SAVED_OFFSET;
  fs->regs.reg[14].loc.offset = (long)&sc->r14 - new_cfa;
  fs->regs.reg[15].how = REG_SAVED_OFFSET;
  fs->regs.reg[15].loc.offset = (long)&sc->r15 - new_cfa;
  fs->regs.reg[16].how = REG_SAVED_OFFSET;
  fs->regs.reg[16].loc.offset = (long)&sc->rip - new_cfa;
  fs->retaddr_column = 16;
  fs->signal_frame = 1;
  return _URC_NO_REASON;
}

Следующая строка:

unsigned char *pc = context->ra;

Присваивает подконтрольный нам адрес возврата указателю “pc” (счетчик программы). Безо всякой проверки выполняется следующая конструкция:

if (*(unsigned char *)(pc+0) == 0x48 && *(unsigned long long *)(pc+1) == RT_SIGRETURN_SYSCALL)

Вот почему SSP по-умолчанию каждый раз рушится на этих строках кода, когда адрес возврата перезаписан случайным адресом. Что если мы адрес возврата направим на подконтрольную нам валидную память (на которую можно безопасно сослаться)?

struct ucontext *uc_ = context->cfa;
sc = (struct sigcontext *) (void *) &uc_->uc_mcontext;

В этой точке мы контролируем сигнальный контекст:

struct sigcontext *sc;

Остальной код заполняет состояние кадра ("_Unwind_FrameState *fs") используя наше подконтрольное значение. После этой операции код вернется к разматывающему циклу (функция “_Unwind_Backtrace”). После чего вспомогательная функция подхватывает управление через следующий вызов:

if ((*trace) (&context, trace_argument) != _URC_NO_REASON)

Как было сказано ранее, вспомогательная функция отвечает за проверку есть ли какой-нибудь “прогресс” в размотке стэка через анализ CFA. Она также предотвращает от зацикливания вокруг одних и тех же кадров. И что важно, она использует следующие данные:

return (_Unwind_Ptr) context->ra;       <- _Unwind_GetIP function
return (_Unwind_Ptr) context->cfa;      <- _Unwind_GetCFA function

Обоими значениями можно управлять. В конце размоточной функции, обновляется контекст (“uw_update_context”). На этой точке находится новый кадр и подвергается разбору (с использованием полностью подконтрольных нам данных):

/* CONTEXT describes the unwind state for a frame, and FS describes the FDE of its caller. 
Update CONTEXT to refer to the caller as well. Note that the args_size and lsda members are
not updated here, but later in uw_frame_state_for. */

static void uw_update_context (struct _Unwind_Context *context, _Unwind_FrameState *fs)
{
  uw_update_context_1 (context, fs);
  /* In general this unwinder doesn't make any distinction between undefined and same_value
  rule. Call-saved registers are assumed to have same_value rule by default and explicit undefined
  rule is handled like same_value. The only exception is DW_CFA_undefined on retaddr_column
  which is supposed to mark outermost frame in DWARF 3. */
  if (fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (fs->retaddr_column)].how == REG_UNDEFINED)
    /* uw_frame_state_for uses context->ra == 0 check to find outermost stack frame. */
    context->ra = 0;
  else
    /* Compute the return address now, since the return address column can change from frame to frame. */
    context->ra = __builtin_extract_return_addr(_Unwind_GetPtr (context, fs->retaddr_column));
}

Это просто обертка над “uw_update_context_1”. Прежде чем мы исследуем ее, давайте взглянем на последующий блок if-else. Нам интересен случай “else”, который обновляет адрес возврата в контексте:

context->ra = __builtin_extract_return_addr (_Unwind_GetPtr (context, fs->retaddr_column));

Цитата из документации gcc:

Что делает “_Unwind_GetPtr”?

static inline void * _Unwind_GetPtr (struct _Unwind_Context *context, int index)
{
  return (void *)(_Unwind_Ptr) _Unwind_GetGR (context, index);
}

Где:

_Unwind_GetGR (struct _Unwind_Context *context, int index)
{
  int size;
  _Unwind_Context_Reg_Val val;
  #ifdef DWARF_ZERO_REG
    if (index == DWARF_ZERO_REG)
      return 0;
  #endif
  index = DWARF_REG_TO_UNWIND_COLUMN (index);
  gcc_assert (index < (int) sizeof(dwarf_reg_size_table));
  size = dwarf_reg_size_table[index];
  val = context->reg[index];
  if (_Unwind_IsExtendedContext (context) && context->by_value[index])
    return _Unwind_Get_Unwind_Word (val);
  /* This will segfault if the register hasn't been saved. */
  if (size == sizeof(_Unwind_Ptr))
    return * (_Unwind_Ptr *) (_Unwind_Internal_Ptr) val;
  else
  {
    gcc_assert (size == sizeof(_Unwind_Word));
    return * (_Unwind_Word *) (_Unwind_Internal_Ptr) val;
  }
}

Короче говоря, эта функция вычитает из контекстного регистра, сравнивает ее значение со значением “index”. В нашем случае, это будет значение из регистра адреса возврата.

Что делает функция “uw_update_context_1”? Эта замысловатая функция играет с CFA. Если коротко, то функция пытается вычислить CFA через сохраненный указатель кадра. Если указатель кадра не сохранен (может случиться на многих архитектурах или если установлен флаг “-fomit-frame-pointer”), то отслеживание нового CFA выполняется через анализ предыдущего (CFA).

После пересчета нового CFA, контекст обновляется значением текущего регистра в специальном кадре. В некоторых случаях выполняется функция “execute_stack_op”. Она тоже сложносочиненная, и она оперирует с внутренними структурами gcc. В этом случае функция:

“Decode a DW_OP stack program”

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

Вся история начинается снова. Если свеже-вычисленный кадр имеет адрес возврата, указывающий какую-то недостижимую память, программа обрушится на AV при чтении (прямое разыменование указателя адреса возврата, которому не следует доверять).

К (не)счастью, я не смог изменить AV при чтении на какой-либо вид AV при записи или на что-нибудь управляемое, что могло бы обеспечить выполнение кода. Возможно я слишком глуп для игры с алгоритмом DWARF, и кто-то найдет способ как проделать это. Знайте, что мы контролируем почти весь контекст и внутренние структуры, но я не был в состоянии найти способ управления метаданными в этом алгоритме (мы можем смотреть на это как на конечный_автомат/стэк-машину), за исключением самого CFA и контекста, который используется для дампа необходимой информации (при отладке, так разбираются секции памяти и т.д.) и вычисления следующего/нового кадра…

Также, быть убитым функцией “gcc_unreachable” - обычное дело. Эта функция вызывается каждый раз, когда какая-нибудь внутренняя функция находит, что значения в контексте, которые указывают на критические данные, не такие, какими должны быть. Подобную ситуацию может устроить и функция “gcc_assert”. Все должно быть идеально выравнено и иметь идеальные значения если мы не хотим быть убитыми…

В следующей секции этой работы я собираюсь сэмулировать этот сценарий под отладчиком (gdb).

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

Последний сценарий (сложный) будет в случае вычисления FDE-объекта. Этот случай даже хуже и намного сложнее :wink: В теории у нас есть больший шанс получения AV при чтении/выполнения кода, к (не)счастью, у меня не получилось ни того, ни другого. Давайте начнем…

Функция “uw_frame_state_for” вместо вызова “x86_64_fallback_frame_state” идет дальше…

fs->pc = context->bases.func;
cie = get_cie (fde);
insn = extract_cie_info (cie, context, fs);
if (insn == NULL)
  /* CIE contained unknown augmentation. */
  return _URC_FATAL_PHASE1_ERROR;
/* First decode all the insns in the CIE. */
end = (const unsigned char *) next_fde ((const struct dwarf_fde*) cie);
execute_cfa_program (insn, end, context, fs);

Если FDE был найден, CIE-объект вычисляется относительно FDE:

static inline const struct dwarf_cie * get_cie (const struct dwarf_fde *f)
{
  return (const void *)&f->CIE_delta - f->CIE_delta;
}

Функция “extract_cie_info” разбирает текущий CIE-объект и извлекает необходимую информацию, которая присвоена к структуре состояния кадра (“fs”). Дополнительно, эта функция возвращает указатель на байт после его увеличения или NULL если обнаружено увеличение, не поддающееся расшифровке. С этого указателя следующий FDE вычисляется так, чтобы получить все возможные инструкции для текущего FDE. Настало время выполнения БОЛЬШОЙ и СЛОЖНОЙ функции - “execute_cfa_program”.

Для начала, как следующий FDE вычисляется? Очень простым путем:

static inline const fde * next_fde (const fde *f)
{
  return (const fde *) ((const char *) f + f->length + sizeof (f->length));
}

Давайте вернемся к основной проблеме. Что делает “execute_cfa_program”? Цитируем внутренние комментарии:

Дальше:

Несомненно, эта функция “эмулирует” инструкцию и/или выражение DWARF. Ее можно рассматривать как ядро стэк-машины DWARF. В основном статус кадра (“fs”) обновляется с использованием текущих разбираемых данных. Если в разбор попали непредвиденные байты, процесс убивается через функцию “gcc_unreachable”.

Эта функция дважды выполняется в функции “uw_frame_state_for” - для текущего FDE и верхнего FDE. Следующая функция возвращается в основной цикл и этот процесс может повториться (или процесс, исследованный ранее). У меня не получилось заставить этот алгоритм (конечный автомат) выполнить мой код или привести к AV при записи. Опять только AV при чтении или приходил к убийству процесса.

Основная проблема в том, что мы не можем создать свой FDE, но мы все еще можем использовать существующий приводя в замешательство DWARF-машину (через управление адресом возврата). У любой программы есть сотни FDE. Даже если разработчик не написал никакого Exception Handler (EH), у динамических библиотек может быть свой. К тому же gcc может создать кое-какие. Следующий листинг показывает сколько есть потенциальных FDE в примере программы и в glibc:

[pi3@forum.reverse4you.org ~]$ readelf -w ./test|grep FDE|sort -u|uniq
00000018 00000014 0000001c FDE cie=00000000 pc=00400540..0040056a
00000048 00000024 0000001c FDE cie=00000030 pc=004004d0..00400540
00000070 0000001c 00000044 FDE cie=00000030 pc=00400630..004006b3
00000090 00000044 00000064 FDE cie=00000030 pc=004006c0..00400725
000000d8 00000014 000000ac FDE cie=00000030 pc=00400730..00400732
[pi3@forum.reverse4you.org ~]$ readelf -w /lib/libc-2.17.so|grep FDE|sort -
u|uniq|wc -l
3665

FDE с DWARF-выражениями (не только с DWARF-инструкциями):

[pi3@forum.reverse4you.org ~]$ readelf -w ./test|grep DW_OP
DW_CFA_def_cfa_expression (DW_OP_breg7 (rsp): 8; DW_OP_breg16
(rip): 0; DW_OP_lit15; DW_OP_and; DW_OP_lit11;
DW_OP_ge; DW_OP_lit3; DW_OP_shl; DW_OP_plus)

[pi3@forum.reverse4you.org ~]$ readelf -w /lib/libc-2.17.so|grep DW_OP|wc -l
2140

Властелин Колец и истории DWARF (гномов)… :wink:

Здесь я хотел бы немного помечтать… Что могло бы случиться если бы мы смогли создать свой FDE? Мы смогли бы создать любую DWARF-инструкцию и/или -выражение! В этом случае мы можем попробовать эксплуатировать саму стэк-машину (конечный автомат) DWARF. Есть ли подходящий для этого код? Вероятно есть… gcc исправил важную ошибку в DWARF 17 мая 2013 года. Давайте взглянем на DW_CFA_register-инструкцию DWARF до исправления:

case DW_CFA_register:
{
  _uleb128_t reg2;
  insn_ptr = read_uleb128 (insn_ptr, &reg);
  insn_ptr = read_uleb128 (insn_ptr, &reg2);
  fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].how = REG_SAVED_REG;
  fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].loc.reg = (_Unwind_Word)reg2;
}
break;

И после исправления:

case DW_CFA_register:
{
  _uleb128_t reg2;
  insn_ptr = read_uleb128 (insn_ptr, &reg);
  insn_ptr = read_uleb128 (insn_ptr, &reg2);
  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
  if (UNWIND_COLUMN_IN_RANGE (reg))
  {
    fs->regs.reg[reg].how = REG_SAVED_REG;
    fs->regs.reg[reg].loc.reg = (_Unwind_Word)reg2;
  }
}
break;

Где:

#define UNWIND_COLUMN_IN_RANGE(x) \
__builtin_expect((x) <= DWARF_FRAME_REGISTERS, 1)

И:

#define DWARF_FRAME_REGISTERS 17

Любой код, скомпилированный gcc без следующей заплатки, легко эксплуатируется – но это лишь случайные мысли.

Btw. Очень интересный вызов был введен на codegate 2014 CTF, который также включает в себя эксплуатацию DWARF. Только одна команда решила эту проблему – PPP. В их случае был выполнен EH через проброс исключения в обработчик SIGSEGV. У них был примитив для перезаписи указателя “frame_hdr_cache_head”, который указывает на разрешенные FDE (EH). Так как они могли создать свой собственный FDE и CIE-объект, которые разбирались алгоритмом обработки исключений DWARF. Если есть индивидуальная подпрограмма (знания, основанные на управляемом CIE-объекте), указатель извлекается, “fs” обновляется и в конце вызывается EH. Я рекомендую ознакомиться с работой Brian Pak на его блоге:

http://www.bpak.org/blog/2014/02/codegate-2014-membership-800pt-pwnable-write-up/

В следующей секции этой работы я собираюсь сэмулировать подобный сценарий под отладчиком (gdb).

Случайные мысли…

Не относится к безопасности…

Ладно, давайте обобщим все, что можно сделать (с SPP) с перспективы, не учитывающей безопасность:

  1. Мы можем изменять имя программы (с перспективы SSP) через перезапись области памяти, куда указывает указатель “argv[0]”.
  2. Мы можем обрушить код Stack Smashing Protector многими способами:
    • Через порчу области памяти, указываемой переменной “__environ”.
    • Через установку “LIBC_FATAL_STDERR_” на край валидных адресов.
    • Через принуждение “alloca()” к провалу – например, через истощение стэка.
    • Есть еще одна ошибка, которую я всесторонне исследую в пункте 4. Она может косвенно заставить обрушиться SSP. Она присутствует в стэк-машине (конечном автомате) DWARF, которая ответственна за сбор информации о трасировке стэка ("__backtrace()") и ее распечатку.
  3. Мы можем слегка изменять поток исполнения SSP. К (не)счастью, это никак не влияет на основной поток (но что на счет безопасности?). Возможны следующие сценарии:
    • Заставить SSP открыть “/dev/tty”
    • Заставить SSP не открывать “/dev/tty” и присвоить дескриптору “fd” значение “STDERR_FILENO”:
      #define STDERR_FILENO 2 /* Standard error output. */
      
    • Обрушение SSP через сценарий 2b
  4. Мы можем косвенно обрушить SSP через раскрутку алгоритма (read-AV или нас могут убить функции “gcc_unreachable” или “gcc_assert”) – стэк-машины DWARF:
    • Имитацией FDE object was not found
    • Имитацией FDE object was found.

Как-то связано с безопасностью…

1. Мы можем заставить SSP выделить много памяти и привести к Denial of Service через атаку Resource Exhaustion.

Это должно быть объяснено немного лучше… Мы контролируем следующую переменную:

ssize_t total = 0;

Которая в одном месте используется как второй аргумент функции “mmap()”:

void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);

Кое-кто может осознать, что “ssize_t” - приведение к типу “size_t”. Вероятно здесь это не имеет значения. А что же важно, как объявлен “(s)size_t”? Стандарт C99 2007 года говорит:

"7.17 Общие определения <stddef.h>


size_t является беззнаковым целым типом, возвращаемым как результат sizeof; "

и читаем далее:

“Типы, используемые для size_t и ptrdiff_t не должны иметь ранг конверсии больший чем тот, что у знакового длинного целого, если только реализация не поддерживает объекты достаточно большие, чтобы сделать это обязательным.”

Что фактически означает, что для 32 битов “(s)size_t” == “int”, но для 64 битов “(s)size_t” == “long”. Этот тип всегда покрывает все адресное пространство памяти процесса. Из-за этого мы не можем переполнить переменную “total”. Мы можем управлять только одним компонентом, входящим в вычисление. Этот компонент вычисляется через функцию “strlen()” и никогда не возвращает нужное число (около 0xFFFFF000 на 32 битах и около 0xFFFFFFFFFFFFF000 для 64 битов).

К (не)счастью мы все еще можем заставить SSP динамически выделять относительно большие куски памяти и заставить его перекопировать существующие данные из памяти процесса в свеже-выделенный буфер памяти. Если вы делаете это очень осторожно, вы можете попытаться остановиться на данных, сброшенных в своп, которые заставляют систему выполнить относительно тяжелую операцию по выгрузке этих данных обратно в рабочий набор пользователя. Затем, т.к. на каждую страницу в свеже-выделенной памяти будет ссылка (копирование через функцию “memcpy()”), система сгенерирует ошибку страницы для них всех и будет вынуждена сделать реальное выделение (сделать доступной в текущем рабочем наборе) и перекопирование физических данных из одной физической страницы в другую. Дополнительно, если вы сможете найти самый длинный консистентный кусок памяти, атака будет намного более эффективной.

Конечно, можно еще поспорить о том, можем ли мы закинуть этот сценарий в ведро Resource Exhaustion или нет… С моей точки зрения это возможно, но я также понимаю, что кто-то может с этой точкой зрения не согласиться.

2. Теоретическая утечка информации.

Если посмотреть лучше на некоторые идеи в пункте 3b, не относящиеся к безопасности, в некоторых очень редких ситуациях (невозможных в реальном мире?) может возникнуть уязвимость удаленной утечки информации. Этот сценарий статически присваевает значение 2 дескриптору “fd”, который по-умолчанию соответствует потоку вывода ошибок. К сожалению, у вас нет гарантий, что процесс не сможет это изменить. Возможно, что приложение свяжет этот дескриптор с каким-нибудь открытым клиентским сокетом (например, через часто используемую функцию “dup2()”). Этот сценарий может произойти с любым приложением (библиотекой?), который эмулирует псевдотерминалы и т.п. Может произойти еще более редкая ситуация, если приложение для какой-то цели закроет дескриптор №2 и потом попытается открыть что-нибудь (файл, устройство, что-нибудь еще) или попытается создать сокет; по-умолчанию этот номер (2) будет использован повторно (может быть устроено, например каким-нибудь видом уязвимости, который позволит тебе закрывать что угодно). SSP отправляет такие данные, как имя приложения (которое читается из указателя, который может быть также подделан), трассировка стэка, и т.п. на вывод именно на дескриптор “fd”. Если вы сможете изменить указатель на имя приложения так, чтобы он указывал на желаемые данные, эти данные могут быть отправлены на дескриптор 2 (который может оказаться сокетом, соответствующим клиентскому подключению).

Теоретически возможный сценарий атаки:

a) Утечка инфы по стэковой печеньке. Если приложение вызывает какую-нибудь защищенную функцию после переполнения стэка, печенька не будет перезаписана и она будет цела так, чтобы ее можно было слить из стэка. Этот сценарий позволяет вам одолеть защиту SSP в два хода. Первый ход: вы заставляете SSP слить стэковую печеньку. Второй ход: вы подготавливаете полностью рабочий поток переполнения (который будет включать правильное значение печеньки). Конечно, этот сценарий будет возможен только в подобных “fork()” приложениях (не считая приложения, которые делают “fork()” + “exec*()”, как OpenSSH или Postfix).

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

b) Любой вид утечки информации. Можете слить все, что захотитe. Это может быть полезным для преодоления ASLR как нормальная протечка образа (базовый адрес общей библиотеки, если бинарник PIE, базовый адрес образа программы). Утечка секрета любого типа из приложения если приложение не будет разрушена самой этой теоретической атакой.

c) Порча файлов. Если дескриптор будет назначен (соответствовать) какому-либо файлу, вывод SSP испортит этот файл. Другой теоретический сценарий – когда вы портите указатель на имя программы на данные, которыми полностью управляете, вы можете испортить файл данными, которые точно вам нужны. Особенно опасно если открыты критически важные файлы (вроде “passwd / shadow / services”, etc.).

Ленивая практика…

Я слишком ленив, чтобы проверять все возможные сценарии. Поэтому я проверил только некоторые…

Для начала давайте создадим очень простую уязвимую программу:

#include <stdio.h>
int main(int argc, char *argv[]) 
{
  char buf[100];
  memset(buf,0x0,sizeof(buf));
  if (argv[1])
    strcpy(buf,argv[1]);
  printf("DONE!\n");
  return 0;
}

и скомпилируем с флагом “-fstack-protector-all”:

[pi3@forum.reverse4you.org ~]$ gcc test.c -o test -g -ggdb -fstack-protectorall
test.c: In function ‘main’:
test.c:7:4: warning: incompatible implicit declaration of builtin function ‘memset’ [enabled by default]
 memset(buf,0x0,sizeof(buf));
 ^
test.c:9:7: warning: incompatible implicit declaration of builtin function ‘strcpy’ [enabled by default]
 strcpy(buf,argv[1]);
 ^
[pi3@forum.reverse4you.org ~]$ ./test `perl -e 'print "A"x110'`
DONE!
*** stack smashing detected ***: ./test terminated
======= Backtrace: =========
/lib64/libc.so.6(__fortify_fail+0x37)[0x35c190d6b7]
/lib64/libc.so.6(__fortify_fail+0x0)[0x35c190d680]
./test[0x40075a]
/lib64/libc.so.6(__libc_start_main+0xf5)[0x35c1821b75]
./test[0x4005f9]
======= Memory map: ========
00400000-00401000 r-xp 00000000 fd: 02 262194
/home/pi3/test
00600000-00601000 r--p 00000000 fd: 02 262194
/home/pi3/test
00601000-00602000 rw-p 00001000 fd: 02 262194
/home/pi3/test
018e7000-01908000 rw-p 00000000 00:00 0
[heap]
35c1000000-35c1021000 r-xp 00000000 fd:01 1061612
/usr/lib64/ld-2.17.so
35c1220000-35c1221000 r--p 00020000 fd:01 1061612
/usr/lib64/ld-2.17.so
35c1221000-35c1222000 rw-p 00021000 fd:01 1061612
/usr/lib64/ld-2.17.so
35c1222000-35c1223000 rw-p 00000000 00:00 0
35c1800000-35c19b6000 r-xp 00000000 fd:01 1061613
/usr/lib64/libc-2.17.so
35c19b6000-35c1bb6000 ---p 001b6000 fd:01 1061613
/usr/lib64/libc-2.17.so
35c1bb6000-35c1bba000 r--p 001b6000 fd:01 1061613
/usr/lib64/libc-2.17.so
35c1bba000-35c1bbc000 rw-p 001ba000 fd:01 1061613
/usr/lib64/libc-2.17.so
35c1bbc000-35c1bc1000 rw-p 00000000 00:00 0
35c4000000-35c4015000 r-xp 00000000 fd:01 1061670
/usr/lib64/libgcc_s-4.8.1-20130603.so.1
35c4015000-35c4214000 ---p 00015000 fd:01 1061670
/usr/lib64/libgcc_s-4.8.1-20130603.so.1
35c4214000-35c4215000 r--p 00014000 fd:01 1061670
/usr/lib64/libgcc_s-4.8.1-20130603.so.1
35c4215000-35c4216000 rw-p 00015000 fd:01 1061670
/usr/lib64/libgcc_s-4.8.1-20130603.so.1
7f3ab2d94000-7f3ab2d97000 rw-p 00000000 00:00 0
7f3ab2da7000-7f3ab2dab000 rw-p 00000000 00:00 0
7fff57436000-7fff57457000 rw-p 00000000 00:00 0
[stack]
7fff575d4000-7fff575d6000 r-xp 00000000 00:00 0
[vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0
[vsyscall]
Aborted (core dumped)

[pi3@forum.reverse4you.org ~]$

Как видите, SSP правильно работает, обнаруживает переполнение; получена соответствующая информация, и процесс убивается. Все было распечатано в терминале. Как мы видели в коде SSP/Glibc, SSP также печатает следующую строку:

*** stack smashing detected ***: ./test terminated

Давайте сейчас попробуем вызвать кое-какие ошибки в SSP.

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

Обычная работа SSP:

(gdb) r `perl -e 'print "A"x110'`
Starting program: /home/pi3/test `perl -e 'print "A"x110'`
DONE!
*** stack smashing detected ***: /home/pi3/test terminated
======= Backtrace: =========
/lib64/libc.so.6(__fortify_fail+0x37)[0x35c190d6b7]
/lib64/libc.so.6(__fortify_fail+0x0)[0x35c190d680]
/home/pi3/test[0x4006b1]
/lib64/libc.so.6(__libc_start_main+0xf5)[0x35c1821b75]
/home/pi3/test[0x400569]
======= Memory map: ========
00400000-00401000 r-xp 00000000 fd: 02 262194
/home/pi3/test
00600000-00601000 r--p 00000000 fd: 02 262194
/home/pi3/test
00601000-00602000 rw-p 00001000 fd: 02 262194
/home/pi3/test
00602000-00623000 rw-p 00000000 00:00 0
[heap]
35c1000000-35c1021000 r-xp 00000000 fd:01 1061612
/usr/lib64/ld-2.17.so
35c1220000-35c1221000 r--p 00020000 fd:01 1061612
/usr/lib64/ld-2.17.so
35c1221000-35c1222000 rw-p 00021000 fd:01 1061612
/usr/lib64/ld-2.17.so
35c1222000-35c1223000 rw-p 00000000 00:00 0
35c1800000-35c19b6000 r-xp 00000000 fd:01 1061613
/usr/lib64/libc-2.17.so
35c19b6000-35c1bb6000 ---p 001b6000 fd:01 1061613
/usr/lib64/libc-2.17.so
35c1bb6000-35c1bba000 r--p 001b6000 fd:01 1061613
/usr/lib64/libc-2.17.so
35c1bba000-35c1bbc000 rw-p 001ba000 fd:01 1061613
/usr/lib64/libc-2.17.so
35c1bbc000-35c1bc1000 rw-p 00000000 00:00 0
35c4000000-35c4015000 r-xp 00000000 fd:01 1061670
/usr/lib64/libgcc_s-4.8.1-20130603.so.1
35c4015000-35c4214000 ---p 00015000 fd:01 1061670
/usr/lib64/libgcc_s-4.8.1-20130603.so.1
35c4214000-35c4215000 r--p 00014000 fd:01 1061670
/usr/lib64/libgcc_s-4.8.1-20130603.so.1
35c4215000-35c4216000 rw-p 00015000 fd:01 1061670
/usr/lib64/libgcc_s-4.8.1-20130603.so.1
7ffff7fe6000-7ffff7fe9000 rw-p 00000000 00:00 0
7ffff7ffa000-7ffff7ffd000 rw-p 00000000 00:00 0
7ffff7ffd000-7ffff7fff000 r-xp 00000000 00:00 0
[vdso]
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0
[stack]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0
[vsyscall]
Program received signal SIGABRT, Aborted.
0x00000035c1835a19 in __GI_raise (sig=sig@entry=6) at
../nptl/sysdeps/unix/sysv/linux/raise.c:56
56 return INLINE_SYSCALL (tgkill, 3, pid, selftid, sig);

(gdb) print __libc_argv[0]
$10 = 0x7fffffffe445 "/home/pi3/test"
(gdb)

Перезапустим и переполним агрументы (argv):

(gdb) r `perl -e 'print "A"x1000'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/pi3/test `perl -e 'print "A"x1000'`
DONE!
Program received signal SIGSEGV, Segmentation fault.
__GI_getenv (name=0x35c197bc64 "BC_FATAL_STDERR_",
name@entry=0x35c197bc62 "LIBC_FATAL_STDERR_") at getenv.c:89
89 if (name_start == ep_start && !strncmp (*ep + 2, name, len)

(gdb) print __libc_argv[0]
$11 = 0x4141414141414141 <Address 0x4141414141414141 out of
bounds>
(gdb)

Готово. Как мы ожидали – это возможно. Также, трассировка стэка не распечатана и программа где-то рухнула – мы столкнулись с одним из описаных ошибок.

Не относится к безопасности:

Crash 2a:

(gdb) r `perl -e 'print "A"x5000'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/pi3/test `perl -e 'print "A"x5000'`
DONE!
Program received signal SIGSEGV, Segmentation fault.
__GI_getenv (name=0x35c197bc64 "BC_FATAL_STDERR_",
name@entry=0x35c197bc62 "LIBC_FATAL_STDERR_") at getenv.c:89
89 if (name_start == ep_start && !strncmp (*ep + 2, name, len)

(gdb) bt
#0 __GI_getenv (name=0x35c197bc64 "BC_FATAL_STDERR_",
name@entry=0x35c197bc62 "LIBC_FATAL_STDERR_") at getenv.c:89
#1 0x00000035c18391c2 in __GI___libc_secure_getenv
(name=name@entry=0x35c197bc62 "LIBC_FATAL_STDERR_") at securegetenv.c:30
#2 0x00000035c1875a9a in __libc_message
(do_abort=do_abort@entry=2,
 fmt=fmt@entry=0x35c197d302 "*** %s ***: %s terminated\n") at
../sysdeps/unix/sysv/linux/libc_fatal.c:66
#3 0x00000035c190d6b7 in __GI___fortify_fail
(msg=msg@entry=0x35c197d2ea "stack smashing detected") at
fortify_fail.c:31
#4 0x00000035c190d680 in __stack_chk_fail () at
stack_chk_fail.c:28
#5 0x00000000004006b1 in main (argc=2, argv=0x7fffffffce58) at
test.c:15
(gdb) list
84 #else
85 uint16_t ep_start = (((unsigned char *) *ep)[0]
86  | (((unsigned char *) *ep)[1] << 8));
87 #endif
88
89 if (name_start == ep_start && !strncmp (*ep + 2, name, len)
90    && (*ep)[len + 2] == '=')
91  return &(*ep)[len + 3];
92 }
93 }

(gdb) x/i $rip
=> 0x35c183892d <__GI_getenv+173>: cmp (%rbx),%r12w

(gdb) i r rbx
rbx 0x4141414141414141 4702111234474983745

(gdb) print ep
$12 = (char **) 0x7fffffffce70

(gdb) print *ep
$13 = 0x4141414141414141 <Address 0x4141414141414141 out of bounds>

(gdb)

Crash 2d:

(gdb) r `perl -e 'print "A"x300'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/pi3/test `perl -e 'print "A"x300'`
DONE!
*** stack smashing detected ***: /home/pi3/test terminated
Program received signal SIGSEGV, Segmentation fault.
x86_64_fallback_frame_state (context=0x7fffffffd3a0,
context=0x7fffffffd3a0, fs=0x7fffffffd490) at ./md-unwindsupport.h:58
58 if (*(unsigned char *)(pc+0) == 0x48

(gdb) bt
#0 x86_64_fallback_frame_state (context=0x7fffffffd3a0,
context=0x7fffffffd3a0, fs=0x7fffffffd490) at ./md-unwind-support.h:58
#1 uw_frame_state_for (context=context@entry=0x7fffffffd3a0,
fs=fs@entry=0x7fffffffd490)
 at ../../../libgcc/unwind-dw2.c:1253
#2 0x00000035c400ff19 in _Unwind_Backtrace (trace=0x35c1909bc0
<backtrace_helper>, trace_argument=0x7fffffffd650)
 at ../../../libgcc/unwind.inc:290
#3 0x00000035c1909d36 in __GI___backtrace
(array=array@entry=0x7fffffffd830, size=size@entry=64)
 at ../sysdeps/x86_64/backtrace.c:109
#4 0x00000035c1875d64 in __libc_message
(do_abort=do_abort@entry=2,
 fmt=fmt@entry=0x35c197d302 "*** %s ***: %s terminated\n") at
../sysdeps/unix/sysv/linux/libc_fatal.c:176
#5 0x00000035c190d6b7 in __GI___fortify_fail
(msg=msg@entry=0x35c197d2ea "stack smashing detected") at
fortify_fail.c:31
#6 0x00000035c190d680 in __stack_chk_fail () at
stack_chk_fail.c:28
#7 0x00000000004006b1 in main (argc=2, argv=0x7fffffffe0b8) at
test.c:15

(gdb) print context->ra
$14 = (void *) 0x4141414141414141

(gdb) x/i $rip
=> 0x35c400f018 <uw_frame_state_for+1080>: cmpb $0x48,(%rcx)

(gdb) i r rcx
rcx 0x4141414141414141 4702111234474983745

(gdb) list 50
45 x86_64_fallback_frame_state (struct _Unwind_Context *context,
46 _Unwind_FrameState *fs)
47 {
48   unsigned char *pc = context->ra;
49   struct sigcontext *sc;
50   long new_cfa;
51
52   /* movq $__NR_rt_sigreturn, %rax ; syscall. */
53   #ifdef __LP64__
54     #define RT_SIGRETURN_SYSCALL 0x050f0000000fc0c7ULL
55   #else
56     #define RT_SIGRETURN_SYSCALL 0x050f40000201c0c7ULL
57   #endif
58   if (*(unsigned char *)(pc+0) == 0x48
59    && *(unsigned long long *)(pc+1) == RT_SIGRETURN_SYSCALL)
60   {
61     struct ucontext *uc_ = context->cfa;
62     /* The void * cast is necessary to avoid an aliasing warning.

Сценарий 3a:

[pi3@forum.reverse4you.org ~]$ gdb -q -p 16473
Attaching to process 16473
Reading symbols from /home/pi3/test...done.
Reading symbols from /lib64/libc.so.6...Reading symbols from
/usr/lib/debug/lib64/libc-2.17.so.debug...done.
done.
Loaded symbols for /lib64/libc.so.6
Reading symbols from /lib64/ld-linux-x86-64.so.2...Reading
symbols from /usr/lib/debug/lib64/ld-2.17.so.debug...done.
done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
0x00000035c18e7650 in __read_nocancel () at
../sysdeps/unix/syscall-template.S:81
81 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)

(gdb) break libc_fatal.c:66
Breakpoint 1 at 0x35c1875a37: file
../sysdeps/unix/sysv/linux/libc_fatal.c, line 66.

(gdb) c
Continuing.
Breakpoint 1, __libc_message (do_abort=do_abort@entry=2,
fmt=fmt@entry=0x35c197d302 "*** %s ***: %s terminated\n")
 at ../sysdeps/unix/sysv/linux/libc_fatal.c:66
66 const char *on_2 = __libc_secure_getenv
("LIBC_FATAL_STDERR_");

(gdb) list
61 FATAL_PREPARE;
62 #endif
63
64 /* Open a descriptor for /dev/tty unless the user explicitly
65 requests errors on standard error. */
66 const char *on_2 = __libc_secure_getenv("LIBC_FATAL_STDERR_");
67 if (on_2 == NULL || *on_2 == '\0')
68 fd = open_not_cancel_2 (_PATH_TTY, O_RDWR | O_NOCTTY | O_NDELAY);
69
70 if (fd == -1)

(gdb) print on_2
$1 = <optimized out>

(gdb) break libc_fatal.c:67
Breakpoint 2 at 0x35c1875a9a: file
../sysdeps/unix/sysv/linux/libc_fatal.c, line 67.

(gdb) c
Continuing.
Breakpoint 2, __libc_message (do_abort=do_abort@entry=2,
fmt=fmt@entry=0x35c197d302 "*** %s ***: %s terminated\n")
 at ../sysdeps/unix/sysv/linux/libc_fatal.c:67
67 if (on_2 == NULL || *on_2 == '\0')

(gdb) print on_2
$2 = 0x0

(gdb) break libc_fatal.c:70
Breakpoint 3 at 0x35c1875abb: file
../sysdeps/unix/sysv/linux/libc_fatal.c, line 70.

(gdb) c
Continuing.
Breakpoint 3, __libc_message (do_abort=do_abort@entry=2,
fmt=fmt@entry=0x35c197d302 "*** %s ***: %s terminated\n")
 at ../sysdeps/unix/sysv/linux/libc_fatal.c:70
70 if (fd == -1)
<some inline debug jump>
<some inline debug jump>
<some inline debug jump>

(gdb) ni
68 fd = open_not_cancel_2 (_PATH_TTY, O_RDWR | O_NOCTTY | O_NDELAY);

(gdb) print fd
$4 = -1

(gdb) ni
70 if (fd == -1)

(gdb) print fd
$5 = 3

(gdb)

и двойная проверка в списке дескрипторов открытых файлов этого процесса:

[pi3@forum.reverse4you.org ~]$ ls -al /proc/16473/fd
total 0
dr-x------. 2 pi3 pi3 0 Sep 29 11:02 .
dr-xr-xr-x. 9 pi3 pi3 0 Sep 29 11:02 ..
lrwx------. 1 pi3 pi3 64 Sep 29 11:06 0 -> /dev/pts/3
lrwx------. 1 pi3 pi3 64 Sep 29 11:06 1 -> /dev/pts/3
lrwx------. 1 pi3 pi3 64 Sep 29 11:02 2 -> /dev/pts/3
lrwx------. 1 pi3 pi3 64 Sep 29 11:06 3 -> /dev/tty
[pi3@forum.reverse4you.org ~]$

Сценарий 3b:

[pi3@forum.reverse4you.org ~]$ gdb -q -p 16531
Attaching to process 16531
Reading symbols from /home/pi3/test...done.
Reading symbols from /lib64/libc.so.6...Reading symbols from
/usr/lib/debug/lib64/libc-2.17.so.debug...done.
done.
Loaded symbols for /lib64/libc.so.6
Reading symbols from /lib64/ld-linux-x86-64.so.2...Reading
symbols from /usr/lib/debug/lib64/ld-2.17.so.debug...done.
done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
0x00000035c18e7650 in __read_nocancel () at
../sysdeps/unix/syscall-template.S:81
81 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)

(gdb) break libc_fatal.c:66
Breakpoint 1 at 0x35c1875a37: file
../sysdeps/unix/sysv/linux/libc_fatal.c, line 66.

(gdb) break libc_fatal.c:67
Breakpoint 2 at 0x35c1875a9a: file
../sysdeps/unix/sysv/linux/libc_fatal.c, line 67.

(gdb) c
Continuing.
Breakpoint 1, __libc_message (do_abort=do_abort@entry=2,
fmt=fmt@entry=0x35c197d302 "*** %s ***: %s terminated\n")
 at ../sysdeps/unix/sysv/linux/libc_fatal.c:66
66 const char *on_2 = __libc_secure_getenv
("LIBC_FATAL_STDERR_");

(gdb) break libc_fatal.c:70
Breakpoint 3 at 0x35c1875abb: file
../sysdeps/unix/sysv/linux/libc_fatal.c, line 70.

(gdb) print on_2
$1 = <optimized out>

(gdb) c
Continuing.
Breakpoint 2, __libc_message (do_abort=do_abort@entry=2,
fmt=fmt@entry=0x35c197d302 "*** %s ***: %s terminated\n")
 at ../sysdeps/unix/sysv/linux/libc_fatal.c:67
67 if (on_2 == NULL || *on_2 == '\0')

(gdb) print on_2
$2 = 0x7fff2b8aafe0 "/tmp/pi3"

(gdb) c
Continuing.

и повторно проверяем:

[pi3@forum.reverse4you.org ~]$ ls -al /proc/16531/fd
total 0
dr-x------. 2 pi3 pi3 0 Sep 29 11:11 .
dr-xr-xr-x. 9 pi3 pi3 0 Sep 29 11:11 ..
lrwx------. 1 pi3 pi3 64 Sep 29 11:14 0 -> /dev/pts/3
lrwx------. 1 pi3 pi3 64 Sep 29 11:14 1 -> /dev/pts/3
lrwx------. 1 pi3 pi3 64 Sep 29 11:11 2 -> /dev/pts/3
[pi3@forum.reverse4you.org ~]$
#2

Сценарий 4a:

Для начала давайте просто докажем, что простое переполнение адреса возврата может повлечь крах AV при чтении. Первое переполнение печеньки без затрагивания адреса возврата:

[pi3@forum.reverse4you.org ~]$ gdb -q ./test
Reading symbols from /home/pi3/test...(no debugging symbols
found)...done.

(gdb) r `perl -e 'print "A"x120'`
Starting program: /home/pi3/test `perl -e 'print "A"x120'`
DONE!
*** stack smashing detected ***: /home/pi3/test terminated
======= Backtrace: =========
/lib64/libc.so.6(__fortify_fail+0x37)[0x35c190d6b7]
/lib64/libc.so.6(__fortify_fail+0x0)[0x35c190d680]
/home/pi3/test[0x4006b1]
/lib64/libc.so.6(__libc_start_main+0x80)[0x35c1821b00]
/home/pi3/test[0x400569]
======= Memory map: ========
00400000-00401000 r-xp 00000000 fd:02 262194
/home/pi3/test
00600000-00601000 r--p 00000000 fd:02 262194
/home/pi3/test
00601000-00602000 rw-p 00001000 fd:02 262194
/home/pi3/test
00602000-00623000 rw-p 00000000 00:00 0
[heap]
35c1000000-35c1021000 r-xp 00000000 fd:01 1061612
/usr/lib64/ld-2.17.so
35c1220000-35c1221000 r--p 00020000 fd:01 1061612
/usr/lib64/ld-2.17.so
35c1221000-35c1222000 rw-p 00021000 fd:01 1061612
/usr/lib64/ld-2.17.so
35c1222000-35c1223000 rw-p 00000000 00:00 0
35c1800000-35c19b6000 r-xp 00000000 fd:01 1061613
/usr/lib64/libc-2.17.so
35c19b6000-35c1bb6000 ---p 001b6000 fd:01 1061613
/usr/lib64/libc-2.17.so
35c1bb6000-35c1bba000 r--p 001b6000 fd:01 1061613
/usr/lib64/libc-2.17.so
35c1bba000-35c1bbc000 rw-p 001ba000 fd:01 1061613
/usr/lib64/libc-2.17.so
35c1bbc000-35c1bc1000 rw-p 00000000 00:00 0
35c4000000-35c4015000 r-xp 00000000 fd:01 1061670
/usr/lib64/libgcc_s-4.8.1-20130603.so.1
35c4015000-35c4214000 ---p 00015000 fd:01 1061670
/usr/lib64/libgcc_s-4.8.1-20130603.so.1
35c4214000-35c4215000 r--p 00014000 fd:01 1061670
/usr/lib64/libgcc_s-4.8.1-20130603.so.1
35c4215000-35c4216000 rw-p 00015000 fd:01 1061670
/usr/lib64/libgcc_s-4.8.1-20130603.so.1
7ffff7fe6000-7ffff7fe9000 rw-p 00000000 00:00 0
7ffff7ffa000-7ffff7ffd000 rw-p 00000000 00:00 0
7ffff7ffd000-7ffff7fff000 r-xp 00000000 00:00 0
[vdso]
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0
[stack]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0
[vsyscall]
Program received signal SIGABRT, Aborted.
0x00000035c1835a19 in __GI_raise (sig=sig@entry=6) at
../nptl/sysdeps/unix/sysv/linux/raise.c:56
56 return INLINE_SYSCALL (tgkill, 3, pid, selftid, sig);

(gdb) bt
#0 0x00000035c1835a19 in __GI_raise (sig=sig@entry=6) at
../nptl/sysdeps/unix/sysv/linux/raise.c:56
#1 0x00000035c1837128 in __GI_abort () at abort.c:90
#2 0x00000035c1875d47 in __libc_message
(do_abort=do_abort@entry=2,
 fmt=fmt@entry=0x35c197d302 "*** %s ***: %s terminated\n") at
../sysdeps/unix/sysv/linux/libc_fatal.c:196
#3 0x00000035c190d6b7 in __GI___fortify_fail
(msg=msg@entry=0x35c197d2ea "stack smashing detected") at
fortify_fail.c:31
#4 0x00000035c190d680 in __stack_chk_fail () at
stack_chk_fail.c:28
#5 0x00000000004006b1 in main ()

(gdb) list libc_fatal.c:196
191 close_not_cancel_no_status (fd2);
192 }
193 }
194
195 /* Terminate the process. */
196 abort ();
197 }
198 }
199
200

Перезапись адреса возврата:

(gdb) r `perl -e 'print "A"x124'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/pi3/test `perl -e 'print "A"x124'`
DONE!
*** stack smashing detected ***: /home/pi3/test terminated
Program received signal SIGSEGV, Segmentation fault.
x86_64_fallback_frame_state (context=0x7fffffffd420,
context=0x7fffffffd420, fs=0x7fffffffd510) at ./md-unwindsupport.h:58
58 if (*(unsigned char *)(pc+0) == 0x48

(gdb) bt
#0 x86_64_fallback_frame_state (context=0x7fffffffd420,
context=0x7fffffffd420, fs=0x7fffffffd510)
 at ./md-unwind-support.h:58
#1 uw_frame_state_for (context=context@entry=0x7fffffffd420,
fs=fs@entry=0x7fffffffd510)
 at ../../../libgcc/unwind-dw2.c:1253
#2 0x00000035c400ff19 in _Unwind_Backtrace (trace=0x35c1909bc0
<backtrace_helper>, trace_argument=0x7fffffffd6d0)
 at ../../../libgcc/unwind.inc:290
#3 0x00000035c1909d36 in __GI___backtrace
(array=array@entry=0x7fffffffd8b0, size=size@entry=64)
 at ../sysdeps/x86_64/backtrace.c:109
#4 0x00000035c1875d64 in __libc_message
(do_abort=do_abort@entry=2,
 fmt=fmt@entry=0x35c197d302 "*** %s ***: %s terminated\n") at
../sysdeps/unix/sysv/linux/libc_fatal.c:176
#5 0x00000035c190d6b7 in __GI___fortify_fail
(msg=msg@entry=0x35c197d2ea "stack smashing detected") at
fortify_fail.c:31
#6 0x00000035c190d680 in __stack_chk_fail () at
stack_chk_fail.c:28
#7 0x00000000004006b1 in main ()

(gdb) print pc
$2 = (unsigned char *) 0x41414141 <Address 0x41414141 out of
bounds>

(gdb)

Как вы можете видеть, вместо убийства процесса, получен SIGSEGV. Сбой произошел именно там, где мы и предполагали исходя из предыдущего анализа. Давайте теперь сэмулируем полный контроль над памятью, куда указывает адрес возврата (сигнальный кадр):

(gdb) r `perl -e 'print "A"x300'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/pi3/test `perl -e 'print "A"x300'`
DONE!
*** stack smashing detected ***: /home/pi3/test terminated
Breakpoint 9, uw_frame_state_for
(context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd1c0)
 at ../../../libgcc/unwind-dw2.c:1233
1233 {

(gdb) c
...
<many times> 
...

(gdb) print context->ra
$161 = (void *) 0x4141414141414141

(gdb) set context->ra = 0x7ffffffde101
...
<let’s pad the memory>
...

(gdb) set $pos = 0

(gdb) while ($pos < 4000)
>set *(0x7ffffffde101+$pos++) = 0x4141414141414141
>end

(gdb) while ($pos < 4000)
>set *(0x7ffffffde101-$pos++) = 0x4141414141414141
>end

(gdb) list
1228 args_size and lsda members of CONTEXT, as they are
really information
1229 about the caller's frame. */
1230
1231 static _Unwind_Reason_Code
1232 uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs)
1233 {
1234 const struct dwarf_fde *fde;
1235 const struct dwarf_cie *cie;
1236 const unsigned char *aug, *insn, *end;
1237

(gdb)
1238 memset (fs, 0, sizeof (*fs));
1239 context->args_size = 0;
1240 context->lsda = 0;
1241
1242 if (context->ra == 0)
1243 return _URC_END_OF_STACK;
1244
1245 fde = _Unwind_Find_FDE (context->ra + _Unwind_IsSignalFrame (context) - 1,
1246 &context->bases);
1247 if (fde == NULL)

(gdb)
1248 {
1249 #ifdef MD_FALLBACK_FRAME_STATE_FOR
1250 /* Couldn't find frame unwind info for this function. Try a
1251 target-specific fallback mechanism. This will necessarily
1252 not provide a personality routine or LSDA. */
1253 return MD_FALLBACK_FRAME_STATE_FOR (context, fs);
1254 #else
1255 return _URC_END_OF_STACK;
1256 #endif
1257 }

(gdb) b 1247
Breakpoint 20 at 0x35c400ec8c: file ../../../libgcc/unwind-dw2.c, line 1247.

(gdb) c
Continuing.
Breakpoint 20, uw_frame_state_for
(context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwind-dw2.c:1247
1247 if (fde == NULL)

(gdb) del 20

(gdb) list
1242 if (context->ra == 0)
1243 return _URC_END_OF_STACK;
1244
1245 fde = _Unwind_Find_FDE (context->ra + _Unwind_IsSignalFrame (context) - 1,
1246 &context->bases);
1247 if (fde == NULL)
1248 {
1249 #ifdef MD_FALLBACK_FRAME_STATE_FOR
1250 /* Couldn't find frame unwind info for this function. Try a
1251 target-specific fallback mechanism. This will necessarily

(gdb) si
1245 fde = _Unwind_Find_FDE (context->ra + _Unwind_IsSignalFrame (context) - 1,

(gdb)
1247 if (fde == NULL)

(gdb)
1232 uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs)

(gdb)
0x00000035c400f00c 1232 uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs)

(gdb)
x86_64_fallback_frame_state (context=0x7fffffffd370,
context=0x7fffffffd370, fs=0x7fffffffd460) at ./md-unwindsupport.h:68
68 return _URC_END_OF_STACK;

(gdb)
58 if (*(unsigned char *)(pc+0) == 0x48

(gdb) list
53 #ifdef __LP64__
54 #define RT_SIGRETURN_SYSCALL 0x050f0000000fc0c7ULL
55 #else
56 #define RT_SIGRETURN_SYSCALL 0x050f40000201c0c7ULL
57 #endif
58 if (*(unsigned char *)(pc+0) == 0x48
59 && *(unsigned long long *)(pc+1) == RT_SIGRETURN_SYSCALL)
60 {
61 struct ucontext *uc_ = context->cfa;
62 /* The void * cast is necessary to avoid an aliasing warning.

(gdb)
63 The aliasing warning is correct, but should not be a problem
64 because it does not alias anything. */
65 sc = (struct sigcontext *) (void *) &uc_->uc_mcontext;
66 }
67 else
68 return _URC_END_OF_STACK;
69
70 new_cfa = sc->rsp;
71 fs->regs.cfa_how = CFA_REG_OFFSET;
72 /* Register 7 is rsp */

(gdb) x/20i $rip
=> 0x35c400f018 <uw_frame_state_for+1080>: cmpb $0x48,(%rcx)
 0x35c400f01b <uw_frame_state_for+1083>: jne 0x35c400eded
<uw_frame_state_for+525>
 0x35c400f021 <uw_frame_state_for+1089>: movabs
$0x50f0000000fc0c7,%rsi
 0x35c400f02b <uw_frame_state_for+1099>: cmp %rsi,0x1(%rcx)
 0x35c400f02f <uw_frame_state_for+1103>: jne 0x35c400eded
<uw_frame_state_for+525>
 0x35c400f035 <uw_frame_state_for+1109>: mov
0xa0(%rdx),%rax
 0x35c400f03c <uw_frame_state_for+1116>: lea
0x90(%rdx),%rsi
 0x35c400f043 <uw_frame_state_for+1123>: movl
$0x1,0x140(%r12)
 0x35c400f04f <uw_frame_state_for+1135>: movq
$0x7,0x130(%r12)
 0x35c400f05b <uw_frame_state_for+1147>: movl $0x1,0x8(%r12)
 0x35c400f064 <uw_frame_state_for+1156>: movl
$0x1,0x18(%r12)
 0x35c400f06d <uw_frame_state_for+1165>: movl
$0x1,0x28(%r12)
 0x35c400f076 <uw_frame_state_for+1174>: mov %rax,%rcx
 0x35c400f079 <uw_frame_state_for+1177>: sub %rax,%rsi
 0x35c400f07c <uw_frame_state_for+1180>: movl
$0x1,0x38(%r12)
 0x35c400f085 <uw_frame_state_for+1189>: sub %rdx,%rcx
 0x35c400f088 <uw_frame_state_for+1192>: mov %rsi,(%r12)
 0x35c400f08c <uw_frame_state_for+1196>: lea
0x88(%rdx),%rsi
 0x35c400f093 <uw_frame_state_for+1203>: mov
%rcx,0x128(%r12)
 0x35c400f09b <uw_frame_state_for+1211>: lea
0x28(%rdx),%rcx

(gdb) x/x $rcx
0x7ffffffde101: 0x41
...
<we need to change the memory layout to pass the checks>
...

(gdb) set *$rcx=0x48

(gdb) x/x $rcx
0x7ffffffde101: 0x48

(gdb) echo 0x5 0f 00 00 00 0f c0 c7 \n
0x5 0f 00 00 00 0f c0 c7

(gdb) set *($rcx+5)=00

(gdb) set *($rcx+6)=00

(gdb) set *($rcx+7)=0x0f

(gdb) set *($rcx+8)=0x05

(gdb) x/8x $rcx+1
0x7ffffffde102: 0xc7 0xc0 0x0f 0x00 0x00 0x00 0x0f 0x05

(gdb) si
0x00000035c400f01b 58 if (*(unsigned char *)(pc+0) == 0x48

(gdb)
59 && *(unsigned long long *)(pc+1) == RT_SIGRETURN_SYSCALL)

(gdb)
0x00000035c400f02b 59 && *(unsigned long long *)(pc+1) == RT_SIGRETURN_SYSCALL)

(gdb)
0x00000035c400f02f 59 && *(unsigned long long *)(pc+1) == RT_SIGRETURN_SYSCALL)

(gdb) list
54 #define RT_SIGRETURN_SYSCALL 0x050f0000000fc0c7ULL
55 #else
56 #define RT_SIGRETURN_SYSCALL 0x050f40000201c0c7ULL
57 #endif
58 if (*(unsigned char *)(pc+0) == 0x48
59 && *(unsigned long long *)(pc+1) == RT_SIGRETURN_SYSCALL)
60 {
61 struct ucontext *uc_ = context->cfa;
62 /* The void * cast is necessary to avoid an aliasing warning.
63 The aliasing warning is correct, but should not be a problem

(gdb)
64 because it does not alias anything. */
65 sc = (struct sigcontext *) (void *) &uc_->uc_mcontext;
66 }
67 else
68 return _URC_END_OF_STACK;
69
70 new_cfa = sc->rsp;
71 fs->regs.cfa_how = CFA_REG_OFFSET;
72 /* Register 7 is rsp */
73 fs->regs.cfa_reg = 7;

(gdb) si
70 new_cfa = sc->rsp;

(gdb) print sc
$163 = (struct sigcontext *) 0x7fffffffdfd8

(gdb) print/x *sc
$165 = {r8 = 0x4141414141414141, r9 = 0x4141414141414141, r10 =
0x4141414141414141, r11 = 0x4141414141414141,
 r12 = 0x4141414141414141, r13 = 0x4141414141414141, r14 =
0x4141414141414141, r15 = 0x4141414141414141,
 rdi = 0x4141414141414141, rsi = 0x4141414141414141, rbp =
0x4141414141414141, rbx = 0x4141414141414141,
 rdx = 0x4141414141414141, rax = 0x4141414141414141, rcx =
0x4141414141414141, rsp = 0x4141414141414141,
 rip = 0x7f0041414141, eflags = 0x0, cs = 0x569, gs = 0x40, fs =
0x0, __pad0 = 0x0, err = 0x7fffffffe078, trapno = 0x1c,
oldmask = 0x2, cr2 = 0x7fffffffe367, {fpstate = 0x7fffffffe376,
__fpstate_word = 0x7fffffffe376}, __reserved1 = {0x0,
 0x7fffffffe4a3, 0x7fffffffe4ae, 0x7fffffffe4c0,
0x7fffffffe4df, 0x7fffffffe514, 0x7fffffffe52b, 0x7fffffffe53b}}

(gdb) si
78 fs->regs.reg[0].loc.offset = (long)&sc->rax - new_cfa;

(gdb)
71 fs->regs.cfa_how = CFA_REG_OFFSET;

(gdb)
73 fs->regs.cfa_reg = 7;

(gdb)
77 fs->regs.reg[0].how = REG_SAVED_OFFSET;

(gdb)
79 fs->regs.reg[1].how = REG_SAVED_OFFSET;

(gdb)
81 fs->regs.reg[2].how = REG_SAVED_OFFSET;

(gdb)
74 fs->regs.cfa_offset = new_cfa - (long) context->cfa;

(gdb)
78 fs->regs.reg[0].loc.offset = (long)&sc->rax - new_cfa;

(gdb)
83 fs->regs.reg[3].how = REG_SAVED_OFFSET;

(gdb)
74 fs->regs.cfa_offset = new_cfa - (long) context->cfa;

(gdb)
78 fs->regs.reg[0].loc.offset = (long)&sc->rax - new_cfa;

(gdb)
80 fs->regs.reg[1].loc.offset = (long)&sc->rdx - new_cfa;

(gdb)
74 fs->regs.cfa_offset = new_cfa - (long) context->cfa;

(gdb)
78 fs->regs.reg[0].loc.offset = (long)&sc->rax - new_cfa;

(gdb)
85 fs->regs.reg[4].how = REG_SAVED_OFFSET;

(gdb)
80 fs->regs.reg[1].loc.offset = (long)&sc->rdx - new_cfa;

(gdb)
87 fs->regs.reg[5].how = REG_SAVED_OFFSET;

(gdb)
89 fs->regs.reg[6].how = REG_SAVED_OFFSET;

(gdb)
92 fs->regs.reg[8].loc.offset = (long)&sc->r8 - new_cfa;

(gdb)
80 fs->regs.reg[1].loc.offset = (long)&sc->rdx - new_cfa;

(gdb)
82 fs->regs.reg[2].loc.offset = (long)&sc->rcx - new_cfa;

(gdb)
92 fs->regs.reg[8].loc.offset = (long)&sc->r8 - new_cfa;

(gdb)
94 fs->regs.reg[9].loc.offset = (long)&sc->r9 - new_cfa;

(gdb)
91 fs->regs.reg[8].how = REG_SAVED_OFFSET;

(gdb)
82 fs->regs.reg[2].loc.offset = (long)&sc->rcx - new_cfa;

(gdb)
93 fs->regs.reg[9].how = REG_SAVED_OFFSET;

(gdb)
95 fs->regs.reg[10].how = REG_SAVED_OFFSET;

(gdb)
94 fs->regs.reg[9].loc.offset = (long)&sc->r9 - new_cfa;

(gdb)
82 fs->regs.reg[2].loc.offset = (long)&sc->rcx - new_cfa;

(gdb)
84 fs->regs.reg[3].loc.offset = (long)&sc->rbx - new_cfa;

(gdb)
94 fs->regs.reg[9].loc.offset = (long)&sc->r9 - new_cfa;

(gdb)
96 fs->regs.reg[10].loc.offset = (long)&sc->r10 - new_cfa;

(gdb)
97 fs->regs.reg[11].how = REG_SAVED_OFFSET;

(gdb)
84 fs->regs.reg[3].loc.offset = (long)&sc->rbx - new_cfa;

(gdb)
99 fs->regs.reg[12].how = REG_SAVED_OFFSET;

(gdb)
101 fs->regs.reg[13].how = REG_SAVED_OFFSET;

(gdb)
96 fs->regs.reg[10].loc.offset = (long)&sc->r10 - new_cfa;

(gdb)
84 fs->regs.reg[3].loc.offset = (long)&sc->rbx - new_cfa;

(gdb)
86 fs->regs.reg[4].loc.offset = (long)&sc->rsi - new_cfa;

(gdb)
96 fs->regs.reg[10].loc.offset = (long)&sc->r10 - new_cfa;

(gdb)
98 fs->regs.reg[11].loc.offset = (long)&sc->r11 - new_cfa;

(gdb)
103 fs->regs.reg[14].how = REG_SAVED_OFFSET;

(gdb)
86 fs->regs.reg[4].loc.offset = (long)&sc->rsi - new_cfa;

(gdb)
105 fs->regs.reg[15].how = REG_SAVED_OFFSET;

(gdb)
98 fs->regs.reg[11].loc.offset = (long)&sc->r11 - new_cfa;

(gdb)
86 fs->regs.reg[4].loc.offset = (long)&sc->rsi - new_cfa;

(gdb)
88 fs->regs.reg[5].loc.offset = (long)&sc->rdi - new_cfa;

(gdb)
98 fs->regs.reg[11].loc.offset = (long)&sc->r11 - new_cfa;

(gdb)
100 fs->regs.reg[12].loc.offset = (long)&sc->r12 - new_cfa;

(gdb)
88 fs->regs.reg[5].loc.offset = (long)&sc->rdi - new_cfa;

(gdb)
100 fs->regs.reg[12].loc.offset = (long)&sc->r12 - new_cfa;

(gdb)
88 fs->regs.reg[5].loc.offset = (long)&sc->rdi - new_cfa;

(gdb)
90 fs->regs.reg[6].loc.offset = (long)&sc->rbp - new_cfa;

(gdb)
100 fs->regs.reg[12].loc.offset = (long)&sc->r12 - new_cfa;

(gdb)
102 fs->regs.reg[13].loc.offset = (long)&sc->r13 - new_cfa;

(gdb)
90 fs->regs.reg[6].loc.offset = (long)&sc->rbp - new_cfa;

(gdb)
102 fs->regs.reg[13].loc.offset = (long)&sc->r13 - new_cfa;

(gdb)
90 fs->regs.reg[6].loc.offset = (long)&sc->rbp - new_cfa;

(gdb)
102 fs->regs.reg[13].loc.offset = (long)&sc->r13 - new_cfa;

(gdb)
104 fs->regs.reg[14].loc.offset = (long)&sc->r14 - new_cfa;

(gdb)
0x00000035c400f1be 104 fs->regs.reg[14].loc.offset =
(long)&sc->r14 - new_cfa;

(gdb)
0x00000035c400f1c1 104 fs->regs.reg[14].loc.offset =
(long)&sc->r14 - new_cfa;

(gdb)
106 fs->regs.reg[15].loc.offset = (long)&sc->r15 - new_cfa;

(gdb)
108 fs->regs.reg[16].loc.offset = (long)&sc->rip - new_cfa;

(gdb)
0x00000035c400f1d4 108 fs->regs.reg[16].loc.offset =
(long)&sc->rip - new_cfa;

(gdb)
106 fs->regs.reg[15].loc.offset = (long)&sc->r15 - new_cfa;

(gdb)
111 return _URC_NO_REASON;

(gdb)
106 fs->regs.reg[15].loc.offset = (long)&sc->r15 - new_cfa;

(gdb)
107 fs->regs.reg[16].how = REG_SAVED_OFFSET;

(gdb)
108 fs->regs.reg[16].loc.offset = (long)&sc->rip - new_cfa;

(gdb)
109 fs->retaddr_column = 16;

(gdb)
110 fs->signal_frame = 1;

(gdb)
0x00000035c400f20d 110 fs->signal_frame = 1;

(gdb)
uw_frame_state_for (context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwind-dw2.c:1296
1296 }

(gdb)
0x00000035c400edf1 1296 }

(gdb) list
1291 insn = aug;
1292 end = (const unsigned char *) next_fde (fde);
1293 execute_cfa_program (insn, end, context, fs);
1294
1295 return _URC_NO_REASON;
1296 }
1297 ^L
1298 typedef struct frame_state
1299 {
1300 void *cfa;

(gdb) si
0x00000035c400edf2 1296 }

(gdb) si
0x00000035c400edf3 1296 }

(gdb)
0x00000035c400edf5 1296 }

(gdb)
0x00000035c400edf7 1296 }

(gdb)
0x00000035c400edf9 1296 }

(gdb)
0x00000035c400edfb 1296 }

(gdb) si
_Unwind_Backtrace (trace=0x35c1909bc0 <backtrace_helper>,
trace_argument=0x7fffffffd620) at ../../../libgcc/unwind.inc:291
291 if (code != _URC_NO_REASON && code != _URC_END_OF_STACK)

(gdb)
290 code = uw_frame_state_for (&context, &fs);

(gdb)
291 if (code != _URC_NO_REASON && code != _URC_END_OF_STACK)

(gdb)
0x00000035c400ff21 291 if (code != _URC_NO_REASON && code != _URC_END_OF_STACK)

(gdb)
0x00000035c400ff23 291 if (code != _URC_NO_REASON && code != _URC_END_OF_STACK)

(gdb)
295 if ((*trace) (&context, trace_argument) != _URC_NO_REASON)

(gdb)
0x00000035c400fef3 295 if ((*trace) (&context, trace_argument) != _URC_NO_REASON)

(gdb) n
299 if (code == _URC_END_OF_STACK)

(gdb) print code
$167 = _URC_NO_REASON

(gdb) n
303 uw_update_context (&context, &fs);

(gdb) print fs->regs.cfa_how
$168 = CFA_REG_OFFSET

(gdb) p/x context->cfa
$169 = 0x7fffffffdfb0

(gdb) si
0x00000035c400ff06 303 uw_update_context (&context, &fs);

(gdb)
0x00000035c400ff09 303 uw_update_context (&context, &fs);

(gdb)
uw_update_context (context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwind-dw2.c:1505
1505 {

(gdb)
0x00000035c400eb51 1505 {

(gdb)
0x00000035c400eb54 1505 {

(gdb)
0x00000035c400eb55 1505 {

(gdb)
0x00000035c400eb58 1505 {

(gdb)
1506 uw_update_context_1 (context, fs);

(gdb)
uw_update_context_1 (context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwind-dw2.c:1382
1382 {

(gdb)
1383 struct _Unwind_Context orig_context = *context;

(gdb) n
1382 {

(gdb)
1383 struct _Unwind_Context orig_context = *context;

(gdb)
1382 {

(gdb)
1383 struct _Unwind_Context orig_context = *context;

(gdb)
1405 if (!_Unwind_GetGRPtr (&orig_context,
__builtin_dwarf_sp_column ()))

(gdb)
1406 _Unwind_SetSpColumn (&orig_context, context->cfa, &tmp_sp);

(gdb)
1407 _Unwind_SetGRPtr (context, __builtin_dwarf_sp_column (), NULL);

(gdb)
1411 switch (fs->regs.cfa_how)

(gdb)
1407 _Unwind_SetGRPtr (context, __builtin_dwarf_sp_column (), NULL);

(gdb)
1411 switch (fs->regs.cfa_how)

(gdb)
1414 cfa = _Unwind_GetPtr (&orig_context, fs->regs.cfa_reg);

(gdb)
1415 cfa += fs->regs.cfa_offset;

(gdb)
1416 break;

(gdb)
1436 switch (fs->regs.reg[i].how)

(gdb)
1432 context->cfa = cfa;

(gdb)
1467 _Unwind_SetGRPtr (context, i, (void *) val);

(gdb)
1436 switch (fs->regs.reg[i].how)

(gdb) 
1443 _Unwind_SetGRPtr (context, i,

(gdb)
1435 for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)

(gdb)
1436 switch (fs->regs.reg[i].how)

(gdb)
1443 _Unwind_SetGRPtr (context, i,

(gdb)
1435 for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)

(gdb)
1436 switch (fs->regs.reg[i].how)

(gdb)
1443 _Unwind_SetGRPtr (context, i,

(gdb)
1435 for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)

(gdb)
1436 switch (fs->regs.reg[i].how)

(gdb)
1443 _Unwind_SetGRPtr (context, i,

(gdb)
1435 for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)

(gdb)
1436 switch (fs->regs.reg[i].how)

(gdb)
1443 _Unwind_SetGRPtr (context, i,

(gdb)
1435 for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)

(gdb)
1436 switch (fs->regs.reg[i].how)

(gdb)
1443 _Unwind_SetGRPtr (context, i,

(gdb)
1435 for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)

(gdb)
1436 switch (fs->regs.reg[i].how)

(gdb)
1443 _Unwind_SetGRPtr (context, i,

(gdb)
1435 for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)

(gdb)
1436 switch (fs->regs.reg[i].how)

(gdb)
1435 for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)

(gdb)
1436 switch (fs->regs.reg[i].how)

(gdb)
1443 _Unwind_SetGRPtr (context, i,

(gdb)
1435 for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)

(gdb)
1436 switch (fs->regs.reg[i].how)

(gdb) 
1443 _Unwind_SetGRPtr (context, i,

(gdb)
1435 for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)

(gdb)
1436 switch (fs->regs.reg[i].how)

(gdb)
1443 _Unwind_SetGRPtr (context, i,

(gdb)
1435 for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)

(gdb)
1436 switch (fs->regs.reg[i].how)

(gdb)
1443 _Unwind_SetGRPtr (context, i,

(gdb)
1435 for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)

(gdb)
1436 switch (fs->regs.reg[i].how)

(gdb)
1443 _Unwind_SetGRPtr (context, i,

(gdb)
1435 for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)

(gdb)
1436 switch (fs->regs.reg[i].how)

(gdb)
1443 _Unwind_SetGRPtr (context, i,

(gdb)
1435 for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)

(gdb)
1436 switch (fs->regs.reg[i].how)

(gdb)
1443 _Unwind_SetGRPtr (context, i,

(gdb)
1435 for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)

(gdb)
1436 switch (fs->regs.reg[i].how)

(gdb)
1443 _Unwind_SetGRPtr (context, i,

(gdb)
1435 for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)

(gdb)
1436 switch (fs->regs.reg[i].how)

(gdb)
1443 _Unwind_SetGRPtr (context, i,

(gdb)
1435 for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)

(gdb)
1436 switch (fs->regs.reg[i].how)

(gdb)
1435 for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)

(gdb)
1491 _Unwind_SetSignalFrame (context, fs->signal_frame);

(gdb) 
1496 }

(gdb)
uw_update_context (context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwind-dw2.c:1514
1514 if (fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (fs->retaddr_column)].how

(gdb)
1523 (_Unwind_GetPtr (context, fs->retaddr_column));

(gdb)
1522 context->ra = __builtin_extract_return_addr

(gdb) n
1524 }

(gdb) print context->ra
$170 = (void *) 0x7f0041414141 <- Newly calculated return address

(gdb) list
1519 else
1520 /* Compute the return address now, since the return address column
1521 can change from frame to frame. */
1522 context->ra = __builtin_extract_return_addr
1523 (_Unwind_GetPtr (context, fs->retaddr_column));
1524 }
1525
1526 static void
1527 uw_advance_context (struct _Unwind_Context *context, _Unwind_FrameState *fs)
1528 {

(gdb) x/20i $rip
=> 0x35c400ebba <uw_update_context+106>: add $0x8,%rsp
 0x35c400ebbe <uw_update_context+110>: pop %rbx
 0x35c400ebbf <uw_update_context+111>: pop %rbp
 0x35c400ebc0 <uw_update_context+112>: retq
 0x35c400ebc1 <uw_update_context+113>: nopl 0x0(%rax)
 0x35c400ebc8 <uw_update_context+120>: movq $0x0,0x98(%rbx)
 0x35c400ebd3 <uw_update_context+131>: add $0x8,%rsp
 0x35c400ebd7 <uw_update_context+135>: pop %rbx
 0x35c400ebd8 <uw_update_context+136>: pop %rbp
 0x35c400ebd9 <uw_update_context+137>: retq
 0x35c400ebda: nopw 0x0(%rax,%rax,1)
 0x35c400ebe0 <uw_frame_state_for>: push %r15
 0x35c400ebe2 <uw_frame_state_for+2>: mov $0x180,%edx
 0x35c400ebe7 <uw_frame_state_for+7>: push %r14
 0x35c400ebe9 <uw_frame_state_for+9>: push %r13
 0x35c400ebeb <uw_frame_state_for+11>: mov %rdi,%r13
 0x35c400ebee <uw_frame_state_for+14>: mov %rsi,%rdi
 0x35c400ebf1 <uw_frame_state_for+17>: push %r12
 0x35c400ebf3 <uw_frame_state_for+19>: mov %rsi,%r12
 0x35c400ebf6 <uw_frame_state_for+22>: push %rbp

(gdb) i r rcx
rcx 0x7f0041414141 139639071523137

(gdb) p/x *fs
$171 = {regs = {reg = {{loc = {reg = 0xbebf3ebebebe9eff, offset =
0xbebf3ebebebe9eff, exp = 0xbebf3ebebebe9eff}, how = 0x1},
 {loc = {reg = 0xbebf3ebebebe9ef7, offset =
0xbebf3ebebebe9ef7, exp = 0xbebf3ebebebe9ef7}, how = 0x1}, {loc =
{
 reg = 0xbebf3ebebebe9f07, offset = 0xbebf3ebebebe9f07,
exp = 0xbebf3ebebebe9f07}, how = 0x1}, {loc = {
 reg = 0xbebf3ebebebe9eef, offset = 0xbebf3ebebebe9eef,
exp = 0xbebf3ebebebe9eef}, how = 0x1}, {loc = {
 reg = 0xbebf3ebebebe9edf, offset = 0xbebf3ebebebe9edf,
exp = 0xbebf3ebebebe9edf}, how = 0x1}, {loc = {
 reg = 0xbebf3ebebebe9ed7, offset = 0xbebf3ebebebe9ed7,
exp = 0xbebf3ebebebe9ed7}, how = 0x1}, {loc = {
 reg = 0xbebf3ebebebe9ee7, offset = 0xbebf3ebebebe9ee7,
exp = 0xbebf3ebebebe9ee7}, how = 0x1}, {loc = {reg = 0x0,
 offset = 0x0, exp = 0x0}, how = 0x0}, {loc = {reg =
0xbebf3ebebebe9e97, offset = 0xbebf3ebebebe9e97,
 exp = 0xbebf3ebebebe9e97}, how = 0x1}, {loc = {reg =
0xbebf3ebebebe9e9f, offset = 0xbebf3ebebebe9e9f,
 exp = 0xbebf3ebebebe9e9f}, how = 0x1}, {loc = {reg =
0xbebf3ebebebe9ea7, offset = 0xbebf3ebebebe9ea7,
 exp = 0xbebf3ebebebe9ea7}, how = 0x1}, {loc = {reg =
0xbebf3ebebebe9eaf, offset = 0xbebf3ebebebe9eaf,
 exp = 0xbebf3ebebebe9eaf}, how = 0x1}, {loc = {reg =
0xbebf3ebebebe9eb7, offset = 0xbebf3ebebebe9eb7,
 exp = 0xbebf3ebebebe9eb7}, how = 0x1}, {loc = {reg =
0xbebf3ebebebe9ebf, offset = 0xbebf3ebebebe9ebf,
 exp = 0xbebf3ebebebe9ebf}, how = 0x1}, {loc = {reg =
0xbebf3ebebebe9ec7, offset = 0xbebf3ebebebe9ec7,
 exp = 0xbebf3ebebebe9ec7}, how = 0x1}, {loc = {reg =
0xbebf3ebebebe9ecf, offset = 0xbebf3ebebebe9ecf,
 exp = 0xbebf3ebebebe9ecf}, how = 0x1}, {loc = {reg =
0xbebf3ebebebe9f17, offset = 0xbebf3ebebebe9f17,
 exp = 0xbebf3ebebebe9f17}, how = 0x1}, {loc = {reg =
0x0, offset = 0x0, exp = 0x0}, how = 0x0}}, prev = 0x0,
 cfa_offset = 0x4140c14141416191, cfa_reg = 0x7, cfa_exp =
0x0, cfa_how = 0x1}, pc = 0x0, personality = 0x0,
 data_align = 0x0, code_align = 0x0, retaddr_column = 0x10,
fde_encoding = 0x0, lsda_encoding = 0x0, saw_z = 0x0,
 signal_frame = 0x1, eh_ptr = 0x0}

(gdb)

Самые последние значения в контексте и переменные состояния соответствуют относительным смещениям от значений 0x4141414141414141. Следующая итерация основного цикла в итоге приводит к AV при чтении потому, как только что вычисленный адрес возврата указывает на недостижимую область памяти. Мы вернулись к исходной точке.

Сценарий 4b – давайте попробуем проверить эмулирутся ли инструкции DWARF если мы полностью контролируем FDE:

(gdb) r `perl -e 'print "A"x300'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/pi3/test `perl -e 'print "A"x300'`
DONE!
*** stack smashing detected ***: /home/pi3/test terminated
Breakpoint 9, uw_frame_state_for
(context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd1c0)
 at ../../../libgcc/unwind-dw2.c:1233
1233 {

(gdb) c
Continuing.
...
<keep going until we hit what we want...> #####
...

(gdb) source pi3-test <- prepare the memory and do padding #####
Breakpoint 18 at 0x35c400ec8c: file ../../../libgcc/unwind-dw2.c,
line 1247.
Breakpoint 18, uw_frame_state_for
(context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460)
 at ../../../libgcc/unwind-dw2.c:1247
1247 if (fde == NULL)
1245 fde = _Unwind_Find_FDE (context->ra +
_Unwind_IsSignalFrame (context) - 1,
1247 if (fde == NULL)
1259 fs->pc = context->bases.func;
get_cie (f=<optimized out>) at ../../../libgcc/unwind-dw2-
fde.h:157
157 return (const void *)&f->CIE_delta - f->CIE_delta;
uw_frame_state_for (context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwind-dw2.c:1259
1259 fs->pc = context->bases.func;
get_cie (f=0x7ffffffde101) at ../../../libgcc/unwind-dw2-
fde.h:157
157 return (const void *)&f->CIE_delta - f->CIE_delta;
0x00000035c400ecaf 157 return (const void *)&f->CIE_delta -
f->CIE_delta;

(gdb) si
extract_cie_info (fs=0x7fffffffd460, context=0x7fffffffd370,
cie=0x7fffbebc9fc4) at ../../../libgcc/unwind-dw2.c:415
415 const unsigned char *aug = cie->augmentation;

(gdb) set cie = context->ra

(gdb) print *cie
$138 = {length = 1094795585, CIE_id = 1094795585, version = 65
'A', augmentation = 0x7ffffffde10a 'A' <repeats 200 times>...}

(gdb) set cie->version = 0x1
...
<fix the memory to avoid killing by gcc_unreachable() or gcc_assert()> #####
...

(gdb) print *cie
$139 = {length = 1094795585, CIE_id = 1094795585, version = 1
'\001',
 augmentation = 0x7ffffffde10a 'A' <repeats 200 times>...}

(gdb) si
416 const unsigned char *p = aug + strlen ((const char *)aug)
+ 1;

(gdb)
0x00000035c400ecb9 416 const unsigned char *p = aug +
strlen ((const char *)aug) + 1;

(gdb) n
423 if (aug[0] == 'e' && aug[1] == 'h')

(gdb) si
416 const unsigned char *p = aug + strlen ((const char *)aug) + 1;

(gdb)
423 if (aug[0] == 'e' && aug[1] == 'h')

(gdb)
433 if (__builtin_expect (cie->version >= 4, 0))

(gdb) x/20i $rip
=> 0x35c400ecce <uw_frame_state_for+238>: cmpb $0x3,0x8(%r14)
 0x35c400ecd3 <uw_frame_state_for+243>: ja 0x35c400f592
<uw_frame_state_for+2482>
 0x35c400ecd9 <uw_frame_state_for+249>: xor %esi,%esi
 0x35c400ecdb <uw_frame_state_for+251>: xor %ecx,%ecx
 0x35c400ecdd <uw_frame_state_for+253>: nopl (%rax)
 0x35c400ece0 <uw_frame_state_for+256>: add $0x1,%rdi
 0x35c400ece4 <uw_frame_state_for+260>: movzbl -
0x1(%rdi),%edx
 0x35c400ece8 <uw_frame_state_for+264>: mov %rdx,%rax
 0x35c400eceb <uw_frame_state_for+267>: and $0x7f,%eax
 0x35c400ecee <uw_frame_state_for+270>: shl %cl,%rax
 0x35c400ecf1 <uw_frame_state_for+273>: add $0x7,%ecx
 0x35c400ecf4 <uw_frame_state_for+276>: or %rax,%rsi
 0x35c400ecf7 <uw_frame_state_for+279>: test %dl,%dl
 0x35c400ecf9 <uw_frame_state_for+281>: js 0x35c400ece0
<uw_frame_state_for+256>
 0x35c400ecfb <uw_frame_state_for+283>: mov
%rsi,0x160(%r12)
 0x35c400ed03 <uw_frame_state_for+291>: lea
0x40(%rsp),%rsi
 0x35c400ed08 <uw_frame_state_for+296>: callq 0x35c400d090
<read_sleb128>
 0x35c400ed0d <uw_frame_state_for+301>: mov %rax,%rbx
 0x35c400ed10 <uw_frame_state_for+304>: mov
0x40(%rsp),%rax
 0x35c400ed15 <uw_frame_state_for+309>: xor %esi,%esi

(gdb) x/x $r14+0x8
0x7ffffffde109: 0x01

(gdb) si
0x00000035c400ecd3 433 if (__builtin_expect (cie->version
>= 4, 0))

(gdb)
x86_64_fallback_frame_state (context=<optimized out>,
context=<optimized out>, fs=<optimized out>)
 at ./md-unwind-support.h:68
68 return _URC_END_OF_STACK;

(gdb) x/20i $rip
=> 0x35c400ecd9 <uw_frame_state_for+249>: xor %esi,%esi
 0x35c400ecdb <uw_frame_state_for+251>: xor %ecx,%ecx
 0x35c400ecdd <uw_frame_state_for+253>: nopl (%rax)
 0x35c400ece0 <uw_frame_state_for+256>: add $0x1,%rdi
 0x35c400ece4 <uw_frame_state_for+260>: movzbl -
0x1(%rdi),%edx
 0x35c400ece8 <uw_frame_state_for+264>: mov %rdx,%rax
 0x35c400eceb <uw_frame_state_for+267>: and $0x7f,%eax
 0x35c400ecee <uw_frame_state_for+270>: shl %cl,%rax
 0x35c400ecf1 <uw_frame_state_for+273>: add $0x7,%ecx
 0x35c400ecf4 <uw_frame_state_for+276>: or %rax,%rsi
 0x35c400ecf7 <uw_frame_state_for+279>: test %dl,%dl
 0x35c400ecf9 <uw_frame_state_for+281>: js 0x35c400ece0
<uw_frame_state_for+256>
 0x35c400ecfb <uw_frame_state_for+283>: mov
%rsi,0x160(%r12)
 0x35c400ed03 <uw_frame_state_for+291>: lea
0x40(%rsp),%rsi
 0x35c400ed08 <uw_frame_state_for+296>: callq 0x35c400d090
<read_sleb128>
 0x35c400ed0d <uw_frame_state_for+301>: mov %rax,%rbx
 0x35c400ed10 <uw_frame_state_for+304>: mov
0x40(%rsp),%rax
 0x35c400ed15 <uw_frame_state_for+309>: xor %esi,%esi
 0x35c400ed17 <uw_frame_state_for+311>: xor %ecx,%ecx
 0x35c400ed19 <uw_frame_state_for+313>: mov
%rax,0x158(%r12)

(gdb) si
0x00000035c400ecdb 68 return _URC_END_OF_STACK;

(gdb)
0x00000035c400ecdd 68 return _URC_END_OF_STACK;

(gdb) x/20i $rip
=> 0x35c400ecdd <uw_frame_state_for+253>: nopl (%rax)
 0x35c400ece0 <uw_frame_state_for+256>: add $0x1,%rdi
 0x35c400ece4 <uw_frame_state_for+260>: movzbl -
0x1(%rdi),%edx
 0x35c400ece8 <uw_frame_state_for+264>: mov %rdx,%rax
 0x35c400eceb <uw_frame_state_for+267>: and $0x7f,%eax
 0x35c400ecee <uw_frame_state_for+270>: shl %cl,%rax
 0x35c400ecf1 <uw_frame_state_for+273>: add $0x7,%ecx
 0x35c400ecf4 <uw_frame_state_for+276>: or %rax,%rsi
 0x35c400ecf7 <uw_frame_state_for+279>: test %dl,%dl
 0x35c400ecf9 <uw_frame_state_for+281>: js 0x35c400ece0
<uw_frame_state_for+256>
 0x35c400ecfb <uw_frame_state_for+283>: mov
%rsi,0x160(%r12)
 0x35c400ed03 <uw_frame_state_for+291>: lea
0x40(%rsp),%rsi
 0x35c400ed08 <uw_frame_state_for+296>: callq 0x35c400d090
<read_sleb128>
 0x35c400ed0d <uw_frame_state_for+301>: mov %rax,%rbx
 0x35c400ed10 <uw_frame_state_for+304>: mov
0x40(%rsp),%rax
 0x35c400ed15 <uw_frame_state_for+309>: xor %esi,%esi
 0x35c400ed17 <uw_frame_state_for+311>: xor %ecx,%ecx
 0x35c400ed19 <uw_frame_state_for+313>: mov
%rax,0x158(%r12)
 0x35c400ed21 <uw_frame_state_for+321>: cmpb $0x1,0x8(%r14)
 0x35c400ed26 <uw_frame_state_for+326>: je 0x35c400f270
<uw_frame_state_for+1680>

(gdb) si
read_uleb128 (val=<optimized out>, p=0x7ffffffdf0a5 "") at
../../../libgcc/unwind-pe.h:140
140 byte = *p++;

(gdb)
0x00000035c400ece4 140 byte = *p++;

(gdb)
141 result |= ((_uleb128_t)byte & 0x7f) << shift;

(gdb)
0x00000035c400eceb 141 result |= ((_uleb128_t)byte &
0x7f) << shift;

(gdb)
0x00000035c400ecee 141 result |= ((_uleb128_t)byte &
0x7f) << shift;

(gdb)
142 shift += 7;

(gdb)
141 result |= ((_uleb128_t)byte & 0x7f) << shift;

(gdb)
144 while (byte & 0x80);

(gdb)
0x00000035c400ecf9 144 while (byte & 0x80);

(gdb)
extract_cie_info (fs=0x7fffffffd460, context=0x7fffffffd370,
cie=0x7ffffffde101) at ../../../libgcc/unwind-dw2.c:442
442 fs->code_align = (_Unwind_Word)utmp;

(gdb) print utmp
$140 = 0

(gdb) si
443 p = read_sleb128 (p, &stmp);

(gdb) x/20i $rip
=> 0x35c400ed03 <uw_frame_state_for+291>: lea
0x40(%rsp),%rsi
 0x35c400ed08 <uw_frame_state_for+296>: callq 0x35c400d090
<read_sleb128>
 0x35c400ed0d <uw_frame_state_for+301>: mov %rax,%rbx
 0x35c400ed10 <uw_frame_state_for+304>: mov
0x40(%rsp),%rax
 0x35c400ed15 <uw_frame_state_for+309>: xor %esi,%esi
 0x35c400ed17 <uw_frame_state_for+311>: xor %ecx,%ecx
 0x35c400ed19 <uw_frame_state_for+313>: mov
%rax,0x158(%r12)
 0x35c400ed21 <uw_frame_state_for+321>: cmpb $0x1,0x8(%r14)
 0x35c400ed26 <uw_frame_state_for+326>: je 0x35c400f270
<uw_frame_state_for+1680>
 0x35c400ed2c <uw_frame_state_for+332>: nopl 0x0(%rax)
 0x35c400ed30 <uw_frame_state_for+336>: add $0x1,%rbx
 0x35c400ed34 <uw_frame_state_for+340>: movzbl -
0x1(%rbx),%edx
 0x35c400ed38 <uw_frame_state_for+344>: mov %rdx,%rax
 0x35c400ed3b <uw_frame_state_for+347>: and $0x7f,%eax
 0x35c400ed3e <uw_frame_state_for+350>: shl %cl,%rax
 0x35c400ed41 <uw_frame_state_for+353>: add $0x7,%ecx
 0x35c400ed44 <uw_frame_state_for+356>: or %rax,%rsi
 0x35c400ed47 <uw_frame_state_for+359>: test %dl,%dl
 0x35c400ed49 <uw_frame_state_for+361>: js 0x35c400ed30
<uw_frame_state_for+336>
 0x35c400ed4b <uw_frame_state_for+363>: mov
%rsi,0x168(%r12)

(gdb) si
0x00000035c400ed08 443 p = read_sleb128 (p, &stmp);

(gdb)
read_sleb128 (p=0x7ffffffdf0a6 "", val=val@entry=0x7fffffffd320)
at ../../../libgcc/unwind-pe.h:154
154 {

(gdb)
159 result = 0;

(gdb)
155 unsigned int shift = 0;

(gdb)
0x00000035c400d098 155 unsigned int shift = 0;

(gdb)
162 byte = *p++;

(gdb)
0x00000035c400d0a4 162 byte = *p++;

(gdb)
163 result |= ((_uleb128_t)byte & 0x7f) << shift;

(gdb)
0x00000035c400d0ab 163 result |= ((_uleb128_t)byte &
0x7f) << shift;

(gdb)
0x00000035c400d0ae 163 result |= ((_uleb128_t)byte &
0x7f) << shift;

(gdb)
164 shift += 7;

(gdb)
163 result |= ((_uleb128_t)byte & 0x7f) << shift;

(gdb)
166 while (byte & 0x80);

(gdb)
0x00000035c400d0ba 166 while (byte & 0x80);

(gdb)
169 if (shift < 8 * sizeof(result) && (byte & 0x40) != 0)

(gdb)
0x00000035c400d0bf 169 if (shift < 8 * sizeof(result) &&
(byte & 0x40) != 0)

(gdb)
0x00000035c400d0c1 169 if (shift < 8 * sizeof(result) &&
(byte & 0x40) != 0)

(gdb)
0x00000035c400d0c4 169 if (shift < 8 * sizeof(result) &&
(byte & 0x40) != 0)

(gdb)
172 *val = (_sleb128_t) result;

(gdb)
174 }

(gdb)
0x00000035c400ed0d in extract_cie_info (fs=0x7fffffffd460,
context=0x7fffffffd370, cie=0x7ffffffde101)
 at ../../../libgcc/unwind-dw2.c:443
443 p = read_sleb128 (p, &stmp);

(gdb)
444 fs->data_align = (_Unwind_Sword)stmp;

(gdb)
445 if (cie->version == 1)

(gdb)
0x00000035c400ed17 445 if (cie->version == 1)

(gdb)
444 fs->data_align = (_Unwind_Sword)stmp;

(gdb)
445 if (cie->version == 1)

(gdb)
0x00000035c400ed26 445 if (cie->version == 1)

(gdb)
446 fs->retaddr_column = *p++;

(gdb)
0x00000035c400f273 446 fs->retaddr_column = *p++;

(gdb)
0x00000035c400f277 446 fs->retaddr_column = *p++;

(gdb)
0x00000035c400f27f 446 fs->retaddr_column = *p++;

(gdb)
452 fs->lsda_encoding = DW_EH_PE_omit;

(gdb)
457 if (*aug == 'z')

(gdb)
417 const unsigned char *ret = NULL;

(gdb)
457 if (*aug == 'z')

(gdb)
0x00000035c400ed65 457 if (*aug == 'z')

(gdb)
read_encoded_value_with_base (val=<optimized out>, p=<optimized
out>, base=<optimized out>, encoding=<optimized out>)
 at ../../../libgcc/unwind-pe.h:225
225 p = read_sleb128 (p, &tmp);

(gdb)
0x00000035c400ed70 225 p = read_sleb128 (p, &tmp);

(gdb)
207 switch (encoding & 0x0f)

(gdb)
225 p = read_sleb128 (p, &tmp);

(gdb)
0x00000035c400ed80 225 p = read_sleb128 (p, &tmp);

(gdb)
extract_cie_info (fs=0x7fffffffd460, context=0x7fffffffd370,
cie=0x7ffffffde101) at ../../../libgcc/unwind-dw2.c:467
467 while (*aug != '\0')

(gdb)
0x00000035c400edab 467 while (*aug != '\0')

(gdb)
470 if (aug[0] == 'L')

(gdb)
0x00000035c400edb3 470 if (aug[0] == 'L')

(gdb)
477 else if (aug[0] == 'R')

(gdb)
0x00000035c400ed8a 477 else if (aug[0] == 'R')

(gdb)
484 else if (aug[0] == 'P')

(gdb)
0x00000035c400ed8e 484 else if (aug[0] == 'P')

(gdb)
494 else if (aug[0] == 'S')

(gdb)
0x00000035c400ed92 494 else if (aug[0] == 'S')

(gdb)
494 else if (aug[0] == 'S')

(gdb)
uw_frame_state_for (context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwind-dw2.c:1263
1263 if (insn == NULL)

(gdb)
0x00000035c400f2b6 1263 if (insn == NULL)

(gdb) list
1258
1259 fs->pc = context->bases.func;
1260
1261 cie = get_cie (fde);
1262 insn = extract_cie_info (cie, context, fs);
1263 if (insn == NULL)
1264 /* CIE contained unknown augmentation. */
1265 return _URC_FATAL_PHASE1_ERROR;
1266
1267 /* First decode all the insns in the CIE. */

(gdb) x/20i $rip
=> 0x35c400f2b6 <uw_frame_state_for+1750>: jne 0x35c400eeac
<uw_frame_state_for+716>
 0x35c400f2bc <uw_frame_state_for+1756>: add $0x58,%rsp
 0x35c400f2c0 <uw_frame_state_for+1760>: mov $0x3,%eax
 0x35c400f2c5 <uw_frame_state_for+1765>: pop %rbx
 0x35c400f2c6 <uw_frame_state_for+1766>: pop %rbp
 0x35c400f2c7 <uw_frame_state_for+1767>: pop %r12
 0x35c400f2c9 <uw_frame_state_for+1769>: pop %r13
 0x35c400f2cb <uw_frame_state_for+1771>: pop %r14
 0x35c400f2cd <uw_frame_state_for+1773>: pop %r15
 0x35c400f2cf <uw_frame_state_for+1775>: retq
 0x35c400f2d0 <uw_frame_state_for+1776>: lea 0x1(%rsi),%rdi
 0x35c400f2d4 <uw_frame_state_for+1780>: movb $0x0,(%rsi)
 0x35c400f2d7 <uw_frame_state_for+1783>: mov $0x7f,%dl
 0x35c400f2d9 <uw_frame_state_for+1785>: test $0x2,%dil
 0x35c400f2dd <uw_frame_state_for+1789>: je 0x35c400ec10
<uw_frame_state_for+48>
 0x35c400f2e3 <uw_frame_state_for+1795>: nopl
0x0(%rax,%rax,1)
 0x35c400f2e8 <uw_frame_state_for+1800>: xor %ecx,%ecx
 0x35c400f2ea <uw_frame_state_for+1802>: add $0x2,%rdi
 0x35c400f2ee <uw_frame_state_for+1806>: sub $0x2,%edx
 0x35c400f2f1 <uw_frame_state_for+1809>: mov %cx,-0x2(%rdi)

(gdb) print context->ra
$142 = (void *) 0x7ffffffde101

(gdb) set $rdi = context->ra

(gdb) si
uw_frame_state_for (context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwind-dw2.c:1263
1263 if (insn == NULL)

(gdb) x/20i $rip
=> 0x35c400f2b3 <uw_frame_state_for+1747>: test %rdi,%rdi
 0x35c400f2b6 <uw_frame_state_for+1750>: jne 0x35c400eeac
<uw_frame_state_for+716>
 0x35c400f2bc <uw_frame_state_for+1756>: add $0x58,%rsp
 0x35c400f2c0 <uw_frame_state_for+1760>: mov $0x3,%eax
 0x35c400f2c5 <uw_frame_state_for+1765>: pop %rbx
 0x35c400f2c6 <uw_frame_state_for+1766>: pop %rbp
 0x35c400f2c7 <uw_frame_state_for+1767>: pop %r12
 0x35c400f2c9 <uw_frame_state_for+1769>: pop %r13
 0x35c400f2cb <uw_frame_state_for+1771>: pop %r14
 0x35c400f2cd <uw_frame_state_for+1773>: pop %r15
 0x35c400f2cf <uw_frame_state_for+1775>: retq
 0x35c400f2d0 <uw_frame_state_for+1776>: lea 0x1(%rsi),%rdi
 0x35c400f2d4 <uw_frame_state_for+1780>: movb $0x0,(%rsi)
 0x35c400f2d7 <uw_frame_state_for+1783>: mov $0x7f,%dl
 0x35c400f2d9 <uw_frame_state_for+1785>: test $0x2,%dil
 0x35c400f2dd <uw_frame_state_for+1789>: je 0x35c400ec10
<uw_frame_state_for+48>
 0x35c400f2e3 <uw_frame_state_for+1795>: nopl
0x0(%rax,%rax,1)
 0x35c400f2e8 <uw_frame_state_for+1800>: xor %ecx,%ecx
 0x35c400f2ea <uw_frame_state_for+1802>: add $0x2,%rdi
 0x35c400f2ee <uw_frame_state_for+1806>: sub $0x2,%edx

(gdb) i r rdi r9
rdi 0x7ffffffde101 140737488216321
r9 0x7ffffffde101 140737488216321

(gdb) si
0x00000035c400f2b6 1263 if (insn == NULL)

(gdb)
next_fde (f=0x7ffffffde101) at ../../../libgcc/unwind-dw2-
fde.h:163
163 return (const fde *) ((const char *) f + f->length +
sizeof (f->length));

(gdb) si
uw_frame_state_for (context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwind-dw2.c:1269
1269 execute_cfa_program (insn, end, context, fs);

(gdb) print context
$144 = (struct _Unwind_Context *) 0x7fffffffd370

(gdb) print *context
$145 = {reg = {0x0, 0x0, 0x0, 0x7fffffffdef8, 0x0, 0x0,
0x7fffffffdfa0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7fffffffdec0,
 0x7fffffffdec8, 0x7fffffffded0, 0x7fffffffded8,
0x7fffffffdfa8, 0x0}, cfa = 0x7fffffffdfb0, ra = 0x7ffffffde101,
 lsda = 0x0, bases = {tbase = 0x0, dbase = 0x0, func = 0x400630
<main>}, flags = 4611686018427387904, version = 0,
 args_size = 0, by_value = '\000' <repeats 17 times>}

(gdb) si
0x00000035c400eeb2 1269 execute_cfa_program (insn, end,
context, fs);

(gdb)
next_fde (f=0x7ffffffde101) at ../../../libgcc/unwind-dw2-
fde.h:163
163 return (const fde *) ((const char *) f + f->length +
sizeof (f->length));

(gdb) list
158 }
159
160 static inline const fde *
161 next_fde (const fde *f)
162 {
163 return (const fde *) ((const char *) f + f->length + sizeof (f->length));
164 }
165
166 extern const fde * _Unwind_Find_FDE (void *, struct dwarf_eh_bases *);
167

(gdb) si
uw_frame_state_for (context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwind-dw2.c:1269
1269 execute_cfa_program (insn, end, context, fs);

(gdb)
execute_cfa_program (insn_ptr=0x7ffffffde101 "AAAAAAAA\001", 'A'
<repeats 191 times>...,
 insn_end=0x8000413f2246 <Address 0x8000413f2246 out of
bounds>, context=context@entry=0x7fffffffd370,
 fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwinddw2.c:942
942 {

(gdb) si
0x00000035c400d431 942 {

(gdb) si
0x00000035c400d434 942 {

(gdb)
0x00000035c400d436 942 {

(gdb)
0x00000035c400d439 942 {

(gdb)
0x00000035c400d43b 942 {

(gdb)
0x00000035c400d43d 942 {

(gdb)
0x00000035c400d440 942 {

(gdb)
0x00000035c400d442 942 {

(gdb)
0x00000035c400d443 942 {

(gdb)
0x00000035c400d446 942 {

(gdb)
957 while (insn_ptr < insn_end

(gdb) print insn_ptr
$146 = (const unsigned char *) 0x7ffffffde101 "AAAAAAAA\001", 'A'
<repeats 191 times>...

(gdb) print insn_end
$147 = (const unsigned char *) 0x8000413f2246 <Address
0x8000413f2246 out of bounds>

(gdb) print insn_end - insn_ptr
$148 = 1094795589

(gdb) si
946 fs->regs.prev = NULL;

(gdb)
957 while (insn_ptr < insn_end

(gdb)
_Unwind_IsSignalFrame (context=<optimized out>) at
../../../libgcc/unwind-dw2.c:202
202 return (context->flags & SIGNAL_FRAME_BIT) ? 1 : 0;

(gdb) list
197 read_8s (const void *p) { const union unaligned *up = p; return up->s8; }
198 ^L
199 static inline _Unwind_Word
200 _Unwind_IsSignalFrame (struct _Unwind_Context *context)
201 {
202 return (context->flags & SIGNAL_FRAME_BIT) ? 1 : 0;
203 }
204
205 static inline void
206 _Unwind_SetSignalFrame (struct _Unwind_Context *context,
int val)

(gdb) si
execute_cfa_program (insn_ptr=0x7ffffffde101 "AAAAAAAA\001", 'A'
<repeats 191 times>...,
 insn_end=0x8000413f2246 <Address 0x8000413f2246 out of
bounds>, context=context@entry=0x7fffffffd370,
 fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwinddw2.c:958
958 && fs->pc < context->ra + _Unwind_IsSignalFrame (context))

(gdb) list
953 there are delay instructions that adjust the stack,these must be
954 reflected at the point immediately before the call insn.
955 In signal frames, return address is after last completed instruction,
956 so we add 1 to return address to make the comparison <=. */
957 while (insn_ptr < insn_end
958 && fs->pc < context->ra + _Unwind_IsSignalFrame (context))
959 {
960 unsigned char insn = *insn_ptr++;
961 _uleb128_t reg, utmp;
962 _sleb128_t offset, stmp;

(gdb) print fs->pc
$149 = (void *) 0x400630 <main>

(gdb) print context->ra
$150 = (void *) 0x7ffffffde101

(gdb) si
0x00000035c400d46c 958 && fs->pc < context->ra +
_Unwind_IsSignalFrame (context))

(gdb) x/20i $rip
=> 0x35c400d46c <execute_cfa_program+60>: mov %rdx,%r14
 0x35c400d46f <execute_cfa_program+63>: shr $0x3f,%rax
 0x35c400d473 <execute_cfa_program+67>: add
0x98(%rdx),%rax
 0x35c400d47a <execute_cfa_program+74>: cmp %rax,%rcx
 0x35c400d47d <execute_cfa_program+77>: jae 0x35c400d50b
<execute_cfa_program+219>
 0x35c400d483 <execute_cfa_program+83>: lea -
0x38(%rbp),%rax
 0x35c400d487 <execute_cfa_program+87>: lea
0x5162(%rip),%rdx # 0x35c40125f0
 0x35c400d48e <execute_cfa_program+94>: xor %r8d,%r8d
 0x35c400d491 <execute_cfa_program+97>: mov %rax,-
0x48(%rbp)
 0x35c400d495 <execute_cfa_program+101>: nopl (%rax)
 0x35c400d498 <execute_cfa_program+104>: movzbl (%rbx),%eax
 0x35c400d49b <execute_cfa_program+107>: lea 0x1(%rbx),%r12
 0x35c400d49f <execute_cfa_program+111>: mov %eax,%esi
 0x35c400d4a1 <execute_cfa_program+113>: and
$0xffffffc0,%esi
 0x35c400d4a4 <execute_cfa_program+116>: cmp $0x40,%sil
 0x35c400d4a8 <execute_cfa_program+120>: je 0x35c400d4d0
<execute_cfa_program+160>
 0x35c400d4aa <execute_cfa_program+122>: cmp $0x80,%sil
 0x35c400d4ae <execute_cfa_program+126>: je 0x35c400d520
<execute_cfa_program+240>
 0x35c400d4b0 <execute_cfa_program+128>: cmp $0xc0,%sil
 0x35c400d4b4 <execute_cfa_program+132>: je 0x35c400d578
<execute_cfa_program+328>

(gdb) si
_Unwind_IsSignalFrame (context=0x7fffffffd370) at
../../../libgcc/unwind-dw2.c:202
202 return (context->flags & SIGNAL_FRAME_BIT) ? 1 : 0;

(gdb)
execute_cfa_program (insn_ptr=0x7ffffffde101 "AAAAAAAA\001", 'A'
<repeats 191 times>...,
 insn_end=0x8000413f2246 <Address 0x8000413f2246 out of
bounds>, context=context@entry=0x7fffffffd370,
 fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwinddw2.c:958
958 && fs->pc < context->ra + _Unwind_IsSignalFrame
(context))

(gdb) si
0x00000035c400d47a 958 && fs->pc < context->ra +
_Unwind_IsSignalFrame (context))

(gdb)
0x00000035c400d47d 958 && fs->pc < context->ra +
_Unwind_IsSignalFrame (context))

(gdb)
1169 insn_ptr = read_sleb128 (insn_ptr, &stmp);

(gdb)
985 else switch (insn)

(gdb) print insn
$151 = <optimized out>

(gdb) b *0x35c400d495
Breakpoint 19 at 0x35c400d495: file ../../../libgcc/unwind-dw2.c,
line 1169.

(gdb) c
Continuing.
Breakpoint 19, 0x00000035c400d495 in execute_cfa_program (
 insn_ptr=0x7ffffffde101 "AAAAAAAA\001", 'A' <repeats 191
times>...,
 insn_end=0x8000413f2246 <Address 0x8000413f2246 out of
bounds>, context=context@entry=0x7fffffffd370,
 fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwinddw2.c:1169
1169 insn_ptr = read_sleb128 (insn_ptr, &stmp);

(gdb) x/20i $rip-10
 0x35c400d48b <execute_cfa_program+91>: push %rcx
 0x35c400d48c <execute_cfa_program+92>: add %al,(%rax)
 0x35c400d48e <execute_cfa_program+94>: xor %r8d,%r8d
 0x35c400d491 <execute_cfa_program+97>: mov %rax,-
0x48(%rbp)
=> 0x35c400d495 <execute_cfa_program+101>: nopl (%rax)
 0x35c400d498 <execute_cfa_program+104>: movzbl (%rbx),%eax
 0x35c400d49b <execute_cfa_program+107>: lea 0x1(%rbx),%r12
 0x35c400d49f <execute_cfa_program+111>: mov %eax,%esi
 0x35c400d4a1 <execute_cfa_program+113>: and
$0xffffffc0,%esi
 0x35c400d4a4 <execute_cfa_program+116>: cmp $0x40,%sil
 0x35c400d4a8 <execute_cfa_program+120>: je 0x35c400d4d0
<execute_cfa_program+160>
 0x35c400d4aa <execute_cfa_program+122>: cmp $0x80,%sil
 0x35c400d4ae <execute_cfa_program+126>: je 0x35c400d520
<execute_cfa_program+240>
 0x35c400d4b0 <execute_cfa_program+128>: cmp $0xc0,%sil
 0x35c400d4b4 <execute_cfa_program+132>: je 0x35c400d578
<execute_cfa_program+328>
 0x35c400d4ba <execute_cfa_program+138>: cmp $0x2f,%al
 0x35c400d4bc <execute_cfa_program+140>: ja 0x35c400d5a9
<execute_cfa_program+377>
 0x35c400d4c2 <execute_cfa_program+146>: movslq
(%rdx,%rax,4),%rax
 0x35c400d4c6 <execute_cfa_program+150>: add %rdx,%rax
 0x35c400d4c9 <execute_cfa_program+153>: jmpq *%rax

(gdb) x/x $rbx
0x7ffffffde101: 0x41

(gdb) set *(0x7ffffffde101) = 0x1

(gdb) x/8x 0x7ffffffde101
0x7ffffffde101: 0x01 0x00 0x00 0x00 0x41 0x41 0x41 0x41

(gdb) set *(0x7ffffffde101) = 0x80 <- emulate DWARF instruction ##### Ж
...
<fix the memory to avoid killing by gcc_unreachable() or gcc_assert()> #####
...

(gdb) si
960 unsigned char insn = *insn_ptr++;

(gdb) 
0x00000035c400d49b 960 unsigned char insn =

(gdb)
964 if ((insn & 0xc0) == DW_CFA_advance_loc)

(gdb)
0x00000035c400d4a1 964 if ((insn & 0xc0) ==

DW_CFA_advance_loc)

(gdb)
0x00000035c400d4a4 964 if ((insn & 0xc0) ==
DW_CFA_advance_loc)

(gdb)
0x00000035c400d4a8 964 if ((insn & 0xc0) ==
DW_CFA_advance_loc)

(gdb)
966 else if ((insn & 0xc0) == DW_CFA_offset)

(gdb)
0x00000035c400d4ae 966 else if ((insn & 0xc0) ==
DW_CFA_offset)

(gdb)
968 reg = insn & 0x3f;

(gdb)
0x00000035c400d523 968 reg = insn & 0x3f;

(gdb)
read_uleb128 (val=<optimized out>, p=<optimized out>) at
../../../libgcc/unwind-pe.h:137
137 result = 0;

(gdb)
execute_cfa_program (insn_ptr=0x7ffffffde102 "",
insn_end=0x8000413f2246 <Address 0x8000413f2246 out of bounds>,
 context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwind-dw2.c:968
968 reg = insn & 0x3f;

(gdb) x/20i $rip
=> 0x35c400d528 <execute_cfa_program+248>: and $0x3f,%edi
 0x35c400d52b <execute_cfa_program+251>: xor %ecx,%ecx
 0x35c400d52d <execute_cfa_program+253>: nopl (%rax)
 0x35c400d530 <execute_cfa_program+256>: add $0x1,%rbx
 0x35c400d534 <execute_cfa_program+260>: movzbl - 0x1(%rbx),%r9d
 0x35c400d539 <execute_cfa_program+265>: mov %r9,%rax
 0x35c400d53c <execute_cfa_program+268>: and $0x7f,%eax
 0x35c400d53f <execute_cfa_program+271>: shl %cl,%rax
 0x35c400d542 <execute_cfa_program+274>: add $0x7,%ecx
 0x35c400d545 <execute_cfa_program+277>: or %rax,%rsi
 0x35c400d548 <execute_cfa_program+280>: test %r9b,%r9b
 0x35c400d54b <execute_cfa_program+283>: js 0x35c400d530 <execute_cfa_program+256>
 0x35c400d54d <execute_cfa_program+285>: imul 0x158(%r13),%rsi
 0x35c400d555 <execute_cfa_program+293>: cmp $0x11,%rdi
 0x35c400d559 <execute_cfa_program+297>: ja 0x35c400d4e8 <execute_cfa_program+184>
 0x35c400d55b <execute_cfa_program+299>: shl $0x4,%rdi
 0x35c400d55f <execute_cfa_program+303>: add %r13,%rdi
 0x35c400d562 <execute_cfa_program+306>: movl $0x1,0x8(%rdi)
 0x35c400d569 <execute_cfa_program+313>: mov %rsi,(%rdi)
 0x35c400d56c <execute_cfa_program+316>: jmpq 0x35c400d4e8 <execute_cfa_program+184>

(gdb) si
read_uleb128 (val=<synthetic pointer>, p=0x7ffffffde102 "") at
../../../libgcc/unwind-pe.h:133
133 unsigned int shift = 0;

(gdb)
0x00000035c400d52d 133 unsigned int shift = 0;

(gdb)
140 byte = *p++;

(gdb)
0x00000035c400d534 140 byte = *p++;

(gdb)
141 result |= ((_uleb128_t)byte & 0x7f) << shift;

(gdb)
0x00000035c400d53c 141 result |= ((_uleb128_t)byte &
0x7f) << shift;

(gdb)
0x00000035c400d53f 141 result |= ((_uleb128_t)byte &
0x7f) << shift;

(gdb)
142 shift += 7;

(gdb)
141 result |= ((_uleb128_t)byte & 0x7f) << shift;

(gdb)
144 while (byte & 0x80);

(gdb)
0x00000035c400d54b 144 while (byte & 0x80);

(gdb)
execute_cfa_program (insn_ptr=0x7ffffffde103 "",
insn_end=0x8000413f2246 <Address 0x8000413f2246 out of bounds>,
 context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwind-dw2.c:970
970 offset = (_Unwind_Sword) utmp * fs->data_align;

(gdb)
972 if (UNWIND_COLUMN_IN_RANGE (reg))

(gdb)
0x00000035c400d559 972 if (UNWIND_COLUMN_IN_RANGE
(reg))

(gdb)
0x00000035c400d55b 972 if (UNWIND_COLUMN_IN_RANGE
(reg))

(gdb)
0x00000035c400d55f 972 if (UNWIND_COLUMN_IN_RANGE
(reg))

(gdb)
974 fs->regs.reg[reg].how = REG_SAVED_OFFSET;

(gdb)
975 fs->regs.reg[reg].loc.offset = offset;

(gdb) print fs
$153 = (_Unwind_FrameState *) 0x7fffffffd460

(gdb) print *fs
$154 = {regs = {reg = {{loc = {reg = 0, offset = 0, exp = 0x0},
how = REG_SAVED_OFFSET}, {loc = {reg = 0, offset = 0,
 exp = 0x0}, how = REG_UNSAVED} <repeats 17 times>},
prev = 0x0, cfa_offset = 0, cfa_reg = 0, cfa_exp = 0x0,
 cfa_how = CFA_UNSET}, pc = 0x400630 <main>, personality =
0x0, data_align = 0, code_align = 0, retaddr_column = 0,
 fde_encoding = 0 '\000', lsda_encoding = 255 '\377', saw_z = 0
'\000', signal_frame = 0 '\000', eh_ptr = 0x0}

(gdb) si
0x00000035c400d56c 975 fs-
>regs.reg[reg].loc.offset = offset;

(gdb) print offset
$155 = 0

(gdb) si
957 while (insn_ptr < insn_end

(gdb)
0x00000035c400d4eb 957 while (insn_ptr < insn_end

(gdb)
_Unwind_IsSignalFrame (context=0x7fffffffd370) at
../../../libgcc/unwind-dw2.c:202
202 return (context->flags & SIGNAL_FRAME_BIT) ? 1 : 0;

(gdb)
execute_cfa_program (insn_ptr=0x7ffffffde103 "",
insn_end=0x8000413f2246 <Address 0x8000413f2246 out of bounds>,
 context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwind-dw2.c:958
958 && fs->pc < context->ra + _Unwind_IsSignalFrame
(context))

(gdb)
_Unwind_IsSignalFrame (context=0x7fffffffd370) at
../../../libgcc/unwind-dw2.c:202
202 return (context->flags & SIGNAL_FRAME_BIT) ? 1 : 0;

(gdb)
execute_cfa_program (insn_ptr=0x7ffffffde103 "",
insn_end=0x8000413f2246 <Address 0x8000413f2246 out of bounds>,
 context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwind-dw2.c:958
958 && fs->pc < context->ra + _Unwind_IsSignalFrame
(context))

(gdb)
0x00000035c400d506 958 && fs->pc < context->ra +
_Unwind_IsSignalFrame (context))

(gdb)
0x00000035c400d509 958 && fs->pc < context->ra +
_Unwind_IsSignalFrame (context))

(gdb)
960 unsigned char insn = *insn_ptr++;

(gdb)
0x00000035c400d49b 960 unsigned char insn =
*insn_ptr++;

(gdb)
964 if ((insn & 0xc0) == DW_CFA_advance_loc)

(gdb)
0x00000035c400d4a1 964 if ((insn & 0xc0) ==
DW_CFA_advance_loc)

(gdb) x/20i $rip
=> 0x35c400d4a1 <execute_cfa_program+113>: and
$0xffffffc0,%esi
 0x35c400d4a4 <execute_cfa_program+116>: cmp $0x40,%sil
 0x35c400d4a8 <execute_cfa_program+120>: je 0x35c400d4d0
<execute_cfa_program+160>
 0x35c400d4aa <execute_cfa_program+122>: cmp $0x80,%sil
 0x35c400d4ae <execute_cfa_program+126>: je 0x35c400d520
<execute_cfa_program+240>
 0x35c400d4b0 <execute_cfa_program+128>: cmp $0xc0,%sil
 0x35c400d4b4 <execute_cfa_program+132>: je 0x35c400d578
<execute_cfa_program+328>
 0x35c400d4ba <execute_cfa_program+138>: cmp $0x2f,%al
 0x35c400d4bc <execute_cfa_program+140>: ja 0x35c400d5a9
<execute_cfa_program+377>
 0x35c400d4c2 <execute_cfa_program+146>: movslq
(%rdx,%rax,4),%rax
 0x35c400d4c6 <execute_cfa_program+150>: add %rdx,%rax
 0x35c400d4c9 <execute_cfa_program+153>: jmpq *%rax
 0x35c400d4cb <execute_cfa_program+155>: nopl
0x0(%rax,%rax,1)
 0x35c400d4d0 <execute_cfa_program+160>: and $0x3f,%eax
 0x35c400d4d3 <execute_cfa_program+163>: mov %r12,%rbx
 0x35c400d4d6 <execute_cfa_program+166>: imul
0x160(%r13),%rax
 0x35c400d4de <execute_cfa_program+174>: add %rcx,%rax
 0x35c400d4e1 <execute_cfa_program+177>: mov
%rax,0x148(%r13)
 0x35c400d4e8 <execute_cfa_program+184>: cmp %r15,%rbx
 0x35c400d4eb <execute_cfa_program+187>: jae 0x35c400d50b
<execute_cfa_program+219>

(gdb) set $esi = 0xc8

(gdb) set $esi = 0x2f
...
<fix the memory to avoid killing by gcc_unreachable() or gcc_assert()> #####
...

(gdb) si
0x00000035c400d4a4 964 if ((insn & 0xc0) ==
DW_CFA_advance_loc)

(gdb)
0x00000035c400d4a8 964 if ((insn & 0xc0) ==
DW_CFA_advance_loc)

(gdb)
966 else if ((insn & 0xc0) == DW_CFA_offset)

(gdb)
0x00000035c400d4ae 966 else if ((insn & 0xc0) ==
DW_CFA_offset)

(gdb)
978 else if ((insn & 0xc0) == DW_CFA_restore)

(gdb)
0x00000035c400d4b4 978 else if ((insn & 0xc0) ==
DW_CFA_restore)

(gdb)
985 else switch (insn)

(gdb)
0x00000035c400d4bc 985 else switch (insn)

(gdb)
0x00000035c400d4c2 985 else switch (insn)

(gdb)
0x00000035c400d4c6 985 else switch (insn)

(gdb)
0x00000035c400d4c9 985 else switch (insn)

(gdb)
960 unsigned char insn = *insn_ptr++;

(gdb)
0x00000035c400d5b1 960 unsigned char insn =
*insn_ptr++;

(gdb)
957 while (insn_ptr < insn_end

(gdb) x/20i $rip
=> 0x35c400d4e8 <execute_cfa_program+184>: cmp %r15,%rbx
 0x35c400d4eb <execute_cfa_program+187>: jae 0x35c400d50b
<execute_cfa_program+219>
 0x35c400d4ed <execute_cfa_program+189>: mov
0xc0(%r14),%rax
 0x35c400d4f4 <execute_cfa_program+196>: mov
0x148(%r13),%rcx
 0x35c400d4fb <execute_cfa_program+203>: shr $0x3f,%rax
 0x35c400d4ff <execute_cfa_program+207>: add
0x98(%r14),%rax
 0x35c400d506 <execute_cfa_program+214>: cmp %rax,%rcx
 0x35c400d509 <execute_cfa_program+217>: jb 0x35c400d498
<execute_cfa_program+104>
 0x35c400d50b <execute_cfa_program+219>: lea -
0x28(%rbp),%rsp
 0x35c400d50f <execute_cfa_program+223>: pop %rbx
 0x35c400d510 <execute_cfa_program+224>: pop %r12
 0x35c400d512 <execute_cfa_program+226>: pop %r13
 0x35c400d514 <execute_cfa_program+228>: pop %r14
 0x35c400d516 <execute_cfa_program+230>: pop %r15
 0x35c400d518 <execute_cfa_program+232>: pop %rbp
 0x35c400d519 <execute_cfa_program+233>: retq
 0x35c400d51a <execute_cfa_program+234>: nopw
0x0(%rax,%rax,1)
 0x35c400d520 <execute_cfa_program+240>: mov %rax,%rdi
 0x35c400d523 <execute_cfa_program+243>: mov %r12,%rbx
 0x35c400d526 <execute_cfa_program+246>: xor %esi,%esi

(gdb) i r r15 rbx
r15 0x8000413f2246 140738583011910
rbx 0x7ffffffde104 140737488216324

(gdb) set $r15 = $rbx
...
<Try to jump out from the while loop because it will takie
forever ant requires fixing memory object for each interation> #####
...

(gdb) i r r15 rbx
r15 0x7ffffffde104 140737488216324
rbx 0x7ffffffde104 140737488216324

(gdb) si
0x00000035c400d4eb 957 while (insn_ptr < insn_end

(gdb)
1224 }

(gdb)
0x00000035c400d50f 1224 }

(gdb)
0x00000035c400d510 1224 }

(gdb)
0x00000035c400d512 1224 }

(gdb)
0x00000035c400d514 1224 }

(gdb)
0x00000035c400d516 1224 }

(gdb)
0x00000035c400d518 1224 }

(gdb)
0x00000035c400d519 1224 }

(gdb)
uw_frame_state_for (context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwind-dw2.c:1273
1273 aug += 2 * size_of_encoded_value (fs->fde_encoding);

(gdb)
1272 aug = (const unsigned char *) fde + sizeof (*fde);

(gdb)
size_of_encoded_value (encoding=0 '\000') at
../../../libgcc/unwind-pe.h:74
74 if (encoding == DW_EH_PE_omit)

(gdb)
0x00000035c400eece 74 if (encoding == DW_EH_PE_omit)

(gdb)
77 switch (encoding & 0x07)

(gdb)
0x00000035c400eed7 77 switch (encoding & 0x07)

(gdb)
0x00000035c400eed9 77 switch (encoding & 0x07)

(gdb)
0x00000035c400eedf 77 switch (encoding & 0x07)

(gdb)
77 switch (encoding & 0x07)

(gdb)
0x00000035c400f51b 77 switch (encoding & 0x07)

(gdb)
0x00000035c400eef6 77 switch (encoding & 0x07)

(gdb)
uw_frame_state_for (context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwind-dw2.c:1273
1273 aug += 2 * size_of_encoded_value (fs->fde_encoding);

(gdb)
1274 insn = NULL;

(gdb)
1275 if (fs->saw_z)

(gdb)
0x00000035c400ef09 1275 if (fs->saw_z)

(gdb)
1281 if (fs->lsda_encoding != DW_EH_PE_omit)

(gdb)
0x00000035c400ef38 1281 if (fs->lsda_encoding !=
DW_EH_PE_omit)

(gdb)
0x00000035c400ef3c 1281 if (fs->lsda_encoding !=
DW_EH_PE_omit)

(gdb)
next_fde (f=<optimized out>) at ../../../libgcc/unwind-dw2-
fde.h:163
163 return (const fde *) ((const char *) f + f->length +
sizeof (f->length));

(gdb)
uw_frame_state_for (context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwind-dw2.c:1291
1291 insn = aug;

(gdb)
1293 execute_cfa_program (insn, end, context, fs);

(gdb)
1291 insn = aug;

(gdb)
1293 execute_cfa_program (insn, end, context, fs);

(gdb)
0x00000035c400f472 1293 execute_cfa_program (insn, end,
context, fs);

(gdb)
next_fde (f=0x7ffffffde101) at ../../../libgcc/unwind-dw2-
fde.h:163
163 return (const fde *) ((const char *) f + f->length +
sizeof (f->length));

(gdb)
uw_frame_state_for (context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwind-dw2.c:1293
1293 execute_cfa_program (insn, end, context, fs);

(gdb)
execute_cfa_program (insn_ptr=insn_ptr@entry=0x7ffffffde119 'A'
<repeats 200 times>..., 
 insn_end=0x7ffffffde185 'A' <repeats 200 times>...,
context=context@entry=0x7fffffffd370, fs=fs@entry=0x7fffffffd460)
 at ../../../libgcc/unwind-dw2.c:942
942 {

(gdb)
0x00000035c400d431 942 {

(gdb)
0x00000035c400d434 942 {

(gdb)
0x00000035c400d436 942 {

(gdb)
0x00000035c400d439 942 {

(gdb)
0x00000035c400d43b 942 {

(gdb)
0x00000035c400d43d 942 {

(gdb)
0x00000035c400d440 942 {

(gdb)
0x00000035c400d442 942 {

(gdb)
0x00000035c400d443 942 {

(gdb)
0x00000035c400d446 942 {

(gdb)
957 while (insn_ptr < insn_end

(gdb) n
946 fs->regs.prev = NULL;

(gdb)
957 while (insn_ptr < insn_end

(gdb)
958 && fs->pc < context->ra + _Unwind_IsSignalFrame
(context))

(gdb)
1169 insn_ptr = read_sleb128 (insn_ptr, &stmp);

(gdb)
985 else switch (insn)

(gdb) x/20i $rip
=> 0x35c400d487 <execute_cfa_program+87>: lea
0x5162(%rip),%rdx # 0x35c40125f0
 0x35c400d48e <execute_cfa_program+94>: xor %r8d,%r8d
 0x35c400d491 <execute_cfa_program+97>: mov %rax,-
0x48(%rbp)
 0x35c400d495 <execute_cfa_program+101>: nopl (%rax)
 0x35c400d498 <execute_cfa_program+104>: movzbl (%rbx),%eax
 0x35c400d49b <execute_cfa_program+107>: lea 0x1(%rbx),%r12
 0x35c400d49f <execute_cfa_program+111>: mov %eax,%esi
 0x35c400d4a1 <execute_cfa_program+113>: and
$0xffffffc0,%esi
 0x35c400d4a4 <execute_cfa_program+116>: cmp $0x40,%sil
 0x35c400d4a8 <execute_cfa_program+120>: je 0x35c400d4d0
<execute_cfa_program+160>
 0x35c400d4aa <execute_cfa_program+122>: cmp $0x80,%sil
 0x35c400d4ae <execute_cfa_program+126>: je 0x35c400d520
<execute_cfa_program+240>
 0x35c400d4b0 <execute_cfa_program+128>: cmp $0xc0,%sil
 0x35c400d4b4 <execute_cfa_program+132>: je 0x35c400d578
<execute_cfa_program+328>
 0x35c400d4ba <execute_cfa_program+138>: cmp $0x2f,%al
 0x35c400d4bc <execute_cfa_program+140>: ja 0x35c400d5a9
<execute_cfa_program+377>
 0x35c400d4c2 <execute_cfa_program+146>: movslq
(%rdx,%rax,4),%rax
 0x35c400d4c6 <execute_cfa_program+150>: add %rdx,%rax
 0x35c400d4c9 <execute_cfa_program+153>: jmpq *%rax
 0x35c400d4cb <execute_cfa_program+155>: nopl
0x0(%rax,%rax,1)

(gdb) x/x $rbx
0x7ffffffde119: 0x41

(gdb) set *$rbx = 0x80 <- Let’s try the same again ;) ##### Ж
...
<fix the memory to avoid killing by gcc_unreachable() or gcc_assert()> #####
...

(gdb) si
943 struct frame_state_reg_info *unused_rs = NULL;

(gdb)
1169 insn_ptr = read_sleb128 (insn_ptr, &stmp);

(gdb)
0x00000035c400d495 1169 insn_ptr = read_sleb128
(insn_ptr, &stmp);

(gdb)
960 unsigned char insn = *insn_ptr++;

(gdb)
0x00000035c400d49b 960 unsigned char insn =
*insn_ptr++;

(gdb)
964 if ((insn & 0xc0) == DW_CFA_advance_loc)

(gdb)
0x00000035c400d4a1 964 if ((insn & 0xc0) ==
DW_CFA_advance_loc)

(gdb)
0x00000035c400d4a4 964 if ((insn & 0xc0) ==
DW_CFA_advance_loc)

(gdb)
0x00000035c400d4a8 964 if ((insn & 0xc0) ==
DW_CFA_advance_loc)

(gdb)
966 else if ((insn & 0xc0) == DW_CFA_offset)

(gdb)
0x00000035c400d4ae 966 else if ((insn & 0xc0) ==
DW_CFA_offset)

(gdb)
968 reg = insn & 0x3f;

(gdb)
0x00000035c400d523 968 reg = insn & 0x3f;

(gdb)
read_uleb128 (val=<optimized out>, p=<optimized out>) at
../../../libgcc/unwind-pe.h:137
137 result = 0;

(gdb)
execute_cfa_program (insn_ptr=0x7ffffffde11a "",
insn_ptr@entry=0x7ffffffde119 "\200",
 insn_end=0x7ffffffde185 'A' <repeats 200 times>...,
context=context@entry=0x7fffffffd370, fs=fs@entry=0x7fffffffd460)
 at ../../../libgcc/unwind-dw2.c:968
968 reg = insn & 0x3f;

(gdb)
read_uleb128 (val=<synthetic pointer>, p=0x7ffffffde11a "") at
../../../libgcc/unwind-pe.h:133
133 unsigned int shift = 0;

(gdb)
0x00000035c400d52d 133 unsigned int shift = 0;

(gdb)
140 byte = *p++;

(gdb)
0x00000035c400d534 140 byte = *p++;

(gdb)
141 result |= ((_uleb128_t)byte & 0x7f) << shift;

(gdb)
0x00000035c400d53c 141 result |= ((_uleb128_t)byte &
0x7f) << shift;

(gdb)
0x00000035c400d53f 141 result |= ((_uleb128_t)byte &
0x7f) << shift;

(gdb)
142 shift += 7;

(gdb)
141 result |= ((_uleb128_t)byte & 0x7f) << shift;

(gdb)
144 while (byte & 0x80);

(gdb)
0x00000035c400d54b 144 while (byte & 0x80);

(gdb)
execute_cfa_program (insn_ptr=0x7ffffffde11b "",
insn_ptr@entry=0x7ffffffde119 "\200",
 insn_end=0x7ffffffde185 'A' <repeats 200 times>...,
context=context@entry=0x7fffffffd370, fs=fs@entry=0x7fffffffd460)
 at ../../../libgcc/unwind-dw2.c:970
970 offset = (_Unwind_Sword) utmp * fs->data_align;

(gdb)
972 if (UNWIND_COLUMN_IN_RANGE (reg))

(gdb)
0x00000035c400d559 972 if (UNWIND_COLUMN_IN_RANGE
(reg))

(gdb) 
0x00000035c400d55b 972 if (UNWIND_COLUMN_IN_RANGE
(reg))

(gdb)
0x00000035c400d55f 972 if (UNWIND_COLUMN_IN_RANGE
(reg))

(gdb)
974 fs->regs.reg[reg].how = REG_SAVED_OFFSET;

(gdb)
975 fs->regs.reg[reg].loc.offset = offset;

(gdb)
0x00000035c400d56c 975 fs-
>regs.reg[reg].loc.offset = offset;

(gdb)
957 while (insn_ptr < insn_end

(gdb)
0x00000035c400d4eb 957 while (insn_ptr < insn_end

(gdb) x/20i $rip-10
 0x35c400d4e1 <execute_cfa_program+177>: mov
%rax,0x148(%r13)
 0x35c400d4e8 <execute_cfa_program+184>: cmp %r15,%rbx
=> 0x35c400d4eb <execute_cfa_program+187>: jae 0x35c400d50b
<execute_cfa_program+219>
 0x35c400d4ed <execute_cfa_program+189>: mov
0xc0(%r14),%rax
 0x35c400d4f4 <execute_cfa_program+196>: mov
0x148(%r13),%rcx
 0x35c400d4fb <execute_cfa_program+203>: shr $0x3f,%rax
 0x35c400d4ff <execute_cfa_program+207>: add
0x98(%r14),%rax
 0x35c400d506 <execute_cfa_program+214>: cmp %rax,%rcx
 0x35c400d509 <execute_cfa_program+217>: jb 0x35c400d498
<execute_cfa_program+104>
 0x35c400d50b <execute_cfa_program+219>: lea -
0x28(%rbp),%rsp
 0x35c400d50f <execute_cfa_program+223>: pop %rbx
 0x35c400d510 <execute_cfa_program+224>: pop %r12
 0x35c400d512 <execute_cfa_program+226>: pop %r13
 0x35c400d514 <execute_cfa_program+228>: pop %r14
 0x35c400d516 <execute_cfa_program+230>: pop %r15
 0x35c400d518 <execute_cfa_program+232>: pop %rbp
 0x35c400d519 <execute_cfa_program+233>: retq
 0x35c400d51a <execute_cfa_program+234>: nopw
0x0(%rax,%rax,1)
 0x35c400d520 <execute_cfa_program+240>: mov %rax,%rdi
 0x35c400d523 <execute_cfa_program+243>: mov %r12,%rbx

(gdb) i r r15 rbx
r15 0x7ffffffde185 140737488216453
rbx 0x7ffffffde11b 140737488216347

(gdb) set $r15 = $rbx

(gdb) set $rip = 0x35c400d4e8
...
<fix while loop again… <- its 2nd entry to this function (for upper FDE)> #####
...

(gdb) si
0x00000035c400d4eb 957 while (insn_ptr < insn_end

(gdb)
1224 }

(gdb)
0x00000035c400d50f 1224 }

(gdb)
0x00000035c400d510 1224 }

(gdb)
0x00000035c400d512 1224 }

(gdb)
0x00000035c400d514 1224 }

(gdb)
0x00000035c400d516 1224 }

(gdb)
0x00000035c400d518 1224 }

(gdb)
0x00000035c400d519 1224 }

(gdb)
uw_frame_state_for (context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460) at ../../../libgcc/unwind-dw2.c:1296
1296 }

(gdb) list
1291 insn = aug;
1292 end = (const unsigned char *) next_fde (fde);
1293 execute_cfa_program (insn, end, context, fs);
1294
1295 return _URC_NO_REASON;
1296 }
1297 ^L
1298 typedef struct frame_state
1299 {
1300 void *cfa;

(gdb)
1301 void *eh_ptr;
1302 long cfa_offset;
1303 long args_size;
1304 long reg_or_offset[PRE_GCC3_DWARF_FRAME_REGISTERS+1];
1305 unsigned short cfa_reg;
1306 unsigned short retaddr_column;
1307 char saved[PRE_GCC3_DWARF_FRAME_REGISTERS+1];
1308 } frame_state;
1309
1310 struct frame_state * __frame_state_for (void *, struct
frame_state *);

(gdb) si
1295 return _URC_NO_REASON;

(gdb)
1296 }

(gdb)
0x00000035c400f486 1296 }

(gdb)
0x00000035c400f487 1296 }

(gdb)
0x00000035c400f489 1296 }

(gdb)
0x00000035c400f48b 1296 }

(gdb)
0x00000035c400f48d 1296 }

(gdb)
0x00000035c400f48f 1296 }

(gdb)
_Unwind_Backtrace (trace=0x35c1909bc0 <backtrace_helper>,
trace_argument=0x7fffffffd620) at ../../../libgcc/unwind.inc:291
291 if (code != _URC_NO_REASON && code !=
_URC_END_OF_STACK)

(gdb)
290 code = uw_frame_state_for (&context, &fs);

(gdb) n
291 if (code != _URC_NO_REASON && code !=
_URC_END_OF_STACK)

(gdb) print code
$157 = _URC_NO_REASON

(gdb) n
295 if ((*trace) (&context, trace_argument) !=
_URC_NO_REASON)

(gdb)
299 if (code == _URC_END_OF_STACK)

(gdb) print code
$158 = _URC_NO_REASON

(gdb) n
303 uw_update_context (&context, &fs);

(gdb) si
0x00000035c400ff06 303 uw_update_context (&context,
&fs);

(gdb) n
Program received signal SIGABRT, Aborted.
0x00000035c1835a19 in __GI_raise (sig=sig@entry=6) at
../nptl/sysdeps/unix/sysv/linux/raise.c:56
56 return INLINE_SYSCALL (tgkill, 3, pid, selftid, sig);

(gdb) bt
#0 0x00000035c1835a19 in __GI_raise (sig=sig@entry=6) at
../nptl/sysdeps/unix/sysv/linux/raise.c:56
#1 0x00000035c1837128 in __GI_abort () at abort.c:90
#2 0x00000035c400e8d5 in uw_update_context_1
(context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460)
 at ../../../libgcc/unwind-dw2.c:1430
#3 0x00000035c400eb61 in uw_update_context
(context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460)
 at ../../../libgcc/unwind-dw2.c:1506
#4 0x00000035c400ff0e in _Unwind_Backtrace (trace=0x35c1909bc0
<backtrace_helper>, trace_argument=0x7fffffffd620)
 at ../../../libgcc/unwind.inc:303
#5 0x00000035c1909d36 in __GI___backtrace
(array=array@entry=0x7fffffffd800, size=size@entry=64)
 at ../sysdeps/x86_64/backtrace.c:109
#6 0x00000035c1875d64 in __libc_message
(do_abort=do_abort@entry=2,
 fmt=fmt@entry=0x35c197d302 "*** %s ***: %s terminated\n") at
../sysdeps/unix/sysv/linux/libc_fatal.c:176
#7 0x00000035c190d6b7 in __GI___fortify_fail
(msg=msg@entry=0x35c197d2ea "stack smashing detected") at
fortify_fail.c:31
#8 0x00000035c190d680 in __stack_chk_fail () at
stack_chk_fail.c:28
#9 0x00000000004006b1 in main ()

(gdb) up
#1 0x00000035c1837128 in __GI_abort () at abort.c:90
90 raise (SIGABRT);

(gdb)
#2 0x00000035c400e8d5 in uw_update_context_1
(context=context@entry=0x7fffffffd370,
fs=fs@entry=0x7fffffffd460)
 at ../../../libgcc/unwind-dw2.c:1430
1430 gcc_unreachable (); <- one of the object wasn’t patched ;) #####

(gdb) print fs->regs.cfa_how
$159 = CFA_UNSET

(gdb) print *fs
$160 = {regs = {reg = {{loc = {reg = 0, offset = 0, exp = 0x0},
how = REG_SAVED_OFFSET}, {loc = {reg = 0, offset = 0,
 exp = 0x0}, how = REG_UNSAVED} <repeats 17 times>},
prev = 0x0, cfa_offset = 0, cfa_reg = 0, cfa_exp = 0x0,
 cfa_how = CFA_UNSET}, pc = 0x400630 <main>, personality =
0x0, data_align = 0, code_align = 0, retaddr_column = 0,
 fde_encoding = 0 '\000', lsda_encoding = 255 '\377', saw_z = 0
'\000', signal_frame = 0 '\000', eh_ptr = 0x0}

Как-то связано с безопасностью:

Сценарий 1 – заставить SSP перекопировать существующую область памяти процесса в свеже-выделенную. Это очень сложно, ибо для достижения уязвимого кода, необходимо обойти все возможные крахи, которые я описал ранее (и эмулировал). Это означает починку блока среды, указателей на аргументы и т.п. Я выключил защиту ASLR + применил кое-какие патчи к памяти для мониторинга управляющего потока и обнаружения возможного истощения ресурсов. В моем сценарии я выделил 2GB памяти, куда я направил указатель на имя программы.

[pi3@forum.reverse4you.org ~]$ gdb -q -p 13633
Attaching to process 13633
Reading symbols from /home/pi3/test...done.
Reading symbols from /lib64/libc.so.6...Reading symbols from
/usr/lib/debug/lib64/libc-2.17.so.debug...done.
done.
Loaded symbols for /lib64/libc.so.6
Reading symbols from /lib64/ld-linux-x86-64.so.2...Reading
symbols from /usr/lib/debug/lib64/ld-2.17.so.debug...done.
done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
0x00000035c18e7650 in __read_nocancel () at
../sysdeps/unix/syscall-template.S:81
81 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)

(gdb) b libc_fatal.c:66
Breakpoint 1 at 0x35c1875a37: file
../sysdeps/unix/sysv/linux/libc_fatal.c, line 66.

(gdb) c
Continuing.
Breakpoint 1, __libc_message (do_abort=do_abort@entry=2,
fmt=fmt@entry=0x35c197d302 "*** %s ***: %s terminated\n")
 at ../sysdeps/unix/sysv/linux/libc_fatal.c:66
66 const char *on_2 = __libc_secure_getenv
("LIBC_FATAL_STDERR_");

(gdb) ni
57 va_start (ap, fmt);

(gdb)
0x00000035c1875a45 57 va_start (ap, fmt);

(gdb)
0x00000035c1875a4c 57 va_start (ap, fmt);

(gdb)
0x00000035c1875a56 57 va_start (ap, fmt);

(gdb)
52 {

(gdb)
57 va_start (ap, fmt);

(gdb)
58 va_copy (ap_copy, ap);

(gdb)
0x00000035c1875a75 58 va_copy (ap_copy, ap);

(gdb) 
0x00000035c1875a7c 58 va_copy (ap_copy, ap);

(gdb)
0x00000035c1875a80 58 va_copy (ap_copy, ap);

(gdb)
0x00000035c1875a87 58 va_copy (ap_copy, ap);

(gdb)
0x00000035c1875a8e 58 va_copy (ap_copy, ap);

(gdb)
66 const char *on_2 = __libc_secure_getenv
("LIBC_FATAL_STDERR_");

(gdb)
67 if (on_2 == NULL || *on_2 == '\0')

(gdb) si
0x00000035c1875a9d 67 if (on_2 == NULL || *on_2 == '\0')

(gdb) x/20i 0x00000035c1875a9d
=> 0x35c1875a9d <__libc_message+205>: je 0x35c1875aa8
<__libc_message+216>
 0x35c1875a9f <__libc_message+207>: cmpb $0x0,(%rax)
 0x35c1875aa2 <__libc_message+210>: jne 0x35c1875c68
<__libc_message+664>
 0x35c1875aa8 <__libc_message+216>: lea
0x1061c6(%rip),%rdi # 0x35c197bc75
 0x35c1875aaf <__libc_message+223>: xor %eax,%eax
 0x35c1875ab1 <__libc_message+225>: mov $0x902,%esi
 0x35c1875ab6 <__libc_message+230>: callq 0x35c18e7459
<__open_nocancel>
 0x35c1875abb <__libc_message+235>: cmp $0xffffffff,%eax
 0x35c1875abe <__libc_message+238>: mov %eax,-0x718(%rbp)
 0x35c1875ac4 <__libc_message+244>: je 0x35c1875c68
<__libc_message+664>
 0x35c1875aca <__libc_message+250>: mov -0x720(%rbp),%rax
 0x35c1875ad1 <__libc_message+257>: xor %r14d,%r14d
 0x35c1875ad4 <__libc_message+260>: xor %r13d,%r13d
 0x35c1875ad7 <__libc_message+263>: movzbl (%rax),%r12d
 0x35c1875adb <__libc_message+267>: mov %rax,%rbx
 0x35c1875ade <__libc_message+270>: test %r12b,%r12b
 0x35c1875ae1 <__libc_message+273>: je 0x35c1875c8e
<__libc_message+702>
 0x35c1875ae7 <__libc_message+279>: nopw 0x0(%rax,%rax,1)
 0x35c1875af0 <__libc_message+288>: mov %r12d,%edx
 0x35c1875af3 <__libc_message+291>: mov %rbx,%rax

(gdb) i r rax
rax 0x7fff8b4a4fe0 140735530291168

(gdb) x/x $rax
0x7fff8b4a4fe0: 0x706d742f

(gdb) si
0x00000035c1875a9f 67 if (on_2 == NULL || *on_2 == '\0')

(gdb) si
0x00000035c1875aa2 67 if (on_2 == NULL || *on_2 == '\0')

(gdb) x/10i 0x35c1875c68
 0x35c1875c68 <__libc_message+664>: movl $0x2,-0x718(%rbp)
 0x35c1875c72 <__libc_message+674>: jmpq 0x35c1875aca
<__libc_message+250>
 0x35c1875c77 <__libc_message+679>: mov -0x708(%rbp),%rax
 0x35c1875c7e <__libc_message+686>: lea 0x8(%rax),%rdx
 0x35c1875c82 <__libc_message+690>: mov %rdx,-0x708(%rbp)
 0x35c1875c89 <__libc_message+697>: jmpq 0x35c1875b8f
<__libc_message+447>
 0x35c1875c8e <__libc_message+702>: mov -0x720(%rbp),%rsi
 0x35c1875c95 <__libc_message+709>: lea -0x6f8(%rbp),%rdx
 0x35c1875c9c <__libc_message+716>: mov $0x3,%edi
 0x35c1875ca1 <__libc_message+721>: callq 0x35c18f0430
<__vsyslog>

(gdb) i r rip
rip 0x35c1875c68 0x35c1875c68 <__libc_message+664>

(gdb) bt
#0 __libc_message (do_abort=do_abort@entry=2,
fmt=fmt@entry=0x35c197d302 "*** %s ***: %s terminated\n")
 at ../sysdeps/unix/sysv/linux/libc_fatal.c:71
#1 0x00000035c190d6b7 in __GI___fortify_fail
(msg=msg@entry=0x35c197d2ea "stack smashing detected") at
fortify_fail.c:31
#2 0x00000035c190d680 in __stack_chk_fail () at
stack_chk_fail.c:28
#3 0x000000000040075a in main (argc=2, argv=0x7fff8b4a32e8) at
test.c:15
...
...
<bla bla bla>
...
...

(gdb) c
Continuing.
Breakpoint 4, __libc_message (do_abort=do_abort@entry=2,
fmt=fmt@entry=0x35c197d302 "*** %s ***: %s terminated\n")
 at ../sysdeps/unix/sysv/linux/libc_fatal.c:95
95 len = strlen (str);
(gdb) print str
$5 = 0x35c197d2ea "stack smashing detected"

(gdb) c
Continuing.
Breakpoint 3, __libc_message (do_abort=do_abort@entry=2,
fmt=fmt@entry=0x35c197d302 "*** %s ***: %s terminated\n")
 at ../sysdeps/unix/sysv/linux/libc_fatal.c:106
106 newp->str = str;
...
...
<bla bla bla>
...
...

(gdb) ni
0x00000035c1875b9a 95 len = strlen (str);

(gdb)
96 cp += 2;

(gdb) print len
$11 = -2147483647

Программа правильно скомпилирована с отладочными данными (flags: “-ggdb” and “–g”), но gdb смог найти правильное определение переменной “len” и сделал ее дамп как целочисленной (integer) (что является типом по-умолчанию в случае когда gdb не знает тип переменной).

Благодарности:

Mateusz ‘j00ru’ Jurczyk, Gynvael Coldwind – за обзор и отличную конфронтацию мыслям (дискуссию).
Rafał ‘nergal’ Wojtczuk – за отличную дискуссию и помощь с DWARF.
Brian Pak – за отличное исследование и, как минимум, за прочтение этой работы :wink:
sergio and #trololol – за мотивацию и, как минимум, за прочтение этой работы :wink:

Ссылки:

1.http://www.phrack.org/issues.html?issue=67&id=13&mode=txt
2.http://xorl.wordpress.com/2010/10/14/linux-glibc-stack-canary-values/
3.http://www.bpak.org/blog/2014/02/codegate-2014-membership-800pt-pwnable-write-up/
4.https://www.usenix.org/legacy/event/woot11/tech/final_files/Oakley.pdf
5.http://dwarfstd.org/

© Translated by Rain special for r0 Crew