靈感來源於:rwkgyg/sing-box-yg
目前各種 PaaS、SaaS、BaaS 等等平台,在用來托管一些常用的應用 —— 如 Xray、Alist 等等應用時,經常會提示違規,不允許部署。在經過大量的測試後,我發現此類平台多以關鍵字檢測作為封禁的因素,所以只要做到日誌、文件名、文本文件內容不含此類關鍵字,就能繼續部署,正常使用。
多數情況下,部署此類應用都是通過一個功能完善的 Shell 程序自動化進行,在其中把文件名和日誌做好處理即可,但是最後 shell 腳本本身的內容又不可避免的會出現 Xray、Alist 等應用的下載鏈接,導致 Shell 腳本本身被平台查處封禁。當然通過多個變量拆分構造下載鏈接避開關鍵字也是一種處理方法,但是無疑這增加了腳本的編寫複雜度,所以在通過查看 rwkgyg/sing-box-yg 時我獲取了靈感,編寫了一個 Python 腳本專門用於混淆 Shell 腳本:
import os
import uuid
import base64
# 讀取你的腳本
with open('script.sh', 'r') as f:
script = f.read()
# 使用 base64 對你的腳本進行編碼
encoded_script = base64.b64encode(script.encode()).decode()
# 定義每個字符串段的大小
chunk_size = 4
# 將編碼後的腳本分割成多個小段
chunks = [encoded_script[i:i+chunk_size]
for i in range(0, len(encoded_script), chunk_size)]
# 創建一個新的文件來存儲最終的腳本
with open('final_script.sh', 'w') as f:
# 輸出每個變量賦值語句
variable_names = []
for chunk in chunks:
while True:
var_name = str(uuid.uuid4()).replace('-', '') # 生成一個 UUID 作為變量名
if not var_name.startswith('0') and not var_name.startswith('1') and not var_name.startswith('2') and not var_name.startswith('3') and not var_name.startswith('4') and not var_name.startswith('5') and not var_name.startswith('6') and not var_name.startswith('7') and not var_name.startswith('8') and not var_name.startswith('9'):
break
variable_names.append(var_name)
f.write(f'{var_name}=\'{chunk}\'\n')
# 輸出連接所有變量的語句
f.write('eval "$(echo -n "')
for var_name in variable_names:
f.write(f'${var_name}')
f.write('" | base64 --decode)"')
# 修改文件權限,使其可執行
os.chmod('final_script.sh', 0o755)
這個 Python 腳本的原理很簡單,就是先把原 Shell 腳本使用 Base64 編碼,然後分割成很多個字符串,再使用 UUID 作為變量名,把每個字符串都存儲在一個變量中,最後把 UUID 變量全都連起來,使用 eval 命令執行。
簡單粗暴,效果很好,用了的都說好。
第二個版本:
import base64
import uuid
import re
def generate_var_names():
chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
for first in chars:
for second in chars:
yield f"{first}{second}"
while True:
var_name = str(uuid.uuid4()).replace('-', '')
if re.match(r'^[a-zA-Z]', var_name):
yield var_name
with open('your_script.sh', 'r') as file:
script_content = file.read()
first_encoded = base64.b64encode(script_content.encode('utf-8')).decode('utf-8')
first_eval_command = f'eval "$(echo -n "{first_encoded}" | base64 --decode)"'
second_encoded = base64.b64encode(first_eval_command.encode('utf-8')).decode('utf-8')
var_names = generate_var_names()
var_assignments = []
eval_parts = []
for i in range(0, len(second_encoded), 4):
chunk = second_encoded[i:i+4]
var_name = next(var_names)
var_assignments.append(f'{var_name}="{chunk}"')
eval_parts.append(f'${var_name}')
final_eval_command = f'eval "$(echo -n "{"".join(eval_parts)}" | base64 --decode)"'
var_assignment_line = ';'.join(var_assignments)
with open('obfuscated_script.sh', 'w') as file:
file.write(var_assignment_line + '\n')
file.write(final_eval_command + '\n')
print("原腳本已混淆為 obfuscated_script.sh")```
```
配套解碼:
```
import os
import re
def decode_script(script_content):
decoded_script = script_content
while True:
eval_match = re.search(r'eval\s*"\$\(\s*echo\s+-n\s+"([^"]+)"\s*\|\s*base64\s+--decode\s*\)"', decoded_script)
if not eval_match:
break
base64_string = eval_match.group(1)
modified_script = decoded_script.replace('eval', 'echo', 1)
result = os.popen(f'bash -c \'{modified_script}\'').read()
print("Decoded part:", result)
decoded_script = re.sub(r'eval\s*"\$\(\s*echo\s+-n\s+"([^"]+)"\s*\|\s*base64\s+--decode\s*\)"', result, decoded_script, count=1)
return result.strip()
with open('obfuscated_script.sh', 'r') as file:
obfuscated_script_content = file.read()
deobfuscated_script_content = decode_script(obfuscated_script_content)
with open('deobfuscated_script.sh', 'w') as file:
file.write(deobfuscated_script_content)
print("混淆腳本已逆向生成 deobfuscated_script.sh")
```