2017-01-23 6 views
6

나는 학생들을 위해 버퍼 오버플로 운동을 c에 작성하려고한다.ebx가 간단한 함수의 스택 프레임에 저장되는 이유는 무엇입니까?

일반적으로 스택 프레임은 함수 매개 변수, 반환 주소, 기본 포인터 및 로컬 변수로 구성됩니다. 하지만 때로는 추가 포인터가 기본 포인터와 함께 저장된다는 것을 발견했습니다. 수업에서 캐일 (calee)에 저장된 레지스터는 사용하기 전에 저장해야한다는 것을 기억합니다. 그러나 C 코드를 컴파일하여 어셈블리를 생성하는 경우가 있습니다.이 어셈블리는 의도없이 레지스터를 저장하고 사용합니다. 이 행동을 나에게 설명해주십시오.

의 주요 기능

int main (int argc, char** argv) { 
    func(); 

    return 0; 
} 

과 기능을 가정

void func() { 
    char buf[5]; 
    strcpy(buf,"AAAA"); 
    strcpy(buf,"BBBB"); 
} 

내가 gdb를 결과 실행 파일을 디버깅 할 경우

break func 
run 
info frame 

모든 것이 괜찮 스택 프레임은 EBP를 포함 및 eip.

나는 그래서 EBX가 추가로 스택 프레임에 저장됩니다

Saved registers: 
    ebx at 0xffffd1cc, ebp at 0xffffd1d0, eip at 0xffffd1d4 

를 얻을 내가

void func() { 
    char buf[5]; 
    gets(buf); 
} 

를 사용하는 경우? 왜? 내가

disas func 

을 실행하면 나는 그래서 EBX가 저장됩니다

Dump of assembler code for function func: 
    0x56555730 <+0>: push %ebp 
    0x56555731 <+1>: mov %esp,%ebp 
    0x56555733 <+3>: push %ebx 
    0x56555734 <+4>: sub $0x8,%esp 
    0x56555737 <+7>: call 0x5655576e <__x86.get_pc_thunk.ax> 
    0x5655573c <+12>: add $0x18c4,%eax 
=> 0x56555741 <+17>: lea -0x9(%ebp),%edx 
    0x56555744 <+20>: push %edx 
    0x56555745 <+21>: mov %eax,%ebx 
    0x56555747 <+23>: call 0x56555590 <[email protected]> 
    0x5655574c <+28>: add $0x4,%esp 
    0x5655574f <+31>: nop 
    0x56555750 <+32>: mov -0x4(%ebp),%ebx 
    0x56555753 <+35>: leave 
    0x56555754 <+36>: ret  
End of assembler dump. 

얻을. 승인. 그러나 그것은 무엇을 위해 사용 되는가? gets()를 호출하기 전에 eax가 ebx로 옮겨집니다. 그러나 나중에 사용되지 않습니다. 이전 ebx는 스택에서 복원되어 돌아 오기 전에 복원됩니다. 그것은 쓸모없는 것처럼 보인다. Btw. 뭐예요 전체 call get_pc_thunk 물건? 내가 printf와 사용하는 경우

대등 행동, 대신 가져옵니다

void func() { 
    char buf[5]; 
    strcpy(buf, "AAAA"); 
    printf("%s",buf); 
} 

GDB 출력 :

(gdb) info frame 
Stack level 0, frame at 0xffffd1d8: 
eip = 0x56555741 in func (/home/mischa/stuff/test/test.c:35); saved eip = 0x56555779 
called by frame at 0xffffd1e0 
source language c. 
Arglist at 0xffffd1d0, args: 
Locals at 0xffffd1d0, Previous frame's sp is 0xffffd1d8 
Saved registers: 
    ebx at 0xffffd1cc, ebp at 0xffffd1d0, eip at 0xffffd1d4 
(gdb) disas func 
Dump of assembler code for function func: 
    0x56555730 <+0>: push %ebp 
    0x56555731 <+1>: mov %esp,%ebp 
    0x56555733 <+3>: push %ebx 
    0x56555734 <+4>: sub $0x8,%esp 
    0x56555737 <+7>: call 0x56555780 <__x86.get_pc_thunk.ax> 
    0x5655573c <+12>: add $0x18c4,%eax 
