📄 scan.cpp
字号:
//std::cout << "[case 3]";
// unhandled jmp indirect.
done = true;
break;
case insn__calli:
if(target_is_import(module, offset, s))
{
break;
}
if(get_argtype_lo(s.icode->argtype[0]) == argtype_mem)
{
// call [<target>]
// check for known target. if found, we need to visit it.
if(s.icode->has_disp && !s.icode->ea.disp8)
{
U4 disp = s.icode->disp;
if(s.icode->ea.base == 31 &&s.icode->ea.index == 31 && disp >= module.image_base())
{
disp -= module.image_base();
if(disp + 4 - 1 < module.image_size())
{
// We already made sure the taret isn't an import name.
U4 value = module.get_dword(disp);
if(value >= module.image_base())
{
value -= module.image_base();
if(value < module.image_size())
{
// 'value' is a default jmp [<value>] target. we do not care whether there's
// a relocation there or not. we will visit it too.
if(!scanx.meta[value].target)
{
//std::cerr << std::hex << value + module.image_base() << std::dec << std::endl;
scanx.meta[value].target = 1;
targets.push_front(value);
//calls.push_front(value);
}
if(!scanx.meta[value].procedure)
{
scanx.meta[value].procedure = 1;
calls.push_front(value);
}
break;
}
}
}
}
}
}
break;
case insn_jmp:
// follow target.
// If we have jmp to next insn, we don't have to do anything special.
tmp = (U8)s.icode->imm + (U8)offset + (U8)s.size + (U8)module.image_base();
tmp &= (U8)(U4)0xffffffff;
if(tmp < module.image_base())
{
std::cerr << " error!" << std::endl;
std::cerr << "[1] JMP to " << std::hex << tmp << std::dec << " is below image!" << std::endl;
return false;
}
tmp -= module.image_base();
offset = tmp;
if(offset >= module.image_size())
{
std::cerr << " error!" << std::endl;
std::cerr << "Tried to scan insn at " << std::hex << (offset + module.image_base()) << std::dec << " which is beyond image!" << std::endl;
return false;
}
// bugfix 01-02-2009
scanx.meta[offset].target = 1;
// end bugfix
// bugfix 01-03-2009
scanx.meta[offset].branch = 1;
// end bugfix
continue;
case insn_call:
tmp = (U8)s.icode->imm + (U8)offset + (U8)s.size + (U8)module.image_base();
tmp &= (U8)(U4)0xffffffff;
if(tmp < module.image_base())
{
std::cerr << " error!" << std::endl;
std::cerr << std::hex << offset + module.image_base() << std::dec << std::endl;
std::cerr << "[2] JMP to " << std::hex << tmp << std::dec << " is below image!" << std::endl;
return false;
}
tmp -= module.image_base();
if(tmp >= module.image_size())
{
std::cerr << " error!" << std::endl;
std::cerr << "CALL to " << std::hex << tmp + module.image_base() << std::dec << " is beyond image!" << std::endl;
return false;
}
//bugfix.
//if(tmp == (U8)(offset) + (U8)(obj[offset].length))
// ; // call to following insn
//else
if(!scanx.meta[tmp].target)
{
scanx.meta[tmp].target = 1;
targets.push_front(tmp);
//calls.push_front(tmp);
}
// begin change
if(!scanx.meta[tmp].procedure)
{
calls.push_front(tmp);
scanx.meta[tmp].procedure = 1;
}
// end change
// bugfix--done if noreturn.
if(noreturn_call(module, tmp))
{
//std::cout << "Detected noreturn" << std::endl;
done = true;
}
break;
case insn__jcc:
case insn__jrcxz:
case insn__loopnz:
case insn__loopz:
case insn__loop:
tmp = (U8)s.icode->imm + (U8)offset + (U8)s.size + (U8)module.image_base();
tmp &= (U8)(U4)0xffffffff;
if(tmp < module.image_base())
{
std::cerr << " error!" << std::endl;
std::cerr << "[3] JMP to " << std::hex << tmp << std::dec << " is below image!" << std::endl;
return false;
}
tmp -= module.image_base();
if(tmp >= module.image_size())
{
std::cerr << " error!" << std::endl;
std::cerr << "CALL to " << std::hex << tmp + module.image_base() << std::dec << " is beyond image!" << std::endl;
return false;
}
// bug fix--always need to do this, because there's an in-label there.
scanx.meta[tmp].branch = 1;
// end bug fix
if(tmp == (U8)(offset) + (U8)(scanx.meta[offset].length))
; // branch to following insn
else
{
if(!scanx.meta[tmp].target)
{
scanx.meta[tmp].target = 1;
targets.push_front(tmp);
}
}
break;
default:
break;
}
if(done)
break;
offset += (U8)(scanx.meta[offset].length);
if(offset >= module.image_size())
{
std::cerr << " error!" << std::endl;
std::cerr << "Tried to scan insn at " << std::hex << (offset + module.image_base()) << std::dec << " which is beyond image!" << std::endl;
return false;
}
if(scanx.meta[offset].target == 1)
{
// Crossed our tracks twice, e.g. been here already.
break;
}
} // for
} // entrypoints for loop
// Next thing to do is build the call graph (this is the second pass).
// We have a list of calls--anything CALLed or an external entrypoint (which are calls too).
// There is the possibility, in principle, for DATA to be exported. At best, this will lead to
// an invalid opcode. I know of no C/C++ compiler that will "fall thru" from one procedure to
// another, although sloppy ASM coders might do that. In general, if an invalid opcode is
// encountered, it means we do not support the instruction yet, or it means we're trying to
// decode DATA.
// - Basically, if we enter another procedure's ENTRYPOINT, either by falling thru or via a
// jump, and it's an exter....
for(std::list<U8>::iterator iter = calls.begin(); iter != calls.end(); ++iter)
{
assert(scanx.meta[*iter].procedure == 1);
}
U8 procofs, q, next;
U4 procnum = 0;
//scanprocs_t procs;
procs.procs.clear();
int dots_printed = 0;
int dots_index = 0;
int dots_count = calls.size();
int dots_next = dots_count / 10;
int dots_wanted;
while(!calls.empty())
{
++dots_index;
dots_wanted = (dots_index * 10) / dots_count;
if(dots_wanted > dots_printed)
{
dots_next += dots_count / 10;
dots_wanted -= dots_printed;
while(dots_wanted != 0)
{
std::cerr << ".";
--dots_wanted;
++dots_printed;
}
}
procofs = calls.back();
calls.pop_back();
++procnum;
scanproc_t &proc = procs.proc(procofs);
// What does this do? no_decompile BEGINS equal to 0, isn't this just
// a reality check?
assert(proc.u.s.no_decompile == 0);
targets.clear();
targets.push_front(procofs);
fcn[procofs] = procnum;
if(proc.blocks.find(procofs) != proc.blocks.end())
{
std::cerr << " internal error, reentered a procedure!" << std::endl;
return false;
}
// create a new basic block.
scanbb_t *bb = &proc.blocks[procofs];
bb->offset = procofs;
bb->stop = procofs;
assert(bb->stop < module.image_size());
bb->out_edges.clear();
bb->invokation = (U8)(-1ll);
while(!targets.empty())
{
offset = targets.back();
targets.pop_back();
if(proc.blocks.find(offset) != proc.blocks.end())
bb = &proc.blocks[offset];
else
{
// We're creating a new basic block.
bb = &proc.blocks[offset];
bb->offset = offset;
bb->stop = offset;
assert(bb->stop < module.image_size());
bb->out_edges.clear();
bb->invokation = (U8)(-1ll);
}
//std::cerr << std::hex << offset << std::dec << std::endl;
icode_size = 0;
icode_index = 15;
bool done;// = false;
for(;;)
{
assert(offset < module.image_size());
// std::cout << std::hex << offset + module.image_base() << std::dec << std::endl;
icode_index = (icode_index + 1) & 15;
if(icode_size < 16)
++icode_size;
icode_list[icode_index] = offset;
if(offset != procofs)
{
// This is not the entrypoint.
bb->stop = offset;
assert(bb->stop < module.image_size());
}
if(fcn[offset] != procnum)
{
// This is part of another procedure!
if(fcn[offset] == 0)
{
std::cerr << "(INTERNAL ERROR)!" << std::endl;
return false;
}
if(offset != procofs && // is this test necessary?
scanx.meta[offset].procedure)
{
// And it's an entry-point.
// fixme--add out edge.
// If here, we either fell thru to another procedure's entrypoint, or
// we JUMPed to it. This is needs an out edge.
bb->invokation = offset;
// --- begin changes ---
proc.calls.insert(offset);
// --- end changes ---
break;
}
// Reenter it.
fcn[offset] = procofs;
}
if(scanx.meta[offset].length == 0)
{
// This is an invalid opcode!
// fixme--don't decompile this procedure.
#if 0
std::cerr << "**** INVALID OPCODE ****" << std::endl;
if(icode_size > 0)
{
offset = (icode_list[(icode_index - 1) & 15]);
std::cerr << "Was just here: " << std::hex << module.image_base() + offset << std::dec << std::endl;
}
return false;
#endif
proc.u.s.no_decompile = 1;
break;
}
// Ok, valid opcode!
s.icode = &scanx.icode[offset];
s.size = scanx.meta[offset].length;
done = false;
switch(s.icode->insn)
{
// for any type of return, we're done with the current visit.
case insn__retfnum:
case insn__retf:
case insn__iret:
proc.u.s.no_decompile = 1;
done = true;
assert(bb->out_edges.empty());
bb->out_edges.push_back(offset + (U8)(scanx.meta[offset].length));
break;
case insn__ret:
case insn__retnum:
{
UINT depth = 0;
if(s.icode->insn == insn__retnum)
depth = (UINT)(U4)(U2)(s.icode->imm);
if(proc.return_depth == -1)
proc.return_depth = depth;
else
if(proc.return_depth != depth)
proc.return_many = true;
done = true;
assert(bb->out_edges.empty());
bb->out_edges.push_back(offset + (U8)(scanx.meta[offset].length));
break;
}
case insn__jmpfd:
case insn__jmpfi:
// jmp far - unhandled. don't decompile this procedure.
proc.u.s.no_decompile = 1;
done = true;
assert(bb->out_edges.empty());
bb->out_edges.push_back(offset + (U8)(scanx.meta[offset].length));
break;
case insn__callfd:
case insn__callfi:
// call far - unhandled. don't decompile this procedure.
proc.u.s.no_decompile = 1;
// note: calls do not interrupt basic blocks.
break;
case insn__jmpi:
// --- begin ---
if(target_is_import(module, offset, s))
{
done = true;
// add out edge for call graph--the jmp [indirect] is to an external procedure and is
// thus an invokation.
proc.imports.insert(module.lt_int.target->names[s.icode->disp - module.image_base()].import_name);
assert(bb->out_edges.empty());
bb->out_edges.push_back(offset + (U8)(scanx.meta[offset].length));
break;
}
if(get_argtype_lo(s.icode->argtype[0]) == argtype_mem)
{
// jmp [<target>]
std::list<U8> swtargets;
if(handle_switch(module, offset, s, scanx, swtargets) || handle_switch_zx(module, offset, s, scanx, swtargets))
{
U8 x;
assert(bb->out_edges.empty());
bb->out_edges.push_back(offset + (U8)(scanx.meta[offset].length));
for(std::list<U8>::iterator i = swtargets.begin(); i != swtargets.end(); ++i)
{
x = *i;
if(x < module.image_base())
{
std::cerr << " error!" << std::endl;
std::cerr << "Insn at " << std::hex << (offset + module.image_base()) << std::dec << " is switch with target to " << std::hex << x << std::dec << " which is below image base!" << std::endl;
return false;
}
x -= module.image_base();
if(x >= module.image_size())
{
std::cerr << " error!" << std::endl;
std::cerr << "Insn at " << std::hex << (offset + module.image_base()) << std::dec << " is switch with target to " << std::hex << (x + module.image_base()) << std::dec << " which is beyond image!" << std::endl;
return false;
}
/*
if(!scanx.meta[x].target)
{
//std::cerr << std::hex << (offset + module.image_base()) << " -> " << (x + module.image_base()) << std::dec << std::endl;
scanx.meta[x].target = 1;
targets.push_front(x);
}
*/
// out_edge[1] is switch target 0.
bb->out_edges.push_back(x);
if(fcn[x] != procnum)
{
// Target is not a part of current procedure we've already visited.
// Because this is a jmp [<target>], we will reenter it.
fcn[x] = procnum;
targets.push_front(x);
}
}
done = true;
break;
}
// check for known target. if found, we need to visit it... but whose procedure is it?
if(s.icode->has_disp && !s.icode->ea.disp8)
{
U4 disp = s.icode->disp;
if(s.icode->ea.base == 31 &&s.icode->ea.index == 31 && disp >= module.image_base())
{
disp -= module.image_base();
if(disp + 4 - 1 < module.image_size())
{
// We already made sure the taret isn't an import name.
U4 value = module.get_dword(disp);
if(value >= module.image_base())
{
value -= module.image_base();
if(value < module.image_size())
{
// 'value' is a default jmp [<value>] target. we do not care whether there's
// a relocation there or not. we will visit it too.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -