PWN二进制安全修仙秘籍【第二章#二进制文件篇03】静态链接

devtools/2024/10/11 11:23:10/

各位观众姥爷是否还记得在这一章的第一节,我们介绍过一个概念叫链接,静态链接就是链接的一种,不记得的滚回去温习❗❗PWN二进制安全修仙秘籍【第二章#二进制文件篇01】从源代码到可执行文件icon-default.png?t=O83Ahttps://blog.csdn.net/weixin_53801131/article/details/142418934

 那么这一节主要讲静态链接的过程

1. 示例代码编写

以下内容完全抄袭以下文章,可以移步至下述文章进行阅读👇

静态链接与动态链接icon-default.png?t=O83Ahttps://blog.csdn.net/weixin_50749380/article/details/134931262

首先我们先创建一个目录叫做 test test 目录作为根目录,根目录下创建四个源文件,分别是 add.csub.cdiv.ctest.h

目录图长这样👇

 这四个源文件内容分别如下:

test.h头文件定义三种运算,注意头文件只进行定义👇

// test.h
#ifndef __TEST_H_
#define __TEST_H_
int add(int a,int b);
int sub(int a,int b);
int div(int a,int b);
#endif

 add.c文件用于计算变量a和b的和👇

// add.c
#include "test.h"
int add(int a,int b) {return a + b;
}

 sub.c文件用于计算a和b的差👇

// sub.c
#include "test.h"
int sub(int a,int b) {return a - b;
}

 div.c文件用于计算a和b的商👇

// div.c
#include "test.h"
int div(int a,int b) {return a / b;
}

注意区别include后面接的是方括号还是双引号:

include" "是先从程序源文件所在目录查找,若未找到,则去系统默认目录查找。

include<>直接去系统默认目录查找。

2. 编译示例代码

2.1 编译目标文件

将所有源文件都编译成目标文件:

gcc -c *.c

*.c表示所有以.c结尾的文件,也即所有的源文件。执行完该命令,会发现 test 目录中多了三个目标文件,分别是 add.osub.o 和 div.o

 2.2 打包成静态库文件

把所有目标文件打包成静态库文件:

ar rcs libtest.a *.o

*.o表示所有以.o结尾的文件,也即所有的目标文件。执行完该命令,发现 test 目录中多了一个静态库文件 libtest.a,大功告成。

 我们也可以通过以下命令查看静态链接库内的文件:

ar -t libtest.a

 

 2.3 规范化目录及文件

在比较规范的项目目录中;lib 文件夹一般用来存放库文件include 文件夹一般用来存放头文件src 文件夹一般用来存放源文件bin 文件夹一般用来存放可执行文件

|-- include
|   -- test.h
|-- lib
|  -- libtest.a
|-- src
|  -- main.c

main.c文件可以像下面这样使用 libtest.a 中的函数,main.c文件内容如下👇

#include <stdio.h>
#include "test.h"  //必须引入头文件
int main() {int m, n;printf("Input two numbers: ");scanf("%d %d", &m, &n);printf("%d+%d=%d\n", m, n, add(m, n));printf("%d-%d=%d\n", m, n, sub(m, n));printf("%d÷%d=%d\n", m, n, div(m, n));return 0;
}

在编译 main.c 的时候,我们需要使用-I(大写的字母i)选项指明头文件的包含路径;使用-L选项指明静态库的包含路径;使用-l(小写字母L)选项指明静态库的名字。所以,main.c 的完整编译命令为:

gcc src/main.c -I include/ -L lib/ -l test -o math

注意,使用-l选项指明静态库的名字时,既不需要lib前缀,也不需要.a后缀,只能写 testGCC 会自动加上前缀和后缀。

 

 运行编译生成后的文件

 发现除号好像错了,不管了,接着往下看

3. 静态链接的过程

静态链接做的最重要的两件事

符号解析

重定位

先说符号解析是什么

符号解析就是将每个符号(符号就是函数、全局变量、静态变量这些能变的)的引用与其定义进行关联。 

重定位又是什么咧?别急,我知道你很急,但你先别急

重定位就是将符号的定义与一个内存地址进行关联,然后修改这些符号的引用,让它指向这个内存地址。 

 3.1 分析静态链接过程的示例代码

下面再利用以下代码分析静态链接的过程

先是一个main.c文件,具体内容如下

//main.c
extern int shared;
extern void func(int *a, int *b);
int main() {int a=100;func(&a, &shared);return 0;
}

然后是func.c文件,具体内容如下

