Skip to content

Commit d9293bd

Browse files
committed
feat: 添加敏感信息导出功能
1 parent f6c5c19 commit d9293bd

File tree

8 files changed

+214
-5
lines changed

8 files changed

+214
-5
lines changed

README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737
- [x] Hook小程序,动态调试,开启小程序F12
3838
- [x] 重新打包wxapkg,可破解小程序
3939
- [x] 监听将要打包的文件夹,并自动打包
40+
- [x] 敏感数据导出
4041
- [ ] 支持小游戏
41-
- [ ] 敏感数据导出
4242

4343
### 工程结构还原
4444

@@ -130,7 +130,7 @@
130130
## 用法
131131

132132
> -id=<输入AppID> -in=<输入文件1,输入文件2> 或 -in=<输入目录> -out=<输出目录>
133-
> [-ext=<文件后缀>] [-restore] [-pretty] [-noClean] [-help] [-hook] [-save] [-repack=<输入目录>] [-watch]
133+
> [-ext=<文件后缀>] [-restore] [-pretty] [-noClean] [-help] [-hook] [-save] [-repack=<输入目录>] [-watch] [-sensitive]
134134
135135
### 参数说明
136136
- `-id string`
@@ -165,6 +165,8 @@
165165
- **注意:目前仅支持一次打包一个文件,同时仅支持未被解析的源文件(未使用-restore)**
166166
- `-watch`
167167
- 是否监听将要打包的文件夹,并自动打包,默认不监听
168+
- `-sensitive`
169+
- 是否导出敏感数据,默认不导出
168170
- `-help`
169171
- 显示帮助信息
170172

cmd/root.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"github.com/Ackites/KillWxapkg/internal/restore"
1010
)
1111

