2013-01-13 247 views
12

我正在努力尋找能夠幫助我的管理層理解對已編譯的C代碼進行反向工程是多麼困難/容易的事實。反編譯彙編代碼有多難?

本網站提出過類似問題(例如,請參閱Is it possible to 「decompile」 a Windows .exe? Or at least view the Assembly?Possible to decompile DLL written in C?),但這些問題的要點是反編譯的C代碼是「很難但並非完全不可能」。

爲了方便實際的答案,我包含了一個神祕函數的編譯代碼,我建議這個問題的答案衡量所提出的技術的成敗,看他們是否可以確定這個函數是什麼確實。這對於SO來說可能並不常見,但我認爲這是獲得這個工程問題的「良好主觀」或事實答案的最佳方式。因此,什麼是你最好的猜測這個功能在做什麼,以及如何?

這是編譯後的代碼,編譯Mac OSX上用gcc:

_mystery: 
Leh_func_begin1: 
    pushq %rbp 
Ltmp0: 
    movq %rsp, %rbp 
Ltmp1: 
    movsd LCPI1_0(%rip), %xmm1 
    subsd %xmm0, %xmm1 
    pxor %xmm2, %xmm2 
    ucomisd %xmm1, %xmm2 
    jbe  LBB1_2 
    xorpd LCPI1_1(%rip), %xmm1 
LBB1_2: 
    ucomisd LCPI1_2(%rip), %xmm1 
    jb  LBB1_8 
    movsd LCPI1_0(%rip), %xmm1 
    movsd LCPI1_3(%rip), %xmm2 
    pxor %xmm3, %xmm3 
    movsd LCPI1_1(%rip), %xmm4 
    jmp  LBB1_4 
    .align 4, 0x90 
LBB1_5: 
    ucomisd LCPI1_2(%rip), %xmm1 
    jb  LBB1_9 
    movapd %xmm5, %xmm1 
LBB1_4: 
    movapd %xmm0, %xmm5 
    divsd %xmm1, %xmm5 
    addsd %xmm1, %xmm5 
    mulsd %xmm2, %xmm5 
    movapd %xmm5, %xmm1 
    mulsd %xmm1, %xmm1 
    subsd %xmm0, %xmm1 
    ucomisd %xmm1, %xmm3 
    jbe  LBB1_5 
    xorpd %xmm4, %xmm1 
    jmp  LBB1_5 
LBB1_8: 
    movsd LCPI1_0(%rip), %xmm5 
LBB1_9: 
    movapd %xmm5, %xmm0 
    popq %rbp 
    ret 
Leh_func_end1: 

UPDATE

@Igor Skochinsky是第一個找到正確的答案:這的確是一個天真的實現Heron算法計算平方根。原來的源代碼是在這裏:

#include <stdio.h> 

#define EPS 1e-7 

double mystery(double x){ 
    double y=1.; 
    double diff; 
    diff=y*y-x; 
    diff=diff<0?-diff:diff; 
    while(diff>=EPS){ 
    y=(y+x/y)/2.; 
    diff=y*y-x; 
    diff=diff<0?-diff:diff; 
    } 
    return y; 
} 

int main() { 
    printf("The square root of 2 is %g\n", mystery(2.)); 
} 
+15

你有7k +的聲望和地址「網站版主」?你沒有研究過這個網站的工作原理嗎? –

+0

我想知道如果我現在應該啓動meta.so線程來處理此問題的合法性... – djechlin

+0

可惜我沒有hexrays反編譯器許可證。我懷疑這代碼很簡單。 – CodesInChaos

回答

15

下面是反編譯的結果與Hex-Rays Decompiler後,我轉換代碼,以86(它不支持在目前64位),增加了一些數據定義在原崗位缺失,和組裝它:

//------------------------------------------------------------------------- 
// Data declarations 

double LCPI1_0 = 1.0; // weak 
double LCPI1_1[2] = { 0.0, 0.0 }; // weak 
double LCPI1_2 = 1.2; // weak 
double LCPI1_3 = 1.3; // weak 


