2012-09-03 3 views
3

내 질문은 Stack allocation, padding, and alignment과 관련있다. 다음 함수를 고려하십시오 어셈블리 수준에서함수 호출 중 스택 구조

void func(int a,int b) 
{ 
    char buffer[5]; 
} 

는 기능은 다음과 같습니다 : 나는 스택에 24 바이트가 할당되는 방법을 알고 싶어요

pushl %ebp 
movl %esp, %ebp 
subl $24, %esp 

. 16 바이트 char 버퍼 [5] 할당 된 이해합니다. 나는 여분의 8 바이트가 왜 쓰이고 어떻게 할당되는지 이해하지 못한다. 위 링크의 맨 위 대답은 퇴근 후 퇴장을 의미합니다. 누군가 그것을 확장 할 수 있습니까?

[bottom] b , a , return address , frame pointer , buffer1 [top] 

을하지만 난 간단한 버퍼 오버 플로우를 작성하고 반환 주소를 변경하는 것을 시도하고 있기 때문에이 잘못 될 수 :

나는 스택의 구조는 다음과 같습니다 것을 생각하고있다. 그러나 어떤 이유로 든 반송 주소는 변하지 않습니다. 스택에 다른 것이 있습니까?

답변

5

추가 공간에는 몇 가지 이유가 있습니다. 하나는 변수 정렬을위한 것입니다. 두 번째는 스택 검사를위한 패딩을 도입하는 것입니다 (일반적으로 릴리스 빌드 사용 공간이 아닌 디버그 빌드). 세 번째는 레지스터 또는 컴파일러가 생성 한 임시 변수의 임시 저장을위한 추가 공간을 갖는 것입니다.

C 호출 시퀀스에서 정상적으로 수행되는 방식은 인수를 스택에 푸시하는 일련의 푸시 명령어가 있고 함수를 호출하기 위해 호출 명령어가 사용된다는 것입니다. 호출 명령은 반환 주소를 스택으로 푸시합니다.

함수가 반환되면 호출하는 함수는 푸시 된 인수를 제거합니다.

push OFFSET [email protected]@[email protected]@A  ; pHead 
call [email protected]@[email protected]@@Z ; exterminateStartingFrom 
add esp, 4 

이 함수를 호출 스택에 변수의 주소를 추진하고있다 (함수 이름이 엉망입니다 : 같은 예를 들어 함수에 대한 호출이 (이것은 C++ 프로그램으로 비주얼 스튜디오 2005입니다) 보이는 것 C++ 당), 호출 된 함수가 반환 된 후 스택 포인터에 주소에 사용 된 바이트 수를 추가하여 스택을 다시 조정합니다.

다음은 호출 된 함수의 입력 부분입니다. 이것이하는 일은 스택에 지역 변수를위한 공간을 할당하는 것입니다. 입력 환경을 설정 한 후 스택에서 함수 인수를 가져옵니다.

push ebp 
mov ebp, esp 
sub esp, 232    ; 000000e8H 
push ebx 
push esi 
push edi 
lea edi, DWORD PTR [ebp-232] 

함수가 반환되면 함수는 기본적으로 스택이 함수가 호출 된 시점의 위치로 되돌아갑니다. 각 함수는 반환되기 전에 스택에 대한 변경 내용을 정리합니다.

pop edi 
pop esi 
pop ebx 
add esp, 232    ; 000000e8H 
pop ebp 
ret 0 

반송 주소를 변경하려고합니다. 이 예제에서 볼 수있는 것은 반환 주소가 스택에 푸시 된 마지막 인수 다음에 오는 것입니다.

여기는 brief writeup on function call conventions입니다. 또한이 document on Intel assembler instructions을 살펴보십시오.

Visual Studio 2005에서 몇 가지 예제를 사용하면 다음 코드를 수행하면이 예제 함수의 반환 값에 액세스 할 수 있습니다.

void MyFunct (unsigned short arg) { 
    unsigned char *retAddress = (unsigned char *)&arg; 
    retAddress -=4; 
    printf ("Return address is 0x%2.2x%2.2x%2.2x%2.2x\n", retAddress[3], retAddress[2], retAddress[1], retAddress[0]); 
} 

공지 사항이 반환 주소가 상위 바이트로 하위 바이트에서 저장되는 바이트 순서로 반환 주소를 넣어 나타납니다 해결이 윈도우 32 비트에 대한 호출 어셈블러 명령.

+0

푸시 및 해당 팝 작업을 이해합니다. func() 내부에서 func()의 반환 주소를 변경하려고합니다. char 버퍼 [5]를 만든 후 int 포인터를 만듭니다. 포인터를 다음을 수행하여 리턴 주소를 가리 키도록합니다. int * ret = buffer + 28; 프레임 포인터가 버퍼 다음에 오면 (버퍼 크기가 24이고 프레임 포인터가 4) 가정하십시오. 그래서 지금 ret가 반환 주소를 가리켜 야합니까? –

+1

함수에서 반환 주소를 출력하는 예제를 제공하기 위해 편집을 완료했습니다. Visual Studio 2005에서 디버거를 사용하여 메모리 및 어셈블리 해제 창을 살펴 보았습니다. –

+0

고마워요! 스택 맨 위에서 반송 주소를 찾으려고했습니다. 스택의 반환 주소 아래에있는 인수의 주소에서 빼서 동일한 작업을 수행했습니다. 함수 내부에서 지역 변수의 주소로 접근하는 것에 대해 의문점이 있지만, 내가 한 일은 내 문제를 해결해 준다 :-) –

3

여분의 공간은 스택 정렬을위한 것이며, 이는 일반적으로 더 나은 성능을 위해 수행됩니다.

+0

그래서 여분의 공간이 스택에 있습니까? 그것은 buffer1 이하입니까? 나에게 주어진 스택 구조가 맞습니까? –

+0

적절한 호출 규칙이 사용되는 경우 매개 변수를 레지스터로 전달할 수 있지만 일반적으로 레이아웃은 정확합니다. 여분의 공간은 지역 변수 앞뒤에있을 수 있습니다. Before는 지역 변수를 정렬하는 데 사용됩니다. after는 함수가 다른 함수를 호출 할 경우 매개 변수와 반환 주소를 정렬하는 데 사용됩니다. –