此代碼似乎已編譯爲-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
@Abhineet都能跟得上。 OP瞭解這些。正如你在我的回答中看到的那樣,'ebx'是這個代碼中的PIC寄存器,你在那個wikipedia頁面上找不到任何有關PIC的信息。您的評論在這裏沒有用,抱歉。 –
@JonathonReinhart好吧,所以我明白我做了一個無用的評論。你能否發佈一些鏈接/參考文獻來閱讀關於GOT和PIC的更多信息? PIC是否與Windows中的ASLR相同?是-fPIC ==/DYNAMICBASE(VC++)? – Abhineet
在Windows上不需要PIC。 PE文件是可重定位的 - 加載程序在加載時修復它們(其中包括對全局變量的內部引用)。 ['/DYNAMICBASE'](https://msdn.microsoft.com/en-us/library/ff820372.aspx)不會改變任何東西,除非在標題中設置一個標誌來告訴加載器它可以隨意地隨機在運行時重新發布。 –