首页
社区
课程
招聘
[原创]OLLVM学习
发表于: 2025-8-5 14:49 6171

[原创]OLLVM学习

2025-8-5 14:49
6171

ollvm 搭建的环境为

测试代码:

ollvm编译:

接下来就得到了混淆后的代码,先看一下两者的对比

正常代码:

图片描述

混淆代码:
图片描述

而且基本所有的函数都是这样的,很难看,不够在我测试的时候,我发现ida9.0的伪代码会直接将这个基本的bcf去除,得到和源码一样的伪代码,可能是混淆程度太低了

伪代码对比:

图片描述

BCF混淆后的代码:

图片描述

可以发现很多的x,y操作,同时看汇编也会发现有许多的垃圾指令

具有很多的( y_3 >= 10 && (((x_2 - 1) * x_2) & 1) != 0 )

其实这个肯定是虚假的,x*(x-1)肯定是偶数,&1的结果肯定为假,就是一堆虚假指令

图片描述

看了oacia大佬关于去除这方面的方法,我觉得还是将对x,y赋值的地方直接进行nop比较好,使用idapython进行统一的nop,又快又有通用性还具有方便性

具体修改如下:
图片描述

图片描述

使用idapython统一修改

图片描述
很完美的去除

还有一种方法就是D810,但是如果是魔改的就需要自己去添加规则了

具体方法可以参考大佬的介绍

另外一个方法就是将bss段设置成只读

ollvm编译:

在流程方面倒是没有很大的差距,但是在伪代码方面非常的复杂

源代码:

图片描述

混淆代码:

图片描述

只能说非常的抽象啊

这个让我想起来了腾讯安卓23年初赛的那道vm,都是类似于这种的

1、D810:可以用d810试试

2、GAMBA简化运算

可以参考一下这个项目的识别

不过我用了一下,成功的概率不是很大,不如直接交给ai或者使用python手动赋值观察

这个控制流平坦化是我们平常最容易见到的一种混淆,学习一下这个的混淆与去混淆

控制流平坦化就是模糊基本块之间的关系,添加一些虚假的控制块来处理

借鉴一下几个大佬的图

正常逻辑是这个:

图片描述

而添加了虚假块之后:

图片描述

形成的流程图:

图片描述

各部分介绍:

序言:函数的第一个执行的基本块
主 (子) 分发器:控制程序跳转到下一个待执行的基本块
retn 块:函数出口
真实块:混淆前的基本块,程序真正执行工作的块
预处理器:跳转到主分发器

ollvm编译:

图片描述

标准的ollvm控制流平坦化

对于经典的ollvm来说,各个块之间的关系如下:

图片描述

去混淆的步骤:

那接下来的任务就很明确了,就是寻找这些真实块之间的关系,然后patch一下,将所有的虚假块全部nop

这里考虑的就是使用angr还是unidbg/unicoin

unicorn:

因为大概率会针对so,写一下arm64的(好像不是很兼容?)

angr:

安装使用(直接使用docker)

angr的模拟执行就是deflt.py项目相关,可以参考一下大佬写的讲解:

deflt.py

arm64架构的:

最开始是为了腾讯23 final题目而写的这篇文章,所以上面的学到的是为了这个做准备,研究一下这个非标准的控制流平坦化怎么解决,也不知道能实现什么地步

先来看一下流程图以及伪代码:

图片描述

图片描述

其实不去混淆的话也能看,毕竟关键的csel条件跳转已经去除了,但是为了学习还是研究一下怎么去除

经过我的观察,我觉得类似这种的像是真实块,(好像猜错了)

图片描述

结尾一B或者BL跳转的是真实块,先用颜色标注一下看看都有多少地方以及哪些地方是

找到了18处,加上返回块应该是19处

但是经过验证不是上面的做法

首先应该找到函数的循环头,通过循环头可以直接找到所有对应的真实块,这个循环头就是许多真实块最终跳转的地方,也就用广搜来搜索多次经过的地方

图片描述

也就是这个地方,序言的后面,许多函数最后经过的地方

寻找汇聚块

对于非标准OLLVM的汇聚块基本就是循环头了,而对于标准FLA而言,汇聚块就是预处理块,我们根据预处理块寻找真实块,现在不就是根据汇聚块寻找真实块吗

我差不多得到啦这些跳转的关系:

跑不完啊,难搞

错误的,没写出来

不对啊

希望有去混淆大手子教教,真不会了
如果上面内容有错误,还请各位大佬们教教

ollvm三种混淆模式的反混淆思路

OLLVM 之虚假控制流源码学习

OLLVM学习

非标准ollvm-fla分析

git clone -b llvm-4.0 --depth=1 https://github.com/obfuscator-llvm/obfuscator.git
git clone -b llvm-4.0 --depth=1 https://github.com/obfuscator-llvm/obfuscator.git
sudo apt install docker.io
sudo apt install docker.io
sudo docker pull nickdiego/ollvm-build
sudo docker pull nickdiego/ollvm-build
git clone --depth=1 https://github.com/oacia/docker-ollvm.git
git clone --depth=1 https://github.com/oacia/docker-ollvm.git
ollvm-build.sh`  后面跟的参数是 `ollvm的源码目录
sudo docker-ollvm/ollvm-build.sh /home/xw/Desktop/ollvm/obfuscator/
ollvm-build.sh`  后面跟的参数是 `ollvm的源码目录
sudo docker-ollvm/ollvm-build.sh /home/xw/Desktop/ollvm/obfuscator/
#!/usr/bin/env bash
# 一键部署 OLLVM 可执行文件到 /usr/local/ollvm/bin,并加 -ollvm 后缀
 
set -e
 
SRC_DIR="/home/xw/Desktop/ollvm/obfuscator/build_release/bin"
DEST_DIR="/usr/local/ollvm/bin"
 
sudo mkdir -p "${DEST_DIR}"
 
