代码混淆是逆向分析中最令人头疼的问题之一。
一
unidbg环境搭建
public AntiOllvm()
{
//创建模拟器
emulator = AndroidEmulatorBuilder
.for64Bit()
.addBackendFactory(new Unicorn2Factory(true))
.setProcessName("com.example.antiollvm")
.build();
Memory memory = emulator.getMemory();
//设置andorid系统库版本
memory.setLibraryResolver(new AndroidResolver(26));
//创建虚拟机
vm = emulator.createDalvikVM();
vm.setVerbose(true);
//加载动态库
dm = vm.loadLibrary(new File("f:\\kshs\\libtprt.so"), false);
module = dm.getModule();
}
public static void main(String[] args) {
AntiOllvm ao = new AntiOllvm();
ao.callJniOnload();
}public void callJniOnload()
{
dm.callJNI_OnLoad(emulator);
}
vm.loadLibrary(new File("f:\\androidlib\\libc.so"),false);
vm.loadLibrary(new File("f:\\androidlib\\libm.so"),false);
vm.loadLibrary(new File("f:\\androidlib\\libstdc++.so"),false);
vm.loadLibrary(new File("f:\\androidlib\\ld-android.so"),false);
vm.loadLibrary(new File("f:\\androidlib\\libdl.so"),false);
public void logIns()
{
emulator.getBackend().hook_add_new(new CodeHook() {
@Override
public void hook(Backend backend, long address, int size, Object user) {
Capstone capstone = new Capstone(Capstone.CS_ARCH_ARM64,Capstone.CS_MODE_ARM);
byte[] bytes = emulator.getBackend().mem_read(address, 4);
Instruction[] disasm = capstone.disasm(bytes, 0);
System.out.printf("%x:%s %s\n",address-module.base ,disasm[0].getMnemonic(),disasm[0].getOpStr());
}@Override
public void onAttach(UnHook unHook) {}
@Override
public void detach() {}
}, module.base, module.base+module.size, null);
}
二
去除间接跳转
CMP W8, W27
CSEL X9, X28, X23, LT
LDR X9, [X19,X9]
ADD X9, X9, X24
BR X9
*(x19+x28)+x24 //记为addrT。
*(x19+x23)+x24 //记为addrF。
CMP W8, W27
NOP
NOP
BLT addrT
B addrF
//保存指令和寄存器环境类:
class InsAndCtx
{
long addr;
Instruction ins;
List<Number> regs;public long getAddr() {
return addr;
}public void setAddr(long addr) {
this.addr = addr;
}public void setIns(Instruction ins) {
this.ins = ins;
}public Instruction getIns() {
return ins;
}public void setRegs(List<Number> regs) {
this.regs = regs;
}public List<Number> getRegs() {
return regs;
}
}//patch类
class PatchIns{
long addr;//patch 地址
String ins;//patch的指令public long getAddr() {
return addr;
}public void setAddr(long addr) {
this.addr = addr;
}public String getIns() {
return ins;
}public void setIns(String ins) {
this.ins = ins;
}
}// 指令栈
private Stack<InsAndCtx> instructions;//所有需要patch的指令
private List<PatchIns> patchs;//保存指令寄存器环境
public List<Number> saveRegs(Backend bk)
{
List<Number> nb = new ArrayList<>();
for(int i=0;i<29;i++)
{
nb.add(bk.reg_read(i+Arm64Const.UC_ARM64_REG_X0));
}
nb.add(bk.reg_read(Arm64Const.UC_ARM64_REG_FP));
nb.add(bk.reg_read(Arm64Const.UC_ARM64_REG_LR));
return nb;
}//指令hook,每条指令执行前保存环境
public void processBr()
{
emulator.getBackend().hook_add_new(new CodeHook() {
@Override
public void hook(Backend backend, long address, int size, Object user) {
Capstone capstone = new Capstone(Capstone.CS_ARCH_ARM64,Capstone.CS_MODE_ARM);
byte[] bytes = emulator.getBackend().mem_read(address, 4);
Instruction[] disasm = capstone.disasm(bytes, 0);
InsAndCtx iac = new InsAndCtx();
iac.setIns(disasm[0]);
iac.setRegs(saveRegs(backend));
iac.setAddr(address);
instructions.push(iac);
do_processbr();
}@Override
public void onAttach(UnHook unHook) {
System.out.println("attach");
}@Override
public void detach() {
System.out.println("detach");
}
},module.base+start, module.base+end,null);
}//指令栈回溯,根据处理结果,生成patchIns,供最后统一patch
public void do_processbr()
{
Instruction ins = instructions.peek().getIns();
if(ins.getMnemonic().equals("br") && ins.getOpStr().equals("x9"))
{
boolean finish = false;
long base = -1;
long listoffset = -1;
long cond1 = -1;
long cond2 = -1;
String cond = "";
long addinstaddr = -1;
long brinsaddr = instructions.peek().getAddr() - module.base;
long selectaddr = -1;
long ldaaddr = -1;try {
while (!finish && !instructions.empty())
{
instructions.pop();
ins = instructions.peek().getIns();
if(ins.getMnemonic().toLowerCase(Locale.ROOT).equals("add"))
{
String[] split = ins.getOpStr().split(",");
if(split.length == 3)
{
if(split[0].toLowerCase(Locale.ROOT).trim().equals("x9") && split[1].toLowerCase(Locale.ROOT).trim().equals("x9"))
{
String reg = split[2].trim().toLowerCase(Locale.ROOT);
base = getRegValue(reg,instructions.peek().getRegs()).longValue();
addinstaddr = instructions.peek().getAddr() - module.base;
}
else {
break;
}
}
else
{
break;
}
}if(ins.getMnemonic().toLowerCase(Locale.ROOT).equals("ldr"))
{
String[] sp = ins.getOpStr().toLowerCase().split(",");
if(sp.length == 3)
{
if(sp[0].trim().toLowerCase(Locale.ROOT).equals("x9") && sp[2].trim().toLowerCase(Locale.ROOT).equals("x9]"))
{
String reg = sp[1].toLowerCase(Locale.ROOT).trim().substring(1);
listoffset = getRegValue(reg,instructions.peek().getRegs()).longValue()-module.base;
ldaaddr = instructions.peek().getAddr()- module.base;
}
}
}if(ins.getMnemonic().trim().toLowerCase(Locale.ROOT).equals("csel"))
{
String[] sp = ins.getOpStr().toLowerCase(Locale.ROOT).split(",");
if(sp.length == 4)
{
cond = sp[3].trim();
if(sp[0].trim().equals("x9"))
{
String reg1 = sp[1].trim();
String reg2 = sp[2].trim();
cond1 = getRegValue(reg1,instructions.peek().getRegs()).longValue();
cond2 = getRegValue(reg2,instructions.peek().getRegs()).longValue();
selectaddr = instructions.peek().getAddr() - module.base;
}
}
}if(ins.getMnemonic().trim().toLowerCase(Locale.ROOT).equals("cmp"))
{
if(base == -1 || listoffset == -1 || cond1 == -1 || cond2 == -1 || cond.equals("") || addinstaddr == -1 || ldaaddr == -1 || selectaddr == -1)
{
break;
}
else
{
long offset1 = base + readInt64(emulator.getBackend(), module.base+listoffset+cond1) - module.base;
long offset2 = base + readInt64(emulator.getBackend(),module.base+listoffset+cond2) - module.base;
if( brinsaddr - addinstaddr != 4)
{
System.out.println("add ins and br ins gap more than 4 size,may make mistake");
}
String condBr = "b"+cond.toLowerCase(Locale.ROOT) + " 0x"+ Integer.toHexString((int) (offset1 - addinstaddr));
String br = "b 0x" + Integer.toHexString((int)(offset2 - brinsaddr));
PatchIns pi1 = new PatchIns();
pi1.setAddr(addinstaddr);
pi1.setIns(condBr);
patchs.add(pi1);
PatchIns pi2 = new PatchIns();
pi2.setAddr(brinsaddr);
pi2.setIns(br);
patchs.add(pi2);
PatchIns pi3 = new PatchIns();
pi3.setAddr(selectaddr);
pi3.setIns("nop");
patchs.add(pi3);
PatchIns pi4 = new PatchIns();
pi4.setAddr(ldaaddr);
pi4.setIns("nop");
patchs.add(pi4);
finish = true;
}
}
}
}catch (Exception e)
{
e.printStackTrace();
}
}
}//遍历patch表,执行patch,生成新的so,使用Ketstone将汇编转为机器码。
public void patch()
{
try {
File f = new File(inName);
FileInputStream fis = new FileInputStream(f);
byte[] data = new byte[(int) f.length()];
fis.read(data);
fis.close();
for(PatchIns pi:patchs)
{
System.out.println("procrss addr:"+Integer.toHexString((int) pi.addr)+",code:"+pi.getIns());
Keystone ks = new Keystone(KeystoneArchitecture.Arm64, KeystoneMode.LittleEndian);
KeystoneEncoded assemble = ks.assemble(pi.getIns());
for(int i=0;i<assemble.getMachineCode().length;i++)
{
data[(int) pi.addr+i] = assemble.getMachineCode()[i];
}
}
File fo = new File(outName);
FileOutputStream fos = new FileOutputStream(fo);
fos.write(data);
fos.flush();
fos.close();
System.out.println("finish");
}
catch (Exception e)
{
e.printStackTrace();
}
}
三
去除控制流平坦化
//用于记录条件块中,条件成立,条件不成立时对应的索引值,同时记录条件。
class selectBr
{
long insaddr;
long trueindex;
long falseindex;
String cond;
public String getCond() {
return cond;
}
public void setCond(String cond) {
this.cond = cond;
}
public long getInsaddr() {
return insaddr;
}
public void setInsaddr(long insaddr) {
this.insaddr = insaddr;
}
public long getTrueindex() {
return trueindex;
}
public void setTrueindex(long trueindex) {
this.trueindex = trueindex;
}
public long getFalseindex() {
return falseindex;
}
public void setFalseindex(long falseindex) {
this.falseindex = falseindex;
}
}
//真实块,索引值和开始地址
class TrueBlock{long index;
long startAddr;public TrueBlock(){}
public TrueBlock(long l,long s)
{
index = l;
startAddr = s;
}public long getIndex() {
return index;
}public void setIndex(long index) {
this.index = index;
}public long getStartAddr() {
return startAddr;
}public void setStartAddr(long startAddr) {
this.startAddr = startAddr;
}
}//记录真实块
private List<TrueBlock>tbs;
//记录条件块
private List<selectBr> sbs ;
//记录索引顺序
private List<Long> indexOrder;public void processFlt()
{
emulator.getBackend().hook_add_new(new CodeHook() {
@Override
public void hook(Backend backend, long address, int size, Object user) {
Capstone capstone = new Capstone(Capstone.CS_ARCH_ARM64,Capstone.CS_MODE_ARM);
byte[] bytes = emulator.getBackend().mem_read(address, 4);
Instruction[] disasm = capstone.disasm(bytes, 0);
InsAndCtx iac = new InsAndCtx();
iac.setIns(disasm[0]);
iac.setRegs(saveRegs(backend));
iac.setAddr(address);
instructions.add(iac);
do_processflt();
}@Override
public void onAttach(UnHook unHook) {
System.out.println("attach");
}@Override
public void detach() {
System.out.println("detach");
}
},module.base+start, module.base+end, null);
}
public void do_processflt()
{
if(instructions.empty())
{
return;
}
Instruction ins = instructions.peek().getIns();
if(instructions.peek().getAddr() - module.base == dispatcher)
{
indexOrder.add(getRegValue("x8",instructions.peek().getRegs()).longValue());
}
if(ins.getMnemonic().toLowerCase(Locale.ROOT).equals("b.eq")) {
InsAndCtx beq = instructions.peek();
//等于跳转,检查是否为cmp x8,
while (true)
{
if(instructions.empty())
{
break;
}
instructions.pop();
ins = instructions.peek().getIns();
if(ins.getMnemonic().toLowerCase(Locale.ROOT).equals("cmp"))
{
String[] sp = ins.getOpStr().toLowerCase(Locale.ROOT).split(",");
if(sp[0].equals("w8"))
{
//找到一个真实块
TrueBlock tb = new TrueBlock();
long regValue = getRegValue(sp[1].trim(), instructions.peek().getRegs()).longValue();
long targetAddr = 0;
String offset = beq.getIns().getOpStr().toLowerCase(Locale.ROOT);
long offsetvalue = getLongFromOpConst(offset);
targetAddr = beq.getAddr() + offsetvalue - module.base;
tb.setIndex(regValue);
tb.setStartAddr(targetAddr);
tbs.add(tb);
break;
}
}
}
}
//处理分支块
if(ins.getMnemonic().toLowerCase(Locale.ROOT).equals("b"))
{
long offset = getLongFromOpConst(ins.getOpStr());
if(offset != 0)
{
long target = offset + instructions.peek().getAddr() - module.base;
//直接跳向主发生器
if(target == dispatcher)
{
instructions.pop();
ins = instructions.peek().getIns();
if(ins.getMnemonic().toLowerCase(Locale.ROOT).equals("csel"))
{
String[] sp = ins.getOpStr().toLowerCase(Locale.ROOT).split(",");
if(sp[0].trim().equals("w8"))
{
String cond = sp[3].trim();
String reg1 = sp[1].trim();
String reg2 = sp[2].trim();
selectBr sb = new selectBr();
sb.setInsaddr(instructions.peek().getAddr() - module.base);
sb.setCond(cond);
sb.setTrueindex(getRegValue(reg1,instructions.peek().getRegs()).longValue());
sb.setFalseindex(getRegValue(reg2,instructions.peek().getRegs()).longValue());
sbs.add(sb);
}
}
}
}
}
}
四
总结
看雪ID:乐子人
https://bbs.kanxue.com/user-home-872365.htm
# 往期推荐
3、安卓加固脱壳分享
球分享
球点赞
球在看