Skip to content

Commit e9f8408

Browse files
committed
Updated README.md, code logic and performance
1 parent a468194 commit e9f8408

File tree

3 files changed

+165
-125
lines changed

3 files changed

+165
-125
lines changed

README.md

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# PHP Obfuscator
2+
3+
## Overview
4+
5+
PHP Obfuscator is a command-line tool to obfuscate PHP source code files using YAK Pro - Php Obfuscator and PHP-Parser. The tool aims to protect the intellectual property of your PHP project by making it more difficult for others to understand or reverse-engineer your code. The tool can be used to obfuscate single files, multiple files, or an entire project directory.
6+
7+
## Requirements
8+
9+
- Python 3.6+
10+
- PHP-Parser (PHP library)
11+
- YAK Pro - Php Obfuscator (PHP library)
12+
13+
## Installation
14+
15+
1. Clone the repository
16+
17+
```
18+
git clone https://github.com/your-github-repo/php-obfuscator.git
19+
```
20+
21+
2. Change to the project directory
22+
23+
```
24+
cd php-obfuscator
25+
```
26+
27+
3. Install YAK Pro - Php Obfuscator
28+
29+
You need to install this in to the project directory **_php-obfuscator_**
30+
31+
```
32+
git clone https://github.com/pk-fr/yakpro-po.git
33+
```
34+
35+
After that, you need to configure the package from `yakpro-po.cnf` in to the **_php-obfuscator > yakpro-po_** folder.
36+
37+
**Recommended settings for the YAK Pro configuration:**
38+
39+
```php
40+
// Allowed modes are: 'PREFER_PHP7', 'PREFER_PHP5', 'ONLY_PHP7', 'ONLY_PHP5'
41+
$conf->parser_mode = 'PREFER_PHP7';
42+
43+
$conf->obfuscate_constant_name = false;
44+
$conf->obfuscate_variable_name = false;
45+
$conf->obfuscate_function_name = false;
46+
$conf->obfuscate_class_name = false;
47+
$conf->obfuscate_interface_name = false;
48+
$conf->obfuscate_trait_name = false;
49+
$conf->obfuscate_class_constant_name = false;
50+
$conf->obfuscate_property_name = false;
51+
$conf->obfuscate_method_name = false;
52+
$conf->obfuscate_namespace_name = false;
53+
$conf->obfuscate_label_name = false;
54+
55+
// User comment to insert inside each obfuscated file
56+
$conf->user_comment = false;
57+
```
58+
59+
4. Install PHP-Parser
60+
61+
You need to install this in to the **yakpro-po** directory in to the project directory **_php-obfuscator > yakpro-po_**
62+
63+
```
64+
git clone https://github.com/nikic/PHP-Parser.git
65+
```
66+
5. Install the required Python packages
67+
68+
**From the project directory, you need to run this command:**
69+
70+
```
71+
pip install -r requirements.txt
72+
```
73+
74+
## Usage
75+
76+
1. Ensure you are in the project directory.
77+
2. Run the script
78+
79+
```
80+
python main.py
81+
```
82+
83+
or
84+
85+
```
86+
bash start.sh
87+
```
88+
89+
**Note:** Don't forget to change the path to the script in to the `start.sh` bash file.
90+
91+
3. Follow the prompts to configure the obfuscation settings, including:
92+
- Mode (single file, multiple files, or entire project directory)
93+
- Output directory path
94+
- File or directory paths to exclude
95+
- Whether to create backups of original PHP files
96+
97+
4. After the obfuscation process is completed, you can find the obfuscated files in the specified output directory.
98+
99+
## Used Libraries
100+
101+
### Python
102+
103+
- **os**, **sys**, **shutil**, and **re** - standard Python libraries for working with the file system and regular expressions
104+
- **logging** - standard Python library for logging
105+
- **concurrent.futures** - standard Python library for parallel processing
106+
- **tqdm** - external library for progress bars
107+
108+
### PHP
109+
110+
- [YAK Pro - a PHP library for obfuscating PHP code](https://github.com/pk-fr/yakpro-po)
111+
- [PHP-Parser - a PHP library to parse and traverse PHP code](https://github.com/nikic/PHP-Parser/tree/4.x/)
112+
113+
## Troubleshooting
114+
115+
If you encounter issues after obfuscating your PHP project, you may need to revert your files to the original (non-obfuscated) versions and reevaluate your obfuscation strategy. Always keep backups of your original code before applying obfuscation, as it can be difficult or impossible to reverse the process and recover the original code.
116+
117+
## License
118+
119+
This project is released under the MIT License.

main.py

Lines changed: 45 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,26 @@
11
import sys
2-
import base64
32
import os
4-
import re
53
import logging
64
import shutil
7-
from zlib import compress
85
from concurrent.futures import ThreadPoolExecutor
96
from tqdm import tqdm
107
from config import (log_filename, YELLOW, BLUE, GREEN, RED, RESET)
8+
from subprocess import call
119

1210
# Configure logging
1311
logging.basicConfig(filename=log_filename, level=logging.DEBUG, format='%(asctime)s %(levelname)s:%(message)s', datefmt='%Y-%m-%d %H:%M:%S')
1412

15-
def obfuscate_php(input_file, obfuscation_level, create_backup, output_directory):
13+
def obfuscate_php(input_file, create_backup, output_directory):
1614
if create_backup:
1715
backup_file = f"{os.path.splitext(input_file)[0]}_backup.php"
1816
shutil.copy2(input_file, backup_file)
1917
logging.info(f"Created backup: {backup_file}")
2018

2119
try:
22-
logging.info(f"Obfuscating {input_file} with level: {obfuscation_level}")
23-
24-
with open(input_file, "r") as f:
25-
php_code = f.read()
26-
27-
if obfuscation_level == 'low':
28-
encoded_php_code = base64.b64encode(php_code.encode()).decode()
29-
obfuscated_code = f"<?php eval(base64_decode('{encoded_php_code}')); ?>"
30-
elif obfuscation_level == 'medium':
31-
compressed_php_code = compress(php_code.encode())
32-
encoded_php_code = base64.b64encode(compressed_php_code).decode()
33-
obfuscated_code = f"<?php eval(gzinflate(base64_decode('{encoded_php_code}'))); ?>"
34-
elif obfuscation_level == 'high':
35-
compressed_php_code = compress(php_code.encode())
36-
encoded_php_code = base64.b64encode(compressed_php_code).decode()
37-
obfuscated_code = f"<?php eval(gzinflate(base64_decode('{encoded_php_code}'))); ?>"
38-
39-
var_pattern = re.compile(r'\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*')
40-
var_names = set(var_pattern.findall(obfuscated_code))
41-
for var_name in var_names:
42-
new_var_name = f"${obfuscation_level}_{var_name[1:]}"
43-
obfuscated_code = obfuscated_code.replace(var_name, new_var_name)
44-
45-
# Save the obfuscated code to the output directory
4620
output_file = os.path.join(output_directory, f"obfuscated_{os.path.basename(input_file)}")
47-
with open(output_file, "w") as f:
48-
f.write(obfuscated_code)
21+
logging.info(f"Obfuscating {input_file}")
22+
23+
call(["/home/mnestorov/AutomationScripts/ObfuscatesPhpCode/yakpro-po/yakpro-po.php", "-o", output_file, input_file])
4924

5025
print(f"{GREEN}Obfuscated file saved as {output_file}{RESET}")
5126
logging.info(f"Obfuscated {input_file} successfully")
@@ -55,10 +30,10 @@ def obfuscate_php(input_file, obfuscation_level, create_backup, output_directory
5530
print(f"{RED}Error while obfuscating {input_file}: {e}{RESET}")
5631

5732
def obfuscate_file(args):
58-
input_file, obfuscation_level, create_backup, output_directory = args
59-
obfuscate_php(input_file, obfuscation_level, create_backup, output_directory)
33+
input_file, create_backup, output_directory = args
34+
obfuscate_php(input_file, create_backup, output_directory)
6035

61-
def process_directory(directory, obfuscation_level, exclude_list, create_backup, output_directory, max_workers=4):
36+
def process_directory(directory, exclude_list, create_backup, output_directory, max_workers=4):
6237
total_files = 0
6338
for root, _, files in os.walk(directory):
6439
for file in files:
@@ -77,7 +52,7 @@ def process_directory(directory, obfuscation_level, exclude_list, create_backup,
7752
logging.info(f"Skipping {input_file}: excluded")
7853
continue
7954

80-
file_list.append((input_file, obfuscation_level, create_backup, output_directory))
55+
file_list.append((input_file, create_backup, output_directory))
8156

8257
progress_bar = tqdm(total=len(file_list), desc="Obfuscating", unit="file")
8358

@@ -86,9 +61,9 @@ def process_directory(directory, obfuscation_level, exclude_list, create_backup,
8661
progress_bar.update()
8762

8863
progress_bar.close()
89-
90-
def interactive_mode():
91-
print(f"{GREEN}Welcome to the PHP Obfuscator Interactive Mode!{RESET}")
64+
65+
def main():
66+
print(f"{GREEN}Welcome to the PHP Obfuscator!{RESET}")
9267
print(f"{GREEN}Follow the prompts to obfuscate your PHP files.\n{RESET}")
9368

9469
print(f"{GREEN}Choose the mode for obfuscating your PHP files:{RESET}")
@@ -97,102 +72,47 @@ def interactive_mode():
9772
print(f"{BLUE}3: Entire project directory{RESET}")
9873
mode = input(f"{GREEN}Enter the mode number (1/2/3): {RESET}")
9974

100-
print(f"{GREEN}\nChoose the obfuscation level:{RESET}")
101-
print(f"{BLUE}1: Low (Base64 encoding){RESET}")
102-
print(f"{BLUE}2: Medium (zlib compression + Base64 encoding){RESET}")
103-
print(f"{BLUE}3: High (zlib compression + Base64 encoding + simple variable renaming){RESET}")
104-
obfuscation_level = input(f"{GREEN}Enter the obfuscation level number (1/2/3): {RESET}")
105-
106-
exclude_input = input(f"{GREEN}\nEnter a comma-separated list of files or directories to exclude (leave empty if none): {RESET}")
107-
exclude_list = [path.strip() for path in exclude_input.split(',') if path.strip()]
108-
109-
backup_input = input(f"{GREEN}\nCreate backups of original PHP files? (y/n): {RESET}")
110-
create_backup = backup_input.lower() == 'y'
111-
112-
output_directory = input(f"{GREEN}\nEnter the output directory path: {RESET}")
75+
output_directory = input(f"{GREEN}Enter the output directory path: {RESET}")
11376
if not os.path.exists(output_directory):
11477
os.makedirs(output_directory)
11578

116-
if mode == '1':
117-
input_file = input(f"{GREEN}\nEnter the PHP file path: {RESET}")
118-
obfuscate_php(input_file, obfuscation_level, create_backup, output_directory)
119-
elif mode == '2':
120-
input_files = input(f"{GREEN}\nEnter a comma-separated list of PHP files to obfuscate: {RESET}")
121-
files = [file.strip() for file in input_files.split(',') if file.strip()]
122-
for file in files:
123-
obfuscate_php(file, obfuscation_level, create_backup, output_directory)
124-
elif mode == '3':
125-
input_directory = input(f"{GREEN}\nEnter the directory path containing PHP files: {RESET}")
126-
process_directory(input_directory, obfuscation_level, exclude_list, create_backup, output_directory)
127-
else:
128-
print(f"{RED}Invalid mode. Exiting...{RESET}")
129-
sys.exit(1)
130-
131-
def main():
132-
interactive = input(f"{YELLOW}Do you want to run the script in interactive mode? (y/n): {RESET}").lower()
133-
if interactive == 'y':
134-
interactive_mode()
135-
else:
136-
print(f"{GREEN}Choose the mode for obfuscating your PHP files: {RESET}")
137-
print(f"{BLUE}1: Single file{RESET}")
138-
print(f"{BLUE}2: Multiple files{RESET}")
139-
print(f"{BLUE}3: Entire project directory{RESET}")
140-
mode = input(f"{GREEN}Enter the mode number (1/2/3): {RESET}")
141-
142-
print(f"{GREEN}\nSelect obfuscation level: {RESET}")
143-
print(f"{BLUE}1: Low (Base64 encoding){RESET}")
144-
print(f"{BLUE}2: Medium (zlib compression + Base64 encoding){RESET}")
145-
print(f"{BLUE}3: High (zlib compression + Base64 encoding + simple variable renaming){RESET}")
146-
obfuscation_level = input(f"{GREEN}Enter the obfuscation level number (1/2/3): {RESET}")
147-
148-
output_directory = input(f"{GREEN}Enter the output directory path: {RESET}")
149-
if not os.path.exists(output_directory):
150-
os.makedirs(output_directory)
151-
152-
level_mapping = {'1': 'low', '2': 'medium', '3': 'high'}
153-
if obfuscation_level not in level_mapping:
154-
logging.warning("Invalid obfuscation level. Choose from: 1, 2, or 3")
155-
print(f"{RED}Invalid obfuscation level. Choose from: 1, 2, or 3{RESET}")
156-
sys.exit(1)
157-
obfuscation_level = level_mapping[obfuscation_level]
79+
exclude_input = input(f"{GREEN}Enter file or directory paths to exclude (separated by a space) (you can skip this step): {RESET}")
80+
exclude_list = [os.path.abspath(exclude.strip()) for exclude in exclude_input.split()]
15881

159-
exclude_input = input(f"{GREEN}Enter file or directory paths to exclude (separated by a space): {RESET}")
160-
exclude_list = [os.path.abspath(exclude.strip()) for exclude in exclude_input.split()]
82+
backup_input = input(f"{GREEN}Create backups of original PHP files? (y/n): {RESET}").lower()
83+
create_backup = backup_input == 'y'
16184

162-
backup_input = input(f"{GREEN}Create backups of original PHP files? (y/n): {RESET}").lower()
163-
create_backup = backup_input == 'y'
85+
if mode == '1':
86+
input_file = input(f"{GREEN}Enter the PHP file path: {RESET}")
87+
if not input_file.lower().endswith(".php") or not os.path.isfile(input_file):
88+
logging.warning("Invalid PHP file path")
89+
print(f"{RED}Invalid PHP file path{RESET}")
90+
sys.exit(1)
91+
if any(os.path.commonpath([input_file, exclude]) == os.path.abspath(exclude) for exclude in exclude_list):
92+
logging.info(f"Skipping {input_file}: excluded")
93+
else:
94+
obfuscate_php(input_file, create_backup, output_directory)
95+
elif mode == '2':
96+
file_paths = input(f"{GREEN}Enter the PHP file paths separated by a space: {RESET}")
97+
files = file_paths.split()
16498

165-
if mode == '1':
166-
input_file = input(f"{GREEN}Enter the PHP file path: {RESET}")
99+
for input_file in files:
167100
if not input_file.lower().endswith(".php") or not os.path.isfile(input_file):
168-
logging.warning("Invalid PHP file path")
169-
print(f"{RED}Invalid PHP file path{RESET}")
170-
sys.exit(1)
171-
if any(os.path.commonpath([input_file, exclude]) == os.path.abspath(exclude) for exclude in exclude_list):
172-
logging.info(f"Skipping {input_file}: excluded")
173-
else:
174-
obfuscate_php(input_file, obfuscation_level, create_backup)
175-
elif mode == '2':
176-
file_paths = input(f"{GREEN}Enter the PHP file paths separated by a space: {RESET}")
177-
files = file_paths.split()
178-
179-
for input_file in files:
180-
if not input_file.lower().endswith(".php") or not os.path.isfile(input_file):
181-
logging.warning(f"Skipping {input_file}: not a valid PHP file")
182-
print(f"{RED}Skipping {input_file}: not a valid PHP file{RESET}")
183-
continue
184-
obfuscate_php(input_file, obfuscation_level, create_backup)
185-
elif mode == '3':
186-
input_directory = input(f"{GREEN}Enter the project directory path: {RESET}")
187-
if not os.path.isdir(input_directory):
188-
logging.warning("Invalid directory path")
189-
print(f"{RED}Invalid directory path{RESET}")
190-
sys.exit(1)
191-
process_directory(input_directory, obfuscation_level, exclude_list, create_backup)
192-
else:
193-
logging.warning("Invalid mode. Choose from: 1, 2, or 3")
194-
print(f"{RED}Invalid mode. Choose from: 1, 2, or 3{RESET}")
101+
logging.warning(f"Skipping {input_file}: not a valid PHP file")
102+
print(f"{RED}Skipping {input_file}: not a valid PHP file{RESET}")
103+
continue
104+
obfuscate_php(input_file, create_backup, output_directory)
105+
elif mode == '3':
106+
input_directory = input(f"{GREEN}Enter the project directory path: {RESET}")
107+
if not os.path.isdir(input_directory):
108+
logging.warning("Invalid directory path")
109+
print(f"{RED}Invalid directory path{RESET}")
195110
sys.exit(1)
111+
process_directory(input_directory, exclude_list, create_backup, output_directory)
112+
else:
113+
logging.warning("Invalid mode. Choose from: 1, 2, or 3")
114+
print(f"{RED}Invalid mode. Choose from: 1, 2, or 3{RESET}")
115+
sys.exit(1)
196116

197117
if __name__ == "__main__":
198118
main()

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
tqdm

0 commit comments

Comments
 (0)