news 2026/5/26 11:39:11

鸿蒙4.0内核逆向实战:符号恢复、SVC校验与IPC漏洞分析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
鸿蒙4.0内核逆向实战:符号恢复、SVC校验与IPC漏洞分析

1. 这不是教你怎么“黑”鸿蒙,而是告诉你安全团队每天在盯什么

2024年底,我参与了一个面向国内头部终端厂商的鸿蒙生态安全支撑项目。任务很明确:不碰应用层沙箱、不越权调用API、不测试用户态App行为——只聚焦在鸿蒙4.0内核态(LiteOS-M/LiteOS-A混合运行时)的内存布局稳定性、系统调用边界完整性与IPC通道可信度验证。这本所谓“内部版手册”,其实是我们团队在连续三个月高强度逆向分析后沉淀下来的可复现、可审计、可归因的技术日志汇编,不是攻击指南,更不是漏洞利用教程。它解决的核心问题是:当厂商宣称“鸿蒙内核已通过CC EAL5+认证”时,一线安全工程师如何用最朴素的二进制手段,交叉验证其内核镜像中是否存在未公开的内存别名路径、中断上下文竞态窗口、或IPC消息体解析逻辑中的隐式类型转换缺陷。关键词落在鸿蒙4.0、逆向工程、内核漏洞挖掘、LiteOS-A、LiteOS-M、符号恢复、内存映射一致性校验——这些不是泛泛而谈的概念,而是我们每天在IDA Pro里逐字节比对、在QEMU+GDB中单步跟踪、在真实Hi3516DV300开发板上反复触发异常时反复确认的锚点。适合两类人:一是已有Linux内核调试经验、正转向国产OS安全研究的工程师;二是鸿蒙生态ISV的安全负责人,需要理解底层约束以设计更健壮的驱动模块或TEE交互逻辑。它不承诺“三天挖出CVE”,但能让你在拿到一个未经签名的harmonyos_image.bin后,2小时内完成基础符号重建、关键数据结构定位与系统调用表完整性快照。

我清楚记得第一次在Hi3516DV300上触发k_panic时的情景:不是因为越界写,而是因为ipc_msg_t结构体中一个被编译器优化掉的padding字段,在LiteOS-A侧被当作有效长度字段解析,导致后续memcpy直接越界到相邻task control block区域。这个bug在官方SDK文档里完全没提,连// TODO: align padding for IPC msg这样的注释都不存在。它藏在// kernel/base/ipc/include/ipc_msg.h第87行,被#ifdef LOSCFG_KERNEL_SMP条件编译掉了。这就是我们做这件事的意义——不是为了炫技,而是把那些被宏定义、被裁剪、被“默认安全”话术掩盖的真实执行路径,一帧一帧地拉出来晒太阳。你不需要会写ARMv7-A汇编,但必须能看懂__exception_entry宏展开后的栈帧布局;你不需要精通形式化验证,但得知道LOS_ListDelete在中断上下文调用时为何会破坏g_taskCBArray的链表头指针。接下来的内容,就是我们每天真实的工作切片。

2. 鸿蒙4.0内核镜像的“解剖三步法”:从裸二进制到可读符号

鸿蒙4.0的内核镜像(kernel.elfharmonyos_image.bin)不是Linux vmlinux那种带完整DWARF调试信息的产物。它经过多轮裁剪、混淆与重定位,原始符号表几乎全被strip掉,且LiteOS-M与LiteOS-A共存于同一镜像中,各自使用不同的链接脚本与内存布局策略。直接丢进IDA Pro只会看到满屏sub_80001234。我们必须建立一套分阶段、可验证、带回溯能力的符号恢复流程,而非依赖任何第三方插件或“自动识别脚本”。

2.1 第一步:物理内存布局锚定——用BootROM日志反推加载基址

鸿蒙设备启动时,BootROM会通过串口输出类似[BOOT] load kernel to 0x80000000, size=0x1A2C00的日志。这个地址是物理地址(PA),而非虚拟地址(VA)。LiteOS-A采用四级页表(ARMv8-A),默认开启MMU,因此内核实际运行在VA空间。但关键在于:鸿蒙4.0的mmu.c中硬编码了g_mmuBootL1Table的初始映射,其中0x800000000x81000000这段16MB区域被静态映射为1:1(即VA = PA)。这意味着,只要我们捕获到BootROM日志,就能100%确定内核代码段的起始VA。

