2010-07-18 24 views
5

mã của tôi là như sau: preload.c, với các nội dung sau đây:LD_PRELOAD ảnh hưởng đến đứa trẻ mới ngay cả sau khi unsetenv ("LD_PRELOAD")

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

int __attribute__((constructor)) main_init(void) 
{ 
    printf("Unsetting LD_PRELOAD: %x\n",unsetenv("LD_PRELOAD")); 
    FILE *fp = popen("ls", "r"); 
    pclose(fp); 
} 

sau đó trong vỏ (làm lệnh thứ 2 với chăm sóc !!):

gcc preload.c -shared -Wl,-soname,mylib -o mylib.so -fPIC 
    LD_PRELOAD=./mylib.so bash 

!!! cẩn thận với lệnh cuối cùng, nó sẽ dẫn đến vòng lặp vô hạn của "sh-ls". Dừng lại sau 2 giây với^C, (hoặc tốt hơn^Z và sau đó xem ps).

Thông tin thêm

  1. Vấn đề này liên quan đến bash trong một cách nào đó; hoặc là lệnh mà người dùng chạy, hoặc như là bash popen thực thi.
  2. bổ sung Các yếu tố chính: 1) thực hiện popen từ thư viện được tải sẵn, 2) có thể cần phải làm popen trong phần khởi tạo của thư viện.
  3. nếu bạn sử dụng:

    LD_DEBUG=all LD_DEBUG_OUTPUT=/tmp/ld-debug LD_PRELOAD=./mylib.so bash 
    

    thay vì lệnh cuối cùng, bạn sẽ nhận được nhiều tác phẩm ld-debug, tên /tmp/ld-debug.*. Một cho mỗi quá trình chia hai. TRONG TẤT CẢ CÁC LỌC NÀY, bạn sẽ thấy rằng các ký hiệu được tìm kiếm lần đầu tiên trong mylib.so mặc dù LD_PRELOAD đã bị xóa khỏi môi trường.

+0

chúng ta đang nói về ngôn ngữ nào? – mvds

+0

chúng ta đang nói về ngôn ngữ C – avner

+0

@ user395074, sau đó, có lẽ, bạn nên đã điều chỉnh thẻ của bạn để phản ánh ngôn ngữ (nhấp vào liên kết "chỉnh sửa"). Ngoài ra, thẻ [preloader] dường như không phản ánh thành phần hệ điều hành mà chúng ta đang thảo luận. –

Trả lời

8

chỉnh sửa: vì vậy vấn đề/câu hỏi thực sự là: howcome không thể bạn bỏ đặt LD_PRELOAD một cách đáng tin cậy bằng cách sử dụng một tải trước main_init() từ trong vòng bash.

Lý do là execve, được gọi sau khi bạn popen, mất môi trường từ (có lẽ)

extern char **environ; 

đó là một số biến trạng thái toàn cầu trỏ đến môi trường của bạn. unsetenv() thường sửa đổi môi trường của bạn và do đó sẽ có ảnh hưởng đến nội dung của **environ.

Nếu bash cố gắng làm điều gì đó đặc biệt với môi trường (tốt ... nó có phải là một vỏ không?) Thì bạn có thể gặp rắc rối.

Thỉnh thoảng, bash quá tải unsetenv() thậm chí trước main_init(). Thay đổi mã ví dụ thành:

extern char**environ; 

int __attribute__((constructor)) main_init(void) 
{ 
int i; 
printf("Unsetting LD_PRELOAD: %x\n",unsetenv("LD_PRELOAD")); 
printf("LD_PRELOAD: \"%s\"\n",getenv("LD_PRELOAD")); 
printf("Environ: %lx\n",environ); 
printf("unsetenv: %lx\n",unsetenv); 
for (i=0;environ[i];i++) printf("env: %s\n",environ[i]); 
fflush(stdout); 
FILE *fp = popen("ls", "r"); 
pclose(fp); 
} 

hiển thị sự cố. Trong lần chạy bình thường (chạy cat, ls, vv) tôi nhận được phiên bản này của unsetenv:

unsetenv: 7f4c78fd5290 
unsetenv: 7f1127317290 
unsetenv: 7f1ab63a2290 
tuy nhiên

