【S2E插件分析】Recipe插件 -- 2.PovGenerationPolicy

Recipe 插件 – 2. PovGenerationPolicy

根据 【S2E插件分析】Recipe插件 -- 1.Recipe ,最后是 PovGenerationPolicy 插件注册了 Recipe 的 onPovReady 事件。

1
m_recipe->onPovReady.connect(sigc::bind(sigc::mem_fun(*this, &PovGenerationPolicy::onPovReadyHandler), false));

这里主要研究它干了什么。

1
2
3
4
5
6
void PovGenerationPolicy::onPovReadyHandler(
S2EExecutionState *state,
const PovOptions &opt,
const std::string &recipeName,
bool isCrash
);

PovOptions 的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct PovOptions {
PovType m_type;
uint64_t m_faultAddress;
uint64_t m_ipMask;
uint64_t m_regMask;
uint64_t m_regNum;
size_t m_bytesBeforeSecret;
ExprList m_extraConstraints;
VariableRemapping m_remapping;
PovOptions() {
m_type = POV_GENERAL;
m_faultAddress = 0;
m_ipMask = 0;
m_regMask = 0;
m_regNum = 0;
m_bytesBeforeSecret = 0;
}
};

在 Recipe.cpp 发出 onPovReady 时,是这样设置 PovOptions 变量的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PovOptions opt;

opt.m_type = settings.type; // POV_GENERAL, POV_TYPE1, POV_TYPE2 三选一
opt.m_faultAddress = state->regs()->getPc();
// 在 Recipe.cpp 中处理的每一条语句附加的约束,都保存在 constraints 向量里。
opt.m_extraConstraints = l_recipeConditions.constraints;
opt.m_remapping = l_recipeConditions.remappings;

if (settings.type == PovType::POV_TYPE1) {
opt.m_ipMask = settings.ipMask;
opt.m_regMask = settings.regMask;
opt.m_regNum = settings.gp->reg();
} else if (settings.type == PovType::POV_TYPE2) {
opt.m_bytesBeforeSecret = settings.skip;
}

下面是 onPovReadyHandler() 的具体内容:

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
   getInfoStream(state) << "Generating PoV type " << opt.m_type << " at " << hexval(opt.m_faultAddress) << " from recipe '" << recipeName << "'\n";

UniquePovKey uniquePovKey = std::make_tuple(opt.m_faultAddress, opt.m_type, recipeName);
// 限制同一类型的 recipe 不能应用太多次
if (m_uniquePovMap[uniquePovKey] >= m_maxPovCount) {
getDebugStream(state) << "PoV limit reached\n";
return;
}
m_uniquePovMap[uniquePovKey]++;


std::string prefix;
if (recipeName.length()) {
std::stringstream povFilenameSS;
povFilenameSS << "recipe-" << recipeName;
prefix = povFilenameSS.str();
} else if (isCrash) {
// 这里 Recipe.cpp 中发出 onPovReady 事件时没有传入 isCrash 变量,
// 不知道是怎么处理的。
prefix = "crash";
}

std::vector<std::string> filePaths;
// 直接调用 PovGenerator 生成利用
if (!m_povGenerator->generatePoV(state, opt, prefix, filePaths)) {
getWarningsStream(state) << "Failed to generate PoV\n";
return;
}

onPovReady.emit(state, opt, recipeName, filePaths, isCrash ? CRASH : POV);

这里又弹出了 onPovReady 事件,不过这个事件只在 CGCInterface 这个插件里使用,是用来在 CGC 里提交 flag 的,就不看了。

所以,其实约束求解的地方还是在 PovGenerator 插件中。

这里还有一个我感兴趣的地方是,调用 Recipe 生成了 Pov 之后,对应的状态会被杀死。而杀死的操作是在这个插件(PovGenerationPolicy) 里完成的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// This will kill any states that fork because of target symbolic pc provided that there was
// already a PoV generated for at the source instruction pc.

void PovGenerationPolicy::onSymbolicAddress(S2EExecutionState *state, ref<Expr> virtualAddress,
uint64_t concreteAddress, bool &concretize,
CorePlugin::symbolicAddressReason reason) {
if (!m_process->isTrackedPc(state, state->regs()->getPc(), true)) {
return;
}

if (reason != CorePlugin::symbolicAddressReason::PC) {
return;
}

for (const auto &it : m_uniquePovMap) {
auto pc = std::get<0>(it.first);
if (state->regs()->getPc() == pc) {
s2e()->getExecutor()->terminateState(*state, "Killing state because that PC has already generated a PoV");
}
}
}

根据注释中的描述,符号化的 pc 会导致 S2E fork,由于已经生成了 pov,那么就不需要再继续运行这个 state 了,因此把生成了 pov 的 state 都杀死。

这里我有两个疑问:

  1. 通过 onSymbolicAddress 事件来杀死 state,但在 Recipe.cpp 中,是通过 onSymbolicAddress 事件来触发 tryRecipes,如何保证 Recipe.cpp 注册的回调函数优先于 PovGenerationPolicy 呢?
  2. 如果我希望修改 Recipe,让约束附加之后不终止,而是继续运行,在合适的时机附加下一次约束,那么,符号化的 pc 是否会 fork 从而影响我的操作?我应该如何处理?