import requests
import urllib3
import time
import re
import sys
import os

urllib3.disable_warnings()

BASE = 'https://dexshellx.com'
USER = 'slex'
PASS = 'pppp0000'
APK_PATH = 'demo2.apk'  # Must exist in same folder
FILTER = "!/\ncom/\n_COROUTINE/\nteacher/**"

session = requests.Session()
session.verify = False
session.headers.update({
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36'
})

log = []
def p(msg):
    print(msg, flush=True)
    log.append(msg)

p('=== STEP 1: Homepage (Get Cookie) ===')
r = session.get(BASE + '/', timeout=15)
p(f'Status: {r.status_code} | URL: {r.url}')

p('\n=== STEP 2: Login ===')
session.get(BASE + '/dex/login', timeout=10)
r2 = session.post(BASE + '/dex/login', data={
    'user': USER,
    'pass': PASS,
    'language': 'en_US'
}, timeout=15, allow_redirects=True)
p(f'Status: {r2.status_code} | Final URL: {r2.url}')
if '/login' in r2.url:
    p('FAILED: Still on login page!')
    sys.exit(1)
p('SUCCESS: Logged in!')

p('\n=== STEP 3: Check Protection Options Page ===')
r3 = session.get(BASE + '/dex/protection/options', timeout=15)
p(f'Status: {r3.status_code} | URL: {r3.url}')
# Save HTML for inspection
with open('options_page.html', 'w', encoding='utf-8', errors='replace') as f:
    f.write(r3.text)
p('Saved options_page.html for inspection')

# Find form fields
form_fields = re.findall(r'<input[^>]+name=["\']([^"\']+)["\']', r3.text)
form_selects = re.findall(r'<select[^>]+name=["\']([^"\']+)["\']', r3.text)
form_textareas = re.findall(r'<textarea[^>]+name=["\']([^"\']+)["\']', r3.text)
p(f'Form input names: {form_fields}')
p(f'Form select names: {form_selects}')
p(f'Form textarea names: {form_textareas}')

# Find action URL
action = re.findall(r'<form[^>]+action=["\']([^"\']+)["\']', r3.text)
p(f'Form actions: {action}')

p('\n=== STEP 4: Upload APK ===')
if not os.path.exists(APK_PATH):
    p(f'ERROR: {APK_PATH} not found!')
    sys.exit(1)

p(f'APK size: {os.path.getsize(APK_PATH)} bytes')

with open(APK_PATH, 'rb') as f:
    files = {'apkFile': (APK_PATH, f, 'application/vnd.android.package-archive')}
    post_data = {
        'mode': 'standard',
        'userModeValue': 'false',
        'signatureAlias': 'android',
        'stringFilters': FILTER,
        'hideFilters': FILTER,
        'classFilters': FILTER,
        'optimize': 'on',
        'stripLogging': 'on',
        'crashHandler': 'on',
        'webViewSupport': 'on',
        'manifestMangling': 'on',
        'assets': 'on',
        'res': 'on',
        'nameObf': 'on',
        'root': 'on',
        'strings': 'on',
        'annotationEnc': 'on',
        'stringEnc': 'on',
        'hideAccess': 'on',
        'classEnc': 'on',
        'jniObf': 'on',
        'nativeLib': 'on',
        'antiEmulator': 'on',
        'antiXposed': 'on',
    }
    p('Uploading APK (this may take 1-2 min)...')
    r4 = session.post(
        BASE + '/dex/protection/options',
        data=post_data,
        files=files,
        timeout=300,
        allow_redirects=True,
        headers={'Referer': BASE + '/dex/protection/options?mode=standard'}
    )

p(f'Upload status: {r4.status_code} | Final URL: {r4.url}')

# Save upload response
with open('upload_response.html', 'w', encoding='utf-8', errors='replace') as f:
    f.write(r4.text)
p('Saved upload_response.html')

# Extract process ID
process_id = None
patterns = [
    r'process/detail\?id=(\d+)',
    r'data-process-id=["\'](\d+)["\']',
    r'name=["\']processId["\'][^>]+value=["\'](\d+)["\']',
    r'"id"\s*:\s*(\d+)',
    r'processId["\']?\s*:\s*["\']?(\d+)',
]
for pat in patterns:
    m = re.search(pat, r4.text)
    if m:
        process_id = m.group(1)
        p(f'Process ID found: {process_id} (pattern: {pat})')
        break

if not process_id:
    p('ERROR: Could not find process ID! Check upload_response.html')
    sys.exit(1)

p(f'\n=== STEP 5: Poll Status (Process ID: {process_id}) ===')
poll_url = BASE + f'/dex/process/detail/data?id={process_id}'
detail_url = BASE + f'/dex/process/detail?id={process_id}'
max_wait = 1800  # 30 min
waited = 0
interval = 15
status = 'RUNNING'

while waited < max_wait:
    time.sleep(interval)
    waited += interval
    r5 = session.get(poll_url, timeout=30, headers={'Referer': detail_url, 'Accept': 'application/json, */*'})
    raw = r5.text
    
    try:
        import json
        j = json.loads(raw)
        if 'status' in j:
            status = j['status'].upper()
        p(f'[{waited}s] JSON status: {status} | raw: {raw[:100]}')
    except:
        m = re.search(r'data-status=["\']([^"\']+)["\']', raw)
        if m:
            status = m.group(1).upper()
        p(f'[{waited}s] HTML status: {status}')

    if status in ['DONE', 'SUCCESS', 'FINISHED', 'COMPLETED']:
        p('SUCCESS! APK is ready!')
        break
    elif status in ['ERROR', 'FAILED', 'FAILURE']:
        p(f'FAILED: Protection failed with status {status}')
        sys.exit(1)

if status not in ['DONE', 'SUCCESS', 'FINISHED', 'COMPLETED']:
    p('TIMEOUT: APK not ready in time!')
    sys.exit(1)

p(f'\n=== STEP 6: Download Protected APK ===')
r6 = session.get(detail_url, timeout=30)
dl_match = re.search(
    r'href=["\']([^"\']*process/download\?id=' + process_id + r'[^"\']*type=apk[^"\']*)["\']',
    r6.text
)
if not dl_match:
    # Try alternative patterns
    dl_match = re.search(r'href=["\']([^"\']*download[^"\']*)["\']', r6.text)
    if dl_match:
        p(f'Alt download URL found: {dl_match.group(1)}')
    else:
        with open('detail_page.html', 'w', encoding='utf-8', errors='replace') as f:
            f.write(r6.text)
        p('ERROR: Download link not found! Check detail_page.html')
        sys.exit(1)

dl_url = BASE + dl_match.group(1).replace('&amp;', '&')
p(f'Download URL: {dl_url}')

r7 = session.get(dl_url, timeout=300, headers={'Referer': detail_url})
p(f'Download status: {r7.status_code} | Content-Type: {r7.headers.get("Content-Type","")} | Size: {len(r7.content)} bytes')

if r7.status_code == 200 and len(r7.content) > 10000:
    out_file = 'demo2_PROTECTED.apk'
    with open(out_file, 'wb') as f:
        f.write(r7.content)
    p(f'DONE! Protected APK saved as: {out_file} ({len(r7.content)//1024} KB)')
else:
    p(f'ERROR: Download failed or file too small ({len(r7.content)} bytes)')

p('\n=== ALL DONE ===')