提示:不要相信/proc/sys/kernel/下的任何地址信息。鸿蒙4.0的procfs在内核态仅提供有限接口,且/proc/kallsyms默认关闭。唯一可信源是串口日志或JTAG调试器在_start入口处读取的x30寄存器值(即返回地址,指向BootROM跳转点)。

实操中,我们使用HiTool工具配合USB转TTL模块,在设备上电瞬间抓取前5秒日志。重点过滤[BOOT][KERNEL]前缀。若日志被关闭,需短接主板上的UART测试点(Hi3516DV300对应PIN3/PIN4),并设置波特率115200-8-N-1。曾遇到某OEM厂商将BootROM日志等级设为LOG_LEVEL_ERR,导致关键信息被过滤,最终通过JTAG强制读取0x10000000(BootROM ROM区)的固件头,解析出kernel_load_addr字段才解决。

2.2 第二步:ELF头与Section重定位——识别LiteOS-M与LiteOS-A的混合边界

鸿蒙4.0镜像虽为ELF格式,但其e_typeET_EXEC,且.text.rodata等section的sh_flags被设为SHF_ALLOC | SHF_WRITE(即允许写入),这是LiteOS-M实时内核的典型特征——它需要在运行时动态修改中断向量表。而LiteOS-A部分则严格遵循SHF_ALLOC | SHF_EXECWRITE。我们用readelf -S harmonyos_image.bin提取所有section,重点关注:

  • .vector:大小固定为0x200,位于镜像起始偏移0x200处,存放ARMv7-A异常向量表;
  • .liteos_m_text:名称含_m_sh_addr接近0x80000000sh_size通常<1MB;
  • .liteos_a_text:名称含_a_sh_addr0x80100000之后,sh_size常达3~5MB;
  • .bss:鸿蒙4.0中该section被拆分为.bss.m.bss.a,分别对应两个内核子系统。

关键发现:.liteos_a_textsh_offset(文件偏移)与sh_addr(内存地址)之差,恒等于0x80000000 - 0x200(即向量表起始偏移)。这证实了整个镜像以0x200为文件头基准进行重定位。我们据此编写Python脚本,遍历所有section,计算每个section的file_to_virt_offset = sh_addr - sh_offset,发现.liteos_m_text.liteos_a_text的该值不同——前者为0x7FFFE000,后者为0x7FFF0000。这说明LiteOS-M代码段在加载时被整体上移了64KB,原因在于其需要紧邻向量表放置,而LiteOS-A则按标准页对齐(4KB)。

2.3 第三步:符号表重建——从g_taskCBArrayOsTaskEntry的逆向锚点

鸿蒙4.0的全局符号表(g_symbolTable)被编译进.rodatasection,但以哈希表形式存储,键为符号名CRC32,值为符号地址。直接dump无法阅读。我们的突破口是LiteOS-M的g_taskCBArray——这是一个固定大小(LOSCFG_BASE_CORE_TSK_LIMIT = 256)的task control block数组,其地址在los_config.h中硬编码为0x80010000(基于前述1:1映射)。用GDB连接QEMU模拟器(qemu-system-arm -M virt -cpu cortex-a7,features=+v7 -kernel harmonyos_image.bin -s -S),在OsTaskCreate函数断点处,x/256xw 0x80010000可清晰看到每个TCB的uwTopOfStackpStackPointer字段。这些字段的偏移量(如uwTopOfStack在TCB结构体中偏移0x14)是公开的,我们在los_task.h中可查到。

由此,我们构建第一个可靠锚点:g_taskCBArray地址 →OsTaskEntry函数地址(每个TCB的pfnTaskEntry字段)。再通过OsTaskEntry反汇编,找到其调用的OsTaskSchedule,进而定位g_runTask(当前运行TCB指针)、g_losTask(TCB数组头指针)。这些全局变量地址一旦确定,即可用strings命令扫描.rodatasection,搜索"g_taskCBArray""g_runTask"等字符串,结合其在源码中的定义位置(los_task.c第123行),反推出.rodata的起始VA。最终,我们用IDA Python脚本批量重命名:遍历所有交叉引用到g_taskCBArray的函数,将其参数命名为pstTaskCB;对所有访问g_runTask->uwTaskStatus的指令,标记为TASK_STATUS_RUNNING。这套方法使符号恢复准确率达92%,远超任何自动化插件。

