前言

代码审计工具的实现都是基于代码审计经验开发出来用于优化工作效率的工具,我们要学好代码审计就必须要熟悉代码审计的思路。而且代码审计是基于PHP语言基础上学习的,学习代码审计最基本的要求就是能读懂代码。

常见的代码审计思路有以下四种:

根据敏感关键字回溯参数传递过程; 查找可控变量,正向追踪变量传递过程; 寻找敏感功能点,通读功能点代码; 直接通读全文代码。 敏感函数回溯参数过程 根据敏感函数来逆向追踪参数的传递过程,是目前使用的最多的一种方式,因为大多数漏洞是由于函数的使用不当造成的。另外非函数使用不当的漏洞,如SQL注入,等以后学习再详细介绍。这种方式的优缺点如下:

优点:只需搜索相应敏感关键字,即可快速挖掘想要的漏洞,可定向挖掘,高效、高质量; 缺点:由于没有通读代码,对程序整体架构了解不够深入,在挖掘漏洞时定位利用会花点时间,另外对逻辑漏洞挖掘覆盖不到。 espcms注入挖掘案例:

下载网址:http://down.chinaz.com/soft/27695.htm

下载espcms源程序 (ps:一些程序源代码可以在chinaz.com上面下载) 打开seay源代码审计系统,点击左上角新建项目,选择下载的espcms文件夹,点击自动审计,开始审计,得到可能存在漏洞,漏洞文件的路径,和漏洞代码列表。

我们挑选其中的一条代码

双击直接定位到这行代码,选中该变量后,可以看到变量的传递过程,在左侧点击parentid函数,在下面详细信息的地方可以看到parentid函数,在下面详细信息的地方可以看到parentid函数,在下面详细信息的地方可以看到parentid变量获得。

右键选中这行代码,定位函数主体accept,点击右键,选择定位函数

可以看到跳转到了class_function.php文件,代码如下:

可以看到这是一个获取GET、POST、COOKIE参数值得函数,我们传入的变量是parentid和R,则代表在POST、GET中都可以获取parentid参数,最后经过一个daddslashes()函数,实际上是包装的addslashes()函数,对单引号等字符进行过滤。看前面的SQL语句是这样的: sql=“select∗fromsql = “select * from sql=“select∗fromdb_table where parentid=$parentid”; 并不需要单引号来闭合,可以直接注入。

在citylist.php文件看到oncitylist()函数在important类中,选中该类名右键点击,选择全局搜索 可以看到index.php文件有实例化该类,代码如下:

