ARM与MIPS指令集逆向实战:从环境搭建到Mirai僵尸网络深度解析 1. 项目概述与核心价值最近在分析一些物联网僵尸网络的样本特别是Mirai及其变种发现一个挺有意思的现象很多刚入行的朋友一看到ARM或者MIPS架构的ELF文件就有点发怵。x86的汇编好歹在学校里多少接触过但碰到这些嵌入式架构的指令感觉就像在看天书IDA反编译出来的代码逻辑支离破碎分析起来寸步难行。这其实是一个很现实的痛点因为当今的物联网恶意软件其战场早已从传统的PC端转移到了数以亿计的摄像头、路由器、智能设备上而这些设备的“心脏”绝大多数正是ARM和MIPS处理器。我手头这个名为“Mirai恶意软件逆向分析指南ARM与MIPS指令集深度解析”的项目就是想解决这个问题。它不是什么高深的理论研究而是一份实打实的、面向安全分析工程师和逆向爱好者的实战手册。核心目标很明确当你拿到一个针对IoT设备的、ARM或MIPS架构的Mirai变种样本时这份指南能帮你快速理解其汇编代码的逻辑定位关键功能点比如C2通信、漏洞利用模块、进程隐藏手段等从而完成从“看得见二进制”到“看得懂它在干什么”的跨越。为什么非得啃ARM和MIPS从搜索的热词就能看出大家的关注点单周期mips cpu设计、arm compiler 5、qemu模拟arm开发板……这背后反映的是IoT安全的研究门槛正在从单纯的漏洞挖掘和网络分析下探到底层的二进制逆向层面。Mirai家族之所以生命力如此顽强正是因为它能跨平台编译适配多种CPU架构。就像资料里提到的从x86、ARM、MIPS到甚至不那么常见的PowerPC和ARC它都能感染。因此掌握这两种主流嵌入式指令集就等于拿到了打开大多数IoT恶意软件黑盒的钥匙。这份指南适合谁如果你是安全分析师需要对捕获的IoT僵尸程序进行行为剖析和家族归类如果你是恶意软件研究员希望深入理解其模块化设计和对抗技巧甚至如果你是嵌入式开发或计算机体系结构的学生想看看理论上的指令集在真实的恶意代码中是如何被“玩出花”的那么接下来的内容都会对你有所帮助。我们不空谈理论所有解析都将紧扣Mirai样本中的真实代码片段用逆向的视角把那些生硬的指令助记符还原成攻击者的逻辑意图。2. 逆向环境搭建与工具链配置工欲善其事必先利其器。分析ARM/MIPS架构的恶意软件第一步就是搭建一个称手的逆向环境。这不仅仅是装个IDA Pro那么简单你需要一套能够处理跨架构二进制文件的完整工具链以及一个可以安全运行和动态调试样本的沙箱环境。2.1 静态分析工具选型与配置静态分析是我们的主战场核心工具自然是反汇编器。1. IDA Pro 相应处理器模块这是行业标准。确保你的IDA Pro安装了ARM和MIPS的处理器模块。对于ARM架构需要特别注意区分ARM模式32位ARM指令和Thumb模式16位/32位混合指令。Mirai为了减小体积其ARM版本的功能函数经常使用Thumb指令集编译。在IDA加载文件时如果识别有误需要手动在Processor type中选择正确的类型如ARM little-endian, MIPS little-endian。一个实用技巧是查看ELF文件头readelf -h中的Machine字段EM_ARM对应ARMEM_MIPS对应MIPS。2. Ghidra作为NSA开源的工具Ghidra在反编译跨架构二进制文件方面表现非常出色且完全免费。它对ARM和MIPS的反编译逻辑比IDA的旧版本更健壮尤其是在处理某些编译器优化过的代码时。你可以将Ghidra作为IDA的补充交叉验证反编译结果。3. 交叉编译工具链Cross-Compile Toolchain这是很多人会忽略但极其重要的一环。我们为什么需要它两个核心用途编译分析辅助工具比如你想写个小的Shellcode在模拟环境里测试或者编译一个特定的库文件来验证样本的导入函数。理解编译约定不同的工具链如arm-linux-gnueabi-gccvsarm-linux-musleabi-gcc在函数调用约定、栈帧布局上可能有细微差别。Mirai的编译环境通常是musl-libc和uClibc这类为嵌入式优化的C库使用对应的工具链能帮助你更准确地理解栈和寄存器的使用。如何获取对于Debian/Ubuntu系统可以直接用apt安装# 用于ARM架构软浮点常见于旧款路由器 sudo apt install gcc-arm-linux-gnueabi binutils-arm-linux-gnueabi # 用于ARM架构硬浮点性能更高 sudo apt install gcc-arm-linux-gnueabihf binutils-arm-linux-gnueabihf # 用于MIPS架构小端序常见 sudo apt install gcc-mipsel-linux-gnu binutils-mipsel-linux-gnu # 用于MIPS架构大端序部分网络设备 sudo apt install gcc-mips-linux-gnu binutils-mips-linux-gnu4. 基础命令行工具file快速查看文件类型和架构。readelf查看ELF文件头、节区、符号表、动态链接信息。命令readelf -a sample.bin能输出大量信息。objdump进行反汇编。objdump -d -M intel sample.bin对于x86或objdump -d sample.bin可以快速浏览代码段有时比图形化工具更快。strings提取文件中的字符串是寻找C2地址、硬编码凭证、调试信息的第一步。注意从网络获取的恶意软件样本绝对不要在联网的主机或生产环境中直接运行。所有动态分析都必须在隔离的虚拟环境或专用沙箱中进行。2.2 动态分析与调试环境搭建静态分析能看逻辑动态分析能看行为。对于Mirai这类包含网络通信、进程操作等行为的样本动态调试至关重要。1. QEMU用户模式模拟QEMU User Mode这是最轻量、最方便的跨架构二进制运行方式。它允许你在x86主机上直接运行ARM/MIPS的ELF可执行文件。# 安装QEMU用户模式支持 sudo apt install qemu-user qemu-user-static # 运行动态链接的ARM样本需要对应的ARM版libc库 # 可以先使用chroot到一个包含ARM根文件系统的目录或者使用qemu-arm -L /path/to/arm/libc指定库路径 qemu-arm ./mirai.arm.elf # 运行动态链接的MIPS样本 qemu-mipsel ./mirai.mips.elf对于静态链接的样本Mirai通常就是静态链接以摆脱库依赖直接运行即可qemu-arm-static ./mirai.arm.static.elf。2. 使用GDB进行跨架构调试QEMU内置了GDB服务器支持可以让我们用熟悉的GDB进行调试。# 首先在终端1用QEMU启动样本并等待GDB连接 qemu-arm -g 1234 ./mirai.arm.static.elf # 然后在终端2启动对应架构的GDB需要安装gdb-multiarch或交叉编译的gdb gdb-multiarch ./mirai.arm.static.elf (gdb) target remote localhost:1234 (gdb) break main (gdb) continue现在你就可以像调试本地程序一样设置断点、单步执行、查看寄存器和内存了。这是理解程序流、验证函数功能的无价工具。3. 系统模式模拟与完整沙箱用户模式模拟功能有限无法模拟完整的系统调用如原始套接字、fork等。这时需要QEMU系统模式。你可以准备一个ARM或MIPS架构的Linux内核镜像和根文件系统例如使用buildroot定制一个精简系统在QEMU中启动一个完整的虚拟机。在这个虚拟机里你可以安装strace、ltrace、tcpdump等工具全方位监控样本的行为。虽然搭建稍复杂但对于分析复杂的网络交互和进程关系是必须的。4. 网络行为分析环境Mirai的核心是DDoS攻击因此必须在一个可控的网络环境中观察其行为。建议使用一个独立的虚拟网络如VirtualBox的Host-Only网络或VMware的私有网络将运行样本的QEMU虚拟机或沙箱置于该网络中并在宿主机或另一台虚拟机上运行Wireshark进行抓包。同时可以搭建一个假的C2服务器例如用Python的socket库模拟来接收僵尸程序的“上线”请求从而分析其通信协议。3. ARM指令集核心精要与Mirai代码解析ARM架构以其高能效比统治了移动和嵌入式市场Mirai的ARM变体也是最常见的。理解ARM汇编需要抓住几个与x86截然不同的核心概念。3.1 ARM架构基础与逆向切入点首先ARM是RISC精简指令集架构指令长度固定在ARM模式下为32位指令格式规整。其最显著的特点是大量使用寄存器。ARM有16个通用寄存器R0-R12用于通用计算R13通常用作栈指针SPR14用作链接寄存器LR保存函数返回地址R15是程序计数器PC。在逆向Mirai时第一个挑战往往是识别函数开头Prologue和结尾Epilogue。由于ARM的调用约定AAPCS函数通常这样开始PUSH {R4-R7, LR} ; 将需要保存的寄存器和返回地址LR压栈 ADD R7, SP, #0xC ; 可能设置帧指针FP但编译器优化后常省略 SUB SP, SP, #0x20 ; 在栈上为局部变量分配空间函数结束时ADD SP, SP, #0x20 ; 释放局部变量空间 POP {R4-R7, PC} ; 恢复寄存器并直接将LR弹出到PC实现返回注意POP {..., PC}这个操作它等价于x86的RET。在Thumb模式下由于指令长度限制可能会看到BX LR分支并交换指令集状态来返回。3.2 关键指令模式与Mirai实例剖析ARM指令的一个强大特性是条件执行。几乎每条指令都可以根据状态寄存器CPSR的条件码如EQ, NE, GT, LT来条件执行。这在逆向中会造成一些反编译代码可读性下降需要适应。让我们结合Mirai中常见的代码模式来看1. 内存访问与字符串解密Mirai经常混淆字符串在运行时解密。这涉及到LDR加载和STR存储指令。; 假设R0指向加密字符串R1是密钥循环解密 LDRB R2, [R0] ; 从R0地址加载一个字节到R2 EOR R2, R2, R1 ; R2 R2 XOR R1 (异或解密) STRB R2, [R0] ; 将解密后的字节存回 ADD R0, R0, #1 ; 指针加1 CMP R2, #0 ; 检查是否解密到字符串结束符\0 BNE loop ; 如果不是继续循环这里看到了EOR异或指令它是简单的加密/解密常用操作。在逆向时如果你看到一段循环对一片内存区域进行LDRB-EOR-STRB操作很可能就是在解密字符串R1寄存器里的值就是密钥。2. 系统调用与网络操作ARM下系统调用通过SWI软件中断指令或其别名SVC实现。但在较新的EABI中更常见的是通过svc #0调用。然而在静态链接的二进制中Mirai更可能直接调用包装好的函数。网络相关函数如socket,connect,send的参数传递遵循AAPCSR0-R3传递前四个参数更多参数通过栈传递。; 模拟一个 socket(AF_INET, SOCK_STREAM, 0) 调用 MOV R0, #2 ; AF_INET 2 MOV R1, #1 ; SOCK_STREAM 1 MOV R2, #0 ; protocol 0 BL socket ; 分支并链接到socket函数返回套接字描述符在R0 MOV R4, R0 ; 将套接字fd保存到R4BL指令是“带链接的分支”它会将下一条指令的地址返回地址存入LR寄存器然后跳转到目标函数。这是函数调用的标准方式。3. 条件分支与循环控制Mirai的扫描器或心跳逻辑里充满循环。ARM没有专门的LOOP指令循环通过比较和条件分支实现。MOV R5, #0 ; i 0 loop_start: CMP R5, #100 ; 比较 i 和 100 BGE loop_end ; 如果 i 100跳转到循环结束 ; ... 循环体 ... ADD R5, R5, #1 ; i B loop_start ; 无条件跳回循环开始 loop_end:B是无条件分支JumpBGE是“大于或等于”时的条件分支。理解CMP后的条件码GE, LT, EQ, NE等是理清程序逻辑的关键。4. Thumb指令集识别为了代码密度Mirai大量使用Thumb指令。在IDA中你可能看到代码段在ARM和Thumb模式间切换。地址的最后一位为1表示Thumb模式例如0x8001。在函数开头你可能会看到BX R0这样的指令其中R0的最低位置1用于切换到Thumb模式。Thumb指令多为16位格式更紧凑例如MOVS R0, #0x10。实操心得在静态分析时遇到看似混乱的指令流先不要急于逐句翻译。首先寻找函数边界PUSH/POP模式然后确定关键的系统调用或库函数如socket,connect,fork,execve。Mirai的逻辑模块相对清晰比如初始化、解析C2、扫描、攻击等。找到这些锚点就能把大段的代码划分成功能块逆向效率会大大提高。4. MIPS指令集核心精要与Mirai代码解析MIPS是另一种经典的RISC架构在路由器、网关等网络设备中历史悠久。其指令集设计非常规整强调“流水线友好”但也有一些独特的约定需要掌握。4.1 MIPS架构基础与逆向特点MIPS有32个通用寄存器命名方式为$0到$31但通常使用别名$zero($0): 恒为零。$at($1): 汇编器临时寄存器慎用。$v0,$v1($2,$3): 存放函数返回值。$a0-$a3($4-$7): 传递前四个函数参数。$t0-$t9($8-$15,$24,$25): 临时寄存器调用者保存。$s0-$s7($16-$23): 保存寄存器被调用者保存。$gp($28): 全局指针。$sp($29): 栈指针。$fp($30): 帧指针。$ra($31): 返回地址。MIPS的一个核心特点是延迟槽Delay Slot在分支指令j,jal,beq,bne等之后的一条指令总是会先于分支跳转本身被执行。这在逆向时会造成极大的困惑。现代汇编器和反汇编器通常会帮你处理这个细节但在看原始机器码或某些反编译输出时仍需留意。4.2 关键指令模式与Mirai实例剖析1. 函数调用与返回函数调用使用JAL跳转并链接指令它将下一条指令的地址PC8因为延迟槽存入$ra寄存器然后跳转。JAL sub_function ; 调用函数$ra PC8 NOP ; 延迟槽指令通常为NOP函数返回使用JR $ra跳转到$ra寄存器的地址。函数开头通常会保存$ra和$s系列寄存器到栈上sub_function: ADDIU $sp, $sp, -0x20 ; 在栈上分配空间 SW $ra, 0x1C($sp) ; 保存返回地址 SW $s0, 0x18($sp) ; 保存保存寄存器 ... ; 函数体 LW $ra, 0x1C($sp) ; 恢复返回地址 LW $s0, 0x18($sp) ; 恢复保存寄存器 JR $ra ; 返回 ADDIU $sp, $sp, 0x20 ; 延迟槽指令恢复栈空间注意最后两行JR $ra的延迟槽指令是ADDIU $sp, $sp, 0x20这意味着恢复栈空间的操作在跳转返回之前就已经执行了。这是MIPS逆向必须适应的思维。2. 内存访问与全局数据MIPS使用LW加载字、SW存储字、LB/SB加载/存储字节等指令。由于MIPS指令是32位无法在一条指令内编码一个32位立即数地址因此访问全局变量或静态数据需要两步; 假设要加载一个全局变量 global_var 到 $t0 LUI $t0, %hi(global_var) ; 将 global_var 地址的高16位加载到 $t0 的高位 LW $t1, %lo(global_var)($t0) ; $t0 地址低16位加载数据到 $t1LUI加载高位立即数和LW的组合非常常见。在Mirai中这常用于加载硬编码的IP地址、端口号或字符串常量。3. 算术运算与循环MIPS的算术指令如ADD,ADDIU无符号立即数加,SUB,ANDI等。循环结构类似ARMLI $t0, 0 ; i 0 (LI是伪指令可能被汇编为 LUI/ORI) loop: SLTI $t1, $t0, 100 ; $t1 (i 100) ? 1 : 0 BEQ $t1, $zero, end_loop ; 如果 i 100跳出循环 NOP ; 延迟槽 ... ; 循环体 ADDIU $t0, $t0, 1 ; i J loop NOP ; 延迟槽 end_loop:SLTI小于立即数则置位是常用的比较指令。注意每个分支指令BEQ后面都跟着一个延迟槽指令这里用了NOP。4. 系统调用在MIPS的O32 ABI中系统调用通过将系统调用号放入$v0参数放入$a0-$a3然后执行syscall指令实现。LI $v0, 4001 ; syscall number for sys_socket (可能因内核而异) LI $a0, 2 ; AF_INET LI $a1, 1 ; SOCK_STREAM LI $a2, 0 ; protocol syscall MOV $s0, $v0 ; 保存返回的套接字描述符系统调用号需要查表确定不同内核版本可能不同。在静态链接的二进制中Mirai同样更可能直接调用libc的包装函数。注意事项MIPS有**大端序Big-Endian和小端序Little-Endian**之分。网络设备传统上多用大端序但现代许多MIPS Linux系统也使用小端序。用file命令或查看ELF头可以确定。这直接影响你对多字节数据如IP地址0x0100007F是127.0.0.1还是1.0.0.127的解释务必首先确认。5. Mirai样本关键功能逆向实战掌握了指令集基础我们就可以深入Mirai样本内部看看这些指令是如何组合起来实现恶意功能的。我们以常见的几个模块为例。5.1 C2通信协议解析Mirai僵尸程序上线时会向硬编码的C2服务器发起TCP连接。逆向的第一步就是找到这个连接逻辑。定位字符串在IDA的字符串窗口搜索“.”、“:”或可能的IP地址格式。Mirai早期版本字符串未加密后期版本会加密。如果看到一堆乱码或非ASCII字符很可能就是加密后的C2地址和端口。追踪解密函数找到引用这些加密字符串的代码位置通常附近会有一个循环解密函数如前文所述的XOR循环。分析解密逻辑提取出密钥和明文。分析socket和connect调用找到解密后的IP和端口被传递到哪里。通常你会看到类似下面的模式以ARM为例// 伪C代码对应汇编 int sock socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in server_addr; server_addr.sin_family AF_INET; server_addr.sin_port htons(port); // 端口可能经过变换 server_addr.sin_addr.s_addr inet_addr(ip_str); connect(sock, (struct sockaddr*)server_addr, sizeof(server_addr));在汇编中你需要识别出htons可能是一个内联的字节交换操作和inet_addr可能是一个自定义的解析函数或库调用的实现。通信协议连接建立后僵尸程序会发送一个上线包。通过分析send或write调用前的缓冲区准备过程可以解析出协议格式。Mirai的协议通常包含一个魔数、僵尸的架构标识如“ARM”、“MIPS”、版本号等。5.2 扫描与传播模块分析Mirai以其高效的扫描能力著称。其扫描器通常作为一个独立的进程或线程运行。生成随机IP找到生成目标IP地址的代码。这通常是一个线性同余生成器LCG或类似的伪随机算法。在汇编中你会看到一系列的乘、加、取模运算。连接尝试与漏洞利用针对生成的IP和特定端口如23/Telnet, 2323/定制服务程序会调用connect。如果连接成功紧接着会尝试发送漏洞利用载荷例如发送默认密码列表进行暴力破解或发送一个缓冲区溢出攻击包。你需要分析send发送的数据来源这可能是硬编码在数据段的一段二进制数据Shellcode。多线程/多进程为了加速扫描Mirai会fork多个进程或创建多个线程。寻找fork或pthread_create的调用。在ARM/MIPS中fork的系统调用后需要判断返回值在父进程中返回子进程PID在子进程中返回0。汇编中通常通过比较$v0/R0返回值寄存器和0来实现分支。5.3 攻击模块与命令分发僵尸程序在连接C2后会进入一个命令循环等待并执行攻击指令。命令循环主逻辑通常是一个while(1)循环内部包含recv或read调用从C2套接字读取命令。命令解析接收到的数据包会被解析。可能有一个命令分发表switch-case结构或函数指针表。在汇编中这通常表现为根据某个值命令号进行一系列的比较和条件跳转每个分支跳转到不同的处理函数。攻击向量实现找到处理UDP FLOOD、TCP SYN FLOOD、HTTP FLOOD等攻击命令的函数。这些函数会包含构造特定协议数据包、大量调用sendto或connect/send的逻辑。注意看它是如何伪造源IP、随机化攻击参数的。5.4 持久化与隐藏技巧为了存活Mirai会尝试一些简单的隐藏手段。进程名伪装通过修改argv[0]来改变ps命令显示的名称。这通常通过修改进程内存中argv指针指向的内容来实现。在汇编中可能会看到对__uClibc_main或类似初始化函数之前的内存区域进行写操作。文件自删除执行后删除自身的磁盘文件。这通过unlink系统调用实现。你可能会在main函数开头或结尾附近找到对/proc/self/exe或argv[0]的unlink调用。防调试简单的反调试如检查/proc/self/status中的TracerPid字段或调用ptrace(PTRACE_TRACEME, ...)使自己只能被跟踪一次。在汇编中这表现为打开文件、读取内容、字符串比较等操作。实操心得逆向这类IoT恶意软件不要试图一次性理解全部代码。采用“自顶向下”和“自底向上”结合的方式。先通过字符串、导入函数如果是动态链接找到关键函数入口如main、start然后顺着主流程看。同时对于像memcpy、strcmp、inet_addr这类常见的库函数要能快速在汇编中识别其模式例如strcmp通常是一个循环逐字节比较直到遇到\0。善用IDA的“重命名”、“添加注释”、“创建结构体”功能让代码变得可读。对于复杂的算法如随机数生成可以尝试将相关的汇编片段提取出来用Python或C模拟运行验证其功能。6. 常见问题与高级调试技巧在实际逆向过程中你肯定会遇到各种拦路虎。这里总结一些典型问题及其解决思路。6.1 静态分析中的常见挑战反编译结果混乱问题IDA或Ghidra的反编译视图出现大量goto、逻辑不通的语句。原因编译器优化尤其是-Os为尺寸优化、指令集切换ARM/Thumb、不标准的栈帧布局以及反编译器对某些指令模式识别不佳。解决手动修正函数边界检查函数开头和结尾的栈操作PUSH/POP, ADDIU $sp是否匹配如果不匹配尝试用IDA的Edit function - Adjust stack pointer。识别跳转表对于switch语句编译器可能生成跳转表。在ARM中可能使用LDR PC, [PC, Rn, LSL #2]在MIPS中可能使用LW $t0, X($gp)然后JR $t0。找到这个表并让IDA识别它Edit - Other - Specify switch idiom。结合动态调试在关键分支点下断点看实际执行流然后回到静态视图修正分析。字符串被加密或混淆问题字符串窗口空空如也或全是乱码。解决查找解密函数在代码中搜索对数据段的循环访问LDRB/LB指令在循环中附近常有异或、加减等算术指令。写IDAPython脚本一旦找到解密算法例如每个字节与0xAA异或可以写一个简单的IDAPython脚本遍历数据段尝试解密并创建注释或重命名地址。动态提取在调试器中在解密函数执行后直接查看内存中解密缓冲区的内容。识别库函数与系统调用问题静态链接的二进制没有导入表所有库函数代码都内嵌了。解决使用FLIRT签名IDA的FLIRT技术可以识别许多标准库的代码模式。确保加载了对应的签名文件如libcfor ARM/MIPS。特征匹配记住常见函数的汇编特征。例如memcpy通常有循环和LDRB/STRB或LDM/STM指令strlen是一个向前搜索\0的循环。参考开源代码对于uClibc或musl-libc可以下载对应架构的源代码编译后反汇编对比函数开头几行指令这有助于识别。6.2 动态调试中的疑难杂症QEMUGDB调试时程序崩溃或行为异常原因1系统调用不兼容。用户模式QEMU可能无法完美模拟所有系统调用或某些参数。排查在GDB中使用catch syscall命令捕获所有系统调用观察是哪个调用导致了问题。有时需要切换到完整的系统模式QEMU进行调试。原因2环境变量或文件系统缺失。程序可能依赖/proc、/dev下的特定文件。排查使用strace -f -o log.txt qemu-xxx ./sample运行查看程序尝试访问了哪些资源然后在调试环境中创建这些资源如空的/proc/net/route文件。如何调试fork出的子进程GDB配置在GDB中使用set follow-fork-mode child命令可以让调试器在fork后自动跟踪子进程。对于多进程扫描这非常有用。QEMU系统模式在完整的QEMU虚拟机中你可以使用gdbserver附加到任意进程gdbserver :1234 --attach PID然后从宿主机GDB连接。网络行为抓取不全问题在用户模式QEMU中样本的网络通信可能走主机的网络栈抓包容易但某些原始套接字操作可能受限。解决在系统模式QEMU中将虚拟机的网卡连接到宿主机的虚拟网桥或TAP设备上然后在宿主机上对该网络接口抓包。这样可以捕获到最原始的网络流量。6.3 高级分析与对抗技巧反反调试如果样本有反调试可以尝试以下方法Patch二进制在静态分析中找到反调试检查的代码例如比较TracerPid的指令用IDA将条件跳转如BNE修改为无条件跳转B或其相反条件BEQ然后保存为新文件再调试。使用调试器脚本在GDB中可以在反调试函数入口设置断点然后直接修改寄存器或内存的值来绕过检查。例如让ptrace调用返回0成功而非-1失败。提取配置与IOC最终目标之一是提取攻击指标IoC。C2地址与端口通过字符串解密和网络连接逻辑分析获得。扫描目标端口列表在数据段寻找连续的WORD或DWORD数组可能就是它要扫描的端口列表。漏洞利用载荷/默认密码字典在代码中寻找大块的、看似随机的数据区域很可能就是内嵌的Shellcode或密码列表。可以将其提取出来用Python或C写个小程序模拟解密或执行流程验证其功能。编写YARA规则基于逆向出的特征可以编写YARA规则来检测同类样本。特征可以包括特定代码序列如独特的字符串解密循环。硬编码的魔数通信协议中的固定字节。导入函数特征虽然静态链接但内部函数调用图仍有特征。节区名或文件哈希较弱的特征但可作辅助。逆向分析是一个需要耐心和细致的过程尤其是面对不同架构的二进制文件。从搭建环境、熟悉指令集到一步步跟踪代码、理解逻辑每一步都可能遇到坑。但每解开一个混淆字符串每理清一个功能模块你对整个恶意软件生态的理解就会加深一分。对于Mirai这类样本其代码结构在跨架构版本中具有高度相似性熟练分析一个架构后再分析另一个会快很多。最重要的是养成系统性的分析方法先整体后局部动静结合大胆假设小心验证。