安装工具

1
2
3
4
5
6
7
8
9
10
ida pro
ghidra
pwntools zio
one_gadget ROPgadget ropper
pwndbg peda gef
checksec
qemu busybox seccomp-tools
pathelf main_arena_offset
windbg ollydbg x96dbg
LibcSearcher(libc_database) glibc_all_in_one

C程序形成过程

编译原理

GCC基本使用

  • 初步入门GCC的使用,也顺便了解一下源文件到可执行文件的过程。

  • GCC全称(GNU Compiler Collection)

  • GCC官网(MinGW)[MinGW-w64]

  • 可以去GCC官网,查看官方文档,深入了解GCC

GCC安装

下载GCC

  • 在Linux环境下安装GCC
1
2
3
sudo apt install gcc g++
# 安装完成后检查是否成功安装输入以下指令
gcc --version
  • 在Windows下安装GCC
  • Windows下安装的GCC是,MinGW(Minimalist GNU for Windows),官网在上面。
  • 进入官网后点击下面的红色框

image-20240316205127956

  • 跳转页面后下滑找到下图的红色框,点击红色框最左边的

image-20240316205216990

  • 跳转后再点击红色框里面的,跳转到github上下载

image-20240316205302679

  • 选择下图红色框版本安装,听说13.2.0版本有点bug,可以先下载12.2.0版本

image-20240316205413090

配置系统环境变量

  • 下载压缩包到自己指定的文件夹中,再将压缩包解压后,点击进去文件夹,进入到bin目录里面去,bin目录的地址

image-20240316210025564

  • 点击右键点击此电脑,属性,找到高级系统设置

image-20240316210210205

  • 按照下图红框依次点击

image-20240316210304384

  • 完成上图后,会出现如下页面,点击新建,将bin目录的绝对地址复制进去

image-20240316210420002

检查GCC安装是否成功

  • 添加完环境变量后就按 Ctrl + R,输入 cmd,打开终端即可
  • 然后输入gcc --version ,出现如下内容就表面gcc安装成功了

image-20240316210703755

编译单个文件

  • 用C语言和C++分别写两个程序进行编译
1
2
3
4
5
6
7
#include<stdio.h>
int main(void)
{
printf("hello world!");

return 0;
}
1
2
3
4
5
6
7
8
9
#include<iostream>
using namespace std;

int main()
{
std::cout<<"Hello World From CPP"<< std::endl;

return 0;
}
  • 两个程序的目录下,输入cmd指令,终端就会在该目录下运行

image-20240316211539268

image-20240316211632461

1
2
3
4
5
6
7
8
9
10
11
12
gcc hello.c
# 会生成一个a.exe文件
# 使用gcc hello.c编译时,默认生成文件名为a的可执行文件
# 在Linux下会生成a.out文件
# 要运行则需要:./a.out
gcc -o hello hello.c
gcc hello.c -o hello
# 这样就会生成一个文件名为hello的可执行文件

# 编译.cpp文件则不能不能用gcc指令,要用g++指令
g++ -o hello2 hello.cpp

gcc指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
gcc(g++) <options> <sourcefiles>
options
-o<filename> 指定文件名
-fexec-charset=GBK 指定运行时编码
-finput-charset=UTF-8 指定源文件编码
-Wall 输出警告信息
-O(0-3) 指定代码优化级别
-E -o<filename.i> 编译过程进行到预处理
-S 编译过程进行到编译
-c 编译过程进行到汇编
-static 静态链接
-I<dir> 指定头文件搜索路径
-L<dir> 指定库搜索路径

-Wl,-rpath,<libpath> 指定运行时动态链接搜索库(Linux有效)
-Wl指定的是链接选项,支持的选项用ld -help查看

编译过程

gcc hello.c -o hello

  • GCC编译器驱动程序读取源程序文件hello.c,并把它编译成一个可执行文件main
  • 这个编译过程分为四个阶段:预处理(Preprocessing)、编译(Compilation)、汇编(Assembly)、链接(Linking)

