GoodSync 文件名加解密算法逆向

15次阅读

共计 7035 个字符,预计需要花费 18 分钟才能阅读完成。

本帖最后由 Mr.lin 于 2024-4-10 03:25 编辑

前言

众所周知 GoodSync 文件名加密算法不公开, 它们只提到 AES 加密

当我决定切换同步工具的时候遇到了这个问题, 大量文件加密传到云端, 不使用 GoodSync 无法解密文件名

我马上开一个工单去问它们客服, 态度屌的一批

为了解决问题, 更好配合 rclone 之类的同步工具, 我们开始分析

不想看分析的直接拉到底, 下载加解密工具

加密部分逆向分析

000000014014BF40  | 40:53             | push rbx                               |
000000014014BF42  | 55                | push rbp                               |
000000014014BF43  | 56                | push rsi                               |
000000014014BF44  | 57                | push rdi                               |
000000014014BF45  | 41:54             | push r12                               |
000000014014BF47  | 41:55             | push r13                               |
000000014014BF49  | 41:56             | push r14                               |
000000014014BF4B  | 41:57             | push r15                               |
000000014014BF4D  | 48:81EC A8000000  | sub rsp,A8                             |
000000014014BF54  | 48:C74424 58 FEFF | mov qword ptr ss:[rsp+58],FFFFFFFFFFFF |
000000014014BF5D  | 48:8B05 1426AF00  | mov rax,qword ptr ds:[140C3E578]       |
000000014014BF64  | 48:33C4           | xor rax,rsp                            |
….
000000014014BF83  | C64424 40 00      | mov byte ptr ss:[rsp+40],0             |
000000014014BF88  | 48:8D5424 40      | lea rdx,qword ptr ss:[rsp+40]          |
000000014014BF8D  | E8 1EEFFFFF       | call gsexplorer.14014AEB0              | gen_salt_seek 易见, 该函数根据加密数据生成一个字节 存入 qword ptr ss:[rsp+40]
000000014014BF92  | E8 591F1F00       | call gsexplorer.14033DEF0              | EVP_aes_256_cbc AES 加密
000000014014BF97  | 48:894424 48      | mov qword ptr ss:[rsp+48],rax          |
000000014014BF9C  | E8 7FEC1E00       | call gsexplorer.14033AC20              |
000000014014BFA1  | 48:8BD8           | mov rbx,rax                            |
000000014014BFA4  | 48:894424 50      | mov qword ptr ss:[rsp+50],rax          |
000000014014BFA9  | 48:8BC8           | mov rcx,rax                            |
000000014014BFAC  | E8 4FED1E00       | call gsexplorer.14033AD00              |
000000014014BFB1  | 33FF              | xor edi,edi                            |
000000014014BFB3  | 8BD7              | mov edx,edi                            |
000000014014BFB5  | 4C:8D4424 60      | lea r8,qword ptr ss:[rsp+60]           | 注意 salt, 生成过程
000000014014BFBA  | 44:0FB64C24 40    | movzx r9d,byte ptr ss:[rsp+40]         | r9d 为之前生成 salt seek, 见上 qword ptr ss:[rsp+40]
000000014014BFC0  | 0FB6C2            | movzx eax,dl                           |
000000014014BFC3  | 6BC8 07           | imul ecx,eax,7                         | edx: salt 索引 * 7
000000014014BFC6  | 41:02C9           | add cl,r9b                             | 上步结果与 r9b 相加存入 ecx
000000014014BFC9  | 41:8808           | mov byte ptr ds:[r8],cl                | salt 数组每位 ecx
000000014014BFCC  | FFC2              | inc edx                                |
000000014014BFCE  | 4D:8D40 01        | lea r8,qword ptr ds:[r8+1]             |
000000014014BFD2  | 83FA 08           | cmp edx,8                              | slat 8 位
000000014014BFD5  | 7C E9             | jl gsexplorer.14014BFC0                | 该循环生成 salt
000000014014BFD7  | 48:8B6D 00        | mov rbp,qword ptr ss:[rbp]             |

000000014014C002  | 8BF0              | mov esi,eax                            |
000000014014C004  | E8 87171E00       | call gsexplorer.14032D790              | EVP_sha1
000000014014C009  | 48:8BD0           | mov rdx,rax                            |
000000014014C00C  | 48:8D4424 68      | lea rax,qword ptr ss:[rsp+68]          |
000000014014C011  | 48:894424 38      | mov qword ptr ss:[rsp+38],rax          |
000000014014C016  | 48:8D4424 78      | lea rax,qword ptr ss:[rsp+78]          |
000000014014C01B  | 48:894424 30      | mov qword ptr ss:[rsp+30],rax          | count
000000014014C020  | C74424 28 0004000 | mov dword ptr ss:[rsp+28],400          | data_len
000000014014C028  | 897424 20         | mov dword ptr ss:[rsp+20],esi          |
000000014014C02C  | 4C:8BCD           | mov r9,rbp                             |
000000014014C02F  | 4C:8D4424 60      | lea r8,qword ptr ss:[rsp+60]           |
000000014014C034  | 48:8B7424 48      | mov rsi,qword ptr ss:[rsp+48]          |
000000014014C039  | 48:8BCE           | mov rcx,rsi                            |
000000014014C03C  | E8 EFE51E00       | call gsexplorer.14033A630              | EVP_BytesToKey 计算 key 和 iv
000000014014C041  | 85C0              | test eax,eax                           |

