2023年腾讯游戏安全竞赛安卓决赛题解复现

flag获取

和初赛一样,还是hook同样的代码,

let so_name = "libil2cpp.so"
let bases = Module.findBaseAddress(so_name);
let module = Process.getModuleByName(so_name);
let base_addr = module.base
let base_size = module.size

console.log("[*] base", bases);
function hook_coin() {

    var address = bases.add(0x466A48);
    // console.log(Memory.readInt(address))
    Interceptor.attach(address, {
        onEnter: function (args) {
        },
        onLeave: function (retval) {
            retval.replace(1000)
            console.log('ret', retval)

        }
    })
}

libil2cpp.so分析:

通过hook一下小键盘的函数,我们可以发现他的算法不在libil2cpp这个so里面,而是在libsec2023.so里面,下面是对libil2cpp.so的分析,就是一些

let so_name = "libil2cpp.so"
let bases = Module.findBaseAddress(so_name);
let module = Process.getModuleByName(so_name);
let base_addr = module.base
let base_size = module.size
//hook小键盘测试,不能直接读取String得值,因为没有String这个类型,他只是一个类型罢了
/*
struct Il2CppString {
    void* klass;        // 指向类元数据的指针
    void* monitor;      // 用于线程同步
    int32_t length;     // 字符串长度(字符数)
    uint16_t chars[0];  // 字符数组(UTF-16,每个字符占2字节)
};
*/
//写一个读取String得函数
function getStringValue(addr) {
    var length = ptr(addr).add(0x10).readU32()
    var str_real_addr = ptr(addr).add(0x14)
    var string_val = str_real_addr.readUtf16String(length)
    return string_val
}
//其中下面我们要hook的一个函数的参数是结构体,需要解析一下
/*
public class SmallKeyboard.iII1i // TypeDefIndex: 3311{
    // Fields
    public GameObject KeyObj; // 0x10
    public SmallKeyboard.KeyboardType KeyType; // 0x18
    public string SValue; // 0x20
}
*/
function parseiII1i(addr) {
    var types = ptr(addr).add(0x18).readPointer()
    var iII1i_addr = ptr(addr).add(0x20).readPointer()
    var iII1i_value = getStringValue(iII1i_addr)
    return iII1i_value
}