预处理阶段

  1. 处理#include预编译指令,将被包含的文件直接插入到预编译指令的位置
  2. 处理所有的条件预编译指令,比如#if、#ifdef、#elif、#else、#endif等
  3. 预处理器将所有的#define删除,并且展开所有的宏定义
  4. 删除所有的注释
  5. 添加行号和文件标识、以便编译时产生调试用的行号及编译错误警告行号
  6. 保留所有的#pragma编译器指令,因为编译器需要使用它们
  • 测试

image-20240319100523233

1
2
3
4
5
6
7
8
9
10
11
# fun.h文件
#ifndef FUN_H
#define FUN_H

int add(int a, int b)
{
return a+b;

}

#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# main.cpp文件
#include"fun.h"

#define ADD(x,y) x + y
#define PI 3.1415927

#define STR

#ifdef STR
const char *str= "已定义了STR";
#else
const char *str= "没有定义STR";
#endif

/*
没有注释强行整注释
这是块注释
*/

int main()
{
double f = PI; // 没有注释强行整点注释
double d = ADD(PI, 2.71828);
return 0;
}
  • 在终端使用gcc对这两个文件分步处理
1
2
g++ -E main.cpp -o main.i
# -E指令是让gcc只做预处理,做完预处理后会生成.i后缀

image-20240319101352446

image-20240319101411790

  1. 预处理阶段把.h头文件的函数复制到main.i文件中

  2. 处理了所有的条件预编译指令,.h头文件的预编译指令也是删除后再复制过来的

  3. 预处理器将所有的#define删除,将宏定义的东西全部给替换了,比如PI

  4. 所有注释都被删除

编译

  • 编译器将预处理完的文本文件main.i进行一系列的词法分析、语法分析、语义分析和优化、翻译成文本文件main.s
1
2
3
g++ -S main.i
# -S指令是进行编译,该部分不需要用-o指定输出文件名,
# gcc会根据输入文件名,确定输出文件名

image-20240319102319077

汇编阶段

  • 汇编器将main.s翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保持在目标文件main.o中,main.o是一个二进制文件
1
2
g++ -c main.s
# -c指令是汇编指令

image-20240319102455562

  • 汇编后再打开main.o文件就会出现乱码,这是一些二进制指令

链接阶段

  • main函数程序调用了printf函数,它存在于一个单独的编译号了的目标文件中,而这个文件必须以某种方式合并到我们的main.o程序中
  • 链接器就负责处理这种合并。结果就得到了main文件,它是一个可执行目标文件(或者称为可执行文件)
  • 该文件可以被加载到内存中,由系统执行。(链接程序运行需要一大堆目标文件,以及所依赖的其他库文件,最后生成可执行文件)
1
g++ -o main main.o
  • 链接完成后就会生成.exe文件了

image-20240319103018984

  • 接下来,看另一个main.c的程序
1
2
3
4
5
6
7
8
9
# main.c文件
#include<stdio.h>

int main()
{
printf("Hello world From C\n");
return 0;
}

image-20240319103513750

静态链接

  • 在生成可执行文件的时候(链接阶段),把所有需要的函数的二进制代码都包含到可执行文件中去。
  • 在程序发布的时候就不需要依赖库,也就是不再需要带着库一块发布,程序可以独立执行。
  • 缺点:
    • 浪费内存空间。在多进程的操作系统下,同一时间,内存中可能存在多个相同的公共库函数
    • 程序的开发与发布流程受模块制约。只要有一个模块更新,那么就需要重新编译打包整个代码

具体演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// main.cpp文件
#include"fun.h"
#include <iostream>

#define ADD(x,y) x + y
#define PI 3.1415927

#define STR

#ifdef STR
const char *str= "已定义了STR";
#else
const char *str= "没有定义STR";
#endif

