package main
import (
"bytes"
"compress/flate"
"encoding/base64"
"fmt"
"io"
"net/http"
"regexp"
"strconv"
"strings"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.LoadHTMLGlob("templates/*")
r.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", nil)
})
r.POST("/decrypt", func(c *gin.Context) {
file, err := c.FormFile("phpfile")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "文件上传失败: " + err.Error()})
return
}
f, err := file.Open()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "无法打开文件"})
return
}
defer f.Close()
content, err := io.ReadAll(f)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "读取文件失败"})
return
}
decrypted, mode, err := decryptXBphp(content)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "解密失败: " + err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"decrypted": decrypted,
"mode": mode,
})
})
fmt.Println("服务启动在 http://localhost:8080")
r.Run(":8080")
}
func decryptXBphp(content []byte) (string, string, error) {
contentStr := string(content)
// 检测模式: Mode2 包含 gzinflate 和中文混淆
if strings.Contains(contentStr, "gzinflate") && strings.Contains(contentStr, "explode") {
return decryptMode2(content)
}
// Mode1: 简单模式
return decryptMode1(contentStr)
}
// Mode1: 简单XBphp格式 (十进制数组 + 直接base64)
func decryptMode1(content string) (string, string, error) {
// 提取数组 $o
o, err := extractOArray(content)
if err != nil {
return "", "", err
}
// 生成密钥
key := generateKey(o)
// 提取base64加密数据
ePattern := regexp.MustCompile(`base64_decode\s*\(\s*"([^"]+)"\s*\)`)
eMatches := ePattern.FindAllStringSubmatch(content, -1)
if len(eMatches) == 0 {
return "", "", fmt.Errorf("未找到加密数据")
}
encryptedData := eMatches[0][1]
// 解密流程: base64解码 -> XOR解密 -> base64解码
decoded, err := base64.StdEncoding.DecodeString(encryptedData)
if err != nil {
return "", "", fmt.Errorf("base64解码失败: %v", err)
}
xored := xorDecrypt(decoded, []byte(key))
// 尝试再次base64解码
finalDecoded, err := base64.StdEncoding.DecodeString(string(xored))
if err != nil {
return string(xored), "Mode1", nil
}
return string(finalDecoded), "Mode1", nil
}
// Mode2: 混淆XBphp格式 (十六进制 + gzinflate + 中文字符)
func decryptMode2(content []byte) (string, string, error) {
contentStr := string(content)
// 提取数组 $o (支持十六进制)
o, err := extractOArray(contentStr)
if err != nil {
return "", "", err
}
// 生成密钥
key := generateKey(o)
// 从gzinflate压缩数据中提取函数列表
var encryptedData string
funcList, extractErr := extractGzInflateDataWithError(content)
if extractErr != nil {
return "", "", fmt.Errorf("提取gzinflate数据失败: %v", extractErr)
}
if len(funcList) > 6 {
encryptedData = funcList[6]
}
if encryptedData == "" {
return "", "", fmt.Errorf("函数列表元素不足 (got %d, need >6)", len(funcList))
}
// 解密
decoded, err := base64.StdEncoding.DecodeString(encryptedData)
if err != nil {
return "", "", fmt.Errorf("base64解码失败: %v", err)
}
xored := xorDecrypt(decoded, []byte(key))
finalDecoded, err := base64.StdEncoding.DecodeString(string(xored))
if err != nil {
return string(xored), "Mode2", nil
}
return string(finalDecoded), "Mode2", nil
}
// 从PHP文件中提取gzinflate压缩的函数列表
func extractGzInflateDataWithError(content []byte) ([]string, error) {
// 方法1: 通过 ))); 找到 gzinflate 结束位置,然后解析参数
closePattern := regexp.MustCompile('\s*,\s*(0x[0-9a-fA-F]+|\d+)\s*,\s*(-?\d+)\s*\)\s*\)\s*\))
closeMatch := closePattern.FindSubmatchIndex(content)
if closeMatch == nil {
return nil, fmt.Errorf("未找到 gzinflate 结束模式")
}
// closeMatch[0] 是匹配开始位置 (即 ' 的位置)
dataEnd := closeMatch[0]
// 查找 substr(' 开始位置
substrStart := bytes.LastIndex(content[:dataEnd], []byte("substr('"))
if substrStart == -1 {
return nil, fmt.Errorf("未找到 substr(' 标记")
}
dataStart := substrStart + 8 // len("substr('")
// 提取原始二进制数据
rawData := content[dataStart:dataEnd]
// 处理PHP单引号字符串转义 (\' -> ' 和 \\ -> \)
rawData = unescapePhpSingleQuote(rawData)
// 提取offset和length参数
offset := 0
length := 0
if closeMatch[2] != -1 && closeMatch[3] != -1 {
offset = parseNumber(string(content[closeMatch[2]:closeMatch[3]]))
}
if closeMatch[4] != -1 && closeMatch[5] != -1 {
length = parseNumber(string(content[closeMatch[4]:closeMatch[5]]))
}
// 应用 substr 参数
originalLen := len(rawData)
if offset > 0 && offset < len(rawData) {
rawData = rawData[offset:]
}
if length < 0 && len(rawData)+length > 0 {
rawData = rawData[:len(rawData)+length]
}
// 尝试 deflate 解压
var decompressed []byte
var lastErr error
decompressed, lastErr = tryDeflate(rawData)
if lastErr != nil {
// 尝试跳过一些字节
for skip := 1; skip <= 50 && skip < len(rawData); skip++ {
if d, e := tryDeflate(rawData[skip:]); e == nil {
decompressed = d
lastErr = nil
break
}
}
}
if decompressed == nil || len(decompressed) < 10 {
// 显示数据的前几个字节用于调试
preview := rawData
if len(preview) > 20 {
preview = preview[:20]
}
return nil, fmt.Errorf("deflate解压失败 (原始:%d, offset:%d, len:%d, 处理后:%d, 前20字节:%x, err:%v)",
originalLen, offset, length, len(rawData), preview, lastErr)
}
// 尝试多种分隔符分割
decompStr := string(decompressed)
var parts []string
// 尝试不同的分隔符
separators := []string{"|||", "|\x02|\x05|\x02", "\x02|\x05|\x02", "|\x02|"}
for _, sep := range separators {
parts = strings.Split(decompStr, sep)
if len(parts) > 6 {
break
}
}
// 如果还是不够,尝试用正则匹配
if len(parts) <= 6 {
// 按 | 分割后过滤空元素和控制字符
rawParts := strings.Split(decompStr, "|")
parts = nil
for _, p := range rawParts {
// 去掉控制字符
cleaned := strings.TrimFunc(p, func(r rune) bool {
return r < 32
})
if cleaned != "" {
parts = append(parts, cleaned)
}
}
}
if len(parts) <= 6 {
preview := decompStr
if len(preview) > 200 {
preview = preview[:200]
}
return nil, fmt.Errorf("解压数据格式错误 (parts:%d, 预览:%q)", len(parts), preview)
}
return parts, nil
}
// 处理PHP单引号字符串转义
func unescapePhpSingleQuote(data []byte) []byte {
var result []byte
for i := 0; i < len(data); i++ {
if data[i] == '\' && i+1 < len(data) {
next := data[i+1]
if next == '\'' || next == '\' {
result = append(result, next)
i++
continue
}
}
result = append(result, data[i])
}
return result
}
// 尝试deflate解压
func tryDeflate(data []byte) ([]byte, error) {
// 尝试raw deflate (使用flate包)
reader := flate.NewReader(bytes.NewReader(data))
defer reader.Close()
return io.ReadAll(reader)
}
// 提取$o数组,支持十进制和十六进制
func extractOArray(content string) ([]int, error) {
oPattern := regexp.MustCompile(\$o\s*=\s*array\s*\(([^)]+)\))
oMatch := oPattern.FindStringSubmatch(content)
if oMatch == nil {
return nil, fmt.Errorf("未找到加密数组 $o")
}
oValues := strings.Split(oMatch[1], ",")
var o []int
for _, v := range oValues {
v = strings.TrimSpace(v)
if v == "" {
continue
}
num := parseNumber(v)
o = append(o, num)
}
if len(o) == 0 {
return nil, fmt.Errorf("数组 $o 为空")
}
return o, nil
}
// 解析数字,支持十进制和十六进制
func parseNumber(s string) int {
s = strings.TrimSpace(s)
if strings.HasPrefix(s, "0x") || strings.HasPrefix(s, "0X") {
num, := strconv.ParseInt(s[2:], 16, 64)
return int(num)
}
num, := strconv.Atoi(s)
return num
}
// 生成XBphp密钥
func generateKey(o []int) string {
var key bytes.Buffer
for i := 0; i < len(o); i++ {
var ch byte
switch i % 3 {
case 0:
ch = byte((o[i] / 2) - 7)
case 1:
ch = byte((o[i] / 3) + 3)
case 2:
ch = byte(((o[i] + 1) / 4) - 5)
}
key.WriteByte(ch)
}
return key.String()
}
// XOR解密
func xorDecrypt(data []byte, key []byte) []byte {
result := make([]byte, len(data))
keyLen := len(key)
for i := 0; i < len(data); i++ {
result[i] = data[i] ^ key[i%keyLen]
}
return result
}