function hook_smallKeyboard() {
    var hooklist = [0x465760, 0x465C8C, 0x465990, 0x465B40, 0x465D98]
    //分析后得到函数一是对输入中的数据进行处理,每次点击数字或者del就会调用
    //函数二是组件判断,应该就是组件初始化
    //函数三是我们点击ok,就输出我们输入的值
    //函数四是生成一个7位的随机数,生成的随机数就是token
    //函数五应该就是一个赋值操作
    for (var i = 0; i < hooklist.length; i++) {
        var addr = hooklist[i];
        const idx = i;
        Interceptor.attach(bases.add(addr), {
            onEnter: function (args) {
                if (idx === 0) {
                    console.log("==========第一个函数开始调用==============")
                    var SmallKeyboardthis = args[0]
                    var strInput = SmallKeyboardthis.add(0x18).readPointer()
                    var oO0o0o0 = SmallKeyboardthis.add(0x38).readPointer()
                    var iIIIi = SmallKeyboardthis.add(0x40).readPointer()
                    var str_strInpt = getStringValue(strInput)
                    var str_oO0o0o0 = getStringValue(oO0o0o0)
                    var str_iIIIi = getStringValue(iIIIi)
                    console.log("str_strInpt", str_strInpt)//没有回显
                    console.log("str_oO0o0o0", str_oO0o0o0)//已经生成的token
                    console.log("str_iIIIi", str_iIIIi)//按下的字符
                }
                else if (idx == 2) {
                    console.log("==========第三个函数开始调用==============")
                    console.log(args[1])
                    //这个值就是我们输出的最后一个值
                }
            },
            onLeave: function (retval) {
                if (idx == 2) {
                    console.log('RegisterNatives called from:\\n' + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\\n') + '\\n');

                    console.log("==", retval)
                }
            }
        })

    }
}

然后我们去看这些小键盘的函数,尤其是第三个,0x465990,毕竟他是涉及ok的,然后就发现他的跳转了

image-20250729160422906

然后就是去看这个跳转到哪里了

代码如下:

function hook_addr_in_so(addr) {
    var process_Obj_Module_Arr = Process.enumerateModules();
    for (var i = 0; i < process_Obj_Module_Arr.length; i++) {
        if (addr > process_Obj_Module_Arr[i].base && addr < process_Obj_Module_Arr[i].base.add(process_Obj_Module_Arr[i].size)) {
            console.log(addr.toString(16), "is in", process_Obj_Module_Arr[i].name, "offset: 0x" + (addr - process_Obj_Module_Arr[i].base).toString(16));
        }
    }
}

function enc_jump() {
    const baseAddr = Module.findBaseAddress('libil2cpp.so')
    var offest = 0x13BCDD4
    const func_inp = bases.add(offest)

    Interceptor.attach(func_inp, {
        onEnter: function (args) {
            hook_addr_in_so(ptr(this.context.x2))
        },
        onLeave: function (retval) {
            //console.log('RegisterNatives called from:\\n' + Thread.backtrace(this.context, Backtracer.ACCURATE).map(hook_addr_in_so).join('\\n') + '\\n');
            // console.log(retval)
            // console.log('====', this.context.x2)
            // var buf = Memory.readByteArray(ptr(retval), 0x10);
            // console.log(hexdump(buf, {
            //     offset: 0,
            //     length: 0x10,
            //     header: true,
            //     ansi: true
            // }));
        },
    })
}

image-20250729160556382

到此,我们对libil2cpp.so的分析结束

libsec2023.so分析:

image-20250729160739530

我们发现跳转到这里,但是当我们hook这个so的函数的时候,他就输出被检测出来了

反调试分析:

说明有对frida的检测,我们经过测试是crc检测,因为crc需要不断的扫内存,我们直接使用stackplz随便对一个地址下个硬件断点就行

2|oriole:/data/local/tmp # ./stackplz --pid 22724 --brk 0x744A280CB8:r --stack
[*] save maps to maps_22724.txt
set breakpoint at kernel:false, addr:0x744a280cb8, type:1
start 1 modules
[22724|22787] event_addr:0x744a280cb8 hit_count:1, Backtrace:
  #00 pc 000000000007d934  /data/app/~~aQKQSDle_3eICEO3gsHPRw==/com.com.sec2023.rocketmouse.mouse-8sttfhilCgPLUR963AOOgQ==/lib/arm64/libsec2023.so
  #01 pc 000000000007b2fc  /data/app/~~aQKQSDle_3eICEO3gsHPRw==/com.com.sec2023.rocketmouse.mouse-8sttfhilCgPLUR963AOOgQ==/lib/arm64/libsec2023.so
  #02 pc 0000000000075664  /data/app/~~aQKQSDle_3eICEO3gsHPRw==/com.com.sec2023.rocketmouse.mouse-8sttfhilCgPLUR963AOOgQ==/lib/arm64/libsec2023.so
  #03 pc 0000000000074f5c  /data/app/~~aQKQSDle_3eICEO3gsHPRw==/com.com.sec2023.rocketmouse.mouse-8sttfhilCgPLUR963AOOgQ==/lib/arm64/libsec2023.so
  #04 pc 0000000000051c50  /data/app/~~aQKQSDle_3eICEO3gsHPRw==/com.com.sec2023.rocketmouse.mouse-8sttfhilCgPLUR963AOOgQ==/lib/arm64/libsec2023.so
  #05 pc 00000000000c226c  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+204)
  #06 pc 0000000000054a30  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)

[22724|22787] event_addr:0x744a280cb8 hit_count:2, Backtrace:
  #00 pc 000000000007d934  /data/app/~~aQKQSDle_3eICEO3gsHPRw==/com.com.sec2023.rocketmouse.mouse-8sttfhilCgPLUR963AOOgQ==/lib/arm64/libsec2023.so
  #01 pc 000000000007b2fc  /data/app/~~aQKQSDle_3eICEO3gsHPRw==/com.com.sec2023.rocketmouse.mouse-8sttfhilCgPLUR963AOOgQ==/lib/arm64/libsec2023.so
  #02 pc 0000000000075664  /data/app/~~aQKQSDle_3eICEO3gsHPRw==/com.com.sec2023.rocketmouse.mouse-8sttfhilCgPLUR963AOOgQ==/lib/arm64/libsec2023.so
  #03 pc 0000000000074f5c  /data/app/~~aQKQSDle_3eICEO3gsHPRw==/com.com.sec2023.rocketmouse.mouse-8sttfhilCgPLUR963AOOgQ==/lib/arm64/libsec2023.so
  #04 pc 0000000000051c50  /data/app/~~aQKQSDle_3eICEO3gsHPRw==/com.com.sec2023.rocketmouse.mouse-8sttfhilCgPLUR963AOOgQ==/lib/arm64/libsec2023.so
  #05 pc 00000000000c226c  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+204)
  #06 pc 0000000000054a30  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)

也就是这几个地址:7d934 7b2fc 75664 74f5c 51c50

看汇编也估计就是从7b2fc这个地址开始变化,往下面跟就行了,就是在下面的cmp比较那,你可以多次hook这个地方看看哪些值不变就修改哪里

image-20250729172204297

我trace了2次,两次不一样的值,直接强制改为相同的值就行,可以改汇编可以改寄存器,修正完就解决了

下面是anti脚本,借鉴我师哥的想法直接hook sleep函数,挺完美的

var soname = 'libsec2023.so';
var startaddr = 0x7B300;
var endaddr = 0x7B300 + 0x1000
var size = endaddr - startaddr;
let so_name = "libsec2023.so"
let bases = Module.findBaseAddress(so_name);
let module = Process.getModuleByName(so_name);
let base_addr = module.base
let base_size = module.size
function anti_check1() {
    var addr = base_addr.add(0x7b320)
    Memory.protect(addr, 4, 'rwx');
    addr.writeInt(0x2a0803e0)
}

function anti_check2() {
    var addr = base_addr.add(0x7b320)
    Interceptor.attach(addr, {
        onEnter: function (args) {
            this.context.x0 = 0x696a4761
        }
    })
}
var cnt = 0
function anti_check3() {
    Interceptor.attach(Module.findExportByName(null, "sleep"), {
        onEnter: function (args) {
            cnt += 1
            if (cnt == 2) {
                console.log("hook sleep finish")
            }
            console.log(`sleep ${args[0]}s`)
            var thread = Process.getCurrentThreadId()
            console.log(`the threadid in sleep is: ${thread}`)
            args[0] = ptr(0x100000)
        }
    })
}
anti_check3()

继续回到正文,我们过掉反调试以后,就去看他哪里进行了加密

image-20250729175735843

通过hook,我们发现就个地方进行了加密

混淆处理:

BR跳转混淆

看汇编知道有许多的br跳转,这种br跳转去除很简单那们就是将br x8修改为 BL xxaddr

image-20250729175905504

有的是已经ida给我们静态计算好的,也就不需要我们再进行trace去找了,对于ida无法静态计算的,我们可以通过stalker去trace 然后手动patch来恢复符号

image-20250729180158517

类似这种

image-20250729180248725

手动patch就行

image-20250729180515504

这是简单patch后的,部分符号我已经修改了

不透明谓词混淆

image-20250729180754784

类似于这种,但是我们可以点进去发现他是有值的,就必须手动将其类型修改,然后ida才能识别并计算出来

image-20250729180937692

ida统一修改如下:

idapthon参看https://python.docs.hex-rays.com/namespaceida__idaapi.html

# 1、修改变量的参数类型
from idc import *
def change_type(addr,new_type):
    SetType(addr,new_type)

start_addr=0x1270d0
end_addr=0x13d9a0
for addr in range(start_addr,end_addr,8):
    new_type="const unsigned __int64"
    change_type(addr,new_type)
print("finish")

image-20250729181514196

CSEL+BR混淆

但是看一下内部函数

image-20250729181918021

image-20250729181926263

还是看不了具体的东西,我最开始是直接在内部trace,然后手动在一些br跳转处进行处理的

trace代码: https://github.com/XiaoWaaay/frida_trace

效果只能说能看,但是免不了有一些函数还是不行

2025-07-29_182355

不过第一次而言,对于我分析第一次加密足够了,就是大量的hook+trace分析,下面再说具体的算法,然后就参考了师哥的去混淆代码,正好学习一下unidbg的去混淆代码该如何书写

首先贴一下要去除的格式:

CMP             W8, W27
CSEL            X9, X22, X21, LT
LDR             X9, [X19,X9]
ADD             X9, X9, X26
BR              X9

拿上面这个举例,这个就是记录下来 x22 x21的值,然后去动态计算x9的值,肯定会得到两种x9的值,然后将后四句改为

cmp xx xx
B.XX true_addr
B false_addr
NOP
NOP

这里的关键就是如何去计算这些寄存器的值,关键就是两个问题?

1.trace还是hook,hook能不能得到两种情况的值?

2.能不能通过trace,在trace的情况下,通过往上翻代码(回溯寄存器),能不能计算出来这个寄存器值

所以我们可以通过hook的情况来解决这个问题

下面贴一下代码

首先先用ida拿到不能正常看的地址,也就是混淆函数的筛选

import idc
import idautils
import idaapi
func_start=[]
def ida_decode(begin,end):
    global func_start
    All_func=list(idautils.Functions(begin,end))#获取所有函数的首地址
    for i in range(len(All_func)):
        func=idaapi.get_func(All_func[i])#拿到该函数的操作权
        cfunc=str(idaapi.decompile(func.start_ea))#对这个函数进行反编译
        if "__asm { BR" in cfunc and "_ReadStatusReg" in cfunc:
            func_start.append(func.start_ea)

begin=0x4f1d0
end=0x10526c
ida_decode(begin,end)

for i in func_start:
    print(hex(i),end=",")


#下面是结尾函数
func_end=[]
for i in range(len(func_start)):
    Asm_data=""
    ea=func_start[i]
    while True:
        asm_code=str(hex(ea))+":    "+idc.generate_disasm_line(ea,0)+"\n"
        now_ea=ea
        ea=idc.next_head(ea)
        if "RET" in asm_code:
            asm_next_code=str(hex(ea))+":    "+idc.generate_disasm_line(ea,0)+"\n"
            if ".__stack_chk_fail" in asm_next_code:
                func_end.append(ea)
            else:
                func_end.append(now_ea)
            break
print(len(func_end))
for i in (func_end):
    print(hex(i),end=",")

image-20250729214206138

上面会得到已经反编译出来的所有不能正常看的函数的值,下面就是编写unidbg代码来hook这些函数,从而计算这些if跳转的地址

package com.tengxun2023;
import capstone.Capstone;
import capstone.api.Instruction;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.Backend;
import com.github.unidbg.arm.backend.CodeHook;
import com.github.unidbg.arm.backend.UnHook;
import com.github.unidbg.debugger.DebuggerType;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.DvmClass;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.virtualmodule.android.AndroidModule;
import com.github.unidbg.Emulator;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.debugger.BreakPointCallback;
import com.github.unidbg.debugger.DebuggerType;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.virtualmodule.android.AndroidModule;

import java.io.*;

import com.github.unidbg.arm.context.Arm64RegisterContext;

import keystone.Keystone;
import keystone.KeystoneArchitecture;
import keystone.KeystoneEncoded;
import keystone.KeystoneMode;
import unicorn.Arm64Const;

import javax.swing.*;
import java.io.File;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

public class fin_2023 {
    public final AndroidEmulator emulator;
    public final VM vm;
    public final Memory memory;
    public final Module module;
    DvmClass cNative;
    //基本的环境配置
    public fin_2023(){
        emulator = AndroidEmulatorBuilder.for64Bit().build();
        memory =  emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));
        emulator.getSyscallHandler().setEnableThreadDispatcher(true);
        vm = emulator.createDalvikVM();
        new AndroidModule(emulator,vm).register(memory);

        DalvikModule dalvikModule = vm.loadLibrary(new File("unidbg-android/src/test/java/com/tengxun2023/libsec2023.so"), true);
        module = dalvikModule.getModule();
        vm.callJNI_OnLoad(emulator,module);
    }
    public static void padding_reg2val(HashMap<String,Integer> reg2val){
        reg2val.put("x0", Arm64Const.UC_ARM64_REG_X0);
        reg2val.put("x1",Arm64Const.UC_ARM64_REG_X1);
        reg2val.put("x2",Arm64Const.UC_ARM64_REG_X2);
        reg2val.put("x3",Arm64Const.UC_ARM64_REG_X3);
        reg2val.put("x4",Arm64Const.UC_ARM64_REG_X4);
        reg2val.put("x5",Arm64Const.UC_ARM64_REG_X5);
        reg2val.put("x6",Arm64Const.UC_ARM64_REG_X6);
        reg2val.put("x7",Arm64Const.UC_ARM64_REG_X7);
        reg2val.put("x8",Arm64Const.UC_ARM64_REG_X8);
        reg2val.put("x9",Arm64Const.UC_ARM64_REG_X9);
        reg2val.put("x10",Arm64Const.UC_ARM64_REG_X10);
        reg2val.put("x11",Arm64Const.UC_ARM64_REG_X11);
        reg2val.put("x12",Arm64Const.UC_ARM64_REG_X12);
        reg2val.put("x13",Arm64Const.UC_ARM64_REG_X13);
        reg2val.put("x14",Arm64Const.UC_ARM64_REG_X14);
        reg2val.put("x15",Arm64Const.UC_ARM64_REG_X15);
        reg2val.put("x16",Arm64Const.UC_ARM64_REG_X16);
        reg2val.put("x17",Arm64Const.UC_ARM64_REG_X17);
        reg2val.put("x18",Arm64Const.UC_ARM64_REG_X18);
        reg2val.put("x19",Arm64Const.UC_ARM64_REG_X19);
        reg2val.put("x20",Arm64Const.UC_ARM64_REG_X20);
        reg2val.put("x21",Arm64Const.UC_ARM64_REG_X21);
        reg2val.put("x22",Arm64Const.UC_ARM64_REG_X22);
        reg2val.put("x23",Arm64Const.UC_ARM64_REG_X23);
        reg2val.put("x24",Arm64Const.UC_ARM64_REG_X24);
        reg2val.put("x25",Arm64Const.UC_ARM64_REG_X25);
        reg2val.put("x26",Arm64Const.UC_ARM64_REG_X26);
        reg2val.put("x27",Arm64Const.UC_ARM64_REG_X27);
        reg2val.put("x28",Arm64Const.UC_ARM64_REG_X28);
        reg2val.put("x29",Arm64Const.UC_ARM64_REG_FP);
        reg2val.put("x30",Arm64Const.UC_ARM64_REG_LR);
        reg2val.put("sp",Arm64Const.UC_ARM64_REG_SP);
        reg2val.put("pc",Arm64Const.UC_ARM64_REG_PC);

    }

    //将数组转换为long型
    public static long byteArrayToLong(byte[] byteArray){
        ByteBuffer buffer=ByteBuffer.wrap(byteArray);
        buffer.order(ByteOrder.LITTLE_ENDIAN);
        return buffer.getLong();
    }

    //读取寄存器的值
    public  long read_reg_by_str(Backend backend,String reg){
        long true_reg_val=-1;
    if(reg2val.containsKey(reg)){
        true_reg_val=backend.reg_read(reg2val.get(reg)).longValue();
    }
    else if(reg.equals("xzr")){
        true_reg_val=0;
    }else{
        assert false;
    }
    return true_reg_val;
    }


    public HashMap<String,Integer> reg2val=new HashMap<>();
    public Set<Long> patch_addr_set=new HashSet<>();
    public HashMap<String,Integer> patch_addr2bytes=new HashMap<>();

    //计算跳转指令的数量
    public void anti_br_by_static(Backend backend,long begin,long end){
        try{
            //获取区域内所有的汇编指令
            Capstone capstone=new Capstone(Capstone.CS_ARCH_ARM64,Capstone.CS_MODE_LITTLE_ENDIAN);
            byte[] bytes=backend.mem_read(begin+ module.base,end-begin);
            Instruction[] disasm=capstone.disasm(bytes,0);
            HashMap<Long,byte[]>reg2val =new HashMap<>();

            for(int i=0;i<disasm.length;i++){
                if("csel".equals(disasm[i].getMnemonic()) && "ldr".equals(disasm[i+1].getMnemonic()) && "add".equals(disasm[i+2].getMnemonic()) && "br".equals(disasm[i+3].getMnemonic())){
                    String ins1_reg[] =disasm[i].getOpStr().split(",");
                    String ins1_reg1_name=ins1_reg[0].trim();
                    String ins1_reg2_name=ins1_reg[1].trim();
                    String ins1_reg3_name=ins1_reg[2].trim();
                    String ins1_op_name=ins1_reg[3].trim();

                    String ins2_regs[]=disasm[i+1].getOpStr().split(",");
                    String ins3_regs[]=disasm[i+2].getOpStr().split(",");

                    String ins2_reg1_name=ins2_regs[0].replaceAll("[\\s\\[]","");
                    String ins2_reg2_name=ins2_regs[1].replaceAll("[\\s\\[]","");
                    String ins2_reg3_name=ins2_regs[2].replaceAll("[\\s\\[]","");

                    String ins3_reg1_name=ins3_regs[0].replaceAll("[\\s\\[]","");
                    String ins3_reg2_name=ins3_regs[1].replaceAll("[\\s\\[]","");
                    String ins3_reg3_name=ins3_regs[2].replaceAll("[\\s\\[]","");

                    long true_op_reg_val=-1;
                    long false_op_reg_val=-1;
                    Boolean true_tag=false;
                    Boolean false_tag=false;

                    //接下来是指令回溯,往上翻代码来静态计算
                    try{
                        for(int j=i-1;j>=i-4;j--){
                            String spl_regs[]=disasm[j].getOpStr().split(",");
                            String src_reg = spl_regs[0].replace('w','x').trim();
                            if((disasm[j].getMnemonic().contains("mov") && disasm[j].getMnemonic().contains("movk")==false) && (src_reg.contains(ins1_reg2_name) || src_reg.contains(ins1_reg3_name))){
                                String ins_regs[]=disasm[j].getOpStr().split(",");
                                if(src_reg.contains(ins1_reg2_name)){
                                    if(true_tag){
                                        continue;
                                    }
                                    String true_op_reg_val_str=ins_regs[1].replaceAll("[\\s\\[#]","");
                                    true_op_reg_val=Long.parseLong(true_op_reg_val_str.substring(2),16);
                                    true_tag=true;
                                }
                                else if(src_reg.contains(ins1_reg3_name)){
                                    if(false_tag){
                                        continue;
                                    }
                                    String false_op_reg_val_str=ins_regs[1].replaceAll("[\\s\\[#]","");
                                    false_op_reg_val=Long.parseLong(false_op_reg_val_str.substring(2),16);
                                    false_tag=true;
                                }
                            }

                        }
                    }catch(Exception e){
                        System.out.printf("%s\n",e);
                    }

                    if(ins1_reg2_name.contains("xzr") || ins1_reg2_name.contains("wzr")){
                        true_op_reg_val=0;
                    }
                    if(ins1_reg3_name.contains("xzr") || ins1_reg3_name.contains("wzr")){
                        false_op_reg_val=0;
                    }

                    if(true_op_reg_val==-1){
                        true_op_reg_val=read_reg_by_str(backend,ins1_reg2_name);
                    }
                    if(false_op_reg_val==-1){
                        false_op_reg_val=read_reg_by_str(backend,ins1_reg3_name);
                    }

                    long base_reg_val=read_reg_by_str(backend,ins2_reg2_name);
                    long add_reg_val=read_reg_by_str(backend,ins3_reg3_name);

                    byte[] true_jmp_addr_byte=backend.mem_read(base_reg_val+true_op_reg_val,8);
                    long true_jmp_addr=byteArrayToLong(true_jmp_addr_byte);
                    byte[] fals_jmp_addr_byte = backend.mem_read(base_reg_val + false_op_reg_val, 8);
                    long fals_jmp_addr = byteArrayToLong(fals_jmp_addr_byte);

                    long true_fin_jmp = true_jmp_addr +add_reg_val;
                    long fals_fin_jmp = fals_jmp_addr + add_reg_val;

                    String new_asm1=String.format("B.%s\t0x%x",ins1_op_name,true_fin_jmp);
                    String new_asm2=String.format("B\t0x%x",fals_fin_jmp);
                    Keystone keyStone=new Keystone(KeystoneArchitecture.Arm64, KeystoneMode.LittleEndian);

                    KeystoneEncoded assemble1 =keyStone.assemble(new_asm1,(int) begin+4*i);
                    byte[] machineCode1=assemble1.getMachineCode();

                    KeystoneEncoded assemble2 =keyStone.assemble(new_asm2,(int) begin+4*i+4);
                    byte[] machineCode2=assemble2.getMachineCode();

                    KeystoneEncoded assemble3=keyStone.assemble("nop");
                    byte[] machineCode3=assemble3.getMachineCode();

                    byte[] final_patch=new byte[16];

                    System.arraycopy(machineCode1, 0, final_patch, 0, 4);
                    System.arraycopy(machineCode2, 0, final_patch, 4, 4);
                    System.arraycopy(machineCode3, 0, final_patch, 8, 4);
                    System.arraycopy(machineCode3, 0, final_patch, 12, 4);
                    reg2val.put(begin + 4 * i,final_patch);
               }
            }
            System.out.printf("begin,end=0x%x,0x%x\n",begin,end);
            System.out.printf("patcher={}\n");
            for(Long address:reg2val.keySet()){
                byte[] patch_bytes=reg2val.get(address);
                System.out.printf("patcher[0x%x]  =%s\n",address, Arrays.toString(patch_bytes));
            }
            System.out.printf("patcher()\n");
            System.out.printf("\n\n");

        }catch (Exception ee){
            System.out.printf("==>%s\n",ee);
        }

    }

    public void hook_and_anti(long begin_addr,long end_addr){
        int arr[]={0};
        emulator.getBackend().hook_add_new(new CodeHook() {

            @Override
            public void hook(Backend backend, long address, int size, Object user) {
                int[] newArr=(int[]) user;
                if(newArr[0]==1){
                    return;
                }
                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);
                if("csel".equals(disasm[0].getMnemonic())){
                    anti_br_by_static(backend,begin_addr,end_addr);
                    newArr[0]=1;
                }

            }

            @Override
            public void onAttach(UnHook unHook) {

            }

            @Override
            public void detach() {

            }
        }, module.base+begin_addr,module.base+end_addr,arr);
    }

    public class res_two_regs{
        public String reg1_name;
        public String reg2_name;
        public String if_op;
        public long reg1_val;
        public long reg2_val;
        public res_two_regs(String reg1_name,String reg2_name,long reg1_val,long reg2_val){
            this.reg1_name = reg1_name;
            this.reg2_name = reg2_name;
            this.reg1_val = reg1_val;
            this.reg2_val = reg2_val;
            this.if_op =if_op;
        }
    }

    //强行绕过第一部分的检查
    public void set_hook(){
        emulator.getBackend().hook_add_new(new CodeHook() {
            @Override
            public void hook(Backend backend, long address, int size, Object user) {
                if (address==0x94440+module.base){
                    backend.reg_write(Arm64Const.UC_ARM64_REG_X0,0);
                }
            }

            @Override
            public void onAttach(UnHook unHook) {

            }

            @Override
            public void detach() {

            }
        },0x9443c+ module.base,0x94444+ module.base,null);
    }

    public void anti_confuse(){
        padding_reg2val(reg2val);

        // 这里记录了要去混淆的函数收尾地址,需要注意,这个尾地址,用func.end_ea,idapython得到的可能不对
        long br_addr[][] = {
                {0xe2db4,0x0000000000E39FC},
                {0xe227c,0x0000000000E2DB0},
                {0xcf004,0x0000000000CFAC0},
                {0xd2ad0,0x0000000000D31D0},
                {0xd6158,0x0000000000D6DF4},
                {0xd6e00,0x0000000000D7AC4},
                {0xd1cc0,0x0000000000D241C},
                {0xe6bf4,0x0000000000E76D8},
                {0xe3e18,0x0000000000E497C},
                {0xe4c50,0x0000000000E58A4},
                {0xe0be8,0x0000000000E1144},
                {0xd0b2c,0x0000000000D10A0},
                {0xe8054,0x0000000000E8854},
                {0xd7f4c,0x0000000000D8B60},
                {0xd5518,0x0000000000D5A74},
                {0x00000000000EB408,0x0000000000EB8BC}
        };
        set_hook();
        for (int i = 0; i < br_addr.length; i++) {
            long begin = br_addr[i][0];
            long end = br_addr[i][1];
            hook_and_anti(begin,end);
        }
        module.callFunction(emulator,0x000000000094368,114514);
    }



    public  static  void main(String[] args){
        fin_2023 mainActivity = new fin_2023();
        mainActivity.anti_confuse();
    }

}

