首页
社区
课程
招聘
[原创]2023年腾讯游戏安全竞赛安卓决赛题解复现
发表于: 2025-8-6 21:28 874

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

2025-8-6 21:28
874

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

flag获取

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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的分析,就是一些

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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的,然后就发现他的跳转了
图片描述

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
            // }));
        },
    })
}

图片描述

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

libsec2023.so分析:

图片描述

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

反调试分析:

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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这个地方看看哪些值不变就修改哪里

图片描述

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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()

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

图片描述

混淆处理:

BR跳转混淆

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

图片描述

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

图片描述

类似这种

图片描述

手动patch就行

图片描述

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

不透明谓词混淆

图片描述

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

图片描述

ida统一修改如下:

idapthon参看105K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6H3P5i4c8Z5L8$3&6Q4x3X3g2V1L8$3y4K6i4K6u0W2K9r3g2^5i4K6u0V1M7X3q4&6M7#2)9J5k6h3y4G2L8g2)9J5c8X3&6S2L8h3g2K6M7r3q4U0k6h3W2V1j5g2)9#2k6W2)9#2k6X3W2V1j5h3q4H3K9g2)9J5k6h3S2@1L8h3H3`.

1
2
3
4
5
6
7
8
9
10
11
# 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")

图片描述

CSEL+BR混淆

但是看一下内部函数

图片描述

图片描述

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

trace代码:
frida_trace

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

图片描述

不过第一次而言,对于我分析第一次加密足够了,就是大量的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拿到不能正常看的地址,也就是混淆函数的筛选

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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=",")

图片描述

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
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的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
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脚本匹配一下去除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#使用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混淆以及不透明谓词混淆吧,反正挺清晰的应该

图片描述

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

图片描述

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// 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的时候看的流程,没啥用,其实也能看到关键逻辑

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

直接frida trace或者frida hook就行

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
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
            }));
        }
    });
}

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

图片描述

可以看到运行了多少次

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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的去混淆

先看函数

图片描述

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

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

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

图片描述

点进去看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
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了,可能有误删的,但是程序没跑完,我电脑跑不完了,就直接先这样看

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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) {
 
        }
    });
}

得到:

图片描述

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
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,学会了在对这种平坦化的代码进行算法还原的时候,一定要当前的值往后先翻一翻,记录下来他的出现的地方,要不然走到后面都不知道哪里会再次调用了,容易搞乱之间的顺序

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
[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,就是第一次加密的算法

得到的加密代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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

解密代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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以及最后的算法还原都收获颇多,也是多感谢师哥以及其他大佬的文章在做题的过程中对我的帮助。

参考:

2023 腾讯游戏安全 mobile 决赛 wp

IDApython文档

dexguard反混淆-unidbg去除BR寄存器跳转

[原创]2023腾讯游戏安全竞赛决赛题解(安卓)

[原创] 2023腾讯游戏安全大赛-安卓赛道决赛"TVM"分析与还原

[原创]记一次基于unidbg模拟执行的去除ollvm混淆


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

最后于 2025-8-6 21:37 被xiaowaaa编辑 ,原因: 图片没上传上
收藏
免费 2
支持
分享
最新回复 (2)
雪    币: 7
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2025-8-7 11:18
0
雪    币: 204
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2025-8-7 14:33
0
游客
登录 | 注册 方可回帖
返回