因爲平時業餘興趣和工做須要,研究過並使用過期下流行的各類開源的x86/64彙編和反彙編引擎。若是要對彙編指令進行分析和操做,要麼本身研究Intel指令集寫一個,要麼就用現成的開源引擎。本身寫太浪費時間,又是苦力活,還容易出錯,因此仍是使用現成的好一點。
這裏對我曾使用過的比較流行的反彙編引擎作個比較,我使用過的反彙編引擎有:
1. Ollydbg的ODDisassm
Ollydbg的ODDisassm,這是我最先使用的一個開源的反彙編引擎,07年在《加密解密》(三)
中我寫的一個很簡單的虛擬機就是使用的這個庫,由於那個時候尚未那麼多可選擇。不過多虧有這樣一個基礎庫,整個虛擬機從設計到開發完成只用了兩個星期便開發完成(當時對反彙編庫的要求不高,只要求能用字符串文本作中間表示進行編碼/解碼)。
這個反彙編庫的優勢是含有彙編接口(即文本解析,將文本字符串解析並編碼成二進制),就拿這個特性來講在當時也算是獨樹一幟的了,到目前爲止開源界在作這個工做的人也不多,
不過近年出現的調試器新秀x64dbg,也附帶開發了開源的彙編庫XEDParse,功能與OD的文本解析功能類似,而且支持的指令集更加完整,BUG更少,同時還支持X64,維護一直很強勁。
可是ODDisassm的缺點也不少,好比:
1. 指令集支持不全,因爲Ollydbg年久失修,如今甚至連對MMX指令集都不全,而如今的INTEL/AMD的擴展指令集標準又更新了多個版本,什麼SSE5/AVX/AES/XOP就更別提了,徹底沒法解析。
2. 解碼出來的結構不詳細,好比指令前綴支持不夠友好,這點從Ollydbg的反彙編窗口能夠看出,除了movs/cmps等指令之外,repcc與其餘指令組合時都是單獨分開的;
再好比寄存器沒法表示ah\bh\ch\dh這種高8位寄存器。
3. 做者一次性開源後便再也不維護開源版本,對於反彙編上的BUG很難即時修復。
不過這些也能夠理解,由於在當時做者的開發目的是進行文本彙編\反彙編,因此沒有爲解碼出的信息創建結構體以及接口。總的來講,現在再使用這個反彙編引擎,已經落後於時代了。
2. BeaEngine
BeaEngine是我用的第二個庫,當時使用OD庫已經不能知足個人需求了。在作反編譯器的時候,須要一個可以解碼信息越多越好的庫,因而我找到了BeaEngine,這個庫我記得之前的版本不支持高8位寄存器識別,如今的版本也支持了。
在使用過程當中基本上沒有發現什麼明顯的缺點,不經常使用的新的擴展指令集也實現了很多。
目前實現的擴展指令集有:php
FPU, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, VMX, CLMUL, AES, MPX
同時它也給不一樣種類的指令進行了分類,這在判斷不一樣指令時很方便。它還有一個特色是能夠解碼出每一條指令所使用到和影響到的寄存器,包括標誌位寄存器,甚至精確到標誌位寄存器的每個位置。
這個功能用來作優化器與混淆器再好不過了。
可是我的認爲BeaEngine的編碼風格實在是不咋地,各類變量強制轉換,各類命名風格,給人一種亂亂的感受。對我這種對編碼有潔癖的人來講,實在是沒法忍受,因此後來又換了其餘的庫。若是你不在乎這些,BeaEngine的性能仍是比較不錯的。
還有一點,BeaEngine常常性的爆出一些BUG,因此使用它時要有心理準備。
3. udis86
udis86應該是我最喜好的反彙編引擎了。udis86支持的X86擴展指令集有:git
MMX, FPU (x87), AMD 3DNow, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AES, AMD-V, INTEL-VMX, SMX
udis86的代碼風格十分簡潔,功能函數短小,變量命名清楚又簡潔,接口乾淨意思明白,操做靈活,若是你本身有需求維護一個本身的分支,花不了幾十分鐘的時間就能熟悉整個代碼構架。
說了這麼多溢美之詞,它到底有什麼優勢呢?
udis86的優勢就是接口很靈活,你能夠選擇對一條指令使用ud_decode只進行解碼,再對解碼後的結構使用ud_translate_intel轉換成文本格式,
或者你也能夠直接使用ud_disassemble一次性完成整個操做。並且這些接口都只須要一行就能搞定。
因爲udis86的這種組合模式設計理念,他能夠適應各類場景,若是你要開發一個IDA那樣的反彙編器,它能作;你要開發一個指令模擬器、分析器、優化器、混淆器,它也能作。
這種理念直接使udis86在擁有了強大的適應能力的同時還兼顧了性能,我作過性能測試,
udis86是我用過的解碼細節能力相近的狀況下,解碼速度最快的引擎了。
至於缺點的話,目前還沒發現,不過,udis86不支持BeaEngine那種的寄存器分析,算是點小遺憾。
4. Capstone
capstone能夠說是全部反彙編引擎中集大成者,對於它我要多費點口水,由於我對他是又愛又恨。capstone是基於LLVM框架中的MC組件部分移植過來,因此LLVM支持的CPU構架,capstone也都支持。
它支持的CPU構架有:github
Arm, Arm64 (Armv8), M68K, Mips, PowerPC, Sparc, SystemZ, XCore & X86 (include X86_64)
並且Capstone對X86構架的指令集支持是最全的,這一點是其餘引擎都比不上的,其支持的X86擴展指令集有:sass
3dnow, 3dnowa, x86_64, adx, aes, atom, avx, avx2, avx512cd, avx512er, avx512f, avx512pf, bmi, bmi2, fma, fma4, fsgsbase, lzcnt, mmx, sha, slm, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, xop.
很強吧?
在目前移動端如此火熱的背景下,支持ARM的反彙編庫仍是很是少的,若是要同時進行X86與ARM下的編譯器方面的開發,能使用一個統一的接口那天然是更好。
另外capstone的next分支中也支持BeaEngine那種解碼時分析指令使用和影響到的寄存器這種炫酷的特技(master分支沒有這個接口),有這樣的基礎庫存在真的能夠偷很多的懶。
僅從X86/64平臺來看,不管是從解碼能力仍是指令集支持,Capstone能夠稱得上是一個徹底超越了BeaEngine的存在。
老套路,說完了好話,又該說缺點了。
因爲capstone是從LLVM移植過來,capstone是C語言的項目,而LLVM是C++項目,因此在移植過程當中作了不少適配工做,顯得很臃腫。
舉個栗子,LLVM中的MCInst是一個單條底層機制指令的描述類,因爲capstone是C項目,移植時將這些類變成告終構,把成員函數變成了獨立的C函數,好比MCInst_Init,MCInst_setOpcode等等。而且因爲LLVM框架的複雜性和高度兼容性,裏面的全部的概念都作了高度抽象,而且Capstone又作了適配接口將其轉換到本身的構架中,這會形成解碼時中間層過多,性能降低。一條指令的解碼過程用到的重要的中間層結構順序是這樣的: 框架
MCInst => InternalInstruction => cs_insn
最基礎的解碼工做依靠LLVM的構架解碼到Capstone的InternalInstruction,它是一個包含了解碼過程當中的全部細節的內部結構,解碼完成後再調用update_pub_insn將認爲須要公開暴露出來的內容複製到cs_insn中。而其餘反彙編引擎都是一次性解碼到目標結構中的。
Capstone的解碼過程如此複雜,天然會對性能形成影響,我作過一個不太嚴格的性能測試,Capstone的性能消耗時間大概是udis86的五、6倍(順便吹一下,這裏我給Capstone提交了一個小小的Pull Request,分別在這裏和這裏,PR中附帶了一個benchmark,通過測試性能提升了將近有20%),若是換種方式測試,udis86只使用ud_decode進行解碼,而Capstone沒有獨立的解碼接口,
須要作一些Hack,也讓它不生成彙編文本,那麼Capstone的消耗時間大概是udis86的2倍多,因而可知Capstone的文本操做又比udis86慢更多。
其次,Capstone的內存消耗很大,解碼一條指令時你傳入的指令結構cs_insn必須由動態分配函數來分配,而且還要分配兩次,一次是cs_insn,一次是cs_detail。這會形成巨量的內存碎片,另外,每一條指令的結構體都很大,有多大我不記得了,sizeof(cs_insn)+sizeof(cs_detail)好像至少在2K以上。
必須使用動態內存這一點是Capstone與其餘反彙編引擎不同的地方。
若是要使用Capstone作大量指令分析的話,那麼得給它配一個固定對象內存分配器才行,那樣能稍微緩解一下內存碎片狀況,也能提升一點性能。
多是基於以上理由,x64dbg社區原本最開始是使用BeaEngine做爲支撐基礎的,可是BeaEngine老是爆出很多BUG,因此後來選擇由Capstone替換,可是也僅用Capstone來作GUI的文本反彙編,由於它解碼速度雖然不行,可是BUG不多(由於LLVM有蘋果那麼大個公司作支撐),而流圖和指令分析(還不完善)目前仍然使用的BeaEngine,這也是沒辦法的事,畢竟性能也很重要。
還有一個問題,若是你須要的是解碼能力強的反彙編引擎,那麼建議你在選擇前先對比一下各引擎的解碼結構,有沒有你須要或者必須有的字段。
由於Capstone有一個坑爹的地方,雖然其自己的解碼能力其實很強的,可是Capstone把中間層封裝了一遍,只暴露其認爲須要暴露的字段,而且其主要維護者有點執拗(也能夠說嚴謹),他堅持認爲不太經常使用的字段不必暴露出來,而接口是越簡潔越好。
好比說指令中當即數Immediate所在的偏移,內存操做數中Displacement所在的偏移,在內部結構InternalInstruction中原本是有的,可是複製到公開結構cs_insn結構中就丟棄了。
還有REP與REPE前綴,雖然都是同一個常量表示,可是配上不一樣指令功能仍是不同的,對於這個,capstone內部有一個valid_repe函數能夠區別,可是也沒有暴露到公開結構中,都當作REP來識別。雖然這些都很冷門,可是作指令分析和變形,這些仍是頗有用處的。
因此我我的認爲capstone的接口用起來實在讓人不爽,可是它的功能又實在太強大,若是研究下其源碼的內部構造,會發現不少接口沒提供,可是內部卻有的好東西,因此我本身維護了一個分支,痛並快樂着的使用着。
其餘
其實還有XDE,不過我沒使用過,就不作評論了。
另外,blackbone中含有一個長度反彙編引擎也值得一提,名字叫ldasm,其實它也算不上一個引擎,由於它只有一個函數,做用只是計算一條指令的長度,在hook的時候重定位跳轉指令時頗有用處。代碼傳送門
總結
這幾款反彙編引擎各有長處(OD除外。。),可是又各自有那麼一丁點兒的缺陷,這世上沒有完美的事情,人家都開源了,有的用就不錯了,本身總要作些事情不是?
挑一款好用的庫,使用過程當中發現BUG,給社區提交個Issue,或者作個解決方案再發個Pull Request,也算付了使用費了。
下面對這3款反彙編引擎以我的經驗作個比較:
性能 :udis86 > BeaEngine > capstone
解碼能力 :capstone > BeaEngine > udis86 (udis86不支持寄存器分析,其他解碼能力是相近的)
平臺支持 :capstone > (udis86 = BeaEngine)
X86擴展指令集:capstone > (udis86 ≈BeaEngine)
若是你須要的是一個X86/64下的性能又好同時解碼能力又強的反彙編引擎,而且不須要寄存器分析這種特技的話,那麼udis86合適你;
若是你還須要帶寄存器分析功能的話,那麼BeaEngine與capstone合適你;若是你還須要ARM構架支持的話,capstone應該會更適合你。
每一款引擎各有優劣,使用起來仁者見仁智者見智,鞋子合不合適只有本身的腳才知道了。
如要查看個人其餘相關技術文章,歡迎訪問bughoho.me函數