不过上面的代码是没有自动化patch的,还需要配合idapython进行patch

下面是idapython的代码:

import idc
import idaapi

def patchers():
    global patcher,begin,end
    for address in patcher:
        patch_code=patcher[address]
        print(patch_code)
        for addr in range(address,address+16):
            idc.patch_byte(addr,patch_code[addr-address])
    print("finsh")

    def upc(begin,end):
        for i in range(begin,end):
            idc.del_items(i)
        
        for i in range(begin,end):
            idc.create_insn(i)
        idaapi.add_func(begin,end)
        print("upc finish")
    
    upc(begin,end)
    
begin,end = 0xe4c50,0xe58a4
patcher = {} 
patcher[0xe4d8c]  = [32, 87, 0, 84, 3, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe4e4c]  = [-117, 0, 0, 84, 77, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe4fc8]  = [-128, 0, 0, 84, 116, -1, -1, 23, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe5188]  = [-128, 0, 0, 84, 4, -1, -1, 23, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe4e84]  = [-117, 0, 0, 84, -48, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe53c4]  = [-128, 0, 0, 84, 117, -2, -1, 23, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe5544]  = [-128, 0, 0, 84, 21, -2, -1, 23, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe5784]  = [-128, 0, 0, 84, -123, -3, -1, 23, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe5100]  = [-117, 0, 0, 84, -26, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe5140]  = [-117, 0, 0, 84, -64, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe515c]  = [-128, 0, 0, 84, 15, -1, -1, 23, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe565c]  = [-128, 0, 0, 84, -49, -3, -1, 23, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe4dd8]  = [-117, 0, 0, 84, -118, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe4e18]  = [-128, 0, 0, 84, -32, -1, -1, 23, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe5098]  = [-128, 0, 0, 84, 64, -1, -1, 23, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe52d8]  = [-128, 0, 0, 84, -80, -2, -1, 23, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe5418]  = [-128, 0, 0, 84, 96, -2, -1, 23, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe51d4]  = [-128, 0, 0, 84, -15, -2, -1, 23, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe4f10]  = [-117, 0, 0, 84, -25, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe4f90]  = [-117, 0, 0, 84, 104, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe5010]  = [-117, 0, 0, 84, -30, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe5310]  = [-117, 0, 0, 84, -119, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe5450]  = [-117, 0, 0, 84, -55, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe5750]  = [-128, 0, 0, 84, -110, -3, -1, 23, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe4f2c]  = [-128, 0, 0, 84, -101, -1, -1, 23, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe4fac]  = [-117, 0, 0, 84, -43, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe502c]  = [-128, 0, 0, 84, 91, -1, -1, 23, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe532c]  = [-128, 0, 0, 84, -101, -2, -1, 23, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe546c]  = [-128, 0, 0, 84, 75, -2, -1, 23, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe4e68]  = [-117, 0, 0, 84, -126, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe53a8]  = [-117, 0, 0, 84, -87, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe54a8]  = [-128, 0, 0, 84, 60, -2, -1, 23, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe50e4]  = [-21, -28, -1, 84, 3, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe5124]  = [96, 14, 0, 84, 29, -1, -1, 23, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe4da0]  = [-117, 0, 0, 84, 41, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe4ea0]  = [-128, 0, 0, 84, -66, -1, -1, 23, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe4dbc]  = [-117, 0, 0, 84, 74, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe507c]  = [-117, 0, 0, 84, -36, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe52bc]  = [-117, 0, 0, 84, -116, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe53fc]  = [-117, 0, 0, 84, -47, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe54fc]  = [-128, 0, 0, 84, 39, -2, -1, 23, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe4df4]  = [-117, 0, 0, 84, -31, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patcher[0xe4ef4]  = [-117, 0, 0, 84, 118, 0, 0, 20, 31, 32, 3, -43, 31, 32, 3, -43]
patchers() 

这样可以恢复个七七八八,不过我再搜索的时候发现了unidbg可以自己patcher,学习一下,代码如下:

下面是最终版代码:

(一定要限制范围,不要patch全部的,太容易乱了)

final:

package com.tengxun2023;

import capstone.Capstone;
import capstone.api.Instruction;
import com.alibaba.fastjson.util.IOUtils;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.Backend;
import com.github.unidbg.arm.backend.CodeHook;
import com.github.unidbg.arm.backend.UnHook;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.DvmClass;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.virtualmodule.android.AndroidModule;


import keystone.Keystone;
import keystone.KeystoneArchitecture;
import keystone.KeystoneEncoded;
import keystone.KeystoneMode;
import unicorn.Arm64Const;

import javax.sound.midi.Patch;
import java.io.File;
import java.io.FileInputStream;
import java.nio.file.Files;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.*;
import java.util.List;
import java.util.Stack;

//我要在这个里面写一个不同类型的混淆的去除手段
public class anti {

    //初始化环境
    private List<PatchBR> patch_list;
    private Deque<InsAndCtx> instructions;

    public final AndroidEmulator emulator;
    public final VM vm;
    public final Memory memory;
    public final Module module;
    private Set<Long> executedAddresses = new HashSet<>();
    public String inname="unidbg-android/src/test/java/com/tengxun2023/input.so";
    public String outname="unidbg-android/src/test/java/com/tengxun2023/output.so";
    DvmClass cNative;
    public anti(){

        instructions = new ArrayDeque<>();
        patch_list = new ArrayList<>();
        emulator = AndroidEmulatorBuilder.for64Bit().build();
        memory =  emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));
        emulator.getSyscallHandler().setEnableThreadDispatcher(true);
        vm = emulator.createDalvikVM();
        new AndroidModule(emulator,vm).register(memory);

        DalvikModule dalvikModule = vm.loadLibrary(new File("unidbg-android/src/test/java/com/tengxun2023/libsec2023.so"), true);
        module = dalvikModule.getModule();
        long br_addr[][] = {
                {0xe2db4,0x0000000000E39FC},
                {0xe227c,0x0000000000E2DB0},
                {0xcf004,0x0000000000CFAC0},
                {0xd2ad0,0x0000000000D31D0},
                {0xd6158,0x0000000000D6DF4},
                {0xd6e00,0x0000000000D7AC4},
                {0xd1cc0,0x0000000000D241C},
                {0xe6bf4,0x0000000000E76D8},
                {0xe3e18,0x0000000000E497C},
                {0xe4c50,0x0000000000E58A4},
                {0xe0be8,0x0000000000E1144},
                {0xd0b2c,0x0000000000D10A0},
                {0xe8054,0x0000000000E8854},
                {0xd7f4c,0x0000000000D8B60},
                {0xd5518,0x0000000000D5A74},
                {0xEB408,0x0000000000EB8BC},
                {0xE4C50,0xE58A0}
        };
        set_hook();
        for (int i = 0; i < br_addr.length; i++) {
            long begin = br_addr[i][0];
            long end = br_addr[i][1];
            save_code(begin,end);
        }
        //vm.callJNI_OnLoad(emulator,module);
        module.callFunction(emulator,0x000000000094368,114514);
    }
    
    public static void main(String[] args){

        anti anti_ob=new anti();
        anti_ob.destory();


    }

    //首先先定义一些类

    //保存指令和寄存器类
    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;
        }
    }

    //混淆的类型
    enum br_type{
        direct_br,direct_blr,indirect_br;
    }

    enum error_type{
        ok,conflict_br_type,conflict_b_jump_address
    }

    class PatchBR{
        List<PatchIns>patchs;
        List<Long> br_jump_to;
        long br_addr;
        br_type type;
        error_type errorno;
        String error_info;
        PatchBR(){
            //List<PatchIns> patchs = new ArrayList<>();
            patchs =new ArrayList<>();
            br_jump_to=new ArrayList<>();
            errorno=error_type.ok;
        }
    }
    //指令栈
