首页
社区
课程
招聘
[原创]一种通过“快照”的方式绕过So初始化时的检测
2022-8-26 17:51 25799

[原创]一种通过“快照”的方式绕过So初始化时的检测

2022-8-26 17:51
25799

说明

本文的思路是很久以前就有的,但是找不到一个合适的工具“拍摄快照”一直都没有成功实现,直到看到这篇文章才成功实现,感谢该文作者seeflower。如果原作者认为本文章不合适,请联系我删除,谢谢啦~

背景

每次在虚拟机中打开ida,逆向一堆JniOnLoad或者.iniarray段被混淆的代码时。我都想着有没有一种方法,免去这些初始化流程(当然这些初始化操作是厂家的重点防护对象)。

 

有一天,工作完之后,手头上的jnionload还没有分析完,将VMware中运行的机器设置暂停,明天再分析。突然我想到,既然VMware能通过快照这种方式保存虚拟机的运行时状态,即使关机也可以恢复。那我能不能通过对Android APP”拍摄“一份”快照“(SnapShot),正好停在我想逆向的函数前,修改入参,然后反复运行这份快照,绕过初始化过程、绕过各种检测呢?

 

思路有了,接下来就是寻找工具,怎么去实现了。

工具

  1. 需要虚拟机载体:Unidbg似乎是一个好的选择
  2. 需要快照:Android APP运行时的内存dump
  3. 需要相机:lldb 注:其实没看到seelflower的文章时,相机一开始选择的是frida 去dump内存,但是研究了一段时间之后,发现frida有太多不足了,首先无法获取特殊状态寄存器的值,没有断点机制(frida运行时似乎是单线程),通过mprotct修改内存Readable容易发生crash。(也有可能是我能力不够,frida脚本放在附件中,可以尝试用frida实现,最大的难点是无法实现断点,中断app状态)

怎么拍

为了能让Unidbg成功载入快照,拍摄内容:

  1. App运行内存
  2. 内存段的地址、权限信息
  3. 断点中断时的寄存器信息
  4. SO符号表(包括中断时地址)

有了目标,现在开始实现。

 

​ 第一步:装载lldb(相机)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#获取lldb android 服务端
$NDK_PATH/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang/9.0.8/lib/linux/aarch64/lldb-server
 
#ADB导入设备、赋予权限
adb push lldb-server /data/local/tmp/
adb shell
cd /data/local/tmp
chmod 755 lldb-server
 
#开启服务
./lldb-server p --server --listen unix-abstract:///data/local/tmp/debug.sock
 
#pc端连接android
$ lldb
 platform select remote-android
 platform connect unix-abstract-connect:///data/local/tmp/debug.sock

第二步:编写dump脚本

 

​ 内存段(Segment)、寄存器(Register)的lldb Dump脚本

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
from asyncio.log import logger
from datetime import datetime
import hashlib
import json
from pathlib import Path
from typing import TYPE_CHECKING, Dict, List
import sys
import zlib
import os
 
if TYPE_CHECKING:
    from lldb import *
 
import lldb
black_list = {
    'startswith': ['/dev', '/system/fonts', '/dmabuf'],
    'endswith': ['(deleted)', '.apk', '.odex', '.vdex', '.dex', '.jar', '.art', '.oat', '.art]'],
    'includes': [],
}
dump_path = Path("./dump/")
 
 
def __lldb_init_module(debugger: 'SBDebugger', internal_dict: dict):
    debugger.HandleCommand('command script add -f lldb_dumper.dumpMem dumpmem')
 
def dumpMem(debugger: 'SBDebugger', command: str, exe_ctx: 'SBExecutionContext', result: 'SBCommandReturnObject', internal_dict: dict):
    target = exe_ctx.GetTarget() # type: SBTarget
    arch_long = dump_arch_info(target)
    frame = exe_ctx.GetFrame() # type: SBFrame
    regs = dump_regs(frame)
 
    target = exe_ctx.GetTarget() # type: SBTarget
    sections = dump_memory_info(target)
 
    max_seg_size = 64 * 1024 * 1024
