翻譯FLEXlm9.2的破解教學一

看雪資料發表於2015-11-15

翻譯第一篇 On Software Reverse Engineering - 1
            Reverse Engineering, FLEXlm, IMSL
    本文討論了軟體反向工程方法和FLEXLM系統的學習例項。
問題描述
    我下載了IMSL_CNL5.5(C數字庫),但它由FLEXLM保護。對於不同的OS和編譯器有不同的二進位制下載,但透過平臺可採用通用的破解技術(在我們的例子中,FLEXLM許可證檔案是當然的獨立平臺)。該產品主要包含了數學和統計庫(靜態和動態的)子程式,FLEXLM的FEATURE名稱為“CMATH”和"CSTAT"。X86的釋出版還有一個CMATH的最佳化版本叫做“CMPERF",它透過捆綁Intel MKL6.1以實現更有效的執行。對於CMPERF沒有額外的許可證。因為CMATH和CSTAT使用了相同的許可證機制,從現在開始,我們將聚焦在CMATH上。CSTAT的(破解)過程是完全類似的。
    出於有效的目的,該步提供了一個簡單的程式”cmath.c",將它和目標檔案“cmath_s.lib"連結以得到可執行檔案。它呼叫imsl_f_lin_sol_gen()並檢查許可證檔案,如果許可證檔案不正確,將報告錯誤。從網上,我們找到IMS_CNL5.0的如下許可證檔案:
SERVER hostname hostid 27000 
DAEMON VNI "<vni_dir>\license\bin\bin.i386nt\vni.exe"
FEATURE CMATH VNI 5.0 permanent uncounted 3F23BE3056E4 HOSTID=ANY
FEATURE CSTAT VNI 5.0 permanent uncounted 2C60CD4570B0 HOSTID=ANY
   我們試用它並如預料一樣的得到“Version incorrect(版本不正確)”的錯誤;用5.5替換5.0,得到“incorrect softkey code(不正確的軟體加密程式碼)”錯誤,因此很明顯的該方法不會工作。事實上,它採取了相當複雜的工作以應對FLEXLM。軟體破解有不同的級別,其相應的複雜範圍從相對簡單到相當困難-我們將在後面看到。現在,我們在下面列出了我們的任務和使用的工具。
   物件:Visual Numerics IMSL CNL 5.5
   保護:Macrovision FLEXlm 9.2
   工具:Microsoft Visual Studio 7.1 (CL, NMAKE, DUMPBIN, LIB, …)
        RedHat Cygwin 1.3.5
        IDM UltraEdit 9.0
        Datarescue IDA Pro 4.3 (FLAIR, …)
        URSoft W32Dasm 8.9
        Sysinternals File Monitor 6.0
   資源:Macrovision FLEXlm 9.2 SDK source code
        Macrovision FLEXlm 8.1 SDK binary release
初步試探
    所有的破解起步於資訊收集。例如,透過在UltraEdit中搜尋cmath_s.lib很容易發現目標程式採用了FLEXLM9.2進行許可證管理。閱讀相關的文獻也是非常有幫助的。在[4]中有一些優秀的論文,講述了以前攻擊早期的FLEXLM版本,它們包含了許多珍貴的知識。
    在實踐中,我載入cmath.exe到W32Dasm偵錯程式中並很快開始了漂亮的追蹤。透過反彙編程式碼集追蹤需要驚人的經驗,可能你花費了數小時進行來回的跳轉而沒有得到程式實際執行的任何線索。但我試圖找出一些(有用的)東西(在File Monitor的幫助下): 
