Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python3
- """
- 注意:此代码完全由deepseek-v3.2模型编写,SystemFileB负责本地化
- C++ 源代码混淆器(基于 Clang AST)
- 收集所有出现在普通代码中的标识符(包括关键字和 main),
- 为每个标识符生成随机宏名,并在源文件开头插入 #define,
- 同时将源文件中所有出现的位置替换为宏名。
- 预处理指令(如 #include)中的标识符会被跳过,避免破坏编译。
- """
- import sys
- import os
- import argparse
- import random
- import string
- import clang.cindex
- # 常见预定义宏(避免混淆,因为它们有特殊含义)
- PREDEFINED_MACROS = {
- '__LINE__', '__FILE__', '__DATE__', '__TIME__', '__STDC__',
- '__cplusplus', '__ASSEMBLER__', '__INCLUDE_LEVEL__', '__BASE_FILE__',
- '__TIMESTAMP__', '__COUNTER__', '__FUNCTION__', '__PRETTY_FUNCTION__',
- '__func__'
- }
- # 需要排除的特殊关键字(混淆后会破坏语法)
- SPECIAL_KEYWORDS = {'operator'} # operator 必须与后面的运算符连用,不能单独替换
- # 最终黑名单
- BLACKLIST = PREDEFINED_MACROS | SPECIAL_KEYWORDS
- def generate_obfuscated_name(existing_names):
- """
- 生成一个唯一的随机混淆名。
- - 由大小写字母、数字、_ 和 $ 组成
- - 首字符不能为数字
- - 不与 existing_names 中的任何名称重复
- """
- chars = string.ascii_letters + string.digits + '_'
- while True:
- length = random.randint(8, 20)
- first = random.choice(string.ascii_letters + '_')
- rest = ''.join(random.choices(chars, k=length-1))
- name = first + rest
- if name not in existing_names:
- return name
- def collect_identifiers_from_cursor(cursor, main_file_path, identifiers):
- """递归遍历 AST,收集主文件中所有标识符和关键字"""
- loc = cursor.location
- if loc.file and os.path.abspath(loc.file.name) == main_file_path:
- # 获取该节点覆盖的所有 token
- for token in cursor.get_tokens():
- if token.kind in (clang.cindex.TokenKind.IDENTIFIER,
- clang.cindex.TokenKind.KEYWORD):
- identifiers.add(token.spelling)
- # 递归子节点
- for child in cursor.get_children():
- collect_identifiers_from_cursor(child, main_file_path, identifiers)
- def main():
- parser = argparse.ArgumentParser(description='使用 Clang AST 来混淆C++代码')
- parser.add_argument('-i', '--input', help='输入的 C++ 源代码文件')
- parser.add_argument('-o', '--output', help='输出文件 (默认: input.obfuscated.cpp)')
- parser.add_argument('-c', dest='compiler_args', nargs=argparse.REMAINDER,
- help='给 clang 的编译参数 (比如: -std=c++17 -I...)')
- args = parser.parse_args()
- if args.input==None:
- print("错误: 无输入文件\n请使用--help参数查看用法")
- sys.exit(1)
- # 如果需要,可在此设置 libclang 路径(例如:/usr/lib/llvm-14/lib)
- # clang.cindex.Config.set_library_path('/usr/lib/llvm-14/lib')
- # 解析翻译单元
- index = clang.cindex.Index.create()
- tu = index.parse(args.input, args.compiler_args or [])
- if not tu:
- print('错误: 无法解析翻译单元')
- sys.exit(1)
- # 输出诊断信息(警告/错误)
- for diag in tu.diagnostics:
- print(diag, file=sys.stderr)
- if diag.severity >= clang.cindex.Diagnostic.Error:
- print('出现编译错误, 将继续...', file=sys.stderr)
- # 获取主文件对象及其绝对路径
- main_cursor = tu.cursor
- # 直接使用输入文件的绝对路径作为主文件(避免依赖 location.file)
- main_file_path = os.path.abspath(args.input)
- if not os.path.isfile(main_file_path):
- print(f'错误: 输入文件 {main_file_path} 不存在')
- sys.exit(1)
- # 收集主文件中所有出现在 AST 中的标识符(包括关键字)
- identifiers = set()
- collect_identifiers_from_cursor(main_cursor, main_file_path, identifiers)
- # 从收集中移除黑名单中的名称(它们将保持原样)
- original_names = identifiers - BLACKLIST
- if not original_names:
- print('没有符号用于混淆')
- sys.exit(0)
- # 生成原始名称到混淆名的映射
- obfuscated_map = {}
- # 已使用的名称包括原始名称、黑名单、以及新生成的混淆名
- used_names = set(original_names) | BLACKLIST
- for name in sorted(original_names): # 排序以保证确定性
- obf_name = generate_obfuscated_name(used_names)
- obfuscated_map[name] = obf_name
- used_names.add(obf_name)
- # 获取整个源文件的预处理 token(包含所有预处理指令)
- tokens = list(tu.get_tokens(extent=tu.cursor.extent))
- # 标记需要跳过的 token 索引(所有预处理行)
- skip_indices = set()
- i = 0
- while i < len(tokens):
- tok = tokens[i]
- # 检测预处理指令起始:单独的 '#' token
- if tok.kind == clang.cindex.TokenKind.PUNCTUATION and tok.spelling == '#':
- # 跳过从当前 token 开始直到该行结束的所有 token
- line = tok.location.line
- j = i
- while j < len(tokens) and tokens[j].location.line == line:
- skip_indices.add(j)
- j += 1
- i = j # 跳过已处理的行
- continue
- i += 1
- # 收集需要替换的 token 位置
- replacements = []
- for idx, token in enumerate(tokens):
- if idx in skip_indices:
- continue # 跳过预处理行内的 token
- loc = token.location
- # 仅处理主文件中的 token
- if not loc.file or os.path.abspath(loc.file.name) != main_file_path:
- continue
- if token.kind in (clang.cindex.TokenKind.IDENTIFIER,
- clang.cindex.TokenKind.KEYWORD):
- name = token.spelling
- if name in obfuscated_map:
- start = token.location.offset
- end = token.extent.end.offset
- if start is not None and end is not None and start < end:
- replacements.append((start, end, obfuscated_map[name]))
- # 收集需要替换的 token 位置
- replacements = []
- for idx, token in enumerate(tokens):
- if idx in skip_indices:
- continue # 跳过已标记的 token
- loc = token.location
- # 仅处理主文件中的 token
- if not loc.file or os.path.abspath(loc.file.name) != main_file_path:
- continue
- if token.kind in (clang.cindex.TokenKind.IDENTIFIER,
- clang.cindex.TokenKind.KEYWORD):
- name = token.spelling
- if name in obfuscated_map:
- start = token.location.offset
- end = token.extent.end.offset
- if start is not None and end is not None and start < end:
- replacements.append((start, end, obfuscated_map[name]))
- # 按起始偏移排序
- replacements.sort(key=lambda x: x[0])
- # 读取源文件(二进制模式)
- with open(args.input, 'rb') as f:
- source_bytes = f.read()
- # 构建新源代码(逐段拼接字节串)
- new_parts = []
- last_pos = 0
- for start, end, obf_name in replacements:
- if last_pos < start:
- new_parts.append(source_bytes[last_pos:start])
- new_parts.append(obf_name.encode('utf-8'))
- last_pos = end
- if last_pos < len(source_bytes):
- new_parts.append(source_bytes[last_pos:])
- obfuscated_bytes = b''.join(new_parts)
- # 生成宏定义头(编码为 UTF-8 字节)
- header_lines = [f'#define {obf} {orig}' for orig, obf in obfuscated_map.items()]
- header = '\n'.join(header_lines) + '\n\n'
- header_bytes = header.encode('utf-8')
- final_bytes = header_bytes + obfuscated_bytes
- # 确定输出文件并以二进制写入
- if args.output:
- output_path = args.output
- else:
- base, ext = os.path.splitext(args.input)
- output_path = base + '.obfuscated' + ext
- with open(output_path, 'wb') as f:
- f.write(final_bytes)
- print(f'混淆后的代码将写到 {output_path} 中')
- if __name__ == '__main__':
- main()
Add Comment
Please, Sign In to add comment