找到
295
篇与
易航
相关的结果
- 第 25 页
-
易航网址引导系统 – 功能强大,轻量易用 概述 易航网址引导系统不仅是一款功能强大、轻量便捷的网址导航系统,更是一个旨在解决网址变更带来的用户流失问题,并提供灵活的定制化方案的网址引导系统。 主要功能 网站收录: 快速高效: 提供一站式网站快速收录服务,高效自动化的收录流程,确保您的网站快速被主流搜索引擎索引。 智能审核: 支持自动审核收录申请,判断条件为对方站点首页是否添加本站链接,方便快捷。 个性化设置: 支持自定义自动收录分类和排除站点,满足您的个性化需求。 实时排名: 支持站点自动排名,按对方站点最新点入时间排序,帮助您了解热门链接。 邮件通知: 支持站点提交通知功能,收录成功后自动发送邮件通知,方便您及时掌握网站状态。 友链管理: 便捷管理: 提供友链列表展示和新增功能,方便您管理友链。 灵活自定义: 可以自定义友情链接的rel属性,满足不同需求。 站点管理: 全面掌控: 提供站点列表展示和新增功能,方便您管理所有站点。 个性化配置: 支持对站点的详细设置,例如标题、链接、关键词、描述、图标、备案号、QQ、置顶、广告等,打造个性化网站。 自助工具: 快速导入: 支持一键导入Edge/Google浏览器书签链接(Netscape Bookmark格式文件都可使用),方便快捷。 一键还原: 可一键恢复出厂设置,无需手动删除系统重装,操作简便。 系统设置功能和亮点 系统设置功能: 网站设置 (site): 设置网站基本信息,包括网站标题、副标题、关键词、描述、站长QQ号、Favicon、Logo、ICP备案号、版权信息、网站创建日期等。并支持开启/关闭网站伪静态功能,提供Nginx和Apache的伪静态配置示例。 后台设置 (admin): 设置后台安全性和用户体验,包括开启/关闭后台登录验证码、后台加载动画和自定义后台登录页面背景等。 主题设置 (pane-theme): 自定义网站主题,包括配置公共静态资源CDN地址,提升网站加载速度,以及自定义网站背景壁纸,增加个性化。 邮箱与提醒 (email): 配置邮件发送和接收设置,包括SMTP服务器地址、加密方式、SMTP端口、发信邮箱、邮箱密码和收信邮箱等。 亮点: 模块化设计: 将系统设置划分为多个模块,方便管理和操作,提高效率。 简洁易用: 界面简洁,操作简单,易于理解,即使没有专业技术也能轻松上手。 安全可靠: 提供登录验证码和邮箱加密等安全措施,保护系统安全,让您安心使用。 个性化定制: 支持自定义网站信息、后台背景、主题背景等,满足个性化需求。 性能优化: 提供CDN配置功能,提升网站加载速度,优化用户体验。 插件功能和亮点整理 安全防护: 防CC模块 (CcDefend): 提供CC攻击防护功能,有效防御CC攻击,保障网站安全。 网站防火墙 (WebScan): 防止站点被攻击和注入非法代码,为网站提供全面安全防护。 隐藏内容,请前往内页查看详情 隐藏内容,请前往内页查看详情 用户体验: 隐藏内容,请前往内页查看详情 音乐播放器 (MusicPlayer): 支持播放各大平台音乐,丰富网站内容,提升用户体验。 功能增强: 自动审核友链 (AutoFriend): 通过对方站点进入本站首页后将自动添加对方友链,简化友链管理,节省时间。 插入代码 (InsertCode): 前台页面头部和底部插入自定义代码,方便添加统计代码、广告代码等,增强网站功能。 灯笼挂件 (NewYearLantern): 一款可以自定义节日的灯笼挂件,提升网站节日气氛,营造欢乐氛围。 插件亮点: 功能丰富: 提供多种功能插件,满足不同需求,增强网站功能。 安全可靠: 提供安全防护插件,保护网站安全,确保网站稳定运行。 易于使用: 插件操作简单,易于安装和使用,无需复杂操作。 持续更新: 不断更新插件功能,提升用户体验,始终保持最新功能。 易航出品: 由易航团队开发,质量保证,值得信赖。 易航网址导航系统的优势 易航网址导航系统不仅是一个网址导航系统,同时也是一款功能强大而又轻量的网址引导系统。它具有以下显著特点和优势: 解决网址变更的问题: 在诸如敏感行业和其他需要频繁更换网址的场景中,易航网址引导系统提供了一个稳定的平台。用户无需担心客户因为找不到新的网址而流失的问题。 多样化的主题和自定义能力: 系统内置多达26套主题,每套主题都可以进行自定义设置。这使得用户可以根据自己的品牌风格或需求,轻松地创建个性化的引导页面。 插件化开发: 易航网址引导系统支持插件化开发,目前内置了七款实用插件,用户可以根据需要自行开发定制插件,进一步增强系统的功能和灵活性。 轻量级且易于部署: 无需复杂的安装和解压过程,即可快速部署和使用。这大大降低了使用门槛,使得系统在不同的环境中都能轻松应用。 数据管理和安全性: 采用易航原创的JsonDb数据包进行数据管理,同时内置硬防洪和硬防墙插件,有效防范恶意攻击,确保系统的稳定和安全性。 SEO友好: 对前台URL进行伪静态重写,这使得系统在搜索引擎中的表现更为友好,有助于提升页面的搜索排名和曝光度。 使用指南 使用文档:https://yepydvmpxo.k.topthink.com/@php-website-guide/ 后台演示地址:http://guide.demo.yihang.info/admin 前台演示地址:http://guide.demo.yihang.info 主题截图 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 安装教程 PHP版本:8.2 Nginx服务器首先站点部署伪静态 location ~* ^/content/JsonDb/.*.(json)$ { deny all; } location / { if (!-e $request_filename){ rewrite ^(.*)$ /index.php?s=$1 last; break; } }后台地址:域名/admin 初始账号:admin 初始密码:123456 源码下载 易航网址引导系统 下载地址:https://wwm.lanzoue.com/b01k39s9g 提取码:6666 更新日志 更新日志.md · 易航/网址引导系统 – Gitee.com
-
凡人修仙传高燃视频 第01集 何不过去讨杯喜酒吃? 第02集 跟这里的一切告个别吧,再回首,物是人非 第03集 各位道友来说说你们是怎么喜欢上这《凡人修仙传》的? 第04集 修仙路远,往来皆过客,何曾有归人 第05集 句句不提悲痛,句句都是心酸 第06集 我也想 成为你这样的修士 第07集 天南大战 第08集 不是爬上去就能通过吗,为什么做这么绝啊 第09集 启蒙老师墨老 第10集 凭什么仙家就可以遨游天地,而我等凡人只能做这井底之蛙 第11集 人生苦短,终归尘土,这世间多少好景色,你就代为师去看看吧 第12集 生动刻画了仙凡终有一别! 第13集 金青要是不跑的话,或许还有一线生机吧 第14集 人生如棋,落子无悔! 第15集 一向冷静谨慎的韩立,这次面对曲魂被夺舍终于不再冷静! 第16集 这不是结丹了嘛,想试试遁术来着 第17集 二十四年,韩立成功度过红尘劫 第18集 红尘气,催人老,十余年,当真是弹指一挥间,或者说,凡人本就要走这一遭,人生如棋落子无悔,这让韩立更加明白,仙凡有别的道理 第19集 成王败寇,自有天定 凭什么仙家就可以遨游天地,而我等凡人只能做这井底之蛙!
-
初探PHP-Parser和PHP代码混淆 初探PHP-Parser PHP-Parser 是 nikic 用PHP编写的PHP5.2到PHP7.4解析器,其目的是简化静态代码分析和操作。 解析 创建一个解析器实例: use PhpParser\ParserFactory; $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);ParserFactory接收以下几个参数: ParserFactory::PREFER_PHP7 :优先解析PHP7,如果PHP7解析失败则将脚本解析成PHP5 ParserFactory::PREFER_PHP5 :优先解析PHP5,如果PHP5解析失败则将脚本解析成PHP7 ParserFactory::ONLY_PHP7 :只解析成PHP7 ParserFactory::ONLY_PHP5 :只解析成PHP5 将PHP脚本解析成抽象语法树(AST) <?php use PhpParser\Error; use PhpParser\ParserFactory; require 'vendor/autoload.php'; $code = file_get_contents("./test.php"); $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); try { $ast = $parser->parse($code); } catch (Error $error) { echo "Parse error: {$error->getMessage()}"; } var_dump($ast);节点转储 如果是用上面的 var_dump 的话显示的 AST 可能会比较乱,那么我们可以使用 NodeDumper 生成一个更加直观的 AST <?php use PhpParser\NodeDumper; $nodeDumper = new NodeDumper; echo $nodeDumper->dump($stmts);或者我们使用 vendor/bin/php-parse 也是一样的效果 λ vendor/bin/php-parse test.php ====> File test.php: ==> Node dump: array( 0: Stmt_Expression( expr: Expr_Assign( var: Expr_Variable( name: a ) expr: Scalar_LNumber( value: 1 ) ) ) )节点树结构 PHP是一个成熟的脚本语言,它大约有140个不同的节点。但是为了方便使用,将他们分为三类: PhpParser\Node\Stmts 是语句节点,即不返回值且不能出现在表达式中的语言构造。例如,类定义是一个语句,它不返回值,你不能编写类似 func(class {}) 的语句。 PhpParser\Node\expr 是表达式节点,即返回值的语言构造,因此可以出现在其他表达式中。如:$var (PhpParser\Node\Expr\Variable)和func() (PhpParser\Node\Expr\FuncCall)。 PhpParser\Node\Scalars 是表示标量值的节点,如"string" (PhpParser\Node\scalar\string)、0 (PhpParser\Node\scalar\LNumber) 或魔术常量,如"FILE" (PhpParser\Node\scalar\MagicConst\FILE) 。所有 PhpParser\Node\scalar 都是延伸自 PhpParser\Node\Expr ,因为 scalar 也是表达式。 需要注意的是 PhpParser\Node\Name 和 PhpParser\Node\Arg 不在以上的节点之中 格式化打印 使用 PhpParser\PrettyPrinter 格式化代码 <?php use PhpParser\Error; use PhpParser\ParserFactory; use PhpParser\PrettyPrinter; require 'vendor/autoload.php'; $code = file_get_contents('./index.php'); $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); try { $ast = $parser->parse($code); } catch (Error $error) { echo "Parse error: {$error->getMessage()}"; return; } $prettyPrinter = new PrettyPrinter\Standard; $prettyCode = $prettyPrinter->prettyPrintFile($ast); echo $prettyCode;节点遍历 使用 PhpParser\NodeTraverser 我们可以遍历每一个节点,举几个简单的例子:解析php中的所有字符串,并输出 <?php use PhpParser\Error; use PhpParser\ParserFactory; use PhpParser\NodeTraverser; use PhpParser\NodeVisitorAbstract; use PhpParser\Node; require 'vendor/autoload.php'; class MyVisitor extends NodeVisitorAbstract { public function leaveNode(Node $node) { if ($node instanceof Node\Scalar\String_) { echo $node->value; } } } $code = file_get_contents("./test.php"); $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); $traverser = new NodeTraverser; $traverser->addVisitor(new MyVisitor); try { $ast = $parser->parse($code); $stmts = $traverser->traverse($ast); } catch (Error $error) { echo "Parse error: {$error->getMessage()}"; return; }遍历php中出现的函数以及类中的成员方法 <?php class MyVisitor extends NodeVisitorAbstract { public function leaveNode(Node $node) { if ( $node instanceof Node\Expr\FuncCall || $node instanceof Node\Stmt\ClassMethod || $node instanceof Node\Stmt\Function_ || $node instanceof Node\Expr\MethodCall ) { echo $node->name; } } }替换php脚本中函数以及类的成员方法函数名为小写 <?php class MyVisitor extends NodeVisitorAbstract { public function leaveNode(Node $node) { if ($node instanceof Node\Expr\FuncCall) { $node->name->parts[0] = strtolower($node->name->parts[0]); } elseif ($node instanceof Node\Stmt\ClassMethod) { $node->name->name = strtolower($node->name->name); } elseif ($node instanceof Node\Stmt\Function_) { $node->name->name = strtolower($node->name->name); } elseif ($node instanceof Node\Expr\MethodCall) { $node->name->name = strtolower($node->name->name); } } }需要注意的是所有的 visitors 都必须实现 PhpParser\NodeVisitor 接口,该接口定义了如下4个方法: public function beforeTraverse(array $nodes); public function enterNode(\PhpParser\Node $node); public function leaveNode(\PhpParser\Node $node); public function afterTraverse(array $nodes); beforeTraverse 方法在遍历开始之前调用一次,并将其传递给调用遍历器的节点。此方法可用于在遍历之前重置值或准备遍历树。 afterTraverse 方法与 beforeTraverse 方法类似,唯一的区别是它只在遍历之后调用一次。 在每个节点上都调用 enterNode 和 leaveNode 方法,前者在它被输入时,即在它的子节点被遍历之前,后者在它被离开时。 这四个方法要么返回更改的节点,要么根本不返回(即null),在这种情况下,当前节点不更改。 PHP代码混淆 下面举两个php混淆的例子,比较简单(郑老板@zsx所说的20分钟内能解密出来的那种),主要是加深一下我们对 PhpParser 使用 phpjiami 大部分混淆都会把代码格式搞得很乱,用 PhpParser\PrettyPrinter 格式化一下代码 <?php use PhpParser\Error; use PhpParser\ParserFactory; use PhpParser\PrettyPrinter; require 'vendor/autoload.php'; $code = file_get_contents('./test.php'); $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); try { $ast = $parser->parse($code); } catch (Error $error) { echo "Parse error: {$error->getMessage()}\n"; return; } $prettyPrinter = new PrettyPrinter\Standard; $prettyCode = $prettyPrinter->prettyPrintFile($ast); file_put_contents('en_test.php', $prettyCode);格式基本能看了 图片 因为函数和变量的乱码让我们之后的调试比较难受,所以简单替换一下混淆的函数和变量 <?php use PhpParser\Error; use PhpParser\NodeFinder; use PhpParser\ParserFactory; require 'vendor/autoload.php'; $code = file_get_contents("./index_1.php"); $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); $nodeFinder = new NodeFinder; try { $ast = $parser->parse($code); } catch (Error $error) { echo "Parse error: {$error->getMessage()}\n"; return; } $Funcs = $nodeFinder->findInstanceOf($ast, PhpParser\Node\Stmt\Function_::class); $map = []; $v = 0; foreach ($Funcs as $func) { $funcname = $func->name->name; if (!isset($map[$funcname])) { if (!preg_match('/^[a-z0-9A-Z_]+$/', $funcname)) { $code = str_replace($funcname, "func" . $v, $code); $v++; $map[$funcname] = $v; } } } $v = 0; $map = []; $tokens = token_get_all($code); foreach ($tokens as $token) { if ($token[0] === T_VARIABLE) { if (!isset($map[$token[1]])) { if (!preg_match('/^\$[a-zA-Z0-9_]+$/', $token[1])) { $code = str_replace($token[1], '$v' . $v++, $code); $map[$token[1]] = $v; } } } } file_put_contents("index_2.php", $code);变量和函数基本能看了,还是有一些数据是乱码,这个是它自定义函数加密的字符串,大多数都是php内置的函数,我们调试一下就基本能看到了 图片 但是得注意一下,phpjiami有几个反调试的地,在35行的地方打个断点 图片 可以看到4个反调试的点: 第一个点: 当你是以cli运行php的时候就会直接 die() 掉,直接注释掉即可 php_sapi_name()=="cli" ? die() : '';第二个点: 和第一个差不多,也是验证运行环境的,cli模式下没有这些变量索引,直接注释即可 if (!isset($_SERVER["HTTP_HOST"]) && !isset($_SERVER["SERVER_ADDR"]) && !isset($_SERVER["REMOTE_ADDR"])) { die(); }第三个点: 如果你在 if 语句处停留超过100ms的话就会直接 die 掉,注释即可 $v46 = microtime(true) * 1000; eval(""); if (microtime(true) * 1000 - $v46 > 100) { die(); }第四个点: $51就是整个文件内容,这行是用于加密前的文件对比是否完整,如果不完整则执行$52(),因为$52不存在所以会直接报错退出,而如果对比是完整的话那么就是$53,虽然$53也不存在,但只是抛出一个Warning,所以我们这里也是直接把这行注释掉。 !strpos(func2(substr($v51, func2("???"), func2("???"))), md5(substr($51, func2("??"), func2("???")))) ? $52() : $53;注释完之后我们在return那里打一个断点,可以发现在return那里我们需要解密的文件内容呈现了出来。 图片 解密之后的内容: <?php @eval("//Encode by phpjiami.com,Free user."); // Clear the uploads directory every hour highlight_file(__FILE__); $sandbox = "uploads/" . md5("De1CTF2020" . $_SERVER['REMOTE_ADDR']); @mkdir($sandbox); @chdir($sandbox); if ($_POST["submit"]) { if (($_FILES["file"]["size"] < 2048) && Check()) { if ($_FILES["file"]["error"] > 0) { die($_FILES["file"]["error"]); } else { $filename = md5($_SERVER['REMOTE_ADDR']) . "_" . $_FILES["file"]["name"]; move_uploaded_file($_FILES["file"]["tmp_name"], $filename); echo "save in:" . $sandbox . "/" . $filename; } } else { echo "Not Allow!"; } } function Check() { $BlackExts = array("php"); $ext = explode(".", $_FILES["file"]["name"]); $exts = trim(end($ext)); $file_content = file_get_contents($_FILES["file"]["tmp_name"]); if ( !preg_match('/[a-z0-9;~^`&|]/is', $file_content) && !in_array($exts, $BlackExts) && !preg_match('/\.\./', $_FILES["file"]["name"]) ) { return true; } return false; } ?> <html> <head> <meta charset="utf-8"> <title>upload</title> </head> <body> <form action="index.php" method="post" enctype="multipart/form-data"> <input type="file" name="file" id="file"><br> <input type="submit" name="submit" value="submit"> </form> </body> </html>其实phpjiami加密之后的整个脚本就是解密我们文件的脚本,我们的文件内容被加密之后放在了 ?> 最后面 图片 整个解密过程也比较简单,其中$v51是我们加密之后内容,$v55是解密后的内容。 $v55 = str_rot13(@gzuncompress(func2(substr($v51,-974,$v55))));其中func2是解密函数 图片 最后是拿func2解密之后的代码放在这个 eval 中执行 图片 还有一种比较简单快捷的方法是通过 hook eval 去获取 eval 的参数,因为不涉及 PHP-Parser 所以就不过多展开了。 Enphp混淆 官网:http://enphp.djunny.com/ github: https://github.com/djunny/enphp 使用官方的加密例子: <?php include './func_v2.php'; $options = array( //混淆方法名 1=字母混淆 2=乱码混淆 'ob_function' => 2, //混淆函数产生变量最大长度 'ob_function_length' => 3, //混淆函数调用 1=混淆 0=不混淆 或者 array('eval', 'strpos') 为混淆指定方法 'ob_call' => 1, //随机插入乱码 'insert_mess' => 0, //混淆函数调用变量产生模式 1=字母混淆 2=乱码混淆 'encode_call' => 2, //混淆class 'ob_class' => 0, //混淆变量 方法参数 1=字母混淆 2=乱码混淆 'encode_var' => 2, //混淆变量最大长度 'encode_var_length' => 5, //混淆字符串常量 1=字母混淆 2=乱码混淆 'encode_str' => 2, //混淆字符串常量变量最大长度 'encode_str_length' => 3, // 混淆html 1=混淆 0=不混淆 'encode_html' => 2, // 混淆数字 1=混淆为0x00a 0=不混淆 'encode_number' => 1, // 混淆的字符串 以 gzencode 形式压缩 1=压缩 0=不压缩 'encode_gz' => 0, // 加换行(增加可阅读性) 'new_line' => 1, // 移除注释 1=移除 0=保留 'remove_comment' => 1, // debug 'debug' => 1, // 重复加密次数,加密次数越多反编译可能性越小,但性能会成倍降低 'deep' => 1, // PHP 版本 'php' => 7, ); $file = 'test.php'; $target_file = 'en_test.php'; enphp_file($file, $target_file, $options);加密之后大概长这样子 图片 可以看到,我们的大部分字符串、函数等等都被替换成了类似于 $GLOBALS{乱码}[num] 这种形式,我们将其输出看一下: 图片 可以看到我们原本的脚本中的字符串都在此数组里面,所以我们只要将$GLOBALS{乱码}[num]还原成原来对应的字符串即可。 那么我们如何获取 $GLOBALS{乱码} 数组的内容,很简单,在我们获取AST节点处打断点即可找到相关内容: 图片 $split = $ast[2]->expr->expr->args[0]->value->value; $all = $ast[2]->expr->expr->args[1]->value->value; $str = explode($split, $all); var_dump($str);可以看到,和上面输出的是一样的(如果加密等级不一样则还需要加一层 gzinflate ) 图片 然后就是通过AST一个节点一个节点将其替换即可,如果不知道节点类型的同学可以用 $GLOBALS[A][1] ,将其输出出来看一下即可,然后根据节点的类型和数据进行判断即可,如下: class PhpParser\Node\Expr\ArrayDimFetch#1104 (3) { public $var => class PhpParser\Node\Expr\ArrayDimFetch#1102 (3) { public $var => class PhpParser\Node\Expr\Variable#1099 (2) { public $name => string(7) "GLOBALS" protected $attributes => array(2) { ... } } public $dim => class PhpParser\Node\Expr\ConstFetch#1101 (2) { public $name => class PhpParser\Node\Name#1100 (2) { ... } protected $attributes => array(2) { ... } } protected $attributes => array(2) { 'startLine' => int(2) 'endLine' => int(2) } } public $dim => class PhpParser\Node\Scalar\LNumber#1103 (2) { public $value => int(1) protected $attributes => array(3) { 'startLine' => int(2) 'endLine' => int(2) 'kind' => int(10) } } protected $attributes => array(2) { 'startLine' => int(2) 'endLine' => int(2) } }根据上面的节点编写脚本 public function leaveNode(Node $node) { if ( $node instanceof PhpParser\Node\Expr\ArrayDimFetch && $node->var instanceof PhpParser\Node\Expr\ArrayDimFetch && $node->var->var instanceof PhpParser\Node\Expr\Variable && $node->var->var->name === "GLOBALS" && $node->var->dim instanceof PhpParser\Node\Expr\ConstFetch && $node->var->dim->name instanceof PhpParser\Node\Name && $node->var->dim->name->parts[0] === $this->str && $node->dim instanceof PhpParser\Node\Scalar\LNumber ) { return new PhpParser\Node\Scalar\String_($this->str_arr[$node->dim->value]); } return null; }解出来的内容如下,可以看到大部分已经成功解密出来了 图片 还有就是解密的一部分出现这样语句:('highlight_file')(__FILE__); ,很明显不符合我们平时的写法,将其节点重命名一下 if ( ($node instanceof Node\Expr\FuncCall || $node instanceof Node\Expr\StaticCall || $node instanceof Node\Expr\MethodCall) && $node->name instanceof Node\Scalar\String_ ) { $node->name = new Node\Name($node->name->value); }现在看起来就舒服多了 图片 我们分析剩下乱码的部分 图片 可以看到是函数里面的局部变量还是乱码,从第一句可以看出所有的局部变量都是以 & $GLOBALS[乱码] 为基础的,而 & $GLOBALS[乱码] 是我们上面已经找出来的,所以也是将其替换即可。 if ( $node instanceof \PhpParser\Node\Stmt\Expression && $node->expr instanceof \PhpParser\Node\Expr\AssignRef && $node->expr->var instanceof \PhpParser\Node\Expr\Variable && $node->expr->expr instanceof \PhpParser\Node\Expr\ArrayDimFetch && $node->expr->expr->var instanceof \PhpParser\Node\Expr\Variable && $node->expr->expr->var->name === "GLOBALS" && $node->expr->expr->dim instanceof \PhpParser\Node\Expr\ConstFetch && $node->expr->expr->dim->name instanceof \PhpParser\Node\Name && $node->expr->expr->dim->name->parts != [] ) { $this->Localvar = $node->expr->var->name; return NodeTraverser::REMOVE_NODE; } else if ( $node instanceof \PhpParser\Node\Expr\ArrayDimFetch && $node->var instanceof \PhpParser\Node\Expr\Variable && $node->var->name === $this->Localvar && $node->dim instanceof \PhpParser\Node\Scalar\LNumber ) { return new \PhpParser\Node\Scalar\String_($this->str_arr[$node->dim->value]); }替换之后,可以看到与 & $GLOBALS[乱码] 有关的已经全部被替换了,只有变量部分是乱码了 替换变量为 $v 这种形式 <?php function BeautifyVariables($code) { $v = 0; $map = []; $tokens = token_get_all($code); foreach ($tokens as $token) { if ($token[0] === T_VARIABLE) { if (!isset($map[$token[1]])) { if (!preg_match('/^\$[a-zA-Z0-9_]+$/', $token[1])) { $code = str_replace($token[1], '$v' . $v++, $code); $map[$token[1]] = $v; } } } } return $code; }至此所有代码全部被还原(除了变量名这种不可抗拒因素之外) 图片 还有一部分是没有用的全局变量和常量,手动或者根据AST去进行删除即可,下面贴一下完整解密脚本 <?php require "./vendor/autoload.php"; use \PhpParser\Error; use \PhpParser\ParserFactory; use \PhpParser\NodeTraverser; use \PhpParser\NodeVisitorAbstract; use \PhpParser\Node; use \PhpParser\PrettyPrinter\Standard; class MyVisitor extends NodeVisitorAbstract { public $str; public $str_arr; public $Localvar; public function leaveNode(Node $node) { if ( $node instanceof \PhpParser\Node\Expr\ArrayDimFetch && $node->var instanceof \PhpParser\Node\Expr\ArrayDimFetch && $node->var->var instanceof \PhpParser\Node\Expr\Variable && $node->var->var->name === "GLOBALS" && $node->var->dim instanceof \PhpParser\Node\Expr\ConstFetch && $node->var->dim->name instanceof \PhpParser\Node\Name && $node->var->dim->name->parts[0] === $this->str && $node->dim instanceof \PhpParser\Node\Scalar\LNumber ) { return new \PhpParser\Node\Scalar\String_($this->str_arr[$node->dim->value]); } if (($node instanceof Node\Expr\FuncCall || $node instanceof Node\Expr\StaticCall || $node instanceof Node\Expr\MethodCall) && $node->name instanceof Node\Scalar\String_ ) { $node->name = new Node\Name($node->name->value); } if ( $node instanceof \PhpParser\Node\Stmt\Expression && $node->expr instanceof \PhpParser\Node\Expr\AssignRef && $node->expr->var instanceof \PhpParser\Node\Expr\Variable && $node->expr->expr instanceof \PhpParser\Node\Expr\ArrayDimFetch && $node->expr->expr->var instanceof \PhpParser\Node\Expr\Variable && $node->expr->expr->var->name === "GLOBALS" && $node->expr->expr->dim instanceof \PhpParser\Node\Expr\ConstFetch && $node->expr->expr->dim->name instanceof \PhpParser\Node\Name && $node->expr->expr->dim->name->parts != [] ) { $this->Localvar = $node->expr->var->name; return NodeTraverser::REMOVE_NODE; } else if ( $node instanceof \PhpParser\Node\Expr\ArrayDimFetch && $node->var instanceof \PhpParser\Node\Expr\Variable && $node->var->name === $this->Localvar && $node->dim instanceof \PhpParser\Node\Scalar\LNumber ) { return new \PhpParser\Node\Scalar\String_($this->str_arr[$node->dim->value]); } return null; } } function BeautifyVariables($code) { $v = 0; $map = []; $tokens = token_get_all($code); foreach ($tokens as $token) { if ($token[0] === T_VARIABLE) { if (!isset($map[$token[1]])) { if (!preg_match('/^\$[a-zA-Z0-9_]+$/', $token[1])) { $code = str_replace($token[1], '$v' . $v++, $code); $map[$token[1]] = $v; } } } } return $code; } $code = file_get_contents("./en_test.php"); $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); try { $ast = $parser->parse($code); } catch (Error $error) { echo "Parse error: {$error->getMessage()}\n"; return; } var_dump($ast); $split = $ast[2]->expr->expr->args[0]->value->value; $all = $ast[2]->expr->expr->args[1]->value->value; $str1 = $ast[2]->expr->var->dim->name->parts[0]; $str_arr = explode($split, $all); $visitor = new MyVisitor; $visitor->str = $str1; $visitor->str_arr = $str_arr; $traverser = new NodeTraverser; $traverser->addVisitor($visitor); $stmts = $traverser->traverse($ast); $prettyPrinter = new Standard; $code = $prettyPrinter->prettyPrintFile($stmts); $code = BeautifyVariables($code); echo $code;注:需要注意的是 enphp 使用的全局变量不一定是 GLOBALS ,也可能是 _SERVER、_GET 等等,根据具体情况进行判断,还有就是加密等级不同对应的解密方式也是不同的,不过其中的思想都是大同小异 参考 https://www.52pojie.cn/thread-693641-1-1.html https://www.52pojie.cn/thread-883976-1-1.html https://xz.aliyun.com/t/7363 https://github.com/nikic/PHP-Parser/blob/master/doc/2_Usage_of_basic_components.markdown
-
关于 PHP 初级闭包(Closure) 匿名函数 提到闭包就不得不想起匿名函数,也叫闭包函数(closures),貌似 PHP 闭包实现主要就是靠它。声明一个匿名函数是这样: $func = function() { }; //带结束符可以看到,匿名函数因为没有名字,如果要使用它,需要将其返回给一个变量。匿名函数也像普通函数一样可以声明参数,调用方法也相同: $func = function($param) { echo $param; }; $func('易航博客'); //输出:易航博客顺便提一下,PHP 在引入闭包之前,也有一个可以创建匿名函数的函数: create function ,但是代码逻辑只能写成字符串,这样看起来很晦涩并且不好维护,所以很少有人用。 实现闭包 将匿名函数在普通函数中当做参数传入,也可以被返回。这就实现了一个简单的闭包。 下边有三个例子 例一:在函数里定义一个匿名函数,并且调用它 function printStr() { $func = function ($str) { echo $str; }; $func('易航博客'); } printStr();例二:在函数中把匿名函数返回,并且调用它 function getPrintStrFunc() { $func = function ($str) { echo $str; }; return $func; } $printStrFunc = getPrintStrFunc(); $printStrFunc('易航博客');例三:把匿名函数当做参数传递,并且调用它 function callFunc($func) { $func('易航博客'); } // 也可以直接将匿名函数进行传递。如果你了解js,这种写法可能会很熟悉 callFunc(function( $str ) { echo $str; });连接闭包和外界变量的关键字:USE 闭包可以保存所在代码块上下文的一些变量和值。PHP 在默认情况下,匿名函数不能调用所在代码块的上下文变量,而需要通过使用 use 关键字。 换一个例子看看: function getMoney() { $rmb = 1; $dollar = 6; $func = function () use ($rmb) { echo $rmb; echo $dollar; }; $func(); } getMoney(); //输出: //1 //报错,找不到dorllar变量可以看到,dollar 没有在 use 关键字中声明,在这个匿名函数里也就不能获取到它,所以开发中要注意这个问题。 有人可能会想到,是否可以在匿名函数中改变上下文的变量,但我发现是不可以的: function getMoney() { $rmb = 1; $func = function () use ($rmb) { echo $rmb; //把$rmb的值加1 $rmb++; }; $func(); echo $rmb; } getMoney(); // 输出: // 1 // 1所以 use 所引用的也只不过是变量的一个副本而已。但是我想要完全引用变量,而不是复制。 要达到这种效果,其实在变量前加上 & 符号就可以了: function getMoney() { $rmb = 1; $func = function () use (&$rmb) { echo $rmb; //把$rmb的值加1 $rmb++; }; $func(); echo $rmb; } getMoney(); // 输出: // 1 // 2这样匿名函数就可以引用上下文的变量了。如果将匿名函数返回给外界,匿名函数会保存 use 所引用的变量,而外界则不能得到这些变量,这样形成‘闭包’这个概念可能会更清晰一些。 根据描述改变一下上面的例子: function getMoneyFunc() { $rmb = 1; $func = function () use (&$rmb) { echo $rmb; //把$rmb的值加1 $rmb++; }; return $func; } $getMoney = getMoneyFunc(); $getMoney(); $getMoney(); $getMoney(); // 输出: // 1 // 2 // 3总结 PHP 闭包的特性并没有太大惊喜,其实用 class 就可以实现类似甚至强大得多的功能,更不能和 js 的闭包相提并论,只能期待 PHP 以后对闭包支持的改进。不过匿名函数还是挺有用的,比如在使用 preg_replace_callback 等之类的函数可以不用在外部声明回调函数了。 图片
-
浅聊一下XSS和CSRF攻击以及防御方法 XSS攻击 图片 XSS,即为(CrossSiteScripting),中文名为跨站脚本,是发生在目标用户的浏览器层面上的,当渲染 DOM 树的过程成发生了不在预期内执行的 JS 代码时,就发生了 XSS 攻击,大多数 XSS 攻击的主要方式是嵌入一段远程或者第三方域上的 JS 代码。实际上是在目标网站的作用域下执行了这段JS代码。 XSS防御 XSS 防御的总体思路是:对输入(和URL参数)进行过滤,对输出进行编码。也就是对提交的所有内容进行过滤,对 url 中的参数进行过滤,过滤掉会导致脚本执行的相关内容 然后对动态输出到页面的内容进行html编码,使脚本无法在浏览器中执行。虽然对输入过滤可以被绕过,但是也还是会拦截很大一部分的XSS攻击。 CSRF攻击 CSRF(CrossSiteRequest Forgery,跨站请求伪造),字面理解意思就是在别的站点伪造了一个请求。专业术语来说就是在受害者访问一个网站时,其 Cookie 还没有过期的情况下,攻击者伪造一个链接地址发送受害者并欺骗让其点击,从而形成 CSRF 攻击。 CSRF防御 防御 CSRF 攻击主要有三种策略:验证HTTP Referer 字段;在请求地址中添加 Token 并验证;在 HTTP 头中自定义属性并验证。
-
被“嫌弃”的PHP:未来仍光明 即使面临各种新技术的挑战,PHP 的未来仍然光明。多年来,在 Web 开发社区内部形成了一种厌恶 PHP 的气氛。现如今,厌恶 PHP 和赞美新技术(如 Node)几乎成了一种奇想。特别是在年轻的社区,PHP 被认为是一只“恐龙”。 PHP 是一门伟大的编程语言。 它并不完美,有利有弊,但总的来说,如果你从事 Web 开发工作,你最好试着去理解它,而不是跟风去厌恶它…… 你甚至可以从中学到一些东西! 图片 我们来看看 PHP 和 Node 之间的区别,这些区别让很多人留在了 PHP 平台,也让其他人转向了不同的编程语言! 社区 PHP 社区比 Node 社区大。 NPM/Node 社区也很大,但缺乏能够真正维护好和做好 Node 包的人。 很多模块被弃用或不再更新。最糟糕的是,大多数模块依赖了其他大量的模块,这意味着如果你使用的模块依赖了一个包含漏洞的旧模块,你可能不知情,或者需要花很多时间自己去更新所有的东西。 这很重要,因为不管一门编程语言或一个框架有多好,如果没有人维护,或者如果没有关于它的讨论、PR或者开源项目,它最终就会消亡。 PHP 生态系统为你提供了大量的库:JWT 身份验证、生成 Excel 电子表格和 PDF、缓存管理、ORM 框架……这些库被广泛使用,具有很好的安全性,且提供了良好的文档。 Symfony 框架也提供了一些官方文档,比如 LexikJWTAuthentication! 事实上,大约 80% 的 Web 应用使用 PHP 开发。 框架 Symfony 和 Laravel 这两个主要的 PHP 框架现在是 Web 的一个巨大组成部分。Laravel 在美国很受欢迎,Symfony 在欧洲很受欢迎,如果我们把 WordPress 去掉,这两个框架占了 PHP 生态系统的 90% 以上。 这些框架比大多数 Node 的框架都要老,并且比现在的 Node 框架拥有更广泛的包和文档生态系统。 在使用 Symfony 过程中遇到了问题?版本 3?版本 4?这都不是事!大量的 StackOverflow 帖子、Medium 文章、官方文档可供你参考。 Symfony 和 Laravel 也提供了一些“基本的项目结构”,你当然可以不用它,你可以按照你想要的方式构建你的项目,但这些基本模式通常适用于多种类型的应用。 在大多数情况下,我们可以通过配置对它们进行调整,以满足我们的各种需求。由于这些框架已经推出了好几年,你可能想到的大多数有用的特性都已经有了,因为在你之前的那些开发人员也有与你同样的需求。 我们以 Express 为例,它是 Node 最著名的框架,主要用于编写 API,它并没有提供强制的结构。这意味着一个没有经验的开发人员更有可能构建出一些不符合标准的东西,而 PHP 框架在这方面的风险要小得多。 性能 Node 的速度很快,在某些情况下比 PHP 更快,但 PHP 也不是太糟糕。 PHP 8.1 借助 OPCache 和 JIT 编译获得更快的执行速度。 Node 在速度方面利用了它的异步特性,但它是单线程的。PHP 利用了在多个线程上运行的优势,而且是同步的。 事实上,现在服务器的价格一般都不会很高,伸缩一个 Web 应用从未像今天这样容易。对于小型 Web 应用来说,鉴于如今的计算能力,性能不再是一个值得花太多时间去争论的点。 然而,对于大规模的应用程序,价格可能是一个关注点。 这就是为什么把常用的 PHP-FPM/Nginx 栈换成 Swoole 会是一个不错的选择。 我曾经看到过一些 PHP 应用程序将 Swoole 作为底层的 HTTP 服务器,在性能上击败了 Node! 此外,使用消息队列是平衡应用程序工作负载的一个很好的方法,这可以很容易地使用 PHP 和 Node 来实现。 易用性 虽然 Node/Express 经常被用来编写 API,并与使用 React/Angular/Vue 等框架构建的前端通信,但大多数 PHP 框架都采用了 MVC 模式。 MVC 即模型视图控制器。一张图片胜过长篇大论:如果你不了解 MVC,这里有一张图可以帮你快速理解 MVC 模式。 图片 构建一个前后端分离的应用程序通常比使用支持前后端技术栈的框架要慢。事实上,许多后端开发人员知道如何编写出色的 HTML/CSS,但不熟悉 React 或其他框架的概念或语法。 结论 PHP 和 Node 各有优点和缺点。 如果你需要稳定性、可靠性和长期支持,我建议使用 PHP。 这些框架成熟且安全,在我看来是首选。 不过,对于需要高吞吐量和实时数据处理的 API 来说,Node 是一个不错的选择。另外,有一些项目不能用 PHP 来完成,比如 Discord 机器人(尽管可能可以用 PHP 来实现,但已经有一个官方的 JavaScript 库……) 有时候,用另一种编程语言来开发应用程序也是不错的,我们可以从中发现一些新的概念或做事的方式,然后将它们应用到其他编程语言中。
-
PHP 8.2 将在今年内发布,一起来看看都有什么新特征 PHP 8.2 预计将于今年 11 月发布,最新的稳定版本是 PHP 8.1.5。虽然现在还为时过早,但对更新的接受程度参差不齐。 但是,知道会发生什么可以帮助您为最新的 PHP 版本做好准备。通过了解新功能和不推荐使用的功能,您可以了解更新可能如何影响开发。这些知识还可以帮助您为最终发布做好准备。 在这篇文章中,我们将回顾最新的 PHP 版本。然后我们将介绍 PHP 8.2 中的新功能并讨论发布时间表。 图片 PHP 版本概述 PHP 7.4 引入了类型化属性、下划线数字分隔符和各种改进。从那时起,已经发布了更多的 PHP 迭代。 2020 年 11 月发布的 PHP 8.0 带来了一些基本功能。除了语法和性能增强之外,该版本还包括: 命名参数 匹配语法 Union 类型 Constructor Property Promotion JIT(影响 PHP 执行源代码的方式) 一年后出现了 PHP 8.1,这是最新的主要 PHP 版本。此更新包括重要功能,例如: Intersection 类型 只读属性 Enums Fibers 从不返回类型 掌握最新版本的 PHP 有助于提高网站的性能和安全性。但是,重要的是要知道在升级之前会发生哪些变化。如果您有兴趣测试 PHP 8.2 的当前状态,可以通过 GitHub 进行。 PHP 8.2 中的新功能 PHP 8.2 预计将于 2022 年底发布。这是当前的发布时间表,计划于 2022 年 11 月 24 日发布通用版本 (GA): 19be6b 19be6b 19be6b 19be6b 19be6b 19be6b 19be6b 19be6b 19be6b 19be6b 19be6b 19be6b 19be6b 19be6b 根据 PHP 网站上的官方文档,应该有一些新特性和不推荐使用的功能。 新的 memory_reset_peak_usage 函数 PHP 8.2 将包含一个名为 memory_reset_peak_usage 的新函数。它将重置 memory_get_peak_usage 函数返回的峰值内存使用量。 对于涉及多次调用操作并记录每次迭代的峰值内存使用情况的情况,此功能将很有帮助。开发人员将能够使用此新功能在请求的生命周期内的任何给定时间重置峰值内存使用量。 只读类 在 PHP 8.1 中引入,只读属性将在 PHP 8.2 中扩展以添加语法糖,以便所有类属性一次都是只读的: readonly class Post { public function __construct( public string $title, public Author $author, public string $body, public DateTime $publishedAt, ) { } }这将防止将动态属性添加到类中。 Null 和 False 独立类型 在 PHP 8.2 中,false 的返回类型将作为独立类型使用,而不是严格的联合类型,用于发生错误时,这已经是可能的: function alwaysFalse(): false { return false; }null 类型也是如此。例如,作为独立类型,与以前不同,NullPost::getAuthor() 将只能返回 null 。 弃用动态属性 动态属性将在 PHP 8.2 中被弃用,导致 PHP 9.0 出现 ErrorException。这些属性是在对象上设置的: class Post { public string $title; } // … $post->name = 'Name';动态属性允许在没有严格的类声明的情况下创建类(例如,值对象)时具有灵活性。对于依赖动态属性的开发人员来说,他们的弃用可能会成为一个问题,因为这会促使他们更多地进行静态分析。出于这个原因,一些开发人员对 PHP 8.2 的这种变化感到担忧。 但是,使用 __get 和 __set 的类仍将支持动态属性,stdClass 的对象也将如此。 或者,开发人员可以在这些属性的类上使用在全局命名空间中声明的新 #[AllowDynamicProperties]attribute: # [AllowDynamicProperties] class User{} $user = new User(); $user->foo = 'bar';虽然不建议这样做,但另一种选择是禁用弃用警告。 新的 /n 修饰符 PHP 8.2 将包含对 preg_* 函数系列的 /n (no capture) modifier 的支持。使用时,除了已命名的捕获组之外,任何具有()meta-characters 的组都不会捕获。 本质上,结果与将每个组标记为非捕获相同。 此更改背后的原因是修饰符简化了多个组的复杂正则表达式。开发人员可以将所有组标记为非捕获,而不是将每个组都营销为非捕获。 然后,他们可以选择并命名需要捕获的特定组。 在回溯中编辑参数 许多开发人员使用从代码库跟踪堆栈跟踪和生产错误的服务。这些服务可以在出现问题时通知用户。例如,在调试应用程序时跟踪调用堆栈很有帮助。 但是,有时堆栈跟踪可能包含敏感信息,例如用户名和密码。PHP 8.2 将包含一个 #[SensitiveParameter] 属性,当出现问题时,该属性将防止此信息包含在堆栈跟踪中: function test($foo, #[\SensitiveParameter] $bar, $baz) { throw new Exception('Error'); } test('foo', 'bar', 'baz');PHP 8.2 将使用敏感参数从堆栈跟踪中编辑私有信息,使其更加安全。这些参数确保数据不会出现在错误日志中。请注意,此属性仅可用于参数。 弃用 ${} 字符串插值 有多种方法可以使用 PHP 在字符串中嵌入变量。但是,PHP 8.2 将弃用两种方法。第一个是在字符串中使用 ${}: $str = "Hello ${world}";第二个是使用 ${}(变量) : $str = "Hello ${(world)}";这对开发人员来说可能不是一个重大问题,因为两种最流行的字符串插值方法仍然有效。 弃用部分支持的可调用对象 另一个不推荐使用的更改是部分支持的 callables。有多种方法可以在 PHP 中创建可调用对象。可以使用 $callable() 语法、user_call_func(array) 或使用带有回调的函数调用带或不带参数的函数。 已弃用的可调用模式包括以下内容: $callable = "self::method"; $callable = "parent::method"; $callable = "static::method"; $callable = ["self", "method"]; $callable = ["parent", "method"]; $callable = ["static", "method"]; $callable = ["MyClass", "MyParentClass::myMethod"]; $callable = [new MyClass(), "MyOtherClass::myMethod"]从 PHP 8.2 开始,调用上述任何一个都将导致以下弃用通知: class Test { public static function myMethod(): void { echo "Called"; } public static function call(): void { $callable = 'self::myMethod'; call_user_func($callable); } } $callable = Test::call(); // "Called"但是,将这些可调用对象传递给 is_callable 函数或将它们与可调用参数类型一起使用不会生成弃用消息。为了防止出现弃用通知,开发人员可以使用::class 魔术方法将可调用代码中的 parent、self 和 static 关键字转换为各自的类名。 更改背后的部分原因是允许将可调用对象用于类型化属性。它使它们不那么依赖于上下文。 MySQLi 不能再用 libmysql 编译 过去,PHP 支持两个库来连接 MySQL 数据库:mysqlnd 和 libmysql。自 PHP 5.4 起,前者已成为默认库。但是,可以通过扩展编译 MySQLi。 从 PHP 8.2 开始,将不支持使用 libmysql 编译 MySQLi 扩展。尝试这样做会导致配置错误: ./configure --with-mysqli=FOO不再支持将 mysqli 与外部库链接 这不太可能对开发人员造成任何重大错误。但是,通过 LDAP 和 SASL 自动重新连接和身份验证支持 libmysql 支持的两个 mysqlnd 不可用的最大功能。
-
PHP 过滤 XSS 攻击插件源码实例 Xss 攻击是最经常遇到的攻击了,其中原理大家应该都懂了,我就不再这里做更多的详解了。 今天给大家分享的实例源码,直接可用的那种。虽然现在很多框架都封装了这种,但是作为 PHP 开发者的你,XSS 攻击原理与防止还是要懂得的。 图片 文档说明: 1.将 waf.php 传到要包含的文件的目录 2.在页面中加入防护,有两种做法,根据情况二选一即可: a 在所需要防护的页面加入代码: require_once('waf.php');就可以做到页面防注入、跨站。如果想整站防注,就在网站的一个公用文件中,如数据库链接文件 config.inc.php 中添加 require_once('waf.php'); 来调用本代码 b 在每个文件最前加上代码 在 php.ini 中找到: Automatically add files before or after any PHP document. auto_prepend_file = waf.php路径;PHP 文件 waf.php 隐藏内容,请前往内页查看详情