- 세그먼트 레지스터는 버퍼 오버 플로우 검사에 사용되는 리눅스 [아래의 코드를 참조 stac에서 일부 char 배열을 정의한 k] :
static void
printint(int xx, int base, int sgn)
{
char digits[] = "ABCDEF";
char buf[16];
int i, neg;
uint x;
neg = 0;
if(sgn && xx < 0){
neg = 1;
x = -xx;
} else {
x = xx;
}
i = 0;
do{
buf[i++] = digits[x % base];
}while((x /= base) != 0);
if(neg)
buf[i++] = '-';
while(--i >= 0)
my_putc(buf[i]);
}
이제 코드 gcc 생성 코드의 디스 어셈블리가 표시됩니다. 기능 printint에 대한 어셈블러 코드의
덤프 : 우리는이 함수에서 스택 기반의 문자 배열을 제거하면
0x00000000004005a6 <+0>: push %rbp
0x00000000004005a7 <+1>: mov %rsp,%rbp
0x00000000004005aa <+4>: sub $0x50,%rsp
0x00000000004005ae <+8>: mov %edi,-0x44(%rbp)
0x00000000004005b1 <+11>: mov %esi,-0x48(%rbp)
0x00000000004005b4 <+14>: mov %edx,-0x4c(%rbp)
0x00000000004005b7 <+17>: mov %fs:0x28,%rax ------> obtaining an 8 byte guard from based on a fixed offset from fs segment register [from the descriptor base in the corresponding gdt entry]
0x00000000004005c0 <+26>: mov %rax,-0x8(%rbp) -----> pushing it as the first local variable on to stack
0x00000000004005c4 <+30>: xor %eax,%eax
0x00000000004005c6 <+32>: movl $0x33323130,-0x20(%rbp)
0x00000000004005cd <+39>: movl $0x37363534,-0x1c(%rbp)
0x00000000004005d4 <+46>: movl $0x42413938,-0x18(%rbp)
0x00000000004005db <+53>: movl $0x46454443,-0x14(%rbp)
...
...
// function end
0x0000000000400686 <+224>: jns 0x40066a <printint+196>
0x0000000000400688 <+226>: mov -0x8(%rbp),%rax -------> verifying if the stack was smashed
0x000000000040068c <+230>: xor %fs:0x28,%rax --> checking the value on stack is matching the original one based on fs
0x0000000000400695 <+239>: je 0x40069c <printint+246>
0x0000000000400697 <+241>: callq 0x400460 <[email protected]>
0x000000000040069c <+246>: leaveq
0x000000000040069d <+247>: retq
지금, GCC는이 가드 검사를 생성하지 않습니다.
커널 모듈의 경우에도 gcc가 생성 한 것과 같은 것을 보았습니다. 기본적으로 일부 커널 코드를 botrapping하는 동안 충돌이 발생했으며 가상 주소 0x28로 오류가 발생했습니다. 나중에 나는 스택 포인터를 올바르게 초기화했고 프로그램을 올바르게로드했다는 생각을했습니다. gdt에 올바른 항목이 없기 때문에 fs 기반 오프셋을 유효한 가상 주소로 변환합니다.
그러나 커널 코드의 경우 단순히 __stack_chk_fail @ plt>와 같은 점프 대신 오류가 무시되었습니다.
gcc에이 가드를 추가하는 관련 컴파일러 옵션은 -fstack-protector입니다. 나는 이것이 사용자 app을 컴파일하는 기본적으로 가능하다고 생각한다.
커널의 경우 config CC_STACKPROTECTOR 옵션을 통해이 gcc 플래그를 활성화 할 수 있습니다.
config CC_STACKPROTECTOR
699 bool "Enable -fstack-protector buffer overflow detection (EXPERIMENTAL)"
700 depends on SUPERH32
701 help
702 This option turns on the -fstack-protector GCC feature. This
703 feature puts, at the beginning of functions, a canary value on
704 the stack just before the return address, and validates
705 the value just before actually returning. Stack based buffer
706 overflows (that need to overwrite this return address) now also
707 overwrite the canary, which gets detected and the attack is then
708 neutralized via a kernel panic.
709
710 This feature requires gcc version 4.2 or above.
이는 DPL 각 세그먼트에 대한 최소한의 보안 수준을 설정하지만, 그래서 리눅스/아치/86 /이 /가 ASM/stackprotector.h
이 좋아, 그것은 보인다 포함되어 /의 FS를 gs와 관련 커널 파일 어쨌든 사용자로서 어떤 선형 주소에 액세스 할 수있는 것처럼, 왜 커널을위한 여분의 세그먼트가 있습니까? 사용자로서 메모리 주소 x에 액세스하려는 경우 x 오프셋과 함께 사용자 데이터 세그먼트 만 사용합니다. 커널은 오프셋 x가있는 커널 데이터 세그먼트를 사용할 수 있지만 동일한 선형 주소, 즉 실제 메모리의 동일한 주소로 매핑되므로 어떤 보호 기능을 제공합니까? –
@anjruu : 일부 어셈블리 지침에는 특정 권한 수준이 필요합니다. 그렇지 않으면 일반 보호 (GP) 오류가 발생합니다. 예를 들어, 포트로부터 바이트를 읽는 'IN' 명령은 현재 PL (CPL)이 입출력 PL (IOPL;'FLAGS'레지스터의 비트 12와 13)보다 작거나 같아야하며, Linux의 경우 0입니다. CPL은 'CS'(코드 세그먼트) 레지스터에 해당하는 세그먼트 설명 자의 DPL입니다. –
@Daniel : Gotcha, 맞아. 감사! –