//    private Stack<Instruction> instructions;
    //所有需要patch的指令
    private List<PatchIns> patchs;
    //保存指令寄存器环境
    public List<Number> saveRegs(Backend backend){
        List<Number> regs=new ArrayList<>();
        for(int i=0;i<29;i++){
            regs.add(backend.reg_read(i+ Arm64Const.UC_ARM64_REG_X0));
        }
        regs.add(backend.reg_read(Arm64Const.UC_ARM64_REG_FP));
        regs.add(backend.reg_read(Arm64Const.UC_ARM64_REG_LR));
        return regs;
    }

    //从寄存器中取值
    private Number getRegValue(String reg, List<Number> saved) {
        reg = reg.toLowerCase(Locale.ROOT);
        if (reg.equals("xzr") || reg.equals("wzr")) return 0;

        boolean is32 = reg.startsWith("w");
        int idx = Integer.parseInt(reg.substring(1));
        long val = saved.get(idx).longValue();
        return is32 ? (val & 0xFFFFFFFFL) : val;
    }



    //这里借鉴大佬设置一个错误处理
    public boolean identify_br(PatchBR pb){
        for(PatchBR _pb :patch_list){
            if(pb.br_addr== _pb.br_addr){
                if(pb.type!=_pb.type){
                    pb.errorno = error_type.conflict_br_type;
                    pb.error_info = "[error]confict br jump type at 0x"+Integer.toHexString((int) pb.br_addr)+"type->"+pb.type+" "+_pb.type;
                } else if (pb.br_jump_to!=_pb.br_jump_to) {
                    pb.errorno = error_type.conflict_br_type;
                    pb.error_info = "[error]confict br jump address at 0x"+Integer.toHexString((int) pb.br_addr)+"type->"+pb.br_jump_to+" "+_pb.br_jump_to;
                }
                return false;
            }

        }
        return true;
    }

    //读取地址的字节
    public long readInt64(Backend backend,long addr){
        byte[] bytes=backend.mem_read(addr,8);
        long res=0;
        for (int i=0;i<bytes.length;i++){
            res=((bytes[i]&0xffL)<<(8*i))+res;
        }
        return res;
    }


    //对每条环境都保存环境
    public void save_code(long begin, long end) {
        emulator.getBackend().hook_add_new(new CodeHook() {
            @Override
            public void hook(Backend backend, long address, int size, Object user) {
                // 如果该地址已经执行过,直接跳过
                if (executedAddresses.contains(address)) {
                    return;  // 跳过重复执行的地址
                }

                // 标记地址已执行
                executedAddresses.add(address);

                // 执行钩子逻辑
                if (address == 0x94440 + module.base) {
                    backend.reg_write(Arm64Const.UC_ARM64_REG_X0, 0);
                    System.out.println("修改cmp");
                }
                
                // 执行指令分析
                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);

                if (instructions.size() > 10) {
                    instructions.removeFirst();
                }
                instructions.addLast(iac);

                execute(backend);
            }

            @Override
            public void onAttach(UnHook unHook) {
                System.out.printf("attach");
            }

            @Override
            public void detach() {
                System.out.printf("detach");
            }
        }, module.base + begin, module.base + end, null);
    }

    //在这里进行执行判断逻辑,无非就是几种混淆,后面的混淆也可以在下面进行添加
    //在这里进行执行判断逻辑,无非就是几种混淆,后面的混淆也可以在下面进行添加
    public void execute(Backend backend) {

        if (instructions.isEmpty()) {
            return;
        }

        List<InsAndCtx> instructionList = new ArrayList<>(instructions);


        // 规则匹配
        for (int i = instructionList.size() - 1; i >= 0; i--) {
            InsAndCtx curr_iac = instructionList.get(i);
            String mnemonic = curr_iac.ins.getMnemonic();
            String opStr = curr_iac.ins.getOpStr();

            try{
                // 匹配 br 或 blr 指令
                if (mnemonic.equals("br") || mnemonic.equals("blr")) {
                    boolean csel_ldr_add_br = false;

                    // 检查是否为复杂的间接跳转模式 (csel, ldr, add, br)
                    if (mnemonic.equals("br") && opStr.trim().equals("x9") && i >= 3) {
                        InsAndCtx addIns = instructionList.get(i - 1);
                        InsAndCtx ldrIns = instructionList.get(i - 2);
                        InsAndCtx cselIns = instructionList.get(i - 3);
                        if (addIns.ins.getMnemonic().equals("add") && ldrIns.ins.getMnemonic().equals("ldr") && cselIns.ins.getMnemonic().equals("csel")) {
                            csel_ldr_add_br = true;
                        }
                    }

                    // --- 处理复杂的间接跳转 (csel/ldr/add/br) ---
                    if (csel_ldr_add_br) {
                        InsAndCtx br = curr_iac;
                        InsAndCtx add = instructionList.get(i - 1);
                        InsAndCtx ldr = instructionList.get(i - 2);
                        InsAndCtx csel = instructionList.get(i - 3);

                        // 1. 从 CSEL 指令中获取信息
                        String[] csel_operands = csel.ins.getOpStr().toLowerCase(Locale.ROOT).split(",");
                        // csel xd, xn, xm, cond => csel_operands = [" xd", " xn", " xm", " cond"]
                        String reg1 = csel_operands[1].trim(); // source register if true
                        String reg2 = csel_operands[2].trim(); // source register if false
                        String condition = csel_operands[3].trim(); // condition

                        //  FIX: Correctly get values from both reg1 and reg2
                        long branch1 = getRegValue(reg1, csel.getRegs()).longValue();
                        long branch2 = getRegValue(reg2, csel.getRegs()).longValue();

                        // 2. 从 LDR 指令中获取信息
                        // Assuming ldr xd, [xn] format
                        String[] ldr_operands = ldr.ins.getOpStr().split(",");
                        String base_reg = ldr_operands[1].replaceAll("[\\[\\]]", "").trim();
                        long base_reg_val = getRegValue(base_reg, ldr.getRegs()).longValue();

                        // 3. 从 ADD 指令中获取信息
                        String[] add_operands = add.ins.getOpStr().split(",");
                        String reg_add = add_operands[2].trim();
                        long add_reg_val = getRegValue(reg_add, add.getRegs()).longValue();

                        // 4. 计算两个可能的跳转目标地址
                        // Simulating: read from jump table -> add offset
                        long true_addr = readInt64(backend, base_reg_val + branch1) + add_reg_val;
                        long false_addr = readInt64(backend, base_reg_val + branch2) + add_reg_val;


                        // 5. FIX: 创建正确的 Patch 序列
                        PatchBR pb = new PatchBR();
                        pb.type = br_type.indirect_br;
                        pb.br_addr = br.addr - module.base;

                        // Patch 1: csel操作
                        PatchIns pi1 = new PatchIns();
                        pi1.setAddr(csel.addr - module.base);
                        pi1.setIns(String.format("b.%s 0x%x", condition, true_addr - module.base));
                        pb.patchs.add(pi1);

                        // Patch 2: Replace ldr操作
                        PatchIns pi2 = new PatchIns();
                        pi2.setAddr(ldr.addr - module.base);
                        pi2.setIns(String.format("b 0x%x", false_addr - module.base));
                        pb.patchs.add(pi2);

                        // Patch 3 & 4: NOP
                        PatchIns pi3 = new PatchIns();
                        pi3.setAddr(add.addr - module.base);
                        pi3.setIns("nop");
                        pb.patchs.add(pi3);

                        PatchIns pi4 = new PatchIns();
                        pi4.setAddr(br.addr - module.base);
                        pi4.setIns("nop");
                        pb.patchs.add(pi4);

                        pb.br_jump_to.add(true_addr - module.base);
                        pb.br_jump_to.add(false_addr - module.base);

                        if (identify_br(pb)) {
                            System.out.println("[info] find indirect br jump at 0x" + Long.toHexString(pb.br_addr) +" -> true: 0x" + Long.toHexString(true_addr - module.base)+" ("+condition+"), false: 0x" + Long.toHexString(false_addr - module.base) + ", type:" + pb.type);
                            patch_list.add(pb);
                            doPatch();
                        }

                        return;

                        // --- 处理简单的直接跳转 (br/blr) ---
                    } else {
                        InsAndCtx br = curr_iac;
                        PatchBR pb = new PatchBR();
                        pb.br_addr = br.addr - module.base;

                        String jump_reg = br.ins.getOpStr().trim();
                        long target_addr = getRegValue(jump_reg, br.getRegs()).longValue();

                        pb.br_jump_to.add(target_addr - module.base);

                        PatchIns pi1 = new PatchIns();
                        pi1.setAddr(br.addr - module.base);

                        if (mnemonic.equals("br")) {
                            pb.type = br_type.direct_br;
                            pi1.setIns(String.format("b 0x%x", target_addr - module.base));
                        } else { // blr
                            pb.type = br_type.direct_blr;
                            pi1.setIns(String.format("bl 0x%x", target_addr - module.base));
                        }
                        pb.patchs.add(pi1);

                        if (identify_br(pb)) {
                            System.out.println("[info] find "+mnemonic+" jump at 0x" + Long.toHexString(pb.br_addr) + " -> to 0x" + Long.toHexString(target_addr - module.base) + ", type:" + pb.type);
                            patch_list.add(pb);
                            doPatch();
                        }
                        return;
                    }
                }
            } catch (Exception e){
                System.err.println("Error processing instruction at address 0x" + Long.toHexString(curr_iac.addr));
                e.printStackTrace();
            }
        }
    }
    public void doPatch() {
        try {
            // 1. 读原 so
            File f = new File(inname);
            byte[] data = Files.readAllBytes(f.toPath());

            if (patch_list.isEmpty()) {
                System.out.println("No patches to apply.");
                return;
            }

            // 2. 遍历每个 br-patch 组
            for (PatchBR br : patch_list) {
                if (br.errorno != error_type.ok) {
                    System.out.println(br.error_info);   // 有冲突时仅日志提示
                    continue;
                }

                for (PatchIns pi : br.patchs) {  // ← 这里才是真正的 patch 指令
                    System.out.printf("Processing addr: 0x%X, code: %s%n", pi.getAddr(), pi.getIns());

                    // 使用 Keystone 编译指令为机器码
                    Keystone ks = new Keystone(KeystoneArchitecture.Arm64, KeystoneMode.LittleEndian);
                    KeystoneEncoded enc = ks.assemble(pi.getIns(), (int) pi.getAddr());
                    byte[] mc = enc.getMachineCode();

                    // 将机器码写入原文件的数据中
                    System.arraycopy(mc, 0, data, (int) pi.addr, mc.length);
                }
            }

            // 3. 写出修补后的 so
            Files.write(new File(outname).toPath(), data);
            System.out.println("Patch applied successfully.");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    void destory(){
        IOUtils.close(emulator);
        System.out.println("destory");
    }

    public void set_hook(){
        emulator.getBackend().hook_add_new(new CodeHook() {
            @Override
            public void hook(Backend backend, long address, int size, Object user) {
                if (address==0x94440+module.base){
                    backend.reg_write(Arm64Const.UC_ARM64_REG_X0,0);
                }
            }

            @Override
            public void onAttach(UnHook unHook) {

            }

            @Override
            public void detach() {

            }
        },0x9443c+ module.base,0x94444+ module.base,null);
    }


}

其余的有没执行到的,直接写idapython脚本匹配一下去除

#使用idapython匹配字节然后进行nop
import ida_bytes
import idc
import idaapi


def find_and_patch(start,end):
    pattern=['CMP', 'MOV', 'MOV', 'CSEL', 'LDR', 'ADD', 'BR']
    res=[]
    while start<end:
        ea=start
        cnt=0
        for i in range(len(pattern)):
            if pattern[i] != idc.print_insn_mnem(ea):
                break
            else:
                cnt+=1
                ea=idc.next_head(ea,end)
        if cnt==7:
            res.append((start,start+4*7))
            start+=4*7
        else:
            start+=4
    return res

def set_color(start,end):
    for addr in range(start,end):
        idc.set_color(addr,idc.CIC_ITEM,0xd0ffc0)

def do_patch(start,end):
    nop_bytes = (0x1F2003D5).to_bytes(4, "big")
    while start<end:
        ida_bytes.patch_bytes(start,nop_bytes)
        start+=4
begin=0xE2DB4
end=0xE39F8
patchs=find_and_patch(begin,end)
print(patchs)
for i in range(len(patchs)):
    set_color(patchs[i][0],patchs[i][1])
    do_patch(patchs[i][0],patchs[i][1])

算法分析:

第一段加密:

第一段加密对应着最开始的BR混淆以及不透明谓词混淆吧,反正挺清晰的应该

image-20250805114026257

都是通过frida 一点点hook出来的

image-20250805114058741

然后可以发现有一个encode函数,我们可以进行发现有混淆,不过能看

// The function seems has been flattened
void __fastcall encode(__int64 input, __int64 len_17, __int64 a3, _QWORD *inp)
{
  int n0x3B9A031E; // w8
  int v9; // w0
  int n111673319; // w9
  bool v11; // zf
  int n1288343544; // w9
  _BYTE v13[128]; // [xsp+30h] [xbp-1E0h] BYREF
  _BYTE v14[128]; // [xsp+B0h] [xbp-160h] BYREF
  _BYTE inp_1[128]; // [xsp+130h] [xbp-E0h] BYREF
  __int64 x25____________x21____x19_1024; // [xsp+1B0h] [xbp-60h]

  x25____________x21____x19_1024 = *(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);// x25应该是提前写好的一个值
                                                // x21是输入
                                                // x19是1024
  copy1(inp, 1LL);                              // 这里有一个固定的参数一,是一个长数
                                                // 
                                                // 
                                                // 左移16位,然后与原来的值进行或操作
                                                // 这里竟然吧x21的值给覆盖了
                                                // 也就是把输入的值给覆盖了
                                                // 上面这些初始化了一些值,低216位
                                                // 这里处理高16位,同时跟上面的赋值操作并运算
                                                // 这里跳转了,跳到了0x9f4c4,也就是刚开始定义为密钥初始化
                                                // 但是这个函数好像不是密钥初始化,是用于函数分发的
                                                // 而且调用了很多次,一直在循环读取某一块内存,不知道是干什么的,跳转的太多了
  copy2(inp_1, input);
  copy2(v14, len_17);                           // 这里再次跳转,跳到0xa1314
  n0x3B9A031E = 0x3B9A031E;
LABEL_9:
  // 这里跳转了
  // 再次跳转,0xa040c
  // 应该还是和刚才哪个相似,都是为了对数值进行处理
  // 跳转了0xa2c38
  // 没跳转
  while ( n0x3B9A031E >= 0x6A7FFE7 )
  {
    // 跳转了
    if ( n0x3B9A031E < 999949086 )
    {
      if ( n0x3B9A031E == 0x6A7FFE7 )
      {
        muls(inp_1, inp_1, v13);
        mods(v13, a3, inp_1);
        n1288343544 = 1288343544;               // 这里应该就是来处理平方的
                                                // ,一个是大整数平方,一个是大整数取模
                                                // 就是熟知的快速幂算法
        goto LABEL_13;
      }
      goto LABEL_3;
    }
    // 没跳转
    if ( n0x3B9A031E == 999949086 )
    {
      v11 = (v14[0] & 1) == 0;
      n0x3B9A031E = -778487121;
      n111673319 = -850153581;
      goto LABEL_17;
    }
LABEL_3:
    if ( n0x3B9A031E < -251900671 )
    {
      do
      {
        while ( n0x3B9A031E < -778487121 )
        {
          if ( n0x3B9A031E != -850153581 )
            goto LABEL_3;
          sub_A236C(v14, v13);
          copy2(v14, v13);
          v9 = sub_A1AE0(v14);
          n0x3B9A031E = -251900671;
          n111673319 = 111673319;
          v11 = v9 == 0;
LABEL_17:
          // 再次跳转,0xa040c
          // 应该还是和刚才哪个相似,都是为了对数值进行处理
          // 跳转了0xa2c38
          // 没跳转
          // 跳转了
          // 没跳转
          if ( v11 )
            n0x3B9A031E = n111673319;
          // 跳转了
          if ( n0x3B9A031E >= -251900671 )
            goto LABEL_9;
        }
        if ( n0x3B9A031E != -778487121 )
          goto LABEL_3;
        muls(inp, inp_1, v13);                  // 再次跳转,0xa040c
                                                // 应该还是和刚才哪个相似,都是为了对数值进行处理
        mods(v13, a3, inp);                     // 跳转了0xa2c38
        n1288343544 = -561759123;
LABEL_13:
        n0x3B9A031E = n1288343544 - 288394458;  // 没跳转
                                                // 跳转了
      }
      while ( n1288343544 - 288394458 < -251900671 );
    }
  }
  if ( n0x3B9A031E != -251900671 )
    goto LABEL_3;
}

注释是trace的时候看的流程,没啥用,其实也能看到关键逻辑

 muls(inp_1, inp_1, v13);
 mods(v13, a3, inp_1);

直接frida trace或者frida hook就行

我是线trace看逻辑猜,在frida hook看数据变化

function encry1() {
    const baseAddr = Module.findBaseAddress('libsec2023.so');
    let tar = 0
    const offset = 0xA040C;
    const func_inp = baseAddr.add(offset);
    Interceptor.attach(func_inp, {
        onEnter: function (args) {
            console.log("函数一 参数一");
            const buf = Memory.readByteArray(args[0], 0x30);
            console.log(hexdump(buf, {
                offset: 0,
                length: 0x30,
                header: true,
                ansi: true
            }));
            console.log("函数一 参数二");
            var buf2 = Memory.readByteArray(args[1], 0x30);
            console.log(hexdump(buf2, {
                offset: 0,
                length: 0x30,
                header: true,
                ansi: true
            }));

            console.log("函数一 参数三");
            var buf2 = Memory.readByteArray(args[2], 0x30);
            console.log(hexdump(buf2, {
                offset: 0,
                length: 0x30,
                header: true,
                ansi: true
            }));

        },
        onLeave: function (retval) {
            console.log("函数一 返回值");
            var buf3 = Memory.readByteArray(retval, 0x30);
            console.log(hexdump(buf3, {
                offset: 0,
                length: 0x30,
                header: true,
                ansi: true
            }));
        }
    });
}
function encry2() {
    const baseAddr = Module.findBaseAddress('libsec2023.so');
    let tar = 0
    const offset = 0xA2C38;
    const func_inp = baseAddr.add(offset);
    Interceptor.attach(func_inp, {
        onEnter: function (args) {
            console.log("函数二 参数一");
            const buf = Memory.readByteArray(args[0], 0x30);
            console.log(hexdump(buf, {
                offset: 0,
                length: 0x30,
                header: true,
                ansi: true
            }));
            console.log("函数二 参数二");
            var buf2 = Memory.readByteArray(args[1], 0x30);
            console.log(hexdump(buf2, {
                offset: 0,
                length: 0x30,
                header: true,
                ansi: true
            }));

            console.log("函数二 参数三");
            var buf2 = Memory.readByteArray(args[2], 0x30);
            console.log(hexdump(buf2, {
                offset: 0,
                length: 0x30,
                header: true,
                ansi: true
            }));

        },
        onLeave: function (retval) {
            // 可选:你也可以打印返回值或回溯栈
            //console.log("===", this.context.x1)
            console.log("函数二 返回值");
            var buf3 = Memory.readByteArray(retval, 0x30);
            console.log(hexdump(buf3, {
                offset: 0,
                length: 0x30,
                header: true,
                ansi: true
            }));
        }
    });
}

就能看到数据变化了,尽量取值大一点才能看到变化

image-20250805120835713

可以看到运行了多少次

通过一点点观察就是一个快速幂算法

def encry_1(base,ord,mod):
    res=1
    while ord>0:
        if ord%2==1:
            res=res*base%mod
        base=base*base%mod
        ord//=2
    return res%mod

a=0x4d2
ord=17
mods=0x0028a831a5bf4b902e95318e50c2075259f91094d08d84409e1b76eadfa0865d1278acc90fa7c6cf6acb375
tar=0x25f6b048b4f32e3ce9175bb64930f65101a706ae74988a4ec87b4d5ec7feb9223ab782bcf1ec9d7fee750
print(hex(encry_1(0x4d2,17,mods)))

经过测试就是求这个a

这个其实就是一个离散对数问题(学到了)

大数能直接被分解成质数,基于这个来写解密脚本

import gmpy2
def decry_1():
    cip=0x25f6b048b4f32e3ce9175bb64930f65101a706ae74988a4ec87b4d5ec7feb9223ab782bcf1ec9d7fee750
    n=0x28a831a5bf4b902e95318e50c2075259f91094d08d84409e1b76eadfa0865d1278acc90fa7c6cf6acb375
    e=17
    p = 555183147936225271626794036740589959032732535469347
    q = 640704384372038524783151782406101498608483916642951
    phi=(p-1)*(q-1)
    d=gmpy2.invert(e,phi)
    m=pow(cip,d,n)
    return m

m=decry_1()
print(hex(m))
#0x7be300df8b2c

所以第一层加密结果就是0x7be300df8b2c

因为还差2位,就只能看第二段加密了

第二段加密:

第二段加密对应的就是我们上面所作的csel的去混淆

先看函数

image-20250805111112906

我搞得另外一个程序分段了,从这个看逻辑,关键就在

sub_95970 和 sub_98E50这个函数,因为这两个函数有混淆(trace发现这里面有使用到输入的地方)

猜测就是sub_98E50这个函数,毕竟上面那个很像就是来读取压缩包内容的

image-20250805111650849

点进去看看

void __fastcall __noreturn sub_98E50(__int64 a1, int a2)
{
  __int64 v2; // x11
  int n1911078787; // w8
  bool v4; // zf
  int n252508079; // w9
  unsigned __int64 v6; // x9
  int n1911078787_1; // w9
  __int64 *v8; // [xsp+8h] [xbp-E8h]
  unsigned __int64 v9; // [xsp+10h] [xbp-E0h]
  char v11; // [xsp+20h] [xbp-D0h]
  unsigned __int64 v12; // [xsp+30h] [xbp-C0h]
  unsigned __int64 v13; // [xsp+38h] [xbp-B8h]
  unsigned __int64 v14; // [xsp+40h] [xbp-B0h]
  int n3; // [xsp+4Ch] [xbp-A4h]
  __int64 v16; // [xsp+58h] [xbp-98h]
  __int64 v17; // [xsp+60h] [xbp-90h]
  __int64 *v18; // [xsp+68h] [xbp-88h]
  unsigned __int64 v19; // [xsp+70h] [xbp-80h]
  __int64 v20; // [xsp+78h] [xbp-78h]
  unsigned __int64 v21; // [xsp+80h] [xbp-70h]
  unsigned __int64 v22; // [xsp+88h] [xbp-68h]
  unsigned __int64 v23; // [xsp+90h] [xbp-60h]

  v2 = a1;
  _ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
  v16 = *(a1 + 376);
  v8 = (a1 + 264);
  v9 = a2;
LABEL_39:
  v4 = v16 == 0;
  n1911078787 = -1356816398;
  n252508079 = -1966562382;
LABEL_52:
  n1911078787_1 = n252508079 - 288394458;
  if ( !v4 )
    n1911078787 = n1911078787_1;
  while ( 1 )
  {
    while ( 1 )
    {
      while ( n1911078787 < -35886379 )
      {
        if ( n1911078787 >= -1022605219 )
        {
          if ( n1911078787 >= -799327844 )
          {
            if ( n1911078787 < -738166060 )
            {
              if ( n1911078787 == -799327844 )
              {
LABEL_30:
                v11 = 0;
                v21 = v22 + 1;
                n1911078787 = -591289714;
                if ( v22 <= v9 )
                  goto LABEL_43;
              }
            }
            else
            {
              v6 = v21;
LABEL_49:
              v14 = v6;
              n3 = 3;
LABEL_7:
              n1911078787 = 1911078787;
              if ( n3 )
                n1911078787 = 1565738013;
              v12 = v14;
            }
          }
          else
          {
            n1911078787 = 951268337;
          }
        }
        else if ( n1911078787 >= -1765623770 )
        {
          if ( n1911078787 < -1356816398 )
          {
            if ( n1911078787 == -1765623770 )
            {
LABEL_22:
              *v18 = v20 + 4;
              v13 = v22;
LABEL_25:
              v23 = v13;
              n1911078787 = 828056523;
              if ( !*(v2 + 280) )
                goto LABEL_34;
            }
          }
          else
          {
            n1911078787 = -976886875;
          }
        }
        else if ( n1911078787 >= -2002346238 )
        {
          if ( n1911078787 == -2002346238 )
            goto LABEL_36;
        }
        else if ( n1911078787 == -2142698382 )
        {
          goto LABEL_7;
        }
      }
      if ( n1911078787 >= 951268337 )
        break;
      if ( n1911078787 >= 725054412 )
      {
        if ( n1911078787 >= 746343868 )
        {
          if ( n1911078787 >= 828056523 )
          {
            v6 = v23;
            goto LABEL_49;
          }
          if ( n1911078787 == 746343868 )
          {
LABEL_43:
            v13 = v21;
            n1911078787 = -738166060;
            if ( (v11 & 1) == 0 )
              goto LABEL_25;
          }
        }
        else if ( n1911078787 == 725054412 )
        {
          goto LABEL_25;
        }
      }
      else if ( n1911078787 >= 724515125 )
      {
        if ( n1911078787 == 724515125 )
          goto LABEL_39;
      }
      else if ( n1911078787 == -35886379 )
      {
        v12 = 0LL;
        *(v2 + 392) = 1;
        goto LABEL_47;
      }
    }
    if ( n1911078787 >= 1842981774 )
    {
      if ( n1911078787 >= 1911078787 )
      {
        if ( n1911078787 >= 2040010456 )
        {
          if ( n1911078787 == 2040010456 )
          {
            v17 = sub_BAE2C(
                    *(v2 + 400),
                    208LL,
                    1842981774LL,
                    1565738013LL,
                    746343868LL,
                    725054412LL,
                    724515125LL,
                    272LL);
            v2 = a1;
            v4 = v17 == 0;
            n1911078787 = -1022605219;
            n252508079 = 252508079;
            goto LABEL_52;
          }
        }
        else if ( n1911078787 == 1911078787 )
        {
LABEL_47:
          v22 = v12;
          v18 = v8;
          v19 = *v8;
          if ( *v8 != -1 )
          {
LABEL_36:
            *(v2 + 288) = 1;
            sub_E2DB4(
              *(v16 + 4 * (v19 >> 2)),
              v2,
              1842981774LL,
              1565738013LL,
              746343868LL,
              725054412LL,
              724515125LL,
              272LL);
            v20 = *v18;
            v2 = a1;
            if ( *v18 != v19 )
              goto LABEL_30;
            goto LABEL_22;
          }
          n1911078787 = 1565738013;
        }
      }
      else if ( n1911078787 == 1842981774 )
      {
LABEL_34:
        n3 = 0;
        v14 = v23;
        goto LABEL_7;
      }
    }
    else
    {
      n1911078787 = -976886875;
    }
  }
}

这个是我使用unidbg来patch的,然后会出现几个 _asm{x8 }这样的,因为我unidbg没出现,我就直接全部nop了,可能有误删的,但是程序没跑完,我电脑跑不完了,就直接先这样看

不难发现就是这两处可能是关键

   v17 = sub_BAE2C(
                    *(v2 + 400),
                    208LL,
                    1842981774LL,
                    1565738013LL,
                    746343868LL,
                    725054412LL,
                    724515125LL,
                    272LL);
                    
                    
      sub_E2DB4(
              *(v16 + 4 * (v19 >> 2)),
              v2,
              1842981774LL,
              1565738013LL,
              746343868LL,
              725054412LL,
              724515125LL,
              272LL);
            v20 = *v18;
            v2 = a1;             

直接hook一下这两个地方看看呗

我输入 0x7be37bdf8b2c 136,216,966,040,364 第二段也就是123

let cnt = 0
function encry3() {
    const baseAddr = Module.findBaseAddress('libsec2023.so');
    let tar = 0
    const offset = 0x99374;
    const func_inp = baseAddr.add(offset);
    Interceptor.attach(func_inp, {
        onEnter: function (args) {

            //x0是第一个参数(不知道什么作用),x1是第二个参数(是一个地址,里面放着输入),还用到x9(是一个地址,可能是跳转地址),x10(一个固定参数)

            var x0_val = this.context.x0;
            var x1_val = this.context.x1;
            var x1_data = Memory.readU32(x1_val)
            var x9_data = this.context.x9 - base_addr
            //var x10_val = this.context.x10
            //毕竟是vm,应该就是调用某个地址的函数,然后传参   tar_func(x0,x1)
            console.log(cnt, "\t", x9_data.toString(16), "\t", x0_val, "\t", x1_data)
            cnt += 1

        },
        onLeave: function (retval) {

        }
    });
}

得到:

image-20250805154139370

跳转了几个地址看了一下全是混淆,没啥逻辑,只能trace了

本来想用frida 来trace的,直接崩啦,直接上unidbg吧

我之前没用过unidbg的trace的,这次正好学习一下,感觉unidbg的trace还是挺简单的,感觉效率是真快,1g也就1.2min

package com.tengxun2023;

import com.github.unidbg.Emulator;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.listener.TraceCodeListener;
import com.github.unidbg.virtualmodule.android.AndroidModule;

import capstone.Capstone;
import capstone.api.Instruction;
import com.alibaba.fastjson.util.IOUtils;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.Backend;
import com.github.unidbg.arm.backend.CodeHook;
import com.github.unidbg.arm.backend.UnHook;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.DvmClass;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.virtualmodule.android.AndroidModule;


import keystone.Keystone;
import keystone.KeystoneArchitecture;
import keystone.KeystoneEncoded;
import keystone.KeystoneMode;
import unicorn.Arm64Const;

import javax.sound.midi.Patch;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.*;
import java.util.List;
import java.util.Stack;
import java.io.File;

public class tracer_fin_2023 {
    public final AndroidEmulator emulator;
    public final VM vm;
    public final Memory memory;
    public final Module module;

    public tracer_fin_2023(){
        emulator = AndroidEmulatorBuilder.for64Bit().build();
        memory =  emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));
        emulator.getSyscallHandler().setEnableThreadDispatcher(true);
        vm = emulator.createDalvikVM();
        new AndroidModule(emulator,vm).register(memory);

        DalvikModule dalvikModule = vm.loadLibrary(new File("unidbg-android/src/test/java/com/tengxun2023/libsec2023.so"), true);
        module = dalvikModule.getModule();
        vm.callJNI_OnLoad(emulator,module);
    }

    public  static  void main(String[] args) throws FileNotFoundException {
        tracer_fin_2023 mainActivity = new tracer_fin_2023();
        mainActivity.tracer();

    }

    public  long byteArrayToLong(byte[] byteArray) {
        ByteBuffer buffer = ByteBuffer.wrap(byteArray);
        buffer.order(ByteOrder.LITTLE_ENDIAN);  // 设置字节顺序为小端
        return buffer.getLong();
    }

    long fun_idx=0;
    public void set_hook_vm(){
        emulator.getBackend().hook_add_new(new CodeHook() {
            @Override
            public void hook(Backend backend, long address, int size, Object user) {
                if(address==0x99374+ module.base){
                    if(fun_idx>=22){
                        try{
                            System.out.printf("%d:\n",fun_idx);
                            Thread.sleep(1);
                        }catch (InterruptedException e){
                            e.printStackTrace();
                        }
                    }
                    fun_idx+=1;
                }

            }

            @Override
            public void onAttach(UnHook unHook) {

            }

            @Override
            public void detach() {

            }
        },0x000000000099370 + module.base,0x00000000009937C + module.base,null);
    }


    public byte[] mapfun =new byte[0x140000];

    private void tracer() throws FileNotFoundException{
        emulator.traceWrite(0x404f2010L+(0xe<<3),0x404f2010L+(0xe<<3)+0x4);
        emulator.traceRead(0x404f2010L+(0xe<<3),0x404f2010L+(0xe<<3)+0x4);
        set_hook();
        set_hook_vm();
        padding_reg2val(reg2val);

        long [][]hook_list= {
                { 0xcf004,0xcfac0 },
                { 0xcfac4,0xcfddc },
                { 0xd0b2c,0xd10a0 },
                { 0xd1ae8,0xd1cbc },
                { 0xd1cc0,0xd241c },
                { 0xd2420,0xd27bc },
                { 0xd2ad0,0xd31d0 },
                { 0xd5518,0xd5a74 },
                { 0xd5a78,0xd5d8c },
                { 0xd6158,0xd6df8 },
                { 0xd6e00,0xd7ac4 },
                { 0xd7f4c,0xd8b60 },
                { 0xd8b64,0xd8e58 },
                { 0xdf458,0xdf758 },
                { 0xdf75c,0xdfa68 },
                { 0xdf75c,0xdfa68 },
                { 0xe0be8,0xe1144 },
                { 0xe1148,0xe1454 },
                { 0xe227c,0xe2db0 },
                { 0xe2db4,0xe39fc },
                { 0xe3e18,0xe497c },
                { 0xe4980,0xe4c4c },
                { 0xe4c50,0xe58a4 },
                { 0xe6bf4,0xe76d8 },
                { 0xe8054,0xe8854 },
                { 0xe9564,0xe98b0 },
                { 0xd0a88, 0xD0B28},
                { 0xe1468, 0xE14DC}
        };
        for(int i=0;i<hook_list.length;i++){
            long st=hook_list[i][0];
            mapfun[(int) st]=1;
        }

        traceStream =  new PrintStream(new FileOutputStream(traceFile), true);

        module.callFunction(emulator,0x94368,0x123412341234L);

    }

    String traceFile="unidbg-android/src/test/java/com/tengxun2023/all_trace.txt";
    PrintStream  traceStream = null;
    public HashMap<String,Integer> reg2val=new HashMap<>();
    public  long read_reg_by_str(Backend backend,String reg){
        long true_reg_val=-1;
        if(reg2val.containsKey(reg)){
            true_reg_val=backend.reg_read(reg2val.get(reg)).longValue();
        }
        else if(reg.equals("xzr")){
            true_reg_val=0;
        }else{
            assert false;
        }
        return true_reg_val;
    }

    public static void padding_reg2val(HashMap<String,Integer> reg2val){
        reg2val.put("x0", Arm64Const.UC_ARM64_REG_X0);
        reg2val.put("x1",Arm64Const.UC_ARM64_REG_X1);
        reg2val.put("x2",Arm64Const.UC_ARM64_REG_X2);
        reg2val.put("x3",Arm64Const.UC_ARM64_REG_X3);
        reg2val.put("x4",Arm64Const.UC_ARM64_REG_X4);
        reg2val.put("x5",Arm64Const.UC_ARM64_REG_X5);
        reg2val.put("x6",Arm64Const.UC_ARM64_REG_X6);
        reg2val.put("x7",Arm64Const.UC_ARM64_REG_X7);
        reg2val.put("x8",Arm64Const.UC_ARM64_REG_X8);
        reg2val.put("x9",Arm64Const.UC_ARM64_REG_X9);
        reg2val.put("x10",Arm64Const.UC_ARM64_REG_X10);
        reg2val.put("x11",Arm64Const.UC_ARM64_REG_X11);
        reg2val.put("x12",Arm64Const.UC_ARM64_REG_X12);
        reg2val.put("x13",Arm64Const.UC_ARM64_REG_X13);
        reg2val.put("x14",Arm64Const.UC_ARM64_REG_X14);
        reg2val.put("x15",Arm64Const.UC_ARM64_REG_X15);
        reg2val.put("x16",Arm64Const.UC_ARM64_REG_X16);
        reg2val.put("x17",Arm64Const.UC_ARM64_REG_X17);
        reg2val.put("x18",Arm64Const.UC_ARM64_REG_X18);
        reg2val.put("x19",Arm64Const.UC_ARM64_REG_X19);
        reg2val.put("x20",Arm64Const.UC_ARM64_REG_X20);
        reg2val.put("x21",Arm64Const.UC_ARM64_REG_X21);
        reg2val.put("x22",Arm64Const.UC_ARM64_REG_X22);
        reg2val.put("x23",Arm64Const.UC_ARM64_REG_X23);
        reg2val.put("x24",Arm64Const.UC_ARM64_REG_X24);
        reg2val.put("x25",Arm64Const.UC_ARM64_REG_X25);
        reg2val.put("x26",Arm64Const.UC_ARM64_REG_X26);
        reg2val.put("x27",Arm64Const.UC_ARM64_REG_X27);
        reg2val.put("x28",Arm64Const.UC_ARM64_REG_X28);
        reg2val.put("x29",Arm64Const.UC_ARM64_REG_FP);
        reg2val.put("x30",Arm64Const.UC_ARM64_REG_LR);
        reg2val.put("sp",Arm64Const.UC_ARM64_REG_SP);
        reg2val.put("pc",Arm64Const.UC_ARM64_REG_PC);

    }

    public void set_hook(){
        emulator.getBackend().hook_add_new(new CodeHook() {
            @Override
            public void hook(Backend backend, long address, int size, Object user) {
                if (address==0x94440+module.base){
                    backend.reg_write(Arm64Const.UC_ARM64_REG_X0,0);
                    //从这里开始trace代码痕迹
                    emulator.traceCode(module.base, 0x40150000, new TraceCodeListener() {
                        @Override
                        public void onInstruction(Emulator<?> emulator, long address, Instruction insn) {
                            if(mapfun[(int) address -(int) module.base]==1){
                                traceStream.printf("\n=================================================================\n");
                                traceStream.printf("\n\n\n%d\tcall [0x%x]\n\n\n",fun_idx,address- module.base);
                                traceStream.printf("=================================================================\n");
                            }
                        }
                    }).setRedirect(traceStream);
                }
            }

            @Override
            public void onAttach(UnHook unHook) {

            }

            @Override
            public void detach() {

            }
        },0x9443c+ module.base,0x94444+ module.base,null);
    }


}

