2016-07-14 77 views
2

我正在學習逆向工程的基礎。雖然倒車的crackme它發生在我身上看到這種模式在幾乎每一個函數的開頭:EBX寄存器用於內存訪問的模式是什麼?

0x08048766
pushl %ebp        
movl %esp, %ebp    
pushl %ebx    # because ebx is a callee-saved register 
subl $0x14,%esp  # of course $0x14 changes depending on the function 
calll 0x08048766 
addl $0x1a5f, %ebx  # also this value sometime changes depending on the function 

哪裏有,不只是這一個功能:

movl 0(%esp), %ebx   
retl 

所以基本上,這是正常的,每個函數首先初始化寄存器ebpesp。然後寄存器ebx被推入堆棧,這也是爲ebx完全可以理解的是一個被調用方保存寄存器,它隨後用於在函數來引用一些靜態數據(從.rodata),例如:

leal -0x17b7(%ebx), %eax 
movl %eax, 0(%esp) 
calll printf 

現在最有趣的(也是我不明白的)部分:如果我理解正確,ebx首先用esp(這個使用函數0x08048766)指出的值初始化,爲什麼?那裏有什麼?這不是一個未初始化的問題嗎?

然後將另一個值添加到ebx。這個值代表什麼?

我想更好地瞭解在這種情況下如何使用寄存器ebx以及如何計算它指向的地址。

您可以看看完整的程序here,但不幸的是沒有任何C源代碼可用。

+1

@Abhineet都能跟得上。 OP瞭解這些。正如你在我的回答中看到的那樣,'ebx'是這個代碼中的PIC寄存器,你在那個wikipedia頁面上找不到任何有關PIC的信息。您的評論在這裏沒有用,抱歉。 –

+0

@JonathonReinhart好吧,所以我明白我做了一個無用的評論。你能否發佈一些鏈接/參考文獻來閱讀關於GOT和PIC的更多信息? PIC是否與Windows中的ASLR相同?是-fPIC ==/DYNAMICBASE(VC++)? – Abhineet

+3

在Windows上不需要PIC。 PE文件是可重定位的 - 加載程序在加載時修復它們(其中包括對全局變量的內部引用)。 ['/DYNAMICBASE'](https://msdn.microsoft.com/en-us/library/ff820372.aspx)不會改變任何東西,除非在標題中設置一個標誌來告訴加載器它可以隨意地隨機在運行時重新發布。 –

回答

7

此代碼似乎已編譯爲-fPIC。 PIC代表「與位置無關的代碼」,這意味着它可以加載到任何地址,並且仍然能夠訪問它的全局變量。

在這種情況下,ebx被稱爲PIC寄存器,它被用來指向GOT(全局偏移表)的末尾。 GOT已經(從程序的基地址*)偏移到正在使用的每個全局變量。

很多時候,瞭解這些事情的最好方法是自己編譯一些代碼,然後查看輸出。當你有你的符號時,它特別容易。

讓我們做一個實驗:

照片。ç

int global; 

int main(void) 
{ 
    global = 4; 
    return 0; 
} 

編譯

$ gcc -v 
... 
gcc version 5.3.1 20160406 (Red Hat 5.3.1-6) (GCC) 

$ gcc -m32 -Wall -Werror -fPIC -o pic pic.c 

(略)

$ readelf -S pic 
Section Headers: 
    [Nr] Name    Type   Addr  Off Size ES Flg Lk Inf Al 
    [13] .text    PROGBITS  080482f0 0002f0 000182 00 AX 0 0 16 
    [15] .rodata   PROGBITS  08048488 000488 00000c 00 A 0 0 4 
    [22] .got    PROGBITS  08049ffc 000ffc 000004 04 WA 0 0 4 
    [23] .got.plt   PROGBITS  0804a000 001000 000014 04 WA 0 0 4 
    [24] .data    PROGBITS  0804a014 001014 000004 00 WA 0 0 1 
    [25] .bss    NOBITS   0804a018 001018 000008 00 WA 0 0 4 

拆卸(Intel語法,因爲AT &牛逼驅使我堅果)

$ objdump -Mintel -d --no-show-raw-insn pic 

080483eb <main>: 
80483eb: push ebp 
80483ec: mov ebp,esp 
80483ee: call 804840b <__x86.get_pc_thunk.ax> ; EAX = EIP + 5 
80483f3: add eax,0x1c0d   ; EAX = 0x804a000 (.got.plt, end of .got) 
80483f8: lea eax,[eax+0x1c]  ; EAX = 0x804a01C (.bss + 4) 

80483fe: mov DWORD PTR [eax],0x4 ; set `global` to 4 
8048404: mov eax,0x0 
8048409: pop ebp 
804840a: ret  

0804840b <__x86.get_pc_thunk.ax>: 
804840b: mov eax,DWORD PTR [esp] 
804840e: ret  
804840f: nop 

說明

在這種情況下,我的GCC決定使用eax作爲PIC寄存器,而不是ebx

另外,請注意,編譯器(GCC 5.3.1)在這裏做了一些有趣的事情。它不是通過GOT訪問變量,它主要使用GOT作爲「錨點」,而是直接偏移到.bss部分中的變量。


返回代碼:

pushl %ebp        
movl %esp, %ebp    
pushl %ebx    ; because ebx is a callee-saved register 
subl $0x14,%esp  ; end of typical prologue 

calll 0x08048766  ; __i686_get_pc_thunk_bx 
         ; Gets the current value of EIP after this call into EBX. 
         ; There is no other way to do this in x86 without a call 

addl $0x1a5f, %ebx  ; Add the displacement to the end of the GOT. 
         ; This displacement of course changes depending on 
         ; where the function is. 
         ; EBX now points to the end of the GOT. 

leal -0x17b7(%ebx), %eax ; EAX = EBX - 0x17b7 
movl %eax, 0(%esp)   ; Put EAX on stack (arg 0 to printf) 
          ; EAX should point to some string 
calll printf 

在你的代碼也,它實際上並沒有「用」 GOT(否則,我們將看到第二存儲器解除參考);它將其用作字符串的錨點,可能位於GOT之前的只讀數據部分(.rodata)。

如果你看一下在功能0x08048766,你會看到它看起來是這樣的:

mov (%esp),%eax ; Put return address (pushed onto stack by call insn) 
        ; in eax 
ret     ; Return 
+0

這是一個非常詳細和明確的答案,毫無疑問,我可以找到最好的答案。 謝謝! (只是一點修正:EAX + 0x804a01C應該是EAX = 0x804a01C,我認爲) – TTK

+0

@TTK不客氣。感謝您指出了這一點;我修復了它。我強烈建議編寫代碼(比我在這裏展示的更大,更復雜的程序),並將其拆解。 –

+0

@TTK另外,您可以像我一樣查看節標題('readelf -S')來驗證我對您的程序的分析。只有你提供的反彙編(這對你的問題是足夠的,不用擔心),很難說清楚。但它是有道理的,因爲'printf'需要一個指向字符串的指針(不是GOT條目)。 –

相關問題