利用代码中的指针的解释
在获取root shell的一些漏洞攻击中,我经常看到这样一个指针:
int i; unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191);
任何人都可以解释一下这个指针吗? 我认为8191是内核堆栈的大小。 p
指向内核栈的底部? 这里是如何使用指针p
:
int i; unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191); for (i = 0; i < 1024-13; i++) { if (p[0] == uid && p[1] == uid && p[2] == uid && p[3] == uid && p[4] == gid && p[5] == gid && p[6] == gid && p[7] == gid) { p[0] = p[1] = p[2] = p[3] = 0; p[4] = p[5] = p[6] = p[7] = 0; p = (unsigned *) ((char *)(p + 8) + sizeof(void *)); p[0] = p[1] = p[2] = ~0; break; } p++; }
该代码将获取局部variablesi
的地址以获取指向当前堆栈帧的指针。 然后,它将地址alignment到8K页(这就是你对x & ~8191
:8191所做的是2 ^ 13 – 1,这意味着~8191
除了低13位之外都是1,所以用数字加和会清除低13位,即将数字与2 ^ 13的最接近的低倍数alignment,换句话说,alignment到8K边界)。
然后它将这个地址解释为指向指针的指针,并从中加载指向的地址。 请参阅了解从进程内核堆栈获取task_struct指针以获取更多信息。
之后,它试图find一个特定的结构存储在地址之后的某个地方:它通过以下1024-13
unsigned
s来查找,试图在存储当前进程信息(可能)的地方find一个地方:当它find一块内存保存当前的UID和GID的多个副本,它假定它已经find它。 在这种情况下,它会修改它,以便当前进程获得UID和GID 0,从而使进程在根目录下运行(并将其全部存储到以下function标志中)。
参看 struct cred
。
我要发布另外一个答案,因为这里真的有什么要补充的。
unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191);
结果p是指向8192字节大小内存块的开始的指针。 但是,代码是错误的。 如果p高于INT_MAX(它可以是或者它将被转换为无符号的,而不是无符号的长),则高位被掩码剪掉。 正确的代码如下:
unsigned *p = *(unsigned**)(((ptrdiff_t)&i) & ~(ptrdiff_t)8191);
或者使用uintptr_t:
unsigned *p = *(unsigned**)(((uintptr_t)&i) & ~(uintptr_t)8191U);
有必要将其转换为整数并返回到代码工作的指针; 但是要保证一个int大小的指针需要使用ptrdiff_t(我们记得有符号和无符号的行为对于按位运算是完全相同的)。 至于为什么他们不写hex常量,谁在乎。 做这些事情的家伙心知肚明。 读取8191然后读取0x1FFF可能会更快。