Oxyry Python Obfuscator(https://pyob.oxyry.com/)코드 난독화 사이트가 없어져서 그냥 제가 만들었습니다.
왜 없애는지 어차피 이렇게 개발자들이 다시 만들텐데 정말 귀찮게 됐었네요.
필요하신분 아래 코드 또는 첨부 파일 다운받으셔서 난독화_GUI.py 사용하시면 됩니다.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
코드 난독화 변환 도구
다양한 난독화 기법을 사용하여 Python 코드를 난독화합니다.
"""
import os
import re
import random
import string
import base64
import zlib
import argparse
class CodeObfuscator:
"""코드 난독화 클래스"""
def __init__(self):
self.variable_mapping = {}
self.function_mapping = {}
self.class_mapping = {}
self.string_mapping = {}
self.comment_removed = False
def generate_random_name(self, length: int = 8) -> str:
"""랜덤 변수명 생성"""
chars = string.ascii_letters + '_'
return ''.join(random.choice(chars) for _ in range(length))
def remove_comments(self, code: str) -> str:
"""주석 제거"""
# 한 줄 주석 제거
lines = code.split('\n')
cleaned_lines = []
for line in lines:
# 문자열 내의 #은 보존
in_string = False
string_char = None
cleaned_line = ""
for i, char in enumerate(line):
if char in ['"', "'"] and (i == 0 or line[i-1] != '\\'):
if not in_string:
in_string = True
string_char = char
elif string_char == char:
in_string = False
string_char = None
if char == '#' and not in_string:
break
cleaned_line += char
if cleaned_line.strip():
cleaned_lines.append(cleaned_line)
return '\n'.join(cleaned_lines)
def remove_docstrings(self, code: str) -> str:
"""docstring 제거"""
# 삼중 따옴표 docstring 제거
code = re.sub(r'""".*?"""', '', code, flags=re.DOTALL)
code = re.sub(r"'''.*?'''", '', code, flags=re.DOTALL)
return code
def obfuscate_strings(self, code: str) -> str:
"""문자열 난독화"""
def replace_string(match):
original_string = match.group(1)
if len(original_string) > 3: # 짧은 문자열은 건너뛰기
# base64로 인코딩
encoded = base64.b64encode(original_string.encode()).decode()
return f'base64.b64decode("{encoded}").decode()'
return match.group(0)
# 문자열을 base64로 인코딩
code = re.sub(r'"([^"]*)"', replace_string, code)
code = re.sub(r"'([^']*)'", replace_string, code)
return code
def obfuscate_variables(self, code: str) -> str:
"""변수명 난독화"""
# 변수명 패턴 찾기
variable_pattern = r'\b([a-zA-Z_][a-zA-Z0-9_]*)\s*='
def replace_variable(match):
var_name = match.group(1)
if var_name not in ['self', 'cls', '__init__', '__main__', 'if', 'for', 'while', 'def', 'class', 'import', 'from', 'as', 'in', 'is', 'not', 'and', 'or', 'True', 'False', 'None']:
if var_name not in self.variable_mapping:
self.variable_mapping[var_name] = self.generate_random_name()
return f'{self.variable_mapping[var_name]} ='
return match.group(0)
code = re.sub(variable_pattern, replace_variable, code)
# 변수 사용 부분도 변경
for original, obfuscated in self.variable_mapping.items():
code = re.sub(r'\b' + re.escape(original) + r'\b', obfuscated, code)
return code
def obfuscate_functions(self, code: str) -> str:
"""함수명 난독화"""
# 함수 정의 패턴
function_pattern = r'def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\('
def replace_function(match):
func_name = match.group(1)
if not func_name.startswith('__'):
if func_name not in self.function_mapping:
self.function_mapping[func_name] = self.generate_random_name()
return f'def {self.function_mapping[func_name]}('
return match.group(0)
code = re.sub(function_pattern, replace_function, code)
# 함수 호출 부분도 변경
for original, obfuscated in self.function_mapping.items():
code = re.sub(r'\b' + re.escape(original) + r'\s*\(', f'{obfuscated}(', code)
return code
def add_junk_code(self, code: str) -> str:
"""쓰레기 코드 추가"""
junk_lines = [
f'{self.generate_random_name()} = {random.randint(1, 100)}',
f'{self.generate_random_name()} = "{self.generate_random_name()}"',
f'if False: {self.generate_random_name()} = {random.randint(1, 100)}',
f'while False: {self.generate_random_name()} = {random.randint(1, 100)}',
f'for {self.generate_random_name()} in []: pass'
]
lines = code.split('\n')
new_lines = []
for line in lines:
new_lines.append(line)
if random.random() < 0.1: # 10% 확률로 쓰레기 코드 추가
new_lines.append(random.choice(junk_lines))
return '\n'.join(new_lines)
def encode_code(self, code: str) -> str:
"""코드를 base64로 인코딩하여 실행 코드로 변환"""
compressed = zlib.compress(code.encode('utf-8'))
encoded = base64.b64encode(compressed).decode()
decoder_code = f'''
import base64
import zlib
exec(zlib.decompress(base64.b64decode("{encoded}")).decode('utf-8'))
'''
return decoder_code
def obfuscate(self, code: str, level: int = 1, encode: bool = False) -> str:
"""코드 난독화 메인 함수"""
print("난독화 시작...")
# 1단계: 기본 정리
if level >= 1:
print("1단계: 주석 및 docstring 제거...")
code = self.remove_comments(code)
code = self.remove_docstrings(code)
# 2단계: 변수명 난독화
if level >= 2:
print("2단계: 변수명 난독화...")
code = self.obfuscate_variables(code)
# 3단계: 함수명 난독화
if level >= 3:
print("3단계: 함수명 난독화...")
code = self.obfuscate_functions(code)
# 4단계: 문자열 난독화
if level >= 4:
print("4단계: 문자열 난독화...")
code = self.obfuscate_strings(code)
# 5단계: 쓰레기 코드 추가
if level >= 5:
print("5단계: 쓰레기 코드 추가...")
code = self.add_junk_code(code)
# 최종 인코딩
if encode:
print("최종 인코딩...")
code = self.encode_code(code)
print("난독화 완료!")
return code
def read_file(file_path: str) -> str:
"""파일 읽기"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
return f.read()
except Exception as e:
print(f"파일 읽기 오류: {e}")
return ""
def write_file(file_path: str, content: str) -> bool:
"""파일 쓰기"""
try:
os.makedirs(os.path.dirname(file_path), exist_ok=True)
with open(file_path, 'w', encoding='utf-8') as f:
f.write(content)
return True
except Exception as e:
print(f"파일 쓰기 오류: {e}")
return False
def main():
parser = argparse.ArgumentParser(description='Python 코드 난독화 도구')
parser.add_argument('input_file', help='입력 파일 경로')
parser.add_argument('-o', '--output', help='출력 파일 경로')
parser.add_argument('-l', '--level', type=int, default=3, choices=[1, 2, 3, 4, 5],
help='난독화 레벨 (1-5, 기본값: 3)')
parser.add_argument('-e', '--encode', action='store_true',
help='최종 코드를 base64로 인코딩')
parser.add_argument('--no-comments', action='store_true',
help='주석 제거')
args = parser.parse_args()
# 파일 읽기
code = read_file(args.input_file)
if not code:
return
# 난독화 실행
obfuscator = CodeObfuscator()
obfuscated_code = obfuscator.obfuscate(code, level=args.level, encode=args.encode)
# 출력 파일 경로 설정
if args.output:
output_path = args.output
else:
base_name = os.path.splitext(args.input_file)[0]
output_path = f"{base_name}_obfuscated.py"
# 파일 저장
if write_file(output_path, obfuscated_code):
print(f"난독화된 코드가 저장되었습니다: {output_path}")
# 원본과 난독화된 코드 크기 비교
original_size = len(code)
obfuscated_size = len(obfuscated_code)
print(f"원본 크기: {original_size} 문자")
print(f"난독화 크기: {obfuscated_size} 문자")
print(f"크기 변화: {((obfuscated_size - original_size) / original_size * 100):.1f}%")
if __name__ == "__main__":
main()
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
코드 난독화 GUI 도구
사용자 친화적인 인터페이스로 코드를 난독화합니다.
"""
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
import os
import threading
from 코드_난독화_도구 import CodeObfuscator, read_file, write_file
class ObfuscatorGUI:
def __init__(self, root):
self.root = root
self.root.title("코드 난독화 도구")
self.root.geometry("800x600")
self.root.resizable(True, True)
# 변수 초기화
self.input_file_path = tk.StringVar()
self.output_file_path = tk.StringVar()
self.obfuscation_level = tk.IntVar(value=3)
self.encode_code = tk.BooleanVar(value=False)
self.remove_comments = tk.BooleanVar(value=True)
self.setup_ui()
def setup_ui(self):
# 메인 프레임
main_frame = ttk.Frame(self.root, padding="10")
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# 그리드 가중치 설정
self.root.columnconfigure(0, weight=1)
self.root.rowconfigure(0, weight=1)
main_frame.columnconfigure(1, weight=1)
main_frame.rowconfigure(3, weight=1)
# 파일 선택 섹션
ttk.Label(main_frame, text="입력 파일:").grid(row=0, column=0, sticky=tk.W, pady=5)
ttk.Entry(main_frame, textvariable=self.input_file_path, width=50).grid(row=0, column=1, sticky=(tk.W, tk.E), padx=5, pady=5)
ttk.Button(main_frame, text="찾아보기", command=self.browse_input_file).grid(row=0, column=2, padx=5, pady=5)
ttk.Label(main_frame, text="출력 파일:").grid(row=1, column=0, sticky=tk.W, pady=5)
ttk.Entry(main_frame, textvariable=self.output_file_path, width=50).grid(row=1, column=1, sticky=(tk.W, tk.E), padx=5, pady=5)
ttk.Button(main_frame, text="찾아보기", command=self.browse_output_file).grid(row=1, column=2, padx=5, pady=5)
# 옵션 섹션
options_frame = ttk.LabelFrame(main_frame, text="난독화 옵션", padding="10")
options_frame.grid(row=2, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=10)
options_frame.columnconfigure(1, weight=1)
# 난독화 레벨
ttk.Label(options_frame, text="난독화 레벨:").grid(row=0, column=0, sticky=tk.W, pady=5)
level_frame = ttk.Frame(options_frame)
level_frame.grid(row=0, column=1, sticky=tk.W, pady=5)
levels = [
("1 - 기본 (주석 제거)", 1),
("2 - 변수명 난독화", 2),
("3 - 함수명 난독화", 3),
("4 - 문자열 난독화", 4),
("5 - 최고 (쓰레기 코드 추가)", 5)
]
for i, (text, value) in enumerate(levels):
ttk.Radiobutton(level_frame, text=text, variable=self.obfuscation_level,
value=value).grid(row=0, column=i, padx=10)
# 추가 옵션
ttk.Checkbutton(options_frame, text="코드 인코딩 (base64)",
variable=self.encode_code).grid(row=1, column=0, columnspan=2, sticky=tk.W, pady=5)
ttk.Checkbutton(options_frame, text="주석 제거",
variable=self.remove_comments).grid(row=2, column=0, columnspan=2, sticky=tk.W, pady=5)
# 버튼 섹션
button_frame = ttk.Frame(main_frame)
button_frame.grid(row=3, column=0, columnspan=3, pady=10)
ttk.Button(button_frame, text="난독화 실행", command=self.run_obfuscation).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="미리보기", command=self.preview_obfuscation).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="초기화", command=self.reset_form).pack(side=tk.LEFT, padx=5)
# 결과 표시 섹션
result_frame = ttk.LabelFrame(main_frame, text="결과", padding="10")
result_frame.grid(row=4, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=10)
result_frame.columnconfigure(0, weight=1)
result_frame.rowconfigure(0, weight=1)
# 노트북 (탭) 생성
self.notebook = ttk.Notebook(result_frame)
self.notebook.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# 원본 코드 탭
original_frame = ttk.Frame(self.notebook)
self.notebook.add(original_frame, text="원본 코드")
self.original_text = scrolledtext.ScrolledText(original_frame, wrap=tk.WORD, height=15)
self.original_text.pack(fill=tk.BOTH, expand=True)
# 난독화된 코드 탭
obfuscated_frame = ttk.Frame(self.notebook)
self.notebook.add(obfuscated_frame, text="난독화된 코드")
self.obfuscated_text = scrolledtext.ScrolledText(obfuscated_frame, wrap=tk.WORD, height=15)
self.obfuscated_text.pack(fill=tk.BOTH, expand=True)
# 통계 탭
stats_frame = ttk.Frame(self.notebook)
self.notebook.add(stats_frame, text="통계")
self.stats_text = scrolledtext.ScrolledText(stats_frame, wrap=tk.WORD, height=15)
self.stats_text.pack(fill=tk.BOTH, expand=True)
# 진행 상황 표시
self.progress_var = tk.StringVar(value="준비됨")
ttk.Label(main_frame, textvariable=self.progress_var).grid(row=5, column=0, columnspan=3, pady=5)
def browse_input_file(self):
filename = filedialog.askopenfilename(
title="입력 파일 선택",
filetypes=[("Python files", "*.py"), ("All files", "*.*")]
)
if filename:
self.input_file_path.set(filename)
self.load_original_code()
# 자동으로 출력 파일명 설정
base_name = os.path.splitext(filename)[0]
self.output_file_path.set(f"{base_name}_obfuscated.py")
def browse_output_file(self):
filename = filedialog.asksaveasfilename(
title="출력 파일 선택",
defaultextension=".py",
filetypes=[("Python files", "*.py"), ("All files", "*.*")]
)
if filename:
self.output_file_path.set(filename)
def load_original_code(self):
file_path = self.input_file_path.get()
if file_path and os.path.exists(file_path):
try:
code = read_file(file_path)
self.original_text.delete(1.0, tk.END)
self.original_text.insert(1.0, code)
self.progress_var.set("원본 코드 로드됨")
except Exception as e:
messagebox.showerror("오류", f"파일 읽기 오류: {e}")
def run_obfuscation(self):
if not self.input_file_path.get():
messagebox.showwarning("경고", "입력 파일을 선택해주세요.")
return
if not self.output_file_path.get():
messagebox.showwarning("경고", "출력 파일을 선택해주세요.")
return
# 별도 스레드에서 실행
thread = threading.Thread(target=self._run_obfuscation_thread)
thread.daemon = True
thread.start()
def _run_obfuscation_thread(self):
try:
self.progress_var.set("난독화 진행 중...")
self.root.update()
# 원본 코드 읽기
original_code = read_file(self.input_file_path.get())
if not original_code:
messagebox.showerror("오류", "입력 파일을 읽을 수 없습니다.")
return
# 난독화 실행
obfuscator = CodeObfuscator()
obfuscated_code = obfuscator.obfuscate(
original_code,
level=self.obfuscation_level.get(),
encode=self.encode_code.get()
)
# 파일 저장
if write_file(self.output_file_path.get(), obfuscated_code):
# 결과 표시
self.obfuscated_text.delete(1.0, tk.END)
self.obfuscated_text.insert(1.0, obfuscated_code)
# 통계 계산
original_size = len(original_code)
obfuscated_size = len(obfuscated_code)
size_change = ((obfuscated_size - original_size) / original_size * 100)
stats = f"""난독화 완료!
파일 정보:
- 입력 파일: {self.input_file_path.get()}
- 출력 파일: {self.output_file_path.get()}
크기 정보:
- 원본 크기: {original_size:,} 문자
- 난독화 크기: {obfuscated_size:,} 문자
- 크기 변화: {size_change:+.1f}%
난독화 옵션:
- 레벨: {self.obfuscation_level.get()}
- 인코딩: {'예' if self.encode_code.get() else '아니오'}
- 주석 제거: {'예' if self.remove_comments.get() else '아니오'}
난독화된 파일이 성공적으로 저장되었습니다!
"""
self.stats_text.delete(1.0, tk.END)
self.stats_text.insert(1.0, stats)
self.notebook.select(2) # 통계 탭으로 이동
self.progress_var.set("난독화 완료!")
messagebox.showinfo("완료", "코드 난독화가 완료되었습니다!")
else:
messagebox.showerror("오류", "파일 저장에 실패했습니다.")
except Exception as e:
messagebox.showerror("오류", f"난독화 중 오류가 발생했습니다: {e}")
self.progress_var.set("오류 발생")
def preview_obfuscation(self):
if not self.input_file_path.get():
messagebox.showwarning("경고", "입력 파일을 선택해주세요.")
return
try:
self.progress_var.set("미리보기 생성 중...")
self.root.update()
# 원본 코드 읽기
original_code = read_file(self.input_file_path.get())
if not original_code:
messagebox.showerror("오류", "입력 파일을 읽을 수 없습니다.")
return
# 난독화 실행 (미리보기용)
obfuscator = CodeObfuscator()
obfuscated_code = obfuscator.obfuscate(
original_code,
level=self.obfuscation_level.get(),
encode=self.encode_code.get()
)
# 결과 표시
self.obfuscated_text.delete(1.0, tk.END)
self.obfuscated_text.insert(1.0, obfuscated_code)
# 통계 계산
original_size = len(original_code)
obfuscated_size = len(obfuscated_code)
size_change = ((obfuscated_size - original_size) / original_size * 100)
stats = f"""미리보기 통계:
크기 정보:
- 원본 크기: {original_size:,} 문자
- 난독화 크기: {obfuscated_size:,} 문자
- 크기 변화: {size_change:+.1f}%
난독화 옵션:
- 레벨: {self.obfuscation_level.get()}
- 인코딩: {'예' if self.encode_code.get() else '아니오'}
- 주석 제거: {'예' if self.remove_comments.get() else '아니오'}
"""
self.stats_text.delete(1.0, tk.END)
self.stats_text.insert(1.0, stats)
self.notebook.select(1) # 난독화된 코드 탭으로 이동
self.progress_var.set("미리보기 완료")
except Exception as e:
messagebox.showerror("오류", f"미리보기 생성 중 오류가 발생했습니다: {e}")
self.progress_var.set("오류 발생")
def reset_form(self):
self.input_file_path.set("")
self.output_file_path.set("")
self.obfuscation_level.set(3)
self.encode_code.set(False)
self.remove_comments.set(True)
self.original_text.delete(1.0, tk.END)
self.obfuscated_text.delete(1.0, tk.END)
self.stats_text.delete(1.0, tk.END)
self.progress_var.set("준비됨")
def main():
root = tk.Tk()
app = ObfuscatorGUI(root)
root.mainloop()
if __name__ == "__main__":
main()
내용이 길어 첨부파일로만 올립니다.
0.난독화_GUI.py의 레벨5를 사용하면서 코드 인코딩, 주석 암호화 또는 인코딩 암호화를 사용전 난독화 실행을 한번 실행후 결과를 확인해서 사용해보고 잘될때 코드 인코딩, 주석 암호화 또는 인코딩 암호화를 사용하시기 바랍니다.