前言:感觉最近碰到了很多不会的知识,一时间有点手忙脚乱。高版本的堆攻击还不能熟练运用,house of 系列的高版本还有很多疏漏,写题的时候又碰见了 V8,LLVM,异构 PWN,内核,phppwn 等等等等之前没有接触过的难题,及时到现在还是没有学会上面说的这些东西,只能说感觉要学习的东西还有很多很多。
由于一时间碰见这么多要学习的东西,求学心切反而感觉导致自己有点浮躁和分心,因此决定沉下心来一个一个学习,虽然感觉栈的知识也很久没有复习过了 emmmm…….T_T// 有没有谁愿意带带我 ……
LLVM PASS :
LLVM 是 C++编写的构架编译器的框架系统,可用于优化以任意程序语言编写的程序。
LLVM Pass 可用于对代码进行优化或者对代码插桩(插入新代码),LLVM 的核心库中提供了一些 Pass 类可以继承,通过实现它的一些方法,可以对传入的 LLVM IR 进行遍历并操作。
LLVM IR 即代码的中间表示,有三种形式:
.ll 格式:人类可以阅读的文本:**主要用于开发者调试和学习。**它的语法有点类似汇编语言,但比底层汇编高级得多,带有强类型系统和无限的虚拟寄存器(以%开头)。.bc 格式:适合机器存储的二进制文件:**主要用于高效存储、快速加载以及链接时优化 (LTO)。**它占用的磁盘空间比.ll小得多,且 LLVM 解析它的速度远快于解析文本文件。内存表示:当前端(如 Clang)解析完源代码,或者 LLVM 从磁盘读取了.bc/.ll文件后,都会在内存中构建出一套极其复杂的 C++ 对象树。在编写 Pass 时,操作的llvm::Module、llvm::Function、llvm::BasicBlock和llvm::Instruction等 C++ 类,就是 IR 在内存中的实体。所有的优化、增删改查都是在这个层面发生的。
下面给出<font style="color:rgb(166, 44, 100);">.ll</font>格式和<font style="color:rgb(166, 44, 100);">.bc</font>格式生成及相互转换的常用指令清单:
1 | .c -> .ll:clang -emit-llvm -S a.c -o a.ll |
LLVM 环境:
需要安装<font style="color:rgb(166, 44, 100);">CTF</font>题目中常用的三个版本的<font style="color:rgb(166, 44, 100);">clang</font>及<font style="color:rgb(166, 44, 100);">LLVM</font>:
1 | sudo apt install clang-8 |
opt 是LLVM的优化器和分析器,可加载指定的模块,对输入的 LLVM IR 或者 LLVM 字节码进行优化或分析。
CTF题目一般会给出所需版本的opt文件(可用./opt –version查看版本)
或者在README文档中告知opt版本。
安装好 llvm 后,可在/usr/lib/llvm-xx/bin/opt路径下找到对应llvm版本的opt文件,一般不开PIE保护
attention:
可以在安装 clang 和 LLVM 的时候由于 pwn 环境版本过高导致启用上述低版本的 clang 和 LLVM,我的解决方法是在 PWN 环境上起一个 docker 容器,找到适配的 ubuntu 镜像即可
不知道为什么,我的电脑只要装上 ubuntu 镜像去开启虚拟机时虚拟机就会变得很卡,导致无法使用,因此我一直使用的是 kali linux 的镜像。
最近碰到了 V8 和 LLVM 的题目,由于 kali 是 glibc-2.42 版本的,对于很多低版本的题目是不适配的,只能起一个 docker 容器在里面写题,感觉还是挺麻烦的,也不清楚是否有更好的方法去解决。
Kali Linux 是基于 Debian 的发行版,对 Docker 的支持非常好。只要在 Kali 上安装了 Docker 引擎,就可以像在其他宿主机系统上一样,轻松拉取并运行 Ubuntu 容器。事实上这种工作现在大部分可以交给 ai 去做。
安装 docker 和 ubuntu:
1. 更新系统包列表
首先,打开终端并确保你的 Kali 系统包索引是最新的:
1 | sudo apt update |
2. 安装 Docker
在 Kali 中,你可以直接使用内置的包管理器来安装 Docker:
1 | sudo apt install docker.io -y |
3. 启动并启用 Docker 服务
安装完成后,启动 Docker 服务,并设置它在系统引导时自动启动
1 | sudo systemctl start docker |
(可选) 验证 Docker 是否正常运行:
1 | sudo docker --version |
4. 拉取并运行 Ubuntu 容器
现在 Docker 已经准备就绪,你可以直接使用 docker run 命令来拉取并进入一个交互式的 Ubuntu 容器:
1 | sudo docker run -it ubuntu /bin/bash |
我们可以利用命令去查看当前的 docker 容器:
1 | sudo docker ps -a |

可以把这个停止的容器启动起来
1 | sudo docker start <你的容器ID> |
再进入这个正在运行的容器
1 | sudo docker exec -it <你的容器ID> /bin/bash |
再在 ubuntu docker 容器当中装上自己需要的 dbg,pwntools,ropper 等等等等的 PWN环境即可
把 kali_share 的文件复制给 docker
我是在 kali 当中创建了一个和物理机共享的的文件夹 kali_share,方便传输文件,但是起了 ubuntu 环境也就意味着不能直接把文件传到自己需要的环境当中
假设 Kali 的当前目录是 .../CISCN-2021 satool。你需要把题目附件(二进制文件或 libc 库)丢进容器里才能调试。这时候要用到 **docker cp** 命令,它就像宿主机和容器之间的“U盘”:
假设你的题目文件名为 satool,你想把它复制到 satool20 容器的 /root 目录下:
1 | sudo docker cp ./satool satool20:/root/ |
(反过来,如果你在容器里写了 exp 脚本想拿出来,把路径对调一下就可以了:sudo docker cp satool20:/root/exp.py ./)
安装上了 docker 之后也是成功的完成了第一道 LLVM 题目 T_T///

gdb调试:
我们以 CISCN-2021 satool 为例:
我们直接在对应的 ubuntu 20.04 环境下开始 gdb 调试即可:
1 | gdb ./opt |


不过 opt 并不会一开始就将 SAPass.so 模块加入进来,而是会在 call 了一堆 llvm 初始化之后的第一个 call 函数执行完之后才会加载 SAPass.so 模块:像这些 call 都是典型的 llvm 初始化


那么有一种方法可以快速定位加载 SAPass.so 的时候,从而利用 vmmap 查找 SAPass.so 基地址:
寻找 SAPass.so 基地址:
假设我们当前处于 pwndbg-b main-r 之后:

执行 catch load:
1 | catch load |
这会让程序在每次调用底层加载库时自动断下,继续执行:
1 | r -load ./SAPass.so -SAPass ./exp.ll |


c continue:会依次经过我们下的断点
1 | c |

继续 c,直到出现如下字样:
1 | Catchpoint 2 Inferior loaded ./SAPass.so |

此时我们就可以利用 vmmap 去查看 SAPass.so 的基地址了:

圈出来的就是so模块的基地址(高版本opt会显示在内存分布表的下方),直接用这个基地址加上对应偏移就可以得到so模块中的汇编指令地址了,也就能下断点了。
runOnFunction
runOnFunction函数位于虚表中的最后一个位置,比赛题的漏洞基本就是这个,所以在做LLVM Pass pwn的时候定位函数的位置可以从虚表入手:可以直接搜索虚表 vtable 的位置

sub_19D0是虚表中的最后一个,也就是我们常说的 runOnFunction

进入之后 F5 反编译一下:


说些什么吧!