应用安全 --- IDAPro脚本 之 导出函数引用数据
# -*- coding: utf-8 -*- """ IDA Pro 9.3 Script - Extract Function Referenced Data Regions 输出格式:IDA LST 风格(.rdata:XXXXXXXX db/dq/...)Usage: 运行脚本后,通过对话框输入函数名称或地址 """ import idaapi import idautils import idc import ida_bytes import ida_segment import ida_name import ida_funcs import ida_xref import os import json import struct import re import bisect from collections import OrderedDict, Counter# ==================== 配置区域 ==================== OUTPUT_DIR = "" # 输出目录,为空则使用IDB所在目录 OUTPUT_FORMAT = "lst" # "lst" | "json" | "binary" | "all" MAX_DATA_SIZE = 0x100000 # 单块数据最大读取限制 (1MB) FOLLOW_POINTERS = True # 是否跟踪指针指向的数据 MAX_DEPTH = 2 # 指针跟踪深度 LST_BYTES_PER_LINE = 16 # lst 格式每行 hex dump 宽度(仅影响 unknown 块) # ==================================================# ======================================================== # 兼容层:判断是否 64 位 # ======================================================== def is_64bit():try:import ida_idareturn ida_ida.inf_is_64bit()except (ImportError, AttributeError):passtry:return idaapi.get_inf_structure().is_64bit()except AttributeError:passtry:return idc.get_inf_attr(idc.INF_LFLAGS) & 0x0002 != 0except Exception:passreturn False# ======================================================== # 日志 # ======================================================== def log(msg, level="INFO"):prefix = {"INFO": "[*]", "OK": "[+]", "WARN": "[!]", "ERROR": "[-]"}.get(level, "[?]")print(f"{prefix} {msg}")# ======================================================== # 输出目录 # ======================================================== def get_output_dir():d = OUTPUT_DIR if OUTPUT_DIR else os.path.dirname(idc.get_idb_path())os.makedirs(d, exist_ok=True)return d# ======================================================== # 段属性工具 # ======================================================== DATA_SEG_NAMES = {".data", ".rdata", ".rodata", ".bss", ".idata", ".edata",".tls", ".rsrc", ".got", ".got.plt", ".init_array", ".fini_array","DATA", "BSS", }def is_data_segment(seg):if seg is None:return Falseif seg.type in (idaapi.SEG_DATA, idaapi.SEG_BSS,idaapi.SEG_NULL, idaapi.SEG_UNDF):return Truesname = ida_segment.get_segm_name(seg).lower()return any(sname.startswith(n.lower()) for n in DATA_SEG_NAMES)def is_code_segment(seg):return seg is not None and seg.type == idaapi.SEG_CODE# ======================================================== # 数据类型判断 # ======================================================== def determine_data_type(flags):if ida_bytes.is_byte(flags): return "byte"if ida_bytes.is_word(flags): return "word"if ida_bytes.is_dword(flags): return "dword"if ida_bytes.is_qword(flags): return "qword"if ida_bytes.is_strlit(flags): return "string"if ida_bytes.is_struct(flags): return "struct"if ida_bytes.is_float(flags): return "float"if ida_bytes.is_double(flags): return "double"if ida_bytes.is_align(flags): return "align"if ida_bytes.is_code(flags): return "code"return "unknown"def ida_known_item_size(ea):flags = ida_bytes.get_flags(ea)dtype = determine_data_type(flags)if dtype in ("byte", "word", "dword", "qword","float", "double", "string", "struct", "align"):sz = idc.get_item_size(ea)if sz and sz > 0:return sz, Trueif dtype == "code":return 0, Falsesz = idc.get_item_size(ea)if sz and sz > 0:return sz, Falsereturn 0, False# ======================================================== # 智能数据边界 # ======================================================== def compute_data_size(ea, all_known_eas, seg):sz, reliable = ida_known_item_size(ea)if reliable and sz > 0:return sz, "ida_defined"next_anchor = _find_next_anchor(ea, all_known_eas, seg)if next_anchor is not None and next_anchor > ea:return min(next_anchor - ea, MAX_DATA_SIZE), f"inferred_to_next(0x{next_anchor:X})"if seg:remaining = seg.end_ea - eacapped = min(remaining, MAX_DATA_SIZE)if capped > 0:return capped, "segment_end"return 1, "fallback_1byte"def _find_next_anchor(ea, all_known_eas, seg):candidates = []idx = bisect.bisect_right(all_known_eas, ea)if idx < len(all_known_eas):candidates.append(all_known_eas[idx])nh = idc.next_head(ea + 1, idaapi.BADADDR)if nh and nh != idaapi.BADADDR:candidates.append(nh)next_named = ida_name.get_next_name_ea(ea)if next_named and next_named != idaapi.BADADDR:candidates.append(next_named)seg_end = seg.end_ea if seg else idaapi.BADADDRif seg:candidates.append(seg_end)candidates = [c for c in candidates if ea < c <= seg_end]return min(candidates) if candidates else None# ======================================================== # 安全读取 # ======================================================== def read_safe(ea, size):if size <= 0:return b""data = ida_bytes.get_bytes(ea, min(size, MAX_DATA_SIZE))return bytes(data) if data else b""def get_string_at(ea):str_type = idc.get_str_type(ea)if str_type is None or str_type < 0:str_type = idc.STRTYPE_Cs = idc.get_strlit_contents(ea, -1, str_type)if s is None:return Nonetry:return s.decode("utf-8", errors="replace")except Exception:return repr(s)# ======================================================== # ★ IDA LST 风格渲染核心 # ========================================================def _fmt_addr(ea, seg_name, addr_width):"""生成 '.rdata:0000000140928A80' 风格的地址前缀"""return f"{seg_name}:{ea:0{addr_width}X}"def _ascii_char(b):if 0x20 <= b < 0x7F and b != ord("'"):return chr(b)return Nonedef _render_string_literal(raw: bytes) -> str:"""把字节序列渲染成 IDA 风格字符串,例如 'hello',0"""parts = []i = 0while i < len(raw):b = raw[i]if b == 0:# 终结符单独列出parts.append("0")i += 1continuech = _ascii_char(b)if ch:# 尽量合并连续可打印字符run = []while i < len(raw) and _ascii_char(raw[i]) is not None:run.append(_ascii_char(raw[i]))i += 1parts.append(f"'{''.join(run)}'")else:parts.append(f"{b:02X}h")i += 1return ", ".join(parts)def render_ida_lst_entry(entry: dict, addr_width: int = 16) -> list:""" 将单个 entry 渲染为若干行 IDA LST 风格字符串。返回 list[str],每个元素是一行(不含换行符)。""" ea = entry["ea_int"]seg_name = entry["segment"]dtype = entry["type"]name = entry["name"]raw = bytes(entry["data_bytes"])size = len(raw)from_ea = entry["from_ea"]depth = entry["depth"]ptr_size = 8 if is_64bit() else 4lines = []addr_prefix = _fmt_addr(ea, seg_name, addr_width)# ---------- 注释头:名称 + xref ----------comment_parts = []if from_ea and from_ea != "N/A":comment_parts.append(f"DATA XREF: {from_ea}")if depth > 0:comment_parts.append(f"depth={depth}")if entry.get("ptr_chain"):comment_parts.append("chain: " + " -> ".join(entry["ptr_chain"]))# ---------- 按类型生成主体行 ----------def one_line(addr, mnemonic, operand, comment=""):pfx = _fmt_addr(addr, seg_name, addr_width)cmt = f" ; {comment}" if comment else ""# IDA 风格:地址(20) 名称/标签 助记符 操作数return f"{pfx} {mnemonic:<6} {operand}{cmt}"# --- 名称行(如果有 label)---if name:label_line = f"{addr_prefix} {name}"# 追加 xref 注释if comment_parts:label_line += f" ; {'; '.join(comment_parts)}"lines.append(label_line)# 之后的数据行不再重复注释comment_parts = []cmt_suffix = ("; " + "; ".join(comment_parts)) if comment_parts else ""# ===== qword(指针/偏移)=====if dtype == "qword" and size >= 8:# 尝试识别为 offsetval = struct.unpack_from("<Q", raw[:8])[0]target_name = ida_name.get_name(val) if val else ""if target_name:operand = f"offset {target_name}"else:operand = f"{val:016X}h"lines.append(one_line(ea, "dq", operand, cmt_suffix.lstrip("; ")))# 剩余字节逐字节输出for i in range(8, size):lines.append(one_line(ea + i, "db", f"{raw[i]:3d} ; {raw[i]:02X}h"))return lines# ===== dword =====if dtype == "dword" and size >= 4:val = struct.unpack_from("<I", raw[:4])[0]lines.append(one_line(ea, "dd", f"{val:08X}h", cmt_suffix.lstrip("; ")))for i in range(4, size):lines.append(one_line(ea + i, "db", f"{raw[i]:3d} ; {raw[i]:02X}h"))return lines# ===== word =====if dtype == "word" and size >= 2:val = struct.unpack_from("<H", raw[:2])[0]lines.append(one_line(ea, "dw", f"{val:04X}h", cmt_suffix.lstrip("; ")))for i in range(2, size):lines.append(one_line(ea + i, "db", f"{raw[i]:3d} ; {raw[i]:02X}h"))return lines# ===== byte =====if dtype == "byte" and size == 1:b = raw[0]ch = _ascii_char(b)operand = f"{b:3d} ; {b:02X}h" + (f" '{ch}'" if ch else "")lines.append(one_line(ea, "db", operand, cmt_suffix.lstrip("; ")))return lines# ===== string =====if dtype == "string":sv = entry.get("string_value") or ""# 第一行:db 字符串内容,注释写原始字符串str_repr = _render_string_literal(raw)preview = sv[:60].replace("\n", "\\n") if sv else raw[:60].hex()lines.append(one_line(ea, "db", str_repr, f'"{preview}"'))return lines# ===== float =====if dtype == "float" and size >= 4:val = struct.unpack_from("<f", raw[:4])[0]lines.append(one_line(ea, "dd", f"{val} ; float", cmt_suffix.lstrip("; ")))return lines# ===== double =====if dtype == "double" and size >= 8:val = struct.unpack_from("<d", raw[:8])[0]lines.append(one_line(ea, "dq", f"{val} ; double", cmt_suffix.lstrip("; ")))return lines# ===== align =====if dtype == "align":lines.append(one_line(ea, "align", str(size), cmt_suffix.lstrip("; ")))return lines# ===== unknown / struct / code / fallback =====# 逐字节输出,每 16 字节一段加 ASCII 注释(模拟 hex dump)first = Truefor i in range(0, size):b = raw[i]ch = _ascii_char(b)cmt = f"{b:02X}h" + (f" '{ch}'" if ch else "")# 第一行附加 xref 注释if first and cmt_suffix:cmt = cmt + " " + cmt_suffixfirst = Falselines.append(one_line(ea + i, "db", f"{b:3d}", cmt))return linesdef build_lst_text(collected: OrderedDict, func_ea: int, func_name: str) -> str:""" 把所有 entry 按地址分组(同一段连续),生成完整的 LST 文本。""" addr_width = 16 if is_64bit() else 8ptr_size = 8 if is_64bit() else 4out = []out.append("; =" * 35)out.append(f"; Function Data Extraction — {func_name} @ 0x{func_ea:X}")out.append(f"; Entries: {len(collected)} PtrSize: {ptr_size*8}-bit")out.append("; =" * 35)out.append("")cur_seg = Nonefor entry in collected.values():seg_name = entry["segment"]# 换段时插入段分隔注释if seg_name != cur_seg:if cur_seg is not None:out.append("")out.append(f"; {'─'*66}")out.append(f"; Segment: {seg_name}")out.append(f"; {'─'*66}")cur_seg = seg_name# 渲染该条目entry_lines = render_ida_lst_entry(entry, addr_width)out.extend(entry_lines)out.append("")out.append("; end of extraction")return "\n".join(out)# ======================================================== # 函数解析:支持名称或地址输入 # ======================================================== def resolve_function(input_str):s = input_str.strip()if not s:return None, "输入为空"func_ea = Nonecandidate = Nonetry:if s.lower().startswith("0x"):candidate = int(s, 16)elif re.fullmatch(r"[0-9a-fA-F]{4,}", s):candidate = int(s, 16)else:candidate = int(s, 0)except ValueError:candidate = Noneif candidate is not None:f = ida_funcs.get_func(candidate)if f is not None:func_ea = f.start_ealog(f"地址 0x{candidate:X} -> 函数 0x{func_ea:X}")else:log(f"0x{candidate:X} 不在已知函数内,尝试按名称查找...", "WARN")if func_ea is None:name_ea = ida_name.get_name_ea(idaapi.BADADDR, s)if name_ea not in (idaapi.BADADDR, 0):f = ida_funcs.get_func(name_ea)if f is not None:func_ea = f.start_ealog(f"名称 '{s}' -> 函数 0x{func_ea:X}")else:return None, f"名称 '{s}' 对应地址 0x{name_ea:X} 不在任何函数内"else:log(f"精确名称未找到,模糊匹配 '{s}'...", "WARN")matches = [(f_ea, ida_name.get_name(f_ea) or "")for f_ea in idautils.Functions()if s.lower() in (ida_name.get_name(f_ea) or "").lower()]if len(matches) == 1:func_ea = matches[0][0]log(f"模糊匹配: {matches[0][1]} @ 0x{func_ea:X}", "OK")elif len(matches) > 1:show = matches[:10]log(f"模糊匹配到 {len(matches)} 个:", "WARN")for i, (mea, mn) in enumerate(show):log(f" [{i}] {mn} @ 0x{mea:X}")choice = idaapi.ask_str("0", 0,f"找到 {len(matches)} 个,输入序号 (0~{len(show)-1}):")if choice is None:return None, "用户取消"try:idx = int(choice.strip())if 0 <= idx < len(show):func_ea = show[idx][0]else:return None, f"序号 {idx} 超出范围"except ValueError:return None, f"无效序号: {choice}"else:return None, f"未找到包含 '{s}' 的函数"if func_ea is None:return None, f"无法解析: '{s}'"return func_ea, ""# ======================================================== # 收集函数引用数据 # ======================================================== def collect_function_refs(func_ea, follow_ptrs=True, max_depth=MAX_DEPTH):raw_refs = {}visited_code = set()pending_ptrs = []func = ida_funcs.get_func(func_ea)if func is None:log(f"无法获取函数: 0x{func_ea:X}", "ERROR")return OrderedDict()func_name = ida_name.get_name(func_ea) or f"sub_{func_ea:X}"log(f"分析函数: {func_name} @ 0x{func_ea:X}")ptr_size = 8 if is_64bit() else 4ptr_fmt = "<Q" if ptr_size == 8 else "<I"all_insn_eas = []for chunk_start, chunk_end in idautils.Chunks(func_ea):ea = chunk_startwhile ea < chunk_end:all_insn_eas.append(ea)visited_code.add(ea)ea = idc.next_head(ea, chunk_end)log(f"函数共 {len(all_insn_eas)} 条指令")def record_ref(ref_ea, from_ea=None, depth=0, chain=None):if ref_ea in (0, idaapi.BADADDR):returnseg = ida_segment.getseg(ref_ea)if seg is None:returnif is_code_segment(seg) and ref_ea in visited_code:returnif ref_ea not in raw_refs:raw_refs[ref_ea] = {"from_ea": from_ea, "depth": depth, "chain": chain or []}log("--- 阶段1: DataRefsFrom ---")for insn_ea in all_insn_eas:for ref_ea in idautils.DataRefsFrom(insn_ea):record_ref(ref_ea, insn_ea, 0)log("--- 阶段2: 操作数解析 ---")for insn_ea in all_insn_eas:insn = idaapi.insn_t()if idaapi.decode_insn(insn, insn_ea) <= 0:continuefor op in insn.ops:if op.type == idaapi.o_void:breakref_ea = idaapi.BADADDRif op.type == idaapi.o_mem:ref_ea = op.addrelif op.type == idaapi.o_displ:ref_ea = op.addrelif op.type == idaapi.o_imm:cand = op.valueif cand > 0x1000:s = ida_segment.getseg(cand)if s and is_data_segment(s):ref_ea = candif ref_ea not in (0, idaapi.BADADDR):record_ref(ref_ea, insn_ea, 0)log("--- 阶段3: xref 补充 ---")for insn_ea in all_insn_eas:xref = ida_xref.xrefblk_t()ok = xref.first_from(insn_ea, ida_xref.XREF_DATA)while ok:record_ref(xref.to, insn_ea, 0)ok = xref.next_from()log(f"第一轮共发现 {len(raw_refs)} 个候选地址")log("--- 第二轮: 智能边界计算 + 数据读取 ---")sorted_eas = sorted(raw_refs.keys())collected = OrderedDict()def build_entry(ref_ea, meta):seg = ida_segment.getseg(ref_ea)if seg is None:return Noneflags = ida_bytes.get_flags(ref_ea)dtype = determine_data_type(flags)name = ida_name.get_name(ref_ea) or ""seg_name = ida_segment.get_segm_name(seg)size, size_method = compute_data_size(ref_ea, sorted_eas, seg)data_bytes = read_safe(ref_ea, size)actual_size = len(data_bytes)entry = {"ea": f"0x{ref_ea:X}","ea_int": ref_ea,"name": name,"segment": seg_name,"type": dtype,"size": actual_size,"size_method": size_method,"data_hex": data_bytes.hex(),"data_bytes": list(data_bytes),"from_ea": f"0x{meta['from_ea']:X}" if meta["from_ea"] else "N/A","depth": meta["depth"],"ptr_chain": meta["chain"],}if dtype == "string":entry["string_value"] = get_string_at(ref_ea)if actual_size >= ptr_size:try:entry["ptr_value"] = struct.unpack(ptr_fmt, data_bytes[:ptr_size])[0]except Exception:entry["ptr_value"] = 0else:entry["ptr_value"] = int.from_bytes(data_bytes, "little") if data_bytes else 0log(f" [D{meta['depth']}] 0x{ref_ea:X} | {seg_name:12s} | {dtype:8s} "f"| {actual_size:6d}B | [{size_method}] | {name or '<unnamed>'}")return entryfor ref_ea in sorted_eas:meta = raw_refs[ref_ea]entry = build_entry(ref_ea, meta)if entry:collected[ref_ea] = entryif follow_ptrs and meta["depth"] < max_depth:pv = entry["ptr_value"]if pv and pv != idaapi.BADADDR:pending_ptrs.append((pv, ref_ea, meta["depth"] + 1,meta["chain"] + [f"0x{ref_ea:X}"]))if follow_ptrs:log(f"--- 阶段4: 指针追踪 (最大深度={max_depth}) ---")processed = set()while pending_ptrs:ptr_ea, from_ea, depth, chain = pending_ptrs.pop(0)if ptr_ea in processed or ptr_ea in collected:continueprocessed.add(ptr_ea)seg = ida_segment.getseg(ptr_ea)if seg is None:continueif is_code_segment(seg) and ptr_ea in visited_code:continuebisect.insort(sorted_eas, ptr_ea)raw_refs[ptr_ea] = {"from_ea": from_ea, "depth": depth, "chain": chain}meta = raw_refs[ptr_ea]entry = build_entry(ptr_ea, meta)if entry:collected[ptr_ea] = entryif depth < max_depth:pv = entry["ptr_value"]if pv and pv != idaapi.BADADDR:pending_ptrs.append((pv, ptr_ea, depth + 1,chain + [f"0x{ptr_ea:X}"]))log(f"共收集 {len(collected)} 个数据条目")return OrderedDict(sorted(collected.items()))# ======================================================== # 统计 # ======================================================== def print_statistics(collected):type_cnt = Counter(e["type"] for e in collected.values())seg_cnt = Counter(e["segment"] for e in collected.values())total_b = sum(e["size"] for e in collected.values())log("=" * 60)log(f" 总条目数: {len(collected)} 总数据量: {total_b} bytes ({total_b/1024:.2f} KB)")log(" 类型: " + " ".join(f"{t}×{c}" for t, c in type_cnt.most_common()))log(" 段: " + " ".join(f"{s}×{c}" for s, c in seg_cnt.most_common()))log("=" * 60)# ======================================================== # 保存文件 # ======================================================== def save_outputs(func_ea, collected, out_dir):func_name = ida_name.get_name(func_ea) or f"sub_{func_ea:X}"safe_name = "".join(c if c.isalnum() or c in "-_." else "_" for c in func_name)base = os.path.join(out_dir, f"funcdata_{safe_name}_0x{func_ea:X}")saved = []# ---- LST(默认/主要格式)----if OUTPUT_FORMAT in ("lst", "all"):path = base + ".lst"text = build_lst_text(collected, func_ea, func_name)with open(path, "w", encoding="utf-8") as f:f.write(text)saved.append(path)log(f"LST 已保存: {path}", "OK")# ---- JSON ----if OUTPUT_FORMAT in ("json", "all"):path = base + ".json"export = {"meta": {"func_ea": f"0x{func_ea:X}","func_name": func_name,"entry_count": len(collected),"ida_version": idaapi.get_kernel_version(),"ptr_size": 8 if is_64bit() else 4,},"entries": [{k: v for k, v in e.items() if k != "ea_int"}for e in collected.values()]}with open(path, "w", encoding="utf-8") as f:json.dump(export, f, indent=2, ensure_ascii=False)saved.append(path)log(f"JSON 已保存: {path}", "OK")# ---- Binary ----if OUTPUT_FORMAT in ("binary", "all"):path = base + ".bin"MAGIC, VER = b"FDAT", 2entries = list(collected.values())parts = [struct.pack("<4sII", MAGIC, VER, len(entries))]for e in entries:raw = bytes(e["data_bytes"])name_b = e["name"].encode("utf-8") if e["name"] else b""parts.append(struct.pack("<QII", e["ea_int"], len(raw), len(name_b)))parts.append(name_b)parts.append(raw)blob = b"".join(parts)with open(path, "wb") as f:f.write(blob)saved.append(path)log(f"BIN 已保存: {path} ({len(blob)} bytes)", "OK")return saved# ======================================================== # 批量处理 # ======================================================== def process_batch(input_list):results = {}for item in input_list:log(f"\n{'='*60}\n处理: {item}")func_ea, err = resolve_function(item)if func_ea is None:log(f"跳过 '{item}': {err}", "WARN")results[item] = {"status": "failed", "error": err}continuecollected = collect_function_refs(func_ea,follow_ptrs=FOLLOW_POINTERS,max_depth=MAX_DEPTH)if not collected:results[item] = {"status": "empty", "func_ea": func_ea}continueprint_statistics(collected)saved = save_outputs(func_ea, collected, get_output_dir())results[item] = {"status": "ok", "func_ea": func_ea,"entries": len(collected), "files": saved}return results# ======================================================== # 主入口 # ======================================================== def main():log("=" * 60)log(f"IDA Pro 9.3 - Function Data Extractor v4 "f"({'64' if is_64bit() else '32'}-bit)")log(f"输出格式: {OUTPUT_FORMAT}")log("=" * 60)cur_ea = idc.get_screen_ea()cur_func = ida_funcs.get_func(cur_ea)default = (ida_name.get_name(cur_func.start_ea) or f"0x{cur_func.start_ea:X}"if cur_func else f"0x{cur_ea:X}")user_input = idaapi.ask_str(default, 0,"输入函数名称或地址(多个目标用逗号分隔)\n""示例: main, 0x401000, sub_402ABC")if user_input is None:log("已取消", "WARN"); returntargets = [t.strip() for t in re.split(r"[,;\n]+", user_input) if t.strip()]if not targets:log("未输入任何目标", "WARN"); returnlog(f"待处理 {len(targets)} 个: {targets}")if len(targets) == 1:func_ea, err = resolve_function(targets[0])if func_ea is None:log(f"解析失败: {err}", "ERROR"); returncollected = collect_function_refs(func_ea,follow_ptrs=FOLLOW_POINTERS,max_depth=MAX_DEPTH)if not collected:log("未找到任何数据引用", "WARN"); returnprint_statistics(collected)out_dir = get_output_dir()saved = save_outputs(func_ea, collected, out_dir)log("=" * 60)log(f"完成!输出目录: {out_dir}", "OK")for p in saved:log(f" {p}", "OK")log("=" * 60)else:results = process_batch(targets)log("\n" + "=" * 60 + "\n批量汇总:")for target, r in results.items():status = r["status"]if status == "ok":log(f" [OK] {target:30s} -> {r['entries']} 条", "OK")elif status == "empty":log(f" [空] {target:30s} -> 无数据引用", "WARN")else:log(f" [ERR] {target:30s} -> {r.get('error','?')}", "ERROR")log("=" * 60)if __name__ == "__main__":main()
免责声明
本文档所有内容仅供安全研究、学术交流与技术学习使用,严禁用于任何未经授权的逆向破解、网络攻击、隐私窃取、恶意软件开发及其他违反《中华人民共和国网络安全法》《数据安全法》等法律法规的行为,使用者应确保已获得目标软件权利人的合法授权并自行承担因使用本文档内容所产生的一切法律责任与后果,作者不对任何直接或间接损害承担任何责任,继续阅读即视为您已知悉并同意上述全部条款。
