_nosay 发表于 2016-11-04 19:20

从volatile扯到so注入

本帖最后由 _nosay 于 2016-11-04 19:59 编辑


[*]模拟①:变量“显式”被改变(编译阶段,编译器能"看到")
    其它线程"意外"改变stop值:低优化级别时,stop总是从内存读,所以stop值被子线程修改后,循环结束;高优化级别时,假设编译器为stop变量安排一个寄存器eax,编译时发现stop被修改的语句时,也自动添加修改eax寄存器的指令
   


[*]模拟②:变量“隐式”被改变(运行阶段,编译器"看不到")
    运行时硬件"意外"改变stop值:低优化级别时,每次访问stop时,都重新从内存读取,显然循环体一次都不会执行;高优化级别时,假设编译器为stop变量安排一个寄存器eax,之后访问stop的指令,都被编译成访问eax的指令,并且编译器又不能预测硬件将来什么时候会改变stop,所以就不可能生成修改eax寄存器的指令,那么eax就一直为0,从而循环一直执行
   
(疑问:为啥不是-O0时不进入循环,-O3时一直循环{:qq27:})


[*]volatile(“易变的”)
    volatile相当于对编译器“不足”的弥补(有些时候编译器无法预测,但程序员知道,那就就人为的告诉编译器),其中volatile就是告诉编译器:加了优化选项时,哪些变量仍然当作不优化处理。这样,不优化时,编译就不会为变量安排寄存器(不相信在第二个例子中,为stop变量加上volatile修饰符,再按分别按-O3级别编译,看运行结果是否和-O0一样)。


[*]const(“不变的”)
    const用于向编译器说明当前函数或当前程序中不准修改变量,用于编译阶段向程序员提醒错误,并不能限制运行时其它地方修改这个变量对应的内存。

    const与volatile修饰并不矛盾,一个变量可以同时加const、volatile修饰:
    ① const针对的是编译阶段,从而只保证了当前程序不修改某些变量;
    ② volatile针对的是运行阶段,防止除当前进程的其它地方(硬件、其它CPU、其它进程)"意外"修改某些变量。

    进程间不都用的虚拟地址吗,怎么会被其它进程修改?
    ① 内核代码虚拟地址与物理地址映射规则很简单,特殊情况下某个内核进程,希望修改另一个内核进程的某个变量是可以做到的;
    ② 对于用户态程序,至少我知道通过“注入”技术是可以做到的(注意:跟sql注入、内存共享都没有关系,内存共享用的是mmap()系统调用,修改是的共享的内存,而注入用的是ptrace()系统调用,修改是的指向共享内存的指针)。


[*]动态注入
    演示代码(求打赏):
    ① gcc test.c -o test -g -Wall
      gcc injection.c -o injection -g -Wall
      test.c代码:
      
    ② 执行./test,按道理只可能一直打印"who are you?"
      执行./injection,竟然让test进程打印了一下"it's me~"{:qq28:}

    injection.c程序中定义的CODE,是用于打印"it's me~"的机器码,根据你是32/64位系统,分别利用helloworld-32.c、helloworld-64.c提取(知道“位置无关”这种技巧,看这两个文件中的汇编代码就没什么问题了)。
    以64位系统为例,编译helloworld-64.c得到hello可执行文件,并利用gdb提取机器码:
   


[*]动态链接原理
    http://www.cnblogs.com/catch/p/3857964.html

amarant 发表于 2016-11-07 16:00

很好很强大,提个问题,injection.c为什么会有对test.c的PTRACE权限呢?

_nosay 发表于 2016-11-07 16:22

回复 2# amarant


root执行{:qq28:}

amarant 发表于 2016-11-08 08:46

回复 3# _nosay

很不错,感谢分享

爻易 发表于 2016-11-09 16:26

能调试然后能调戏;P

我爱你我的菜 发表于 2016-11-23 16:21

疑问:为啥不是-O0时不进入循环,-O3时一直循环

我爱你我的菜 发表于 2016-11-23 16:39

这和你的结论相反啊,优化了的反而结果是对的

我爱你我的菜 发表于 2016-11-23 17:01

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

void *start_routine(void *arg)
{
        sleep(2);
        *(int *)arg = 1;
        return NULL;
}

int main()
{

        /*volatile*/ int stop=0;
        unsigned long rbp,rsp;

        __asm__ __volatile__(
                "movq %%rbp, %%rax\n\t"
                "movq %%rsp, %%rbx\n\t"
                "movl $1, (%%rsp)"
                :"=a"(rsp), "=b"(rsp)
                :
                :"memory");
        //printf("rbp:%p,rsp:%p,&rbp:%p,&rsp:%p,&stop:%p\n",(void*)rbp,(void*)rsp,(void*)&rbp,(void*)&rsp,&stop);
        //printf("rbp:%p,rsp:%p,&stop:%p\n",(void*)rbp,(void*)rsp,&stop);
        int b;
        b=stop;
        while(!b)
        {
                printf("%d\n",b);
                sleep(1);
        }
        return 0;
}



这个例子能说明问题。。。。。。。。

_nosay 发表于 2018-05-22 19:06

回复 8# 我爱你我的菜

嗯,这个方法好。

_nosay 发表于 2018-05-22 19:15

回复 8# 我爱你我的菜

但我发现,不优化时,rsp直接和stop位置相等,优化时,rsp要加一点才能和stop位置相等,是因为位置不同,没有修改到stop,才一直循环的,所以帖子中关于“编译器看不见”的猜测,是错的。
页: [1]
查看完整版本: 从volatile扯到so注入