linux一些問題解決方法的收集
在Unix作業系統的動態連結庫的世界中,LD_PRELOAD就是這樣一個環境變數,它可以影響程式的執行時的連結(Runtime linker),它允許你定義在程式執行前優先載入的動態連結庫。
這個功能主要就是用來有選擇性的載入Unix作業系統不同動態連結庫中的相同函式。通過這個環境變數,我們可以在主程式和其動態連結庫的中間載入別的動態連結庫,甚至覆蓋正常的函式庫。一方面,我們可以以此功能來使用自己的或是更好的函式(無需別人的原始碼),而另一方面,我們也可以以向別人的程式注入惡意程式,從而達到那不可告人的罪惡的目的。
我們知道,Linux的用的都是glibc,有一個叫libc.so.6的檔案,這是幾乎所有Linux下命令的動態連結中,其中有標準C的各種函式。Unix作業系統中對於GCC而言,預設情況下,所編譯的程式中對標準C函式的連結,都是通過動態連結方式來連結libc.so.6這個函式庫的。
OK。還是讓我用一個例子來看一下用LD_PRELOAD來hack別人的程式。
Unix作業系統LD_PRELOAD示例一
我們寫下面一段例程:
- #include <stdio.h>
- #include <string.h>
- int main(int argc, char **argv)
- {
- char passwd[] = "password";
- if (argc <2) {
- printf("usage: %s <password>\n", argv[0]);
- return;
- }
-
if (!strcmp(passwd, argv[1])) {
- printf("Correct Password!\n");
- return;
- }
- printf("Invalid Password!\n");
- }
在上面這段Unix作業系統程式中,我們使用了strcmp函式來判斷兩個字串是否相等。下面,我們使用一個動態函式庫來過載strcmp函式:
- #include <stdio.h>
- #include <string.h>
- int strcmp(const char *s1, const char *s2)
- {
-
printf("hack function invoked. s1=<%s>
- return 0;
- }
編譯程式:
- $ gcc -o verifypasswd verifypasswd.c
- $ gcc -shared -o hack.so hack.c
測試一下程式:(得到正確結果)
- $ ./verifypasswd asdf
- Invalid Password!
設定LD_PRELOAD變數:(使我們重寫過的strcmp函式的hack.so成為優先載入連結庫)
$ export LD_PRELOAD="./hack.so"
再次執行程式:
- $ ./verifypasswd asdf
- hack function invoked. s1=<password>s2=<asdf>
- Correct Password!
1)我們的hack.so中的strcmp被呼叫了。
2)主程式中執行結果被影響了。
如果這是一個Unix作業系統登入程式,那麼這也就意味著我們用任意口令都可以進入Unix作業系統了。
(3) ldconfig命令詳解,linux動態連結庫http://hi.baidu.com/ostech/blog/item/d4a40b99f0846ab9c8eaf43b.html
動態連結庫管理命令 為了讓動態連結庫為系統所共享,還需執行動態連結庫的管理命令--ldconfig.此執行程式存放在/sbin目錄下. ldconfig命令的用途,主要是在預設搜尋目錄(/lib和/usr/lib)以及動態庫配置檔案/etc/ld.so.conf內所列的目錄下,搜尋出可共享的動態連結庫(格式如前介紹,lib*.so*),進而創建出動態裝入程式(ld.so)所需的連線和快取檔案.快取檔案預設為/etc/ld.so.cache,此檔案儲存已排好序的動態連結庫名字列表. ldconfig通常在系統啟動時執行,而當用戶安裝了一個新的動態連結庫時,就需要手工執行這個命令. ldconfig命令列用法如下: ldconfig [-v|--verbose] [-n] [-N] [-X] [-f CONF] [-C CACHE] [-r ROOT] [-l] [-p|--print-cache] [-c FORMAT] [--format=FORMAT] [-V] [-?|--help|--usage] path... ldconfig可用的選項說明如下: (1) -v或--verbose : 用此選項時,ldconfig將顯示正在掃描的目錄及搜尋到的動態連結庫,還有它所建立的連線的名字. (2) -n : 用此選項時,ldconfig僅掃描命令列指定的目錄,不掃描預設目錄(/lib,/usr/lib),也不掃描配置檔案/etc/ld.so.conf所列的目錄. (3) -N : 此選項指示ldconfig不重建快取檔案(/etc/ld.so.cache).若未用-X選項,ldconfig照常更新檔案的連線. (4) -X : 此選項指示ldconfig不更新檔案的連線.若未用-N選項,則快取檔案正常更新. (5) -f CONF : 此選項指定動態連結庫的配置檔案為CONF,系統預設為/etc/ld.so.conf. (6) -C CACHE : 此選項指定生成的快取檔案為CACHE,系統預設的是/etc/ld.so.cache,此檔案存放已排好序的可共享的動態連結庫的列表. (7) -r ROOT : 此選項改變應用程式的根目錄為ROOT(是呼叫chroot函式實現的).選擇此項時,系統預設的配置檔案/etc/ld.so.conf,實際對應的為ROOT/etc/ld.so.conf.如用-r /usr/zzz時,開啟配置檔案/etc/ld.so.conf時,實際開啟的是/usr/zzz/etc/ld.so.conf檔案.用此選項,可以大大增加動態連結庫管理的靈活性. (8) -l : 通常情況下,ldconfig搜尋動態連結庫時將自動建立動態連結庫的連線.選擇此項時,將進入專家模式,需要手工設定連線.一般使用者不用此項. (9) -p或--print-cache : 此選項指示ldconfig打印出當前快取檔案所儲存的所有共享庫的名字. (10) -c FORMAT 或 --format=FORMAT : 此選項用於指定快取檔案所使用的格式,共有三種:old(老格式),new(新格式)和compat(相容格式,此為預設格式). (11) -V : 此選項打印出ldconfig的版本資訊,而後退出. (12) -? 或 --help 或 --usage : 這三個選項作用相同,都是讓ldconfig打印出其幫助資訊,而後退出. 舉三個例子: 例1: # ldconfig -p 793 libs found in cache `/etc/ld.so.cache' libzvt.so.2 (libc6) =>; /usr/lib/libzvt.so.2 libzvt.so (libc6) =>; /usr/lib/libzvt.so libz.so.1.1.3 (libc6) =>; /usr/lib/libz.so.1.1.3 libz.so.1 (libc6) =>; /lib/libz.so.1 ...... # 注: 有時候使用者想知道系統中有哪些動態連結庫,或者想知道系統中有沒有某個動態連結庫,這時,可用-p選項讓ldconfig輸出快取檔案中的動態連結庫列表,從而查詢得到.例子中,ldconfig命令的輸出結果第1行表明在快取檔案/etc/ld.so.cache中找到793個共享庫,第2行開始便是一系列共享庫的名字及其全名(絕對路徑).因為實際輸出結果太多,為節省篇幅,以......表示省略的部分. 例2: # ldconfig -v /lib: liby.so.1 ->; liby.so.1 libnss_wins.so ->; libnss_wins.so ...... /usr/lib: libjscript.so.2 ->; libjscript.so.2.0.0 libkspell.so.2 ->; libkspell.so.2.0.0 ...... /usr/X11R6/lib: libmej-0.8.10.so ->; libmej-0.8.10.so libXaw3d.so.7 ->; libXaw3d.so.7.0 ...... # 注: ldconfig命令在執行正常的情況下,預設不輸出什麼東西.本例中用了-v選項,以使ldconfig在執行時輸出正在掃描的目錄及搜尋到的共享庫,使用者可以清楚地看到執行的結果.執行結束後,ldconfig將重新整理快取檔案/etc/ld.so.cache. 例3: # ldconfig /usr/zhsoft/lib |
首先ldd不是一個可執行程式,而只是一個shell指令碼
2、ldd能夠顯示可執行模組的dependency,其原理是通過設定一系列的環境變數,如下:LD_TRACE_LOADED_OBJECTS、LD_WARN、LD_BIND_NOW、LD_LIBRARY_VERSION、LD_VERBOSE等。當LD_TRACE_LOADED_OBJECTS環境變數不為空時,任何可執行程式在執行時,它都會只顯示模組的dependency,而程式並不真正執行。要不你可以在shell終端測試一下,如下:
(1) export LD_TRACE_LOADED_OBJECTS=1
(2) 再執行任何的程式,如ls等,看看程式的執行結果
3、ldd顯示可執行模組的dependency的工作原理,其實質是通過ld-linux.so(elf動態庫的裝載
器)來實現的。我們知道,ld-linux.so模組會先於executable模組程式工作,並獲得控制權,因此當上述的那些環境變數被設定時,ld-linux.so選擇了顯示可執行模組的dependency。
4、實際上可以直接執行ld-linux.so模組,如:/lib/ld-linux.so.2 --list program(這相當於ldd program)
ldd命令使用方法(摘自ldd --help)
名稱 ldd - 列印共享庫的依賴關係
大綱 ldd [選項]... 檔案...
描述 ldd 輸出在命令列上指定的每個程式或共享庫需要的共享庫。
選項
--version
列印ldd的版本號
-v --verbose
列印所有資訊,例如包括符號的版本資訊
-d --data-relocs
執行符號重部署,並報告缺少的目標物件(只對ELF格式適用)
-r --function-relocs
對目標物件和函式執行重新部署,並報告缺少的目標物件和函式(只對ELF格式適用)
--help 用法資訊
注意:
ldd的標準版本與glibc2一起提供。Libc5與老版本以前提供,在一些系統中還存在。在libc5版本中長選項不支援。另一方面,glibc2版本不支援-V選項,只提供等價的--version選項。
如果命令列中給定的庫名字包含'/',這個程式的libc5版本將使用它作為庫名字;否則它將在標準位置搜尋庫。執行一個當前目錄下的共享庫,加字首"./"。
錯誤:
ldd不能工作在a.out格式的共享庫上。
ldd不能工作在一些非常老的a.out程式上,這些程式在支援ldd的編譯器發行前已經建立。如果你在這種型別的程式上使用ldd,程式將嘗試argc = 0的執行方式,其結果不可預知。
(5)LD_LIBRARY_PATHhttp://skatings.blogbus.com/logs/50437681.html
Linux 執行的時候,是如何管理共享庫(*.so)的?在 Linux 下面,共享庫的尋找和載入是由 /lib/ld.so 實現的。 ld.so 在標準路經(/lib, /usr/lib) 中尋找應用程式用到的共享庫。
但是,如果需要用到的共享庫在非標準路經,ld.so 怎麼找到它呢?
目前,Linux 通用的做法是將非標準路經加入 /etc/ld.so.conf,然後執行 ldconfig 生成 /etc/ld.so.cache。 ld.so 載入共享庫的時候,會從 ld.so.cache 查詢。
傳統上,Linux 的先輩 Unix 還有一個環境變數:LD_LIBRARY_PATH 來處理非標準路經的共享庫。ld.so 載入共享庫的時候,也會查詢這個變數所設定的路經。
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./lib
export LD_LIBRARY_PATH
但是,有不少聲音主張要避免使用 LD_LIBRARY_PATH 變數,尤其是作為全域性變數。這些聲音是:
* LD_LIBRARY_PATH is not the answer - http://prefetch.net/articles/linkers.badldlibrary.html
* Why LD_LIBRARY_PATH is bad - http://xahlee.org/UnixResource_dir/_/ldpath.html
* LD_LIBRARY_PATH - just say no - http://blogs.sun.com/rie/date/20040710
解決這一問題的另一方法是在編譯的時候通過 -R<path> 選項指定 run-time path。
1. 往/lib和/usr/lib裡面加東西,是不用修改/etc/ld.so.conf的,但是完了之後要調一下ldconfig,不然這個library會找不到
2. 想往上面兩個目錄以外加東西的時候,一定要修改/etc/ld.so.conf,然後再呼叫ldconfig,不然也會找不到。
比如安裝了一個mysql到/usr/local/mysql,mysql有一大堆library在/usr/local/mysql/lib下面,這時就需要在/etc/ld.so.conf下面加一行/usr/local/mysql/lib,儲存過後ldconfig一下,新的library才能在程式執行時被找到。
3. 如果想在這兩個目錄以外放lib,但是又不想在/etc/ld.so.conf中加東西(或者是沒有許可權加東西)。那也可以,就是export一個全域性變數LD_LIBRARY_PATH,然後執行程式的時候就會去這個目錄中找library。一般來講這只是一種臨時的解決方案,在沒有許可權或臨時需要的時候使用。
4. ldconfig做的這些東西都與執行程式時有關,跟編譯時一點關係都沒有。編譯的時候還是該加-L就得加,不要混淆了。
5. 總之,就是不管做了什麼關於library的變動後,最好都ldconfig一下,不然會出現一些意想不到的結果。不會花太多的時間,但是會省很多的事。
LD_LIBRARY_PATH 這個環境變數是大家最為熟悉的,它告訴loader:在哪些目錄中可以找到共享庫。可以設定多個搜尋目錄,這些目錄之間用冒號分隔開。在linux下,還提供了另外一種方式來完成同樣的功能,你可以把這些目錄加到/etc/ld.so.conf中,然後呼叫ldconfig。當然,這是系統範圍內全域性有效的,而環境變數只對當前shell有效。按照慣例,除非你用上述方式指明,loader是不會在當前目錄下去找共享庫的,正如shell不會在當前目前找可執行檔案一樣。
================================================================================================
在shell下嘗試設定LD_LIBRARY_PATH,以下面這種形式設定,老是報錯bash: LD_LIBRARY_PATH: command not found,
LD_LIBRARY_PATH=/usr/local/lib
LD_LIBRARY_PATH = $ LD_LIBRARY_PATH:/usr/local/lib
可能是因為系統之前沒有設定過LD_LIBRARY_PATH,於是改成這樣:
export LD_LIBRARY_PATH=/usr/local/lib
然後用 echo $LD_LIBRARY_PATH檢查一下是否真的設定成功,發現可以。
接著在該shell下執行eclipse生成的可執行檔案,沒有錯誤。
另外,如果不想每次新啟一個shell都設定LD_LIBRARY_PATH,可以編輯~/.bash_profile檔案:
$ vi ~/.bash_profile
新增:
LD_LIBRARY_PATH=/usr/local/lib
export LD_LIBRARY_PATH
這兩行,完成之後.bash_profile如下所示:
# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
# User specific environment and startup programs
PATH=$PATH:$HOME/bin
LD_LIBRARY_PATH=/usr/local/lib
export PATH
export LD_LIBRARY_PATH
然後執行 $ source ~/.bash_profile 就行了。但是這種方法只能用在shell下,想在eclipse裡面執行,還是不行:
嘗試了eclipse專案properties裡面的各種設定都不起作用。
用“eclipse LD_LIBRARY_PATH”作為關鍵字(可見關鍵字多麼重要)才搜到這麼篇文章 《eclipse+cdt+gcc編譯選項控制》 http://hi.baidu.com/zsffei/blog/item/7b17c043ceb51e1772f05de1.html
才知道應該在eclipse的專案屬性-->C/C++ Build-->Settings-->Tool settings-->GCC C++ Linker-->Miscellaneous的Other options (-Xlinker [option])新增 -R/usr/local/lib
(6) 動態連結庫的搜尋優先順序
http://hi.baidu.com/ilonng/blog/item/f851a0e9048d1231b80e2db8.html
http://blog.csdn.net/gogdizzy/article/details/6591267
首先回答前面的問題,一共有多少種方法來指定告訴linux共享庫連結器ld.so已經編譯好的庫libbase.so的位置呢?答案是一共有五種,它們都可以通知ld.so去哪些地方找下已經編譯好的c語言函式動態庫,它們是: 那麼,動態連結器ld.so在這五種路徑中,是按照什麼樣的順序來搜尋需要的動態共享庫呢?答案這裡先告知就是按照上面的順序來得,即優先順序是:1-->2-->3-->4-->5。我們可以寫簡單的程式來證明這個結論。
首先,寫成5個函式,這5個函式名稱都叫pt,但是裡面的內容不一樣:
pt2.c
pt3.c
pt4.c
pt5.c
然後,分別編譯這5個函式,然後將它們分別移到上面5種情況對應的5個不同目錄下:
gcc -fPIC -c pt2.c -o pt.o
gcc -fPIC -c pt3.c -o pt.o
gcc -fPIC -c pt4.c -o pt.o
gcc -fPIC -c pt5.c -o pt.o
再次,編寫一個main函式m,讓它來呼叫函式pt:
int main(){
最後,準備環境,讓ld都知道這5個路徑:
之後測試:
gcc m.c -o m -L/tmp/st/1 -lpt
mv /tmp/st/2/libpt.so /tmp/st/2/libpt2.so
mv /tmp/st/3/libpt.so /tmp/st/3/libpt3.so
rm /lib/libpt.so 故證明這五種路徑指定方法的優先順序是1-->2-->3-->4-->5! |