004133DE E89D2F0100 call 00426380 ;讀 license.dat
... ...
00413411 C 0041345B ... ... ;迴圈讀取“CMATH”
... ...
004138CD E83E670000 call 0041A010 ;必須返回0以透過檢查
004138D2 83C41C add esp, 0000001C
004138D5 8945F0 mov dword ptr [ebp-10], eax ;返回值儲存在EAX中
004138D8 837DF000 cmp dword ptr [ebp-10], 00000000
004138DC 0F848C070000 je 0041406E ;進行imsl_f_lin_sol_gen()
... ...
00413907 8B8D18F7FFFF mov ecx, dword ptr [ebp+FFFFF718]
0041390D 33C0 xor eax, eax
0041390F 8A81A7444100 mov al, byte ptr [ecx+004144A7]
00413915 FF24857F444100 jmp dword ptr [4*eax+0041447F] ;跳到錯誤資訊
    很明顯,子程式0041A010是關鍵。步入裡面以揭示更多複雜的結構-在它裡面有一些複雜的呼叫鏈。在實踐中,該過程返回FFFFFFF8 (or C8),它將是錯誤程式碼。因此,我們在004138D5處設定一個斷點,使用W32Dasm"Modify Data(修改資料)"按鍵修改EAX暫存器的值為0,並讓它繼續執行。哇噻!程式被迷惑了,並且輸出了正確的結果就好像我們已有了正確的許可證資料。
    現在,事情變得很明朗了,我們可以在程式碼段004138D2 C 004138DC上打補丁以設定返回值總是為0。為了達到該目的,我們可以參照[5]中關於MOV和JMP的詳細X86指令格式,剩下的事情就是修改程式碼了。注意,新增NOP用於補綴更改留下的特別的位元組以使修改的可執行檔案和原檔案的長度相同。事實上,我們改變的僅有三行不同:
 004138CD E83E670000 call 0041A010 ;返回必須為0以透過檢查
004138D2 83C41C add esp, 0000001C ;堆疊指標調整
004138D5 C745F000000000 mov [ebp-10], 00000000 ;假裝返回值為O
004138DC 90 nop ;補綴特別的位元組以保持程式碼排列
004138DD E98C070000 jmp 0041406E ;無條件跳轉到imsl_f_lin_sol_gen()
    當然補丁cmath.exe僅是概念的證據,實際事情是補丁庫自身。cmath.c per se是非常短的,事實上cmath.exe的所有內容來自cmath_s.lib,包括IMSL函式和FLEXLM程式碼。為了定位上面的程式碼,我們在UltraEdit[2]中搜尋二進位制串837DF0000F848C070000 (關於CMP和JE行的程式碼),它將引導我們到在cmath_s.lib中程式碼放置的唯一位置(檔案偏移00106F70 C 00106F80)。照著更改位元組,我們將得到一個補丁庫。透過連結程式到新的補丁庫進行測試,除了許可證檔案無效外,所有功能都執行良好。
     因此,我們已經取得了第一個成功。接著,我們對庫的DLL版本做相同的事情,並且理想的開發一個工具自動進行而不是手工進行,但現在我們要省略它。在這點上,補丁通常是最容易的也是破解的第一步,它僅要求對保護方案有很少的洞察。此外,補丁僅針對一個特定的二進位制目標工作,補丁工具對不同的版本或在不同的平臺上無效。儘管補丁是強有力和有效的,但它距完全的反向工程還很遠。
進一步分析
    我們採用透過一個假的"OK"來迷惑FLEXLM,但是我們仍然不知道真的許可證程式碼。許多軟體採用這種保護方式:基於使用者資訊(名稱,組織,購買的特徵等)和某種演算法,程式計算一些hash/checksum/license code(許可證程式碼)/signature(簽名),並將它和一個使用者提供的(資料)進行比較。如果它們匹配,使用者將被授權。對於我們所見的所有軟體,該機制幾乎是通用的,FLEXLM也不例外。在我們的許可證檔案中,程式碼3F23BE3056E4是"SIGN="簽名。
    關於FLELXELM更多的一點:之所以叫FLEXLM是因為它聲稱對商業軟體許可證管理提供了一個靈活的解決方案。它作了許多努力以適應各種情況-counted/uncounted, feature/incremental, server/local, borrowing, trial, mobile, …(保持原文) C但是我們對這些並不感興趣。我們所要的是正確的簽名以使我們能在任何時間、任何地點執行具體的目標程式。
