我试图获取在
start_routine
调用中传递的正在运行的线程的pthread_create()
的地址。显然不在/proc/[tid]/stat
或/proc/[tid]/status
中。我发现
start_routine
是struct pthread
的成员,并由pthread_create
设置[1]。 struct
地址。我还在调试库
start_routine
[2]中找到了td_thr_get_info
。它用有关线程的信息填充thread_db.h
,包括启动函数[3]。但是,它需要一个struct
作为参数,我不知道如何正确创建它。#1 楼
可以通过pthread_start_thread()
从indirect call
调用启动例程,例如在我的dsl vm (very very old )
中我看到了
call [esi+0x8c]
call [reg32 + const]
为我提供了thrdfunc()符号,用于以下测试程序>
gdb -q ./someexe
break main
r
break pthread_start_thread
c
info symbol *( *(unsigned long *)($ebp +8) + 0x8c ) )
编译并链接到
#include <pthread.h>
void *thrdfunc (void * foo) {
pthread_exit(0);
}
int main (void) {
pthread_t thread;
pthread_create(&thread,0,thrdfunc,0);
return 0;
}
#2 楼
我倾向于只使用gdb
附加到进程,发出一个信息线程,选择带有线程nn
的线程并执行bt
。所需的函数是start_thread()
调用的函数。尽管这将在启动函数中,而不是在条目本身中(尽管您可能会扫描一个众所周知的函数序言)。#3 楼
感谢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, ®s);
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));
}
#4 楼
由于所有线程共享创建它们的进程的地址空间,因此我认为您想要的是对dlsym的调用:http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym .html
dlsym()函数应获取通过dlopen()调用可访问的对象中定义的符号的地址。 handle
参数是从对dlopen()的调用返回的值(并且自调用dlclose()以来未释放
),并且name是
符号的名称,如下所示:
======= NEXT ATTEMPT ========
开始在pthread.h中浏览。到目前为止,我发现的最好的东西是:
pthread_attr_getstack()
堆栈属性指定用于
的存储区域创建线程的堆栈。
存储器的基址(最低可寻址字节)应为stackaddr,存储器的大小应为
stacksize字节。 stacksize至少应为{PTHREAD_STACK_MIN}。
stackaddr应该适当对齐以用作堆栈;
例如,pthread_attr_setstack()可能因[EINVAL]失败,如果(< &0x7)不能为0。
stackaddr和stacksize描述的堆栈中的所有页面都应该可以通过
线程读取和写入。
POSIX线程编程在这里。
这是我能找到的最接近
NtQueryInformationThread
的功能。 评论
感谢您的回答。我实际上想在运行时获取起始地址。而且我看不到提供它的危险。 Windows中有此功能,我不知道有任何使用它的攻击。
–user2804197
2014年4月1日23:14
如果公开一个函数来计算线程的起始地址,那么为什么首先要担心ASLR?使ASLR阻止能够预测代码的加载位置。如果您公开这样的功能……您就是击败了ASLR。无论如何,我想我很快就会有一个真正的答案...
–avgvstvs
2014年4月2日在13:24
更新的答案。
–avgvstvs
2014年4月2日在13:29
“使ASLR阻止了能够预测代码的加载位置。如果公开这样的功能……您就击败了ASLR。” IMO的ASLR的目标是通过使返回地址和库的地址未知来防止攻击者首先执行任意代码。攻击者如何获取get_start_address()函数的地址来规避ASLR?
–user2804197
14年4月3日在15:00
我看不到如何使用dlsym()获得线程的起始地址。它返回共享对象的符号地址。启动线程不会创建或更改任何共享库,因此无法从共享库中提取它的起始地址。还是我错过了什么?无论如何,感谢您尝试帮助我。
–user2804197
2014年4月3日15:08
评论
+1当然,GDB可以做的任何事情,都可以使用ptrace API。
–乔纳森·莱因哈特(Jonathon Reinhart)
2014年4月4日0:01