diff --git a/README.md b/README.md
index 3332bef..5ddcf6d 100644
--- a/README.md
+++ b/README.md
@@ -149,9 +149,9 @@ GET /api/v1/chatlog?time=2023-01-01&talker=wxid_xxx
参数说明:
- `time`: 时间范围,格式为 `YYYY-MM-DD` 或 `YYYY-MM-DD~YYYY-MM-DD`
- `talker`: 聊天对象标识(支持 wxid、群聊 ID、备注名、昵称等)
-- `limit`: 返回记录数量(默认 100)
-- `offset`: 分页偏移量(默认 0)
-- `format`: 输出格式,支持 `json`、`csv` 或纯文本(默认 纯文本)
+- `limit`: 返回记录数量
+- `offset`: 分页偏移量
+- `format`: 输出格式,支持 `json`、`csv` 或纯文本
### 其他 API 接口
diff --git a/cmd/chatlog/cmd_server.go b/cmd/chatlog/cmd_server.go
new file mode 100644
index 0000000..c6c01d1
--- /dev/null
+++ b/cmd/chatlog/cmd_server.go
@@ -0,0 +1,43 @@
+package chatlog
+
+import (
+ "runtime"
+
+ "github.com/sjzar/chatlog/internal/chatlog"
+
+ "github.com/rs/zerolog/log"
+ "github.com/spf13/cobra"
+)
+
+func init() {
+ rootCmd.AddCommand(serverCmd)
+ serverCmd.Flags().StringVarP(&serverAddr, "addr", "a", "127.0.0.1:5030", "server address")
+ serverCmd.Flags().StringVarP(&serverDataDir, "data-dir", "d", "", "data dir")
+ serverCmd.Flags().StringVarP(&serverWorkDir, "work-dir", "w", "", "work dir")
+ serverCmd.Flags().StringVarP(&serverPlatform, "platform", "p", runtime.GOOS, "platform")
+ serverCmd.Flags().IntVarP(&serverVer, "version", "v", 3, "version")
+}
+
+var (
+ serverAddr string
+ serverDataDir string
+ serverWorkDir string
+ serverPlatform string
+ serverVer int
+)
+
+var serverCmd = &cobra.Command{
+ Use: "server",
+ Short: "Start HTTP server",
+ Run: func(cmd *cobra.Command, args []string) {
+ m, err := chatlog.New("")
+ if err != nil {
+ log.Err(err).Msg("failed to create chatlog instance")
+ return
+ }
+ if err := m.CommandHTTPServer(serverAddr, serverDataDir, serverWorkDir, serverPlatform, serverVer); err != nil {
+ log.Err(err).Msg("failed to start server")
+ return
+ }
+ },
+}
diff --git a/internal/chatlog/http/static/index.htm b/internal/chatlog/http/static/index.htm
index d0a071e..b8d05a1 100644
--- a/internal/chatlog/http/static/index.htm
+++ b/internal/chatlog/http/static/index.htm
@@ -1,156 +1,733 @@
-
-
-
-
-
- ('-. .-. ('-. .-') _
- ( OO ) / ( OO ).-. ( OO) )
- .-----. ,--. ,--. / . --. / / '._ ,--. .-'),-----. ,----.
- ' .--./ | | | | | \-. \ |'--...__) | |.-') ( OO' .-. ' ' .-./-')
- | |('-. | .| | .-'-' | | '--. .--' | | OO ) / | | | | | |_( O- )
- /_) |OO ) | | \| |_.' | | | | |`-' | \_) | |\| | | | .--, \
- || |`-'| | .-. | | .-. | | | (| '---.' \ | | | |(| | '. (_/
-(_' '--'\ | | | | | | | | | | | | `' '-' ' | '--' |
- `-----' `--' `--' `--' `--' `--' `------' `-----' `------'
-
-
- _____ _ _ _
- / __ \| | | | | |
- | / \/| |__ __ _ | |_ | | ___ __ _
- | | | '_ \ / _` || __|| | / _ \ / _` |
- | \__/\| | | || (_| || |_ | || (_) || (_| |
- \____/|_| |_| \__,_| \__||_| \___/ \__, |
- __/ |
- |___/
-
-
-
- ,-----. ,--. ,--. ,--.
- ' .--./ | ,---. ,--,--. ,-' '-. | | ,---. ,---.
- | | | .-. | ' ,-. | '-. .-' | | | .-. | | .-. |
- ' '--'\ | | | | \ '-' | | | | | ' '-' ' ' '-' '
- `-----' `--' `--' `--`--' `--' `--' `---' .`- /
- `---'
-
-
- ____ _ _ _
- / ___| | |__ __ _ | |_ | | ___ __ _
- | | | '_ \ / _` | | __| | | / _ \ / _` |
- | |___ | | | | | (_| | | |_ | | | (_) | | (_| |
- \____| |_| |_| \__,_| \__| |_| \___/ \__, |
- |___/
-
-
- _____ _____ _____ _____ _____ _______ _____
- /\ \ /\ \ /\ \ /\ \ /\ \ /::\ \ /\ \
- /::\ \ /::\____\ /::\ \ /::\ \ /::\____\ /::::\ \ /::\ \
- /::::\ \ /:::/ / /::::\ \ \:::\ \ /:::/ / /::::::\ \ /::::\ \
- /::::::\ \ /:::/ / /::::::\ \ \:::\ \ /:::/ / /::::::::\ \ /::::::\ \
- /:::/\:::\ \ /:::/ / /:::/\:::\ \ \:::\ \ /:::/ / /:::/~~\:::\ \ /:::/\:::\ \
- /:::/ \:::\ \ /:::/____/ /:::/__\:::\ \ \:::\ \ /:::/ / /:::/ \:::\ \ /:::/ \:::\ \
- /:::/ \:::\ \ /::::\ \ /::::\ \:::\ \ /::::\ \ /:::/ / /:::/ / \:::\ \ /:::/ \:::\ \
- /:::/ / \:::\ \ /::::::\ \ _____ /::::::\ \:::\ \ /::::::\ \ /:::/ / /:::/____/ \:::\____\ /:::/ / \:::\ \
- /:::/ / \:::\ \ /:::/\:::\ \ /\ \ /:::/\:::\ \:::\ \ /:::/\:::\ \ /:::/ / |:::| | |:::| | /:::/ / \:::\ ___\
- /:::/____/ \:::\____\/:::/ \:::\ /::\____\/:::/ \:::\ \:::\____\ /:::/ \:::\____\/:::/____/ |:::|____| |:::| |/:::/____/ ___\:::| |
- \:::\ \ \::/ /\::/ \:::\ /:::/ /\::/ \:::\ /:::/ / /:::/ \::/ /\:::\ \ \:::\ \ /:::/ / \:::\ \ /\ /:::|____|
- \:::\ \ \/____/ \/____/ \:::\/:::/ / \/____/ \:::\/:::/ / /:::/ / \/____/ \:::\ \ \:::\ \ /:::/ / \:::\ /::\ \::/ /
- \:::\ \ \::::::/ / \::::::/ / /:::/ / \:::\ \ \:::\ /:::/ / \:::\ \:::\ \/____/
- \:::\ \ \::::/ / \::::/ / /:::/ / \:::\ \ \:::\__/:::/ / \:::\ \:::\____\
- \:::\ \ /:::/ / /:::/ / \::/ / \:::\ \ \::::::::/ / \:::\ /:::/ /
- \:::\ \ /:::/ / /:::/ / \/____/ \:::\ \ \::::::/ / \:::\/:::/ /
- \:::\ \ /:::/ / /:::/ / \:::\ \ \::::/ / \::::::/ /
- \:::\____\ /:::/ / /:::/ / \:::\____\ \::/____/ \::::/ /
- \::/ / \::/ / \::/ / \::/ / ~~ \::/____/
- \/____/ \/____/ \/____/ \/____/
-
-
-
- ___ _ _ __ ____ __ _____ ___
- / __)( )_( ) /__\ (_ _)( ) ( _ ) / __)
- ( (__ ) _ ( /(__)\ )( )(__ )(_)( ( (_-.
- \___)(_) (_)(__)(__) (__) (____)(_____) \___/
-
-
- ________ ___ ___ ________ _________ ___ ________ ________
- |\ ____\ |\ \|\ \ |\ __ \ |\___ ___\ |\ \ |\ __ \ |\ ____\
- \ \ \___| \ \ \\\ \ \ \ \|\ \ \|___ \ \_| \ \ \ \ \ \|\ \ \ \ \___|
- \ \ \ \ \ __ \ \ \ __ \ \ \ \ \ \ \ \ \ \\\ \ \ \ \ ___
- \ \ \____ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \____ \ \ \\\ \ \ \ \|\ \
- \ \_______\ \ \__\ \__\ \ \__\ \__\ \ \__\ \ \_______\ \ \_______\ \ \_______\
- \|_______| \|__|\|__| \|__|\|__| \|__| \|_______| \|_______| \|_______|
-
-
- ╔═╗┬ ┬┌─┐┌┬┐┬ ┌─┐┌─┐
- ║ ├─┤├─┤ │ │ │ ││ ┬
- ╚═╝┴ ┴┴ ┴ ┴ ┴─┘└─┘└─┘
-
-
- ▄▄· ▄ .▄ ▄▄▄· ▄▄▄▄▄▄▄▌ ▄▄ •
- ▐█ ▌▪██▪▐█▐█ ▀█ •██ ██• ▪ ▐█ ▀ ▪
- ██ ▄▄██▀▐█▄█▀▀█ ▐█.▪██▪ ▄█▀▄ ▄█ ▀█▄
- ▐███▌██▌▐▀▐█ ▪▐▌ ▐█▌·▐█▌▐▌▐█▌.▐▌▐█▄▪▐█
- ·▀▀▀ ▀▀▀ · ▀ ▀ ▀▀▀ .▀▀▀ ▀█▄▀▪·▀▀▀▀
-
-
- ,. - ., .·¨'`; ,.·´¨;\ ,., ' , . ., ° ,. ' , ·. ,.-·~·., ‘ ,.-·^*ª'` ·,
- ,·'´ ,. - , ';\ '; ;'\ '; ;::\ ;´ '· ., ;'´ , ., _';\' / ';\ / ·'´,.-·-., `,'‚ .·´ ,·'´:¯'`·, '\‘
- ,·´ .'´\:::::;' ;:'\ ' ; ;::'\ ,' ;::'; .´ .-, ';\ \:´¨¯:;' `;::'\:'\ ,' ,'::'\ / .'´\:::::::'\ '\ ° ,´ ,'\:::::::::\,.·\'
- / ,'´::::'\;:-/ ,' ::; ' ; ;::_';,. ,.' ;:::';° / /:\:'; ;:'\' \::::; ,'::_'\;' ,' ;:::';' ,·' ,'::::\:;:-·-:'; ';\‚ / /:::\;·'´¯'`·;\:::\°
- ,' ;':::::;'´ '; /\::;' ' .' ,. -·~-·, ;:::'; ' ,' ,'::::'\'; ;::'; ,' ,'::;' ‘ '; ,':::;' ;. ';:::;´ ,' ,':'\‚ ; ;:::;' '\;:·´
- ; ;:::::; '\*'´\::\' ° '; ;'\::::::::; '/::::; ,.-·' '·~^*'´¨, ';::; ; ;:::; ° ; ,':::;' ' '; ;::; ,'´ .'´\::';‚ '; ;::/ ,·´¯'; °
- '; ';::::'; '\::'\/.' ; ';:;\;::-··; ;::::; ':, ,·:²*´¨¯'`; ;::'; ; ;::;' ‘ ,' ,'::;' '; ':;: ,.·´,.·´::::\;'° '; '·;' ,.·´, ;'\
- \ '·:;:'_ ,. -·'´.·´\‘ ':,.·´\;' ;' ,' :::/ ' ,' / \::::::::'; ;::'; ; ;::;'‚ ; ';_:,.-·´';\‘ \·, `*´,.·'´::::::;·´ \'·. `'´,.·:´'; ;::\'
- '\:` · .,. -·:´::::::\' \:::::\ \·.'::::; ,' ,'::::\·²*'´¨¯':,'\:; ',.'\::;'‚ ', _,.-·'´:\:\‘ \\:¯::\:::::::;:·´ '\::\¯::::::::'; ;::'; ‘
- \:::::::\:::::::;:·'´' \;:·´ \:\::'; \`¨\:::/ \::\' \::\:;'‚ \¨:::::::::::\'; `\:::::\;::·'´ ° `·:\:::;:·´';.·´\::;'
- `· :;::\;::-·´ `·\;' '\::\;' '\;' ' \;:' ‘ '\;::_;:-·'´‘ ¯ ¯ \::::\;'‚
- ' `¨' ° '¨ ‘ '\:·´'
-
-
- ▄████▄ ██░ ██ ▄▄▄ ▄▄▄█████▓ ██▓ ▒█████ ▄████
- ▒██▀ ▀█ ▓██░ ██▒▒████▄ ▓ ██▒ ▓▒▓██▒ ▒██▒ ██▒ ██▒ ▀█▒
- ▒▓█ ▄ ▒██▀▀██░▒██ ▀█▄ ▒ ▓██░ ▒░▒██░ ▒██░ ██▒▒██░▄▄▄░
- ▒▓▓▄ ▄██▒░▓█ ░██ ░██▄▄▄▄██ ░ ▓██▓ ░ ▒██░ ▒██ ██░░▓█ ██▓
- ▒ ▓███▀ ░░▓█▒░██▓ ▓█ ▓██▒ ▒██▒ ░ ░██████▒░ ████▓▒░░▒▓███▀▒
- ░ ░▒ ▒ ░ ▒ ░░▒░▒ ▒▒ ▓▒█░ ▒ ░░ ░ ▒░▓ ░░ ▒░▒░▒░ ░▒ ▒
- ░ ▒ ▒ ░▒░ ░ ▒ ▒▒ ░ ░ ░ ░ ▒ ░ ░ ▒ ▒░ ░ ░
- ░ ░ ░░ ░ ░ ▒ ░ ░ ░ ░ ░ ░ ▒ ░ ░ ░
- ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
- ░
-
-
- ▄█▄ ▄ █ ██ ▄▄▄▄▀ █ ████▄ ▄▀
- █▀ ▀▄ █ █ █ █ ▀▀▀ █ █ █ █ ▄▀
- █ ▀ ██▀▀█ █▄▄█ █ █ █ █ █ ▀▄
- █▄ ▄▀ █ █ █ █ █ ███▄ ▀████ █ █
- ▀███▀ █ █ ▀ ▀ ███
- ▀ █
- ▀
-
+
+
+
+
+
🎉 恭喜!Chatlog 服务已成功启动
+
+ Chatlog 是一个帮助你轻松使用自己聊天数据的工具,现在你可以通过 HTTP
+ API 访问你的聊天记录、联系人和群聊信息。
+
+
+
+
+
🔍 API 接口与调试
+
+
+
+
最近会话
+
群聊
+
联系人
+
聊天记录
+
+
+
+
+
+
+ 查询最近会话列表。GET /api/v1/session
+
+
+
+
+
+
+
+
+
+
+
+
+ 查询群聊列表,可选择性地按关键词搜索。GET /api/v1/chatroom
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 查询联系人列表,可选择性地按关键词搜索。GET /api/v1/contact
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 查询指定时间范围内与特定联系人或群聊的聊天记录。GET /api/v1/chatlog
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
🤖 MCP 集成
+
+ Chatlog 支持 MCP (Model Context Protocol) SSE 协议,可与支持 MCP 的 AI
+ 助手无缝集成。
+
+
SSE 端点:/sse
+
+ 详细集成指南请参考
+ MCP 集成指南
+
+
+
+
+
-
+
diff --git a/internal/wechat/key/darwin/v3.go b/internal/wechat/key/darwin/v3.go
index fd92ca5..fd0decc 100644
--- a/internal/wechat/key/darwin/v3.go
+++ b/internal/wechat/key/darwin/v3.go
@@ -22,7 +22,7 @@ const (
var V3KeyPatterns = []KeyPatternInfo{
{
Pattern: []byte{0x72, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x69, 0x33, 0x32},
- Offset: 24,
+ Offsets: []int{24},
},
}
@@ -122,16 +122,73 @@ func (e *V3Extractor) findMemory(ctx context.Context, pid uint32, memoryChannel
return err
}
- log.Debug().Msgf("Read memory region, size: %d bytes", len(memory))
+ totalSize := len(memory)
+ log.Debug().Msgf("Read memory region, size: %d bytes", totalSize)
- // Send memory data to channel for processing
- select {
- case memoryChannel <- memory:
- log.Debug().Msg("Memory region sent for analysis")
- case <-ctx.Done():
- return ctx.Err()
+ // If memory is small enough, process it as a single chunk
+ if totalSize <= MinChunkSize {
+ select {
+ case memoryChannel <- memory:
+ log.Debug().Msg("Memory sent as a single chunk for analysis")
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+ return nil
}
+ chunkCount := MaxWorkers * ChunkMultiplier
+
+ // Calculate chunk size based on fixed chunk count
+ chunkSize := totalSize / chunkCount
+ if chunkSize < MinChunkSize {
+ // Reduce number of chunks if each would be too small
+ chunkCount = totalSize / MinChunkSize
+ if chunkCount == 0 {
+ chunkCount = 1
+ }
+ chunkSize = totalSize / chunkCount
+ }
+
+ // Process memory in chunks from end to beginning
+ for i := chunkCount - 1; i >= 0; i-- {
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ default:
+ // Calculate start and end positions for this chunk
+ start := i * chunkSize
+ end := (i + 1) * chunkSize
+
+ // Ensure the last chunk includes all remaining memory
+ if i == chunkCount-1 {
+ end = totalSize
+ }
+
+ // Add overlap area to catch patterns at chunk boundaries
+ if i > 0 {
+ start -= ChunkOverlapBytes
+ if start < 0 {
+ start = 0
+ }
+ }
+
+ chunk := memory[start:end]
+
+ log.Debug().
+ Int("chunk_index", i+1).
+ Int("total_chunks", chunkCount).
+ Int("chunk_size", len(chunk)).
+ Int("start_offset", start).
+ Int("end_offset", end).
+ Msg("Processing memory chunk")
+
+ select {
+ case memoryChannel <- chunk:
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+ }
+ }
return nil
}
@@ -173,24 +230,26 @@ func (e *V3Extractor) SearchKey(ctx context.Context, memory []byte) (string, boo
break // No more matches found
}
- // Check if we have enough space for the key
- keyOffset := index + keyPattern.Offset
- if keyOffset < 0 || keyOffset+32 > len(memory) {
- index -= 1
- continue
- }
+ // Try each offset for this pattern
+ for _, offset := range keyPattern.Offsets {
+ // Check if we have enough space for the key
+ keyOffset := index + offset
+ if keyOffset < 0 || keyOffset+32 > len(memory) {
+ continue
+ }
- // Extract the key data, which is 32 bytes long
- keyData := memory[keyOffset : keyOffset+32]
+ // Extract the key data, which is at the offset position and 32 bytes long
+ keyData := memory[keyOffset : keyOffset+32]
- // Validate key against database header
- if e.validator.Validate(keyData) {
- log.Debug().
- Str("pattern", hex.EncodeToString(keyPattern.Pattern)).
- Int("offset", keyPattern.Offset).
- Str("key", hex.EncodeToString(keyData)).
- Msg("Key found")
- return hex.EncodeToString(keyData), true
+ // Validate key against database header
+ if e.validator.Validate(keyData) {
+ log.Debug().
+ Str("pattern", hex.EncodeToString(keyPattern.Pattern)).
+ Int("offset", offset).
+ Str("key", hex.EncodeToString(keyData)).
+ Msg("Key found")
+ return hex.EncodeToString(keyData), true
+ }
}
index -= 1
diff --git a/internal/wechat/key/darwin/v4.go b/internal/wechat/key/darwin/v4.go
index 09530a5..f538e1a 100644
--- a/internal/wechat/key/darwin/v4.go
+++ b/internal/wechat/key/darwin/v4.go
@@ -16,17 +16,16 @@ import (
)
const (
- MaxWorkers = 8
+ MaxWorkers = 8
+ MinChunkSize = 1 * 1024 * 1024 // 1MB
+ ChunkOverlapBytes = 1024 // Greater than all offsets
+ ChunkMultiplier = 2 // Number of chunks = MaxWorkers * ChunkMultiplier
)
var V4KeyPatterns = []KeyPatternInfo{
{
Pattern: []byte{0x20, 0x66, 0x74, 0x73, 0x35, 0x28, 0x25, 0x00},
- Offset: 16,
- },
- {
- Pattern: []byte{0x20, 0x66, 0x74, 0x73, 0x35, 0x28, 0x25, 0x00},
- Offset: -80,
+ Offsets: []int{16, -80, 64},
},
}
@@ -126,14 +125,72 @@ func (e *V4Extractor) findMemory(ctx context.Context, pid uint32, memoryChannel
return err
}
- log.Debug().Msgf("Read memory region, size: %d bytes", len(memory))
+ totalSize := len(memory)
+ log.Debug().Msgf("Read memory region, size: %d bytes", totalSize)
- // Send memory data to channel for processing
- select {
- case memoryChannel <- memory:
- log.Debug().Msg("Memory region sent for analysis")
- case <-ctx.Done():
- return ctx.Err()
+ // If memory is small enough, process it as a single chunk
+ if totalSize <= MinChunkSize {
+ select {
+ case memoryChannel <- memory:
+ log.Debug().Msg("Memory sent as a single chunk for analysis")
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+ return nil
+ }
+
+ chunkCount := MaxWorkers * ChunkMultiplier
+
+ // Calculate chunk size based on fixed chunk count
+ chunkSize := totalSize / chunkCount
+ if chunkSize < MinChunkSize {
+ // Reduce number of chunks if each would be too small
+ chunkCount = totalSize / MinChunkSize
+ if chunkCount == 0 {
+ chunkCount = 1
+ }
+ chunkSize = totalSize / chunkCount
+ }
+
+ // Process memory in chunks from end to beginning
+ for i := chunkCount - 1; i >= 0; i-- {
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ default:
+ // Calculate start and end positions for this chunk
+ start := i * chunkSize
+ end := (i + 1) * chunkSize
+
+ // Ensure the last chunk includes all remaining memory
+ if i == chunkCount-1 {
+ end = totalSize
+ }
+
+ // Add overlap area to catch patterns at chunk boundaries
+ if i > 0 {
+ start -= ChunkOverlapBytes
+ if start < 0 {
+ start = 0
+ }
+ }
+
+ chunk := memory[start:end]
+
+ log.Debug().
+ Int("chunk_index", i+1).
+ Int("total_chunks", chunkCount).
+ Int("chunk_size", len(chunk)).
+ Int("start_offset", start).
+ Int("end_offset", end).
+ Msg("Processing memory chunk")
+
+ select {
+ case memoryChannel <- chunk:
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+ }
}
return nil
@@ -177,24 +234,26 @@ func (e *V4Extractor) SearchKey(ctx context.Context, memory []byte) (string, boo
break // No more matches found
}
- // Check if we have enough space for the key
- keyOffset := index + keyPattern.Offset
- if keyOffset < 0 || keyOffset+32 > len(memory) {
- index -= 1
- continue
- }
+ // Try each offset for this pattern
+ for _, offset := range keyPattern.Offsets {
+ // Check if we have enough space for the key
+ keyOffset := index + offset
+ if keyOffset < 0 || keyOffset+32 > len(memory) {
+ continue
+ }
- // Extract the key data, which is 16 bytes after the pattern and 32 bytes long
- keyData := memory[keyOffset : keyOffset+32]
+ // Extract the key data, which is at the offset position and 32 bytes long
+ keyData := memory[keyOffset : keyOffset+32]
- // Validate key against database header
- if e.validator.Validate(keyData) {
- log.Debug().
- Str("pattern", hex.EncodeToString(keyPattern.Pattern)).
- Int("offset", keyPattern.Offset).
- Str("key", hex.EncodeToString(keyData)).
- Msg("Key found")
- return hex.EncodeToString(keyData), true
+ // Validate key against database header
+ if keyData, ok := e.validate(ctx, keyData); ok {
+ log.Debug().
+ Str("pattern", hex.EncodeToString(keyPattern.Pattern)).
+ Int("offset", offset).
+ Str("key", hex.EncodeToString(keyData)).
+ Msg("Key found")
+ return hex.EncodeToString(keyData), true
+ }
}
index -= 1
@@ -204,11 +263,19 @@ func (e *V4Extractor) SearchKey(ctx context.Context, memory []byte) (string, boo
return "", false
}
+func (e *V4Extractor) validate(ctx context.Context, keyDate []byte) ([]byte, bool) {
+ if e.validator.Validate(keyDate) {
+ return keyDate, true
+ }
+ // Try to find a valid key by ***
+ return nil, false
+}
+
func (e *V4Extractor) SetValidate(validator *decrypt.Validator) {
e.validator = validator
}
type KeyPatternInfo struct {
Pattern []byte
- Offset int
+ Offsets []int
}
diff --git a/internal/wechatdb/datasource/windowsv3/datasource.go b/internal/wechatdb/datasource/windowsv3/datasource.go
index 2a8434c..92d62e4 100644
--- a/internal/wechatdb/datasource/windowsv3/datasource.go
+++ b/internal/wechatdb/datasource/windowsv3/datasource.go
@@ -35,7 +35,7 @@ var Groups = []dbm.Group{
},
{
Name: Contact,
- Pattern: `^MicroMsg.db$`,
+ Pattern: `^MicroMsg\.db$`,
BlackList: []string{},
},
{