重新定位线程控制块

时间:2014-01-09 19:47:34

标签: assembly process kernel internals

我目前处于需要重新定位TCB(线程控制块)的情况。 根据我的理解,在调用VDSO时,流程执行中有以下方案(根据this article

  • 全部由指令调用*%gs:0x10
  • 触发
  • %gs是使用全局描述符表的段寄存器。 GDT是存储在与进程相关联的内核堆栈中的关联表,并保持段寄存器值与进程的地址空间中的地址之间的对应关系。它是随机初始化并由加载程序(glibc库的一部分)
  • 选择
  • 一旦建立了对应关系,处理器就会在*(GDT [%gs] + 0x10)处读取地址并进行VDSO中的呼叫。
  • 执行系统调用并从内核返回到用户进程使用内核AS中的返回地址存储。

我的问题是我需要用其他东西覆盖我的一些进程内存,并且大多数时候,它会删除地址部分(GDT [%gs] + 0x10)。

我想要做的是将TCB的内容(从GDT [%gs]重新定位到GDT [%gs] + 0x ..)在我的进程'AS中的空闲位置,但这意味着改变GDT内容,我认为我不能从用户模式做什么。换句话说,我想在我的GDT中更改%GS的关联。

提前感谢您的回答, / iansus

1 个答案:

答案 0 :(得分:0)

好的,我找到了解决方案:

int relocateTCB()
{

#ifndef TCB_LDT_INDEX
#define TCB_LDT_INDEX 6
#endif


    struct user_desc* u_info = (struct user_desc*) malloc(sizeof(struct user_desc));
    int r,j;
    int mstart, map;
    int PS = sysconf(_SC_PAGESIZE);

    if(u_info == NULL)
    {
        errno = EFAULT;
        return -1;
    }

    u_info->entry_number = TCB_LDT_INDEX;
    r = syscall(SYS_get_thread_area, u_info);

    if(r==-1) return -1;

    mstart = (u_info->base_addr) & ~(PS-1);
    map = (int) mmap(NULL, PS, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, (off_t)0);

    if(!map)
    {
        errno = EFAULT;
        return -1;
    }

    for(j=mstart; j<mstart+PS; j+=sizeof(int))
    {
        * ((int*) (map+j-mstart)) = * ((int*) j);
    }

    u_info->base_addr = map + u_info->base_addr - mstart;
    r = syscall(SYS_set_thread_area, u_info);

    if(r==-1)
    {
        errno = EINVAL;
        return -1;
    }

    for(j=mstart; j<mstart+PS; j+=sizeof(int))
        *((int*) j) = 0;

    // Keep *%gs = %gs
    *((int*) u_info->base_addr) = u_info->base_addr;

    return 0;
}