/*
没有注释强行整注释
这是块注释
*/

int main()
{
std::cout << PI << std::endl;
double f = PI; // 没有注释强行整点注释
double d = ADD(PI, 2.71828);
return 0;
}
  • 先使用动态链接
1
2
g++ -c main.cpp
g++ -o main main.o
  • 最终生成main.exe可执行文件,大小56kb

image-20240319105334826

  • 再使用静态链接指令编译
1
g++ -static -o main main.o

image-20240319105519525

  • 可以看到动态链接和静态链接的main.exe程序大小相差很大

动态链接

.dll文件是动态链接库

gcc默认使用动态链接

  • 在编译的时候不直接拷贝可执行代码,而是通过记录一系列符号和参数,在程序运行或加载时将这些信息传递给操作系统
  • 操作系统负责将需要的动态链接库加载到内存中,然后程序运行到指定的代码时,去共享执行内存中已经加载的动态库可执行代码,最终达到运行时链接的目的
  • 解决了静态链接的缺陷,更适应现代的大规模的软件开发。
  • 由于是运行时加载,可能会影响程序的前期执行性能

编码问题

  • 程序
1
2
3
4
5
6
7
8
9
10
#include<iostream>

int main()
{
std::cout<<"白日依山尽"<<std::endl;
std::cout<<"黄河入海流"<<std::endl;
std::cout<<"欲穷千里目"<<std::endl;
std::cout<<"更上一层楼"<<std::endl;
return 0
}
  • 在代码中输出了一些汉字,直接编译运行会出现这样一团乱码
    • 原因:现在这些编辑器用的都是UTF-8编码,但命令行编码使用的是GBK。

image-20240320082328054

  • 解决:
  1. 将代码文件编码转换为GBK,设置为GBK后保持一下

image-20240320082651125

image-20240320082727648

  1. 将命令行的编码改为UTF-8

输入指令chcp 65001

image-20240320082939373

  1. 在编译的时候指明各种编码

g++ -o main -finput-charset=UTF-8 -fexec-charset=GBK main.cpp

image-20240320083226917

警告与优化选项

  • 警告
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<iostream>

int* fun()
{
int a=100;
return &a;
}

int main()
{
int c=100;
unsigned int d =210;
bool gt = c >d;
return 0;
}
  • 对该代码进行编译,会出现警告:在函数里面返回了一个局部变量的地址

image-20240320083858538

-W指令用来开启警告,-Wall表示开启所有的警告

image-20240320084049522

-Wunsued-variable,即-W指令+警告类型,开启指定的警告

-Wno-return-local-addr,关闭指定警告

-Werror把警告当做错误来处理

  • 优化选项

-O0或-O1或-O2或-O3对代码进行优化,3代表是最高级别的优化

与小写字母o区别

多文件编译

指定头文件搜索路径

汇编基础

描述字的量词

名称 翻译 大小
bit 比特 1位(1b)
byte 字节 8位(1B)
word 16位
dword 双字 32位
qword 四字 64位
  • dword:double word
  • qword: quadra word

寄存器种类

  • 寄存器
  • 最重要的是rip的寄存器

image-20240313125131627

1
2
3
4
5
rbp rsp # 与栈有关,保护栈
rax # 储存程序的返回值,return的数据都储存在rax
rip # 存放当前执行的指令地址
rsp # 存放当前栈帧的栈顶地址
rbp # 存放当前栈帧的栈底
  • eflags寄存器的结构图

image-20240313124942196

Linux

ELF文件结构

  • linux环境中,二进制可执行文件的类型是ELF(Executable and Linkable)文件
  • 下图节头表又被称为段表

image-20240317103103423

查看ELF文件

  • ELF文件的基本信息存在于elf的头部信息中,这些信息包括指令的运行架构、程序入口等等

  • 可以通过 readelf -h <elf_name>来查看头部信息

例如:readelf -h question_1_x64

