題:
為什麼拆卸不是一門精確的科學?
Einar
2013-08-04 18:44:00 UTC
view on stackexchange narkive permalink

此處是新手。

來自維基百科

反彙編並不是一門精確的科學:在具有可變寬度指令的CISC平台上,或在如果存在自我修改的代碼,則單個程序有可能具有兩個或更多個合理的分解。確定在程序運行期間實際遇到的指令將減少已被證明無法解決的停止問題。

  1. 為什麼在CISC上反彙編不是一門精確的科學?我知道重新組裝反彙編的代碼可能會產生不同但相似的操作碼相同的指令,但是由於它們相似,因此不應影響生成的程序。而且,如果這不是一門精確的科學知識,即assemble(disassemble(opcode))!=操作碼,那麼CPU如何確定哪種方式來解釋操作碼流?

  2. 從側面講,這是否意味著在RISC平台上拆卸是一門精確的科學?

  3. ol>
一個更抽象的原因可能是編譯不是1對1。每個高級語句都不會編譯為唯一的一組操作碼。就像在數學中一樣,您具有域和範圍。編譯會將域映射到範圍。如果該域比範圍大得多,則編譯不是1到1。因此,沒有反函數或重新編譯/反彙編函數(一般來說,該解決方案並非來自於必要,而是您可以選擇)。
五 答案:
0xea
2013-08-04 18:52:54 UTC
view on stackexchange narkive permalink

回答第一個問題。最大的問題是您無法真正將數據與代碼分開。基本上有兩種解決方法:

  1. 線性掃描
  2. 遞歸遍歷
  3. ol>

    使用線性掃描的Dissasembler從某個地址開始,並且可解決直到結束時一一說明,而無需跟隨跳轉或以任何方式對已分解的代碼進行推理。例如,SoftICE和Windbg使用線性掃描。這可能是個問題,因為您不知道何時停止。您不知道指令在哪裡結束,數據在哪裡開始。為此,您必須依賴可執行格式的元數據,例如節大小。

    另一方面,遞歸遍曆算法考慮了跳轉和調用,而反彙編器遵循跳轉,僅分解了將實際執行的代碼。例如,IDA和OllyDBG使用此方法。它的好處是,它本質上知道什麼代碼和什麼數據。但這顯然有其缺點。首先,通過純遞歸遍歷,並非所有代碼都會被分解。例如,在運行時通過計算其地址而未直接引用的函數將不會被發現。同樣,引擎具有一些啟發式方法來繞過此問題。

    以一個程序為例,它跳回了幾條指令,但它並沒有跳到先前已分解並執行的指令的開頭,而是跳到了中間。反彙編程序應如何決定顯示哪一個?如果這是編碼人員的意圖,則兩者都可能有效。遞歸遍歷分解器可能會顯示第一個分解的版本,並且只是標記跳轉到函數的中間。但是,如果您不詳細檢查字節,則很難說出跳轉後實際執行的操作。

    還有許多其他示例。

    這兩種方法的另一個大問題是自我修改代碼。只是不能靜態完成。

    CPU在執行代碼時本身沒有任何問題,換句話說,它是動態的。

    要回答第二個問題,我認為這不是僅是CISC體系結構的問題,其中一些也可以應用於RISC。

實際上,他們應該說馮·諾依曼體系結構(允許自我修改),而不是CISC。
@Ruslan:與自我修改無關。關鍵是可變寬度指令允許模棱兩可的解釋。例如。您可能會在指令中間插入“ JMP”,從而導致讀取單獨的有效指令序列。這對於固定寬度的指令集是不可能的,而固定寬度的指令集始終是RISC *(幾乎?)*。
是的,自修改代碼不是我所說的唯一問題,正如0xC0000022L進一步說明的那樣
*“自修改代碼不是唯一的問題” *-嗯,這甚至不是一個問題。在固定寬度的體系結構上,反彙編自修改代碼應該沒有問題。當然,反彙編器無法*在*運行時修改後*(這是一個無法確定的問題)*,彙編看起來會是什麼樣子,但這超出了反彙編二進製文件的範圍。
OllyDBG使用線性掃描嗎?
我非常確定可以,但是現在找不到該聲明的參考。我可能將它與SoftICE混合在一起。在答案中更正。謝謝!
debray
2013-08-04 20:26:15 UTC
view on stackexchange narkive permalink