# dump内存
    process = exe_ctx.GetProcess() # type: SBProcess
    segments = dump_memory(process, dump_path, black_list, max_seg_size)
    (dump_path / "regs.json").write_text(json.dumps(regs))
    (dump_path / "sections.json").write_text(json.dumps(sections))
    (dump_path / "segments.json").write_text(json.dumps(segments))
 
 
def dump_arch_info(target: 'SBTarget'):
    triple = target.GetTriple()
    print(f'[dump_arch_info] triple => {triple}')
    # 'aarch64', 'unknown', 'linux', 'android'
    arch, vendor, sys, abi = triple.split('-')
    if arch == 'aarch64' or arch == 'arm64':
        return 'arm64le'
    elif arch == 'aarch64_be':
        return 'arm64be'
    elif arch == 'armeb':
        return 'armbe'
    elif arch == 'arm':
        return 'armle'
    else:
        return ''
 
def dump_regs(frame: 'SBFrame'):
    regs = {} # type: Dict[str, int]
    registers = None # type: List[SBValue]
    for registers in frame.GetRegisters():
        # - General Purpose Registers
        # - Floating Point Registers
        print(f'registers name => {registers.GetName()}')
        for register in registers:
            register_name = register.GetName()
            register.SetFormat(lldb.eFormatHex)
            register_value = register.GetValue()
            regs[register_name] = register_value
    print(f'regs => {json.dumps(regs, ensure_ascii=False, indent=4)}')
    return regs
 
 
def dump_memory_info(target: 'SBTarget'):
    logger.debug('start dump_memory_info')
    sections = []
    # 先查找全部分段信息
    for module in target.module_iter():
        module: SBModule
        for section in module.section_iter():
            section: SBSection
            module_name = module.file.GetFilename()
            start, end, size, name = get_section_info(target, section)
            section_info = {
                'module': module_name,
                'start': start,
                'end': end,
                'size': size,
                'name': name,
            }
            # size 好像有负数的情况 不知道是什么情况
            print(f'Appending: {name}')
            sections.append(section_info)
    return sections
 
def get_section_info(tar, sec):
    name = sec.name if sec.name is not None else ""
    if sec.GetParent().name is not None:
        name = sec.GetParent().name + "." + sec.name
    module_name = sec.addr.module.file.GetFilename()
    module_name = module_name if module_name is not None else ""
    long_name = module_name + "." + name
    return sec.GetLoadAddress(tar), (sec.GetLoadAddress(tar) + sec.size), sec.size, long_name
 