, chạy bash hoặc sh:

unsetenv: 46d170 

Vì vậy, có bạn có nó. bash đã có bạn bị lừa ;-)

Vì vậy, chỉ thay đổi môi trường tại chỗ sử dụng riêng unsetenv của bạn, tác động lên **environ:

for (i=0;environ[i];i++) 
{ 
    if (strstr(environ[i],"LD_PRELOAD=")) 
    { 
     printf("hacking out LD_PRELOAD from environ[%d]\n",i); 
     environ[i][0] = 'D'; 
    } 
} 

mà có thể được nhìn thấy để làm việc trong strace:

execve("/bin/sh", ["sh", "-c", "ls"], [... "DD_PRELOAD=mylib.so" ...]) = 0 

QED

+0

LD_PRELOAD không nằm trong môi trường của tiến trình con (không phải trong bố mẹ sau unsetenv). Câu trả lời của Pavel gợi ý về lý do: LD_PRELOAD được trình tải sử dụng chứ không phải bởi chương trình của tôi. mylib.so đã được tải và trình tải có thể giữ hành vi của nó mà không cần đọc lại LD_PRELOAD. ngã ba & thực thi là một lựa chọn; tuy nhiên, popen được ưu tiên nhiều vì nó cho phép tôi đọc đầu ra của quá trình con theo một cách đơn giản. strace gace rất nhiều thông tin đã không giúp tôi. Tôi đã sử dụng: xuất LD_DEBUG = symnols và thấy rõ ràng rằng tất cả các ký hiệu được tìm kiếm trong mylib.so trước bất kỳ lib nào khác. – avner

+0

điều này càng trở nên thú vị hơn, chỉ cần cập nhật câu trả lời của tôi với một số đề xuất khác – mvds

+0

@avner: thậm chí nhiều đề xuất trong câu trả lời, xây dựng '.so' của riêng tôi và vẫn không thể nhìn thấy những gì bạn đang thấy ... – mvds

2

(Câu trả lời là đầu cơ thuần túy và có thể là không chính xác).

Có thể, khi bạn chia nhỏ quy trình của mình, ngữ cảnh của các thư viện được tải vẫn tồn tại. Vì vậy, mylib.sođã được tải khi bạn gọi chương trình chính qua LD_PRELOAD. Khi bạn bỏ đặt biến và chia nhỏ, nó không được nạp lại; tuy nhiên nó đã được đã được tải bởi quy trình gốc. Có lẽ, bạn nên dỡ bỏ nó một cách rõ ràng sau khi forking.

Bạn cũng có thể thử để "giảm hạng" biểu tượng trong mylib.so. Để làm điều này, mở lại nó qua dlopen với cờ mà đặt nó vào cuối hàng đợi có độ phân giải biểu tượng:

dlopen("mylib.so", RTLD_NOLOAD | RTLD_LOCAL); 

+0

Cảm ơn lời bình luận của Pavel. Điều này nghe có vẻ hợp lý; tuy nhiên, trong trường hợp của tôi, cách giải quyết này là không thể. Tôi không thể thay đổi bất cứ điều gì sau khi ngã ba, kể từ khi tôi đang sử dụng popen mà hiện các ngã ba/exec cho tôi. (Tuy nhiên, ngã ba & exec là tùy chọn dự phòng nếu tôi không thể làm điều đó với popen). Quá trình cha mẹ phải duy trì với môi trường LD_PRELOAD (nó là một quá trình khách hàng với nhiều luồng). – avner

+0

là bạn nói rằng sau khi 'exec' các thư viện nạp vẫn tồn tại? Tôi thấy trong một strace rằng ngay cả libc được mở lại sau khi 'exec', không thể tưởng tượng một số thư viện khác sẽ chiếm ưu thế. – mvds

+0

@mvds, đang được mở lại và đang được tải/tải là những thứ khác nhau. Bạn có thể mở lại một thư viện đã được tải. –

0

câu trả lời từ mvd không chính xác!

popen() sẽ sinh ra quy trình con kế thừa cài đặt trước .so nằm trong quá trình cha mẹ. quá trình con này không quan tâm đến môi trường LD_PRELOAD.

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