=> 0x56555741 <+17>: movl $0x41414141,-0x9(%ebp) 
    0x56555748 <+24>: movb $0x0,-0x5(%ebp) 
    0x5655574c <+28>: lea -0x9(%ebp),%edx 
    0x5655574f <+31>: push %edx 
    0x56555750 <+32>: lea -0x17f0(%eax),%edx 
    0x56555756 <+38>: push %edx 
    0x56555757 <+39>: mov %eax,%ebx 
    0x56555759 <+41>: call 0x565555a0 <[email protected]> 
    0x5655575e <+46>: add $0x8,%esp 
    0x56555761 <+49>: nop 
    0x56555762 <+50>: mov -0x4(%ebp),%ebx 
    0x56555765 <+53>: leave 
    0x56555766 <+54>: ret  
End of assembler dump. 

누군가가 나에게 이것을 설명하시기 바랍니다 수 있습니까?

나는 다음 CMakeLists.txt로 컴파일 cmake를 사용

cmake_minimum_required (VERSION 2.8) 

# projectname is the same as the main-executable 
project(test) 

# compile with 32 bit 
add_definitions('-m32') 

# Disable compiler optimization 
add_definitions('-O0') 

# include debugging information 
add_definitions('-g') 

# Align items on the stack to 4 bytes. This makes stuff easier. 
# See https://stackoverflow.com/questions/1061818/stack-allocation-padding-and-alignment 
add_definitions('-mpreferred-stack-boundary=2') 

# disable compiler buffer overflow protection 
add_definitions('-z execstack -z norelro -fno-stack-protector') 

# executable source code 
add_executable(test test.c) 

cmake는 GCC를 사용하는 것 같다.

+1

최적화가 비활성화되었으므로? –

+3

'ebx'는 GOT에 대한 포인터로 사용되지만 실제로는 위치 독립적 인 코드에만 해당됩니다. 너의 cmake 물건들로부터 그것을 컴파일했는지는 분명치 않다. 문제를 재현하기 위해'-fPIC' 명령 행 옵션을'gcc'에 넘겨 주어야했습니다. – Jester

+0

'strcpy (buf, "AAAA");는'movl'에 이어'movb'에 인라인되고 있으므로 함수를 전혀 호출하지 않아서 실행되지 않습니다. (마지막 해체에서 지적한 코드를 참조하십시오.) – rici

답변

10

컴파일러 툴체인은 기본적으로 위치 독립적 실행 파일 (PIE)을 생성하도록 (아마도 배포자가) 구성되었습니다. 32 비트 x86에서 위치 독립적 코드가 호출자와 다른 라이브러리에있을 수있는 함수를 호출하려면 호출시 호출 모듈의 GOT 주소를 ebx에로드해야합니다. 이것은 ABI 요구 사항입니다. ebx은 x86 ABI의 호출 저장 레지스터이므로 호출자는 자신의 호출자에게 반환하기 전에 저장하고 나중에 복원해야합니다.

내가 주제 A를 쓴이 문서에서는이 정보가 될 수 있지만 :

GCC의 매우 최신 버전에

https://ewontfix.com/18/

이 새로운 -fno-plt 옵션은 GOT에서 부하를 인라인으로이 문제를 방지 할 수있다 오히려 ebx에 의존하는 PLT를 사용하는 것보다

+0

고맙습니다! PIE 생성을 비활성화하려면 어떻게합니까? 내가 만든 코드를 비교하고 싶습니다. –

+1

@MichaelPalm : 컴파일시'-fno-PIE'를 사용하고, 링크 할 때는'-no-pie' 또는'-nopie'를 사용하십시오. 원한다면 컴파일과 링크를 위해서 사용하십시오. '-no-pie' 또는'-nopie'가 필요한지 여부는 여러분이 GCC 6+를 파이썬에서 기본값으로 지원하는지 (이전) 아니면 Gentoo에서 유래 한 오래된 gcc에 대한 기본 파이 패치를 사용하는지에 따라 다릅니다 (후자의 형태). –

+0

나를 위해 일했습니다 :-) –