如何在 linux 上获取正在运行的线程的起始地址?

逆向工程 linux 线
2021-06-13 05:09:46

问题陈述

我正在尝试获取调用中start_routine传递的正在运行的线程的地址pthread_create()

研究至今

它显然不在/proc/[tid]/stat或 中/proc/[tid]/status

我发现它start_routine[ 1 ]的成员struct pthread并由pthread_create[ 1 ]设置如果我知道这个地址struct,我就能读到这个start_routine地址。

我还在td_thr_get_info调试库thread_db.h[ 2 ] 中找到了定义struct用有关线程的信息填充 a ,包括启动函数 [ 3 ]。但是,它需要一个struct td_thragent作为参数,我不知道如何正确创建它。

4个回答

启动例程pthread_start_thread()indirect call类似调用

call [reg32 + const]  

您可以反汇编函数并确认使用的寄存器和使用的常量

在我的 dsl vm (very very old )

我明白 call [esi+0x8c]

gdb -q ./someexe
break main
r
break pthread_start_thread
c
info symbol *( *(unsigned long *)($ebp +8) + 0x8c ) )

为我提供以下测试程序的符号 thrdfunc ()

#include <pthread.h>
void *thrdfunc (void * foo) {
pthread_exit(0);
}
int main (void) {
pthread_t thread;
pthread_create(&thread,0,thrdfunc,0);
return 0;
}

编译并链接到

gcc -pthread -o pthreadtest pthreadtext.c

我倾向于仅用于gdb附加到进程,发出信息线程,选择带有线程的线程nn并执行bt. 您想要的函数是由 调用的函数start_thread()虽然,这将在 start 函数中,而不是条目本身(尽管您可以扫描一个众所周知的函数序言)。

多亏了 blabb 和 Jonathon Reinhart 的提示,我才能够编写一个get_thread_start_address()函数。它读取用于pthread_start_thread()调用启动例程的相同地址在内核 3.2.0-4-686-pae 中,这个地址是GS+0x234. ptrace用来获取GS register和实际的GS segment address. 这是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <asm/ldt.h>
#include <asm/ptrace.h>

int attach(int tid);
int detach(int tid);
void print_error(char* func_name, int errnumber);

int get_gs_register(int tid){
    struct user_regs_struct regs;
    int ret = ptrace(PTRACE_GETREGS, tid, NULL, &regs);
    if(ret == -1){
        print_error("PTRACE_GETREGS", errno);
        return -1;
    }
    return regs.xgs;
}

// This is needed to get the actual GS segment address from the GS register value
int get_thread_area_base(tid, gs){
    struct user_desc desc;
    memset(&desc, 0, sizeof(desc));
    int ret = ptrace(PTRACE_GET_THREAD_AREA, tid, gs / LDT_ENTRY_SIZE, &desc);
    if(ret == -1){
        print_error("PTRACE_GET_THREAD_AREA", errno);
        return -1;
    }
    return desc.base_addr;
}

void* get_start_address(tid, start_address_pointer){
    char start_addr_str[4];
    int mem_file;
    char mem_file_path[255];
    snprintf(mem_file_path, sizeof(mem_file_path), "/proc/%i/mem", tid);
    mem_file = open(mem_file_path, O_RDONLY);
    if(mem_file == -1){
        print_error("open()", errno);
        return (void*) -4;
    }
    int ret = lseek(mem_file, start_address_pointer, SEEK_SET);
    if(ret == -1){
        print_error("lseek()", errno);
        return (void*) -5;
    }

    ret = read(mem_file, start_addr_str, 4);
    if(ret == -1){
        print_error("read()", errno);
        return (void*) -6;
    }   

    return (void*) *((int*)start_addr_str);
}

int main(int argc, char* argv[]){
    if(argc <= 1){
        printf("Usage: %s TID\n", argv[0]);
        return -1;
    }   
    int tid = atoi(argv[1]);    
    int gs;
    int thread_area_base;
    int start_address_offset = 0x234;
    void* start_address;

    int ret = attach(tid);
    if(ret == -1) return -1;

    gs = get_gs_register(tid);
    if(gs == -1){
        detach(tid);
        return -2;
    }

    thread_area_base = get_thread_area_base(tid, gs);
    if(thread_area_base == -1){
        detach(tid);
        return -3;
    }
    printf("thread_area_base: %p\n", (void*) thread_area_base);
    unsigned int start_address_pointer = thread_area_base + start_address_offset;
    printf("start_address_pointer: %p\n", (void*) start_address_pointer);

    start_address = get_start_address(tid, start_address_pointer);
    printf("start_address: %p\n", start_address);

    detach(tid);
    return 0;
}

int attach(int tid){
    int status; 
    int ret = ptrace(PTRACE_ATTACH, tid, NULL, NULL);
    if(ret == -1){
        print_error("PTRACE_ATTACH", errno);
    }

    ret = waitpid(-1, &status, __WALL);
    if(ret == -1){
        print_error("waitpid()", errno);
    }
    return ret;
}   

int detach(int tid){
    int ret = ptrace(PTRACE_DETACH, tid, NULL, NULL);
    if(ret == -1){
        print_error("PTRACE_DETACH", errno);
    }
    return ret;
}

void print_error(char* func_name, int errnumber){
    printf("%s failed. %i, %s\n", func_name, errnumber, strerror(errnumber));
}

由于所有线程共享创建它们的进程的地址空间,我认为您想要的是对 dlsym 的调用:

http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html

dlsym() 函数应获取在可通过 dlopen() 调用访问的对象中定义的符号的地址。句柄参数是调用 dlopen() 返回的值(此后没有通过调用 dlclose() 释放),而 name 是作为字符串的符号名称。

========下一次尝试========

开始在 pthread.h 中闲逛。到目前为止,我已经找到了最好的事情是这样

pthread_attr_getstack()

堆栈属性指定用于创建线程堆栈的存储区域。存储的基址(最低可寻址字节)应为 stackaddr,存储的大小应为 stacksize 字节。堆栈大小应至少为 {PTHREAD_STACK_MIN}。stackaddr 应适当对齐以用作堆栈;例如,如果 (stackaddr & 0x7) 不为 0,则 pthread_attr_setstack() 可能会失败并显示 [EINVAL]。 stackaddr 和 stacksize 描述的堆栈中的所有页面都应该是线程可读和可写的。

还有更多关于POSIX线程编程这里

这是NtQueryInformationThread我能找到的最接近的功能