저는 보안 관련 일을 공부하고 있습니다. 지금은 저의 스택으로 놀고 있습니다. 내가하고있는 일은 매우 사소한 것이어야한다. 스택을 실행하려고하지도 않는다. 64 비트 시스템에서 명령어 포인터를 제어 할 수 있다는 것을 보여 주기만하면된다. 나는 (NX-bit, ASLR, 또한 -fno-stack-protector -z execstack으로 컴파일하면서) 그걸 가지고 놀 수 있다는 것을 알고있는 모든 보호 메커니즘을 해제했다. 64 비트 어셈블리에 대한 많은 경험이 없으며 검색 및 실험 시간을 보내고 난 후에 누군가가 내가 겪고있는 문제에 대해 밝힐 수 있는지 궁금합니다.64 비트 스택 상주 버퍼 오버플로?
나는 경계 검사없이 문자열을 스택 상주 버퍼에 단순히 복사하는 프로그램 (아래 소스 코드)을 가지고 있습니다. 그러나 0x41 시리즈로 덮어 쓸 때 RIP가 0x4141414141414141로 설정되어있을 것으로 예상하고 대신 RBP가이 값으로 설정된다는 것을 알게됩니다. 세분화 오류가 발생하지만 RSP가 올바른 값으로 설정된 경우에도 RET 명령 실행시 RIP가이 (잘못된) 값으로 업데이트되지 않습니다. 심지어 GDB에서 RET 명령 바로 전에 RSP에서 일련의 0x41을 포함하는 읽을 수있는 메모리가 있는지 확인했습니다. 나는 LEAVE 명령이했다는 인상을
이었다
MOV (E) SP (E) BP
POP (E) BP 64 비트에 그러나
은 " LEAVEQ은 "명령 (유사) 할 것 같다
MOV의 RBP를 QWORD PTR은 [RSP]
나는 단순히 전에 모든 레지스터의 내용을 관찰에서이 작업을 수행 생각하고 있어요 d를 실행합니다. LEAVEQ는 여전히 RET 명령어의 컨텍스트 종속적 인 이름 인 것처럼 보이지만 (GDB의 디스어셈블러는 그것을 제공한다), 여전히 0xC9이므로.
그리고 RET 명령은 RBP 레지스터에서 뭔가를 수행하는 것처럼 보입니다. 아마도 역 참조를 할 것입니까? RET는 (유사에)했다는 것을 나는 인상이었다
MOV의 RIP을 QWORD PTR은 [RSP] 내가 언급하지만 같은
, 그것은 역 참조 RBP 것 같다, 나는 내가이 얻을 않기 때문에 생각하고 있어요 다른 레지스터에 부정한 값이 포함되어 있지 않은 경우의 세그멘테이션 폴트 프로그램에 대한
소스 코드 :
#include <stdio.h>
#include <string.h>
int vuln_function(int argc,char *argv[])
{
char buffer[512];
for(int i = 0; i < 512; i++) {
buffer[i] = 0x42;
}
printf("The buffer is at %p\n",buffer);
if(argc > 1) {
strcpy(buffer,argv[1]);
}
return 0;
}
int main(int argc,char *argv[])
{
vuln_function(argc,argv);
return 0;
}
다음은 오버 플로우하기 전에, 그것이 디버거에서 볼이 쉽게하는 0x42와 버퍼의 법적 부분을 채우기 위해 단지가 루프 . 디버깅 세션의
발췌은 다음과 같습니다 버퍼가하는 0x42 년대 충전 한 후
(gdb) disas vulnerable
Dump of assembler code for function vulnerable:
0x000000000040056c <+0>: push rbp
0x000000000040056d <+1>: mov rbp,rsp
0x0000000000400570 <+4>: sub rsp,0x220
0x0000000000400577 <+11>: mov DWORD PTR [rbp-0x214],edi
0x000000000040057d <+17>: mov QWORD PTR [rbp-0x220],rsi
0x0000000000400584 <+24>: mov DWORD PTR [rbp-0x4],0x0
0x000000000040058b <+31>: jmp 0x40059e <vulnerable+50>
0x000000000040058d <+33>: mov eax,DWORD PTR [rbp-0x4]
0x0000000000400590 <+36>: cdqe
0x0000000000400592 <+38>: mov BYTE PTR [rbp+rax*1-0x210],0x42
0x000000000040059a <+46>: add DWORD PTR [rbp-0x4],0x1
0x000000000040059e <+50>: cmp DWORD PTR [rbp-0x4],0x1ff
0x00000000004005a5 <+57>: jle 0x40058d <vulnerable+33>
0x00000000004005a7 <+59>: lea rax,[rbp-0x210]
0x00000000004005ae <+66>: mov rsi,rax
0x00000000004005b1 <+69>: mov edi,0x40070c
0x00000000004005b6 <+74>: mov eax,0x0
0x00000000004005bb <+79>: call 0x4003d8 <[email protected]>
0x00000000004005c0 <+84>: cmp DWORD PTR [rbp-0x214],0x1
0x00000000004005c7 <+91>: jle 0x4005e9 <vulnerable+125>
0x00000000004005c9 <+93>: mov rax,QWORD PTR [rbp-0x220]
0x00000000004005d0 <+100>: add rax,0x8
0x00000000004005d4 <+104>: mov rdx,QWORD PTR [rax]
0x00000000004005d7 <+107>: lea rax,[rbp-0x210]
0x00000000004005de <+114>: mov rsi,rdx
0x00000000004005e1 <+117>: mov rdi,rax
0x00000000004005e4 <+120>: call 0x4003f8 <[email protected]>
0x00000000004005e9 <+125>: mov eax,0x0
0x00000000004005ee <+130>: leave
0x00000000004005ef <+131>: ret
내가 바로의 strcpy() 호출하기 전에 휴식하지만.
(gdb) break *0x00000000004005e1
이 프로그램은 인수로 650 0x41을의의와 실행,이 스택에 리턴 주소를 덮어을 충분히해야한다.
(gdb) run `perl -e 'print "A"x650'`
반송 주소 0x00400610 (메인의 디스 어셈블리 참조)에서 메모리를 검색합니다.
(gdb) find $rsp, +1024, 0x00400610
0x7fffffffda98
1 pattern found.
나는 X/200X와 메모리를 검사하고 나는 그 크기 때문에 생략 한 멋진 개요를,하지만 난 분명히 버퍼의 법적 크기를 나타내는는 0x42, 및 반환 주소를 볼 수 있습니다 .
0x7fffffffda90: 0xffffdab0 0x00007fff 0x00400610 0x00000000
새로운 브레이크 포인트 단지()의 strcpy 후 :
(gdb) x/4x 0x7fffffffda90
0x7fffffffda90: 0x41414141 0x41414141 0x41414141 0x41414141
(gdb) x/4x $rsp
0x7fffffffda98: 0x41414141 0x41414141 0x41414141 0x41414141
:
(gdb) break *0x00000000004005e9
(gdb) set disassemble-next-line on
(gdb) si
19 }
=> 0x00000000004005ee <vulnerable+130>: c9 leave
0x00000000004005ef <vulnerable+131>: c3 ret
(gdb) i r
rax 0x0 0
rbx 0x0 0
rcx 0x4141414141414141 4702111234474983745
rdx 0x414141 4276545
rsi 0x7fffffffe17a 140737488347514
rdi 0x7fffffffdb00 140737488345856
rbp 0x7fffffffda90 0x7fffffffda90
rsp 0x7fffffffd870 0x7fffffffd870
r8 0x1 1
r9 0x270 624
r10 0x6 6
r11 0x7ffff7b9fff0 140737349550064
r12 0x400410 4195344
r13 0x7fffffffdb90 140737488346000
r14 0x0 0
r15 0x0 0
rip 0x4005ee 0x4005ee <vulnerable+130>
0x00000000004005ee <vulnerable+130>: c9 leave
=> 0x00000000004005ef <vulnerable+131>: c3 ret
(gdb) i r
rax 0x0 0
rbx 0x0 0
rcx 0x4141414141414141 4702111234474983745
rdx 0x414141 4276545
rsi 0x7fffffffe17a 140737488347514
rdi 0x7fffffffdb00 140737488345856
rbp 0x4141414141414141 0x4141414141414141
rsp 0x7fffffffda98 0x7fffffffda98
r8 0x1 1
r9 0x270 624
r10 0x6 6
r11 0x7ffff7b9fff0 140737349550064
r12 0x400410 4195344
r13 0x7fffffffdb90 140737488346000
r14 0x0 0
r15 0x0 0
rip 0x4005ef 0x4005ef <vulnerable+131>
(gdb) si
Program received signal SIGSEGV, Segmentation fault.
0x00000000004005ee <vulnerable+130>: c9 leave
=> 0x00000000004005ef <vulnerable+131>: c3 ret
(gdb) i r
rax 0x0 0
rbx 0x0 0
rcx 0x4141414141414141 4702111234474983745
rdx 0x414141 4276545
rsi 0x7fffffffe17a 140737488347514
rdi 0x7fffffffdb00 140737488345856
rbp 0x4141414141414141 0x4141414141414141
rsp 0x7fffffffda98 0x7fffffffda98
r8 0x1 1
r9 0x270 624
r10 0x6 6
r11 0x7ffff7b9fff0 140737349550064
r12 0x400410 4195344
r13 0x7fffffffdb90 140737488346000
r14 0x0 0
r15 0x0 0
rip 0x4005ef 0x4005ef <vulnerable+131>
나는 반환 주소를 덮어 쓴 나는이 주소로 설정 얻을 RIP를 볼 것으로 예상해야하는지 확인
그러나 RIP는 분명합니다.
rip 0x4005ef 0x4005ef <vulnerable+131>
내가 예상 한대로 RIP가 업데이트되지 않은 이유는 무엇입니까? LEAVEQ와 RETQ는 64 비트에서 실제로 무엇을합니까? 간단히 말해, 나는 무엇을 여기에서 놓치고 있냐? 어떤 차이가 있는지보기 위해 컴파일 할 때 컴파일러 인수를 생략하려고 시도했지만 아무런 차이가없는 것 같습니다.
leaveq가 이전에 할당 된 스택 공간을 해제하는 방법을 "알았"지 궁금해 할 때 답을 찾았습니다. 당신의 설명이 명확 해졌습니다. SP가 BP로 변경되어 스택 할당이 제거되면 새 BP가 스택에서 팝 아웃되고 함수가 시작될 때 POP가 팝되었습니다. 감사. – suprjami