//----- (00000000) -------------------------------------------------------- 
void __usercall mystery(__m128d a1<xmm0>) 
{ 
    __m128d v1; // [email protected] 
    __m128d v2; // [email protected] 
    __int128 v3; // [email protected] 
    __m128d v4; // [email protected] 
    __m128d v5; // [email protected] 

    v1 = (__m128d)*(unsigned __int64 *)&LCPI1_0; 
    v1.m128d_f64[0] = LCPI1_0 - a1.m128d_f64[0]; 
    if (LCPI1_0 - a1.m128d_f64[0] < 0.0) 
    v1 = _mm_xor_pd(v1, *(__m128d *)LCPI1_1); 
    if (v1.m128d_f64[0] >= LCPI1_2) 
    { 
    v2 = (__m128d)*(unsigned __int64 *)&LCPI1_0; 
    v3 = *(unsigned __int64 *)&LCPI1_3; 
    while (1) 
    { 
     v4 = a1; 
     v4.m128d_f64[0] = (v4.m128d_f64[0]/v2.m128d_f64[0] + v2.m128d_f64[0]) * *(double *)&v3; 
     v5 = v4; 
     v5.m128d_f64[0] = v5.m128d_f64[0] * v5.m128d_f64[0] - a1.m128d_f64[0]; 
     if (v5.m128d_f64[0] < 0.0) 
     v5 = _mm_xor_pd(a1, (__m128d)*(unsigned __int64 *)LCPI1_1); 
     if (v5.m128d_f64[0] < LCPI1_2) 
     break; 
     v2 = a1; 
    } 
    } 
} 
// 90: using guessed type double LCPI1_0; 
// 98: using guessed type double LCPI1_1[2]; 
// A8: using guessed type double LCPI1_2; 
// B0: using guessed type double LCPI1_3; 

// ALL OK, 1 function(s) have been successfully decompiled 

顯然,它可以使用一些改善(XMM支持是有些基本的現在),但我認爲基本的算法已經是可以理解的。

編輯:因爲很明顯只有所有XMM寄存器的低位雙倍被使用,所以看起來這個函數實際上可以用於標量雙精度而不是矢量。至於_mm_xor_pd(xorpd)內部函數,我認爲這只是編譯器實現符號反轉的方式 - 通過用一個預定義的常量進行xoring,其中符號位位置爲1,其他位置爲0。考慮到上述情況,並在進行一些清理之後,我得到以下代碼:

double mystery(double a1) 
{ 
    double v1; // [email protected] 
    double v2; // [email protected] 
    double v3; // [email protected] 
    double v4; // [email protected] 
    double v5; // [email protected] 

    v1 = LCPI1_0 - a1; 
    if (v1 < 0.0) 
    v1 = -v1; 
    if (v1 < LCPI1_2) 
    { 
    v4 = LCPI1_0; 
    } 
    else 
    { 
    v2 = LCPI1_0; 
    v3 = LCPI1_3; 
    while (1) 
    { 
     v4 = a1; 
     v4 = (v4/v2 + v2) * v3; 
     v5 = v4; 
     v5 = v5 * v5 - a1; 
     if (v5 < 0.0) 
     v5 = -v5; 
     if (v5 < LCPI1_2) 
     break; 
     v2 = a1; 
    } 
    } 
    return v4; 
} 

它生成的組件與原始帖子非常相似。

+0

因此,對於此代碼是做什麼*的最佳猜測是什麼?我認爲你需要在低級代碼恢復的基礎上進行算法識別。 PS:不錯的工作逆向工程到你得到的地方,+1儘管被關閉:) –

+0

看起來像平方根計算的巴比倫方法。 LCPI1_0是初始近似值,LCPI1_2是ε,LCPI1_3是常數0.5。 –

+0

@IgorSkochinsky恭喜你,你該死! – lindelof

6

對任何代碼進行反向工程/反編譯都是需要花費的時間和這樣做的好處的問題;沒有多難。

如果你有一些絕對不能允許的祕密醬汁,那麼你唯一可以做的就是將這個祕密醬汁作爲一種網絡服務,必要時會被調用。這樣的二進制文件永遠不會離開你的企業牆。

甚至一旦黑客在他們控制的系統上編譯了二進制文件,任何事情都可以被追蹤。哎呀,原始的PC克隆是通過對IBM BIOS進行逆向工程創建的。

所以,回到關鍵點:再說一遍,這不是一個問題:問題是否有人想嘗試......這是基於他們從中獲得的感知價值的問題。無論是直接美元(接收還是儲蓄),競爭優勢還是乾脆吹噓權利。更復雜的是應用程序的可用性:更廣泛的分佈意味着更高的潛力,可以發現它正在成爲黑客工作的一部分。

如果這些值存在,那麼你可以放心,有人會嘗試,他們會成功。哪個應該引導你到下一個問題:如果他們這樣做會怎樣?最糟糕的結果是什麼?

在某些情況下,它只是一個丟失銷售,你可能還沒有得到。在其他情況下,這可能是業務的損失。

1

從根本上講,做單獨的機器指令「逆向工程」非常容易,因爲機器指令具有非常明確的語義。這會給你錯誤的C代碼,但肯定不是目標。 (知道文件中的某些二進制模式是,機器指令在技術上是圖靈硬的,例如在某些情況下是不可能的;在編譯器生成的代碼的情況下不太可能)。

除此之外,您正在嘗試推斷算法和意圖。這非常困難;包含所有這些知識從哪裏來?

您可能會發現我的 paper on reverse engineering有趣。它提出了一種編碼必要知識的方法。在一定程度上也有commercial tools to do this。據我的理解,這並不像我的論文所概述的方案那樣遠,但仍然產生非常合理的C代碼。 (我沒有使用這個工具的具體經驗,但對作者和他的工具非常尊重)。