image-20240314221225075

分析ELF文件头

image-20240314221442158

Magic

  • 几乎所有的可执行文件格式的最开始的几个字节都是magic
  • 比如a.out格式最开始两个字节为0x01、0x07。PE/COFF文件最开始两个字节为0x4d、0x5a,即ASCII字符的MZ
1
2
3
4
5
Magic: 7f 45 4c 46 
02
01
01
00 00 00 00 00 00 00 00 00
  • Magic最前面的4个字节7f 45 4c 46(都是16进制)

    • 7f对应的ASCII字符里面的**DEL(删除)**控制符
    • 45、4c、46是ELF这3个字母的ASCII编码
  • 接下来的一个字节:0x02

    • 标识ELF文件类:0x02表示64位的
    • image-20240314222410445
  • 接下来一个字节:0x01

    • 标识字节序:0x01表示小端序
    • image-20240314222931869
  • 接下来的一个字节:0x01

    • 规定ELF文件的主版本号,一般都是1
  • 因为ELF标准自1.2版以后就再也没有更新了。后面的9个字节ELF标准没有定义,一般填0,有些平台会使用这9个字节作为扩展标志。

类型

类型: DYN (Position-Independent Executable file)

image-20240317102321958

系统架构

  • e_machine成员表示该ELF文件的平台属性,比如下图表示该ELF文件只能在x86-64位的机器下使用

image-20240317102516258

image-20240317102647993

ELF文件头结构体

  • ELF文件在各种平台下都通用,ELF文件有32位版本和64位版本
  • 它的文件头结构也有这两个版本分别叫Elf32_EhdrElf64_Ehdr

image-20240317100905157

image-20240317100931473

  • 带地址的有差别,其他没啥区别
  • 除了前五个对应了结构体的的一个,后面都与结构体一一对应

image-20240317101659220

基本操作命令

1
2
标准格式:命令名称 [命令参数] [命令对象]
其中命令参数有长和短两种格式,分别用"--"和"-"做前缀。例如:--help和-h
  • 下面是Linux的一些基本操作命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
ls [OPtion]... [FILE]...			列出文件信息
cd [-L] [-P [-e]] [-@] [dir] 切换工作目录
pwd [-LP] 显示当前工作目录
uname [OPTION]... 打印系统信息
whoami [OPTION]... 打印用户名
man [OPTION...] [SECTION] PAGE... 查询帮助信息
find [OPTION] [path..] [expression] 查找文件
echo [SHORT-OPTION]... [STRING]... 打印文本,参数“-e”可激活转义字符
cat [OPTION]... [FILE]... 打印到标准输出
less [options] file... 分页打印文本,比more提供更丰富的功能
head/tail [OPTION] [FILE]... 打印文本的前/后N行
grep[OPTION]... PATTERN [FILE]... 匹配文本模式
cut OPTION... [FILE]... 通过列提取文本
diff [OPTION]... [FILE]... 比较文本差异
mv [OPTION]... [-T] SOURCE DEST 移动或重命名文件
cp [OPTION]... [-T] SOURCE DEST 复制文件勾八
rm [OPTION]... [FILE]... 删除文件
ps [options] 查看进程状态
top [options] 实时查看系统运行情况
kill [options] <pid> [...] 杀死进程
ifconfig [-v] [-a] [-s] [interface] 查看或设置网络设备
ping [options] destination 判断网络或设置网络设备
netstat [options] 判断网络主机是否响应
nc [options] 建立TCP/UDP连接并监听
su [options] [username] 切换到超级用户
touch [OPTION]... FILE... 创建文件
mkdir [OPTION]... DIRECTORY... 创建目录
chmod [OPTION]... MODE[,MODE]... FILE... 变更文件或目录权限
chown [OPTION]... [OWNER][:[GROUP]] FILE... 变更文件或目录所属者
nano / vim / emacs 终端文件编辑器
history [-c] [-d offset] [n] 查看".bash_history"中历史命令
exit 退出shell
  • 使用变量:
