3.9 如何关闭ASLR
https://scz.617.cn/unix/201205021022.txt
A: scz
将内核参数randomize_va_space置0,会在系统范围内关闭ASLR:
sysctl -w kernel.randomize_va_space=0
echo 0 > /proc/sys/kernel/randomize_va_space
从GDB 7开始,默认会调用personality( ADDR_NO_RANDOMIZE ),这将关闭被调试进 程的ASLR。
(gdb) show disable-randomization Disabling randomization of debuggee's virtual address space is on. (gdb) shell cat /proc/$(pidof -s smbd)/personality 00040000
如果想更接近真实情形:
(gdb) set disable-randomization off
注意,personality()必须在exec*()之前完成,否则无效。所以Attach的情形就是真 实情形,只有start、run受影响。
A: Mkrtich Soghomonyan 2011-03-25
内核参数randomize_va_space置0会关闭整个系统的ASLR,有时候只想关闭单个进程 的ASLR,可以用setarch命令实现这点。
$ setarch uname -m
-R cat /proc/self/maps > 1.txt
$ setarch uname -m
-R cat /proc/self/maps > 2.txt
$ diff 1.txt 2.txt
奇怪的是,setarch关闭单个进程ASLR时,stack的地址有时会变,并不总是固定的, 而内核参数randomize_va_space置0时,就没有观察到这种现象。
我们来研究一下"setarch -R"到底干了什么。
echo 0 > /proc/sys/kernel/randomize_va_space
strace -o 1.txt setarch uname -m
-R which col
/usr/bin/col
strace -o 2.txt setarch uname -m
which col
/usr/bin/col
diff 1.txt 2.txt
... 80c80 < personality(0x40008 / PER_??? /) = 0
personality(PER_LINUX32) = 0 ...
排除明显无意义的区别,注意到personality( 0x40008 )。"man setarch"时看到-R 参数对应ADDR_NO_RANDOMIZE,在头文件里搜一下:
find /usr/include -name "*.h" -exec grep -Hn ADDR_NO_RANDOMIZE {} \;
/usr/include/sys/personality.h:30: ADDR_NO_RANDOMIZE = 0x0040000, /usr/include/linux/personality.h:11: ADDR_NO_RANDOMIZE = 0x0040000, / disable randomization of VA space / /usr/include/linux/personality.h:29:#define PER_CLEAR_ON_SETID (READ_IMPLIES_EXEC|ADDR_NO_RANDOMIZE)
在"sys/personality.h"中找到两个值:
ADDR_NO_RANDOMIZE = 0x0040000 PER_LINUX32 = 0x0008
0x40008就是"ADDR_NO_RANDOMIZE|PER_LINUX32"。很明显,"setarch -R"主要就是调 用personality( ADDR_NO_RANDOMIZE | PER_LINUX32 )。
一般来说,为了编程关闭单个进程的ASLR,先fork()出子进程,在子进程中调用:
personality( original_persona | ADDR_NO_RANDOMIZE )
然后exec...()。
下面写程序测试personality()的行为:
/ * gcc-3.3 -Wall -pipe -O3 -s -o personality_demo personality_demo.c * gcc-3.3 -Wall -pipe -g -o personality_demo personality_demo.c /
include
include
include
include
include
include
include
include
include
include
int main ( int argc, char * argv[] ) { pid_t pid; int original_persona; int test_persona; int status; struct user_regs_struct regs;
if ( argc > 1 )
{
test_persona = 0;
}
else
{
test_persona = ADDR_NO_RANDOMIZE;
}
pid = fork();
if ( pid < 0 )
{
perror( "fork() failed" );
exit( EXIT_FAILURE );
}
if ( 0 == pid )
{
/*
* 子进程
*/
errno = 0;
ptrace( PT_TRACE_ME, 0, 0, 0 );
if ( errno )
{
perror( "child ptrace( PT_TRACE_ME ) failed" );
exit( EXIT_FAILURE );
}
else
{
original_persona = personality( 0xffffffff );
if ( -1 == original_persona )
{
perror( "personality( 0xffffffff ) failed" );
exit( EXIT_FAILURE );
}
if ( -1 == personality( original_persona | test_persona ) )
{
perror( "personality( original_persona | test_persona ) failed" );
exit( EXIT_FAILURE );
}
if ( -1 == execl( "/usr/bin/which", "which", "col", NULL ) )
{
perror( "execl() failed" );
}
/*
* 保护性退出
*/
exit( EXIT_FAILURE );
}
}
else
{
/*
* 父进程
*/
printf( "child = %u\n", pid );
wait( &status );
while ( WIFSTOPPED( status ) )
{
#if 0
/*
* 这个值一般是0x57F或1407,有些人会直接判断status是否等于这个
* 值,我们不建议这种山寨搞法。
*/
printf( "status = 0x%08X\n", ( unsigned int )status );
#endif
if ( -1 == ptrace( PTRACE_GETREGS, pid, 0, ®s ) )
{
perror( "child ptrace( PTRACE_GETREGS ) failed" );
}
#if 0
/*
* /usr/include/sys/user.h
*
* struct user_regs_struct
* {
* long int ebx;
* long int ecx;
* long int edx;
* long int esi;
* long int edi;
* long int ebp;
* long int eax;
* long int xds;
* long int xes;
* long int xfs;
* long int xgs;
* long int orig_eax;
* long int eip;
* long int xcs;
* long int eflags;
* long int esp;
* long int xss;
* };
*/
printf
(
"eax = 0x%08X\n"
"ebx = 0x%08X\n"
"ecx = 0x%08X\n"
"edx = 0x%08X\n"
"esi = 0x%08X\n"
"edi = 0x%08X\n"
"ebp = 0x%08X\n"
"esp = 0x%08X\n"
"eip = 0x%08X\n"
"eflags = 0x%08X\n"
"orig_eax = 0x%08X\n",
( unsigned int )regs.eax,
( unsigned int )regs.ebx,
( unsigned int )regs.ecx,
( unsigned int )regs.edx,
( unsigned int )regs.esi,
( unsigned int )regs.edi,
( unsigned int )regs.ebp,
( unsigned int )regs.esp,
( unsigned int )regs.eip,
( unsigned int )regs.eflags,
( unsigned int )regs.orig_eax
);
getchar();
#else
/*
* 仅仅是为了产生一次阻塞,给个机会执行"cat /proc/<pid>/maps"。
* 为什么选0xB?这纯属实验结果,因为0xB只出现了一次。
*/
if ( 0xB == regs.orig_eax )
{
getchar();
}
#endif
if ( -1 == ptrace( PTRACE_SYSCALL, pid, 0, 0 ) )
{
perror( "child ptrace( PTRACE_SYSCALL ) failed" );
}
wait( &status );
} /* end of while */
}
return( EXIT_SUCCESS );
} / end of main /
先启用全局ASLR:
sysctl -w kernel.randomize_va_space=2
kernel.randomize_va_space = 2
测试ASLR的效果:
./personality_demo no
child = 28684
/usr/bin/col
./personality_demo no
child = 28691
/usr/bin/col
./personality_demo no
child = 28696
/usr/bin/col
cat /proc/28684/maps > 1.txt
cat /proc/28691/maps > 2.txt
cat /proc/28696/maps > 3.txt
diff 1.txt 2.txt
4,7c4,7 < b7f54000-b7f55000 r-xp b7f54000 00:00 0 [vdso] < b7f55000-b7f70000 r-xp 00000000 08:01 1046552 /lib/ld-2.11.2.so < b7f70000-b7f72000 rw-p 0001a000 08:01 1046552 /lib/ld-2.11.2.so < bfebf000-bfed4000 rw-p bfebf000 00:00 0 [stack]
b7fb1000-b7fb2000 r-xp b7fb1000 00:00 0 [vdso] b7fb2000-b7fcd000 r-xp 00000000 08:01 1046552 /lib/ld-2.11.2.so b7fcd000-b7fcf000 rw-p 0001a000 08:01 1046552 /lib/ld-2.11.2.so bfcb4000-bfcca000 rw-p bfcb4000 00:00 0 [stack]
diff 1.txt 3.txt
4,7c4,7 < b7f54000-b7f55000 r-xp b7f54000 00:00 0 [vdso] < b7f55000-b7f70000 r-xp 00000000 08:01 1046552 /lib/ld-2.11.2.so < b7f70000-b7f72000 rw-p 0001a000 08:01 1046552 /lib/ld-2.11.2.so < bfebf000-bfed4000 rw-p bfebf000 00:00 0 [stack]
b7fcb000-b7fcc000 r-xp b7fcb000 00:00 0 [vdso] b7fcc000-b7fe7000 r-xp 00000000 08:01 1046552 /lib/ld-2.11.2.so b7fe7000-b7fe9000 rw-p 0001a000 08:01 1046552 /lib/ld-2.11.2.so bfc70000-bfc86000 rw-p bfc70000 00:00 0 [stack]
我这个系统上randomize_va_space置为2,heap也没有被随机化啊,不给力。
uname -a
Linux debian 2.6.18-4-686 #1 SMP Wed May 9 23:03:12 UTC 2007 i686 GNU/Linux
接着测试关闭单个进程ASLR的效果:
./personality_demo
child = 28709
/usr/bin/col
./personality_demo
child = 28714
/usr/bin/col
./personality_demo
child = 28717
/usr/bin/col
cat /proc/28709/maps > 4.txt
cat /proc/28714/maps > 5.txt
cat /proc/28717/maps > 6.txt
diff 4.txt 5.txt
diff 4.txt 6.txt
7c7 < bffeb000-c0000000 rw-p bffeb000 00:00 0 [stack]
bffea000-c0000000 rw-p bffea000 00:00 0 [stack]
除了stack有时候在变,其它的ASLR都消失了。
看上去personality( ADDR_NO_RANDOMIZE )对stack的处理有点古怪。
D: scz
对setuid程序,Linux内核会清除传递给personality()的如下标志位:
ADDR_NO_RANDOMIZE ADDR_COMPAT_LAYOUT MMAP_PAGE_ZERO READ_IMPLIES_EXEC
即使当前用户是root。