echo ">>> 正在批量创建软链接 ..."
for f in "${SRC_DIR}"/*; do
    base=$(basename "$f")
    sudo ln -sf "$f" "${DEST_DIR}/${base}-ollvm"
done
 
echo ">>> 已完成链接到 ${DEST_DIR}"
#!/usr/bin/env bash
# 一键部署 OLLVM 可执行文件到 /usr/local/ollvm/bin,并加 -ollvm 后缀
 
set -e
 
SRC_DIR="/home/xw/Desktop/ollvm/obfuscator/build_release/bin"
DEST_DIR="/usr/local/ollvm/bin"
 
sudo mkdir -p "${DEST_DIR}"
 
echo ">>> 正在批量创建软链接 ..."
for f in "${SRC_DIR}"/*; do
    base=$(basename "$f")
    sudo ln -sf "$f" "${DEST_DIR}/${base}-ollvm"
done
 
echo ">>> 已完成链接到 ${DEST_DIR}"
vim ~/.bashrc
export PATH="/usr/local/ollvm/bin:$PATH"
source ~/.hasbrc
vim ~/.bashrc
export PATH="/usr/local/ollvm/bin:$PATH"
source ~/.hasbrc
#include<stdio.h>
 
/*
RC4初始化函数
*/
void rc4_init(unsigned char* s, unsigned char* key, unsigned long Len_k)
{
    int i = 0, j = 0;
    char k[256] = { 0 };
    unsigned char tmp = 0;
    for (i = 0; i < 256; i++) {
        s[i] = i;
        k[i] = key[i % Len_k];
    }
    for (i = 0; i < 256; i++) {
        j = (j + s[i] + k[i]) % 256;
        tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
    }
}
 
/*
RC4加解密函数
unsigned char* Data     加解密的数据
unsigned long Len_D     加解密数据的长度
unsigned char* key      密钥
unsigned long Len_k     密钥长度
*/
void rc4_crypt(unsigned char* Data, unsigned long Len_D, unsigned char* key, unsigned long Len_k) //加解密
{
    unsigned char s[256];
    rc4_init(s, key, Len_k);
    int i = 0, j = 0, t = 0;
    unsigned long k = 0;
    unsigned char tmp;
    for (k = 0; k < Len_D; k++) {
        i = (i + 1) % 256;
        j = (j + s[i]) % 256;
        tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
        t = (s[i] + s[j]) % 256;
        Data[k] = Data[k] ^ s[t];
    }
}
int main()
{
    //字符串密钥
    unsigned char key[] = "zstuctf";
    unsigned long key_len = sizeof(key) - 1;
    //数组密钥
    //unsigned char key[] = {};
    //unsigned long key_len = sizeof(key);
 
    //加解密数据
    unsigned char data[] = { 0x7E, 0x6D, 0x55, 0xC0, 0x0C, 0xF0, 0xB4, 0xC7, 0xDC, 0x45,
        0xCE, 0x15, 0xD1, 0xB5, 0x1E, 0x11, 0x14, 0xDF, 0x6E, 0x95,
        0x77, 0x9A, 0x12, 0x99 };
    //加解密
    rc4_crypt(data, sizeof(data), key, key_len);
 
    for (int i = 0; i < sizeof(data); i++)
    {
        printf("%c", data[i]);
    }
    printf("\n");
    return 0;
}
//zstuctf{xXx_team_Is_GooD
#include<stdio.h>
 
/*
RC4初始化函数
*/
void rc4_init(unsigned char* s, unsigned char* key, unsigned long Len_k)
{
    int i = 0, j = 0;
    char k[256] = { 0 };
    unsigned char tmp = 0;
    for (i = 0; i < 256; i++) {
        s[i] = i;
        k[i] = key[i % Len_k];
    }
    for (i = 0; i < 256; i++) {
        j = (j + s[i] + k[i]) % 256;
        tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
    }
}
 
/*
RC4加解密函数
unsigned char* Data     加解密的数据
unsigned long Len_D     加解密数据的长度
unsigned char* key      密钥
unsigned long Len_k     密钥长度
*/
void rc4_crypt(unsigned char* Data, unsigned long Len_D, unsigned char* key, unsigned long Len_k) //加解密
{
    unsigned char s[256];
    rc4_init(s, key, Len_k);
    int i = 0, j = 0, t = 0;
    unsigned long k = 0;
    unsigned char tmp;
    for (k = 0; k < Len_D; k++) {
        i = (i + 1) % 256;
        j = (j + s[i]) % 256;
        tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
        t = (s[i] + s[j]) % 256;
        Data[k] = Data[k] ^ s[t];
    }
}
int main()
{
    //字符串密钥
    unsigned char key[] = "zstuctf";
    unsigned long key_len = sizeof(key) - 1;
    //数组密钥
    //unsigned char key[] = {};
    //unsigned long key_len = sizeof(key);
 
    //加解密数据
    unsigned char data[] = { 0x7E, 0x6D, 0x55, 0xC0, 0x0C, 0xF0, 0xB4, 0xC7, 0xDC, 0x45,
        0xCE, 0x15, 0xD1, 0xB5, 0x1E, 0x11, 0x14, 0xDF, 0x6E, 0x95,
        0x77, 0x9A, 0x12, 0x99 };
    //加解密
    rc4_crypt(data, sizeof(data), key, key_len);
 
    for (int i = 0; i < sizeof(data); i++)
    {
        printf("%c", data[i]);
    }
    printf("\n");
    return 0;
}
//zstuctf{xXx_team_Is_GooD
clang-ollvm -mllvm -bcf -mllvm -bcf_loop=3 -mllvm -bcf_prob=40 test.c -o test-bcf
clang-ollvm -mllvm -bcf -mllvm -bcf_loop=3 -mllvm -bcf_prob=40 test.c -o test-bcf
#去除bcf混淆
import ida_xref     #交叉引用操作
import ida_idaapi
import ida_bytes
import ida_segment
 