因為我們已經有了FLEXLM SDK9.2的原始碼,接下來的事情就是閱讀它。FLEXLM SDK是Macrovision提供給客戶的所謂的" 守護神",並幫助他們封裝他們自己的"守護神軟體“給”終端使用者“。在我們的例子中,Visual Numerics(可視數字)是Vendor(守護神),IMSL CNL 5.5是守護神軟體,我們是終端使用者。每個Vendor有一個唯一的名字或Vendor ID。正如在許可證中所見,這裡是"VNI"。通常,Vendors僅能得到二進位制版本或FLEXLM SDK的部分源(程式碼),但我們很幸運的得到了關於FLEXLM的完整的原始碼。
    但是,事實證明所有的C原始碼(非C++)並非易讀的。 該工程演變超過了十年(1988的第一版);新舊函式疊加/纏繞在一起,常常伴有不必要的多餘;它的註釋很少並且一些舊型別程式碼習慣很差;宏的和處理指令的過多使用令人非常煩惱。最初是在UNIX平臺上開發,後來透過NMAKE工具過渡到Windows環境。為有效的建立和除錯如此大的一個應用程式,我們需要一個好的IDE,但在Windows下沒有如此的IDE能直接容納Makefile。在Windows上,Visual Studio可能是最好的IDE並具有“Makefile Project"能力。我們為FLEXLM SDK著手建立一個VS7工程。它花了我一些時間完成-需要修改在makefiles(製作檔案)中的一些錯誤-但當它能用時,確實很方便。
    FLEXLM的核心元件是lmgr.lib(或lmgr9a.dll),所有的其他元件都取決於它。Vendors和終端使用者更熟悉lmgrd.exe,lmtools.exe,lmnewgen.exe, makekey.exe等。在成功的建立後,我們試圖產生VNI許可證檔案但失敗了,因為我們並沒有其vendors和seeds。透過[2], [3], [4],每個Vendor從Macrovision接收到了5個vendor密碼(VENDOR_KEY1,… VENDOR_KEY5),並且他們自身選擇3個隨機seeds (LM_SEED1, LM_SEED2, LM_SEED3)。這8個數放置在lm_code.h中並被加密、模糊,最後嵌入到目標程式和產生許可證的工具中。我們的工作當然是回覆這些數,但現在?
    或許我們可以嘗試容易一點的方法:因為目標程式將計算真正的簽名並和從許可證中讀取的進行對比,我們可以在對比發生時找到真正的簽名而無需知道Vendor密碼和seeds。這並不是如看起來的那麼簡單,它要求我們在正確的時間、正確的位置設定斷點。在數小時的追蹤後,我們確定的僅是沿著指令流是一個死結,它無助於定位對比程式碼,除非FLEXLM使用一些標準的API如strncmp()或Win32CompareString()(實際上FLEXLM在l_privat.h中定義它自己的宏STRNCMP)。
    這使我們產生了一個關於反向工程的基本問題:我們怎樣能理解混亂的、高熵(?)的反彙編程式碼呢?這裡沒有完整的答案,我們可以參考[7]中關於一些嚴肅的理論討論。但是這裡我們想聚焦於一個特殊的技術:由IDR Pro介紹的FLAIR(a.k.a. FLIRT, c.f. [6])。
    我們都知道,如果應用程式由除錯版本建立且符號檔案(.PDB, .DBG檔案) 可獲得時,除錯將非常容易。符號是關於程式包括標識(變數、函式)名稱和記憶體偏移、原始碼行號等的資訊。在一個除錯建立編譯器/連結器中,儲存這些到應用程式二進位制或單獨的符號檔案中。它們可以給除錯者展現使用者程式碼,將更接近於原始碼(甚至更好,就象在VS中的原始碼除錯)。很顯然,在我們的例子中,提交給終端使用者的軟體釋出版本中的符號也被剝離出來了。
