mcpField
B
binary-analysis-patterns

binary-analysis-patterns

github.com/oimiragieo安全4466 次安装17 周前更新
代码仓库

关于 binary-analysis-patterns

面向授权防御性安全分析的编译二进制参考技能,汇集 x86、ARM 的反汇编、控制流、数据结构及反编译模式。

主要功能

  1. 01x86-64 与 ARM 汇编模式
  2. 02调用约定参考
  3. 03控制流与数据结构模式
  4. 04反编译与类型恢复
  5. 05Ghidra 和 IDA Pro 分析提示
  6. 06静态分析安全实践

适用场景

  1. 01初步检查陌生二进制文件
  2. 02还原函数和数据结构
  3. 03对照反汇编检查反编译结果

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/oimiragieo/agent-studio --skill binary-analysis-patterns

安装后在 Claude Code 中输入/binary-analysis-patterns确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

commands/
binary-analysis-patterns.md
hooks/
post-execute.cjs
pre-execute.cjs
references/
research-requirements.md
rules/
binary-analysis-patterns.md
schemas/
input.schema.json
output.schema.json
scripts/
main.cjs
templates/
implementation-template.md

详细介绍

二进制分析模式

安全声明

仅限授权用途:这些技能仅用于防御性安全分析和经授权的研究:

  • 获得书面授权的渗透测试活动
  • CTF 比赛和安全研究
  • 防御性安全和恶意软件分析
  • 遵循适当披露流程的安全研究
  • 受控环境中的教育用途

切勿用于:

  • 创建或增强恶意代码
  • 未经授权访问系统
  • 以不正当方式绕过软件许可
  • 窃取知识产权
  • 任何非法活动

全面介绍分析编译后二进制文件、理解汇编代码和重建程序逻辑的模式与技术。

反汇编基础

x86-64 指令模式
函数序言/尾声
; Standard prologue
push rbp           ; Save base pointer
mov rbp, rsp       ; Set up stack frame
sub rsp, 0x20      ; Allocate local variables

; Leaf function (no calls)
; May skip frame pointer setup
sub rsp, 0x18      ; Just allocate locals

; Standard epilogue
mov rsp, rbp       ; Restore stack pointer
pop rbp            ; Restore base pointer
ret

; Leave instruction (equivalent)
leave              ; mov rsp, rbp; pop rbp
ret
调用约定

System V AMD64(Linux、macOS)

; Arguments: RDI, RSI, RDX, RCX, R8, R9, then stack
; Return: RAX (and RDX for 128-bit)
; Caller-saved: RAX, RCX, RDX, RSI, RDI, R8-R11
; Callee-saved: RBX, RBP, R12-R15

; Example: func(a, b, c, d, e, f, g)
mov rdi, [a]       ; 1st arg
mov rsi, [b]       ; 2nd arg
mov rdx, [c]       ; 3rd arg
mov rcx, [d]       ; 4th arg
mov r8, [e]        ; 5th arg
mov r9, [f]        ; 6th arg
push [g]           ; 7th arg on stack
call func

Microsoft x64(Windows)

; Arguments: RCX, RDX, R8, R9, then stack
; Shadow space: 32 bytes reserved on stack
; Return: RAX

; Example: func(a, b, c, d, e)
sub rsp, 0x28      ; Shadow space + alignment
mov rcx, [a]       ; 1st arg
mov rdx, [b]       ; 2nd arg
mov r8, [c]        ; 3rd arg
mov r9, [d]        ; 4th arg
mov [rsp+0x20], [e] ; 5th arg on stack
call func
add rsp, 0x28
ARM 汇编模式
ARM64(AArch64)调用约定
; Arguments: X0-X7
; Return: X0 (and X1 for 128-bit)
; Frame pointer: X29
; Link register: X30