def do_patch(ea):
    if(ida_bytes.get_bytes(ea,1)==b"\x8B"):
        reg=(ord(ida_bytes.get_bytes(ea+1,1)) & 0b00111000)>>3
        ida_bytes.patch_bytes(ea,(0xB8+reg).to_bytes(1,"little")+b"\x00\x00\x00\x00\x90\x90"#patch_bytes(ea, buf)
    else:
        print("error")
 
seg=ida_segment.get_segm_by_name(".bss")
start=seg.start_ea
end=seg.end_ea
 
for addr in range(start,end,4):
    ref=ida_xref.get_first_dref_to(addr)
    print(hex(ref).center(20,'-'))
     
    #获取所有的交叉引用
    while (ref!=ida_idaapi.BADADDR):#BADADDR如果不是无效地址
        do_patch(ref)
        print("patch at"+hex(ref))
        '''
        ida_idaapi.ea_t get_next_dref_to    (   ida_idaapi.ea_t     to,
        ida_idaapi.ea_t     current )
        '''
        ref=ida_xref.get_next_dref_to(addr,ref)
    print("-"*20)
print("BCF 去除 finish")
#去除bcf混淆
import ida_xref     #交叉引用操作
import ida_idaapi
import ida_bytes
import ida_segment
 
def do_patch(ea):
    if(ida_bytes.get_bytes(ea,1)==b"\x8B"):
        reg=(ord(ida_bytes.get_bytes(ea+1,1)) & 0b00111000)>>3
        ida_bytes.patch_bytes(ea,(0xB8+reg).to_bytes(1,"little")+b"\x00\x00\x00\x00\x90\x90"#patch_bytes(ea, buf)
    else:
        print("error")
 
seg=ida_segment.get_segm_by_name(".bss")
start=seg.start_ea
end=seg.end_ea
 
for addr in range(start,end,4):
    ref=ida_xref.get_first_dref_to(addr)
    print(hex(ref).center(20,'-'))
     
    #获取所有的交叉引用
    while (ref!=ida_idaapi.BADADDR):#BADADDR如果不是无效地址
        do_patch(ref)
        print("patch at"+hex(ref))
        '''
        ida_idaapi.ea_t get_next_dref_to    (   ida_idaapi.ea_t     to,
        ida_idaapi.ea_t     current )
        '''
        ref=ida_xref.get_next_dref_to(addr,ref)
    print("-"*20)
print("BCF 去除 finish")
clang-ollvm -mllvm -sub -mllvm -sub_loop=3 test.c -o test-sub
clang-ollvm -mllvm -sub -mllvm -sub_loop=3 test.c -o test-sub
clang-ollvm -mllvm -fla -mllvm -split -mllvm -split_num=3 test.c -o test-fla
clang-ollvm -mllvm -fla -mllvm -split -mllvm -split_num=3 test.c -o test-fla
1、找到真实块:手动找、idapython通过各个块之间的关系找、angr模拟执行找、unidbg找
2、得到真实块之间的关系:模拟执行、trace
3、修复各个真实块之间的跳转关系,nop掉虚假的控制块
1、找到真实块:手动找、idapython通过各个块之间的关系找、angr模拟执行找、unidbg找
2、得到真实块之间的关系:模拟执行、trace
3、修复各个真实块之间的跳转关系,nop掉虚假的控制块
#idapython寻找真实块
import idaapi
import idc
 
target_func=0x401CE0#要处理的函数地址
preprocess_block=0x402057#预处理块的地址
#寻找所有预处理器的前驱
True_Block=[]
Fake_Block=[]
'''FlowChart
Constructor
@param f: A func_t type, use get_func(ea) to get a reference
@param bounds: A tuple of the form (start, end). Used if "f" is None
@param flags: one of the FC_xxxx flags.
这个函数就是返回ida流程图中的所有块中的操作权,比如查看函数起始地址,结束地址等等
'''
f_block=idaapi.FlowChart(idaapi.get_func(target_func),flags=idaapi.FC_PREDS)
for block in f_block:
    #print(block)
    #预处理器的前驱都是真实块
    if block.start_ea==preprocess_block:
        Fake_Block.append((block.start_ea,idc.prev_head(block.end_ea)))
        print("Find True Bolcks\n")
        tbs=block.preds()
        for tb in tbs:
            True_Block.append((tb.start_ea,idc.prev_head(tb.end_ea)))
        print(True_Block)
    elif not [x for x in block.succs()]:
        print("find ret")
        True_Block.append((block.start_ea,idc.prev_head(block.end_ea)))   
    elif block.start_ea!=target_func:
        Fake_Block.append((block.start_ea,idc.prev_head(block.end_ea)))
 
print('True block')
print(True_Block)
print("Fake Block")
print(Fake_Block)
#idapython寻找真实块
import idaapi
import idc
 
target_func=0x401CE0#要处理的函数地址
preprocess_block=0x402057#预处理块的地址
#寻找所有预处理器的前驱
True_Block=[]
Fake_Block=[]
'''FlowChart
Constructor
@param f: A func_t type, use get_func(ea) to get a reference
@param bounds: A tuple of the form (start, end). Used if "f" is None
@param flags: one of the FC_xxxx flags.
这个函数就是返回ida流程图中的所有块中的操作权,比如查看函数起始地址,结束地址等等
'''
f_block=idaapi.FlowChart(idaapi.get_func(target_func),flags=idaapi.FC_PREDS)
for block in f_block:
    #print(block)
    #预处理器的前驱都是真实块
    if block.start_ea==preprocess_block:
        Fake_Block.append((block.start_ea,idc.prev_head(block.end_ea)))
        print("Find True Bolcks\n")
        tbs=block.preds()
        for tb in tbs:
            True_Block.append((tb.start_ea,idc.prev_head(tb.end_ea)))
        print(True_Block)
    elif not [x for x in block.succs()]:
        print("find ret")
        True_Block.append((block.start_ea,idc.prev_head(block.end_ea)))   
    elif block.start_ea!=target_func:
        Fake_Block.append((block.start_ea,idc.prev_head(block.end_ea)))
 
print('True block')
print(True_Block)
print("Fake Block")
print(Fake_Block)
#使用unicorn来模拟执行hook
from threading import stack_size
from unicorn import *
from unicorn.x86_const import *
from keystone import * #pip install keystone-engine
from capstone import *
 
Base=0x400000
Code=Base+0x0
Code_size=0x100000
Stack=0x7F00000000
stack_size=0x100000
Fs=0x7FF0000000
Fs_size=0x100000
 
ks=Ks(KS_ARCH_X86,KS_MODE_64)
uc=Uc(UC_ARCH_X86,UC_MODE_64)
cs=Cs(CS_ARCH_X86,CS_MODE_64)
 
tbs=[(4202572, 4202582), (4202137, 4202137), (4202142, 4202229), (4202234, 4202248), (4202253, 4202260), (4202265, 4202278), (4202283, 4202300), (4202305, 4202329), (4202334, 4202356), (4202361, 4202376), (4202381, 4202403), (4202408, 4202425), (4202430, 4202463), (4202468, 4202481), (4202486, 4202502), (4202507, 4202520), (4202525, 4202532), (4202537, 4202567)]
 
tb_call=[]
main_addr=0x401CE0
main_end=0x402056
 
def hook_code(uc:unicorn.Uc,address,size,user_data):
    #反汇编这一句的指令
    for i in cs.disasm(CODE_DATA[address-Base:address-Base+size],address):
        if i.mnemonic =="call":
            print("调用call,跳过--")
            uc.reg_write(UC_X86_REG_RIP,address+size)
        elif i.mnemonic == "ret":
            print("执行结束")
            uc.emu_stop()
            print(tb_call)
        for tb in tbs:
            if address==tb[1]:
                #print(tb)
                #print (uc.reg_read (UC_X86_REG_FLAGS))#ZF 标志位在第 6 位
                ZF_flag=(uc.reg_read(UC_X86_REG_FLAGS)&0b1000000)>>6
                tb_call.append((tb,ZF_flag))
                break
                 
def hook_mem_access(uc:unicorn.Uc,type,address,size,value,userdata):
    pc=uc.reg_read(UC_X86_REG_RSP)
    print('pc:%x type:%d addr:%x size:%x' % (pc, type, address, size))
    return True
 
def inituc(uc:unicorn.Uc):
    uc.mem_map(Code,Code_size,UC_PROT_ALL)#分配代码段内存
    uc.mem_map(Stack,stack_size,UC_PROT_ALL)#分配栈内存
    uc.mem_write(Code,CODE_DATA)#分配代码数组
    uc.reg_write(UC_X86_REG_RSP,Stack+0x10000)#设置栈顶指针
    uc.hook_add(UC_HOOK_CODE,hook_code)#设置hook代码
    uc.hook_add(UC_HOOK_MEM_UNMAPPED, hook_mem_access)#当代码访问未映射内存是触发
    uc.hook_add(UC_HOOK_INTR, hook_mem_access)  # 来捕捉中断
 
     
 
with open("./test-fla", "rb") as f:
    CODE_DATA = f.read()
 
inituc(uc)
try:
    uc.emu_start(main_addr,0)
except Exception as e:
    print(e)
#使用unicorn来模拟执行hook
from threading import stack_size
from unicorn import *
from unicorn.x86_const import *
from keystone import * #pip install keystone-engine
from capstone import *
 
Base=0x400000
Code=Base+0x0
Code_size=0x100000
Stack=0x7F00000000
stack_size=0x100000
Fs=0x7FF0000000
Fs_size=0x100000
 
ks=Ks(KS_ARCH_X86,KS_MODE_64)
uc=Uc(UC_ARCH_X86,UC_MODE_64)
cs=Cs(CS_ARCH_X86,CS_MODE_64)
 
tbs=[(4202572, 4202582), (4202137, 4202137), (4202142, 4202229), (4202234, 4202248), (4202253, 4202260), (4202265, 4202278), (4202283, 4202300), (4202305, 4202329), (4202334, 4202356), (4202361, 4202376), (4202381, 4202403), (4202408, 4202425), (4202430, 4202463), (4202468, 4202481), (4202486, 4202502), (4202507, 4202520), (4202525, 4202532), (4202537, 4202567)]
 
tb_call=[]
main_addr=0x401CE0
main_end=0x402056
 
def hook_code(uc:unicorn.Uc,address,size,user_data):
    #反汇编这一句的指令
    for i in cs.disasm(CODE_DATA[address-Base:address-Base+size],address):
        if i.mnemonic =="call":
            print("调用call,跳过--")
            uc.reg_write(UC_X86_REG_RIP,address+size)
        elif i.mnemonic == "ret":
            print("执行结束")
            uc.emu_stop()
            print(tb_call)
        for tb in tbs:
            if address==tb[1]:
                #print(tb)
                #print (uc.reg_read (UC_X86_REG_FLAGS))#ZF 标志位在第 6 位
                ZF_flag=(uc.reg_read(UC_X86_REG_FLAGS)&0b1000000)>>6
                tb_call.append((tb,ZF_flag))
                break
                 
def hook_mem_access(uc:unicorn.Uc,type,address,size,value,userdata):
    pc=uc.reg_read(UC_X86_REG_RSP)
    print('pc:%x type:%d addr:%x size:%x' % (pc, type, address, size))
    return True
 
def inituc(uc:unicorn.Uc):
    uc.mem_map(Code,Code_size,UC_PROT_ALL)#分配代码段内存
    uc.mem_map(Stack,stack_size,UC_PROT_ALL)#分配栈内存
    uc.mem_write(Code,CODE_DATA)#分配代码数组
    uc.reg_write(UC_X86_REG_RSP,Stack+0x10000)#设置栈顶指针
    uc.hook_add(UC_HOOK_CODE,hook_code)#设置hook代码
    uc.hook_add(UC_HOOK_MEM_UNMAPPED, hook_mem_access)#当代码访问未映射内存是触发
    uc.hook_add(UC_HOOK_INTR, hook_mem_access)  # 来捕捉中断
 
     
 
with open("./test-fla", "rb") as f:
    CODE_DATA = f.read()
 
inituc(uc)
try:
    uc.emu_start(main_addr,0)
except Exception as e:
    print(e)
from unicorn import *
from unicorn.arm64_const import *
from capstone import *
 
# 模拟内存区域配置
BASE = 0x400000
CODE_ADDR = BASE
CODE_SIZE = 0x100000
STACK_ADDR = 0x80000000
STACK_SIZE = 0x100000
 
# 初始化 disasm 与 unicorn 引擎
cs = Cs(CS_ARCH_ARM64, CS_MODE_ARM)
uc = Uc(UC_ARCH_ARM64, UC_MODE_ARM)
 
# 你自己定义的 block 范围(例)
tbs = [(0x401000, 0x401010), (0x401020, 0x401040)]  # 请按实际地址替换
 
tb_call = []
main_addr = 0x401000  # 请设置成入口点
main_end = 0          # 设为0表示直到遇到异常或 ret
 
# code hook
def hook_code(uc, address, size, user_data):
    for i in cs.disasm(CODE_DATA[address - BASE:address - BASE + size], address):
        print("Executing: 0x%x:\t%s\t%s" % (i.address, i.mnemonic, i.op_str))
 
        if i.mnemonic == "blr" or i.mnemonic == "bl":
            print("调用 bl/blr,跳过")
            # 模拟压栈 + 跳转
            sp = uc.reg_read(UC_ARM64_REG_SP)
            ret_addr = address + size
            uc.mem_write(sp - 16, ret_addr.to_bytes(8, 'little'))
            uc.reg_write(UC_ARM64_REG_SP, sp - 16)
            uc.reg_write(UC_ARM64_REG_PC, ret_addr)
        elif i.mnemonic == "ret":
            print("执行 ret,停止")
            uc.emu_stop()
            print("tb_call:", tb_call)
 
        for tb in tbs:
            if address == tb[1]:
                nzcv = uc.reg_read(UC_ARM64_REG_NZCV)
                ZF = (nzcv >> 30) & 1  # NZCV: bit 30 is Z
                tb_call.append((tb, ZF))
                break
 
# memory hook
def hook_mem_invalid(uc, access, address, size, value, user_data):
    print(f"[!] Memory Access Violation at 0x{address:x}")
    return False  # 返回 False 让 Unicorn 抛出异常停止
 
# 初始化内存与 hook
def init_uc():
    uc.mem_map(CODE_ADDR, CODE_SIZE)
    uc.mem_map(STACK_ADDR, STACK_SIZE)
    uc.mem_write(CODE_ADDR, CODE_DATA)
    uc.reg_write(UC_ARM64_REG_SP, STACK_ADDR + STACK_SIZE - 0x10)
    uc.hook_add(UC_HOOK_CODE, hook_code)
    uc.hook_add(UC_HOOK_MEM_UNMAPPED, hook_mem_invalid)
 
# 加载代码
with open("test-fla-arm64", "rb") as f:
    CODE_DATA = f.read()
 
# 运行
init_uc()
try:
    uc.emu_start(main_addr, main_end)
except Exception as e:
    print(f"[!] Emulation error: {e}")
from unicorn import *
from unicorn.arm64_const import *
from capstone import *
 
# 模拟内存区域配置
BASE = 0x400000
CODE_ADDR = BASE
CODE_SIZE = 0x100000
STACK_ADDR = 0x80000000
STACK_SIZE = 0x100000
 
# 初始化 disasm 与 unicorn 引擎
cs = Cs(CS_ARCH_ARM64, CS_MODE_ARM)
uc = Uc(UC_ARCH_ARM64, UC_MODE_ARM)
 
# 你自己定义的 block 范围(例)
tbs = [(0x401000, 0x401010), (0x401020, 0x401040)]  # 请按实际地址替换
 
tb_call = []
main_addr = 0x401000  # 请设置成入口点
main_end = 0          # 设为0表示直到遇到异常或 ret
 
# code hook
def hook_code(uc, address, size, user_data):
    for i in cs.disasm(CODE_DATA[address - BASE:address - BASE + size], address):
        print("Executing: 0x%x:\t%s\t%s" % (i.address, i.mnemonic, i.op_str))
 
        if i.mnemonic == "blr" or i.mnemonic == "bl":
            print("调用 bl/blr,跳过")
            # 模拟压栈 + 跳转
            sp = uc.reg_read(UC_ARM64_REG_SP)
            ret_addr = address + size
            uc.mem_write(sp - 16, ret_addr.to_bytes(8, 'little'))
            uc.reg_write(UC_ARM64_REG_SP, sp - 16)
            uc.reg_write(UC_ARM64_REG_PC, ret_addr)
        elif i.mnemonic == "ret":
            print("执行 ret,停止")
            uc.emu_stop()
            print("tb_call:", tb_call)
 
        for tb in tbs:
            if address == tb[1]:
                nzcv = uc.reg_read(UC_ARM64_REG_NZCV)
                ZF = (nzcv >> 30) & 1  # NZCV: bit 30 is Z
                tb_call.append((tb, ZF))
                break
 
# memory hook
def hook_mem_invalid(uc, access, address, size, value, user_data):
    print(f"[!] Memory Access Violation at 0x{address:x}")
    return False  # 返回 False 让 Unicorn 抛出异常停止
 
# 初始化内存与 hook
def init_uc():
    uc.mem_map(CODE_ADDR, CODE_SIZE)
    uc.mem_map(STACK_ADDR, STACK_SIZE)
    uc.mem_write(CODE_ADDR, CODE_DATA)
    uc.reg_write(UC_ARM64_REG_SP, STACK_ADDR + STACK_SIZE - 0x10)
    uc.hook_add(UC_HOOK_CODE, hook_code)
    uc.hook_add(UC_HOOK_MEM_UNMAPPED, hook_mem_invalid)
 
# 加载代码
with open("test-fla-arm64", "rb") as f:
    CODE_DATA = f.read()
 
# 运行
init_uc()
try:
    uc.emu_start(main_addr, main_end)
except Exception as e:
    print(f"[!] Emulation error: {e}")
docker pull angr/angr
docker run -it angr/angr
docker pull angr/angr
docker run -it angr/angr
#patch脚本
#先明确目的,nop掉所有的虚假块,按照刚才模拟执行的顺序patch成有顺序的块
 
import idaapi
import ida_bytes
import idc
from keystone import *
ks=Ks(KS_ARCH_X86,KS_MODE_64)
 
#因为正常的ollvm真实块最后都是jmp 预处理器,所以判断jump修改就行
#同时有的逻辑会存在if语句,所以我们要根据特殊情况来处理jnz和jz的情况
def jump_patch(start,target,j_code='jmp'):
    global debug
    patch_byte,count=ks.asm(f"{j_code} {hex(target)}",addr=start)
    #获取当前地址指令长度,补齐字节
    patch_byte=bytes(patch_byte)+b"\x00"*(idc.get_item_size(start)+len(patch_byte))
    print(hex(start),f"{j_code} {hex(target)}",patch_byte)
    ida_bytes.patch_bytes(start,patch_byte)
 
#将无关代码全部nop
def patch_nop(start,end):
    while start<end:
        ida_bytes.patch_bytes(start,bytes([0x90]))
        start+=1
#nop掉某一行不再是大范围nop了
def patch_nop_line(addr):
    patch_nop(addr,addr+idc+idc.get_item_size(addr))
 
preamble_block = 0x402057  # 序言块的地址
internal_reg = '[rbp+var_B4]'#中间变量的名称,遇到这个想都不用想直接 NOP
 
tb_path= [((4202142, 4202229), 1), ((4202234, 4202248), 1), ((4202253, 4202260), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 1), ((4202334, 4202356), 1), ((4202537, 4202567), 1)]
 
tbs=[(4202572, 4202582), (4202137, 4202137), (4202142, 4202229), (4202234, 4202248), (4202253, 4202260), (4202265, 4202278), (4202283, 4202300), (4202305, 4202329), (4202334, 4202356), (4202361, 4202376), (4202381, 4202403), (4202408, 4202425), (4202430, 4202463), (4202468, 4202481), (4202486, 4202502), (4202507, 4202520), (4202525, 4202532), (4202537, 4202567)]
fbs=[(4201721, 4201738), (4201744, 4201744), (4201749, 4201760), (4201766, 4201766), (4201771, 4201782), (4201788, 4201788), (4201793, 4201804), (4201810, 4201810), (4201815, 4201826), (4201832, 4201832), (4201837, 4201851), (4201857, 4201857), (4201862, 4201876), (4201882, 4201882), (4201887, 4201901), (4201907, 4201907), (4201912, 4201926), (4201932, 4201932), (4201937, 4201951), (4201957, 4201957), (4201962, 4201976), (4201982, 4201982), (4201987, 4202001), (4202007, 4202007), (4202012, 4202026), (4202032, 4202032), (4202037, 4202051), (4202057, 4202057), (4202062, 4202076), (4202082, 4202082), (4202087, 4202101), (4202107, 4202107), (4202112, 4202126), (4202132, 4202132), (4202137, 4202137), (4202142, 4202229), (4202234, 4202248), (4202253, 4202260), (4202265, 4202278), (4202283, 4202300), (4202305, 4202329), (4202334, 4202356), (4202361, 4202376), (4202381, 4202403), (4202408, 4202425), (4202430, 4202463), (4202468, 4202481), (4202486, 4202502), (4202507, 4202520), (4202525, 4202532), (4202537, 4202567), (4202583, 4202583)]
 
block_info = {}  #判断每一个真实块有没有 patch 结束
for i in range(len(tbs)):
    block_info[tbs[i][0]] = {'finish': 0,'ret':0}
 
#nop掉所有的无关块
for fb in fbs:
    patch_nop(fb[1],fb[1]+idc.get_item_size(fb[1]))
 
#分析真实块的指令
for tb in tbs:
    dont_patch=False
    current_addr=tb[0]
    while current_addr<=tb[1]:
        if "cmov" in idc.print_insn_mnem(current_addr):
            patch_nop_line(current_addr)
            dont_patch=True
        elif internal_reg in idc.print_operand(current_addr,0):
            print("非法指令")
            patch_nop_line(current_addr)
        elif 'ret' in idc.print_insn_mnem(current_addr):
            block_info[tb[0]]['ret']=1
            dont_patch=True
    #对每一个真实块的结尾地址进行nop
    if not dont_patch:
        patch_nop_line(tb[1])
        block_info[tb[0]]['finish']=1
 
#序言块到第一个真实块
jump_patch(preamble_block,tb_path[0][0][0])
for i in range(len(tb_path)-1):
    # 不是返回块,也未完成 patch, 剩下的指令都是有分支跳转的.
    if block_info[tb_path[i][0][0]]['finish']==0 and block_info[tb_path[i][0][0]]['ret']==0:
        ZF=tb_path[i][1]
        #如果当前真实块的尾地址不是下一个真实块的始地址就进行patch jnzjz,否则就是正常连接
        if(idc.next_head(tb_path[i][0][1]) != tb_path[i+1][0][0]):
            #打上标记,避免重复
            block_info[tb_path[i][0][0]]['finish']=1
            j_code=('jnz','jz')
            jump_patch(tb_path[i][0][1],tb_path[i+1][0][0],j_code[ZF])
#patch脚本
#先明确目的,nop掉所有的虚假块,按照刚才模拟执行的顺序patch成有顺序的块
 
import idaapi
import ida_bytes
import idc
from keystone import *
ks=Ks(KS_ARCH_X86,KS_MODE_64)
 
#因为正常的ollvm真实块最后都是jmp 预处理器,所以判断jump修改就行
#同时有的逻辑会存在if语句,所以我们要根据特殊情况来处理jnz和jz的情况
def jump_patch(start,target,j_code='jmp'):
    global debug
    patch_byte,count=ks.asm(f"{j_code} {hex(target)}",addr=start)
    #获取当前地址指令长度,补齐字节
    patch_byte=bytes(patch_byte)+b"\x00"*(idc.get_item_size(start)+len(patch_byte))
    print(hex(start),f"{j_code} {hex(target)}",patch_byte)
    ida_bytes.patch_bytes(start,patch_byte)
 
#将无关代码全部nop
def patch_nop(start,end):
    while start<end:
        ida_bytes.patch_bytes(start,bytes([0x90]))
        start+=1
#nop掉某一行不再是大范围nop了
def patch_nop_line(addr):
    patch_nop(addr,addr+idc+idc.get_item_size(addr))
 
preamble_block = 0x402057  # 序言块的地址
internal_reg = '[rbp+var_B4]'#中间变量的名称,遇到这个想都不用想直接 NOP
 
tb_path= [((4202142, 4202229), 1), ((4202234, 4202248), 1), ((4202253, 4202260), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 0), ((4202334, 4202356), 0), ((4202361, 4202376), 1), ((4202381, 4202403), 0), ((4202408, 4202425), 1), ((4202430, 4202463), 1), ((4202468, 4202481), 1), ((4202486, 4202502), 0), ((4202507, 4202520), 1), ((4202525, 4202532), 1), ((4202265, 4202278), 1), ((4202283, 4202300), 1), ((4202305, 4202329), 1), ((4202334, 4202356), 1), ((4202537, 4202567), 1)]
 
tbs=[(4202572, 4202582), (4202137, 4202137), (4202142, 4202229), (4202234, 4202248), (4202253, 4202260), (4202265, 4202278), (4202283, 4202300), (4202305, 4202329), (4202334, 4202356), (4202361, 4202376), (4202381, 4202403), (4202408, 4202425), (4202430, 4202463), (4202468, 4202481), (4202486, 4202502), (4202507, 4202520), (4202525, 4202532), (4202537, 4202567)]
fbs=[(4201721, 4201738), (4201744, 4201744), (4201749, 4201760), (4201766, 4201766), (4201771, 4201782), (4201788, 4201788), (4201793, 4201804), (4201810, 4201810), (4201815, 4201826), (4201832, 4201832), (4201837, 4201851), (4201857, 4201857), (4201862, 4201876), (4201882, 4201882), (4201887, 4201901), (4201907, 4201907), (4201912, 4201926), (4201932, 4201932), (4201937, 4201951), (4201957, 4201957), (4201962, 4201976), (4201982, 4201982), (4201987, 4202001), (4202007, 4202007), (4202012, 4202026), (4202032, 4202032), (4202037, 4202051), (4202057, 4202057), (4202062, 4202076), (4202082, 4202082), (4202087, 4202101), (4202107, 4202107), (4202112, 4202126), (4202132, 4202132), (4202137, 4202137), (4202142, 4202229), (4202234, 4202248), (4202253, 4202260), (4202265, 4202278), (4202283, 4202300), (4202305, 4202329), (4202334, 4202356), (4202361, 4202376), (4202381, 4202403), (4202408, 4202425), (4202430, 4202463), (4202468, 4202481), (4202486, 4202502), (4202507, 4202520), (4202525, 4202532), (4202537, 4202567), (4202583, 4202583)]
 
block_info = {}  #判断每一个真实块有没有 patch 结束
for i in range(len(tbs)):
    block_info[tbs[i][0]] = {'finish': 0,'ret':0}
 
#nop掉所有的无关块
for fb in fbs:
    patch_nop(fb[1],fb[1]+idc.get_item_size(fb[1]))
 
#分析真实块的指令
for tb in tbs:
    dont_patch=False
    current_addr=tb[0]
    while current_addr<=tb[1]:
        if "cmov" in idc.print_insn_mnem(current_addr):
            patch_nop_line(current_addr)
            dont_patch=True
        elif internal_reg in idc.print_operand(current_addr,0):
            print("非法指令")
            patch_nop_line(current_addr)
        elif 'ret' in idc.print_insn_mnem(current_addr):
            block_info[tb[0]]['ret']=1
            dont_patch=True
    #对每一个真实块的结尾地址进行nop
    if not dont_patch:
        patch_nop_line(tb[1])
        block_info[tb[0]]['finish']=1
 
#序言块到第一个真实块
jump_patch(preamble_block,tb_path[0][0][0])
for i in range(len(tb_path)-1):
    # 不是返回块,也未完成 patch, 剩下的指令都是有分支跳转的.
    if block_info[tb_path[i][0][0]]['finish']==0 and block_info[tb_path[i][0][0]]['ret']==0:
        ZF=tb_path[i][1]
        #如果当前真实块的尾地址不是下一个真实块的始地址就进行patch jnzjz,否则就是正常连接
        if(idc.next_head(tb_path[i][0][1]) != tb_path[i+1][0][0]):
            #打上标记,避免重复
            block_info[tb_path[i][0][0]]['finish']=1
            j_code=('jnz','jz')
            jump_patch(tb_path[i][0][1],tb_path[i+1][0][0],j_code[ZF])
import idc
import idaapi
import ida_bytes
from keystone import Ks, KS_ARCH_ARM64, KS_MODE_LITTLE_ENDIAN
 
ks = Ks(KS_ARCH_ARM64, KS_MODE_LITTLE_ENDIAN)
 
# ==== 配置部分 ====
# preamble block 跳转到主路径
preamble_block = 0x400800
 
# 被用于混淆的 filler blocks,全 NOP 掉
fbs = [
    (0x400820, 0x40083C),
    (0x400840, 0x40085C),
    # ...
]
 
# 有效代码块(基本块范围),用于 patch 分支
tbs = [
    (0x400860, 0x40087C),
    (0x400880, 0x40089C),
    # ...
]
 
# 动态执行模拟得到的路径(块 + 是否跳转)
tb_path = [
    ((0x400860, 0x40087C), 1),  # ZF==1 跳转
    ((0x400880, 0x40089C), 0),
    # ...
]
 
# ===== PATCH工具函数 =====
def patch_nop(start, end):
    while start < end:
        ida_bytes.patch_bytes(start, b"\x1F\x20\x03\xD5"# NOP
        start += 4  # 每条 ARM64 指令 4 字节
 
def patch_nop_line(addr):
    patch_nop(addr, addr + 4)
 
def assemble_and_patch(addr, asm_str):
    encoding, count = ks.asm(asm_str, addr)
    if not encoding:
        print(f"[!] Keystone failed on: {asm_str}")
        return
    patch_bytes = bytes(encoding)
    ida_bytes.patch_bytes(addr, patch_bytes)
    print(f"[+] PATCH {hex(addr)}: {asm_str} -> {patch_bytes.hex()}")
 
# patch B label 或 B.EQ label 等跳转指令
def patch_branch(start_addr, target_addr, condition=None):
    if condition is None:
        asm = f"B #{target_addr}"
    else:
        asm = f"B.{condition.upper()} #{target_addr}"
    assemble_and_patch(start_addr, asm)
 
# ========== 逻辑 ==========
# NOP 掉所有 filler blocks
for fb in fbs:
    patch_nop(fb[0], fb[1])
 
# 每个真实块处理条件跳转与非法变量引用
block_info = {}
for tb in tbs:
    start, end = tb
    block_info[start] = {'ret': 0, 'finish': 0}
    addr = start
    while addr < end:
        mnem = idc.print_insn_mnem(addr)
        if "CSEL" in mnem or "CMOV" in mnem:
            patch_nop_line(addr)
        elif "RET" in mnem:
            block_info[start]['ret'] = 1
        addr = idc.next_head(addr)
 
# 序言块跳转第一个基本块
first_tb_start = tb_path[0][0][0]
patch_branch(preamble_block, first_tb_start)
 
# 主路径 patch:连接每个路径的结尾到下一个块
for i in range(len(tb_path) - 1):
    curr_tb, ZF = tb_path[i]
    next_tb, _ = tb_path[i + 1]
 
    block_start = curr_tb[0]
    block_end = curr_tb[1]
 
    if block_info[block_start]['ret'] or block_info[block_start]['finish']:
        continue
 
    jump_addr = block_end
    next_addr = next_tb[0]
 
    if ZF == 1:
        patch_branch(jump_addr, next_addr, condition="eq")
    else:
        patch_branch(jump_addr, next_addr, condition="ne")
 
    block_info[block_start]['finish'] = 1
import idc
import idaapi
import ida_bytes
from keystone import Ks, KS_ARCH_ARM64, KS_MODE_LITTLE_ENDIAN
 
ks = Ks(KS_ARCH_ARM64, KS_MODE_LITTLE_ENDIAN)
 
# ==== 配置部分 ====
# preamble block 跳转到主路径
preamble_block = 0x400800
 
# 被用于混淆的 filler blocks,全 NOP 掉
fbs = [
    (0x400820, 0x40083C),
    (0x400840, 0x40085C),
    # ...
]
 
# 有效代码块(基本块范围),用于 patch 分支
tbs = [
    (0x400860, 0x40087C),
    (0x400880, 0x40089C),
    # ...
]
 
# 动态执行模拟得到的路径(块 + 是否跳转)
tb_path = [
    ((0x400860, 0x40087C), 1),  # ZF==1 跳转
    ((0x400880, 0x40089C), 0),
    # ...
]
 
# ===== PATCH工具函数 =====
def patch_nop(start, end):
    while start < end:
        ida_bytes.patch_bytes(start, b"\x1F\x20\x03\xD5"# NOP
        start += 4  # 每条 ARM64 指令 4 字节
 
def patch_nop_line(addr):
    patch_nop(addr, addr + 4)
 
def assemble_and_patch(addr, asm_str):
    encoding, count = ks.asm(asm_str, addr)
    if not encoding:
        print(f"[!] Keystone failed on: {asm_str}")
        return
    patch_bytes = bytes(encoding)
    ida_bytes.patch_bytes(addr, patch_bytes)
    print(f"[+] PATCH {hex(addr)}: {asm_str} -> {patch_bytes.hex()}")

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 17
支持
分享
最新回复 (12)
雪    币: 104
活跃值: (7295)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
反手一个收藏!我就当学会了!
2025-8-5 16:31
0
雪    币: 3727
活跃值: (5872)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
感谢分享,很详细的文章
2025-8-5 20:58
0
雪    币: 1598
活跃值: (2611)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
很详细的分享 感谢
2025-8-6 09:06
0
雪    币: 2832
活跃值: (12122)
能力值: (RANK:385 )
在线值:
发帖
回帖
粉丝
5
太详细了.值得学习.
2025-8-7 10:08
0
雪    币: 677
活跃值: (1501)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
BCF可以看看hluwa大佬提到的DCE方式去除:https[:]//wiki.hluwa.cn/ - "Hex-Rays 十步杀一人,两步秒OLLVM-BCF"
SUB指令替换推荐IDA 8后集成的goomba项目https[:]//github.com/HexRaysSA/goomba
(以上只针对标准的ollvm混淆)
2025-8-7 12:06
1
雪    币: 42
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
可以私聊吗
2025-8-9 20:22
0
雪    币: 1361
活跃值: (865)
能力值: ( LV3,RANK:25 )
在线值:
发帖
回帖
粉丝
8
mb_yqsvfqmf 可以私聊吗
怎么了师傅
2025-8-9 21:31
0
雪    币: 1361
活跃值: (865)
能力值: ( LV3,RANK:25 )
在线值:
发帖
回帖
粉丝
9
Vchase BCF可以看看hluwa大佬提到的DCE方式去除:https[:]//wiki.hluwa.cn/ - "Hex-Rays 十步杀一人,两步秒OLLVM-BCF" SUB指令替换 ...

`死代码消除(DCE, Dead Code Elimination)`的,所以只要能实现运行一遍 DCE 即可消除掉 BCF 混淆;感觉大佬说的意思应该是模拟执行吧,给执行到的地址加入到列表中,然后未执行到的直接全部patch,应该是这个道理吧

最后于 2025-8-9 21:37 被xiaowaaa编辑 ,原因:
2025-8-9 21:37
0
雪    币: 42
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
10
xiaowaaa 怎么了师傅
有点私事
2025-8-11 03:22
0
雪    币: 168
活跃值: (245)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11

2025-9-9 17:15
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12
6666
2025-10-7 20:44
0
雪    币: 2
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13

分享很实用!BCF用IDApython批量NOP确实高效,尤其适合处理魔改版本;FLA恢复推荐angr结合Unicorn动态追踪,新手可先用静态分析熟悉预处理块。我把关键点整理成了思维导图 ,方便大家快速掌握处理技巧‌!

2025-11-11 10:26
0
游客
登录 | 注册 方可回帖
返回