除了前面的答案中描述的問題外,這是程序可以進行多個反彙編的另一種方式。考慮具有以下邏輯的代碼(我對語法屠殺深表歉意):

buf = ... 一些字符串 i> ...
val = 閱讀 b>()
如果 b>(val == 7){
mprotect b>(buf,...,PROT_EXEC); / *使buf可執行* /
goto b> buf; / *執行buf * /
}
其他 b> {
b> print b>(buf);
}

在這種情況下, buf 是代碼(因此應反彙編)還是數據(因此不應當反彙編)取決於輸入。因此,該程序具有多個可能的反彙編。請注意,這是因為代碼從根本上無法與數據區分開來,並且與RISC與CISC無關。

本文對如何將代碼偽裝成看似合理的數據進行了令人愉快的討論:

J。梅森(Mason),S.Small,F.Monrose,G.MacManus。英文Shellcode。伊利諾伊州芝加哥舉行的第16屆ACM計算機和通信安全ACM會議論文集(CCS)。 2009年11月。 [PDF]

0xC0000022L
2013-08-04 19:02:27 UTC
view on stackexchange narkive permalink

我認為該評論旨在描述的事實是,在CISC上,以x86為例,該反彙編有幾種可能的合理的表示形式。但是我認為這也可以部分地歸結為典型的RISC實現,在這種情況下-彙編程序可以提供類似CISC的助記符,由不同的基本(RISC)操作碼組合表示。

例如, rep 前綴在許多操作碼上是沒有意義的,但是在過去已被使用,這使得反向工程師更難。但是,對於最終要看拆卸的人來說,哪個代表更好?帶有 rep 前綴的一個或不帶前綴的(這反映了CPU的更多功能)。

如果反彙編程序保持其所找到的內容正確,則應包括 rep 前綴。另一方面,反彙編程序的任務是使機器代碼對人類可讀。因此,加倍努力並確保刪除多餘的前綴是為了簡化起見,這是非常有意義的。類似地,代表 nop 的多字節操作碼(無操作)可以被完全剝離,或者以一種與眾不同的方式來表示(例如IDA使用對齊字節),或者每個代表為 nop (分別用於1字節和5字節版本)。

所以從我的角度來看,反彙編機器代碼 是一門精確的科學,因為在那裡的每個操作碼是一個精確的助記符(在這裡忽略 jne / jnz 對偶)。否則,正如您所指出的那樣,CPU將如何處理並弄清楚該怎麼辦?但是,為了給人類逆向工程師提供表示,有時(我經常說嗎?)為了可讀性和理解性而對其進行了後期處理。在那裡,拆卸器和拆卸器會發生變化。


另一個問題是,為了遵循所有可能的代碼路徑,必須使用試探法(這是 good 反彙編程序的顯著特徵之一)。否則,很難將數據與代碼區分開。但是據我所知,“ CISC”不能因其特質而被選中,因此我認為這不是發表此評論的主要原因。

Codoka
2015-04-10 13:23:05 UTC
view on stackexchange narkive permalink

反彙編中的一個關鍵問題在於精確地將代碼與二進制可執行文件中的數據分離。為此,您將需要一個靜態分析,可以精確地確定間接跳轉(跳轉到運行時計算出的地址)的目標。間接跳轉的例子有很多,包括 switch 目標計算和C ++中的vtable。

這種類型的靜態分析(如果存在)將能夠解決已知無法確定的停頓問題。因此,這種靜態分析將不存在。基於此,拆卸工具需要依靠試探法和/或近似法來確定間接跳躍目標。關於此問題的更多詳細信息,請參見 ARM反彙編的可靠性,這裡有一個類似問題的答案。如果二進製文件被混淆和/或具有自修改代碼,則代碼/數據分離的任務將變得更加困難。

