Compare commits

..
262 Commits
Author SHA1 Message Date
87owo c220035fd9 Add files via upload 2026-05-21 00:27:56 +08:00
87owo d7b98d3cde Add files via upload 2026-05-21 00:27:38 +08:00
87owo 0ab876633f Add files via upload 2026-05-21 00:27:01 +08:00
87owo cabe76ca54 Add files via upload 2026-05-21 00:26:14 +08:00
87owo c57224e258 Add files via upload 2026-05-21 00:25:35 +08:00
87owo b56101d2e6 Add files via upload 2026-05-18 21:31:43 +08:00
87owo 16211c28d1 Add files via upload 2026-05-17 16:48:13 +08:00
87owo 139d3e0c3d Add files via upload 2026-05-17 16:47:14 +08:00
87owo f0652fc5ec Add files via upload 2026-05-17 01:32:52 +08:00
87owo 55489cd634 Add files via upload 2026-05-17 01:31:49 +08:00
87owo 742887c0e4 Add files via upload 2026-05-15 22:34:23 +08:00
87owo c3e5cd2c0d Add files via upload 2026-05-15 22:33:13 +08:00
87owo 63e125627c Update README.md 2026-05-15 14:39:15 +08:00
87owo c0189e9d05 Add files via upload 2026-05-14 22:31:46 +08:00
87owo 133e3208ec Add files via upload 2026-05-14 20:02:53 +08:00
87owo 17c6fd6d0f Add files via upload 2026-05-13 18:37:07 +08:00
87owo 2aa7ea43b8 Add files via upload 2026-05-13 17:56:05 +08:00
87owo 6d7dbfac31 Add files via upload 2026-05-13 00:51:04 +08:00
87owo d4942c6dd6 Add files via upload 2026-05-13 00:49:54 +08:00
87owo 39ae3c24f6 Add files via upload 2026-05-12 22:41:11 +08:00
87owo a04d74a949 Add files via upload 2026-05-10 17:54:55 +08:00
87owo b93a7ebd94 Add files via upload 2026-05-10 17:45:08 +08:00
87owo cbc7b0340f Add files via upload 2026-05-10 17:38:28 +08:00
87owo ea7a197848 Add files via upload 2026-05-10 17:31:14 +08:00
87owo 82d4df889e Add files via upload 2026-05-10 13:06:24 +08:00
87owo df3a2a1010 Add files via upload 2026-05-10 13:06:05 +08:00
87owo 67cd6c2f59 Add files via upload 2026-05-10 02:30:07 +08:00
87owo b1788e7df9 Add files via upload 2026-05-10 02:28:27 +08:00
87owo 1cb8a81518 Add files via upload 2026-05-10 00:06:45 +08:00
87owo e1b61f2675 Update README.md 2026-05-09 22:53:01 +08:00
87owo 834d44856c Add files via upload 2026-05-09 22:49:48 +08:00
87owo c088eeb5bc Add files via upload 2026-05-09 22:47:09 +08:00
87owo 921510b7b7 Add files via upload 2026-05-09 22:27:36 +08:00
87owo 17a54f7a8a Add files via upload 2026-05-08 20:02:34 +08:00
87owo 43ff924efc Add files via upload 2026-05-08 18:24:36 +08:00
87owo f9bd4b6628 Add files via upload 2026-05-08 18:20:52 +08:00
87owo 702aa22139 Delete Interface/static/js/style.css 2026-05-08 18:20:37 +08:00
87owo 57d53f63e5 Add files via upload 2026-05-08 18:17:20 +08:00
87owo 835ada4a53 Add files via upload 2026-05-08 13:51:08 +08:00
87owo 7f204f3e1b Add files via upload 2026-05-08 10:53:11 +08:00
87owo 70e13b8414 Delete Engine/Properties/train_server_lfp.py 2026-05-07 14:13:19 +08:00
87owo df9bb8cbb8 Add files via upload 2026-05-07 14:12:56 +08:00
87owo cebdb32502 Add files via upload 2026-05-07 13:58:44 +08:00
87owo 58bd61343a Add files via upload 2026-05-06 14:27:54 +08:00
87owo 90faf1007e Add files via upload 2026-05-06 14:14:48 +08:00
87owo f8dbd12b69 Add files via upload 2026-05-06 12:54:54 +08:00
87owo cf6ba66dd2 Add files via upload 2026-05-04 23:29:36 +08:00
87owo 2080b5d10a Add files via upload 2026-05-04 23:29:14 +08:00
87owo dd180977d9 Add files via upload 2026-05-04 23:28:48 +08:00
87owo 221dcbaf1b Add files via upload 2026-05-04 23:28:13 +08:00
87owo 1a2098f4fb Add files via upload 2026-05-04 23:27:50 +08:00
87owo 73eab17110 Add files via upload 2026-05-03 11:34:05 +08:00
87owo 978a336358 Update README.md 2026-05-03 03:27:57 +08:00
87owo 6a0fd4e427 Update README.md 2026-05-03 03:23:58 +08:00
87owo 5fd4efd920 Add files via upload 2026-05-03 03:20:31 +08:00
87owo 43be2987f5 Add files via upload 2026-05-02 17:47:38 +08:00
87owo 06cc048fdc Add files via upload 2026-05-02 17:47:03 +08:00
87owo 5752471420 Add files via upload 2026-05-02 16:16:46 +08:00
87owo 3dfe12b90a Add files via upload 2026-05-02 15:48:47 +08:00
87owo ad1e3501b1 Add files via upload 2026-05-02 15:33:52 +08:00
87owo 1b46572e4c Add files via upload 2026-05-02 15:09:42 +08:00
87owo 9d8e42d34c Add files via upload 2026-05-02 15:09:28 +08:00
87owo 3f6e9dab3e Add files via upload 2026-05-02 15:09:06 +08:00
87owo ad85f11875 Add files via upload 2026-05-02 01:49:17 +08:00
87owo 9ae31834cc Add files via upload 2026-05-02 01:16:32 +08:00
87owo 49a4dc6942 Add files via upload 2026-05-02 01:16:05 +08:00
87owo a9f58cc30f Add files via upload 2026-05-02 01:15:51 +08:00
87owo bdb2198582 Add files via upload 2026-05-02 01:15:28 +08:00
87owo f32a77ca8b Add files via upload 2026-05-01 23:16:30 +08:00
87owo 121d3fb0ae Add files via upload 2026-05-01 23:15:59 +08:00
87owo 1dd72f7600 Add files via upload 2026-05-01 23:15:22 +08:00
87owo c7c168b007 Add files via upload 2026-05-01 23:14:45 +08:00
87owo 441b41c6af Add files via upload 2026-05-01 23:13:56 +08:00
87owo 49a907283a Add files via upload 2026-05-01 15:41:33 +08:00
87owo 0ffc52f9ba Add files via upload 2026-05-01 13:10:33 +08:00
87owo 4e96ec49e7 Update README.md 2026-04-29 21:04:53 +08:00
87owo dfb4646f51 Delete Embed directory 2026-04-29 21:04:30 +08:00
87owo a8e82c38c9 Add files via upload 2026-04-29 18:09:24 +08:00
87owo dba23289fd Add files via upload 2026-04-29 17:41:00 +08:00
87owo c2c1eb98d9 Add files via upload 2026-04-29 16:19:44 +08:00
87owo 364176912d Add files via upload 2026-04-28 14:38:35 +08:00
87owo 202245baad Add files via upload 2026-04-28 14:10:07 +08:00
87owo c3fa99640c Add files via upload 2026-04-27 15:03:53 +08:00
87owo a7c34b5a6a Add files via upload 2026-04-27 10:19:47 +08:00
87owo 05de092642 Update README.md 2026-04-26 22:35:28 +08:00
87owo 1bfa40c841 Add files via upload 2026-04-26 21:13:42 +08:00
87owo c325435ae8 Add files via upload 2026-04-26 15:41:47 +08:00
87owo b978bd41c3 Add files via upload 2026-04-26 14:31:23 +08:00
87owo f03d8733b0 Add files via upload 2026-04-26 14:30:49 +08:00
87owo a624b3b759 Update README.md 2026-04-26 14:29:57 +08:00
87owo d683398ba9 Add files via upload 2026-04-26 14:28:24 +08:00
87owo 42f692b82d Add files via upload 2026-04-26 14:27:33 +08:00
87owo 855fe9115e Delete Engine/Pattern directory 2026-04-26 14:26:30 +08:00
87owo d6bd383822 Add files via upload 2026-04-26 02:13:58 +08:00
87owo b1b77b73ac Add files via upload 2026-04-25 21:45:46 +08:00
87owo 8d87adcc50 Add files via upload 2026-04-25 21:45:32 +08:00
87owo 49bf6c54e7 Delete Interface/main.py 2026-04-25 20:53:22 +08:00
87owo 7e92e0b1d9 Add files via upload 2026-04-25 20:52:55 +08:00
87owo f0805d320c Add files via upload 2026-04-25 19:19:36 +08:00
87owo 364f48a6f0 Update README.md 2026-04-25 16:55:34 +08:00
87owo cfebf6df0e Update README.md 2026-04-25 16:40:38 +08:00
87owo 688855b72d Update README.md 2026-04-25 16:38:08 +08:00
87owo 77b275a04b Update README.md 2026-04-25 16:30:11 +08:00
87owo 98b142df12 Update README.md 2026-04-25 16:01:11 +08:00
87owo c5fe845f18 Delete PYAS_Resource.py 2026-04-25 15:59:56 +08:00
87owo e5e9629d31 Delete PYAS_Interface.py 2026-04-25 15:59:48 +08:00
87owo b65b9e6bfb Delete PYAS_Config.py 2026-04-25 15:59:26 +08:00
87owo 93ae8b02e3 Add files via upload 2026-04-25 15:55:56 +08:00
87owo 5c183ea200 Add files via upload 2026-04-25 15:54:58 +08:00
87owo 661765f08c Add files via upload 2026-04-25 15:54:33 +08:00
87owo 60fa19c681 Add files via upload 2026-04-25 15:53:27 +08:00
87owo 606eddba53 Add files via upload 2026-04-21 14:56:52 +08:00
87owo 71605ade31 Add files via upload 2026-04-19 02:21:41 +08:00
87owo ee409e4872 Add files via upload 2026-04-18 01:41:13 +08:00
87owo 91024abc4a Add files via upload 2026-04-15 19:59:41 +08:00
87owo 99de36a6cf Add files via upload 2026-04-15 19:57:35 +08:00
87owo ac1e48dc30 Add files via upload 2026-04-12 13:49:40 +08:00
87owo 494bab3380 Add files via upload 2026-04-12 13:42:38 +08:00
87owo 6696ca93ce Add files via upload 2026-04-11 16:50:33 +08:00
87owo b7e80fb8ed Add files via upload 2026-04-10 17:47:07 +08:00
87owo ce5b642d3e Add files via upload 2026-04-10 15:50:15 +08:00
87owo 76d414164a Update PYAS.py 2026-04-10 15:16:07 +08:00
87owo b6d13047ba Merge pull request #40 from MichaelMVS/fix-bare-except-clauses
Fix bare except clauses to catch only Exception types
2026-04-10 15:05:22 +08:00
87owo f1ba295796 Update README.md 2026-04-10 14:45:49 +08:00
zocomputer 415abef4fc Fix bare except clauses to catch only Exception types
Replace bare 'except:' with 'except Exception:' in three locations:
- check_process_survival (line ~1632): when accessing pe.szExeFile
- get_process_name (line ~1782): when getting process name from snapshot
- pipe_server_thread (line ~2340): when terminating malicious processes

Bare except clauses also catch SystemExit and KeyboardInterrupt,
which can prevent proper shutdown and mask serious issues.
Using 'except Exception:' allows the code to properly handle
expected errors while letting system-exiting events propagate.
2026-04-10 06:40:26 +00:00
87owo 95af2014d8 Update README.md 2026-04-10 14:39:38 +08:00
87owo f75b039daf Add files via upload 2026-04-10 14:35:02 +08:00
87owo 65b1ac78f3 Rename train_vt.py to train_server_lfp.py 2026-04-10 14:27:44 +08:00
87owo b067591882 Add files via upload 2026-04-10 14:24:58 +08:00
87owo 7941d76fd7 Delete Engine/Properties/train.py 2026-04-10 14:13:24 +08:00
87owo 7bff601beb Add files via upload 2026-04-10 14:12:27 +08:00
87owo 7b4da32bee Update README.md 2026-04-10 14:00:42 +08:00
87owo bbf2974703 Update README.md 2026-04-10 13:50:28 +08:00
87owo f5abaf06de Add files via upload 2026-04-08 17:57:14 +08:00
87owo 88023dd09e Add files via upload 2026-04-08 01:20:52 +08:00
87owo eec2996fab Add files via upload 2026-04-07 23:53:35 +08:00
87owo 97ca200c29 Add files via upload 2026-04-07 23:52:55 +08:00
87owo f0600ea3a3 Add files via upload 2026-04-07 23:52:20 +08:00
87owo 19b3eb51d7 Add files via upload 2026-04-04 16:02:36 +08:00
87owo 871dabf142 Add files via upload 2026-04-02 01:30:02 +08:00
87owo 58795b9349 Add files via upload 2026-04-01 23:42:12 +08:00
87owo 1ef769842b Add files via upload 2026-04-01 22:45:57 +08:00
87owo 6051570414 Add files via upload 2026-04-01 22:45:18 +08:00
87owo da5d5d0dc7 Add files via upload 2026-03-31 19:39:18 +08:00
87owo 782aa22d4d Add files via upload 2026-03-31 19:14:08 +08:00
87owo 0084867f38 Add files via upload 2026-03-29 21:14:20 +08:00
87owo 5a27858f86 Add files via upload 2026-03-27 02:17:54 +08:00
87owo 7905d8375f Add files via upload 2026-03-25 15:59:26 +08:00
87owo ba8aa63d75 Add files via upload 2026-03-16 15:10:16 +08:00
87owo ac82778e26 Add files via upload 2026-03-11 17:55:19 +08:00
87owo 55d2661e38 Add files via upload 2026-03-11 17:37:54 +08:00
87owo 4803b89294 Update train.py 2026-03-11 16:05:26 +08:00
87owo e48adbd87d Add files via upload 2026-03-11 16:00:07 +08:00
87owo 227314d83c Add files via upload 2026-03-08 03:53:52 +08:00
87owo 3c0e21eb02 Add files via upload 2026-03-08 03:42:55 +08:00
87owo 86d6153ef1 Add files via upload 2026-03-08 02:56:01 +08:00
87owo 78cf8bded3 Add files via upload 2026-03-07 12:50:56 +08:00
87owo 26d12181cd Add files via upload 2026-03-07 12:49:07 +08:00
87owo 8d184b011c Add files via upload 2026-03-01 18:40:56 +08:00
87owo ed3a1444ec Add files via upload 2026-03-01 18:19:34 +08:00
87owo d3e70a47cd Add files via upload 2026-03-01 18:19:08 +08:00
87owo 3171151219 Add files via upload 2026-03-01 18:16:04 +08:00
87owo e407867635 Add files via upload 2026-03-01 17:46:39 +08:00
87owo 6d9cfab55f Add files via upload 2026-02-26 02:50:09 +08:00
87owo f0bce029f0 Add files via upload 2026-02-26 01:21:41 +08:00
87owo d47960a0cf Add files via upload 2026-02-26 01:20:03 +08:00
87owo 8416018afe Add files via upload 2026-02-23 17:48:40 +08:00
87owo e93fc70169 Add files via upload 2026-02-23 17:47:50 +08:00
87owo 5b3474c781 Add files via upload 2026-02-23 01:04:21 +08:00
87owo 8d2a9f3b2b Add files via upload 2026-02-20 14:14:19 +08:00
87owo 73d09f9b09 Add files via upload 2026-02-20 13:49:18 +08:00
87owo 4e7c6efa2a Add files via upload 2026-02-20 13:46:13 +08:00
87owo 81a884bc61 Update PYAS_Engine.py 2026-02-18 23:56:27 +08:00
87owo ac4f960352 Add files via upload 2026-02-10 17:45:23 +08:00
87owo e4b3e4e438 Add files via upload 2026-02-10 17:44:48 +08:00
87owo d6ee476296 Add files via upload 2026-02-10 17:44:15 +08:00
87owo c23741ba9a Add files via upload 2026-02-10 17:43:47 +08:00
87owo 7bac5b4740 Add files via upload 2026-02-10 17:43:04 +08:00
87owo 1fe440168e Add files via upload 2026-02-04 23:11:56 +08:00
87owo 26ad57138b Add files via upload 2026-02-04 23:11:14 +08:00
87owo 92e1fdaf0f Add files via upload 2026-02-04 23:09:59 +08:00
87owo 357540d0ad Update README.md 2026-01-24 01:19:33 +08:00
87owo 405622a91e Update README.md 2026-01-24 01:14:02 +08:00
87owo 5686c73c56 Update README.md 2026-01-24 01:11:38 +08:00
87owo 76e2f787ac Update README.md 2026-01-24 01:10:28 +08:00
87owo 02b67f4e6a Update README.md 2026-01-24 01:06:59 +08:00
87owo a2ab527761 Update README.md 2026-01-24 01:05:17 +08:00
87owo f1142ae742 Add files via upload 2026-01-23 14:48:01 +08:00
87owo decb222dac Add files via upload 2026-01-23 14:25:34 +08:00
87owo 00c598e6f7 Add files via upload 2026-01-23 14:25:11 +08:00
87owo e9843dd3a5 Add files via upload 2026-01-23 14:24:27 +08:00
87owo 37f390742b Add files via upload 2026-01-23 14:23:14 +08:00
87owo 533e7dfd15 Add files via upload 2026-01-23 13:49:06 +08:00
87owo ddcbe1cd51 Delete Engine directory 2026-01-23 13:46:15 +08:00
87owo 4eabd04bc6 Add files via upload 2026-01-23 13:44:24 +08:00
87owo c8f2da6ac9 Add files via upload 2026-01-23 13:43:17 +08:00
87owo 8820ac4bf3 Add files via upload 2026-01-23 13:42:37 +08:00
87owo 236b200f39 Add files via upload 2026-01-12 14:28:24 +08:00
87owo 6789e32674 Add files via upload 2026-01-12 14:27:37 +08:00
87owo 9eb55deedf Add files via upload 2026-01-12 13:29:30 +08:00
87owo 6e76b5b8eb Add files via upload 2026-01-12 13:28:04 +08:00
87owo 491b0da83d Add files via upload 2026-01-10 22:48:17 +08:00
87owo 5d2c17e940 Add files via upload 2026-01-10 22:47:35 +08:00
87owo 0497c4a9e7 Add files via upload 2026-01-10 20:05:44 +08:00
87owo 6d0765848b Add files via upload 2026-01-10 20:03:39 +08:00
87owo 40c4ae71bd Merge pull request #35 from secretlay3r/main
refactor: replace ifs with dicts/sets + simplify exception handling
2026-01-10 19:37:25 +08:00
secretlay3r 8edba10dff Update PYAS.py
backup_mbr():
- Shorten except blocks logic into one
- Add len(mbr) == 512 check  
- Add MBR_SIGNATURE instead of just b"\x55\xAA"

pipe_server_thread():
- Replace whole ifs chain with RULES dict
- Replace [4001, 5001] with KILL_CODES set (for readability)

get_window_info()  and  get_process_name():
- Extract process name logic into private _get_process_name()
2026-01-10 14:17:27 +03:00
87owo 85aa7bc652 Add files via upload 2026-01-09 14:05:47 +08:00
87owo 854ad1db07 Add files via upload 2026-01-09 00:11:30 +08:00
87owo 955600a1e1 Add files via upload 2026-01-07 22:19:13 +08:00
87owo b739134d85 Add files via upload 2026-01-07 22:18:36 +08:00
87owo 723152566d Add files via upload 2026-01-07 21:10:34 +08:00
87owo 678b045e0e Update README.md 2026-01-07 17:47:13 +08:00
87owo 681f4df49a Add files via upload 2026-01-07 17:44:23 +08:00
87owo b19f7d7098 Add files via upload 2026-01-06 15:11:42 +08:00
87owo daaae3ec10 Add files via upload 2026-01-06 14:43:06 +08:00
87owo c14957afff Update and rename convert_full.py to convert.py 2026-01-04 22:34:35 +08:00
87owo 843a4998f1 Delete Engine/Model/convert.py 2026-01-04 20:45:08 +08:00
87owo 9637e74edc Add files via upload 2026-01-04 20:17:23 +08:00
87owo b1bdf4f671 Add files via upload 2026-01-04 18:55:43 +08:00
87owo 238ed8e364 Add files via upload 2026-01-04 18:55:04 +08:00
87owo 8aa1509863 Add files via upload 2026-01-04 13:41:45 +08:00
87owo aac182e30b Add files via upload 2026-01-04 13:40:19 +08:00
87owo ac7e953acc Add files via upload 2026-01-04 12:28:25 +08:00
87owo 6ae0f27e05 Add files via upload 2026-01-04 11:46:45 +08:00
87owo f6e5a92400 Add files via upload 2026-01-02 22:27:59 +08:00
87owo 5e5ff3428c Add files via upload 2026-01-02 22:26:42 +08:00
87owo 1cdbb9c402 Add files via upload 2026-01-02 22:25:22 +08:00
87owo 8caacd1870 Update README.md 2026-01-01 21:38:30 +08:00
87owo cfbc4c9c20 Update README.md 2026-01-01 21:29:21 +08:00
87owo 76dbf1c5d0 Add files via upload 2026-01-01 20:38:03 +08:00
87owo c0e7c4afa5 Add files via upload 2026-01-01 18:31:39 +08:00
87owo 9da0970d85 Delete ProtectRules.cpp 2026-01-01 18:31:01 +08:00
87owo d6f2ad32a7 Delete ProtectRegistry.cpp 2026-01-01 18:30:54 +08:00
87owo 8dbc87a331 Delete ProtectProcess.cpp 2026-01-01 18:30:45 +08:00
87owo 77eb1ce44e Delete ProtectFile.cpp 2026-01-01 18:30:37 +08:00
87owo e12f4ae61a Delete ProtectBoot.cpp 2026-01-01 18:30:29 +08:00
87owo c23a73625f Delete DriverEntry.cpp 2026-01-01 18:30:20 +08:00
87owo 3a66946686 Delete DriverCommon.h 2026-01-01 18:30:13 +08:00
87owo 7fef43701f Add files via upload 2026-01-01 18:29:52 +08:00
87owo 7f5eb34ac6 Add files via upload 2026-01-01 17:41:40 +08:00
87owo ba4369f829 Add files via upload 2026-01-01 03:39:49 +08:00
87owo 81af2e51fc Delete Plugins/Filter/ProtectScreen.c 2026-01-01 03:37:53 +08:00
87owo dff09064c8 Delete Plugins/Filter/ProtectRules.h 2026-01-01 03:37:45 +08:00
87owo ee0bb32cce Delete Plugins/Filter/ProtectRules.c 2026-01-01 03:37:35 +08:00
87owo 4a215c308b Delete Plugins/Filter/ProtectRegistry.c 2026-01-01 03:37:18 +08:00
87owo 79493a96ea Delete Plugins/Filter/ProtectRemote.c 2026-01-01 03:37:07 +08:00
87owo 3cca1c4671 Delete Plugins/Filter/ProtectProcess.c 2026-01-01 03:36:54 +08:00
87owo 72e9923d08 Delete Plugins/Filter/ProtectMatch.h 2026-01-01 03:36:43 +08:00
87owo 406e152891 Delete Plugins/Filter/ProtectMatch.c 2026-01-01 03:36:27 +08:00
87owo e9eb569d50 Delete Plugins/Filter/ProtectInject.c 2026-01-01 03:36:17 +08:00
87owo 640b389e44 Delete Plugins/Filter/ProtectImage.c 2026-01-01 03:36:07 +08:00
87owo 314e3349d6 Delete Plugins/Filter/ProtectFile.c 2026-01-01 03:35:56 +08:00
87owo 0ae4d72bc9 Delete Plugins/Filter/ProtectBoot.c 2026-01-01 03:35:45 +08:00
87owo 8920a5fb21 Delete Plugins/Filter/DriverPipe.c 2026-01-01 03:35:34 +08:00
87owo b338ff5dd9 Delete Plugins/Filter/DriverEntry.h 2026-01-01 03:35:25 +08:00
87owo 87e2fcb37c Delete Plugins/Filter/DriverEntry.c 2026-01-01 03:35:05 +08:00
87owo bab43e3b17 Add files via upload 2026-01-01 03:34:44 +08:00
87owo e602088330 Add files via upload 2025-12-29 20:14:49 +08:00
87owo 2f6db92077 Add files via upload 2025-12-29 20:14:14 +08:00
87owo cee33c0f77 Add files via upload 2025-12-28 14:58:49 +08:00
87owo 4fcdaa10b6 Add files via upload 2025-12-28 14:54:52 +08:00
49 changed files with 10985 additions and 290260 deletions

No files matched your search

File renamed without changes.
-68
View File
@@ -1,68 +0,0 @@
from PIL import Image
import numpy, pefile, os
####################################################################################################
def preprocess_image(file_data, size, channels=1):
width, height = size
wah = int(numpy.ceil(numpy.sqrt(len(file_data) / channels)))
arr = numpy.frombuffer(file_data, dtype=numpy.uint8)
img = numpy.zeros(wah*wah*channels, dtype=numpy.uint8)
img[:len(file_data)] = arr
image = Image.fromarray(img.reshape((wah, wah)), 'L')
return image.resize((width, height), Image.Resampling.NEAREST)
####################################################################################################
def is_text_file(content, sample_size=1024):
raw = content[:sample_size]
if not raw:
return False
text_char = set(range(32, 127)) | {9, 10, 13}
nontext = sum(b not in text_char for b in raw)
return nontext / len(raw) < 0.15
def get_type(file_path):
suffix = {".com", ".dll", ".drv", ".exe", ".ocx", ".scr", ".sys", ".mui", ".cpl"}
match_data = {}
if not os.path.splitext(file_path)[-1].lower() in suffix:
return match_data
try:
with pefile.PE(file_path, fast_load=True) as pe:
for section in pe.sections:
name = section.Name.rstrip(b'\x00').decode('latin1').lower()
data = section.get_data()
if data:
match_data[name] = section.get_data()
except pefile.PEFormatError:
with open(file_path, 'rb') as f:
file_content = f.read()
if is_text_file(file_content):
match_data[os.path.splitext(file_path)[-1].lower()] = file_content
return match_data
####################################################################################################
def file_to_images(input_dir, output_dir):
for root, dirs, files in os.walk(input_dir):
for file in files:
try:
file_path = os.path.join(root, file)
for name, data in get_type(file_path).items():
print(name, file_path)
relative_path = os.path.relpath(root, input_dir)
output_path_dir = os.path.join(output_dir, relative_path)
if not os.path.exists(output_path_dir):
os.makedirs(output_path_dir)
image = preprocess_image(data, (224, 224))
image.save(os.path.join(output_path_dir, f"{name}_{file}.png"))
except Exception as e:
print(e)
####################################################################################################
input_directory = input('Enter the folder path: ')
output_directory = "./Image_Output"
file_to_images(input_directory, output_directory)
input('Conversion Complete')
-34
View File
@@ -1,34 +0,0 @@
import os, hashlib
####################################################################################################
def calc_sha256(path):
with open(path, "rb") as f:
return hashlib.sha256(f.read()).digest()
def dedup_realtime(root):
seen = set()
deleted = 0
for dirpath, _, files in os.walk(root):
for name in files:
p = os.path.join(dirpath, name)
try:
h = calc_sha256(p)
except Exception:
continue
if h in seen:
try:
os.remove(p)
print(p)
deleted += 1
except Exception:
pass
else:
seen.add(h)
print(f"deleted {deleted}")
####################################################################################################
if __name__ == "__main__":
dedup_realtime(input("Input file path: "))
input('Deduplicate Complete')
-163
View File
@@ -1,163 +0,0 @@
import tensorflow as tf
import os, tf2onnx, numpy, random
from PIL import Image
from tensorflow.keras import *
from collections import Counter
####################################################################################################
Image.MAX_IMAGE_PIXELS = None
tf.keras.mixed_precision.set_global_policy('mixed_float16')
####################################################################################################
def load_dataset(data_dir, image_size=(224, 224), val_split=0.00001, batch_size=256, color_mode="grayscale"):
channels = 1 if color_mode == "grayscale" else 3
file_paths, labels, class_indices = get_file_list(data_dir)
combined = list(zip(file_paths, labels))
random.shuffle(combined)
file_paths[:], labels[:] = zip(*combined)
num_val = int(val_split * len(file_paths))
train_files = file_paths[num_val:]
train_labels = labels[num_val:]
val_files = file_paths[:num_val]
val_labels = labels[:num_val]
num_classes = len(class_indices)
train_ds = tf.data.Dataset.from_tensor_slices((train_files, train_labels))
train_ds = train_ds.map(lambda f, l: parse_fn(f, l, image_size, channels, num_classes), num_parallel_calls=tf.data.AUTOTUNE)
train_ds = train_ds.batch(batch_size).prefetch(tf.data.AUTOTUNE)
val_ds = tf.data.Dataset.from_tensor_slices((val_files, val_labels))
val_ds = val_ds.map(lambda f, l: parse_fn(f, l, image_size, channels, num_classes), num_parallel_calls=tf.data.AUTOTUNE)
val_ds = val_ds.batch(batch_size).prefetch(tf.data.AUTOTUNE)
train_ds.class_indices = class_indices
return train_ds, val_ds
####################################################################################################
def parse_fn(filename, label, image_size, channels, num_classes):
img = tf.io.read_file(filename)
img = tf.image.decode_image(img, channels=channels, expand_animations=False)
img = tf.cond(tf.reduce_all(tf.equal(tf.shape(img)[:2], image_size)), lambda: tf.cast(img, tf.float32), lambda: tf.image.resize(img, image_size))
img = tf.cast(img, tf.float32) / 255.0
label = tf.one_hot(label, depth=num_classes)
return img, label
def get_file_list(directory):
file_paths, labels, class_indices = [], [], {}
for i, cls in enumerate(sorted([d for d in os.listdir(directory) if os.path.isdir(os.path.join(directory, d))])):
class_indices[cls] = i
cls_dir = os.path.join(directory, cls)
for root, _, files in os.walk(cls_dir):
for file in files:
if file.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp', '.gif')):
file_paths.append(os.path.join(root, file))
labels.append(i)
return file_paths, labels, class_indices
####################################################################################################
def multi_conv(x, strides):
convs = []
for k in [(1, 7), (1, 1), (3, 3), (5, 5)]:
c = layers.DepthwiseConv2D(kernel_size=k, strides=strides, padding='same', activation='gelu')(x)
convs.append(c)
x = layers.Concatenate()(convs)
x = layers.BatchNormalization()(x)
return layers.Activation('gelu')(x)
def gated_conv(x, strides):
c = layers.DepthwiseConv2D(3, strides=strides, padding='same', activation='gelu')(x)
g = layers.DepthwiseConv2D(3, strides=strides, padding='same', activation='sigmoid')(x)
return layers.Multiply()([c, g])
def coordinate_attention(x, reduction):
input_shape = tf.shape(x)
h, w = input_shape[1], input_shape[2]
filters = x.shape[-1]
mip = max(8, filters // reduction)
x_h = layers.Lambda(lambda t: tf.reduce_mean(t, axis=2, keepdims=True))(x)
x_w = layers.Lambda(lambda t: tf.reduce_mean(t, axis=1, keepdims=True))(x)
x_w_t = layers.Permute((2, 1, 3))(x_w)
x_cat = layers.Concatenate(axis=1)([x_h, x_w_t])
x_cat = layers.Conv2D(mip, 1, padding='valid', use_bias=False)(x_cat)
x_cat = layers.BatchNormalization()(x_cat)
x_cat = layers.Activation('gelu')(x_cat)
x_cat_h, x_cat_w = layers.Lambda(lambda t: tf.split(t[0], [t[1], t[2]], axis=1))([x_cat, h, w])
x_h_sigmoid = layers.Conv2D(filters, 1, padding='valid', activation='sigmoid')(x_cat_h)
x_cat_w = layers.Permute((2, 1, 3))(x_cat_w)
x_w_sigmoid = layers.Conv2D(filters, 1, padding='valid', activation='sigmoid')(x_cat_w)
return layers.Multiply()([x, x_h_sigmoid, x_w_sigmoid])
def build_model(input_shape, num_classes):
i = layers.Input(shape=input_shape)
x = multi_conv(i, strides=1)
x = gated_conv(x, strides=2)
x = multi_conv(x, strides=2)
x = multi_conv(x, strides=2)
x = multi_conv(x, strides=2)
x = multi_conv(x, strides=2)
x = coordinate_attention(x, reduction=4)
x = layers.GlobalMaxPooling2D()(x)
x = layers.Dense(x.shape[-1] // 2, activation='gelu', dtype='float32')(x)
o = layers.Dense(num_classes, activation='softmax', dtype='float32')(x)
return models.Model(i, o)
####################################################################################################
def calculate_lr_cosine_sq(epoch, total_epochs=25, lr_start=5e-4, lr_end=1e-6):
cos_val = 0.5 * (1 + numpy.cos(epoch / total_epochs * numpy.pi))
return lr_end + (lr_start - lr_end) * (cos_val ** 2)
def lr_scheduler(epoch):
return calculate_lr_cosine_sq(epoch)
def categorical_focal_loss(gamma=2.0, alpha=0.25):
def focal_loss(y_true, y_pred):
y_pred = tf.clip_by_value(y_pred, tf.keras.backend.epsilon(), 1.0 - tf.keras.backend.epsilon())
cross_entropy = -y_true * tf.math.log(y_pred)
loss = alpha * tf.math.pow(1.0 - y_pred, gamma) * cross_entropy
return tf.reduce_sum(loss, axis=-1)
return focal_loss
class CustomModelCheckpoint(callbacks.Callback):
def on_epoch_end(self, epoch, logs=False):
models.save_model(self.model, f"PYAS_Model_Epoch_{epoch + 1}.h5")
spec = (tf.TensorSpec((None,) + train_ds.image_shape, tf.float32),)
model_proto, _ = tf2onnx.convert.from_keras(self.model, input_signature=spec, opset=17)
with open(f"PYAS_Model_Epoch_{epoch + 1}.onnx", "wb") as f:
f.write(model_proto.SerializeToString())
####################################################################################################
train_ds, val_ds = load_dataset(r'.\Image_File_Pefile')
for imgs, _ in train_ds.take(1):
train_ds.image_shape = imgs.shape[1:]
break
try:
model = models.load_model('PYAS_Model_Epoch_0.h5')
print("Load model from disk")
except:
print("Creating a new model")
model = build_model(train_ds.image_shape, len(train_ds.class_indices))
print(f"Total parameters: {model.count_params()}")
model.compile(optimizer='adam', loss=categorical_focal_loss(gamma=2.0, alpha=0.25),
metrics=['accuracy', metrics.Precision(name='precision'), metrics.Recall(name='recall'),
metrics.FalsePositives(name='fp'), metrics.TrueNegatives(name='tn')])
model.fit(train_ds, epochs=25, callbacks=[CustomModelCheckpoint(), callbacks.LearningRateScheduler(lr_scheduler)], validation_data=val_ds)
input('Training Complete')
+684
View File
@@ -0,0 +1,684 @@
import os, sys, math, time, datetime, pefile, hashlib
import ctypes, ctypes.wintypes, multiprocessing
import re, zlib
####################################################################################################
try:
import orjson
JSON_DUMPS = lambda x: orjson.dumps(x).decode('utf-8')
JSON_LOADS = orjson.loads
HAS_ORJSON = True
except ImportError:
import json
JSON_DUMPS = json.dumps
JSON_LOADS = json.loads
HAS_ORJSON = False
JSONL_PATH = "pe_features.jsonl"
BATCH_SIZE = 1000
MAX_FILE_SIZE = 4 * 1024 * 1024 * 1024
MAX_WORKERS = os.cpu_count() - 2
####################################################################################################
class WINTRUST_FILE_INFO(ctypes.Structure):
_fields_ = [
("cbStruct", ctypes.wintypes.DWORD),
("pcwszFilePath", ctypes.wintypes.LPCWSTR),
("hFile", ctypes.wintypes.HANDLE),
("pgKnownSubject", ctypes.wintypes.LPVOID)
]
class WINTRUST_DATA_UNION(ctypes.Union):
_fields_ = [
("pFile", ctypes.POINTER(WINTRUST_FILE_INFO)),
("pCatalog", ctypes.wintypes.LPVOID),
("pBlob", ctypes.wintypes.LPVOID),
("pSgnr", ctypes.wintypes.LPVOID),
("pCert", ctypes.wintypes.LPVOID)
]
class WINTRUST_DATA(ctypes.Structure):
_fields_ = [
("cbStruct", ctypes.wintypes.DWORD),
("pPolicyCallbackData", ctypes.wintypes.LPVOID),
("pSIPClientData", ctypes.wintypes.LPVOID),
("dwUIChoice", ctypes.wintypes.DWORD),
("fdwRevocationChecks", ctypes.wintypes.DWORD),
("dwUnionChoice", ctypes.wintypes.DWORD),
("u", WINTRUST_DATA_UNION),
("dwStateAction", ctypes.wintypes.DWORD),
("hWVTStateData", ctypes.wintypes.HANDLE),
("pwszURLReference", ctypes.wintypes.LPCWSTR),
("dwProvFlags", ctypes.wintypes.DWORD),
("dwUIContext", ctypes.wintypes.DWORD),
("pSignatureSettings", ctypes.wintypes.LPVOID)
]
class GUID(ctypes.Structure):
_fields_ = [
("Data1", ctypes.wintypes.DWORD),
("Data2", ctypes.wintypes.WORD),
("Data3", ctypes.wintypes.WORD),
("Data4", ctypes.c_ubyte * 8)
]
V2_GUID = GUID(0x00AAC56B, 0xCD44, 0x11D0, (ctypes.c_ubyte * 8)(0x8C, 0xC2, 0x00, 0xC0, 0x4F, 0xC2, 0x95, 0xEE))
####################################################################################################
def verify_signature(file_path):
if os.name != 'nt': return 0
try:
wintrust = ctypes.windll.wintrust
wintrust.WinVerifyTrust.argtypes = [ctypes.wintypes.HWND, ctypes.POINTER(GUID), ctypes.wintypes.LPVOID]
wintrust.WinVerifyTrust.restype = ctypes.wintypes.LONG
abs_path = os.path.abspath(file_path)
fi = WINTRUST_FILE_INFO()
fi.cbStruct = ctypes.sizeof(WINTRUST_FILE_INFO)
fi.pcwszFilePath = abs_path
fi.hFile = None
fi.pgKnownSubject = None
wt_union = WINTRUST_DATA_UNION()
wt_union.pFile = ctypes.pointer(fi)
td = WINTRUST_DATA()
td.cbStruct = ctypes.sizeof(WINTRUST_DATA)
td.pPolicyCallbackData = None
td.pSIPClientData = None
td.dwUIChoice = 2
td.fdwRevocationChecks = 0
td.dwUnionChoice = 1
td.u = wt_union
td.dwStateAction = 0
td.hWVTStateData = None
td.pwszURLReference = None
td.dwProvFlags = 0
td.dwUIContext = 0
td.pSignatureSettings = None
return 1 if wintrust.WinVerifyTrust(None, ctypes.byref(V2_GUID), ctypes.byref(td)) == 0 else 0
except Exception:
return 0
####################################################################################################
class FeatureExtractor:
_STRING_PATTERN = re.compile(b'[\x20-\x7E]{5,}')
@staticmethod
def _safe_float(val):
try:
f = float(val)
return f if not (math.isinf(f) or math.isnan(f)) else 0.0
except Exception:
return 0.0
@staticmethod
def _calc_entropy(data):
if not data: return 0.0
import numpy as np
arr = np.frombuffer(data, dtype=np.uint8)
counts = np.bincount(arr, minlength=256)
probs = counts[counts > 0] / len(arr)
return float(-np.sum(probs * np.log2(probs)))
@classmethod
def _extract_strings(cls, file_bytes):
res = {"StringCount": 0.0, "StringMeanLength": 0.0, "StringEntropy": 0.0}
for i in range(95):
res[f'StringHist_{i:02d}'] = 0.0
count = 0
total_len = 0
char_counts = [0] * 95
combined_data = bytearray()
for match in cls._STRING_PATTERN.finditer(file_bytes):
m = match.group()
count += 1
length = len(m)
total_len += length
combined_data.extend(m)
for b in m:
char_counts[b - 0x20] += 1
if count == 0:
return res
res["StringCount"] = float(count)
res["StringMeanLength"] = float(total_len) / count
res["StringEntropy"] = cls._calc_entropy(combined_data)
for i in range(95):
res[f'StringHist_{i:02d}'] = float(char_counts[i]) / total_len
return res
@classmethod
def _extract_histograms(cls, file_bytes):
import numpy as np
arr = np.frombuffer(file_bytes, dtype=np.uint8)
sz = len(arr)
res = {}
if sz == 0:
for i in range(256):
res[f'ByteHist_{i:02X}'] = 0.0
res[f'ByteEnt_{i:02X}'] = 0.0
return res
byte_counts = np.bincount(arr, minlength=256)
byte_hist = byte_counts / sz
for i, v in enumerate(byte_hist):
res[f'ByteHist_{i:02X}'] = float(v)
ent_hist = np.zeros(256, dtype=np.float64)
window = 2048
step = 1024
num_windows = (sz - window) // step + 1 if sz >= window else 1
for i in range(num_windows):
w = arr if sz < window else arr[i*step : i*step+window]
w_counts = np.bincount(w, minlength=256)
p = w_counts[w_counts > 0] / len(w)
entropy = -np.sum(p * np.log2(p))
ent_bin = int(entropy * 2.0)
if ent_bin > 15:
ent_bin = 15
byte_bins = w_counts.reshape(16, 16).sum(axis=1)
idx_start = ent_bin * 16
ent_hist[idx_start : idx_start+16] += byte_bins
ent_sum = np.sum(ent_hist)
if ent_sum > 0:
ent_hist /= ent_sum
for i, v in enumerate(ent_hist):
res[f'ByteEnt_{i:02X}'] = float(v)
return res
@classmethod
def _extract_overlay_features(cls, pe, fsize):
overlay_offset = pe.get_overlay_data_start_offset()
if not overlay_offset or overlay_offset >= fsize:
return {"HasOverlay": 0.0, "OverlaySize": 0.0, "OverlayRatio": 0.0, "OverlayEntropy": 0.0}
overlay_data = pe.get_overlay()
if not overlay_data:
return {"HasOverlay": 0.0, "OverlaySize": 0.0, "OverlayRatio": 0.0, "OverlayEntropy": 0.0}
sz = len(overlay_data)
return {
"HasOverlay": 1.0,
"OverlaySize": float(sz),
"OverlayRatio": float(sz) / float(fsize),
"OverlayEntropy": cls._calc_entropy(overlay_data)
}
@classmethod
def _extract_rich_header(cls, pe):
if not hasattr(pe, 'RICH_HEADER') or not pe.RICH_HEADER:
return {"HasRichHeader": 0.0, "RichHeaderCount": 0.0}
return {
"HasRichHeader": 1.0,
"RichHeaderCount": float(len(pe.RICH_HEADER.values) // 2) if hasattr(pe.RICH_HEADER, 'values') else 0.0
}
@classmethod
def _extract_ep_anomalies(cls, pe):
if not hasattr(pe, 'OPTIONAL_HEADER') or not hasattr(pe, 'sections') or not pe.sections:
return {"EntryPointSectionIndex": -1.0, "EntryPointInExecutable": 0.0, "EntryPointInLastSection": 0.0}
ep = pe.OPTIONAL_HEADER.AddressOfEntryPoint
ep_section_idx = -1
ep_in_exec = 0.0
for idx, sec in enumerate(pe.sections):
if sec.VirtualAddress <= ep < (sec.VirtualAddress + sec.Misc_VirtualSize):
ep_section_idx = idx
if sec.Characteristics & 0x20000000:
ep_in_exec = 1.0
break
return {
"EntryPointSectionIndex": float(ep_section_idx),
"EntryPointInExecutable": ep_in_exec,
"EntryPointInLastSection": 1.0 if ep_section_idx == len(pe.sections) - 1 else 0.0
}
@classmethod
def _extract_advanced_resources(cls, pe):
res_data = {
"ResourceMaxEntropy": 0.0,
"ResourceMinEntropy": 0.0,
"ResourceMeanEntropy": 0.0,
"ResourceLangCount": 0.0,
"ResourceRCDataCount": 0.0
}
if not hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'):
return res_data
entropies = []
langs = set()
rcdata_count = 0
for entry_type in pe.DIRECTORY_ENTRY_RESOURCE.entries:
if hasattr(entry_type, 'id') and entry_type.id == 10:
rcdata_count += len(getattr(entry_type.directory, 'entries', []))
if not hasattr(entry_type, 'directory'):
continue
for entry_id in entry_type.directory.entries:
if not hasattr(entry_id, 'directory'):
continue
for entry_lang in entry_id.directory.entries:
if hasattr(entry_lang, 'id'):
langs.add(entry_lang.id)
if hasattr(entry_lang, 'data'):
try:
data = pe.get_data(entry_lang.data.struct.OffsetToData, entry_lang.data.struct.Size)
entropies.append(cls._calc_entropy(data))
except Exception:
continue
res_data["ResourceLangCount"] = float(len(langs))
res_data["ResourceRCDataCount"] = float(rcdata_count)
if entropies:
res_data["ResourceMaxEntropy"] = max(entropies)
res_data["ResourceMinEntropy"] = min(entropies)
res_data["ResourceMeanEntropy"] = sum(entropies) / len(entropies)
return res_data
@classmethod
def _extract_load_config(cls, pe):
cfg = {"HasLoadConfig": 0.0, "HasCFG": 0.0, "HasSEHTable": 0.0}
if hasattr(pe, 'DIRECTORY_ENTRY_LOAD_CONFIG'):
cfg["HasLoadConfig"] = 1.0
struct = pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct
if getattr(struct, 'GuardCFFunctionTable', 0) != 0:
cfg["HasCFG"] = 1.0
if getattr(struct, 'SEHandlerTable', 0) != 0:
cfg["HasSEHTable"] = 1.0
return cfg
@classmethod
def _extract_security_directory(cls, pe, file_bytes, fsize):
res = {"HasSignature": 0.0, "SignatureCount": 0.0}
if not hasattr(pe, 'OPTIONAL_HEADER') or not hasattr(pe.OPTIONAL_HEADER, 'DATA_DIRECTORY'):
return res
sec_idx = 4
directories = pe.OPTIONAL_HEADER.DATA_DIRECTORY
if len(directories) <= sec_idx:
return res
sec_dir = directories[sec_idx]
if sec_dir.Size == 0 or sec_dir.VirtualAddress == 0:
return res
res["HasSignature"] = 1.0
offset = sec_dir.VirtualAddress
size = sec_dir.Size
if offset + size > fsize or offset < 0 or size < 0:
return res
count = 0
curr = offset
end = offset + size
while curr + 8 <= end:
dw_length = int.from_bytes(file_bytes[curr:curr+4], 'little')
if dw_length < 8:
break
count += 1
curr += (dw_length + 7) & ~7
res["SignatureCount"] = float(count)
return res
@classmethod
def extract(cls, file_bytes, file_path, fsize):
if fsize == 0: return None
base, dlls, apis, pe = {}, set(), set(), None
try:
pe = pefile.PE(data=file_bytes, fast_load=True)
try:
pe.parse_rich_header()
pe.parse_data_directories(directories=[
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_DEBUG'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_TLS'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG']
])
except Exception:
pass
base['TrustSigned'] = verify_signature(file_path)
base['FileEntropy'] = cls._calc_entropy(file_bytes)
base['FileSize'] = fsize
fh = pe.FILE_HEADER
base['Machine'] = getattr(fh, 'Machine', 0)
base['NumberOfSections'] = getattr(fh, 'NumberOfSections', 0)
base['TimeDateStamp'] = getattr(fh, 'TimeDateStamp', 0)
base['PointerToSymbolTable'] = getattr(fh, 'PointerToSymbolTable', 0)
base['NumberOfSymbols'] = getattr(fh, 'NumberOfSymbols', 0)
base['SizeOfOptionalHeader'] = getattr(fh, 'SizeOfOptionalHeader', 0)
base['Characteristics'] = getattr(fh, 'Characteristics', 0)
curr_ts = datetime.datetime.utcnow().timestamp()
base['HasInvalidTimestamp'] = 1.0 if (base['TimeDateStamp'] < 631152000 or base['TimeDateStamp'] > curr_ts + 2592000) else 0.0
base['FileTimeException'] = 1.0 if base['TimeDateStamp'] == 0 else 0.0
base['Is64Bit'] = 1.0 if base['Machine'] in (0x8664, 0xAA64, 0x0200) else 0.0
base['IsExe'] = 1.0 if pe.is_exe() else 0.0
base['IsDll'] = 1.0 if pe.is_dll() else 0.0
base['ExceptionCount'] = 0.0
if hasattr(pe, 'OPTIONAL_HEADER'):
op = pe.OPTIONAL_HEADER
fields = ['Magic', 'MajorLinkerVersion', 'MinorLinkerVersion', 'SizeOfCode', 'SizeOfInitializedData', 'SizeOfUninitializedData', 'AddressOfEntryPoint', 'BaseOfCode', 'ImageBase', 'SectionAlignment', 'FileAlignment', 'MajorOperatingSystemVersion', 'MinorOperatingSystemVersion', 'MajorImageVersion', 'MinorImageVersion', 'MajorSubsystemVersion', 'MinorSubsystemVersion', 'SizeOfImage', 'SizeOfHeaders', 'CheckSum', 'Subsystem', 'DllCharacteristics', 'SizeOfStackReserve', 'SizeOfStackCommit', 'SizeOfHeapReserve', 'SizeOfHeapCommit', 'LoaderFlags', 'NumberOfRvaAndSizes']
for f in fields: base[f] = getattr(op, f, 0)
base['IsDriver'] = 1.0 if base.get('Subsystem') == 1 else 0.0
if hasattr(op, 'DATA_DIRECTORY'):
for i, directory in enumerate(op.DATA_DIRECTORY):
base[f'DataDirectory_{i}_Size'] = directory.Size
base[f'DataDirectory_{i}_VA'] = directory.VirtualAddress
exc_idx = pefile.DIRECTORY_ENTRY.get('IMAGE_DIRECTORY_ENTRY_EXCEPTION', 3)
if len(op.DATA_DIRECTORY) > exc_idx and op.DATA_DIRECTORY[exc_idx].Size > 0:
base['ExceptionCount'] = op.DATA_DIRECTORY[exc_idx].Size // 12 if base['Machine'] in (0x8664, 0xAA64) else 0.0
char_flags = [0x00000020, 0x00000040, 0x00000080, 0x02000000, 0x20000000, 0x40000000, 0x80000000]
for flag in char_flags:
base[f'Char_{flag:08X}_Count'] = 0.0
base[f'Char_{flag:08X}_MeanEntropy'] = 0.0
for i in range(50):
base[f'SectionHash_{i:02d}'] = 0.0
if hasattr(pe, 'sections'):
base['SectionCount'] = len(pe.sections)
entropies, raw_sizes, v_sizes = [], [], []
exec_sec, write_sec, read_sec, sec_exc = 0, 0, 0, 0
for section in pe.sections:
sec_name = section.Name.rstrip(b'\x00')
hash_val = zlib.crc32(sec_name) % 50
base[f'SectionHash_{hash_val:02d}'] += 1.0
try:
s_data = section.get_data()
s_entropy = cls._calc_entropy(s_data)
except Exception:
s_entropy = 0.0
entropies.append(s_entropy)
raw_sizes.append(section.SizeOfRawData)
v_sizes.append(section.Misc_VirtualSize)
if section.Characteristics & 0x20000000: exec_sec += 1
if section.Characteristics & 0x80000000: write_sec += 1
if section.Characteristics & 0x40000000: read_sec += 1
if section.SizeOfRawData + section.PointerToRawData > fsize: sec_exc = 1
for flag in char_flags:
if section.Characteristics & flag:
base[f'Char_{flag:08X}_Count'] += 1.0
base[f'Char_{flag:08X}_MeanEntropy'] += s_entropy
base['SectionMaxEntropy'] = max(entropies) if entropies else 0.0
base['SectionMinEntropy'] = min(entropies) if entropies else 0.0
base['SectionMeanEntropy'] = sum(entropies) / len(entropies) if entropies else 0.0
base['SectionMaxRawSize'] = max(raw_sizes) if raw_sizes else 0.0
base['SectionMinRawSize'] = min(raw_sizes) if raw_sizes else 0.0
base['SectionMeanRawSize'] = sum(raw_sizes) / len(raw_sizes) if raw_sizes else 0.0
base['SectionMaxVSize'] = max(v_sizes) if v_sizes else 0.0
base['SectionMinVSize'] = min(v_sizes) if v_sizes else 0.0
base['SectionMeanVSize'] = sum(v_sizes) / len(v_sizes) if v_sizes else 0.0
base['ExecutableSections'] = exec_sec
base['WritableSections'] = write_sec
base['ReadableSections'] = read_sec
base['SectionException'] = float(sec_exc)
for flag in char_flags:
if base[f'Char_{flag:08X}_Count'] > 0:
base[f'Char_{flag:08X}_MeanEntropy'] = base[f'Char_{flag:08X}_MeanEntropy'] / base[f'Char_{flag:08X}_Count']
string_keys = ['FileDescription', 'FileVersion', 'ProductName', 'ProductVersion', 'CompanyName', 'LegalCopyright', 'Comments', 'InternalName', 'LegalTrademarks', 'SpecialBuild', 'PrivateBuild']
for key in string_keys:
base[f'{key}Length'] = 0.0
if hasattr(pe, 'FileInfo'):
for fileinfo_list in pe.FileInfo:
for fileinfo in fileinfo_list:
if getattr(fileinfo, 'name', '') in ('StringFileInfo', b'StringFileInfo'):
for st in getattr(fileinfo, 'StringTable', []):
for key, val in st.entries.items():
try:
k = key.decode('utf-8', 'ignore') if isinstance(key, bytes) else str(key)
if k in string_keys:
v = val.decode('utf-8', 'ignore') if isinstance(val, bytes) else str(val)
base[f'{k}Length'] = float(len(v))
except Exception:
continue
if hasattr(pe, 'DIRECTORY_ENTRY_TLS') and hasattr(pe.DIRECTORY_ENTRY_TLS, 'struct'):
base['HasTlsCallbacks'] = 1.0 if getattr(pe.DIRECTORY_ENTRY_TLS.struct, 'AddressOfCallBacks', 0) != 0 else 0.0
if hasattr(pe, 'VS_FIXEDFILEINFO') and len(pe.VS_FIXEDFILEINFO) > 0:
flags = getattr(pe.VS_FIXEDFILEINFO[0], 'FileFlags', 0)
base['IsDebug'] = 1.0 if flags & 0x1 else 0.0
base['IsPreRelease'] = 1.0 if flags & 0x2 else 0.0
base['IsPatched'] = 1.0 if flags & 0x4 else 0.0
base['IsPrivateBuild'] = 1.0 if flags & 0x8 else 0.0
base['IsSpecialBuild'] = 1.0 if flags & 0x20 else 0.0
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
base['ImportCount'] = len(pe.DIRECTORY_ENTRY_IMPORT)
func_count = 0
for entry in pe.DIRECTORY_ENTRY_IMPORT:
if getattr(entry, 'dll', None):
try: dlls.add(entry.dll.decode('ascii', 'ignore').lower())
except Exception: pass
for imp in getattr(entry, 'imports', []):
func_count += 1
if getattr(imp, 'name', None):
try: apis.add(imp.name.decode('ascii', 'ignore'))
except Exception: pass
base['ImportFunctionCount'] = func_count
base['ExportCount'] = len(pe.DIRECTORY_ENTRY_EXPORT.symbols) if (hasattr(pe, 'DIRECTORY_ENTRY_EXPORT') and hasattr(pe.DIRECTORY_ENTRY_EXPORT, 'symbols')) else 0.0
if hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'):
icon_count = 0
for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries:
if getattr(entry, 'id', None) == 3 and hasattr(entry, 'directory'):
icon_count += len(getattr(entry.directory, 'entries', []))
base['IconCount'] = icon_count
else:
base['IconCount'] = 0.0
base['DebugCount'] = len(pe.DIRECTORY_ENTRY_DEBUG) if hasattr(pe, 'DIRECTORY_ENTRY_DEBUG') else 0.0
base.update(cls._extract_strings(file_bytes))
base.update(cls._extract_histograms(file_bytes))
base.update(cls._extract_overlay_features(pe, fsize))
base.update(cls._extract_rich_header(pe))
base.update(cls._extract_ep_anomalies(pe))
base.update(cls._extract_advanced_resources(pe))
base.update(cls._extract_load_config(pe))
base.update(cls._extract_security_directory(pe, file_bytes, fsize))
for k in base: base[k] = cls._safe_float(base[k])
return {"Base": base, "DLLs": list(dlls), "APIs": list(apis)}
except Exception: return None
finally:
if pe: pe.close()
####################################################################################################
def load_existing_hashes():
hashes = set()
if os.path.exists(JSONL_PATH):
with open(JSONL_PATH, 'r', encoding='utf-8') as f:
for line in f:
try:
data = JSON_LOADS(line)
if 'FileHash' in data: hashes.add(data['FileHash'])
except Exception: pass
return hashes
GLOBAL_HASHES = set()
def worker_init(shared_hashes):
global GLOBAL_HASHES
GLOBAL_HASHES = shared_hashes
def _extract_worker_wrapper(args):
return _extract_worker(*args)
def _extract_worker(file_path, label):
try:
fsize = os.path.getsize(file_path)
if fsize == 0 or fsize > MAX_FILE_SIZE:
return 'error', None
with open(file_path, "rb") as f:
file_bytes = f.read()
sha256 = hashlib.sha256(file_bytes).hexdigest()
if sha256 in GLOBAL_HASHES:
return 'skipped', None
res = FeatureExtractor.extract(file_bytes, file_path, fsize)
if res:
final_res = {"Label": label, "FileHash": sha256, **res}
return 'success', (sha256, JSON_DUMPS(final_res) + "\n")
return 'error', None
except Exception:
return 'error', None
####################################################################################################
def scan_and_save(path, label, existing_hashes):
print(f"\n[*] Scanning path: {path} (Label={label})")
files = [path] if os.path.isfile(path) else [os.path.join(r, f) for r, _, fs in os.walk(path) for f in fs]
total = len(files)
if total == 0:
print("[-] No files found.")
return
print(f"[*] Found {total} files. Starting {MAX_WORKERS}-process extraction...\n")
if HAS_ORJSON:
print("[+] orjson acceleration enabled.")
count, errors, skipped, processed_files = 0, 0, 0, 0
start_time = time.time()
batch_buffer = []
pool = multiprocessing.Pool(processes=MAX_WORKERS, initializer=worker_init, initargs=(existing_hashes,))
tasks = [(fpath, label) for fpath in files]
try:
for status, data in pool.imap_unordered(_extract_worker_wrapper, tasks):
processed_files += 1
if status == 'error':
errors += 1
elif status == 'skipped':
skipped += 1
elif status == 'success':
sha256, json_str = data
if sha256 not in existing_hashes:
existing_hashes.add(sha256)
batch_buffer.append(json_str)
count += 1
else:
skipped += 1
if len(batch_buffer) >= BATCH_SIZE:
with open(JSONL_PATH, 'a', encoding='utf-8') as f_out:
f_out.writelines(batch_buffer)
batch_buffer.clear()
elapsed = time.time() - start_time
rate = processed_files / elapsed if elapsed > 0 else 0
sys.stdout.write(f"\r[{processed_files}/{total}] Added: {count} | Skipped: {skipped} | Errors: {errors} | Speed: {rate:.1f} files/s")
sys.stdout.flush()
pool.close()
pool.join()
except KeyboardInterrupt:
print("\n\n[-] User aborted. Force terminating all child processes...")
pool.terminate()
pool.join()
except Exception as e:
print(f"\n[-] Critical error: {e}")
pool.terminate()
pool.join()
finally:
if batch_buffer:
try:
with open(JSONL_PATH, 'a', encoding='utf-8') as f_out:
f_out.writelines(batch_buffer)
except Exception as save_err:
print(f"\n[-] Failed to save remaining buffer: {save_err}")
print(f"\n\n[+] Scan complete. Total added: {count}, Total skipped: {skipped}")
####################################################################################################
if __name__ == "__main__":
multiprocessing.freeze_support()
print("\n---------------- PE Dataset Builder v4.0 ----------------\n")
existing_hashes = load_existing_hashes()
print(f"[+] Found {len(existing_hashes)} existing hashes.\n")
try:
while True:
print("-" * 57)
target_path = input("\n[*] Enter path (file or directory): ").strip().strip('"').strip("'")
if target_path.lower() in ['exit', 'q']: break
if not target_path or not os.path.exists(target_path):
print("[-] Invalid path.")
continue
while True:
label_input = input("[*] Enter label (0=Safe, 1=Malware): ").strip()
if label_input.lower() in ['exit', 'q']: sys.exit()
if label_input in ['0', '1']: break
print("[-] Please enter 0 or 1.")
scan_and_save(target_path, int(label_input), existing_hashes)
except KeyboardInterrupt:
print("\n\n[-] Aborted.")
+680
View File
@@ -0,0 +1,680 @@
import os, sys, math, json, pefile, datetime
import numpy as np
import onnxruntime as ort
import ctypes, ctypes.wintypes
import re, zlib
####################################################################################################
MODEL_FILE = "Pefile_General_S1.onnx"
FEATURE_FILE = "features.json"
MAX_FILE_SIZE = 4 * 1024 * 1024 * 1024
####################################################################################################
class WINTRUST_FILE_INFO(ctypes.Structure):
_fields_ = [
("cbStruct", ctypes.wintypes.DWORD),
("pcwszFilePath", ctypes.wintypes.LPCWSTR),
("hFile", ctypes.wintypes.HANDLE),
("pgKnownSubject", ctypes.wintypes.LPVOID)
]
class WINTRUST_DATA_UNION(ctypes.Union):
_fields_ = [
("pFile", ctypes.POINTER(WINTRUST_FILE_INFO)),
("pCatalog", ctypes.wintypes.LPVOID),
("pBlob", ctypes.wintypes.LPVOID),
("pSgnr", ctypes.wintypes.LPVOID),
("pCert", ctypes.wintypes.LPVOID)
]
class WINTRUST_DATA(ctypes.Structure):
_fields_ = [
("cbStruct", ctypes.wintypes.DWORD),
("pPolicyCallbackData", ctypes.wintypes.LPVOID),
("pSIPClientData", ctypes.wintypes.LPVOID),
("dwUIChoice", ctypes.wintypes.DWORD),
("fdwRevocationChecks", ctypes.wintypes.DWORD),
("dwUnionChoice", ctypes.wintypes.DWORD),
("u", WINTRUST_DATA_UNION),
("dwStateAction", ctypes.wintypes.DWORD),
("hWVTStateData", ctypes.wintypes.HANDLE),
("pwszURLReference", ctypes.wintypes.LPCWSTR),
("dwProvFlags", ctypes.wintypes.DWORD),
("dwUIContext", ctypes.wintypes.DWORD),
("pSignatureSettings", ctypes.wintypes.LPVOID)
]
class GUID(ctypes.Structure):
_fields_ = [
("Data1", ctypes.wintypes.DWORD),
("Data2", ctypes.wintypes.WORD),
("Data3", ctypes.wintypes.WORD),
("Data4", ctypes.c_ubyte * 8)
]
V2_GUID = GUID(0x00AAC56B, 0xCD44, 0x11D0, (ctypes.c_ubyte * 8)(0x8C, 0xC2, 0x00, 0xC0, 0x4F, 0xC2, 0x95, 0xEE))
####################################################################################################
class WinTrust:
@staticmethod
def verify(file_path):
if os.name != 'nt':
return 0
try:
wintrust = ctypes.windll.wintrust
wintrust.WinVerifyTrust.argtypes = [ctypes.wintypes.HWND, ctypes.POINTER(GUID), ctypes.wintypes.LPVOID]
wintrust.WinVerifyTrust.restype = ctypes.wintypes.LONG
fi = WINTRUST_FILE_INFO()
fi.cbStruct = ctypes.sizeof(WINTRUST_FILE_INFO)
fi.pcwszFilePath = os.path.abspath(file_path)
fi.hFile = None
fi.pgKnownSubject = None
wt_union = WINTRUST_DATA_UNION()
wt_union.pFile = ctypes.pointer(fi)
td = WINTRUST_DATA()
td.cbStruct = ctypes.sizeof(WINTRUST_DATA)
td.pPolicyCallbackData = None
td.pSIPClientData = None
td.dwUIChoice = 2
td.fdwRevocationChecks = 0
td.dwUnionChoice = 1
td.u = wt_union
td.dwStateAction = 0
td.hWVTStateData = None
td.pwszURLReference = None
td.dwProvFlags = 0
td.dwUIContext = 0
td.pSignatureSettings = None
return 1 if wintrust.WinVerifyTrust(None, ctypes.byref(V2_GUID), ctypes.byref(td)) == 0 else 0
except Exception:
return 0
####################################################################################################
class FeatureExtractor:
_STRING_PATTERN = re.compile(b'[\x20-\x7E]{5,}')
@staticmethod
def _safe_float(val):
try:
f = float(val)
return f if not (math.isinf(f) or math.isnan(f)) else 0.0
except Exception:
return 0.0
@staticmethod
def _calc_entropy(data):
if not data: return 0.0
import numpy as np
arr = np.frombuffer(data, dtype=np.uint8)
counts = np.bincount(arr, minlength=256)
probs = counts[counts > 0] / len(arr)
return float(-np.sum(probs * np.log2(probs)))
@classmethod
def _extract_strings(cls, file_bytes):
res = {"StringCount": 0.0, "StringMeanLength": 0.0, "StringEntropy": 0.0}
for i in range(95):
res[f'StringHist_{i:02d}'] = 0.0
count = 0
total_len = 0
char_counts = [0] * 95
combined_data = bytearray()
for match in cls._STRING_PATTERN.finditer(file_bytes):
m = match.group()
count += 1
length = len(m)
total_len += length
combined_data.extend(m)
for b in m:
char_counts[b - 0x20] += 1
if count == 0:
return res
res["StringCount"] = float(count)
res["StringMeanLength"] = float(total_len) / count
res["StringEntropy"] = cls._calc_entropy(combined_data)
for i in range(95):
res[f'StringHist_{i:02d}'] = float(char_counts[i]) / total_len
return res
@classmethod
def _extract_histograms(cls, file_bytes):
import numpy as np
arr = np.frombuffer(file_bytes, dtype=np.uint8)
sz = len(arr)
res = {}
if sz == 0:
for i in range(256):
res[f'ByteHist_{i:02X}'] = 0.0
res[f'ByteEnt_{i:02X}'] = 0.0
return res
byte_counts = np.bincount(arr, minlength=256)
byte_hist = byte_counts / sz
for i, v in enumerate(byte_hist):
res[f'ByteHist_{i:02X}'] = float(v)
ent_hist = np.zeros(256, dtype=np.float64)
window = 2048
step = 1024
num_windows = (sz - window) // step + 1 if sz >= window else 1
for i in range(num_windows):
w = arr if sz < window else arr[i*step : i*step+window]
w_counts = np.bincount(w, minlength=256)
p = w_counts[w_counts > 0] / len(w)
entropy = -np.sum(p * np.log2(p))
ent_bin = int(entropy * 2.0)
if ent_bin > 15:
ent_bin = 15
byte_bins = w_counts.reshape(16, 16).sum(axis=1)
idx_start = ent_bin * 16
ent_hist[idx_start : idx_start+16] += byte_bins
ent_sum = np.sum(ent_hist)
if ent_sum > 0:
ent_hist /= ent_sum
for i, v in enumerate(ent_hist):
res[f'ByteEnt_{i:02X}'] = float(v)
return res
@classmethod
def _extract_overlay_features(cls, pe, fsize):
overlay_offset = pe.get_overlay_data_start_offset()
if not overlay_offset or overlay_offset >= fsize:
return {"HasOverlay": 0.0, "OverlaySize": 0.0, "OverlayRatio": 0.0, "OverlayEntropy": 0.0}
overlay_data = pe.get_overlay()
if not overlay_data:
return {"HasOverlay": 0.0, "OverlaySize": 0.0, "OverlayRatio": 0.0, "OverlayEntropy": 0.0}
sz = len(overlay_data)
return {
"HasOverlay": 1.0,
"OverlaySize": float(sz),
"OverlayRatio": float(sz) / float(fsize),
"OverlayEntropy": cls._calc_entropy(overlay_data)
}
@classmethod
def _extract_rich_header(cls, pe):
if not hasattr(pe, 'RICH_HEADER') or not pe.RICH_HEADER:
return {"HasRichHeader": 0.0, "RichHeaderCount": 0.0}
return {
"HasRichHeader": 1.0,
"RichHeaderCount": float(len(pe.RICH_HEADER.values) // 2) if hasattr(pe.RICH_HEADER, 'values') else 0.0
}
@classmethod
def _extract_ep_anomalies(cls, pe):
if not hasattr(pe, 'OPTIONAL_HEADER') or not hasattr(pe, 'sections') or not pe.sections:
return {"EntryPointSectionIndex": -1.0, "EntryPointInExecutable": 0.0, "EntryPointInLastSection": 0.0}
ep = pe.OPTIONAL_HEADER.AddressOfEntryPoint
ep_section_idx = -1
ep_in_exec = 0.0
for idx, sec in enumerate(pe.sections):
if sec.VirtualAddress <= ep < (sec.VirtualAddress + sec.Misc_VirtualSize):
ep_section_idx = idx
if sec.Characteristics & 0x20000000:
ep_in_exec = 1.0
break
return {
"EntryPointSectionIndex": float(ep_section_idx),
"EntryPointInExecutable": ep_in_exec,
"EntryPointInLastSection": 1.0 if ep_section_idx == len(pe.sections) - 1 else 0.0
}
@classmethod
def _extract_advanced_resources(cls, pe):
res_data = {
"ResourceMaxEntropy": 0.0,
"ResourceMinEntropy": 0.0,
"ResourceMeanEntropy": 0.0,
"ResourceLangCount": 0.0,
"ResourceRCDataCount": 0.0
}
if not hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'):
return res_data
entropies = []
langs = set()
rcdata_count = 0
for entry_type in pe.DIRECTORY_ENTRY_RESOURCE.entries:
if hasattr(entry_type, 'id') and entry_type.id == 10:
rcdata_count += len(getattr(entry_type.directory, 'entries', []))
if not hasattr(entry_type, 'directory'):
continue
for entry_id in entry_type.directory.entries:
if not hasattr(entry_id, 'directory'):
continue
for entry_lang in entry_id.directory.entries:
if hasattr(entry_lang, 'id'):
langs.add(entry_lang.id)
if hasattr(entry_lang, 'data'):
try:
data = pe.get_data(entry_lang.data.struct.OffsetToData, entry_lang.data.struct.Size)
entropies.append(cls._calc_entropy(data))
except Exception:
continue
res_data["ResourceLangCount"] = float(len(langs))
res_data["ResourceRCDataCount"] = float(rcdata_count)
if entropies:
res_data["ResourceMaxEntropy"] = max(entropies)
res_data["ResourceMinEntropy"] = min(entropies)
res_data["ResourceMeanEntropy"] = sum(entropies) / len(entropies)
return res_data
@classmethod
def _extract_load_config(cls, pe):
cfg = {"HasLoadConfig": 0.0, "HasCFG": 0.0, "HasSEHTable": 0.0}
if hasattr(pe, 'DIRECTORY_ENTRY_LOAD_CONFIG'):
cfg["HasLoadConfig"] = 1.0
struct = pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct
if getattr(struct, 'GuardCFFunctionTable', 0) != 0:
cfg["HasCFG"] = 1.0
if getattr(struct, 'SEHandlerTable', 0) != 0:
cfg["HasSEHTable"] = 1.0
return cfg
@classmethod
def _extract_security_directory(cls, pe, file_bytes, fsize):
res = {"HasSignature": 0.0, "SignatureCount": 0.0}
if not hasattr(pe, 'OPTIONAL_HEADER') or not hasattr(pe.OPTIONAL_HEADER, 'DATA_DIRECTORY'):
return res
sec_idx = 4
directories = pe.OPTIONAL_HEADER.DATA_DIRECTORY
if len(directories) <= sec_idx:
return res
sec_dir = directories[sec_idx]
if sec_dir.Size == 0 or sec_dir.VirtualAddress == 0:
return res
res["HasSignature"] = 1.0
offset = sec_dir.VirtualAddress
size = sec_dir.Size
if offset + size > fsize or offset < 0 or size < 0:
return res
count = 0
curr = offset
end = offset + size
while curr + 8 <= end:
dw_length = int.from_bytes(file_bytes[curr:curr+4], 'little')
if dw_length < 8:
break
count += 1
curr += (dw_length + 7) & ~7
res["SignatureCount"] = float(count)
return res
@classmethod
def extract(cls, file_path):
pe = None
try:
fsize = os.path.getsize(file_path)
if fsize == 0 or fsize > MAX_FILE_SIZE:
return None
with open(file_path, "rb") as f:
file_bytes = f.read()
base, dlls, apis = {}, set(), set()
pe = pefile.PE(data=file_bytes, fast_load=True)
try:
pe.parse_rich_header()
pe.parse_data_directories(directories=[
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_DEBUG'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_TLS'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG']
])
except Exception:
pass
base['TrustSigned'] = float(WinTrust.verify(file_path))
base['FileEntropy'] = cls._calc_entropy(file_bytes)
base['FileSize'] = float(fsize)
fh = pe.FILE_HEADER
base['Machine'] = getattr(fh, 'Machine', 0)
base['NumberOfSections'] = getattr(fh, 'NumberOfSections', 0)
base['TimeDateStamp'] = getattr(fh, 'TimeDateStamp', 0)
base['PointerToSymbolTable'] = getattr(fh, 'PointerToSymbolTable', 0)
base['NumberOfSymbols'] = getattr(fh, 'NumberOfSymbols', 0)
base['SizeOfOptionalHeader'] = getattr(fh, 'SizeOfOptionalHeader', 0)
base['Characteristics'] = getattr(fh, 'Characteristics', 0)
curr_ts = datetime.datetime.utcnow().timestamp()
base['HasInvalidTimestamp'] = 1.0 if (base['TimeDateStamp'] < 631152000 or base['TimeDateStamp'] > curr_ts + 2592000) else 0.0
base['FileTimeException'] = 1.0 if base['TimeDateStamp'] == 0 else 0.0
base['Is64Bit'] = 1.0 if base['Machine'] in (0x8664, 0xAA64, 0x0200) else 0.0
base['IsExe'] = 1.0 if pe.is_exe() else 0.0
base['IsDll'] = 1.0 if pe.is_dll() else 0.0
base['ExceptionCount'] = 0.0
if hasattr(pe, 'OPTIONAL_HEADER'):
op = pe.OPTIONAL_HEADER
fields = ['Magic', 'MajorLinkerVersion', 'MinorLinkerVersion', 'SizeOfCode', 'SizeOfInitializedData', 'SizeOfUninitializedData', 'AddressOfEntryPoint', 'BaseOfCode', 'ImageBase', 'SectionAlignment', 'FileAlignment', 'MajorOperatingSystemVersion', 'MinorOperatingSystemVersion', 'MajorImageVersion', 'MinorImageVersion', 'MajorSubsystemVersion', 'MinorSubsystemVersion', 'SizeOfImage', 'SizeOfHeaders', 'CheckSum', 'Subsystem', 'DllCharacteristics', 'SizeOfStackReserve', 'SizeOfStackCommit', 'SizeOfHeapReserve', 'SizeOfHeapCommit', 'LoaderFlags', 'NumberOfRvaAndSizes']
for f in fields: base[f] = getattr(op, f, 0)
base['IsDriver'] = 1.0 if base.get('Subsystem') == 1 else 0.0
if hasattr(op, 'DATA_DIRECTORY'):
for i, directory in enumerate(op.DATA_DIRECTORY):
base[f'DataDirectory_{i}_Size'] = directory.Size
base[f'DataDirectory_{i}_VA'] = directory.VirtualAddress
exc_idx = pefile.DIRECTORY_ENTRY.get('IMAGE_DIRECTORY_ENTRY_EXCEPTION', 3)
if len(op.DATA_DIRECTORY) > exc_idx and op.DATA_DIRECTORY[exc_idx].Size > 0:
base['ExceptionCount'] = op.DATA_DIRECTORY[exc_idx].Size // 12 if base['Machine'] in (0x8664, 0xAA64) else 0.0
char_flags = [0x00000020, 0x00000040, 0x00000080, 0x02000000, 0x20000000, 0x40000000, 0x80000000]
for flag in char_flags:
base[f'Char_{flag:08X}_Count'] = 0.0
base[f'Char_{flag:08X}_MeanEntropy'] = 0.0
for i in range(50):
base[f'SectionHash_{i:02d}'] = 0.0
if hasattr(pe, 'sections'):
base['SectionCount'] = len(pe.sections)
entropies, raw_sizes, v_sizes = [], [], []
exec_sec, write_sec, read_sec, sec_exc = 0, 0, 0, 0
for section in pe.sections:
sec_name = section.Name.rstrip(b'\x00')
hash_val = zlib.crc32(sec_name) % 50
base[f'SectionHash_{hash_val:02d}'] += 1.0
try:
s_data = section.get_data()
s_entropy = cls._calc_entropy(s_data)
except Exception:
s_entropy = 0.0
entropies.append(s_entropy)
raw_sizes.append(section.SizeOfRawData)
v_sizes.append(section.Misc_VirtualSize)
if section.Characteristics & 0x20000000: exec_sec += 1
if section.Characteristics & 0x80000000: write_sec += 1
if section.Characteristics & 0x40000000: read_sec += 1
if section.SizeOfRawData + section.PointerToRawData > fsize: sec_exc = 1
for flag in char_flags:
if section.Characteristics & flag:
base[f'Char_{flag:08X}_Count'] += 1.0
base[f'Char_{flag:08X}_MeanEntropy'] += s_entropy
base['SectionMaxEntropy'] = max(entropies) if entropies else 0.0
base['SectionMinEntropy'] = min(entropies) if entropies else 0.0
base['SectionMeanEntropy'] = sum(entropies) / len(entropies) if entropies else 0.0
base['SectionMaxRawSize'] = max(raw_sizes) if raw_sizes else 0.0
base['SectionMinRawSize'] = min(raw_sizes) if raw_sizes else 0.0
base['SectionMeanRawSize'] = sum(raw_sizes) / len(raw_sizes) if raw_sizes else 0.0
base['SectionMaxVSize'] = max(v_sizes) if v_sizes else 0.0
base['SectionMinVSize'] = min(v_sizes) if v_sizes else 0.0
base['SectionMeanVSize'] = sum(v_sizes) / len(v_sizes) if v_sizes else 0.0
base['ExecutableSections'] = exec_sec
base['WritableSections'] = write_sec
base['ReadableSections'] = read_sec
base['SectionException'] = float(sec_exc)
for flag in char_flags:
if base[f'Char_{flag:08X}_Count'] > 0:
base[f'Char_{flag:08X}_MeanEntropy'] = base[f'Char_{flag:08X}_MeanEntropy'] / base[f'Char_{flag:08X}_Count']
string_keys = ['FileDescription', 'FileVersion', 'ProductName', 'ProductVersion', 'CompanyName', 'LegalCopyright', 'Comments', 'InternalName', 'LegalTrademarks', 'SpecialBuild', 'PrivateBuild']
for key in string_keys:
base[f'{key}Length'] = 0.0
if hasattr(pe, 'FileInfo'):
for fileinfo_list in pe.FileInfo:
for fileinfo in fileinfo_list:
if getattr(fileinfo, 'name', '') in ('StringFileInfo', b'StringFileInfo'):
for st in getattr(fileinfo, 'StringTable', []):
for key, val in st.entries.items():
try:
k = key.decode('utf-8', 'ignore') if isinstance(key, bytes) else str(key)
if k in string_keys:
v = val.decode('utf-8', 'ignore') if isinstance(val, bytes) else str(val)
base[f'{k}Length'] = float(len(v))
except Exception:
continue
if hasattr(pe, 'DIRECTORY_ENTRY_TLS') and hasattr(pe.DIRECTORY_ENTRY_TLS, 'struct'):
base['HasTlsCallbacks'] = 1.0 if getattr(pe.DIRECTORY_ENTRY_TLS.struct, 'AddressOfCallBacks', 0) != 0 else 0.0
if hasattr(pe, 'VS_FIXEDFILEINFO') and len(pe.VS_FIXEDFILEINFO) > 0:
flags = getattr(pe.VS_FIXEDFILEINFO[0], 'FileFlags', 0)
base['IsDebug'] = 1.0 if flags & 0x1 else 0.0
base['IsPreRelease'] = 1.0 if flags & 0x2 else 0.0
base['IsPatched'] = 1.0 if flags & 0x4 else 0.0
base['IsPrivateBuild'] = 1.0 if flags & 0x8 else 0.0
base['IsSpecialBuild'] = 1.0 if flags & 0x20 else 0.0
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
base['ImportCount'] = len(pe.DIRECTORY_ENTRY_IMPORT)
func_count = 0
for entry in pe.DIRECTORY_ENTRY_IMPORT:
if getattr(entry, 'dll', None):
try: dlls.add(entry.dll.decode('ascii', 'ignore').lower())
except Exception: pass
for imp in getattr(entry, 'imports', []):
func_count += 1
if getattr(imp, 'name', None):
try: apis.add(imp.name.decode('ascii', 'ignore'))
except Exception: pass
base['ImportFunctionCount'] = func_count
base['ExportCount'] = len(pe.DIRECTORY_ENTRY_EXPORT.symbols) if (hasattr(pe, 'DIRECTORY_ENTRY_EXPORT') and hasattr(pe.DIRECTORY_ENTRY_EXPORT, 'symbols')) else 0.0
if hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'):
icon_count = 0
for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries:
if getattr(entry, 'id', None) == 3 and hasattr(entry, 'directory'):
icon_count += len(getattr(entry.directory, 'entries', []))
base['IconCount'] = icon_count
else:
base['IconCount'] = 0.0
base['DebugCount'] = len(pe.DIRECTORY_ENTRY_DEBUG) if hasattr(pe, 'DIRECTORY_ENTRY_DEBUG') else 0.0
base.update(cls._extract_strings(file_bytes))
base.update(cls._extract_histograms(file_bytes))
base.update(cls._extract_overlay_features(pe, fsize))
base.update(cls._extract_rich_header(pe))
base.update(cls._extract_ep_anomalies(pe))
base.update(cls._extract_advanced_resources(pe))
base.update(cls._extract_load_config(pe))
base.update(cls._extract_security_directory(pe, file_bytes, fsize))
for k in base: base[k] = cls._safe_float(base[k])
return {"Base": base, "DLLs": list(dlls), "APIs": list(apis)}
except Exception: return None
finally:
if pe: pe.close()
####################################################################################################
class ModelPredictor:
def __init__(self, model_path, feature_path):
self.model_features = self._load_features(feature_path)
self.sess = ort.InferenceSession(model_path, providers=['CPUExecutionProvider'])
self.input_name = self.sess.get_inputs()[0].name
expected_dim = self.sess.get_inputs()[0].shape[1]
current_dim = len(self.model_features)
if current_dim != expected_dim:
raise ValueError(f"Dimension mismatch! Model expects {expected_dim} features, but feature file lists {current_dim}.")
self.feat_map = {feat: i for i, feat in enumerate(self.model_features)}
def _load_features(self, path):
if not os.path.exists(path):
raise FileNotFoundError(f"Feature file not found: {path}")
with open(path, 'r') as f:
return json.load(f)
def predict(self, raw_data):
vec = np.zeros((1, len(self.model_features)), dtype=np.float32)
base = raw_data.get('Base', {})
dlls = raw_data.get('DLLs', [])
apis = raw_data.get('APIs', [])
for k, v in base.items():
if k in self.feat_map:
vec[0, self.feat_map[k]] = v
for d in dlls:
h = zlib.crc32(d.encode('utf-8', 'ignore')) % 256
feat_name = f"DllHash_{h:03d}"
if feat_name in self.feat_map:
vec[0, self.feat_map[feat_name]] += 1.0
for a in apis:
h = zlib.crc32(a.encode('utf-8', 'ignore')) % 1024
feat_name = f"ApiHash_{h:04d}"
if feat_name in self.feat_map:
vec[0, self.feat_map[feat_name]] += 1.0
outputs = self.sess.run(None, {self.input_name: vec})
if len(outputs) > 1:
result = outputs[1]
if isinstance(result, list) and len(result) > 0:
prob_dict = result[0]
if hasattr(prob_dict, 'get'):
return float(prob_dict.get(1, prob_dict.get('1', 0.0)))
elif isinstance(result, np.ndarray):
if result.ndim == 2 and result.shape[1] > 1:
return float(result[0][1])
return 0.0
####################################################################################################
def scan_target(target, predictor):
files = []
if os.path.isfile(target):
files.append(target)
elif os.path.isdir(target):
for r, _, fs in os.walk(target):
for f in fs:
files.append(os.path.join(r, f))
if not files:
print("[-] No valid PE files found.")
return
print(f"\n[*] Scanning {len(files)} files...\n")
print(f"{'RESULT':<10} | {'PROB':<8} | {'FILE'}")
print("-" * 80)
safe_count = 0
mal_count = 0
for fpath in files:
try:
data = FeatureExtractor.extract(fpath)
if not data:
print(f"{'ERROR':<10} | {'----':<8} | {fpath}")
continue
prob = predictor.predict(data)
is_malware = prob > 0.5
label = "MALWARE" if is_malware else "SAFE"
if is_malware:
mal_count += 1
else:
safe_count += 1
print(f"{label:<10} | {prob:.4f} | {fpath}")
except Exception as e:
print(f"{'FAIL':<10} | {'----':<8} | {fpath} ({str(e)})")
print("-" * 80)
print(f"[*] Summary: Safe={safe_count}, Malware={mal_count}, Total={len(files)}")
####################################################################################################
if __name__ == "__main__":
print("\n-------------------------- PE Malware Predictor v4.0 --------------------------\n")
print(f"[*] © 2020-2026 87owo (PYAS Security)")
print(f"[*] Loading model features and weights...")
base_dir = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(os.path.abspath(__file__))
model_path = os.path.join(base_dir, MODEL_FILE)
feature_path = os.path.join(base_dir, FEATURE_FILE)
try:
predictor = ModelPredictor(model_path, feature_path)
except Exception as e:
print(f"[-] Critical Error: {e}")
sys.exit(1)
if len(sys.argv) > 1:
for path in sys.argv[1:]:
target = path.strip('"').strip("'")
if os.path.exists(target):
print()
print("-" * 80)
scan_target(target, predictor)
else:
print(f"[-] Path does not exist: {target}")
else:
while True:
try:
print()
print("-" * 80)
path = input("\n[?] Enter File or Folder Path (or 'q' to exit): ").strip().strip('"').strip("'")
if path.lower() in ['q', 'exit']: break
if not os.path.exists(path):
print("[-] Path does not exist.")
continue
scan_target(path, predictor)
except KeyboardInterrupt:
break
except Exception as e:
print(f"[-] Error: {e}")
+259
View File
@@ -0,0 +1,259 @@
import os, sys, time, json, onnxmltools, zlib
import numpy as np
import pandas as pd
import lightgbm as lgb
import scipy.sparse as sp
from collections import defaultdict
from onnxmltools.convert.common.data_types import FloatTensorType
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, roc_auc_score
try:
import orjson
JSON_LOADS = orjson.loads
except ImportError:
JSON_LOADS = json.loads
####################################################################################################
JSONL_PATH = "pe_features.jsonl"
MODEL_FILE = "model.txt"
ONNX_FILE = "Pefile_General_S1.onnx"
FEATURE_FILE = "features.json"
TEST_SIZE = 0.0001
RANDOM_SEED = 42
DLL_HASH_DIM = 256
API_HASH_DIM = 1024
EXCLUDE_FEATURES = {
'label', 'filehash',
}
LGBM_PARAMS = {
'objective': 'binary',
'metric': ['binary_logloss', 'auc'],
'boosting_type': 'gbdt',
'num_leaves': 256,
'feature_fraction': 0.8,
'bagging_fraction': 1.0,
'bagging_freq': 5,
'verbose': -1,
'seed': RANDOM_SEED,
'n_jobs': -1,
'max_depth': -1,
'min_data_in_leaf': 50,
}
####################################################################################################
def analyze_schema(jsonl_path):
print("[*] Stage 1: Scanning global schema...")
base_keys = set()
num_lines = 0
with open(jsonl_path, 'rb') as f:
for line in f:
try:
data = JSON_LOADS(line)
base_data = data.get('Base')
if base_data:
base_keys.update(base_data.keys())
num_lines += 1
except Exception:
continue
base_schema = sorted([k for k in base_keys if k.lower().strip() not in EXCLUDE_FEATURES])
dll_schema = [f"DllHash_{i:03d}" for i in range(DLL_HASH_DIM)]
api_schema = [f"ApiHash_{i:04d}" for i in range(API_HASH_DIM)]
return base_schema, dll_schema, api_schema, num_lines
####################################################################################################
def load_data_efficiently(jsonl_path, base_schema, dll_schema, api_schema, num_lines):
print(f"[*] Stage 2: Building sparse feature matrix dynamically (Expected samples: {num_lines})...")
final_features = base_schema + dll_schema + api_schema
num_features = len(final_features)
base_idx = {feat: i for i, feat in enumerate(base_schema)}
offset_dll = len(base_schema)
offset_api = offset_dll + DLL_HASH_DIM
rows, cols, vals = [], [], []
y = np.zeros(num_lines, dtype=np.int8)
with open(jsonl_path, 'rb') as f:
row_idx = 0
for line in f:
try:
data = JSON_LOADS(line)
y[row_idx] = data.get('Label', 0)
base_data = data.get('Base')
if base_data:
for k, v in base_data.items():
idx = base_idx.get(k)
if idx is not None:
val = float(v)
if val != 0.0:
rows.append(row_idx)
cols.append(idx)
vals.append(val)
dll_data = data.get('DLLs')
if dll_data:
for d in dll_data:
idx = zlib.crc32(d.encode('utf-8', 'ignore')) % DLL_HASH_DIM
rows.append(row_idx)
cols.append(offset_dll + idx)
vals.append(1.0)
api_data = data.get('APIs')
if api_data:
for a in api_data:
idx = zlib.crc32(a.encode('utf-8', 'ignore')) % API_HASH_DIM
rows.append(row_idx)
cols.append(offset_api + idx)
vals.append(1.0)
row_idx += 1
except Exception:
continue
y = y[:row_idx]
X = sp.csr_matrix((vals, (rows, cols)), shape=(row_idx, num_features), dtype=np.float32)
return X, pd.Series(y), final_features
####################################################################################################
def _calculate_dynamic_lr(current_iter: int) -> float:
return max(0.01, 0.1 - (current_iter // 80) * 0.01)
def train_process(X, y, feature_names):
print(f"[*] Dataset shape: {X.shape}")
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=TEST_SIZE, random_state=RANDOM_SEED, stratify=y
)
n_pos = (y_train == 1).sum()
n_neg = (y_train == 0).sum()
base_weight = n_neg / n_pos if n_pos > 0 else 1.0
weight_ratio_target = 0.005
final_pos_weight = base_weight * weight_ratio_target
print(f"[*] Sample distribution: Safe={n_neg}, Malware={n_pos}")
print(f"[*] Weight correction: Base={base_weight:.4f}, Final={final_pos_weight:.4f}")
train_data = lgb.Dataset(X_train, label=y_train, feature_name=feature_names)
valid_data = lgb.Dataset(X_test, label=y_test, reference=train_data, feature_name=feature_names)
params = LGBM_PARAMS.copy()
params['scale_pos_weight'] = final_pos_weight
print("\n[*] Starting LightGBM training...")
start_time = time.time()
model = lgb.train(
params,
train_data,
valid_sets=[valid_data],
num_boost_round=500,
callbacks=[
lgb.log_evaluation(period=50),
lgb.reset_parameter(learning_rate=_calculate_dynamic_lr)
]
)
elapsed = time.time() - start_time
print(f"\n[+] Training complete, time elapsed {elapsed:.2f} seconds")
return model, X_test, y_test
####################################################################################################
def export_onnx_model(model, feature_names):
print(f"[*] Exporting ONNX model to {ONNX_FILE}...")
try:
initial_type = [('float_input', FloatTensorType([None, len(feature_names)]))]
onnx_model = onnxmltools.convert_lightgbm(model, initial_types=initial_type)
onnxmltools.utils.save_model(onnx_model, ONNX_FILE)
with open(FEATURE_FILE, 'w') as f:
json.dump(feature_names, f)
print(f"[+] ONNX export successful. Feature order saved to {FEATURE_FILE}")
except Exception as e:
print(f"[-] ONNX export failed: {e}")
def evaluate_and_save(model, X_test, y_test):
print("\n------------------- Evaluation Report -------------------\n")
y_prob = model.predict(X_test, num_iteration=model.best_iteration)
y_pred = (y_prob > 0.5).astype(int)
acc = accuracy_score(y_test, y_pred)
auc = roc_auc_score(y_test, y_prob)
tp = np.sum((y_test == 1) & (y_pred == 1))
fn = np.sum((y_test == 1) & (y_pred == 0))
tn = np.sum((y_test == 0) & (y_pred == 0))
fp = np.sum((y_test == 0) & (y_pred == 1))
total_malware = tp + fn
total_safe = tn + fp
detection_rate = (tp / total_malware * 100) if total_malware > 0 else 0.0
fpr = (fp / total_safe * 100) if total_safe > 0 else 0.0
print(f"Accuracy : {acc:.6f}")
print(f"ROC AUC : {auc:.6f}\n")
print(f"True Positive (Class 1) : {detection_rate:.3f}% ({tp}/{total_malware})")
print(f"False Positive (Class 0) : {fpr:.3f}% ({fp}/{total_safe})")
print("\n---------------------------------------------------------\n")
print("[*] Top 20 Features:")
feature_names = model.feature_name()
importance = model.feature_importance(importance_type='gain')
feature_imp = pd.DataFrame(sorted(zip(importance, feature_names), reverse=True), columns=['Value', 'Feature'])
print(feature_imp.head(20).to_string(index=False))
print("\n---------------------------------------------------------\n")
model.save_model(MODEL_FILE)
print(f"[+] Native model saved to: {os.path.abspath(MODEL_FILE)}")
export_onnx_model(model, feature_names)
####################################################################################################
if __name__ == "__main__":
print("\n---------------- PE Malware Trainer v4.0 ----------------\n")
if not os.path.exists(JSONL_PATH):
print(f"[-] Error: Feature file {JSONL_PATH} not found.")
sys.exit(1)
base_schema, dll_schema, api_schema, num_lines = analyze_schema(JSONL_PATH)
if num_lines == 0:
print("[-] Error: Feature file is empty.")
sys.exit(1)
print(f"[*] Detected Base features: {len(base_schema)}")
print(f"[*] Hashing DLL features: {len(dll_schema)}")
print(f"[*] Hashing API features: {len(api_schema)}")
X, y, final_features = load_data_efficiently(JSONL_PATH, base_schema, dll_schema, api_schema, num_lines)
try:
model, X_test, y_test = train_process(X, y, final_features)
evaluate_and_save(model, X_test, y_test)
except KeyboardInterrupt:
print("\n\n[-] Training aborted.")
except Exception as e:
print(f"\n[-] System exception: {e}")
+259
View File
@@ -0,0 +1,259 @@
import os, sys, time, json, onnxmltools, zlib
import numpy as np
import pandas as pd
import lightgbm as lgb
import scipy.sparse as sp
from collections import defaultdict
from onnxmltools.convert.common.data_types import FloatTensorType
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, roc_auc_score
try:
import orjson
JSON_LOADS = orjson.loads
except ImportError:
JSON_LOADS = json.loads
####################################################################################################
JSONL_PATH = "pe_features.jsonl"
MODEL_FILE = "model.txt"
ONNX_FILE = "Pefile_General_T1.onnx"
FEATURE_FILE = "features.json"
TEST_SIZE = 0.0001
RANDOM_SEED = 42
DLL_HASH_DIM = 256
API_HASH_DIM = 1024
EXCLUDE_FEATURES = {
'label', 'filehash',
}
LGBM_PARAMS = {
'objective': 'binary',
'metric': ['binary_logloss', 'auc'],
'boosting_type': 'gbdt',
'num_leaves': 128,
'feature_fraction': 0.8,
'bagging_fraction': 1.0,
'bagging_freq': 5,
'verbose': -1,
'seed': RANDOM_SEED,
'n_jobs': -1,
'max_depth': -1,
'min_data_in_leaf': 50,
}
####################################################################################################
def analyze_schema(jsonl_path):
print("[*] Stage 1: Scanning global schema...")
base_keys = set()
num_lines = 0
with open(jsonl_path, 'rb') as f:
for line in f:
try:
data = JSON_LOADS(line)
base_data = data.get('Base')
if base_data:
base_keys.update(base_data.keys())
num_lines += 1
except Exception:
continue
base_schema = sorted([k for k in base_keys if k.lower().strip() not in EXCLUDE_FEATURES])
dll_schema = [f"DllHash_{i:03d}" for i in range(DLL_HASH_DIM)]
api_schema = [f"ApiHash_{i:04d}" for i in range(API_HASH_DIM)]
return base_schema, dll_schema, api_schema, num_lines
####################################################################################################
def load_data_efficiently(jsonl_path, base_schema, dll_schema, api_schema, num_lines):
print(f"[*] Stage 2: Building sparse feature matrix dynamically (Expected samples: {num_lines})...")
final_features = base_schema + dll_schema + api_schema
num_features = len(final_features)
base_idx = {feat: i for i, feat in enumerate(base_schema)}
offset_dll = len(base_schema)
offset_api = offset_dll + DLL_HASH_DIM
rows, cols, vals = [], [], []
y = np.zeros(num_lines, dtype=np.int8)
with open(jsonl_path, 'rb') as f:
row_idx = 0
for line in f:
try:
data = JSON_LOADS(line)
y[row_idx] = data.get('Label', 0)
base_data = data.get('Base')
if base_data:
for k, v in base_data.items():
idx = base_idx.get(k)
if idx is not None:
val = float(v)
if val != 0.0:
rows.append(row_idx)
cols.append(idx)
vals.append(val)
dll_data = data.get('DLLs')
if dll_data:
for d in dll_data:
idx = zlib.crc32(d.encode('utf-8', 'ignore')) % DLL_HASH_DIM
rows.append(row_idx)
cols.append(offset_dll + idx)
vals.append(1.0)
api_data = data.get('APIs')
if api_data:
for a in api_data:
idx = zlib.crc32(a.encode('utf-8', 'ignore')) % API_HASH_DIM
rows.append(row_idx)
cols.append(offset_api + idx)
vals.append(1.0)
row_idx += 1
except Exception:
continue
y = y[:row_idx]
X = sp.csr_matrix((vals, (rows, cols)), shape=(row_idx, num_features), dtype=np.float32)
return X, pd.Series(y), final_features
####################################################################################################
def _calculate_dynamic_lr(current_iter: int) -> float:
return max(0.01, 0.1 - (current_iter // 80) * 0.01)
def train_process(X, y, feature_names):
print(f"[*] Dataset shape: {X.shape}")
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=TEST_SIZE, random_state=RANDOM_SEED, stratify=y
)
n_pos = (y_train == 1).sum()
n_neg = (y_train == 0).sum()
base_weight = n_neg / n_pos if n_pos > 0 else 1.0
weight_ratio_target = 0.001
final_pos_weight = base_weight * weight_ratio_target
print(f"[*] Sample distribution: Safe={n_neg}, Malware={n_pos}")
print(f"[*] Weight correction: Base={base_weight:.4f}, Final={final_pos_weight:.4f}")
train_data = lgb.Dataset(X_train, label=y_train, feature_name=feature_names)
valid_data = lgb.Dataset(X_test, label=y_test, reference=train_data, feature_name=feature_names)
params = LGBM_PARAMS.copy()
params['scale_pos_weight'] = final_pos_weight
print("\n[*] Starting LightGBM training...")
start_time = time.time()
model = lgb.train(
params,
train_data,
valid_sets=[valid_data],
num_boost_round=500,
callbacks=[
lgb.log_evaluation(period=50),
lgb.reset_parameter(learning_rate=_calculate_dynamic_lr)
]
)
elapsed = time.time() - start_time
print(f"\n[+] Training complete, time elapsed {elapsed:.2f} seconds")
return model, X_test, y_test
####################################################################################################
def export_onnx_model(model, feature_names):
print(f"[*] Exporting ONNX model to {ONNX_FILE}...")
try:
initial_type = [('float_input', FloatTensorType([None, len(feature_names)]))]
onnx_model = onnxmltools.convert_lightgbm(model, initial_types=initial_type)
onnxmltools.utils.save_model(onnx_model, ONNX_FILE)
with open(FEATURE_FILE, 'w') as f:
json.dump(feature_names, f)
print(f"[+] ONNX export successful. Feature order saved to {FEATURE_FILE}")
except Exception as e:
print(f"[-] ONNX export failed: {e}")
def evaluate_and_save(model, X_test, y_test):
print("\n------------------- Evaluation Report -------------------\n")
y_prob = model.predict(X_test, num_iteration=model.best_iteration)
y_pred = (y_prob > 0.5).astype(int)
acc = accuracy_score(y_test, y_pred)
auc = roc_auc_score(y_test, y_prob)
tp = np.sum((y_test == 1) & (y_pred == 1))
fn = np.sum((y_test == 1) & (y_pred == 0))
tn = np.sum((y_test == 0) & (y_pred == 0))
fp = np.sum((y_test == 0) & (y_pred == 1))
total_malware = tp + fn
total_safe = tn + fp
detection_rate = (tp / total_malware * 100) if total_malware > 0 else 0.0
fpr = (fp / total_safe * 100) if total_safe > 0 else 0.0
print(f"Accuracy : {acc:.6f}")
print(f"ROC AUC : {auc:.6f}\n")
print(f"True Positive (Class 1) : {detection_rate:.3f}% ({tp}/{total_malware})")
print(f"False Positive (Class 0) : {fpr:.3f}% ({fp}/{total_safe})")
print("\n---------------------------------------------------------\n")
print("[*] Top 20 Features:")
feature_names = model.feature_name()
importance = model.feature_importance(importance_type='gain')
feature_imp = pd.DataFrame(sorted(zip(importance, feature_names), reverse=True), columns=['Value', 'Feature'])
print(feature_imp.head(20).to_string(index=False))
print("\n---------------------------------------------------------\n")
model.save_model(MODEL_FILE)
print(f"[+] Native model saved to: {os.path.abspath(MODEL_FILE)}")
export_onnx_model(model, feature_names)
####################################################################################################
if __name__ == "__main__":
print("\n---------------- PE Malware Trainer v4.0 ----------------\n")
if not os.path.exists(JSONL_PATH):
print(f"[-] Error: Feature file {JSONL_PATH} not found.")
sys.exit(1)
base_schema, dll_schema, api_schema, num_lines = analyze_schema(JSONL_PATH)
if num_lines == 0:
print("[-] Error: Feature file is empty.")
sys.exit(1)
print(f"[*] Detected Base features: {len(base_schema)}")
print(f"[*] Hashing DLL features: {len(dll_schema)}")
print(f"[*] Hashing API features: {len(api_schema)}")
X, y, final_features = load_data_efficiently(JSONL_PATH, base_schema, dll_schema, api_schema, num_lines)
try:
model, X_test, y_test = train_process(X, y, final_features)
evaluate_and_save(model, X_test, y_test)
except KeyboardInterrupt:
print("\n\n[-] Training aborted.")
except Exception as e:
print(f"\n[-] System exception: {e}")
File diff suppressed because it is too large. Load diff
+841
View File
@@ -0,0 +1,841 @@
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
-webkit-tap-highlight-color: transparent;
outline: none;
}
:root {
--bg-window: rgb(245, 245, 247);
--bg-nav: rgb(255, 255, 255);
--bg-panel: rgb(255, 255, 255);
--bg-hover: rgba(0, 0, 0, 0.05);
--text-primary: rgb(29, 29, 31);
--text-secondary: rgb(134, 134, 139);
--border-color: rgb(210, 210, 215);
--accent-color: rgb(0, 122, 255);
--accent-alpha: rgba(0, 122, 255, 0.1);
--accent-shadow: rgba(0, 122, 255, 0.2);
--accent-hover: rgba(0, 122, 255, 0.3);
--shadow-sm: 0 2px 12px rgba(0, 0, 0, 0.04);
}
body {
background-color: transparent;
margin: 0;
padding: 0;
width: 100vw;
height: 100vh;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
color: var(--text-primary);
}
.app-container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
background-color: var(--bg-window);
}
.navigate-window {
height: 44px;
background-color: var(--bg-nav);
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 10px 0 20px;
border-bottom: 1px solid var(--border-color);
transition: background-color 0.3s;
}
.navigate-title {
font-size: 17px;
font-weight: 600;
letter-spacing: -0.2px;
}
.window-controls {
display: flex;
gap: 4px;
-webkit-app-region: no-drag;
}
.ctrl-btn {
width: 46px;
height: 32px;
border: none;
background: transparent;
border-radius: 6px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
transition: background-color 0.15s;
color: var(--text-secondary);
}
.ctrl-btn:hover { background-color: var(--bg-hover); color: var(--text-primary); }
#close_button:hover { background-color: #ff3b30; color: white; }
.theme-svg {
width: 16px;
height: 16px;
}
.main-layout {
display: flex;
height: calc(100% - 44px);
}
.options-window {
width: 64px;
background-color: var(--bg-nav);
display: flex;
flex-direction: column;
align-items: center;
padding: 16px 0;
gap: 12px;
border-right: 1px solid var(--border-color);
transition: background-color 0.3s;
}
.nav-btn {
width: 44px;
height: 44px;
border: none;
background: transparent;
border-radius: 10px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
color: var(--text-secondary);
transition: all 0.2s;
position: relative;
}
.nav-btn svg { width: 22px; height: 22px; }
.nav-btn:hover { background-color: var(--bg-hover); color: var(--text-primary); }
.nav-btn.active { background-color: var(--accent-alpha); color: var(--accent-color); }
.spacer { flex-grow: 1; }
.content-window {
flex-grow: 1;
position: relative;
overflow: hidden;
}
.page {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: 28px 32px;
opacity: 0;
visibility: hidden;
transform: translateY(8px);
transition: opacity 0.25s ease, transform 0.25s ease;
display: flex;
flex-direction: column;
}
.page.active {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.state-widget {
flex: 1;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 16px;
color: var(--accent-color);
}
.state-title {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
letter-spacing: -0.5px;
}
.log-widget {
flex-grow: 1;
margin-top: 20px;
}
.license-widget {
flex-grow: 1;
}
.log-text, .license-text {
width: 100%;
height: 100%;
border: 1px solid var(--border-color);
border-radius: 12px;
background-color: var(--bg-panel);
color: var(--text-primary);
resize: none;
padding: 16px;
font-size: 13px;
line-height: 1.6;
white-space: pre;
overflow-x: auto;
}
.progress-widget {
height: 70px;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
gap: 16px;
}
.header-group {
flex: 1;
min-width: 0;
}
.header-group h2 {
font-size: 24px;
font-weight: 600;
letter-spacing: -0.5px;
white-space: nowrap;
}
.header-group p {
font-size: 14px;
color: var(--text-secondary);
margin-top: 4px;
white-space: nowrap;
overflow-x: auto;
padding-bottom: 4px;
}
.modern-action-group {
display: flex;
gap: 12px;
align-items: center;
flex-shrink: 0;
}
.modern-select {
appearance: none;
}
.modern-select:hover, .modern-select:focus {
border-color: var(--accent-color);
box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.2);
}
.modern-btn {
padding: 8px 20px;
border: 1px solid var(--border-color);
border-radius: 10px;
background-color: transparent;
color: var(--text-primary);
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.modern-btn:hover { background-color: var(--bg-hover); }
.primary-btn {
background-color: var(--accent-color);
color: white;
border: none;
box-shadow: 0 2px 4px var(--accent-shadow);
}
.primary-btn:hover {
box-shadow: 0 4px 8px var(--accent-hover);
transform: translateY(-1px);
background-color: var(--accent-color);
opacity: 0.95;
}
.primary-btn:active {
transform: translateY(0);
opacity: 0.8;
}
.danger-btn { color: #ff3b30; border-color: #ff3b30; }
.danger-btn:hover { background-color: rgba(255, 59, 48, 0.1); }
.hidden { display: none !important; }
.scan-content {
flex-grow: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
.virus-widget {
width: 100%;
flex-grow: 1;
min-height: 0;
border: 1px solid var(--border-color);
border-radius: 12px;
background-color: var(--bg-panel);
overflow-y: auto;
overflow-x: auto;
}
.virus-list {
list-style: none;
padding: 0;
margin: 0;
min-width: max-content;
}
.list-widget {
display: flex;
flex-direction: column;
height: 100%;
overflow-y: auto;
padding-right: 12px;
gap: 12px;
}
.list-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 18px 24px;
border-radius: 16px;
background-color: var(--bg-panel);
border: 1px solid var(--border-color);
transition: background-color 0.2s;
gap: 16px;
}
.item-info {
flex: 1;
min-width: 0;
overflow-x: auto;
padding-bottom: 2px;
}
.list-item[data-target]:hover, .list-item[data-action]:hover {
background-color: var(--bg-hover);
}
.item-info h2 {
font-size: 16px;
font-weight: 600;
letter-spacing: -0.2px;
white-space: nowrap;
}
.item-info p {
font-size: 13px;
color: var(--text-secondary);
margin-top: 4px;
white-space: nowrap;
}
.toggle-switch {
position: relative;
display: inline-block;
width: 50px;
height: 28px;
}
.toggle-switch input { opacity: 0; width: 0; height: 0; }
.slider {
position: absolute;
cursor: pointer;
top: 0; left: 0; right: 0; bottom: 0;
background-color: var(--border-color);
transition: .3s;
border-radius: 28px;
}
.slider:before {
position: absolute;
content: "";
height: 24px; width: 24px;
left: 2px; bottom: 2px;
background-color: white;
transition: .3s;
border-radius: 50%;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.toggle-switch input:checked + .slider {
background-color: var(--accent-color);
}
.toggle-switch input:checked + .slider:before {
transform: translateX(22px);
}
.icon-action-btn {
width: 32px; height: 32px;
border-radius: 8px;
border: none;
background-color: transparent;
color: var(--text-secondary);
cursor: pointer;
display: flex; justify-content: center; align-items: center;
transition: background-color 0.2s;
}
.icon-action-btn:hover { background-color: var(--bg-hover); color: var(--text-primary); }
.icon-action-btn svg { width: 20px; height: 20px; }
.sub-header-toolbar {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
width: 100%;
}
.sub-header-toolbar h2 {
font-size: 24px;
font-weight: 600;
letter-spacing: -0.5px;
white-space: nowrap;
}
.icon-btn {
background: transparent;
border: none;
color: var(--text-secondary);
cursor: pointer;
padding: 6px;
border-radius: 8px;
display: flex; align-items: center; justify-content: center;
transition: background-color 0.2s;
}
.icon-btn:hover { background-color: var(--bg-hover); color: var(--text-primary); }
.icon-btn svg { width: 24px; height: 24px; }
.search-box {
display: flex;
align-items: center;
background-color: var(--bg-panel);
border: 1px solid var(--border-color);
border-radius: 10px;
padding: 0 14px;
width: 200px;
transition: border-color 0.2s, box-shadow 0.2s;
}
.search-box:focus-within {
border-color: var(--accent-color);
box-shadow: 0 0 0 2px var(--accent-shadow);
}
.search-box svg {
width: 18px; height: 18px;
color: var(--text-secondary);
}
.search-box input {
border: none;
background: transparent;
color: var(--text-primary);
padding: 10px;
outline: none;
width: 100%;
font-size: 14px;
}
.flex-grow {
flex-grow: 1;
min-height: 0;
}
.manage-widget {
border: 1px solid var(--border-color);
border-radius: 12px;
background-color: var(--bg-panel);
overflow-y: auto;
overflow-x: auto;
}
.manage-list {
list-style: none;
padding: 0;
margin: 0;
min-width: max-content;
}
.manage-list-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
border-bottom: 1px solid var(--border-color);
transition: background-color 0.2s;
}
.manage-list-item:last-child {
border-bottom: none;
}
.manage-list-item:hover {
background-color: var(--bg-hover);
}
.manage-list-item input[type="checkbox"] {
width: 16px;
height: 16px;
accent-color: var(--accent-color);
cursor: pointer;
flex-shrink: 0;
}
.manage-list-item-content {
flex-grow: 1;
min-width: 0;
font-size: 14px;
color: var(--text-primary);
white-space: nowrap;
}
.custom-select-wrapper {
position: relative;
min-width: 100px;
display: inline-grid;
vertical-align: middle;
user-select: none;
}
select.hidden + .custom-select-wrapper {
display: none !important;
}
.custom-select-trigger {
grid-area: 1 / 1;
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 16px;
border-radius: 10px;
border: 1px solid var(--border-color);
background-color: var(--bg-panel);
color: var(--text-primary);
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: border-color 0.2s, box-shadow 0.2s;
gap: 12px;
box-sizing: border-box;
}
.custom-select-phantom {
grid-area: 1 / 1;
visibility: hidden;
pointer-events: none;
display: grid;
padding: 8px 16px;
font-size: 14px;
font-weight: 500;
box-sizing: border-box;
}
.custom-select-phantom span {
grid-area: 1 / 1;
white-space: nowrap;
padding-right: 28px;
}
.custom-select-wrapper.open .custom-select-trigger,
.custom-select-trigger:hover {
border-color: var(--accent-color);
box-shadow: 0 0 0 2px var(--accent-shadow);
}
.custom-select-icon {
display: flex;
align-items: center;
color: var(--text-secondary);
transition: transform 0.3s ease;
}
.custom-select-wrapper.open .custom-select-icon {
transform: rotate(180deg);
}
.custom-select-icon svg { width: 16px; height: 16px; }
.custom-select-options {
position: absolute;
top: calc(100% + 8px);
left: 0;
min-width: 100%;
width: max-content;
white-space: nowrap;
background-color: var(--bg-panel);
border: 1px solid var(--border-color);
border-radius: 10px;
box-shadow: var(--shadow-sm);
z-index: 100;
list-style: none;
margin: 0;
max-height: 0;
padding: 0 6px;
border-width: 0;
overflow: hidden;
opacity: 0;
visibility: hidden;
transform: translateY(-10px);
transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s 0.2s, max-height 0s 0.2s, padding 0s 0.2s, border-width 0s 0.2s, box-shadow 0s 0.2s;
}
.custom-select-wrapper.open .custom-select-options {
max-height: 260px;
padding: 6px;
border-width: 1px;
overflow-y: auto;
opacity: 1;
visibility: visible;
transform: translateY(0);
transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s 0s, max-height 0s 0s, padding 0s 0s, border-width 0s 0s, box-shadow 0s 0s;
}
.custom-select-option {
padding: 8px 12px;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
color: var(--text-primary);
transition: background-color 0.2s, color 0.2s;
text-align: center;
}
.custom-select-option:hover {
background-color: var(--bg-hover);
}
.custom-select-option.selected {
background-color: var(--accent-alpha);
color: var(--accent-color);
font-weight: 600;
}
.home-top-widget {
display: flex;
align-items: center;
height: 200px;
gap: 24px;
width: 100%;
}
.quick-actions {
flex: 1;
display: flex;
flex-direction: column;
gap: 12px;
height: 100%;
justify-content: center;
}
#loading_overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: #e0e0e0;
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
transition: opacity 0.4s ease, visibility 0.4s ease;
}
#loading_overlay.fade-out {
opacity: 0;
visibility: hidden;
}
.spinner {
animation: rotate 2s linear infinite;
width: 50px;
height: 50px;
}
.spinner .path {
stroke: #86868b;
stroke-linecap: round;
animation: dash 1.5s ease-in-out infinite;
}
@keyframes rotate {
100% { transform: rotate(360deg); }
}
@keyframes dash {
0% { stroke-dasharray: 1, 150; stroke-dashoffset: 0; }
50% { stroke-dasharray: 90, 150; stroke-dashoffset: -35; }
100% { stroke-dasharray: 90, 150; stroke-dashoffset: -124; }
}
.app-container.fade-in {
opacity: 1 !important;
}
.manage-list-header {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
border-bottom: 1px solid var(--border-color);
background-color: var(--bg-panel);
font-weight: 600;
font-size: 13px;
color: var(--text-secondary);
position: sticky;
top: 0;
z-index: 10;
}
.manage-list-header input[type="checkbox"] {
width: 16px;
height: 16px;
accent-color: var(--accent-color);
cursor: pointer;
flex-shrink: 0;
}
.header-cols-container, .manage-list-item-content {
display: flex;
flex-grow: 1;
min-width: 0;
gap: 16px;
align-items: center;
}
.col-header {
position: relative;
cursor: pointer;
display: flex;
align-items: center;
padding: 0 8px;
border-right: 1px solid var(--border-color);
background-color: var(--bg-panel);
user-select: none;
-webkit-user-select: none;
}
.col-header:last-child {
border-right: none;
}
.header-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex-grow: 1;
}
.manage-list-header, .col-header, .col-resizer {
user-select: none;
-webkit-user-select: none;
}
.col-resizer {
position: absolute;
right: -5px;
top: 0;
bottom: 0;
width: 10px;
cursor: col-resize;
z-index: 10;
background-color: transparent !important;
}
.col-resizer::after {
content: "";
position: absolute;
left: 50%;
top: 0;
bottom: 0;
width: 1px;
background-color: transparent;
transition: background-color 0.2s;
}
.col-resizer:hover::after,
.col-resizer.resizing::after {
background-color: var(--text-secondary) !important;
opacity: 0.6;
}
body.resizing-active,
body.resizing-active * {
user-select: none !important;
-webkit-user-select: none !important;
cursor: col-resize !important;
}
.col-resizer:hover {
background-color: var(--accent-color);
opacity: 0.5;
}
.col-header:hover {
color: var(--text-primary);
}
.sort-icon {
font-size: 10px;
margin-left: 4px;
color: var(--accent-color);
width: 12px;
}
.row-col {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 14px;
color: var(--text-primary);
}
.custom-context-menu {
position: absolute;
z-index: 10000;
background-color: var(--bg-panel);
border: 1px solid var(--border-color);
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 4px;
display: none;
min-width: 160px;
}
.custom-context-menu.show {
display: block;
}
.custom-context-menu-item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 12px;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
color: var(--text-primary);
transition: background-color 0.2s;
}
.custom-context-menu-item:hover {
background-color: var(--bg-hover);
}
.custom-context-menu-item svg {
width: 16px;
height: 16px;
color: var(--text-secondary);
}
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border-color); border-radius: 10px; }
::-webkit-scrollbar-thumb:hover { background: var(--text-secondary); }
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

File diff suppressed because it is too large. Load diff
+923
View File
@@ -0,0 +1,923 @@
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
document.addEventListener(eventName, e => {
e.preventDefault();
});
});
document.addEventListener('DOMContentLoaded', () => {
const { dict, langMap } = window.AppI18n;
const appState = {
lang: "english_switch",
scanning: false,
firstLaunch: false,
taskmgrTimer: null,
taskmgrActive: false,
virusMap: new Map(),
virusResults: [],
junkList: []
};
const reverseI18nMap = Object.entries(dict["traditional_switch"]).reduce((acc, [key, val]) => {
acc[val] = key;
return acc;
}, {});
const initI18nKeys = () => {
document.querySelectorAll("[data-i18n]").forEach(el => {
let key = el.getAttribute('data-i18n');
if (!key) {
const text = el.textContent.trim();
key = reverseI18nMap[text] || text;
el.setAttribute('data-i18n', key);
}
el.dataset.originText = el.textContent.trim();
});
document.querySelectorAll("[data-i18n-placeholder]").forEach(el => {
let key = el.getAttribute('data-i18n-placeholder');
if (reverseI18nMap[key]) {
el.setAttribute('data-i18n-placeholder', reverseI18nMap[key]);
}
el.dataset.originPlaceholder = el.getAttribute('placeholder');
});
document.querySelectorAll("option[data-i18n]").forEach(el => {
let key = el.getAttribute('data-i18n');
if (!key) {
const text = el.textContent.trim();
key = reverseI18nMap[text] || text;
el.setAttribute('data-i18n', key);
}
});
};
document.addEventListener('click', () => {
document.querySelectorAll('.custom-select-wrapper').forEach(w => w.classList.remove('open'));
});
const getMsg = (key) => (dict[appState.lang] || dict["english_switch"])[key] || key;
const formatMsg = (key, ...args) => {
let msg = getMsg(key);
args.forEach((arg, i) => msg = msg.split(`{${i}}`).join(arg));
return msg;
};
const escapeHtml = (unsafe) => {
if (typeof unsafe !== 'string') return unsafe;
return unsafe.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
};
const themes = {
"white_switch": { "--bg-window": "245, 245, 247", "--bg-nav": "255, 255, 255", "--bg-panel": "255, 255, 255", "--bg-hover": "rgba(0,0,0,0.05)", "--text-primary": "29, 29, 31", "--text-secondary": "134, 134, 139", "--border-color":"210, 210, 215", "--accent-color":"0, 122, 255", "--accent-alpha":"rgba(0, 122, 255, 0.1)", "--accent-shadow":"rgba(0, 122, 255, 0.2)", "--accent-hover":"rgba(0, 122, 255, 0.3)" },
"black_switch": { "--bg-window": "0, 0, 0", "--bg-nav": "28, 28, 30", "--bg-panel": "28, 28, 30", "--bg-hover": "rgba(255,255,255,0.1)", "--text-primary": "245, 245, 247", "--text-secondary": "134, 134, 139", "--border-color":"44, 44, 46", "--accent-color":"10, 132, 255", "--accent-alpha":"rgba(10, 132, 255, 0.1)", "--accent-shadow":"rgba(10, 132, 255, 0.2)", "--accent-hover":"rgba(10, 132, 255, 0.3)" },
"red_switch": { "--bg-window": "253, 246, 246", "--bg-nav": "255, 240, 240", "--bg-panel": "255, 255, 255", "--bg-hover": "rgba(255,59,48,0.05)", "--text-primary": "29, 29, 31", "--text-secondary": "134, 134, 139", "--border-color":"250, 220, 220", "--accent-color":"255, 59, 48", "--accent-alpha":"rgba(255, 59, 48, 0.1)", "--accent-shadow":"rgba(255, 59, 48, 0.2)", "--accent-hover":"rgba(255, 59, 48, 0.3)" },
"yellow_switch": { "--bg-window": "254, 251, 243", "--bg-nav": "255, 248, 230", "--bg-panel": "255, 255, 255", "--bg-hover": "rgba(255,149,0,0.05)", "--text-primary": "29, 29, 31", "--text-secondary": "134, 134, 139", "--border-color":"250, 240, 215", "--accent-color":"255, 149, 0", "--accent-alpha":"rgba(255, 149, 0, 0.1)", "--accent-shadow":"rgba(255, 149, 0, 0.2)", "--accent-hover":"rgba(255, 149, 0, 0.3)" },
"green_switch": { "--bg-window": "246, 253, 248", "--bg-nav": "235, 250, 240", "--bg-panel": "255, 255, 255", "--bg-hover": "rgba(52,199,89,0.05)", "--text-primary": "29, 29, 31", "--text-secondary": "134, 134, 139", "--border-color":"220, 240, 225", "--accent-color":"52, 199, 89", "--accent-alpha":"rgba(52, 199, 89, 0.1)", "--accent-shadow":"rgba(52, 199, 89, 0.2)", "--accent-hover":"rgba(52, 199, 89, 0.3)" },
"blue_switch": { "--bg-window": "246, 249, 253", "--bg-nav": "235, 244, 255", "--bg-panel": "255, 255, 255", "--bg-hover": "rgba(0,122,255,0.05)", "--text-primary": "29, 29, 31", "--text-secondary": "134, 134, 139", "--border-color":"220, 230, 250", "--accent-color":"0, 122, 255", "--accent-alpha":"rgba(0, 122, 255, 0.1)", "--accent-shadow":"rgba(0, 122, 255, 0.2)", "--accent-hover":"rgba(0, 122, 255, 0.3)" }
};
const cols = {
log: [{key: 'time_str', label: 'col_date', flex: 1.5}, {key: 'level', label: 'col_type', flex: 0.8}, {key: 'action', label: 'col_func', flex: 1.2, isI18n: true}, {key: 'source', label: 'col_path', flex: 3}],
process: [{key: 'name', label: 'col_name', flex: 1.5}, {key: 'pid', label: 'col_pid', flex: 0.5}, {key: 'path', label: 'col_path', flex: 3}],
virus: [{key: 'label', label: 'col_type', flex: 1, isI18n: true}, {key: 'path', label: 'col_path', flex: 3}],
pathOnly: [{key: 'path', label: 'col_path', flex: 1}],
junk: [{key: 'path', label: 'col_path', flex: 3}, {key: 'sizeStr', label: 'col_size', flex: 1}],
popup: [{key: 'exe', label: 'col_prog', flex: 1}, {key: 'class', label: 'col_class', flex: 1}, {key: 'title', label: 'col_title', flex: 2}],
repair: [{key: 'display', label: 'col_repair', flex: 1, isI18n: true}]
};
const buildCustomSelectElement = (select) => {
select.style.display = 'none';
const wrapper = document.createElement('div');
wrapper.className = 'custom-select-wrapper';
const selectedOpt = select.options[select.selectedIndex];
const triggerKey = selectedOpt ? selectedOpt.getAttribute('data-i18n') : '';
const triggerTextContent = selectedOpt ? getMsg(triggerKey || selectedOpt.textContent) : '';
const triggerHTML = `
<div class="custom-select-trigger">
<span class="custom-select-text" ${triggerKey ? `data-i18n="${triggerKey}"` : ''}>${triggerTextContent}</span>
<div class="custom-select-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg></div>
</div>`;
wrapper.innerHTML = triggerHTML;
const optionsContainer = document.createElement('ul');
optionsContainer.className = 'custom-select-options';
const phantom = document.createElement('div');
phantom.className = 'custom-select-phantom';
phantom.setAttribute('aria-hidden', 'true');
Array.from(select.options).forEach(option => {
if (option.disabled) return;
const optKey = option.getAttribute('data-i18n');
const optText = optKey ? getMsg(optKey) : option.textContent;
const li = document.createElement('li');
li.className = `custom-select-option ${option.selected ? 'selected' : ''}`;
li.textContent = optText;
li.dataset.value = option.value;
if (optKey) li.setAttribute('data-i18n', optKey);
li.addEventListener('click', (e) => {
e.stopPropagation();
select.value = option.value;
const triggerText = wrapper.querySelector('.custom-select-text');
triggerText.textContent = li.textContent;
if (optKey) triggerText.setAttribute('data-i18n', optKey);
wrapper.classList.remove('open');
optionsContainer.querySelectorAll('.custom-select-option').forEach(el => el.classList.remove('selected'));
li.classList.add('selected');
select.dispatchEvent(new Event('change'));
});
optionsContainer.appendChild(li);
const phantomSpan = document.createElement('span');
phantomSpan.textContent = optText;
if (optKey) phantomSpan.setAttribute('data-i18n', optKey);
phantom.appendChild(phantomSpan);
});
wrapper.appendChild(phantom);
wrapper.appendChild(optionsContainer);
select.parentNode.insertBefore(wrapper, select.nextSibling);
const trigger = wrapper.querySelector('.custom-select-trigger');
trigger.addEventListener('click', (e) => {
e.stopPropagation();
const isOpen = wrapper.classList.contains('open');
document.querySelectorAll('.custom-select-wrapper').forEach(w => w.classList.remove('open'));
if (!isOpen) wrapper.classList.add('open');
});
};
const updateCustomSelectUI = (selectId, val) => {
const select = document.getElementById(selectId);
if (!select) return;
select.value = val;
const wrapper = select.nextElementSibling;
if (wrapper && wrapper.classList.contains('custom-select-wrapper')) {
const triggerText = wrapper.querySelector('.custom-select-text');
const selectedOpt = select.options[select.selectedIndex];
if (selectedOpt) {
const optKey = selectedOpt.getAttribute('data-i18n');
triggerText.textContent = optKey ? getMsg(optKey) : selectedOpt.textContent;
if (optKey) triggerText.setAttribute('data-i18n', optKey);
}
wrapper.querySelectorAll('.custom-select-option').forEach(el => {
el.classList.toggle('selected', el.dataset.value === val);
});
}
};
const translateText = (lang) => {
appState.lang = lang;
document.querySelectorAll("[data-i18n]").forEach(el => {
const key = el.getAttribute('data-i18n');
if (key) el.textContent = getMsg(key);
});
document.querySelectorAll("[data-i18n-placeholder]").forEach(el => {
const key = el.getAttribute('data-i18n-placeholder');
if (key) el.setAttribute('placeholder', getMsg(key));
});
document.querySelectorAll('.manage-widget, .virus-widget').forEach(widget => {
if (widget.buildHeader) widget.buildHeader();
if (widget.renderData) widget.renderData();
});
document.documentElement.lang = langMap[lang] || "en";
const progressText = document.getElementById('progress_text');
if (progressText && progressText.dataset.dynamicMsg) {
try {
const msgs = JSON.parse(progressText.dataset.dynamicMsg);
if (msgs[lang]) progressText.textContent = msgs[lang];
} catch (e) {}
}
};
const applyTheme = (theme) => {
const themeVars = themes[theme];
if (themeVars) {
for (const [key, value] of Object.entries(themeVars)) {
document.documentElement.style.setProperty(key, value.startsWith("rgba") ? value : `rgb(${value})`);
}
}
};
const renderDataGrid = (containerSelector, dataList, columns, valKey, defaultChecked = false) => {
const widget = document.querySelector(`${containerSelector} .manage-widget`) || document.querySelector(`${containerSelector} .virus-widget`);
const listUl = document.querySelector(`${containerSelector} .manage-list`) || document.querySelector(`${containerSelector} .virus-list`);
const searchInput = document.querySelector(`${containerSelector} .search-box input`);
if (!widget || !listUl) return;
const isInit = !widget.gridState;
if (isInit) {
const containerWidth = widget.clientWidth || 800;
const availableWidth = Math.max(containerWidth - 60 - (columns.length * 16), 400);
const totalFlex = columns.reduce((sum, c) => sum + (c.flex || 1), 0);
widget.gridState = {
sortKey: columns[0]?.key || '', sortAsc: true, filterText: "",
colWidths: columns.map(c => Math.max(((c.flex || 1) / totalFlex) * availableWidth, 60)),
checkedSet: new Set()
};
widget.gridState.colWidths.forEach((w, idx) => widget.style.setProperty(`--col-${idx}`, `${w}px`));
const header = document.createElement('div');
header.className = 'manage-list-header';
widget.insertBefore(header, widget.firstChild);
widget.buildHeader = () => {
const headerColsHtml = columns.map((col, idx) => `
<div class="col-header" data-key="${col.key}" data-idx="${idx}" style="width: var(--col-${idx}); flex: 0 0 auto;">
<span class="header-text" data-i18n="${col.label}">${getMsg(col.label)}</span>
<span class="sort-icon">${widget.gridState.sortKey === col.key ? (widget.gridState.sortAsc ? '▲' : '▼') : ''}</span>
<div class="col-resizer"></div>
</div>`).join('');
header.innerHTML = `<input type="checkbox" class="select-all-cb" title="${getMsg('btn_select_all')}"><div class="header-cols-container">${headerColsHtml}</div>`;
header.querySelectorAll('.col-header').forEach(el => {
el.addEventListener('click', () => {
const key = el.dataset.key;
widget.gridState.sortAsc = widget.gridState.sortKey === key ? !widget.gridState.sortAsc : true;
widget.gridState.sortKey = key;
widget.buildHeader();
widget.renderData(widget.gridState.filterText, false);
});
});
header.querySelectorAll('.col-resizer').forEach(resizer => {
resizer.addEventListener('mousedown', (e) => {
e.stopPropagation();
const idx = parseInt(resizer.parentElement.dataset.idx);
const startX = e.clientX;
const startWidth = widget.gridState.colWidths[idx];
const onMouseMove = (me) => {
const newWidth = Math.max(startWidth + (me.clientX - startX), 40);
widget.gridState.colWidths[idx] = newWidth;
widget.style.setProperty(`--col-${idx}`, `${newWidth}px`);
};
const onMouseUp = () => {
document.body.classList.remove('resizing-active');
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
};
document.body.classList.add('resizing-active');
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
});
resizer.addEventListener('click', (e) => e.stopPropagation());
});
const selectAllCb = header.querySelector('.select-all-cb');
if (selectAllCb) {
selectAllCb.addEventListener('change', (e) => {
const isChecked = e.target.checked;
listUl.querySelectorAll('.manage-list-item input[type="checkbox"]').forEach(cb => cb.checked = isChecked);
(widget.gridState.lastDisplayData || []).forEach(item => {
const valStr = String(item[valKey]);
isChecked ? widget.gridState.checkedSet.add(valStr) : widget.gridState.checkedSet.delete(valStr);
if (containerSelector === '#scan_window') appState.virusMap.set(valStr, isChecked);
});
if (containerSelector === '#scan_window') checkVirusListEmpty();
});
}
};
widget.buildHeader();
listUl.addEventListener('click', (e) => {
const li = e.target.closest('.manage-list-item');
if (!li) return;
const cb = li.querySelector('input[type="checkbox"]');
if (e.target.tagName !== 'INPUT') cb.checked = !cb.checked;
cb.checked ? widget.gridState.checkedSet.add(cb.value) : widget.gridState.checkedSet.delete(cb.value);
if (containerSelector === '#scan_window') {
appState.virusMap.set(cb.value, cb.checked);
checkVirusListEmpty();
}
if (widget.updateSelectAllState) widget.updateSelectAllState();
});
if (searchInput) searchInput.addEventListener('input', (e) => widget.renderData(e.target.value, false));
}
const state = widget.gridState;
widget.renderData = (filterText = state.filterText, resetDataCheck = false) => {
state.filterText = filterText;
if (resetDataCheck) {
state.checkedSet.clear();
if (defaultChecked) dataList.forEach(item => state.checkedSet.add(String(item[valKey])));
} else {
const validKeys = new Set(dataList.map(item => String(item[valKey])));
for (const val of state.checkedSet) if (!validKeys.has(val)) state.checkedSet.delete(val);
if (state.checkedSet.size === 0 && defaultChecked && dataList.length > 0) {
if (!listUl.querySelector('input[type="checkbox"]:checked')) {
dataList.forEach(item => state.checkedSet.add(String(item[valKey])));
}
}
}
const header = widget.querySelector('.manage-list-header');
const selectAllCb = header ? header.querySelector('.select-all-cb') : null;
if (selectAllCb) selectAllCb.disabled = dataList.length === 0;
const ft = filterText.toLowerCase();
let displayData = dataList.filter(item => !filterText || columns.some(col => String(item[col.key] || '').toLowerCase().includes(ft)));
displayData.sort((a, b) => {
const valA = a[state.sortKey] ?? '', valB = b[state.sortKey] ?? '';
if (state.sortKey === 'sizeStr' && 'size' in a && 'size' in b) return state.sortAsc ? a.size - b.size : b.size - a.size;
const numA = parseFloat(valA), numB = parseFloat(valB);
if (!isNaN(numA) && !isNaN(numB) && String(valA).trim() !== '' && String(valB).trim() !== '') return state.sortAsc ? numA - numB : numB - numA;
return state.sortAsc ? String(valA).localeCompare(String(valB)) : String(valB).localeCompare(String(valA));
});
widget.gridState.lastDisplayData = displayData;
widget.updateSelectAllState = () => {
if (!selectAllCb) return;
const cData = widget.gridState.lastDisplayData || [];
if (cData.length === 0) { selectAllCb.checked = false; selectAllCb.indeterminate = false; return; }
const checkedCount = cData.filter(item => containerSelector === '#scan_window' ? appState.virusMap.get(String(item[valKey])) : widget.gridState.checkedSet.has(String(item[valKey]))).length;
selectAllCb.checked = (checkedCount > 0 && checkedCount === cData.length);
selectAllCb.indeterminate = (checkedCount > 0 && checkedCount < cData.length);
};
const MAX_ITEMS = 2000;
const limitData = displayData.slice(0, MAX_ITEMS);
let htmlContent = limitData.map(item => {
const itemValStr = String(item[valKey]);
let isChecked = containerSelector === '#scan_window' ? (appState.virusMap.get(itemValStr) ?? true) : state.checkedSet.has(itemValStr);
if (containerSelector === '#scan_window') appState.virusMap.set(itemValStr, isChecked);
const rowColsHtml = columns.map((col, idx) => {
const rawVal = String(item[col.key] || '');
let valStr = col.isI18n ? getMsg(rawVal) : rawVal;
if (rawVal === '未知路徑' || rawVal === 'path_unknown') valStr = getMsg('path_unknown');
return `<div class="row-col" style="width: var(--col-${idx}); flex: 0 0 auto;" title="${escapeHtml(valStr)}">${escapeHtml(valStr)}</div>`;
}).join('');
return `<li class="manage-list-item"><input type="checkbox" value="${escapeHtml(itemValStr)}" ${isChecked ? 'checked' : ''} data-path="${escapeHtml(item.path || '')}"><div class="manage-list-item-content">${rowColsHtml}</div></li>`;
}).join('');
if (displayData.length > MAX_ITEMS) {
htmlContent += `<li class="manage-list-item" style="pointer-events: none; justify-content: center;"><div style="color: var(--text-secondary); padding: 10px;">${escapeHtml(formatMsg("msg_too_many", MAX_ITEMS))}</div></li>`;
}
const scrollContainer = listUl.parentElement;
const scrollTop = scrollContainer ? scrollContainer.scrollTop : 0;
listUl.innerHTML = htmlContent;
if (scrollContainer) scrollContainer.scrollTop = scrollTop;
widget.updateSelectAllState();
};
widget.renderData(state.filterText, isInit);
};
const getCheckedValues = (selector) => Array.from(document.querySelectorAll(`${selector} .manage-list-item input[type="checkbox"]:checked`)).map(cb => cb.value);
const formatSize = (bytes) => {
if (bytes === 0) return '0 B';
const k = 1024, sizes = ['B', 'KB', 'MB', 'GB'], i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
const rebuildCustomSelect = (selectId) => {
const select = document.getElementById(selectId);
if (!select) return;
let sibling = select.nextElementSibling;
while (sibling && sibling.classList.contains('custom-select-wrapper')) {
const toRemove = sibling;
sibling = sibling.nextElementSibling;
toRemove.remove();
}
buildCustomSelectElement(select);
};
const changeScanSelectMode = (mode) => {
const select = document.getElementById('scan_method_select');
if (!select) return;
select.innerHTML = `<option value="none" selected disabled data-i18n="btn_select">${getMsg('btn_select')}</option>`;
if (mode === 'scan') {
select.innerHTML += `
<option value="smart" data-i18n="scan_smart">${getMsg('scan_smart')}</option>
<option value="file" data-i18n="scan_file">${getMsg('scan_file')}</option>
<option value="path" data-i18n="scan_path">${getMsg('scan_path')}</option>
<option value="full" data-i18n="scan_full">${getMsg('scan_full')}</option>`;
} else if (mode === 'action') {
select.innerHTML += `
<option value="delete" data-i18n="act_del">${getMsg('act_del')}</option>
<option value="ignore" data-i18n="act_ignore">${getMsg('act_ignore')}</option>
<option value="quarantine" data-i18n="act_quarantine">${getMsg('act_quarantine')}</option>
<option value="whitelist" data-i18n="act_white">${getMsg('act_white')}</option>`;
}
rebuildCustomSelect('scan_method_select');
};
const checkVirusListEmpty = () => {
updateCustomSelectUI('scan_method_select', 'none');
if (appState.virusResults.length === 0) {
appState.virusMap.clear();
changeScanSelectMode('scan');
const title = document.querySelector('#scan_window .section-title');
const text = document.getElementById('progress_text');
if (title) {
title.setAttribute('data-i18n', 'scan_virus');
title.textContent = getMsg('scan_virus');
}
if (text) {
text.setAttribute('data-i18n', 'scan_virus_desc');
text.removeAttribute('data-dynamic-msg');
text.textContent = getMsg('scan_virus_desc');
}
}
};
const triggerScan = (method) => {
if (!window.pywebview) return;
appState.scanning = true;
appState.virusMap.clear();
appState.virusResults = [];
renderDataGrid('#scan_window', appState.virusResults, cols.virus, 'path', true);
changeScanSelectMode('scan');
document.getElementById('scan_method_select').classList.add('hidden');
const stopBtn = document.getElementById('stop_btn');
stopBtn.classList.remove('hidden');
stopBtn.disabled = false;
const title = document.querySelector('#scan_window .section-title');
const text = document.getElementById('progress_text');
if (title) { title.setAttribute('data-i18n', 'status_scanning'); title.textContent = getMsg('status_scanning'); }
if (text) { text.setAttribute('data-i18n', 'msg_init'); text.removeAttribute('data-dynamic-msg'); text.textContent = getMsg('msg_init'); }
window.pywebview.api.trigger_scan(method);
};
const handleVirusActions = (action) => {
if (!window.pywebview) return;
const paths = Array.from(appState.virusMap.entries()).filter(([_, checked]) => checked).map(([p]) => p);
if (paths.length === 0) return updateCustomSelectUI('scan_method_select', 'none');
const wrapper = document.getElementById('scan_method_select').nextElementSibling;
if (wrapper) wrapper.style.pointerEvents = 'none';
const finalize = () => {
if (wrapper) wrapper.style.pointerEvents = '';
checkVirusListEmpty();
};
const removeItems = (removedPaths) => {
const removedSet = new Set(removedPaths);
removedPaths.forEach(p => appState.virusMap.delete(p));
appState.virusResults = appState.virusResults.filter(r => !removedSet.has(r.path));
renderDataGrid('#scan_window', appState.virusResults, cols.virus, 'path', false);
};
if (action === 'delete') {
window.pywebview.api.solve_scan(paths).then((deleted) => { removeItems(deleted); finalize(); });
} else if (action === 'ignore') {
window.pywebview.api.remove_virus_result(paths).then(() => { removeItems(paths); finalize(); });
} else if (action === 'quarantine' || action === 'whitelist') {
const listKey = action === 'quarantine' ? 'quarantine' : 'white_list';
window.pywebview.api.manage_named_list(listKey, paths, 'add').then(() => {
window.pywebview.api.remove_virus_result(paths).then(() => { removeItems(paths); finalize(); });
});
}
};
const fetchProcs = () => {
if (!appState.taskmgrActive || !window.pywebview) return;
const widget = document.querySelector('#taskmgr_window .manage-widget');
const blockFetch = widget && (widget.gridState?.checkedSet.size > 0 || widget.matches(':hover')) || document.querySelector('.custom-context-menu.show') || document.body.classList.contains('resizing-active');
if (!blockFetch) {
window.pywebview.api.get_process_list().then(procs => {
const mapped = procs.map(p => ({ name: p.name, pid: p.pid, path: (p.path && p.path !== "None" && p.path !== "Unknown") ? p.path : "path_unknown" }));
renderDataGrid('#taskmgr_window', mapped, cols.process, 'pid', false);
if (appState.taskmgrActive) appState.taskmgrTimer = setTimeout(fetchProcs, 2000);
}).catch(() => { if (appState.taskmgrActive) appState.taskmgrTimer = setTimeout(fetchProcs, 2000); });
} else {
if (appState.taskmgrActive) appState.taskmgrTimer = setTimeout(fetchProcs, 2000);
}
};
const refreshConfigLists = () => {
if (!window.pywebview) return;
window.pywebview.api.get_config().then(cfg => {
renderDataGrid('#whitelist_window', (cfg.white_list || []).map(i => ({path: i.file || i})), cols.pathOnly, 'path');
renderDataGrid('#quarantine_window', (cfg.quarantine || []).map(i => ({path: i.file || i})), cols.pathOnly, 'path');
renderDataGrid('#custom_protect_window', (cfg.custom_rule || []).map(i => ({path: i.file || i})), cols.pathOnly, 'path');
const popupRules = (cfg.block_list || []).map(item => ({ exe: item.exe || '*', class: item.class || '*', title: item.title || '*', value: item.exe || item.title }));
renderDataGrid('#popup_window', popupRules, cols.popup, 'value');
});
};
const switchPage = (targetId) => {
const oldActive = document.querySelector('.page.active');
if (oldActive && oldActive.id !== targetId) {
if (oldActive.id === 'junk_window') {
appState.junkList = [];
renderDataGrid('#junk_window', [], cols.junk, 'path', true);
const input = document.querySelector('#junk_window .search-box input');
if (input) input.value = '';
} else if (oldActive.id === 'repair_window') {
renderDataGrid('#repair_window', [], cols.repair, 'value', true);
}
}
document.querySelectorAll('.page, .nav-btn').forEach(el => el.classList.remove('active'));
document.getElementById(targetId)?.classList.add('active');
document.querySelector(`aside .nav-btn[data-target="${targetId}"]`)?.classList.add('active');
appState.taskmgrActive = targetId === 'taskmgr_window';
if (appState.taskmgrActive) fetchProcs();
else if (appState.taskmgrTimer) { clearTimeout(appState.taskmgrTimer); appState.taskmgrTimer = null; }
if (['whitelist_window', 'quarantine_window', 'popup_window', 'custom_protect_window'].includes(targetId)) refreshConfigLists();
if (targetId === 'log_export_window' && window.pywebview) {
window.pywebview.api.get_logs().then(logs => {
const logList = logs.reverse().map(log => ({ time_str: `[${log.time_str}]`, level: log.level, action: log.action, source: log.source || '-', id: log.id }));
renderDataGrid('#log_export_window', logList, cols.log, 'id', true);
});
}
};
window.addVirusResult = (label, path) => {
if (!appState.virusMap.has(path)) {
appState.virusResults.push({ label, path });
appState.virusMap.set(path, true);
const widget = document.querySelector('#scan_window .virus-widget');
if (widget && widget.renderData) {
if (window.virusRenderTimeout) clearTimeout(window.virusRenderTimeout);
window.virusRenderTimeout = setTimeout(() => widget.renderData(widget.gridState.filterText, false), 50);
}
}
};
window.updateScanProgress = (path) => {
const title = document.querySelector('#scan_window .section-title');
const text = document.getElementById('progress_text');
if (title) { title.setAttribute('data-i18n', 'status_scanning'); title.textContent = getMsg('status_scanning'); }
if (text) { text.removeAttribute('data-i18n'); text.removeAttribute('data-dynamic-msg'); text.textContent = path; }
};
window.updateDeleteProgress = (path) => {
const title = document.querySelector('#scan_window .section-title');
const text = document.getElementById('progress_text');
if (title) { title.setAttribute('data-i18n', 'status_deleting'); title.textContent = getMsg('status_deleting'); }
if (text) { text.removeAttribute('data-i18n'); text.removeAttribute('data-dynamic-msg'); text.textContent = path; }
};
window.finishScan = (msgData, count) => {
appState.scanning = false;
const title = document.querySelector('#scan_window .section-title');
const text = document.getElementById('progress_text');
if (title) { title.setAttribute('data-i18n', 'scan_virus'); title.textContent = getMsg('scan_virus'); }
if (text) {
if (typeof msgData === 'object' && msgData !== null) {
text.removeAttribute('data-i18n');
text.dataset.dynamicMsg = JSON.stringify(msgData);
text.textContent = msgData[appState.lang] || msgData["english_switch"];
} else {
text.removeAttribute('data-dynamic-msg');
text.setAttribute('data-i18n', msgData);
text.textContent = getMsg(msgData);
}
}
document.getElementById('stop_btn')?.classList.add('hidden');
document.getElementById('scan_method_select')?.classList.remove('hidden');
changeScanSelectMode(count > 0 ? 'action' : 'scan');
updateCustomSelectUI('scan_method_select', 'none');
};
window.updateLogs = (entry) => {
const logWidget = document.querySelector('.log-text');
if (logWidget) {
let parts = [`[${entry.time_str}]`, entry.level, getMsg(entry.action || '')];
if (entry.source) parts.push(`Src: ${entry.source}`);
if (entry.target) parts.push(`Tgt: ${entry.target}`);
if (entry.code) parts.push(`Code: ${entry.code}`);
if (entry.pid) parts.push(`PID: ${entry.pid}`);
if (entry.hash) parts.push(`Hash: ${entry.hash}`);
if (entry.detail) parts.push(`Detail: ${entry.detail}`);
if (entry.operate !== null) parts.push(`Op: ${entry.operate}`);
parts.push(`Success: ${entry.success}`);
let val = logWidget.value + parts.join(' | ') + '\n';
if (val.length > 20000) val = val.substring(val.indexOf('\n', val.length - 15000) + 1);
logWidget.value = val;
logWidget.scrollTop = logWidget.scrollHeight;
}
if (document.getElementById('log_export_window')?.classList.contains('active') && window.pywebview) {
window.pywebview.api.get_logs().then(logs => {
const logList = logs.reverse().map(log => ({ time_str: `[${log.time_str}]`, level: log.level, action: log.action, source: log.source || '-', id: log.id }));
renderDataGrid('#log_export_window', logList, cols.log, 'id', true);
});
}
if ((entry.action === 'System' && entry.detail === 'Engine Initialization Complete') || (entry.level === 'WARN' && entry.action === 'init_engine_thread')) {
const overlay = document.getElementById('loading_overlay');
if (overlay && !overlay.classList.contains('fade-out')) {
overlay.classList.add('fade-out');
setTimeout(() => {
document.querySelector('.app-container')?.classList.add('fade-in');
if (window.onEngineReady) window.onEngineReady();
}, 400);
}
}
};
window.triggerContextScan = (targets) => {
if (appState.scanning) return;
switchPage('scan_window');
if (window.pywebview) {
appState.scanning = true;
appState.virusMap.clear();
appState.virusResults = [];
renderDataGrid('#scan_window', appState.virusResults, cols.virus, 'path', true);
document.getElementById('scan_method_select')?.classList.add('hidden');
const stopBtn = document.getElementById('stop_btn');
if (stopBtn) { stopBtn.classList.remove('hidden'); stopBtn.disabled = false; }
changeScanSelectMode('scan');
const title = document.querySelector('#scan_window .section-title');
const text = document.getElementById('progress_text');
if (title) { title.setAttribute('data-i18n', 'status_scanning'); title.textContent = getMsg('status_scanning'); }
if (text) { text.setAttribute('data-i18n', 'msg_init'); text.removeAttribute('data-dynamic-msg'); text.textContent = getMsg('msg_init'); }
const paths = Array.isArray(targets) ? targets : [targets];
window.pywebview.api.start_scan(paths);
}
};
window.revertSwitch = () => {};
document.querySelectorAll('[data-target]').forEach(btn => btn.addEventListener('click', (e) => switchPage(e.currentTarget.getAttribute('data-target'))));
document.getElementById('quick_scan_btn')?.addEventListener('click', () => { if (!appState.scanning) { switchPage('scan_window'); triggerScan('smart'); }});
document.getElementById('stop_btn')?.addEventListener('click', (e) => { if (window.pywebview) window.pywebview.api.stop_scan(); e.target.disabled = true; });
document.getElementById('theme_select')?.addEventListener('change', (e) => { applyTheme(e.target.value); window.pywebview?.api.update_config('theme', e.target.value); });
document.getElementById('lang_select')?.addEventListener('change', (e) => { translateText(e.target.value); window.pywebview?.api.update_config('language', e.target.value); });
document.getElementById('scan_method_select')?.addEventListener('change', (e) => {
const val = e.target.value;
if (val === 'none' || appState.scanning) return;
if (['smart', 'file', 'path', 'full'].includes(val)) triggerScan(val);
else if (['delete', 'ignore', 'quarantine', 'whitelist'].includes(val)) handleVirusActions(val);
});
document.querySelectorAll('.toggle-switch input').forEach((toggle, index) => {
toggle.addEventListener('change', async (e) => {
const switchMap = ["process_switch", "document_switch", "system_switch", "driver_switch", "network_switch", "sensitive_switch", "extension_switch", "cloud_switch", "context_switch"];
const key = switchMap[index];
if (window.pywebview && key) {
toggle.disabled = true;
try {
const result = await window.pywebview.api.update_config(key, e.target.checked);
if (result !== undefined && result !== null) toggle.checked = result;
} catch { toggle.checked = !e.target.checked; }
finally { toggle.disabled = false; }
}
});
});
document.querySelector('#junk_window .primary-btn')?.addEventListener('click', (e) => {
if (!window.pywebview) return;
e.target.setAttribute('data-i18n', 'msg_init'); e.target.textContent = getMsg('msg_init');
const widget = document.querySelector('#junk_window .manage-widget');
if (widget?.gridState) widget.gridState.checkedSet.clear();
window.pywebview.api.scan_system_junk().then(list => {
appState.junkList = list.map(item => ({ path: item.path, sizeStr: formatSize(item.size), size: item.size }));
renderDataGrid('#junk_window', appState.junkList, cols.junk, 'path', true);
e.target.setAttribute('data-i18n', 'btn_scan'); e.target.textContent = getMsg('btn_scan');
});
});
document.querySelector('#junk_window .danger-btn')?.addEventListener('click', () => {
const checked = getCheckedValues('#junk_window');
if (checked.length === 0 || !window.pywebview) return;
window.pywebview.api.clean_system_junk(checked).then(size => {
window.pywebview.api.show_alert(getMsg("title_prompt"), `${getMsg("msg_cleaned")}${formatSize(size)}`, "info");
appState.junkList = appState.junkList.filter(item => !checked.includes(item.path));
renderDataGrid('#junk_window', appState.junkList, cols.junk, 'path', true);
});
});
document.querySelector('#repair_window .primary-btn')?.addEventListener('click', () => {
if (!window.pywebview) return;
window.pywebview.api.scan_system_repair().then(list => renderDataGrid('#repair_window', list, cols.repair, 'value', true));
});
document.querySelector('#repair_window .danger-btn')?.addEventListener('click', () => {
const checked = getCheckedValues('#repair_window');
if (checked.length === 0 || !window.pywebview) return;
window.pywebview.api.execute_system_repair(checked).then(() => {
window.pywebview.api.show_alert(getMsg("title_prompt"), getMsg("msg_repaired"), "info");
renderDataGrid('#repair_window', [], cols.repair, 'value');
});
});
['whitelist_window', 'quarantine_window', 'popup_window', 'custom_protect_window'].forEach(id => {
const listKeyMap = {
'popup_window': 'block_list',
'custom_protect_window': 'custom_rule',
'whitelist_window': 'white_list',
'quarantine_window': 'quarantine'
};
const listKey = listKeyMap[id];
document.querySelector(`#${id} .modern-btn:not(.primary-btn)`)?.addEventListener('click', () => {
const checked = getCheckedValues(`#${id}`);
if (checked.length > 0 && window.pywebview) window.pywebview.api.remove_list_items(listKey, checked).then(refreshConfigLists);
});
if (id !== 'popup_window') {
document.querySelector(`#${id} .primary-btn`)?.addEventListener('click', () => {
window.pywebview?.api.select_files().then(files => {
if (files?.length > 0) window.pywebview.api.manage_named_list(listKey, files, 'add').then(refreshConfigLists);
});
});
}
});
document.querySelector('#popup_window .primary-btn')?.addEventListener('click', (e) => {
if (!window.pywebview) return;
e.target.setAttribute('data-i18n', 'msg_click_target'); e.target.textContent = getMsg('msg_click_target'); e.target.disabled = true;
window.pywebview.api.capture_popup_window().then(rule => {
e.target.setAttribute('data-i18n', 'btn_add'); e.target.textContent = getMsg('btn_add'); e.target.disabled = false;
if (rule) window.pywebview.api.add_popup_rule(rule).then(ok => ok && refreshConfigLists());
});
});
document.querySelector('#taskmgr_window .primary-btn')?.addEventListener('click', () => {
if (!window.pywebview) return;
document.querySelectorAll('#taskmgr_window .manage-list-item input[type="checkbox"]:checked').forEach(cb => {
if (cb.dataset.path && cb.dataset.path !== "path_unknown") window.pywebview.api.open_file_location(cb.dataset.path);
});
});
document.querySelector('#taskmgr_window .danger-btn')?.addEventListener('click', () => {
const cbs = document.querySelectorAll('#taskmgr_window .manage-list-item input[type="checkbox"]:checked');
if (cbs.length > 0 && window.pywebview) {
window.pywebview.api.show_confirm(getMsg("msg_end_proc_title"), getMsg("msg_end_proc_confirm")).then(res => {
if (res) Promise.all(Array.from(cbs).map(cb => window.pywebview.api.kill_process(parseInt(cb.value)))).then(() => {
window.pywebview.api.get_process_list().then(procs => renderDataGrid('#taskmgr_window', procs.map(p => ({ name: p.name, pid: p.pid, path: (p.path && p.path !== "None" && p.path !== "Unknown") ? p.path : "path_unknown" })), cols.process, 'pid', false));
});
});
}
});
document.querySelector('#log_export_window .primary-btn')?.addEventListener('click', () => {
const checked = getCheckedValues('#log_export_window');
if (checked.length === 0) return window.pywebview?.api.show_alert(getMsg("title_prompt"), getMsg("msg_sel_log_export"), "warning");
window.pywebview?.api.export_logs(checked).then(res => res && window.pywebview.api.show_alert(getMsg("title_prompt"), getMsg("msg_export_success"), "info"));
});
document.querySelector('#log_export_window .danger-btn')?.addEventListener('click', () => {
const checked = getCheckedValues('#log_export_window');
if (checked.length === 0 || !window.pywebview) return;
window.pywebview.api.show_confirm(getMsg("title_prompt"), getMsg("msg_del_log_confirm")).then(res => {
if (res) window.pywebview.api.clear_logs(checked).then(() => {
window.pywebview.api.get_logs().then(logs => renderDataGrid('#log_export_window', logs.reverse().map(log => ({ time_str: `[${log.time_str}]`, level: log.level, action: log.action, source: log.source || '-', id: log.id })), cols.log, 'id', true));
window.pywebview.api.show_alert(getMsg("title_prompt"), getMsg("msg_log_deleted"), "info");
});
});
});
document.getElementById('minimize_button')?.addEventListener('click', () => window.pywebview?.api.minimize());
document.getElementById('close_button')?.addEventListener('click', () => window.pywebview?.api.hide_window());
const contextMenu = document.createElement('div');
contextMenu.className = 'custom-context-menu';
contextMenu.innerHTML = `<div class="custom-context-menu-item" id="ctx_open_location"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg><span data-i18n="ctx_open_loc"></span></div>`;
document.body.appendChild(contextMenu);
let ctxTarget = null;
document.addEventListener('contextmenu', (e) => {
const li = e.target.closest('.manage-list-item');
if (li) {
const cb = li.querySelector('input[type="checkbox"]');
if (cb && cb.dataset.path && cb.dataset.path !== "path_unknown") {
e.preventDefault();
ctxTarget = cb.dataset.path;
const span = contextMenu.querySelector('span');
span.textContent = getMsg('ctx_open_loc');
contextMenu.style.left = `${e.clientX}px`;
contextMenu.style.top = `${e.clientY}px`;
contextMenu.classList.add('show');
const rect = contextMenu.getBoundingClientRect();
if (rect.right > window.innerWidth) contextMenu.style.left = `${window.innerWidth - rect.width - 5}px`;
if (rect.bottom > window.innerHeight) contextMenu.style.top = `${window.innerHeight - rect.height - 5}px`;
return;
}
}
contextMenu.classList.remove('show');
});
document.addEventListener('click', (e) => { if (!e.target.closest('.custom-context-menu')) contextMenu.classList.remove('show'); });
document.addEventListener('scroll', () => contextMenu.classList.remove('show'), true);
document.getElementById('ctx_open_location').addEventListener('click', () => {
if (window.pywebview && ctxTarget) window.pywebview.api.open_file_location(ctxTarget);
contextMenu.classList.remove('show');
});
initI18nKeys();
const bindSettingAction = (key, action) => {
const item = Array.from(document.querySelectorAll('#about_window .list-item, #setting_window .list-item')).find(el => {
const h2 = el.querySelector('h2');
return h2 && h2.getAttribute('data-i18n') === key;
});
if (item) {
const btn = item.querySelector('button');
if (btn) {
btn.addEventListener('click', action);
} else {
item.style.cursor = 'pointer';
item.dataset.action = key;
item.addEventListener('click', action);
}
}
};
bindSettingAction('web_official', () => window.pywebview?.api.open_website());
bindSettingAction('update_check', () => {
window.pywebview?.api.check_update().then(res => {
if (res.error) window.pywebview.api.show_alert(getMsg("title_error"), getMsg("msg_update_fail"), "error");
else if (res.has_update) window.pywebview.api.show_confirm(getMsg("title_prompt"), `${getMsg("msg_new_version")} ${res.latest}\n(${res.current} -> ${res.latest})`).then(ok => ok && window.pywebview.api.open_url(res.url));
else window.pywebview.api.show_alert(getMsg("title_prompt"), `${getMsg("msg_latest_version")} ${res.current}`, "info");
});
});
bindSettingAction('opt_reset', () => {
window.pywebview?.api.show_confirm(getMsg("title_prompt"), getMsg("opt_reset_desc")).then(res => {
if (res) window.pywebview.api.reset_config().then(() => window.pywebview.api.show_alert(getMsg("title_prompt"), getMsg("msg_reset_success"), "info"));
});
});
document.querySelectorAll('select.modern-select').forEach(buildCustomSelectElement);
renderDataGrid('#scan_window', appState.virusResults, cols.virus, 'path', true);
renderDataGrid('#taskmgr_window', [], cols.process, 'pid', false);
renderDataGrid('#junk_window', [], cols.junk, 'path', true);
renderDataGrid('#repair_window', [], cols.repair, 'value', true);
renderDataGrid('#whitelist_window', [], cols.pathOnly, 'path', false);
renderDataGrid('#quarantine_window', [], cols.pathOnly, 'path', false);
renderDataGrid('#custom_protect_window', [], cols.pathOnly, 'path', false);
renderDataGrid('#popup_window', [], cols.popup, 'value', false);
renderDataGrid('#log_export_window', [], cols.log, 'id', true);
window.addEventListener('pywebviewready', () => {
window.pywebview.api.get_config().then(cfg => {
const theme = cfg.theme || "white_switch";
const lang = cfg.language || "english_switch";
appState.firstLaunch = cfg.first_launch;
applyTheme(theme);
translateText(lang);
updateCustomSelectUI('theme_select', theme);
updateCustomSelectUI('lang_select', lang);
const switchMap = ["process_switch", "document_switch", "system_switch", "driver_switch", "network_switch", "sensitive_switch", "extension_switch", "cloud_switch", "context_switch"];
document.querySelectorAll('.toggle-switch input').forEach((toggle, index) => {
if (appState.firstLaunch) {
toggle.checked = false;
toggle.disabled = true;
} else if (switchMap[index]) {
toggle.checked = !!cfg[switchMap[index]];
}
});
window.pywebview.api.init_ui_ready();
}).catch(err => {
console.error("Config load failed:", err);
window.pywebview.api.init_ui_ready();
});
});
window.onEngineReady = async () => {
if (!appState.firstLaunch) return;
await window.pywebview.api.update_config("first_launch", false);
const switchMap = ["process_switch", "document_switch", "system_switch", "driver_switch", "network_switch", "sensitive_switch", "extension_switch", "cloud_switch", "context_switch"];
const seq = ["cloud_switch", "process_switch", "document_switch", "system_switch", "network_switch", "driver_switch"];
for (const key of seq) {
const index = switchMap.indexOf(key);
if (index !== -1) {
const toggle = document.querySelectorAll('.toggle-switch input')[index];
if (toggle) {
toggle.checked = true;
try {
const res = await window.pywebview.api.update_config(key, true);
if (res !== undefined && res !== null) toggle.checked = res;
} catch { toggle.checked = false; }
finally { toggle.disabled = false; }
await new Promise(r => setTimeout(r, 200));
}
}
}
document.querySelectorAll('.toggle-switch input').forEach(t => t.disabled = false);
appState.firstLaunch = false;
};
});
+427
View File
@@ -0,0 +1,427 @@
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PYAS Security</title>
<link rel="stylesheet" href="/static/css/style.css">
<script defer src="/static/js/i18n.js"></script>
<script defer src="/static/js/main.js"></script>
</head>
<body>
<div id="loading_overlay">
<svg class="spinner" viewBox="0 0 50 50">
<circle class="path" cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
</svg>
</div>
<div class="app-container" style="opacity: 0; transition: opacity 0.5s ease;">
<header class="navigate-window pywebview-drag-region">
<div class="navigate-title" data-i18n>PYAS Security</div>
<div class="window-controls">
<button class="ctrl-btn" id="about_button" data-target="about_window">
<svg viewBox="0 0 24 24" class="theme-svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>
</button>
<button class="ctrl-btn" id="minimize_button">
<svg viewBox="0 0 24 24" class="theme-svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line></svg>
</button>
<button class="ctrl-btn" id="close_button">
<svg viewBox="0 0 24 24" class="theme-svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
</button>
</div>
</header>
<div class="main-layout">
<aside class="options-window pywebview-drag-region">
<button class="nav-btn active" data-target="home_window">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
</button>
<button class="nav-btn" data-target="scan_window">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
</button>
<button class="nav-btn" data-target="tool_window">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><rect x="3" y="14" width="7" height="7"></rect></svg>
</button>
<button class="nav-btn" data-target="protect_window">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg>
</button>
<div class="spacer"></div>
<button class="nav-btn" data-target="setting_window">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
</button>
</aside>
<main class="content-window">
<section id="home_window" class="page active">
<div class="home-top-widget">
<div class="state-widget">
<svg viewBox="0 0 24 24" class="state-icon theme-svg" style="width: 120px; height: 120px;" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path><polyline points="9 12 11 14 15 10"></polyline></svg>
<h2 class="state-title" data-i18n>此裝置已受到防護</h2>
</div>
<div class="quick-actions">
<div class="list-item" style="cursor: pointer;" id="quick_scan_btn" data-action="scan">
<div class="item-info">
<h2 data-i18n>智能掃描</h2><p data-i18n>此選項可以快速掃描裝置安全性</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="log_export_window">
<div class="item-info">
<h2 data-i18n>日誌導出</h2><p data-i18n>此選項可以篩選日誌並匯出存檔</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
</div>
</div>
<div class="log-widget">
<textarea class="log-text" readonly></textarea>
</div>
</section>
<section id="scan_window" class="page">
<div class="progress-widget">
<div class="header-group">
<h2 class="section-title" data-i18n>病毒掃描</h2>
<p class="section-desc" id="progress_text" data-i18n>此選項可以選擇進階掃描方式</p>
</div>
<div class="action-group modern-action-group">
<select id="scan_method_select" class="modern-select">
<option value="none" selected disabled data-i18n>選擇</option>
<option value="smart" data-i18n>智能掃描</option>
<option value="file" data-i18n>檔案掃描</option>
<option value="path" data-i18n>路徑掃描</option>
<option value="full" data-i18n>全盤掃描</option>
</select>
<button class="action-btn modern-btn hidden" id="stop_btn" data-i18n>停止</button>
</div>
</div>
<div class="scan-content">
<div class="virus-widget">
<ul class="virus-list" id="virus_list"></ul>
</div>
</div>
</section>
<section id="protect_window" class="page">
<div class="list-widget">
<div class="list-item">
<div class="item-info">
<h2 data-i18n>進程防護</h2><p data-i18n>此選項可以終止惡意進程運行</p>
</div>
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>檔案防護</h2><p data-i18n>此選項可以隔離惡意檔案建立</p>
</div>
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>系統防護</h2><p data-i18n>此選項可以實時修復系統異常</p>
</div>
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>驅動防護</h2><p data-i18n>此選項可以實時攔截惡意行為</p>
</div>
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>網路防護</h2><p data-i18n>此選項可以攔截惡意網路連接</p>
</div>
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
</div>
<div class="list-item" style="cursor: pointer;" data-target="custom_protect_window">
<div class="item-info">
<h2 data-i18n>自訂防護</h2><p data-i18n>此選項可以導入自訂防護規則</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
</div>
</section>
<section id="tool_window" class="page">
<div class="list-widget">
<div class="list-item" style="cursor: pointer;" data-target="taskmgr_window">
<div class="item-info">
<h2 data-i18n>進程管理</h2><p data-i18n>此選項可以實時查看系統進程</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="junk_window">
<div class="item-info">
<h2 data-i18n>垃圾清理</h2><p data-i18n>此選項可以清理暫存垃圾檔案</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="repair_window">
<div class="item-info">
<h2 data-i18n>系統修復</h2><p data-i18n>此選項可以修復系統登錄檔項目</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="popup_window">
<div class="item-info">
<h2 data-i18n>彈窗攔截</h2><p data-i18n>此選項可以選擇指定窗口攔截</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="whitelist_window">
<div class="item-info">
<h2 data-i18n>白名單</h2><p data-i18n>此選項可以查看並管理白名單</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="quarantine_window">
<div class="item-info">
<h2 data-i18n>隔離區</h2><p data-i18n>此選項可以查看並管理隔離區</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
</div>
</section>
<section id="setting_window" class="page">
<div class="list-widget">
<div class="list-item">
<div class="item-info">
<h2 data-i18n>增強模式</h2><p data-i18n>此選項可以增強靜態掃描引擎</p>
</div>
<label class="toggle-switch"><input type="checkbox"><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>擴展引擎</h2><p data-i18n>此選項可以使用擴展規則引擎</p>
</div>
<label class="toggle-switch"><input type="checkbox"><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>雲端服務</h2><p data-i18n>此選項可以自動上報可疑檔案</p>
</div>
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>選單掃描</h2><p data-i18n>此選項可以啟用右鍵掃描功能</p>
</div>
<label class="toggle-switch"><input type="checkbox"><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>顯色主題</h2><p data-i18n>此選項可以選擇主題顏色</p>
</div>
<select id="theme_select" class="modern-select">
<option value="white_switch" data-i18n>白色主題</option>
<option value="red_switch" data-i18n>紅色主題</option>
<option value="yellow_switch" data-i18n>黃色主題</option>
<option value="green_switch" data-i18n>綠色主題</option>
<option value="blue_switch" data-i18n>藍色主題</option>
<option value="black_switch" data-i18n>黑色主題</option>
</select>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>顯示語言</h2><p data-i18n>此選項可以選擇顯示語言</p>
</div>
<select id="lang_select" class="modern-select">
<option value="english_switch">English</option>
<option value="traditional_switch">繁體中文</option>
<option value="simplified_switch">简体中文</option>
<option value="japanese_switch">日本語</option>
<option value="korean_switch">한국어</option>
<option value="french_switch">Français</option>
<option value="spanish_switch">Español</option>
<option value="hindi_switch">हिन्दी</option>
<option value="arabic_switch">العربية</option>
<option value="russian_switch">Русский</option>
<option value="slovenian_switch">Slovenščina</option>
</select>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>重置選項</h2><p data-i18n>此選項可以重置所有設定</p>
</div>
<button class="action-btn modern-btn danger-btn" data-i18n>執行</button>
</div>
</div>
</section>
<section id="about_window" class="page">
<div class="list-widget">
<div class="list-item">
<div class="item-info">
<h2 data-i18n>產品金鑰</h2><p data-i18n>此選項可以啟用產品金鑰</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"></path></svg></button>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>官方網站</h2><p data-i18n>此選項可以前往官方網站</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg></button>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>檢查更新</h2><p data-i18n>此選項可以檢查可用更新</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"></polyline><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path></svg></button>
</div>
<div class="license-widget">
<textarea class="license-text" readonly>87owo Publicly Available Source Code License Agreement
Last updated: 2025-12-19
1. License and Ownership
- This software and its source code are source available for public view.
- Access or execution of certain features may require an activation key.
- 87owo reserves all legal rights, no intellectual property is transferred.
- All project archives, source code, and binaries remain fully proprietary.
2. Use and Prohibitions
- You may view, download, and modify the code for local and personal use.
- Redistribution of any original or modified code requires prior consent.
- Commercial hosting or SaaS deployment is prohibited without permission.
- Reverse engineering, license removal, or illegal use is strictly barred.
3. Data and Privacy
- This project may collect operational data for optimization and analysis.
- All collected data is strictly anonymized to ensure the user's privacy.
- Transmission of information is encrypted to prevent unauthorized access.
4. Updates and Terms
- New releases may include critical patches, enhancements, or UI changes.
- Continued use of these services constitutes acceptance of updated terms.
- The author reserves the right to modify these license terms at any time.
5. Warranty and Liability
- All services and assets are provided "as is" without any form of warranty.
- 87owo is not liable for any direct or indirect damages from software use.
- Users assume all risks regarding performance, data, and system integrity.
© 2020-2026 87owo (PYAS Security). All rights reserved.</textarea>
</div>
</div>
</section>
<section id="taskmgr_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>進程管理</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋進程..." placeholder="搜尋進程...">
</div>
<button class="action-btn modern-btn primary-btn" data-i18n>查看</button>
<button class="action-btn modern-btn danger-btn" data-i18n>結束</button>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="whitelist_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>白名單管理</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋檔案..." placeholder="搜尋檔案...">
</div>
<button class="action-btn modern-btn primary-btn" data-i18n>加入</button>
<button class="action-btn modern-btn" data-i18n>移除</button>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="quarantine_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>隔離區管理</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋檔案..." placeholder="搜尋檔案...">
</div>
<button class="action-btn modern-btn primary-btn" data-i18n>加入</button>
<button class="action-btn modern-btn" data-i18n>移除</button>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="junk_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>垃圾清理</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋檔案..." placeholder="搜尋檔案...">
</div>
<button class="action-btn modern-btn primary-btn" data-i18n>掃描</button>
<button class="action-btn modern-btn danger-btn" data-i18n>刪除</button>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="repair_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>系統修復</h2>
<div class="spacer"></div>
<button class="action-btn modern-btn primary-btn" data-i18n>掃描</button>
<button class="action-btn modern-btn danger-btn" data-i18n>修復</button>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="popup_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>彈窗攔截</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋窗口..." placeholder="搜尋窗口...">
</div>
<button class="action-btn modern-btn primary-btn" data-i18n>加入</button>
<button class="action-btn modern-btn" data-i18n>移除</button>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="custom_protect_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="protect_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>自訂防護</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋規則..." placeholder="搜尋規則...">
</div>
<button class="action-btn modern-btn primary-btn" data-i18n>加入</button>
<button class="action-btn modern-btn" data-i18n>移除</button>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="log_export_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="home_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>日誌導出</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋日誌..." placeholder="搜尋日誌...">
</div>
<button class="action-btn modern-btn primary-btn" data-i18n>導出</button>
<button class="action-btn modern-btn danger-btn" data-i18n>刪除</button>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
</main>
</div>
</div>
</body>
</html>
+1861 -1725
View File
File diff suppressed because it is too large. Load diff
-130
View File
@@ -1,130 +0,0 @@
translate_dict = {
"traditional_switch": {"开启":"開啟","关闭":"關閉","On":"開啟","Off":"關閉"},
"simplified_switch": {"開啟":"开启","關閉":"关闭","On":"开启","Off":"关闭",
"錯誤":"错误","發現":"发现","關閉":"关闭","裝置":"设备","記憶體":"内存",
"軟體":"软件","檔案":"文件","確定":"确认","設定":"设置","關於":"关于",
"總數":"总数","註冊":"注册","圖標":"图标","路徑":"路径","網路":"网络",
"位元":"字节","靈敏":"敏感","複製":"复制","開啟":"开启","靜態":"静态",
"訊":"讯","項":"项","掃":"扫","試":"试","黃":"黄","嗎":"吗","防":"保",
"鑰":"钥","當":"当","後":"后","護":"护","導":"导","區":"区","復":"复",
"攔":"拦","請":"请","統":"统","刪":"删","進":"进","個":"个","選":"选",
"啟":"启","監":"监","並":"并","徑":"径","盤":"盘","隱":"隐","動":"动",
"狀":"状","誌":"志","這":"这","會":"会","題":"题","擇":"择","語":"语",
"無":"无","顯":"显","變":"变","戶":"户","碼":"码","雲":"云","介":"界",
"稱":"称","類":"类","輸":"输","執":"执","數":"数","尋":"寻","網":"网",
"詢":"询","製":"制","員":"员","許":"许","條":"条","藍":"蓝","紅":"红",
"綠":"绿","態":"态","務":"务","連":"连","檢":"检","測":"测","關":"关",
"強":"强","驅":"驱","為":"为","腳":"脚","單":"单","開":"开","擴":"扩",
"惡":"恶","據":"据","庫":"库","經":"经","敗":"败","謝":"谢","別":"别",
"載":"载","誤":"误","報":"报","鏈":"链","竄":"窜","蹤":"踪","與":"与",
"異":"异","實":"实","時":"时","彈":"弹","窗":"窗","暫":"暂","訂":"订",
"資":"资","產":"产","金":"密","離":"离","規":"规","則":"则","來":"来",
"殼":"壳","碼":"码","線":"线","餘":"余",},
"english_switch": {"開啟":"On","關閉":"Off","开启":"On","关闭":"Off",",":", ",
"立即刪除":"Delete Now","安全模式":"Safe Mode","官方網站":"Official Website ",
"腳本":"Script ","鏈式":"Chain ","記憶體":"Memory ","加載":"Load ",
"行為":"Behavior ","可疑":"Suspicious ","勒索":"Ransom ","主要":"Main ",
"許可條款":"License Terms ","惡意":"Malicious", "開發":"Developers ",
"啟用":"Enable ","禁用":"Disable ","正在初始化中":"Initializing ",
"正在掃描":"Scanning ","正在刪除":"Deleting ","正在追蹤":"Tracking ",
"最小化到":"Minimize To ","靈敏度":"Sensitivity ","正在檢查":"Checking ",
"確定要":"Sure Want To ","引擎靈敏度":"Sensitivity ","更新":"Update ",
"白色主題":"White","黃色主題":"Yellow","紅色主題":"Red","語言":"Language ",
"藍色主題":"Blue","綠色主題":"Green","自訂主題":"Custom","黑色主題":"Black",
"特別感謝":"Contributors ","版本資訊":"Information","通訊":"Connect ",
"註冊":"Registry ","退出":"Quit ","檔案":"File ","當前":"Currently ",
"狀態":"State ","掃描":"Scan ","工具":"Tools ","防護":"Protect ",
"失敗":"Failed ","請":"Please ","路徑":"Path ","方式":"Method ",
"需要":"Need ","病毒":"Virus ","選擇":"Select ","返回":"Back",
"受到":"Been ","錯誤":"Error","完成":"Complete ","引擎":"Engine ",
"托盤":"Tray ","圖標":"Icon ","不安全":"Unsafe","增強":"Enhance ",
"系統":"System ","雲端":"Cloud ","重啟":"Reboot ","修復":"Repair ",
"裝置":"Device ","發現":"Found ","成功":"Success ","清理":"Cleanup ",
"確定":"OK","執行":"Execute ","警告":"Warning ","使用":"Use ",
"網路":"Network ","變更":"Change ","總數":"Total","結束":"End ",
"選項":"Options ","監控":"Monitor ","連接":"Connect ","可以":"Can ",
"進程":"Process ","攔截":"Block ","引導":"Boot ","刪除":"Delete ",
"分區":"Partition ","設定":"Setting ","關於":"About","模式":"Mode ",
"提高":"Improve ","安全":"Security ","顯色":"Color ","顏色":"Color ",
"主題":"Theme ", "回收":"Collect ", "無用":"Useless ","導入":"Import ",
"風險":"Risk ","介面":"UI ","核心":"Core ","服務":"Services ",
"管理":"Manage","所有":"All ","您":"You ","取消":"Cancel ","未":"Not ",
"垃圾":"Junk ","位元":"byte ","本機":"Computer ","停止":"Stop ",
"到":"To ","白名單":"White List ","軟體":"Software ","正在":"",
"增加":"Add ","移除":"Delete ","項目":"Items ","生成":"Create ","稱":"",
"複製":"Copy ","此":"This ","並":"And ","全盤":"Full ","最新":"Latest ",
"已存在":"Already ","數據庫":"Database ","誤報":"False Positive ",
"高":"High ","新":"New ","和":"And ","可能會":"Possible ","會":"Can ",
"日誌":"Log","已":"Has ","彈窗":"Window ","要":"To ","驅動":"Driver ",
"的":"","顯示":"","個":"","嗎":"","了":"","行":"","立即":"","規則":"Rule ",
"密鑰":"Key ","檢測":"Detect ","製作":"Maker ","運行":"Running",
"擴展":"Extension ","套件":"Kit ","自動":"Auto ","上報":"Report ",
"分析":"Analysis ","自身":"Self ","其他":"Other ","配置":"Config ",
"丟失":"Lost ","網站":"Website ","不兼容":"Not Compatible ","列表":"List ",
"竄改":"Tamper ","第三方":"Other ","實時":"Real Time ","查看":"View ",
"指定":"Specify ","清空":"Empty ","資源回收桶":"Recycle Bin ", "無法":"Cannot ",
"與":"With ","當":"When ","功能":"Function ","異常":"Abnormal ", "來":"To ",
"重置":"Reset ","窗口":"Window ","暫存":"Temp ","自訂":"Custom ",
"不支持":"Not Support ","共":"Total ","秒":"Seconds ","耗時":"Takes ",
"沙箱":"Sandbox ", "虛擬":"Virtual ", "隔離":"Quarantine ", "時":"",
"建立":"Create ","環境":"Environment ","沙箱":"Sandbox ","化":"", "表":"",
"達":"Reached ", "上限":"Limit ", "區":"", "金鑰":"Key ", "產品":"Product ",
"運行庫":".NET CLR ","殼碼":"Shellcode ","線程":"Thread ","注入":"Inject ",
"前往":"Go ","檢查":"Check ","可用":"Available ","靜態":"Static ",
"版本":"Version ","是":"","剩餘":"Remain ","遠控":"Remote ","監控":"Monitor ",
" ":" "},
"white_switch": {
"_white": "_black",},
"red_switch": {
"235, 235, 235": "255, 235, 235",
"245, 245, 245": "255, 225, 225",
"255, 255, 255": "255, 245, 245",
"240, 240, 240": "255, 220, 220",
"250, 250, 250": "255, 210, 210",
"220, 255, 220": "255, 220, 220",
"210, 255, 210": "255, 210, 210",
"_white": "_black"
},
"yellow_switch": {
"235, 235, 235": "255, 255, 235",
"245, 245, 245": "255, 255, 225",
"255, 255, 255": "255, 255, 245",
"240, 240, 240": "255, 255, 220",
"250, 250, 250": "255, 255, 210",
"220, 255, 220": "255, 255, 220",
"210, 255, 210": "255, 255, 210",
"_white": "_black"
},
"green_switch": {
"235, 235, 235": "235, 255, 235",
"245, 245, 245": "225, 255, 225",
"255, 255, 255": "245, 255, 245",
"240, 240, 240": "220, 255, 220",
"250, 250, 250": "210, 255, 210",
"_white": "_black"
},
"blue_switch": {
"235, 235, 235": "235, 235, 255",
"245, 245, 245": "225, 225, 255",
"255, 255, 255": "245, 245, 255",
"240, 240, 240": "220, 220, 255",
"250, 250, 250": "210, 210, 255",
"220, 255, 220": "220, 220, 255",
"210, 255, 210": "210, 210, 255",
"_white": "_black"
},
"black_switch": {
"255, 255, 255": "20, 20, 20",
"245, 245, 245": "40, 40, 40",
"235, 235, 235": "30, 30, 30",
"240, 240, 240": "70, 70, 70",
"250, 250, 250": "80, 80, 80",
"220, 255, 220": "70, 70, 70",
"210, 255, 210": "80, 80, 80",
"0, 0, 0": "255, 255, 255",
"_black": "_white"
}
}
+802 -131
View File
File diff suppressed because it is too large. Load diff
-1576
View File
File diff suppressed because it is too large. Load diff
-14520
View File
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -2,9 +2,9 @@ import pyinstaller_versionfile
pyinstaller_versionfile.create_versionfile(
output_file="versionfile.txt",
version='3.3.7',
version='3.5.6.0',
company_name="PYAS Security",
file_description="Python Antivirus Software",
file_description="PYAS Security Antivirus",
internal_name="PYAS",
legal_copyright="PYAS Security",
original_filename="PYAS.exe",
+171
View File
@@ -0,0 +1,171 @@
#pragma once
#include <fltKernel.h>
#include <ntddk.h>
#include <ntdddisk.h>
#include <ntddscsi.h>
#include <ntstrsafe.h>
constexpr auto PYAS_PORT_NAME = L"\\PYAS_Output_Pipe";
constexpr auto MAX_PATH_LEN = 1024;
constexpr auto PYAS_POOL_TAG = 'SAYP';
#ifndef PROCESS_TERMINATE
#define PROCESS_TERMINATE (0x0001)
#endif
#ifndef PROCESS_CREATE_THREAD
#define PROCESS_CREATE_THREAD (0x0002)
#endif
#ifndef PROCESS_VM_OPERATION
#define PROCESS_VM_OPERATION (0x0008)
#endif
#ifndef PROCESS_VM_READ
#define PROCESS_VM_READ (0x0010)
#endif
#ifndef PROCESS_VM_WRITE
#define PROCESS_VM_WRITE (0x0020)
#endif
#ifndef PROCESS_DUP_HANDLE
#define PROCESS_DUP_HANDLE (0x0040)
#endif
#ifndef PROCESS_CREATE_PROCESS
#define PROCESS_CREATE_PROCESS (0x0080)
#endif
#ifndef PROCESS_SET_INFORMATION
#define PROCESS_SET_INFORMATION (0x0200)
#endif
#ifndef PROCESS_QUERY_INFORMATION
#define PROCESS_QUERY_INFORMATION (0x0400)
#endif
#ifndef PROCESS_SUSPEND_RESUME
#define PROCESS_SUSPEND_RESUME (0x0800)
#endif
#ifndef PROCESS_QUERY_LIMITED_INFORMATION
#define PROCESS_QUERY_LIMITED_INFORMATION (0x1000)
#endif
#ifndef PROCESS_SET_QUOTA
#define PROCESS_SET_QUOTA (0x0100)
#endif
#ifndef THREAD_TERMINATE
#define THREAD_TERMINATE (0x0001)
#endif
#ifndef THREAD_SUSPEND_RESUME
#define THREAD_SUSPEND_RESUME (0x0002)
#endif
#ifndef THREAD_SET_CONTEXT
#define THREAD_SET_CONTEXT (0x0010)
#endif
#ifndef THREAD_SET_INFORMATION
#define THREAD_SET_INFORMATION (0x0020)
#endif
#ifndef THREAD_SET_THREAD_TOKEN
#define THREAD_SET_THREAD_TOKEN (0x0080)
#endif
#ifndef POOL_FLAG_NON_PAGED
#define POOL_FLAG_NON_PAGED 0x0000000000000040UI64
#endif
#ifndef FSCTL_MANAGE_BYPASS_IO
#define FSCTL_MANAGE_BYPASS_IO CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 188, METHOD_NEITHER, FILE_ANY_ACCESS)
typedef enum _FS_BPIO_OPERATIONS {
FS_BPIO_OP_ENABLE = 1,
FS_BPIO_OP_DISABLE = 2,
FS_BPIO_OP_QUERY = 3,
FS_BPIO_OP_VOLUME_STACK_PAUSE = 4,
FS_BPIO_OP_VOLUME_STACK_RESUME = 5,
FS_BPIO_OP_STREAM_PAUSE = 6,
FS_BPIO_OP_STREAM_RESUME = 7,
FS_BPIO_OP_GET_INFO = 8
} FS_BPIO_OPERATIONS;
typedef struct _FS_BPIO_INPUT {
FS_BPIO_OPERATIONS Operation;
ULONG InFlags;
ULONGLONG Reserved1;
ULONGLONG Reserved2;
} FS_BPIO_INPUT, * PFS_BPIO_INPUT;
typedef struct _FS_BPIO_OUTPUT {
FS_BPIO_OPERATIONS Operation;
ULONG OutFlags;
ULONGLONG Reserved1;
ULONGLONG Reserved2;
NTSTATUS Status;
} FS_BPIO_OUTPUT, * PFS_BPIO_OUTPUT;
#endif
typedef struct _PYAS_MESSAGE {
ULONG MessageCode;
ULONG ProcessId;
WCHAR Path[MAX_PATH_LEN];
} PYAS_MESSAGE, * PPYAS_MESSAGE;
typedef struct _PYAS_USER_MESSAGE {
ULONG Command;
WCHAR Path[MAX_PATH_LEN];
} PYAS_USER_MESSAGE, * PPYAS_USER_MESSAGE;
typedef struct _DRIVER_DATA {
PDRIVER_OBJECT DriverObject;
PFLT_FILTER FilterHandle;
PFLT_PORT ServerPort;
PFLT_PORT ClientPort;
PEPROCESS PyasProcess;
ULONG PyasPid;
LARGE_INTEGER Cookie;
KSPIN_LOCK PortMutex;
KSPIN_LOCK TrackerMutex;
EX_RUNDOWN_REF PortRundown;
} DRIVER_DATA, * PDRIVER_DATA;
typedef struct _RULE_NODE {
struct _RULE_NODE* Next;
UNICODE_STRING Pattern;
} RULE_NODE, * PRULE_NODE;
extern DRIVER_DATA GlobalData;
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreCreate(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreWrite(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreSetInfo(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
FLT_PREOP_CALLBACK_STATUS ProtectFile_FileSystemControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
FLT_PREOP_CALLBACK_STATUS ProtectFile_SetSecurity(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
FLT_PREOP_CALLBACK_STATUS ProtectBoot_PreDeviceControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
NTSTATUS InitializeProcessProtection();
VOID UninitializeProcessProtection();
NTSTATUS InitializeRegistryProtection(PDRIVER_OBJECT DriverObject);
VOID UninitializeRegistryProtection();
NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath);
VOID UnloadRules();
VOID InitializeRulesEngine();
VOID UninitializeRulesEngine();
VOID AddDynamicWhitelist(PUNICODE_STRING RuleStr);
VOID RemoveDynamicWhitelist(PUNICODE_STRING RuleStr);
VOID ImageLoadNotify(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo);
NTSTATUS GetProcessImageName(HANDLE ProcessId, PUNICODE_STRING* ImageName);
BOOLEAN IsProcessTrusted(HANDLE ProcessId);
BOOLEAN IsTargetProtected(HANDLE ProcessId);
BOOLEAN WildcardMatch(PCWSTR Pattern, PCWSTR String, USHORT StringLengthBytes);
BOOLEAN CheckRegistryRule(PCUNICODE_STRING KeyName);
BOOLEAN CheckFileExtensionRule(PCUNICODE_STRING FileName);
BOOLEAN CheckProtectedPathRule(PCUNICODE_STRING FileName);
BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID WriteBuffer, ULONG WriteLength, BOOLEAN IsWrite);
NTSTATUS SendMessageToUser(ULONG Code, ULONG Pid, PWCHAR Path, USHORT PathSize);
FORCEINLINE PVOID PyasAllocate(SIZE_T Size) {
return ExAllocatePool2(POOL_FLAG_NON_PAGED, Size, PYAS_POOL_TAG);
}
FORCEINLINE VOID PyasFree(PVOID Ptr) {
if (Ptr) ExFreePoolWithTag(Ptr, PYAS_POOL_TAG);
}
-265
View File
@@ -1,265 +0,0 @@
#include <ntifs.h>
#include "DriverEntry.h"
#include "ProtectRules.h"
LARGE_INTEGER g_CmRegHandle;
PDEVICE_OBJECT g_ControlDeviceObject;
KSPIN_LOCK g_ProtectLock;
FAST_MUTEX HookMutex;
PDRIVER_OBJECT DiskDrvObj = NULL;
KEVENT g_LogDrainEvent;
BOOLEAN g_Unloading = FALSE;
BOOLEAN g_CmRegActive = FALSE;
EX_RUNDOWN_REF g_Rundown;
PDEVICE_OBJECT g_GuardDevice = NULL;
PDRIVER_OBJECT g_DriverObject = NULL;
PDEVICE_OBJECT g_MbrFilterTargets[MAX_MBR_TARGETS] = { 0 };
ULONG g_MbrFilterCount = 0;
volatile LONG g_LogWorkCount = 0;
static UNICODE_STRING g_DeviceName;
static WCHAR g_DeviceNameBuf[64];
static UNICODE_STRING g_SymLink = RTL_CONSTANT_STRING(L"\\??\\PYAS_Driver");
static BOOLEAN g_SymLinkCreated = FALSE;
static NTSTATUS CombinedDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
if (DeviceObject == g_ControlDeviceObject) {
PIO_STACK_LOCATION s = IoGetCurrentIrpStackLocation(Irp);
if (s->MajorFunction == IRP_MJ_CREATE || s->MajorFunction == IRP_MJ_CLOSE || s->MajorFunction == IRP_MJ_CLEANUP) {
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_DEVICE_REQUEST;
}
PIO_STACK_LOCATION s = IoGetCurrentIrpStackLocation(Irp);
if (s->MajorFunction == IRP_MJ_PNP) {
PDEVICE_OBJECT lower = (PDEVICE_OBJECT)DeviceObject->DeviceExtension;
if (s->MinorFunction == IRP_MN_REMOVE_DEVICE) {
NTSTATUS st;
IoSkipCurrentIrpStackLocation(Irp);
st = lower ? IoCallDriver(lower, Irp) : STATUS_SUCCESS;
if (lower) IoDetachDevice(lower);
IoDeleteDevice(DeviceObject);
return st;
}
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver((PDEVICE_OBJECT)DeviceObject->DeviceExtension, Irp);
}
return BootProtectDispatch(DeviceObject, Irp);
}
static NTSTATUS AttachDiskFilters(PDRIVER_OBJECT DriverObject)
{
UNICODE_STRING name = RTL_CONSTANT_STRING(L"\\Driver\\Disk");
UNICODE_STRING sym = RTL_CONSTANT_STRING(L"IoDriverObjectType");
POBJECT_TYPE* pType = (POBJECT_TYPE*)MmGetSystemRoutineAddress(&sym);
if (!pType || !*pType)
return STATUS_NOT_SUPPORTED;
NTSTATUS status = ObReferenceObjectByName(&name, OBJ_CASE_INSENSITIVE, NULL, 0, *pType, KernelMode, NULL, (PVOID*)&DiskDrvObj);
if (!NT_SUCCESS(status))
return status;
ULONG done = 0;
status = IoEnumerateDeviceObjectList(DiskDrvObj, NULL, 0, &done);
if (status != STATUS_BUFFER_TOO_SMALL && status != STATUS_SUCCESS) {
ObDereferenceObject(DiskDrvObj);
DiskDrvObj = NULL;
return status;
}
PDEVICE_OBJECT* list = ExAllocatePool2(POOL_FLAG_NON_PAGED, done * sizeof(PDEVICE_OBJECT), 'dskT');
if (!list) {
ObDereferenceObject(DiskDrvObj);
DiskDrvObj = NULL;
return STATUS_INSUFFICIENT_RESOURCES;
}
status = IoEnumerateDeviceObjectList(DiskDrvObj, list, done * sizeof(PDEVICE_OBJECT), &done);
if (!NT_SUCCESS(status)) {
ExFreePool2(list, 'dskT', NULL, 0);
ObDereferenceObject(DiskDrvObj);
DiskDrvObj = NULL;
return status;
}
for (ULONG i = 0; i < done && g_MbrFilterCount < MAX_MBR_TARGETS; ++i) {
PDEVICE_OBJECT lower = list[i], filter, attached = NULL;
status = IoCreateDevice(DriverObject, 0, NULL, lower->DeviceType, lower->Characteristics, FALSE, &filter);
if (!NT_SUCCESS(status))
continue;
filter->Flags |= (lower->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE));
status = IoAttachDeviceToDeviceStackSafe(filter, lower, &attached);
if (!NT_SUCCESS(status) || !attached) {
IoDeleteDevice(filter);
continue;
}
filter->StackSize = lower->StackSize + 1;
filter->AlignmentRequirement = lower->AlignmentRequirement;
filter->DeviceExtension = attached;
filter->Flags &= ~DO_DEVICE_INITIALIZING;
g_MbrFilterTargets[g_MbrFilterCount++] = filter;
}
for (ULONG i = 0; i < done; ++i)
ObDereferenceObject(list[i]);
ExFreePool2(list, 'dskT', NULL, 0);
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
UNREFERENCED_PARAMETER(RegistryPath);
g_DriverObject = DriverObject;
KeInitializeSpinLock(&g_ProtectLock);
ExInitializeFastMutex(&HookMutex);
KeInitializeEvent(&g_LogDrainEvent, NotificationEvent, TRUE);
g_Unloading = FALSE;
g_CmRegActive = FALSE;
InterlockedExchange(&g_LogWorkCount, 0);
ExInitializeRundownProtection(&g_Rundown);
{
UNICODE_STRING guardName = RTL_CONSTANT_STRING(L"\\Device\\PYAS_Driver_Guard");
NTSTATUS gs = IoCreateDevice(DriverObject, 0, &guardName, FILE_DEVICE_UNKNOWN, 0, FALSE, &g_GuardDevice);
if (!NT_SUCCESS(gs))
return STATUS_DEVICE_BUSY;
}
{
LARGE_INTEGER t = KeQueryPerformanceCounter(NULL);
g_DeviceName.Buffer = g_DeviceNameBuf;
g_DeviceName.MaximumLength = sizeof(g_DeviceNameBuf);
RtlStringCchPrintfW(g_DeviceName.Buffer, RTL_NUMBER_OF(g_DeviceNameBuf), L"\\Device\\PYAS_Driver_%p_%I64x", DriverObject, t.QuadPart);
g_DeviceName.Length = (USHORT)(wcslen(g_DeviceName.Buffer) * sizeof(WCHAR));
}
NTSTATUS status = IoCreateDevice(DriverObject, 0, &g_DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &g_ControlDeviceObject);
if (!NT_SUCCESS(status))
return status;
DriverObject->DriverUnload = DriverUnload;
for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; ++i)
DriverObject->MajorFunction[i] = CombinedDispatch;
DriverObject->Flags |= 0x20;
g_ControlDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
IoDeleteSymbolicLink(&g_SymLink);
{
UINT i = 0;
for (; i < 50; ++i) {
status = IoCreateSymbolicLink(&g_SymLink, &g_DeviceName);
if (NT_SUCCESS(status))
break;
LARGE_INTEGER d = { 0 };
d.QuadPart = -10LL * 1000LL * 10LL;
KeDelayExecutionThread(KernelMode, FALSE, &d);
IoDeleteSymbolicLink(&g_SymLink);
}
if (!NT_SUCCESS(status)) {
IoDeleteDevice(g_ControlDeviceObject);
g_ControlDeviceObject = NULL;
if (g_GuardDevice) { IoDeleteDevice(g_GuardDevice); g_GuardDevice = NULL; }
return status;
}
}
g_SymLinkCreated = TRUE;
{
static WCHAR regAltBuf[32];
static UNICODE_STRING regAlt;
LARGE_INTEGER t = KeQueryPerformanceCounter(NULL);
RtlStringCchPrintfW(regAltBuf, RTL_NUMBER_OF(regAltBuf), L"385100.%I64x", t.QuadPart);
RtlInitUnicodeString(&regAlt, regAltBuf);
status = CmRegisterCallbackEx(RegistryProtectCallback, &regAlt, DriverObject, NULL, &g_CmRegHandle, NULL);
if (NT_SUCCESS(status))
g_CmRegActive = TRUE;
}
InitImageProtect();
InitInjectProtect();
InitRemoteProtect();
InitScreenProtect();
InitProcessProtect();
InitFileProtect();
AttachDiskFilters(DriverObject);
return STATUS_SUCCESS;
}
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
g_Unloading = TRUE;
if (g_SymLinkCreated) {
IoDeleteSymbolicLink(&g_SymLink);
g_SymLinkCreated = FALSE;
}
if (g_CmRegActive) {
CmUnRegisterCallback(g_CmRegHandle);
g_CmRegActive = FALSE;
}
UninitFileProtect();
UninitInjectProtect();
UninitImageProtect();
UninitRemoteProtect();
UninitScreenProtect();
UninitProcessProtect();
ExWaitForRundownProtectionRelease(&g_Rundown);
if (InterlockedCompareExchange((volatile LONG*)&g_LogWorkCount, 0, 0) != 0) {
KeWaitForSingleObject(&g_LogDrainEvent, Executive, KernelMode, FALSE, NULL);
}
if (InterlockedCompareExchange(&g_LogWorkCount, 0, 0) != 0) {
KeResetEvent(&g_LogDrainEvent);
while (InterlockedCompareExchange(&g_LogWorkCount, 0, 0) != 0)
KeWaitForSingleObject(&g_LogDrainEvent, Executive, KernelMode, FALSE, NULL);
}
for (ULONG i = 0; i < g_MbrFilterCount; ++i) {
if (g_MbrFilterTargets[i]) {
IoDetachDevice((PDEVICE_OBJECT)g_MbrFilterTargets[i]->DeviceExtension);
IoDeleteDevice(g_MbrFilterTargets[i]);
g_MbrFilterTargets[i] = NULL;
}
}
g_MbrFilterCount = 0;
if (DiskDrvObj) {
ObDereferenceObject(DiskDrvObj);
DiskDrvObj = NULL;
}
if (g_ControlDeviceObject) {
IoDeleteDevice(g_ControlDeviceObject);
g_ControlDeviceObject = NULL;
}
if (g_GuardDevice) {
IoDeleteDevice(g_GuardDevice);
g_GuardDevice = NULL;
}
}
+220
View File
@@ -0,0 +1,220 @@
#include "DriverCommon.h"
DRIVER_DATA GlobalData;
static BOOLEAN g_ImageNotifyRegistered = FALSE;
static NTSTATUS InstanceSetup(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_SETUP_FLAGS Flags, DEVICE_TYPE VolumeDeviceType, FLT_FILESYSTEM_TYPE VolumeFilesystemType) {
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Flags);
UNREFERENCED_PARAMETER(VolumeDeviceType);
UNREFERENCED_PARAMETER(VolumeFilesystemType);
return STATUS_SUCCESS;
}
static NTSTATUS InstanceQueryTeardown(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags) {
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Flags);
return STATUS_SUCCESS;
}
static VOID InstanceTeardownStart(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_TEARDOWN_FLAGS Flags) {
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Flags);
}
static VOID InstanceTeardownComplete(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_TEARDOWN_FLAGS Flags) {
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Flags);
}
static NTSTATUS DriverUnload(FLT_FILTER_UNLOAD_FLAGS Flags) {
UNREFERENCED_PARAMETER(Flags);
if (GlobalData.ServerPort) {
FltCloseCommunicationPort(GlobalData.ServerPort);
GlobalData.ServerPort = NULL;
}
if (GlobalData.FilterHandle) {
FltUnregisterFilter(GlobalData.FilterHandle);
GlobalData.FilterHandle = NULL;
}
if (g_ImageNotifyRegistered) {
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
g_ImageNotifyRegistered = FALSE;
}
UninitializeRegistryProtection();
UninitializeProcessProtection();
UnloadRules();
UninitializeRulesEngine();
return STATUS_SUCCESS;
}
static NTSTATUS PortMessage(PVOID PortCookie, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength, PULONG ReturnOutputBufferLength) {
UNREFERENCED_PARAMETER(PortCookie);
UNREFERENCED_PARAMETER(OutputBuffer);
UNREFERENCED_PARAMETER(OutputBufferLength);
if (InputBuffer && InputBufferLength >= sizeof(PYAS_USER_MESSAGE)) {
PPYAS_USER_MESSAGE msg = (PPYAS_USER_MESSAGE)InputBuffer;
if (msg->Command == 1 || msg->Command == 2) {
msg->Path[MAX_PATH_LEN - 1] = L'\0';
UNICODE_STRING us;
RtlInitUnicodeString(&us, msg->Path);
if (us.Length > 0) {
if (msg->Command == 1) {
AddDynamicWhitelist(&us);
}
else {
RemoveDynamicWhitelist(&us);
}
}
}
}
if (ReturnOutputBufferLength) {
*ReturnOutputBufferLength = 0;
}
return STATUS_SUCCESS;
}
static NTSTATUS PortConnect(PFLT_PORT ClientPort, PVOID ServerPortCookie, PVOID ConnectionContext, ULONG SizeOfContext, PVOID* ConnectionPortCookie) {
UNREFERENCED_PARAMETER(ServerPortCookie);
UNREFERENCED_PARAMETER(ConnectionContext);
UNREFERENCED_PARAMETER(SizeOfContext);
UNREFERENCED_PARAMETER(ConnectionPortCookie);
KIRQL OldIrql;
KeAcquireSpinLock(&GlobalData.PortMutex, &OldIrql);
GlobalData.ClientPort = ClientPort;
GlobalData.PyasPid = (ULONG)(ULONG_PTR)PsGetCurrentProcessId();
GlobalData.PyasProcess = PsGetCurrentProcess();
ExReInitializeRundownProtection(&GlobalData.PortRundown);
KeReleaseSpinLock(&GlobalData.PortMutex, OldIrql);
return STATUS_SUCCESS;
}
static VOID PortDisconnect(PVOID ConnectionCookie) {
UNREFERENCED_PARAMETER(ConnectionCookie);
KIRQL OldIrql;
KeAcquireSpinLock(&GlobalData.PortMutex, &OldIrql);
GlobalData.ClientPort = NULL;
GlobalData.PyasPid = 0;
GlobalData.PyasProcess = NULL;
KeReleaseSpinLock(&GlobalData.PortMutex, OldIrql);
ExWaitForRundownProtectionRelease(&GlobalData.PortRundown);
}
CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
{ IRP_MJ_CREATE, 0, ProtectFile_PreCreate, NULL },
{ IRP_MJ_WRITE, 0, ProtectFile_PreWrite, NULL },
{ IRP_MJ_SET_INFORMATION, 0, ProtectFile_PreSetInfo, NULL },
{ IRP_MJ_SET_SECURITY, 0, ProtectFile_SetSecurity, NULL },
{ IRP_MJ_FILE_SYSTEM_CONTROL, 0, ProtectFile_FileSystemControl, NULL },
{ IRP_MJ_DEVICE_CONTROL, 0, ProtectBoot_PreDeviceControl, NULL },
{ IRP_MJ_OPERATION_END }
};
CONST FLT_REGISTRATION FilterRegistration = {
sizeof(FLT_REGISTRATION),
FLT_REGISTRATION_VERSION,
0,
NULL,
Callbacks,
DriverUnload,
InstanceSetup,
InstanceQueryTeardown,
InstanceTeardownStart,
InstanceTeardownComplete,
NULL,
NULL,
NULL
};
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
NTSTATUS status = STATUS_SUCCESS;
PSECURITY_DESCRIPTOR sd = NULL;
OBJECT_ATTRIBUTES oa = { 0 };
UNICODE_STRING name = { 0 };
RtlZeroMemory(&GlobalData, sizeof(GlobalData));
GlobalData.DriverObject = DriverObject;
KeInitializeSpinLock(&GlobalData.PortMutex);
KeInitializeSpinLock(&GlobalData.TrackerMutex);
ExInitializeRundownProtection(&GlobalData.PortRundown);
InitializeRulesEngine();
status = LoadRulesFromDisk(RegistryPath);
status = FltRegisterFilter(DriverObject, &FilterRegistration, &GlobalData.FilterHandle);
if (!NT_SUCCESS(status)) {
UnloadRules();
UninitializeRulesEngine();
return status;
}
status = FltBuildDefaultSecurityDescriptor(&sd, FLT_PORT_ALL_ACCESS);
if (NT_SUCCESS(status)) {
RtlInitUnicodeString(&name, PYAS_PORT_NAME);
InitializeObjectAttributes(&oa, &name, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, sd);
status = FltCreateCommunicationPort(GlobalData.FilterHandle, &GlobalData.ServerPort, &oa, NULL, PortConnect, PortDisconnect, PortMessage, 1);
FltFreeSecurityDescriptor(sd);
}
if (!NT_SUCCESS(status)) {
FltUnregisterFilter(GlobalData.FilterHandle);
GlobalData.FilterHandle = NULL;
UnloadRules();
UninitializeRulesEngine();
return status;
}
InitializeProcessProtection();
InitializeRegistryProtection(DriverObject);
NTSTATUS imageNotifyStatus = PsSetLoadImageNotifyRoutine(ImageLoadNotify);
if (NT_SUCCESS(imageNotifyStatus)) {
g_ImageNotifyRegistered = TRUE;
}
status = FltStartFiltering(GlobalData.FilterHandle);
if (!NT_SUCCESS(status)) {
if (g_ImageNotifyRegistered) {
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
g_ImageNotifyRegistered = FALSE;
}
UninitializeRegistryProtection();
UninitializeProcessProtection();
if (GlobalData.ServerPort) {
FltCloseCommunicationPort(GlobalData.ServerPort);
GlobalData.ServerPort = NULL;
}
if (GlobalData.FilterHandle) {
FltUnregisterFilter(GlobalData.FilterHandle);
GlobalData.FilterHandle = NULL;
}
UnloadRules();
UninitializeRulesEngine();
}
return status;
}
-187
View File
@@ -1,187 +0,0 @@
#pragma once
#include <ntifs.h>
#include <ntstrsafe.h>
#include <ntdddisk.h>
#include <ntddscsi.h>
#include <fltKernel.h>
#define PIPE_NAME L"\\??\\pipe\\PYAS_Output_Pipe"
#ifndef POOL_FLAG_NON_PAGED
#define POOL_FLAG_NON_PAGED 0x00000001
#endif
#ifndef PROCESS_TERMINATE
#define PROCESS_TERMINATE 0x0001
#endif
#ifndef PROCESS_CREATE_THREAD
#define PROCESS_CREATE_THREAD 0x0002
#endif
#ifndef PROCESS_VM_OPERATION
#define PROCESS_VM_OPERATION 0x0008
#endif
#ifndef PROCESS_VM_WRITE
#define PROCESS_VM_WRITE 0x0020
#endif
#ifndef PROCESS_VM_READ
#define PROCESS_VM_READ 0x0010
#endif
#ifndef PROCESS_DUP_HANDLE
#define PROCESS_DUP_HANDLE 0x0040
#endif
#ifndef PROCESS_CREATE_PROCESS
#define PROCESS_CREATE_PROCESS 0x0080
#endif
#ifndef PROCESS_SET_QUOTA
#define PROCESS_SET_QUota 0x0100
#endif
#ifndef PROCESS_SET_INFORMATION
#define PROCESS_SET_INFORMATION 0x0200
#endif
#ifndef PROCESS_SUSPEND_RESUME
#define PROCESS_SUSPEND_RESUME 0x0800
#endif
#ifndef PROCESS_QUERY_LIMITED_INFORMATION
#define PROCESS_QUERY_LIMITED_INFORMATION 0x1000
#endif
#ifndef PROCESS_SET_LIMITED_INFORMATION
#define PROCESS_SET_LIMITED_INFORMATION 0x2000
#endif
#ifndef THREAD_TERMINATE
#define THREAD_TERMINATE 0x0001
#endif
#ifndef THREAD_SUSPEND_RESUME
#define THREAD_SUSPEND_RESUME 0x0002
#endif
#ifndef THREAD_SET_CONTEXT
#define THREAD_SET_CONTEXT 0x0010
#endif
#ifndef THREAD_IMPERSONATE
#define THREAD_IMPERSONATE 0x0100
#endif
#ifndef THREAD_DIRECT_IMPERSONATION
#define THREAD_DIRECT_IMPERSONATION 0x0200
#endif
#ifndef JOB_OBJECT_ASSIGN_PROCESS
#define JOB_OBJECT_ASSIGN_PROCESS 0x0001
#endif
#ifndef JOB_OBJECT_TERMINATE
#define JOB_OBJECT_TERMINATE 0x0008
#endif
#ifndef FILE_DISPOSITION_DELETE
#define FILE_DISPOSITION_DELETE 0x00000001
#endif
#ifndef UINT
typedef unsigned int UINT;
#endif
#define MAX_MBR_TARGETS 64
extern POBJECT_TYPE* PsJobType;
extern volatile LONG g_LogWorkCount;
extern BOOLEAN g_Unloading;
extern KEVENT g_LogDrainEvent;
extern KSPIN_LOCK g_ProtectLock;
extern LARGE_INTEGER g_CmRegHandle;
extern POBJECT_TYPE* IoDriverObjectType;
extern POBJECT_TYPE* IoFileObjectType;
extern FAST_MUTEX HookMutex;
extern EX_RUNDOWN_REF g_Rundown;
extern PDEVICE_OBJECT g_GuardDevice;
extern PDRIVER_OBJECT g_DriverObject;
extern PFLT_FILTER g_FilterHandle;
NTSYSAPI PCSTR NTAPI PsGetProcessImageFileName(PEPROCESS Process);
typedef PVOID(NTAPI* _pyas_ExAllocatePool2)(ULONG, SIZE_T, ULONG);
typedef VOID(NTAPI* _pyas_ExFreePool2)(PVOID, ULONG, PVOID, ULONG);
typedef PVOID(NTAPI* _pyas_ExAllocatePoolWithTag)(POOL_TYPE, SIZE_T, ULONG);
typedef VOID(NTAPI* _pyas_ExFreePoolWithTag)(PVOID, ULONG);
static __forceinline PVOID PYAS_ExAllocatePool2(ULONG f, SIZE_T s, ULONG t) {
static _pyas_ExAllocatePool2 p2 = 0;
if (!p2) {
UNICODE_STRING n = RTL_CONSTANT_STRING(L"ExAllocatePool2");
p2 = (_pyas_ExAllocatePool2)MmGetSystemRoutineAddress(&n);
}
if (p2)
return p2(f, s, t);
static _pyas_ExAllocatePoolWithTag p1 = 0;
if (!p1) {
UNICODE_STRING n = RTL_CONSTANT_STRING(L"ExAllocatePoolWithTag");
p1 = (_pyas_ExAllocatePoolWithTag)MmGetSystemRoutineAddress(&n);
}
if (p1)
return p1((f & POOL_FLAG_NON_PAGED) ? NonPagedPoolNx : PagedPool, s, t);
return NULL;
}
static __forceinline VOID PYAS_ExFreePool2(PVOID a, ULONG t, PVOID h, ULONG f) {
UNREFERENCED_PARAMETER(h);
UNREFERENCED_PARAMETER(f);
static _pyas_ExFreePool2 p2 = 0;
if (!p2) {
UNICODE_STRING n = RTL_CONSTANT_STRING(L"ExFreePool2");
p2 = (_pyas_ExFreePool2)MmGetSystemRoutineAddress(&n);
}
if (p2) {
p2(a, t, h, f);
return;
}
static _pyas_ExFreePoolWithTag p1 = 0;
if (!p1) {
UNICODE_STRING n = RTL_CONSTANT_STRING(L"ExFreePoolWithTag");
p1 = (_pyas_ExFreePoolWithTag)MmGetSystemRoutineAddress(&n);
}
if (p1) {
p1(a, t);
return;
}
}
#define ExAllocatePool2 PYAS_ExAllocatePool2
#define ExFreePool2 PYAS_ExFreePool2
VOID DriverUnload(PDRIVER_OBJECT DriverObject);
VOID SendPipeLog(PCSTR msg, SIZE_T len);
VOID UninitImageProtect(VOID);
VOID UninitInjectProtect(VOID);
VOID UninitRemoteProtect(VOID);
VOID UninitScreenProtect(VOID);
VOID UninitProcessProtect(VOID);
VOID UninitFileProtect(VOID);
VOID LogAnsi3(PCSTR tag, ULONG upid, PUNICODE_STRING s1, PUNICODE_STRING s2);
NTSYSAPI NTSTATUS NTAPI ZwQueryInformationProcess(
_In_ HANDLE ProcessHandle,
_In_ PROCESSINFOCLASS ProcessInformationClass,
_Out_writes_bytes_opt_(ProcessInformationLength) PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength,
_Out_opt_ PULONG ReturnLength
);
NTSTATUS ObReferenceObjectByName(
IN PUNICODE_STRING ObjectName,
IN ULONG Attributes,
IN PACCESS_STATE PassedAccessState OPTIONAL,
IN ACCESS_MASK DesiredAccess OPTIONAL,
IN POBJECT_TYPE ObjectType,
IN KPROCESSOR_MODE AccessMode,
IN OUT PVOID ParseContext OPTIONAL,
OUT PVOID* Object
);
NTSTATUS BootProtectDispatch(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
);
NTSTATUS RegistryProtectCallback(PVOID ctx, PVOID arg1, PVOID arg2);
NTSTATUS InitImageProtect(VOID);
NTSTATUS InitInjectProtect(VOID);
NTSTATUS InitRemoteProtect(VOID);
NTSTATUS InitScreenProtect(VOID);
NTSTATUS InitProcessProtect(VOID);
NTSTATUS InitFileProtect(VOID);
BOOLEAN GetProcessImagePathByPid(HANDLE pid, PUNICODE_STRING ProcessImagePath);
-80
View File
@@ -1,80 +0,0 @@
#include <ntifs.h>
#include <ntstrsafe.h>
#include "DriverEntry.h"
extern PDEVICE_OBJECT g_ControlDeviceObject;
typedef struct _PIPE_LOG_CTX {
PIO_WORKITEM Item;
SIZE_T Len;
PCHAR Buf;
}PIPE_LOG_CTX, * PPIPE_LOG_CTX;
static VOID PipeLogWork(PDEVICE_OBJECT DeviceObject, PVOID Context)
{
UNREFERENCED_PARAMETER(DeviceObject);
PPIPE_LOG_CTX ctx = (PPIPE_LOG_CTX)Context;
HANDLE h = NULL;
UNICODE_STRING name;
OBJECT_ATTRIBUTES oa = { 0 };
IO_STATUS_BLOCK iosb;
RtlInitUnicodeString(&name, PIPE_NAME);
InitializeObjectAttributes(&oa, &name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
if (NT_SUCCESS(ZwCreateFile(&h, FILE_GENERIC_WRITE, &oa, &iosb, NULL, 0, 0, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0))) {
ZwWriteFile(h, NULL, NULL, NULL, &iosb, ctx->Buf, (ULONG)(ctx->Len > 0xFFFFFFFF ? 0xFFFFFFFF : ctx->Len), NULL, NULL);
ZwClose(h);
}
IoFreeWorkItem(ctx->Item);
ExFreePool2(ctx->Buf, 'golP', NULL, 0);
ExFreePool2(ctx, 'golP', NULL, 0);
if (InterlockedDecrement(&g_LogWorkCount) == 0) {
KeSetEvent(&g_LogDrainEvent, IO_NO_INCREMENT, FALSE);
}
}
VOID SendPipeLog(PCSTR msg, SIZE_T len)
{
if (!msg || len == 0)
return;
if (!g_ControlDeviceObject)
return;
if (g_Unloading)
return;
PPIPE_LOG_CTX ctx = (PPIPE_LOG_CTX)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(PIPE_LOG_CTX), 'golP');
if (!ctx)
return;
ctx->Len = len;
ctx->Buf = (PCHAR)ExAllocatePool2(POOL_FLAG_NON_PAGED, ctx->Len, 'golP');
if (!ctx->Buf) {
ExFreePool2(ctx, 'golP', NULL, 0);
return;
}
RtlCopyMemory(ctx->Buf, msg, ctx->Len);
ctx->Item = IoAllocateWorkItem(g_ControlDeviceObject);
if (!ctx->Item) {
ExFreePool2(ctx->Buf, 'golP', NULL, 0);
ExFreePool2(ctx, 'golP', NULL, 0);
return;
}
InterlockedIncrement(&g_LogWorkCount);
IoQueueWorkItem(ctx->Item, PipeLogWork, DelayedWorkQueue, ctx);
}
VOID LogAnsi3(PCSTR tag, ULONG upid, PUNICODE_STRING s1, PUNICODE_STRING s2)
{
ANSI_STRING a1 = { 0 }, a2 = { 0 };
CHAR buf[1024] = { 0 };
if (s1)
RtlUnicodeStringToAnsiString(&a1, s1, TRUE);
if (s2)
RtlUnicodeStringToAnsiString(&a2, s2, TRUE);
RtlStringCchPrintfA(buf, RTL_NUMBER_OF(buf), "%s | %u | %s | %s", tag, upid, a1.Buffer ? a1.Buffer : "", a2.Buffer ? a2.Buffer : "");
SendPipeLog(buf, strlen(buf));
RtlFreeAnsiString(&a1);
RtlFreeAnsiString(&a2);
}
+40 -15
View File
@@ -1,27 +1,52 @@
[Version]
Signature = "$WINDOWS NT$"
Class = System
ClassGuid = {4d36e97d-e325-11ce-bfc1-08002be10318}
Class = ActivityMonitor
ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2}
Provider = %ManufacturerName%
DriverVer = 04/26/2026,3.5.1.0
CatalogFile = PYAS_Driver.cat
PnpLockdown = 1
DriverVer =
[DestinationDirs]
DefaultDestDir = 13
[DefaultInstall.ntamd64]
CopyFiles = @PYAS_Driver.sys
AddService = PYAS_Driver,0x00000002
[SourceDisksFiles]
PYAS_Driver.sys = 1
DefaultDestDir = 12
PYAS_Driver.DriverFiles = 12
[SourceDisksNames]
1 = %DiskName%,,,""
[SourceDisksFiles]
PYAS_Driver.sys = 1,,
[DefaultInstall.NTamd64]
OptionDesc = %ServiceDescription%
CopyFiles = PYAS_Driver.DriverFiles
[DefaultInstall.NTamd64.Services]
AddService = %ServiceName%,,PYAS_Driver.Service
[PYAS_Driver.DriverFiles]
PYAS_Driver.sys
[PYAS_Driver.Service]
DisplayName = %ServiceName%
Description = %ServiceDescription%
ServiceBinary = %12%\PYAS_Driver.sys
Dependencies = FltMgr
ServiceType = 2
StartType = 3
ErrorControl = 1
LoadOrderGroup = "FSFilter Activity Monitor"
AddReg = PYAS_Driver.AddReg
[PYAS_Driver.AddReg]
HKR,"Parameters","DebugFlags",0x00010001,0x0
HKR,"Parameters","SupportedFeatures",0x00010001,0x3
HKR,"Parameters\Instances","DefaultInstance",0x00000000,"PYAS Instance"
HKR,"Parameters\Instances\PYAS Instance","Altitude",0x00000000,"320000"
HKR,"Parameters\Instances\PYAS Instance","Flags",0x00010001,0x0
[Strings]
ManufacturerName = "PYAS_Driver"
DiskName = "PYAS_Driver"
ServiceName="PYAS_Driver"
ServiceDisplayName="PYAS_Driver"
ManufacturerName = "PYAS Security"
DiskName = "PYAS_Driver Source Disk"
ServiceName = "PYAS_Driver"
ServiceDescription = "PYAS Security Driver"
+35
View File
@@ -0,0 +1,35 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36518.9 d17.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PYAS_Driver", "PYAS_Driver.vcxproj", "{699C79A8-A904-1976-F72A-CDD7E811CFD2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Debug|ARM64.ActiveCfg = Debug|ARM64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Debug|ARM64.Build.0 = Debug|ARM64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Debug|ARM64.Deploy.0 = Debug|ARM64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Debug|x64.ActiveCfg = Debug|x64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Debug|x64.Build.0 = Debug|x64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Debug|x64.Deploy.0 = Debug|x64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Release|ARM64.ActiveCfg = Release|ARM64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Release|ARM64.Build.0 = Release|ARM64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Release|ARM64.Deploy.0 = Release|ARM64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Release|x64.ActiveCfg = Release|x64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Release|x64.Build.0 = Release|x64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Release|x64.Deploy.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D166699A-50D5-470E-9559-39B8E842528D}
EndGlobalSection
EndGlobal
+128
View File
@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{699C79A8-A904-1976-F72A-CDD7E811CFD2}</ProjectGuid>
<TemplateGuid>{dd38f7fc-d7bd-488b-9242-7d8754cde80d}</TemplateGuid>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
<Configuration>Debug</Configuration>
<Platform Condition="'$(Platform)' == ''">x64</Platform>
<RootNamespace>PYAS_Driver</RootNamespace>
<WindowsTargetPlatformVersion>$(LatestTargetPlatformVersion)</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
<ConfigurationType>Driver</ConfigurationType>
<DriverType>WDM</DriverType>
<Driver_SpectreMitigation>false</Driver_SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
<ConfigurationType>Driver</ConfigurationType>
<DriverType>WDM</DriverType>
<Driver_SpectreMitigation>false</Driver_SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
<ConfigurationType>Driver</ConfigurationType>
<DriverType>WDM</DriverType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
<ConfigurationType>Driver</ConfigurationType>
<DriverType>WDM</DriverType>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
<EnableInf2cat>false</EnableInf2cat>
<Inf2CatUseLocalTime>true</Inf2CatUseLocalTime>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
<Inf2CatUseLocalTime>true</Inf2CatUseLocalTime>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<DriverSign>
<FileDigestAlgorithm>sha256</FileDigestAlgorithm>
</DriverSign>
<ClCompile>
<LanguageStandard>stdcpp20</LanguageStandard>
<TreatWarningAsError>false</TreatWarningAsError>
</ClCompile>
<Link>
<AdditionalDependencies>$(DDK_LIB_PATH)fltmgr.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DriverSign>
<FileDigestAlgorithm>sha256</FileDigestAlgorithm>
</DriverSign>
<ClCompile>
<TreatWarningAsError>false</TreatWarningAsError>
</ClCompile>
<Link>
<AdditionalDependencies>$(DDK_LIB_PATH)fltmgr.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<Inf Include="PYAS_Driver.inf" />
</ItemGroup>
<ItemGroup>
<FilesToPackage Include="$(TargetPath)" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="DriverCommon.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="DriverEntry.cpp" />
<ClCompile Include="ProtectBoot.cpp" />
<ClCompile Include="ProtectFile.cpp" />
<ClCompile Include="ProtectProcess.cpp" />
<ClCompile Include="ProtectRegistry.cpp" />
<ClCompile Include="ProtectRules.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Driver Files">
<UniqueIdentifier>{8E41214B-6785-4CFE-B992-037D68949A14}</UniqueIdentifier>
<Extensions>inf;inv;inx;mof;mc;</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<Inf Include="PYAS_Driver.inf">
<Filter>Driver Files</Filter>
</Inf>
</ItemGroup>
<ItemGroup>
<ClInclude Include="DriverCommon.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="ProtectRules.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ProtectFile.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ProtectBoot.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ProtectRegistry.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ProtectProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DriverEntry.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
-175
View File
@@ -1,175 +0,0 @@
#include <ntifs.h>
#include "DriverEntry.h"
#include "ProtectMatch.h"
#define PROTECTED_BOOT_AREA_SIZE (34 * 512)
static VOID GetExeNameForLog(HANDLE pid, PUNICODE_STRING exe)
{
if (!exe || !exe->Buffer || exe->MaximumLength == 0)
return;
exe->Length = 0;
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
if (GetProcessImagePathByPid(pid, exe))
return;
}
PEPROCESS eproc = PsGetCurrentProcess();
PCSTR n = PsGetProcessImageFileName(eproc);
size_t l = n ? strlen(n) : 0;
for (size_t i = 0; i < l && i < (exe->MaximumLength / sizeof(WCHAR)) - 1; i++)
exe->Buffer[i] = (WCHAR)n[i];
exe->Buffer[l] = 0;
exe->Length = (USHORT)(l * sizeof(WCHAR));
}
static BOOLEAN QueryDevName(PDEVICE_OBJECT dev, PUNICODE_STRING out)
{
if (KeGetCurrentIrql() > APC_LEVEL)
return FALSE;
if (!dev || !out || !out->Buffer || out->MaximumLength == 0)
return FALSE;
ULONG need = 0;
NTSTATUS s = ObQueryNameString(dev, NULL, 0, &need);
if (s != STATUS_INFO_LENGTH_MISMATCH || need == 0)
return FALSE;
POBJECT_NAME_INFORMATION oni = (POBJECT_NAME_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, need, 'nOmD');
if (!oni)
return FALSE;
BOOLEAN ok = FALSE;
s = ObQueryNameString(dev, oni, need, &need);
if (NT_SUCCESS(s) && oni->Name.Buffer && oni->Name.Length && oni->Name.Length < out->MaximumLength) {
RtlCopyMemory(out->Buffer, oni->Name.Buffer, oni->Name.Length);
out->Length = (USHORT)oni->Name.Length;
out->Buffer[out->Length / sizeof(WCHAR)] = 0;
ok = TRUE;
}
ExFreePool2(oni, 'nOmD', NULL, 0);
return ok;
}
static BOOLEAN IsDangerousDiskIo(PIO_STACK_LOCATION s)
{
if (!s) return FALSE;
if (s->MajorFunction == IRP_MJ_DEVICE_CONTROL || s->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) {
ULONG code = s->Parameters.DeviceIoControl.IoControlCode;
if (code == IOCTL_DISK_SET_DRIVE_LAYOUT || code == IOCTL_DISK_SET_DRIVE_LAYOUT_EX) return TRUE;
if (code == IOCTL_DISK_DELETE_DRIVE_LAYOUT) return TRUE;
if (code == IOCTL_DISK_FORMAT_TRACKS || code == IOCTL_DISK_FORMAT_TRACKS_EX) return TRUE;
if (code == IOCTL_DISK_SET_PARTITION_INFO || code == IOCTL_DISK_SET_PARTITION_INFO_EX) return TRUE;
if (code == IOCTL_SCSI_PASS_THROUGH || code == IOCTL_SCSI_PASS_THROUGH_DIRECT) return TRUE;
}
return FALSE;
}
static BOOLEAN IsBootSectorWrite(PIO_STACK_LOCATION s)
{
if (s->MajorFunction == IRP_MJ_WRITE) {
LARGE_INTEGER offset = s->Parameters.Write.ByteOffset;
if (offset.QuadPart < PROTECTED_BOOT_AREA_SIZE) {
return TRUE;
}
}
return FALSE;
}
NTSTATUS BootProtectDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
PDEVICE_OBJECT lower = (PDEVICE_OBJECT)DeviceObject->DeviceExtension;
if (!lower) {
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_DEVICE_REQUEST;
}
if (!ExAcquireRundownProtection(&g_Rundown)) {
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(lower, Irp);
}
if (g_Unloading) {
IoSkipCurrentIrpStackLocation(Irp);
NTSTATUS rs = IoCallDriver(lower, Irp);
ExReleaseRundownProtection(&g_Rundown);
return rs;
}
PIO_STACK_LOCATION s = IoGetCurrentIrpStackLocation(Irp);
if (s->MajorFunction == IRP_MJ_POWER) {
PoStartNextPowerIrp(Irp);
IoSkipCurrentIrpStackLocation(Irp);
NTSTATUS ps = PoCallDriver(lower, Irp);
ExReleaseRundownProtection(&g_Rundown);
return ps;
}
HANDLE pid = PsGetCurrentProcessId();
ULONG upid = (ULONG)(ULONG_PTR)pid;
if (upid == 0 || upid == 4) {
IoSkipCurrentIrpStackLocation(Irp);
NTSTATUS ns = IoCallDriver(lower, Irp);
ExReleaseRundownProtection(&g_Rundown);
return ns;
}
BOOLEAN blocked = FALSE;
WCHAR eb[512] = { 0 };
UNICODE_STRING exe = { 0 };
exe.Buffer = eb;
exe.MaximumLength = sizeof(eb);
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
GetExeNameForLog(pid, &exe);
if (IsWhitelist(&exe)) {
IoSkipCurrentIrpStackLocation(Irp);
NTSTATUS ns2 = IoCallDriver(lower, Irp);
ExReleaseRundownProtection(&g_Rundown);
return ns2;
}
}
if (lower->DeviceType == FILE_DEVICE_DISK) {
if (IsDangerousDiskIo(s)) {
blocked = TRUE;
}
if (!blocked && IsBootSectorWrite(s)) {
blocked = TRUE;
}
if (blocked) {
if (KeGetCurrentIrql() <= APC_LEVEL) {
WCHAR db[512] = { 0 };
UNICODE_STRING dev = { 0 };
dev.Buffer = db;
dev.MaximumLength = sizeof(db);
QueryDevName(lower, &dev);
LogAnsi3("BOOT_BLOCK", upid, &exe, dev.Length ? &dev : &exe);
}
}
}
if (blocked) {
Irp->IoStatus.Status = STATUS_ACCESS_DENIED;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
ExReleaseRundownProtection(&g_Rundown);
return STATUS_ACCESS_DENIED;
}
IoSkipCurrentIrpStackLocation(Irp);
NTSTATUS rs2 = IoCallDriver(lower, Irp);
ExReleaseRundownProtection(&g_Rundown);
return rs2;
}
+33
View File
@@ -0,0 +1,33 @@
#include "DriverCommon.h"
#ifndef IOCTL_DISK_FORMAT_TRACKS
#define IOCTL_DISK_FORMAT_TRACKS CTL_CODE(IOCTL_DISK_BASE, 0x0006, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#endif
#ifndef IOCTL_DISK_FORMAT_TRACKS_EX
#define IOCTL_DISK_FORMAT_TRACKS_EX CTL_CODE(IOCTL_DISK_BASE, 0x000b, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#endif
FLT_PREOP_CALLBACK_STATUS ProtectBoot_PreDeviceControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(CompletionContext);
if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK;
if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK;
ULONG IoControlCode = Data->Iopb->Parameters.DeviceIoControl.Common.IoControlCode;
if (IoControlCode == IOCTL_DISK_SET_DRIVE_LAYOUT_EX ||
IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT ||
IoControlCode == IOCTL_DISK_FORMAT_TRACKS ||
IoControlCode == IOCTL_DISK_FORMAT_TRACKS_EX) {
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
UNICODE_STRING MsgStr = RTL_CONSTANT_STRING(L"Disk_Wiper_Attempt");
SendMessageToUser(4001, (ULONG)(ULONG_PTR)Pid, MsgStr.Buffer, MsgStr.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
return FLT_PREOP_COMPLETE;
}
}
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
-322
View File
@@ -1,322 +0,0 @@
#include <ntifs.h>
#include <fltKernel.h>
#include "DriverEntry.h"
#include "ProtectMatch.h"
#pragma comment(lib, "FltMgr.lib")
PFLT_FILTER g_FilterHandle = NULL;
extern PDRIVER_OBJECT g_DriverObject;
EX_RUNDOWN_REF g_Rundown;
// 定義標誌以便在不同 IRQL 下安全操作
#define MAX_PATH_LEN 512
// 獲取進程名稱 (僅在確定需要攔截時調用,減少性能開銷)
static VOID GetExeNameSafe(HANDLE pid, PUNICODE_STRING exe)
{
if (!exe || !exe->Buffer || !exe->MaximumLength)
return;
exe->Length = 0;
// 嘗試獲取短名稱 (最安全)
PEPROCESS cur = PsGetCurrentProcess();
PCSTR n = NULL;
if (cur) n = PsGetProcessImageFileName(cur);
// 如果在 PASSIVE_LEVEL,嘗試獲取完整路徑 (用於精確白名單)
// 注意:這裡依然有風險,但僅在攔截路徑執行
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
if (GetProcessImagePathByPid(pid, exe)) {
return;
}
}
// 降級方案:使用短名稱
size_t l = n ? strlen(n) : 0;
for (size_t i = 0; i < l && i < (exe->MaximumLength / sizeof(WCHAR)) - 1; i++)
exe->Buffer[i] = (WCHAR)n[i];
exe->Buffer[l] = 0;
exe->Length = (USHORT)(l * sizeof(WCHAR));
}
static BOOLEAN PreOpCheck(VOID)
{
if (g_Unloading)
return FALSE;
if (!ExAcquireRundownProtection(&g_Rundown))
return FALSE;
return TRUE;
}
static VOID PostOpRelease(VOID)
{
ExReleaseRundownProtection(&g_Rundown);
}
static BOOLEAN IsDeleteOrRename(PFLT_CALLBACK_DATA Data)
{
FILE_INFORMATION_CLASS infoClass = Data->Iopb->Parameters.SetFileInformation.FileInformationClass;
if (infoClass == FileDispositionInformation) {
PFILE_DISPOSITION_INFORMATION di = (PFILE_DISPOSITION_INFORMATION)Data->Iopb->Parameters.SetFileInformation.InfoBuffer;
return di && di->DeleteFile;
}
if (infoClass == FileDispositionInformationEx) {
PFILE_DISPOSITION_INFORMATION_EX dx = (PFILE_DISPOSITION_INFORMATION_EX)Data->Iopb->Parameters.SetFileInformation.InfoBuffer;
return dx && (dx->Flags & FILE_DISPOSITION_DELETE);
}
if (infoClass == FileRenameInformation || infoClass == FileRenameInformationEx)
return TRUE;
return FALSE;
}
static BOOLEAN IsWriteAccess(PFLT_CALLBACK_DATA Data)
{
// 如果是寫入操作,直接返回
if (Data->Iopb->MajorFunction == IRP_MJ_WRITE)
return TRUE;
// 如果是設置信息 (已經通過 IsDeleteOrRename 過濾),視為寫入(修改元數據)
if (Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION)
return TRUE;
// 如果是創建/打開文件,檢查權限
if (Data->Iopb->MajorFunction == IRP_MJ_CREATE) {
ULONG access = Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess;
// 檢查是否包含寫入數據、追加數據、刪除、修改屬性等權限
if (access & (FILE_WRITE_DATA | FILE_APPEND_DATA | DELETE |
WRITE_DAC | WRITE_OWNER | GENERIC_WRITE | GENERIC_ALL)) {
return TRUE;
}
}
return FALSE;
}
static FLT_PREOP_CALLBACK_STATUS FilePreOp(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_Flt_CompletionContext_Outptr_ PVOID* CompletionContext
)
{
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(CompletionContext);
// 1. 基礎檢查 (Paging IO 不攔截)
if (FlagOn(Data->Iopb->IrpFlags, IRP_PAGING_IO)) {
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
if (!PreOpCheck())
return FLT_PREOP_SUCCESS_NO_CALLBACK;
// 2. 獲取 PID
HANDLE pid = PsGetCurrentProcessId();
ULONG upid = (ULONG)(ULONG_PTR)pid;
// 忽略 System (4) 和 Idle (0) 進程,避免系統崩潰
if (upid == 0 || upid == 4) {
PostOpRelease();
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
// 3. 檢查操作類型 (僅關心 Create, Write, SetInfo)
BOOLEAN checkFile = (Data->Iopb->MajorFunction == IRP_MJ_CREATE || Data->Iopb->MajorFunction == IRP_MJ_WRITE);
BOOLEAN checkSetInfo = (Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION);
if (!checkFile && !checkSetInfo) {
PostOpRelease();
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
if (checkSetInfo && !IsDeleteOrRename(Data)) {
PostOpRelease();
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
// 4. 獲取文件名 (這一步最關鍵)
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
// 使用 QUERY_DEFAULT | NORMALIZED 確保獲取標準化路徑
NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
if (!NT_SUCCESS(status)) {
PostOpRelease();
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
status = FltParseFileNameInformation(nameInfo);
if (!NT_SUCCESS(status)) {
FltReleaseFileNameInformation(nameInfo);
PostOpRelease();
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
// 5. 邏輯判斷 (優化順序:文件名白名單 -> 黑名單 -> 操作檢查 -> 進程白名單)
// A. 全局文件白名單 (例如 Windows 目錄下的某些操作)
if (IsWhitelist(&nameInfo->Name)) {
FltReleaseFileNameInformation(nameInfo);
PostOpRelease();
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
// B. 黑名單匹配
BOOLEAN hitFile = MatchBlockFile(&nameInfo->Name);
// 勒索防護:必須在敏感目錄 且 有敏感後綴 (如 .txt, .doc)
// 註:Updater.bat 產生 .txt,應命中這裡
BOOLEAN hitRansom = MatchBlockRansom(&nameInfo->Name) && HasBlockedSuffix(&nameInfo->Name);
// 如果沒有命中任何規則,直接放行,節省資源
if (!hitFile && !hitRansom) {
FltReleaseFileNameInformation(nameInfo);
PostOpRelease();
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
// C. 檢查是否為寫入行為 (讀取不攔截)
if (!IsWriteAccess(Data)) {
FltReleaseFileNameInformation(nameInfo);
PostOpRelease();
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
// D. 到了這裡,說明命中規則且正在寫入,準備攔截
// 這時候才去獲取進程名稱,檢查進程白名單
WCHAR exe_buffer[MAX_PATH_LEN] = { 0 };
UNICODE_STRING exe = { 0 };
exe.Buffer = exe_buffer;
exe.MaximumLength = sizeof(exe_buffer);
GetExeNameSafe(pid, &exe); // 獲取進程名
if (IsWhitelistExcept(&exe)) {
// 進程在白名單中 (如 ntoskrnl, explorer 等)
FltReleaseFileNameInformation(nameInfo);
PostOpRelease();
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
// E. 執行攔截
if (hitRansom) {
LogAnsi3("RANSOM_BLOCK", upid, &exe, &nameInfo->Name);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
Data->IoStatus.Information = 0;
FltReleaseFileNameInformation(nameInfo);
PostOpRelease();
return FLT_PREOP_COMPLETE;
}
if (hitFile) {
LogAnsi3("FILE_BLOCK", upid, &exe, &nameInfo->Name);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
Data->IoStatus.Information = 0;
FltReleaseFileNameInformation(nameInfo);
PostOpRelease();
return FLT_PREOP_COMPLETE;
}
// 默認放行
FltReleaseFileNameInformation(nameInfo);
PostOpRelease();
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
static NTSTATUS FilterUnloadCallback(FLT_FILTER_UNLOAD_FLAGS Flags)
{
UNREFERENCED_PARAMETER(Flags);
g_Unloading = TRUE;
ExWaitForRundownProtectionRelease(&g_Rundown);
if (g_FilterHandle != NULL) {
FltUnregisterFilter(g_FilterHandle);
g_FilterHandle = NULL;
}
return STATUS_SUCCESS;
}
static NTSTATUS FilterInstanceSetup(
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_ FLT_INSTANCE_SETUP_FLAGS Flags,
_In_ DEVICE_TYPE VolumeDeviceType,
_In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType
)
{
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Flags);
UNREFERENCED_PARAMETER(VolumeDeviceType);
UNREFERENCED_PARAMETER(VolumeFilesystemType);
// 自動附加到所有卷
return STATUS_SUCCESS;
}
static VOID FilterInstanceTeardownStart(
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags
)
{
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Flags);
}
static VOID FilterInstanceTeardownComplete(
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags
)
{
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Flags);
}
const FLT_OPERATION_REGISTRATION Callbacks[] = {
{ IRP_MJ_CREATE, 0, FilePreOp, NULL },
{ IRP_MJ_WRITE, 0, FilePreOp, NULL },
{ IRP_MJ_SET_INFORMATION, 0, FilePreOp, NULL },
{ IRP_MJ_OPERATION_END }
};
const FLT_REGISTRATION FilterRegistration = {
sizeof(FLT_REGISTRATION),
FLT_REGISTRATION_VERSION,
0,
NULL,
Callbacks,
FilterUnloadCallback,
FilterInstanceSetup,
NULL,
FilterInstanceTeardownStart,
FilterInstanceTeardownComplete,
NULL,
NULL,
NULL,
NULL
};
NTSTATUS InitFileProtect(VOID)
{
ExInitializeRundownProtection(&g_Rundown);
if (!g_DriverObject)
return STATUS_UNSUCCESSFUL;
NTSTATUS status = FltRegisterFilter(g_DriverObject, &FilterRegistration, &g_FilterHandle);
if (!NT_SUCCESS(status))
return status;
status = FltStartFiltering(g_FilterHandle);
if (!NT_SUCCESS(status)) {
FltUnregisterFilter(g_FilterHandle);
g_FilterHandle = NULL;
}
return status;
}
VOID UninitFileProtect(VOID)
{
g_Unloading = TRUE;
ExWaitForRundownProtectionRelease(&g_Rundown);
if (g_FilterHandle != NULL) {
FltUnregisterFilter(g_FilterHandle);
g_FilterHandle = NULL;
}
}
+292
View File
@@ -0,0 +1,292 @@
#include "DriverCommon.h"
static BOOLEAN IsHoneyToken(PUNICODE_STRING Path) {
if (!Path || !Path->Buffer) return FALSE;
if (WildcardMatch(L"*PYAS_Honey*", Path->Buffer, Path->Length) ||
WildcardMatch(L"*Backup_Secret*", Path->Buffer, Path->Length)) {
return TRUE;
}
return FALSE;
}
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreCreate(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(CompletionContext);
if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK;
if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK;
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
UNICODE_STRING FileName = { 0 };
NTSTATUS status = STATUS_SUCCESS;
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
if (!NT_SUCCESS(status) || !nameInfo) {
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
status = FltParseFileNameInformation(nameInfo);
if (!NT_SUCCESS(status)) {
FltReleaseFileNameInformation(nameInfo);
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
FileName = nameInfo->Name;
if (!FileName.Buffer || FileName.Length == 0) {
FltReleaseFileNameInformation(nameInfo);
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
HANDLE Pid = PsGetCurrentProcessId();
BOOLEAN Trusted = IsProcessTrusted(Pid);
ULONG CreateDisposition = (Data->Iopb->Parameters.Create.Options >> 24) & 0xFF;
BOOLEAN IsCreateAction = (CreateDisposition == FILE_CREATE ||
CreateDisposition == FILE_SUPERSEDE ||
CreateDisposition == FILE_OVERWRITE ||
CreateDisposition == FILE_OVERWRITE_IF ||
CreateDisposition == FILE_OPEN_IF);
if (IsHoneyToken(&FileName)) {
if (!Trusted) {
if ((Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess & (FILE_WRITE_DATA | DELETE)) || IsCreateAction) {
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, FileName.Buffer, FileName.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
callbackStatus = FLT_PREOP_COMPLETE;
goto cleanup;
}
}
}
if (WildcardMatch(L"\\Device\\PhysicalDrive*", FileName.Buffer, FileName.Length) ||
(WildcardMatch(L"\\Device\\Harddisk*", FileName.Buffer, FileName.Length) && !WildcardMatch(L"*Volume*", FileName.Buffer, FileName.Length))) {
if (Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess &
(FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE | WRITE_DAC | WRITE_OWNER)) {
if (!Trusted) {
UNICODE_STRING MsgStr = RTL_CONSTANT_STRING(L"Disk_Wiper_Attempt");
SendMessageToUser(4001, (ULONG)(ULONG_PTR)Pid, MsgStr.Buffer, MsgStr.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
callbackStatus = FLT_PREOP_COMPLETE;
goto cleanup;
}
}
}
if (CheckProtectedPathRule(&FileName)) {
if (!Trusted) {
if ((Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess &
(FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | DELETE | WRITE_DAC | GENERIC_WRITE)) || IsCreateAction) {
SendMessageToUser(2001, (ULONG)(ULONG_PTR)Pid, FileName.Buffer, FileName.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
callbackStatus = FLT_PREOP_COMPLETE;
goto cleanup;
}
}
}
if (CheckFileExtensionRule(&FileName)) {
if (!Trusted) {
if (Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess & (DELETE | FILE_DELETE_CHILD)) {
if (CheckRansomActivity(Pid, &FileName, NULL, 0, FALSE)) {
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, FileName.Buffer, FileName.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
callbackStatus = FLT_PREOP_COMPLETE;
goto cleanup;
}
}
}
}
cleanup:
if (nameInfo) {
FltReleaseFileNameInformation(nameInfo);
}
return callbackStatus;
}
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreSetInfo(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(CompletionContext);
if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK;
if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK;
FILE_INFORMATION_CLASS infoClass = Data->Iopb->Parameters.SetFileInformation.FileInformationClass;
if (infoClass != FileDispositionInformation &&
infoClass != FileRenameInformation &&
infoClass != FileRenameInformationEx) {
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
if (!NT_SUCCESS(status) || !nameInfo) goto cleanup;
status = FltParseFileNameInformation(nameInfo);
if (!NT_SUCCESS(status) || !nameInfo->Name.Buffer) goto cleanup;
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
if (CheckProtectedPathRule(&nameInfo->Name)) {
SendMessageToUser(2001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
callbackStatus = FLT_PREOP_COMPLETE;
goto cleanup;
}
if (CheckFileExtensionRule(&nameInfo->Name)) {
if (CheckRansomActivity(Pid, &nameInfo->Name, NULL, 0, FALSE)) {
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
callbackStatus = FLT_PREOP_COMPLETE;
goto cleanup;
}
}
}
cleanup:
if (nameInfo) {
FltReleaseFileNameInformation(nameInfo);
}
return callbackStatus;
}
FLT_PREOP_CALLBACK_STATUS ProtectFile_SetSecurity(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(CompletionContext);
if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK;
if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK;
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
if (!NT_SUCCESS(status) || !nameInfo) goto cleanup;
status = FltParseFileNameInformation(nameInfo);
if (!NT_SUCCESS(status) || !nameInfo->Name.Buffer) goto cleanup;
SECURITY_INFORMATION SecurityInfo = Data->Iopb->Parameters.SetSecurity.SecurityInformation;
if (SecurityInfo & (OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION)) {
if (CheckProtectedPathRule(&nameInfo->Name)) {
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
SendMessageToUser(2001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
callbackStatus = FLT_PREOP_COMPLETE;
goto cleanup;
}
}
}
cleanup:
if (nameInfo) {
FltReleaseFileNameInformation(nameInfo);
}
return callbackStatus;
}
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreWrite(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
UNREFERENCED_PARAMETER(CompletionContext);
UNREFERENCED_PARAMETER(FltObjects);
if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK;
if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK;
if (Data->Iopb->IrpFlags & (IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO | IRP_NOCACHE)) {
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
ULONG WriteLength = Data->Iopb->Parameters.Write.Length;
if (WriteLength == 0) return FLT_PREOP_SUCCESS_NO_CALLBACK;
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
if (!NT_SUCCESS(status) || !nameInfo) goto cleanup;
status = FltParseFileNameInformation(nameInfo);
if (!NT_SUCCESS(status) || !nameInfo->Name.Buffer) goto cleanup;
if (CheckFileExtensionRule(&nameInfo->Name)) {
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
PVOID SystemBuffer = NULL;
PMDL Mdl = Data->Iopb->Parameters.Write.MdlAddress;
if (!Mdl) {
if (Data->Iopb->Parameters.Write.WriteBuffer && KeGetCurrentIrql() <= APC_LEVEL) {
if (NT_SUCCESS(FltLockUserBuffer(Data))) {
Mdl = Data->Iopb->Parameters.Write.MdlAddress;
}
}
}
if (Mdl) {
SystemBuffer = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority | MdlMappingNoExecute);
}
if (CheckRansomActivity(Pid, &nameInfo->Name, SystemBuffer, WriteLength, TRUE)) {
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
callbackStatus = FLT_PREOP_COMPLETE;
}
}
}
cleanup:
if (nameInfo) {
FltReleaseFileNameInformation(nameInfo);
}
return callbackStatus;
}
FLT_PREOP_CALLBACK_STATUS ProtectFile_FileSystemControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(CompletionContext);
if (Data->Iopb->Parameters.FileSystemControl.Common.FsControlCode != FSCTL_MANAGE_BYPASS_IO) {
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
PFS_BPIO_INPUT InputBuffer = (PFS_BPIO_INPUT)Data->Iopb->Parameters.FileSystemControl.Neither.InputBuffer;
if (!InputBuffer) return FLT_PREOP_SUCCESS_NO_CALLBACK;
FS_BPIO_OPERATIONS Operation;
__try {
Operation = InputBuffer->Operation;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
if (Operation == FS_BPIO_OP_ENABLE) {
if (NT_SUCCESS(FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo)) && nameInfo) {
if (NT_SUCCESS(FltParseFileNameInformation(nameInfo))) {
if (CheckFileExtensionRule(&nameInfo->Name) || CheckProtectedPathRule(&nameInfo->Name)) {
Data->IoStatus.Status = STATUS_NOT_SUPPORTED;
callbackStatus = FLT_PREOP_COMPLETE;
}
}
}
}
if (nameInfo) {
FltReleaseFileNameInformation(nameInfo);
}
return callbackStatus;
}
-124
View File
@@ -1,124 +0,0 @@
#include <ntifs.h>
#include "DriverEntry.h"
#include "ProtectMatch.h"
static BOOLEAN g_ImageNotifyEnabled = FALSE;
static BOOLEAN CheckVadExec(PVOID a)
{
MEMORY_BASIC_INFORMATION mbi = { 0 };
SIZE_T ret = 0;
NTSTATUS s = ZwQueryVirtualMemory(NtCurrentProcess(), a, MemoryBasicInformation, &mbi, sizeof(mbi), &ret);
if (!NT_SUCCESS(s))
return FALSE;
if (mbi.State != MEM_COMMIT)
return FALSE;
if (mbi.Type == SEC_IMAGE)
return FALSE;
if (mbi.Type != MEM_PRIVATE && mbi.Type != MEM_MAPPED)
return FALSE;
return (mbi.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)) != 0;
}
static BOOLEAN WalkForShellcode(ULONG h)
{
#define MAXWALK 20
PVOID frames[MAXWALK] = { 0 };
ULONG c = RtlWalkFrameChain(frames, MAXWALK, 1);
if (c == 0)
return FALSE;
ULONG limit = RTL_NUMBER_OF(frames);
ULONG count = c < limit ? c : limit;
ULONG d = h < count ? h : count;
for (ULONG i = 0; i < d; i++) {
PVOID p = frames[i];
if (p && CheckVadExec(p))
return TRUE;
}
for (ULONG i = 0; i < d; i++) {
ULONG idx = count - 1 - i;
if (idx >= d) {
PVOID p = frames[idx];
if (p && CheckVadExec(p))
return TRUE;
}
}
return FALSE;
}
static VOID ImageLoadNotify(IN PUNICODE_STRING FullImageName, IN HANDLE ProcessId, IN PIMAGE_INFO ImageInfo)
{
UNREFERENCED_PARAMETER(ImageInfo);
if (g_Unloading)
return;
if (ProcessId == (HANDLE)0 || ProcessId == (HANDLE)4)
return;
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
return;
if (!ExAcquireRundownProtection(&g_Rundown))
return;
WCHAR eb[512] = { 0 };
UNICODE_STRING exe = { 0 };
exe.Buffer = eb;
exe.MaximumLength = sizeof(eb);
exe.Length = 0;
if (!GetProcessImagePathByPid(ProcessId, &exe)) {
PEPROCESS eproc = PsGetCurrentProcess();
PCSTR n = PsGetProcessImageFileName(eproc);
size_t l = n ? strlen(n) : 0;
for (size_t i = 0; i < l && i < RTL_NUMBER_OF(eb) - 1; i++)
eb[i] = (WCHAR)n[i];
eb[l] = 0;
exe.Length = (USHORT)(l * sizeof(WCHAR));
}
if (exe.Length & 1) exe.Length--;
{
size_t elen = exe.Length / sizeof(WCHAR);
if ((elen >= 14 && _wcsnicmp(exe.Buffer + elen - 14, L"powershell.exe", 14) == 0) ||
(elen >= 8 && _wcsnicmp(exe.Buffer + elen - 8, L"pwsh.exe", 8) == 0) ||
(elen >= 10 && _wcsnicmp(exe.Buffer + elen - 10, L"sppsvc.exe", 10) == 0)) {
ExReleaseRundownProtection(&g_Rundown);
return;
}
}
if (IsWhitelist(&exe)) {
ExReleaseRundownProtection(&g_Rundown);
return;
}
if (FullImageName && FullImageName->Buffer) {
if (MatchClrFromNonFramework(FullImageName)) {
LogAnsi3("CLR_BLOCK", (ULONG)(ULONG_PTR)ProcessId, &exe, FullImageName);
ExReleaseRundownProtection(&g_Rundown);
return;
}
}
if (WalkForShellcode(8)) {
UNICODE_STRING none = RTL_CONSTANT_STRING(L"None");
LogAnsi3("SHELLCODE_BLOCK", (ULONG)(ULONG_PTR)ProcessId, &exe, &none);
ExReleaseRundownProtection(&g_Rundown);
return;
}
ExReleaseRundownProtection(&g_Rundown);
}
NTSTATUS InitImageProtect(VOID)
{
NTSTATUS s = PsSetLoadImageNotifyRoutine(ImageLoadNotify);
if (NT_SUCCESS(s))
g_ImageNotifyEnabled = TRUE;
return s;
}
VOID UninitImageProtect(VOID)
{
if (g_ImageNotifyEnabled) {
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
g_ImageNotifyEnabled = FALSE;
}
}
-177
View File
@@ -1,177 +0,0 @@
#include <ntifs.h>
#include "DriverEntry.h"
#include "ProtectMatch.h"
static PVOID g_ObRegHandle = NULL;
static POB_OPERATION_REGISTRATION g_ObOps = NULL;
static OB_CALLBACK_REGISTRATION g_ObReg = { 0 };
static OB_PREOP_CALLBACK_STATUS PreProcessCallback(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION Info)
{
UNREFERENCED_PARAMETER(RegistrationContext);
if (g_Unloading)
return OB_PREOP_SUCCESS;
if (!Info || Info->KernelHandle)
return OB_PREOP_SUCCESS;
if (!Info->Object)
return OB_PREOP_SUCCESS;
if (Info->Operation != OB_OPERATION_HANDLE_CREATE && Info->Operation != OB_OPERATION_HANDLE_DUPLICATE)
return OB_PREOP_SUCCESS;
if (!ExAcquireRundownProtection(&g_Rundown))
return OB_PREOP_SUCCESS;
HANDLE cur = PsGetCurrentProcessId();
if (cur == (HANDLE)0 || cur == (HANDLE)4) {
ExReleaseRundownProtection(&g_Rundown);
return OB_PREOP_SUCCESS;
}
HANDLE target = PsGetProcessId((PEPROCESS)Info->Object);
if (target == cur) {
ExReleaseRundownProtection(&g_Rundown);
return OB_PREOP_SUCCESS;
}
ACCESS_MASK* desired = (Info->Operation == OB_OPERATION_HANDLE_CREATE) ? &Info->Parameters->CreateHandleInformation.DesiredAccess : &Info->Parameters->DuplicateHandleInformation.DesiredAccess;
ACCESS_MASK mask = PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE;
PCSTR tn = PsGetProcessImageFileName((PEPROCESS)Info->Object);
if (tn && (_stricmp(tn, "lsass.exe") == 0))
mask |= PROCESS_VM_READ;
if ((*desired & mask) == 0) {
ExReleaseRundownProtection(&g_Rundown);
return OB_PREOP_SUCCESS;
}
BOOLEAN logok = (KeGetCurrentIrql() == PASSIVE_LEVEL);
WCHAR cbuf[512] = { 0 }, tbuf[512] = { 0 };
UNICODE_STRING cexe = { 0 }, texe = { 0 };
cexe.Buffer = cbuf; cexe.MaximumLength = sizeof(cbuf);
texe.Buffer = tbuf; texe.MaximumLength = sizeof(tbuf);
if (logok)
GetProcessImagePathByPid(cur, &cexe);
if (logok && IsWhitelist(&cexe)) {
ExReleaseRundownProtection(&g_Rundown);
return OB_PREOP_SUCCESS;
}
ACCESS_MASK old = *desired;
*desired &= ~mask;
if ((*desired) != old && logok) {
GetProcessImagePathByPid(target, &texe);
if (cexe.Length == 0) {
PEPROCESS eproc = PsGetCurrentProcess();
PCSTR n = PsGetProcessImageFileName(eproc);
size_t l = n ? strlen(n) : 0;
for (size_t i = 0; i < l && i < RTL_NUMBER_OF(cbuf) - 1; i++)
cbuf[i] = (WCHAR)n[i];
cbuf[l] = 0;
cexe.Length = (USHORT)(l * sizeof(WCHAR));
}
LogAnsi3("INJECT_BLOCK", (ULONG)(ULONG_PTR)cur, &cexe, &texe);
}
ExReleaseRundownProtection(&g_Rundown);
return OB_PREOP_SUCCESS;
}
static OB_PREOP_CALLBACK_STATUS PreThreadCallback(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION Info)
{
UNREFERENCED_PARAMETER(RegistrationContext);
if (!Info || Info->KernelHandle)
return OB_PREOP_SUCCESS;
if (Info->Object == NULL)
return OB_PREOP_SUCCESS;
if (Info->Operation != OB_OPERATION_HANDLE_CREATE && Info->Operation != OB_OPERATION_HANDLE_DUPLICATE)
return OB_PREOP_SUCCESS;
if (!ExAcquireRundownProtection(&g_Rundown))
return OB_PREOP_SUCCESS;
HANDLE cur = PsGetCurrentProcessId();
if (cur == (HANDLE)0 || cur == (HANDLE)4) {
ExReleaseRundownProtection(&g_Rundown);
return OB_PREOP_SUCCESS;
}
HANDLE owner = PsGetThreadProcessId((PETHREAD)Info->Object);
if (owner == cur) {
ExReleaseRundownProtection(&g_Rundown);
return OB_PREOP_SUCCESS;
}
ACCESS_MASK tmask = THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_IMPERSONATE | THREAD_DIRECT_IMPERSONATION;
ACCESS_MASK* desired = (Info->Operation == OB_OPERATION_HANDLE_CREATE) ? &Info->Parameters->CreateHandleInformation.DesiredAccess : &Info->Parameters->DuplicateHandleInformation.DesiredAccess;
if ((*desired & tmask) == 0) {
ExReleaseRundownProtection(&g_Rundown);
return OB_PREOP_SUCCESS;
}
BOOLEAN logok = (KeGetCurrentIrql() == PASSIVE_LEVEL);
WCHAR cbuf[512] = { 0 };
UNICODE_STRING cexe = { 0 };
cexe.Buffer = cbuf;
cexe.MaximumLength = sizeof(cbuf);
if (logok)
GetProcessImagePathByPid(cur, &cexe);
if (logok && IsWhitelist(&cexe)) {
ExReleaseRundownProtection(&g_Rundown);
return OB_PREOP_SUCCESS;
}
ACCESS_MASK old = *desired;
*desired &= ~tmask;
if ((*desired) != old && logok) {
if (cexe.Length == 0) {
PEPROCESS eproc = PsGetCurrentProcess();
PCSTR n = PsGetProcessImageFileName(eproc);
size_t l = n ? strlen(n) : 0;
for (size_t i = 0; i < l && i < RTL_NUMBER_OF(cbuf) - 1; i++)
cbuf[i] = (WCHAR)n[i];
cbuf[l] = 0;
cexe.Length = (USHORT)(l * sizeof(WCHAR));
}
UNICODE_STRING tnone = RTL_CONSTANT_STRING(L"None");
LogAnsi3("THREAD_BLOCK", (ULONG)(ULONG_PTR)cur, &cexe, &tnone);
}
ExReleaseRundownProtection(&g_Rundown);
return OB_PREOP_SUCCESS;
}
NTSTATUS InitInjectProtect(VOID)
{
static UNICODE_STRING obAlt;
RtlInitUnicodeString(&obAlt, L"385000");
g_ObOps = (POB_OPERATION_REGISTRATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(OB_OPERATION_REGISTRATION) * 2, 'bOpr');
if (!g_ObOps)
return STATUS_INSUFFICIENT_RESOURCES;
RtlZeroMemory(g_ObOps, sizeof(OB_OPERATION_REGISTRATION) * 2);
g_ObOps[0].ObjectType = PsProcessType;
g_ObOps[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
g_ObOps[0].PreOperation = PreProcessCallback;
g_ObOps[1].ObjectType = PsThreadType;
g_ObOps[1].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
g_ObOps[1].PreOperation = PreThreadCallback;
RtlZeroMemory(&g_ObReg, sizeof(g_ObReg));
g_ObReg.Version = OB_FLT_REGISTRATION_VERSION;
g_ObReg.OperationRegistrationCount = 2;
g_ObReg.Altitude = obAlt;
g_ObReg.RegistrationContext = NULL;
g_ObReg.OperationRegistration = g_ObOps;
NTSTATUS s = ObRegisterCallbacks(&g_ObReg, &g_ObRegHandle);
if (!NT_SUCCESS(s)) {
ExFreePool2(g_ObOps, 'bOpr', NULL, 0);
g_ObOps = NULL;
RtlZeroMemory(&g_ObReg, sizeof(g_ObReg));
}
return s;
}
VOID UninitInjectProtect(VOID)
{
if (g_ObRegHandle) {
ObUnRegisterCallbacks(g_ObRegHandle);
g_ObRegHandle = NULL;
}
if (g_ObOps) {
ExFreePool2(g_ObOps, 'bOpr', NULL, 0);
g_ObOps = NULL;
}
RtlZeroMemory(&g_ObReg, sizeof(g_ObReg));
}
-348
View File
@@ -1,348 +0,0 @@
#include <ntifs.h>
#include "DriverEntry.h"
#include "ProtectRules.h"
#include "ProtectMatch.h"
static __forceinline BOOLEAN USOK(PUNICODE_STRING s)
{
return s && s->Buffer && s->Length;
}
static BOOLEAN WildMatchN(const wchar_t* str, SIZE_T n, const wchar_t* pat)
{
if (*pat == 0)
return n == 0;
if (*pat == L'*') {
if (pat[1] == L'*') {
for (SIZE_T i = 0; i <= n; ++i)
if (WildMatchN(str + i, n - i, pat + 2))
return TRUE;
return FALSE;
}
SIZE_T i = 0;
for (; i < n && str[i] != L'\\'; ++i)
if (WildMatchN(str + i, n - i, pat + 1))
return TRUE;
return WildMatchN(str + i, n - i, pat + 1);
}
if (*pat == L'?')
return n > 0 && str[0] != L'\\' && WildMatchN(str + 1, n - 1, pat + 1);
if (n == 0)
return FALSE;
WCHAR sc = str[0], pc = *pat;
if (sc >= L'A' && sc <= L'Z')
sc += 32;
if (pc >= L'A' && pc <= L'Z')
pc += 32;
return sc == pc && WildMatchN(str + 1, n - 1, pat + 1);
}
static BOOLEAN MatchList(PUNICODE_STRING s, wchar_t** list)
{
if (!USOK(s) || !list)
return FALSE;
SIZE_T s_len = s->Length / sizeof(WCHAR);
for (SIZE_T i = 0; list[i]; ++i) {
wchar_t* pat = list[i];
if (wcschr(pat, L'*') || wcschr(pat, L'?')) {
if (WildMatchN(s->Buffer, s_len, pat))
return TRUE;
}
else {
SIZE_T pat_len = wcslen(pat);
if (s_len >= pat_len && _wcsnicmp(s->Buffer, pat, pat_len) == 0) {
if (s_len == pat_len || s->Buffer[s_len - pat_len - 1] == L'\\') {
return TRUE;
}
}
if (s_len >= pat_len && _wcsnicmp(s->Buffer + s_len - pat_len, pat, pat_len) == 0) {
return TRUE;
}
}
}
return FALSE;
}
static wchar_t* MatchListGetRule(PUNICODE_STRING s, wchar_t** list)
{
if (!USOK(s) || !list)
return 0;
SIZE_T s_len = s->Length / sizeof(WCHAR);
for (SIZE_T i = 0; list[i]; ++i) {
wchar_t* pat = list[i];
if (wcschr(pat, L'*') || wcschr(pat, L'?')) {
if (WildMatchN(s->Buffer, s_len, pat))
return pat;
}
else {
SIZE_T pat_len = wcslen(pat);
if (s_len >= pat_len && _wcsnicmp(s->Buffer, pat, pat_len) == 0) {
if (s_len == pat_len || s->Buffer[s_len - pat_len - 1] == L'\\') {
return pat;
}
}
if (s_len >= pat_len && _wcsnicmp(s->Buffer + s_len - pat_len, pat, pat_len) == 0) {
return pat;
}
}
}
return 0;
}
static BOOLEAN StrStrIW(const wchar_t* s, const wchar_t* sub)
{
if (!s || !sub)
return FALSE;
SIZE_T n = wcslen(s), m = wcslen(sub);
if (m == 0 || n < m)
return FALSE;
for (SIZE_T i = 0; i + m <= n; i++) {
if (_wcsnicmp(s + i, sub, m) == 0)
return TRUE;
}
return FALSE;
}
static BOOLEAN EndsWithNameUS(PUNICODE_STRING s, const wchar_t* name)
{
if (!USOK(s) || !name)
return FALSE;
SIZE_T n = s->Length / sizeof(WCHAR);
SIZE_T m = wcslen(name);
return n >= m && _wcsnicmp(s->Buffer + (n - m), name, m) == 0;
}
BOOLEAN IsRegistryBlock(PUNICODE_STRING key, PUNICODE_STRING valueName, PUNICODE_STRING exe)
{
if (USOK(key) && WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\MACHINE\\SYSTEM\\ControlSet001\\Services\\**"))
return FALSE;
if (USOK(exe) && IsWhitelist(exe))
return FALSE;
if (USOK(exe) && USOK(key)) {
if ((EndsWithNameUS(exe, L"services.exe") || EndsWithNameUS(exe, L"svchost.exe") || EndsWithNameUS(exe, L"sc.exe")) &&
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\SYSTEM\\CurrentControlSet\\Services\\**"))
return FALSE;
if ((EndsWithNameUS(exe, L"powershell.exe") || EndsWithNameUS(exe, L"pwsh.exe")) &&
(WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Internet Settings\\ZoneMap\\**") ||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Internet Settings\\ZoneMap") ||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Notifications\\Data\\**") ||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Notifications\\Data")))
return FALSE;
if ((EndsWithNameUS(exe, L"msiexec.exe") || EndsWithNameUS(exe, L"TrustedInstaller.exe")) &&
(WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Installer\\**") ||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Uninstall\\**") ||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\SYSTEM\\CurrentControlSet\\Services\\**")))
return FALSE;
if (EndsWithNameUS(exe, L"schtasks.exe") && WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\Windows NT\\CurrentVersion\\Schedule\\TaskCache\\**"))
return FALSE;
}
if (USOK(key) && MatchBlockReg(key))
return TRUE;
if (USOK(valueName)) {
for (SIZE_T i = 0; g_BlockReg[i]; ++i) {
wchar_t* pat = g_BlockReg[i];
if (!wcschr(pat, L'\\') && !wcschr(pat, L'*') && !wcschr(pat, L'?') && _wcsnicmp(valueName->Buffer, pat, wcslen(pat)) == 0)
return TRUE;
}
}
return FALSE;
}
BOOLEAN GetProcessImagePathByPid(HANDLE pid, PUNICODE_STRING ProcessImagePath)
{
PEPROCESS process = NULL;
PVOID buf = NULL;
HANDLE h = NULL;
NTSTATUS status;
ULONG len = 0;
status = PsLookupProcessByProcessId(pid, &process);
if (!NT_SUCCESS(status))
return FALSE;
status = ObOpenObjectByPointer(process, OBJ_KERNEL_HANDLE, NULL, PROCESS_QUERY_LIMITED_INFORMATION, *PsProcessType, KernelMode, &h);
if (!NT_SUCCESS(status))
goto cleanup;
status = ZwQueryInformationProcess(h, ProcessImageFileName, NULL, 0, &len);
if (status != STATUS_INFO_LENGTH_MISMATCH || len == 0)
goto cleanup;
buf = ExAllocatePool2(POOL_FLAG_NON_PAGED, len, 'iPgN');
if (!buf)
goto cleanup;
status = ZwQueryInformationProcess(h, ProcessImageFileName, buf, len, &len);
if (NT_SUCCESS(status)) {
PUNICODE_STRING image = (PUNICODE_STRING)buf;
if (image->Buffer && image->Length > 0 && ProcessImagePath->MaximumLength >= image->Length + sizeof(WCHAR)) {
RtlCopyMemory(ProcessImagePath->Buffer, image->Buffer, image->Length);
ProcessImagePath->Length = image->Length;
ProcessImagePath->Buffer[image->Length / sizeof(WCHAR)] = 0;
status = TRUE;
}
else {
status = FALSE;
}
}
else {
status = FALSE;
}
cleanup:
if (buf)
ExFreePool2(buf, 'iPgN', NULL, 0);
if (h)
ZwClose(h);
if (process)
ObDereferenceObject(process);
return NT_SUCCESS(status);
}
BOOLEAN IsWhitelist(PUNICODE_STRING s)
{
if (MatchList(s, g_WhitelistExcept))
return FALSE;
return MatchList(s, g_Whitelist);
}
BOOLEAN MatchBlockReg(PUNICODE_STRING s)
{
return MatchList(s, g_BlockReg);
}
wchar_t* GetMatchedBlockRegRule(PUNICODE_STRING s)
{
return MatchListGetRule(s, g_BlockReg);
}
BOOLEAN MatchBlockFile(PUNICODE_STRING s)
{
return MatchList(s, g_BlockFile);
}
BOOLEAN MatchBlockRansom(PUNICODE_STRING s)
{
return MatchList(s, g_BlockRansom);
}
wchar_t* GetMatchedBlockFileRule(PUNICODE_STRING s)
{
return MatchListGetRule(s, g_BlockFile);
}
BOOLEAN HasBlockedSuffix(PUNICODE_STRING s)
{
if (!USOK(s)) {
return FALSE;
}
for (SIZE_T i = 0; g_Blocksuffix[i]; ++i) {
if (EndsWithNameUS(s, g_Blocksuffix[i])) {
return TRUE;
}
}
return FALSE;
}
BOOLEAN MatchRemoteSuspectBin(PUNICODE_STRING img)
{
if (!USOK(img))
return FALSE;
for (SIZE_T i = 0; g_RemoteSuspectBins[i]; ++i)
if (EndsWithNameUS(img, g_RemoteSuspectBins[i]))
return TRUE;
return FALSE;
}
BOOLEAN MatchRemoteCommand(PUNICODE_STRING cmd, PUNICODE_STRING img)
{
if (!USOK(cmd))
return FALSE;
const wchar_t* s = cmd->Buffer;
for (SIZE_T i = 0; g_RemoteCmdIndicatorsHttp[i]; ++i)
if (StrStrIW(s, g_RemoteCmdIndicatorsHttp[i]))
return TRUE;
for (SIZE_T i = 0; g_RemoteCmdIndicatorsGeneric[i]; ++i)
if (StrStrIW(s, g_RemoteCmdIndicatorsGeneric[i]))
return TRUE;
if (USOK(img) && EndsWithNameUS(img, L"cmd.exe")) {
for (SIZE_T i = 0; g_RemoteCmdFromCmdExe[i]; ++i)
if (StrStrIW(s, g_RemoteCmdFromCmdExe[i]))
return TRUE;
}
if (USOK(img) && EndsWithNameUS(img, L"rundll32.exe")) {
for (SIZE_T i = 0; g_RemoteCmdFromRundll32Exe[i]; ++i)
if (StrStrIW(s, g_RemoteCmdFromRundll32Exe[i]))
return TRUE;
}
if (USOK(img) && EndsWithNameUS(img, L"regsvr32.exe") && StrStrIW(s, L"/i:") && StrStrIW(s, L"http"))
return TRUE;
if (USOK(img) && EndsWithNameUS(img, L"mshta.exe")) {
for (SIZE_T i = 0; g_RemoteCmdFromMshtaExe[i]; ++i)
if (StrStrIW(s, g_RemoteCmdFromMshtaExe[i]))
return TRUE;
}
return FALSE;
}
BOOLEAN MatchScreenCapModule(PUNICODE_STRING mod)
{
if (!USOK(mod))
return FALSE;
for (SIZE_T i = 0; g_ScreenCapModules[i]; ++i)
if (EndsWithNameUS(mod, g_ScreenCapModules[i]))
return TRUE;
return FALSE;
}
BOOLEAN MatchSuspiciousProcPath(PUNICODE_STRING img)
{
if (!USOK(img))
return FALSE;
const wchar_t* s = img->Buffer;
if (StrStrIW(s, L"\\Users\\")) {
for (SIZE_T i = 0; g_ScreenUserSubdirs[i]; ++i)
if (StrStrIW(s, g_ScreenUserSubdirs[i]))
return TRUE;
}
for (SIZE_T i = 0; g_ScreenOtherProcNeedles[i]; ++i)
if (StrStrIW(s, g_ScreenOtherProcNeedles[i]))
return TRUE;
return FALSE;
}
BOOLEAN MatchClrFromNonFramework(PUNICODE_STRING fullImageName)
{
if (!USOK(fullImageName))
return FALSE;
const wchar_t* s = fullImageName->Buffer;
return (StrStrIW(s, L"\\clr.dll") || StrStrIW(s, L"\\mscorwks.dll")) && !StrStrIW(s, L"\\Windows\\Microsoft.NET\\") && !StrStrIW(s, L"\\Framework");
}
BOOLEAN IsWhitelistExcept(PUNICODE_STRING img)
{
if (!USOK(img))
return FALSE;
return MatchList(img, g_WhitelistExcept);
}
-23
View File
@@ -1,23 +0,0 @@
#pragma once
#include <ntifs.h>
wchar_t* GetMatchedBlockRegRule(PUNICODE_STRING s);
wchar_t* GetMatchedBlockFileRule(PUNICODE_STRING s);
BOOLEAN GetProcessImagePathByPid(HANDLE pid, PUNICODE_STRING ProcessImagePath);
BOOLEAN IsWhitelist(PUNICODE_STRING s);
BOOLEAN IsWhitelistExcept(PUNICODE_STRING img);
BOOLEAN MatchBlockReg(PUNICODE_STRING s);
BOOLEAN IsRegistryBlock(PUNICODE_STRING key, PUNICODE_STRING valueName, PUNICODE_STRING exe);
BOOLEAN MatchBlockFile(PUNICODE_STRING s);
BOOLEAN MatchBlockRansom(PUNICODE_STRING s);
BOOLEAN HasBlockedSuffix(PUNICODE_STRING s);
BOOLEAN MatchRemoteSuspectBin(PUNICODE_STRING img);
BOOLEAN MatchRemoteCommand(PUNICODE_STRING cmd, PUNICODE_STRING img);
BOOLEAN MatchScreenCapModule(PUNICODE_STRING mod);
BOOLEAN MatchSuspiciousProcPath(PUNICODE_STRING img);
BOOLEAN MatchClrFromNonFramework(PUNICODE_STRING fullImageName);
-125
View File
@@ -1,125 +0,0 @@
#include <ntifs.h>
#include <ntstrsafe.h>
#include "DriverEntry.h"
typedef union _PS_PROTECTION {
UCHAR Level;
struct {
UCHAR Type : 3;
UCHAR Audit : 1;
UCHAR Signer : 4;
} Flags;
} PS_PROTECTION, * PPS_PROTECTION;
static PVOID g_PsSetProcessProtection = NULL;
static HANDLE g_ProtectedPid = NULL;
static PVOID g_ObCookie = NULL;
static NTSTATUS SetPPL31(_In_ PEPROCESS proc)
{
if (!proc || !g_PsSetProcessProtection)
return STATUS_PROCEDURE_NOT_FOUND;
PS_PROTECTION p = { 0 };
p.Level = 0x31;
((VOID(NTAPI*)(PEPROCESS, PS_PROTECTION))g_PsSetProcessProtection)(proc, p);
return STATUS_SUCCESS;
}
static OB_PREOP_CALLBACK_STATUS ObPreOperation(
_In_ PVOID RegistrationContext,
_Inout_ POB_PRE_OPERATION_INFORMATION info
)
{
UNREFERENCED_PARAMETER(RegistrationContext);
if (!info || info->KernelHandle)
return OB_PREOP_SUCCESS;
if (info->ObjectType == *PsProcessType) {
PEPROCESS proc = (PEPROCESS)info->Object;
HANDLE pid = PsGetProcessId(proc);
if (g_ProtectedPid && pid == g_ProtectedPid && PsGetCurrentProcessId() != g_ProtectedPid) {
ACCESS_MASK* mask = (info->Operation == OB_OPERATION_HANDLE_CREATE)
? &info->Parameters->CreateHandleInformation.DesiredAccess
: &info->Parameters->DuplicateHandleInformation.DesiredAccess;
if (*mask & PROCESS_TERMINATE) {
LogAnsi3("KILL_ATTEMPT",
(ULONG)(ULONG_PTR)PsGetCurrentProcessId(),
NULL, NULL);
*mask &= ~(PROCESS_TERMINATE | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD);
}
}
}
return OB_PREOP_SUCCESS;
}
static VOID ProcessNotify(
_In_opt_ PEPROCESS Process,
_In_ HANDLE Pid,
_In_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo
)
{
UNICODE_STRING target = RTL_CONSTANT_STRING(L"\\??\\C:\\PYAS.exe");
if (CreateInfo) {
if (Process && CreateInfo->ImageFileName &&
RtlEqualUnicodeString(CreateInfo->ImageFileName, &target, TRUE)) {
NTSTATUS st = SetPPL31(Process);
UNREFERENCED_PARAMETER(st);
InterlockedExchangePointer((PVOID*)&g_ProtectedPid, Pid);
LogAnsi3("PROC_PPL", (ULONG)(ULONG_PTR)Pid, (PUNICODE_STRING)CreateInfo->ImageFileName, NULL);
}
}
else {
if (g_ProtectedPid && Pid == g_ProtectedPid) {
InterlockedExchangePointer((PVOID*)&g_ProtectedPid, NULL);
}
}
}
NTSTATUS InitProcessProtect(VOID)
{
UNICODE_STRING n = RTL_CONSTANT_STRING(L"PsSetProcessProtection");
g_PsSetProcessProtection = MmGetSystemRoutineAddress(&n);
static OB_OPERATION_REGISTRATION ops[1];
RtlZeroMemory(ops, sizeof(ops));
ops[0].ObjectType = PsProcessType;
ops[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
ops[0].PreOperation = ObPreOperation;
ops[0].PostOperation = NULL;
UNICODE_STRING alt = RTL_CONSTANT_STRING(L"321000");
OB_CALLBACK_REGISTRATION reg;
RtlZeroMemory(&reg, sizeof(reg));
reg.Version = OB_FLT_REGISTRATION_VERSION;
reg.OperationRegistrationCount = 1;
reg.OperationRegistration = ops;
reg.RegistrationContext = NULL;
reg.Altitude = alt;
NTSTATUS s = ObRegisterCallbacks(&reg, &g_ObCookie);
if (!NT_SUCCESS(s)) {
g_ObCookie = NULL;
return s;
}
s = PsSetCreateProcessNotifyRoutineEx(ProcessNotify, FALSE);
if (!NT_SUCCESS(s)) {
ObUnRegisterCallbacks(g_ObCookie);
g_ObCookie = NULL;
return s;
}
return STATUS_SUCCESS;
}
VOID UninitProcessProtect(VOID)
{
if (g_ObCookie) {
ObUnRegisterCallbacks(g_ObCookie);
g_ObCookie = NULL;
}
PsSetCreateProcessNotifyRoutineEx(ProcessNotify, TRUE);
}
+226
View File
@@ -0,0 +1,226 @@
#include "DriverCommon.h"
static PVOID ObRegistrationHandle = NULL;
static OB_CALLBACK_REGISTRATION ObRegistration;
static OB_OPERATION_REGISTRATION ObCallbacks[2];
static BOOLEAN IsSystemImage(PUNICODE_STRING FullImageName) {
if (!FullImageName || !FullImageName->Buffer) return FALSE;
if (WildcardMatch(L"*\\Windows\\System32\\*", FullImageName->Buffer, FullImageName->Length) ||
WildcardMatch(L"*\\Windows\\SysWOW64\\*", FullImageName->Buffer, FullImageName->Length) ||
WildcardMatch(L"*\\Windows\\WinSxS\\*", FullImageName->Buffer, FullImageName->Length) ||
WildcardMatch(L"*\\Windows\\Microsoft.NET\\*", FullImageName->Buffer, FullImageName->Length) ||
WildcardMatch(L"*\\Common Files\\Microsoft Shared\\*", FullImageName->Buffer, FullImageName->Length) ||
WildcardMatch(L"*\\Program Files*\\*", FullImageName->Buffer, FullImageName->Length)) {
return TRUE;
}
return FALSE;
}
VOID ImageLoadNotify(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo) {
UNREFERENCED_PARAMETER(ImageInfo);
if (KeGetCurrentIrql() > APC_LEVEL) return;
if (!FullImageName || !FullImageName->Buffer) return;
if (ProcessId == (HANDLE)0) return;
if (IsSystemImage(FullImageName)) return;
if (IsTargetProtected(ProcessId)) {
if (CheckFileExtensionRule(FullImageName)) {
SendMessageToUser(6001, (ULONG)(ULONG_PTR)ProcessId, FullImageName->Buffer, FullImageName->Length);
}
}
}
static BOOLEAN IsCriticalSystemProcess(HANDLE ProcessId) {
if (ProcessId == (HANDLE)4) return TRUE;
if (KeGetCurrentIrql() != PASSIVE_LEVEL) return FALSE;
NTSTATUS status;
BOOLEAN isCritical = FALSE;
PEPROCESS Process = NULL;
PUNICODE_STRING imageFileName = NULL;
status = PsLookupProcessByProcessId(ProcessId, &Process);
if (!NT_SUCCESS(status)) return FALSE;
status = SeLocateProcessImageName(Process, &imageFileName);
if (NT_SUCCESS(status) && imageFileName && imageFileName->Buffer) {
if (WildcardMatch(L"*\\Windows\\System32\\csrss.exe", imageFileName->Buffer, imageFileName->Length) ||
WildcardMatch(L"*\\Windows\\System32\\lsass.exe", imageFileName->Buffer, imageFileName->Length) ||
WildcardMatch(L"*\\Windows\\System32\\services.exe", imageFileName->Buffer, imageFileName->Length) ||
WildcardMatch(L"*\\Windows\\System32\\wininit.exe", imageFileName->Buffer, imageFileName->Length) ||
WildcardMatch(L"*\\Windows\\System32\\smss.exe", imageFileName->Buffer, imageFileName->Length)) {
isCritical = TRUE;
}
ExFreePool(imageFileName);
}
ObDereferenceObject(Process);
return isCritical;
}
static BOOLEAN IsBlacklistedAdminTool(HANDLE ProcessId) {
if (KeGetCurrentIrql() != PASSIVE_LEVEL) return FALSE;
NTSTATUS status;
BOOLEAN isBlacklisted = FALSE;
PEPROCESS Process = NULL;
PUNICODE_STRING imageFileName = NULL;
status = PsLookupProcessByProcessId(ProcessId, &Process);
if (!NT_SUCCESS(status)) return FALSE;
status = SeLocateProcessImageName(Process, &imageFileName);
if (NT_SUCCESS(status) && imageFileName) {
if (imageFileName->Buffer) {
if (WildcardMatch(L"*Taskmgr.exe", imageFileName->Buffer, imageFileName->Length) ||
WildcardMatch(L"*taskkill.exe", imageFileName->Buffer, imageFileName->Length) ||
WildcardMatch(L"*ProcessHacker.exe", imageFileName->Buffer, imageFileName->Length) ||
WildcardMatch(L"*procmon.exe", imageFileName->Buffer, imageFileName->Length)) {
isBlacklisted = TRUE;
}
}
ExFreePool(imageFileName);
}
ObDereferenceObject(Process);
return isBlacklisted;
}
static OB_PREOP_CALLBACK_STATUS PreOpenProcess(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation) {
UNREFERENCED_PARAMETER(RegistrationContext);
if (OperationInformation->KernelHandle) return OB_PREOP_SUCCESS;
PEPROCESS TargetProcess = (PEPROCESS)OperationInformation->Object;
if (!TargetProcess) return OB_PREOP_SUCCESS;
HANDLE TargetPid = PsGetProcessId(TargetProcess);
HANDLE SourcePid = PsGetCurrentProcessId();
if (SourcePid == TargetPid) return OB_PREOP_SUCCESS;
if ((ULONG)(ULONG_PTR)SourcePid == GlobalData.PyasPid) return OB_PREOP_SUCCESS;
if (SourcePid == (HANDLE)4) return OB_PREOP_SUCCESS;
if (IsTargetProtected(TargetPid)) {
if (IsCriticalSystemProcess(SourcePid)) return OB_PREOP_SUCCESS;
BOOLEAN bIsTrusted = IsProcessTrusted(SourcePid);
if (bIsTrusted) {
if (IsBlacklistedAdminTool(SourcePid)) {
bIsTrusted = FALSE;
}
}
if (bIsTrusted) return OB_PREOP_SUCCESS;
ACCESS_MASK DenyMask = PROCESS_TERMINATE |
PROCESS_VM_OPERATION |
PROCESS_VM_WRITE |
PROCESS_CREATE_THREAD |
PROCESS_VM_READ |
PROCESS_DUP_HANDLE |
PROCESS_SUSPEND_RESUME |
PROCESS_SET_INFORMATION |
PROCESS_SET_QUOTA;
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask);
if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) {
OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask);
}
}
return OB_PREOP_SUCCESS;
}
static OB_PREOP_CALLBACK_STATUS PreOpenThread(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation) {
UNREFERENCED_PARAMETER(RegistrationContext);
if (OperationInformation->KernelHandle) return OB_PREOP_SUCCESS;
PETHREAD TargetThread = (PETHREAD)OperationInformation->Object;
if (!TargetThread) return OB_PREOP_SUCCESS;
PEPROCESS TargetProcess = IoThreadToProcess(TargetThread);
if (!TargetProcess) return OB_PREOP_SUCCESS;
HANDLE TargetPid = PsGetProcessId(TargetProcess);
HANDLE SourcePid = PsGetCurrentProcessId();
if (SourcePid == TargetPid) return OB_PREOP_SUCCESS;
if ((ULONG)(ULONG_PTR)SourcePid == GlobalData.PyasPid) return OB_PREOP_SUCCESS;
if (SourcePid == (HANDLE)4) return OB_PREOP_SUCCESS;
if (IsTargetProtected(TargetPid)) {
if (IsCriticalSystemProcess(SourcePid)) return OB_PREOP_SUCCESS;
BOOLEAN bIsTrusted = IsProcessTrusted(SourcePid);
if (bIsTrusted) {
if (IsBlacklistedAdminTool(SourcePid)) {
bIsTrusted = FALSE;
}
}
if (bIsTrusted) return OB_PREOP_SUCCESS;
ACCESS_MASK DenyMask = THREAD_TERMINATE |
THREAD_SUSPEND_RESUME |
THREAD_SET_CONTEXT |
THREAD_SET_INFORMATION |
THREAD_SET_THREAD_TOKEN;
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask);
if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) {
OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask);
}
}
return OB_PREOP_SUCCESS;
}
NTSTATUS InitializeProcessProtection() {
static UNICODE_STRING Altitude = RTL_CONSTANT_STRING(L"320000.PYAS.Ob");
ObCallbacks[0].ObjectType = PsProcessType;
ObCallbacks[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
ObCallbacks[0].PreOperation = PreOpenProcess;
ObCallbacks[0].PostOperation = NULL;
ObCallbacks[1].ObjectType = PsThreadType;
ObCallbacks[1].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
ObCallbacks[1].PreOperation = PreOpenThread;
ObCallbacks[1].PostOperation = NULL;
ObRegistration.Version = OB_FLT_REGISTRATION_VERSION;
ObRegistration.OperationRegistrationCount = 2;
ObRegistration.Altitude = Altitude;
ObRegistration.RegistrationContext = NULL;
ObRegistration.OperationRegistration = ObCallbacks;
NTSTATUS status = ObRegisterCallbacks(&ObRegistration, &ObRegistrationHandle);
if (!NT_SUCCESS(status) && (status == STATUS_FLT_INSTANCE_ALTITUDE_COLLISION || status == STATUS_OBJECT_NAME_COLLISION)) {
static UNICODE_STRING FallbackAltitude = RTL_CONSTANT_STRING(L"320000.PYAS.Ob.Fallback");
ObRegistration.Altitude = FallbackAltitude;
status = ObRegisterCallbacks(&ObRegistration, &ObRegistrationHandle);
}
return status;
}
VOID UninitializeProcessProtection() {
if (ObRegistrationHandle) {
ObUnRegisterCallbacks(ObRegistrationHandle);
ObRegistrationHandle = NULL;
}
}
-118
View File
@@ -1,118 +0,0 @@
#include <ntifs.h>
#include "DriverEntry.h"
#include "ProtectMatch.h"
static BOOLEAN QueryRegPathFromObject(PVOID KeyObject, PUNICODE_STRING OutPath, POBJECT_NAME_INFORMATION* OutInfo)
{
ULONG len = 0;
NTSTATUS s = ObQueryNameString(KeyObject, NULL, 0, &len);
if (s != STATUS_INFO_LENGTH_MISMATCH)
return FALSE;
POBJECT_NAME_INFORMATION p = (POBJECT_NAME_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, len, 'rgeR');
if (!p)
return FALSE;
s = ObQueryNameString(KeyObject, p, len, &len);
if (!NT_SUCCESS(s) || !p->Name.Buffer || p->Name.Length == 0) {
ExFreePool2(p, 'rgeR', NULL, 0);
return FALSE;
}
*OutInfo = p;
OutPath->Buffer = p->Name.Buffer;
OutPath->Length = p->Name.Length;
OutPath->MaximumLength = p->Name.Length;
return TRUE;
}
NTSTATUS RegistryProtectCallback(PVOID ctx, PVOID arg1, PVOID arg2)
{
UNREFERENCED_PARAMETER(ctx);
if (g_Unloading)
return STATUS_SUCCESS;
if (!ExAcquireRundownProtection(&g_Rundown))
return STATUS_SUCCESS;
REG_NOTIFY_CLASS type = (REG_NOTIFY_CLASS)(ULONG_PTR)arg1;
NTSTATUS status = STATUS_SUCCESS;
UNICODE_STRING checkReg = { 0 };
POBJECT_NAME_INFORMATION nameInfo = NULL;
UNICODE_STRING exe = { 0 };
WCHAR exeBuf[260] = { 0 };
exe.Buffer = exeBuf;
exe.MaximumLength = sizeof(exeBuf);
exe.Length = 0;
HANDLE pid = PsGetCurrentProcessId();
if (pid == (HANDLE)0 || pid == (HANDLE)4) {
ExReleaseRundownProtection(&g_Rundown);
return STATUS_SUCCESS;
}
PUNICODE_STRING valueName = NULL;
BOOLEAN need_log = FALSE;
BOOLEAN canQuery = (KeGetCurrentIrql() == PASSIVE_LEVEL);
if (type == RegNtPreSetValueKey) {
PREG_SET_VALUE_KEY_INFORMATION s = (PREG_SET_VALUE_KEY_INFORMATION)arg2;
valueName = s->ValueName;
if (canQuery)
QueryRegPathFromObject(s->Object, &checkReg, &nameInfo);
}
else if (type == RegNtPreDeleteValueKey) {
PREG_DELETE_VALUE_KEY_INFORMATION d = (PREG_DELETE_VALUE_KEY_INFORMATION)arg2;
valueName = d->ValueName;
if (canQuery)
QueryRegPathFromObject(d->Object, &checkReg, &nameInfo);
}
else if (type == RegNtPreDeleteKey) {
PREG_DELETE_KEY_INFORMATION d = (PREG_DELETE_KEY_INFORMATION)arg2;
if (canQuery)
QueryRegPathFromObject(d->Object, &checkReg, &nameInfo);
}
else if (type == RegNtPreRenameKey) {
PREG_RENAME_KEY_INFORMATION r = (PREG_RENAME_KEY_INFORMATION)arg2;
if (canQuery)
QueryRegPathFromObject(r->Object, &checkReg, &nameInfo);
}
else if (type == RegNtPreCreateKeyEx) {
PREG_CREATE_KEY_INFORMATION c = (PREG_CREATE_KEY_INFORMATION)arg2;
if (c->CompleteName)
checkReg = *c->CompleteName;
}
else if (type == RegNtPreReplaceKey) {
PREG_REPLACE_KEY_INFORMATION rk = (PREG_REPLACE_KEY_INFORMATION)arg2;
if (canQuery)
QueryRegPathFromObject(rk->Object, &checkReg, &nameInfo);
}
else if (type == RegNtPreSaveKey) {
PREG_SAVE_KEY_INFORMATION sk = (PREG_SAVE_KEY_INFORMATION)arg2;
if (canQuery)
QueryRegPathFromObject(sk->Object, &checkReg, &nameInfo);
}
else if (type == RegNtPreRestoreKey) {
PREG_RESTORE_KEY_INFORMATION res = (PREG_RESTORE_KEY_INFORMATION)arg2;
if (canQuery)
QueryRegPathFromObject(res->Object, &checkReg, &nameInfo);
}
else if (type == RegNtPreLoadKey) {
PREG_LOAD_KEY_INFORMATION lk = (PREG_LOAD_KEY_INFORMATION)arg2;
if (lk->KeyName)
checkReg = *lk->KeyName;
}
if (checkReg.Buffer || (valueName && valueName->Buffer)) {
if (canQuery)
GetProcessImagePathByPid(pid, &exe);
if (IsRegistryBlock(&checkReg, valueName, &exe)) {
need_log = TRUE;
status = STATUS_ACCESS_DENIED;
}
}
if (nameInfo)
ExFreePool2(nameInfo, 'rgeR', NULL, 0);
if (need_log)
LogAnsi3("REG_BLOCK", (ULONG)(ULONG_PTR)pid, &exe, &checkReg);
ExReleaseRundownProtection(&g_Rundown);
return status;
}
+254
View File
@@ -0,0 +1,254 @@
#include "DriverCommon.h"
static LARGE_INTEGER Cookie;
static BOOLEAN IsBamRegistryPath(PCUNICODE_STRING KeyName) {
if (!KeyName || !KeyName->Buffer) return FALSE;
if (WildcardMatch(L"*\\Services\\bam\\*", KeyName->Buffer, KeyName->Length)) {
return TRUE;
}
return FALSE;
}
static BOOLEAN IsWriteAccess(ACCESS_MASK Access) {
if (Access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK |
DELETE | WRITE_DAC | WRITE_OWNER | GENERIC_WRITE)) {
return TRUE;
}
return FALSE;
}
static PWCHAR GetFullPath(PVOID RootObject, PUNICODE_STRING CompleteName, PULONG OutLength) {
PWCHAR Buffer = NULL;
ULONG TotalSize = 0;
PCUNICODE_STRING RootName = NULL;
BOOLEAN NeedFreeRootName = FALSE;
if (RootObject) {
NTSTATUS status = CmCallbackGetKeyObjectIDEx(&Cookie, RootObject, NULL, &RootName, 0);
if (NT_SUCCESS(status) && RootName) {
NeedFreeRootName = TRUE;
}
}
ULONG RootLen = (RootName && RootName->Buffer) ? RootName->Length : 0;
ULONG RelLen = (CompleteName && CompleteName->Buffer) ? CompleteName->Length : 0;
TotalSize = RootLen + sizeof(WCHAR) + RelLen + sizeof(WCHAR);
Buffer = (PWCHAR)PyasAllocate(TotalSize);
if (!Buffer) {
if (NeedFreeRootName) CmCallbackReleaseKeyObjectIDEx(RootName);
return NULL;
}
RtlZeroMemory(Buffer, TotalSize);
PWCHAR Current = Buffer;
if (RootLen > 0) {
RtlCopyMemory(Current, RootName->Buffer, RootLen);
Current += (RootLen / sizeof(WCHAR));
if (Current > Buffer && *(Current - 1) != L'\\') {
*Current = L'\\';
Current++;
}
}
if (RelLen > 0) {
if (RootLen > 0 && CompleteName->Buffer[0] == L'\\') {
RtlCopyMemory(Current, CompleteName->Buffer + 1, RelLen - sizeof(WCHAR));
}
else {
RtlCopyMemory(Current, CompleteName->Buffer, RelLen);
}
}
if (NeedFreeRootName) CmCallbackReleaseKeyObjectIDEx(RootName);
if (OutLength) *OutLength = (ULONG)wcslen(Buffer) * sizeof(WCHAR);
return Buffer;
}
static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID Argument2) {
UNREFERENCED_PARAMETER(CallbackContext);
if (KeGetCurrentIrql() > APC_LEVEL) return STATUS_SUCCESS;
REG_NOTIFY_CLASS Class = (REG_NOTIFY_CLASS)(ULONG_PTR)Argument1;
NTSTATUS status = STATUS_SUCCESS;
if (Class == RegNtPreCreateKey || Class == RegNtPreOpenKey || Class == RegNtPreOpenKeyEx) {
PVOID RootObject = NULL;
PUNICODE_STRING CompleteName = NULL;
ACCESS_MASK DesiredAccess = 0;
if (Class == RegNtPreCreateKey) {
REG_CREATE_KEY_INFORMATION* Info = (REG_CREATE_KEY_INFORMATION*)Argument2;
RootObject = Info->RootObject;
CompleteName = Info->CompleteName;
DesiredAccess = Info->DesiredAccess;
}
else {
REG_OPEN_KEY_INFORMATION* Info = (REG_OPEN_KEY_INFORMATION*)Argument2;
RootObject = Info->RootObject;
CompleteName = Info->CompleteName;
DesiredAccess = Info->DesiredAccess;
}
if (!IsWriteAccess(DesiredAccess)) {
return STATUS_SUCCESS;
}
ULONG PathLen = 0;
PWCHAR FullPathBuffer = GetFullPath(RootObject, CompleteName, &PathLen);
if (FullPathBuffer) {
UNICODE_STRING FullPath;
RtlInitUnicodeString(&FullPath, FullPathBuffer);
if (CheckRegistryRule(&FullPath)) {
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
status = STATUS_ACCESS_DENIED;
}
}
PyasFree(FullPathBuffer);
}
return status;
}
if (Class == RegNtPreDeleteKey) {
REG_DELETE_KEY_INFORMATION* Info = (REG_DELETE_KEY_INFORMATION*)Argument2;
PCUNICODE_STRING KeyName = NULL;
if (NT_SUCCESS(CmCallbackGetKeyObjectIDEx(&Cookie, Info->Object, NULL, &KeyName, 0))) {
if (KeyName) {
if (CheckRegistryRule(KeyName)) {
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, KeyName->Buffer, KeyName->Length);
status = STATUS_ACCESS_DENIED;
}
}
CmCallbackReleaseKeyObjectIDEx(KeyName);
}
}
return status;
}
if (Class == RegNtPreSetValueKey) {
REG_SET_VALUE_KEY_INFORMATION* Info = (REG_SET_VALUE_KEY_INFORMATION*)Argument2;
PCUNICODE_STRING KeyName = NULL;
if (NT_SUCCESS(CmCallbackGetKeyObjectIDEx(&Cookie, Info->Object, NULL, &KeyName, 0))) {
if (KeyName) {
if (KeyName->Buffer) {
if (!IsBamRegistryPath(KeyName)) {
ULONG FullSize = KeyName->Length + sizeof(WCHAR) +
(Info->ValueName ? Info->ValueName->Length : 0) +
sizeof(WCHAR);
if (FullSize <= 0xFFFF) {
PWCHAR Buffer = (PWCHAR)PyasAllocate(FullSize);
if (Buffer) {
UNICODE_STRING FullPath;
FullPath.Buffer = Buffer;
FullPath.Length = 0;
FullPath.MaximumLength = (USHORT)FullSize;
RtlCopyUnicodeString(&FullPath, KeyName);
if (Info->ValueName && Info->ValueName->Length > 0) {
if (FullPath.Length > 0 && FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] != L'\\') {
RtlAppendUnicodeToString(&FullPath, L"\\");
}
RtlAppendUnicodeStringToString(&FullPath, Info->ValueName);
}
if (CheckRegistryRule(&FullPath)) {
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
status = STATUS_ACCESS_DENIED;
}
}
PyasFree(Buffer);
}
}
}
}
CmCallbackReleaseKeyObjectIDEx(KeyName);
}
}
return status;
}
if (Class == RegNtPreDeleteValueKey) {
REG_DELETE_VALUE_KEY_INFORMATION* Info = (REG_DELETE_VALUE_KEY_INFORMATION*)Argument2;
PCUNICODE_STRING KeyName = NULL;
if (NT_SUCCESS(CmCallbackGetKeyObjectIDEx(&Cookie, Info->Object, NULL, &KeyName, 0))) {
if (KeyName) {
if (KeyName->Buffer) {
if (!IsBamRegistryPath(KeyName)) {
ULONG FullSize = KeyName->Length + sizeof(WCHAR) +
(Info->ValueName ? Info->ValueName->Length : 0) +
sizeof(WCHAR);
if (FullSize <= 0xFFFF) {
PWCHAR Buffer = (PWCHAR)PyasAllocate(FullSize);
if (Buffer) {
UNICODE_STRING FullPath;
FullPath.Buffer = Buffer;
FullPath.Length = 0;
FullPath.MaximumLength = (USHORT)FullSize;
RtlCopyUnicodeString(&FullPath, KeyName);
if (Info->ValueName && Info->ValueName->Length > 0) {
if (FullPath.Length > 0 && FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] != L'\\') {
RtlAppendUnicodeToString(&FullPath, L"\\");
}
RtlAppendUnicodeStringToString(&FullPath, Info->ValueName);
}
if (CheckRegistryRule(&FullPath)) {
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
status = STATUS_ACCESS_DENIED;
}
}
PyasFree(Buffer);
}
}
}
}
CmCallbackReleaseKeyObjectIDEx(KeyName);
}
}
return status;
}
return STATUS_SUCCESS;
}
NTSTATUS InitializeRegistryProtection(PDRIVER_OBJECT DriverObject) {
static UNICODE_STRING Altitude = RTL_CONSTANT_STRING(L"320000.PYAS.Cm");
NTSTATUS status = CmRegisterCallbackEx(RegistryCallback, &Altitude, DriverObject, NULL, &Cookie, NULL);
if (!NT_SUCCESS(status)) {
static UNICODE_STRING FallbackAltitude = RTL_CONSTANT_STRING(L"320000.PYAS.Cm.Fallback");
status = CmRegisterCallbackEx(RegistryCallback, &FallbackAltitude, DriverObject, NULL, &Cookie, NULL);
}
return status;
}
VOID UninitializeRegistryProtection() {
if (Cookie.QuadPart != 0) {
CmUnRegisterCallback(Cookie);
Cookie.QuadPart = 0;
}
}
-83
View File
@@ -1,83 +0,0 @@
#include <ntifs.h>
#include "DriverEntry.h"
#include "ProtectMatch.h"
static BOOLEAN g_RemoteNotifyEnabled = FALSE;
static VOID ProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{
UNREFERENCED_PARAMETER(Process);
UNREFERENCED_PARAMETER(ProcessId);
if (!CreateInfo)
return;
if (g_Unloading)
return;
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
return;
if (!ExAcquireRundownProtection(&g_Rundown))
return;
HANDLE ppid = CreateInfo->CreatingThreadId.UniqueProcess;
if (ppid == (HANDLE)0 || ppid == (HANDLE)4) {
ExReleaseRundownProtection(&g_Rundown);
return;
}
UNICODE_STRING parent = { 0 };
WCHAR pbuf[512] = { 0 };
parent.Buffer = pbuf;
parent.MaximumLength = sizeof(pbuf);
if (!GetProcessImagePathByPid(ppid, &parent)) {
PEPROCESS eproc = PsGetCurrentProcess();
PCSTR n = PsGetProcessImageFileName(eproc);
size_t l = n ? strlen(n) : 0;
for (size_t i = 0; i < l && i < RTL_NUMBER_OF(pbuf) - 1; i++)
pbuf[i] = (WCHAR)n[i];
pbuf[l] = 0;
parent.Length = (USHORT)(l * sizeof(WCHAR));
}
if (IsWhitelist(&parent)) {
ExReleaseRundownProtection(&g_Rundown);
return;
}
UNICODE_STRING img = { 0 };
if (CreateInfo->ImageFileName)
img = *CreateInfo->ImageFileName;
UNICODE_STRING cmd = { 0 };
if (CreateInfo->CommandLine)
cmd = *CreateInfo->CommandLine;
BOOLEAN suspect = FALSE;
if (MatchRemoteSuspectBin(&img) && MatchRemoteCommand(&cmd, &img))
suspect = TRUE;
if (suspect) {
CreateInfo->CreationStatus = STATUS_ACCESS_DENIED;
if (cmd.Buffer && cmd.Length)
LogAnsi3("REMOTE_BLOCK", (ULONG)(ULONG_PTR)ppid, &parent, &cmd);
else if (img.Buffer && img.Length)
LogAnsi3("REMOTE_BLOCK", (ULONG)(ULONG_PTR)ppid, &parent, &img);
else {
UNICODE_STRING none = RTL_CONSTANT_STRING(L"None");
LogAnsi3("REMOTE_BLOCK", (ULONG)(ULONG_PTR)ppid, &parent, &none);
}
}
ExReleaseRundownProtection(&g_Rundown);
}
NTSTATUS InitRemoteProtect(VOID)
{
NTSTATUS s = PsSetCreateProcessNotifyRoutineEx(ProcessNotifyEx, FALSE);
if (NT_SUCCESS(s))
g_RemoteNotifyEnabled = TRUE;
return s;
}
VOID UninitRemoteProtect(VOID)
{
if (g_RemoteNotifyEnabled) {
PsSetCreateProcessNotifyRoutineEx(ProcessNotifyEx, TRUE);
g_RemoteNotifyEnabled = FALSE;
}
}
-266
View File
@@ -1,266 +0,0 @@
#include <ntifs.h>
#include "ProtectRules.h"
wchar_t* g_Whitelist[] = {
L"\\Device\\HarddiskVolume*\\Program*\\**",
L"\\Device\\HarddiskVolume*\\Users\\*\\AppData\\**",
L"**\\PYAS*.exe",
L"Registry",
L"vmmem*",
NULL
};
wchar_t* g_WhitelistExcept[] = {
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\vds.exe",
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\bcdedit.exe",
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\schtasks.exe",
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\ntoskrnl.exe",
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\wbadmin.exe",
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\bcdboot.exe",
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\dism.exe",
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\bootsect.exe",
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\net1.exe",
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\msiexec.exe",
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\lsass.exe",
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\csrss.exe",
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\smss.exe",
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\wininit.exe",
NULL
};
wchar_t* g_AttachDisk[] = {
L"\\FileSystem\\Ntfs",
L"\\FileSystem\\Refs",
L"\\FileSystem\\Fastfat",
L"\\FileSystem\\exfat",
L"\\FileSystem\\Udfs",
NULL
};
wchar_t* g_BlockFile[] = {
L"\\Device\\Harddisk*\\DR*",
L"\\Device\\Harddisk*\\Partition\\**",
L"\\Device\\HarddiskVolume*\\Boot\\**",
L"\\Device\\HarddiskVolume*\\EFI\\**",
L"\\Device\\HarddiskVolume*\\bootmgr",
L"\\Device\\HarddiskVolume*\\Recovery\\**",
L"\\Device\\HarddiskVolume*\\System Volume Information\\**",
L"\\Device\\HarddiskVolume*\\Windows\\**",
L"\\Device\\HarddiskVolume*\\$*\\**",
L"\\Device\\HarddiskVolume*\\ProgramData\\PYAS\\**",
L"**\\PYAS.exe",
L"**\\CON**",
L"**\\PRN**",
L"**\\AUX**",
L"**\\NUL**",
L"**\\COM*\\**",
L"**\\LPT*\\**",
L"**\\evil*.exe",
L"**\\OSDATA**",
NULL
};
wchar_t* g_BlockRansom[] = {
L"\\Device\\HarddiskVolume*\\Users\\*\\Desktop\\**",
L"\\Device\\HarddiskVolume*\\Users\\*\\Downloads\\**",
L"\\Device\\HarddiskVolume*\\Users\\*\\Documents\\**",
L"\\Device\\HarddiskVolume*\\Users\\*\\Music\\**",
L"\\Device\\HarddiskVolume*\\Users\\*\\Pictures\\**",
L"\\Device\\HarddiskVolume*\\Users\\*\\Videos\\**",
L"\\Device\\HarddiskVolume*\\Users\\*\\OneDrive\\**",
NULL
};
wchar_t* g_Blocksuffix[] = {
L".exe", L".dll", L".sys", L".com", L".scr",
L".zip", L".7z", L".rar", L".tar", L".gz",
L".js", L".bat", L".cmd", L".ps1", L".vbs",
L".ppt", L".pptx", L".wps", L".txt", L".rtf",
L".pdf", L".xls", L".xlsx", L".doc", L".docx",
L".jpg", L".jpeg", L".png", L".webp", L".gif",
L".mp3", L".wav", L".aac", L".ogg", L".flac",
L".mp4", L".avi", L".mov", L".wmv", L".mkv",
L".aux", L".cur", L".mui", L".ttf", L".efi",
NULL
};
wchar_t* g_BlockReg[] = {
L"\\REGISTRY\\MACHINE\\BCD00000000\\**",
L"\\REGISTRY\\MACHINE\\SAM\\**",
L"\\REGISTRY\\MACHINE\\SECURITY\\**",
L"\\REGISTRY\\USER\\*_Classes\\*\\shell\\open\\command\\**",
L"\\REGISTRY\\USER\\S-1-*\\SOFTWARE\\NetWire\\**",
L"\\REGISTRY\\USER\\S-1-*\\SOFTWARE\\Remcos*\\**",
L"\\REGISTRY\\USER\\S-1-*\\SOFTWARE\\DC3_FEXEC\\**",
L"DisableAntiSpyware",
L"DisableWindowsUpdateAccess",
L"EnableLUA",
L"ConsentPromptBehaviorAdmin",
L"NoControlPanel",
L"NoDrives",
L"NoFileMenu",
L"NoFind",
L"NoStartMenuPinnedList",
L"NoSetFolders",
L"NoSetFolderOptions",
L"NoViewOnDrive",
L"NoClose",
L"NoDesktop",
L"NoLogoff",
L"NoFolderOptions",
L"RestrictRun",
L"NoViewContextMenu",
L"HideClock",
L"NoStartMenuMyGames",
L"NoStartMenuMyMusic",
L"DisableCMD",
L"NoAddingComponents",
L"NoWinKeys",
L"NoStartMenuLogOff",
L"NoSimpleNetIDList",
L"NoLowDiskSpaceChecks",
L"DisableLockWorkstation",
L"Restrict_Run",
L"DisableTaskMgr",
L"DisableRegistryTools",
L"DisableChangePassword",
L"Wallpaper",
L"NoComponents",
L"NoStartMenuMorePrograms",
L"NoActiveDesktop",
L"NoSetActiveDesktop",
L"NoRecentDocsMenu",
L"NoWindowsUpdate",
L"NoChangeStartMenu",
L"NoFavoritesMenu",
L"NoRecentDocsHistory",
L"NoSetTaskbar",
L"NoSMHelp",
L"NoTrayContextMenu",
L"NoManageMyComputerVerb",
L"NoRealMode",
L"NoRun",
L"ClearRecentDocsOnExit",
L"NoActiveDesktopChanges",
L"NoStartMenuNetworkPlaces",
L"NoLockScreen",
L"HideFastUserSwitching",
L"ConsentPromptBehaviorUser",
L"EnableSecureUIAPaths",
L"NoSRM",
L"NoInteractiveServices",
L"ShutdownWithoutLogon",
L"AutoAdminLogon",
L"ImagePath",
L"BootExecute",
L"SubSystems",
L"FirmwareBootDevice",
L"BootDriverFlags",
L"SystemStartOptions",
L"CrashDumpEnabled",
L"DisableAutomaticRestartOnFailure",
L"SystemBiosVersion",
L"NoTheme",
L"PendingFileRenameOperations",
L"SystemSetupInProgress",
L"CmdLine",
L"SetupType",
L"AutoReboot",
L"Userinit",
L"UIHost",
L"Debugger",
L"DefaultUserName",
L"DefaultPassword",
L"AltDefaultUserName",
L"AltDefaultPassword",
L"DisableCAD",
L"LegalNoticeCaption",
L"LegalNoticeText",
NULL
};
wchar_t* g_RemoteSuspectBins[] = {
L"powershell.exe",
L"pwsh.exe",
L"wscript.exe",
L"cscript.exe",
L"mshta.exe",
L"rundll32.exe",
L"regsvr32.exe",
L"cmd.exe",
L"bitsadmin.exe",
L"msbuild.exe",
L"installutil.exe",
L"wmic.exe",
NULL
};
wchar_t* g_RemoteCmdIndicatorsHttp[] = {
L"http://",
L"https://",
NULL
};
wchar_t* g_RemoteCmdIndicatorsGeneric[] = {
L"-enc",
L"-e",
L"-encodedcommand",
L"frombase64string(",
L"iex(",
L"downloadstring(",
NULL
};
wchar_t* g_RemoteCmdFromCmdExe[] = {
L"powershell",
L"pwsh",
L"mshta",
L"rundll32",
L"wscript",
L"cscript",
NULL
};
wchar_t* g_RemoteCmdFromRundll32Exe[] = {
L".dll,",
L"javascript:",
L"http",
NULL
};
wchar_t* g_RemoteCmdRegsvr32Need[] = {
L"/i:",
NULL
};
wchar_t* g_RemoteCmdFromMshtaExe[] = {
L"http://",
L"https://",
NULL
};
wchar_t* g_ScreenCapModules[] = {
L"windows.graphics.capture.dll",
L"dxgi.dll",
L"d3d11.dll",
L"dwmapi.dll",
L"dcomp.dll",
L"mfreadwrite.dll",
L"mfplat.dll",
L"opengl32.dll",
L"gdi32full.dll",
L"win32u.dll",
NULL
};
wchar_t* g_ScreenUserSubdirs[] = {
L"\\AppData\\",
L"\\Temp\\",
L"\\Downloads\\",
NULL
};
wchar_t* g_ScreenOtherProcNeedles[] = {
L"\\ProgramData\\",
NULL
};
+933
View File
@@ -0,0 +1,933 @@
#include "DriverCommon.h"
constexpr auto MAX_TRACKERS = 64;
constexpr auto RANSOM_TIME_WINDOW_MS = 3000;
constexpr auto RANSOM_COUNT_THRESHOLD = 5;
constexpr auto HIGH_ENTROPY_THRESHOLD = 10;
constexpr auto TRUST_CACHE_SIZE = 128;
constexpr auto TRUST_CACHE_TTL_SEC = 300;
typedef struct _RANSOM_TRACKER {
PEPROCESS Process;
LARGE_INTEGER ProcessCreateTime;
ULONG ActivityCount;
LARGE_INTEGER LastActivityTime;
} RANSOM_TRACKER, * PRANSOM_TRACKER;
typedef struct _TRUST_CACHE_ENTRY {
PEPROCESS Process;
LARGE_INTEGER ProcessCreateTime;
BOOLEAN IsTrusted;
LARGE_INTEGER CacheTime;
} TRUST_CACHE_ENTRY, * PTRUST_CACHE_ENTRY;
static RANSOM_TRACKER RansomTrackers[MAX_TRACKERS];
static TRUST_CACHE_ENTRY TrustCache[TRUST_CACHE_SIZE];
static KSPIN_LOCK TrustCacheLock;
static BOOLEAN g_CacheInitialized = FALSE;
static ERESOURCE RuleLock;
static PRULE_NODE g_RegistryBlockList = NULL;
static PRULE_NODE g_RegistryTrustedList = NULL;
static PRULE_NODE g_ProcessTrustedPaths = NULL;
static PRULE_NODE g_ProcessExploitable = NULL;
static PRULE_NODE g_FileProtectedPaths = NULL;
static PRULE_NODE g_FileExceptionPaths = NULL;
static PRULE_NODE g_FileRansomExts = NULL;
const PCWSTR Helper_NaturallyCompressedExtensions[] = {
L".zip", L".7z", L".rar", L".tar", L".gz",
L".jpg", L".jpeg", L".png", L".webp", L".gif",
L".mp3", L".wav", L".aac", L".ogg", L".flac",
L".mp4", L".avi", L".mov", L".wmv", L".mkv",
L".docx", L".xlsx", L".pptx", L".pdf", L".wps",
L".apk", L".jar", L".class", L".db", L".sqlite"
};
static VOID FreeList(PRULE_NODE* Head) {
PRULE_NODE Current = *Head;
while (Current) {
PRULE_NODE Next = Current->Next;
if (Current->Pattern.Buffer) PyasFree(Current->Pattern.Buffer);
PyasFree(Current);
Current = Next;
}
*Head = NULL;
}
static VOID AddRule(PRULE_NODE* Head, PUNICODE_STRING RuleStr) {
if (!RuleStr || !RuleStr->Buffer) return;
PRULE_NODE Check = *Head;
while (Check) {
if (RtlEqualUnicodeString(&Check->Pattern, RuleStr, TRUE)) {
return;
}
Check = Check->Next;
}
PRULE_NODE Node = (PRULE_NODE)PyasAllocate(sizeof(RULE_NODE));
if (!Node) return;
SIZE_T Size = RuleStr->Length + sizeof(WCHAR);
Node->Pattern.Buffer = (PWCHAR)PyasAllocate(Size);
if (!Node->Pattern.Buffer) {
PyasFree(Node);
return;
}
RtlCopyMemory(Node->Pattern.Buffer, RuleStr->Buffer, RuleStr->Length);
Node->Pattern.Buffer[RuleStr->Length / sizeof(WCHAR)] = L'\0';
Node->Pattern.Length = RuleStr->Length;
Node->Pattern.MaximumLength = (USHORT)Size;
Node->Next = *Head;
*Head = Node;
}
static BOOLEAN HasSuffix(PCUNICODE_STRING String, PCWSTR Suffix) {
if (!String || !String->Buffer || !Suffix) return FALSE;
SIZE_T StringLenChars = String->Length / sizeof(WCHAR);
SIZE_T SuffixLenChars = 0;
while (Suffix[SuffixLenChars] != L'\0') {
SuffixLenChars++;
}
if (StringLenChars < SuffixLenChars) return FALSE;
PCWSTR Ptr = String->Buffer + (StringLenChars - SuffixLenChars);
for (SIZE_T i = 0; i < SuffixLenChars; i++) {
if (RtlDowncaseUnicodeChar(Ptr[i]) != RtlDowncaseUnicodeChar(Suffix[i])) {
return FALSE;
}
}
return TRUE;
}
BOOLEAN WildcardMatch(PCWSTR Pattern, PCWSTR String, USHORT StringLengthBytes) {
if (Pattern == NULL || String == NULL) return FALSE;
USHORT StringLenChars = StringLengthBytes / sizeof(WCHAR);
PCWSTR mp = NULL;
PCWSTR cp = NULL;
PCWSTR StringEnd = String + StringLenChars;
while (String < StringEnd) {
if (*Pattern == L'*') {
mp = ++Pattern;
cp = String + 1;
}
else if (*Pattern == L'?' || (RtlDowncaseUnicodeChar(*Pattern) == RtlDowncaseUnicodeChar(*String))) {
Pattern++;
String++;
}
else if (mp != NULL) {
Pattern = mp;
String = cp++;
}
else {
return FALSE;
}
}
while (*Pattern == L'*') {
Pattern++;
}
return (*Pattern == L'\0') ? TRUE : FALSE;
}
static ULONG ProcessJsonUnescape(PWCHAR Buffer, ULONG LengthChars) {
if (!Buffer || LengthChars == 0) return 0;
ULONG WriteIdx = 0;
ULONG ReadIdx = 0;
while (ReadIdx < LengthChars) {
if (Buffer[ReadIdx] == L'\\' && (ReadIdx + 1 < LengthChars)) {
WCHAR NextChar = Buffer[ReadIdx + 1];
if (NextChar == L'\\' || NextChar == L'"' || NextChar == L'/') {
Buffer[WriteIdx++] = NextChar;
ReadIdx += 2;
}
else if (NextChar == L'n') { Buffer[WriteIdx++] = L'\n'; ReadIdx += 2; }
else if (NextChar == L'r') { Buffer[WriteIdx++] = L'\r'; ReadIdx += 2; }
else if (NextChar == L't') { Buffer[WriteIdx++] = L'\t'; ReadIdx += 2; }
else {
Buffer[WriteIdx++] = Buffer[ReadIdx++];
}
}
else {
Buffer[WriteIdx++] = Buffer[ReadIdx++];
}
}
Buffer[WriteIdx] = L'\0';
return WriteIdx * sizeof(WCHAR);
}
static VOID SkipWhitespace(PCHAR* Ptr, PCHAR End) {
while (*Ptr < End) {
char c = **Ptr;
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
(*Ptr)++;
}
else {
break;
}
}
}
static VOID ParseAndLoadRules(PCHAR JsonContent, ULONG ContentLength, PCSTR KeyName, PRULE_NODE* ListHead) {
if (!JsonContent || !KeyName || !ListHead) return;
PCHAR Ptr = JsonContent;
PCHAR End = JsonContent + ContentLength;
SIZE_T KeyLen = 0;
while (KeyName[KeyLen] != '\0') KeyLen++;
while (Ptr < End) {
if (*Ptr == '"') {
if ((SIZE_T)(End - Ptr) > KeyLen && RtlCompareMemory(Ptr + 1, KeyName, KeyLen) == KeyLen) {
if (*(Ptr + 1 + KeyLen) == '"') {
Ptr += 1 + KeyLen + 1;
SkipWhitespace(&Ptr, End);
if (Ptr >= End || *Ptr != ':') continue;
Ptr++;
SkipWhitespace(&Ptr, End);
if (Ptr >= End || *Ptr != '[') continue;
Ptr++;
while (Ptr < End) {
SkipWhitespace(&Ptr, End);
if (Ptr >= End || *Ptr == ']') {
if (Ptr < End) Ptr++;
return;
}
if (*Ptr == '"') {
PCHAR StartQuote = ++Ptr;
BOOLEAN Escaped = FALSE;
while (Ptr < End) {
if (*Ptr == '"' && Escaped == FALSE) break;
if (*Ptr == '\\') {
Escaped = (Escaped == FALSE) ? TRUE : FALSE;
}
else {
Escaped = FALSE;
}
Ptr++;
}
if (Ptr < End && *Ptr == '"') {
ULONG UTF8Len = (ULONG)(Ptr - StartQuote);
if (UTF8Len > 0) {
ULONG WideSize = 0;
RtlUTF8ToUnicodeN(NULL, 0, &WideSize, StartQuote, UTF8Len);
if (WideSize > 0) {
PWCHAR WideBuffer = (PWCHAR)PyasAllocate(WideSize + sizeof(WCHAR));
if (WideBuffer) {
ULONG ResultSize = 0;
RtlUTF8ToUnicodeN(WideBuffer, WideSize, &ResultSize, StartQuote, UTF8Len);
ULONG FinalSize = ProcessJsonUnescape(WideBuffer, ResultSize / sizeof(WCHAR));
UNICODE_STRING Us;
Us.Buffer = WideBuffer;
Us.Length = (USHORT)FinalSize;
Us.MaximumLength = (USHORT)(WideSize + sizeof(WCHAR));
AddRule(ListHead, &Us);
PyasFree(WideBuffer);
}
}
}
Ptr++;
}
}
else {
Ptr++;
}
}
return;
}
}
}
Ptr++;
}
}
VOID InitializeRulesEngine() {
ExInitializeResourceLite(&RuleLock);
KeInitializeSpinLock(&TrustCacheLock);
RtlZeroMemory(TrustCache, sizeof(TrustCache));
g_CacheInitialized = TRUE;
}
VOID UninitializeRulesEngine() {
ExDeleteResourceLite(&RuleLock);
g_CacheInitialized = FALSE;
}
static VOID RemoveRule(PRULE_NODE* Head, PUNICODE_STRING RuleStr) {
if (!RuleStr || !RuleStr->Buffer) return;
PRULE_NODE Current = *Head;
PRULE_NODE Previous = NULL;
while (Current) {
if (RtlEqualUnicodeString(&Current->Pattern, RuleStr, TRUE)) {
PRULE_NODE ToDelete = Current;
if (Previous) {
Previous->Next = Current->Next;
}
else {
*Head = Current->Next;
}
Current = Current->Next;
if (ToDelete->Pattern.Buffer) PyasFree(ToDelete->Pattern.Buffer);
PyasFree(ToDelete);
}
else {
Previous = Current;
Current = Current->Next;
}
}
}
VOID AddDynamicWhitelist(PUNICODE_STRING RuleStr) {
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&RuleLock, TRUE);
AddRule(&g_ProcessTrustedPaths, RuleStr);
AddRule(&g_FileExceptionPaths, RuleStr);
KIRQL OldIrql;
KeAcquireSpinLock(&TrustCacheLock, &OldIrql);
RtlZeroMemory(TrustCache, sizeof(TrustCache));
KeReleaseSpinLock(&TrustCacheLock, OldIrql);
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
}
VOID RemoveDynamicWhitelist(PUNICODE_STRING RuleStr) {
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&RuleLock, TRUE);
RemoveRule(&g_ProcessTrustedPaths, RuleStr);
RemoveRule(&g_FileExceptionPaths, RuleStr);
KIRQL OldIrql;
KeAcquireSpinLock(&TrustCacheLock, &OldIrql);
RtlZeroMemory(TrustCache, sizeof(TrustCache));
KeReleaseSpinLock(&TrustCacheLock, OldIrql);
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
}
NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath) {
NTSTATUS status = STATUS_SUCCESS;
HANDLE FileHandle = NULL;
IO_STATUS_BLOCK IoStatus = { 0 };
OBJECT_ATTRIBUTES oa = { 0 };
UNICODE_STRING ImagePathName;
OBJECT_ATTRIBUTES RegOa = { 0 };
HANDLE RegHandle = NULL;
PKEY_VALUE_PARTIAL_INFORMATION Info = NULL;
ULONG ResultLength = 0;
PWCHAR PathBuffer = NULL;
SIZE_T PathBufferSize = 0;
UNICODE_STRING FinalPath = { 0 };
RtlInitUnicodeString(&ImagePathName, L"ImagePath");
InitializeObjectAttributes(&RegOa, RegistryPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
status = ZwOpenKey(&RegHandle, KEY_READ, &RegOa);
if (!NT_SUCCESS(status)) return status;
status = ZwQueryValueKey(RegHandle, &ImagePathName, KeyValuePartialInformation, NULL, 0, &ResultLength);
if (status != STATUS_BUFFER_TOO_SMALL) {
ZwClose(RegHandle);
return status;
}
Info = (PKEY_VALUE_PARTIAL_INFORMATION)PyasAllocate(ResultLength);
if (!Info) {
ZwClose(RegHandle);
return STATUS_INSUFFICIENT_RESOURCES;
}
status = ZwQueryValueKey(RegHandle, &ImagePathName, KeyValuePartialInformation, Info, ResultLength, &ResultLength);
ZwClose(RegHandle);
if (!NT_SUCCESS(status)) {
PyasFree(Info);
return status;
}
if (Info->Type == REG_EXPAND_SZ || Info->Type == REG_SZ) {
PathBufferSize = Info->DataLength + 1024;
PathBuffer = (PWCHAR)PyasAllocate(PathBufferSize);
if (PathBuffer) {
RtlZeroMemory(PathBuffer, PathBufferSize);
if (Info->DataLength > 0) {
RtlCopyMemory(PathBuffer, Info->Data, Info->DataLength);
}
PWCHAR LastSlash = NULL;
PWCHAR Current = PathBuffer;
while (*Current) {
if (*Current == L'\\') LastSlash = Current;
Current++;
}
if (LastSlash) {
*LastSlash = L'\0';
SIZE_T CurrentPathLen = wcslen(PathBuffer);
const WCHAR FilterSuffix[] = L"\\Filter";
SIZE_T FilterLen = (sizeof(FilterSuffix) / sizeof(WCHAR)) - 1;
if (CurrentPathLen >= FilterLen) {
PWCHAR SuffixStart = PathBuffer + CurrentPathLen - FilterLen;
BOOLEAN Match = TRUE;
for (SIZE_T i = 0; i < FilterLen; i++) {
if (RtlDowncaseUnicodeChar(SuffixStart[i]) != RtlDowncaseUnicodeChar(FilterSuffix[i])) {
Match = FALSE;
break;
}
}
if (Match) {
*SuffixStart = L'\0';
}
}
RtlStringCbCatW(PathBuffer, PathBufferSize, L"\\Rules\\Rules_Driver_P1.json");
if (wcsncmp(PathBuffer, L"\\??\\", 4) != 0 &&
wcsncmp(PathBuffer, L"\\SystemRoot", 11) != 0 &&
wcsncmp(PathBuffer, L"\\DosDevices\\", 12) != 0) {
PWCHAR TmpBuffer = (PWCHAR)PyasAllocate(PathBufferSize + 16);
if (TmpBuffer) {
RtlStringCbCopyW(TmpBuffer, PathBufferSize + 16, L"\\??\\");
RtlStringCbCatW(TmpBuffer, PathBufferSize + 16, PathBuffer);
PyasFree(PathBuffer);
PathBuffer = TmpBuffer;
}
}
RtlInitUnicodeString(&FinalPath, PathBuffer);
}
}
}
PyasFree(Info);
if (!FinalPath.Buffer) {
if (PathBuffer) PyasFree(PathBuffer);
return STATUS_UNSUCCESSFUL;
}
InitializeObjectAttributes(&oa, &FinalPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
status = ZwCreateFile(&FileHandle, GENERIC_READ | SYNCHRONIZE, &oa, &IoStatus, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (NT_SUCCESS(status)) {
FILE_STANDARD_INFORMATION FileInfo = { 0 };
status = ZwQueryInformationFile(FileHandle, &IoStatus, &FileInfo, sizeof(FileInfo), FileStandardInformation);
if (NT_SUCCESS(status) && FileInfo.EndOfFile.LowPart > 0) {
PVOID FileBuffer = PyasAllocate(FileInfo.EndOfFile.LowPart + 1);
if (FileBuffer) {
status = ZwReadFile(FileHandle, NULL, NULL, NULL, &IoStatus, FileBuffer, FileInfo.EndOfFile.LowPart, NULL, NULL);
if (NT_SUCCESS(status)) {
((PCHAR)FileBuffer)[FileInfo.EndOfFile.LowPart] = '\0';
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&RuleLock, TRUE);
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_Registry_BlockList", &g_RegistryBlockList);
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_Registry_TrustedList", &g_RegistryTrustedList);
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_Process_TrustedPaths", &g_ProcessTrustedPaths);
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_Process_ExploitableBlacklist", &g_ProcessExploitable);
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_File_ProtectedPaths", &g_FileProtectedPaths);
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_File_ExceptionPaths", &g_FileExceptionPaths);
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_File_RansomwareExtensions", &g_FileRansomExts);
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
}
PyasFree(FileBuffer);
}
}
ZwClose(FileHandle);
}
if (PathBuffer) PyasFree(PathBuffer);
return status;
}
VOID UnloadRules() {
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&RuleLock, TRUE);
FreeList(&g_RegistryBlockList);
FreeList(&g_RegistryTrustedList);
FreeList(&g_ProcessTrustedPaths);
FreeList(&g_ProcessExploitable);
FreeList(&g_FileProtectedPaths);
FreeList(&g_FileExceptionPaths);
FreeList(&g_FileRansomExts);
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
}
NTSTATUS GetProcessImageName(HANDLE ProcessId, PUNICODE_STRING* ImageName) {
if (KeGetCurrentIrql() != PASSIVE_LEVEL) {
return STATUS_UNSUCCESSFUL;
}
NTSTATUS status;
PEPROCESS Process = NULL;
*ImageName = NULL;
status = PsLookupProcessByProcessId(ProcessId, &Process);
if (!NT_SUCCESS(status)) return status;
status = SeLocateProcessImageName(Process, ImageName);
ObDereferenceObject(Process);
return status;
}
static BOOLEAN IsWindowsSystemApp(PCWSTR Buffer, USHORT Length) {
if (WildcardMatch(L"*\\Windows\\SystemApps\\*", Buffer, Length)) return TRUE;
if (WildcardMatch(L"*\\Windows\\ImmersiveControlPanel\\*", Buffer, Length)) return TRUE;
if (WildcardMatch(L"*\\Windows\\explorer.exe", Buffer, Length)) return TRUE;
return FALSE;
}
BOOLEAN IsProcessTrusted(HANDLE ProcessId) {
if ((ULONG)(ULONG_PTR)ProcessId == GlobalData.PyasPid) return TRUE;
if (ProcessId == (HANDLE)4) return TRUE;
PEPROCESS Process = NULL;
if (!NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &Process))) {
return FALSE;
}
LARGE_INTEGER createTime;
createTime.QuadPart = PsGetProcessCreateTimeQuadPart(Process);
if (g_CacheInitialized) {
KIRQL OldIrql;
KeAcquireSpinLock(&TrustCacheLock, &OldIrql);
ULONG Hash = (((ULONG)(ULONG_PTR)ProcessId) >> 2) & (TRUST_CACHE_SIZE - 1);
if (TrustCache[Hash].Process == Process && TrustCache[Hash].ProcessCreateTime.QuadPart == createTime.QuadPart) {
LARGE_INTEGER Now;
KeQuerySystemTime(&Now);
if ((Now.QuadPart - TrustCache[Hash].CacheTime.QuadPart) < (TRUST_CACHE_TTL_SEC * 10000000LL)) {
BOOLEAN cachedResult = TrustCache[Hash].IsTrusted;
KeReleaseSpinLock(&TrustCacheLock, OldIrql);
ObDereferenceObject(Process);
return cachedResult;
}
}
KeReleaseSpinLock(&TrustCacheLock, OldIrql);
}
if (KeGetCurrentIrql() != PASSIVE_LEVEL) {
ObDereferenceObject(Process);
return FALSE;
}
PUNICODE_STRING imageFileName = NULL;
NTSTATUS status = GetProcessImageName(ProcessId, &imageFileName);
BOOLEAN isTrusted = FALSE;
KeEnterCriticalRegion();
ExAcquireResourceSharedLite(&RuleLock, TRUE);
if (NT_SUCCESS(status) && imageFileName && imageFileName->Buffer) {
PRULE_NODE Node = g_ProcessExploitable;
while (Node) {
if (WildcardMatch(Node->Pattern.Buffer, imageFileName->Buffer, imageFileName->Length)) {
goto update_cache;
}
Node = Node->Next;
}
if (IsWindowsSystemApp(imageFileName->Buffer, imageFileName->Length)) {
isTrusted = TRUE;
goto update_cache;
}
Node = g_ProcessTrustedPaths;
while (Node) {
if (WildcardMatch(Node->Pattern.Buffer, imageFileName->Buffer, imageFileName->Length)) {
isTrusted = TRUE;
goto update_cache;
}
Node = Node->Next;
}
}
update_cache:
if (g_CacheInitialized) {
KIRQL OldIrql;
KeAcquireSpinLock(&TrustCacheLock, &OldIrql);
ULONG Hash = (((ULONG)(ULONG_PTR)ProcessId) >> 2) & (TRUST_CACHE_SIZE - 1);
TrustCache[Hash].Process = Process;
TrustCache[Hash].ProcessCreateTime = createTime;
TrustCache[Hash].IsTrusted = isTrusted;
KeQuerySystemTime(&TrustCache[Hash].CacheTime);
KeReleaseSpinLock(&TrustCacheLock, OldIrql);
}
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
if (imageFileName) ExFreePool(imageFileName);
ObDereferenceObject(Process);
return isTrusted;
}
BOOLEAN IsTargetProtected(HANDLE ProcessId) {
if ((ULONG)(ULONG_PTR)ProcessId == GlobalData.PyasPid) return TRUE;
return FALSE;
}
BOOLEAN CheckRegistryRule(PCUNICODE_STRING KeyName) {
if (!KeyName || !KeyName->Buffer) return FALSE;
if (KeyName->Length < 4 * sizeof(WCHAR)) return FALSE;
if (WildcardMatch(L"*{645FF040-5081-101B-9F08-00AA002F954E}\\DefaultIcon", KeyName->Buffer, KeyName->Length)) {
return FALSE;
}
KeEnterCriticalRegion();
ExAcquireResourceSharedLite(&RuleLock, TRUE);
PRULE_NODE AllowNode = g_RegistryTrustedList;
while (AllowNode) {
if (WildcardMatch(AllowNode->Pattern.Buffer, KeyName->Buffer, KeyName->Length)) {
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
return FALSE;
}
AllowNode = AllowNode->Next;
}
PRULE_NODE Node = g_RegistryBlockList;
BOOLEAN Match = FALSE;
while (Node) {
if (WildcardMatch(Node->Pattern.Buffer, KeyName->Buffer, KeyName->Length)) {
Match = TRUE;
break;
}
Node = Node->Next;
}
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
return Match;
}
BOOLEAN CheckFileExtensionRule(PCUNICODE_STRING FileName) {
if (!FileName || !FileName->Buffer) return FALSE;
KeEnterCriticalRegion();
ExAcquireResourceSharedLite(&RuleLock, TRUE);
PRULE_NODE ExNode = g_FileExceptionPaths;
while (ExNode) {
if (WildcardMatch(ExNode->Pattern.Buffer, FileName->Buffer, FileName->Length)) {
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
return FALSE;
}
ExNode = ExNode->Next;
}
PRULE_NODE Node = g_FileRansomExts;
BOOLEAN Match = FALSE;
while (Node) {
if (HasSuffix(FileName, Node->Pattern.Buffer)) {
Match = TRUE;
break;
}
Node = Node->Next;
}
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
return Match;
}
BOOLEAN CheckProtectedPathRule(PCUNICODE_STRING FileName) {
if (!FileName || !FileName->Buffer) return FALSE;
if (WildcardMatch(L"*\\Windows\\System32\\config\\systemprofile*", FileName->Buffer, FileName->Length)) {
return FALSE;
}
KeEnterCriticalRegion();
ExAcquireResourceSharedLite(&RuleLock, TRUE);
PRULE_NODE ExNode = g_FileExceptionPaths;
while (ExNode) {
if (WildcardMatch(ExNode->Pattern.Buffer, FileName->Buffer, FileName->Length)) {
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
return FALSE;
}
ExNode = ExNode->Next;
}
PRULE_NODE Node = g_FileProtectedPaths;
BOOLEAN Match = FALSE;
while (Node) {
if (WildcardMatch(Node->Pattern.Buffer, FileName->Buffer, FileName->Length)) {
Match = TRUE;
break;
}
Node = Node->Next;
}
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
return Match;
}
static BOOLEAN IsHighEntropy(PVOID Buffer, ULONG Length) {
if (!Buffer || Length < 256) return FALSE;
PUSHORT Histogram = (PUSHORT)PyasAllocate(256 * sizeof(USHORT));
if (!Histogram) return FALSE;
PUCHAR Ptr = (PUCHAR)Buffer;
ULONG ScanLen = (Length > 1024) ? 1024 : Length;
__try {
for (ULONG i = 0; i < ScanLen; i++) {
Histogram[Ptr[i]]++;
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
PyasFree(Histogram);
return FALSE;
}
USHORT MaxFreq = 0;
for (int i = 0; i < 256; i++) {
if (Histogram[i] > MaxFreq) MaxFreq = Histogram[i];
}
ULONG ExpectedAvg = ScanLen / 256;
BOOLEAN Result = (MaxFreq < (ExpectedAvg + HIGH_ENTROPY_THRESHOLD));
PyasFree(Histogram);
return Result;
}
static BOOLEAN IsNaturallyCompressed(PCUNICODE_STRING FileName) {
if (!FileName || !FileName->Buffer) return FALSE;
for (int i = 0; i < sizeof(Helper_NaturallyCompressedExtensions) / sizeof(Helper_NaturallyCompressedExtensions[0]); i++) {
if (HasSuffix(FileName, Helper_NaturallyCompressedExtensions[i])) return TRUE;
}
return FALSE;
}
static BOOLEAN IsNoisyRansomPath(PCUNICODE_STRING FileName) {
if (!FileName || !FileName->Buffer) return FALSE;
if (WildcardMatch(L"*\\Windows\\Temp\\*", FileName->Buffer, FileName->Length) ||
WildcardMatch(L"*\\Windows\\SystemTemp\\*", FileName->Buffer, FileName->Length) ||
WildcardMatch(L"*\\Windows\\Prefetch\\*", FileName->Buffer, FileName->Length) ||
WildcardMatch(L"*\\Windows\\Logs\\*", FileName->Buffer, FileName->Length) ||
WildcardMatch(L"*\\Profiles\\*", FileName->Buffer, FileName->Length) ||
WildcardMatch(L"*\\Program*\\*", FileName->Buffer, FileName->Length) ||
WildcardMatch(L"*\\AppData\\Local\\*", FileName->Buffer, FileName->Length) ||
WildcardMatch(L"*\\User Data\\*", FileName->Buffer, FileName->Length) ||
WildcardMatch(L"*$Recycle.Bin*", FileName->Buffer, FileName->Length)) {
return TRUE;
}
return FALSE;
}
static BOOLEAN IsUserDirectoryPath(PCUNICODE_STRING FileName) {
if (!FileName || !FileName->Buffer) return FALSE;
return WildcardMatch(L"*\\Users\\*", FileName->Buffer, FileName->Length);
}
static BOOLEAN IsExplorerProcess(HANDLE ProcessId) {
if (KeGetCurrentIrql() > APC_LEVEL) return FALSE;
PUNICODE_STRING imageFileName = NULL;
NTSTATUS status = GetProcessImageName(ProcessId, &imageFileName);
BOOLEAN isExplorer = FALSE;
if (NT_SUCCESS(status) && imageFileName) {
if (imageFileName->Buffer && HasSuffix(imageFileName, L"\\explorer.exe")) {
isExplorer = TRUE;
}
ExFreePool(imageFileName);
}
return isExplorer;
}
BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID WriteBuffer, ULONG WriteLength, BOOLEAN IsWrite) {
if (!IsUserDirectoryPath(FileName)) {
return FALSE;
}
if (IsNoisyRansomPath(FileName)) {
return FALSE;
}
if (!IsWrite) {
if (IsExplorerProcess(ProcessId)) return FALSE;
}
BOOLEAN SuspiciousWrite = FALSE;
if (IsWrite && WriteBuffer && WriteLength > 0) {
if (!IsNaturallyCompressed(FileName) && IsHighEntropy(WriteBuffer, WriteLength)) {
SuspiciousWrite = TRUE;
}
}
if (IsWrite && !SuspiciousWrite) {
return FALSE;
}
PEPROCESS Process = NULL;
if (!NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &Process))) {
return FALSE;
}
LARGE_INTEGER Now = { 0 };
KeQuerySystemTime(&Now);
LARGE_INTEGER createTime;
createTime.QuadPart = PsGetProcessCreateTimeQuadPart(Process);
BOOLEAN Result = FALSE;
KIRQL OldIrql;
KeAcquireSpinLock(&GlobalData.TrackerMutex, &OldIrql);
PRANSOM_TRACKER Tracker = NULL;
PRANSOM_TRACKER CandidateSlot = NULL;
PRANSOM_TRACKER LruSlot = &RansomTrackers[0];
for (int i = 0; i < MAX_TRACKERS; i++) {
PRANSOM_TRACKER Current = &RansomTrackers[i];
if (Current->LastActivityTime.QuadPart < LruSlot->LastActivityTime.QuadPart) {
LruSlot = Current;
}
if (Current->Process == Process && Current->ProcessCreateTime.QuadPart == createTime.QuadPart) {
Tracker = Current;
break;
}
BOOLEAN IsExpired = FALSE;
if (Current->Process != NULL) {
LARGE_INTEGER Diff;
Diff.QuadPart = Now.QuadPart - Current->LastActivityTime.QuadPart;
if (Diff.QuadPart > (RANSOM_TIME_WINDOW_MS * 10000LL)) {
IsExpired = TRUE;
}
}
if ((Current->Process == NULL || IsExpired) && !CandidateSlot) {
CandidateSlot = Current;
}
}
if (!Tracker) {
if (CandidateSlot) {
Tracker = CandidateSlot;
}
else {
Tracker = LruSlot;
}
Tracker->Process = Process;
Tracker->ProcessCreateTime = createTime;
Tracker->ActivityCount = 0;
Tracker->LastActivityTime = Now;
}
else {
LARGE_INTEGER Diff;
Diff.QuadPart = Now.QuadPart - Tracker->LastActivityTime.QuadPart;
if (Diff.QuadPart > (RANSOM_TIME_WINDOW_MS * 10000LL)) {
Tracker->ActivityCount = 0;
}
Tracker->LastActivityTime = Now;
}
ULONG Weight = 2;
Tracker->ActivityCount += Weight;
if (Tracker->ActivityCount >= RANSOM_COUNT_THRESHOLD) {
Result = TRUE;
}
KeReleaseSpinLock(&GlobalData.TrackerMutex, OldIrql);
ObDereferenceObject(Process);
return Result;
}
NTSTATUS SendMessageToUser(ULONG Code, ULONG Pid, PWCHAR Path, USHORT PathSize) {
if (KeGetCurrentIrql() > APC_LEVEL) return STATUS_UNSUCCESSFUL;
if (!ExAcquireRundownProtection(&GlobalData.PortRundown)) {
return STATUS_PORT_DISCONNECTED;
}
NTSTATUS status = STATUS_PORT_DISCONNECTED;
if (GlobalData.ClientPort) {
PPYAS_MESSAGE msg = (PPYAS_MESSAGE)PyasAllocate(sizeof(PYAS_MESSAGE));
if (msg) {
msg->MessageCode = Code;
msg->ProcessId = Pid;
if (Path && PathSize > 0) {
size_t MaxSize = sizeof(msg->Path) - sizeof(WCHAR);
size_t BytesToCopy = PathSize > MaxSize ? MaxSize : PathSize;
__try {
RtlCopyMemory(msg->Path, Path, BytesToCopy);
msg->Path[BytesToCopy / sizeof(WCHAR)] = L'\0';
}
__except (EXCEPTION_EXECUTE_HANDLER) {
PyasFree(msg);
ExReleaseRundownProtection(&GlobalData.PortRundown);
return STATUS_ACCESS_VIOLATION;
}
}
LARGE_INTEGER timeout;
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
timeout.QuadPart = -(5 * 10000);
}
else {
timeout.QuadPart = 0;
}
status = FltSendMessage(GlobalData.FilterHandle, &GlobalData.ClientPort, msg, sizeof(PYAS_MESSAGE), NULL, NULL, &timeout);
PyasFree(msg);
}
else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
ExReleaseRundownProtection(&GlobalData.PortRundown);
return status;
}
-25
View File
@@ -1,25 +0,0 @@
#pragma once
#include <ntifs.h>
extern wchar_t* g_Whitelist[];
extern wchar_t* g_WhitelistExcept[];
extern wchar_t* g_AttachDisk[];
extern wchar_t* g_BlockFile[];
extern wchar_t* g_BlockRansom[];
extern wchar_t* g_Blocksuffix[];
extern wchar_t* g_BlockReg[];
extern wchar_t* g_RemoteSuspectBins[];
extern wchar_t* g_RemoteCmdIndicatorsHttp[];
extern wchar_t* g_RemoteCmdIndicatorsGeneric[];
extern wchar_t* g_RemoteCmdFromCmdExe[];
extern wchar_t* g_RemoteCmdFromRundll32Exe[];
extern wchar_t* g_RemoteCmdRegsvr32Need[];
extern wchar_t* g_RemoteCmdFromMshtaExe[];
extern wchar_t* g_ScreenCapModules[];
extern wchar_t* g_ScreenUserSubdirs[];
extern wchar_t* g_ScreenOtherProcNeedles[];
-67
View File
@@ -1,67 +0,0 @@
#include <ntifs.h>
#include "DriverEntry.h"
#include "ProtectMatch.h"
static BOOLEAN g_ScreenImgCb = FALSE;
static VOID ImageLoadNotify(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo)
{
UNREFERENCED_PARAMETER(ImageInfo);
if (g_Unloading)
return;
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
return;
if (!ExAcquireRundownProtection(&g_Rundown))
return;
if (!FullImageName || !FullImageName->Buffer || FullImageName->Length == 0) {
ExReleaseRundownProtection(&g_Rundown);
return;
}
UNICODE_STRING proc = { 0 };
WCHAR pbuf[512] = { 0 };
proc.Buffer = pbuf;
proc.MaximumLength = sizeof(pbuf);
if (!GetProcessImagePathByPid(ProcessId, &proc)) {
PEPROCESS eproc = NULL;
if (NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &eproc))) {
PCSTR n = PsGetProcessImageFileName(eproc);
SIZE_T l = n ? strlen(n) : 0;
for (SIZE_T i = 0; i < l && i < RTL_NUMBER_OF(pbuf) - 1; i++)
pbuf[i] = (WCHAR)n[i];
pbuf[l] = 0;
proc.Length = (USHORT)(l * sizeof(WCHAR));
ObDereferenceObject(eproc);
}
}
if (proc.Length == 0) {
ExReleaseRundownProtection(&g_Rundown);
return;
}
if (IsWhitelist(&proc)) {
ExReleaseRundownProtection(&g_Rundown);
return;
}
BOOLEAN suspicious = MatchSuspiciousProcPath(&proc);
if (suspicious && MatchScreenCapModule(FullImageName))
LogAnsi3("SCREEN_BLOCK", (ULONG)(ULONG_PTR)ProcessId, &proc, FullImageName);
ExReleaseRundownProtection(&g_Rundown);
}
NTSTATUS InitScreenProtect(VOID)
{
NTSTATUS s = PsSetLoadImageNotifyRoutine(ImageLoadNotify);
if (NT_SUCCESS(s)) g_ScreenImgCb = TRUE;
return s;
}
VOID UninitScreenProtect(VOID)
{
if (g_ScreenImgCb) {
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
g_ScreenImgCb = FALSE;
}
}
+20
View File
@@ -0,0 +1,20 @@
#include <windows.h>
#include <ntverp.h>
#define VER_FILETYPE VFT_DRV
#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
#define VER_FILEDESCRIPTION_STR "PYAS Security Driver Protect"
#define VER_INTERNALNAME_STR "PYAS"
#define VER_LEGALCOPYRIGHT_STR "PYAS Security"
#define VER_ORIGINALFILENAME_STR "PYAS_Driver.sys"
#define VER_PRODUCTNAME_STR "PYAS"
#define VER_FILEVERSION 3,5,1,0
#define VER_FILEVERSION_STR "3,5,1,0"
#define VER_PRODUCTVERSION 3,5,1,0
#define VER_PRODUCTVERSION_STR "3,5,1,0"
#define VER_COMPANYNAME_STR "PYAS Security"
#include "common.ver"
+226
View File
@@ -0,0 +1,226 @@
{
"Rule_Registry_BlockList": [
"\\REGISTRY\\MACHINE\\BCD00000000",
"\\REGISTRY\\MACHINE\\BCD00000000\\*",
"*\\Classes\\.exe",
"*\\Classes\\.bat",
"*\\Classes\\.cmd",
"*\\Classes\\.com",
"*\\Classes\\exefile\\*",
"*\\Classes\\batfile\\*",
"*\\Classes\\cmdfile\\*",
"*\\Classes\\comfile\\*",
"*\\mouclass",
"*\\kbdclass",
"*\\DisableAntiSpyware",
"*\\DisableWindowsUpdateAccess",
"*\\EnableLUA",
"*\\ConsentPromptBehaviorAdmin",
"*\\NoControlPanel",
"*\\NoDrives",
"*\\NoFileMenu",
"*\\NoFind",
"*\\NoStartMenuPinnedList",
"*\\NoSetFolders",
"*\\NoSetFolderOptions",
"*\\NoViewOnDrive",
"*\\NoClose",
"*\\NoDesktop",
"*\\NoLogoff",
"*\\NoFolderOptions",
"*\\RestrictRun",
"*\\NoViewContextMenu",
"*\\HideClock",
"*\\NoStartMenuMyGames",
"*\\NoStartMenuMyMusic",
"*\\DisableCMD",
"*\\NoAddingComponents",
"*\\NoWinKeys",
"*\\NoStartMenuLogOff",
"*\\NoSimpleNetIDList",
"*\\NoLowDiskSpaceChecks",
"*\\DisableLockWorkstation",
"*\\Restrict_Run",
"*\\DisableTaskMgr",
"*\\DisableRegistryTools",
"*\\DisableChangePassword",
"*\\NoComponents",
"*\\NoStartMenuMorePrograms",
"*\\NoActiveDesktop",
"*\\NoSetActiveDesktop",
"*\\NoRecentDocsMenu",
"*\\NoWindowsUpdate",
"*\\NoChangeStartMenu",
"*\\NoFavoritesMenu",
"*\\NoRecentDocsHistory",
"*\\NoSetTaskbar",
"*\\NoSMHelp",
"*\\NoTrayContextMenu",
"*\\NoManageMyComputerVerb",
"*\\NoRealMode",
"*\\NoRun",
"*\\ClearRecentDocsOnExit",
"*\\NoActiveDesktopChanges",
"*\\NoStartMenuNetworkPlaces",
"*\\NoLockScreen",
"*\\HideFastUserSwitching",
"*\\ConsentPromptBehaviorUser",
"*\\EnableSecureUIAPaths",
"*\\NoSRM",
"*\\NoInteractiveServices",
"*\\ShutdownWithoutLogon",
"*\\AutoAdminLogon",
"*\\BootExecute",
"*\\SubSystems",
"*\\FirmwareBootDevice",
"*\\BootDriverFlags",
"*\\SystemStartOptions",
"*\\DisableAutomaticRestartOnFailure",
"*\\SystemBiosVersion",
"*\\NoTheme",
"*\\SystemSetupInProgress",
"*\\SetupType",
"*\\AutoReboot",
"*\\Userinit",
"*\\UIHost",
"*\\Debugger",
"*\\DefaultUserName",
"*\\DefaultPassword",
"*\\AltDefaultUserName",
"*\\AltDefaultPassword",
"*\\DisableCAD",
"*\\LegalNoticeCaption",
"*\\LegalNoticeText",
],
"Rule_Registry_TrustedList": [
],
"Rule_Process_TrustedPaths": [
"\\Device\\HarddiskVolume*\\Windows\\*",
"\\Device\\HarddiskVolume*\\Program Files\\*",
"\\Device\\HarddiskVolume*\\Program Files (x86)\\*",
"\\Device\\HarddiskVolume*\\ProgramData\\Microsoft\\Windows Defender\\*",
"\\Device\\HarddiskVolume*\\ProgramData\\Microsoft\\Windows Defender Advanced Threat Protection\\*",
"*\\Sources\\SetupHost.exe",
"*\\Work\\*\\DismHost.exe",
"*\\Google\\Chrome\\Application\\chrome.exe",
"*\\Microsoft\\Edge\\Application\\msedge.exe",
"*\\Mozilla Firefox\\firefox.exe",
"*\\Thorium\\Application\\thorium.exe",
"*\\Steam\\steam.exe",
"*\\steam\\*\\steam.exe",
"*\\Battle.net\\*\\Battle.net.exe",
"*\\Epic Games\\Launcher\\Portal\\Binaries\\*\\EpicGamesLauncher.exe",
"*\\resources\\bin\\SDK\\DownloadSDKServer.exe",
"*\\ThunderEv.exe",
"*\\ThunderStart.exe",
"*\\KuGou.exe",
"*\\XLGame.exe",
"*\\XLLiveUD.exe",
"*\\hrkill-*.exe",
"*\\bin\\HipsDaemon.exe",
"*\\360Safe.exe",
"*\\bilibili\\bilibili.exe",
"*\\Kingsoft\\WPS Office\\ksolaunch.exe",
"*\\Kingsoft\\WPS Office\\*\\wps.exe",
"*\\PC4399_WPFLauncher.exe",
"*\\bin\\APlayer.exe",
"*\\Neverness To Everness\\NTELauncher.exe",
"*\\Application\\AndrowsLauncher.exe",
],
"Rule_Process_ExploitableBlacklist": [
"*\\cmd.exe",
"*\\powershell.exe",
"*\\pwsh.exe",
"*\\wscript.exe",
"*\\cscript.exe",
"*\\mshta.exe",
"*\\regsvr32.exe",
"*\\certutil.exe",
"*\\bitsadmin.exe",
"*\\wmic.exe",
"*\\hh.exe",
"*\\cmstp.exe",
"*\\installutil.exe",
"*\\regasm.exe",
"*\\regsvcs.exe",
"*\\msbuild.exe",
"*\\csc.exe",
"*\\vbc.exe",
"*\\jsc.exe",
"*\\bash.exe",
"*\\scrcons.exe",
"*\\schtasks.exe",
"*\\at.exe",
"*\\reg.exe",
"*\\sc.exe",
"*\\net.exe",
"*\\net1.exe",
"*\\whoami.exe",
"*\\ipconfig.exe",
"*\\taskkill.exe",
"*\\ProcessHacker.exe",
"*\\procmon.exe",
"*\\procexp.exe",
],
"Rule_File_ProtectedPaths": [
"*\\PYAS.exe",
"*\\Plugins\\Filter\\PYAS_Driver.sys",
"*\\Plugins\\Rules\\Rules_Driver_P1.json",
"*\\ProgramData\\PYAS\\*.json",
"*\\Engine\\Pattern\\*.onnx",
"*\\Engine\\Properties\\*.onnx",
"\\Device\\HarddiskVolume*\\bootmgr",
"\\Device\\HarddiskVolume*\\boot.ini",
"\\Device\\HarddiskVolume*\\BOOTNXT",
"\\Device\\HarddiskVolume*\\EFI\\*",
"\\Device\\HarddiskVolume*\\Boot\\*",
"\\Device\\HarddiskVolume*\\Recovery\\*",
"\\Device\\HarddiskVolume*\\System Volume Information\\*",
"\\Device\\HarddiskVolume*\\Windows\\System32\\config\\*",
"*\\CON",
"*\\CON\\*",
"*\\PRN",
"*\\PRN\\*",
"*\\AUX",
"*\\AUX\\*",
"*\\NUL",
"*\\NUL\\*",
"*\\COM?",
"*\\COM?\\*",
"*\\LPT?",
"*\\LPT?\\*",
"*\\OSDATA",
"*\\OSDATA\\*",
"*\\*<*",
"*\\*>*",
],
"Rule_File_ExceptionPaths": [
"*\\Users\\*\\steam\\*",
"*\\AppData\\*",
"*\\Temp\\*",
"*\\Local\\Temp\\*",
"*\\ventoy\\Boot\\*",
"*\\AdminRegionConfig.*",
"*\\nt_qq\\nt_*\\*",
"*\\cache\\*",
],
"Rule_File_RansomwareExtensions": [
".zip", ".7z", ".rar", ".tar", ".gz",
".ppt", ".pptx", ".wps", ".txt", ".rtf",
".pdf", ".xls", ".xlsx", ".doc", ".docx",
".jpg", ".jpeg", ".png", ".webp", ".gif",
".mp3", ".wav", ".aac", ".ogg", ".flac",
".mp4", ".avi", ".mov", ".wmv", ".mkv",
]
}
+105 -136
View File
@@ -1,22 +1,32 @@
# PYAS
# PYAS Security
Antivirus software written in Python and C that blocks threats through deep learning and behavioral monitoring!
Antivirus software written in Python and C++ that blocks threats through Machine Learning and behavioral monitoring!
<img width="2245" height="1477" alt="PYAS_UI" src="https://github.com/user-attachments/assets/87d40261-7655-49ad-a19c-1ffcca60584f" />
<img width="2440" height="1600" alt="PYAS_All" src="https://github.com/user-attachments/assets/1991aad3-64cc-4266-ac67-dab70c891ce7" />
## Requirements
Python 3.10 is recommended. Other Python versions may require different pip commands.
```
pip install numpy==1.26.4
pip install onnxruntime==1.18.1
pip install pefile==2023.2.7
pip install Pillow==11.0.0
pip install pyperclip==1.9.0
pip install PySide6==6.9.1
pip install requests==2.32.4
pip install yara-python==4.5.4
pip install pystray
pip install pefile
pip install requests
pip install pywebview
pip install Pillow
pip install yara-python
pip install numpy
pip install onnxruntime
```
Non-essential requirements installation, only used for model training or other functions.
```
pip install pandas
pip install scikit-learn
pip install lightgbm
pip install onnxmltools
pip install orjson
```
## File Information
@@ -25,34 +35,28 @@ The following lists the storage locations of all relevant code and other related
```
PYAS/
├── Engine/
│ ├── Models/
│ │ ├── convert.py # Convert executable files or other files to images
│ │ └── train.py # TensorFlow CNN model training complete code
│ │
├── Engine/ # Complete code for Yara signatures and AI model training
│ ├── Heuristic/
│ │ └── ... # Yara Rules
│ └── Properties/
│ └── ... # LightGBM Model
│
├── Interface/ # Interface Interaction and Icons with WebView2
│ ├── static/
│ │ └── ... # style.css, main.js, icon.ico
│ └── templates/
│ └── ... # index.html
│
├── Plugins/ # Main filter driver protect and rules
│ ├── Filter/
│ │ └── ... # DriverCommon.h, DriverEntry.cpp, ProtectRegistry.cpp, ...
│ └── Rules/
│ ├── rules.yar # Yara virus signature rule matching
│ └── rules.ips # IP network address rule matching
│
├── Plugins/
│ └── Filter/
│ ├── DriverEntry.c # Main driver entry and initialization logic
│ ├── DriverEntry.h # Global driver definitions, constants, and functions
│ ├── DriverPipe.c # Kernel to user pipe logging implementation
│ ├── ProtectBoot.c # Disk boot sector write protection
│ ├── ProtectImage.c # Image load monitoring and shellcode detection
│ ├── ProtectInject.c # Process thread handle access control to prevent injection
│ ├── ProtectReg.c # Registry modification protection
│ ├── ProtectRules.c # White, block list, and matching logic for files registry
│ └── ProtectRules.h # Protection rule declarations
│
├── PYAS.py # Main application entry point and UI to engine interface
├── PYAS_Config.py # Configuration loading, saving, and global parameters
├── PYAS_Engine.py # Core scanning engine: YARA, IP, ONNX model execution
├── PYAS_Interface.py # User interface components and event handling
├── PYAS_Resource.py # Static image and icon resource management
│ └── ... # Rules_Driver_P1.json
|
├── PYAS.py # Main antivirus application entry point
├── PYAS_Engine.py # Core scanning engine algorithm
├── PYAS_Version.py # Version metadata for packaging and updates
└── ... # Other supplementary folders and files
└── ...
```
## Architecture diagram
@@ -62,122 +66,83 @@ PYAS Security antivirus software general architecture diagram.
```mermaid
graph TD
%% Global Styles
classDef ui fill:#e1f5fe,stroke:#01579b,stroke-width:2px;
classDef logic fill:#fff9c4,stroke:#fbc02d,stroke-width:2px;
classDef engine fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px;
classDef thread fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px;
classDef kernel fill:#ffebee,stroke:#c62828,stroke-width:2px;
classDef data fill:#eceff1,stroke:#455a64,stroke-width:2px,stroke-dasharray: 5 5;
classDef userMode fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:#0d47a1
classDef kernelMode fill:#fff3e0,stroke:#e65100,stroke-width:2px,color:#e65100
classDef storage fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:#4a148c
classDef interaction fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,stroke-dasharray: 5 5,color:#1b5e20
User((User))
subgraph Presentation_Layer ["Presentation Layer (View)"]
direction TB
UI_Static["PYAS_Interface.py<br/>(Ui_main_window Class)"]:::ui
UI_Controller["PYAS.py<br/>(MainWindow_Controller Class)"]:::ui
TrayIcon["System Tray Icon"]:::ui
subgraph UserMode ["User Mode (Python / Web)"]
UI["Web UI (pywebview)"]:::userMode
PYASCore["PYAS Core (PYAS.py)"]:::userMode
User <--> UI_Controller
UI_Static --- UI_Controller
UI_Controller --- TrayIcon
end
subgraph Core_Logic_Layer ["Core Logic & Controller"]
direction TB
Init_Env["Initialization<br/>(init_environ, init_variable,<br/>init_windll)"]:::logic
Config_Mgr["Configuration Manager<br/>(load_config, save_config)"]:::logic
subgraph System_Tools ["System Tools"]
Sys_Repair["System Repair<br/>(repair_system_mbr,<br/>repair_system_image, etc.)"]:::logic
Sys_Clean["System Cleaner<br/>(clean_button, traverse_temp)"]:::logic
Proc_Mgr["Process Manager<br/>(list_process, kill_process)"]:::logic
List_Mgr["List Manager<br/>(manage_named_list:<br/>Whitelist/Quarantine)"]:::logic
subgraph Engines ["Scanning Engines (PYAS_Engine.py)"]
PEScanner["PE/ML Scanner (pe_scanner)"]:::userMode
RuleScanner["YARA Scanner (rule_scanner)"]:::userMode
CloudScanner["Cloud API (cloud_scanner)"]:::userMode
SignScanner["Signature Verify (sign_scanner)"]:::userMode
end
UI_Controller --> Init_Env
Init_Env --> Config_Mgr
UI_Controller --> System_Tools
end
subgraph Protection_Layer ["Real-time Protection Threads (Daemons)"]
direction TB
Thread_Proc["Process Guard Thread<br/>(protect_proc_thread)<br/>[WMI/Snapshot]"]:::thread
Thread_File["File Guard Thread<br/>(protect_file_thread)<br/>[ReadDirectoryChangesW]"]:::thread
Thread_Net["Network Guard Thread<br/>(protect_net_thread)<br/>[GetExtendedTcpTable]"]:::thread
Thread_Sys["System Guard Thread<br/>(protect_system_thread)<br/>[Reg/MBR Check]"]:::thread
Thread_Popup["Popup Blocker Thread<br/>(popup_intercept_thread)<br/>[EnumWindows]"]:::thread
Thread_Pipe["Driver IPC Thread<br/>(pipe_server_thread)<br/>[NamedPipe Server]"]:::thread
Init_Env --> Thread_Proc
Init_Env --> Thread_File
Init_Env --> Thread_Net
Init_Env --> Thread_Sys
Init_Env --> Thread_Popup
Init_Env --> Thread_Pipe
end
subgraph Engine_Layer ["Detection Engine (PYAS_Engine.py)"]
direction TB
Scan_Worker["Scan Worker<br/>(scan_worker / scan_engine)"]:::engine
subgraph Scanners
Sign_Check["Signature Scanner<br/>(sign_scanner)<br/>[WinVerifyTrust]"]:::engine
AI_Model["AI Model Scanner<br/>(model_scanner)<br/>[ONNX/ResNet]"]:::engine
Yara_Rules["Rule Scanner<br/>(rule_scanner)<br/>[Yara/IP Lists]"]:::engine
CommClient["Filter Communication Client"]:::interaction
end
subgraph KernelMode ["Kernel Mode (C++ Minifilter Driver)"]
CommServer["ALPC Port (\\PYAS_Output_Pipe)"]:::interaction
DriverCore["Driver Entry (DriverEntry.cpp)"]:::kernelMode
RuleEngine["Rules & Trust Cache (ProtectRules.cpp)"]:::kernelMode
subgraph Protections ["Protection Subsystems"]
FileProtect["File Minifilter (ProtectFile.cpp)"]:::kernelMode
ProcessProtect["ObRegisterCallbacks (ProtectProcess.cpp)"]:::kernelMode
RegProtect["CmRegisterCallbackEx (ProtectRegistry.cpp)"]:::kernelMode
BootProtect["IRP_MJ_DEVICE_CONTROL (ProtectBoot.cpp)"]:::kernelMode
end
Thread_Proc --> Scan_Worker
Thread_File --> Scan_Worker
Thread_Net --> Scan_Worker
UI_Controller -->|Manual Scan| Scan_Worker
Scan_Worker --> Sign_Check
Scan_Worker --> AI_Model
Scan_Worker --> Yara_Rules
end
subgraph Kernel_OS_Layer ["Windows Kernel & Driver"]
direction TB
WinAPI["Windows API (ctypes)<br/>(kernel32, user32, ntdll,<br/>advapi32, iphlpapi, psapi)"]:::kernel
Driver_Sys["Kernel Driver<br/>(PYAS_Driver.sys)"]:::kernel
OS_FS["File System (NTFS)"]:::kernel
OS_Net["Network Stack"]:::kernel
Init_Env -- Load DLLs --> WinAPI
Thread_File -- Monitor --> OS_FS
Thread_Net -- Monitor --> OS_Net
Thread_Proc -- Suspend/Kill --> WinAPI
Thread_Pipe <==>|"Named Pipe IPC<br/>(\\.\pipe\PYAS_Output_Pipe)"| Driver_Sys
Driver_Sys -- Callback/Block --> WinAPI
subgraph OS ["Windows OS & Storage"]
FS["File System (FltMgr)"]:::storage
Reg["Registry (CmMgr)"]:::storage
ProcMgr["Process/Thread Manager (ObMgr)"]:::storage
Disk["Physical Disk / MBR"]:::storage
end
subgraph Data_Persistence ["Data & Resources"]
Config_File[("Config.json")]:::data
Model_Files[("Model Files<br/>(.onnx)")]:::data
Rule_Files[("Rule Files<br/>(.yar)")]:::data
Quarantine_Dir[("/Quarantine/")]:::data
CloudAPI["PYAS Cloud Server"]:::storage
Config_Mgr <--> Config_File
AI_Model <--> Model_Files
Yara_Rules <--> Rule_Files
List_Mgr <--> Quarantine_Dir
end
%% User Mode Internal
UI <-->|JS Evaluate / JSON| PYASCore
PYASCore -->|Extract Features & Scan| Engines
CloudScanner -.->|HTTPS REST API| CloudAPI
PYASCore <-->|FilterSendMessage / FilterGetMessage| CommClient
%% User-Kernel Boundary
CommClient <-->|PYAS_MESSAGE / PYAS_USER_MESSAGE| CommServer
%% Kernel Mode Internal
CommServer <-->|Message Dispatch| DriverCore
DriverCore --> RuleEngine
RuleEngine --> Protections
%% Kernel-OS Boundary
FileProtect -->|IRP_MJ_CREATE, IRP_MJ_WRITE...| FS
ProcessProtect -->|PreOpenProcess, PreOpenThread| ProcMgr
RegProtect -->|RegNtPreCreateKey, RegNtPreSetValueKey...| Reg
BootProtect -->|IOCTL_DISK_FORMAT_TRACKS...| Disk
```
## Machine Learning
The machine learning-based virus scanning engine training achieved extremely high accuracy.
<img width="895" height="1097" alt="train" src="https://github.com/user-attachments/assets/fa7cf0e2-01a1-4234-9358-718c2b8a2c9a" />
## Support System
Please ensure your computer supports Microsoft Visual C++ 2015-2022 Redistributable and Edge Webview2.
| Config | Permissions | System version | Processor | Memory | Storage |
|-----------|---------------|----------------------|-----------|--------|---------|
| Minimum | Administrator | >= Windows 10 (20H1) | 1 GHz | 200MB | 100MB |
| Minimum | Administrator | >= Windows 10 (20H1) | 1 GHz | 300MB | 100MB |
| Recommend | Administrator | >= Windows 10 (21H2) | 3 GHz | 500MB | 200MB |
## Packaged Releases
Download the installer. If it is incompatible with your system, you can repackage it yourself.
Packaged Download: https://github.com/87owo/PYAS/releases
## Official Website
If you are interested in this project, you can visit the website to see other related content.
@@ -186,10 +151,14 @@ Source Available : https://github.com/87owo/PYAS
Official Website : https://pyas-security.com/antivirus
Online Analyze : https://pyas-security.com/analyze
Packaged Download: https://github.com/87owo/PYAS/releases
## Project License
For any questions, needs, or bug feedback, please contact us through the following website.
Source Issues : https://github.com/87owo/PYAS/issues
Official Email : mailto:service.pyas@gmail.com
Official Email : service@pyas-security.com