1
2
3
4
5
var = value		给变量var赋值value
$var, ${var} 去变量的值
'cmd', $(cmd) 代换标准输出
'string' 非替换字符串
"string" 可替换字符串

工具操作指令

1
2
3
4
5
6
7
8
9
10
gcc question_1.c -o question_1_X64
gcc -S question_1.c
file
ldd
nm
hexdump
objdump -d -M intel
readelf -a
gdb
socat tcp-I:8888,fork exec:./a.out,reuseaddr

file指令

file question_1_x64

1
2
question_1_x64: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, 
BuildID[sha1]=93f72a0d1b001212ba5860a6a1da7e57a36e3f47, for GNU/Linux 3.2.0, not stripped

readelf指令

readelf -a question_1_x64 |less

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
ELF 头:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
类别: ELF64
数据: 2 补码,小端序 (little endian)
Version: 1 (current)
OS/ABI: UNIX - System V
ABI 版本: 0
类型: DYN (Position-Independent Executable file)
系统架构: Advanced Micro Devices X86-64
版本: 0x1
入口点地址: 0x1100
程序头起点: 64 (bytes into file)
Start of section headers: 14424 (bytes into file)
标志: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 13
Size of section headers: 64 (bytes)
Number of section headers: 31
Section header string table index: 30

节头:
[号] 名称 类型 地址 偏移量
大小 全体大小 旗标 链接 信息 对齐
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000000318 00000318
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.gnu.pr[...] NOTE 0000000000000338 00000338
0000000000000030 0000000000000000 A 0 0 8
[ 3] .note.gnu.bu[...] NOTE 0000000000000368 00000368
0000000000000024 0000000000000000 A 0 0 4
[ 4] .note.ABI-tag NOTE 000000000000038c 0000038c
0000000000000020 0000000000000000 A 0 0 4
[ 5] .gnu.hash GNU_HASH 00000000000003b0 000003b0
0000000000000034 0000000000000000 A 6 0 8
[ 6] .dynsym DYNSYM 00000000000003e8 000003e8
0000000000000168 0000000000000018 A 7 1 8
[ 7] .dynstr STRTAB 0000000000000550 00000550
00000000000000d7 0000000000000000 A 0 0 1
[ 8] .gnu.version VERSYM 0000000000000628 00000628
000000000000001e 0000000000000002 A 6 0 2

nm指令

nm question_1_x64 |less

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
000000000000038c r __abi_tag
0000000000004018 B __bss_start
0000000000004048 b completed.0
w __cxa_finalize@GLIBC_2.2.5
0000000000004000 D __data_start
0000000000004000 W data_start
0000000000001130 t deregister_tm_clones
00000000000011a0 t __do_global_dtors_aux
0000000000003d98 d __do_global_dtors_aux_fini_array_entry
0000000000004008 D __dso_handle
0000000000003da0 d _DYNAMIC
0000000000004018 D _edata
0000000000004050 B _end
0000000000001304 T _fini
00000000000011e0 t frame_dummy
0000000000003d90 d __frame_dummy_init_array_entry
0000000000002138 r __FRAME_END__
0000000000001252 T func
U gets@GLIBC_2.2.5
0000000000003f90 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
000000000000200c r __GNU_EH_FRAME_HDR
0000000000001000 T _init
00000000000011e9 T init_func
0000000000002000 R _IO_stdin_used
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U __libc_start_main@GLIBC_2.34
0000000000001275 T main
U printf@GLIBC_2.2.5
U puts@GLIBC_2.2.5
0000000000001160 t register_tm_clones
U setvbuf@GLIBC_2.2.5
0000000000004010 D sh
U __stack_chk_fail@GLIBC_2.4
0000000000001100 T _start
0000000000004040 B stderr@GLIBC_2.2.5
0000000000004030 B stdin@GLIBC_2.2.5
0000000000004020 B stdout@GLIBC_2.2.5
U system@GLIBC_2.2.5
0000000000004018 D __TMC_END__

