2012-01-16 30 views
9

Vì nhiều mục đích khác nhau, tôi đang cố gắng lấy địa chỉ của tiêu đề ELF của tệp thi hành chính mà không cần phân tích cú pháp /proc/self/maps. Tôi đã thử phân tích cú pháp chuỗi link_list được cung cấp bởi các hàm dlopen/dlinfo nhưng chúng không chứa mục nhập tại đó l_addr trỏ đến địa chỉ cơ sở của tệp thực thi chính. Có cách nào để làm điều này (Tiêu chuẩn hay không) mà không cần phân tích cú pháp /proc/self/maps?Lấy tiêu đề ELF của tệp thực thi chính

Một ví dụ về những gì tôi đang cố gắng để làm:

#include <stdio.h> 
#include <elf.h> 
int main() 
{ 
    Elf32_Ehdr* header = /* Somehow obtain the address of the ELF header of this program */; 
    printf("%p\n", header); 
    /* Read the header and do stuff, etc */ 
    return 0; 
} 
+1

'read()' từ '/ proc/self/exe' – Dave

+0

Ah, tôi quên đề cập đến, tôi cũng cần địa chỉ cơ sở cùng với tiêu đề ELF và tiêu đề ELF phải là địa chỉ cơ sở là –

Trả lời

16

Con trỏ void * trả về bởi dlopen(0, RTLD_LAZY) mang đến cho bạn một struct link_map *, tương ứng với thực thi chính.

Gọi dl_iterate_phdr cũng trả về mục nhập cho thực thi chính trên thực hiện gọi lại rất đầu tiên.

Bạn có thể bị nhầm lẫn bởi thực tế là .l_addr == 0 trong bản đồ liên kết và rằng dlpi_addr == 0 khi sử dụng dl_iterate_phdr.

Điều này đang xảy ra, vì l_addr (và dlpi_addr) không thực sự ghi lại địa chỉ tải của hình ảnh ELF. Thay vào đó, họ ghi lại di chuyển đã được áp dụng cho hình ảnh đó.

Thông thường, tệp thi hành chính được xây dựng để tải tại 0x400000 (đối với x86_64 Linux) hoặc tại 0x08048000 (đối với ix86 Linux) và được tải tại cùng địa chỉ đó (nghĩa là chúng không được di chuyển).

Nhưng nếu bạn liên kết tệp thực thi của mình với cờ -pie, thì nó sẽ được liên kết tại số 0x0, và nó sẽ được di chuyển sang một số địa chỉ khác.

Vậy làm thế nào để bạn đến được tiêu đề ELF? Dễ dàng:

#ifndef _GNU_SOURCE 
#define _GNU_SOURCE 
#endif 

#include <link.h> 
#include <stdio.h> 
#include <stdlib.h> 

static int 
callback(struct dl_phdr_info *info, size_t size, void *data) 
{ 
    int j; 
    static int once = 0; 

    if (once) return 0; 
    once = 1; 

    printf("relocation: 0x%lx\n", (long)info->dlpi_addr); 

    for (j = 0; j < info->dlpi_phnum; j++) { 
    if (info->dlpi_phdr[j].p_type == PT_LOAD) { 
     printf("a.out loaded at %p\n", 
      (void *) (info->dlpi_addr + info->dlpi_phdr[j].p_vaddr)); 
     break; 
    } 
    } 
    return 0; 
} 

int 
main(int argc, char *argv[]) 
{ 
    dl_iterate_phdr(callback, NULL); 
    exit(EXIT_SUCCESS); 
} 


$ gcc -m32 t.c && ./a.out 
relocation: 0x0 
a.out loaded at 0x8048000 

$ gcc -m64 t.c && ./a.out 
relocation: 0x0 
a.out loaded at 0x400000 

$ gcc -m32 -pie -fPIC t.c && ./a.out 
relocation: 0xf7789000 
a.out loaded at 0xf7789000 

