Oxyry Python Obfuscator(https://pyob.oxyry.com/)코드 난독화 사이트가 없어져서 그냥 제가 만들었습니다.
왜 없애는지 어차피 이렇게 개발자들이 다시 만들텐데 정말 귀찮게 됐었네요.
필요하신분 아래 코드 또는 첨부 파일 다운받으셔서 난독화_GUI.py 사용하시면 됩니다.
링크 : https://github.com/all-share-source-code/code-encryption.git
GitHub - all-share-source-code/code-encryption
Contribute to all-share-source-code/code-encryption development by creating an account on GitHub.
github.com
#!/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를 사용하면서 코드 인코딩, 주석 암호화 또는 인코딩 암호화를 사용전 난독화 실행을 한번 실행후 결과를 확인해서 사용해보고 잘될때 코드 인코딩, 주석 암호화 또는 인코딩 암호화를 사용하시기 바랍니다.