这个跑上2.3min就可以拿到1g的trace记录了

通过这次的trace,学会了在对这种平坦化的代码进行算法还原的时候,一定要当前的值往后先翻一翻,记录下来他的出现的地方,要不然走到后面都不知道哪里会再次调用了,容易搞乱之间的顺序

下面是我的算法还原记录:

[19:05:42 782][libsec2023.so 0x0e1318] [007c4092] 0x120e1318: "and x0, x0, #0xffffffff" x0=0x114514 => x0=0x114514

[19:05:42 784][libsec2023.so 0x0eaa24] [0f20c29a] 0x120eaa24: "lsl x15, x0, x2" x0=0x114514 x2=0x0 => x15=0x114514

[19:05:42 784][libsec2023.so 0x0eaa2c] [0f24c29a] 0x120eaa2c: "lsr x15, x0, x2" x0=0x114514 x2=0x0 => x15=0x114514

[19:05:43 714][libsec2023.so 0x0e1348] [0900142a] 0x120e1348: "orr w9, w0, w20" w0=0x114514 w20=0x0 => w9=0x114514


def enc1:
	#对每个字节循环执行下面的操作
	inp=0x114514
	inp&=0xffffffff
	inp<<0
	inp>>0
	inp | 0


	inp=0x114514
	inp&=0xffffffff
	inp<<0
	inp>>0
	inp | 0


	inp=0x114514
	inp&=0xffffffff
	inp<<0
	inp>>0
	inp | 0


	inp=0x114514
	inp&=0xffffffff
	inp1=inp>>13 	0x2 
	inp2=inp<<0xd  	0x228a28000
	tmp1= inp1 | inp2  0x228a28002  
	tmp1>>=0x1f		
	tmp1&=0x1


	inp=114514
	inp&=0xffffffff
	inp1=inp<<0x5  	0x228a280	
	inp2=inp>>0x5 	0x8a28 			后面还有对这个的操作,大概再689747行也就是0xeb074处,579250处也再次调用
	op5_tmp1=inp1 | 0x2  		0x228a282 

		下面开始对  	0x228a282这个值操作