$archive = indexget(‘archive’, ‘R’);
$archive = empty($archive) ? ‘adminuser’ : $archive;
$action = indexget(‘action’, ‘R’);
$action = empty($action) ? ‘login’ : $action;
$soft_MOD = array(‘admin’, ‘public’, ‘product’, ‘forum’, ‘filemanage’, ‘basebook’, ‘member’, ‘order’, ‘other’, ‘news’, ‘inc’, ‘cache’, ‘bann’, ‘logs’, ‘template’);
if (in_array($point, $soft_MOD)) {
include admin_ROOT . adminfile . “/control/$archive.php”;
$control = new important();
$action = ‘on’ . $action;
if (method_exists($control, $action)) {
$control->$action();
} else {
exit(‘错误:系统方法错误!’);
}

这里可以看到一个include文件的操作,可惜经过了addslashes()函数无法进行阶段使其包含任意文件,只能包含本地的PHP文件,往下是实例化类并且调用函数的操作,根据代码可以构造出利用EXP:

http://127.0.0.1/espcms/upload/adminsoft/index.php?archive=citylist&action=citylist&parentid=-1 union select 1,2,user(),4,5

通读全文代码 通读全文代码也有一定的技巧,否则很难读懂Web程序的,也很难理解代码的业务逻辑。首先我们要看程序的大体结构,如主目录有哪些文件,模块目录有哪些文件,插件目录有哪些文件,另外还要注意文件的大小,创建时间,就可以大概知道这个程序实现了那些功能,核心文件有哪些。

如discuz的主目录如下图所示:

在看目录结构的时候,特别注意以下几个文件:

1.函数集文件 函数集文件通常命名中包含functions或者common等关键字,这些文件里面是一些公共的函数,提供给其他文件统一调用,所以大多数文件都会在文件头部包含到其他文件。寻找这些文件的一个技巧就是打开index.php或者一些功能性文件。2.配置文件 配置文件通常命名中包含config关键字,配置文件包括Web程序运行必须的功能性配置选项以及数据库等配置信息。从这个文件可以了解程序的小部分功能,另外看这个文件的时候注意观察配置文件中参数是用单引号还是双引号,如果是双引号,则很可能会存在代码执行漏洞。3.安全过滤文件 安全过滤文件对我们做代码审计至关重要,通常命名中有filter、safe、check等关键字,这类文件主要是对参数进行过滤,比较常见的是针对SQL注入和XSS过滤,还有文件路径、执行的系统命令的参数。4.index文件 index是一个程序的入口文件,所以我们只要读一遍index文件就可以大致了解整个程序的架构、运行的流程、包含到的文件。骑士cms通读审计案例 (1)查看应用文件结构

首先看看有哪些文件和文件夹,寻找名称里有没有带api、admin、manage、include一类关键字的文件和文件夹。可以看到有一个include文件夹,一般比较核心的文件都会放在这个文件夹中。

(2)查看关键文件代码 在这个文件夹里可以看多多个数十K的文件,弱common.fun.php就是本程序的核心文件,基础函数基本就在这个文件中实现。一打开文件,立马看多一大堆过滤函数,首先是一个SQL注入过滤函数:

function addslashes_deep($value) {
if (empty($value))
{
return $value;
}
else
{
if (!get_magic_quotes_gpc())
{
$value=is_array($value) ? array_map(‘addslashes_deep’, $value) : mystrip_tags(addslashes($value));
}
else
{
$value=is_array($value) ? array_map(‘addslashes_deep’, $value) : mystrip_tags($value);
}
return $value;
}
}

该函数将传入的变量使用addslashes()函数进行过滤,过滤掉了单引号、双引号、NULL字符以及斜杠,要记住,在挖掘SQL注入漏洞时,只要参数在拼接到SQL语句前,除非有宽字节注入或者其他特殊情况,否则使用了这个函数就不能注入了。

再往下是一个XSS过滤的函数mystrip_tags(),代码如下:

function mystrip_tags($string)
{
$string = new_html_special_chars($string);
$string = remove_xss($string);
return $string;
}

这个函数调用了new_html_special_chars()和remove_xss()函数来过滤XSS,代码如下:

function new_html_special_chars($string) {
$string = str_replace(array(‘&’, ‘"’, ‘<’, ‘>’), array(‘&’, ‘“‘, ‘<’, ‘>’), $string);
$string = strip_tags($string);
return $string;
}
function remove_xss($string) {
$string = preg_replace(‘/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S’, ‘’, $string);$parm1 = Array('javascript', 'union','vbscript', 'expression', 'applet', 'xml', 'blink', 'link', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base');$parm2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload','style','href','action','location','background','src','poster');$parm3 = Array('alert','sleep','load_file','confirm','prompt','benchmark','select','update','insert','delete','alter','drop','truncate','script','eval','outfile','dumpfile');$parm = array_merge($parm1, $parm2, $parm3); for ($i = 0; $i < sizeof($parm); $i++) { $pattern = '/'; for ($j = 0; $j < strlen($parm[$i]); $j++) { if ($j > 0) { $pattern .= '('; $pattern .= '(&#[x|X]0([9][a][b]);?)?'; $pattern .= '|(&#0([9][10][13]);?)?'; $pattern .= ')?'; }$pattern .= $parm[$i][$j]; }$pattern .= '/i';$string = preg_replace($pattern, '****', $string);
}
return $string;
}

在new_html_special_chars()函数中可以看到,这个函数对&符号、双引号以及尖括号进行了html实体编码,并且使用strip_tags()函数进行了二次过滤。而remove_xss()函数则是对一些标签关键字、事件关键字以及敏感函数关键字进行了替换。

再往下有一个获取IP地址的函数getip(),是可以伪造IP地址的:

function getip()
{
if (getenv(‘HTTP_CLIENT_IP’) and strcasecmp(getenv(‘HTTP_CLIENT_IP’),’unknown’)) {
$onlineip=getenv(‘HTTP_CLIENT_IP’);
}elseif (getenv(‘HTTP_X_FORWARDED_FOR’) and strcasecmp(getenv(‘HTTP_X_FORWARDED_FOR’),’unknown’)) {
$onlineip=getenv(‘HTTP_X_FORWARDED_FOR’);
}elseif (getenv(‘REMOTE_ADDR’) and strcasecmp(getenv(‘REMOTE_ADDR’),’unknown’)) {
$onlineip=getenv(‘REMOTE_ADDR’);
}elseif (isset($_SERVER[‘REMOTE_ADDR’]) and $_SERVER[‘REMOTE_ADDR’] and strcasecmp($_SERVER[‘REMOTE_ADDR’],’unknown’)) {
$onlineip=$_SERVER[‘REMOTE_ADDR’];
}
preg_match(“/\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}/“,$onlineip,$match);
return $onlineip = $match[0] ? $match[0] : ‘unknown’;
}

很多应用都会由于在获取IP时没有验证IP格式,而存在注入漏洞,不过这里只是可以伪造IP。

再往下可以看到一个值得关注的地方,SQL查询统一操作函数inserttable()以及updatetable()函数,大多数SQL语句执行都会经过这里,所以我们要关注这个地方是否还有过滤等问题。

function inserttable($tablename, $insertsqlarr, $returnid=0, $replace = false, $silent=0) {
global $db;
$insertkeysql = $insertvaluesql = $comma = ‘’;
foreach ($insertsqlarr as $insert_key => $insert_value) {
$insertkeysql .= $comma.’'.$insert_key.'‘;
$insertvaluesql .= $comma.’\’’.$insert_value.’\’’;
$comma = ‘, ‘;
}
$method = $replace?’REPLACE’:’INSERT’;
// echo $method.” INTO $tablename ($insertkeysql) VALUES ($insertvaluesql)”, $silent?’SILENT’:’’;die;
$state = $db->query($method.” INTO $tablename ($insertkeysql) VALUES ($insertvaluesql)”, $silent?’SILENT’:’’);
if($returnid && !$replace) {
return $db->insert_id();
}else {
return $state;
}
}

再往下则是wheresql()函数,是SQL语句查询的Where条件拼接的地方,我们可以看到参数都使用了单引号进行包裹,代码如下:

function wheresql($wherearr=’’) {
$wheresql=””;
if (is_array($wherearr))
{
$where_set=’ WHERE ‘;
foreach ($wherearr as $key => $value)
{
$wheresql .=$where_set. $comma.$key.’=”‘.$value.’”‘;
$comma = ‘ AND ‘;
$where_set=’ ‘;
}
}
return $wheresql;
}

还有一个访问令牌生成函数asyn_userkey(),拼接用户名、密码salt以及密码进行一次md5,访问的时候只要在GET参数key的值里面加上生成的这个key即可验证是否有权限,被用在注册、找回密码等验证过程中,代码如下:

function asyn_userkey($uid) {
global $db;
$sql = “select * from “.table(‘members’).” where uid = ‘“.intval($uid).”‘ LIMIT 1”;
$user=$db->getone($sql);
return md5($user[‘username’].$user[‘pwd_hash’].$user[‘password’]);
}

(3)查看配置文件 上面我们介绍到配置文件通常带有“config”这样的关键字,我们只要搜索带有这个关键字的文件名即可:

在搜索结果中我们可以看到搜索到多个文件,结合经验可以判断config.php以及cache_config.php才是真正的配置文件,打开config.php查看代码:

<?php
$dbhost = “localhost”;
$dbname =”74cms”;
$dbuser =”root”;
$dbpass =”123456”;
$pre =”qs_”;
$QS_cookiedomain = ‘’;
$QS_cookiepath = “/74cms/“;
$QS_pwdhash = “K0ciF:RkE4xNhu@S”;
define(‘QISHI_CHARSET’,’gb2312’);
define(‘QISHI_DBCHARSET’,’GBK’);
?>

很明显看到,很有可能存在我们之前说过的双引号解析代码执行的问题,通常这个配置是在安装系统的时候设置的,或者后台也有设置的地方。

看看数据库连接时设置的编码,找到骑士cms连接MySQL的代码在include\mysql.class.php文件的connect()函数,代码如下:

function connect($dbhost, $dbuser, $dbpw, $dbname = ‘’, $dbcharset = ‘gbk’, $connect=1){
$func = empty($connect) ? ‘mysql_pconnect’ : ‘mysql_connect’;
if(!$this->linkid = @$func($dbhost, $dbuser, $dbpw, true)){
$this->dbshow(‘Can not connect to Mysql!’);
} else {
if($this->dbversion() > ‘4.1’){
mysql_query( “SET NAMES gbk”);
if($this->dbversion() > ‘5.0.1’){
mysql_query(“SET sql_mode = ‘’”,$this->linkid);
mysql_query(“SET character_set_connection=”.$dbcharset.”, character_set_results=”.$dbcharset.”, character_set_client=binary”, $this->linkid);
}
}
}
if($dbname){
if(mysql_select_db($dbname, $this->linkid)===false){
$this->dbshow(“Can’t select MySQL database($dbname)!”);
}
}
}

这段代码有个关键的地方,有安全隐患。 代码首先判断MySQL版本是否大于4.1,如果是则执行下面代码: mysql_query( “SET NAMES gbk”); 执行这个语句之后在判断,如果版本大于5则执行下面代码: mysql_query(“SET character_set_connection=”.$dbcharset.”, character_set_results=”.$dbcharset.”, character_set_client=binary”, $this->linkid); 也就是说在MySQL版本小于5的情况下是不会执行这行代码的, 但是执行了”set names gbk”,我们在之前介绍过”set names gbk”其实干了三件事,等同于: SET character_set_connection=’gbk’, character_set_results=’gbk’, character_set_client=’gbk’ 因此在MySQL版本大于4.1小于5的情况下,基本所有跟数据库有关的操作都存在宽字节注入。

(4)跟读首页文件 通过对系统文件大概的了解,我们队这套程序的整体架构已经有了一定的了解,但是还不够,需要跟读一下index.php文件,看看程序运行的时候回调用哪些文件和函数。

打开首页文件index.php可以看到如下代码:

if(!file_exists(dirname(FILE).’/data/install.lock’))
header(“Location:install/index.php”);
define(‘IN_QISHI’, true);
$alias=”QS_index”;
require_once(dirname(FILE).’/include/common.inc.php’);

首先判断安装锁文件是否存在,如果不存在则跳转到install\index.php

接下来是包含\include\common.inc.php文件,跟进文件查看

require_once(QISHI_ROOT_PATH.’data/config.php’);
header(“Content-Type:text/html;charset=”.QISHI_CHARSET);
require_once(QISHI_ROOT_PATH.’include/common.fun.php’);
require_once(QISHI_ROOT_PATH.’include/74cms_version.php’);

\include\common.inc.php文件在开头包含了三个文件,data\config.php为数据库配置文件,include\common.fun.php文件为基础函数库文件,include\74cms_version.php为应用版本文件。

再看下面的代码:

f (!empty($_GET))
{
$_GET = addslashes_deep($_GET);
}
if (!empty($_POST))
{
$_POST = addslashes_deep($_POST);
}
$_COOKIE = addslashes_deep($_COOKIE);
$_REQUEST = addslashes_deep($_REQUEST);

这段代码调用了include\common.fun.php文件里面的addslashes_deep()函数对GET、POST、COOKIE参数进行了过滤。

再往下可以看到有一个包含文件的操作: require_once(QISHI_ROOT_PATH.’include/tpl.inc.php’);

包含了include\tpl.inc.php文件,跟进这个文件看看:

include_once(QISHI_ROOT_PATH.’include/template_lite/class.template.php’);
$smarty = new Template_Lite;
$smarty -> cache_dir = QISHI_ROOT_PATH.’temp/caches/‘.$_CFG[‘template_dir’];
$smarty -> compile_dir = QISHI_ROOT_PATH.’temp/templates_c/‘.$_CFG[‘template_dir’];
$smarty -> template_dir = QISHI_ROOT_PATH.’templates/‘.$_CFG[‘template_dir’];
$smarty -> reserved_template_varname = “smarty”;
$smarty -> left_delimiter = “”;
$smarty -> force_compile = false;
$smarty -> assign(‘_PLUG’, $_PLUG);
$smarty -> assign(‘QISHI’, $_CFG);
$smarty -> assign(‘page_select’,$page_select);

首先看到包含了include\template_lite\class.template.php文件,这是一个映射程序模板的类,继续往下看,可以看到这段代码实例化了这个类对象赋值给¥smarty变量。

继续跟进则回到index.php文件代码:

if(!$smarty->is_cached($mypage[‘tpl’],$cached_id))
{
require_once(QISHI_ROOT_PATH.’include/mysql.class.php’);
$db = new mysql($dbhost,$dbuser,$dbpass,$dbname);
unset($dbhost,$dbuser,$dbpass,$dbname);
$smarty->display($mypage[‘tpl’],$cached_id);
}
else
{
$smarty->display($mypage[‘tpl’],$cached_id);
}

判断是否已经缓存,然后调用display()函数输出页面。接下来像审计index.php文件一样跟进其他功能入口文件即可完成代码通读。

根据功能点定向审计 根据经验我们简单介绍几个功能点会出现的漏洞:

1.文件上传功能 这里说的文件上传在很多功能点都会出现,比如像文章编辑、资料编辑、头像上传、附件上传,这个功能最常见的漏洞就是任意文件上传了,后端程序没有严格地限制上传的格式,导致可以上传或者存在绕过的情况,而除了文件上传功能外,还经常发生SQL注入漏洞。2.文件管理功能 在文件管理功能中,如果程序将文件名或者文件路径直接在参数中传递,则很有可能会存在任意文件的操作漏洞,比如任意文件读取等,利用的方法是在路径中使用…/或者…\跳转目录。 除了任意文件操作漏洞外,还可能会存在XSS漏洞,程序会在页面中输出文件名,而通常会疏忽对文件名进行过滤,导致可以在数据库中存入带有尖括号等特殊符号的文件名,最后在页面显示的时候就会被执行。3.登录认证功能 登录认证功能不是指一个过程,而是整个操作过程中的认证,目前的认证方式大多是基于Cookie和Session,不少程序会把当前登陆的用户账号等认证信息放到Cookie中,或许是加密方式。进行操作的时候直接从Cookie中读取当前用户信息,这里就存在一个算法可信的问题,如果这段Cookie信息没有加salt一类的东西,就可以导致任意用户登录漏洞,只要知道用户的不扥信息,即可生成认证令牌,甚至有的程序会直接把用户名放到Cookie中,操作的时候直接读取这个用户名的数据,这也是常说的越权漏洞。4.找回密码功能 找回密码虽然看起来不像任意文件上传这种可以危害到服务器安全的漏洞,但是如果可以重置管理员的密码,也是可以间接控制业务权限甚至拿到服务权限的。找回密码功能的漏洞有很多利用场景,最常见的是验证码爆破。目前特别是APP应用,请求后端验证码的时候大多是4位,并且没有限制验证码的错误次数和有效时间,于是就出现了爆破的漏洞。

零基础入门

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

同时每个成长路线对应的板块都有配套的视频提供:

因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享
视频配套资料&国内外网安书籍、文档&工具
当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。

因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

【代码审计】那些代码审计的思路.md相关推荐

  1. 一次代码审计实战案例【思路流程】

    前言: 利用这个 CMS 看看能不能挖到漏洞,运气还是不错的挖到了两个,分别是 SSRF 与文件覆盖 GETSHELL,下面给大家讲解一下这次审计的思路过程.该 CMS 版本是 4.2.以下漏洞均被 ...

  2. java代码审计_Java代码审计| Spring框架思路篇

    Java的WEB框架是Java进阶课程,当要进行Spring的漏洞分析,要有一定的Java代码知识储备. Java后端标准的学习路线:JavaSE->JavaEE->Java Web框架 ...

  3. 某cms代码审计RCE艰难bypass(思路清奇)

    0x01 前言 闲来无事挖挖漏洞,发现一个经过了一些过滤的漏洞,踩了无数的坑,然后冥思苦想了许多方法,终于找到了一个点,使得可以进行命令执行与getshell.这里的漏洞点不值一提,但是因为绕过方法挺 ...

  4. 代码审计--12--源代码审计思路(上)

    一.引子 一个Java应用的代码审计工作应该从哪里入手,对于新手来说也许会不知所措,如果在不了解整个应用系统情况下(包括业务功能.框架使用等)盲目的直接去阅读项目中源代码,往往会迷失在代码中导致漏洞误 ...

  5. 代码审计--13--源代码审计思路(下)

    三.按照业务类型正向审计 前面提到逆向回溯的审计方式针对特征明显的安全漏洞挖掘是非常有效的,但是同样会有很多弊端,通过逆向回溯的方式只能对通用漏洞进行快速审计,不能全面挖掘更有价值的漏洞,如果在时间允 ...

  6. java代码审计_Java代码审计入门篇

    本期斗哥带来Java代码审计的一些环境和工具准备. Java这个语言相对于PHP来说还是比较复杂的,所以一开始接触到Java源码审计的时候会遇到一些关于环境和配置上一些困难,本文记录斗哥在开始去审计J ...

  7. [ 代码审计篇 ] 代码审计案例详解(二) XXE代码审计案例

  8. 代码审计--49--PHP代码审计中常见的漏洞(一)

    经过漫长时间的迭代更新,内容从一开始的碎片化网罗搜集,到排版与整合划分,再到学习与实践验证,最后摒弃初始,用自己的语言与实践结论通俗的将它展现出来.强化知识技能的同时也为读者带来更好的阅读体验. 本篇 ...

  9. PHP代码审计之简单思路方法

    今天继续给大家介绍渗透测试相关知识,本文主要内容是PHP代码审计之简单思路方法. 免责声明: 本文所介绍的内容仅做学习交流使用,严禁利用文中技术进行非法行为,否则造成一切严重后果自负! 再次强调:严禁 ...

最新文章

  1. 复习计算机网络day1-计算机网络的初步了解
  2. first dairy----the birth of my blog···
  3. div+css如何让一行内的文字两端对齐?
  4. HuggingFace-transformers系列的介绍以及在下游任务中的使用
  5. plsql cursor 函数
  6. 数据挖掘工具软件Weka学习教程
  7. excel插入页码_Excel里毫不起眼的页眉页脚,居然有这3种高能用法!
  8. 使用gson解决java对象循环引用问题
  9. 校园客户端linux创翼,电信创翼校园客户端-电信创翼校园网客户端下载 4.7.9.589 官方版 - 河东下载站...
  10. DELL H330(LSI 3008)RAID卡刷IT直通模式注意事项(避坑指南)
  11. 测试英语的软件有哪些内容,小学英语课本点读软件有哪些?小学英语课本点读APP测评...
  12. 游戏设计梦工厂学习笔记(一)
  13. linux 重启apache:apachectl -k graceful
  14. 神奇宝贝/数码宝贝分类器笔记-机器学习-李宏毅2021
  15. 机房监控系统解说—防火墙篇
  16. STM32+LTC6804/LTC6811采集电压温度电流/BMS储能采集板
  17. solidworks的openGL选项是灰色的处理方法
  18. 在家也能逛家居商场!宜家推出VR购物应用
  19. 体验 Chrome 开发者工具的 12个骚操作!
  20. 图形学初步--------种子填充算法

热门文章

  1. html p首字母缩进,html里p标签里面如何让每一行首行缩进两格???
  2. git命令和遇见得 warning:
  3. (27)Verilog实现倍频【方法四】
  4. 什么是CSTC价值协议
  5. LSB文本水印的嵌入与提取
  6. Linux下lsb_release命令的安装教程
  7. 高精度地图,从入门到入行
  8. Tenth season third episode,Ross is going to be so tan(black)??????
  9. openstack glance-api-paster
  10. 计组作业【题目记录】