def dump_memory(process: 'SBProcess', dump_path: Path, black_list: Dict[str, List[str]], max_seg_size: int):
    logger.debug('start dump memory')
    memory_list = []
    mem_info = lldb.SBMemoryRegionInfo()
    start_addr = -1
    next_region_addr = 0
    while next_region_addr > start_addr:
        # 从内存起始位置开始获取内存信息
        err = process.GetMemoryRegionInfo(next_region_addr, mem_info) # type: SBError
        if not err.success:
            logger.warning(f'GetMemoryRegionInfo failed, {err}, break')
            break
        # 获取当前位置的结尾地址
        next_region_addr = mem_info.GetRegionEnd()
        # 如果超出上限 结束遍历
        if next_region_addr >= sys.maxsize:
            logger.info(f'next_region_addr:0x{next_region_addr:x} >= sys.maxsize, break')
            break
        # 获取当前这块内存的起始地址和结尾地址
        start = mem_info.GetRegionBase()
        end = mem_info.GetRegionEnd()
        # 很多内存块没有名字 预设一个
        region_name = 'UNKNOWN'
        # 记录分配了的内存
        if mem_info.IsMapped():
            name = mem_info.GetName()
            if name is None:
                name = ''
            mem_info_obj = {
                'start': start,
                'end': end,
                'name': name,
                'permissions': {
                    'r': mem_info.IsReadable(),
                    'w': mem_info.IsWritable(),
                    'x': mem_info.IsExecutable(),
                },
                'content_file': '',
            }
            memory_list.append(mem_info_obj)
    # 开始正式dump
    for seg_info in memory_list:
        try:
            start_addr = seg_info['start'] # type: int
            end_addr = seg_info['end'] # type: int
            region_name = seg_info['name'] # type: str
            permissions = seg_info['permissions'] # type: Dict[str, bool]
 
            # 跳过不可读 之后考虑下是不是能修改权限再读
            if seg_info['permissions']['r'] is False:
                logger.warning(f'Skip dump {region_name} permissions => {permissions}')
                continue
 
            # 超过预设大小的 跳过dump
            predicted_size = end_addr - start_addr
            if predicted_size > max_seg_size:
                logger.warning(f'Skip dump {region_name} size:0x{predicted_size:x}')
                continue
 
            skip_dump = False
 
            for rule in black_list['startswith']:
                if region_name.startswith(rule):
                    skip_dump = True
                    logger.warning(f'Skip dump {region_name} hit startswith rule:{rule}')
            if skip_dump: continue
 
            for rule in black_list['endswith']:
                if region_name.endswith(rule):
                    skip_dump = True
                    logger.warning(f'Skip dump {region_name} hit endswith rule:{rule}')
            if skip_dump: continue
 
            for rule in black_list['includes']:
                if rule in region_name:
                    skip_dump = True
                    logger.warning(f'Skip dump {region_name} hit includes rule:{rule}')
            if skip_dump: continue
 
            # 开始读取内存
            ts = datetime.now()
            err = lldb.SBError()
            seg_content = process.ReadMemory(start_addr, predicted_size, err)
            tm = (datetime.now() - ts).total_seconds()
            # 读取成功的才写入本地文件 并计算md5
            # 内存里面可能很多地方是0 所以压缩写入文件 减少占用
            if seg_content is None:
                logger.debug(f'Segment empty: @0x{start_addr:016x} {region_name} => {err}')
            else:
                logger.info(f'Dumping @0x{start_addr:016x} {tm:.2f}s size:0x{len(seg_content):x}: {region_name} {permissions}')
                compressed_seg_content = zlib.compress(seg_content)
                md5_sum = hashlib.md5(compressed_seg_content).hexdigest() + '.bin'
                seg_info['content_file'] = md5_sum
                (dump_path / md5_sum).write_bytes(compressed_seg_content)
        except Exception as e:
            # 这里好像不会出现异常 因为前面有 SBError 处理了 不过还是保留
            logger.error(f'Exception reading segment {region_name}', exc_info=e)
 
    return memory_list

​ 符号表(SymTab)的lldb Python 脚本:

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
from audioop import add
import json
from pathlib import Path
from typing import TYPE_CHECKING
 
 
if TYPE_CHECKING:
    from lldb import *
 
import lldb
symbolList = []
 
def __lldb_init_module(debugger: 'SBDebugger', internal_dict: dict):
    debugger.HandleCommand('command script add -f lldb_symb.dumpsym dumpsym')
 
def dumpsym(debugger: 'SBDebugger', command: str, exe_ctx: 'SBExecutionContext', result: 'SBCommandReturnObject', internal_dict: dict):
    target = exe_ctx.GetTarget()
    m = target.modules
 
    for module in m:
        module:SBModule
        module_name = module.file.GetFilename()
        if command in module_name:
            symbols_array = module.get_symbols_array()
 
            for symbol in symbols_array:
 
                symbolList.append(symInfo(symbol,target))
            print(json.dumps(symbolList))
            dump_path = Path("./")
            f_name = command+"_sym.json"
            (dump_path / f_name).write_text(json.dumps(symbolList))
 
def symInfo(symbol,target):
    return {"name":symbol.name,"addr":symbol.addr.GetLoadAddress(target)}

​ 第三步:按下快门

 

​ 将脚本导入到lldb:

 

​ command script import lldb_dumper.py

 