但這並沒有結束。即使沒有符號,我們仍然可以從二進位制檔案、特別是庫(檔案)中得到一些有用的東西。在Windows上,EXE和DLL是PE格式,而OBJ和LIB是COFF格式(LIB不僅僅是一堆OBJ堆放在一起);在兩種格式庫中的呼叫透過函式名稱和引數進行,它們必須公開可視[3]。為達到該目的,PE有輸入和輸入段,COFF有符號表列。VS提供了一組命令找尋它們。
F:\>dumpbin /disasm %vni_dir%\cnl55\cmath.exe
F:\>dumpbin /rawdata %vni_dir%\cnl55\cmath.exe
F:\>dumpbin /imports %vni_dir%\cnl55\cmath.exe
F:\>dumpbin /exports %vni_dir%\cnl55\bin\cmath.dll
F:\>dumpbin /imports %vni_dir%\cnl55\bin\cmath.dll
F:\>dumpbin /exports %vni_dir%\cnl55\lib\cmath.lib
F:\>dumpbin /symbols %vni_dir%\cnl55\lib\cmath_s.lib
F:\>dumpbin /linkermember %vni_dir%\cnl55\lib\cmath.lib
F:\>dumpbin /linkermember %vni_dir%\cnl55\lib\cmath_s.lib
F:\>dumpbin /archivemembers %vni_dir%\cnl55\lib\cmath.lib
F:\>dumpbin /archivemembers %vni_dir%\cnl55\lib\cmath_s.lib
F:\>lib /list %vni_dir%\cnl55\lib\cmath.lib
F:\>lib /list %vni_dir%\cnl55\lib\cmath_s.lib
F:\>lib /extract:vc++\flexlm.obj %vni_dir%\cnl55\lib\cmath_s.lib
F:\>dumpbin /symbols /disasm flexlm.obj
    在此,我們必須縮小LIB和DLL之間的差異,它僅僅是靜態連結VS動態連結的差異。開發一個程式通常需要三步:
初步:Source File (C, H, C++…), ASCII format/原始檔 ASII格式
中間:Object File (OBJ, LIB…), COFF format /目標檔案 COFF格式
最後:Image File (EXE, DLL, SYS…), PE format /映像檔案 PE格式
    編譯器處理原始檔為目標檔案,連結器將目標檔案連結到輸出的映像檔案,載入器將映像檔案從磁碟載入到記憶體。注意DLL已經被連結器處理了(在連結後),轉換標識為記憶體地址或“被替換的數字字元”。相反的,目標檔案是“連結前(的檔案)”並保留了原始的符號;否則連結器將不能解析它們。因此DLL比LIB檔案更接近於EXE,哪怕(LIB)的名稱為“庫”。實際上,“動態連結”部分是在執行時由系統載入器進行的(一些修正/重定位),而不透過連結器。你可以說這是微軟用了誤導術語、迷惑使用者並隱藏其技術要點的花招,但他們確實擅長於這些。
    從一個破解者(原為駭客,我認為破解者更好)的視點看,這意味這越到後面,熵更高,資訊更少。特別的,LIB比DLL提供了更多的線索。DLL僅輸出了公開的API並隱藏了專用的(如在cmath.dll中僅IMSL API是輸出的,而FLEXLM函式內部保留),但是LIB符號包含了兩者。事實上,為了建立一個程式以呼叫DLL API,我們需要傳遞其輸入庫到連結器。輸入庫是一個COFF LIB檔案,它作為一個符號參考進行服務(透過指向DLL),並不包含函式體。
