ThinkPHP5底层代码逻辑梳理
梳理⼀遍TP代码底层逻辑,为后⾯的漏洞总结做准备
环境部署
以TP5.0.22为例(为下次的TP-RCE环境做好准备)+ PHP 5.6.27-NTS
⽬录架构
根据类的命名空间可以快速定位⽂件位置,在ThinkPHP5.0的规范⾥⾯,命名空间其实对应了⽂件的所在⽬录,app命名空间通常代表了⽂件的起始⽬录为application,⽽think命名空间则代表了⽂件的其实⽬录为thinkphp/library/think,后⾯的命名空间则表⽰从起始⽬录开始的⼦⽬录,如下图所⽰:
框架流程
我们先进⼊到默认的⼊⼝⽂件(public/index.php)
// 定义应⽤⽬录
define('APP_PATH',__DIR__.'/../application/');
// 加载框架引导⽂件
require__DIR__.'/../thinkphp/start.php';
引⼊start.php进⼊到⾥⾯看看有什么
框架引导⽂件(thinkphp/start.php)
进⼊框架引导⽂件看到两⾏代码
// ThinkPHP 引导⽂件
// 1. 加载基础⽂件
require__DIR__.'/base.php';
// 2. 执⾏应⽤
App::run()->send();
基础⽂件(thinkphp/base.php)
在此⽂件⾸先看到全⾯⼤段的是定义常量或者是检查常量是否存在,主要是以下⼏点需要重点注意
将Loader类引⼊
注册⾃动加载机制
注册系统⾃动加载,spl_autoload_register将函数注册到SPL __autoload函数队列中。如果该队列中的函数尚未激活,则激活它们。此函数可以注册任意数量的⾃动加载器,当使⽤尚未被定义的类(class)和接⼝(interface)时⾃动去加载。通过注册⾃动加载器,脚本引擎在 PHP 出错失败前有了最后⼀个机会加载所需的类。
Composer ⾃动加载⽀持
注册命名空间定义:think=>thinkphp/library/think,behavior=>thinkphp/library/behavior,traits=>thinkphp/library/traits
加载类库映射⽂件
⾃动加载 extend ⽬录
注册异常处理机制
加载惯例配置
执⾏应⽤(thinkphp/library/think/App.php)
⾸先返回⼀个request实例,将应⽤初始化返回配置信息。
之后进⾏如下的操作:
查看是否存在模块控制器绑定
对于request的实例根据设置的过滤规则进⾏过滤
加载语⾔包
监听app_dispatch
进⾏URL路由检测(routecheck后⾯细讲)
记录当前调度信息,路由以及请求信息到⽇志中
dispatch进⾏不同的调度,返回
请求缓存检查并进⾏$data = self::exec($dispatch, $config);,根据data
清除类的实例化
输出数据到客户端,$response = $data;,返回⼀个Response类实例
调⽤**Response->send()**⽅法将数据返回值客户端
总结
画个图过⼀遍整个流程
根据PATH_INFO进⾏URL路由检测(App::routeCheck)
通过$path = $request->path()可以获得到请求的path_info,$depr是定义的分隔符,默认时:/,之后进⾏路由检测步骤如下查看是否存在路由缓存,存在就包含
读取应⽤所在的路由⽂件,⼀般默认为route.php
导⼊路由配置
Route::check (根据路由定义返回不同的URL调度)
检查解析缓存
替换分隔符,将"/“换成了”|"
获取当前请求类型的路由规则,由于在之前的Composer ⾃动加载⽀持,在vendortopthink/think-captcha/src/helper.php中注册了路由,所以在$rules = isset(self::$rules[$method]) ? self::$rules[$method] : [];中的Route::$rules[‘get’]已经存在了相应的路
由规则
检测域名部署
检测URL绑定
静态路由规则检查
路由规则检查self::checkRoute($request, $rules, $url, $depr)
检查参数有效性
国内php空间检查参数有效性
替换掉路由ext参数
检查分组路由
检查指定特殊路由,例如:__miss__和__atuo__
检查路由规则checkRule
检查完整规则定义
检查路由的参数分隔符
检查是否完整匹配路由
最终未被匹配路由的进⼊到self::parseRule('', $miss['route'], $url, $miss['option'])进⾏处理,这就牵涉到TP对于路由的多种定义
检查是否强制使⽤路由$must = !is_null(self::$routeMust) ? self::$routeMust : $config['url_route_must']
路由⽆效,将⾃动解析模块的URL地址会进⼊到Route::parseUrl($path, $depr, $config['controller_auto_search'])(后⾯的RCE会⽤到这⼀点,将会在后续⽂章进⾏详细分析)
最终将结果记录到调度信息
总结
⾸先看看路由定义:
定义⽅式定义格式
⽅式1:路由到模块/控制器(模块/控制器/操作)?额外参数1=值1&额外参数2=值2…
⽅式2:路由到重定向地址‘外部地址’(默认301重定向) 或者 (‘外部地址’,‘重定向代码’)
⽅式3:路由到控制器的⽅法‘@(模块/控制器/)操作’
⽅式4:路由到类的⽅法‘\完整的命名空间类::静态⽅法’ 或者 ‘\完整的命名空间类@动态⽅法’
⽅式5:路由到闭包函数闭包函数定义(⽀持参数传⼊)
具体链接可以看看这个
在画个图过⼀遍整个路由流程
⼩章总结
TP⼤概就到这⾥重要的流程就⾛完了,后续我会直接复盘RCE漏洞,如果再有什么新增的知识点我会更新章节在本⽂。
如果⽂章哪⾥有错误,恳请⼤家联系我⼀起讨论。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论