Skip to main content
Glama
so_utils.py14.2 kB
"""SO文件分析工具""" import os import zipfile import tempfile import shutil from pathlib import Path from typing import Optional, List, Dict try: import lief LIEF_AVAILABLE = True except ImportError: LIEF_AVAILABLE = False def check_lief() -> dict: """检查lief库是否可用""" return { "available": LIEF_AVAILABLE, "message": "lief is available" if LIEF_AVAILABLE else "Please install: pip install lief" } def list_libs_from_apk(apk_path: str) -> dict: """ 列出APK中的所有SO库 Args: apk_path: APK文件路径 Returns: dict: {"success": bool, "libs": list, "error": str} """ if not os.path.exists(apk_path): return {"success": False, "libs": [], "error": f"APK not found: {apk_path}"} try: libs = [] with zipfile.ZipFile(apk_path, 'r') as zf: for name in zf.namelist(): if name.startswith("lib/") and name.endswith(".so"): parts = name.split("/") if len(parts) >= 3: arch = parts[1] lib_name = parts[2] size = zf.getinfo(name).file_size libs.append({ "name": lib_name, "arch": arch, "path": name, "size": size, "size_mb": round(size / 1024 / 1024, 2) }) # 按架构分组 by_arch = {} for lib in libs: arch = lib["arch"] if arch not in by_arch: by_arch[arch] = [] by_arch[arch].append(lib["name"]) return { "success": True, "libs": libs, "by_arch": by_arch, "total": len(libs), "architectures": list(by_arch.keys()), "error": "" } except Exception as e: return {"success": False, "libs": [], "error": str(e)} def extract_so_from_apk( apk_path: str, lib_name: str, arch: str = "arm64-v8a", output_dir: Optional[str] = None ) -> dict: """ 从APK中提取SO文件 Args: apk_path: APK文件路径 lib_name: SO库名称(如 libflutter.so) arch: 架构(arm64-v8a, armeabi-v7a, x86, x86_64) output_dir: 输出目录(可选) Returns: dict: {"success": bool, "output_path": str, "error": str} """ if not os.path.exists(apk_path): return {"success": False, "output_path": "", "error": f"APK not found: {apk_path}"} lib_path_in_apk = f"lib/{arch}/{lib_name}" try: with zipfile.ZipFile(apk_path, 'r') as zf: if lib_path_in_apk not in zf.namelist(): return { "success": False, "output_path": "", "error": f"Library not found: {lib_path_in_apk}" } if output_dir is None: output_dir = tempfile.mkdtemp(prefix="so_extract_") output_path = os.path.join(output_dir, lib_name) with zf.open(lib_path_in_apk) as src, open(output_path, 'wb') as dst: dst.write(src.read()) return { "success": True, "output_path": output_path, "arch": arch, "error": "" } except Exception as e: return {"success": False, "output_path": "", "error": str(e)} def get_so_info(so_path: str) -> dict: """ 获取SO文件基本信息 Args: so_path: SO文件路径 Returns: dict: SO信息 """ if not LIEF_AVAILABLE: return {"success": False, "error": "lief not available. Install: pip install lief"} if not os.path.exists(so_path): return {"success": False, "error": f"File not found: {so_path}"} try: binary = lief.parse(so_path) if binary is None: return {"success": False, "error": "Failed to parse SO file"} # 基本信息 info = { "name": os.path.basename(so_path), "format": binary.format.name if hasattr(binary.format, 'name') else str(binary.format), "size": os.path.getsize(so_path), "size_mb": round(os.path.getsize(so_path) / 1024 / 1024, 2), } # ELF特有信息 if hasattr(binary, 'header'): header = binary.header info["architecture"] = header.machine_type.name if hasattr(header.machine_type, 'name') else str(header.machine_type) info["is_64bit"] = header.identity_class.name == "CLASS64" if hasattr(header.identity_class, 'name') else False info["endianness"] = header.identity_data.name if hasattr(header.identity_data, 'name') else "unknown" # 导出函数数量 if hasattr(binary, 'exported_functions'): info["exported_functions_count"] = len(list(binary.exported_functions)) # 导入函数数量 if hasattr(binary, 'imported_functions'): info["imported_functions_count"] = len(list(binary.imported_functions)) # 依赖库 if hasattr(binary, 'libraries'): info["dependencies"] = list(binary.libraries) return { "success": True, "info": info, "error": "" } except Exception as e: return {"success": False, "error": str(e)} def get_exports(so_path: str, search: str = "", limit: int = 100) -> dict: """ 获取SO导出函数列表 Args: so_path: SO文件路径 search: 搜索过滤 limit: 最多返回数量 Returns: dict: {"success": bool, "exports": list, "error": str} """ if not LIEF_AVAILABLE: return {"success": False, "exports": [], "error": "lief not available"} if not os.path.exists(so_path): return {"success": False, "exports": [], "error": f"File not found: {so_path}"} try: binary = lief.parse(so_path) if binary is None: return {"success": False, "exports": [], "error": "Failed to parse SO file"} exports = [] for func in binary.exported_functions: name = func.name if hasattr(func, 'name') else str(func) if search and search.lower() not in name.lower(): continue exports.append({ "name": name, "address": hex(func.address) if hasattr(func, 'address') else "0x0" }) total = len(exports) return { "success": True, "exports": exports[:limit], "total": total, "returned": min(total, limit), "error": "" } except Exception as e: return {"success": False, "exports": [], "error": str(e)} def get_imports(so_path: str, search: str = "", limit: int = 100) -> dict: """ 获取SO导入函数列表 Args: so_path: SO文件路径 search: 搜索过滤 limit: 最多返回数量 Returns: dict: {"success": bool, "imports": list, "error": str} """ if not LIEF_AVAILABLE: return {"success": False, "imports": [], "error": "lief not available"} if not os.path.exists(so_path): return {"success": False, "imports": [], "error": f"File not found: {so_path}"} try: binary = lief.parse(so_path) if binary is None: return {"success": False, "imports": [], "error": "Failed to parse SO file"} imports = [] for func in binary.imported_functions: name = func.name if hasattr(func, 'name') else str(func) if search and search.lower() not in name.lower(): continue imports.append({ "name": name, "library": func.library.name if hasattr(func, 'library') and func.library else "unknown" }) total = len(imports) return { "success": True, "imports": imports[:limit], "total": total, "returned": min(total, limit), "error": "" } except Exception as e: return {"success": False, "imports": [], "error": str(e)} def get_strings(so_path: str, min_length: int = 4, search: str = "", limit: int = 200) -> dict: """ 提取SO文件中的字符串 Args: so_path: SO文件路径 min_length: 最小字符串长度 search: 搜索过滤 limit: 最多返回数量 Returns: dict: {"success": bool, "strings": list, "error": str} """ if not os.path.exists(so_path): return {"success": False, "strings": [], "error": f"File not found: {so_path}"} try: strings = [] with open(so_path, 'rb') as f: data = f.read() # 提取ASCII字符串 current = [] for byte in data: if 32 <= byte < 127: # 可打印ASCII current.append(chr(byte)) else: if len(current) >= min_length: s = ''.join(current) if not search or search.lower() in s.lower(): strings.append(s) current = [] # 处理最后一个字符串 if len(current) >= min_length: s = ''.join(current) if not search or search.lower() in s.lower(): strings.append(s) total = len(strings) return { "success": True, "strings": strings[:limit], "total": total, "returned": min(total, limit), "error": "" } except Exception as e: return {"success": False, "strings": [], "error": str(e)} def search_symbol(so_path: str, pattern: str, limit: int = 50) -> dict: """ 搜索符号(导出+导入) Args: so_path: SO文件路径 pattern: 搜索模式 limit: 最多返回数量 Returns: dict: {"success": bool, "results": list, "error": str} """ if not LIEF_AVAILABLE: return {"success": False, "results": [], "error": "lief not available"} if not os.path.exists(so_path): return {"success": False, "results": [], "error": f"File not found: {so_path}"} try: binary = lief.parse(so_path) if binary is None: return {"success": False, "results": [], "error": "Failed to parse SO file"} results = [] pattern_lower = pattern.lower() # 搜索导出 for func in binary.exported_functions: name = func.name if hasattr(func, 'name') else str(func) if pattern_lower in name.lower(): results.append({ "name": name, "type": "export", "address": hex(func.address) if hasattr(func, 'address') else "0x0" }) # 搜索导入 for func in binary.imported_functions: name = func.name if hasattr(func, 'name') else str(func) if pattern_lower in name.lower(): results.append({ "name": name, "type": "import", "library": func.library.name if hasattr(func, 'library') and func.library else "unknown" }) return { "success": True, "results": results[:limit], "total": len(results), "pattern": pattern, "error": "" } except Exception as e: return {"success": False, "results": [], "error": str(e)} def detect_flutter(apk_path: str) -> dict: """ 检测是否是Flutter应用 Args: apk_path: APK文件路径 Returns: dict: {"success": bool, "is_flutter": bool, "details": dict, "error": str} """ if not os.path.exists(apk_path): return {"success": False, "is_flutter": False, "error": f"APK not found: {apk_path}"} try: flutter_indicators = { "libflutter.so": False, "libapp.so": False, "flutter_assets": False, "kernel_blob.bin": False } flutter_archs = [] with zipfile.ZipFile(apk_path, 'r') as zf: for name in zf.namelist(): # 检查libflutter.so if "libflutter.so" in name: flutter_indicators["libflutter.so"] = True # 提取架构 parts = name.split("/") if len(parts) >= 2 and parts[0] == "lib": arch = parts[1] if arch not in flutter_archs: flutter_archs.append(arch) # 检查libapp.so(Dart代码编译后) if "libapp.so" in name: flutter_indicators["libapp.so"] = True # 检查flutter_assets if "flutter_assets" in name: flutter_indicators["flutter_assets"] = True # 检查kernel_blob.bin if "kernel_blob.bin" in name: flutter_indicators["kernel_blob.bin"] = True is_flutter = flutter_indicators["libflutter.so"] and flutter_indicators["libapp.so"] return { "success": True, "is_flutter": is_flutter, "indicators": flutter_indicators, "architectures": flutter_archs, "confidence": sum(flutter_indicators.values()) / len(flutter_indicators) * 100, "error": "" } except Exception as e: return {"success": False, "is_flutter": False, "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