欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

ThinkPHP5底层代码逻辑梳理

程序员文章站 2022-04-17 08:59:09
梳理一遍TP代码底层逻辑,为后面的漏洞总结做准备环境部署以TP5.0.22为例(为下次的TP-RCE环境做好准备)+ PHP 5.6.27-NTS目录架构根据类的命名空间可以快速定位文件位置,在ThinkPHP5.0的规范里面,命名空间其实对应了文件的所在目录,app命名空间通常代表了文件的起始目录为application,而think命名空间则代表了文件的其实目录为thinkphp/library/think,后面的命名空间则表示从起始目录开始的子目录,如下图所示:框架流程我们先进入到默认...

梳理一遍TP代码底层逻辑,为后面的漏洞总结做准备

环境部署

以TP5.0.22为例(为下次的TP-RCE环境做好准备)+ PHP 5.6.27-NTS

目录架构

根据类的命名空间可以快速定位文件位置,在ThinkPHP5.0的规范里面,命名空间其实对应了文件的所在目录,app命名空间通常代表了文件的起始目录为application,而think命名空间则代表了文件的其实目录为thinkphp/library/think,后面的命名空间则表示从起始目录开始的子目录,如下图所示:
ThinkPHP5底层代码逻辑梳理

框架流程

我们先进入到默认的入口文件(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后面细讲)
  • 记录当前调度信息,路由以及请求信息到日志中
  • 请求缓存检查并进行$data = self::exec($dispatch, $config);,根据dispatchdispatch进行不同的调度,返回data
  • 清除类的实例化
  • 输出数据到客户端,$response = $data;,返回一个Response类实例
  • 调用**Response->send()**方法将数据返回值客户端

总结

画个图过一遍整个流程
ThinkPHP5底层代码逻辑梳理

根据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’]已经存在了相应的路由规则ThinkPHP5底层代码逻辑梳理

    • 检测域名部署

    • 检测URL绑定

    • 静态路由规则检查

    • 路由规则检查self::checkRoute($request, $rules, $url, $depr)

      • 检查参数有效性
      • 替换掉路由ext参数
      • 检查分组路由
      • 检查指定特殊路由,例如:__miss____atuo__
      • 检查路由规则checkRule
        • 检查完整规则定义
        • 检查路由的参数分隔符
        • 检查是否完整匹配路由
      • 最终未被匹配路由的进入到self::parseRule('', $miss['route'], $url, $miss['option'])进行处理,这就牵涉到TP对于路由的多种定义ThinkPHP5底层代码逻辑梳理
    • 检查是否强制使用路由$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:路由到闭包函数 闭包函数定义(支持参数传入)

具体链接可以看看这个开发手册

在画个图过一遍整个路由流程
ThinkPHP5底层代码逻辑梳理

小章总结

TP大概就到这里重要的流程就走完了,后续我会直接复盘RCE漏洞,如果再有什么新增的知识点我会更新章节在本文。

如果文章哪里有错误,恳请大家联系我一起讨论。

本文地址:https://blog.csdn.net/qq_39495209/article/details/107486928