有点混乱
PIC/PIE
PIC ( position independent code ) 一开始是用于动态库的加载的,这么多动态库肯定得服从任意的地址分配,而二进制文件的位置是固定的。后来发现 ASLR 的效果很好,希望引入 PIE ( position independent execute )让二进制的地址也随机化。
在 gcc manual 13.2 中有关选项的解释如下
static
On systems that support dynamic linking, this overrides -pie and prevents linking with the shared libraries. On other systems, this option has no effect.
不和共享库链接
static-pie
Produce a static position independent executable on targets that support it. A static position independent executable is similar to a static executable, but can be loaded at any address without a dynamic linker. For predictable results, you must also specify the same set of options used for compilation (-fpie, -fPIE, or model suboptions) when you specify this linker option.
不需要动态库在任意地址加载
fpic
-fpic Generate position-independent code (PIC) suitable for use in a shared library, if supported for the target machine. Such code accesses all constant addresses through a global offset table (GOT). The dynamic loader resolves the GOT entries when the program starts (the dynamic loader is not part of GCC; it is part of the operating system). If the GOT size for the linked executable exceeds a machine-specific maximum size, you get an error message from the linker indicating that -fpic does not work; in that case, recompile with -fPIC instead. (These maximums are 8k on the SPARC, 28k on AArch64 and 32k on the m68k and RS/6000. The x86 has no such limit.) Position-independent code requires special support, and therefore works only on certain machines. For the x86, GCC supports PIC for System V but not for the Sun 386i. Code generated for the IBM RS/6000 is always position-independent. When this flag is set, the macros `__pic__` and `__PIC__` are defined to 1. -fPIC If supported for the target machine, emit position-independent code, suitable for dynamic linking and avoiding any limit on the size of the global offset table. This option makes a difference on AArch64, m68k, PowerPC and SPARC. Position-independent code requires special support, and therefore works only on certain machines. When this flag is set, the macros `__pic__` and `__PIC__` are defined to 2. -fpie -fPIE These options are similar to -fpic and -fPIC, but the generated position-independent code can be only linked into executables. Usually these options are used to compile code that will be linked using the -pie GCC option. -fpie and -fPIE both define the macros `__pie__` and `__PIE__`. The macros have the value 1 for -fpie and 2 for -fPIE.
PIC 需要通过 GOT 表到达地址,dnamic loader 会在程序启动/程序过程中解析 GOT 表。
PIE 与 PIC 的概念接近,但只能被链接到可执行文件。
RELRO
可以向 dynamic loader 指定在什么时候解析 GOT。在 man ld 里可以看见
lazy
When generating an executable or shared library, mark it to tell the dynamic linker to defer function call resolution to the point when the function is called (lazy binding), rather than at load time.Lazy binding is the default.
lazy binding 是默认的
now
When generating an executable or shared library, mark it to tell the dynamic linker to resolve all symbols when the program is started, or when the shared library is loaded by dlopen, instead of deferring function call resolution to the point when the function is first called.
但是我们用 gcc 不加 -z
选项编译的文件是 FULL RELRO。gcc 是默认会传递 -z now 参数 默认是 full relro
后面的东西是随手贴上去的
master of orw 复现
用 syscall 接收远程文件跳转执行
gcc -o test -static -fno-stack-protector -no-pie -O3 ./lib_uring_test.c -luring
-static
可重定位文件中,符号的地址还没有填写上去(还是 0)
pic 的实现
在 x64 中,开了 PIC 之后,数据和函数的地址是通过 RIP 的偏移计算的。测试一下。
int d = 3;
int add(int a, int b){
return a+b;
}
int main(){
int x = add(d, 3);
}
汇编如下:
pwndbg> disass /r main
Dump of assembler code for function main:
0x0000000000001141 <+0>: f3 0f 1e fa endbr64
0x0000000000001145 <+4>: 55 push rbp
0x0000000000001146 <+5>: 48 89 e5 mov rbp,rsp
0x0000000000001149 <+8>: 48 83 ec 10 sub rsp,0x10
0x000000000000114d <+12>: 48 8d 05 bc 2e 00 00 lea rax,[rip+0x2ebc] # 0x4010 <d>
0x0000000000001154 <+19>: 8b 00 mov eax,DWORD PTR [rax]
0x0000000000001156 <+21>: be 03 00 00 00 mov esi,0x3
0x000000000000115b <+26>: 89 c7 mov edi,eax
0x000000000000115d <+28>: e8 c7 ff ff ff call 0x1129 <add>
0x0000000000001162 <+33>: 89 45 fc mov DWORD PTR [rbp-0x4],eax
0x0000000000001165 <+36>: b8 00 00 00 00 mov eax,0x0
0x000000000000116a <+41>: c9 leave
0x000000000000116b <+42>: c3 ret
End of assembler dump.
0x115d
处的 call add 的语句是 e8 cd ff ff ff
即跳转到(当前地址 - 0x2e)的地方。
0x114d
是通过 rip 来相对寻址的
emmm 下面是不开 -fPIC 的我怎么觉得没什么差别
pwndbg> disass /r main
Dump of assembler code for function main:
0x0000000000001141 <+0>: f3 0f 1e fa endbr64
0x0000000000001145 <+4>: 55 push rbp
0x0000000000001146 <+5>: 48 89 e5 mov rbp,rsp
0x0000000000001149 <+8>: 48 83 ec 10 sub rsp,0x10
0x000000000000114d <+12>: 8b 05 bd 2e 00 00 mov eax,DWORD PTR [rip+0x2ebd] # 0x4010 <d>
0x0000000000001153 <+18>: be 03 00 00 00 mov esi,0x3
0x0000000000001158 <+23>: 89 c7 mov edi,eax
0x000000000000115a <+25>: e8 ca ff ff ff call 0x1129 <add>
0x000000000000115f <+30>: 89 45 fc mov DWORD PTR [rbp-0x4],eax
0x0000000000001162 <+33>: b8 00 00 00 00 mov eax,0x0
0x0000000000001167 <+38>: c9 leave
0x0000000000001168 <+39>: c3 ret
End of assembler dump.
还有一些问题待解决:
- 开启 pie 的程序是如何随机的?
- static link 包括那些库函数会被包括进来