=====================================================================
	op5_tmp1&=0xffffffff
	op6_1 = op5_tmp1 << 0x10  	0x228a2820000
	op6_2 = op5_tmp1 >> 0x10 	x15=0x228    
	op6_2&=0xffffffff


	op7_1=op6_2 ^ 0x10 		0x238 		后面有用到,大概在565537


	op8_1=op7_1 &0xff 		0x38


	op9_1=op8_1 &0xffffffff 	0x38
	op9_2=op9_1 >> 0x1e  		0x0
	op9_3=op9_1 << 0x2 			0xe0
	op9_4=op9_2 | op9_3 		0xe0


	op10_1=op9_4 &0xfffffffc 	0xe0
	op10_2=op10_1 & 0x3fc000003  	0xe0000000
	op_10_4=op7_1 & 0xffffffff
	op10_5=op10_4>>0x6 	0x8
	op10_6=op10_4<<0x1a 	0x8e0000000
	op10_7=op10_5 | op10_6 	0x8e0000008
	op10_8=op10_7 & 0x3fc000003  0xe0000000
	op10_9=op10_8 | op10_2        0xe00000e0 那看来上面的那个或其实是这里的
	op10_10=op10_9 & 0x3 			0x0
	op10_11=op10_10 &0xfffffffc    0xe0
	op10_12=op10 | op10_11  	  	0xe0


	跳到566698  0x0cfcbc

	op11_1=op10_12 &0xffffffff   0xe0
	op11_2=op11_1 <<0   0xe0
	op11_3=op11_1>>0    0xe0
	op11_4=op11_2 +0x6b     0x14b
	op11_5=op11_4+0x0       0x14b
	op11_6=op11_3 | 0xffffffff00000000  0xe0
	op11_7=op11_6+0x6b     0x14b
	tmp1>>=0x1f		
	tmp1&=0x1
		上面以及下面两句好像不是操作
	op11_7>0x20
	op11_7&=1

	op11_7&=0xffffffff   0x14b

	0x14b好像就这样追完了
