• <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, 評論 - 101, 引用 - 0
            數據加載中……

            360注冊表操作原理分析

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

            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 
            里面的函數HvpGetCellMapped進行hook,在而后分別對哪兩個打開的注冊表項進行若干的注冊表操作(調用前面獲取的NtRegxxx來完成),那么360為什么要hook那個函數膩,原因是,NtRegxxxx的若干操作會調用相應的CmRegxxxx來完成,而CmRegxxx又會調用HvpGetCellMapped這個函數,Fake_HvpGetCellMapped的作用很簡單: 
            假設對那個自己的測試鍵值調用了NtSetValueKey,在找個函數里可以找到如下代碼: 
            if (NT_SUCCESS(status)) 

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


            這里就是和諧的CmSetValueKey,當這個東西運行,在這個函數里可以找到調用了HvpGetCellMapped這個函數,然而現在卻調用了Fake_HvpGetCellMapped這個函數,找個函數里面通過棧回溯機制,定位到上面那個圖的代碼對應的 
            CALL CmpSetValueKey,(棧回溯是通過找個函數的第一個參數 
            作為回溯查找對象的,前面打開自己的注冊表時保存了找個值,這樣就確定了CmRegxxxx的地址。 

            后面還有一段用于校驗CmRegxxxx是否被inline hook的,就不說了。 

            360操作注冊表

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

            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 
            里面的函數HvpGetCellMapped進行hook,在而后分別對哪兩個打開的注冊表項進行若干的注冊表操作(調用前面獲取的NtRegxxx來完成),那么360為什么要hook那個函數膩,原因是,NtRegxxxx的若干操作會調用相應的CmRegxxxx來完成,而CmRegxxx又會調用HvpGetCellMapped這個函數,Fake_HvpGetCellMapped的作用很簡單: 
            假設對那個自己的測試鍵值調用了NtSetValueKey,在找個函數里可以找到如下代碼: 
            if (NT_SUCCESS(status)) 

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


            這里就是和諧的CmSetValueKey,當這個東西運行,在這個函數里可以找到調用了HvpGetCellMapped這個函數,然而現在卻調用了Fake_HvpGetCellMapped這個函數,找個函數里面通過棧回溯機制,定位到上面那個圖的代碼對應的 
            CALL CmpSetValueKey,(棧回溯是通過找個函數的第一個參數 
            作為回溯查找對象的,前面打開自己的注冊表時保存了找個值,這樣就確定了CmRegxxxx的地址。 
            后面還有一段用于校驗CmRegxxxx是否被inline hook的,就不說了。 

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

            寬帶撥號連接密碼恢復原理

             

            技術可以通過努力學習而有所成就,但攻防的思維方式即使苦心鉆研往往也無大的收獲,就得多多借鑒高手的經驗了……

             

             編者按:在讀者來信中,經常有朋友詢問如何做一名成功的黑客或安全專家,如何才能找到好的安全技術學習方法。其實,除了掌握一些必備的基礎知識和工具外,還要懂得編程等技術;另外攻防思路的養成和培訓也是很重要的。因為技術可以通過努力學習而有所成就,但攻防的思維方式即使苦心鉆研往往也無大的收獲,就得多多借鑒高手的經驗了。在本文中,作者通過對NT平臺撥號連接密碼恢復原理的研究,層層索引,步步入微的思維方法就值得推薦。

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

              Dialupass星號密碼顯示之謎

              看上去,Dialupass是非普通的星號密碼顯示工具,那它的原理是什么呢?上Google查了一番,沒找到相關資料。一生氣便抄起家伙——Windbg,準備把它大卸八塊。郁悶的是,用Windbg加載后,密碼就不能還原出來了,顯示的是星號。換替補Ollydbg上場,情況依舊。怪了,莫非這小工具有Anti-Debug功能?當時只是一絲懷疑,因為實在不相信這樣的小工具作者會花心思來保護。

              小知識:

              Windbg工具:

              Windbg是微軟開發的免費源碼級調試工具。可以用于Kernel模式調試和用戶模式調試,還可以調試Dump文件。

              Anti-Debug技術:

              Anti-Debug,即反跟蹤技術。防止 Cracker 用 SoftICE 之類的調試器動態跟蹤,分析軟件。反跟蹤技術一般是具有針對性的,即針對某種調試器的反跟蹤,而不能防止所有的調試器跟蹤。

              在用S-ICE跟蹤的過程中,發現有這么一個調用:GetProcAddress(xx, “IsDebugPresent”)。原來真的有Anti-Debug功能,好在比較簡單。統計了一下,總共有五處進行了Anti-Debug檢查。

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

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

              1. 關鍵函數不直接調用,而是用LoadLibraryA和GetProcAddress來獲取函數地址后再CALL;
              2. 函數名是經過編碼的,反匯編后看字符串是看不到的;
              3. 關鍵地方一概用花指令來迷惑你和反匯編軟件。

             其實原理很簡單,就是用rasapi32.dll里面的一些函數來獲取撥號連接的一些信息,再用 ADVAPI32!LsaRetrievePrivateData 函數來獲取密碼。


             

              關鍵字:LsaRetrievePrivateData和RasDialParams

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

              小提示:

              參考資源①和②是俄羅斯人公布的演示代碼,沒有對LsaRetrievePrivateData返回的數據進行拆分用戶名和密碼。參考資源③是日本人公布的完整的應用程序的代碼,可惜在對LsaRetrievePrivateData返回的數據進行拆分處理時存在BUG,導致有些情況下用戶名和密碼取得不正確。

              ①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/

              后來發現Lsadump2 DUMP出來的數據里面包含了“LsaRetrievePrivateData”返回的數據。Lsadump2的原理大致如下:

              1.插入一個線程到Lsass.exe進程;
              2.打開LSA Policy database;
              3.從注冊表“HKLM\SECURITY\Policy\Secrets”中枚舉子鍵;
              4.LsarOpenSecret;
              5.LsarQuerySecret。

              進一步跟蹤后發現,其實ADVAPI32!LsaRetrievePrivateData是通過NdrClientCall2發送RPC調用到Lsass.exe進程,Lsass.exe里面再調用LsarOpenSecret、LsarQuerySecret來完成獲取撥號連接信息過程的(LsarOpenSecret里面有權限判斷,非Admin組用戶是沒有權限來調用ADVAPI32!LsaRetrievePrivateData的)。

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

              SID對應的是用戶的String SID (“HKLM\SECURITY”這個鍵只有System有權限讀寫)。

              LsarQuerySecret從注冊表中讀取出來數據后,接著調用LsapCrDecryptValue函數來解密,對于同一臺機器來說,解密時用的KEY始終都是固定的,這個KEY在lsasrv.dll里面的變量名為_LsapDbSecretCipherKey。在Windows 2003里面,變量名不一樣,對應的有兩個,分別為LsapDbSecretCipherKeyWrite和LsapDbSecretCipherKeyRead,但這兩個變量里面的數據是一樣的。

              LsapCrDecryptValue用的似乎是標準DES算法,解密時主要流程如下:

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

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

             

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

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

              而在調用ADVAPI32!LsaRetrievePrivateData時,LsarOpenSecret返回的Handle偏移0x45處值為0x0,所以LsarQuerySecret返回的是解密后又加密的數據,所以在ADVAPI32!LsaRetrievePrivateData里面還有一個對應的解密過程。相應地,LsapCrEncryptValue加密的主要流程如下:
              lsasrv!LsapCrEncryptValue→advapi32!SystemFunction004→advapi32!EncryptDataLength→advapi32!SystemFunction001→advapi32!DES_ECB_LM→advapi32!des

              _LsapDbSecretCipherKey是如何產生的?

              開始我以為在同一版本的Windows里面,_LsapDbSecretCipherKey是固定的,后來發現我錯了。那么這個


            _LsapDbSecretCipherKey是如何產生的?流程如下:

              1.調用ntdll!NtConnectPort打開 L“\Security\WxApiPort”;
              2.調用ntdll!NtRequestWaitReplyPort得到一些數據;
              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字節數據COPY到lsasrv.dll里面的_LsapDbSysKey變量。

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

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

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

              4.從注冊表“HKLM\SECURITY\Policy\PolSecretEncryptionKey”中讀取出來一段數據,調用函數_LsapDbDecryptKeyWithSyskey,把它用_LsapDbSysKey來解密,_LsapDbSecretCipherKey就在解密完后的數據里面(LsapDbDecryptKeyWithSyskey函數做的其實就是MD5和RC4運算)。

              從注冊表中獲取撥號連接密碼

              了解原理后,我們就可以直接從注冊表里面來獲取撥號連接中的密碼等數據了。但有幾個問題需要解決:

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

              2.催化劑:如何獲取_LsapDbSysKey解密用的函數,_LsapDbDecryptKeyWithSyskey為非導出函數。可以用Flashsky的代碼來獲取SYSKEY,利用公開的MD5和RC4庫函數來解密。

              直接從Lsass.exe進程里面搜索_LsapDbSecretCipherKey,它的結構如下:

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

              pbData指向存儲KEY的地址,KEY長度固定為0x10字節,即cbData和cbMaxData都是固定為0x10。所以從Lsass進程的空間里面搜索“\x10\x00\x00\x00\x10\x00\x00\x00”即可找到正確的KEY。結果可能會有多個,可以把所有搜索到的KEY都試一下,總有一個正確的。

              3.工具:解密函數LsapCrDecryptValue為非導出函數,怎么辦?或許可以根據特征碼來搜索,但總覺得不太可靠。幸好,LsapCrDecryptValue調用的advapi32!SystemFunction005是導出函數。或者直接利用公開的DES庫函數,自己來運算。

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

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

            SAM的散列存儲加密解密算法以及SYSKEY的計算

            SAM的散列存儲加密解密算法以及SYSKEY的計算


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

            SAM的散列存儲加密解密算法以及SYSKEY的計算
            轉摘請注明作者和安全焦點
            作者:FLASHSKY
            SITE:WWW.XFOCUS.NETWWW.SHOPSKY.COM
            郵件:flashsky@xfocus.org
            作者單位:啟明星辰積極防御實驗室

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

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

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


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

                下面就是給出的完全實現計算SYSKEY-》sampsecretsessionkey,對特定用戶的SAM中加密的密碼散列再解密的代碼:當然如果從運行系統中解密散列也可以直接讀取sampsecretsessionkey,就象PWDUMP3那樣做的一樣也是可以的,但是如果要實現離線的就還需要再分析更多的東西。
                另外關于PWDUMP具體的方法,由于其是加密和反跟蹤的,我沒時間做仔細調式分析,但是從REGMON監視其注冊表操作的過程來說,是沒有去讀LSA下的任何東西的,因此可以斷定他是直接獲得ampsecretsessionkey來進行對SAM內容的解密到散列的。

            //下面幾個函數的實現,DES相關的盒,ECB等的定義參考我以前發的文章 中的代碼,這里不再給出
            //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函數的實現沒有解密的部分,并且有個小錯誤,因此這里再給出完整的一個

            #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;
                //下面為了簡單,這3個是直接指定的用戶,相對SID的注冊表鍵名和相對SID,大家也可以寫成完全根據用戶名獲取的方式
                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中的鍵讀取先要提升自己成為LOCASYSTEM權限,這里并沒有實現,自己增加其中的代碼
                //讀出F鍵中用于計算
                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中的結構,我們就直接讀最后的那一串,一般都是如此存放在此
                //但也不排除例外,那就需要具體分析V中的結構來計算了
                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);
                
                //計算SYSKEY,W2K系統默認SYSKEY使用,且成為其固定的一個組件
                getsyskey(syskey);
                //利用SYSKEY,F鍵中的KEY計算sampsecretsessionkey
                getsampsecretsessionkey(syskey,fkey);
                memcpy(sampsecretsessionkey,fkey+0x10,0x10);
                //上面的就是系統引導時完成的工作,這個sampsecretsessionkey固定保存在LSASS的進程空間內
                //當認證等或如PWDUMP3工作時候,就是先從系統中直接讀sampsecretsessionkey再進行處理

                //根據具體用戶的相對SID,要恢復散列的散列類型,生成SESSIONKEY,如下面就是針對LM散列的
                getsamkey(sampsecretsessionkey,&uid,passwordtype[0],sessionkey);
                memcpy(lmhash,ensamlm,0x10);
                //利用SESSIONKEY,SAM中加密的LM散列,相對SID做RC4/DES解密獲得真正的散列
                getsamhash(lmhash,sessionkey,&uid);
                printf("HASH::");
                for(i=0;i<0x10;i++)
                    printf("%02x",lmhash[i]);
                printf(":");
                //根據具體用戶的相對SID,要恢復散列的散列類型,生成SESSIONKEY,如下面就是針對NTLM散列的
                getsamkey(sampsecretsessionkey,&uid,passwordtype[1],sessionkey);
                memcpy(nthash,ensamnt,0x10);
                //利用SESSIONKEY,SAM中加密的NTLM散列,相對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散列,相對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)
            {
                //根據具體用戶的相對SID,要恢復散列的散列類型,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個鍵的CLASS值組合起來做換位就是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字節就是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 閱讀(395) | 評論 (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 閱讀(299) | 評論 (0)編輯 收藏

            free

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

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

            ping網段并記錄

            :ping網段并記錄
            @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                              請選擇你所掃描的類型
            echo                         ----------------------------- 
            echo . 
            echo                  [A] :IP段掃描           [B] :精確IP及端口掃描
            :x
            set /p choice="請輸入掃描類型:A or B :"
            if /i %choice%==a goto ipduan else goto y
            :y
            if /i %choice%==b goto ip else goto x
            :ipduan
            echo 進入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 進入精確掃描模式:
            set /p ipa="輸入掃描的ip:(例如:192.168.0.1)"
            set /p port1="輸入要掃描的開始端口:"
            set /p port2="輸入要掃描的結束端口:"
            for /l %%i in (%port1%,1,%port2%) do (telnet %ipa% %%i)
            pause>nul 

            誰能幫我找個工具或者做個 .bat代碼實現以下需求 我要批量PING 服務器

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

            我并非問的是同一網段的IP哦,不是掃描軟件那種 192.168.0.1 - 192.168.0.255 這樣的,而是我有很多根本就完全不同的IP地址批量PING 沒規則 只能導入文件給IP表

            問題補充:

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

            test.bat的內容
            @echo off
            echo 正在ping網址,請稍候……
            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 閱讀(345) | 評論 (0)編輯 收藏

            Bat文件修改環境變量 創建桌面快捷方式

            Bat文件修改環境變量

            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

            創建桌面快捷方式

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

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

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

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


            當Holz還在 UNC(北卡羅來納大學)攻讀數學 Phd、研究流體力學時,發現用鼠標和鍵盤來創建、控制 3D 模型非常麻煩,因為會有許多不必要的復雜操作(點擊、下拉菜單等)。于是他就希望創造一種「能讓虛擬建模和現實建模一樣方便」的技術。在 4 年的研究和無數的硬件更迭后,Leap 終于誕生了。

            kinect面世后,leap motion的出現成為人機交互的一大熱點。這個通過紅外 LED 和攝像頭以不同于其他運動控制技術的方式來完成對手指的追蹤,其有效精確度達到了0.01mm。同時,這種全新的動作感應方式也使得Leap 造價低廉、體積小巧的原因,通過其提供的兩個U盤大小的程序,可以形成一個8立方英尺的感應空間,用戶在這個空間內可以任意活動,系統會準確追蹤。更詳細的內容,看看癮科技的介紹吧!

            leap motion官網目前可以預定,鏈接在此;售價$69.99,運費$5.99。收貨地址可以選擇中國。不知道到選擇中國會不會發貨。

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

            linux下so動態庫一些不為人知的秘密(中) 2

             繼續上一篇《 linux下so動態庫一些不為人知的秘密(中) 》介紹so搜索路徑,還有一個類似于-path,叫LD_RUN_PATH環境變量, 它也是把路徑編譯進可執行文件內,不同的是它只設置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/]

            另外還可以通過配置/etc/ld.so.conf,在其中加入一行
            /tmp/
            這個配置項也是只對運行期有效,并且是全局用戶都生效,需要root權限修改,修改完后需要使用命令ldconfig /etc/ld.so.conf 加載到ld.so.cache中,避免重啟系統就可以立即生效。
            除了前面介紹的那些搜索路徑外,還有缺省搜索路徑/usr/lib/ /lib/ 目錄,可以通過-z nodefaultlib編譯選項禁止搜索缺省路徑。
            [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

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

            查看一個程序搜索其各個動態庫另一個簡單的辦法是使用 LD_DEBUG這個環境變量;
            [stevenrao] $ export LD_DEBUG=libs
            [stevenrao] $ ./demo
            下一篇介紹動態庫內符號問題

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

            linux下so動態庫一些不為人知的秘密2

             上一篇(linuxso動態庫一些不為人知的秘密(上))介紹了linuxso一些依賴問題,本篇將介紹linuxso路徑搜索問題。

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

            既然顯示加載這么多優點,那么為什么實際生產中很少碼農使用它呢, 主要原因還是起使用不是很方便,需要開發人員多寫不少代碼。所以不被大多數碼農使用,還有一個重要原因應該是能提前發現錯誤,在部署的時候就能發現缺少哪些so,而不是等到實際上限運行的時候才發現缺東少西。

            下面舉個工作中最常碰到的問題,來引申出本篇內容吧。

            寫一個最簡單的so tmp.cpp

            1. int test()

            2. {

            3. return 20;

            4. }

            編譯=>鏈接=》運行, 下面main.cpp 內容請參見上一篇文章。

            [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

            這個錯誤是最常見的錯誤了。運行程序的時候找不到依賴的so。一般人使用方法是修改LD_LIBRARY_PATH這個環境變量

            export LD_LIBRARY_PATH=/tmp

            [stevenrao]$ ./demo

            test

            這樣就OK, 不過這樣export 只對當前shell有效,當另開一個shell時候,又要重新設置。可以把export LD_LIBRARY_PATH=/tmp 語句寫到 ~/.bashrc中,這樣就對當前用戶有效了,寫到/etc/bashrc中就對所有用戶有效了。

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

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

            [stevenrao]$ ./demo

            test

            [stevenrao]$ ldd demo

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

            /tmp/libtmp.so (0x00007f53ed30f000)

            絕對路徑雖然申請設置環境變量步驟,但是缺陷也是致命的,這個so必須放在絕對路徑下,不能放到其他地方,這樣給部署帶來很大麻煩。所以應該禁止使用絕對路徑鏈接so

            搜索路徑分兩種,一種是鏈接時候的搜索路徑,一種是運行時期的搜索路徑。像前面提到的 -L/tmp/ 是屬于鏈接時期的搜索路徑,即給ld程序提供的編譯鏈接時候尋找動態庫路徑;而 LD_LIBRARY_PATH則既屬于鏈接期搜索路徑,又屬于運行時期的搜索路徑。

            這里需要介紹鏈-rpath鏈接選項,它是指定運行時候都使用的搜索路徑。聰明的同學馬上就想到,運行時搜索路徑,那它記錄在哪兒呢。也像. LD_LIBRARY_PATH那樣,每部署一臺機器就需要配一下嗎。呵呵,不需要..,因為它已經被硬編碼到可執行文件內部了。看看下面演示

            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文件內部了,路徑和程序深深的耦合到一起

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

            linux下so動態庫一些不為人知的秘密(上) (2012-08-14 22:48)



            linux 下有動態庫和靜態庫,動態庫以.so為擴展名,靜態庫以.a為擴展名。二者都使用廣泛。本文主要講動態庫方面知識。

            基本上每一個linux 程序都至少會有一個動態庫,查看某個程序使用了那些動態庫,使用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都是需要使用的,下面舉個例子
            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. }
            使用缺省參數編譯結果
            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,又是什么情況呢,下面我加入鏈接壓縮庫,數學庫,線程庫
            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)
            看看,雖然沒有用到,但是一樣有鏈接進來,那看看程序啟動時候有沒有去加載它們呢
            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. ...
            看,有加載,所以必定會影響進程啟動速度,所以我們最后不要把無用的so編譯進來,這里會有什么影響呢?
            大家知不知道linux從程序(program或對象)變成進程(process或進程),要經過哪些步驟呢,這里如果詳細的說,估計要另開一篇文章。簡單的說分三步:
            1、fork進程,在內核創建進程相關內核項,加載進程可執行文件;
            2、查找依賴的so,一一加載映射虛擬地址
            3、初始化程序變量。
            可以看到,第二步中dll依賴越多,進程啟動越慢,并且發布程序的時候,這些鏈接但沒有使用的so,同樣要一起跟著發布,否則進程啟動時候,會失敗,找不到對應的so。所以我們不能像上面那樣,把一些毫無意義的so鏈接進來,浪費資源。但是開發人員寫makefile 一般有沒有那么細心,圖省事方便,那么有什么好的辦法呢。繼續看下去,下面會給你解決方法。
            使用 ldd -u demo 查看不需要鏈接的so,看下面,一面了然,無用的so全部暴露出來了吧
            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 編譯選項
            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:

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

             

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

            Linux下合并多個.a庫函數 到動態庫so

            Linux下合并多個.a庫函數 到動態庫so

            在LINUX下用靜態庫進行鏈接的時候經常會碰到如下情況:存在與靜態庫名一樣的動態庫。以MKL為例,在目錄/MKLROOT/lib/em64t下,庫libmkl_core.a和庫libmkl_core.so同名。這樣的話我們如果在鏈接時加入鏈接的庫名-lmkl_core,編譯器只同動態庫libmkl_core.so鏈接,而屏避掉靜態庫文件libmkl_core.a。所以靜態庫文件里的對象文件無法被鏈接。解決這一問題有兩個辦法:

            1. 直接將靜態庫的絕對路徑加到編譯過程中去:

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

            這樣的話編譯器就會根據指定的文件進行編譯鏈接,不過這種做法在靜態庫文件少的時候可用,如果文件一多,就會讓整個指令冗長,可讀性差。下面是一種比較可取的方法。

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

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

            (GROUP一定要大寫)

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

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

            上面的選項(-lmkl)就相當于讓編譯器gcc到文本文件libmkl.a指定的靜態庫文件中尋找.o文件進行鏈接,而不用人工地將每個靜態庫地址都輸進行。查找.o對象文件的順序從左到右,所以應該將最低層的靜態庫放到最右邊,把需要調用右邊庫里的對象的庫放到左邊,否則會出現找不到對象文件,導致報函數沒定義的錯誤。

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

            關于Unix靜態庫和動態庫的分析

            關于Unix靜態庫和動態庫的分析

            基本概念

            庫有動態與靜態兩種,動態通常用.so為后綴,靜態用.a為后綴。 例如:libhello.so libhello.a

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

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

            1、使用庫

            當要使用靜態的程序庫時,連接器會找出程序所需的函數,然后將它們拷貝到執行文件,由于這種拷貝是完整的,所以一旦連接成功,靜態程序庫也就不再需要了。 然而,對動態庫而言,就不是這樣。動態庫會在執行程序內留下一個標記指明當程序執行時,首先必須載入這個庫。由于動態庫節省空間,linux下進行連接的 缺省操作是首先連接動態庫,也就是說,如果同時存在靜態和動態庫,不特別指定的話,將與動態庫相連接。

            現在假設有一個叫hello的程序開發包,它提供一個靜態庫libhello.a 一個動態庫libhello.so,一個頭文件hello.h,頭文件中提供sayhello()這個函數

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

            另外還有一些說明文檔。

            這一個典型的程序開發包結構 與動態庫連接 linux默認的就是與動態庫連接,下面這段程序testlib.c使用hello庫中的sayhello()函數

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

            使用如下命令進行編譯

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

            用如下命令連接:

            $gcc testlib.o -lhello -o testlib

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

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

            注:這個特別的”-WI,-Bstatic”參數,實際上是傳給了連接器ld. 指示它與靜態庫連接,如果系統中只有靜態庫當然就不需要這個參數了。如果要和多個庫相連接,而每個庫的連接方式不一樣,比如上面的程序既要和 libhello進行靜態連接,又要和libbye進行動態連接,其命令應為:

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

            2、動態庫的路徑問題

            為了讓執行程序順利找到動態庫,有三種方法:

            1. 把庫拷貝到/usr/lib和/lib目錄下。
            2. 在LD_LIBRARY_PATH環境變量中加上庫所在路徑。例如動態庫 libhello.so在/home/ting/lib目錄下,以bash為例,使用命令: $export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ting/lib
            3. 修改/etc/ld.so.conf文件,把庫所在的路徑加到文件末尾,并執行ldconfig刷新。這樣,加入的目錄下的所有庫文件都可見。

            3、查看庫中的符號

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

            $nm libhello.so | grep printf U

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

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

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

            4、生成庫

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

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

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

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

            1.連接成靜態庫 連接成靜態庫使用ar工具,其實ar是archive的意思

            $ar cqs libhello.a hello.o

            2.連接成動態庫 生成動態庫用gcc來完成,由于可能存在多個版本,因此通常指定版本號:

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

            另外再建立兩個符號連接:

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

            這樣一個libhello的動態連接庫就生成了。最重要的是傳gcc -shared 參數使其生成是動態庫而不是普通執行程序。 -Wl 表示后面的參數也就是-soname,libhello.so.1直接傳給連接器ld進行處理。實際上,每一個庫都有一個soname,當連接器發現它正 在查找的程序庫中有這樣一個名稱,連接器便會將soname嵌入連結中的二進制文件內,而不是它正在運行的實際文件名,在程序執行期間,程序會查找擁有 soname名字的文件,而不是庫的文件名,換句話說,soname是庫的區分標志。這樣做的目的主要是允許系統中多個版本的庫文件共存,習慣上在命名庫 文件的時候通常與soname相同 libxxxx.so.major.minor 其中,xxxx是庫的名字,major是主版本號,minor 是次版本號

            總結

            通過對LINUX庫工作的分析,我們已經可以理解程序運行時如何去別的地方尋找“庫”。

            附上針對這個工程的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 $@

            附上文件的目錄結構:

            |– 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


            說來也巧了,今天剛好在twitter上看到某大牛的推:

             

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


            延伸閱讀:

             

            1. linux動態鏈接庫的使用;
            2. linux 下鏈接庫的生成使用;

            本文來源: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. 關于Unix靜態庫和動態庫的分析基本概念 | EvilCode 邪惡代碼

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

            Linux下靜態庫_庫的基本概念;如何生成靜態庫動態庫;nm查看庫中包含那些函數、ar生成靜態庫,查看庫中包含那些.o文件、ldd查看程序依賴的.so文件;gcc/g++與庫相關的參數-L,-l,-fPIC,-shared;靜態庫鏈接時搜索過程;動態庫鏈接時,加載時搜索的過程;動態庫找不到的問題;動態庫升級步驟

            Linux下靜態庫_庫的基本概念;如何生成靜態庫動態庫;nm查看庫中包含那些函數、ar生成靜態庫,查看庫中包含那些.o文件、ldd查看程序依賴的.so文件;gcc/g++與庫相關的參數-L,-l,-fPIC,-shared;靜態庫鏈接時搜索過程;動態庫鏈接時,加載時搜索的過程;動態庫找不到的問題;動態庫升級步驟

            2010-11-23 10:47:45| 分類: Linux系統編程 | 標簽: |字號 訂閱

            一、基本概念

            1.1、什么是庫

            在 windows 平臺和 linux 平臺下都大量存在著庫。

            本質上來說庫是 一種可執行代碼的二進制形式,可以被操作系統載入內存執行

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

            本文僅限于介紹 linux 下的庫。

             

            1.2、 庫的種類

            linux 下的庫有兩種:靜態庫和共享庫(動態庫)。

            二者的不同點在于代碼被載入的時刻不同。

            靜態庫的代碼在編譯過程中已經被載入可執行程序,因此體積較大。

            靜態用.a為后綴, 例如: libhello.a

            共享庫(動態庫)的代碼是在可執行程序運行時才載入內存的,在編譯過程中僅簡單的引用,因此代碼體積較小。

            動態通常用.so為后綴, 例如:libhello.so

            共享庫(動態庫)的好處是,不同的應用程序如果調用相同的庫,那么在內存里只需要有一份該共享庫的實例。

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

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

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

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

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

            1、生成靜態庫 生成靜態庫使用ar工具,其實ar是archive的意思

            $ar cqs libhello.a hello.o

            2、生成動態庫 用gcc來完成,由于可能存在多個版本,因此通常指定版本號:

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

            nm列出的符號有很多, 常見的有三種::

            一種是在庫中被調用,但并沒有在庫中定義(表明需要其他庫支持),用U表示

            一種是在庫中定義的函數,用T表示,這是最常見的

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

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

                $nm libhello.so | grep printf 

            發現printf是U類符號,說明printf被引用,但是并沒有在庫中定義。

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

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

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

            1.8、使用ar工具,可以生成靜態庫,同時可以查看靜態庫中包含那些.o文件,即有那些源文件構成

            可以使用 ar -t libname.a 來查看一個靜態庫由那些.o文件構成。

            可以使用 ar q libname.a xxx1.o xxx2.o xxx3.o ... xxxn.o 生成靜態庫

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

            可以使用下面的方法,給連接器傳遞參數,看是否鏈接動態庫還是靜態庫。

            -WI,-Bstatic -llibname //指定讓gcc/g++鏈接靜態庫

            使用:

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

            -WI,-Bdynamic -llibname //指定讓gcc/g++鏈接動態庫

            使用:

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

            如果要完全靜態加在,使用-static參數,即將所有的庫以靜態的方式鏈入可執行程序,這樣生成的可執行程序,不再依賴任何庫,同事出現的問題是,這樣編譯出來的程序非常大,占用空間。

             
            五、有關環境變量: 
            LIBRARY_PATH環境變量:指定程序靜態鏈接庫文件搜索路徑
            LD_LIBRARY_PATH環境變量:指定程序動態鏈接庫文件搜索路徑
             
            六、動態庫升級問題:
               在動態鏈接庫升級時,
            不能使用cp newlib.so oldlib.so,這樣有可能會使程序core掉;
            而應該使用:
            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文件時,如果在不停程序的情況下,直接用 cp new.so old.so 的方式替換程序使用的動態庫文件會導致正在運行中的程序崩潰。

             

            解決方法:

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

            linux系統的動態庫有兩種使用方法:運行時動態鏈接庫,動態加載庫并在程序控制之下使用。

             

            1、為什么在不停程序的情況下,直接用 cp 命令替換程序使用的 so 文件,會使程序崩潰?
            很多同學在工作中遇到過這樣一個問題,在替換 so 文件時,如果在不停程序的情況下,直接用cp new.so old.so的方式替換程序使用的動態庫文件會導致正在運行中的程序崩潰,退出。

            這與 cp 命令的實現有關,cp 并不改變目標文件的 inode,cp 的目標文件會繼承被覆蓋文件的屬性而非源文件。實際上它是這樣實現的:
            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” 打開目標文件時,原 so 文件的鏡像被意外的破壞了這樣動態鏈接器 ld.so 不能訪問到 so 文件中的函數入口。從而導致 Segmentation fault,程序崩潰。ld.so 加載 so 文件及“再定位”的機制比較復雜。

             

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

            在用新的so文件 libnew.so 替換舊的so文件 libold.so 時,如果采用如下方法:
            rm libold.so //如果內核正在使用libold.so,那么inode節點不會立刻別刪除掉。
            cp libnew.so libold.so
            采用這種方法,目標文件 libold.so 的 inode 其實已經改變了,原來的 libold.so 文件雖然不能用 ”ls”查看到,但其 inode 并沒有被真正刪除,直到內核釋放對它的引用。

            (即: rm libold.so,此時,如果ld.so正在加在libold.so,內核就在引用libold.so的inode節點,rm libold.so的inode并沒有被真正刪除,當ld.so對libold.so的引用結束,inode才會真正刪除。這樣程序就不會崩潰,因為它還在使用舊的libold.so,當下次再使用libold.so時,已經被替換,就會使用新的libold.so)

            同理,mv只是改變了文件名,其 inode 不變,新文件使用了新的 inode。這樣動態鏈接器 ld.so 仍然使用原來文件的 inode 訪問舊的 so 文件。因而程序依然能正常運行。

            (即: mv libold.so ***后,如果程序使用動態庫,還是使用舊的inode節點,當下次再使用libold.so時,就會使用新的libold.so)


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


            但是,為什么系統會阻止 cp 覆蓋可執行程序,而不阻止覆蓋 so 文件呢

            這是因為 Linux 有個 Demand Paging 機制,所謂“Demand Paging”,簡單的說,就是系統為了節約物理內存開銷,并不會程序運行時就將所有頁(page)都加載到內存中,而只有在系統有訪問需求時才將其加載。“Demand Paging”要求正在運行中的程序鏡像注意,并非文件本身不被意外修改因此內核在啟動程序后會鎖定這個程序鏡像的 inode

            對于 so 文件,它是靠 ld.so 加載的,而ld.so畢竟也是用戶態程序,沒有權利去鎖定inode,也不應與內核的文件系統底層實現耦合

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

            指定使用靜態庫

            Linux動態鏈接庫的問題_靜態庫、動態鏈接庫同時存在時,gcc/g++默認用動態庫,參數-WI,-Bstatic指定使用靜態庫,參數-WI,-Bdynamic指定使用動態庫\編譯OK,運行找不到的問題_解決-WI,rlibpathname  

            2011-03-11 14:30:23|  分類: Linux系統編程|字號 訂閱

            1、在Linux下,動態庫和靜態庫同事存在時,gcc/g++的鏈接程序,默認鏈接的動態庫。

                             可以使用下面的方法,給連接器傳遞參數,看是否鏈接動態庫還是靜態庫。

                      

                            -WI,-Bstatic             -llibname                                   //指定讓gcc/g++鏈接靜態庫

            使用:

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

                     

                          -WI,-Bdynamic       -llibname                                    //指定讓gcc/g++鏈接動態庫

            使用:

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

             

                           如果要完全靜態加在,使用-static參數,即將所有的庫以靜態的方式鏈入可執行程序,這樣生成的可執行程序,不再依賴任何庫,同事出現的問題是,這樣編譯出來的程序非常大,占用空間。

             

                     

             

            2、Linux下動態庫為什么會出現編譯OK,運行時找不到的情況。

                              原因是:

                                      linux下鏈接器默認是不記錄庫的搜索路徑的,只記錄名字,所以才會有編譯時OK,但運行時,找不到的情況。

                            解決方法:

                                     想在程序中記錄路徑,可以使用-WI,-rlibpath指定動態庫的搜索路徑。

                                     使用方法。

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

             

            3、關于Linux下庫的更多問題,可以參看:

                            自己的博客:   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 閱讀(4167) | 評論 (0)編輯 收藏

            僅列出標題
            共25頁: 1 2 3 4 5 6 7 8 9 Last 
            亚洲狠狠婷婷综合久久久久| 国内精品久久久久久久久电影网| 亚洲精品久久久www| 久久精品国产精品亚洲精品 | 日本强好片久久久久久AAA| 国产精品欧美久久久天天影视| 国产精品久久久久乳精品爆| 欧美国产成人久久精品| 亚洲国产精品热久久| 日产精品久久久久久久性色| 无码8090精品久久一区| 久久99国产精品久久99| 一本一本久久a久久综合精品蜜桃| 久久精品国产久精国产| 色综合久久中文字幕无码| 久久久久无码中| 亚洲一区中文字幕久久| 久久久久亚洲精品无码蜜桃| 久久中文字幕人妻丝袜| 久久午夜福利电影| 国产成人99久久亚洲综合精品 | 久久青青国产| 国产99久久久久久免费看| 精品久久久久久中文字幕人妻最新| 亚洲欧美日韩精品久久亚洲区 | 品成人欧美大片久久国产欧美...| 日产精品久久久一区二区| 99久久免费国产精品特黄| 久久久久这里只有精品| 国产亚洲色婷婷久久99精品91| av午夜福利一片免费看久久| 久久亚洲美女精品国产精品| 天天躁日日躁狠狠久久 | 国产精品美女久久久m| 亚洲女久久久噜噜噜熟女| 欧美亚洲国产精品久久高清| 久久中文字幕人妻丝袜| 久久久久波多野结衣高潮| 亚洲精品无码久久久影院相关影片| 久久精品中文字幕一区| 亚洲国产精品高清久久久|