Skip to content

Commit 730bbe9

Browse files
committed
v1.9.3
1 parent 7613c9c commit 730bbe9

3,192 files changed

Lines changed: 132770 additions & 2843 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,17 @@
88

99
## 更新说明
1010

11-
### v1.9.2
11+
### v1.9.3
1212

1313
感谢师傅们提供的修改建议,目前已更新一下功能:
1414

15-
1. 新增redis命令执行功能;
15+
1. 优化redis界面,修复连接过多导致的页面无限下拉的bug;
16+
2. 新增显示更新详情功能;
17+
3. 参考fine修改小程序反编译界面;
18+
4. tools新建工具的时间添加打开工具文件位置的功能;
19+
5. 新增二级菜单的显示/隐藏,位置移动;
20+
6. 修改ftp下载方式为浏览器下载,避免EasyTools卡死;
21+
7. 优化JwtCreck功能;
1622

1723
## 工具介绍
1824

@@ -94,7 +100,7 @@ cd /D EasyToolsFiles\tools\gui_webshell\Godzilla && loader.vbs [可选参数]
94100

95101
![image-20250826211856564](images/image-20250826211856564.png)
96102

97-
![image-20250826211907568](images/image-20250826211907568.png)
103+
![image-20250826211907568.png](images/image-20250826211907568.png)
98104

99105
### 简连助手
100106

@@ -228,4 +234,5 @@ https://github.com/gchq/CyberChef
228234
https://github.com/ZororoZ/fscanOutput
229235
https://github.com/o8oo8o/WebSSH
230236
https://github.com/broken5/unveilr
237+
https://github.com/fasnow/fine
231238
~~~

app/connect/ftp/ftp.go

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,47 @@
11
package ftp
22

33
import (
4+
"crypto/md5"
45
"fmt"
56
"io"
67
"net/http"
78
"path/filepath"
89
"strconv"
10+
"sync"
911
"time"
1012

1113
"github.com/gin-contrib/cors"
1214
"github.com/gin-gonic/gin"
1315
"github.com/jlaffaye/ftp"
1416
)
1517

18+
var (
19+
downloadTokens = make(map[string]FTPConfig)
20+
tokenMutex sync.RWMutex
21+
)
22+
23+
type FTPConfig struct {
24+
Host string
25+
Port int
26+
Username string
27+
Password string
28+
Path string
29+
Created time.Time
30+
}
31+
32+
// 清理过期的令牌
33+
func cleanupTokens() {
34+
tokenMutex.Lock()
35+
defer tokenMutex.Unlock()
36+
37+
now := time.Now()
38+
for token, config := range downloadTokens {
39+
if now.Sub(config.Created) > 10*time.Minute { // 10分钟过期
40+
delete(downloadTokens, token)
41+
}
42+
}
43+
}
44+
1645
func connectFTP(host string, port int, username, password string) (*ftp.ServerConn, error) {
1746
address := fmt.Sprintf("%s:%d", host, port)
1847
conn, err := ftp.Dial(address, ftp.DialWithTimeout(5*time.Second))
@@ -157,6 +186,120 @@ func StartWebFTP() {
157186
}
158187
})
159188