​ command script import lldb_symb.py

 

​ 附加到目标so:

1
2
3
4
#env: lldb
#附加so
attach ${pid}
br set -s ${so_name} -n ${函数名} # 或者使用 -a ${相对偏移}

​ 触发断点以后,通过以下命令得到快照文件:

1
2
dumpsym  ${so_name}#dump符号表
dumpmem  #dump内存信息

生成的文件有:

 

​ 符号表json文件:libxxxxxxx.so_sym.json

 

​ dump文件夹:./dump/*

 

​ Register json文件:./dump/regs.json

 

​ Segment json文件:./dump/segments.json

 

​ Section json文件:./dump/sections.json

怎么加载快照

Unidbg是基于Unicorn增加JNI支持的Unicorn Plus Pro Ultra(就是牛皮哦~),所以Unidbg也通过了一系列Api对寄存器和内存操作:

 

恢复寄存器状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
String js = FileUtil.readString("/Users/fangg3/WorkSpace/tools/androidReverse/unidbg-0.9.6/unidbg-android/src/test/java/com/lldb/dumper/dump/regs.json", StandardCharsets.UTF_8);
JSONObject json = new JSONObject(js);
Backend backend = emulator.getBackend();
Memory memory = emulator.getMemory();
backend.reg_write(Arm64Const.UC_ARM64_REG_X0, Long.decode(json.getStr("x0")));
backend.reg_write(Arm64Const.UC_ARM64_REG_X1, Long.decode(json.getStr("x1")));
backend.reg_write(Arm64Const.UC_ARM64_REG_X2, Long.decode(json.getStr("x2")));
        ...
        backend.reg_write(Arm64Const.UC_ARM64_REG_X28, Long.decode(json.getStr("x28")));
 
backend.reg_write(Arm64Const.UC_ARM64_REG_LR, Long.decode(json.getStr("lr")));
backend.reg_write(Arm64Const.UC_ARM64_REG_FP, Long.decode(json.getStr("fp")));
backend.reg_write(Arm64Const.UC_ARM64_REG_PC, Long.decode(json.getStr("pc")));
backend.reg_write(Arm64Const.UC_ARM64_REG_SP, Long.decode(json.getStr("sp")));
 
backend.reg_write(ArmConst.UC_ARM_REG_CPSR, Long.decode(json.getStr("cpsr")));
 
backend.reg_write(Arm64Const.UC_ARM64_REG_CPACR_EL1, 0x300000L);
//backend.reg_write(Arm64Const.UC_ARM64_REG_TPIDR_EL0, 0x000000797d778588L);
        //TPIDR_EL0 这个寄存器很特殊,存储了当前线程信息,需要lldb下断点获取

恢复内存状态:

 

因为执行快照时,并不需要一些内存段,这里过滤一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Boolean isWhite(String name) {
    String[] list = {
            "libxxxxxx.so", //目标so
            "libc.so"// 一般都要加载的
            "[stack:14414]",// 堆栈相关 这里14414不固定 可以看看sp指向哪里,在segments.json里找
            "[anon:.bss]",
            "[anon:bionic TLS]" //TPIDR_EL0 寄存器会指向这里
                                                  //还有其他根据实际情况添加
    };
    for (String a : list) {
        if (name.contains(a))
            return true;
    }
 
    return false;
}

这里内存文件已经zlib压缩,需要解压后再写入内存中。

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
    int UNICORN_PAGE_SIZE = 0x1000;
 
    private long align_page_down(long x) {
        return x & ~(UNICORN_PAGE_SIZE - 1);
    }
 
    private long align_page_up(long x) {
        return (x + UNICORN_PAGE_SIZE - 1) & ~(UNICORN_PAGE_SIZE - 1);
    }
 
    private void map_segment(long address, long size, int perms) {
 
        long mem_start = address;
        long mem_end = address + size;
        long mem_start_aligned = align_page_down(mem_start);
        long mem_end_aligned = align_page_up(mem_end);
 
        if (mem_start_aligned < mem_end_aligned) {
            emulator.getBackend().mem_map(mem_start_aligned, mem_end_aligned - mem_start_aligned, perms);
        }
    }   
 
void loadMemory() {
        String js = FileUtil.readString("/Users/fangg3/WorkSpace/tools/androidReverse/unidbg-0.9.6/unidbg-android/src/test/java/com/lldb/dumper/dump/segments.json", StandardCharsets.UTF_8);
        JSONArray json = new JSONArray(js);
        Memory memory = emulator.getMemory();
        System.out.println(json.size());
        try {
 
 
            for (int i = 0; i < json.size(); i++) {
 
                JSONObject seg_json = new JSONObject(json.get(i));
                String name = seg_json.getStr("name");
                Long start = seg_json.getLong("start");
                Long end = seg_json.getLong("end");
                String filename = seg_json.getStr("content_file");
                JSONObject permissions = seg_json.getJSONObject("permissions");
                Boolean r = permissions.getBool("r");
                Boolean w = permissions.getBool("w");
                Boolean x = permissions.getBool("x");
 
                if (Objects.equals(filename, "") || filename == null) {
                    continue;
                }
                if (!isWhite(name)) {
 
                    continue;
                }
                int per = 0;
                if (r) {
                    per |= UnicornConst.UC_PROT_READ;
                }
                if (w) {
                    per |= UnicornConst.UC_PROT_WRITE;
                }
                if (x) {
                    per |= UnicornConst.UC_PROT_EXEC;
                }
                map_segment(start, end - start, per);
                System.out.println(filename + " " + name);
                byte[] content_file = FileUtil.readBytes("/Users/fangg3/WorkSpace/tools/androidReverse/unidbg-0.9.6/unidbg-android/src/test/java/com/lldb/dumper/dump/" + filename);
                content_file = ZipUtil.unZlib(content_file);
                emulator.getBackend().mem_write(start, content_file);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("load memory over");
    }

运行快照

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
List<Object> list = new ArrayList<Object>();
       list.add(vm.getJNIEnv());
   DvmClass NativeApiobj = vm.resolveClass("com/mihoyo/hyperion/net/aaaaa");
       list.add(NativeApiobj.hashCode());
       list.add(vm.addLocalObject(new
 
   StringObject(vm,"123123123")));
       list.add(vm.addLocalObject(new
 
   StringObject(vm,"123123123")));
 
   Number result = Module.emulateFunction(emulator, ctx_addr, list.toArray());
 
   //        获取返回结果
   String sign_str = (String) vm.getObject(result.intValue()).getValue();
       System.out.println("sign_str="+sign_str);

一些优化

有时候,会出现Memory unmap的情况,这个时候,在Segments.json,找到对应地址,将name 添加到whileList里面即可。

 

用unidbg 的libc 替换内存段中的libc,便于调试:

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
//载入libc.so
...
vm.loadLibrary(new File("/Users/fangg3/WorkSpace/tools/androidReverse/unidbg-0.9.6/unidbg-android/src/main/resources/android/sdk23/lib64/libc.so"), false);
Module libc = emulator.getMemory().findModule("libc.so");
...
//过滤符号
      public static String getSymName(long addr) {
        JSONArray jarr = (JSONArray) parse;
        for (int i = 0; i < jarr.size(); i++) {
            JSONObject jsonObject = jarr.getJSONObject(i);
            if (jsonObject.getLong("addr") == addr) {
                return jsonObject.getStr("name");
            }
        }
        return "";
    }
 
//添加hook 筛选运行地址 pc寄存器指向下一条运行的地址
...
        emulator.getBackend().hook_add_new(new CodeHook() {
            @Override
            public void hook(Backend backend, long address, int size, Object user) {
                long pc_value = emulator.getBackend().reg_read(Arm64Const.UC_ARM64_REG_PC).longValue();
                   if (sym_json.contains(String.valueOf(pc_value))) {
                    String symName = getSymName(address);
                    Symbol symbolByName = libc.findSymbolByName(symName);
                    if (symbolByName != null) {
                        emulator.getBackend().reg_write(Arm64Const.UC_ARM64_REG_PC, symbolByName.getAddress());
                        System.out.println("call " + symName);
                    }
 
                 }
 
            }

一些大问题

有大佬能帮忙看看这些问题是什么原因么:

  1. arm32类型 So运行快照时,有时会出现arm/thumb指令切换问题。
  2. 某些So运行的时候,函数已经结束了,Unidbg还继续往下跑,不返回结果。
  3. 反正Bug还蛮多的....

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
点赞17
打赏
分享
最新回复 (18)
雪    币: 2
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
alwinwd 2022-8-27 07:46
2
0
近期一直纠结于怎么构造参数,看到这个思路打开了
雪    币: 3488
活跃值: (2862)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
StriveMario 2022-8-30 11:55
3
0
666
雪    币: 171
活跃值: (2096)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
FANGG3 2022-8-30 11:59
4
0
StriveMario 666
菜死了 哥(捕捉Mario)
雪    币: 1898
活跃值: (1755)
能力值: (RANK:400 )
在线值:
发帖
回帖
粉丝
莫灰灰 9 2022-8-30 17:29
5
0
牛逼!
雪    币: 1929
活跃值: (12850)
能力值: ( LV9,RANK:190 )
在线值:
发帖
回帖
粉丝
珍惜Any 2 2022-8-30 17:30
6
0
so检测unidbg刻不容缓
雪    币: 7822
活跃值: (21286)
能力值: ( LV12,RANK:550 )
在线值:
发帖
回帖
粉丝
随风而行aa 10 2022-8-30 17:59
7
0
牛逼
雪    币: 1694
活跃值: (4002)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
小黄鸭爱学习 2022-9-1 09:52
8
0
unidbg检测点应该很多吧 龙哥说几百个都不止
雪    币: 149
活跃值: (2023)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
saloyun 2022-9-2 10:39
9
0
真是个人才啊,楼主骨络清奇!!!
雪    币: 3987
活跃值: (2390)
能力值: ( LV10,RANK:165 )
在线值:
发帖
回帖
粉丝
DMemory 3 2022-9-2 11:20
10
0
思路很好啊,学习了
雪    币: 12075
活跃值: (15434)
能力值: ( LV12,RANK:240 )
在线值:
发帖
回帖
粉丝
pureGavin 2 2022-9-2 15:31
11
0
感谢分享
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
hxd97244 2022-9-3 20:11
12
0
学习了,感谢分享
雪    币: 85
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
blue_sky 2022-9-5 18:20
13
0
666,mark
雪    币: 27
活跃值: (358)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
weisente 2022-9-7 10:16
14
0
kkkk
雪    币: 1784
活跃值: (2180)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zhuzhu_biu 2022-10-6 16:17
15
1

大佬有没有想过用frida stalker去获取运行时的寄存器,如果用lldb 还需要过反调试 

最后于 2022-10-6 16:21 被zhuzhu_biu编辑 ,原因:
雪    币: 158
活跃值: (796)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
教教我吧~ 2023-9-25 10:11
16
0
请问有没有完整代码库呀
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_ldbucrik 2024-1-4 09:38
17
0
大佬,这个思路是不是可以用于ttd呢
雪    币: 171
活跃值: (2096)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
FANGG3 2024-1-9 10:26
18
0
mb_ldbucrik 大佬,这个思路是不是可以用于ttd呢
这个思路还是远远不够的,ttd有太多东西需要处理了. 在我的理解中,ttd是record all + replay all. 这个思路只是snapshot.
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_ldbucrik 2024-1-10 09:19
19
0
FANGG3 这个思路还是远远不够的,ttd有太多东西需要处理了. 在我的理解中,ttd是record all + replay all. 这个思路只是snapshot.
好的,目前在网上也没有找到带有ttd功能的arm调试器,win的x96debug到是有这个功能
游客
登录 | 注册 方可回帖
返回