ICEYOU/README.md

496 lines
22 KiB
Markdown

# ICEYOU - Personal Anti-Intrusion Monitor v:1.2
ICEYOU watches your Windows machine while you're away. The moment any input, USB event, or camera tamper happens after idle, it locks the screen behind a full-screen white overlay with a mandatory password / face-unlock gate, snaps a photo of whoever is at the keyboard, records what they typed, and emails you the evidence.
## Why do it
This project was derived for the traveler or the individual who has a creeper in their proximity. Individuals can allow the system to perform baseline security measures to ensure tampering is not being performed, at an event, a hotel, even at home. Usage is based on a lack of trust with accountability.
## What It Does
| Capability | Detail |
|---|---|
| Idle / away detection | Configurable idle timeout flips ICEYOU into "away" mode |
| Whiteout lockout | Full-screen overlay with embedded password + face unlock |
| Motion recording | Rolling 5s pre + event + 5s post AVI clips saved to `motion_clips/` while locked |
| Escape-key blocking | Low-level Windows keyboard hook blocks CTRL+ESC, ALT+TAB, WIN, ALT+F4, CTRL+SHIFT+ESC, etc. while locked |
| Snapshot on attempt | Webcam photo for every unlock attempt (success or failure) |
| Credential logging | Failed password attempts saved to `events.log` and emailed |
| Face recognition unlock | LBPH-based owner verification with `Unlock by Face` button + auto-attempt |
| Camera tamper detection | Triggers full lockdown if lens is covered while locked |
| USB / device monitor | Drive insertions/removals during away are treated as intrusions |
| Email alerts | SMTP with photo attachment + entered credentials |
| OS workstation lock | After N failed password attempts, Windows lock kicks in (whiteout reappears on resume) |
| System tray UI | Pause/resume monitoring, force away, send test email, open snapshots/log |
| Hotkeys | Force away & re-show unlock prompt globally |
---
## Security Notice (Important for Gitea / Remote Hosting)
**Never commit `config.json`** — it contains your unlock passphrase and email credentials.
- `config.json` is already listed in `.gitignore`.
- Only `config.example.json` (with placeholders) should ever be pushed to Gitea or any remote repository.
- Before pushing, always run:
```powershell
git status
git diff --cached --name-only
```
and verify that `config.json`, `events.log`, `faces/`, `snapshots/`, and `motion_clips/` do **not** appear.
If you accidentally stage `config.json`, use `git rm --cached config.json` immediately.
---
## Prerequisites
- **Windows 10 or 11** (the DirectShow camera backend and low-level keyboard hook are Windows-specific)
- **Python 3.11 or 3.12** (3.11 recommended for best OpenCV compatibility)
- A webcam (USB or built-in). Multiple cameras are supported.
- (Optional but recommended) A Gmail account with **App Password** enabled for email alerts
## Full Installation
### 1. Create and activate a virtual environment
```powershell
python -m venv .venv
.\.venv\Scripts\Activate.ps1
```
### 2. Install dependencies
```powershell
pip install --upgrade pip
pip install -r requirements.txt
```
> **Important**: ICEYOU requires `opencv-contrib-python` (for LBPH face recognition). The requirements file pins a compatible version.
### 3. Copy the configuration template
```powershell
copy config.example.json config.json
notepad.exe config.json
```
Edit at minimum:
- `unlock_password` — your secret phrase
- `email` section (if you want alerts)
- `camera_device_indices` — list the cameras you want to use (run the list command below to discover indices)
### 3.5 Configuring Gmail for Email Alerts (Requires 2FA)
Gmail no longer accepts regular account passwords for SMTP. You **must** use an **App Password**.
**Steps:**
1. Go to your Google Account → **Security**
2. Enable **2-Step Verification** (if not already enabled)
3. Under “Signing in to Google”, click **App passwords**
4. Select **Mail** as the app and **Other (Custom name)** → name it `ICEYOU`
5. Copy the **16-character App Password** that Google generates (e.g. `abcd efgh ijkl mnop`)
6. Paste it into `config.json`:
```json
"email": {
"enabled": true,
"smtp_server": "smtp.gmail.com",
"smtp_port": 587,
"use_tls": true,
"username": "your.email@gmail.com",
"password": "abcd efgh ijkl mnop", // ← the 16-char App Password (spaces optional)
"from_addr": "your.email@gmail.com",
"to_addr": "your.alerts@domain.com"
}
```
**Notes:**
- Regular password → “Username and Password not accepted” error.
- The App Password is **not** your normal Gmail password.
- You can revoke App Passwords anytime from the same Google Security page.
- Gmail often delivers the first few alerts to **Spam** — mark them “Not spam” so future alerts go to Inbox.
### 4. Discover available cameras (optional but recommended)
```powershell
python face_enrollment.py --list-cameras
```
Note the indices of your real webcams (ignore virtual cameras like OBS unless you want them).
### 5. (Strongly Recommended) Enroll your face for password-less unlock
Run the enrollment tool multiple times with different appearances / accessories / hair styles:
```powershell
# Basic enrollment (no accessories)
python face_enrollment.py --name owner
# With glasses
python face_enrollment.py --name owner_glasses
# With headphones
python face_enrollment.py --name owner_headphone
# With both glasses + headphones + hair in a bun
python face_enrollment.py --name owner_headphones_bun
```
Each run captures 5 poses (straight, left, right, up, down). The tool automatically retrains `faces/model.yml`.
**Tips for good enrollment**:
- Good, even lighting (avoid strong back-lighting)
- Neutral expression + slight smile
- Hold still for ~2 seconds per pose
- Remove the camera cover / ensure the lens is clean
After enrollment you should see:
- `faces/owner/` containing many `.png` images
- `faces/model.yml` (the trained model)
- `faces/labels.json`
### 6. Enable face unlock (optional)
In `config.json`:
```json
"face_recognition_enabled": true,
"face_unlock": {
"enabled": true,
"confidence_threshold": 70.0,
"min_successful_matches": 2,
"auto_attempt": true
}
```
- `min_successful_matches: 2` requires two successful frames before unlocking (protects against look-alikes).
- Lower `confidence_threshold` = stricter matching.
### 7. Run ICEYOU
```powershell
python main.py
```
A red-shield tray icon appears in the system tray. Right-click it for the full menu.
### 8. (Optional) Add to Windows Startup
Create a shortcut to `main.py` (or use the `startup.py` helper if present) and place it in:
```
%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup
```
Or run as a scheduled task (recommended for reliability).
## First Run Checklist
- [ ] `config.json` has a strong `unlock_password`
- [ ] Email settings tested with tray → **Send Test Email**
- [ ] At least one camera index works (`camera_device_indices`)
- [ ] Face model trained and `face_unlock.enabled: true` (if desired)
- [ ] White screen dimming settings reviewed (`white_screen_dimming`)
- [ ] Motion recording enabled (default = on)
- [ ] Test the full flow: Force away (`Ctrl+Alt+L`) → wait for whiteout → type wrong password → verify email arrives with snapshot
## Generated Files & Directories (Ignored by Git)
| Path | Purpose | Safe to delete? |
|-----------------------|----------------------------------------------|---------------------|
| `faces/` | Enrollment photos + trained `model.yml` | Yes (re-enroll) |
| `snapshots/` | Intrusion & unlock attempt photos | Yes |
| `motion_clips/` | Pre/post-motion AVI recordings | Yes |
| `events.log` | JSON audit trail of all events | Yes (rotates) |
| `config.json` | Your personal settings (never commit) | No (backup first) |
---
## Hotkeys & Manual Control
| Hotkey (default) | Action |
|---|---|
| `Ctrl+Alt+L` | Force away mode + show whiteout immediately |
| `Ctrl+Alt+U` | Re-show the whiteout password prompt (useful if it was hidden) |
Combos are configurable in `config.json → hotkeys` using pynput syntax (`<ctrl>+<shift>+<alt>+p`, etc.). Restart ICEYOU after editing.
---
## Tray Menu
| Item | Description |
|---|---|
| Start / Stop Monitoring | Toggle monitor thread |
| Toggle Away Mode | Manual away on/off (away forces the whiteout) |
| Toggle White Screen | Disable the whiteout while still monitoring |
| Open Snapshots Folder | Browse all captured photos |
| View Motion Clips | Open `motion_clips/` (post-auth in practice) |
| Open Log File | View `events.log` |
| **Send Test Email** | Sanity-check SMTP without triggering an intrusion |
| Settings | Opens `config.json` in Notepad (restart to apply) |
| Exit | Stops everything cleanly |
---
## Intrusion Response Flow
1. **Detect** — Idle ≥ `idle_timeout_seconds` (or you pressed `Ctrl+Alt+L`) → away mode + whiteout up.
2. **Trigger** — Any keyboard, mouse, USB, or camera-obscured event during away fires an intrusion:
- Snapshot captured (saved to `snapshots/`)
- `intrusion_detected` event logged
- Email alert sent with photo
- Whiteout stays up with the password / face-unlock prompt
3. **Locked state**`manual_lock` is set; ordinary activity **cannot** clear away mode. Only a correct unlock does.
4. **Password gate** — Cannot be cancelled or closed. After `max_unlock_attempts` (default 3) wrong passwords, Windows workstation lock kicks in. When the user returns from the Windows lock screen the **ICEYOU whiteout is still there** — unlocking Windows alone is not enough.
5. **Success** — Whiteout dismisses, failure counter resets, auto-away is snoozed briefly so you can work.
---
## Unlock Attempt Audit Trail
Every interaction with the unlock prompt is recorded:
- **Snapshot** of whoever is at the keyboard: `snapshots/snapshot_<timestamp>_unlock_<success|fail>_<password|face>.jpg`
- **Structured log entry** appended to `events.log` (one JSON object per line):
- `method``"password"` or `"face"`
- `success` — boolean
- `snapshot` — path to the photo
- `entered`**only on failed password attempts** — the literal text the intruder typed
- `label`, `confidence` — for face attempts
- **Email alert** (if `email.enabled`) with the snapshot attached:
- Failed password attempts include the typed text in the body
- Successful unlocks confirm but **never** include your real password
- Auto face attempts (every 4 s) are silent (no log / no email) to avoid flooding. Only **manual** `Unlock by Face` clicks and **successful** matches are recorded.
If alerts aren't arriving: tray → **Send Test Email** (or `python test_email.py`). SMTP errors land in `events.log` as `email_failed`. Gmail → external-domain alerts often hit **Spam** — check there first and mark "Not spam" to whitelist.
---
## Face Recognition Unlock
Optional second unlock path next to the password.
1. **Install the contrib OpenCV build** (required for `cv2.face`):
```powershell
pip install --upgrade --force-reinstall opencv-contrib-python
```
2. **Enroll** your face — multi-pose, multi-look:
```powershell
python face_enrollment.py --name owner
python face_enrollment.py --camera 1 --list-cameras
```
The capture window cycles through 5 poses (straight, left, right, up, down). Controls:
- **Click** the preview window — most reliable capture
- **SPACE / ENTER / C** — capture (window must have focus)
- Hold a steady face ≥ 2 s — auto-capture
- **R** restart, **Q / ESC** quit
For glasses / headphones / hats, **re-run with the same `--name`** so LBPH learns all your looks under one identity. Each session appends to `faces/<label>/capture_NN.png` and re-trains `faces/model.yml`.
3. **Enable in `config.json`**:
```json
"face_recognition_enabled": true,
"face_unlock": {
"enabled": true,
"confidence_threshold": 85.0,
"auto_attempt": true,
"attempt_interval_seconds": 4.0,
"frames_per_attempt": 4,
"allowed_labels": ["owner"]
}
```
- `confidence_threshold`**lower = stricter** (LBPH distance). 50 = very strong, 70 = balanced, 85 = lenient. Tune based on the `conf=…` value shown on misses.
- `allowed_labels` — list of enrolled identities allowed to unlock. `null` = any enrolled label.
- `auto_attempt` — background tries every `attempt_interval_seconds` while the whiteout is up.
While the whiteout is shown:
- Auto-attempts run silently every 4 s (default).
- The **Unlock by Face** button lets you trigger a manual attempt (this is logged + emailed; auto attempts are not).
- Face failures do **not** count toward the 3-strike OS-lock — only typed passwords do, so a bad camera angle can never lock you out.
**Status hint**: failed face attempts show `Face not recognised (best=<label>, conf=<value>)` on the whiteout. If you consistently see `conf` slightly above your threshold, either raise the threshold or enroll more samples.
---
## Camera Tamper Detection
When the whiteout is active, ICEYOU periodically samples the camera (default every 12 s). If the frame mean brightness or pixel variance drops below thresholds (lens covered, sticker over webcam, etc.), it immediately triggers full lockdown — email alert + OS workstation lock — **without** waiting for input. Configure in `config.json → camera_obscured_detection`.
---
## Motion Recording
While the whiteout is up, [src/iceyou/motion_recorder.py](src/iceyou/motion_recorder.py) continuously samples the webcam and keeps a **rolling 5 s pre-buffer** of frames. When motion is detected (frame differencing above `sensitivity`), it writes the buffered pre-roll + the event + 5 s of post-roll to `motion_clips/motion_<timestamp>.avi` (MJPG codec). Each finalized clip:
- Logs a `motion_recorded` line to `events.log` with the clip path.
- Pops a system-tray notification ("Motion event saved: motion_…avi").
- Does **not** trigger an email (snapshots + unlock attempts already cover that).
Recording starts on `WhiteScreen.show()` and stops on `WhiteScreen.hide()` — there is no recording when ICEYOU is not in the locked state.
**Viewing clips post-authentication:** the tray menu has a **View Motion Clips** item that opens the `motion_clips/` folder in Explorer. While the whiteout is up the escape-key hook prevents the intruder from reaching the tray icon, so this item is effectively post-authentication. Once you unlock, right-click the tray icon and pick **View Motion Clips** to review.
Config (`config.json → motion_recording`):
```jsonc
"motion_recording": {
"enabled": true,
"clips_dir": "motion_clips",
"fps": 15,
"pre_seconds": 5.0,
"post_seconds": 5.0,
"sensitivity": 2500, // min contour area in pixels; lower = more sensitive
"cooldown_seconds": 8.0 // min gap between separate clips
}
```
---
## Escape-Key Blocking
While the whiteout is shown, a low-level Windows keyboard hook ([src/iceyou/keyboard_block.py](src/iceyou/keyboard_block.py)) swallows:
- `WIN` (left + right)
- `CTRL+ESC` (Start menu)
- `ALT+TAB`, `ALT+ESC` (task switching)
- `ALT+F4` (close window)
- `ALT+SPACE` (window menu)
- `CTRL+SHIFT+ESC` (Task Manager hotkey)
Disable with `"block_escape_keys": false` if needed (e.g. development). The hook is removed automatically on unlock.
**Hard limits (Windows-enforced, cannot be intercepted from user mode):**
- `CTRL+ALT+DEL` — Secure Attention Sequence is kernel-enforced. An intruder can still reach the security screen → Task Manager → kill `python.exe`.
- Hard power off, pulling the keyboard.
- Logging in as a different Windows user.
To harden against CTRL+ALT+DEL → Task Manager, run as Administrator and set the registry value `HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\System\DisableTaskMgr = 1 (DWORD)`. ICEYOU does **not** apply this automatically because it's a system-wide setting affecting every program.
---
## Configuration Reference (`config.json`)
```jsonc
{
"idle_timeout_seconds": 100, // Idle seconds before auto-away
"monitoring_enabled": true, // Master on/off (start in monitoring mode)
"camera_device_index": 1, // (Legacy single-cam fallback - used only if camera_device_indices is omitted)
"camera_device_indices": [0, 1], // Multi-cam: probe each of these. Dead indices are auto-skipped.
"camera_probe_timeout_seconds": 2.5, // Per-camera open+first-frame budget; phantom devices that block longer are marked DEAD
"snapshot_on_trigger": true, // Save a photo on every intrusion / attempt (one per alive camera)
"snapshot_dir": "snapshots",
"log_file": "events.log",
"email": {
"enabled": true,
"smtp_server": "smtp.gmail.com",
"smtp_port": 587,
"use_tls": true,
"username": "you@gmail.com",
"password": "YOUR_16_CHAR_APP_PASSWORD", // Gmail App Password (requires 2FA)
"from_addr": "you@gmail.com",
"to_addr": "alerts@you.com",
"subject_prefix": "[ICEYOU Alert]"
},
"lock_workstation": true, // OS lock after max_unlock_attempts password fails
"device_monitoring": true, // Watch USB drives
"white_screen_on_away": true, // Show the whiteout when away
"unlock_password": "CHANGE_ME_STRONG_PASSPHRASE", // Your secret phrase
"max_unlock_attempts": 3, // Wrong passwords before OS lock
"block_escape_keys": true, // Install OS keyboard hook while locked
"camera_obscured_detection": {
"enabled": true,
"brightness_threshold": 25,
"variance_threshold": 10,
"check_interval_seconds": 6
},
"hotkeys": {
"force_away": "<ctrl>+<alt>+l",
"unlock": "<ctrl>+<alt>+u"
},
"face_recognition_enabled": true,
"face_unlock": {
"enabled": true,
"confidence_threshold": 85.0, // Lower = stricter (LBPH distance)
"auto_attempt": true,
"attempt_interval_seconds": 4.0,
"frames_per_attempt": 4,
"allowed_labels": ["owner"] // null = accept any enrolled label
}
}
```
---
## Troubleshooting
| Symptom | Fix |
|---|---|
| No tray icon | Make sure `python main.py` is running; check `events.log` for startup errors. |
| Email alerts not arriving | Check Spam folder. Use **Send Test Email** in tray (or `python test_email.py`). Look for `email_failed` in `events.log`. |
| Face unlock never matches | Status line shows `conf=<value>`. Either raise `confidence_threshold` in `config.json` or re-enroll with more samples / better lighting / same accessories you're wearing now. |
| Password field clears mid-typing | Already fixed — input while the whiteout is up is treated as challenge, not re-intrusion. Pull latest `monitor.py` + `white_screen.py`. |
| Tk error `Tcl_AsyncDelete: async handler deleted by the wrong thread` | Already fixed — `WhiteScreen` uses a persistent Tk root on a dedicated thread. |
| Tk error `can't set fullscreen attribute… override-redirect flag is set` | Already fixed — `WhiteScreen` sizes to screen via `geometry()` instead of `-fullscreen`. |
| `numpy<2` conflict with `opencv-contrib-python` | Use the recommended `.venv` virtualenv. |
| Antivirus flags ICEYOU | Low-level keyboard hooks trigger heuristics. Whitelist the project folder. |
---
## Project Structure
```
ICEYOU/
├── main.py Tray app entrypoint
├── face_enrollment.py Standalone face capture + LBPH training
├── test_email.py CLI SMTP sanity check
├── config.json Runtime settings (gitignored)
├── config.example.json Template
├── events.log JSON-lines event audit trail
├── snapshots/ All captured webcam photos
├── motion_clips/ 5s-before + event + 5s-after AVI clips from the whiteout
├── faces/
│ ├── model.yml Trained LBPH model
│ ├── labels.json Label id → name map
│ └── <label>/capture_NN.png Enrolled photos per identity
└── src/iceyou/
├── tray_app.py System tray + wiring
├── monitor.py Away state machine + intrusion trigger
├── white_screen.py Persistent Tk fullscreen lockout + unlock UI
├── face_recognizer.py LBPH recognizer with multi-crop verification
├── motion_recorder.py Rolling-buffer motion clips while locked
├── keyboard_block.py Low-level Windows keyboard hook
├── camera.py Webcam capture, snapshots, obscured detection
├── input_hooks.py pynput keyboard + mouse global hooks
├── device_monitor.py Drive insertion/removal poller
├── actions.py Email alerts (intrusion + unlock attempts)
├── utils.py Win32 idle time, lock workstation, timestamps
├── startup.py HKCU Run registry add/remove
└── config.py Deep-merged config loader with dotted-key get
```
---
## Privacy & Use
ICEYOU accesses your camera, keyboard, mouse, and writes intruder photos + entered text to local disk and email. It is intended **strictly for protecting your own machine**. Do not use it for unauthorized surveillance of others.
---
## Roadmap
- Encrypted at-rest storage of snapshots and log
- Optional cloud upload of evidence
- Windows service mode (run without an interactive session)
- Web dashboard for remote review
- DNN-based face recognition (FaceNet / dlib) for higher accuracy than LBPH