仅供技术研究与学习,严禁用于非法用途。作者不承担任何违法责任。
交流群: QQ:1092055800 TG:https://t.me/+ArHIx-Km9jkxNjZl
lsdriver 是一个运行在 Android ARM64 内核中的内存调试与输入辅助模块。它不创建字符设备,也不通过 ioctl、netlink、procfs 或 sysfs 暴露命令接口;用户态程序通过固定地址共享内存与内核线程通信。
当前驱动侧提供这些核心能力。点击功能标题可以跳到对应的技术实现说明:
-
- 对指定
pid的用户虚拟地址执行读写。 - 内核侧自动跨页拆分,用户态封装侧对超过
0x1000字节的大块读写继续分片,避免覆盖共享缓冲。 - 读大块内存时遇到失败页会跳过并对失败块清零,只要成功处理过字节就返回成功字节数。
- 对指定
-
- 返回模块列表
modules[],每个模块包含多个区段segs[],区段带index、prot、start、end。 - 返回可扫描内存区域
regions[],主要用于指针扫描、特征扫描和通用内存搜索。 - 模块收集包含
/data/与/dev/前缀,支持/dev/zero (deleted)这类动态代码映射。 - 识别紧邻模块尾部的匿名可写 BSS,并兼容被改成
-w-p的 BSS。 - 对 VMA 碎裂、远端诱饵段、权限污染进行后处理,尽量给用户态提供稳定的模块基址和区段索引。
- 返回模块列表
-
- 自动查找多点触摸 input 设备并劫持
input_dev->mt。 - 将虚拟手指固定放在 slot 9,物理手指保留在 slot 0 到 slot 8。
- 支持
TouchDown、TouchMove、TouchUp,并返回触摸设备原始坐标范围。 - 手动维护
BTN_TOUCH、BTN_TOOL_FINGER、BTN_TOOL_DOUBLETAP,降低真实触摸与虚拟触摸互相污染的概率。
- 自动查找多点触摸 input 设备并劫持
-
- 读取 CPU 支持的 BRP/WRP 数量。
- 支持执行断点
X与访问观察点R、W、RW。 - 支持 1 到 8 字节断点长度;AArch64 执行断点会按硬件要求修正为 4 字节。
- 命中后按触发 PC 聚合记录,保存命中次数、PC、LR、SP、PSTATE、X0-X29、FPSR、FPCR、Q0-Q31。
- 每个寄存器都有 2-bit mask,可选择不操作、读取现场到记录、或把记录值写回现场。
-
- 在模块
.text中预留跳板槽位,用于安装运行时 hook。 - 通过
kallsyms_lookup_name找目标符号,通过aarch64_insn_patch_text_nosyncpatch 代码。 - 当前用于接管
breakpoint_handler与watchpoint_handler,把硬件断点/观察点命中转发到驱动自己的回调。 - 跳板会保存/恢复通用寄存器和 NZCV,调用工作函数后执行被覆盖的原指令,再返回目标函数入口后的下一条指令。
- 在模块
-
- 用户态进程名设置为
LS后,驱动连接线程会将固定地址0x2025827000的共享内存页 pin 住并vmap到内核。 - 调度线程轮询共享结构的
kernel/user状态位并按op分发请求。 - 线程运行细节见 线程模型。
- 用户态进程名设置为
-
- 驱动注册
do_exitkprobe,检测LS相关主线程退出时释放缓存状态并销毁虚拟触摸。 - 模块初始化时会隐藏模块链表、sysfs 模块对象、部分 vmalloc 信息和工作线程链表节点。
- 驱动注册
-
Android-LS/include/DriverMemory.h在这些基础能力上提供 C++ 封装和上层工具。- 包括读写模板、模块地址查询、扫描区域合并、模块 dump、硬件断点记录管理和特征扫描。
用户态封装主要接口:
Read<T>/Read(address, buffer, size)/Write(...)ReadString/ReadWStringGetPid/SetGlobalPidGetMemoryInformation/GetModuleAddress/GetScanRegionsDumpModule,按枚举出的模块跨度 dump 并修复部分 ELF Program Header 和 Dynamic 表GetHwbpInfoRef/SetProcessHwbpRef/RemoveProcessHwbpRef/RemoveHwbpRecordSignatureScanner,基于驱动读内存和扫描区域完成特征生成、过滤和扫描
lsdriver/ 目录下核心文件:
lsdriver.c:模块入口、连接线程、调度线程、进程退出监听、模块/线程隐藏逻辑io_struct.h:共享内存协议定义,包括操作码、请求结构、内存信息结构、硬件断点记录结构physical.h:进程虚拟地址到物理地址翻译、物理内存读写、跨页读写主流程process_memory_enum.h:进程 VMA 枚举、模块收集、扫描区过滤、模块区段反诱饵后处理virtual_input.h:虚拟触摸初始化、slot 劫持、虚拟触摸事件上报与销毁hwbp.h:硬件断点用户可见接口、命中现场记录/回放逻辑arm64_debug_monitor.h:sched_switch 监听、debug monitor inline hook、BVR/BCR/WVR/WCR 写入与清理arm64_reg.h:ARM64 调试寄存器、FP/SIMD 寄存器读写辅助inline_hook_frame.h:ARM64 inline hook 跳板框架,负责保存原指令、构造跳板、patch 目标入口和卸载恢复export_fun.h:kallsyms_lookup_name获取、CFI 绕过、页表辅助函数Makefile:模块编译参数
Android-LS/include/DriverMemory.h 是配套用户态 C++ 封装,负责共享内存映射、握手、同步、自旋锁、读写分片、坐标换算和上层辅助功能。
协议结构定义在 io_struct.h 的 struct req_obj。
用户态初始化流程:
- 调用
prctl(PR_SET_NAME, "LS", 0, 0, 0)把进程名设置为LS。 - 使用
mmap在固定地址0x2025827000创建sizeof(req_obj)大小的共享内存。 - 清零
req_obj后调用一次IoCommitAndWait()等待驱动握手。
内核连接线程流程:
- 周期遍历进程列表,查找
task->comm == "LS"的进程。 - 对固定用户地址执行
get_user_pages_remote,把共享内存页 pin 住。 - 通过
vmap把这些页映射成内核可访问虚拟地址,赋给全局req。 - 设置
ProcessExit = true,再设置req->user = true通知用户态连接完成。
get_user_pages_remote 对 5.10、5.15、6.1、6.5、6.12 的参数签名做了条件编译适配。
kernel:用户态置true,表示有请求需要内核处理;内核处理前将其清为false。user:内核置true,表示处理完成或握手完成;用户态等待后将其清为false。
当前代码中 kernel / user 是普通 bool 字段,不是 atomic_t。用户态封装用 SpinLock 串行化对共享请求结构的访问,内核调度线程单线程消费请求。
op:操作码,类型为enum sm_req_opstatus:返回状态或成功处理的字节数pid:目标进程 PIDtarget_addr:目标进程虚拟地址size:读写长度user_buffer[0x1000]:单次读写数据缓冲mem_info:模块和扫描区域枚举结果bt:硬件断点类型bl:硬件断点长度bs:硬件断点线程范围配置字段bp_info:硬件断点资源信息和命中记录POSITION_X/POSITION_Y:触摸设备原始坐标范围x/y:触摸事件坐标
op_o:空调用,用于握手或探活op_r:读目标进程内存op_w:写目标进程内存op_m:枚举目标进程内存布局op_down:虚拟触摸按下op_move:虚拟触摸移动op_up:虚拟触摸抬起op_init_touch:初始化虚拟触摸并返回触摸坐标范围op_brps_weps_info:读取 CPU BRP/WRP 数量op_set_process_hwbp:设置硬件断点/观察点op_remove_process_hwbp:删除当前硬件断点/观察点监控op_kexit:结束内核工作线程循环
模块初始化后启动两个内核线程:
ConnectThreadFunctionDispatchThreadFunction
连接线程只在 ProcessExit == false 时扫描进程。找到 LS 后,它 pin 住共享内存页、vmap 到内核、设置连接状态,然后每 2 秒继续检查一次状态。
如果 get_user_pages_remote 失败,会通过动态解析到的 release_pages 回收已经 pin 的页,避免直接内联 put_page() 引入 Android page_pinner 静态键依赖。
调度线程只在 ProcessExit == true 时消费请求:
- 检查
req->kernel。 - 关闭 DAIF 中断位,降低请求处理期间被打断的概率。
- 清除
req->kernel。 - 根据
req->op分发到内存读写、内存枚举、触摸、硬件断点或退出逻辑。 - 设置
req->user = true通知用户态。 - 恢复 DAIF。
空闲轮询策略:
- 前
5000轮使用cpu_relax()忙等,追求低延迟。 - 之后使用
usleep_range(50, 100)降低空闲功耗。 - 尚未连接用户进程时使用
msleep(2000)深睡眠。
实现文件:physical.h
入口函数:
read_process_memory(pid, vaddr, buffer, size)write_process_memory(pid, vaddr, buffer, size)- 两者最终都进入
_process_memory_rw(...)
当前实际启用路径:
- 使用
mmu_translate_va_to_pa(...)把目标进程用户虚拟地址翻译为物理地址。 - 使用
linear_read_physical(...)/linear_write_physical(...)通过内核线性映射访问物理内存。
保留但当前未启用的路径:
walk_translate_va_to_pa(...):手动走用户页表获取 PFN。pte_read_physical(...)/pte_write_physical(...):复用一页vmalloc空间,改写其 PTE 来映射任意物理页。
mmu_translate_va_to_pa(...) 的核心流程:
- 读取并保存当前
DAIF和TTBR0_EL1。 - 关闭 D/A/I/F 中断位。
- 将目标进程
mm->pgd的物理地址编码成新的TTBR0_EL1,兼容 LPA2 场景下的 52 位物理地址格式。 - 执行
AT S1E0R, va触发硬件翻译。 - 读取
PAR_EL1,检查失败位。 - 从
PAR_EL1[51:12]还原物理页地址,并拼回页内偏移。 - 使用
tlbi vaae1is清理本次 ASID=0 翻译带来的 TLB 污染。 - 恢复原
TTBR0_EL1和DAIF。
这条路径依赖硬件翻译结果,避免手动页表遍历时对页表层级、折叠层级和内核版本差异做大量分支。
_process_memory_rw(...) 做了这些处理:
- 缓存上一次使用的
mm_struct:s_last_pid/s_last_mm - 缓存上一次翻译的页:
s_last_vpage_base/s_last_ppage_base - 按页拆分跨页访问
- 对 1、2、4、8 字节读写使用固定大小
__builtin_memcpy - 对大块读取失败页清零并继续后续页
返回值语义:
- 成功处理过字节:返回成功处理的字节数
- 完全失败:返回最后一次失败错误码
- 参数错误:返回
-EINVAL - 找不到进程或 task:返回
-ESRCH
用户态 DriverMemory.h 还会对超过 0x1000 的读写进行二次分片,因为共享结构中的 user_buffer 固定为一页大小。
实现文件:process_memory_enum.h
入口函数:
enum_process_memory(pid, &req->mem_info)
输出结构:
memory_info.module_countmemory_info.modules[MAX_MODULES]memory_info.region_countmemory_info.regions[MAX_SCAN_REGIONS]
模块来源规则:
- 文件映射路径前缀命中
/data/ - 文件映射路径前缀命中
/dev/
保留 /dev/ 的原因是部分保护或动态代码会通过 /dev/zero (deleted)、共享映射、手动 loader、JIT 或跳板方式承载代码。这样用户态可以把这类动态代码区域也按模块段处理、dump 或纳入扫描范围。
BSS 识别规则:
- 当前 VMA 没有文件映射
- 当前 VMA 带
VM_WRITE,不强求VM_READ - 当前 VMA 与上一段首尾相连
- 上一段属于正在追踪的模块
BSS 区段的 index 固定为 -1,后续不会参与普通连续 index 编号。
扫描区域只收集私有 rw-p VMA,并过滤明显无关或噪声较大的区域。
路径前缀黑名单:
/dev//system//vendor//apex/
关键词黑名单:
.oat.art.odex.vdex.dex.ttfdalvikgrallocashmem
匿名区域还会排除:
- 主线程栈
[vvar][vdso][vsyscall]
用户态 GetScanRegions() 会把 regions[] 与所有模块 segs[] 合并后排序,因此扫描时既能扫匿名读写区域,也能扫模块静态段。
枚举完成后,每个模块会做一次区段清洗:
- 按虚拟地址排序。
- 用 16MB 断层阈值做体积聚类,选出模块主体区间。
- 删除远离主体的诱饵 VMA。
- 对尾部 BSS 做豁免,允许 BSS 从主体尾部继续延伸。
- 按拓扑重新标记 RO、RX、RW、BSS。
- 根据标记反推标准
prot,把异常-w-BSS 规范为 RW。 - 首尾相连且同类的区段进行拉链式合并。
- 普通区段重新编号为
0, 1, 2...,BSS 保持-1。
最终用户态可以通过 module.segs[index].start/end 获得更稳定的模块基址、代码段、数据段和 BSS 范围。
实现文件:virtual_input.h
核心常量:
VTOUCH_TRACKING_ID_BASE = 40000TARGET_SLOT_IDX = 9PHYSICAL_SLOTS = 9TOTAL_SLOTS = 10
初始化流程:
- 通过
generic_kallsyms_lookup_name("input_class")获取 input class。 - 使用
class_for_each_device查找具备EV_ABS、ABS_MT_SLOT、BTN_TOUCH和input->mt的触摸设备。 get_device持有设备引用。- 分配新的
input_mt,至少保留 10 个 slot 的存储空间。 - 复制旧 MT 状态,保存
original_mt,替换dev->mt。 - 对物理驱动暴露
num_slots = 9,对 Android 上报ABS_MT_SLOT支持到 10 个 slot。 - 缓存设备是否支持
ABS_MT_TOUCH_MAJOR、ABS_MT_WIDTH_MAJOR、ABS_MT_PRESSURE。
事件上报流程:
- 使用
local_irq_save缩小真实触摸中断与虚拟注入交错的窗口。 - 保存当前
ABS_MT_SLOT。 - 临时把
mt->num_slots切到 10。 - 选择 slot 9,调用
input_mt_report_slot_state上报虚拟手指状态。 - 按下或移动时上报 X/Y,并在设备支持时伪造面积和压力。
- 切回
mt->num_slots = 9。 - 恢复原 slot。
- 手动更新全局按键并
input_sync。 - 恢复中断。
全局按键处理:
- 虚拟按下后会临时清除设备
keybit中的BTN_TOUCH、BTN_TOOL_FINGER、BTN_TOOL_DOUBLETAP,避免真实手指快速抬起把虚拟滑动打断。 - 虚拟抬起或销毁时恢复这些 keybit。
update_global_keys()会统计 0 到 8 号物理 slot 和虚拟 slot 状态,重新上报全局触摸按键。
销毁流程:
- 如果虚拟手指仍处于按下状态,先发送抬起。
- 恢复原始
input_mt和原始ABS_MT_SLOT范围。 - 释放劫持的
input_mt及red矩阵。 put_device释放触摸设备引用。
主要文件:
hwbp.harm64_debug_monitor.harm64_reg.hinline_hook_frame.h
当前实现不再走 register_user_hw_breakpoint / unregister_hw_breakpoint 的 perf 断点链路。旧 perf 方案仍保留在注释中,实际启用的是调度监听加 ARM64 debug 寄存器直写方案。
get_hw_breakpoint_info() 通过 id_aa64dfr0_el1 读取:
- BRP 数量:
((dfr0 >> 12) & 0xF) + 1 - WRP 数量:
((dfr0 >> 20) & 0xF) + 1
结果写入 req->bp_info.num_brps 和 req->bp_info.num_wrps。
set_process_hwbp(...) 会填充全局 breakpoint_config:
pidbtblbsaddron_hitbp_info
然后调用 start_task_run_monitor(&bp_config):
- 安装 inline hook,接管
breakpoint_handler与watchpoint_handler。 - 注册
sched_switchtrace 回调。 - 在目标线程组被调度到 CPU 时写入硬件断点寄存器。
- 在目标线程组被切走时清空断点控制寄存器并关闭当前 CPU 的硬件调试。
remove_process_hwbp() 调用 stop_task_run_monitor():
- 注销
sched_switchtrace 回调 - 清空全局配置
- 移除 inline hook
注意:bs 字段当前会写入配置,但 probe_sched_switch 现有代码按 next->tgid == pid / prev->tgid == pid 生效,没有继续区分主线程、子线程或全部线程。
目标线程组切入 CPU 时:
- 调用
enable_hardware_debug_on_cpu():- 解锁
OSLAR_EL1 - 设置
MDSCR_EL1.MDE - 设置
MDSCR_EL1.KDE
- 解锁
- 将用户输入转换成
arch_hw_breakpoint风格的address与ctrl:X转为ARM_BREAKPOINT_EXECUTER转为ARM_BREAKPOINT_LOADW转为ARM_BREAKPOINT_STORERW转为LOAD | STORE- AArch64 执行断点长度修正为 4
- 地址按执行断点 4 字节或观察点 8 字节边界向下对齐,长度按 offset 左移
- 执行断点写入:
DBGBVR5_EL1DBGBCR5_EL1
- 访问观察点写入:
DBGWVR3_EL1DBGWCR3_EL1
目标线程组切走 CPU 时:
- 清零
DBGBCR5_EL1 - 清零
DBGWCR3_EL1 - 调用
disable_hardware_debug_on_cpu()关闭 MDE/KDE 并重新上 OS Lock
inline hook 表:
breakpoint_handler->work_trampoline_breakpointwatchpoint_handler->work_trampoline_watchpoint
命中后流程:
- hook 跳板保存通用寄存器和 NZCV。
- 调用工作函数。
- 工作函数调用
bp_config.on_hit(regs, bp_config)。 sample_hbp_handler()按regs->pc查找或创建hwbp_record。- 新 PC 记录默认把所有寄存器 mask 设为读取。
- 根据每个寄存器的 mask 执行读取或写回。
- 工作函数关闭当前 BCR/WCR 的 enable bit,让当前命中指令可以步过。
- 跳板恢复寄存器、执行被覆盖的原指令并返回原异常处理函数后续位置。
命中记录容量:
- 最多保存
0x100个不同 PC 的记录。 - 每条记录保存
hit_count、PC、通用寄存器、状态寄存器、FP/SIMD 寄存器。
寄存器 mask 语义:
HWBP_OP_NONE:不处理该寄存器HWBP_OP_READ:从命中现场读取到hwbp_recordHWBP_OP_WRITE:把hwbp_record中的值写回命中现场
初始化时执行:
bypass_cfi():尝试 patch CFI slowpath,使动态函数指针调用更容易通过。hide_myself():- 6.12 以下从
vmap_area_list和vmap_area_root摘除自身 vmalloc 区间。 - 从
THIS_MODULE->list摘除,使/proc/modules不显示。 - 删除
THIS_MODULE->mkobj.kobj,使/sys/module不显示。 - 清理 holder 依赖链表和 sysfs holder 链接。
- 6.12 以下从
allocate_physical_page_info():为保留的 PTE 物理映射读写方案准备一页vmalloc空间。- 启动连接线程和调度线程。
- 注册
do_exitkprobe。 - 从
task->tasks链表摘除两个工作线程。
do_exit kprobe 只处理线程组主线程退出。若 task->comm 包含 ls 或 LS:
- 主动调用一次
read_process_memory(666666, 1, &ProcessExit, 1),借路径释放缓存的mm_struct - 调用
v_touch_destroy()清理虚拟触摸 - 将
ProcessExit置回false
实现文件:inline_hook_frame.h
这个框架是驱动内部的 ARM64 入口 hook 工具。当前主要服务于硬件断点功能:当普通 kprobe 被 NOKPROBE_SYMBOL 拒绝、ftrace 又不可用时,驱动通过 inline hook 接管 debug monitor 的异常入口,把命中现场转给自己的处理逻辑。
每个 hook 使用 struct hook_entry 描述:
target_sym:目标内核函数符号名target_addr:运行时解析出的目标地址work_fn:被 hook 后先执行的工作函数trampoline:模块.text中分配到的跳板槽位saved_insn:目标入口被覆盖前的原始 4 字节指令installed:安装状态
便捷宏 HOOK_ENTRY(sym, fn) 用来声明 hook 表。当前 hook 表在 arm64_debug_monitor.h 中定义:
breakpoint_handler->work_trampoline_breakpointwatchpoint_handler->work_trampoline_watchpoint
框架在模块代码段中预留:
TRAMP_SLOT_COUNT = 10TRAMP_WORDS = 116TRAMP_BYTES = 464
预留区初始填充 NOP,安装 hook 时会把对应槽位 patch 成完整跳板代码。这样跳板本身位于可执行代码段,不需要额外申请可执行内存。
trampoline_build() 生成的跳板逻辑:
- 栈上开辟 272 字节临时空间。
- 保存 X0-X30。
- 保存 NZCV 条件标志。
- 按
struct pt_regs前缀填充regs[0..30]、sp、pc、pstate,其中pstate当前用于保存 NZCV。 - 设置临时帧指针,并把
struct pt_regs *现场地址放入 X0。 - 通过数据槽加载
work_fn地址并blr x9调用工作函数,工作函数签名为int work_fn(struct pt_regs *regs)。 work_fn可读写regs->regs[0..30]、regs->sp、regs->pc和regs->pstate。- 无论
work_fn返回值是什么,都会先按regs中的值恢复保存现场,因此regs->sp会写回真实 SP。 regs->pc保持为原入口地址时,work_fn返回 0 会恢复现场、执行原始入口指令,再用已 patch 的b跳回target_addr + 4;返回 1 会恢复现场后ret x30,不再执行原函数入口。regs->pc被改成其他地址时,会恢复现场后用ret x16跳到新的regs->pc,让 PC 修改真实生效。
动态 regs->pc 路径需要一个通用寄存器承载跳转目标,当前使用 X16;因此修改 PC 时,X16 会被用作最终跳转寄存器。普通继续执行路径仍会在执行原始入口指令前恢复 X16/X17。
hook_entry_install() 的流程:
- 通过
generic_kallsyms_lookup_name(target_sym)解析目标函数地址。 - 从预留代码段获取一个 trampoline slot。
- 读取并保存目标入口原始指令。
- 构造跳板,写入原指令、返回地址和工作函数地址。
- 用
aarch64_insn_patch_text_nosync把跳板代码 patch 到预留槽位。 - 用
arm64_make_b生成从目标入口跳到 trampoline 的b指令。 - patch 目标函数入口为
b trampoline。 - 标记 hook 已安装。
hook_entry_remove() 会把 saved_insn patch 回 target_addr,清空 trampoline 指针并标记未安装。
inline_hook_install() 负责批量安装 hook 表,任何一条安装失败都会回滚已经安装的项。inline_hook_remove() 负责逆序卸载。
硬件断点启动时,start_task_run_monitor() 会安装 inline hook 并注册 sched_switch:
- 目标线程被调度到 CPU 时写入 BVR/BCR 或 WVR/WCR。
- 断点命中后进入内核原始
breakpoint_handler或watchpoint_handler。 - hook 先跳到驱动 trampoline。
- trampoline 调用
work_trampoline_breakpoint或work_trampoline_watchpoint。 - 工作函数调用
sample_hbp_handler()记录/回放寄存器。 - 工作函数关闭当前断点 enable bit,让命中指令可以继续执行。
- trampoline 执行原始入口指令并返回原异常处理函数。
这套链路让驱动可以直接拿到 pt_regs 现场,又不依赖 perf 硬件断点 API。
实现文件:Android-LS/include/DriverMemory.h
- 构造
Driver(bool touch)时先InitCommunication(),再按参数决定是否InitTouch()。 InitCommunication()负责设置进程名、固定地址mmap、清零请求结构、等待驱动握手。IoCommitAndWait()将req->kernel置true,忙等req->user,完成后清除req->user。- 共享请求结构由
SpinLock保护,避免多线程同时改写同一个req_obj。
Read<T>/Read(...)/Write(...)走驱动op_r/op_w。GetMemoryInformation()走驱动op_m。GetModuleAddress(moduleName, segmentIndex, outAddress, isStart)根据模块名后缀和区段 index 查询起止地址。GetScanRegions()合并匿名扫描区与所有模块区段,并按地址排序。DumpModule()根据模块跨度读取内存,按页抢救失败区域,输出到/sdcard/dump/<module>.dump.so,并尝试修复 ELF Header、Program Header 和部分 Dynamic 指针。
用户态 TouchDown / TouchMove / TouchUp 会把外部屏幕坐标换算到触摸设备原始坐标。
横屏且设备原始坐标仍是竖屏时,默认按“右侧充电口”方向换算:
x = (1 - normY) * POSITION_Xy = normX * POSITION_Y
GetHwbpInfoRef()刷新并返回bp_infoSetProcessHwbpRef(...)设置断点RemoveProcessHwbpRef()删除断点RemoveHwbpRecord(index)删除指定 PC 命中记录并压缩数组
SignatureScanner 基于驱动内存读和 GetScanRegions() 实现:
ScanAddressSignature(addr, range):围绕目标地址生成特征码文件FilterSignature(addr):多次运行后把变化字节过滤为通配符ScanSignature(pattern, range):按内存区域扫描特征ScanSignatureFromFile():从Signature.txt读取特征并扫描,结果追加写回文件
默认相对路径会回退到 /data/akernel/。
在目标内核源码树执行:
make -C <KDIR> M=$PWD/lsdriver ARCH=arm64 LLVM=1 moduleslsdriver/Makefile 当前默认启用:
-O3-Wno-error-fno-stack-protector-fomit-frame-pointer-funroll-loops-fstrict-aliasing-ffunction-sections -fdata-sections
DEBUG、KASAN/UBSAN/KCSAN 关闭、ftrace 插桩移除、强制内联、分支保护关闭等选项保留为注释,需要按目标内核情况自行启用。