# ldd 的問題 ## 問題:為何下面三次 ldd /bin/ls 位址都不一樣 ``` $ ldd /bin/ls linux-vdso.so.1 (0x00007ffc8c3e9000) libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f82f5acc000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f82f56db000) libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f82f5469000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f82f5265000) /lib64/ld-linux-x86-64.so.2 (0x00007f82f5f16000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f82f5046000) $ ldd /bin/ls linux-vdso.so.1 (0x00007ffcad9ea000) libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f9d681ea000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9d67df9000) libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f9d67b87000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f9d67983000) /lib64/ld-linux-x86-64.so.2 (0x00007f9d68634000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f9d67764000) $ ldd /bin/ls linux-vdso.so.1 (0x00007ffd57d47000) libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f7f2acde000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7f2a8ed000) libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f7f2a67b000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f7f2a477000) /lib64/ld-linux-x86-64.so.2 (0x00007f7f2b128000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f7f2a258000) ``` ## 回答 ### Jim Huang 先去看 ldd 的實作原理,是透過 glibc 裡頭的 ld-linux 嘗試載入 ELF執行檔,並且設定 LD_TRACE_LOADED_OBJECTS=1 環境變數,確保動態連器「載入」給定程式的過程中,印出對應的符號資訊。這也是為何 ldd 不能搭配 cross-compiler 用 —— ldd 需要仰賴 host 環境的執行時期資訊—— 於是這與執行 readelf 並解析 ELF 的行為截然不同。又,稍早`Xatierlike Lee` 提過 ASLR,這就讓載入的符號會有不同的地址 * [ldd source](https://sourceware.org/git/?p=glibc.git;a=tree;f=elf;h=0715fb250908cab481261ae3dffd9b070b69c933;hb=HEAD) * [_mi_random_ini](https://github.com/microsoft/mimalloc/blob/master/src/init.c#L150-L152) Microsoft 的 mimalloc 更是直接運用 ASLR,作為 PRNG 的種子) ### Jesse Huang ld.so裡面會先載入,接著判斷是不是trace mode(用 LD_TRACE_LOADED_OBJECTS) ,如果是trace mode就直接輸出資訊並exit,不做之後的relocation和執行程式,可以去翻翻看rtld.c 另外ldd在linux上是shell script,不過在bsd上是c program,也有source code可以看。 ### 陳冠廷 接著上面 Jesse Huang 所述,如果系統的 `ld.so` 沒有辦法載入程式,也就是程式用了自己的 loader 的話,ldd 就會設置 LD_TRACE_LOADED_OBJECTS 環境變數然後直接讓程式跑起來,所以隨便 ldd 來路不明的 ELF 可能有安全隱憂。 * [ldd arbitrary code execution](https://catonmat.net/ldd-arbitrary-code-execution) ### 李奇霖 我後來在man找到答案 Finding the vDSO The base address of the vDSO (if one exists) is passed by the kernel to each program in the initial auxiliary vector (see getauxval(3)), via the AT_SYSINFO_EHDR tag. You must not assume the vDSO is mapped at any particular location in the user's memory map. The base address will usually be randomized at run time every time a new process image is created (at execve(2) time). This is one for security reasons, to prevent "return-to-libc" ttacks. For some architectures, there is also an AT_SYSINFO tag. This is used only for locating the vsyscall entry point and is frequently omitted or set to 0 (meaning it's not available). This tag is a throwback to the initial vDSO work (see History below) and its use should be avoided. ### 參考連結 * [ld](http://man7.org/linux/man-pages/man8/ld.so.8.html) * ld 的環境變數