已從二進製文件中提取了代碼和數據的近似值。然後需要解決將語義附加到它們的問題,這也是困難的。例如, struct {int i;當由指令訪問時,int j;} 看起來類似於 int arr [2] 。因此,在不依賴諸如調試符號之類的外部信息的情況下,很難將唯一語義附加到執行的代碼上。

較大的數組可以與結構區分開來,因為它的元素是如何訪問的,甚至可能和您的`arr [2]小。
@Jongware同意。這只是一個簡單的示例,它說明了在反彙編中具有多種有效含義的想法。
perror
2017-06-20 22:03:54 UTC
view on stackexchange narkive permalink

初步說明

首先,我們需要就二進製程序的正確反彙編是什麼達成共識。我會提出以下定義:

二進製程序的正確反彙編將給出程序可能執行的所有可能指令集,無論其輸入如何

另一種表示方式是說,我們在每個可能接受的輸入上公開程序所有可能執行的指令。

停止問題

在這裡我們已經可以與圖靈機上的停止問題相提並論,可以將其定義為以下內容( Wikipedia):

停止問題是根據任意計算機程序的描述和輸入來確定程序將結束運行還是永遠繼續運行的問題。

這個(顯然)非常簡單的問題已被圖靈證明無法確定,這意味著,即使我們可以使用程序自動處理一定數量的病例,某些病理病例也總是會從我們的程序將無法告訴您機器/程序是否會在給定的輸入上停止。

當然,這種病理情況非常多(因此,沒有希望枚舉它們的機會)

回到我們的反彙編問題!

探究程序的所有可能路徑確實是不可確定的由於停頓問題 >!

的確,解決停頓問題的一種雙重方法是可訪問性問題,您要在其中想知道是否有輸入可以使您到達問題的特定點。程序。並且,知道程序是否可以到達內存中的特定位置並將其解釋為一條指令(,指令指針在某個時候獲取該地址的值)可訪問性問題。

因此,不可分解

但是,在現實世界中?

我知道,我知道,這只是數學...並非現實...大多數混淆(自願或非自願)都可以解決並自動從二進制代碼中刪除...

好吧,這主要是因為進行這些混淆的人並不習慣於不確定問題...

想像一下,您在程序中插入了一個無法確定的問題的計算,甚至只是一個足夠困難和復雜的東西,都會破壞應用於它的任何自動推理。 >舉個例子,讓我們以Collat​​z序列( Wikipedia)為例,推測該序列總是在1點後結束。但是,其背後的算術問題是如此復雜,以至於大約一個世紀以來就一直存在這種猜想……這是一個完美的不透明謂詞!當然,可能存在這種猜想的證明,但是這個問題非常複雜,足以開始在其上進行構建,並使計算機探索程序的狀態空間感到困惑。

事實上,這是當前的如今,在強力混淆方面的研究方向...我們幾乎已經用過以前使用過的小技巧,人們開始在有更好根據的問題上構建事物。即使我們在軟件混淆方面仍想與 Shannon (信息論之父)相提並論,以與密碼學進行比較。

最後一句話

因此,我們看到拆卸問題與停止問題緊密相關。而且,使用高度複雜的問題可能是現代軟件混淆的下一步。

我要說的最後一個事實是,如果我們必須堅持拆卸技術的最新水平,那麼當前的拆卸工具可能遠遠落後於我們可以做的事情。當我看到當前的史前工具是多麼的時候,我總是在痛苦中哭泣……但是將所有現代技術付諸實踐將需要大量的開發和維護工作,以致似乎沒有人願意為此做準備(但這只是我的本能。拙見)。



該問答將自動從英語翻譯而來。原始內容可在stackexchange上找到,我們感謝它分發的cc by-sa 3.0許可。
Loading...