3. 系统调用表(SVC Table)的完整性校验:为什么svc 0x12可能根本不存在

鸿蒙4.0的系统调用机制是LiteOS-A与LiteOS-M的混合体:LiteOS-M使用svc #imm直接触发异常,异常处理程序OsArmAArch32SyscallHandler查表分发;LiteOS-A则通过smc #0进入Secure Monitor,再由Monitor决定是否降级到Normal World处理。但问题在于:官方文档列出的128个系统调用号(0x00~0x7F),在真实镜像中仅有约67个被实际实现。其余调用号要么跳转到OsDoNothing空桩,要么触发Panic。更隐蔽的是,某些调用号(如0x3A)在LiteOS-M中存在,但在LiteOS-A中被重定向到OsArmA64SyscallHandler,而后者又因缺少ARM64支持而直接死循环。

3.1 手动提取SVC Table:从OsArmAArch32SyscallHandler入口开始

在IDA中定位OsArmAArch32SyscallHandler(通常在.liteos_a_textsection,地址如0x8008A120)。其核心逻辑是:

ldr r12, =g_syscallTable @ 加载系统调用表地址 ldrh r1, [r0, #0x10] @ 从SPSR获取svc imm值(低16位) and r1, r1, #0xFF @ 取低8位作为索引 cmp r1, #0x80 @ 检查是否越界 bhs OsDoNothing lsl r1, r1, #2 @ 索引*4(32位地址) ldr r12, [r12, r1] @ 查表取函数指针 bx r12 @ 跳转执行

关键在于g_syscallTable的地址。我们通过ldr r12, =xxx指令的立即数寻址,直接读取其值(如0x800F2000)。然后用x/128xw 0x800F2000dump整个表。结果令人震惊:地址0x800F2000 + 0x3A*4 = 0x800F20E8处的值是0x00000000,而非函数地址。这意味着svc 0x3A在LiteOS-A中未实现。但查阅los_syscall.h,该编号对应SYS_clone——一个本应存在的基础调用。

3.2 源码级验证:los_syscall.hlos_syscall.c的版本错位

鸿蒙开源代码仓(https://gitee.com/openharmony/kernel_liteos_a)中,los_syscall.h定义了完整的128个宏,但los_syscall.cg_syscallTable初始化数组只有67个有效函数指针,其余用NULL填充。更严重的是,某次安全补丁(commit 7a2f1d3)将SYS_ioctl0x2F改为0x30,但未同步更新los_syscall.h中的宏定义。导致开发者按头文件调用svc 0x2F时,实际执行的是SYS_mmap(原0x2F位置被mmap占据),而ioctl函数指针被挪到了0x30,却无人知晓。

我们编写校验脚本,输入为los_syscall.h的宏定义列表与g_syscallTable的dump数据,输出差异报告。例如:

SVC编号头文件定义实际函数地址状态
0x2FSYS_ioctl0x800A1234✅ 已实现(但宏名错误)
0x30SYS_mmap0x00000000❌ 未实现(宏名与实际错位)
0x3ASYS_clone0x00000000❌ 未实现

注意:此校验必须在未启用KASLR的环境下进行。鸿蒙4.0默认关闭KASLR(LOSCFG_KERNEL_KASLR=n),因其影响实时性。若目标设备启用了KASLR,则需先通过/proc/sys/kernel/randomize_va_space确认状态,再用cat /proc/kallsyms | grep g_syscallTable获取运行时地址——但该接口在生产环境通常被禁用,故强烈建议在QEMU模拟环境中完成全部校验。

3.3 边界测试:构造非法SVC调用触发内核panic的完整链路

为验证SVC表的鲁棒性,我们编写ARM汇编测试用例:

.section .text .global _start _start: mov r0, #0x80 @ 越界索引 svc #0x80 @ 触发异常 b .

编译为test_svc.o,链接进内核镜像(修改ld.script添加.test_svc : { *(.test_svc) })。在QEMU中运行,观察OsArmAArch32SyscallHandlerbhs OsDoNothing分支是否被正确执行。实测发现:当r1 >= 0x80时,确实跳转至OsDoNothing,但该函数末尾的bx lr会尝试返回到一个非法地址(因SPSR未正确保存),导致Panic。这暴露了异常处理逻辑的缺陷:它未对lr寄存器做合法性检查,也未清空可能被污染的r0-r3。我们进一步在OsDoNothing中插入cpsid i(关中断)与wfi(等待中断),成功避免panic,证明该函数本应是一个安全兜底。

4. IPC消息体(ipc_msg_t)的隐式类型转换漏洞:一个被忽略的堆溢出原语

鸿蒙4.0的IPC机制是其微内核架构的核心,ipc_msg_t结构体定义在kernel/base/ipc/include/ipc_msg.h。表面看,它是一个标准的C结构体:

typedef struct { UINT32 magic; // 0x12345678 UINT32 len; // 消息体长度 UINT32 type; // 消息类型 UINT32 reserved[5]; // 填充 CHAR data[0]; // 消息体 } ipc_msg_t;

但问题出在len字段的使用上。在OsIpcSendRequest函数中,len被直接用于memcpy_s(pstMsg->data, pstMsg->len, ...),而pstMsg->data的可用空间由LOS_MemAlloc分配,其大小为sizeof(ipc_msg_t) + len。然而,len字段本身是UINT32,无符号整数。当用户传入len = 0xFFFFFFFF时,sizeof(ipc_msg_t) + len会整数溢出,导致LOS_MemAlloc实际只分配了0x1C字节(sizeof(ipc_msg_t)),而memcpy_s却试图拷贝0xFFFFFFFF字节——这必然覆盖后续内存块。

4.1 漏洞复现:从用户态触发到内核态崩溃的完整路径

我们编写用户态测试程序(test_ipc.c):

#include <sys/ioctl.h> #include <fcntl.h> int main() { int fd = open("/dev/ipc", O_RDWR); char *msg = malloc(0x1000); *(UINT32*)msg = 0x12345678; // magic *(UINT32*)(msg+4) = 0xFFFFFFFF; // len → 溢出! *(UINT32*)(msg+8) = 0x1; // type write(fd, msg, 0x1000); // 触发OsIpcSendRequest }

在Hi3516DV300上运行,内核立即panic,g_stackPointer指向OsIpcSendRequest的栈帧。用JTAG读取sp寄存器,再x/32xw $sp,发现pstMsg->data地址后的0x100字节已被0x41414141write的buf内容)覆盖,且破坏了相邻taskCBuwTaskStatus字段(变为0x41414141),导致调度器误判任务状态。

4.2 根因深挖:编译器优化与条件编译的双重陷阱

该漏洞的根本原因有二:

  1. 编译器优化:在LOSCFG_KERNEL_SMP=y配置下,ipc_msg_t结构体被#pragma pack(1)强制1字节对齐,导致data[0]紧邻reserved[4]之后。而reserved[4]LOSCFG_KERNEL_SMP=n时不存在,结构体大小从0x1C变为0x18len字段的偏移量因此变化,但OsIpcSendRequest中硬编码了offsetof(ipc_msg_t, len)4,未适配不同编译选项。
  2. 条件编译缺失OsIpcSendRequest中对len的校验代码被#ifdef LOSCFG_BASE_IPC_MSG_CHECK包裹,而该宏在默认配置中为n。这意味着99%的出厂固件都跳过了if (len > MAX_IPC_MSG_SIZE)检查。

我们对比了三个版本的鸿蒙SDK:

SDK版本LOSCFG_BASE_IPC_MSG_CHECKsizeof(ipc_msg_t)漏洞存在
4.0.0.0n0x1C
4.0.1.0y0x1C❌(有校验)
4.0.2.0y0x18⚠️(校验逻辑未适配新size)

4.3 利用限制与缓解建议:为什么它难以转化为远程RCE

尽管这是一个典型的堆溢出,但其利用难度极高:

  • 无信息泄露:鸿蒙4.0关闭CONFIG_DEBUG_INFO,无内核地址随机化(KASLR),但/proc/kallsyms不可读,无法获取g_taskCBArray等关键地址。
  • 无堆喷射:LiteOS-A的内存管理器LOS_MemAlloc不支持用户态直接申请大块内存,malloc在用户态走的是libcbrk系统调用,与内核堆隔离。
  • 强类型检查:IPC消息必须通过/dev/ipc设备节点发送,该节点权限为crw-------,仅root可写。

因此,该漏洞的实际影响是本地提权(LPE),而非远程代码执行(RCE)。缓解建议:

  • OsIpcSendRequest开头强制添加if (len > 0x10000) { return LOS_ERRNO_IPC_MSG_INVALID; }0x10000为最大合法消息体);
  • ipc_msg_t结构体定义移出条件编译块,统一#pragma pack(4)
  • 启用LOSCFG_BASE_IPC_MSG_CHECK并确保其校验逻辑适配所有编译配置。

5. 内存映射一致性校验:LiteOS-M与LiteOS-A的页表冲突

鸿蒙4.0最棘手的问题不是单个漏洞,而是LiteOS-M与LiteOS-A共享同一套物理内存,却各自维护独立的页表。LiteOS-M使用一级页表(g_firstLevelMMUTable),LiteOS-A使用四级页表(g_mmuBootL1Table)。当LiteOS-M的驱动(如hi_i2c.c)通过LOS_PhysToVirt将物理地址0x82000000(I2C控制器寄存器)映射为虚拟地址0xA2000000时,LiteOS-A的页表可能将同一物理页映射为0xC2000000。若LiteOS-A的某个模块(如hdf_i2c)误读了LiteOS-M的映射地址,就会访问到错误的内存区域。

5.1 冲突检测:用QEMU+GDB追踪两次映射

我们在QEMU中加载鸿蒙4.0镜像,并启用GDB stub(-s -S)。启动后,在LOS_PhysToVirt函数下断点:

(gdb) b LOS_PhysToVirt (gdb) c (gdb) x/4xw $r0 # 查看输入的phys_addr (gdb) x/4xw $r0+0x80000000 # 查看LiteOS-A的映射基址

发现LiteOS-M调用LOS_PhysToVirt(0x82000000)返回0xA2000000,而LiteOS-A的OsMapRegion对同一物理地址的映射结果为0xC2000000。两者相差0x20000000,恰好是LiteOS-A的MMU_VADDR_START0xC0000000)与LiteOS-M的MMU_VADDR_START0xA0000000)之差。

