Skip to main content
Glama
patch_utils.py10.7 kB
"""二进制修改工具""" import os from typing import Optional, List, Dict, Union try: import lief LIEF_AVAILABLE = True except ImportError: LIEF_AVAILABLE = False try: from capstone import Cs, CS_ARCH_ARM64, CS_ARCH_ARM, CS_MODE_ARM, CS_MODE_LITTLE_ENDIAN CS_MODE_THUMB = 0 # fallback try: from capstone import CS_MODE_THUMB except: pass CAPSTONE_AVAILABLE = True except ImportError: CAPSTONE_AVAILABLE = False def patch_bytes( file_path: str, offset: int, new_bytes: bytes, output_path: Optional[str] = None ) -> dict: """ 在指定偏移处修改字节 Args: file_path: 文件路径 offset: 偏移量 new_bytes: 新字节 output_path: 输出路径(可选,默认覆盖原文件) Returns: dict: {"success": bool, "original": str, "patched": str, "error": str} """ if not os.path.exists(file_path): return {"success": False, "error": f"File not found: {file_path}"} try: with open(file_path, 'rb') as f: data = bytearray(f.read()) if offset < 0 or offset + len(new_bytes) > len(data): return {"success": False, "error": f"Invalid offset: {offset}"} # 保存原始字节 original = bytes(data[offset:offset + len(new_bytes)]) # 修改 data[offset:offset + len(new_bytes)] = new_bytes # 保存 out_path = output_path or file_path with open(out_path, 'wb') as f: f.write(data) return { "success": True, "offset": hex(offset), "original": original.hex(), "patched": new_bytes.hex(), "output_path": out_path, "error": "" } except Exception as e: return {"success": False, "error": str(e)} def search_bytes( file_path: str, pattern: bytes, limit: int = 20 ) -> dict: """ 搜索字节模式 Args: file_path: 文件路径 pattern: 字节模式 limit: 最多返回数量 Returns: dict: {"success": bool, "offsets": list, "error": str} """ if not os.path.exists(file_path): return {"success": False, "offsets": [], "error": f"File not found: {file_path}"} try: with open(file_path, 'rb') as f: data = f.read() offsets = [] start = 0 while len(offsets) < limit: pos = data.find(pattern, start) if pos == -1: break offsets.append({ "offset": pos, "hex_offset": hex(pos) }) start = pos + 1 return { "success": True, "offsets": offsets, "count": len(offsets), "pattern": pattern.hex(), "error": "" } except Exception as e: return {"success": False, "offsets": [], "error": str(e)} def replace_bytes( file_path: str, find_pattern: bytes, replace_with: bytes, output_path: Optional[str] = None, replace_all: bool = False ) -> dict: """ 查找并替换字节 Args: file_path: 文件路径 find_pattern: 查找模式 replace_with: 替换内容 output_path: 输出路径 replace_all: 是否替换所有 Returns: dict: {"success": bool, "replaced_count": int, "error": str} """ if not os.path.exists(file_path): return {"success": False, "replaced_count": 0, "error": f"File not found: {file_path}"} if len(find_pattern) != len(replace_with): return { "success": False, "replaced_count": 0, "error": "Find and replace patterns must be the same length" } try: with open(file_path, 'rb') as f: data = bytearray(f.read()) count = 0 start = 0 while True: pos = data.find(find_pattern, start) if pos == -1: break data[pos:pos + len(replace_with)] = replace_with count += 1 if not replace_all: break start = pos + len(replace_with) if count == 0: return { "success": False, "replaced_count": 0, "error": "Pattern not found" } out_path = output_path or file_path with open(out_path, 'wb') as f: f.write(data) return { "success": True, "replaced_count": count, "find_pattern": find_pattern.hex(), "replace_with": replace_with.hex(), "output_path": out_path, "error": "" } except Exception as e: return {"success": False, "replaced_count": 0, "error": str(e)} def disassemble( so_path: str, address: int, size: int = 64, arch: str = "auto" ) -> dict: """ 反汇编指定地址的代码 Args: so_path: SO文件路径 address: 起始地址(文件偏移) size: 字节数 arch: 架构(auto/arm64/arm) Returns: dict: {"success": bool, "instructions": list, "error": str} """ if not CAPSTONE_AVAILABLE: return { "success": False, "instructions": [], "error": "capstone not available. Install: pip install capstone" } if not os.path.exists(so_path): return {"success": False, "instructions": [], "error": f"File not found: {so_path}"} try: with open(so_path, 'rb') as f: data = f.read() # 自动检测架构 if arch == "auto": if LIEF_AVAILABLE: binary = lief.parse(so_path) if binary and hasattr(binary, 'header'): arch_name = binary.header.machine_type.name if hasattr(binary.header.machine_type, 'name') else "" if "AARCH64" in arch_name or "ARM64" in arch_name.upper(): arch = "arm64" else: arch = "arm" else: arch = "arm64" # 默认 else: arch = "arm64" # 配置反汇编器 if arch == "arm64": md = Cs(CS_ARCH_ARM64, CS_MODE_ARM) else: md = Cs(CS_ARCH_ARM, CS_MODE_ARM + CS_MODE_THUMB) # 提取代码 if address < 0 or address + size > len(data): return {"success": False, "instructions": [], "error": f"Invalid address range: {address} + {size} > {len(data)}"} code = bytes(data[address:address + size]) # 反汇编 instructions = [] try: for insn in md.disasm(code, address): instructions.append({ "address": hex(insn.address), "bytes": insn.bytes.hex(), "mnemonic": insn.mnemonic, "operands": insn.op_str, "text": f"{insn.mnemonic} {insn.op_str}" }) except Exception as disasm_err: # 如果反汇编失败,返回原始字节 return { "success": True, "instructions": [], "raw_bytes": code.hex(), "count": 0, "architecture": arch, "error": f"Disassembly failed: {disasm_err}, showing raw bytes" } # 如果没有指令但有数据,可能是数据段或对齐问题 if not instructions and code: return { "success": True, "instructions": [], "raw_bytes": code.hex(), "count": 0, "architecture": arch, "note": "No valid instructions found at this address. Might be data section or misaligned.", "error": "" } return { "success": True, "instructions": instructions, "count": len(instructions), "architecture": arch, "start_address": hex(address), "error": "" } except Exception as e: import traceback return {"success": False, "instructions": [], "error": f"{str(e)}\n{traceback.format_exc()}"} def get_function_bytes( so_path: str, function_name: str, size: int = 64 ) -> dict: """ 获取函数的字节码 Args: so_path: SO文件路径 function_name: 函数名 size: 读取字节数 Returns: dict: {"success": bool, "bytes": str, "address": str, "error": str} """ if not LIEF_AVAILABLE: return {"success": False, "bytes": "", "address": "", "error": "lief not available"} if not os.path.exists(so_path): return {"success": False, "bytes": "", "address": "", "error": f"File not found: {so_path}"} try: binary = lief.parse(so_path) if binary is None: return {"success": False, "bytes": "", "address": "", "error": "Failed to parse SO file"} # 查找函数 target_func = None for func in binary.exported_functions: name = func.name if hasattr(func, 'name') else str(func) if name == function_name or function_name in name: target_func = func break if target_func is None: return { "success": False, "bytes": "", "address": "", "error": f"Function not found: {function_name}" } address = target_func.address if hasattr(target_func, 'address') else 0 # 读取字节 with open(so_path, 'rb') as f: data = f.read() if address < 0 or address + size > len(data): return {"success": False, "bytes": "", "address": hex(address), "error": "Invalid address"} func_bytes = data[address:address + size] return { "success": True, "function_name": target_func.name if hasattr(target_func, 'name') else function_name, "address": hex(address), "bytes": func_bytes.hex(), "size": len(func_bytes), "error": "" } except Exception as e: return {"success": False, "bytes": "", "address": "", "error": str(e)}

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/1600822305/so-analyzer-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server