12-
func Execute(appID, input, outputDir, fileExt string, restoreDir bool, pretty bool, noClean bool, save bool) {
12+
func Execute(appID, input, outputDir, fileExt string, restoreDir bool, pretty bool, noClean bool, save bool, sensitive bool) {
1313
// 存储配置
1414
configManager := NewSharedConfigManager()
1515
configManager.Set("appID", appID)
@@ -20,6 +20,7 @@ func Execute(appID, input, outputDir, fileExt string, restoreDir bool, pretty bo
2020
configManager.Set("pretty", pretty)
2121
configManager.Set("noClean", noClean)
2222
configManager.Set("save", save)
23+
configManager.Set("sensitive", sensitive)
2324

2425
inputFiles := ParseInput(input, fileExt)
2526

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ require (
1111
golang.org/x/crypto v0.26.0
1212
golang.org/x/net v0.28.0
1313
golang.org/x/text v0.17.0
14+
gopkg.in/yaml.v3 v3.0.1
1415
)
1516

1617
require (

go.sum

+4
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,9 @@ golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
2626
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
2727
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
2828
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
29+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
30+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
2931
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
3032
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
33+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
34+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

internal/key/key.go

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package key
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
8+
"gopkg.in/yaml.v3"
9+
)
10+
11+
type Rule struct {
12+
Id string `yaml:"id"`
13+
Enabled bool `yaml:"enabled"`
14+
Pattern string `yaml:"pattern"`
15+
}
16+
17+
type Rules struct {
18+
Rules []Rule `yaml:"rules"`
19+
}
20+
21+
func init() {
22+
configDir := "config"
23+
configFile := filepath.Join(configDir, "rule.yaml")
24+
25+
if _, err := os.Stat(configFile); os.IsNotExist(err) {
26+
if err := os.MkdirAll(configDir, 0755); err != nil {
27+
fmt.Printf("Error creating config directory: %v\n", err)
28+
return
29+
}
30+
CreateConfigFile()
31+
}
32+
}
33+
34+
func ReadRuleFile() (*Rules, error) {
35+
configFile := filepath.Join("config", "rule.yaml")
36+
file, err := os.ReadFile(configFile)
37+
if err != nil {
38+
return nil, fmt.Errorf("error reading rule file: %v", err)
39+
}
40+
41+
var rules Rules
42+
if err := yaml.Unmarshal(file, &rules); err != nil {
43+
return nil, fmt.Errorf("error unmarshalling rule file: %v", err)
44+
}
45+
46+
return &rules, nil
47+
}
48+
49+
func CreateConfigFile() {
50+
configFile := filepath.Join("config", "rule.yaml")
51+
defaultRules := Rules{
52+
Rules: []Rule{
53+
{Id: "domain", Enabled: false, Pattern: ""},
54+
{Id: "path", Enabled: false, Pattern: ""},
55+
{Id: "domain_url", Enabled: false, Pattern: ""},
56+
{Id: "ip", Enabled: false, Pattern: ""},
57+
{Id: "ip_url", Enabled: false, Pattern: `\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}`},
58+
{Id: "email", Enabled: true, Pattern: `\b[A-Za-z0-9._\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,61}\b`},
59+
{Id: "id_card", Enabled: true, Pattern: `\b([1-9]\d{5}(19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx])\b`},
60+
{Id: "phone", Enabled: true, Pattern: `\b1[3-9]\d{9}\b`},
61+
{Id: "jwt_token", Enabled: true, Pattern: `eyJ[A-Za-z0-9_/+\-]{10,}={0,2}\.[A-Za-z0-9_/+\-\\]{15,}={0,2}\.[A-Za-z0-9_/+\-\\]{10,}={0,2}`},
62+
{Id: "Aliyun_AK_ID", Enabled: true, Pattern: `\bLTAI[A-Za-z\d]{12,30}\b`},
63+
{Id: "QCloud_AK_ID", Enabled: true, Pattern: `\bAKID[A-Za-z\d]{13,40}\b`},
64+
{Id: "JDCloud_AK_ID", Enabled: true, Pattern: `\bJDC_[0-9A-Z]{25,40}\b`},
65+
{Id: "AWS_AK_ID", Enabled: true, Pattern: `["''](?:A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}["'']`},
66+
{Id: "VolcanoEngine_AK_ID", Enabled: true, Pattern: `\b(?:AKLT|AKTP)[a-zA-Z0-9]{35,50}\b`},
67+
{Id: "Kingsoft_AK_ID", Enabled: true, Pattern: `\bAKLT[a-zA-Z0-9-_]{16,28}\b`},
68+
{Id: "GCP_AK_ID", Enabled: true, Pattern: `\bAIza[0-9A-Za-z_\-]{35}\b`},
69+
{Id: "secret_key", Enabled: true, Pattern: ""},
70+
{Id: "bearer_token", Enabled: true, Pattern: `\b[Bb]earer\s+[a-zA-Z0-9\-=._+/\\]{20,500}\b`},
71+
{Id: "basic_token", Enabled: true, Pattern: `\b[Bb]asic\s+[A-Za-z0-9+/]{18,}={0,2}\b`},
72+
{Id: "auth_token", Enabled: true, Pattern: `["''\[]*[Aa]uthorization["''\]]*\s*[:=]\s*[''"]?\b(?:[Tt]oken\s+)?[a-zA-Z0-9\-_+/]{20,500}[''"]?`},
73+
{Id: "private_key", Enabled: true, Pattern: `-----\s*?BEGIN[ A-Z0-9_-]*?PRIVATE KEY\s*?-----[a-zA-Z0-9\/\n\r=+]*-----\s*?END[ A-Z0-9_-]*? PRIVATE KEY\s*?-----`},
74+
{Id: "gitlab_v2_token", Enabled: true, Pattern: `\b(glpat-[a-zA-Z0-9\-=_]{20,22})\b`},
75+
{Id: "github_token", Enabled: true, Pattern: `\b((?:ghp|gho|ghu|ghs|ghr|github_pat)_[a-zA-Z0-9_]{36,255})\b`},
76+
{Id: "qcloud_api_gateway_appkey", Enabled: true, Pattern: `\bAPID[a-zA-Z0-9]{32,42}\b`},
77+
{Id: "wechat_appid", Enabled: true, Pattern: `["''](wx[a-z0-9]{15,18})["'']`},
78+
{Id: "wechat_corpid", Enabled: true, Pattern: `["''](ww[a-z0-9]{15,18})["'']`},
79+
{Id: "wechat_id", Enabled: true, Pattern: `["''](gh_[a-z0-9]{11,13})["'']`},
80+
{Id: "password", Enabled: true, Pattern: `(?i)(?:admin_?pass|password|[a-z]{3,15}_?password|user_?pass|user_?pwd|admin_?pwd)\\?['"]*\s*[:=]\s*\\?['"][a-z0-9!@#$%&*]{5,50}\\?['"]`},
81+
{Id: "wechat_webhookurl", Enabled: true, Pattern: `\bhttps://qyapi.weixin.qq.com/cgi-bin/webhook/send\?key=[a-zA-Z0-9\-]{25,50}\b`},
82+
{Id: "dingtalk_webhookurl", Enabled: true, Pattern: `\bhttps://oapi.dingtalk.com/robot/send\?access_token=[a-z0-9]{50,80}\b`},
83+
{Id: "feishu_webhookurl", Enabled: true, Pattern: `\bhttps://open.feishu.cn/open-apis/bot/v2/hook/[a-z0-9\-]{25,50}\b`},
84+
{Id: "slack_webhookurl", Enabled: true, Pattern: `\bhttps://hooks.slack.com/services/[a-zA-Z0-9\-_]{6,12}/[a-zA-Z0-9\-_]{6,12}/[a-zA-Z0-9\-_]{15,24}\b`},
85+
{Id: "grafana_api_key", Enabled: true, Pattern: `\beyJrIjoi[a-zA-Z0-9\-_+/]{50,100}={0,2}\b`},
86+
{Id: "grafana_cloud_api_token", Enabled: true, Pattern: `\bglc_[A-Za-z0-9\-_+/]{32,200}={0,2}\b`},
87+
{Id: "grafana_service_account_token", Enabled: true, Pattern: `\bglsa_[A-Za-z0-9]{32}_[A-Fa-f0-9]{8}\b`},
88+
{Id: "app_key", Enabled: true, Pattern: `\b(?:VUE|APP|REACT)_[A-Z_0-9]{1,15}_(?:KEY|PASS|PASSWORD|TOKEN|APIKEY)['"]*[:=]"(?:[A-Za-z0-9_\-]{15,50}|[a-z0-9/+]{50,100}==?)"`},
89+
},
90+
}
91+
92+
data, err := yaml.Marshal(&defaultRules)
93+
if err != nil {
94+
fmt.Printf("Error marshalling default rules: %v\n", err)
95+
return
96+
}
97+
98+
if err := os.WriteFile(configFile, data, 0755); err != nil {
99+
fmt.Printf("Error writing default rule file: %v\n", err)
100+
}
101+
}

internal/key/match.go

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package key
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"os"
7+
"regexp"
8+
"strings"
9+
"sync"
10+
)
11+
12+
var (
13+
rulesInstance *Rules
14+
once sync.Once
15+
jsonMutex sync.Mutex
16+
)
17+
18+
func getRulesInstance() (*Rules, error) {
19+
var err error
20+
once.Do(func() {
21+
rulesInstance, err = ReadRuleFile()
22+
})
23+
return rulesInstance, err
24+
}
25+
26+
func MatchRules(input string) error {
27+
rules, err := getRulesInstance()
28+
if err != nil {
29+
return fmt.Errorf("%v", err)
30+
}
31+
32+
for _, rule := range rules.Rules {
33+
if rule.Enabled {
34+
re, err := regexp.Compile(rule.Pattern)
35+
if err != nil {
36+
return fmt.Errorf("failed to compile regex for rule %s: %v", rule.Id, err)
37+
}
38+
matches := re.FindAllStringSubmatch(input, -1)
39+
for _, match := range matches {
40+
if len(match) > 0 {
41+
if strings.TrimSpace(match[0]) == "" {
42+
continue
43+
}
44+
err := appendToJSON(rule.Id, match[0])
45+
if err != nil {
46+
return fmt.Errorf("failed to append to JSON: %v", err)
47+
}
48+
}
49+
}
50+
}
51+
}
52+
53+
return nil
54+
}
55+
56+
func appendToJSON(ruleId, matchedContent string) error {
57+
jsonMutex.Lock()
58+
defer jsonMutex.Unlock()
59+
60+
file, err := os.OpenFile("sensitive_data.json", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
61+
if err != nil {
62+
return fmt.Errorf("failed to open JSON file: %v", err)
63+
}
64+
defer func(file *os.File) {
65+
err := file.Close()
66+
if err != nil {
67+
fmt.Printf("failed to close JSON file: %v", err)
68+
}
69+
}(file)
70+
71+
record := map[string]string{
72+
"rule_id": ruleId,
73+
"content": matchedContent,
74+
}
75+
76+
encoder := json.NewEncoder(file)
77+
if err := encoder.Encode(record); err != nil {
78+
return fmt.Errorf("failed to write to JSON file: %v", err)
79+
}
80+
81+
return nil
82+
}

internal/unpack/unpack.go

+16
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import (
1010
"path/filepath"
1111
"sync"
1212

13+
"github.com/Ackites/KillWxapkg/internal/key"
14+
15+
"github.com/Ackites/KillWxapkg/internal/config"
16+
1317
formatter2 "github.com/Ackites/KillWxapkg/internal/formatter"
1418
)
1519

@@ -221,5 +225,17 @@ func processFile(outputDir string, file WxapkgFile, reader io.ReaderAt, bufferPo
221225
return fmt.Errorf("写入文件失败: %w", err)
222226
}
223227

228+
configManager := config.NewSharedConfigManager()
229+
if sensitive, ok := configManager.Get("sensitive"); ok {
230+
if p, o := sensitive.(bool); o {
231+
if p {
232+
// 查找敏感信息
233+
if err := key.MatchRules(string(content)); err != nil {
234+
return fmt.Errorf("%v", err)
235+
}
236+
}
237+
}
238+
}
239+
224240
return nil
225241
}

main.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ var (
2222
save bool
2323
repack string
2424
watch bool
25+
sensitive bool
2526
)
2627

2728
func init() {
@@ -36,6 +37,7 @@ func init() {
3637
flag.BoolVar(&save, "save", false, "是否保存解密后的文件")
3738
flag.StringVar(&repack, "repack", "", "重新打包wxapkg文件")
3839
flag.BoolVar(&watch, "watch", false, "是否监听将要打包的文件夹,并自动打包")
40+
flag.BoolVar(&sensitive, "sensitive", false, "是否获取敏感数据")
3941
}
4042

4143
func main() {
@@ -67,12 +69,12 @@ func main() {
6769
}
6870

6971
if appID == "" || input == "" {
70-
fmt.Println("使用方法: program -id=<AppID> -in=<输入文件1,输入文件2> 或 -in=<输入目录> -out=<输出目录> [-ext=<文件后缀>] [-restore] [-pretty] [-noClean] [-hook] [-save] [-repack=<输入目录>] [-watch]")
72+
fmt.Println("使用方法: program -id=<AppID> -in=<输入文件1,输入文件2> 或 -in=<输入目录> -out=<输出目录> [-ext=<文件后缀>] [-restore] [-pretty] [-noClean] [-hook] [-save] [-repack=<输入目录>] [-watch] [-sensitive]")
7173
flag.PrintDefaults()
7274
fmt.Println()
7375
return
7476
}
7577

7678
// 执行命令
77-
cmd.Execute(appID, input, outputDir, fileExt, restoreDir, pretty, noClean, save)
79+
cmd.Execute(appID, input, outputDir, fileExt, restoreDir, pretty, noClean, save, sensitive)
7880
}

0 commit comments

Comments
 (0)