189+
// 生成下载令牌
190+
r.POST("/api/ftp/generate-download-url", func(c *gin.Context) {
191+
var req struct {
192+
Host string `json:"host"`
193+
Port int `json:"port"`
194+
Username string `json:"username"`
195+
Password string `json:"password"`
196+
Path string `json:"path"`
197+
}
198+
if err := c.ShouldBindJSON(&req); err != nil {
199+
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
200+
return
201+
}
202+
203+
// 验证FTP连接
204+
conn, err := connectFTP(req.Host, req.Port, req.Username, req.Password)
205+
if err != nil {
206+
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
207+
return
208+
}
209+
defer conn.Quit()
210+
211+
// 验证文件存在
212+
_, err = conn.FileSize(req.Path)
213+
if err != nil {
214+
c.JSON(http.StatusInternalServerError, gin.H{"error": "文件不存在: " + err.Error()})
215+
return
216+
}
217+
218+
// 生成唯一令牌
219+
token := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%s%d%s%s%s%d",
220+
req.Host, req.Port, req.Username, req.Password, req.Path, time.Now().UnixNano()))))
221+
222+
// 存储令牌信息
223+
tokenMutex.Lock()
224+
downloadTokens[token] = FTPConfig{
225+
Host: req.Host,
226+
Port: req.Port,
227+
Username: req.Username,
228+
Password: req.Password,
229+
Path: req.Path,
230+
Created: time.Now(),
231+
}
232+
tokenMutex.Unlock()
233+
234+
// 定期清理令牌
235+
go cleanupTokens()
236+
237+
downloadURL := fmt.Sprintf("http://127.0.0.1:52869/api/ftp/direct-download?token=%s", token)
238+
239+
c.JSON(http.StatusOK, gin.H{"downloadUrl": downloadURL})
240+
})
241+
242+
// 直接下载接口
243+
r.GET("/api/ftp/direct-download", func(c *gin.Context) {
244+
token := c.Query("token")
245+
if token == "" {
246+
c.JSON(http.StatusBadRequest, gin.H{"error": "缺少下载令牌"})
247+
return
248+
}
249+
250+
// 获取令牌对应的配置
251+
tokenMutex.RLock()
252+
config, exists := downloadTokens[token]
253+
tokenMutex.RUnlock()
254+
255+
if !exists {
256+
c.JSON(http.StatusBadRequest, gin.H{"error": "下载令牌无效或已过期"})
257+
return
258+
}
259+
260+
// 连接FTP
261+
conn, err := connectFTP(config.Host, config.Port, config.Username, config.Password)
262+
if err != nil {
263+
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
264+
return
265+
}
266+
defer conn.Quit()
267+
268+
// 获取文件信息
269+
size, err := conn.FileSize(config.Path)
270+
if err != nil {
271+
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取文件大小失败: " + err.Error()})
272+
return
273+
}
274+
275+
// 开始下载
276+
resp, err := conn.Retr(config.Path)
277+
if err != nil {
278+
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
279+
return
280+
}
281+
defer resp.Close()
282+
283+
// 设置响应头
284+
filename := filepath.Base(config.Path)
285+
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
286+
c.Header("Content-Type", "application/octet-stream")
287+
c.Header("Content-Length", fmt.Sprintf("%d", size))
288+
c.Status(http.StatusOK)
289+
290+
// 流式传输
291+
buf := make([]byte, 32*1024)
292+
_, err = io.CopyBuffer(c.Writer, io.LimitReader(resp, size), buf)
293+
if err != nil && err != io.EOF {
294+
fmt.Println("下载中断:", err)
295+
}
296+
297+
// 下载完成后删除令牌(可选)
298+
tokenMutex.Lock()
299+
delete(downloadTokens, token)
300+
tokenMutex.Unlock()
301+
})
302+
160303
// 删除文件或目录
161304
r.POST("/api/ftp/delete", func(c *gin.Context) {
162305
var req struct {

app/connect/ssh/gin/version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
package gin
66

77
// Version is the current gin framework's version.
8-
const Version = "v1.9.1"
8+
const Version = "v1.9.3"

app/controller/base.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package controller
33
import (
44
"EasyTools/app/util"
55
"context"
6-
76
"gorm.io/gorm"
87
)
98

app/controller/checkUpdata.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type Update struct {
1717
type GitHubRelease struct {
1818
TagName string `json:"tag_name"`
1919
HTMLURL string `json:"html_url"`
20+
Body string `json:"body"` // 添加更新内容字段
2021
}
2122

2223
type CheckResult struct {
@@ -32,7 +33,7 @@ func CheckVersion() *Update {
3233
func (u *Update) GetLatestRelease() (*CheckResult, error) {
3334
owner := "doki-byte"
3435
repo := "EasyTools"
35-
currentVersion := "v1.9.2" // 请确保与前端保持一致
36+
currentVersion := "v1.9.3" // 请确保与前端保持一致
3637

3738
latest, err := CheckLatestRelease(owner, repo)
3839
if err != nil {
@@ -73,18 +74,23 @@ func CheckLatestRelease(owner, repo string) (*GitHubRelease, error) {
7374
}
7475

7576
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36")
77+
req.Header.Set("Accept", "application/vnd.github.v3+json")
7678

7779
resp, err := client.Do(req)
7880
if err != nil {
7981
return nil, fmt.Errorf("请求失败: %s", err)
8082
}
8183
defer resp.Body.Close()
8284

85+
if resp.StatusCode != http.StatusOK {
86+
return nil, fmt.Errorf("GitHub API 返回错误状态码: %d", resp.StatusCode)
87+
}
88+
8389
var release GitHubRelease
8490
if err := json.NewDecoder(resp.Body).Decode(&release); err != nil {
8591
return nil, fmt.Errorf("解析响应失败: %s", err)
8692
}
8793

88-
fmt.Println("最新版本信息:", release)
94+
fmt.Printf("最新版本信息: %s - %s\n", release.TagName, release.HTMLURL)
8995
return &release, nil
9096
}

app/controller/jwtcrack.go

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"errors"
1414
"fmt"
1515
"os"
16+
"path/filepath"
1617
"strings"
1718
"time"
1819

@@ -208,8 +209,6 @@ func (j *JwtCrackController) BruteForceJWT(tokenStr string, alg string, filepath
208209
}
209210
}
210211

211-
var lastErr error
212-
213212
for _, key := range keyList {
214213
token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
215214
if token.Method.Alg() != alg {
@@ -218,7 +217,7 @@ func (j *JwtCrackController) BruteForceJWT(tokenStr string, alg string, filepath
218217
return []byte(key), nil
219218
})
220219
if err != nil {
221-
lastErr = err
220+
_ = err
222221
continue
223222
}
224223

@@ -243,7 +242,7 @@ func (j *JwtCrackController) BruteForceJWT(tokenStr string, alg string, filepath
243242

244243
// 所有密钥都尝试失败
245244
return JwtResult{
246-
Error: "所有密钥尝试失败: " + lastErr.Error()}
245+
Error: "所有密钥尝试失败"}
247246
}
248247

249248
// 从文件读取密钥列表
@@ -454,3 +453,41 @@ func loadEd25519PublicKeyFromPEM(path string) (ed25519.PublicKey, error) {
454453

455454
return edKey, nil
456455
}
456+
457+
// 获取默认字典路径
458+
func (j *JwtCrackController) GetDefaultDictPath() string {
459+
appPath := j.getAppPath()
460+
defaultDictPath := filepath.Join(appPath, "tools", "dict", "jwtdict.txt")
461+
462+
// 检查文件是否存在
463+
if _, err := os.Stat(defaultDictPath); os.IsNotExist(err) {
464+
// 如果文件不存在,创建目录和默认字典文件
465+
configDir := filepath.Dir(defaultDictPath)
466+
if err := os.MkdirAll(configDir, 0755); err != nil {
467+
return ""
468+
}
469+
470+
// 创建默认字典文件,包含一些常见密钥
471+
defaultDictContent := `secret
472+
key
473+
password
474+
123456
475+
admin
476+
token
477+
jwt
478+
abcdefghijklmnopqrstuvwxyz
479+
ABCDEFGHIJKLMNOPQRSTUVWXYZ
480+
0123456789
481+
supersecret
482+
verysecret
483+
muchsecret
484+
wowsecret
485+
suchsecret`
486+
487+
if err := os.WriteFile(defaultDictPath, []byte(defaultDictContent), 0644); err != nil {
488+
return ""
489+
}
490+
}
491+
492+
return defaultDictPath
493+
}

app/controller/run.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"EasyTools/app/connect/redis"
55
hotkey2 "EasyTools/app/hotkey"
66
"EasyTools/app/model"
7-
proxy "EasyTools/app/proxy/client"
7+
"EasyTools/app/proxy/client"
88
"EasyTools/app/restmate"
99
"context"
1010
"embed"
@@ -44,13 +44,17 @@ func WailsRun(assets embed.FS, port int, appIcon, systemTrayIcon []byte) {
4444
// 启动 Wails 服务
4545
err := wails.Run(&options.App{
4646
Title: "EasyTools:一款实用的渗透测试工具箱 ",
47-
Width: 1200,
48-
Height: 800,
47+
Width: 1220,
48+
Height: 850,
4949
AssetServer: &assetserver.Options{
5050
Assets: assets,
5151
},
5252
Frameless: false,
5353
BackgroundColour: &options.RGBA{R: 255, G: 255, B: 255, A: 1},
54+
OnBeforeClose: func(ctx context.Context) (prevent bool) {
55+
runtime.EventsEmit(ctx, "app-exit")
56+
return true
57+
},
5458
OnStartup: func(ctx context.Context) {
5559
// 设置 context 对象
5660
system.setCtx(ctx)
@@ -74,11 +78,11 @@ func WailsRun(assets embed.FS, port int, appIcon, systemTrayIcon []byte) {
7478
server.start(port)
7579
if runtime2.GOOS == "windows" {
7680
// 优先初始化数据库表结构
77-
server.schema(&model.User{}, &model.Sites{}, &model.Tools{}, &model.Password_data{}, &model.Google_query{}, &model.Antivirus_list{})
81+
server.schema(&model.User{}, &model.Sites{}, &model.Tools{}, &model.Password_data{}, &model.Google_query{}, &model.Antivirus_list{}, &model.WechatConfig{}, &model.MiniAppInfo{}, &model.VersionTask{})
7882
// 异步执行文件释放(防止阻塞主流程)
7983
go server.initFile().initMianSha()
8084
} else {
81-
server.schema(&model.User{}, &model.Sites{}, &model.Tools{}, &model.Password_data{}, &model.Google_query{}, &model.Antivirus_list{})
85+
server.schema(&model.User{}, &model.Sites{}, &model.Tools{}, &model.Password_data{}, &model.Google_query{}, &model.Antivirus_list{}, &model.WechatConfig{}, &model.MiniAppInfo{}, &model.VersionTask{})
8286
go server.initFile()
8387
}
8488
// 启动监控协程
@@ -97,7 +101,7 @@ func WailsRun(assets embed.FS, port int, appIcon, systemTrayIcon []byte) {
97101
memTotal := system.GetMemUsageTotal() // 实现获取总内存使用的方法
98102

99103
// 格式化标题
100-
newTitle := fmt.Sprintf("EasyTools:一款实用的渗透测试工具箱 v1.9.2 CPU: %.2f%% | 自身: %.2f MB | 内存: %.2f%%",
104+
newTitle := fmt.Sprintf("EasyTools:一款实用的渗透测试工具箱 v1.9.3 CPU: %.2f%% | 自身: %.2f MB | 内存: %.2f%%",
101105
cpuUsage, memSelf, memTotal)
102106

103107
// 更新窗口标题
@@ -112,6 +116,7 @@ func WailsRun(assets embed.FS, port int, appIcon, systemTrayIcon []byte) {
112116
WebviewIsTransparent: false,
113117
WindowIsTranslucent: false,
114118
DisableFramelessWindowDecorations: true,
119+
Theme: windows.Light,
115120
},
116121
Mac: &mac.Options{
117122
TitleBar: mac.TitleBarDefault(),

0 commit comments

Comments
 (0)