RET Instruction in Intel Machine

2011/02/03 20:05

Intel 머신에서는 함수의 에필로그(Epilogue) 마지막에 RET 명령이 있습니다. 이 명령은 현재의 ESP(스택 포인터)를 참조하여서 함수가 종료된 후 이전의 실행흐름으로 되돌아가는 명령입니다. 그래서 하나의 함수가 실행되면서 중간중간 다른 함수가 실행되어도 흐름이 유지될 수 있는 것이죠.

어떤 문서에 보니까 RET 명령을 다음과 같이 설명하고 있었습니다.

mov *esp, eip
jmp eip


위 코드대로라면, BOF를 이용한 연속적인 함수 호출이나 RTL(Return-to-Libc) 등이 설명되지 않습니다. 덕분에 한 동안 전 혼란에 빠졌고, Exploit 페이로드들을 이해할 수 없었죠. 그래서 디버거(GDB)를 이용하여 확인해보았습니다.

void foo ()
{
}

int main (void)
{
foo ();
}


위 코드를 컴파일하면 다음과 같은 프로그램이 생성됩니다.

080483b4 <foo>:
80483b4: 55 push %ebp
80483b5: 89 e5 mov %esp,%ebp
80483b7: 5d pop %ebp
80483b8: c3 ret

080483b9 <main>:
80483b9: 55 push %ebp
80483ba: 89 e5 mov %esp,%ebp
80483bc: e8 f3 ff ff ff call 80483b4 <foo>
80483c1: 5d pop %ebp
80483c2: c3 ret


main 함수와 foo 함수 모두의 에필로그 마지막에 RET 명령이 있습니다. RET 명령이 실행되는 시점에 어떤 변화가 있는지를 살펴보기 위해서 foo 함수 내에서 RET 명령이 실행되기 직전(0x80483b8)과 main 함수 내에서 foo 함수가 호출된 직후(0x80483c1)에 브레이크포인트(정지점)를 걸고 실행하였습니다.

사용자 삽입 이미지
[그림1] foo 함수 내에서 RET 명령이 실행되기 직전

사용자 삽입 이미지
[그림2] main 함수 내에서 foo 함수가 호출된 직후

[그림1]과 [그림2]를 비교하면 ESP가 변한 것을 확인할 수 있습니다. RET 명령이 실행되기 전의 ESP는 되돌아갈 주소인 0x080483c1을 가리키고 있습니다. 그러나 RET 명령이 실행된 이후의 ESP는 4가 증가하여 다른 값을 가리키고 있습니다. 그리고 EIP는 ESP가 가리키던 0x080483c1를 저장하고 있죠.

CPU의 프로그램 카운터(PC)는 EIP의 값이 될 것이므로 프로그램의 실행흐름은 RET 명령이 실행되기 이전의 ESP가 가리키던 주소로 변하게 됩니다.

정리해보면, RET 명령이 실행되면 ESP가 가리키던 값이 EIP에 저장되고, ESP는 4 증가합니다. 이를 하나의 명령문으로 줄이면 아래와 같습니다.

pop eip


참 직관적이죠? 사실, RET 명령은 이보다 조금 더 복잡합니다. Intel x86 Machine 에서는 4 종류의 RET 명령을 제공하고 있기 때문이죠. 위에서 확인한 RET는 기계어 코드로 C3, CB에 해당합니다. 그리고 조금 더 복잡한 C2, CA가 더 있습니다. 더 자세한 내용은 [표1]을 참고하세요.


Opcode Mnemonic Description
C3 RET Near return to calling procedure.
CB RET Far return to calling procedure.
C2 iw RET imm16 Near return to calling procedure and pop imm16 bytes from stack.
CA iw RET imm16 Far return to calling procedure and pop imm16 bytes from stack.
[표1] Return from Procedure1
크리에이티브 커먼즈 라이센스
Creative Commons License
주석.

6l4ck3y3 0x03 Linux RCE , ,

Trackback Address:이 글에는 트랙백을 보낼 수 없습니다