├── .gitignore ├── README.md ├── frida-core.h ├── go.mod ├── main.go ├── script └── hook.js └── src ├── hook.js └── utils.go /.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | 20 | # Go workspace file 21 | go.work 22 | go.work.sum 23 | go.sum 24 | .DS_Store 25 | .vscode/launch.json 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WeChatOpenDevTools 2 | 3 | 微信 Mac arm DevTools 4 | 5 | ### **注意本库只能作为学习用途, 造成的任何问题与本库开发者无关, 如侵犯到你的权益,请联系删除。** 6 | 7 | ### **注意本库只能作为学习用途, 造成的任何问题与本库开发者无关, 如侵犯到你的权益,请联系删除。** 8 | 9 | ### **注意本库只能作为学习用途, 造成的任何问题与本库开发者无关, 如侵犯到你的权益,请联系删除。** 10 | 11 | --- 12 | 13 | ## 支持版本列表 14 | 15 | > 感谢志远大佬、感谢 JaveleyQAQ 大佬 16 | 17 | 本项目仅支持 mac-arm 微信最新版本 18 | 19 | ## 使用方法 20 | 21 | ``` 22 | go install github.com/baiqll/wechatopendevtools@latest 23 | ``` 24 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/baiqll/wechatopendevtools 2 | 3 | go 1.22.2 4 | 5 | require github.com/frida/frida-go v0.12.0 6 | 7 | require github.com/google/uuid v1.3.0 // indirect 8 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/json" 6 | "fmt" 7 | "os" 8 | 9 | utils "github.com/baiqll/wechatopendevtools/src" 10 | 11 | "github.com/frida/frida-go/frida" 12 | ) 13 | 14 | 15 | func main() { 16 | mgr := frida.NewDeviceManager() 17 | 18 | 19 | localDevice, err := mgr.LocalDevice() 20 | if err != nil { 21 | fmt.Println("[-] ","frida 启动失败") 22 | 23 | os.Exit(1) 24 | } 25 | // 获取 hook script 26 | 27 | 28 | // 获取 pid 29 | wechataexp_id ,err:= utils.GetWeChatAppExPID() 30 | if err != nil { 31 | fmt.Println("[-] ","找不到 WeChatAppExP") 32 | os.Exit(1) 33 | } 34 | 35 | hook_script ,err := utils.GetHookScript() 36 | if err != nil { 37 | fmt.Println("[-] ","找不到 hook.js") 38 | os.Exit(1) 39 | } 40 | 41 | session, err := localDevice.Attach(wechataexp_id, nil) 42 | if err != nil { 43 | fmt.Println("[-] ", "Attach 错误") 44 | os.Exit(1) 45 | } 46 | 47 | script, err := session.CreateScript(hook_script) 48 | if err != nil { 49 | fmt.Println("[-] ", "注入 script 失败") 50 | os.Exit(1) 51 | } 52 | 53 | script.On("message", func(message string) { 54 | 55 | var customMsg utils.Message 56 | 57 | err := json.Unmarshal([]byte(message), &customMsg) 58 | 59 | if err != nil { 60 | fmt.Println("[-] ", "消息错误") 61 | os.Exit(1) 62 | } 63 | 64 | if customMsg.Type == "send"{ 65 | 66 | fmt.Println("[*] ", customMsg.Payload) 67 | }else{ 68 | fmt.Println("[-] ", customMsg.Payload) 69 | 70 | } 71 | 72 | }) 73 | 74 | if err := script.Load(); err != nil { 75 | fmt.Println("hook script 错误", err) 76 | os.Exit(1) 77 | } 78 | 79 | r := bufio.NewReader(os.Stdin) 80 | r.ReadLine() 81 | } -------------------------------------------------------------------------------- /script/hook.js: -------------------------------------------------------------------------------- 1 | //获取WeChatAppEx.exe的基址 2 | var module = Process.findModuleByName("WeChatAppEx Framework"); 3 | var base = module.base; 4 | 5 | var address = { 6 | enableVconsole: "0x25505f8", 7 | setupInterceptor: "0x23f028c", 8 | adevTools: "0x6c219f8", 9 | version: "WeChat v3.8.8.18", 10 | }; 11 | 12 | send("WeChatAppEx 注入成功!"); 13 | send("当前小程序版本: " + address.version); 14 | send("等待小程序加载..."); 15 | 16 | /* 公共部分 */ 17 | function readStdString(s) { 18 | var flag = s.add(23).readU8(); 19 | if (flag == 0x80) { 20 | // 从堆中读取 21 | var size = s.add(8).readUInt(); 22 | return s.readPointer().readUtf8String(size); 23 | } else { 24 | // 从栈中读取 25 | return s.readUtf8String(flag); 26 | } 27 | } 28 | function writeStdString(s, content) { 29 | var flag = s.add(23).readU8(); 30 | if (flag == 0x80) { 31 | // 从堆中写入 32 | var orisize = s.add(8).readUInt(); 33 | if (content.length > orisize) { 34 | throw "must below orisize!"; 35 | } 36 | s.readPointer().writeUtf8String(content); 37 | s.add(8).writeUInt(content.length); 38 | } else { 39 | // 从栈中写入 40 | if (content.length > 22) { 41 | throw "max 23 for stack str"; 42 | } 43 | s.writeUtf8String(content); 44 | s.add(23).writeU8(content.length); 45 | } 46 | } 47 | 48 | /** hook 部分 **/ 49 | 50 | // 开启 enable_vconsole 51 | function enableVconsole() { 52 | Interceptor.attach(base.add(address.enableVconsole), { 53 | onEnter(args) { 54 | for (var i = 0; i < 0x1000; i += 8) { 55 | try { 56 | var s = readStdString(args[2].add(i)); 57 | var s1 = s.replaceAll( 58 | '"enable_vconsole":false', 59 | '"enable_vconsole": true' 60 | ); 61 | if (s !== s1) { 62 | writeStdString(args[2].add(i), s1); 63 | } 64 | } catch (a) { 65 | // console.log(a); 66 | } 67 | } 68 | }, 69 | }); 70 | } 71 | 72 | // 开启 devtools 所有功能 73 | function setupInterceptor() { 74 | Interceptor.attach(base.add(address.setupInterceptor), { 75 | onEnter(args) { 76 | args[1] = ptr(0x1); 77 | send("已还原完整F12"); 78 | }, 79 | }); 80 | } 81 | 82 | // 解除 仅首次启动微信打开 devTools 83 | 84 | function bypassOnlyFirst() { 85 | var menuItemDevTools = base.add(address.adevTools); 86 | Memory.protect(menuItemDevTools, 8, "rw-"); 87 | menuItemDevTools.writeUtf8String("DevTools"); 88 | enableVconsole(); 89 | setupInterceptor(); 90 | } 91 | 92 | bypassOnlyFirst(); 93 | -------------------------------------------------------------------------------- /src/hook.js: -------------------------------------------------------------------------------- 1 | //获取WeChatAppEx.exe的基址 2 | var module = Process.findModuleByName("WeChatAppEx Framework"); 3 | var base = module.base; 4 | 5 | // var address = { 6 | // enableVconsole: "0x25505f8", 7 | // setupInterceptor: "0x23f028c", 8 | // adevTools: "0x6c219f8", 9 | // version: "WeChat v3.8.8.18", 10 | // }; 11 | 12 | var address = { 13 | enableVconsole: "0x2550414", 14 | setupInterceptor: "0x23f00a8", 15 | adevTools: "0x6c218f8", 16 | version: "WeChat v3.8.9", 17 | }; 18 | 19 | send("WeChatAppEx 注入成功!"); 20 | send("当前小程序版本: " + address.version); 21 | send("等待小程序加载..."); 22 | 23 | /* 公共部分 */ 24 | function readStdString(s) { 25 | var flag = s.add(23).readU8(); 26 | if (flag == 0x80) { 27 | // 从堆中读取 28 | var size = s.add(8).readUInt(); 29 | return s.readPointer().readUtf8String(size); 30 | } else { 31 | // 从栈中读取 32 | return s.readUtf8String(flag); 33 | } 34 | } 35 | function writeStdString(s, content) { 36 | var flag = s.add(23).readU8(); 37 | if (flag == 0x80) { 38 | // 从堆中写入 39 | var orisize = s.add(8).readUInt(); 40 | if (content.length > orisize) { 41 | throw "must below orisize!"; 42 | } 43 | s.readPointer().writeUtf8String(content); 44 | s.add(8).writeUInt(content.length); 45 | } else { 46 | // 从栈中写入 47 | if (content.length > 22) { 48 | throw "max 23 for stack str"; 49 | } 50 | s.writeUtf8String(content); 51 | s.add(23).writeU8(content.length); 52 | } 53 | } 54 | 55 | /** hook 部分 **/ 56 | 57 | // 开启 enable_vconsole 58 | function enableVconsole() { 59 | Interceptor.attach(base.add(address.enableVconsole), { 60 | onEnter(args) { 61 | for (var i = 0; i < 0x1000; i += 8) { 62 | try { 63 | var s = readStdString(args[2].add(i)); 64 | var s1 = s.replaceAll( 65 | '"enable_vconsole":false', 66 | '"enable_vconsole": true' 67 | ); 68 | if (s !== s1) { 69 | writeStdString(args[2].add(i), s1); 70 | } 71 | } catch (a) { 72 | // console.log(a); 73 | } 74 | } 75 | }, 76 | }); 77 | } 78 | 79 | // 开启 devtools 所有功能 80 | function setupInterceptor() { 81 | Interceptor.attach(base.add(address.setupInterceptor), { 82 | onEnter(args) { 83 | args[1] = ptr(0x1); 84 | send("已还原完整F12"); 85 | }, 86 | }); 87 | } 88 | 89 | // 解除 仅首次启动微信打开 devTools 90 | 91 | function bypassOnlyFirst() { 92 | var menuItemDevTools = base.add(address.adevTools); 93 | Memory.protect(menuItemDevTools, 8, "rw-"); 94 | menuItemDevTools.writeUtf8String("DevTools"); 95 | enableVconsole(); 96 | setupInterceptor(); 97 | } 98 | 99 | bypassOnlyFirst(); 100 | -------------------------------------------------------------------------------- /src/utils.go: -------------------------------------------------------------------------------- 1 | package src 2 | 3 | import ( 4 | "bytes" 5 | "embed" 6 | "io/ioutil" 7 | "os/exec" 8 | "regexp" 9 | "strconv" 10 | ) 11 | 12 | //go:embed hook.js 13 | var jsFile embed.FS 14 | 15 | type Message struct { 16 | Type string 17 | Payload string 18 | } 19 | 20 | func GetWeChatAppExPID()(pid int,err error){ 21 | 22 | command := "ps aux | grep 'WeChatAppEx' | grep -v 'grep' | grep ' --client_version' | grep '-user-agent=' | awk '{print $2}' | tail -n 1" 23 | 24 | cmd := exec.Command("sh", "-c", command) 25 | var out bytes.Buffer 26 | cmd.Stdout = &out 27 | cmd.Run() 28 | re := regexp.MustCompile(`[\s\n\t]+`) 29 | 30 | pid, err = strconv.Atoi(re.ReplaceAllString(out.String(), "")) 31 | 32 | return 33 | 34 | } 35 | 36 | func GetHookScript()(script string,err error){ 37 | 38 | 39 | // 使用embed包提供的文件系统访问嵌入的文件 40 | file, err := jsFile.Open("hook.js") 41 | 42 | if err != nil { 43 | return 44 | } 45 | defer file.Close() 46 | 47 | // 读取并打印文件内容 48 | contents, err := ioutil.ReadAll(file) 49 | if err != nil { 50 | return 51 | } 52 | 53 | script = string(contents) 54 | 55 | return 56 | 57 | } --------------------------------------------------------------------------------