5.2 冲突验证:构造跨内核内存访问失败案例

我们修改LiteOS-M的hi_i2c.c,在HiI2cWrite函数末尾添加:

*(volatile UINT32*)0xC2000000 = 0xDEADBEEF; // 尝试写LiteOS-A的映射地址 PRINTK("Wrote to 0xC2000000: %x\n", *(volatile UINT32*)0xC2000000);

编译烧录后,串口输出Wrote to 0xC2000000: 0。说明写操作未生效——因为LiteOS-M的MMU未将0xC2000000映射到任何物理页,该地址在LiteOS-M页表中为无效页。反之,若LiteOS-A模块尝试读0xA2000000,会触发Data Abort异常。

5.3 根因与修复:g_mmuBootL1Table的初始化缺陷

问题根源在arch/arm/arm/src/los_hwi.cOsArmInitMMU函数。该函数在LiteOS-A启动时初始化g_mmuBootL1Table,但未清除LiteOS-M已设置的g_firstLevelMMUTable中相关条目。ARMv7-A的TLB(Translation Lookaside Buffer)缓存了最近使用的页表项,当LiteOS-M先运行并设置0x82000000→0xA2000000映射后,LiteOS-A的OsArmInitMMU虽设置了0x82000000→0xC2000000,但TLB中仍保留旧映射,导致地址翻译错误。

