重写编译后二进制文件中的函数 - The Trail of Bits Blog
Aditi Gupta, 卡内基梅隆大学
2019年9月2日
密码学, 实习项目, mcsema
作为Trail of Bits的暑期实习生,我一直在开发Fennec——一个基于McSema(由Trail of Bits开发的二进制提升工具)构建的自动化替换编译二进制文件中函数调用的工具。
问题背景
假设你有一个已编译的二进制文件,但无法获取原始源代码。当你发现程序存在缺陷或需要修改时,直接通过十六进制编辑器修补文件会非常繁琐。而能够编写C函数并直接替换原有函数将极大提升效率。
我暑期开发的任务就是创建这样一个便捷工具。只需知道目标函数名称,编写替代的C函数并编译后输入Fennec,即可自动生成优化后的新二进制文件。
密码学应用实例
为了展示Fennec的功能,我们以现实中常见的密码学漏洞为例:AES-CBC加密模式中使用静态初始化向量(IV)。在CBC加密的第一步,明文块会与IV进行异或操作。IV是128位的数据块,在任何加密中仅使用一次以防止密文重复。加密后,该密文将作为下一个明文块的IV。
当IV在整个明文过程中保持恒定时会引发安全问题。在固定IV下,如果所有消息以相同明文块开头,将生成相同密文。这意味着攻击者可将多个密文作为群体分析而非独立消息。以下是一个静态生成IV的示例:
1
2
3
|
unsigned char *generate_iv() {
return (unsigned char *)"0123456789012345";
}
|
开发者有时会使用OpenSSL等密码库进行加密,但自行编写IV生成函数。这非常危险,因为非随机IV会导致AES不安全。Fennec正是为修复此类问题而生——它会检测IV生成是随机还是静态的,并在需要时用新的安全IV函数替换。
实现过程
最终目标是通过McSema将可执行二进制文件提升为LLVM位码,并结合LLVM操作自动替换函数。我从理解密码学实例开始,探索不同的二进制修补方法作为背景知识。
首先,我通过Matasano Cryptopals挑战学习AES的用法及破解方式。此阶段我获得了调用OpenSSL的C语言加密和解密程序,以及一些用于攻击实现的Python脚本。加密程序使用了静态IV生成函数,这正是后期需要自动替换的目标。
整个暑期我持续使用这些C二进制文件。随后我开始研究二进制修补,考察了LD_PRELOAD和Witchcraft Compiler Collection。但这些仅适用于动态链接函数,而我的目标是替换二进制文件内部的函数调用。
为避免提升位码带来的复杂性,我最初使用直接从源代码生成的干净位码。我尝试在LLVM传递中直接修改函数的位码,但很快转向编写新的C函数并使原程序调用新函数。
经过实验,我创建了一个LLVM传递,将所有对旧函数的调用替换为新函数。在转向提升位码前,我添加了代码以保留调用原函数的能力。在密码学示例中,这意味着能检测原函数是否生成静态IV,并根据情况决定是否替换(而非盲目替换)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// 代表原二进制文件中函数的存根函数
unsigned char *generate_iv_original() {
unsigned char *result = (unsigned char *)"";
// 此函数内容无关紧要
return result;
}
unsigned char *random_iv() {
unsigned char *iv = malloc(sizeof(int) * 16);
RAND_bytes(iv, 16); // OpenSSL调用
return iv;
}
unsigned char *replacement() {
unsigned char *original = generate_iv_original();
for (int i = 0; i < 10; i++) {
unsigned char *iv = generate_iv_original();
if (iv == original) { // 如果IV是静态的
return random_iv();
}
}
return original;
}
|
工具在干净位码上运作后,我开始研究提升位码。通过提升和重新编译二进制文件,并查看中间表示,我熟悉了McSema的工作方式。由于McSema改变了函数调用方式,需要额外努力使工具在提升位码上同样有效。我必须用McSema提升原始二进制文件和替换函数。由于非提升二进制文件中的替换函数不遵循McSema的调用约定,不能简单交换,因此需要更多工作。
通过McSema,函数名称和类型变得更加复杂,但我最终制定了有效流程。与干净位码工具类似,原函数可保留供替换使用。
最后一步是将流程通用化并封装成命令行工具供他人使用。我测试了多种目标(包括剥离符号的二进制文件和动态加载函数),添加测试并验证安装过程。
函数替换传递
完整流程包含三个主要步骤:1) 使用McSema将二进制文件提升为位码;2) 使用LLVM传递在位码中执行函数替换;3) 重新编译为新二进制文件。LLVM传递是工具的核心,实际执行函数替换。
传递通过遍历程序中的每条指令并检查是否为要替换函数的调用来工作。以下代码检查每条指令是否调用目标函数:
1
2
3
4
5
6
7
8
9
|
for (auto &B : F) {
for (auto &I : B) {
// 检查指令是否为要替换函数的调用
if (auto *op = dyn_cast<CallInst>(&I)) {
auto function = op->getCalledFunction();
if (function != NULL) {
auto name = function->getName();
if (name == OriginalFunction) {
...
|
然后,通过查找指定名称且类型与原函数相同的新函数来找到替换函数:
1
2
3
4
5
6
|
Type *retType = function->getReturnType();
FunctionType *newFunctionType =
FunctionType::get(retType, function->getFunctionType()->params(), false);
// 创建新函数
newFunction = (Function *)(F.getParent()->getOrInsertFunction(ReplacementFunction, newFunctionType));
|
下一步是将原函数的参数传递给新调用:
1
2
3
4
5
6
7
8
|
CallSite CS(&I);
// 获取要传递给替换函数的原函数参数
std::vector<Value *> arguments;
for (unsigned int i = 0; i < CS.arg_size(); i++) {
arguments.push_back(CS.getArgument(i));
}
|
创建新调用后,我们需要确保所有使用原调用的地方更新为新调用:
1
2
3
4
5
6
7
8
|
// 创建新调用
CallInst *newCall = CallInst::Create(newFunction, arguments, "", &I);
// 将原调用的所有使用更新为新调用
for (auto &U : I.uses()) {
User* user = U.getUser();
user->setOperand(U.getOperandNo(), newCall);
}
|
完整工具
虽然LLVM传递执行函数替换,但完整流程通过bash脚本封装。首先,使用McSema反汇编并提升两个输入二进制文件。
使用McSema提升二进制文件
接下来,分析和调整位码以找到McSema表示的函数名称。此代码段支持动态加载函数和剥离符号的二进制文件,这些会影响函数名称。我们需要知道这些名称,以便在实际替换时将它们作为参数传递给LLVM传递。如果查找原始二进制文件中的名称,LLVM传递将无法找到匹配函数,因为我们使用的是提升后的位码。
查找要替换的函数名称
最后运行传递。如果不需要访问原函数,只需在原始二进制文件上运行传递。但如果需要从替换函数调用原函数,则需在原始二进制文件和替换二进制文件上运行传递。第二种情况下,我们将原函数替换为新函数,并将存根函数替换为原函数。最后,将所有内容重新编译为新的可工作二进制文件。
运行传递并从更新后的位码编译新二进制文件
成果
Fennec通过二进制提升和重新编译使难题变得相对可控。它在修复遗留软件的安全漏洞时特别有用,尤其是无法获取源代码的情况。
使用此工具可自动修复密码学IV漏洞。如下所示,原始二进制文件每次使用静态IV加密相同消息生成相同密文。而运行Fennec后,新生成的二进制文件使用不同IV,即使对相同明文(蓝色)每次运行也产生唯一密文。
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
|
# 原始二进制文件
aditi@nessie:~/ToB-Summer19$ ./encrypt ""
MDEyMzQ1Njc4OTAxMjM0NQ==/reJh+5rktBatDpyuJNQEBo++0pyIRGZiNsmZkN09HTPIOBVqQ9ov6CrxPXO7dC4cUJGYzBEsejHuTQyjVQh+XsLCHyDkURmfCuJ+a97raPY+o8pKKt8yf/xTmYMtyq2zf7EQxqPxv2bXKdP+6K+h9KyuO3q4+3JbuJFTesNLy8Np1m9ShJ9UAHvAdO6LCZvQ
N91kz0ytIH+s7LgajIWyises+yz26UBQwOzZLeLcQp4=
176
aditi@nessie:~/ToB-Summer19$ ./encrypt ""
MDEyMzQ1Njc4OTAxMjM0NQ==/reJh+5rktBatDpyuJNQEBo++0pyIRGZiNsmZkN09HTPIOBVqQ9ov6CrxPXO7dC4cUJGYzBEsejHuTQyjVQh+XsLCHyDkURmfCuJ+a97raPY+o8pKKt8yf/xTmYMtyq2zf7EQxqPxv2bXKdP+6K+h9KyuO3q4+3JbuJFTesNLy8Np1m9ShJ9UAHvAdO6LCZvQ
N91kz0ytIH+s7LgajIWyises+yz26UBQwOzZLeLcQp4=
176
aditi@nessie:~/ToB-Summer19$ ./encrypt ""
MDEyMzQ1Njc4OTAxMjM0NQ==/reJh+5rktBatDpyuJNQEBo++0pyIRGZiNsmZkN09HTPIOBVqQ9ov6CrxPXO7dC4cUJGYzBEsejHuTQyjVQh+XsLCHyDkURmfCuJ+a97raPY+o8pKKt8yf/xTmYMtyq2zf7EQxqPxv2bXKdP+6K+h9KyuO3q4+3JbuJFTesNLy8Np1m9ShJ9UAHvAdO6LCZvQ
N91kz0ytIH+s7LgajIWyises+yz26UBQwOzZLeLcQp4=
176
aditi@nessie:~/ToB-Summer19$ bash run.sh 2 ../mcsema-2.0.0-ve/remill-2.0.0/remill-build-2/ /home/aditi/ToB-Summer19/ida-6.9/idal64 encrypt replaceIV generate_iv replacement generate_iv_original -lcrypto
# Fennec修改后的二进制文件
aditi@nessie:~/ToB-Summer19$ ./encrypt.new ""
L+PYRFiOKMcu18hSqdGQEw==/aK2hYm/GXHwA2tqZxPmoNccQwW+Zhj7E0PQUSRF+lOLJiEMwOc7yv+/Z2AA0pEJjP7Jq4lHMpq2eIVl73lvav0pJiVlOcmfnFwQ9cu0MW0EWqUdgl2FCsWKtO/TAfGhcQPopJyvP8KD/LHlru4QIfZiym7//tt0V9vvabFCLNiSTRG350XKO/zoydeuRFfSu
0HmNNQbAcLSQkcUETH424RyQ4SxmcreW3krOw30kfJY=
176
aditi@nessie:~/ToB-Summer19$ ./encrypt.new ""
hYnowxN2Z3QyPIzwNaFzJw==/pzCq+V1q5ipHoqJXZ9MaeDr+nMdV5E1RbeI+YrcQqXjFHcVmDSq4yZboEuIJJjkbNbdO5DG6n3CQnZ1C7CumGdaZsddaYJueORROk7X+PnQZUq5bKqvdN7ZJEhK7qaerjogOF4TAotDV3ryLC6l/EWY01DkhGrf0hlXAkjQnOz28lXF40GNMd6pIjcoIbZze
V72v5s5q67fVdKdCzVE3BH76qX8qYS9YnN5JkGLERYA=
176
aditi@nessie:~/ToB-Summer19$ ./encrypt.new ""
r3/wMu5nD3rEFn7N88fCjQ==/MisK9RcK8RLsqjV2nrAfprghBYrBmeJS3FbJ4YG6zHBk+uA0CcZ+R4CSDolAaAPlCmkupfxy6bFHNEqyMVv7moPaiJEAkHDDU/FKen8eAJjMvz9+RK+xmQja238jk7xmaS6JbJOdh8teQ2XiMzlHsBYBVpw89UBFrTqOSN8qtlgU3aR4xUVlwZAA1+Pg2GHy
2CIWQI6ioHGDhN3P3po7MaOldJAgHGZO5d2GluroI70=
176
|
您可以在此处下载Fennec并查看使用说明。
如果您对工具有任何疑问或评论,可以在Twitter上通过@aditi_gupta0找到Aditi!
如果您喜欢这篇文章,请分享至:
Twitter
LinkedIn
GitHub
Mastodon
Hacker News
页面内容
近期文章
使用Deptective调查依赖项
做好准备,Buttercup,AIxCC评分轮正式开始!
使智能合约超越私钥风险的成熟方法
Go解析器中意想不到的安全隐患
审查Silence Laboratories首批DKLs23库的收获
© 2025 Trail of Bits.
使用Hugo和Mainroad主题生成。