易航 发布的文章 - 易航博客
首页
统计
友链
4K壁纸
留言板
关于
推荐
百度一下: 易航博客
搜 索
1
Joe主题再续前缘版 - 本站同款
5,822 阅读
2
易航网址引导系统
3,100 阅读
3
Js自动播放HTML音乐(不受浏览器限制,无需先与浏览器交互,无需对浏览器进行修改)
1,997 阅读
4
六个好看实用的HTML登录界面源码
1,774 阅读
5
V免签全开源免签约码支付系统(支持:支付宝 微信 QQ)
716 阅读
源码大全
PHP源码
HTML模板
Java源码
小程序源码
热门程序
Typecho
ThinkPHP
Joe主题
技术教程
HTML教程
PHP教程
CSS教程
JS教程
MySQL教程
SEO教程
正则表达式教程
网络技术
话题讨论
PHP话题
书虫小说
剑来
雪中悍刀行
值得一看
媒体新闻
人生语录
热门事件
活动线报
经验分享
关于我们
站点公告
其他文章
登录
/
注册
搜 索
标签搜索
网站源码
PHP
PHP教程
PHP源码
PHP函数方法
JavaScript
HTML模板
HTML
Joe主题
网站模板
Typecho
ThinkPHP
PHP网络请求
支付系统
通信协议
码支付
API接口
Typecho插件
PHP发送数据
网络攻击
易航
累计撰写
90
篇文章
累计收到
924
条评论
首页
栏目
源码大全
PHP源码
HTML模板
Java源码
小程序源码
热门程序
Typecho
ThinkPHP
Joe主题
技术教程
HTML教程
PHP教程
CSS教程
JS教程
MySQL教程
SEO教程
正则表达式教程
网络技术
话题讨论
PHP话题
书虫小说
剑来
雪中悍刀行
值得一看
媒体新闻
人生语录
热门事件
活动线报
经验分享
关于我们
站点公告
其他文章
页面
统计
友链
4K壁纸
留言板
关于
推荐
百度一下: 易航博客
用户登录
登录
注册
找到
90
篇与
易航
相关的结果
2023-01-05
网易云官方歌曲解析接口
单曲音频文件解析接口:http://music.163.com/song/media/outer/url?id=歌曲的ID
2023年01月05日
434 阅读
2 评论
1 点赞
2022-12-19
iFrame父子相互获取对方dom元素
父页面获取子页面dom的方法/* 必须用onload */ window.onload = () => { const sonWindow = document.querySelector("iframe").contentWindow; const sonDiv = sonWindow.document.querySelector(".son") console.log(sonDiv); }子页面获取父页面dom的方法/* 必须用onload */ window.onload = () => { const parentWindow = window.parent; const parentDiv = parentWindow.document.querySelector(".parent") console.log(parentDiv); } location.href='http://bri6.cn/archives/69.html'
2022年12月19日
67 阅读
1 评论
0 点赞
2022-12-13
ThinkPHP中日期时间区间查询以及whereTime用法
一、使用where方法进行时间的比较查询where('create_time', '> time', '2021-8-8'); // 大于某个时间 where('create_time', '<= time', '2020-8-8'); // 小于某个时间 where('create_time', 'between time', ['2020-1-1', '2020-10-1']); // 时间区间查询二、使用whereTime方法whereTime('birthday', '>=', '1970-10-1')->select(); // 大于某个时间 whereTime('birthday', '<', '2000-10-1')->select(); // 小于某个时间 whereTime('birthday', 'between', ['1970-10-1', '2000-10-1'])->select(); // 时间区间查询 whereTime('birthday', 'not between', ['1970-10-1', '2000-10-1'])->select(); // 不在某个时间区间三、时间表达式// 获取今天的文章 Db::table('think_news')->whereTime('create_time', 'today')->select(); // 获取昨天的文章 Db::table('think_news')->whereTime('create_time', 'yesterday')->select(); // 获取本周的文章 Db::table('think_news')->whereTime('create_time', 'week')->select(); // 获取上周的文章 Db::table('think_news')->whereTime('create_time', 'last week')->select(); // 获取本月的文章 Db::table('think_news')->whereTime('create_time', 'month')->select(); // 获取上月的文章 Db::table('think_news')->whereTime('create_time', 'last month')->select(); // 获取今年的文章 Db::table('think_news')->whereTime('create_time', 'year')->select(); // 获取去年的文章 Db::table('think_news')->whereTime('create_time', 'last year')->select();四、如果查询当天、本周、本月和今年的时间,还可以简化为:// 获取今天的文章 Db::table('think_news')->whereTime('create_time', 'd')->select(); // 获取本周的文章 Db::table('think_news')->whereTime('create_time', 'w')->select(); // 获取本月的文章 Db::table('think_news')->whereTime('create_time', 'm')->select(); // 获取今年的文章 Db::table('think_news')->whereTime('create_time', 'y')->select();五、时间范围查询// 查询两个小时内的文章 Db::table('think_news')->whereTime('create_time', '-2 hours')->select(); // 查询两天内的文章 Db::table('think_news')->whereTime('create_time', '-2 days')->select(); location.href='http://bri6.cn/archives/68.html'
2022年12月13日
380 阅读
0 评论
2 点赞
2022-12-12
Typecho一键调整网站为冬天情景插件
效果图获取插件隐藏内容,请前往内页查看详情安装教程将下载下来的插件压缩包上传至 Typecho站点根目录/usr/plugins 目录解压即可
2022年12月12日
630 阅读
88 评论
3 点赞
2022-12-11
网站添加雪花飘落特效
<script src="https://cdn.jsdelivr.net/gh/xh8039/static-assets/public/js/snow-falling.min.js"></script> location.href='http://bri6.cn/archives/144.html'
2022年12月11日
211 阅读
0 评论
1 点赞
2022-12-09
网站添加Loading加载动画
Loading动画作用网站添加Loading动画可有效防止部分浏览器打开网页后因为有些静态资源还没有加载完毕导致的闪屏、白屏、花屏、错误排版等BUG效果展示![Loading动画]()Loading动画代码HTML是从上至下解析,所以添加到哪里合适应该知道了吧 location.href='http://bri6.cn/archives/143.html'
2022年12月09日
279 阅读
5 评论
10 点赞
2022-11-21
给你的网站增加一款简洁而功能强大的音乐播放器
H5播放器介绍APlayer 是一个简洁漂亮、功能强大的 Html5 音乐播放器MetingJS 是为 APlayer 添加网易云、QQ音乐、酷狗音乐等支持的插件安装教程安装很简单,一共需要调用三个文件:APlayer.min.js APlayer.min.css Meting.min.js你可以使用 CDN 调用,只需要在 <head> 里面插入:<link href="https://cdn.bootcdn.net/ajax/libs/aplayer/1.10.1/APlayer.min.css" rel="stylesheet"> <script src="https://cdn.bootcdn.net/ajax/libs/aplayer/1.10.1/APlayer.min.js"></script>在 footer 里面插入:<script src="https://cdn.bootcdn.net/ajax/libs/meting/2.0.1/Meting.min.js"></script>当然,你可以将这些文件托管在自己的服务器,把上面的调用链接改成自己的就行了使用方法APlayer 原生用法先看一个最简单的例子:<div id="aplayer"></div> <script type="text/javascript"> const ap = new APlayer({ container: document.getElementById('aplayer'), audio: [{ name: '你从未离去', artist: '白挺', url: 'https://doge.ottoli.cn/你从未离去.mp3', cover: 'https://doge.ottoli.cn/你从未离去.jpg' }] }); </script>在js 代码中:参数 container 值为 document.getElementById('aplayer') 意思是定义当前播放器容器 id 为 aplayer参数 audio 中有 4 个子参数,定义关于音频的相关参数:参数 name 定义音频名称参数 artist 定义艺术家名参数 url 指向音频文件的地址参数 cover 指向音频封面的地址然后,在需要使用播放器的地方,将容器 <div> 的 id 设置为参数 container 中设定的值即可MetingJS 的用法前面已经看到,APlayer 原生用法设置参数十分繁琐,而且只能调用音频文件直链,增加服务器开销。而使用 MetingJS 就很好地解决了这个问题先看一个最简单的例子: 对应的代码为:<meting-js server="netease" type="song" id="31365604" > </meting-js>一个 MetingJS 播放器至少需要三个参数:server 指定调用的 API ,可选 netease, tencent, kugou, xiami, baidu ,分别对应网易云音乐、QQ音乐、酷狗音乐、虾米音乐、百度音乐type 指定调用类型,可选 song, playlist, album, search, artist ,分别对应单曲、歌单、专辑、搜索结果、艺术家id 指定调用的 id ,一般可以在地址栏中找到当 type 选择的是个播放列表时,生成的播放器是这样的: 播放列表默认是打开的,你可以使用参数 listFolded="true" 使其默认折叠当你设定 fixed="true" ,会生成一个吸附在页面左下角的播放器,就像我的博客左下角那个当你设定 mini="true" ,会生成一个 mini 播放器: 值得注意的是:除了 mini 模式,MetingJS 生成的播放器默认是带有歌词的(而且关不掉)全部参数说明请查阅 MetingJS 官方文档(其实除了三个必要参数其余都和 APlayer 原生参数一样)
2022年11月21日
464 阅读
1 评论
5 点赞
2022-10-10
易航网址引导系统
项目介绍易航原创一款极其优雅的易航网址引导页管理系统 联系易航QQ反馈BUG:2136118039 项目亮点1、采用光年全新v5模板开发后台2、后台内置8款主题色,分别是简约白、炫光绿、渐变紫、活力橙、少女粉、少女紫、科幻蓝、护眼黑3、可管理无数引导页主题并且主题内可以进行不同的自定义设置,目前内置16套主题 持续增加中...4、可单独开发各种插件,插件可进行自定义设置,目前内置七款实用插件5、无需安装解压部署即可使用6、数据管理包采用易航原创JsonDb数据包7、主题内置神奇的$this语法 可快速开发并保留著作版权8、对前台URL进行伪静态重写 对搜索引擎更加友好9、内置硬防洪和硬防墙插件 告别域名忧虑10、系统全开源 告别后门风险项目演示站后台地址:http://guide.demo.bri6.cn/admin账号admin 密码123456前台地址:http://guide.demo.bri6.cn/项目截图获取源码安装教程PHP版本8.0+首先站点部署伪静态为ThinkPHP后台地址:域名/admin初始账号:admin初始密码:123456历史版本v1.02022-11-09 00:46:36 星期三 初步完成项目建设v1.12022-11-09 21:15:36 星期三 修复移动端后台登录失败问题修复后台表格页面列表无法删除的问题优化后台列表页面编辑和删除两个按钮的鼠标选中提示修复后台标签图标加载失败问题全面优化后台表格页面底层写法后台表格页面新增搜索功能新增后台在线更新程序功能v1.22022-11-10 20:46:36 星期日 修复自动添加站点检测不到对方站点已添加本站的BUG对前台URL进行重写,可以通过.html等后缀访问文件,需要设置伪静态为ThinkPHP新增分类排序功能和链接排序功能新增自定义网站首页背景功能v1.32022-11-15 00:30:12 星期二初步完成插件化开发功能修复default主题输出顶级分类链接无效问题全新优化default主题输出HTML算法修复系统自带404模板部分情况下返回首页定位错误的BUG全新重写后台菜单栏实现算法修复后台页面点击二级菜单栏跳转后的页面二级菜单栏没有打开的BUG全新系统底层代码结构初步完成主题自定义设置功能 主题内置神奇的$this语法 PHP小白也可以开发主题修复链接禁用后前端仍然显示的BUG新增3款引导页模板v1.42022-11-15 18:00:43 星期二新增梦想启航主题,已保留版权新增暗黑流光主题新增拟态百主题新增小枫引导页主题,已保留版权修复程序更新后一直卡在加载界面的BUG修复isIndex方法判断首页错误的BUG链接管理新增可增加链接LOGO 仅部分主题展示新增网站设置处可设置Favicon和Logo,匹配所有主题v1.52022-11-24 18:48:31 星期四优化后台主题管理页面主题列表上下之间的间距新增一款高度自定义化的主题优化系统底层架构初步完成插件自定义设置优化主题设置底层代码新增QQ阻止举报插件,QQ内打开站点点击举报会自动跳转百度新增音乐播放器插件,支持目前各大平台音乐播放v1.62022-11-25 17:06:46 星期五新增一款简约浏览器主页主题新增一款冷黑灯光主题新增一款简约主题修复后台更新个人信息页面账号状态选择栏显示数字的BUG修复自动友链插件出现警告文字的BUG优化后台设置主题的速度修复音乐播放器插件在梦想启航主题下播放列表文字不显示的BUG重写后台主题管理循环输出主题信息算法,优化页面展示情况,点击图片可预览大图优化PC端后台滚动条样式使用主题内置$this语法中的getLink方法将从返回多维数组优化为返回多维对象新增站点可以配置通用客服QQ号v1.72022-11-27 21:47:06 星期日新增一款很好看的微导航主题新增Aimeng主题修复后台网站配置页面编辑网站创建时间时日期选中插件不生效的BUG修复冷黑灯光主题不显示引导网址的BUG修复后台删除数据后不显示成功提示的BUG修复防举报插件部分浏览器打开后直接跳转百度的BUG新增通用主题背景图设置,分别可设置PC端和移动端的主题背景图,主题内置背景图优先新增分类可设置LOGO图标功能,是否展示要看主题是否支持修复后台删除有链接的分类之后链接列表不显示的BUGv1.82022-12-02 17:20:05 星期五新增二次元导航发布页移动端主题新增粉色网址发布页主题优化插件音乐播放器的视图层高度后台菜单栏图标优化为彩色图标后台表格页面编辑和删除按钮优化为彩色优化与服务端的底层连接流程,即使服务端挂了也能正常使用新增后台可自定义是否开启页面Loading动画v1.92022-12-06 18:46:38 星期二后台首页新增统计链接总数、友链总数、主题总数、插件总数优化防举报插件JS代码优化后台from表单生成流程新增简单导航主题后台表格新增排序功能新增站点统计插件优化后台页面导航栏图标样式新增友链LOGO、关键词、简介自动扫描补充功能新增自动友链插件自定义排除站点,方便排除搜索引擎平台优化后台动态生成表单的提交方式新增后台所有表格页面的一键禁用或激活功能2.0内测版正在努力开发中...删库跑路!!!
2022年10月10日
3,100 阅读
34 评论
30 点赞
2022-10-03
凡人修仙传
暂无简介
2022年10月03日
337 阅读
4 评论
15 点赞
2022-09-06
初探PHP-Parser和PHP代码混淆
初探PHP-ParserPHP-Parser 是 nikic 用PHP编写的PHP5.2到PHP7.4解析器,其目的是简化静态代码分析和操作。解析创建一个解析器实例:use PhpParser\ParserFactory; $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);ParserFactory接收以下几个参数:ParserFactory::PREFER_PHP7 :优先解析PHP7,如果PHP7解析失败则将脚本解析成PHP5ParserFactory::PREFER_PHP5 :优先解析PHP5,如果PHP5解析失败则将脚本解析成PHP7ParserFactory::ONLY_PHP7 :只解析成PHP7ParserFactory::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: "; } 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: "; 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: "; 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: \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: \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: \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.htmlhttps://www.52pojie.cn/thread-883976-1-1.htmlhttps://xz.aliyun.com/t/7363https://github.com/nikic/PHP-Parser/blob/master/doc/2_Usage_of_basic_components.markdown
2022年09月06日
171 阅读
1 评论
0 点赞
1
2
...
9