extern crate core;
use core::ops::{Deref, DerefMut};
struct MutPtr<T>{
ptr: *mut T
}
impl<T> MutPtr<T>{
fn new(value: &mut T) -> MutPtr<T>{
MutPtr{ptr: value}
}
}
impl<T> Deref for MutPtr<T>{
type Target = T;
fn deref(&self) -> &T{
unsafe{
&(*self.ptr)
}
}
}
impl<T> DerefMut for MutPtr<T>{
fn deref_mut(&mut self) -> &mut T{
unsafe{
&mut (*self.ptr)
}
}
}
struct Bar{
v: i32
}
fn error()-> MutPtr<Bar> {
let mut b = Bar{v:42};
let ptr_b = MutPtr::new(&mut b);
ptr_b
}
fn main(){
let mut b = Bar{v:42};
let mut ptr_b = MutPtr::new(&mut b);
let mut ptr_b1 = MutPtr::new(&mut b);
ptr_b.v = 10;
println!("{}",b.v);
ptr_b1.v = 21;
println!("{}",b.v);
let mut err = error();
println!("{}",err.v);
err.v = 42; // what happens here?
println!("{}",err.v);
}
在第49行,我将写入一些内存地址
err.v = 42;
我知道这很糟糕,但我想知道究竟发生了什么?起初我预计它会崩溃但我希望我能够更改err.v地址的值。但写作没有做任何事情。
内存似乎是写保护的?
我只是“幸运”,写作没有改变什么?
答案 0 :(得分:3)
我只是&#34;幸运&#34;写的没有改变什么?
是的,幸运的。
写入随机存储器是undefined behaviour:编译器认为它永远不会发生并优化假设。如果它确实发生,那么没有限制或保证可以对结果行为做出决定。例如。它可以change the return address在下一个函数调用结束时使用,使CPU跳转到某些&#34;随机&#34;一块记忆。它通常非常糟糕,这些事情可能是一个可利用的安全漏洞。
在这种情况下,你已经写过,可能因为err
指向堆栈的低位而导致程序没有发生可怕的死亡,程序在写入发生时并没有使用。如果在使用堆栈的那个区域时发生了写操作,那么修改指针以指向无意义的事情很容易发生:
use std::mem;
#[inline(never)]
fn bad() -> &'static mut u32 {
let mut x = 0u32;
unsafe { mem::transmute(&mut x) }
}
#[inline(never)]
fn innocent(x: &mut u32) {
println!("{:p}", &*x);
*x = 0xDEADBEEF;
println!("{:p}", x);
*x = 0;
}
fn main() {
let ptr = bad();
innocent(ptr);
}
在playpen上-O2
,此时会打印:
0x7fff03dbae84
0xdeadbeef03dbae84
playpen: application terminated abnormally with signal 4 (Illegal instruction)
第一行是x
的实际值。 x
之后的下一行是*x = 0xDEADBEEF;
...也就是说,写入直接发送到存储x
本身的堆栈部分,将上半部分更改为0xDEADBEEF
。此时,x
是一个无意义的值,因此*x = 0
会导致段错误(由于Rust默认会覆盖某些信号处理程序而显示为中止)。
我说&#34;此刻&#34;因为程序的行为对确切的编译器版本/优化级别/源代码非常敏感,例如更改第一个print
以删除&*
会使程序打印0x7fff03dbae84
两次然后中止(可能是因为正在修改返回地址而不是x
)。
如果攻击者可以控制写入无效指针的内容,则他们可能/可能能够修改写指针以使程序跳转到piece of shellcode并打开您的应用程序。我看到可能因为even the smallest "unexploitable" problems被证明可以被剥削。