Чтобы отключить определение разрушения стека, используйте -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);
}