; Function prologue
stp x29, x30, [sp, #-16]!  ; Save FP and LR
mov x29, sp                 ; Set frame pointer

; Function epilogue
ldp x29, x30, [sp], #16    ; Restore FP and LR
ret
ARM32 调用约定
; Arguments: R0-R3, then stack
; Return: R0 (and R1 for 64-bit)
; Link register: LR (R14)

; Function prologue
push {fp, lr}
add fp, sp, #4

; Function epilogue
pop {fp, pc}    ; Return by popping PC

控制流模式

条件分支
; if (a == b)
cmp eax, ebx
jne skip_block
; ... if body ...
skip_block:

; if (a < b) - signed
cmp eax, ebx
jge skip_block    ; Jump if greater or equal
; ... if body ...
skip_block:

; if (a < b) - unsigned
cmp eax, ebx
jae skip_block    ; Jump if above or equal
; ... if body ...
skip_block:
循环模式
; for (int i = 0; i < n; i++)
xor ecx, ecx           ; i = 0
loop_start:
cmp ecx, [n]           ; i < n
jge loop_end
; ... loop body ...
inc ecx                ; i++
jmp loop_start
loop_end:

; while (condition)
jmp loop_check
loop_body:
; ... body ...
loop_check:
cmp eax, ebx
jl loop_body

; do-while
loop_body:
; ... body ...
cmp eax, ebx
jl loop_body
Switch 语句模式
; Jump table pattern
mov eax, [switch_var]
cmp eax, max_case
ja default_case
jmp [jump_table + eax*8]

; Sequential comparison (small switch)
cmp eax, 1
je case_1
cmp eax, 2
je case_2
cmp eax, 3
je case_3
jmp default_case

数据结构模式

数组访问
; array[i] - 4-byte elements
mov eax, [rbx + rcx*4]        ; rbx=base, rcx=index

; array[i] - 8-byte elements
mov rax, [rbx + rcx*8]

; Multi-dimensional array[i][j]
; arr[i][j] = base + (i * cols + j) * element_size
imul eax, [cols]
add eax, [j]
mov edx, [rbx + rax*4]
结构体访问
struct Example {
    int a;      // offset 0
    char b;     // offset 4
    // padding  // offset 5-7
    long c;     // offset 8
    short d;    // offset 16
};
; Accessing struct fields
mov rdi, [struct_ptr]
mov eax, [rdi]         ; s->a (offset 0)
movzx eax, byte [rdi+4] ; s->b (offset 4)
mov rax, [rdi+8]       ; s->c (offset 8)
movzx eax, word [rdi+16] ; s->d (offset 16)
链表遍历
; while (node != NULL)
list_loop:
test rdi, rdi          ; node == NULL?
jz list_done
; ... process node ...
mov rdi, [rdi+8]       ; node = node->next (assuming next at offset 8)
jmp list_loop
list_done:

常见代码模式

字符串操作
; strlen pattern
xor ecx, ecx
strlen_loop:
cmp byte [rdi + rcx], 0
je strlen_done
inc ecx
jmp strlen_loop
strlen_done:
; ecx contains length

; strcpy pattern
strcpy_loop:
mov al, [rsi]
mov [rdi], al
test al, al
jz strcpy_done
inc rsi
inc rdi
jmp strcpy_loop
strcpy_done:

; memcpy using rep movsb
mov rdi, dest
mov rsi, src
mov rcx, count
rep movsb
算术模式
; Multiplication by constant
; x * 3
lea eax, [rax + rax*2]

; x * 5
lea eax, [rax + rax*4]

; x * 10
lea eax, [rax + rax*4]  ; x * 5
add eax, eax            ; * 2

; Division by power of 2 (signed)
mov eax, [x]
cdq                     ; Sign extend to EDX:EAX
and edx, 7              ; For divide by 8
add eax, edx            ; Adjust for negative
sar eax, 3              ; Arithmetic shift right

; Modulo power of 2
and eax, 7              ; x % 8
位操作
; Test specific bit
test eax, 0x80          ; Test bit 7
jnz bit_set

; Set bit
or eax, 0x10            ; Set bit 4

; Clear bit
and eax, ~0x10          ; Clear bit 4

; Toggle bit
xor eax, 0x10           ; Toggle bit 4

; Count leading zeros
bsr eax, ecx            ; Bit scan reverse
xor eax, 31             ; Convert to leading zeros

; Population count (popcnt)
popcnt eax, ecx         ; Count set bits

反编译模式

变量恢复
; Local variable at rbp-8
mov qword [rbp-8], rax  ; Store to local
mov rax, [rbp-8]        ; Load from local

; Stack-allocated array
lea rax, [rbp-0x40]     ; Array starts at rbp-0x40
mov [rax], edx          ; array[0] = value
mov [rax+4], ecx        ; array[1] = value
函数签名恢复
; Identify parameters by register usage
func:
    ; rdi used as first param (System V)
    mov [rbp-8], rdi    ; Save param to local
    ; rsi used as second param
    mov [rbp-16], rsi
    ; Identify return by RAX at end
    mov rax, [result]
    ret
类型恢复
; 1-byte operations suggest char/bool
movzx eax, byte [rdi]   ; Zero-extend byte
movsx eax, byte [rdi]   ; Sign-extend byte

; 2-byte operations suggest short
movzx eax, word [rdi]
movsx eax, word [rdi]

; 4-byte operations suggest int/float
mov eax, [rdi]
movss xmm0, [rdi]       ; Float

; 8-byte operations suggest long/double/pointer
mov rax, [rdi]
movsd xmm0, [rdi]       ; Double

Ghidra 分析技巧

改进反编译效果
// In Ghidra scripting
// Fix function signature
Function func = getFunctionAt(toAddr(0x401000));
func.setReturnType(IntegerDataType.dataType, SourceType.USER_DEFINED);

// Create structure type
StructureDataType struct = new StructureDataType("MyStruct", 0);
struct.add(IntegerDataType.dataType, "field_a", null);
struct.add(PointerDataType.dataType, "next", null);

// Apply to memory
createData(toAddr(0x601000), struct);
模式匹配脚本
# Find all calls to dangerous functions
for func in currentProgram.getFunctionManager().getFunctions(True):
    for ref in getReferencesTo(func.getEntryPoint()):
        if func.getName() in ["strcpy", "sprintf", "gets"]:
            print(f"Dangerous call at {ref.getFromAddress()}")

IDA Pro 模式

IDAPython 分析
import idaapi
import idautils
import idc

# Find all function calls
def find_calls(func_name):
    for func_ea in idautils.Functions():
        for head in idautils.Heads(func_ea, idc.find_func_end(func_ea)):
            if idc.print_insn_mnem(head) == "call":
                target = idc.get_operand_value(head, 0)
                if idc.get_func_name(target) == func_name:
                    print(f"Call to {func_name} at {hex(head)}")

# Rename functions based on strings
def auto_rename():
    for s in idautils.Strings():
        for xref in idautils.XrefsTo(s.ea):
            func = idaapi.get_func(xref.frm)
            if func and "sub_" in idc.get_func_name(func.start_ea):
                # Use string as hint for naming
                pass

最佳实践

分析工作流
  1. 初步筛查:文件类型、架构、导入项/导出项
  2. 字符串分析:识别有意义的字符串和错误消息
  3. 函数识别:入口点、导出项、交叉引用
  4. 控制流映射:了解程序结构
  5. 数据结构恢复:识别结构体、数组、全局变量
  6. 算法识别:加密、哈希、压缩
  7. 文档记录:注释、重命名的符号、类型定义
常见陷阱
  • 优化器产物:代码结构可能与源代码不同
  • 内联函数:函数可能被展开为内联代码
  • 尾调用优化:使用 jmp 而非 call + ret
  • 死代码:优化产生的不可达代码
  • 位置无关代码:基于 RIP 的相对寻址

铁律

  1. 务必先进行静态分析,再执行任何动态操作——先反汇编并映射控制流;未进行静态分析就执行不可信二进制文件会带来安全风险,还会破坏证据的可复现性。
  2. 切勿将反编译器的输出视为事实依据——反编译器生成的是近似结果;对于安全关键路径,务必将反编译结果与原始反汇编代码交叉核对。
  3. 切勿假设程序会顺序执行——间接跳转、虚分派表和 JIT 编译代码都会打破线性流程;追踪代码路径前,务必检查交叉引用和跳转表。
  4. 务必在开始时记录架构和调用约定——x86、x64、ARM 和 MIPS 的调用约定各不相同;识别错误会导致所有参数和返回值分析都出错。
  5. 务必将未经确认的假设标记为尚未验证,直至通过动态分析确认——静态分析并不完整;为每项推断出的行为标记 [STATIC-ONLY],并尽可能使用动态跟踪证据进行验证。

反模式

反模式失败原因正确做法
在静态分析前执行二进制文件破坏取证状态;存在安全风险先通过静态分析梳理入口点和控制流
完全相信反编译器的输出反编译器会引入伪影和错误对关键路径交叉核对原始反汇编
假设代码流是线性的会遗漏间接跳转、虚函数表和 JIT 路径跟踪前检查所有交叉引用和跳转表
跳过架构文档调用约定错误会使所有分析失效开始任何分析前记录架构和 ABI
将静态推断视为已确认未经动态验证的推断可能有误在运行时跟踪确认前标记为 [STATIC-ONLY]

内存协议(必须遵守)

开始前: 阅读 C:\dev\projects\agent-studio\.claude\context\memory\learnings.md

完成后:

  • 新模式 -> C:\dev\projects\agent-studio\.claude\context\memory\learnings.md
  • 发现问题 -> C:\dev\projects\agent-studio\.claude\context\memory\issues.md
  • 做出决策 -> C:\dev\projects\agent-studio\.claude\context\memory\decisions.md

假设会被中断:如果没有记录在内存中,就等于没有发生。

评论

支持 Markdown