$ gcc -m64 -pie -fPIC t.c && ./a.out 
relocation: 0x7f3824964000 
a.out loaded at 0x7f3824964000 

Cập nhật:

Tại sao trang người đàn ông nói "địa chỉ cơ sở" và không di dời?

Đó là một lỗi ;-)

Tôi đoán rằng những người đàn ông trang được viết rất lâu trước khi prelinkpie, và ASLR tồn tại. Nếu không có prelink, các thư viện được chia sẻ luôn được liên kết để tải tại địa chỉ 0x0, và sau đó relocationbase address trở thành một và giống nhau.

cách dlpi_name trỏ đến chuỗi trống khi thông tin đề cập đến tệp thực thi chính?

Đó là một sự cố khi triển khai.

Cách này hoạt động, là hạt nhân open(2) s thực thi và chuyển mô tả tệp mở cho bộ nạp (trong vector auxv[], dưới dạng AT_EXECFD). Mọi thứ trình tải đều biết về tệp thực thi mà nó nhận được bằng cách đọc bộ mô tả tệp đó.

Không có cách nào dễ dàng trên UNIX để ánh xạ một bộ mô tả tệp quay lại tên mà nó đã được mở dưới dạng. Đối với một điều, UNIX hỗ trợ liên kết cứng và có thể có nhiều tên tệp tham chiếu đến cùng một tệp.

Hạt nhân Linux mới hơn cũng chuyển vào tên đã được sử dụng để execve(2) thực thi (cũng trong auxv[], dưới dạng AT_EXECFN). Nhưng đó là tùy chọn, và ngay cả khi nó được thông qua, glibc không đặt nó vào .l_name/dlpi_name để không phá vỡ các chương trình hiện tại mà đã trở thành phụ thuộc vào tên đang trống.

Thay vào đó, glibc lưu tên đó trong __progname__progname_full.

Bộ nạp coudreadlink(2) tên từ /proc/self/exe trên các hệ thống không sử dụng AT_EXECFN, nhưng hệ thống /proc tập tin không được bảo đảm đến được gắn một trong hai, do đó vẫn sẽ để lại nó với một cái tên trống đôi khi.

+0

Yea Tôi đoán tôi đã nhầm lẫn rồi. Nhưng tôi tự hỏi, ở đâu trong manpage nói rằng 'l_addr' hoặc' dlpi_addr' là địa chỉ được di chuyển? Tất cả các trang man tôi đã đọc chỉ nói "địa chỉ cơ sở" –

+0

Ngoài ra, làm cách nào để 'dlpi_name' trỏ đến một chuỗi rỗng khi' thông tin' đề cập đến tệp thực thi chính? Không nên nó chứa tên của thực thi chính? –

+0

Tôi đã cập nhật câu trả lời. Bạn nhận được 3 câu trả lời cho giá của 1 ;-) –

0

Có là glibc dl_iterate_phdr() chức năng. Tôi không chắc chắn nó cung cấp cho bạn chính xác những gì bạn muốn, nhưng đó là gần như tôi biết:

"Hàm dl_iterate_phdr() cho phép ứng dụng truy vấn vào thời gian chạy để tìm ra các đối tượng chia sẻ mà nó đã tải. " http://linux.die.net/man/3/dl_iterate_phdr

+0

nhận tất cả các đối tượng được chia sẻ mà chương trình đã tải, mà tôi có thể thực hiện bằng cách đi qua chuỗi link_list và đó có thể là những gì nó thực hiện trong hàm. Nhưng tôi muốn địa chỉ cơ sở của chính ứng dụng, không phải là các đối tượng được chia sẻ mà nó đã tải. –

+0

Bạn đã thử nghiệm rằng nó không trả lại bản thân ứng dụng? –

+0

Xin lỗi, tôi đã tắt những gì tôi đọc trong manpage –

Các vấn đề liên quan