000000014014C086  | 48:8BCB           | mov rcx,rbx                            |
000000014014C089  | E8 02F81E00       | call gsexplorer.14033B890              | EVP_EncryptInit_ex 初始化加密方法
000000014014C08E  | 83F8 01           | cmp eax,1                              |
000000014014C091  | 74 2C             | je gsexplorer.14014C0BF                |
000000014014C093  | 41:C706 06000000  | mov dword ptr ds:[r14],6               |

000000014014C15A  | 4C:8D4424 44      | lea r8,qword ptr ss:[rsp+44]           |
000000014014C15F  | 48:8BD6           | mov rdx,rsi                            | rsi
000000014014C162  | 48:8B5C24 50      | mov rbx,qword ptr ss:[rsp+50]          |
000000014014C167  | 48:8BCB           | mov rcx,rbx                            | rcx:&”gs_zipfs_state”
000000014014C16A  | E8 51F71E00       | call gsexplorer.14033B8C0              | EVP_EncryptUpdate 加密数据段
000000014014C16F  | 48:637C24 44      | movsxd rdi,dword ptr ss:[rsp+44]       |
000000014014C174  | 48:8D143E         | lea rdx,qword ptr ds:[rsi+rdi]         |
000000014014C178  | 4C:8D4424 44      | lea r8,qword ptr ss:[rsp+44]           |
000000014014C17D  | 48:8BCB           | mov rcx,rbx                            | rcx:&”gs_zipfs_state”
000000014014C180  | E8 9BF51E00       | call gsexplorer.14033B720              | EVP_EncryptFinal_ex
000000014014C185  | 037C24 44         | add edi,dword ptr ss:[rsp+44]          |
000000014014C189  | 4C:63C7           | movsxd r8,edi                          |
000000014014C18C  | 0FB64424 40       | movzx eax,byte ptr ss:[rsp+40]         |
000000014014C191  | 41:880430         | mov byte ptr ds:[r8+rsi],al            | 最后将 salt seek 加入最后一个字节, 准备解密
000000014014C195  | FFC7              | inc edi                                |
000000014014C197  | 8BD7              | mov edx,edi                            |
000000014014C199  | 49:8BCF           | mov rcx,r15                            | rcx:&”gs_zipfs_state”
000000014014C19C  | E8 DF02EEFF       | call gsexplorer.14002C480              |
000000014014C1A1  | 40:B7 01          | mov dil,1                              |
000000014014C1A4  | 48:85DB           | test rbx,rbx                           |
000000014014C1A7  | 74 08             | je gsexplorer.14014C1B1                |
000000014014C1A9  | 48:8BCB           | mov rcx,rbx                            |
000000014014C1AC  | E8 2FEA1E00       | call gsexplorer.14033ABE0              | 注意 rsi 指针
000000014014C1B1  | 40:0FB6C7         | movzx eax,dil                          |
000000014014C1B5  | 48:8B8C24 9800000 | mov rcx,qword ptr ss:[rsp+98]          |
000000014014C1BD  | 48:33CC           | xor rcx,rsp                            |
000000014014C1C0  | E8 DBFD1D00       | call gsexplorer.14032BFA0              |
000000014014C1C5  | 48:81C4 A8000000  | add rsp,A8                             |
000000014014C1CC  | 41:5F             | pop r15                                |
000000014014C1CE  | 41:5E             | pop r14                                |
000000014014C1D0  | 41:5D             | pop r13                                |
000000014014C1D2  | 41:5C             | pop r12                                |
000000014014C1D4  | 5F                | pop rdi                                |
000000014014C1D5  | 5E                | pop rsi                                |
000000014014C1D6  | 5D                | pop rbp                                |
000000014014C1D7  | 5B                | pop rbx                                |
000000014014C1D8  | C3                | ret                                    | 复制代码

加密流程

1. 根据被加密数据生成一个 `salt seek`
2. 根据 `salt seek` 生成 `salt`, 使用该盐值与密钥根据 `sha1` 生成 `key` 和 `iv`
3. 使用 AES 加密数据
4. 将 `salt seek` 追加到加密结果
5. 使用 `base64` 加密数组, 这部分 GoodSync 稍微改变了 Base64 Table

解密过程

观察到 gsdata 文件夹和压缩包 gs_zipfs_state
GS 会在解密前先使用加密算法计算加密后的文件名, 从远端拉取这个文件回本地解密
该文件内包含同步信息, 例如被加密的文件名

项目

剩下的部分想必对于 18+ cm 的 mjj 是很自然的

考虑到程序的易用性, 写了一个 python 包供 mjj 们调用

安装只需要
python -m pip install goodsync 复制代码

项目开源, 希望能得到 mjj 们的星星

https://github.com/bitjerry/goodsync

项目只实现了加解密, 更多功能欢迎 PR, 如果你有其它语言实现也欢迎 PR

正文完
 0