AutoCUG
需求:
服务器放在学院机房,会不定期更换ip地址,每次更换ip需要前往机房费事费力。
解决办法1:
总体思路:
编写脚本,定时Ping百度网址,如果Ping不通,则打开火狐浏览器进入到校园网认证界面,使用油猴脚本实现自动登录,同时将IP地址发送到管理员邮箱。
编写Python脚本实现以下功能:
- Ping百度网址
- 打开浏览器
- 运行发送邮件sh脚本
- 日志记录
import os
import webbrowser
from datetime import datetime
import glob
import heapq
import time
class AutoCUG():
def __init__(self, test_url, target_url, mail_sh, log_folder):
self.test_url = test_url
self.target_url = target_url
self.mail_sh = mail_sh
self.log_folder = log_folder
def open_website_if_unreachable(self):
# 确保日志文件夹存在
if not os.path.exists(self.log_folder):
os.makedirs(self.log_folder)
# 获取当前时间并格式化为字符串
current_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
# 构建日志文件名
log_filename = f"{current_time}.log"
# 完整的日志文件路径
log_path = os.path.join(self.log_folder, log_filename)
# 使用ping命令检查网站是否可达
response = os.system(f"ping -c 1 {self.test_url}")
# 打开日志文件并写入日志
with open(log_path, 'w') as log_file:
# 如果ping操作返回非零值,表示无法ping通
if response != 0:
# 打开网页
webbrowser.get('firefox').open(self.target_url)
log_message = f"无法ping通 {self.test_url},已尝试打开网页。\n"
log_file.write(log_message)
print(log_message)
time.sleep(60)#延时1min,确保网页打开
#发送ip地址给管理员邮件
os.system(f"sh {self.mail_sh}")
else:
log_message = f"{self.test_url} 可以ping通。\n"
log_file.write(log_message)
print(log_message)
def manage_log_folder(self, max_logs=100):#日志文件最大数为100
# 获取日志文件夹中的所有日志文件
log_files = glob.glob(os.path.join(self.log_folder, '*.log'))
# 如果日志文件数量超过最大限制
if len(log_files) > max_logs:
# 使用heapq.nsmallest获取最早创建的日志文件(基于文件名,假设文件名包含时间戳)
files_to_delete = heapq.nsmallest(len(log_files) - max_logs, log_files, key=os.path.getctime)
# 删除这些文件
for file in files_to_delete:
os.remove(file)
if __name__ == '__main__':
test_url = 'www.baidu.com'
target_url = 'http://192.168.167.115'
mail_sh = '/home/user/桌面/NetWork/email.sh'
log_folder = '/home/user/桌面/NetWork/log'
demo=AutoCUG(test_url, target_url, mail_sh, log_folder)
demo.open_website_if_unreachable()
demo.manage_log_folder(max_logs=100)
编写油猴脚本实现:
- 打开认证页面则自动输入帐号密码
- 点击登录
// ==UserScript==
// @name AutoCUG
// @namespace http://tampermonkey.net/
// @version 2024-01-26
// @description try to take over the world!
// @author You
// @match *://192.168.167.115/*
// @match *://nap.cug.edu.cn
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant none
// ==/UserScript==
(function() {
'use strict';
function AutoCUG(){
document.querySelector('#username').value = "账号";
document.querySelector("#password").value = "密码";
document.getElementById("login-account").click();
}
AutoCUG();
})();
编写Shell脚本实现:
- 获取指定网口ip地址与上次ip地址进行比较
- 发送到管理员邮箱
- 记录ip地址
#!/bin/bash
# 存储IP地址的文件路径
ip_file="/home/user/桌面/NetWork/ip.txt"
# 获取当前时间
time_now=$(date "+%Y-%m-%d %H:%M:%S")
# 获取当前IP地址
current_ip=$(ifconfig eno1np0 | grep inet -w | tr -s ' ' | cut -d ' ' -f 3)
# 读取上一次保存的IP地址和时间
previous_info=$(cat "$ip_file" 2>/dev/null)
# 分隔保存的信息中的时间和IP地址
previous_time=$(echo "$previous_info" | cut -d ' ' -f 2)
previous_ip=$(echo "$previous_info" | cut -d ' ' -f 4)
# 将IP地址和时间写入文件
echo "[$time_now] IP地址: $current_ip" > "$ip_file"
# 如果IP地址发生变化,则发送邮件
if [ "$current_ip" != "$previous_ip" ]; then
# 发送邮件
echo "IP地址:$current_ip" | s-nail -s "$time_now : IP地址" XXXXXXX@qq.com
fi
设置定时任务
Crontab -e
0 */6 * * * /home/user/anaconda3/bin/python /home/user/桌面/NetWork/AutoCUG.py
解决办法2:
总体思路:
破解校园网登录方法,实现ping不通时获取网口ip直接登录
深蓝校园网认证破解思路:
来源于:https://blog.csdn.net/qq_20534023/article/details/124159181
Python代码:
import os
import webbrowser
from datetime import datetime
import glob
import heapq
import time
import requests
import re
import math
import hashlib
import hmac
import hashlib
import socket
import psutil
def get_ip_address(ifname):
# 获取网口信息
for interface in psutil.net_if_addrs():
if interface == ifname:
for addr in psutil.net_if_addrs()[interface]:
# 检查地址类型是否为IPv4
if addr.family == socket.AF_INET:
return addr.address
return None
#xencode-----------------------------------------------------------------------
def force(msg):
ret = []
for w in msg:
ret.append(ord(w))
return bytes(ret)
def ordat(msg, idx):
if len(msg) > idx:
return ord(msg[idx])
return 0
def sencode(msg, key):
l = len(msg)
pwd = []
for i in range(0, l, 4):
pwd.append(
ordat(msg, i) | ordat(msg, i + 1) << 8 | ordat(msg, i + 2) << 16
| ordat(msg, i + 3) << 24)
if key:
pwd.append(l)
return pwd
def lencode(msg, key):
l = len(msg)
ll = (l - 1) << 2
if key:
m = msg[l - 1]
if m < ll - 3 or m > ll:
return
ll = m
for i in range(0, l):
msg[i] = chr(msg[i] & 0xff) + chr(msg[i] >> 8 & 0xff) + chr(
msg[i] >> 16 & 0xff) + chr(msg[i] >> 24 & 0xff)
if key:
return "".join(msg)[0:ll]
return "".join(msg)
def get_xencode(msg, key):
if msg == "":
return ""
pwd = sencode(msg, True)
pwdk = sencode(key, False)
if len(pwdk) < 4:
pwdk = pwdk + [0] * (4 - len(pwdk))
n = len(pwd) - 1
z = pwd[n]
y = pwd[0]
c = 0x86014019 | 0x183639A0
m = 0
e = 0
p = 0
q = math.floor(6 + 52 / (n + 1))
d = 0
while 0 < q:
d = d + c & (0x8CE0D9BF | 0x731F2640)
e = d >> 2 & 3
p = 0
while p < n:
y = pwd[p + 1]
m = z >> 5 ^ y << 2
m = m + ((y >> 3 ^ z << 4) ^ (d ^ y))
m = m + (pwdk[(p & 3) ^ e] ^ z)
pwd[p] = pwd[p] + m & (0xEFB8D130 | 0x10472ECF)
z = pwd[p]
p = p + 1
y = pwd[0]
m = z >> 5 ^ y << 2
m = m + ((y >> 3 ^ z << 4) ^ (d ^ y))
m = m + (pwdk[(p & 3) ^ e] ^ z)
pwd[n] = pwd[n] + m & (0xBB390742 | 0x44C6F8BD)
z = pwd[n]
q = q - 1
return lencode(pwd, False)
#-----------------------------------------------------------------------------------
#base64-----------------------------------------------------------------------------
_PADCHAR = "="
_ALPHA = "LVoJPiCN2R8G90yg+hmFHuacZ1OWMnrsSTXkYpUq/3dlbfKwv6xztjI7DeBE45QA"
def _getbyte(s, i):
x = ord(s[i])
if (x > 255):
print("INVALID_CHARACTER_ERR: DOM Exception 5")
exit(0)
return x
def get_base64(s):
i=0
b10=0
x = []
imax = len(s) - len(s) % 3
if len(s) == 0:
return s
for i in range(0,imax,3):
b10 = (_getbyte(s, i) << 16) | (_getbyte(s, i + 1) << 8) | _getbyte(s, i + 2)
x.append(_ALPHA[(b10 >> 18)])
x.append(_ALPHA[((b10 >> 12) & 63)])
x.append(_ALPHA[((b10 >> 6) & 63)])
x.append(_ALPHA[(b10 & 63)])
i=imax
if len(s) - imax ==1:
b10 = _getbyte(s, i) << 16
x.append(_ALPHA[(b10 >> 18)] + _ALPHA[((b10 >> 12) & 63)] + _PADCHAR + _PADCHAR)
else:
b10 = (_getbyte(s, i) << 16) | (_getbyte(s, i + 1) << 8)
x.append(_ALPHA[(b10 >> 18)] + _ALPHA[((b10 >> 12) & 63)] + _ALPHA[((b10 >> 6) & 63)] + _PADCHAR)
return "".join(x)
#------------------------------------------------------------------------------
#md5--------------------
def get_md5(password,token):
return hmac.new(token.encode(), password.encode(), hashlib.md5).hexdigest()
#sha1--------------------
def get_sha1(value):
return hashlib.sha1(value.encode()).hexdigest()
header={
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36'
}
init_url="http://192.168.167.115"
get_challenge_api="http://192.168.167.115//cgi-bin/get_challenge"
srun_portal_api="http://192.168.167.115/cgi-bin/srun_portal"
get_info_api="http://192.168.167.115/cgi-bin/rad_user_info?callback=jQuery112406118340540763985_1556004912581&_=1556004912582"
n = '200'
type = '1'
ac_id='1'
enc = "srun_bx1"
def get_chksum():
chkstr = token+username
chkstr += token+hmd5
chkstr += token+ac_id
chkstr += token+ip
chkstr += token+n
chkstr += token+type
chkstr += token+i
return chkstr
def get_info():
info_temp={
"username":username,
"password":password,
"ip":ip,
"acid":ac_id,
"enc_ver":enc
}
i=re.sub("'",'"',str(info_temp))
i=re.sub(" ",'',i)
return i
def get_token():
# print("获取token")
global token
get_challenge_params={
"callback": "jQuery112404953340710317169_"+str(int(time.time()*1000)),
"username":username,
"ip":ip,
"_":int(time.time()*1000),
}
get_challenge_res=requests.get(get_challenge_api,params=get_challenge_params,headers=header)
token=re.search('"challenge":"(.*?)"',get_challenge_res.text).group(1)
#print(get_challenge_res.text)
print("token为:"+token)
def do_complex_work():
global i,hmd5,chksum
i=get_info()
i="{SRBX1}"+get_base64(get_xencode(i,token))
hmd5=get_md5(password,token)
chksum=get_sha1(get_chksum())
print("所有加密工作已完成")
def login():
srun_portal_params={
'callback': 'jQuery11240645308969735664_'+str(int(time.time()*1000)),
'action':'login',
'username':username,
'password':'{MD5}'+hmd5,
'ac_id':ac_id,
'ip':ip,
'chksum':chksum,
'info':i,
'n':n,
'type':type,
'os':'windows+10',
'name':'windows',
'double_stack':'0',
'_':int(time.time()*1000)
}
# print(srun_portal_params)
srun_portal_res=requests.get(srun_portal_api,params=srun_portal_params,headers=header)
#print(srun_portal_res.text)
class AutoCUG():
def __init__(self, test_url, target_url, mail_sh, log_folder):
self.test_url = test_url
self.target_url = target_url
self.mail_sh = mail_sh
self.log_folder = log_folder
def open_website_if_unreachable(self):
# 确保日志文件夹存在
if not os.path.exists(self.log_folder):
os.makedirs(self.log_folder)
# 获取当前时间并格式化为字符串
current_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
# 构建日志文件名
log_filename = f"{current_time}.log"
# 完整的日志文件路径
log_path = os.path.join(self.log_folder, log_filename)
# 使用ping命令检查网站是否可达
response = os.system(f"ping -c 1 {self.test_url}")
# 打开日志文件并写入日志
with open(log_path, 'w') as log_file:
# 如果ping操作返回非零值,表示无法ping通
if response != 0:
get_token()
do_complex_work()
login()
res=requests.get(get_info_api,headers=header)
print("用户",username,'登陆成功!')
time.sleep(20)
#发送ip地址给管理员邮件
os.system(f"sh {self.mail_sh}")
else:
log_message = f"{self.test_url} 可以ping通。\n"
log_file.write(log_message)
print(log_message)
def manage_log_folder(self, max_logs=100):
# 获取日志文件夹中的所有日志文件
log_files = glob.glob(os.path.join(self.log_folder, '*.log'))
# 如果日志文件数量超过最大限制
if len(log_files) > max_logs:
# 使用heapq.nsmallest获取最早创建的日志文件(基于文件名,假设文件名包含时间戳)
files_to_delete = heapq.nsmallest(len(log_files) - max_logs, log_files, key=os.path.getctime)
# 删除这些文件
for file in files_to_delete:
os.remove(file)
if __name__ == '__main__':
test_url = 'www.baidu.com'
target_url = 'http://192.168.167.115'
mail_sh = '/home/user/桌面/NetWork/email.sh'
log_folder = '/home/user/桌面/NetWork/log'
global username,password,ip
interface_name = 'eno1np0' #网口名称
ip =get_ip_address(interface_name)#需要认证服务器的ip地址
username=''#用户名
password=''#密码
demo=AutoCUG(test_url, target_url, mail_sh, log_folder)
demo.open_website_if_unreachable()
demo.manage_log_folder(max_logs=100)
#webbrowser.get('firefox').open('http://192.168.167.115')
解决办法3(Python全流程)
需安装以下库
certifi==2024.8.30
charset-normalizer==3.4.0
idna==3.10
psutil==6.1.0
requests==2.32.3
urllib3==2.2.3
代码:
import os
import webbrowser
from datetime import datetime
import glob
import heapq
import time
import requests
import re
import math
import hashlib
import hmac
import hashlib
import socket
import psutil
import smtplib
from email.mime.text import MIMEText
# 存储所有网卡的 IPv4 地址
def get_ipv4_addresses():
ipv4_addresses = {}
# 获取所有网卡及其地址信息
for interface, addrs in psutil.net_if_addrs().items():
for addr in addrs:
# 检查地址类型是否为 IPv4
if addr.family == socket.AF_INET:
# 将网卡名称和对应的 IPv4 地址存储到字典中
#ipv4_addresses[interface] = addr.address
# 检查地址是否以 "172." 开头
if addr.address.startswith("172."):
return addr.address
#发送邮件
def send_email(email_info):
try:
# 从字典中获取参数
smtp_server = email_info['smtp_server']
port = email_info['port']
sender_email = email_info['sender_email']
sender_password = email_info['sender_password']
recipient_email = email_info['recipient_email']
subject = email_info['subject']
body = email_info['body']
# 创建邮件内容
message = MIMEText(body, "plain", "utf-8")
message["From"] = sender_email
message["To"] = recipient_email
message["Subject"] = subject
# 连接 SMTP 服务器
with smtplib.SMTP(smtp_server, port) as server:
server.starttls() # 开启 TLS 加密
server.login(sender_email, sender_password)
# 发送邮件
response = server.sendmail(sender_email, recipient_email, message.as_string())
if response:
print(f"部分邮件发送失败: {response}")
else:
print("邮件发送成功!")
except smtplib.SMTPException as e:
# 过滤特定错误 (-1, b'\x00\x00\x00')
if e.args == (-1, b'\x00\x00\x00'):
print("邮件已成功发送,但服务器返回了非标准响应,忽略该错误。")
else:
print(f"SMTP 错误: {e}")
except Exception as e:
print(f"未知错误: {e}")
#xencode-----------------------------------------------------------------------
def force(msg):
ret = []
for w in msg:
ret.append(ord(w))
return bytes(ret)
def ordat(msg, idx):
if len(msg) > idx:
return ord(msg[idx])
return 0
def sencode(msg, key):
l = len(msg)
pwd = []
for i in range(0, l, 4):
pwd.append(
ordat(msg, i) | ordat(msg, i + 1) << 8 | ordat(msg, i + 2) << 16
| ordat(msg, i + 3) << 24)
if key:
pwd.append(l)
return pwd
def lencode(msg, key):
l = len(msg)
ll = (l - 1) << 2
if key:
m = msg[l - 1]
if m < ll - 3 or m > ll:
return
ll = m
for i in range(0, l):
msg[i] = chr(msg[i] & 0xff) + chr(msg[i] >> 8 & 0xff) + chr(
msg[i] >> 16 & 0xff) + chr(msg[i] >> 24 & 0xff)
if key:
return "".join(msg)[0:ll]
return "".join(msg)
def get_xencode(msg, key):
if msg == "":
return ""
pwd = sencode(msg, True)
pwdk = sencode(key, False)
if len(pwdk) < 4:
pwdk = pwdk + [0] * (4 - len(pwdk))
n = len(pwd) - 1
z = pwd[n]
y = pwd[0]
c = 0x86014019 | 0x183639A0
m = 0
e = 0
p = 0
q = math.floor(6 + 52 / (n + 1))
d = 0
while 0 < q:
d = d + c & (0x8CE0D9BF | 0x731F2640)
e = d >> 2 & 3
p = 0
while p < n:
y = pwd[p + 1]
m = z >> 5 ^ y << 2
m = m + ((y >> 3 ^ z << 4) ^ (d ^ y))
m = m + (pwdk[(p & 3) ^ e] ^ z)
pwd[p] = pwd[p] + m & (0xEFB8D130 | 0x10472ECF)
z = pwd[p]
p = p + 1
y = pwd[0]
m = z >> 5 ^ y << 2
m = m + ((y >> 3 ^ z << 4) ^ (d ^ y))
m = m + (pwdk[(p & 3) ^ e] ^ z)
pwd[n] = pwd[n] + m & (0xBB390742 | 0x44C6F8BD)
z = pwd[n]
q = q - 1
return lencode(pwd, False)
#-----------------------------------------------------------------------------------
#base64-----------------------------------------------------------------------------
_PADCHAR = "="
_ALPHA = "LVoJPiCN2R8G90yg+hmFHuacZ1OWMnrsSTXkYpUq/3dlbfKwv6xztjI7DeBE45QA"
def _getbyte(s, i):
x = ord(s[i])
if (x > 255):
print("INVALID_CHARACTER_ERR: DOM Exception 5")
exit(0)
return x
def get_base64(s):
i=0
b10=0
x = []
imax = len(s) - len(s) % 3
if len(s) == 0:
return s
for i in range(0,imax,3):
b10 = (_getbyte(s, i) << 16) | (_getbyte(s, i + 1) << 8) | _getbyte(s, i + 2)
x.append(_ALPHA[(b10 >> 18)])
x.append(_ALPHA[((b10 >> 12) & 63)])
x.append(_ALPHA[((b10 >> 6) & 63)])
x.append(_ALPHA[(b10 & 63)])
i=imax
if len(s) - imax ==1:
b10 = _getbyte(s, i) << 16
x.append(_ALPHA[(b10 >> 18)] + _ALPHA[((b10 >> 12) & 63)] + _PADCHAR + _PADCHAR)
else:
b10 = (_getbyte(s, i) << 16) | (_getbyte(s, i + 1) << 8)
x.append(_ALPHA[(b10 >> 18)] + _ALPHA[((b10 >> 12) & 63)] + _ALPHA[((b10 >> 6) & 63)] + _PADCHAR)
return "".join(x)
#------------------------------------------------------------------------------
#md5--------------------
def get_md5(password,token):
return hmac.new(token.encode(), password.encode(), hashlib.md5).hexdigest()
#sha1--------------------
def get_sha1(value):
return hashlib.sha1(value.encode()).hexdigest()
header={
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36'
}
init_url="http://192.168.167.115"
get_challenge_api="http://192.168.167.115//cgi-bin/get_challenge"
srun_portal_api="http://192.168.167.115/cgi-bin/srun_portal"
get_info_api="http://192.168.167.115/cgi-bin/rad_user_info?callback=jQuery112406118340540763985_1556004912581&_=1556004912582"
n = '200'
type = '1'
ac_id='1'
enc = "srun_bx1"
def get_chksum():
chkstr = token+username
chkstr += token+hmd5
chkstr += token+ac_id
chkstr += token+ip
chkstr += token+n
chkstr += token+type
chkstr += token+i
return chkstr
def get_info():
info_temp={
"username":username,
"password":password,
"ip":ip,
"acid":ac_id,
"enc_ver":enc
}
i=re.sub("'",'"',str(info_temp))
i=re.sub(" ",'',i)
return i
def get_token():
# print("获取token")
global token
get_challenge_params={
"callback": "jQuery112404953340710317169_"+str(int(time.time()*1000)),
"username":username,
"ip":ip,
"_":int(time.time()*1000),
}
get_challenge_res=requests.get(get_challenge_api,params=get_challenge_params,headers=header)
token=re.search('"challenge":"(.*?)"',get_challenge_res.text).group(1)
#print(get_challenge_res.text)
#print("token为:"+token)
def do_complex_work():
global i,hmd5,chksum
i=get_info()
i="{SRBX1}"+get_base64(get_xencode(i,token))
hmd5=get_md5(password,token)
chksum=get_sha1(get_chksum())
#print("所有加密工作已完成")
def login():
srun_portal_params={
'callback': 'jQuery11240645308969735664_'+str(int(time.time()*1000)),
'action':'login',
'username':username,
'password':'{MD5}'+hmd5,
'ac_id':ac_id,
'ip':ip,
'chksum':chksum,
'info':i,
'n':n,
'type':type,
'os':'windows+10',
'name':'windows',
'double_stack':'0',
'_':int(time.time()*1000)
}
# print(srun_portal_params)
srun_portal_res=requests.get(srun_portal_api,params=srun_portal_params,headers=header)
#print(srun_portal_res.text)
class AutoCUG():
def __init__(self, test_url, target_url, log_folder):
self.test_url = test_url
self.target_url = target_url
self.log_folder = log_folder
def open_website_if_unreachable(self):
# 确保日志文件夹存在
if not os.path.exists(self.log_folder):
os.makedirs(self.log_folder)
# 构建日志文件名
log_filename = "network_check.log"
# 完整的日志文件路径
log_path = os.path.join(self.log_folder, log_filename)
# 获取当前时间并格式化为字符串
current_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
# 使用ping命令检查网站是否可达
response = os.system(f"ping -c 1 {self.test_url}")
# 打开日志文件并写入日志
with open(log_path, 'a') as log_file:
# 如果ping操作返回非零值,表示无法ping通
if response != 0:
log_message = f"{current_time}:{self.test_url} 不能ping通。本机IP地址为{ip}\n"
log_file.write(log_message)
get_token()
do_complex_work()
login()
res=requests.get(get_info_api,headers=header)
if res.status_code==200:
log_file.write(f"{current_time}:用户{username}登陆成功!\n")
time.sleep(20)
#发送ip地址给管理员邮件
send_email(email_info)
log_file.write(f"{current_time}:邮件发送成功")
else:
log_message = f"{current_time}:{self.test_url} 可以ping通。本机IP地址为{ip}\n"
log_file.write(log_message)
if __name__ == '__main__':
test_url = 'www.baidu.com'
target_url = 'http://192.168.167.115'
log_folder = '/root/NetWork/log'
global username,password,ip
ip =get_ipv4_addresses()#需要认证服务器的ip地址
username='' "#cug用户名
password=' '#cug密码
# 配置邮件发送参数
email_info = {
'smtp_server' : "smtp.qq.com", # SMTP 地址
'port' : 587, # SMTP 端口(587 是 TLS,465 是 SSL)
'sender_email' : "",#发送邮箱
'sender_password': "",#授权码而不是账号密码
'recipient_email' : "", #接收邮箱
'subject' : "服务器IP地址",
'body' : f"服务器IP地址为{ip}"
}
demo=AutoCUG(test_url, target_url, log_folder)
demo.open_website_if_unreachable()
Crontab设置
每6小时运行
0 */6 * * * /root/miniconda3/bin /root/NetWork/AutoCUG.py
标签:
Tools
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理