├── readme.md └── poc.c /readme.md: -------------------------------------------------------------------------------- 1 | # Apple M1 Wechat DumpKey 2 | 3 | ## 2025-06-23 复活,最近有需求了,顺便更新一下 4 | 5 | ### AppStore WeChat Version. 3.8.10 (28633) 6 | 7 | > 下载 [dumpkey.zip](https://github.com/kekeimiku/dumpkey/releases) 8 | 9 | ```shell 10 | # dumpkey 11 | 12 | # example: 13 | sudo ./dumpkey $(pgrep WeChat |head -1) /Users/keke/Library/Containers/com.tencent.xinWeChat/Data/Library/Application\ Support/com.tencent.xinWeChat/[version_id]/[account_id]/Message/msg_0.db 14 | 15 | key: 8390b***********************ac9e299a00076 16 | sudo ./dumpkey $(pgrep WeChat |head -1) 0.03s user 0.02s system 93% cpu 0.051 total 17 | ``` 18 | 19 | ## 2023-04-14 这个仓库的代码不再更新 20 | 21 | > 下载 [ptrsx-aarch64-apple-darwin.zip](https://github.com/kekeimiku/PointerSearcher-X/releases/tag/v0.4.1) 然后根据对应微信版本执行以下命令 22 | 23 | ### AppStore WeChat Version 3.7.0 (25070) 24 | 25 | ```shell 26 | sudo ./dumper test --pid $(pgrep WeChat |head -1) --path "WeChat+87194768@8@24@16@32@8@8@64@8@0@0" -n 32 27 | ``` 28 | 29 | ### WeChat Version. 3.7.1 (25682) ( 网友贡献 [#2](https://github.com/kekeimiku/dumpkey/issues/2) 应该是官网版本) 30 | 31 | ```shell 32 | sudo ./dumper test --pid $(pgrep WeChat |head -1) --path "WeChat+87473296@8@8@16@32@8@8@64@8@0@0" -n 32 33 | ``` 34 | 35 | ### AppStore WeChat Version. 3.7.1 (25683) 36 | 37 | ```shell 38 | sudo ./dumper test --pid $(pgrep WeChat |head -1) --path "WeChat+87749776@8@8@16@32@8@8@64@8@0@0" -n 32 39 | ``` 40 | 41 | ### AppStore WeChat Version. 3.8.0 (26253) 42 | 43 | ```shell 44 | sudo ./dumper test --pid $(pgrep WeChat |head -1) --path "WeChat+82448200@8@8@16@32@8@8@64@8@0@0" -n 32 45 | ``` 46 | 47 | ### AppStore WeChat Version. 3.8.1 (26639) 48 | 49 | ```shell 50 | sudo ./dumper test --pid $(pgrep WeChat |head -1) --path "WeChat+78706432@8@8@16@32@8@8@64@8@0@0" -n 32 51 | ``` 52 | 53 | ### AppStore WeChat Version. 3.8.2 (27305) 54 | 55 | ```shell 56 | sudo ./dumper test --pid $(pgrep WeChat |head -1) --path "WeChat+80055232@8@8@16@32@8@8@64@8@0@0" -n 32 57 | ``` 58 | 59 | ### WeChat Version. 3.8.2 (27317) ([#4](https://github.com/kekeimiku/dumpkey/issues/4) 通过brew安装,应该是官网版本) 60 | 61 | ```shell 62 | sudo ./dumper test --pid $(pgrep WeChat |head -1) --path "WeChat+79795584@8@8@16@32@8@8@64@8@0@0" -n 32 63 | ``` 64 | 65 | ### AppStore WeChat Version. 3.8.3 (27318) 66 | 67 | ```shell 68 | sudo ./dumper test --pid $(pgrep WeChat |head -1) --path "WeChat+80055232@8@8@16@32@8@8@64@8@0@0" -n 32 69 | ``` 70 | 71 | ### AppStore WeChat Version. 3.8.4 (27738) 72 | 73 | ```shell 74 | sudo ./dumper test --pid $(pgrep WeChat |head -1) --path "WeChat+81059936@8@8@16@32@8@8@64@8@0@0" -n 32 75 | ``` 76 | 77 | ## 报错 error: xxxx, `code: 5` 78 | 79 | 如果版本都对,但是运行报错,可以尝试去除微信签名。运行以下命令 80 | 81 | ```shell 82 | sudo codesign --sign - --force --deep /Applications/WeChat.app 83 | ``` 84 | 85 | 如果还不行,可以尝试关闭 `SIP` 86 | -------------------------------------------------------------------------------- /poc.c: -------------------------------------------------------------------------------- 1 | // clang poc.c -o dumpkey -O3 -flto 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define DBPAGE_SIZE 1024 9 | #define KEY_SIZE 32 10 | #define SALT_SIZE 16 11 | #define HMAC_SIZE 20 12 | #define IV_SIZE 16 13 | #define AES_BLOCK_SIZE 16 14 | 15 | bool testkey(const unsigned char *page, const unsigned char *key) { 16 | if (!page || !key) 17 | return false; 18 | 19 | unsigned char mac_salt[SALT_SIZE]; 20 | for (int i = 0; i < SALT_SIZE; i++) 21 | mac_salt[i] = page[i] ^ 0x3A; 22 | 23 | unsigned char mac_key[KEY_SIZE]; 24 | 25 | CCKeyDerivationPBKDF(kCCPBKDF2, (const char *)key, KEY_SIZE, mac_salt, 26 | SALT_SIZE, kCCPRFHmacAlgSHA1, 2, mac_key, KEY_SIZE); 27 | 28 | int reserve = ((IV_SIZE + HMAC_SIZE + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE) * 29 | AES_BLOCK_SIZE; 30 | int end = DBPAGE_SIZE - reserve + IV_SIZE; 31 | 32 | CCHmacContext hmacContext; 33 | CCHmacInit(&hmacContext, kCCHmacAlgSHA1, mac_key, KEY_SIZE); 34 | CCHmacUpdate(&hmacContext, page + SALT_SIZE, end - SALT_SIZE); 35 | 36 | unsigned char page_no[4] = {1, 0, 0, 0}; 37 | CCHmacUpdate(&hmacContext, page_no, 4); 38 | 39 | unsigned char hmac_result[HMAC_SIZE]; 40 | CCHmacFinal(&hmacContext, hmac_result); 41 | 42 | return memcmp(hmac_result, page + end, HMAC_SIZE) == 0; 43 | } 44 | 45 | int dumpkey(pid_t pid, const char *filename, char *outkey) { 46 | mach_port_name_t target_task; 47 | kern_return_t kr; 48 | kr = task_for_pid(mach_task_self(), pid, &target_task); 49 | if (kr != KERN_SUCCESS) { 50 | fprintf(stderr, "%s (%d)\n", mach_error_string(kr), kr); 51 | return -1; 52 | } 53 | 54 | unsigned char page[DBPAGE_SIZE]; 55 | FILE *fp = fopen(filename, "rb"); 56 | if (!fp || fread(page, 1, DBPAGE_SIZE, fp) != DBPAGE_SIZE) { 57 | fprintf(stderr, "failed to read db file\n"); 58 | if (fp) 59 | fclose(fp); 60 | return -1; 61 | } 62 | fclose(fp); 63 | 64 | mach_vm_address_t address = 0; 65 | mach_vm_size_t size; 66 | vm_region_extended_info_data_t info; 67 | mach_msg_type_number_t infoCnt = VM_REGION_EXTENDED_INFO_COUNT; 68 | mach_port_t object_name; 69 | unsigned char pattern[9] = {0x72, 0x74, 0x72, 0x65, 0x65, 70 | 0x5F, 0x69, 0x33, 0x32}; 71 | 72 | while (1) { 73 | kr = mach_vm_region(target_task, &address, &size, VM_REGION_EXTENDED_INFO, 74 | (vm_region_info_t)&info, &infoCnt, &object_name); 75 | if (kr != KERN_SUCCESS) 76 | break; 77 | 78 | if ((info.protection & VM_PROT_READ) && (info.protection & VM_PROT_WRITE) && 79 | (info.user_tag == VM_MEMORY_MALLOC_NANO)) { 80 | 81 | unsigned char *data = malloc(size); 82 | 83 | mach_vm_size_t outsize = 0; 84 | kr = mach_vm_read_overwrite(target_task, address, size, 85 | (mach_vm_address_t)data, &outsize); 86 | if (kr != KERN_SUCCESS) { 87 | free(data); 88 | break; 89 | } 90 | 91 | unsigned char *pos = data, *end = pos + outsize; 92 | while ((pos = memmem(pos, end - pos, pattern, 9))) { 93 | unsigned char *key = pos + 24; 94 | if (key + KEY_SIZE > end) 95 | break; 96 | 97 | if (testkey(page, key)) { 98 | for (int i = 0; i < KEY_SIZE; i++) 99 | sprintf(outkey + i * 2, "%02x", key[i]); 100 | free(data); 101 | return 0; 102 | } 103 | 104 | pos++; 105 | } 106 | free(data); 107 | } 108 | address += size; 109 | } 110 | 111 | return -1; 112 | } 113 | 114 | int main(int argc, char *argv[]) { 115 | if (argc < 3) { 116 | fprintf(stderr, "Usage: %s \n", argv[0]); 117 | return -1; 118 | } 119 | 120 | pid_t pid = atoi(argv[1]); 121 | 122 | char key[100] = {0}; 123 | if (dumpkey(pid, argv[2], key) == 0) { 124 | printf("key: %s\n", key); 125 | } else { 126 | printf("not found key\n"); 127 | } 128 | 129 | return 0; 130 | } 131 | --------------------------------------------------------------------------------