diff --git a/cmd/common.go b/cmd/common.go index 80a19f963..e334fc1fa 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -5,10 +5,19 @@ import ( "path/filepath" "strconv" + "github.com/OpenListTeam/OpenList/v4/internal/bootstrap" "github.com/OpenListTeam/OpenList/v4/pkg/utils" log "github.com/sirupsen/logrus" ) +func Init() { + bootstrap.Init() +} + +func Release() { + bootstrap.Release() +} + var pid = -1 var pidFile string diff --git a/drivers/123_open/driver.go b/drivers/123_open/driver.go index ac75e51d7..49c0737ce 100644 --- a/drivers/123_open/driver.go +++ b/drivers/123_open/driver.go @@ -2,6 +2,7 @@ package _123_open import ( "context" + "encoding/hex" "fmt" "strconv" "time" @@ -156,28 +157,147 @@ func (d *Open123) Remove(ctx context.Context, obj model.Obj) error { } func (d *Open123) Put(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) (model.Obj, error) { - // 1. 创建文件 + utils.Log.Debugf("╔══════════════════════════════════════════════════════════╗") + utils.Log.Debugf("║ [123网盘Put] 开始上传文件") + utils.Log.Debugf("╠══════════════════════════════════════════════════════════╣") + utils.Log.Debugf("║ 文件名: %s", file.GetName()) + utils.Log.Debugf("║ 文件大小: %d bytes", file.GetSize()) + utils.Log.Debugf("║ 目标目录ID: %s", dstDir.GetID()) + + // 1. 准备参数 // parentFileID 父目录id,上传到根目录时填写 0 parentFileId, err := strconv.ParseInt(dstDir.GetID(), 10, 64) if err != nil { + utils.Log.Errorf("║ ✗ 解析父目录ID失败: %v", err) + utils.Log.Debugf("╚══════════════════════════════════════════════════════════╝") return nil, fmt.Errorf("parse parentFileID error: %v", err) } + utils.Log.Debugf("║ 父目录ID(int64): %d", parentFileId) + + utils.Log.Debugf("╠══════════════════════════════════════════════════════════╣") + utils.Log.Debugf("║ [步骤1] 获取文件MD5(etag)") + // etag 文件md5 etag := file.GetHash().GetHash(utils.MD5) + utils.Log.Debugf("║ 从FileStreamer获取的MD5: '%s'", etag) + utils.Log.Debugf("║ MD5长度: %d", len(etag)) + utils.Log.Debugf("║ MD5.Width要求: %d", utils.MD5.Width) + utils.Log.Debugf("║ 长度检查: len(%d) < Width(%d) = %v", len(etag), utils.MD5.Width, len(etag) < utils.MD5.Width) + + // 验证etag是否是有效的十六进制字符串 + if etag != "" { + if _, err := hex.DecodeString(etag); err == nil { + utils.Log.Debugf("║ ✓ 当前MD5是有效的十六进制字符串") + } else { + utils.Log.Warnf("║ ✗ 当前MD5不是有效的十六进制: %v", err) + } + } + if len(etag) < utils.MD5.Width { - _, etag, err = stream.CacheFullAndHash(file, &up, utils.MD5) + utils.Log.Debugf("║ ⚠ MD5长度不足,需要流式计算真实MD5") + utils.Log.Debugf("║ 开始调用 stream.CacheFullAndHash...") + utils.Log.Debugf("║ 这将:") + utils.Log.Debugf("║ 1. 缓存整个文件到本地临时文件") + utils.Log.Debugf("║ 2. 同时计算文件的真实MD5") + utils.Log.Debugf("║ 3. 返回 hex.EncodeToString(md5Hash.Sum(nil))") + + cachedFile, newEtag, err := stream.CacheFullAndHash(file, &up, utils.MD5) if err != nil { + utils.Log.Errorf("║ ✗ 计算MD5失败: %v", err) + utils.Log.Debugf("╚══════════════════════════════════════════════════════════╝") return nil, err } + + utils.Log.Debugf("║ ✓ 计算完成") + utils.Log.Debugf("║ 旧MD5: '%s'", etag) + utils.Log.Debugf("║ 新MD5: '%s'", newEtag) + utils.Log.Debugf("║ 缓存文件: %v (类型: %T)", cachedFile != nil, cachedFile) + + // 验证新MD5格式 + if decoded, err := hex.DecodeString(newEtag); err == nil { + utils.Log.Debugf("║ ✓ 新MD5是有效的十六进制字符串 (decoded length: %d)", len(decoded)) + } else { + utils.Log.Errorf("║ ✗ 新MD5不是有效的十六进制: %v", err) + } + + etag = newEtag + utils.Log.Debugf("║ 使用新计算的MD5: '%s'", etag) + } else { + utils.Log.Debugf("║ ✓ MD5长度符合要求,直接使用") + utils.Log.Debugf("║ 当前MD5: '%s'", etag) + utils.Log.Warnf("║") + utils.Log.Warnf("║ ⚠⚠⚠ 关键问题:此MD5来自源网盘!⚠⚠⚠") + utils.Log.Warnf("║") + utils.Log.Warnf("║ 如果源是百度网盘:") + utils.Log.Warnf("║ 百度返回: 加密MD5 (如 'b0ea37602n80afe104da316baece0b3c')") + utils.Log.Warnf("║ 经过解密: 标准MD5 (如 'ae2b620eb1c972072765c6d305f9740c')") + utils.Log.Warnf("║ 但是!解密后的MD5 ≠ 文件真实MD5") + utils.Log.Warnf("║ 这只是百度的内部文件标识符") + utils.Log.Warnf("║") + utils.Log.Warnf("║ 后果:") + utils.Log.Warnf("║ 使用错误MD5上传到123网盘") + utils.Log.Warnf("║ → 123服务器计算文件真实MD5") + utils.Log.Warnf("║ → 发现与提交的MD5不匹配") + utils.Log.Warnf("║ → complete失败: '服务端的哈希和之前提交的不同'") + utils.Log.Warnf("║") + utils.Log.Warnf("║ 解决方案: 强制重新计算文件真实MD5") + utils.Log.Warnf("║") + + // 测试:强制重新计算真实MD5进行对比 + utils.Log.Warnf("║ [测试] 强制计算文件真实MD5进行对比...") + savedEtag := etag + cachedFile, realEtag, err := stream.CacheFullAndHash(file, &up, utils.MD5) + if err != nil { + utils.Log.Errorf("║ ✗ 计算真实MD5失败: %v", err) + } else { + utils.Log.Warnf("║ 对比结果:") + utils.Log.Warnf("║ 源网盘提供的MD5: %s", savedEtag) + utils.Log.Warnf("║ 文件真实MD5: %s", realEtag) + utils.Log.Warnf("║ 是否一致: %v", savedEtag == realEtag) + if savedEtag != realEtag { + utils.Log.Errorf("║") + utils.Log.Errorf("║ ✗✗✗ 确认:源网盘MD5是错误的!✗✗✗") + utils.Log.Errorf("║") + utils.Log.Errorf("║ 这就是导致上传失败的根本原因!") + utils.Log.Errorf("║ 必须使用真实计算的MD5: %s", realEtag) + utils.Log.Errorf("║") + etag = realEtag // 使用真实MD5 + } else { + utils.Log.Debugf("║ ✓ 源网盘MD5是正确的,可以直接使用") + etag = savedEtag + } + utils.Log.Debugf("║ 缓存文件: %v", cachedFile != nil) + } } + + utils.Log.Debugf("╠══════════════════════════════════════════════════════════╣") + utils.Log.Debugf("║ [步骤2] 调用create接口") + utils.Log.Debugf("║ 参数:") + utils.Log.Debugf("║ parentFileId: %d", parentFileId) + utils.Log.Debugf("║ filename: %s", file.GetName()) + utils.Log.Debugf("║ etag: %s", etag) + utils.Log.Debugf("║ size: %d", file.GetSize()) + utils.Log.Debugf("║ duplicate: 2") + utils.Log.Debugf("║ containDir: false") + createResp, err := d.create(parentFileId, file.GetName(), etag, file.GetSize(), 2, false) if err != nil { + utils.Log.Errorf("║ ✗ create调用失败: %v", err) + utils.Log.Debugf("╚══════════════════════════════════════════════════════════╝") return nil, err } + utils.Log.Debugf("║ ✓ create调用成功") + utils.Log.Debugf("║ 响应: Reuse=%v, FileID=%d, PreuploadID=%s", + createResp.Data.Reuse, createResp.Data.FileID, createResp.Data.PreuploadID) + // 是否秒传 if createResp.Data.Reuse { + utils.Log.Debugf("╠══════════════════════════════════════════════════════════╣") + utils.Log.Debugf("║ [秒传] 文件已存在,可以秒传") // 秒传成功才会返回正确的 FileID,否则为 0 if createResp.Data.FileID != 0 { + utils.Log.Debugf("║ ✓ 秒传成功!FileID: %d", createResp.Data.FileID) + utils.Log.Debugf("╚══════════════════════════════════════════════════════════╝") return File{ FileName: file.GetName(), Size: file.GetSize(), @@ -185,20 +305,48 @@ func (d *Open123) Put(ctx context.Context, dstDir model.Obj, file model.FileStre Type: 2, Etag: etag, }, nil + } else { + utils.Log.Warnf("║ ⚠ Reuse=true但FileID=0,继续正常上传流程") } + } else { + utils.Log.Debugf("║ 不能秒传,需要上传分片") } + utils.Log.Debugf("╠══════════════════════════════════════════════════════════╣") + utils.Log.Debugf("║ [步骤3] 上传分片") // 2. 上传分片 err = d.Upload(ctx, file, createResp, up) if err != nil { + utils.Log.Errorf("║ ✗ 上传分片失败: %v", err) + utils.Log.Debugf("╚══════════════════════════════════════════════════════════╝") return nil, err } + utils.Log.Debugf("║ ✓ 所有分片上传完成") + + utils.Log.Debugf("╠══════════════════════════════════════════════════════════╣") + utils.Log.Debugf("║ [步骤4] 等待complete确认 (最多60秒)") + utils.Log.Debugf("║ PreuploadID: %s", createResp.Data.PreuploadID) // 3. 上传完毕 - for range 60 { + for i := range 60 { uploadCompleteResp, err := d.complete(createResp.Data.PreuploadID) + + if i == 0 || i%5 == 0 { + utils.Log.Debugf("║ [轮询%d/60] 调用complete...", i+1) + } + + if err != nil { + utils.Log.Warnf("║ [轮询%d/60] complete返回错误: %v", i+1, err) + } else { + utils.Log.Debugf("║ [轮询%d/60] 响应: Completed=%v, FileID=%d", + i+1, uploadCompleteResp.Data.Completed, uploadCompleteResp.Data.FileID) + } + // 返回错误代码未知,如:20103,文档也没有具体说 if err == nil && uploadCompleteResp.Data.Completed && uploadCompleteResp.Data.FileID != 0 { + utils.Log.Debugf("║ ✓ 上传完成确认成功!") + utils.Log.Debugf("║ 最终FileID: %d", uploadCompleteResp.Data.FileID) + utils.Log.Debugf("╚══════════════════════════════════════════════════════════╝") up(100) return File{ FileName: file.GetName(), @@ -208,9 +356,13 @@ func (d *Open123) Put(ctx context.Context, dstDir model.Obj, file model.FileStre Etag: etag, }, nil } + // 若接口返回的completed为 false 时,则需间隔1秒继续轮询此接口,获取上传最终结果。 time.Sleep(time.Second) } + + utils.Log.Errorf("║ ✗ complete超时!60秒后仍未确认完成") + utils.Log.Debugf("╚══════════════════════════════════════════════════════════╝") return nil, fmt.Errorf("upload complete timeout") } diff --git a/drivers/123_open/upload.go b/drivers/123_open/upload.go index 9bfc081a7..72c63e7bf 100644 --- a/drivers/123_open/upload.go +++ b/drivers/123_open/upload.go @@ -24,20 +24,50 @@ import ( // 创建文件 V2 func (d *Open123) create(parentFileID int64, filename string, etag string, size int64, duplicate int, containDir bool) (*UploadCreateResp, error) { + utils.Log.Debugf(" ┌─ [create API] 调用开始 ─────────────────────────┐") + utils.Log.Debugf(" │ URL: %s", UploadCreate) + + lowerEtag := strings.ToLower(etag) + utils.Log.Debugf(" │ 请求参数:") + utils.Log.Debugf(" │ parentFileId: %d", parentFileID) + utils.Log.Debugf(" │ filename: %s", filename) + utils.Log.Debugf(" │ etag(原始): %s", etag) + utils.Log.Debugf(" │ etag(小写): %s", lowerEtag) + utils.Log.Debugf(" │ size: %d", size) + utils.Log.Debugf(" │ duplicate: %d", duplicate) + utils.Log.Debugf(" │ containDir: %v", containDir) + var resp UploadCreateResp _, err := d.Request(UploadCreate, http.MethodPost, func(req *resty.Request) { req.SetBody(base.Json{ "parentFileId": parentFileID, "filename": filename, - "etag": strings.ToLower(etag), + "etag": lowerEtag, "size": size, "duplicate": duplicate, "containDir": containDir, }) }, &resp) + if err != nil { + utils.Log.Errorf(" │ ✗ API调用失败: %v", err) + utils.Log.Debugf(" └───────────────────────────────────────────────┘") return nil, err } + + utils.Log.Debugf(" │ ✓ API调用成功") + utils.Log.Debugf(" │ 响应:") + utils.Log.Debugf(" │ Code: %d", resp.Code) + utils.Log.Debugf(" │ Message: %s", resp.Message) + utils.Log.Debugf(" │ Data.Reuse: %v", resp.Data.Reuse) + utils.Log.Debugf(" │ Data.FileID: %d", resp.Data.FileID) + utils.Log.Debugf(" │ Data.PreuploadID: %s", resp.Data.PreuploadID) + utils.Log.Debugf(" │ Data.SliceSize: %d", resp.Data.SliceSize) + if len(resp.Data.Servers) > 0 { + utils.Log.Debugf(" │ Data.Servers[0]: %s", resp.Data.Servers[0]) + } + utils.Log.Debugf(" └───────────────────────────────────────────────┘") + return &resp, nil } @@ -168,14 +198,30 @@ func (d *Open123) Upload(ctx context.Context, file model.FileStreamer, createRes // 上传完毕 func (d *Open123) complete(preuploadID string) (*UploadCompleteResp, error) { + utils.Log.Debugf(" ┌─ [complete API] 调用 ────────────────────────┐") + utils.Log.Debugf(" │ URL: %s", UploadComplete) + utils.Log.Debugf(" │ 请求参数:") + utils.Log.Debugf(" │ preuploadID: %s", preuploadID) + var resp UploadCompleteResp _, err := d.Request(UploadComplete, http.MethodPost, func(req *resty.Request) { req.SetBody(base.Json{ "preuploadID": preuploadID, }) }, &resp) + if err != nil { + utils.Log.Debugf(" │ ✗ API调用失败: %v", err) + utils.Log.Debugf(" └──────────────────────────────────────────────┘") return nil, err } + + utils.Log.Debugf(" │ ✓ API响应:") + utils.Log.Debugf(" │ Code: %d", resp.Code) + utils.Log.Debugf(" │ Message: %s", resp.Message) + utils.Log.Debugf(" │ Data.Completed: %v", resp.Data.Completed) + utils.Log.Debugf(" │ Data.FileID: %d", resp.Data.FileID) + utils.Log.Debugf(" └──────────────────────────────────────────────┘") + return &resp, nil } diff --git a/drivers/baidu_netdisk/types.go b/drivers/baidu_netdisk/types.go index 03e84b396..da90a35df 100644 --- a/drivers/baidu_netdisk/types.go +++ b/drivers/baidu_netdisk/types.go @@ -67,6 +67,114 @@ func fileToObj(f File) *model.ObjThumb { if f.ServerMtime == 0 { f.ServerMtime = f.Mtime } + + // ========== DEBUG: MD5处理流程 ========== + utils.Log.Debugf("╔══════════════════════════════════════════════════════════╗") + utils.Log.Debugf("║ [百度网盘] 文件对象转换开始") + utils.Log.Debugf("╠══════════════════════════════════════════════════════════╣") + utils.Log.Debugf("║ 文件名: %s", f.ServerFilename) + utils.Log.Debugf("║ 文件ID: %d", f.FsId) + utils.Log.Debugf("║ 文件大小: %d bytes", f.Size) + utils.Log.Debugf("║ 是否目录: %v", f.Isdir == 1) + utils.Log.Debugf("╠══════════════════════════════════════════════════════════╣") + + // 记录百度API返回的原始MD5 + originalMd5 := f.Md5 + utils.Log.Debugf("║ [步骤1] 百度API返回的原始MD5") + utils.Log.Debugf("║ 值: '%s'", originalMd5) + utils.Log.Debugf("║ 长度: %d", len(originalMd5)) + utils.Log.Debugf("║ 是否为空: %v", originalMd5 == "") + + // 打印MD5的字节表示 + if originalMd5 != "" { + utils.Log.Debugf("║ 字节值: %v", []byte(originalMd5)) + // 检查是否包含非十六进制字符 + hasNonHex := false + for i, ch := range originalMd5 { + if !((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) { + hasNonHex = true + utils.Log.Warnf("║ ⚠ 位置%d包含非十六进制字符: '%c' (ASCII:%d)", i, ch, ch) + } + } + if !hasNonHex { + utils.Log.Debugf("║ ✓ 所有字符都是有效的十六进制字符") + } + } + + utils.Log.Debugf("╠══════════════════════════════════════════════════════════╣") + utils.Log.Debugf("║ [步骤2] 调用DecryptMd5进行处理") + utils.Log.Debugf("║ 注释说明: '直接获取的MD5是错误的'") + utils.Log.Debugf("║ 问题: 百度返回的MD5格式如 '1261d72d03471f7b7b805fd60e024b8d'") + utils.Log.Debugf("║ 问题: 这已经是标准的32位十六进制,为何说是错误的?") + + // 执行解密 + decryptedMd5 := DecryptMd5(f.Md5) + + utils.Log.Debugf("╠══════════════════════════════════════════════════════════╣") + utils.Log.Debugf("║ [步骤3] DecryptMd5处理结果") + utils.Log.Debugf("║ 原始MD5: '%s'", originalMd5) + utils.Log.Debugf("║ 处理后MD5: '%s'", decryptedMd5) + utils.Log.Debugf("║ 是否相同: %v", originalMd5 == decryptedMd5) + + if originalMd5 != decryptedMd5 { + utils.Log.Warnf("║ ⚠ MD5被修改了!") + utils.Log.Warnf("║ 这意味着执行了解密/转换操作") + + // 测试备用加密方法:使用解密后的MD5加密,看是否能还原成百度的格式 + utils.Log.Debugf("╠══════════════════════════════════════════════════════════╣") + utils.Log.Debugf("║ [验证] 使用encMd5测试逆向过程") + utils.Log.Debugf("║ 假设: encMd5(解密后MD5) 应该 = 百度加密MD5") + reEncrypted := encMd5(decryptedMd5) + utils.Log.Debugf("║ encMd5('%s') = '%s'", decryptedMd5, reEncrypted) + utils.Log.Debugf("║ 是否匹配百度原始: %v", reEncrypted == originalMd5) + if reEncrypted == originalMd5 { + utils.Log.Debugf("║ ✓ 完美匹配!解密算法正确") + } else { + utils.Log.Warnf("║ ✗ 不匹配!") + utils.Log.Warnf("║ 百度原始: %s", originalMd5) + utils.Log.Warnf("║ 重新加密: %s", reEncrypted) + } + } else { + utils.Log.Debugf("║ ✓ MD5未被修改(直接返回原值)") + utils.Log.Debugf("║ 这意味着百度返回的就是标准格式") + } + + utils.Log.Debugf("╠══════════════════════════════════════════════════════════╣") + utils.Log.Debugf("║ [步骤4] 创建HashInfo对象") + + // 创建HashInfo + hashInfo := utils.NewHashInfo(utils.MD5, decryptedMd5) + + utils.Log.Debugf("║ 已将MD5传入HashInfo") + utils.Log.Debugf("╠══════════════════════════════════════════════════════════╣") + utils.Log.Debugf("║ [关键问题分析]") + utils.Log.Debugf("║ 1. 百度API现在返回的MD5格式是什么?") + utils.Log.Debugf("║ - 如果是标准32位hex: DecryptMd5不应该做任何处理") + utils.Log.Debugf("║ - 如果是加密格式: DecryptMd5需要解密") + utils.Log.Debugf("║ 2. '直接获取的MD5是错误的'这个注释是否过时?") + utils.Log.Debugf("║ - 可能百度以前返回加密MD5,现在已经改为标准格式") + utils.Log.Debugf("║ 3. 解密后的MD5是否是文件的真实MD5?") + utils.Log.Debugf("║ - 这是核心问题!") + utils.Log.Debugf("║ - 如果解密后MD5 ≠ 文件真实MD5:") + utils.Log.Debugf("║ → 上传到123网盘时MD5校验会失败") + utils.Log.Debugf("║ → 必须重新计算文件真实MD5") + utils.Log.Debugf("║ - 如果解密后MD5 = 文件真实MD5:") + utils.Log.Debugf("║ → 可以直接使用,秒传成功") + utils.Log.Debugf("║ 4. 百度的MD5加密/解密可能只是防爬虫手段") + utils.Log.Debugf("║ - 解密后的MD5可能仍然不是文件真实MD5") + utils.Log.Debugf("║ - 只是百度内部的文件标识符") + utils.Log.Debugf("║ 5. 如果Etag验证失败,可能的原因:") + utils.Log.Debugf("║ - MD5本身格式正确,但不是文件真实MD5") + utils.Log.Debugf("║ - 客户端对Etag格式有特殊要求") + utils.Log.Debugf("║ - Etag的引号、大小写等格式问题") + utils.Log.Debugf("║") + utils.Log.Debugf("║ [建议]") + utils.Log.Debugf("║ 从百度网盘复制到其他网盘时:") + utils.Log.Debugf("║ - 不要信任百度提供的MD5(无论是否解密)") + utils.Log.Debugf("║ - 强制重新计算文件的真实MD5") + utils.Log.Debugf("║ - 或者在目标网盘驱动中添加MD5验证逻辑") + utils.Log.Debugf("╚══════════════════════════════════════════════════════════╝") + return &model.ObjThumb{ Object: model.Object{ ID: strconv.FormatInt(f.FsId, 10), @@ -78,7 +186,7 @@ func fileToObj(f File) *model.ObjThumb { IsFolder: f.Isdir == 1, // 直接获取的MD5是错误的 - HashInfo: utils.NewHashInfo(utils.MD5, DecryptMd5(f.Md5)), + HashInfo: hashInfo, }, Thumbnail: model.Thumbnail{Thumbnail: f.Thumbs.Url3}, } diff --git a/drivers/baidu_netdisk/util.go b/drivers/baidu_netdisk/util.go index 8f55911d1..d2039832f 100644 --- a/drivers/baidu_netdisk/util.go +++ b/drivers/baidu_netdisk/util.go @@ -446,23 +446,142 @@ func (d *BaiduNetdisk) requestForUploadUrl(path, uploadId string) (string, error // } func DecryptMd5(encryptMd5 string) string { + utils.Log.Debugf("┌─────────────────────────────────────────────────────────┐") + utils.Log.Debugf("│ [DecryptMd5] 开始处理") + utils.Log.Debugf("│ 输入: '%s' (长度:%d)", encryptMd5, len(encryptMd5)) + + if encryptMd5 == "" { + utils.Log.Debugf("│ 输入为空,直接返回") + utils.Log.Debugf("└─────────────────────────────────────────────────────────┘") + return encryptMd5 + } + + // 检查是否已经是有效的十六进制 if _, err := hex.DecodeString(encryptMd5); err == nil { + utils.Log.Debugf("│ ✓ 已是有效的32位十六进制MD5,无需解密") + utils.Log.Debugf("│ 输出: '%s'", encryptMd5) + utils.Log.Debugf("└─────────────────────────────────────────────────────────┘") return encryptMd5 + } else { + utils.Log.Debugf("│ ✗ 不是有效十六进制: %v", err) + utils.Log.Debugf("│ 需要执行解密操作") } + utils.Log.Debugf("│") + utils.Log.Debugf("│ [步骤1] XOR解密") + var out strings.Builder out.Grow(len(encryptMd5)) for i, n := 0, int64(0); i < len(encryptMd5); i++ { if i == 9 { + // 特殊处理:第9位字符减去'g' n = int64(unicode.ToLower(rune(encryptMd5[i])) - 'g') + utils.Log.Debugf("│ 位置%d: '%c' -> toLower('%c') - 'g' = %d", + i, encryptMd5[i], unicode.ToLower(rune(encryptMd5[i])), n) } else { n, _ = strconv.ParseInt(encryptMd5[i:i+1], 16, 64) } - out.WriteString(strconv.FormatInt(n^int64(15&i), 16)) + xorResult := n ^ int64(15&i) + out.WriteString(strconv.FormatInt(xorResult, 16)) + } + + xorOutput := out.String() + utils.Log.Debugf("│ XOR结果: '%s'", xorOutput) + + utils.Log.Debugf("│") + utils.Log.Debugf("│ [步骤2] 位置交换") + utils.Log.Debugf("│ [8:16] (8个字符): '%s'", xorOutput[8:16]) + utils.Log.Debugf("│ [0:8] (8个字符): '%s'", xorOutput[:8]) + utils.Log.Debugf("│ [24:32] (8个字符): '%s'", xorOutput[24:32]) + utils.Log.Debugf("│ [16:24] (8个字符): '%s'", xorOutput[16:24]) + + finalMd5 := xorOutput[8:16] + xorOutput[:8] + xorOutput[24:32] + xorOutput[16:24] + + utils.Log.Debugf("│") + utils.Log.Debugf("│ [最终结果]") + utils.Log.Debugf("│ 输出: '%s' (长度:%d)", finalMd5, len(finalMd5)) + + // 验证最终结果 + if _, err := hex.DecodeString(finalMd5); err == nil { + utils.Log.Debugf("│ ✓ 解密成功!输出是有效的32位十六进制MD5") + } else { + utils.Log.Warnf("│ ✗ 解密结果不是有效的十六进制: %v", err) } - encryptMd5 = out.String() - return encryptMd5[8:16] + encryptMd5[:8] + encryptMd5[24:32] + encryptMd5[16:24] + utils.Log.Debugf("└─────────────────────────────────────────────────────────┘") + + return finalMd5 +} + +// encMd5 备用加密方法(基于Java实现) +// 用于验证加密逻辑的另一种实现方式 +func encMd5(md5 string) string { + utils.Log.Debugf(" ┌─ [encMd5备用方法] ──────────────────────────┐") + utils.Log.Debugf(" │ 输入MD5: %s", md5) + + // 位置交换 + temp := md5[8:16] + md5[0:8] + md5[24:32] + md5[16:24] + utils.Log.Debugf(" │ 位置交换后: %s", temp) + + var res strings.Builder + res.Grow(32) + + for i := 0; i < len(temp); i++ { + c := temp[i] + // Character.digit(c, 16) 等价于 strconv.ParseInt + digit, _ := strconv.ParseInt(string(c), 16, 64) + xorResult := digit ^ int64(15&i) + res.WriteString(strconv.FormatInt(xorResult, 16)) + } + + // 特殊处理第9位:将十六进制数字加上'g' + result := res.String() + utils.Log.Debugf(" │ XOR后: %s", result) + + if len(result) > 9 { + digit9, _ := strconv.ParseInt(string(result[9]), 16, 64) + char9 := rune(digit9 + 'g') + result = result[:9] + string(char9) + result[10:] + utils.Log.Debugf(" │ 替换位置9: '%c' (digit:%d + 'g')", char9, digit9) + } + + utils.Log.Debugf(" │ 最终输出: %s", result) + utils.Log.Debugf(" └────────────────────────────────────────────┘") + + return result +} + +// DecryptMd5Alternative 备用解密方法(使用encMd5逆向) +// 如果标准解密失败,可以尝试这个方法 +func DecryptMd5Alternative(encryptMd5 string) string { + utils.Log.Debugf("┌─────────────────────────────────────────────────────────┐") + utils.Log.Debugf("│ [DecryptMd5Alternative] 备用解密方法") + utils.Log.Debugf("│ 输入: '%s'", encryptMd5) + + if encryptMd5 == "" { + utils.Log.Debugf("│ 输入为空") + utils.Log.Debugf("└─────────────────────────────────────────────────────────┘") + return encryptMd5 + } + + // 检查是否已经是有效的十六进制 + if _, err := hex.DecodeString(encryptMd5); err == nil { + utils.Log.Debugf("│ 已是有效hex,直接返回") + utils.Log.Debugf("└─────────────────────────────────────────────────────────┘") + return encryptMd5 + } + + // 使用encMd5方法验证:尝试加密标准MD5,看是否能得到百度的加密格式 + // 这里我们假设输入是加密的,需要通过逆向得到原始MD5 + + utils.Log.Debugf("│ 尝试使用encMd5逆向解密...") + utils.Log.Debugf("│ 提示: encMd5(真实MD5) = 百度加密MD5") + utils.Log.Debugf("│ 当前我们有百度加密MD5,需要找到真实MD5") + utils.Log.Debugf("└─────────────────────────────────────────────────────────┘") + + // 注意:这只是演示,实际解密需要逆向encMd5的每一步 + // 当前仍使用标准解密方法 + return DecryptMd5(encryptMd5) } func EncryptMd5(originalMd5 string) string {