==========================================================================

	接下来有一个对上面继续的操作就是0x228a282这个她正好在0x14b结束之后
	接下来就是
	cip=0x228a282
	cip>>8
	cip<<8   	0x228a2
	and 0xffffffff
	cip^= 0x8 		0x228aa
	and 0xff 		0xaa
	tmp1=0xaa    只是为了后续的表示,没有这一句
	然后就是同步63行开始的代码,trace的代码在597849处继续
	and 0xffffffff
	op1_1=tmp >>0x1e 		0x0
	op1_2=tmp <<0x2   	0x2a8
	op1_3=op1_1 | op1_2
	

	上面这两句可能还是没有作用
	op1_4=op3&0xfffffffc
	op1_5=op1_4 &0x303fffffc  0x2a8    后续还有一处对0x2a8的或在627788行
	op1_6=op1_5 & 0x3fc000003  0x2a8000002    
	op1_7=op1_6 |   op1_5    0x2a80002aa
	op1_8=op1_7 & 0x3 	0x2
	op1_9=op1_8 | op1_5   0x2aa


	op2_1=op1_9<<0
	op2_2=op1_9>>0  0x2aa 		后续还会使用应该,记住
	op2_3=op2_2+0xa2   0x34c  这里肯定有调用
	op2_4=op2_3+0x0  	0x34c
	op2_5= op2_4 | 0xffffffff00000000 0x0xffffffff000002aa
	op2_6=op2_5+0xa2   	0x34c  再一次相加
	and 0xffffffff

	0x34c 最终也追完了,结束在629234行的地方

======================================================================
	继续搜 0x228a282 看看是不是还有一次,找到了 在641237行加载
	and 0xffffffff
	<<0 			0x228a282
	>>0 			0x228a282
	and 0xffffffff   0x228a282
	xor 0x0  		0x228a282    后续还有在使用这个值在689747行
	and 0xff  		0x82       
	tmp2=0x82

	op1_1=tmp2 &0xffffffff  0x82
	op1_2=op1_1 >>0x1e   0x0
	op1_3=op1_1 <<0x2   0x208
	op1_4=op1_2 | op1_3   0x208

	op2_1=op1_4 & 0xfffffffc  0x208
	op2_2=op2_1 & 0x303fffffc 0x208   后续还会使用,
	op2_3=op2_1 & 0x3fc000003 0x208000002
	op2_4=OP2_2 | op2_3   0x20800020a
	op2_5=op2_4 & 0x3     0x2
	op2_6=op2_5 | op2_1    0x20a

	op3_1=op2_6 &0xffffffff
	op3_2=op3_1 <<0 	0x20a
	op3_3=op3_1 >> 0   0x20a
	op3_4=op3_1 +0x16  0x220    这里肯定也会调用

	op4_1=op3_4 +0x0 0x220

	>>0x20
	&1 
	这两句我没看懂

	op5_1 &0xffffffff
	op5_2 =op5_1 | 0xffffffff00000000  	0xffffffff00000220
	得到结果220

====================================================================

key=[0x6b , 0xa2 , 0x16]
没啥思路了,上面循环得到三个值   0x14b  0x34c   0x220
0x14b:572192结束
0x34c:634230结束
0x220:这个倒是很多,但是很多都是取值赋值操作,没有什么加密的痕迹,应该在别的地方
搜索0x228a282也没得到什么信息在689808截至
看来第一段应该是一个循环,然后有三个小循环,
尝试搜索这三个值&0xff的值
就是  0x4b   0x4c  0x20 
0x4b:795280行
0x4c:750538行
0x20:这个太多了,估计在70w行以后

