remill是一个能将机器码转化为LLVM IR的library,这个library只专注于LLVM IR的提取,所以作为工具使用的话,还要用官方写的另外一个工具,McSema
我们接下来将分析一下remill的源码,因为官方写的McSema比较大,因此我们用官方写的一个小例子作为分析的入口吧
我们先来看下关于这个小工具的Readme,简单了解下它能做什么
remill-lift
is an example program that shows how to use some of the Remill
APIs, specifically, the TraceLifter
API.
Here is an example usage of remill-lift
:
1 | remill-lift-6.0 --arch amd64 --ir_out /dev/stdout --bytes c704ba01000000 |
This lifts the AMD64 mov DWORD PTR [rdx + rdi * 4], 1
to LLVM bitcode. It will output the lifted module to the stdout
, showing something similar to the following:
1 | ; Function Attrs: noinline nounwind ssp |
简单来说就是将一段机器码翻译为IR
我们来看下main函数吧
main函数
略过一堆初始化判断的过程
首先是用UnhexlifyInputBytes将输入的hex转化为byte,存到Memory这个类型里面
然后一系列初始化获取trace_lifter
1 | Memory memory = UnhexlifyInputBytes(addr_mask); |
然后下面是真正的Lift过程,调用Lift函数,我们跟进去看一下
1 | // Lift all discoverable traces starting from `--entry_address` into |
trace_lifter.Lift
在这里我们也同样跳过一堆判断,直接跳到Decode那里
首先是一个while循环,判断是否还有没有被处理的inst
然后获取地址,获取地址所在的block
假如这个inst已经被lift了,就继续
假如这个inst是某个trace的第一个inst,就不decode和lift,跳过这个inst
1 | // Decode instructions. |
这里是读取inst的byte
1 | // Read instruction bytes. |
接下来是利用直接decode这个inst,转换为XED形式,详细的就不多说
1 | (void) arch->DecodeInstruction(inst_addr, state.inst_bytes, state.inst); |
然后下面就是真正lift的部分,我们跟进去看一下
1 | auto lift_status = inst_lifter.LiftIntoBlock(state.inst, state.block); |
inst_lifter.LiftIntoBlock
首先初始化一下,然后调用GetInstructionFunction,我们跟进去看一下1
2
3
4
5
6
7llvm::Function *func = block->getParent();
llvm::Module *module = func->getParent();
llvm::Function *isel_func = nullptr;
auto status = kLiftedInstruction;
if (arch_inst.IsValid()) {
isel_func = GetInstructionFunction(module, arch_inst.function);
GetInstructionFunction
这里注释也说的非常清楚了,找到那个实现了该inst语义相等的函数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// Try to find the function that implements this semantics.
llvm::Function *GetInstructionFunction(llvm::Module *module,
const std::string &function) {
std::stringstream ss;
ss << "ISEL_" << function;
auto isel_name = ss.str();
auto isel = FindGlobaVariable(module, isel_name);
if (!isel) {
return nullptr; // Falls back on `UNIMPLEMENTED_INSTRUCTION`.
}
if (!isel->isConstant() || !isel->hasInitializer()) {
LOG(FATAL)
<< "Expected a `constexpr` variable as the function pointer for "
<< "instruction semantic function " << function
<< ": " << LLVMThingToString(isel);
}
auto sem = isel->getInitializer()->stripPointerCasts();
return llvm::dyn_cast_or_null<llvm::Function>(sem);
}
我们可以在github的例子看一下 How to implement the semantics of an instruction
就是实现以下的函数,然后和fcd差不多,利用clang生成其IR,再进行优化
1 | template <typename D, typename S1, typename S2> |