此代码似乎已编译为-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)不会改变任何东西,除非在标题中设置一个标志来告诉加载器它可以随意地随机在运行时重新发布。 –