//func.c
int shared = 1;
int tmp = 0;
void func(int *a, int *b) {tmp = *a;*a = *b;*b= tmp;
}

使用以下命令对上述两个文件进行链接,形成一个可执行文件,并保留编译过程的中间文件。

gcc -static -fno-static-protector main.c func.c -save-temps --verbose -o func.ELF

下面这些都是我生成的文件

 3.2 对示例代码的地址进行分析

上面这么多中间文件,我们这里只看中间产物main.o和静态链接可执行文件func.ELF

使用objdump命令查看文件各个节的详细信息

这里我们对比来看,着重看.text、.data和.bss节

> objdump -h main.omain.o:     file format pe-x86-64Sections:
Idx Name          Size      VMA               LMA               File off  Algn0 .text         00000040  0000000000000000  0000000000000000  0000012c  2**4CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE1 .data         00000000  0000000000000000  0000000000000000  00000000  2**4ALLOC, LOAD, DATA2 .bss          00000000  0000000000000000  0000000000000000  00000000  2**4ALLOC
> objdump -h func.ELFfunc.ELF:     file format pei-x86-64Sections:
Idx Name          Size      VMA               LMA               File off  Algn0 .text         00001d18  0000000000401000  0000000000401000  00000400  2**4CONTENTS, ALLOC, LOAD, READONLY, CODE, DATA1 .data         000000e0  0000000000403000  0000000000403000  00002200  2**4CONTENTS, ALLOC, LOAD, DATA2 .bss          00000980  0000000000407000  0000000000407000  00000000  2**5ALLOC

解释一下,VMA(Virtual Memory Address)是虚拟地址

LMA(Local Memory Address)是加载地址

尚未进行链接的过程文件main.o的VMA都是0,而在链接完成后的func.ELF中,相似节被合并,且完成了虚拟地址的分配。

3.3 对中间文件main.o进行分析

使用objdump查看main.o的反汇编代码,参数“-mi386:intel”表示以intel格式输出

> objdump -d -M intel --section=.text main.omain.o:     file format pe-x86-64Disassembly of section .text:0000000000000000 <main>:0:   55                      push   rbp1:   48 89 e5                mov    rbp,rsp4:   48 83 ec 30             sub    rsp,0x308:   e8 00 00 00 00          call   d <main+0xd>d:   c7 45 fc 64 00 00 00    mov    DWORD PTR [rbp-0x4],0x6414:   48 8d 45 fc             lea    rax,[rbp-0x4]18:   48 8b 15 00 00 00 00    mov    rdx,QWORD PTR [rip+0x0]        # 1f <main+0x1f>1f:   48 89 c1                mov    rcx,rax22:   e8 00 00 00 00          call   27 <main+0x27>27:   b8 00 00 00 00          mov    eax,0x02c:   48 83 c4 30             add    rsp,0x3030:   5d                      pop    rbp31:   c3                      ret

在地址0x22处,可以看到main函数的地址从0开始,对func函数的调用在偏移0x27处。

0xe8是call指令的操作码,后四个字节是被调用函数相对于调用指令的下一条指令的偏移量;此时符号还没用重定位,相对偏移为0x00000000,通过计算call指令调用的地址为0x20+(-0)=0x20,要注意这只是一个临时地址。

3.4 对静态链接可执行文件func.ELF进行分析

同理,进行分析

>objdump -d -M intel --section=.text func.ELF | grep -A 16 "<main>"
0000000000401550 <main>:401550:       55                      push   rbp401551:       48 89 e5                mov    rbp,rsp401554:       48 83 ec 30             sub    rsp,0x30401558:       e8 23 01 00 00          call   401680 <__main>40155d:       c7 45 fc 64 00 00 00    mov    DWORD PTR [rbp-0x4],0x64401564:       48 8d 45 fc             lea    rax,[rbp-0x4]401568:       48 8b 15 21 2f 00 00    mov    rdx,QWORD PTR [rip+0x2f21]        # 404490 <.refptr.shared>40156f:       48 89 c1                mov    rcx,rax401572:       e8 19 00 00 00          call   401590 <func>401577:       b8 00 00 00 00          mov    eax,0x040157c:       48 83 c4 30             add    rsp,0x30401580:       5d                      pop    rbp➜  hehao objdump -d -M intel --section=.text func.ELF | grep -A 16 "<func>"
0000000000401590 <func>:401590:       55                      push   rbp401591:       48 89 e5                mov    rbp,rsp401594:       48 89 4d 10             mov    QWORD PTR [rbp+0x10],rcx401598:       48 89 55 18             mov    QWORD PTR [rbp+0x18],rdx40159c:       48 8b 45 10             mov    rax,QWORD PTR [rbp+0x10]

