2016-12-14 131 views
2

我目前正在研究二進制炸彈分配的階段2。我無法準確解讀某個函數在被調用時會發生什麼。我一直堅持了幾天。解密x86彙編函數

功能是:

它被調用在較大功能 「phase_2」:

0000000000400f4d <phase_2>: 
    400f4d: 53      push %rbx 
    400f4e: 48 83 ec 60    sub $0x60,%rsp 
    400f52: 48 c7 44 24 30 00 00 movq $0x0,0x30(%rsp) 
    400f59: 00 00 
    400f5b: 48 c7 44 24 38 00 00 movq $0x0,0x38(%rsp) 
    400f62: 00 00 
    400f64: 48 c7 44 24 40 00 00 movq $0x0,0x40(%rsp) 
    400f6b: 00 00 
    400f6d: 48 c7 44 24 48 00 00 movq $0x0,0x48(%rsp) 
    400f74: 00 00 
    400f76: 48 c7 44 24 50 00 00 movq $0x0,0x50(%rsp) 
    400f7d: 00 00 
    400f7f: 48 c7 04 24 00 00 00 movq $0x0,(%rsp) 
    400f86: 00 
    400f87: 48 c7 44 24 08 00 00 movq $0x0,0x8(%rsp) 
    400f8e: 00 00 
    400f90: 48 c7 44 24 10 00 00 movq $0x0,0x10(%rsp) 
    400f97: 00 00 
    400f99: 48 c7 44 24 18 00 00 movq $0x0,0x18(%rsp) 
    400fa0: 00 00 
    400fa2: 48 c7 44 24 20 00 00 movq $0x0,0x20(%rsp) 
    400fa9: 00 00 
    400fab: 48 8d 4c 24 58   lea 0x58(%rsp),%rcx 
    400fb0: 48 8d 54 24 5c   lea 0x5c(%rsp),%rdx 
    400fb5: be 9e 26 40 00   mov $0x40269e,%esi 
    400fba: b8 00 00 00 00   mov $0x0,%eax 
    400fbf: e8 6c fc ff ff   callq 400c30 <[email protected]> 
    400fc4: 83 f8 02    cmp $0x2,%eax 
    400fc7: 74 05     je  400fce <phase_2+0x81> 
    400fc9: e8 c1 06 00 00   callq 40168f <explode_bomb> 
    400fce: 83 7c 24 5c 64   cmpl $0x64,0x5c(%rsp) 
    400fd3: 76 07     jbe 400fdc <phase_2+0x8f> 
    400fd5: 83 7c 24 58 64   cmpl $0x64,0x58(%rsp) 
    400fda: 77 05     ja  400fe1 <phase_2+0x94> 
    400fdc: e8 ae 06 00 00   callq 40168f <explode_bomb> 
    400fe1: 48 8d 74 24 30   lea 0x30(%rsp),%rsi 
    400fe6: 8b 7c 24 5c    mov 0x5c(%rsp),%edi 
    400fea: e8 3b ff ff ff   callq 400f2a <func2a> 
    400fef: 48 89 e6    mov %rsp,%rsi 
    400ff2: 8b 7c 24 58    mov 0x58(%rsp),%edi 
    400ff6: e8 2f ff ff ff   callq 400f2a <func2a> 
    400ffb: bb 00 00 00 00   mov $0x0,%ebx 
    401000: 8b 04 1c    mov (%rsp,%rbx,1),%eax 
    401003: 39 44 1c 30    cmp %eax,0x30(%rsp,%rbx,1) 
    401007: 74 05     je  40100e <phase_2+0xc1> 
    401009: e8 81 06 00 00   callq 40168f <explode_bomb> 
    40100e: 48 83 c3 04    add $0x4,%rbx 
    401012: 48 83 fb 28    cmp $0x28,%rbx 
    401016: 75 e8     jne 401000 <phase_2+0xb3> 
    401018: 48 83 c4 60    add $0x60,%rsp 
    40101c: 5b      pop %rbx 
    40101d: c3      retq 

我徹底明白了什麼phase_2是幹什麼的,我只是不明白是什麼func2a在做什麼以及它如何影響0x30(%rsp)的值等等。因此,我總是在0x401003處進行比較聲明,最終炸彈在那裏爆炸。

我的問題是我不明白輸入(相位解決方案)如何通過func2a影響0x30(%rsp)的值。

回答

11
400f2a: 85 ff     test %edi,%edi 
    400f2c: 74 1d     je  400f4b <func2a+0x21> 

這僅僅是當edi爲零提前退出(je相同jz)。

400f2e: b9 cd cc cc cc   mov $0xcccccccd,%ecx 
    400f33: 89 f8     mov %edi,%eax 
    400f35: f7 e1     mul %ecx 
    400f37: c1 ea 03    shr $0x3,%edx 

這是一個經典的優化技巧;它是除以乘以逆的整數算術等價值(見here for details);在實踐中,這裏說的是edx = edi/10;

400f3a: 8d 04 92    lea (%rdx,%rdx,4),%eax 
    400f3d: 01 c0     add %eax,%eax 

這就是利用lea執行算術(和它在Intel語法的方式更清楚,在那裏它是lea eax,[rdx+rdx*4] =>eax = edx*5),然後求和以本身的結果。這一切歸結爲eax = edx*10

400f3f: 29 c7     sub %eax,%edi 

然後,將它減去edi


因此,所有這一切是計算edi最後一個小數位數複雜(但快)的方式;我們到現在爲止什麼是一樣的東西:

void func2a(unsigned edi) { 
    if(edi==0) return; 
label1: 
    edx=edi/10; 
    edi%=10; 
    // ... 
} 

label1:是存在的,因爲400f33是後來的跳轉目標)


去上:

400f41: 83 04 be 01    addl $0x1,(%rsi,%rdi,4) 

再次,這是方法在英特爾語法中對我更加清楚 - add dword [rsi+rdi*4],byte +0x1。它是一個32位數組的常規增量intrdi乘以4);因此,我們可以設想rsi指向一個整數數組,用剛纔計算的edi的最後一位索引。

void func2a(unsigned edi, int rsi[]) { 
    if(edi==0) return; 
label1: 
    edx=edi/10; 
    edi%=10; 
    rsi[edi]++; 
} 

然後:

400f45: 89 d7     mov %edx,%edi 
    400f47: 85 d2     test %edx,%edx 
    400f49: 75 e8     jne 400f33 <func2a+0x9> 

移動我們上面edi計算的分工和循環的結果,如果它是不同於零。

400f4b: f3 c3     repz retq 

返回(使用an unusual encoding of the instruction that is optimal for certain AMD processors)。


因此,通過與while循環重寫跳躍,並給予一些有意義的名字......

// number is edi, digits_count is rsi, as per regular 
// x64 SystemV calling convention 
void count_digits(unsigned number, int digits_count[]) { 
    while(number) { 
     digits_count[number%10]++; 
     number/=10; 
    } 
} 

也就是說,這是一個函數,給定一個整數,計數單的出現通過遞增digits_count陣列中的相應存儲桶來生成十進制數字。


有趣的事實:如果我們給上面gcc C代碼(幾乎所有最新版本的-O1we obtain back exactly the assembly you provided

+3

「如果我們將上面的C代碼提供給gcc,我們會返回到您提供的程序集。」 - >完美的逆向工程。很好的答案! –