hexdump指令

hexdump question_1_x64 | less

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
0003e80 0000 0000 0000 0000 0008 0000 0000 0000
0003e90 0008 0000 0000 0000 0106 0000 0001 0000
0003ea0 0003 0000 0000 0000 4000 0000 0000 0000
0003eb0 3000 0000 0000 0000 0018 0000 0000 0000
0003ec0 0000 0000 0000 0000 0008 0000 0000 0000
0003ed0 0000 0000 0000 0000 010c 0000 0008 0000
0003ee0 0003 0000 0000 0000 4020 0000 0000 0000
0003ef0 3018 0000 0000 0000 0030 0000 0000 0000
0003f00 0000 0000 0000 0000 0020 0000 0000 0000
0003f10 0000 0000 0000 0000 0111 0000 0001 0000
0003f20 0030 0000 0000 0000 0000 0000 0000 0000
0003f30 3018 0000 0000 0000 002b 0000 0000 0000
0003f40 0000 0000 0000 0000 0001 0000 0000 0000
0003f50 0001 0000 0000 0000 0001 0000 0002 0000
0003f60 0000 0000 0000 0000 0000 0000 0000 0000
0003f70 3048 0000 0000 0000 0468 0000 0000 0000
0003f80 001d 0000 0012 0000 0008 0000 0000 0000
0003f90 0018 0000 0000 0000 0009 0000 0003 0000
0003fa0 0000 0000 0000 0000 0000 0000 0000 0000
0003fb0 34b0 0000 0000 0000 028b 0000 0000 0000
0003fc0 0000 0000 0000 0000 0001 0000 0000 0000
0003fd0 0000 0000 0000 0000 0011 0000 0003 0000
0003fe0 0000 0000 0000 0000 0000 0000 0000 0000
0003ff0 373b 0000 0000 0000 011a 0000 0000 0000
0004000 0000 0000 0000 0000 0001 0000 0000 0000
0004010 0000 0000 0000 0000
0004018

strings指令

strings question_1_x64 |less

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/lib64/ld-linux-x86-64.so.2
mfUa
__cxa_finalize
__libc_start_main
gets
setvbuf
stdout
puts
system
stdin
stderr
__stack_chk_fail
printf
libc.so.6
GLIBC_2.4
GLIBC_2.2.5
GLIBC_2.34
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable

ldd指令

ldd question_1_x64

1
2
3
linux-vdso.so.1 (0x00007fffddda6000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x0000759e6c000000)
/lib64/ld-linux-x86-64.so.2 (0x0000759e6c3e0000)

objdump指令

objdump -d question_1_x64 |less

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
question_1_x64:     文件格式 elf64-x86-64


Disassembly of section .init:

0000000000001000 <_init>:
1000: f3 0f 1e fa endbr64
1004: 48 83 ec 08 sub $0x8,%rsp
1008: 48 8b 05 d9 2f 00 00 mov 0x2fd9(%rip),%rax # 3fe8 <__gmon_start__@Base>
100f: 48 85 c0 test %rax,%rax
1012: 74 02 je 1016 <_init+0x16>
1014: ff d0 call *%rax
1016: 48 83 c4 08 add $0x8,%rsp
101a: c3 ret

Disassembly of section .plt:

0000000000001020 <.plt>:
1020: ff 35 72 2f 00 00 push 0x2f72(%rip) # 3f98 <_GLOBAL_OFFSET_TABLE_+0x8>
1026: f2 ff 25 73 2f 00 00 bnd jmp *0x2f73(%rip) # 3fa0 <_GLOBAL_OFFSET_TABLE_+0x10>
102d: 0f 1f 00 nopl (%rax)
1030: f3 0f 1e fa endbr64
1034: 68 00 00 00 00 push $0x0
1039: f2 e9 e1 ff ff ff bnd jmp 1020 <_init+0x20>
103f: 90 nop
1040: f3 0f 1e fa endbr64
1044: 68 01 00 00 00 push $0x1
1049: f2 e9 d1 ff ff ff bnd jmp 1020 <_init+0x20>
104f: 90 nop
1050: f3 0f 1e fa endbr64
1054: 68 02 00 00 00 push $0x2
1059: f2 e9 c1 ff ff ff bnd jmp 1020 <_init+0x20>
105f: 90 nop
1060: f3 0f 1e fa endbr64

  • 使用intel指令的汇编

objdump -d question_1_x64 -M intel|less

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
question_1_x64:     文件格式 elf64-x86-64


Disassembly of section .init:

0000000000001000 <_init>:
1000: f3 0f 1e fa endbr64
1004: 48 83 ec 08 sub rsp,0x8
1008: 48 8b 05 d9 2f 00 00 mov rax,QWORD PTR [rip+0x2fd9] # 3fe8 <__gmon_start__@Base>
100f: 48 85 c0 test rax,rax
1012: 74 02 je 1016 <_init+0x16>
1014: ff d0 call rax
1016: 48 83 c4 08 add rsp,0x8
101a: c3 ret

Disassembly of section .plt:

0000000000001020 <.plt>:
1020: ff 35 72 2f 00 00 push QWORD PTR [rip+0x2f72] # 3f98 <_GLOBAL_OFFSET_TABLE_+0x8>
1026: f2 ff 25 73 2f 00 00 bnd jmp QWORD PTR [rip+0x2f73] # 3fa0 <_GLOBAL_OFFSET_TABLE_+0x10>
102d: 0f 1f 00 nop DWORD PTR [rax]
1030: f3 0f 1e fa endbr64
1034: 68 00 00 00 00 push 0x0
1039: f2 e9 e1 ff ff ff bnd jmp 1020 <_init+0x20>
103f: 90 nop
1040: f3 0f 1e fa endbr64
1044: 68 01 00 00 00 push 0x1
1049: f2 e9 d1 ff ff ff bnd jmp 1020 <_init+0x20>
104f: 90 nop
1050: f3 0f 1e fa endbr64
1054: 68 02 00 00 00 push 0x2
1059: f2 e9 c1 ff ff ff bnd jmp 1020 <_init+0x20>
105f: 90 nop
1060: f3 0f 1e fa endbr64

  • 段表

Linux查看文件的指令

  • cat命令—用于将文件内容输出到终端上,经常使用于查看文本文件的内容。
  • less命令—用于分页查看文件内容,可以向上翻页、向下翻页、搜索关键字等,合适查看大文件。
  • more命令—与less类似,也是用于分页查看文件内容,但是功能较少,只能向下翻页。
  • head命令—用于查看文件的前几行内容,默许情况下显示前10行。
  • tail命令—用于查看文件的后几行内容,默许情况下显示文件的最后10行。
  • nl命令—用于在文件中加上行号,便于查看和编辑文件。
  • vi/vim命令—是一种文本编辑器,可以用于查看和编辑文件内容,适用于高级用户。

cat命令

cat命令—用于将文件内容输出到终端上,经常使用于查看文本文件的内容。

less命令

less命令—用于分页查看文件内容,可以向上翻页、向下翻页、搜索关键字等,合适查看大文件。

more命令

more命令—与less类似,也是用于分页查看文件内容,但是功能较少,只能向下翻页。

head命令

head命令—用于查看文件的前几行内容,默许情况下显示前10行。

tail命令

tail命令—用于查看文件的后几行内容,默许情况下显示文件的最后10行。

nl命令

nl命令—用于在文件中加上行号,便于查看和编辑文件。

vi/vim命令

vi/vim命令—是一种文本编辑器,可以用于查看和编辑文件内容,适用于高级用户。