我从上往下翻,翻到了  56	call [0xe8054]  估计应该块开始下一次了691784行
从这里看上面三个值哪个近


直接看0x4c了

==============================================================================
第二段加密
==============================================================================
[19:05:51 400][libsec2023.so 0x0d8e00] [011c4092] 0x120d8e00: "and x1, x0, #0xff" x0=0x4c => x1=0x4c

def enc2:
	73804行有0x20
	cip=0x20
	cip&0xff

	op2_1=cip>>0x1d 		0x0 
	op2_2=cip<<0x3 			0x100
	op2_3=op2_1 | op2_2 	0x100

	op3_1=op2_3 & 0x7f8 	
	op3_2=op3_1 | 0x1 		0x101        
	op3_3=op3_2 & 0x7ff
	op3_4=op3_3 | 0x0  		0x101
	op3_5=op3_4 ^ 0xa2 		0x117






	cip=0x4c  		750538行
	cip & 0xff 

	op1_1=cip>>5 		0x2
	op1_2=cip<<0x1b   	0x260000000
	op1_3=op1_1 | op1_2   	0x260000002
	上面这个应该后续没啥用了,很像上面第一次加密的
	直接跳到了783246行

	op2_1=cip>>0x1d 		0x0
	op2_2=cip<<0x3 			0x260
	op2_3=op2_1 | op2_2 	0x260  后续的使用应该是这个了,0x4c很长都不会使用

	op3_1=op2_3 & 0x7f8 	0x260
	op3_2=op3_1 | 0x2       0x262  
	op3_3=op3_2 & 0x7ff
	op3_4=op3_3 | 0x0  		0x262
	op3_5=op3_4 ^ 0xa2 		0x2c0

	   后面就没了,看一下0x4b吧
=============================================================================
	很像上面的操作
	cip2=0x4b
	cip2&=0xff

	op1_1=cip2>>5   0x2
	op1_2=cip2<<0x1b     0x258000000
	op1_3=op1_1 | op1_2 		0x258000002

	op2_1=cip2>>0x1d 		0x0
	op2_2=cip2<<0x3 		0x258
	op2_3=op2_1 | op2_2 	0x258

	op3_1=op2_3 & 0x7f8     0x258
	op3_2=op3_1 | 0x2 		0x25a
	op3_3=op3_2 & 0x7ff 
	op3_4=op3_3 | 0x0  		0x25a
	op3_5=op3_4 ^ 0x6b 		0x231

看着像倒序
def enc2(inp):
	key=[0x6b , 0xa2 , 0x16]
	tmp2=[0]*3
	for i in range(2,-1,-1):
		tmp2[i]=inp[i]<<3 | inp[i]>>5
		tmp2[i]^=key[i]
		tmp2[i]&0xff

得到结果:  [0x17,0xc0,0x31]

0x31 0xc0 0x17



================================================================================
加密三
============================================================================
840333行
	0x31+0x75 	0xa6
858407行
	0xc0^0xfe    0x3e 	98w行以后可能会调用
876979行
	0x0xffa63e17+0xc1 	 x10=0xffa63ed8   
	这里竟然不是单纯的0x17,抽象,刚开始搜0x17没搜到,搜的17 才搜到

得到结果:
   [a6,3e,d8]




============================================================================
加密四:
============================================================================
923319行搜到0xa6
	0xa6 &0xff
	inc1=0xa6

	op1_1=inc1>>0x1f 	0x0
	op1_2=inc1<<0x1 	0x14c
	op1_3=op1_1 | op1_2  0x14c
	op1_4=op1_3 | 0x0 	0x14c

	op2_1=op1_4 &0xfffffffe 0x14c
	op2_2=op2_1 & 0x101fffffe 	0x14c
	有一个不知道怎么回事的值
[19:05:55 122][libsec2023.so 0x0d2654] [080114aa] 0x120d2654: "orr x8, x8, x20" x8=0x14c000001 x20=0x14c => x8=0x14c00014d
[19:05:55 122][libsec2023.so 0x0d2658] [2b7d40d2] 0x120d2658: "eor x11, x9, #0xffffffff" x9=0x1 => x11=0xfffffffe
[19:05:55 122][libsec2023.so 0x0d265c] [6b01188a] 0x120d265c: "and x11, x11, x24" x11=0xfffffffe x24=0x14c => x11=0x14c
	难搞

	op3_1=op2_2 | 0x1 		0x14d    0x1不知道怎么来的
	op3_2=op3_1 ^ 0x2 		0x14f    0x2不知道怎么来的·
	op3_3=op3_2&0xffffffff
	op3_4=op3_3>>0x10
	op3_5=op3_3<<0x10
	op3_6=op3_4 | op3_5 	0x14f0000
	op3_7=op3_6 & 0x0xff0000   0x4f0000 这个不知道是什么

------------------------------------------------------------------------

1003977行处
	cip=0x3e 			
	cip&0xffffffff

	op1_1=cip >> 0x7    0x0 		100w处还有一次调用
	op1_2=cip << 0x19   0x7c000000
	op1_3=op1_1 | op1_2 	0x7c000000
	op1_4=cip>>0x1f 	0x0
	op1_5=cip<<0x1 		0x7c
	op1_6=op1_5 | op1_4  0x7c

	op2_1=op1_6 & 0x1fe 	0x7c
	op2_2 =op2_1 | 0x0      0x7c

	op2_3=op2_2 &0x1f 		0x7c
	op2_4=op2_3 | 0x0 	0x7c
	op2_5=op2_4 ^ 0x1    0x7d   	0x7c后续没啥操作了

	op3_1=op2_5 <<0x8    0x7d00
	op3_2 =op2_5 >> 0x8  0x0
	0x7d00也没啥操作了后续,而且7d00不像是有什么操作的样子

结束在1038429处

-----------------------------------------------------------------------
在10659558处开始操作
	cip=0xd8
	cip&0xffffffff

	op1_1=cip>>0x7 		0x1
	op1_2=cip<<0x19     0x1b0000000
	op1_3=op1_1 | op1_2 	0x1b0000001   没有后续操作1

在1081077行处
	op2_1=cip>>0x1f  	0x0
	op2_2=cip<<0x1       0x1b0
	op2_3=op2_1 | op2_2    0x1b0

	后续没有对0xd8的操作l,就是看上面这两处位移运算怎么来了

	op3_1=op2_3 & 0x1fe   0x1b0
	op3_2 =op3_1 | 0x1     0x1b1
	op3_3=op3_2 &0x1ff     0x1b1
	op3_4=op3_3 | 0x0   	0x1b1
	op3_5 =op3_4 ^0x0    0x1b1

	op3_6=op3_5 &0xff 	0xb1

	op4_1=op3_6 <<0
	op4_2=op3_6<<0

	&0xffffffff
	后续也没啥操作了

----------------------------------------------------------------
得到
应该是得到   [0x4f0000  , 0x7d00  0xb1  ]

追踪这些数据在1042512行处得到:
[19:05:57 001][libsec2023.so 0x0e9fc0] [8a02088b] 0x120e9fc0: "add x10, x20, x8" x20=0x4f0000 x8=0x7d00 => x10=0x4f7d00
相加了
[19:05:58 531][libsec2023.so 0x0e9fc0] [8a02088b] 0x120e9fc0: "add x10, x20, x8" x20=0x4f7d00 x8=0xb1 => x10=0x4f7db1
在1104537处再次相加

追踪这个值,发现他又开始左移5,右移5,就是第一次加密的算法


得到的加密代码:

def encry2(enc):
    for i in range(256):
        enc&=0xffffffff
        enc1=enc<<5 | enc>>0x13
        key=[0x6b , 0xa2 , 0x16]
        fin1=[0]*3
        # print("==========")
        #第一次加密,是一个三层循环
        for i in range(3):
            t=16-8*i
            tmp1=((enc1>>t) ^ t)&0xff
            tmp2=(tmp1<<0x2) &0xfffffffc
            tmp3=((tmp1 >>0x6) | (tmp1 << 0x1a))
            tmp4=(((tmp3 & 0x3fc000003) | tmp2) & 0x3) | tmp2
            tmp4+=key[i]
            tmp4&=0xff
            fin1[i]=tmp4
        # print("===第一段加密结果===")
        # for i in range(3):
        #     print(hex(fin1[i]),end=',')
        # print()
        #第二段加密
        fin2=[0]*3
        for i in range(2,-1,-1):
            tmp1=(fin1[i]<<3 | fin1[i]>>5)
            fin2[i]=tmp1^key[i]
            fin2[i]&=0xff
        # print("===第二段加密结果====")
        # for i in range(3):
        #     print(hex(fin2[i]),end=',')
        # print()
        fin3=[0]*3
        fin3[0]=(fin2[0]+0x75)&0xff
        fin3[1]=(fin2[1]^0xfe)&0xff
        fin3[2]=(fin2[2]+0xc1)&0xff
        # print("===第三段加密结果====")
        # for i in range(3):
        #     print(hex(fin3[i]), end=',')
        # print()
        fin4=[0]*3
        for i in range(3):
            t=16-8*i
            tmp1=fin3[i]>>7 | fin3[i]<<1
            tmp2=tmp1^(2-i)
            tmp2&=0xff
            tmp3=tmp2<<t
            fin4[i]=tmp3
        # print("===第四段加密结果====")
        # for i in range(3):
        #     print(hex(fin4[i]), end=',')
        # print()
        enc=sum(fin4)
    return enc

解密代码:

def decry2(token):
    token &= 0xffffffff
    key = [0x6b, 0xa2, 0x16]
    for i in range(256):
        enc1=token
        cip1=[0]*3
        for i in range(3):
            t=16-8*i
            tmp1=enc1>>t
            tmp1&=0xff
            tmp2=tmp1^(2-i)
            tmp3=(tmp2<<7) | (tmp2>>1)
            cip1[i]=tmp3&0xff

        cip1[0]=(cip1[0]-0x75)&0xff
        cip1[1]=(cip1[1]^0xfe)&0xff
        cip1[2]=(cip1[2]-0xc1)&0xff
        cip2=[0]*3
        for i in range(2,-1,-1):
            cip1[i]^=key[i]
            tmp1=cip1[i]>>3 | cip1[i]<<5
            tmp1&=0xff
            cip2[i]=tmp1

        cip3=[0]*3
        for i in range(3):
            t=16-8*i
            cip2[i]&=0xff
            tmp1=cip2[i]-key[i]
            tmp2=(tmp1&0xff)>>2 | (tmp1&0xff)<<6
            tmp3=tmp2^t
            tmp3&=0xff
            tmp4=tmp3<<t
            cip3[i]=tmp4

        tmp=sum(cip3)
        tmp&=0xffffffff
        token=tmp<<0x13 | tmp >>0x5
        token&=0xffffff


    a = 0x7BE300DF8B2C
    token_part=(token&0xffff00)<<40 | (token&0xff)<<24
    ans = token_part | a
    print(hex(ans))
    print("解密结果为",ans)
    return ans

token=3153664
decry2(token)

早知道使用C来写解密代码了,移位运算真的难搞

这道题目做了好久,也在做这个题目的时候不断学习着新的东西,这道题应该是我收获最大的一道题目了,学到的东西也是最多的。从反调试到去混淆,unidbg,idapython以及最后的算法还原都收获颇多,也是多感谢师哥以及其他大佬的文章在做题的过程中对我的帮助。

参考:

https://www.cnblogs.com/lordtianqiyi/p/18745518

https://python.docs.hex-rays.com/namespaceida__idaapi.html

https://oacia.dev/unidbg-anti-br/

https://bbs.kanxue.com/thread-276956.htm

https://bbs.kanxue.com/thread-279011.htm

posted @ 2025-08-06 20:17  xiaowaaa  阅读(38)  评论(0)    收藏  举报