漏洞利用代码中指针的解释

Explanation of a pointer in exploit code

在一些获取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++; 
} 

代码采用局部变量i的地址来获取指向当前堆栈帧的指针。然后,它将地址对齐到 8K 页(这就是你对 x & ~8191 所做的:8191 是 2^13 - 1 这意味着 ~8191 是除了低 13 位之外的所有内容,所以用一个number 将清除低 13 位,即将数字对齐到最接近的 2^13 的较低倍数,换句话说,对齐到 8K 边界)。

然后它获取这个地址并将其解释为指向指针的指针并从中加载指向的地址。有关详细信息,请参阅 Understanding the getting of task_struct pointer from process kernel stack

After that, it tries to locate a specific structure stored somewhere after that address: 它会查看后面的1024-13 unsigneds,试图在内存中找到当前进程信息所在的位置(probably) is stored:当它发现一块内存中保存着当前 UID 和 GID 的多个副本时,它就认为它已经找到了。在这种情况下,它会修改它,以便当前进程获得 UID 和 GID 0,使进程 运行 处于 root 之下(加上它将全一存储到以下功能标志中)。

比照。 struct cred.

我要 post 另一个答案,因为这里确实有东西要补充。

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(我们记得有符号和无符号的按位运算行为完全相同)。至于为什么他们不用十六进制常量来编写它们,谁在乎呢。做这些事情的人都知道他们 2 的力量。读取 8191 可能比读取 0x1FFF 更快。