Linux输入输出

  • 如果题目关闭标准错误输入输出,那么得到shell后就输入指令exec 1>&0将标准输入输出开启

偏移量

  1. 返回地址偏移量:在栈溢出攻击中,攻击者通常会尝试覆盖函数的返回地址,以控制程序的执行流程。攻击者需要计算出返回地址在目标函数栈帧中的偏移量,从而正确地覆盖返回地址。

  2. 缓冲区偏移量:攻击者通常会利用缓冲区溢出来实现栈溢出攻击。攻击者需要计算出目标缓冲区在栈帧中的偏移量,以正确地构造恶意输入来溢出缓冲区。

  3. Shellcode 的加载地址:如果攻击者试图在栈溢出攻击中注入恶意代码(如 Shellcode),则需要考虑 Shellcode 在内存中的加载地址。攻击者需要计算出 Shellcode 在栈帧中的偏移量,以正确地设置返回地址指向 Shellcode。

  4. 其他关键数据的偏移量:除了返回地址和缓冲区之外,攻击者可能还需要考虑其他关键数据在栈帧中的偏移量,以实现更复杂的攻击目标。

综上所述,在进行栈溢出攻击时,攻击者需要仔细分析目标函数的栈帧结构,计算关键数据的偏移量,并正确地构造恶意输入,以实现对程序的控制或执行恶意代码。通过正确地计算和使用偏移量,攻击者可以更有效地利用栈溢出漏洞进行攻击。

shellcode

  • shellcode是一段可被CPU直接执行的程序码

  • 使用shellcode进行攻击是一项经典而强大的技术,借助shellcode几乎可以完成任何事情,包括但不限于泄露敏感信息、反弹shell、布置后门等。

  • 编写shellcode的方式有很多,而方式的选择取决于实际场景。如当需要编写复杂的shellcode的时候,需要手搓;当需要注入特定模版的shellcode时,可以采取工具生成;当需要观察shellcode的字符、长度时,可以采用在线网站生成。

  • shellcode是一段可被CPU直接执行的程序码

  • 使用shellcode进行攻击是一项经典而强大的技术,借助shellcode几乎可以完成任何事情,包括但不限于泄露敏感信息、反弹shell、布置后门等。

  • 编写shellcode的方式有很多,而方式的选择取决于实际场景。如当需要编写复杂的shellcode的时候,需要手搓;当需要注入特定模版的shellcode时,可以采取工具生成;当需要观察shellcode的字符、长度时,可以采用在线网站生成。

纯手搓

  • 学到最后,会回归到最原始的方式:手搓shellcode。

纯汇编

  • 如果使用gcc编译模版如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
;#gcc -c start.s -o start.o
;#ld -e _start -z noexecstack start.o -o start


    ;//使用intel语法
    .intel_syntax noprefix
    .text
    .globl    _start
    .type     _start, @function
_start:
    ;// SYS_exit
    mov rdi,0
    mov rax,60
    syscall
  • 也可以使用nasm,只是编译的命令不一样。

  • 推荐书籍《二进制程序的链接、装载与库》

内联汇编

  • 有时候需要调用一下库函数,可以使用内联汇编 Extended Asm (Using the GNU compiler collection(GCC),直接在程序中使用asm(…);编写内联汇编语句。

  • 但是在剥离shellcode的时候,需要将 call xxxxxxxx的偏移进行修正

使用tiny_libc

  • 为了快速、高效、准确地编写出复杂的shellcode,使用musl库实现简单的libc库,姑且称之为 tiny_libc,项目地址在

借助工具

pwntools的shellcraft模版

  • pwntools的shellcraft定义了非常多的模版,支持的架构有x86/x64/arm/arm64等等。

alph3

AE64

shellcode encoder

交叉编译

  • 这个部分可以到异架构的时候看

在线网站

其他相关资料

一些内核网站