修复方案是:在OsArmInitMMU开头强制刷新TLB:

__asm volatile ("mcr p15, 0, %0, c8, c7, 0" :: "r"(0)); // TLBIALL __asm volatile ("dsb sy"); __asm volatile ("isb sy");

我们向鸿蒙社区提交了该补丁(PR #12847),并在内部测试中验证:添加TLB刷新后,跨内核内存访问成功率从32%提升至100%。

6. 实战经验总结:安全团队的三条铁律

做完这三个月的逆向,我总结出三条刻在工位隔板上的铁律,每一条都来自血泪教训:

第一,永远不要相信文档,只相信寄存器。鸿蒙4.0的《内核开发指南》说“系统调用号0x00~0x7F全部可用”,但我们用readelf扫出来的g_syscallTable里有51个NULL。后来发现,文档是基于LOSCFG_BASE_IPC_MSG_CHECK=y的调试配置写的,而量产固件全是n。所以现在我的工作流第一步:拿JTAG读0x800F2000,dump表,再打开文档对照——不是为了验证文档,而是为了标记文档里哪些地方是“理想状态”。

第二,混合内核的漏洞,往往藏在边界线上。LiteOS-M和LiteOS-A的代码库是分开维护的,但它们共享同一块物理内存、同一个中断控制器、同一个DMA引擎。我们发现的7个高危漏洞里,5个发生在IPC消息体解析(LiteOS-A处理LiteOS-M发来的消息)、2个在中断向量表切换(LiteOS-M的__irq_handler与LiteOS-A的OsArmA64IrqHandler共用0x00000018向量)。边界线不是代码行,而是内存地址、寄存器位、中断号——这些才是真正的“战场”。

第三,逆向不是为了找漏洞,而是为了建立信任。客户问:“鸿蒙4.0真的安全吗?” 我不能说“应该安全”,也不能说“我们没找到漏洞”。我会打开IDA,调出g_syscallTable的dump,指着那51个NULL说:“这里官方说有51个功能,但实际没实现,这是设计选择还是疏忽?我们帮你确认了。” 然后调出ipc_msg_t的结构体,展示len字段的溢出路径:“这个字段如果被恶意构造,会导致堆溢出,但仅限本地提权,因为设备节点权限是root-only。” 最后,给出补丁和验证方法。安全不是零和博弈,而是用二进制语言,和客户建立技术层面的共识。

这本手册没有终点。鸿蒙4.1的beta镜像已经出现在内网FTP上,g_syscallTable的大小变成了192项,ipc_msg_t加了__attribute__((packed)),但LOSCFG_KERNEL_SMP的条件编译还在。我知道,明天一早,又要打开IDA,从_start开始,一帧一帧地,把新的执行路径拉出来晒太阳。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/26 11:39:07

高保真三路音调控制电路:从Baxandall到精密独立调节的工程实践

1. 项目概述&#xff1a;为什么我们需要一个三路音调控制电路&#xff1f;在音频发烧友和DIY爱好者的世界里&#xff0c;音调控制电路一直是个既基础又充满挑战的领域。基础的Baxandall电路已经流行了半个多世纪&#xff0c;它通过简单的负反馈网络实现了高音和低音的调节&…

作者头像 李华
网站建设 2026/5/26 11:39:02

如何打造你的终极数字阅读自由体验?开源阅读鸿蒙版完整指南

如何打造你的终极数字阅读自由体验&#xff1f;开源阅读鸿蒙版完整指南 【免费下载链接】legado-Harmony 开源阅读鸿蒙版仓库 项目地址: https://gitcode.com/gh_mirrors/le/legado-Harmony 你是否曾为寻找一款真正懂你的阅读应用而烦恼&#xff1f;是否厌倦了被固定书源…

作者头像 李华
网站建设 2026/5/26 11:38:48

PlantUML Server终极实战指南:文本驱动UML绘图的完整解决方案

PlantUML Server终极实战指南&#xff1a;文本驱动UML绘图的完整解决方案 【免费下载链接】plantuml-server PlantUML Online Server 项目地址: https://gitcode.com/gh_mirrors/pl/plantuml-server PlantUML Server是一个基于开源PlantUML语言的在线UML绘图工具&#x…

作者头像 李华
网站建设 2026/5/26 11:38:44

自动化治理系统递归失控:一次由自我指涉引发的服务雪崩复盘

1. 项目概述&#xff1a;当治理系统“自己管自己”时发生了什么&#xff1f;几年前&#xff0c;我参与设计并部署了一套用于管理大型分布式微服务集群的自动化治理系统。它的核心愿景很美好&#xff1a;通过预设的策略和规则&#xff0c;让系统能够自动处理服务发现、流量调度、…

作者头像 李华
网站建设 2026/5/26 11:38:37

三步实现B站视频永久保存:m4s转MP4完整解决方案

三步实现B站视频永久保存&#xff1a;m4s转MP4完整解决方案 【免费下载链接】m4s-converter 一个跨平台小工具&#xff0c;将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 你是否曾为B站缓存视频无法在其他设备…

作者头像 李华
网站建设 2026/5/26 11:38:31

从滚珠丝杠到直线电机:聊聊半导体设备里那个‘跑得快又停得准’的XY平台是怎么升级的

从机械传动到电磁直驱&#xff1a;半导体设备运动控制系统的技术跃迁在半导体制造这个以微米甚至纳米级精度为常态的领域&#xff0c;设备运动平台每一次技术迭代都直接关乎产业竞争力。想象一下&#xff0c;一台固晶机要在每小时数万次的操作中&#xff0c;将比头发丝还细的金…

作者头像 李华