現在,我們可以發現在庫檔案中的函式,找出我們關心的函式,甚至可以提取相應的目標檔案(僅針對LIB)。下面是一個imsl_f_lin_sol_gen()的例子。在我們討論之前,駐留在PE除錯段或單獨的符號檔案中的除錯資訊最好靠近於原始碼(除錯資訊位於第一和第二步之間)。此外,我們在此獲得的東西在反向工程中仍然是非常重要的(除錯-建立符號是COFF符號的一個擴充套件集):
F:\>dumpbin /archivemembers /symbols %vni_dir%\cnl55\lib\cmath_s.lib|egrep "member|imsl_f_lin_sol_gen”
... ...
Archive member name at 1C8CEE: vc++\gmres.obj/
00B 00000000 SECT3 notype () External | _imsl_f_lin_sol_gen_min_residual
... ...
Archive member name at 1DAAAA: vc++\fspgen.obj/
00B 00000000 SECT3 notype () External | _imsl_f_lin_sol_gen_coordinate
05A 00000000 UNDEF notype () External | _imsl_f_lin_sol_gen
... ...
Archive member name at 1FC10E: /3128 vc++\fdmbndg.obj
007 00000000 SECT2 notype () External | _imsl_f_lin_sol_gen_band
... ...
Archive member name at 58AC3E: /6132 vc++\flinslg.obj
007 00000000 SECT2 notype () External | _imsl_f_lin_sol_gen
... ...
F:\>dumpbin /exports %vni_dir%\cnl55\bin\cmath.dll | grep -i imsl_f_lin_sol_gen
438 1B5 000432C0 imsl_f_lin_sol_gen
439 1B6 0023B130 imsl_f_lin_sol_gen_band
440 1B7 0024AE30 imsl_f_lin_sol_gen_coordinate
441 1B8 00259C30 imsl_f_lin_sol_gen_min_residual
 F:\>lib /extract:vc++\flinslg.obj %vni_dir%\cnl55\lib\cmath_s.lib
    IDA FLAIR將帶入更深入的一步。雖然在原始碼中API呼叫看起來好像“x = imsl_f_lin_sol_gen(n, a, b, 0);”,在反彙編中它顯示為“call 004033B0” (靜態連結) or “call [00402054]” (動態連結)。沿著其記憶體地址的恰當的函式名稱標記將使彙編分析容易得多,而這恰恰是FLAIR所能做到的。許多偵錯程式有類似的註解功能,但通常限於輸出的API。IDA試圖擴充套件到包含儘可能多的函式。
    FLAIR的思想是為每個可標識的庫函式建立一個“簽名”,以使IDA在分析彙編程式碼時能認知和標記它。在名稱指示時,它基本上就是一個認知的問題。再次,由於內容的不同,它僅為LIB工作而不針對釋出的DLL版本。我們必須看到DLL有其優勢諸如程式碼共享和主程式簡化。例如,cmath.exe的靜態連結大小約700KB,但其動態連結小於4KB。但破解所關心的是,LIB是比DLL更好的方式(當追蹤DLL連結的應用程式時,我們的大多數時間都是在10000000+ 或 80000000+ 區而不是熟悉的00400000+ 區; 在DLL版本的cmath.exe中,指令“102D12BA: call 102D7980” 返回FFFFFFF8)。
    IDA FLAIR並非完美的-它不能操作DLL,一些函式不能被識別,可能發生錯誤的認知等。但它仍然是切實可行的。它最初的目的是分離樣板API(諸如Win32,MFC,ATL等[6]),因此我們可以聚焦在主程式演算法上而不是標準的庫函式上。在我們的例項中,我們更感興趣的是得到這些重點的FLEXLM函式,因此我們不必步入每個呼叫以得到全部程式碼迷惑的一個大(結構)圖。在實際中,我們建立了cmath_s.lib和lmgr.lib的簽名檔案,在IDA中將它們應用到cmath.exe,發現FLAIR做得很好-它認知了大多數FLEXLM函式。作為一個優秀的靜態分析工具,IDA也提供了一個WinGraph32特徵稱作“顯示流程圖表”。我發現它在對照原始碼時特別有助於程式碼的理解。

相關文章