关于静态链接和动态链接在操作系统中的一些过程

内容基本出自-程序员的自我修养

主要目的:编译SO库时最大限度地隐藏内部符号表,使得IDA pro等反编译工具看到的大部分函数都是一些无意义的名称,从而加大逆向分析难度。

使用动态编译的方式是为了节省内存和硬盘资源,动态编译的可执行文件需要so(share object)或者lld(Dynamic Link Library)文件,其中的符号表一般不会隐藏,

否则,链接器无法通过符号接口正确对object文件进行链接。

源文件引用未定义的其他文件符号时,将会被修正的位置用00000000(绝对位移调用,如全局变量)FFFFFFFC(相对位移调用,如函数)。

 

c++的全局对象在main函数之前初始化,main之后析构

linux程序的入口通常是_start,当程序与glibc库链接在一起最终形成可执行文件后,该函数是程序的初始化部门入口,

程序初始化部分完成一系列初始化过程后,会调用main函数来执行程序主体。main函数结束后,回到初始化部分,进行一些清理工作,

结束进程。

程序一些特定的操作必须在main函数之前执行,有些必须在main函数之后执行,ELF文件为此定义了两种特殊的段

.init 该段保存可执行指令,构成了进程的初始化代码。当一个程序开始运行时,在main函数调用前,glibc的初始化部分安排执行这个段中的代码(混淆解密代码也在这里执行)

.fini该段保存进程终止代码指令。当main函数正常退出时,Glibc会安排执行这个段中的代码。

 

 

静态库可以简单看成一组目标文件(obj)的集合

用ar压缩程序将这些目标文件压缩到一起,并进行编号和索引。如linux的usr/lib/libc.a

 

为什么静态运行库里面目标文件只包含一个函数?libc.a里面prinf.o只有printf()函数。

答:链接器在链接静态库的时候是以目标文件为单位的。如果很多函数放在同一个目标文件中,没有用到的函数会造成

空间的浪费。

 

windows内核文件 ntoskrnl.exe

链接过程控制一般由 链接控制参数或链接脚本完成 ,链接脚本以lds作为拓展名

 

默认的ld链接脚本放在usr/lib/ldscripts下

对于可执行文件,可以通过ld -s禁止链接器产生符号表或者strip命令去除程序中的符号表

 

动态库 也称运行时

c语言指针大小与虚拟空间的位数相同

32位系统下,程序能使用的空间能否超过4G?

虚拟空间,答案为否

计算机物理内存,答案为是,windows下用AWE申请超过4G的物理空间

linux则使用mmap()系统调用来实现。每个进程的0X0000000到0XC00000000为用户空间,3GB.其余为系统空间1GB

 

进程建立的过程

1、创建一个独立的虚拟地址空间

 

这一步将虚拟地址空间的各个页映射到相应的物理空间

2、读取可执行文件头,建立虚拟空间与可执行文件的映射关系

这一步做的是虚拟空间和可执行文件的映射关系,当操作系统捕捉到缺页错误时,它应知道程序当前所需要的页在可执行文件中的哪一个位置

 

由于可执行文件在装在时是被映射的虚拟空间,所以可执行文件很多时候又被叫做映像文件。

这种映射关系只是保存在操作系统内部的一个数据结构。

linux中将进程虚拟空间中的一个段叫做虚拟内存区域(VMA),如.text中的VMA,在虚拟空间中的地址为0x08048000-0x049000,对应elf文件中偏移为0的.text,并且只读

进程虚拟空间中的堆和栈也由VMA进行管理

3、将CPU指令寄存器设置成可执行文件入口,启动运行。

ELF文件头里包含程序的入口地址(程序装在进内存后的虚拟地址)

 

 

ELF文件中,段的组合根据段的权限决定

1、以代码段为代表的权限为可读可执行段

2、以数据段和BSS段为代表的可读可写段

3、以只读数据段为代表的只读段

对于权限相同的段,把他们合并到一起当作一个段进行映射(linux中一个段映射整数倍的页)

 

ELF可执行文件中有

program Header Table 用于保存segment 执行视图

Section Header 用于保存section链接视图

 

为什么要用动态链接?

静态链接过于浪费计算机内存和硬盘资源,尤其是内存空间

试想每个程序都有1MB的库文件,100个运行程序则有100份重复的库文件存于内存中

 

将链接过程推迟到运行时再进行,这就是动态链接。

动态链接还有一个特点是程序在运行时可以动态选择加载各种程序模块,即插件

程序与libc.so的链接工作是由动态链接器完成的,而不是静态链接器ld完成的

 

链接器如何知道foobar的引用是一个静态符号还是一个动态符号?

这实际上就是我们要用到lib.so的原因。lib.so中保存了完整的符号信息(因为运行时进行动态链接还须使用符号信息),把Lib.so也作为链接的

输入文件之一,链接器在解析符号时就可以知道,foobar是一个定义在Lib.so的动态符号。这样链接器就可以对foobar的引用做特殊的处理,使他成为一个对动态符号的引用。(为什么混淆要在lib.so中进行)

 

ld-2.6.1.so是linux下的动态链接器,在动态链接的可执行文件运行时,与普通共享对象一样被映射到了进程的地址空间,在系统开始运行程序前,先将进程控制器

交给动态链接器,由它完成所有动态链接工作以后再把控制权交给程序,然后开始执行

 

装载时重定位可以解决动态模块中有绝对地址引用,但是缺点是指令部分无法在多个进程之间共享,

用地址无关代码 PIC技术可以将指令中需要被修改的部分分离,跟数据部分放在一起,这样指令部分可以保持不变,而数据部分

可以在每个进程中拥有一个副本。

 

存放在数据段的GOT可以被修改,GOT中存有共享模块变量(静态变量 函数)的地址。

当两个进程同时调用lib.so库时,lib库中的数据段部分在每个进程中都有一份副本,和定义在程序内部的全局变量没什么区别。

改动lib库中的全局变量不会影响另一个程序。(线程则会影响)

 

PLT 延迟绑定

静态编译 编译时绑定地址

动态编译 装入时绑定(动态链接器进行绑定)

PLT延迟绑定 函数在被首次用到时进行绑定

PLT延迟绑定在 查询GOT进行跳转之前增加了一层间接跳转,通过一个叫PLT的项的结构进行跳转,每个外部函数在PLT中都有一个

相应的项。

 

在映射完可执行文件后,系统会先启动一个动态链接器,动态链接器工作完成后,交给可执行文件入口地址,程序执行

在动态链接的ELF可执行文件中,有一个专门的.interp段,保存该可执行文件所需动态链接器的路径。

 

.dynamic段

这个段保存了动态链接器需要的基本信息,如依赖那些共享对象,动态链接符号表的位置,动态链接重定位表的位置、共享对象初始化代码的地址等

 

 

导入导出函数的含义

 

动态链接器完成自举后,将可执行文件和链接器本身的符号表都合并到一个全局符号表中,若是共享模块中的符号表被去除,则无法对其进行正常链接。故可以通过对其混淆,

使得IDA pro等反编译工具看到的大部分函数都是一些无意义的名称,配合虚假控制流、指令替换、控制流扁平化等方法,从而加大逆向分析难度。

posted @ 2021-04-02 12:29  huhuf6  阅读(427)  评论(0编辑  收藏  举报