在地址0x401572处,调用func函数的call指令,其下一条mov指令位置0x401577,因此相对于mov指令偏移量为0x19的地址为0x401577+0x19=0x0x401590

3.5 分析重定位表

重定位表的作用是什么?

它是可重定位文件中最重要的内容,用于告诉链接器如何修改节的内容。每个重定位表对应一个需要被重定位的节,如下所示

> objdump -r main.omain.o:     file format pe-x86-64RELOCATION RECORDS FOR [.text]:
OFFSET           TYPE              VALUE
0000000000000009 R_X86_64_PC32     __main
000000000000001b R_X86_64_PC32     .refptr.shared
0000000000000023 R_X86_64_PC32     func

有三个偏移量,说明.text存在三个重定位入口。类型为R_X86_64_PC32说明为相对寻址(CPU将指令中编码的32位值加上PC下一条指令地址的值得到有效地址),还有一种是R_X86_64_32用于绝对寻址(直接使用在指令中编码的32位值作为有效地址)


http://www.ppmy.cn/devtools/124147.html

相关文章

机器学习:神经网络与深度学习的原理、应用场景及优缺点

一、神经网络与深度学习概述 深度学习是机器学习的一个分支领域&#xff0c;它通过构建具有多个层次的神经网络来自动学习数据的特征表示。神经网络是深度学习的基本模型结构&#xff0c;模拟了生物神经元之间的信息传递方式。 二、深度神经网络&#xff08;DNN&#xff09; …

【RabbitMQ——消息应答机制——分布式事务解决方式】

1. RabbitMQ高级-消息确认机制的配置 NONE值是禁用发布确认模式&#xff0c;是默认值 CORRELATED值是发布消息成功到交换器后会触发回调方法&#xff0c;如1示例SIMPLE值经测试有两种效果&#xff0c;其一效果和CORRELATED值一样会触发回调方法&#xff0c;其二在发布消息成功…

Hive3.x版本调优总结

文章目录 第 1 章 Explain 查看执行计划&#xff08;重点&#xff09;1.1 创建测试用表1&#xff09;建大表、小表和 JOIN 后表的语句2&#xff09;分别向大表和小表中导入数据 1.2 基本语法1.3 案例实操 第 2 章 Hive 建表优化2.1 分区表2.1.1 分区表基本操作2.1.2 二级分区2.…

【GeekBand】C++设计模式笔记7_Bridge_桥接模式

1. “单一职责”模式 在软件组件的设计中&#xff0c;如果责任划分的不清晰&#xff0c;使用继承得到的结果往往是随着需求的变化&#xff0c;子类急剧膨胀&#xff0c;同时充斥着重复代码&#xff0c;这时候的关键是划清责任。典型模式 DecoratorBridge 2. Bridge 桥接模式…

Matlab中实现类属性仅在首次创建类实例时初始化

背景描述: 在自定义类中&#xff0c;需要定义一些属性(标志位)用于触发某些方法&#xff0c;标志位只需要在类对象第一次实例化时赋初值&#xff0c;之后的值需要在特定的地方设置。怎样保证在不同实例中&#xff0c;标志位的值仅在特定的时候改变&#xff0c;其他时候保持不变…

Spring Cloud微服务

引言 在过去的几十年中&#xff0c;软件架构的发展经历了从单体应用到微服务的演变。微服务架构是一种将应用程序分解为小的、独立的服务的方法&#xff0c;每个服务可以独立地部署和扩展。Spring Cloud为开发和部署基于Spring的微服务提供了一系列的工具和框架&#xff0c;使…

malloc源码分析之 ----- 你想要啥chunk

文章目录 malloc源码分析之 ----- 你想要啥chunktcachefastbinsmall binunsorted binbin处理top malloc源码分析之 ----- 你想要啥chunk tcache malloc源码&#xff0c;这里以glibc-2.29为例&#xff1a; void * __libc_malloc (size_t bytes) {mstate ar_ptr;void *victim;vo…

Vue.js 计算属性

Vue.js 计算属性 Vue.js 是一款流行的前端JavaScript框架,以其简洁、灵活和高效的特点著称。在Vue.js中,计算属性(computed properties)是一种核心特性,它允许开发者定义依赖于其他数据的复杂逻辑,并且能够在数据变化时自动更新。本文将深入探讨Vue.js计算属性的工作原理…