Код разбивки стека не работает на ядре Linux 2.6.38.7 ... Пожалуйста, помогите - PullRequest
2 голосов
/ 20 августа 2011

Я читал «Руководство по кодировщикам оболочки» и обращался к этой ссылке для практики переполнения стека. Но, похоже, разработчики ядра Linux сделали ядро ​​очень безопасным. Вот мои проблемы.

1) Этот код

void function(int a, int b, int c) {
   char buffer1[8];
   char buffer2[10];
   int* ret;

   ret = buffer1 + 6;
   *ret+=8;
}

void main() {
  int x;

  x = 0;
  function(1,2,3);
  x = 1;
  printf("%d\n",x);
}

дает вывод

$ cc smash.c
smash.c: In function ‘function’:
smash.c:7:8: warning: assignment from incompatible pointer type
$ ./a.out
1

, но замена строки *ret+=8 на *ret=8 дает следующий вывод

*** stack smashing detected ***: ./a.out terminated
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(__fortify_fail+0x50)[0xa86df0]
/lib/i386-linux-gnu/libc.so.6(+0xe5d9a)[0xa86d9a]
./a.out[0x8048448]
./a.out[0x8048477]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xe7)[0x9b7e37]
./a.out[0x8048381]
======= Memory map: ========
003df000-003e0000 r-xp 00000000 00:00 0          [vdso]
009a1000-00afb000 r-xp 00000000 08:01 3277633    /lib/i386-linux-gnu/libc-2.13.so
00afb000-00afc000 ---p 0015a000 08:01 3277633    /lib/i386-linux-gnu/libc-2.13.so
00afc000-00afe000 r--p 0015a000 08:01 3277633    /lib/i386-linux-gnu/libc-2.13.so
...
...

Если заменить ret = buffer1 + 6 на ret = buffer1 + 7, результат будет таким же, как указано выше. Если я заменю ret = buffer1 + 6 на ret=buffer1+8 (или любое большее значение), то будет разбит стек для ОБА описанных выше случаев (т. Е. Увеличу ли я значение *ret на 8 или изменит его на 8).

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

Платформа: i386 Ядро: 2.6.38

Ответы [ 2 ]

11 голосов
/ 21 августа 2011

Чтобы отключить определение разрушения стека, используйте -fno-stack-protector при компиляции. Вы также можете использовать -ggdb и -mpreferred-stack-border = 4 при работе через «Руководство по кодировщикам оболочек», чтобы включить символы GDB и стандартизировать стек.

редактировать: Когда я скомпилировал предоставленный вами код (gcc -fno-stack-protector -ggdb -mpreferred-stack-boundary=4 -o sc in.c), компилятор переставил порядок локальных переменных в function. Я нашел это с помощью GDB:

willi@ubuntu:~/testing$ gdb sc
(gdb) set disassembly-flavor intel
(gdb) disassemble function
Dump of assembler code for function function:
   0x080483c4 <+0>: push   ebp
   0x080483c5 <+1>: mov    ebp,esp
   0x080483c7 <+3>: sub    esp,0x20
   0x080483ca <+6>: lea    eax,[ebp-0xc]
   0x080483cd <+9>: add    eax,0x6
   0x080483d0 <+12>:    mov    DWORD PTR [ebp-0x4],eax
   0x080483d3 <+15>:    mov    eax,DWORD PTR [ebp-0x4]
   0x080483d6 <+18>:    mov    eax,DWORD PTR [eax]
   0x080483d8 <+20>:    lea    edx,[eax+0x8]
   0x080483db <+23>:    mov    eax,DWORD PTR [ebp-0x4]
   0x080483de <+26>:    mov    DWORD PTR [eax],edx
   0x080483e0 <+28>:    leave  
   0x080483e1 <+29>:    ret    
End of assembler dump.

0x080483ca говорит мне, что ebp - 0xC - это buffer1, а 0x080483d0 - мне, что ebp - 0x4 - это ret. Таким образом, переменные не существуют в стеке, как они существуют в нашем C-коде. Учитывая, что ret является нашей самой верхней локальной переменной, мы можем работать с ней напрямую. Давайте работать с вашим кодом, хотя.

Чтобы изменить указатель возврата, нам нужно изменить адрес, сохраненный чуть ниже сохраненного ebp, поэтому ebp + 0x4. Таким образом, чтобы получить указатель возврата из нашей переменной buffer1, мы должны добавить 0xC (чтобы получить ebp), а затем 0x4 (указатель возврата равен 0x4 при ebp). Теперь мы можем делать наши модификации.

Я беру из вашего кода C, что вы хотите пропустить присвоение x = 1 и вернусь непосредственно к printf. Я разобрал main, чтобы найти соответствующую модификацию указателя возврата:

(gdb) disassemble main
Dump of assembler code for function main:
   0x080483e2 <+0>: push   ebp
   0x080483e3 <+1>: mov    ebp,esp
   0x080483e5 <+3>: and    esp,0xfffffff0
   0x080483e8 <+6>: sub    esp,0x20
   0x080483eb <+9>: mov    DWORD PTR [esp+0x1c],0x0
   0x080483f3 <+17>:    mov    DWORD PTR [esp+0x8],0x3
   0x080483fb <+25>:    mov    DWORD PTR [esp+0x4],0x2
   0x08048403 <+33>:    mov    DWORD PTR [esp],0x1
   0x0804840a <+40>:    call   0x80483c4 <function>
   0x0804840f <+45>:    mov    DWORD PTR [esp+0x1c],0x1
   0x08048417 <+53>:    mov    eax,DWORD PTR [esp+0x1c]
   0x0804841b <+57>:    mov    DWORD PTR [esp+0x4],eax
   0x0804841f <+61>:    mov    DWORD PTR [esp],0x80484f0
   0x08048426 <+68>:    call   0x80482f4 <printf@plt>
   0x0804842b <+73>:    leave  
   0x0804842c <+74>:    ret    
End of assembler dump.

Без изменения указателя возврата вызов function в 0x0804840a возвращается к 0x0804840f. Но мы хотим пропустить это и вернуться к следующей инструкции. Следующая инструкция начинается с 0x08048417, что на 0x8 байтов дальше. Таким образом, наша модификация указателя возврата должна увеличить его значение на 0x8.

Принимая это во внимание, я использовал следующий код для вывода "0", а не "1":

void function(int a, int b, int c) {
   char buffer1[8];
   char buffer2[10];
   int* ret;

   ret = buffer1 + 0x10;
   *ret+=8;
}

void main() {
  int x;

  x = 0;
  function(1,2,3);
  x = 1;
  printf("%d\n",x);
}
0 голосов
/ 22 августа 2011

Защита от разрушения стека может быть отключена следующим образом:

$ gcc -ggdb -m32 -o buffer1 -fno-stack-protector -mpreferred-stack-boundary=4 buffer1.c

Эта ссылка предоставляет дополнительную информацию о функциях безопасности Linux

...