使用unicorn来trace还原ollvm混淆的非标准算法
frida -U com.kanxue.ollvm_ndk_9 -l dump_so.js
dump_so("libnative-lib.so")
if __name__ == '__main__':
initGlobalData()
#创建uc对象
uc=unicorn.Uc(unicorn.UC_ARCH_ARM64,unicorn.UC_MODE_ARM)
#从内存中dump下来so的基址
code_addr=0x7eae047000
#用来存放so代码的大小,尽量大一点。内存不值钱
code_size=8*0x1000*0x1000
#创建一块内存
uc.mem_map(code_addr,code_size)
#在上面那块内存后面继续划一片内存来当做栈空间
stack_addr=code_addr+code_size
stack_size=0x1000
#栈顶的位置,这里是64位的,所以偏移8个字节
stack_top=stack_addr+stack_size-0x8
#申请一块栈空间
uc.mem_map(stack_addr,stack_size)
#栈空间往后继续划一块空间用来存放参数
args_addr=stack_addr+stack_size
args_size=0x1000
uc.mem_map(args_addr, args_size)
#设置每句汇编执行都会调用hook_code
#uc.hook_add(unicorn.UC_HOOK_CODE,hook_code)
#读取so
with open("./libnative-lib.so_0x7eae047000_0x38000.so","rb") as f:
sodata=f.read()
#给前面创建的空间写入so的数据
uc.mem_write(code_addr,sodata)
#要执行的代码开始位置
start_addr=code_addr+0xFCB4
#要执行的代码结束位置
end_addr=code_addr+0xFF2C
#随机生成一个入参
input_str = ranstr(36)
print("input:%s input_addr:0x%x" % (input_str,args_addr))
input_byte=str.encode(input_str)
#将生成的入参写入前面创建的内存空间
uc.mem_write(args_addr,input_byte)
#ida中看到的函数有参数1、2,然后分别对应X0和X1,写入对应数据,栈寄存器给一个栈顶的地址
uc.reg_write(unicorn.arm64_const.UC_ARM64_REG_X0,args_addr)
uc.reg_write(unicorn.arm64_const.UC_ARM64_REG_X1,len(input_str))
uc.reg_write(unicorn.arm64_const.UC_ARM64_REG_SP,stack_top)
#开始执行代码段
uc.emu_start(start_addr,end_addr)
#ida中看到返回值是直接写在入参中,所以结果我们直接从入参的内存中读取
result=uc.mem_read(args_addr,args_size)
print("result:",result.decode(encoding="utf-8"))
#最后释放创建的内存
uc.mem_unmap(args_addr, args_size)
uc.mem_unmap(stack_addr,stack_size)
uc.mem_unmap(code_addr,code_size)
input:nxRH3WwuTJgUfqcOS94CM5QEkoPeF0sZ8mGj input_addr:0x7eb6048000
result: oySI2Vvt-KfTg-4NR8-BL4Pj-nQdG1r[9laf
uc.hook_add(unicorn.UC_HOOK_CODE,hook_code)
def hook_code(uc: unicorn.Uc, address, size, user_data):
inst_code=uc.mem_read(address,size)
for inst in cs.disasm(inst_code,size):
print("0x%x:\t%s\t%s" % (address, inst.mnemonic, inst.op_str))
0x7eae056f08: ldr x25, [sp, #0x10]
0x7eae056f0c: sub w9, w10, w9
0x7eae056f10: strb w8, [x0, #0x23]
0x7eae056f14: ldrb w8, [x11, w9, uxtw]
0x7eae056f18: strb w8, [x0, #0x22]
0x7eae056f1c: ldp x20, x19, [sp, #0x40]
0x7eae056f20: ldp x22, x21, [sp, #0x30]
0x7eae056f24: ldp x24, x23, [sp, #0x20]
0x7eae056f28: add sp, sp, #0x50
#上一次汇编指令
global pre_codestr
#上一次汇编的第一个寄存器名称
global pre_regname
#是否有记录上一次的数据
global has_pre
#监控的地址
global watch_addrs
import unicorn
import random
import string
import capstone
import re
import globalData
import binascii
def ranstr(num):
salt = ''.join(random.sample(string.ascii_letters + string.digits, num))
return salt
cs = capstone.Cs(capstone.CS_ARCH_ARM64, capstone.CS_MODE_ARM)
cs.detail = True
all_regs = None
reg_names = {
"X0": unicorn.arm64_const.UC_ARM64_REG_X0,
"X1": unicorn.arm64_const.UC_ARM64_REG_X1,
"X2": unicorn.arm64_const.UC_ARM64_REG_X2,
"X3": unicorn.arm64_const.UC_ARM64_REG_X3,
"X4": unicorn.arm64_const.UC_ARM64_REG_X4,
"X5": unicorn.arm64_const.UC_ARM64_REG_X5,
"X6": unicorn.arm64_const.UC_ARM64_REG_X6,
"X7": unicorn.arm64_const.UC_ARM64_REG_X7,
"X8": unicorn.arm64_const.UC_ARM64_REG_X8,
"X9": unicorn.arm64_const.UC_ARM64_REG_X9,
"X10": unicorn.arm64_const.UC_ARM64_REG_X10,
"X11": unicorn.arm64_const.UC_ARM64_REG_X11,
"X12": unicorn.arm64_const.UC_ARM64_REG_X12,
"X13": unicorn.arm64_const.UC_ARM64_REG_X13,
"X14": unicorn.arm64_const.UC_ARM64_REG_X14,
"X15": unicorn.arm64_const.UC_ARM64_REG_X15,
"X16": unicorn.arm64_const.UC_ARM64_REG_X16,
"X17": unicorn.arm64_const.UC_ARM64_REG_X17,
"X18": unicorn.arm64_const.UC_ARM64_REG_X18,
"X19": unicorn.arm64_const.UC_ARM64_REG_X19,
"X20": unicorn.arm64_const.UC_ARM64_REG_X20,
"X21": unicorn.arm64_const.UC_ARM64_REG_X21,
"X22": unicorn.arm64_const.UC_ARM64_REG_X22,
"X23": unicorn.arm64_const.UC_ARM64_REG_X23,
"X24": unicorn.arm64_const.UC_ARM64_REG_X24,
"X25": unicorn.arm64_const.UC_ARM64_REG_X25,
"X26": unicorn.arm64_const.UC_ARM64_REG_X26,
"X27": unicorn.arm64_const.UC_ARM64_REG_X27,
"X28": unicorn.arm64_const.UC_ARM64_REG_X28,
"W0": unicorn.arm64_const.UC_ARM64_REG_W0,
"W1": unicorn.arm64_const.UC_ARM64_REG_W1,
"W2": unicorn.arm64_const.UC_ARM64_REG_W2,
"W3": unicorn.arm64_const.UC_ARM64_REG_W3,
"W4": unicorn.arm64_const.UC_ARM64_REG_W4,
"W5": unicorn.arm64_const.UC_ARM64_REG_W5,
"W6": unicorn.arm64_const.UC_ARM64_REG_W6,
"W7": unicorn.arm64_const.UC_ARM64_REG_W7,
"W8": unicorn.arm64_const.UC_ARM64_REG_W8,
"W9": unicorn.arm64_const.UC_ARM64_REG_W9,
"W10": unicorn.arm64_const.UC_ARM64_REG_W10,
"W11": unicorn.arm64_const.UC_ARM64_REG_W11,
"W12": unicorn.arm64_const.UC_ARM64_REG_W12,
"W13": unicorn.arm64_const.UC_ARM64_REG_W13,
"W14": unicorn.arm64_const.UC_ARM64_REG_W14,
"W15": unicorn.arm64_const.UC_ARM64_REG_W15,
"W16": unicorn.arm64_const.UC_ARM64_REG_W16,
"W17": unicorn.arm64_const.UC_ARM64_REG_W17,
"W18": unicorn.arm64_const.UC_ARM64_REG_W18,
"W19": unicorn.arm64_const.UC_ARM64_REG_W19,
"W20": unicorn.arm64_const.UC_ARM64_REG_W20,
"W21": unicorn.arm64_const.UC_ARM64_REG_W21,
"W22": unicorn.arm64_const.UC_ARM64_REG_W22,
"W23": unicorn.arm64_const.UC_ARM64_REG_W23,
"W24": unicorn.arm64_const.UC_ARM64_REG_W24,
"W25": unicorn.arm64_const.UC_ARM64_REG_W25,
"W26": unicorn.arm64_const.UC_ARM64_REG_W26,
"W27": unicorn.arm64_const.UC_ARM64_REG_W27,
"W28": unicorn.arm64_const.UC_ARM64_REG_W28,
"SP": unicorn.arm64_const.UC_ARM64_REG_SP,
}
#初始化全局数据
def initGlobalData():
globalData.has_pre=False
globalData.pre_codestr=""
globalData.pre_regname=""
#添加监视列表,trace时打印该内存的变动
globalData.watch_addrs= {0x7eae07e060:""}
def hook_code(uc: unicorn.Uc, address, size, user_data):
inst_code=uc.mem_read(address,size)
for inst in cs.disasm(inst_code,size):
#判断是否保存有上次的指令,有的话,则先打印上次的指令,并且查询上次的第一个寄存器的新数值
if globalData.has_pre and globalData.pre_regname:
regindex = reg_names[globalData.pre_regname.upper()]
regvalue = uc.reg_read(regindex)
globalData.pre_codestr+="\t//%s=0x%x" % (globalData.pre_regname,regvalue)
print(globalData.pre_codestr)
globalData.pre_codestr=""
globalData.has_pre=False
#监控我关心的内存空间,如果发生变动会再打印
if len(globalData.watch_addrs)>0:
for i,v in globalData.watch_addrs.items():
idata= uc.mem_read(i,0x10)
buf= binascii.b2a_hex(idata)
hexstr=buf.decode(encoding="utf-8")
if globalData.watch_addrs[i]==hexstr:
continue
globalData.watch_addrs[i]=hexstr
print("0x%x\t%s" % (i, hexstr))
#拼接当前行的汇编指令
opstr="0x%x:\t%s\t%s" % (address, inst.mnemonic, inst.op_str)
#从当前行指令中匹配出所有的寄存器
res = re.findall(r'[^0]([wx][0-9]+)', " " + inst.op_str, re.I | re.M)
#如果有多个寄存器,取第一个为数值被改变的寄存器
if len(res)>0:
globalData.pre_regname = res[0]
res=list(set(res))
#如果有sp寄存器,则单独插入
if "sp" in inst.op_str:
res.append("sp")
#如果没有寄存器,则不需要记录为上次的,直接打印即可
if len(res)<=0:
has_pre=False
print(opstr)
continue
#记录数据为上次的指令
fenge = "\t\t------"
curreg=""
for regname in res:
regindex=reg_names[regname.upper()]
regvalue=uc.reg_read(regindex)
curreg+="%s=0x%x\t" % (regname,regvalue)
globalData.pre_codestr=opstr +fenge+ curreg
globalData.has_pre=True
# Press the green button in the gutter to run the script.
if __name__ == '__main__':
initGlobalData()
#创建uc对象
uc=unicorn.Uc(unicorn.UC_ARCH_ARM64,unicorn.UC_MODE_ARM)
#从内存中dump下来so的基址
code_addr=0x7eae047000
#用来存放so代码的大小,尽量大一点。内存不值钱
code_size=8*0x1000*0x1000
#创建一块内存
uc.mem_map(code_addr,code_size)
#在上面那块内存后面继续划一片内存来当做栈空间
stack_addr=code_addr+code_size
stack_size=0x1000
#栈顶的位置,这里是64位的,所以偏移8个字节
stack_top=stack_addr+stack_size-0x8
#申请一块栈空间
uc.mem_map(stack_addr,stack_size)
#栈空间往后继续划一块空间用来存放参数
args_addr=stack_addr+stack_size
args_size=0x1000
uc.mem_map(args_addr, args_size)
#设置每句汇编执行都会调用hook_code
uc.hook_add(unicorn.UC_HOOK_CODE,hook_code)
#读取so
with open("./libnative-lib.so_0x7eae047000_0x38000.so","rb") as f:
sodata=f.read()
#给前面创建的空间写入so的数据
uc.mem_write(code_addr,sodata)
#要执行的代码开始位置
start_addr=code_addr+0xFCB4
#要执行的代码结束位置
end_addr=code_addr+0xFF2C
#随机生成一个入参
input_str = ranstr(36)
print("input:%s input_addr:0x%x" % (input_str,args_addr))
input_byte=str.encode(input_str)
#将生成的入参写入前面创建的内存空间
uc.mem_write(args_addr,input_byte)
#ida中看到的函数有参数1、2,然后分别对应X0和X1,写入对应数据,栈寄存器给一个栈顶的地址
uc.reg_write(unicorn.arm64_const.UC_ARM64_REG_X0,args_addr)
uc.reg_write(unicorn.arm64_const.UC_ARM64_REG_X1,len(input_str))
uc.reg_write(unicorn.arm64_const.UC_ARM64_REG_SP,stack_top)
#开始执行代码段
uc.emu_start(start_addr,end_addr)
#ida中看到返回值是直接写在入参中,所以结果我们直接从入参的内存中读取
result=uc.mem_read(args_addr,args_size)
print("result:",result.decode(encoding="utf-8"))
#最后释放创建的内存
uc.mem_unmap(args_addr, args_size)
uc.mem_unmap(stack_addr,stack_size)
uc.mem_unmap(code_addr,code_size)
input:L8Cs5ntgNWiPMQRUKYXva0juFw9JqI4ApyZT input_addr:0x7eb6048000
0x7eae07e060 30313233343536373839616263646566
0x7eae056cb8: str x25, [sp, #0x10] ------x25=0x0 sp=0x7eb6047fa8 //x25=0x0
0x7eae056cbc: stp x24, x23, [sp, #0x20] ------x23=0x0 x24=0x0 sp=0x7eb6047fa8 //x24=0x0
0x7eae056cc0: stp x22, x21, [sp, #0x30] ------x21=0x0 x22=0x0 sp=0x7eb6047fa8 //x22=0x0
0x7eae056cc4: stp x20, x19, [sp, #0x40] ------x19=0x0 x20=0x0 sp=0x7eb6047fa8 //x20=0x0
0x7eae056cc8: ldrb w22, [x0, #0x18] ------x0=0x7eb6048000 w22=0x0 //w22=0x46
0x7eae056ccc: movz w8, #0xf6c3 ------w8=0x0 //w8=0xf6c3
0x7eae056cd0: movz w9, #0xa29a ------w9=0x0 //w9=0xa29a
0x7eae056cd4: movz w13, #0x4941 ------w13=0x0 //w13=0x4941
0x7eae056cd8: movz w14, #0x7f29 ------w14=0x0 //w14=0x7f29
0x7eae056cdc: movz w15, #0x57d9 ------w15=0x0 //w15=0x57d9
0x7eae056ce0: movz w16, #0xcdcc ------w16=0x0 //w16=0xcdcc
0x7eae056ce4: movz w17, #0x425b ------w17=0x0 //w17=0x425b
0x7eae056ce8: movz w2, #0x30e6 ------w2=0x0 //w2=0x30e6
0x7eae056cec: movz w3, #0x7f2a ------w3=0x0 //w3=0x7f2a
0x7eae056ee8: ldrb w8, [sp, #0xc] ------w8=0x6cdff6c3 sp=0x7eb6047fa8 //w8=0x90
0x7eae056eec: adrp x11, #0x28000 ------x11=0x9db //x11=0x7eae07e000
0x7eae056ef0: ldr w9, [sp, #0x1c] ------w9=0x6594a29a sp=0x7eb6047fa8 //w9=0x9db
0x7eae056ef4: add x11, x11, #0x60 ------x11=0x7eae07e000 //x11=0x7eae07e060
0x7eae056ef8: and x8, x8, #0xf ------x8=0x90 //x8=0x0
0x7eae056efc: ldr w10, [sp, #0x1c] ------w10=0x22 sp=0x7eb6047fa8 //w10=0x9db
0x7eae056f00: ldrb w8, [x11, x8] ------w8=0x0 x8=0x0 x11=0x7eae07e060 //w8=0x30
0x7eae056f04: and w9, w9, #0xfffffff0 ------w9=0x9db //w9=0x9d0
0x7eae056f08: ldr x25, [sp, #0x10] ------x25=0x0 sp=0x7eb6047fa8 //x25=0x0
0x7eae056f0c: sub w9, w10, w9 ------w9=0x9d0 w10=0x9db //w9=0xb
0x7eae056f10: strb w8, [x0, #0x23] ------w8=0x30 x0=0x7eb6048000 //w8=0x30
0x7eae056f14: ldrb w8, [x11, w9, uxtw] ------w8=0x30 w9=0xb x11=0x7eae07e060 //w8=0x62
0x7eae056f18: strb w8, [x0, #0x22] ------w8=0x62 x0=0x7eb6048000 //w8=0x62
0x7eae056f1c: ldp x20, x19, [sp, #0x40] ------x20=0x9db x19=0xa0504942 sp=0x7eb6047fa8 //x20=0x0
0x7eae056f20: ldp x22, x21, [sp, #0x30] ------x21=0x90 x22=0xa0504942 sp=0x7eb6047fa8 //x22=0x0
0x7eae056f24: ldp x24, x23, [sp, #0x20] ------x23=0x98c x24=0x4e sp=0x7eb6047fa8 //x24=0x0
ldrb w8, [x11, w9, uxtw] ------w8=0x30 w9=0xb x11=0x7eae07e060 //w8=0x62
0x7eae07e060 30313233343536373839616263646566
0x7eae07e060 0123456789abcdef
0x7eae056f0c: sub w9, w10, w9 ------w9=0x9d0 w10=0x9db //w9=0xb
0x7eae056f04: and w9, w9, #0xfffffff0 ------w9=0x9db //w9=0x9d0
0x7eae056e34: add w20, w23, w20 ------w20=0x4f w23=0x98c //w20=0x9db
"0x7eae056e34: add w20, w23, w20 ------w20=0x69 w23=0x0 //w20=0x69"
"0x7eae056e34: add w20, w23, w20 ------w20=0x6b w23=0x69 //w20=0xd4"
"0x7eae056e34: add w20, w23, w20 ------w20=0x6a w23=0xd4 //w20=0x13e"
"0x7eae056e34: add w20, w23, w20 ------w20=0x75 w23=0x13e //w20=0x1b3"
"0x7eae056e34: add w20, w23, w20 ------w20=0x50 w23=0x1b3 //w20=0x203"
"0x7eae056e34: add w20, w23, w20 ------w20=0x73 w23=0x203 //w20=0x276"
"0x7eae056e34: add w20, w23, w20 ------w20=0x37 w23=0x276 //w20=0x2ad"
"0x7eae056e34: add w20, w23, w20 ------w20=0x62 w23=0x2ad //w20=0x30f"
"0x7eae056e34: add w20, w23, w20 ------w20=0x38 w23=0x30f //w20=0x347"
"0x7eae056e34: add w20, w23, w20 ------w20=0x46 w23=0x347 //w20=0x38d"
"0x7eae056e34: add w20, w23, w20 ------w20=0x61 w23=0x38d //w20=0x3ee"
"0x7eae056e34: add w20, w23, w20 ------w20=0x68 w23=0x3ee //w20=0x456"
"0x7eae056e34: add w20, w23, w20 ------w20=0x4a w23=0x456 //w20=0x4a0"
"0x7eae056e34: add w20, w23, w20 ------w20=0x70 w23=0x4a0 //w20=0x510"
"0x7eae056e34: add w20, w23, w20 ------w20=0x35 w23=0x510 //w20=0x545"
"0x7eae056e34: add w20, w23, w20 ------w20=0x6e w23=0x545 //w20=0x5b3"
"0x7eae056e34: add w20, w23, w20 ------w20=0x48 w23=0x5b3 //w20=0x5fb"
"0x7eae056e34: add w20, w23, w20 ------w20=0x72 w23=0x5fb //w20=0x66d"
"0x7eae056e34: add w20, w23, w20 ------w20=0x67 w23=0x66d //w20=0x6d4"
"0x7eae056e34: add w20, w23, w20 ------w20=0x52 w23=0x6d4 //w20=0x726"
"0x7eae056e34: add w20, w23, w20 ------w20=0x4e w23=0x726 //w20=0x774"
"0x7eae056e34: add w20, w23, w20 ------w20=0x4c w23=0x774 //w20=0x7c0"
"0x7eae056e34: add w20, w23, w20 ------w20=0x30 w23=0x7c0 //w20=0x7f0"
"0x7eae056e34: add w20, w23, w20 ------w20=0x66 w23=0x7f0 //w20=0x856"
"0x7eae056e34: add w20, w23, w20 ------w20=0x57 w23=0x856 //w20=0x8ad"
"0x7eae056e34: add w20, w23, w20 ------w20=0x55 w23=0x8ad //w20=0x902"
"0x7eae056e34: add w20, w23, w20 ------w20=0x47 w23=0x902 //w20=0x949"
"0x7eae056e34: add w20, w23, w20 ------w20=0x43 w23=0x949 //w20=0x98c"
"0x7eae056e34: add w20, w23, w20 ------w20=0x4f w23=0x98c //w20=0x9db"
#include <iostream>
# include <stdlib.h>
#include <stdio.h>
char getAscii(int num){
char tmp[3];
snprintf(tmp,3,"%x",num);
return tmp[0];
}
int main() {
char input[]="L8Cs5ntgNWiPMQRUKYXva0juFw9JqI4ApyZT";
char output[strlen(input)];
int sum=0;
int eorsum=0xff;
for(int i=0;i<strlen(input);i++){
output[i]=input[i]^0x1;
if(i==0x8||i==0xd||i==0x12||i==0x18){
output[i]=0x2d;
continue;
}else if(i==0xe){
output[i]=0x34;
continue;
}else if(i==0x23){
output[i]=input[0x22];
continue;
}else if(i==0x22){
output[i]=input[0x9];
continue;
}else if(i==0x17){
output[i]=input[0x18]^0x1;
}
if(i<0x22){
int addvalue=input[i];
if(i==0x17){
addvalue=input[0x18];
}
sum+=addvalue;
eorsum^=addvalue;
}
}
int data22=sum-(sum&0xfffffff0);
char tmp22=getAscii(data22);
int data23=eorsum&0xf;
char tmp23=getAscii(data23);
output[0x22]=tmp22;
output[0x23]=tmp23;
output[strlen(input)]=0x0;
printf("input:%s output:%s\n",input,output);
return 0;
input:L8Cs5ntgNWiPMQRUKYXva0juFw9JqI4ApyZT output:M9Br4ouf-VhQL-4TJX-w`1kG-v8KpH5@qx7c
优秀学员作品展示
优秀学员作品展示:
# 九月
# 八月
# 七月
# 六月
从三道题目入手入门frida
单纯使用Frida书写类抽取脱壳工具的一些心路历程和实践
某聊天app的音视频通话逆向
# 五月
初试IDA&FRIDA联合调试简单ollvm保护的加密函数源码
# 四月
某抽取壳的原理简析
frida辅助脱壳
# 三月
《安卓高级研修班(网课)》6月班开始招生!
PS:以上为总体服务计划,具体课程时间(段)安排以最终合同约定的课程表为准。
就业班注意事项:
强化班注意事项:
金融风险注意事项:
扫码立即报名!
3W:《Fart&frida》
扫码免费试看
2W:《Dalvik下动态注册原理追踪 》
扫码免费试看
3W班高研网课开学大礼包:
一部pixel手机(sailfish)(安卓8脱壳镜像)
A:月薪三万计划的内容与线下班的内容是一样的,我们在线下班沉淀大家的切实的需求和疑问,重新编排和制作内容作为网课与大家分享。月薪两万计划的内容由三万计划的讲师全新制作,充分体现工作场景一线的需求,更加贴近实战、实用,有用、好用。
A:目前针对ollvm和vmp,任何所谓的自动化,都是带很多前提和条件限制的;目前最快的还原ollvm或vmp的方法,还是手动分析,一般快则两三日、慢则一两周,基本上可以还原出来。
ollvm或vmp虽然非常复杂,但是并不代表没有取巧和判断的方法;依托于我们丰富的经验,我们会在课上将我们调试和分析的普适方法和一般性及特殊性技巧教给大家,同时带领大家开发属于自己的ollvm和vmp虚拟机,让学员既能够自己给自己的程序加密,又能够分析别人的经过ollvm或vmp保护的算法,这才是我们看雪高级研修班所传达的授人以渔的精神。
扫码立即报名!
球分享
球点赞
球在看
点击“阅读原文”,了解更多!